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