Chinaunix首页 | 论坛 | 博客
  • 博客访问: 179967
  • 博文数量: 32
  • 博客积分: 1910
  • 博客等级: 上尉
  • 技术积分: 495
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-31 21:28
文章存档

2009年(3)

2008年(29)

我的朋友

分类: LINUX

2008-11-30 20:38:22

USB2.0驱动移植

开发环境:

硬件:UP-NETARM2410-S嵌入式实验平台

软件:PC机操作系统REDHAT LINUX 9.0MINICOMARMV4L-UNKNOWN-LI

NUX开发环境

编译器版本arm-linux-gcc 3.4.6

内核版本linux 2.6.18

采用的是usb2.0 主控制芯片为ISP1160/01

一、加载驱动支持:

linux2.6.12以上的版本 已经加入了ISP116X芯片的支持我们可以在kernelmenuconfig中添加该驱动:

这里我为了方便调试采用的 是模块加载的方式.用户可根据自己的实际需要来进行设置!

二、驱动特点

下面介绍该驱动都支持那些功能:

       该驱动经过了一系列的软硬件测试支持USB 闪存、鼠标、键盘、网卡等设备;

       支持挂起续传等操作,支持唤醒功能(例如:移动鼠标等)

       经过大小端模式设置

       可编程I/O寻址模式(不支持DMA模式)

       不支持同步传输模式,例如:USB的视频、音频设备就不支持,还在对此驱动做改进!

 

三、注册platform 设备驱动

 

1、进入arch/arm/mach-s3c2410/mach-smdk2410.c文件中注册该设备:

添加如下内容:


/* I use bank 3 and EINT0 for isp116x */

#define USB_IO_PHYS 0x18000000
/* Platform delay */
static void isp116x_pfm_delay(struct device *dev, int delay)
{
 /* On this platform, we work with 200MHz clock, giving
 5 ns per instruction. The cycle below involves 2
 instructions and we lose 2 more instruction times due
 to pipeline flush at jump. I.e., we consume 20 ns
 per cycle.
 */
 int cyc = delay / 20;
 __asm__ volatile ("0:\n"
 "     subs  %0, %1, #1\n"
 "     bge   0b\n"
 :"=r" (cyc)
 :"0"(cyc)
 );
}
/* Define chip configuration */
static struct isp116x_platform_data isp116x_pfm_data = {
 .sel15Kres = 1,
 .remote_wakeup_enable = 1,
 .no_power_switching = 1,
// .reset = isp116x_pfm_reset,
 .delay = isp116x_pfm_delay,
};
/* Define chip address and IRQ line */
static struct resource isp116x_pfm_resources[] = {
 [0] = {   /// data (A0 = 0)
  .start = USB_IO_PHYS,
  .end = USB_IO_PHYS + 1,
  .flags = IORESOURCE_MEM,
 },
 [1] = {   /// addr (A0 = 1)
  .start = USB_IO_PHYS + 2,
  .end = USB_IO_PHYS + 3,
  .flags = IORESOURCE_MEM,
 },
 [2] = {
  .start = IRQ_EINT0,
  .end = IRQ_EINT0,
  .flags = IORESOURCE_IRQ,
 },
};
static struct platform_device isp116x_pfm_usbhost_device = {
 .name = "isp116x-hcd",
 .num_resources = ARRAY_SIZE(isp116x_pfm_resources),
 .resource = isp116x_pfm_resources,
 .dev.platform_data = &isp116x_pfm_data,
};
//add end

2、然后在结构体中添加:&s3c_device_export_usb

////////////////////////////////////////////////////
static struct platform_device *smdk2410_devices[] __initdata = {
    ...

    ...
    &s3c_device_export_usb, //add by lyj 20070911

};

 

3、这里要说明几点:

1)、我是用的是s3c2410bank3地址空间,在isp116x_pfm_resources【】结构体中注册时使用的是实际的物理地址;

2)、注册该设备时,首先要定义可用于访问的寄存器地址.中断号等资源(resource)。然后将这些资源(resource) 作为 platform dev .通过platform_add_devices函数将你定义的paltform_device变量注册到系统的dev里面.。或者你可以象我这样将你需要的驱动添加:

static struct platform_device *smdk2410_devices[] __initdata = {
    ...    ...
    &s3c_device_export_usb, //add by lyj 20070911
};

3)、这里有条关键的语句:

.name = "isp116x-hcd",

它和驱动程序中的&s3c_device_sdi,里的devicename是一致的(drivers/usb/host/isp116x-hcd.c)

static struct platform_driver isp116x_driver = {
        .probe = isp116x_probe,
        .remove = isp116x_remove,
        .suspend = isp116x_suspend,
        .resume = isp116x_resume,
        .driver = {
                   .name = "isp116x-hcd",

                   },
};

 

4)、我们继续讨论该驱动是如何加载的:

在驱动isp116x-hcd.c中:

static int __init isp116x_init(void)
{
        if (usb_disabled())
                return -ENODEV;
 
        INFO("driver %s, %s\n", hcd_name, DRIVER_VERSION);
        return platform_driver_register(&isp116x_driver);
}


