Commit 36a9bab9 authored by Paul Stewart's avatar Paul Stewart Committed by Vojtech Pavlik

USB Urefs for hid-core/hiddev

  I've written a patch Vojtech and I discussed for enhancing the
hiddev code to optionally provide more detailed output on read().
The old functionality is still supported by default, and in
situations where HID usage codes are unique across reports, the
old method is still preferable due to its terseness.
    
  The new method provides the ability to determine exactly which
value has changed, in cases where the HID usage codes are not  
unique.  It also provides a means to optionally receive notification
when input reports are received from the device, whether or not
any of the values in the report have changed.

  The details of the changes are as follows:
  
  - All current code behaves identically

  - A new ioctl pair HIDIOCGFLAG/HIDIOCSFLAG gets and clears
    flags on the hiddev device.                             

  - If you set the flag HIDDEV_FLAG_UREF, the read() call switches
    from reading hiddev_event structures to hiddev_usage_ref
    structures.  The change takes effect immediately, even to
    already queued events that haven't been read() yet.  Here's
    an example of enabling FLAG_UREF:                          

    {
        int flag = HIDDEV_FLAG_UREF;
        if (ioctl(fd, HIDIOCSFLAG, &flag) != 0) {
                perror("ioctl");
                exit(1);
        }
    }
  
  - With the HIDDEV_FLAG_REPORT set (which is only allowed if
    HIDDEV_FLAG_UREF is also set), there is a special uref that
    will be read() in addition to the ones corresponding to
    changes in the device state: when uref.field_index is set to
    HID_FIELD_INDEX_NONE, this uref is a notification that the
    report referred to by report_type and report_id has been
    received from the device.  This can be useful in situations
    when the notification of the arrival of a report is useful
    even if there is no change in state.
