This shows you the differences between two versions of the page.
project:usb:code:joystick [2007/04/27 00:21] |
project:usb:code:joystick [2007/04/27 00:21] (current) |
||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Joystick ====== | ||
+ | This is my near-complete code, I don't think I'll have any time to actually " | ||
+ | * A HID-Class Get_Report request has never sent non-zero data in my experiments, | ||
+ | * '' | ||
+ | * Similarly, multi-transfers for Set_Report requests have not been tested very thoroughly. The little testing done was encouraging and I'm quite confident about the code which handles it. | ||
+ | * X/Y axis of joystick don't seem to be controlled properly. I am sure the error lies in the report descriptor as I had this working before I changed that, and have not altered the code that produces and sends random data. | ||
+ | |||
+ | {{project: | ||
+ | |||
+ | <code cpp> | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | // | ||
+ | // Setup PIC and CCS compiler | ||
+ | #fuses XT, PUT, NOWDT, NOPROTECT | ||
+ | #use delay(clock = 4000000) | ||
+ | #case | ||
+ | |||
+ | // By using C6 and C7, we will make use of the 877's hardware USART abilities | ||
+ | #use rs232(baud = 19200, xmit = PIN_C4, rcv = PIN_C5, stream=DEBUG) | ||
+ | |||
+ | #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 // | ||
+ | #define D12_MAIN_BUFFER_SIZE 64 // | ||
+ | |||
+ | // 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 | ||
+ | |||
+ | // USB HID Class request types | ||
+ | #define GET_REPORT 0x01 | ||
+ | #define GET_IDLE 0x02 | ||
+ | #define GET_PROTOCOL 0x03 | ||
+ | #define SET_REPORT 0x09 | ||
+ | #define SET_IDLE 0x0A | ||
+ | #define SET_PROTOCOL 0x0B | ||
+ | |||
+ | // STATE | ||
+ | #define STATE_DEFAULT 0x00 | ||
+ | #define STATE_ADDRESSED 0x01 | ||
+ | #define STATE_CONFIGURED 0x02 | ||
+ | |||
+ | // | ||
+ | // Structures and Enumerations | ||
+ | struct REQUEST { | ||
+ | int8 bmRequestType; | ||
+ | int8 bRequest; | ||
+ | int16 wValue; | ||
+ | int16 wIndex; | ||
+ | int16 wLength; | ||
+ | }; | ||
+ | |||
+ | struct REPORT { | ||
+ | int8 buttons; | ||
+ | int8 x; | ||
+ | int8 y; | ||
+ | }; | ||
+ | |||
+ | // | ||
+ | // Global Variable Declarations | ||
+ | short DEBUGGED; | ||
+ | short CTRL_IN_BUSY; | ||
+ | short CTRL_IN_SEND_REPORT; | ||
+ | unsigned char DEBUG_LEVEL; | ||
+ | unsigned char LOAD_CIN_OFFSET; | ||
+ | unsigned int16 LOAD_CIN_LENGTH; | ||
+ | unsigned char REPORT_OUT[2]; | ||
+ | unsigned char LOAD_COUT_OFFSET; | ||
+ | unsigned int16 LOAD_COUT_LENGTH; | ||
+ | unsigned char SEND_REPORT_OFFSET; | ||
+ | unsigned char SET_ADDRESS_PENDING; | ||
+ | unsigned char CONFIGURATION; | ||
+ | unsigned char STATE; | ||
+ | unsigned char ADDRESS; | ||
+ | struct REPORT REPORT_IN; | ||
+ | |||
+ | // | ||
+ | // 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[] = { | ||
+ | // Device | ||
+ | 0x12, | ||
+ | 0x01, | ||
+ | 0x10, | ||
+ | 0x01, | ||
+ | 0x00, | ||
+ | 0x00, | ||
+ | 0x00, | ||
+ | D12_CTRL_BUFFER_SIZE, | ||
+ | 0x25, | ||
+ | 0x09, | ||
+ | 0x34, | ||
+ | 0x12, | ||
+ | 0x88, | ||
+ | 0x02, | ||
+ | 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, | ||
+ | 0x00, | ||
+ | 0x00, | ||
+ | 0x00 //BYTE iInterface | ||
+ | |||
+ | // HID | ||
+ | 0x09, | ||
+ | 0x21, | ||
+ | 0x10, | ||
+ | 0x01, | ||
+ | 0x00, | ||
+ | 0x01, | ||
+ | 0x22, | ||
+ | 0x3D, | ||
+ | 0x00, | ||
+ | |||
+ | // Endpoint | ||
+ | 0x07, | ||
+ | 0x05, | ||
+ | 0x82, | ||
+ | 0x03, | ||
+ | 0x10, | ||
+ | 0x00, | ||
+ | 0xFF, | ||
+ | |||
+ | // Lang_ID (String0) | ||
+ | 0x04, | ||
+ | 0x03, | ||
+ | 0x09, | ||
+ | 0x04, | ||
+ | |||
+ | // String1 | ||
+ | 28+2, | ||
+ | 0x03 // | ||
+ | // Noting that text is always unicode, hence the ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | |||
+ | // String2 | ||
+ | 22+2, // bLength | ||
+ | 0x03, // bDescriptorType = String Desc | ||
+ | // Noting that text is always unicode, hence the ' | ||
+ | ' | ||
+ | ' ', 00, ' | ||
+ | ' | ||
+ | |||
+ | // String3 | ||
+ | 28+2, // 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. | ||
+ | // !! Remember to update wItemLength (which is in Hex) in the HID Descriptor !! | ||
+ | 61+2, // bLength (NOT PART OF DESCRIPTOR!) | ||
+ | 0x22, // bDescriptorType (NOT PART OF DESCRIPTOR!) | ||
+ | |||
+ | // Report descriptor proper | ||
+ | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) | ||
+ | 0x09, 0x04, // USAGE (Joystick) | ||
+ | 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, 0x03, // INPUT (Cnst, | ||
+ | 0x05, 0x01, // | ||
+ | 0x09, 0x30, // USAGE (X) | ||
+ | 0x09, 0x31, // USAGE (Y) | ||
+ | 0x15, 0x81, // | ||
+ | 0x25, 0x7f, // | ||
+ | 0x75, 0x08, // | ||
+ | 0x95, 0x02, // | ||
+ | 0x81, 0x06, // INPUT (Data, | ||
+ | 0x06, 0x00, 0xff, // | ||
+ | 0x09, 0x01, // USAGE (Vendor Usage 1) | ||
+ | 0x75, 0x08, // | ||
+ | 0x95, 0x02, // | ||
+ | 0x91, 0x02, // | ||
+ | 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 Handle_Ctrl_Out_EP(); | ||
+ | #SEPARATE void Stall_Endpt(int8); | ||
+ | void Standard_Request(struct REQUEST *pReq); | ||
+ | void Get_Descriptor(struct REQUEST *pReq); | ||
+ | void Send_Null_Packet(int8); | ||
+ | void Set_Address(struct REQUEST *pReq); | ||
+ | void Set_Configuration(struct REQUEST *pReq); | ||
+ | void Class_Request(struct REQUEST *pReq); | ||
+ | #SEPARATE void Handle_Ctrl_In_EP(); | ||
+ | #SEPARATE void Transaction_Error(int8); | ||
+ | #SEPARATE void Debug_Request(struct REQUEST *pReq); | ||
+ | short Send_Report(int8); | ||
+ | |||
+ | // | ||
+ | // Entry point | ||
+ | void main(void){ | ||
+ | |||
+ | DEBUGGED = 0; | ||
+ | Init_PIC(); | ||
+ | |||
+ | REPORT_IN.x = 0; | ||
+ | REPORT_IN.y = 0; | ||
+ | REPORT_IN.buttons = 0; | ||
+ | |||
+ | // 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(); | ||
+ | |||
+ | // Random data, or GBA controlled? | ||
+ | if(port_a & 0x08){ // PIN_A3 | ||
+ | while(TRUE){ | ||
+ | REPORT_IN.x = rand(); | ||
+ | REPORT_IN.y = rand(); | ||
+ | REPORT_IN.buttons = rand(); | ||
+ | } | ||
+ | } else { | ||
+ | #use rs232(baud = 9600, xmit = PIN_C6, rcv = PIN_C7) | ||
+ | DEBUG7(" | ||
+ | #use rs232(baud = 19200, xmit = PIN_C4, rcv = PIN_C5) | ||
+ | |||
+ | enable_interrupts(INT_RDA); | ||
+ | while(TRUE); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | // | ||
+ | // Used for passing commands or data to the PDIUSBD12 | ||
+ | void D12_Write(unsigned char type, int data) | ||
+ | { | ||
+ | int8 t; | ||
+ | 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 | ||
+ | t = 64; | ||
+ | while(t--); | ||
+ | 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, t; | ||
+ | |||
+ | set_tris_d(0xFF); | ||
+ | for(i = 0; i<reads; i++) | ||
+ | { | ||
+ | output_low(D12_RD_N); | ||
+ | t = 64; | ||
+ | while(t--); | ||
+ | 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){DEBUG7(" | ||
+ | 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(" | ||
+ | Handle_Ctrl_Out_EP(); | ||
+ | } else | ||
+ | if (endpt_int & INT_CTRL_IN) { | ||
+ | DEBUG2(" | ||
+ | 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_Report(ENDPT2_IN); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // 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_CIN_OFFSET = 0; // Start at beginning on next request | ||
+ | LOAD_CIN_LENGTH = 0; // Explicity state there is nothing to send | ||
+ | CTRL_IN_BUSY = 0; // It isn't | ||
+ | CTRL_IN_SEND_REPORT = 0; // Shouldn' | ||
+ | SEND_REPORT_OFFSET = 0; // Ensure we start sending from the beginning | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Check for a SETUP token, and act upon it | ||
+ | #SEPARATE | ||
+ | void 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(TRUE/ | ||
+ | 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, | ||
+ | |||
+ | // Cancel any current control endpoint transactions | ||
+ | SEND_REPORT_OFFSET = 0; | ||
+ | LOAD_COUT_LENGTH = 0; | ||
+ | LOAD_CIN_LENGTH = 0; | ||
+ | CTRL_IN_SEND_REPORT = FALSE; | ||
+ | |||
+ | |||
+ | if(data[1] == 0x08){ // Valid setup token is 8 bytes | ||
+ | for(i=2; | ||
+ | DEBUG0(" | ||
+ | } | ||
+ | pReq = (struct REQUEST *) & | ||
+ | |||
+ | // Output some debugging info | ||
+ | if(DEBUG_LEVEL <= 1)Debug_Request(pReq); | ||
+ | |||
+ | switch((pReq-> | ||
+ | // Standard request | ||
+ | case 0x00: | ||
+ | DEBUG4(" | ||
+ | Standard_Request(pReq); | ||
+ | break; | ||
+ | |||
+ | // Class request | ||
+ | case 0x01: | ||
+ | DEBUG4(" | ||
+ | Class_Request(pReq); | ||
+ | break; | ||
+ | |||
+ | // Endpoint request | ||
+ | case 0x02: | ||
+ | DEBUG4(" | ||
+ | break; | ||
+ | |||
+ | // Unsupported | ||
+ | default: | ||
+ | DEBUG7(" | ||
+ | DEBUG7("? | ||
+ | // Stall this endpoint (indicating we cannot handle the request) | ||
+ | Stall_Endpt(CTRL_OUT); | ||
+ | Debug_Request(pReq); | ||
+ | break; | ||
+ | } | ||
+ | } else { | ||
+ | // Setup token is an invalid length | ||
+ | Stall_Endpt(CTRL_OUT); | ||
+ | Debug_Request(pReq); | ||
+ | } | ||
+ | } else | ||
+ | if(LOAD_COUT_LENGTH - LOAD_COUT_OFFSET){ // | ||
+ | |||
+ | // Smaller of "how much we're expecting" | ||
+ | buffer[0] = ((LOAD_COUT_LENGTH - LOAD_COUT_OFFSET) > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : (LOAD_COUT_LENGTH - LOAD_COUT_OFFSET); | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Read(data, | ||
+ | |||
+ | DEBUG4(" | ||
+ | DEBUG4(" | ||
+ | |||
+ | buffer[0] += 2; // Avoid calculating " | ||
+ | for(i=2; i< | ||
+ | REPORT_OUT[LOAD_COUT_OFFSET] = data[i]; | ||
+ | LOAD_COUT_OFFSET++; | ||
+ | } | ||
+ | D12_Write(D12_COMMAND, | ||
+ | |||
+ | // Print recieved data to GBA if we're done | ||
+ | if(LOAD_COUT_LENGTH - LOAD_COUT_OFFSET == 0){ | ||
+ | #use rs232(baud = 9600, xmit = PIN_C6, rcv = PIN_C7) // Hardware USART (GBA) | ||
+ | printf(" | ||
+ | for(i=0; | ||
+ | printf(" | ||
+ | } | ||
+ | printf(" | ||
+ | #use rs232(baud = 19200, xmit = PIN_C4, rcv = PIN_C5) // Software USART (Debug) | ||
+ | } | ||
+ | } else { | ||
+ | // Not a setup token and we're not expecting data | ||
+ | DEBUG7(" | ||
+ | D12_Write(D12_COMMAND, | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Read(data, | ||
+ | |||
+ | if(data[1] == 0){ | ||
+ | DEBUG2(" | ||
+ | } else { | ||
+ | data[1] = (data[1] < D12_CTRL_BUFFER_SIZE) ? data[1] : D12_CTRL_BUFFER_SIZE; | ||
+ | |||
+ | data[1] += 2; // Avoid calculating " | ||
+ | for(i=2; | ||
+ | DEBUG7(" | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } else | ||
+ | if (buffer[0] & STAT_ERROR) // | ||
+ | { | ||
+ | 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 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 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){ | ||
+ | Set_Address(pReq); | ||
+ | } else { | ||
+ | RequestOK = FALSE; | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | case GET_DESCRIPTOR_REQ: | ||
+ | DEBUG4(" | ||
+ | if(STATE == STATE_DEFAULT || STATE == STATE_ADDRESSED || STATE == STATE_CONFIGURED){ | ||
+ | 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){ | ||
+ | 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(" | ||
+ | Stall_Endpt(CTRL_OUT); | ||
+ | Debug_Request(pReq); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // 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 Get_Descriptor(struct REQUEST *pReq){ | ||
+ | |||
+ | unsigned int16 ReqDataLen, size; | ||
+ | unsigned char i, type, stringCount; | ||
+ | ReqDataLen = pReq-> | ||
+ | LOAD_CIN_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_CIN_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_CIN_OFFSET = i; // Start of descriptor | ||
+ | LOAD_CIN_LENGTH = ReqDataLen; | ||
+ | |||
+ | DEBUG0(" | ||
+ | |||
+ | break; // Quit while() loop | ||
+ | } else { | ||
+ | // Types didn't match, try the next descriptor | ||
+ | i = i + size; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | if(LOAD_CIN_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 | ||
+ | Stall_Endpt(CTRL_OUT); | ||
+ | Stall_Endpt(CTRL_IN); | ||
+ | Debug_Request(pReq); | ||
+ | } else { | ||
+ | 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 Transaction_Error(int8 error){ | ||
+ | DEBUG7(" | ||
+ | switch (error) | ||
+ | { | ||
+ | case 0x02 : //0001 PID Encoding Error | ||
+ | case 0x04 : //0010 PID Unknown | ||
+ | case 0x06 : //0011 Unexpected packet | ||
+ | case 0x08 : //0100 Token CRC Error | ||
+ | case 0x0A : //0101 Data CRC Error | ||
+ | case 0x0C : //0110 Time out Error | ||
+ | case 0x0E : //0111 Never happens | ||
+ | case 0x10 : //1000 Unexpected End of Packet | ||
+ | case 0x12 : //1001 Sent or received NAK | ||
+ | case 0x14 : //1010 Sent Stall, token received Endpt Stalled | ||
+ | case 0x16 : //1011 Overflow Error | ||
+ | case 0x1A : //1101 BitStuff Error | ||
+ | case 0x1E : //1111 Wrong DATA PID | ||
+ | break; | ||
+ | default : | ||
+ | DEBUG7(" | ||
+ | DEBUG7("? | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Output debugging info about a USB request | ||
+ | |||
+ | #SEPARATE void Debug_Request(struct REQUEST *pReq){ | ||
+ | #ifdef __DEBUGGING_ENABLED | ||
+ | printf(" | ||
+ | if(pReq-> | ||
+ | printf(" | ||
+ | } else { | ||
+ | printf(" | ||
+ | } | ||
+ | |||
+ | printf(" | ||
+ | switch(pReq-> | ||
+ | |||
+ | case 0x00: printf(" | ||
+ | case 0x01: printf(" | ||
+ | case 0x02: printf(" | ||
+ | case 0x03: printf("? | ||
+ | |||
+ | // Unsupported | ||
+ | default: | ||
+ | DEBUG7(" | ||
+ | // Stall this endpoint (indicating we cannot handle the request) | ||
+ | Stall_Endpt(CTRL_OUT); | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | printf(" | ||
+ | printf(" | ||
+ | printf(" | ||
+ | #endif | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Responds to the Set_Address request of the host, and then | ||
+ | // sets the D12 address (NB: The D12's address is actually | ||
+ | // changed AFTER the null packet phase ) | ||
+ | void Set_Address(struct REQUEST *pReq){ | ||
+ | |||
+ | // The D12 buffers the Set_Address command and executes it upon recieving an ACK | ||
+ | // in reply to the null packet we'll send in a moment. | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | ADDRESS = (pReq-> | ||
+ | STATE = STATE_ADDRESSED; | ||
+ | |||
+ | // Acknowledge token by replying with a null data packet | ||
+ | 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 | ||
+ | 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 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 Set_Configuration(struct REQUEST *pReq){ | ||
+ | short RequestOK = 1; | ||
+ | |||
+ | switch(pReq-> | ||
+ | case 0: | ||
+ | // Revert to unconfigured state | ||
+ | STATE = STATE_ADDRESSED; | ||
+ | CONFIGURATION = 0; | ||
+ | break; | ||
+ | |||
+ | case 1: | ||
+ | STATE = STATE_CONFIGURED; | ||
+ | CONFIGURATION = 1; | ||
+ | Send_Report(ENDPT2_IN); | ||
+ | break; | ||
+ | |||
+ | default: | ||
+ | DEBUG7(" | ||
+ | RequestOK = 0; | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | if(RequestOK){ | ||
+ | // Note that the CTRL_IN buffer was cleared in Handle_Ctrl_Out_EP(); | ||
+ | Send_Null_Packet(CTRL_IN); | ||
+ | } else { | ||
+ | // Indicate we don't like this request | ||
+ | Stall_Endpt(CTRL_IN); | ||
+ | Debug_Request(pReq); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Handle USB class requests | ||
+ | void Class_Request(struct REQUEST *pReq) { | ||
+ | short RequestOK = TRUE; | ||
+ | |||
+ | switch(pReq-> | ||
+ | |||
+ | case SET_REPORT: | ||
+ | DEBUG4(" | ||
+ | switch(pReq-> | ||
+ | case 1: | ||
+ | case 2: | ||
+ | case 3: | ||
+ | default: | ||
+ | } | ||
+ | DEBUG4(" | ||
+ | DEBUG4(" | ||
+ | DEBUG4(" | ||
+ | |||
+ | // Setup globals to recieve data on CTRL_OUT | ||
+ | LOAD_COUT_LENGTH = pReq-> | ||
+ | LOAD_COUT_OFFSET = 0; | ||
+ | |||
+ | // Acknowledge | ||
+ | Send_Null_Packet(CTRL_IN); | ||
+ | break; | ||
+ | |||
+ | case GET_REPORT: | ||
+ | DEBUG4(" | ||
+ | switch(pReq-> | ||
+ | case 1: | ||
+ | case 2: | ||
+ | case 3: | ||
+ | default: | ||
+ | } | ||
+ | DEBUG4(" | ||
+ | DEBUG4(" | ||
+ | DEBUG4(" | ||
+ | |||
+ | // Flag that we want to send a report | ||
+ | CTRL_IN_SEND_REPORT = TRUE; | ||
+ | |||
+ | // Acknowledge | ||
+ | Send_Null_Packet(CTRL_IN); | ||
+ | break; | ||
+ | |||
+ | default: | ||
+ | RequestOK = FALSE; | ||
+ | DEBUG7(" | ||
+ | DEBUG7("? | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | if(RequestOK == FALSE){ | ||
+ | Stall_Endpt(CTRL_OUT); | ||
+ | Debug_Request(pReq); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Responsible for sending data in response to setup tokens among other things. | ||
+ | // Activties include sending descriptors. | ||
+ | #SEPARATE | ||
+ | void Handle_Ctrl_In_EP() { | ||
+ | unsigned char buffer[2]; | ||
+ | |||
+ | // Clear interrupt | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Read(buffer, | ||
+ | DEBUG1(" | ||
+ | |||
+ | if (LOAD_CIN_LENGTH > 0) { | ||
+ | unsigned char DataLen, DataEnd; | ||
+ | |||
+ | // Smaller of " | ||
+ | DataLen = (LOAD_CIN_LENGTH > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_CIN_LENGTH; | ||
+ | DataEnd = LOAD_CIN_OFFSET + DataLen; | ||
+ | DEBUG0(" | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | for(; LOAD_CIN_OFFSET< | ||
+ | { | ||
+ | DEBUG0(" | ||
+ | D12_Write(D12_DATA, | ||
+ | LOAD_CIN_LENGTH--; | ||
+ | } | ||
+ | D12_Write(D12_COMMAND, | ||
+ | |||
+ | // Are we done? | ||
+ | if(LOAD_CIN_LENGTH == 0){ | ||
+ | // Did the last packet fill the buffer completely? | ||
+ | if((DataLen % D12_CTRL_BUFFER_SIZE) == 0){ | ||
+ | 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_SEND_REPORT) | ||
+ | { | ||
+ | // Send_Report returns true if there is not more data to send | ||
+ | CTRL_IN_SEND_REPORT = ~(Send_Report(CTRL_IN)); | ||
+ | } | ||
+ | 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; | ||
+ | DEBUG0(" | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Interrupt handler for USART hardware data reception | ||
+ | #INT_RDA | ||
+ | void Handle_UART_Reception() { | ||
+ | char c; | ||
+ | #use rs232(baud = 9600, xmit = PIN_C6, rcv = PIN_C7) // Hardware USART (GBA) | ||
+ | c = getc(); | ||
+ | #use rs232(baud = 19200, xmit = PIN_C4, rcv = PIN_C5) // Software USART (Debug) | ||
+ | DEBUG1(" | ||
+ | output_toggle(LED_N); | ||
+ | |||
+ | switch(c){ | ||
+ | case ' | ||
+ | case ' | ||
+ | case '<': | ||
+ | case '>': | ||
+ | case ' | ||
+ | case ' | ||
+ | case ' | ||
+ | case ' | ||
+ | REPORT_IN.x = rand(); | ||
+ | REPORT_IN.y = rand(); | ||
+ | REPORT_IN.buttons = rand(); | ||
+ | break; | ||
+ | case ' | ||
+ | REPORT_IN.x = 0; | ||
+ | REPORT_IN.y = 0; | ||
+ | REPORT_IN.buttons = 0; | ||
+ | break; | ||
+ | default: | ||
+ | DEBUG1(" | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Fill an endpoint with report data. | ||
+ | // Should return false if more data is to follow, and should | ||
+ | // return true if all data has been sent. | ||
+ | // | ||
+ | // Note development of this function was never finished, | ||
+ | // the author was not successful in getting multi-part | ||
+ | // transfers working (i.e. report larger than the endpoint | ||
+ | // buffer). | ||
+ | // | ||
+ | // Also this function seems to return lots of null data | ||
+ | // when sending the report though CTRL_IN. | ||
+ | short Send_Report(int8 ENDPT) { | ||
+ | int8 DataLen; | ||
+ | DEBUG2(" | ||
+ | |||
+ | switch(ENDPT){ | ||
+ | case CTRL_OUT: | ||
+ | case CTRL_IN: | ||
+ | DataLen = D12_CTRL_BUFFER_SIZE; | ||
+ | break; | ||
+ | |||
+ | case ENDPT2_OUT: | ||
+ | case ENDPT2_IN: | ||
+ | DataLen = D12_MAIN_BUFFER_SIZE; | ||
+ | break; | ||
+ | |||
+ | default: | ||
+ | DEBUG7(" | ||
+ | return TRUE; // i.e. no more data to send | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | // Smaller of "data left to send" and "size of endpoint buffer" | ||
+ | DataLen = ((sizeof(struct REPORT) - SEND_REPORT_OFFSET) > DataLen) ? DataLen : (sizeof(struct REPORT) - SEND_REPORT_OFFSET); | ||
+ | |||
+ | DEBUG2(" | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | DataLen += SEND_REPORT_OFFSET; | ||
+ | for(; SEND_REPORT_OFFSET < DataLen; SEND_REPORT_OFFSET++){ | ||
+ | D12_Write(D12_DATA, | ||
+ | DEBUG2(" | ||
+ | } | ||
+ | D12_Write(D12_COMMAND, | ||
+ | |||
+ | // Are we done? | ||
+ | if(SEND_REPORT_OFFSET == sizeof(struct REPORT)){ | ||
+ | SEND_REPORT_OFFSET = 0; | ||
+ | return TRUE; | ||
+ | } else { | ||
+ | return FALSE; | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </ |