12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21ccd7a2aSDavid Herrmann /*
31ccd7a2aSDavid Herrmann * User-space I/O driver support for HID subsystem
41ccd7a2aSDavid Herrmann * Copyright (c) 2012 David Herrmann
51ccd7a2aSDavid Herrmann */
61ccd7a2aSDavid Herrmann
71ccd7a2aSDavid Herrmann /*
81ccd7a2aSDavid Herrmann */
91ccd7a2aSDavid Herrmann
101ccd7a2aSDavid Herrmann #include <linux/atomic.h>
11befde022SDmitry Torokhov #include <linux/compat.h>
128c01db76SEric Biggers #include <linux/cred.h>
131ccd7a2aSDavid Herrmann #include <linux/device.h>
141ccd7a2aSDavid Herrmann #include <linux/fs.h>
151ccd7a2aSDavid Herrmann #include <linux/hid.h>
161ccd7a2aSDavid Herrmann #include <linux/input.h>
171ccd7a2aSDavid Herrmann #include <linux/miscdevice.h>
181ccd7a2aSDavid Herrmann #include <linux/module.h>
191ccd7a2aSDavid Herrmann #include <linux/mutex.h>
201ccd7a2aSDavid Herrmann #include <linux/poll.h>
211ccd7a2aSDavid Herrmann #include <linux/sched.h>
221ccd7a2aSDavid Herrmann #include <linux/spinlock.h>
231ccd7a2aSDavid Herrmann #include <linux/uhid.h>
241ccd7a2aSDavid Herrmann #include <linux/wait.h>
251ccd7a2aSDavid Herrmann
261ccd7a2aSDavid Herrmann #define UHID_NAME "uhid"
27ace3d861SDavid Herrmann #define UHID_BUFSIZE 32
28ace3d861SDavid Herrmann
29ace3d861SDavid Herrmann struct uhid_device {
30d937ae5fSDavid Herrmann struct mutex devlock;
314ea5763fSJann Horn
324ea5763fSJann Horn /* This flag tracks whether the HID device is usable for commands from
334ea5763fSJann Horn * userspace. The flag is already set before hid_add_device(), which
344ea5763fSJann Horn * runs in workqueue context, to allow hid_add_device() to communicate
354ea5763fSJann Horn * with userspace.
364ea5763fSJann Horn * However, if hid_add_device() fails, the flag is cleared without
374ea5763fSJann Horn * holding devlock.
384ea5763fSJann Horn * We guarantee that if @running changes from true to false while you're
394ea5763fSJann Horn * holding @devlock, it's still fine to access @hid.
404ea5763fSJann Horn */
41d365c6cfSDavid Herrmann bool running;
42d365c6cfSDavid Herrmann
43d365c6cfSDavid Herrmann __u8 *rd_data;
44d365c6cfSDavid Herrmann uint rd_size;
45d365c6cfSDavid Herrmann
464ea5763fSJann Horn /* When this is NULL, userspace may use UHID_CREATE/UHID_CREATE2. */
47ace3d861SDavid Herrmann struct hid_device *hid;
486664ef72SDavid Herrmann struct uhid_event input_buf;
49ace3d861SDavid Herrmann
50ace3d861SDavid Herrmann wait_queue_head_t waitq;
51ace3d861SDavid Herrmann spinlock_t qlock;
52ace3d861SDavid Herrmann __u8 head;
53ace3d861SDavid Herrmann __u8 tail;
54ace3d861SDavid Herrmann struct uhid_event *outq[UHID_BUFSIZE];
55fcfcf0deSDavid Herrmann
568cad5b01SDavid Herrmann /* blocking GET_REPORT support; state changes protected by qlock */
57fcfcf0deSDavid Herrmann struct mutex report_lock;
58fcfcf0deSDavid Herrmann wait_queue_head_t report_wait;
595942b849SDavid Herrmann bool report_running;
608cad5b01SDavid Herrmann u32 report_id;
6111c22155SDavid Herrmann u32 report_type;
62fcfcf0deSDavid Herrmann struct uhid_event report_buf;
6367f8ecc5SRoderick Colenbrander struct work_struct worker;
64ace3d861SDavid Herrmann };
651ccd7a2aSDavid Herrmann
661ccd7a2aSDavid Herrmann static struct miscdevice uhid_misc;
671ccd7a2aSDavid Herrmann
uhid_device_add_worker(struct work_struct * work)6867f8ecc5SRoderick Colenbrander static void uhid_device_add_worker(struct work_struct *work)
6967f8ecc5SRoderick Colenbrander {
7067f8ecc5SRoderick Colenbrander struct uhid_device *uhid = container_of(work, struct uhid_device, worker);
7167f8ecc5SRoderick Colenbrander int ret;
7267f8ecc5SRoderick Colenbrander
7367f8ecc5SRoderick Colenbrander ret = hid_add_device(uhid->hid);
7467f8ecc5SRoderick Colenbrander if (ret) {
7567f8ecc5SRoderick Colenbrander hid_err(uhid->hid, "Cannot register HID device: error %d\n", ret);
7667f8ecc5SRoderick Colenbrander
774ea5763fSJann Horn /* We used to call hid_destroy_device() here, but that's really
784ea5763fSJann Horn * messy to get right because we have to coordinate with
794ea5763fSJann Horn * concurrent writes from userspace that might be in the middle
804ea5763fSJann Horn * of using uhid->hid.
814ea5763fSJann Horn * Just leave uhid->hid as-is for now, and clean it up when
824ea5763fSJann Horn * userspace tries to close or reinitialize the uhid instance.
834ea5763fSJann Horn *
844ea5763fSJann Horn * However, we do have to clear the ->running flag and do a
854ea5763fSJann Horn * wakeup to make sure userspace knows that the device is gone.
864ea5763fSJann Horn */
87c8e7ff41SJann Horn WRITE_ONCE(uhid->running, false);
884ea5763fSJann Horn wake_up_interruptible(&uhid->report_wait);
8967f8ecc5SRoderick Colenbrander }
9067f8ecc5SRoderick Colenbrander }
9167f8ecc5SRoderick Colenbrander
uhid_queue(struct uhid_device * uhid,struct uhid_event * ev)92ace3d861SDavid Herrmann static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev)
93ace3d861SDavid Herrmann {
94ace3d861SDavid Herrmann __u8 newhead;
95ace3d861SDavid Herrmann
96ace3d861SDavid Herrmann newhead = (uhid->head + 1) % UHID_BUFSIZE;
97ace3d861SDavid Herrmann
98ace3d861SDavid Herrmann if (newhead != uhid->tail) {
99ace3d861SDavid Herrmann uhid->outq[uhid->head] = ev;
100ace3d861SDavid Herrmann uhid->head = newhead;
101ace3d861SDavid Herrmann wake_up_interruptible(&uhid->waitq);
102ace3d861SDavid Herrmann } else {
103ace3d861SDavid Herrmann hid_warn(uhid->hid, "Output queue is full\n");
104ace3d861SDavid Herrmann kfree(ev);
105ace3d861SDavid Herrmann }
106ace3d861SDavid Herrmann }
107ace3d861SDavid Herrmann
uhid_queue_event(struct uhid_device * uhid,__u32 event)108ace3d861SDavid Herrmann static int uhid_queue_event(struct uhid_device *uhid, __u32 event)
109ace3d861SDavid Herrmann {
110ace3d861SDavid Herrmann unsigned long flags;
111ace3d861SDavid Herrmann struct uhid_event *ev;
112ace3d861SDavid Herrmann
113ace3d861SDavid Herrmann ev = kzalloc(sizeof(*ev), GFP_KERNEL);
114ace3d861SDavid Herrmann if (!ev)
115ace3d861SDavid Herrmann return -ENOMEM;
116ace3d861SDavid Herrmann
117ace3d861SDavid Herrmann ev->type = event;
118ace3d861SDavid Herrmann
119ace3d861SDavid Herrmann spin_lock_irqsave(&uhid->qlock, flags);
120ace3d861SDavid Herrmann uhid_queue(uhid, ev);
121ace3d861SDavid Herrmann spin_unlock_irqrestore(&uhid->qlock, flags);
122ace3d861SDavid Herrmann
123ace3d861SDavid Herrmann return 0;
124ace3d861SDavid Herrmann }
125ace3d861SDavid Herrmann
uhid_hid_start(struct hid_device * hid)126d365c6cfSDavid Herrmann static int uhid_hid_start(struct hid_device *hid)
127d365c6cfSDavid Herrmann {
128ec4b7deaSDavid Herrmann struct uhid_device *uhid = hid->driver_data;
129c2b2f16cSDavid Herrmann struct uhid_event *ev;
130c2b2f16cSDavid Herrmann unsigned long flags;
131ec4b7deaSDavid Herrmann
132c2b2f16cSDavid Herrmann ev = kzalloc(sizeof(*ev), GFP_KERNEL);
133c2b2f16cSDavid Herrmann if (!ev)
134c2b2f16cSDavid Herrmann return -ENOMEM;
135c2b2f16cSDavid Herrmann
136c2b2f16cSDavid Herrmann ev->type = UHID_START;
137c2b2f16cSDavid Herrmann
138c2b2f16cSDavid Herrmann if (hid->report_enum[HID_FEATURE_REPORT].numbered)
139c2b2f16cSDavid Herrmann ev->u.start.dev_flags |= UHID_DEV_NUMBERED_FEATURE_REPORTS;
140c2b2f16cSDavid Herrmann if (hid->report_enum[HID_OUTPUT_REPORT].numbered)
141c2b2f16cSDavid Herrmann ev->u.start.dev_flags |= UHID_DEV_NUMBERED_OUTPUT_REPORTS;
142c2b2f16cSDavid Herrmann if (hid->report_enum[HID_INPUT_REPORT].numbered)
143c2b2f16cSDavid Herrmann ev->u.start.dev_flags |= UHID_DEV_NUMBERED_INPUT_REPORTS;
144c2b2f16cSDavid Herrmann
145c2b2f16cSDavid Herrmann spin_lock_irqsave(&uhid->qlock, flags);
146c2b2f16cSDavid Herrmann uhid_queue(uhid, ev);
147c2b2f16cSDavid Herrmann spin_unlock_irqrestore(&uhid->qlock, flags);
148c2b2f16cSDavid Herrmann
149c2b2f16cSDavid Herrmann return 0;
150d365c6cfSDavid Herrmann }
151d365c6cfSDavid Herrmann
uhid_hid_stop(struct hid_device * hid)152d365c6cfSDavid Herrmann static void uhid_hid_stop(struct hid_device *hid)
153d365c6cfSDavid Herrmann {
154ec4b7deaSDavid Herrmann struct uhid_device *uhid = hid->driver_data;
155ec4b7deaSDavid Herrmann
156ec4b7deaSDavid Herrmann hid->claimed = 0;
157ec4b7deaSDavid Herrmann uhid_queue_event(uhid, UHID_STOP);
158d365c6cfSDavid Herrmann }
159d365c6cfSDavid Herrmann
uhid_hid_open(struct hid_device * hid)160d365c6cfSDavid Herrmann static int uhid_hid_open(struct hid_device *hid)
161d365c6cfSDavid Herrmann {
162e7191474SDavid Herrmann struct uhid_device *uhid = hid->driver_data;
163e7191474SDavid Herrmann
164e7191474SDavid Herrmann return uhid_queue_event(uhid, UHID_OPEN);
165d365c6cfSDavid Herrmann }
166d365c6cfSDavid Herrmann
uhid_hid_close(struct hid_device * hid)167d365c6cfSDavid Herrmann static void uhid_hid_close(struct hid_device *hid)
168d365c6cfSDavid Herrmann {
169e7191474SDavid Herrmann struct uhid_device *uhid = hid->driver_data;
170e7191474SDavid Herrmann
171e7191474SDavid Herrmann uhid_queue_event(uhid, UHID_CLOSE);
172d365c6cfSDavid Herrmann }
173d365c6cfSDavid Herrmann
uhid_hid_parse(struct hid_device * hid)174d365c6cfSDavid Herrmann static int uhid_hid_parse(struct hid_device *hid)
175d365c6cfSDavid Herrmann {
176037c061bSDavid Herrmann struct uhid_device *uhid = hid->driver_data;
177037c061bSDavid Herrmann
178037c061bSDavid Herrmann return hid_parse_report(hid, uhid->rd_data, uhid->rd_size);
179d365c6cfSDavid Herrmann }
180d365c6cfSDavid Herrmann
18111c22155SDavid Herrmann /* must be called with report_lock held */
__uhid_report_queue_and_wait(struct uhid_device * uhid,struct uhid_event * ev,__u32 * report_id)18211c22155SDavid Herrmann static int __uhid_report_queue_and_wait(struct uhid_device *uhid,
18311c22155SDavid Herrmann struct uhid_event *ev,
18411c22155SDavid Herrmann __u32 *report_id)
185289a7162SJiri Kosina {
186289a7162SJiri Kosina unsigned long flags;
187289a7162SJiri Kosina int ret;
188289a7162SJiri Kosina
189289a7162SJiri Kosina spin_lock_irqsave(&uhid->qlock, flags);
19011c22155SDavid Herrmann *report_id = ++uhid->report_id;
1918493eccaSBenjamin Tissoires uhid->report_type = ev->type + 1;
1925942b849SDavid Herrmann uhid->report_running = true;
193289a7162SJiri Kosina uhid_queue(uhid, ev);
194289a7162SJiri Kosina spin_unlock_irqrestore(&uhid->qlock, flags);
195289a7162SJiri Kosina
196289a7162SJiri Kosina ret = wait_event_interruptible_timeout(uhid->report_wait,
197c8e7ff41SJann Horn !uhid->report_running || !READ_ONCE(uhid->running),
1980e0d7520SDavid Herrmann 5 * HZ);
199c8e7ff41SJann Horn if (!ret || !READ_ONCE(uhid->running) || uhid->report_running)
200289a7162SJiri Kosina ret = -EIO;
20111c22155SDavid Herrmann else if (ret < 0)
202289a7162SJiri Kosina ret = -ERESTARTSYS;
20311c22155SDavid Herrmann else
204289a7162SJiri Kosina ret = 0;
205289a7162SJiri Kosina
2065942b849SDavid Herrmann uhid->report_running = false;
207289a7162SJiri Kosina
20811c22155SDavid Herrmann return ret;
20911c22155SDavid Herrmann }
21011c22155SDavid Herrmann
uhid_report_wake_up(struct uhid_device * uhid,u32 id,const struct uhid_event * ev)21111c22155SDavid Herrmann static void uhid_report_wake_up(struct uhid_device *uhid, u32 id,
21211c22155SDavid Herrmann const struct uhid_event *ev)
21311c22155SDavid Herrmann {
21411c22155SDavid Herrmann unsigned long flags;
21511c22155SDavid Herrmann
21611c22155SDavid Herrmann spin_lock_irqsave(&uhid->qlock, flags);
21711c22155SDavid Herrmann
21811c22155SDavid Herrmann /* id for old report; drop it silently */
21911c22155SDavid Herrmann if (uhid->report_type != ev->type || uhid->report_id != id)
22011c22155SDavid Herrmann goto unlock;
22111c22155SDavid Herrmann if (!uhid->report_running)
22211c22155SDavid Herrmann goto unlock;
22311c22155SDavid Herrmann
22411c22155SDavid Herrmann memcpy(&uhid->report_buf, ev, sizeof(*ev));
22511c22155SDavid Herrmann uhid->report_running = false;
22611c22155SDavid Herrmann wake_up_interruptible(&uhid->report_wait);
22711c22155SDavid Herrmann
22811c22155SDavid Herrmann unlock:
22911c22155SDavid Herrmann spin_unlock_irqrestore(&uhid->qlock, flags);
23011c22155SDavid Herrmann }
23111c22155SDavid Herrmann
uhid_hid_get_report(struct hid_device * hid,unsigned char rnum,u8 * buf,size_t count,u8 rtype)23211c22155SDavid Herrmann static int uhid_hid_get_report(struct hid_device *hid, unsigned char rnum,
23311c22155SDavid Herrmann u8 *buf, size_t count, u8 rtype)
23411c22155SDavid Herrmann {
23511c22155SDavid Herrmann struct uhid_device *uhid = hid->driver_data;
23611c22155SDavid Herrmann struct uhid_get_report_reply_req *req;
23711c22155SDavid Herrmann struct uhid_event *ev;
23811c22155SDavid Herrmann int ret;
23911c22155SDavid Herrmann
240c8e7ff41SJann Horn if (!READ_ONCE(uhid->running))
24111c22155SDavid Herrmann return -EIO;
24211c22155SDavid Herrmann
24311c22155SDavid Herrmann ev = kzalloc(sizeof(*ev), GFP_KERNEL);
24411c22155SDavid Herrmann if (!ev)
24511c22155SDavid Herrmann return -ENOMEM;
24611c22155SDavid Herrmann
24711c22155SDavid Herrmann ev->type = UHID_GET_REPORT;
24811c22155SDavid Herrmann ev->u.get_report.rnum = rnum;
24911c22155SDavid Herrmann ev->u.get_report.rtype = rtype;
25011c22155SDavid Herrmann
25111c22155SDavid Herrmann ret = mutex_lock_interruptible(&uhid->report_lock);
25211c22155SDavid Herrmann if (ret) {
25311c22155SDavid Herrmann kfree(ev);
25411c22155SDavid Herrmann return ret;
25511c22155SDavid Herrmann }
25611c22155SDavid Herrmann
25711c22155SDavid Herrmann /* this _always_ takes ownership of @ev */
25811c22155SDavid Herrmann ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.get_report.id);
25911c22155SDavid Herrmann if (ret)
26011c22155SDavid Herrmann goto unlock;
26111c22155SDavid Herrmann
26211c22155SDavid Herrmann req = &uhid->report_buf.u.get_report_reply;
26311c22155SDavid Herrmann if (req->err) {
26411c22155SDavid Herrmann ret = -EIO;
26511c22155SDavid Herrmann } else {
26611c22155SDavid Herrmann ret = min3(count, (size_t)req->size, (size_t)UHID_DATA_MAX);
26711c22155SDavid Herrmann memcpy(buf, req->data, ret);
26811c22155SDavid Herrmann }
26911c22155SDavid Herrmann
270289a7162SJiri Kosina unlock:
271289a7162SJiri Kosina mutex_unlock(&uhid->report_lock);
27211c22155SDavid Herrmann return ret;
27311c22155SDavid Herrmann }
27411c22155SDavid Herrmann
uhid_hid_set_report(struct hid_device * hid,unsigned char rnum,const u8 * buf,size_t count,u8 rtype)27511c22155SDavid Herrmann static int uhid_hid_set_report(struct hid_device *hid, unsigned char rnum,
27611c22155SDavid Herrmann const u8 *buf, size_t count, u8 rtype)
27711c22155SDavid Herrmann {
27811c22155SDavid Herrmann struct uhid_device *uhid = hid->driver_data;
27911c22155SDavid Herrmann struct uhid_event *ev;
28011c22155SDavid Herrmann int ret;
28111c22155SDavid Herrmann
282c8e7ff41SJann Horn if (!READ_ONCE(uhid->running) || count > UHID_DATA_MAX)
28311c22155SDavid Herrmann return -EIO;
28411c22155SDavid Herrmann
28511c22155SDavid Herrmann ev = kzalloc(sizeof(*ev), GFP_KERNEL);
28611c22155SDavid Herrmann if (!ev)
28711c22155SDavid Herrmann return -ENOMEM;
28811c22155SDavid Herrmann
28911c22155SDavid Herrmann ev->type = UHID_SET_REPORT;
29011c22155SDavid Herrmann ev->u.set_report.rnum = rnum;
29111c22155SDavid Herrmann ev->u.set_report.rtype = rtype;
29211c22155SDavid Herrmann ev->u.set_report.size = count;
29311c22155SDavid Herrmann memcpy(ev->u.set_report.data, buf, count);
29411c22155SDavid Herrmann
29511c22155SDavid Herrmann ret = mutex_lock_interruptible(&uhid->report_lock);
29611c22155SDavid Herrmann if (ret) {
29711c22155SDavid Herrmann kfree(ev);
29811c22155SDavid Herrmann return ret;
29911c22155SDavid Herrmann }
30011c22155SDavid Herrmann
30111c22155SDavid Herrmann /* this _always_ takes ownership of @ev */
30211c22155SDavid Herrmann ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.set_report.id);
30311c22155SDavid Herrmann if (ret)
30411c22155SDavid Herrmann goto unlock;
30511c22155SDavid Herrmann
30611c22155SDavid Herrmann if (uhid->report_buf.u.set_report_reply.err)
30711c22155SDavid Herrmann ret = -EIO;
30811c22155SDavid Herrmann else
30911c22155SDavid Herrmann ret = count;
31011c22155SDavid Herrmann
31111c22155SDavid Herrmann unlock:
31211c22155SDavid Herrmann mutex_unlock(&uhid->report_lock);
31311c22155SDavid Herrmann return ret;
314289a7162SJiri Kosina }
315289a7162SJiri Kosina
uhid_hid_raw_request(struct hid_device * hid,unsigned char reportnum,__u8 * buf,size_t len,unsigned char rtype,int reqtype)3167c4003bcSDavid Herrmann static int uhid_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
3177c4003bcSDavid Herrmann __u8 *buf, size_t len, unsigned char rtype,
3187c4003bcSDavid Herrmann int reqtype)
3197c4003bcSDavid Herrmann {
32011c22155SDavid Herrmann u8 u_rtype;
32111c22155SDavid Herrmann
32211c22155SDavid Herrmann switch (rtype) {
32311c22155SDavid Herrmann case HID_FEATURE_REPORT:
32411c22155SDavid Herrmann u_rtype = UHID_FEATURE_REPORT;
32511c22155SDavid Herrmann break;
32611c22155SDavid Herrmann case HID_OUTPUT_REPORT:
32711c22155SDavid Herrmann u_rtype = UHID_OUTPUT_REPORT;
32811c22155SDavid Herrmann break;
32911c22155SDavid Herrmann case HID_INPUT_REPORT:
33011c22155SDavid Herrmann u_rtype = UHID_INPUT_REPORT;
33111c22155SDavid Herrmann break;
33211c22155SDavid Herrmann default:
33311c22155SDavid Herrmann return -EINVAL;
33411c22155SDavid Herrmann }
33511c22155SDavid Herrmann
3367c4003bcSDavid Herrmann switch (reqtype) {
3377c4003bcSDavid Herrmann case HID_REQ_GET_REPORT:
33811c22155SDavid Herrmann return uhid_hid_get_report(hid, reportnum, buf, len, u_rtype);
3397c4003bcSDavid Herrmann case HID_REQ_SET_REPORT:
34011c22155SDavid Herrmann return uhid_hid_set_report(hid, reportnum, buf, len, u_rtype);
3417c4003bcSDavid Herrmann default:
3427c4003bcSDavid Herrmann return -EIO;
3437c4003bcSDavid Herrmann }
3447c4003bcSDavid Herrmann }
3457c4003bcSDavid Herrmann
uhid_hid_output_raw(struct hid_device * hid,__u8 * buf,size_t count,unsigned char report_type)346d365c6cfSDavid Herrmann static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count,
347d365c6cfSDavid Herrmann unsigned char report_type)
348d365c6cfSDavid Herrmann {
3493b3baa82SDavid Herrmann struct uhid_device *uhid = hid->driver_data;
3503b3baa82SDavid Herrmann __u8 rtype;
3513b3baa82SDavid Herrmann unsigned long flags;
3523b3baa82SDavid Herrmann struct uhid_event *ev;
3533b3baa82SDavid Herrmann
3543b3baa82SDavid Herrmann switch (report_type) {
3553b3baa82SDavid Herrmann case HID_FEATURE_REPORT:
3563b3baa82SDavid Herrmann rtype = UHID_FEATURE_REPORT;
3573b3baa82SDavid Herrmann break;
3583b3baa82SDavid Herrmann case HID_OUTPUT_REPORT:
3593b3baa82SDavid Herrmann rtype = UHID_OUTPUT_REPORT;
3603b3baa82SDavid Herrmann break;
3613b3baa82SDavid Herrmann default:
3623b3baa82SDavid Herrmann return -EINVAL;
3633b3baa82SDavid Herrmann }
3643b3baa82SDavid Herrmann
3653b3baa82SDavid Herrmann if (count < 1 || count > UHID_DATA_MAX)
3663b3baa82SDavid Herrmann return -EINVAL;
3673b3baa82SDavid Herrmann
3683b3baa82SDavid Herrmann ev = kzalloc(sizeof(*ev), GFP_KERNEL);
3693b3baa82SDavid Herrmann if (!ev)
3703b3baa82SDavid Herrmann return -ENOMEM;
3713b3baa82SDavid Herrmann
3723b3baa82SDavid Herrmann ev->type = UHID_OUTPUT;
3733b3baa82SDavid Herrmann ev->u.output.size = count;
3743b3baa82SDavid Herrmann ev->u.output.rtype = rtype;
3753b3baa82SDavid Herrmann memcpy(ev->u.output.data, buf, count);
3763b3baa82SDavid Herrmann
3773b3baa82SDavid Herrmann spin_lock_irqsave(&uhid->qlock, flags);
3783b3baa82SDavid Herrmann uhid_queue(uhid, ev);
3793b3baa82SDavid Herrmann spin_unlock_irqrestore(&uhid->qlock, flags);
3803b3baa82SDavid Herrmann
3813b3baa82SDavid Herrmann return count;
382d365c6cfSDavid Herrmann }
383d365c6cfSDavid Herrmann
uhid_hid_output_report(struct hid_device * hid,__u8 * buf,size_t count)384596cfdd8SFrank Praznik static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf,
385596cfdd8SFrank Praznik size_t count)
386596cfdd8SFrank Praznik {
38741abfb36SBenjamin Tissoires return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT);
388596cfdd8SFrank Praznik }
389596cfdd8SFrank Praznik
39052d22534SThomas Weißschuh static const struct hid_ll_driver uhid_hid_driver = {
391d365c6cfSDavid Herrmann .start = uhid_hid_start,
392d365c6cfSDavid Herrmann .stop = uhid_hid_stop,
393d365c6cfSDavid Herrmann .open = uhid_hid_open,
394d365c6cfSDavid Herrmann .close = uhid_hid_close,
395d365c6cfSDavid Herrmann .parse = uhid_hid_parse,
3967c4003bcSDavid Herrmann .raw_request = uhid_hid_raw_request,
397596cfdd8SFrank Praznik .output_report = uhid_hid_output_report,
398*1c5d4221SLee Jones .max_buffer_size = UHID_DATA_MAX,
399d365c6cfSDavid Herrmann };
400d365c6cfSDavid Herrmann
401befde022SDmitry Torokhov #ifdef CONFIG_COMPAT
402befde022SDmitry Torokhov
403befde022SDmitry Torokhov /* Apparently we haven't stepped on these rakes enough times yet. */
404befde022SDmitry Torokhov struct uhid_create_req_compat {
405befde022SDmitry Torokhov __u8 name[128];
406befde022SDmitry Torokhov __u8 phys[64];
407befde022SDmitry Torokhov __u8 uniq[64];
408befde022SDmitry Torokhov
409befde022SDmitry Torokhov compat_uptr_t rd_data;
410befde022SDmitry Torokhov __u16 rd_size;
411befde022SDmitry Torokhov
412befde022SDmitry Torokhov __u16 bus;
413befde022SDmitry Torokhov __u32 vendor;
414befde022SDmitry Torokhov __u32 product;
415befde022SDmitry Torokhov __u32 version;
416befde022SDmitry Torokhov __u32 country;
417befde022SDmitry Torokhov } __attribute__((__packed__));
418befde022SDmitry Torokhov
uhid_event_from_user(const char __user * buffer,size_t len,struct uhid_event * event)419befde022SDmitry Torokhov static int uhid_event_from_user(const char __user *buffer, size_t len,
420befde022SDmitry Torokhov struct uhid_event *event)
421befde022SDmitry Torokhov {
4227365abbaSAndy Lutomirski if (in_compat_syscall()) {
423befde022SDmitry Torokhov u32 type;
424befde022SDmitry Torokhov
425befde022SDmitry Torokhov if (get_user(type, buffer))
426befde022SDmitry Torokhov return -EFAULT;
427befde022SDmitry Torokhov
428befde022SDmitry Torokhov if (type == UHID_CREATE) {
429befde022SDmitry Torokhov /*
430befde022SDmitry Torokhov * This is our messed up request with compat pointer.
431befde022SDmitry Torokhov * It is largish (more than 256 bytes) so we better
432befde022SDmitry Torokhov * allocate it from the heap.
433befde022SDmitry Torokhov */
434befde022SDmitry Torokhov struct uhid_create_req_compat *compat;
435befde022SDmitry Torokhov
43680897aa7SDavid Herrmann compat = kzalloc(sizeof(*compat), GFP_KERNEL);
437befde022SDmitry Torokhov if (!compat)
438befde022SDmitry Torokhov return -ENOMEM;
439befde022SDmitry Torokhov
440befde022SDmitry Torokhov buffer += sizeof(type);
441befde022SDmitry Torokhov len -= sizeof(type);
442befde022SDmitry Torokhov if (copy_from_user(compat, buffer,
443befde022SDmitry Torokhov min(len, sizeof(*compat)))) {
444befde022SDmitry Torokhov kfree(compat);
445befde022SDmitry Torokhov return -EFAULT;
446befde022SDmitry Torokhov }
447befde022SDmitry Torokhov
448befde022SDmitry Torokhov /* Shuffle the data over to proper structure */
449befde022SDmitry Torokhov event->type = type;
450befde022SDmitry Torokhov
451befde022SDmitry Torokhov memcpy(event->u.create.name, compat->name,
452befde022SDmitry Torokhov sizeof(compat->name));
453befde022SDmitry Torokhov memcpy(event->u.create.phys, compat->phys,
454befde022SDmitry Torokhov sizeof(compat->phys));
455befde022SDmitry Torokhov memcpy(event->u.create.uniq, compat->uniq,
456befde022SDmitry Torokhov sizeof(compat->uniq));
457befde022SDmitry Torokhov
458befde022SDmitry Torokhov event->u.create.rd_data = compat_ptr(compat->rd_data);
459befde022SDmitry Torokhov event->u.create.rd_size = compat->rd_size;
460befde022SDmitry Torokhov
461befde022SDmitry Torokhov event->u.create.bus = compat->bus;
462befde022SDmitry Torokhov event->u.create.vendor = compat->vendor;
463befde022SDmitry Torokhov event->u.create.product = compat->product;
464befde022SDmitry Torokhov event->u.create.version = compat->version;
465befde022SDmitry Torokhov event->u.create.country = compat->country;
466befde022SDmitry Torokhov
467befde022SDmitry Torokhov kfree(compat);
468befde022SDmitry Torokhov return 0;
469befde022SDmitry Torokhov }
470befde022SDmitry Torokhov /* All others can be copied directly */
471befde022SDmitry Torokhov }
472befde022SDmitry Torokhov
473befde022SDmitry Torokhov if (copy_from_user(event, buffer, min(len, sizeof(*event))))
474befde022SDmitry Torokhov return -EFAULT;
475befde022SDmitry Torokhov
476befde022SDmitry Torokhov return 0;
477befde022SDmitry Torokhov }
478befde022SDmitry Torokhov #else
uhid_event_from_user(const char __user * buffer,size_t len,struct uhid_event * event)479befde022SDmitry Torokhov static int uhid_event_from_user(const char __user *buffer, size_t len,
480befde022SDmitry Torokhov struct uhid_event *event)
481befde022SDmitry Torokhov {
482befde022SDmitry Torokhov if (copy_from_user(event, buffer, min(len, sizeof(*event))))
483befde022SDmitry Torokhov return -EFAULT;
484befde022SDmitry Torokhov
485befde022SDmitry Torokhov return 0;
486befde022SDmitry Torokhov }
487befde022SDmitry Torokhov #endif
488befde022SDmitry Torokhov
uhid_dev_create2(struct uhid_device * uhid,const struct uhid_event * ev)4894522643aSPetri Gynther static int uhid_dev_create2(struct uhid_device *uhid,
4904522643aSPetri Gynther const struct uhid_event *ev)
4914522643aSPetri Gynther {
4924522643aSPetri Gynther struct hid_device *hid;
49325be7fe2SDavid Herrmann size_t rd_size, len;
49441c4a464SDavid Herrmann void *rd_data;
4954522643aSPetri Gynther int ret;
4964522643aSPetri Gynther
4974ea5763fSJann Horn if (uhid->hid)
4984522643aSPetri Gynther return -EALREADY;
4994522643aSPetri Gynther
50041c4a464SDavid Herrmann rd_size = ev->u.create2.rd_size;
50141c4a464SDavid Herrmann if (rd_size <= 0 || rd_size > HID_MAX_DESCRIPTOR_SIZE)
5024522643aSPetri Gynther return -EINVAL;
5034522643aSPetri Gynther
50441c4a464SDavid Herrmann rd_data = kmemdup(ev->u.create2.rd_data, rd_size, GFP_KERNEL);
50541c4a464SDavid Herrmann if (!rd_data)
5064522643aSPetri Gynther return -ENOMEM;
5074522643aSPetri Gynther
50841c4a464SDavid Herrmann uhid->rd_size = rd_size;
50941c4a464SDavid Herrmann uhid->rd_data = rd_data;
51041c4a464SDavid Herrmann
5114522643aSPetri Gynther hid = hid_allocate_device();
5124522643aSPetri Gynther if (IS_ERR(hid)) {
5134522643aSPetri Gynther ret = PTR_ERR(hid);
5144522643aSPetri Gynther goto err_free;
5154522643aSPetri Gynther }
5164522643aSPetri Gynther
5174d26d1d1SDavid Herrmann /* @hid is zero-initialized, strncpy() is correct, strlcpy() not */
5184d26d1d1SDavid Herrmann len = min(sizeof(hid->name), sizeof(ev->u.create2.name)) - 1;
5194d26d1d1SDavid Herrmann strncpy(hid->name, ev->u.create2.name, len);
5204d26d1d1SDavid Herrmann len = min(sizeof(hid->phys), sizeof(ev->u.create2.phys)) - 1;
5214d26d1d1SDavid Herrmann strncpy(hid->phys, ev->u.create2.phys, len);
5224d26d1d1SDavid Herrmann len = min(sizeof(hid->uniq), sizeof(ev->u.create2.uniq)) - 1;
5234d26d1d1SDavid Herrmann strncpy(hid->uniq, ev->u.create2.uniq, len);
5244522643aSPetri Gynther
5254522643aSPetri Gynther hid->ll_driver = &uhid_hid_driver;
5264522643aSPetri Gynther hid->bus = ev->u.create2.bus;
5274522643aSPetri Gynther hid->vendor = ev->u.create2.vendor;
5284522643aSPetri Gynther hid->product = ev->u.create2.product;
5294522643aSPetri Gynther hid->version = ev->u.create2.version;
5304522643aSPetri Gynther hid->country = ev->u.create2.country;
5314522643aSPetri Gynther hid->driver_data = uhid;
5324522643aSPetri Gynther hid->dev.parent = uhid_misc.this_device;
5334522643aSPetri Gynther
5344522643aSPetri Gynther uhid->hid = hid;
5354522643aSPetri Gynther uhid->running = true;
5364522643aSPetri Gynther
53767f8ecc5SRoderick Colenbrander /* Adding of a HID device is done through a worker, to allow HID drivers
53867f8ecc5SRoderick Colenbrander * which use feature requests during .probe to work, without they would
53967f8ecc5SRoderick Colenbrander * be blocked on devlock, which is held by uhid_char_write.
54067f8ecc5SRoderick Colenbrander */
54167f8ecc5SRoderick Colenbrander schedule_work(&uhid->worker);
5424522643aSPetri Gynther
5434522643aSPetri Gynther return 0;
5444522643aSPetri Gynther
5454522643aSPetri Gynther err_free:
5464522643aSPetri Gynther kfree(uhid->rd_data);
54741c4a464SDavid Herrmann uhid->rd_data = NULL;
54841c4a464SDavid Herrmann uhid->rd_size = 0;
5494522643aSPetri Gynther return ret;
5504522643aSPetri Gynther }
5514522643aSPetri Gynther
uhid_dev_create(struct uhid_device * uhid,struct uhid_event * ev)55256c47754SDavid Herrmann static int uhid_dev_create(struct uhid_device *uhid,
55356c47754SDavid Herrmann struct uhid_event *ev)
55456c47754SDavid Herrmann {
55556c47754SDavid Herrmann struct uhid_create_req orig;
55656c47754SDavid Herrmann
55756c47754SDavid Herrmann orig = ev->u.create;
55856c47754SDavid Herrmann
55956c47754SDavid Herrmann if (orig.rd_size <= 0 || orig.rd_size > HID_MAX_DESCRIPTOR_SIZE)
56056c47754SDavid Herrmann return -EINVAL;
56156c47754SDavid Herrmann if (copy_from_user(&ev->u.create2.rd_data, orig.rd_data, orig.rd_size))
56256c47754SDavid Herrmann return -EFAULT;
56356c47754SDavid Herrmann
56456c47754SDavid Herrmann memcpy(ev->u.create2.name, orig.name, sizeof(orig.name));
56556c47754SDavid Herrmann memcpy(ev->u.create2.phys, orig.phys, sizeof(orig.phys));
56656c47754SDavid Herrmann memcpy(ev->u.create2.uniq, orig.uniq, sizeof(orig.uniq));
56756c47754SDavid Herrmann ev->u.create2.rd_size = orig.rd_size;
56856c47754SDavid Herrmann ev->u.create2.bus = orig.bus;
56956c47754SDavid Herrmann ev->u.create2.vendor = orig.vendor;
57056c47754SDavid Herrmann ev->u.create2.product = orig.product;
57156c47754SDavid Herrmann ev->u.create2.version = orig.version;
57256c47754SDavid Herrmann ev->u.create2.country = orig.country;
57356c47754SDavid Herrmann
57456c47754SDavid Herrmann return uhid_dev_create2(uhid, ev);
57556c47754SDavid Herrmann }
57656c47754SDavid Herrmann
uhid_dev_destroy(struct uhid_device * uhid)577d365c6cfSDavid Herrmann static int uhid_dev_destroy(struct uhid_device *uhid)
578d365c6cfSDavid Herrmann {
5794ea5763fSJann Horn if (!uhid->hid)
580d365c6cfSDavid Herrmann return -EINVAL;
581d365c6cfSDavid Herrmann
582c8e7ff41SJann Horn WRITE_ONCE(uhid->running, false);
583fcfcf0deSDavid Herrmann wake_up_interruptible(&uhid->report_wait);
584d365c6cfSDavid Herrmann
58567f8ecc5SRoderick Colenbrander cancel_work_sync(&uhid->worker);
58667f8ecc5SRoderick Colenbrander
587d365c6cfSDavid Herrmann hid_destroy_device(uhid->hid);
5884ea5763fSJann Horn uhid->hid = NULL;
589d365c6cfSDavid Herrmann kfree(uhid->rd_data);
590d365c6cfSDavid Herrmann
591d365c6cfSDavid Herrmann return 0;
592d365c6cfSDavid Herrmann }
593d365c6cfSDavid Herrmann
uhid_dev_input(struct uhid_device * uhid,struct uhid_event * ev)5945e87a36aSDavid Herrmann static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev)
5955e87a36aSDavid Herrmann {
596c8e7ff41SJann Horn if (!READ_ONCE(uhid->running))
5975e87a36aSDavid Herrmann return -EINVAL;
5985e87a36aSDavid Herrmann
5995e87a36aSDavid Herrmann hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data,
6005e87a36aSDavid Herrmann min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0);
6015e87a36aSDavid Herrmann
6025e87a36aSDavid Herrmann return 0;
6035e87a36aSDavid Herrmann }
6045e87a36aSDavid Herrmann
uhid_dev_input2(struct uhid_device * uhid,struct uhid_event * ev)6054522643aSPetri Gynther static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev)
6064522643aSPetri Gynther {
607c8e7ff41SJann Horn if (!READ_ONCE(uhid->running))
6084522643aSPetri Gynther return -EINVAL;
6094522643aSPetri Gynther
6104522643aSPetri Gynther hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input2.data,
6114522643aSPetri Gynther min_t(size_t, ev->u.input2.size, UHID_DATA_MAX), 0);
6124522643aSPetri Gynther
6134522643aSPetri Gynther return 0;
6144522643aSPetri Gynther }
6154522643aSPetri Gynther
uhid_dev_get_report_reply(struct uhid_device * uhid,struct uhid_event * ev)616fa71f32bSDavid Herrmann static int uhid_dev_get_report_reply(struct uhid_device *uhid,
617fcfcf0deSDavid Herrmann struct uhid_event *ev)
618fcfcf0deSDavid Herrmann {
619c8e7ff41SJann Horn if (!READ_ONCE(uhid->running))
620fcfcf0deSDavid Herrmann return -EINVAL;
621fcfcf0deSDavid Herrmann
62211c22155SDavid Herrmann uhid_report_wake_up(uhid, ev->u.get_report_reply.id, ev);
62311c22155SDavid Herrmann return 0;
62411c22155SDavid Herrmann }
625fcfcf0deSDavid Herrmann
uhid_dev_set_report_reply(struct uhid_device * uhid,struct uhid_event * ev)62611c22155SDavid Herrmann static int uhid_dev_set_report_reply(struct uhid_device *uhid,
62711c22155SDavid Herrmann struct uhid_event *ev)
62811c22155SDavid Herrmann {
629c8e7ff41SJann Horn if (!READ_ONCE(uhid->running))
63011c22155SDavid Herrmann return -EINVAL;
631fcfcf0deSDavid Herrmann
63211c22155SDavid Herrmann uhid_report_wake_up(uhid, ev->u.set_report_reply.id, ev);
633fcfcf0deSDavid Herrmann return 0;
634fcfcf0deSDavid Herrmann }
635fcfcf0deSDavid Herrmann
uhid_char_open(struct inode * inode,struct file * file)6361ccd7a2aSDavid Herrmann static int uhid_char_open(struct inode *inode, struct file *file)
6371ccd7a2aSDavid Herrmann {
638ace3d861SDavid Herrmann struct uhid_device *uhid;
639ace3d861SDavid Herrmann
640ace3d861SDavid Herrmann uhid = kzalloc(sizeof(*uhid), GFP_KERNEL);
641ace3d861SDavid Herrmann if (!uhid)
642ace3d861SDavid Herrmann return -ENOMEM;
643ace3d861SDavid Herrmann
644d937ae5fSDavid Herrmann mutex_init(&uhid->devlock);
645fcfcf0deSDavid Herrmann mutex_init(&uhid->report_lock);
646ace3d861SDavid Herrmann spin_lock_init(&uhid->qlock);
647ace3d861SDavid Herrmann init_waitqueue_head(&uhid->waitq);
648fcfcf0deSDavid Herrmann init_waitqueue_head(&uhid->report_wait);
649d365c6cfSDavid Herrmann uhid->running = false;
65067f8ecc5SRoderick Colenbrander INIT_WORK(&uhid->worker, uhid_device_add_worker);
651ace3d861SDavid Herrmann
652ace3d861SDavid Herrmann file->private_data = uhid;
653c5bf68feSKirill Smelkov stream_open(inode, file);
654ace3d861SDavid Herrmann
6551ccd7a2aSDavid Herrmann return 0;
6561ccd7a2aSDavid Herrmann }
6571ccd7a2aSDavid Herrmann
uhid_char_release(struct inode * inode,struct file * file)6581ccd7a2aSDavid Herrmann static int uhid_char_release(struct inode *inode, struct file *file)
6591ccd7a2aSDavid Herrmann {
660ace3d861SDavid Herrmann struct uhid_device *uhid = file->private_data;
661ace3d861SDavid Herrmann unsigned int i;
662ace3d861SDavid Herrmann
663d365c6cfSDavid Herrmann uhid_dev_destroy(uhid);
664d365c6cfSDavid Herrmann
665ace3d861SDavid Herrmann for (i = 0; i < UHID_BUFSIZE; ++i)
666ace3d861SDavid Herrmann kfree(uhid->outq[i]);
667ace3d861SDavid Herrmann
668ace3d861SDavid Herrmann kfree(uhid);
669ace3d861SDavid Herrmann
6701ccd7a2aSDavid Herrmann return 0;
6711ccd7a2aSDavid Herrmann }
6721ccd7a2aSDavid Herrmann
uhid_char_read(struct file * file,char __user * buffer,size_t count,loff_t * ppos)6731ccd7a2aSDavid Herrmann static ssize_t uhid_char_read(struct file *file, char __user *buffer,
6741ccd7a2aSDavid Herrmann size_t count, loff_t *ppos)
6751ccd7a2aSDavid Herrmann {
676d937ae5fSDavid Herrmann struct uhid_device *uhid = file->private_data;
677d937ae5fSDavid Herrmann int ret;
678d937ae5fSDavid Herrmann unsigned long flags;
679d937ae5fSDavid Herrmann size_t len;
680d937ae5fSDavid Herrmann
681d937ae5fSDavid Herrmann /* they need at least the "type" member of uhid_event */
682d937ae5fSDavid Herrmann if (count < sizeof(__u32))
683d937ae5fSDavid Herrmann return -EINVAL;
684d937ae5fSDavid Herrmann
685d937ae5fSDavid Herrmann try_again:
686d937ae5fSDavid Herrmann if (file->f_flags & O_NONBLOCK) {
687d937ae5fSDavid Herrmann if (uhid->head == uhid->tail)
688d937ae5fSDavid Herrmann return -EAGAIN;
689d937ae5fSDavid Herrmann } else {
690d937ae5fSDavid Herrmann ret = wait_event_interruptible(uhid->waitq,
691d937ae5fSDavid Herrmann uhid->head != uhid->tail);
692d937ae5fSDavid Herrmann if (ret)
693d937ae5fSDavid Herrmann return ret;
694d937ae5fSDavid Herrmann }
695d937ae5fSDavid Herrmann
696d937ae5fSDavid Herrmann ret = mutex_lock_interruptible(&uhid->devlock);
697d937ae5fSDavid Herrmann if (ret)
698d937ae5fSDavid Herrmann return ret;
699d937ae5fSDavid Herrmann
700d937ae5fSDavid Herrmann if (uhid->head == uhid->tail) {
701d937ae5fSDavid Herrmann mutex_unlock(&uhid->devlock);
702d937ae5fSDavid Herrmann goto try_again;
703d937ae5fSDavid Herrmann } else {
704d937ae5fSDavid Herrmann len = min(count, sizeof(**uhid->outq));
705adefb69bSVinicius Costa Gomes if (copy_to_user(buffer, uhid->outq[uhid->tail], len)) {
706d937ae5fSDavid Herrmann ret = -EFAULT;
707d937ae5fSDavid Herrmann } else {
708d937ae5fSDavid Herrmann kfree(uhid->outq[uhid->tail]);
709d937ae5fSDavid Herrmann uhid->outq[uhid->tail] = NULL;
710d937ae5fSDavid Herrmann
711d937ae5fSDavid Herrmann spin_lock_irqsave(&uhid->qlock, flags);
712d937ae5fSDavid Herrmann uhid->tail = (uhid->tail + 1) % UHID_BUFSIZE;
713d937ae5fSDavid Herrmann spin_unlock_irqrestore(&uhid->qlock, flags);
714d937ae5fSDavid Herrmann }
715d937ae5fSDavid Herrmann }
716d937ae5fSDavid Herrmann
717d937ae5fSDavid Herrmann mutex_unlock(&uhid->devlock);
718d937ae5fSDavid Herrmann return ret ? ret : len;
7191ccd7a2aSDavid Herrmann }
7201ccd7a2aSDavid Herrmann
uhid_char_write(struct file * file,const char __user * buffer,size_t count,loff_t * ppos)7211ccd7a2aSDavid Herrmann static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
7221ccd7a2aSDavid Herrmann size_t count, loff_t *ppos)
7231ccd7a2aSDavid Herrmann {
7246664ef72SDavid Herrmann struct uhid_device *uhid = file->private_data;
7256664ef72SDavid Herrmann int ret;
7266664ef72SDavid Herrmann size_t len;
7276664ef72SDavid Herrmann
7286664ef72SDavid Herrmann /* we need at least the "type" member of uhid_event */
7296664ef72SDavid Herrmann if (count < sizeof(__u32))
7306664ef72SDavid Herrmann return -EINVAL;
7316664ef72SDavid Herrmann
7326664ef72SDavid Herrmann ret = mutex_lock_interruptible(&uhid->devlock);
7336664ef72SDavid Herrmann if (ret)
7346664ef72SDavid Herrmann return ret;
7356664ef72SDavid Herrmann
7366664ef72SDavid Herrmann memset(&uhid->input_buf, 0, sizeof(uhid->input_buf));
7376664ef72SDavid Herrmann len = min(count, sizeof(uhid->input_buf));
738befde022SDmitry Torokhov
739befde022SDmitry Torokhov ret = uhid_event_from_user(buffer, len, &uhid->input_buf);
740befde022SDmitry Torokhov if (ret)
7416664ef72SDavid Herrmann goto unlock;
7426664ef72SDavid Herrmann
7436664ef72SDavid Herrmann switch (uhid->input_buf.type) {
744d365c6cfSDavid Herrmann case UHID_CREATE:
7458c01db76SEric Biggers /*
7468c01db76SEric Biggers * 'struct uhid_create_req' contains a __user pointer which is
7478c01db76SEric Biggers * copied from, so it's unsafe to allow this with elevated
7488c01db76SEric Biggers * privileges (e.g. from a setuid binary) or via kernel_write().
7498c01db76SEric Biggers */
750967747bbSArnd Bergmann if (file->f_cred != current_cred()) {
7518c01db76SEric Biggers pr_err_once("UHID_CREATE from different security context by process %d (%s), this is not allowed.\n",
7528c01db76SEric Biggers task_tgid_vnr(current), current->comm);
7538c01db76SEric Biggers ret = -EACCES;
7548c01db76SEric Biggers goto unlock;
7558c01db76SEric Biggers }
756d365c6cfSDavid Herrmann ret = uhid_dev_create(uhid, &uhid->input_buf);
757d365c6cfSDavid Herrmann break;
7584522643aSPetri Gynther case UHID_CREATE2:
7594522643aSPetri Gynther ret = uhid_dev_create2(uhid, &uhid->input_buf);
7604522643aSPetri Gynther break;
761d365c6cfSDavid Herrmann case UHID_DESTROY:
762d365c6cfSDavid Herrmann ret = uhid_dev_destroy(uhid);
763d365c6cfSDavid Herrmann break;
7645e87a36aSDavid Herrmann case UHID_INPUT:
7655e87a36aSDavid Herrmann ret = uhid_dev_input(uhid, &uhid->input_buf);
7665e87a36aSDavid Herrmann break;
7674522643aSPetri Gynther case UHID_INPUT2:
7684522643aSPetri Gynther ret = uhid_dev_input2(uhid, &uhid->input_buf);
7694522643aSPetri Gynther break;
770fa71f32bSDavid Herrmann case UHID_GET_REPORT_REPLY:
771fa71f32bSDavid Herrmann ret = uhid_dev_get_report_reply(uhid, &uhid->input_buf);
772fcfcf0deSDavid Herrmann break;
77311c22155SDavid Herrmann case UHID_SET_REPORT_REPLY:
77411c22155SDavid Herrmann ret = uhid_dev_set_report_reply(uhid, &uhid->input_buf);
77511c22155SDavid Herrmann break;
7766664ef72SDavid Herrmann default:
7776664ef72SDavid Herrmann ret = -EOPNOTSUPP;
7786664ef72SDavid Herrmann }
7796664ef72SDavid Herrmann
7806664ef72SDavid Herrmann unlock:
7816664ef72SDavid Herrmann mutex_unlock(&uhid->devlock);
7826664ef72SDavid Herrmann
7836664ef72SDavid Herrmann /* return "count" not "len" to not confuse the caller */
7846664ef72SDavid Herrmann return ret ? ret : count;
7851ccd7a2aSDavid Herrmann }
7861ccd7a2aSDavid Herrmann
uhid_char_poll(struct file * file,poll_table * wait)787afc9a42bSAl Viro static __poll_t uhid_char_poll(struct file *file, poll_table *wait)
7881ccd7a2aSDavid Herrmann {
7891f9dec1eSDavid Herrmann struct uhid_device *uhid = file->private_data;
7909e635c28SJiri Kosina __poll_t mask = EPOLLOUT | EPOLLWRNORM; /* uhid is always writable */
7911f9dec1eSDavid Herrmann
7921f9dec1eSDavid Herrmann poll_wait(file, &uhid->waitq, wait);
7931f9dec1eSDavid Herrmann
7941f9dec1eSDavid Herrmann if (uhid->head != uhid->tail)
7959e635c28SJiri Kosina mask |= EPOLLIN | EPOLLRDNORM;
7961f9dec1eSDavid Herrmann
7979e635c28SJiri Kosina return mask;
7981ccd7a2aSDavid Herrmann }
7991ccd7a2aSDavid Herrmann
8001ccd7a2aSDavid Herrmann static const struct file_operations uhid_fops = {
8011ccd7a2aSDavid Herrmann .owner = THIS_MODULE,
8021ccd7a2aSDavid Herrmann .open = uhid_char_open,
8031ccd7a2aSDavid Herrmann .release = uhid_char_release,
8041ccd7a2aSDavid Herrmann .read = uhid_char_read,
8051ccd7a2aSDavid Herrmann .write = uhid_char_write,
8061ccd7a2aSDavid Herrmann .poll = uhid_char_poll,
8071ccd7a2aSDavid Herrmann .llseek = no_llseek,
8081ccd7a2aSDavid Herrmann };
8091ccd7a2aSDavid Herrmann
8101ccd7a2aSDavid Herrmann static struct miscdevice uhid_misc = {
8111ccd7a2aSDavid Herrmann .fops = &uhid_fops,
81219872d20SDavid Herrmann .minor = UHID_MINOR,
8131ccd7a2aSDavid Herrmann .name = UHID_NAME,
8141ccd7a2aSDavid Herrmann };
815ca75d601SPrasannaKumar Muralidharan module_misc_device(uhid_misc);
8161ccd7a2aSDavid Herrmann
8171ccd7a2aSDavid Herrmann MODULE_LICENSE("GPL");
8181ccd7a2aSDavid Herrmann MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
8191ccd7a2aSDavid Herrmann MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem");
82019872d20SDavid Herrmann MODULE_ALIAS_MISCDEV(UHID_MINOR);
82160cbd53eSMarcel Holtmann MODULE_ALIAS("devname:" UHID_NAME);
822