diff --git a/devlib/module/thermal.py b/devlib/module/thermal.py index 8220b22..f442c51 100644 --- a/devlib/module/thermal.py +++ b/devlib/module/thermal.py @@ -13,8 +13,11 @@ # limitations under the License. import re +import logging +import devlib.utils.asyn as asyn from devlib.module import Module +from devlib.exception import TargetStableCalledProcessError class TripPoint(object): def __init__(self, zone, _id): @@ -27,19 +30,22 @@ class TripPoint(object): def target(self): return self.zone.target - def get_temperature(self): + @asyn.asyncf + async def get_temperature(self): """Returns the currently configured temperature of the trip point""" temp_file = self.target.path.join(self.zone.path, self.temp_node) - return self.target.read_int(temp_file) + return await self.target.read_int.asyn(temp_file) - def set_temperature(self, temperature): + @asyn.asyncf + async def set_temperature(self, temperature): temp_file = self.target.path.join(self.zone.path, self.temp_node) - self.target.write_value(temp_file, temperature) + await self.target.write_value.asyn(temp_file, temperature) - def get_type(self): + @asyn.asyncf + async def get_type(self): """Returns the type of trip point""" type_file = self.target.path.join(self.zone.path, self.type_node) - return self.target.read_value(type_file) + return await self.target.read_value.asyn(type_file) class ThermalZone(object): def __init__(self, target, root, _id): @@ -47,6 +53,7 @@ class ThermalZone(object): self.name = 'thermal_zone' + _id self.path = target.path.join(root, self.name) self.trip_points = {} + self.type = self.target.read_value(self.target.path.join(self.path, 'type')) for entry in self.target.list_directory(self.path, as_root=target.is_rooted): re_match = re.match('^trip_point_([0-9]+)_temp', entry) @@ -56,19 +63,70 @@ class ThermalZone(object): def add_trip_point(self, _id): self.trip_points[int(_id)] = TripPoint(self, _id) - def is_enabled(self): + @asyn.asyncf + async def is_enabled(self): """Returns a boolean representing the 'mode' of the thermal zone""" - value = self.target.read_value(self.target.path.join(self.path, 'mode')) + value = await self.target.read_value.asyn(self.target.path.join(self.path, 'mode')) return value == 'enabled' - def set_enabled(self, enabled=True): + @asyn.asyncf + async def set_enabled(self, enabled=True): value = 'enabled' if enabled else 'disabled' - self.target.write_value(self.target.path.join(self.path, 'mode'), value) + await self.target.write_value.asyn(self.target.path.join(self.path, 'mode'), value) - def get_temperature(self): + @asyn.asyncf + async def get_temperature(self): """Returns the temperature of the thermal zone""" - temp_file = self.target.path.join(self.path, 'temp') - return self.target.read_int(temp_file) + sysfs_temperature_file = self.target.path.join(self.path, 'temp') + return await self.target.read_int.asyn(sysfs_temperature_file) + + @asyn.asyncf + async def get_policy(self): + """Returns the policy of the thermal zone""" + temp_file = self.target.path.join(self.path, 'policy') + return await self.target.read_value.asyn(temp_file) + + @asyn.asyncf + async def set_policy(self, policy): + """ + Sets the policy of the thermal zone + + :params policy: Thermal governor name + :type policy: str + """ + await self.target.write_value.asyn(self.target.path.join(self.path, 'policy'), policy) + + @asyn.asyncf + async def get_offset(self): + """Returns the temperature offset of the thermal zone""" + offset_file = self.target.path.join(self.path, 'offset') + return await self.target.read_value.asyn(offset_file) + + @asyn.asyncf + async def set_offset(self, offset): + """ + Sets the temperature offset in milli-degrees of the thermal zone + + :params offset: Temperature offset in milli-degrees + :type policy: int + """ + await self.target.write_value.asyn(self.target.path.join(self.path, 'offset'), policy) + + @asyn.asyncf + async def set_emul_temp(self, offset): + """ + Sets the emulated temperature in milli-degrees of the thermal zone + + :params offset: Emulated temperature in milli-degrees + :type policy: int + """ + await self.target.write_value.asyn(self.target.path.join(self.path, 'emul_temp'), policy) + + @asyn.asyncf + async def get_available_policies(self): + """Returns the policies available for the thermal zone""" + temp_file = self.target.path.join(self.path, 'available_policies') + return await self.target.read_value.asyn(temp_file) class ThermalModule(Module): name = 'thermal' @@ -83,6 +141,9 @@ class ThermalModule(Module): def __init__(self, target): super(ThermalModule, self).__init__(target) + self.logger = logging.getLogger(self.name) + self.logger.debug('Initialized [%s] module', self.name) + self.zones = {} self.cdevs = [] @@ -105,3 +166,32 @@ class ThermalModule(Module): """Disables all the thermal zones in the target""" for zone in self.zones.values(): zone.set_enabled(False) + + @asyn.asyncf + async def get_all_temperatures(self, error='raise'): + """ + Returns dictionary with current reading of all thermal zones. + + :params error: Sensor read error handling (raise or ignore) + :type error: str + + :returns: a dictionary in the form: {tz_type:temperature} + """ + + async def get_temperature_noexcep(item): + tzid, tz = item + try: + temperature = await tz.get_temperature.asyn() + except TargetStableCalledProcessError as e: + if error == 'raise': + raise e + elif error == 'ignore': + self.logger.warning(f'Skipping thermal_zone_id={tzid} thermal_zone_type={tz.type} error="{e}"') + return None + else: + raise ValueError(f'Unknown error parameter value: {error}') + return temperature + + tz_temps = await self.target.async_manager.map_concurrently(get_temperature_noexcep, self.zones.items()) + + return {tz.type: temperature for (tzid, tz), temperature in tz_temps.items() if temperature is not None}