diff options
| -rw-r--r-- | drivers/hid/uhid.c | 120 | ||||
| -rw-r--r-- | include/linux/uhid.h | 17 |
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 | ||
| 47 | static struct miscdevice uhid_misc; | 53 | static struct miscdevice uhid_misc; |
| @@ -143,7 +149,84 @@ static int uhid_hid_parse(struct hid_device *hid) | |||
| 143 | static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, | 149 | static 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 | |||
| 227 | unlock: | ||
| 228 | mutex_unlock(&uhid->report_lock); | ||
| 229 | return ret ? ret : len; | ||
| 147 | } | 230 | } |
| 148 | 231 | ||
| 149 | static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, | 232 | static 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 | ||
| 374 | static 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 | |||
| 394 | unlock: | ||
| 395 | spin_unlock_irqrestore(&uhid->qlock, flags); | ||
| 396 | return 0; | ||
| 397 | } | ||
| 398 | |||
| 287 | static int uhid_char_open(struct inode *inode, struct file *file) | 399 | static 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 | ||
| 37 | struct uhid_create_req { | 39 | struct 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 | ||
| 78 | struct uhid_feature_req { | ||
| 79 | __u32 id; | ||
| 80 | __u8 rnum; | ||
| 81 | __u8 rtype; | ||
| 82 | } __attribute__((__packed__)); | ||
| 83 | |||
| 84 | struct uhid_feature_answer_req { | ||
| 85 | __u32 id; | ||
| 86 | __u16 err; | ||
| 87 | __u16 size; | ||
| 88 | __u8 data[UHID_DATA_MAX]; | ||
| 89 | }; | ||
| 90 | |||
| 76 | struct uhid_event { | 91 | struct 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 | ||
