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 char string[128]; 91 char* str = string; 92 size_t max_len = sizeof(string); 93 94 uint kelvinOffset = device->kelvin_offset; 95 96 acpi_thermal_control(device, drvOpGetThermalType, &therm_info, 0); 97 98 snprintf(str, max_len, " Critical Temperature: %" B_PRIu32 ".%" B_PRIu32 " °C\n", 99 ((therm_info.critical_temp - kelvinOffset) / 10), 100 ((therm_info.critical_temp - kelvinOffset) % 10)); 101 max_len -= strlen(str); 102 str += strlen(str); 103 104 snprintf(str, max_len, " Current Temperature: %" B_PRIu32 ".%" B_PRIu32 " °C\n", 105 ((therm_info.current_temp - kelvinOffset) / 10), 106 ((therm_info.current_temp - kelvinOffset) % 10)); 107 max_len -= strlen(str); 108 str += strlen(str); 109 110 if (therm_info.hot_temp > 0) { 111 snprintf(str, max_len, " Hot Temperature: %" B_PRIu32 ".%" B_PRIu32 " °C\n", 112 ((therm_info.hot_temp - kelvinOffset) / 10), 113 ((therm_info.hot_temp - kelvinOffset) % 10)); 114 max_len -= strlen(str); 115 str += strlen(str); 116 } 117 118 if (therm_info.passive_package) { 119 /* Incomplete. 120 Needs to obtain acpi global lock. 121 acpi_object_type needs Reference entry (with Handle that can be resolved) 122 what you see here is _highly_ unreliable. 123 */ 124 /* if (therm_info.passive_package->data.package.count > 0) { 125 sprintf((char *)buf + *num_bytes, " Passive Devices\n"); 126 *num_bytes = strlen((char *)buf); 127 for (i = 0; i < therm_info.passive_package->data.package.count; i++) { 128 sprintf((char *)buf + *num_bytes, " Processor: %lu\n", therm_info.passive_package->data.package.objects[i].data.processor.cpu_id); 129 *num_bytes = strlen((char *)buf); 130 } 131 } 132 */ 133 free(therm_info.passive_package); 134 } 135 136 max_len = user_strlcpy((char*)buf, string, *num_bytes); 137 if (max_len < B_OK) 138 return B_BAD_ADDRESS; 139 *num_bytes = max_len; 140 } else { 141 *num_bytes = 0; 142 } 143 144 return B_OK; 145 } 146 147 148 static status_t 149 acpi_thermal_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes) 150 { 151 return B_ERROR; 152 } 153 154 155 status_t 156 acpi_thermal_control(void* _cookie, uint32 op, void* arg, size_t len) 157 { 158 acpi_thermal_device_info* device = (acpi_thermal_device_info*)_cookie; 159 status_t err = B_ERROR; 160 161 acpi_thermal_type* att = NULL; 162 163 acpi_object_type object; 164 165 acpi_data buffer; 166 buffer.pointer = &object; 167 buffer.length = sizeof(object); 168 169 switch (op) { 170 case drvOpGetThermalType: { 171 att = (acpi_thermal_type*)arg; 172 173 // Read basic temperature thresholds. 174 err = device->acpi->evaluate_method (device->acpi_cookie, "_CRT", 175 NULL, &buffer); 176 177 att->critical_temp = 178 (err == B_OK && object.object_type == ACPI_TYPE_INTEGER) 179 ? object.integer.integer : 0; 180 181 err = device->acpi->evaluate_method (device->acpi_cookie, "_TMP", 182 NULL, &buffer); 183 184 att->current_temp = 185 (err == B_OK && object.object_type == ACPI_TYPE_INTEGER) 186 ? object.integer.integer : 0; 187 188 err = device->acpi->evaluate_method(device->acpi_cookie, "_HOT", 189 NULL, &buffer); 190 191 att->hot_temp = 192 (err == B_OK && object.object_type == ACPI_TYPE_INTEGER) 193 ? object.integer.integer : 0; 194 195 // Read Passive Cooling devices 196 att->passive_package = NULL; 197 //err = device->acpi->get_object(device->acpi_cookie, "_PSL", &(att->passive_package)); 198 199 att->active_count = 0; 200 att->active_devices = NULL; 201 202 err = B_OK; 203 break; 204 } 205 } 206 return err; 207 } 208 209 210 static status_t 211 acpi_thermal_close (void* cookie) 212 { 213 return B_OK; 214 } 215 216 217 static status_t 218 acpi_thermal_free (void* cookie) 219 { 220 return B_OK; 221 } 222 223 224 // #pragma mark - driver module API 225 226 227 static float 228 acpi_thermal_support(device_node *parent) 229 { 230 const char* bus; 231 uint32 device_type; 232 233 // make sure parent is really the ACPI bus manager 234 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 235 return -1; 236 237 if (strcmp(bus, "acpi")) 238 return 0.0; 239 240 // check whether it's really a thermal Device 241 if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM, 242 &device_type, false) != B_OK || device_type != ACPI_TYPE_THERMAL) { 243 return 0.0; 244 } 245 246 // TODO check there are _CRT and _TMP ? 247 248 return 0.6; 249 } 250 251 252 static status_t 253 acpi_thermal_register_device(device_node *node) 254 { 255 device_attr attrs[] = { 256 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "ACPI Thermal" }}, 257 { NULL } 258 }; 259 260 return sDeviceManager->register_node(node, ACPI_THERMAL_MODULE_NAME, attrs, 261 NULL, NULL); 262 } 263 264 265 static status_t 266 acpi_thermal_init_driver(device_node* node, void** _driverCookie) 267 { 268 *_driverCookie = node; 269 return B_OK; 270 } 271 272 273 static void 274 acpi_thermal_uninit_driver(void* driverCookie) 275 { 276 } 277 278 279 static status_t 280 acpi_thermal_register_child_devices(void* _cookie) 281 { 282 device_node* node = _cookie; 283 int path_id; 284 char name[128]; 285 286 path_id = sDeviceManager->create_id(ACPI_THERMAL_PATHID_GENERATOR); 287 if (path_id < 0) { 288 dprintf("acpi_thermal_register_child_devices: couldn't create a path_id\n"); 289 return B_ERROR; 290 } 291 292 snprintf(name, sizeof(name), ACPI_THERMAL_BASENAME, path_id); 293 294 return sDeviceManager->publish_device(node, name, 295 ACPI_THERMAL_DEVICE_MODULE_NAME); 296 } 297 298 299 static status_t 300 acpi_thermal_init_device(void* _cookie, void** cookie) 301 { 302 device_node* node = (device_node*)_cookie; 303 acpi_thermal_device_info* device; 304 device_node* parent; 305 306 device = (acpi_thermal_device_info*)calloc(1, sizeof(*device)); 307 if (device == NULL) 308 return B_NO_MEMORY; 309 310 device->node = node; 311 312 parent = sDeviceManager->get_parent_node(node); 313 sDeviceManager->get_driver(parent, (driver_module_info**)&device->acpi, 314 (void**)&device->acpi_cookie); 315 sDeviceManager->put_node(parent); 316 317 *cookie = device; 318 return B_OK; 319 } 320 321 322 static void 323 acpi_thermal_uninit_device(void* _cookie) 324 { 325 acpi_thermal_device_info* device = (acpi_thermal_device_info*)_cookie; 326 free(device); 327 } 328 329 330 331 module_dependency module_dependencies[] = { 332 { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager }, 333 {} 334 }; 335 336 337 driver_module_info acpi_thermal_driver_module = { 338 { 339 ACPI_THERMAL_MODULE_NAME, 340 0, 341 NULL 342 }, 343 344 acpi_thermal_support, 345 acpi_thermal_register_device, 346 acpi_thermal_init_driver, 347 acpi_thermal_uninit_driver, 348 acpi_thermal_register_child_devices, 349 NULL, // rescan 350 NULL, // removed 351 }; 352 353 354 struct device_module_info acpi_thermal_device_module = { 355 { 356 ACPI_THERMAL_DEVICE_MODULE_NAME, 357 0, 358 NULL 359 }, 360 361 acpi_thermal_init_device, 362 acpi_thermal_uninit_device, 363 NULL, 364 365 acpi_thermal_open, 366 acpi_thermal_close, 367 acpi_thermal_free, 368 acpi_thermal_read, 369 acpi_thermal_write, 370 NULL, 371 acpi_thermal_control, 372 373 NULL, 374 NULL 375 }; 376 377 module_info* modules[] = { 378 (module_info*)&acpi_thermal_driver_module, 379 (module_info*)&acpi_thermal_device_module, 380 NULL 381 }; 382