aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hidraw.c
diff options
context:
space:
mode:
authorGlenn Elliott <gelliott@cs.unc.edu>2012-03-04 19:47:13 -0500
committerGlenn Elliott <gelliott@cs.unc.edu>2012-03-04 19:47:13 -0500
commitc71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch)
treeecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /drivers/hid/hidraw.c
parentea53c912f8a86a8567697115b6a0d8152beee5c8 (diff)
parent6a00f206debf8a5c8899055726ad127dbeeed098 (diff)
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts: litmus/sched_cedf.c
Diffstat (limited to 'drivers/hid/hidraw.c')
-rw-r--r--drivers/hid/hidraw.c176
1 files changed, 134 insertions, 42 deletions
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index a3866b5c0c43..c79578b5a788 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -19,6 +19,8 @@
19 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 19 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
20 */ 20 */
21 21
22#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23
22#include <linux/fs.h> 24#include <linux/fs.h>
23#include <linux/module.h> 25#include <linux/module.h>
24#include <linux/errno.h> 26#include <linux/errno.h>
@@ -32,7 +34,6 @@
32#include <linux/hid.h> 34#include <linux/hid.h>
33#include <linux/mutex.h> 35#include <linux/mutex.h>
34#include <linux/sched.h> 36#include <linux/sched.h>
35#include <linux/smp_lock.h>
36 37
37#include <linux/hidraw.h> 38#include <linux/hidraw.h>
38 39
@@ -90,7 +91,7 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count,
90 ret = -EFAULT; 91 ret = -EFAULT;
91 goto out; 92 goto out;
92 } 93 }
93 ret += len; 94 ret = len;
94 95
95 kfree(list->buffer[list->tail].value); 96 kfree(list->buffer[list->tail].value);
96 list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1); 97 list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);
@@ -100,16 +101,15 @@ out:
100 return ret; 101 return ret;
101} 102}
102 103
103/* the first byte is expected to be a report number */ 104/* The first byte is expected to be a report number.
104static 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)
105{ 107{
106 unsigned int minor = iminor(file->f_path.dentry->d_inode); 108 unsigned int minor = iminor(file->f_path.dentry->d_inode);
107 struct hid_device *dev; 109 struct hid_device *dev;
108 __u8 *buf; 110 __u8 *buf;
109 int ret = 0; 111 int ret = 0;
110 112
111 mutex_lock(&minors_lock);
112
113 if (!hidraw_table[minor]) { 113 if (!hidraw_table[minor]) {
114 ret = -ENODEV; 114 ret = -ENODEV;
115 goto out; 115 goto out;
@@ -123,6 +123,70 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t
123 } 123 }
124 124
125 if (count > HID_MAX_BUFFER_SIZE) { 125 if (count > HID_MAX_BUFFER_SIZE) {
126 hid_warn(dev, "pid %d passed too large report\n",
127 task_pid_nr(current));
128 ret = -EINVAL;
129 goto out;
130 }
131
132 if (count < 2) {
133 hid_warn(dev, "pid %d passed too short report\n",
134 task_pid_nr(current));
135 ret = -EINVAL;
136 goto out;
137 }
138
139 buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
140 if (!buf) {
141 ret = -ENOMEM;
142 goto out;
143 }
144
145 if (copy_from_user(buf, buffer, count)) {
146 ret = -EFAULT;
147 goto out_free;
148 }
149
150 ret = dev->hid_output_raw_report(dev, buf, count, report_type);
151out_free:
152 kfree(buf);
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);
163 mutex_unlock(&minors_lock);
164 return ret;
165}
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) {
126 printk(KERN_WARNING "hidraw: pid %d passed too large report\n", 190 printk(KERN_WARNING "hidraw: pid %d passed too large report\n",
127 task_pid_nr(current)); 191 task_pid_nr(current));
128 ret = -EINVAL; 192 ret = -EINVAL;
@@ -142,16 +206,30 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t
142 goto out; 206 goto out;
143 } 207 }
144 208
145 if (copy_from_user(buf, buffer, count)) { 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)) {
146 ret = -EFAULT; 224 ret = -EFAULT;
147 goto out_free; 225 goto out_free;
148 } 226 }
149 227
150 ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT); 228 ret = len;
229
151out_free: 230out_free:
152 kfree(buf); 231 kfree(buf);
153out: 232out:
154 mutex_unlock(&minors_lock);
155 return ret; 233 return ret;
156} 234}
157 235
@@ -193,15 +271,13 @@ static int hidraw_open(struct inode *inode, struct file *file)
193 271
194 dev = hidraw_table[minor]; 272 dev = hidraw_table[minor];
195 if (!dev->open++) { 273 if (!dev->open++) {
196 if (dev->hid->ll_driver->power) { 274 err = hid_hw_power(dev->hid, PM_HINT_FULLON);
197 err = dev->hid->ll_driver->power(dev->hid, PM_HINT_FULLON); 275 if (err < 0)
198 if (err < 0) 276 goto out_unlock;
199 goto out_unlock; 277
200 } 278 err = hid_hw_open(dev->hid);
201 err = dev->hid->ll_driver->open(dev->hid);
202 if (err < 0) { 279 if (err < 0) {
203 if (dev->hid->ll_driver->power) 280 hid_hw_power(dev->hid, PM_HINT_NORMAL);
204 dev->hid->ll_driver->power(dev->hid, PM_HINT_NORMAL);
205 dev->open--; 281 dev->open--;
206 } 282 }
207 } 283 }
@@ -218,25 +294,30 @@ static int hidraw_release(struct inode * inode, struct file * file)
218 unsigned int minor = iminor(inode); 294 unsigned int minor = iminor(inode);
219 struct hidraw *dev; 295 struct hidraw *dev;
220 struct hidraw_list *list = file->private_data; 296 struct hidraw_list *list = file->private_data;
297 int ret;
221 298
222 if (!hidraw_table[minor]) 299 mutex_lock(&minors_lock);
223 return -ENODEV; 300 if (!hidraw_table[minor]) {
301 ret = -ENODEV;
302 goto unlock;
303 }
224 304
225 list_del(&list->node); 305 list_del(&list->node);
226 dev = hidraw_table[minor]; 306 dev = hidraw_table[minor];
227 if (!--dev->open) { 307 if (!--dev->open) {
228 if (list->hidraw->exist) { 308 if (list->hidraw->exist) {
229 if (dev->hid->ll_driver->power) 309 hid_hw_power(dev->hid, PM_HINT_NORMAL);
230 dev->hid->ll_driver->power(dev->hid, PM_HINT_NORMAL); 310 hid_hw_close(dev->hid);
231 dev->hid->ll_driver->close(dev->hid);
232 } else { 311 } else {
233 kfree(list->hidraw); 312 kfree(list->hidraw);
234 } 313 }
235 } 314 }
236
237 kfree(list); 315 kfree(list);
316 ret = 0;
317unlock:
318 mutex_unlock(&minors_lock);
238 319
239 return 0; 320 return ret;
240} 321}
241 322
242static long hidraw_ioctl(struct file *file, unsigned int cmd, 323static long hidraw_ioctl(struct file *file, unsigned int cmd,
@@ -291,18 +372,30 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
291 default: 372 default:
292 { 373 {
293 struct hid_device *hid = dev->hid; 374 struct hid_device *hid = dev->hid;
294 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) {
295 ret = -EINVAL; 393 ret = -EINVAL;
296 break; 394 break;
297 } 395 }
298 396
299 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME(0))) { 397 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME(0))) {
300 int len; 398 int len = strlen(hid->name) + 1;
301 if (!hid->name) {
302 ret = 0;
303 break;
304 }
305 len = strlen(hid->name) + 1;
306 if (len > _IOC_SIZE(cmd)) 399 if (len > _IOC_SIZE(cmd))
307 len = _IOC_SIZE(cmd); 400 len = _IOC_SIZE(cmd);
308 ret = copy_to_user(user_arg, hid->name, len) ? 401 ret = copy_to_user(user_arg, hid->name, len) ?
@@ -311,19 +404,14 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
311 } 404 }
312 405
313 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS(0))) { 406 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS(0))) {
314 int len; 407 int len = strlen(hid->phys) + 1;
315 if (!hid->phys) {
316 ret = 0;
317 break;
318 }
319 len = strlen(hid->phys) + 1;
320 if (len > _IOC_SIZE(cmd)) 408 if (len > _IOC_SIZE(cmd))
321 len = _IOC_SIZE(cmd); 409 len = _IOC_SIZE(cmd);
322 ret = copy_to_user(user_arg, hid->phys, len) ? 410 ret = copy_to_user(user_arg, hid->phys, len) ?
323 -EFAULT : len; 411 -EFAULT : len;
324 break; 412 break;
325 } 413 }
326 } 414 }
327 415
328 ret = -ENOTTY; 416 ret = -ENOTTY;
329 } 417 }
@@ -340,6 +428,10 @@ static const struct file_operations hidraw_ops = {
340 .open = hidraw_open, 428 .open = hidraw_open,
341 .release = hidraw_release, 429 .release = hidraw_release,
342 .unlocked_ioctl = hidraw_ioctl, 430 .unlocked_ioctl = hidraw_ioctl,
431#ifdef CONFIG_COMPAT
432 .compat_ioctl = hidraw_ioctl,
433#endif
434 .llseek = noop_llseek,
343}; 435};
344 436
345void hidraw_report_event(struct hid_device *hid, u8 *data, int len) 437void hidraw_report_event(struct hid_device *hid, u8 *data, int len)
@@ -420,14 +512,14 @@ void hidraw_disconnect(struct hid_device *hid)
420 512
421 hidraw->exist = 0; 513 hidraw->exist = 0;
422 514
515 device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
516
423 mutex_lock(&minors_lock); 517 mutex_lock(&minors_lock);
424 hidraw_table[hidraw->minor] = NULL; 518 hidraw_table[hidraw->minor] = NULL;
425 mutex_unlock(&minors_lock); 519 mutex_unlock(&minors_lock);
426 520
427 device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
428
429 if (hidraw->open) { 521 if (hidraw->open) {
430 hid->ll_driver->close(hid); 522 hid_hw_close(hid);
431 wake_up_interruptible(&hidraw->wait); 523 wake_up_interruptible(&hidraw->wait);
432 } else { 524 } else {
433 kfree(hidraw); 525 kfree(hidraw);
@@ -446,7 +538,7 @@ int __init hidraw_init(void)
446 hidraw_major = MAJOR(dev_id); 538 hidraw_major = MAJOR(dev_id);
447 539
448 if (result < 0) { 540 if (result < 0) {
449 printk(KERN_WARNING "hidraw: can't get major number\n"); 541 pr_warn("can't get major number\n");
450 result = 0; 542 result = 0;
451 goto out; 543 goto out;
452 } 544 }