免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 59796 | 回复: 212

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

论坛徽章:
0
发表于 2008-06-06 09:51 |显示全部楼层
昨天在网上搜的。
usb_Acr33B.tmp.pdf (1.18 MB, 下载次数: 1808)

论坛徽章:
0
发表于 2008-06-06 13:32 |显示全部楼层
Linux 那些事儿之我是U 盘

摘要

2005 年6 月,复旦大学微电子系本科毕业答辩上,老师问我:请你用一句话介绍一下usb 技

术.我回了一句:老师,你有病吧,要能用一句话介绍我还费这么大劲写这么长的文章干嘛?

关键词:Linux, Kernel, 2.6, bus, usb, device driver, mass storage, scsi,

urb, bulk, control, host, pipe, command, 林志玲

目录

引子2
小城故事3
MAKEFILE 不是MAKE LOVE 4
变态的模块机制 6
想到达明天现在就要启程 9
未曾开始却似结束 11
狂欢是一群人的孤单 12
总线,设备,和驱动(上) 13
总线,设备,和驱动(下) 15
我是谁的他? 16
从协议中来,到协议中去(上) 19
从协议中来,到协议中去(中) 20
从协议中来,到协议中去(下) 23
梦开始的地方 24
设备花名册 27
冰冻三尺非一日之寒 32
冬天来了,春天还会远吗?(一) 36
冬天来了,春天还会远吗?(二) 41
冬天来了,春天还会远吗?(三) 45
冬天来了,春天还会远吗(四) 47
冬天来了,春天还会远吗?(五) 52
通往春天的管道 57
传说中的URB 63
心锁69
第一次亲密接触(一) 71
第一次亲密接触(二) 76

论坛徽章:
0
发表于 2008-06-06 13:33 |显示全部楼层
第一次亲密接触(三) 80
第一次亲密接触(四) 82
将控制传输进行到底 85
横空出世的SCSI 88
谁是最变态的结构体 92
SCSI 数据结构-像雾像雨又像风 102
彼岸花的传说(一) 108
彼岸花的传说(二) 109
彼岸花的传说(三) 114
彼岸花的传说(四) 117
彼岸花的传说(五) 123
彼岸花的传说(六) 128
彼岸花的传说(七) 131
彼岸花的传说(八) 135
彼岸花的传说(THE END) 138
SCSI 命令之我型我秀 139
迷雾重重的BULK 传输(一) 144
迷雾重重的BULK 传输(二) 150
迷雾重重的BULK 传输(三) 154
迷雾重重的BULK 传输(四) 158
迷雾重重的BULK 传输(五) 163
迷雾重重的BULK 传输(六) 166
跟着感觉走(一) 171
跟着感觉走(二) 173
光荣属于苹果,属于诺基亚,属于摩托罗拉,属于索尼爱立信! 179
有多少爱可以胡来?(一) 182
有多少爱可以胡来?(二) 188
当梦醒了天晴了 193
其实世上本有路,走的人多了,也便没了路 198

引子

也许是在复旦养成了昼伏夜出的坏习惯,工作之后也总是很晚也不愿意睡.来到北京之后,开
始听广播听都市之声的北京不眠夜.这个节目是从23 点直到第二天凌晨一点,我常常是听完
了才会睡觉.无论是北京还是上海,对我来说,生存总是那么困难,生活的压力总是那么大,每
天只有在这个节目中才能够寻找到一丝温暖.我不喜欢躺在床上听,而是喜欢一边听一边做
点别的事情,于是心血来潮的决定,写点文字吧,听着电波里别人分享心情,不妨也用文字来记
录自己的心情吧.

我首先想到的是写一些和Linux 相关的文字.事实上我并不喜欢Linux,学习Linux完全是一
种无奈,工作中要用,迫于生计,不得不去学习,而学习Linux 的过程中唯一让我觉得还有些乐
趣的是当遇到问题的时候可以去网上问去网上查,很多人写了很多文档可以让我们这些菜鸟
们参考学习,这样才让我们在工作中走了很多弯路.挺感谢那些分享自己知识的人.碰巧最近

