Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vendor Device Receiving URB but fifo buffer random/empty #2806

Closed
1 task done
moefear85 opened this issue Sep 16, 2024 · 25 comments · Fixed by espressif/esp-usb#62
Closed
1 task done

Vendor Device Receiving URB but fifo buffer random/empty #2806

moefear85 opened this issue Sep 16, 2024 · 25 comments · Fixed by espressif/esp-usb#62
Labels

Comments

@moefear85
Copy link

moefear85 commented Sep 16, 2024

Operating System

Linux

Board

esp32s2 module

Firmware

esp-idf v5.3.1

What happened ?

I'm trying to send "hello esp" from a custom linux usb driver to an esp32s2 esp-idf application running the tinyusb stack. I enabled "CFG_TUD_VENDOR 1" in tusb_config.h". When I send a 64 byte packet to the esp32s2, the stack detects the packet, but its contents are random/empty.

I added some logging statements here and there in esp-idf & tinyusb, to try to pinpoint the problem. But I can't go deep into the stack, it's beyond my expertise. I used wireshark to verify that a single 64 byte bulk packet is being sent to the esp at endpoint 1 with the text "hello esp!". tinyusb is detecting it, but not providing the actual contents of the packet.

I tried using my own task for tusb_task(), as well as espressif's from sdkconfig, but it makes no difference.

As I hope is clear from the provided logs, dumps, and screenshots, I defined the void tud_vendor_rx_cb(uint8_t itf) function so I get notified every time a packet arrives, so I can print its dump. I print the dump once before calling int read=tud_vendor_read(buffer_in,available); and once after, so it is clear how the buffer changes. Sometimes it remains empty, in other cases it is filled with random non-/readable data. It would seem to me like a stray pointer issue.

It would be nice if you could help me confirm whether I am doing something wrong, or whether the esp32s2 dwc2 doesn't support vendor device class, or if there is perhaps a bug in the stack.

One thing I find strage, is at the end of the boot process, there is the following:

Open EP 01 with Size = 64
  Open EP 81 with Size = 64
    Allocated 64 bytes at offset 896
  Queue EP 01 with 64 bytes ...
  VENDOR opened
  Bind EP 01 to driver id 0
  Bind EP 81 to driver id 0
  Queue EP 80 with 0 bytes ...

notice after opening & allocating EP 0x01 and 0x81, the stack is queuing EP 01 with 64 bytes. It is unclear to me why this is happening, and what is triggering it. In this time the esp is connected to the host, but actually bound to the driver, so why is it queuing data into EP 01? What data is it queuing? From the esp perspective, this is the rx endpoint. But the host isn't sending anything. How/why is data getting queued into that endpoint? Why is it getting queued before endpoints 0x01 and 0x81 are bound and even before vendor is opened, instead of after? Is this correct behavior?

Another problem:
If I change the endpoint to 0x81 (ie make it a receive request), then the tinyusb stack never responds, doesn't even register that the request arrived, and so tud_vendor_tx_cb is never called, and nothing is ever output to the logs.

How to reproduce ?

This is the code that is running on the esp32s2. I'm using the tinyusb component that esp-idf automatically downloads. They modify the CMakeLists.txt file a bit and import it in a slightly different way than the original.

esp_code.c.txt

Some log statements are the result of adding log statements to vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) as follows:
vendord_xfer_cb.txt

In case it is relevant, here is my sdkconfig:
sdkconfig.txt

Debug Log as txt file (LOG/CFG_TUSB_DEBUG=2)

This is the initial log after booting the mcu. It is not yet attached to the PC.

esp_log.txt

After connecting the USB port, the following log is produced:

esp_log_after.txt

Here is the wireshark dump (please remove the .txt extension, it is a pcapng capture, not a text file):
esp32s2 - vendor device class - usb dump.pcapng.txt

Screenshots

screenshot from vscode esp-idf monitor output:
image

wireshark screenshot running on host:
Screenshot_20240916_194007

