Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HID: uhid: implement feature requests

HID standard allows sending a feature request to the device which is
answered by an HID report. uhid implements this by sending a UHID_FEATURE
event to user-space which then must answer with UHID_FEATURE_ANSWER. If it
doesn't do this in a timely manner, the request is discarded silently.

We serialize the feature requests, that is, there is always only a single
active feature-request sent to user-space, other requests have to wait.
HIDP and USB-HID do it the same way.

Because we discard feature-requests silently, we must make sure to match
a response to the corresponding request. We use sequence-IDs for this so
user-space must copy the ID from the request into the answer.
Feature-answers are ignored if they do not contain the same ID as the
currently pending feature request.

Internally, we must make sure that feature-requests are synchronized with
UHID_DESTROY and close() events. We must not dead-lock when closing the
HID device, either, so we have to use separate locks.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

authored by

David Herrmann and committed by
Jiri Kosina
fcfcf0de 3b3baa82

+136 -1
+119 -1
drivers/hid/uhid.c
··· 42 42 __u8 head; 43 43 __u8 tail; 44 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 53 static struct miscdevice uhid_misc; ··· 149 143 static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, 150 144 __u8 *buf, size_t count, unsigned char rtype) 151 145 { 152 - return 0; 146 + struct uhid_device *uhid = hid->driver_data; 147 + __u8 report_type; 148 + struct uhid_event *ev; 149 + unsigned long flags; 150 + int ret; 151 + size_t len; 152 + struct uhid_feature_answer_req *req; 153 + 154 + if (!uhid->running) 155 + return -EIO; 156 + 157 + switch (rtype) { 158 + case HID_FEATURE_REPORT: 159 + report_type = UHID_FEATURE_REPORT; 160 + break; 161 + case HID_OUTPUT_REPORT: 162 + report_type = UHID_OUTPUT_REPORT; 163 + break; 164 + case HID_INPUT_REPORT: 165 + report_type = UHID_INPUT_REPORT; 166 + break; 167 + default: 168 + return -EINVAL; 169 + } 170 + 171 + ret = mutex_lock_interruptible(&uhid->report_lock); 172 + if (ret) 173 + return ret; 174 + 175 + ev = kzalloc(sizeof(*ev), GFP_KERNEL); 176 + if (!ev) { 177 + ret = -ENOMEM; 178 + goto unlock; 179 + } 180 + 181 + spin_lock_irqsave(&uhid->qlock, flags); 182 + ev->type = UHID_FEATURE; 183 + ev->u.feature.id = atomic_inc_return(&uhid->report_id); 184 + ev->u.feature.rnum = rnum; 185 + ev->u.feature.rtype = report_type; 186 + 187 + atomic_set(&uhid->report_done, 0); 188 + uhid_queue(uhid, ev); 189 + spin_unlock_irqrestore(&uhid->qlock, flags); 190 + 191 + ret = wait_event_interruptible_timeout(uhid->report_wait, 192 + atomic_read(&uhid->report_done), 5 * HZ); 193 + 194 + /* 195 + * Make sure "uhid->running" is cleared on shutdown before 196 + * "uhid->report_done" is set. 197 + */ 198 + smp_rmb(); 199 + if (!ret || !uhid->running) { 200 + ret = -EIO; 201 + } else if (ret < 0) { 202 + ret = -ERESTARTSYS; 203 + } else { 204 + spin_lock_irqsave(&uhid->qlock, flags); 205 + req = &uhid->report_buf.u.feature_answer; 206 + 207 + if (req->err) { 208 + ret = -EIO; 209 + } else { 210 + ret = 0; 211 + len = min(count, 212 + min_t(size_t, req->size, UHID_DATA_MAX)); 213 + memcpy(buf, req->data, len); 214 + } 215 + 216 + spin_unlock_irqrestore(&uhid->qlock, flags); 217 + } 218 + 219 + atomic_set(&uhid->report_done, 1); 220 + 221 + unlock: 222 + mutex_unlock(&uhid->report_lock); 223 + return ret ? ret : len; 153 224 } 154 225 155 226 static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, ··· 348 265 if (!uhid->running) 349 266 return -EINVAL; 350 267 268 + /* clear "running" before setting "report_done" */ 351 269 uhid->running = false; 270 + smp_wmb(); 271 + atomic_set(&uhid->report_done, 1); 272 + wake_up_interruptible(&uhid->report_wait); 352 273 353 274 hid_destroy_device(uhid->hid); 354 275 kfree(uhid->rd_data); ··· 371 284 return 0; 372 285 } 373 286 287 + static int uhid_dev_feature_answer(struct uhid_device *uhid, 288 + struct uhid_event *ev) 289 + { 290 + unsigned long flags; 291 + 292 + if (!uhid->running) 293 + return -EINVAL; 294 + 295 + spin_lock_irqsave(&uhid->qlock, flags); 296 + 297 + /* id for old report; drop it silently */ 298 + if (atomic_read(&uhid->report_id) != ev->u.feature_answer.id) 299 + goto unlock; 300 + if (atomic_read(&uhid->report_done)) 301 + goto unlock; 302 + 303 + memcpy(&uhid->report_buf, ev, sizeof(*ev)); 304 + atomic_set(&uhid->report_done, 1); 305 + wake_up_interruptible(&uhid->report_wait); 306 + 307 + unlock: 308 + spin_unlock_irqrestore(&uhid->qlock, flags); 309 + return 0; 310 + } 311 + 374 312 static int uhid_char_open(struct inode *inode, struct file *file) 375 313 { 376 314 struct uhid_device *uhid; ··· 405 293 return -ENOMEM; 406 294 407 295 mutex_init(&uhid->devlock); 296 + mutex_init(&uhid->report_lock); 408 297 spin_lock_init(&uhid->qlock); 409 298 init_waitqueue_head(&uhid->waitq); 299 + init_waitqueue_head(&uhid->report_wait); 410 300 uhid->running = false; 301 + atomic_set(&uhid->report_done, 1); 411 302 412 303 file->private_data = uhid; 413 304 nonseekable_open(inode, file); ··· 512 397 break; 513 398 case UHID_INPUT: 514 399 ret = uhid_dev_input(uhid, &uhid->input_buf); 400 + break; 401 + case UHID_FEATURE_ANSWER: 402 + ret = uhid_dev_feature_answer(uhid, &uhid->input_buf); 515 403 break; 516 404 default: 517 405 ret = -EOPNOTSUPP;
+17
include/linux/uhid.h
··· 32 32 UHID_OUTPUT, 33 33 UHID_OUTPUT_EV, 34 34 UHID_INPUT, 35 + UHID_FEATURE, 36 + UHID_FEATURE_ANSWER, 35 37 }; 36 38 37 39 struct uhid_create_req { ··· 75 73 __s32 value; 76 74 } __attribute__((__packed__)); 77 75 76 + struct uhid_feature_req { 77 + __u32 id; 78 + __u8 rnum; 79 + __u8 rtype; 80 + } __attribute__((__packed__)); 81 + 82 + struct uhid_feature_answer_req { 83 + __u32 id; 84 + __u16 err; 85 + __u16 size; 86 + __u8 data[UHID_DATA_MAX]; 87 + }; 88 + 78 89 struct uhid_event { 79 90 __u32 type; 80 91 ··· 96 81 struct uhid_input_req input; 97 82 struct uhid_output_req output; 98 83 struct uhid_output_ev_req output_ev; 84 + struct uhid_feature_req feature; 85 + struct uhid_feature_answer_req feature_answer; 99 86 } u; 100 87 } __attribute__((__packed__)); 101 88