]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/hid/usbhid/hiddev.c
HID: Switch hiddev to unlocked_ioctl
[linux-2.6-omap-h63xx.git] / drivers / hid / usbhid / hiddev.c
index 5fc4019956ba2ffe17b2d5dda04206aa7b5d9a4d..fe4f36472cb7a05b5af52233a30908134dc910ab 100644 (file)
@@ -393,7 +393,158 @@ static unsigned int hiddev_poll(struct file *file, poll_table *wait)
 /*
  * "ioctl" file op
  */
-static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg)
+{
+       struct hid_device *hid = hiddev->hid;
+       struct hiddev_report_info rinfo;
+       struct hiddev_usage_ref_multi *uref_multi = NULL;
+       struct hiddev_usage_ref *uref;
+       struct hid_report *report;
+       struct hid_field *field;
+       int i;
+
+       uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
+       if (!uref_multi)
+               return -ENOMEM;
+       lock_kernel();
+       uref = &uref_multi->uref;
+       if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
+               if (copy_from_user(uref_multi, user_arg,
+                                  sizeof(*uref_multi)))
+                       goto fault;
+       } else {
+               if (copy_from_user(uref, user_arg, sizeof(*uref)))
+                       goto fault;
+       }
+
+       switch (cmd) {
+       case HIDIOCGUCODE:
+               rinfo.report_type = uref->report_type;
+               rinfo.report_id = uref->report_id;
+               if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+                       goto inval;
+
+               if (uref->field_index >= report->maxfield)
+                       goto inval;
+
+               field = report->field[uref->field_index];
+               if (uref->usage_index >= field->maxusage)
+                       goto inval;
+
+               uref->usage_code = field->usage[uref->usage_index].hid;
+
+               if (copy_to_user(user_arg, uref, sizeof(*uref)))
+                       goto fault;
+
+               kfree(uref_multi);
+               return 0;
+
+       default:
+               if (cmd != HIDIOCGUSAGE &&
+                   cmd != HIDIOCGUSAGES &&
+                   uref->report_type == HID_REPORT_TYPE_INPUT)
+                       goto inval;
+
+               if (uref->report_id == HID_REPORT_ID_UNKNOWN) {
+                       field = hiddev_lookup_usage(hid, uref);
+                       if (field == NULL)
+                               goto inval;
+               } else {
+                       rinfo.report_type = uref->report_type;
+                       rinfo.report_id = uref->report_id;
+                       if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+                               goto inval;
+
+                       if (uref->field_index >= report->maxfield)
+                               goto inval;
+
+                       field = report->field[uref->field_index];
+
+                       if (cmd == HIDIOCGCOLLECTIONINDEX) {
+                               if (uref->usage_index >= field->maxusage)
+                                       goto inval;
+                       } else if (uref->usage_index >= field->report_count)
+                               goto inval;
+
+                       else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) &&
+                                (uref_multi->num_values > HID_MAX_MULTI_USAGES ||
+                                 uref->usage_index + uref_multi->num_values > field->report_count))
+                               goto inval;
+                       }
+
+               switch (cmd) {
+               case HIDIOCGUSAGE:
+                       uref->value = field->value[uref->usage_index];
+                       if (copy_to_user(user_arg, uref, sizeof(*uref)))
+                               goto fault;
+                       goto goodreturn;
+
+               case HIDIOCSUSAGE:
+                       field->value[uref->usage_index] = uref->value;
+                       goto goodreturn;
+
+               case HIDIOCGCOLLECTIONINDEX:
+                       kfree(uref_multi);
+                       return field->usage[uref->usage_index].collection_index;
+               case HIDIOCGUSAGES:
+                       for (i = 0; i < uref_multi->num_values; i++)
+                               uref_multi->values[i] =
+                                   field->value[uref->usage_index + i];
+                       if (copy_to_user(user_arg, uref_multi,
+                                        sizeof(*uref_multi)))
+                               goto fault;
+                       goto goodreturn;
+               case HIDIOCSUSAGES:
+                       for (i = 0; i < uref_multi->num_values; i++)
+                               field->value[uref->usage_index + i] =
+                                   uref_multi->values[i];
+                       goto goodreturn;
+               }
+
+goodreturn:
+               unlock_kernel();
+               kfree(uref_multi);
+               return 0;
+fault:
+               unlock_kernel();
+               kfree(uref_multi);
+               return -EFAULT;
+inval:
+               unlock_kernel();
+               kfree(uref_multi);
+               return -EINVAL;
+       }
+}
+
+static noinline int hiddev_ioctl_string(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg)
+{
+       struct hid_device *hid = hiddev->hid;
+       struct usb_device *dev = hid_to_usb_dev(hid);
+       int idx, len;
+       char *buf;
+
+       if (get_user(idx, (int __user *)user_arg))
+               return -EFAULT;
+
+       if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL)
+               return -ENOMEM;
+
+       if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) {
+               kfree(buf);
+               return -EINVAL;
+       }
+
+       if (copy_to_user(user_arg+sizeof(int), buf, len+1)) {
+               kfree(buf);
+               return -EFAULT;
+       }
+
+       kfree(buf);
+
+       return len;
+}
+
+static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        struct hiddev_list *list = file->private_data;
        struct hiddev *hiddev = list->hiddev;
@@ -402,15 +553,16 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
        struct hiddev_collection_info cinfo;
        struct hiddev_report_info rinfo;
        struct hiddev_field_info finfo;
-       struct hiddev_usage_ref_multi *uref_multi = NULL;
-       struct hiddev_usage_ref *uref;
        struct hiddev_devinfo dinfo;
        struct hid_report *report;
        struct hid_field *field;
        struct usbhid_device *usbhid = hid->driver_data;
        void __user *user_arg = (void __user *)arg;
        int i;
