Coder Social home page Coder Social logo

raw-gadget's People

Contributors

aristochen avatar nickray avatar rgerganov avatar xairy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

raw-gadget's Issues

I have a card reader, use AristoChen/usb-proxy proxy it to windows pc failed

1. usb card reader info

root@orangepipc:~/usb-proxy# lsusb
Bus 008 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 006 Device 004: ID 2ce3:9563 Generic EMV Smartcard Reader
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 009 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
root@orangepipc:~/usb-proxy# lsusb -v -d 2ce3:9563

Bus 006 Device 004: ID 2ce3:9563 Generic EMV Smartcard Reader
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.01
  bDeviceClass            0
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0         8
  idVendor           0x2ce3
  idProduct          0x9563
  bcdDevice            1.20
  iManufacturer           1 Generic
  iProduct                2 EMV Smartcard Reader
  iSerial                 0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x005d
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0xa0
      (Bus Powered)
      Remote Wakeup
    MaxPower               50mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           3
      bInterfaceClass        11 Chip/SmartCard
      bInterfaceSubClass      0
      bInterfaceProtocol      0
      iInterface              0
      ChipCard Interface Descriptor:
        bLength                54
        bDescriptorType        33
        bcdCCID              1.10  (Warning: Only accurate for version 1.0)
        nMaxSlotIndex           0
        bVoltageSupport         7  5.0V 3.0V 1.8V
        dwProtocols             3  T=0 T=1
        dwDefaultClock       4000
        dwMaxiumumClock     12000
        bNumClockSupported      3
        dwDataRate          10753 bps
        dwMaxDataRate      688172 bps
        bNumDataRatesSupp.     95
        dwMaxIFSD             254
        dwSyncProtocols  00000007  2-wire 3-wire I2C
        dwMechanical     00000000
        dwFeatures       000404BE
          Auto configuration based on ATR
          Auto activation on insert
          Auto voltage selection
          Auto clock change
          Auto baud rate change
          Auto PPS made by CCID
          Auto IFSD exchange
          Short and extended APDU level exchange
        dwMaxCCIDMsgLen       272
        bClassGetResponse    echo
        bClassEnvelope       echo
        wlcdLayout           none
        bPINSupport             0
        bMaxCCIDBusySlots       1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0004  1x 4 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0010  1x 16 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0010  1x 16 bytes
        bInterval               0
Binary Object Store Descriptor:
  bLength                 5
  bDescriptorType        15
  wTotalLength       0x000c
  bNumDeviceCaps          1
  USB 2.0 Extension Device Capability:
    bLength                 7
    bDescriptorType        16
    bDevCapabilityType      2
    bmAttributes   0x00000000
      (Missing must-be-set LPM bit!)
Device Status:     0x0000
  (Bus Powered)

2. when proxy windows pc can't capture pcap use wireshark

3. usb-proxy console output like loop back to get device, when get config response write to raw_gadget

root@orangepipc:~/usb-proxy# ./usbbr --device=musb-hdrc.4.auto --driver=musb-hdrc -vv
Device is: musb-hdrc.4.auto
Driver is: musb-hdrc
vendor_id is: -1
product_id is: -1
Device opened successfully
Setup USB config successfully
Start hotplug_monitor thread, thread id(5635)
Start for EP0, thread id(5622)
ep #0:
  name: ep1in
  addr: 1
  type: iso blk int
  dir : in  ___
  maxpacket_limit: 512
  max_streams: 0
ep #1:
  name: ep1out
  addr: 1
  type: iso blk int
  dir : ___ out
  maxpacket_limit: 512
  max_streams: 0
ep #2:
  name: ep2in
  addr: 2
  type: iso blk int
  dir : in  ___
  maxpacket_limit: 512
  max_streams: 0
ep #3:
  name: ep2out
  addr: 2
  type: iso blk int
  dir : ___ out
  maxpacket_limit: 512
  max_streams: 0
ep #4:
  name: ep3in
  addr: 3
  type: iso blk int
  dir : in  ___
  maxpacket_limit: 512
  max_streams: 0
ep #5:
  name: ep3out
  addr: 3
  type: iso blk int
  dir : ___ out
  maxpacket_limit: 512
  max_streams: 0
ep #6:
  name: ep4in
  addr: 4
  type: iso blk int
  dir : in  ___
  maxpacket_limit: 512
  max_streams: 0
ep #7:
  name: ep4out
  addr: 4
  type: iso blk int
  dir : ___ out
  maxpacket_limit: 512
  max_streams: 0
event: connect, length: 0
libusb: error [udev_hotplug_event] ignoring udev action change
libusb: error [usbi_get_context] API misuse! Using non-default context as implicit default.
libusb: error [udev_hotplug_event] ignoring udev action change
event: control, length: 8
  bRequestType: 0x80  (IN), bRequest: 0x06, wValue: 0x0100, wIndex: 0x0000, wLength: 64
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  desc = USB_DT_DEVICE
Control transfer succeed
Sending data to EP0(control_in): 12 01 01 02 00 00 00 40 e3 2c 63 95 20 01 01 02 00 01
ep0: transferred 18 bytes (in)
event: control, length: 8
  bRequestType: 0x80  (IN), bRequest: 0x06, wValue: 0x0100, wIndex: 0x0000, wLength: 18
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  desc = USB_DT_DEVICE
Control transfer succeed
Sending data to EP0(control_in): 12 01 01 02 00 00 00 40 e3 2c 63 95 20 01 01 02 00 01
ep0: transferred 18 bytes (in)
event: control, length: 8
  bRequestType: 0x80  (IN), bRequest: 0x06, wValue: 0x0200, wIndex: 0x0000, wLength: 255
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  desc = USB_DT_CONFIG
Control transfer succeed
Sending data to EP0(control_in): 09 02 5d 00 01 01 00 a0 19 09 04 00 00 03 0b 00 00 00 36 21 10 01 00 07 03 00 00 00 a0 0f 00 00 e0 2e 00 00 03 01 2a 00 00 2c 80 0a 00 5f fe 00 00 00 07 00 00 00 00 00 00 00 be 04 04 00 10 01 00 00 ff ff 00 00 00 01 07 05 81 03 04 00 01 07 05 02 02 10 00 00 07 05 83 02 10 00 00
ep0: transferred 93 bytes (in)
event: control, length: 8
  bRequestType: 0x80  (IN), bRequest: 0x06, wValue: 0x0100, wIndex: 0x0000, wLength: 64
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  desc = USB_DT_DEVICE
Control transfer succeed
Sending data to EP0(control_in): 12 01 01 02 00 00 00 40 e3 2c 63 95 20 01 01 02 00 01
ep0: transferred 18 bytes (in)
event: control, length: 8
  bRequestType: 0x80  (IN), bRequest: 0x06, wValue: 0x0100, wIndex: 0x0000, wLength: 18
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  desc = USB_DT_DEVICE
Control transfer succeed
Sending data to EP0(control_in): 12 01 01 02 00 00 00 40 e3 2c 63 95 20 01 01 02 00 01
ep0: transferred 18 bytes (in)
event: control, length: 8
  bRequestType: 0x80  (IN), bRequest: 0x06, wValue: 0x0200, wIndex: 0x0000, wLength: 255
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  desc = USB_DT_CONFIG
Control transfer succeed
Sending data to EP0(control_in): 09 02 5d 00 01 01 00 a0 19 09 04 00 00 03 0b 00 00 00 36 21 10 01 00 07 03 00 00 00 a0 0f 00 00 e0 2e 00 00 03 01 2a 00 00 2c 80 0a 00 5f fe 00 00 00 07 00 00 00 00 00 00 00 be 04 04 00 10 01 00 00 ff ff 00 00 00 01 07 05 81 03 04 00 01 07 05 02 02 10 00 00 07 05 83 02 10 00 00
ep0: transferred 93 bytes (in)
event: control, length: 8
  bRequestType: 0x80  (IN), bRequest: 0x06, wValue: 0x0100, wIndex: 0x0000, wLength: 64
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  desc = USB_DT_DEVICE
Control transfer succeed
Sending data to EP0(control_in): 12 01 01 02 00 00 00 40 e3 2c 63 95 20 01 01 02 00 01
ep0: transferred 18 bytes (in)
event: control, length: 8
  bRequestType: 0x80  (IN), bRequest: 0x06, wValue: 0x0100, wIndex: 0x0000, wLength: 18
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  desc = USB_DT_DEVICE
Control transfer succeed
Sending data to EP0(control_in): 12 01 01 02 00 00 00 40 e3 2c 63 95 20 01 01 02 00 01
ep0: transferred 18 bytes (in)
event: control, length: 8
  bRequestType: 0x80  (IN), bRequest: 0x06, wValue: 0x0200, wIndex: 0x0000, wLength: 255
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  desc = USB_DT_CONFIG
Control transfer succeed
Sending data to EP0(control_in): 09 02 5d 00 01 01 00 a0 19 09 04 00 00 03 0b 00 00 00 36 21 10 01 00 07 03 00 00 00 a0 0f 00 00 e0 2e 00 00 03 01 2a 00 00 2c 80 0a 00 5f fe 00 00 00 07 00 00 00 00 00 00 00 be 04 04 00 10 01 00 00 ff ff 00 00 00 01 07 05 81 03 04 00 01 07 05 02 02 10 00 00 07 05 83 02 10 00 00
ep0: transferred 93 bytes (in)
event: control, length: 8
  bRequestType: 0x80  (IN), bRequest: 0x06, wValue: 0x0100, wIndex: 0x0000, wLength: 64
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  desc = USB_DT_DEVICE
Control transfer succeed
Sending data to EP0(control_in): 12 01 01 02 00 00 00 40 e3 2c 63 95 20 01 01 02 00 01
ep0: transferred 18 bytes (in)
event: control, length: 8
  bRequestType: 0x80  (IN), bRequest: 0x06, wValue: 0x0100, wIndex: 0x0000, wLength: 18
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  desc = USB_DT_DEVICE
Control transfer succeed
Sending data to EP0(control_in): 12 01 01 02 00 00 00 40 e3 2c 63 95 20 01 01 02 00 01
ep0: transferred 18 bytes (in)
event: control, length: 8
  bRequestType: 0x80  (IN), bRequest: 0x06, wValue: 0x0200, wIndex: 0x0000, wLength: 255
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  desc = USB_DT_CONFIG
Control transfer succeed
Sending data to EP0(control_in): 09 02 5d 00 01 01 00 a0 19 09 04 00 00 03 0b 00 00 00 36 21 10 01 00 07 03 00 00 00 a0 0f 00 00 e0 2e 00 00 03 01 2a 00 00 2c 80 0a 00 5f fe 00 00 00 07 00 00 00 00 00 00 00 be 04 04 00 10 01 00 00 ff ff 00 00 00 01 07 05 81 03 04 00 01 07 05 02 02 10 00 00 07 05 83 02 10 00 00

