Linux驱动开发之USB驱动深入学习(三)——USB2.0ECHI驱动注册

一、前言

本篇博客仅对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主机控制器(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,
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值