We are currently migrating Bugzilla to GitHub issues.
Any changes made to the bug tracker now will be lost, so please do not post new bugs or make changes to them.
When we're done, all bug URLs will redirect to their equivalent location on the new bug tracker.

Bug 4624

Summary: [patch] KMS/DRM fails on FreeBSD because /dev/dri/card* nodes are symlinks
Product: SDL Reporter: Jan Martin Mikkelsen <janm>
Component: videoAssignee: Sam Lantinga <slouken>
Status: RESOLVED FIXED QA Contact: Sam Lantinga <slouken>
Severity: normal    
Priority: P2 CC: sylvain.becker
Version: 2.0.9   
Hardware: All   
OS: FreeBSD   
Attachments: Patch to scan /dev/dri based on names rather than file type

Description Jan Martin Mikkelsen 2019-05-07 15:32:33 UTC
Created attachment 3776 [details]
Patch to scan /dev/dri based on names rather than file type

Loading KMS/DRM on FreeBSD fails because the "available" code in the driver checks for character device nodes under /dev/dri and the /dev/dri/card* files are symlinks rather than device nodes nodes on FreeBSD. The symlink points to /dev/drm/0.

The attached patch counts /dev/dri/card* entries rather than directory entries which are character devices.
Comment 1 Jan Martin Mikkelsen 2019-06-18 14:16:31 UTC
Is any additional information necessary for this patch?
Comment 2 Sam Lantinga 2019-06-18 15:00:02 UTC
This is fixed for SDL 2.0.10, thanks!

*** This bug has been marked as a duplicate of bug 4241 ***
Comment 3 Jan Martin Mikkelsen 2019-06-18 15:17:54 UTC
Thanks.

The patch in that commit fails on FreeBSD. Specifically:

    1.60 +    folder = opendir(KMSDRM_DRI_PATH);
    1.61 +    if (folder) {
    1.62 +        while ((res = readdir(folder))) {
    1.63 +            if (res->d_type == DT_CHR) {
    1.64 +                devcount++;
    1.65 +            }
    1.66 +        }
    1.67 +        closedir(folder);
    1.68 +    }
    1.69 +

Only examines entries under /dev/dri which are character devices. On FreeBSD these are character devices which point to an entry under /dev/drm. For example:

janm@test-tipro: ~ $ ls -l /dev/dri
total 0
lrwxr-xr-x  1 root  wheel   8 18 Jun 17:10 card0 -> ../drm/0
lrwxr-xr-x  1 root  wheel  10 18 Jun 17:10 renderD128 -> ../drm/128

The patch in this bug report updates this to look for entries that start with "card" instead. It changes the "res->d_type == DT_CHR" test to "res->d_namlen > 4 && strncmp(res->d_name, "card", 4)".

I just looked at the patch and realised it was the wrong way around, sorry.
Comment 4 Sam Lantinga 2019-06-18 21:16:29 UTC
Got it, thanks!
https://hg.libsdl.org/SDL/rev/e490856ac51c
Comment 5 Sylvain 2019-06-19 07:18:32 UTC
This failed to compile on Linux, 

error: no member named 'd_namlen' in 'struct dirent'; did you mean 'd_name'?
            if (res->d_namlen > 4 && strncmp(res->d_name, "card", 4)) {
                     ^~~~~~~~
                     d_name


Patch added https://hg.libsdl.org/SDL/rev/dd47d4ea431e
Comment 6 Jan Martin Mikkelsen 2019-06-19 07:30:49 UTC
OK, looking closer at struct dirent portability, I see that not all systems have all fields, and I shouldn't rely in d_namelen.

I'm not sure that "if (res->d_name)" is meaningful; it should always be true. Embarrassingly, I also see an inverted test here. 

Something simple like:

         if (strncmp(res->d_name, "card", 4) == 0)

Looks more appropriate.
Comment 7 Sylvain 2019-06-19 07:45:40 UTC
You're right, "if (res->d_name)" is not meaningful.

Yes also for: strncmp(res->d_name, "card", 4) == 0


But it still requires "len > 4" right ?
(otherwise "c" "ca" "car" would match ?)
Comment 8 Sylvain 2019-06-19 08:12:31 UTC
This is committed https://hg.libsdl.org/SDL/rev/df4fda4ec707
Please try it!
Comment 9 Jan Martin Mikkelsen 2019-06-19 08:15:10 UTC
(In reply to Sylvain from comment #7)
> You're right, "if (res->d_name)" is not meaningful.
> 
> Yes also for: strncmp(res->d_name, "card", 4) == 0
> 
> 
> But it still requires "len > 4" right ?
> (otherwise "c" "ca" "car" would match ?)

Shorter than four characters (eg. "c", "ca" and "car") will not match. The issue with this approach is that "card" will match, where we really want "card0". This is why I originally had a "> 4" test.

It could also be:

        if (strlen(res->d_name) > 4 && strncmp(res->d_name, "card", 4) == 0)

I don't think it is worth looking too hard at this case. The code uses the count to later go through the "card%d" entries starting from zero up to the number of matches on this pattern. A more correct approach is to enumerate all of the /dev/dri/card* entries, and use the actual filename in check_modesetting, and when it matches, extract the card unit number.

I plan to have a closer look at kmsdrm to look at things like this, and to see how hard it would be to add multiple display support. I will do that when I have a little more time; this patch was intended to make what was there work on FreeBSD.
Comment 10 Jan Martin Mikkelsen 2019-06-19 08:20:32 UTC
(In reply to Sylvain from comment #8)
> This is committed https://hg.libsdl.org/SDL/rev/df4fda4ec707
> Please try it!

This patch looks good, if I see problems (unlikely) I will let you know.
Comment 11 Jan Martin Mikkelsen 2019-06-19 08:21:49 UTC
(In reply to Sylvain from comment #8)
> This is committed https://hg.libsdl.org/SDL/rev/df4fda4ec707
> Please try it!

Thanks for testing!