一、前言
本篇博客仅对ECHI主机控制器驱动的注册部分进行简要叙述,后面再对一些重要的接口进行分析讲解。
二、USB
1、概述
USB(Universal Serial Bus)即“通用外部总线”,在各种场所已经大量使用。它的接口简单(只有5v电源和地、两根数据线D+和D-),可以外接硬盘、键盘、鼠标、打印机等多种设备。
USB总线规范有1.1版和2.0版。USB1.1支持两种传输速率:低速1.5Mbit/s,全速12Mbit/s,对于鼠标、键盘、CD-ROM等设备,这样的速率够了。但是在访问硬盘、摄像机时,还是稍显不足、为此,USB2.0提供了一种更高的传输速率:高速,他可以达到480Mbit/s。USB2.0向下兼容USB1.1,可以将遵循USB1.1规范的设备连接到USB2.0控制器上,也可以把USB2.0的设备连接到USB1.1控制器上。
2、硬件拓扑结构
USB主机控制器(USB Host Controller)通过跟集线器与其他USB设备相连。集线器也属于USB设备,通过它可以在一个USB接口上扩展出多个接口。除根集线器外。可以层叠5个集线器,每条USB电缆的最大长度是5m,所以USB总线的最大距离为30m。一条USB总线上可以外接127个设备,包括根集线器和其他集线器。整个结构图是一个星状结构,一条USB总线上所有设备共享一条通往主机的数据通道,同一时刻只能有一个设备与主机通信。
3、主机控制器
USB主机控制器分为3种:UHCI、OHCI和EHCI。其中HCI表示“Host Controller Interface”。UHCI、OHCI属于USB1.1的主机控制器规范,而EHCI属于USB2.0的主机控制器规范。UHCI(即Universal HCI),它是由Inrel公司制定的标准,它的硬件所做的事情比较少,这使得软件比较复杂。与之相对的是OHCI(即Open HCI),塔友Compaq、Microsoft和National Semiconductor联合制定,在硬件方面它具备更多的智能,使得软件相对简单,因此,我们公司用的就是OHCI。
三、源码分析
USB驱动路径:kernel/driver/usb/。
ECHI驱动文件路径:kernel/driver/usb/host/echi-hcd.c。
虽然对于大型的驱动模块从框架切入能更好的理解代码,但我目前的基础知识还比较薄弱,所以还是依旧从驱动入口看起。
#ifdef CONFIG_USB_EHCI_HCD_PLATFORM
#include "ehci-platform.c"
#define PLATFORM_DRIVER ehci_platform_driver
#endif
static int __init ehci_hcd_init(void)
{
int retval = 0;
if (usb_disabled())
return -ENODEV;
printk(KERN_INFO "%s: " DRIVER_DESC "\n", hcd_name);
//设置usb_hcds_loaded的第USB_EHCI_LOADED位为1
//作用通过下面的打印就能知道:它能让ehci主机控制器驱动先于uhci和ohci
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");
#ifdef PLATFORM_DRIVER
retval = platform_driver_register(&PLATFORM_DRIVER);
if (retval < 0)
goto clean0;
#endif
return retval;
#ifdef PLATFORM_DRIVER
platform_driver_unregister(&PLATFORM_DRIVER);
clean0:
#endif
clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
return retval;
}
static void __exit ehci_hcd_cleanup(void)
{
#ifdef PLATFORM_DRIVER
platform_driver_unregister(&PLATFORM_DRIVER);
#endif
clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
}
module_init(ehci_hcd_init);
module_exit(ehci_hcd_cleanup);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR (DRIVER_AUTHOR);
MODULE_LICENSE ("GPL");
以上代码是我经过删减剩下的。
在echi-hcd的模块入口和出口函数有许多的宏控,而我留下了我目前所学习的平台(sunplus 8368u)相关的代码。
在init函数中做的事情相对较少,就是进行位设置、位检测和平台驱动注册。
位设置和位检测中的USB_*_LOADED被定义在kernel/include/linux/usb/hcd.h,值为0、1、2。
平台驱动注册所用的平台驱动结构体PLATFORM_DRIVER,可以看到上述代码最上方的define,显然,这个结构体在同级目录下的ehci-platform.c文件中。
static const struct platform_device_id ehci_platform_table[] = {
{"ehci-platform", 0},
{}
};
static struct platform_driver ehci_platform_driver = {
.id_table = ehci_platform_table,
.probe = ehci_platform_probe,
.remove = __devexit_p(ehci_platform_remove),
#ifdef CONFIG_PM_WARP
.resume = sp_ehci_fb_resume,
.suspend = sp_ehci_fb_suspend,
#endif
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.owner = THIS_MODULE,
.name = "ehci-platform",
#ifndef CONFIG_PM_WARP
.pm = &ehci_platform_pm_ops,
#endif
}
};
平台设备一般通过.driver.name进行匹配,匹配成功之后则进入probe函数。
static int __devinit ehci_platform_probe(struct platform_device *dev)
{
struct usb_hcd *hcd;
struct resource *res_mem;
int irq;
int err = -ENOMEM;
struct ehci_hcd_sp *ehci_sp;
BUG_ON(!dev->dev.platform_data);
if (usb_disabled())
return -ENODEV;
/* 获取中断号 */
irq = platform_get_irq(dev, 0);
if (irq < 0) {
pr_err("no irq provieded");
return irq;
}
/* 获取平台设备资源 */
res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!res_mem) {
pr_err("no memory recourse provieded");
return -ENXIO;
}
/* 创建和初始化一个hcd结构体 */
//usb_hcd主要描述主机控制器相关信息
//hc_driver结构体类型内主要是控制主机的钩子函数
hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, dev_name(&dev->dev));
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = res_mem->start;
hcd->rsrc_len = resource_size(res_mem);
//从rsrc_start地址开始申请一个rsrc_len长度的内存,用于后面做io映射
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_err("controller already in use");
err = -EBUSY;
goto err_put_hcd;
}
//io映射:映射实际io物理地址到虚拟内存
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs)
goto err_release_region;
tasklet_init(&hcd->host_irq_tasklet, ehci_irq_tasklet, (unsigned long)hcd);
err = usb_add_hcd(hcd, irq, IRQ_TYPE_LEVEL_HIGH);
if (err)
goto err_iounmap;
platform_set_drvdata(dev, hcd);
/****************************************************/
ehci_sp = (struct ehci_hcd_sp *)hcd_to_ehci(hcd);
//创建sys调试节点
if (dev->id < 3) {
device_create_file(&dev->dev, &dev_attr_usb_role_switch);
device_create_file(&dev->dev, &dev_attr_usb_ctrl_reset);
}
device_create_file(&dev->dev, &dev_attr_usb_speed_switch);
device_create_file(&dev->dev, &dev_attr_usb_uphy_swing);
device_create_file(&dev->dev, &dev_attr_usb_disconnect_level);
device_create_file(&dev->dev, &dev_attr_usb_rx_sqct_level);
device_create_file(&dev->dev, &dev_attr_usb_rx_clk_invert);
ehci_sp->flag = 0;
printk("flag ***%p %p %d %d %p\n", hcd, hcd->hcd_priv, sizeof(struct ehci_hcd_sp), hcd->driver->hcd_priv_size, &ehci_sp->flag);
/* ohci reset uphy notifier */
ehci_sp->ehci_notifier.notifier_call = ehci_notifier_call;
usb_register_notify(&ehci_sp->ehci_notifier);
init_waitqueue_head(&hcd->reset_queue);
hcd->ptr_flag = &ehci_sp->flag;
ehci_sp->reset_thread = kthread_create(ehci_reset_thread,
hcd_to_ehci(hcd),
"ehci_reset_wait_event");
if (IS_ERR(ehci_sp->reset_thread)) {
pr_err("Create EHCI(%d) reset thread fail!\n", dev->id);
return err;
}
/* Tell the thread to start working */
wake_up_process(ehci_sp->reset_thread);
return err;
err_iounmap:
iounmap(hcd->regs);
err_release_region:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err_put_hcd:
usb_put_hcd(hcd);
return err;
}
probe函数我们先关注两个结构体,第三行的hcd和31行的ehci_platform_hc_driver。
hcd的类型为“struct usb_hcd *hcd”,这个结构体主要用来描述主机控制器的相关信息,定义位置在kernel/include/linux/usb/hcd.h。
ehci_platform_hc_driver的类型为“struct hc_driver”,这个结构体则是一些控制usb主机控制器的回调函数。
以下是echi_hcd驱动的hc_driver。
static const struct hc_driver ehci_platform_hc_driver = {
.description = hcd_name,
.product_desc = "Generic Platform EHCI Controller",
//私有数据大小
.hcd_priv_size = sizeof(struct ehci_hcd_sp),
//中断处理函数
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
//复位、开始、停止、关闭函数
.reset = ehci_platform_reset,
.start = ehci_run,
.stop = ehci_stop,
.shutdown = ehci_shutdown,
//usb logo test support
.usb_logo_test = ehci_usb_logo_test,
.urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
.endpoint_disable = ehci_endpoint_disable,
.endpoint_reset = ehci_endpoint_reset,
.get_frame_number = ehci_get_frame,
.hub_status_data = ehci_hub_status_data,
.hub_control = ehci_hub_control,
#if defined(CONFIG_PM)
.bus_suspend = ehci_bus_suspend,
.bus_resume = ehci_bus_resume,
#endif
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
.get_port_status_from_register = ehci_get_port_status_from_register,
};