Blame view

drivers/hid/uhid.c 18 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1ccd7a2a3   David Herrmann   HID: uhid: introd...
2
3
4
5
6
7
  /*
   * User-space I/O driver support for HID subsystem
   * Copyright (c) 2012 David Herrmann
   */
  
  /*
1ccd7a2a3   David Herrmann   HID: uhid: introd...
8
9
10
   */
  
  #include <linux/atomic.h>
befde0226   Dmitry Torokhov   HID: uhid: make c...
11
  #include <linux/compat.h>
8c01db761   Eric Biggers   HID: uhid: forbid...
12
  #include <linux/cred.h>
1ccd7a2a3   David Herrmann   HID: uhid: introd...
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  #include <linux/device.h>
  #include <linux/fs.h>
  #include <linux/hid.h>
  #include <linux/input.h>
  #include <linux/miscdevice.h>
  #include <linux/module.h>
  #include <linux/mutex.h>
  #include <linux/poll.h>
  #include <linux/sched.h>
  #include <linux/spinlock.h>
  #include <linux/uhid.h>
  #include <linux/wait.h>
  
  #define UHID_NAME	"uhid"
ace3d8614   David Herrmann   HID: uhid: add in...
27
28
29
  #define UHID_BUFSIZE	32
  
  struct uhid_device {
d937ae5fa   David Herrmann   HID: uhid: implem...
30
  	struct mutex devlock;
d365c6cfd   David Herrmann   HID: uhid: add UH...
31
32
33
34
  	bool running;
  
  	__u8 *rd_data;
  	uint rd_size;
ace3d8614   David Herrmann   HID: uhid: add in...
35
  	struct hid_device *hid;
6664ef72a   David Herrmann   HID: uhid: implem...
36
  	struct uhid_event input_buf;
ace3d8614   David Herrmann   HID: uhid: add in...
37
38
39
40
41
42
  
  	wait_queue_head_t waitq;
  	spinlock_t qlock;
  	__u8 head;
  	__u8 tail;
  	struct uhid_event *outq[UHID_BUFSIZE];
fcfcf0deb   David Herrmann   HID: uhid: implem...
43

8cad5b017   David Herrmann   HID: uhid: turn r...
44
  	/* blocking GET_REPORT support; state changes protected by qlock */
fcfcf0deb   David Herrmann   HID: uhid: implem...
45
46
  	struct mutex report_lock;
  	wait_queue_head_t report_wait;
5942b849b   David Herrmann   HID: uhid: invert...
47
  	bool report_running;
8cad5b017   David Herrmann   HID: uhid: turn r...
48
  	u32 report_id;
11c221553   David Herrmann   HID: uhid: implem...
49
  	u32 report_type;
fcfcf0deb   David Herrmann   HID: uhid: implem...
50
  	struct uhid_event report_buf;
67f8ecc55   Roderick Colenbrander   HID: uhid: fix ti...
51
  	struct work_struct worker;
ace3d8614   David Herrmann   HID: uhid: add in...
52
  };
1ccd7a2a3   David Herrmann   HID: uhid: introd...
53
54
  
  static struct miscdevice uhid_misc;
