This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

USB CDC enumeration problem with XP

Hello,

I'm using a MAX3420E in order to comm a PCB with Atmel microcontroller, with a PC using USB. In windows vista, the PC recognizes a Serial (Virtual) Port and works right, but when I try it in Windows XP, it seems that PC doesn't recognizes the device.

It appears in the device manager as "unknown device" with VID_0000&PID_0000...

I've been searching for a similar post, and found one that had the same problem VID_0000, but the last message does't give any solution...

Any clue? Thank you very much in advance.

The descriptors I use are list below:

// DESCRIPTORS
const unsigned char DD[]=
// DEVICE Descriptor
{0x12, // bLength = 18d
0x01, // bDescriptorType = Device (1)
0x10,0x01, // bcdUSB(L/H) USB spec rev (BCD)
0x02,0x00,0x00, // bDeviceClass, bDeviceSubClass, bDeviceProtocol (descrito en Interface Descriptor)
0x40, // bMaxPacketSize0 EP0 is 64 bytes
0x6A,0x0B, // idVendor(L/H)--Maxim is 0B6A (2922 en decimal)
0x46,0x53, // idProduct(L/H)--5346 (21318 en decimal)
0x00,0x01, // bcdDevice--1234
1,2,3, // iManufacturer, iProduct, iSerialNumber
1}; // bNumConfigurations

const unsigned char CD[]=
// CONFIGURATION Descriptor
{0x09, // bLength
0x02, // bDescriptorType = Config
67,0x00, // wTotalLength(L/H) = Nº Bytes (67[0x43}) (Sin cabeceras 48[0x30]) 0x4300
0x02, // bNumInterfaces = 2
0x01, // bConfigValue
0x00, // iConfiguration
0xA0, // bmAttributes. b7=1 b6=NO self-powered b5=RWU supported (1 1 1 0 0 0 0 0)
0xFA, // MaxPower is 200 ma

// INTERFACE Descriptor
0x09, // length = 9
0x04, // type = IF
0x00, // InterFace Number = 0
0x00, // bAlternate Setting
0x01, // bNum Endpoints = 1 IN
0x02, // bInterfaceClass = 2 Communication
0x02, // bInterfaceSubClass = 2
0x01, // bInterfaceProtocol =1 (SubClass ACM=Abstract Control Mode, InterfaceProtocol=V.25ter, common AT commands)
0x00, // iInterface

// Header Functional Descriptor (marks beginning of the concatenated set of Functional Descriptors)
0x05, // bFunctionLength, Descriptor size in bytes --[18] --[48]
0x24, // bDescriptorType, CS_INTERFACE
0x00, // bDescriptorSubtype, Header Functional Descriptor
0x10,0x01, // bcdCDC, CDC specification release number in BCD format ([0x10, 0x01])

// Call Management Functional Descriptor
0x05, // bFunctionLength, Descriptor size in bytes
0x24, // bDescriptorType, CS_INTERFACE
0x01, // bDescriptorSubtype, Call Management Functional Descriptor
0x03, // bmCapabilities, Device doesn't call management itself (0->3)
0x01, // bDataInterface, Interface used for call management

// Abstract Control Management Functional Descriptor
0x04, // bDescriptorLength, Descriptor size in bytes
0x24, // bDescriptorType, CS_INTERFACE
0x02, // bDescriptorSubtype, Abstract Control Management Functional Descriptor
0x06, // bmCapabilities, Support for the GET/SET_LINE_CODING, BREAK & SET_CONTROL_LINE_STATE

// Union Functional Descriptor
0x05, // bFunctionLength, Descriptor size in bytes
0x24, // bDescriptorType, CS_INTERFACE
0x06, // bDescriptorSubtype, Union Functional Descriptor
0x00, // bMasterInterface, The controlling interface for the union (bInterfaceNumber of a Communication or Data Class interface in this configuration)
0x01, // bSlaveInterface0, The controlled interace in the union (bInterfaceNumber of an interface in this configuration)

Functional Descriptor*****************
// Endpoint Descriptor EP3-IN
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x83, // bEndpointAddress (EP3 IN)
0x03, // bmAttributes (interrupt = 3)
0x40,0x00, // wMaxPacketSize (64[0x0040])
0x02, // bInterval, Maximum latency (0x02)

// INTERFACE Descriptor
0x09, // length = 9 --[44]
0x04, // type = IF
0x01, // InterFace Number = 1
0x00, // bAlternate Setting
0x02, // bNum Endpoints = 2 (IN&OUT)
0x0A, // bInterfaceClass = A (Data)
0x00,0x00, // bInterfaceSubClass, bInterfaceProtocol (SubClass ACM=Abstract Control Mode, InterfaceProtocol=V.25ter, common AT commands)
0x00, // iInterface

// Endpoint Descriptor EP1-OUT
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x01, // bEndpointAddress (EP1-OUT)
0x02, // bmAttributes (bulk = 2)
0x40,0x00, // wMaxPacketSize (64[0x40])
0x00, // bInterval (0x00)

// Endpoint Descriptor EP2-IN
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x82, // bEndpointAddress (EP2-IN)
0x02, // bmAttributes (bulk = 2)
0x40,0x00, // wMaxPacketSize (64[0x40])
0x00, // bInterval (0x00)
};

