Skip to content

usb_hid OSError on Custom Device #8289

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

Closed
AdityaMitra5102 opened this issue Aug 17, 2023 · 14 comments
Closed

usb_hid OSError on Custom Device #8289

AdityaMitra5102 opened this issue Aug 17, 2023 · 14 comments
Labels
Milestone

Comments

@AdityaMitra5102
Copy link

CircuitPython version

Adafruit CircuitPython 8.2.3 on 2023-08-11; Raspberry Pi Pico with rp2040

Code/REPL

import usb_hid

REPORT_DESCRIPTOR = bytes((
    0x06, 0x00, 0xFF,  # Usage Page (Vendor Defined 0xFF00)
    0x09, 0x01,        # Usage (0x01)
    0xA1, 0x01,        # Collection (Application)
    0x06, 0x00, 0xFF,  #   Usage Page (Vendor Defined 0xFF00)
    0x09, 0x01,        #   Usage (0x01)
    0x75, 0x10,        #   Report Size (16)
    0x95, 0x01,        #   Report Count (1)
    0x91, 0x02,        #   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
    0x06, 0x00, 0xFF,  #   Usage Page (Vendor Defined 0xFF00)
    0x09, 0x02,        #   Usage (0x02)
    0x75, 0x08,        #   Report Size (8)
    0x96, 0xE6, 0x00,  #   Report Count (230)
    0x91, 0x02,        #   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
    0x06, 0x00, 0xFF,  #   Usage Page (Vendor Defined 0xFF00)
    0x09, 0x01,        #   Usage (0x01)
    0x75, 0x10,        #   Report Size (16)
    0x95, 0x01,        #   Report Count (1)
    0x81, 0x02,        #   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x06, 0x00, 0xFF,  #   Usage Page (Vendor Defined 0xFF00)
    0x09, 0x02,        #   Usage (0x02)
    0x75, 0x08,        #   Report Size (8)
    0x96, 0xE6, 0x00,  #   Report Count (230)
    0x81, 0x02,        #   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0xC0,              # End Collection
))

dev = usb_hid.Device(
    report_descriptor=REPORT_DESCRIPTOR,
    usage_page=65280,           #Usage Page (Vendor Defined 0xFF00)
    usage=0x01,                #Usage (0x01)
    report_ids=(0,),           # Descriptor uses no report ID.
    in_report_lengths=(230,),    # This device sends 230 bytes in its report.
    out_report_lengths=(0,),   # It does not receive any reports.
)

usb_hid.enable(
    (dev,)
)
report=bytearray(230)

def send():
    dev.send_report(report)

send()

Behavior

'''
Traceback (most recent call last):
File "boot.py", line 47, in
File "boot.py", line 45, in send
OSError: USB busy
'''

Description

  • Error when trying to send 230 byte reports
  • Trying to emulate vendor specific device
  • Sending blank report for test
  • Got the REPORT_DESCRIPTOR from the device I am trying to emulate
  • It should send the bytes as Raw HID so that I can read the same using tools like hidtrace or pywinusb.

Additional information

Help would be appreciated

@AdityaMitra5102
Copy link
Author

image

THis is the HID Trace of the device I am trying to emulate

@dhalbert
Copy link
Collaborator

The USB device is not brought up until code.py is run. Try putting the actual send

report=bytearray(230)

def send():
    dev.send_report(report)

send()

in your code.py (maybe as part of a test loop, e.g. send each time you get some input()). Note that you must do a hard reset to have boot.py re-executed if it changes.

@dhalbert dhalbert added this to the Support milestone Aug 17, 2023
@AdityaMitra5102
Copy link
Author

AdityaMitra5102 commented Aug 17, 2023

Tried that. This is my code in code.py

import usb_hid