67f8ecc55   Roderick Colenbrander   HID: uhid: fix ti...
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
  static void uhid_device_add_worker(struct work_struct *work)
  {
  	struct uhid_device *uhid = container_of(work, struct uhid_device, worker);
  	int ret;
  
  	ret = hid_add_device(uhid->hid);
  	if (ret) {
  		hid_err(uhid->hid, "Cannot register HID device: error %d
  ", ret);
  
  		hid_destroy_device(uhid->hid);
  		uhid->hid = NULL;
  		uhid->running = false;
  	}
  }
ace3d8614   David Herrmann   HID: uhid: add in...
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
  static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev)
  {
  	__u8 newhead;
  
  	newhead = (uhid->head + 1) % UHID_BUFSIZE;
  
  	if (newhead != uhid->tail) {
  		uhid->outq[uhid->head] = ev;
  		uhid->head = newhead;
  		wake_up_interruptible(&uhid->waitq);
  	} else {
  		hid_warn(uhid->hid, "Output queue is full
  ");
  		kfree(ev);
  	}
  }
  
  static int uhid_queue_event(struct uhid_device *uhid, __u32 event)
  {
  	unsigned long flags;
  	struct uhid_event *ev;
  
  	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
  	if (!ev)
  		return -ENOMEM;
  
  	ev->type = event;
  
  	spin_lock_irqsave(&uhid->qlock, flags);
  	uhid_queue(uhid, ev);
  	spin_unlock_irqrestore(&uhid->qlock, flags);
  
  	return 0;
  }
d365c6cfd   David Herrmann   HID: uhid: add UH...
104
105
  static int uhid_hid_start(struct hid_device *hid)
  {
ec4b7dea4   David Herrmann   HID: uhid: add UH...
106
  	struct uhid_device *uhid = hid->driver_data;
c2b2f16c5   David Herrmann   HID: uhid: report...
107
108
109
110
111
112
113
114
  	struct uhid_event *ev;
  	unsigned long flags;
  
  	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
  	if (!ev)
  		return -ENOMEM;
  
  	ev->type = UHID_START;
ec4b7dea4   David Herrmann   HID: uhid: add UH...
115

c2b2f16c5   David Herrmann   HID: uhid: report...
116
117
118
119
120
121
122
123
124
125
126
127
  	if (hid->report_enum[HID_FEATURE_REPORT].numbered)
  		ev->u.start.dev_flags |= UHID_DEV_NUMBERED_FEATURE_REPORTS;
  	if (hid->report_enum[HID_OUTPUT_REPORT].numbered)
  		ev->u.start.dev_flags |= UHID_DEV_NUMBERED_OUTPUT_REPORTS;
  	if (hid->report_enum[HID_INPUT_REPORT].numbered)
  		ev->u.start.dev_flags |= UHID_DEV_NUMBERED_INPUT_REPORTS;
  
  	spin_lock_irqsave(&uhid->qlock, flags);
  	uhid_queue(uhid, ev);
  	spin_unlock_irqrestore(&uhid->qlock, flags);
  
  	return 0;
d365c6cfd   David Herrmann   HID: uhid: add UH...
128
129
130
131
  }
  
  static void uhid_hid_stop(struct hid_device *hid)
  {
ec4b7dea4   David Herrmann   HID: uhid: add UH...
132
133
134
135
  	struct uhid_device *uhid = hid->driver_data;
  
  	hid->claimed = 0;
  	uhid_queue_event(uhid, UHID_STOP);
d365c6cfd   David Herrmann   HID: uhid: add UH...
136
137
138
139
  }
  
  static int uhid_hid_open(struct hid_device *hid)
  {
e7191474a   David Herrmann   HID: uhid: forwar...
140
141
142
  	struct uhid_device *uhid = hid->driver_data;
  
  	return uhid_queue_event(uhid, UHID_OPEN);
d365c6cfd   David Herrmann   HID: uhid: add UH...
143
144
145
146
  }
  
  static void uhid_hid_close(struct hid_device *hid)
  {
e7191474a   David Herrmann   HID: uhid: forwar...
147
148
149
  	struct uhid_device *uhid = hid->driver_data;
  
  	uhid_queue_event(uhid, UHID_CLOSE);
d365c6cfd   David Herrmann   HID: uhid: add UH...
150
  }
d365c6cfd   David Herrmann   HID: uhid: add UH...
151
152
  static int uhid_hid_parse(struct hid_device *hid)
  {
037c061bc   David Herrmann   HID: uhid: forwar...
153
154
155
  	struct uhid_device *uhid = hid->driver_data;
  
  	return hid_parse_report(hid, uhid->rd_data, uhid->rd_size);
d365c6cfd   David Herrmann   HID: uhid: add UH...
156
  }
11c221553   David Herrmann   HID: uhid: implem...
157
158
159
160
161
162
163
164
165
166
  /* must be called with report_lock held */
  static int __uhid_report_queue_and_wait(struct uhid_device *uhid,
  					struct uhid_event *ev,
  					__u32 *report_id)
  {
  	unsigned long flags;
  	int ret;
  
  	spin_lock_irqsave(&uhid->qlock, flags);
  	*report_id = ++uhid->report_id;
8493ecca7   Benjamin Tissoires   HID: uHID: fix ex...
167
  	uhid->report_type = ev->type + 1;
11c221553   David Herrmann   HID: uhid: implem...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
  	uhid->report_running = true;
  	uhid_queue(uhid, ev);
  	spin_unlock_irqrestore(&uhid->qlock, flags);
  
  	ret = wait_event_interruptible_timeout(uhid->report_wait,
  				!uhid->report_running || !uhid->running,
  				5 * HZ);
  	if (!ret || !uhid->running || uhid->report_running)
  		ret = -EIO;
  	else if (ret < 0)
  		ret = -ERESTARTSYS;
  	else
  		ret = 0;
  
  	uhid->report_running = false;
  
  	return ret;
  }
  
  static void uhid_report_wake_up(struct uhid_device *uhid, u32 id,
  				const struct uhid_event *ev)
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&uhid->qlock, flags);
  
  	/* id for old report; drop it silently */
  	if (uhid->report_type != ev->type || uhid->report_id != id)
  		goto unlock;
  	if (!uhid->report_running)
  		goto unlock;
  
  	memcpy(&uhid->report_buf, ev, sizeof(*ev));
  	uhid->report_running = false;
  	wake_up_interruptible(&uhid->report_wait);
  
  unlock:
  	spin_unlock_irqrestore(&uhid->qlock, flags);
  }
fa71f32b5   David Herrmann   HID: uhid: add AB...
207
  static int uhid_hid_get_report(struct hid_device *hid, unsigned char rnum,
11c221553   David Herrmann   HID: uhid: implem...
208
  			       u8 *buf, size_t count, u8 rtype)
