Sergei Trofimov | 1ba7fbd | 2016-12-09 15:06:35 +0000 | [diff] [blame] | 1 | .. _modules: |
| 2 | |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 3 | Modules |
| 4 | ======= |
| 5 | |
| 6 | Modules add additional functionality to the core :class:`Target` interface. |
| 7 | Usually, it is support for specific subsystems on the target. Modules are |
| 8 | instantiated as attributes of the :class:`Target` instance. |
| 9 | |
| 10 | hotplug |
| 11 | ------- |
| 12 | |
| 13 | Kernel ``hotplug`` subsystem allows offlining ("removing") cores from the |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 14 | system, and onlining them back in. The ``devlib`` module exposes a simple |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 15 | interface to this subsystem |
| 16 | |
| 17 | .. code:: python |
| 18 | |
| 19 | from devlib import LocalLinuxTarget |
| 20 | target = LocalLinuxTarget() |
| 21 | |
| 22 | # offline cpus 2 and 3, "removing" them from the system |
| 23 | target.hotplug.offline(2, 3) |
| 24 | |
| 25 | # bring CPU 2 back in |
| 26 | target.hotplug.online(2) |
| 27 | |
| 28 | # Make sure all cpus are online |
| 29 | target.hotplug.online_all() |
| 30 | |
| 31 | cpufreq |
| 32 | ------- |
| 33 | |
| 34 | ``cpufreq`` is the kernel subsystem for managing DVFS (Dynamic Voltage and |
| 35 | Frequency Scaling). It allows controlling frequency ranges and switching |
| 36 | policies (governors). The ``devlib`` module exposes the following interface |
| 37 | |
| 38 | .. note:: On ARM big.LITTLE systems, all cores on a cluster (usually all cores |
| 39 | of the same type) are in the same frequency domain, so setting |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 40 | ``cpufreq`` state on one core on a cluster will affect all cores on |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 41 | that cluster. Because of this, some devices only expose cpufreq sysfs |
| 42 | interface (which is what is used by the ``devlib`` module) on the |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 43 | first cpu in a cluster. So to keep your scripts portable, always use |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 44 | the fist (online) CPU in a cluster to set ``cpufreq`` state. |
| 45 | |
| 46 | .. method:: target.cpufreq.list_governors(cpu) |
| 47 | |
| 48 | List cpufreq governors available for the specified cpu. Returns a list of |
| 49 | strings. |
| 50 | |
| 51 | :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| 52 | ``1`` or ``"cpu1"``). |
| 53 | |
| 54 | .. method:: target.cpufreq.list_governor_tunables(cpu) |
| 55 | |
| 56 | List the tunables for the specified cpu's current governor. |
| 57 | |
| 58 | :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| 59 | ``1`` or ``"cpu1"``). |
| 60 | |
| 61 | |
| 62 | .. method:: target.cpufreq.get_governor(cpu) |
| 63 | |
| 64 | Returns the name of the currently set governor for the specified cpu. |
| 65 | |
| 66 | :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| 67 | ``1`` or ``"cpu1"``). |
| 68 | |
Sergei Trofimov | 1ba7fbd | 2016-12-09 15:06:35 +0000 | [diff] [blame] | 69 | .. method:: target.cpufreq.set_governor(cpu, governor, \*\*kwargs) |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 70 | |
| 71 | Sets the governor for the specified cpu. |
| 72 | |
| 73 | :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| 74 | ``1`` or ``"cpu1"``). |
Marc Bonnici | 1fd5636 | 2017-01-10 15:35:21 +0000 | [diff] [blame] | 75 | :param governor: The name of the governor. This must be one of the governors |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 76 | supported by the CPU (as returned by ``list_governors()``. |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 77 | |
| 78 | Keyword arguments may be used to specify governor tunable values. |
| 79 | |
| 80 | |
| 81 | .. method:: target.cpufreq.get_governor_tunables(cpu) |
| 82 | |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 83 | Return a dict with the values of the specified CPU's current governor. |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 84 | |
| 85 | :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| 86 | ``1`` or ``"cpu1"``). |
| 87 | |
Sergei Trofimov | 1ba7fbd | 2016-12-09 15:06:35 +0000 | [diff] [blame] | 88 | .. method:: target.cpufreq.set_governor_tunables(cpu, \*\*kwargs) |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 89 | |
| 90 | Set the tunables for the current governor on the specified CPU. |
| 91 | |
| 92 | :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| 93 | ``1`` or ``"cpu1"``). |
| 94 | |
| 95 | Keyword arguments should be used to specify tunable values. |
| 96 | |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 97 | .. method:: target.cpufreq.list_frequencies(cpu) |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 98 | |
| 99 | List DVFS frequencies supported by the specified CPU. Returns a list of ints. |
| 100 | |
| 101 | :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| 102 | ``1`` or ``"cpu1"``). |
| 103 | |
| 104 | .. method:: target.cpufreq.get_min_frequency(cpu) |
| 105 | target.cpufreq.get_max_frequency(cpu) |
| 106 | target.cpufreq.set_min_frequency(cpu, frequency[, exact=True]) |
| 107 | target.cpufreq.set_max_frequency(cpu, frequency[, exact=True]) |
| 108 | |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 109 | Get and set min and max frequencies on the specified CPU. "set" functions are |
| 110 | available with all governors other than ``userspace``. |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 111 | |
| 112 | :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| 113 | ``1`` or ``"cpu1"``). |
| 114 | :param frequency: Frequency to set. |
| 115 | |
| 116 | .. method:: target.cpufreq.get_frequency(cpu) |
| 117 | target.cpufreq.set_frequency(cpu, frequency[, exact=True]) |
| 118 | |
| 119 | Get and set current frequency on the specified CPU. ``set_frequency`` is only |
| 120 | available if the current governor is ``userspace``. |
| 121 | |
| 122 | :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| 123 | ``1`` or ``"cpu1"``). |
| 124 | :param frequency: Frequency to set. |
| 125 | |
| 126 | cpuidle |
| 127 | ------- |
| 128 | |
Marc Bonnici | 1fd5636 | 2017-01-10 15:35:21 +0000 | [diff] [blame] | 129 | ``cpuidle`` is the kernel subsystem for managing CPU low power (idle) states. |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 130 | |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 131 | .. method:: target.cpuidle.get_driver() |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 132 | |
| 133 | Return the name current cpuidle driver. |
| 134 | |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 135 | .. method:: target.cpuidle.get_governor() |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 136 | |
| 137 | Return the name current cpuidle governor (policy). |
| 138 | |
| 139 | .. method:: target.cpuidle.get_states([cpu=0]) |
| 140 | |
| 141 | Return idle states (optionally, for the specified CPU). Returns a list of |
| 142 | :class:`CpuidleState` instances. |
| 143 | |
| 144 | .. method:: target.cpuidle.get_state(state[, cpu=0]) |
| 145 | |
| 146 | Return :class:`CpuidleState` instance (optionally, for the specified CPU) |
| 147 | representing the specified idle state. ``state`` can be either an integer |
| 148 | index of the state or a string with the states ``name`` or ``desc``. |
| 149 | |
| 150 | .. method:: target.cpuidle.enable(state[, cpu=0]) |
| 151 | target.cpuidle.disable(state[, cpu=0]) |
| 152 | target.cpuidle.enable_all([cpu=0]) |
| 153 | target.cpuidle.disable_all([cpu=0]) |
| 154 | |
| 155 | Enable or disable the specified or all states (optionally on the specified |
| 156 | CPU. |
| 157 | |
Marc Bonnici | 1fd5636 | 2017-01-10 15:35:21 +0000 | [diff] [blame] | 158 | You can also call ``enable()`` or ``disable()`` on :class:`CpuidleState` objects |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 159 | returned by get_state(s). |
| 160 | |
| 161 | cgroups |
| 162 | ------- |
| 163 | |
| 164 | TODO |
| 165 | |
| 166 | hwmon |
| 167 | ----- |
| 168 | |
| 169 | TODO |
| 170 | |
| 171 | API |
| 172 | --- |
| 173 | |
Sergei Trofimov | 1ba7fbd | 2016-12-09 15:06:35 +0000 | [diff] [blame] | 174 | Generic Module API Description |
| 175 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 176 | |
| 177 | Modules implement discrete, optional pieces of functionality ("optional" in the |
| 178 | sense that the functionality may or may not be present on the target device, or |
| 179 | that it may or may not be necessary for a particular application). |
| 180 | |
| 181 | Every module (ultimately) derives from :class:`Module` class. A module must |
| 182 | define the following class attributes: |
| 183 | |
| 184 | :name: A unique name for the module. This cannot clash with any of the existing |
Marc Bonnici | 1fd5636 | 2017-01-10 15:35:21 +0000 | [diff] [blame] | 185 | names and must be a valid Python identifier, but is otherwise free-form. |
Sergei Trofimov | 1ba7fbd | 2016-12-09 15:06:35 +0000 | [diff] [blame] | 186 | :kind: This identifies the type of functionality a module implements, which in |
| 187 | turn determines the interface implemented by the module (all modules of |
| 188 | the same kind must expose a consistent interface). This must be a valid |
| 189 | Python identifier, but is otherwise free-form, though, where possible, |
| 190 | one should try to stick to an already-defined kind/interface, lest we end |
| 191 | up with a bunch of modules implementing similar functionality but |
| 192 | exposing slightly different interfaces. |
| 193 | |
| 194 | .. note:: It is possible to omit ``kind`` when defining a module, in |
| 195 | which case the module's ``name`` will be treated as its |
| 196 | ``kind`` as well. |
| 197 | |
| 198 | :stage: This defines when the module will be installed into a :class:`Target`. |
| 199 | Currently, the following values are allowed: |
| 200 | |
| 201 | :connected: The module is installed after a connection to the target has |
| 202 | been established. This is the default. |
| 203 | :early: The module will be installed when a :class:`Target` is first |
| 204 | created. This should be used for modules that do not rely on a |
| 205 | live connection to the target. |
| 206 | |
| 207 | Additionally, a module must implement a static (or class) method :func:`probe`: |
| 208 | |
| 209 | .. method:: Module.probe(target) |
| 210 | |
| 211 | This method takes a :class:`Target` instance and returns ``True`` if this |
| 212 | module is supported by that target, or ``False`` otherwise. |
| 213 | |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 214 | .. note:: If the module ``stage`` is ``"early"``, this method cannot assume |
Sergei Trofimov | 1ba7fbd | 2016-12-09 15:06:35 +0000 | [diff] [blame] | 215 | that a connection has been established (i.e. it can only access |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 216 | attributes of the Target that do not rely on a connection). |
Sergei Trofimov | 1ba7fbd | 2016-12-09 15:06:35 +0000 | [diff] [blame] | 217 | |
| 218 | Installation and invocation |
| 219 | *************************** |
| 220 | |
| 221 | The default installation method will create an instance of a module (the |
| 222 | :class:`Target` instance being the sole argument) and assign it to the target |
| 223 | instance attribute named after the module's ``kind`` (or ``name`` if ``kind`` is |
| 224 | ``None``). |
| 225 | |
| 226 | It is possible to change the installation procedure for a module by overriding |
| 227 | the default :func:`install` method. The method must have the following |
| 228 | signature: |
| 229 | |
| 230 | .. method:: Module.install(cls, target, **kwargs) |
| 231 | |
| 232 | Install the module into the target instance. |
| 233 | |
| 234 | |
| 235 | Implementation and Usage Patterns |
| 236 | ********************************* |
| 237 | |
| 238 | There are two common ways to implement the above API, corresponding to the two |
| 239 | common uses for modules: |
| 240 | |
| 241 | - If a module provides an interface to a particular set of functionality (e.g. |
| 242 | an OS subsystem), that module would typically derive directly form |
| 243 | :class:`Module` and would leave ``kind`` unassigned, so that it is accessed |
| 244 | by it name. Its instance's methods and attributes provide the interface for |
| 245 | interacting with its functionality. For examples of this type of module, see |
| 246 | the subsystem modules listed above (e.g. ``cpufreq``). |
| 247 | - If a module provides a platform- or infrastructure-specific implementation of |
| 248 | a common function, the module would derive from one of :class:`Module` |
| 249 | subclasses that define the interface for that function. In that case the |
| 250 | module would be accessible via the common ``kind`` defined its super. The |
| 251 | module would typically implement :func:`__call__` and be invoked directly. For |
| 252 | examples of this type of module, see common function interface definitions |
| 253 | below. |
| 254 | |
| 255 | |
| 256 | Common Function Interfaces |
| 257 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 258 | |
| 259 | This section documents :class:`Module` classes defining interface for common |
| 260 | functions. Classes derived from them provide concrete implementations for |
| 261 | specific platforms. |
| 262 | |
| 263 | |
| 264 | HardResetModule |
| 265 | *************** |
| 266 | |
| 267 | .. attribute:: HardResetModule.kind |
| 268 | |
| 269 | "hard_reset" |
| 270 | |
| 271 | .. method:: HardResetModule.__call__() |
| 272 | |
| 273 | Must be implemented by derived classes. |
Marc Bonnici | 1fd5636 | 2017-01-10 15:35:21 +0000 | [diff] [blame] | 274 | |
Sergei Trofimov | 1ba7fbd | 2016-12-09 15:06:35 +0000 | [diff] [blame] | 275 | Implements hard reset for a target devices. The equivalent of physically |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 276 | power cycling the device. This may be used by client code in situations |
Sergei Trofimov | 1ba7fbd | 2016-12-09 15:06:35 +0000 | [diff] [blame] | 277 | where the target becomes unresponsive and/or a regular reboot is not |
| 278 | possible. |
| 279 | |
| 280 | |
| 281 | BootModule |
| 282 | ********** |
| 283 | |
| 284 | .. attribute:: BootModule.kind |
| 285 | |
| 286 | "hard_reset" |
| 287 | |
| 288 | .. method:: BootModule.__call__() |
| 289 | |
| 290 | Must be implemented by derived classes. |
| 291 | |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 292 | Implements a boot procedure. This takes the device from (hard or soft) |
Sergei Trofimov | 1ba7fbd | 2016-12-09 15:06:35 +0000 | [diff] [blame] | 293 | reset to a booted state where the device is ready to accept connections. For |
| 294 | a lot of commercial devices the process is entirely automatic, however some |
| 295 | devices (e.g. development boards), my require additional steps, such as |
| 296 | interactions with the bootloader, in order to boot into the OS. |
| 297 | |
| 298 | .. method:: Bootmodule.update(\*\*kwargs) |
| 299 | |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 300 | Update the boot settings. Some boot sequences allow specifying settings |
Sergei Trofimov | 1ba7fbd | 2016-12-09 15:06:35 +0000 | [diff] [blame] | 301 | that will be utilized during boot (e.g. linux kernel boot command line). The |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 302 | default implementation will set each setting in ``kwargs`` as an attribute of |
Sergei Trofimov | 1ba7fbd | 2016-12-09 15:06:35 +0000 | [diff] [blame] | 303 | the boot module (or update the existing attribute). |
| 304 | |
| 305 | |
| 306 | FlashModule |
| 307 | *********** |
| 308 | |
| 309 | .. attribute:: FlashModule.kind |
| 310 | |
| 311 | "flash" |
| 312 | |
| 313 | .. method:: __call__(image_bundle=None, images=None, boot_config=None) |
| 314 | |
| 315 | Must be implemented by derived classes. |
| 316 | |
| 317 | Flash the target platform with the specified images. |
| 318 | |
| 319 | :param image_bundle: A compressed bundle of image files with any associated |
| 320 | metadata. The format of the bundle is specific to a |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 321 | particular implementation. |
Sergei Trofimov | 1ba7fbd | 2016-12-09 15:06:35 +0000 | [diff] [blame] | 322 | :param images: A dict mapping image names/identifiers to the path on the |
| 323 | host file system of the corresponding image file. If both |
| 324 | this and ``image_bundle`` are specified, individual images |
| 325 | will override those in the bundle. |
| 326 | :param boot_config: Some platforms require specifying boot arguments at the |
| 327 | time of flashing the images, rather than during each |
| 328 | reboot. For other platforms, this will be ignored. |
| 329 | |
| 330 | |
| 331 | Module Registration |
| 332 | ~~~~~~~~~~~~~~~~~~~ |
| 333 | |
| 334 | Modules are specified on :class:`Target` or :class:`Platform` creation by name. |
| 335 | In order to find the class associated with the name, the module needs to be |
| 336 | registered with ``devlib``. This is accomplished by passing the module class |
| 337 | into :func:`register_module` method once it is defined. |
| 338 | |
| 339 | .. note:: If you're wiring a module to be included as part of ``devlib`` code |
| 340 | base, you can place the file with the module class under |
| 341 | ``devlib/modules/`` in the source and it will be automatically |
Marc Bonnici | 8733b9c | 2017-02-28 17:40:44 +0000 | [diff] [blame] | 342 | enumerated. There is no need to explicitly register it in that case. |
Sergei Trofimov | 1ba7fbd | 2016-12-09 15:06:35 +0000 | [diff] [blame] | 343 | |
| 344 | The code snippet below illustrates an implementation of a hard reset function |
| 345 | for an "Acme" device. |
| 346 | |
| 347 | .. code:: python |
| 348 | |
| 349 | import os |
| 350 | from devlib import HardResetModule, register_module |
| 351 | |
| 352 | |
| 353 | class AcmeHardReset(HardResetModule): |
| 354 | |
| 355 | name = 'acme_hard_reset' |
| 356 | |
| 357 | def __call__(self): |
Marc Bonnici | 1fd5636 | 2017-01-10 15:35:21 +0000 | [diff] [blame] | 358 | # Assuming Acme board comes with a "reset-acme-board" utility |
Sergei Trofimov | 1ba7fbd | 2016-12-09 15:06:35 +0000 | [diff] [blame] | 359 | os.system('reset-acme-board {}'.format(self.target.name)) |
| 360 | |
| 361 | register_module(AcmeHardReset) |
| 362 | |