原文:http://blog.csdn.net/fudan_abc/article/details/1807181
写一下UHCI吧,也顺便怀念一下Intel,以及Intel的那几个女同事们,好久没联系了,你们可好?
UHCI是Intel提出来的.虽然离开Intel一年多了,但我总觉得也许有一天我还会回到Intel.所以关于Intel的东西,我多少会关注一下.我挺怀念Intel的,虽然钱也不多,但是那时候毕竟刚毕业,对钱的问题也没想太多.
UHCI全名Universal Host Controller Interface,它是一种USB主机控制器的接口规范,江湖中把遵守它的硬件称为UHCI主机控制器.在Linux中,把这种硬件叫做HC,或者说Host Controller,而把与它对应的软件叫做HCD.即HC Driver.Linux中这个HCD所对应的模块叫做uhci-hcd.
当我们看一个模块的时候,首先是看Kconfig和Makefile文件.在drivers/usb/host/Kconfig文件中:
161 config USB_UHCI_HCD
162 tristate "UHCI HCD (most Intel and VIA) support"
163 depends on USB && PCI
164 ---help---
165 The Universal Host Controller Interface is a standard by Intel for
166 accessing the USB hardware in the PC (which is also called the USB
167 host controller). If your USB host controller conforms to this
168 standard, you may want to say Y, but see below. All recent boards
169 with Intel PCI chipsets (like intel 430TX, 440FX, 440LX, 440BX,
170 i810, i820) conform to this standard. Also all VIA PCI chipsets
171 (like VIA VP2, VP3, MVP3, Apollo Pro, Apollo Pro II or Apollo Pro
172 133). If unsure, say Y.
173
174 To compile this driver as a module, choose M here: the
175 module will be called uhci-hcd.
众里寻他千百度之后,我发现了上面这段文字,注意那句depends on USB && PCI.这句话的意思就是说这个选项是依赖于另外两个选项,CONFIG_USB和CONFIG_PCI,很显然这两个选项代表着Linux中usb的核心代码和pci的核心代码.
UHCI作为USB主机控制器的接口,它依赖于usb核心这很正常,但为何它也依赖于pci核心呢?理由很简单,UHCI主机控制器本身通常是PCI设备,即通常它会插在PCI插槽里,或者直接就集成在主板上.但总之,大多数的UHCI主机控制器是连在PCI总线上的.所以,很无奈的是,写UHCI驱动程序就不得不了解一点PCI设备驱动程序.
先用lspci命令看一下,
localhost:/usr/src/linux-2.6.22.1/drivers/usb/host # lspci | grep USB
00:1d.0 USB Controller: Intel Corporation Enterprise Southbridge UHCI USB #1 (rev 09)
00:1d.1 USB Controller: Intel Corporation Enterprise Southbridge UHCI USB #2 (rev 09)
00:1d.2 USB Controller: Intel Corporation Enterprise Southbridge UHCI USB #3 (rev 09)
00:1d.7 USB Controller: Intel Corporation Enterprise Southbridge EHCI USB (rev 09)
比如在我的计算机里,就有三个UHCI主机控制器,以及另一个主机控制器,EHCI主机控制器,它们都是pci设备.
接着我们来看Makefile.
localhost:/usr/src/linux-2.6.22.1/drivers/usb/host # cat Makefile
#
# Makefile for USB Host Controller Drivers
#
ifeq ($(CONFIG_USB_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
obj-$(CONFIG_PCI) += pci-quirks.o
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
很显然,我们要的就是与CONFIG_USB_UHCI_HCD对应的uhci-hcd.o这个模块.而与uhci-hcd.o最相关的就是与之同名的C文件.这是它的源文件.在drivers/usb/host/uhci-hcd.c的最后7行,我们看到:
969 module_init(uhci_hcd_init);
970 module_exit(uhci_hcd_cleanup);
971
972 MODULE_AUTHOR(DRIVER_AUTHOR);
973 MODULE_DESCRIPTION(DRIVER_DESC);
974 MODULE_LICENSE("GPL");
正如每个女人都应该有一支口红,每个模块都应该有两个宏,它们是module_init和module_exit,分别用来初始化和注销自己.而这两行代码的意思就是说uhci_hcd_init这个函数将会在你加载这个模块的时候被调用,uhci_hcd_cleanup则是将会在你卸载这个模块的时候被执行.
所以我们没有办法,只能从uhci_hcd_init开始我们的故事.
917 static int __init uhci_hcd_init(void)
918 {
919 int retval = -ENOMEM;
920
921 printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION "%s/n",
922 ignore_oc ? ", overcurrent ignored" : "");
923
924 if (usb_disabled())
925 return -ENODEV;
926
927 if (DEBUG_CONFIGURED) {
928 errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
929 if (!errbuf)
930 goto errbuf_failed;
931 uhci_debugfs_root = debugfs_create_dir("uhci", NULL);
932 if (!uhci_debugfs_root)
933 goto debug_failed;
934 }
935
936 uhci_up_cachep = kmem_cache_create("uhci_urb_priv",
937 sizeof(struct urb_priv), 0, 0, NULL, NULL);
938 if (!uhci_up_cachep)
939 goto up_failed;
940
941 retval = pci_register_driver(&uhci_pci_driver);
942 if (retval)
943 goto init_failed;
944
945 return 0;
946
947 init_failed:
948 kmem_cache_destroy(uhci_up_cachep);
949
950 up_failed:
951 debugfs_remove(uhci_debugfs_root);
952
953 debug_failed:
954 kfree(errbuf);
955
956 errbuf_failed:
957
958 return retval;
959 }
Linux下USB HUB的驱动的实现和分析:
在系统初始化的时候在usb_init函数中调用usb_hub_init函数,就进入了hub的初始化。
在usb_hub_init函数中完成了注册hub驱动,并且利用函数kthread_run创建一个内核线程。该线程用来管理监视hub的状态,所有的情况都通过该线程来报告。
USB设备是热插拔,这就和PCI设备不同,PCI设备是在系统启动的时候都固定了,因此PCI设备只需要初始化进行枚举就可以了,采用递归算法即可。而USB设备需要热插拔,因此在hub_probe函数中调用hub_configure函数来配置hub,在这个函数中主要是利用函数usb_alloc_urb函数来分配一个urb,利用usb_fill_int_urb来初始化这个urb结构,包括hub的中断服务程序hub_irq的,查询的周期等。
每当有设备连接到USB接口时,USB总线在查询hub状态信息的时候会触发hub的中断服务程序hub_irq,在该函数中利用kick_khubd将hub结构通过event_list添加到khubd的队列hub_event_list,然后唤醒khubd。进入hub_e vents函数,该函数用来处理khubd事件队列,从khubd的hub_event_list中的每个usb_hub数据结构。该函数中首先判断hub是否出错,然后通过一个for循环来检测每个端口的状态信息。利用usb_port_status获取端口信息,如果发生变化就调用hub_port_connect_change函数来配置端口等。