Blame view

drivers/hid/uhid.c 17.2 KB
1ccd7a2a3   David Herrmann   HID: uhid: introd...
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * User-space I/O driver support for HID subsystem
   * Copyright (c) 2012 David Herrmann
   */
  
  /*
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License as published by the Free
   * Software Foundation; either version 2 of the License, or (at your option)
   * any later version.
   */
  
  #include <linux/atomic.h>
befde0226   Dmitry Torokhov   HID: uhid: make c...
14
  #include <linux/compat.h>
1ccd7a2a3   David Herrmann   HID: uhid: introd...
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  #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...
29
30
31
  #define UHID_BUFSIZE	32
  
  struct uhid_device {
d937ae5fa   David Herrmann   HID: uhid: implem...
32
  	struct mutex devlock;
d365c6cfd   David Herrmann   HID: uhid: add UH...
33
34
35
36
  	bool running;
  
  	__u8 *rd_data;
  	uint rd_size;
ace3d8614   David Herrmann   HID: uhid: add in...
37
  	struct hid_device *hid;
6664ef72a   David Herrmann   HID: uhid: implem...
38
  	struct uhid_event input_buf;
ace3d8614   David Herrmann   HID: uhid: add in...
39
40
41
42
43
44
  
  	wait_queue_head_t waitq;
  	spinlock_t qlock;
  	__u8 head;
  	__u8 tail;
  	struct uhid_event *outq[UHID_BUFSIZE];
fcfcf0deb   David Herrmann   HID: uhid: implem...
45

8cad5b017   David Herrmann   HID: uhid: turn r...
46
  	/* blocking GET_REPORT support; state changes protected by qlock */
fcfcf0deb   David Herrmann   HID: uhid: implem...
47
48
  	struct mutex report_lock;
  	wait_queue_head_t report_wait;
5942b849b   David Herrmann   HID: uhid: invert...
49
  	bool report_running;
8cad5b017   David Herrmann   HID: uhid: turn r...
50
  	u32 report_id;
11c221553   David Herrmann   HID: uhid: implem...
51
  	u32 report_type;
fcfcf0deb   David Herrmann   HID: uhid: implem...
52
  	struct uhid_event report_buf;
ace3d8614   David Herrmann   HID: uhid: add in...
53
  };
1ccd7a2a3   David Herrmann   HID: uhid: introd...
54
55
  
  static struct miscdevice uhid_misc;
ace3d8614   David Herrmann   HID: uhid: add in...
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
  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...
90
91
  static int uhid_hid_start(struct hid_device *hid)
  {
ec4b7dea4   David Herrmann   HID: uhid: add UH...
92
  	struct uhid_device *uhid = hid->driver_data;
c2b2f16c5   David Herrmann   HID: uhid: report...
93
94
95
96
97
98
99
100
  	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...
101

c2b2f16c5   David Herrmann   HID: uhid: report...
102
103
104
105
106
107
108
109
110
111
112
113
  	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...
114
115
116
117
  }
  
  static void uhid_hid_stop(struct hid_device *hid)
  {
ec4b7dea4   David Herrmann   HID: uhid: add UH...
118
119
120
121
  	struct uhid_device *uhid = hid->driver_data;
  
  	hid->claimed = 0;
  	uhid_queue_event(uhid, UHID_STOP);
d365c6cfd   David Herrmann   HID: uhid: add UH...
122
123
124
125
  }
  
  static int uhid_hid_open(struct hid_device *hid)
  {
e7191474a   David Herrmann   HID: uhid: forwar...
126
127
128
  	struct uhid_device *uhid = hid->driver_data;
  
  	return uhid_queue_event(uhid, UHID_OPEN);
d365c6cfd   David Herrmann   HID: uhid: add UH...
129
130
131
132
  }
  
  static void uhid_hid_close(struct hid_device *hid)
  {
e7191474a   David Herrmann   HID: uhid: forwar...
133
134
135
  	struct uhid_device *uhid = hid->driver_data;
  
  	uhid_queue_event(uhid, UHID_CLOSE);
d365c6cfd   David Herrmann   HID: uhid: add UH...
136
  }
