Revision 269
Added by markw almost 11 years ago
| firmware/usb/LICENSE | ||
|---|---|---|
| 
     This part is GPL - in fact this whole firmware should be - just need to check a few things...
 
   | 
||
| firmware/usb/debug.h | ||
|---|---|---|
| 
     #define hid_debugf(IN, ...) {};
 
   | 
||
| 
     #define hidp_debugf(IN, ...) {};
 
   | 
||
| 
     #define iprintf(IN, ...) {};
 
   | 
||
| firmware/usb/events.h | ||
|---|---|---|
| 
     void event_keyboard(uint8_t i, uint8_t buf[])
 
   | 
||
| 
     {
 
   | 
||
| 
     	int j;
 
   | 
||
| 
     	printf("Event keyboard:%d\n", i);
 
   | 
||
| 
             for (j=0;j!=6;++j)
 
   | 
||
| 
     	{
 
   | 
||
| 
     		printf("Event keyboard:%d = %x\n", j, buf[j]);
 
   | 
||
| 
     	}
 
   | 
||
| 
     }
 
   | 
||
| 
     void event_mouse(uint8_t a, uint8_t b, uint8_t c)
 
   | 
||
| 
     {
 
   | 
||
| 
     	printf("Event mouse:%d %d %d\n",a,b,c);
 
   | 
||
| 
     }
 
   | 
||
| 
     void event_digital_joystick(uint8_t idx, uint8_t jmap)
 
   | 
||
| 
     {
 
   | 
||
| 
     	printf("Event joystick:%d %x\n", idx,jmap);
 
   | 
||
| 
     }
 
   | 
||
| 
     void event_analog_joystick(uint8_t idx, int8_t x, int8_t y)
 
   | 
