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:
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_VBUS_DRAW=2
CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
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 “sudo”).
Thank you for the reply. Let me focus on the kernel aspect first. I use jetsonhacks to build and copy in the following sequence:
getKernelSource.sh
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).
copyImage.sh
reboot
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”.
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? /lib/modules/$(uname -r)/kernel
…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.
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: CONFIG_LOCALVERSION="-tegra-usb_gadget"
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.
About:
…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 /proc/config.gz.
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:
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.
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
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 lsusb says.
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
modprobe configfs
modprobe libcomposite
cd /sys/kernel/config/usb_gadget
mkdir g1
cd g1
echo "64" > bMaxPacketSize0
echo "0x0200" > bcdUSB # USB2
echo "0x100" > bcdDevice # v1.0.0
echo "0x1d6b" > idVendor # Linux Foundation
echo "0x0104" > idProduct # Multifunction Composite Gadget
mkdir strings/0x409
mkdir configs/c1.1
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
mkdir functions/hid.0
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.