论坛徽章:
0
发表于 2008-06-06 13:33 |显示全部楼层
我也看了点冬冬,并且这些冬冬在网上的资料也比较少,所以我想我不妨也把自己那一夜的
收获写出来,或许以后也能给别人提供一些帮助,想想也是,整个Linux 社区不正是这样吗,像
陈奕迅唱的那样,”把一个人的温暖转移到另一个人的胸膛”.

我要写的是Linux 设备驱动程序相关的,主要分析的是Linux 中与U 盘相关的那部分代码.
过去也没有看过,但是今年4 月底的某一天,一个偶然的原因,我一时冲动就看了一遍.我们几
个同学在人大附近打麻将,打到夜深了,因为我们几人人住的位置都离得挺远的,各自回去都
得打车,于是决定不如去权金城开个房间,晚上就睡那得了.在权金城洗浴中心,和几个同学洗
浴过后,有人去按摩了,而我和另一个人则留在了房间里,无聊中,那位哥们见我带了电脑,说
他有部A 片,很不错,不是很大,所以他存在U 盘里的,他还挺逗的说这是2008 年北京奥运会
指定A 片,问我有没有兴趣,这还用问,当然有兴趣了,于是立马打开电脑,插入u 盘,然后不一
会我就傻了,因为我的电脑根本就不能识别U 盘,首先我的电脑比较旧,装的是双系统,一个是
Win 98,这个没办法,没有U 盘驱动,另一个是Linux,2.6 的内核,按理应该是支持U 盘的,
问题是实际情况却是我没有看到U 盘,/dev/目录下面根本没有这么一个盘符,于是我没办法
了,一脸沮丧,而同学在旁边自然表示出了对Linux 很鄙视的神情.

过了一会,他去看电视了,正好有英超,我却没有心情看电视,想想就觉得奇怪,怎么会不能使
用U 盘呢,这不可能啊,一定是我自己对Linux 下面的一些冬冬没有弄清楚,于是我决定好好
看看问题到底出在哪,记得当时看了一下/var/log/messages 这个日志文件里边好像记录
了一些信息,感觉像是一些错误信息,但是看不明白它到底在说什么.同学开始劝我,算了算了,
改天再看吧,这话我可不愿意听,不是说Linux 内核源代码是公开的吗,大不了看看源代码,搞
清楚工作原理了还怕问题不能解决?无非就是一些C 代码而已,好歹哥们也是认真学过谭浩
强大哥那本C 程序设计的.而且当初那本书课后习题老师基本上都让我们做了,虽说是参考
了那本习题解答的书,可就算写代码不行,读代码还是没问题吧,语法什么的基本上还是很清
楚的,什么判断结构循环结构,包括goto 语句,还是记得的.

所以我就开始看了,正所谓梦想有多远,就能走多远.以前我只是玩CS 玩仙剑的时候能够整
晚整晚不睡,但那个晚上,为了告诉我同学,Linux 下也能看A 片,Linux 下遇到问题更适合自
己解决,我愣是从一点看到快天亮,终于把drivers/usb/storage/ 目录下面一万余行的代码
给看了一遍.当然没有看得太仔细,但是很显然把整个原理搞清楚了,问题也很快得以解决.

所以此刻,我整理了一下思路,决定把那晚看的冬冬用文字记录下来.也算为了纪念那个不寻
常的夜晚吧.不过我估计这个篇幅不会短,因为光那一万余行的代码贴出来就得占许许多多
页了,所以这件事情也许会占用我不少时间,然而,还好,每晚有北京不眠夜的陪伴,而且,也许
当我把心思投入到写这个故事的时候,能够把那些压力那些烦恼那种孤独那种郁闷以及那种
对生活的绝望给暂时忘记些许.