REPORT_DESCRIPTOR = bytes((
    0x06, 0x00, 0xFF,  # Usage Page (Vendor Defined 0xFF00)
    0x09, 0x01,        # Usage (0x01)
    0xA1, 0x01,        # Collection (Application)
    0x06, 0x00, 0xFF,  #   Usage Page (Vendor Defined 0xFF00)
    0x09, 0x01,        #   Usage (0x01)
    0x75, 0x10,        #   Report Size (16)
    0x95, 0x01,        #   Report Count (1)
    0x91, 0x02,        #   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
    0x06, 0x00, 0xFF,  #   Usage Page (Vendor Defined 0xFF00)
    0x09, 0x02,        #   Usage (0x02)
    0x75, 0x08,        #   Report Size (8)
    0x96, 0xE6, 0x00,  #   Report Count (230)
    0x91, 0x02,        #   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
    0x06, 0x00, 0xFF,  #   Usage Page (Vendor Defined 0xFF00)
    0x09, 0x01,        #   Usage (0x01)
    0x75, 0x10,        #   Report Size (16)
    0x95, 0x01,        #   Report Count (1)
    0x81, 0x02,        #   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x06, 0x00, 0xFF,  #   Usage Page (Vendor Defined 0xFF00)
    0x09, 0x02,        #   Usage (0x02)
    0x75, 0x08,        #   Report Size (8)
    0x96, 0xE6, 0x00,  #   Report Count (230)
    0x81, 0x02,        #   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0xC0,              # End Collection
))

dev = usb_hid.Device(
    report_descriptor=REPORT_DESCRIPTOR,
    usage_page=65280,           #Usage Page (Vendor Defined 0xFF00)
    usage=0x01,                #Usage (0x01)
    report_ids=(0,),           # Descriptor uses no report ID.
    in_report_lengths=(230,),    # This device sends 230 bytes in its report.
    out_report_lengths=(0,),   # It does not receive any reports.
)


report=bytearray(230)

report[0]=0x01
report[1]=0x02 #Test Data

def send():
    dev.send_report(report)

while True:
    send()
    time.sleep(1)

Output:
Traceback (most recent call last):
File "", line 49, in
File "", line 46, in send
OSError: USB error

(Using with Thonny IDE)

@dhalbert
Copy link
Collaborator

dhalbert commented Aug 17, 2023

Sorry if was not clear. This code should be in boot.py:

import usb_hid

REPORT_DESCRIPTOR = ...

dev = usb_hid.Device(
    ...
)

usb_hid.enable(
    (dev,)
)

The remaining code that actually does the send should be in code.py. You have to split it into two pieces:

  • The HID device definition goes in boot.py
  • The use goes in code.py

@AdityaMitra5102
Copy link
Author

AdityaMitra5102 commented Aug 17, 2023

Sorry if it is a bad question but how do I get the 'dev' object in code.py if it was defined in boot.py? Or do I run the dev=usb_hid.Device(...) again in code.py?

I am unable to find any proper documentation regarding this actually.

@dhalbert
Copy link
Collaborator

You need to find the device you created in boot.py. There is a convenience routine in the adafruit_hid library to do this: https://github.com/adafruit/Adafruit_CircuitPython_HID/blob/9ec404f8b318af5b4f02855311e5a00233ce29b5/adafruit_hid/__init__.py#L33
Here is an example of its usage in mouse.py:
https://github.com/adafruit/Adafruit_CircuitPython_HID/blob/9ec404f8b318af5b4f02855311e5a00233ce29b5/adafruit_hid/mouse.py#L39

import usb_hid

import adafruit_hid

# 0xff00 == 65280
dev = adafruit_hid.find_device(usb_hid.devices, usage_page=0xff00, usage=0x01)
#...

I agree that the Learn Guide for this should include a code.py example in addition to the boot.py example. I was assuming people would study the existing devices in the https://github.com/adafruit/Adafruit_CircuitPython_HID library.

@AdityaMitra5102
Copy link
Author

AdityaMitra5102 commented Aug 17, 2023

So currently my boot.py reads

import usb_hid

REPORT_DESCRIPTOR = bytes((
    0x06, 0x00, 0xFF,  # Usage Page (Vendor Defined 0xFF00)
    0x09, 0x01,        # Usage (0x01)
    0xA1, 0x01,        # Collection (Application)
    0x06, 0x00, 0xFF,  #   Usage Page (Vendor Defined 0xFF00)
    0x09, 0x01,        #   Usage (0x01)
    0x75, 0x10,        #   Report Size (16)
    0x95, 0x01,        #   Report Count (1)
    0x91, 0x02,        #   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
    0x06, 0x00, 0xFF,  #   Usage Page (Vendor Defined 0xFF00)
    0x09, 0x02,        #   Usage (0x02)
    0x75, 0x08,        #   Report Size (8)
    0x96, 0xE6, 0x00,  #   Report Count (230)
    0x91, 0x02,        #   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
    0x06, 0x00, 0xFF,  #   Usage Page (Vendor Defined 0xFF00)
    0x09, 0x01,        #   Usage (0x01)
    0x75, 0x10,        #   Report Size (16)
    0x95, 0x01,        #   Report Count (1)
    0x81, 0x02,        #   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x06, 0x00, 0xFF,  #   Usage Page (Vendor Defined 0xFF00)
    0x09, 0x02,        #   Usage (0x02)
    0x75, 0x08,        #   Report Size (8)
    0x96, 0xE6, 0x00,  #   Report Count (230)
    0x81, 0x02,        #   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0xC0,              # End Collection
))

