本文以usb storage设备驱动来分析,由于usb storage涉及到usb驱动框架,scsi驱动框架,block io驱动框架,先来看下初始化

1. usb storage设备的初始化流程

static struct usb_driver usb_storage_driver = {.name =     "usb-storage",.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,
};static int __init usb_stor_init(void)
{int retval;pr_info("Initializing USB Mass Storage driver...\n");/* register the driver, return usb_register return code if error */retval = usb_register(&usb_storage_driver);if (retval == 0) {pr_info("USB Mass Storage support registered.\n");usb_usual_set_present(USB_US_TYPE_STOR);}return retval;
}

如果开机时有U盘插在hub上,就会在初始化时分配usb_device,然后获取配置,触发generic_probe,选择一个配置usb_choose_configuration,并且设置该配置usb_set_configuration,在该函数里面对每个接口都调用device_add 函数注册到系统。当usb_stor_init 调用时,就会触发usb_device_match,接着调用该驱动的probe函数。

static int storage_probe(struct usb_interface *intf,const struct usb_device_id *id)
{struct us_data *us;int result;/** If libusual is configured, let it decide whether a standard* device should be handled by usb-storage or by ub.* If the device isn't standard (is handled by a subdriver* module) then don't accept it.*/if (usb_usual_check_type(id, USB_US_TYPE_STOR) ||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.*/result = usb_stor_probe1(&us, intf, id,(id - usb_storage_usb_ids) + us_unusual_dev_list);if (result)return result;/* No special transport or protocol settings in the main module */result = usb_stor_probe2(us);return result;
}

分了两个阶段,两个函数,usb_stor_probe1和usb_stor_probe2。

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 *host;struct us_data *us;int result;US_DEBUGP("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(&usb_stor_host_template, 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);memset(us, 0, sizeof(struct us_data));mutex_init(&(us->dev_mutex));init_completion(&us->cmnd_ready);init_completion(&(us->notify));init_waitqueue_head(&us->delay_wait);init_completion(&us->scanning_done);/* 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:US_DEBUGP("storage_probe() failed\n");release_everything(us);return result;
}
struct scsi_host_template usb_stor_host_template = {/* basic userland interface stuff */.name =                "usb-storage",.proc_name =           "usb-storage",.proc_info =           proc_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,.cmd_per_lun =           1,/* unknown initiator id */.this_id =         -1,.slave_alloc =          slave_alloc,.slave_configure =     slave_configure,/* lots of sg segments can be handled */.sg_tablesize =            SCSI_MAX_SG_CHAIN_SEGMENTS,/* limit the total size of a transfer to 120 KB */.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
};
int usb_stor_probe2(struct us_data *us)
{struct task_struct *th;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;}US_DEBUGP("Transport: %s\n", us->transport_name);US_DEBUGP("Protocol: %s\n", us->protocol_name);/* 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);    //在这个函数里面还创建了内核线程usb-storageif (result)goto BadDevice;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_add_host函数,那么scsi_scan_host在哪呢if (result) {dev_warn(dev,"Unable to add the scsi host\n");goto BadDevice;}/* Start up the thread for delayed SCSI-device scanning */th = kthread_create(usb_stor_scan_thread, us, "usb-stor-scan"); //scsi_scan_host函数是在这个内核线程里面被调用的if (IS_ERR(th)) {dev_warn(dev,"Unable to start the device-scanning thread\n");complete(&us->scanning_done);quiesce_and_remove_host(us);result = PTR_ERR(th);goto BadDevice;}usb_autopm_get_interface_no_resume(us->pusb_intf);wake_up_process(th);return 0;/* We come here if there are any problems */
BadDevice:US_DEBUGP("storage_probe() failed\n");release_everything(us);return result;
}

有两个内核线程usb-stor-scan和usb-storage

