免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: projl
打印 上一主题 下一主题

《Linux 那些事儿之我是U盘》 [复制链接]

论坛徽章:
0
71 [报告]
发表于 2008-06-06 14:12 |只看该作者
首先,什么是信号量?毕业的时候,校园招聘中常常笔试面试会考信号量,会考死锁,不过通常考的会比较
简单,更多的情况是考察一些基本概念,印象中曾经在Sun 中国工程研究院的面试中被问起过,当然也有比
较复杂一点的,要求结合具体的问题对某种算法进行合理性分析,比如Intel 某年在上海交大的笔试题,考到
了经典的哲学家进餐问题,不过幸运的是这种变态的笔试题我没遇上,我进Intel 的时候只被问起解释冒泡
排序法...

我们整个故事中有两个信号量,它们都是us 的成员,一个是这个dev_semaphore, 另一个是sema,在
定义struct us_data 的时候我们已经看到过,

111 struct semaphore dev_semaphore; /* protect pusb_dev */
156 struct semaphore sema; /* to sleep thread on */

sema 也同样是在storage_probe 的一开始就做了初始化,

944 init_MUTEX_LOCKED(&(us->sema));

设备驱动中最难的部分在于三个方面,一个是涉及到内存管理的代码,一个是涉及到进程管理的代码,另一
个就是信号量和互斥锁或者别的锁的代码.这些部分如果不合理将容易导致系统崩溃,而信号量最容易导致
的就是死锁.

网名为卖血上网的哥们说话了,那么到底什么是信号量?或者什么是互斥锁?

先说互斥锁.它诞生于这样一个背景.这个世界上,有些东西只能属于某一个人,或者说在一个时间里只能
属于一个人,这你承认吧,比如一个女孩的心.当你要追求一个女孩时,你首先会去了解其人是否名花有主,若
是否,你才会去追求,若已然有主,那么你只能放弃,或者准确的说,你只能等待.当然你可以很激昂的说,如果
等待可以换来奇迹的话,我宁愿等下去,哪怕一年,抑或一生!然而,她爱的是他,终究不是你,所以你伤悲,流泪,
却打不开她心中那把锁.这里你应该就能感觉到什么是互斥锁了,一个女孩如果心有所属,那么对你来说,就
仿佛已有人在你之前给她上了一把锁,而钥匙,不在你这里.

那么你问代码中为什么要锁?Ok,我告诉你,如果你正在操作一个队列,比如一个队列一百个元素,你想把
第七十个读出来,于是你去遍历这个队列,可是如果没有锁,那么可能你遍历的时候别人也可以操作这个队列,
比如你马上就要找到第七十个了,可是,可是,注意了,可是,这个时候,说不定哪位哥们儿缺德,把第七十个数
给删除了,那你不是白忙活了?所以,怎么办?设一个锁,每个人要想操作这个队列就得先获得这把锁,而一旦
你获得了这把锁,你在操作这把锁期间,别人就不能操作,因为他要操作他就得先获得锁,而这个时候锁在你
这里,所以他只能等待,等你结束了,释放了锁,他才可以去操作.那么互斥锁指的就是这种情况,一个资源只
能同时被一个进程操作,互斥的字面意思也正是如此.互相排斥,就像爱情是自私的一样.

那什么是信号量?信号量和互斥锁略有不同.它允许一个时间里有几个进程同时访问一段资源.到底允许
几个可以设定,这称为设定信号量的初值,如设定为1,那就说明是同一时间只有一个进程可以访问,那么这
就是互斥锁了,不过有的时候一个资源确实可以让几个进程访问,特别是读访问,你想一个文件可以被两个进
程同时读,这不要紧吧,各读各的,谁也影响不了谁,只要大家都不写就是了.设定信号量的初值,比如设定5,
那么就是说,同一时间你就让最多5 个进程同时去读这个文件.每个进程获取了信号量就把信号量的值减一,
到第六个进程了,它去判断,发现值已经等于0 了,于是它不能访问了,只能等待,等待别的进程释放锁.

