I’d like to use the USB Gadget functionality to emulate a keyboard for a host PC. That is, the host PC should see the TX2 as a USB keyboard. I think I got the idea on how to do it based on other posts and manuals (here, here and here) however, I’m facing some difficulties.
- I’ve rebuilt the kernel with the HID USB Gadget driver enabled (image below).
Based on my understanding, I should be able to confirm that the support is enabled by checking "zcat /proc/config.gz | grep ‘USB_GADGET’ “ outputs. However, the config didn’t change:
That is, CONFIG_USB_G_HID didn’t show up. Did I do smth wrong?
Normally, the next step would be to create a device in /sys/kernel/config/usb_gadget/ by making a folder there that would reflect a device. However, I’m getting an error “cannot create directory ‘key_test’: Cannot allocate memory”. I saw others reporting a similar problem for other use cases, and I’m wondering if this is the right way at all, or there is a different sequence of steps to create a USB gadget on a TX2?
There are sample gadget setups in /opt/nvidia/l4t-usb-device-mode, but they appear to be only for a storage device (and a camera?). Can you pls please point me to any examples or manuals that can help me?
I don’t know for certain on the missing “
/proc/config.gz” item, but probably what you examined was correct, but one would have to know if the starting kernel config matched the running system, and then if the build was correct, and since you used “
=y”, whether the kernel was installed correctly. If there is a change in “
uname -r”, then you’d have also had to have installed all modules again in the new module path, but keeping “
uname -r” would avoid that issue. So there could be issues related to install even if the kernel build was correct. Don’t know. Perhaps all that follows is irrelevant because the kernel/feature was not installed as expected. Verifying the kernel started with a matching config, accepted the modified config, was build correctly, that the “
uname -r” is correct, and that modules are found would be the next step.
One does not normally create files in “
/sys”, but gadget API does make some of this possible. Normally, these are “pseudo” files, and not real files which exist only in RAM and are created by kernel drivers as a way for the driver to provide or accept information. Normally (but not in this case), you would find a file there not by creating it directly, but instead by creating a valid configuration wherever your gadget configuration is, and should the configuration succeed, then the file would just appear in “
/sys” to reflect that success (or in this case allow creation). For the case of gadget, the directory being created exists because the driver owning the parent directory allows this (other content is basically data to the driver because the feature in the kernel allows this).
The files in “
/opt/nvidia/” are examples. Note that part of this is configuration settings, e.g., the name of a file for the mass storage device to read. The “stop” and “start” files make good examples. However, the kernel feature would need to be correctly installed, and the user making the changes in “
/sys” would have to be root (with “
Thank you for the reply. Let me focus on the kernel aspect first. I use jetsonhacks to build and copy in the following sequence:
- editConfig.sh → enable USB Gadget Drivers; also append LOCALVERSION so it’s “-tegra-usb_gadget”
- makeKernek.sh: builds with no errors or warnings
- makeModules.sh: build and installs successfully with one warning: “could not open drivers/misc/mods/mods.dtb.S: no such file or directory” (I don’t know if it’s relevant to my problem or not).
- uname -r shows “4.9.253-tegra-usb_gadget” which is great, but
/proc/config.gz still doesn’t have it.
Common sense tells me that either I’m doing smth incorrectly or the jertsonhacks scripts are doing smth wrong. I would appreciate if you share your thoughts.
I don’t use those scripts, I simply download the one provided with the particular L4T release. To see L4T release use “
head -n 1 /etc/nv_tegra_release”.
JetPack/SDK Manager is only a front end, it is the L4T which is the actual Ubuntu operating system. A given JetPack/SDKM is paired with a given L4T release. These are the URLs for listing releases based on either L4T version or JetPack/SDKM version:
It is likely the script downloads the exact same source, though I can’t confirm. I give the above URLs if people want to download the source manually for the exact release.
The part I don’t know about (and I have not dissected the scripts) is what the beginning configuration is. This might be where things went wrong. Also, I see “
uname -r” has changed, so this means you are guaranteed your kernel was installed, but it doesn’t say if modules were installed. Do you see modules in the subdirectories of this?
…if not, then build/install steps were incomplete. Are those files in place?
Should the above set of modules be in place, then we would want to concentrate on how you edited the configuration. For example, using an ordinary text editor, versus using
menuconfig or some other configuration editor.
Thanks. Here we go:
head -n 1 /etc/nv_tegra_release results in:
R32 (release), REVISION: 6.1, GCID: 27863751, BOARD: t186ref, EABI: aarch64, DATE: Mon Jul 26 19:36:31 UTC 2021
The copy script downloads the following L4T:
NVIDIA Jetson TX2
Jetpack 4.6 [L4T 32.6.1]
Kernel Release: 4.9
And places the source to /usr/src/kernel/kernel-4.9
I checked the .config there and it appears to contain changes that I want:
cat .config | grep LOCALVERSION results in:
cat .config | grep USB_G_HID shows:
Also, as you suggested, I checked /lib/modules/$(uname -r)/kernel and the folder looks identical for the base 4.9.253-tegra and my version 4.9.253-tegra-usb_gadget:
Any suggestions on where I should go from here? Should I drop the jetsonhacks scripts and use the vanilla build instructions? If yes, where can I get a manual on how to build 32.6.1?
I hesitate to say to drop the jetsonhacks scripts, but I know more about manual configuration, so it is easier to discuss if not using those scripts.
…if you were to compile your kernel right there in the source, then this would be correct. However, if you use the “
O=/some/where/else/for/temp/output”, then this would not apply and would be incorrect. I rarely suggest compiling directly from a root owned directory (in this case “
/usr/src”) since one must also run as root. This does work, but might not be the best practice.
It does seem modules were installed correctly, so that isn’t in question.
I’m looking at an older TX2 right now, so it isn’t exactly the same as yours (the kernel version is older, so I don’t expect the same options to be available in the
config.gz). What I don’t see is a “
CONFIG_USB_G_HID” entry. Perhaps this is new and your release has this, but not my older release (
4.9.140). Do you know how the
CONFIG_USB_G_HID became part of the configuration? For example, was this already there (even if it wasn’t “
=y”), and then configured to enable? If you run this command, does this show up (to indicate the kernel has the feature in the running system):
zcat /proc/config.gz | grep 'CONFIG_USB_G_HID'
If someone just added that to the config, and it wasn’t part of the actual kernel source, then the feature would be missing in the final
You are actually right! CONFIG_USB_G_HID is not in the original config and I believe the config GUI editor added it there. Now it makes sense why it has no effect.
What are the correct flags to enable to unlock the Gadget keyboard and mouse USB functionality?
The kernel code itself has a document directory, and the “gadget” system is documented there for that particular kernel release. For example, if the kernel source is at “
/usr/src/sources/kernel/kernel-4.9”, then there is a subdirectory “
Documentation/usb/” and “
Documentation/ABI/testing/”. You might find something in “
testing/” related to fields to fill in, and the “
usb/” part has “
usb/gadget_hid.txt”, which has more details. The API might change, and so this is where you’d find the definitive information.
Keyboard and mouse are “HID” devices, and “HID” implies standard drivers rather than custom drivers, which is why the gadget framework can be of use. I have not followed the changes in gadget over various releases, but the docs for your particular kernel release would be the place to start, specifically using the HID documents.
Thanks for bearing with me. Unfortunately, the documentation you mention doesn’t discuss the kernel config flags (unless I need to read between the lines). I think I found the flag I need from this post: CONFIG_USB_CONFIGFS_F_HID. I still tend to modify flags in GUI since I’m not 100% confident in what I’m doing. So I change two flags: device drivers → USB support → USB gadget support → USB functions configurable through configfs CONFIG_USB_CONFIGFS_F_HID (I turned it on) → HID function (I turned it on since it looks related)
After rebooting, I use the script you mention in another post to stop the device mode: “/opt/nvidia/l4t-usb-device-mode/nvidia nv-l4t-usb-device-mode-stop.sh”.
After that, I attempt to create a keyboard gadget device by using the command sequence that is based on this post, except for excluding rndis.usb0 related stuff (I assume I don’t need it), but with an addition of the keyboard protocol in report_desc:
echo 0x1d6b > idVendor
echo 0x0104 > idProduct
echo 0x0100 > bcdDevice
echo 0x0200 > bcdUSB
echo 0xEF > bDeviceClass
echo 0x02 > bDeviceSubClass
echo 0x01 > bDeviceProtocol
mkdir -p strings/0x409
echo "deadbeef00115599" > strings/0x409/serialnumber
echo "Something" > strings/0x409/manufacturer
echo "Something" > strings/0x409/product
mkdir -p functions/hid.usb0
echo 1 > functions/hid.usb0/protocol
echo 1 > functions/hid.usb0/subclass
echo 8 > functions/hid.usb0/report_length
echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.usb0/report_desc
mkdir -p functions/acm.usb0
mkdir -p functions/acm.usb1
mkdir -p functions/acm.usb2
mkdir -p functions/acm.usb3
mkdir -p configs/c.1
echo 250 > configs/c.1/MaxPower
ln -s functions/rndis.usb0 configs/c.1/
ln -s functions/hid.usb0 configs/c.1/
ln -s functions/acm.usb0 configs/c.1/
ln -s functions/acm.usb1 configs/c.1/
ln -s functions/acm.usb2 configs/c.1/
ln -s functions/acm.usb3 configs/c.1/
echo 1 > os_desc/use
echo 0xcd > os_desc/b_vendor_code
echo MSFT100 > os_desc/qw_sign
ln -s configs/c.1 os_desc
udevadm settle -t 5 || :
ls /sys/class/udc/ > UDC
When I connect the TX2 to my host Ubuntu PC, I notice that the following usb device showing up:
Bus 001 Device 005: ID 1d6b:0104 Linux Foundation Multifunction Composite Gadget
I assume I did at least smth right if I see the above.
Now I use the kernel documentation to test the device. I compile the code mentioned in the documentation (I compile it identical to this repo) and run
hid_gadget_test /dev/hidg0 keyboard expecting to be able to control the host PC through the TX2 keyboard, but the only thing I get is a standard printout on TX2 of what keyboard keys I can use and the following two lines:
recv report: 00
recv report: 00
Pressing any keys on the TX2’s keyboard doesn’t have any effect on the host PC.
How can I figure out what I’m missing?
Yes, that looks like progress. A composite device is like multiple device on a single USB cable (one can have multiple hardware devices talk over USB, or even the same hardware using different software). You might want to see what the verbose listing is:
sudo lsusb -vvv -d 1d6b:0104
I couldn’t say what is needed to complete this, but RidgeRun commonly writes about or provides hardware for use with Jetsons, and I noticed they have this article:
That article assumes you are using one of their custom boards, which is what the “RidgeRun SDK” is about, but mostly you could skip the customization parts related to their carrier boards and probably find some useful information in that article. Other than that, I’d say to see what the fully verbose
It finally works. In the end I used this manual as a reference but I think my previous attempts were also correct.
In the end I enabled only two kernel flags:
Device Drivers -> USB support -> USB Gadget Support -> USB Gadget Drivers-> USB functions configurable through configfs -> HID function and
Device Drivers -> USB support -> USB Gadget Support -> USB Gadget Drivers-> USB gadget drivers -> HID gadget
Here is the final version of my keyboard device script (someone may find it useful):
/opt/nvidia/l4t-usb-device-mode/nv-l4t-usb-device-mode-stop.sh # stops nvidia gadgets
echo "64" > bMaxPacketSize0
echo "0x0200" > bcdUSB # USB2
echo "0x100" > bcdDevice # v1.0.0
echo "0x1d6b" > idVendor # Linux Foundation
echo "0x0104" > idProduct # Multifunction Composite Gadget
echo "Logitech" > strings/0x409/manufacturer
echo "G915 keyboard" > strings/0x409/product
echo 6g65796d616d6570390 > strings/0x409/serialnumber
mkdir configs/c1.1/strings/0x409/ -p
echo "Config 1: ECM network" > configs/c1.1/strings/0x409/configuration
echo 120 > configs/c1.1/MaxPower
# emulate hid keyboard
echo 1 > functions/hid.0/protocol # set the HID protocol
echo 1 > functions/hid.0/subclass # set the device subclass
echo 8 > functions/hid.0/report_length # set the byte length of HID reports
echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.0/report_desc
ln -s functions/hid.0 configs/c1.1
# enable the USB device contoller
ls /sys/class/udc > UDC
chmod 777 /dev/hidg0
The main problem I had was not in the implementation but in testing: this method didn’t work correctly for my case, so I resorted to issuing keystrokes directly through the terminal. For instance,
echo -ne "\0\0\x17\xb\x4\x11\xe\x2c" > /dev/hidg0 && echo -ne "\0\0\0\0\x1c\x12\x18\0" > /dev/hidg0 && echo -ne "\0\0\0\0\0\0\0\0" > /dev/hidg0 results in typing “thank you” on the host.
This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.