User Tools

Site Tools


project:usb:code:enumerated

Enumerated

Here is the code as it stood when I first got the firmware to enumerate as an HID Device on Windows XP Pro SP2 without errors. Note that this code only seems to work at debugging level 5, or when debugging is disabled (via commenting #define __DEBUGGING_ENABLED). Not really sure why it doesn't work at debugging levels 6 and 7, but it doesn't :-/

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++; if(DEBUG_LEVEL <= 0)printf
    #define DEBUG1  if(DEBUG_LEVEL <= 1)DEBUGGED++; if(DEBUG_LEVEL <= 1)printf
    #define DEBUG2  if(DEBUG_LEVEL <= 2)DEBUGGED++; if(DEBUG_LEVEL <= 2)printf
    #define DEBUG3  if(DEBUG_LEVEL <= 3)DEBUGGED++; if(DEBUG_LEVEL <= 3)printf
    #define DEBUG4  if(DEBUG_LEVEL <= 4)DEBUGGED++; if(DEBUG_LEVEL <= 4)printf
    #define DEBUG5  if(DEBUG_LEVEL <= 5)DEBUGGED++; if(DEBUG_LEVEL <= 5)printf
    #define DEBUG6  if(DEBUG_LEVEL <= 6)DEBUGGED++; if(DEBUG_LEVEL <= 6)printf
    #define DEBUG7  if(DEBUG_LEVEL <= 7)DEBUGGED++; 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
 
//--------------------------------------------------------
// Global Variable Declarations
unsigned char DEBUG_LEVEL;
unsigned char *pLOAD;       // Used for transmitting descriptors etc
unsigned char *pLOAD_TERMINATOR;        // Marks the last byte to be loaded
unsigned char SET_ADDRESS_PENDING;
unsigned int8 DEBUGGED;         // Flags if a DEBUGx statement was executed
unsigned char SEND_NULL_PACKET; // Should we send a zero-length payload?
 
//--------------------------------------------------------
// Structures
struct REQUEST {
    int8    bmRequestType;
    int8    bRequest;
    int16   wValue;
    int16   wIndex;
    int16   wLength;    // Data Phase's data length
};
 
struct DEVICE {
    int8    bLength;
    int8    bDescriptorType;
    int16   bcdUSB;
    int8    bDeviceClass;
    int8    bDeviceSubClass;
    int8    bDeviceProtocol;
    int8    bMaxPacketSize;
    int16   idVendor;
    int16   idProduct;
    int16   bcdDevice;
    int8    iManufacturer;
    int8    iProduct;
    int8    iSerialNumber;
    int8    bNumConfigurations;
};
 
struct CONFIGURATION {
    int8    bLength;
    int8    bDescriptorType;
    int8    wTotalLength;
    int8    wZero;
    int8    bNumInterfaces;
    int8    bConfigurationValue;
    int8    iConfiguration;
    int8    bmAttributes;
    int8    MaxPower;
};
 
struct INTERFACE {
    int8    bLength;
    int8    bDescriptionType;
    int8    bInterfaceNumber;
    int8    bAlternateSetting;
    int8    bNumEndpoints;
    int8    bInterfaceClass;
    int8    bInterfaceSubClass;
    int8    bInterfaceProtocol;
    int8    iInterface;
};
 
struct HIDDESC {
    int8    bLength;
    int8    bDescriptorType;
    int16   bcdHID;
    int8    bCountryCode;
    int8    bNumDescriptors;
    int8    bReportDescriptorType;
    int16   wItemLength;
};
 
struct ENDPOINT {
    int8    bLength;
    int8    bDescriptorType;
    int8    bEndpointAddress;
    int8    bmAttributes;
    int16   wMaxPacketSize;
    int8    bInterval;
};
 
struct CONFIG {
    struct CONFIGURATION sConfig;
    struct INTERFACE sInterface;
    struct HIDDESC sHIDDesc;
    struct ENDPOINT sEndpoint1;
};
 
 
 
//--------------------------------------------------------
// USB Descriptors
/*
struct DEVICE sDevice = {
    sizeof( struct DEVICE), //BYTE bLength
    0x01,                   //BYTE bDescriptorType
    0x0110,                 //WORD bcdUSB version supported
    0x00,                   //BYTE bDeviceClass
    0x00,                   //BYTE bDeviceSubClass
    0x00,                   //BYTE bDeviceProtocol
    D12_CTRL_BUFFER_SIZE,   //BYTE bMaxPacketSize (probably 16)
    0x05C7,                 //WORD idVendor
    0x0113,                 //WORD idProduct, For Philips Hub mouse
    0x0001,                 //WORD bcdDevice
    0x01,                   //BYTE iManufacturer
    0x02,                   //BYTE iProduct
    0x01,                   //BYTE iSerialNumber
    0x01                    //BYTE bNumConfigurations
};
*/
 
struct DEVICE sDevice = {
    sizeof( struct DEVICE), //BYTE bLength
    0x01,                   //BYTE bDescriptorType
    0x0110,                 //WORD bcdUSB version supported
    0xff,                   //BYTE bDeviceClass
    0xff,                   //BYTE bDeviceSubClass
    0xff,                   //BYTE bDeviceProtocol
    D12_CTRL_BUFFER_SIZE,   //BYTE bMaxPacketSize (probably 16)
    0x7104,                 //WORD idVendor
    0x0211,                 //WORD idProduct, For Philips Hub mouse
    0x0000,                 //WORD bcdDevice
    0x01,                   //BYTE iManufacturer
    0x02,                   //BYTE iProduct
    0x00,                   //BYTE iSerialNumber
    0x01                    //BYTE bNumConfigurations
};
struct CONFIG sConfiguration = {
    sizeof(struct CONFIGURATION),   //BYTE bLength
    0x02,                           //BYTE bDescriptorType //Assigned by USB
    sizeof( struct CONFIG),         //BYTE wTotalLength
    0x00,                           //BYTE wZero, always 00 (Actually, wTotalLength is a word, so that's wrong..)
    0x01,                           //BYTE bNumInterfaces
    0x01,                           //BYTE bConfigurationValue
    0x00,                           //BYTE iConfiguration
    0xa0,                           //BYTE bmAttributes, Bus powered and remote wakeup
    0x05,                           //BYTE MaxPower
    sizeof(struct INTERFACE),       //BYTE bLength
    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
    sizeof(struct HIDDESC),         //BYTE bLength;
    0x21,                           //BYTE bDescriptorType;
    0x0001,                         //WORD bcdHID;
    0x00,                           //BYTE bCountryCode;
    0x01,                           //BYTE bNumDescriptors;
    0x22,                           //BYTE bReportDescriptorType;
    0x3200,                         //WORD wItemLength;
    sizeof(struct ENDPOINT),        //BYTE bLength
    0x05,                           //BYTE bDescriptorType, assigned by USB
    0x81,                           //BYTE bEndpointAddress, IN endpoint, endpoint 1
    0x03,                           //BYTE bmAttributes, Interrupt endpoint
    0x0800,                         //WORD wMaxPacketSize
    0x0A,                           //Polling Time
}; //Interval
 
unsigned char LANG_ID[4] = {
sizeof(LANG_ID),    // bLength
0x03,               // bDescriptorType = String Desc
0x09,               // wLangID (Lo) (Lang ID for English = 0x0409)
0x04,               // wLangID (Hi) (Lang ID for English = 0x0409)
};
 
//--------------------------------------------------------
// 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))
        D12_Interrupt_Handler();    // Kick-start
    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) {
            DEBUG2("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 = 0;
                DEBUG2("AS ");
            } else if (pLOAD_TERMINATOR - pLOAD > 0) {
                unsigned char DataLen;
 
                // Smaller of "length of data to send" and "control buffer size"
                DataLen = ((pLOAD_TERMINATOR - pLOAD) > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : (pLOAD_TERMINATOR - pLOAD);
                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 += pLOAD;           // Avoid recalulating this every iteration
                for(; pLOAD<DataLen; pLOAD++)
                {
                    D12_Write(D12_DATA, *pLOAD);
                }
                D12_Write(D12_COMMAND, VALIDATE_BUFFER);    // Mark the buffer as ready to go!
 
                DEBUG4("DS ");
            } else if (SEND_NULL_PACKET) {
                D12_Send_Null_Packet(CTRL_IN);
            } 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;
    pLOAD = pLOAD_TERMINATOR = 0;
    SEND_NULL_PACKET = 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);
    DEBUG2("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++){
                    DEBUG1("%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)
{
    DEBUG4("S_%d ", ENDPT);
    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(struct DEVICE) ){
                ReqDataLen = sizeof(struct DEVICE);
            }
            pLOAD = (unsigned char *) &sDevice; // Mark the Device descriptor for loading
        break;
 
        // Configuration Descriptor
        case 0x0002:
            DEBUG3("CDR ");
            // Don't transmit more than we need to
            if ( ReqDataLen > (unsigned int16) sizeof(struct CONFIG) ){
                ReqDataLen = sizeof(struct CONFIG);
            }
            pLOAD = (unsigned char *) &sConfiguration;  // Mark the Configuration descriptor for loading
        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:
                    pLOAD = &LANG_ID[0];    // LAND_ID is an array
                break;
 
                // Unknown string descriptor
                default:
                    DEBUG4("\x07");     // Bell character (^G)
                    DEBUG4("?SDR ");
                    supported = 0;
                break;
            }
 
            // Don't transmit more than we need to
            if ( ReqDataLen > (unsigned int16) sizeof(*pLOAD) ){    // All string descriptors are kept in arrays
                ReqDataLen = sizeof(*pLOAD);
            }
        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
       pLOAD_TERMINATOR = pLOAD + 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);
 
    DEBUG2("Z ");
}
project/usb/code/enumerated.txt · Last modified: 2007/04/27 01:21 (external edit)