162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * User-space I/O driver support for HID subsystem 462306a36Sopenharmony_ci * Copyright (c) 2012 David Herrmann 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci/* 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/atomic.h> 1162306a36Sopenharmony_ci#include <linux/compat.h> 1262306a36Sopenharmony_ci#include <linux/cred.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/fs.h> 1562306a36Sopenharmony_ci#include <linux/hid.h> 1662306a36Sopenharmony_ci#include <linux/input.h> 1762306a36Sopenharmony_ci#include <linux/miscdevice.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/mutex.h> 2062306a36Sopenharmony_ci#include <linux/poll.h> 2162306a36Sopenharmony_ci#include <linux/sched.h> 2262306a36Sopenharmony_ci#include <linux/spinlock.h> 2362306a36Sopenharmony_ci#include <linux/uhid.h> 2462306a36Sopenharmony_ci#include <linux/wait.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define UHID_NAME "uhid" 2762306a36Sopenharmony_ci#define UHID_BUFSIZE 32 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct uhid_device { 3062306a36Sopenharmony_ci struct mutex devlock; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* This flag tracks whether the HID device is usable for commands from 3362306a36Sopenharmony_ci * userspace. The flag is already set before hid_add_device(), which 3462306a36Sopenharmony_ci * runs in workqueue context, to allow hid_add_device() to communicate 3562306a36Sopenharmony_ci * with userspace. 3662306a36Sopenharmony_ci * However, if hid_add_device() fails, the flag is cleared without 3762306a36Sopenharmony_ci * holding devlock. 3862306a36Sopenharmony_ci * We guarantee that if @running changes from true to false while you're 3962306a36Sopenharmony_ci * holding @devlock, it's still fine to access @hid. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci bool running; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci __u8 *rd_data; 4462306a36Sopenharmony_ci uint rd_size; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* When this is NULL, userspace may use UHID_CREATE/UHID_CREATE2. */ 4762306a36Sopenharmony_ci struct hid_device *hid; 4862306a36Sopenharmony_ci struct uhid_event input_buf; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci wait_queue_head_t waitq; 5162306a36Sopenharmony_ci spinlock_t qlock; 5262306a36Sopenharmony_ci __u8 head; 5362306a36Sopenharmony_ci __u8 tail; 5462306a36Sopenharmony_ci struct uhid_event *outq[UHID_BUFSIZE]; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* blocking GET_REPORT support; state changes protected by qlock */ 5762306a36Sopenharmony_ci struct mutex report_lock; 5862306a36Sopenharmony_ci wait_queue_head_t report_wait; 5962306a36Sopenharmony_ci bool report_running; 6062306a36Sopenharmony_ci u32 report_id; 6162306a36Sopenharmony_ci u32 report_type; 6262306a36Sopenharmony_ci struct uhid_event report_buf; 6362306a36Sopenharmony_ci struct work_struct worker; 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic struct miscdevice uhid_misc; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void uhid_device_add_worker(struct work_struct *work) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct uhid_device *uhid = container_of(work, struct uhid_device, worker); 7162306a36Sopenharmony_ci int ret; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ret = hid_add_device(uhid->hid); 7462306a36Sopenharmony_ci if (ret) { 7562306a36Sopenharmony_ci hid_err(uhid->hid, "Cannot register HID device: error %d\n", ret); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* We used to call hid_destroy_device() here, but that's really 7862306a36Sopenharmony_ci * messy to get right because we have to coordinate with 7962306a36Sopenharmony_ci * concurrent writes from userspace that might be in the middle 8062306a36Sopenharmony_ci * of using uhid->hid. 8162306a36Sopenharmony_ci * Just leave uhid->hid as-is for now, and clean it up when 8262306a36Sopenharmony_ci * userspace tries to close or reinitialize the uhid instance. 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * However, we do have to clear the ->running flag and do a 8562306a36Sopenharmony_ci * wakeup to make sure userspace knows that the device is gone. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci WRITE_ONCE(uhid->running, false); 8862306a36Sopenharmony_ci wake_up_interruptible(&uhid->report_wait); 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci __u8 newhead; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci newhead = (uhid->head + 1) % UHID_BUFSIZE; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (newhead != uhid->tail) { 9962306a36Sopenharmony_ci uhid->outq[uhid->head] = ev; 10062306a36Sopenharmony_ci uhid->head = newhead; 10162306a36Sopenharmony_ci wake_up_interruptible(&uhid->waitq); 10262306a36Sopenharmony_ci } else { 10362306a36Sopenharmony_ci hid_warn(uhid->hid, "Output queue is full\n"); 10462306a36Sopenharmony_ci kfree(ev); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int uhid_queue_event(struct uhid_device *uhid, __u32 event) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci unsigned long flags; 11162306a36Sopenharmony_ci struct uhid_event *ev; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci ev = kzalloc(sizeof(*ev), GFP_KERNEL); 11462306a36Sopenharmony_ci if (!ev) 11562306a36Sopenharmony_ci return -ENOMEM; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci ev->type = event; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci spin_lock_irqsave(&uhid->qlock, flags); 12062306a36Sopenharmony_ci uhid_queue(uhid, ev); 12162306a36Sopenharmony_ci spin_unlock_irqrestore(&uhid->qlock, flags); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int uhid_hid_start(struct hid_device *hid) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct uhid_device *uhid = hid->driver_data; 12962306a36Sopenharmony_ci struct uhid_event *ev; 13062306a36Sopenharmony_ci unsigned long flags; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci ev = kzalloc(sizeof(*ev), GFP_KERNEL); 13362306a36Sopenharmony_ci if (!ev) 13462306a36Sopenharmony_ci return -ENOMEM; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci ev->type = UHID_START; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (hid->report_enum[HID_FEATURE_REPORT].numbered) 13962306a36Sopenharmony_ci ev->u.start.dev_flags |= UHID_DEV_NUMBERED_FEATURE_REPORTS; 14062306a36Sopenharmony_ci if (hid->report_enum[HID_OUTPUT_REPORT].numbered) 14162306a36Sopenharmony_ci ev->u.start.dev_flags |= UHID_DEV_NUMBERED_OUTPUT_REPORTS; 14262306a36Sopenharmony_ci if (hid->report_enum[HID_INPUT_REPORT].numbered) 14362306a36Sopenharmony_ci ev->u.start.dev_flags |= UHID_DEV_NUMBERED_INPUT_REPORTS; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci spin_lock_irqsave(&uhid->qlock, flags); 14662306a36Sopenharmony_ci uhid_queue(uhid, ev); 14762306a36Sopenharmony_ci spin_unlock_irqrestore(&uhid->qlock, flags); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void uhid_hid_stop(struct hid_device *hid) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct uhid_device *uhid = hid->driver_data; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci hid->claimed = 0; 15762306a36Sopenharmony_ci uhid_queue_event(uhid, UHID_STOP); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int uhid_hid_open(struct hid_device *hid) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct uhid_device *uhid = hid->driver_data; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return uhid_queue_event(uhid, UHID_OPEN); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic void uhid_hid_close(struct hid_device *hid) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct uhid_device *uhid = hid->driver_data; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci uhid_queue_event(uhid, UHID_CLOSE); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int uhid_hid_parse(struct hid_device *hid) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct uhid_device *uhid = hid->driver_data; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return hid_parse_report(hid, uhid->rd_data, uhid->rd_size); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/* must be called with report_lock held */ 18262306a36Sopenharmony_cistatic int __uhid_report_queue_and_wait(struct uhid_device *uhid, 18362306a36Sopenharmony_ci struct uhid_event *ev, 18462306a36Sopenharmony_ci __u32 *report_id) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci unsigned long flags; 18762306a36Sopenharmony_ci int ret; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci spin_lock_irqsave(&uhid->qlock, flags); 19062306a36Sopenharmony_ci *report_id = ++uhid->report_id; 19162306a36Sopenharmony_ci uhid->report_type = ev->type + 1; 19262306a36Sopenharmony_ci uhid->report_running = true; 19362306a36Sopenharmony_ci uhid_queue(uhid, ev); 19462306a36Sopenharmony_ci spin_unlock_irqrestore(&uhid->qlock, flags); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci ret = wait_event_interruptible_timeout(uhid->report_wait, 19762306a36Sopenharmony_ci !uhid->report_running || !READ_ONCE(uhid->running), 19862306a36Sopenharmony_ci 5 * HZ); 19962306a36Sopenharmony_ci if (!ret || !READ_ONCE(uhid->running) || uhid->report_running) 20062306a36Sopenharmony_ci ret = -EIO; 20162306a36Sopenharmony_ci else if (ret < 0) 20262306a36Sopenharmony_ci ret = -ERESTARTSYS; 20362306a36Sopenharmony_ci else 20462306a36Sopenharmony_ci ret = 0; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci uhid->report_running = false; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return ret; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic void uhid_report_wake_up(struct uhid_device *uhid, u32 id, 21262306a36Sopenharmony_ci const struct uhid_event *ev) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci unsigned long flags; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci spin_lock_irqsave(&uhid->qlock, flags); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* id for old report; drop it silently */ 21962306a36Sopenharmony_ci if (uhid->report_type != ev->type || uhid->report_id != id) 22062306a36Sopenharmony_ci goto unlock; 22162306a36Sopenharmony_ci if (!uhid->report_running) 22262306a36Sopenharmony_ci goto unlock; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci memcpy(&uhid->report_buf, ev, sizeof(*ev)); 22562306a36Sopenharmony_ci uhid->report_running = false; 22662306a36Sopenharmony_ci wake_up_interruptible(&uhid->report_wait); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ciunlock: 22962306a36Sopenharmony_ci spin_unlock_irqrestore(&uhid->qlock, flags); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int uhid_hid_get_report(struct hid_device *hid, unsigned char rnum, 23362306a36Sopenharmony_ci u8 *buf, size_t count, u8 rtype) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct uhid_device *uhid = hid->driver_data; 23662306a36Sopenharmony_ci struct uhid_get_report_reply_req *req; 23762306a36Sopenharmony_ci struct uhid_event *ev; 23862306a36Sopenharmony_ci int ret; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (!READ_ONCE(uhid->running)) 24162306a36Sopenharmony_ci return -EIO; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci ev = kzalloc(sizeof(*ev), GFP_KERNEL); 24462306a36Sopenharmony_ci if (!ev) 24562306a36Sopenharmony_ci return -ENOMEM; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci ev->type = UHID_GET_REPORT; 24862306a36Sopenharmony_ci ev->u.get_report.rnum = rnum; 24962306a36Sopenharmony_ci ev->u.get_report.rtype = rtype; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci ret = mutex_lock_interruptible(&uhid->report_lock); 25262306a36Sopenharmony_ci if (ret) { 25362306a36Sopenharmony_ci kfree(ev); 25462306a36Sopenharmony_ci return ret; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* this _always_ takes ownership of @ev */ 25862306a36Sopenharmony_ci ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.get_report.id); 25962306a36Sopenharmony_ci if (ret) 26062306a36Sopenharmony_ci goto unlock; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci req = &uhid->report_buf.u.get_report_reply; 26362306a36Sopenharmony_ci if (req->err) { 26462306a36Sopenharmony_ci ret = -EIO; 26562306a36Sopenharmony_ci } else { 26662306a36Sopenharmony_ci ret = min3(count, (size_t)req->size, (size_t)UHID_DATA_MAX); 26762306a36Sopenharmony_ci memcpy(buf, req->data, ret); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ciunlock: 27162306a36Sopenharmony_ci mutex_unlock(&uhid->report_lock); 27262306a36Sopenharmony_ci return ret; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic int uhid_hid_set_report(struct hid_device *hid, unsigned char rnum, 27662306a36Sopenharmony_ci const u8 *buf, size_t count, u8 rtype) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct uhid_device *uhid = hid->driver_data; 27962306a36Sopenharmony_ci struct uhid_event *ev; 28062306a36Sopenharmony_ci int ret; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (!READ_ONCE(uhid->running) || count > UHID_DATA_MAX) 28362306a36Sopenharmony_ci return -EIO; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ev = kzalloc(sizeof(*ev), GFP_KERNEL); 28662306a36Sopenharmony_ci if (!ev) 28762306a36Sopenharmony_ci return -ENOMEM; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci ev->type = UHID_SET_REPORT; 29062306a36Sopenharmony_ci ev->u.set_report.rnum = rnum; 29162306a36Sopenharmony_ci ev->u.set_report.rtype = rtype; 29262306a36Sopenharmony_ci ev->u.set_report.size = count; 29362306a36Sopenharmony_ci memcpy(ev->u.set_report.data, buf, count); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ret = mutex_lock_interruptible(&uhid->report_lock); 29662306a36Sopenharmony_ci if (ret) { 29762306a36Sopenharmony_ci kfree(ev); 29862306a36Sopenharmony_ci return ret; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* this _always_ takes ownership of @ev */ 30262306a36Sopenharmony_ci ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.set_report.id); 30362306a36Sopenharmony_ci if (ret) 30462306a36Sopenharmony_ci goto unlock; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (uhid->report_buf.u.set_report_reply.err) 30762306a36Sopenharmony_ci ret = -EIO; 30862306a36Sopenharmony_ci else 30962306a36Sopenharmony_ci ret = count; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ciunlock: 31262306a36Sopenharmony_ci mutex_unlock(&uhid->report_lock); 31362306a36Sopenharmony_ci return ret; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int uhid_hid_raw_request(struct hid_device *hid, unsigned char reportnum, 31762306a36Sopenharmony_ci __u8 *buf, size_t len, unsigned char rtype, 31862306a36Sopenharmony_ci int reqtype) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci u8 u_rtype; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci switch (rtype) { 32362306a36Sopenharmony_ci case HID_FEATURE_REPORT: 32462306a36Sopenharmony_ci u_rtype = UHID_FEATURE_REPORT; 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci case HID_OUTPUT_REPORT: 32762306a36Sopenharmony_ci u_rtype = UHID_OUTPUT_REPORT; 32862306a36Sopenharmony_ci break; 32962306a36Sopenharmony_ci case HID_INPUT_REPORT: 33062306a36Sopenharmony_ci u_rtype = UHID_INPUT_REPORT; 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci default: 33362306a36Sopenharmony_ci return -EINVAL; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci switch (reqtype) { 33762306a36Sopenharmony_ci case HID_REQ_GET_REPORT: 33862306a36Sopenharmony_ci return uhid_hid_get_report(hid, reportnum, buf, len, u_rtype); 33962306a36Sopenharmony_ci case HID_REQ_SET_REPORT: 34062306a36Sopenharmony_ci return uhid_hid_set_report(hid, reportnum, buf, len, u_rtype); 34162306a36Sopenharmony_ci default: 34262306a36Sopenharmony_ci return -EIO; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, 34762306a36Sopenharmony_ci unsigned char report_type) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct uhid_device *uhid = hid->driver_data; 35062306a36Sopenharmony_ci __u8 rtype; 35162306a36Sopenharmony_ci unsigned long flags; 35262306a36Sopenharmony_ci struct uhid_event *ev; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci switch (report_type) { 35562306a36Sopenharmony_ci case HID_FEATURE_REPORT: 35662306a36Sopenharmony_ci rtype = UHID_FEATURE_REPORT; 35762306a36Sopenharmony_ci break; 35862306a36Sopenharmony_ci case HID_OUTPUT_REPORT: 35962306a36Sopenharmony_ci rtype = UHID_OUTPUT_REPORT; 36062306a36Sopenharmony_ci break; 36162306a36Sopenharmony_ci default: 36262306a36Sopenharmony_ci return -EINVAL; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (count < 1 || count > UHID_DATA_MAX) 36662306a36Sopenharmony_ci return -EINVAL; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci ev = kzalloc(sizeof(*ev), GFP_KERNEL); 36962306a36Sopenharmony_ci if (!ev) 37062306a36Sopenharmony_ci return -ENOMEM; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci ev->type = UHID_OUTPUT; 37362306a36Sopenharmony_ci ev->u.output.size = count; 37462306a36Sopenharmony_ci ev->u.output.rtype = rtype; 37562306a36Sopenharmony_ci memcpy(ev->u.output.data, buf, count); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci spin_lock_irqsave(&uhid->qlock, flags); 37862306a36Sopenharmony_ci uhid_queue(uhid, ev); 37962306a36Sopenharmony_ci spin_unlock_irqrestore(&uhid->qlock, flags); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return count; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int uhid_hid_output_report(struct hid_device *hid, __u8 *buf, 38562306a36Sopenharmony_ci size_t count) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT); 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic const struct hid_ll_driver uhid_hid_driver = { 39162306a36Sopenharmony_ci .start = uhid_hid_start, 39262306a36Sopenharmony_ci .stop = uhid_hid_stop, 39362306a36Sopenharmony_ci .open = uhid_hid_open, 39462306a36Sopenharmony_ci .close = uhid_hid_close, 39562306a36Sopenharmony_ci .parse = uhid_hid_parse, 39662306a36Sopenharmony_ci .raw_request = uhid_hid_raw_request, 39762306a36Sopenharmony_ci .output_report = uhid_hid_output_report, 39862306a36Sopenharmony_ci .max_buffer_size = UHID_DATA_MAX, 39962306a36Sopenharmony_ci}; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/* Apparently we haven't stepped on these rakes enough times yet. */ 40462306a36Sopenharmony_cistruct uhid_create_req_compat { 40562306a36Sopenharmony_ci __u8 name[128]; 40662306a36Sopenharmony_ci __u8 phys[64]; 40762306a36Sopenharmony_ci __u8 uniq[64]; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci compat_uptr_t rd_data; 41062306a36Sopenharmony_ci __u16 rd_size; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci __u16 bus; 41362306a36Sopenharmony_ci __u32 vendor; 41462306a36Sopenharmony_ci __u32 product; 41562306a36Sopenharmony_ci __u32 version; 41662306a36Sopenharmony_ci __u32 country; 41762306a36Sopenharmony_ci} __attribute__((__packed__)); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int uhid_event_from_user(const char __user *buffer, size_t len, 42062306a36Sopenharmony_ci struct uhid_event *event) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci if (in_compat_syscall()) { 42362306a36Sopenharmony_ci u32 type; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (get_user(type, buffer)) 42662306a36Sopenharmony_ci return -EFAULT; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (type == UHID_CREATE) { 42962306a36Sopenharmony_ci /* 43062306a36Sopenharmony_ci * This is our messed up request with compat pointer. 43162306a36Sopenharmony_ci * It is largish (more than 256 bytes) so we better 43262306a36Sopenharmony_ci * allocate it from the heap. 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ci struct uhid_create_req_compat *compat; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci compat = kzalloc(sizeof(*compat), GFP_KERNEL); 43762306a36Sopenharmony_ci if (!compat) 43862306a36Sopenharmony_ci return -ENOMEM; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci buffer += sizeof(type); 44162306a36Sopenharmony_ci len -= sizeof(type); 44262306a36Sopenharmony_ci if (copy_from_user(compat, buffer, 44362306a36Sopenharmony_ci min(len, sizeof(*compat)))) { 44462306a36Sopenharmony_ci kfree(compat); 44562306a36Sopenharmony_ci return -EFAULT; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* Shuffle the data over to proper structure */ 44962306a36Sopenharmony_ci event->type = type; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci memcpy(event->u.create.name, compat->name, 45262306a36Sopenharmony_ci sizeof(compat->name)); 45362306a36Sopenharmony_ci memcpy(event->u.create.phys, compat->phys, 45462306a36Sopenharmony_ci sizeof(compat->phys)); 45562306a36Sopenharmony_ci memcpy(event->u.create.uniq, compat->uniq, 45662306a36Sopenharmony_ci sizeof(compat->uniq)); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci event->u.create.rd_data = compat_ptr(compat->rd_data); 45962306a36Sopenharmony_ci event->u.create.rd_size = compat->rd_size; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci event->u.create.bus = compat->bus; 46262306a36Sopenharmony_ci event->u.create.vendor = compat->vendor; 46362306a36Sopenharmony_ci event->u.create.product = compat->product; 46462306a36Sopenharmony_ci event->u.create.version = compat->version; 46562306a36Sopenharmony_ci event->u.create.country = compat->country; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci kfree(compat); 46862306a36Sopenharmony_ci return 0; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci /* All others can be copied directly */ 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (copy_from_user(event, buffer, min(len, sizeof(*event)))) 47462306a36Sopenharmony_ci return -EFAULT; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci#else 47962306a36Sopenharmony_cistatic int uhid_event_from_user(const char __user *buffer, size_t len, 48062306a36Sopenharmony_ci struct uhid_event *event) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci if (copy_from_user(event, buffer, min(len, sizeof(*event)))) 48362306a36Sopenharmony_ci return -EFAULT; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return 0; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci#endif 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic int uhid_dev_create2(struct uhid_device *uhid, 49062306a36Sopenharmony_ci const struct uhid_event *ev) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct hid_device *hid; 49362306a36Sopenharmony_ci size_t rd_size, len; 49462306a36Sopenharmony_ci void *rd_data; 49562306a36Sopenharmony_ci int ret; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (uhid->hid) 49862306a36Sopenharmony_ci return -EALREADY; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci rd_size = ev->u.create2.rd_size; 50162306a36Sopenharmony_ci if (rd_size <= 0 || rd_size > HID_MAX_DESCRIPTOR_SIZE) 50262306a36Sopenharmony_ci return -EINVAL; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci rd_data = kmemdup(ev->u.create2.rd_data, rd_size, GFP_KERNEL); 50562306a36Sopenharmony_ci if (!rd_data) 50662306a36Sopenharmony_ci return -ENOMEM; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci uhid->rd_size = rd_size; 50962306a36Sopenharmony_ci uhid->rd_data = rd_data; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci hid = hid_allocate_device(); 51262306a36Sopenharmony_ci if (IS_ERR(hid)) { 51362306a36Sopenharmony_ci ret = PTR_ERR(hid); 51462306a36Sopenharmony_ci goto err_free; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* @hid is zero-initialized, strncpy() is correct, strlcpy() not */ 51862306a36Sopenharmony_ci len = min(sizeof(hid->name), sizeof(ev->u.create2.name)) - 1; 51962306a36Sopenharmony_ci strncpy(hid->name, ev->u.create2.name, len); 52062306a36Sopenharmony_ci len = min(sizeof(hid->phys), sizeof(ev->u.create2.phys)) - 1; 52162306a36Sopenharmony_ci strncpy(hid->phys, ev->u.create2.phys, len); 52262306a36Sopenharmony_ci len = min(sizeof(hid->uniq), sizeof(ev->u.create2.uniq)) - 1; 52362306a36Sopenharmony_ci strncpy(hid->uniq, ev->u.create2.uniq, len); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci hid->ll_driver = &uhid_hid_driver; 52662306a36Sopenharmony_ci hid->bus = ev->u.create2.bus; 52762306a36Sopenharmony_ci hid->vendor = ev->u.create2.vendor; 52862306a36Sopenharmony_ci hid->product = ev->u.create2.product; 52962306a36Sopenharmony_ci hid->version = ev->u.create2.version; 53062306a36Sopenharmony_ci hid->country = ev->u.create2.country; 53162306a36Sopenharmony_ci hid->driver_data = uhid; 53262306a36Sopenharmony_ci hid->dev.parent = uhid_misc.this_device; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci uhid->hid = hid; 53562306a36Sopenharmony_ci uhid->running = true; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* Adding of a HID device is done through a worker, to allow HID drivers 53862306a36Sopenharmony_ci * which use feature requests during .probe to work, without they would 53962306a36Sopenharmony_ci * be blocked on devlock, which is held by uhid_char_write. 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_ci schedule_work(&uhid->worker); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cierr_free: 54662306a36Sopenharmony_ci kfree(uhid->rd_data); 54762306a36Sopenharmony_ci uhid->rd_data = NULL; 54862306a36Sopenharmony_ci uhid->rd_size = 0; 54962306a36Sopenharmony_ci return ret; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic int uhid_dev_create(struct uhid_device *uhid, 55362306a36Sopenharmony_ci struct uhid_event *ev) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci struct uhid_create_req orig; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci orig = ev->u.create; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (orig.rd_size <= 0 || orig.rd_size > HID_MAX_DESCRIPTOR_SIZE) 56062306a36Sopenharmony_ci return -EINVAL; 56162306a36Sopenharmony_ci if (copy_from_user(&ev->u.create2.rd_data, orig.rd_data, orig.rd_size)) 56262306a36Sopenharmony_ci return -EFAULT; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci memcpy(ev->u.create2.name, orig.name, sizeof(orig.name)); 56562306a36Sopenharmony_ci memcpy(ev->u.create2.phys, orig.phys, sizeof(orig.phys)); 56662306a36Sopenharmony_ci memcpy(ev->u.create2.uniq, orig.uniq, sizeof(orig.uniq)); 56762306a36Sopenharmony_ci ev->u.create2.rd_size = orig.rd_size; 56862306a36Sopenharmony_ci ev->u.create2.bus = orig.bus; 56962306a36Sopenharmony_ci ev->u.create2.vendor = orig.vendor; 57062306a36Sopenharmony_ci ev->u.create2.product = orig.product; 57162306a36Sopenharmony_ci ev->u.create2.version = orig.version; 57262306a36Sopenharmony_ci ev->u.create2.country = orig.country; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return uhid_dev_create2(uhid, ev); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic int uhid_dev_destroy(struct uhid_device *uhid) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci if (!uhid->hid) 58062306a36Sopenharmony_ci return -EINVAL; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci WRITE_ONCE(uhid->running, false); 58362306a36Sopenharmony_ci wake_up_interruptible(&uhid->report_wait); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci cancel_work_sync(&uhid->worker); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci hid_destroy_device(uhid->hid); 58862306a36Sopenharmony_ci uhid->hid = NULL; 58962306a36Sopenharmony_ci kfree(uhid->rd_data); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci return 0; 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci if (!READ_ONCE(uhid->running)) 59762306a36Sopenharmony_ci return -EINVAL; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, 60062306a36Sopenharmony_ci min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci if (!READ_ONCE(uhid->running)) 60862306a36Sopenharmony_ci return -EINVAL; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input2.data, 61162306a36Sopenharmony_ci min_t(size_t, ev->u.input2.size, UHID_DATA_MAX), 0); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci return 0; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic int uhid_dev_get_report_reply(struct uhid_device *uhid, 61762306a36Sopenharmony_ci struct uhid_event *ev) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci if (!READ_ONCE(uhid->running)) 62062306a36Sopenharmony_ci return -EINVAL; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci uhid_report_wake_up(uhid, ev->u.get_report_reply.id, ev); 62362306a36Sopenharmony_ci return 0; 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic int uhid_dev_set_report_reply(struct uhid_device *uhid, 62762306a36Sopenharmony_ci struct uhid_event *ev) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci if (!READ_ONCE(uhid->running)) 63062306a36Sopenharmony_ci return -EINVAL; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci uhid_report_wake_up(uhid, ev->u.set_report_reply.id, ev); 63362306a36Sopenharmony_ci return 0; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic int uhid_char_open(struct inode *inode, struct file *file) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct uhid_device *uhid; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci uhid = kzalloc(sizeof(*uhid), GFP_KERNEL); 64162306a36Sopenharmony_ci if (!uhid) 64262306a36Sopenharmony_ci return -ENOMEM; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci mutex_init(&uhid->devlock); 64562306a36Sopenharmony_ci mutex_init(&uhid->report_lock); 64662306a36Sopenharmony_ci spin_lock_init(&uhid->qlock); 64762306a36Sopenharmony_ci init_waitqueue_head(&uhid->waitq); 64862306a36Sopenharmony_ci init_waitqueue_head(&uhid->report_wait); 64962306a36Sopenharmony_ci uhid->running = false; 65062306a36Sopenharmony_ci INIT_WORK(&uhid->worker, uhid_device_add_worker); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci file->private_data = uhid; 65362306a36Sopenharmony_ci stream_open(inode, file); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci return 0; 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic int uhid_char_release(struct inode *inode, struct file *file) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct uhid_device *uhid = file->private_data; 66162306a36Sopenharmony_ci unsigned int i; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci uhid_dev_destroy(uhid); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci for (i = 0; i < UHID_BUFSIZE; ++i) 66662306a36Sopenharmony_ci kfree(uhid->outq[i]); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci kfree(uhid); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci return 0; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic ssize_t uhid_char_read(struct file *file, char __user *buffer, 67462306a36Sopenharmony_ci size_t count, loff_t *ppos) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci struct uhid_device *uhid = file->private_data; 67762306a36Sopenharmony_ci int ret; 67862306a36Sopenharmony_ci unsigned long flags; 67962306a36Sopenharmony_ci size_t len; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* they need at least the "type" member of uhid_event */ 68262306a36Sopenharmony_ci if (count < sizeof(__u32)) 68362306a36Sopenharmony_ci return -EINVAL; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_citry_again: 68662306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 68762306a36Sopenharmony_ci if (uhid->head == uhid->tail) 68862306a36Sopenharmony_ci return -EAGAIN; 68962306a36Sopenharmony_ci } else { 69062306a36Sopenharmony_ci ret = wait_event_interruptible(uhid->waitq, 69162306a36Sopenharmony_ci uhid->head != uhid->tail); 69262306a36Sopenharmony_ci if (ret) 69362306a36Sopenharmony_ci return ret; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci ret = mutex_lock_interruptible(&uhid->devlock); 69762306a36Sopenharmony_ci if (ret) 69862306a36Sopenharmony_ci return ret; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (uhid->head == uhid->tail) { 70162306a36Sopenharmony_ci mutex_unlock(&uhid->devlock); 70262306a36Sopenharmony_ci goto try_again; 70362306a36Sopenharmony_ci } else { 70462306a36Sopenharmony_ci len = min(count, sizeof(**uhid->outq)); 70562306a36Sopenharmony_ci if (copy_to_user(buffer, uhid->outq[uhid->tail], len)) { 70662306a36Sopenharmony_ci ret = -EFAULT; 70762306a36Sopenharmony_ci } else { 70862306a36Sopenharmony_ci kfree(uhid->outq[uhid->tail]); 70962306a36Sopenharmony_ci uhid->outq[uhid->tail] = NULL; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci spin_lock_irqsave(&uhid->qlock, flags); 71262306a36Sopenharmony_ci uhid->tail = (uhid->tail + 1) % UHID_BUFSIZE; 71362306a36Sopenharmony_ci spin_unlock_irqrestore(&uhid->qlock, flags); 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci mutex_unlock(&uhid->devlock); 71862306a36Sopenharmony_ci return ret ? ret : len; 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic ssize_t uhid_char_write(struct file *file, const char __user *buffer, 72262306a36Sopenharmony_ci size_t count, loff_t *ppos) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci struct uhid_device *uhid = file->private_data; 72562306a36Sopenharmony_ci int ret; 72662306a36Sopenharmony_ci size_t len; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* we need at least the "type" member of uhid_event */ 72962306a36Sopenharmony_ci if (count < sizeof(__u32)) 73062306a36Sopenharmony_ci return -EINVAL; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci ret = mutex_lock_interruptible(&uhid->devlock); 73362306a36Sopenharmony_ci if (ret) 73462306a36Sopenharmony_ci return ret; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci memset(&uhid->input_buf, 0, sizeof(uhid->input_buf)); 73762306a36Sopenharmony_ci len = min(count, sizeof(uhid->input_buf)); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci ret = uhid_event_from_user(buffer, len, &uhid->input_buf); 74062306a36Sopenharmony_ci if (ret) 74162306a36Sopenharmony_ci goto unlock; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci switch (uhid->input_buf.type) { 74462306a36Sopenharmony_ci case UHID_CREATE: 74562306a36Sopenharmony_ci /* 74662306a36Sopenharmony_ci * 'struct uhid_create_req' contains a __user pointer which is 74762306a36Sopenharmony_ci * copied from, so it's unsafe to allow this with elevated 74862306a36Sopenharmony_ci * privileges (e.g. from a setuid binary) or via kernel_write(). 74962306a36Sopenharmony_ci */ 75062306a36Sopenharmony_ci if (file->f_cred != current_cred()) { 75162306a36Sopenharmony_ci pr_err_once("UHID_CREATE from different security context by process %d (%s), this is not allowed.\n", 75262306a36Sopenharmony_ci task_tgid_vnr(current), current->comm); 75362306a36Sopenharmony_ci ret = -EACCES; 75462306a36Sopenharmony_ci goto unlock; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci ret = uhid_dev_create(uhid, &uhid->input_buf); 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci case UHID_CREATE2: 75962306a36Sopenharmony_ci ret = uhid_dev_create2(uhid, &uhid->input_buf); 76062306a36Sopenharmony_ci break; 76162306a36Sopenharmony_ci case UHID_DESTROY: 76262306a36Sopenharmony_ci ret = uhid_dev_destroy(uhid); 76362306a36Sopenharmony_ci break; 76462306a36Sopenharmony_ci case UHID_INPUT: 76562306a36Sopenharmony_ci ret = uhid_dev_input(uhid, &uhid->input_buf); 76662306a36Sopenharmony_ci break; 76762306a36Sopenharmony_ci case UHID_INPUT2: 76862306a36Sopenharmony_ci ret = uhid_dev_input2(uhid, &uhid->input_buf); 76962306a36Sopenharmony_ci break; 77062306a36Sopenharmony_ci case UHID_GET_REPORT_REPLY: 77162306a36Sopenharmony_ci ret = uhid_dev_get_report_reply(uhid, &uhid->input_buf); 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci case UHID_SET_REPORT_REPLY: 77462306a36Sopenharmony_ci ret = uhid_dev_set_report_reply(uhid, &uhid->input_buf); 77562306a36Sopenharmony_ci break; 77662306a36Sopenharmony_ci default: 77762306a36Sopenharmony_ci ret = -EOPNOTSUPP; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ciunlock: 78162306a36Sopenharmony_ci mutex_unlock(&uhid->devlock); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* return "count" not "len" to not confuse the caller */ 78462306a36Sopenharmony_ci return ret ? ret : count; 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic __poll_t uhid_char_poll(struct file *file, poll_table *wait) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct uhid_device *uhid = file->private_data; 79062306a36Sopenharmony_ci __poll_t mask = EPOLLOUT | EPOLLWRNORM; /* uhid is always writable */ 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci poll_wait(file, &uhid->waitq, wait); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (uhid->head != uhid->tail) 79562306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci return mask; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic const struct file_operations uhid_fops = { 80162306a36Sopenharmony_ci .owner = THIS_MODULE, 80262306a36Sopenharmony_ci .open = uhid_char_open, 80362306a36Sopenharmony_ci .release = uhid_char_release, 80462306a36Sopenharmony_ci .read = uhid_char_read, 80562306a36Sopenharmony_ci .write = uhid_char_write, 80662306a36Sopenharmony_ci .poll = uhid_char_poll, 80762306a36Sopenharmony_ci .llseek = no_llseek, 80862306a36Sopenharmony_ci}; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic struct miscdevice uhid_misc = { 81162306a36Sopenharmony_ci .fops = &uhid_fops, 81262306a36Sopenharmony_ci .minor = UHID_MINOR, 81362306a36Sopenharmony_ci .name = UHID_NAME, 81462306a36Sopenharmony_ci}; 81562306a36Sopenharmony_cimodule_misc_device(uhid_misc); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 81862306a36Sopenharmony_ciMODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>"); 81962306a36Sopenharmony_ciMODULE_DESCRIPTION("User-space I/O driver support for HID subsystem"); 82062306a36Sopenharmony_ciMODULE_ALIAS_MISCDEV(UHID_MINOR); 82162306a36Sopenharmony_ciMODULE_ALIAS("devname:" UHID_NAME); 822