aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@gmail.com>2014-07-29 11:14:24 -0400
committerJiri Kosina <jkosina@suse.cz>2014-08-25 04:28:08 -0400
commit11c221553080408b203a00b91ad5f647dfb218d1 (patch)
treeba4aa924ec09d7775ba887fbdaeaee45cff191b0
parent7c4003bc367d5ff1cbce579a883f17698a9a6da2 (diff)
HID: uhid: implement SET_REPORT
We so far lacked support for hid_hw_raw_request(..., HID_REQ_SET_REPORT); Add support for it and simply forward the request to user-space. Note that SET_REPORT is synchronous, just like GET_REPORT, even though it does not provide any data back besides an error code. If a transport layer does SET_REPORT asynchronously, they can just ACK it immediately by writing an uhid_set_report_reply to uhid. This patch re-uses the synchronous uhid-report infrastructure to query user-space. Note that this means you cannot run SET_REPORT and GET_REPORT in parallel. However, that has always been a restriction of HID and due to its blocking nature, this is just fine. Maybe some future transport layer supports parallel requests (very unlikely), however, until then lets not over-complicate things and avoid request-lookup-tables. Signed-off-by: David Herrmann <dh.herrmann@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--drivers/hid/uhid.c206
-rw-r--r--include/uapi/linux/uhid.h17
2 files changed, 155 insertions, 68 deletions
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index 8bf613e3783d..19511481a7d3 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -49,6 +49,7 @@ struct uhid_device {
49 wait_queue_head_t report_wait; 49 wait_queue_head_t report_wait;
50 bool report_running; 50 bool report_running;
51 u32 report_id; 51 u32 report_id;
52 u32 report_type;
52 struct uhid_event report_buf; 53 struct uhid_event report_buf;
53}; 54};
54 55
@@ -124,95 +125,166 @@ static int uhid_hid_parse(struct hid_device *hid)
124 return hid_parse_report(hid, uhid->rd_data, uhid->rd_size); 125 return hid_parse_report(hid, uhid->rd_data, uhid->rd_size);
125} 126}
126 127
128/* must be called with report_lock held */
129static int __uhid_report_queue_and_wait(struct uhid_device *uhid,
130 struct uhid_event *ev,
131 __u32 *report_id)
132{
133 unsigned long flags;
134 int ret;
135
136 spin_lock_irqsave(&uhid->qlock, flags);
137 *report_id = ++uhid->report_id;
138 uhid->report_type = ev->type;
139 uhid->report_running = true;
140 uhid_queue(uhid, ev);
141 spin_unlock_irqrestore(&uhid->qlock, flags);
142
143 ret = wait_event_interruptible_timeout(uhid->report_wait,
144 !uhid->report_running || !uhid->running,
145 5 * HZ);
146 if (!ret || !uhid->running || uhid->report_running)
147 ret = -EIO;
148 else if (ret < 0)
149 ret = -ERESTARTSYS;
150 else
151 ret = 0;
152
153 uhid->report_running = false;
154
155 return ret;
156}
157
158static void uhid_report_wake_up(struct uhid_device *uhid, u32 id,
159 const struct uhid_event *ev)
160{
161 unsigned long flags;
162
163 spin_lock_irqsave(&uhid->qlock, flags);
164
165 /* id for old report; drop it silently */
166 if (uhid->report_type != ev->type || uhid->report_id != id)
167 goto unlock;
168 if (!uhid->report_running)
169 goto unlock;
170
171 memcpy(&uhid->report_buf, ev, sizeof(*ev));
172 uhid->report_running = false;
173 wake_up_interruptible(&uhid->report_wait);
174
175unlock:
176 spin_unlock_irqrestore(&uhid->qlock, flags);
177}
178
127static int uhid_hid_get_report(struct hid_device *hid, unsigned char rnum, 179static int uhid_hid_get_report(struct hid_device *hid, unsigned char rnum,
128 __u8 *buf, size_t count, unsigned char rtype) 180 u8 *buf, size_t count, u8 rtype)
129{ 181{
130 struct uhid_device *uhid = hid->driver_data; 182 struct uhid_device *uhid = hid->driver_data;
131 __u8 report_type; 183 struct uhid_get_report_reply_req *req;
132 struct uhid_event *ev; 184 struct uhid_event *ev;
133 unsigned long flags;
134 int ret; 185 int ret;
135 size_t uninitialized_var(len);
136 struct uhid_get_report_reply_req *req;
137 186
138 if (!uhid->running) 187 if (!uhid->running)
139 return -EIO; 188 return -EIO;
140 189
141 switch (rtype) { 190 ev = kzalloc(sizeof(*ev), GFP_KERNEL);
142 case HID_FEATURE_REPORT: 191 if (!ev)
143 report_type = UHID_FEATURE_REPORT; 192 return -ENOMEM;
144 break; 193
145 case HID_OUTPUT_REPORT: 194 ev->type = UHID_GET_REPORT;
146 report_type = UHID_OUTPUT_REPORT; 195 ev->u.get_report.rnum = rnum;
147 break; 196 ev->u.get_report.rtype = rtype;
148 case HID_INPUT_REPORT:
149 report_type = UHID_INPUT_REPORT;
150 break;
151 default:
152 return -EINVAL;
153 }
154 197
155 ret = mutex_lock_interruptible(&uhid->report_lock); 198 ret = mutex_lock_interruptible(&uhid->report_lock);
156 if (ret) 199 if (ret) {
200 kfree(ev);
157 return ret; 201 return ret;
202 }
158 203
159 ev = kzalloc(sizeof(*ev), GFP_KERNEL); 204 /* this _always_ takes ownership of @ev */
160 if (!ev) { 205 ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.get_report.id);
161 ret = -ENOMEM; 206 if (ret)
162 goto unlock; 207 goto unlock;
208
209 req = &uhid->report_buf.u.get_report_reply;
210 if (req->err) {
211 ret = -EIO;
212 } else {
213 ret = min3(count, (size_t)req->size, (size_t)UHID_DATA_MAX);
214 memcpy(buf, req->data, ret);
163 } 215 }
164 216
165 spin_lock_irqsave(&uhid->qlock, flags); 217unlock:
166 ev->type = UHID_GET_REPORT; 218 mutex_unlock(&uhid->report_lock);
167 ev->u.get_report.id = ++uhid->report_id; 219 return ret;
168 ev->u.get_report.rnum = rnum; 220}
169 ev->u.get_report.rtype = report_type;
170 221
171 uhid->report_running = true; 222static int uhid_hid_set_report(struct hid_device *hid, unsigned char rnum,
172 uhid_queue(uhid, ev); 223 const u8 *buf, size_t count, u8 rtype)
173 spin_unlock_irqrestore(&uhid->qlock, flags); 224{
225 struct uhid_device *uhid = hid->driver_data;
226 struct uhid_event *ev;
227 int ret;
174 228
175 ret = wait_event_interruptible_timeout(uhid->report_wait, 229 if (!uhid->running || count > UHID_DATA_MAX)
176 !uhid->report_running || !uhid->running, 230 return -EIO;
177 5 * HZ);
178 231
179 if (!ret || !uhid->running) { 232 ev = kzalloc(sizeof(*ev), GFP_KERNEL);
180 ret = -EIO; 233 if (!ev)
181 } else if (ret < 0) { 234 return -ENOMEM;
182 ret = -ERESTARTSYS;
183 } else {
184 spin_lock_irqsave(&uhid->qlock, flags);
185 req = &uhid->report_buf.u.get_report_reply;
186 235
187 if (req->err) { 236 ev->type = UHID_SET_REPORT;
188 ret = -EIO; 237 ev->u.set_report.rnum = rnum;
189 } else { 238 ev->u.set_report.rtype = rtype;
190 ret = 0; 239 ev->u.set_report.size = count;
191 len = min(count, 240 memcpy(ev->u.set_report.data, buf, count);
192 min_t(size_t, req->size, UHID_DATA_MAX));
193 memcpy(buf, req->data, len);
194 }
195 241
196 spin_unlock_irqrestore(&uhid->qlock, flags); 242 ret = mutex_lock_interruptible(&uhid->report_lock);
243 if (ret) {
244 kfree(ev);
245 return ret;
197 } 246 }
198 247
199 uhid->report_running = false; 248 /* this _always_ takes ownership of @ev */
249 ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.set_report.id);
250 if (ret)
251 goto unlock;
252
253 if (uhid->report_buf.u.set_report_reply.err)
254 ret = -EIO;
255 else
256 ret = count;
200 257
201unlock: 258unlock:
202 mutex_unlock(&uhid->report_lock); 259 mutex_unlock(&uhid->report_lock);
203 return ret ? ret : len; 260 return ret;
204} 261}
205 262
206static int uhid_hid_raw_request(struct hid_device *hid, unsigned char reportnum, 263static int uhid_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
207 __u8 *buf, size_t len, unsigned char rtype, 264 __u8 *buf, size_t len, unsigned char rtype,
208 int reqtype) 265 int reqtype)
209{ 266{
267 u8 u_rtype;
268
269 switch (rtype) {
270 case HID_FEATURE_REPORT:
271 u_rtype = UHID_FEATURE_REPORT;
272 break;
273 case HID_OUTPUT_REPORT:
274 u_rtype = UHID_OUTPUT_REPORT;
275 break;
276 case HID_INPUT_REPORT:
277 u_rtype = UHID_INPUT_REPORT;
278 break;
279 default:
280 return -EINVAL;
281 }
282
210 switch (reqtype) { 283 switch (reqtype) {
211 case HID_REQ_GET_REPORT: 284 case HID_REQ_GET_REPORT:
212 return uhid_hid_get_report(hid, reportnum, buf, len, rtype); 285 return uhid_hid_get_report(hid, reportnum, buf, len, u_rtype);
213 case HID_REQ_SET_REPORT: 286 case HID_REQ_SET_REPORT:
214 /* TODO: implement proper SET_REPORT functionality */ 287 return uhid_hid_set_report(hid, reportnum, buf, len, u_rtype);
215 return -ENOSYS;
216 default: 288 default:
217 return -EIO; 289 return -EIO;
218 } 290 }
@@ -490,25 +562,20 @@ static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev)
490static int uhid_dev_get_report_reply(struct uhid_device *uhid, 562static int uhid_dev_get_report_reply(struct uhid_device *uhid,
491 struct uhid_event *ev) 563 struct uhid_event *ev)
492{ 564{
493 unsigned long flags;
494
495 if (!uhid->running) 565 if (!uhid->running)
496 return -EINVAL; 566 return -EINVAL;
497 567
498 spin_lock_irqsave(&uhid->qlock, flags); 568 uhid_report_wake_up(uhid, ev->u.get_report_reply.id, ev);
499 569 return 0;
500 /* id for old report; drop it silently */ 570}
501 if (uhid->report_id != ev->u.get_report_reply.id)
502 goto unlock;
503 if (!uhid->report_running)
504 goto unlock;
505 571
506 memcpy(&uhid->report_buf, ev, sizeof(*ev)); 572static int uhid_dev_set_report_reply(struct uhid_device *uhid,
507 uhid->report_running = false; 573 struct uhid_event *ev)
508 wake_up_interruptible(&uhid->report_wait); 574{
575 if (!uhid->running)
576 return -EINVAL;
509 577
510unlock: 578 uhid_report_wake_up(uhid, ev->u.set_report_reply.id, ev);
511 spin_unlock_irqrestore(&uhid->qlock, flags);
512 return 0; 579 return 0;
513} 580}
514 581
@@ -637,6 +704,9 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
637 case UHID_GET_REPORT_REPLY: 704 case UHID_GET_REPORT_REPLY:
638 ret = uhid_dev_get_report_reply(uhid, &uhid->input_buf); 705 ret = uhid_dev_get_report_reply(uhid, &uhid->input_buf);
639 break; 706 break;
707 case UHID_SET_REPORT_REPLY:
708 ret = uhid_dev_set_report_reply(uhid, &uhid->input_buf);
709 break;
640 default: 710 default:
641 ret = -EOPNOTSUPP; 711 ret = -EOPNOTSUPP;
642 } 712 }
diff --git a/include/uapi/linux/uhid.h b/include/uapi/linux/uhid.h
index 116536eeae62..62aac0e4edf3 100644
--- a/include/uapi/linux/uhid.h
+++ b/include/uapi/linux/uhid.h
@@ -37,6 +37,8 @@ enum uhid_event_type {
37 UHID_GET_REPORT_REPLY, 37 UHID_GET_REPORT_REPLY,
38 UHID_CREATE2, 38 UHID_CREATE2,
39 UHID_INPUT2, 39 UHID_INPUT2,
40 UHID_SET_REPORT,
41 UHID_SET_REPORT_REPLY,
40}; 42};
41 43
42struct uhid_create2_req { 44struct uhid_create2_req {
@@ -84,6 +86,19 @@ struct uhid_get_report_reply_req {
84 __u8 data[UHID_DATA_MAX]; 86 __u8 data[UHID_DATA_MAX];
85} __attribute__((__packed__)); 87} __attribute__((__packed__));
86 88
89struct uhid_set_report_req {
90 __u32 id;
91 __u8 rnum;
92 __u8 rtype;
93 __u16 size;
94 __u8 data[UHID_DATA_MAX];
95} __attribute__((__packed__));
96
97struct uhid_set_report_reply_req {
98 __u32 id;
99 __u16 err;
100} __attribute__((__packed__));
101
87/* 102/*
88 * Compat Layer 103 * Compat Layer
89 * All these commands and requests are obsolete. You should avoid using them in 104 * All these commands and requests are obsolete. You should avoid using them in
@@ -165,6 +180,8 @@ struct uhid_event {
165 struct uhid_get_report_reply_req get_report_reply; 180 struct uhid_get_report_reply_req get_report_reply;
166 struct uhid_create2_req create2; 181 struct uhid_create2_req create2;
167 struct uhid_input2_req input2; 182 struct uhid_input2_req input2;
183 struct uhid_set_report_req set_report;
184 struct uhid_set_report_reply_req set_report_reply;
168 } u; 185 } u;
169} __attribute__((__packed__)); 186} __attribute__((__packed__));
170 187