This shows you the differences between two versions of the page.
— |
project:usb:code:enumerated2 [2007/04/27 00:21] (current) |
||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Enumerated2 ====== | ||
+ | Like [[project: | ||
+ | |||
+ | {{project: | ||
+ | |||
+ | <code cpp> | ||
+ | #include < | ||
+ | |||
+ | // | ||
+ | // Setup PIC and CCS compiler | ||
+ | #fuses XT, PUT, NOWDT, NOPROTECT | ||
+ | #use delay(clock = 4000000) | ||
+ | |||
+ | #use rs232(baud = 19200, xmit = PIN_C6, rcv = PIN_C7, 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 | ||
+ | |||
+ | // WHICH_DESCTIPOR values | ||
+ | #define DES_NULL 0x00 | ||
+ | #define DES_DEVICE 0x01 | ||
+ | #define DES_CONFIGURATION 0x02 | ||
+ | #define DES_LANG_ID 0x03 | ||
+ | #define DES_STRING1 0x04 | ||
+ | #define DES_STRING2 0x05 | ||
+ | #define DES_STRING3 0x06 | ||
+ | |||
+ | // | ||
+ | // Global Variable Declarations | ||
+ | short DEBUGGED; | ||
+ | short CTRL_IN_JUST_FINISHED;// | ||
+ | unsigned char DEBUG_LEVEL; | ||
+ | unsigned char LOAD_INDEX; | ||
+ | unsigned char LOAD_LENGTH; | ||
+ | unsigned char SET_ADDRESS_PENDING; | ||
+ | unsigned char WHICH_DESCRIPTOR; | ||
+ | // a switch statements in the CTRL_IN interrupt handler | ||
+ | |||
+ | // | ||
+ | // Structures | ||
+ | struct REQUEST { | ||
+ | int8 bmRequestType; | ||
+ | int8 bRequest; | ||
+ | int16 wValue; | ||
+ | int16 wIndex; | ||
+ | int16 wLength; | ||
+ | }; | ||
+ | |||
+ | // | ||
+ | // USB Descriptors | ||
+ | unsigned char const sDevice[] = { | ||
+ | 0x12, | ||
+ | 0x01, | ||
+ | 0x10, | ||
+ | 0x01, | ||
+ | 0xff, | ||
+ | 0xff, | ||
+ | 0xff, | ||
+ | D12_CTRL_BUFFER_SIZE, | ||
+ | 0x04, | ||
+ | 0x71, | ||
+ | 0x11, | ||
+ | 0x02, | ||
+ | 0x00, | ||
+ | 0x00, | ||
+ | 0x01, | ||
+ | 0x02, | ||
+ | 0x00, | ||
+ | 0x01 //BYTE bNumConfigurations | ||
+ | }; | ||
+ | |||
+ | unsigned char const sConfiguration[] = { | ||
+ | 0x09, | ||
+ | 0x02, | ||
+ | 0x22, | ||
+ | 0x00, | ||
+ | 0x01, | ||
+ | 0x01, | ||
+ | 0x00, | ||
+ | 0xa0, | ||
+ | 0x05, | ||
+ | |||
+ | 0x09, | ||
+ | 0x04, | ||
+ | 0x00, | ||
+ | 0x00, | ||
+ | 0x01, | ||
+ | 0x03, | ||
+ | 0x01, | ||
+ | 0x01, | ||
+ | 0x00 //BYTE iInterface | ||
+ | |||
+ | 0x09, | ||
+ | 0x21, | ||
+ | 0x01, | ||
+ | 0x00, | ||
+ | 0x00, | ||
+ | 0x01, | ||
+ | 0x22, | ||
+ | 0x00, | ||
+ | 0x32, | ||
+ | |||
+ | 0x07, | ||
+ | 0x05, | ||
+ | 0x81, | ||
+ | 0x03, | ||
+ | 0x10, | ||
+ | 0x00, | ||
+ | 0x0A, | ||
+ | }; //Interval | ||
+ | |||
+ | unsigned char const sLang_ID[] = { | ||
+ | 0x04, | ||
+ | 0x03, | ||
+ | 0x09, | ||
+ | 0x04, | ||
+ | }; | ||
+ | |||
+ | unsigned char const sString1[] = { | ||
+ | 0x76, | ||
+ | 0x03 // | ||
+ | // Noting that text is always unicode, hence the ' | ||
+ | ' | ||
+ | ' ', 00, ' | ||
+ | ' ', 00, ' | ||
+ | ' | ||
+ | ' | ||
+ | ' ', 00, ' | ||
+ | ' | ||
+ | ' | ||
+ | ' ', 00, ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | }; | ||
+ | |||
+ | |||
+ | unsigned char const sString2[] = { | ||
+ | 0x46, // bLength | ||
+ | 0x03, // bDescriptorType = String Desc | ||
+ | // Noting that text is always unicode, hence the ' | ||
+ | ' | ||
+ | ' | ||
+ | ',', | ||
+ | ' ', 00, ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | }; | ||
+ | |||
+ | |||
+ | unsigned char const sString3[] = { | ||
+ | 0x08, // bLength | ||
+ | 0x03, // bDescriptorType = String Desc | ||
+ | // Noting that text is always unicode, hence the ' | ||
+ | ' | ||
+ | }; | ||
+ | |||
+ | |||
+ | // | ||
+ | // Function prototypes | ||
+ | void Init_PIC(); | ||
+ | void Init_D12(); | ||
+ | void D12_Write(short, | ||
+ | void D12_Read(unsigned char, int); | ||
+ | void D12_Interrupt_Handler(); | ||
+ | void D12_Handle_Ctrl_Out_EP(); | ||
+ | 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); | ||
+ | #SEPARATE void D12_Transaction_Error(int8); | ||
+ | #SEPARATE void Debug_D12_Request(struct REQUEST *pReq); | ||
+ | |||
+ | // | ||
+ | // Entry point | ||
+ | void main(void){ | ||
+ | |||
+ | DEBUGGED = 0; | ||
+ | Init_PIC(); | ||
+ | |||
+ | delay_ms(1); | ||
+ | |||
+ | // 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(); | ||
+ | |||
+ | while(TRUE); | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Used for passing commands or data to the PDIUSBD12 | ||
+ | void D12_Write(short 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: | ||
+ | // | ||
+ | // | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // 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 > 0)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(" | ||
+ | |||
+ | // Clear interrupt | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Read(buffer, | ||
+ | DEBUG1(" | ||
+ | |||
+ | if(SET_ADDRESS_PENDING){ | ||
+ | // Acknowledge token by replying with a null data packet | ||
+ | D12_Send_Null_Packet(CTRL_IN); | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | SET_ADDRESS_PENDING = FALSE; | ||
+ | CTRL_IN_JUST_FINISHED = TRUE; // Unless otherwise flagged, the next CTRL_IN interrupt just means the data was sent | ||
+ | DEBUG3(" | ||
+ | } else if (WHICH_DESCRIPTOR) { | ||
+ | unsigned char DataLen; | ||
+ | DEBUG5(" | ||
+ | |||
+ | switch(WHICH_DESCRIPTOR){ | ||
+ | case DES_DEVICE: | ||
+ | DEBUG5(" | ||
+ | // Smaller of " | ||
+ | DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX; | ||
+ | DEBUG0(" | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | DataLen += LOAD_INDEX; | ||
+ | for(; LOAD_INDEX< | ||
+ | { | ||
+ | if(DEBUG_LEVEL <= 5)printf(" | ||
+ | D12_Write(D12_DATA, | ||
+ | } | ||
+ | D12_Write(D12_COMMAND, | ||
+ | |||
+ | // Check if we're finished | ||
+ | if(LOAD_INDEX == sizeof(sDevice))WHICH_DESCRIPTOR = FALSE; | ||
+ | CTRL_IN_JUST_FINISHED = TRUE; // Unless otherwise flagged, the next CTRL_IN interrupt just means the data was sent | ||
+ | DEBUG2(" | ||
+ | break; | ||
+ | |||
+ | case DES_CONFIGURATION: | ||
+ | DEBUG5(" | ||
+ | // Smaller of " | ||
+ | DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX; | ||
+ | DEBUG0(" | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | DataLen += LOAD_INDEX; | ||
+ | for(; LOAD_INDEX< | ||
+ | { | ||
+ | DEBUG5(" | ||
+ | D12_Write(D12_DATA, | ||
+ | } | ||
+ | D12_Write(D12_COMMAND, | ||
+ | |||
+ | // Check if we're finished | ||
+ | if(LOAD_INDEX == sizeof(sConfiguration))WHICH_DESCRIPTOR = FALSE; | ||
+ | CTRL_IN_JUST_FINISHED = TRUE; // Unless otherwise flagged, the next CTRL_IN interrupt just means the data was sent | ||
+ | DEBUG2(" | ||
+ | break; | ||
+ | |||
+ | case DES_LANG_ID: | ||
+ | DEBUG5(" | ||
+ | // Smaller of " | ||
+ | DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX; | ||
+ | DEBUG0(" | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | DataLen += LOAD_INDEX; | ||
+ | for(; LOAD_INDEX< | ||
+ | { | ||
+ | DEBUG5(" | ||
+ | D12_Write(D12_DATA, | ||
+ | } | ||
+ | D12_Write(D12_COMMAND, | ||
+ | |||
+ | // Check if we're finished | ||
+ | if(LOAD_INDEX == sizeof(sLang_ID))WHICH_DESCRIPTOR = FALSE; | ||
+ | CTRL_IN_JUST_FINISHED = TRUE; // Unless otherwise flagged, the next CTRL_IN interrupt just means the data was sent | ||
+ | DEBUG2(" | ||
+ | break; | ||
+ | |||
+ | case DES_STRING1: | ||
+ | DEBUG5(" | ||
+ | // Smaller of " | ||
+ | DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX; | ||
+ | DEBUG0(" | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | DataLen += LOAD_INDEX; | ||
+ | for(; LOAD_INDEX< | ||
+ | { | ||
+ | DEBUG5(" | ||
+ | D12_Write(D12_DATA, | ||
+ | } | ||
+ | D12_Write(D12_COMMAND, | ||
+ | |||
+ | // Check if we're finished | ||
+ | if(LOAD_INDEX == sizeof(sString1))WHICH_DESCRIPTOR = FALSE; | ||
+ | CTRL_IN_JUST_FINISHED = TRUE; // Unless otherwise flagged, the next CTRL_IN interrupt just means the data was sent | ||
+ | DEBUG2(" | ||
+ | break; | ||
+ | |||
+ | case DES_STRING2: | ||
+ | DEBUG5(" | ||
+ | // Smaller of " | ||
+ | DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX; | ||
+ | DEBUG0(" | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | DataLen += LOAD_INDEX; | ||
+ | for(; LOAD_INDEX< | ||
+ | { | ||
+ | DEBUG5(" | ||
+ | D12_Write(D12_DATA, | ||
+ | } | ||
+ | D12_Write(D12_COMMAND, | ||
+ | |||
+ | // Check if we're finished | ||
+ | if(LOAD_INDEX == sizeof(sString2))WHICH_DESCRIPTOR = FALSE; | ||
+ | CTRL_IN_JUST_FINISHED = TRUE; // Unless otherwise flagged, the next CTRL_IN interrupt just means the data was sent | ||
+ | DEBUG2(" | ||
+ | break; | ||
+ | |||
+ | case DES_STRING3: | ||
+ | DEBUG5(" | ||
+ | // Smaller of " | ||
+ | DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX; | ||
+ | DEBUG0(" | ||
+ | |||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Write(D12_DATA, | ||
+ | D12_Write(D12_DATA, | ||
+ | |||
+ | DataLen += LOAD_INDEX; | ||
+ | for(; LOAD_INDEX< | ||
+ | { | ||
+ | DEBUG5(" | ||
+ | D12_Write(D12_DATA, | ||
+ | } | ||
+ | D12_Write(D12_COMMAND, | ||
+ | |||
+ | // Check if we're finished | ||
+ | if(LOAD_INDEX == sizeof(sString3))WHICH_DESCRIPTOR = FALSE; | ||
+ | CTRL_IN_JUST_FINISHED = TRUE; // Unless otherwise flagged, the next CTRL_IN interrupt just means the data was sent | ||
+ | DEBUG2(" | ||
+ | break; | ||
+ | |||
+ | default: | ||
+ | DEBUG2(" | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | } else if (CTRL_IN_JUST_FINISHED) { | ||
+ | // This interrupt is just the D12 telling us it's emptied its buffer (i.e. sent it to the host) | ||
+ | CTRL_IN_JUST_FINISHED = FALSE; | ||
+ | } else { | ||
+ | // Stall this endpoint (indicating we cannot handle the request) | ||
+ | D12_Stall_Endpt(CTRL_IN); | ||
+ | } | ||
+ | } else | ||
+ | // INT_ENDPT1_OUT | ||
+ | if (endpt_int & INT_ENDPT1_IN) { | ||
+ | DEBUG2(" | ||
+ | D12_Write(D12_COMMAND, | ||
+ | D12_Read(buffer, | ||
+ | DEBUG2(" | ||
+ | } | ||
+ | // INT_ENDPT2_OUT (Main OUT) | ||
+ | // INT_ENDPT2_IN (Main 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(){ | ||
+ | SET_ADDRESS_PENDING = 0; | ||
+ | WHICH_DESCRIPTOR = 0; | ||
+ | LOAD_INDEX = 0; | ||
+ | |||
+ | 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 | ||
+ | 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: | ||
+ | DEBUG2(" | ||
+ | D12_Standard_Request(pReq); | ||
+ | break; | ||
+ | |||
+ | // Class request | ||
+ | case 0x01: | ||
+ | DEBUG2(" | ||
+ | break; | ||
+ | |||
+ | // Endpoint request | ||
+ | case 0x02: | ||
+ | DEBUG2(" | ||
+ | break; | ||
+ | |||
+ | // Unsupported | ||
+ | default: | ||
+ | DEBUG4(" | ||
+ | DEBUG2("? | ||
+ | // 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 | ||
+ | void D12_Stall_Endpt(int8 ENDPT) | ||
+ | { | ||
+ | DEBUG5(" | ||
+ | switch(ENDPT){ | ||
+ | case 0: DEBUG5(" | ||
+ | case 1: DEBUG5(" | ||
+ | case 2: DEBUG5(" | ||
+ | case 3: DEBUG5(" | ||
+ | case 4: DEBUG5(" | ||
+ | case 5: DEBUG5(" | ||
+ | default: DEBUG5("? | ||
+ | } | ||
+ | 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) | ||
+ | { | ||
+ | switch(pReq-> | ||
+ | case GET_STATUS_REQ: | ||
+ | DEBUG5(" | ||
+ | break; | ||
+ | |||
+ | case CLEAR_FEATURE_REQ: | ||
+ | DEBUG5(" | ||
+ | break; | ||
+ | |||
+ | case SET_FEATURE_REQ: | ||
+ | DEBUG5(" | ||
+ | break; | ||
+ | |||
+ | case SET_ADDRESS_REQ: | ||
+ | DEBUG5(" | ||
+ | D12_Set_Address(pReq); | ||
+ | break; | ||
+ | |||
+ | case GET_DESCRIPTOR_REQ: | ||
+ | DEBUG5(" | ||
+ | D12_Get_Descriptor(pReq); | ||
+ | break; | ||
+ | |||
+ | case SET_DESCRIPTOR_REQ: | ||
+ | DEBUG5(" | ||
+ | break; | ||
+ | |||
+ | case GET_CONFIGURATION_REQ: | ||
+ | DEBUG5(" | ||
+ | break; | ||
+ | |||
+ | case SET_CONFIGURATION_REQ: | ||
+ | DEBUG5(" | ||
+ | break; | ||
+ | |||
+ | case GET_INTERFACE_REQ: | ||
+ | DEBUG5(" | ||
+ | break; | ||
+ | |||
+ | case SET_INTERFACE_REQ: | ||
+ | DEBUG5(" | ||
+ | break; | ||
+ | |||
+ | case SYNCH_FRAME_REQ: | ||
+ | DEBUG5(" | ||
+ | break; | ||
+ | |||
+ | default: | ||
+ | DEBUG7(" | ||
+ | DEBUG5("? | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // Service the host's request for a descriptor | ||
+ | void D12_Get_Descriptor(struct REQUEST *pReq){ | ||
+ | |||
+ | unsigned int16 ReqDataLen; | ||
+ | short supported = 1; | ||
+ | ReqDataLen = pReq-> | ||
+ | |||
+ | switch (pReq-> | ||
+ | // Device Descriptor | ||
+ | case 0x0001: | ||
+ | DEBUG3(" | ||
+ | // Don't transmit more than we need to | ||
+ | if ( ReqDataLen > (unsigned int16) sizeof(sDevice) ){ | ||
+ | ReqDataLen = sizeof(sDevice); | ||
+ | } | ||
+ | WHICH_DESCRIPTOR = DES_DEVICE; | ||
+ | break; | ||
+ | |||
+ | // Configuration Descriptor | ||
+ | case 0x0002: | ||
+ | DEBUG3(" | ||
+ | // Don't transmit more than we need to | ||
+ | if ( ReqDataLen > (unsigned int16) sizeof(sConfiguration) ){ | ||
+ | ReqDataLen = sizeof(sConfiguration); | ||
+ | } | ||
+ | WHICH_DESCRIPTOR = DES_CONFIGURATION; | ||
+ | break; | ||
+ | |||
+ | // String descriptor | ||
+ | case 0x0003: | ||
+ | DEBUG3(" | ||
+ | // Now we need to check which string was requested (low byte) | ||
+ | switch(pReq-> | ||
+ | // LANG_ID string descriptor (what language strings we can return) | ||
+ | case 0: | ||
+ | WHICH_DESCRIPTOR = DES_LANG_ID; | ||
+ | // Don't transmit more than we need to | ||
+ | if ( ReqDataLen > (unsigned int16) sizeof(sLang_ID) ){ | ||
+ | ReqDataLen = sizeof(sLang_ID); | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | case 1: | ||
+ | WHICH_DESCRIPTOR = DES_STRING1; | ||
+ | // Don't transmit more than we need to | ||
+ | if ( ReqDataLen > (unsigned int16) sizeof(sString1) ){ | ||
+ | ReqDataLen = sizeof(sString1); | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | case 2: | ||
+ | WHICH_DESCRIPTOR = DES_STRING2; | ||
+ | // Don't transmit more than we need to | ||
+ | if ( ReqDataLen > (unsigned int16) sizeof(sString2) ){ | ||
+ | ReqDataLen = sizeof(sString2); | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | case 3: | ||
+ | WHICH_DESCRIPTOR = DES_STRING3; | ||
+ | // Don't transmit more than we need to | ||
+ | if ( ReqDataLen > (unsigned int16) sizeof(sString3) ){ | ||
+ | ReqDataLen = sizeof(sString3); | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | // Unknown string descriptor | ||
+ | default: | ||
+ | DEBUG4(" | ||
+ | DEBUG4("? | ||
+ | supported = 0; | ||
+ | break; | ||
+ | } | ||
+ | break; | ||
+ | |||
+ | // Unsupported | ||
+ | default: | ||
+ | supported = 0; | ||
+ | DEBUG4(" | ||
+ | DEBUG5("? | ||
+ | // Not sure which endpoint would need to be stalled.. | ||
+ | // presumably the one the request came from (CTRL_OUT) | ||
+ | D12_Stall_Endpt(CTRL_OUT); | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | | ||
+ | // Note that if our descriptor is larger then what the host requested, | ||
+ | // | ||
+ | // | ||
+ | // However, if our descriptor is larger than the D12's CTRL_OUT buffer, then | ||
+ | // | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // | ||
+ | // 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){ | ||
+ | DEBUG5(" | ||
+ | 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(" | ||
+ | DEBUG5("? | ||
+ | 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){ | ||
+ | SET_ADDRESS_PENDING = (pReq-> | ||
+ | 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, | ||
+ | |||
+ | DEBUG3(" | ||
+ | } | ||
+ | </ |