主要参考了以下资料: usbmassbulk_10.pdf , usbmass-ufi10.pdf, SCSI Commands Reference Manual,spc-3.pdf
以下驱动模块通过usb接口发送 READ_CAPACITY 指令给U盘,然后读取U盘响应。
最终打印了block的总数以及一个block 的length。
要成功运行此模块,需要在内核中禁用 CONFIG_USB_STORAGE ,不然不会被USB设备匹配上。
内核版本 : 4.4.194 。
另外具体格式参考了 windows 下Bus Hound 抓包工具的数据。
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/usb.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/usb/storage.h>
#include <scsi/scsi_proto.h>
#include <asm-generic/errno-base.h>
#include <uapi/asm-generic/errno.h>
#include <uapi/linux/cciss_defs.h>
/**
* USB Mass Storage Class
*
* https://www.oreilly.com/library/view/linux-device-drivers/0596005903/ch13.html
*
* https://www.kernel.org/doc/html/v4.12/driver-api/usb/writing_usb_driver.html
*
* http://www.linux-usb.org/
*
* https://embetronicx.com/tutorials/linux/device-drivers/usb-device-driver-basics/
*
* https://github.com/Embetronicx/Tutorials/blob/master/Linux/Device_Driver/usb_device_driver/usb_driver.c
*
* https://www.usb.org/sites/default/files/Mass_Storage_Specification_Overview_v1.4_2-19-2010.pdf
*
* https://www.usb.org/sites/default/files/Mass_Storage_Specification_Overview_v1.4_2-19-2010.pdf
*
* https://www.intel.com/content/www/us/en/products/docs/io/universal-serial-bus/universal-serial-bus-specifications.html
*
*
* https://www.linux.it/~rubini/docs/usb/usb.html
*
* http://www.linux-usb.org/devices.html
*
*
* https://en.wikipedia.org/wiki/SCSI
*
* https://en.wikipedia.org/wiki/SCSI_command
*
*
* https://www.t10.org/lists/2op.htm
*
* https://wiki.osdev.org/USB_Mass_Storage_Class_Devices
*
*
* https://support.huawei.com/enterprise/zh/doc/EDOC1000149331/ec501220
*
* Devices usually have one or more configurations.
*
*
* Whenever a USB device is attached to the bus it will be enumerated by the
USB subsystem - i.e an unique device number (1-127) is assigned and then the
device descriptor is read. Such a desciptor is a data structure which contains
information about the device and its properties.
Configurations often have one or more interfaces.
Interfaces usually have one or more settings.
Interfaces have zero or more endpoints.
*
*
* lsusb
* usb-devices
*
* Bulk Only Mass Storage Devices are the most common type of USB storage device, and they include most USB thumb drives, hard drives, CD,
* DVD and Blu-ray drives as well. They normally use the SCSI Command Set,
* but the USB Mass Storage specifications allow devices to use other command sets as well.
*
* USB Mass Storage Class Bulk-Only (BBB) Transport
*
* Universal Serial Bus Specification
*
*
* In SCSI terminology, communication takes place between an initiator and a target. The initiator sends a command to the target, which then responds.
* SCSI commands are sent in a Command Descriptor Block (CDB).
* The CDB consists of a one byte operation code followed by five or more bytes containing command-specific parameters.
*
*
* At the end of the command sequence, the target returns a status code byte, such as 00h for success,
* 02h for an error (called a Check Condition), or 08h for busy
*
*There are four categories of SCSI commands: N (non-data), W (writing data from initiator to target), R (reading data), and B (bidirectional).
*
*Test unit ready: Queries device to see if it is ready for data transfers (disk spun up, media loaded, etc.).
Inquiry: Returns basic device information.
Request sense: Returns any error codes from the previous command that returned an error status.
Send diagnostic and Receive diagnostic results: runs a simple self-test, or a specialised test defined in a diagnostic page.
Start/Stop unit: Spins disks up and down, or loads/unloads media (CD, tape, etc.).
Read capacity: Returns storage capacity.
Format unit: Prepares a storage medium for use. In a disk, a low level format will occur. Some tape drives will erase the tape in response to this command.
Read: (four variants): Reads data from a device.
Write: (four variants): Writes data to a device.
Log sense: Returns current information from log pages.
Mode sense: Returns current device parameters from mode pages.
Mode select: Sets device parameters in a mode page.
SCSI commands are sent in a command descriptor block (CDB), which consists of a one byte operation code (opcode) followed by five
or more bytes containing command-specific parameters.
Upon receiving and processing the CDB the device will return a status code byte and other information.
The target usually provides the initiators one or more logical unit numbers (LUN).
In a storage area, a LUN often represents a SCSI disk on which a host can perform a read and write operation.
Every endpoint in a device can handle a queue of urbs, so that multiple urbs can be sent to the same endpoint before the queue is empty
T: Bus=06 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 3 Spd=5000 MxCh= 0
D: Ver= 3.20 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs= 1
P: Vendor=0951 ProdID=1666 Rev=01.10
S: Manufacturer=Kingston
S: Product=DataTraveler 3.0
S: SerialNumber=E0D55EA573EB165178570CE3
C: #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=144mA
I: If#=0x0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=andy_usbdriver
*
*/
#define USB_VENDOR_ID (0x0951)
#define USB_PRODUCT_ID (0x1666)
//#define TestUnitReady
#define Inquiry
#define MYTAG 1333
//#define inquiry_device_identification
#define inquiry_supported_vpd_pages
#define PRINT_USB_ENDPOINT_DESCRIPTOR( i ) \
{ \
pr_info("USB_INTERFACE_DESCRIPTOR:\n"); \
pr_info("-----------------------------\n"); \
pr_info("bLength: 0x%x\n", i.bLength); \
pr_info("bDescriptorType: 0x%x\n", i.bDescriptorType); \
pr_info("bInterfaceNumber: 0x%x\n", i.bInterfaceNumber); \
pr_info("bAlternateSetting: 0x%x\n", i.bAlternateSetting); \
pr_info("bNumEndpoints: 0x%x\n", i.bNumEndpoints); \
pr_info("bInterfaceClass: 0x%x\n", i.bInterfaceClass); \
pr_info("bInterfaceSubClass: 0x%x\n", i.bInterfaceSubClass); \
pr_info("bInterfaceProtocol: 0x%x\n", i.bInterfaceProtocol); \
pr_info("iInterface: 0x%x\n", i.iInterface); \
pr_info("\n"); \
}
struct usb_device *usb_dev;
int endpoint_out_addr;
int endpoint_in_addr;
struct bulk_cs_wrap *bcs;
struct bulk_cb_wrap *bcb;
u8 *buffer;
#define MAX_BYTES 1024
typedef struct {
BYTE total_size[4];
BYTE block_size[4];
} Response;
static void sendcmd(void) {
int status;
int actual_length = 0;
int totalsize;
int blocksize;
Response *response;
bcb = kmalloc(sizeof(*bcb), GFP_KERNEL);
memset(bcb, 0, sizeof(struct bulk_cb_wrap));
bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
bcb->DataTransferLength = MAX_BYTES;
bcb->Flags = US_BULK_FLAG_IN;
bcb->Lun = 0;
bcb->Length = 12;
bcb->Tag = MYTAG;
bcb->CDB[0] = READ_CAPACITY;
bcb->CDB[1] = 0x00;
bcb->CDB[2] = 0x00;
bcb->CDB[3] = 0x00;
bcb->CDB[4] = 0x00;
bcb->CDB[5] = 0x00;
bcb->CDB[6] = 0x00;
bcb->CDB[7] = 0x00;
bcb->CDB[8] = 0x00;
bcb->CDB[9] = 0x00;
buffer = kmalloc(MAX_BYTES, GFP_KERNEL);
memset(buffer, 0, MAX_BYTES);
status = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, endpoint_out_addr),
bcb, US_BULK_CB_WRAP_LEN, &actual_length, 1000);
if (status) {
dev_info(&usb_dev->dev, "usb_bulk_msg fail ,status %d\n", status);
return;
}
dev_info(&usb_dev->dev, "usb_bulk_msg success \n");
dev_info(&usb_dev->dev, "actual_length = %d\n", actual_length);
if (actual_length == US_BULK_CB_WRAP_LEN) {
dev_info(&usb_dev->dev, "actual_length success");
}
dev_info(&usb_dev->dev, "start receive data from usb device\n");
//read response from usb device;
status = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, endpoint_in_addr),
buffer, MAX_BYTES, &actual_length, 1000);
if (status) {
dev_info(&usb_dev->dev, "usb_bulk_msg fail ,status %d\n", status);
return;
}
dev_info(&usb_dev->dev, "usb_bulk_msg success \n");
dev_info(&usb_dev->dev, "actual_length = %d\n", actual_length);
if (actual_length == 0) {
dev_info(&usb_dev->dev, "no date received\n");
return;
}
if (actual_length == 13) {
dev_info(&usb_dev->dev, "only bcs received\n");
bcs = (struct bulk_cs_wrap*) buffer;
if (bcs->Status == 0) {
dev_info(&usb_dev->dev, "bcs Status success\n");
}
dev_info(&usb_dev->dev, "bcs Tag 0x%x\n", bcs->Tag);
dev_info(&usb_dev->dev, "bcs Residue %d\n", bcs->Residue);
if (bcs->Tag == MYTAG) {
dev_info(&usb_dev->dev, "bcs Tag success\n");
} else {
dev_info(&usb_dev->dev, "bcs Tag fail\n");
}
return;
}
if (actual_length != 13) {
response = (Response*) buffer;
totalsize = (response->total_size[0] << 24) |(response->total_size[1] << 16) | (response->total_size[2] << 8) | (response->total_size[3]);
dev_info(&usb_dev->dev, "totalsize = %d\n", totalsize);
blocksize = (response->block_size[0] << 24) |(response->block_size[1] << 16) | (response->block_size[2] << 8) | (response->block_size[3]);
dev_info(&usb_dev->dev, "block_size = %d\n", blocksize);
//now read bcs
bcs = kmalloc(sizeof(*bcs), GFP_ATOMIC);
memset(bcs, 0, sizeof(struct bulk_cb_wrap));
bcs->Status = 10; //just check to see if the result status will be changed to 0 by the usb device .
status = usb_bulk_msg(usb_dev,
usb_rcvbulkpipe(usb_dev, endpoint_in_addr), bcs,
US_BULK_CS_WRAP_LEN, &actual_length, 50);
if (status == 0) {
dev_info(&usb_dev->dev, "actual_length2 %d\n", actual_length);
dev_info(&usb_dev->dev, "bcs Status %d\n", bcs->Status);
if (bcs->Status == 0) {
dev_info(&usb_dev->dev, "bcs Status success\n");
}
dev_info(&usb_dev->dev, "bcs Tag 0x%x\n", bcs->Tag);
if (bcs->Tag == MYTAG) {
dev_info(&usb_dev->dev, "bcs Tag success\n");
} else {
dev_info(&usb_dev->dev, "bcs Tag fail\n");
}
}
}
}
struct vdsk_dev {
unsigned long size;
u8 *data;
spinlock_t lock;
struct gendisk *mydisk;
struct request_queue *queue;
};
struct vdsk_dev *dev;
int myprobe(struct usb_interface *interface, const struct usb_device_id *id) {
unsigned int i;
unsigned int endpoints_count;
int endpoint_type;
int interfaceClass;
int interfaceSubClass;
int interfaceProtocol;
struct usb_endpoint_descriptor endpoint_descriptor;
struct usb_host_interface *iface_desc = interface->cur_altsetting;
usb_dev = interface_to_usbdev(interface);
dev_info(&interface->dev, "myprobe start\n");
dev_info(&interface->dev, "idVendor = %x\n", id->idVendor);
dev_info(&interface->dev, "idProduct = %x\n", id->idProduct);
endpoints_count = iface_desc->desc.bNumEndpoints;
dev_info(&interface->dev, "endpoints_count = %d\n", endpoints_count);
interfaceClass = iface_desc->desc.bInterfaceClass;
interfaceSubClass = iface_desc->desc.bInterfaceSubClass;
interfaceProtocol = iface_desc->desc.bInterfaceProtocol;
// dev_info(&interface->dev, "bInterfaceClass = 0x%x\n", interfaceClass);
// dev_info(&interface->dev, "interfaceSubClass = 0x%x\n", interfaceSubClass);
// dev_info(&interface->dev, "interfaceProtocol = 0x%x\n", interfaceProtocol);
switch (interfaceClass) {
case USB_CLASS_MASS_STORAGE:
dev_info(&interface->dev, "interfaceClass: USB_CLASS_MASS_STORAGE\n");
break;
default:
dev_info(&interface->dev, "never \n");
break;
}
switch (interfaceSubClass) {
case USB_SC_SCSI:
dev_info(&interface->dev, "interfaceSubClass: USB_SC_SCSI\n");
break;
default:
dev_info(&interface->dev, "never \n");
break;
}
switch (interfaceProtocol) {
case USB_PR_BULK:
dev_info(&interface->dev, "interfaceProtocol: USB_PR_BULK\n");
break;
default:
dev_info(&interface->dev, "never \n");
break;
}
for (i = 0; i < endpoints_count; i++) {
endpoint_descriptor = iface_desc->endpoint[i].desc;
if (usb_endpoint_is_bulk_out(&endpoint_descriptor)) {
endpoint_out_addr = endpoint_descriptor.bEndpointAddress;
}
if (usb_endpoint_is_bulk_in(&endpoint_descriptor)) {
endpoint_in_addr = endpoint_descriptor.bEndpointAddress;
}
dev_info(&interface->dev, "endpoint %d bDescriptorType = %d\n", i,
endpoint_descriptor.bDescriptorType);
dev_info(&interface->dev, "endpoint %d bEndpointAddress = %d\n", i,
endpoint_descriptor.bEndpointAddress);
endpoint_type = endpoint_descriptor.bmAttributes
& USB_ENDPOINT_XFERTYPE_MASK;
switch (endpoint_type) {
case USB_ENDPOINT_XFER_CONTROL:
dev_info(&interface->dev, "control endpoint\n");
break;
case USB_ENDPOINT_XFER_ISOC:
dev_info(&interface->dev, "ISOCHRONOUS endpoint\n");
break;
case USB_ENDPOINT_XFER_BULK:
dev_info(&interface->dev, "bulk endpoint\n");
break;
case USB_ENDPOINT_XFER_INT:
dev_info(&interface->dev, "Interrupt endpoint\n");
break;
default:
dev_info(&interface->dev, "never \n");
break;
}
}
sendcmd();
dev_info(&interface->dev, "myprobe end\n");
return 0;
}
void mydisconnect(struct usb_interface *intf) {
dev_info(&intf->dev, "mydisconnect\n");
kfree(bcs);
kfree(bcb);
kfree(buffer);
}
static struct usb_device_id my_id_table[] = { { USB_INTERFACE_INFO(
USB_CLASS_MASS_STORAGE,
USB_SC_SCSI, USB_PR_BULK) }, { } };
//static struct usb_device_id my_id_table[] = { { USB_DEVICE(USB_VENDOR_ID,
//USB_PRODUCT_ID) }, { } };
MODULE_DEVICE_TABLE(usb, my_id_table);
static struct usb_driver my_usbdriver = { .name = "andy_usbdriver", .probe =
myprobe, .disconnect = mydisconnect, .id_table = my_id_table, };
module_usb_driver( my_usbdriver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andy");