||
| 
     {
 
   | 
||
| 
     	printf("Event analog joystick:%d %d %d\n", idx,x,y);
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     #define JOY_RIGHT       0x01
 
   | 
||
| 
     #define JOY_LEFT        0x02
 
   | 
||
| 
     #define JOY_DOWN        0x04
 
   | 
||
| 
     #define JOY_UP          0x08
 
   | 
||
| 
     #define JOY_BTN1        0x10
 
   | 
||
| 
     #define JOY_BTN2        0x20
 
   | 
||
| 
     #define JOY_BTN3        0x40
 
   | 
||
| 
     #define JOY_BTN4        0x80
 
   | 
||
| 
     #define JOY_MOVE        (JOY_RIGHT|JOY_LEFT|JOY_UP|JOY_DOWN)
 
   | 
||
| 
     | 
||
| 
     | 
||
| firmware/usb/hid.c | ||
|---|---|---|
| 
     #include <stdio.h>
 
   | 
||
| 
     | 
||
| 
     #include "usb.h"
 
   | 
||
| 
     #include "timer.h"
 
   | 
||
| 
     #include "hidparser.h"
 
   | 
||
| 
     #include "events.h"
 
   | 
||
| 
     #include "debug.h"
 
   | 
||
| 
     #include "usbhostslave.h"
 
   | 
||
| 
     | 
||
| 
     static unsigned char kbd_led_state = 0;  // default: all leds off
 
   | 
||
| 
     static unsigned char joysticks = 0;      // number of detected usb joysticks
 
   | 
||
| 
     | 
||
| 
     uint8_t hid_get_joysticks(void) {
 
   | 
||
| 
       return joysticks;
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     //get HID report descriptor 
 
   | 
||
| 
     static uint8_t hid_get_report_descr(usb_device_t *dev, uint8_t i, uint16_t size)  {
 
   | 
||
| 
       //  hid_debugf("%s(%x, if=%d, size=%d)", __FUNCTION__, dev->bAddress, iface, size);
 
   | 
||
| 
     | 
||
| 
       uint8_t buf[size];
 
   | 
||
| 
       usb_hid_info_t *info = &(dev->hid_info);
 
   | 
||
| 
       uint8_t rcode = usb_ctrl_req( dev, HID_REQ_HIDREPORT, USB_REQUEST_GET_DESCRIPTOR, 0x00, 
 
   | 
||
| 
     			      HID_DESCRIPTOR_REPORT, info->iface[i].iface_idx, size, buf);
 
   | 
||
| 
     | 
||
| 
       if(!rcode) {
 
   | 
||
| 
         hid_debugf("HID report descriptor:");
 
   | 
||
| 
         //MWW hexdump(buf, size, 0);
 
   | 
||
| 
     | 
||
| 
         // we got a report descriptor. Try to parse it
 
   | 
||
| 
         if(parse_report_descriptor(buf, size, &(info->iface[i].conf))) {
 
   | 
||
| 
           if(info->iface[i].conf.type == CONFIG_TYPE_JOYSTICK) {
 
   | 
||
| 
     	hid_debugf("Detected USB joystick #%d", joysticks);
 
   | 
||
| 
     | 
||
| 
     	info->iface[i].device_type = HID_DEVICE_JOYSTICK;
 
   | 
||
| 
     	info->iface[i].jindex = joysticks++;
 
   | 
||
| 
           }
 
   | 
||
| 
         }
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       return rcode;
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     static uint8_t hid_set_idle(usb_device_t *dev, uint8_t iface, uint8_t reportID, uint8_t duration ) {
 
   | 
||
| 
       //  hid_debugf("%s(%x, if=%d id=%d, dur=%d)", __FUNCTION__, dev->bAddress, iface, reportID, duration);
 
   | 
||
| 
     | 
||
| 
       return( usb_ctrl_req( dev, HID_REQ_HIDOUT, HID_REQUEST_SET_IDLE, reportID, 
 
   | 
||
| 
     		       duration, iface, 0x0000, NULL));
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     static uint8_t hid_set_protocol(usb_device_t *dev, uint8_t iface, uint8_t protocol) {
 
   | 
||
| 
       //  hid_debugf("%s(%x, if=%d proto=%d)", __FUNCTION__, dev->bAddress, iface, protocol);
 
   | 
||
| 
     | 
||
| 
       return( usb_ctrl_req( dev, HID_REQ_HIDOUT, HID_REQUEST_SET_PROTOCOL, protocol, 
 
   | 
||
| 
     		       0x00, iface, 0x0000, NULL));
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     static uint8_t hid_set_report(usb_device_t *dev, uint8_t iface, uint8_t report_type, uint8_t report_id, 
 
   | 
||
| 
     			      uint16_t nbytes, uint8_t* dataptr ) {
 
   | 
||
| 
       //  hid_debugf("%s(%x, if=%d data=%x)", __FUNCTION__, dev->bAddress, iface, dataptr[0]);
 
   | 
||
| 
     | 
||
| 
       return( usb_ctrl_req(dev, HID_REQ_HIDOUT, HID_REQUEST_SET_REPORT, report_id, 
 
   | 
||
| 
     		       report_type, iface, nbytes, dataptr));
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     /* todo: handle parsing in chunks */
 
   | 
||
| 
     static uint8_t usb_hid_parse_conf(usb_device_t *dev, uint8_t conf, uint16_t len) {
 
   | 
||
| 
       usb_hid_info_t *info = &(dev->hid_info);
 
   | 
||
| 
       uint8_t rcode;
 
   | 
||
| 
       bool isGoodInterface = false;
 
   | 
||
| 
     | 
||
| 
       union buf_u {
 
   | 
||
| 
         usb_configuration_descriptor_t conf_desc;
 
   | 
||
| 
         usb_interface_descriptor_t iface_desc;
 
   | 
||
| 
         usb_endpoint_descriptor_t ep_desc;
 
   | 
||
| 
         usb_hid_descriptor_t hid_desc;
 
   | 
||
| 
         uint8_t raw[len];
 
   | 
||
| 
       } buf, *p;
 
   | 
||
| 
     | 
||
| 
       // usb_interface_descriptor
 
   | 
||
| 
     | 
||
| 
       if(rcode = usb_get_conf_descr(dev, len, conf, &buf.conf_desc)) 
 
   | 
||
| 
         return rcode;
 
   | 
||
| 
     | 
||
| 
       /* scan through all descriptors */
 
   | 
||
| 
       p = &buf;
 
   | 
||
| 
       while(len > 0) {
 
   | 
||
| 
         switch(p->conf_desc.bDescriptorType) {
 
   | 
||
| 
         case USB_DESCRIPTOR_CONFIGURATION:
 
   | 
||
| 
           // hid_debugf("conf descriptor size %d", p->conf_desc.bLength);
 
   | 
||
| 
           // we already had this, so we simply ignore it
 
   | 
||
| 
           break;
 
   | 
||
| 
     | 
||
| 
         case USB_DESCRIPTOR_INTERFACE:
 
   | 
||
| 
           isGoodInterface = false;
 
   | 
||
| 
           // hid_debugf("iface descriptor size %d", p->iface_desc.bLength);
 
   | 
||
| 
     | 
||
| 
           /* check the interface descriptors for supported class */
 
   | 
||
| 
     | 
||
| 
           // only HID interfaces are supported
 
   | 
||
| 
           if(p->iface_desc.bInterfaceClass == USB_CLASS_HID) {
 
   | 
||
| 
     	//	puts("iface is HID");
 
   | 
||
| 
     | 
||
| 
     	if(info->bNumIfaces < MAX_IFACES) {
 
   | 
||
| 
     	  // ok, let's use this interface
 
   | 
||
| 
     	  isGoodInterface = true;
 
   | 
||
| 
     | 
||
| 
     	  info->iface[info->bNumIfaces].iface_idx = p->iface_desc.bInterfaceNumber;
 
   | 
||
| 
     	  info->iface[info->bNumIfaces].has_boot_mode = false;
 
   | 
||
| 
     	  info->iface[info->bNumIfaces].is_5200daptor = false;
 
   | 
||
| 
     	  info->iface[info->bNumIfaces].key_state = 0;
 
   | 
||
| 
     	  info->iface[info->bNumIfaces].device_type = HID_DEVICE_UNKNOWN;
 
   | 
||
| 
     	  info->iface[info->bNumIfaces].conf.type = CONFIG_TYPE_NONE;
 
   | 
||
| 
     | 
||
| 
     	  if(p->iface_desc.bInterfaceSubClass == HID_BOOT_INTF_SUBCLASS) {
 
   | 
||
| 
     	    // hid_debugf("Iface %d is Boot sub class", info->bNumIfaces);
 
   | 
||
| 
     	    info->iface[info->bNumIfaces].has_boot_mode = true;
 
   | 
||
| 
     	  }
 
   | 
||
| 
     | 
||
| 
     	  switch(p->iface_desc.bInterfaceProtocol) {
 
   | 
||
| 
     	  case HID_PROTOCOL_NONE:
 
   | 
||
| 
     	    hid_debugf("HID protocol is NONE");
 
   | 
||
| 
     	    break;
 
   | 
||
| 
     | 
||
| 
     	  case HID_PROTOCOL_KEYBOARD:
 
   | 
||
| 
     	    hid_debugf("HID protocol is KEYBOARD");
 
   | 
||
| 
     	    info->iface[info->bNumIfaces].device_type = HID_DEVICE_KEYBOARD;
 
   | 
||
| 
     	    break;
 
   | 
||
| 
     | 
||
| 
     	  case HID_PROTOCOL_MOUSE:
 
   | 
||
| 
     	    hid_debugf("HID protocol is MOUSE");
 
   | 
||
| 
     	    info->iface[info->bNumIfaces].device_type = HID_DEVICE_MOUSE;
 
   | 
||
| 
     	    break;
 
   | 
||
| 
     | 
||
| 
     	  default:
 
   | 
||
| 
     	    hid_debugf("HID protocol is %d", p->iface_desc.bInterfaceProtocol);
 
   | 
||
| 
     	    break;
 
   | 
||
| 
     	  }
 
   | 
||
| 
     	}
 
   | 
||
| 
           }
 
   | 
||
| 
           break;
 
   | 
||
| 
     | 
||
| 
         case USB_DESCRIPTOR_ENDPOINT:
 
   | 
||
| 
           //      hid_debugf("endpoint descriptor size %d", p->ep_desc.bLength);
 
   | 
||
| 
     | 
||
| 
           if(isGoodInterface) {
 
   | 
||
| 
     | 
||
| 
     	// only interrupt in endpoints are supported
 
   | 
||
| 
     	if ((p->ep_desc.bmAttributes & 0x03) == 3 && (p->ep_desc.bEndpointAddress & 0x80) == 0x80) {
 
   | 
||
| 
     	  hid_debugf("endpoint %d, interval = %dms", 
 
   | 
||
| 
     		  p->ep_desc.bEndpointAddress & 0x0F, p->ep_desc.bInterval);
 
   | 
||
| 
     | 
||
| 
     	  // Fill in the endpoint info structure
 
   | 
||
| 
     	  uint8_t epidx = info->bNumIfaces;
 
   | 
||
| 
     	  info->iface[epidx].interval = p->ep_desc.bInterval;
 
   | 
||
| 
     	  info->iface[epidx].ep.epAddr	 = (p->ep_desc.bEndpointAddress & 0x0F);
 
   | 
||
| 
     	  info->iface[epidx].ep.maxPktSize = p->ep_desc.wMaxPacketSize[0];
 
   | 
||
| 
     	  info->iface[epidx].ep.epAttribs	 = 0;
 
   | 
||
| 
     	  info->iface[epidx].ep.bmNakPower = USB_NAK_NOWAIT;
 
   | 
||
| 
     	  info->bNumIfaces++;
 
   | 
||
| 
     	}
 
   | 
||
| 
           }
 
   | 
||
| 
           break;
 
   | 
||
| 
     | 
||
| 
         case HID_DESCRIPTOR_HID:
 
   | 
||
| 
           hid_debugf("hid descriptor size %d", p->ep_desc.bLength);
 
   | 
||
| 
     | 
||
| 
           if(isGoodInterface) {
 
   | 
||
| 
     	// we need a report descriptor
 
   | 
||
| 
     	if(p->hid_desc.bDescrType == HID_DESCRIPTOR_REPORT) {
 
   | 
||
| 
     	  uint16_t len = p->hid_desc.wDescriptorLength[0] + 
 
   | 
||
| 
     	    256 * p->hid_desc.wDescriptorLength[1];
 
   | 
||
| 
     	  hid_debugf(" -> report descriptor size = %d", len);
 
   | 
||
| 
     | 
||
| 
     	  info->iface[info->bNumIfaces].report_desc_size = len;
 
   | 
||
| 
     	}
 
   | 
||
| 
           }
 
   | 
||
| 
           break;
 
   | 
||
| 
     | 
||
| 
         default:
 
   | 
||
| 
           hid_debugf("unsupported descriptor type %d size %d", p->raw[1], p->raw[0]);
 
   | 
||
| 
         }
 
   | 
||
| 
     | 
||
| 
         // advance to next descriptor
 
   | 
||
| 
         len -= p->conf_desc.bLength;
 
   | 
||
| 
         p = (union buf_u*)(p->raw + p->conf_desc.bLength);
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       if(len != 0) {
 
   | 
||
| 
         hid_debugf("Config underrun: %d", len);
 
   | 
||
| 
         return USB_ERROR_CONFIGURAION_SIZE_MISMATCH;
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       return 0;
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     static uint8_t usb_hid_init(usb_device_t *dev) {
 
   | 
||
| 
       hid_debugf("%s(%x)", __FUNCTION__, dev->bAddress);
 
   | 
||
| 
     | 
||
| 
       uint8_t rcode;
 
   | 
||
| 
       uint8_t i;
 
   | 
||
| 
       uint16_t vid, pid;
 
   | 
||
| 
     | 
||
| 
       usb_hid_info_t *info = &(dev->hid_info);
 
   | 
||
| 
     | 
||
| 
       union {
 
   | 
||
| 
         usb_device_descriptor_t dev_desc;
 
   | 
||
| 
         usb_configuration_descriptor_t conf_desc;
 
   | 
||
| 
       } buf;
 
   | 
||
| 
     | 
||
| 
       // reset status
 
   | 
||
| 
       info->bPollEnable = false;
 
   | 
||
| 
       info->bNumIfaces = 0;
 
   | 
||
| 
     | 
||
| 
       for(i=0;i<MAX_IFACES;i++) {
 
   | 
||
| 
         info->iface[i].qNextPollTime = 0;
 
   | 
||
| 
         info->iface[i].ep.epAddr	 = i;
 
   | 
||
| 
         info->iface[i].ep.maxPktSize = 8;
 
   | 
||
| 
         info->iface[i].ep.epAttribs	 = 0;
 
   | 
||
| 
         info->iface[i].ep.bmNakPower = USB_NAK_MAX_POWER;
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       // try to re-read full device descriptor from newly assigned address
 
   | 
||
| 
       if(rcode = usb_get_dev_descr( dev, sizeof(usb_device_descriptor_t), &buf.dev_desc ))
 
   | 
||
| 
         return rcode;
 
   | 
||
| 
     | 
||
| 
       // save vid/pid for automatic hack later
 
   | 
||
| 
       vid = buf.dev_desc.idVendor;
 
   | 
||
| 
       pid = buf.dev_desc.idProduct;
 
   | 
||
| 
     | 
||
| 
       uint8_t num_of_conf = buf.dev_desc.bNumConfigurations;
 
   | 
||
| 
       //  hid_debugf("number of configurations: %d", num_of_conf);
 
   | 
||
| 
     | 
||
| 
       for(i=0; i<num_of_conf; i++) {
 
   | 
||
| 
         if(rcode = usb_get_conf_descr(dev, sizeof(usb_configuration_descriptor_t), i, &buf.conf_desc)) 
 
   | 
||
| 
           return rcode;
 
   | 
||
| 
     | 
||
| 
         //    hid_debugf("conf descriptor %d has total size %d", i, buf.conf_desc.wTotalLength);
 
   | 
||
| 
     | 
||
| 
         // parse directly if it already fitted completely into the buffer
 
   | 
||
| 
         usb_hid_parse_conf(dev, i, buf.conf_desc.wTotalLength);
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       // check if we found valid hid interfaces
 
   | 
||
| 
       if(!info->bNumIfaces) {
 
   | 
||
| 
         hid_debugf("no hid interfaces found");
 
   | 
||
| 
         return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       // Set Configuration Value
 
   | 
||
| 
       rcode = usb_set_conf(dev, buf.conf_desc.bConfigurationValue);
 
   | 
||
| 
     | 
||
| 
       // process all supported interfaces
 
   | 
||
| 
       for(i=0; i<info->bNumIfaces; i++) {
 
   | 
||
| 
         // no boot mode, try to parse HID report descriptor
 
   | 
||
| 
         if(!info->iface[i].has_boot_mode) {
 
   | 
||
| 
           rcode = hid_get_report_descr(dev, i, info->iface[i].report_desc_size);
 
   | 
||
| 
           if(rcode) return rcode;
 
   | 
||
| 
     | 
||
| 
           if(info->iface[i].device_type == CONFIG_TYPE_JOYSTICK) {
 
   | 
||
| 
     	char k;
 
   | 
||
| 
     | 
||
| 
     	iprintf("Report: type = %d, id = %d, size = %d\n", 
 
   | 
||
| 
     		info->iface[i].conf.type,
 
   | 
||
| 
     		info->iface[i].conf.report_id,
 
   | 
||
| 
     		info->iface[i].conf.report_size);
 
   | 
||
| 
     | 
||
| 
     	for(k=0;k<2;k++)
 
   | 
||
| 
     	  iprintf("Axis%d: %d@%d %d->%d\n", k, 
 
   | 
||
| 
     		  info->iface[i].conf.joystick.axis[k].size,
 
   | 
||
| 
     		  info->iface[i].conf.joystick.axis[k].byte_offset,
 
   | 
||
| 
     		  info->iface[i].conf.joystick.axis[k].logical.min,
 
   | 
||
| 
     		  info->iface[i].conf.joystick.axis[k].logical.max);
 
   | 
||
| 
     | 
||
| 
     	for(k=0;k<4;k++)
 
   | 
||
| 
     	  iprintf("Button%d: @%d/%d\n", k,
 
   | 
||
| 
     		  info->iface[i].conf.joystick.button[k].byte_offset,
 
   | 
||
| 
     		  info->iface[i].conf.joystick.button[k].bitmask);
 
   | 
||
| 
           }
 
   | 
||
| 
     | 
||
| 
     | 
||
| 
           // use fixed setup for known interfaces 
 
   | 
||
| 
           if((vid == 0x0079) && (pid == 0x0011) && (i==0)) {
 
   | 
||
| 
     	iprintf("hacking cheap NES pad\n");
 
   | 
||
| 
     | 
||
| 
             // fixed setup for nes gamepad
 
   | 
||
| 
             info->iface[0].conf.joystick.button[0].byte_offset = 5;
 
   | 
||
| 
             info->iface[0].conf.joystick.button[0].bitmask = 32;
 
   | 
||
| 
             info->iface[0].conf.joystick.button[1].byte_offset = 5;
 
   | 
||
| 
             info->iface[0].conf.joystick.button[1].bitmask = 64;
 
   | 
||
| 
             info->iface[0].conf.joystick.button[2].byte_offset = 6;
 
   | 
||
| 
             info->iface[0].conf.joystick.button[2].bitmask = 16;
 
   | 
||
| 
             info->iface[0].conf.joystick.button[3].byte_offset = 6;
 
   | 
||
| 
             info->iface[0].conf.joystick.button[3].bitmask = 32;
 
   | 
||
| 
           }
 
   | 
||
| 
     | 
||
| 
           if((vid == 0x04d8) && (pid == 0xf6ec) && (i==0)) {
 
   | 
||
| 
     	iprintf("hacking 5200daptor\n");
 
   | 
||
| 
     | 
||
| 
     	info->iface[0].conf.joystick.button[2].byte_offset = 4;
 
   | 
||
| 
     	info->iface[0].conf.joystick.button[2].bitmask = 0x40;    // "Reset"
 
   | 
||
| 
     	info->iface[0].conf.joystick.button[3].byte_offset = 4;
 
   | 
||
| 
     	info->iface[0].conf.joystick.button[3].bitmask = 0x10;    // "Start"
 
   | 
||
| 
     | 
||
| 
     	info->iface[0].is_5200daptor = true;
 
   | 
||
| 
           }
 
   | 
||
| 
         }
 
   | 
||
| 
     | 
||
| 
         rcode = hid_set_idle(dev, info->iface[i].iface_idx, 0, 0);
 
   | 
||
| 
         // MWW if (rcode && rcode != hrSTALL)
 
   | 
||
| 
         if (rcode && rcode != OHS900_STATMASK_STALL_RXED)
 
   | 
||
| 
           return rcode;
 
   | 
||
| 
     | 
||
| 
         // enable boot mode
 
   | 
||
| 
         if(info->iface[i].has_boot_mode)
 
   | 
||
| 
           hid_set_protocol(dev, info->iface[i].iface_idx, HID_BOOT_PROTOCOL);
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       puts("HID configured");
 
   | 
||
| 
     | 
||
| 
       // update leds
 
   | 
||
| 
       for(i=0;i<MAX_IFACES;i++)
 
   | 
||
| 
         if(dev->hid_info.iface[i].device_type == HID_DEVICE_KEYBOARD)
 
   | 
||
| 
           hid_set_report(dev, dev->hid_info.iface[i].iface_idx, 2, 0, 1, &kbd_led_state);
 
   | 
||
| 
     | 
||
| 
       info->bPollEnable = true;
 
   | 
||
| 
       return 0;
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     static uint8_t usb_hid_release(usb_device_t *dev) {
 
   | 
||
| 
       usb_hid_info_t *info = &(dev->hid_info);
 
   | 
||
| 
     | 
||
| 
       puts(__FUNCTION__);
 
   | 
||
| 
     | 
||
| 
       uint8_t i;
 
   | 
||
| 
       // check if a joystick is released
 
   | 
||
| 
       for(i=0;i<info->bNumIfaces;i++) {
 
   | 
||
| 
         if(info->iface[i].device_type == HID_DEVICE_JOYSTICK) {
 
   | 
||
| 
           uint8_t c_jindex = info->iface[i].jindex;
 
   | 
||
| 
           hid_debugf("releasing joystick #%d, renumbering", c_jindex);
 
   | 
||
| 
     | 
||
| 
           // walk through all devices and search for sticks with a higher id
 
   | 
||
| 
     | 
||
| 
           // search for all joystick interfaces on all hid devices
 
   | 
||
| 
           usb_device_t *dev = usb_get_devices();
 
   | 
||
| 
           uint8_t j;
 
   | 
||
| 
           for(j=0;j<USB_NUMDEVICES;j++) {
 
   | 
||
| 
     	if(dev[j].bAddress && (dev[j].class == &usb_hid_class)) {
 
   | 
||
| 
     	  // search for joystick interfaces
 
   | 
||
| 
     	  uint8_t k;
 
   | 
||
| 
     	  for(k=0;k<MAX_IFACES;k++) {
 
   | 
||
| 
     	    if(dev[j].hid_info.iface[k].device_type == HID_DEVICE_JOYSTICK) {
 
   | 
||
| 
     	      if(dev[j].hid_info.iface[k].jindex > c_jindex) {
 
   | 
||
| 
     		hid_debugf("decreasing jindex of dev #%d from %d to %d", j, 
 
   | 
||
| 
     			dev[j].hid_info.iface[k].jindex, dev[j].hid_info.iface[k].jindex-1);
 
   | 
||
| 
     		dev[j].hid_info.iface[k].jindex--;
 
   | 
||
| 
     	      }
 
   | 
||
| 
     	    }
 
   | 
||
| 
     	  }
 
   | 
||
| 
     	}
 
   | 
||
| 
           }
 
   | 
||
| 
           // one less joystick in the system ...
 
   | 
||
| 
           joysticks--;
 
   | 
||
| 
         }
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       return 0;
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     // special 5200daptor button processing
 
   | 
||
| 
     static void handle_5200daptor(usb_hid_iface_info_t *iface, uint8_t *buf) {
 
   | 
||
| 
     | 
||
| 
       // list of buttons that are reported as keys
 
   | 
||
| 
       static const struct {
 
   | 
||
| 
         uint8_t byte_offset;   // offset of the byte within the report which the button bit is in
 
   | 
||
| 
         uint8_t mask;          // bitmask of the button bit
 
   | 
||
| 
         uint8_t key_code[2];   // usb keycodes to be sent for joystick 0 and joystick 1
 
   | 
||
| 
       } button_map[] = {
 
   | 
||
| 
         { 4, 0x10, 0x3a, 0x3d }, /* START -> f1/f4 */
 
   | 
||
| 
         { 4, 0x20, 0x3b, 0x3e }, /* PAUSE -> f2/f5 */
 
   | 
||
| 
         { 4, 0x40, 0x3c, 0x3f }, /* RESET -> f3/f6 */
 
   | 
||
| 
         { 5, 0x01, 0x1e, 0x21 }, /*     1 ->  1/4  */
 
   | 
||
| 
         { 5, 0x02, 0x1f, 0x22 }, /*     2 ->  2/5  */
 
   | 
||
| 
         { 5, 0x04, 0x20, 0x23 }, /*     3 ->  3/6  */
 
   | 
||
| 
         { 5, 0x08, 0x14, 0x15 }, /*     4 ->  q/r  */
 
   | 
||
| 
         { 5, 0x10, 0x1a, 0x17 }, /*     5 ->  w/t  */
 
   | 
||
| 
         { 5, 0x20, 0x08, 0x1c }, /*     6 ->  e/y  */
 
   | 
||
| 
         { 5, 0x40, 0x04, 0x09 }, /*     7 ->  a/f  */
 
   | 
||
| 
         { 5, 0x80, 0x16, 0x0a }, /*     8 ->  s/g  */
 
   | 
||
| 
         { 6, 0x01, 0x07, 0x0b }, /*     9 ->  d/h  */
 
   | 
||
| 
         { 6, 0x02, 0x1d, 0x19 }, /*     * ->  z/v  */
 
   | 
||
| 
         { 6, 0x04, 0x1b, 0x05 }, /*     0 ->  x/b  */
 
   | 
||
| 
         { 6, 0x08, 0x06, 0x11 }, /*     # ->  c/n  */
 
   | 
||
| 
         { 0, 0x00, 0x00, 0x00 }  /* ----  end ---- */
 
   | 
||
| 
       };
 
   | 
||
| 
     | 
||
| 
       // keyboard events are only generated for the first
 
   | 
||
| 
       // two joysticks in the system
 
   | 
||
| 
       if(iface->jindex > 1) return;
 
   | 
||
| 
     | 
||
| 
       // build map of pressed keys
 
   | 
||
| 
       uint8_t i;
 
   | 
||
| 
       uint16_t keys = 0;
 
   | 
||
| 
       for(i=0;button_map[i].mask;i++) 
 
   | 
||
| 
         if(buf[button_map[i].byte_offset] & button_map[i].mask)
 
   | 
||
| 
           keys |= (1<<i);
 
   | 
||
| 
     | 
||
| 
       // check if keys have changed
 
   | 
||
| 
       if(iface->key_state != keys) {
 
   | 
||
| 
         uint8_t buf[6] = { 0,0,0,0,0,0 };
 
   | 
||
| 
         uint8_t p = 0;
 
   | 
||
| 
     | 
||
| 
         // report up to 6 pressed keys
 
   | 
||
| 
         for(i=0;(i<16)&&(p<6);i++) 
 
   | 
||
| 
           if(keys & (1<<i))
 
   | 
||
| 
     	buf[p++] = button_map[i].key_code[iface->jindex];
 
   | 
||
| 
     | 
||
| 
         //    iprintf("5200: %d %d %d %d %d %d\n", buf[0],buf[1],buf[2],buf[3],buf[4],buf[5]);
 
   | 
||
| 
     | 
||
| 
         // generate key events
 
   | 
||
| 
         event_keyboard(0x00, buf);
 
   | 
||
| 
     | 
||
| 
         // save current state of keys
 
   | 
||
| 
         iface->key_state = keys;
 
   | 
||
| 
       }
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     static uint8_t usb_hid_poll(usb_device_t *dev) {
 
   | 
||
| 
       usb_hid_info_t *info = &(dev->hid_info);
 
   | 
||
| 
       int8_t i;
 
   | 
||
| 
     | 
||
| 
       if (!info->bPollEnable)
 
   | 
||
| 
         return 0;
 
   | 
||
| 
     | 
||
| 
       for(i=0;i<info->bNumIfaces;i++) {
 
   | 
||
| 
         usb_hid_iface_info_t *iface = info->iface+i;
 
   | 
||
| 
     | 
||
| 
         if(iface->device_type != HID_DEVICE_UNKNOWN) {
 
   | 
||
| 
     | 
||
| 
           if (iface->qNextPollTime <= timer_get_msec()) {
 
   | 
||
| 
     	//      hid_debugf("poll %d...", iface->ep.epAddr);
 
   | 
||
| 
     | 
||
| 
     	uint16_t read = iface->ep.maxPktSize;
 
   | 
||
| 
     	uint8_t buf[iface->ep.maxPktSize];
 
   | 
||
| 
     	uint8_t rcode = 
 
   | 
||
| 
     	  usb_in_transfer(dev, &(iface->ep), &read, buf);
 
   | 
||
| 
     | 
||
| 
     	if (rcode) {
 
   | 
||
| 
     	  // MWW if (rcode != hrNAK)
 
   | 
||
| 
     	  if (rcode != OHS900_STATMASK_NAK_RXED)
 
   | 
||
| 
     	    hid_debugf("%s() error: %d", __FUNCTION__, rcode);
 
   | 
||
| 
     	} else {
 
   | 
||
| 
     | 
||
| 
     	  // successfully received some bytes
 
   | 
||
| 
     	  if(iface->has_boot_mode) {
 
   | 
||
| 
     	    if(iface->device_type == HID_DEVICE_MOUSE) {
 
   | 
||
| 
     	      // boot mouse needs at least three bytes
 
   | 
||
| 
     	      if(read >= 3) {
 
   | 
||
| 
     		// forward all three bytes to the user_io layer
 
   | 
||
| 
     		event_mouse(buf[0], buf[1], buf[2]);
 
   | 
||
| 
     	      }
 
   | 
||
| 
     	    }
 
   | 
||
| 
     | 
||
| 
     	    if(iface->device_type == HID_DEVICE_KEYBOARD) {
 
   | 
||
| 
     	      // boot kbd needs at least eight bytes
 
   | 
||
| 
     	      if(read >= 8) {
 
   | 
||
| 
     		event_keyboard(buf[0], buf+2);
 
   | 
||
| 
     	      }
 
   | 
||
| 
     	    }
 
   | 
||
| 
     	  }
 
   | 
||
| 
     | 
||
| 
     	  if(iface->device_type == HID_DEVICE_JOYSTICK) {
 
   | 
||
| 
     	    hid_config_t *conf = &iface->conf;
 
   | 
||
| 
     	    if(read >= conf->report_size) {
 
   | 
||
| 
     	      uint8_t jmap = 0;
 
   | 
||
| 
     	      uint16_t a[2];
 
   | 
||
| 
     	      uint8_t idx, i;
 
   | 
||
| 
     | 
||
| 
     	      // hid_debugf("Joystick data:"); hexdump(buf, read, 0);
 
   | 
||
| 
     | 
||
| 
     	      // two axes ...
 
   | 
||
| 
     	      for(i=0;i<2;i++) {
 
   | 
||
| 
     		a[i] = buf[conf->joystick.axis[i].byte_offset];
 
   | 
||
| 
     		if(conf->joystick.axis[i].size == 16)
 
   | 
||
| 
     		  a[i] += (buf[conf->joystick.axis[i].byte_offset+1])<<8;
 
   | 
||
| 
     | 
||
| 
     		// scale to 0 -> 255 range. 99% of the joysticks already deliver that
 
   | 
||
| 
     		if((conf->joystick.axis[i].logical.min != 0) ||
 
   | 
||
| 
     		   (conf->joystick.axis[i].logical.max != 255)) {
 
   | 
||
| 
     		  a[i] = ((a[i] - conf->joystick.axis[i].logical.min) * 255)/
 
   | 
||
| 
     		    (conf->joystick.axis[i].logical.max - 
 
   | 
||
| 
     		     conf->joystick.axis[i].logical.min);
 
   | 
||
| 
     		}
 
   | 
||
| 
     	      }
 
   | 
||
| 
     | 
||
| 
     	      if(a[0] <  64) jmap |= JOY_LEFT;
 
   | 
||
| 
     	      if(a[0] > 192) jmap |= JOY_RIGHT;
 
   | 
||
| 
     	      if(a[1] <  64) jmap |= JOY_UP;
 
   | 
||
| 
     	      if(a[1] > 192) jmap |= JOY_DOWN;
 
   | 
||
| 
     | 
||
| 
     	      //	      iprintf("JOY X:%d Y:%d\n", a[0], a[1]);
 
   | 
||
| 
     | 
||
| 
     	      // ... and four buttons
 
   | 
||
| 
     	      for(i=0;i<4;i++)
 
   | 
||
| 
     		if(buf[conf->joystick.button[i].byte_offset] & 
 
   | 
||
| 
     		   conf->joystick.button[i].bitmask) jmap |= (JOY_BTN1<<i);
 
   | 
||
| 
     | 
||
| 
     	      //	      iprintf("JOY D:%d\n", jmap);
 
   | 
||
| 
     | 
||
| 
     	      // swap joystick 0 and 1 since 1 is the one 
 
   | 
||
| 
     	      // used primarily on most systems
 
   | 
||
| 
     	      idx = iface->jindex;
 
   | 
||
| 
     	      if(idx == 0)      idx = 1;
 
   | 
||
| 
     	      else if(idx == 1) idx = 0;
 
   | 
||
| 
     | 
||
| 
     	      // check if joystick state has changed
 
   | 
||
| 
     	      if(jmap != iface->jmap) {
 
   | 
||
| 
     		//	      iprintf("jmap %d changed to %x\n", idx, jmap);
 
   | 
||
| 
     | 
||
| 
     		// and feed into joystick input system
 
   | 
||
| 
     		event_digital_joystick(idx, jmap);
 
   | 
||
| 
     		iface->jmap = jmap;
 
   | 
||
| 
     	      }
 
   | 
||
| 
     | 
||
| 
     	      // also send analog values
 
   | 
||
| 
     	      event_analog_joystick(idx, a[0]-128, a[1]-128);
 
   | 
||
| 
     | 
||
| 
     	      // do special 5200daptor treatment
 
   | 
||
| 
     	      if(iface->is_5200daptor)
 
   | 
||
| 
     		handle_5200daptor(iface, buf);
 
   | 
||
| 
     	    }
 
   | 
||
| 
     	  }
 
   | 
||
| 
     	}
 
   | 
||
| 
     	iface->qNextPollTime += iface->interval;   // poll at requested rate
 
   | 
||
| 
           }
 
   | 
||
| 
         }
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       return 0;
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     void hid_set_kbd_led(unsigned char led, bool on) {
 
   | 
||
| 
       // check if led state has changed
 
   | 
||
| 
       if( (on && !(kbd_led_state&led)) || (!on && (kbd_led_state&led))) {
 
   | 
||
| 
         if(on) kbd_led_state |=  led;
 
   | 
||
| 
         else   kbd_led_state &= ~led;
 
   | 
||
| 
     | 
||
| 
         // search for all keyboard interfaces on all hid devices
 
   | 
||
| 
         usb_device_t *dev = usb_get_devices();
 
   | 
||
| 
         int i;
 
   | 
||
| 
         for(i=0;i<USB_NUMDEVICES;i++) {
 
   | 
||
| 
           if(dev[i].bAddress && (dev[i].class == &usb_hid_class)) {
 
   | 
||
| 
     	// search for keyboard interfaces
 
   | 
||
| 
     	int j;
 
   | 
||
| 
     	for(j=0;j<MAX_IFACES;j++)
 
   | 
||
| 
     	  if(dev[i].hid_info.iface[j].device_type == HID_DEVICE_KEYBOARD)
 
   | 
||
| 
     	    hid_set_report(dev+i, dev[i].hid_info.iface[j].iface_idx, 2, 0, 1, &kbd_led_state);
 
   | 
||
| 
           }
 
   | 
||
| 
         }
 
   | 
||
| 
       }
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     const usb_device_class_config_t usb_hid_class = {
 
   | 
||
| 
       usb_hid_init, usb_hid_release, usb_hid_poll };  
 
   | 
||
| 
     | 
||
| firmware/usb/hid.h | ||
|---|---|---|
| 
     #ifndef HID_H
 
   | 
||
| 
     #define HID_H
 
   | 
||
| 
     | 
||
| 
     #include <stdbool.h>
 
   | 
||
| 
     #include <inttypes.h>
 
   | 
||
| 
     #include "hidparser.h"
 
   | 
||
| 
     | 
||
| 
     #define HID_LED_NUM_LOCK    0x01
 
   | 
||
| 
     #define HID_LED_CAPS_LOCK   0x02
 
   | 
||
| 
     #define HID_LED_SCROLL_LOCK 0x04
 
   | 
||
| 
     | 
||
| 
     /* HID constants. Not part of chapter 9 */
 
   | 
||
| 
     /* Class-Specific Requests */
 
   | 
||
| 
     #define HID_REQUEST_GET_REPORT      0x01
 
   | 
||
| 
     #define HID_REQUEST_GET_IDLE        0x02
 
   | 
||
| 
     #define HID_REQUEST_GET_PROTOCOL    0x03
 
   | 
||
| 
     #define HID_REQUEST_SET_REPORT      0x09
 
   | 
||
| 
     #define HID_REQUEST_SET_IDLE        0x0A
 
   | 
||
| 
     #define HID_REQUEST_SET_PROTOCOL    0x0B
 
   | 
||
| 
     | 
||
| 
     #define HID_DESCRIPTOR_HID		0x21
 
   | 
||
| 
     #define HID_DESCRIPTOR_REPORT		0x22
 
   | 
||
| 
     #define HID_DESRIPTOR_PHY		0x23
 
   | 
||
| 
     | 
||
| 
     /* Protocol Selection */
 
   | 
||
| 
     #define HID_BOOT_PROTOCOL                       0x00
 
   | 
||
| 
     #define HID_RPT_PROTOCOL                        0x01
 
   | 
||
| 
     | 
||
| 
     /* HID Interface Class SubClass Codes */
 
   | 
||
| 
     #define HID_BOOT_INTF_SUBCLASS          0x01
 
   | 
||
| 
     | 
||
| 
     #define HID_PROTOCOL_NONE           0x00
 
   | 
||
| 
     #define HID_PROTOCOL_KEYBOARD       0x01
 
   | 
||
| 
     #define HID_PROTOCOL_MOUSE          0x02
 
   | 
||
| 
     | 
||
| 
     #define HID_REQ_HIDREPORT     USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_INTERFACE
 
   | 
||
| 
     #define HID_REQ_HIDOUT        USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
 
   | 
||
| 
     | 
||
| 
     #define MAX_IFACES  2  // max supported interfaces per device. 2 to support kbd/mouse combos
 
   | 
||
| 
     | 
||
| 
     #define HID_DEVICE_UNKNOWN  0
 
   | 
||
| 
     #define HID_DEVICE_MOUSE    1
 
   | 
||
| 
     #define HID_DEVICE_KEYBOARD 2
 
   | 
||
| 
     #define HID_DEVICE_JOYSTICK 3
 
   | 
||
| 
     | 
||
| 
     typedef struct {
 
   | 
||
| 
       ep_t ep;    // interrupt endpoint info structure
 
   | 
||
| 
     | 
||
| 
       uint8_t iface_idx;
 
   | 
||
| 
       uint16_t report_desc_size;
 
   | 
||
| 
     | 
||
| 
       uint8_t device_type;
 
   | 
||
| 
       bool has_boot_mode: 1;     // device supports boot mode
 
   | 
||
| 
       bool is_5200daptor: 1;     // device is a 5200daptor with special key handling
 
   | 
||
| 
       uint16_t key_state;        // needed to detect key state changes in 5200daptor
 
   | 
||
| 
     | 
||
| 
       // additional info extracted from the report descriptor
 
   | 
||
| 
       // (currently only used for joysticks) 
 
   | 
||
| 
       uint8_t jmap;           // last reported joystick state
 
   | 
||
| 
       uint8_t jindex;         // joystick index
 
   | 
||
| 
       hid_config_t conf;
 
   | 
||
| 
     | 
||
| 
       uint8_t interval;
 
   | 
||
| 
       uint32_t qNextPollTime;     // next poll time
 
   | 
||
| 
     | 
||
| 
     } usb_hid_iface_info_t;
 
   | 
||
| 
     | 
||
| 
     typedef struct {
 
   | 
||
| 
       bool	   bPollEnable;	      // poll enable flag
 
   | 
||
| 
       uint8_t  bNumIfaces;
 
   | 
||
| 
     | 
||
| 
       usb_hid_iface_info_t iface[MAX_IFACES];
 
   | 
||
| 
     } usb_hid_info_t;
 
   | 
||
| 
     | 
||
| 
     /* HID descriptor */
 
   | 
||
| 
     typedef struct  {
 
   | 
||
| 
       uint8_t         bLength;
 
   | 
||
| 
       uint8_t         bDescriptorType;
 
   | 
||
| 
       uint16_t        bcdHID;                         // HID class specification release
 
   | 
||
| 
       uint8_t         bCountryCode;
 
   | 
||
| 
       uint8_t         bNumDescriptors;                // Number of additional class specific descriptors
 
   | 
||
| 
       uint8_t         bDescrType;                     // Type of class descriptor
 
   | 
||
| 
       uint8_t         wDescriptorLength[2];           // Total size of the Report descriptor
 
   | 
||
| 
     } __attribute__((packed)) usb_hid_descriptor_t;
 
   | 
||
| 
     | 
||
| 
     // interface to usb core
 
   | 
||
| 
     extern const usb_device_class_config_t usb_hid_class;
 
   | 
||
| 
     | 
||
| 
     void hid_set_kbd_led(unsigned char led, bool on);
 
   | 
||
| 
     uint8_t hid_get_joysticks(void);
 
   | 
||
| 
     | 
||
| 
     #endif // HID_H
 
   | 
||
| firmware/usb/hidparser.c | ||
|---|---|---|
| 
     // http://www.frank-zhao.com/cache/hid_tutorial_1.php
 
   | 
||
| 
     | 
||
| 
     #include <inttypes.h>
 
   | 
||
| 
     #include <stdbool.h>
 
   | 
||
| 
     #include <stdio.h>
 
   | 
||
| 
     | 
||
| 
     #include "hidparser.h"
 
   | 
||
| 
     #include "debug.h"
 
   | 
||
| 
     | 
||
| 
     #if 1
 
   | 
||
| 
     #define hidp_extreme_debugf(...) hidp_debugf(__VA_ARGS__)
 
   | 
||
| 
     #else
 
   | 
||
| 
     #define hidp_extreme_debugf(...)
 
   | 
||
| 
     #endif
 
   | 
||
| 
     | 
||
| 
     typedef struct {
 
   | 
||
| 
       uint8_t bSize: 2;
 
   | 
||
| 
       uint8_t bType: 2;
 
   | 
||
| 
       uint8_t bTag: 4;
 
   | 
||
| 
     } __attribute__((packed)) item_t;
 
   | 
||
| 
     | 
||
| 
     // flags for joystick components required
 
   | 
||
| 
     #define JOYSTICK_REQ_AXIS_X  0x01
 
   | 
||
| 
     #define JOYSTICK_REQ_AXIS_Y  0x02
 
   | 
||
| 
     #define JOYSTICK_REQ_BTN_0   0x04
 
   | 
||
| 
     #define JOYSTICK_COMPLETE    (JOYSTICK_REQ_AXIS_X | JOYSTICK_REQ_AXIS_Y | JOYSTICK_REQ_BTN_0)
 
   | 
||
| 
     | 
||
| 
     #define USAGE_PAGE_GENERIC_DESKTOP  1
 
   | 
||
| 
     #define USAGE_PAGE_SIMULATION       2
 
   | 
||
| 
     #define USAGE_PAGE_VR               3
 
   | 
||
| 
     #define USAGE_PAGE_SPORT            4
 
   | 
||
| 
     #define USAGE_PAGE_GAMING           5
 
   | 
||
| 
     #define USAGE_PAGE_GENERIC_DEVICE   6
 
   | 
||
| 
     #define USAGE_PAGE_KEYBOARD         7
 
   | 
||
| 
     #define USAGE_PAGE_LEDS             8
 
   | 
||
| 
     #define USAGE_PAGE_BUTTON           9
 
   | 
||
| 
     #define USAGE_PAGE_ORDINAL         10
 
   | 
||
| 
     #define USAGE_PAGE_TELEPHONY       11
 
   | 
||
| 
     #define USAGE_PAGE_CONSUMER        12
 
   | 
||
| 
     | 
||
| 
     | 
||
| 
     #define USAGE_POINTER   1
 
   | 
||
| 
     #define USAGE_MOUSE     2
 
   | 
||
| 
     #define USAGE_JOYSTICK  4
 
   | 
||
| 
     #define USAGE_GAMEPAD   5
 
   | 
||
| 
     #define USAGE_KEYBOARD  6
 
   | 
||
| 
     #define USAGE_KEYPAD    7
 
   | 
||
| 
     #define USAGE_MULTIAXIS 8
 
   | 
||
| 
     | 
||
| 
     #define USAGE_X       48
 
   | 
||
| 
     #define USAGE_Y       49
 
   | 
||
| 
     #define USAGE_Z       50
 
   | 
||
| 
     #define USAGE_WHEEL   56
 
   | 
||
| 
     | 
||
| 
     bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_config_t *conf) {
 
   | 
||
| 
       int8_t app_collection = 0;
 
   | 
||
| 
       int8_t phys_log_collection = 0;
 
   | 
||
| 
       uint8_t skip_collection = 0;
 
   | 
||
| 
       int8_t generic_desktop = -1;   // depth at which first gen_desk was found
 
   | 
||
| 
       uint8_t collection_depth = 0;
 
   | 
||
| 
     | 
||
| 
       uint8_t i;
 
   | 
||
| 
     | 
||
| 
       // 
 
   | 
||
| 
       uint8_t report_size, report_count;
 
   | 
||
| 
       uint16_t bit_count = 0, usage_count = 0;
 
   | 
||
| 
       uint16_t logical_minimum=0, logical_maximum=0;
 
   | 
||
| 
     | 
||
| 
       // mask used to check of all required components have been found, so
 
   | 
||
| 
       // that e.g. both axes and the button of a joystick are ready to be used
 
   | 
||
| 
       uint8_t setup_complete = 0;
 
   | 
||
| 
     | 
||
| 
       // joystick/mouse components
 
   | 
||
| 
       int8_t axis[2] = { -1, -1};
 
   | 
||
| 
       uint8_t btns = 0;
 
   | 
||
| 
     | 
||
| 
       conf->type = CONFIG_TYPE_NONE;
 
   | 
||
| 
     | 
||
| 
       while(rep_size) {
 
   | 
||
| 
         // extract short item
 
   | 
||
| 
         uint8_t tag = ((item_t*)rep)->bTag;
 
   | 
||
| 
         uint8_t type = ((item_t*)rep)->bType;
 
   | 
||
| 
         uint8_t size = ((item_t*)rep)->bSize;
 
   | 
||
| 
     | 
||
| 
         rep++;
 
   | 
||
| 
         rep_size--;   // one byte consumed
 
   | 
||
| 
     | 
||
| 
         uint32_t value = 0;
 
   | 
||
| 
         if(size) {      // size 1/2/3
 
   | 
||
| 
           value = *rep++;
 
   | 
||
| 
           rep_size--;
 
   | 
||
| 
         }
 
   | 
||
| 
     | 
||
| 
         if(size > 1) {  // size 2/3
 
   | 
||
| 
           value = (value & 0xff) + ((uint32_t)(*rep++)<<8);
 
   | 
||
| 
           rep_size--;
 
   | 
||
| 
         }
 
   | 
||
| 
     | 
||
| 
         if(size > 2) {  // size 3
 
   | 
||
| 
           value &= 0xffff;
 
   | 
||
| 
           value |= ((uint32_t)(*rep++)<<16);
 
   | 
||
| 
           value |= ((uint32_t)(*rep++)<<24);
 
   | 
||
| 
           rep_size-=2;
 
   | 
||
| 
         }
 
   | 
||
| 
     | 
||
| 
         //    hidp_extreme_debugf("Value = %d (%u)\n", value, value);
 
   | 
||
| 
     | 
||
| 
         // we are currently skipping an unknown/unsupported collection) 
 
   | 
||
| 
         if(skip_collection) {
 
   | 
||
| 
           if(!type) {  // main item
 
   | 
||
| 
     	// any new collection increases the depth of collections to skip
 
   | 
||
| 
     	if(tag == 10) {
 
   | 
||
| 
     	  skip_collection++;
 
   | 
||
| 
     	  collection_depth++;
 
   | 
||
| 
     	}
 
   | 
||
| 
     | 
||
| 
     	// any end collection decreases it
 
   | 
||
| 
     	if(tag == 12) {
 
   | 
||
| 
     	  skip_collection--;
 
   | 
||
| 
     	  collection_depth--;
 
   | 
||
| 
     | 
||
| 
     	  // leaving the depth the generic desktop was valid for
 
   | 
||
| 
     	  if(generic_desktop > collection_depth)
 
   | 
||
| 
     	    generic_desktop = -1;
 
   | 
||
| 
     	}
 
   | 
||
| 
           }
 
   | 
||
| 
     | 
||
| 
     | 
||
| 
         } else {
 
   | 
||
| 
           //      hidp_extreme_debugf("-> Item tag=%d type=%d size=%d\n", tag, type, size);
 
   | 
||
| 
     | 
||
| 
           switch(type) {
 
   | 
||
| 
           case 0:
 
   | 
||
| 
     	// main item
 
   | 
||
| 
     | 
||
| 
     	switch(tag) {
 
   | 
||
| 
     	case 8:
 
   | 
||
| 
     	  // 
 
   | 
||
| 
     	  if(btns) {
 
   | 
||
| 
     	    if(conf->type == CONFIG_TYPE_JOYSTICK) {
 
   | 
||
| 
     	      // scan for up to four buttons
 
   | 
||
| 
     	      char b;
 
   | 
||
| 
     	      for(b=0;b<4;b++) {
 
   | 
||
| 
     		if(report_count > b) {
 
   | 
||
| 
     		  uint16_t this_bit = bit_count+b;
 
   | 
||
| 
     | 
||
| 
     		  hidp_debugf("BUTTON%d @ %d (byte %d, mask %d)\n", b, 
 
   | 
||
| 
     			      this_bit, this_bit/8, 1 << (this_bit%8));
 
   | 
||
| 
     | 
||
| 
     		  conf->joystick.button[b].byte_offset = this_bit/8;
 
   | 
||
| 
     		  conf->joystick.button[b].bitmask = 1 << (this_bit%8);
 
   | 
||
| 
     		}
 
   | 
||
| 
     	      }
 
   | 
||
| 
     | 
||
| 
     	      // we found at least one button which is all we want to accept this as a valid 
 
   | 
||
| 
     	      // joystick
 
   | 
||
| 
     	      setup_complete |= JOYSTICK_REQ_BTN_0;
 
   | 
||
| 
     	    }
 
   | 
||
| 
     	  }
 
   | 
||
| 
     | 
||
| 
     	  // 
 
   | 
||
| 
     	  char c;
 
   | 
||
| 
     	  for(c=0;c<2;c++) {
 
   | 
||
| 
     	    if(axis[c] >= 0) {
 
   | 
||
| 
     	      uint16_t cnt = bit_count + report_size * axis[c];
 
   | 
||
| 
     	      hidp_debugf("  (%c-AXIS @ %d (byte %d))\n", 'X'+c,
 
   | 
||
| 
     		     cnt, cnt/8);
 
   | 
||
| 
     | 
||
| 
     	      // only 8/16 bit axes at byte boundaries are supported for
 
   | 
||
| 
     	      // joysticks
 
   | 
||
| 
     	      if((conf->type == CONFIG_TYPE_JOYSTICK) &&
 
   | 
||
| 
     		 ((report_size == 8) || (report_size == 16)) && ((cnt&7) == 0)) {
 
   | 
||
| 
     		// save in joystick config
 
   | 
||
| 
     		conf->joystick.axis[c].byte_offset = cnt/8;
 
   | 
||
| 
     		conf->joystick.axis[c].size = report_size;
 
   | 
||
| 
     		conf->joystick.axis[c].logical.min = logical_minimum;
 
   | 
||
| 
     		conf->joystick.axis[c].logical.max = logical_maximum;
 
   | 
||
| 
     		conf->joystick.axis[c].size = report_size;
 
   | 
||
| 
     		if(c==0) setup_complete |= JOYSTICK_REQ_AXIS_X;
 
   | 
||
| 
     		if(c==1) setup_complete |= JOYSTICK_REQ_AXIS_Y;
 
   | 
||
| 
     	      }
 
   | 
||
| 
     | 
||
| 
     	      if((report_size != 8) && (report_size != 16))
 
   | 
||
| 
     		hidp_debugf("Unsupported report size %d\n", report_size);
 
   | 
||
| 
     | 
||
| 
     	      if((cnt&7) != 0) 
 
   | 
||
| 
     		hidp_debugf("Unsupported bit offset %d\n", cnt&7);
 
   | 
||
| 
     	    }
 
   | 
||
| 
     	  }
 
   | 
||
| 
     | 
||
| 
     	  hidp_extreme_debugf("INPUT(%d)\n", value);
 
   | 
||
| 
     | 
||
| 
     	  // reset for next inputs
 
   | 
||
| 
     	  bit_count += report_count * report_size;
 
   | 
||
| 
     	  usage_count = 0;
 
   | 
||
| 
     	  btns = 0;
 
   | 
||
| 
     	  axis[0] = axis[1] = -1;
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	case 9:
 
   | 
||
| 
     	  hidp_extreme_debugf("OUTPUT(%d)\n", value);
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	case 11:
 
   | 
||
| 
     	  hidp_extreme_debugf("FEATURE(%d)\n", value);
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	case 10:
 
   | 
||
| 
     	  hidp_extreme_debugf("COLLECTION(%d)\n", value);
 
   | 
||
| 
     	  collection_depth++;
 
   | 
||
| 
     	  usage_count = 0;
 
   | 
||
| 
     | 
||
| 
     	  if(value == 1) {	   // app collection
 
   | 
||
| 
     	    hidp_extreme_debugf("  -> application\n");
 
   | 
||
| 
     	    app_collection++;
 
   | 
||
| 
     	  } else if(value == 0) {  // physical collection
 
   | 
||
| 
     	    hidp_extreme_debugf("  -> physical\n");
 
   | 
||
| 
     	    phys_log_collection++;
 
   | 
||
| 
     	  } else if(value == 2) {  // logical collection
 
   | 
||
| 
     	    hidp_extreme_debugf("  -> logical\n");
 
   | 
||
| 
     	    phys_log_collection++;
 
   | 
||
| 
     	  } else {
 
   | 
||
| 
     	    hidp_extreme_debugf("skipping unsupported collection\n");
 
   | 
||
| 
     	    skip_collection++;
 
   | 
||
| 
     	  }
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	case 12:
 
   | 
||
| 
     	  hidp_extreme_debugf("END_COLLECTION(%d)\n", value);
 
   | 
||
| 
     	  collection_depth--;
 
   | 
||
| 
     | 
||
| 
     	  // leaving the depth the generic desktop was valid for
 
   | 
||
| 
     	  if(generic_desktop > collection_depth)
 
   | 
||
| 
     	    generic_desktop = -1;
 
   | 
||
| 
     | 
||
| 
     	  if(phys_log_collection) {
 
   | 
||
| 
     	    hidp_extreme_debugf("  -> phys/log end\n");
 
   | 
||
| 
     	    phys_log_collection--;
 
   | 
||
| 
     	  } else if(app_collection) {
 
   | 
||
| 
     	    hidp_extreme_debugf("  -> app end\n");
 
   | 
||
| 
     	    app_collection--;
 
   | 
||
| 
     	  } else {
 
   | 
||
| 
     	    hidp_debugf(" -> unexpected\n");
 
   | 
||
| 
     	    return false;
 
   | 
||
| 
     	  }
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	default:
 
   | 
||
| 
     	  hidp_debugf("unexpected main item %d\n", tag);
 
   | 
||
| 
     	  return false;
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     	}
 
   | 
||
| 
     	break;
 
   | 
||
| 
     | 
||
| 
           case 1:
 
   | 
||
| 
     	// global item
 
   | 
||
| 
     	switch(tag) {
 
   | 
||
| 
     	case 0:
 
   | 
||
| 
     	  hidp_extreme_debugf("USAGE_PAGE(%d/0x%x)\n", value, value);
 
   | 
||
| 
     | 
||
| 
     	  if(value == USAGE_PAGE_KEYBOARD) {
 
   | 
||
| 
     	    hidp_extreme_debugf(" -> Keyboard\n");
 
   | 
||
| 
     	  } else if(value == USAGE_PAGE_GAMING) {
 
   | 
||
| 
     	    hidp_extreme_debugf(" -> Game device\n");
 
   | 
||
| 
     	  } else if(value == USAGE_PAGE_LEDS) {
 
   | 
||
| 
     	    hidp_extreme_debugf(" -> LEDs\n");
 
   | 
||
| 
     	  } else if(value == USAGE_PAGE_CONSUMER) {
 
   | 
||
| 
     	    hidp_extreme_debugf(" -> Consumer\n");
 
   | 
||
| 
     	  } else if(value == USAGE_PAGE_BUTTON) {
 
   | 
||
| 
     	    hidp_extreme_debugf(" -> Buttons\n");
 
   | 
||
| 
     	    btns = 1;
 
   | 
||
| 
     	  } else if(value == USAGE_PAGE_GENERIC_DESKTOP) {
 
   | 
||
| 
     	    hidp_extreme_debugf(" -> Generic Desktop\n");
 
   | 
||
| 
     | 
||
| 
     	    if(generic_desktop < 0)
 
   | 
||
| 
     	      generic_desktop = collection_depth;
 
   | 
||
| 
     	  } else
 
   | 
||
| 
     	    hidp_extreme_debugf(" -> UNSUPPORTED USAGE_PAGE\n");
 
   | 
||
| 
     | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	case 1:
 
   | 
||
| 
     	  hidp_extreme_debugf("LOGICAL_MINIMUM(%d/%d)\n", value, (int8_t)value);
 
   | 
||
| 
     	  logical_minimum = value;
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	case 2:
 
   | 
||
| 
     	  hidp_extreme_debugf("LOGICAL_MAXIMUM(%d)\n", value);
 
   | 
||
| 
     	  logical_maximum = value;
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	case 3:
 
   | 
||
| 
     	  hidp_extreme_debugf("PHYSICAL_MINIMUM(%d/%d)\n", value, (int8_t)value);
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	case 4:
 
   | 
||
| 
     	  hidp_extreme_debugf("PHYSICAL_MAXIMUM(%d)\n", value);
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	case 5:
 
   | 
||
| 
     	  hidp_extreme_debugf("UNIT_EXPONENT(%d)\n", value);
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	case 6:
 
   | 
||
| 
     	  hidp_extreme_debugf("UNIT(%d)\n", value);
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	case 7:
 
   | 
||
| 
     	  hidp_extreme_debugf("REPORT_SIZE(%d)\n", value);
 
   | 
||
| 
     	  report_size = value;
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	case 8:
 
   | 
||
| 
     	  hidp_extreme_debugf("REPORT_ID(%d)\n", value);
 
   | 
||
| 
     	  conf->report_id = value;
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	case 9:
 
   | 
||
| 
     	  hidp_extreme_debugf("REPORT_COUNT(%d)\n", value);
 
   | 
||
| 
     	  report_count = value;
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	default:
 
   | 
||
| 
     	  hidp_debugf("unexpected global item %d\n", tag);
 
   | 
||
| 
     	  return false;
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     	}
 
   | 
||
| 
     	break;
 
   | 
||
| 
     | 
||
| 
           case 2:
 
   | 
||
| 
     	// local item
 
   | 
||
| 
     	switch(tag) {
 
   | 
||
| 
     	case 0:
 
   | 
||
| 
     	  // we only support mice, keyboards and joysticks
 
   | 
||
| 
     	  hidp_extreme_debugf("USAGE(%d/0x%x)\n", value, value);
 
   | 
||
| 
     | 
||
| 
     	  if( !collection_depth && (value == USAGE_KEYBOARD)) {
 
   | 
||
| 
     	    // usage(keyboard) is always allowed
 
   | 
||
| 
     	    hidp_debugf(" -> Keyboard\n");
 
   | 
||
| 
     	    conf->type = CONFIG_TYPE_KEYBOARD;
 
   | 
||
| 
     	  } else if(!collection_depth && (value == USAGE_MOUSE)) {
 
   | 
||
| 
     	    // usage(mouse) is always allowed
 
   | 
||
| 
     	    hidp_debugf(" -> Mouse\n");
 
   | 
||
| 
     	    conf->type = CONFIG_TYPE_MOUSE;
 
   | 
||
| 
     	  } else if(!collection_depth && 
 
   | 
||
| 
     		    ((value == USAGE_GAMEPAD) || (value == USAGE_JOYSTICK))) {
 
   | 
||
| 
     	    hidp_extreme_debugf(" -> Gamepad/Joystick\n");
 
   | 
||
| 
     	    hidp_debugf("Gamepad/Joystick usage found\n");
 
   | 
||
| 
     	    conf->type = CONFIG_TYPE_JOYSTICK;
 
   | 
||
| 
     	  } else if(value == USAGE_POINTER && app_collection) {
 
   | 
||
| 
     	    // usage(pointer) is allowed within the application collection
 
   | 
||
| 
     | 
||
| 
     	    hidp_debugf(" -> Pointer\n");
 
   | 
||
| 
     | 
||
| 
     	  } else if((value == USAGE_X || value == USAGE_Y) && app_collection) {
 
   | 
||
| 
     	    // usage(x) and usage(y) are allowed within the app collection
 
   | 
||
| 
     	    hidp_extreme_debugf(" -> axis usage\n");
 
   | 
||
| 
     | 
||
| 
     	    // we support x and y axis on joysticks
 
   | 
||
| 
     	    if(conf->type == CONFIG_TYPE_JOYSTICK) {
 
   | 
||
| 
     	      if(value == USAGE_X) {
 
   | 
||
| 
     		hidp_extreme_debugf("JOYSTICK: found x axis @ %d\n", usage_count);
 
   | 
||
| 
     		axis[0] = usage_count;
 
   | 
||
| 
     	      }
 
   | 
||
| 
     	      if(value == USAGE_Y) {
 
   | 
||
| 
     		hidp_extreme_debugf("JOYSTICK: found y axis @ %d\n", usage_count);
 
   | 
||
| 
     		axis[1] = usage_count;
 
   | 
||
| 
     	      }
 
   | 
||
| 
     	    }
 
   | 
||
| 
     	  } else {
 
   | 
||
| 
     	    hidp_extreme_debugf(" -> UNSUPPORTED USAGE\n");
 
   | 
||
| 
     	    //	    return false;
 
   | 
||
| 
     	  }
 
   | 
||
| 
     | 
||
| 
     	  usage_count++;
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	case 1:
 
   | 
||
| 
     	  hidp_extreme_debugf("USAGE_MINIMUM(%d)\n", value);
 
   | 
||
| 
     	  usage_count -= (value-1);
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	case 2:
 
   | 
||
| 
     	  hidp_extreme_debugf("USAGE_MAXIMUM(%d)\n", value);
 
   | 
||
| 
     	  usage_count += value;
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     | 
||
| 
     	default:
 
   | 
||
| 
     	  hidp_extreme_debugf("unexpected local item %d\n", tag);
 
   | 
||
| 
     	  //	  return false;
 
   | 
||
| 
     	  break;
 
   | 
||
| 
     	}
 
   | 
||
| 
     	break;
 
   | 
||
| 
     | 
||
| 
           default:
 
   | 
||
| 
     	// reserved
 
   | 
||
| 
     	hidp_extreme_debugf("unexpected resreved item %d\n", tag);
 
   | 
||
| 
     	//	return false;
 
   | 
||
| 
     	break;
 
   | 
||
| 
           }
 
   | 
||
| 
         }
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       hidp_debugf("total bit count: %d (%d bytes, %d bits)\n", 
 
   | 
||
| 
     	 bit_count, bit_count/8, bit_count%8);
 
   | 
||
| 
     | 
||
| 
       conf->report_size = bit_count/8;
 
   | 
||
| 
     | 
||
| 
       // check if something useful was detected
 
   | 
||
| 
       if(conf->type == CONFIG_TYPE_JOYSTICK) {
 
   | 
||
| 
         if(setup_complete == JOYSTICK_COMPLETE) {
 
   | 
||
| 
           hidp_debugf("Joystick ok\n");
 
   | 
||
| 
           return true;
 
   | 
||
| 
         }
 
   | 
||
| 
     | 
||
| 
         hidp_debugf("Ignoring incomplete joystick %x\n", setup_complete);
 
   | 
||
| 
       } else
 
   | 
||
| 
         hidp_debugf("No joystick\n");
 
   | 
||
| 
     | 
||
| 
       return false;
 
   | 
||
| 
     }
 
   | 
||
| firmware/usb/hidparser.h | ||
|---|---|---|
| 
     #ifndef HIDPARSER_H
 
   | 
||
| 
     #define HIDPARSER_H
 
   | 
||
| 
     | 
||
| 
     #define CONFIG_TYPE_NONE     0
 
   | 
||
| 
     #define CONFIG_TYPE_MOUSE    1
 
   | 
||
| 
     #define CONFIG_TYPE_KEYBOARD 2
 
   | 
||
| 
     #define CONFIG_TYPE_JOYSTICK 3
 
   | 
||
| 
     | 
||
| 
     // currently only joysticks are supported
 
   | 
||
| 
     typedef struct {
 
   | 
||
| 
       uint8_t type: 2;             // CONFIG_TYPE_...
 
   | 
||
| 
       uint8_t report_id;
 
   | 
||
| 
       uint8_t report_size;
 
   | 
||
| 
     | 
||
| 
       union {
 
   | 
||
| 
         struct {
 
   | 
||
| 
           struct {
 
   | 
||
| 
     	uint8_t byte_offset;
 
   | 
||
| 
     	uint8_t size;          // 8 or 16 bits supported
 
   | 
||
| 
     	struct {
 
   | 
||
| 
     	  uint16_t min;
 
   | 
||
| 
     	  uint16_t max;
 
   | 
||
| 
     	} logical;
 
   | 
||
| 
           } axis[2];               // x and y axis
 
   | 
||
| 
     | 
||
| 
           struct {
 
   | 
||
| 
     	uint8_t byte_offset;
 
   | 
||
| 
     	uint8_t bitmask;
 
   | 
||
| 
           } button[4];             // 4 buttons
 
   | 
||
| 
         } joystick;
 
   | 
||
| 
       };
 
   | 
||
| 
     } hid_config_t;
 
   | 
||
| 
     | 
||
| 
     bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_config_t *conf);
 
   | 
||
| 
     | 
||
| 
     #endif // HIDPARSER_H
 
   | 
||
| firmware/usb/hub.c | ||
|---|---|---|
| 
     #include <stdio.h>
 
   | 
||
| 
     | 
||
| 
     #include "usb.h"
 
   | 
||
| 
     #include "timer.h"
 
   | 
||
| 
     #include "debug.h"
 
   | 
||
| 
     | 
||
| 
     static uint8_t usb_hub_clear_hub_feature(usb_device_t *dev, uint8_t fid )  {
 
   | 
||
| 
       return( usb_ctrl_req( dev, USB_HUB_REQ_CLEAR_HUB_FEATURE, 
 
   | 
||
| 
            USB_REQUEST_CLEAR_FEATURE, fid, 0, 0, 0, NULL));
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     // Clear Port Feature
 
   | 
||
| 
     static uint8_t usb_hub_clear_port_feature(usb_device_t *dev, uint8_t fid, uint8_t port, uint8_t sel )  {
 
   | 
||
| 
       return( usb_ctrl_req( dev , USB_HUB_REQ_CLEAR_PORT_FEATURE, 
 
   | 
||
| 
            USB_REQUEST_CLEAR_FEATURE, fid, 0, ((0x0000|port)|(sel<<8)), 0, NULL));
 
   | 
||
| 
     }
 
   | 
||
| 
     // Get Hub Descriptor
 
   | 
||
| 
     static uint8_t usb_hub_get_hub_descriptor(usb_device_t *dev, uint8_t index, 
 
   | 
||
| 
     					  uint16_t nbytes, usb_hub_descriptor_t *dataptr )  {
 
   | 
||
| 
       return( usb_ctrl_req( dev, USB_HUB_REQ_GET_HUB_DESCRIPTOR, 
 
   | 
||
| 
     			USB_REQUEST_GET_DESCRIPTOR, index, 0x29, 0, nbytes, (uint8_t*)dataptr));
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     // Set Port Feature
 
   | 
||
| 
     static uint8_t usb_hub_set_port_feature(usb_device_t *dev, uint8_t fid, uint8_t port, uint8_t sel ) {
 
   | 
||
| 
       return( usb_ctrl_req( dev, USB_HUB_REQ_SET_PORT_FEATURE, 
 
   | 
||
| 
            USB_REQUEST_SET_FEATURE, fid, 0, (((0x0000|sel)<<8)|port), 0, NULL));
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     // Get Port Status
 
   | 
||
| 
     static uint8_t usb_hub_get_port_status(usb_device_t *dev, uint8_t port, uint16_t nbytes, uint8_t* dataptr )  {
 
   | 
||
| 
       return( usb_ctrl_req( dev, USB_HUB_REQ_GET_PORT_STATUS, 
 
   | 
||
| 
            USB_REQUEST_GET_STATUS, 0, 0, port, nbytes, dataptr));
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     static uint8_t usb_hub_init(usb_device_t *dev) {
 
   | 
||
| 
       iprintf("%s()\n", __FUNCTION__);
 
   | 
||
| 
     | 
||
| 
       uint8_t rcode;
 
   | 
||
| 
       uint8_t i;
 
   | 
||
| 
     | 
||
| 
       usb_hub_info_t *info = &(dev->hub_info);
 
   | 
||
| 
     | 
||
| 
       union {
 
   | 
||
| 
         usb_device_descriptor_t dev_desc;
 
   | 
||
| 
         usb_configuration_descriptor_t conf_desc;
 
   | 
||
| 
         usb_hub_descriptor_t hub_desc;
 
   | 
||
| 
       } buf;
 
   | 
||
| 
     | 
||
| 
       // reset status
 
   | 
||
| 
       info->bNbrPorts = 0; 
 
   | 
||
| 
       info->qNextPollTime = 0;
 
   | 
||
| 
       info->bPollEnable = false;
 
   | 
||
| 
     | 
||
| 
       info->ep.epAddr	= 1;
 
   | 
||
| 
       info->ep.maxPktSize	= 8;  //kludge
 
   | 
||
| 
       info->ep.epAttribs     = 0;
 
   | 
||
| 
       info->ep.bmNakPower	= USB_NAK_NOWAIT;
 
   | 
||
| 
     | 
||
| 
       rcode = usb_get_dev_descr( dev, 8, &buf.dev_desc );
 
   | 
||
| 
       if( rcode ) {
 
   | 
||
| 
         puts("failed to get device descriptor 1");
 
   | 
||
| 
         return rcode;
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       // Extract device class from device descriptor
 
   | 
||
| 
       // If device class is not a hub return
 
   | 
||
| 
       if (buf.dev_desc.bDeviceClass != USB_CLASS_HUB) {
 
   | 
||
| 
         puts("not a hub!");    
 
   | 
||
| 
         return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       // try to re-read full device descriptor from newly assigned address
 
   | 
||
| 
       if(rcode = usb_get_dev_descr( dev, sizeof(usb_device_descriptor_t), &buf.dev_desc )) {
 
   | 
||
| 
         puts("failed to get device descriptor 2");
 
   | 
||
| 
         return rcode;
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       // Get hub descriptor
 
   | 
||
| 
       rcode = usb_hub_get_hub_descriptor(dev, 0, 8, &buf.hub_desc);
 
   | 
||
| 
     | 
||
| 
       if (rcode) {
 
   | 
||
| 
         puts("failed to get hub descriptor");
 
   | 
||
| 
         return rcode;
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       // Save number of ports for future use
 
   | 
||
| 
       info->bNbrPorts = buf.hub_desc.bNbrPorts;
 
   | 
||
| 
     | 
||
| 
       // Read configuration Descriptor in Order To Obtain Proper Configuration Value
 
   | 
||
| 
       rcode = usb_get_conf_descr(dev, sizeof(usb_configuration_descriptor_t), 0, &buf.conf_desc);
 
   | 
||
| 
       if (rcode) {
 
   | 
||
| 
         puts("failed to read configuration descriptor");
 
   | 
||
| 
         return rcode;
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       // Set Configuration Value
 
   | 
||
| 
       rcode = usb_set_conf(dev, buf.conf_desc.bConfigurationValue);
 
   | 
||
| 
       if (rcode) {
 
   | 
||
| 
         iprintf("failed to set configuration to %d\n", buf.conf_desc.bConfigurationValue);
 
   | 
||
| 
         return rcode;
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       // Power on all ports
 
   | 
||
| 
       for (i=1; i<=info->bNbrPorts; i++)
 
   | 
||
| 
         usb_hub_set_port_feature(dev, HUB_FEATURE_PORT_POWER, i, 0);	// HubPortPowerOn(i);
 
   | 
||
| 
     | 
||
| 
       if(!dev->parent)
 
   | 
||
| 
         usb_SetHubPreMask();
 
   | 
||
| 
     | 
||
| 
       info->bPollEnable = true;
 
   | 
||
| 
     | 
||
| 
       return 0;
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     static uint8_t usb_hub_release(usb_device_t *dev) {
 
   | 
||
| 
       puts(__FUNCTION__);
 
   | 
||
| 
     | 
||
| 
       // root hub unplugged
 
   | 
||
| 
       if(!dev->parent)
 
   | 
||
| 
         usb_ResetHubPreMask();
 
   | 
||
| 
     | 
||
| 
       return 0;
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     static void usb_hub_show_port_status(uint8_t port, uint16_t status, uint16_t changed) {
 
   | 
||
| 
       iprintf("Status of port %d:\n", port);
 
   | 
||
| 
     | 
||
| 
       if(status & USB_HUB_PORT_STATUS_PORT_CONNECTION)    puts(" connected");
 
   | 
||
| 
       if(status & USB_HUB_PORT_STATUS_PORT_ENABLE)        puts(" enabled");
 
   | 
||
| 
       if(status & USB_HUB_PORT_STATUS_PORT_SUSPEND)       puts(" suspended");
 
   | 
||
| 
       if(status & USB_HUB_PORT_STATUS_PORT_OVER_CURRENT)  puts(" over current");
 
   | 
||
| 
       if(status & USB_HUB_PORT_STATUS_PORT_RESET)         puts(" reset");
 
   | 
||
| 
       if(status & USB_HUB_PORT_STATUS_PORT_POWER)         puts(" powered");
 
   | 
||
| 
       if(status & USB_HUB_PORT_STATUS_PORT_LOW_SPEED)     puts(" low speed");
 
   | 
||
| 
       if(status & USB_HUB_PORT_STATUS_PORT_HIGH_SPEED)    puts(" high speed");
 
   | 
||
| 
       if(status & USB_HUB_PORT_STATUS_PORT_TEST)          puts(" test");
 
   | 
||
| 
       if(status & USB_HUB_PORT_STATUS_PORT_INDICATOR)     puts(" indicator");
 
   | 
||
| 
     | 
||
| 
       iprintf("Changes on port %d:\n", port);
 
   | 
||
| 
       if(changed & USB_HUB_PORT_STATUS_PORT_CONNECTION)   puts(" connected");
 
   | 
||
| 
       if(changed & USB_HUB_PORT_STATUS_PORT_ENABLE)       puts(" enabled");
 
   | 
||
| 
       if(changed & USB_HUB_PORT_STATUS_PORT_SUSPEND)      puts(" suspended");
 
   | 
||
| 
       if(changed & USB_HUB_PORT_STATUS_PORT_OVER_CURRENT) puts(" over current");
 
   | 
||
| 
       if(changed & USB_HUB_PORT_STATUS_PORT_RESET)        puts(" reset");
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     static uint8_t usb_hub_port_status_change(usb_device_t *dev, uint8_t port, hub_event_t evt) {
 
   | 
||
| 
       usb_hub_info_t *info = &(dev->hub_info);
 
   | 
||
| 
     | 
||
| 
       iprintf("status change on port %d, 0x%x\n", port, evt.bmEvent);
 
   | 
||
| 
       usb_hub_show_port_status(port, evt.bmStatus, evt.bmChange);
 
   | 
||
| 
     | 
||
| 
       static bool bResetInitiated = false;
 
   | 
||
| 
     | 
||
| 
       switch (evt.bmEvent) {
 
   | 
||
| 
         // Device connected event
 
   | 
||
| 
       case USB_HUB_PORT_EVENT_CONNECT:
 
   | 
||
| 
       case USB_HUB_PORT_EVENT_LS_CONNECT:
 
   | 
||
| 
         iprintf(" dev %x: port %d connect!\n", dev->bAddress, port);
 
   | 
||
| 
     | 
||
| 
         if (bResetInitiated) {
 
   | 
||
| 
           iprintf("reset already in progress\n");
 
   | 
||
| 
           return 0;
 
   | 
||
| 
         }
 
   | 
||
| 
     | 
||
| 
         //    timer_delay_msec(100);
 
   | 
||
| 
     | 
||
| 
         iprintf("resetting port %d\n", port);
 
   | 
||
| 
         usb_hub_clear_port_feature(dev, HUB_FEATURE_C_PORT_ENABLE, port, 0);
 
   | 
||
| 
         usb_hub_clear_port_feature(dev, HUB_FEATURE_C_PORT_CONNECTION, port, 0);
 
   | 
||
| 
         usb_hub_set_port_feature(dev, HUB_FEATURE_PORT_RESET, port, 0);	
 
   | 
||
| 
         bResetInitiated = true;
 
   | 
||
| 
         return HUB_ERROR_PORT_HAS_BEEN_RESET;
 
   | 
||
| 
     | 
||
| 
         // Device disconnected event
 
   | 
||
| 
       case USB_HUB_PORT_EVENT_DISCONNECT:
 
   | 
||
| 
         iprintf(" port %d disconnect!\n", port);
 
   | 
||
| 
     | 
||
| 
         usb_hub_clear_port_feature(dev, HUB_FEATURE_C_PORT_ENABLE, port, 0);
 
   | 
||
| 
         usb_hub_clear_port_feature(dev, HUB_FEATURE_C_PORT_CONNECTION, port, 0);
 
   | 
||
| 
         bResetInitiated = false;
 
   | 
||
| 
     | 
||
| 
         usb_release_device(dev->bAddress, port);
 
   | 
||
| 
     | 
||
| 
         return 0;
 
   | 
||
| 
     | 
||
| 
         // Reset complete event
 
   | 
||
| 
       case USB_HUB_PORT_EVENT_RESET_COMPLETE:
 
   | 
||
| 
       case USB_HUB_PORT_EVENT_LS_RESET_COMPLETE:
 
   | 
||
| 
         iprintf(" port %d reset complete!\n", port);
 
   | 
||
| 
         usb_hub_clear_port_feature(dev, HUB_FEATURE_C_PORT_RESET, port, 0);
 
   | 
||
| 
         usb_hub_clear_port_feature(dev, HUB_FEATURE_C_PORT_CONNECTION, port, 0);
 
   | 
||
| 
     | 
||
| 
         usb_configure(dev->bAddress, port, 
 
   | 
||
| 
     	  (evt.bmStatus & USB_HUB_PORT_STATUS_PORT_LOW_SPEED)!=0 );
 
   | 
||
| 
     | 
||
| 
         bResetInitiated = false;
 
   | 
||
| 
         break;
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       return 0;
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     static uint8_t usb_hub_check_hub_status(usb_device_t *dev, uint8_t ports) {
 
   | 
||
| 
       usb_hub_info_t *info = &(dev->hub_info);
 
   | 
||
| 
     | 
||
| 
       uint8_t	rcode;
 
   | 
||
| 
       uint8_t	buf[8];
 
   | 
||
| 
       uint16_t	read = 1;
 
   | 
||
| 
     | 
||
| 
       //   iprintf("%s(addr=%x)\n", __FUNCTION__, dev->bAddress);
 
   | 
||
| 
     | 
||
| 
       rcode = usb_in_transfer(dev, &(info->ep), &read, buf);
 
   | 
||
| 
       if(rcode)
 
   | 
||
| 
         return rcode;
 
   | 
||
| 
     | 
||
| 
       uint8_t port, mask;
 
   | 
||
| 
       for(port=1,mask=0x02; port<8; mask<<=1, port++) {
 
   | 
||
| 
         if (buf[0] & mask) {
 
   | 
||
| 
           hub_event_t evt;
 
   | 
||
| 
           evt.bmEvent = 0;
 
   | 
||
| 
     | 
||
| 
           rcode = usb_hub_get_port_status(dev, port, sizeof(evt.evtBuff), evt.evtBuff);
 
   | 
||
| 
           if (rcode)
 
   | 
||
| 
     	continue;
 
   | 
||
| 
     | 
||
| 
           rcode = usb_hub_port_status_change(dev, port, evt);
 
   | 
||
| 
     | 
||
| 
           if (rcode == HUB_ERROR_PORT_HAS_BEEN_RESET)
 
   | 
||
| 
     	return 0;
 
   | 
||
| 
     | 
||
| 
           if (rcode)
 
   | 
||
| 
     	return rcode;
 
   | 
||
| 
         }
 
   | 
||
| 
       } // for
 
   | 
||
| 
     | 
||
| 
       for (port=1; port<=ports; port++) {
 
   | 
||
| 
         hub_event_t	evt;
 
   | 
||
| 
         evt.bmEvent = 0;
 
   | 
||
| 
     | 
||
| 
         rcode = usb_hub_get_port_status(dev, port, 4, evt.evtBuff);
 
   | 
||
| 
         if (rcode)
 
   | 
||
| 
           continue;
 
   | 
||
| 
     | 
||
| 
         if ((evt.bmStatus & USB_HUB_PORT_STATE_CHECK_DISABLED) != USB_HUB_PORT_STATE_DISABLED)
 
   | 
||
| 
           continue;
 
   | 
||
| 
     | 
||
| 
         // Emulate connection event for the port
 
   | 
||
| 
         evt.bmChange |= USB_HUB_PORT_STATUS_PORT_CONNECTION;
 
   | 
||
| 
     | 
||
| 
         rcode = usb_hub_port_status_change(dev, port, evt);
 
   | 
||
| 
         if (rcode == HUB_ERROR_PORT_HAS_BEEN_RESET)
 
   | 
||
| 
           return 0;
 
   | 
||
| 
     | 
||
| 
         if (rcode)
 
   | 
||
| 
           return rcode;
 
   | 
||
| 
       }
 
   | 
||
| 
       return 0;
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     static uint8_t usb_hub_poll(usb_device_t *dev) {
 
   | 
||
| 
       usb_hub_info_t *info = &(dev->hub_info);
 
   | 
||
| 
     | 
||
| 
       uint8_t rcode = 0;
 
   | 
||
| 
     | 
||
| 
       if (!info->bPollEnable)
 
   | 
||
| 
         return 0;
 
   | 
||
| 
     | 
||
| 
       if (info->qNextPollTime <= timer_get_msec()) {
 
   | 
||
| 
         rcode = usb_hub_check_hub_status(dev, info->bNbrPorts);
 
   | 
||
| 
         info->qNextPollTime = timer_get_msec() + 100;   // poll 10 times a second
 
   | 
||
| 
       }
 
   | 
||
| 
     | 
||
| 
       return rcode;
 
   | 
||
| 
     }
 
   | 
||
| 
     | 
||
| 
     const usb_device_class_config_t usb_hub_class = {
 
   | 
||
| 
       usb_hub_init, usb_hub_release, usb_hub_poll };  
 
   | 
||
| 
     | 
||
| firmware/usb/hub.h | ||
|---|---|---|
| 
     #ifndef HUB_H
 
   | 
||
| 
     #define HUB_H
 
   | 
||
| 
     | 
||
| 
     #include <stdbool.h>
 
   | 
||
| 
     #include <inttypes.h>
 
   | 
||
| 
     | 
||
| 
     typedef struct {
 
   | 
||
| 
       uint8_t  bNbrPorts;	    // number of ports
 
   | 
||
| 
       uint32_t qNextPollTime;   // next poll time
 
   | 
||
| 
       bool	   bPollEnable;	    // poll enable flag
 
   | 
||
| 
       ep_t ep;	            // interrupt endpoint info structure
 
   | 
||
| 
     } usb_hub_info_t;
 
   | 
||
| 
     | 
||
| 
     // Hub Requests
 
   | 
||
| 
     #define USB_HUB_REQ_CLEAR_HUB_FEATURE		USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
 
   | 
||
| 
     #define USB_HUB_REQ_CLEAR_PORT_FEATURE	USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
 
   | 
||
| 
     #define USB_HUB_REQ_CLEAR_TT_BUFFER		USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
 
   | 
||
| 
     #define USB_HUB_REQ_GET_HUB_DESCRIPTOR	USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
 
   | 
||
| 
     #define USB_HUB_REQ_GET_HUB_STATUS		USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
 
   | 
||
| 
     #define USB_HUB_REQ_GET_PORT_STATUS		USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
 
   | 
||
| 
     #define USB_HUB_REQ_RESET_TT			USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
 
   | 
||
| 
     #define USB_HUB_REQ_SET_HUB_DESCRIPTOR	USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
 
   | 
||
| 
     #define USB_HUB_REQ_SET_HUB_FEATURE		USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
 
   | 
||
| 
     #define USB_HUB_REQ_SET_PORT_FEATURE		USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
 
   | 
||
| 
     #define USB_HUB_REQ_GET_TT_STATE		USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
 
   | 
||
Untested support for usbhostslave from opencores. Based on working usb code in mist.