Pavel Herrmann | 732527c | 2012-08-08 01:42:18 +0000 | [diff] [blame] | 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: |
| 97 | { |
| 98 | if (is_activated(inst)) |
| 99 | return; |
| 100 | driver_activate(inst->bus); |
| 101 | get_driver(inst)->probe(inst); |
| 102 | } |
| 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: |
| 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 | } |
| 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. |