aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hidraw.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid/hidraw.c')
-rw-r--r--drivers/hid/hidraw.c112
1 files changed, 103 insertions, 9 deletions
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index 468e87b53ed2..54409cba018c 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -91,7 +91,7 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count,
91 ret = -EFAULT; 91 ret = -EFAULT;
92 goto out; 92 goto out;
93 } 93 }
94 ret += len; 94 ret = len;
95 95
96 kfree(list->buffer[list->tail].value); 96 kfree(list->buffer[list->tail].value);
97 list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1); 97 list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);
@@ -102,15 +102,14 @@ out:
102} 102}
103 103
104/* the first byte is expected to be a report number */ 104/* the first byte is expected to be a report number */
105static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) 105/* This function is to be called with the minors_lock mutex held */
106static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type)
106{ 107{
107 unsigned int minor = iminor(file->f_path.dentry->d_inode); 108 unsigned int minor = iminor(file->f_path.dentry->d_inode);
108 struct hid_device *dev; 109 struct hid_device *dev;
109 __u8 *buf; 110 __u8 *buf;
110 int ret = 0; 111 int ret = 0;
111 112
112 mutex_lock(&minors_lock);
113
114 if (!hidraw_table[minor]) { 113 if (!hidraw_table[minor]) {
115 ret = -ENODEV; 114 ret = -ENODEV;
116 goto out; 115 goto out;
@@ -148,14 +147,92 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t
148 goto out_free; 147 goto out_free;
149 } 148 }
150 149
151 ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT); 150 ret = dev->hid_output_raw_report(dev, buf, count, report_type);
152out_free: 151out_free:
153 kfree(buf); 152 kfree(buf);
154out: 153out:
154 return ret;
155}
156
157/* the first byte is expected to be a report number */
158static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
159{
160 ssize_t ret;
161 mutex_lock(&minors_lock);
162 ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);
155 mutex_unlock(&minors_lock); 163 mutex_unlock(&minors_lock);
156 return ret; 164 return ret;
157} 165}
158 166
167
168/* This function performs a Get_Report transfer over the control endpoint
169 per section 7.2.1 of the HID specification, version 1.1. The first byte
170 of buffer is the report number to request, or 0x0 if the defice does not
171 use numbered reports. The report_type parameter can be HID_FEATURE_REPORT
172 or HID_INPUT_REPORT. This function is to be called with the minors_lock
173 mutex held. */
174static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type)
175{
176 unsigned int minor = iminor(file->f_path.dentry->d_inode);
177 struct hid_device *dev;
178 __u8 *buf;
179 int ret = 0, len;
180 unsigned char report_number;
181
182 dev = hidraw_table[minor]->hid;
183
184 if (!dev->hid_get_raw_report) {
185 ret = -ENODEV;
186 goto out;
187 }
188
189 if (count > HID_MAX_BUFFER_SIZE) {
190 printk(KERN_WARNING "hidraw: pid %d passed too large report\n",
191 task_pid_nr(current));
192 ret = -EINVAL;
193 goto out;
194 }
195
196 if (count < 2) {
197 printk(KERN_WARNING "hidraw: pid %d passed too short report\n",
198 task_pid_nr(current));
199 ret = -EINVAL;
200 goto out;
201 }
202
203 buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
204 if (!buf) {
205 ret = -ENOMEM;
206 goto out;
207 }
208
209 /* Read the first byte from the user. This is the report number,
210 which is passed to dev->hid_get_raw_report(). */
211 if (copy_from_user(&report_number, buffer, 1)) {
212 ret = -EFAULT;
213 goto out_free;
214 }
215
216 ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type);
217
218 if (ret < 0)
219 goto out_free;
220
221 len = (ret < count) ? ret : count;
222
223 if (copy_to_user(buffer, buf, len)) {
224 ret = -EFAULT;
225 goto out_free;
226 }
227
228 ret = len;
229
230out_free:
231 kfree(buf);
232out:
233 return ret;
234}
235
159static unsigned int hidraw_poll(struct file *file, poll_table *wait) 236static unsigned int hidraw_poll(struct file *file, poll_table *wait)
160{ 237{
161 struct hidraw_list *list = file->private_data; 238 struct hidraw_list *list = file->private_data;
@@ -295,7 +372,24 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
295 default: 372 default:
296 { 373 {
297 struct hid_device *hid = dev->hid; 374 struct hid_device *hid = dev->hid;
298 if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) { 375 if (_IOC_TYPE(cmd) != 'H') {
376 ret = -EINVAL;
377 break;
378 }
379
380 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) {
381 int len = _IOC_SIZE(cmd);
382 ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT);
383 break;
384 }
385 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) {
386 int len = _IOC_SIZE(cmd);
387 ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT);
388 break;
389 }
390
391 /* Begin Read-only ioctls. */
392 if (_IOC_DIR(cmd) != _IOC_READ) {
299 ret = -EINVAL; 393 ret = -EINVAL;
300 break; 394 break;
301 } 395 }
@@ -327,7 +421,7 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
327 -EFAULT : len; 421 -EFAULT : len;
328 break; 422 break;
329 } 423 }
330 } 424 }
331 425
332 ret = -ENOTTY; 426 ret = -ENOTTY;
333 } 427 }
@@ -428,12 +522,12 @@ void hidraw_disconnect(struct hid_device *hid)
428 522
429 hidraw->exist = 0; 523 hidraw->exist = 0;
430 524
525 device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
526
431 mutex_lock(&minors_lock); 527 mutex_lock(&minors_lock);
432 hidraw_table[hidraw->minor] = NULL; 528 hidraw_table[hidraw->minor] = NULL;
433 mutex_unlock(&minors_lock); 529 mutex_unlock(&minors_lock);
434 530
435 device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
436
437 if (hidraw->open) { 531 if (hidraw->open) {
438 hid_hw_close(hid); 532 hid_hw_close(hid);
439 wake_up_interruptible(&hidraw->wait); 533 wake_up_interruptible(&hidraw->wait);