以下驱动代码参考了 Bus Hound 抓包数据 。
USB设备是一个金士顿的U盘 。
发送INQUERY 0x12,返回内容为 KingstonDataTraveler 3.0
。
测试的内核版本是 4.4.194 。
目前不清楚的是为什么返回数据的长度是32,看了几个文档都不是这个值,但是抓包工具显示是32 。
#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
*
* https://wiki.osdev.org/ATAPI
*
* file:///C:/Users/86186/Desktop/usbmass-ufi10.pdf
*
* 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
#define RETURN_DATA_LENGTH 0x20
typedef struct {
// byte 0
u8 device_type :5;
u8 :3;
// byte 1
u8 :7;
u8 :1;
// byte 2
u8 :8;
// byte 3
u8 data_format :4;
u8 :4;
// byte 4
u8 additional_length;
// byte 5 - byte 7
u8 :8;
u8 :8;
u8 :8;
// byte 8 - byte 15
u8 vendor_information[8];
// byte 16 - byte 31
u8 product_Identification[16];
// byte 32 - byte 35
u8 product_revision[4];
// byte 36 - byte 47
u8 unknown[12];
} inquery_response_struct;
static void inquiry_cmd(void) {
int status;
int actual_length = 0;
int i ;
inquery_response_struct *response;
bcb = kmalloc(sizeof(*bcb), GFP_KERNEL);
memset(bcb, 0, sizeof(struct bulk_cb_wrap));
dev_info(&usb_dev->dev, "sizeof(int) %lu\n", sizeof(int));
dev_info(&usb_dev->dev, "sizeof(unsigned int) %lu\n", sizeof(unsigned int));
dev_info(&usb_dev->dev, "sizeof(long) %lu\n", sizeof(long));
bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
bcb->DataTransferLength = RETURN_DATA_LENGTH;
bcb->Flags = US_BULK_FLAG_IN;
bcb->Lun = 0;
bcb->Length = 6;
bcb->Tag = MYTAG;
/**
* INQUIRY
*/
bcb->CDB[0] = INQUIRY;
bcb->CDB[1] = 0x00;
bcb->CDB[2] = 0x00;
bcb->CDB[3] = 0x00;
bcb->CDB[4] = RETURN_DATA_LENGTH;
bcb->CDB[5] = 0x00;
buffer = kmalloc(MAX_BYTES, GFP_KERNEL);
memset(buffer, 0, MAX_BYTES);
// The CDB consists of a one byte operation code followed by five or more bytes containing command-specific parameters.
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");
actual_length = -1;
//read response from usb device;
status = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, endpoint_in_addr),
buffer, RETURN_DATA_LENGTH, &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;
}
for(i=0;i<actual_length;i++){
dev_info(&usb_dev->dev, "buffer %d = %c\n",i,*(buffer+i));
}
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 = (inquery_response_struct*) buffer;
dev_info(&usb_dev->dev, "device_type = %d\n", response->device_type);
dev_info(&usb_dev->dev, "additional_length = %d\n",
response->additional_length);
dev_info(&usb_dev->dev, "vendor_information = %s\n",
response->vendor_information);
dev_info(&usb_dev->dev, "product_Identification = %s\n",
response->product_Identification);
dev_info(&usb_dev->dev, "product_revision = %s\n",
response->product_revision);
dev_info(&usb_dev->dev, "unknown = %s\n",
response->unknown);
//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;
}
}
inquiry_cmd();
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");