# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.525   -> 1.526  
#	   drivers/usb/hid.h	1.9     -> 1.10   
#	include/linux/hiddev.h	1.1     -> 1.2    
#	drivers/usb/hiddev.c	1.5     -> 1.6    
#	drivers/usb/hid-core.c	1.16    -> 1.17   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/03/17	stewart@inverse.wetlogic.net	1.526
# 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.
# --------------------------------------------
#
diff -Nru a/drivers/usb/hid-core.c b/drivers/usb/hid-core.c
--- a/drivers/usb/hid-core.c	Sun Mar 17 11:01:42 2002
+++ b/drivers/usb/hid-core.c	Sun Mar 17 11:01:42 2002
@@ -110,10 +110,11 @@
 	memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
 		+ values * sizeof(unsigned));
 
-	report->field[report->maxfield++] = field;
+	report->field[report->maxfield] = field;
 	field->usage = (struct hid_usage *)(field + 1);
 	field->value = (unsigned *)(field->usage + usages);
 	field->report = report;
+	field->index = report->maxfield++;
 
 	return field;
 }
@@ -741,8 +742,20 @@
 	if (hid->claimed & HID_CLAIMED_INPUT)
 		hidinput_hid_event(hid, field, usage, value);
 #ifdef CONFIG_USB_HIDDEV
-	if (hid->claimed & HID_CLAIMED_HIDDEV)
-		hiddev_hid_event(hid, usage->hid, value);
+	if (hid->claimed & HID_CLAIMED_HIDDEV) {
+		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
 }
 
@@ -838,6 +851,21 @@
 		dbg("undefined report_id %d received", n);
 		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;
 
diff -Nru a/drivers/usb/hid.h b/drivers/usb/hid.h
--- a/drivers/usb/hid.h	Sun Mar 17 11:01:42 2002
+++ b/drivers/usb/hid.h	Sun Mar 17 11:01:42 2002
@@ -276,6 +276,7 @@
 	__s32     unit_exponent;
 	unsigned  unit;
 	struct hid_report *report;	/* associated report */
+	unsigned index;			/* index into report->field[] */
 };
 
 #define HID_MAX_FIELDS 64
diff -Nru a/drivers/usb/hiddev.c b/drivers/usb/hiddev.c
--- a/drivers/usb/hiddev.c	Sun Mar 17 11:01:42 2002
+++ b/drivers/usb/hiddev.c	Sun Mar 17 11:01:42 2002
@@ -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;
@@ -146,17 +147,19 @@
  * 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)
+void hiddev_hid_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;
 	}
@@ -257,43 +260,67 @@
 {
 	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 + 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);
+		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);
+		}
+
 	}
 
 	return retval;
@@ -357,6 +384,25 @@
 		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:
 		{
diff -Nru a/include/linux/hiddev.h b/include/linux/hiddev.h
--- a/include/linux/hiddev.h	Sun Mar 17 11:01:42 2002
+++ b/include/linux/hiddev.h	Sun Mar 17 11:01:42 2002
@@ -119,6 +119,7 @@
 	__s32 value;
 };
 
+#define HID_FIELD_INDEX_NONE 0xffffffff
 
 /*
  * Protocol version.
@@ -143,6 +144,15 @@
 #define HIDIOCGUSAGE            _IOWR('H', 0x0B, struct hiddev_usage_ref)
 #define HIDIOCSUSAGE            _IOW('H', 0x0C, 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 
  * following:
@@ -179,7 +189,7 @@
 #ifdef CONFIG_USB_HIDDEV
 int hiddev_connect(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);
 void __exit hiddev_exit(void);
 #else