d365c6cfd   David Herrmann   HID: uhid: add UH...
137
138
  static int uhid_hid_parse(struct hid_device *hid)
  {
037c061bc   David Herrmann   HID: uhid: forwar...
139
140
141
  	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...
142
  }
11c221553   David Herrmann   HID: uhid: implem...
143
144
145
146
147
148
149
150
151
152
  /* 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...
153
  	uhid->report_type = ev->type + 1;
11c221553   David Herrmann   HID: uhid: implem...
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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
  	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...
193
  static int uhid_hid_get_report(struct hid_device *hid, unsigned char rnum,
11c221553   David Herrmann   HID: uhid: implem...
194
  			       u8 *buf, size_t count, u8 rtype)
289a71621   Jiri Kosina   HID: uhid: reintr...
195
196
  {
  	struct uhid_device *uhid = hid->driver_data;
11c221553   David Herrmann   HID: uhid: implem...
197
  	struct uhid_get_report_reply_req *req;
289a71621   Jiri Kosina   HID: uhid: reintr...
198
  	struct uhid_event *ev;
289a71621   Jiri Kosina   HID: uhid: reintr...
199
  	int ret;
289a71621   Jiri Kosina   HID: uhid: reintr...
200
201
202
  
  	if (!uhid->running)
  		return -EIO;
11c221553   David Herrmann   HID: uhid: implem...
203
204
205
206
207
208
209
  	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...
210
211
  
  	ret = mutex_lock_interruptible(&uhid->report_lock);
11c221553   David Herrmann   HID: uhid: implem...
212
213
  	if (ret) {
  		kfree(ev);
289a71621   Jiri Kosina   HID: uhid: reintr...
214
  		return ret;
11c221553   David Herrmann   HID: uhid: implem...
215
  	}
289a71621   Jiri Kosina   HID: uhid: reintr...
216

11c221553   David Herrmann   HID: uhid: implem...
217
218
219
  	/* 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...
220
  		goto unlock;
11c221553   David Herrmann   HID: uhid: implem...
221
222
223
224
225
226
227
  
  	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...
228
  	}
11c221553   David Herrmann   HID: uhid: implem...
229
230
231
232
  unlock:
  	mutex_unlock(&uhid->report_lock);
  	return ret;
  }
289a71621   Jiri Kosina   HID: uhid: reintr...
233

11c221553   David Herrmann   HID: uhid: implem...
234
235
236
237
238
239
  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...
240

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

11c221553   David Herrmann   HID: uhid: implem...
244
245
246
  	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
  	if (!ev)
  		return -ENOMEM;
289a71621   Jiri Kosina   HID: uhid: reintr...
247

11c221553   David Herrmann   HID: uhid: implem...
248
249
250
251
252
  	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...
253

11c221553   David Herrmann   HID: uhid: implem...
254
255
256
257
  	ret = mutex_lock_interruptible(&uhid->report_lock);
  	if (ret) {
  		kfree(ev);
  		return ret;
289a71621   Jiri Kosina   HID: uhid: reintr...
258
  	}
11c221553   David Herrmann   HID: uhid: implem...
259
260
261
262
263
264
265
266
267
  	/* 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...
268
269
270
  
  unlock:
  	mutex_unlock(&uhid->report_lock);
11c221553   David Herrmann   HID: uhid: implem...
271
  	return ret;
289a71621   Jiri Kosina   HID: uhid: reintr...
272
  }
7c4003bc3   David Herrmann   HID: uhid: rename...
273
274
275
276
  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...
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
  	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...
292
293
  	switch (reqtype) {
  	case HID_REQ_GET_REPORT:
11c221553   David Herrmann   HID: uhid: implem...
294
  		return uhid_hid_get_report(hid, reportnum, buf, len, u_rtype);
7c4003bc3   David Herrmann   HID: uhid: rename...
295
  	case HID_REQ_SET_REPORT:
11c221553   David Herrmann   HID: uhid: implem...
296
  		return uhid_hid_set_report(hid, reportnum, buf, len, u_rtype);
7c4003bc3   David Herrmann   HID: uhid: rename...
297
298
299
300
  	default:
  		return -EIO;
  	}
  }
d365c6cfd   David Herrmann   HID: uhid: add UH...
301
302
303
  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...
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
  	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...
337
  }
596cfdd80   Frank Praznik   HID: Add the tran...
338
339
340
  static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf,
  				  size_t count)
  {
41abfb360   Benjamin Tissoires   HID: uHID: remove...
341
  	return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT);
596cfdd80   Frank Praznik   HID: Add the tran...
342
  }
d365c6cfd   David Herrmann   HID: uhid: add UH...
343
344
345
346
347
  static struct hid_ll_driver uhid_hid_driver = {
  	.start = uhid_hid_start,
  	.stop = uhid_hid_stop,
  	.open = uhid_hid_open,
  	.close = uhid_hid_close,
d365c6cfd   David Herrmann   HID: uhid: add UH...
348
  	.parse = uhid_hid_parse,
7c4003bc3   David Herrmann   HID: uhid: rename...
349
  	.raw_request = uhid_hid_raw_request,
596cfdd80   Frank Praznik   HID: Add the tran...
350
  	.output_report = uhid_hid_output_report,
d365c6cfd   David Herrmann   HID: uhid: add UH...
351
  };
befde0226   Dmitry Torokhov   HID: uhid: make c...
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
  #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)
  {
  	if (is_compat_task()) {
  		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...
386
  			compat = kzalloc(sizeof(*compat), GFP_KERNEL);
befde0226   Dmitry Torokhov   HID: uhid: make c...
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
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
  			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...
438
439
440
441
  static int uhid_dev_create2(struct uhid_device *uhid,
  			    const struct uhid_event *ev)
  {
  	struct hid_device *hid;
25be7fe2b   David Herrmann   HID: uhid: avoid ...
442
  	size_t rd_size, len;
41c4a4642   David Herrmann   HID: uhid: avoid ...
443
  	void *rd_data;
4522643aa   Petri Gynther   HID: uhid: Add UH...
444
445
446
447
  	int ret;
  
  	if (uhid->running)
  		return -EALREADY;
41c4a4642   David Herrmann   HID: uhid: avoid ...
448
449
  	rd_size = ev->u.create2.rd_size;
  	if (rd_size <= 0 || rd_size > HID_MAX_DESCRIPTOR_SIZE)
4522643aa   Petri Gynther   HID: uhid: Add UH...
450
  		return -EINVAL;
41c4a4642   David Herrmann   HID: uhid: avoid ...
451
452
  	rd_data = kmemdup(ev->u.create2.rd_data, rd_size, GFP_KERNEL);
  	if (!rd_data)
4522643aa   Petri Gynther   HID: uhid: Add UH...
453
  		return -ENOMEM;
41c4a4642   David Herrmann   HID: uhid: avoid ...
454
455
  	uhid->rd_size = rd_size;
  	uhid->rd_data = rd_data;
4522643aa   Petri Gynther   HID: uhid: Add UH...
456
457
458
459
460
  	hid = hid_allocate_device();
  	if (IS_ERR(hid)) {
  		ret = PTR_ERR(hid);
  		goto err_free;
  	}
25be7fe2b   David Herrmann   HID: uhid: avoid ...
461
462
463
464
465
466
  	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...
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
  
  	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;
  
  	ret = hid_add_device(hid);
  	if (ret) {
  		hid_err(hid, "Cannot register HID device
  ");
  		goto err_hid;
  	}
  
  	return 0;
  
  err_hid:
  	hid_destroy_device(hid);
  	uhid->hid = NULL;
  	uhid->running = false;
  err_free:
  	kfree(uhid->rd_data);
41c4a4642   David Herrmann   HID: uhid: avoid ...
495
496
  	uhid->rd_data = NULL;
  	uhid->rd_size = 0;
4522643aa   Petri Gynther   HID: uhid: Add UH...
497
498
  	return ret;
  }
56c477546   David Herrmann   HID: uhid: forwar...
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
  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...
523
524
525
526
527
528
  static int uhid_dev_destroy(struct uhid_device *uhid)
  {
  	if (!uhid->running)
  		return -EINVAL;
  
  	uhid->running = false;
fcfcf0deb   David Herrmann   HID: uhid: implem...
529
  	wake_up_interruptible(&uhid->report_wait);
d365c6cfd   David Herrmann   HID: uhid: add UH...
530
531
532
533
534
535
  
  	hid_destroy_device(uhid->hid);
  	kfree(uhid->rd_data);
  
  	return 0;
  }
5e87a36ae   David Herrmann   HID: uhid: allow ...
536
537
538
539
540
541
542
543
544
545
  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...
546
547
548
549
550
551
552
553
554
555
  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...
556
557
  static int uhid_dev_get_report_reply(struct uhid_device *uhid,
  				     struct uhid_event *ev)
fcfcf0deb   David Herrmann   HID: uhid: implem...
558
  {
fcfcf0deb   David Herrmann   HID: uhid: implem...
559
560
  	if (!uhid->running)
  		return -EINVAL;
11c221553   David Herrmann   HID: uhid: implem...
561
562
563
  	uhid_report_wake_up(uhid, ev->u.get_report_reply.id, ev);
  	return 0;
  }
fcfcf0deb   David Herrmann   HID: uhid: implem...
564

11c221553   David Herrmann   HID: uhid: implem...
565
566
567
568
569
  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...
570

11c221553   David Herrmann   HID: uhid: implem...
571
  	uhid_report_wake_up(uhid, ev->u.set_report_reply.id, ev);
fcfcf0deb   David Herrmann   HID: uhid: implem...
572
573
  	return 0;
  }
1ccd7a2a3   David Herrmann   HID: uhid: introd...
574
575
  static int uhid_char_open(struct inode *inode, struct file *file)
  {
ace3d8614   David Herrmann   HID: uhid: add in...
576
577
578
579
580
  	struct uhid_device *uhid;
  
  	uhid = kzalloc(sizeof(*uhid), GFP_KERNEL);
  	if (!uhid)
  		return -ENOMEM;
d937ae5fa   David Herrmann   HID: uhid: implem...
581
  	mutex_init(&uhid->devlock);
fcfcf0deb   David Herrmann   HID: uhid: implem...
582
  	mutex_init(&uhid->report_lock);
ace3d8614   David Herrmann   HID: uhid: add in...
583
584
  	spin_lock_init(&uhid->qlock);
  	init_waitqueue_head(&uhid->waitq);
fcfcf0deb   David Herrmann   HID: uhid: implem...
585
  	init_waitqueue_head(&uhid->report_wait);
d365c6cfd   David Herrmann   HID: uhid: add UH...
586
  	uhid->running = false;
ace3d8614   David Herrmann   HID: uhid: add in...
587
588
589
  
  	file->private_data = uhid;
  	nonseekable_open(inode, file);
1ccd7a2a3   David Herrmann   HID: uhid: introd...
590
591
592
593
594
  	return 0;
  }
  
  static int uhid_char_release(struct inode *inode, struct file *file)
  {
ace3d8614   David Herrmann   HID: uhid: add in...
595
596
  	struct uhid_device *uhid = file->private_data;
  	unsigned int i;
d365c6cfd   David Herrmann   HID: uhid: add UH...
597
  	uhid_dev_destroy(uhid);
ace3d8614   David Herrmann   HID: uhid: add in...
598
599
600
601
  	for (i = 0; i < UHID_BUFSIZE; ++i)
  		kfree(uhid->outq[i]);
  
  	kfree(uhid);
1ccd7a2a3   David Herrmann   HID: uhid: introd...
602
603
604
605
606
607
  	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...
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
  	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...
637
  		if (copy_to_user(buffer, uhid->outq[uhid->tail], len)) {
d937ae5fa   David Herrmann   HID: uhid: implem...
638
639
640
641
642
643
644
645
646
647
648
649
650
  			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...
651
652
653
654
655
  }
  
  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...
656
657
658
659
660
661
662
663
664
665
666
667
668
669
  	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...
670
671
672
  
  	ret = uhid_event_from_user(buffer, len, &uhid->input_buf);
  	if (ret)
6664ef72a   David Herrmann   HID: uhid: implem...
673
  		goto unlock;
6664ef72a   David Herrmann   HID: uhid: implem...
674
675
  
  	switch (uhid->input_buf.type) {
d365c6cfd   David Herrmann   HID: uhid: add UH...
676
677
678
  	case UHID_CREATE:
  		ret = uhid_dev_create(uhid, &uhid->input_buf);
  		break;
4522643aa   Petri Gynther   HID: uhid: Add UH...
679
680
681
  	case UHID_CREATE2:
  		ret = uhid_dev_create2(uhid, &uhid->input_buf);
  		break;
d365c6cfd   David Herrmann   HID: uhid: add UH...
682
683
684
  	case UHID_DESTROY:
  		ret = uhid_dev_destroy(uhid);
  		break;
5e87a36ae   David Herrmann   HID: uhid: allow ...
685
686
687
  	case UHID_INPUT:
  		ret = uhid_dev_input(uhid, &uhid->input_buf);
  		break;
4522643aa   Petri Gynther   HID: uhid: Add UH...
688
689
690
  	case UHID_INPUT2:
  		ret = uhid_dev_input2(uhid, &uhid->input_buf);
  		break;
fa71f32b5   David Herrmann   HID: uhid: add AB...
691
692
  	case UHID_GET_REPORT_REPLY:
  		ret = uhid_dev_get_report_reply(uhid, &uhid->input_buf);
fcfcf0deb   David Herrmann   HID: uhid: implem...
693
  		break;
11c221553   David Herrmann   HID: uhid: implem...
694
695
696
  	case UHID_SET_REPORT_REPLY:
  		ret = uhid_dev_set_report_reply(uhid, &uhid->input_buf);
  		break;
6664ef72a   David Herrmann   HID: uhid: implem...
697
698
699
700
701
702
703
704
705
  	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...
706
707
708
709
  }
  
  static unsigned int uhid_char_poll(struct file *file, poll_table *wait)
  {
1f9dec1e0   David Herrmann   HID: uhid: allow ...
710
711
712
713
714
715
  	struct uhid_device *uhid = file->private_data;
  
  	poll_wait(file, &uhid->waitq, wait);
  
  	if (uhid->head != uhid->tail)
  		return POLLIN | POLLRDNORM;
1ccd7a2a3   David Herrmann   HID: uhid: introd...
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
  	return 0;
  }
  
  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...
731
  	.minor		= UHID_MINOR,
1ccd7a2a3   David Herrmann   HID: uhid: introd...
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
  	.name		= UHID_NAME,
  };
  
  static int __init uhid_init(void)
  {
  	return misc_register(&uhid_misc);
  }
  
  static void __exit uhid_exit(void)
  {
  	misc_deregister(&uhid_misc);
  }
  
  module_init(uhid_init);
  module_exit(uhid_exit);
  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...
750
  MODULE_ALIAS_MISCDEV(UHID_MINOR);
60cbd53e4   Marcel Holtmann   HID: uhid: add de...
751
  MODULE_ALIAS("devname:" UHID_NAME);