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

4.115
Last change on this file since e7fade3 was e7fade3, checked in by Daniel Hellstrom <daniel@…>, on 11/28/11 at 08:52:03

DRVMGR: added driver manager to cpukit/libdrvmgr

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