This shows you the differences between two versions of the page.
project:usb:code:hid-mouse [2007/04/27 00:21] |
project:usb:code:hid-mouse [2007/04/27 00:21] (current) |
||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== HID-Mouse ====== | ||
+ | This firmware enumerates the device as an HID-complient device, and then further ' | ||
+ | |||
+ | It doesn' | ||
+ | |||
+ | > Actually I found a fix for this (taken from my [[: | ||
+ | > | ||
+ | >>The 'D12 has special support built in to it for changing the address value at the correct time. | ||
+ | >> | ||
+ | >> | ||
+ | >> | ||
+ | >>The D12 will wait until the appropriate time (right after the zero length packet representing the STATUS phase gets IN'd by the host) before it actually switches from the default address (0) to the new address. | ||
+ | |||
+ | Note that this is the first version of the firmware that uses the parsing/ | ||
+ | |||
+ | {{project: | ||
+ | |||
+ | <code cpp> | ||
+ | #include < | ||
+ | |||
+ | // | ||
+ | // Setup PIC and CCS compiler | ||
+ | #fuses XT, PUT, NOWDT, NOPROTECT | ||
+ | #use delay(clock = 4000000) | ||
+ | |||
+ | #use rs232(baud = 19200, xmit = PIN_C4, rcv = PIN_C5, disable_ints) | ||
+ | // By using C6 and C7, we will make use of the 877's hardware USART abilities | ||
+ | |||
+ | #use fast_io(a) | ||
+ | #use standard_io(c) | ||
+ | #byte port_a = 5 | ||
+ | set_tris_a(0xFF); | ||
+ | |||
+ | #use standard_io(b) | ||
+ | #byte port_d = 8 | ||
+ | |||
+ | // | ||
+ | // Debug options | ||
+ | // 0 is most verbose, 1 less so etc | ||
+ | |||
+ | // Comment out the following line to bulid without debugging | ||
+ | // NB: This creates a LOT of warnings during compilation: | ||
+ | // a "Code has no effect" | ||
+ | |||
+ | #define __DEBUGGING_ENABLED | ||
+ | |||
+ | #ifdef __DEBUGGING_ENABLED | ||
+ | #define DEBUG0 if(DEBUG_LEVEL <= 0)DEBUGGED=1; | ||
+ | #define DEBUG1 if(DEBUG_LEVEL <= 1)DEBUGGED=1; | ||
+ | #define DEBUG2 if(DEBUG_LEVEL <= 2)DEBUGGED=1; | ||
+ | #define DEBUG3 if(DEBUG_LEVEL <= 3)DEBUGGED=1; | ||
+ | #define DEBUG4 if(DEBUG_LEVEL <= 4)DEBUGGED=1; | ||
+ | #define DEBUG5 if(DEBUG_LEVEL <= 5)DEBUGGED=1; | ||
+ | #define DEBUG6 if(DEBUG_LEVEL <= 6)DEBUGGED=1; | ||
+ | #define DEBUG7 if(DEBUG_LEVEL <= 7)DEBUGGED=1; | ||
+ | #else | ||
+ | #define DEBUG0 | ||
+ | #define DEBUG1 | ||
+ | #define DEBUG2 | ||
+ | #define DEBUG3 | ||
+ | #define DEBUG4 | ||
+ | #define DEBUG5 | ||
+ | #define DEBUG6 | ||
+ | #define DEBUG7 | ||
+ | #endif | ||
+ | |||
+ | // | ||
+ | // Definitions | ||
+ | |||
+ | // Yellow LED | ||
+ | #define LED_N PIN_B7 | ||
+ | |||
+ | // Constants | ||
+ | #define ON 1 | ||
+ | #define OFF 0 | ||
+ | |||
+ | #define D12_DATA 0 | ||
+ | #define D12_COMMAND 1 | ||
+ | |||
+ | // D12 pins | ||
+ | #define D12_A0 PIN_B6 | ||
+ | #define D12_WR_N PIN_B2 | ||
+ | #define D12_RD_N PIN_B1 | ||
+ | #define D12_SUSPEND PIN_B4 | ||
+ | #define D12_INT_N PIN_B0 | ||
+ | |||
+ | // D12 Constants | ||
+ | #define D12_CTRL_BUFFER_SIZE 16 // | ||
+ | |||
+ | // D12 Endpoint indexes (for bitwise OR'ing with base commands) | ||
+ | #define CTRL_OUT 0 | ||
+ | #define CTRL_IN 1 | ||
+ | #define ENDPT1_OUT 2 | ||
+ | #define ENDPT1_IN 3 | ||
+ | #define ENDPT2_OUT 4 | ||
+ | #define ENDPT2_IN 5 | ||
+ | |||
+ | // D12 Commands | ||
+ | #define SET_ADDRESS 0xD0 | ||
+ | #define SET_ENDPT_ENABLE 0xD8 | ||
+ | #define SET_MODE 0xF3 | ||
+ | #define SET_DMA 0xFB | ||
+ | #define READ_INT 0xF4 | ||
+ | #define SELECT_ENDPT 0x00 // + endpoint index | ||
+ | #define READ_ENDPT_STATUS 0x40 // + endpoint index | ||
+ | #define READ_BUFFER 0xF0 | ||
+ | #define WRITE_BUFFER 0xF0 | ||
+ | #define SET_ENDPT_STATUS 0x40 // + endpoint index | ||
+ | #define ACK_SETUP 0xF1 | ||
+ | #define CLEAR_BUFFER 0xF2 | ||
+ | #define VALIDATE_BUFFER 0xFA | ||
+ | #define SEND_RESUME 0xF6 | ||
+ | #define READ_FRAME_NUM 0xF5 | ||
+ | |||
+ | // D12 Interrupt byte 1 | ||
+ | #define INT_CTRL_OUT 0x01 // bit 0 | ||
+ | #define INT_CTRL_IN 0x02 // bit 1 | ||
+ | #define INT_ENDPT1_OUT 0x04 // bit 2 | ||
+ | #define INT_ENDPT1_IN 0x08 // bit 3 | ||
+ | #define INT_ENDPT2_OUT 0x10 // bit 4 | ||
+ | #define INT_ENDPT2_IN 0x20 // bit 5 | ||
+ | #define INT_BUS_RESET 0x40 // bit 6 | ||
+ | #define INT_SUSPEND_CHANGE 0x80 // bit 7 | ||
+ | |||
+ | // D12 Interrupt byte 2 | ||
+ | #define DMA_EOT_INT 0x01 // bit 0 | ||
+ | |||
+ | // D12 Last transaction status | ||
+ | #define STAT_XFER_SUCCESS 0x01 // | ||
+ | #define STAT_ERROR 0x1E // | ||
+ | #define STAT_SETUP 0x20 // | ||
+ | #define STAT_DATA 0x40 // | ||
+ | #define STAT_NOT_READ 0x80 // | ||
+ | |||
+ | // USB bmRequestTypes | ||
+ | #define REQTYPE_XFER_DIRECTION 0x80 // | ||
+ | #define REQTYPE_CMD_TYPE 0x60 // | ||
+ | #define REQTYPE_RECIPIENT 0x1F // | ||
+ | |||
+ | // USB Standard request types | ||
+ | #define GET_STATUS_REQ 0x00 | ||
+ | #define CLEAR_FEATURE_REQ 0x01 | ||
+ | #define SET_FEATURE_REQ 0x03 | ||
+ | #define SET_ADDRESS_REQ 0x05 | ||
+ | #define GET_DESCRIPTOR_REQ 0x06 | ||
+ | #define SET_DESCRIPTOR_REQ 0x07 | ||
+ | #define GET_CONFIGURATION_REQ 0x08 | ||
+ | #define SET_CONFIGURATION_REQ 0x09 | ||
+ | #define GET_INTERFACE_REQ 0x0A | ||
+ | #define SET_INTERFACE_REQ 0x0B | ||
+ | #define SYNCH_FRAME_REQ 0x0C | ||
+ | |||
+ | // STATE | ||
+ | #define STATE_DEFAULT 0x00 | ||
+ | #define STATE_ADDRESSED 0x01 | ||
+ | #define STATE_CONFIGURED 0x02 | ||
+ | |||
+ | // | ||
+ | // Global Variable Declarations | ||
+ | short DEBUGGED; | ||
+ | short CTRL_IN_BUSY; | ||
+ | unsigned char DEBUG_LEVEL; | ||
+ | unsigned char LOAD_OFFSET; | ||
+ | unsigned int16 LOAD_LENGTH; | ||
+ | unsigned char SET_ADDRESS_PENDING; | ||
+ | unsigned char CONFIGURATION; | ||
+ | unsigned char STATE; | ||
+ | unsigned char ADDRESS; | ||
+ | |||
+ | // | ||
+ | // Structures and Enumerations | ||
+ | struct REQUEST { | ||
+ | int8 bmRequestType; | ||
+ | int8 bRequest; | ||
+ | int16 wValue; | ||
+ | int16 wIndex; | ||
+ | int16 wLength; | ||
+ | }; | ||
+ | |||
+ | struct { | ||
+ | int8 IDLE_TIME; | ||
+ | } *HID; | ||
+ | |||
+ | struct { | ||
+ | int8 x; | ||
+ | int8 y; | ||
+ | short a; | ||
+ | short b; | ||
+ | } *MOUSE; | ||
+ | |||
+ | // | ||
+ | // USB Descriptors | ||
+ | |||
+ | // Constants are stored in ROM, and hence cannot be accessed via pointers. | ||
+ | // Instead of having to duplicate code for each descriptor, we instead | ||
+ | // store them in a large block and ' | ||
+ | // is requested. | ||
+ | unsigned char const DESCRIPTORS[] = { | ||
+ | 0x12, | ||
+ | 0x01, | ||
+ | 0x10, | ||
+ | 0x01, | ||
+ | 0x00, | ||
+ | 0x00, | ||
+ | 0x00, | ||
+ | D12_CTRL_BUFFER_SIZE, | ||
+ | 0xC7, | ||
+ | 0x05, | ||
+ | 0x13, | ||
+ | 0x01, | ||
+ | 0x01, | ||
+ | 0x00, | ||
+ | 0x01, | ||
+ | 0x02, | ||
+ | 0x03, | ||
+ | 0x01 //BYTE bNumConfigurations | ||
+ | |||
+ | // Configuration & associated subdescriptors | ||
+ | // Configuration | ||
+ | 0x09, | ||
+ | 0x02, | ||
+ | 0x22, | ||
+ | 0x00, | ||
+ | 0x01, | ||
+ | 0x01, | ||
+ | 0x00, | ||
+ | 0xa0, | ||
+ | 0x32, | ||
+ | |||
+ | // Interface | ||
+ | 0x09, | ||
+ | 0x04, | ||
+ | 0x00, | ||
+ | 0x00, | ||
+ | 0x01, | ||
+ | 0x03, | ||
+ | 0x01, | ||
+ | 0x02, | ||
+ | 0x00 //BYTE iInterface | ||
+ | |||
+ | // HID | ||
+ | 0x09, | ||
+ | 0x21, | ||
+ | 0x10, | ||
+ | 0x01, | ||
+ | 0x00, | ||
+ | 0x01, | ||
+ | 0x22, | ||
+ | 0x32, | ||
+ | 0x00, | ||
+ | |||
+ | // Endpoint | ||
+ | 0x07, | ||
+ | 0x05, | ||
+ | 0x82, | ||
+ | 0x03, | ||
+ | 0x10, | ||
+ | 0x00, | ||
+ | 0xFF, | ||
+ | |||
+ | // Lang_ID (String0) | ||
+ | 0x04, | ||
+ | 0x03, | ||
+ | 0x09, | ||
+ | 0x04, | ||
+ | |||
+ | // String1 | ||
+ | 30, | ||
+ | 0x03 // | ||
+ | // Noting that text is always unicode, hence the ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | |||
+ | // String2 | ||
+ | 20, // bLength | ||
+ | 0x03, // bDescriptorType = String Desc | ||
+ | // Noting that text is always unicode, hence the ' | ||
+ | ' | ||
+ | ' | ||
+ | |||
+ | // String3 | ||
+ | 30, // bLength | ||
+ | 0x03, // bDescriptorType = String Desc | ||
+ | // Noting that text is always unicode, hence the ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | |||
+ | // Report, NOTE that bLength and bDescriptorType are NOT part of the report descriptor, | ||
+ | // and have been addded so that Get_Descriptor() can parse this char array. | ||
+ | 52, // bLength (NOT PART OF DESCRIPTOR!) | ||
+ | 0x22, // bDescriptorType (NOT PART OF DESCRIPTOR!) | ||
+ | |||
+ | // Report descriptor proper | ||
+ | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) | ||
+ | 0x09, 0x02, // USAGE (Mouse) | ||
+ | 0xa1, 0x01, // COLLECTION (Application) | ||
+ | 0x09, 0x01, // USAGE (Pointer) | ||
+ | 0xa1, 0x00, // | ||
+ | 0x05, 0x09, // | ||
+ | 0x19, 0x01, // | ||
+ | 0x29, 0x03, // | ||
+ | 0x15, 0x00, // | ||
+ | 0x25, 0x01, // | ||
+ | 0x95, 0x03, // | ||
+ | 0x75, 0x01, // | ||
+ | 0x81, 0x02, // INPUT (Data, | ||
+ | 0x95, 0x01, // | ||
+ | 0x75, 0x05, // | ||
+ | 0x81, 0x01, // INPUT (Cnst, | ||
+ | 0x05, 0x01, // | ||
+ | 0x09, 0x30, // USAGE (X) | ||
+ | 0x09, 0x31, // USAGE (Y) | ||
+ | 0x15, 0x81, // | ||
+ | 0x25, 0x7f, // | ||
+ | 0x75, 0x08, // | ||
+ | 0x95, 0x02, // | ||
+ | 0x81, 0x06, // INPUT (Data, | ||
+ | 0xc0, // | ||
+ | 0xc0 // END_COLLECTION | ||
+ | }; | ||
+ | |||
+ | |||
+ | // | ||
+ | // Function prototypes | ||
+ | void Init_PIC(); | ||
+ | void Init_D12(); | ||
+ | void D12_Write(unsigned char, int); | ||
+ | void D12_Read(unsigned char, int); | ||
+ | void D12_Interrupt_Handler(); | ||
+ | #SEPARATE void D12_Handle_Ctrl_Out_EP(); | ||
+ | #SEPARATE void D12_Stall_Endpt(int8); | ||
+ | void D12_Standard_Request(struct REQUEST *pReq); | ||
+ | void D12_Get_Descriptor(struct REQUEST *pReq); | ||
+ | void D12_Send_Null_Packet(int8); | ||
+ | void D12_Set_Address(struct REQUEST *pReq); | ||
+ | void D12_Set_Configuration(struct REQUEST *pReq); | ||
+ | void D12_Class_Request(struct REQUEST *pReq); | ||
+ | #SEPARATE void D12_Handle_Ctrl_In_EP(); | ||
+ | #SEPARATE char gba_getch(); | ||
+ | #SEPARATE void D12_Transaction_Error(int8); | ||
+ | #SEPARATE void Debug_D12_Request(struct REQUEST *pReq); | ||
+ | void Send_Mouse_Data(); | ||
+ | |||
+ | // | ||
+ | // Entry point | ||
+ | void main(void){ | ||
+ | |||
+ | DEBUGGED = 0; | ||
+ | Init_PIC(); | ||
+ | |||
+ | MOUSE-> | ||
+ | MOUSE-> | ||
+ | MOUSE-> | ||
+ | MOUSE-> | ||
+ | |||
+ | // No need to init the D12, as it will trigger a bus reset interrupt as soon | ||
+ | // as it is powered / connects to the USB bus (not too sure which though) | ||
+ | |||
+ | if(input(D12_INT_N) == 0) D12_Interrupt_Handler(); | ||
+ | |||
+ | // Pre-determined movement, or GBA controlled? | ||
+ | if(port_a & 0x08){ // PIN_A3 | ||
+ | MOUSE-> | ||
+ | MOUSE-> | ||
+ | MOUSE-> | ||
+ | MOUSE-> | ||
+ | while(TRUE); | ||
+ | } else { | ||
+ | #use rs232(baud = 9600, xmit = PIN_C6, rcv = PIN_C7, disable_ints) | ||
+ | printf(" | ||
+ | #use rs232(baud = 19200, xmit = PIN_C4, rcv = PIN_C5, disable_ints) | ||
+ | |||
+ | enable_interrupts(INT_RDA); | ||
+ | while(TRUE); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | // | ||
+ | // Used for passing commands or data to the PDIUSBD12 | ||
+ | void D12_Write(unsigned char type, int data) | ||
+ | { | ||
+ | int8 i; | ||
+ | switch(type) | ||
+ | { | ||
+ | case D12_DATA: | ||
+ | case D12_COMMAND: | ||
+ | set_tris_d(0x00); | ||
+ | output_high(D12_RD_N); | ||
+ | |||
+ | if(type == D12_COMMAND) | ||
+ | output_high(D12_A0); | ||
+ | else | ||
+ | output_low(D12_A0); | ||
+ | |||
+ | port_d = data; // Setup bus | ||
+ | // | ||
+ | i = 8; | ||
+ | while(i--); | ||
+ | output_low(D12_WR_N); | ||
+ | output_high(D12_WR_N); | ||
+ | |||
+ | if(type == D12_COMMAND) | ||
+ | output_low(D12_A0); | ||
+ | break; | ||
+ | default: | ||
+ | DEBUG7(" | ||
+ | DEBUG7(" | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Used for reading data from the PDIUSBD12 | ||
+ | void D12_Read(unsigned char* buffer, int reads) | ||
+ | { | ||
+ | int i; | ||
+ | |||
+ | set_tris_d(0xFF); | ||
+ | for(i = 0; i<reads; i++) | ||
+ | { | ||
+ | output_low(D12_RD_N); | ||
+ | buffer[i] = port_d; // Latch in the bus | ||
+ | output_high(D12_RD_N); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // FIXME: Probably want to save some registers when handling | ||
+ | // this interrupt, as it takes quite a long time. | ||
+ | #INT_EXT | ||
+ | void D12_Interrupt_Handler() | ||
+ | { | ||
+ | unsigned char buffer[2], endpt_int, other_int; | ||
+ | |||
+ | // Loop in case another interrupt is triggered while we handle this one | ||
+ | while(! input(D12_INT_N)){ | ||
+ | // Don't add newlines if we've not sent any data to the terminal | ||
+ | if(DEBUGGED == 1)printf(" | ||
+ | DEBUGGED = 0; | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Read(buffer, | ||
+ | endpt_int = buffer[0]; | ||
+ | other_int = buffer[1]; | ||
+ | |||
+ | DEBUG0(" | ||
+ | |||
+ | if (endpt_int & INT_BUS_RESET) { | ||
+ | DEBUG7(" | ||
+ | // D12 Firmware programming guide recommends using a flag for this... ahh well | ||
+ | Init_D12(); | ||
+ | } else | ||
+ | if (endpt_int & INT_SUSPEND_CHANGE) { | ||
+ | DEBUG1(" | ||
+ | } else | ||
+ | if(endpt_int & INT_CTRL_OUT) { | ||
+ | // Control Out Endpoint interrupt | ||
+ | DEBUG3(" | ||
+ | D12_Handle_Ctrl_Out_EP(); | ||
+ | } else | ||
+ | if (endpt_int & INT_CTRL_IN) { | ||
+ | DEBUG3(" | ||
+ | D12_Handle_Ctrl_In_EP(); | ||
+ | } else | ||
+ | if (endpt_int & INT_ENDPT1_OUT){ | ||
+ | DEBUG3(" | ||
+ | } | ||
+ | if (endpt_int & INT_ENDPT1_IN) { | ||
+ | DEBUG3(" | ||
+ | } else | ||
+ | if(endpt_int & INT_ENDPT2_OUT) { | ||
+ | DEBUG3(" | ||
+ | } else | ||
+ | if (endpt_int & INT_ENDPT2_IN) { | ||
+ | DEBUG0(" | ||
+ | Send_Mouse_Data(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Setups the hardware at its most basic level | ||
+ | void Init_PIC(){ | ||
+ | output_low(LED_N); | ||
+ | |||
+ | set_tris_b(0x01); | ||
+ | set_tris_d(0x00); | ||
+ | port_d = 0xFF; | ||
+ | |||
+ | output_high(D12_RD_N); | ||
+ | output_high(D12_WR_N); | ||
+ | |||
+ | output_low(D12_A0); | ||
+ | output_low(D12_SUSPEND); | ||
+ | |||
+ | disable_interrupts(GLOBAL); | ||
+ | |||
+ | ext_int_edge(H_TO_L); | ||
+ | enable_interrupts(INT_EXT); | ||
+ | clear_interrupt(INT_EXT); | ||
+ | enable_interrupts(GLOBAL); | ||
+ | |||
+ | DEBUG_LEVEL = port_a & 0x07;// Read DIP switches (3 lower digits only) | ||
+ | DEBUG7(" | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Takes the D12 out of reset and connects it to the USB bus | ||
+ | void Init_D12(){ | ||
+ | STATE = STATE_DEFAULT; | ||
+ | CONFIGURATION = 0; // Unconfigured | ||
+ | ADDRESS = 0; // Revert to default address | ||
+ | SET_ADDRESS_PENDING = 0; // Is not pending | ||
+ | LOAD_OFFSET = 0; // Start at beginning on next request | ||
+ | LOAD_LENGTH = 0; // Explicity state there is nothing to send | ||
+ | |||
+ | DEBUG0(" | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | DEBUG0(" | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | DEBUG0(" | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Check for a SETUP token, and act upon it | ||
+ | #SEPARATE | ||
+ | void D12_Handle_Ctrl_Out_EP() { | ||
+ | |||
+ | unsigned char buffer[2]; | ||
+ | unsigned char data[D12_CTRL_BUFFER_SIZE]; | ||
+ | struct REQUEST *pReq; // Will be pointed to to data[] when appropriate | ||
+ | int i; | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Read(buffer, | ||
+ | DEBUG3(" | ||
+ | |||
+ | if(buffer[0] & STAT_NOT_READ){ // | ||
+ | // Nothing yet ;) | ||
+ | } | ||
+ | |||
+ | if(buffer[0] & STAT_XFER_SUCCESS){ | ||
+ | if(buffer[0] & STAT_SETUP){ // | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Read(data, | ||
+ | DEBUG2(" | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Read(data, | ||
+ | DEBUG2(" | ||
+ | |||
+ | // Acknowledge that we like this (NB CTRL_OUT is already selected) | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | |||
+ | // Prevent previous data from being sent (need to ack_setup to re-enable clear buffer) | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | |||
+ | |||
+ | if(data[1] == 0x08){ // Valid setup token is 8 bytes | ||
+ | for(i=2; | ||
+ | DEBUG0(" | ||
+ | } | ||
+ | pReq = (struct REQUEST *) & | ||
+ | |||
+ | // Output some debugging info | ||
+ | Debug_D12_Request(pReq); | ||
+ | |||
+ | switch((pReq-> | ||
+ | // Standard request | ||
+ | case 0x00: | ||
+ | DEBUG4(" | ||
+ | D12_Standard_Request(pReq); | ||
+ | break; | ||
+ | |||
+ | // Class request | ||
+ | case 0x01: | ||
+ | DEBUG4(" | ||
+ | D12_Class_Request(pReq); | ||
+ | break; | ||
+ | |||
+ | // Endpoint request | ||
+ | case 0x02: | ||
+ | DEBUG4(" | ||
+ | break; | ||
+ | |||
+ | // Unsupported | ||
+ | default: | ||
+ | DEBUG7(" | ||
+ | DEBUG7("? | ||
+ | // Stall this endpoint (indicating we cannot handle the request) | ||
+ | D12_Stall_Endpt(CTRL_OUT); | ||
+ | break; | ||
+ | } | ||
+ | } else { | ||
+ | // Setup token is an invalid length | ||
+ | D12_Stall_Endpt(CTRL_OUT); | ||
+ | } | ||
+ | } | ||
+ | } else | ||
+ | if (buffer[0] & STAT_ERROR) // | ||
+ | { | ||
+ | D12_Transaction_Error(buffer[0] & STAT_ERROR); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Stalls an enpoint, so the D12 will return STALL to the host, | ||
+ | // which usually indicates we don't understand / support the host's | ||
+ | // request | ||
+ | #SEPARATE | ||
+ | void D12_Stall_Endpt(int8 ENDPT) | ||
+ | { | ||
+ | DEBUG7(" | ||
+ | switch(ENDPT){ | ||
+ | case 0: DEBUG7(" | ||
+ | case 1: DEBUG7(" | ||
+ | case 2: DEBUG7(" | ||
+ | case 3: DEBUG7(" | ||
+ | case 4: DEBUG7(" | ||
+ | case 5: DEBUG7(" | ||
+ | default: DEBUG7("? | ||
+ | } | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Handle standard USB requests, such as those encountered in | ||
+ | // SETUP tokens | ||
+ | void D12_Standard_Request(struct REQUEST *pReq) { | ||
+ | short RequestOK = TRUE; | ||
+ | |||
+ | switch(pReq-> | ||
+ | case GET_STATUS_REQ: | ||
+ | DEBUG4(" | ||
+ | if(STATE == STATE_CONFIGURED || (STATE == STATE_ADDRESSED && ADDRESS == 0)){ | ||
+ | } else { | ||
+ | RequestOK = FALSE; | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | case CLEAR_FEATURE_REQ: | ||
+ | DEBUG4(" | ||
+ | if(STATE == STATE_CONFIGURED || (STATE == STATE_ADDRESSED && ADDRESS == 0)){ | ||
+ | } else { | ||
+ | RequestOK = FALSE; | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | case SET_FEATURE_REQ: | ||
+ | DEBUG4(" | ||
+ | if(STATE == STATE_CONFIGURED || (STATE == STATE_ADDRESSED && ADDRESS == 0)){ | ||
+ | } else { | ||
+ | RequestOK = FALSE; | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | case SET_ADDRESS_REQ: | ||
+ | DEBUG4(" | ||
+ | if(STATE == STATE_DEFAULT || STATE == STATE_ADDRESSED){ | ||
+ | D12_Set_Address(pReq); | ||
+ | } else { | ||
+ | RequestOK = FALSE; | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | case GET_DESCRIPTOR_REQ: | ||
+ | DEBUG4(" | ||
+ | if(STATE == STATE_DEFAULT || STATE == STATE_ADDRESSED || STATE == STATE_CONFIGURED){ | ||
+ | D12_Get_Descriptor(pReq); | ||
+ | } else { | ||
+ | RequestOK = FALSE; | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | case SET_DESCRIPTOR_REQ: | ||
+ | DEBUG4(" | ||
+ | if(STATE == STATE_ADDRESSED || STATE == STATE_CONFIGURED){ | ||
+ | } else { | ||
+ | RequestOK = FALSE; | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | case GET_CONFIGURATION_REQ: | ||
+ | DEBUG4(" | ||
+ | if(STATE == STATE_ADDRESSED || STATE == STATE_CONFIGURED){ // | ||
+ | } else { | ||
+ | RequestOK = FALSE; | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | case SET_CONFIGURATION_REQ: | ||
+ | DEBUG4(" | ||
+ | if(STATE == STATE_ADDRESSED || STATE == STATE_CONFIGURED){ | ||
+ | D12_Set_Configuration(pReq); | ||
+ | } else { | ||
+ | RequestOK = FALSE; | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | case GET_INTERFACE_REQ: | ||
+ | DEBUG4(" | ||
+ | if(STATE == STATE_CONFIGURED){ | ||
+ | } else { | ||
+ | RequestOK = FALSE; | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | case SET_INTERFACE_REQ: | ||
+ | DEBUG4(" | ||
+ | if(STATE == STATE_CONFIGURED){ | ||
+ | } else { | ||
+ | RequestOK = FALSE; | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | case SYNCH_FRAME_REQ: | ||
+ | DEBUG4(" | ||
+ | if(STATE == STATE_CONFIGURED){ | ||
+ | } else { | ||
+ | RequestOK = FALSE; | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | default: | ||
+ | RequestOK = FALSE; | ||
+ | DEBUG7(" | ||
+ | DEBUG7("? | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | if(RequestOK == FALSE){ | ||
+ | DEBUG7(" | ||
+ | D12_Stall_Endpt(CTRL_OUT); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Service the host's request for a descriptor. | ||
+ | // Descriptos are stored end-to-end in a large block of ROM | ||
+ | // as the constant ' | ||
+ | // This block of ROM is searched/ | ||
+ | // the requested descriptor. | ||
+ | void D12_Get_Descriptor(struct REQUEST *pReq){ | ||
+ | |||
+ | unsigned int16 ReqDataLen, size; | ||
+ | unsigned char i, type, stringCount; | ||
+ | ReqDataLen = pReq-> | ||
+ | LOAD_LENGTH = 0; // Ensure we don't send unrelated data | ||
+ | |||
+ | DEBUG0(" | ||
+ | i = 0; | ||
+ | stringCount = 0; | ||
+ | while(i < sizeof(DESCRIPTORS)-1){ | ||
+ | // Read details from current descriptor | ||
+ | size = (unsigned int16) DESCRIPTORS[i]; | ||
+ | type = DESCRIPTORS[i+1]; | ||
+ | DEBUG0(" | ||
+ | |||
+ | if(type == (pReq-> | ||
+ | |||
+ | // Take care of exceptions | ||
+ | switch(type){ | ||
+ | |||
+ | // Configuration descriptors are expected to be sent with all their subordinate | ||
+ | // descriptors. | ||
+ | |||
+ | case 0x02: // Configuration descriptor | ||
+ | size = (unsigned int16) DESCRIPTORS[i+2]; | ||
+ | size |= (unsigned int16) DESCRIPTORS[i+3] << 8; // wTotalLength high-byte | ||
+ | DEBUG0(" | ||
+ | break; | ||
+ | |||
+ | // There can be multiple string descriptors, | ||
+ | |||
+ | case 0x03: // String descriptor | ||
+ | // Is there a need to try the next string? | ||
+ | if( (pReq-> | ||
+ | stringCount++; | ||
+ | i = i + size; // Jump to next descriptor in loop | ||
+ | continue; | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | |||
+ | // Some descriptors do NOT contain bLength and bType bytes, these have been added | ||
+ | // by the author to facilitate this parsing approach. Hence we reduce the size by 2 | ||
+ | // bytes and increase the LOAD_OFFSET by 2 bytes in such cases. | ||
+ | // The artificial bLength value denotes the number of bytes until the next descriptor, | ||
+ | // to ensure this code can 'step into' the next descriptor. | ||
+ | |||
+ | case 0x22: // Report descriptor | ||
+ | i = i + 2; | ||
+ | size = size - 2; | ||
+ | DEBUG0(" | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | // Don't transmit more than we need to | ||
+ | if( ReqDataLen > size) ReqDataLen = size; | ||
+ | |||
+ | // Setup globals for the Ctrl_IN interrupt handler | ||
+ | LOAD_OFFSET = i; // Start of descriptor | ||
+ | LOAD_LENGTH = ReqDataLen; | ||
+ | |||
+ | DEBUG0(" | ||
+ | |||
+ | break; // Quit while() loop | ||
+ | } else { | ||
+ | // Types didn't match, try the next descriptor | ||
+ | i = i + size; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | if(LOAD_LENGTH == 0){ // We didn't find a match | ||
+ | DEBUG7(" | ||
+ | DEBUG7("? | ||
+ | // Not sure which endpoint would need to be stalled.. | ||
+ | // presumably both as it's a bidirection endpoint, of | ||
+ | // sorts | ||
+ | D12_Stall_Endpt(CTRL_OUT); | ||
+ | D12_Stall_Endpt(CTRL_IN); | ||
+ | } else { | ||
+ | D12_Handle_Ctrl_In_EP(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // When an error code is encountered from a 'Read Last Transaction Command' | ||
+ | // this function is called to clean up the mess | ||
+ | |||
+ | #SEPARATE void D12_Transaction_Error(int8 error){ | ||
+ | DEBUG7(" | ||
+ | switch (error) | ||
+ | { | ||
+ | case 0x02 : //0001 PID Encoding Error | ||
+ | break; | ||
+ | case 0x04 : //0010 PID Unknown | ||
+ | break; | ||
+ | case 0x06 : //0011 Unexpected packet | ||
+ | break; | ||
+ | case 0x08 : //0100 Token CRC Error | ||
+ | break; | ||
+ | case 0x0A : //0101 Data CRC Error | ||
+ | break; | ||
+ | case 0x0C : //0110 Time out Error | ||
+ | break; | ||
+ | case 0x0E : //0111 Never happens | ||
+ | break; | ||
+ | case 0x10 : //1000 Unexpected End of Packet | ||
+ | break; | ||
+ | case 0x12 : //1001 Sent or received NAK | ||
+ | break; | ||
+ | case 0x14 : //1010 Sent Stall, token received Endpt Stalled | ||
+ | break; | ||
+ | case 0x16 : //1011 Overflow Error | ||
+ | break; | ||
+ | case 0x1A : //1101 BitStuff Error | ||
+ | break; | ||
+ | case 0x1E : //1111 Wrong DATA PID | ||
+ | break; | ||
+ | default : | ||
+ | DEBUG7(" | ||
+ | DEBUG7("? | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Output debugging info about a USB request | ||
+ | |||
+ | #SEPARATE void Debug_D12_Request(struct REQUEST *pReq){ | ||
+ | |||
+ | DEBUG4(" | ||
+ | if(pReq-> | ||
+ | DEBUG4(" | ||
+ | } else { | ||
+ | DEBUG4(" | ||
+ | } | ||
+ | |||
+ | DEBUG4(" | ||
+ | switch(pReq-> | ||
+ | // Device | ||
+ | case 0x00: | ||
+ | DEBUG4(" | ||
+ | break; | ||
+ | |||
+ | // Interface | ||
+ | case 0x01: | ||
+ | DEBUG4(" | ||
+ | break; | ||
+ | |||
+ | // Endpoint | ||
+ | case 0x02: | ||
+ | DEBUG4(" | ||
+ | break; | ||
+ | |||
+ | // Other | ||
+ | case 0x03: | ||
+ | DEBUG4("? | ||
+ | break; | ||
+ | |||
+ | // Unsupported | ||
+ | default: | ||
+ | DEBUG7(" | ||
+ | // Stall this endpoint (indicating we cannot handle the request) | ||
+ | D12_Stall_Endpt(CTRL_OUT); | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | DEBUG4(" | ||
+ | DEBUG4(" | ||
+ | DEBUG4(" | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Responds to the Set_Address request of the host, and then | ||
+ | // sets the D12 address (NB: The address is changed AFTER the | ||
+ | // we respond to the host) | ||
+ | void D12_Set_Address(struct REQUEST *pReq){ | ||
+ | |||
+ | // Acknowledge token by replying with a null data packet | ||
+ | D12_Send_Null_Packet(CTRL_IN); | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | ADDRESS = (pReq-> | ||
+ | STATE = STATE_ADDRESSED; | ||
+ | |||
+ | CTRL_IN_BUSY = TRUE; // Unless something else is pending, the next CTRL_IN interrupt just means the data was sent | ||
+ | DEBUG3(" | ||
+ | |||
+ | } | ||
+ | |||
+ | // | ||
+ | // Send a zero-length packet to the selected endpoint | ||
+ | // Useful for empty data stages in setup transactions, | ||
+ | // as signalling the end of a stream when the last packet was | ||
+ | // full (i.e. don't let the host assume there is no more data, | ||
+ | // tell it!) | ||
+ | void D12_Send_Null_Packet(int8 ENDPT) { | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | |||
+ | CTRL_IN_BUSY = TRUE; | ||
+ | |||
+ | DEBUG3(" | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Responds to Set_Confugration request of the host, checks the | ||
+ | // requested configuration is supported and responds appropriately with | ||
+ | // an ACK or a STALL, and updates the global state variable. | ||
+ | void D12_Set_Configuration(struct REQUEST *pReq){ | ||
+ | short RequestOK = 1; | ||
+ | |||
+ | switch(pReq-> | ||
+ | //case 0: | ||
+ | // // Revert to unconfigured state | ||
+ | // STATE = STATE_ADDRESSED; | ||
+ | // | ||
+ | //break; | ||
+ | |||
+ | case 1: | ||
+ | STATE = STATE_CONFIGURED; | ||
+ | CONFIGURATION = 1; | ||
+ | Send_Mouse_Data(); | ||
+ | break; | ||
+ | |||
+ | default: | ||
+ | DEBUG7(" | ||
+ | RequestOK = 0; | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | if(RequestOK){ | ||
+ | // Note that the CTRL_IN buffer was cleared in D12_Handle_Ctrl_Out_EP(); | ||
+ | D12_Send_Null_Packet(CTRL_IN); | ||
+ | } else { | ||
+ | // Indicate we don't like this request | ||
+ | D12_Stall_Endpt(CTRL_IN); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Handle USB class requests | ||
+ | void D12_Class_Request(struct REQUEST *pReq) { | ||
+ | short RequestOK = TRUE; | ||
+ | |||
+ | #define HID_SET_IDLE 0x0a | ||
+ | |||
+ | switch(pReq-> | ||
+ | case HID_SET_IDLE: | ||
+ | HID-> | ||
+ | D12_Send_Null_Packet(CTRL_IN); | ||
+ | break; | ||
+ | |||
+ | default: | ||
+ | RequestOK = FALSE; | ||
+ | DEBUG7(" | ||
+ | DEBUG7("? | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | if(RequestOK == FALSE){ | ||
+ | D12_Stall_Endpt(CTRL_OUT); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Responsible for sending data in response to setup tokens among other things. | ||
+ | // Activties include sending descriptors. | ||
+ | #SEPARATE | ||
+ | void D12_Handle_Ctrl_In_EP() { | ||
+ | unsigned char buffer[2]; | ||
+ | |||
+ | // Clear interrupt | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Read(buffer, | ||
+ | DEBUG1(" | ||
+ | |||
+ | if (LOAD_LENGTH > 0) { | ||
+ | unsigned char DataLen, DataEnd; | ||
+ | DEBUG5(" | ||
+ | |||
+ | // Smaller of " | ||
+ | DataLen = (LOAD_LENGTH > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH; | ||
+ | DataEnd = LOAD_OFFSET + DataLen; | ||
+ | DEBUG0(" | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | for(; LOAD_OFFSET< | ||
+ | { | ||
+ | DEBUG0(" | ||
+ | D12_Write(D12_DATA, | ||
+ | LOAD_LENGTH--; | ||
+ | } | ||
+ | D12_Write(D12_COMMAND, | ||
+ | |||
+ | // Are we done? | ||
+ | if(LOAD_LENGTH == 0){ | ||
+ | // Did the last packet fill the buffer completely? | ||
+ | if((DataLen % D12_CTRL_BUFFER_SIZE) == 0){ | ||
+ | D12_Send_Null_Packet(CTRL_IN); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | CTRL_IN_BUSY = TRUE; // Unless something else is pending, the next CTRL_IN interrupt just means the data was sent | ||
+ | |||
+ | DEBUG2(" | ||
+ | } else if (CTRL_IN_BUSY) { | ||
+ | // This interrupt is just the D12 telling us it's emptied its buffer (i.e. sent it to the host) | ||
+ | CTRL_IN_BUSY = FALSE; | ||
+ | } else { | ||
+ | // Stall this endpoint (indicating we cannot handle the request) | ||
+ | D12_Stall_Endpt(CTRL_IN); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Uses compiler directives to make a wrapper for getch(), | ||
+ | // that specifically uses seperate pins from the normal | ||
+ | // debug statements | ||
+ | #SEPARATE | ||
+ | char gba_getch(){ | ||
+ | #use rs232(baud = 9600, xmit = PIN_C6, rcv = PIN_C7, disable_ints) | ||
+ | return getch(); | ||
+ | #use rs232(baud = 19200, xmit = PIN_C4, rcv = PIN_C5, disable_ints) | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Interrupt handler for USART hardware data reception | ||
+ | #INT_RDA | ||
+ | void Handle_UART_Reception() { | ||
+ | output_toggle(LED_N); | ||
+ | |||
+ | switch(gba_getch()){ | ||
+ | case ' | ||
+ | MOUSE-> | ||
+ | break; | ||
+ | |||
+ | case ' | ||
+ | MOUSE-> | ||
+ | break; | ||
+ | |||
+ | case '<': | ||
+ | MOUSE-> | ||
+ | break; | ||
+ | |||
+ | case '>': | ||
+ | MOUSE-> | ||
+ | break; | ||
+ | |||
+ | case ' | ||
+ | MOUSE-> | ||
+ | break; | ||
+ | |||
+ | case ' | ||
+ | MOUSE-> | ||
+ | break; | ||
+ | |||
+ | case ' | ||
+ | MOUSE-> | ||
+ | MOUSE-> | ||
+ | MOUSE-> | ||
+ | MOUSE-> | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Fill our main endpoint with our report data | ||
+ | void Send_Mouse_Data() { | ||
+ | DEBUG1(" | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | } | ||
+ | </ |