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