| .. _modules: |
| |
| Modules |
| ======= |
| |
| Modules add additional functionality to the core :class:`Target` interface. |
| Usually, it is support for specific subsystems on the target. Modules are |
| instantiated as attributes of the :class:`Target` instance. |
| |
| hotplug |
| ------- |
| |
| Kernel ``hotplug`` subsystem allows offlining ("removing") cores from the |
| system, and onlining them back in. The ``devlib`` module exposes a simple |
| interface to this subsystem |
| |
| .. code:: python |
| |
| from devlib import LocalLinuxTarget |
| target = LocalLinuxTarget() |
| |
| # offline cpus 2 and 3, "removing" them from the system |
| target.hotplug.offline(2, 3) |
| |
| # bring CPU 2 back in |
| target.hotplug.online(2) |
| |
| # Make sure all cpus are online |
| target.hotplug.online_all() |
| |
| cpufreq |
| ------- |
| |
| ``cpufreq`` is the kernel subsystem for managing DVFS (Dynamic Voltage and |
| Frequency Scaling). It allows controlling frequency ranges and switching |
| policies (governors). The ``devlib`` module exposes the following interface |
| |
| .. note:: On ARM big.LITTLE systems, all cores on a cluster (usually all cores |
| of the same type) are in the same frequency domain, so setting |
| ``cpufreq`` state on one core on a cluster will affect all cores on |
| that cluster. Because of this, some devices only expose cpufreq sysfs |
| interface (which is what is used by the ``devlib`` module) on the |
| first cpu in a cluster. So to keep your scripts portable, always use |
| the fist (online) CPU in a cluster to set ``cpufreq`` state. |
| |
| .. method:: target.cpufreq.list_governors(cpu) |
| |
| List cpufreq governors available for the specified cpu. Returns a list of |
| strings. |
| |
| :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| ``1`` or ``"cpu1"``). |
| |
| .. method:: target.cpufreq.list_governor_tunables(cpu) |
| |
| List the tunables for the specified cpu's current governor. |
| |
| :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| ``1`` or ``"cpu1"``). |
| |
| |
| .. method:: target.cpufreq.get_governor(cpu) |
| |
| Returns the name of the currently set governor for the specified cpu. |
| |
| :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| ``1`` or ``"cpu1"``). |
| |
| .. method:: target.cpufreq.set_governor(cpu, governor, \*\*kwargs) |
| |
| Sets the governor for the specified cpu. |
| |
| :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| ``1`` or ``"cpu1"``). |
| :param governor: The name of the governor. This must be one of the governors |
| supported by the CPU (as returned by ``list_governors()``. |
| |
| Keyword arguments may be used to specify governor tunable values. |
| |
| |
| .. method:: target.cpufreq.get_governor_tunables(cpu) |
| |
| Return a dict with the values of the specified CPU's current governor. |
| |
| :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| ``1`` or ``"cpu1"``). |
| |
| .. method:: target.cpufreq.set_governor_tunables(cpu, \*\*kwargs) |
| |
| Set the tunables for the current governor on the specified CPU. |
| |
| :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| ``1`` or ``"cpu1"``). |
| |
| Keyword arguments should be used to specify tunable values. |
| |
| .. method:: target.cpufreq.list_frequencies(cpu) |
| |
| List DVFS frequencies supported by the specified CPU. Returns a list of ints. |
| |
| :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| ``1`` or ``"cpu1"``). |
| |
| .. method:: target.cpufreq.get_min_frequency(cpu) |
| target.cpufreq.get_max_frequency(cpu) |
| target.cpufreq.set_min_frequency(cpu, frequency[, exact=True]) |
| target.cpufreq.set_max_frequency(cpu, frequency[, exact=True]) |
| |
| Get and set min and max frequencies on the specified CPU. "set" functions are |
| available with all governors other than ``userspace``. |
| |
| :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| ``1`` or ``"cpu1"``). |
| :param frequency: Frequency to set. |
| |
| .. method:: target.cpufreq.get_frequency(cpu) |
| target.cpufreq.set_frequency(cpu, frequency[, exact=True]) |
| |
| Get and set current frequency on the specified CPU. ``set_frequency`` is only |
| available if the current governor is ``userspace``. |
| |
| :param cpu: The cpu; could be a numeric or the corresponding string (e.g. |
| ``1`` or ``"cpu1"``). |
| :param frequency: Frequency to set. |
| |
| cpuidle |
| ------- |
| |
| ``cpuidle`` is the kernel subsystem for managing CPU low power (idle) states. |
| |
| .. method:: target.cpuidle.get_driver() |
| |
| Return the name current cpuidle driver. |
| |
| .. method:: target.cpuidle.get_governor() |
| |
| Return the name current cpuidle governor (policy). |
| |
| .. method:: target.cpuidle.get_states([cpu=0]) |
| |
| Return idle states (optionally, for the specified CPU). Returns a list of |
| :class:`CpuidleState` instances. |
| |
| .. method:: target.cpuidle.get_state(state[, cpu=0]) |
| |
| Return :class:`CpuidleState` instance (optionally, for the specified CPU) |
| representing the specified idle state. ``state`` can be either an integer |
| index of the state or a string with the states ``name`` or ``desc``. |
| |
| .. method:: target.cpuidle.enable(state[, cpu=0]) |
| target.cpuidle.disable(state[, cpu=0]) |
| target.cpuidle.enable_all([cpu=0]) |
| target.cpuidle.disable_all([cpu=0]) |
| |
| Enable or disable the specified or all states (optionally on the specified |
| CPU. |
| |
| You can also call ``enable()`` or ``disable()`` on :class:`CpuidleState` objects |
| returned by get_state(s). |
| |
| cgroups |
| ------- |
| |
| TODO |
| |
| hwmon |
| ----- |
| |
| TODO |
| |
| API |
| --- |
| |
| Generic Module API Description |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Modules implement discrete, optional pieces of functionality ("optional" in the |
| sense that the functionality may or may not be present on the target device, or |
| that it may or may not be necessary for a particular application). |
| |
| Every module (ultimately) derives from :class:`Module` class. A module must |
| define the following class attributes: |
| |
| :name: A unique name for the module. This cannot clash with any of the existing |
| names and must be a valid Python identifier, but is otherwise free-form. |
| :kind: This identifies the type of functionality a module implements, which in |
| turn determines the interface implemented by the module (all modules of |
| the same kind must expose a consistent interface). This must be a valid |
| Python identifier, but is otherwise free-form, though, where possible, |
| one should try to stick to an already-defined kind/interface, lest we end |
| up with a bunch of modules implementing similar functionality but |
| exposing slightly different interfaces. |
| |
| .. note:: It is possible to omit ``kind`` when defining a module, in |
| which case the module's ``name`` will be treated as its |
| ``kind`` as well. |
| |
| :stage: This defines when the module will be installed into a :class:`Target`. |
| Currently, the following values are allowed: |
| |
| :connected: The module is installed after a connection to the target has |
| been established. This is the default. |
| :early: The module will be installed when a :class:`Target` is first |
| created. This should be used for modules that do not rely on a |
| live connection to the target. |
| |
| Additionally, a module must implement a static (or class) method :func:`probe`: |
| |
| .. method:: Module.probe(target) |
| |
| This method takes a :class:`Target` instance and returns ``True`` if this |
| module is supported by that target, or ``False`` otherwise. |
| |
| .. note:: If the module ``stage`` is ``"early"``, this method cannot assume |
| that a connection has been established (i.e. it can only access |
| attributes of the Target that do not rely on a connection). |
| |
| Installation and invocation |
| *************************** |
| |
| The default installation method will create an instance of a module (the |
| :class:`Target` instance being the sole argument) and assign it to the target |
| instance attribute named after the module's ``kind`` (or ``name`` if ``kind`` is |
| ``None``). |
| |
| It is possible to change the installation procedure for a module by overriding |
| the default :func:`install` method. The method must have the following |
| signature: |
| |
| .. method:: Module.install(cls, target, **kwargs) |
| |
| Install the module into the target instance. |
| |
| |
| Implementation and Usage Patterns |
| ********************************* |
| |
| There are two common ways to implement the above API, corresponding to the two |
| common uses for modules: |
| |
| - If a module provides an interface to a particular set of functionality (e.g. |
| an OS subsystem), that module would typically derive directly form |
| :class:`Module` and would leave ``kind`` unassigned, so that it is accessed |
| by it name. Its instance's methods and attributes provide the interface for |
| interacting with its functionality. For examples of this type of module, see |
| the subsystem modules listed above (e.g. ``cpufreq``). |
| - If a module provides a platform- or infrastructure-specific implementation of |
| a common function, the module would derive from one of :class:`Module` |
| subclasses that define the interface for that function. In that case the |
| module would be accessible via the common ``kind`` defined its super. The |
| module would typically implement :func:`__call__` and be invoked directly. For |
| examples of this type of module, see common function interface definitions |
| below. |
| |
| |
| Common Function Interfaces |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| This section documents :class:`Module` classes defining interface for common |
| functions. Classes derived from them provide concrete implementations for |
| specific platforms. |
| |
| |
| HardResetModule |
| *************** |
| |
| .. attribute:: HardResetModule.kind |
| |
| "hard_reset" |
| |
| .. method:: HardResetModule.__call__() |
| |
| Must be implemented by derived classes. |
| |
| Implements hard reset for a target devices. The equivalent of physically |
| power cycling the device. This may be used by client code in situations |
| where the target becomes unresponsive and/or a regular reboot is not |
| possible. |
| |
| |
| BootModule |
| ********** |
| |
| .. attribute:: BootModule.kind |
| |
| "hard_reset" |
| |
| .. method:: BootModule.__call__() |
| |
| Must be implemented by derived classes. |
| |
| Implements a boot procedure. This takes the device from (hard or soft) |
| reset to a booted state where the device is ready to accept connections. For |
| a lot of commercial devices the process is entirely automatic, however some |
| devices (e.g. development boards), my require additional steps, such as |
| interactions with the bootloader, in order to boot into the OS. |
| |
| .. method:: Bootmodule.update(\*\*kwargs) |
| |
| Update the boot settings. Some boot sequences allow specifying settings |
| that will be utilized during boot (e.g. linux kernel boot command line). The |
| default implementation will set each setting in ``kwargs`` as an attribute of |
| the boot module (or update the existing attribute). |
| |
| |
| FlashModule |
| *********** |
| |
| .. attribute:: FlashModule.kind |
| |
| "flash" |
| |
| .. method:: __call__(image_bundle=None, images=None, boot_config=None) |
| |
| Must be implemented by derived classes. |
| |
| Flash the target platform with the specified images. |
| |
| :param image_bundle: A compressed bundle of image files with any associated |
| metadata. The format of the bundle is specific to a |
| particular implementation. |
| :param images: A dict mapping image names/identifiers to the path on the |
| host file system of the corresponding image file. If both |
| this and ``image_bundle`` are specified, individual images |
| will override those in the bundle. |
| :param boot_config: Some platforms require specifying boot arguments at the |
| time of flashing the images, rather than during each |
| reboot. For other platforms, this will be ignored. |
| |
| |
| Module Registration |
| ~~~~~~~~~~~~~~~~~~~ |
| |
| Modules are specified on :class:`Target` or :class:`Platform` creation by name. |
| In order to find the class associated with the name, the module needs to be |
| registered with ``devlib``. This is accomplished by passing the module class |
| into :func:`register_module` method once it is defined. |
| |
| .. note:: If you're wiring a module to be included as part of ``devlib`` code |
| base, you can place the file with the module class under |
| ``devlib/modules/`` in the source and it will be automatically |
| enumerated. There is no need to explicitly register it in that case. |
| |
| The code snippet below illustrates an implementation of a hard reset function |
| for an "Acme" device. |
| |
| .. code:: python |
| |
| import os |
| from devlib import HardResetModule, register_module |
| |
| |
| class AcmeHardReset(HardResetModule): |
| |
| name = 'acme_hard_reset' |
| |
| def __call__(self): |
| # Assuming Acme board comes with a "reset-acme-board" utility |
| os.system('reset-acme-board {}'.format(self.target.name)) |
| |
| register_module(AcmeHardReset) |
| |