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

[BUG] capture_hid_report fails on cheap ONN Mouse from Walmart using QT Py RP2040 #18

Open
ATMakersBill opened this issue Mar 22, 2022 · 30 comments
Labels
bug Something isn't working

Comments

@ATMakersBill
Copy link

ATMakersBill commented Mar 22, 2022

UPDATE: Thanks to Dan Halbert at Adafruit, I tried testing with not just two mice, but two DIFFERENT mice and the original code worked with the other brand. So, this issue is very different now... it's that Pico-PIO-USB doesn't enumerate or support a very cheap mouse from Walmart. My gaming mouse enumerated and reported fine.

So, we could close this one and open a new issue or update this one - I'll add the new info in a separate comment and we can split if if needed. Thanks


I have wired a USB Female jack via 2x 22Ohm resistors to pins A0 and A1 of a QT Py RP2040 (GPIO29 and GPIO28) respectively. (See Image).
WIN_20220322_17_19_34_Pro

When trying the capture_hid_report example, I get errors when an optical mouse is plugged in. (See attached USB Descriptor from USBView)
opticalmouseDescr.txt
...

I expected to see HID reports printed to the USB console.
...

When run as-is, with only the config.pin_dp set to 28 instead of the default of 0, I got no terminal at all. When placed back in bootloader mode, Putty would fail with "unable to configure USB".

When I commented out the config.alarm_pool line (Line 18 of capture_hid_report) which I thought would put the interrupts in the main core, I got

Root 0 connected
Device 0 Connected
control in[timeout]
Enumeration failed(-2)

When I moved all of the processing to core0 by calling core1_main() directly from main() and moving pio_usb_host_task() to the main loop, I got:

Root 0 connected
Device 0 Connected
control in[complete]
Enumerating 093a:2510, class:0, address:1
control out[complete]
control in[timeout]
Failed to get string descriptor (Manufacture)
control in[timeout]
Failed to get string descriptor (Product)
control in[timeout]
Enumeration failed(-2)
Root 0 disconnected
Disconnect
Disconnect device 1
Root 0 connected
Device 0 Connected
control in[complete]
Enumerating 093a:2510, class:0, address:1
control out[complete]
control in[timeout]
Failed to get string descriptor (Manufacture)
control in[timeout]
Failed to get string descriptor (Product)
control in[timeout]
Enumeration failed(-2)
Root 0 disconnected
Disconnect
Disconnect device 1
Root 0 connected
Device 0 Connected

I realize that this may be my fault, but I am at a loss as to what to try next... :-/

Any ideas?


@ATMakersBill ATMakersBill added the bug Something isn't working label Mar 22, 2022
@ATMakersBill
Copy link
Author

