本文是基于mini2440开发板Linux版本号是linux-2.6.32.2的学习笔记
一. usb总线device的注册
USB 总线device的定义如下:
struct platform_device s3c_device_usb =
{
.name = "s3c2410-ohci",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_usb_resource),
.resource = s3c_usb_resource,
.dev = {
.dma_mask = &s3c_device_usb_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
可以看到,文件里面配置的总线的device的名称是"s3c2410-ohci"。
总线有一下几种:
UHCI : intel 定义出来的如何使用这个“主机控制器”。适用于 低速(USB 1.1)或全速(USB2.0)的 USB 设备。当说到 USB2.0 时有“全速”和“高速”。intel 是生产 CPU 的,所以用 UHCI 规范做出来的 USB 主机控制器,硬件功能强大,软件实现稍微简单点。
OHCI :是微软定义。这套规范做出来的主机控制器,硬件稍弱,但软件复杂。
EHCI :支持高速(480Mbps)。 EHCI 专用于高速设备。
看一下USB总线device的硬件资源,硬件资源如下:
static struct resource s3c_usb_resource[] =
{
[0] = {
.start = S3C_PA_USBHOST,
.end = S3C_PA_USBHOST + 0x100 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_USBH,
.end = IRQ_USBH,
.flags = IORESOURCE_IRQ,
}
};
其中寄存器的起始地址是 0x49000000,中断号是:S3C2410_IRQ(26)
总线device调用platform_device_register函数注册到内核。
二. USB总线driver的注册
搜索"s3c2410-ohci", USB总线driver在ohci-s3c2410.c文件中注册。driver的定义如下:
static struct platform_driver ohci_hcd_s3c2410_driver =
{
.probe = ohci_hcd_s3c2410_drv_probe,
.remove = ohci_hcd_s3c2410_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
/*.suspend = ohci_hcd_s3c2410_drv_suspend, */
/*.resume = ohci_hcd_s3c2410_drv_resume, */
.driver = {
.owner = THIS_MODULE,
.name = "s3c2410-ohci",
},
};
但是在本文件中并没有注册ohci_hcd_s3c2410_driver,搜索ohci_hcd_s3c2410_driver,发现在ohci-hcd.c文件中注册了。内核把ohci驱动跟平台有关的函数定义在各个平台文件下,把公共的部分放在ohci-hcd.c文件中。
在这里是根据平台宏,把ohci-s3c2410.c整个文件包含进来。
#if defined(CONFIG_ARCH_S3C2410) || defined(CONFIG_ARCH_S3C64XX)
#include "ohci-s3c2410.c"
#define PLATFORM_DRIVER ohci_hcd_s3c2410_driver
#endif
ohci_hcd_s3c2410_driver被定义成了PLATFORM_DRIVER,在函数ohci_hcd_mod_init中注册,而ohci_hcd_mod_init在内核起来的时候调用。
static int __init ohci_hcd_mod_init(void)
{
retval = platform_driver_register(&PLATFORM_DRIVER);
if (retval < 0)
goto error_platform;
}
三. USB总线driver的probe函数
probe函数是ohci_hcd_s3c2410_drv_probe。
static int usb_hcd_s3c2410_probe (const struct hc_driver *driver, struct platform_device *dev)
{
struct usb_hcd *hcd = NULL;
int retval;
s3c2410_usb_set_power(dev->dev.platform_data, 1, 1);
s3c2410_usb_set_power(dev->dev.platform_data, 2, 1);
hcd = usb_create_hcd(driver, &dev->dev, "s3c24xx");
if (hcd == NULL)
return -ENOMEM;
hcd->rsrc_start = dev->resource[0].start;
hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
dev_err(&dev->dev, "request_mem_region failed\n");
retval = -EBUSY;
goto err_put;
}
clk = clk_get(&dev->dev, "usb-host");
if (IS_ERR(clk)) {
dev_err(&dev->dev, "cannot get usb-host clock\n");
retval = -ENOENT;
goto err_mem;
}
usb_clk = clk_get(&dev->dev, "usb-bus-host");
if (IS_ERR(usb_clk)) {
dev_err(&dev->dev, "cannot get usb-bus-host clock\n");
retval = -ENOENT;
goto err_clk;
}
s3c2410_start_hc(dev, hcd);
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
dev_err(&dev->dev, "ioremap failed\n");
retval = -ENOMEM;
goto err_ioremap;
}
ohci_hcd_init(hcd_to_ohci(hcd));
retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED);
if (retval != 0)
goto err_ioremap;
return 0;
err_ioremap:
s3c2410_stop_hc(dev);
iounmap(hcd->regs);
clk_put(usb_clk);
err_clk:
clk_put(clk);
err_mem:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err_put:
usb_put_hcd(hcd);
return retval;
}
从上可以看到,probe函数主要做了以下事情:
1.调用usb_create_hcd函数创建一个主机控制器驱动。
struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
struct device *dev, const char *bus_name)
{
struct usb_hcd *hcd;
//申请内存空间,并且初始化
hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
if (!hcd) {
dev_dbg (dev, "hcd alloc failed\n");
return NULL;
}
//把hcd赋值给dev->p->driver_data
dev_set_drvdata(dev, hcd);
kref_init(&hcd->kref);
//主机控制器的usb总线初始化
usb_bus_init(&hcd->self);
hcd->self.controller = dev;
hcd->self.bus_name = bus_name;
hcd->self.uses_dma = (dev->dma_mask != NULL);
init_timer(&hcd->rh_timer);
hcd->rh_timer.function = rh_timer_func;
hcd->rh_timer.data = (unsigned long) hcd;
#ifdef CONFIG_PM
INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
#endif
//把主机控制器的控制方法driver赋值给hcd->driver
hcd->driver = driver;
hcd->product_desc = (driver->product_desc) ? driver->product_desc :
"USB Host Controller";
return hcd;
}
2.获取硬件寄存器地址,并映射,把映射后的地址保存在usb_hcd结构体中。
3.获取"usb-host"和"usb-bus-host"时钟。
4.使能"usb-bus-host"和"usb-host"时钟。
5.调用ohci_hcd_init函数。
6.调用usb_add_hcd函数添加主机控制器驱动。usb_add_hcd函数要传入上面的中断号S3C2410_IRQ(26)
总之,就是创建添加一个主机控制器驱动,使能时钟。
四. usb_add_hcd函数
主机控制器驱动需要总线,这个总线需要注册到usb的总线链表,因为USB可能有好几条总线。
主机控制器驱动需要总线的控制方法,控制方法就在hc_driver里面。
主机控制器驱动需要注册一个root hub设备。hub就是用来级连的。但是普通的hub,一头接上级hub, 另一头可以有多个口,多个口就可以级连多个设备。而Root Hub比较特殊,虽然也是一种usb设备,但是它只属于Host Controller,通常芯片会把Host Controller和Root Hub集成在一起。
主机控制器Host Controller和Root Hub之间的关系如下:
int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags)
{
int retval;
struct usb_device *rhdev;
dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
hcd->authorized_default = hcd->wireless? 0 : 1;
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
/* HC is in reset state, but accessible. Now do the one-time init,
* bottom up so that hcds can customize the root hubs before khubd
* starts talking to them. (Note, bus id is assigned early too.)
*/
//初始化一个buffer池,赋值给hcd->pool
if ((retval = hcd_buffer_create(hcd)) != 0) {
dev_dbg(hcd->self.controller, "pool alloc failed\n");
return retval;
}
//申请总线注册usb总线
if ((retval = usb_register_bus(&hcd->self)) < 0)
goto err_register_bus;
//申请一个root hub设备
if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) {
dev_err(hcd->self.controller, "unable to allocate root hub\n");
retval = -ENOMEM;
goto err_allocate_root_hub;
}
//设定root hub的速度
switch (hcd->driver->flags & HCD_MASK) {
case HCD_USB11:
rhdev->speed = USB_SPEED_FULL;
break;
case HCD_USB2:
rhdev->speed = USB_SPEED_HIGH;
break;
case HCD_USB3:
rhdev->speed = USB_SPEED_SUPER;
break;
default:
goto err_allocate_root_hub;
}
hcd->self.root_hub = rhdev;
/* wakeup flag init defaults to "everything works" for root hubs,
* but drivers can override it in reset() if needed, along with
* recording the overall controller's system wakeup capability.
*/
//把root hub的wakeup能力打开
device_init_wakeup(&rhdev->dev, 1);
/* "reset" is misnamed; its role is now one-time init. the controller
* should already have been reset (and boot firmware kicked off etc).
*/
if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {
dev_err(hcd->self.controller, "can't setup\n");
goto err_hcd_driver_setup;
}
/* NOTE: root hub and controller capabilities may not be the same */
if (device_can_wakeup(hcd->self.controller)
&& device_can_wakeup(&hcd->self.root_hub->dev))
dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");
/* enable irqs just before we start the controller */
//如果有中断,申请中断,没有的话把hcd的irq赋值为-1
if (hcd->driver->irq) {
/* IRQF_DISABLED doesn't work as advertised when used together
* with IRQF_SHARED. As usb_hcd_irq() will always disable
* interrupts we can remove it here.
*/
if (irqflags & IRQF_SHARED)
irqflags &= ~IRQF_DISABLED;
snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
hcd->driver->description, hcd->self.busnum);
if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
hcd->irq_descr, hcd)) != 0) {
dev_err(hcd->self.controller,
"request interrupt %d failed\n", irqnum);
goto err_request_irq;
}
hcd->irq = irqnum;
dev_info(hcd->self.controller, "irq %d, %s 0x%08llx\n", irqnum,
(hcd->driver->flags & HCD_MEMORY) ?
"io mem" : "io base",
(unsigned long long)hcd->rsrc_start);
} else {
hcd->irq = -1;
if (hcd->rsrc_start)
dev_info(hcd->self.controller, "%s 0x%08llx\n",
(hcd->driver->flags & HCD_MEMORY) ?
"io mem" : "io base",
(unsigned long long)hcd->rsrc_start);
}
if ((retval = hcd->driver->start(hcd)) < 0) {
dev_err(hcd->self.controller, "startup error %d\n", retval);
goto err_hcd_driver_start;
}
/* starting here, usbcore will pay attention to this root hub */
//给每个端口设置为最多500mA电流,因为hcd->power_budget我们没有设置过
rhdev->bus_mA = min(500u, hcd->power_budget);
//注册root hub设备
if ((retval = register_root_hub(hcd)) != 0)
goto err_register_root_hub;
retval = sysfs_create_group(&rhdev->dev.kobj, &usb_bus_attr_group);
if (retval < 0) {
printk(KERN_ERR "Cannot register USB bus sysfs attributes: %d\n",
retval);
goto error_create_attr_group;
}
//在ohci_run函数中把hcd->uses_new_polling设置为1,在ohci_run函数中也把hcd->poll_rh设置为了1
//这个函数的作用是启动定时器hcd->rh_timer,时间为250ms,定时查询root Hub的状态
if (hcd->uses_new_polling && hcd->poll_rh)
usb_hcd_poll_rh_status(hcd);
return retval;
error_create_attr_group:
mutex_lock(&usb_bus_list_lock);
usb_disconnect(&hcd->self.root_hub);
mutex_unlock(&usb_bus_list_lock);
err_register_root_hub:
hcd->driver->stop(hcd);
err_hcd_driver_start:
if (hcd->irq >= 0)
free_irq(irqnum, hcd);
err_request_irq:
err_hcd_driver_setup:
hcd->self.root_hub = NULL;
usb_put_dev(rhdev);
err_allocate_root_hub:
usb_deregister_bus(&hcd->self);
err_register_bus:
hcd_buffer_destroy(hcd);
return retval;
}
上面这个函数主要做的就是:
- 初始化一个buffer池
- 申请总线注册usb总线
- 给每个端口设置为最多500mA电流
- 注册root hub设备
- 启动定时器hcd->rh_timer,定时查询端口寄存器的状态
文章参考:https://blog.csdn.net/fudan_abc/article/category/325189