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
32static device_manager_info* sDeviceManager;
33
34typedef 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
42status_t acpi_thermal_control(void* _cookie, uint32 op, void* arg, size_t len);
43
44static 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 */
54static void
55guess_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
67static status_t
68acpi_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
80static status_t
81acpi_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
148static status_t
149acpi_thermal_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes)
150{
151	return B_ERROR;
152}
153
154
155status_t
156acpi_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
210static status_t
211acpi_thermal_close (void* cookie)
212{
213	return B_OK;
214}
215
216
217static status_t
218acpi_thermal_free (void* cookie)
219{
220	return B_OK;
221}
222
223
224//	#pragma mark - driver module API
225
226
227static float
228acpi_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
252static status_t
253acpi_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
265static status_t
266acpi_thermal_init_driver(device_node* node, void** _driverCookie)
267{
268	*_driverCookie = node;
269	return B_OK;
270}
271
272
273static void
274acpi_thermal_uninit_driver(void* driverCookie)
275{
276}
277
278
279static status_t
280acpi_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
299static status_t
300acpi_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
322static void
323acpi_thermal_uninit_device(void* _cookie)
324{
325	acpi_thermal_device_info* device = (acpi_thermal_device_info*)_cookie;
326	free(device);
327}
328
329
330
331module_dependency module_dependencies[] = {
332	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager },
333	{}
334};
335
336
337driver_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
354struct 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
377module_info* modules[] = {
378	(module_info*)&acpi_thermal_driver_module,
379	(module_info*)&acpi_thermal_device_module,
380	NULL
381};
382