usb storage驱动分析
本文以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驱动分析相关推荐
- USB转串口驱动分析(一)
之前追踪代码用的grep命令效率太低了,所以这次下载C代码阅读跳转利器ctags.cscope用于分析代码 因为用的是Centos6.7所以需要用到yum install安装软件 [wuyujun@w ...
- linux下usb转串口驱动分析
linux下usb转串口驱动分析 分类: linux driver 2012-06-08 15:11 456人阅读 评论(0) 收藏 举报 linux struct interface returni ...
- linux设备驱动之USB主机控制器驱动分析
http://www.cnblogs.com/sdphome/archive/2011/09/29/2195791.html 一:前言 Usb是一个很复杂的系统.在usb2.0规范中,将其定义成了一个 ...
- linux设备驱动之USB主机控制器驱动分析 【转】
转自:http://blog.chinaunix.net/uid-20543183-id-1930831.html ------------------------------------------ ...
- USB转串口驱动分析(二)
在static int __init usb_serial_init(void)里 tty_set_operations(usb_serial_tty_driver, &serial_ops) ...
- usb控制linux关机,linux设备驱动之USB主机控制器驱动分析
一:前言 Usb是一个很复杂的系统.在usb2.0规范中,将其定义成了一个分层模型.linux中的代码也是按照这个分层模型来设计的.具体的分为 usb设备,hub和主机控制器三部份.在阅读代码的时候, ...
- 七,USB设备驱动 - 分析USB储存驱动程序
前面学习了USB驱动的一些基础概念与重要的数据结构,那么究竟如何编写一个USB 驱动程序呢?编写与一个USB设备驱动程序的方法和其他总线驱动方式类似,驱动程序把驱动程序对象注册到USB子系统中,稍后再 ...
- USB mass storage驱动分析
1. USB驱动代码在/drivers/usb/gadget下,有文件:android.c,其他驱动文件f_adb.c,f_mass_storage.c:其中android.c 依赖于f_adb.c ...
- USB OTG驱动分析(一)
前一段时间弄了 2 个礼拜的 OTG 驱动调试,感觉精神疲惫啊.主要原因还是自己对 OTG 功能不了解造成的.现在终于完成但是对实质原理还有些模糊.所以 自己重新总结一下.因为自己是菜鸟,所以用菜鸟的 ...
最新文章
- thymeleaf引入css js写法
- mysql优化varchar索引_MySQL优化--概述以及索引优化分析
- Array.forEach
- Spark源码分析之Master注册机制原理
- 区间DP lightoj 1422
- 【LeetCode】【数组】题号:414,第三大的数
- webstorm主题
- css vue 内联_Vue绑定内联样式问题
- 【Android】Android 中定义图片的资源文件
- 一个 Duang~ 的CSS3动画
- MATLAB学习笔记(一)常值函数与跳变函数的绘制
- 应用礼学赋能新员工职业素养提升
- G2:双折线图动态获取数据
- 从BAT到BATH 中国云计算世界杯大幕刚启
- MinIO文件服务器,从安装到使用
- SVN 检查修改或者提交代码时候一直显示please wait的解决办法(汉化版本显示请稍候)
- iOS CocoaPods 详解
- 板子ping不通主机
- Barcode Reader SDK5.xCrack,条形码识别支持多种文档和图像格式
- CentOS7 安装极点五笔输入法
热门文章
- ArcGIS教程:解决在mxd文件,定义投影之后,数据源还是的投影坐标系还是未发生改变的问题。
- MoveIT和KDL中进行机械臂位置和姿态插值
- 适合后台管理系统开发的前端框架
- linux终端 字符界面 显示乱码
- 颜值,自拍,美学三大任务简介和数据集下载
- 回归预测 | MATLAB实现MPR多元多项式回归
- JavaScript变量提升(Hoisting)详解
- 使用Python操作Excel图表之 为最后一个数据点添加数据标签
- Xiaomi Redmi Note7刷机
- html怎么修改版权信息,微擎修改免费版标题、logo、页脚版权信息教程(仅供学习)...