##4. This two file is pcap capture from orangepc and windows
orangepc pcap
windows pcap

5. It may not raw_gadget issues, but i can not find how to debug it, if you could help me, I would be very grateful. Thank you.

PCAP replay script

Just a wild idea to write a program that will parse and replay a PCAP file with Raw Gadget.

`make: *** [Makefile:7: default] Error 2`

Hi,

I'm having the hardest time to get my Raspi Zero to show up as a USB.
I found a couple of blogs pointing towards this github so I figured I'd try to install dummy_hcd since it is in fact missing in my system.
I had some cpp issues with fallthrough, so now I've updated to the latest version of CPP.

If I now run make like this to make sure it uses the correct cpp version:

# #!/bin/bash

dir=$(pwd);
install_dir="/home/●●●●●/install/";
mod_dir="/lib/modules/$(uname -r)";
cpp_dir="/opt/gcc-10.1.0/bin";

echo -e "\nBaking dummy_hcd";
sudo ln -s "$mod_dir" "./build";
cd "${install_dir}/git/raw-gadget/dummy_hcd";
sudo make CC="${cpp_dir}/gcc-10.1" CPP="${cpp_dir}/g++-10.1" CXX="${cpp_dir}/g++-10.1" LD="${cpp_dir}/g++-10.1";
cd "${mod_dir}/build";
sudo "./insmod.sh";

cd "$dir";

I still get the following error:

make -C /lib/modules/4.19.97+/build M=/usr/src/linux-headers-4.19.97+/dummy_hcd SUBDIRS=/usr/src/linux-headers-4.19.97+/dummy_hcd modules
make[1]: Entering directory '/usr/src/linux-headers-4.19.97+'
  CC [M]  /usr/src/linux-headers-4.19.97+/dummy_hcd/dummy_hcd.o
/usr/src/linux-headers-4.19.97+/dummy_hcd/dummy_hcd.c: In function ‘dummy_enable’:
/usr/src/linux-headers-4.19.97+/dummy_hcd/dummy_hcd.c:556:4: error: ‘fallthrough’ undeclared (first use in this function)
  556 |    fallthrough;
      |    ^~~~~~~~~~~
/usr/src/linux-headers-4.19.97+/dummy_hcd/dummy_hcd.c:556:4: note: each undeclared identifier is reported only once for each function it appears in
/usr/src/linux-headers-4.19.97+/dummy_hcd/dummy_hcd.c: In function ‘dummy_timer’:
/usr/src/linux-headers-4.19.97+/dummy_hcd/dummy_hcd.c:1971:4: error: ‘fallthrough’ undeclared (first use in this function)
 1971 |    fallthrough;
      |    ^~~~~~~~~~~
/usr/src/linux-headers-4.19.97+/dummy_hcd/dummy_hcd.c: In function ‘dummy_hub_control’:
/usr/src/linux-headers-4.19.97+/dummy_hcd/dummy_hcd.c:2148:4: error: ‘fallthrough’ undeclared (first use in this function)
 2148 |    fallthrough;
      |    ^~~~~~~~~~~
make[2]: *** [scripts/Makefile.build:310: /usr/src/linux-headers-4.19.97+/dummy_hcd/dummy_hcd.o] Error 1
make[1]: *** [Makefile:1522: _module_/usr/src/linux-headers-4.19.97+/dummy_hcd] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-4.19.97+'
make: *** [Makefile:7: default] Error 2

Do you have any suggestions on what I can try to debug this some more?

Dmesg does not define Manufacturer, Product, SerialNumber

Hi everyone!
I built the project under 6.2.0-rc3-meson64. When launching the keyboard from the example, lsusb sees both the vendor and the product name just fine. But dmesg does not determine the vendor, and the product and serial number is defined as "x". Maybe someone can suggest a way to solve the problem?

Experiment with USB/IP as a loopback HCD/UDC

USB/IP provides a usbip-vudc module, which acts as a virtual UDC. In combination with usbip-vhci, this might prove as an alternative to Dummy HCD/UDC that supports isochronous transfers (which Raw Gadget does not really support yet).

Can "iInterface" be set to an arbitrary string from this?

One of the issues I recently had was that it's not actually possible for any of the current Linux usb gadgets to set "iInterface". This is wired to a specific value based upon the gadget driver and is not exposed in configFS. Because of its commonality across all the function drivers, this is a problematic amount of work to fix.

Can this raw-gadget be used to side step that issue?

Thanks.

Add endpoint FIFO ioctls

Is it possible to add an ioctl to get status/clear fifo for an endpoint? Essentially passing these functions through

int usb_ep_fifo_status(struct usb_ep *ep);
void usb_ep_fifo_flush(struct usb_ep *ep);

Common C header for examples

Each example has copy-pasted definitions of wrappers and helpers for Raw Gadget ioctls. Provide a common header file with these definitions.

Fail to compile raw-gadget after execute update.sh

Hi,

the following error message will appear if trying to compile raw-gadget kernel module after update.sh is executed

$ make
make -C /lib/modules/5.10.60/build M=/root/raw-gadget/raw_gadget SUBDIRS=/root/raw-gadget/raw_gadget modules
make[1]: Entering directory '/usr/src/linux-source'
  CC [M]  /root/raw-gadget/raw_gadget/raw_gadget.o