platform_driver_register向系统注册这个驱动程序.而这个函数会在isp116x_driver的信息里提取name为搜索内容,搜索系统注册的device中有没有这个platform_device 如果有注册,那么接着会执行platform_driver probe函数.isp116x_probe函数

probe函数里,用的最多和刚才platform_device有关的语句就是platform_get_resource,这条语句用于获取platform_device里的resource资料.例如映射的IO地址,中断等。

 

四、加载驱动并调试

加载之后重新编译内核,并编译模块isp116x-hcd.ko,成功之后重新烧写内核并下载驱动

加载驱动后提示信息如下:

[/mnt/yaffs/usb2.0]insmod isp116x-hcd.ko
Using isp116x-hcd.ko
116x: driver isp116x-hcd, 03 Nov 2005
isp116x-hcd isp116x-hcd: ISP116x Host Controller
isp116x-hcd isp116x-hcd: new USB bus registered, assigned bus number 2
isp116x-hcd isp116x-hcd: irq 16, io base 0x18000002
usb usb2: Product: ISP116x Host Controller
usb usb2: Manufacturer: Linux 2.6.18 isp116x-hcd
usb usb2: SerialNumber: isp116x-hcd
usb usb2: configuration #1 chosen from 1 choice
hub 2-0:1.0: USB hub found
hub 2-0:1.0: 2 ports detected

插上USB摄像提示信息:

[/mnt/yaffs/usb2.0]usb 2-2: new full speed USB device using isp116x-hcd and address 2
usb 2-2: Product: PC Camera
usb 2-2: Manufacturer: Vimicro Corp.
usb 2-2: configuration #1 chosen from 1 choice
 
经过USB2.0存储盘测试,读写速度明显要快!

 

不过该驱动有几个问题需要大家明白:

1、不支持ISO(同步传输),如果您的需求要求支持ISO那么你需要改写驱动,加入ISO功能支持,或者使用isp176x芯片,目前该芯片的驱动支持ISO功能。

2、这里摘录了原文:

Known bugs

Kernel 2.6.13 - There is a one, related to power switching of ports. Apply the following to fix it. Also, this patch converts the driver to a new platform code interface, reducing the amount of the platform support code needed for the driver. If you'll use the unpatched driver from 2.6.13 then see a about setting up its platform support code.

该驱动有一个关于电源管理的漏洞,可以通过下载补丁来解决:

 

 

参考文档:

http://www.mail-archive.com/linux-usb-devel@lists.sourceforge.net/msg44127.html

附录:

关于platform_device相关问题:

首先你需要为SOC的各个功能部分定义他的一些资源.例如可用于访问的寄存器地址.中断号,DMA什么的。然后将这些资源(resource) 作为 platform dev .通过platform_add_devices函数将你定义的paltform_device变量注册到系统的dev里面.。或者你可以象我这样将你需要的驱动添加:
static struct platform_device *smdk2410_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_bl,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
&s3c_device_sdi,
&s3c_device_adc,
&s3c_device_nand,
&s3c_device_usbgadget,
&s3c_device_ts,
&s3c_device_buttons,
&s3c_device_rtc,
&s3c_device_spi0,
&s3c_device_timer1,//add by cefanty for battery charging
};
这样你的硬件的信息和资源就会注册到系统中.

说了半天,这回该说这有什么用了。
你编写的驱动或者移植别人的驱动,一般在驱动里有这样的代码,例如:
static struct platform_driver s3c2410sdi_driver =
{
.probe          = s3c2410sdi_probe,
.remove         = s3c2410sdi_remove,
.suspend= s3c2410mci_suspend,
.resume= s3c2410mci_resume,
.driver={
.name= "s3c2410-sdi",
.bus    = &platform_bus_type,
.owner= THIS_MODULE,
},
};

看到 .name= "s3c2410-sdi",这条关键的语句没有??,它和我在上面注册的&s3c_device_sdi,里的device的名称是一致的.我这里展开我的s3c_device_sdi,的内容
:
/* SDI */

static struct resource s3c_sdi_resource[] = {
[0] = {
.start = S3C2410_PA_SDI,
.end   = S3C2410_PA_SDI + S3C24XX_SZ_SDI - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SDI,
.end   = IRQ_SDI,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = 3,
.end  = 3,
.flags = IORESOURCE_DMA,
}
};

struct platform_device s3c_device_sdi = {
.name  = "s3c2410-sdi",
.id  = -1,
.num_resources  = ARRAY_SIZE(s3c_sdi_resource),
.resource  = s3c_sdi_resource,
};

在驱动程序里的init代码大致如下:
static int __init s3c2410sdi_init(void)
{
return platform_driver_register(&s3c2410sdi_driver);
}
platform_driver_register向系统注册这个驱动程序.而这个函数会在s3c2410sdi_driver的信息里提取name为搜索内容,搜索系统注册的device中有没有这个platform_device。 如果有注册,那么接着会执行platform_driver probe函数.在这里显然是s3c2410sdi_probe函数  probe函数里,用的最多和刚才platform_device有关的语句是platform_get_resource,这条语句用于获取platform_device里的resource资料.例如映射的IO地址,中断等.剩下等得就是ioremap, request_irq等的事情了

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