289a71621   Jiri Kosina   HID: uhid: reintr...
209
210
  {
  	struct uhid_device *uhid = hid->driver_data;
11c221553   David Herrmann   HID: uhid: implem...
211
  	struct uhid_get_report_reply_req *req;
289a71621   Jiri Kosina   HID: uhid: reintr...
212
  	struct uhid_event *ev;
289a71621   Jiri Kosina   HID: uhid: reintr...
213
  	int ret;
289a71621   Jiri Kosina   HID: uhid: reintr...
214
215
216
  
  	if (!uhid->running)
  		return -EIO;
11c221553   David Herrmann   HID: uhid: implem...
217
218
219
220
221
222
223
  	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
  	if (!ev)
  		return -ENOMEM;
  
  	ev->type = UHID_GET_REPORT;
  	ev->u.get_report.rnum = rnum;
  	ev->u.get_report.rtype = rtype;
289a71621   Jiri Kosina   HID: uhid: reintr...
224
225
  
  	ret = mutex_lock_interruptible(&uhid->report_lock);
11c221553   David Herrmann   HID: uhid: implem...
226
227
  	if (ret) {
  		kfree(ev);
289a71621   Jiri Kosina   HID: uhid: reintr...
228
  		return ret;
11c221553   David Herrmann   HID: uhid: implem...
229
  	}
289a71621   Jiri Kosina   HID: uhid: reintr...
230

11c221553   David Herrmann   HID: uhid: implem...
231
232
233
  	/* this _always_ takes ownership of @ev */
  	ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.get_report.id);
  	if (ret)
289a71621   Jiri Kosina   HID: uhid: reintr...
234
  		goto unlock;
11c221553   David Herrmann   HID: uhid: implem...
235
236
237
238
239
240
241
  
  	req = &uhid->report_buf.u.get_report_reply;
  	if (req->err) {
  		ret = -EIO;
  	} else {
  		ret = min3(count, (size_t)req->size, (size_t)UHID_DATA_MAX);
  		memcpy(buf, req->data, ret);
289a71621   Jiri Kosina   HID: uhid: reintr...
242
  	}
11c221553   David Herrmann   HID: uhid: implem...
243
244
245
246
  unlock:
  	mutex_unlock(&uhid->report_lock);
  	return ret;
  }
289a71621   Jiri Kosina   HID: uhid: reintr...
247

11c221553   David Herrmann   HID: uhid: implem...
248
249
250
251
252
253
  static int uhid_hid_set_report(struct hid_device *hid, unsigned char rnum,
  			       const u8 *buf, size_t count, u8 rtype)
  {
  	struct uhid_device *uhid = hid->driver_data;
  	struct uhid_event *ev;
  	int ret;
289a71621   Jiri Kosina   HID: uhid: reintr...
254

11c221553   David Herrmann   HID: uhid: implem...
255
256
  	if (!uhid->running || count > UHID_DATA_MAX)
  		return -EIO;
289a71621   Jiri Kosina   HID: uhid: reintr...
257

11c221553   David Herrmann   HID: uhid: implem...
258
259
260
  	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
  	if (!ev)
  		return -ENOMEM;
289a71621   Jiri Kosina   HID: uhid: reintr...
261

11c221553   David Herrmann   HID: uhid: implem...
262
263
264
265
266
  	ev->type = UHID_SET_REPORT;
  	ev->u.set_report.rnum = rnum;
  	ev->u.set_report.rtype = rtype;
  	ev->u.set_report.size = count;
  	memcpy(ev->u.set_report.data, buf, count);
289a71621   Jiri Kosina   HID: uhid: reintr...
267

11c221553   David Herrmann   HID: uhid: implem...
268
269
270
271
  	ret = mutex_lock_interruptible(&uhid->report_lock);
  	if (ret) {
  		kfree(ev);
  		return ret;
289a71621   Jiri Kosina   HID: uhid: reintr...
272
  	}
11c221553   David Herrmann   HID: uhid: implem...
273
274
275
276
277
278
279
280
281
  	/* this _always_ takes ownership of @ev */
  	ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.set_report.id);
  	if (ret)
  		goto unlock;
  
  	if (uhid->report_buf.u.set_report_reply.err)
  		ret = -EIO;
  	else
  		ret = count;
289a71621   Jiri Kosina   HID: uhid: reintr...
282
283
284
  
  unlock:
  	mutex_unlock(&uhid->report_lock);
11c221553   David Herrmann   HID: uhid: implem...
285
  	return ret;
289a71621   Jiri Kosina   HID: uhid: reintr...
286
  }
