Linux USB总线设备驱动(二)

本文是基于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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值