论坛徽章:
0
72 [报告]
发表于 2008-06-06 14:12 |只看该作者
不过也许,一把钥匙只能开一把锁,更能代表爱情的专一.所以实际上,Linux 内核代码中锁用得更多,而初
值不为1 的信号量用得相对来说不多.比如我们刚才看到的这两个信号量,都是属于当作互斥锁在用.因为他
们的初值一个被设置成了1(init_MUTEX), 一个被设置成了0(init_MUTEX_LOCKED). 设置成一很好理
解,就是一把互斥锁,只能容许一个进程去获得它,设置成0 呢,就表示这把锁已经被锁住了,得有进程释放它
才可以.我们这里767 行这句down(&us->dev_semaphore) 和up(&us->dev_semaphore) 是一对,
一个是获得锁,一个是释放锁.它们就是为了保护中间那段代码.我刚才说我不想讲这段代码,的确,现在讲为
什么这里要用锁还为时过早,整个故事中us->dev_semaphore 出现的次数不是很多,但是我们必须对整
个代码都熟悉了才可能理解为什么要用这把锁,因为这些代码都是环环相扣的,不能孤立来看.所以,我们将
在故事的收尾阶段来一次性来以高屋建瓴的方式看每一处down(&us->dev_semaphore) 和
up(&us->dev_semaphore) 的使用原因.接下来我们看到了这把锁的时候,也将一并跳过不提.到最后再
来看.需要说的是这里down 和up 这两个函数的作用分别就是去获得锁和释放锁.对于down 来说,它每次
判断一下信号量的值是否大于0,若是,就进入下面的代码,同时将信号量的值减一,若否,就等待,或者说专业
一点,进入睡眠.对于down(),我们小时候那部<<挥剑问情>>的歌词很形象的描述了其行为,

男:挥剑问情,如果问是有情,也愿以身相许,以身相殉;

女:挥剑问情,如果问是无情,又怕回首别是一种伤心.

不过,我们这里看到了两把锁,除了us->dev_semaphore 以外,另一个是us->sema,现在还没有使用
它,但是我们可以先说一下,us->sema 在整个故事中出现的次数不多,总共只有三次.加上这里提到的这句
初始化为0 的语句,一个出现了四次.所以我们在遇到它的时候不需要跳过,会详细的讲.因为很容易理解它
为什么会用在那里.请你记住,这个信号量或者说这把锁是被初始化为0了,不是1,它一开始就处于锁住的状
态,到时候你就会知道为什么了.我们边走边看.

另外一个需要说一下的是,与down ()相似的有一个叫做down_interruptible () 的函数,它们的区别在
于后者可被信号打断,而前者不可被信号打断,而一旦问是无情,那么他们将进入等待,或者专业一点说,进入
睡眠,直到某一天...

我们将看到的获取us->sema 的函数正是down_interruptible. 而释放锁的函数仍然可以用up.

第一次亲密接触(一)

直到现在我们才将第一次真正的开始接触usb的四种数据传输之一,控制传输.应该说从这一刻开始,代码
开始变得复杂了.不过不要怕,有我在.在这个美妙的夏夜,让我们剪一段月光,来解代码的霜.

769 至777 行,做了一件事情,确定这个设备的max lun. 不要说你不知道什么是max lun. 不知道的回去
跪主板吧,我很负责任的向你推荐我们Intel 最新的3 系列整合芯片组主板.

在get_transport()函数中,我们针对各种情况给us->max_lun 赋了值,但是我们当时就注意到了,唯独
对于Bulk-Only 的设备,当时并没有赋值,所以这里我们看到,对于us->protocol 等于US_PR_BULK 的
情况,有一个专门的函数来获得这个设备的max lun. 网友女为悦己者_整_容好奇的问,为什么写代码的同
志在这里对Bulk-Only 的设备表现出一种”偏偏喜欢你”的态度呢?没什么特别的,党中央规定的.所有的
usb 设备遵守一个规范,这个规范叫做usb spec,而usb 设备分为很多种类,usb mass storage 是其中一
类,而mass storage 设备又分为很多子类,每个子类又有它自己的规范,比如U 盘它所遵守的就是usb