I have checked existing issues, dicussion and documentation

  • I confirm I have checked existing issues, dicussion and documentation.
@HiFiPhile
Copy link
Collaborator

HiFiPhile commented Sep 16, 2024

What you met is really awkward, never heard something like this.
What I can suggest is try to rule out problems:

  1. ESP's USB wrapper layer is not covered by this project, try to test without it: https://github.com/hathach/tinyusb/tree/master/examples/device/cdc_msc_freertos
    You can find build instruction here: https://github.com/hathach/tinyusb/blob/master/examples/device/audio_test_freertos/README.md
  2. Try to use this script stripped from my project to do read / write.
import libusb_package
import usb.core
import usb.util

#---------------------------------------------------------
#   Main
#---------------------------------------------------------

def main():
    for dev in libusb_package.find(find_all=True):
        intf = usb.util.find_descriptor(dev[0], custom_match=find_intf("Your interface name"))
        if intf is not None: break

    if intf:
        def ep_read(len):
            try:
                return ep_in.read(len, 100)
            except:
                return None
        def ep_write(buf):
            try:
                ep_out.write(buf, 100)
            except:
                pass

        maximum_packet_size = 64

        ep_in  = usb.util.find_descriptor(intf, custom_match = \
        lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN)

        ep_out = usb.util.find_descriptor(intf, custom_match = \
        lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUT)


        buf = [0x01, reg, 0, 0]
        buf[3] = (buf[0] + buf[1] + buf[2]) % 256
        ep_write(bytes(buf))

        ret = ep_read(maximum_packet_size)


if __name__ == '__main__':
    main()

@moefear85
Copy link
Author

your suggestion seems to be rubberstamped. could you try something relevant to the actual logs? why do you think that the espressif wrapper is the source of this behavior? does tinyusb provide a working example for vendor class on espressif mcus?

@HiFiPhile
Copy link
Collaborator

your suggestion seems to be rubberstamped. could you try something relevant to the actual logs?

That's why I said it's really awkward, after read your logs !

why do you think that the espressif wrapper is the source of this behavior

It's an unknown factor.

does tinyusb provide a working example for vendor class on espressif mcus?

WebUSB example use vendor class, unfortunately no FreeRTOS version is done.
https://github.com/hathach/tinyusb/tree/master/examples/device/webusb_serial

CDC class use nearly the same structure as vendor class, that's why I linked taht example.

@moefear85
Copy link
Author

moefear85 commented Sep 16, 2024

That's why I said it's really awkward, after read your logs !

that's not what "rubberstamped" means! what exactly is awkward? can you be more specific? you can try google translator to french if that helps.

It's an unknown factor.

the chip revision is also an unknown factor. but i don't see why that would be less relevant. In fact could it be because rev 0.0? I'll look deeper into this tomorrow if I have time. If I find anything I will inform you.

@HiFiPhile
Copy link
Collaborator

What I got in translation is : "to officially approve a decision or plan without thinking about it:"
Correct me if was wrong, what I thought is you are accusing me of blindly reply without understand the issue.

I said the issue is awkward since bulk in & out transfer is a basic function that maybe half of the people use, if it's broken we should already received many manifestations. That's why I suggest to rule out other factors first.

@moefear85
Copy link
Author

i didnf accuse anyone of anything. you started shouting fir no reason. jt makes a discussion difficult. i guess um on my own on this issue. ill compafe it to cdc & work it out.

@tore-espressif
Copy link
Contributor

tore-espressif commented Sep 17, 2024

@moefear85 Thank you for the detailed logs.

In the 'ESP additions to TinyUSB' we focus on classes that need some ESP specific handling, eg. port to our network layer, or port to our storage layer. The Vendor specific class can be used directly from TinyUSB.

Anyway, the support of Vendor Specific class will be improved in espressif/esp-usb#62 . You descriptors will get a default values, so you can start developing you application faster.

@HiFiPhile I tried single and dual Vendor specific interfaces, all work fine on ESP32-S3