7c4003bc3   David Herrmann   HID: uhid: rename...
287
288
289
290
  static int uhid_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
  				__u8 *buf, size_t len, unsigned char rtype,
  				int reqtype)
  {
11c221553   David Herrmann   HID: uhid: implem...
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
  	u8 u_rtype;
  
  	switch (rtype) {
  	case HID_FEATURE_REPORT:
  		u_rtype = UHID_FEATURE_REPORT;
  		break;
  	case HID_OUTPUT_REPORT:
  		u_rtype = UHID_OUTPUT_REPORT;
  		break;
  	case HID_INPUT_REPORT:
  		u_rtype = UHID_INPUT_REPORT;
  		break;
  	default:
  		return -EINVAL;
  	}
7c4003bc3   David Herrmann   HID: uhid: rename...
306
307
  	switch (reqtype) {
  	case HID_REQ_GET_REPORT:
11c221553   David Herrmann   HID: uhid: implem...
308
  		return uhid_hid_get_report(hid, reportnum, buf, len, u_rtype);
7c4003bc3   David Herrmann   HID: uhid: rename...
309
  	case HID_REQ_SET_REPORT:
11c221553   David Herrmann   HID: uhid: implem...
310
  		return uhid_hid_set_report(hid, reportnum, buf, len, u_rtype);
7c4003bc3   David Herrmann   HID: uhid: rename...
311
312
313
314
  	default:
  		return -EIO;
  	}
  }
d365c6cfd   David Herrmann   HID: uhid: add UH...
315
316
317
  static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count,
  			       unsigned char report_type)
  {
3b3baa82e   David Herrmann   HID: uhid: forwar...
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
  	struct uhid_device *uhid = hid->driver_data;
  	__u8 rtype;
  	unsigned long flags;
  	struct uhid_event *ev;
  
  	switch (report_type) {
  	case HID_FEATURE_REPORT:
  		rtype = UHID_FEATURE_REPORT;
  		break;
  	case HID_OUTPUT_REPORT:
  		rtype = UHID_OUTPUT_REPORT;
  		break;
  	default:
  		return -EINVAL;
  	}
  
  	if (count < 1 || count > UHID_DATA_MAX)
  		return -EINVAL;
  
  	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
  	if (!ev)
  		return -ENOMEM;
  
  	ev->type = UHID_OUTPUT;
  	ev->u.output.size = count;
  	ev->u.output.rtype = rtype;
  	memcpy(ev->u.output.data, buf, count);
  
  	spin_lock_irqsave(&uhid->qlock, flags);
  	uhid_queue(uhid, ev);
  	spin_unlock_irqrestore(&uhid->qlock, flags);
  
  	return count;
d365c6cfd   David Herrmann   HID: uhid: add UH...
351
  }
596cfdd80   Frank Praznik   HID: Add the tran...
352
353
354
  static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf,
  				  size_t count)
  {
41abfb360   Benjamin Tissoires   HID: uHID: remove...
355
  	return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT);
596cfdd80   Frank Praznik   HID: Add the tran...
356
  }
fc2237a72   Jason Gerecke   HID: introduce hi...
357
  struct hid_ll_driver uhid_hid_driver = {
d365c6cfd   David Herrmann   HID: uhid: add UH...
358
359
360
361
  	.start = uhid_hid_start,
  	.stop = uhid_hid_stop,
  	.open = uhid_hid_open,
  	.close = uhid_hid_close,
d365c6cfd   David Herrmann   HID: uhid: add UH...
362
  	.parse = uhid_hid_parse,
7c4003bc3   David Herrmann   HID: uhid: rename...
363
  	.raw_request = uhid_hid_raw_request,
596cfdd80   Frank Praznik   HID: Add the tran...
364
  	.output_report = uhid_hid_output_report,
d365c6cfd   David Herrmann   HID: uhid: add UH...
365
  };
fc2237a72   Jason Gerecke   HID: introduce hi...
366
  EXPORT_SYMBOL_GPL(uhid_hid_driver);
d365c6cfd   David Herrmann   HID: uhid: add UH...
367