小城故事

这个故事中使用的是2.6.10 的内核代码.Linux 内核代码目录中, 所有去设备驱动程序有关的代
码都在drivers/目录下面,在这个目录中我们用ls 命令可以看到很多子目录.

论坛徽章:
0
发表于 2008-06-06 13:34 |显示全部楼层
localhost:/usr/src/linux-2.6.10/drivers # ls

Kconfig atm cdrom eisa ide macintosh message net parpo
rt s390 tc w1
Makefile base char fc4 ieee1394 mca misc nubus pci
sbus telephony zorro
acorn block cpufreq firmware input md mmc oprofile pcmci
a scsi usb
acpi bluetooth dio i2c isdn media mtd parisc pnp se
rial video

其中usb 目录包含了所有usb 设备的驱动,而usb 目录下面又有它自己的子目录,进去看一下,

localhost:/usr/src/linux-2.6.10/drivers # cd usb/
locahost:/usr/src/linux-2.6.10/drivers/usb # ls
Kconfig Makefile README atm class core gadget host image input media

misc net serial storage usb-skeleton.c

注意到每一个目录下面都有一个Kconfig 文件和一个Makefile, 这很重要.稍后会有介绍.

而我们的故事其实是围绕着drivers/usb/storage 这个目录来展开的.实际上这里边的代码清清
楚楚地展示了我们日常频繁接触的U 盘是如何工作的,是如何被驱动起来的.但是这个目录里边
的冬冬并不是生活在世外桃源,他们总是和外面的世界有着千丝万缕的瓜葛.可以继续进来看一
下,

localhost:/usr/src/linux-2.6.10/drivers/usb # cd storage/
localhost:/usr/src/linux-2.6.10/drivers/usb/storage # ls
Kconfig debug.c freecom.c isd200.c protocol.c sddr09.c shuttle_usbat
.c unusual_devs.h
Makefile debug.h freecom.h isd200.h protocol.h sddr09.h shuttle_usba

t.h usb.c
datafab.c dpcm.c initializers.c jumpshot.c scsiglue.c sddr55.c transport.c
usb.h
datafab.h dpcm.h initializers.h jumpshot.h scsiglue.h sddr55.h transport.h

咋一看,着实吓了一跳,用`wc -l *` 这个命令统计一下,12076 行,晕死...

但是,也许,生活中总是充满了跌宕起伏.

认真看了一下Makefile 和Kconfig 之后,心情明显好了许多.

Makefile 不是Make Love

出来混,迟早要还的.

从前在复旦,混了四年,没有学到任何东西,每天就是逃课,上网,玩游戏,睡觉.毕业的时候,身边的
人读研的读研,出国的出国,找工作的吧,去麦肯锡的去麦肯锡,去IBM 的去IBM.而自己却一无所
长,没有任何技能,直到这时候才发现那四年欠了很多债,早知今日,何必当初.幸运的是,我还有一

4

论坛徽章:
0
发表于 2008-06-06 13:35 |显示全部楼层
张复旦的文凭,依靠着这张文凭,混进了Intel. 然而,工作以后,更是发现当初在校期间没有好好读
书其实真是在欠债,当初没学,工作以后还是要学,的确是迟早要还的,逃是逃不掉的.

毕业的时候,人家跟我说Makefile 我完全不知,但是一说Make Love 我就来劲了.现在想来依然
觉得丢人.

基本上,Linux 内核中每一个目录下边都有一个Makefile,Makefile 和Kconfig 就像一个城市

地图,地图带领我们去认识一个城市,而Makefile 和Kconfig 则可以让我们了解这个目录下面

结构.drivers/usb/storage/ 目录下边的Makefile 内容如下
:


#
# Makefile for the USB Mass Storage device drivers.
#
# 15 Aug 2000, Christoph Hellwig <hch@infradead.org>
# Rewritten to use lists instead of if-statements.
#