// STRING descriptors. An array of string arrays

const unsigned char strDesc[][64]= {
// STRING descriptor 0--Language string
{ 0x04, // bLength
0x03, // bDescriptorType = string
0x09,0x04 // wLANGID(L/H) = English-United Sates
},
// STRING descriptor 1--Manufacturer ID
{ 34, // bLength (0x2C para 23 (incluyendo tamaño y tipo??)
0x03, // bDescriptorType = string
'x',0,
'x',0,
'x',0,
'x',0,
' ',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,// text in Unicode
},
// STRING descriptor 2 - Product ID
{ 8, // bLength
0x03, // bDescriptorType = string
'X',0,
'X',0,
'X',0,
},

// STRING descriptor 3 - Serial Number ID
{ 8, // bLength
0x03, // bDescriptorType = string
'X',0,
'X',0,
'X',0,
}};

const unsigned char SerialConf[]=
{ 0x00,0xE8,0x03,0x00, // dwDTFRate. Baudrate (9600hex->38400 baud)
0, // bCharFormat. 1 Stop bit
0, // bParityType: none
8, // Number of data bits: 8
};

Parents
  • I add thesecond part...
    First...I should have commented you that the program runs over API based on tinyOS (meshnetics), and the main bucle (my aplication) is called every Xms, I say 'X', because I can configures it with '0 ms' to make it runs 'as fast as possible'...but don't know the exact time.
    About the debug outputs, I just copy the variable's values meanwhile I debug.

    void send_descriptor(void)
    {
            WORD sendlen,desclen;
            BYTE *pDdata=NULL;                                      // pointer to ROM Descriptor data to send
            bool send_ZLP=FALSE;
            desclen = 0; // check for zero as error condition (no statements satisfied)
            switch (SUD[wValueH])                           // wValueH is descriptor type
            {
            case  GD_DEVICE:
                  desclen = DD[0];  // descriptor length
                  pDdata = (BYTE *) DD;
                  break;
            case  GD_CONFIGURATION:
                  desclen = CD[2];  // Config descriptor includes interface, header and ep descriptors (67 Bytes [0x0043])
                              pDdata = (BYTE *) CD;
                  break;
            case  GD_STRING:
                  desclen = strDesc[SUD[wValueL]][0];   // wValueL=string index, array[0] is the length
                  pDdata = (BYTE *) strDesc[SUD[wValueL]];       // point to first array element
                  break;
            case CS_INTERFACE:
                             if(SUD[wIndexL]==0)    // Interface Number=0. Del EndPoint 2 (interrupción), Communication
                             {
                                    desclen = CD[9];
                                    pDdata = (BYTE *) &CD[9];
                             }
                             else if (SUD[wIndexL]==1)      // Interface Number=1. Del EndPoint 1 (bulk), Datos
                             {
                                    desclen = CD[44];
                                    pDdata = (BYTE *) &CD[44];
                             }
                                    break;
            }       // end switch on descriptor type
    //
            if (desclen!=0)                          // one of the case statements above filled in a value
            {
                    sendlen = desclen;                      // Send Lower data
                    if((desclen % 64) == 0) send_ZLP=TRUE;  // Flag to send ZLP
                    while(desclen>0)
                    {
                            if(desclen>64) sendlen=64;   // if packet>64 bytes
                            else sendlen=desclen;           // if packet<=64 bytes
                            while(!(rreg(rEPIRQ) & bmIN0BAVIRQ));// While out buff. isn't free
                            writebytes(rEP0FIFO,sendlen,pDdata);// Write data in FIFO (EP_CONTROL)
                            pDdata=(pDdata+sendlen);
                            if(desclen<64) wregAS(rEP0BC,sendlen);       // load EP0BC to arm the EP0-IN transfer & ACKSTAT
                            else wreg(rEP0BC,sendlen);      // load EP0BC to arm the EP0-IN transfer & ACKSTAT
                            desclen-=sendlen;                       // Resta los datos ya enviados
                    }
                    if(send_ZLP)
                            wregAS(rEP0BC,0);                       // load EP0BC to arm the EP0-IN transfer & ACKSTAT
            }
            else STALL_EP0;  // none of the descriptor types match
    }
    
    
    void class_request(void)
    {
            switch (SUD[bRequest])
            {
            case SEND_ENCAPSULATED_COMMAND: wregAS(rEP0BC, 0);      break; // send_encapsulated_command();  break;
            case GET_ENCAPSULATED_RESPONSE: wregAS(rEP0BC, 0);      break; // send_encapsulated_command();  break; // get_encapsulated_response();  break;
    
            case SET_COMM_FEATURE:  break;
            case GET_COMM_FEATURE:  break;
            case CLEAR_COMM_FEATURE:break;
            case SET_LINE_CONDING:
            {
                    wregAS(rEP0BC, 0);                              // Envía un paquete de longitud 0 para la etapa de 'STATUS' (?)
                    if(SUD[wLengthL]>0)
                    {
                            while(!(rreg(rEPIRQ) & bmOUT0DAVIRQ) && SUD[7]<=5) SUD[7]++;
            readbytes(rEP0FIFO,SUD[wLengthL],(uint8_t *)&SUD[0]);               // Lee la configuración serie por el EP0 (hay que cambiar algo para que lea?)
                    }
            } break;
            case GET_LINE_CODING:
            {
                    wregAS(rEP0BC, 0);                              // Envía un paquete de longitud 0 para la etapa de 'STATUS' (?)
    writebytes(rEP0FIFO,SUD[wLengthL],(uint8_t *)&SerialConf[0]);
                    wreg(rEP0BC, SUD[wLengthL]);
            } break;
            case SET_CONTROL_LINE_STATE:
            {
                    if(configval)
                    {
                            USB_init=1;                                     // Si llega a este punto, el dispositivo ha terminado de inicializarse
                            if((SUD[wValueL]&0x01)==0x01)// PC listo para aceptar tramas del AZC
                                    PC_DTR=1;
                            if(SUD[wValueL]==0x00)          // Se ha cortado la comunicación con el PC
                                    PC_DTR=0;
                    }
                    wregAS(rEP3INBC, 0);
            } break;
            case SEND_BREAK:        break;
            default: STALL_EP0;                                     // none of the descriptor types match
            }
    }
    
    
    void wreg(BYTE reg, BYTE dat)
    {
            uint8_t data[2];
            USB_CSE;
                    data[0]=reg+2;
                    data[1]=dat;
                    spi_readWrite(data, 2);
            USB_CSD;
    }
    
    
    // Write a MAX3410E register with the "ACK STATUS" bit set in the command byte
    void wregAS(BYTE reg, BYTE dat)
    {
            uint8_t data[2];
            USB_CSE;
                    data[0]=reg+3;
                    data[1]=dat;
                    spi_readWrite(data, 2);
            USB_CSD;
    }
    
    
    // Read a register, return its value.
    uint8_t rreg(BYTE reg)
    {
            uint8_t dat[2];
            dat[0]=reg;
            USB_CSE;
                    spi_readWrite(&dat[0], 2);
            USB_CSD;
            return(dat[1]);
    }
    
    // Read a byte (as rreg), but also set the AckStat bit in the command byte.
    BYTE rregAS(BYTE reg)
    {
            uint8_t dat[2];
            dat[0]=reg+1;;
            USB_CSE;
                    spi_readWrite(&dat[0], 2);
            USB_CSD;
            return(dat[1]);
    }
    
    
    void readbytes(BYTE reg, BYTE N, uint8_t *p)
    {
    //      uint8_t ind_read=0;
            p[0]=reg;
            USB_CSE;
                    spi_readWrite(&p[0], N+1);
            USB_CSD
            *(uint64_t*)p=*(uint64_t*)(p+1);
    //      for(ind_read=0;ind_read<N;ind_read++)
            //      p[ind_read] = rreg(reg);
    }
    
    
    void writebytes(BYTE reg, BYTE N, uint8_t *p)
    {
            uint8_t ind_write=0;
            for(ind_write=0;ind_write<N;ind_write++)
                    wreg(reg, p[ind_write]);
    }
    