befde0226   Dmitry Torokhov   HID: uhid: make c...
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
  #ifdef CONFIG_COMPAT
  
  /* Apparently we haven't stepped on these rakes enough times yet. */
  struct uhid_create_req_compat {
  	__u8 name[128];
  	__u8 phys[64];
  	__u8 uniq[64];
  
  	compat_uptr_t rd_data;
  	__u16 rd_size;
  
  	__u16 bus;
  	__u32 vendor;
  	__u32 product;
  	__u32 version;
  	__u32 country;
  } __attribute__((__packed__));
  
  static int uhid_event_from_user(const char __user *buffer, size_t len,
  				struct uhid_event *event)
  {
7365abbad   Andy Lutomirski   drivers/hid/uhid....
389
  	if (in_compat_syscall()) {
befde0226   Dmitry Torokhov   HID: uhid: make c...
390
391
392
393
394
395
396
397
398
399
400
401
  		u32 type;
  
  		if (get_user(type, buffer))
  			return -EFAULT;
  
  		if (type == UHID_CREATE) {
  			/*
  			 * This is our messed up request with compat pointer.
  			 * It is largish (more than 256 bytes) so we better
  			 * allocate it from the heap.
  			 */
  			struct uhid_create_req_compat *compat;
80897aa78   David Herrmann   HID: uhid: fix le...
402
  			compat = kzalloc(sizeof(*compat), GFP_KERNEL);
befde0226   Dmitry Torokhov   HID: uhid: make c...
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
  			if (!compat)
  				return -ENOMEM;
  
  			buffer += sizeof(type);
  			len -= sizeof(type);
  			if (copy_from_user(compat, buffer,
  					   min(len, sizeof(*compat)))) {
  				kfree(compat);
  				return -EFAULT;
  			}
  
  			/* Shuffle the data over to proper structure */
  			event->type = type;
  
  			memcpy(event->u.create.name, compat->name,
  				sizeof(compat->name));
  			memcpy(event->u.create.phys, compat->phys,
  				sizeof(compat->phys));
  			memcpy(event->u.create.uniq, compat->uniq,
  				sizeof(compat->uniq));
  
  			event->u.create.rd_data = compat_ptr(compat->rd_data);
  			event->u.create.rd_size = compat->rd_size;
  
  			event->u.create.bus = compat->bus;
  			event->u.create.vendor = compat->vendor;
  			event->u.create.product = compat->product;
  			event->u.create.version = compat->version;
  			event->u.create.country = compat->country;
  
  			kfree(compat);
  			return 0;
  		}
  		/* All others can be copied directly */
  	}
  
  	if (copy_from_user(event, buffer, min(len, sizeof(*event))))
  		return -EFAULT;
  
  	return 0;
  }
  #else
  static int uhid_event_from_user(const char __user *buffer, size_t len,
  				struct uhid_event *event)
  {
  	if (copy_from_user(event, buffer, min(len, sizeof(*event))))
  		return -EFAULT;
  
  	return 0;
  }
  #endif
4522643aa   Petri Gynther   HID: uhid: Add UH...
454
455
456
457
  static int uhid_dev_create2(struct uhid_device *uhid,
  			    const struct uhid_event *ev)
  {
  	struct hid_device *hid;
25be7fe2b   David Herrmann   HID: uhid: avoid ...
458
  	size_t rd_size, len;
41c4a4642   David Herrmann   HID: uhid: avoid ...
459
  	void *rd_data;
4522643aa   Petri Gynther   HID: uhid: Add UH...
460
461
462
463
  	int ret;
  
  	if (uhid->running)
  		return -EALREADY;
41c4a4642   David Herrmann   HID: uhid: avoid ...
464
465
  	rd_size = ev->u.create2.rd_size;
  	if (rd_size <= 0 || rd_size > HID_MAX_DESCRIPTOR_SIZE)
4522643aa   Petri Gynther   HID: uhid: Add UH...
466
  		return -EINVAL;
41c4a4642   David Herrmann   HID: uhid: avoid ...
467
468
  	rd_data = kmemdup(ev->u.create2.rd_data, rd_size, GFP_KERNEL);
  	if (!rd_data)
4522643aa   Petri Gynther   HID: uhid: Add UH...
469
  		return -ENOMEM;
41c4a4642   David Herrmann   HID: uhid: avoid ...
470
471
  	uhid->rd_size = rd_size;
  	uhid->rd_data = rd_data;
4522643aa   Petri Gynther   HID: uhid: Add UH...
472
473
474
475
476
  	hid = hid_allocate_device();
  	if (IS_ERR(hid)) {
  		ret = PTR_ERR(hid);
  		goto err_free;
  	}
4d26d1d1e   David Herrmann   Revert "HID: uhid...
477
478
479
480
481
482
483
  	/* @hid is zero-initialized, strncpy() is correct, strlcpy() not */
  	len = min(sizeof(hid->name), sizeof(ev->u.create2.name)) - 1;
  	strncpy(hid->name, ev->u.create2.name, len);
  	len = min(sizeof(hid->phys), sizeof(ev->u.create2.phys)) - 1;
  	strncpy(hid->phys, ev->u.create2.phys, len);
  	len = min(sizeof(hid->uniq), sizeof(ev->u.create2.uniq)) - 1;
  	strncpy(hid->uniq, ev->u.create2.uniq, len);
4522643aa   Petri Gynther   HID: uhid: Add UH...
484
485
486
487
488
489
490
491
492
493
494
495
  
  	hid->ll_driver = &uhid_hid_driver;
  	hid->bus = ev->u.create2.bus;
  	hid->vendor = ev->u.create2.vendor;
  	hid->product = ev->u.create2.product;
  	hid->version = ev->u.create2.version;
  	hid->country = ev->u.create2.country;
  	hid->driver_data = uhid;
  	hid->dev.parent = uhid_misc.this_device;
  
  	uhid->hid = hid;
  	uhid->running = true;
67f8ecc55   Roderick Colenbrander   HID: uhid: fix ti...
496
497
498
499
500
  	/* Adding of a HID device is done through a worker, to allow HID drivers
  	 * which use feature requests during .probe to work, without they would
  	 * be blocked on devlock, which is held by uhid_char_write.
  	 */
  	schedule_work(&uhid->worker);
4522643aa   Petri Gynther   HID: uhid: Add UH...
501
502
  
  	return 0;
4522643aa   Petri Gynther   HID: uhid: Add UH...
503
504
  err_free:
  	kfree(uhid->rd_data);
41c4a4642   David Herrmann   HID: uhid: avoid ...
505
506
  	uhid->rd_data = NULL;
  	uhid->rd_size = 0;
4522643aa   Petri Gynther   HID: uhid: Add UH...
507
508
  	return ret;
  }
