xref: /haiku/src/add-ons/kernel/drivers/power/acpi_thermal/acpi_thermal.c (revision 1acbe440b8dd798953bec31d18ee589aa3f71b73)
1 /* ++++++++++
2     ACPI Generic Thermal Zone Driver.
3     Obtains general status of passive devices, monitors / sets critical temperatures
4     Controls active devices.
5 +++++ */
6 #include <KernelExport.h>
7 #include <Drivers.h>
8 #include <Errors.h>
9 #include <string.h>
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 
14 #include <ACPI.h>
15 #include <pnp_devfs.h>
16 #include "acpi_thermal.h"
17 
18 #define ACPI_THERMAL_MODULE_NAME "drivers/bin/acpi_thermal/acpi_device_v1"
19 
20 /* Base Namespace devices are published to */
21 #define ACPI_THERMAL_BASENAME "acpi/thermal/%d"
22 
23 // name of pnp generator of path ids
24 #define ACPI_THERMAL_PATHID_GENERATOR "acpi_thermal/path_id"
25 
26 device_manager_info *gDeviceManager;
27 
28 typedef struct acpi_ns_device_info {
29 	device_node_handle node;
30 	acpi_device_module_info *acpi;
31 	acpi_device acpi_cookie;
32 } acpi_thermal_device_info;
33 
34 
35 status_t acpi_thermal_control(acpi_thermal_device_info* device, uint32 op, void* arg, size_t len);
36 
37 
38 static status_t
39 acpi_thermal_open(acpi_thermal_device_info *device, uint32 flags, void** cookie)
40 {
41 	*cookie = device;
42 	return B_OK;
43 }
44 
45 
46 static status_t
47 acpi_thermal_read(acpi_thermal_device_info* device, off_t position, void *buf, size_t* num_bytes)
48 {
49 	acpi_thermal_type therm_info;
50 	if (*num_bytes < 1)
51 		return B_IO_ERROR;
52 
53 	if (position == 0) {
54 		size_t max_len = *num_bytes;
55 		char *str = (char *)buf;
56 		dprintf("acpi_thermal: read()\n");
57 		acpi_thermal_control(device, drvOpGetThermalType, &therm_info, 0);
58 
59 		snprintf(str, max_len, "  Critical Temperature: %lu.%lu K\n",
60 				(therm_info.critical_temp / 10), (therm_info.critical_temp % 10));
61 
62 		max_len -= strlen(str);
63 		str += strlen(str);
64 		snprintf(str, max_len, "  Current Temperature: %lu.%lu K\n",
65 				(therm_info.current_temp / 10), (therm_info.current_temp % 10));
66 
67 		if (therm_info.hot_temp > 0) {
68 			max_len -= strlen(str);
69 			str += strlen(str);
70 			snprintf(str, max_len, "  Hot Temperature: %lu.%lu K\n",
71 					(therm_info.hot_temp / 10), (therm_info.hot_temp % 10));
72 		}
73 
74 		if (therm_info.passive_package) {
75 /* Incomplete.
76      Needs to obtain acpi global lock.
77      acpi_object_type needs Reference entry (with Handle that can be resolved)
78      what you see here is _highly_ unreliable.
79 */
80 /*      		if (therm_info.passive_package->data.package.count > 0) {
81 				sprintf((char *)buf + *num_bytes, "  Passive Devices\n");
82 				*num_bytes = strlen((char *)buf);
83 				for (i = 0; i < therm_info.passive_package->data.package.count; i++) {
84 					sprintf((char *)buf + *num_bytes, "    Processor: %lu\n", therm_info.passive_package->data.package.objects[i].data.processor.cpu_id);
85 					*num_bytes = strlen((char *)buf);
86 				}
87 			}
88 */
89 			free(therm_info.passive_package);
90 		}
91 		*num_bytes = strlen((char *)buf);
92 	} else {
93 		*num_bytes = 0;
94 	}
95 
96 	return B_OK;
97 }
98 
99 
100 static status_t
101 acpi_thermal_write (void* cookie, off_t position, const void* buffer, size_t* num_bytes)
102 {
103 	return B_ERROR;
104 }
105 
106 
107 status_t
108 acpi_thermal_control (acpi_thermal_device_info* device, uint32 op, void* arg, size_t len)
109 {
110 	status_t err = B_ERROR;
111 
112 	acpi_thermal_type *att = NULL;
113 
114 	size_t bufsize = sizeof(acpi_object_type);
115 	acpi_object_type buf;
116 
117 	switch (op) {
118 		case drvOpGetThermalType: {
119 			dprintf("acpi_thermal: GetThermalType()\n");
120 			att = (acpi_thermal_type *)arg;
121 
122 			// Read basic temperature thresholds.
123 			err = device->acpi->evaluate_method(device->acpi_cookie, "_CRT", &buf, bufsize, NULL, 0);
124 			att->critical_temp = buf.data.integer;
125 			err = device->acpi->evaluate_method(device->acpi_cookie, "_TMP", &buf, bufsize, NULL, 0);
126 			att->current_temp = buf.data.integer;
127 			err = device->acpi->evaluate_method(device->acpi_cookie, "_HOT", &buf, bufsize, NULL, 0);
128 			if (err == B_OK) {
129 				att->hot_temp = buf.data.integer;
130 			} else {
131 				att->hot_temp = 0;
132 			}
133 			dprintf("acpi_thermal: GotBasicTemperatures()\n");
134 
135 			// Read Passive Cooling devices
136 			att->passive_package = NULL;
137 			err = device->acpi->get_object(device->acpi_cookie, "_PSL", &(att->passive_package));
138 
139 			att->active_count = 0;
140 			att->active_devices = NULL;
141 
142 			err = B_OK;
143 			break;
144 		}
145 	}
146 	return err;
147 }
148 
149 
150 static status_t
151 acpi_thermal_close (void* cookie)
152 {
153 	return B_OK;
154 }
155 
156 
157 static status_t
158 acpi_thermal_free (void* cookie)
159 {
160 	return B_OK;
161 }
162 
163 
164 static float
165 acpi_thermal_support(device_node_handle parent, bool *_noConnection)
166 {
167 	char *bus;
168 	uint32 device_type;
169 
170 	// make sure parent is really the ACPI bus manager
171 	if (gDeviceManager->get_attr_string(parent, B_DRIVER_BUS, &bus, false) != B_OK) {
172 		dprintf("no bus\n");
173 		return 0.0;
174 	}
175 
176 	if (strcmp(bus, "acpi")) {
177 		dprintf("bad bus\n");
178 		goto err;
179 	}
180 
181 	// check whether it's really a thermal Device
182 	if (gDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM, &device_type, false) != B_OK
183 		|| device_type != ACPI_TYPE_THERMAL) {
184 		dprintf("bad type\n");
185 		goto err;
186 	}
187 
188 	// TODO check there are _CRT and _TMP ?
189 
190 	free(bus);
191 	return 0.6;
192 err:
193 	free(bus);
194 	return 0.0;
195 }
196 
197 
198 static status_t
199 acpi_thermal_init_device(device_node_handle node, void *user_cookie, void **cookie)
200 {
201 	acpi_thermal_device_info *device;
202 	status_t res;
203 
204 	device = (acpi_thermal_device_info *)calloc(1, sizeof(*device));
205 	if (device == NULL)
206 		return B_NO_MEMORY;
207 
208 	device->node = node;
209 
210 	// register it everywhere
211 	res = gDeviceManager->init_driver(gDeviceManager->get_parent(node), NULL,
212 			(driver_module_info **)&device->acpi, (void**) &device->acpi_cookie);
213 	if (res != B_OK)
214 		goto err;
215 
216 	*cookie = device;
217 	return B_OK;
218 
219 err:
220 	free(device);
221 	return res;
222 }
223 
224 
225 static status_t
226 acpi_thermal_uninit_device(acpi_thermal_device_info *device)
227 {
228 	gDeviceManager->uninit_driver(gDeviceManager->get_parent(device->node));
229 	free(device);
230 
231 	return B_OK;
232 }
233 
234 
235 static status_t
236 acpi_thermal_added(device_node_handle node)
237 {
238 	int path_id;
239 	char name[128];
240 
241 	path_id = gDeviceManager->create_id(ACPI_THERMAL_PATHID_GENERATOR);
242 	if (path_id < 0) {
243 		return B_ERROR;
244 	}
245 
246 	snprintf(name, sizeof(name), ACPI_THERMAL_BASENAME, path_id);
247 	{
248 		device_attr attrs[] = {
249 			{ B_DRIVER_MODULE, B_STRING_TYPE, { string: ACPI_THERMAL_MODULE_NAME }},
250 
251 			{ PNP_DRIVER_CONNECTION, B_STRING_TYPE, { string: "acpi_thermal" }},
252 			// we want devfs on top of us (who wouldn't?)
253 			{ B_DRIVER_FIXED_CHILD, B_STRING_TYPE, { string: PNP_DEVFS_MODULE_NAME }},
254 
255 			{ PNP_MANAGER_ID_GENERATOR, B_STRING_TYPE, { string: ACPI_THERMAL_PATHID_GENERATOR }},
256 			{ PNP_MANAGER_AUTO_ID, B_UINT32_TYPE, { ui32: path_id }},
257 
258 			// tell which name we want to have in devfs
259 			{ PNP_DEVFS_FILENAME, B_STRING_TYPE, { string: name }},
260 			{ NULL }
261 		};
262 
263 		return gDeviceManager->register_device(node, attrs, NULL, &node);
264 	}
265 
266 }
267 
268 
269 static status_t
270 std_ops(int32 op, ...)
271 {
272 	switch (op) {
273 		case B_MODULE_INIT:
274 		case B_MODULE_UNINIT:
275 			return B_OK;
276 
277 		default:
278 			return B_ERROR;
279 	}
280 }
281 
282 module_dependency module_dependencies[] = {
283 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager },
284 	{}
285 };
286 
287 
288 pnp_devfs_driver_info acpi_thermal_dump_module = {
289 	{
290 		{
291 			ACPI_THERMAL_MODULE_NAME,
292 			0,
293 			std_ops
294 		},
295 
296 		acpi_thermal_support,
297 		acpi_thermal_added,
298 		acpi_thermal_init_device,
299 		(status_t (*) (void *))acpi_thermal_uninit_device,
300 		NULL
301 	},
302 
303 	(pnp_device_open_hook) 	&acpi_thermal_open,
304 	acpi_thermal_close,
305 	acpi_thermal_free,
306 	(device_control_hook)	&acpi_thermal_control,
307 
308 	(device_read_hook) acpi_thermal_read,
309 	acpi_thermal_write,
310 
311 	NULL,
312 	NULL,
313 
314 	NULL,
315 	NULL
316 };
317 
318 module_info *modules[] = {
319 	(module_info *)&acpi_thermal_dump_module,
320 	NULL
321 };
322