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