论坛徽章:
0
73 [报告]
发表于 2008-06-06 14:13 |只看该作者
mass storage class bulk-only transport spec. 而这个规范说得很清楚,对于这种设备,它的lun 不是凭
感觉就能判断的,你得发送一个命令给它,向它去查询,然后它会做出响应,这样你才能知道它究竟是几个lun.
这条命令就是”GET MAX LUN”,详见spec 3.2.

所以我们即使不认真看这个函数也可以知道究竟发生了什么.而且我们之前还说过,普通的U 盘的max
lun 肯定是0.对于那种读卡器才可能有多个lun. 不过我们还是不妨来深入的看一下这个函数,毕竟这个函
数是在drivers/usb/storage/transport.c 中,属于我们的辖区.当然更重要的是,此前我们一直没有真正
见识过究竟一次usb 传输是怎么回事,作为usb 设备驱动程序究竟如何发起usb 传输,而这个函数正好给了
我们一次不错的机会.同时我们也将会知道,了解了一次控制传输之后,别的传输也会很容易理解.

首先我们知道这次传输的目的是为了获得一个数字,max lun, 而获得这个数据的方式是发送命令,所以整
个过程就是,你发送一个命令给设备,设备返回一个值给你,这么简单的传输叫什么?控制传输.很显然,地球
人都知道,控制传输是usb 四种传输方式中最简单的那种.来看具体代码:

908 /* Determine what the maximum LUN supported is */

909 int usb_stor_Bulk_max_lun(struct us_data *us)

910 {

911 int result;

912

913 /* issue the command */

914 result = usb_stor_control_msg(us, us->recv_ctrl_pipe,

915 US_BULK_GET_MAX_LUN,

916 USB_DIR_IN | USB_TYPE_CLASS |

917 USB_RECIP_INTERFACE,

918 0, us->ifnum, us->iobuf, 1, HZ);

919

920 US_DEBUGP("GetMaxLUN command result is %d, data is %d\n",

921 result, us->iobuf[0]);

922

923 /* if we have a successful request, return the result */

924 if (result > 0)

925 return us->iobuf[0];

926

927 /*

928 * Some devices (i.e. Iomega Zip100) need this -- apparently

929 * the bulk pipes get STALLed when the GetMaxLUN request is

930 * processed. This is, in theory, harmless to all other devices

931 * (regardless of if they stall or not).

932 */

933 if (result == -EPIPE) {

934

usb_stor_clear_halt(us, us->recv_bulk_pipe)
;
935


usb_stor_clear_halt(us, us->send_bulk_pipe)
;
936
}
937
938 /
*

论坛徽章:
0
74 [报告]
发表于 2008-06-06 14:13 |只看该作者
939 * Some devices don't like GetMaxLUN. They may STALL the control

940 * pipe, they may return a zero-length result, they may do nothing at

941 * all and timeout, or they may fail in even more bizarrely creative

942 * ways. In these cases the best approach is to use the default

943 * value: only one LUN.

944 */

945 return 0;

946 }

代码不长,不过并不容易.首先914 行, usb_stor_control_msg() 函数被调用,这也是我们自己定义的函
数,所以也得讲,同样来自drivers/usb/storage/transport.c:

209

210 /*

211 * Transfer one control message, with timeouts, and allowing early

212 * termination. Return codes are usual -Exxx, *not* USB_STOR_XFER_xxx.

213 */

214 int usb_stor_control_msg(struct us_data *us, unsigned int pipe,

215 u8 request, u8 requesttype, u16 value, u16 index,

216 void *data, u16 size, int timeout)

