xref: /haiku/src/add-ons/kernel/bus_managers/acpi/NamespaceDump.cpp (revision f2762b2d5658b0b6c9ff62997a1485194cc77e7c)
1 /*
2  * Copyright 2006-2013, Jérôme Duval. All rights reserved.
3  * Copyright 2011-2012, Fredrik Holmqvis. All rights reserved.
4  * Copyright 2008, Stefano Ceccherini. All rights reserved.
5  * Copyright 2006, Bryan Varner. All rights reserved.
6  * Distributed under the terms of the MIT License.
7  */
8 
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 
15 #include <Drivers.h>
16 
17 #include <util/kernel_cpp.h>
18 #include <util/ring_buffer.h>
19 
20 #include "ACPIPrivate.h"
21 
22 
23 class RingBuffer {
24 public:
25 	RingBuffer(size_t size = 1024);
26 	~RingBuffer();
27 	size_t Read(void *buffer, ssize_t length);
28 	size_t Write(const void *buffer, ssize_t length);
29 	size_t WritableAmount() const;
30 	size_t ReadableAmount() const;
31 
32 	bool Lock();
33 	void Unlock();
34 	void DestroyLock();
35 private:
36 	ring_buffer *fBuffer;
37 	sem_id fLock;
38 };
39 
40 
41 typedef struct acpi_ns_device_info {
42 	device_node *node;
43 	acpi_root_info	*acpi;
44 	void	*acpi_cookie;
45 	thread_id thread;
46 	sem_id read_sem;
47 	RingBuffer *buffer;
48 } acpi_ns_device_info;
49 
50 
51 
52 // called with the buffer lock held
53 static bool
54 make_space(acpi_ns_device_info *device, size_t space)
55 {
56 	size_t available = device->buffer->WritableAmount();
57 	if (space <= available)
58 		return true;
59 	bool released = false;
60 	do {
61 		device->buffer->Unlock();
62 
63 		if (!released) {
64 			if (release_sem_etc(device->read_sem, 1, B_RELEASE_IF_WAITING_ONLY) == B_OK)
65 				released = true;
66 		}
67 		snooze(10000);
68 
69 		if (!device->buffer->Lock())
70 			return false;
71 
72 	} while (device->buffer->WritableAmount() < space);
73 
74 	return true;
75 }
76 
77 
78 
79 static void
80 dump_acpi_namespace(acpi_ns_device_info *device, char *root, int indenting)
81 {
82 	char result[255];
83 	char output[320];
84 	char tabs[255] = "";
85 	char hid[16] = "";
86 	int i;
87 	size_t written = 0;
88 	for (i = 0; i < indenting; i++)
89 		strlcat(tabs, "|    ", sizeof(tabs));
90 
91 	strlcat(tabs, "|--- ", sizeof(tabs));
92 
93 	int depth = sizeof(char) * 5 * indenting + sizeof(char); // index into result where the device name will be.
94 
95 	void *counter = NULL;
96 	while (device->acpi->get_next_entry(ACPI_TYPE_ANY, root, result, 255, &counter) == B_OK) {
97 		uint32 type = device->acpi->get_object_type(result);
98 		snprintf(output, sizeof(output), "%s%s", tabs, result + depth);
99 		switch(type) {
100 			case ACPI_TYPE_INTEGER:
101 				strncat(output, "     INTEGER", sizeof(output));
102 				break;
103 			case ACPI_TYPE_STRING:
104 				strncat(output, "     STRING", sizeof(output));
105 				break;
106 			case ACPI_TYPE_BUFFER:
107 				strncat(output, "     BUFFER", sizeof(output));
108 				break;
109 			case ACPI_TYPE_PACKAGE:
110 				strncat(output, "     PACKAGE", sizeof(output));
111 				break;
112 			case ACPI_TYPE_FIELD_UNIT:
113 				strncat(output, "     FIELD UNIT", sizeof(output));
114 				break;
115 			case ACPI_TYPE_DEVICE:
116 				hid[0] = 0; /* zero-terminate string; get_device_hid can (and will) fail! */
117 				device->acpi->get_device_hid(result, hid, sizeof(hid));
118 				strncat(output, "     DEVICE (", sizeof(output));
119 				strncat(output, hid, sizeof(output));
120 				strncat(output, ")", sizeof(output));
121 				break;
122 			case ACPI_TYPE_EVENT:
123 				strncat(output, "     EVENT", sizeof(output));
124 				break;
125 			case ACPI_TYPE_METHOD:
126 				strncat(output, "     METHOD", sizeof(output));
127 				break;
128 			case ACPI_TYPE_MUTEX:
129 				strncat(output, "     MUTEX", sizeof(output));
130 				break;
131 			case ACPI_TYPE_REGION:
132 				strncat(output, "     REGION", sizeof(output));
133 				break;
134 			case ACPI_TYPE_POWER:
135 				strncat(output, "     POWER", sizeof(output));
136 				break;
137 			case ACPI_TYPE_PROCESSOR:
138 				strncat(output, "     PROCESSOR", sizeof(output));
139 				break;
140 			case ACPI_TYPE_THERMAL:
141 				strncat(output, "     THERMAL", sizeof(output));
142 				break;
143 			case ACPI_TYPE_BUFFER_FIELD:
144 				strncat(output, "     BUFFER_FIELD", sizeof(output));
145 				break;
146 			case ACPI_TYPE_ANY:
147 			default:
148 				break;
149 		}
150 		written = 0;
151 		RingBuffer &ringBuffer = *device->buffer;
152 		size_t toWrite = strlen(output);
153 
154 		if (toWrite == 0)
155 			break;
156 
157 		toWrite = strlcat(output, "\n", sizeof(output));
158 
159 		if (!ringBuffer.Lock())
160 			break;
161 
162 		if (ringBuffer.WritableAmount() < toWrite &&
163 			!make_space(device, toWrite))
164 			break;
165 
166 		written = ringBuffer.Write(output, toWrite);
167 		ringBuffer.Unlock();
168 		dump_acpi_namespace(device, result, indenting + 1);
169 	}
170 }
171 
172 
173 static int32
174 acpi_namespace_dump(void *arg)
175 {
176 	acpi_ns_device_info *device = (acpi_ns_device_info*)(arg);
177 	dump_acpi_namespace(device, NULL, 0);
178 
179 	delete_sem(device->read_sem);
180 	device->read_sem = -1;
181 
182 	return 0;
183 }
184 
185 extern "C" {
186 /* ----------
187 	acpi_namespace_open - handle open() calls
188 ----- */
189 
190 static status_t
191 acpi_namespace_open(void *_cookie, const char* path, int flags, void** cookie)
192 {
193 	acpi_ns_device_info *device = (acpi_ns_device_info *)_cookie;
194 
195 	dprintf("\nacpi_ns_dump: device_open\n");
196 
197 	*cookie = device;
198 
199 	RingBuffer *ringBuffer = new RingBuffer(1024);
200 	if (ringBuffer == NULL)
201 		return B_NO_MEMORY;
202 
203 	device->read_sem = create_sem(0, "read_sem");
204 	if (device->read_sem < B_OK) {
205 		delete ringBuffer;
206 		return device->read_sem;
207 	}
208 
209 	device->thread = spawn_kernel_thread(acpi_namespace_dump, "acpi dumper",
210 		 B_NORMAL_PRIORITY, device);
211 	if (device->thread < 0) {
212 		delete ringBuffer;
213 		delete_sem(device->read_sem);
214 		return device->thread;
215 	}
216 
217 	device->buffer = ringBuffer;
218 
219 	resume_thread(device->thread);
220 
221 	return B_OK;
222 }
223 
224 
225 /* ----------
226 	acpi_namespace_read - handle read() calls
227 ----- */
228 static status_t
229 acpi_namespace_read(void *_cookie, off_t position, void *buf, size_t* num_bytes)
230 {
231 	acpi_ns_device_info *device = (acpi_ns_device_info *)_cookie;
232 	RingBuffer &ringBuffer = *device->buffer;
233 
234 	if (!ringBuffer.Lock()) {
235 		*num_bytes = 0;
236 		return B_ERROR;
237 	}
238 
239 	if (ringBuffer.ReadableAmount() == 0) {
240 		ringBuffer.Unlock();
241 		status_t status = acquire_sem_etc(device->read_sem, 1, B_CAN_INTERRUPT, 0);
242 		if (status != B_OK && status != B_BAD_SEM_ID) {
243 			*num_bytes = 0;
244 			return status;
245 		}
246 		if (!ringBuffer.Lock()) {
247 			*num_bytes = 0;
248 			return B_ERROR;
249 		}
250 	}
251 
252 	*num_bytes = ringBuffer.Read(buf, *num_bytes);
253 	ringBuffer.Unlock();
254 
255 	return B_OK;
256 }
257 
258 
259 /* ----------
260 	acpi_namespace_write - handle write() calls
261 ----- */
262 
263 static status_t
264 acpi_namespace_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes)
265 {
266 	*num_bytes = 0;				/* tell caller nothing was written */
267 	return B_IO_ERROR;
268 }
269 
270 
271 /* ----------
272 	acpi_namespace_control - handle ioctl calls
273 ----- */
274 
275 static status_t
276 acpi_namespace_control(void* cookie, uint32 op, void* arg, size_t len)
277 {
278 	dprintf("acpi_ns_dump: device_control\n");
279 	return B_DEV_INVALID_IOCTL;
280 }
281 
282 
283 /* ----------
284 	acpi_namespace_close - handle close() calls
285 ----- */
286 
287 static status_t
288 acpi_namespace_close(void* cookie)
289 {
290 	dprintf("acpi_ns_dump: device_close\n");
291 	return B_OK;
292 }
293 
294 
295 /* -----
296 	acpi_namespace_free - called after the last device is closed, and after
297 	all i/o is complete.
298 ----- */
299 static status_t
300 acpi_namespace_free(void* cookie)
301 {
302 	status_t status;
303 	acpi_ns_device_info *device = (acpi_ns_device_info *)cookie;
304 	dprintf("acpi_ns_dump: device_free\n");
305 
306 	if (device->read_sem >= 0)
307 		delete_sem(device->read_sem);
308 
309 	device->buffer->DestroyLock();
310 	wait_for_thread(device->thread, &status);
311 	delete device->buffer;
312 
313 	return B_OK;
314 }
315 
316 
317 //	#pragma mark - device module API
318 
319 
320 static status_t
321 acpi_namespace_init_device(void *_cookie, void **cookie)
322 {
323 	device_node *node = (device_node *)_cookie;
324 	status_t err;
325 
326 	acpi_ns_device_info *device = (acpi_ns_device_info *)calloc(1, sizeof(*device));
327 	if (device == NULL)
328 		return B_NO_MEMORY;
329 
330 	device->node = node;
331 	err = gDeviceManager->get_driver(node, (driver_module_info **)&device->acpi,
332 		(void **)&device->acpi_cookie);
333 	if (err != B_OK) {
334 		free(device);
335 		return err;
336 	}
337 
338 	*cookie = device;
339 	return B_OK;
340 }
341 
342 
343 static void
344 acpi_namespace_uninit_device(void *_cookie)
345 {
346 	acpi_ns_device_info *device = (acpi_ns_device_info *)_cookie;
347 	free(device);
348 }
349 
350 }
351 
352 struct device_module_info acpi_ns_dump_module = {
353 	{
354 		ACPI_NS_DUMP_DEVICE_MODULE_NAME,
355 		0,
356 		NULL
357 	},
358 
359 	acpi_namespace_init_device,
360 	acpi_namespace_uninit_device,
361 	NULL,
362 
363 	acpi_namespace_open,
364 	acpi_namespace_close,
365 	acpi_namespace_free,
366 	acpi_namespace_read,
367 	acpi_namespace_write,
368 	NULL,
369 	acpi_namespace_control,
370 
371 	NULL,
372 	NULL
373 };
374 
375 
376 RingBuffer::RingBuffer(size_t size)
377 {
378 	fBuffer = create_ring_buffer(size);
379 	fLock = create_sem(1, "ring buffer lock");
380 }
381 
382 
383 RingBuffer::~RingBuffer()
384 {
385 	delete_ring_buffer(fBuffer);
386 }
387 
388 
389 size_t
390 RingBuffer::Read(void *buffer, ssize_t size)
391 {
392 	return ring_buffer_read(fBuffer, (uint8*)buffer, size);
393 }
394 
395 
396 size_t
397 RingBuffer::Write(const void *buffer, ssize_t size)
398 {
399 	return ring_buffer_write(fBuffer, (uint8*)buffer, size);
400 }
401 
402 
403 size_t
404 RingBuffer::ReadableAmount() const
405 {
406 	return ring_buffer_readable(fBuffer);
407 }
408 
409 
410 size_t
411 RingBuffer::WritableAmount() const
412 {
413 	return ring_buffer_writable(fBuffer);
414 }
415 
416 
417 bool
418 RingBuffer::Lock()
419 {
420 	//status_t status = acquire_sem_etc(fLock, 1, B_CAN_INTERRUPT, 0);
421 	status_t status = acquire_sem(fLock);
422 	return status == B_OK;
423 }
424 
425 
426 void
427 RingBuffer::Unlock()
428 {
429 	release_sem(fLock);
430 }
431 
432 
433 void
434 RingBuffer::DestroyLock()
435 {
436 	delete_sem(fLock);
437 }
438 
439