xref: /haiku/src/add-ons/kernel/drivers/power/acpi_thermal/acpi_thermal.c (revision 1d9d47fc72028bb71b5f232a877231e59cfe2438)
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 "acpi_thermal_dev.h"
7 #include "acpi_thermal.h"
8 
9 acpi_module_info *acpi;
10 
11 static thermal_dev *device_list = NULL;
12 static sem_id dev_list_lock = -1;
13 static int device_count = 0;
14 
15 static char **device_names = NULL;
16 
17 /* Enumerates the devices of ACPI_THERMAL_TYPE
18  * looking for devices with _CRT and _TMP methods.
19  * Those devices, be they passive or active
20  * are usefull things to monitor / control.
21  */
22 void enumerate_devices (char* parent)
23 {
24 	char result[255];
25 	void *counter = NULL;
26 	thermal_dev *td = NULL;
27 
28 	acpi_object_type buf;
29 	size_t bufsize = sizeof(buf);
30 
31 	while (acpi->get_next_entry(ACPI_TYPE_THERMAL, parent, result, 255, &counter) == B_OK) {
32 		if ((acpi->evaluate_method(result, "_TMP", &buf, bufsize, NULL, 0) == B_OK) &&
33 		    (acpi->evaluate_method(result, "_CRT", &buf, bufsize, NULL, 0) == B_OK))
34 		{
35 			td = (thermal_dev *) malloc(sizeof(thermal_dev));
36 			if (td != NULL) {
37 				td->open = 0;
38 				td->num = device_count;
39 
40 				dprintf("acpi_thermal: Adding thermal device from: %s\n", result);
41 				td->path = malloc(sizeof(char) * strlen(result) + 1);
42 				strcpy(td->path, result);
43 
44 				acquire_sem(dev_list_lock);
45 				td->next = device_list;
46 				device_list = td;
47 				device_count++;
48 				release_sem(dev_list_lock);
49 			}
50 		}
51 		enumerate_devices(result);
52 	}
53 }
54 
55 void
56 cleanup_published_names(void)
57 {
58 	int i;
59 
60 	dprintf("acpi_thermal: cleanup_published_names()\n");
61 	if (device_names) {
62 		for (i = 0; device_names[i]; i++){
63 			free(device_names[i]);
64 		}
65 		free(device_names);
66 	}
67 }
68 
69 status_t
70 init_hardware (void)
71 {
72 	return B_OK;
73 }
74 
75 status_t
76 init_driver (void)
77 {
78 	if (get_module(B_ACPI_MODULE_NAME, (module_info **)&acpi) < 0) {
79 		dprintf("Failed to get ACPI module\n");
80 		return B_ERROR;
81 	}
82 
83 	if ((dev_list_lock = create_sem(1, "dev_list_lock")) < 0) {
84 		put_module(B_ACPI_MODULE_NAME);
85 		return dev_list_lock;
86 	}
87 
88 	enumerate_devices("\\");
89 	return B_OK;
90 }
91 
92 
93 void
94 uninit_driver (void)
95 {
96 	thermal_dev *td;
97 	acquire_sem(dev_list_lock);
98 	td = device_list;
99 
100 	while (td != NULL) {
101 		device_list = td->next;
102 		device_count--;
103 
104 		free(td->path);
105 		free(td);
106 		td = device_list;
107 	}
108 
109 	delete_sem(dev_list_lock);
110 
111 	put_module(B_ACPI_MODULE_NAME);
112 
113 	cleanup_published_names();
114 }
115 
116 
117 static status_t
118 acpi_thermal_open (const char *name, uint32 flags, void** cookie)
119 {
120 	thermal_dev *td;
121 	int devnum = atoi(name + strlen(basename));
122 
123 	acquire_sem(dev_list_lock);
124 	for (td = device_list; td; td = td->next) {
125 		if (td->num == devnum) {
126 			dprintf("acpi_thermal: opening ACPI path %s\n", td->path);
127 			td->open++;
128 			*cookie = td;
129 
130 			release_sem(dev_list_lock);
131 			return B_OK;
132 		}
133 	}
134 	release_sem(dev_list_lock);
135 
136 	*cookie = NULL;
137 	return B_ERROR;
138 }
139 
140 static status_t
141 acpi_thermal_read (void* cookie, off_t position, void *buf, size_t* num_bytes)
142 {
143 	int i;
144 	acpi_thermal_type therm_info;
145 	if (*num_bytes < 1)
146 		return B_IO_ERROR;
147 
148 	if (position == 0) {
149 		dprintf("acpi_thermal: read()\n");
150 		acpi_thermal_control(cookie, drvOpGetThermalType, &therm_info, 0);
151 		sprintf((char *)buf, "ACPI Thermal Device %u\n", therm_info.devnum);
152 		*num_bytes = strlen((char *)buf);
153 
154 		sprintf((char *)buf + *num_bytes, "  Critical Temperature: %lu.%lu K\n",
155 				(therm_info.critical_temp / 10), (therm_info.critical_temp % 10));
156 		*num_bytes = strlen((char *)buf);
157 
158 		sprintf((char *)buf + *num_bytes, "  Current Temperature: %lu.%lu K\n",
159 				(therm_info.current_temp / 10), (therm_info.current_temp % 10));
160 		*num_bytes = strlen((char *)buf);
161 
162 		if (therm_info.hot_temp > 0) {
163 			sprintf((char *)buf + *num_bytes, "  Hot Temperature: %lu.%lu K\n",
164 					(therm_info.hot_temp / 10), (therm_info.hot_temp % 10));
165 			*num_bytes = strlen((char *)buf);
166 		}
167 
168 		if (therm_info.passive_package) {
169 /* Incomplete.
170      Needs to obtain acpi global lock.
171      acpi_object_type needs Reference entry (with Handle that can be resolved)
172      what you see here is _highly_ unreliable.
173 */
174 /*      		if (therm_info.passive_package->data.package.count > 0) {
175 				sprintf((char *)buf + *num_bytes, "  Passive Devices\n");
176 				*num_bytes = strlen((char *)buf);
177 				for (i = 0; i < therm_info.passive_package->data.package.count; i++) {
178 					sprintf((char *)buf + *num_bytes, "    Processor: %lu\n", therm_info.passive_package->data.package.objects[i].data.processor.cpu_id);
179 					*num_bytes = strlen((char *)buf);
180 				}
181 			}
182 */
183 			free(therm_info.passive_package);
184 		}
185 	} else {
186 		*num_bytes = 0;
187 	}
188 
189 	return B_OK;
190 }
191 
192 static status_t
193 acpi_thermal_write (void* cookie, off_t position, const void* buffer, size_t* num_bytes)
194 {
195 	return B_ERROR;
196 }
197 
198 static status_t
199 acpi_thermal_control (void* cookie, uint32 op, void* arg, size_t len)
200 {
201 	status_t err = B_ERROR;
202 
203 	thermal_dev *td = (thermal_dev *)cookie;
204 	char objname[255];
205 	acpi_thermal_type *att = NULL;
206 
207 	size_t bufsize = sizeof(acpi_object_type);
208 	acpi_object_type buf;
209 
210 	switch (op) {
211 		case drvOpGetThermalType: {
212 			dprintf("acpi_thermal: GetThermalType()\n");
213 			att = (acpi_thermal_type *)arg;
214 
215 			// Read basic temperature thresholds.
216 			att->devnum = td->num;
217 			err = acpi->evaluate_method(td->path, "_CRT", &buf, bufsize, NULL, 0);
218 			att->critical_temp = buf.data.integer;
219 			err = acpi->evaluate_method(td->path, "_TMP", &buf, bufsize, NULL, 0);
220 			att->current_temp = buf.data.integer;
221 			err = acpi->evaluate_method(td->path, "_HOT", &buf, bufsize, NULL, 0);
222 			if (err == B_OK) {
223 				att->hot_temp = buf.data.integer;
224 			} else {
225 				att->hot_temp = 0;
226 			}
227 			dprintf("acpi_thermal: GotBasicTemperatures()\n");
228 
229 			// Read Passive Cooling devices
230 			att->passive_package = NULL;
231 			sprintf(objname, "%s._PSL", td->path);
232 			err = acpi->get_object(objname, &(att->passive_package));
233 
234 			att->active_count = 0;
235 			att->active_devices = NULL;
236 
237 			err = B_OK;
238 			break;
239 		}
240 	}
241 	return err;
242 }
243 
244 static status_t
245 acpi_thermal_close (void* cookie)
246 {
247 	return B_OK;
248 }
249 
250 static status_t
251 acpi_thermal_free (void* cookie)
252 {
253 	thermal_dev *td = (thermal_dev *)cookie;
254 
255 	acquire_sem(dev_list_lock);
256 	td->open--;
257 	release_sem(dev_list_lock);
258 
259 	return B_OK;
260 }
261 
262 device_hooks acpi_thermal_hooks = {
263 	acpi_thermal_open, 			/* -> open entry point */
264 	acpi_thermal_close, 		/* -> close entry point */
265 	acpi_thermal_free,			/* -> free cookie */
266 	acpi_thermal_control, 		/* -> control entry point */
267 	acpi_thermal_read,			/* -> read entry point */
268 	acpi_thermal_write,			/* -> write entry point */
269 	NULL, NULL, NULL, NULL
270 };
271 
272 const char**
273 publish_devices()
274 {
275 	thermal_dev *td;
276 	int i;
277 
278 	dprintf("acpi_thermal: publish_devices()\n");
279 
280 	cleanup_published_names();
281 
282 	acquire_sem(dev_list_lock);
283 	device_names = (char **) malloc(sizeof(char*) * (device_count + 1));
284 	if (device_names) {
285 		for (i = 0, td = device_list; td; td = td->next) {
286 			if ((device_names[i] = (char *) malloc(strlen(basename) + 4))) {
287 				sprintf(device_names[i], "%s%d", basename, td->num);
288 				dprintf("acpi_thermal: Publishing \"/dev/%s\"\n", device_names[i]);
289 				i++;
290 			}
291 		}
292 		device_names[i] = NULL;
293 	}
294 	release_sem(dev_list_lock);
295 
296 	return (const char**) device_names;
297 }
298 
299 device_hooks*
300 find_device(const char* name)
301 {
302 	return &acpi_thermal_hooks;
303 }
304