diff --git a/sw/Makefile b/sw/Makefile index 37f22c5..50bc795 100644 --- a/sw/Makefile +++ b/sw/Makefile @@ -15,6 +15,7 @@ LISTS=lists TESTS=tests SRCS=$(wildcard *.s) $(wildcard *.c) +SRCS+=$(wildcard usb/*.c) OBJS+=$(patsubst %.s,%.o,$(filter %s,$(SRCS))) OBJS+=$(patsubst %.c,%.o,$(filter %c,$(SRCS))) @@ -45,8 +46,8 @@ $(BIN): $(OBJS) $(CC) $(CFLAGS) -l $(LISTS)/$<.list -c $< -o $@ $(LISTS): - mkdir $(LISTS) - mkdir $(LISTS)/$(TESTS) + mkdir -p $(addprefix $(LISTS)/,$(sort $(dir $(SRCS)))) + mkdir $(LISTS)/$(sort $(dir $(TEST_SRCS))) .PHONY: clean clean: diff --git a/sw/gpio.h b/sw/gpio.h new file mode 100644 index 0000000..bfe97bc --- /dev/null +++ b/sw/gpio.h @@ -0,0 +1,9 @@ +#ifndef _GPIO_H +#define _GPIO_H + +#include + +#define USB_GPIO (uint8_t*)0x7ff1 +extern uint8_t* usb_gpio; + +#endif \ No newline at end of file diff --git a/sw/io.h b/sw/io.h new file mode 100644 index 0000000..b15abe8 --- /dev/null +++ b/sw/io.h @@ -0,0 +1,9 @@ +#ifndef _IO_H +#define _IO_H + +#include + +#define USB_GPIO 0x7ff1 +extern uint8_t* usb_gpio; + +#endif diff --git a/sw/usb/GenericMacros.h b/sw/usb/GenericMacros.h new file mode 100644 index 0000000..c980123 --- /dev/null +++ b/sw/usb/GenericMacros.h @@ -0,0 +1,9 @@ +/* Generic macros */ + +/* Word <> two chars. Works both ways */ +#define LOBYTE(x) ((char*)(&(x)))[0] +#define HIBYTE(x) ((char*)(&(x)))[1] + +/* Bit set/clear */ +#define bitset(var, bitno) ((var) |= 1 << (bitno)) +#define bitclr(var, bitno) ((var) &= ~(1 << (bitno))) diff --git a/sw/usb/GenericTypeDefs.h b/sw/usb/GenericTypeDefs.h new file mode 100644 index 0000000..6d0ba52 --- /dev/null +++ b/sw/usb/GenericTypeDefs.h @@ -0,0 +1,115 @@ +//Modified version of Generic Types as included by Microchip C18 + +#ifndef __GENERIC_TYPE_DEFS_H_ +#define __GENERIC_TYPE_DEFS_H_ + +typedef enum _BOOL { + FALSE = 0, TRUE +} BOOL; // Undefined size + +#define ON TRUE +#define OFF FALSE + +typedef unsigned char BYTE; // 8-bit unsigned +typedef unsigned short int WORD; // 16-bit unsigned +typedef unsigned long DWORD; // 32-bit unsigned +typedef signed char CHAR; // 8-bit signed +typedef signed short int SHORT; // 16-bit signed +typedef signed long LONG; // 32-bit signed + +typedef union _BYTE_VAL { + BYTE Val; + struct { + unsigned int b0 :1; + unsigned int b1 :1; + unsigned int b2 :1; + unsigned int b3 :1; + unsigned int b4 :1; + unsigned int b5 :1; + unsigned int b6 :1; + unsigned int b7 :1; + } bits; +} BYTE_VAL; + +typedef union _WORD_VAL { + WORD Val; + BYTE v[2]; + struct { + BYTE LB; + BYTE HB; + } byte; + struct { + unsigned int b0 :1; + unsigned int b1 :1; + unsigned int b2 :1; + unsigned int b3 :1; + unsigned int b4 :1; + unsigned int b5 :1; + unsigned int b6 :1; + unsigned int b7 :1; + unsigned int b8 :1; + unsigned int b9 :1; + unsigned int b10 :1; + unsigned int b11 :1; + unsigned int b12 :1; + unsigned int b13 :1; + unsigned int b14 :1; + unsigned int b15 :1; + } bits; +} WORD_VAL; + +typedef union _DWORD_VAL { + DWORD Val; + WORD w[2]; + BYTE v[4]; + struct { + WORD LW; + WORD HW; + } word; + struct { + BYTE LB; + BYTE HB; + BYTE UB; + BYTE MB; + } byte; + struct { + unsigned int b0 :1; + unsigned int b1 :1; + unsigned int b2 :1; + unsigned int b3 :1; + unsigned int b4 :1; + unsigned int b5 :1; + unsigned int b6 :1; + unsigned int b7 :1; + unsigned int b8 :1; + unsigned int b9 :1; + unsigned int b10 :1; + unsigned int b11 :1; + unsigned int b12 :1; + unsigned int b13 :1; + unsigned int b14 :1; + unsigned int b15 :1; + unsigned int b16 :1; + unsigned int b17 :1; + unsigned int b18 :1; + unsigned int b19 :1; + unsigned int b20 :1; + unsigned int b21 :1; + unsigned int b22 :1; + unsigned int b23 :1; + unsigned int b24 :1; + unsigned int b25 :1; + unsigned int b26 :1; + unsigned int b27 :1; + unsigned int b28 :1; + unsigned int b29 :1; + unsigned int b30 :1; + unsigned int b31 :1; + } bits; +} DWORD_VAL; + +#ifndef NULL +#define NULL 0 +#endif + +#endif //__GENERIC_TYPE_DEFS_H_ diff --git a/sw/usb/HID.c b/sw/usb/HID.c new file mode 100644 index 0000000..2a7f73b --- /dev/null +++ b/sw/usb/HID.c @@ -0,0 +1,242 @@ +/* HID class support functions */ + +#include + +#include "project_config.h" + +BYTE bigbuf[256]; //256 bytes +extern DEV_RECORD devtable[]; + +HID_DEVICE hid_device = { { 0 } }; +EP_RECORD hid_ep[2] = { { 0 } }; //HID class endpoints, 1 control, 1 interrupt-IN +//the third endpoint is not implemented + +/* HID Mouse probe. Called from USB state machine. */ +/* assumes configuration length is less than 256 bytes */ +/* looks for Class:03, Subclass: 01, Protocol: 02 in interface descriptor */ +/* sets mouse in boot protocol */ +/* assumes single configuration and interface configuration 0 */ +BOOL HIDMProbe(BYTE addr, DWORD flags) { + BYTE tmpbyte; + BYTE rcode; + BYTE confvalue; + WORD total_length; + USB_DESCR* data_ptr = (USB_DESCR *) &bigbuf; + BYTE* byte_ptr = bigbuf; + + (void) flags; + + rcode = XferGetConfDescr(addr, 0, CONF_DESCR_LEN, 0, bigbuf); //get configuration descriptor + if (rcode) { //error handling + //printf("unable to get configuration descriptor"); + return (FALSE); + } + if (data_ptr->descr.config.wTotalLength > 256) { + total_length = 256; + } else { + total_length = data_ptr->descr.config.wTotalLength; + } + rcode = XferGetConfDescr(addr, 0, total_length, 0, bigbuf); //get the whole configuration + if (rcode) { //error handling + //printf("unable to get configuration"); + return (FALSE); + } + confvalue = data_ptr->descr.config.bConfigurationValue; + //printf("checking configuration value (length: %d): ", + // data_ptr->descr.config.wTotalLength); + //for (int i = 0; i < data_ptr->descr.config.wTotalLength; i++) { + //printf("%x ", (unsigned char) (bigbuf[i] & 0xff)); + //} + while (byte_ptr < bigbuf + total_length) { + if (data_ptr->descr.config.bDescriptorType != USB_DESCRIPTOR_INTERFACE) { + byte_ptr = byte_ptr + data_ptr->descr.config.bLength; + data_ptr = (USB_DESCR*) byte_ptr; + } // if( data_ptr->descr.config.bDescriptorType != USB_DESCRIPTOR_INTERFACE + else { + //printf("starting interface parsing at byte location %d\n", + // data_ptr->descr.config.bLength); + BYTE class = data_ptr->descr.interface.bInterfaceClass; + BYTE subclass = data_ptr->descr.interface.bInterfaceSubClass; + BYTE protocol = data_ptr->descr.interface.bInterfaceProtocol; + //printf("class %x, subclass %x, protocol %x,\n", class, subclass, + // protocol); + //interface descriptor + if (class == HID_INTF && subclass == BOOT_INTF_SUBCLASS + && protocol == HID_PROTOCOL_MOUSE) { + //detected a mouse + devtable[addr].devclass = HID_M; //device class + tmpbyte = devtable[addr].epinfo->MaxPktSize; + HID_init(); //initialize data structures + devtable[addr].epinfo = hid_ep; //switch endpoint information structure + devtable[addr].epinfo[0].MaxPktSize = tmpbyte; + hid_device.interface = + data_ptr->descr.interface.bInterfaceNumber; + hid_device.addr = addr; + byte_ptr = byte_ptr + data_ptr->descr.config.bLength; + data_ptr = (USB_DESCR*) byte_ptr; + while (byte_ptr < bigbuf + total_length) { + if (data_ptr->descr.config.bDescriptorType + != USB_DESCRIPTOR_ENDPOINT) { //skip to endpoint descriptor + byte_ptr = byte_ptr + data_ptr->descr.config.bLength; + data_ptr = (USB_DESCR*) byte_ptr; + } else { + /* fill endpoint information structure */ + devtable[addr].epinfo[1].epAddr = + data_ptr->descr.endpoint.bEndpointAddress; + devtable[addr].epinfo[1].Attr = + data_ptr->descr.endpoint.bmAttributes; + devtable[addr].epinfo[1].MaxPktSize = + data_ptr->descr.endpoint.wMaxPacketSize; + devtable[addr].epinfo[1].Interval = + data_ptr->descr.endpoint.bInterval; + // devtable[ addr ].epinfo[ 1 ].rcvToggle = bmRCVTOG0; + /* configure device */ + rcode = XferSetConf(addr, 0, confvalue); //set configuration + if (rcode) { //error handling + return (FALSE); + } + rcode = XferSetProto(addr, 0, hid_device.interface, + BOOT_PROTOCOL); + if (rcode) { //error handling + return (FALSE); + } else { + return (TRUE); + } + } + } //while( byte_ptr.... + } //if (Class matches + else { //if class don't match; die on first interface. Not really correct + return (FALSE); + } + } //else if( data_ptr-> + } // while( byte_ptr < &buf + total_length + return (FALSE); +} +/* HID Keyboard probe. Called from USB state machine. */ +/* assumes configuration length is less than 256 bytes */ +/* looks for Class:03, Subclass: 01, Protocol: 01 in interface descriptor */ +/* sets keyboard in boot protocol */ +/* assumes single configuration, single endpoint, and interface configuration 0 */ +BOOL HIDKProbe(BYTE addr, DWORD flags) { + BYTE tmpbyte; + BYTE rcode; + BYTE confvalue; + WORD total_length; + USB_DESCR* data_ptr = (USB_DESCR *) &bigbuf; + BYTE* byte_ptr = bigbuf; + + (void) flags; + + rcode = XferGetConfDescr(addr, 0, CONF_DESCR_LEN, 0, bigbuf); //get configuration descriptor + if (rcode) { //error handling + return (FALSE); + } + if (data_ptr->descr.config.wTotalLength > 256) { + total_length = 256; + } else { + total_length = data_ptr->descr.config.wTotalLength; + } + rcode = XferGetConfDescr(addr, 0, total_length, 0, bigbuf); //get the whole configuration + if (rcode) { //error handling + return (FALSE); + } + confvalue = data_ptr->descr.config.bConfigurationValue; //save configuration value to use later + while (byte_ptr < bigbuf + total_length) { //parse configuration + if (data_ptr->descr.config.bDescriptorType != USB_DESCRIPTOR_INTERFACE) { //skip to the next descriptor + byte_ptr = byte_ptr + data_ptr->descr.config.bLength; + data_ptr = (USB_DESCR*) byte_ptr; + } // if( data_ptr->descr.config.bDescriptorType != USB_DESCRIPTOR_INTERFACE + else { + //printf("starting interface parsing at byte location %d\n", + // data_ptr->descr.config.bLength); + BYTE class = data_ptr->descr.interface.bInterfaceClass; + BYTE subclass = data_ptr->descr.interface.bInterfaceSubClass; + BYTE protocol = data_ptr->descr.interface.bInterfaceProtocol; + //printf("class %x, subclass %x, protocol %x,\n", class, subclass, + // protocol); + //interface descriptor + if (class == HID_INTF && subclass == BOOT_INTF_SUBCLASS + && protocol == HID_PROTOCOL_KEYBOARD) { + //detected a keyboard + devtable[addr].devclass = HID_K; //fill device class + tmpbyte = devtable[addr].epinfo->MaxPktSize; //save max.packet size + HID_init(); //initialize data structures + devtable[addr].epinfo = hid_ep; //switch endpoint information structure + devtable[addr].epinfo[0].MaxPktSize = tmpbyte; //fill in max.packet size + hid_device.interface = + data_ptr->descr.interface.bInterfaceNumber; //fill in interface number to be used in HID requests + hid_device.addr = addr; //fill in address + byte_ptr = byte_ptr + data_ptr->descr.config.bLength; //skip to the next descriptor + data_ptr = (USB_DESCR*) byte_ptr; + while (byte_ptr < bigbuf + total_length) { + if (data_ptr->descr.config.bDescriptorType + != USB_DESCRIPTOR_ENDPOINT) { //skip to endpoint descriptor + byte_ptr = byte_ptr + data_ptr->descr.config.bLength; + data_ptr = (USB_DESCR*) byte_ptr; + } else { + /* fill endpoint information structure */ + devtable[addr].epinfo[1].epAddr = + data_ptr->descr.endpoint.bEndpointAddress; + devtable[addr].epinfo[1].Attr = + data_ptr->descr.endpoint.bmAttributes; + devtable[addr].epinfo[1].MaxPktSize = + data_ptr->descr.endpoint.wMaxPacketSize; + devtable[addr].epinfo[1].Interval = + data_ptr->descr.endpoint.bInterval; + /* configure device */ + rcode = XferSetConf(addr, 0, confvalue); //set configuration + if (rcode) { //error handling + return (FALSE); + } + rcode = XferSetProto(addr, 0, hid_device.interface, + BOOT_PROTOCOL); + if (rcode) { //error handling + return (FALSE); + } else { + return (TRUE); + } + } + } //while( byte_ptr.... + } //if (Class matches + else { //if class don't match; stop processing after first interface. Not really correct + return (FALSE); + } + } //else if( data_ptr-> + } // while( byte_ptr < &buf + total_length + return (FALSE); +} +/* HID data structures initialization */ +void HID_init(void) { + hid_ep[1].sndToggle = bmSNDTOG0; + hid_ep[1].rcvToggle = bmRCVTOG0; +} +/* poll boot mouse */ +BYTE mousePoll(BOOT_MOUSE_REPORT* buf) { + BYTE rcode; + MAXreg_wr( rPERADDR, hid_device.addr); //set peripheral address + rcode = XferInTransfer(hid_device.addr, 1, 8, (BYTE*) buf, + devtable[hid_device.addr].epinfo[1].MaxPktSize); + return (rcode); +} +/* poll boot keyboard */ +BYTE kbdPoll(BOOT_KBD_REPORT* buf) { + BYTE rcode; + MAXreg_wr( rPERADDR, hid_device.addr); //set peripheral address + rcode = XferInTransfer(hid_device.addr, 1, 8, (BYTE*) buf, + devtable[hid_device.addr].epinfo[1].MaxPktSize); + return (rcode); +} +BOOL HIDMEventHandler(BYTE address, BYTE event, void *data, DWORD size) { + (void) address; + (void) event; + (void) size; + (void) data; + return (FALSE); +} +BOOL HIDKEventHandler(BYTE address, BYTE event, void *data, DWORD size) { + (void) address; + (void) event; + (void) size; + (void) data; + return (FALSE); +} diff --git a/sw/usb/HID.h b/sw/usb/HID.h new file mode 100644 index 0000000..80e9144 --- /dev/null +++ b/sw/usb/HID.h @@ -0,0 +1,50 @@ +/* HID support header */ + +#ifndef _HID_h_ +#define _HID_h + +/* HID device structure */ +typedef struct { + BYTE addr; + BYTE interface; +} HID_DEVICE; +/* Boot mouse report 8 bytes */ +typedef struct { +// struct { +// unsigned one:1; +// unsigned two:1; +// unsigned three:1; +// unsigned :5; +// } button; + BYTE button; + BYTE Xdispl; + BYTE Ydispl; + BYTE bytes3to7[5]; //optional bytes +} BOOT_MOUSE_REPORT; +/* boot keyboard report 8 bytes */ +typedef struct { + BYTE mod; +// struct { +// unsigned LCtrl:1; +// unsigned LShift:1; +// unsigned LAlt:1; +// unsigned LWin:1; +// /**/ +// unsigned RCtrl:1; +// unsigned RShift:1; +// unsigned RAlt:1; +// unsigned RWin:1; +// } mod; + BYTE reserved; + BYTE keycode[6]; +} BOOT_KBD_REPORT; + +/* Function prototypes */ +BOOL HIDMProbe(BYTE address, DWORD flags); +BOOL HIDKProbe(BYTE address, DWORD flags); +void HID_init(void); +BYTE mousePoll(BOOT_MOUSE_REPORT* buf); +BYTE kbdPoll(BOOT_KBD_REPORT* buf); +BOOL HIDMEventHandler(BYTE addr, BYTE event, void *data, DWORD size); +BOOL HIDKEventHandler(BYTE addr, BYTE event, void *data, DWORD size); +#endif // _HID_h_ diff --git a/sw/usb/MAX3421E.c b/sw/usb/MAX3421E.c new file mode 100644 index 0000000..c0ad68c --- /dev/null +++ b/sw/usb/MAX3421E.c @@ -0,0 +1,250 @@ +//Fill in your low-level SPI functions here, as per your host platform + +#define _MAX3421E_C_ + +#include +#include +#include +#include "project_config.h" +#include +#include +#include + +//variables and data structures +//External variables +extern BYTE usb_task_state; +uint8_t* usb_gpio = USB_GPIO; + +/* Functions */ +void SPI_init(BYTE sync_mode, BYTE bus_mode, BYTE smp_phase) { + //Don't need to initialize SPI port, already ready to go with BSP + (void) sync_mode; + (void) bus_mode; + (void) smp_phase; +} + +//writes single byte to MAX3421E via SPI, simultanously reads status register and returns it +BYTE SPI_wr(BYTE data) { + (void) data; + //This function is never used by the code, so you do not need to fill it in + return -1; +} +//writes register to MAX3421E via SPI +void MAXreg_wr(BYTE reg, BYTE val) { + //psuedocode: + //select MAX3421E (may not be necessary if you are using SPI peripheral) + //write reg + 2 via SPI + //write val via SPI + //read return code from SPI peripheral (see Intel documentation) + //if return code < 0 print an error + //deselect MAX3421E (may not be necessary if you are using SPI peripheral) + BYTE regdir = reg + 2; //reg bitshift plus direction bit. + + spi_byte(regdir); + spi_byte(val); + spi_deselect(); +} +//multiple-byte write +//returns a pointer to a memory position after last written +BYTE* MAXbytes_wr(BYTE reg, BYTE nbytes, BYTE* data) { + //psuedocode: + //select MAX3421E (may not be necessary if you are using SPI peripheral) + //write reg + 2 via SPI + //write data[n] via SPI, where n goes from 0 to nbytes-1 + //read return code from SPI peripheral (see Intel documentation) + //if return code < 0 print an error + //deselect MAX3421E (may not be necessary if you are using SPI peripheral) + //return (data + nbytes); + int i; + BYTE regdir = reg + 2; + + spi_byte(regdir); + + for (i = 0; i < nbytes; i++){ + spi_byte(data[i]); + } + + spi_deselect(); + + return data + nbytes; +} + +//reads register from MAX3421E via SPI +BYTE MAXreg_rd(BYTE reg) { + //psuedocode: + //select MAX3421E (may not be necessary if you are using SPI peripheral) + //write reg via SPI + //read val via SPI + //read return code from SPI peripheral (see Intel documentation) + //if return code < 0 print an error + //deselect MAX3421E (may not be necessary if you are using SPI peripheral) + //return val + BYTE regdir = reg; + BYTE rxdata; + + spi_byte(regdir); + rxdata = spi_byte(0); + spi_deselect(); + + return rxdata; +} +//multiple-byte write +//returns a pointer to a memory position after last written +BYTE* MAXbytes_rd(BYTE reg, BYTE nbytes, BYTE* data) { + //psuedocode: + //select MAX3421E (may not be necessary if you are using SPI peripheral) + //write reg via SPI + //read data[n] from SPI, where n goes from 0 to nbytes-1 + //read return code from SPI peripheral (see Intel documentation) + //if return code < 0 print an error + //deselect MAX3421E (may not be necessary if you are using SPI peripheral) + //return (data + nbytes); + int i; + BYTE regdir = reg; + + spi_byte(regdir); + + for (i = 0; i < nbytes; i++){ + data[i] = spi_byte(0); + } + + spi_deselect(); + + return data + nbytes; +} +/* reset MAX3421E using chip reset bit. SPI configuration is not affected */ +void MAX3421E_reset(void) { + //hardware reset, then software reset + BYTE tmp; + + *usb_gpio &= ~USB_GPIO_RST; + //TODO + //sleep(1); + *usb_gpio |= USB_GPIO_RST; + tmp = 0; + MAXreg_wr( rUSBCTL, bmCHIPRES); //Chip reset. This stops the oscillator + MAXreg_wr( rUSBCTL, 0x00); //Remove the reset + while (!(MAXreg_rd( rUSBIRQ) & bmOSCOKIRQ)) { //wait until the PLL stabilizes + tmp++; //timeout after 256 attempts + if (tmp == 0) { + //printf("reset timeout!"); + } + } +} +/* turn USB power on/off */ +/* ON pin of VBUS switch (MAX4793 or similar) is connected to GPOUT7 */ +/* OVERLOAD pin of Vbus switch is connected to GPIN7 */ +/* OVERLOAD state low. NO OVERLOAD or VBUS OFF state high. */ +BOOL Vbus_power(BOOL action) { + // power on/off successful + return (1); +} + +/* probe bus to determine device presense and speed */ +void MAX_busprobe(void) { + BYTE bus_sample; + +// MAXreg_wr(rHCTL,bmSAMPLEBUS); + bus_sample = MAXreg_rd( rHRSL); //Get J,K status + bus_sample &= ( bmJSTATUS | bmKSTATUS); //zero the rest of the byte + + switch (bus_sample) { //start full-speed or low-speed host + case ( bmJSTATUS): + /*kludgy*/ + if (usb_task_state != USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE) { //bus reset causes connection detect interrupt + if (!(MAXreg_rd( rMODE) & bmLOWSPEED)) { + MAXreg_wr( rMODE, MODE_FS_HOST); //start full-speed host + //printf("Starting in full speed\n"); + } else { + MAXreg_wr( rMODE, MODE_LS_HOST); //start low-speed host + //printf("Starting in low speed\n"); + } + usb_task_state = ( USB_STATE_ATTACHED); //signal usb state machine to start attachment sequence + } + break; + case ( bmKSTATUS): + if (usb_task_state != USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE) { //bus reset causes connection detect interrupt + if (!(MAXreg_rd( rMODE) & bmLOWSPEED)) { + MAXreg_wr( rMODE, MODE_LS_HOST); //start low-speed host + //printf("Starting in low speed\n"); + } else { + MAXreg_wr( rMODE, MODE_FS_HOST); //start full-speed host + //printf("Starting in full speed\n"); + } + usb_task_state = ( USB_STATE_ATTACHED); //signal usb state machine to start attachment sequence + } + break; + case ( bmSE1): //illegal state + usb_task_state = ( USB_DETACHED_SUBSTATE_ILLEGAL); + break; + case ( bmSE0): //disconnected state + if (!((usb_task_state & USB_STATE_MASK) == USB_STATE_DETACHED)) //if we came here from other than detached state + usb_task_state = ( USB_DETACHED_SUBSTATE_INITIALIZE); //clear device data structures + else { + MAXreg_wr( rMODE, MODE_FS_HOST); //start full-speed host + usb_task_state = ( USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE); + } + break; + } //end switch( bus_sample ) +} +/* MAX3421E initialization after power-on */ +void MAX3421E_init(void) { + /* Configure full-duplex SPI, interrupt pulse */ + MAXreg_wr( rPINCTL, (bmFDUPSPI + bmINTLEVEL + bmGPXB)); //Full-duplex SPI, level interrupt, GPX + MAX3421E_reset(); //stop/start the oscillator + /* configure power switch */ + Vbus_power( OFF); //turn Vbus power off + MAXreg_wr( rGPINIEN, bmGPINIEN7); //enable interrupt on GPIN7 (power switch overload flag) + Vbus_power( ON); + /* configure host operation */ + MAXreg_wr( rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST | bmSEPIRQ); // set pull-downs, SOF, Host, Separate GPIN IRQ on GPX + //MAXreg_wr( rHIEN, bmFRAMEIE|bmCONDETIE|bmBUSEVENTIE ); // enable SOF, connection detection, bus event IRQs + MAXreg_wr( rHIEN, bmCONDETIE); //connection detection + /* HXFRDNIRQ is checked in Dispatch packet function */ + MAXreg_wr(rHCTL, bmSAMPLEBUS); // update the JSTATUS and KSTATUS bits + MAX_busprobe(); //check if anything is connected + MAXreg_wr( rHIRQ, bmCONDETIRQ); //clear connection detect interrupt + MAXreg_wr( rCPUCTL, 0x01); //enable interrupt pin +} + +/* MAX3421 state change task and interrupt handler */ +void MAX3421E_Task(void) { + if ((*usb_gpio & USB_GPIO_IRQ) == 0) { + //printf("MAX interrupt\n\r"); + MaxIntHandler(); + } + if ((*usb_gpio & USB_GPIO_GPX) != 0) { + //printf("GPX interrupt\n\r"); + MaxGpxHandler(); + } +} + +void MaxIntHandler(void) { + BYTE HIRQ; + BYTE HIRQ_sendback = 0x00; + HIRQ = MAXreg_rd( rHIRQ); //determine interrupt source + //printf("IRQ: %x\n", HIRQ); + if (HIRQ & bmFRAMEIRQ) { //->1ms SOF interrupt handler + HIRQ_sendback |= bmFRAMEIRQ; + } //end FRAMEIRQ handling + + if (HIRQ & bmCONDETIRQ) { + MAX_busprobe(); + HIRQ_sendback |= bmCONDETIRQ; //set sendback to 1 to clear register + } + if (HIRQ & bmSNDBAVIRQ) //if the send buffer is clear (previous transfer completed without issue) + { + MAXreg_wr(rSNDBC, 0x00);//clear the send buffer (not really necessary, but clears interrupt) + } + if (HIRQ & bmBUSEVENTIRQ) { //bus event is either reset or suspend + usb_task_state++; //advance USB task state machine + HIRQ_sendback |= bmBUSEVENTIRQ; + } + /* End HIRQ interrupts handling, clear serviced IRQs */ + MAXreg_wr( rHIRQ, HIRQ_sendback); //write '1' to CONDETIRQ to ack bus state change +} + +void MaxGpxHandler(void) { + BYTE GPINIRQ; + GPINIRQ = MAXreg_rd( rGPINIRQ); //read both IRQ registers +} diff --git a/sw/usb/MAX3421E.h b/sw/usb/MAX3421E.h new file mode 100644 index 0000000..eb29fe8 --- /dev/null +++ b/sw/usb/MAX3421E.h @@ -0,0 +1,250 @@ +/* MAX3421E support header */ +/* Register names and bit masks for MAX3421 in host mode */ +/* Function prototypes in MAX3421E.c */ +#ifndef _MAX3421E_H_ +#define _MAX3421E_H_ + +#define USB_GPIO_RST (1 << 0) +#define USB_GPIO_IRQ (1 << 1) +#define USB_GPIO_GPX (1 << 2) + +/* SPI interface definitions */ +/* SSPSTAT REGISTER */ + +// Master SPI mode only +#define SMPEND 0x80 // Input data sample at end of data out +#define SMPMID 0x00 // Input data sample at middle of data out + +#define MODE_00 0 // Setting for SPI bus Mode 0,0 +//CKE 0x40 // SSPSTAT register +//CKP 0x00 // SSPCON1 register + +#define MODE_01 1 // Setting for SPI bus Mode 0,1 +//CKE 0x00 // SSPSTAT register +//CKP 0x00 // SSPCON1 register + +#define MODE_10 2 // Setting for SPI bus Mode 1,0 +//CKE 0x40 // SSPSTAT register +//CKP 0x10 // SSPCON1 register + +#define MODE_11 3 // Setting for SPI bus Mode 1,1 +//CKE 0x00 // SSPSTAT register +//CKP 0x10 // SSPCON1 register + +/* SSPCON1 REGISTER */ +#define SSPENB 0x20 // Enable serial port and configures SCK, SDO, SDI + +#define SPI_FOSC_4 0 // SPI Master mode, clock = Fosc/4 +#define SPI_FOSC_16 1 // SPI Master mode, clock = Fosc/16 +#define SPI_FOSC_64 2 // SPI Master mode, clock = Fosc/64 +#define SPI_FOSC_TMR2 3 // SPI Master mode, clock = TMR2 output/2 +#define SLV_SSON 4 // SPI Slave mode, /SS pin control enabled +#define SLV_SSOFF 5 // SPI Slave mode, /SS pin control disabled + +/* MAX3421E command byte format: rrrrr0wa where 'r' is register number */ +// +// MAX3421E Registers in HOST mode. +// +#define rRCVFIFO 0x08 //1<<3 +#define rSNDFIFO 0x10 //2<<3 +#define rSUDFIFO 0x20 //4<<3 +#define rRCVBC 0x30 //6<<3 +#define rSNDBC 0x38 //7<<3 + +#define rUSBIRQ 0x68 //13<<3 +/* USBIRQ Bits */ +#define bmVBUSIRQ 0x40 //b6 +#define bmNOVBUSIRQ 0x20 //b5 +#define bmOSCOKIRQ 0x01 //b0 + +#define rUSBIEN 0x70 //14<<3 +/* USBIEN Bits */ +#define bmVBUSIE 0x40 //b6 +#define bmNOVBUSIE 0x20 //b5 +#define bmOSCOKIE 0x01 //b0 + +#define rUSBCTL 0x78 //15<<3 +/* USBCTL Bits */ +#define bmCHIPRES 0x20 //b5 +#define bmPWRDOWN 0x10 //b4 + +#define rCPUCTL 0x80 //16<<3 +/* CPUCTL Bits */ +#define bmPUSLEWID1 0x80 //b7 +#define bmPULSEWID0 0x40 //b6 +#define bmIE 0x01 //b0 + +#define rPINCTL 0x88 //17<<3 +/* PINCTL Bits */ +#define bmFDUPSPI 0x10 //b4 +#define bmINTLEVEL 0x08 //b3 +#define bmPOSINT 0x04 //b2 +#define bmGPXB 0x02 //b1 +#define bmGPXA 0x01 //b0 +// GPX pin selections +#define GPX_OPERATE 0x00 +#define GPX_VBDET 0x01 +#define GPX_BUSACT 0x02 +#define GPX_SOF 0x03 + +#define rREVISION 0x90 //18<<3 + +#define rIOPINS1 0xa0 //20<<3 + +/* IOPINS1 Bits */ +#define bmGPOUT0 0x01 +#define bmGPOUT1 0x02 +#define bmGPOUT2 0x04 +#define bmGPOUT3 0x08 +#define bmGPIN0 0x10 +#define bmGPIN1 0x20 +#define bmGPIN2 0x40 +#define bmGPIN3 0x80 + +#define rIOPINS2 0xa8 //21<<3 +/* IOPINS2 Bits */ +#define bmGPOUT4 0x01 +#define bmGPOUT5 0x02 +#define bmGPOUT6 0x04 +#define bmGPOUT7 0x08 +#define bmGPIN4 0x10 +#define bmGPIN5 0x20 +#define bmGPIN6 0x40 +#define bmGPIN7 0x80 + +#define rGPINIRQ 0xb0 //22<<3 +/* GPINIRQ Bits */ +#define bmGPINIRQ0 0x01 +#define bmGPINIRQ1 0x02 +#define bmGPINIRQ2 0x04 +#define bmGPINIRQ3 0x08 +#define bmGPINIRQ4 0x10 +#define bmGPINIRQ5 0x20 +#define bmGPINIRQ6 0x40 +#define bmGPINIRQ7 0x80 + +#define rGPINIEN 0xb8 //23<<3 +/* GPINIEN Bits */ +#define bmGPINIEN0 0x01 +#define bmGPINIEN1 0x02 +#define bmGPINIEN2 0x04 +#define bmGPINIEN3 0x08 +#define bmGPINIEN4 0x10 +#define bmGPINIEN5 0x20 +#define bmGPINIEN6 0x40 +#define bmGPINIEN7 0x80 + +#define rGPINPOL 0xc0 //24<<3 +/* GPINPOL Bits */ +#define bmGPINPOL0 0x01 +#define bmGPINPOL1 0x02 +#define bmGPINPOL2 0x04 +#define bmGPINPOL3 0x08 +#define bmGPINPOL4 0x10 +#define bmGPINPOL5 0x20 +#define bmGPINPOL6 0x40 +#define bmGPINPOL7 0x80 + +#define rHIRQ 0xc8 //25<<3 +/* HIRQ Bits */ +#define bmBUSEVENTIRQ 0x01 // indicates BUS Reset Done or BUS Resume +#define bmRWUIRQ 0x02 +#define bmRCVDAVIRQ 0x04 +#define bmSNDBAVIRQ 0x08 +#define bmSUSDNIRQ 0x10 +#define bmCONDETIRQ 0x20 +#define bmFRAMEIRQ 0x40 +#define bmHXFRDNIRQ 0x80 + +#define rHIEN 0xd0 //26<<3 +/* HIEN Bits */ +#define bmBUSEVENTIE 0x01 +#define bmRWUIE 0x02 +#define bmRCVDAVIE 0x04 +#define bmSNDBAVIE 0x08 +#define bmSUSDNIE 0x10 +#define bmCONDETIE 0x20 +#define bmFRAMEIE 0x40 +#define bmHXFRDNIE 0x80 + +#define rMODE 0xd8 //27<<3 +/* MODE Bits */ +#define bmHOST 0x01 +#define bmLOWSPEED 0x02 +#define bmHUBPRE 0x04 +#define bmSOFKAENAB 0x08 +#define bmSEPIRQ 0x10 +#define bmDELAYISO 0x20 +#define bmDMPULLDN 0x40 +#define bmDPPULLDN 0x80 + +#define rPERADDR 0xe0 //28<<3 + +#define rHCTL 0xe8 //29<<3 +/* HCTL Bits */ +#define bmBUSRST 0x01 +#define bmFRMRST 0x02 +#define bmSAMPLEBUS 0x04 +#define bmSIGRSM 0x08 +#define bmRCVTOG0 0x10 +#define bmRCVTOG1 0x20 +#define bmSNDTOG0 0x40 +#define bmSNDTOG1 0x80 + +#define rHXFR 0xf0 //30<<3 +/* Host transfer token values for writing the HXFR register (R30) */ +/* OR this bit field with the endpoint number in bits 3:0 */ +#define tokSETUP 0x10 // HS=0, ISO=0, OUTNIN=0, SETUP=1 +#define tokIN 0x00 // HS=0, ISO=0, OUTNIN=0, SETUP=0 +#define tokOUT 0x20 // HS=0, ISO=0, OUTNIN=1, SETUP=0 +#define tokINHS 0x80 // HS=1, ISO=0, OUTNIN=0, SETUP=0 +#define tokOUTHS 0xA0 // HS=1, ISO=0, OUTNIN=1, SETUP=0 +#define tokISOIN 0x40 // HS=0, ISO=1, OUTNIN=0, SETUP=0 +#define tokISOOUT 0x60 // HS=0, ISO=1, OUTNIN=1, SETUP=0 + +#define rHRSL 0xf8 //31<<3 +/* HRSL Bits */ +#define bmRCVTOGRD 0x10 +#define bmSNDTOGRD 0x20 +#define bmKSTATUS 0x40 +#define bmJSTATUS 0x80 +#define bmSE0 0x00 //SE0 - disconnect state +#define bmSE1 0xc0 //SE1 - illegal state +/* Host error result codes, the 4 LSB's in the HRSL register */ +#define hrSUCCESS 0x00 +#define hrBUSY 0x01 +#define hrBADREQ 0x02 +#define hrUNDEF 0x03 +#define hrNAK 0x04 +#define hrSTALL 0x05 +#define hrTOGERR 0x06 +#define hrWRONGPID 0x07 +#define hrBADBC 0x08 +#define hrPIDERR 0x09 +#define hrPKTERR 0x0A +#define hrCRCERR 0x0B +#define hrKERR 0x0C +#define hrJERR 0x0D +#define hrTIMEOUT 0x0E +#define hrBABBLE 0x0F + +#define MODE_FS_HOST (bmDPPULLDN|bmDMPULLDN|bmHOST|bmSOFKAENAB) +#define MODE_LS_HOST (bmDPPULLDN|bmDMPULLDN|bmHOST|bmLOWSPEED|bmSOFKAENAB) + +/* MAX3421E.c function prototypes */ +void SPI_init(BYTE sync_mode, BYTE bus_mode, BYTE smp_phase); +BYTE SPI_wr(BYTE data); +void MAXreg_wr(BYTE reg, BYTE val); +BYTE* MAXbytes_wr(BYTE reg, BYTE nbytes, BYTE * data); +BYTE MAXreg_rd(BYTE reg); +BYTE* MAXbytes_rd(BYTE reg, BYTE nbytes, BYTE *data); +void MAX3421E_reset(void); +BOOL Vbus_power(BOOL action); +void MAX3421E_init(void); +void MAX_busprobe(void); +void MAX3421E_Task(void); +void MaxIntHandler(void); +void MaxGpxHandler(void); + +#endif //_MAX3421E_H_ + diff --git a/sw/usb/README b/sw/usb/README new file mode 100644 index 0000000..b66c227 --- /dev/null +++ b/sw/usb/README @@ -0,0 +1,12 @@ +This is a project directory of Lightweight USB host for Microchip PIC18 and Maxim MAX3421E USB Host controller. +This is a migration from FreeRTOS implementation, which I decided to stop developing because the end product will not fit into PIC18. +Therefore, you will find fragments of strange code every now and then. + +The code is compiled using Microchip C18 compiler in MPLAB. MPLAB project file is provided but not guaranteed to work on your system +due to absolute path issue. You can manually edit the .mcp file or make your own. The project uses standard linker script and headers. + +In addition, logic analyzer trace is provided in LPF file. Too see the trace you will need to download Logicport software from Intronix, +http://www.pctestinstruments.com/downloads.htm + +For hardware implementation information go to http://www.circuitsathome.com + diff --git a/sw/usb/USB.h b/sw/usb/USB.h new file mode 100644 index 0000000..1ee6af6 --- /dev/null +++ b/sw/usb/USB.h @@ -0,0 +1,277 @@ +/* USB task support header */ + +#ifndef _USB_h_ +#define _USB_h_ + +// ***************************************************************************** +// Section: State Machine Constants +// ***************************************************************************** + +/* States are defined by 4 high bits + Substates are defined by 4 low bits */ + +#define USB_STATE_MASK 0xf0 // +#define USB_SUBSTATE_MASK 0x0f // + +#define SUBSUBSTATE_MASK 0x000F // + +#define NEXT_STATE 0x0100 // +#define NEXT_SUBSTATE 0x0010 // +#define NEXT_SUBSUBSTATE 0x0001 // + +#define SUBSUBSTATE_ERROR 0x000F // + +#define NO_STATE 0xFFFF // + +/* + ******************************************************************************* + DETACHED state machine values + + This state machine handles the condition when no device is attached. + */ + +#define USB_STATE_DETACHED 0x00 +#define USB_DETACHED_SUBSTATE_INITIALIZE 0x01 +#define USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE 0x03 +#define USB_DETACHED_SUBSTATE_ILLEGAL 0x04 + +/* + ******************************************************************************* + ATTACHED state machine values + + This state machine gets the device descriptor of the remote device. We get the + size of the device descriptor, and use that size to get the entire device + descriptor. Then we check the VID and PID and make sure they appear in the TPL. + */ + +#define USB_STATE_ATTACHED 0x10 +#define USB_ATTACHED_SUBSTATE_SETTLE 0x11 +#define USB_ATTACHED_SUBSTATE_RESET_DEVICE 0x12 +#define USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE 0x13 +#define USB_ATTACHED_SUBSTATE_WAIT_SOF 0x14 +#define USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE 0x15 +//#define USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR 0x16 +//#define USB_ATTACHED_SUBSTATE_VALIDATE_VID_PID 0x17 +//#define USB_ATTACHED_SUBSTATE_VALIDATE_CLSP 0x18 + +/* + ******************************************************************************* + ADDRESSING state machine values + + This state machine sets the address of the remote device. + */ + +#define USB_STATE_ADDRESSING 0x20 +/* + ******************************************************************************* + CONFIGURING state machine values + + This state machine sets the configuration of the remote device, and sets up + internal variables to support the device. + */ +#define USB_STATE_CONFIGURING 0x30 + +/* + ******************************************************************************* + RUNNING state machine values + + */ + +#define USB_STATE_RUNNING 0x40 +//#define RUNNING_SUBSTATE_NORMAL_RUN 0x0000 // +//#define RUNNING_SUBSTATE_SUSPEND_AND_RESUME 0x0010 // +//#define RUNNING_SUBSUBSTATE_SUSPEND 0x0000 // +//#define RUNNING_SUBSUBSTATE_RESUME 0x0001 // +//#define RUNNING_SUBSUBSTATE_RESUME_WAIT 0x0002 // +//#define RUNNING_SUBSUBSTATE_RESUME_RECOVERY 0x0003 // +//#define RUNNING_SUBSUBSTATE_RESUME_RECOVERY_WAIT 0x0004 // +//#define RUNNING_SUBSUBSTATE_RESUME_COMPLETE 0x0005 // + +/* + ******************************************************************************* + HOLDING state machine values + + */ + +#define STATE_HOLDING 0x50 // + +#define HOLDING_SUBSTATE_HOLD_INIT 0x0000 // +#define HOLDING_SUBSTATE_HOLD 0x0001 // + +/* Error state machine state. Non-recoverable */ + +#define USB_STATE_ERROR 0xff + +// ***************************************************************************** +// Section: Token State Machine Constants +// ***************************************************************************** + +#define TSTATE_MASK 0x00F0 // +#define TSUBSTATE_MASK 0x000F // + +#define TSUBSTATE_ERROR 0x000F // + +#define TSTATE_IDLE 0x0000 // + +#define TSTATE_CONTROL_NO_DATA 0x0010 // +#define TSUBSTATE_CONTROL_NO_DATA_SETUP 0x0000 // +#define TSUBSTATE_CONTROL_NO_DATA_ACK 0x0001 // +#define TSUBSTATE_CONTROL_NO_DATA_COMPLETE 0x0002 // + +#define TSTATE_CONTROL_READ 0x0020 // +#define TSUBSTATE_CONTROL_READ_SETUP 0x0000 // +#define TSUBSTATE_CONTROL_READ_DATA 0x0001 // +#define TSUBSTATE_CONTROL_READ_ACK 0x0002 // +#define TSUBSTATE_CONTROL_READ_COMPLETE 0x0003 // + +#define TSTATE_CONTROL_WRITE 0x0030 // +#define TSUBSTATE_CONTROL_WRITE_SETUP 0x0000 // +#define TSUBSTATE_CONTROL_WRITE_DATA 0x0001 // +#define TSUBSTATE_CONTROL_WRITE_ACK 0x0002 // +#define TSUBSTATE_CONTROL_WRITE_COMPLETE 0x0003 // + +#define TSTATE_INTERRUPT_READ 0x0040 // +#define TSUBSTATE_INTERRUPT_READ_DATA 0x0000 // +#define TSUBSTATE_INTERRUPT_READ_COMPLETE 0x0001 // + +#define TSTATE_INTERRUPT_WRITE 0x0050 // +#define TSUBSTATE_INTERRUPT_WRITE_DATA 0x0000 // +#define TSUBSTATE_INTERRUPT_WRITE_COMPLETE 0x0001 // + +#define TSTATE_ISOCHRONOUS_READ 0x0060 // +#define TSUBSTATE_ISOCHRONOUS_READ_DATA 0x0000 // +#define TSUBSTATE_ISOCHRONOUS_READ_COMPLETE 0x0001 // + +#define TSTATE_ISOCHRONOUS_WRITE 0x0070 // +#define TSUBSTATE_ISOCHRONOUS_WRITE_DATA 0x0000 // +#define TSUBSTATE_ISOCHRONOUS_WRITE_COMPLETE 0x0001 // + +#define TSTATE_BULK_READ 0x0080 // +#define TSUBSTATE_BULK_READ_DATA 0x0000 // +#define TSUBSTATE_BULK_READ_COMPLETE 0x0001 // + +#define TSTATE_BULK_WRITE 0x0090 // +#define TSUBSTATE_BULK_WRITE_DATA 0x0000 // +#define TSUBSTATE_BULK_WRITE_COMPLETE 0x0001 // + +// ************************ +// Standard USB Requests +#define SR_GET_STATUS 0x00 // Get Status +#define SR_CLEAR_FEATURE 0x01 // Clear Feature +#define SR_RESERVED 0x02 // Reserved +#define SR_SET_FEATURE 0x03 // Set Feature +#define SR_SET_ADDRESS 0x05 // Set Address +#define SR_GET_DESCRIPTOR 0x06 // Get Descriptor +#define SR_SET_DESCRIPTOR 0x07 // Set Descriptor +#define SR_GET_CONFIGURATION 0x08 // Get Configuration +#define SR_SET_CONFIGURATION 0x09 // Set Configuration +#define SR_GET_INTERFACE 0x0a // Get Interface +#define SR_SET_INTERFACE 0x0b // Set Interface + +// Get Descriptor codes +#define GD_DEVICE 0x01 // Get device descriptor: Device +#define GD_CONFIGURATION 0x02 // Get device descriptor: Configuration +#define GD_STRING 0x03 // Get device descriptor: String +#define GD_HID 0x21 // Get descriptor: HID +#define GD_REPORT 0x22 // Get descriptor: Report + +// HID bRequest values +#define GET_REPORT 1 +#define GET_IDLE 2 +#define GET_PROTOCOL 3 +#define SET_REPORT 9 +#define SET_IDLE 0x0A +#define SET_PROTOCOL 0x0B +#define INPUT_REPORT 1 + +////****************************************************************************** +////****************************************************************************** +//// Section: Macros +//// +//// These macros are all internal to the host layer. +////****************************************************************************** +////****************************************************************************** +// +//#define _USB_InitErrorCounters() { numCommandTries = USB_NUM_COMMAND_TRIES; } +//#define _USB_SetDATA01(x) { pCurrentEndpoint->status.bfNextDATA01 = x; } +//#define _USB_SetErrorCode(x) { usbDeviceInfo.errorCode = x; } +//#define _USB_SetHoldState() { usbHostState = STATE_HOLDING; } +//#define _USB_SetNextState() { usbHostState = (usbHostState & STATE_MASK) + NEXT_STATE; } +//#define _host_tasks_SetNextSubState() { host_tasks_state =( host_tasks_state & (STATE_MASK | SUBSTATE_MASK)) + NEXT_SUBSTATE; } +//#define _USB_SetNextSubSubState() { usbHostState = usbHostState + NEXT_SUBSUBSTATE; } +//#define _USB_SetNextTransferState() { pCurrentEndpoint->transferState ++; } +//#define _USB_SetPreviousSubSubState() { usbHostState = usbHostState - NEXT_SUBSUBSTATE; } +//#define _USB_SetTransferErrorState(x) { x->transferState = (x->transferState & TSTATE_MASK) | TSUBSTATE_ERROR; } +//#define freez(x) { free(x); x = NULL; } + +/* data structures */ + +// ***************************************************************************** +/* USB Mass Storage Device Information + + This structure is used to hold all the information about an attached Mass Storage device. + */ +typedef struct _USB_MSD_DEVICE_INFO { + BYTE blockData[31]; // Data buffer for device communication. + BYTE deviceAddress; // Address of the device on the bus. + BYTE errorCode; // Error code of last error. + BYTE state; // State machine state of the device. + BYTE returnState; // State to return to after performing error handling. + union { + struct { + unsigned int bfDirection :1; // Direction of current transfer (0=OUT, 1=IN). + unsigned int bfReset :1; // Flag indicating to perform Mass Storage Reset. + unsigned int bfClearDataIN :1; // Flag indicating to clear the IN endpoint. + unsigned int bfClearDataOUT :1; // Flag indicating to clear the OUT endpoint. + }; + BYTE val; + } flags; + BYTE maxLUN; // The maximum Logical Unit Number of the device. + BYTE interface; // Interface number we are using. + BYTE epin_idx; // Bulk IN endpoint index in devinfo.epinfo_ptr[]. + BYTE epout_idx; // Bulk OUT endpoint index in devinfo.epinfo_ptr[]. + BYTE endpointDATA; // Endpoint to use for the current transfer. + BYTE *userData; // Pointer to the user's data buffer. + DWORD userDataLength; // Length of the user's data buffer. + DWORD bytesTransferred; // Number of bytes transferred to/from the user's data buffer. + DWORD dCBWTag; // The value of the dCBWTag to verify against the dCSWtag. + BYTE attemptsCSW; // Number of attempts to retrieve the CSW. +} USB_MSD_DEVICE_INFO; + +///* class driver event handler */ +//typedef BOOL (* rom CLASS_EVENT_HANDLER) ( BYTE address, BYTE event, void *data, DWORD size ); +// +///* class driver initialization */ +//typedef BOOL (* rom CLASS_INIT) ( BYTE address, DWORD flags ); +// +//// ***************************************************************************** +///* Client Driver Table Structure +// +// */ +// +//typedef struct _CLASS_CALLBACK_TABLE +//{ +// CLASS_INIT Initialize; // Initialization routine +// CLASS_EVENT_HANDLER EventHandler; // Event routine +// DWORD flags; // Initialization flags +// +//} CLASS_CALLBACK_TABLE; + +//* Functions */ +// +//void vUSBtask_init( void ); +//void vUSB_task( void *pvParameters ); +//void prvUSBdata_init( void ); +//char bUSB_Control_Write_ND( BYTE addr, BYTE ep ); +//char bUSB_Control_Read( BYTE addr, BYTE ep ); +//char bUSB_IN_Transfer( BYTE ep, WORD nbytes, BYTE maxpktsize, BYTE * data ); +//char bUSB_Dispatch_Pkt( BYTE token, BYTE ep ); +//BOOL prvMSDInit( BYTE address, DWORD flags ); +//BOOL prvMSDEventHandler( BYTE address, BYTE event, void *data, DWORD size ); +//BOOL prvCDCProbe( BYTE address, DWORD flags ); +//BOOL prvCDCEventHandler( BYTE address, BYTE event, void *data, DWORD size ); +//BOOL prvDummyProbe( BYTE address , DWORD flags ); +//BOOL prvDummyEventHandler( BYTE address, BYTE event, void *data, DWORD size ); +//BYTE flush_Q( xQueueHandle QueueH ); + +#endif //_USB_h_ diff --git a/sw/usb/project_config.h b/sw/usb/project_config.h new file mode 100644 index 0000000..0c966e6 --- /dev/null +++ b/sw/usb/project_config.h @@ -0,0 +1,22 @@ +/* Project name project configuration file */ + +#ifndef _project_config_h_ +#define _project_config_h_ + +#include "GenericMacros.h" +#include "GenericTypeDefs.h" +#include "HID.h" +#include "MAX3421E.h" +#include "transfer.h" +#include "usb_ch9.h" +#include "USB.h" + +/* USB constants */ +/* time in milliseconds */ +#define USB_SETTLE_TIME 200 //USB settle after reset +#define USB_XFER_TIMEOUT 5000 //USB transfer timeout + +#define USB_NAK_LIMIT 2 +#define USB_RETRY_LIMIT 3 + +#endif // _project_config_h diff --git a/sw/usb/transfer.c b/sw/usb/transfer.c new file mode 100644 index 0000000..73ac5bc --- /dev/null +++ b/sw/usb/transfer.c @@ -0,0 +1,475 @@ + +/* USB transfers */ +#define _transfer_c_ + +#include "project_config.h" +#include + +EP_RECORD dev0ep = {{ 0 }}; //Endpoint data structure for uninitialized device during enumeration +EP_RECORD msd_ep[ 3 ] = {{ 0 }}; //Mass storage bulk-only transport endpoints: 1 control and 2 bulk, IN and OUT +//ep records for other classes are defined in class-specific modules + +/* macros to aid filling in TPL */ +#define INIT_VID_PID(v,p) 0x##p##v +#define INIT_CL_SC_P(c,s,p) 0x##00##p##s##c + +//const rom USB_TPL_ENTRY TplTable[ USB_NUMTARGETS + 1 ] = { +//// VID & PID or Client +//// Class, Subclass & Protocol Config Numep Eprecord Driver +//{ INIT_VID_PID( 0000, 0000 ), 0, 1, &dev0ep, 0, "Uninitialized" }, +//{ INIT_VID_PID( 0781, 5406 ), 0, 3, msd_ep, MSD_DRIVER, "Mass storage" }, //Sandisk U3 Cruzer Micro +////{ INIT_VID_PID( 0CF2, 6220 ), 0, 0 }, //ENE UB6220 +//{ INIT_CL_SC_P( 03, 01, 02 ), 0, 3, hid_ep, HIDM_DRIVER, "HID Mouse with Boot protocol" }, // +//{ INIT_VID_PID( aaaa, 5555 ), 0, 1, NULL, 0, NULL }, // +//{ INIT_CL_SC_P( 08, 06, 50 ), 0, 3, msd_ep, MSD_DRIVER, "Mass storage" } //Mass storage bulk only class +//}; + +/* control transfers function pointers */ +const CTRL_XFER ctrl_xfers[ 2 ] = { + XferCtrlND, + XferCtrlData +}; + +/* device table. Filled during enumeration */ +/* index corresponds to device address */ +/* each entry contains pointer to endpoint structure */ +/* and device class to use in various places */ +DEV_RECORD devtable[ USB_NUMDEVICES + 1 ]; + +/* Client Driver Function Pointer Table */ +CLASS_CALLBACK_TABLE ClientDriverTable[ USB_NUMCLASSES ] = { + { + MSDProbe, //Mass storage class device init + MSDEventHandler, + 0 + }, + { + HIDMProbe, //HID class device init + HIDMEventHandler, + 0 + }, + { + HIDKProbe, + HIDKEventHandler, + 0 + }, + { + DummyProbe, + DummyEventHandler, + 0 + } +}; +/* Control transfer stages function pointer table */ + + +/* USB state machine related variables */ + +BYTE usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; +BYTE usb_error; +BYTE last_usb_task_state = 0; + +/* Control transfer. Sets address, endpoint, fills control packet with necessary data, dispatches control packet, and initiates bulk IN transfer, */ +/* depending on request. Actual requests are defined as macros */ +/* return codes: */ +/* 00 = success */ +/* 01-0f = non-zero HRSLT */ +BYTE XferCtrlReq( BYTE addr, BYTE ep, BYTE bmReqType, BYTE bRequest, BYTE wValLo, BYTE wValHi, WORD wInd, WORD nbytes, BYTE* dataptr ) +{ + BOOL direction = FALSE; //request direction, IN or OUT + BYTE datastage = 1; //request data stage present or absent + BYTE rcode; + SETUP_PKT setup_pkt; + if( dataptr == NULL ) { + datastage = 0; + } + MAXreg_wr( rPERADDR, addr ); //set peripheral address + /* fill in setup packet */ + if( bmReqType & 0x80 ) { + direction = TRUE; //determine request direction + } + /* fill in setup packet */ + setup_pkt.ReqType_u.bmRequestType = bmReqType; + setup_pkt.bRequest = bRequest; + setup_pkt.wVal_u.wValueLo = wValLo; + setup_pkt.wVal_u.wValueHi = wValHi; + setup_pkt.wIndex = wInd; + setup_pkt.wLength = nbytes; + MAXbytes_wr( rSUDFIFO, 8, (BYTE *)&setup_pkt ); //transfer to setup packet FIFO + rcode = XferDispatchPkt( tokSETUP, ep ); //dispatch packet + if( rcode ) { //return HRSLT if not zero + return( rcode ); + } + rcode = ctrl_xfers[ datastage ]( addr, ep, nbytes, dataptr, direction ); //call data stage or no data stage transfer + return( rcode ); +} +/* Control transfer with data stage */ +BYTE XferCtrlData( BYTE addr, BYTE ep, WORD nbytes, BYTE* dataptr, BOOL direction ) +{ + BYTE rcode; + + //MAXreg_wr( rHCTL, bmRCVTOG1 ); //set toggle to DATA1 + if( direction ) { //IN transfer + devtable[ addr ].epinfo[ ep ].rcvToggle = bmRCVTOG1; + rcode = XferInTransfer( addr, ep, nbytes, dataptr, devtable[ addr ].epinfo[ ep ].MaxPktSize ); + if( rcode ) { + return( rcode ); + } + rcode = XferDispatchPkt( tokOUTHS, ep ); + return( rcode ); + } + else { //OUT not implemented + return( 0xff ); + } +} +/* Control transfer with status stage and no data stage */ +BYTE XferCtrlND( BYTE addr, BYTE ep, WORD nbytes, BYTE* dataptr, BOOL direction ) +{ + BYTE rcode; + + (void) addr; + (void) nbytes; + (void) dataptr; + + if( direction ) { //GET + rcode = XferDispatchPkt( tokOUTHS, ep ); + } + else { + rcode = XferDispatchPkt( tokINHS, ep ); + } + return( rcode ); +} +/* Dispatch a packet. Assumes peripheral address is set and, if necessary, sudFIFO-sendFIFO loaded. */ +/* Result code: 0 success, nonzero = error condition */ +/* If NAK, tries to re-send up to USB_NAK_LIMIT times */ +/* If bus timeout, re-sends up to USB_RETRY_LIMIT times */ +/* return codes 0x00-0x0f are HRSLT( 0x00 being success ), 0xff means timeout */ +BYTE XferDispatchPkt( BYTE token, BYTE ep ) +{ + DWORD timeout = 0;//(alt_nticks()*1000)/alt_ticks_per_second() + USB_XFER_TIMEOUT; + BYTE tmpdata; + BYTE rcode; + char retry_count = 0; + BYTE nak_count = 0; + + while( 1 ) { + MAXreg_wr( rHXFR, ( token|ep )); //launch the transfer + rcode = 0xff; + /* + while( (alt_nticks()*1000)/alt_ticks_per_second() < timeout ) { + tmpdata = MAXreg_rd( rHIRQ ); + if( tmpdata & bmHXFRDNIRQ ) { + MAXreg_wr( rHIRQ, bmHXFRDNIRQ ); //clear the interrupt + rcode = 0x00; + break; + } + } + */ + if( rcode != 0x00 ) { //exit if timeout + return( rcode ); + } + rcode = ( MAXreg_rd( rHRSL ) & 0x0f ); + if( rcode == hrNAK ) { + nak_count++; + if( nak_count == USB_NAK_LIMIT ) { + break; + } + else { + continue; + } + } + if( rcode == hrTIMEOUT ) { + retry_count++; + if( retry_count == USB_RETRY_LIMIT ) { + break; + } + else { + continue; + } + } + else break; + }//while( 1 ) + return( rcode ); +} +/* IN transfer to arbitrary endpoint. Assumes PERADDR is set. Handles multiple packets if necessary. Transfers 'nbytes' bytes. + Keep sending INs and writes data to memory area pointed by 'data' */ +/* rcode 0 if no errors. rcode 01-0f is relayed from prvXferDispatchPkt(). Rcode f0 means RCVDAVIRQ error, + fe USB xfer timeout */ +BYTE XferInTransfer( BYTE addr/* not sure if it's necessary */, BYTE ep, WORD nbytes, BYTE* data, BYTE maxpktsize ) +{ + BYTE rcode; + //BYTE i; + //BYTE tmpbyte; + BYTE pktsize; + WORD xfrlen = 0; + MAXreg_wr( rHCTL, devtable[ addr ].epinfo[ ep ].rcvToggle ); //set toggle value + while( 1 ) { // use a 'return' to exit this loop + rcode = XferDispatchPkt( tokIN, ep ); //IN packet to EP-'endpoint'. Function takes care of NAKS. + if( rcode ) { + return( rcode ); //should be 0, indicating ACK. Else return error code. + } + /* check for RCVDAVIRQ and generate error if not present */ + /* the only case when absence of RCVDAVIRQ makes sense is when toggle error occurred. Need to add handling for that */ + if(( MAXreg_rd( rHIRQ ) & bmRCVDAVIRQ ) == 0 ) { + return ( 0xf0 ); //receive error + } + pktsize = MAXreg_rd( rRCVBC ); //number of received bytes + //printf ("pktsize: %d\n", pktsize); + data = MAXbytes_rd( rRCVFIFO, pktsize, data ); + MAXreg_wr( rHIRQ, bmRCVDAVIRQ ); // Clear the IRQ & free the buffer + xfrlen += pktsize; // add this packet's byte count to total transfer length + /* The transfer is complete under two conditions: */ + /* 1. The device sent a short packet (L.T. maxPacketSize) */ + /* 2. 'nbytes' have been transferred. */ + if (( pktsize < maxpktsize ) || (xfrlen >= nbytes )) { // have we transferred 'nbytes' bytes? + if( MAXreg_rd( rHRSL ) & bmRCVTOGRD ) { //save toggle value + devtable[ addr ].epinfo[ ep ].rcvToggle = bmRCVTOG1; + } + else { + devtable[ addr ].epinfo[ ep ].rcvToggle = bmRCVTOG0; + } + return( 0 ); + } + }//while( 1 ) +} +/* initialization of USB data structures */ +void USB_init( void ) +{ + BYTE i; + for( i = 0; i < ( USB_NUMDEVICES + 1 ); i++ ) { + devtable[ i ].epinfo = NULL; //clear device table + devtable[ i ].devclass = 0; + } + devtable[ 0 ].epinfo = &dev0ep; //set single ep for uninitialized device + dev0ep.MaxPktSize = 0; + dev0ep.sndToggle = bmSNDTOG0; //set DATA0/1 toggles to 0 + dev0ep.rcvToggle = bmRCVTOG0; +} + + +USB_DEVICE_DESCRIPTOR buf = {0}; +USB_STRING_DESCRIPTOR strDesc = {0}; + +/* USB state machine. Connect/disconnect, enumeration, initialization */ +/* error codes: 01-0f HRSLT */ +/* ff - unsupported device */ +/* fe - no address available */ +/* fd - no client driver available */ +void USB_Task( void ) +{ + static DWORD usb_delay = 0; + static BYTE tmp_addr; + + BYTE rcode, tmpdata; + BYTE i; + + switch( usb_task_state & USB_STATE_MASK ) { + /* Detached state - when nothing is connected to ( or just disconnected from) USB bus */ + case( USB_STATE_DETACHED ): + switch( usb_task_state ) { + case( USB_DETACHED_SUBSTATE_INITIALIZE ): + /* cleanup device data structures */ + USB_init(); + usb_task_state = USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE; + break; + case( USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE ): + /* Do nothing */ + MAXreg_wr(rHCTL,bmSAMPLEBUS); + break; + case( USB_DETACHED_SUBSTATE_ILLEGAL ): + /* don't know what to do yet */ + break; + }//switch( usb_task_state ) + break;//( USB_STATE_DETACHED ): + /**/ + case( USB_STATE_ATTACHED ): //prepare for enumeration + switch( usb_task_state ) { + case( USB_STATE_ATTACHED ): + //TODO + //usb_delay = (alt_nticks()*1000)/alt_ticks_per_second() + 200; //initial settle 200ms + usb_delay = 0; + usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE; + break;//case( USB_STATE_ATTACHED ) + case( USB_ATTACHED_SUBSTATE_SETTLE ): //waiting for settle timer to expire + //TODO + /* + if( (alt_nticks()*1000)/alt_ticks_per_second() > usb_delay ) { + usb_task_state = USB_ATTACHED_SUBSTATE_RESET_DEVICE; + } + */ + break;//case( USB_ATTACHED_SUBSTATE_SETTLE ) + case( USB_ATTACHED_SUBSTATE_RESET_DEVICE ): + MAXreg_wr( rHIRQ, bmBUSEVENTIRQ ); //clear bus event IRQ + MAXreg_wr( rHCTL, bmBUSRST ); //issue bus reset + usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE; + break;//case( USB_ATTACHED_SUBSTATE_RESET_DEVICE ) + case( USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE ): //wait for bus reset and first SOF + if(( MAXreg_rd( rHCTL ) & bmBUSRST ) == 0 ) { + tmpdata = MAXreg_rd( rMODE ) | bmSOFKAENAB; //start SOF generation + MAXreg_wr( rMODE, tmpdata ); + usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_SOF; + } + break;//case( USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE ) + case( USB_ATTACHED_SUBSTATE_WAIT_SOF ): + if( MAXreg_rd( rHIRQ ) | bmFRAMEIRQ ) { //when first SOF received we can continue + usb_task_state = USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE; + } + break;//case( USB_ATTACHED_SUBSTATE_WAIT_SOF ) + case( USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE ): //send request for first 8 bytes of device descriptor + devtable[ 0 ].epinfo->MaxPktSize = 0x0008; //fill max packet size with minimum allowed + rcode = XferGetDevDescr( 0, 0, 8, (BYTE *)&buf ); //get device descriptor size + if( rcode == 0 ) { + devtable[ 0 ].epinfo->MaxPktSize = buf.bMaxPacketSize0; + + rcode = XferGetDevDescr( 0, 0, buf.bLength, (BYTE *)&buf ); //get full descriptor + //pull the string descriptor for the product if it exists + //hackish, store this somewhere + if (buf.iManufacturer != 0) + { + rcode = XferGetStrDescr( 0, 0, 2, buf.iManufacturer, LANG_EN_US, (BYTE *)&strDesc); + rcode = XferGetStrDescr( 0, 0, strDesc.bLength, buf.iManufacturer, LANG_EN_US, (BYTE *)&strDesc); + //printf ("Mfgr string(%i): %s\n", buf.iManufacturer, ConvUTF8ToStr(strDesc.bString, (strDesc.bLength>>1)-1)); + } + if (buf.iProduct != 0) + { + rcode = XferGetStrDescr( 0, 0, 2, buf.iProduct, LANG_EN_US, (BYTE *)&strDesc); + rcode = XferGetStrDescr( 0, 0, strDesc.bLength, buf.iProduct, LANG_EN_US, (BYTE *)&strDesc); + //printf ("Product string(%i): %s\n", buf.iProduct, ConvUTF8ToStr(strDesc.bString, (strDesc.bLength>>1)-1)); + } + usb_task_state = USB_STATE_ADDRESSING; + + } + else { + usb_error = rcode; + last_usb_task_state = usb_task_state; + usb_task_state = USB_STATE_ERROR; + } + break;//case( USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE ): + }//switch( usb_task_state ) + break;//case ( USB_STATE_ATTACHED ) + case( USB_STATE_ADDRESSING ): //give device an address + for( i = 1; i < USB_NUMDEVICES; i++ ) { + if( devtable[ i ].epinfo == NULL ) { + devtable[ i ].epinfo = devtable[ 0 ].epinfo; //set correct MaxPktSize + //devtable[ i ].epinfo->MaxPktSize = devtable[ 0 ].epinfo->MaxPktSize; //copy uninitialized device record to have correct MaxPktSize + rcode = XferSetAddr( 0, 0, i ); + if( rcode == 0 ) { + tmp_addr = i; + usb_task_state = USB_STATE_CONFIGURING; + } + else { + usb_error = rcode; //set address error + last_usb_task_state = usb_task_state; + usb_task_state = USB_STATE_ERROR; + } + break; //break if address assigned or error occurred during address assignment attempt + } + } + if( usb_task_state == USB_STATE_ADDRESSING ) { + usb_error = 0xfe; + last_usb_task_state = usb_task_state; + usb_task_state = USB_STATE_ERROR; + } + break;//case ( USB_STATE_ADDRESSING ) + case( USB_STATE_CONFIGURING ): //checking for driver + //run device class probes until one returns TRUE + for( i = 0; i < USB_NUMCLASSES; i++ ) { + rcode = ClientDriverTable[ i ].Initialize( tmp_addr, 0 ); + if( rcode == TRUE ) { + usb_task_state = USB_STATE_RUNNING; + break; + } + } + if( usb_task_state == USB_STATE_CONFIGURING ) { + usb_error = 0xfd; + last_usb_task_state = usb_task_state; + usb_task_state = USB_STATE_ERROR; + } + break;//( USB_STATE_CONFIGURING ) + case( USB_STATE_RUNNING ): + //vTaskDelay( LED_RATE ); + break;//( USB_STATE_RUNNING ) + case( USB_STATE_ERROR ): + //vTaskDelay( LED_RATE ); //stay here if error + break;//( USB_STATE_ERROR ) + default: + //Should never get here + break; + }//switch( usb_task_state & STATE_MASK ) +} + +//place-holders for MSD (mass-storage device) drivers, we don't have them ported. +//returns TRUE if device is successfully identified and configured, otherwise returns FALSE +BOOL MSDProbe( BYTE addr, DWORD flags ) +{ + (void) addr; + (void) flags; + return( FALSE ); +} + +BOOL MSDEventHandler( BYTE address, BYTE event, void *data, DWORD size ) +{ + (void) address; + (void) event; + (void) data; + (void) size; + return( FALSE ); + +} +//CDC (communication device class also not supported) +BOOL CDCProbe( BYTE address, DWORD flags ) +{ + (void) address; + (void) flags; + + return( FALSE ); + +} + +BOOL CDCEventHandler( BYTE address, BYTE event, void *data, DWORD size ) +{ + (void) address; + (void) event; + (void) data; + (void) size; + + return( FALSE ); +} + +BOOL DummyProbe( BYTE address , DWORD flags ) +{ + (void) address; + (void) flags; + + return( FALSE ); +} + +BOOL DummyEventHandler( BYTE address, BYTE event, void *data, DWORD size ) +{ + (void) address; + (void) event; + (void) data; + (void) size; + + return( FALSE ); +} +/* Function to access usb_task_state variable from outside */ +BYTE GetUsbTaskState( void ) +{ + return( usb_task_state ); +} +/* Function to access devtable[] from outside */ +DEV_RECORD* GetDevtable( BYTE index ) +{ + return( &devtable[ index ] ); +} + +char* ConvUTF8ToStr(BYTE* utf8, BYTE length) +{ + BYTE i; + for (i = 0; i < length; i++) + { + utf8[i] = utf8[2*i]; + } + utf8[length] = 0x00; + return (char*)utf8; +} diff --git a/sw/usb/transfer.h b/sw/usb/transfer.h new file mode 100644 index 0000000..5832c29 --- /dev/null +++ b/sw/usb/transfer.h @@ -0,0 +1,253 @@ +/* USB transfers support header */ + +#ifndef _transfer_h_ +#define _transfer_h_ + +/* Targeted peripheral list table */ +#define USB_NUMTARGETS 4 //number of targets in TPL, not counting uninitialized device +#define USB_NUMDEVICES 8 //number of supported devices +#define USB_NUMCLASSES 5 //number of device classes in class callback table +#define UNINIT 0 //uninitialized +#define HID_K 1 //HID Keyboard boot driver number in DEV_RECORD +#define HID_M 2 //HID Mouse boot driver number in DEV_RECORD +#define MSD 3 //Mass storage class driver number in DEV_RECORD +#define RAPHNET 4 + +/* Standard Device Requests */ + +#define USB_REQUEST_GET_STATUS 0 // Standard Device Request - GET STATUS +#define USB_REQUEST_CLEAR_FEATURE 1 // Standard Device Request - CLEAR FEATURE +#define USB_REQUEST_SET_FEATURE 3 // Standard Device Request - SET FEATURE +#define USB_REQUEST_SET_ADDRESS 5 // Standard Device Request - SET ADDRESS +#define USB_REQUEST_GET_DESCRIPTOR 6 // Standard Device Request - GET DESCRIPTOR +#define USB_REQUEST_SET_DESCRIPTOR 7 // Standard Device Request - SET DESCRIPTOR +#define USB_REQUEST_GET_CONFIGURATION 8 // Standard Device Request - GET CONFIGURATION +#define USB_REQUEST_SET_CONFIGURATION 9 // Standard Device Request - SET CONFIGURATION +#define USB_REQUEST_GET_INTERFACE 10 // Standard Device Request - GET INTERFACE +#define USB_REQUEST_SET_INTERFACE 11 // Standard Device Request - SET INTERFACE +#define USB_REQUEST_SYNCH_FRAME 12 // Standard Device Request - SYNCH FRAME + +#define USB_FEATURE_ENDPOINT_HALT 0 // CLEAR/SET FEATURE - Endpoint Halt +#define USB_FEATURE_DEVICE_REMOTE_WAKEUP 1 // CLEAR/SET FEATURE - Device remote wake-up +#define USB_FEATURE_TEST_MODE 2 // CLEAR/SET FEATURE - Test mode + +/* Setup Data Constants */ + +#define USB_SETUP_HOST_TO_DEVICE 0x00 // Device Request bmRequestType transfer direction - host to device transfer +#define USB_SETUP_DEVICE_TO_HOST 0x80 // Device Request bmRequestType transfer direction - device to host transfer +#define USB_SETUP_TYPE_STANDARD 0x00 // Device Request bmRequestType type - standard +#define USB_SETUP_TYPE_CLASS 0x20 // Device Request bmRequestType type - class +#define USB_SETUP_TYPE_VENDOR 0x40 // Device Request bmRequestType type - vendor +#define USB_SETUP_RECIPIENT_DEVICE 0x00 // Device Request bmRequestType recipient - device +#define USB_SETUP_RECIPIENT_INTERFACE 0x01 // Device Request bmRequestType recipient - interface +#define USB_SETUP_RECIPIENT_ENDPOINT 0x02 // Device Request bmRequestType recipient - endpoint +#define USB_SETUP_RECIPIENT_OTHER 0x03 // Device Request bmRequestType recipient - other + +/* USB descriptors */ + +#define USB_DESCRIPTOR_DEVICE 0x01 // bDescriptorType for a Device Descriptor. +#define USB_DESCRIPTOR_CONFIGURATION 0x02 // bDescriptorType for a Configuration Descriptor. +#define USB_DESCRIPTOR_STRING 0x03 // bDescriptorType for a String Descriptor. +#define USB_DESCRIPTOR_INTERFACE 0x04 // bDescriptorType for an Interface Descriptor. +#define USB_DESCRIPTOR_ENDPOINT 0x05 // bDescriptorType for an Endpoint Descriptor. +#define USB_DESCRIPTOR_DEVICE_QUALIFIER 0x06 // bDescriptorType for a Device Qualifier. +#define USB_DESCRIPTOR_OTHER_SPEED 0x07 // bDescriptorType for a Other Speed Configuration. +#define USB_DESCRIPTOR_INTERFACE_POWER 0x08 // bDescriptorType for Interface Power. +#define USB_DESCRIPTOR_OTG 0x09 // bDescriptorType for an OTG Descriptor. + +/* OTG SET FEATURE Constants */ +#define OTG_FEATURE_B_HNP_ENABLE 3 // SET FEATURE OTG - Enable B device to perform HNP +#define OTG_FEATURE_A_HNP_SUPPORT 4 // SET FEATURE OTG - A device supports HNP +#define OTG_FEATURE_A_ALT_HNP_SUPPORT 5 // SET FEATURE OTG - Another port on the A device supports HNP + +/* USB Endpoint Transfer Types */ +#define USB_TRANSFER_TYPE_CONTROL 0x00 // Endpoint is a control endpoint. +#define USB_TRANSFER_TYPE_ISOCHRONOUS 0x01 // Endpoint is an isochronous endpoint. +#define USB_TRANSFER_TYPE_BULK 0x02 // Endpoint is a bulk endpoint. +#define USB_TRANSFER_TYPE_INTERRUPT 0x03 // Endpoint is an interrupt endpoint. +#define bmUSB_TRANSFER_TYPE 0x03 // bit mask to separate transfer type from ISO attributes + +/* Standard Feature Selectors for CLEAR_FEATURE Requests */ +#define USB_FEATURE_ENDPOINT_STALL 0 // Endpoint recipient +#define USB_FEATURE_DEVICE_REMOTE_WAKEUP 1 // Device recipient +#define USB_FEATURE_TEST_MODE 2 // Device recipient + +/* MSD class requests. Not part of chapter 9 */ +#define USB_MSD_GET_MAX_LUN 0xFE // Device Request code to get the maximum LUN. +#define USB_MSD_RESET 0xFF // Device Request code to reset the device. + +/* 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 + +/* Class Descriptor Types */ +#define HID_DESCRIPTOR_HID 0x21 +#define HID_DESCRIPTOR_REPORT 0x22 +#define HID_DESRIPTOR_PHY 0x23 + +/* Protocol Selection */ +#define BOOT_PROTOCOL 0x00 +#define RPT_PROTOCOL 0x01 +/* HID Interface Class Code */ +#define HID_INTF 0x03 +/* HID Interface Class SubClass Codes */ +#define BOOT_INTF_SUBCLASS 0x01 +/* HID Interface Class Protocol Codes */ +#define HID_PROTOCOL_NONE 0x00 +#define HID_PROTOCOL_KEYBOARD 0x01 +#define HID_PROTOCOL_MOUSE 0x02 + +/* USB Setup Packet Structure */ +typedef struct { + union { // offset description + BYTE bmRequestType; // 0 Bit-map of request type + struct { + unsigned int recipient :5; // Recipient of the request + unsigned int type :2; // Type of request + unsigned int direction :1; // Direction of data X-fer + }; + } ReqType_u; + BYTE bRequest; // 1 Request + union { + WORD wValue; // 2 Depends on bRequest + struct { + BYTE wValueLo; + BYTE wValueHi; + }; + } wVal_u; + WORD wIndex; // 4 Depends on bRequest + WORD wLength; // 6 Depends on bRequest +} SETUP_PKT, *PSETUP_PKT; + +/* Endpoint information structure */ +/* bToggle of endpoint 0 initialized to 0xff */ +/* during enumeration bToggle is set to 00 */ +typedef struct { + BYTE epAddr; //copy from endpoint descriptor. Bit 7 indicates direction ( ignored for control endpoints ) + BYTE Attr; // Endpoint transfer type. + WORD MaxPktSize; // Maximum packet size. + BYTE Interval; // Polling interval in frames. + BYTE sndToggle; //last toggle value, bitmask for HCTL toggle bits + BYTE rcvToggle; //last toggle value, bitmask for HCTL toggle bits + /* not sure if both are necessary */ +} EP_RECORD; +/* device record structure */ +typedef struct { + EP_RECORD* epinfo; //device endpoint information + BYTE devclass; //device class +} DEV_RECORD; + +//targeted peripheral list element +//NOTE: this is currently not implemented - typically an embedded host will provide a TPL +//to enumerate supported devices. +typedef struct { + union { + DWORD val; + struct { + WORD idVendor; + WORD idProduct; + }; + struct { + BYTE bClass; + BYTE bSubClass; + BYTE bProtocol; + }; + } dev_u; + BYTE bConfig; //configuration + BYTE numep; //number of endpoints + EP_RECORD* epinfo; //endpoint information structure + BYTE CltDrv; //client driver + const char * desc; //device description +} USB_TPL_ENTRY; +/* control transfer */ +typedef BYTE (*CTRL_XFER)(BYTE addr, BYTE ep, WORD nbytes, BYTE* dataptr, + BOOL direction); +/* class driver initialization */ +typedef BOOL (*CLASS_INIT)(BYTE address, DWORD flags); +/* class driver event handler */ +typedef BOOL (*CLASS_EVENT_HANDLER)(BYTE address, BYTE event, void *data, + DWORD size); +/* Client Driver Table Structure */ +typedef struct { + CLASS_INIT Initialize; // Initialization routine + CLASS_EVENT_HANDLER EventHandler; // Event routine + DWORD flags; // Initialization flags +} CLASS_CALLBACK_TABLE; + +/* Common setup data constant combinations */ +#define bmREQ_GET_DESCR USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE //get descriptor request type +#define bmREQ_SET USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE //set request type for all but 'set feature' and 'set interface' +#define bmREQ_CL_GET_INTF USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE //get interface request type + +#define bmREQ_HIDOUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE +#define bmREQ_HIDIN USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE + +/* Function macros */ + +//char XferCtrlReq( BYTE addr, BYTE ep, BYTE bmReqType, BYTE bRequest, BYTE wValLo, BYTE wValHi, WORD wInd, WORD nbytes, char* dataptr ) +/* Set address request macro. Human-readable form of bXferCtrlReq */ +/* won't necessarily work for device in 'Configured' state */ +#define XferSetAddr( oldaddr, ep, newaddr ) \ + XferCtrlReq( oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, NULL ) +/* Set Configuration Request */ +#define XferSetConf( addr, ep, conf_value ) \ + XferCtrlReq( addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, NULL ) +///* Get configuration request */ +//#define bXferGetConf( addr, ep, urb_ptr ) bXferCtrlReq( addr, ep, 1, ( bmREQ_GET_DESCR ), USB_REQUEST_GET_CONFIGURATION, 0x00, 0x00, 0x00, urb_ptr ); +/* Get device descriptor request macro */ +#define XferGetDevDescr( addr, ep, nbytes, dataptr ) \ + XferCtrlReq( addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, dataptr ) +///* Get configuration descriptor request macro */ +#define XferGetConfDescr( addr, ep, nbytes, conf, dataptr ) \ + XferCtrlReq( addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, dataptr ) +///* Get string descriptor request macro */ +#define XferGetStrDescr( addr, ep, nbytes, index, langid, dataptr ) \ + XferCtrlReq( addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, nbytes, dataptr ) +///* Get MAX LUN MSD class request macro */ +//#define bXferGetMaxLUN( addr, intf, urb_ptr ) bXferCtrlReq( addr, 0, 1, ( bmREQ_CL_GET_INTF ), USB_MSD_GET_MAX_LUN, 0, 0, intf, urb_ptr ) +#define XferGetHIDDescr(addr, ep, desc, nbytes, dataptr) \ + XferCtrlReq(addr, ep, bmREQ_HIDIN, USB_REQUEST_GET_DESCRIPTOR, desc, HID_DESCRIPTOR_REPORT, 0x000, nbytes, dataptr) +/* class requests */ +#define XferSetProto( addr, ep, interface, protocol ) \ + XferCtrlReq( addr, ep, bmREQ_HIDOUT, HID_REQUEST_SET_PROTOCOL, protocol, 0x00, interface, 0x0000, NULL ) +#define XferGetProto( addr, ep, interface, dataptr ) \ + XferCtrlReq( addr, ep, bmREQ_HIDIN, HID_REQUEST_GET_PROTOCOL, 0x00, 0x00, interface, 0x0001, dataptr ) +#define XferGetIdle( addr, ep, interface, reportID, dataptr ) \ + XferCtrlReq( addr, ep, bmREQ_HIDIN, HID_REQUEST_GET_IDLE, reportID, 0, interface, 0x0001, dataptr ) + +/* Function prototypes */ + +BYTE XferCtrlReq(BYTE addr, BYTE ep, BYTE bmReqType, BYTE bRequest, BYTE wValLo, + BYTE wValHi, WORD wInd, WORD nbytes, BYTE* dataptr); +BYTE XferCtrlData(BYTE addr, BYTE ep, WORD nbytes, BYTE* dataptr, + BOOL direction); +BYTE XferCtrlND(BYTE addr, BYTE ep, WORD nbytes, BYTE* dataptr, BOOL direction); +//BYTE startCtrlReq( BYTE addr, BYTE ep, BYTE bmReqType, BYTE bRequest, BYTE wValLo, BYTE wValHi, WORD wInd, WORD nbytes, char* dataptr ); +BYTE XferDispatchPkt(BYTE token, BYTE ep); +BYTE XferInTransfer(BYTE addr, BYTE ep, WORD nbytes, BYTE* data, + BYTE maxpktsize); +//BYTE XferInTransfer_mps( BYTE ep, char* data, BYTE maxpktsize ); +void USB_init(void); +void USB_Task(void); +BYTE GetUsbTaskState(void); +DEV_RECORD* GetDevtable(BYTE index); + +/* Client driver routines */ +BOOL MSDProbe(BYTE address, DWORD flags); +BOOL MSDEventHandler(BYTE address, BYTE event, void *data, DWORD size); +BOOL CDCProbe(BYTE address, DWORD flags); +BOOL CDCEventHandler(BYTE address, BYTE event, void *data, DWORD size); +BOOL RaphnetProbe(BYTE address, DWORD flags); +BOOL RaphnetEventHandler(BYTE address, BYTE event, void *data, DWORD size); +BOOL DummyProbe(BYTE address, DWORD flags); +BOOL DummyEventHandler(BYTE address, BYTE event, void *data, DWORD size); + +//Function to be able to display string descriptors +char* ConvUTF8ToStr(BYTE* utf8, BYTE length); + +#endif //_transfer_h_ diff --git a/sw/usb/usb_ch9.h b/sw/usb/usb_ch9.h new file mode 100644 index 0000000..0550161 --- /dev/null +++ b/sw/usb/usb_ch9.h @@ -0,0 +1,189 @@ +/* + + USB Chapter 9 Protocol (Header File) + + This file defines data structures, constants, and macros that are used to + to support the USB Device Framework protocol described in Chapter 9 of the + USB 2.0 specification. + + In addition to that, class-specific descriptors are typedef'd here as well to keep descriptors together. + They are typedefs anyway and won't take any real code space. + */ + +#ifndef _USB_CH9_H_ +#define _USB_CH9_H_ + +/* Misc.USB constants */ +#define DEV_DESCR_LEN 18 //device descriptor length +#define CONF_DESCR_LEN 9 //configuration descriptor length +#define INTR_DESCR_LEN 9 //interface descriptor length +#define EP_DESCR_LEN 7 //endpoint descriptor length +/* Device descriptor structure */ +typedef struct { + BYTE bLength; // Length of this descriptor. + BYTE bDescriptorType; // DEVICE descriptor type (USB_DESCRIPTOR_DEVICE). + WORD bcdUSB; // USB Spec Release Number (BCD). + BYTE bDeviceClass; // Class code (assigned by the USB-IF). 0xFF-Vendor specific. + BYTE bDeviceSubClass; // Subclass code (assigned by the USB-IF). + BYTE bDeviceProtocol; // Protocol code (assigned by the USB-IF). 0xFF-Vendor specific. + BYTE bMaxPacketSize0; // Maximum packet size for endpoint 0. + WORD idVendor; // Vendor ID (assigned by the USB-IF). + WORD idProduct; // Product ID (assigned by the manufacturer). + WORD bcdDevice; // Device release number (BCD). + BYTE iManufacturer; // Index of String Descriptor describing the manufacturer. + BYTE iProduct; // Index of String Descriptor describing the product. + BYTE iSerialNumber; // Index of String Descriptor with the device's serial number. + BYTE bNumConfigurations; // Number of possible configurations. +} USB_DEVICE_DESCRIPTOR; +/* Configuration Descriptor Structure */ +typedef struct { + BYTE bLength; // Length of this descriptor. + BYTE bDescriptorType; // CONFIGURATION descriptor type (USB_DESCRIPTOR_CONFIGURATION). + WORD wTotalLength; // Total length of all descriptors for this configuration. + BYTE bNumInterfaces; // Number of interfaces in this configuration. + BYTE bConfigurationValue; // Value of this configuration (1 based). + BYTE iConfiguration; // Index of String Descriptor describing the configuration. + BYTE bmAttributes; // Configuration characteristics. + BYTE bMaxPower; // Maximum power consumed by this configuration. +} USB_CONFIGURATION_DESCRIPTOR; +/* Conf.descriptor attribute bits */ +#define USB_CFG_DSC_REQUIRED 0x80 // Required attribute +//#define USB_CFG_DSC_SELF_PWR (0x40|USB_CFG_DSC_REQUIRED) // Device is self powered. +//#define USB_CFG_DSC_REM_WAKE (0x20|USB_CFG_DSC_REQUIRED) // Device can request remote wakup +#define USB_CFG_DSC_SELF_PWR (0x40) // Device is self powered. +#define USB_CFG_DSC_REM_WAKE (0x20) // Device can request remote wakup +/* USB Interface Descriptor Structure */ +typedef struct { + BYTE bLength; // Length of this descriptor. + BYTE bDescriptorType; // INTERFACE descriptor type (USB_DESCRIPTOR_INTERFACE). + BYTE bInterfaceNumber; // Number of this interface (0 based). + BYTE bAlternateSetting; // Value of this alternate interface setting. + BYTE bNumEndpoints; // Number of endpoints in this interface. + BYTE bInterfaceClass; // Class code (assigned by the USB-IF). 0xFF-Vendor specific. + BYTE bInterfaceSubClass; // Subclass code (assigned by the USB-IF). + BYTE bInterfaceProtocol; // Protocol code (assigned by the USB-IF). 0xFF-Vendor specific. + BYTE iInterface; // Index of String Descriptor describing the interface. +} USB_INTERFACE_DESCRIPTOR; +/* USB Endpoint Descriptor Structure */ +typedef struct { + BYTE bLength; // Length of this descriptor. + BYTE bDescriptorType; // ENDPOINT descriptor type (USB_DESCRIPTOR_ENDPOINT). + BYTE bEndpointAddress; // Endpoint address. Bit 7 indicates direction (0=OUT, 1=IN). + BYTE bmAttributes; // Endpoint transfer type. + WORD wMaxPacketSize; // Maximum packet size. + BYTE bInterval; // Polling interval in frames. +} USB_ENDPOINT_DESCRIPTOR; +/* Endpoint Direction */ +#define EP_DIR_IN 0x80 // Data flows from device to host +#define EP_DIR_OUT 0x00 // Data flows from host to device +/* USB Endpoint Attributes */ +// Section: Transfer Types +#define EP_ATTR_CONTROL (0<<0) // Endoint used for control transfers +#define EP_ATTR_ISOCH (1<<0) // Endpoint used for isochronous transfers +#define EP_ATTR_BULK (2<<0) // Endpoint used for bulk transfers +#define EP_ATTR_INTR (3<<0) // Endpoint used for interrupt transfers +// Section: Synchronization Types (for isochronous enpoints) +#define EP_ATTR_NO_SYNC (0<<2) // No Synchronization +#define EP_ATTR_ASYNC (1<<2) // Asynchronous +#define EP_ATTR_ADAPT (2<<2) // Adaptive synchronization +#define EP_ATTR_SYNC (3<<2) // Synchronous +// Section: Usage Types (for isochronous endpoints) +#define EP_ATTR_DATA (0<<4) // Data Endpoint +#define EP_ATTR_FEEDBACK (1<<4) // Feedback endpoint +#define EP_ATTR_IMP_FB (2<<4) // Implicit Feedback data EP +// Section: Max Packet Sizes +#define EP_MAX_PKT_INTR_LS 8 // Max low-speed interrupt packet +#define EP_MAX_PKT_INTR_FS 64 // Max full-speed interrupt packet +#define EP_MAX_PKT_ISOCH_FS 1023 // Max full-speed isochronous packet +#define EP_MAX_PKT_BULK_FS 64 // Max full-speed bulk packet +#define EP_LG_PKT_BULK_FS 32 // Large full-speed bulk packet +#define EP_MED_PKT_BULK_FS 16 // Medium full-speed bulk packet +#define EP_SM_PKT_BULK_FS 8 // Small full-speed bulk packet +/* USB OTG Descriptor Structure */ +typedef struct { + BYTE bLength; // Length of this descriptor. + BYTE bDescriptorType; // OTG descriptor type (USB_DESCRIPTOR_OTG). + BYTE bmAttributes; // OTG attributes. +} USB_OTG_DESCRIPTOR; +/* USB String Descriptor Structure */ +typedef struct { + BYTE bLength; //size of this descriptor + BYTE bDescriptorType; //type, USB_DSC_STRING + BYTE bString[256 - 2]; //buffer for string +} USB_STRING_DESCRIPTOR; +/* Section: USB Device Qualifier Descriptor Structure */ +typedef struct { + BYTE bLength; // Size of this descriptor + BYTE bDescriptorType; // Type, always USB_DESCRIPTOR_DEVICE_QUALIFIER + WORD bcdUSB; // USB spec version, in BCD + BYTE bDeviceClass; // Device class code + BYTE bDeviceSubClass; // Device sub-class code + BYTE bDeviceProtocol; // Device protocol + BYTE bMaxPacketSize0; // EP0, max packet size + BYTE bNumConfigurations; // Number of "other-speed" configurations + BYTE bReserved; // Always zero (0) +} USB_DEVICE_QUALIFIER_DESCRIPTOR; +/* Section: USB Specification Constants */ +#define PID_OUT 0x1 // PID for an OUT token +#define PID_ACK 0x2 // PID for an ACK handshake +#define PID_DATA0 0x3 // PID for DATA0 data +#define PID_PING 0x4 // Special PID PING +#define PID_SOF 0x5 // PID for a SOF token +#define PID_NYET 0x6 // PID for a NYET handshake +#define PID_DATA2 0x7 // PID for DATA2 data +#define PID_SPLIT 0x8 // Special PID SPLIT +#define PID_IN 0x9 // PID for a IN token +#define PID_NAK 0xA // PID for a NAK handshake +#define PID_DATA1 0xB // PID for DATA1 data +#define PID_PRE 0xC // Special PID PRE (Same as PID_ERR) +#define PID_ERR 0xC // Special PID ERR (Same as PID_PRE) +#define PID_SETUP 0xD // PID for a SETUP token +#define PID_STALL 0xE // PID for a STALL handshake +#define PID_MDATA 0xF // PID for MDATA data + +#define PID_MASK_DATA 0x03 // Data PID mask +#define PID_MASK_DATA_SHIFTED (PID_MASK_DATA << 2) // Data PID shift to proper position + +#define LANG_EN_US 0x0409 //US language code, probably the only supported by string descriptors + +/* USB Token Types */ +/* defined in MAX3421E.h */ + +/* Section: OTG Descriptor Constants */ +#define OTG_HNP_SUPPORT 0x02 // OTG Descriptor bmAttributes - HNP support flag +#define OTG_SRP_SUPPORT 0x01 // OTG Descriptor bmAttributes - SRP support flag +/* Section: USB Class Code Definitions */ +#define USB_HUB_CLASSCODE 0x09 // Class code for a hub. + +/* HID class-specific defines */ + +/* USB HID Descriptor header per HID 1.1 spec */ +/* section 6.2.1 */ +/* the header is variable length. Only first class descriptor fields are defined */ +typedef union { + struct { + BYTE bLength; + BYTE bDescriptorType; + WORD bcdHID; + BYTE bCountryCode; + BYTE bNumDescriptors; + BYTE bDescrType; + WORD wDescriptorLength; + }; +} USB_HID_DESCRIPTOR; + +/* combined descriptor for easy parsing */ +typedef struct { + union { + BYTE buf[80]; + USB_DEVICE_DESCRIPTOR device; + USB_CONFIGURATION_DESCRIPTOR config; + USB_INTERFACE_DESCRIPTOR interface; + USB_ENDPOINT_DESCRIPTOR endpoint; + USB_STRING_DESCRIPTOR string; + /* class descriptors */ + USB_HID_DESCRIPTOR HID; + } descr; +} USB_DESCR; +#endif // _USB_CH9_H_ +