PORTS Forum
Ports and Interfaces => USB => Topic started 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:
~~~
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;