/root/raw-gadget/raw_gadget/raw_gadget.c: In function ‘raw_ioctl_run’:
/root/raw-gadget/raw_gadget/raw_gadget.c:546:8: error: implicit declaration of function ‘usb_gadget_register_driver’; did you mean ‘usb_gadget_unregister_driver’? [-Werror=implicit-function-declaration]
  546 |  ret = usb_gadget_register_driver(&dev->driver);
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~~
      |        usb_gadget_unregister_driver
cc1: some warnings being treated as errors
make[2]: *** [scripts/Makefile.build:280: /root/raw-gadget/raw_gadget/raw_gadget.o] Error 1
make[1]: *** [Makefile:1824: /root/raw-gadget/raw_gadget] Error 2
make[1]: Leaving directory '/usr/src/linux-source'
make: *** [Makefile:7: default] Error 2

looks like this patch is causing the issue, usb_gadget_probe_driver() was renamed to usb_gadget_register_driver(), the following patch works fine with my SBC with 5.10.60 kernel version.

diff --git a/raw_gadget/update.sh b/raw_gadget/update.sh
index 6ffcb66..c363e12 100755
--- a/raw_gadget/update.sh
+++ b/raw_gadget/update.sh
@@ -9,4 +9,12 @@ BRANCH=usb-next
 wget $REPO/drivers/usb/gadget/legacy/raw_gadget.c?h=$BRANCH -O raw_gadget.c
 wget $REPO/include/uapi/linux/usb/raw_gadget.h?h=$BRANCH -O raw_gadget.h

+
+HEADER_FILE=/lib/modules/"$(uname -r)"/build/include/linux/usb/gadget.h
+if [[ $(grep -c "usb_gadget_probe_driver(" < "$HEADER_FILE") -ne 0 ]]
+then
+    echo "Replacing from usb_gadget_register_driver() to usb_gadget_probe_driver()"
+    sed -i 's/usb_gadget_register_driver/usb_gadget_probe_driver/g' raw_gadget.c
+fi
+
 git apply ./include.patch

failed to start g_mass_storage

Hi,

When I make this folder I got this message from dmesg screen and I did not find a solution.

udc dummy_udc.0: failed to start g_mass_storage: -2
Module has invalid ELF structures

Fix dwc2 issuing disconnect instead of reset

Not a bug in Raw Gadget but still related to its usage.

dwc2 calls the gadget driver's ->disconnect() callback when the port to which the device is connected gets reset by
the host (via SET_FEATURE/PORT_RESET). It should call ->reset() instead.

Asked kernel maintainers about getting this fixed, see here.

Test Raw Gadget on OpenStick