217 {

218 int status;

219

220 US_DEBUGP("%s: rq=%02x rqtype=%02x value=%04x index=%02x
len=%u\n",

221 __FUNCTION__, request, requesttype,

222 value, index, size);

223

224 /* fill in the devrequest structure */

225 us->cr->bRequestType = requesttype;

226 us->cr->bRequest = request;

227 us->cr->wValue = cpu_to_le16(value);

228 us->cr->wIndex = cpu_to_le16(index);

229 us->cr->wLength = cpu_to_le16(size);

230

231 /* fill and submit the URB */

232 usb_fill_control_urb(us->current_urb, us->pusb_dev, pipe,

233

(unsigned char*) us->cr, data, size,
234


usb_stor_blocking_completion, NULL)
;
235 status = usb_stor_msg_common(us, timeout)
;
236
237 /* return the actual length of the data transferred if no error *
/
238 if (status == 0)
239


status = us->current_urb->actual_length;

论坛徽章:
0
75 [报告]
发表于 2008-06-06 14:14 |只看该作者
240 return status;

241 }

这里相对麻烦一点的函数是usb_stor_msg_common, 仍然是我们自己定义的函数,所以我们又得继续
往下一层看,这么多层函数调用的确挺让人看了头晕,一个人总要走陌生的路,看陌生的风景,听陌生的歌,然
后在某个不经意的瞬间,你会发现,原本费尽心机想要弄清楚的函数就这么把自己弄糊涂了.然而,就像世上
的每一条路都是弯的一样,每一个模块都会有曲折的函数调用,写代码的哥们儿为了表现自己一流的编剧水
平,永远不会让我们一路平稳的看完整个模块的.除了面对,我们别无选择,毕竟从我们年幼时,摇篮就告诉我
们,人生是不平静的,也是动荡的,所以为何不微笑着面对这些麻烦?这个函数仍旧是来自
drivers/usb/storage/transport.c:

132 /* This is the common part of the URB message submission code

133 *

134 * All URBs from the usb-storage driver involved in handling a queued scsi

135 * command _must_ pass through this function (or something like it) for the

136 * abort mechanisms to work properly.

137 */

138 static int usb_stor_msg_common(struct us_data *us, int timeout)

139 {

140 struct completion urb_done;

141 struct timer_list to_timer;

142 int status;

143

144 /* don't submit URBs during abort/disconnect processing */

145 if (us->flags & ABORTING_OR_DISCONNECTING)

146 return -EIO;

147

148 /* set up data structures for the wakeup system */

149 init_completion(&urb_done);

150

151 /* fill the common fields in the URB */

152 us->current_urb->context = &urb_done;

153 us->current_urb->actual_length = 0;

154 us->current_urb->error_count = 0;

155 us->current_urb->status = 0;

156

157 /* we assume that if transfer_buffer isn't us->iobuf then it

158 * hasn't been mapped for DMA. Yes, this is clunky, but it's

159 * easier than always having the caller tell us whether the

160 * transfer buffer has already been mapped. */

161 us->current_urb->transfer_flags =

162

URB_ASYNC_UNLINK | URB_NO_SETUP_DMA_MAP;
163 if (us->current_urb->transfer_buffer == us->iobuf)
164


us->current_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
165 us->current_urb->transfer_dma = us->iobuf_dma;

论坛徽章:
0
76 [报告]
发表于 2008-06-06 14:14 |只看该作者
166 us->current_urb->setup_dma = us->cr_dma;
167
168 /* submit the URB *
/
169 status = usb_submit_urb(us->current_urb, GFP_NOIO)
;
170 if (status)
{
171 /* something went wrong *
/
172 return status;
173
}
174
175 /* since the URB has been submitted successfully, it's now okay
176 * to cancel it *
/
177 set_bit(US_FLIDX_URB_ACTIVE, &us->flags)
;
178
179 /* did an abort/disconnect occur during the submission? *
/
180 if (us->flags & ABORTING_OR_DISCONNECTING)
{
181
182 /* cancel the URB, if it hasn't been cancelled already *
/
183 if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->flags))
{
184 US_DEBUGP("-- cancelling URB\n")
;
185 usb_unlink_urb(us->current_urb)
;
186
}
187
}
188
189 /* submit the timeout timer, if a timeout was requested *
/
190 if (timeout > 0)
{
191 init_timer(&to_timer)
;
192 to_timer.expires = jiffies + timeout;
193 to_timer.function = timeout_handler;
194 to_timer.data = (unsigned long) us;
195 add_timer(&to_timer)
;
196
}
197
198 /* wait for the completion of the URB *
/
199 wait_for_completion(&urb_done)
;
200 clear_bit(US_FLIDX_URB_ACTIVE, &us->flags)
;
201
202 /* clean up the timeout timer *
/
203 if (timeout > 0)
204 del_timer_sync(&to_timer)
;
205
206 /* return the URB status *
/
207 return us->current_urb->status;
208
}

论坛徽章:
0
77 [报告]
发表于 2008-06-06 14:15 |只看该作者
好了,代码贴完了,现在让我们一点一点去认识这段代码.记住我们最终就是为了看清楚
usb_stor_Bulk_max_lun() 这个函数究竟是怎么获得max lun 的.

第一次亲密接触(二)

对于设备驱动程序而言,控制传输要做的事情很简单,向usb core 提交一个urb,这个urb 中间包含了一
个命令,或者说控制请求,因为命令更适合于我们后来要讲的某个重要的概念.这里我们要发送的就是GET
MAX LUN. 我们调用了一个函数, usb_stor_control_msg, 其作用从名字上也可以看出,发送控制信息,即
控制请求.做一件事情要遵守一件事情的规矩,发送控制信息必须按照一定的格式,发出去了人家才能看得懂.
就像你要给远方的恋人寄一封信,你要在信封上按基本格式填写一些东西,你的信才能被寄出去对吗.

我们结合usb_stor_control_msg 函数本身以及我们调用它的时候其具体上下文包括实际传递给它的
参数来读这段代码,看看这个格式究竟是如何的.首先第一个参数us,这个不用多说了吧.星星还是那个星星,
月亮也还是那个月亮,山也还是那座山,梁也还是那到梁,us 自然总还是那个us.而us 中有一个成员,叫做cr,
见struct us_data 的定义:

149 struct usb_ctrlrequest *cr; /* control requests */

她是struct usb_ctrlrequest 结构指针, usb规范规定了一个控制请求的格式为一个8 个字节的数据包,
而struct usb_ctrlrequest 正是专门为此而准备的8 个字节的一个变量,所以控制传输中总会用到她.她的
定义在include/linux/usb_ch9.h 中:

86 /**

87 * struct usb_ctrlrequest - SETUP data for a USB device control request

88 * @bRequestType: matches the USB bmRequestType field

89 * @bRequest: matches the USB bRequest field

90 * @wValue: matches the USB wValue field (le16 byte order)

91 * @wIndex: matches the USB wIndex field (le16 byte order)

92 * @wLength: matches the USB wLength field (le16 byte order)

93 *

94 * This structure is used to send control requests to a USB device. It matches

95 * the different fields of the USB 2.0 Spec section 9.3, table 9-2. See the

96 * USB spec for a fuller description of the different fields, and what they are

97 * used for.

98 *

99 * Note that the driver for any interface can issue control requests.

100 * For most devices, interfaces don't coordinate with each other, so

101 * such requests may be made at any time.

102 */

103 struct usb_ctrlrequest {

104 __u8 bRequestType;

105 __u8 bRequest;

106 __le16 wValue;

107 __le16 wIndex;

论坛徽章:
0
78 [报告]
发表于 2008-06-06 14:16 |只看该作者
108 __le16 wLength;

109 } __attribute__ ((packed));

不过需要说明一点,在usb spec 中,不叫cr, 而叫setup packet,在struct urb 里边就有这么一个名字
一样的成员,

799 unsigned char *setup_packet; /* (in) setup packet (control only) */

因为在Linux 中usb 的世界里一个通用的数据结构是urb,很多函数都是以urb 为参数的,所以稍后我们
实际上还是会让urb 中的setup_packet 指针指向这个usb_ctrlrequest 结构体的.毕竟我们最终要提交
的就是urb,而不是cr.并且cr 只是出现在控制传输,而urb 却是四大传输都要用到的.

Ok,在usb mass storage bulk only transport 协议里面,规定的很清楚,要发送GET MAX LUN 请求,
必须把bmRequestType 设置为device to host,class,interface, 同时把bRequest 设置为254(FEh),
即咱们这里的0xfe,而wValue 设置为0,wIndex 设置为接口的编号,最后wLength 设置为1.

需要解释一下,什么叫bmRequestType 设置device to host,class,interface? 实际上,usb 2.0 规范
里面指出,所有的usb 设备都会响应主机的一些请求,这些请求必须来自主机,通过设备的默认控制管道发
出.(即0 号端点所对应的那个管道)这些请求,或者说这些request,是通过控制传输来实现的,请求以及请
求所需的参数都是通过setup packet 来发送的.主机负责建立好这个setup packet.( 也就是咱们刚才的
那个cr,后来的setup_packet.) 每个setup packet 包含8 个bytes.她们的含义如下:

byte0: bmRequestType, 注意,在刚才咱们代码中数据结构struct ctrlrequest 里边是写的
bRequestType, 但是她们对应的是相同的冬冬.而之所以usb 协议里写成bmRequestType, 是因为她实
际上又是一个位图(m 表示map),也就是说,尽管她只有1 个byte,但是她仍然是当作8 位来用.她的8 位
的含义是:

D7: 控制传输数据传输的方向,0 表示方向为主机到设备,1 表示方向为设备到主机.(有时控制传
输除了发命令阶段以外,可能还有数据传输阶段,此处表示的是在数据传输那个阶段的传输方向.)

D6...5: 请求的类型,0 称为标准类型(Standard),1 称为Class,2 称为Vendor,3 则是保留的,
不能使用.

D4...0: 接收者,0 表示设备,1 表示接口,2 表示端点,3 表示其它,4...31 都是保留的,不能使用.

所以,bmRequestType 被设device to host,class,interface, 表示,数据传输阶段传输方向是设
备到主机,请求的类型是Class, 意思是,请求类型可以有好几种,首先usb spec本身定义了很多种标准请求,
所有的usb 设备都必须支持这些标准请求,或者或标准request,另一方面,一类设备或者厂商自己也可以定
义自己的额外的一些请求,显然这里get max lun 就是usb mass storage 这个类,或者说这个class 所定
义的请求,因此,请求类型设置为class, 而request 接收者可以是设备,也可以是接口,还可以是端点,对get
max lun 这个命令来说,她是针对一个u 盘的,所以她的接收者应该是interface.

byte1: bRequest,1 个byte, 指定了是哪个请求.每一个请求都有一个编号,咱们这里是GET MAX
LUN,其编号是FEh.

byte2...3: wValue,2 个bytes, 不同请求有不同的值,咱们这里刚才已经说了,必须为0.

论坛徽章:
0
79 [报告]
发表于 2008-06-06 14:16 |只看该作者
byte4...5: wIndex,2 个bytes, 不同请求有不同的值,咱们这里要求被设置为是interface number.

byte6...7: wLength,2 个bytes,如果接下来有数据传输阶段,则这个值表征了数据传输阶段传输多少
个bytes,没啥好说的,这个值在GET MAX LUN 请求中被规定为1,也就是说返回1 个byte 即可.

结合函数调用的实参和函数的定义,可见,对于这个cr 来说,其bRequest 被赋值为
US_BULK_GET_MAX_LUN,bRequestType 被赋值为
USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE, 而wValue 被设为0,wIndex 被设置为
us->ifnum, 推开记忆的门,我们发现在associate_dev() 函数中,us->ifnum 被赋值为
intf->cur_altsetting->desc.bInterfaceNumber, 即她确实对应了interface number. wLength 被赋
值为1.设置好cr 之后,就可以把她传递给urb 的setup_packet 了.对比上面的usb_stor_control_msg()
函数中第232 行,和下面函数usb_fill_control_urb() 的定义,即可看出urb 的setup_packet 指针指向
了这个cr.具体来看usb_fill_control_urb() 函数,这个函数定义于include/linux/usb.h 中:

812 /**

813 * usb_fill_control_urb - initializes a control urb

814 * @urb: pointer to the urb to initialize.

815 * @dev: pointer to the struct usb_device for this urb.

816 * @pipe: the endpoint pipe

817 * @setup_packet: pointer to the setup_packet buffer

818 * @transfer_buffer: pointer to the transfer buffer

819 * @buffer_length: length of the transfer buffer

820 * @complete: pointer to the usb_complete_t function

821 * @context: what to set the urb context to.

822 *

823 * Initializes a control urb with the proper information needed to submit

824 * it to a device.

825 */

826 static inline void usb_fill_control_urb (struct urb *urb,

827 struct usb_device *dev,

828 unsigned int pipe,

829 unsigned char *setup_packet,

830 void *transfer_buffer,

831 int buffer_length,

832 usb_complete_t complete,

833 void *context)

834 {

835 spin_lock_init(&urb->lock);

836 urb->dev = dev;

837 urb->pipe = pipe;

838 urb->setup_packet = setup_packet;

839 urb->transfer_buffer = transfer_buffer;

840 urb->transfer_buffer_length = buffer_length;

841 urb->complete = complete;

842 urb->context = context;

843 }

论坛徽章:
0
80 [报告]
发表于 2008-06-06 14:17 |只看该作者
很显然,她就是为特定的pipe 填充一个urb(最初urb 申请的时候被初始化为0 了嘛不是).对比函数调用
和函数定义,可知, 这个pipe 被设为了us->recv_ctrl_pipe, 即接收控制管道,也就是说专门为设备向主机
发送数据而设置的管道.而这个urb 就是us->current_urb, 并且除了urb->setup_packet(unsigned
char 类型的指针)指向了us->cr 之外,urb->transfer_buffer(void 指针)指向了
us->iobuf,urb->transfer_buffer_length(int 类型)被赋值为1,urb->complete(usb_complete_t
类型)被赋值为usb_stor_blocking_completion.

此处特意提一下usb_complete_t 类型,在include/linux/usb.h 中,有这么一行,

612 typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);

这里用了typedef 来简化声明,不熟悉typedef 功能的可以去查一下,typedef 的强大使得以下两种声明
作用相同:

一种是:

void (*func1)(struct urb *,struct pt_regs *);

void (*func2)(struct urb *,struct pt_regs *);

void (*func3)(struct urb *,struct pt_regs *);

另一种是:

typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);