@moefear85
Copy link
Author

moefear85 commented Sep 18, 2024

@tore-espressif @HiFiPhile

thx for recognizing the effort in preparing the logs, to help in debugging. I noticed, the dcd driver being used (in tinyusb) is src/portable/synopsys/dwc2/dcd_dwc2.c, which includes dwc2_esp32.h although there is also a driver in src/portable/espressif/esp32sx/dcd_esp32sx.c. Why are there 2 drivers (the contents are very similar), why is one chosen not the other, is this choice configurable, and is it possible to test the dcd_esp32sx driver instead, as perhaps there is some subtle bug in dcd_dwc2.c.

Is it possible that the driver expects/requires that vendor_device use specific endpoints other than 0x01 and 0x81? I'm pretty sure at this point the driver is simply returning all zeroes, it is not a mistake on my part or in the vendor_device.c. Under what circumstances would the driver (or peripheral) return all zeroes for a completed OUT packet?

I recall from espforum that the usb IP in the esp32s2 is a synopsis dwc2 (designware core usb 2.0). Is the dcd_esp32sx.c driver obsolete?

Either way, I did some more testing. I checked the addresses of _vendor_itf.epout_buf at runtime at all points where it is accessed, and I'm always getting 0x3ffc8bd4. So it seems that vendor_device.c::_prep_out_transaction(...) which calls usbd_edpt_xfer which in turn calls dcd_edpt_xfer. That in turn seems to be taking the right address (of _vendor_itf.epout_buf) and storing it in xfer_ctl_t for endpoint 0x01. But still on completion it returns all zeroes.

Is the address 0x3ffc8bd4 correct and meaningful (properly aligned, DMA capable, has r/W permissions, etc)?

Am I perhaps using a (known) bad commit for tinyusb? git log gives:

commit 33d36cb23d6f8415d0e8a09d15a5e6cd8c0a6d05 (grafted, HEAD -> release/v0.15, tag: v0.15.0.10, origin/release/v0.15)
Author: Tomas Rezucha <[email protected]>
Date:   Thu Jun 6 18:55:55 2024 +0200

the same for esp_usb component gives:

commit 39b0de39a90c9e391db26f06d54e4b10f58be231 (grafted, HEAD -> master, origin/master, origin/HEAD)
Author: Roma Jam <[email protected]>
Date:   Tue Aug 20 11:52:50 2024 +0200

    Merge pull request #49 from espressif/fix/hid_host_hub_support

@moefear85
Copy link
Author

I just checked the esp32s2 errata. It says:

3.5 [USB OTG] Abnormal data during AHB bus arbitration by USB OTG
Affected revisions: v0.0
Description
When the USB OTG peripheral and some other competing peripherals (listed below) simultaneously execute a
request on the Advanced High-performance Bus (AHB), the AHB may generate incorrect arbitration signals, which
results in the USB OTG peripheral reading or writing erroneous data. The competing peripherals include:
• I2S
• SPI
Workarounds
1. Avoid AHB bus competition between USB OTG and above peripherals by not using DMA mode of USB
OTG, or disabling DMA mode of above peripherals.
2. Avoid competing with the USB OTG’s AHB bus access. Specifically, set USB OTG’s AHB burst transfer
mode to INCR to prevent competition from the other peripherals. In this mode, USB OTG will occupy the
AHB bus exclusively until the burst transfer is completed.
Note: Use the INCR burst mode with care, as it requires adjustment to maximum packet size (MPS) for USB
OTG endpoints, so that burst time is smaller than the timeout period of the competing peripherals.
Espressif Systems 7
Submit Document Feedback
ESP32-S2 Series SoC Errata Version 1.2
3 All Errata Descriptions
Solution
Fixed in chip revision v1.0. With this fix, the AHB bus will correctly arbitrate competing access. ESP-IDF adds
USB OTG support starting from v4.4. When the specific conditions listed below are met, ESP-IDF enables the
INCR mode workaround, i.e., using the INCR mode to guarantee that the USB OTG’s exclusive access to the
AHB. The conditions for ESP-IDF to enable this workaround are as follows:
1. For chip revision v0.0, ESP-IDF always enables the workaround.
2. ESP-IDF added support for chip revision v1.0 in ESP-IDF v4.4.6, v5.0.4, v5.1.2, and v5.2. In these and
above version, the software automatically detects the chip revision. When chip revision v1.0 or later revisions
are detected, ESP-IDF no longer enables the workaround.
3. In ESP-IDF versions that do not support chip revision v1.0, i.e., v4.4-v4.4.5, v5.0-v5.0.3, v5.1-v5.1.1,
ESP-IDF always enables the workaround.