dev = usb_hid.Device(
    report_descriptor=REPORT_DESCRIPTOR,
    usage_page=65280,           #Usage Page (Vendor Defined 0xFF00)
    usage=0x01,                #Usage (0x01)
    report_ids=(0,),           # Descriptor uses no report ID.
    in_report_lengths=(230,),    # This device sends 230 bytes in its report.
    out_report_lengths=(0,),   # It does not receive any reports.
)

usb_hid.enable(
    (dev,)
)

And my code.py reads

import usb_hid
import adafruit_hid

dev = adafruit_hid.find_device(usb_hid.devices, usage_page=0xff00, usage=0x01)


report=bytearray(230)

report[0]=0x01
report[1]=0x02 #Test Data

def send():
    dev.send_report(report)

while True:
    send()
    time.sleep(1)

However, I am still running into

Traceback (most recent call last):
  File "<stdin>", line 16, in <module>
  File "<stdin>", line 13, in send
OSError: USB error

I am unable to understand why this is happening. Is RPi Pico capable enough to send report of size 230 bytes?

@AdityaMitra5102
Copy link
Author

image
Ok, so apparently boot.py is working because HID Trace can read the device with proper size. But Sending the data is still failing.

@dhalbert
Copy link
Collaborator

dhalbert commented Aug 17, 2023

A few notes:

The report descriptor you have above has 232 bytes total of "input" report and 232 bytes of "output" report. ((IN and OUT are with respect to the host.) Your screenshot in the previous post shows that. The usage-page and usage values used in find_device() refer to the top-level device, and don't selectively choose what is inside the Collection. But your IN report length is given as 230, not 232, and the OUT report length is given as 0, not 232.

My understanding is that for Full Speed USB HID reports, the limit on the report size is 64 bytes. See for example https://stackoverflow.com/a/60100992/142996. What is the device you are trying to emulate? Is it a "High speed device"? The RP2040 does not support USB High Speed, only Full Speed. CircuitPython only has a few boards with microcontrollers that have High Speed USB interfaces. I am not sure of the state of our High Speed support at the moment, but can check with someone later today.

I have some tests for 64-byte RAW HID here: #7806.

@AdityaMitra5102
Copy link
Author

AdityaMitra5102 commented Aug 17, 2023

Okay. I am very confused, to be honest. I am trying to emulate Baltech Card Reader Gen 2. Specs

@dhalbert
Copy link
Collaborator

How did you get the report descriptor? I'm assuming you used some tool for that. Is there documentation for it from the manufacturer? I don't have an explanation for why it seems to be 232 bytes and normally would be restricted to 64 bytes, since the PDF you linked says it's a Full Speed device.

Do you have an understanding of the device protocol, or are you trying to reverse-engineer it?

I do note that the datasheet says:

USB HID (works with native drivers included in Windows, Mac, Linux and most other operating systems)
USB HID keyboard emulation (requires programming of reader with Stock Configuration)

Could you load an actual device with the "Stock Configuration", see what that is doing, and emulate that instead?

This is now drifting away from a CircuitPython issue, and we might continue this discussion on our Discord, https://adafru.it/discord, or the forums: https://forums.adafruit.com/viewforum.php?f=60

@dhalbert
Copy link
Collaborator

HID report descriptor info, from discord:

image
image

@dhalbert
Copy link
Collaborator

From discord:

... the one highlighted in red is one packet. The next one in blue. I am using Device Monitoring studio

dhalbert pushed a commit to dhalbert/circuitpython that referenced this issue Sep 19, 2023
The delay is 1 ms. It avoids the crashes reported by the
issues adafruit#8289, adafruit#8792 and adafruit#9236 with esp-idf versions >= 4.2, but does
not solve an underlying problem in the esp-idf.
@dhalbert
Copy link
Collaborator

dhalbert commented Mar 8, 2024

Closing for now. Please reopen if you are still having trouble that is CircuitPython-related.

@dhalbert dhalbert closed this as completed Mar 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants