source: rtems/cpukit/libdrvmgr/drvmgr.c @ 0b93d4f8

5
Last change on this file since 0b93d4f8 was 98b52e3, checked in by Sebastian Huber <sebastian.huber@…>, on 12/04/17 at 07:48:10

drvmgr: Use API mutex

  • Property mode set to 100644
File size: 17.9 KB
Line 
1/* Driver Manager Interface Implementation.
2 *
3 * COPYRIGHT (c) 2009 Cobham Gaisler AB.
4 *
5 * The license and distribution terms for this file may be
6 * found in the file LICENSE in this distribution or at
7 * http://www.rtems.org/license/LICENSE.
8 */
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include <drvmgr/drvmgr.h>
15#include <drvmgr/drvmgr_confdefs.h>
16
17#include <rtems/sysinit.h>
18
19#include "drvmgr_internal.h"
20
21/* Enable debugging */
22/*#define DEBUG 1*/
23
24#ifdef DEBUG
25#define DBG(x...) printk(x)
26#else
27#define DBG(x...)
28#endif
29
30struct drvmgr drvmgr = {
31        .level =                0,
32        .initializing_objs =    0,
33        .lock =                 API_MUTEX_INITIALIZER("_Drvmgr"),
34        .root_dev =             {0},
35        .root_drv =             NULL,
36
37        .drivers =      LIST_INITIALIZER(struct drvmgr_drv, next),
38
39        .buses = {
40                LIST_INITIALIZER(struct drvmgr_bus, next),
41                LIST_INITIALIZER(struct drvmgr_bus, next),
42                LIST_INITIALIZER(struct drvmgr_bus, next),
43                LIST_INITIALIZER(struct drvmgr_bus, next),
44                LIST_INITIALIZER(struct drvmgr_bus, next),
45        },
46        .buses_inactive =       LIST_INITIALIZER(struct drvmgr_bus, next),
47
48        .devices = {
49                LIST_INITIALIZER(struct drvmgr_dev, next),
50                LIST_INITIALIZER(struct drvmgr_dev, next),
51                LIST_INITIALIZER(struct drvmgr_dev, next),
52                LIST_INITIALIZER(struct drvmgr_dev, next),
53                LIST_INITIALIZER(struct drvmgr_dev, next),
54        },
55        .devices_inactive =     LIST_INITIALIZER(struct drvmgr_dev, next),
56};
57
58static int do_bus_init(
59        struct drvmgr *mgr,
60        struct drvmgr_bus *bus,
61        int level);
62static int do_dev_init(
63        struct drvmgr *mgr,
64        struct drvmgr_dev *dev,
65        int level);
66
67/* DRIVER MANAGER */
68
69void bsp_driver_level_hook(int level) __attribute__((weak));
70
71/* default hook does nothing, BSP may override. */
72void bsp_driver_level_hook(int level)
73{
74}
75
76void _DRV_Manager_init_level(int level)
77{
78        struct drvmgr *mgr = &drvmgr;
79
80        if (mgr->level >= level)
81                return;
82
83        /* Set new Level */
84        mgr->level = level;
85
86        /* Initialize buses and devices into this new level */
87        drvmgr_init_update();
88
89        bsp_driver_level_hook(level);
90}
91
92/* Initialize Data structures of the driver manager and call driver
93 * register functions configured by the user.
94 */
95void _DRV_Manager_initialization(void)
96{
97        drvmgr_drv_reg_func *drvreg;
98
99        /* Call driver register functions. */
100        drvreg = &drvmgr_drivers[0];
101        while (*drvreg) {
102                /* Make driver register */
103                (*drvreg)();
104                drvreg++;
105        }
106}
107
108/* Take ready devices and buses into the correct init level step by step.
109 * Once a bus or a device has been registered there is no turning
110 * back - they are taken to the level of the driver manager.
111 */
112void drvmgr_init_update(void)
113{
114        struct drvmgr *mgr = &drvmgr;
115        struct drvmgr_bus *bus;
116        struct drvmgr_dev *dev;
117        int bus_might_been_registered;
118        int level;
119
120        /* "Lock" to make sure we don't use up the stack and that the lists
121         * remain consistent.
122         */
123        DRVMGR_LOCK_WRITE();
124        if (mgr->initializing_objs || (mgr->level == 0))
125                goto out;
126        mgr->initializing_objs = 1;
127
128        /* Take all buses and devices ready into the same stage
129         * as the driver manager global level.
130         */
131        for (level = 0; level < mgr->level; level++) {
132
133                bus_might_been_registered = 0;
134
135                /* Take buses into next level */
136
137                while ((bus = BUS_LIST_HEAD(&mgr->buses[level])) != NULL) {
138
139                        /* Remove first in the list (will be inserted in
140                         * appropriate list by do_bus_init())
141                         */
142                        drvmgr_list_remove_head(&mgr->buses[level]);
143
144                        DRVMGR_UNLOCK();
145
146                        /* Initialize Bus, this will register devices on
147                         * the bus. Take bus into next level.
148                         */
149                        do_bus_init(mgr, bus, level+1);
150
151                        DRVMGR_LOCK_WRITE();
152                }
153
154                /* Take devices into next level */
155                while ((dev = DEV_LIST_HEAD(&mgr->devices[level])) != NULL) {
156
157                        /* Always process first in list */
158                        dev = DEV_LIST_HEAD(&mgr->devices[level]);
159
160                        /* Remove first in the list (will be inserted in
161                         * appropriate list by do_dev_init())
162                         */
163                        drvmgr_list_remove_head(&mgr->devices[level]);
164
165                        DRVMGR_UNLOCK();
166
167                        /* Initialize Device, this may register a new bus */
168                        do_dev_init(mgr, dev, level+1);
169
170                        DRVMGR_LOCK_WRITE();
171
172                        bus_might_been_registered = 1;
173                }
174
175                /* Make sure all buses registered and ready are taken at
176                 * the same time into init level N.
177                 */
178                if (bus_might_been_registered) {
179                        level = -1; /* restart loop */
180                }
181        }
182
183        /* Release bus/device initialization "Lock" */
184        mgr->initializing_objs = 0;
185
186out:
187        DRVMGR_UNLOCK();
188}
189
190/* Take bus into next level */
191static int do_bus_init(
192        struct drvmgr *mgr,
193        struct drvmgr_bus *bus,
194        int level)
195{
196        int (*init)(struct drvmgr_bus *);
197
198        /* If bridge device has failed during initialization, the bus is not
199         * initialized further.
200         */
201        if (bus->dev->state & DEV_STATE_INIT_FAILED) {
202                bus->state |= BUS_STATE_DEPEND_FAILED;
203                goto inactivate_out;
204        }
205
206        if (bus->ops && (init = bus->ops->init[level-1])) {
207                /* Note: This init1 function may register new devices */
208                bus->error = init(bus);
209                if (bus->error != DRVMGR_OK) {
210                        /* An error of some kind during bus initialization.
211                         *
212                         * Child devices and their buses are not inactived
213                         * directly here, instead they will all be catched by
214                         * do_dev_init() and do_bus_init() by checking if
215                         * parent or bridge-device failed. We know that
216                         * initialization will happen later for those devices.
217                         */
218                        goto inactivate_out;
219                }
220        }
221
222        DRVMGR_LOCK_WRITE();
223
224        /* Bus taken into the new level */
225        bus->level = level;
226
227        /* Put bus into list of buses reached level 'level'.
228         * Put at end of bus list so that init[N+1]() calls comes
229         * in the same order as init[N]()
230         */
231        drvmgr_list_add_tail(&mgr->buses[level], bus);
232
233        DRVMGR_UNLOCK();
234
235        return 0;
236
237inactivate_out:
238        DRVMGR_LOCK_WRITE();
239        bus->state |= BUS_STATE_INIT_FAILED;
240        bus->state |= BUS_STATE_LIST_INACTIVE;
241        drvmgr_list_add_head(&mgr->buses_inactive, bus);
242        DRVMGR_UNLOCK();
243
244        DBG("do_bus_init(%d): (DEV: %s) failed\n", level, bus->dev->name);
245
246        return 1;
247}
248
249/* Take device to initialization level 1 */
250static int do_dev_init(
251        struct drvmgr *mgr,
252        struct drvmgr_dev *dev,
253        int level)
254{
255        int (*init)(struct drvmgr_dev *);
256
257        /* Try to allocate Private Device Structure for driver if driver
258         * requests for this feature.
259         */
260        if (dev->drv && dev->drv->dev_priv_size && !dev->priv) {
261                dev->priv = malloc(dev->drv->dev_priv_size);
262                memset(dev->priv, 0, dev->drv->dev_priv_size);
263        }
264
265        /* If parent bus has failed during initialization,
266         * the device is not initialized further.
267         */
268        if (dev->parent && (dev->parent->state & BUS_STATE_INIT_FAILED)) {
269                dev->state |= DEV_STATE_DEPEND_FAILED;
270                goto inactivate_out;
271        }
272
273        /* Call Driver's Init Routine */
274        if (dev->drv && (init = dev->drv->ops->init[level-1])) {
275                /* Note: This init function may register new devices */
276                dev->error = init(dev);
277                if (dev->error != DRVMGR_OK) {
278                        /* An error of some kind has occured in the
279                         * driver/device, the failed device is put into the
280                         * inactive list, this way Init2,3 and/or 4 will not
281                         * be called for this device.
282                         *
283                         * The device is not removed from the bus (not
284                         * unregistered). The driver can be used to find
285                         * device information and debugging for example even
286                         * if device initialization failed.
287                         *
288                         * Child buses and their devices are not inactived
289                         * directly here, instead they will all be catched by
290                         * do_dev_init() and do_bus_init() by checking if
291                         * parent or bridge-device failed. We know that
292                         * initialization will happen later for those devices.
293                         */
294                        goto inactivate_out;
295                }
296        }
297
298        DRVMGR_LOCK_WRITE();
299        /* Dev taken into new level */
300        dev->level = level;
301
302        /* Put at end of device list so that init[N+1]() calls comes
303         * in the same order as init[N]()
304         */
305        drvmgr_list_add_tail(&mgr->devices[level], dev);
306        DRVMGR_UNLOCK();
307
308        return 0;
309
310inactivate_out:
311        DRVMGR_LOCK_WRITE();
312        dev->state |= DEV_STATE_INIT_FAILED;
313        dev->state |= DEV_STATE_LIST_INACTIVE;
314        drvmgr_list_add_head(&mgr->devices_inactive, dev);
315        DRVMGR_UNLOCK();
316
317        DBG("do_dev_init(%d): DRV: %s (DEV: %s) failed\n",
318                level, dev->drv->name, dev->name);
319
320        return 1; /* Failed to take device into requested level */
321}
322
323/* Register Root device driver */
324int drvmgr_root_drv_register(struct drvmgr_drv *drv)
325{
326        struct drvmgr *mgr = &drvmgr;
327        struct drvmgr_dev *root = &mgr->root_dev;
328
329        if (mgr->root_drv) {
330                /* Only possible to register root device once */
331                return DRVMGR_FAIL;
332        }
333
334        /* Set root device driver */
335        drv->next = NULL;
336        mgr->root_drv = drv;
337
338        /* Init root device non-NULL fields */
339        root->minor_drv = -1;
340        root->minor_bus = 0;
341        root->businfo = mgr;
342        root->name = "root bus";
343        /* Custom Driver association */
344        root->drv = mgr->root_drv;
345
346        /* This registers the root device and a bus */
347        drvmgr_dev_register(root);
348
349        return DRVMGR_OK;
350}
351
352/* Register a driver */
353int drvmgr_drv_register(struct drvmgr_drv *drv)
354{
355        struct drvmgr *mgr = &drvmgr;
356
357        /* All drivers must have been registered before start of init,
358         * because the manager does not scan all existing devices to find
359         * suitable hardware for this driver, and it is not protected with
360         * a lock therefore.
361         */
362        if (mgr->level > 0)
363                return -1;
364
365        drv->obj_type = DRVMGR_OBJ_DRV;
366
367        /* Put driver into list of registered drivers */
368        drvmgr_list_add_head(&mgr->drivers, drv);
369
370        /* TODO: we could scan for devices that this new driver has support
371         *       for. However, at this stage we assume that all drivers are
372         *       registered before devices are registered.
373         *
374         * LOCK: From the same assumsion locking the driver list is not needed
375         *       either.
376         */
377
378        return 0;
379}
380
381/* Insert a device into a driver's device list and assign a driver minor number
382 * to the device.
383 *
384 * The devices are ordered by their minor number (sorted linked list of devices)
385 * the minor number is found by looking for a gap or at the end.
386 */
387static void drvmgr_insert_dev_into_drv(
388        struct drvmgr_drv *drv,
389        struct drvmgr_dev *dev)
390{
391        struct drvmgr_dev *curr, **pprevnext;
392        int minor;
393
394        minor = 0;
395        pprevnext = &drv->dev;
396        curr = drv->dev;
397
398        while (curr) {
399                if (minor < curr->minor_drv) {
400                        /* Found a gap. Insert new device between prev
401                         * and curr. */
402                        break;
403                }
404                minor++;
405                pprevnext = &curr->next_in_drv;
406                curr = curr->next_in_drv;
407        }
408        dev->next_in_drv = curr;
409        *pprevnext = dev;
410
411        /* Set minor */
412        dev->minor_drv = minor;
413        drv->dev_cnt++;
414}
415
416/* Insert a device into a bus device list and assign a bus minor number to the
417 * device.
418 *
419 * The devices are ordered by their minor number (sorted linked list of devices)
420 * and by their registeration order if not using the same driver.
421 *
422 * The minor number is found by looking for a gap or at the end.
423 */
424static void drvmgr_insert_dev_into_bus(
425        struct drvmgr_bus *bus,
426        struct drvmgr_dev *dev)
427{
428        struct drvmgr_dev *curr, **pprevnext;
429        int minor;
430
431        minor = 0;
432        pprevnext = &bus->children;
433        curr = bus->children;
434
435        while (curr) {
436                if (dev->drv && (dev->drv == curr->drv)) {
437                        if (minor < curr->minor_bus) {
438                                /* Found a gap. Insert new device between prev
439                                 * and curr. */
440                                break;
441                        }
442                        minor++;
443                }
444                pprevnext = &curr->next_in_bus;
445                curr = curr->next_in_bus;
446        }
447        dev->next_in_bus = curr;
448        *pprevnext = dev;
449
450        /* Set minor. Devices without driver are given -1 */
451        if (dev->drv == NULL)
452                minor = -1;
453        dev->minor_bus = minor;
454        bus->dev_cnt++;
455}
456
457/* Try to find a driver for a device (unite a device with driver).
458 * a device with a driver
459 */
460static struct drvmgr_drv *drvmgr_dev_find_drv(
461                struct drvmgr_dev *dev)
462{
463        struct drvmgr *mgr = &drvmgr;
464        struct drvmgr_drv *drv;
465
466        /* NOTE: No locking is needed here since Driver list is supposed to be
467         *       initialized once during startup, we treat it as a static
468         *       read-only list
469         */
470
471        /* Try to find a driver that can handle this device */
472        for (drv = DRV_LIST_HEAD(&mgr->drivers); drv; drv = drv->next)
473                if (dev->parent->ops->unite(drv, dev) == 1)
474                        break;
475
476        return drv;
477}
478
479/* Register a device */
480int drvmgr_dev_register(struct drvmgr_dev *dev)
481{
482        struct drvmgr *mgr = &drvmgr;
483        struct drvmgr_drv *drv;
484        struct drvmgr_bus *bus = dev->parent;
485        struct drvmgr_key *keys;
486        struct drvmgr_list *init_list = &mgr->devices_inactive;
487
488        DBG("DEV_REG: %s at bus \"%s\"\n", dev->name,
489                bus && bus->dev && bus->dev->name ? bus->dev->name : "UNKNOWN");
490
491        /* Custom driver assocation? */
492        if (dev->drv) {
493                drv = dev->drv;
494                DBG("CUSTOM ASSOCIATION (%s to %s)\n", dev->name, drv->name);
495        } else {
496                /* Try to find a driver that can handle this device */
497                dev->drv = drv = drvmgr_dev_find_drv(dev);
498        }
499
500        DRVMGR_LOCK_WRITE();
501
502        /* Assign Bus Minor number and put into bus device list
503         * unless root device.
504         */
505        if (bus)
506                drvmgr_insert_dev_into_bus(bus, dev);
507
508        if (!drv) {
509                /* No driver found that can handle this device, put into
510                 * inactive list
511                 */
512                dev->minor_drv = -1;
513                dev->state |= DEV_STATE_LIST_INACTIVE;
514        } else {
515                /* United device with driver.
516                 * Put the device on the registered device list
517                 */
518                dev->state |= DEV_STATE_UNITED;
519
520                /* Check if user want to skip this core. This is not a
521                 * normal request, however in a multi-processor system
522                 * the two(or more) RTEMS instances must not use the same
523                 * devices in a system, not reporting a device to
524                 * it's driver will effectively accomplish this. In a
525                 * non Plug & Play system one can easily avoid this
526                 * problem by not report the core, but in a Plug & Play
527                 * system the bus driver will report all found cores.
528                 *
529                 * To stop the two RTEMS instances from using the same
530                 * device the user can simply define a resource entry
531                 * for a certain device but set the keys field to NULL.
532                 */
533                if (drvmgr_keys_get(dev, &keys) == 0 && keys == NULL) {
534                        /* Found Driver resource entry point
535                         * for this device, it was NULL, this
536                         * indicates to skip the core.
537                         *
538                         * We put it into the inactive list
539                         * marking it as ignored.
540                         */
541                        dev->state |= DEV_STATE_IGNORED;
542                } else {
543                        /* Assign Driver Minor number and put into driver's
544                         * device list
545                         */
546                        drvmgr_insert_dev_into_drv(drv, dev);
547
548                        /* Just register device, it will be initialized
549                         * later together with bus.
550                         *
551                         * At the end of the list (breadth first search)
552                         */
553                        init_list = &mgr->devices[0];
554
555                        DBG("Registered %s (DRV: %s) on %s\n",
556                                dev->name, drv->name,
557                                bus ? bus->dev->name : "NO PARENT");
558                }
559        }
560
561        drvmgr_list_add_tail(init_list, dev);
562
563        DRVMGR_UNLOCK();
564
565        /* Trigger Device initialization if not root device and
566         * has a driver
567         */
568        if (bus && dev->drv)
569                drvmgr_init_update();
570
571        return 0;
572}
573
574/* Register a bus */
575int drvmgr_bus_register(struct drvmgr_bus *bus)
576{
577        struct drvmgr *mgr = &drvmgr;
578        struct drvmgr_bus *bus_up;
579
580        /* Get bus architecture depth - the distance from root bus */
581        bus->depth = 0;
582        bus_up = bus->dev->parent;
583        while (bus_up) {
584                bus->depth++;
585                bus_up = bus_up->dev->parent;
586        }
587
588        DRVMGR_LOCK_WRITE();
589
590        /* Put driver into list of found buses */
591        drvmgr_list_add_tail(&mgr->buses[0], bus);
592
593        DRVMGR_UNLOCK();
594
595        /* Take bus into level1 and so on */
596        drvmgr_init_update();
597
598        return 0;
599}
600
601/* Allocate memory for a Device structure */
602int drvmgr_alloc_dev(struct drvmgr_dev **pdev, int extra)
603{
604        struct drvmgr_dev *dev;
605        int size;
606
607        /* The extra memory "service" is aligned to 4 bytes boundary. */
608        size = ((sizeof(struct drvmgr_dev) + 3) & ~0x3) + extra;
609        dev = (struct drvmgr_dev *)calloc(size, 1);
610        if (!dev) {
611                /* Failed to allocate device structure - critical error */
612                rtems_fatal_error_occurred(RTEMS_NO_MEMORY);
613        }
614        *pdev = dev;
615        dev->obj_type = DRVMGR_OBJ_DEV;
616
617        return 0;
618}
619
620/* Allocate memory for a Bus structure */
621int drvmgr_alloc_bus(struct drvmgr_bus **pbus, int extra)
622{
623        struct drvmgr_bus *bus;
624        int size;
625
626        /* The extra memory "service" is aligned to 4 bytes boundary. */
627        size = ((sizeof(struct drvmgr_bus) + 3) & ~0x3) + extra;
628        bus = (struct drvmgr_bus *)calloc(size, 1);
629        if (!bus) {
630                /* Failed to allocate device structure - critical error */
631                rtems_fatal_error_occurred(RTEMS_NO_MEMORY);
632        }
633        *pbus = bus;
634        bus->obj_type = DRVMGR_OBJ_BUS;
635
636        return 0;
637}
638
639/* Add driver resources to a bus instance */
640void drvmgr_bus_res_add(struct drvmgr_bus *bus,
641                                struct drvmgr_bus_res *bres)
642{
643        /* insert first in bus resource list. Locking isn't needed since
644         * resources can only be added before resource requests are made.
645         * When bus has been registered resources are considered a read-only
646         * tree.
647         */
648        bres->next = bus->reslist;
649        bus->reslist = bres;
650}
651
652#ifdef RTEMS_DRVMGR_STARTUP
653
654RTEMS_SYSINIT_ITEM(
655  _DRV_Manager_initialization,
656  RTEMS_SYSINIT_DRVMGR,
657  RTEMS_SYSINIT_ORDER_MIDDLE
658);
659
660/* BSPs has already registered their "root bus" driver in the
661 * bsp_predriver hook or so.
662 *
663 * Init Drivers to Level 1, constraints:
664 *   - Interrupts and system clock timer does not work.
665 *   - malloc() work, however other memory services may not
666 *     have been initialized yet.
667 *   - initializes most basic stuff
668 *
669 * Typical setup in Level 1:
670 *   - Find most devices in system, do PCI scan and configuration.
671 *   - Reset hardware if needed.
672 *   - Install IRQ driver
673 *   - Install Timer driver
674 *   - Install console driver and debug printk()
675 *   - Install extra memory.
676 */
677static void _DRV_Manager_init_level_1(void)
678{
679  _DRV_Manager_init_level(1);
680}
681
682RTEMS_SYSINIT_ITEM(
683  _DRV_Manager_init_level_1,
684  RTEMS_SYSINIT_DRVMGR_LEVEL_1,
685  RTEMS_SYSINIT_ORDER_MIDDLE
686);
687
688/* Init Drivers to Level 2, constraints:
689 *  - Interrupts can be registered and enabled.
690 *  - System Clock is running
691 *  - Console may be used.
692 *
693 * This is typically where drivers are initialized
694 * for the first time.
695 */
696static void _DRV_Manager_init_level_2(void)
697{
698  _DRV_Manager_init_level(2);
699}
700
701RTEMS_SYSINIT_ITEM(
702  _DRV_Manager_init_level_2,
703  RTEMS_SYSINIT_DRVMGR_LEVEL_2,
704  RTEMS_SYSINIT_ORDER_MIDDLE
705);
706
707/* Init Drivers to Level 3
708 *
709 * This is typically where normal drivers are initialized
710 * for the second time, they may depend on other drivers
711 * API inited in level 2
712 */
713static void _DRV_Manager_init_level_3(void)
714{
715  _DRV_Manager_init_level(3);
716}
717
718RTEMS_SYSINIT_ITEM(
719  _DRV_Manager_init_level_3,
720  RTEMS_SYSINIT_DRVMGR_LEVEL_3,
721  RTEMS_SYSINIT_ORDER_MIDDLE
722);
723
724/* Init Drivers to Level 4,
725 * Init drivers that depend on services initialized in Level 3
726 */
727static void _DRV_Manager_init_level_4(void)
728{
729  _DRV_Manager_init_level(4);
730}
731
732RTEMS_SYSINIT_ITEM(
733  _DRV_Manager_init_level_4,
734  RTEMS_SYSINIT_DRVMGR_LEVEL_4,
735  RTEMS_SYSINIT_ORDER_MIDDLE
736);
737
738#endif /* RTEMS_DRVMGR_STARTUP */
Note: See TracBrowser for help on using the repository browser.