56c477546   David Herrmann   HID: uhid: forwar...
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
  static int uhid_dev_create(struct uhid_device *uhid,
  			   struct uhid_event *ev)
  {
  	struct uhid_create_req orig;
  
  	orig = ev->u.create;
  
  	if (orig.rd_size <= 0 || orig.rd_size > HID_MAX_DESCRIPTOR_SIZE)
  		return -EINVAL;
  	if (copy_from_user(&ev->u.create2.rd_data, orig.rd_data, orig.rd_size))
  		return -EFAULT;
  
  	memcpy(ev->u.create2.name, orig.name, sizeof(orig.name));
  	memcpy(ev->u.create2.phys, orig.phys, sizeof(orig.phys));
  	memcpy(ev->u.create2.uniq, orig.uniq, sizeof(orig.uniq));
  	ev->u.create2.rd_size = orig.rd_size;
  	ev->u.create2.bus = orig.bus;
  	ev->u.create2.vendor = orig.vendor;
  	ev->u.create2.product = orig.product;
  	ev->u.create2.version = orig.version;
  	ev->u.create2.country = orig.country;
  
  	return uhid_dev_create2(uhid, ev);
  }
d365c6cfd   David Herrmann   HID: uhid: add UH...
533
534
535
536
537
538
  static int uhid_dev_destroy(struct uhid_device *uhid)
  {
  	if (!uhid->running)
  		return -EINVAL;
  
  	uhid->running = false;
fcfcf0deb   David Herrmann   HID: uhid: implem...
539
  	wake_up_interruptible(&uhid->report_wait);
d365c6cfd   David Herrmann   HID: uhid: add UH...
540

67f8ecc55   Roderick Colenbrander   HID: uhid: fix ti...
541
  	cancel_work_sync(&uhid->worker);
d365c6cfd   David Herrmann   HID: uhid: add UH...
542
543
544
545
546
  	hid_destroy_device(uhid->hid);
  	kfree(uhid->rd_data);
  
  	return 0;
  }
5e87a36ae   David Herrmann   HID: uhid: allow ...
547
548
549
550
551
552
553
554
555
556
  static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev)
  {
  	if (!uhid->running)
  		return -EINVAL;
  
  	hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data,
  			 min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0);
  
  	return 0;
  }
4522643aa   Petri Gynther   HID: uhid: Add UH...
557
558
559
560
561
562
563
564
565
566
  static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev)
  {
  	if (!uhid->running)
  		return -EINVAL;
  
  	hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input2.data,
  			 min_t(size_t, ev->u.input2.size, UHID_DATA_MAX), 0);
  
  	return 0;
  }
fa71f32b5   David Herrmann   HID: uhid: add AB...
567
568
  static int uhid_dev_get_report_reply(struct uhid_device *uhid,
  				     struct uhid_event *ev)
fcfcf0deb   David Herrmann   HID: uhid: implem...
569
  {
fcfcf0deb   David Herrmann   HID: uhid: implem...
570
571
  	if (!uhid->running)
  		return -EINVAL;
11c221553   David Herrmann   HID: uhid: implem...
572
573
574
  	uhid_report_wake_up(uhid, ev->u.get_report_reply.id, ev);
  	return 0;
  }
fcfcf0deb   David Herrmann   HID: uhid: implem...
575

11c221553   David Herrmann   HID: uhid: implem...
576
577
578
579
580
  static int uhid_dev_set_report_reply(struct uhid_device *uhid,
  				     struct uhid_event *ev)
  {
  	if (!uhid->running)
  		return -EINVAL;
fcfcf0deb   David Herrmann   HID: uhid: implem...
581

11c221553   David Herrmann   HID: uhid: implem...
582
  	uhid_report_wake_up(uhid, ev->u.set_report_reply.id, ev);
fcfcf0deb   David Herrmann   HID: uhid: implem...
583
584
  	return 0;
  }
