[TOC]
当前已从linux kernel剥离出USB storage驱动代码,并做成源码树形式,可通过ctags或source insight等工具进行解读,路径在git-usb目录下,其中代码已添加必要注释,以下会按照函数流程做主要代码的解读。
函数入口在文件drivers/usb/storage/usb.c中,为最后一行代码:
/* USB storage入口函数 */
module_usb_stor_driver(usb_storage_driver, usb_stor_host_template, DRV_NAME);
这是一个usb storage封装注册函数,其中参数usb_storage_driver即usb storage接口驱动struct usb_driver结构体实例,用于在usb核心层识别usb接口驱动,usb_stor_host_template为scsi_host_template结构体实例,为一个本地变量,用于与scsi层通信,定义如下:
/* struct scsi_host_template结构体实例usb_stor_host_template */
static struct scsi_host_template usb_stor_host_template;
宏定义DRV_NAME如下:
/* 驱动名,后续很多地方用到 */
#define DRV_NAME "usb-storage"
解析该封装函数如下:
#define module_usb_stor_driver(__driver, __sht, __name) \
static int __init __driver##_init(void) \
{ \
usb_stor_host_template_init(&(__sht), __name, THIS_MODULE); \
return usb_register(&(__driver)); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
usb_deregister(&(__driver)); \
} \
module_exit(__driver##_exit)
这其中介绍一下usb_stor_host_template_init()函数,其是初始化usb.c中定义的struct scsi_host_template实例,代码实现在drivers/usb/storage/scsiglue.c中:
/* 初始化struct scsi_host_template结构体实例 */
void usb_stor_host_template_init(struct scsi_host_template *sht,
const char *name, struct module *owner)
{
*sht = usb_stor_host_template;
sht->name = name;
sht->proc_name = name;
sht->module = owner;
}
EXPORT_SYMBOL_GPL(usb_stor_host_template_init);
即将传入的参数sht与具体的实现usb_stor_host_template结构体联结,结构体usb_stor_host_template在该段代码实现之上定义:
/*
* this defines our host template, with which we'll allocate hosts
*/
static const struct scsi_host_template usb_stor_host_template = {
/* basic userland interface stuff */
.name = "usb-storage",
.proc_name = "usb-storage",
.show_info = show_info,
.write_info = write_info,
.info = host_info,
/* command interface -- queued only */
.queuecommand = queuecommand,
/* error and abort handlers */
.eh_abort_handler = command_abort,
.eh_device_reset_handler = device_reset,
.eh_bus_reset_handler = bus_reset,
/* queue commands only, only one command per LUN */
.can_queue = 1,
/* unknown initiator id */
.this_id = -1,
.slave_alloc = slave_alloc,
.slave_configure = slave_configure,
.target_alloc = target_alloc,
/* lots of sg segments can be handled */
.sg_tablesize = SG_MAX_SEGMENTS,
/*
* Limit the total size of a transfer to 120 KB.
*
* Some devices are known to choke with anything larger. It seems like
* the problem stems from the fact that original IDE controllers had
* only an 8-bit register to hold the number of sectors in one transfer
* and even those couldn't handle a full 256 sectors.
*
* Because we want to make sure we interoperate with as many devices as
* possible, we will maintain a 240 sector transfer size limit for USB
* Mass Storage devices.
*
* Tests show that other operating have similar limits with Microsoft
* Windows 7 limiting transfers to 128 sectors for both USB2 and USB3
* and Apple Mac OS X 10.11 limiting transfers to 256 sectors for USB2
* and 2048 for USB3 devices.
*/
.max_sectors = 240,
/*
* merge commands... this seems to help performance, but
* periodically someone should test to see which setting is more
* optimal.
*/
.use_clustering = 1,
/* emulated HBA */
.emulated = 1,
/* we do our own delay after a device or bus reset */
.skip_settle_delay = 1,
/* sysfs device attributes */
.sdev_attrs = sysfs_device_attr_list,
/* module management */
.module = THIS_MODULE
};
这个结构体是usb storage与scsi核心层最最关键的接口,结构体内实现了很多函数,scsi核心层会通过这些函数下发数据(queuecommand()函数),报异常(command_abort()函数),设备重置(device_reset()处理句柄),总线重置(bus_reset()处理句柄)等,还有scsi控制器信息显示相关函数show_info(),write_info(),host_info()等。此处只关注queuecommand()和command_abort()函数,且待后续用到才一一介绍。
现继续跟踪1.1节介绍过的usb_storage_driver结构体实现。
static struct usb_driver usb_storage_driver = {
.name = DRV_NAME,
.probe = storage_probe,
.disconnect = usb_stor_disconnect,
.suspend = usb_stor_suspend,
.resume = usb_stor_resume,
.reset_resume = usb_stor_reset_resume,
.pre_reset = usb_stor_pre_reset,
.post_reset = usb_stor_post_reset,
.id_table = usb_storage_usb_ids,
.supports_autosuspend = 1,
.soft_unbind = 1,
};
这其中主要看三个元素实现:.probe()、.disconnect()、.id_table,其它部分暂时没能力和精力分析清楚。先看id_table对应的实现:usb_storage_usb_ids,代码实现如下:
struct usb_device_id usb_storage_usb_ids[] = {
# include "unusual_devs.h"
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, usb_storage_usb_ids);
unusual_devs,顾名思义,不是通用设备,这个文件不简单,是所有不常用的USB存储设备及常用的USB存储设备的集合,稍后会继续介绍。此处即是对新匹配的设备进行设备,若在设备列表内,则执行probe()匹配函数。进unusual_devs.h文件查看一下,此处该文件在两处使用了。此处作为usb_storage_usb_ids[]结构体内元素第一次使用,另一处在probe()函数中使用:
/* patch submitted by Vivian Bregier <Vivian.Bregier@imag.fr> */
UNUSUAL_DEV( 0x03eb, 0x2002, 0x0100, 0x0100, /* 不常用设备 */
"ATMEL",
"SND1 Storage",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_IGNORE_RESIDUE),
...
/* 通用设备 */
/* Control/Bulk transport for all SubClass values */
USUAL_DEV(USB_SC_RBC, USB_PR_CB),
USUAL_DEV(USB_SC_8020, USB_PR_CB),
USUAL_DEV(USB_SC_QIC, USB_PR_CB),
USUAL_DEV(USB_SC_UFI, USB_PR_CB),
USUAL_DEV(USB_SC_8070, USB_PR_CB),
USUAL_DEV(USB_SC_SCSI, USB_PR_CB),
/* Control/Bulk/Interrupt transport for all SubClass values */
USUAL_DEV(USB_SC_RBC, USB_PR_CBI),
USUAL_DEV(USB_SC_8020, USB_PR_CBI),
USUAL_DEV(USB_SC_QIC, USB_PR_CBI),
USUAL_DEV(USB_SC_UFI, USB_PR_CBI),
USUAL_DEV(USB_SC_8070, USB_PR_CBI),
USUAL_DEV(USB_SC_SCSI, USB_PR_CBI),
/* Bulk-only transport for all SubClass values */
USUAL_DEV(USB_SC_RBC, USB_PR_BULK),
USUAL_DEV(USB_SC_8020, USB_PR_BULK),
USUAL_DEV(USB_SC_QIC, USB_PR_BULK),
USUAL_DEV(USB_SC_UFI, USB_PR_BULK),
USUAL_DEV(USB_SC_8070, USB_PR_BULK),
USUAL_DEV(USB_SC_SCSI, USB_PR_BULK), /* U盘使用此宏定义 */
其中此处的UNUSUAL_DEV()和USUAL_DEV()宏定义在drivers/usb/storage/usual-tables.c中定义,仅用于驱动匹配设备时之用:
/*
* The table of devices
*/
#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
#define COMPLIANT_DEV UNUSUAL_DEV
#define USUAL_DEV(useProto, useTrans) \
{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans) }
...
#undef UNUSUAL_DEV /* 注销宏定义*/
#undef COMPLIANT_DEV
#undef USUAL_DEV
现在开始主要介绍probe()函数,先看函数:
/* The main probe routine for standard devices */
static int storage_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct us_unusual_dev *unusual_dev;
struct us_data *us;
int result;
int size;
/* If uas is enabled and this device can do uas then ignore it. */
#if IS_ENABLED(CONFIG_USB_UAS) /* uas: usb attached scsi protocol */
if (uas_use_uas_driver(intf, id, NULL))
return -ENXIO;
#endif
/*
* If the device isn't standard (is handled by a subdriver
* module) then don't accept it.
*/
if (usb_usual_ignore_device(intf))
return -ENXIO;
/*
* Call the general probe procedures.
*
* The unusual_dev_list array is parallel to the usb_storage_usb_ids
* table, so we use the index of the id entry to find the
* corresponding unusual_devs entry.
*/
size = ARRAY_SIZE(us_unusual_dev_list);
if (id >= usb_storage_usb_ids && id < usb_storage_usb_ids + size) {
unusual_dev = (id - usb_storage_usb_ids) + us_unusual_dev_list;
} else {
unusual_dev = &for_dynamic_ids;
dev_dbg(&intf->dev, "Use Bulk-Only transport with the Transparent SCSI protocol for dynamic id: 0x%04x 0x%04x\n",
id->idVendor, id->idProduct);
}
result = usb_stor_probe1(&us, intf, id, unusual_dev,
&usb_stor_host_template);
if (result)
return result;
/* No special transport or protocol settings in the main module */
result = usb_stor_probe2(us);
return result;
}
现在开始从上往下分析:
storage_probe()函数声明了四个函数变量,代码如下:
struct us_unusual_dev *unusual_dev;
struct us_data *us;
int result;
int size;
其中变量unusual_dev不常用device列表结构体的实例,struct us_unusual_dev结构体定义如下,在probe函数中用于排查不常用device之用:
/*
* Unusual device list definitions
*/
struct us_unusual_dev {
const char* vendorName;
const char* productName;
__u8 useProtocol;
__u8 useTransport;
int (*initFunction)(struct us_data *);
};
struct us_data结构体是USB storage驱动中自定义的贯穿始终的一个非常重要的结构体,相当于一个全局变量了。变量名us即usb storage。 result变量为函数结果收集,size变量作为不常用device集合的个数变量在用。
uas_use_uas_driver()函数,暂时不进行解读,但是UAS/UASP可以简单介绍以下:
函数实现在drivers/usb/storage/usual-tables.c中:
#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ \
.vid = id_vendor, \
.pid = id_product, \
.bcdmin = bcdDeviceMin, \
.bcdmax = bcdDeviceMax, \
}
static struct ignore_entry ignore_ids[] = {
# include "unusual_alauda.h"
# include "unusual_cypress.h"
# include "unusual_datafab.h"
# include "unusual_ene_ub6250.h"
# include "unusual_freecom.h"
# include "unusual_isd200.h"
# include "unusual_jumpshot.h"
# include "unusual_karma.h"
# include "unusual_onetouch.h"
# include "unusual_realtek.h"
# include "unusual_sddr09.h"
# include "unusual_sddr55.h"
# include "unusual_usbat.h"
{ } /* Terminating entry */
};
#undef UNUSUAL_DEV
/* Return an error if a device is in the ignore_ids list */
int usb_usual_ignore_device(struct usb_interface *intf)
{
struct usb_device *udev;
unsigned vid, pid, bcd;
struct ignore_entry *p;
udev = interface_to_usbdev(intf);
vid = le16_to_cpu(udev->descriptor.idVendor);
pid = le16_to_cpu(udev->descriptor.idProduct);
bcd = le16_to_cpu(udev->descriptor.bcdDevice);
for (p = ignore_ids; p->vid; ++p) {
if (p->vid == vid && p->pid == pid &&
p->bcdmin <= bcd && p->bcdmax >= bcd)
return -ENXIO;
}
return 0;
}
此处又重新定义了一遍UNUSUAL_DEV宏定义,usb_usual_ignore_device()函数的功能即是排查匹配上的设备,如果不是标准device,则退出此通用USB storage驱动,而去匹配对应的子模块驱动。此处也不做深究。
接下来是对变量unusual_dev的赋值,代码如下:
/*
* Call the general probe procedures.
*
* The unusual_dev_list array is parallel to the usb_storage_usb_ids
* table, so we use the index of the id entry to find the
* corresponding unusual_devs entry.
*/
size = ARRAY_SIZE(us_unusual_dev_list);
if (id >= usb_storage_usb_ids && id < usb_storage_usb_ids + size) {
unusual_dev = (id - usb_storage_usb_ids) + us_unusual_dev_list;
} else {
unusual_dev = &for_dynamic_ids;
dev_dbg(&intf->dev, "Use Bulk-Only transport with the Transparent SCSI protocol for dynamic id: 0x%04x 0x%04x\n",
id->idVendor, id->idProduct);
}
其中us_unusual_dev_list为usb.c中定义的静态变量,代码如下:
/*
* The entries in this table correspond, line for line,
* with the entries in usb_storage_usb_ids[], defined in usual-tables.c.
*/
/*
*The vendor name should be kept at eight characters or less, and
* the product name should be kept at 16 characters or less. If a device
* has the US_FL_FIX_INQUIRY flag, then the vendor and product names
* normally generated by a device through the INQUIRY response will be
* taken from this list, and this is the reason for the above size
* restriction. However, if the flag is not present, then you
* are free to use as many characters as you like.
*/
#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \
vendor_name, product_name, use_protocol, use_transport, \
init_function, Flags) \
{ \
.vendorName = vendor_name, \
.productName = product_name, \
.useProtocol = use_protocol, \
.useTransport = use_transport, \
.initFunction = init_function, \
}
#define COMPLIANT_DEV UNUSUAL_DEV
#define USUAL_DEV(use_protocol, use_transport) \
{ \
.useProtocol = use_protocol, \
.useTransport = use_transport, \
}
#define UNUSUAL_VENDOR_INTF(idVendor, cl, sc, pr, \
vendor_name, product_name, use_protocol, use_transport, \
init_function, Flags) \
{ \
.vendorName = vendor_name, \
.productName = product_name, \
.useProtocol = use_protocol, \
.useTransport = use_transport, \
.initFunction = init_function, \
}
static struct us_unusual_dev us_unusual_dev_list[] = {
# include "unusual_devs.h"
{ } /* Terminating entry */
};
static struct us_unusual_dev for_dynamic_ids =
USUAL_DEV(USB_SC_SCSI, USB_PR_BULK);
#undef UNUSUAL_DEV
#undef COMPLIANT_DEV
#undef USUAL_DEV
#undef UNUSUAL_VENDOR_INTF
此处做了以下几件事:
函数实现如下,稍后各小节再逐条进行分析:
/* First part of general USB mass-storage probing */
int usb_stor_probe1(struct us_data **pus,
struct usb_interface *intf,
const struct usb_device_id *id,
struct us_unusual_dev *unusual_dev,
struct scsi_host_template *sht)
{
struct Scsi_Host *host;
struct us_data *us;
int result;
dev_info(&intf->dev, "USB Mass Storage device detected\n");
/*
* Ask the SCSI layer to allocate a host structure, with extra
* space at the end for our private us_data structure.
*/
host = scsi_host_alloc(sht, sizeof(*us));
if (!host) {
dev_warn(&intf->dev, "Unable to allocate the scsi host\n");
return -ENOMEM;
}
/*
* Allow 16-byte CDBs and thus > 2TB
*/
host->max_cmd_len = 16;
host->sg_tablesize = usb_stor_sg_tablesize(intf);
*pus = us = host_to_us(host);
mutex_init(&(us->dev_mutex));
us_set_lock_class(&us->dev_mutex, intf);
init_completion(&us->cmnd_ready);
init_completion(&(us->notify));
init_waitqueue_head(&us->delay_wait);
INIT_DELAYED_WORK(&us->scan_dwork, usb_stor_scan_dwork);
/* Associate the us_data structure with the USB device */
result = associate_dev(us, intf);
if (result)
goto BadDevice;
/* Get the unusual_devs entries and the descriptors */
result = get_device_info(us, id, unusual_dev);
if (result)
goto BadDevice;
/* Get standard transport and protocol settings */
get_transport(us);
get_protocol(us);
/*
* Give the caller a chance to fill in specialized transport
* or protocol settings.
*/
return 0;
BadDevice:
usb_stor_dbg(us, "storage_probe() failed\n");
release_everything(us);
return result;
}
EXPORT_SYMBOL_GPL(usb_stor_probe1);
该函数开头定义了三个变量:
struct Scsi_Host *host;
struct us_data *us;
int result;
struct Scsi_Host结构体实例host,此处简单分析一下struct scsi_host_template和struct Scsi_Host结构体的关系,在Linux中,每一个scsi主机控制器对应一个数据结构,Scsi_Host(而Linux中将通过使用一个scsi_host_template结构指针为参数的函数来为Scsi_Host初始化,scsi_host_template实现控制器的操作函数以及命令封装,两个结构体包含了很多相同的元素,但又不完全相同,他们协同工作,互相关联,但是各自起的作用不一样,且Scsi_Host有且仅有一个struct scsi_host_template),但有些Scsi_Host对应的并非是真实的scsi卡,虽然硬件是并不存在,但仍然需要一个Scsi_Host,如U盘,因为她被模拟成SCSI设备,所以得为她准备一个SCSI卡,这个可以在插入U盘后,通过cat /proc/scsi/scsi的输出来了解:
$ cat /proc/scsi/scsi
Attached devices:
Host: scsi1 Channel: 00 Id: 00 Lun: 00
Vendor: ATA Model: WDC WD10JPVX-22J Rev: 1A01
Type: Direct-Access ANSI SCSI revision: 05
Host: scsi3 Channel: 00 Id: 00 Lun: 00
Vendor: TSSTcorp Model: CDDVDW SH-224DB Rev: CM00
Type: CD-ROM ANSI SCSI revision: 05
Host: scsi4 Channel: 00 Id: 00 Lun: 00
Vendor: Netac Model: OnlyDisk Rev: 1.0
Type: Direct-Access ANSI SCSI revision: 02
当前在/proc/scsi目录下还会因为插入U盘后,动态的生成usb-storage目录,里面即是每个新插入的U盘的一些信息,如当前插入的U盘,显示为4,其内信息如下:
$ cat /proc/scsi/usb-storage/4
Host scsi4: usb-storage
Vendor: Netac
Product: OnlyDisk
Serial Number: 43A02D3FDE34408D
Protocol: Transparent SCSI
Transport: Bulk
Quirks:
struct us_data已经介绍,一个usb storage驱动实现的结构体,非常的重要。int result作为函数结构检查;
通过将&usb_stor_host_template作为参数传给scsi_host_alloc()函数,分配一个struct Scsi_Host结构体实例,以及相应内存等,此处将us也作为参数传入,是在host的unsigned long hostdata[0]元素中分配了struct us_data结构体内存,作为其私有数据以供后续使用。(此处不继续深究scsi_host_alloc()实现)
/*
* Ask the SCSI layer to allocate a host structure, with extra
* space at the end for our private us_data structure.
*/
host = scsi_host_alloc(sht, sizeof(*us));
if (!host) {
dev_warn(&intf->dev, "Unable to allocate the scsi host\n");
return -ENOMEM;
}
/*
* Allow 16-byte CDBs and thus > 2TB
*/
host->max_cmd_len = 16;
host->sg_tablesize = usb_stor_sg_tablesize(intf);
*pus = us = host_to_us(host);
mutex_init(&(us->dev_mutex));
us_set_lock_class(&us->dev_mutex, intf);
init_completion(&us->cmnd_ready);
init_completion(&(us->notify));
init_waitqueue_head(&us->delay_wait);
INIT_DELAYED_WORK(&us->scan_dwork, usb_stor_scan_dwork);
定义host的元素max_cmd_len为16,以及sg_tablesize大小,通过intf对应的usb设备所在的总线支持的sg_tablesize大小:
static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf)
{
struct usb_device *usb_dev = interface_to_usbdev(intf);
if (usb_dev->bus->sg_tablesize) {
return usb_dev->bus->sg_tablesize;
}
return SG_ALL;
}
将函数传入的struct us_data二级指针参数pus与本函数内定义的us和host相关联:
*pus = us = host_to_us(host);
接下来则是各类锁,信号量,等待队列,延迟工作队列的初始化:
mutex_init(&(us->dev_mutex));
us_set_lock_class(&us->dev_mutex, intf);
init_completion(&us->cmnd_ready);
init_completion(&(us->notify));
init_waitqueue_head(&us->delay_wait);
INIT_DELAYED_WORK(&us->scan_dwork, usb_stor_scan_dwork);
此处需要注意的是us->cmnd_ready和us->notify,其在scsi cmd和abort传递上实现了内核同步机制。此处初始化了一个延迟工作delay_work,稍后在调用的地方再讲解这个实现的函数:usb_stor_scan_dwork()。
/* Associate the us_data structure with the USB device */
result = associate_dev(us, intf);
if (result)
goto BadDevice;
正如注释所说,是将USB device和us变量联结起来,代码实现如下:
/* Associate our private data with the USB device */
static int associate_dev(struct us_data *us, struct usb_interface *intf)
{
/* Fill in the device-related fields */
us->pusb_dev = interface_to_usbdev(intf);
us->pusb_intf = intf;
us->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
usb_stor_dbg(us, "Vendor: 0x%04x, Product: 0x%04x, Revision: 0x%04x\n",
le16_to_cpu(us->pusb_dev->descriptor.idVendor),
le16_to_cpu(us->pusb_dev->descriptor.idProduct),
le16_to_cpu(us->pusb_dev->descriptor.bcdDevice));
usb_stor_dbg(us, "Interface Subclass: 0x%02x, Protocol: 0x%02x\n",
intf->cur_altsetting->desc.bInterfaceSubClass,
intf->cur_altsetting->desc.bInterfaceProtocol);
/* Store our private data in the interface */
usb_set_intfdata(intf, us);
/* Allocate the control/setup and DMA-mapped buffers */
us->cr = kmalloc(sizeof(*us->cr), GFP_KERNEL);
if (!us->cr)
return -ENOMEM;
us->iobuf = usb_alloc_coherent(us->pusb_dev, US_IOBUF_SIZE,
GFP_KERNEL, &us->iobuf_dma);
if (!us->iobuf) {
usb_stor_dbg(us, "I/O buffer allocation failed\n");
return -ENOMEM;
}
return 0;
}
这其中做了以下几件事:
/* Allocate the control/setup and DMA-mapped buffers */
us->cr = kmalloc(sizeof(*us->cr), GFP_KERNEL);
if (!us->cr)
return -ENOMEM;
/* Get the unusual_devs entries and the descriptors */
result = get_device_info(us, id, unusual_dev);
if (result)
goto BadDevice;
get_device_info()实现如下,用于获取USB device的硬件信息:
/* Get the unusual_devs entries and the string descriptors */
static int get_device_info(struct us_data *us, const struct usb_device_id *id,
struct us_unusual_dev *unusual_dev)
{
struct usb_device *dev = us->pusb_dev;
struct usb_interface_descriptor *idesc =
&us->pusb_intf->cur_altsetting->desc;
struct device *pdev = &us->pusb_intf->dev;
/* Store the entries */
us->unusual_dev = unusual_dev;
us->subclass = (unusual_dev->useProtocol == USB_SC_DEVICE) ?
idesc->bInterfaceSubClass :
unusual_dev->useProtocol;
us->protocol = (unusual_dev->useTransport == USB_PR_DEVICE) ?
idesc->bInterfaceProtocol :
unusual_dev->useTransport;
us->fflags = id->driver_info;
usb_stor_adjust_quirks(us->pusb_dev, &us->fflags);
if (us->fflags & US_FL_IGNORE_DEVICE) {
dev_info(pdev, "device ignored\n");
return -ENODEV;
}
/*
* This flag is only needed when we're in high-speed, so let's
* disable it if we're in full-speed
*/
if (dev->speed != USB_SPEED_HIGH)
us->fflags &= ~US_FL_GO_SLOW;
if (us->fflags)
dev_info(pdev, "Quirks match for vid %04x pid %04x: %lx\n",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct),
us->fflags);
/*
* Log a message if a non-generic unusual_dev entry contains an
* unnecessary subclass or protocol override. This may stimulate
* reports from users that will help us remove unneeded entries
* from the unusual_devs.h table.
*/
if (id->idVendor || id->idProduct) {
static const char *msgs[3] = {
"an unneeded SubClass entry",
"an unneeded Protocol entry",
"unneeded SubClass and Protocol entries"};
struct usb_device_descriptor *ddesc = &dev->descriptor;
int msg = -1;
if (unusual_dev->useProtocol != USB_SC_DEVICE &&
us->subclass == idesc->bInterfaceSubClass)
msg += 1;
if (unusual_dev->useTransport != USB_PR_DEVICE &&
us->protocol == idesc->bInterfaceProtocol)
msg += 2;
if (msg >= 0 && !(us->fflags & US_FL_NEED_OVERRIDE))
dev_notice(pdev, "This device "
"(%04x,%04x,%04x S %02x P %02x)"
" has %s in unusual_devs.h (kernel"
" %s)\n"
" Please send a copy of this message to "
"<linux-usb@vger.kernel.org> and "
"<usb-storage@lists.one-eyed-alien.net>\n",
le16_to_cpu(ddesc->idVendor),
le16_to_cpu(ddesc->idProduct),
le16_to_cpu(ddesc->bcdDevice),
idesc->bInterfaceSubClass,
idesc->bInterfaceProtocol,
msgs[msg],
utsname()->release);
}
return 0;
}
该函数做了以下处理:
/*
* Nick Bowler <nbowler@elliptictech.com>
* SCSI stack spams (otherwise harmless) error messages.
*/
UNUSUAL_DEV( 0xc251, 0x4003, 0x0100, 0x0100,
"Keil Software, Inc.",
"V2M MotherBoard",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NOT_LOCKABLE),
USUAL_DEV(USB_SC_SCSI, USB_PR_BULK),
这其中,当为unusual_dev时,则unusual_dev->useProtocol == USB_SC_DEVICE,所以us->subclass就等于了idesc->bInterfaceSubClass,而当为通用存储设备时,则us->subclass等于了unusual_dev->useProtocol,同理us->protocol也一样,而us->fflags对应的即是US_FL_NOT_LOCKABLE,这宏定义在include/linux/usb_usual.h中,:
#define US_DO_ALL_FLAGS \
US_FLAG(SINGLE_LUN, 0x00000001) \
/* allow access to only LUN 0 */ \
US_FLAG(NEED_OVERRIDE, 0x00000002) \
/* unusual_devs entry is necessary */ \
US_FLAG(SCM_MULT_TARG, 0x00000004) \
/* supports multiple targets */ \
US_FLAG(FIX_INQUIRY, 0x00000008) \
/* INQUIRY response needs faking */ \
US_FLAG(FIX_CAPACITY, 0x00000010) \
/* READ CAPACITY response too big */ \
US_FLAG(IGNORE_RESIDUE, 0x00000020) \
/* reported residue is wrong */ \
US_FLAG(BULK32, 0x00000040) \
/* Uses 32-byte CBW length */ \
US_FLAG(NOT_LOCKABLE, 0x00000080) \
/* PREVENT/ALLOW not supported */ \
US_FLAG(GO_SLOW, 0x00000100) \
/* Need delay after Command phase */ \
US_FLAG(NO_WP_DETECT, 0x00000200) \
/* Don't check for write-protect */ \
US_FLAG(MAX_SECTORS_64, 0x00000400) \
/* Sets max_sectors to 64 */ \
US_FLAG(IGNORE_DEVICE, 0x00000800) \
/* Don't claim device */ \
US_FLAG(CAPACITY_HEURISTICS, 0x00001000) \
/* sometimes sizes is too big */ \
US_FLAG(MAX_SECTORS_MIN,0x00002000) \
/* Sets max_sectors to arch min */ \
US_FLAG(BULK_IGNORE_TAG,0x00004000) \
/* Ignore tag mismatch in bulk operations */ \
US_FLAG(SANE_SENSE, 0x00008000) \
/* Sane Sense (> 18 bytes) */ \
US_FLAG(CAPACITY_OK, 0x00010000) \
/* READ CAPACITY response is correct */ \
US_FLAG(BAD_SENSE, 0x00020000) \
/* Bad Sense (never more than 18 bytes) */ \
US_FLAG(NO_READ_DISC_INFO, 0x00040000) \
/* cannot handle READ_DISC_INFO */ \
US_FLAG(NO_READ_CAPACITY_16, 0x00080000) \
/* cannot handle READ_CAPACITY_16 */ \
US_FLAG(INITIAL_READ10, 0x00100000) \
/* Initial READ(10) (and others) must be retried */ \
US_FLAG(WRITE_CACHE, 0x00200000) \
/* Write Cache status is not available */ \
US_FLAG(NEEDS_CAP16, 0x00400000) \
/* cannot handle READ_CAPACITY_10 */ \
US_FLAG(IGNORE_UAS, 0x00800000) \
/* Device advertises UAS but it is broken */ \
US_FLAG(BROKEN_FUA, 0x01000000) \
/* Cannot handle FUA in WRITE or READ CDBs */ \
US_FLAG(NO_ATA_1X, 0x02000000) \
/* Cannot handle ATA_12 or ATA_16 CDBs */ \
US_FLAG(NO_REPORT_OPCODES, 0x04000000) \
/* Cannot handle MI_REPORT_SUPPORTED_OPERATION_CODES */ \
US_FLAG(MAX_SECTORS_240, 0x08000000) \
/* Sets max_sectors to 240 */ \
US_FLAG(NO_REPORT_LUNS, 0x10000000) \
/* Cannot handle REPORT_LUNS */ \
US_FLAG(ALWAYS_SYNC, 0x20000000) \
/* lies about caching, so always sync */ \
#define US_FLAG(name, value) US_FL_##name = value ,
enum { US_DO_ALL_FLAGS };
#undef US_FLAG
该函数的功能是向内核申请了一个/sys接口,路径是/sys/module/usb_storage/parameters/quirks,用户可以通过实时动态的对某些USB mass storage device添加限制标记。先看代码实现,再介绍如何添加特定设备限定标记。
代码实现下:
/* Works only for digits and letters, but small and fast */
#define TOLOWER(x) ((x) | 0x20)
/* Adjust device flags based on the "quirks=" module parameter */
void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags)
{
char *p;
u16 vid = le16_to_cpu(udev->descriptor.idVendor);
u16 pid = le16_to_cpu(udev->descriptor.idProduct);
unsigned f = 0;
unsigned int mask = (US_FL_SANE_SENSE | US_FL_BAD_SENSE |
US_FL_FIX_CAPACITY | US_FL_IGNORE_UAS |
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 |
US_FL_NO_READ_DISC_INFO | US_FL_NO_READ_CAPACITY_16 |
US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE |
US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES |
US_FL_MAX_SECTORS_240 | US_FL_NO_REPORT_LUNS |
US_FL_ALWAYS_SYNC);
p = quirks;
while (*p) {
/* Each entry consists of VID:PID:flags */
if (vid == simple_strtoul(p, &p, 16) &&
*p == ':' &&
pid == simple_strtoul(p+1, &p, 16) &&
*p == ':')
break;
/* Move forward to the next entry */
while (*p) {
if (*p++ == ',')
break;
}
}
if (!*p) /* No match */
return;
/* Collect the flags */
while (*++p && *p != ',') {
switch (TOLOWER(*p)) {
case 'a':
f |= US_FL_SANE_SENSE;
break;
case 'b':
f |= US_FL_BAD_SENSE;
break;
case 'c':
f |= US_FL_FIX_CAPACITY;
break;
case 'd':
f |= US_FL_NO_READ_DISC_INFO;
break;
case 'e':
f |= US_FL_NO_READ_CAPACITY_16;
break;
case 'f':
f |= US_FL_NO_REPORT_OPCODES;
break;
case 'g':
f |= US_FL_MAX_SECTORS_240;
break;
case 'h':
f |= US_FL_CAPACITY_HEURISTICS;
break;
case 'i':
f |= US_FL_IGNORE_DEVICE;
break;
case 'j':
f |= US_FL_NO_REPORT_LUNS;
break;
case 'l':
f |= US_FL_NOT_LOCKABLE;
break;
case 'm':
f |= US_FL_MAX_SECTORS_64;
break;
case 'n':
f |= US_FL_INITIAL_READ10;
break;
case 'o':
f |= US_FL_CAPACITY_OK;
break;
case 'p':
f |= US_FL_WRITE_CACHE;
break;
case 'r':
f |= US_FL_IGNORE_RESIDUE;
break;
case 's':
f |= US_FL_SINGLE_LUN;
break;
case 't':
f |= US_FL_NO_ATA_1X;
break;
case 'u':
f |= US_FL_IGNORE_UAS;
break;
case 'w':
f |= US_FL_NO_WP_DETECT;
break;
case 'y':
f |= US_FL_ALWAYS_SYNC;
break;
/* Ignore unrecognized flag characters */
}
}
*fflags = (*fflags & ~mask) | f;
}
EXPORT_SYMBOL_GPL(usb_stor_adjust_quirks);
这部分代码实现的流程如下:
static char quirks[128];
module_param_string(quirks, quirks, sizeof(quirks), S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks");
下面以US_FL_IGNORE_DEVICE标记简单介绍一下如何操作quirks接口:
# lsusb
Bus 001 Device 015: ID 0930:6545 Toshiba Corp. Kingston DataTraveler 102/2.0 / HEMA Flash Drive 2 GB / PNY Attache 4GB Stick
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 002: ID 0d8c:0105 C-Media Electronics, Inc. CM108 Audio Controller
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 002 Device 010: ID 058f:6387 Alcor Micro Corp. Flash Drive
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 005 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 004 Device 003: ID 046d:c077 Logitech, Inc. M105 Optical Mouse
Bus 004 Device 002: ID 04f3:0103 Elan Microelectronics Corp. ActiveJet K-2024 Multimedia Keyboard
Bus 004 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
~# echo "058f:6387:i,0930:6545:i" > /sys/module/usb_storage/parameters/quirks
~# cat /sys/module/usb_storage/parameters/quirks
058f:6387:i,0930:6545:i
[97564.445606] usb 2-1: new full-speed USB device number 11 using ohci-pci
[97564.660644] usb 2-1: not running at top speed; connect to a high speed hub
[97564.676651] usb 2-1: New USB device found, idVendor=058f, idProduct=6387
[97564.676659] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[97564.676663] usb 2-1: Product: Mass Storage
[97564.676668] usb 2-1: Manufacturer: Generic
[97564.676672] usb 2-1: SerialNumber: 0A0067B7
[97564.676681] device: '2-1': device_add
[97564.676751] bus: 'usb': add device 2-1
[97564.677410] bus: 'usb': driver_probe_device: matched device 2-1 with driver usb
[97564.677416] bus: 'usb': really_probe: probing driver usb with device 2-1
[97564.677427] devices_kset: Moving 2-1 to end of list
[97564.678649] device: '2-1:1.0': device_add
[97564.678683] bus: 'usb': add device 2-1:1.0
[97564.678744] bus: 'usb': driver_probe_device: matched device 2-1:1.0 with driver usb-storage
[97564.678748] bus: 'usb': really_probe: probing driver usb-storage with device 2-1:1.0
[97564.678757] devices_kset: Moving 2-1:1.0 to end of list
[97564.678768] usb-storage 2-1:1.0: USB Mass Storage device detected
[97564.678876] usb-storage 2-1:1.0: device ignored
[97564.678931] usb-storage: probe of 2-1:1.0 rejects match -19
[97564.679008] device: 'ep_01': device_add
[97564.679033] device: 'ep_82': device_add
[97564.679073] driver: 'usb': driver_bound: bound to device '2-1'
[97564.679081] bus: 'usb': really_probe: bound device 2-1 to driver usb
[97564.679094] device: 'ep_00': device_add
get_device_info()剩下部分代码主要是对udev和unusual_dev做判断:
if (us->fflags & US_FL_IGNORE_DEVICE) {
dev_info(pdev, "device ignored\n");
return -ENODEV;
}
/*
* This flag is only needed when we're in high-speed, so let's
* disable it if we're in full-speed
*/
if (dev->speed != USB_SPEED_HIGH)
us->fflags &= ~US_FL_GO_SLOW;
if (us->fflags)
dev_info(pdev, "Quirks match for vid %04x pid %04x: %lx\n",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct),
us->fflags);
至此,get_device_info()函数分析完成。
/* Get standard transport and protocol settings */
get_transport(us);
get_protocol(us);
先简单介绍下这两个函数,后续在USB storage内核线程的地方再详细介绍;
该函数为us->transport及us->transport_reset填充了处理句柄:
/* Get the transport settings */
static void get_transport(struct us_data *us)
{
switch (us->protocol) {
case USB_PR_CB:
us->transport_name = "Control/Bulk";
us->transport = usb_stor_CB_transport;
us->transport_reset = usb_stor_CB_reset;
us->max_lun = 7;
break;
case USB_PR_CBI:
us->transport_name = "Control/Bulk/Interrupt";
us->transport = usb_stor_CB_transport;
us->transport_reset = usb_stor_CB_reset;
us->max_lun = 7;
break;
case USB_PR_BULK: /* USB mass storage调用 */
us->transport_name = "Bulk";
us->transport = usb_stor_Bulk_transport;
us->transport_reset = usb_stor_Bulk_reset;
break;
}
}
该函数为us->proto_handler填充了处理句柄:
/* Get the protocol settings */
static void get_protocol(struct us_data *us)
{
switch (us->subclass) {
case USB_SC_RBC:
us->protocol_name = "Reduced Block Commands (RBC)";
us->proto_handler = usb_stor_transparent_scsi_command;
break;
case USB_SC_8020:
us->protocol_name = "8020i";
us->proto_handler = usb_stor_pad12_command;
us->max_lun = 0;
break;
case USB_SC_QIC:
us->protocol_name = "QIC-157";
us->proto_handler = usb_stor_pad12_command;
us->max_lun = 0;
break;
case USB_SC_8070:
us->protocol_name = "8070i";
us->proto_handler = usb_stor_pad12_command;
us->max_lun = 0;
break;
case USB_SC_SCSI: /* USB mass storage遵循scsi传输协议 */
us->protocol_name = "Transparent SCSI";
us->proto_handler = usb_stor_transparent_scsi_command;
break;
case USB_SC_UFI:
us->protocol_name = "Uniform Floppy Interface (UFI)";
us->proto_handler = usb_stor_ufi_command;
break;
}
}
至此usb_stor_probe1()函数介绍完全,返回其上级调用函数。
/* No special transport or protocol settings in the main module */
result = usb_stor_probe2(us);
函数实现如下,其后各节开始逐步解析该函数实现:
/* Second part of general USB mass-storage probing */
int usb_stor_probe2(struct us_data *us)
{
int result;
struct device *dev = &us->pusb_intf->dev;
/* Make sure the transport and protocol have both been set */
if (!us->transport || !us->proto_handler) {
result = -ENXIO;
goto BadDevice;
}
usb_stor_dbg(us, "Transport: %s\n", us->transport_name);
usb_stor_dbg(us, "Protocol: %s\n", us->protocol_name);
if (us->fflags & US_FL_SCM_MULT_TARG) {
/*
* SCM eUSCSI bridge devices can have different numbers
* of LUNs on different targets; allow all to be probed.
*/
us->max_lun = 7;
/* The eUSCSI itself has ID 7, so avoid scanning that */
us_to_host(us)->this_id = 7;
/* max_id is 8 initially, so no need to set it here */
} else {
/* In the normal case there is only a single target */
us_to_host(us)->max_id = 1;
/*
* Like Windows, we won't store the LUN bits in CDB[1] for
* SCSI-2 devices using the Bulk-Only transport (even though
* this violates the SCSI spec).
*/
if (us->transport == usb_stor_Bulk_transport)
us_to_host(us)->no_scsi2_lun_in_cdb = 1;
}
/* fix for single-lun devices */
if (us->fflags & US_FL_SINGLE_LUN)
us->max_lun = 0;
/* Find the endpoints and calculate pipe values */
result = get_pipes(us);
if (result)
goto BadDevice;
/*
* If the device returns invalid data for the first READ(10)
* command, indicate the command should be retried.
*/
if (us->fflags & US_FL_INITIAL_READ10)
set_bit(US_FLIDX_REDO_READ10, &us->dflags);
/* Acquire all the other resources and add the host */
result = usb_stor_acquire_resources(us);
if (result)
goto BadDevice;
usb_autopm_get_interface_no_resume(us->pusb_intf);
snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s",
dev_name(&us->pusb_intf->dev));
result = scsi_add_host(us_to_host(us), dev);
if (result) {
dev_warn(dev,
"Unable to add the scsi host\n");
goto HostAddErr;
}
/* Submit the delayed_work for SCSI-device scanning */
set_bit(US_FLIDX_SCAN_PENDING, &us->dflags);
if (delay_use > 0)
dev_dbg(dev, "waiting for device to settle before scanning\n");
queue_delayed_work(system_freezable_wq, &us->scan_dwork,
delay_use * HZ);
return 0;
/* We come here if there are any problems */
HostAddErr:
usb_autopm_put_interface_no_suspend(us->pusb_intf);
BadDevice:
usb_stor_dbg(us, "storage_probe() failed\n");
release_everything(us);
return result;
}
EXPORT_SYMBOL_GPL(usb_stor_probe2);
该函数只声明了两个变量,result为结果收集,dev为接口device;
/* Make sure the transport and protocol have both been set */
if (!us->transport || !us->proto_handler) {
result = -ENXIO;
goto BadDevice;
}
usb_stor_dbg(us, "Transport: %s\n", us->transport_name);
usb_stor_dbg(us, "Protocol: %s\n", us->protocol_name);
if (us->fflags & US_FL_SCM_MULT_TARG) {
/*
* SCM eUSCSI bridge devices can have different numbers
* of LUNs on different targets; allow all to be probed.
*/
us->max_lun = 7;
/* The eUSCSI itself has ID 7, so avoid scanning that */
us_to_host(us)->this_id = 7;
/* max_id is 8 initially, so no need to set it here */
} else {
/* In the normal case there is only a single target */
us_to_host(us)->max_id = 1;
/*
* Like Windows, we won't store the LUN bits in CDB[1] for
* SCSI-2 devices using the Bulk-Only transport (even though
* this violates the SCSI spec).
*/
if (us->transport == usb_stor_Bulk_transport)
us_to_host(us)->no_scsi2_lun_in_cdb = 1;
}
/* fix for single-lun devices */
if (us->fflags & US_FL_SINGLE_LUN)
us->max_lun = 0;
此部分做了以下几件事:
/* Find the endpoints and calculate pipe values */
result = get_pipes(us);
if (result)
goto BadDevice;
找到USB mass storage device所在接口上的的端点,并依此计算出pipe值;代码实现如下,以下各小节对其中各部分进行细致分析:
/* Get the pipe settings */
static int get_pipes(struct us_data *us)
{
struct usb_host_interface *alt = us->pusb_intf->cur_altsetting;
struct usb_endpoint_descriptor *ep_in;
struct usb_endpoint_descriptor *ep_out;
struct usb_endpoint_descriptor *ep_int;
int res;
/*
* Find the first endpoint of each type we need.
* We are expecting a minimum of 2 endpoints - in and out (bulk).
* An optional interrupt-in is OK (necessary for CBI protocol).
* We will ignore any others.
*/
res = usb_find_common_endpoints(alt, &ep_in, &ep_out, NULL, NULL);
if (res) {
usb_stor_dbg(us, "bulk endpoints not found\n");
return res;
}
res = usb_find_int_in_endpoint(alt, &ep_int);
if (res && us->protocol == USB_PR_CBI) {
usb_stor_dbg(us, "interrupt endpoint not found\n");
return res;
}
/* Calculate and store the pipe values */
us->send_ctrl_pipe = usb_sndctrlpipe(us->pusb_dev, 0);
us->recv_ctrl_pipe = usb_rcvctrlpipe(us->pusb_dev, 0);
us->send_bulk_pipe = usb_sndbulkpipe(us->pusb_dev,
usb_endpoint_num(ep_out));
us->recv_bulk_pipe = usb_rcvbulkpipe(us->pusb_dev,
usb_endpoint_num(ep_in));
if (ep_int) {
us->recv_intr_pipe = usb_rcvintpipe(us->pusb_dev,
usb_endpoint_num(ep_int));
us->ep_bInterval = ep_int->bInterval;
}
return 0;
}
struct usb_host_interface *alt = us->pusb_intf->cur_altsetting;
struct usb_endpoint_descriptor *ep_in;
struct usb_endpoint_descriptor *ep_out;
struct usb_endpoint_descriptor *ep_int;
int res;
alt变量为匹配上的USB接口下的当前接口设定结构体实例,可通过该变量找到端点及端点描述符;ep_in、ep_out、ep_int是端点描述符实例;res为结果收集。
/*
* Find the first endpoint of each type we need.
* We are expecting a minimum of 2 endpoints - in and out (bulk).
* An optional interrupt-in is OK (necessary for CBI protocol).
* We will ignore any others.
*/
res = usb_find_common_endpoints(alt, &ep_in, &ep_out, NULL, NULL);
if (res) {
usb_stor_dbg(us, "bulk endpoints not found\n");
return res;
}
这个函数的目的是找到bulk传输类型的ep_in和ep_out端点,因为USB mass storage主要就是bulk传输(一般只会实现bulk端点)。函数usb_find_common_endpoints()是USB core层实现的通用函数,主要是寻找alt接口下bulk端点和int端点;代码实现如下:
/**
* usb_find_common_endpoints() -- look up common endpoint descriptors
* @alt: alternate setting to search
* @bulk_in: pointer to descriptor pointer, or NULL
* @bulk_out: pointer to descriptor pointer, or NULL
* @int_in: pointer to descriptor pointer, or NULL
* @int_out: pointer to descriptor pointer, or NULL
*
* Search the alternate setting's endpoint descriptors for the first bulk-in,
* bulk-out, interrupt-in and interrupt-out endpoints and return them in the
* provided pointers (unless they are NULL).
*
* If a requested endpoint is not found, the corresponding pointer is set to
* NULL.
*
* Return: Zero if all requested descriptors were found, or -ENXIO otherwise.
*/
int usb_find_common_endpoints(struct usb_host_interface *alt,
struct usb_endpoint_descriptor **bulk_in,
struct usb_endpoint_descriptor **bulk_out,
struct usb_endpoint_descriptor **int_in,
struct usb_endpoint_descriptor **int_out)
{
struct usb_endpoint_descriptor *epd;
int i;
if (bulk_in)
*bulk_in = NULL;
if (bulk_out)
*bulk_out = NULL;
if (int_in)
*int_in = NULL;
if (int_out)
*int_out = NULL;
for (i = 0; i < alt->desc.bNumEndpoints; ++i) {
epd = &alt->endpoint[i].desc;
if (match_endpoint(epd, bulk_in, bulk_out, int_in, int_out))
return 0;
}
return -ENXIO;
}
EXPORT_SYMBOL_GPL(usb_find_common_endpoints);
此处传入的参数为alt,ep_out,ep_in;对应函数内的alt、bulk_in和bulk_out;继续分析:
static bool match_endpoint(struct usb_endpoint_descriptor *epd,
struct usb_endpoint_descriptor **bulk_in,
struct usb_endpoint_descriptor **bulk_out,
struct usb_endpoint_descriptor **int_in,
struct usb_endpoint_descriptor **int_out)
{
switch (usb_endpoint_type(epd)) {
case USB_ENDPOINT_XFER_BULK:
if (usb_endpoint_dir_in(epd)) {
if (bulk_in && !*bulk_in) {
*bulk_in = epd;
break;
}
} else {
if (bulk_out && !*bulk_out) {
*bulk_out = epd;
break;
}
}
return false;
case USB_ENDPOINT_XFER_INT:
if (usb_endpoint_dir_in(epd)) {
if (int_in && !*int_in) {
*int_in = epd;
break;
}
} else {
if (int_out && !*int_out) {
*int_out = epd;
break;
}
}
return false;
default:
return false;
}
return (!bulk_in || *bulk_in) && (!bulk_out || *bulk_out) &&
(!int_in || *int_in) && (!int_out || *int_out);
}
该函数的功能如下:
#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
#define USB_ENDPOINT_XFER_BULK 2
#define USB_ENDPOINT_XFER_INT 3
/**
* usb_endpoint_type - get the endpoint's transfer type
* @epd: endpoint to be checked
*
* Returns one of USB_ENDPOINT_XFER_{CONTROL, ISOC, BULK, INT} according
* to @epd's transfer type.
*/
static inline int usb_endpoint_type(const struct usb_endpoint_descriptor *epd)
{
return epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
}
即通过端点描述符下的bmAttributes元素进行匹配。该值也可以通过lsusb命令查看得到;
#define USB_ENDPOINT_DIR_MASK 0x80
#define USB_DIR_IN 0x80 /* to host */
/**
* usb_endpoint_dir_in - check if the endpoint has IN direction
* @epd: endpoint to be checked
*
* Returns true if the endpoint is of type IN, otherwise it returns false.
*/
static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
{
return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);
}
如果是bulk_in,则将该端点epd赋值给bulk_in变量;否则就给bulk_out变量;
int传输模式下,通过usb_endpoint_dir_in(epd)确定epd方向,再赋值给对应的int_in或int_out;
若符合以上条件,则返回true,否则返回false;
函数usb_find_int_in_endpoint()其实就是封装了usb_find_common_endpoints(),只是传给该函数的参数是给int_in,代码实现如下:
res = usb_find_int_in_endpoint(alt, &ep_int);
if (res && us->protocol == USB_PR_CBI) {
usb_stor_dbg(us, "interrupt endpoint not found\n");
return res;
}
static inline int __must_check
usb_find_int_in_endpoint(struct usb_host_interface *alt,
struct usb_endpoint_descriptor **int_in)
{
return usb_find_common_endpoints(alt, NULL, NULL, int_in, NULL);
}
此部分是给CBI协议类型设备提供的,BOT协议类型设备一般是不实现中断端点的;
/* Calculate and store the pipe values */
us->send_ctrl_pipe = usb_sndctrlpipe(us->pusb_dev, 0);
us->recv_ctrl_pipe = usb_rcvctrlpipe(us->pusb_dev, 0);
us->send_bulk_pipe = usb_sndbulkpipe(us->pusb_dev,
usb_endpoint_num(ep_out));
us->recv_bulk_pipe = usb_rcvbulkpipe(us->pusb_dev,
usb_endpoint_num(ep_in));
if (ep_int) {
us->recv_intr_pipe = usb_rcvintpipe(us->pusb_dev,
usb_endpoint_num(ep_int));
us->ep_bInterval = ep_int->bInterval;
}
代码实现如下:
#define PIPE_CONTROL 2
static inline unsigned int __create_pipe(struct usb_device *dev,
unsigned int endpoint)
{
return (dev->devnum << 8) | (endpoint << 15);
}
#define usb_sndctrlpipe(dev, endpoint) \
((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint))
#define usb_rcvctrlpipe(dev, endpoint) \
((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
#define PIPE_BULK 3
#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */
/**
* usb_endpoint_num - get the endpoint's number
* @epd: endpoint to be checked
*
* Returns @epd's number: 0 to 15.
*/
static inline int usb_endpoint_num(const struct usb_endpoint_descriptor *epd)
{
return epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
}
#define usb_sndbulkpipe(dev, endpoint) \
((PIPE_BULK << 30) | __create_pipe(dev, endpoint))
通过ep_out和ep_in端点描述符的epd->bEndpointAddress获取到端点号,再计算出发送/接收bulk pipe值;
#define usb_rcvbulkpipe(dev, endpoint) \
((PIPE_BULK << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
#define usb_rcvintpipe(dev, endpoint) \
((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
该函数是USB storage驱动中的重头戏。里面实现了一个内核线程usb_stor_control_thread,就是该线程在操作数据的传输。代码实现如下:
/* Initialize all the dynamic resources we need */
static int usb_stor_acquire_resources(struct us_data *us)
{
int p;
struct task_struct *th;
us->current_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!us->current_urb)
return -ENOMEM;
/*
* Just before we start our control thread, initialize
* the device if it needs initialization
*/
if (us->unusual_dev->initFunction) {
p = us->unusual_dev->initFunction(us);
if (p)
return p;
}
/* Start up our control thread */
th = kthread_run(usb_stor_control_thread, us, "usb-storage");
if (IS_ERR(th)) {
dev_warn(&us->pusb_intf->dev,
"Unable to start control thread\n");
return PTR_ERR(th);
}
us->ctl_thread = th;
return 0;
}
代码逻辑如下:
增加USB接口电源管理计数,代码实现如下:
/**
* usb_autopm_get_interface_no_resume - increment a USB interface's PM-usage counter
* @intf: the usb_interface whose counter should be incremented
*
* This routine increments @intf's usage counter but does not carry out an
* autoresume.
*
* This routine can run in atomic context.
*/
void usb_autopm_get_interface_no_resume(struct usb_interface *intf)
{
struct usb_device *udev = interface_to_usbdev(intf);
usb_mark_last_busy(udev);
atomic_inc(&intf->pm_usage_cnt);
pm_runtime_get_noresume(&intf->dev);
}
EXPORT_SYMBOL_GPL(usb_autopm_get_interface_no_resume);
相对于scsi控制器host的注册及使用,此处简单介绍一下。前面使用scsi_host_alloc()函数,它的作用就是给struct Scsi_Host结构体申请了空间,而真正要想模拟一个scsi的情景,需要三个函数:scsi_host_alloc()、scsi_add_host()、scsi_scan_host();只有调用了第二个函数之后,scsi核心层才知道有这么一个host的存在,而在第三个函数被调用之后,真正的设备才被发现。
所以,此处调用scsi_add_host(),是将USB storage所在的虚拟host通报给SCSI核心层,代码实现如下:
result = scsi_add_host(us_to_host(us), dev);
if (result) {
dev_warn(dev,
"Unable to add the scsi host\n");
goto HostAddErr;
}
/* Submit the delayed_work for SCSI-device scanning */
set_bit(US_FLIDX_SCAN_PENDING, &us->dflags);
if (delay_use > 0)
dev_dbg(dev, "waiting for device to settle before scanning\n");
queue_delayed_work(system_freezable_wq, &us->scan_dwork,
delay_use * HZ);
这里需要分两个部分来解释,一个是us->dflags标记,一个是us->scan_dwork;下面分小节来讲解。
经过分析,该标记主要是用来检测scsi控制器的状态之用,其在struct us_data结构体内的解释如下:
unsigned long dflags; /* dynamic atomic bitflags */
此处是在scsi_scan_host()函数调用前设置,标志马上进入scsi scan状态,该标志还在内核线程中做scsi cmd timeout状态检测,以及在scsi abort函数实现中做设置scsi cmd timeout状态等;稍后就会分析到;
前面在2.5.3小节已经介绍在函数usb_stor_probe1()中已调用INIT_DELAYED_WORK()初始化了struct delayed_work us->scan_dwork,并赋予了执行函数:usb_stor_scan_dwork(),此处即将该延迟工作加入到的工作队列system_freezable_wq中;
==【注1】==
以个人理解来分析一下system_freezable_wq工作队列,代码实现如下(本USB storage代码树中没有该函数实现副本,请参照主线内核):
struct workqueue_struct *system_freezable_wq __read_mostly;
EXPORT_SYMBOL_GPL(system_freezable_wq);
system_freezable_wq = alloc_workqueue("events_freezable",
WQ_FREEZABLE, 0);
WQ_FREEZABLE是一个和电源管理相关的内容。在系统Hibernation或者suspend的时候,有一个步骤就是冻结用户空间的进程以及部分(标注freezable的)内核线程(包括workqueue的worker thread)。标记WQ_FREEZABLE的workqueue需要参与到进程冻结的过程中,worker thread被冻结的时候,会处理完当前所有的work,一旦冻结完成,那么就不会启动新的work的执行,直到进程被解冻^sample_1。
所以,当内核进入suspend状态时,会冻结该workqueue,导致,插拔U盘无反应?
此处的delay_use是和之前的quirks一样的,通过在sys文件系统下创建接口,可供用户层修改,默认值为1,代码实现及对应接口路径如下:
static unsigned int delay_use = 1;
module_param(delay_use, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
~$ cat /sys/module/usb_storage/parameters/delay_use
1
此刻,我们该进入之前INIT_DELAYED_WORK(&us->scan_dwork, usb_stor_scan_dwork)中的延迟工作函数usb_stor_scan_dwork()分析了,代码如下:
/* Delayed-work routine to carry out SCSI-device scanning */
static void usb_stor_scan_dwork(struct work_struct *work)
{
struct us_data *us = container_of(work, struct us_data,
scan_dwork.work);
struct device *dev = &us->pusb_intf->dev;
dev_dbg(dev, "starting scan\n");
/* For bulk-only devices, determine the max LUN value */
if (us->protocol == USB_PR_BULK &&
!(us->fflags & US_FL_SINGLE_LUN) &&
!(us->fflags & US_FL_SCM_MULT_TARG)) {
mutex_lock(&us->dev_mutex);
us->max_lun = usb_stor_Bulk_max_lun(us);
/*
* Allow proper scanning of devices that present more than 8 LUNs
* While not affecting other devices that may need the previous
* behavior
*/
if (us->max_lun >= 8)
us_to_host(us)->max_lun = us->max_lun+1;
mutex_unlock(&us->dev_mutex);
}
scsi_scan_host(us_to_host(us));
dev_dbg(dev, "scan complete\n");
/* Should we unbind if no devices were detected? */
usb_autopm_put_interface(us->pusb_intf);
clear_bit(US_FLIDX_SCAN_PENDING, &us->dflags);
}
该函数的主要功能有两个:
对于bulk-only devices,需要向device获取max LUN值,该部分实现如下:
struct us_data *us = container_of(work, struct us_data,
scan_dwork.work);
struct device *dev = &us->pusb_intf->dev;
/* For bulk-only devices, determine the max LUN value */
if (us->protocol == USB_PR_BULK &&
!(us->fflags & US_FL_SINGLE_LUN) &&
!(us->fflags & US_FL_SCM_MULT_TARG)) {
mutex_lock(&us->dev_mutex);
us->max_lun = usb_stor_Bulk_max_lun(us);
/*
* Allow proper scanning of devices that present more than 8 LUNs
* While not affecting other devices that may need the previous
* behavior
*/
if (us->max_lun >= 8)
us_to_host(us)->max_lun = us->max_lun+1;
mutex_unlock(&us->dev_mutex);
}
已针对USB_PR_BULK协议以及US_FL_SINGLE_LUN和US_FL_SCM_MULT_TARG标记做了限制,通过调用usb_stor_Bulk_max_lun()函数来获取max lun,函数实现如下:
/* Determine what the maximum LUN supported is */
int usb_stor_Bulk_max_lun(struct us_data *us)
{
int result;
/* issue the command */
us->iobuf[0] = 0;
result = usb_stor_control_msg(us, us->recv_ctrl_pipe,
US_BULK_GET_MAX_LUN,
USB_DIR_IN | USB_TYPE_CLASS |
USB_RECIP_INTERFACE,
0, us->ifnum, us->iobuf, 1, 10*HZ);
usb_stor_dbg(us, "GetMaxLUN command result is %d, data is %d\n",
result, us->iobuf[0]);
/*
* If we have a successful request, return the result if valid. The
* CBW LUN field is 4 bits wide, so the value reported by the device
* should fit into that.
*/
if (result > 0) {
if (us->iobuf[0] < 16) {
return us->iobuf[0];
} else {
dev_info(&us->pusb_intf->dev,
"Max LUN %d is not valid, using 0 instead",
us->iobuf[0]);
}
}
/*
* Some devices don't like GetMaxLUN. They may STALL the control
* pipe, they may return a zero-length result, they may do nothing at
* all and timeout, or they may fail in even more bizarrely creative
* ways. In these cases the best approach is to use the default
* value: only one LUN.
*/
return 0;
}
通过调用usb_stor_control_msg函数下发控制命令US_BULK_GET_MAX_LUN,该函数的参数需要分析一下,参见本github中的手册文档USB_Storage_spec.md^sample_2中对US_BULK_GET_MAX_LUN命令的说明,即下表命令规则:
bmRequestType | bRequest | wValue | wIndex | wLength | Data |
---|---|---|---|---|---|
10100001b | 11111110b | 0000h | Interface | 0001h | 1 byte |
而函数参数对应关系如下:
显然,此处是完全按照硬件协议规定来定义的,usb_stor_control_msg()函数实现如下:
/*
* Transfer one control message, with timeouts, and allowing early
* termination. Return codes are usual -Exxx, *not* USB_STOR_XFER_xxx.
*/
int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
u8 request, u8 requesttype, u16 value, u16 index,
void *data, u16 size, int timeout)
{
int status;
usb_stor_dbg(us, "rq=%02x rqtype=%02x value=%04x index=%02x len=%u\n",
request, requesttype, value, index, size);
/* fill in the devrequest structure */
us->cr->bRequestType = requesttype;
us->cr->bRequest = request;
us->cr->wValue = cpu_to_le16(value);
us->cr->wIndex = cpu_to_le16(index);
us->cr->wLength = cpu_to_le16(size);
/* fill and submit the URB */
usb_fill_control_urb(us->current_urb, us->pusb_dev, pipe,
(unsigned char*) us->cr, data, size,
usb_stor_blocking_completion, NULL);
status = usb_stor_msg_common(us, timeout);
/* return the actual length of the data transferred if no error */
if (status == 0)
status = us->current_urb->actual_length;
return status;
}
EXPORT_SYMBOL_GPL(usb_stor_control_msg);
us->cr在之前已经申请了内存空间,此处直接填充该结构,接着调用USB core层函数usb_fill_control_urb()来填充us->current_urb结构,并将urb->complete赋值为usb_stor_blocking_completion(),再调用usb_stor_msg_common()函数进一步处理并下发给USB HCD,usb_stor_blocking_completion()和usb_stor_msg_common()代码实现如下:
/*
* This is the completion handler which will wake us up when an URB
* completes.
*/
static void usb_stor_blocking_completion(struct urb *urb)
{
struct completion *urb_done_ptr = urb->context;
complete(urb_done_ptr);
}
/*
* This is the common part of the URB message submission code
*
* All URBs from the usb-storage driver involved in handling a queued scsi
* command _must_ pass through this function (or something like it) for the
* abort mechanisms to work properly.
*/
static int usb_stor_msg_common(struct us_data *us, int timeout)
{
struct completion urb_done;
long timeleft;
int status;
/* don't submit URBs during abort processing */
if (test_bit(US_FLIDX_ABORTING, &us->dflags))
return -EIO;
/* set up data structures for the wakeup system */
init_completion(&urb_done);
/* fill the common fields in the URB */
us->current_urb->context = &urb_done;
us->current_urb->transfer_flags = 0;
/*
* we assume that if transfer_buffer isn't us->iobuf then it
* hasn't been mapped for DMA. Yes, this is clunky, but it's
* easier than always having the caller tell us whether the
* transfer buffer has already been mapped.
*/
if (us->current_urb->transfer_buffer == us->iobuf)
us->current_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
us->current_urb->transfer_dma = us->iobuf_dma;
/* submit the URB */
status = usb_submit_urb(us->current_urb, GFP_NOIO);
if (status) {
/* something went wrong */
return status;
}
/*
* since the URB has been submitted successfully, it's now okay
* to cancel it
*/
set_bit(US_FLIDX_URB_ACTIVE, &us->dflags);
/* did an abort occur during the submission? */
if (test_bit(US_FLIDX_ABORTING, &us->dflags)) {
/* cancel the URB, if it hasn't been cancelled already */
if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) {
usb_stor_dbg(us, "-- cancelling URB\n");
usb_unlink_urb(us->current_urb);
}
}
/* wait for the completion of the URB */
timeleft = wait_for_completion_interruptible_timeout(
&urb_done, timeout ? : MAX_SCHEDULE_TIMEOUT);
clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags);
if (timeleft <= 0) {
usb_stor_dbg(us, "%s -- cancelling URB\n",
timeleft == 0 ? "Timeout" : "Signal");
usb_kill_urb(us->current_urb);
}
/* return the URB status */
return us->current_urb->status;
}
该函数工作流程如下:
usb_stor_control_msg()函数接着检查usb_stor_msg_common()函数返回值,即status,如果无错误,则返回的status被赋值为us->current_urb->actual_length;
回到usb_stor_Bulk_max_lun()函数,再对usb_stor_control_msg()返回值result进行检查设置:
Scsi_Host主机控制器最后调用函数:扫描scsi主机控制器;至此USB模拟scsi控制器完成,U盘即可被应用层发现。应用层可通过scsi接口进行查看USB mass storage的状态。
函数最后两行:
usb_autopm_put_interface(us->pusb_intf);
clear_bit(US_FLIDX_SCAN_PENDING, &us->dflags);
终于进入USB storage传输的主体,也是USB storage最重要的一部分。现看函数实现:
static int usb_stor_control_thread(void * __us)
{
struct us_data *us = (struct us_data *)__us;
struct Scsi_Host *host = us_to_host(us);
struct scsi_cmnd *srb;
for (;;) {
usb_stor_dbg(us, "*** thread sleeping\n");
if (wait_for_completion_interruptible(&us->cmnd_ready))
break;
usb_stor_dbg(us, "*** thread awakened\n");
/* lock the device pointers */
mutex_lock(&(us->dev_mutex));
/* lock access to the state */
scsi_lock(host);
/* When we are called with no command pending, we're done */
srb = us->srb;
if (srb == NULL) {
scsi_unlock(host);
mutex_unlock(&us->dev_mutex);
usb_stor_dbg(us, "-- exiting\n");
break;
}
/* has the command timed out *already* ? */
if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
srb->result = DID_ABORT << 16;
goto SkipForAbort;
}
scsi_unlock(host);
/*
* reject the command if the direction indicator
* is UNKNOWN
*/
if (srb->sc_data_direction == DMA_BIDIRECTIONAL) {
usb_stor_dbg(us, "UNKNOWN data direction\n");
srb->result = DID_ERROR << 16;
}
/*
* reject if target != 0 or if LUN is higher than
* the maximum known LUN
*/
else if (srb->device->id &&
!(us->fflags & US_FL_SCM_MULT_TARG)) {
usb_stor_dbg(us, "Bad target number (%d:%llu)\n",
srb->device->id,
srb->device->lun);
srb->result = DID_BAD_TARGET << 16;
}
else if (srb->device->lun > us->max_lun) {
usb_stor_dbg(us, "Bad LUN (%d:%llu)\n",
srb->device->id,
srb->device->lun);
srb->result = DID_BAD_TARGET << 16;
}
/*
* Handle those devices which need us to fake
* their inquiry data
*/
else if ((srb->cmnd[0] == INQUIRY) &&
(us->fflags & US_FL_FIX_INQUIRY)) {
unsigned char data_ptr[36] = {
0x00, 0x80, 0x02, 0x02,
0x1F, 0x00, 0x00, 0x00};
usb_stor_dbg(us, "Faking INQUIRY command\n");
fill_inquiry_response(us, data_ptr, 36);
srb->result = SAM_STAT_GOOD;
}
/* we've got a command, let's do it! */
else {
US_DEBUG(usb_stor_show_command(us, srb));
us->proto_handler(srb, us);
usb_mark_last_busy(us->pusb_dev);
}
/* lock access to the state */
scsi_lock(host);
/* was the command aborted? */
if (srb->result == DID_ABORT << 16) {
SkipForAbort:
usb_stor_dbg(us, "scsi command aborted\n");
srb = NULL; /* Don't call srb->scsi_done() */
}
/*
* If an abort request was received we need to signal that
* the abort has finished. The proper test for this is
* the TIMED_OUT flag, not srb->result == DID_ABORT, because
* the timeout might have occurred after the command had
* already completed with a different result code.
*/
if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
complete(&(us->notify));
/* Allow USB transfers to resume */
clear_bit(US_FLIDX_ABORTING, &us->dflags);
clear_bit(US_FLIDX_TIMED_OUT, &us->dflags);
}
/* finished working on this command */
us->srb = NULL;
scsi_unlock(host);
/* unlock the device pointers */
mutex_unlock(&us->dev_mutex);
/* now that the locks are released, notify the SCSI core */
if (srb) {
usb_stor_dbg(us, "scsi cmd done, result=0x%x\n",
srb->result);
srb->scsi_done(srb);
}
} /* for (;;) */
/* Wait until we are told to stop */
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop())
break;
schedule();
}
__set_current_state(TASK_RUNNING);
return 0;
}
直接进第一个for循环,第一部分,us->cmnd_ready完成量的调用,这个用于同步功能的完成量。
us->cmnd_ready在本驱动中主要用于该内核线程和scsi中间层命令下发的同步,此处实现为:
if (wait_for_completion_interruptible(&us->cmnd_ready))
break;
调用complete(&us->cmnd_ready)的地方有两处,一个是在释放内核资源时调用,此处不解释;另一处则是在drivers/usb/storage/scsiglue.c中struct scsi_host_template usb_stor_host_template结构体实例中的queuecommand()函数中,即scsi中间层下发scsi cmd给USB storage驱动;
queuecommand()实现如下:
/* queue a command */
/* This is always called with scsi_lock(host) held */
static int queuecommand_lck(struct scsi_cmnd *srb,
void (*done)(struct scsi_cmnd *))
{
struct us_data *us = host_to_us(srb->device->host);
/* check for state-transition errors */
if (us->srb != NULL) {
printk(KERN_ERR USB_STORAGE "Error in %s: us->srb = %p\n",
__func__, us->srb);
return SCSI_MLQUEUE_HOST_BUSY;
}
/* fail the command if we are disconnecting */
if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
usb_stor_dbg(us, "Fail command during disconnect\n");
srb->result = DID_NO_CONNECT << 16;
done(srb);
return 0;
}
/* enqueue the command and wake up the control thread */
srb->scsi_done = done;
us->srb = srb;
complete(&us->cmnd_ready);
return 0;
}
static DEF_SCSI_QCMD(queuecommand)
srb为scsi cmd的current srb,为struct scsi_cmnd结构体;该函数流程如下:
继续usb_stor_control_thread()函数;接下来对us->srb和us->dflags的US_FLIDX_TIMED_OUT做检测判断,此处使用了自旋锁及scsi_lock/unlock(),代码实现如下:
/* lock the device pointers */
mutex_lock(&(us->dev_mutex));
/* lock access to the state */
scsi_lock(host);
/* When we are called with no command pending, we're done */
srb = us->srb;
if (srb == NULL) {
scsi_unlock(host);
mutex_unlock(&us->dev_mutex);
usb_stor_dbg(us, "-- exiting\n");
break;
}
/* has the command timed out *already* ? */
if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
srb->result = DID_ABORT << 16;
goto SkipForAbort;
}
scsi_unlock(host);
接下来将对由queuecommand()赋值的srb进行细致的检测,代码如下:
/*
* reject the command if the direction indicator
* is UNKNOWN
*/
if (srb->sc_data_direction == DMA_BIDIRECTIONAL) {
usb_stor_dbg(us, "UNKNOWN data direction\n");
srb->result = DID_ERROR << 16;
}
/*
* reject if target != 0 or if LUN is higher than
* the maximum known LUN
*/
else if (srb->device->id &&
!(us->fflags & US_FL_SCM_MULT_TARG)) {
usb_stor_dbg(us, "Bad target number (%d:%llu)\n",
srb->device->id,
srb->device->lun);
srb->result = DID_BAD_TARGET << 16;
}
else if (srb->device->lun > us->max_lun) {
usb_stor_dbg(us, "Bad LUN (%d:%llu)\n",
srb->device->id,
srb->device->lun);
srb->result = DID_BAD_TARGET << 16;
}
/*
* Handle those devices which need us to fake
* their inquiry data
*/
else if ((srb->cmnd[0] == INQUIRY) &&
(us->fflags & US_FL_FIX_INQUIRY)) {
unsigned char data_ptr[36] = {
0x00, 0x80, 0x02, 0x02,
0x1F, 0x00, 0x00, 0x00};
usb_stor_dbg(us, "Faking INQUIRY command\n");
fill_inquiry_response(us, data_ptr, 36);
srb->result = SAM_STAT_GOOD;
}
/* we've got a command, let's do it! */
else {
US_DEBUG(usb_stor_show_command(us, srb));
us->proto_handler(srb, us);
usb_mark_last_busy(us->pusb_dev);
}
代码流程如下:
先看代码实现:
/*
* Handle those devices which need us to fake
* their inquiry data
*/
else if ((srb->cmnd[0] == INQUIRY) &&
(us->fflags & US_FL_FIX_INQUIRY)) {
unsigned char data_ptr[36] = {
0x00, 0x80, 0x02, 0x02,
0x1F, 0x00, 0x00, 0x00};
usb_stor_dbg(us, "Faking INQUIRY command\n");
fill_inquiry_response(us, data_ptr, 36);
srb->result = SAM_STAT_GOOD;
}
scsi 子系统里的设备使用 scsi 命令来通信,scsi spec 定义了一大堆的命令,spec 里称这个为命令集,即所谓的 command set.其中一些命令是每一个 scsi 设备都必须支持的,另一些命令则是可选的.而作为 U盘,它所支持的是 scsi transparent command set,所以它基本上就是支持所有的 scsi 命令了,不过我们其实并不关心任何一个具体的命令,只需要了解一些最基本的命令就是了.比如我们需要知道,所有的 scsi设备都至少需要支持以下这四个 scsi 命令:INQUIRY,REQUEST SENSE,SEND DIAGNOSTIC,TEST UNIT READY。
事实上,INQUIRY命令是最最基本的一个SCSI命令,比如主机第一次探测device的时候就要用INQUIRY命令来了解这是一个什么设备。通常大多数设备的vendor name 和 product name 是通过 INQUIRY 命令来获得的,而这个 us->fflag 表明,这些设备的 vendor name 和 product name 不需要查询,或者根本就不支持查询,她们的 vendor name 和 product name直接就定义好了,在 unusal_devs.h 中就设好了。
此处的srb->cmnd[0]包含的就是一个scsi命令,这个判断的意思是,如果device不支持scsi的INQUIRY命令,则USB storage直接在此处封装一个INQUIRY命令的返回结果给scsi中间层,这样就做到两边都满意了。先继续看fill_inquiry_response()函数:
/*
* fill_inquiry_response takes an unsigned char array (which must
* be at least 36 characters) and populates the vendor name,
* product name, and revision fields. Then the array is copied
* into the SCSI command's response buffer (oddly enough
* called request_buffer). data_len contains the length of the
* data array, which again must be at least 36.
*/
void fill_inquiry_response(struct us_data *us, unsigned char *data,
unsigned int data_len)
{
if (data_len < 36) /* You lose. */
return;
memset(data+8, ' ', 28);
if (data[0]&0x20) { /*
* USB device currently not connected. Return
* peripheral qualifier 001b ("...however, the
* physical device is not currently connected
* to this logical unit") and leave vendor and
* product identification empty. ("If the target
* does store some of the INQUIRY data on the
* device, it may return zeros or ASCII spaces
* (20h) in those fields until the data is
* available from the device.").
*/
} else {
u16 bcdDevice = le16_to_cpu(us->pusb_dev->descriptor.bcdDevice);
int n;
n = strlen(us->unusual_dev->vendorName);
memcpy(data+8, us->unusual_dev->vendorName, min(8, n));
n = strlen(us->unusual_dev->productName);
memcpy(data+16, us->unusual_dev->productName, min(16, n));
data[32] = 0x30 + ((bcdDevice>>12) & 0x0F);
data[33] = 0x30 + ((bcdDevice>>8) & 0x0F);
data[34] = 0x30 + ((bcdDevice>>4) & 0x0F);
data[35] = 0x30 + ((bcdDevice) & 0x0F);
}
usb_stor_set_xfer_buf(data, data_len, us->srb);
}
EXPORT_SYMBOL_GPL(fill_inquiry_response);
每个SCSI命令,在device应答时,都是依据SCSI协议里规定的格式。所以相对INQUIRY命令,规范中规定,响应数据必须至少包含36各字节,所以函数开头就直接检测这个data_len必须至少36字节。
此处推荐一个可以查询SCSI命令的工具,安装包sg3-utils,然后就可以看到一个sg_inq命令了,其它sg_*没有研究过,此处只介绍下sg_inq命令,插入U盘,运行命令:
$ sudo sg_inq /dev/sdb
standard INQUIRY:
PQual=0 Device_type=0 RMB=1 LU_CONG=0 version=0x06 [SPC-4]
[AERC=0] [TrmTsk=0] NormACA=0 HiSUP=0 Resp_data_format=2
SCCS=0 ACC=0 TPGS=0 3PC=0 Protect=0 [BQue=0]
EncServ=0 MultiP=0 [MChngr=0] [ACKREQQ=0] Addr16=0
[RelAdr=0] WBus16=0 Sync=0 [Linked=0] [TranDis=0] CmdQue=0
length=36 (0x24) Peripheral device type: disk
Vendor identification: Kingston
Product identification: DataTraveler 2.0
Product revision level: PMAP
Unit serial number: CB806100040A
这里面看得懂的信息并不多,但是length=36,显示了命令响应的长度,以及Vendor ID,Product ID,Product revision等;
==【data数据说明及填充】==
继续fill_inquiry_response(),接下来的两句:
memset(data+8, ' ', 28);
if (data[0]&0x20) {
SCSI协议规定了,标准的INQUIRY命令的data[0],总共8个bit位,其中bit[7:5]被称为peripheral qualifier,bit[4:0]被称为perpheral device type;此处的0x20就表示peripheral qualifier 这个外围设备限定符为 001b,而 peripheral device type这个外围设备类型则为 00h。查询SCSI协议可知,后者代表的是设备类型为磁盘,或者说直接访问设备;前者代表的是目标设备的当前LUN支持这种类型,然而,实际的物理设备并没有连接在当前LUN上。
在data[36]中,从data[8]一直到data[35]这28个字节都是保存的vendor和product的信息,SCSI协议里面写了,如果设备里有保存这些信息,那么它可以暂时先返回0x20h,所以可以先把data[8]到data[35]都给设置成空,等到保存在设备上的这些信息可以读了再去读^sample_3。
此处fill_inquiry_response()传入的data的data[0]不是0x20,所以继续分析:
u16 bcdDevice = le16_to_cpu(us->pusb_dev->descriptor.bcdDevice);
int n;
n = strlen(us->unusual_dev->vendorName);
memcpy(data+8, us->unusual_dev->vendorName, min(8, n));
n = strlen(us->unusual_dev->productName);
memcpy(data+16, us->unusual_dev->productName, min(16, n));
data[32] = 0x30 + ((bcdDevice>>12) & 0x0F);
data[33] = 0x30 + ((bcdDevice>>8) & 0x0F);
data[34] = 0x30 + ((bcdDevice>>4) & 0x0F);
data[35] = 0x30 + ((bcdDevice) & 0x0F);
==【usb_stor_set_xfer_buf()函数解析】==
函数实现如下,正如英文注释,即存储该buffer内容到srb的transfer buffer中,并设置SCSI residue。
/*
* Store the contents of buffer into srb's transfer buffer and set the
* SCSI residue.
*/
void usb_stor_set_xfer_buf(unsigned char *buffer,
unsigned int buflen, struct scsi_cmnd *srb)
{
unsigned int offset = 0;
struct scatterlist *sg = NULL;
buflen = min(buflen, scsi_bufflen(srb));
buflen = usb_stor_access_xfer_buf(buffer, buflen, srb, &sg, &offset,
TO_XFER_BUF);
if (buflen < scsi_bufflen(srb))
scsi_set_resid(srb, scsi_bufflen(srb) - buflen);
}
EXPORT_SYMBOL_GPL(usb_stor_set_xfer_buf);
取buflen和scsi_bufflen(srb)之间最小值,继续跟踪usb_stor_access_xfer_buf()函数,此函数设计到内存管理;代码实现如下:
/*
* Copy a buffer of length buflen to/from the srb's transfer buffer.
* Update the **sgptr and *offset variables so that the next copy will
* pick up from where this one left off.
*/
unsigned int usb_stor_access_xfer_buf(unsigned char *buffer,
unsigned int buflen, struct scsi_cmnd *srb, struct scatterlist **sgptr,
unsigned int *offset, enum xfer_buf_dir dir)
{
unsigned int cnt = 0;
struct scatterlist *sg = *sgptr;
struct sg_mapping_iter miter;
unsigned int nents = scsi_sg_count(srb);
if (sg)
nents = sg_nents(sg);
else
sg = scsi_sglist(srb);
sg_miter_start(&miter, sg, nents, dir == FROM_XFER_BUF ?
SG_MITER_FROM_SG: SG_MITER_TO_SG);
if (!sg_miter_skip(&miter, *offset))
return cnt;
while (sg_miter_next(&miter) && cnt < buflen) {
unsigned int len = min_t(unsigned int, miter.length,
buflen - cnt);
if (dir == FROM_XFER_BUF)
memcpy(buffer + cnt, miter.addr, len);
else
memcpy(miter.addr, buffer + cnt, len);
if (*offset + len < miter.piter.sg->length) {
*offset += len;
*sgptr = miter.piter.sg;
} else {
*offset = 0;
*sgptr = sg_next(miter.piter.sg);
}
cnt += len;
}
sg_miter_stop(&miter);
return cnt;
}
EXPORT_SYMBOL_GPL(usb_stor_access_xfer_buf);
先介绍下scatter/gather,这是一种用于高性能IO的标准技术,通常意味着一种DMA传输方式,对于一个给定的数据块,可能在内存中存在于一些离散的缓冲区,换言之,就是说一些不连续的内存缓冲区一起保存一个数据块,如果没有 scatter/gather呢,那么当我们要建立一个从内存到磁盘的传输,那么操作系统通常会为每一个buffer做一次传输,或者干脆就是把这些不连续的buffer里边的冬冬全都移动到另一个很大的buffer里边,然后再开始传输。那么这两种方法显然都是效率不高的。毫无疑问,如果【操作系统/驱动程序/硬件】能够把这些来自内存中离散位置的数据收集起来(gather up)并转移她们到适当位置整个这个步骤是一个单一的操作的话,效率肯定就会更高。反之,如果要从磁盘向内存中传输,而有一个单一的操作能够把数据块直接分散开来(scatter)到达内存中需要的位置,而不再需要中间的那个块移动,或者别的方法,那么显然,效率总会更高。
尽管如此,此处的sg_miter_start()、sg_miter_skip()、sg_miter_next()、sg_miter_stop()函数仍然很难理解,暂且不介绍,等以后学精了再补充;
回到usb_stor_set_xfer_buf()函数,有关SCSI residue的设定,residue是指在一次传输中,数据并未传输完,则先记下还剩未传输的部分。
总之,usb_stor_set_xfer_buf()函数的目的,就是将填充好的data数据结构,拷贝到us->srb的transfer buffer中,以反馈回SCSI中间层;
回到usb_stor_control_thread()内核线程,srb->result = SAM_STAT_GOOD,填充完srb后,再设置命令处理结果标记,标志命令已被device"处理"。
如果所有的检测都已通过,则USB storage驱动层已获取到srb命令,现在是调用协议层和传输层来完成命令的下发。
us->proto_handler(srb, us)函数在usb_stor_probe1()的get_protocol()函数中指定,因为U盘遵循USB_SC_SCSI协议,所以直接调用usb_stor_transparent_scsi_command()。
函数实现如下:
void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb,
struct us_data *us)
{
/* send the command to the transport layer */
usb_stor_invoke_transport(srb, us);
}
EXPORT_SYMBOL_GPL(usb_stor_transparent_scsi_command);
/*
* Invoke the transport and basic error-handling/recovery methods
*
* This is used by the protocol layers to actually send the message to
* the device and receive the response.
*/
void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
{
int need_auto_sense;
int result;
/* send the command to the transport layer */
scsi_set_resid(srb, 0);
result = us->transport(srb, us);
/*
* if the command gets aborted by the higher layers, we need to
* short-circuit all other processing
*/
if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
usb_stor_dbg(us, "-- command was aborted\n");
srb->result = DID_ABORT << 16;
goto Handle_Errors;
}
/* if there is a transport error, reset and don't auto-sense */
if (result == USB_STOR_TRANSPORT_ERROR) {
usb_stor_dbg(us, "-- transport indicates error, resetting\n");
srb->result = DID_ERROR << 16;
goto Handle_Errors;
}
/* if the transport provided its own sense data, don't auto-sense */
if (result == USB_STOR_TRANSPORT_NO_SENSE) {
srb->result = SAM_STAT_CHECK_CONDITION;
last_sector_hacks(us, srb);
return;
}
srb->result = SAM_STAT_GOOD;
/*
* Determine if we need to auto-sense
*
* I normally don't use a flag like this, but it's almost impossible
* to understand what's going on here if I don't.
*/
need_auto_sense = 0;
/*
* If we're running the CB transport, which is incapable
* of determining status on its own, we will auto-sense
* unless the operation involved a data-in transfer. Devices
* can signal most data-in errors by stalling the bulk-in pipe.
*/
if ((us->protocol == USB_PR_CB || us->protocol == USB_PR_DPCM_USB) &&
srb->sc_data_direction != DMA_FROM_DEVICE) {
usb_stor_dbg(us, "-- CB transport device requiring auto-sense\n");
need_auto_sense = 1;
}
/*
* If we have a failure, we're going to do a REQUEST_SENSE
* automatically. Note that we differentiate between a command
* "failure" and an "error" in the transport mechanism.
*/
if (result == USB_STOR_TRANSPORT_FAILED) {
usb_stor_dbg(us, "-- transport indicates command failure\n");
need_auto_sense = 1;
}
/*
* Determine if this device is SAT by seeing if the
* command executed successfully. Otherwise we'll have
* to wait for at least one CHECK_CONDITION to determine
* SANE_SENSE support
*/
if (unlikely((srb->cmnd[0] == ATA_16 || srb->cmnd[0] == ATA_12) &&
result == USB_STOR_TRANSPORT_GOOD &&
!(us->fflags & US_FL_SANE_SENSE) &&
!(us->fflags & US_FL_BAD_SENSE) &&
!(srb->cmnd[2] & 0x20))) {
usb_stor_dbg(us, "-- SAT supported, increasing auto-sense\n");
us->fflags |= US_FL_SANE_SENSE;
}
/*
* A short transfer on a command where we don't expect it
* is unusual, but it doesn't mean we need to auto-sense.
*/
if ((scsi_get_resid(srb) > 0) &&
!((srb->cmnd[0] == REQUEST_SENSE) ||
(srb->cmnd[0] == INQUIRY) ||
(srb->cmnd[0] == MODE_SENSE) ||
(srb->cmnd[0] == LOG_SENSE) ||
(srb->cmnd[0] == MODE_SENSE_10))) {
usb_stor_dbg(us, "-- unexpectedly short transfer\n");
}
/* Now, if we need to do the auto-sense, let's do it */
if (need_auto_sense) {
int temp_result;
struct scsi_eh_save ses;
int sense_size = US_SENSE_SIZE;
struct scsi_sense_hdr sshdr;
const u8 *scdd;
u8 fm_ili;
/* device supports and needs bigger sense buffer */
if (us->fflags & US_FL_SANE_SENSE)
sense_size = ~0;
Retry_Sense:
usb_stor_dbg(us, "Issuing auto-REQUEST_SENSE\n");
scsi_eh_prep_cmnd(srb, &ses, NULL, 0, sense_size);
/* FIXME: we must do the protocol translation here */
if (us->subclass == USB_SC_RBC || us->subclass == USB_SC_SCSI ||
us->subclass == USB_SC_CYP_ATACB)
srb->cmd_len = 6;
else
srb->cmd_len = 12;
/* issue the auto-sense command */
scsi_set_resid(srb, 0);
temp_result = us->transport(us->srb, us);
/* let's clean up right away */
scsi_eh_restore_cmnd(srb, &ses);
if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
usb_stor_dbg(us, "-- auto-sense aborted\n");
srb->result = DID_ABORT << 16;
/* If SANE_SENSE caused this problem, disable it */
if (sense_size != US_SENSE_SIZE) {
us->fflags &= ~US_FL_SANE_SENSE;
us->fflags |= US_FL_BAD_SENSE;
}
goto Handle_Errors;
}
/*
* Some devices claim to support larger sense but fail when
* trying to request it. When a transport failure happens
* using US_FS_SANE_SENSE, we always retry with a standard
* (small) sense request. This fixes some USB GSM modems
*/
if (temp_result == USB_STOR_TRANSPORT_FAILED &&
sense_size != US_SENSE_SIZE) {
usb_stor_dbg(us, "-- auto-sense failure, retry small sense\n");
sense_size = US_SENSE_SIZE;
us->fflags &= ~US_FL_SANE_SENSE;
us->fflags |= US_FL_BAD_SENSE;
goto Retry_Sense;
}
/* Other failures */
if (temp_result != USB_STOR_TRANSPORT_GOOD) {
usb_stor_dbg(us, "-- auto-sense failure\n");
/*
* we skip the reset if this happens to be a
* multi-target device, since failure of an
* auto-sense is perfectly valid
*/
srb->result = DID_ERROR << 16;
if (!(us->fflags & US_FL_SCM_MULT_TARG))
goto Handle_Errors;
return;
}
/*
* If the sense data returned is larger than 18-bytes then we
* assume this device supports requesting more in the future.
* The response code must be 70h through 73h inclusive.
*/
if (srb->sense_buffer[7] > (US_SENSE_SIZE - 8) &&
!(us->fflags & US_FL_SANE_SENSE) &&
!(us->fflags & US_FL_BAD_SENSE) &&
(srb->sense_buffer[0] & 0x7C) == 0x70) {
usb_stor_dbg(us, "-- SANE_SENSE support enabled\n");
us->fflags |= US_FL_SANE_SENSE;
/*
* Indicate to the user that we truncated their sense
* because we didn't know it supported larger sense.
*/
usb_stor_dbg(us, "-- Sense data truncated to %i from %i\n",
US_SENSE_SIZE,
srb->sense_buffer[7] + 8);
srb->sense_buffer[7] = (US_SENSE_SIZE - 8);
}
scsi_normalize_sense(srb->sense_buffer, SCSI_SENSE_BUFFERSIZE,
&sshdr);
usb_stor_dbg(us, "-- Result from auto-sense is %d\n",
temp_result);
usb_stor_dbg(us, "-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
sshdr.response_code, sshdr.sense_key,
sshdr.asc, sshdr.ascq);
#ifdef CONFIG_USB_STORAGE_DEBUG
usb_stor_show_sense(us, sshdr.sense_key, sshdr.asc, sshdr.ascq);
#endif
/* set the result so the higher layers expect this data */
srb->result = SAM_STAT_CHECK_CONDITION;
scdd = scsi_sense_desc_find(srb->sense_buffer,
SCSI_SENSE_BUFFERSIZE, 4);
fm_ili = (scdd ? scdd[3] : srb->sense_buffer[2]) & 0xA0;
/*
* We often get empty sense data. This could indicate that
* everything worked or that there was an unspecified
* problem. We have to decide which.
*/
if (sshdr.sense_key == 0 && sshdr.asc == 0 && sshdr.ascq == 0 &&
fm_ili == 0) {
/*
* If things are really okay, then let's show that.
* Zero out the sense buffer so the higher layers
* won't realize we did an unsolicited auto-sense.
*/
if (result == USB_STOR_TRANSPORT_GOOD) {
srb->result = SAM_STAT_GOOD;
srb->sense_buffer[0] = 0x0;
}
/*
* ATA-passthru commands use sense data to report
* the command completion status, and often devices
* return Check Condition status when nothing is
* wrong.
*/
else if (srb->cmnd[0] == ATA_16 ||
srb->cmnd[0] == ATA_12) {
/* leave the data alone */
}
/*
* If there was a problem, report an unspecified
* hardware error to prevent the higher layers from
* entering an infinite retry loop.
*/
else {
srb->result = DID_ERROR << 16;
if ((sshdr.response_code & 0x72) == 0x72)
srb->sense_buffer[1] = HARDWARE_ERROR;
else
srb->sense_buffer[2] = HARDWARE_ERROR;
}
}
}
/*
* Some devices don't work or return incorrect data the first
* time they get a READ(10) command, or for the first READ(10)
* after a media change. If the INITIAL_READ10 flag is set,
* keep track of whether READ(10) commands succeed. If the
* previous one succeeded and this one failed, set the REDO_READ10
* flag to force a retry.
*/
if (unlikely((us->fflags & US_FL_INITIAL_READ10) &&
srb->cmnd[0] == READ_10)) {
if (srb->result == SAM_STAT_GOOD) {
set_bit(US_FLIDX_READ10_WORKED, &us->dflags);
} else if (test_bit(US_FLIDX_READ10_WORKED, &us->dflags)) {
clear_bit(US_FLIDX_READ10_WORKED, &us->dflags);
set_bit(US_FLIDX_REDO_READ10, &us->dflags);
}
/*
* Next, if the REDO_READ10 flag is set, return a result
* code that will cause the SCSI core to retry the READ(10)
* command immediately.
*/
if (test_bit(US_FLIDX_REDO_READ10, &us->dflags)) {
clear_bit(US_FLIDX_REDO_READ10, &us->dflags);
srb->result = DID_IMM_RETRY << 16;
srb->sense_buffer[0] = 0;
}
}
/* Did we transfer less than the minimum amount required? */
if ((srb->result == SAM_STAT_GOOD || srb->sense_buffer[2] == 0) &&
scsi_bufflen(srb) - scsi_get_resid(srb) < srb->underflow)
srb->result = DID_ERROR << 16;
last_sector_hacks(us, srb);
return;
/*
* Error and abort processing: try to resynchronize with the device
* by issuing a port reset. If that fails, try a class-specific
* device reset.
*/
Handle_Errors:
/*
* Set the RESETTING bit, and clear the ABORTING bit so that
* the reset may proceed.
*/
scsi_lock(us_to_host(us));
set_bit(US_FLIDX_RESETTING, &us->dflags);
clear_bit(US_FLIDX_ABORTING, &us->dflags);
scsi_unlock(us_to_host(us));
/*
* We must release the device lock because the pre_reset routine
* will want to acquire it.
*/
mutex_unlock(&us->dev_mutex);
result = usb_stor_port_reset(us);
mutex_lock(&us->dev_mutex);
if (result < 0) {
scsi_lock(us_to_host(us));
usb_stor_report_device_reset(us);
scsi_unlock(us_to_host(us));
us->transport_reset(us);
}
clear_bit(US_FLIDX_RESETTING, &us->dflags);
last_sector_hacks(us, srb);
}
实测,usb_stor_invoke_transport()函数当前长达319行,所以要解析起来还是蛮复杂的。但是在讲解之前,还得先介绍us->transport(srb, us)。
函数的实现如下:
int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
{
struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf;
unsigned int transfer_length = scsi_bufflen(srb);
unsigned int residue;
int result;
int fake_sense = 0;
unsigned int cswlen;
unsigned int cbwlen = US_BULK_CB_WRAP_LEN;
/* Take care of BULK32 devices; set extra byte to 0 */
if (unlikely(us->fflags & US_FL_BULK32)) {
cbwlen = 32;
us->iobuf[31] = 0;
}
/* set up the command wrapper */
bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
bcb->DataTransferLength = cpu_to_le32(transfer_length);
bcb->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ?
US_BULK_FLAG_IN : 0;
bcb->Tag = ++us->tag;
bcb->Lun = srb->device->lun;
if (us->fflags & US_FL_SCM_MULT_TARG)
bcb->Lun |= srb->device->id << 4;
bcb->Length = srb->cmd_len;
/* copy the command payload */
memset(bcb->CDB, 0, sizeof(bcb->CDB));
memcpy(bcb->CDB, srb->cmnd, bcb->Length);
/* send it to out endpoint */
usb_stor_dbg(us, "Bulk Command S 0x%x T 0x%x L %d F %d Trg %d LUN %d CL %d\n",
le32_to_cpu(bcb->Signature), bcb->Tag,
le32_to_cpu(bcb->DataTransferLength), bcb->Flags,
(bcb->Lun >> 4), (bcb->Lun & 0x0F),
bcb->Length);
result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
bcb, cbwlen, NULL);
usb_stor_dbg(us, "Bulk command transfer result=%d\n", result);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
/* DATA STAGE */
/* send/receive data payload, if there is any */
/*
* Some USB-IDE converter chips need a 100us delay between the
* command phase and the data phase. Some devices need a little
* more than that, probably because of clock rate inaccuracies.
*/
if (unlikely(us->fflags & US_FL_GO_SLOW))
usleep_range(125, 150);
if (transfer_length) {
unsigned int pipe = srb->sc_data_direction == DMA_FROM_DEVICE ?
us->recv_bulk_pipe : us->send_bulk_pipe;
result = usb_stor_bulk_srb(us, pipe, srb);
usb_stor_dbg(us, "Bulk data transfer result 0x%x\n", result);
if (result == USB_STOR_XFER_ERROR)
return USB_STOR_TRANSPORT_ERROR;
/*
* If the device tried to send back more data than the
* amount requested, the spec requires us to transfer
* the CSW anyway. Since there's no point retrying the
* the command, we'll return fake sense data indicating
* Illegal Request, Invalid Field in CDB.
*/
if (result == USB_STOR_XFER_LONG)
fake_sense = 1;
/*
* Sometimes a device will mistakenly skip the data phase
* and go directly to the status phase without sending a
* zero-length packet. If we get a 13-byte response here,
* check whether it really is a CSW.
*/
if (result == USB_STOR_XFER_SHORT &&
srb->sc_data_direction == DMA_FROM_DEVICE &&
transfer_length - scsi_get_resid(srb) ==
US_BULK_CS_WRAP_LEN) {
struct scatterlist *sg = NULL;
unsigned int offset = 0;
if (usb_stor_access_xfer_buf((unsigned char *) bcs,
US_BULK_CS_WRAP_LEN, srb, &sg,
&offset, FROM_XFER_BUF) ==
US_BULK_CS_WRAP_LEN &&
bcs->Signature ==
cpu_to_le32(US_BULK_CS_SIGN)) {
usb_stor_dbg(us, "Device skipped data phase\n");
scsi_set_resid(srb, transfer_length);
goto skipped_data_phase;
}
}
}
/*
* See flow chart on pg 15 of the Bulk Only Transport spec for
* an explanation of how this code works.
*/
/* get CSW for device status */
usb_stor_dbg(us, "Attempting to get CSW...\n");
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
bcs, US_BULK_CS_WRAP_LEN, &cswlen);
/*
* Some broken devices add unnecessary zero-length packets to the
* end of their data transfers. Such packets show up as 0-length
* CSWs. If we encounter such a thing, try to read the CSW again.
*/
if (result == USB_STOR_XFER_SHORT && cswlen == 0) {
usb_stor_dbg(us, "Received 0-length CSW; retrying...\n");
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
bcs, US_BULK_CS_WRAP_LEN, &cswlen);
}
/* did the attempt to read the CSW fail? */
if (result == USB_STOR_XFER_STALLED) {
/* get the status again */
usb_stor_dbg(us, "Attempting to get CSW (2nd try)...\n");
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
bcs, US_BULK_CS_WRAP_LEN, NULL);
}
/* if we still have a failure at this point, we're in trouble */
usb_stor_dbg(us, "Bulk status result = %d\n", result);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
skipped_data_phase:
/* check bulk status */
residue = le32_to_cpu(bcs->Residue);
usb_stor_dbg(us, "Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n",
le32_to_cpu(bcs->Signature), bcs->Tag,
residue, bcs->Status);
if (!(bcs->Tag == us->tag || (us->fflags & US_FL_BULK_IGNORE_TAG)) ||
bcs->Status > US_BULK_STAT_PHASE) {
usb_stor_dbg(us, "Bulk logical error\n");
return USB_STOR_TRANSPORT_ERROR;
}
/*
* Some broken devices report odd signatures, so we do not check them
* for validity against the spec. We store the first one we see,
* and check subsequent transfers for validity against this signature.
*/
if (!us->bcs_signature) {
us->bcs_signature = bcs->Signature;
if (us->bcs_signature != cpu_to_le32(US_BULK_CS_SIGN))
usb_stor_dbg(us, "Learnt BCS signature 0x%08X\n",
le32_to_cpu(us->bcs_signature));
} else if (bcs->Signature != us->bcs_signature) {
usb_stor_dbg(us, "Signature mismatch: got %08X, expecting %08X\n",
le32_to_cpu(bcs->Signature),
le32_to_cpu(us->bcs_signature));
return USB_STOR_TRANSPORT_ERROR;
}
/*
* try to compute the actual residue, based on how much data
* was really transferred and what the device tells us
*/
if (residue && !(us->fflags & US_FL_IGNORE_RESIDUE)) {
/*
* Heuristically detect devices that generate bogus residues
* by seeing what happens with INQUIRY and READ CAPACITY
* commands.
*/
if (bcs->Status == US_BULK_STAT_OK &&
scsi_get_resid(srb) == 0 &&
((srb->cmnd[0] == INQUIRY &&
transfer_length == 36) ||
(srb->cmnd[0] == READ_CAPACITY &&
transfer_length == 8))) {
us->fflags |= US_FL_IGNORE_RESIDUE;
} else {
residue = min(residue, transfer_length);
scsi_set_resid(srb, max(scsi_get_resid(srb),
(int) residue));
}
}
/* based on the status code, we report good or bad */
switch (bcs->Status) {
case US_BULK_STAT_OK:
/* device babbled -- return fake sense data */
if (fake_sense) {
memcpy(srb->sense_buffer,
usb_stor_sense_invalidCDB,
sizeof(usb_stor_sense_invalidCDB));
return USB_STOR_TRANSPORT_NO_SENSE;
}
/* command good -- note that data could be short */
return USB_STOR_TRANSPORT_GOOD;
case US_BULK_STAT_FAIL:
/* command failed */
return USB_STOR_TRANSPORT_FAILED;
case US_BULK_STAT_PHASE:
/*
* phase error -- note that a transport reset will be
* invoked by the invoke_transport() function
*/
return USB_STOR_TRANSPORT_ERROR;
}
/* we should never get here, but if we do, we're in trouble */
return USB_STOR_TRANSPORT_ERROR;
}
EXPORT_SYMBOL_GPL(usb_stor_Bulk_transport);
实测代码长218行,非常显然,usb_stor_Bulk_transport()和usb_stor_transparent_scsi_command()才是USB storage的主体核心;下面逐一进行分析;
函数实现了很多变量,代码如下:
struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf;
unsigned int transfer_length = scsi_bufflen(srb);
unsigned int residue;
int result;
int fake_sense = 0;
unsigned int cswlen;
unsigned int cbwlen = US_BULK_CB_WRAP_LEN;
/* command block wrapper */
struct bulk_cb_wrap {
__le32 Signature; /* contains 'USBC' */
__u32 Tag; /* unique per command id */
__le32 DataTransferLength; /* size of data */
__u8 Flags; /* direction in bit 0 */
__u8 Lun; /* LUN normally 0 */
__u8 Length; /* length of the CDB */
__u8 CDB[16]; /* max command */
};
这个结构体是和手册上定义的Command Block Wrapper (CBW)一模一样的;而us->iobuf是已经被申请了的DMA内存;
/* command status wrapper */
struct bulk_cs_wrap {
__le32 Signature; /* contains 'USBS' */
__u32 Tag; /* same as original command */
__le32 Residue; /* amount not transferred */
__u8 Status; /* see below */
};
usb_stor_Bulk_transport()函数主要分了四个部分,摘取代码英文注释分为如下:
接下来会逐一分小节介绍,在这之前,usb_stor_Bulk_transport()还有个unlikely的判断:
/* Take care of BULK32 devices; set extra byte to 0 */
if (unlikely(us->fflags & US_FL_BULK32)) {
cbwlen = 32;
us->iobuf[31] = 0;
}
即用于根据us->fflags的US_FL_BULK32标记,做出限定CBW的长度,这也是个特例。
==【填充CBW】==
此部分,即优先发送一个SCSI命令,通过封装成CBW结构,即由bcb携带并发送给device:
/* set up the command wrapper */
bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
bcb->DataTransferLength = cpu_to_le32(transfer_length);
bcb->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ?
US_BULK_FLAG_IN : 0;
bcb->Tag = ++us->tag;
bcb->Lun = srb->device->lun;
if (us->fflags & US_FL_SCM_MULT_TARG)
bcb->Lun |= srb->device->id << 4;
bcb->Length = srb->cmd_len;
/* copy the command payload */
memset(bcb->CDB, 0, sizeof(bcb->CDB));
memcpy(bcb->CDB, srb->cmnd, bcb->Length);
/* send it to out endpoint */
usb_stor_dbg(us, "Bulk Command S 0x%x T 0x%x L %d F %d Trg %d LUN %d CL %d\n",
le32_to_cpu(bcb->Signature), bcb->Tag,
le32_to_cpu(bcb->DataTransferLength), bcb->Flags,
(bcb->Lun >> 4), (bcb->Lun & 0x0F),
bcb->Length);
result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
bcb, cbwlen, NULL);
usb_stor_dbg(us, "Bulk command transfer result=%d\n", result);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
以上所涉及到的宏定义代码实现如下(包含上面部分宏定义):
#define US_BULK_CB_WRAP_LEN 31
#define US_BULK_CB_SIGN 0x43425355 /* spells out 'USBC' */
#define US_BULK_FLAG_IN (1 << 7)
#define US_BULK_FLAG_OUT 0
==【拷贝cmd命令数据】==
将srb内携带的cmd数据填充到bcb封装中,此部分也是填充CBW结构:
/* copy the command payload */
memset(bcb->CDB, 0, sizeof(bcb->CDB));
memcpy(bcb->CDB, srb->cmnd, bcb->Length);
==【发送CBW】== 使用usb_stor_bulk_transfer_buf()函数发送携带SCSI smd的CBW,代码实现如下:
/*
* Transfer one buffer via bulk pipe, without timeouts, but allowing early
* termination. Return codes are USB_STOR_XFER_xxx. If the bulk pipe
* stalls during the transfer, the halt is automatically cleared.
*/
int usb_stor_bulk_transfer_buf(struct us_data *us, unsigned int pipe,
void *buf, unsigned int length, unsigned int *act_len)
{
int result;
usb_stor_dbg(us, "xfer %u bytes\n", length);
/* fill and submit the URB */
usb_fill_bulk_urb(us->current_urb, us->pusb_dev, pipe, buf, length,
usb_stor_blocking_completion, NULL);
result = usb_stor_msg_common(us, 0);
/* store the actual length of the data transferred */
if (act_len)
*act_len = us->current_urb->actual_length;
return interpret_urb_result(us, pipe, length, result,
us->current_urb->actual_length);
}
EXPORT_SYMBOL_GPL(usb_stor_bulk_transfer_buf);
代码流程如下:
return interpret_urb_result(us, pipe, length, result,
us->current_urb->actual_length);
/*
* Interpret the results of a URB transfer
*
* This function prints appropriate debugging messages, clears halts on
* non-control endpoints, and translates the status to the corresponding
* USB_STOR_XFER_xxx return code.
*/
static int interpret_urb_result(struct us_data *us, unsigned int pipe,
unsigned int length, int result, unsigned int partial)
{
usb_stor_dbg(us, "Status code %d; transferred %u/%u\n",
result, partial, length);
switch (result) {
/* no error code; did we send all the data? */
case 0:
if (partial != length) {
usb_stor_dbg(us, "-- short transfer\n");
return USB_STOR_XFER_SHORT;
}
usb_stor_dbg(us, "-- transfer complete\n");
return USB_STOR_XFER_GOOD;
/* stalled */
case -EPIPE:
/*
* for control endpoints, (used by CB[I]) a stall indicates
* a failed command
*/
if (usb_pipecontrol(pipe)) {
usb_stor_dbg(us, "-- stall on control pipe\n");
return USB_STOR_XFER_STALLED;
}
/* for other sorts of endpoint, clear the stall */
usb_stor_dbg(us, "clearing endpoint halt for pipe 0x%x\n",
pipe);
if (usb_stor_clear_halt(us, pipe) < 0)
return USB_STOR_XFER_ERROR;
return USB_STOR_XFER_STALLED;
/* babble - the device tried to send more than we wanted to read */
case -EOVERFLOW:
usb_stor_dbg(us, "-- babble\n");
return USB_STOR_XFER_LONG;
/* the transfer was cancelled by abort, disconnect, or timeout */
case -ECONNRESET:
usb_stor_dbg(us, "-- transfer cancelled\n");
return USB_STOR_XFER_ERROR;
/* short scatter-gather read transfer */
case -EREMOTEIO:
usb_stor_dbg(us, "-- short read transfer\n");
return USB_STOR_XFER_SHORT;
/* abort or disconnect in progress */
case -EIO:
usb_stor_dbg(us, "-- abort or disconnect in progress\n");
return USB_STOR_XFER_ERROR;
/* the catch-all error case */
default:
usb_stor_dbg(us, "-- unknown error\n");
return USB_STOR_XFER_ERROR;
}
}
针对不同的urb返回状态,返回不同的宏定义标记,宏定义实现如下:
/*
* usb_stor_bulk_transfer_xxx() return codes, in order of severity
*/
#define USB_STOR_XFER_GOOD 0 /* good transfer */
#define USB_STOR_XFER_SHORT 1 /* transferred less than expected */
#define USB_STOR_XFER_STALLED 2 /* endpoint stalled */
#define USB_STOR_XFER_LONG 3 /* device tried to send too much */
#define USB_STOR_XFER_ERROR 4 /* transfer died in the middle */
==【结果检查】==
发送完CBW后,此处检查发送urb的状态,即本次USB传输是否成功,若不成功,也就没必须继续了。
usb_stor_dbg(us, "Bulk command transfer result=%d\n", result);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
先对us->fflags的US_FL_GO_SLOW进行检测,函数解析如下:
/*
* Some USB-IDE converter chips need a 100us delay between the
* command phase and the data phase. Some devices need a little
* more than that, probably because of clock rate inaccuracies.
*/
if (unlikely(us->fflags & US_FL_GO_SLOW))
usleep_range(125, 150);
然后针对有中间数据传输的情况进行数据的传送,代码实现如下:
if (transfer_length) {
unsigned int pipe = srb->sc_data_direction == DMA_FROM_DEVICE ?
us->recv_bulk_pipe : us->send_bulk_pipe;
result = usb_stor_bulk_srb(us, pipe, srb);
usb_stor_dbg(us, "Bulk data transfer result 0x%x\n", result);
if (result == USB_STOR_XFER_ERROR)
return USB_STOR_TRANSPORT_ERROR;
/*
* If the device tried to send back more data than the
* amount requested, the spec requires us to transfer
* the CSW anyway. Since there's no point retrying the
* the command, we'll return fake sense data indicating
* Illegal Request, Invalid Field in CDB.
*/
if (result == USB_STOR_XFER_LONG)
fake_sense = 1;
/*
* Sometimes a device will mistakenly skip the data phase
* and go directly to the status phase without sending a
* zero-length packet. If we get a 13-byte response here,
* check whether it really is a CSW.
*/
if (result == USB_STOR_XFER_SHORT &&
srb->sc_data_direction == DMA_FROM_DEVICE &&
transfer_length - scsi_get_resid(srb) ==
US_BULK_CS_WRAP_LEN) {
struct scatterlist *sg = NULL;
unsigned int offset = 0;
if (usb_stor_access_xfer_buf((unsigned char *) bcs,
US_BULK_CS_WRAP_LEN, srb, &sg,
&offset, FROM_XFER_BUF) ==
US_BULK_CS_WRAP_LEN &&
bcs->Signature ==
cpu_to_le32(US_BULK_CS_SIGN)) {
usb_stor_dbg(us, "Device skipped data phase\n");
scsi_set_resid(srb, transfer_length);
goto skipped_data_phase;
}
}
}
下面解析代码流程:
/*
* Common used function. Transfer a complete command
* via usb_stor_bulk_transfer_sglist() above. Set cmnd resid
*/
int usb_stor_bulk_srb(struct us_data* us, unsigned int pipe,
struct scsi_cmnd* srb)
{
unsigned int partial;
int result = usb_stor_bulk_transfer_sglist(us, pipe, scsi_sglist(srb),
scsi_sg_count(srb), scsi_bufflen(srb),
&partial);
scsi_set_resid(srb, scsi_bufflen(srb) - partial);
return result;
}
EXPORT_SYMBOL_GPL(usb_stor_bulk_srb);
/*
* Transfer a scatter-gather list via bulk transfer
*
* This function does basically the same thing as usb_stor_bulk_transfer_buf()
* above, but it uses the usbcore scatter-gather library.
*/
static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe,
struct scatterlist *sg, int num_sg, unsigned int length,
unsigned int *act_len)
{
int result;
/* don't submit s-g requests during abort processing */
if (test_bit(US_FLIDX_ABORTING, &us->dflags))
return USB_STOR_XFER_ERROR;
/* initialize the scatter-gather request block */
usb_stor_dbg(us, "xfer %u bytes, %d entries\n", length, num_sg);
result = usb_sg_init(&us->current_sg, us->pusb_dev, pipe, 0,
sg, num_sg, length, GFP_NOIO);
if (result) {
usb_stor_dbg(us, "usb_sg_init returned %d\n", result);
return USB_STOR_XFER_ERROR;
}
/*
* since the block has been initialized successfully, it's now
* okay to cancel it
*/
set_bit(US_FLIDX_SG_ACTIVE, &us->dflags);
/* did an abort occur during the submission? */
if (test_bit(US_FLIDX_ABORTING, &us->dflags)) {
/* cancel the request, if it hasn't been cancelled already */
if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags)) {
usb_stor_dbg(us, "-- cancelling sg request\n");
usb_sg_cancel(&us->current_sg);
}
}
/* wait for the completion of the transfer */
usb_sg_wait(&us->current_sg);
clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags);
result = us->current_sg.status;
if (act_len)
*act_len = us->current_sg.bytes;
return interpret_urb_result(us, pipe, length, result,
us->current_sg.bytes);
}
直接看usb_stor_bulk_transfer_sglist()函数实现,此处注释已讲解明了,通过bulk pipe传输scatter-gather列表;此处调用了USB core层的scatter-gather库实现;
进函数即检测us->dflags的US_FLIDX_ABORTING标记,确认当前USB storage未出错;
下面得着重介绍下scatter-gather库的实现,现看函数usb_sg_init(),查看该函数第一个参数:us->current_sg,这是一个struct usb_sg_request结构体元素,长得和us->current_urb差不多,摘抄struct us_data结构体内这部分元素定义:
/* control and bulk communications data */
struct urb *current_urb; /* USB requests */
struct usb_ctrlrequest *cr; /* control requests */
struct usb_sg_request current_sg; /* scatter-gather req. */
此部分即,当传输scsi cmd时,因为数据量少,所以使用了urb request;此处会做大数据量传输,所以使用scatter-gather request;对于每次 urb 请求,我们所作的只是申请一个结构体变量或者说申请指针然后申请内存,第二步就是提交 urb,即调用 usb_submit_urb(),剩下的事情 usb core 就会去帮我们处理了,Linux 中的模块机制酷就酷在这里,每个模块都给别人服务,也同时享受着别人提供的服务。同样对于 sg request,usb core 也实现了这些,我们只需要申请并初始化一个 struct usb_sg_request 的结构体,然后提交,然后 usb core 那边自然就知道该怎么处理了。查看struct usb_sg_request结构体实现:
/**
* struct usb_sg_request - support for scatter/gather I/O
* @status: zero indicates success, else negative errno
* @bytes: counts bytes transferred.
*
* These requests are initialized using usb_sg_init(), and then are used
* as request handles passed to usb_sg_wait() or usb_sg_cancel(). Most
* members of the request object aren't for driver access.
*
* The status and bytecount values are valid only after usb_sg_wait()
* returns. If the status is zero, then the bytecount matches the total
* from the request.
*
* After an error completion, drivers may need to clear a halt condition
* on the endpoint.
*/
struct usb_sg_request {
int status;
size_t bytes;
/* private:
* members below are private to usbcore,
* and are not provided for driver access!
*/
spinlock_t lock;
struct usb_device *dev;
int pipe;
int entries;
struct urb **urbs;
int count;
struct completion complete;
};
整个 usb 系统都会使用这个数据结构,如果我们希望使用 scatter-gather 方式的话。usb core 已经为我们准备好了数据结构和相应的函数,我们只需要调用即可。一共有三个函数,她们是usb_sg_init(),usb_sg_wait(),usb_sg_cancel()。我们要提交一个 sg 请求,需要做的是,先用 usb_sg_init 来初始化请求,然后 usb_sg_wait()正式提交,然后我们该做的就都做了。如果想撤销一个 sg 请求,那么调用usb_sg_cancel 即可。
分析一下usb_sg_init()的参数:
继续分析:
回到usb_stor_bulk_srb()函数,再用scsi_set_resid设置未传输完的字节数;
回到usb_stor_Bulk_transport(),检测本次的传输结果状态:
/* babble - the device tried to send more than we wanted to read */
case -EOVERFLOW:
usb_stor_dbg(us, "-- babble\n");
return USB_STOR_XFER_LONG;
即在从device读模式时,device发的数据大于host想要读取的,后面再解析fake_sense不为0的处理;
==【skipped_data_phase判断】==
/*
* Sometimes a device will mistakenly skip the data phase
* and go directly to the status phase without sending a
* zero-length packet. If we get a 13-byte response here,
* check whether it really is a CSW.
*/
if (result == USB_STOR_XFER_SHORT &&
srb->sc_data_direction == DMA_FROM_DEVICE &&
transfer_length - scsi_get_resid(srb) ==
US_BULK_CS_WRAP_LEN) {
struct scatterlist *sg = NULL;
unsigned int offset = 0;
if (usb_stor_access_xfer_buf((unsigned char *) bcs,
US_BULK_CS_WRAP_LEN, srb, &sg,
&offset, FROM_XFER_BUF) ==
US_BULK_CS_WRAP_LEN &&
bcs->Signature ==
cpu_to_le32(US_BULK_CS_SIGN)) {
usb_stor_dbg(us, "Device skipped data phase\n");
scsi_set_resid(srb, transfer_length);
goto skipped_data_phase;
}
}
注释上说,有些device会犯错误,直接跳过了data环节,直接传回CSW状态,这个时候USB storage驱动就得判断分析以下了。
#define US_BULK_CS_WRAP_LEN 13
unsigned int transfer_length = scsi_bufflen(srb);
static inline int scsi_get_resid(struct scsi_cmnd *cmd)
{
return cmd->sdb.resid;
}
static inline void scsi_set_resid(struct scsi_cmnd *cmd, int resid)
{
cmd->sdb.resid = resid;
}
scsi_set_resid(srb, scsi_bufflen(srb) - partial);
主要还是各个实现太分散,顾此忘彼,USB storage驱动在传输完一次sg后,partial为实际的传输长度;transfer_length则是host希望传输/接收的数据长度;所以对于DMA_FROM_DEVICE这种情况,此处的transfer_length - scsi_get_resid(srb)即是这个partial的值。而如果partial等于了US_BULK_CS_WRAP_LEN,即13字节,则需要再做下判断,是data数据就是13字节,还是device误操作把CSW给提前传上来了,而没有穿个0长度包。
继续代码
手册第15页的流程图对此部分代码做了解析,再次贴出该流程和接下来的代码部分如下:
/*
* See flow chart on pg 15 of the Bulk Only Transport spec for
* an explanation of how this code works.
*/
/* get CSW for device status */
usb_stor_dbg(us, "Attempting to get CSW...\n");
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
bcs, US_BULK_CS_WRAP_LEN, &cswlen);
/*
* Some broken devices add unnecessary zero-length packets to the
* end of their data transfers. Such packets show up as 0-length
* CSWs. If we encounter such a thing, try to read the CSW again.
*/
if (result == USB_STOR_XFER_SHORT && cswlen == 0) {
usb_stor_dbg(us, "Received 0-length CSW; retrying...\n");
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
bcs, US_BULK_CS_WRAP_LEN, &cswlen);
}
/* did the attempt to read the CSW fail? */
if (result == USB_STOR_XFER_STALLED) {
/* get the status again */
usb_stor_dbg(us, "Attempting to get CSW (2nd try)...\n");
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
bcs, US_BULK_CS_WRAP_LEN, NULL);
}
/* if we still have a failure at this point, we're in trouble */
usb_stor_dbg(us, "Bulk status result = %d\n", result);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
...
下面根据流程图来逐步解析代码:
/*
* Transfer one buffer via bulk pipe, without timeouts, but allowing early
* termination. Return codes are USB_STOR_XFER_xxx. If the bulk pipe
* stalls during the transfer, the halt is automatically cleared.
*/
int usb_stor_bulk_transfer_buf(struct us_data *us, unsigned int pipe,
void *buf, unsigned int length, unsigned int *act_len)
{
int result;
usb_stor_dbg(us, "xfer %u bytes\n", length);
/* fill and submit the URB */
usb_fill_bulk_urb(us->current_urb, us->pusb_dev, pipe, buf, length,
usb_stor_blocking_completion, NULL);
result = usb_stor_msg_common(us, 0);
/* store the actual length of the data transferred */
if (act_len)
*act_len = us->current_urb->actual_length;
return interpret_urb_result(us, pipe, length, result,
us->current_urb->actual_length);
}
EXPORT_SYMBOL_GPL(usb_stor_bulk_transfer_buf);
继续将传输结果赋给result;
/*
* Some broken devices add unnecessary zero-length packets to the
* end of their data transfers. Such packets show up as 0-length
* CSWs. If we encounter such a thing, try to read the CSW again.
*/
if (result == USB_STOR_XFER_SHORT && cswlen == 0) {
usb_stor_dbg(us, "Received 0-length CSW; retrying...\n");
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
bcs, US_BULK_CS_WRAP_LEN, &cswlen);
}
/* did the attempt to read the CSW fail? */
if (result == USB_STOR_XFER_STALLED) {
/* get the status again */
usb_stor_dbg(us, "Attempting to get CSW (2nd try)...\n");
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
bcs, US_BULK_CS_WRAP_LEN, NULL);
}
/* if we still have a failure at this point, we're in trouble */
usb_stor_dbg(us, "Bulk status result = %d\n", result);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
特别将状态检测做一小节来解析;
if (!(bcs->Tag == us->tag || (us->fflags & US_FL_BULK_IGNORE_TAG)) ||
bcs->Status > US_BULK_STAT_PHASE) {
usb_stor_dbg(us, "Bulk logical error\n");
return USB_STOR_TRANSPORT_ERROR;
}
此处只有在bcs->Tag等于bcb->Tag;us->fflags没有设置US_FL_BULK_IGNORE_TAG标记;bcs->Status小于等于US_BULK_STAT_PHASE时才不会进if语句;
/*
* Some broken devices report odd signatures, so we do not check them
* for validity against the spec. We store the first one we see,
* and check subsequent transfers for validity against this signature.
*/
if (!us->bcs_signature) {
us->bcs_signature = bcs->Signature;
if (us->bcs_signature != cpu_to_le32(US_BULK_CS_SIGN))
usb_stor_dbg(us, "Learnt BCS signature 0x%08X\n",
le32_to_cpu(us->bcs_signature));
} else if (bcs->Signature != us->bcs_signature) {
usb_stor_dbg(us, "Signature mismatch: got %08X, expecting %08X\n",
le32_to_cpu(bcs->Signature),
le32_to_cpu(us->bcs_signature));
return USB_STOR_TRANSPORT_ERROR;
}
/*
* try to compute the actual residue, based on how much data
* was really transferred and what the device tells us
*/
if (residue && !(us->fflags & US_FL_IGNORE_RESIDUE)) {
/*
* Heuristically detect devices that generate bogus residues
* by seeing what happens with INQUIRY and READ CAPACITY
* commands.
*/
if (bcs->Status == US_BULK_STAT_OK &&
scsi_get_resid(srb) == 0 &&
((srb->cmnd[0] == INQUIRY &&
transfer_length == 36) ||
(srb->cmnd[0] == READ_CAPACITY &&
transfer_length == 8))) {
us->fflags |= US_FL_IGNORE_RESIDUE;
} else {
residue = min(residue, transfer_length);
scsi_set_resid(srb, max(scsi_get_resid(srb),
(int) residue));
}
}
/* based on the status code, we report good or bad */
switch (bcs->Status) {
case US_BULK_STAT_OK:
/* device babbled -- return fake sense data */
if (fake_sense) {
memcpy(srb->sense_buffer,
usb_stor_sense_invalidCDB,
sizeof(usb_stor_sense_invalidCDB));
return USB_STOR_TRANSPORT_NO_SENSE;
}
/* command good -- note that data could be short */
return USB_STOR_TRANSPORT_GOOD;
case US_BULK_STAT_FAIL:
/* command failed */
return USB_STOR_TRANSPORT_FAILED;
case US_BULK_STAT_PHASE:
/*
* phase error -- note that a transport reset will be
* invoked by the invoke_transport() function
*/
return USB_STOR_TRANSPORT_ERROR;
}
CSW返回的bCSWStatus段,只会有四种状态:
Value | Description |
---|---|
00h | Command Passed ("good status") |
01h | Command Failed |
02h | Phase Error |
03h and 04h | Reserved (Obsolete) |
05h to FFh | Reserved |
其中大于02h的情况已经被拦截,所以此处只需要讨论前面三种状态了;另外在bcs->Status的US_BULK_STAT_OK下,还存在一种fake_sense=1的可能,即在data阶段从device读数据时,device多发了数据给host,此处定义device babbled,用fake_sense表示,当是这种情况时,需要返回给host一个封装好的无效cdb命令结果。所以就在此处捏造一个处理结果:
case US_BULK_STAT_OK:
/* device babbled -- return fake sense data */
if (fake_sense) {
memcpy(srb->sense_buffer,
usb_stor_sense_invalidCDB,
sizeof(usb_stor_sense_invalidCDB));
return USB_STOR_TRANSPORT_NO_SENSE;
}
其中usb_stor_sense_invalidCDB()结构定义在drivers/usb/storage/scsiglue.c中,实现如下:
/* To Report "Illegal Request: Invalid Field in CDB */
unsigned char usb_stor_sense_invalidCDB[18] = {
[0] = 0x70, /* current error */
[2] = ILLEGAL_REQUEST, /* Illegal Request = 0x05 */
[7] = 0x0a, /* additional length */
[12] = 0x24 /* Invalid Field in CDB */
};
EXPORT_SYMBOL_GPL(usb_stor_sense_invalidCDB);
这是一个字符数组,共18个元素,初始化的时候其中4个元素被赋值。这个结构是按照SCSI协议的sense data的格式来定义的,原理则是如果一个设备接收到一个Request Sense命令,那么它将按规则返回一个sense data。
此处的意思是将usb_stor_sense_invalidCDB数组拷贝给srb->sense_buffer,然后返回USB_STOR_TRANSPORT_NO_SENSE结果,struct scsi_cmnd结构体内对sense_buffer元素的定义及解释如下:
#define SCSI_SENSE_BUFFERSIZE 96
unsigned char *sense_buffer;
/* obtained by REQUEST SENSE when
* CHECK CONDITION is received on original
* command (auto-sense) */
SCSI协议里面规定了,当一个SCSI命令执行出了错,可以再发送一个REQUEST SENSE命令给目标device,然后它会返回一些信息,即sense data,而这部分逻辑被放在了底层驱动中来实现,因为某些scsi host卡可以自动发送REQUEST SENSE命令去了解详情,所以为了统一,就让内核底层驱动来判断了。这个即后续的need_auto_sense变量。
/* we should never get here, but if we do, we're in trouble */
return USB_STOR_TRANSPORT_ERROR;
至此,usb_stor_Bulk_transport()函数解析完成;回到usb_stor_transparent_scsi_command()继续跟进。
usb_stor_transparent_scsi_command()直接封装了usb_stor_invoke_transport();上一小节介绍了usb_stor_invoke_transport()函数中的us->transport(srb, us)实现过程;现在继续分小段来跟踪代码:
result = us->transport(srb, us);
/*
* if the command gets aborted by the higher layers, we need to
* short-circuit all other processing
*/
if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
usb_stor_dbg(us, "-- command was aborted\n");
srb->result = DID_ABORT << 16;
goto Handle_Errors;
}
/* if there is a transport error, reset and don't auto-sense */
if (result == USB_STOR_TRANSPORT_ERROR) {
usb_stor_dbg(us, "-- transport indicates error, resetting\n");
srb->result = DID_ERROR << 16;
goto Handle_Errors;
}
/* if the transport provided its own sense data, don't auto-sense */
if (result == USB_STOR_TRANSPORT_NO_SENSE) {
srb->result = SAM_STAT_CHECK_CONDITION;
last_sector_hacks(us, srb);
return;
}
srb->result = SAM_STAT_GOOD;
result结果是从us->transport(srb, us)返回的,它只会返回四个结果:USB_STOR_TRANSPORT_NO_SENSE、USB_STOR_TRANSPORT_GOOD、USB_STOR_TRANSPORT_FAILED、USB_STOR_TRANSPORT_ERROR。
need_auto_sense变量是用来进一步确认是否需要发送REQUEST SENSE命令给device。此处是在判断是否需要设置该参数:
/*
* Determine if we need to auto-sense
*
* I normally don't use a flag like this, but it's almost impossible
* to understand what's going on here if I don't.
*/
need_auto_sense = 0;
/*
* If we're running the CB transport, which is incapable
* of determining status on its own, we will auto-sense
* unless the operation involved a data-in transfer. Devices
* can signal most data-in errors by stalling the bulk-in pipe.
*/
if ((us->protocol == USB_PR_CB || us->protocol == USB_PR_DPCM_USB) &&
srb->sc_data_direction != DMA_FROM_DEVICE) {
usb_stor_dbg(us, "-- CB transport device requiring auto-sense\n");
need_auto_sense = 1;
}
/*
* If we have a failure, we're going to do a REQUEST_SENSE
* automatically. Note that we differentiate between a command
* "failure" and an "error" in the transport mechanism.
*/
if (result == USB_STOR_TRANSPORT_FAILED) {
usb_stor_dbg(us, "-- transport indicates command failure\n");
need_auto_sense = 1;
}
里面的注释也是非常有趣的,先初始化need_auto_sense为0,然后分别对各种情况做判断:
/*
* Determine if this device is SAT by seeing if the
* command executed successfully. Otherwise we'll have
* to wait for at least one CHECK_CONDITION to determine
* SANE_SENSE support
*/
if (unlikely((srb->cmnd[0] == ATA_16 || srb->cmnd[0] == ATA_12) &&
result == USB_STOR_TRANSPORT_GOOD &&
!(us->fflags & US_FL_SANE_SENSE) &&
!(us->fflags & US_FL_BAD_SENSE) &&
!(srb->cmnd[2] & 0x20))) {
usb_stor_dbg(us, "-- SAT supported, increasing auto-sense\n");
us->fflags |= US_FL_SANE_SENSE;
}
/*
* A short transfer on a command where we don't expect it
* is unusual, but it doesn't mean we need to auto-sense.
*/
if ((scsi_get_resid(srb) > 0) &&
!((srb->cmnd[0] == REQUEST_SENSE) ||
(srb->cmnd[0] == INQUIRY) ||
(srb->cmnd[0] == MODE_SENSE) ||
(srb->cmnd[0] == LOG_SENSE) ||
(srb->cmnd[0] == MODE_SENSE_10))) {
usb_stor_dbg(us, "-- unexpectedly short transfer\n");
}
在讲解need_auto_sense情况之前,我们还是先简单学习一下SENSE DATA数据结构,该结构由SCSI手册定义,手册名:SCSI Primary Commands,当前最新为第5版,全称:spc5r18.pdf^sample_4
spc5r18.pdf手册的4.4节详细介绍了sense data结构,分为两类:fixed format和descriptor format。两种结构存在共同元素,但是所在结构中的位置不一致,需要分别取出来进行处理。先贴出两个结构的格式图:
Descriptor format sense data
Fixed format sense data
内核里面用struct scsi_sense_hdr结构来对应sense data内通用元素,该结构定义如下:
/*
* This is a slightly modified SCSI sense "descriptor" format header.
* The addition is to allow the 0x70 and 0x71 response codes. The idea
* is to place the salient data from either "fixed" or "descriptor" sense
* format into one structure to ease application processing.
*
* The original sense buffer should be kept around for those cases
* in which more information is required (e.g. the LBA of a MEDIUM ERROR).
*/
struct scsi_sense_hdr { /* See SPC-3 section 4.5 */
u8 response_code; /* permit: 0x0, 0x70, 0x71, 0x72, 0x73 */
u8 sense_key;
u8 asc;
u8 ascq;
u8 byte4;
u8 byte5;
u8 byte6;
u8 additional_length; /* always 0 for fixed sense format */
};
因为后续代码会用到sense_key、asc、ascq三个字段,分别对应手册中的SENSE KEY、ADDITIONAL SENSE CODE、ADDITIONAL SENSE CODE QUALIFIER字段,这三个字段提供了一个分层的信息,这个分层结构为应用程序客户端提供一个自顶向下的方法,以决定信息的报告状态。下面简单介绍下这三个元素的作用:
判断了各类情况后,下面开始对need_auto_sense为1的情况进行处理;
/* Now, if we need to do the auto-sense, let's do it */
if (need_auto_sense) {
int temp_result;
struct scsi_eh_save ses;
int sense_size = US_SENSE_SIZE;
struct scsi_sense_hdr sshdr;
const u8 *scdd;
u8 fm_ili;
/* device supports and needs bigger sense buffer */
if (us->fflags & US_FL_SANE_SENSE)
sense_size = ~0;
Retry_Sense:
usb_stor_dbg(us, "Issuing auto-REQUEST_SENSE\n");
scsi_eh_prep_cmnd(srb, &ses, NULL, 0, sense_size);
/* FIXME: we must do the protocol translation here */
if (us->subclass == USB_SC_RBC || us->subclass == USB_SC_SCSI ||
us->subclass == USB_SC_CYP_ATACB)
srb->cmd_len = 6;
else
srb->cmd_len = 12;
/* issue the auto-sense command */
scsi_set_resid(srb, 0);
temp_result = us->transport(us->srb, us);
/* let's clean up right away */
scsi_eh_restore_cmnd(srb, &ses);
if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
usb_stor_dbg(us, "-- auto-sense aborted\n");
srb->result = DID_ABORT << 16;
/* If SANE_SENSE caused this problem, disable it */
if (sense_size != US_SENSE_SIZE) {
us->fflags &= ~US_FL_SANE_SENSE;
us->fflags |= US_FL_BAD_SENSE;
}
goto Handle_Errors;
}
/*
* Some devices claim to support larger sense but fail when
* trying to request it. When a transport failure happens
* using US_FS_SANE_SENSE, we always retry with a standard
* (small) sense request. This fixes some USB GSM modems
*/
if (temp_result == USB_STOR_TRANSPORT_FAILED &&
sense_size != US_SENSE_SIZE) {
usb_stor_dbg(us, "-- auto-sense failure, retry small sense\n");
sense_size = US_SENSE_SIZE;
us->fflags &= ~US_FL_SANE_SENSE;
us->fflags |= US_FL_BAD_SENSE;
goto Retry_Sense;
}
/* Other failures */
if (temp_result != USB_STOR_TRANSPORT_GOOD) {
usb_stor_dbg(us, "-- auto-sense failure\n");
/*
* we skip the reset if this happens to be a
* multi-target device, since failure of an
* auto-sense is perfectly valid
*/
srb->result = DID_ERROR << 16;
if (!(us->fflags & US_FL_SCM_MULT_TARG))
goto Handle_Errors;
return;
}
/*
* If the sense data returned is larger than 18-bytes then we
* assume this device supports requesting more in the future.
* The response code must be 70h through 73h inclusive.
*/
if (srb->sense_buffer[7] > (US_SENSE_SIZE - 8) &&
!(us->fflags & US_FL_SANE_SENSE) &&
!(us->fflags & US_FL_BAD_SENSE) &&
(srb->sense_buffer[0] & 0x7C) == 0x70) {
usb_stor_dbg(us, "-- SANE_SENSE support enabled\n");
us->fflags |= US_FL_SANE_SENSE;
/*
* Indicate to the user that we truncated their sense
* because we didn't know it supported larger sense.
*/
usb_stor_dbg(us, "-- Sense data truncated to %i from %i\n",
US_SENSE_SIZE,
srb->sense_buffer[7] + 8);
srb->sense_buffer[7] = (US_SENSE_SIZE - 8);
}
scsi_normalize_sense(srb->sense_buffer, SCSI_SENSE_BUFFERSIZE,
&sshdr);
usb_stor_dbg(us, "-- Result from auto-sense is %d\n",
temp_result);
usb_stor_dbg(us, "-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
sshdr.response_code, sshdr.sense_key,
sshdr.asc, sshdr.ascq);
#ifdef CONFIG_USB_STORAGE_DEBUG
usb_stor_show_sense(us, sshdr.sense_key, sshdr.asc, sshdr.ascq);
#endif
/* set the result so the higher layers expect this data */
srb->result = SAM_STAT_CHECK_CONDITION;
scdd = scsi_sense_desc_find(srb->sense_buffer,
SCSI_SENSE_BUFFERSIZE, 4);
fm_ili = (scdd ? scdd[3] : srb->sense_buffer[2]) & 0xA0;
/*
* We often get empty sense data. This could indicate that
* everything worked or that there was an unspecified
* problem. We have to decide which.
*/
if (sshdr.sense_key == 0 && sshdr.asc == 0 && sshdr.ascq == 0 &&
fm_ili == 0) {
/*
* If things are really okay, then let's show that.
* Zero out the sense buffer so the higher layers
* won't realize we did an unsolicited auto-sense.
*/
if (result == USB_STOR_TRANSPORT_GOOD) {
srb->result = SAM_STAT_GOOD;
srb->sense_buffer[0] = 0x0;
}
/*
* ATA-passthru commands use sense data to report
* the command completion status, and often devices
* return Check Condition status when nothing is
* wrong.
*/
else if (srb->cmnd[0] == ATA_16 ||
srb->cmnd[0] == ATA_12) {
/* leave the data alone */
}
/*
* If there was a problem, report an unspecified
* hardware error to prevent the higher layers from
* entering an infinite retry loop.
*/
else {
srb->result = DID_ERROR << 16;
if ((sshdr.response_code & 0x72) == 0x72)
srb->sense_buffer[1] = HARDWARE_ERROR;
else
srb->sense_buffer[2] = HARDWARE_ERROR;
}
}
}
此处的主要工作就是发送REQUEST SENSE命令,这个逻辑和之前一样的,即再进行一次Bulk传输,还是三个阶段;现准备一个struct scsi_cmnd,调用us->transport(us->srb, us),然后结束后检查结果。但是此处并没有再申请一个新的struct scsi_cmnd结构,而是沿用了之前的us->srb,只是在用之前调用scsi_eh_prep_cmnd()函数,将srb的一些关键信息保存在struct scsi_eh_save结构实例ses中,等调用完us->transport(us->srb, us)时候,再调用scsi_eh_restore_cmnd()恢复回来。
中间还对USB_SC_RBC/USB_SC_SCSI/USB_SC_CYP_ATACB类设备做了限定,即对于该类设备,REQUEST SENSE长度只需要6字节,而其它的设备该命令长度为12字节。
在分析temp_result结果时,先确认下us->dflags的US_FLIDX_TIMED_OUT标记,并且对存在US_FL_SANE_SENSE的情况进行了分析;即是否是因为Sane Sense大于18字节,才导致了此超时,如果是,那么就没必要继续分析了,直接跳到Handle_Errors流程。
对于大于18字节的sense导致的failed的情况,如注释所说,驱动可以再重新发送一次标准的sense request命令,这能修复部分USB GSM模型;
/*
* Some devices claim to support larger sense but fail when
* trying to request it. When a transport failure happens
* using US_FS_SANE_SENSE, we always retry with a standard
* (small) sense request. This fixes some USB GSM modems
*/
if (temp_result == USB_STOR_TRANSPORT_FAILED &&
sense_size != US_SENSE_SIZE) {
usb_stor_dbg(us, "-- auto-sense failure, retry small sense\n");
sense_size = US_SENSE_SIZE;
us->fflags &= ~US_FL_SANE_SENSE;
us->fflags |= US_FL_BAD_SENSE;
goto Retry_Sense;
}
对于其它错误的分析,即说明连REQUEST SENSE命令也failed了,于是设置srb->result = DID_ERROR << 16;此处需要做个进一步判断了,如果device没有设置US_FL_SCM_MULT_TARG标记,即没有多个target的话,那可以直接reset,但是如果是多个target的情况,则就不能这么简单的reset了。
/* Other failures */
if (temp_result != USB_STOR_TRANSPORT_GOOD) {
usb_stor_dbg(us, "-- auto-sense failure\n");
/*
* we skip the reset if this happens to be a
* multi-target device, since failure of an
* auto-sense is perfectly valid
*/
srb->result = DID_ERROR << 16;
if (!(us->fflags & US_FL_SCM_MULT_TARG))
goto Handle_Errors;
return;
}
如果sense data大于18字节,则驱动认为在将来该device还需更大的sense data支持,即此处给它US_FL_SANE_SENSE标记。但是此次只能做截断sense data操作了,所以强迫将srb->sense_buffer[7] = (US_SENSE_SIZE - 8),此处的减8操作,是因为sense_buffer[7]的值就是n - 7的来的,n为返回的数据。
/*
* If the sense data returned is larger than 18-bytes then we
* assume this device supports requesting more in the future.
* The response code must be 70h through 73h inclusive.
*/
if (srb->sense_buffer[7] > (US_SENSE_SIZE - 8) &&
!(us->fflags & US_FL_SANE_SENSE) &&
!(us->fflags & US_FL_BAD_SENSE) &&
(srb->sense_buffer[0] & 0x7C) == 0x70) {
usb_stor_dbg(us, "-- SANE_SENSE support enabled\n");
us->fflags |= US_FL_SANE_SENSE;
/*
* Indicate to the user that we truncated their sense
* because we didn't know it supported larger sense.
*/
usb_stor_dbg(us, "-- Sense data truncated to %i from %i\n",
US_SENSE_SIZE,
srb->sense_buffer[7] + 8);
srb->sense_buffer[7] = (US_SENSE_SIZE - 8);
}
scsi_normalize_sense()函数,即将srb->sense_buffer中通用sense data元素合成到sshdr变量中。代码实现如下:
/**
* scsi_normalize_sense - normalize main elements from either fixed or
* descriptor sense data format into a common format.
*
* @sense_buffer: byte array containing sense data returned by device
* @sb_len: number of valid bytes in sense_buffer
* @sshdr: pointer to instance of structure that common
* elements are written to.
*
* Notes:
* The "main elements" from sense data are: response_code, sense_key,
* asc, ascq and additional_length (only for descriptor format).
*
* Typically this function can be called after a device has
* responded to a SCSI command with the CHECK_CONDITION status.
*
* Return value:
* true if valid sense data information found, else false;
*/
bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
struct scsi_sense_hdr *sshdr)
{
memset(sshdr, 0, sizeof(struct scsi_sense_hdr));
if (!sense_buffer || !sb_len)
return false;
sshdr->response_code = (sense_buffer[0] & 0x7f);
if (!scsi_sense_valid(sshdr))
return false;
if (sshdr->response_code >= 0x72) {
/*
* descriptor format
*/
if (sb_len > 1)
sshdr->sense_key = (sense_buffer[1] & 0xf);
if (sb_len > 2)
sshdr->asc = sense_buffer[2];
if (sb_len > 3)
sshdr->ascq = sense_buffer[3];
if (sb_len > 7)
sshdr->additional_length = sense_buffer[7];
} else {
/*
* fixed format
*/
if (sb_len > 2)
sshdr->sense_key = (sense_buffer[2] & 0xf);
if (sb_len > 7) {
sb_len = (sb_len < (sense_buffer[7] + 8)) ?
sb_len : (sense_buffer[7] + 8);
if (sb_len > 12)
sshdr->asc = sense_buffer[12];
if (sb_len > 13)
sshdr->ascq = sense_buffer[13];
}
}
return true;
}
EXPORT_SYMBOL(scsi_normalize_sense);
接下来三条打印,可以根据这些信息来进行内核调试:
usb_stor_dbg(us, "-- Result from auto-sense is %d\n",
temp_result);
usb_stor_dbg(us, "-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
sshdr.response_code, sshdr.sense_key,
sshdr.asc, sshdr.ascq);
#ifdef CONFIG_USB_STORAGE_DEBUG
usb_stor_show_sense(us, sshdr.sense_key, sshdr.asc, sshdr.ascq);
#endif
预先设置srb->result的状态;
/* set the result so the higher layers expect this data */
srb->result = SAM_STAT_CHECK_CONDITION;
获取sense data中的descriptor format sense data类型的sense data descriptor,此处scsi_sense_desc_find的参数赋值为4,参考手册得到是需要获取Stream commands类型的描述符(SCSI相关目前还未有过研究,此处就不深究了);fm_ili这个变量的作用在此处的作用也未看懂,后续看懂了补上;
scdd = scsi_sense_desc_find(srb->sense_buffer,
SCSI_SENSE_BUFFERSIZE, 4);
fm_ili = (scdd ? scdd[3] : srb->sense_buffer[2]) & 0xA0;
接下来是判断若拿到的是个空的sense data时,则判断各类情况:
/*
* We often get empty sense data. This could indicate that
* everything worked or that there was an unspecified
* problem. We have to decide which.
*/
if (sshdr.sense_key == 0 && sshdr.asc == 0 && sshdr.ascq == 0 &&
fm_ili == 0) {
/*
* If things are really okay, then let's show that.
* Zero out the sense buffer so the higher layers
* won't realize we did an unsolicited auto-sense.
*/
if (result == USB_STOR_TRANSPORT_GOOD) {
srb->result = SAM_STAT_GOOD;
srb->sense_buffer[0] = 0x0;
}
/*
* ATA-passthru commands use sense data to report
* the command completion status, and often devices
* return Check Condition status when nothing is
* wrong.
*/
else if (srb->cmnd[0] == ATA_16 ||
srb->cmnd[0] == ATA_12) {
/* leave the data alone */
}
/*
* If there was a problem, report an unspecified
* hardware error to prevent the higher layers from
* entering an infinite retry loop.
*/
else {
srb->result = DID_ERROR << 16;
if ((sshdr.response_code & 0x72) == 0x72)
srb->sense_buffer[1] = HARDWARE_ERROR;
else
srb->sense_buffer[2] = HARDWARE_ERROR;
}
}
如此,则need_auto_sense的情况处理结束,下面进入正常的结果处理及错误流程处理中;
正如英文注释所说,某些device在处理READ(10)命令时,不能工作或返回错误data等;如果 US_FL_INITIAL_READ10标记也设置了,则保持跟踪以确保READ(10)命令成功。如果失败,则重试;
/*
* Some devices don't work or return incorrect data the first
* time they get a READ(10) command, or for the first READ(10)
* after a media change. If the INITIAL_READ10 flag is set,
* keep track of whether READ(10) commands succeed. If the
* previous one succeeded and this one failed, set the REDO_READ10
* flag to force a retry.
*/
if (unlikely((us->fflags & US_FL_INITIAL_READ10) &&
srb->cmnd[0] == READ_10)) {
if (srb->result == SAM_STAT_GOOD) {
set_bit(US_FLIDX_READ10_WORKED, &us->dflags);
} else if (test_bit(US_FLIDX_READ10_WORKED, &us->dflags)) {
clear_bit(US_FLIDX_READ10_WORKED, &us->dflags);
set_bit(US_FLIDX_REDO_READ10, &us->dflags);
}
/*
* Next, if the REDO_READ10 flag is set, return a result
* code that will cause the SCSI core to retry the READ(10)
* command immediately.
*/
if (test_bit(US_FLIDX_REDO_READ10, &us->dflags)) {
clear_bit(US_FLIDX_REDO_READ10, &us->dflags);
srb->result = DID_IMM_RETRY << 16;
srb->sense_buffer[0] = 0;
}
}
在函数返回前,做最后一次判断:
/* Did we transfer less than the minimum amount required? */
if ((srb->result == SAM_STAT_GOOD || srb->sense_buffer[2] == 0) &&
scsi_bufflen(srb) - scsi_get_resid(srb) < srb->underflow)
srb->result = DID_ERROR << 16;
确保传输的数据不小于最小需求,即srb->underflow;
last_sector_hacks()判断last sector的情况,也是处理某些错误的流程;
当检查错误完后,直接返回,即若没有出错的话,srb->result被赋值SAM_STAT_GOOD;
如果出错的话,则使用port reset重新同步device,如果这也失败的话,则将device reset;即调用us->transport_reset来完成;
/*
* Error and abort processing: try to resynchronize with the device
* by issuing a port reset. If that fails, try a class-specific
* device reset.
*/
Handle_Errors:
/*
* Set the RESETTING bit, and clear the ABORTING bit so that
* the reset may proceed.
*/
scsi_lock(us_to_host(us));
set_bit(US_FLIDX_RESETTING, &us->dflags);
clear_bit(US_FLIDX_ABORTING, &us->dflags);
scsi_unlock(us_to_host(us));
/*
* We must release the device lock because the pre_reset routine
* will want to acquire it.
*/
mutex_unlock(&us->dev_mutex);
result = usb_stor_port_reset(us);
mutex_lock(&us->dev_mutex);
if (result < 0) {
scsi_lock(us_to_host(us));
usb_stor_report_device_reset(us);
scsi_unlock(us_to_host(us));
us->transport_reset(us);
}
clear_bit(US_FLIDX_RESETTING, &us->dflags);
last_sector_hacks(us, srb);
此部分当前限于了解一下,后续若真遇到此类BUG时,再继续深究。
在处理完us->proto_handler(srb, us)之后,继续回到usb_stor_control_thread()中来,先设置dev忙状态:usb_mark_last_busy(us->pusb_dev);
善后工作如下:
/* lock access to the state */
scsi_lock(host);
/* was the command aborted? */
if (srb->result == DID_ABORT << 16) {
SkipForAbort:
usb_stor_dbg(us, "scsi command aborted\n");
srb = NULL; /* Don't call srb->scsi_done() */
}
/*
* If an abort request was received we need to signal that
* the abort has finished. The proper test for this is
* the TIMED_OUT flag, not srb->result == DID_ABORT, because
* the timeout might have occurred after the command had
* already completed with a different result code.
*/
if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
complete(&(us->notify));
/* Allow USB transfers to resume */
clear_bit(US_FLIDX_ABORTING, &us->dflags);
clear_bit(US_FLIDX_TIMED_OUT, &us->dflags);
}
/* finished working on this command */
us->srb = NULL;
scsi_unlock(host);
/* unlock the device pointers */
mutex_unlock(&us->dev_mutex);
清空us->srb为NULL;通过srb->scsi_done通知SCSI core层srb已经处理完成;
/* now that the locks are released, notify the SCSI core */
if (srb) {
usb_stor_dbg(us, "scsi cmd done, result=0x%x\n",
srb->result);
srb->scsi_done(srb);
}
至此,USB storage的代码流程完成,其中还有很多部分未完成注释,虽然这些部分平时很难涉及;但是一般出现BUG时,其实几本都是些非常冷门的逻辑导致的出错;所以,后续希望还能够继续持续更新;
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。