EXTRA_CFLAGS := -Idrivers/scsi

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

usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG) += debug.o
usb-storage-obj-$(CONFIG_USB_STORAGE_HP8200e) += shuttle_usbat.o
usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR09) += sddr09.o
usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR55) += sddr55.o
usb-storage-obj-$(CONFIG_USB_STORAGE_FREECOM) += freecom.o
usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM) += dpcm.o
usb-storage-obj-$(CONFIG_USB_STORAGE_ISD200) += isd200.o
usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o
usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o

usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \
initializers.o $(usb-storage-obj-y)

关于Kconfig 文件,在故事的最后会介绍,此刻暂且不表,Kconfig 文件比较长,就不贴出来了.但
是通过看Kconfig 文件,我们可以知道,除了CONFIG_USB_STORAGE 这个编译选项是我们真
正需要的以外,别的选项我们都可以不予理睬.比如,关于
CONFIG_USB_STORAGE_DATAFAB,Kconfig 文件中有这么一段,

config USB_STORAGE_DATAFAB

bool "Datafab Compact Flash Reader support (EXPERIMENTAL)"

depends on USB_STORAGE && EXPERIMENTAL

help

Support for certain Datafab CompactFlash readers.

Datafab has a web page at <http://www.datafabusa.com/>.

显然,这个选项和我们没有关系,首先这是专门针对Datafab 公司的产品的,其次CompactFlash
reader 是一种flash 设备,但这显然不是U 盘,因为drivers/usb/storage 这个目录里边的代码
是针对一类设备的,不是某一种特定的设备,这一类设备就是usb mass storage 设备,关于这类
设备,有专门的文档进行介绍,有相应的spec, 描述这类设备的通信或者物理上电特性上等方面的

论坛徽章:
0
发表于 2008-06-06 13:35 |显示全部楼层
规范,U 盘只是其中的一种,这种设备使用的通信协议被称为Bulk-Only Transport 协议.再比如,
关于CONFIG_USB_STORAGE_SDDR55 这个选项,Kconfig 文件中也有对应的一段,

config USB_STORAGE_SDDR55

bool "SanDisk SDDR-55 SmartMedia support (EXPERIMENTAL)"

depends on USB_STORAGE && EXPERIMENTAL

help

Say Y here to include additional code to support the Sandisk SDDR-55

SmartMedia reader in the USB Mass Storage driver.
很显然这是SanDisk 的产品,并且是针对SM 卡的,这也不是U 盘,所以我们也都不去理睬了.事
实上,很容易确定,只有CONFIG_USB_STORAGE 这个选项是我们真正关心的,而它所对应的
模块叫usb-storage,Makefile 中最后一行也说了,

usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \

initializers.o $(usb-storage-obj-y)
这就意味着我们只需要关注的文件就是
scsiglue.c,protocol.c,transport.c,usb.c,initializers.c 以及它们同名的.h 头文件.再次使用
wc -l 命令统计一下这几个文件,发现总长度只有3701 行,比最初看到的12000 多行少了许多,
当时信心就倍增.

不过需要特别注意的是,CONFIG_USB_STORAGE_DEBUG 这个编译选项,它不是我们必须的,
但是如果真的要自己修改或者调试usb-storage 的代码,那么打开这个选项是很有必要的,因为
它会负责打印一些调试信息,以后在源代码中我们会看到它的作用.

变态的模块机制

有一种感动,叫泪流满面,有一种机制,叫模块机制,十月革命一声炮响,给Linux 送来了模块机制.
显然,这种模块机制给那些Linux 的发烧友们带来了方便,因为模块机制意味着人们可以把庞大
的Linux 内核划分为许许多多个小的模块,对于编写设备驱动程序的那帮家伙来说,从此以后他
们可以编写设备驱动程序却不需要把她编译进内核,不用reboot 机器,她只是一个模块,当你需
要她的时候,你可以把她抱入怀中(insmod), 当你不再需要她的时候,你可以把她一脚踢开,甚至,
你可以对她咆哮:"滚吧,贱人!"(rmmod).她不能成为你的手足,只能算你的衣服.

