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 Enumearation problem with Intel host PC

I wonder that I could post a question I faced.

Hi, all.

I implemented USB CDC spec on the pxa270 reference board. I correctly send descriptors as Host requested and It works fine in AMD CPU hosted. but other Host which is Intel based can not recognize Descriptors.

I traced routine by inserting printf() and find out that Host can not take a Device descriptor that is requested by host second. (first descriptor before reset by host is correctly recognized.)

I know that OS will manage Enumeration steps but either Computer has same OS WinXP SP3.

Has anyone experienced this problem?

Thanks in advance.
------------------
Shin Jaeyong

  • As your device works fine for AMD host, the device firmware is OK, basically.
    Maybe timing issue disturbs your device from working on Intel host.

    a) Bus reset handler
    In the bus reset handler, is there any lengthy initialization ?
    Bus reset handler has to finish within the reset recovery time (10ms max).
    In the bus reset handler, enable just the default endpoint.
    In Set_Configuration handler, enable other endpoints, and initialize the communication context (variables and buffers)

    b) Suspend
    Does your device fall into suspend after bus reset ?
    Put a break point (or printf) into the suspend handler.

    Tsuneo

  • Hi Tsuneo.

    While I'm working with this project, your posts you posted any forum are a great help. Thank you.

    I finally solve this problem by changing writing routine which puts value to EP0.

    Actually, eventhough I solved this problem, I can not understand why this routine works but previous routine doesn't.

    previous routine that doesn't work puts value to regs all at once but working routine just puts every 16bytes as PXA27x UDC's EP0 has only 16 bytes memory for each direction.

    In example, first routine is like this :

    case REQ_GET_DESCRIPTOR :
        writePktEP0(descriptor,18);
    

    and modyfied routine is like this :

    case REQ_GET_DESCRIPTOR :
        writePktEP0(descriptor,16,is_short);
        writePktEP0(descriptor,2,is_short);
    

    yeah.. either routines are not exactly same but almost same.

    I think as you said timing issue,
    but still I can not understand :)
    How do you think about it?

    Thanks and Regards.
    Shin Jaeyong.

  • Ah, I see.
    It isn't timing issue.
    It's the difference of host contoller behavior, when they meet with out-of-spec response.

    As PXA27x has only 16 bytes buffer for EP0, you'll set the bMaxPacketSize0 field of the device descriptor to 16.
    At the first Get_Descriptor( DEVICE ), host requests just 8 bytes, up to bMaxPacketSize0 field. This request passes without any error. Seeing the device descriptor, Windows set the bMaxPacketSize0 value to the host controller for EP0.

    At the second Get_Descriptor( DEVICE ), host requests full descriptor, 18 bytes.
    When your device returns 18 bytes in single packet, it's out of spec, because its size is greater than bMaxPacketSize0.
    AMD host controller (OHCI) allows this exception, and it handles the response without error.
    Intel's (UHCI) strictly follows the USB spec, it rejects the exception.

    When your device returns the 18 bytes splitting into two packets, 16 and 2 bytes, it's the expected response for every host controllers. Therefore, both passes the response.

    Tsuneo

  • I found the problem.
    As you said, It may be depend on Host's act.

    Problem is how remaining (less than 4 packets) is assigned to Regs.

    previous routine assigns packets using 4bytes variable.
    if 3bytes (ie.0xB1AB1A) are remaining, those packets are sent to regs like this :

    u32 val;
    val = 0x1A;
    val |= 0xAB << 8;
    val |= 0xB1 << 16;
    val |= 0x00 << 24;
    UDCDR = val;
    

    but working routine assigns packets by 1byte variable as below :

    volatile char* reg = UDCDR;
    loop :
    *reg = remaing_packets_pointer++;
    

    Thank you for your help. :)

    Thanks and regards.
    Shin Jaeyong.

  • > if 3bytes (ie.0xB1AB1A) are remaining,

    What is the order of these 3 bytes on the real descriptor, {0x1A, 0xAB, 0xB1} or {0xB1, 0xAB, 0x1A} ?
    Isn't the order swapped?

    In Keil example for NXP LPC family, the endpoint buffer is filled as follows.

    DWORD USB_WriteEP (DWORD EPNum, BYTE *pData, DWORD cnt) {
      DWORD n;
    
      ...
      for (n = 0; n < (cnt + 3) / 4; n++) {
        TX_DATA = *((__packed DWORD *)pData);
        pData += 4;
      }
      ...
      return (cnt);
    }
    

    Tsuneo

  • Sorry for confusing you.

    those values are swapped values as LE->BE or BE->LE.

    If I send a 32 bits value 0x00B1AB1A, I should convert that value to LE because Pxa27x is basically using Big-Endian. (I'm confused actually. is that BE to LE? or LE to BE? :-)May be I will be right.)

    I'll attach pre and post(working) routine below.

    remain = writesize & 0x3;
    count = writesize - writesize % 4;
    
            if(!(writesize % 16))
    /* if writesize is not a multiple of 16 */
                    is_short = 0;
    
            while (likely(count)) {
                    buf = ((temp = *buffer++) & 0xFF);
                    buf |= ((temp = *buffer++) & 0xFF) << 8;
                    buf |= ((temp = *buffer++) & 0xFF) << 16;
                    buf |= ((temp = *buffer++) & 0xFF) << 24;
                    GETUDCDR(EP0) = buf;
                    count-=4;
    
                    if ((count % 16 == 0))
                            delay();
                    }
    
            if ( unlikely(remain > 0) ) {
                    buf = ((temp = *buffer++) & 0xFF);
    
                    for(i = 1; i < remain ; i++){
                            buf |= ((temp = *buffer++) & 0xFF) << i*8;
                    }
                    GETUDCDR(EP0) = buf;
            }
    
            if (unlikely(is_short)) {
                    UDCCSR |= UDCCSR0_IPR;
                    printf("is short!\n");
            }
    

    remain = writesize & 0x3;
    count = writesize & ~(0x3);
    
    
    if((writesize % 16) == 0) /* if writesize is not a multiple of 16 */ is_short = 0;
    while (count) { buf = ((temp = *buffer++) & 0xFF); buf |= ((temp = *buffer++) & 0xFF) << 8; buf |= ((temp = *buffer++) & 0xFF) << 16; buf |= ((temp = *buffer++) & 0xFF) << 24; GETUDCDR(EP0) = buf; count-=4; }
    if (remain) while ( remain -- ) *reg = *buffer++;
    delay();
    if (is_short) { UDCCSR |= UDCCSR0_IPR;

  • PXA27x is Little Endian, which matches to USB. Just pass data without endian conversion, like above LPC routine.

    PXA270 Processor Developers Manual
    www.marvell.com/.../PXA270_Dev_Manual.pdf

    2.3 Endianness
    The PXA27x processor operates in Little Endian mode only, in which the least significant byte (LSB) of a value is stored in memory at a lower address than the most significant byte (MSB).

    Tsuneo

  • Oops! Thanks Tsuneo :)

    I'm just new to embedded system programming and I already have one. ;)

    Thanks and regards
    Shin Jaeyong.

  • For "move memory" operation, like above routine, we don't need to care of the endianness of the core. In either endian, the byte order of the source memory is preserved on the target. When the core exchanges literals or variables with the USB side, endianness of the core concerns.

    USB memory is assigned to the endpoint buffers in 32-bit word boundary, which matches to the core operation. The extra padding bytes don't do any harm, when the packet size doesn't fit to the word boundary. Then, copy up to the next word boundary, as it is.

    Tsuneo