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
guess_kelvin_offset(acpi_thermal_device_info * device)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
acpi_thermal_open(void * _cookie,const char * path,int flags,void ** cookie)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
acpi_thermal_read(void * _cookie,off_t position,void * buf,size_t * num_bytes)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
acpi_thermal_write(void * cookie,off_t position,const void * buffer,size_t * num_bytes)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
acpi_thermal_control(void * _cookie,uint32 op,void * arg,size_t len)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
acpi_thermal_close(void * cookie)211 acpi_thermal_close (void* cookie)
212 {
213 return B_OK;
214 }
215
216
217 static status_t
acpi_thermal_free(void * cookie)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
acpi_thermal_support(device_node * parent)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
acpi_thermal_register_device(device_node * node)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
acpi_thermal_init_driver(device_node * node,void ** _driverCookie)266 acpi_thermal_init_driver(device_node* node, void** _driverCookie)
267 {
268 *_driverCookie = node;
269 return B_OK;
270 }
271
272
273 static void
acpi_thermal_uninit_driver(void * driverCookie)274 acpi_thermal_uninit_driver(void* driverCookie)
275 {
276 }
277
278
279 static status_t
acpi_thermal_register_child_devices(void * _cookie)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
acpi_thermal_init_device(void * _cookie,void ** cookie)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
acpi_thermal_uninit_device(void * _cookie)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