分类: LINUX
2008-11-30 20:38:22
USB2.0驱动移植
开发环境:
硬件:UP-NETARM2410-S嵌入式实验平台
软件:PC机操作系统REDHAT LINUX 9.0+MINICOM+ARMV
NUX开发环境
编译器版本:arm-linux-gcc
内核版本:linux
采用的是usb2.0 主控制芯片为ISP1160/01
在linux
这里我为了方便调试采用的 是模块加载的方式
下面介绍该驱动都支持那些功能:
该驱动经过了一系列的软硬件测试支持USB 闪存、鼠标、键盘、网卡等设备;
支持挂起续传等操作,支持唤醒功能(例如:移动鼠标等)
经过大小端模式设置
可编程I/O寻址模式(不支持DMA模式)
不支持同步传输模式,例如:USB的视频、音频设备就不支持,还在对此驱动做改进!
1、进入arch/arm/mach-s
添加如下内容:
/* 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、然后在结构体中添加:&s
////////////////////////////////////////////////////
static struct platform_device *smdk2410_devices[] __initdata = {
...
...
&s
};
3、这里要说明几点:
1)、我是用的是s
2)、注册该设备时,首先要定义可用于访问的寄存器地址.中断号等资源(resource)。然后将这些资源(resource) 作为 platform 的dev .通过platform_add_devices函数将你定义的paltform_device变量注册到系统的dev里面.。或者你可以象我这样将你需要的驱动添加:
static struct platform_device *smdk2410_devices[] __initdata = {
... ...
&s
};
3)、这里有条关键的语句:
.name = "isp116x-hcd",
它和驱动程序中的&s3c_device_sdi,里的device的name是一致的(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
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
该驱动有一个关于电源管理的漏洞,可以通过下载补丁来解决:
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 s
[0] = {
.start = S
.end = S
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SDI,
.end = IRQ_SDI,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = 3,
.end = 3,
.flags = IORESOURCE_DMA,
}
};
struct platform_device s
.name = "s
.id = -1,
.num_resources = ARRAY_SIZE(s
.resource = s
};
在驱动程序里的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等的事情了