18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ACPI event handling for Wilco Embedded Controller
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2019 Google LLC
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * The Wilco Embedded Controller can create custom events that
88c2ecf20Sopenharmony_ci * are not handled as standard ACPI objects. These events can
98c2ecf20Sopenharmony_ci * contain information about changes in EC controlled features,
108c2ecf20Sopenharmony_ci * such as errors and events in the dock or display. For example,
118c2ecf20Sopenharmony_ci * an event is triggered if the dock is plugged into a display
128c2ecf20Sopenharmony_ci * incorrectly. These events are needed for telemetry and
138c2ecf20Sopenharmony_ci * diagnostics reasons, and for possibly alerting the user.
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci * These events are triggered by the EC with an ACPI Notify(0x90),
168c2ecf20Sopenharmony_ci * and then the BIOS reads the event buffer from EC RAM via an
178c2ecf20Sopenharmony_ci * ACPI method. When the OS receives these events via ACPI,
188c2ecf20Sopenharmony_ci * it passes them along to this driver. The events are put into
198c2ecf20Sopenharmony_ci * a queue which can be read by a userspace daemon via a char device
208c2ecf20Sopenharmony_ci * that implements read() and poll(). The event queue acts as a
218c2ecf20Sopenharmony_ci * circular buffer of size 64, so if there are no userspace consumers
228c2ecf20Sopenharmony_ci * the kernel will not run out of memory. The char device will appear at
238c2ecf20Sopenharmony_ci * /dev/wilco_event{n}, where n is some small non-negative integer,
248c2ecf20Sopenharmony_ci * starting from 0. Standard ACPI events such as the battery getting
258c2ecf20Sopenharmony_ci * plugged/unplugged can also come through this path, but they are
268c2ecf20Sopenharmony_ci * dealt with via other paths, and are ignored here.
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci * To test, you can tail the binary data with
298c2ecf20Sopenharmony_ci * $ cat /dev/wilco_event0 | hexdump -ve '1/1 "%x\n"'
308c2ecf20Sopenharmony_ci * and then create an event by plugging/unplugging the battery.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <linux/acpi.h>
348c2ecf20Sopenharmony_ci#include <linux/cdev.h>
358c2ecf20Sopenharmony_ci#include <linux/device.h>
368c2ecf20Sopenharmony_ci#include <linux/fs.h>
378c2ecf20Sopenharmony_ci#include <linux/idr.h>
388c2ecf20Sopenharmony_ci#include <linux/io.h>
398c2ecf20Sopenharmony_ci#include <linux/list.h>
408c2ecf20Sopenharmony_ci#include <linux/module.h>
418c2ecf20Sopenharmony_ci#include <linux/poll.h>
428c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
438c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
448c2ecf20Sopenharmony_ci#include <linux/wait.h>
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* ACPI Notify event code indicating event data is available. */
478c2ecf20Sopenharmony_ci#define EC_ACPI_NOTIFY_EVENT		0x90
488c2ecf20Sopenharmony_ci/* ACPI Method to execute to retrieve event data buffer from the EC. */
498c2ecf20Sopenharmony_ci#define EC_ACPI_GET_EVENT		"QSET"
508c2ecf20Sopenharmony_ci/* Maximum number of words in event data returned by the EC. */
518c2ecf20Sopenharmony_ci#define EC_ACPI_MAX_EVENT_WORDS		6
528c2ecf20Sopenharmony_ci#define EC_ACPI_MAX_EVENT_SIZE \
538c2ecf20Sopenharmony_ci	(sizeof(struct ec_event) + (EC_ACPI_MAX_EVENT_WORDS) * sizeof(u16))
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/* Node will appear in /dev/EVENT_DEV_NAME */
568c2ecf20Sopenharmony_ci#define EVENT_DEV_NAME		"wilco_event"
578c2ecf20Sopenharmony_ci#define EVENT_CLASS_NAME	EVENT_DEV_NAME
588c2ecf20Sopenharmony_ci#define DRV_NAME		EVENT_DEV_NAME
598c2ecf20Sopenharmony_ci#define EVENT_DEV_NAME_FMT	(EVENT_DEV_NAME "%d")
608c2ecf20Sopenharmony_cistatic struct class event_class = {
618c2ecf20Sopenharmony_ci	.owner	= THIS_MODULE,
628c2ecf20Sopenharmony_ci	.name	= EVENT_CLASS_NAME,
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/* Keep track of all the device numbers used. */
668c2ecf20Sopenharmony_ci#define EVENT_MAX_DEV 128
678c2ecf20Sopenharmony_cistatic int event_major;
688c2ecf20Sopenharmony_cistatic DEFINE_IDA(event_ida);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* Size of circular queue of events. */
718c2ecf20Sopenharmony_ci#define MAX_NUM_EVENTS 64
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/**
748c2ecf20Sopenharmony_ci * struct ec_event - Extended event returned by the EC.
758c2ecf20Sopenharmony_ci * @size: Number of 16bit words in structure after the size word.
768c2ecf20Sopenharmony_ci * @type: Extended event type, meaningless for us.
778c2ecf20Sopenharmony_ci * @event: Event data words.  Max count is %EC_ACPI_MAX_EVENT_WORDS.
788c2ecf20Sopenharmony_ci */
798c2ecf20Sopenharmony_cistruct ec_event {
808c2ecf20Sopenharmony_ci	u16 size;
818c2ecf20Sopenharmony_ci	u16 type;
828c2ecf20Sopenharmony_ci	u16 event[];
838c2ecf20Sopenharmony_ci} __packed;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci#define ec_event_num_words(ev) (ev->size - 1)
868c2ecf20Sopenharmony_ci#define ec_event_size(ev) (sizeof(*ev) + (ec_event_num_words(ev) * sizeof(u16)))
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/**
898c2ecf20Sopenharmony_ci * struct ec_event_queue - Circular queue for events.
908c2ecf20Sopenharmony_ci * @capacity: Number of elements the queue can hold.
918c2ecf20Sopenharmony_ci * @head: Next index to write to.
928c2ecf20Sopenharmony_ci * @tail: Next index to read from.
938c2ecf20Sopenharmony_ci * @entries: Array of events.
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_cistruct ec_event_queue {
968c2ecf20Sopenharmony_ci	int capacity;
978c2ecf20Sopenharmony_ci	int head;
988c2ecf20Sopenharmony_ci	int tail;
998c2ecf20Sopenharmony_ci	struct ec_event *entries[];
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/* Maximum number of events to store in ec_event_queue */
1038c2ecf20Sopenharmony_cistatic int queue_size = 64;
1048c2ecf20Sopenharmony_cimodule_param(queue_size, int, 0644);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic struct ec_event_queue *event_queue_new(int capacity)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct ec_event_queue *q;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	q = kzalloc(struct_size(q, entries, capacity), GFP_KERNEL);
1118c2ecf20Sopenharmony_ci	if (!q)
1128c2ecf20Sopenharmony_ci		return NULL;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	q->capacity = capacity;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	return q;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic inline bool event_queue_empty(struct ec_event_queue *q)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	/* head==tail when both full and empty, but head==NULL when empty */
1228c2ecf20Sopenharmony_ci	return q->head == q->tail && !q->entries[q->head];
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic inline bool event_queue_full(struct ec_event_queue *q)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	/* head==tail when both full and empty, but head!=NULL when full */
1288c2ecf20Sopenharmony_ci	return q->head == q->tail && q->entries[q->head];
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic struct ec_event *event_queue_pop(struct ec_event_queue *q)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct ec_event *ev;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (event_queue_empty(q))
1368c2ecf20Sopenharmony_ci		return NULL;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	ev = q->entries[q->tail];
1398c2ecf20Sopenharmony_ci	q->entries[q->tail] = NULL;
1408c2ecf20Sopenharmony_ci	q->tail = (q->tail + 1) % q->capacity;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return ev;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/*
1468c2ecf20Sopenharmony_ci * If full, overwrite the oldest event and return it so the caller
1478c2ecf20Sopenharmony_ci * can kfree it. If not full, return NULL.
1488c2ecf20Sopenharmony_ci */
1498c2ecf20Sopenharmony_cistatic struct ec_event *event_queue_push(struct ec_event_queue *q,
1508c2ecf20Sopenharmony_ci					 struct ec_event *ev)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	struct ec_event *popped = NULL;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (event_queue_full(q))
1558c2ecf20Sopenharmony_ci		popped = event_queue_pop(q);
1568c2ecf20Sopenharmony_ci	q->entries[q->head] = ev;
1578c2ecf20Sopenharmony_ci	q->head = (q->head + 1) % q->capacity;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	return popped;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic void event_queue_free(struct ec_event_queue *q)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct ec_event *event;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	while ((event = event_queue_pop(q)) != NULL)
1678c2ecf20Sopenharmony_ci		kfree(event);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	kfree(q);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/**
1738c2ecf20Sopenharmony_ci * struct event_device_data - Data for a Wilco EC device that responds to ACPI.
1748c2ecf20Sopenharmony_ci * @events: Circular queue of EC events to be provided to userspace.
1758c2ecf20Sopenharmony_ci * @queue_lock: Protect the queue from simultaneous read/writes.
1768c2ecf20Sopenharmony_ci * @wq: Wait queue to notify processes when events are available or the
1778c2ecf20Sopenharmony_ci *	device has been removed.
1788c2ecf20Sopenharmony_ci * @cdev: Char dev that userspace reads() and polls() from.
1798c2ecf20Sopenharmony_ci * @dev: Device associated with the %cdev.
1808c2ecf20Sopenharmony_ci * @exist: Has the device been not been removed? Once a device has been removed,
1818c2ecf20Sopenharmony_ci *	   writes, reads, and new opens will fail.
1828c2ecf20Sopenharmony_ci * @available: Guarantee only one client can open() file and read from queue.
1838c2ecf20Sopenharmony_ci *
1848c2ecf20Sopenharmony_ci * There will be one of these structs for each ACPI device registered. This data
1858c2ecf20Sopenharmony_ci * is the queue of events received from ACPI that still need to be read from
1868c2ecf20Sopenharmony_ci * userspace, the device and char device that userspace is using, a wait queue
1878c2ecf20Sopenharmony_ci * used to notify different threads when something has changed, plus a flag
1888c2ecf20Sopenharmony_ci * on whether the ACPI device has been removed.
1898c2ecf20Sopenharmony_ci */
1908c2ecf20Sopenharmony_cistruct event_device_data {
1918c2ecf20Sopenharmony_ci	struct ec_event_queue *events;
1928c2ecf20Sopenharmony_ci	spinlock_t queue_lock;
1938c2ecf20Sopenharmony_ci	wait_queue_head_t wq;
1948c2ecf20Sopenharmony_ci	struct device dev;
1958c2ecf20Sopenharmony_ci	struct cdev cdev;
1968c2ecf20Sopenharmony_ci	bool exist;
1978c2ecf20Sopenharmony_ci	atomic_t available;
1988c2ecf20Sopenharmony_ci};
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci/**
2018c2ecf20Sopenharmony_ci * enqueue_events() - Place EC events in queue to be read by userspace.
2028c2ecf20Sopenharmony_ci * @adev: Device the events came from.
2038c2ecf20Sopenharmony_ci * @buf: Buffer of event data.
2048c2ecf20Sopenharmony_ci * @length: Length of event data buffer.
2058c2ecf20Sopenharmony_ci *
2068c2ecf20Sopenharmony_ci * %buf contains a number of ec_event's, packed one after the other.
2078c2ecf20Sopenharmony_ci * Each ec_event is of variable length. Start with the first event, copy it
2088c2ecf20Sopenharmony_ci * into a persistent ec_event, store that entry in the queue, move on
2098c2ecf20Sopenharmony_ci * to the next ec_event in buf, and repeat.
2108c2ecf20Sopenharmony_ci *
2118c2ecf20Sopenharmony_ci * Return: 0 on success or negative error code on failure.
2128c2ecf20Sopenharmony_ci */
2138c2ecf20Sopenharmony_cistatic int enqueue_events(struct acpi_device *adev, const u8 *buf, u32 length)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct event_device_data *dev_data = adev->driver_data;
2168c2ecf20Sopenharmony_ci	struct ec_event *event, *queue_event, *old_event;
2178c2ecf20Sopenharmony_ci	size_t num_words, event_size;
2188c2ecf20Sopenharmony_ci	u32 offset = 0;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	while (offset < length) {
2218c2ecf20Sopenharmony_ci		event = (struct ec_event *)(buf + offset);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci		num_words = ec_event_num_words(event);
2248c2ecf20Sopenharmony_ci		event_size = ec_event_size(event);
2258c2ecf20Sopenharmony_ci		if (num_words > EC_ACPI_MAX_EVENT_WORDS) {
2268c2ecf20Sopenharmony_ci			dev_err(&adev->dev, "Too many event words: %zu > %d\n",
2278c2ecf20Sopenharmony_ci				num_words, EC_ACPI_MAX_EVENT_WORDS);
2288c2ecf20Sopenharmony_ci			return -EOVERFLOW;
2298c2ecf20Sopenharmony_ci		}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		/* Ensure event does not overflow the available buffer */
2328c2ecf20Sopenharmony_ci		if ((offset + event_size) > length) {
2338c2ecf20Sopenharmony_ci			dev_err(&adev->dev, "Event exceeds buffer: %zu > %d\n",
2348c2ecf20Sopenharmony_ci				offset + event_size, length);
2358c2ecf20Sopenharmony_ci			return -EOVERFLOW;
2368c2ecf20Sopenharmony_ci		}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci		/* Point to the next event in the buffer */
2398c2ecf20Sopenharmony_ci		offset += event_size;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci		/* Copy event into the queue */
2428c2ecf20Sopenharmony_ci		queue_event = kmemdup(event, event_size, GFP_KERNEL);
2438c2ecf20Sopenharmony_ci		if (!queue_event)
2448c2ecf20Sopenharmony_ci			return -ENOMEM;
2458c2ecf20Sopenharmony_ci		spin_lock(&dev_data->queue_lock);
2468c2ecf20Sopenharmony_ci		old_event = event_queue_push(dev_data->events, queue_event);
2478c2ecf20Sopenharmony_ci		spin_unlock(&dev_data->queue_lock);
2488c2ecf20Sopenharmony_ci		kfree(old_event);
2498c2ecf20Sopenharmony_ci		wake_up_interruptible(&dev_data->wq);
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	return 0;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci/**
2568c2ecf20Sopenharmony_ci * event_device_notify() - Callback when EC generates an event over ACPI.
2578c2ecf20Sopenharmony_ci * @adev: The device that the event is coming from.
2588c2ecf20Sopenharmony_ci * @value: Value passed to Notify() in ACPI.
2598c2ecf20Sopenharmony_ci *
2608c2ecf20Sopenharmony_ci * This function will read the events from the device and enqueue them.
2618c2ecf20Sopenharmony_ci */
2628c2ecf20Sopenharmony_cistatic void event_device_notify(struct acpi_device *adev, u32 value)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	struct acpi_buffer event_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
2658c2ecf20Sopenharmony_ci	union acpi_object *obj;
2668c2ecf20Sopenharmony_ci	acpi_status status;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (value != EC_ACPI_NOTIFY_EVENT) {
2698c2ecf20Sopenharmony_ci		dev_err(&adev->dev, "Invalid event: 0x%08x\n", value);
2708c2ecf20Sopenharmony_ci		return;
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* Execute ACPI method to get event data buffer. */
2748c2ecf20Sopenharmony_ci	status = acpi_evaluate_object(adev->handle, EC_ACPI_GET_EVENT,
2758c2ecf20Sopenharmony_ci				      NULL, &event_buffer);
2768c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
2778c2ecf20Sopenharmony_ci		dev_err(&adev->dev, "Error executing ACPI method %s()\n",
2788c2ecf20Sopenharmony_ci			EC_ACPI_GET_EVENT);
2798c2ecf20Sopenharmony_ci		return;
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	obj = (union acpi_object *)event_buffer.pointer;
2838c2ecf20Sopenharmony_ci	if (!obj) {
2848c2ecf20Sopenharmony_ci		dev_err(&adev->dev, "Nothing returned from %s()\n",
2858c2ecf20Sopenharmony_ci			EC_ACPI_GET_EVENT);
2868c2ecf20Sopenharmony_ci		return;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci	if (obj->type != ACPI_TYPE_BUFFER) {
2898c2ecf20Sopenharmony_ci		dev_err(&adev->dev, "Invalid object returned from %s()\n",
2908c2ecf20Sopenharmony_ci			EC_ACPI_GET_EVENT);
2918c2ecf20Sopenharmony_ci		kfree(obj);
2928c2ecf20Sopenharmony_ci		return;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci	if (obj->buffer.length < sizeof(struct ec_event)) {
2958c2ecf20Sopenharmony_ci		dev_err(&adev->dev, "Invalid buffer length %d from %s()\n",
2968c2ecf20Sopenharmony_ci			obj->buffer.length, EC_ACPI_GET_EVENT);
2978c2ecf20Sopenharmony_ci		kfree(obj);
2988c2ecf20Sopenharmony_ci		return;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	enqueue_events(adev, obj->buffer.pointer, obj->buffer.length);
3028c2ecf20Sopenharmony_ci	kfree(obj);
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic int event_open(struct inode *inode, struct file *filp)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct event_device_data *dev_data;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	dev_data = container_of(inode->i_cdev, struct event_device_data, cdev);
3108c2ecf20Sopenharmony_ci	if (!dev_data->exist)
3118c2ecf20Sopenharmony_ci		return -ENODEV;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (atomic_cmpxchg(&dev_data->available, 1, 0) == 0)
3148c2ecf20Sopenharmony_ci		return -EBUSY;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* Increase refcount on device so dev_data is not freed */
3178c2ecf20Sopenharmony_ci	get_device(&dev_data->dev);
3188c2ecf20Sopenharmony_ci	stream_open(inode, filp);
3198c2ecf20Sopenharmony_ci	filp->private_data = dev_data;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	return 0;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic __poll_t event_poll(struct file *filp, poll_table *wait)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	struct event_device_data *dev_data = filp->private_data;
3278c2ecf20Sopenharmony_ci	__poll_t mask = 0;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	poll_wait(filp, &dev_data->wq, wait);
3308c2ecf20Sopenharmony_ci	if (!dev_data->exist)
3318c2ecf20Sopenharmony_ci		return EPOLLHUP;
3328c2ecf20Sopenharmony_ci	if (!event_queue_empty(dev_data->events))
3338c2ecf20Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM | EPOLLPRI;
3348c2ecf20Sopenharmony_ci	return mask;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci/**
3388c2ecf20Sopenharmony_ci * event_read() - Callback for passing event data to userspace via read().
3398c2ecf20Sopenharmony_ci * @filp: The file we are reading from.
3408c2ecf20Sopenharmony_ci * @buf: Pointer to userspace buffer to fill with one event.
3418c2ecf20Sopenharmony_ci * @count: Number of bytes requested. Must be at least EC_ACPI_MAX_EVENT_SIZE.
3428c2ecf20Sopenharmony_ci * @pos: File position pointer, irrelevant since we don't support seeking.
3438c2ecf20Sopenharmony_ci *
3448c2ecf20Sopenharmony_ci * Removes the first event from the queue, places it in the passed buffer.
3458c2ecf20Sopenharmony_ci *
3468c2ecf20Sopenharmony_ci * If there are no events in the the queue, then one of two things happens,
3478c2ecf20Sopenharmony_ci * depending on if the file was opened in nonblocking mode: If in nonblocking
3488c2ecf20Sopenharmony_ci * mode, then return -EAGAIN to say there's no data. If in blocking mode, then
3498c2ecf20Sopenharmony_ci * block until an event is available.
3508c2ecf20Sopenharmony_ci *
3518c2ecf20Sopenharmony_ci * Return: Number of bytes placed in buffer, negative error code on failure.
3528c2ecf20Sopenharmony_ci */
3538c2ecf20Sopenharmony_cistatic ssize_t event_read(struct file *filp, char __user *buf, size_t count,
3548c2ecf20Sopenharmony_ci			  loff_t *pos)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	struct event_device_data *dev_data = filp->private_data;
3578c2ecf20Sopenharmony_ci	struct ec_event *event;
3588c2ecf20Sopenharmony_ci	ssize_t n_bytes_written = 0;
3598c2ecf20Sopenharmony_ci	int err;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	/* We only will give them the entire event at once */
3628c2ecf20Sopenharmony_ci	if (count != 0 && count < EC_ACPI_MAX_EVENT_SIZE)
3638c2ecf20Sopenharmony_ci		return -EINVAL;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	spin_lock(&dev_data->queue_lock);
3668c2ecf20Sopenharmony_ci	while (event_queue_empty(dev_data->events)) {
3678c2ecf20Sopenharmony_ci		spin_unlock(&dev_data->queue_lock);
3688c2ecf20Sopenharmony_ci		if (filp->f_flags & O_NONBLOCK)
3698c2ecf20Sopenharmony_ci			return -EAGAIN;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci		err = wait_event_interruptible(dev_data->wq,
3728c2ecf20Sopenharmony_ci					!event_queue_empty(dev_data->events) ||
3738c2ecf20Sopenharmony_ci					!dev_data->exist);
3748c2ecf20Sopenharmony_ci		if (err)
3758c2ecf20Sopenharmony_ci			return err;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		/* Device was removed as we waited? */
3788c2ecf20Sopenharmony_ci		if (!dev_data->exist)
3798c2ecf20Sopenharmony_ci			return -ENODEV;
3808c2ecf20Sopenharmony_ci		spin_lock(&dev_data->queue_lock);
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci	event = event_queue_pop(dev_data->events);
3838c2ecf20Sopenharmony_ci	spin_unlock(&dev_data->queue_lock);
3848c2ecf20Sopenharmony_ci	n_bytes_written = ec_event_size(event);
3858c2ecf20Sopenharmony_ci	if (copy_to_user(buf, event, n_bytes_written))
3868c2ecf20Sopenharmony_ci		n_bytes_written = -EFAULT;
3878c2ecf20Sopenharmony_ci	kfree(event);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	return n_bytes_written;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic int event_release(struct inode *inode, struct file *filp)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct event_device_data *dev_data = filp->private_data;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	atomic_set(&dev_data->available, 1);
3978c2ecf20Sopenharmony_ci	put_device(&dev_data->dev);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	return 0;
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic const struct file_operations event_fops = {
4038c2ecf20Sopenharmony_ci	.open = event_open,
4048c2ecf20Sopenharmony_ci	.poll  = event_poll,
4058c2ecf20Sopenharmony_ci	.read = event_read,
4068c2ecf20Sopenharmony_ci	.release = event_release,
4078c2ecf20Sopenharmony_ci	.llseek = no_llseek,
4088c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
4098c2ecf20Sopenharmony_ci};
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci/**
4128c2ecf20Sopenharmony_ci * free_device_data() - Callback to free the event_device_data structure.
4138c2ecf20Sopenharmony_ci * @d: The device embedded in our device data, which we have been ref counting.
4148c2ecf20Sopenharmony_ci *
4158c2ecf20Sopenharmony_ci * This is called only after event_device_remove() has been called and all
4168c2ecf20Sopenharmony_ci * userspace programs have called event_release() on all the open file
4178c2ecf20Sopenharmony_ci * descriptors.
4188c2ecf20Sopenharmony_ci */
4198c2ecf20Sopenharmony_cistatic void free_device_data(struct device *d)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	struct event_device_data *dev_data;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	dev_data = container_of(d, struct event_device_data, dev);
4248c2ecf20Sopenharmony_ci	event_queue_free(dev_data->events);
4258c2ecf20Sopenharmony_ci	kfree(dev_data);
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic void hangup_device(struct event_device_data *dev_data)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	dev_data->exist = false;
4318c2ecf20Sopenharmony_ci	/* Wake up the waiting processes so they can close. */
4328c2ecf20Sopenharmony_ci	wake_up_interruptible(&dev_data->wq);
4338c2ecf20Sopenharmony_ci	put_device(&dev_data->dev);
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci/**
4378c2ecf20Sopenharmony_ci * event_device_add() - Callback when creating a new device.
4388c2ecf20Sopenharmony_ci * @adev: ACPI device that we will be receiving events from.
4398c2ecf20Sopenharmony_ci *
4408c2ecf20Sopenharmony_ci * This finds a free minor number for the device, allocates and initializes
4418c2ecf20Sopenharmony_ci * some device data, and creates a new device and char dev node.
4428c2ecf20Sopenharmony_ci *
4438c2ecf20Sopenharmony_ci * The device data is freed in free_device_data(), which is called when
4448c2ecf20Sopenharmony_ci * %dev_data->dev is release()ed. This happens after all references to
4458c2ecf20Sopenharmony_ci * %dev_data->dev are dropped, which happens once both event_device_remove()
4468c2ecf20Sopenharmony_ci * has been called and every open()ed file descriptor has been release()ed.
4478c2ecf20Sopenharmony_ci *
4488c2ecf20Sopenharmony_ci * Return: 0 on success, negative error code on failure.
4498c2ecf20Sopenharmony_ci */
4508c2ecf20Sopenharmony_cistatic int event_device_add(struct acpi_device *adev)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	struct event_device_data *dev_data;
4538c2ecf20Sopenharmony_ci	int error, minor;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	minor = ida_alloc_max(&event_ida, EVENT_MAX_DEV-1, GFP_KERNEL);
4568c2ecf20Sopenharmony_ci	if (minor < 0) {
4578c2ecf20Sopenharmony_ci		error = minor;
4588c2ecf20Sopenharmony_ci		dev_err(&adev->dev, "Failed to find minor number: %d\n", error);
4598c2ecf20Sopenharmony_ci		return error;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
4638c2ecf20Sopenharmony_ci	if (!dev_data) {
4648c2ecf20Sopenharmony_ci		error = -ENOMEM;
4658c2ecf20Sopenharmony_ci		goto free_minor;
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	/* Initialize the device data. */
4698c2ecf20Sopenharmony_ci	adev->driver_data = dev_data;
4708c2ecf20Sopenharmony_ci	dev_data->events = event_queue_new(queue_size);
4718c2ecf20Sopenharmony_ci	if (!dev_data->events) {
4728c2ecf20Sopenharmony_ci		kfree(dev_data);
4738c2ecf20Sopenharmony_ci		error = -ENOMEM;
4748c2ecf20Sopenharmony_ci		goto free_minor;
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci	spin_lock_init(&dev_data->queue_lock);
4778c2ecf20Sopenharmony_ci	init_waitqueue_head(&dev_data->wq);
4788c2ecf20Sopenharmony_ci	dev_data->exist = true;
4798c2ecf20Sopenharmony_ci	atomic_set(&dev_data->available, 1);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	/* Initialize the device. */
4828c2ecf20Sopenharmony_ci	dev_data->dev.devt = MKDEV(event_major, minor);
4838c2ecf20Sopenharmony_ci	dev_data->dev.class = &event_class;
4848c2ecf20Sopenharmony_ci	dev_data->dev.release = free_device_data;
4858c2ecf20Sopenharmony_ci	dev_set_name(&dev_data->dev, EVENT_DEV_NAME_FMT, minor);
4868c2ecf20Sopenharmony_ci	device_initialize(&dev_data->dev);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	/* Initialize the character device, and add it to userspace. */
4898c2ecf20Sopenharmony_ci	cdev_init(&dev_data->cdev, &event_fops);
4908c2ecf20Sopenharmony_ci	error = cdev_device_add(&dev_data->cdev, &dev_data->dev);
4918c2ecf20Sopenharmony_ci	if (error)
4928c2ecf20Sopenharmony_ci		goto free_dev_data;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	return 0;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cifree_dev_data:
4978c2ecf20Sopenharmony_ci	hangup_device(dev_data);
4988c2ecf20Sopenharmony_cifree_minor:
4998c2ecf20Sopenharmony_ci	ida_simple_remove(&event_ida, minor);
5008c2ecf20Sopenharmony_ci	return error;
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_cistatic int event_device_remove(struct acpi_device *adev)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	struct event_device_data *dev_data = adev->driver_data;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	cdev_device_del(&dev_data->cdev, &dev_data->dev);
5088c2ecf20Sopenharmony_ci	ida_simple_remove(&event_ida, MINOR(dev_data->dev.devt));
5098c2ecf20Sopenharmony_ci	hangup_device(dev_data);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	return 0;
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic const struct acpi_device_id event_acpi_ids[] = {
5158c2ecf20Sopenharmony_ci	{ "GOOG000D", 0 },
5168c2ecf20Sopenharmony_ci	{ }
5178c2ecf20Sopenharmony_ci};
5188c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, event_acpi_ids);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic struct acpi_driver event_driver = {
5218c2ecf20Sopenharmony_ci	.name = DRV_NAME,
5228c2ecf20Sopenharmony_ci	.class = DRV_NAME,
5238c2ecf20Sopenharmony_ci	.ids = event_acpi_ids,
5248c2ecf20Sopenharmony_ci	.ops = {
5258c2ecf20Sopenharmony_ci		.add = event_device_add,
5268c2ecf20Sopenharmony_ci		.notify = event_device_notify,
5278c2ecf20Sopenharmony_ci		.remove = event_device_remove,
5288c2ecf20Sopenharmony_ci	},
5298c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
5308c2ecf20Sopenharmony_ci};
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic int __init event_module_init(void)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	dev_t dev_num = 0;
5358c2ecf20Sopenharmony_ci	int ret;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	ret = class_register(&event_class);
5388c2ecf20Sopenharmony_ci	if (ret) {
5398c2ecf20Sopenharmony_ci		pr_err(DRV_NAME ": Failed registering class: %d\n", ret);
5408c2ecf20Sopenharmony_ci		return ret;
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	/* Request device numbers, starting with minor=0. Save the major num. */
5448c2ecf20Sopenharmony_ci	ret = alloc_chrdev_region(&dev_num, 0, EVENT_MAX_DEV, EVENT_DEV_NAME);
5458c2ecf20Sopenharmony_ci	if (ret) {
5468c2ecf20Sopenharmony_ci		pr_err(DRV_NAME ": Failed allocating dev numbers: %d\n", ret);
5478c2ecf20Sopenharmony_ci		goto destroy_class;
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci	event_major = MAJOR(dev_num);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	ret = acpi_bus_register_driver(&event_driver);
5528c2ecf20Sopenharmony_ci	if (ret < 0) {
5538c2ecf20Sopenharmony_ci		pr_err(DRV_NAME ": Failed registering driver: %d\n", ret);
5548c2ecf20Sopenharmony_ci		goto unregister_region;
5558c2ecf20Sopenharmony_ci	}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	return 0;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ciunregister_region:
5608c2ecf20Sopenharmony_ci	unregister_chrdev_region(MKDEV(event_major, 0), EVENT_MAX_DEV);
5618c2ecf20Sopenharmony_cidestroy_class:
5628c2ecf20Sopenharmony_ci	class_unregister(&event_class);
5638c2ecf20Sopenharmony_ci	ida_destroy(&event_ida);
5648c2ecf20Sopenharmony_ci	return ret;
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_cistatic void __exit event_module_exit(void)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	acpi_bus_unregister_driver(&event_driver);
5708c2ecf20Sopenharmony_ci	unregister_chrdev_region(MKDEV(event_major, 0), EVENT_MAX_DEV);
5718c2ecf20Sopenharmony_ci	class_unregister(&event_class);
5728c2ecf20Sopenharmony_ci	ida_destroy(&event_ida);
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_cimodule_init(event_module_init);
5768c2ecf20Sopenharmony_cimodule_exit(event_module_exit);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
5798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Wilco EC ACPI event driver");
5808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5818c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRV_NAME);
582