Check whether Raw Gadget works on OpenStick and add instructions for setting it up (see #51).

OpenStick is a USB LTE modem that can be reflashed with a custom Linux-based OS. See the related links.

Logitech webcam can not proxy with AristoChen/usb-proxy

hi,@xairy,
I test the Logitech webcam, that can not proxy with AristoChen/usb-proxy, because the webcam have 4 interface and each interface has multi altsettings, and some altsettings has no endpoints, then I start usb-proxy, when I click windows 11 webcam preview , the windows driver will send set altsetting control packet to webcam, and then usb-proxy will terminate_eps and restart process thread process_eps, then the raw-gadget ioctl enable returnioctl(USB_RAW_IOCTL_EP_ENABLE): Device or resource busy
https://github.com/AristoChen/usb-proxy/blob/c681f1238371fae331482f7d300a4652586d0dd3/proxy.cpp#L474-L482
this line cause the issues
https://github.com/AristoChen/usb-proxy/blob/c681f1238371fae331482f7d300a4652586d0dd3/proxy.cpp#L267
the log is like this, first not start any thread for this zero endpoint altsetting, then terminate any thing and start thread for altsetting has endpoint when the windows driver send control message change altsetting

Activating 1 endpoints on interface 0
int_in: addr = 3, ep = #4
Creating thread for EP83
Start reading thread for EP83, thread id(1849)
process_eps done
Start writing thread for EP83, thread id(1850)
Activating 0 endpoints on interface 1
process_eps done
Activating 0 endpoints on interface 2
process_eps done
Activating 0 endpoints on interface 3
process_eps done

ep0: transferred 26 bytes (out)
event: control, length: 8
  bRequestType: 0x01 (OUT), bRequest: 0x0b, wValue: 0x000a, wIndex: 0x0001, wLength: 0
  type = USB_TYPE_STANDARD
  req = USB_REQ_SET_INTERFACE
Changing interface/altsetting
Activating 1 endpoints on interface 1
ioctl(USB_RAW_IOCTL_EP_ENABLE): Device or resource busy

I also change the code bypass the zero endpoint altsetting, then first start thread will failed, the issues is same, ioctl(USB_RAW_IOCTL_EP_ENABLE): Device or resource busy

Creating thread for EP83
Start reading thread for EP83, thread id(4029)
process_eps done
Start writing thread for EP83, thread id(4030)
Activating 1 endpoints on interface 1
ioctl(USB_RAW_IOCTL_EP_ENABLE): Device or resource busy

The device is c922 Pro Stream Webcam
The windows 11 driver is Logitech 2021/4/27 1.4.40.0
The usb-proxy run on orangepi pc use the driver is musb-hdrc and device is musb-hdrc.4.auto
See attachment for details
lsusb-output.txt
usb-proxy-console-log.txt

Proxy-based Raw Gadget tests

Proxying USB devices proves to be an effective way to test Raw Gadget features.

A few ideas for testing Raw Gadget via proxying:

  • Provide a simple in-repo proxy that supports all of the features Raw Gadget currently exposes. This proxy can evolve alongside with the new Raw Gadget features added to the dev branch. External proxies can rely on this implementation as a guideline or directly reuse it.

  • Implement automatic virtual testing of Raw Gadget based on proxying the existing functions of the composite framework via Dummy HCD/UDC and USB/IP.

  • Document instructions for manual testing of Raw Gadget with various physical devices, hardware UDCs, and both Linux and Windows hosts.

Also see the TODOs in the existing Raw Gadget testing tooling.

Error redirection a usb device to a qemu virtual machine

The error is reproduced for both the master and dev branches, and for the module from the dev branch it is not possible to run the printer and keyboard example application, but a proxy device is successfully created using the usb_proxy application.

master:
$ sudo ./keyboard
...
ioctl(USB_RAW_IOCTL_EP_WRITE): Cannot send after transport endpoint shutdown

$ sudo ./printer
...
ioctl(USB_RAW_IOCTL_EP_READ): Cannot send after transport endpoint shutdown
ioctl(USB_RAW_IOCTL_EP_WRITE): Cannot send after transport endpoint shutdown

usbredir (example printer):
$ sudo usbredirect --device 0525:a4a8 --as 127.0.0.1:4000 --verbose 5
(usbredirect:12580): usbredirect-ERROR **: 20:22:46.937: usbredirhost: error resetting device: LIBUSB_ERROR_NOT_FOUND
Ловушка трассировки/останова

=====================================================================

dev:
$ sudo ./keyboard

event: connect, length: 0

ep #0:
name: ep1in-bulk
addr: 1
type: ___ blk ___
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #1:
name: ep2out-bulk
addr: 2
type: ___ blk ___
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #2:
name: ep5in-int
addr: 5
type: ___ ___ int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #3:
name: ep6in-bulk
addr: 6
type: ___ blk ___
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #4:
name: ep7out-bulk
addr: 7
type: ___ blk ___
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #5:
name: ep10in-int
addr: 10
type: ___ ___ int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #6:
name: ep11in-bulk
addr: 11
type: ___ blk ___
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #7:
name: ep12out-bulk
addr: 12
type: ___ blk ___
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #8:
name: ep15in-int
addr: 15
type: ___ ___ int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #9:
name: ep1out-bulk
addr: 1
type: ___ blk ___
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #10:
name: ep2in-bulk
addr: 2
type: ___ blk ___
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #11:
name: ep-aout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #12:
name: ep-bin
addr: 255
type: ___ blk int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #13:
name: ep-cout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #14:
name: ep-dout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #15:
name: ep-ein
addr: 255
type: ___ blk int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #16:
name: ep-fout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #17:
name: ep-gin
addr: 255
type: ___ blk int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #18:
name: ep-hout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #19:
name: ep-iout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #20:
name: ep-jin
addr: 255
type: ___ blk int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #21:
name: ep-kout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #22:
name: ep-lin
addr: 255
type: ___ blk int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #23:
name: ep-mout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
int_in: addr = 5

event: 6 (unknown), length: 0

fail: unknown event

$ sudo ./printer

event: connect, length: 0

ep #0:
name: ep1in-bulk
addr: 1
type: ___ blk ___
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #1:
name: ep2out-bulk
addr: 2
type: ___ blk ___
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #2:
name: ep5in-int
addr: 5
type: ___ ___ int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #3:
name: ep6in-bulk
addr: 6
type: ___ blk ___
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #4:
name: ep7out-bulk
addr: 7
type: ___ blk ___
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #5:
name: ep10in-int
addr: 10
type: ___ ___ int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #6:
name: ep11in-bulk
addr: 11
type: ___ blk ___
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #7:
name: ep12out-bulk
addr: 12
type: ___ blk ___
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #8:
name: ep15in-int
addr: 15
type: ___ ___ int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #9:
name: ep1out-bulk
addr: 1
type: ___ blk ___
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #10:
name: ep2in-bulk
addr: 2
type: ___ blk ___
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #11:
name: ep-aout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #12:
name: ep-bin
addr: 255
type: ___ blk int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #13:
name: ep-cout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #14:
name: ep-dout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #15:
name: ep-ein
addr: 255
type: ___ blk int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #16:
name: ep-fout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #17:
name: ep-gin
addr: 255
type: ___ blk int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #18:
name: ep-hout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #19:
name: ep-iout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #20:
name: ep-jin
addr: 255
type: ___ blk int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #21:
name: ep-kout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
ep #22:
name: ep-lin
addr: 255
type: ___ blk int
dir : in ___
maxpacket_limit: 65535
max_streams: 16
ep #23:
name: ep-mout
addr: 255
type: ___ blk int
dir : ___ out
maxpacket_limit: 65535
max_streams: 16
bulk_out: addr = 2
bulk_in: addr = 1

event: 6 (unknown), length: 0

fail: unknown event

usb-proxy:

$ lsusb
...
Bus 002 Device 004: ID 0bda:0129 Realtek Semiconductor Corp. RTS5129 Card Reader Controller
...
$ sudo ./usb-proxy --vendor_id=0bda --product_id=0129
...
ep0: transferred 0 bytes (out)
EP1(bulk_out): read 16 bytes from host
EP82(bulk_in): wrote 4 bytes to host
EP1(bulk_out): read 16 bytes from host
EP82(bulk_in): wrote 4 bytes to host
EP1(bulk_out): read 16 bytes from host
EP82(bulk_in): wrote 4 bytes to host
EP1(bulk_out): read 24 bytes from host
EP1(bulk_out): read 32 bytes from host
EP1(bulk_out): read 12 bytes from host
EP1(bulk_out): read 24 bytes from host

new proxy-usb device:
$ lsusb
Bus 001 Device 003: ID 0bda:0129 Realtek Semiconductor Corp. RTS5129 Card Reader Controller
...

redir with virt-manager:
usb_proxy redir_to_qemu err

(continue) $ sudo ./usb-proxy --vendor_id=0bda --product_id=0129
...
ioctl(USB_RAW_IOCTL_EP_READ): Cannot send after transport endpoint shutdown
ioctl(USB_RAW_IOCTL_EP_WRITE): Cannot send after transport endpoint shutdown

raw gadget can be use to right mass storage device

Hi

I am exploring the raw gadget driver for masss storage with dummy_hcd driver.

I am getting the raw data in user space for mass storage with dummy_hcd. I want to pass that data to target pc using OTG gadget driver.

is there any way to use these raw usb as bridge between them?

and can we read/wright directly to pendrive or sd card with raw driver?
if you have any example for these can you please point me. any help will be appreciable.

Thanks

RPi 4 test result

Hi

I tested raw-gadget with RPi 4, and when the test case 13(bulk, ep halt set/clear) was executed, there will be an error, sometimes it is ioctl(USB_RAW_IOCTL_EP_WRITE): Operation now in progress, and sometimes it is ioctl(USB_RAW_IOCTL_EP_READ): Operation now in progress

seems that there is something interrupting the down_interruptible() function in line 94 of raw-gadget.c, which cause it return -EINTR

then I edited the gadget.c like this, simply just retry if error occured.

diff --git a/tests/gadget.c b/tests/gadget.c
index 449cbba..779eb50 100644
--- a/tests/gadget.c
+++ b/tests/gadget.c
@@ -170,18 +170,20 @@ int usb_raw_ep_enable(int fd, struct usb_endpoint_descriptor *desc) {

 int usb_raw_ep_read(int fd, struct usb_raw_ep_io *io) {
        int rv = ioctl(fd, USB_RAW_IOCTL_EP_READ, io);
-       if (rv < 0) {
+       while (rv < 0) {
                perror("ioctl(USB_RAW_IOCTL_EP_READ)");
-               exit(EXIT_FAILURE);
+               rv = ioctl(fd, USB_RAW_IOCTL_EP_READ, io);
+               //exit(EXIT_FAILURE);
        }
        return rv;
 }

 int usb_raw_ep_write(int fd, struct usb_raw_ep_io *io) {
        int rv = ioctl(fd, USB_RAW_IOCTL_EP_WRITE, io);
-       if (rv < 0) {
+       while (rv < 0) {
                perror("ioctl(USB_RAW_IOCTL_EP_WRITE)");
-               exit(EXIT_FAILURE);
+               rv = ioctl(fd, USB_RAW_IOCTL_EP_WRITE, io);
+               //exit(EXIT_FAILURE);
        }
        return rv;
 }

RPi 4 is able to pass the run_test.py after this modification

The question is: should I just add the test result for RPi 4 that tested with the above modification? or should I also modify the ioctl mechanism in gadget.c like the above?

Clean up request flags

During endpoint operations, Raw Gadget allows providing a USB_RAW_IO_FLAGS_ZERO flag, which sets the zero flag on the submitted USB request.

We need to:

  1. Figure out if the USB_RAW_IO_FLAGS_ZERO is useful being exposed to userspace and remove it if not. For reference: how zero is used in GadgetFS.

  2. Possibly add other request flags supported by the gadget subsystem. For reference: is_last in f_tcm.c, no_interrupt in uvc_video.c.

ioctl(USB_RAW_IOCTL_RUN): No such device

Здравствуйте.
Подскажите, пожалуйста, в чём может быть проблема?
Запускаю Ваш пример https://github.com/xairy/raw-gadget/blob/master/examples/keyboard.c, но операция ioctl(fd, USB_RAW_IOCTL_RUN, 0) возвращает -1, в консоль выводится сообщение ioctl(USB_RAW_IOCTL_RUN): No such device.
Спасибо.

Hello.
Can you tell me what the problem might be?
Running your example https://github.com/xairy/raw-gadget/blob/master/examples/keyboard.c, but the ioctl(fd, USB_RAW_IOCTL_RUN, 0) operation returns -1, and the message ioctl(USB_RAW_IOCTL_RUN): No such deviceis output to the console.
Thanks.

$ uname -a
Linux ants-H110M-S2H 5.3.0-46-generic #38~18.04.1-Ubuntu SMP Tue Mar 31 04:17:56 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$ lsmod | grep raw_gadget
raw_gadget             24576  0
udc_core               57344  1 raw_gadget
$ lsb_release -a
No LSB modules are available.
Distributor ID:	LinuxMint
Description:	Linux Mint 19.3 Tricia
Release:	19.3
Codename:	tricia

ioctl_ep_read does not return length of data read

ioctl_ep_read does not update the length field.

It only copies back to user space the data buffer and not the header with the length field

        ret = copy_to_user((void __user *)(value + sizeof(io)), data, length);

Same for ioctl_ep0_read

Timeout for EVENT_FETCH

I have a thread for ep0_loop and another for ep_int_in_loop, apparently I can't write when EVENT_FETCH is blocking, is there a way to timeout the fetch, so I can write something in between?

EDIT: I figure I could implement a timeout manually, but I'm stuck at cancelling the ioctl call ="(

Dmesg does not define Manufacturer, Product, SerialNumber

Hi everyone!
I built the project under 6.2.0-rc3-meson64. When launching the keyboard from the example, lsusb sees both the vendor and the product name just fine. But dmesg does not determine the vendor, and the product and serial number is defined as "x". Maybe someone can suggest a way to solve the problem?

raw-gadget for linux-at91 kernel

Hi,

I'm trying to create a usb device gadget with the Microchip linux-at91 kernel. I want to use it with a physicall usb controller and have replaced the driver_name and device_name to atmel_usba_udc and 500000.gadget respectively. I am not sure if I have the correct driver_name but I found it in /sys/class/udc/500000.gadget/device/uvent displaying DRIVER=atmel_usba_udc.

I am running the keyboard example code which works well with the dummy_hcd module. However, when I try to use it with the microchip usb controller, I get the following logging message:

event: connect, length: 0
event: control, length: 8
  bRequestType: 0x80 (IN), bRequest: 0x6, wValue: 0x100, wIndex: 0x0, wLength: 64
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  descriptor = USB_DT_DEVICE
event: control, length: 8
  bRequestType: 0x80 (IN), bRequest: 0x6, wValue: 0x100, wIndex: 0x0, wLength: 18
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  descriptor = USB_DT_DEVICE
ioctl(USB_RAW_IOCTL_EP0_WRITE): Connection reset by peer

which indicates a write failure with a USB_REQ_GET_DESCRIPTOR request with type USB_DT_DEVICE.

Support isochronous transfers

Even though Raw Gadget does allow enabling isochronous endpoints, those most likely do not work properly. Implementing proper support for isochronous transfers requires deep investigation of what kind of Raw Gadget changes are required.

A non-exhaustive list of potentially required changes:

  • Support non-blocking I/O (tracked in #59);
  • Expose frame_number for completed requests.

This issue is also mentioned in the kernel documentation.

host to device transfer size

Hi, I've tested on Raspberry PI 4 and BeagleBone Black (using the usb-proxy project) and on both there seems to be an issue with data from host to device.
It seems that when we get data from the host, it could contain more than just one packet (up to info.eps[i].limits.maxpacket_limit) which aside from the weird behavior on the actual device, it may also get lost when sent back to the device.

Any chance this can be tuned to only process one message at a time?

On Raspberry PI 4 the behavior is worse as it transfers 1024 bytes at a time most of the time and sometimes it gets blocked for a second with no transfer at all but the Raspberry PI is somewhat known to exhibit USB issues (I see 11 packets of 1024 bytes transferred in a timeframe of 2 seconds and no packets are going the other way which blocks the use of the device).

The transfers from device to host don't show any issue and they respect the expected message size.

Thanks in advance for any feedback

Logitech wireless mouse AristoChen/usb-proxy proxy pass failed

hi,I test Logitech wireless mouse which has two interface on oranglepc with armbian linux kernel 5.15.93, use https://github.com/AristoChen/usb-proxy,when SET_IDLE request to interface 1 raw_gadget return stalling,and then the next GET DESCRIPTOR Request HID Report also failed,I try to debug this,it like
when the GET DESCRIPTOR Request HID Report on interface 0 complete,the musb-hdrc driver reset the raw_gagdet device,
usb-proxy output

event: control, length: 8
  bRequestType: 0x21 (OUT), bRequest: 0x0a, wValue: 0x0000, wIndex: 0x0000, wLength: 0
  type = USB_TYPE_CLASS
  req = unknown = 0xa
ep0: transferred 0 bytes (out)
event: control, length: 8
  bRequestType: 0x81  (IN), bRequest: 0x06, wValue: 0x2200, wIndex: 0x0000, wLength: 123
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  desc = unknown = 0x22
Sending data to EP0(control_in): 05 01 09 06 a1 01 05 07 19 e0 29 e7 15 00 25 01 75 01 95 08 81 02 81 03 95 05 05 08 19 01 29 05 91 02 95 01 75 03 91 01 95 06 75 08 15 00 26 a4 00 05 07 19 00 2a a4 00 81 00 c0
ep0: transferred 59 bytes (in)
event: control, length: 8
  bRequestType: 0x21 (OUT), bRequest: 0x0a, wValue: 0x0000, **wIndex: 0x0001**, wLength: 0
  type = USB_TYPE_CLASS
  req = unknown = 0xa
**ep0: stalling**
event: control, length: 8
  bRequestType: 0x81  (IN), bRequest: 0x06, wValue: 0x2200, **wIndex: 0x0001**, wLength: 241
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  desc = unknown = 0x22
Sending data to EP0(control_in): 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 10 15 00 25 01 95 10 75 01 81 02 05 01 16 01 f8 26 ff 07 75 0c 95 02 09 30 09 31 81 06 15 81 25 7f 75 08 95 01 09 38 81 06 05 0c 0a 38 02 95 01 81 06 c0 c0 05 0c 09 01 a1 01 85 03 75 10 95 02 15 01 26 8c 02 19 01 2a 8c 02 81 00 c0 05 01 09 80 a1 01 85 04 75 02 95 01 15 01 25 03 09 82 09 81 09 83 81 60 75 06 81 03 c0 06 00 ff 09 01 a1 01 85 10 75 08 95 06 15 00 26 ff 00 09 01 81 00 09 01 91 00 c0 06 00 ff 09 02 a1 01 85 11 75 08 95 13 15 00 26 ff 00 09 02 81 00 09 02 91 00 c0
**ioctl(USB_RAW_IOCTL_EP0_WRITE): Device or resource busy**

dmesg

[ 1718.228210] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G        WC  E     5.15.93-sunxi #23.02.2
[ 1718.228238] Hardware name: Allwinner sun8i Family
[ 1718.228257] [<c010cd21>] (unwind_backtrace) from [<c01095f5>] (show_stack+0x11/0x14)
[ 1718.228300] [<c01095f5>] (show_stack) from [<c09e3d59>] (dump_stack_lvl+0x2b/0x34)
[ 1718.228347] [<c09e3d59>] (dump_stack_lvl) from [<c011c4c9>] (__warn+0xad/0xb0)
[ 1718.228392] [<c011c4c9>] (__warn) from [<c09dda4b>] (warn_slowpath_fmt+0x43/0x7c)
[ 1718.228433] [<c09dda4b>] (warn_slowpath_fmt) from [<c0798c77>] (usb_gadget_udc_reset+0xf/0x28)
[ 1718.228477] [<c0798c77>] (usb_gadget_udc_reset) from [<c0797ddb>] (musb_g_reset+0x15f/0x168)
[ 1718.228520] [<c0797ddb>] (musb_g_reset) from [<c07903c5>] (musb_interrupt+0x6f1/0xa64)
[ 1718.228559] [<c07903c5>] (musb_interrupt) from [<bf817721>] (sunxi_musb_interrupt+0x59/0xd0 [sunxi])
[ 1718.228608] [<bf817721>] (sunxi_musb_interrupt [sunxi]) from [<c01685c1>] (__handle_irq_event_percpu+0x41/0x12c)
[ 1718.228663] [<c01685c1>] (__handle_irq_event_percpu) from [<c01686cf>] (handle_irq_event_percpu+0x23/0x4c)
[ 1718.228710] [<c01686cf>] (handle_irq_event_percpu) from [<c0168731>] (handle_irq_event+0x39/0x50)
[ 1718.228755] [<c0168731>] (handle_irq_event) from [<c016bb6f>] (handle_fasteoi_irq+0x67/0xbc)
[ 1718.228799] [<c016bb6f>] (handle_fasteoi_irq) from [<c01680af>] (handle_domain_irq+0x43/0x58)
[ 1718.228839] [<c01680af>] (handle_domain_irq) from [<c05cca3b>] (gic_handle_irq+0x63/0x74)
[ 1718.228882] [<c05cca3b>] (gic_handle_irq) from [<c0100b97>] (__irq_svc+0x57/0x80)
[ 1718.228917] Exception stack(0xc0f01f00 to 0xc0f01f48)
[ 1718.228945] 1f00: 002d9c22 00000000 00000001 c0115601 ffffe000 00000000 c0f05068 00000001
[ 1718.228971] 1f20: c0f04fc8 00000000 c0e9aab0 00000000 c0f05130 c0f01f50 c0106dc7 c0106dc8
[ 1718.228988] 1f40: 600f0033 ffffffff
[ 1718.229003] [<c0100b97>] (__irq_svc) from [<c0106dc8>] (arch_cpu_idle+0x28/0x2c)
[ 1718.229044] [<c0106dc8>] (arch_cpu_idle) from [<c09ef27d>] (default_idle_call+0x29/0x8c)
[ 1718.229084] [<c09ef27d>] (default_idle_call) from [<c0146811>] (do_idle+0x189/0x1cc)
[ 1718.229120] [<c0146811>] (do_idle) from [<c0146ab5>] (cpu_startup_entry+0x19/0x1c)
[ 1718.229155] [<c0146ab5>] (cpu_startup_entry) from [<c0e00f5d>] (start_kernel+0x59f/0x5c6)
[ 1718.229197] ---[ end trace 5cfb5255e5748863 ]---
[ 1718.391880] raw-gadget.0 gadget: fail, wrong direction, dev->ep0_in_pending is 0, dev->ep0_out_pending is 0
[ 1718.393715] raw-gadget.0 gadget: gadget reset
[ 1718.393747] ------------[ cut here ]------------
[ 1718.393757] WARNING: CPU: 0 PID: 0 at drivers/usb/gadget/legacy/raw_gadget.c:388 usb_gadget_udc_reset+0xf/0x28
[ 1718.393800] Modules linked in: raw_gadget(E) hid_logitech_hidpp joydev input_leds hid_logitech_dj rfkill sunrpc lz4hc lz4 sun9i_hdmi_audio sunxi_cir snd_soc_hdmi_codec sun4i_gpadc_iio industrialio sun8i_thermal sunxi_cedrus(C) v4l2_mem2mem videobuf2_dma_contig videobuf2_memops videobuf2_v4l2 videobuf2_common evdev uio_pdrv_genirq uio cpufreq_dt zram sch_fq_codel ramoops pstore_blk reed_solomon pstore_zone ip_tables x_tables autofs4 sy8106a_regulator lima dw_hdmi_i2s_audio dw_hdmi_cec gpu_sched sunxi phy_generic gpio_keys display_connector
[ 1718.394110] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G        WC  E     5.15.93-sunxi #23.02.2
[ 1718.394130] Hardware name: Allwinner sun8i Family
[ 1718.394150] [<c010cd21>] (unwind_backtrace) from [<c01095f5>] (show_stack+0x11/0x14)
[ 1718.394187] [<c01095f5>] (show_stack) from [<c09e3d59>] (dump_stack_lvl+0x2b/0x34)
[ 1718.394224] [<c09e3d59>] (dump_stack_lvl) from [<c011c4c9>] (__warn+0xad/0xb0)
[ 1718.394260] [<c011c4c9>] (__warn) from [<c09dda4b>] (warn_slowpath_fmt+0x43/0x7c)
[ 1718.394291] [<c09dda4b>] (warn_slowpath_fmt) from [<c0798c77>] (usb_gadget_udc_reset+0xf/0x28)
[ 1718.394326] [<c0798c77>] (usb_gadget_udc_reset) from [<c0797ddb>] (musb_g_reset+0x15f/0x168)
[ 1718.394359] [<c0797ddb>] (musb_g_reset) from [<c07903c5>] (musb_interrupt+0x6f1/0xa64)
[ 1718.394389] [<c07903c5>] (musb_interrupt) from [<bf817721>] (sunxi_musb_interrupt+0x59/0xd0 [sunxi])
[ 1718.394424] raw-gadget.0 gadget: gadget disconnected
[ 1718.394444] ------------[ cut here ]------------
[ 1718.394432] [<bf817721>] (sunxi_musb_interrupt [sunxi]) from [<c01685c1>] (__handle_irq_event_percpu+0x41/0x12c)
[ 1718.394452] WARNING: CPU: 2 PID: 1624 at drivers/usb/gadget/legacy/raw_gadget.c:373 usb_gadget_disconnect+0x87/0x90
[ 1718.394485] Modules linked in:
[ 1718.394475] [<c01685c1>] (__handle_irq_event_percpu) from [<c01686cf>] (handle_irq_event_percpu+0x23/0x4c)
[ 1718.394495]  raw_gadget(E) hid_logitech_hidpp joydev input_leds hid_logitech_dj
[ 1718.394511] [<c01686cf>] (handle_irq_event_percpu) from [<c0168731>] (handle_irq_event+0x39/0x50)
[ 1718.394531]  rfkill sunrpc lz4hc lz4 sun9i_hdmi_audio
[ 1718.394546] [<c0168731>] (handle_irq_event) from [<c016bb6f>] (handle_fasteoi_irq+0x67/0xbc)
[ 1718.394563]  sunxi_cir snd_soc_hdmi_codec sun4i_gpadc_iio industrialio sun8i_thermal
[ 1718.394581] [<c016bb6f>] (handle_fasteoi_irq) from [<c01680af>] (handle_domain_irq+0x43/0x58)
[ 1718.394599]  sunxi_cedrus(C) v4l2_mem2mem videobuf2_dma_contig videobuf2_memops
[ 1718.394612] [<c01680af>] (handle_domain_irq) from [<c05cca3b>] (gic_handle_irq+0x63/0x74)
[ 1718.394626]  videobuf2_v4l2
[ 1718.394634]  videobuf2_common evdev uio_pdrv_genirq uio
[ 1718.394651] [<c05cca3b>] (gic_handle_irq) from [<c0100b97>] (__irq_svc+0x57/0x80)
[ 1718.394663]  cpufreq_dt zram sch_fq_codel
[ 1718.394678] Exception stack(0xc0f01f00 to 0xc0f01f48)
[ 1718.394683]  ramoops pstore_blk reed_solomon
[ 1718.394703] 1f00: 002d9e78 00000000 00000001 c0115601 ffffe000 00000000 c0f05068 00000001
[ 1718.394724] 1f20: c0f04fc8 00000000 c0e9aab0 00000000 c0f03d00 c0f01f50 c0106dc7 c0106dc8
[ 1718.394735]  pstore_zone
[ 1718.394738] 1f40: 600b0033 ffffffff
[ 1718.394749]  ip_tables x_tables
[ 1718.394749] [<c0100b97>] (__irq_svc) from [<c0106dc8>] (arch_cpu_idle+0x28/0x2c)
[ 1718.394763]  autofs4 sy8106a_regulator lima dw_hdmi_i2s_audio
[ 1718.394779] [<c0106dc8>] (arch_cpu_idle) from [<c09ef27d>] (default_idle_call+0x29/0x8c)
[ 1718.394792]  dw_hdmi_cec gpu_sched sunxi phy_generic gpio_keys
[ 1718.394813] [<c09ef27d>] (default_idle_call) from [<c0146811>] (do_idle+0x189/0x1cc)
[ 1718.394826]  display_connector
[ 1718.394840] [<c0146811>] (do_idle) from [<c0146ab5>] (cpu_startup_entry+0x19/0x1c)
[ 1718.394868] [<c0146ab5>] (cpu_startup_entry) from [<c0e00f5d>] (start_kernel+0x59f/0x5c6)
[ 1718.394901] ---[ end trace 5cfb5255e5748864 ]---
[ 1718.394902] CPU: 2 PID: 1624 Comm: usb-proxy Tainted: G        WC  E     5.15.93-sunxi #23.02.2
[ 1718.394923] Hardware name: Allwinner sun8i Family
[ 1718.394935] [<c010cd21>] (unwind_backtrace) from [<c01095f5>] (show_stack+0x11/0x14)
[ 1718.394969] [<c01095f5>] (show_stack) from [<c09e3d59>] (dump_stack_lvl+0x2b/0x34)
[ 1718.395002] [<c09e3d59>] (dump_stack_lvl) from [<c011c4c9>] (__warn+0xad/0xb0)
[ 1718.395036] [<c011c4c9>] (__warn) from [<c09dda4b>] (warn_slowpath_fmt+0x43/0x7c)
[ 1718.395066] [<c09dda4b>] (warn_slowpath_fmt) from [<c0798b27>] (usb_gadget_disconnect+0x87/0x90)
[ 1718.395102] [<c0798b27>] (usb_gadget_disconnect) from [<c0799791>] (usb_gadget_remove_driver+0x21/0x90)
[ 1718.395138] [<c0799791>] (usb_gadget_remove_driver) from [<c0799907>] (usb_gadget_unregister_driver+0x73/0x9c)
[ 1718.395174] [<c0799907>] (usb_gadget_unregister_driver) from [<bf970aeb>] (raw_release+0x3f/0xdc [raw_gadget])
[ 1718.395229] [<bf970aeb>] (raw_release [raw_gadget]) from [<c0296993>] (__fput+0x4f/0x1b0)
[ 1718.395272] [<c0296993>] (__fput) from [<c0134785>] (task_work_run+0x7d/0xa0)
[ 1718.395304] [<c0134785>] (task_work_run) from [<c0120825>] (do_exit+0x359/0x86c)
[ 1718.395333] [<c0120825>] (do_exit) from [<c0120deb>] (do_group_exit+0x2f/0x84)
[ 1718.395359] [<c0120deb>] (do_group_exit) from [<c012a29d>] (get_signal+0x159/0x7c4)
[ 1718.395388] [<c012a29d>] (get_signal) from [<c010919d>] (do_work_pending+0x369/0x454)
[ 1718.395415] [<c010919d>] (do_work_pending) from [<c01000c7>] (slow_work_pending+0x9/0x16)
[ 1718.395440] Exception stack(0xc1635fb0 to 0xc1635ff8)
[ 1718.395458] 5fa0:                                     00000000 00000000 b639dc30 b639dc28
[ 1718.395478] 5fc0: 00000000 b639dc80 00000000 00000109 b639e380 00000001 00000000 00000000
[ 1718.395495] 5fe0: 00000109 b639dc18 b6c53a87 b6bedb04 80000030 00000000
[ 1718.395508] ---[ end trace 5cfb5255e5748865 ]---

Return other errno in raw_ioctl_ep_enable()

Hi,

Orange Pi PC have endpoints from ep1-ep4 for both in and out direction, and if we try to use raw_ioctl_ep_enable() for ep5in, it will return -EBUSY here

ret = -EBUSY;

I guess it will be more intuitive if we return some other errno(such as -EINVAL)? Cuz -EBUSY sounds like EP5in is not available now, but might be available in the future

Here is the patch

diff --git a/raw_gadget/raw_gadget.c b/raw_gadget/raw_gadget.c
index 1378d6c..d5acc67 100644
--- a/raw_gadget/raw_gadget.c
+++ b/raw_gadget/raw_gadget.c
@@ -758,6 +758,7 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
        unsigned long flags;
        struct usb_endpoint_descriptor *desc;
        struct raw_ep *ep;
+       bool ep_num_matched = false;

        desc = memdup_user((void __user *)value, sizeof(*desc));
        if (IS_ERR(desc))
@@ -792,6 +793,7 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
                if (ep->addr != usb_endpoint_num(desc) &&
                                ep->addr != USB_RAW_EP_ADDR_ANY)
                        continue;
+               ep_num_matched = true;
                if (!usb_gadget_ep_match_desc(dev->gadget, ep->ep, desc, NULL))
                        continue;
                ep->ep->desc = desc;
@@ -815,6 +817,12 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
                goto out_unlock;
        }

+       if (!ep_num_matched) {
+               dev_dbg(&dev->gadget->dev, "fail, no proper ep address available\n");
+               ret = -EINVAL;
+               goto out_free;
+       }
+
        dev_dbg(&dev->gadget->dev, "fail, no gadget endpoints available\n");
        ret = -EBUSY;

SuperSpeed device emulation with EC3380-AB doesn't work

Device:

$ sudo ./keyboard 
event: connect, length: 0
event: control, length: 8
  bRequestType: 0x80 (IN), bRequest: 0x6, wValue: 0x100, wIndex: 0x0, wLength: 8
  type = USB_TYPE_STANDARD
  req = USB_REQ_GET_DESCRIPTOR
  descriptor = USB_DT_DEVICE
ioctl(USB_RAW_IOCTL_EP0_WRITE): Protocol error

Host:

Apr  9 19:03:57 host kernel: [ 2858.561124] usb 4-2.4.4: new SuperSpeed USB device number 7 using xhci_hcd
Apr  9 19:04:02 host kernel: [ 2863.596761] usb 4-2.4.4: device descriptor read/8, error -110
Apr  9 19:04:03 host kernel: [ 2863.704532] usb 4-2.4.4: new SuperSpeed USB device number 7 using xhci_hcd

Support O_NONBLOCK I/O

Right now, every Raw Gadget endpoint read/write ioctl submits a USB request and waits until its completion. This is done deliberately to assist with coverage-guided fuzzing by having a single syscall fully process a single USB request.

However, for other use cases, we might want to have a non-blocking operation mode, where Raw Gadget would not wait until the completion of each USB request. The original blocking mode must still be kept in the implementation.

This issue is also mentioned in the kernel documentation.

can not receive URB_INTERRUPT OUT data from pc to hid device

hi,@xairy I have a atm cardreader,which is a hid device, and I use usb-proxy proxy to windows 10 , windows 10 need send URB_INTERRUPT OUT to atm cardreader, to drive motor rotation,but the endpoint 0x02 can not received any data,ep0 and
0x81 work good,it may usb_raw_ep_read(fd, (struct usb_raw_ep_io *)&io); can not work with URB_INTERRUPT OUT? I test on
raspberrypi4 and orangepi pc. thank you very much.

how to send configuration descriptor in 2 parts

first of all thanks for sharing your amazing work.

I'm trying to send a configuration descriptor which has a size of 59 bytes to a device which has bMaxPacketSize0 of 32 bytes.

if i try to send the full 59 bytes i get this

Sending data to EP0(control_in): 09 02 3b 00 02 01 00 a0 32 09 04 00 00 01 03 01 01 00 09 21 01 01 00 01 22 4d 00 07 05 81 03 20 00 08 09 04 01 00 01 03 01 02 00 09 21 01 01 00 01 22 74 00 07 05 82 03 20 00 01
ioctl(USB_RAW_IOCTL_EP0_WRITE): Cannot send after transport endpoint shutdown

whats the right way to send this descriptor to that device is this possible using raw gadget or where can i get info about this.

Assorted raw_gadget.c clean-ups

A couple of clean-ups to investigate:

  • Set ep->dev on ep allocation.
  • Don't pass ep0_status and ep_status through dev, get from req instead.

I don't remember why I didn't address those when upstreaming Raw Gadget.

Manage power on suspend/resume/disconnect in examples

USB specifications demand specific behavior with regards to the USB power management when the device is suspended, resumed, or disconnected. Raw Gadget examples should follow these requirements. The Composite Gadget framework implementation can be used as a reference: suspend, resume, disconnect.

Implementing this requires exposing more Gadget API functions via Raw Gadget ioctls, see #40.

missing -EBUSY/-EINTR inside raw_process_ep_io and raw_process_ep0_io

Hi.
After implementing my proxy( https://github.com/patryk4815/usb-proxy ) I tried to debug why "sometime" my code fails with proxying mass storage.
It was really "random" (only occurs when transferring really large files), so after hours of debugging my code, it looks like the error is your module :(

ftrace from kernel:

       usb_proxy-5634    [000] ..... 35373.583990: usb_ep_queue+0x4/0xc8 <-raw_process_ep_io+0x164/0x4d0 [raw_gadget]
       usb_proxy-5634    [001] ..... 35373.584716: usb_ep_queue+0x4/0xc8 <-raw_process_ep_io+0x164/0x4d0 [raw_gadget]
       usb_proxy-5634    [002] ..... 35373.585358: usb_ep_queue+0x4/0xc8 <-raw_process_ep_io+0x164/0x4d0 [raw_gadget]
       usb_proxy-5634    [000] ..... 35373.585993: usb_ep_queue+0x4/0xc8 <-raw_process_ep_io+0x164/0x4d0 [raw_gadget]
       usb_proxy-5634    [000] ..... 35373.586636: usb_ep_queue+0x4/0xc8 <-raw_process_ep_io+0x164/0x4d0 [raw_gadget]
       usb_proxy-5634    [002] ..... 35373.587320: usb_ep_queue+0x4/0xc8 <-raw_process_ep_io+0x164/0x4d0 [raw_gadget]
       usb_proxy-5634    [002] ..... 35373.587994: usb_ep_queue+0x4/0xc8 <-raw_process_ep_io+0x164/0x4d0 [raw_gadget]
       usb_proxy-5634    [003] ..... 35373.588683: usb_ep_queue+0x4/0xc8 <-raw_process_ep_io+0x164/0x4d0 [raw_gadget]
       usb_proxy-5634    [003] ..... 35373.589416: usb_ep_queue+0x4/0xc8 <-raw_process_ep_io+0x164/0x4d0 [raw_gadget]
       usb_proxy-5634    [003] ..... 35373.590023: usb_ep_queue+0x4/0xc8 <-raw_process_ep_io+0x164/0x4d0 [raw_gadget]
       usb_proxy-5634    [003] ..... 35373.590693: usb_ep_queue+0x4/0xc8 <-raw_process_ep_io+0x164/0x4d0 [raw_gadget]
       usb_proxy-5634    [003] ..... 35373.591659: usb_ep_queue+0x4/0xc8 <-raw_process_ep_io+0x164/0x4d0 [raw_gadget]
       usb_proxy-5634    [003] ..... 35373.591701: usb_ep_dequeue+0x4/0xa8 <-raw_process_ep_io+0x198/0x4d0 [raw_gadget]
       usb_proxy-5634    [003] ..... 35373.591740: usb_ep_queue+0x4/0xc8 <-raw_process_ep_io+0x164/0x4d0 [raw_gadget]

call time:

 3) + 23.000 us   |  usb_ep_queue();
 3) * 14819.81 us |  usb_ep_dequeue();
 3)   9.352 us    |  usb_ep_queue();

dmesg:

kernel: raw-gadget.0 gadget: wait interrupted
kernel: dwc2 fe980000.usb: dwc2_hsotg_ep_sethalt(ep 00000000fe7f0a9a ep2in, 0)
kernel: dwc2 fe980000.usb: dwc2_hsotg_ep_sethalt(ep 00000000cf063d4c ep1out, 0)
kernel: raw-gadget.0 gadget: event fetching interrupted

After some time it randomly get usb_ep_dequeue.
So i looked into raw-gadget code.

https://github.com/xairy/raw-gadget/blob/master/raw_gadget/raw_gadget.c#L1044

	ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
	if (ret) {
		dev_err(&dev->gadget->dev,
				"fail, usb_ep_queue returned %d\n", ret);
		spin_lock_irqsave(&dev->lock, flags);
		dev->state = STATE_DEV_FAILED;
	        // MISSING "ret = -EBUSY"?
		goto out_done;
	}

	ret = wait_for_completion_interruptible(&done);
	if (ret) {
		dev_dbg(&dev->gadget->dev, "wait interrupted\n");
		usb_ep_dequeue(ep->ep, ep->req);
		wait_for_completion(&done);
		spin_lock_irqsave(&dev->lock, flags);
                 // MISSING "ret = -EINTR"?
		goto out_done;
	}

I think there is missing ret = -EINTR. Not sure.
But from USB_RAW_IOCTL_EP_WRITE or USB_RAW_IOCTL_EP_READ i should get response about "error" right?
Now im getting "good" length writted/readed.

Make Raw Gadget and dwc3 compatible

The dwc3 UDC driver handles setup requests with wLength == 0 in an unusual way, see the details here.

We need to either fix dwc3 or make Raw Gadget support the way dwc3 works.

Fail to emulate the printer

Hi,

I'm trying to run /examples/printer.c to see how the bulk tranfer are handled by raw-gadget, but it doesn't work. After entering usb_raw_ep_read in bulk_out thread, the following ioctl will never return (or the thread being killed maybe)

int rv = ioctl(fd, USB_RAW_IOCTL_EP_READ, io);

After I adding some debug info in raw-gadget, I find that in raw_ioctl_ep_read, return value of raw_process_ep_io is set to -512. It's the ERESTARTSYS error but I don't really understand what that means.

ret = raw_process_ep_io(dev, &io, data, false);

Then it returns from raw_ioctl and the printer gets stuck in ep0_loop. The same happens in bulk_in thread too.

Here is the kernel verison, and the file system is just a stretch.img constructed by syzkaller script.

root@syzkaller:~# uname -a
Linux syzkaller 6.1.0-rc7-00190-g97ee9d1c1696-dirty #8 SMP PREEMPT_DYNAMIC Sun Dec  4 14:26:16 CET 2022 x86_64 GNU/Linux

Any idea what's going on and how can I fix this? Thanks.

Raw-gadget ioctls may access uninitialized data

For the IOCTLs of the raw-gadget module that do not accept any data (e.g. raw_ioctl_run), the value argument of the IOCTL call is uninitialized (variadic arg to ioctl()) and should not be read.

This causes issue when using cross-language bindings to IOCTL into the raw-gadget driver, such as using nix from Rust, which intentionally omits any variadic arguments to ioctl since the call requires no data. This causes these functions to spuriously EINVAL as an error code.

I added some logging to the raw_ioctl_run() and was noticing that value was taking on random, uninitialized values and causing the call to emit an error.

Facedancer backend

It should be possible to use Raw Gadget as a backend for Facedancer. At least for UDCs with configurable (direction and transfer type) gadget endpoints.

Facedancer Error w/ Raw Gadget

Currently attempting to run https://github.com/xairy/Facedancer/tree/rawgadget (rubber-ducky.py) on the latest raw-gadget dev branch and receiving the following error, thoughts?

Traceback (most recent call last):
  File "/home/jack/Facedancer/rubber-ducky.py", line 37, in <module>
    main(device, type_letters())
  File "/home/jack/Facedancer/facedancer/devices/__init__.py", line 51, in default_main
    device.emulate(*coroutines)
  File "/home/jack/Facedancer/facedancer/future/device.py", line 172, in emulate
    self.connect()
  File "/home/jack/Facedancer/facedancer/future/device.py", line 124, in connect
    self.backend = FacedancerUSBApp()
                   ^^^^^^^^^^^^^^^^^^
  File "/home/jack/Facedancer/facedancer/core.py", line 23, in FacedancerUSBApp
    return FacedancerApp.autodetect(verbose, quirks)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jack/Facedancer/facedancer/core.py", line 54, in autodetect
    return subclass(verbose=verbose, quirks=quirks)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jack/Facedancer/facedancer/backends/rawgadget.py", line 345, in __init__
    super().__init__(device, verbose)
  File "/home/jack/Facedancer/facedancer/core.py", line 98, in __init__
    self.init_commands()
  File "/home/jack/Facedancer/facedancer/backends/rawgadget.py", line 352, in init_commands
    self.api.set_timeout(usb_raw_timeout_type.USB_RAW_TIMEOUT_EVENT_FETCH, 0, 20)
  File "/home/jack/Facedancer/facedancer/backends/rawgadget.py", line 281, in set_timeout
    RawGadgetRequests.USB_RAW_IOCTL_SET_TIMEOUT(self.fd, arg)
  File "/home/jack/Facedancer/facedancer/backends/rawgadget.py", line 172, in fn
    rv = fcntl.ioctl(fd, req, arg, True)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
OSError: [Errno 22] Invalid argument
sys:1: RuntimeWarning: coroutine 'type_letters' was never awaited

Out-of-scope: Use inside Android device

Hi @xairy ,

Your RAW GADGET module seems very useful. However, it's quite difficult to use it inside an Android device (without rooting it). Then I ask if you feel it's possible to have some Android API to access to low level gadget functions.

Regards.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.