My goal is to convincingly emulate a well known brand of USB Flash Disk, using Raspberry Pi Zero (with or without Wifi). For the sake of this example, I want to emulate a 4GB Sandisk Cruzer Edge, but generally speaking I want the ability to have full control of idVendor, idProduct, iProduct, iManufacturer, iSerial (and any other subtle variables of a USB flash disk)
I only want USB Mass Storage, not a composite device (with HID, or ethernet or serial or anything else)
My target USB Host devices range from: Linux AMD64, Linux MIPS, Linux ARM, Windows x86 I've activated dwc2, partitioned and formatted the file that is to be used as the storage & filesystem and loaded g_mass_storage. It is working fine from the perspective that it successfully mounts the Storage on all targets - I can connect and see the contents, add/delete files, eject and reconnect, seeing changes fine.
I use this to launch the USB Mass Storage gadget:
sudo modprobe g_mass_storage file=/home/pi/piusb_fat32.bin nofua=1 luns=1 ro=0 stall=0 removable=1 cdrom=0 idVendor=0x0781 idProduct=0x556e bcdDevice=0x0103 iManufacturer="SanDisk" iProduct="Cruzer Edge" iSerialNumber="990431108215FFF05368" The problem is that the name of the device remains as Linux File-Stor Gadget. Under Linux & Windows respectively:
- Linux File-Stor Gadget 0414
- USBSTOR\DISK&VEN_LINUX&PROD_FILE-STOR_GADGET&REV_0414
When the pi is connected to Windows 10 1903 x86 laptop, I see two devices appear:
Get-WmiObject Win32_USBControllerDevice -ComputerName localhost -Impersonation Impersonate -Authentication PacketPrivacy | Foreach-Object { [Wmi]$_.Dependent } | Sort-Object PNPClass | ft Name, PNPClass, PNPDeviceID Name PNPClass PNPDeviceID ---- -------- ----------- Linux File-Stor Gadget USB Device DiskDrive USBSTOR\DISK&VEN_LINUX&PROD_FILE-STOR_GADGET&REV_0414\990431108215FFF05368&0 USB Mass Storage Device USB USB\VID_0781&PID_556E\990431108215FFF05368 Whereas with the real Sandisk USB Flash Disk:
Name PNPClass PNPDeviceID ---- -------- ----------- SanDisk Cruzer Edge USB Device DiskDrive USBSTOR\DISK&VEN_SANDISK&PROD_CRUZER_EDGE&REV_1.20\200431108215FFF05367&0 USB Mass Storage Device USB USB\VID_0781&PID_556B\200431108215FFF05367 The "USB Mass Storage Device" has the correct VID, PID, SerialNumber, but the DiskDrive device does not. Linux targets see the same two devices (see below for lsusb & dmesg examples)
From the source, It looks to me like the vendor=”Linux” and model=“File-Stor Gadget” is being set in the function fsg_common_set_inquiry_string in f_mass_storage.c https://github.com/raspberrypi/linux/blob/rpi-4.19.y/drivers/usb/gadget/function/f_mass_storage.c
- I would like to know the best way to override that behaviour (preferably without having to recompile f_mass_storage module every time!)?
- Is there a way that the gadget could pass through iManufacturer, iProduct parameters to use as Vendor and Model?
The steps I used to create the Raspbian environment are: Clean flash Rasbian onto SD card using Balena Etch
sudo su echo "dtoverlay=dwc2" | sudo tee -a /boot/config.txt echo "dwc2" | sudo tee -a /etc/modules echo "g_mass_storage" | sudo tee -a /etc/modules sudo dd if=/dev/zero of=/home/pi/piusb_fat32.bin bs=1 count=0 seek=128M sudo mkdosfs /home/pi/piusb_fat32.bin -n FAT32VOL11 reboot now I have tried:
- On Windows, deleting the cached reg key for the device: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\usbflags\keyname Where keyname is the concatenation of VID+PID+BCD_RELEASE_NUMBER
- Changing the serial number of the device each time I create the gadget, so eliminate caching as the cause.
- Reformatting the USB disk from Windows, as Fat32, with a different volume name (rather than using mkdosfs)
- Multiple versions of raspbian
- 2019-09-26-raspbian-buster-lite (with & without running apt full-upgrade)
- 2018-06-27-raspbian-stretch-lite (with & without running apt full-upgrade)
- 2019-04-08-raspbian-stretch-lite (with & without running apt full-upgrade)
I’m certainly no USB or Linux expert, so this is what I have tried to piece together about how the Raspberry Pi USB ‘gadget’ Storage stack works. There are Five things of relevance:
- dwc2 module (does the OTG host/gadget flip dictated by OTG_SENSE)
- usb_f_mass_storage.ko module (Mass Storage Functions, handles the SCSI stuff to interface between USB & the file that acts as the storage for our USB disk)
- g_mass_storage.ko module (The Mass Storage Gadget)
- the file on the Pi that acts as the storage. this holds the filesystem and data that is read/writen to the emulated USB storage.
- The parameters that we pass to g_mass_storage module when loading it via modprobe
There is a very similar post here, but it hasn’t been active for more than 2 years now, and it didn’t solve my issue, so I’m re-posting here with more info:
And someone else had the same problem, unresolved from 2017:
----On a Kali x86 Laptop: ----------
dmesg (output snipped to show relevant stuff) [66925.017786] usb 1-4.3: New USB device found, idVendor=0781, idProduct=556e [66925.017794] usb 1-4.3: New USB device strings: Mfr=3, Product=4, SerialNumber=5 [66925.017799] usb 1-4.3: Product: Cruzer Edge [66925.017804] usb 1-4.3: Manufacturer: SanDisk [66925.017808] usb 1-4.3: SerialNumber: 990431108215FFF05368 [66925.019129] usb-storage 1-4.3:1.0: USB Mass Storage device detected [66925.019542] scsi host3: usb-storage 1-4.3:1.0 [66926.021250] scsi 3:0:0:0: Direct-Access Linux File-Stor Gadget 0414 PQ: 0 ANSI: 2 [66926.022471] sd 3:0:0:0: Attached scsi generic sg1 type 0 [66926.023505] sd 3:0:0:0: [sdb] 262144 512-byte logical blocks: (134 MB/128 MiB) [66926.023729] sd 3:0:0:0: [sdb] Write Protect is off [66926.023736] sd 3:0:0:0: [sdb] Mode Sense: 0f 00 00 00 [66926.023932] sd 3:0:0:0: [sdb] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA [66926.034208] sdb: [66926.035170] sd 3:0:0:0: [sdb] Attached SCSI removable disk ------lsusb of RPi from kali --
root@kali:~# lsusb -d 0781:556e -v Bus 001 Device 031: ID 0781:556e SanDisk Corp. Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 0 (Defined at Interface level) bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x0781 SanDisk Corp. idProduct 0x556e bcdDevice 1.03 iManufacturer 3 SanDisk iProduct 4 Cruzer Edge iSerial 5 990431108215FFF05368 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 32 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xc0 Self Powered MaxPower 2mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 8 Mass Storage bInterfaceSubClass 6 SCSI bInterfaceProtocol 80 Bulk-Only iInterface 1 Mass Storage Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x01 EP 1 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 1 Device Qualifier (for other device speed): bLength 10 bDescriptorType 6 bcdUSB 2.00 bDeviceClass 0 (Defined at Interface level) bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 bNumConfigurations 1 Device Status: 0x0000 (Bus Powered) ------- Kali's view of the USB Device from /sys/bus/usb ----
root@kali:~# cat /sys/bus/usb/devices/1-4.2/* cat: /sys/bus/usb/devices/1-4.2/ep_00: Is a directory 556e 0781 no SanDisk 0 cat: /sys/bus/usb/devices/1-4.2/port: Is a directory cat: /sys/bus/usb/devices/1-4.2/power: Is a directory Cruzer Edge 0x0 unknown cat: /sys/bus/usb/devices/1-4.2/remove: Permission denied 990431108215FFF05368 480 cat: /sys/bus/usb/devices/1-4.2/subsystem: Is a directory MAJOR=189 MINOR=29 DEVNAME=bus/usb/001/030 DEVTYPE=usb_device DRIVER=usb PRODUCT=781/556e/103 TYPE=0/0/0 BUSNUM=001 DEVNUM=030 426 2.00 ----------How Kali see's the RPi SCSI vendor & model strings ---------
root@kali:~# cat /sys/bus/scsi/devices/target3\:0\:0/3\:0\:0\:0/vendor Linux root@kali:~# cat /sys/bus/scsi/devices/target3\:0\:0/3\:0\:0\:0/model File-Stor Gadget -------- dmesg from Pi Zero after connecting to Windows -----------------
[ 409.446304] dwc2 20980000.usb: new device is high-speed [ 409.479146] dwc2 20980000.usb: new address 9 [ 439.684939] dwc2 20980000.usb: new device is high-speed [ 439.717958] dwc2 20980000.usb: new address 10 [ 439.748612] g_mass_storage gadget: high-speed config #1: Linux File-Backed Storage Based on feedback from @Ephemeral (many thanks...). I have now tested using libcomposite and conclude it is the best answer.
The problem boils down to inquiry_string being unchangeable with g_mass_storage (unless you want to re-compile the module yourself with a different inquiry_string), but libcomposite gives full control of it, and many other parameters of the usb device.
One downside of libcomposite is that its not a one-liner to instantiate the gadget, it requires a bit more work. But after the gadget is created, the inquiry_string can be changed on the fly, which is quite cool! echo 'ABCDEFGH123456789ABCDEFGWXYZ' >> /sys/kernel/config/usb_gadget/gadget1/functions/mass_storage.usb0/lun.0/inquiry_string
inquirystring is tokenized by the host OS into: vendor(len 8) + model(len 16) + rev(len 4) which is what we see in Windows in PNPDeviceID as VEN_ PROD_ REV_
Name PNPClass PNPDeviceID ---- -------- ----------- SanDisk Cruzer Edge USB Device DiskDrive USBSTOR\DISK&VEN_SANDISK&PROD_CRUZER_EDGE&REV_1.20\200431108215FFF05367&0 and in OpenWRT Linux as vendor, model, rev in:
/sys/class/block/sda/device/vendor /sys/class/block/sda/device/model /sys/class/block/sda/device/rev or x86 Kali Linux as vendor, model, rev in:
/sys/bus/scsi/devices/target3\:0\:0/3\:0\:0\:0/vendor /sys/bus/scsi/devices/target3\:0\:0/3\:0\:0\:0/model /sys/bus/scsi/devices/target3\:0\:0/3\:0\:0\:0/rev I found this article quite useful to get a better understanding of the architecture of USB composite devices: https://lwn.net/Articles/395712/
This is the full script I use now:
#!/bin/bash # libcomposite USB mass storage device gadget for raspberry pi zero # Call this script on Pi Zero startup by adding its full path to /etc/rc.local # Assumes you have created the backing file for the storage device with something like this: # sudo dd if=/dev/zero of=/home/pi/piusb_fat32.bin bs=1 count=0 seek=128M # sudo mkdosfs /home/pi/piusb_fat32.bin -n FAT32VOL11 # And done these two steps once: # sudo echo "dtoverlay=dwc2" | sudo tee -a /boot/config.txt # sudo echo "dwc2" | sudo tee -a /etc/modules # Do some simple logging so we know the script is launching at startup date | tee -a /home/pi/startup_script.log echo Starting startup_script | tee -a /home/pi/startup_script.log #Load the kernel module modprobe libcomposite #Gadget name (gadget1 is just an arbitrary name) mkdir /sys/kernel/config/usb_gadget/gadget1 cd /sys/kernel/config/usb_gadget/gadget1 #hardware id echo 0x0419 > bcdDevice echo 0x0200 > bcdUSB echo 0x0781 > idVendor # Sandisk echo 0x556e > idProduct # Cruzer Edge #device config echo 0xEF > bDeviceClass echo 0x02 > bDeviceSubClass echo 0x01 > bDeviceProtocol echo 0x08 > bMaxPacketSize0 #More stuff mkdir strings/0x409 mkdir strings/0x407 cd strings/0x409 echo 'Sandisk' > manufacturer echo 'Cruzer Edge' > product echo 'Serial123456789' > serialnumber cd ../../ cd strings/0x407 echo 'Sandisk' > manufacturer echo 'Cruzer Edge' > product echo 'Serial123456789' > serialnumber cd ../../ mkdir functions/mass_storage.usb0 #Mass Storage 0 config settings cd functions/mass_storage.usb0 echo 0 > stall echo 1 > lun.0/removable echo 0 > lun.0/ro echo /home/pi/piusb_fat32.bin > lun.0/file #Note, the next line is important. Host OS will tokenize it as: vendor(len 8) + model(len 16) + rev(len 4) #Example here: vendor = ABCDEFGH model = 123456789ABCDEFG rev = WXYZ echo 'ABCDEFGH123456789ABCDEFGWXYZ' > lun.0/inquiry_string cd ../../ #OS Descriptor for Windows cd os_desc echo 1 > use echo 0xcd > b_vendor_code echo MSFT100 > qw_sign cd ../ #Bus Hardware Config mkdir configs/c.1 cd configs/c.1 echo 0x80 > bmAttributes echo 100 > MaxPower mkdir strings/0x409 echo "0" > strings/0x409/configuration mkdir strings/0x407 echo "1" > strings/0x407/configuration cd ../../ ln -s functions/mass_storage.usb0 configs/c.1 ln -s configs/c.1 os_desc #Activate the gadget ls /sys/class/udc > UDC This is what it generates in dmesg on Kali x86:
[ 279.856039] usb 1-4.4: new high-speed USB device number 8 using xhci_hcd [ 279.957788] usb 1-4.4: New USB device found, idVendor=0781, idProduct=556e [ 279.957796] usb 1-4.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3 [ 279.957801] usb 1-4.4: Product: Cruzer Edge [ 279.957805] usb 1-4.4: Manufacturer: Sandisk [ 279.957809] usb 1-4.4: SerialNumber: Serial123456789 [ 279.978012] usb-storage 1-4.4:1.0: USB Mass Storage device detected [ 279.978128] scsi host3: usb-storage 1-4.4:1.0 [ 279.978232] usbcore: registered new interface driver usb-storage [ 279.979998] usbcore: registered new interface driver uas [ 280.989354] scsi 3:0:0:0: Direct-Access ABCDEFGH 123456789ABCDEFG WXYZ PQ: 0 ANSI: 2 [ 280.990460] sd 3:0:0:0: Attached scsi generic sg1 type 0 [ 280.990906] sd 3:0:0:0: [sdb] 262144 512-byte logical blocks: (134 MB/128 MiB) [ 280.991089] sd 3:0:0:0: [sdb] Write Protect is off [ 280.991095] sd 3:0:0:0: [sdb] Mode Sense: 0f 00 00 00 [ 280.991271] sd 3:0:0:0: [sdb] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA [ 280.999942] sdb: sdb1 [ 281.001878] sd 3:0:0:0: [sdb] Attached SCSI removable disk And lsusb -vv on kali x86:
root@kali:~# lsusb -vv -d 0781:556e Bus 001 Device 008: ID 0781:556e SanDisk Corp. Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 ? bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x0781 SanDisk Corp. idProduct 0x556e bcdDevice 4.19 iManufacturer 1 Sandisk iProduct 2 Cruzer Edge iSerial 3 Serial123456789 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 32 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 4 0 bmAttributes 0x80 (Bus Powered) MaxPower 100mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 8 Mass Storage bInterfaceSubClass 6 SCSI bInterfaceProtocol 80 Bulk-Only iInterface 5 Mass Storage Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x01 EP 1 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 1 Device Qualifier (for other device speed): bLength 10 bDescriptorType 6 bcdUSB 2.00 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 ? bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 bNumConfigurations 1 Device Status: 0x0000 (Bus Powered) 