也许在现实世界里不会这样,但是在Linux 的虚拟世界里,确实可以是如此,time and time
again,我问自己,模块是否就像现实生活中的妓女一样呢?Linux 内核是嫖客,当他需要这个模块
的时候,他就把人家揽入怀中,当他不需要人家的时候,就把别人踢开,而且,模块总是能够逆来顺
受,尽管Linux 内核会一次次抛弃她,但是每当Linux 内核再次需要她的时候,当内核再次执行
insmod 的时候,模块依然会尽自己的能力去取悦内核,这是否太可悲了些!记得孔子曾经说过,读
懂Linux 内核代码不难,难得是读懂Linux 内核代码背后的哲学!难道这就是传说中的藏在Linux
代码背后的哲学!天哪!

抛开这见鬼的哲学吧.让我们从一个伟大的例子去认识模块.这就是传说中的"Hello World!", 这
个梦幻般的名字我们看过无数次了,每一次她出现在眼前,就意味着我们开始接触一种新的计算
机语言了,或者,如此刻,开始描述一个新的故事.

请看下面这段代码,她就是Linux 下的一个最简单的模块.当你安装这个模块的时候,她会用她
特有的语言向你表白,"Hello,world!", 千真万确,她没有说"Honey,I love you!", 虽然,她可以这

论坛徽章:
0
发表于 2008-06-06 13:36 |显示全部楼层
么说,如果你要求她这么说.而后来你卸载了这个模块,你无情抛弃了她,她很伤心,她很绝望,但她
没有抱怨,她只是淡淡地说,"Goodbye,cruel world!"( 再见,残酷的世界!)
++++++++++++++++++hello.c++++++++++++++++++++

1 #include <linux/init.h> /* Needed for the macros *
/
2 #include <linux/module.h> /* Needed for all modules *
/
3 MODULE_LICENSE("Dual BSD/GPL")
;
4 MODULE_AUTHOR("fudan_abc")
;
5
6 static int __init hello_init(void)


7 {
8 printk(KERN_ALERT "Hello, world!\n");
9 return 0;
10 }
11

12 static void __exit hello_exit(void)
13
{
14 printk(KERN_ALERT "Goodbye, cruel world\n")
;
15
}
16
17 module_init(hello_init)
;
18 module_exit(hello_exit)
;


++++++++++++++++++++++++++++++++++++++++++++++++

你需要使用module_init() 和module_exit(), 你可以称她们为函数,不过实际上她们是一些
宏(macro),现在你可以不用去知道她们背后的故事,只需要知道,在Linux Kernel 2.6 的世界里,
你写的任何一个模块都需要使用她们来初始化或退出,或者说注册以及后来的注销.当你用
module_init() 为一个模块注册了之后,在你使用insmod 这个命令去安装的时
候,module_init() 注册的函数将会被执行,而当你用rmmod 这个命令去卸载一个模块的时
候,module_exit()注册的函数将会被执行.module_init() 被称为驱动程序的初始化入口
(driver initialization entry point).

怎么样演示以上代码的运行呢?没错,你需要一个Makefile.
+++++++++++++++++++++Makefile++++++++++++++++++++++++
+++

1 # To build modules outside of the kernel tree, we run "make"
2 # in the kernel source tree; the Makefile these then includes this
3 # Makefile once again.
4 # This conditional selects whether we are being included from the
5 # kernel Makefile or not.
6 ifeq ($(KERNELRELEASE),
)
7
8


# Assume the source tree is where the running kernel was built
9

# You should set KERNELDIR in the environment if it's elsewhere
10 KERNELDIR ?= /lib/modules/$(shell uname -r)/build
11 # The current directory is passed to sub-makes as argument
12 PWD := $(shell pwd)

