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} acpi_thermal_device_info;
39
40
41status_t acpi_thermal_control(void* _cookie, uint32 op, void* arg, size_t len);
42
43
44static status_t
45acpi_thermal_open(void *_cookie, const char *path, int flags, void** cookie)
46{
47	acpi_thermal_device_info *device = (acpi_thermal_device_info *)_cookie;
48	*cookie = device;
49	return B_OK;
50}
51
52
53static status_t
54acpi_thermal_read(void* _cookie, off_t position, void *buf, size_t* num_bytes)
55{
56	acpi_thermal_device_info* device = (acpi_thermal_device_info*)_cookie;
57	acpi_thermal_type therm_info;
58	if (*num_bytes < 1)
59		return B_IO_ERROR;
60
61	if (position == 0) {
62		size_t max_len = *num_bytes;
63		char *str = (char *)buf;
64		dprintf("acpi_thermal: read()\n");
65		acpi_thermal_control(device, drvOpGetThermalType, &therm_info, 0);
66
67		snprintf(str, max_len, "  Critical Temperature: %lu.%lu K\n",
68				(therm_info.critical_temp / 10), (therm_info.critical_temp % 10));
69
70		max_len -= strlen(str);
71		str += strlen(str);
72		snprintf(str, max_len, "  Current Temperature: %lu.%lu K\n",
73				(therm_info.current_temp / 10), (therm_info.current_temp % 10));
74
75		if (therm_info.hot_temp > 0) {
76			max_len -= strlen(str);
77			str += strlen(str);
78			snprintf(str, max_len, "  Hot Temperature: %lu.%lu K\n",
79					(therm_info.hot_temp / 10), (therm_info.hot_temp % 10));
80		}
81
82		if (therm_info.passive_package) {
83/* Incomplete.
84     Needs to obtain acpi global lock.
85     acpi_object_type needs Reference entry (with Handle that can be resolved)
86     what you see here is _highly_ unreliable.
87*/
88/*      		if (therm_info.passive_package->data.package.count > 0) {
89				sprintf((char *)buf + *num_bytes, "  Passive Devices\n");
90				*num_bytes = strlen((char *)buf);
91				for (i = 0; i < therm_info.passive_package->data.package.count; i++) {
92					sprintf((char *)buf + *num_bytes, "    Processor: %lu\n", therm_info.passive_package->data.package.objects[i].data.processor.cpu_id);
93					*num_bytes = strlen((char *)buf);
94				}
95			}
96*/
97			free(therm_info.passive_package);
98		}
99		*num_bytes = strlen((char *)buf);
100	} else {
101		*num_bytes = 0;
102	}
103
104	return B_OK;
105}
106
107
108static status_t
109acpi_thermal_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes)
110{
111	return B_ERROR;
112}
113
114
115status_t
116acpi_thermal_control(void* _cookie, uint32 op, void* arg, size_t len)
117{
118	acpi_thermal_device_info* device = (acpi_thermal_device_info*)_cookie;
119	status_t err = B_ERROR;
120
121	acpi_thermal_type *att = NULL;
122
123	uint32 integer;
124	acpi_data buffer;
125	buffer.pointer = &integer;
126	buffer.length = sizeof(integer);
127
128	switch (op) {
129		case drvOpGetThermalType: {
130			dprintf("acpi_thermal: GetThermalType()\n");
131			att = (acpi_thermal_type *)arg;
132
133			// Read basic temperature thresholds.
134			err = device->acpi->evaluate_method(device->acpi_cookie, "_CRT", NULL, &buffer);
135			att->critical_temp = (err == B_OK) ? integer : 0;
136			err = device->acpi->evaluate_method(device->acpi_cookie, "_TMP", NULL, &buffer);
137			att->current_temp = (err == B_OK) ? integer : 0;
138			err = device->acpi->evaluate_method(device->acpi_cookie, "_HOT", NULL, &buffer);
139			att->hot_temp = (err == B_OK) ? integer : 0;
140
141			dprintf("acpi_thermal: GotBasicTemperatures()\n");
142
143			// Read Passive Cooling devices
144			att->passive_package = NULL;
145			err = device->acpi->get_object(device->acpi_cookie, "_PSL", &(att->passive_package));
146
147			att->active_count = 0;
148			att->active_devices = NULL;
149
150			err = B_OK;
151			break;
152		}
153	}
154	return err;
155}
156
157
158static status_t
159acpi_thermal_close (void* cookie)
160{
161	return B_OK;
162}
163
164
165static status_t
166acpi_thermal_free (void* cookie)
167{
168	return B_OK;
169}
170
171
172//	#pragma mark - driver module API
173
174
175static float
176acpi_thermal_support(device_node *parent)
177{
178	const char *bus;
179	uint32 device_type;
180
181	// make sure parent is really the ACPI bus manager
182	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
183		return -1;
184
185	if (strcmp(bus, "acpi"))
186		return 0.0;
187
188	// check whether it's really a thermal Device
189	if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM, &device_type, false) != B_OK
190		|| device_type != ACPI_TYPE_THERMAL) {
191		return 0.0;
192	}
193
194	// TODO check there are _CRT and _TMP ?
195
196	return 0.6;
197}
198
199
200static status_t
201acpi_thermal_register_device(device_node *node)
202{
203	device_attr attrs[] = {
204		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "ACPI Thermal" }},
205		{ NULL }
206	};
207
208	return sDeviceManager->register_node(node, ACPI_THERMAL_MODULE_NAME, attrs, NULL, NULL);
209}
210
211
212static status_t
213acpi_thermal_init_driver(device_node *node, void **_driverCookie)
214{
215	*_driverCookie = node;
216	return B_OK;
217}
218
219
220static void
221acpi_thermal_uninit_driver(void *driverCookie)
222{
223}
224
225
226static status_t
227acpi_thermal_register_child_devices(void *_cookie)
228{
229	device_node *node = _cookie;
230	int path_id;
231	char name[128];
232
233	path_id = sDeviceManager->create_id(ACPI_THERMAL_PATHID_GENERATOR);
234	if (path_id < 0) {
235		dprintf("acpi_thermal_register_child_devices: couldn't create a path_id\n");
236		return B_ERROR;
237	}
238
239	snprintf(name, sizeof(name), ACPI_THERMAL_BASENAME, path_id);
240
241	return sDeviceManager->publish_device(node, name, ACPI_THERMAL_DEVICE_MODULE_NAME);
242}
243
244
245static status_t
246acpi_thermal_init_device(void *_cookie, void **cookie)
247{
248	device_node *node = (device_node *)_cookie;
249	acpi_thermal_device_info *device;
250	device_node *parent;
251
252	device = (acpi_thermal_device_info *)calloc(1, sizeof(*device));
253	if (device == NULL)
254		return B_NO_MEMORY;
255
256	device->node = node;
257
258	parent = sDeviceManager->get_parent_node(node);
259	sDeviceManager->get_driver(parent, (driver_module_info **)&device->acpi,
260		(void **)&device->acpi_cookie);
261	sDeviceManager->put_node(parent);
262
263	*cookie = device;
264	return B_OK;
265}
266
267
268static void
269acpi_thermal_uninit_device(void *_cookie)
270{
271	acpi_thermal_device_info *device = (acpi_thermal_device_info *)_cookie;
272	free(device);
273}
274
275
276
277module_dependency module_dependencies[] = {
278	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
279	{}
280};
281
282
283driver_module_info acpi_thermal_driver_module = {
284	{
285		ACPI_THERMAL_MODULE_NAME,
286		0,
287		NULL
288	},
289
290	acpi_thermal_support,
291	acpi_thermal_register_device,
292	acpi_thermal_init_driver,
293	acpi_thermal_uninit_driver,
294	acpi_thermal_register_child_devices,
295	NULL,	// rescan
296	NULL,	// removed
297};
298
299
300struct device_module_info acpi_thermal_device_module = {
301	{
302		ACPI_THERMAL_DEVICE_MODULE_NAME,
303		0,
304		NULL
305	},
306
307	acpi_thermal_init_device,
308	acpi_thermal_uninit_device,
309	NULL,
310
311	acpi_thermal_open,
312	acpi_thermal_close,
313	acpi_thermal_free,
314	acpi_thermal_read,
315	acpi_thermal_write,
316	NULL,
317	acpi_thermal_control,
318
319	NULL,
320	NULL
321};
322
323module_info *modules[] = {
324	(module_info *)&acpi_thermal_driver_module,
325	(module_info *)&acpi_thermal_device_module,
326	NULL
327};
328