static int usb_stor_scan_thread(void * __us)
{struct us_data *us = (struct us_data *)__us;struct device *dev = &us->pusb_intf->dev;dev_dbg(dev, "device found\n");set_freezable();/* Wait for the timeout to expire or for a disconnect */if (delay_use > 0) {dev_dbg(dev, "waiting for device to settle ""before scanning\n");wait_event_freezable_timeout(us->delay_wait,test_bit(US_FLIDX_DONT_SCAN, &us->dflags),delay_use * HZ);}/* If the device is still connected, perform the scanning */if (!test_bit(US_FLIDX_DONT_SCAN, &us->dflags)) {/* For bulk-only devices, determine the max LUN value */if (us->protocol == USB_PR_BULK &&!(us->fflags & US_FL_SINGLE_LUN)) {mutex_lock(&us->dev_mutex);us->max_lun = usb_stor_Bulk_max_lun(us);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);complete_and_exit(&us->scanning_done, 0);
}
static int usb_stor_control_thread(void * __us)
{struct us_data *us = (struct us_data *)__us;struct Scsi_Host *host = us_to_host(us);for(;;) {US_DEBUGP("*** thread sleeping.\n");if (wait_for_completion_interruptible(&us->cmnd_ready))<span style="white-space:pre"> </span>//等待completion变量break;US_DEBUGP("*** 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 */if (us->srb == NULL) {scsi_unlock(host);mutex_unlock(&us->dev_mutex);US_DEBUGP("-- exiting\n");break;}/* has the command timed out *already* ? */if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {us->srb->result = DID_ABORT << 16;goto SkipForAbort;}scsi_unlock(host);/* reject the command if the direction indicator * is UNKNOWN*/if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) {US_DEBUGP("UNKNOWN data direction\n");us->srb->result = DID_ERROR << 16;}/* reject if target != 0 or if LUN is higher than* the maximum known LUN*/else if (us->srb->device->id && !(us->fflags & US_FL_SCM_MULT_TARG)) {US_DEBUGP("Bad target number (%d:%d)\n",us->srb->device->id, us->srb->device->lun);us->srb->result = DID_BAD_TARGET << 16;}else if (us->srb->device->lun > us->max_lun) {US_DEBUGP("Bad LUN (%d:%d)\n",us->srb->device->id, us->srb->device->lun);us->srb->result = DID_BAD_TARGET << 16;}/* Handle those devices which need us to fake * their inquiry data */else if ((us->srb->cmnd[0] == INQUIRY) &&(us->fflags & US_FL_FIX_INQUIRY)) {unsigned char data_ptr[36] = {0x00, 0x80, 0x02, 0x02,0x1F, 0x00, 0x00, 0x00};US_DEBUGP("Faking INQUIRY command\n");fill_inquiry_response(us, data_ptr, 36);us->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(us->srb, us); //处理命令的函数usb_mark_last_busy(us->pusb_dev);}/* lock access to the state */scsi_lock(host);/* indicate that the command is done */if (us->srb->result != DID_ABORT << 16) {US_DEBUGP("scsi cmd done, result=0x%x\n", us->srb->result);us->srb->scsi_done(us->srb);} else {
SkipForAbort:US_DEBUGP("scsi command aborted\n");}/* 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);} /* 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;
}

把storage_probe的调用流程贴下,网上网友总结的非常详细:

storage_probe

usb_stor_probe1

scsi_alloc_host

associate_dev

get_device_info

get_transport          //us->transport = usb_stor_CB_transport;

get_protocol          //us->proto_handler = usb_stor_transparent_scsi_command

usb_stor_probe2

get_pipe

usb_stor_acquire_resources

usb_alloc_urb

kthread_run(usb_stor_control_thread,us,"usb-storage")

scsi_add_host

kthread_run(usb_stor_scan_thread, us, "usb-stor-scan")

wake_up_process       //wake up kthread usb_stor_scan_thread

接下来先看usb_stor_scan_thread线程的流程:

usb_stor_scan_thread

usb_stor_Bulk_max_lun

usb_stor_control_msg

usb_fill_control_urb

usb_stor_msg_common

init_completion

usb_submit_urb

wait_for_completion_interruptible_timeout

scsi_scan_host

2. usb storage设备的读写流程

(1)scsi层:当用户需要访问usb设备,scsi-core会调用scsi_dispatch_cmd函数,scsi_dispatch_cmd会调用scsi_host_template中的queuecommand,对应usb store就是queuecommand函数。

static DEF_SCSI_QCMD(queuecommand)
#define DEF_SCSI_QCMD(func_name) \int func_name(struct Scsi_Host *shost, struct scsi_cmnd *cmd) \{                              \unsigned long irq_flags;               \int rc;                            \spin_lock_irqsave(shost->host_lock, irq_flags);     \scsi_cmd_get_serial(shost, cmd);           \rc = func_name##_lck (cmd, cmd->scsi_done);            \spin_unlock_irqrestore(shost->host_lock, irq_flags);    \return rc;                     \}
static int queuecommand_lck(struct scsi_cmnd *srb,void (*done)(struct scsi_cmnd *))
{struct us_data *us = host_to_us(srb->device->host);US_DEBUGP("%s called\n", __func__);/* 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)) {US_DEBUGP("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;
}

(2)queuecommand唤醒了usb_stor_control_thread线程

该线程会调用us->proto_handler(us->srb, us),此接口在probe的时候已经定义为usb_stor_Bulk_transport

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

usb_stor_invoke_transport

scsi_set_resid(srb, 0)

us->transport(srb, us)           //对应usb_stor_CB_transport

usb_stor_CB_transport

usb_stor_ctrl_transfer     //发送命令

usb_fill_control_urb

usb_stor_msg_common

init_completion

usb_submit_urb

wait_for_completion_interruptible_timeout

interpret_urb_result

usb_stor_bulk_srb           //传输数据

usb_stor_bulk_transfer_sglist

usb_sg_init

usb_sg_wait

interpret_urb_result

scsi_set_resid

usb_stor_intr_transfer         //获取传输状态

usb_fill_int_urb

usb_stor_msg_common

interpret_urb_result

usb storage驱动分析相关推荐

  1. USB转串口驱动分析(一)

    之前追踪代码用的grep命令效率太低了,所以这次下载C代码阅读跳转利器ctags.cscope用于分析代码 因为用的是Centos6.7所以需要用到yum install安装软件 [wuyujun@w ...

  2. linux下usb转串口驱动分析

    linux下usb转串口驱动分析 分类: linux driver 2012-06-08 15:11 456人阅读 评论(0) 收藏 举报 linux struct interface returni ...

  3. linux设备驱动之USB主机控制器驱动分析

    http://www.cnblogs.com/sdphome/archive/2011/09/29/2195791.html 一:前言 Usb是一个很复杂的系统.在usb2.0规范中,将其定义成了一个 ...

  4. linux设备驱动之USB主机控制器驱动分析 【转】

    转自:http://blog.chinaunix.net/uid-20543183-id-1930831.html ------------------------------------------ ...

  5. USB转串口驱动分析(二)

    在static int __init usb_serial_init(void)里 tty_set_operations(usb_serial_tty_driver, &serial_ops) ...

  6. usb控制linux关机,linux设备驱动之USB主机控制器驱动分析

    一:前言 Usb是一个很复杂的系统.在usb2.0规范中,将其定义成了一个分层模型.linux中的代码也是按照这个分层模型来设计的.具体的分为 usb设备,hub和主机控制器三部份.在阅读代码的时候, ...

  7. 七,USB设备驱动 - 分析USB储存驱动程序

    前面学习了USB驱动的一些基础概念与重要的数据结构,那么究竟如何编写一个USB 驱动程序呢?编写与一个USB设备驱动程序的方法和其他总线驱动方式类似,驱动程序把驱动程序对象注册到USB子系统中,稍后再 ...

  8. USB mass storage驱动分析

    1. USB驱动代码在/drivers/usb/gadget下,有文件:android.c,其他驱动文件f_adb.c,f_mass_storage.c:其中android.c 依赖于f_adb.c ...

  9. USB OTG驱动分析(一)

    前一段时间弄了 2 个礼拜的 OTG 驱动调试,感觉精神疲惫啊.主要原因还是自己对 OTG 功能不了解造成的.现在终于完成但是对实质原理还有些模糊.所以 自己重新总结一下.因为自己是菜鸟,所以用菜鸟的 ...

最新文章

  1. thymeleaf引入css js写法
  2. mysql优化varchar索引_MySQL优化--概述以及索引优化分析
  3. Array.forEach
  4. Spark源码分析之Master注册机制原理
  5. 区间DP lightoj 1422
  6. 【LeetCode】【数组】题号:414,第三大的数
  7. webstorm主题
  8. css vue 内联_Vue绑定内联样式问题
  9. 【Android】Android 中定义图片的资源文件
  10. 一个 Duang~ 的CSS3动画
  11. MATLAB学习笔记(一)常值函数与跳变函数的绘制
  12. 应用礼学赋能新员工职业素养提升
  13. G2:双折线图动态获取数据
  14. 从BAT到BATH 中国云计算世界杯大幕刚启
  15. MinIO文件服务器,从安装到使用
  16. SVN 检查修改或者提交代码时候一直显示please wait的解决办法(汉化版本显示请稍候)
  17. iOS CocoaPods 详解
  18. 板子ping不通主机
  19. Barcode Reader SDK5.xCrack,条形码识别支持多种文档和图像格式
  20. CentOS7 安装极点五笔输入法

热门文章

  1. ArcGIS教程:解决在mxd文件,定义投影之后,数据源还是的投影坐标系还是未发生改变的问题。
  2. MoveIT和KDL中进行机械臂位置和姿态插值
  3. 适合后台管理系统开发的前端框架
  4. linux终端 字符界面 显示乱码
  5. 颜值,自拍,美学三大任务简介和数据集下载
  6. 回归预测 | MATLAB实现MPR多元多项式回归
  7. JavaScript变量提升(Hoisting)详解
  8. 使用Python操作Excel图表之 为最后一个数据点添加数据标签
  9. Xiaomi Redmi Note7刷机
  10. html怎么修改版权信息,微擎修改免费版标题、logo、页脚版权信息教程(仅供学习)...