1ccd7a2a3   David Herrmann   HID: uhid: introd...
585
586
  static int uhid_char_open(struct inode *inode, struct file *file)
  {
ace3d8614   David Herrmann   HID: uhid: add in...
587
588
589
590
591
  	struct uhid_device *uhid;
  
  	uhid = kzalloc(sizeof(*uhid), GFP_KERNEL);
  	if (!uhid)
  		return -ENOMEM;
d937ae5fa   David Herrmann   HID: uhid: implem...
592
  	mutex_init(&uhid->devlock);
fcfcf0deb   David Herrmann   HID: uhid: implem...
593
  	mutex_init(&uhid->report_lock);
ace3d8614   David Herrmann   HID: uhid: add in...
594
595
  	spin_lock_init(&uhid->qlock);
  	init_waitqueue_head(&uhid->waitq);
fcfcf0deb   David Herrmann   HID: uhid: implem...
596
  	init_waitqueue_head(&uhid->report_wait);
d365c6cfd   David Herrmann   HID: uhid: add UH...
597
  	uhid->running = false;
67f8ecc55   Roderick Colenbrander   HID: uhid: fix ti...
598
  	INIT_WORK(&uhid->worker, uhid_device_add_worker);
ace3d8614   David Herrmann   HID: uhid: add in...
599
600
  
  	file->private_data = uhid;
c5bf68fe0   Kirill Smelkov   *: convert stream...
601
  	stream_open(inode, file);
ace3d8614   David Herrmann   HID: uhid: add in...
602

1ccd7a2a3   David Herrmann   HID: uhid: introd...
603
604
605
606
607
  	return 0;
  }
  
  static int uhid_char_release(struct inode *inode, struct file *file)
  {
ace3d8614   David Herrmann   HID: uhid: add in...
608
609
  	struct uhid_device *uhid = file->private_data;
  	unsigned int i;
d365c6cfd   David Herrmann   HID: uhid: add UH...
610
  	uhid_dev_destroy(uhid);
ace3d8614   David Herrmann   HID: uhid: add in...
611
612
613
614
  	for (i = 0; i < UHID_BUFSIZE; ++i)
  		kfree(uhid->outq[i]);
  
  	kfree(uhid);
1ccd7a2a3   David Herrmann   HID: uhid: introd...
615
616
617
618
619
620
  	return 0;
  }
  
  static ssize_t uhid_char_read(struct file *file, char __user *buffer,
  				size_t count, loff_t *ppos)
  {
d937ae5fa   David Herrmann   HID: uhid: implem...
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
  	struct uhid_device *uhid = file->private_data;
  	int ret;
  	unsigned long flags;
  	size_t len;
  
  	/* they need at least the "type" member of uhid_event */
  	if (count < sizeof(__u32))
  		return -EINVAL;
  
  try_again:
  	if (file->f_flags & O_NONBLOCK) {
  		if (uhid->head == uhid->tail)
  			return -EAGAIN;
  	} else {
  		ret = wait_event_interruptible(uhid->waitq,
  						uhid->head != uhid->tail);
  		if (ret)
  			return ret;
  	}
  
  	ret = mutex_lock_interruptible(&uhid->devlock);
  	if (ret)
  		return ret;
  
  	if (uhid->head == uhid->tail) {
  		mutex_unlock(&uhid->devlock);
  		goto try_again;
  	} else {
  		len = min(count, sizeof(**uhid->outq));
adefb69b1   Vinicius Costa Gomes   HID: uhid: Fix se...
650
  		if (copy_to_user(buffer, uhid->outq[uhid->tail], len)) {
d937ae5fa   David Herrmann   HID: uhid: implem...
651
652
653
654
655
656
657
658
659
660
661
662
663
  			ret = -EFAULT;
  		} else {
  			kfree(uhid->outq[uhid->tail]);
  			uhid->outq[uhid->tail] = NULL;
  
  			spin_lock_irqsave(&uhid->qlock, flags);
  			uhid->tail = (uhid->tail + 1) % UHID_BUFSIZE;
  			spin_unlock_irqrestore(&uhid->qlock, flags);
  		}
  	}
  
  	mutex_unlock(&uhid->devlock);
  	return ret ? ret : len;
1ccd7a2a3   David Herrmann   HID: uhid: introd...
664
665
666
667
668
  }
  
  static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
  				size_t count, loff_t *ppos)
  {
6664ef72a   David Herrmann   HID: uhid: implem...
669
670
671
672
673
674
675
676
677
678
679
680
681
682
  	struct uhid_device *uhid = file->private_data;
  	int ret;
  	size_t len;
  
  	/* we need at least the "type" member of uhid_event */
  	if (count < sizeof(__u32))
  		return -EINVAL;
  
  	ret = mutex_lock_interruptible(&uhid->devlock);
  	if (ret)
  		return ret;
  
  	memset(&uhid->input_buf, 0, sizeof(uhid->input_buf));
  	len = min(count, sizeof(uhid->input_buf));
befde0226   Dmitry Torokhov   HID: uhid: make c...
683
684
685
  
  	ret = uhid_event_from_user(buffer, len, &uhid->input_buf);
  	if (ret)
6664ef72a   David Herrmann   HID: uhid: implem...
686
  		goto unlock;
6664ef72a   David Herrmann   HID: uhid: implem...
687
688
  
  	switch (uhid->input_buf.type) {
d365c6cfd   David Herrmann   HID: uhid: add UH...
689
  	case UHID_CREATE:
8c01db761   Eric Biggers   HID: uhid: forbid...
690
691
692
693
694
695
696
697
698
699
700
701
  		/*
  		 * 'struct uhid_create_req' contains a __user pointer which is
  		 * copied from, so it's unsafe to allow this with elevated
  		 * privileges (e.g. from a setuid binary) or via kernel_write().
  		 */
  		if (file->f_cred != current_cred() || uaccess_kernel()) {
  			pr_err_once("UHID_CREATE from different security context by process %d (%s), this is not allowed.
  ",
  				    task_tgid_vnr(current), current->comm);
  			ret = -EACCES;
  			goto unlock;
  		}
d365c6cfd   David Herrmann   HID: uhid: add UH...
702
703
  		ret = uhid_dev_create(uhid, &uhid->input_buf);
  		break;
4522643aa   Petri Gynther   HID: uhid: Add UH...
704
705
706
  	case UHID_CREATE2:
  		ret = uhid_dev_create2(uhid, &uhid->input_buf);
  		break;
d365c6cfd   David Herrmann   HID: uhid: add UH...
707
708
709
  	case UHID_DESTROY:
  		ret = uhid_dev_destroy(uhid);
  		break;
5e87a36ae   David Herrmann   HID: uhid: allow ...
710
711
712
  	case UHID_INPUT:
  		ret = uhid_dev_input(uhid, &uhid->input_buf);
  		break;
4522643aa   Petri Gynther   HID: uhid: Add UH...
713
714
715
  	case UHID_INPUT2:
  		ret = uhid_dev_input2(uhid, &uhid->input_buf);
  		break;
fa71f32b5   David Herrmann   HID: uhid: add AB...
716
717
  	case UHID_GET_REPORT_REPLY:
  		ret = uhid_dev_get_report_reply(uhid, &uhid->input_buf);
fcfcf0deb   David Herrmann   HID: uhid: implem...
718
  		break;
11c221553   David Herrmann   HID: uhid: implem...
719
720
721
  	case UHID_SET_REPORT_REPLY:
  		ret = uhid_dev_set_report_reply(uhid, &uhid->input_buf);
  		break;
6664ef72a   David Herrmann   HID: uhid: implem...
722
723
724
725
726
727
728
729
730
  	default:
  		ret = -EOPNOTSUPP;
  	}
  
  unlock:
  	mutex_unlock(&uhid->devlock);
  
  	/* return "count" not "len" to not confuse the caller */
  	return ret ? ret : count;
1ccd7a2a3   David Herrmann   HID: uhid: introd...
731
  }
