source: rtems/cpukit/libdrvmgr/drvmgr.c @ eff69891

4.115
Last change on this file since eff69891 was 65d1f35, checked in by Daniel Hellstrom <daniel@…>, on 02/27/15 at 15:42:36

DRVMGR: updated copyright into one line only

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