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)