分类: 嵌入式
2015-04-02 15:43:57
一.USB驱动框架
在Linux系统中,提供了主机侧和设备侧视角的USB驱动框架,这里,仅仅讲解主机侧角度看到的USB驱动框架。
从主机侧的角度而言,需要编写的USB驱动程序包括主机控制器驱动和设备驱动两类。USB主机控制器驱动程序控
制插入其中的USB设备,而USB设备驱动程序控制该设备如何作为设备与主机通信。在USB主机控制器驱动和USB
设备驱动之间还有一层叫USB核心层。USB核心负责USB驱动管理和协议处理工作,它通过定义一些数据结构、宏
和功能函数,向上为USB设备驱动提供编程接口,向下为USB主机控制器驱动提供编程接口;通过全局变量维护整个
系统的USB设备信息,完成设备热插拔控制、总线数据传输控制等。说了那么多,无图无真相啊~~
Linux USB主机侧驱动总体框架
二.USB驱动移植步骤
1.S5PV210主机控制驱动的移植
USB主机控制器有3种规范,UHCI(Universal Host Controller Interface),这种规范主要是Intel、Via芯片公司提供支
持PC主板的;OHCI(Open Host Controller Interface),这种规范是微软提出来的,主要应用在非PC系统上的嵌入式
领域上的USB芯片;EHCI(Enhanced Host Controller Interface),这种后来为提高USB速度而提出的规范,它支持
最高速度为480Mbps。
在《S5PV210_UM_REV1.1》手册上搜索OHCI关键词,会发现下面一段话
这表明S5PV210这款CPU支持一个USB主机接口,同时支持EHCI和OHCI这两种规范,支持USB1.1和USB2.0规范,支持最高的外设传输速率为480Mbps。注意了,它并不支持USB3.0规范的USB设备,所以做测试的时候,千
万不要拿USB3.0规范的USB设备去测试。
2.1移植ohci-s5p驱动
打开内核目录:driversusbhost,发现Linux系统提供了大量的主机控制器驱动,找遍所有平台,都没有找到ohci-s5p.c源码。很遗憾,3.8的内核没有提供S5PV210的USB HOST控制器驱动程序。最好验证有没有提供的办法就是,
烧写网蜂提供的第二版的uImage进去,然后找个U盘、或者鼠标插入Webee210开发板的USB HOST接口,看看串
口有没有打印什么信息,结果是不会有任何反应的。既然没有提供,这就需要我们自己来编写了,这下不好办了吧?
不用紧张,仔细再找找,还是能发现一些类似的源码,可供我们移植的。我们发现,内核虽然没有提供ohci-s5p.c源码,但是有提供ehci-s5p.c源码,还有ohci相关的其他平台的源码,比如ohci-s3c2410.c、ohci-exynos.c供我们移植参考。
2.1.1 ohci-s5p.c程序
内核既然没有ohci-s5p.c,那我们使用其他平台的ohci源码,这里我们拷贝driversusbhost目录下的ohci-exynos.c
为ohci-s5p.c。然后将所有exynos字符串替换成s5p,由于有些地方是exynos4,所以还需要将s5p4替换为s5p。最后
还需要修改一下头文件,将
#include
改为:
#include
2.2 usb-ohci-s5p.h程序
打开内核目录includelinuxplatform_data,然后拷贝usb-exynos.h为usb-ohci-s5p.h。将所有的exynos4字符串
替换为s5p,将EXYNOS替换为S5P。
最后添加平台数据:
static struct s5p_ohci_platdata s5p_ohci_platdata;
为了以后支持EHCI还添加echi的平台数据,最后usb-ohci-s5p.h修改为:
#ifndef __MACH_S5P_OHCI_H #define __MACH_S5P_OHCI_H /**************Add by Webee*******************/ #ifdef CONFIG_S5P_DEV_USB_EHCI static struct s5p_ehci_platdata s5p_ehci_platdata; #endif static struct s5p_ohci_platdata s5p_ohci_platdata; /**************Add by Webee*******************/ struct s5p_ohci_platdata { int (*phy_init)(struct platform_device *pdev, int type); int (*phy_exit)(struct platform_device *pdev, int type); }; extern void s5p_ohci_set_platdata(struct s5p_ohci_platdata *pd); #endif /* __MACH_S5P_OHCI_H */
2.3 添加s5p_ohci_driver到ohci_hcd.c
打开driversusbhostohci-hcd.c,在CONFIG_USB_OHCI_EXYNOS前面添加如下代码:
#ifdef CONFIG_USB_OHCI_S5P #include "ohci-s5p.c" #define PLATFORM_DRIVERs5p_ohci_driver #endif
因为S5PV210 USB HOST控制器驱动由driversusbhostohci-hcd.c(支持各种SoC下的主机控制器驱动的通
用部分)和driversusbhostohci-s5p.c共同完成。
2.4 添加平台设备
前面我们移植ohci-s5p.c主要是围绕platform_driver来编程的,这又回到了平台驱动设备模型了。还记得
我们移植gpio-key驱动了吗?里面就添加了平台设备来支持平台驱动。今天,我们同样需要添加平台设备
来支持s5p_ohci_driver这个平台驱动。怎么添加呢?参考别人怎么写!
打开archarmplat-samsungdevs.c,找到s5p_device_ehci这个平台设备,模仿它来修改。
打开archarmmach-s5pv210mach-smdkv210.c,在smdkv210_devices[ ]前,添加如下代码:
#include#include #include #include #include #include static struct resource s5p_ohci_resource[] = { [0] = DEFINE_RES_MEM(0xEC300000, SZ_256), [1] = DEFINE_RES_IRQ(S5P_IRQ_VIC1(23)), }; static u64 samsung_device_dma_mask = DMA_BIT_MASK(32); struct platform_device s5p_device_ohci = { .name = "s5p-ohci", .id = -1, .num_resources = ARRAY_SIZE(s5p_ohci_resource), .resource = s5p_ohci_resource, .dev = { .dma_mask = &samsung_device_dma_mask, .coherent_dma_mask = DMA_BIT_MASK(32), } }; void __init s5p_ohci_set_platdata(struct s5p_ohci_platdata *pd) { struct s5p_ohci_platdata *npd; npd = s3c_set_platdata(pd, sizeof(struct s5p_ohci_platdata), &s5p_device_ohci); if (!npd->phy_init) npd->phy_init = s5p_usb_phy_init; if (!npd->phy_exit) npd->phy_exit = s5p_usb_phy_exit; }
怎么确定s5p_ohci_resource里面的内存地址呢?这自然要回到《S5PV210_UM_REV1.1》手册了,在USB
HOST
这章的寄存器介绍里面有这么一段描述:
而IRQ的确定,则是找到下面这段话。
/ * 参考archarmmach-s5pc100includemachirqs.h */
#define IRQ_UHOSTS5P_IRQ_VIC1(23)
然后将定义设置好的s5p_device_ohci添加到smdkv210_devices[],如:
static struct platform_device *smdkv210_devices[] __initdata = { &s5p_device_ohci,/* Add by Webee */ …… &webee210_button_device,/* Add by Webee */ };
最后,在smdkv210_machine_init函数中添加平台数据的设置函数。
#ifdef CONFIG_S5P_DEV_USB_OHCI s5p_ohci_set_platdata(&s5p_ohci_platdata); #endif
2.5 修改Kconfig
2.5.1 移植driversusbhost目录下的Kconfig
打开driversusbhost目录下的Kconfig,在USB_OHCI_EXYNOS前面添加USB_OHCI_S5P的配置支持。
修改后如下:
# Add by Webee config USB_OHCI_S5P boolean "OHCI support for Samsung S5P SoC Series" depends on USB_OHCI_HCD && PLAT_S5P select S5P_DEV_USB_OHCI help Enable support for the Samsung S5P SOC's on-chip OHCI controller. #Add by Webee
2.5.2 移植archarmplat-samsung目录下的Kconfig
打开archarmplat-samsung目录下的Kconfig,在S5P_DEV_USB_EHCI后面添加S5P_DEV_USB_OHCI的配
置支持,修改后如下:
#Add by Webee config S5P_DEV_USB_OHCI bool help Compile in platform device definition for USB OHCI #Add by Webee
2.5.3 移植driversusb目录下的Kconfig
在内核目录下输入make menuconfig配置内核时,搜索S5P_DEV_USB_OHCI发现如下现象,它表明S5P_DEV_USB_OHCI的配置需要先将PLAT_S5P配置上。
搜索: 先按‘/’
打开driversusb目录下的Kconfig,在USB_ARCH_HAS_OHCI模块下添加如下内容:
default y if PLAT_S5P
2.6 创建setup-usb-phy.c文件
在archarmmach-s5pv210目录下创建setup-usb-phy.c文件,为什么要创建这么一个文件呢?还记得前面在smdkv210_machine_init()函数里添加过s5p_ohci_set_platdata(&s5p_ohci_platdata);这个函数吗?
其中就会去设置s5p_ohci_platdata里的phy_init、phy_exit这两个成员函数。那么就需要实现,s5p_usb
_phy_init函数和s5p_usb_phy_exit函数。最后将setup-usb-phy.c文件添加如下代码:
#include#include #include #include #include #include #include #include #include #include #include #include int s5p_usb_phy_init(struct platform_device *pdev, int type) { int err; struct clk *otg_clk; if (type != S5P_USB_PHY_HOST) return -EINVAL; otg_clk = clk_get(&pdev->dev, "otg"); if (IS_ERR(otg_clk)) { dev_err(&pdev->dev, "Failed to get otg clockn"); return PTR_ERR(otg_clk); } err = clk_enable(otg_clk); if (err) { clk_put(otg_clk); return err; } if (readl(S5PV210_USB_PHY_CON) & (0x1<<1)) { clk_disable(otg_clk); clk_put(otg_clk); return 0; } __raw_writel(__raw_readl(S5PV210_USB_PHY_CON) | (0x1<<1), S5PV210_USB_PHY_CON); __raw_writel((__raw_readl(S3C_PHYPWR) & ~(0x1<<7) & ~(0x1<<6)) | (0x1<<8) | (0x1<<5) | (0x1<<4), S3C_PHYPWR); __raw_writel((__raw_readl(S3C_PHYCLK) &~(0x1<<7)) | (0x3<<0), S3C_PHYCLK); __raw_writel((__raw_readl(S3C_RSTCON)) | (0x1<<4) | (0x1<<3), S3C_RSTCON); __raw_writel(__raw_readl(S3C_RSTCON) & ~(0x1<<4) & ~(0x1<<3), S3C_RSTCON); /* "at least 10uS" for PHY reset elsewhere, 20 not enough here... */ udelay(50); clk_disable(otg_clk); clk_put(otg_clk); return 0; } int s5p_usb_phy_exit(struct platform_device *pdev, int type) { if (type != S5P_USB_PHY_HOST) return -EINVAL; __raw_writel(__raw_readl(S3C_PHYPWR) | (0x1<<7)|(0x1<<6), S3C_PHYPWR); __raw_writel(__raw_readl(S5PV210_USB_PHY_CON) & ~(1<<1), S5PV210_USB_PHY_CON); return 0; }
主要工作是获取otg时钟,设置USB相关的寄存器。
2.6.1 编写setup-usb-phy.h文件
在include/linux/platform_data目录下创建setup-usb-phy.h文件。必须创建该头文件,才能使得archarm
mach-s5pv210mach-smdkv210.c里面的程序得以生效,否则在编译时会出错的。
具体代码如下:
3. make menuconfig 配置工作
前面做了那么多工作,为谁而做?为配置选项而做的。这是本章节的重头戏。
在内核目录下输入make menuconfig,进入内核配置菜单栏,配置如下:
具体配置如下所示,由于篇幅有限,这里就不一一截图啦,相信根据下面的方法配置能让大家清晰了解整个
配置过程。
Device Drivers------->
SCSI device support-------->
<*>SCSI device support
[ * ]legacy /proc/scsi/support
<*>SCSI disk support
<*>SCSI CDROM support
<*>SCSI generic support
HID support---------------->
<*>Generic HID driver
[*]USB support------------>
<*>Support for Host-side USB
<*>EHCI HCD ( USB 2.0 ) support
[ * ]Improved Transaction Translator scheduling
[ * ]S5P EHCI support
<*>OHCI HCD support
[ * ]OHCI support for Samsung S5P SoC Series
[ * ]Generic OHCI driver for a platform device
<*>Generic EHCI driver for a platform device
<*>USB Mass Storage support
[ * ]USB Mass Storage verbose debug
<*>SanDisk SDDR-09 (and other SmartMedia,including DPCM support )
<*>SanDisk SDDR-o5 SmartMedia support
完成以上所有移植后,就可以编译内核了,在内核目录下使用make uImage命令编译内核。如果编译
过程中出现说获取不到usbhost时钟,那么将ohci-s5p.c里的s5p_ohci_probe()函数里的:
s5p_ohci->clk = devm_clk_get(&pdev->dev, "usbhost");
改为:
s5p_ohci->clk = devm_clk_get(&pdev->dev, "usb-host");
三.S5PV210主机控制器驱动的测试
移植了那么久,终于可以见证下结果了,是不是有点兴奋呢。好啦,废话就不多说了,让我们一起
见证奇迹吧。
将新的uImage烧到webee210开发板,启动内核,如果能够成功启动内核,并且出现下面的信息:
表明前面的移植是成功的。在这里先肯定下自己。
接下来,好戏即将上场。
四.驱动之手动测试
4.1 U盘测试
将U盘(注意,不要插USB3.0的U盘)插入webee210开发板的USB HOST接口,观察打印信息(不同U盘信息可能不完全一致):
插入U盘的瞬间,就输出那么多的信息。从信息里,我们可以知道,webee210开发板已经识别出你插入的是U盘,
并且知道你的U盘的容量有多大等等信息。那我们接下来测试一下U盘能不能读写呗~~先挂接U盘,然后进入mnt
目录
查下U盘的内容
别大惊小怪哦,打?的是由于中文字体无法正常显示的原因。
接下来我们来验证一下U盘能不能被读写。先创建一个temp.txt文件:
然后往temp.txt里写数据:
/* 注意了,使用echo命令,输入的内容不能有空格*/
查看是否写入成功,用命令:
拔掉U盘时,显示:
4.2 驱动测试之鼠标
将鼠标(注意,不要插USB3.0的鼠标)插入webee210开发板的USB HOST接口,观察打印信息(不同鼠标信
息可能不完全一致):
Webee210开发板也能识别出你插入的是鼠标,并帮你找到相应的鼠标驱动并安装上。当然了,接入不同的
鼠标打印的信息可能会有差别。包括后面的hexdump命令输出的信息也有可能不同。
说到测试,怎么测试鼠标设备驱动呢?还记得输入子系统里怎么测试驱动了吗?还记得input_event结构体?
对,鼠标设备驱动除了有与USB相关,还与输入子系统密切相关。这里,我们使用下面命令来测试鼠标。
当你挪动鼠标,往左挪动、往右挪动;往前挪动、往后挪动;向前滚动鼠标中键、向后滚动鼠标中键;
按下左键、按下右键;只要你对鼠标有动作,它都会上报,并将信息打印出来。具体这些数据有什么含义,
自己去研究一下。有意思的是,不同厂家的鼠标,滚动的动作在input_event结构体里的代表的意义有可能
不一样。到这里,USB HOST相关的驱动就移植完了,USB设备也可以正常使用了。
说明:还可以用于手机usb连接,由于一时之间找不到数据线,所以无法演示效果,相信经过上面的步骤演示,
大家应该也知道手机的测试做法也差不多的。