`
javatome
  • 浏览: 845219 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Linux那些事儿之我是U盘(52)有多少爱可以胡来?(一)

 
阅读更多

上帝给了每个人一支书写人生的铅笔,却未曾给我们橡皮擦.但计算机的世界却并非如此,电脑用着用着觉得不正常了,按一下reset键就一切ok. (当然你要是中了熊猫烧香啊中了冲击波啥的病毒那就另当别论了,,别打岔行不行,我们讲正事呢.)如果人生也可以这样,那么星爷的那段经典的妇孺皆知的人世间最悲哀的……假如……”的对白恐怕就没有意义了.

在驱动程序中,一个非常非常重要的概念就是错误处理.生活不是林黛玉,不会因为忧伤而风情万种,写代码不是写小说,不会因为作者的构思完美而天衣无缝.所以我们来看看在usb-storage,我们是如何来进行错误处理的.

一切都得从那个结构体变量struct scsi_host_template usb_stor_host_template开始说起.其实这个结构体正是我们和scsi核心层最最关键的接口.我们知道这个结构体有很多内容,我们为usb_stor_host_template的许多成员赋了值,但是显然很多我们都没有讲.那么现在是时候去讲了.这其中,我们不妨把与错误处理相关的三个成员给揪出来.这些函数都是我们自己定义的,提供给scsi core那边去调用,就好像queuecommand()一样.

430 /* error and abort handlers */
431 .eh_abort_handler = command_abort,
432 .eh_device_reset_handler = device_reset,
433 .eh_bus_reset_handler = bus_reset,

,让我们一个一个来看.先看两个与reset相关的函数,网友要挑熟女问我为什么有两个reset函数,很简单,当你的电脑有点小毛病了之后,你可以有两种选择,一种是注销就可以了,一种是重起才可以.device_reset在这里对应的就是注销,bus_reset对应的就是重起.当然这样说并不严谨,只能说,一般来说轻微一点,device_reset就够了,如果严重一点,眼看着设备病入膏肓了,那么可能就要bus_reset().

Ok,我们让代码来告诉你.

首先看到的是device_reset().来自drivers/usb/storage/scsiglue.c,

239 /* This invokes the transport reset mechanism to reset the state of the
240 * device */
241 /* This is always called with scsi_lock(srb->host) held */
242 static int device_reset(struct scsi_cmnd *srb)
243 {
244 struct us_data *us = (struct us_data *)srb->device->host->hostdata[0];
245 int result;
246
247 US_DEBUGP("%s called/n", __FUNCTION__);
248
249 scsi_unlock(srb->device->host);
250
251 /* lock the device pointers and do the reset */
252 down(&(us->dev_semaphore));
253 if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
254 result = FAILED;
255 US_DEBUGP("No reset during disconnect/n");
256 } else
257 result = us->transport_reset(us);
258 up(&(us->dev_semaphore));
259
260 /* lock the host for the return */
261 scsi_lock(srb->device->host);
262 return result;
263 }

244,没啥好说的,星星还是那颗星星哟,月亮还是那个月亮,山也还是那座山哟,梁也还是那道梁,碾子是碾子,缸是缸哟,爹是爹来娘是娘,麻油灯啊,还吱吱地响,点的还那么丁点亮,喔哦...喔哦...只有那篱笆墙影子咋那么长,只有那篱笆墙影子咋那么长,还有那看家的狗叫的叫的叫的叫的咋就这么狂...80后不可能没有听过毛阿敏的歌,不可能没有听过这首毛阿敏姐姐的成名作.20多年过去,星星不再像那颗星星,月亮也不象那个月亮,河也不是那条河哟,房也不是那座房.然而,us还是那个us.就像那篱笆墙,影子还那么长.

253,首先看US_FLIDX_DISCONNECTING flag设了没有.显然,disconnecting了就没有必要再reset.关于disconnect,你别急,讲完reset部分就该讲它了.

否则,会调用us->transport_reset,我们前面已经说过了,很久很久以前,我们曾经为us->transport_reset赋值为usb_stor_Bulk_reset,所以这里也就是函数usb_stor_Bulk_reset()会被调用,usb_stor_Bulk_reset定义于drivers/usb/storage/transport.c,

1184 /* This issues a Bulk-only Reset to the device in question, including
1185 * clearing the subsequent endpoint halts that may occur.
1186 */
1187 int usb_stor_Bulk_reset(struct us_data *us)
1188 {
1189 US_DEBUGP("%s called/n", __FUNCTION__);
1190
1191 return usb_stor_reset_common(us, US_BULK_RESET_REQUEST,
1192 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
1193 0, us->ifnum, NULL, 0);
1194 }

进入这个函数一看,很简单,也不干别的,就是调用usb_stor_reset_common().于是,咱们接着来到了这个来自driver/usb/storage/transport.c中的usb_stor_reset_common()函数.

1101 /* This is the common part of the device reset code.
1102 *
1103 * It's handy that every transport mechanism uses the control endpoint for
1104 * resets.
1105 *
1106 * Basically, we send a reset with a 20-second timeout, so we don't get
1107 * jammed attempting to do the reset.
1108 */
1109 static int usb_stor_reset_common(struct us_data *us,
1110 u8 request, u8 requesttype,
1111 u16 value, u16 index, void *data, u16 size)
1112 {
1113 int result;
1114 int result2;
1115 int rc = FAILED;
1116
1117 /* Let the SCSI layer know we are doing a reset, set the
1118 * RESETTING bit, and clear the ABORTING bit so that the reset
1119 * may proceed.
1120 */
1121 scsi_lock(us->host);
1122 usb_stor_report_device_reset(us);
1123 set_bit(US_FLIDX_RESETTING, &us->flags);
1124 clear_bit(US_FLIDX_ABORTING, &us->flags);
1125 scsi_unlock(us->host);
1126
1127 /* A 20-second timeout may seem rather long, but a LaCie
1128 * StudioDrive USB2 device takes 16+ seconds to get going
1129 * following a powerup or USB attach event.
1130 */
1131 result = usb_stor_control_msg(us, us->send_ctrl_pipe,
1132 request, requesttype, value, index, data, size,
1133 20*HZ);
1134 if (result < 0) {
1135 US_DEBUGP("Soft reset failed: %d/n", result);
1136 goto Done;
1137 }
1138
1139 /* Give the device some time to recover from the reset,
1140 * but don't delay disconnect processing. */
1141 wait_event_interruptible_timeout(us->dev_reset_wait,
1142 test_bit(US_FLIDX_DISCONNECTING, &us->flags),
1143 HZ*6);
1144 if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
1145 US_DEBUGP("Reset interrupted by disconnect/n");
1146 goto Done;
1147 }
1148
1149 US_DEBUGP("Soft reset: clearing bulk-in endpoint halt/n");
1150 result = usb_stor_clear_halt(us, us->recv_bulk_pipe);
1151
1152 US_DEBUGP("Soft reset: clearing bulk-out endpoint halt/n");
1153 result2 = usb_stor_clear_halt(us, us->send_bulk_pipe);
1154
1155 /* return a result code based on the result of the control message */
1156 if (result < 0 || result2 < 0) {
1157 US_DEBUGP("Soft reset failed/n");
1158 goto Done;
1159 }
1160 US_DEBUGP("Soft reset done/n");
1161 rc = SUCCESS;
1162
1163 Done:
1164 clear_bit(US_FLIDX_RESETTING, &us->flags);
1165 return rc;
1166 }

前面几行是赋值,然后usb_stor_report_device_reset()被调用. usb_stor_report_device_reset()定义于drivers/usb/storage/scsiglue.c,

308 /* Report a driver-initiated device reset to the SCSI layer.
309 * Calling this for a SCSI-initiated reset is unnecessary but harmless.
310 * The caller must own the SCSI host lock. */
311 void usb_stor_report_device_reset(struct us_data *us)
312 {
313 int i;
314
315 scsi_report_device_reset(us->host, 0, 0);
316 if (us->flags & US_FL_SCM_MULT_TARG) {
317 for (i = 1; i < us->host->max_id; ++i)
318 scsi_report_device_reset(us->host, 0, i);
319 }
320 }

315,scsi_report_device_reset(),drivers/scsi/scsi_error.c中定义的.这个函数scsi core那边要求我们调用的,我们身不由己.然而关于这个函数的细节,只能说,世界太大,我们只在乎我们需要在乎的冬冬,其她的我们无暇顾及.我们只想在960万平方千米的一个角落里,静静的为自己的理想打拼,为自己寻找一份荣耀.usb是我们care,scsi的核心,我们是不想去深究的,只有写scsi核心的同志们会在乎.江湖中流传这么一句话:女孩在乎的是下半生的幸福,男孩关注的是下半身的幸福.同样,Linux世界里,每一个人在乎的冬冬是不一样的……言归正传,咱们是不需要关系这个函数怎么定义的,但是咱们需要知道什么时候会调用她,调用她干嘛?需要传递什么参数?首先,要传递三个参数,第一个,Scsi_Host指针,一块u盘就有一个Scsi_Host,然后第二个参数,channel,然后第三个参数target,描述scsi设备位置的四个参数就三缺一了,缺的就是LUN,因为一个devicereset那么就不会管她上面有几个LUN,有几个都一起给她reset.那么调用这个函数的目的是什么?告诉scsi核心,俺观察到某个设备reset.至于scsi核心会如何处理呢,那咱管不着,也懒得去管.总而言之,言而总之,统而言之,言而统之,咱们的职责是在发现了一个设备reset之后立刻向上级汇报.

US_FL_SCM_MULT_TARG这个flag,咱们也提过好几次,她代表的是支持多个target,这是设备本身的属性,不是咱们的代码愣给设备设的.对于这种设备,scsi_report_device_reset()就会被多调用几次,针对每一个targetreport一次.

结束了scsi_report_device_reset(),自然又回到了usb_stor_reset_common(),1123,1124行设置一个flag,清除一个flag,设置的是US_FLIDX_RESETTING,清除的是US_FLIDX_ABORTING,关于这两个flag,一会咱们结合command_abort()来讲.

1131,usb_stor_control_msg()被调用,再一次看到这个函数想必大家已经不再陌生了吧.她就是发送一个控制命令,其实我们已经很久没有讲控制传输了.这里结合参数来看看传送的什么命令.首先,us还是那个us,不再多说.然后,pipeus->send_ctrl_pipe,就是发送控制管道.然后request,requesttype这些都是在调用usb_stor_reset_common()的时候传递进来的参数,usb_stor_Bulk_reset()中可以看到,requestUS_BULK_RESET_REQUEST,requesttypeUSB_TYPE_CLASS | USB_RECIP_INTERFACE.

US_BULK_RESET_REQUESTdrivers/usb/storage/transport.h中被设置为0xff,这是和usb mass storage class-Bulk Only transport协议相对应的.该协议专门为Bulk-Only Mass Storage设备定义了一个请求,Reset.协议里说,this request is used to reset the mass storage device and its associated interface.协议中规定了,usb host要发送命令reset usb设备的时候,需要通过发送控制管道发送一个请求,即前面提过的ctrlrequest,其格式如下图所示:

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 268.2pt; HEIGHT: 48.6pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image001.emz"></imagedata></shape>

其中bReques这一位须设置为255(FFh),wValue设置为0,wIndex设置为interface number,wLength设置为0.(而我们这里也确实这样做了,wIndex被赋值为us->ifnum,和上次咱们调用usb_stor_control_msg的时候传递的一样,显然interface还是那个interface.江山会变,四季会变,咱们心中的interface始终不变.)

至于requesttype,和咱们在usb_stor_Bulk_max_lun()中讲的差不多,唯一的区别是控制数据传输方向,当时是device to host,现在是host to device,所以当时多了一个USB_DIR_IN,而现在没有写USB_DIR_OUT,原因很简单,USB_DIR_OUT被定义为0,所以或不或她无所谓.

,酱紫,就完成了向设备发送reset命令的任务.返回值小于0就是出错了.

没出错那么就1141,wait_event_interruptible_timeout()被调用.us->dev_reset_wait咱们前面讲过,她是一个等待队列头,storage_probe()中被初始化,而以后在讲storage_disconnect()时会讲到,有这么一句,wake_up(&us->dev_reset_wait),她唤醒的正是这里进入睡眠的进程,这里1141,会进入睡眠,进入睡眠之前先判断US_FLIDX_DISCONNECTING这个flag有没有设置,要是设了就没有必要睡眠了,直接退出吧.1144,再判断一次,是真的设了这个flag那么直接goto Done,返回rc,rc就是初值FAILED.返回之前先清除US_FLIDX_RESETTING flag. 关于wait_event_interruptible_timeout()这个函数,我们当初在分析usb_stor_scan_thread()的时候已经详细的讲过了,所以这里不需要再浪费你我的青春去多讲了.

当然,如果US_FLIDX_DISCONNECTING并没有设置,那么6s钟时间到了,睡到自然醒,11501153,usb_stor_clear_halt(),又是一个很亲切的函数,表忘了当前我们在讲GET MAX LUN的时候就专门介绍了这个函数,而且那时候我们就已经说过,有一种情况下我们要调用这个函数,说的正是我们这里的情况,即当设备reset之后,需要清楚halt这个feature,然后端点才能正常工作.对于我们的两个bulk端点,只要有一个清halt feature失败了,那么整个这个负责reset的函数usb_stor_reset_common就算失败了,并且因此会返回FAILED,而且在返回之前先把US_FLIDX_RESETTING这个flag给去掉.

然后到了这里,usb_stor_reset_commond该返回了,然后我们惊讶的发现, usb_stor_Bulk_reset()也该返回了,再然后我们又惊讶的发现,device_reset()也该返回了.就这样,我们走完了device_reset()这么一个函数.之所以简单是因为它的使命本身就很简单,其实就是给设备发送一个resetrequest,然后clearhalt feature,保证设备的端点没有停止.就这些,这就够了.

分享到:
评论

相关推荐

    Linux那些事儿之我是U盘

    根据给定的信息,“Linux那些事儿之我是U盘”这一标题及描述主要聚焦于USB技术在Linux环境下的工作原理和技术细节。接下来,我们将基于这个主题展开深入探讨,涵盖USB技术的基本概念、USB在Linux系统中的实现机制...

    Linux那些事儿

    Linux那些事儿之我是U盘 Linux那些事儿之我是Hub Linux那些事儿之我是USB Core Linux那些事儿之我是UHCI Linux那些事儿之我是EHCI控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是...

    Linux那些事儿之我是USB(第2版)

    通过以上对《Linux那些事儿之我是USB》第二版的深入解析,我们可以看出这本书不仅是一本技术指南,更是一种学习态度的倡导者。无论是对于初学者还是资深开发者,都能从中获得宝贵的知识和启示。

    Linux那些事儿1-9合集

    读过《linux那些事儿之我是U盘》的人,都知道其风格,我就不多说了。 导读: linux那些事儿之我是U盘 linux那些事儿之我是HUB linux那些事儿之我是USB Core linux那些事儿之我是UHCI Linux那些事儿之我是EHCI主机控制...

    linux那些事儿之我是USB.zip

    本压缩包文件"linux那些事儿之我是USB.zip"包含了深入理解Linux USB驱动及内核相关知识的九个文档,包括Block层、EHCI主机控制器、HUB、PCI、SCSI硬盘、Sysfs、UHCI、USB core以及U盘。这些文档旨在提供一个系统性的...

    LINUX\Linux那些事儿系列

    3. **Linux那些事儿之我是U盘.pdf**: USB闪存驱动器,通常称为U盘,是常见的便携式存储设备。这部分可能讲述Linux如何识别、挂载和管理U盘,包括使用ums(USB Mass Storage)驱动程序,以及如何处理文件系统的读写...

    Linux那些事儿之全集

    导读.doc Linux那些事儿之我是Block层.pdf Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是Hub.pdf Linux那些事儿之我是USB_core.pdf Linux那些事儿之我是U盘.pdf等等 Linux那些事儿系列全在这里了

    Linux那些事儿系列.rar

    》包括《Linux那些事儿之我是Hub》、《Linux那些事儿之我是Sysfs》《Linux那些事儿之我是UHCI》、《Linux那些事儿之我是USB core》、《Linux那些事儿之我是U盘》,令人叹为观止的一个linux系列书籍。只能说,江山代...

    linux 那些事儿全集

    “Linux那些事儿之我是Sysfs.pdf”聚焦于Sysfs虚拟文件系统,它是Linux内核提供的一种机制,使用户空间可以直接访问内核对象。文档会讲解如何通过Sysfs来监控和控制硬件设备。 “Linux那些事儿之我是SCSI硬盘.pdf”...

    linux的那些事儿全集

    Linux那些事儿之我是Block层 Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Hub Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Sysfs ...Linux那些事儿之我是U盘

    linux那些事儿之我是U盘

    本文基于一篇幽默且深入的教程《Linux那些事儿之我是U盘》,该文章通过作者的亲身经历和深入浅出的讲解,介绍了Linux操作系统中USB存储设备的工作原理及相关技术细节。以下是对该文中涉及的重要知识点的梳理。 ####...

Global site tag (gtag.js) - Google Analytics