Linux之 USB驱动框架-usb U盘驱动源码分析(8),Linux USB驱动框架下的U盘驱动源码深度解析(八)

马肤

温馨提示:这篇文章已超过444天没有更新,请注意相关的内容是否还可用!

摘要:本篇文章对Linux系统中的USB驱动框架进行深入探讨,特别是对usb U盘驱动源码进行分析。文章详细解析了USB驱动框架的结构和工作原理,以及U盘驱动源码的实现细节。通过源码分析,读者可以更好地理解Linux系统如何识别和管理USB设备,特别是U盘的接入和使用。文章有助于开发人员理解并开发更为稳定和高效的USB驱动,提升Linux系统的实用性。

一、U盘驱动

Linux之 USB驱动框架-usb U盘驱动源码分析(8),Linux USB驱动框架下的U盘驱动源码深度解析(八) 第1张
(图片来源网络,侵删)

USB Mass Storage是一类USB存储设备,这些设备包括USB磁盘、USB硬盘、USB磁带机、USB光驱、U盘、记忆棒、智能卡和一些USB摄像头等,这类设备由USB协议支持。

首先我想去看看/driver/usb/storage/Makefile

Linux之 USB驱动框架-usb U盘驱动源码分析(8),Linux USB驱动框架下的U盘驱动源码深度解析(八) 第2张
(图片来源网络,侵删)

ccflags-y := -I $(srctree)/drivers/scsi

ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE=USB_STORAGE

obj-$(CONFIG_USB_UAS)           += uas.o

obj-$(CONFIG_USB_STORAGE)       += usb-storage.o

usb-storage-y := scsiglue.o protocol.o transport.o usb.o

usb-storage-y += initializers.o sierra_ms.o option_ms.o

usb-storage-y += usual-tables.o

usb-storage-$(CONFIG_USB_STORAGE_DEBUG) += debug.o

obj-$(CONFIG_USB_STORAGE_ALAUDA)        += ums-alauda.o

obj-$(CONFIG_USB_STORAGE_CYPRESS_ATACB) += ums-cypress.o

obj-$(CONFIG_USB_STORAGE_DATAFAB)       += ums-datafab.o

obj-$(CONFIG_USB_STORAGE_ENE_UB6250)    += ums-eneub6250.o

obj-$(CONFIG_USB_STORAGE_FREECOM)       += ums-freecom.o

obj-$(CONFIG_USB_STORAGE_ISD200)        += ums-isd200.o

obj-$(CONFIG_USB_STORAGE_JUMPSHOT)      += ums-jumpshot.o

obj-$(CONFIG_USB_STORAGE_KARMA)         += ums-karma.o

obj-$(CONFIG_USB_STORAGE_ONETOUCH)      += ums-onetouch.o

obj-$(CONFIG_USB_STORAGE_REALTEK)       += ums-realtek.o

obj-$(CONFIG_USB_STORAGE_SDDR09)        += ums-sddr09.o

obj-$(CONFIG_USB_STORAGE_SDDR55)        += ums-sddr55.o

obj-$(CONFIG_USB_STORAGE_USBAT)         += ums-usbat.o

ums-alauda-y            := alauda.o

ums-cypress-y           := cypress_atacb.o

ums-datafab-y           := datafab.o

ums-eneub6250-y         := ene_ub6250.o

ums-freecom-y           := freecom.o

ums-isd200-y            := isd200.o

ums-jumpshot-y          := jumpshot.o

ums-karma-y             := karma.o

ums-onetouch-y          := onetouch.o

ums-realtek-y           := realtek_cr.o

ums-sddr09-y            := sddr09.o

ums-sddr55-y            := sddr55.o

ums-usbat-y             := shuttle_usbat.o

 

 Makefile中前几行代码,在此我进行一个说明。第一行,-I选项表示需要编译的目录。当本Makefile文件被编译器读取时,会先判断/driver/scsi目录下的文件是否已经被编译,如果没有被编译,则先编译该目录下的文件后,再转到该Makefile文件中。第三行就是USB Mass Storage选项,是总指挥。第四、五行说明了这个文件夹也就是usb-storage模块必须包含的文件,这些文件将是主要分析的对象。第六行是调试部分。目前我们分析USB驱动,所以重点去分析这些文件中的usb.c

开始-》

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,

};

module_usb_stor_driver(usb_storage_driver, usb_stor_host_template, DRV_NAME);

 

 #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)); \

} \

 usb_stor_host_template_init

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);

 

 下面重点我们来看看这个probe函数:

/* The main probe routine for standard devices */

static int storage_probe(struct usb_interface *intf,

                         const struct usb_device_id *id)

{

        const 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)

        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

                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;

}

 我们发现U盘驱动的探测分为两个部分,我们先来看看第一个部分usb_stor_probe1

usb_stor_probe1

