Like Enumerated, except that this uses ROM to keep the descriptors — which does unfortunately mean repeating blocks of code, as pointers cannot be used to access the ROM.
Download compiled HEX firmware
#include <16f877.h> //-------------------------------------------------------- // 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); // All input #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" for each DEBUGx statement #define __DEBUGGING_ENABLED #ifdef __DEBUGGING_ENABLED #define DEBUG0 if(DEBUG_LEVEL <= 0)DEBUGGED=1; if(DEBUG_LEVEL <= 0)printf #define DEBUG1 if(DEBUG_LEVEL <= 1)DEBUGGED=1; if(DEBUG_LEVEL <= 1)printf #define DEBUG2 if(DEBUG_LEVEL <= 2)DEBUGGED=1; if(DEBUG_LEVEL <= 2)printf #define DEBUG3 if(DEBUG_LEVEL <= 3)DEBUGGED=1; if(DEBUG_LEVEL <= 3)printf #define DEBUG4 if(DEBUG_LEVEL <= 4)DEBUGGED=1; if(DEBUG_LEVEL <= 4)printf #define DEBUG5 if(DEBUG_LEVEL <= 5)DEBUGGED=1; if(DEBUG_LEVEL <= 5)printf #define DEBUG6 if(DEBUG_LEVEL <= 6)DEBUGGED=1; if(DEBUG_LEVEL <= 6)printf #define DEBUG7 if(DEBUG_LEVEL <= 7)DEBUGGED=1; if(DEBUG_LEVEL <= 7)printf #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 // (Bytes) // 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 // bit 0 (1=Success) #define STAT_ERROR 0x1E // bits 1-4 #define STAT_SETUP 0x20 // bit 5 (1=Last packet has setup token) #define STAT_DATA 0x40 // bit 6 (0/1 to indicate DATA0 / DATA1 tag of packet) #define STAT_NOT_READ 0x80 // bit 7 (1=Previous status not read (i.e. missed)) // USB bmRequestTypes #define REQTYPE_XFER_DIRECTION 0x80 // 0=OUT (Host to device), 1=IN (Device to host) #define REQTYPE_CMD_TYPE 0x60 // 0=Standard 1=Class 3=Vendor #define REQTYPE_RECIPIENT 0x1F // 0=Device, 1=Interface, 2=Endpoint, 3=Other // 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; // Flags if a DEBUGx statement was executed short CTRL_IN_JUST_FINISHED;// Used to determine if D12 CTRL_IN interrupt is because the buffer was (successfully) sent unsigned char DEBUG_LEVEL; unsigned char LOAD_INDEX; // Used when sending descriptors unsigned char LOAD_LENGTH; // Because sometimes the host requests only part of a descriptor unsigned char SET_ADDRESS_PENDING; unsigned char WHICH_DESCRIPTOR; // Descriptors are constants, so we can't use pointers and need to use // a switch statements in the CTRL_IN interrupt handler //-------------------------------------------------------- // Structures struct REQUEST { int8 bmRequestType; int8 bRequest; int16 wValue; int16 wIndex; int16 wLength; // Data Phase's data length }; //-------------------------------------------------------- // USB Descriptors unsigned char const sDevice[] = { 0x12, //BYTE bLength 0x01, //BYTE bDescriptorType 0x10, //WORD (Lo) bcdUSB version supported 0x01, //WORD (Hi) bcdUSB version supported 0xff, //BYTE bDeviceClass 0xff, //BYTE bDeviceSubClass 0xff, //BYTE bDeviceProtocol D12_CTRL_BUFFER_SIZE, //BYTE bMaxPacketSize (probably 16) 0x04, //WORD (Lo) idVendor 0x71, //WORD (Hi) idVendor 0x11, //WORD (Lo) idProduct, For Philips Hub mouse 0x02, //WORD (Hi) idProduct, For Philips Hub mouse 0x00, //WORD (Lo) bcdDevice 0x00, //WORD (Hi) bcdDevice 0x01, //BYTE iManufacturer 0x02, //BYTE iProduct 0x00, //BYTE iSerialNumber 0x01 //BYTE bNumConfigurations }; unsigned char const sConfiguration[] = { 0x09, //BYTE bLength (Configuration descriptor) 0x02, //BYTE bDescriptorType //Assigned by USB 0x22, //WORD (Lo) wTotalLength 0x00, //WORD (Hi) wTotalLength 0x01, //BYTE bNumInterfaces 0x01, //BYTE bConfigurationValue 0x00, //BYTE iConfiguration 0xa0, //BYTE bmAttributes, Bus powered and remote wakeup 0x05, //BYTE MaxPower 0x09, //BYTE bLength (Interface descriptor) 0x04, //BYTE bDescriptionType, assigned by USB 0x00, //BYTE bInterfaceNumber 0x00, //BYTE bAlternateSetting 0x01, //BYTE bNumEndpoints, uses 1 endpoints 0x03, //BYTE bInterfaceClass, HID Class - 0x03 0x01, //BYTE bInterfaceSubClass 0x01, //BYTE bInterfaceProtocol 0x00 //BYTE iInterface 0x09, //BYTE bLength (HID Descriptor) 0x21, //BYTE bDescriptorType 0x01, //WORD (Lo) bcdHID 0x00, //WORD (Hi) bcdHID 0x00, //BYTE bCountryCode 0x01, //BYTE bNumDescriptors 0x22, //BYTE bReportDescriptorType 0x00, //WORD (Lo) wItemLength 0x32, //WORD (Hi) wItemLength 0x07, //BYTE bLength (Endpoint Descriptor) 0x05, //BYTE bDescriptorType, assigned by USB 0x81, //BYTE bEndpointAddress, IN endpoint, endpoint 1 0x03, //BYTE bmAttributes, Interrupt endpoint 0x10, //WORD (Lo) wMaxPacketSize 0x00, //WORD (Hi) wMaxPacketSize 0x0A, //Polling Time }; //Interval unsigned char const sLang_ID[] = { 0x04, // bLength 0x03, // bDescriptorType = String Desc 0x09, // wLangID (Lo) (Lang ID for English = 0x0409) 0x04, // wLangID (Hi) (Lang ID for English = 0x0409) }; unsigned char const sString1[] = { 0x76, // bLength 0x03 // bDescriptorType = String Desc // Noting that text is always unicode, hence the 'padding' 'P', 00, 'h', 00, 'i', 00, 'l', 00, 'i', 00, ' ', 00, '-', 00, '-', 00, 'p', 00, 's', 00, ' ', 00, 'S', 00, 'e', 00, 'm', 00, 'i', 00, 'c', 00, 'o', 00, 'n', 00, 'd', 00, 'u', 00, 'c', 00, 't', 00, 'o', 00, 'r', 00, 's', 00, ' ', 00, '-', 00, ' ', 00, 'A', 00, 's', 00, 'i', 00, 'a', 00, ' ', 00, 'P', 00, 'r', 00, 'o', 00, 'd', 00, 'u', 00, 'c', 00, 't', 00, ' ', 00, 'I', 00, 'n', 00, 'n', 00, 'o', 00, 'v', 00, 'a', 00, 't', 00, 'i', 00, 'o', 00, 'n', 00, ' ', 00, 'C', 00, 'e', 00, 'n', 00, 't', 00, 'r', 00, 'e', 00, }; unsigned char const sString2[] = { 0x46, // bLength 0x03, // bDescriptorType = String Desc // Noting that text is always unicode, hence the 'padding' 'P', 00, 'D', 00, 'I', 00, 'U', 00, 'S', 00, 'B', 00, 'D', 00, '1', 00, '1', 00, ' ', 00, ',', 00, ' ', 00, 'D', 00, '1', 00, '1', 00, ' ', 00, 'F', 00, 'i', 00, 'r', 00, 'm', 00, 'w', 00, 'a', 00, 'r', 00, 'e', 00, ' ', 00, 'V', 00, 'e', 00, 'r', 00, 's', 00, 'i', 00, 'o', 00, 'n', 00, ' ', 00, '1', 00 }; unsigned char const sString3[] = { 0x08, // bLength 0x03, // bDescriptorType = String Desc // Noting that text is always unicode, hence the 'padding' 'X', 00, 'W', 00, 'L', 00 }; //-------------------------------------------------------- // Function prototypes void Init_PIC(); void Init_D12(); void D12_Write(short, int); 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(); // Put pins in known state, reset D12 etc 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); // Wait for interrupt } //-------------------------------------------------------- // 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); // Set bus to output mode output_high(D12_RD_N); // Ensure we don't conflict with RD_N if(type == D12_COMMAND) output_high(D12_A0); else output_low(D12_A0); port_d = data; // Setup bus //delay_ms(1); // Data settling time i = 8; while(i--); // Settling time (in PIC cycles) output_low(D12_WR_N); // strobe for at least 20ns output_high(D12_WR_N); if(type == D12_COMMAND) output_low(D12_A0); break; default: //DEBUG7("Error in D12_Write(), unknown type: 0x%x!\r\n", type); //DEBUG7("Expecting one of:\r\n\t 0x%x\r\n\t0x%x\r\n", D12_COMMAND, D12_DATA); } } //-------------------------------------------------------- // Used for reading data from the PDIUSBD12 void D12_Read(unsigned char* buffer, int reads) { int i; set_tris_d(0xFF); // Set bus to intput mode 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("\r\n", DEBUGGED); DEBUGGED = 0; D12_Write(D12_COMMAND, READ_INT); D12_Read(buffer, 2); endpt_int = buffer[0]; other_int = buffer[1]; DEBUG0("IR=%x,%x ", endpt_int, other_int); if (endpt_int & INT_BUS_RESET) { DEBUG7("BR "); // D12 Firmware programming guide recommends using a flag for this... ahh well Init_D12(); // Reset D12 settings (not a chip reset) } else if (endpt_int & INT_SUSPEND_CHANGE) { DEBUG1("SC "); } else if(endpt_int & INT_CTRL_OUT) { // Control Out Endpoint interrupt DEBUG3("CO "); D12_Handle_Ctrl_Out_EP(); } else if (endpt_int & INT_CTRL_IN) { DEBUG3("CI "); // Clear interrupt D12_Write(D12_COMMAND, READ_ENDPT_STATUS + CTRL_IN); D12_Read(buffer, 1); DEBUG1("LT=%x ", buffer[0]); if(SET_ADDRESS_PENDING){ // Acknowledge token by replying with a null data packet D12_Send_Null_Packet(CTRL_IN); D12_Write(D12_COMMAND, SET_ADDRESS); D12_Write(D12_DATA, SET_ADDRESS_PENDING); // This contains the address to be set SET_ADDRESS_PENDING = FALSE; CTRL_IN_JUST_FINISHED = TRUE; // Unless otherwise flagged, the next CTRL_IN interrupt just means the data was sent DEBUG3("AS "); } else if (WHICH_DESCRIPTOR) { unsigned char DataLen; DEBUG5("DR "); switch(WHICH_DESCRIPTOR){ case DES_DEVICE: DEBUG5("DDR "); // Smaller of "length of data to send" and "control buffer size" DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX; DEBUG0("DataLen=%x ", DataLen); D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_IN); D12_Write(D12_COMMAND, WRITE_BUFFER); D12_Write(D12_DATA, 0x00); // First byte is reserved D12_Write(D12_DATA, DataLen); // Num of data bytes DataLen += LOAD_INDEX; for(; LOAD_INDEX<DataLen; LOAD_INDEX++) { if(DEBUG_LEVEL <= 5)printf("%x ", sDevice[LOAD_INDEX]); D12_Write(D12_DATA, sDevice[LOAD_INDEX]); } D12_Write(D12_COMMAND, VALIDATE_BUFFER); // Mark the buffer as ready to go! // 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("DS "); break; case DES_CONFIGURATION: DEBUG5("CDR "); // Smaller of "length of data to send" and "control buffer size" DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX; DEBUG0("DataLen=%x ", DataLen); D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_IN); D12_Write(D12_COMMAND, WRITE_BUFFER); D12_Write(D12_DATA, 0x00); // First byte is reserved D12_Write(D12_DATA, DataLen); // Num of data bytes DataLen += LOAD_INDEX; for(; LOAD_INDEX<DataLen; LOAD_INDEX++) { DEBUG5("%x ", sConfiguration[LOAD_INDEX]); D12_Write(D12_DATA, sConfiguration[LOAD_INDEX]); } D12_Write(D12_COMMAND, VALIDATE_BUFFER); // Mark the buffer as ready to go! // 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("DS "); break; case DES_LANG_ID: DEBUG5("SDR "); // Smaller of "length of data to send" and "control buffer size" DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX; DEBUG0("DataLen=%x ", DataLen); D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_IN); D12_Write(D12_COMMAND, WRITE_BUFFER); D12_Write(D12_DATA, 0x00); // First byte is reserved D12_Write(D12_DATA, DataLen); // Num of data bytes DataLen += LOAD_INDEX; for(; LOAD_INDEX<DataLen; LOAD_INDEX++) { DEBUG5("%x ", sLang_ID[LOAD_INDEX]); D12_Write(D12_DATA, sLang_ID[LOAD_INDEX]); } D12_Write(D12_COMMAND, VALIDATE_BUFFER); // Mark the buffer as ready to go! // 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("DS "); break; case DES_STRING1: DEBUG5("SDR "); // Smaller of "length of data to send" and "control buffer size" DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX; DEBUG0("DataLen=%x ", DataLen); D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_IN); D12_Write(D12_COMMAND, WRITE_BUFFER); D12_Write(D12_DATA, 0x00); // First byte is reserved D12_Write(D12_DATA, DataLen); // Num of data bytes DataLen += LOAD_INDEX; for(; LOAD_INDEX<DataLen; LOAD_INDEX++) { DEBUG5("%x ", sString1[LOAD_INDEX]); D12_Write(D12_DATA, sString1[LOAD_INDEX]); } D12_Write(D12_COMMAND, VALIDATE_BUFFER); // Mark the buffer as ready to go! // 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("DS "); break; case DES_STRING2: DEBUG5("SDR "); // Smaller of "length of data to send" and "control buffer size" DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX; DEBUG0("DataLen=%x ", DataLen); D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_IN); D12_Write(D12_COMMAND, WRITE_BUFFER); D12_Write(D12_DATA, 0x00); // First byte is reserved D12_Write(D12_DATA, DataLen); // Num of data bytes DataLen += LOAD_INDEX; for(; LOAD_INDEX<DataLen; LOAD_INDEX++) { DEBUG5("%x ", sString2[LOAD_INDEX]); D12_Write(D12_DATA, sString2[LOAD_INDEX]); } D12_Write(D12_COMMAND, VALIDATE_BUFFER); // Mark the buffer as ready to go! // 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("DS "); break; case DES_STRING3: DEBUG5("SDR "); // Smaller of "length of data to send" and "control buffer size" DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX; DEBUG0("DataLen=%x ", DataLen); D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_IN); D12_Write(D12_COMMAND, WRITE_BUFFER); D12_Write(D12_DATA, 0x00); // First byte is reserved D12_Write(D12_DATA, DataLen); // Num of data bytes DataLen += LOAD_INDEX; for(; LOAD_INDEX<DataLen; LOAD_INDEX++) { DEBUG5("%x ", sString3[LOAD_INDEX]); D12_Write(D12_DATA, sString3[LOAD_INDEX]); } D12_Write(D12_COMMAND, VALIDATE_BUFFER); // Mark the buffer as ready to go! // 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("DS "); break; default: DEBUG2("!DS(%x)", WHICH_DESCRIPTOR); 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("1I "); D12_Write(D12_COMMAND, READ_ENDPT_STATUS + ENDPT1_IN); // Read last transaction status, for endpoint 1 IN D12_Read(buffer, 1); // Clears the IN interrupt DEBUG2("LT=%x ", buffer[0]); } // 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); // Turn on the yellow LED set_tris_b(0x01); //PIN_B1 (D12's INT) is input, the rest are output. set_tris_d(0x00); //All output port_d = 0xFF; //Set bus high, useful for checking the ribbon has not come loose output_high(D12_RD_N); output_high(D12_WR_N); output_low(D12_A0); // Indicates bus is for data output_low(D12_SUSPEND); // Prevent D12 from going into suspend disable_interrupts(GLOBAL); // Stop interrupts from interrupting us while we setup ;) ext_int_edge(H_TO_L); // Set up when to trigger enable_interrupts(INT_EXT); // Enable external interrupts (connected to the D12's INT_N) clear_interrupt(INT_EXT); // Remove pending interrupts enable_interrupts(GLOBAL); // Enable all interrupts DEBUG_LEVEL = port_a & 0x07;// Read DIP switches (3 lower digits only) DEBUG7("\r\n\r\n%d ", DEBUG_LEVEL); } //-------------------------------------------------------- // 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("_SAE "); D12_Write(D12_COMMAND, SET_ADDRESS); D12_Write(D12_DATA, 0x00 | 0x80); DEBUG0("_SEE "); D12_Write(D12_COMMAND, SET_ENDPT_ENABLE); D12_Write(D12_DATA, 0x01); DEBUG0("_SM "); D12_Write(D12_COMMAND, SET_MODE); D12_Write(D12_DATA, 0x1E); // Non-ISO, Softconnect, Interrupt for all, Clock running, no Lazyclock D12_Write(D12_DATA, 0x0B); // Clock 4MHz, Set-to-one isn't, no SOF interrupts } //-------------------------------------------------------- // 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, READ_ENDPT_STATUS + CTRL_OUT); D12_Read(buffer, 1); DEBUG3("LT=%x ", buffer[0]); if(buffer[0] & STAT_NOT_READ){ // Previous status not read // Nothing yet ;) } if(buffer[0] & STAT_XFER_SUCCESS){ if(buffer[0] & STAT_SETUP){ // Setup token D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_OUT); D12_Read(data, 1); DEBUG2("SE=%x ", data[0]); D12_Write(D12_COMMAND, READ_BUFFER); D12_Read(data, D12_CTRL_BUFFER_SIZE); DEBUG2("DL=%x ", data[1]); // Note that [0] is reserved, so [1] contains the data length // Acknowledge that we like this (NB CTRL_OUT is already selected) D12_Write(D12_COMMAND, ACK_SETUP); D12_Write(D12_COMMAND, CLEAR_BUFFER); // Prevent previous data from being sent (need to ack_setup to re-enable clear buffer) D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_IN); D12_Write(D12_COMMAND, ACK_SETUP); D12_Write(D12_COMMAND, CLEAR_BUFFER); if(data[1] == 0x08){ // Valid setup token is 8 bytes for(i=2; i<10; i++){ DEBUG0("%x ", data[i]); } pReq = (struct REQUEST *) &data[2]; // [0] is reserved, [1] is data length, so [2] is actual data // Output some debugging info Debug_D12_Request(pReq); switch((pReq->bmRequestType & REQTYPE_CMD_TYPE) >> 5){ // Standard request case 0x00: DEBUG2("SREQ "); D12_Standard_Request(pReq); break; // Class request case 0x01: DEBUG2("CREQ "); break; // Endpoint request case 0x02: DEBUG2("EREQ "); break; // Unsupported default: DEBUG4("\x07"); // Bell character (^G) DEBUG2("?REQ=%x ", (pReq->bmRequestType & REQTYPE_CMD_TYPE) >> 5); // 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) // Last transaction wasn't successful { 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("S_"); switch(ENDPT){ case 0: DEBUG5("CO "); break; case 1: DEBUG5("CI "); break; case 2: DEBUG5("EO "); break; case 3: DEBUG5("EI "); break; case 4: DEBUG5("MO "); break; case 5: DEBUG5("MI "); break; default: DEBUG5("?(%x) ", ENDPT); break; } D12_Write(D12_COMMAND, SET_ENDPT_STATUS + ENDPT); D12_Write(D12_DATA, 0x01); } //-------------------------------------------------------- // Handle standard USB requests, such as those encountered in // SETUP tokens void D12_Standard_Request(struct REQUEST *pReq) { switch(pReq->bRequest){ case GET_STATUS_REQ: DEBUG5("Get_Staus "); break; case CLEAR_FEATURE_REQ: DEBUG5("Clear_Feature "); break; case SET_FEATURE_REQ: DEBUG5("Set_feature "); break; case SET_ADDRESS_REQ: DEBUG5("Set_Address "); D12_Set_Address(pReq); break; case GET_DESCRIPTOR_REQ: DEBUG5("Get_Descriptor "); D12_Get_Descriptor(pReq); break; case SET_DESCRIPTOR_REQ: DEBUG5("Set_Descriptor "); break; case GET_CONFIGURATION_REQ: DEBUG5("Get_Configuration "); break; case SET_CONFIGURATION_REQ: DEBUG5("Set_Configuration "); break; case GET_INTERFACE_REQ: DEBUG5("Get_Interface "); break; case SET_INTERFACE_REQ: DEBUG5("Set_Interface "); break; case SYNCH_FRAME_REQ: DEBUG5("Synch_Frame "); break; default: DEBUG7("\x07"); // Bell character (^G) DEBUG5("?SREQ=%x ", pReq->bRequest); break; } } //-------------------------------------------------------- // Service the host's request for a descriptor void D12_Get_Descriptor(struct REQUEST *pReq){ unsigned int16 ReqDataLen; short supported = 1; ReqDataLen = pReq->wLength; switch (pReq->wValue >> 8){ // We only care about the high byte // Device Descriptor case 0x0001: DEBUG3("DDR "); // 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("CDR "); // 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("SDR=%x ", pReq->wValue & 0x0F); // Now we need to check which string was requested (low byte) switch(pReq->wValue & 0x0F){ // 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("\x07"); // Bell character (^G) DEBUG4("?SDR "); supported = 0; break; } break; // Unsupported default: supported = 0; DEBUG4("\x07"); // Bell character (^G) DEBUG5("?DR=%x ",(pReq->wValue >> 8)); // Not sure which endpoint would need to be stalled.. // presumably the one the request came from (CTRL_OUT) D12_Stall_Endpt(CTRL_OUT); break; } if(supported){ // Note that if our descriptor is larger then what the host requested, // we only send what we can, it's up to the host to make another request, // with a larger data phase (hence we will start again from the beginning) // However, if our descriptor is larger than the D12's CTRL_OUT buffer, then // we need to send multiple packets, filling the buffer on each CTRL_IN interrupt LOAD_INDEX = 0; // Make sure we start reading our descriptor array from the beginning LOAD_LENGTH = ReqDataLen; } } //-------------------------------------------------------- // 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("!LT=%x ", error); 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("\x07"); // Bell character (^G) DEBUG5("?LT=%x ", error); break; } } //-------------------------------------------------------- // Output debugging info about a USB request #SEPARATE void Debug_D12_Request(struct REQUEST *pReq){ DEBUG4("DIR="); if(pReq->bmRequestType & REQTYPE_XFER_DIRECTION){ DEBUG4("I "); } else { DEBUG4("O "); } DEBUG4("TO="); switch(pReq->bmRequestType & REQTYPE_RECIPIENT){ // Device case 0x00: DEBUG4("D "); break; // Interface case 0x01: DEBUG4("I "); break; // Endpoint case 0x02: DEBUG4("E "); break; // Other case 0x03: DEBUG4("? "); break; // Unsupported default: DEBUG7("\x07"); // Bell character (^G) // Stall this endpoint (indicating we cannot handle the request) D12_Stall_Endpt(CTRL_OUT); break; } DEBUG4("wV=%Lx ", pReq->wValue); DEBUG4("wI=%Lx ", pReq->wIndex); DEBUG4("wL=%Lx ", pReq->wLength); } //-------------------------------------------------------- // 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->wValue | 0x80); DEBUG3("SAEP "); } //-------------------------------------------------------- // Send a zero-length packet to the selected endpoint // Useful for empty data stages in setup transactions, as well // 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, SELECT_ENDPT + ENDPT); D12_Write(D12_COMMAND, WRITE_BUFFER); D12_Write(D12_DATA, 0); // First packet is reserved D12_Write(D12_DATA, 0); // Data length (zero-length packet) D12_Write(D12_COMMAND, VALIDATE_BUFFER); DEBUG3("Z "); }