diff --git a/wlauto/common/linux/device.py b/wlauto/common/linux/device.py index d599c5b0..b9f322aa 100644 --- a/wlauto/common/linux/device.py +++ b/wlauto/common/linux/device.py @@ -107,6 +107,11 @@ class BaseLinuxDevice(Device): # pylint: disable=abstract-method value_name='tunables'), ] + dynamic_modules = [ + 'devcpufreq', + 'cpuidle', + ] + @property def abi(self): if not self._abi: diff --git a/wlauto/core/device.py b/wlauto/core/device.py index 121ca4a0..4587d909 100644 --- a/wlauto/core/device.py +++ b/wlauto/core/device.py @@ -35,6 +35,7 @@ from collections import OrderedDict from contextlib import contextmanager from wlauto.core.extension import Extension, ExtensionMeta, AttributeCollection, Parameter +from wlauto.core.extension_loader import ExtensionLoader from wlauto.exceptions import DeviceError, ConfigError from wlauto.utils.types import list_of_integers, list_of, caseless_string @@ -93,10 +94,34 @@ class CoreParameter(RuntimeParameter): return params +class DynamicModuleSpec(dict): + + @property + def name(self): + return self.keys()[0] + + def __init__(self, *args, **kwargs): + dict.__init__(self) + if args: + if len(args) > 1: + raise ValueError(args) + value = args[0] + else: + value = kwargs + if isinstance(value, basestring): + self[value] = {} + elif isinstance(value, dict) and len(value) == 1: + for k, v in value.iteritems(): + self[k] = v + else: + raise ValueError(value) + + class DeviceMeta(ExtensionMeta): to_propagate = ExtensionMeta.to_propagate + [ ('runtime_parameters', RuntimeParameter, AttributeCollection), + ('dynamic_modules', DynamicModuleSpec, AttributeCollection), ] @@ -157,6 +182,9 @@ class Device(Extension): ] runtime_parameters = [] + # dynamic modules are loaded or not based on whether the device supports + # them (established at runtime by module probling the device). + dynamic_modules = [] # These must be overwritten by subclasses. name = None @@ -197,7 +225,17 @@ class Device(Extension): been connecte). """ - pass + loader = ExtensionLoader() + for module_spec in self.dynamic_modules: + module = self._load_module(loader, module_spec) + if not hasattr(module, 'probe'): + message = 'Module {} does not have "probe" attribute; cannot be loaded dynamically' + raise ValueError(message.format(module.name)) + if module.probe(self): + self.logger.debug('Installing module "{}"'.format(module.name)) + self._install_module(module) + else: + self.logger.debug('Module "{}" is not supported by the device'.format(module.name)) def reset(self): """ diff --git a/wlauto/core/extension.py b/wlauto/core/extension.py index 3787fcbd..6370a103 100644 --- a/wlauto/core/extension.py +++ b/wlauto/core/extension.py @@ -598,28 +598,8 @@ class Extension(object): for module_spec in modules: if not module_spec: continue - if isinstance(module_spec, basestring): - name = module_spec - params = {} - elif isinstance(module_spec, dict): - if len(module_spec) != 1: - message = 'Invalid module spec: {}; dict must have exctly one key -- the module name.' - raise ValueError(message.format(module_spec)) - name, params = module_spec.items()[0] - else: - message = 'Invalid module spec: {}; must be a string or a one-key dict.' - raise ValueError(message.format(module_spec)) - - if not isinstance(params, dict): - message = 'Invalid module spec: {}; dict value must also be a dict.' - raise ValueError(message.format(module_spec)) - - module = loader.get_module(name, owner=self, **params) - module.initialize(None) - for capability in module.capabilities: - if capability not in self.capabilities: - self.capabilities.append(capability) - self._modules.append(module) + module = self._load_module(loader, module_spec) + self._install_module(module) def has(self, capability): """Check if this extension has the specified capability. The alternative method ``can`` is @@ -629,6 +609,33 @@ class Extension(object): can = has + def _load_module(self, loader, module_spec): + if isinstance(module_spec, basestring): + name = module_spec + params = {} + elif isinstance(module_spec, dict): + if len(module_spec) != 1: + message = 'Invalid module spec: {}; dict must have exctly one key -- the module name.' + raise ValueError(message.format(module_spec)) + name, params = module_spec.items()[0] + else: + message = 'Invalid module spec: {}; must be a string or a one-key dict.' + raise ValueError(message.format(module_spec)) + + if not isinstance(params, dict): + message = 'Invalid module spec: {}; dict value must also be a dict.' + raise ValueError(message.format(module_spec)) + + module = loader.get_module(name, owner=self, **params) + module.initialize(None) + return module + + def _install_module(self, module): + for capability in module.capabilities: + if capability not in self.capabilities: + self.capabilities.append(capability) + self._modules.append(module) + def __check_from_loader(self): """ There are a few things that need to happen in order to get a valide extension instance. diff --git a/wlauto/modules/cpufreq.py b/wlauto/modules/cpufreq.py index 3a3f7695..84a819ba 100644 --- a/wlauto/modules/cpufreq.py +++ b/wlauto/modules/cpufreq.py @@ -28,6 +28,10 @@ class CpufreqModule(Module): """ capabilities = ['cpufreq'] + def probe(self, device): # pylint: disable=no-self-use + path = '/sys/devices/system/cpu/cpufreq' + return device.file_exists(path) + def initialize(self, context): # pylint: disable=W0201 CpufreqModule._available_governors = {} diff --git a/wlauto/modules/cpuidle.py b/wlauto/modules/cpuidle.py index 4c60ea40..9977e579 100644 --- a/wlauto/modules/cpuidle.py +++ b/wlauto/modules/cpuidle.py @@ -86,6 +86,9 @@ class Cpuidle(Module): root_path = '/sys/devices/system/cpu/cpuidle' + def probe(self, device): + return device.file_exists(self.root_path) + def initialize(self, context): self.device = self.root_owner signal.connect(self._on_device_init, signal.RUN_INIT, priority=1)