论坛徽章:
0
发表于 2008-06-06 13:36 |显示全部楼层
13

14 modules:

15

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
16
17 modules_install:
18 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
19
20 clean:
21


rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

22

23 .PHONY: modules modules_install clean

24

25 else

26 # called from kernel build system: just declare what our modules are

27 obj-m := hello.o

28 endif
++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++

在lwn 上可以找到这个例子,你可以把以上两个文件放在你的某个目录下,然后执行make,也许
你不一定能成功,因为LK 2.6 要求你编译模块之前,必须先在内核源代码目录下执行make,换言
之,你必须先配置过内核,执行过make, 然后才能make 你自己的模块.原因我就不细说了,你按
着她要求的这么去做就行了.在内核顶层目录make 过之后,你就可以在你当前放置Makefile 的
目录下执行make 了.Ok,make 之后你就应该看到一个叫做hello.ko 的文件生成了,恭喜你,这
就是你将要测试的模块.

执行命令,

#insmod hello.ko

同时在另一个窗口,用命令tail -f /var/log/messages 察看日志文件,你会看到

Hello world 被打印了出来.

再执行命令,

#rmmod hello.ko

此时,在另一窗口你会看到Goodbye,cruel world!被打印了出来.

到这里,我该恭喜你,因为你已经能够编写Linux 内核模块了.这种感觉很美妙,不是吗?你可以
嘲笑秦皇汉武略输文采唐宗宋祖稍逊风骚,还可以嘲笑一代天骄成吉思汗只识弯弓射大雕了. 是
的,Twins 姐姐(s)告诉我们,只要我喜欢,还有什么不可以.

日后我们会看到,2.6 内核中,每个模块都是以module_init 开始,以module_exit 结束.大多
数来说没有必要知道这是为什么,记住就可以了,相信每一个对Linux 有一点常识的人都会知道
这一点的,对大多数人来说,这就像是1+1 为什么等于2 一样,就像是两点之间最短的是直线,不
需要证明,如果一定要证明两点之间直线最短,可以扔一块骨头在B 点,让一条狗从A 点出发,你会
发现狗走的是直线,是的,狗都知道,你还能不知道吗?

论坛徽章:
0
发表于 2008-06-06 13:37 |显示全部楼层
想到达明天现在就要启程

既然知道了怎么编写一个模块,那么编写设备驱动程序自然也就不难了.我相信,每一个会写模块
的人都不会觉得写设备驱动有困难.对自己行不行不确定的话,可以去问一下葛优,他准说:"(神州
行),我看行."

真的,我没说假话.写驱动不是什么难事,你完全可以很自信的说,你已经可以写Device Driver 了.
对,没错,飘柔,就这么自信.

前面说了每一个模块都是以module_init 开始,以module_exit 结束,那么我们就来看一下U
盘的驱动的这个模块.在茫茫人海中,我们很容易找到这个文件:drivers/usb/storage/usb.c,
在这个文件中又不难发现下面这段:

/****************************************************************

*******

1056 * Initialization and registration

1057 **********************************************************
*************/

1058

1059 static int __init usb_stor_init(void)

1060 {
1061 int retval;
1062 printk(KERN_INFO "Initializing USB Mass Storage driver...\n");
1063
1064 /* register the driver, return usb_register return code if error */
1065 retval = usb_register(&usb_storage_driver);
1066 if (retval == 0)
1067 printk(KERN_INFO "USB Mass Storage support registered.\n");
1068
1069 return retval;
1070 }
1071

1072 static void __exit usb_stor_exit(void)
1073
{
1074 US_DEBUGP("usb_stor_exit() called\n")
;
1075
1076 /* Deregister the driver
1077 * This will cause disconnect() to be called for each
1078 * attached unit
1079 *
/
1080 US_DEBUGP("-- calling usb_deregister()\n")
;
1081 usb_deregister(&usb_storage_driver)
;
1082
}
1083
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP