patch-2.4.22 linux-2.4.22/drivers/usb/hiddev.c
Next file: linux-2.4.22/drivers/usb/host/Config.in
Previous file: linux-2.4.22/drivers/usb/hid.h
Back to the patch index
Back to the overall index
- Lines: 448
- Date:
2003-08-25 04:44:42.000000000 -0700
- Orig file:
linux-2.4.21/drivers/usb/hiddev.c
- Orig date:
2001-10-20 19:13:11.000000000 -0700
diff -urN linux-2.4.21/drivers/usb/hiddev.c linux-2.4.22/drivers/usb/hiddev.c
@@ -50,9 +50,10 @@
};
struct hiddev_list {
- struct hiddev_event buffer[HIDDEV_BUFFER_SIZE];
+ struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE];
int head;
int tail;
+ unsigned flags;
struct fasync_struct *fasync;
struct hiddev *hiddev;
struct hiddev_list *next;
@@ -70,7 +71,8 @@
static struct hid_report *
hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
{
- struct hid_report_enum *report_enum;
+ unsigned flags = rinfo->report_id & ~HID_REPORT_ID_MASK;
+ struct hid_report_enum *report_enum = NULL;
struct list_head *list;
if (rinfo->report_type < HID_REPORT_TYPE_MIN ||
@@ -78,27 +80,29 @@
report_enum = hid->report_enum +
(rinfo->report_type - HID_REPORT_TYPE_MIN);
- if ((rinfo->report_id & ~HID_REPORT_ID_MASK) != 0) {
- switch (rinfo->report_id & ~HID_REPORT_ID_MASK) {
- case HID_REPORT_ID_FIRST:
- list = report_enum->report_list.next;
- if (list == &report_enum->report_list) return NULL;
- rinfo->report_id = ((struct hid_report *) list)->id;
- break;
-
- case HID_REPORT_ID_NEXT:
- list = (struct list_head *)
- report_enum->report_id_hash[rinfo->report_id &
- HID_REPORT_ID_MASK];
- if (list == NULL) return NULL;
- list = list->next;
- if (list == &report_enum->report_list) return NULL;
- rinfo->report_id = ((struct hid_report *) list)->id;
- break;
-
- default:
- return NULL;
- }
+
+ switch (flags) {
+ case 0: /* Nothing to do -- report_id is already set correctly */
+ break;
+
+ case HID_REPORT_ID_FIRST:
+ list = report_enum->report_list.next;
+ if (list == &report_enum->report_list) return NULL;
+ rinfo->report_id = ((struct hid_report *) list)->id;
+ break;
+
+ case HID_REPORT_ID_NEXT:
+ list = (struct list_head *)
+ report_enum->report_id_hash[rinfo->report_id &
+ HID_REPORT_ID_MASK];
+ if (list == NULL) return NULL;
+ list = list->next;
+ if (list == &report_enum->report_list) return NULL;
+ rinfo->report_id = ((struct hid_report *) list)->id;
+ break;
+
+ default:
+ return NULL;
}
return report_enum->report_id_hash[rinfo->report_id];
@@ -142,21 +146,20 @@
return NULL;
}
-/*
- * This is where hid.c calls into hiddev to pass an event that occurred over
- * the interrupt pipe
- */
-void hiddev_hid_event(struct hid_device *hid, unsigned int usage, int value)
+static void hiddev_send_event(struct hid_device *hid,
+ struct hiddev_usage_ref *uref)
{
struct hiddev *hiddev = hid->hiddev;
struct hiddev_list *list = hiddev->list;
while (list) {
- list->buffer[list->head].hid = usage;
- list->buffer[list->head].value = value;
- list->head = (list->head + 1) & (HIDDEV_BUFFER_SIZE - 1);
-
- kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ if (uref->field_index != HID_FIELD_INDEX_NONE ||
+ (list->flags & HIDDEV_FLAG_REPORT) != 0) {
+ list->buffer[list->head] = *uref;
+ list->head = (list->head + 1) &
+ (HIDDEV_BUFFER_SIZE - 1);
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ }
list = list->next;
}
@@ -165,6 +168,46 @@
}
/*
+ * This is where hid.c calls into hiddev to pass an event that occurred over
+ * the interrupt pipe
+ */
+void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ unsigned type = field->report_type;
+ struct hiddev_usage_ref uref;
+
+ uref.report_type =
+ (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
+ ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
+ ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0));
+ uref.report_id = field->report->id;
+ uref.field_index = field->index;
+ uref.usage_index = (usage - field->usage);
+ uref.usage_code = usage->hid;
+ uref.value = value;
+
+ hiddev_send_event(hid, &uref);
+}
+
+
+void hiddev_report_event(struct hid_device *hid, struct hid_report *report)
+{
+ unsigned type = report->type;
+ struct hiddev_usage_ref uref;
+
+ memset(&uref, 0, sizeof(uref));
+ uref.report_type =
+ (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
+ ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
+ ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0));
+ uref.report_id = report->id;
+ uref.field_index = HID_FIELD_INDEX_NONE;
+
+ hiddev_send_event(hid, &uref);
+}
+
+/*
* fasync file op
*/
static int hiddev_fasync(int fd, struct file *file, int on)
@@ -193,7 +236,6 @@
struct hiddev_list *list = file->private_data;
struct hiddev_list **listptr;
- lock_kernel();
listptr = &list->hiddev->list;
hiddev_fasync(-1, file, 0);
@@ -209,7 +251,6 @@
}
kfree(list);
- unlock_kernel();
return 0;
}
@@ -259,43 +300,66 @@
{
DECLARE_WAITQUEUE(wait, current);
struct hiddev_list *list = file->private_data;
+ int event_size;
int retval = 0;
- if (list->head == list->tail) {
-
- add_wait_queue(&list->hiddev->wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
+ event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ?
+ sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event);
- while (list->head == list->tail) {
+ if (count < event_size) return 0;
- if (file->f_flags & O_NONBLOCK) {
- retval = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
- if (!list->hiddev->exist) {
- retval = -EIO;
- break;
+ while (retval == 0) {
+ if (list->head == list->tail) {
+ add_wait_queue(&list->hiddev->wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ while (list->head == list->tail) {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ if (!list->hiddev->exist) {
+ retval = -EIO;
+ break;
+ }
+
+ schedule();
}
- schedule();
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&list->hiddev->wait, &wait);
}
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&list->hiddev->wait, &wait);
- }
+ if (retval)
+ return retval;
- if (retval)
- return retval;
+ while (list->head != list->tail &&
+ retval + event_size <= count) {
+ if ((list->flags & HIDDEV_FLAG_UREF) == 0) {
+ if (list->buffer[list->tail].field_index !=
+ HID_FIELD_INDEX_NONE) {
+ struct hiddev_event event;
+ event.hid = list->buffer[list->tail].usage_code;
+ event.value = list->buffer[list->tail].value;
+ if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event)))
+ return -EFAULT;
+ retval += sizeof(struct hiddev_event);
+ }
+ } else {
+ if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE ||
+ (list->flags & HIDDEV_FLAG_REPORT) != 0) {
+ if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref)))
+ return -EFAULT;
+ retval += sizeof(struct hiddev_usage_ref);
+ }
+ }
+ list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1);
+ }
- while (list->head != list->tail && retval + sizeof(struct hiddev_event) <= count) {
- if (copy_to_user(buffer + retval, list->buffer + list->tail,
- sizeof(struct hiddev_event))) return -EFAULT;
- list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1);
- retval += sizeof(struct hiddev_event);
}
return retval;
@@ -329,10 +393,14 @@
struct hiddev *hiddev = list->hiddev;
struct hid_device *hid = hiddev->hid;
struct usb_device *dev = hid->dev;
+ struct hiddev_collection_info cinfo;
struct hiddev_report_info rinfo;
+ struct hiddev_field_info finfo;
struct hiddev_usage_ref uref;
+ struct hiddev_devinfo dinfo;
struct hid_report *report;
struct hid_field *field;
+ int i;
if (!hiddev->exist) return -EIO;
@@ -344,11 +412,18 @@
case HIDIOCAPPLICATION:
if (arg < 0 || arg >= hid->maxapplication)
return -EINVAL;
- return hid->application[arg];
+
+ for (i = 0; i < hid->maxcollection; i++)
+ if (hid->collection[i].type ==
+ HID_COLLECTION_APPLICATION && arg-- == 0)
+ break;
+
+ if (i == hid->maxcollection)
+ return -EINVAL;
+
+ return hid->collection[i].usage;
case HIDIOCGDEVINFO:
- {
- struct hiddev_devinfo dinfo;
dinfo.bustype = BUS_USB;
dinfo.busnum = dev->bus->busnum;
dinfo.devnum = dev->devnum;
@@ -358,7 +433,25 @@
dinfo.version = dev->descriptor.bcdDevice;
dinfo.num_applications = hid->maxapplication;
return copy_to_user((void *) arg, &dinfo, sizeof(dinfo));
- }
+
+ case HIDIOCGFLAG:
+ return put_user(list->flags, (int *) arg);
+
+ case HIDIOCSFLAG:
+ {
+ int newflags;
+ if (get_user(newflags, (int *) arg))
+ return -EFAULT;
+
+ if ((newflags & ~HIDDEV_FLAGS) != 0 ||
+ ((newflags & HIDDEV_FLAG_REPORT) != 0 &&
+ (newflags & HIDDEV_FLAG_UREF) == 0))
+ return -EINVAL;
+
+ list->flags = newflags;
+
+ return 0;
+ }
case HIDIOCGSTRING:
{
@@ -387,7 +480,6 @@
}
case HIDIOCINITREPORT:
-
hid_init_reports(hid);
return 0;
@@ -432,8 +524,6 @@
return copy_to_user((void *) arg, &rinfo, sizeof(rinfo));
case HIDIOCGFIELDINFO:
- {
- struct hiddev_field_info finfo;
if (copy_from_user(&finfo, (void *) arg, sizeof(finfo)))
return -EFAULT;
rinfo.report_type = finfo.report_type;
@@ -462,7 +552,6 @@
finfo.unit = field->unit;
return copy_to_user((void *) arg, &finfo, sizeof(finfo));
- }
case HIDIOCGUCODE:
if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
@@ -485,9 +574,15 @@
return copy_to_user((void *) arg, &uref, sizeof(uref));
case HIDIOCGUSAGE:
+ case HIDIOCSUSAGE:
+ case HIDIOCGCOLLECTIONINDEX:
if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
return -EFAULT;
+ if (cmd == HIDIOCSUSAGE &&
+ uref.report_type == HID_REPORT_TYPE_INPUT)
+ return -EINVAL;
+
if (uref.report_id == HID_REPORT_ID_UNKNOWN) {
field = hiddev_lookup_usage(hid, &uref);
if (field == NULL)
@@ -506,38 +601,32 @@
return -EINVAL;
}
- uref.value = field->value[uref.usage_index];
+ switch (cmd) {
+ case HIDIOCGUSAGE:
+ uref.value = field->value[uref.usage_index];
+ return copy_to_user((void *) arg, &uref, sizeof(uref));
+
+ case HIDIOCSUSAGE:
+ field->value[uref.usage_index] = uref.value;
+ return 0;
- return copy_to_user((void *) arg, &uref, sizeof(uref));
+ case HIDIOCGCOLLECTIONINDEX:
+ return field->usage[uref.usage_index].collection_index;
+ }
+ break;
- case HIDIOCSUSAGE:
- if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
+ case HIDIOCGCOLLECTIONINFO:
+ if (copy_from_user(&cinfo, (void *) arg, sizeof(cinfo)))
return -EFAULT;
- if (uref.report_type == HID_REPORT_TYPE_INPUT)
+ if (cinfo.index >= hid->maxcollection)
return -EINVAL;
- if (uref.report_id == HID_REPORT_ID_UNKNOWN) {
- field = hiddev_lookup_usage(hid, &uref);
- if (field == NULL)
- return -EINVAL;
- } else {
- rinfo.report_type = uref.report_type;
- rinfo.report_id = uref.report_id;
- if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
- return -EINVAL;
-
- if (uref.field_index >= report->maxfield)
- return -EINVAL;
-
- field = report->field[uref.field_index];
- if (uref.usage_index >= field->maxusage)
- return -EINVAL;
- }
-
- field->value[uref.usage_index] = uref.value;
+ cinfo.type = hid->collection[cinfo.index].type;
+ cinfo.usage = hid->collection[cinfo.index].usage;
+ cinfo.level = hid->collection[cinfo.index].level;
- return 0;
+ return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
default:
@@ -576,12 +665,18 @@
int minor, i;
char devfs_name[16];
- for (i = 0; i < hid->maxapplication; i++)
- if (!IS_INPUT_APPLICATION(hid->application[i]))
- break;
- if (i == hid->maxapplication)
- return -1;
+
+ if ((hid->quirks & HID_QUIRK_HIDDEV) == 0) {
+ for (i = 0; i < hid->maxcollection; i++)
+ if (hid->collection[i].type ==
+ HID_COLLECTION_APPLICATION &&
+ !IS_INPUT_APPLICATION(hid->collection[i].usage))
+ break;
+
+ if (i == hid->maxcollection)
+ return -1;
+ }
for (minor = 0; minor < HIDDEV_MINORS && hiddev_table[minor]; minor++);
if (minor == HIDDEV_MINORS) {
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)