linux USB大容量设备驱动入门之读取U盘容量

主要参考了以下资料: 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");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值