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
17class RingBuffer {
18public:
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();
29private:
30	ring_buffer *fBuffer;
31	sem_id fLock;
32};
33
34
35typedef 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
47static bool
48make_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
73static void
74dump_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
165static int32
166acpi_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
177extern "C" {
178/* ----------
179	acpi_namespace_open - handle open() calls
180----- */
181
182static status_t
183acpi_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----- */
220static status_t
221acpi_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
255static status_t
256acpi_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
267static status_t
268acpi_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
279static status_t
280acpi_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----- */
301static status_t
302acpi_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
313static status_t
314acpi_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
336static void
337acpi_namespace_uninit_device(void *_cookie)
338{
339	acpi_ns_device_info *device = (acpi_ns_device_info *)_cookie;
340	free(device);
341}
342
343}
344
345struct 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
369RingBuffer::RingBuffer(size_t size)
370{
371	fBuffer = create_ring_buffer(size);
372	fLock = create_sem(1, "ring buffer lock");
373}
374
375
376RingBuffer::~RingBuffer()
377{
378	delete_ring_buffer(fBuffer);
379}
380
381
382size_t
383RingBuffer::Read(void *buffer, ssize_t size)
384{
385	return ring_buffer_read(fBuffer, (uint8*)buffer, size);
386}
387
388
389size_t
390RingBuffer::Write(const void *buffer, ssize_t size)
391{
392	return ring_buffer_write(fBuffer, (uint8*)buffer, size);
393}
394
395
396size_t
397RingBuffer::ReadableAmount() const
398{
399	return ring_buffer_readable(fBuffer);
400}
401
402
403size_t
404RingBuffer::WritableAmount() const
405{
406	return ring_buffer_writable(fBuffer);
407}
408
409
410bool
411RingBuffer::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
419void
420RingBuffer::Unlock()
421{
422	release_sem(fLock);
423}
424
425
426void
427RingBuffer::DestroyLock()
428{
429	delete_sem(fLock);
430}
431
432