the description doesn't seem to fit exactly to what I am facing, unless the normal functioning of the esp implies DMA SPI read accesses to flash, and that this is conflicting with USB in a way that is not worked around in esp v5.3. My chip is revision v0.0 however. Is this a possibility? I haven't tried esp32s3 yet, it's on my todo list.

@HiFiPhile
Copy link
Collaborator

Before going too far, how did the cdc_msc test carried out, does the serial port echo work ?

If it's working:
You can unbind the serial cdc_acm driver then bind your own driver on the cdc interface as if it's a vendor interface and redo your test.

If not:
@tore-espressif Is there anyway I can got a esp32s2 v0.0 board for testing, I think most boards are using v1.0 ?

@moefear85
Copy link
Author

moefear85 commented Sep 18, 2024

You can unbind the serial cdc_acm driver then bind your own driver

I'll try that in my next attempt. Part of the reason why I'm trying to use vendor device on the esp is because the generic drivers bind automatically, and I can't find clear information on how to prevent this using udev rules, or if that is even possible, and don't want to have to manually unbind every time I plug in the device. But for debugging, it's worth a try. But I don't think it would make much of a difference, because I've tried using usbd.c bare metal (not using vendor read/write functions, rather directly responding to DCD_XFER_COMPLETE event, didn't help. I also tried copying as much as possible from cdc class into vendor class to make them as identical as possible, except for a little bit that I deemed not important and too time consuming to "port". still didn't help. But I'll try the unbind method soon.

In the mean time, I just tried the same setup as before on esp32-s3-wroom-1 module, rev v0.1. Exact same problem. Only getting zeroes. I don't see many options right now. I either dive into dcd_dwc2.c, or access registers directly, or try an stm32. I'm currently reading section 28 of the esp32s2 TRM, just out of curiousity. Most probably I'll just stick with black pill.

@tore-espressif
Copy link
Contributor

Just a few points so we don't get off track:

  1. ESP32-S2 errata: Not a problem here, we don't use DMA mode in TinyUSB
  2. Potential bug in DCD: We can rule this out as Bulk transfers work OK for other classes.

If you need a quick fix, can you add this at the end of your main/CMakeLists.txt?

add_definitions(-DCFG_TUD_VENDOR=1)

If that does not help we will publish a fix soon

@moefear85
Copy link
Author

I tried add_definitions(-DCFG_TUD_VENDOR=1). It had no effect. I already put it in the right place in espressif__esp_tinyusb/include/tusb_config.h with the rest of the class definitions, so this was expected.

If that does not help we will publish a fix soon

tyt. I've compiled a troubleshooting list which I will be going over methodically. Although this is a hobby project, I intend to get to the bottom of this. I came across the debugging fifo read/write USB registers in the TRM. I'll make use of those to double down on proper buffer accesses. Good to know DMA isn't involved, that KISS.

@SlayerPower
Copy link

Operating System
Linux

Board
esp32s2 module

Firmware
esp-idf v5.3.1

What happened ?
I'm trying to send "hello esp" from a custom linux usb driver to an esp32s2 esp-idf application running the tinyusb stack. I enabled "CFG_TUD_VENDOR 1" in tusb_config.h". When I send a 64 byte packet to the esp32s2, the stack detects the packet, but its contents are random/empty.