+       
+       /* Called without BKL by compat methods so no BKL taken */
 
+       /* FIXME: Who or what stop this racing with a disconnect ?? */
        if (!hiddev->exist)
                return -EIO;
 
@@ -470,30 +622,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
                }
 
        case HIDIOCGSTRING:
-               {
-                       int idx, len;
-                       char *buf;
-
-                       if (get_user(idx, (int __user *)arg))
-                               return -EFAULT;
-
-                       if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL)
-                               return -ENOMEM;
-
-                       if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) {
-                               kfree(buf);
-                               return -EINVAL;
-                       }
-
-                       if (copy_to_user(user_arg+sizeof(int), buf, len+1)) {
-                               kfree(buf);
-                               return -EFAULT;
-                       }
-
-                       kfree(buf);
-
-                       return len;
-               }
+               return hiddev_ioctl_string(hiddev, cmd, user_arg);
 
        case HIDIOCINITREPORT:
                usbhid_init_reports(hid);
@@ -578,121 +707,13 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
                return 0;
 
        case HIDIOCGUCODE:
-               uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
-               if (!uref_multi)
-                       return -ENOMEM;
-               uref = &uref_multi->uref;
-               if (copy_from_user(uref, user_arg, sizeof(*uref)))
-                       goto fault;
-
-               rinfo.report_type = uref->report_type;
-               rinfo.report_id = uref->report_id;
-               if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
-                       goto inval;
-
-               if (uref->field_index >= report->maxfield)
-                       goto inval;
-
-               field = report->field[uref->field_index];
-               if (uref->usage_index >= field->maxusage)
-                       goto inval;
-
-               uref->usage_code = field->usage[uref->usage_index].hid;
-
-               if (copy_to_user(user_arg, uref, sizeof(*uref)))
-                       goto fault;
-
-               kfree(uref_multi);
-               return 0;
-
+               /* fall through */
        case HIDIOCGUSAGE:
        case HIDIOCSUSAGE:
        case HIDIOCGUSAGES:
        case HIDIOCSUSAGES:
        case HIDIOCGCOLLECTIONINDEX:
-               uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
-               if (!uref_multi)
-                       return -ENOMEM;
-               uref = &uref_multi->uref;
-               if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
-                       if (copy_from_user(uref_multi, user_arg,
-                                          sizeof(*uref_multi)))
-                               goto fault;
-               } else {
-                       if (copy_from_user(uref, user_arg, sizeof(*uref)))
-                               goto fault;
-               }
-
-               if (cmd != HIDIOCGUSAGE &&
-                   cmd != HIDIOCGUSAGES &&
-                   uref->report_type == HID_REPORT_TYPE_INPUT)
-                       goto inval;
-
-               if (uref->report_id == HID_REPORT_ID_UNKNOWN) {
-                       field = hiddev_lookup_usage(hid, uref);
-                       if (field == NULL)
-                               goto inval;
-               } else {
-                       rinfo.report_type = uref->report_type;
-                       rinfo.report_id = uref->report_id;
-                       if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
-                               goto inval;
-
-                       if (uref->field_index >= report->maxfield)
-                               goto inval;
-
-                       field = report->field[uref->field_index];
-
-                       if (cmd == HIDIOCGCOLLECTIONINDEX) {
-                               if (uref->usage_index >= field->maxusage)
-                                       goto inval;
-                       } else if (uref->usage_index >= field->report_count)
-                               goto inval;
-
-                       else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) &&
-                                (uref_multi->num_values > HID_MAX_MULTI_USAGES ||
-                                 uref->usage_index + uref_multi->num_values > field->report_count))
-                               goto inval;
-                       }
-
-               switch (cmd) {
-                       case HIDIOCGUSAGE:
-                               uref->value = field->value[uref->usage_index];
-                               if (copy_to_user(user_arg, uref, sizeof(*uref)))
-                                       goto fault;
-                               goto goodreturn;
-
-                       case HIDIOCSUSAGE:
-                               field->value[uref->usage_index] = uref->value;
-                               goto goodreturn;
-
-                       case HIDIOCGCOLLECTIONINDEX:
-                               kfree(uref_multi);
-                               return field->usage[uref->usage_index].collection_index;
-                       case HIDIOCGUSAGES:
-                               for (i = 0; i < uref_multi->num_values; i++)
-                                       uref_multi->values[i] =
-                                           field->value[uref->usage_index + i];
-                               if (copy_to_user(user_arg, uref_multi,
-                                                sizeof(*uref_multi)))
-                                       goto fault;
-                               goto goodreturn;
-                       case HIDIOCSUSAGES:
-                               for (i = 0; i < uref_multi->num_values; i++)
-                                       field->value[uref->usage_index + i] =
-                                           uref_multi->values[i];
-                               goto goodreturn;
-               }
-
-goodreturn:
-               kfree(uref_multi);
-               return 0;
-fault:
-               kfree(uref_multi);
-               return -EFAULT;
-inval:
-               kfree(uref_multi);
-               return -EINVAL;
+               return hiddev_ioctl_usage(hiddev, cmd, user_arg);
 
        case HIDIOCGCOLLECTIONINFO:
                if (copy_from_user(&cinfo, user_arg, sizeof(cinfo)))
@@ -754,7 +775,7 @@ static const struct file_operations hiddev_fops = {
        .poll =         hiddev_poll,
        .open =         hiddev_open,
        .release =      hiddev_release,
-       .ioctl =        hiddev_ioctl,
+       .unlocked_ioctl =       hiddev_ioctl,
        .fasync =       hiddev_fasync,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = hiddev_compat_ioctl,