Author Topic: Problem Sending USB Data to Host  (Read 39067 times)

NiGHTS

  • Member
  • ***
  • Posts: 10
Problem Sending USB Data to Host
« on: August 11, 2010, 02:22:08 pm »
Hello. I have a problem with a simple USB concept which, after burning through many EPROM chips, I can't get 8 byte data to send from the firmware to the driver, but I have no problem sending it the other way around.

I am using the cypress CY7C63413C SSOP48, and I am using one of the earliest example source code from Jan Axelson, which is a modification of the logo.asm cypress example which drives an HID mouse. I am using libusb to communicate to the chip, and so far I have been successful writing 8 bytes of data on command via this function:

usb_control_msg(dev, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_ENDPOINT_OUT, 9, 0, 0, buf, 8, 10);

Reading 8 bytes is a whole other story. I have only been successful at putting data inside endpoint 1 from inside here:

Code: [Select]
Master_Loop:
                                                         
 mov A, [event_machine]           
 jacc event_machine_jumptable                 
                                                 
 no_event_pending:                                   
                                                         
   mov A, [configuration]                     
   cmp A, 01h                               
   jnz no_event_task                               
                                                     
   mov A, [ep1_stall]                             
   cmp A, FFh                                   
   jz    no_event_task                           
                                                           
   ; Write to ep1 here
   mov A, 01h
   mov [ep1_dmabuff0], A
   mov [ep1_dmabuff1], A

   mov A, 02h                                   
   or A, [ep1_data_toggle]         
   iowr EP_A1_Counter_Register                     
                                                             
   mov A, ACK_IN                             
   iowr EP_A1_Mode_Register                       
                                                         
   mov A, EVENT_PENDING                             
   mov [event_machine], A                       
                                                             
    event_task_done:                                   
 no_event_task:                                         
 jmp Master_Loop             

But that is unreliable and delayed.

I've also tried creating an endpoint interrupt program for endpoint 1 but I have had no luck with it either.

Code: [Select]
Interrupt_Endpoint_1:
push A

iord EP_A1_Mode_Register
and A, EP_ACK
    jz endpoint1_NAK

    mov A, 00h
    mov [Test7], A

            mov      A, [Test0]
            mov      [ep1_dmabuff0], A
            mov      A, [Test1]
            mov      [ep1_dmabuff1], A

            mov A, 02h
   or    A, [ep1_data_toggle]
   iowr    EP_A1_Counter_Register               

            mov A, ACK_IN_STATUS_OUT
            iowr EP_A1_Mode_Register

            mov A, DATA_TOGGLE
            xor [ep1_data_toggle], A

            mov A, NO_EVENT_PENDING

    mov [event_machine], A


      jmp endpoint1_done
      endpoint1_NAK:

      mov A, NAK_IN
      iowr EP_A1_Mode_Register

      endpoint1_done:
    pop A
 reti

Please help me send data to the PC! I can't find any good way to send data  :-\




Guido Koerber

  • Frequent Contributor
  • ****
  • Posts: 72
Re: Problem Sending USB Data to Host
« Reply #1 on: August 11, 2010, 06:46:26 pm »
If you are using Cypress sample code: Good luck. It is not quite good.

From the two code snippets it is hard to determine what is going wrong.

Basically what you should do is to have an interrupt routine that handles the endpoint when a data packet has been transmitted, checking for and clearing the ACK bit, inverting data toggle bit (you are transmitting, so it is up to you to do that), i.e.:
Code: [Select]
USB_EP1_ISR:

push A ; save accumulator on stack
iord EP_A1_Mode ; test whether we have an ACK bit
and  A, ACK_BIT
      jz   doneEP1 ; do nothing if we don't have an ACK bit

mov  A, NAKIN ; clear ACK bit
iowr EP_A1_Mode

iord EP_A1_Counter ; flip data 0/1 bit after
xor  A, DATATOGGLE ; a successful data transfer
iowr EP_A1_Counter

doneEP1:
pop  A ; restore accumulator from stack
reti ; return from interrupt

In your function to load the endpoint check if the endpoint is still in ACK_IN mode, if it is not you can load the endpoint FIFO and arm it for transmission.

NiGHTS

  • Member
  • ***
  • Posts: 10
Re: Problem Sending USB Data to Host
« Reply #2 on: August 11, 2010, 07:58:04 pm »
Code: [Select]
USB_EP1_ISR:

push A ; save accumulator on stack
iord EP_A1_Mode ; test whether we have an ACK bit
and  A, ACK_BIT
      jz   doneEP1 ; do nothing if we don't have an ACK bit

mov  A, NAKIN ; clear ACK bit
iowr EP_A1_Mode

iord EP_A1_Counter ; flip data 0/1 bit after
xor  A, DATATOGGLE ; a successful data transfer
iowr EP_A1_Counter

doneEP1:
pop  A ; restore accumulator from stack
reti ; return from interrupt

Ok, so this code is executed when an "in" request occurs?

What i need to do is submit 8 bytes from the firmware at any random time to the PC. How can I tell the firmware, using the code you provided, to send [data0] through [data7] bytes whenever it feels like it? As for the PC driver, i will have a thread in a loop checking for any data from endpoint 1 and respond to new data when it returns more than zero bytes.

Also, what if data is loaded and ready to go and a new series of bytes needs to be sent? How could the firmware find out when to safely load ep1's buffer with new data from [data0] to [data7] so I don't overwrite the data that hasn't been sent yet?

I have burned through 14 cypress EPROM's without finding the right combination of code that would allow me to do this.

Guido Koerber

  • Frequent Contributor
  • ****
  • Posts: 72
Re: Problem Sending USB Data to Host
« Reply #3 on: August 11, 2010, 08:15:08 pm »
You should get one of the CY3654 emulators, they are flaky but make it a lot easier to debug.

In your main loop you check if the ACK_IN is set for the endpoint, if that is not the case the endpoint can be loaded with new data. You have to decide what strategy to use if the data is generated faster than you can send it to the host.

NiGHTS

  • Member
  • ***
  • Posts: 10
Re: Problem Sending USB Data to Host
« Reply #4 on: August 11, 2010, 08:26:51 pm »
How do i trigger Endpoint 1 to send data?

Guido Koerber

  • Frequent Contributor
  • ****
  • Posts: 72
Re: Problem Sending USB Data to Host
« Reply #5 on: August 12, 2010, 05:21:07 am »
You load the number of bytes into the count register and set the endpoints mode to ACK_IN.

Do you have a USB analyzer available? There are many more details that can prevent your host from properly receiving the data.

I do not answer such questions by email, helping here in the forum makes the information available for many people.

NiGHTS

  • Member
  • ***
  • Posts: 10
Re: Problem Sending USB Data to Host
« Reply #6 on: August 12, 2010, 09:07:17 am »
Ok, here is what I have so far:

Code: [Select]
;------------------------------------
; Endpoint 1 Processing
USB_EP1_ISR:

push A ; save accumulator on stack
iord EP_A1_Mode ; test whether we have an ACK bit
and  A, ACK_BIT
      jz   doneEP1 ; do nothing if we don't have an ACK bit

mov  A, NAKIN ; clear ACK bit
iowr EP_A1_Mode

iord EP_A1_Counter ; flip data 0/1 bit after
xor  A, DATATOGGLE ; a successful data transfer
iowr EP_A1_Counter

doneEP1:
pop  A ; restore accumulator from stack
reti ; return from interrupt

; ------------------------------------------------
; Called when firmware needs to send data to host
; Pass X=Number of bytes preloaded into EP1_Data bytes
; Returns X=0 if busy, X=unchanged if ok
SendData:
push A
push X

; If busy, exit with X=0
iord EP_A1_Mode
and A, ACK_IN
cmp A, ACK_IN
jnz SendData_Busy

; Set the endpoint mode to ACK_IN to kick start the transmission
mov A, ACK_IN
iowr EP_A1_Mode

; all done, all ok
pop X
pop A
ret

SendData_Busy:
; Set X=0 to indicate that nothing was sent
pop X
mov X, 00h
pop A
ret

Is the way that I am checking for ACK_IN okay? Is this concept correct? Can my firmware call this function and expect to send data using that exact interrupt code?

By the way, I will be using a USB analyzer from now on. That is a very good idea to use! But there is nothing else wrong. The only problem I have is my understanding of how to actually make the data leave the chip. So if I can get the code just right my driver should just work. Like I said, I have had limited success with using EP1 to transmit out of the chip, but my problem has always been 1) determining when it is safe to send data and 2) triggering the sending of the data. Everything else is perfectly working.

So if you can, please tell me if you see any problems with the code I just wrote and I will try it out as soon as my new order of 20 blank cypress chips comes in.

Guido Koerber

  • Frequent Contributor
  • ****
  • Posts: 72
Re: Problem Sending USB Data to Host
« Reply #7 on: August 12, 2010, 09:14:42 am »
You also need to write the number of bytes you want to send to the counter register for the endpoint, make sure to preserve the toggle bit in this case.

Also you need to load your data to the transmit FIFO.

NiGHTS

  • Member
  • ***
  • Posts: 10
Re: Problem Sending USB Data to Host
« Reply #8 on: August 12, 2010, 09:30:49 am »
Ok, with what you just told me:

Code: [Select]
;----------------------------------------
; This code will be in the firmware somewhere...
FirmwareCode:
; load the data into the dma buffer
mov A, [Test0]
mov [ep1_dmabuff0], A
mov A, [Test1]
mov [ep1_dmabuff1], A

mov X, 02h ; 2 bytes of data pre-loaded
call SendData

; do other things...
nop
nop
ret

;------------------------------------
; Endpoint 1 Processing
USB_EP1_ISR:

push A ; save accumulator on stack
iord EP_A1_Mode ; test whether we have an ACK bit
and  A, ACK_BIT
      jz   doneEP1 ; do nothing if we don't have an ACK bit

mov  A, NAKIN ; clear ACK bit
iowr EP_A1_Mode

iord EP_A1_Counter ; flip data 0/1 bit after
xor  A, DATATOGGLE ; a successful data transfer
iowr EP_A1_Counter

doneEP1:
pop  A ; restore accumulator from stack
reti ; return from interrupt

; ------------------------------------------------
; Called when firmware needs to send data to host
; Pass X=Number of bytes preloaded into EP1_Data bytes
; Returns X=0 if busy, X=unchanged if ok
SendData:
push A
push X

; If busy, exit with X=0
iord EP_A1_Mode
and A, ACK_IN
cmp A, ACK_IN
jnz SendData_Busy

mov A, X ; bytes to send
or  A, [ep1_data_toggle] ; Preserve toggle state?
iowr EP_A1_Counter

; Set the endpoint mode to ACK_IN to kick start the transmission
mov A, ACK_IN
iowr EP_A1_Mode

; all done, all ok
pop X
pop A
ret

SendData_Busy:
; Set X=0 to indicate that nothing was sent
pop X
mov X, 00h
pop A
ret


Here I added an example of the firmware calling the SendData function. It loads 2 test bytes to the endpoint 1 FIFO.

Also, right before flagging ACK_IN, It will copy the value in X into the counter as well as the toggle bit in its current state.

Am I doing it right? Does it look like it will work? Do you have any more advice for me?

Again, thank you immensely for your help so far!

Guido Koerber

  • Frequent Contributor
  • ****
  • Posts: 72
Re: Problem Sending USB Data to Host
« Reply #9 on: August 12, 2010, 10:29:47 am »
Do not write to the FIFO before checking that the endpoint is idle, writing to the FIFO is disabled while the endpoint is armed to send data.

The toggle bit is not stored in memory by the interrupt routine, it is in the endpoint data count register.

NiGHTS

  • Member
  • ***
  • Posts: 10
Re: Problem Sending USB Data to Host
« Reply #10 on: August 12, 2010, 11:31:59 am »
Ok, based on what you told me, I've made some more changes:

Code: [Select]
;----------------------------------------
; This code will be in the firmware somewhere...
FirmwareCode:
; load the data into the dma buffer
mov A, [Test0]
mov [SendOut0], A
mov A, [Test1]
mov [SendOut1], A

mov X, 02h ; 2 bytes of data pre-loaded
call SendData

; do other things...
nop
nop
ret

;------------------------------------
; Endpoint 1 Processing
USB_EP1_ISR:

push A ; save accumulator on stack
iord EP_A1_Mode ; test whether we have an ACK bit
and  A, ACK_BIT
      jz   doneEP1 ; do nothing if we don't have an ACK bit

mov  A, NAKIN ; clear ACK bit
iowr EP_A1_Mode

iord EP_A1_Counter ; flip data 0/1 bit after
xor  A, DATATOGGLE ; a successful data transfer
iowr EP_A1_Counter

doneEP1:
pop  A ; restore accumulator from stack
reti ; return from interrupt

; ------------------------------------------------
; Called when firmware needs to send data to host
; Pass X=Number of bytes preloaded into EP1_Data bytes
; Returns X=0 if busy, X=unchanged if ok
SendData:
push A
push X

; If busy, exit with X=0
iord EP_A1_Mode
and A, ACK_IN
cmp A, ACK_IN
jnz SendData_Busy

; Update the counter with the number of bytes to send
mov A, X
mov [tmp], A
iord EP_A1_Counter
or  A, [tmp] ; tmp contains number of bytes to send
iowr EP_A1_Counter

; Transfer the data from the buffer into the endpoint FIFO
mov X, 00h
SendData_Loop_Start:
mov A, [SendOut0 + X]
mov [ep1_dmabuff0 + X], A

inc X
mov A, X
cmp A, 08h
jnz SendData_Loop_Start
SendData_Loop_End:

; Set the endpoint mode to ACK_IN to kick start the transmission
mov A, ACK_IN
iowr EP_A1_Mode

; all done, all ok
pop X
pop A
ret