usb_complete_t func1;

usb_complete_t func2;

usb_complete_t func3;

看出来了吧,如果要声明很多个函数指针,她们的参数又都很复杂,那么显然使用typedef 一次,就可以一
劳永逸了,以后声明就很简单了.所以,咱们也就知道实际上,urb 中的complete 是一个函数指针,她被设置
为指向函数usb_stor_blocking_completion(). 关于usb_stor_blocking_completion() 咱们暂且不提,
等到用的时候再说.

看完了usb_fill_control_urb, 咱们再回到usb_stor_control_msg() 函数,接下来是下一个函
数,usb_stor_msg_common(), 注意了,usb_fill_control_urb 这个函数只填充了urb 中的几个元
素,struct urb 里面包含了很多东西,不过有一些设置是共同的,所以下面用usb_stor_msg_common()
函数来设置,可以看出给这个函数传递的参数只有两个,一个就是us,另一个是timeout( 传给她的值是HZ),
我们继续进入到这个函数中来把她看个清清楚楚明明白白真真切切.天空收容每一片云彩,不论其美丑,所以
天空宽阔无边;大地拥抱每一寸土地,不论其贫富,所以大地广袤无垠;海洋接纳每一条河流,不论其大小,所
以海洋广阔无边;而这个函数支持各种传输,作为usb-storage 模块,无论其采用何种传输方式,无论传输数
据量的多少,最终都一定要调用这个函数,所以我们必须承认这个函数很伟大.

鉴于这个函数的重要性,它适用于各种信息的传送,而不仅仅是控制传送,日后在bulk 传输中我们还将遇
上它,我们将在下一节专门来分析.但是在看这个函数之前,有些事情必须要心里有数,那就是作为设备驱动
程序,只需要提交一个urb 就可以了,剩下的事情usb core 会去处理,有了结果它会通知我们.而提交
urb,usb core为我们准备了一个函数,usb_submit_urb() 不管我们使用什么传输方式,我们都只要调用这
个函数即可,在此之前,我们需要做的只是准备好这么一个urb,把urb中各相关的成员填充好,然后就ok 了.
而这usb_stor_msg_common 正是这样做的.而显然,不同的传输方式其填写urb 的方式也不同.

最后我们需要指出一点,这里我们的cr 是一个指针,有同志会问这个指针申请过内存了吗?答案是肯定的,
忆往昔峥嵘岁月,曾几何时,我们在函数associate_dev() 中就见到了us->cr,并且用usb_buffer_alloc
为其申请了内存,当时我们就讲过的.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP