1/*
2 * Copyright 2020, J��r��me Duval, jerome.duval@gmail.com.
3 * Distributed under the terms of the MIT license.
4 */
5
6
7#include <ACPI.h>
8#include <condition_variable.h>
9#include <Drivers.h>
10#include <Errors.h>
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15
16#include <kernel.h>
17
18
19extern "C" {
20#	include "acpi.h"
21}
22
23
24struct als_driver_cookie {
25	device_node*				node;
26	acpi_device_module_info*	acpi;
27	acpi_device					acpi_cookie;
28};
29
30
31struct als_device_cookie {
32	als_driver_cookie*		driver_cookie;
33	int32					stop_watching;
34};
35
36
37#define ACPI_ALS_DRIVER_NAME "drivers/sensor/acpi_als/driver_v1"
38#define ACPI_ALS_DEVICE_NAME "drivers/sensor/acpi_als/device_v1"
39
40/* Base Namespace devices are published to */
41#define ACPI_ALS_BASENAME "sensor/acpi_als/%d"
42
43// name of pnp generator of path ids
44#define ACPI_ALS_PATHID_GENERATOR "acpi_als/path_id"
45
46#define ACPI_NAME_ALS "ACPI0008"
47
48#define TRACE_ALS
49#ifdef TRACE_ALS
50#	define TRACE(x...) dprintf("acpi_als: " x)
51#else
52#	define TRACE(x...)
53#endif
54#define ERROR(x...) dprintf("acpi_als: " x)
55
56
57static device_manager_info *sDeviceManager;
58static ConditionVariable sALSCondition;
59
60
61static status_t
62acpi_GetInteger(als_driver_cookie *device,
63	const char* path, uint64* number)
64{
65	acpi_data buf;
66	acpi_object_type object;
67	buf.pointer = &object;
68	buf.length = sizeof(acpi_object_type);
69
70	// Assume that what we've been pointed at is an Integer object, or
71	// a method that will return an Integer.
72	status_t status = device->acpi->evaluate_method(device->acpi_cookie, path,
73		NULL, &buf);
74	if (status == B_OK) {
75		if (object.object_type == ACPI_TYPE_INTEGER)
76			*number = object.integer.integer;
77		else
78			status = B_BAD_VALUE;
79	}
80	return status;
81}
82
83
84void
85als_notify_handler(acpi_handle device, uint32 value, void *context)
86{
87	TRACE("als_notify_handler event 0x%" B_PRIx32 "\n", value);
88	sALSCondition.NotifyAll();
89}
90
91
92//	#pragma mark - device module API
93
94
95static status_t
96acpi_als_init_device(void *driverCookie, void **cookie)
97{
98	*cookie = driverCookie;
99	return B_OK;
100}
101
102
103static void
104acpi_als_uninit_device(void *_cookie)
105{
106
107}
108
109
110static status_t
111acpi_als_open(void *initCookie, const char *path, int flags, void** cookie)
112{
113	als_device_cookie *device;
114	device = (als_device_cookie*)calloc(1, sizeof(als_device_cookie));
115	if (device == NULL)
116		return B_NO_MEMORY;
117
118	device->driver_cookie = (als_driver_cookie*)initCookie;
119	device->stop_watching = 0;
120
121	*cookie = device;
122
123	return B_OK;
124}
125
126
127static status_t
128acpi_als_close(void* cookie)
129{
130	return B_OK;
131}
132
133
134static status_t
135acpi_als_read(void* _cookie, off_t position, void *buffer, size_t* numBytes)
136{
137	if (*numBytes < 1)
138		return B_IO_ERROR;
139
140	als_device_cookie *device = (als_device_cookie*)_cookie;
141
142	if (position == 0) {
143		char string[10];
144		uint64 luminance = 0;
145		status_t status = acpi_GetInteger(device->driver_cookie,
146			"_ALI", &luminance);
147		if (status != B_OK)
148			return B_ERROR;
149		snprintf(string, sizeof(string), "%" B_PRIu64 "\n", luminance);
150		size_t max_len = user_strlcpy((char*)buffer, string, *numBytes);
151		if (max_len < B_OK)
152			return B_BAD_ADDRESS;
153		*numBytes = max_len;
154	} else
155		*numBytes = 0;
156
157	return B_OK;
158}
159
160
161static status_t
162acpi_als_write(void* cookie, off_t position, const void* buffer,
163	size_t* numBytes)
164{
165	return B_ERROR;
166}
167
168
169static status_t
170acpi_als_control(void* _cookie, uint32 op, void* arg, size_t len)
171{
172	//als_device_cookie* device = (als_device_cookie*)_cookie;
173
174	return B_DEV_INVALID_IOCTL;
175}
176
177
178static status_t
179acpi_als_free(void* cookie)
180{
181	als_device_cookie* device = (als_device_cookie*)cookie;
182	free(device);
183	return B_OK;
184}
185
186
187//	#pragma mark - driver module API
188
189
190static float
191acpi_als_support(device_node *parent)
192{
193	// make sure parent is really the ACPI bus manager
194	const char *bus;
195	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
196		return -1;
197
198	if (strcmp(bus, "acpi"))
199		return 0.0;
200
201	// check whether it's really a device
202	uint32 device_type;
203	if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM,
204			&device_type, false) != B_OK
205		|| device_type != ACPI_TYPE_DEVICE) {
206		return 0.0;
207	}
208
209	// check whether it's a als device
210	const char *name;
211	if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &name,
212		false) != B_OK || strcmp(name, ACPI_NAME_ALS)) {
213		return 0.0;
214	}
215
216	return 0.6;
217}
218
219
220static status_t
221acpi_als_register_device(device_node *node)
222{
223	device_attr attrs[] = {
224		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "ACPI ALS" }},
225		{ NULL }
226	};
227
228	return sDeviceManager->register_node(node, ACPI_ALS_DRIVER_NAME, attrs,
229		NULL, NULL);
230}
231
232
233static status_t
234acpi_als_init_driver(device_node *node, void **driverCookie)
235{
236	als_driver_cookie *device;
237	device = (als_driver_cookie *)calloc(1, sizeof(als_driver_cookie));
238	if (device == NULL)
239		return B_NO_MEMORY;
240
241	*driverCookie = device;
242
243	device->node = node;
244
245	device_node *parent;
246	parent = sDeviceManager->get_parent_node(node);
247	sDeviceManager->get_driver(parent, (driver_module_info **)&device->acpi,
248		(void **)&device->acpi_cookie);
249
250#ifdef TRACE_ALS
251	const char* device_path;
252	if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_PATH_ITEM,
253		&device_path, false) == B_OK) {
254		TRACE("acpi_als_init_driver %s\n", device_path);
255	}
256#endif
257
258	sDeviceManager->put_node(parent);
259
260	uint64 sta;
261	status_t status = acpi_GetInteger(device, "_STA", &sta);
262	uint64 mask = ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED
263		| ACPI_STA_DEVICE_FUNCTIONING;
264	if (status == B_OK && (sta & mask) != mask) {
265		ERROR("acpi_als_init_driver device disabled\n");
266		return B_ERROR;
267	}
268
269	uint64 luminance;
270	status = acpi_GetInteger(device, "_ALI", &luminance);
271	if (status != B_OK) {
272		ERROR("acpi_als_init_driver error when calling _ALI\n");
273		return B_ERROR;
274	}
275
276	// install notify handler
277	device->acpi->install_notify_handler(device->acpi_cookie,
278		ACPI_ALL_NOTIFY, als_notify_handler, device);
279
280	return B_OK;
281}
282
283
284static void
285acpi_als_uninit_driver(void *driverCookie)
286{
287	TRACE("acpi_als_uninit_driver\n");
288	als_driver_cookie *device = (als_driver_cookie*)driverCookie;
289
290	device->acpi->remove_notify_handler(device->acpi_cookie,
291		ACPI_ALL_NOTIFY, als_notify_handler);
292
293	free(device);
294}
295
296
297static status_t
298acpi_als_register_child_devices(void *cookie)
299{
300	als_driver_cookie *device = (als_driver_cookie*)cookie;
301
302	int pathID = sDeviceManager->create_id(ACPI_ALS_PATHID_GENERATOR);
303	if (pathID < 0) {
304		ERROR("register_child_devices: couldn't create a path_id\n");
305		return B_ERROR;
306	}
307
308	char name[128];
309	snprintf(name, sizeof(name), ACPI_ALS_BASENAME, pathID);
310
311	return sDeviceManager->publish_device(device->node, name,
312		ACPI_ALS_DEVICE_NAME);
313}
314
315
316
317
318
319
320module_dependency module_dependencies[] = {
321	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
322	{}
323};
324
325
326driver_module_info acpi_als_driver_module = {
327	{
328		ACPI_ALS_DRIVER_NAME,
329		0,
330		NULL
331	},
332
333	acpi_als_support,
334	acpi_als_register_device,
335	acpi_als_init_driver,
336	acpi_als_uninit_driver,
337	acpi_als_register_child_devices,
338	NULL,	// rescan
339	NULL,	// removed
340};
341
342
343struct device_module_info acpi_als_device_module = {
344	{
345		ACPI_ALS_DEVICE_NAME,
346		0,
347		NULL
348	},
349
350	acpi_als_init_device,
351	acpi_als_uninit_device,
352	NULL,
353
354	acpi_als_open,
355	acpi_als_close,
356	acpi_als_free,
357	acpi_als_read,
358	acpi_als_write,
359	NULL,
360	acpi_als_control,
361
362	NULL,
363	NULL
364};
365
366module_info *modules[] = {
367	(module_info *)&acpi_als_driver_module,
368	(module_info *)&acpi_als_device_module,
369	NULL
370};
371