parent bb94ccec
...@@ -110,10 +110,11 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned ...@@ -110,10 +110,11 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage) memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
+ values * sizeof(unsigned)); + values * sizeof(unsigned));
report->field[report->maxfield++] = field; report->field[report->maxfield] = field;
field->usage = (struct hid_usage *)(field + 1); field->usage = (struct hid_usage *)(field + 1);
field->value = (unsigned *)(field->usage + usages); field->value = (unsigned *)(field->usage + usages);
field->report = report; field->report = report;
field->index = report->maxfield++;
return field; return field;
} }
...@@ -741,8 +742,20 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field, s ...@@ -741,8 +742,20 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field, s
if (hid->claimed & HID_CLAIMED_INPUT) if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_hid_event(hid, field, usage, value); hidinput_hid_event(hid, field, usage, value);
#ifdef CONFIG_USB_HIDDEV #ifdef CONFIG_USB_HIDDEV
if (hid->claimed & HID_CLAIMED_HIDDEV) if (hid->claimed & HID_CLAIMED_HIDDEV) {
hiddev_hid_event(hid, usage->hid, value); struct hiddev_usage_ref uref;
unsigned type = field->report_type;
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_hid_event(hid, &uref);
}
#endif #endif
} }
...@@ -839,6 +852,21 @@ static int hid_input_report(int type, struct urb *urb) ...@@ -839,6 +852,21 @@ static int hid_input_report(int type, struct urb *urb)
return -1; return -1;
} }
#ifdef CONFIG_USB_HIDDEV
/* Notify listeners that a report has been received */
if (hid->claimed & HID_CLAIMED_HIDDEV) {
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_hid_event(hid, &uref);
}
#endif
size = ((report->size - 1) >> 3) + 1; size = ((report->size - 1) >> 3) + 1;
if (len < size) { if (len < size) {
......
...@@ -276,6 +276,7 @@ struct hid_field { ...@@ -276,6 +276,7 @@ struct hid_field {
__s32 unit_exponent; __s32 unit_exponent;
unsigned unit; unsigned unit;
struct hid_report *report; /* associated report */ struct hid_report *report; /* associated report */
unsigned index; /* index into report->field[] */
}; };
#define HID_MAX_FIELDS 64 #define HID_MAX_FIELDS 64
......
...@@ -50,9 +50,10 @@ struct hiddev { ...@@ -50,9 +50,10 @@ struct hiddev {
}; };
struct hiddev_list { struct hiddev_list {
struct hiddev_event buffer[HIDDEV_BUFFER_SIZE]; struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE];
int head; int head;
int tail; int tail;
unsigned flags;
struct fasync_struct *fasync; struct fasync_struct *fasync;
struct hiddev *hiddev; struct hiddev *hiddev;
struct hiddev_list *next; struct hiddev_list *next;
...@@ -146,17 +147,19 @@ hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref) ...@@ -146,17 +147,19 @@ hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref)
* This is where hid.c calls into hiddev to pass an event that occurred over * This is where hid.c calls into hiddev to pass an event that occurred over
* the interrupt pipe * the interrupt pipe
*/ */
void hiddev_hid_event(struct hid_device *hid, unsigned int usage, int value) void hiddev_hid_event(struct hid_device *hid, struct hiddev_usage_ref *uref)
{ {
struct hiddev *hiddev = hid->hiddev; struct hiddev *hiddev = hid->hiddev;
struct hiddev_list *list = hiddev->list; struct hiddev_list *list = hiddev->list;
while (list) { while (list) {
list->buffer[list->head].hid = usage; if (uref->field_index != HID_FIELD_INDEX_NONE ||
list->buffer[list->head].value = value; (list->flags & HIDDEV_FLAG_REPORT) != 0) {
list->head = (list->head + 1) & (HIDDEV_BUFFER_SIZE - 1); list->buffer[list->head] = *uref;
list->head = (list->head + 1) &
kill_fasync(&list->fasync, SIGIO, POLL_IN); (HIDDEV_BUFFER_SIZE - 1);
kill_fasync(&list->fasync, SIGIO, POLL_IN);
}
list = list->next; list = list->next;
} }
...@@ -257,43 +260,67 @@ static ssize_t hiddev_read(struct file * file, char * buffer, size_t count, ...@@ -257,43 +260,67 @@ static ssize_t hiddev_read(struct file * file, char * buffer, size_t count,
{ {
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
struct hiddev_list *list = file->private_data; struct hiddev_list *list = file->private_data;
int event_size;
int retval = 0; int retval = 0;
if (list->head == list->tail) { event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ?
sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event);
add_wait_queue(&list->hiddev->wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
while (list->head == list->tail) { if (count < event_size) return 0;
if (file->f_flags & O_NONBLOCK) { while (retval == 0) {
retval = -EAGAIN; if (list->head == list->tail) {
break; add_wait_queue(&list->hiddev->wait, &wait);
} set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) {
retval = -ERESTARTSYS; while (list->head == list->tail) {
break; if (file->f_flags & O_NONBLOCK) {
} retval = -EAGAIN;
if (!list->hiddev->exist) { break;
retval = -EIO; }
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); if (retval)
remove_wait_queue(&list->hiddev->wait, &wait); return retval;
}
if (retval) while (list->head != list->tail &&
return retval; 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; return retval;
...@@ -358,6 +385,25 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, ...@@ -358,6 +385,25 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
return copy_to_user((void *) arg, &dinfo, sizeof(dinfo)); 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: case HIDIOCGSTRING:
{ {
int idx, len; int idx, len;
......
...@@ -119,6 +119,7 @@ struct hiddev_usage_ref { ...@@ -119,6 +119,7 @@ struct hiddev_usage_ref {
__s32 value; __s32 value;
}; };
#define HID_FIELD_INDEX_NONE 0xffffffff
/* /*
* Protocol version. * Protocol version.
...@@ -143,6 +144,15 @@ struct hiddev_usage_ref { ...@@ -143,6 +144,15 @@ struct hiddev_usage_ref {
#define HIDIOCGUSAGE _IOWR('H', 0x0B, struct hiddev_usage_ref) #define HIDIOCGUSAGE _IOWR('H', 0x0B, struct hiddev_usage_ref)
#define HIDIOCSUSAGE _IOW('H', 0x0C, struct hiddev_usage_ref) #define HIDIOCSUSAGE _IOW('H', 0x0C, struct hiddev_usage_ref)
#define HIDIOCGUCODE _IOWR('H', 0x0D, struct hiddev_usage_ref) #define HIDIOCGUCODE _IOWR('H', 0x0D, struct hiddev_usage_ref)
#define HIDIOCGFLAG _IOR('H', 0x0E, int)
#define HIDIOCSFLAG _IOW('H', 0x0F, int)
/*
* Flags to be used in HIDIOCSFLAG
*/
#define HIDDEV_FLAG_UREF 0x1
#define HIDDEV_FLAG_REPORT 0x2
#define HIDDEV_FLAGS 0x3
/* To traverse the input report descriptor info for a HID device, perform the /* To traverse the input report descriptor info for a HID device, perform the
* following: * following:
...@@ -179,7 +189,7 @@ struct hiddev_usage_ref { ...@@ -179,7 +189,7 @@ struct hiddev_usage_ref {
#ifdef CONFIG_USB_HIDDEV #ifdef CONFIG_USB_HIDDEV
int hiddev_connect(struct hid_device *); int hiddev_connect(struct hid_device *);
void hiddev_disconnect(struct hid_device *); void hiddev_disconnect(struct hid_device *);
void hiddev_hid_event(struct hid_device *, unsigned int usage, int value); void hiddev_hid_event(struct hid_device *, struct hiddev_usage_ref *ref);
int __init hiddev_init(void); int __init hiddev_init(void);
void __exit hiddev_exit(void); void __exit hiddev_exit(void);
#else #else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment