PORTS Forum

Ports and Interfaces => USB => Topic started by: mdlayt on March 29, 2011, 04:44:39 pm

Title: Cypress Encore USB Library behavior you may want to be aware of
Post by: mdlayt on March 29, 2011, 04:44:39 pm
The Cypress library has some "interesting" code.  I recently ran into this "feature" because I had corrupted the table for endpoint functions (actually corrupted addressing of the table by moving it to RAM).  You might think, as I did, that this would require digging into the code that accesses the table.  It does.  You must change the "romx" code to regular RAM indexing.  But, I made the mistake of thinking, "I'll test that and debug the code as it goes."  And, of course, it works.  Sort of.  And that was long ago enough that when I looked at it again, I convinced myself that it couldn't be broken.  Here is why.

You can see in the code excerpts below that if you pass garbage as the endpoint function to ConfigureEP, it will most likely enable the interrupt for your endpoint anyway.  (Only one specific value--0x7F--will stop it from doing so.)  And you can also see that any endpoint that the host calls Clear Endpoint Halt on, will end up enabled, regardless of whether it is in your current configuration.  So if your Configuration and Endpoint Descriptors are correct, and you run on Windows HID--which does a Clear Endpoint Halt by default, it probably doesn't matter what is in your Endpoint function table.


// psuedo code showing the behavior in Set Configuration: ConfigureEP

#define USB_DIR_IN     (0x80)
#define USB_DIR_OUT    (0x00)
#define USB_DIR_UNUSED (0x7F)

if (ep_direction_table_entry == USB_DIR_UNUSED) {
    leave unconfigured;
} else if (ep_direction_table_entry & USB_DIR_IN) {
    configure as IN;
} else {
    configure as OUT;
}

//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

; the actual code (excerpts)

USB_DIR_IN:                                 EQU    0x80
USB_DIR_OUT:                                EQU    0x00
USB_DIR_UNUSED:                             EQU    0x7F

;-----------------------------------------------------------------------------
;  FUNCTION NAME: USB_CB_h2d_std_dev_09
;
;  DESCRIPTION:   Set Configuration
USB_CB_h2d_std_dev_09:
 ~~~
    mov     X, 0                       ; Start the index at 0, but we inc first
.configure_next:
    inc     X                          ; Do the next one
    push    X                          ; Save the endpoint number
    mov     A, [USB_t1]                ; Get the CONFIG_LOOKUP ROM Address MSB
    mov     X, [USB_t1+1]              ; Get the CONFIG_LOOKUP ROM Address LSB
    romx
    inc     [USB_t1+1]                 ; Point to the next
    adc     [USB_t1], 0                ;
    pop     X
    call    ConfigureEP                ; X contains the EP number
                                       ; A contains the EP Direction
    mov     A, X                       ;
    cmp     A, USB_MAX_EP_NUMBER       ; Configure each of the endpoints
    jnz     .configure_next            ; Do another one?
 ~~~

;-----------------------------------------------------------------------------
;  FUNCTION NAME: ConfigureEP
ConfigureEP:
    cmp     A, USB_DIR_UNUSED          ; Is this endpoint unused?
    jnz     .enable                    ; Only enable it if it is used
    ret                                ; Quick exit if this endpoint is unused
 ~~~
    jacc    .EP_INT_ENABLE             ;
     <...>
    M8C_EnableIntMask USB_INT_REG1, USB_INT_EP1_MASK
     <...>
    M8C_EnableIntMask USB_INT_REG2, USB_INT_EP7_MASK
 ~~~
    and     A, USB_DIR_IN              ; Is it an IN endpoint?
    jnz     .in                        ; Jump on IN
; Flow here for an OUT Endpoint
    mov     reg[X+USB_EP1MODE-1], USB_MODE_ACK_OUT   ; ACK the endpoint
    mov     [X+USB_EndpointAPIStatus], NO_EVENT_PENDING   ; For the API
    jmp     .exit1
; Jump here for an IN Endpoint
.in:
    mov     reg[X+USB_EP1MODE-1], USB_MODE_NAK_IN   ; NAK the endpoint
    mov     [X+USB_EndpointAPIStatus], EVENT_PENDING   ; For the API
 ~~~

;-----------------------------------------------------------------------------
;  FUNCTION NAME: USB_CB_h2d_std_ep_01
USB_CB_h2d_std_ep_01:
    mov     A, reg[USB_EP0DATA+wValueLo]    ; Get the feature selector
    cmp     A, USB_ENDPOINT_HALT       ; Halt is the only selector defined for endpoints
    jnz     USB_Not_Supported

    mov     A, reg[USB_EP0DATA+wIndexLo]    ; Get the Endpoint number
    and     A, ~USB_DIR_IN             ; Strip off the direction bit
    cmp     A, 0                       ; Since we can't halt the Control Endpoint
    jz      .done

    cmp     A, USB_NUM_ENDPOINTS       ; Range check
    jnc     USB_Not_Supported

    mov     X, A                       ; Endpoint number is the index
    and     [X+USB_EndpointStatus], ~USB_ENDPOINT_STATUS_HALT    ; Clear the endpoint halt

    index   USB_USB_EPX_BIT_LOOKUP      ; Find bit position for endpoint
    xor     A, FFh
    and     [USB_EPXDataToggle], A ; Clear the data toggle for this endpoint

    tst     reg[USB_EP0DATA+wIndexLo], USB_DIR_IN    ; IN or OUT endpoint?
    M8C_SetBank1                          ; For EP1_MODE register
    jnz     .in

    ; Mark endpoint as empty so it will be reloaded
    mov     [X+USB_EndpointAPIStatus], NO_EVENT_PENDING
    mov     reg[X + USB_EP1MODE - 1], USB_MODE_ACK_OUT    ; ACK the endpoint
    jmp     .done
.in:
    mov     [X+USB_EndpointAPIStatus], EVENT_PENDING
    mov     reg[X + USB_EP1MODE - 1], USB_MODE_NAK_IN    ; NAK the endpoint
.done:
 ~~~

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;