19 June, 2010

Creating a custom USB Mass Storage Driver

In my previous post I described how to get a USB mass storage (UMS) device recognized. This is not always sufficient, as was the case for me recently. To get around this, it was necessary to pull down the kernel source and modify and rebuild the usb mass storage driver. This blog entry describes what I did.

In my case, the device was also not forthright with it's protocol and transport, so the driver was rejecting the device. The usb-storage driver includes a header file of exception cases for just such usb devices. If you modify this file for your device, you can get around this last hurdle.

You will need to pull down the kernel source, modify one of th usb-storage driver files, recompile the kernel, and install the driver.

Step 1: Download the kernel source. Using Ubuntu, you can use synaptic or apt-get install to pull down the source. Using apt-get for instance, get it using:

apt-get install linux-source

On Ubuntu, the source will be placed in /usr/src as a tar.bz2. You'll need to unzip this. Unzipping in the same location is fine, as this is where the header files are placed anyway.

Step 2: Open "unusual_devs.h" in an editor. It should be under "drivers/usb/storage" directory.

Step 3: Add an entry for your device into "unusual_devs.h". Each entry looks like the following:

UNUSUAL_DEV( 0xbbbb, 0xb001, 0x0001, 0x0001,
"Mfg Name",
"Product Name",
US_SC_SCSI, US_PR_BULK, NULL,
US_FL_FIX_CAPACITY ),

I don't fully understand what all the values mean, but here is a break down of what I do know:

Field Purpose Possible Values
1 USB Vendor Id Hexadecimal
2 USB Product Id Hexadecimal
3 Device Min Hexadecimal
4 Device Max Hexadecimal
5 Vendor Name String
6 Product Name String
7 Protocol US_SC_DEVICE - Use the protocol specified by the device
US_SC_SCSI - Transparent SCSI
US_SC_RBC - Reduced Block Commands (RBC)
US_SC_8020 - 8020i
US_SC_8070 - 8070i
US_SC_QIC - QIC-157
8 Transport US_PR_DEVICE - Use the transport specified by the device
US_PR_CB - Control/Bulk
US_PR_CBI - Control/Bulk/Interrupt
US_PR_BULK - Bulk
9 Init function The name of the function for initialization, or NULL if there is none.
10 Flags US_FL_SANE_SENSE
US_FL_BAD_SENSE
US_FL_FIX_CAPACITY
US_FL_CAPACITY_HEURISTICS
US_FL_IGNORE_DEVICE
US_FL_NOT_LOCKABLE
US_FL_MAX_SECTORS_64
US_FL_CAPACITY_OK
US_FL_IGNORE_RESIDUE
US_FL_SINGLE_LUN
US_FL_NO_WP_DETECT

The key values here are the Protocol, Transport and Flags. For the Protocol and Transport, chances are the auto-detect values won't work, otherwise you would not need to do this. In my case I simply guessed at the values (SCSI is a common protocol, for instance).

Step 4: Compile. Run make as root from the base kernel directory. You may need to install additional compilation tools. Verify that you have a usb-storage.ko file under drivers/usb/storage.

Step 5: Determine your running kernel version. Run "uname -r". This will return the name of your kernel.

Step 6: Install your new kernel module. While you can use the whole kernel, I simply installed the one driver. Rename the existing driver to usb-storage.ko.orig, and copy over the new driver to /lib/modules/<kernel-name>/kernel/drivers/usb/storage, where <kernel-name> is the value returned in step 5.

If all works according to plan, your new device should be recognized when you plug it in.

No comments:

Post a Comment