]>
Commit | Line | Data |
---|---|---|
3f466933 PH |
1 | The U-Boot Driver Model Project |
2 | =============================== | |
3 | Driver cores API document | |
4 | ========================= | |
5 | ||
6 | Pavel Herrmann <morpheus.ibis@gmail.com> | |
7 | ||
8 | 1) Overview | |
9 | ----------- | |
10 | Driver cores will be used as a wrapper for devices of the same type, and as | |
11 | an abstraction for device driver APIs. For each driver API (which roughly | |
12 | correspond to device types), there will be one driver core. Each driver core | |
13 | will implement three APIs - a driver API (which will be the same as API of | |
14 | drivers the core wraps around), a core API (which will be implemented by all | |
15 | cores) and a command API (core-specific API which will be exposed to | |
16 | commands). | |
17 | ||
18 | A) Command API | |
19 | The command API will provide access to shared functionality for a specific | |
20 | device, which is currently located mostly in commands. Commands will be | |
21 | rewritten to be more lightweight by using this API. As this API will be | |
22 | different for each core, it is out of scope of this document. | |
23 | ||
24 | B) Driver API | |
25 | The driver API will act as a wrapper around actual device drivers, | |
26 | providing a single entrypoint for device access. All functions in this API | |
27 | have an instance* argument (probably called "this" or "i"), which will be | |
28 | examined by the core, and a correct function for the specified driver will | |
29 | get called. | |
30 | ||
31 | If the core gets called with a group instance pointer (as discussed in | |
32 | design), it will automatically select the instance that is associated | |
33 | with this core, and use it as target of the call. if the group contains | |
34 | multiple instances of a single type, the caller must explicitly use an | |
35 | accessor to select the correct instance. | |
36 | ||
37 | This accessor will look like: | |
38 | struct instance *get_instance_from_group(struct instance *group, int i) | |
39 | ||
40 | When called with a non-group instance, it will simply return the instance. | |
41 | ||
42 | C) Core API | |
43 | The core API will be implemented by all cores, and will provide | |
44 | functionality for getting driver instances from non-driver code. This API | |
45 | will consist of following functions: | |
46 | ||
47 | int get_count(struct instance *core); | |
48 | struct instance* get_instance(struct instance *core, int index); | |
49 | int init(struct instance *core); | |
50 | int bind(struct instance *core, struct instance *dev, void *ops, | |
51 | void *hint); | |
52 | int unbind(struct instance *core, instance *dev); | |
53 | int replace(struct instance *core, struct_instance *new_dev, | |
54 | struct instance *old_dev); | |
55 | int destroy(struct instance *core); | |
56 | int reloc(struct instance *new_core, struct instance *old_core); | |
57 | ||
58 | The 'hint' parameter of bind() serves for additional data a driver can | |
59 | pass to the core, to help it create the correct internal state for this | |
60 | instance. the replace() function will get called during instance | |
61 | relocation, and will replace the old instance with the new one, keeping | |
62 | the internal state untouched. | |
63 | ||
64 | ||
65 | 2) Lifetime of a driver core | |
66 | ---------------------------- | |
67 | Driver cores will be initialized at runtime, to limit memory footprint in | |
68 | early-init stage, when we have to fit into ~1KB of memory. All active cores | |
69 | will be stored in a tree structure (referenced as "Core tree") in global data, | |
70 | which provides good tradeoff between size and access time. | |
71 | Every core will have a number constant associated with it, which will be used | |
72 | to find the instance in Core tree, and to refer to the core in all calls | |
73 | working with the Core tree. | |
74 | The Core Tree should be implemented using B-tree (or a similar structure) | |
75 | to guarantee acceptable time overhead in all cases. | |
76 | ||
77 | Code for working with the core (i2c in this example) follows: | |
78 | ||
79 | core_init(CORE_I2C); | |
80 | This will check whether we already have a i2c core, and if not it creates | |
81 | a new instance and adds it into the Core tree. This will not be exported, | |
82 | all code should depend on get_core_instance to init the core when | |
83 | necessary. | |
84 | ||
85 | get_core_instance(CORE_I2C); | |
86 | This is an accessor into the Core tree, which will return the instance | |
87 | of i2c core, creating it if necessary | |
88 | ||
89 | core_bind(CORE_I2C, instance, driver_ops); | |
90 | This will get called in bind() function of a driver, and will add the | |
91 | instance into cores internal list of devices. If the core is not found, it | |
92 | will get created. | |
93 | ||
94 | driver_activate(instance *inst); | |
95 | This call will recursively activate all devices necessary for using the | |
96 | specified device. the code could be simplified as: | |
93e14596 WD |
97 | { |
98 | if (is_activated(inst)) | |
99 | return; | |
100 | driver_activate(inst->bus); | |
101 | get_driver(inst)->probe(inst); | |
102 | } | |
3f466933 PH |
103 | |
104 | The case with multiple parents will need to be handled here as well. | |
105 | get_driver is an accessor to available drivers, which will get struct | |
106 | driver based on a name in the instance. | |
107 | ||
108 | i2c_write(instance *inst, ...); | |
109 | An actual call to some method of the driver. This code will look like: | |
93e14596 WD |
110 | { |
111 | driver_activate(inst); | |
112 | struct instance *core = get_core_instance(CORE_I2C); | |
113 | device_ops = get_ops(inst); | |
114 | device_ops->write(...); | |
115 | } | |
3f466933 PH |
116 | |
117 | get_ops will not be an exported function, it will be internal and specific | |
118 | to the core, as it needs to know how are the ops stored, and what type | |
119 | they are. | |
120 | ||
121 | Please note that above examples represent the algorithm, not the actual code, | |
122 | as they are missing checks for validity of return values. | |
123 | ||
124 | core_init() function will get called the first time the core is requested, | |
125 | either by core_link() or core_get_instance(). This way, the cores will get | |
126 | created only when they are necessary, which will reduce our memory footprint. |