Reply
  • I add thesecond part...
    First...I should have commented you that the program runs over API based on tinyOS (meshnetics), and the main bucle (my aplication) is called every Xms, I say 'X', because I can configures it with '0 ms' to make it runs 'as fast as possible'...but don't know the exact time.
    About the debug outputs, I just copy the variable's values meanwhile I debug.

    void send_descriptor(void)
    {
            WORD sendlen,desclen;
            BYTE *pDdata=NULL;                                      // pointer to ROM Descriptor data to send
            bool send_ZLP=FALSE;
            desclen = 0; // check for zero as error condition (no statements satisfied)
            switch (SUD[wValueH])                           // wValueH is descriptor type
            {
            case  GD_DEVICE:
                  desclen = DD[0];  // descriptor length
                  pDdata = (BYTE *) DD;
                  break;
            case  GD_CONFIGURATION:
                  desclen = CD[2];  // Config descriptor includes interface, header and ep descriptors (67 Bytes [0x0043])
                              pDdata = (BYTE *) CD;
                  break;
            case  GD_STRING:
                  desclen = strDesc[SUD[wValueL]][0];   // wValueL=string index, array[0] is the length
                  pDdata = (BYTE *) strDesc[SUD[wValueL]];       // point to first array element
                  break;
            case CS_INTERFACE:
                             if(SUD[wIndexL]==0)    // Interface Number=0. Del EndPoint 2 (interrupción), Communication
                             {
                                    desclen = CD[9];
                                    pDdata = (BYTE *) &CD[9];
                             }
                             else if (SUD[wIndexL]==1)      // Interface Number=1. Del EndPoint 1 (bulk), Datos
                             {
                                    desclen = CD[44];
                                    pDdata = (BYTE *) &CD[44];
                             }
                                    break;
            }       // end switch on descriptor type
    //
            if (desclen!=0)                          // one of the case statements above filled in a value
            {
                    sendlen = desclen;                      // Send Lower data
                    if((desclen % 64) == 0) send_ZLP=TRUE;  // Flag to send ZLP
                    while(desclen>0)
                    {
                            if(desclen>64) sendlen=64;   // if packet>64 bytes
                            else sendlen=desclen;           // if packet<=64 bytes
                            while(!(rreg(rEPIRQ) & bmIN0BAVIRQ));// While out buff. isn't free
                            writebytes(rEP0FIFO,sendlen,pDdata);// Write data in FIFO (EP_CONTROL)
                            pDdata=(pDdata+sendlen);
                            if(desclen<64) wregAS(rEP0BC,sendlen);       // load EP0BC to arm the EP0-IN transfer & ACKSTAT
                            else wreg(rEP0BC,sendlen);      // load EP0BC to arm the EP0-IN transfer & ACKSTAT
                            desclen-=sendlen;                       // Resta los datos ya enviados
                    }
                    if(send_ZLP)
                            wregAS(rEP0BC,0);                       // load EP0BC to arm the EP0-IN transfer & ACKSTAT
            }
            else STALL_EP0;  // none of the descriptor types match
    }
    
    
    void class_request(void)
    {
            switch (SUD[bRequest])
            {
            case SEND_ENCAPSULATED_COMMAND: wregAS(rEP0BC, 0);      break; // send_encapsulated_command();  break;
            case GET_ENCAPSULATED_RESPONSE: wregAS(rEP0BC, 0);      break; // send_encapsulated_command();  break; // get_encapsulated_response();  break;
    
            case SET_COMM_FEATURE:  break;
            case GET_COMM_FEATURE:  break;
            case CLEAR_COMM_FEATURE:break;
            case SET_LINE_CONDING:
            {
                    wregAS(rEP0BC, 0);                              // Envía un paquete de longitud 0 para la etapa de 'STATUS' (?)
                    if(SUD[wLengthL]>0)
                    {
                            while(!(rreg(rEPIRQ) & bmOUT0DAVIRQ) && SUD[7]<=5) SUD[7]++;
            readbytes(rEP0FIFO,SUD[wLengthL],(uint8_t *)&SUD[0]);               // Lee la configuración serie por el EP0 (hay que cambiar algo para que lea?)
                    }
            } break;
            case GET_LINE_CODING:
            {
                    wregAS(rEP0BC, 0);                              // Envía un paquete de longitud 0 para la etapa de 'STATUS' (?)
    writebytes(rEP0FIFO,SUD[wLengthL],(uint8_t *)&SerialConf[0]);
                    wreg(rEP0BC, SUD[wLengthL]);
            } break;
            case SET_CONTROL_LINE_STATE:
            {
                    if(configval)
                    {
                            USB_init=1;                                     // Si llega a este punto, el dispositivo ha terminado de inicializarse
                            if((SUD[wValueL]&0x01)==0x01)// PC listo para aceptar tramas del AZC
                                    PC_DTR=1;
                            if(SUD[wValueL]==0x00)          // Se ha cortado la comunicación con el PC
                                    PC_DTR=0;
                    }
                    wregAS(rEP3INBC, 0);
            } break;
            case SEND_BREAK:        break;
            default: STALL_EP0;                                     // none of the descriptor types match
            }
    }
    
    
    void wreg(BYTE reg, BYTE dat)
    {
            uint8_t data[2];
            USB_CSE;
                    data[0]=reg+2;
                    data[1]=dat;
                    spi_readWrite(data, 2);
            USB_CSD;
    }
    
    
    // Write a MAX3410E register with the "ACK STATUS" bit set in the command byte
    void wregAS(BYTE reg, BYTE dat)
    {
            uint8_t data[2];
            USB_CSE;
                    data[0]=reg+3;
                    data[1]=dat;
                    spi_readWrite(data, 2);
            USB_CSD;
    }
    
    
    // Read a register, return its value.
    uint8_t rreg(BYTE reg)
    {
            uint8_t dat[2];
            dat[0]=reg;
            USB_CSE;
                    spi_readWrite(&dat[0], 2);
            USB_CSD;
            return(dat[1]);
    }
    
    // Read a byte (as rreg), but also set the AckStat bit in the command byte.
    BYTE rregAS(BYTE reg)
    {
            uint8_t dat[2];
            dat[0]=reg+1;;
            USB_CSE;
                    spi_readWrite(&dat[0], 2);
            USB_CSD;
            return(dat[1]);
    }
    
    
    void readbytes(BYTE reg, BYTE N, uint8_t *p)
    {
    //      uint8_t ind_read=0;
            p[0]=reg;
            USB_CSE;
                    spi_readWrite(&p[0], N+1);
            USB_CSD
            *(uint64_t*)p=*(uint64_t*)(p+1);
    //      for(ind_read=0;ind_read<N;ind_read++)
            //      p[ind_read] = rreg(reg);
    }
    
    
    void writebytes(BYTE reg, BYTE N, uint8_t *p)
    {
            uint8_t ind_write=0;
            for(ind_write=0;ind_write<N;ind_write++)
                    wreg(reg, p[ind_write]);
    }
    

Children
  • "About the debug outputs, I just copy the variable's values meanwhile I debug."

    I see.

    As of your above code,
    - Bus reset handler is fine. No problem.
    - Unfortunately, you didn't include the super loop which calls service_irqs()
    The problem lies in the way how service_irqs() is called.

    "I should have commented you that the program runs over API based on tinyOS (meshnetics),
    and the main bucle (my aplication) is called every Xms, I say 'X', because I can configures
    it with '0 ms' to make it runs 'as fast as possible'...but don't know the exact time."

    I suppose service_irqs() is called from your "main bucle" using polling, like Maxim example.
    Is your tinyOS support interrupt?
    Then, move service_irqs() call to external INT pin ISR, which is connected to MAX3420E INT pin.

    Tsuneo