|  | /* | 
|  | * (C) Copyright 2001 | 
|  | * Denis Peter, MPL AG Switzerland | 
|  | * | 
|  | * Part of this source has been derived from the Linux USB | 
|  | * project. | 
|  | * | 
|  | * See file CREDITS for list of people who contributed to this | 
|  | * project. | 
|  | * | 
|  | * 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., 59 Temple Place, Suite 330, Boston, | 
|  | * MA 02111-1307 USA | 
|  | * | 
|  | */ | 
|  | #include <common.h> | 
|  | #include <devices.h> | 
|  | #include <asm/byteorder.h> | 
|  |  | 
|  | #include <usb.h> | 
|  |  | 
|  | #undef USB_KBD_DEBUG | 
|  | /* | 
|  | * if overwrite_console returns 1, the stdin, stderr and stdout | 
|  | * are switched to the serial port, else the settings in the | 
|  | * environment are used | 
|  | */ | 
|  | #ifdef CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE | 
|  | extern int overwrite_console (void); | 
|  | #else | 
|  | int overwrite_console (void) | 
|  | { | 
|  | return (0); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #ifdef	USB_KBD_DEBUG | 
|  | #define	USB_KBD_PRINTF(fmt,args...)	printf (fmt ,##args) | 
|  | #else | 
|  | #define USB_KBD_PRINTF(fmt,args...) | 
|  | #endif | 
|  |  | 
|  |  | 
|  | #define REPEAT_RATE  40/4 /* 40msec -> 25cps */ | 
|  | #define REPEAT_DELAY 10 /* 10 x REAPEAT_RATE = 400msec */ | 
|  |  | 
|  | #define NUM_LOCK	0x53 | 
|  | #define CAPS_LOCK 0x39 | 
|  | #define SCROLL_LOCK 0x47 | 
|  |  | 
|  |  | 
|  | /* Modifier bits */ | 
|  | #define LEFT_CNTR		0 | 
|  | #define LEFT_SHIFT	1 | 
|  | #define LEFT_ALT		2 | 
|  | #define LEFT_GUI		3 | 
|  | #define RIGHT_CNTR	4 | 
|  | #define RIGHT_SHIFT	5 | 
|  | #define RIGHT_ALT		6 | 
|  | #define RIGHT_GUI		7 | 
|  |  | 
|  | #define USB_KBD_BUFFER_LEN 0x20  /* size of the keyboardbuffer */ | 
|  |  | 
|  | static volatile char usb_kbd_buffer[USB_KBD_BUFFER_LEN]; | 
|  | static volatile int usb_in_pointer = 0; | 
|  | static volatile int usb_out_pointer = 0; | 
|  |  | 
|  | unsigned char new[8]; | 
|  | unsigned char old[8]; | 
|  | int repeat_delay; | 
|  | #define DEVNAME "usbkbd" | 
|  | static unsigned char num_lock = 0; | 
|  | static unsigned char caps_lock = 0; | 
|  | static unsigned char scroll_lock = 0; | 
|  | static unsigned char ctrl = 0; | 
|  |  | 
|  | static unsigned char leds __attribute__ ((aligned (0x4))); | 
|  |  | 
|  | static unsigned char usb_kbd_numkey[] = { | 
|  | '1', '2', '3', '4', '5', '6', '7', '8', '9', '0','\r',0x1b,'\b','\t',' ', '-', | 
|  | '=', '[', ']','\\', '#', ';', '\'', '`', ',', '.', '/' | 
|  | }; | 
|  | static unsigned char usb_kbd_numkey_shifted[] = { | 
|  | '!', '@', '#', '$', '%', '^', '&', '*', '(', ')','\r',0x1b,'\b','\t',' ', '_', | 
|  | '+', '{', '}', '|', '~', ':', '"', '~', '<', '>', '?' | 
|  | }; | 
|  |  | 
|  | /****************************************************************** | 
|  | * Queue handling | 
|  | ******************************************************************/ | 
|  | /* puts character in the queue and sets up the in and out pointer */ | 
|  | static void usb_kbd_put_queue(char data) | 
|  | { | 
|  | if((usb_in_pointer+1)==USB_KBD_BUFFER_LEN) { | 
|  | if(usb_out_pointer==0) { | 
|  | return; /* buffer full */ | 
|  | } else{ | 
|  | usb_in_pointer=0; | 
|  | } | 
|  | } else { | 
|  | if((usb_in_pointer+1)==usb_out_pointer) | 
|  | return; /* buffer full */ | 
|  | usb_in_pointer++; | 
|  | } | 
|  | usb_kbd_buffer[usb_in_pointer]=data; | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* test if a character is in the queue */ | 
|  | static int usb_kbd_testc(void) | 
|  | { | 
|  | #ifdef CONFIG_SYS_USB_EVENT_POLL | 
|  | usb_event_poll(); | 
|  | #endif | 
|  | if(usb_in_pointer==usb_out_pointer) | 
|  | return(0); /* no data */ | 
|  | else | 
|  | return(1); | 
|  | } | 
|  | /* gets the character from the queue */ | 
|  | static int usb_kbd_getc(void) | 
|  | { | 
|  | char c; | 
|  | while(usb_in_pointer==usb_out_pointer) { | 
|  | #ifdef CONFIG_SYS_USB_EVENT_POLL | 
|  | usb_event_poll(); | 
|  | #endif | 
|  | } | 
|  | if((usb_out_pointer+1)==USB_KBD_BUFFER_LEN) | 
|  | usb_out_pointer=0; | 
|  | else | 
|  | usb_out_pointer++; | 
|  | c=usb_kbd_buffer[usb_out_pointer]; | 
|  | return (int)c; | 
|  |  | 
|  | } | 
|  |  | 
|  | /* forward decleration */ | 
|  | static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum); | 
|  |  | 
|  | /* search for keyboard and register it if found */ | 
|  | int drv_usb_kbd_init(void) | 
|  | { | 
|  | int error,i; | 
|  | device_t usb_kbd_dev,*old_dev; | 
|  | struct usb_device *dev; | 
|  | char *stdinname  = getenv ("stdin"); | 
|  |  | 
|  | usb_in_pointer=0; | 
|  | usb_out_pointer=0; | 
|  | /* scan all USB Devices */ | 
|  | for(i=0;i<USB_MAX_DEVICE;i++) { | 
|  | dev=usb_get_dev_index(i); /* get device */ | 
|  | if(dev == NULL) | 
|  | return -1; | 
|  | if(dev->devnum!=-1) { | 
|  | if(usb_kbd_probe(dev,0)==1) { /* Ok, we found a keyboard */ | 
|  | /* check, if it is already registered */ | 
|  | USB_KBD_PRINTF("USB KBD found set up device.\n"); | 
|  | old_dev = device_get_by_name(DEVNAME); | 
|  | if(old_dev) { | 
|  | /* ok, already registered, just return ok */ | 
|  | USB_KBD_PRINTF("USB KBD is already registered.\n"); | 
|  | return 1; | 
|  | } | 
|  | /* register the keyboard */ | 
|  | USB_KBD_PRINTF("USB KBD register.\n"); | 
|  | memset (&usb_kbd_dev, 0, sizeof(device_t)); | 
|  | strcpy(usb_kbd_dev.name, DEVNAME); | 
|  | usb_kbd_dev.flags =  DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; | 
|  | usb_kbd_dev.putc = NULL; | 
|  | usb_kbd_dev.puts = NULL; | 
|  | usb_kbd_dev.getc = usb_kbd_getc; | 
|  | usb_kbd_dev.tstc = usb_kbd_testc; | 
|  | usb_kbd_dev.priv = (void *)dev; | 
|  | error = device_register (&usb_kbd_dev); | 
|  | if(error==0) { | 
|  | /* check if this is the standard input device */ | 
|  | if(strcmp(stdinname,DEVNAME)==0) { | 
|  | /* reassign the console */ | 
|  | if(overwrite_console()) { | 
|  | return 1; | 
|  | } | 
|  | error=console_assign(stdin,DEVNAME); | 
|  | if(error==0) | 
|  | return 1; | 
|  | else | 
|  | return error; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  | return error; | 
|  | } | 
|  | } | 
|  | } | 
|  | /* no USB Keyboard found */ | 
|  | return -1; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* deregistering the keyboard */ | 
|  | int usb_kbd_deregister(void) | 
|  | { | 
|  | #ifdef CONFIG_SYS_DEVICE_DEREGISTER | 
|  | return device_deregister(DEVNAME); | 
|  | #else | 
|  | return 1; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * Low Level drivers | 
|  | */ | 
|  |  | 
|  | /* set the LEDs. Since this is used in the irq routine, the control job | 
|  | is issued with a timeout of 0. This means, that the job is queued without | 
|  | waiting for job completion */ | 
|  |  | 
|  | static void usb_kbd_setled(struct usb_device *dev) | 
|  | { | 
|  | struct usb_interface_descriptor *iface; | 
|  | iface = &dev->config.if_desc[0]; | 
|  | leds=0; | 
|  | if(scroll_lock!=0) | 
|  | leds|=1; | 
|  | leds<<=1; | 
|  | if(caps_lock!=0) | 
|  | leds|=1; | 
|  | leds<<=1; | 
|  | if(num_lock!=0) | 
|  | leds|=1; | 
|  | usb_control_msg(dev, usb_sndctrlpipe(dev, 0), | 
|  | USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, | 
|  | 0x200, iface->bInterfaceNumber,(void *)&leds, 1, 0); | 
|  |  | 
|  | } | 
|  |  | 
|  |  | 
|  | #define CAPITAL_MASK 0x20 | 
|  | /* Translate the scancode in ASCII */ | 
|  | static int usb_kbd_translate(unsigned char scancode,unsigned char modifier,int pressed) | 
|  | { | 
|  | unsigned char keycode; | 
|  |  | 
|  | if(pressed==0) { | 
|  | /* key released */ | 
|  | repeat_delay=0; | 
|  | return 0; | 
|  | } | 
|  | if(pressed==2) { | 
|  | repeat_delay++; | 
|  | if(repeat_delay<REPEAT_DELAY) | 
|  | return 0; | 
|  | repeat_delay=REPEAT_DELAY; | 
|  | } | 
|  | keycode=0; | 
|  | if((scancode>3) && (scancode<=0x1d)) { /* alpha numeric values */ | 
|  | keycode=scancode-4 + 0x61; | 
|  | if(caps_lock) | 
|  | keycode&=~CAPITAL_MASK; /* switch to capital Letters */ | 
|  | if(((modifier&(1<<LEFT_SHIFT))!=0)||((modifier&(1<<RIGHT_SHIFT))!=0)) { | 
|  | if(keycode & CAPITAL_MASK) | 
|  | keycode&=~CAPITAL_MASK; /* switch to capital Letters */ | 
|  | else | 
|  | keycode|=CAPITAL_MASK; /* switch to non capital Letters */ | 
|  | } | 
|  | } | 
|  | if((scancode>0x1d) && (scancode<0x3A)) { | 
|  | if(((modifier&(1<<LEFT_SHIFT))!=0)||((modifier&(1<<RIGHT_SHIFT))!=0))  /* shifted */ | 
|  | keycode=usb_kbd_numkey_shifted[scancode-0x1e]; | 
|  | else /* non shifted */ | 
|  | keycode=usb_kbd_numkey[scancode-0x1e]; | 
|  | } | 
|  |  | 
|  | if (ctrl) | 
|  | keycode = scancode - 0x3; | 
|  |  | 
|  | if(pressed==1) { | 
|  | if(scancode==NUM_LOCK) { | 
|  | num_lock=~num_lock; | 
|  | return 1; | 
|  | } | 
|  | if(scancode==CAPS_LOCK) { | 
|  | caps_lock=~caps_lock; | 
|  | return 1; | 
|  | } | 
|  | if(scancode==SCROLL_LOCK) { | 
|  | scroll_lock=~scroll_lock; | 
|  | return 1; | 
|  | } | 
|  | } | 
|  | if(keycode!=0) { | 
|  | USB_KBD_PRINTF("%c",keycode); | 
|  | usb_kbd_put_queue(keycode); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Interrupt service routine */ | 
|  | static int usb_kbd_irq(struct usb_device *dev) | 
|  | { | 
|  | int i,res; | 
|  |  | 
|  | if((dev->irq_status!=0)||(dev->irq_act_len!=8)) | 
|  | { | 
|  | USB_KBD_PRINTF("usb_keyboard Error %lX, len %d\n",dev->irq_status,dev->irq_act_len); | 
|  | return 1; | 
|  | } | 
|  | res=0; | 
|  |  | 
|  | switch (new[0]) { | 
|  | case 0x0:	/* No combo key pressed */ | 
|  | ctrl = 0; | 
|  | break; | 
|  | case 0x01:	/* Left Ctrl pressed */ | 
|  | case 0x10:	/* Right Ctrl pressed */ | 
|  | ctrl = 1; | 
|  | break; | 
|  | } | 
|  |  | 
|  | for (i = 2; i < 8; i++) { | 
|  | if (old[i] > 3 && memscan(&new[2], old[i], 6) == &new[8]) { | 
|  | res|=usb_kbd_translate(old[i],new[0],0); | 
|  | } | 
|  | if (new[i] > 3 && memscan(&old[2], new[i], 6) == &old[8]) { | 
|  | res|=usb_kbd_translate(new[i],new[0],1); | 
|  | } | 
|  | } | 
|  | if((new[2]>3) && (old[2]==new[2])) /* still pressed */ | 
|  | res|=usb_kbd_translate(new[2],new[0],2); | 
|  | if(res==1) | 
|  | usb_kbd_setled(dev); | 
|  | memcpy(&old[0],&new[0], 8); | 
|  | return 1; /* install IRQ Handler again */ | 
|  | } | 
|  |  | 
|  | /* probes the USB device dev for keyboard type */ | 
|  | static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) | 
|  | { | 
|  | struct usb_interface_descriptor *iface; | 
|  | struct usb_endpoint_descriptor *ep; | 
|  | int pipe,maxp; | 
|  |  | 
|  | if (dev->descriptor.bNumConfigurations != 1) return 0; | 
|  | iface = &dev->config.if_desc[ifnum]; | 
|  |  | 
|  | if (iface->bInterfaceClass != 3) return 0; | 
|  | if (iface->bInterfaceSubClass != 1) return 0; | 
|  | if (iface->bInterfaceProtocol != 1) return 0; | 
|  | if (iface->bNumEndpoints != 1) return 0; | 
|  |  | 
|  | ep = &iface->ep_desc[0]; | 
|  |  | 
|  | if (!(ep->bEndpointAddress & 0x80)) return 0; | 
|  | if ((ep->bmAttributes & 3) != 3) return 0; | 
|  | USB_KBD_PRINTF("USB KBD found set protocol...\n"); | 
|  | /* ok, we found a USB Keyboard, install it */ | 
|  | /* usb_kbd_get_hid_desc(dev); */ | 
|  | usb_set_protocol(dev, iface->bInterfaceNumber, 0); | 
|  | USB_KBD_PRINTF("USB KBD found set idle...\n"); | 
|  | usb_set_idle(dev, iface->bInterfaceNumber, REPEAT_RATE, 0); | 
|  | memset(&new[0], 0, 8); | 
|  | memset(&old[0], 0, 8); | 
|  | repeat_delay=0; | 
|  | pipe = usb_rcvintpipe(dev, ep->bEndpointAddress); | 
|  | maxp = usb_maxpacket(dev, pipe); | 
|  | dev->irq_handle=usb_kbd_irq; | 
|  | USB_KBD_PRINTF("USB KBD enable interrupt pipe...\n"); | 
|  | usb_submit_int_msg(dev,pipe,&new[0], maxp > 8 ? 8 : maxp,ep->bInterval); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | #if 0 | 
|  | struct usb_hid_descriptor { | 
|  | unsigned char  bLength; | 
|  | unsigned char  bDescriptorType; /* 0x21 for HID */ | 
|  | unsigned short bcdHID; /* release number */ | 
|  | unsigned char  bCountryCode; | 
|  | unsigned char  bNumDescriptors; | 
|  | unsigned char  bReportDescriptorType; | 
|  | unsigned short wDescriptorLength; | 
|  | } __attribute__ ((packed)); | 
|  |  | 
|  | /* | 
|  | * We parse each description item into this structure. Short items data | 
|  | * values are expanded to 32-bit signed int, long items contain a pointer | 
|  | * into the data area. | 
|  | */ | 
|  |  | 
|  | struct hid_item { | 
|  | unsigned char format; | 
|  | unsigned char size; | 
|  | unsigned char type; | 
|  | unsigned char tag; | 
|  | union { | 
|  | unsigned char   u8; | 
|  | char            s8; | 
|  | unsigned short  u16; | 
|  | short           s16; | 
|  | unsigned long   u32; | 
|  | long            s32; | 
|  | unsigned char  *longdata; | 
|  | } data; | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * HID report item format | 
|  | */ | 
|  |  | 
|  | #define HID_ITEM_FORMAT_SHORT	0 | 
|  | #define HID_ITEM_FORMAT_LONG	1 | 
|  |  | 
|  | /* | 
|  | * Special tag indicating long items | 
|  | */ | 
|  |  | 
|  | #define HID_ITEM_TAG_LONG	15 | 
|  |  | 
|  |  | 
|  | static struct usb_hid_descriptor usb_kbd_hid_desc; | 
|  |  | 
|  | void usb_kbd_display_hid(struct usb_hid_descriptor *hid) | 
|  | { | 
|  | printf("USB_HID_DESC:\n"); | 
|  | printf("  bLenght               0x%x\n",hid->bLength); | 
|  | printf("  bcdHID                0x%x\n",hid->bcdHID); | 
|  | printf("  bCountryCode          %d\n",hid->bCountryCode); | 
|  | printf("  bNumDescriptors       0x%x\n",hid->bNumDescriptors); | 
|  | printf("  bReportDescriptorType 0x%x\n",hid->bReportDescriptorType); | 
|  | printf("  wDescriptorLength     0x%x\n",hid->wDescriptorLength); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Fetch a report description item from the data stream. We support long | 
|  | * items, though they are not used yet. | 
|  | */ | 
|  |  | 
|  | static int fetch_item(unsigned char *start,unsigned char *end, struct hid_item *item) | 
|  | { | 
|  | if((end - start) > 0) { | 
|  | unsigned char b = *start++; | 
|  | item->type = (b >> 2) & 3; | 
|  | item->tag  = (b >> 4) & 15; | 
|  | if (item->tag == HID_ITEM_TAG_LONG) { | 
|  | item->format = HID_ITEM_FORMAT_LONG; | 
|  | if ((end - start) >= 2) { | 
|  | item->size = *start++; | 
|  | item->tag  = *start++; | 
|  | if ((end - start) >= item->size) { | 
|  | item->data.longdata = start; | 
|  | start += item->size; | 
|  | return item->size; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | item->format = HID_ITEM_FORMAT_SHORT; | 
|  | item->size = b & 3; | 
|  | switch (item->size) { | 
|  | case 0: | 
|  | return item->size; | 
|  | case 1: | 
|  | if ((end - start) >= 1) { | 
|  | item->data.u8 = *start++; | 
|  | return item->size; | 
|  | } | 
|  | break; | 
|  | case 2: | 
|  | if ((end - start) >= 2) { | 
|  | item->data.u16 = le16_to_cpu((unsigned short *)start); | 
|  | start+=2; | 
|  | return item->size; | 
|  | } | 
|  | case 3: | 
|  | item->size++; | 
|  | if ((end - start) >= 4) { | 
|  | item->data.u32 = le32_to_cpu((unsigned long *)start); | 
|  | start+=4; | 
|  | return item->size; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * HID report descriptor item type (prefix bit 2,3) | 
|  | */ | 
|  |  | 
|  | #define HID_ITEM_TYPE_MAIN		0 | 
|  | #define HID_ITEM_TYPE_GLOBAL		1 | 
|  | #define HID_ITEM_TYPE_LOCAL		2 | 
|  | #define HID_ITEM_TYPE_RESERVED		3 | 
|  | /* | 
|  | * HID report descriptor main item tags | 
|  | */ | 
|  |  | 
|  | #define HID_MAIN_ITEM_TAG_INPUT			8 | 
|  | #define HID_MAIN_ITEM_TAG_OUTPUT		9 | 
|  | #define HID_MAIN_ITEM_TAG_FEATURE		11 | 
|  | #define HID_MAIN_ITEM_TAG_BEGIN_COLLECTION	10 | 
|  | #define HID_MAIN_ITEM_TAG_END_COLLECTION	12 | 
|  | /* | 
|  | * HID report descriptor main item contents | 
|  | */ | 
|  |  | 
|  | #define HID_MAIN_ITEM_CONSTANT		0x001 | 
|  | #define HID_MAIN_ITEM_VARIABLE		0x002 | 
|  | #define HID_MAIN_ITEM_RELATIVE		0x004 | 
|  | #define HID_MAIN_ITEM_WRAP		0x008 | 
|  | #define HID_MAIN_ITEM_NONLINEAR		0x010 | 
|  | #define HID_MAIN_ITEM_NO_PREFERRED	0x020 | 
|  | #define HID_MAIN_ITEM_NULL_STATE	0x040 | 
|  | #define HID_MAIN_ITEM_VOLATILE		0x080 | 
|  | #define HID_MAIN_ITEM_BUFFERED_BYTE	0x100 | 
|  |  | 
|  | /* | 
|  | * HID report descriptor collection item types | 
|  | */ | 
|  |  | 
|  | #define HID_COLLECTION_PHYSICAL		0 | 
|  | #define HID_COLLECTION_APPLICATION	1 | 
|  | #define HID_COLLECTION_LOGICAL		2 | 
|  | /* | 
|  | * HID report descriptor global item tags | 
|  | */ | 
|  |  | 
|  | #define HID_GLOBAL_ITEM_TAG_USAGE_PAGE		0 | 
|  | #define HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM	1 | 
|  | #define HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM	2 | 
|  | #define HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM	3 | 
|  | #define HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM	4 | 
|  | #define HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT	5 | 
|  | #define HID_GLOBAL_ITEM_TAG_UNIT		6 | 
|  | #define HID_GLOBAL_ITEM_TAG_REPORT_SIZE		7 | 
|  | #define HID_GLOBAL_ITEM_TAG_REPORT_ID		8 | 
|  | #define HID_GLOBAL_ITEM_TAG_REPORT_COUNT	9 | 
|  | #define HID_GLOBAL_ITEM_TAG_PUSH		10 | 
|  | #define HID_GLOBAL_ITEM_TAG_POP			11 | 
|  |  | 
|  | /* | 
|  | * HID report descriptor local item tags | 
|  | */ | 
|  |  | 
|  | #define HID_LOCAL_ITEM_TAG_USAGE		0 | 
|  | #define HID_LOCAL_ITEM_TAG_USAGE_MINIMUM	1 | 
|  | #define HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM	2 | 
|  | #define HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX	3 | 
|  | #define HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM	4 | 
|  | #define HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM	5 | 
|  | #define HID_LOCAL_ITEM_TAG_STRING_INDEX		7 | 
|  | #define HID_LOCAL_ITEM_TAG_STRING_MINIMUM	8 | 
|  | #define HID_LOCAL_ITEM_TAG_STRING_MAXIMUM	9 | 
|  | #define HID_LOCAL_ITEM_TAG_DELIMITER		10 | 
|  |  | 
|  |  | 
|  | static void usb_kbd_show_item(struct hid_item *item) | 
|  | { | 
|  | switch(item->type) { | 
|  | case HID_ITEM_TYPE_MAIN: | 
|  | switch(item->tag) { | 
|  | case HID_MAIN_ITEM_TAG_INPUT: | 
|  | printf("Main Input"); | 
|  | break; | 
|  | case HID_MAIN_ITEM_TAG_OUTPUT: | 
|  | printf("Main Output"); | 
|  | break; | 
|  | case HID_MAIN_ITEM_TAG_FEATURE: | 
|  | printf("Main Feature"); | 
|  | break; | 
|  | case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: | 
|  | printf("Main Begin Collection"); | 
|  | break; | 
|  | case HID_MAIN_ITEM_TAG_END_COLLECTION: | 
|  | printf("Main End Collection"); | 
|  | break; | 
|  | default: | 
|  | printf("Main reserved %d",item->tag); | 
|  | break; | 
|  | } | 
|  | break; | 
|  | case HID_ITEM_TYPE_GLOBAL: | 
|  | switch(item->tag) { | 
|  | case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: | 
|  | printf("- Global Usage Page"); | 
|  | break; | 
|  | case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: | 
|  | printf("- Global Logical Minimum"); | 
|  | break; | 
|  | case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: | 
|  | printf("- Global Logical Maximum"); | 
|  | break; | 
|  | case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: | 
|  | printf("- Global physical Minimum"); | 
|  | break; | 
|  | case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: | 
|  | printf("- Global physical Maximum"); | 
|  | break; | 
|  | case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: | 
|  | printf("- Global Unit Exponent"); | 
|  | break; | 
|  | case HID_GLOBAL_ITEM_TAG_UNIT: | 
|  | printf("- Global Unit"); | 
|  | break; | 
|  | case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: | 
|  | printf("- Global Report Size"); | 
|  | break; | 
|  | case HID_GLOBAL_ITEM_TAG_REPORT_ID: | 
|  | printf("- Global Report ID"); | 
|  | break; | 
|  | case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: | 
|  | printf("- Global Report Count"); | 
|  | break; | 
|  | case HID_GLOBAL_ITEM_TAG_PUSH: | 
|  | printf("- Global Push"); | 
|  | break; | 
|  | case HID_GLOBAL_ITEM_TAG_POP: | 
|  | printf("- Global Pop"); | 
|  | break; | 
|  | default: | 
|  | printf("- Global reserved %d",item->tag); | 
|  | break; | 
|  | } | 
|  | break; | 
|  | case HID_ITEM_TYPE_LOCAL: | 
|  | switch(item->tag) { | 
|  | case HID_LOCAL_ITEM_TAG_USAGE: | 
|  | printf("-- Local Usage"); | 
|  | break; | 
|  | case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: | 
|  | printf("-- Local Usage Minimum"); | 
|  | break; | 
|  | case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: | 
|  | printf("-- Local Usage Maximum"); | 
|  | break; | 
|  | case HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX: | 
|  | printf("-- Local Designator Index"); | 
|  | break; | 
|  | case HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM: | 
|  | printf("-- Local Designator Minimum"); | 
|  | break; | 
|  | case HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM: | 
|  | printf("-- Local Designator Maximum"); | 
|  | break; | 
|  | case HID_LOCAL_ITEM_TAG_STRING_INDEX: | 
|  | printf("-- Local String Index"); | 
|  | break; | 
|  | case HID_LOCAL_ITEM_TAG_STRING_MINIMUM: | 
|  | printf("-- Local String Minimum"); | 
|  | break; | 
|  | case HID_LOCAL_ITEM_TAG_STRING_MAXIMUM: | 
|  | printf("-- Local String Maximum"); | 
|  | break; | 
|  | case HID_LOCAL_ITEM_TAG_DELIMITER: | 
|  | printf("-- Local Delimiter"); | 
|  | break; | 
|  | default: | 
|  | printf("-- Local reserved %d",item->tag); | 
|  | break; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | printf("--- reserved %d",item->type); | 
|  | break; | 
|  | } | 
|  | switch(item->size) { | 
|  | case 1: | 
|  | printf("  %d",item->data.u8); | 
|  | break; | 
|  | case 2: | 
|  | printf("  %d",item->data.u16); | 
|  | break; | 
|  | case 4: | 
|  | printf("  %ld",item->data.u32); | 
|  | break; | 
|  | } | 
|  | printf("\n"); | 
|  | } | 
|  |  | 
|  |  | 
|  | static int usb_kbd_get_hid_desc(struct usb_device *dev) | 
|  | { | 
|  | unsigned char buffer[256]; | 
|  | struct usb_descriptor_header *head; | 
|  | struct usb_config_descriptor *config; | 
|  | int index,len,i; | 
|  | unsigned char *start, *end; | 
|  | struct hid_item item; | 
|  |  | 
|  | if(usb_get_configuration_no(dev,&buffer[0],0)==-1) | 
|  | return -1; | 
|  | head =(struct usb_descriptor_header *)&buffer[0]; | 
|  | if(head->bDescriptorType!=USB_DT_CONFIG) { | 
|  | printf(" ERROR: NOT USB_CONFIG_DESC %x\n",head->bDescriptorType); | 
|  | return -1; | 
|  | } | 
|  | index=head->bLength; | 
|  | config=(struct usb_config_descriptor *)&buffer[0]; | 
|  | len=le16_to_cpu(config->wTotalLength); | 
|  | /* Ok the first entry must be a configuration entry, now process the others */ | 
|  | head=(struct usb_descriptor_header *)&buffer[index]; | 
|  | while(index+1 < len) { | 
|  | if(head->bDescriptorType==USB_DT_HID) { | 
|  | printf("HID desc found\n"); | 
|  | memcpy(&usb_kbd_hid_desc,&buffer[index],buffer[index]); | 
|  | le16_to_cpus(&usb_kbd_hid_desc.bcdHID); | 
|  | le16_to_cpus(&usb_kbd_hid_desc.wDescriptorLength); | 
|  | usb_kbd_display_hid(&usb_kbd_hid_desc); | 
|  | len=0; | 
|  | break; | 
|  | } | 
|  | index+=head->bLength; | 
|  | head=(struct usb_descriptor_header *)&buffer[index]; | 
|  | } | 
|  | if(len>0) | 
|  | return -1; | 
|  | len=usb_kbd_hid_desc.wDescriptorLength; | 
|  | if((index = usb_get_class_descriptor(dev, 0, USB_DT_REPORT, 0, &buffer[0], len)) < 0) { | 
|  | printf("reading report descriptor failed\n"); | 
|  | return -1; | 
|  | } | 
|  | printf(" report descriptor (size %u, read %d)\n", len, index); | 
|  | start = &buffer[0]; | 
|  | end = &buffer[len]; | 
|  | i=0; | 
|  | do { | 
|  | index=fetch_item(start,end,&item); | 
|  | i+=index; | 
|  | i++; | 
|  | if(index>=0) | 
|  | usb_kbd_show_item(&item); | 
|  |  | 
|  | start+=index; | 
|  | start++; | 
|  | } while(index>=0); | 
|  |  | 
|  | } | 
|  |  | 
|  | #endif |