xf86platformBus.c revision 1b5d61b8
1/*
2 * Copyright © 2012 Red Hat.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *
22 * Author: Dave Airlie <airlied@redhat.com>
23 */
24
25/*
26 * This file contains the interfaces to the bus-specific code
27 */
28
29#ifdef HAVE_XORG_CONFIG_H
30#include <xorg-config.h>
31#endif
32
33#ifdef XSERVER_PLATFORM_BUS
34#include <errno.h>
35
36#include <pciaccess.h>
37#include <fcntl.h>
38#include <unistd.h>
39#include "os.h"
40#include "hotplug.h"
41#include "systemd-logind.h"
42
43#include "loaderProcs.h"
44#include "xf86.h"
45#include "xf86_OSproc.h"
46#include "xf86Priv.h"
47#include "xf86str.h"
48#include "xf86Bus.h"
49#include "Pci.h"
50#include "xf86platformBus.h"
51#include "xf86Config.h"
52
53#include "randrstr.h"
54int platformSlotClaimed;
55
56int xf86_num_platform_devices;
57
58struct xf86_platform_device *xf86_platform_devices;
59
60int
61xf86_add_platform_device(struct OdevAttributes *attribs, Bool unowned)
62{
63    xf86_platform_devices = xnfreallocarray(xf86_platform_devices,
64                                            xf86_num_platform_devices + 1,
65                                            sizeof(struct xf86_platform_device));
66
67    xf86_platform_devices[xf86_num_platform_devices].attribs = attribs;
68    xf86_platform_devices[xf86_num_platform_devices].pdev = NULL;
69    xf86_platform_devices[xf86_num_platform_devices].flags =
70        unowned ? XF86_PDEV_UNOWNED : 0;
71
72    xf86_num_platform_devices++;
73    return 0;
74}
75
76int
77xf86_remove_platform_device(int dev_index)
78{
79    int j;
80
81    config_odev_free_attributes(xf86_platform_devices[dev_index].attribs);
82
83    for (j = dev_index; j < xf86_num_platform_devices - 1; j++)
84        memcpy(&xf86_platform_devices[j], &xf86_platform_devices[j + 1], sizeof(struct xf86_platform_device));
85    xf86_num_platform_devices--;
86    return 0;
87}
88
89Bool
90xf86_get_platform_device_unowned(int index)
91{
92    return (xf86_platform_devices[index].flags & XF86_PDEV_UNOWNED) ?
93        TRUE : FALSE;
94}
95
96struct xf86_platform_device *
97xf86_find_platform_device_by_devnum(int major, int minor)
98{
99    int i, attr_major, attr_minor;
100
101    for (i = 0; i < xf86_num_platform_devices; i++) {
102        attr_major = xf86_platform_odev_attributes(i)->major;
103        attr_minor = xf86_platform_odev_attributes(i)->minor;
104        if (attr_major == major && attr_minor == minor)
105            return &xf86_platform_devices[i];
106    }
107    return NULL;
108}
109
110/*
111 * xf86IsPrimaryPlatform() -- return TRUE if primary device
112 * is a platform device and it matches this one.
113 */
114
115static Bool
116xf86IsPrimaryPlatform(struct xf86_platform_device *plat)
117{
118    /* Add max. 1 screen for the IgnorePrimary fallback path */
119    if (xf86ProbeIgnorePrimary && xf86NumScreens == 0)
120        return TRUE;
121
122    if (primaryBus.type == BUS_PLATFORM)
123        return plat == primaryBus.id.plat;
124#ifdef XSERVER_LIBPCIACCESS
125    if (primaryBus.type == BUS_PCI)
126        if (plat->pdev)
127            if (MATCH_PCI_DEVICES(primaryBus.id.pci, plat->pdev))
128                return TRUE;
129#endif
130    return FALSE;
131}
132
133static void
134platform_find_pci_info(struct xf86_platform_device *pd, char *busid)
135{
136    struct pci_slot_match devmatch;
137    struct pci_device *info;
138    struct pci_device_iterator *iter;
139    int ret;
140
141    ret = sscanf(busid, "pci:%04x:%02x:%02x.%u",
142                 &devmatch.domain, &devmatch.bus, &devmatch.dev,
143                 &devmatch.func);
144    if (ret != 4)
145        return;
146
147    iter = pci_slot_match_iterator_create(&devmatch);
148    info = pci_device_next(iter);
149    if (info)
150        pd->pdev = info;
151    pci_iterator_destroy(iter);
152}
153
154static Bool
155xf86_check_platform_slot(const struct xf86_platform_device *pd)
156{
157    int i;
158
159    for (i = 0; i < xf86NumEntities; i++) {
160        const EntityPtr u = xf86Entities[i];
161
162        if (pd->pdev && u->bus.type == BUS_PCI &&
163            MATCH_PCI_DEVICES(pd->pdev, u->bus.id.pci)) {
164            return FALSE;
165        }
166        if ((u->bus.type == BUS_PLATFORM) && (pd == u->bus.id.plat)) {
167            return FALSE;
168        }
169    }
170    return TRUE;
171}
172
173static Bool
174MatchToken(const char *value, struct xorg_list *patterns,
175           int (*compare)(const char *, const char *))
176{
177    const xf86MatchGroup *group;
178
179    /* If there are no patterns, accept the match */
180    if (xorg_list_is_empty(patterns))
181        return TRUE;
182
183    /* If there are patterns but no attribute, reject the match */
184    if (!value)
185        return FALSE;
186
187    /*
188     * Otherwise, iterate the list of patterns ensuring each entry has a
189     * match. Each list entry is a separate Match line of the same type.
190     */
191    xorg_list_for_each_entry(group, patterns, entry) {
192        Bool match = FALSE;
193        char *const *cur;
194
195        for (cur = group->values; *cur; cur++) {
196            if ((*compare)(value, *cur) == 0) {
197                match = TRUE;
198                break;
199            }
200        }
201
202        if (!match)
203            return FALSE;
204    }
205
206    /* All the entries in the list matched the attribute */
207    return TRUE;
208}
209
210static Bool
211OutputClassMatches(const XF86ConfOutputClassPtr oclass,
212                   struct xf86_platform_device *dev)
213{
214    char *driver = dev->attribs->driver;
215
216    if (!MatchToken(driver, &oclass->match_driver, strcmp))
217        return FALSE;
218
219    return TRUE;
220}
221
222static void
223xf86OutputClassDriverList(int index, XF86MatchedDrivers *md)
224{
225    XF86ConfOutputClassPtr cl;
226
227    for (cl = xf86configptr->conf_outputclass_lst; cl; cl = cl->list.next) {
228        if (OutputClassMatches(cl, &xf86_platform_devices[index])) {
229            char *path = xf86_platform_odev_attributes(index)->path;
230
231            xf86Msg(X_INFO, "Applying OutputClass \"%s\" to %s\n",
232                    cl->identifier, path);
233            xf86Msg(X_NONE, "\tloading driver: %s\n", cl->driver);
234
235            xf86AddMatchedDriver(md, cl->driver);
236        }
237    }
238}
239
240/**
241 *  @return The numbers of found devices that match with the current system
242 *  drivers.
243 */
244void
245xf86PlatformMatchDriver(XF86MatchedDrivers *md)
246{
247    int i;
248    struct pci_device *info = NULL;
249    int pass = 0;
250
251    for (pass = 0; pass < 2; pass++) {
252        for (i = 0; i < xf86_num_platform_devices; i++) {
253
254            if (xf86IsPrimaryPlatform(&xf86_platform_devices[i]) && (pass == 1))
255                continue;
256            else if (!xf86IsPrimaryPlatform(&xf86_platform_devices[i]) && (pass == 0))
257                continue;
258
259            xf86OutputClassDriverList(i, md);
260
261            info = xf86_platform_devices[i].pdev;
262#ifdef __linux__
263            if (info)
264                xf86MatchDriverFromFiles(info->vendor_id, info->device_id, md);
265#endif
266
267            if (info != NULL) {
268                xf86VideoPtrToDriverList(info, md);
269            }
270        }
271    }
272}
273
274int
275xf86platformProbe(void)
276{
277    int i;
278    Bool pci = TRUE;
279    XF86ConfOutputClassPtr cl, cl_head = (xf86configptr) ?
280            xf86configptr->conf_outputclass_lst : NULL;
281    char *old_path, *path = NULL;
282
283    config_odev_probe(xf86PlatformDeviceProbe);
284
285    if (!xf86scanpci()) {
286        pci = FALSE;
287    }
288
289    for (i = 0; i < xf86_num_platform_devices; i++) {
290        char *busid = xf86_platform_odev_attributes(i)->busid;
291
292        if (pci && (strncmp(busid, "pci:", 4) == 0)) {
293            platform_find_pci_info(&xf86_platform_devices[i], busid);
294        }
295
296        /*
297         * Deal with OutputClass ModulePath directives, these must be
298         * processed before we do any module loading.
299         */
300        for (cl = cl_head; cl; cl = cl->list.next) {
301            if (!OutputClassMatches(cl, &xf86_platform_devices[i]))
302                continue;
303
304            if (cl->modulepath && xf86ModPathFrom != X_CMDLINE) {
305                old_path = path;
306                XNFasprintf(&path, "%s,%s", cl->modulepath,
307                            path ? path : xf86ModulePath);
308                free(old_path);
309                xf86Msg(X_CONFIG, "OutputClass \"%s\" ModulePath extended to \"%s\"\n",
310                        cl->identifier, path);
311                LoaderSetPath(path);
312            }
313        }
314    }
315
316    free(path);
317
318    /* First see if there is an OutputClass match marking a device as primary */
319    for (i = 0; i < xf86_num_platform_devices; i++) {
320        struct xf86_platform_device *dev = &xf86_platform_devices[i];
321        for (cl = cl_head; cl; cl = cl->list.next) {
322            if (!OutputClassMatches(cl, dev))
323                continue;
324
325            if (xf86CheckBoolOption(cl->option_lst, "PrimaryGPU", FALSE)) {
326                xf86Msg(X_CONFIG, "OutputClass \"%s\" setting %s as PrimaryGPU\n",
327                        cl->identifier, dev->attribs->path);
328                primaryBus.type = BUS_PLATFORM;
329                primaryBus.id.plat = dev;
330                return 0;
331            }
332        }
333    }
334
335    /* Then check for pci_device_is_boot_vga() */
336    for (i = 0; i < xf86_num_platform_devices; i++) {
337        struct xf86_platform_device *dev = &xf86_platform_devices[i];
338
339        if (!dev->pdev)
340            continue;
341
342        pci_device_probe(dev->pdev);
343        if (pci_device_is_boot_vga(dev->pdev)) {
344            primaryBus.type = BUS_PLATFORM;
345            primaryBus.id.plat = dev;
346        }
347    }
348
349    return 0;
350}
351
352void
353xf86MergeOutputClassOptions(int entityIndex, void **options)
354{
355    const EntityPtr entity = xf86Entities[entityIndex];
356    struct xf86_platform_device *dev = NULL;
357    XF86ConfOutputClassPtr cl;
358    XF86OptionPtr classopts;
359    int i = 0;
360
361    switch (entity->bus.type) {
362    case BUS_PLATFORM:
363        dev = entity->bus.id.plat;
364        break;
365    case BUS_PCI:
366        for (i = 0; i < xf86_num_platform_devices; i++) {
367            if (MATCH_PCI_DEVICES(xf86_platform_devices[i].pdev,
368                                  entity->bus.id.pci)) {
369                dev = &xf86_platform_devices[i];
370                break;
371            }
372        }
373        break;
374    default:
375        xf86Msg(X_DEBUG, "xf86MergeOutputClassOptions unsupported bus type %d\n",
376                entity->bus.type);
377    }
378
379    if (!dev)
380        return;
381
382    for (cl = xf86configptr->conf_outputclass_lst; cl; cl = cl->list.next) {
383        if (!OutputClassMatches(cl, dev) || !cl->option_lst)
384            continue;
385
386        xf86Msg(X_INFO, "Applying OutputClass \"%s\" options to %s\n",
387                cl->identifier, dev->attribs->path);
388
389        classopts = xf86optionListDup(cl->option_lst);
390        *options = xf86optionListMerge(*options, classopts);
391    }
392}
393
394static int
395xf86ClaimPlatformSlot(struct xf86_platform_device * d, DriverPtr drvp,
396                  int chipset, GDevPtr dev, Bool active)
397{
398    EntityPtr p = NULL;
399    int num;
400
401    if (xf86_check_platform_slot(d)) {
402        num = xf86AllocateEntity();
403        p = xf86Entities[num];
404        p->driver = drvp;
405        p->chipset = chipset;
406        p->bus.type = BUS_PLATFORM;
407        p->bus.id.plat = d;
408        p->active = active;
409        p->inUse = FALSE;
410        if (dev)
411            xf86AddDevToEntity(num, dev);
412
413        platformSlotClaimed++;
414        return num;
415    }
416    else
417        return -1;
418}
419
420static int
421xf86UnclaimPlatformSlot(struct xf86_platform_device *d, GDevPtr dev)
422{
423    int i;
424
425    for (i = 0; i < xf86NumEntities; i++) {
426        const EntityPtr p = xf86Entities[i];
427
428        if ((p->bus.type == BUS_PLATFORM) && (p->bus.id.plat == d)) {
429            if (dev)
430                xf86RemoveDevFromEntity(i, dev);
431            platformSlotClaimed--;
432            p->bus.type = BUS_NONE;
433            return 0;
434        }
435    }
436    return 0;
437}
438
439
440#define END_OF_MATCHES(m)                                               \
441    (((m).vendor_id == 0) && ((m).device_id == 0) && ((m).subvendor_id == 0))
442
443static Bool doPlatformProbe(struct xf86_platform_device *dev, DriverPtr drvp,
444                            GDevPtr gdev, int flags, intptr_t match_data)
445{
446    Bool foundScreen = FALSE;
447    int entity;
448
449    if (gdev && gdev->screen == 0 && !xf86_check_platform_slot(dev))
450        return FALSE;
451
452    entity = xf86ClaimPlatformSlot(dev, drvp, 0,
453                                   gdev, gdev ? gdev->active : 0);
454
455    if ((entity == -1) && gdev && (gdev->screen > 0)) {
456        unsigned nent;
457
458        for (nent = 0; nent < xf86NumEntities; nent++) {
459            EntityPtr pEnt = xf86Entities[nent];
460
461            if (pEnt->bus.type != BUS_PLATFORM)
462                continue;
463            if (pEnt->bus.id.plat == dev) {
464                entity = nent;
465                xf86AddDevToEntity(nent, gdev);
466                break;
467            }
468        }
469    }
470    if (entity != -1) {
471        if ((dev->flags & XF86_PDEV_SERVER_FD) && (!drvp->driverFunc ||
472                !drvp->driverFunc(NULL, SUPPORTS_SERVER_FDS, NULL))) {
473            systemd_logind_release_fd(dev->attribs->major, dev->attribs->minor, dev->attribs->fd);
474            dev->attribs->fd = -1;
475            dev->flags &= ~XF86_PDEV_SERVER_FD;
476        }
477
478        if (drvp->platformProbe(drvp, entity, flags, dev, match_data))
479            foundScreen = TRUE;
480        else
481            xf86UnclaimPlatformSlot(dev, gdev);
482    }
483    return foundScreen;
484}
485
486static Bool
487probeSingleDevice(struct xf86_platform_device *dev, DriverPtr drvp, GDevPtr gdev, int flags)
488{
489    int k;
490    Bool foundScreen = FALSE;
491    struct pci_device *pPci;
492    const struct pci_id_match *const devices = drvp->supported_devices;
493
494    if (dev->pdev && devices) {
495        int device_id = dev->pdev->device_id;
496        pPci = dev->pdev;
497        for (k = 0; !END_OF_MATCHES(devices[k]); k++) {
498            if (PCI_ID_COMPARE(devices[k].vendor_id, pPci->vendor_id)
499                && PCI_ID_COMPARE(devices[k].device_id, device_id)
500                && ((devices[k].device_class_mask & pPci->device_class)
501                    ==  devices[k].device_class)) {
502                foundScreen = doPlatformProbe(dev, drvp, gdev, flags, devices[k].match_data);
503                if (foundScreen)
504                    break;
505            }
506        }
507    }
508    else if (dev->pdev && !devices)
509        return FALSE;
510    else
511        foundScreen = doPlatformProbe(dev, drvp, gdev, flags, 0);
512    return foundScreen;
513}
514
515static Bool
516isGPUDevice(GDevPtr gdev)
517{
518    int i;
519
520    for (i = 0; i < gdev->myScreenSection->num_gpu_devices; i++) {
521        if (gdev == gdev->myScreenSection->gpu_devices[i])
522            return TRUE;
523    }
524
525    return FALSE;
526}
527
528int
529xf86platformProbeDev(DriverPtr drvp)
530{
531    Bool foundScreen = FALSE;
532    GDevPtr *devList;
533    const unsigned numDevs = xf86MatchDevice(drvp->driverName, &devList);
534    int i, j;
535
536    /* find the main device or any device specificed in xorg.conf */
537    for (i = 0; i < numDevs; i++) {
538        /* skip inactive devices */
539        if (!devList[i]->active)
540            continue;
541
542        for (j = 0; j < xf86_num_platform_devices; j++) {
543            if (devList[i]->busID && *devList[i]->busID) {
544                if (xf86PlatformDeviceCheckBusID(&xf86_platform_devices[j], devList[i]->busID))
545                    break;
546            }
547            else {
548                /* for non-seat0 servers assume first device is the master */
549                if (ServerIsNotSeat0())
550                    break;
551
552                if (xf86IsPrimaryPlatform(&xf86_platform_devices[j]))
553                    break;
554            }
555        }
556
557        if (j == xf86_num_platform_devices)
558             continue;
559
560        foundScreen = probeSingleDevice(&xf86_platform_devices[j], drvp, devList[i],
561                                        isGPUDevice(devList[i]) ? PLATFORM_PROBE_GPU_SCREEN : 0);
562    }
563
564    free(devList);
565
566    return foundScreen;
567}
568
569int
570xf86platformAddGPUDevices(DriverPtr drvp)
571{
572    Bool foundScreen = FALSE;
573    GDevPtr *devList;
574    int j;
575
576    if (!drvp->platformProbe)
577        return FALSE;
578
579    xf86MatchDevice(drvp->driverName, &devList);
580
581    /* if autoaddgpu devices is enabled then go find any unclaimed platform
582     * devices and add them as GPU screens */
583    if (xf86Info.autoAddGPU) {
584        for (j = 0; j < xf86_num_platform_devices; j++) {
585            if (probeSingleDevice(&xf86_platform_devices[j], drvp,
586                                  devList ?  devList[0] : NULL,
587                                  PLATFORM_PROBE_GPU_SCREEN))
588                foundScreen = TRUE;
589        }
590    }
591
592    free(devList);
593
594    return foundScreen;
595}
596
597int
598xf86platformAddDevice(int index)
599{
600    int i, old_screens, scr_index;
601    DriverPtr drvp = NULL;
602    screenLayoutPtr layout;
603    static const char *hotplug_driver_name = "modesetting";
604
605    if (!xf86Info.autoAddGPU)
606        return -1;
607
608    /* force load the driver for now */
609    xf86LoadOneModule(hotplug_driver_name, NULL);
610
611    for (i = 0; i < xf86NumDrivers; i++) {
612        if (!xf86DriverList[i])
613            continue;
614
615        if (!strcmp(xf86DriverList[i]->driverName, hotplug_driver_name)) {
616            drvp = xf86DriverList[i];
617            break;
618        }
619    }
620    if (i == xf86NumDrivers)
621        return -1;
622
623    old_screens = xf86NumGPUScreens;
624    doPlatformProbe(&xf86_platform_devices[index], drvp, NULL,
625                    PLATFORM_PROBE_GPU_SCREEN, 0);
626    if (old_screens == xf86NumGPUScreens)
627        return -1;
628    i = old_screens;
629
630    for (layout = xf86ConfigLayout.screens; layout->screen != NULL;
631         layout++) {
632        xf86GPUScreens[i]->confScreen = layout->screen;
633        break;
634    }
635
636    if (xf86GPUScreens[i]->PreInit &&
637        xf86GPUScreens[i]->PreInit(xf86GPUScreens[i], 0))
638        xf86GPUScreens[i]->configured = TRUE;
639
640    if (!xf86GPUScreens[i]->configured) {
641        ErrorF("hotplugged device %d didn't configure\n", i);
642        xf86DeleteScreen(xf86GPUScreens[i]);
643        return -1;
644    }
645
646   scr_index = AddGPUScreen(xf86GPUScreens[i]->ScreenInit, 0, NULL);
647   if (scr_index == -1) {
648       xf86DeleteScreen(xf86GPUScreens[i]);
649       xf86UnclaimPlatformSlot(&xf86_platform_devices[index], NULL);
650       xf86NumGPUScreens = old_screens;
651       return -1;
652   }
653   dixSetPrivate(&xf86GPUScreens[i]->pScreen->devPrivates,
654                 xf86ScreenKey, xf86GPUScreens[i]);
655
656   CreateScratchPixmapsForScreen(xf86GPUScreens[i]->pScreen);
657
658   if (xf86GPUScreens[i]->pScreen->CreateScreenResources &&
659       !(*xf86GPUScreens[i]->pScreen->CreateScreenResources) (xf86GPUScreens[i]->pScreen)) {
660       RemoveGPUScreen(xf86GPUScreens[i]->pScreen);
661       xf86DeleteScreen(xf86GPUScreens[i]);
662       xf86UnclaimPlatformSlot(&xf86_platform_devices[index], NULL);
663       xf86NumGPUScreens = old_screens;
664       return -1;
665   }
666   /* attach unbound to 0 protocol screen */
667   AttachUnboundGPU(xf86Screens[0]->pScreen, xf86GPUScreens[i]->pScreen);
668
669   RRResourcesChanged(xf86Screens[0]->pScreen);
670   RRTellChanged(xf86Screens[0]->pScreen);
671
672   return 0;
673}
674
675void
676xf86platformRemoveDevice(int index)
677{
678    EntityPtr entity;
679    int ent_num, i, j;
680    Bool found;
681
682    for (ent_num = 0; ent_num < xf86NumEntities; ent_num++) {
683        entity = xf86Entities[ent_num];
684        if (entity->bus.type == BUS_PLATFORM &&
685            entity->bus.id.plat == &xf86_platform_devices[index])
686            break;
687    }
688    if (ent_num == xf86NumEntities)
689        goto out;
690
691    found = FALSE;
692    for (i = 0; i < xf86NumGPUScreens; i++) {
693        for (j = 0; j < xf86GPUScreens[i]->numEntities; j++)
694            if (xf86GPUScreens[i]->entityList[j] == ent_num) {
695                found = TRUE;
696                break;
697            }
698        if (found)
699            break;
700    }
701    if (!found) {
702        ErrorF("failed to find screen to remove\n");
703        goto out;
704    }
705
706    xf86GPUScreens[i]->pScreen->CloseScreen(xf86GPUScreens[i]->pScreen);
707
708    RemoveGPUScreen(xf86GPUScreens[i]->pScreen);
709    xf86DeleteScreen(xf86GPUScreens[i]);
710
711    xf86UnclaimPlatformSlot(&xf86_platform_devices[index], NULL);
712
713    xf86_remove_platform_device(index);
714
715    RRResourcesChanged(xf86Screens[0]->pScreen);
716    RRTellChanged(xf86Screens[0]->pScreen);
717 out:
718    return;
719}
720
721/* called on return from VT switch to find any new devices */
722void xf86platformVTProbe(void)
723{
724    int i;
725
726    for (i = 0; i < xf86_num_platform_devices; i++) {
727        if (!(xf86_platform_devices[i].flags & XF86_PDEV_UNOWNED))
728            continue;
729
730        xf86_platform_devices[i].flags &= ~XF86_PDEV_UNOWNED;
731        xf86PlatformReprobeDevice(i, xf86_platform_devices[i].attribs);
732    }
733}
734
735void xf86platformPrimary(void)
736{
737    /* use the first platform device as a fallback */
738    if (primaryBus.type == BUS_NONE) {
739        xf86Msg(X_INFO, "no primary bus or device found\n");
740
741        if (xf86_num_platform_devices > 0) {
742            primaryBus.id.plat = &xf86_platform_devices[0];
743            primaryBus.type = BUS_PLATFORM;
744
745            xf86Msg(X_NONE, "\tfalling back to %s\n", primaryBus.id.plat->attribs->syspath);
746        }
747    }
748}
749#endif
750