diff -Nur c3000_pre/linux/drivers/usb/device/Config.in c3000_p26_w3/linux/drivers/usb/device/Config.in
--- c3000_pre/linux/drivers/usb/device/Config.in	2004-08-21 09:48:57.000000000 +0900
+++ c3000_p26_w3/linux/drivers/usb/device/Config.in	2006-09-11 14:09:56.000000000 +0900
@@ -35,6 +35,7 @@
    source drivers/usb/device/net_fd/Config.in
    source drivers/usb/device/serial_fd/Config.in
    source drivers/usb/device/storage_fd/Config.in
+   source drivers/usb/device/hid_fd/Config.in
 	
    comment 'USB Device bus interfaces'
    source drivers/usb/device/bi/Config.in
diff -Nur c3000_pre/linux/drivers/usb/device/Makefile c3000_p26_w3/linux/drivers/usb/device/Makefile
--- c3000_pre/linux/drivers/usb/device/Makefile	2004-08-21 09:48:57.000000000 +0900
+++ c3000_p26_w3/linux/drivers/usb/device/Makefile	2006-09-11 14:09:32.000000000 +0900
@@ -21,6 +21,7 @@
 subdir-$(CONFIG_USBD_NET) += net_fd
 subdir-$(CONFIG_USBD_SERIAL) += serial_fd
 subdir-$(CONFIG_USBD_STORAGE) += storage_fd
+subdir-$(CONFIG_USBD_HID) += hid_fd
 
 #subdir-$(CONFIG_USBD_GENERIC_BUS) += gen_bi
 #subdir-$(CONFIG_USBD_L7205_BUS) += l7205_bi
diff -Nur c3000_pre/linux/drivers/usb/device/bi/pxa27x.c c3000_p26_w3/linux/drivers/usb/device/bi/pxa27x.c
--- c3000_pre/linux/drivers/usb/device/bi/pxa27x.c	2004-09-11 13:02:35.000000000 +0900
+++ c3000_p26_w3/linux/drivers/usb/device/bi/pxa27x.c	2006-09-11 10:06:46.000000000 +0900
@@ -867,6 +867,8 @@
 
 void __inline__ pxa_ep0setup(struct usb_endpoint_instance *endpoint, volatile u32 udccs0)
 {
+	static int ctrl_write_f = 0;
+
         if ((udccs0 & (UDCCSR0_SA | UDCCSR0_OPC | UDCCSR0_RNE)) == (UDCCSR0_SA | UDCCSR0_OPC | UDCCSR0_RNE)) {
 
                 int len = 0;
@@ -884,6 +886,22 @@
                 }
                 PXA_SETUP(&ep0_urb->device_request);
 
+                if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) 
+                {
+                        // Control Write - we are receiving more data from the host
+                        // should we setup to receive data
+					    if (le16_to_cpu (ep0_urb->device_request.wLength)) {
+							//printk(KERN_INFO"sl11_ep0: read data %d\n",
+                            //          le16_to_cpu(ep0_urb->device_request.wLength));
+							//printk("not use recv_setup");
+                                PXA_REGS(UDCCSR0,"      <-- setup no response");
+								ctrl_write_f = 1;
+                                return;
+                        }
+
+				}
+				ctrl_write_f = 0;
+
                 // process setup packet
                 if (usbd_recv_setup(ep0_urb)) {
                         // setup processing failed 
@@ -942,15 +960,51 @@
                         PXA_REGS(UDCCSR0,"      <-- setup data");
                 }
 
-        }
+        }else{
 
+			if ((udccs0 & (UDCCSR0_OPC | UDCCSR0_RNE)) == (UDCCSR0_OPC | UDCCSR0_RNE) && (ctrl_write_f == 1)) {
+				int c;
+				int max = 16;
+				unsigned char *cp = ep0_urb->buffer;
+				
+				ctrl_write_f = 0;
+				
+				while (UDCCSRN(0) & UDCCSR0_RNE) {
+					c = UDCDRN(0);
+					if(max > 0){
+						
+						*cp++ = c & 0xff;
+						*cp++ = (c >> 8) & 0xff;
+						*cp++ = (c >> 16) & 0xff;
+						*cp++ = (c >> 24) & 0xff;
+						
+						max -= 4;
+						//printk(" %x %p:",c,cp);
+					}
+				}
+				//printk(" recv_setup\n");
+				
+				PXA_SETUP(&ep0_urb->device_request);
+				
+				// process setup packet
+				if (usbd_recv_setup(ep0_urb)) {
+					// setup processing failed 
+					UDCCSR0 = UDCCSR0_FST;
+					endpoint->state = WAIT_FOR_SETUP;
+					PXA_REGS(udccs0, "      --> bad setup FST");
+					UDCCSR0 = UDCCSR0_IPR;
+					return;
+				}
+				
+				UDCCSR0 = UDCCSR0_IPR;
+			}
+		}		
 }