SendData_Busy:
; Set X=0 to indicate that nothing was sent
pop X
mov X, 00h
pop A
ret

So this time I have a buffer between Test(0-7) and ep1_dmabuff(0-7) called SendOut(0-7) which loads the test data only after it knows it can.

The next thing I changed was the part which updates the counter in the SendOut function. Now EP_A1_Counter's state is merely updated with the count, which I presume will preserve the state of the toggle bit?

Let me know if you see anything else I should add to this.

And again THANK YOU SO MUCH!!

NiGHTS

  • Member
  • ***
  • Posts: 10
Re: Problem Sending USB Data to Host
« Reply #11 on: August 12, 2010, 07:09:27 pm »
I have tried out the code we worked on together on a live chip. I am having the same kind of problem that I've been having in the first place.

This is what my driver code looks like. Remember that I am using libusb for windows.

Code: [Select]
    // Connect to the USB device
    // On the USB Analyzer, these are the Blue events
    char tmp[8];
    usb_dev_handle *dev = NULL;
    usb_init();
    usb_find_busses();
    usb_find_devices();
    open_dev();
    usb_set_configuration(dev, 1);
    usb_claim_interface(dev, 0);

    // request a call to SendData in the firmware which will load 8 bytes of test data into endpoint 1
    // On the USB Analyzer, these are the Green events
    tmp[0] = 0x05;
    tmp[1] = 0x08;
    tmp[2] = 0x01;
    tmp[3] = 0x08;
    tmp[4] = 0x00;
    tmp[5] = 0x00;
    tmp[6] = 0x00;
    tmp[7] = 0x00;
    usb_control_msg(dev, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_ENDPOINT_OUT, 9, 0, 0, tmp, 8, 10);

    // Retrieve the test bytes from endpoint 1
    // On the USB Analyzer, these are the Red events
    for (int t=0;t<8;t++)
        tmp[t] = 0;

    y = usb_interrupt_read(dev, 0x81, tmp, 8, 5000);

    // At this point I do in fact receive the test data, but I can no longer receive any data
    // from endpoint 1 without resetting the device. Therefore, this only works once.

    usb_release_interface(dev, 0);
    usb_close(dev);

The problem is that I can only get data the first time from endpoint 1. After that, it no longer lets me receive data.

I used a USB Software Analyzer and these are the results. I have highlighted the events which correspond to the code in the driver above.



The last event with a status of UNSUCCESSFUL has this error: "USBD_STATUS_BUFFER_OVERRUN"

This might also be helpful to trying to figure out whats wrong. This is a report from libusb about the devices capabilities:



What do you think is wrong?

Guido Koerber

  • Frequent Contributor
  • ****
  • Posts: 72
Re: Problem Sending USB Data to Host
« Reply #12 on: August 12, 2010, 08:45:07 pm »
A software analyzer is close to useless for this kind of debugging, it does not show packets that are corrupted.

I am not familiar with libusb and I never test new devices on Windows first since this useless os is often a major problem when debugging USB. Likely you are running into one of its inherent problems, like Windows totally blocking reports that do not fit the corresponding report descriptor.

Since you are building a HID class device, why are you using libusb?

NiGHTS

  • Member
  • ***
  • Posts: 10
Re: Problem Sending USB Data to Host
« Reply #13 on: August 13, 2010, 01:46:54 pm »
Not sure why I am using windows. The device is meant to be used in linux once I work out all the device bugs. What software analyzer do you reccomend that can run in ubuntu?

I am using the HID descriptors just for testing. Ultimately this will be a proprietary device, only USB device used, and on a dedicated system. I used the modified logo.asm code just to get my feet wet and understand the concepts of transmission.

By the way, I found out what was wrong with sending data. In the endpoint 1 descriptor, I had set the maximum packet size to 3. It was supposed to be 8. Changing that fixed the problem.

Now the only problem I am still having is trying to figure out why this peice of code does not work:

Code: [Select]
; If busy, exit with X=0
iord EP_A1_Mode
and A, ACK_IN
cmp A, ACK_IN
jnz SendData_Busy

This is from the most recent code I posted for the "SendData" function call. When I include this, It is always jumping to "SendData_Busy" even if the endpoint was never called. When I remove this, everything works fine. But by removing this code I can no longer keep SendData from being called when its in the middle of a transfer. What do you think I am doing wrong?

Guido Koerber

  • Frequent Contributor
  • ****
  • Posts: 72
Re: Problem Sending USB Data to Host
« Reply #14 on: August 13, 2010, 06:54:18 pm »
Your jump is wrong.

I recommend to use a hardware analyzer, a software analyzer can only show you a high level view, it will not show you errors in the toggle bit sequence, report size etc.

Make sure your report descriptor defines exactly the same data size as you actually send via your endpoint.