afc9a42b7   Al Viro   the rest of drive...
732
  static __poll_t uhid_char_poll(struct file *file, poll_table *wait)
1ccd7a2a3   David Herrmann   HID: uhid: introd...
733
  {
1f9dec1e0   David Herrmann   HID: uhid: allow ...
734
  	struct uhid_device *uhid = file->private_data;
9e635c285   Jiri Kosina   HID: hidraw, uhid...
735
  	__poll_t mask = EPOLLOUT | EPOLLWRNORM; /* uhid is always writable */
1f9dec1e0   David Herrmann   HID: uhid: allow ...
736
737
738
739
  
  	poll_wait(file, &uhid->waitq, wait);
  
  	if (uhid->head != uhid->tail)
9e635c285   Jiri Kosina   HID: hidraw, uhid...
740
  		mask |= EPOLLIN | EPOLLRDNORM;
1f9dec1e0   David Herrmann   HID: uhid: allow ...
741

9e635c285   Jiri Kosina   HID: hidraw, uhid...
742
  	return mask;
1ccd7a2a3   David Herrmann   HID: uhid: introd...
743
744
745
746
747
748
749
750
751
752
753
754
755
756
  }
  
  static const struct file_operations uhid_fops = {
  	.owner		= THIS_MODULE,
  	.open		= uhid_char_open,
  	.release	= uhid_char_release,
  	.read		= uhid_char_read,
  	.write		= uhid_char_write,
  	.poll		= uhid_char_poll,
  	.llseek		= no_llseek,
  };
  
  static struct miscdevice uhid_misc = {
  	.fops		= &uhid_fops,
19872d20c   David Herrmann   HID: uhid: alloca...
757
  	.minor		= UHID_MINOR,
1ccd7a2a3   David Herrmann   HID: uhid: introd...
758
759
  	.name		= UHID_NAME,
  };
ca75d601b   PrasannaKumar Muralidharan   miscdevice: Add h...
760
  module_misc_device(uhid_misc);
1ccd7a2a3   David Herrmann   HID: uhid: introd...
761

1ccd7a2a3   David Herrmann   HID: uhid: introd...
762
763
764
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
  MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem");
19872d20c   David Herrmann   HID: uhid: alloca...
765
  MODULE_ALIAS_MISCDEV(UHID_MINOR);
60cbd53e4   Marcel Holtmann   HID: uhid: add de...
766
  MODULE_ALIAS("devname:" UHID_NAME);