I added some logging statements here and there in esp-idf & tinyusb, to try to pinpoint the problem. But I can't go deep into the stack, it's beyond my expertise. I used wireshark to verify that a single 64 byte bulk packet is being sent to the esp at endpoint 1 with the text "hello esp!". tinyusb is detecting it, but not providing the actual contents of the packet.

I tried using my own task for tusb_task(), as well as espressif's from sdkconfig, but it makes no difference.

As I hope is clear from the provided logs, dumps, and screenshots, I defined the void tud_vendor_rx_cb(uint8_t itf) function so I get notified every time a packet arrives, so I can print its dump. I print the dump once before calling int read=tud_vendor_read(buffer_in,available); and once after, so it is clear how the buffer changes. Sometimes it remains empty, in other cases it is filled with random non-/readable data. It would seem to me like a stray pointer issue.

It would be nice if you could help me confirm whether I am doing something wrong, or whether the esp32s2 dwc2 doesn't support vendor device class, or if there is perhaps a bug in the stack.

@SlayerPower
Copy link

bro tengo na pregunta, existe algun bug aqui? xd, estoy tratando de hacer un lunar y no puedo :C

@SlayerPower
Copy link

la descripción no parece ajustarse exactamente a lo que estoy enfrentando, a menos que el funcionamiento normal del esp implique accesos de lectura, y que esto esté en conflicto con el USB de una manera que no se soluciona.

@moefear85
Copy link
Author

puedo

google Translation:
Bro, I have a question, is there a bug here? xd, I'm trying to make a mole and I can't :C

what is a mole?

btw... i'm trying this on both stm32f103 and rp2040. but its my first project on rp2040 so im studying the datasheet first. stm32 is running out of stack space so i don't know. stm32f411 are in the mail.

@moefear85
Copy link
Author

I just tried rp2040 using its default tinyusb from its own sdk. exact same problem. the buffer is all 0x00.

@HiFiPhile
Copy link
Collaborator

So with all the devices you tried nothing works ?
Could you share your driver?

@moefear85
Copy link
Author

moefear85 commented Oct 5, 2024

i only tried 2 vendors. the stm stack overflowed. ill check the driver first. maybe what wireshark captures isnt what actually gets sent in all cases, since it is usb.

@moefear85
Copy link
Author

moefear85 commented Oct 5, 2024

its not the driver. the driver works with cdc-acm using unbind method. don't esp & pico use same synopsys hardware? rp2040 is also already riddled with so many usb errata.

@SlayerPower
Copy link

okokokok

@HiFiPhile
Copy link
Collaborator

its not the driver. the driver works with cdc-acm using unbind method. don't esp & pico use same synopsys hardware? rp2040 is also already riddled with so many usb errata.

rp2040 doesn't use dwc2 ip.
https://github.com/hathach/tinyusb/blob/master/src/portable/raspberrypi/rp2040/dcd_rp2040.

Maybe your driver missing something initialization stuff that usb_acm driver already did before it's unbinded.

ill check the driver first. maybe what wireshark captures isnt what actually gets sent in all cases, since it is usb.

That's what I'm doubting. Can be easily verified by my USB analyzer.

@moefear85
Copy link
Author

moefear85 commented Oct 7, 2024

Maybe your driver missing something initialization stuff that usb_acm driver already did before it's unbinded.

maybe you should look at the capture log. if anything is missing compared to usb-cdc, feel free to contribute something useful, or provide a working vendor-class example which is coincidentially missing from esp-idf, pico sdk, & tinyusb examples

@HiFiPhile
Copy link
Collaborator

Here is no place for your entitlement and arrogance.

We offered help FREELY while you talk like a boss and refusing to take suggestions while having few USB knowledge.

Vendor class, dwc2 IP, rp2040 are widely used while your own driver is the remaining factor.

Your are at your own to figure it out then.

Repository owner locked as too heated and limited conversation to collaborators Oct 7, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants