理所当然,我们需要开始分析usb host controller了。
位置linux-2.6.32.21/drivers/usb/host,哇塞,怎么这么多xhci啊,什么ehci,uhci,fhci甚至还真的有xhci,看来搞host controller的人,自已都绝了索性以x来命名了。其实walfred查过资料之后才发现,或许readme其实早就有提示(请看USB子系统学习二中的readme相关部分),这些众多的hci只有ehci和uhci和ohci才是pc上用到的,其他是属于嵌入式系统使用的。那么就算现在我们需要学习ehci和ochi和uhci也不轻松,它们之间是什么关系呢?请别着急,我会娓娓道来。
先看下host controller的作用。见下图,ldd上有一句话说的很好,我决定引用过来“linux内核支持两种主要类型的USB驱动程序:宿主(host)系统上的驱动程序和设备(device)上的驱动程序。从宿主的观点来看(一个普通的USB宿主是一个桌面计算机),宿主系统的USB驱动程序控制插入其中的USB设备,而USB设备的驱动程序则控制该设备如何作为一个USB驱动和主机通信”,这一句话就道出来,我们率先学习宿主(host controller)的重要性。
那么内核驱动文件中driver/usb/host看到的ehci ohci uhci,杂7杂8的,都有什么用,都是干嘛的呢?
1 名字解释
uhci(universal host controller interface): Intel用在自家芯片组上的usb 1.1主控制器(host controller)的硬件实例。
ehci(enhanced host controller interface): usb 2.0的主控制器标准接口。
ohci(open host controller inferface):一个不仅仅是usb用的主控制器接口标准,下面细分为usb,1394,或者更多(别的没有接触过)。主要是遵循csr (configuration space register)标准(另一个标准,呵呵)。是其他厂商在设计usb host controller时遵循的标准,如via, nec, ali, 包括nvidia等等。
2 相关特性
uhci, ohci在硬件实现以及对底层软件访问上都有所不同,但二者又都完全(实际上各自多少都有些不足)支持usb 1.1 specification里边对usb host controller的要求。
同理,ehci是满足usb 2.0 specification里面对usb host controller (high speed)的要求的硬件设计。
应该是从win98之后usb1.1就被广泛支持了,无论是uhci还是ohci。但ms真正支持usb2.0(或者说ehci)是从win2k sp4和winxp sp1。这里所说的真正支持是指系统自带ehci的驱动而不需要第三方的驱动程序。
另外apple现在胳膊拗不过大腿,在mac机上也已经都开始支持usb1.1和2.0接口。而上面的host controller一定是ohci的标准。谨以此献给退居二线的乔老板。
3 他们的规格
uhci ohci ehci他们都是主机控制器的规格
OHCI主要为非PC系统上以及带有SiShe ALi芯片组的PC主板上的USB芯片
UHCI大多为Intel和Via主板上的USB控制器芯片。UHCI的硬件电路比OHCI简单,成本稍低,但驱动复杂。但他们都是由USB1.1规格的。
EHCI是有Intel等几个厂商研发,兼容OHCI UHCI 遵循USB2.0规范。
USB规范都是从寄存器级别规定好的,不过各个厂商可能有自己的几个专用的寄存器。
4 有用的东东
usb 2.0 定义了低速(low speed),全速(full speed),高速(high speed)传输。EHCI 仅仅支持高速传输,所以它必须还要有一个 companion HC,如(UHCI)来支持低速和全速设备,情况时这样的:
1), fs/ls 设备插入到 root hub port,会由 companion HC(uhci/ohci)发现并管理设备;
2),fs/ls 设备插入到 usb 2.0 hub(not root hub),那么由 ehci 通过 split transaction 和 transaction translation(tt)支持 fs/ls 设备。在usb core层会说到这个tt的。
比如,当一个 usb 设备插入 root hub port 时,先要做一件 routing 的事情。所有的 root hub port默认是被 EHCI 占有的,所以,EHCI 和插入的 usb 设备通信,看是不是 hs 设备,如果是好说。如果不是,EHCI 就放弃这个 port 的占有权,让给 companion HC(uhci/ohci)去管理。
既然上一节USB子系统学习之基础篇三(host controller)提到了我们需要擒贼先擒王,先向host controller下手,我们相信跟着本博客走,肯定是不会有错的,不过一旦有问题,也被来找walfred,walfred只是一个喜欢更新OurUnix的博客控而已。顺便提下本博的广告语,OurUnix--一个分享嵌入式,Linux新技术的平台。这年头,广告真是无孔不入呀!汗~~
仍然停在USB HC阶段
这边再补充下,维基上面介绍HCD的一段话:
包含主机控制器和根HUB的硬件为程序员提供了由硬件实现定义的接口主机控制器设备 (HCD)。而实际上它在计算机上就是端口和内存映射。
1.0和1.1的标准有两个竞争的HCD实现。康柏的开放主机控制器接口(OHCI)和Intel的通用主机控制器接口(UHCI)。VIA威盛采纳了UHCI;其他主要的芯片组多使用OHCI。它们的主要区别是UHCI更加依赖软件驱动,因此对CPU要求更高,但是自身的硬件会更廉价。它们的并存导致操作系统开发和硬件厂商都必须在两个方案上开发和测试,从而导致费用上升。因此USB-IF在USB2.0的设计阶段坚持只能有一个实现规范,这就是扩展主机控制器接口(EHCI)。因为EHCI只支持高速传输,所以EHCI控制器包括四个虚拟的全速或者慢速控制器。这里同样是Intel和Via使用虚拟UHCI,其他一般使用OHCI控制器。
某些版本的Windows上,打开设备管理器,如果设备说明中是否有“增强”("Enhanced"),就能够确认它是2.0版的。而在Linux系统中,命令lspci能够列出所有的PCI设备,而USB会分别命名为OHCI、UHCI或者EHCI。
那我们就用电脑试试看撒。
walfred@ubuntu:~/Documents/other/usb$ lspci
00:1d.0 USB Controller: Intel Corporation N10/ICH7 Family USB UHCI Controller #1 (rev 01)
00:1d.1 USB Controller: Intel Corporation N10/ICH 7 Family USB UHCI Controller #2 (rev 01)
00:1d.2 USB Controller: Intel Corporation N10/ICH 7 Family USB UHCI Controller #3 (rev 01)
00:1d.3 USB Controller: Intel Corporation N10/ICH 7 Family USB UHCI Controller #4 (rev 01)
00:1d.7 USB Controller: Intel Corporation N10/ICH 7 Family USB2 EHCI Controller (rev 01)
那现在我们在lsusb看看,是不是应该有4个USB1.1(1.0)接口和1个USB2.0接口。
walfred@ubuntu:~/Documents/other/usb$ lsusb
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
果真,猜测成功!我这可是真正的猜测成功,而不是前两天在上海出现了,类“UFO”的天体,各方专家各显神通的猜测,紫金山派认准即是真UFO,其它门派立即辟谣。各自猜测,却没有任何结论。。。
插播一则报道:
2011年8月23日,中国科学院紫金山天文台研究员王思潮对记者介绍他的判断:“这是一起重大UFO事件,不明飞行物是特殊的空间飞行器,用人类的飞行器很难解释。该飞行器高度是在空间,和杨利伟飞船高度差不多,飞得很慢。”此前有天文专家指出,20日晚多地观测到的UFO是“正处于喷火状态的推进火箭,由于推力不平衡产生如此现象。”
蛋疼,这边讲USB,怎么扯到PCI上面去了,现在心中有疑惑,为什么维基上面会提示我们使用lspci呢,原来我们现在使用的HC都是PCI设备,乖乖,心中恍然大悟,有关PCI暂且不表,因为我暂时也不会,看官现在只要知道USB的HC都是PCI设备即可。
ehci_hcd_init的使命
欢迎程序员回归自己的老本行,跟walfred一起读代码!
位置:kernels/linux-2.6.32.21/drivers/usb/host
walfred@ubuntu:~/work/kernels/linux-2.6.32.21/drivers/usb/host$ vim ehci-hcd.c
static int __init ehci_hcd_init(void)
{
int retval = 0;
if (usb_disabled())
return -ENODEV;
printk(KERN_INFO "%s: " DRIVER_DESC "\n", hcd_name);
set_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) ||
test_bit(USB_OHCI_LOADED, &usb_hcds_loaded))
printk(KERN_WARNING "Warning! ehci_hcd should always be loaded"
" before uhci_hcd and ohci_hcd, not after\n");
pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",
hcd_name,
sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
sizeof(struct ehci_itd), sizeof(struct ehci_sitd));
#ifdef DEBUG
ehci_debug_root = debugfs_create_dir("ehci", usb_debug_root);
if (!ehci_debug_root) {
retval = -ENOENT;
goto err_debug;
}
#endif
#ifdef PLATFORM_DRIVER
retval = platform_driver_register(&PLATFORM_DRIVER);
if (retval < 0)
goto clean0;
#endif
#ifdef PCI_DRIVER
retval = pci_register_driver(&PCI_DRIVER);
if (retval < 0)
goto clean1;
#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
retval = ps3_ehci_driver_register(&PS3_SYSTEM_BUS_DRIVER);
if (retval < 0)
goto clean2;
#endif
#ifdef OF_PLATFORM_DRIVER
retval = of_register_platform_driver(&OF_PLATFORM_DRIVER);
if (retval < 0)
goto clean3;
#endif
return retval;
#ifdef OF_PLATFORM_DRIVER
/* of_unregister_platform_driver(&OF_PLATFORM_DRIVER); */
clean3:
#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
clean2:
#endif
#ifdef PCI_DRIVER
pci_unregister_driver(&PCI_DRIVER);
clean1:
#endif
#ifdef PLATFORM_DRIVER
platform_driver_unregister(&PLATFORM_DRIVER);
clean0:
#endif
#ifdef DEBUG
debugfs_remove(ehci_debug_root);
ehci_debug_root = NULL;
err_debug:
#endif
clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
return retval;
}
module_init(ehci_hcd_init);
从模块的入口函数看,ehci_hcd_init的主要作用就是向各平台注册echi控制器驱动,比如PLATFORM_DRIVER,PCI_DRIVER,PS3_SYSTEM_BUS_DRIVER,OF_PLATFORM_DRIVER等等,这边我们主要看看向pci总线注册驱动的PCI_DRIVER。这边的PCI_DRIVER是一个宏,跟着代码走!
#define PCI_DRIVER ehci_pci_driver
/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver ehci_pci_driver = {
.name = (char *) hcd_name,
.id_table = pci_ids,
.probe = usb_hcd_pci_probe,
.remove = usb_hcd_pci_remove,
.shutdown = usb_hcd_pci_shutdown,
#ifdef CONFIG_PM_SLEEP
.driver = {
.pm = &usb_hcd_pci_pm_ops
},
#endif
};
既然分析的是PCI这一块,我们看看代码怎么走的
#ifdef PCI_DRIVER
retval = pci_register_driver(&PCI_DRIVER);
if (retval < 0)
goto clean1;
#endif
在上面函数中调用了,这么一个函数,用source insight找到其
#define pci_register_driver(driver) \
__pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
看看这个带参宏,调用的是__pci_register_driver,接着找啊找,游阿游
/**
* __pci_register_driver - register a new pci driver
* @drv: the driver structure to register
* @owner: owner module of drv
* @mod_name: module name string
*
* Adds the driver structure to the list of registered drivers.
* Returns a negative value on error, otherwise 0.
* If no error occurred, the driver remains registered even if
* no device was claimed during registration.
*/
int __pci_register_driver(struct pci_driver *drv, struct module *owner,
const char *mod_name)
{
int error;
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &pci_bus_type;
drv->driver.owner = owner;
drv->driver.mod_name = mod_name;
spin_lock_init(&drv->dynids.lock);
INIT_LIST_HEAD(&drv->dynids.list);
/* register with core */
error = driver_register(&drv->driver);
if (error)
goto out;
error = pci_create_newid_file(drv);
if (error)
goto out_newid;
error = pci_create_removeid_file(drv);
if (error)
goto out_removeid;
out:
return error;
out_removeid:
pci_remove_newid_file(drv);
out_newid:
driver_unregister(&drv->driver);
goto out;
}
追寻着一系类的函数,我们终于找到了我们的母亲,error = driver_register(&drv->driver);就这下轻轻一调,我们才把知道了在ehci_hcd_init完成的使命是注册主控驱动。
设备驱动和设备是怎么相遇的呢?
那就顺便分析下 drv->driver.bus = &pci_bus_type;这个蛋疼的玩意吧
struct bus_type pci_bus_type = {
.name = "pci",
.match = pci_bus_match,
.uevent = pci_uevent,
.probe = pci_device_probe,
.remove = pci_device_remove,
.shutdown = pci_device_shutdown,
.dev_attrs = pci_dev_attrs,
.bus_attrs = pci_bus_attrs,
.pm = PCI_PM_OPS_PTR,
};
其中有个bus的专用的,pci_bus_match函数,这是linux设备模型中,bus所独有的,我们也看下
/**
* pci_bus_match - Tell if a PCI device structure has a matching PCI device id structure
* @dev: the PCI device structure to match against
* @drv: the device driver to search for matching PCI device id structures
*
* Used by a driver to check whether a PCI device present in the
* system is in its list of supported devices. Returns the matching
* pci_device_id structure or %NULL if there is no match.
*/
static int pci_bus_match(struct device *dev, struct device_driver *drv)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct pci_driver *pci_drv = to_pci_driver(drv);
const struct pci_device_id *found_id;
found_id = pci_match_device(pci_drv, pci_dev);
if (found_id)
return 1;
return 0;
}
这个函数是条用了你一个比较NB的函数,对,曾经是宏,现在是函数的container_of,这里调用就是能够从设备链表中找到这个pci的dev。
而其中pci_match_device()函数则就起到了媒婆的作用,这个函数做了什么事情呢?怎么让驱动在茫茫设备海中找到这个独一无二的EHCI呢?
事实的真相是这样的:
/**
* pci_match_device - Tell if a PCI device structure has a matching PCI device id structure
* @drv: the PCI driver to match against
* @dev: the PCI device structure to match against
*
* Used by a driver to check whether a PCI device present in the
* system is in its list of supported devices. Returns the matching
* pci_device_id structure or %NULL if there is no match.
*/
static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
struct pci_dev *dev)
{
struct pci_dynid *dynid;
/* Look at the dynamic ids first, before the static ones */
spin_lock(&drv->dynids.lock);
list_for_each_entry(dynid, &drv->dynids.list, node) {
if (pci_match_one_device(&dynid->id, dev)) {
spin_unlock(&drv->dynids.lock);
return &dynid->id;
}
}
spin_unlock(&drv->dynids.lock);
return pci_match_id(drv->id_table, dev);
}
这个函数所用做的就是分别在driver->dynid,driver->id_table这两个列表(由一系列的pci_device_id构成)里面查找,找到则返回这个设备的pci_device_id。(不妨比较一下pci_bus_type->match 和 usb_bus_type->match)
struct pci_device_id {
__u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/
__u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
__u32 class, class_mask; /* (class,subclass,prog-if) triplet */
kernel_ulong_t driver_data; /* Data private to the driver */
};
注意,pci_device_id->driver_data 指向了每个 pci 设备驱动所特有的数据结构,比如 ehci 来说:.driver_data = (unsigned long) &ehci_pci_hc_driver。
而pci_device_probe()的行程呢?
pci_device_probe() ---> __pci_device_probe() ---> pci_call_probe() ---> ( pci_driver->probe() )而ehci_pci_driver->probe = usb_hcd_pci_probe()。
所以说设备模型中总线也有probe函数的目的,是最终调用驱动的probe函数。记不得了总线模型的,可以回看:
所以现在很多电影都是搞倒序,最后再回到一开始的地方,总感觉这些导演上辈子曾是程序员,看代码看多了,那么我们还是回到大导演们曾经看过的代码的开始处吧!
int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct hc_driver *driver;
struct usb_hcd *hcd;
int retval;
if (usb_disabled())
return -ENODEV;
if (!id)
return -EINVAL;
driver = (struct hc_driver *)id->driver_data;
if (!driver)
return -EINVAL;
if (pci_enable_device(dev) < 0)
return -ENODEV;
dev->current_state = PCI_D0;
if (!dev->irq) {
dev_err(&dev->dev,
"Found HC with no IRQ. Check BIOS/PCI %s setup!\n",
pci_name(dev));
retval = -ENODEV;
goto err1;
}
hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev));
if (!hcd) {
retval = -ENOMEM;
goto err1;
}
if (driver->flags & HCD_MEMORY) {
/* EHCI, OHCI */
hcd->rsrc_start = pci_resource_start(dev, 0);
hcd->rsrc_len = pci_resource_len(dev, 0);
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
driver->description)) {
dev_dbg(&dev->dev, "controller already in use\n");
retval = -EBUSY;
goto err2;
}
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
if (hcd->regs == NULL) {
dev_dbg(&dev->dev, "error mapping memory\n");
retval = -EFAULT;
goto err3;
}
} else {
/* UHCI */
int region;
for (region = 0; region < PCI_ROM_RESOURCE; region++) {
if (!(pci_resource_flags(dev, region) &
IORESOURCE_IO))
continue;
hcd->rsrc_start = pci_resource_start(dev, region);
hcd->rsrc_len = pci_resource_len(dev, region);
if (request_region(hcd->rsrc_start, hcd->rsrc_len,
driver->description))
break;
}
if (region == PCI_ROM_RESOURCE) {
dev_dbg(&dev->dev, "no i/o regions available\n");
retval = -EBUSY;
goto err2;
}
}
pci_set_master(dev);
retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
if (retval != 0)
goto err4;
set_hs_companion(dev, hcd);
return retval;
err4:
if (driver->flags & HCD_MEMORY) {
iounmap(hcd->regs);
err3:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
} else
release_region(hcd->rsrc_start, hcd->rsrc_len);
err2:
clear_hs_companion(dev, hcd);
usb_put_hcd(hcd);
err1:
pci_disable_device(dev);
dev_err(&dev->dev, "init %s fail, %d\n", pci_name(dev), retval);
return retval;
}
下一节将重点分析这个probe函数!
上一篇文章,说到了EHCI的使命,本该继续usb_hcd_pci_probe的,但是在讨论usb_hcd_pci_probe之前呢,需要理下未来会在usb_hcd_pci_probe中遇到的结构体。
现看先下面这张数个结构体缠绵的图吧,图像生动,但是截止目前还是不太好理解的,看完图,喝口茶,看看下面的分析对你能不能起点作用。
难缠的结构体,不过C语言就是这样组织的
1.struct pci_driver ehci_pci_driver
还记得在ehci_hcd_init中注册PCI_DRIVER吗,记不得的话记得大明湖畔的PCI_DRIVER也行:
记不得可以翻看下retval = pci_register_driver(&PCI_DRIVER);
该PCI_DRIVER是一个宏
#define PCI_DRIVER ehci_pci_driver
所以我们需要找到ehci_pci_driver
static struct pci_driver ehci_pci_driver = {
.name = (char *) hcd_name,//static const char hcd_name [] = "ehci_hcd";
.id_table = pci_ids,
.probe = usb_hcd_pci_probe,
.remove = usb_hcd_pci_remove,
.shutdown = usb_hcd_pci_shutdown,
#ifdef CONFIG_PM_SLEEP
.driver = {
.pm = &usb_hcd_pci_pm_ops
},
#endif
};
这里面的pci_ids,就会是将来usb_hcd_pci_probe中的第二个参数,我们上一篇中讲过PCI总线的probe最后会调用usb_hcd_pci_probe的,上面的向总线注册ehci_pci_driver,最后还是还回来了。
2.看下pci_ids:
static const struct pci_device_id pci_ids [] = { {
/* handle any USB 2.0 EHCI controller */
PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0),
.driver_data = (unsigned long) &ehci_pci_hc_driver,
},
{ /* end: all zeroes */ }
};
3.static const struct hc_driver ehci_pci_hc_driver
确切的说会用到pci_ids中的ehci_pci_hc_driver,那我们再把ehci_pci_hc_driver提出来看看:
static const struct hc_driver ehci_pci_hc_driver = {
.description = hcd_name,
.product_desc = "EHCI Host Controller",
.hcd_priv_size = sizeof(struct ehci_hcd),
/*
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2,
/*
* basic lifecycle operations
*/
.reset = ehci_pci_setup,
.start = ehci_run,
#ifdef CONFIG_PM
.pci_suspend = ehci_pci_suspend,
.pci_resume = ehci_pci_resume,
#endif
.stop = ehci_stop,
.shutdown = ehci_shutdown,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
.endpoint_disable = ehci_endpoint_disable,
.endpoint_reset = ehci_endpoint_reset,
/*
* scheduling support
*/
.get_frame_number = ehci_get_frame,
/*
* root hub support
*/
.hub_status_data = ehci_hub_status_data,
.hub_control = ehci_hub_control,
.bus_suspend = ehci_bus_suspend,
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
/*-------------------------------------------------------------------------*/
/* PCI driver selection metadata; PCI hotplugging uses this */
static const struct pci_device_id pci_ids [] = { {
/* handle any USB 2.0 EHCI controller */
PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0),
.driver_data = (unsigned long) &ehci_pci_hc_driver,
},
{ /* end: all zeroes */ }
};
可以看到这个结构个头不小,似乎有点意思,看这个我就知道了.product_desc = "EHCI Host Controller",它实际上就是EHCI的驱动,之前提到过EHCI是一个PCI设备,所以它应该包含了PCI用来控制其操作的函数,比如开启(start),关闭(stop),重启(reset),中断的函数,另外还看到了和urb相关的操作,这个urb可是USB通信的核心结构体,暂且不表,以后会详解。
在usb_hcd_pci_probe函数用到结构体基本就是这些了,但是。。。
struct usb_hcd会提意见了,这坨结构体的老大还没有登场昵。这个讲的很好,可以参阅。