I have now tested with two different brands of mouse. A Magic Eagle "gaming" mouse which works fine and a cheap "ONN USB Corded Mouse" from Walmart which never enumerates. Here is a link to the bad mouse (you can grab one for $8 at Walmart - it says it's a "best seller").

I have attached the descriptors and the wireshark captures for both devices to this comment.. I don't see an obvious difference so it might be at a lower level. However, I'm certainly no expert in USB traces.
ONNUSBCordedDescr.txt
ONNUSBCordedMousePCAP.zip

These are the ones that worked:
magiceagle.pcap.zip
magiceagledescr.txt

I am no longer blocked by this issue, but please let me know if I can help in any way.
Bill

@ATMakersBill ATMakersBill changed the title 🪲[BUG] capture_hid_report fails on QT Py RP2040 [BUG] capture_hid_report fails on QT Py RP2040 Mar 22, 2022
@ATMakersBill ATMakersBill changed the title [BUG] capture_hid_report fails on QT Py RP2040 [BUG] capture_hid_report fails on cheap ONN Mouse from Walmart using QT Py RP2040 Mar 22, 2022
@hathach
Copy link
Contributor

hathach commented Mar 29, 2022

IMHO, example currently only works as proof of concept. Most important thing is having the electrical signal works, above that is simply software protocol stack. Support all mices/keyboards are not easy, even tinyusb with more focused software still haven't supported all devices. I think you would have higher chance to get it work by SET_PROTOCOL to boot mode instead of report mode.

I am working on #14 , that would help to glue the tinyusb software stack with this awesome pio controller, that would thing easier for user. Just subscribed and watched out for PRs.

@mnesarco
Copy link

Hi @ATMakersBill did you get any solution for this? I have the same problem (#19)

@ATMakersBill
Copy link
Author

@hathach I am good with using the TinyUSB interface on top of the PIO stack. It sounds like a great solution - I was just hoping for a proof of concept for my hardware (which I got on the other mouse).

Any idea on a timeframe for #14? Weeks? Months?

@ATMakersBill
Copy link
Author

Hi @ATMakersBill did you get any solution for this? I have the same problem (#19)

I think some of the cheap mice actually show up as hubs. There's good reason for this (they can reset the "mouse" part without losing the USB connection) and I think it might be throwing off the USB stack.

@jfedor2
Copy link

jfedor2 commented Apr 11, 2022

I have tested this library with a few devices and most of them work fine, but I too have encountered some that don't work (same problem: enumeration failed):

Microsoft Basic Optical Mouse v2.0 (Vendor ID: 0x045e, Product ID: 0x00cb)
ISY 4-Port USB 2.0 Hub (Vendor ID: 0x1a40, Product ID: 0x0101)

@ATMakersBill
Copy link
Author

ATMakersBill commented Apr 11, 2022 via email

@jfedor2
Copy link

jfedor2 commented Apr 11, 2022

I am starting to think some of these devices are actually acting as hubs with one device attached (so they can reset without disconnecting perhaps)? Does that make any sense?

I don't think this is the problem, because I have another hub that works with the library.

Also when I connect the problematic mouse to a PC, it doesn't show up as a hub.

@moc32
Copy link

moc32 commented Sep 15, 2022

Hello all,
Thank you for your great work in bringing about this much needed solution on the PICO.
I am testing the library and all the functionalities I need are present. I just have some stability issue on host side while testing the demo examples.
Device enumeration is not working for my logitec and lenovo keyboards but it works for my dell mouse and keyboard. Analyzing the communication over a usb analyzer I can see that in the function pio_usb_bus_receive_packet_and_handshake the handshake is not sent to the devices for which the enumeration fails: see attached screenshot. The signals shown in the oscillo look good.
Do you have any idea of what can go wrong.

image

@pgreenland
Copy link

Hi All,

Amazing concept and great work getting it this far!

Unfortunately I'm seeing this issue too.

Using a similar random USB keyboard, I see lots of disconnections and reconnections, with occasional brief periods of stability. Not a new keyboard yet similar wallmart special, first time I've had an issue with it.

@moc32 's suggestion gave me a good place to start. I'm seeing missing acks too. Adding a dash of debug to the pio_usb_bus_receive_packet_and_handshake function is pointing at the rx pio code experiencing some sort of bit errors.

I'm printing the following inside pio_usb_bus_receive_packet_and_handshake based on good / bad crc (ignore the various CRC prints, I was trying to match them up to that calculated by my logic analyser):

printf("good - %u - %04X - %04X - %04X - data: ", idx, crc_receive_inverse, crc_prev2, crc);
for (int i = 0; i < idx; i++) printf("%02X", pp->usb_rx_buffer[i]);
printf("\n");

Here's the debug output produced:

control out[complete]
good - 12 - C768 - C768 - B001 - data: 804B05010906A10105089738
bad - 12 - 8406 - 26BB - 59F1 - data: 80C31901290315609200F97B
control in[error]

The good packet as seen by the logic analyser:

good packet

The data of which matches the good print statement (if you byte swap the crc).

The bad packet as seen by the logic analyser:

bad packet

Here's the data captured by the pico vs data captured by the logic analyser:

80 C3 19 01 29 03 15 60 92 00 F9 7B

80 C3 19 01 29 03 15 00 25 01 F2 F7

                     ^^ ^^ ^^ ^^ ^^

Seems the rx pio somehow got way otta wack with the bitstream at the bytes indicated by the carot?

I'll start poking around in the pio code, @sekigon-gonnoc any pointers?

Thanks,

Phil

@pgreenland
Copy link

Believe I've found the problem....and it's going to be tricky to solve.

Enabled the debug version of the rx pio code (nice touch). Updated the side sets to drive the debug output high, for one cycle when dm pin was being sampled via the jump instruction.

During a failure I see the following at the start of the packet:

start of packet

The debug pulses indicating the execution of the "jmp pin" instructions.

Nicely centered in the bits.

The pico read 804B12011001000000 before going wrong at 88FC7B, which should have been 081177.

Looking at those bytes near the end of the packet:

end of packet

The sample point appears to be getting closer and closer to the transitions.

Given that there's a two flip flop synchroniser on the PIO inputs, assuming that synchroniser is clocked off the pio clock (the datasheet isn't clear), then the input being processed is almost certainly from the previous bit.

Which would make sense in at least the first error case with 0x08 becoming 0x88 by the last bit getting mis-sampled, with the transition missed and a rouge 1 added.

The pio code could do with resynchronising on each edge. It seems to only synchronise on the first edge at present then assumes a perfect clock from the remote device for the entire 96 bit run. Seems that doesn't go well with old / cheap devices which don't have accurate clocks.

At the same time squeezing that functionality in is going to be tricky.

Possibly moving the bit decoding and unstuffing into the host to free up some instructions. Allowing the input to be re-synchronised on each edge. While adding a counter to take timed samples where there are no edges.

Then its just a case of working out when the packet ends and how long it is / ensuring the final bits in the ISR which may not be a multiple of 8 due to the stuff bits are offloaded to the host.

I'm tempted to have a go....for the challenge.....but I'm also looking at a $3 usb hub chip that'll save me hours of pio tinkering 😅.

@sekigon-gonnoc
Copy link
Owner

Thank you for the detailed report.
Removing bit stuffing handler from the PIO is the hard way....
Let me think of a good way

@ATMakersBill
Copy link
Author

I love that there is a known problem. Our assistive tech mouse and joystick projects would still greatly benefit from this. If there’s anything I can do (including funding to help this) please let me know.

@pgreenland
Copy link

@sekigon-gonnoc I had a go re-working (butchering) the receive pio code. Attempting to resync on the edges, while still handing bit stuffing.

It's nowhere near PR ready but might serve as inspiration for a fix: https://github.com/pgreenland/Pico-PIO-USB/tree/attempt_to_resync_on_edges

Moved the eop sm from pio1 to pio0, freeing up some code space in pio1 for the larger receive routine.

The biggest snag I found was with 8 x oversampling, there's not that much time for additional instructions between bits.

For my low speed device, I've upped the oversampling to 16 x for now, which has it enumerating and working reliably. That same tweak won't be possible with the full speed version though :-(.

Also discovered (yet haven't tested) something that may allow you to share a single rx / eop program, rather than have to keep swapping programs.

The pico has an input invert option between the input pads and peripherals, page 248 of the datasheet (INOVER).

I may be wrong, but the RX code was identical, while only the initial two wait's in the eop code needed to change. This is due to the idle state of the lines with the pullup resistor moving between dm and dp to indicate the speed. You may be able to use a single rx / eop program and just toggle the invert option when updating the clock dividers if full speed is required.

@ATMakersBill if you have a moment, try running the capture_hid_report example on my branch, be interested to see if your walmart mouse behaves any better?

While exploring other options to add additional USB ports to the pico, SPI connected host controllers for example. I came up empty, without adding lots of additional cost to the BOM. Getting this working universally would be awesome. Ideally keeping a state machine and 6 instructions spare, allowing the Pico-W to continue using its wifi (it makes use of the PIO for its SPI comms with the wifi module).

@ATMakersBill
Copy link
Author

@ATMakersBill if you have a moment, try running the capture_hid_report example on my branch, be interested to see if your walmart mouse behaves any better?

I’ll be happy to. Probably Wednesday before I can get hardware and a build env setup. My day job had an aggressive script kiddie corrupt some code and it was my bug that allowed it, so my tomorrow will suck :-/

@sekigon-gonnoc
Copy link
Owner

@pgreenland very interesting work.
Based on your suggestion my idea is like this:

  • Separate edge detector and NRZI decoder into different state machines
  • Synchronize clock on rise edge only
  • Unwind edge detect loop
  • invert both dm/dp pins to merge edge detector and EOP detector

This code is just an idea. Adjusting delays and change host code may be necessary

; Rise edge and EOP detector
; x8 or x10 oversamping
; jmp_pin->dm in_pin->dp
; both gpios are inverted
; 15 instructions
.wrap_target
start:
	wait 0 pin 0                ; Wait for sop bit edge

pin_went_low:
	irq 0						; Trigger NRZI decoder

pin_low:
	jmp pin pin_went_high       ; Jump if the pin is now high
 	jmp pin pin_went_high       ; Jump if the pin is now high
	jmp pin pin_went_high       ; Jump if the pin is now high
	jmp pin pin_went_high       ; Jump if the pin is now high
	jmp pin_went_low

pin_went_high:
pin_still_high:
	irq 0						; Trigger NRZI decoder
	in pin, 1
	mov x, isr
	jmp x-- eop					; Jump to eop if jmp_pin and in_pin are high because both inputs are inverted
	jmp pin pin_still_high
	jmp pin_went_low
eop:
	mov isr, null
	irq wait IRQ_RX_EOP
.wrap

; NRZI decoder
; 17 instructions
start:
	set x, 0
.wrap_target
set_y:
	set y, 5
irq_wait:
	wait 1 irq 0			; wait signal from edge detector
	jmp !y flip			; ignore stuff bit, without error check
	jmp PIN pin_high
pin_low:
	jmp !x K1
K2:
	in x, 1
	jmp y-- irq_wait
K1:
	in null, 1
	jmp flip
	
pin_high:
	jmp !x J1
J2:
	in null, 1
	jmp y-- irq_wait
J1:
	mov x, ~x			; x: 0->~0
	in x, 1
	jmp set_y
flip:
	mov x, ~x
.wrap

@pgreenland
Copy link

All sounds good to me! I'll be ready and waiting to test with my garbage keyboard :-P Let me know if there's anything I can help with.

I hadn't considered only synchronising on one edge, or unrolling the loop - both awesome suggestions. No matter how hard I squeezed I couldn't get everything I tried into 8 instructions between edges.

Disclaimer I haven't tried the input inverting - spotted it as an option in the datasheet after stumbling into a reasonable looking register in a PIO simulator. Checkout https://github.com/soundpaint/rp2040pio used it a few times for different protocols now. Bit of a learning curve but it saves a lot of flashing and poking of the pi. And you can step through instructions with the input waveform. I've got code prepared for USB, if you're interested let me know and I'll drop it in a repo.

@sekigon-gonnoc
Copy link
Owner

@pgreenland I will try the simulator. Could you upload the code prepared for USB?

@pgreenland
Copy link

@sekigon-gonnoc I've dropped the simulation sources in a repo with some notes on how to get it going, along with my attempt at a new rx program: https://github.com/pgreenland/pio_usb_simulation - feel free to lift any useful bits / give me a shout if you get stuck.

@sekigon-gonnoc
Copy link
Owner

@pgreenland @ATMakersBill Could you try this if you have time? #62

@ATMakersBill
Copy link
Author

I am back in my office where I have the cheap mice and the boards. My boards are based on the Adafruit QTPy RP2040 and the Kee Boar (https://www.adafruit.com/product/5302?gclid=CjwKCAiAwc-dBhA7EiwAxPRylG8lcBxP42VvFXJf8sFzszYO6Mio6AguK2F1cWfctbU0CxUxRGKufBoC4kcQAvD_BwE) - they have a pins GP0 and GP1 set as D+/D- and I was able to get the basic example working on the main branch. However, I will have to setup the build env again as that machine has been repurposed so it will take me a bit of time. I hope to get to it this week

Thanks for the progress.

PS: I don't think you're asking me to use the simulator - I've no experience with that.

@ATMakersBill
Copy link
Author

Just an update, I am getting this error when trying to build your branch (I tried with my PICO SDK and having it fetch one):

TinyUSB available at /home/bill/pico/test/Pico-PIO-USB/examples/build/_deps/pico_sdk-src/lib/tinyusb/src/portable/raspberrypi/rp2040; enabling build support for USB.
cyw43-driver available at /mnt/c/pico/test/Pico-PIO-USB/examples/build/_deps/pico_sdk-src/lib/cyw43-driver
lwIP available at /home/bill/pico/test/Pico-PIO-USB/examples/build/_deps/pico_sdk-src/lib/lwip
-- Configuring done
CMake Error at host_hid_to_device_cdc/CMakeLists.txt:2 (add_executable):
  Cannot find source file:

    /home/bill/pico/test/Pico-PIO-USB/examples/build/_deps/pico_sdk-src/lib/tinyusb/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c

  Tried extensions .c .C .c++ .cc .cpp .cxx .cu .m .M .mm .h .hh .h++ .hm
  .hpp .hxx .in .txx


-- Generating done

Any ideas?

@sekigon-gonnoc
Copy link
Owner

Please switch tinyusb to master branch.
or download compiled binaries
https://github.com/sekigon-gonnoc/Pico-PIO-USB/actions/runs/3806062486

@ATMakersBill
Copy link
Author

I can't use the compiled binaries as I use a different D+/D- pin configuration. The changes are trivial (see patch file attached) but I think I need to build.

I have updated the submodule (forgot how to do that for a sec) and have started a build - will crash for the night though.

Thanks

@pgreenland
Copy link

@pgreenland @ATMakersBill Could you try this if you have time? #62

Awesome! I'm a bit slammed this week but will give it a go at the weekend - my little test setup may need a bit of dusting off bit shouldn't take much to fire it back up.

Looks like you tried the simulator? :-P

Will let you know how I get on.

Thanks,
Phil

@ATMakersBill
Copy link
Author

Having trouble getting any output at all... afraid I'll have to brush up on getting output to Putty etc... but I'm working on it

@pgreenland
Copy link

@sekigon-gonnoc working perfectly now, great work!

The USB keyboard that was failing previously now enumerates every time (at least 10 times in a row). No unexpected disconnections and hid data streaming over the serial port when running the capture_hid_report example.

@sekigon-gonnoc
Copy link
Owner

@pgreenland Thank you for your report. The simulator and your test bench were really helpful

@sonik-br
Copy link

I'm also having problems trying to use some old low speed devices like keyboard, mouse, joystick.
New devices works, like a "gaming" keyboard and mouse. Old ones simply does not enumerate.
It's possible to fix this with any external hardware? Like using a usb hub?

@sonik-br
Copy link

@sekigon-gonnoc the problem occurs during FS/LS detection?
If so, forcing the code to only support LS devices will make it work?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

8 participants