Chinaunix首页 | 论坛 | 博客
  • 博客访问: 150871
  • 博文数量: 29
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 177
  • 用 户 组: 普通用户
  • 注册时间: 2014-07-05 23:44
文章分类
文章存档

2016年(1)

2015年(8)

2014年(20)

分类: 嵌入式

2015-04-02 15:43:57

USB驱动的移植教程

By at 2014-11-23 22:37:04, 272次浏览 ? 0人收藏 ?

USB驱动移植教程

一.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),这种规范主要是IntelVia芯片公司提供支

PC主板的;OHCI(Open Host Controller Interface),这种规范是微软提出来的,主要应用在非PC系统上的嵌入式

领域上USB芯片;EHCI(Enhanced Host Controller Interface),这种后来为提高USB速度而提出的规范,它支持

最高速度为480Mbps 

在《S5PV210_UM_REV1.1》手册上搜索OHCI关键词,会发现下面一段话

     这表明S5PV210这款CPU支持一个USB主机接口,同时支持EHCIOHCI这两种规范,支持USB1.1USB2.0规范,支持最高的外设传输速率为480Mbps。注意了,它并不支持USB3.0规范的USB设备,所以做测试的时候,千

万不要拿USB3.0规范的USB设备去测试。

2.1移植ohci-s5p驱动

      打开内核目录:driversusbhost,发现Linux系统提供了大量的主机控制器驱动,找遍所有平台,都没有找到ohci-s5p.c源码。很遗憾,3.8的内核没有提供S5PV210USB HOST控制器驱动程序。最好验证有没有提供的办法就是,

烧写网蜂提供的第二版的uImage进去,然后找个U盘、或者鼠标插入Webee210开发板的USB HOST接口,看看串

口有没有打印什么信息,结果是不会有任何反应的。既然没有提供,这就需要我们自己来编写了,这下不好办了吧?

    不用紧张,仔细再找找,还是能发现一些类似的源码,可供我们移植的。我们发现,内核虽然没有提供ohci-s5p.c源码,但是有提供ehci-s5p.c源码,还有ohci相关的其他平台的源码,比如ohci-s3c2410.cohci-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.husb-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_driverohci_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_initphy_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.0U)插入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连接,由于一时之间找不到数据线,所以无法演示效果,相信经过上面的步骤演示,

 大家应该也知道手机的测试做法也差不多的。

阅读(3099) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~