aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/hid/uhid.c120
-rw-r--r--include/linux/uhid.h17
2 files changed, 136 insertions, 1 deletions
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index 421c492dc824..ea560bfa033d 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -42,6 +42,12 @@ struct uhid_device {
42 __u8 head; 42 __u8 head;
43 __u8 tail; 43 __u8 tail;
44 struct uhid_event *outq[UHID_BUFSIZE]; 44 struct uhid_event *outq[UHID_BUFSIZE];
45
46 struct mutex report_lock;
47 wait_queue_head_t report_wait;
48 atomic_t report_done;
49 atomic_t report_id;
50 struct uhid_event report_buf;
45}; 51};
46 52
47static struct miscdevice uhid_misc; 53static struct miscdevice uhid_misc;
@@ -143,7 +149,84 @@ static int uhid_hid_parse(struct hid_device *hid)
143static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, 149static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum,
144 __u8 *buf, size_t count, unsigned char rtype) 150 __u8 *buf, size_t count, unsigned char rtype)
145{ 151{
146 return 0; 152 struct uhid_device *uhid = hid->driver_data;
153 __u8 report_type;
154 struct uhid_event *ev;
155 unsigned long flags;
156 int ret;
157 size_t len;
158 struct uhid_feature_answer_req *req;
159
160 if (!uhid->running)
161 return -EIO;
162
163 switch (rtype) {
164 case HID_FEATURE_REPORT:
165 report_type = UHID_FEATURE_REPORT;
166 break;
167 case HID_OUTPUT_REPORT:
168 report_type = UHID_OUTPUT_REPORT;
169 break;
170 case HID_INPUT_REPORT:
171 report_type = UHID_INPUT_REPORT;
172 break;
173 default:
174 return -EINVAL;
175 }
176
177 ret = mutex_lock_interruptible(&uhid->report_lock);
178 if (ret)
179 return ret;
180
181 ev = kzalloc(sizeof(*ev), GFP_KERNEL);
182 if (!ev) {
183 ret = -ENOMEM;
184 goto unlock;
185 }
186
187 spin_lock_irqsave(&uhid->qlock, flags);
188 ev->type = UHID_FEATURE;
189 ev->u.feature.id = atomic_inc_return(&uhid->report_id);
190 ev->u.feature.rnum = rnum;
191 ev->u.feature.rtype = report_type;
192
193 atomic_set(&uhid->report_done, 0);
194 uhid_queue(uhid, ev);
195 spin_unlock_irqrestore(&uhid->qlock, flags);
196
197 ret = wait_event_interruptible_timeout(uhid->report_wait,
198 atomic_read(&uhid->report_done), 5 * HZ);
199
200 /*
201 * Make sure "uhid->running" is cleared on shutdown before
202 * "uhid->report_done" is set.
203 */
204 smp_rmb();
205 if (!ret || !uhid->running) {
206 ret = -EIO;
207 } else if (ret < 0) {
208 ret = -ERESTARTSYS;
209 } else {
210 spin_lock_irqsave(&uhid->qlock, flags);
211 req = &uhid->report_buf.u.feature_answer;
212
213 if (req->err) {
214 ret = -EIO;
215 } else {
216 ret = 0;
217 len = min(count,
218 min_t(size_t, req->size, UHID_DATA_MAX));
219 memcpy(buf, req->data, len);
220 }
221
222 spin_unlock_irqrestore(&uhid->qlock, flags);
223 }
224
225 atomic_set(&uhid->report_done, 1);
226
227unlock:
228 mutex_unlock(&uhid->report_lock);
229 return ret ? ret : len;
147} 230}
148 231
149static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, 232static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count,
@@ -265,7 +348,11 @@ static int uhid_dev_destroy(struct uhid_device *uhid)
265 if (!uhid->running) 348 if (!uhid->running)
266 return -EINVAL; 349 return -EINVAL;
267 350
351 /* clear "running" before setting "report_done" */
268 uhid->running = false; 352 uhid->running = false;
353 smp_wmb();
354 atomic_set(&uhid->report_done, 1);
355 wake_up_interruptible(&uhid->report_wait);
269 356
270 hid_destroy_device(uhid->hid); 357 hid_destroy_device(uhid->hid);
271 kfree(uhid->rd_data); 358 kfree(uhid->rd_data);
@@ -284,6 +371,31 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev)
284 return 0; 371 return 0;
285} 372}
286 373
374static int uhid_dev_feature_answer(struct uhid_device *uhid,
375 struct uhid_event *ev)
376{
377 unsigned long flags;
378
379 if (!uhid->running)
380 return -EINVAL;
381
382 spin_lock_irqsave(&uhid->qlock, flags);
383
384 /* id for old report; drop it silently */
385 if (atomic_read(&uhid->report_id) != ev->u.feature_answer.id)
386 goto unlock;
387 if (atomic_read(&uhid->report_done))
388 goto unlock;
389
390 memcpy(&uhid->report_buf, ev, sizeof(*ev));
391 atomic_set(&uhid->report_done, 1);
392 wake_up_interruptible(&uhid->report_wait);
393
394unlock:
395 spin_unlock_irqrestore(&uhid->qlock, flags);
396 return 0;
397}
398
287static int uhid_char_open(struct inode *inode, struct file *file) 399static int uhid_char_open(struct inode *inode, struct file *file)
288{ 400{
289 struct uhid_device *uhid; 401 struct uhid_device *uhid;
@@ -293,9 +405,12 @@ static int uhid_char_open(struct inode *inode, struct file *file)
293 return -ENOMEM; 405 return -ENOMEM;
294 406
295 mutex_init(&uhid->devlock); 407 mutex_init(&uhid->devlock);
408 mutex_init(&uhid->report_lock);
296 spin_lock_init(&uhid->qlock); 409 spin_lock_init(&uhid->qlock);
297 init_waitqueue_head(&uhid->waitq); 410 init_waitqueue_head(&uhid->waitq);
411 init_waitqueue_head(&uhid->report_wait);
298 uhid->running = false; 412 uhid->running = false;
413 atomic_set(&uhid->report_done, 1);
299 414
300 file->private_data = uhid; 415 file->private_data = uhid;
301 nonseekable_open(inode, file); 416 nonseekable_open(inode, file);
@@ -398,6 +513,9 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
398 case UHID_INPUT: 513 case UHID_INPUT:
399 ret = uhid_dev_input(uhid, &uhid->input_buf); 514 ret = uhid_dev_input(uhid, &uhid->input_buf);
400 break; 515 break;
516 case UHID_FEATURE_ANSWER:
517 ret = uhid_dev_feature_answer(uhid, &uhid->input_buf);
518 break;
401 default: 519 default:
402 ret = -EOPNOTSUPP; 520 ret = -EOPNOTSUPP;
403 } 521 }
diff --git a/include/linux/uhid.h b/include/linux/uhid.h
index 2c972550a624..9c6974f16966 100644
--- a/include/linux/uhid.h
+++ b/include/linux/uhid.h
@@ -32,6 +32,8 @@ enum uhid_event_type {
32 UHID_OUTPUT, 32 UHID_OUTPUT,
33 UHID_OUTPUT_EV, 33 UHID_OUTPUT_EV,
34 UHID_INPUT, 34 UHID_INPUT,
35 UHID_FEATURE,
36 UHID_FEATURE_ANSWER,
35}; 37};
36 38
37struct uhid_create_req { 39struct uhid_create_req {
@@ -73,6 +75,19 @@ struct uhid_output_ev_req {
73 __s32 value; 75 __s32 value;
74} __attribute__((__packed__)); 76} __attribute__((__packed__));
75 77
78struct uhid_feature_req {
79 __u32 id;
80 __u8 rnum;
81 __u8 rtype;
82} __attribute__((__packed__));
83
84struct uhid_feature_answer_req {
85 __u32 id;
86 __u16 err;
87 __u16 size;
88 __u8 data[UHID_DATA_MAX];
89};
90
76struct uhid_event { 91struct uhid_event {
77 __u32 type; 92 __u32 type;
78 93
@@ -81,6 +96,8 @@ struct uhid_event {
81 struct uhid_input_req input; 96 struct uhid_input_req input;
82 struct uhid_output_req output; 97 struct uhid_output_req output;
83 struct uhid_output_ev_req output_ev; 98 struct uhid_output_ev_req output_ev;
99 struct uhid_feature_req feature;
100 struct uhid_feature_answer_req feature_answer;
84 } u; 101 } u;
85} __attribute__((__packed__)); 102} __attribute__((__packed__));
86 103