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)