1 /* 2 * Copyright 2006-2008, Haiku, Inc. All Rights Reserved. 3 * 4 * Distributed under the terms of the MIT License. 5 * 6 * ACPI Generic Thermal Zone Driver. 7 * Obtains general status of passive devices, monitors / sets critical temperatures 8 * Controls active devices. 9 */ 10 11 #include <KernelExport.h> 12 #include <Drivers.h> 13 #include <Errors.h> 14 #include <string.h> 15 16 #include <stdio.h> 17 #include <stdlib.h> 18 19 #include <ACPI.h> 20 #include "acpi_thermal.h" 21 22 #define ACPI_THERMAL_MODULE_NAME "drivers/power/acpi_thermal/driver_v1" 23 24 #define ACPI_THERMAL_DEVICE_MODULE_NAME "drivers/power/acpi_thermal/device_v1" 25 26 /* Base Namespace devices are published to */ 27 #define ACPI_THERMAL_BASENAME "power/acpi_thermal/%d" 28 29 // name of pnp generator of path ids 30 #define ACPI_THERMAL_PATHID_GENERATOR "acpi_thermal/path_id" 31 32 static device_manager_info* sDeviceManager; 33 34 typedef struct acpi_ns_device_info { 35 device_node* node; 36 acpi_device_module_info* acpi; 37 acpi_device acpi_cookie; 38 uint kelvin_offset; // Initialized on first acpi_thermal_open() call. 39 } acpi_thermal_device_info; 40 41 42 status_t acpi_thermal_control(void* _cookie, uint32 op, void* arg, size_t len); 43 44 static void guess_kelvin_offset(acpi_thermal_device_info* device); 45 46 47 /* 48 * Exact value for Kelvin->Celsius conversion is 273.15, but as ACPI uses deci-Kelvins 49 * that means we only get one digit for the decimal part. Some implementations use 2731, 50 * while others use 2732. We try to guess which one here (as Linux's acpi/thermal.c driver 51 * does) by checking whether the "critical temp" value is set to something reasonable 52 * (> 0 °C), and if it is a multiple of 0.5 °C. 53 */ 54 static void 55 guess_kelvin_offset(acpi_thermal_device_info* device) 56 { 57 acpi_thermal_type therm_info; 58 59 acpi_thermal_control(device, drvOpGetThermalType, &therm_info, 0); 60 61 device->kelvin_offset = 2732; 62 if (therm_info.critical_temp > 2732 && (therm_info.critical_temp % 5) == 1) 63 device->kelvin_offset = 2731; 64 } 65 66 67 static status_t 68 acpi_thermal_open(void* _cookie, const char* path, int flags, void** cookie) 69 { 70 acpi_thermal_device_info* device = (acpi_thermal_device_info*)_cookie; 71 *cookie = device; 72 73 if (device->kelvin_offset == 0) 74 guess_kelvin_offset(device); 75 76 return B_OK; 77 } 78 79 80 static status_t 81 acpi_thermal_read(void* _cookie, off_t position, void* buf, size_t* num_bytes) 82 { 83 acpi_thermal_device_info* device = (acpi_thermal_device_info*)_cookie; 84 acpi_thermal_type therm_info; 85 86 if (*num_bytes < 1) 87 return B_IO_ERROR; 88 89 if (position == 0) { 90 size_t max_len = *num_bytes; 91 char* str = (char*)buf; 92 uint kelvinOffset = device->kelvin_offset; 93 94 acpi_thermal_control(device, drvOpGetThermalType, &therm_info, 0); 95 96 snprintf(str, max_len, " Critical Temperature: %" B_PRIu32 ".%" B_PRIu32 " °C\n", 97 ((therm_info.critical_temp - kelvinOffset) / 10), 98 ((therm_info.critical_temp - kelvinOffset) % 10)); 99 100 max_len -= strlen(str); 101 str += strlen(str); 102 snprintf(str, max_len, " Current Temperature: %" B_PRIu32 ".%" B_PRIu32 " °C\n", 103 ((therm_info.current_temp - kelvinOffset) / 10), 104 ((therm_info.current_temp - kelvinOffset) % 10)); 105 106 if (therm_info.hot_temp > 0) { 107 max_len -= strlen(str); 108 str += strlen(str); 109 snprintf(str, max_len, " Hot Temperature: %" B_PRIu32 ".%" B_PRIu32 " °C\n", 110 ((therm_info.hot_temp - kelvinOffset) / 10), 111 ((therm_info.hot_temp - kelvinOffset) % 10)); 112 } 113 114 if (therm_info.passive_package) { 115 /* Incomplete. 116 Needs to obtain acpi global lock. 117 acpi_object_type needs Reference entry (with Handle that can be resolved) 118 what you see here is _highly_ unreliable. 119 */ 120 /* if (therm_info.passive_package->data.package.count > 0) { 121 sprintf((char *)buf + *num_bytes, " Passive Devices\n"); 122 *num_bytes = strlen((char *)buf); 123 for (i = 0; i < therm_info.passive_package->data.package.count; i++) { 124 sprintf((char *)buf + *num_bytes, " Processor: %lu\n", therm_info.passive_package->data.package.objects[i].data.processor.cpu_id); 125 *num_bytes = strlen((char *)buf); 126 } 127 } 128 */ 129 free(therm_info.passive_package); 130 } 131 *num_bytes = strlen((char*)buf); 132 } else { 133 *num_bytes = 0; 134 } 135 136 return B_OK; 137 } 138 139 140 static status_t 141 acpi_thermal_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes) 142 { 143 return B_ERROR; 144 } 145 146 147 status_t 148 acpi_thermal_control(void* _cookie, uint32 op, void* arg, size_t len) 149 { 150 acpi_thermal_device_info* device = (acpi_thermal_device_info*)_cookie; 151 status_t err = B_ERROR; 152 153 acpi_thermal_type* att = NULL; 154 155 acpi_object_type object; 156 157 acpi_data buffer; 158 buffer.pointer = &object; 159 buffer.length = sizeof(object); 160 161 switch (op) { 162 case drvOpGetThermalType: { 163 att = (acpi_thermal_type*)arg; 164 165 // Read basic temperature thresholds. 166 err = device->acpi->evaluate_method (device->acpi_cookie, "_CRT", 167 NULL, &buffer); 168 169 att->critical_temp = 170 (err == B_OK && object.object_type == ACPI_TYPE_INTEGER) 171 ? object.integer.integer : 0; 172 173 err = device->acpi->evaluate_method (device->acpi_cookie, "_TMP", 174 NULL, &buffer); 175 176 att->current_temp = 177 (err == B_OK && object.object_type == ACPI_TYPE_INTEGER) 178 ? object.integer.integer : 0; 179 180 err = device->acpi->evaluate_method(device->acpi_cookie, "_HOT", 181 NULL, &buffer); 182 183 att->hot_temp = 184 (err == B_OK && object.object_type == ACPI_TYPE_INTEGER) 185 ? object.integer.integer : 0; 186 187 // Read Passive Cooling devices 188 att->passive_package = NULL; 189 //err = device->acpi->get_object(device->acpi_cookie, "_PSL", &(att->passive_package)); 190 191 att->active_count = 0; 192 att->active_devices = NULL; 193 194 err = B_OK; 195 break; 196 } 197 } 198 return err; 199 } 200 201 202 static status_t 203 acpi_thermal_close (void* cookie) 204 { 205 return B_OK; 206 } 207 208 209 static status_t 210 acpi_thermal_free (void* cookie) 211 { 212 return B_OK; 213 } 214 215 216 // #pragma mark - driver module API 217 218 219 static float 220 acpi_thermal_support(device_node *parent) 221 { 222 const char* bus; 223 uint32 device_type; 224 225 // make sure parent is really the ACPI bus manager 226 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 227 return -1; 228 229 if (strcmp(bus, "acpi")) 230 return 0.0; 231 232 // check whether it's really a thermal Device 233 if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM, 234 &device_type, false) != B_OK || device_type != ACPI_TYPE_THERMAL) { 235 return 0.0; 236 } 237 238 // TODO check there are _CRT and _TMP ? 239 240 return 0.6; 241 } 242 243 244 static status_t 245 acpi_thermal_register_device(device_node *node) 246 { 247 device_attr attrs[] = { 248 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "ACPI Thermal" }}, 249 { NULL } 250 }; 251 252 return sDeviceManager->register_node(node, ACPI_THERMAL_MODULE_NAME, attrs, 253 NULL, NULL); 254 } 255 256 257 static status_t 258 acpi_thermal_init_driver(device_node* node, void** _driverCookie) 259 { 260 *_driverCookie = node; 261 return B_OK; 262 } 263 264 265 static void 266 acpi_thermal_uninit_driver(void* driverCookie) 267 { 268 } 269 270 271 static status_t 272 acpi_thermal_register_child_devices(void* _cookie) 273 { 274 device_node* node = _cookie; 275 int path_id; 276 char name[128]; 277 278 path_id = sDeviceManager->create_id(ACPI_THERMAL_PATHID_GENERATOR); 279 if (path_id < 0) { 280 dprintf("acpi_thermal_register_child_devices: couldn't create a path_id\n"); 281 return B_ERROR; 282 } 283 284 snprintf(name, sizeof(name), ACPI_THERMAL_BASENAME, path_id); 285 286 return sDeviceManager->publish_device(node, name, 287 ACPI_THERMAL_DEVICE_MODULE_NAME); 288 } 289 290 291 static status_t 292 acpi_thermal_init_device(void* _cookie, void** cookie) 293 { 294 device_node* node = (device_node*)_cookie; 295 acpi_thermal_device_info* device; 296 device_node* parent; 297 298 device = (acpi_thermal_device_info*)calloc(1, sizeof(*device)); 299 if (device == NULL) 300 return B_NO_MEMORY; 301 302 device->node = node; 303 304 parent = sDeviceManager->get_parent_node(node); 305 sDeviceManager->get_driver(parent, (driver_module_info**)&device->acpi, 306 (void**)&device->acpi_cookie); 307 sDeviceManager->put_node(parent); 308 309 *cookie = device; 310 return B_OK; 311 } 312 313 314 static void 315 acpi_thermal_uninit_device(void* _cookie) 316 { 317 acpi_thermal_device_info* device = (acpi_thermal_device_info*)_cookie; 318 free(device); 319 } 320 321 322 323 module_dependency module_dependencies[] = { 324 { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager }, 325 {} 326 }; 327 328 329 driver_module_info acpi_thermal_driver_module = { 330 { 331 ACPI_THERMAL_MODULE_NAME, 332 0, 333 NULL 334 }, 335 336 acpi_thermal_support, 337 acpi_thermal_register_device, 338 acpi_thermal_init_driver, 339 acpi_thermal_uninit_driver, 340 acpi_thermal_register_child_devices, 341 NULL, // rescan 342 NULL, // removed 343 }; 344 345 346 struct device_module_info acpi_thermal_device_module = { 347 { 348 ACPI_THERMAL_DEVICE_MODULE_NAME, 349 0, 350 NULL 351 }, 352 353 acpi_thermal_init_device, 354 acpi_thermal_uninit_device, 355 NULL, 356 357 acpi_thermal_open, 358 acpi_thermal_close, 359 acpi_thermal_free, 360 acpi_thermal_read, 361 acpi_thermal_write, 362 NULL, 363 acpi_thermal_control, 364 365 NULL, 366 NULL 367 }; 368 369 module_info* modules[] = { 370 (module_info*)&acpi_thermal_driver_module, 371 (module_info*)&acpi_thermal_device_module, 372 NULL 373 }; 374