-
+		
 
 static __inline__ void pxa_ep0(struct usb_endpoint_instance *endpoint, volatile u32 udccs0)
 {
         int j = 0;
-
         PXA_REGS(udccs0,"  --> ep0");
 
         if (udccs0 & UDCCSR0_SST) {
diff -Nur c3000_pre/linux/drivers/usb/device/bi/usbd-bi.c c3000_p26_w3/linux/drivers/usb/device/bi/usbd-bi.c
--- c3000_pre/linux/drivers/usb/device/bi/usbd-bi.c	2004-08-21 09:48:57.000000000 +0900
+++ c3000_p26_w3/linux/drivers/usb/device/bi/usbd-bi.c	2006-09-11 10:06:46.000000000 +0900
@@ -235,106 +235,153 @@
 static int bi_config (struct usb_device_instance *device)
 {
 	int i;
-
+	int bNumInterface;
+	int bNumEndpoint;
+	int bAlternateSetting;
+	int epnum = 1;
+	
 	struct usb_interface_descriptor *interface;
 	struct usb_endpoint_descriptor *endpoint_descriptor;
 
 	int found_tx = 0;
 	int found_rx = 0;
-
-	dbg_init (1, "checking config: config: %d interface: %d alternate: %d",
-		  device->configuration, device->interface, device->alternate);
-
-	bi_disable_endpoints (device);
-
-	// audit configuration for compatibility
-	if (!(interface = usbd_device_interface_descriptor (device,
-                        0, device->configuration, device->interface, device->alternate))) 
-        {
-		dbg_init (0, "cannot fetch interface descriptor c:%d i:%d a:%d",
-			  device->configuration, device->interface, device->alternate);
+	int port = 0;
+	
+	
+	struct usb_configuration_descriptor *configuration_descriptor;
+	struct usb_device_descriptor *device_descriptor;
+	int index = device->configuration;
+	
+	if (!(device_descriptor = usbd_device_device_descriptor (device, port))) {
 		return -EINVAL;
 	}
-
-
-	dbg_init (2, "---> endpoints: %d", interface->bNumEndpoints);
-
-	// iterate across all endpoints for this configuration and verify they are valid 
-	for (i = 0; i < interface->bNumEndpoints; i++) {
-		int transfersize;
-
-		int physical_endpoint;
-		int logical_endpoint;
-
-		//dbg_init(0, "fetching endpoint %d", i);
-                if (!(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, 0,
-                                device-> configuration, device-> interface, device-> alternate, i))) 
-                {
-			dbg_init (0, "cannot fetch endpoint descriptor: %d", i);
-			continue;
+	//dbg_ep0(2, "%d %d", index, device_descriptor->bNumConfigurations);
+	if (index > device_descriptor->bNumConfigurations) {
+		dbg_ep0 (0, "index too large: %d > %d", index, device_descriptor->bNumConfigurations);
+		return -EINVAL;
+	}
+	
+	if (!(configuration_descriptor =
+				usbd_device_configuration_descriptor (device, port, index))) {
+		dbg_ep0 (0, "usbd_device_configuration_descriptor failed: %d", index);
+		return -EINVAL;
+	}
+	bNumInterface = configuration_descriptor->bNumInterfaces;
+	
+	for(bNumInterface = 0; bNumInterface < configuration_descriptor->bNumInterfaces; bNumInterface++){
+		
+		struct usb_interface_instance *interface_instance;
+		
+		//dbg_ep0(3, "[%d] bNumInterfaces: %d", bNumInterface, configuration_descriptor->bNumInterfaces);
+		
+		if (!(interface_instance =
+					usbd_device_interface_instance (device, port, index, bNumInterface))) {
+			dbg_ep0 (3, "[%d] interface_instance NULL", bNumInterface);
+			return -EINVAL;
 		}
-		// XXX check this
-                transfersize = usbd_device_endpoint_transfersize (device, 0,
-                        device->configuration, device->interface, device->alternate, i); 
-
-
-                logical_endpoint = endpoint_descriptor->bEndpointAddress;
-
-		if (!
-		    (physical_endpoint =
-		     udc_check_ep (logical_endpoint, endpoint_descriptor->wMaxPacketSize))) {
-			dbg_init (2,
-				  "endpoint[%d]: l: %02x p: %02x transferSize: %d packetSize: %02x INVALID",
-				  i, logical_endpoint, physical_endpoint, transfersize,
-				  endpoint_descriptor->wMaxPacketSize);
+		// iterate across interface alternates
+		for (bAlternateSetting = 0;
+				 bAlternateSetting < interface_instance->alternates;
+				 bAlternateSetting++) {
+			
+			
+			dbg_init (1, "checking config: config: %d interface: %d alternate: %d",
+								device->configuration, bNumInterface, bAlternateSetting);
+			
+			bi_disable_endpoints (device);
+			
+			// audit configuration for compatibility
+			if (!(interface = usbd_device_interface_descriptor (device,
+						0, device->configuration, bNumInterface, bAlternateSetting))) 
+        {
+					dbg_init (0, "cannot fetch interface descriptor c:%d i:%d a:%d",
+										device->configuration, bNumInterface, bAlternateSetting);
+					return -EINVAL;
+				}
+			
+			
+			dbg_init (2, "---> endpoints: %d", interface->bNumEndpoints);
+			
+			// iterate across all endpoints for this configuration and verify they are valid 
+			for (bNumEndpoint = 0; bNumEndpoint < interface->bNumEndpoints; bNumEndpoint++) {
+				int transfersize;
+				
+				int physical_endpoint;
+				int logical_endpoint;
+				
+				//dbg_init(0, "fetching endpoint %d", bNumEndpoint);
+				if (!(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, 0,
+							device-> configuration, bNumInterface, bAlternateSetting, bNumEndpoint))) 
+					{
+						dbg_init (0, "cannot fetch endpoint descriptor: %d", bNumEndpoint);
+						continue;
+					}
+				// XXX check this
+				transfersize = usbd_device_endpoint_transfersize (device, 0,
+										   device->configuration, bNumInterface, bAlternateSetting, bNumEndpoint); 
+				
+				
+				logical_endpoint = endpoint_descriptor->bEndpointAddress;
+				
+				if (! (physical_endpoint =
+							 udc_check_ep (logical_endpoint, endpoint_descriptor->wMaxPacketSize))) {
+					dbg_init (2,
+										"endpoint[%d]: l: %02x p: %02x transferSize: %d packetSize: %02x INVALID",
+										epnum, logical_endpoint, physical_endpoint, transfersize,
+										endpoint_descriptor->wMaxPacketSize);
+					
+					dbg_init (0, "invalid endpoint: %d %d", logical_endpoint, physical_endpoint);
+					return -EINVAL;
+				} else {
+					struct usb_endpoint_instance *endpoint =
+						device->bus->endpoint_array + physical_endpoint;
+					
+					dbg_init (2, "endpoint[%d]: l: %02x p: %02x transferSize: %d packetSize: %02x FOUND",
+										epnum, logical_endpoint, physical_endpoint, transfersize,
+										endpoint_descriptor->wMaxPacketSize);
+					
+					dbg_init (2, "epd->bEndpointAddress=%02x", endpoint_descriptor->bEndpointAddress);
+					endpoint->endpoint_address = endpoint_descriptor->bEndpointAddress;
+					
+					if (endpoint_descriptor->wMaxPacketSize > 64) {
+						dbg_init (0, "incompatible with endpoint size: %x", endpoint_descriptor->wMaxPacketSize);
+						return -EINVAL;
+					}
+					
+					if (endpoint_descriptor->bEndpointAddress & IN) {
+						found_tx++;
+						endpoint->tx_attributes = endpoint_descriptor->bmAttributes;
+						endpoint->tx_transferSize = transfersize & 0xfff;
+						endpoint->tx_packetSize = endpoint_descriptor->wMaxPacketSize;
+						endpoint->last = 0;
+						if (endpoint->tx_urb) {
+							dbg_init (1, "CLEARING tx_urb: %p", endpoint->tx_urb);
+							usbd_dealloc_urb (endpoint->tx_urb);
+							endpoint->tx_urb = NULL;
+						}
+					} else {
+						found_rx++;
+						endpoint->rcv_attributes = endpoint_descriptor->bmAttributes;
+						endpoint->rcv_transferSize = transfersize & 0xfff;
+						endpoint->rcv_packetSize = endpoint_descriptor->wMaxPacketSize;
+						if (endpoint->rcv_urb) {
+							dbg_init (1, "CLEARING rcv_urb: %p", endpoint->tx_urb);
+							usbd_dealloc_urb (endpoint->rcv_urb);
+							endpoint->rcv_urb = NULL;
+						}
+					}
+					epnum++;
+				}
 
-			dbg_init (0, "invalid endpoint: %d %d", logical_endpoint, physical_endpoint);
-			return -EINVAL;
-		} 
-                else {
-			struct usb_endpoint_instance *endpoint =
-			    device->bus->endpoint_array + physical_endpoint;
-
-			dbg_init (2, "endpoint[%d]: l: %02x p: %02x transferSize: %d packetSize: %02x FOUND",
-				  i, logical_endpoint, physical_endpoint, transfersize,
-				  endpoint_descriptor->wMaxPacketSize);
-
-			dbg_init (2, "epd->bEndpointAddress=%02x", endpoint_descriptor->bEndpointAddress);
-                        endpoint->endpoint_address = endpoint_descriptor->bEndpointAddress;
-
-			if (endpoint_descriptor->wMaxPacketSize > 64) {
-				dbg_init (0, "incompatible with endpoint size: %x", endpoint_descriptor->wMaxPacketSize);
-				return -EINVAL;
-			}
+			}// endpoint
 
-			if (endpoint_descriptor->bEndpointAddress & IN) {
-				found_tx++;
-				endpoint->tx_attributes = endpoint_descriptor->bmAttributes;
-				endpoint->tx_transferSize = transfersize & 0xfff;
-				endpoint->tx_packetSize = endpoint_descriptor->wMaxPacketSize;
-				endpoint->last = 0;
-                                if (endpoint->tx_urb) {
-                                        dbg_init (1, "CLEARING tx_urb: %p", endpoint->tx_urb);
-                                        usbd_dealloc_urb (endpoint->tx_urb);
-                                        endpoint->tx_urb = NULL;
-                                }
-			} else {
-				found_rx++;
-				endpoint->rcv_attributes = endpoint_descriptor->bmAttributes;
-				endpoint->rcv_transferSize = transfersize & 0xfff;
-				endpoint->rcv_packetSize = endpoint_descriptor->wMaxPacketSize;
-                                if (endpoint->rcv_urb) {
-                                        dbg_init (1, "CLEARING rcv_urb: %p", endpoint->tx_urb);
-                                        usbd_dealloc_urb (endpoint->rcv_urb);
-                                        endpoint->rcv_urb = NULL;
-                                }
-			}
-		}
-	}
+		}// alternate
 
+	}// interface
+	
 	// iterate across all endpoints and enable them
-
-        dbg_init(1, "---> device->status: %d", device->status);
+	
+	dbg_init(1, "---> device->status: %d", device->status);
 
 	if (device->status == USBD_OK) {
                 dbg_init (1, "enabling endpoints");
diff -Nur c3000_pre/linux/drivers/usb/device/ep0.c c3000_p26_w3/linux/drivers/usb/device/ep0.c
--- c3000_pre/linux/drivers/usb/device/ep0.c	2004-08-21 09:48:57.000000000 +0900
+++ c3000_p26_w3/linux/drivers/usb/device/ep0.c	2006-09-11 10:06:46.000000000 +0900
@@ -206,6 +206,50 @@
 	//        urb->actual_length, urb->buffer_length, max_buf, max_length, available);
 }
 
+static void copy_config2 (struct urb *urb, void *data, int max_length, int max_buf)
+{
+	int available;
+	int length;
+
+	dbg_ep0(3, "-> actual: %d buf: %d max_buf: %d max_length: %d data: %p", 
+	        urb->actual_length, urb->buffer_length, max_buf, max_length, data);
+
+	if (!data) {
+		dbg_ep0 (1, "data is NULL");
+		return;
+	}
+	if (!(length = max_length)) {
+		dbg_ep0 (1, "length is zero");
+		return;
+	}
+
+	if (length > max_length) {
+		dbg_ep0 (1, "length: %d >= max_length: %d", length, max_length);
+		return;
+	}
+	//	dbg_ep0(1, "   actual: %d buf: %d max_buf: %d max_length: %d length: %d", 
+	//	        urb->actual_length, urb->buffer_length, max_buf, max_length, length);
+
+	if ((available = /*urb->buffer_length */ max_buf - urb->actual_length) <= 0) {
+		return;
+	}
+	//	dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", 
+	//	        urb->actual_length, urb->buffer_length, max_buf, length, available);
+
+	if (length > available) {
+		length = available;
+	}
+	//	dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", 
+	//	        urb->actual_length, urb->buffer_length, max_buf, length, available);
+
+	memcpy (urb->buffer + urb->actual_length, data, length);
+	urb->actual_length += length;
+
+	//	dbg_ep0(3, "<- actual: %d buf: %d max_buf: %d max_length: %d available: %d", 
+	//	        urb->actual_length, urb->buffer_length, max_buf, max_length, available);
+}
+
+
 /**
  * ep0_get_descriptor
  * @device:
@@ -223,11 +267,20 @@
 {
 	int port = 0;		// XXX compound device
 	char *cp;
+	int i;
+	struct usb_device_request *request;
+	int wIndex;
+
+
+	dbg_ep0(3, "max: %x type: %x index: %x", max, descriptor_type, index);
 
-	//dbg_ep0(3, "max: %x type: %x index: %x", max, descriptor_type, index);
 
+	request = &urb->device_request;
+	wIndex = le16_to_cpu (request->wIndex); 
+
+	dbg_ep0 (1, "urb %p buffer %p buffer_len %x \n",urb,urb->buffer, urb->buffer_length);
 	if (!urb || !urb->buffer || !urb->buffer_length || (urb->buffer_length < 255)) {
-		dbg_ep0 (2, "invalid urb %p", urb);
+		dbg_ep0 (2, "invalid urb %p---------------------------------------------------------------------------------", urb);
 		return -EINVAL;
 	}
 
@@ -395,6 +448,24 @@
 		return -EINVAL;
 	case USB_DESCRIPTOR_TYPE_ENDPOINT:
 		return -EINVAL;
+	case USB_DESCRIPTOR_TYPE_HIDREPORT:
+		{
+			struct usb_class_descriptor *class_descriptor;
+			//dbg_ep0(3, "[%d:%d:%d] classes: %d", bNumInterface, bAlternateSetting, 
+			//        class, alternate_instance->classes);
+			if (!(class_descriptor =
+						usbd_device_class_descriptor_index (device, port, index, wIndex, 0, 0))) {
+				dbg_ep0 (3, "HID class NULL");
+				return -EINVAL;
+			}
+			// copy descriptor for this class
+			copy_config2(urb, class_descriptor->descriptor.hid_function.iReportDescriptor,
+						 class_descriptor->descriptor.hid_function.bReportDescriptorLen, max);
+
+		}
+
+		break;
+
 	default:
 		return -EINVAL;
 	}
diff -Nur c3000_pre/linux/drivers/usb/device/hid_fd/Config.help c3000_p26_w3/linux/drivers/usb/device/hid_fd/Config.help
--- c3000_pre/linux/drivers/usb/device/hid_fd/Config.help	1970-01-01 09:00:00.000000000 +0900
+++ c3000_p26_w3/linux/drivers/usb/device/hid_fd/Config.help	2006-09-11 14:05:36.000000000 +0900
@@ -0,0 +1,3 @@
+CONFIG_USBD_HID
+  Enable the HID function driver. 
+
diff -Nur c3000_pre/linux/drivers/usb/device/hid_fd/Config.in c3000_p26_w3/linux/drivers/usb/device/hid_fd/Config.in
--- c3000_pre/linux/drivers/usb/device/hid_fd/Config.in	1970-01-01 09:00:00.000000000 +0900
+++ c3000_p26_w3/linux/drivers/usb/device/hid_fd/Config.in	2006-09-11 14:06:33.000000000 +0900
@@ -0,0 +1,10 @@
+#
+#  HID Function Driver
+#
+
+mainmenu_option next_comment
+comment "HID Function"
+
+dep_tristate '  HID Function Driver' CONFIG_USBD_HID $CONFIG_USBD
+
+endmenu
diff -Nur c3000_pre/linux/drivers/usb/device/hid_fd/Makefile c3000_p26_w3/linux/drivers/usb/device/hid_fd/Makefile
--- c3000_pre/linux/drivers/usb/device/hid_fd/Makefile	1970-01-01 09:00:00.000000000 +0900
+++ c3000_p26_w3/linux/drivers/usb/device/hid_fd/Makefile	2006-09-11 14:04:44.000000000 +0900
@@ -0,0 +1,56 @@
+#
+# Function driver for a HID USB Device
+#
+
+
+O_TARGET	:= hid_fd_drv.o
+list-multi	:= hid_fd.o
+
+hid_fd-objs	:= hid-fd.o 
+
+# Object file lists.
+
+obj-y		:=
+obj-m		:=
+obj-n		:=
+obj-		:=
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_USBD_HID)   += hid_fd.o
+
+# Extract lists of the multi-part drivers.
+# The 'int-*' lists are the intermediate files used to build the multi's.
+
+multi-y		:= $(filter $(list-multi), $(obj-y))
+multi-m		:= $(filter $(list-multi), $(obj-m))
+int-y		:= $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
+int-m		:= $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
+
+# Files that are both resident and modular: remove from modular.
+
+obj-m		:= $(filter-out $(obj-y), $(obj-m))
+int-m		:= $(filter-out $(int-y), $(int-m))
+
+# Translate to Rules.make lists.
+
+O_OBJS		:= $(filter-out $(export-objs), $(obj-y))
+OX_OBJS		:= $(filter     $(export-objs), $(obj-y))
+M_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-m)))
+MI_OBJS		:= $(sort $(filter-out $(export-objs), $(int-m)))
+MIX_OBJS	:= $(sort $(filter     $(export-objs), $(int-m)))
+
+# The global Rules.make.
+
+include $(TOPDIR)/Rules.make
+
+# Link rules for multi-part drivers.
+
+hid_fd.o: $(hid_fd-objs)
+	$(LD) -r -o $@ $(hid_fd-objs)
+
+# dependencies:
+
+hid-fd.o: ../usbd.h ../usbd-bus.h ../usbd-func.h
+
diff -Nur c3000_pre/linux/drivers/usb/device/hid_fd/hid-fd.c c3000_p26_w3/linux/drivers/usb/device/hid_fd/hid-fd.c
--- c3000_pre/linux/drivers/usb/device/hid_fd/hid-fd.c	1970-01-01 09:00:00.000000000 +0900
+++ c3000_p26_w3/linux/drivers/usb/device/hid_fd/hid-fd.c	2006-09-19 21:49:04.000000000 +0900
@@ -0,0 +1,1175 @@
+/*
+ * linux/drivers/usbd/hid_fd/hid-fd.c - hid function driver
+ *
+ * based on net_fd net function driver
+ *
+ * By: piro <piro400@occn.zaq.ne.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include "../usbd-export.h"
+#include "../usbd-build.h"
+#include "../usbd-module.h"
+
+MODULE_AUTHOR ("piro400@occn.zaq.ne.jp");
+MODULE_DESCRIPTION ("USB Device HID Function");
+
+USBD_MODULE_INFO ("hid_fd 0.1");
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <net/arp.h>
+#include <linux/rtnetlink.h>
+#include <linux/smp_lock.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/atmdev.h>
+#include <linux/pkt_sched.h>
+#include <asm/system.h>
+
+#include <linux/autoconf.h>
+
+#include "../usbd.h"
+#include "../usbd-func.h"
+#include "../usbd-bus.h"
+#include "../usbd-inline.h"
+#include "../usbd-arch.h"
+
+#include "../hotplug.h"
+
+#include "../usbd-debug.h"
+
+#define HIDOUT_MAJOR 230
+
+#if !defined (CONFIG_USBD_VENDORID)
+#error No Vendor ID
+#endif
+
+#undef CONFIG_USBD_PRODUCTID
+#define CONFIG_USBD_PRODUCTID			0xfff0
+
+#ifndef CONFIG_USBD_MAXPOWER
+#define CONFIG_USBD_MAXPOWER			0
+#endif
+
+#ifndef CONFIG_USBD_MANUFACTURER
+#define CONFIG_USBD_MANUFACTURER		"Piro"
+#endif
+
+#ifndef CONFIG_USBD_HID_IFNAME
+#define CONFIG_USBD_HID_IFNAME			"usbd"
+#endif
+
+#ifndef CONFIG_USBD_PRODUCT_NAME
+#define CONFIG_USBD_PRODUCT_NAME		"HID Function Driver"
+#endif
+
+#define CONFIG_USBD_KEYBOARD_IN_ENDPOINT	1
+
+#define CONFIG_USBD_MOUSE_IN_ENDPOINT		5
+
+#define MAX_INTERFACES				2
+
+/*
+ * USB 2.0 spec does not mention it, but MaxPower is expected to be at least one 
+ * and is tested for in USB configuration tests.
+ */
+#ifdef CONFIG_USBD_SELFPOWERED
+#define BMATTRIBUTE BMATTRIBUTE_RESERVED | BMATTRIBUTE_SELF_POWERED
+#define BMAXPOWER				1
+#else
+#define BMATTRIBUTE BMATTRIBUTE_RESERVED
+#define BMAXPOWER				CONFIG_USBD_MAXPOWER
+#endif
+
+/* Module Parameters ************************************************************************* */
+
+static char *if_name = CONFIG_USBD_HID_IFNAME;
+static char *dbg = NULL;
+
+static u32 vendor_id;
+static u32 product_id;
+
+static int wait_time = 1;
+
+MODULE_PARM (if_name, "s");
+MODULE_PARM (vendor_id, "i");
+MODULE_PARM (product_id, "i");
+MODULE_PARM (dbg, "s");
+
+MODULE_PARM (wait_time, "i");
+
+MODULE_PARM_DESC (if_name, "HID Interface name prefix");
+MODULE_PARM_DESC (vendor_id, "vendor id");
+MODULE_PARM_DESC (product_id, "product id");
+MODULE_PARM_DESC (dbg, "dbg string");
+
+MODULE_PARM_DESC (wait_time, "usb send wait");
+
+/* Debug switches (module parameter "dbg=...") *********************************************** */
+
+extern int dbgflg_usbdfd_init;
+int dbgflg_usbdfd_usbe;
+int dbgflg_usbdfd_rx;
+int dbgflg_usbdfd_tx;
+int dbgflg_usbdfd_ep0;
+
+static debug_option dbg_table[] = {
+	{&dbgflg_usbdfd_init, NULL, "init", "initialization and termination"},
+	{&dbgflg_usbdfd_ep0, NULL, "ep0", "End Point 0 (setup) packet handling"},
+	{&dbgflg_usbdfd_rx, NULL, "rx", "USB RX (host->device) handling"},
+	{&dbgflg_usbdfd_tx, NULL, "tx", "USB TX (device->host) handling"},
+	{&dbgflg_usbdfd_usbe, NULL, "usbe", "USB events"},
+	{NULL, NULL, "hid", "hid device handling"},
+	{NULL, NULL, NULL, NULL},
+};
+
+debug_option *hidproto_get_dbg_table (void)
+{
+	return (dbg_table);
+}
+
+
+#define dbg_init(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_init,lvl,fmt,##args)
+#define dbg_ep0(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_ep0,lvl,fmt,##args)
+#define dbg_rx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_rx,lvl,fmt,##args)
+#define dbg_tx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_tx,lvl,fmt,##args)
+#define dbg_usbe(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_usbe,lvl,fmt,##args)
+/*
+  #define dbg_init(lvl,fmt,args...) dbgPRINT(5,1,fmt,##args)
+  #define dbg_ep0(lvl,fmt,args...) dbgPRINT(5,1,fmt,##args)
+  #define dbg_rx(lvl,fmt,args...) dbgPRINT(5,1,fmt,##args)
+  #define dbg_tx(lvl,fmt,args...) dbgPRINT(5,1,fmt,##args)
+  #define dbg_usbe(lvl,fmt,args...) dbgPRINT(5,1,fmt,##args)
+*/
+
+
+#define HID_INUSE       0x01
+#define HID_ATTACHED    0x02
+
+struct usb_hid_private {
+	int flags;
+	int interface;
+	struct usb_device_instance *device;
+	int crc;
+	unsigned int maxtransfer;
+	char name[IFNAMSIZ];
+	int index;
+};
+
+static struct usb_hid_private hid_private_array[MAX_INTERFACES];
+
+static int keyboard_led = 0;
+
+static char keyboard_reportdesc[] = {
+	0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
+	0x09, 0x06,                    // USAGE (Keyboard)
+	0xa1, 0x01,                    // COLLECTION (Application)
+	0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
+	0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
+	0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
+	0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
+	0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
+	0x75, 0x01,                    //   REPORT_SIZE (1)
+	0x95, 0x08,                    //   REPORT_COUNT (8)
+	0x81, 0x02,                    //   INPUT (Data,Var,Abs)
+	0x95, 0x01,                    //   REPORT_COUNT (1)
+	0x75, 0x08,                    //   REPORT_SIZE (8)
+	0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
+	0x95, 0x05,                    //   REPORT_COUNT (5)
+	0x75, 0x01,                    //   REPORT_SIZE (1)
+	0x05, 0x08,                    //   USAGE_PAGE (LEDs)
+	0x19, 0x01,                    //   USAGE_MINIMUM (Num Lock)
+	0x29, 0x05,                    //   USAGE_MAXIMUM (Kana)
+	0x91, 0x02,                    //   OUTPUT (Data,Var,Abs)
+	0x95, 0x01,                    //   REPORT_COUNT (1)
+	0x75, 0x03,                    //   REPORT_SIZE (3)
+	0x91, 0x03,                    //   OUTPUT (Cnst,Var,Abs)
+	0x95, 0x06,                    //   REPORT_COUNT (6)
+	0x75, 0x08,                    //   REPORT_SIZE (8)
+	0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
+	0x25, 0x65,                    //   LOGICAL_MAXIMUM (101)
+	0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
+	0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
+	0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
+	0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
+	0xc0                           // END_COLLECTION
+};
+
+static char mouse_reportdesc[] = {
+	0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
+	0x09, 0x02,                    // USAGE (Mouse)
+	0xa1, 0x01,                    // COLLECTION (Application)
+	0x09, 0x01,                    //   USAGE (Pointer)
+	0xa1, 0x00,                    //   COLLECTION (Physical)
+	0x05, 0x09,                    //     USAGE_PAGE (Button)
+	0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
+	0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)
+	0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
+	0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
+	0x95, 0x03,                    //     REPORT_COUNT (3)
+	0x75, 0x01,                    //     REPORT_SIZE (1)
+	0x81, 0x02,                    //     INPUT (Data,Var,Abs)
+	0x95, 0x01,                    //     REPORT_COUNT (1)
+	0x75, 0x05,                    //     REPORT_SIZE (5)
+	0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)
+	0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
+	0x09, 0x30,                    //     USAGE (X)
+	0x09, 0x31,                    //     USAGE (Y)
+	0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
+	0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
+	0x75, 0x08,                    //     REPORT_SIZE (8)
+	0x95, 0x02,                    //     REPORT_COUNT (2)
+	0x81, 0x06,                    //     INPUT (Data,Var,Rel)
+	0xc0,                          //   END_COLLECTION
+	0xc0                           // END_COLLECTION
+};
+
+static struct usb_class_description keyboard_class_descriptions[] = {
+	{USB_ST_HID, 0, 
+	 {hid:
+	  {
+		  bcdHID:0x0110,
+		  bCountryLocalization:0x00,
+		  bNumFollow:0x01,
+		  bReportDescriptorType:USB_DESCRIPTOR_TYPE_HIDREPORT,
+		  bReportDescriptorLen:sizeof (keyboard_reportdesc),
+		  iReportDescriptor:keyboard_reportdesc,
+	  }
+	 }
+	},
+};
+
+/* Data Interface Alternate 1 endpoints
+ */
+static __initdata struct usb_endpoint_description keyboard_alt_1_endpoints[] = {
+	{
+		bEndpointAddress:CONFIG_USBD_KEYBOARD_IN_ENDPOINT,
+		bmAttributes:INTERRUPT,
+		wMaxPacketSize:8,
+		bInterval:10,
+		direction:IN,
+	},
+};
+
+
+/* Data Interface Alternate description(s)
+ */
+static __initdata struct usb_alternate_description key_alternate_descriptions[] = {
+	{
+		iInterface:"HID Keyboard Class",
+		bAlternateSetting:0,
+		classes:sizeof (keyboard_class_descriptions) / sizeof (struct usb_class_description),
+		class_list:keyboard_class_descriptions,
+		endpoints:sizeof (keyboard_alt_1_endpoints) / sizeof (struct usb_endpoint_description),
+		endpoint_list:keyboard_alt_1_endpoints,},
+	
+};
+
+
+
+static struct usb_class_description mouse_class_descriptions[] = {
+	{USB_ST_HID, 0, 
+	 {hid:
+	  {
+		  bcdHID:0x0110,
+		  bCountryLocalization:0x00,
+		  bNumFollow:0x01,
+		  bReportDescriptorType:USB_DESCRIPTOR_TYPE_HIDREPORT,
+		  bReportDescriptorLen:sizeof (mouse_reportdesc),
+		  iReportDescriptor:mouse_reportdesc,
+	  }
+	 }
+	},
+};
+
+
+/* Data Interface Alternate 1 endpoints
+ */
+static __initdata struct usb_endpoint_description mouse_alt_1_endpoints[] = {
+	{
+		bEndpointAddress:CONFIG_USBD_MOUSE_IN_ENDPOINT,
+		bmAttributes:INTERRUPT,
+		wMaxPacketSize:8,
+		bInterval:10,
+		direction:IN,
+	},
+};
+
+
+/* Data Interface Alternate description(s)
+ */
+static __initdata struct usb_alternate_description mouse_alternate_descriptions[] = {
+	{
+		iInterface:"HID Mouse Class",
+		bAlternateSetting:0,
+		classes:sizeof (mouse_class_descriptions) / sizeof (struct usb_class_description),
+		class_list:mouse_class_descriptions,
+		endpoints:sizeof (mouse_alt_1_endpoints) / sizeof (struct usb_endpoint_description),
+		endpoint_list:mouse_alt_1_endpoints,},
+	
+};
+
+#define USB_INTERFACE_CLASS_HID		3
+
+
+/* Interface description(s)
+ */
+static __initdata struct usb_interface_description hid_interfaces[] = {
+	
+	{
+		iInterface:"HID Keyboard",
+		bInterfaceClass:USB_INTERFACE_CLASS_HID,
+		bInterfaceSubClass:1, //boot device
+		bInterfaceProtocol:1, //keyboard
+		alternates:sizeof (key_alternate_descriptions) / sizeof (struct usb_alternate_description),
+		alternate_list:key_alternate_descriptions,},
+
+	{
+		iInterface:"HID Mouse",
+		bInterfaceClass:USB_INTERFACE_CLASS_HID,
+		bInterfaceSubClass:1, //boot device
+		bInterfaceProtocol:2, //mouse
+		alternates:sizeof (mouse_alternate_descriptions) / sizeof (struct usb_alternate_description),
+		alternate_list:mouse_alternate_descriptions,},	
+
+};
+
+
+/* USB Configuration ************************************************************************* */
+
+/* Configuration description(s)
+ */
+struct __initdata usb_configuration_description hid_description[] = {
+	{
+		iConfiguration:"HID Configuration",
+		bmAttributes:BMATTRIBUTE,
+		bMaxPower:BMAXPOWER,
+		interfaces:sizeof (hid_interfaces) / sizeof (struct usb_interface_description),
+		interface_list:hid_interfaces,},
+};
+
+/* Device Description
+ */
+struct __initdata usb_device_description hid_device_description = {
+	bDeviceClass:0,
+	bDeviceSubClass:0,	// XXX
+	bDeviceProtocol:0,	// XXX
+	idVendor:CONFIG_USBD_VENDORID,
+	idProduct:CONFIG_USBD_PRODUCTID,
+	iManufacturer:CONFIG_USBD_MANUFACTURER,
+	iProduct:CONFIG_USBD_PRODUCT_NAME,
+	iSerialNumber:0,
+};
+
+/* Intialize and destroy hid interfaces ************************************************** */
+
+/* *
+ * hid_create - create an interface
+ * @device: usb device instance
+ *
+ */
+void hid_create (struct usb_device_instance *device, struct usb_function_instance *function)
+{
+	int i;
+	struct usb_hid_private *hid_private;
+
+	dbg_init (1, "---> privdata: %p", function->privdata);
+        // lock and find an empty slot
+        {			// get module lock, search for empty device slot, if successful allocate and save
+
+                // check if we already have an interface
+                if ((hid_private = function->privdata) && (hid_private->flags & HID_INUSE)) {
+                        return;
+                }
+                // serial number munge
+
+                for (i = 0; i < MAX_INTERFACES; i++) {
+                        dbg_init (1, "i: %d hid_private_array[i].device: %p\n", i, hid_private_array[i].device);
+                        if (!hid_private_array[i].flags) {
+                                break;
+                        }
+                }
+                if (i >= MAX_INTERFACES) {
+                        dbg_init (1, "%s i >= MAX_INTERFACES %d %d", device->name, i, MAX_INTERFACES);
+			return;
+		}
+								
+		hid_private = &hid_private_array[i];
+		hid_private->flags |= HID_INUSE;
+		hid_private->index = i;
+
+	}
+
+	function->privdata = hid_private;
+
+	dbg_init (1, "function->privdata set to: %p\n", function->privdata);
+
+	hid_private->device = device;
+
+	MOD_INC_USE_COUNT;
+
+	// should check for buffer overflow...
+	if (strlen (if_name) < (IFNAMSIZ - 3)) {
+		sprintf (hid_private->name, "%s%d", if_name, hid_private->index);
+	} else {
+		dbg_init (0, "if_name too long or too short\n");
+		hid_private->name[0] = '\0';
+	}
+
+	// create hid interface
+	hid_private->flags |= HID_ATTACHED;
+
+}
+
+/* *
+ * hid_destroy - destroy an interface
+ * @device: usb device instance
+ *
+ */
+void hid_destroy (struct usb_device_instance *device)
+{
+	struct usb_hid_private *hid_private;
+	struct usb_function_instance *function;
+	int port = 0;		// XXX compound device
+
+	dbg_init (1, "-      -       -       ");
+	if (!(function = device->function_instance_array + port)) {
+		return;
+	}
+
+	dbg_init (1, "destroying");
+
+	if (!(hid_private = function->privdata)) {
+		dbg_init (1, "%s hid_private null", device->name);
+		return;
+	}
+
+	if (hid_private->flags & HID_ATTACHED) {
+
+	}
+
+	{			// get module lock and delete from device array
+		hid_private->device = NULL;
+		hid_private->flags = 0;
+		function->privdata = NULL;
+	}
+
+	MOD_DEC_USE_COUNT;
+
+	return;
+}
+
+
+/* Called when a USB Device is created or destroyed  ***************************************** */
+
+
+// XXX this should get passed a device structure, not bus
+static void hid_function_init (struct usb_bus_instance *bus,
+			       struct usb_device_instance *device,
+			       struct usb_function_driver *function_driver)
+{
+
+}
+
+static void hid_function_exit (struct usb_device_instance *device)
+{
+
+}
+
+/* Called to handle USB Events  ************************************************************** */
+
+/**
+ * hid_event - process a device event
+ * @device: usb device 
+ * @event: the event that happened
+ *
+ * Called by the usb device core layer to respond to various USB events.
+ *
+ * This routine IS called at interrupt time. Please use the usual precautions.
+ *
+ */
+void hid_event (struct usb_device_instance *device, usb_device_event_t event, int data)
+{
+	int port = 0;
+	struct usb_function_instance *function;
+
+	if (!(function = device->function_instance_array + port)) {
+		dbg_usbe (1, "function NULL");
+		return;
+	}
+
+        dbg_usbe (1,"%s data: %d privdata: %p", USBD_DEVICE_EVENTS(event), data, function->privdata);
+
+	dbg_usbe (7, "%p %s %d previous_state: %d device_state: %d",
+		  device, device->name, event, device->device_previous_state, device->device_state);
+
+
+	dbg_usbe (1, "event %x",event);
+	switch (event) {
+
+	case DEVICE_CREATE:	// a bus interface driver has created a usb device
+	        hid_create (device, function);
+		break;
+
+	case DEVICE_RESET:
+	        hid_destroy (device);
+		break;
+
+	case DEVICE_CONFIGURED:	// the host has found a driver that matches
+                device->interface = 0;
+                device->alternate = 0;
+
+	case DEVICE_SET_INTERFACE:	// the host driver has selected an interface
+		hid_create (device, function);
+		break;
+
+	case DEVICE_BUS_INACTIVE:	// suspend
+		hid_destroy (device);
+		break;
+
+	case DEVICE_BUS_ACTIVITY:	// resume
+		hid_create (device, function);
+		break;
+
+	case DEVICE_DESTROY:	// the bus interface driver is unloading
+		hid_destroy (device);
+		break;
+
+	case DEVICE_FUNCTION_PRIVATE:
+		break;
+
+        default:
+		break;
+	}
+
+}
+
+/**
+ * hid_recv_setup - called with a control URB 
+ * @urb - pointer to struct urb
+ *
+ * Check if this is a setup packet, process the device request, put results
+ * back into the urb and return zero or non-zero to indicate success (DATA)
+ * or failure (STALL).
+ *
+ * This routine IS called at interrupt time. Please use the usual precautions.
+ *
+ */
+int hid_recv_setup (struct urb *urb)
+{
+	struct usb_device_request *request;
+	//struct usb_device_instance *device = urb->device;
+	char clear_data[] = {00,00,00,00,00,00,00,00};
+	int length;
+	
+	int classRequest;
+	int reportType;
+	int reportID;
+	int interfaceNo;
+	int reportLength;
+
+	request = &urb->device_request;
+	
+	dbg_ep0 (1, "hid Recv setup:bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x\n",
+		 request->bmRequestType, 
+		 request->bRequest, 
+		 le16_to_cpu(request->wValue), 
+		 le16_to_cpu(request->wIndex), 
+		 le16_to_cpu(request->wLength));
+
+	if ((request->bmRequestType & USB_REQ_DIRECTION_MASK)) {
+		dbg_ep0 (1, "Device-to-Host");
+	} else {
+		dbg_ep0 (1, "Host-to-Device");
+	}
+
+	classRequest = request->bRequest;
+	reportType = (le16_to_cpu(request->wValue) >> 8) & 0xff;
+	reportID = le16_to_cpu(request->wValue) & 0xff;
+	interfaceNo = le16_to_cpu(request->wIndex);
+	reportLength = le16_to_cpu(request->wLength);
+
+	switch (classRequest) {
+	case USB_REQ_GET_REPORT:
+		
+		dbg_ep0 (1, "GET REPORT %p", urb);
+		
+		length = le16_to_cpu(request->wLength);
+		urb->actual_length = 0;
+ 		memcpy (urb->buffer + urb->actual_length, clear_data, length);
+		urb->actual_length = length;
+
+		break;
+
+	case USB_REQ_SET_REPORT:
+		dbg_ep0 (1, "SET REPORT %p", urb);
+
+		switch (interfaceNo) {
+		case 0:
+			if( reportType == 2 && reportID == 0 && reportLength == 1){
+				keyboard_led = *(char*)(urb->buffer);
+			}
+
+			break;
+		default:
+			break;
+		}
+
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+/**
+ * hid_recv_urb - called with a received URB 
+ * @urb - pointer to struct urb
+ *
+ * Return non-zero if we failed and urb is still valid (not disposed)
+ *
+ * This routine IS called at interrupt time. Please use the usual precautions.
+ *
+ */
+int hid_recv_urb (struct urb *urb)
+{
+	printk("recv urb\n");
+	printk("recv urb end\n");
+	return 0;
+}
+
+
+/**
+ * hid_urb_sent - called to indicate URB transmit finished
+ * @urb: pointer to struct urb
+ * @rc: result
+ *
+ * The usb device core layer will use this to let us know when an URB has
+ * been finished with.
+ *
+ * This routine IS called at interrupt time. Please use the usual precautions.
+ *
+ */
+int hid_urb_sent (struct urb *urb, int rc)
+{
+	int port = 0;		// XXX compound device
+	struct usb_device_instance *device;
+	struct usb_function_instance *function;
+	struct usb_hid_private *hid_private;
+
+	if (!urb || !(device = urb->device) ||
+	    !(function = device->function_instance_array + port) ||
+	    !(hid_private = function->privdata)) {
+		return -EINVAL;
+	}
+
+	urb->buffer = NULL;
+	urb->actual_length = 0;
+	urb->privdata = NULL;
+	usbd_dealloc_urb (urb);
+
+	return 0;
+}
+
+
+/* *
+ * hid_keyboard_xmit_rawdata - called to transmit an skb
+ * @interface: which interface
+ *
+ */
+
+static int hid_xmit_rawdata (int interface, int endpoint, char *data, int len)
+{
+	int port = 0;		// XXX compound device
+	struct usb_hid_private *hid_private;
+	struct urb *urb;
+	int timeout;
+
+	//dbg_tx(5,"skb: %p", skb);
+
+	// XXX XXX
+	// return -EINVAL;
+
+	hid_private = &hid_private_array[interface];
+
+	if (!hid_private->device) {
+		dbg_tx (0, "hid_private NULL");
+		return len;
+	}
+
+	if (!(urb = usbd_alloc_urb (hid_private->device,
+				   (hid_private->device->function_instance_array + port),
+				   endpoint, len))) {
+		printk ("urb alloc failed len: %d", len);
+		return len;
+	}
+
+
+	urb->buffer = data;
+	urb->actual_length = len;
+
+	dbg_ep0 (1, "urb: %p buffer: %p acutal_length: %d\n", urb, urb->buffer, urb->actual_length);
+
+	if (usbd_send_urb (urb)) {
+		printk ("usbd_send_urb failed\n");
+		urb->privdata = NULL;
+		usbd_dealloc_urb (urb);
+		return len;
+	}
+
+	timeout = wait_time;
+	while (timeout)
+		timeout = schedule_timeout (timeout);
+
+	return len;
+}
+
+/* *
+ * hid_keyboard_xmit_text 
+ * @interface: which interface
+ *
+ */
+#define LCTRL	0x01
+#define LSHIFT	0x02
+#define LALT	0x04
+#define WIN	0x08
+
+#define RCTRL	0x10
+#define RSHIFT	0x20
+#define RALT	0x40
+#define MENU	0x80
+
+/* char table */
+static unsigned int char_tbl[] = 
+	{
+		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x002a,0x002b,0x0028,0x0000,0x0000,0x0000,0x0000,0x0000,
+		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0029,0x0000,0x0000,0x0000,0x0000,
+		0x002c,0x021e,0x0234,0x0220,0x0221,0x0222,0x0224,0x0034,0x0226,0x0227,0x0225,0x022e,0x0036,0x002d,0x0037,0x0038,
+		0x0027,0x001e,0x001f,0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0233,0x0033,0x0236,0x002e,0x0237,0x0238,
+		0x021f,0x0204,0x0205,0x0206,0x0207,0x0208,0x0209,0x020a,0x020b,0x020c,0x020d,0x020e,0x020f,0x0210,0x0211,0x0212,
+		0x0213,0x0214,0x0215,0x0216,0x0217,0x0218,0x0219,0x021a,0x021b,0x021c,0x021d,0x002f,0x0031,0x0030,0x0223,0x022d,
+		0x0034,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,0x0010,0x0011,0x0012,
+		0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x022f,0x0231,0x0230,0x0234,0x004c
+	};
+
+static int hid_keyboard_xmit_text (char *data, int len)
+{
+	int pos;
+	int c;
+	static int prev;
+	unsigned char sc;
+	unsigned char mod;
+	int iface = 0;
+	char senddata[] = {0,0,0,0,0,0,0,0};
+	int senddatalen = 8;
+
+	for (pos = 0; pos < len; pos++){
+		c = *(data+pos);
+		//printk("c %x \n",c);
+
+		if(c < 0x80){
+			if(c == prev){
+				senddata[0] = 0;
+				senddata[2] = 0;
+				hid_xmit_rawdata(iface, CONFIG_USBD_KEYBOARD_IN_ENDPOINT, senddata, senddatalen);
+			}
+			sc = (char_tbl[c] & 0xff);
+			mod = (char_tbl[c] >> 8);
+			senddata[0] = mod;
+			senddata[2] = sc;
+			hid_xmit_rawdata(iface, CONFIG_USBD_KEYBOARD_IN_ENDPOINT, senddata, senddatalen);
+			prev = c;
+		}
+
+		if (c == 0xff){
+			senddata[0] = 0;
+			senddata[2] = 0;
+			hid_xmit_rawdata(iface, CONFIG_USBD_KEYBOARD_IN_ENDPOINT, senddata, senddatalen);
+		}			
+	}
+	
+	return len;
+}
+
+static int hid_keyboard_xmit_com (char *data, int len)
+{
+	int iface = 0;
+	char senddata[] = {0,0,0,0,0,0,0,0};
+	int senddatalen = 8;
+	unsigned int i[8];
+	int n;
+	int j;
+
+	n = sscanf(data, "%x %x %x %x %x %x %x %x %x", i+0, i+1, i+2, i+3, i+4, i+5, i+6, i+7);
+
+	if (n != 8){
+		return len;
+	}
+
+	for(j = 0; j < 8; j++){
+		senddata[j] = (char)i[j];
+	}
+	
+	hid_xmit_rawdata(iface, CONFIG_USBD_KEYBOARD_IN_ENDPOINT, senddata, senddatalen);
+		
+	return len;
+}
+
+
+static int hid_mouse_xmit_com (char *data, int len)
+{
+	int iface = 0;
+	char senddata[] = {0,0,0};
+	int senddatalen = 3;
+	unsigned int i[3];
+	int n;
+	int j;
+
+	n = sscanf(data, "%x %x %x", i+0, i+1, i+2);
+
+	if (n != 3){
+		return len;
+	}
+
+	for(j = 0; j < 3; j++){
+		senddata[j] = (char)i[j];
+	}
+	
+	hid_xmit_rawdata(iface, CONFIG_USBD_MOUSE_IN_ENDPOINT, senddata, senddatalen);
+		
+	return len;
+}
+
+
+struct usb_function_operations function_ops = {
+	event:hid_event,
+	recv_urb:hid_recv_urb,
+	recv_setup:hid_recv_setup,
+	urb_sent:hid_urb_sent,
+	function_init:hid_function_init,
+	function_exit:hid_function_exit,
+};
+
+struct usb_function_driver function_driver = {
+	name:"HID Keyboard & Mouse",
+	ops:&function_ops,
+	device_description:&hid_device_description,
+	configurations:sizeof (hid_description) / sizeof (struct usb_configuration_description),
+	configuration_description:hid_description,
+	this_module:THIS_MODULE,
+};
+
+
+
+/* proc file system ********************************************************************** */
+
+/* *
+ * hid_device_proc_read - implement proc file system read.
+ * @file: xx
+ * @buf:  xx
+ * @count:  xx
+ * @pos:  xx
+ *
+ * Standard proc file system read function.
+ *
+ * We let upper layers iterate for us, *pos will indicate which device to return * statistics for.
+ */
+static ssize_t
+hid_device_proc_read_condition(struct file *file, char *buf, size_t count, loff_t * pos)
+{
+	int len = 0;
+	char *p;
+
+	if (*pos > 0)
+		return 0;
+
+	p = "Unknown\n";
+
+	len = strlen(p);
+	*pos += len;
+	if (len > count) {
+		len = -EINVAL;
+	}
+	else if (len > 0 && copy_to_user(buf, p, len)) {
+		len = -EFAULT;
+	}
+	return len;
+}
+
+/* *
+ * usbd_monitor_proc_write - implement proc file system write.
+ * @file
+ * @buf
+ * @count
+ * @pos
+ *
+ * Proc file system write function, used to signal monitor actions complete.
+ * (Hotplug script (or whatever) writes to the file to signal the completion
+ * of the script.)  An ugly hack.
+ */
+static ssize_t
+hid_device_proc_write_condition(struct file *file, const char *buf, size_t count, loff_t * pos)
+{
+	unsigned int param=0;
+	char senddata[] = {00, 00, 00, 00, 00, 00, 00, 00};
+	int data_len = 8;
+	int iface = 0;
+
+	sscanf(buf,"%x",&param);
+
+	senddata[2] = (char)param;
+	hid_xmit_rawdata(iface, CONFIG_USBD_KEYBOARD_IN_ENDPOINT, senddata, data_len);
+
+	return count;
+}
+
+static struct file_operations hid_device_proc_operations_condition = {
+	read: hid_device_proc_read_condition,
+	write:hid_device_proc_write_condition,
+};
+
+/* 
+ * hid device file operation
+ */
+
+// MINOR number
+#define KEY_RAW		0
+#define KEY_TEXT	1
+#define KEY_COM		2
+
+#define MOUSE_RAW	10
+#define MOUSE_COM	11
+
+static int read_len;
+static int hid_open(struct inode * inode, struct file * file)
+{
+	//unsigned int minor = MINOR(inode->i_rdev);
+
+	//	printk(" minor %d\n",minor);
+	//	if (minor >= 2)
+	//	return -ENXIO;
+
+	read_len = 0;
+	return 0;
+}
+
+static int hid_release(struct inode * inode, struct file * file)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	int len;
+	char buffer[20];
+
+	switch (minor){
+	case KEY_TEXT:
+		len=1;
+		buffer[0] = 0xff;
+		buffer[len]='\0';
+	  
+		hid_keyboard_xmit_text (buffer, len);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static ssize_t hid_read(struct file * file, char * buf,
+			size_t count, loff_t *ppos)
+{
+	int len;
+	static char message[16];  
+
+	if(read_len > 1)
+		return 0;
+
+	sprintf(message, "%02x\n\0", keyboard_led);
+	
+	len = strlen(message);
+
+	if(len > count)
+		len = count;
+
+	copy_to_user(buf, message, len);
+
+	read_len = len;
+	return len;
+}
+
+static ssize_t hid_write(struct file * file, const char * buf,
+			 size_t count, loff_t *ppos)
+{
+	int len;
+	char buffer[50];
+	int iface = 0;
+
+	len=49;
+	if(len > count)
+		len = count;
+	
+	copy_from_user(buffer, buf, len);
+	
+	buffer[len]='\0';
+
+	switch(MINOR(file->f_dentry->d_inode->i_rdev)){
+	case KEY_RAW:
+		if(len < 8){
+			len = 0;
+			break;
+		}
+		len = 8;
+		len = hid_xmit_rawdata (iface, CONFIG_USBD_KEYBOARD_IN_ENDPOINT, buffer, len);
+
+		break;
+
+	case KEY_TEXT:
+		len = hid_keyboard_xmit_text (buffer, len);
+
+		break;
+	case KEY_COM:
+		len = hid_keyboard_xmit_com (buffer, len);
+
+		break;
+	case MOUSE_COM:
+		len = hid_mouse_xmit_com (buffer, len);
+
+		break;
+	case MOUSE_RAW:
+		if(len < 4){
+			len = 0;
+			break;
+		}
+		len = 4;
+		len = hid_xmit_rawdata (iface, CONFIG_USBD_MOUSE_IN_ENDPOINT, buffer, len);
+
+		break;
+	default:
+		break;
+	}
+
+	return len;
+
+}
+
+static struct file_operations hid_fops = {
+	owner:		THIS_MODULE,
+	write:		hid_write,
+	open:		hid_open,
+	release:	hid_release,
+	read:		hid_read,
+};
+
+
+/* Module init and exit ********************************************************************** */
+
+unsigned char hexdigit (char c)
+{
+	return isxdigit (c) ? (isdigit (c) ? (c - '0') : (c - 'A' + 10))
+		: 0;
+}
+
+/*
+ * hid_modinit - module init
+ *
+ */
+static int __init hid_modinit (void)
+{
+	debug_option *op = find_debug_option (dbg_table, "hid");
+
+	printk (KERN_INFO "%s (dbg=\"%s\")\n",
+		__usbd_module_info, dbg ? dbg : "");
+
+	printk (KERN_INFO "vendorID: %x productID: %x\n", CONFIG_USBD_VENDORID,
+		CONFIG_USBD_PRODUCTID);
+
+	if (devfs_register_chrdev (HIDOUT_MAJOR, "hidout", &hid_fops)) {
+		printk ("lp: unable to get major %d\n", HIDOUT_MAJOR);
+		return -EIO;
+	}
+
+	if (NULL != op) {
+		op->sub_table = hidproto_get_dbg_table ();
+	}
+	if (0 != scan_debug_options ("hid_fd", dbg_table, dbg)) {
+		return (-EINVAL);
+	}
+
+	memset (hid_private_array, 0, sizeof (hid_private_array));
+
+	// Check for non-default packet sizes
+
+	if (vendor_id) {
+		hid_device_description.idVendor = vendor_id;
+	}
+	if (product_id) {
+		hid_device_description.idProduct = product_id;
+	}
+
+	// register us with the usb device support layer
+	if (usbd_register_function (&function_driver)) {
+		dbg_init (0, "usbd_register_function failed");
+		return -EINVAL;
+	}
+
+	{
+		struct proc_dir_entry *p;
+		if ((p = create_proc_entry("usb-condition", 0, 0)) == NULL) {
+			return -ENOMEM;
+		}
+		p->proc_fops = &hid_device_proc_operations_condition;
+
+	}
+
+	return 0;
+}
+
+/*
+ * function_exit - module cleanup
+ *
+ */
+static void __exit hid_modexit (void)
+{
+
+	dbg_init (1, "exiting");
+
+	// de-register us with the usb device support layer
+	usbd_deregister_function (&function_driver);
+
+	// destroy hid interface
+	//hid_destroy(0);
+
+	remove_proc_entry("usb-condition", NULL);
+
+	devfs_unregister_chrdev(HIDOUT_MAJOR, "hidout");
+
+}
+
+module_init (hid_modinit);
+module_exit (hid_modexit);
diff -Nur c3000_pre/linux/drivers/usb/device/hid_fd/hid-fd.h c3000_p26_w3/linux/drivers/usb/device/hid_fd/hid-fd.h
--- c3000_pre/linux/drivers/usb/device/hid_fd/hid-fd.h	1970-01-01 09:00:00.000000000 +0900
+++ c3000_p26_w3/linux/drivers/usb/device/hid_fd/hid-fd.h	2006-09-11 14:07:44.000000000 +0900
@@ -0,0 +1,23 @@
+/*
+ * linux/drivers/usbd/hid_fd/hid-fd.h -- HID USB controller driver. 
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef HID_FD_H
+#define HID_FD_H 1
+#endif
diff -Nur c3000_pre/linux/drivers/usb/device/usbd-bus.c c3000_p26_w3/linux/drivers/usb/device/usbd-bus.c
--- c3000_pre/linux/drivers/usb/device/usbd-bus.c	2004-09-28 16:45:16.000000000 +0900
+++ c3000_p26_w3/linux/drivers/usb/device/usbd-bus.c	2006-09-11 10:06:46.000000000 +0900
@@ -129,6 +129,8 @@
 	unsigned long flags;
 
         dbg_init (1, "endpoint: %d", endpoint->endpoint_address);
+
+if(endpoint->endpoint_address != 0){
 	if (endpoint) {
 		local_irq_save (flags);
 
@@ -145,6 +147,8 @@
 	}
 }
 
+}
+
 /**
  * usbd_flush_tx - flush tx urbs from endpoint
  * @endpoint:
diff -Nur c3000_pre/linux/drivers/usb/device/usbd-func.c c3000_p26_w3/linux/drivers/usb/device/usbd-func.c
--- c3000_pre/linux/drivers/usb/device/usbd-func.c	2004-08-21 09:48:57.000000000 +0900
+++ c3000_p26_w3/linux/drivers/usb/device/usbd-func.c	2006-09-11 10:06:46.000000000 +0900
@@ -256,6 +256,9 @@
 	0,			// 0x11
 	sizeof (struct usb_class_mdlm_descriptor),	// 0x12
 	sizeof (struct usb_class_mdlmd_descriptor),	// 0x13
+	0,0,0,0,0,0,0,0,  //0x14-0x1b
+	0,0,0,0,0,    //0x1c-0x20
+	sizeof (struct usb_class_hid_function_descriptor), 0x21
 
 };
 
@@ -285,6 +288,7 @@
 	case CS_INTERFACE:
 		switch (bDescriptorSubtype) {
 
+		case USB_DT_HID:
 		case USB_ST_HEADER:
 		case USB_ST_CMF:
 		case USB_ST_ACMF:
@@ -328,7 +332,7 @@
 
 	switch (bDescriptorType) {
 	case CS_INTERFACE:
-		descriptor->descriptor.generic.bDescriptorSubtype = bDescriptorSubtype;
+			descriptor->descriptor.generic.bDescriptorSubtype = bDescriptorSubtype;
 
 	case USB_DT_DEVICE:
 	case USB_DT_CONFIG:
@@ -336,6 +340,9 @@
 	case USB_DT_ENDPOINT:
 		descriptor->descriptor.generic.bLength = length;
 		descriptor->descriptor.generic.bDescriptorType = bDescriptorType;
+		if(bDescriptorSubtype == USB_DT_HID){
+			descriptor->descriptor.generic.bDescriptorType = bDescriptorSubtype;
+		}
 		break;
 
 
@@ -374,6 +381,10 @@
 			dbg_init (2, "USB_DT_INTERFACE");
 			usbd_dealloc_string (descriptor->descriptor.interface.iInterface);
 			break;
+		case USB_DT_HID:
+			dbg_init (2, "class descriptor: %p type: %d subtype: %d", descriptor, descriptor->descriptor.generic.bDescriptorType, descriptor->descriptor.generic.bDescriptorSubtype);
+			dbg_init (2, "USB_DT_HID");
+			break;
 		case CS_INTERFACE:
 			dbg_init (2, "class descriptor: %p type: %d subtype: %d", descriptor, descriptor->descriptor.generic.bDescriptorType, descriptor->descriptor.generic.bDescriptorSubtype);
 			dbg_init (2, "USB_CS_INTERFACE");
@@ -449,8 +460,19 @@
 			dbg_init (0, "usbd_alloc_descriptor failed");
 			return NULL;
 		}
+
 		switch (class_description->bDescriptorSubtype) {
 
+		case USB_DT_HID:
+			class_descriptor->descriptor.hid_function.bcdHID = class_description->description.hid.bcdHID;;
+			class_descriptor->descriptor.hid_function.bCountryLocalization = class_description->description.hid.bCountryLocalization;
+			class_descriptor->descriptor.hid_function.bNumFollow = class_description->description.hid.bNumFollow;
+			class_descriptor->descriptor.hid_function.bReportDescriptorType = class_description->description.hid.bReportDescriptorType;
+			class_descriptor->descriptor.hid_function.bReportDescriptorLen = class_description->description.hid.bReportDescriptorLen;
+			class_descriptor->descriptor.hid_function.iReportDescriptor = class_description->description.hid.iReportDescriptor;
+			class_descriptor->descriptor.hid_function.bFunctionLength = HID_FUNCTION_DESCRIPTOR_LEN;
+
+			break;
 		case USB_ST_HEADER:
 			class_descriptor->descriptor.header_function.bcdCDC = cpu_to_le16 (CLASS_BCD_VERSION);
 			break;
diff -Nur c3000_pre/linux/drivers/usb/device/usbd-func.h c3000_p26_w3/linux/drivers/usb/device/usbd-func.h
--- c3000_pre/linux/drivers/usb/device/usbd-func.h	2004-08-21 09:48:57.000000000 +0900
+++ c3000_p26_w3/linux/drivers/usb/device/usbd-func.h	2006-09-11 10:06:46.000000000 +0900
@@ -163,6 +163,8 @@
 #define USB_ST_CSD			0x17
 #define USB_ST_TCM			0x18
 
+#define USB_ST_HID			0x21
+
 /*
  * commuications class description structures
  *
@@ -172,6 +174,15 @@
  * XXX add the other dozen class descriptor description structures....
  */
 
+struct usb_hid_description {
+	__u16 bcdHID;
+	__u8 bCountryLocalization;
+	__u8 bNumFollow;
+	__u8 bReportDescriptorType;
+	__u8 bReportDescriptorLen;
+	char* iReportDescriptor;
+};
+
 struct usb_header_description {
 	__u8 bDescriptorSubtype;
 	__u16 bcdCDC;
@@ -222,6 +233,7 @@
 		struct usb_ethernet_networking_description ethernet_networking;
 		struct usb_mobile_direct_line_model_description mobile_direct;
 		struct usb_mobile_direct_line_model_detail_description mobile_direct_detail;
+		struct usb_hid_description hid;
 	} description;
 };
 
@@ -382,6 +394,17 @@
  *
  * c.f. CDC 5.2 Table 25c
  */
+#define HID_FUNCTION_DESCRIPTOR_LEN 9
+struct usb_class_hid_function_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u16 bcdHID;
+	__u8 bCountryLocalization;
+	__u8 bNumFollow;
+	__u8 bReportDescriptorType;
+	__u16 bReportDescriptorLen;
+	char *iReportDescriptor;
+} __attribute__ ((packed));
 
 struct usb_class_function_descriptor {
 	__u8 bFunctionLength;
@@ -598,6 +621,7 @@
 		struct usb_class_atm_networking_descriptor atm_networking;
 		struct usb_class_mdlm_descriptor mobile_direct;
 		struct usb_class_mdlmd_descriptor mobile_direct_detail;
+		struct usb_class_hid_function_descriptor hid_function;
 	} descriptor;
 
 } __attribute__ ((packed));
diff -Nur c3000_pre/linux/drivers/usb/device/usbd.c c3000_p26_w3/linux/drivers/usb/device/usbd.c
--- c3000_pre/linux/drivers/usb/device/usbd.c	2004-08-21 09:48:57.000000000 +0900
+++ c3000_p26_w3/linux/drivers/usb/device/usbd.c	2006-09-11 10:06:46.000000000 +0900
@@ -212,6 +212,9 @@
         "DEVICE QUALIFIER",     // 6
         "OTHER SPEED",          // 7
         "INTERFACE POWER",      // 8
+        "","","","","","","","","","","","","","","","","","","","","","","","",
+        "HID Desc",   // 0x21
+        "HID Report Desc",      // 0x22
 };
 
 char *usbd_device_status[] = {
diff -Nur c3000_pre/linux/drivers/usb/device/usbd.h c3000_p26_w3/linux/drivers/usb/device/usbd.h
--- c3000_pre/linux/drivers/usb/device/usbd.h	2004-08-21 09:48:57.000000000 +0900
+++ c3000_p26_w3/linux/drivers/usb/device/usbd.h	2006-09-11 10:06:46.000000000 +0900
@@ -340,7 +340,9 @@
 #define USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION   0x07
 #define USB_DESCRIPTOR_TYPE_INTERFACE_POWER             0x08
 
-#define USBD_DEVICE_DESCRIPTORS(x) (((unsigned int)x <= USB_DESCRIPTOR_TYPE_INTERFACE_POWER) ? \
+#define USB_DESCRIPTOR_TYPE_HIDREPORT                0x22
+
+#define USBD_DEVICE_DESCRIPTORS(x) (((unsigned int)x <= USB_DESCRIPTOR_TYPE_HIDREPORT) ? \
                 usbd_device_descriptors[x] : "UNKNOWN")
 
 /*
