source: rtems/cpukit/libdrvmgr/drvmgr.c @ 6d21a3f2

5
Last change on this file since 6d21a3f2 was 6d21a3f2, checked in by Sebastian Huber <sebastian.huber@…>, on 04/17/18 at 04:31:30

drvmgr: Remove bsp_driver_level_hook()

Use RTEMS_SYSINIT_ITEM() instead.

Update #2408.

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