User Tools

Site Tools


project:usb:code:enumerated2

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

project:usb:code:enumerated2 [2007/04/27 00:21] (current)
Line 1: Line 1:
 +====== Enumerated2 ======
  
 +Like [[project:​usb:​code:​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.
 +
 +{{project:​usb:​code:​enumerated2.hex|Download compiled HEX firmware}}
 +
 +<code cpp>
 +#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 ");
 +}
 +</​code>​
project/usb/code/enumerated2.txt · Last modified: 2007/04/27 00:21 (external edit)