/* 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,

                const 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)); //分配Scsi_Host结构体

        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);         /从host结构体中提取出us_data结构体

        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);       //将us_data与USB设备相关联

        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);

 

 U盘驱动的探测的第二部分usb_stor_probe2

/* 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);   //添加scsi

        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);

 

 主要通过assocaite_dev(),get_device_info(),get_transport(),get_protocol(),get_pipes()五个函数来为us结构体赋值,然后调用usb_stor_acquire_resources()来得到设备需要的动态资源。最后创建扫描工作 &us->scan_dwork,让用户能通过cat /proc/scsi/scsi看到U盘设备。现在我们一个个分析下这里提到了每个函数。

1 、为us结构体赋值的设备关联函数associate_dev的实现

/* 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);  /把us设置为接口的私有数据

        /* Allocate the control/setup and DMA-mapped buffers */

        us->cr = kmalloc(sizeof(*us->cr), GFP_KERNEL);    //分配控制urb的控制字符空间

        if (!us->cr)

                return -ENOMEM;

        us->iobuf = usb_alloc_coherent(us->pusb_dev, US_IOBUF_SIZE,

                        GFP_KERNEL, &us->iobuf_dma);   /分配urb的缓冲区

        if (!us->iobuf) {

                usb_stor_dbg(us, "I/O buffer allocation failed\n");

                return -ENOMEM;

        }

        return 0;

}

 继续看获得设备信息函数get_device_info的实现

/* Get the unusual_devs entries and the string descriptors */

static int get_device_info(struct us_data *us, const struct usb_device_id *id,

                const 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;   //不常用的设备

//找到USB设备支持的子类和协议

        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;//USB设备不能被系统识别则退出

        }

        /*

         * 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; //USB设备不支持高速则改为低速

        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 "

                                        " and "

                                        "\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;

}

 

 继续看得到传输方式函数get_transport,这个函数主要获得USB设备支持的通信协议,并设置USB驱动的传输类型。对于U盘,USB协议规定它属于Bulk-only的传输方式,也就是它的us->protocot为US_PR_BULK

/* 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:

                us->transport_name = "Bulk";

                us->transport = usb_stor_Bulk_transport;  //传输函数

                us->transport_reset = usb_stor_Bulk_reset;

                break;

        }

}

 接着我们看获得协议信息的get_protocol函数,该函数根据不同的协议,用来设置协议的传输函数。对于U盘,USB协议规定us->subclass为US_SC_SCSI

/* 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:

                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;

        }

}

 

 最后一个初始化us的函数是获得管道信息的get_pipes函数。

/* 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,  //建立输入批量传输端点

    if (ep_int) {

                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;

}

 析完上面get_pipes的代码,需要补充说明的是,在我们的U盘中只有输入批量传输和输出批量传输两个端点,不存在控制端点,如果出现控制端点,那么设备支持CBI协议,即Control/Bulk/Interrupt协议,另外U盘也没有中断端点。

 分析完上面五个对cr初始化的函数后,我们接着需要看usb_stor_acquire_resources了,这个函数主要功能是初始化设备,并创建数据传输的控制线程。

/* 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);  //申请urb

        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_stor_acquire_resources函数中,我们创建并执行了usb_stor_control_thread这个内核线程,这个控制线程用来完成数据的接收和发送,它会一直运行,直到驱动程序退出。

来看看这个控制线程。

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))  //等待用户层SCSI命令唤醒

                        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 sc_data_direction == DMA_BIDIRECTIONAL) { //方向

                        usb_stor_dbg(us, "UNKNOWN data direction\n");

                        srb->result = DID_ERROR 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 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 cmnd[0] == INQUIRY) &&

                            (us->fflags & US_FL_FIX_INQUIRY)) {//如果SCSI是请求命令的处理

                        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


0
收藏0
文章版权声明:除非注明,否则均为VPS857原创文章,转载或复制请以超链接形式并注明出处。

相关阅读

  • 【研发日记】Matlab/Simulink自动生成代码(二)——五种选择结构实现方法,Matlab/Simulink自动生成代码的五种选择结构实现方法(二),Matlab/Simulink自动生成代码的五种选择结构实现方法详解(二)
  • 超级好用的C++实用库之跨平台实用方法,跨平台实用方法的C++实用库超好用指南,C++跨平台实用库使用指南,超好用实用方法集合,C++跨平台实用库超好用指南,方法与技巧集合
  • 【动态规划】斐波那契数列模型(C++),斐波那契数列模型(C++实现与动态规划解析),斐波那契数列模型解析与C++实现(动态规划)
  • 【C++】,string类底层的模拟实现,C++中string类的模拟底层实现探究
  • uniapp 小程序实现微信授权登录(前端和后端),Uniapp小程序实现微信授权登录全流程(前端后端全攻略),Uniapp小程序微信授权登录全流程攻略,前端后端全指南
  • Vue脚手架的安装(保姆级教程),Vue脚手架保姆级安装教程,Vue脚手架保姆级安装指南,Vue脚手架保姆级安装指南,从零开始教你如何安装Vue脚手架
  • 如何在树莓派 Raspberry Pi中本地部署一个web站点并实现无公网IP远程访问,树莓派上本地部署Web站点及无公网IP远程访问指南,树莓派部署Web站点及无公网IP远程访问指南,本地部署与远程访问实践,树莓派部署Web站点及无公网IP远程访问实践指南,树莓派部署Web站点及无公网IP远程访问实践指南,本地部署与远程访问详解,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南。
  • vue2技术栈实现AI问答机器人功能(流式与非流式两种接口方法),Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法探究,Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法详解
  • 发表评论

    快捷回复:表情:
    评论列表 (暂无评论,0人围观)

    还没有评论,来说两句吧...

    目录[+]

    取消
    微信二维码
    微信二维码
    支付宝二维码