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

2009年(3)

2008年(29)

我的朋友

分类: LINUX

2008-11-30 20:20:51

Linux2.6内核usb gadget驱动移植

 

在写之前恶补一点usb slave端的知识

kernel 2.4版本中,嵌入式USB驱动是在kernel/arch/arm目录下的ep0.c  ep**.c, 在调试USB驱动的时候比较难,主要是如果在中断了加太多的printk, 会影响USB的时序,导致枚举就失败或不停地USB reset

kernel 2.6版本中,USB的驱动改为了gadget接口,在/kernel/drivers/usb/gadget目录下,有一系列的文件,这些文件都是USB的驱动,其中serial.cbulk驱动或CDC ACM驱动, 运行make menuconfig配置好所有的USB功能后,make modules,则在gadget目录下有一些g_serial.ko,  g_enther.ko, g_rnds.ko, g_file_storage.ko, g_gadget.ko. 其中g_serial.ko运行的时候有个参数是use_acm, 若为0则是普通的USB bulk驱动,最好修改serial.c文件,改为不要和tty关联,这时候windows端用USB bulk驱动,在DDK下有源代码,编译一下就可以了。如果不`想写windows端的驱动,可以把use_acm置为1, host端用windows自带的usbser.sys驱动,即USB转串口驱动,有时候在 c:/windows/driver/dllcach目录下,可以用expand把它解出来,具体参考/kernel/document.  windows端需写个inf文件, inf文件里可以把USB转为一个MODEM, 这样Windows就可以拿它来拨号上网了,当然在小机端要和GPRS模组通讯,模组的初始化AT命令可以写在INF文件里。在windows的资源管理中是看不见串口的,只能看到modem (comX), 在超级终端中打不开这个串口,但是在secureCRT中是可以访问这个串口的。如果想虚拟出真正的串口,可以修改INF文件,把它虚拟出真正的串口,想添加MODEM的时候可以选择添加标准MODEM,模组的初始化AT命令可以写在MODEM的属性-->初始化命令中,比如ATD*99***1#

g_file_storage.koU盘的驱动,比如把SD卡当U盘,insmod g_file_storage.ko file=/mnt/mmc luns=1,若有3个分区可以当U盘,则luns=3. 注意busybox中没有mkdosfs来格式化FAT文件系统,需要自己编译修改mkdosfs的源代码。

g_enter.ko是把USB虚拟出一个网卡,在windows端用usblan.sys就可以构成直接对接的网络了。

g_rnds.ko只在XP下有windows端的驱动,windows 2000下要字迹开发,可以到微软的网站上查RNDS.其中可以拿RNDS来调试程序

不论用USB转串口还是USB网卡来配合GDB调试程序,都比tty串口调试快多了

 

1、  配置选项

配置选项相关解释

 

选项配置模块g_file_storage.ko

File-backed Storage Gadget

配置选项,产生模块s3c2410_udc.ko文件

USB Peripheral Controller (S3C2410 USB Device Controller)  --->

(X) S3C2410 USB Device Controller

编译后生成对应的驱动

[root@vm-dev linux-2.6.24.4]# ls drivers/usb/gadget/*.ko

drivers/usb/gadget/g_file_storage.ko  drivers/usb/gadget/s3c2410_udc.ko

[root@vm-dev linux-2.6.24.4]#

2、  编译错误

在编译的时出现关于debugfs的错误,这时注释掉相应的代码就可以了,无关驱动,至于这个debugfs,我也搞不清楚是个什么东西,也没在细研究他,有知道的朋友可以分享一下。

// drivers/usb/gadget/s3c2410_udc.c

static int __init udc_init(void)

{

        int retval;

        ……..

#if 0

//no idea about debugfs

//delete by lyj

        s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL);

        if (IS_ERR(s3c2410_udc_debugfs_root)) {

                printk(KERN_ERR "%s: debugfs dir creation failed %ld\n",

                        gadget_name, PTR_ERR(s3c2410_udc_debugfs_root));

                s3c2410_udc_debugfs_root = NULL;

        }      

#endif

        ………

}

3、  probe函数不能正常执行的问题。网上也有很多仁兄抱怨s3c2410_udc_probe函数没有执行,看来linux2.6还是不那么普及,哈哈!这个是因为还不是很熟悉linux2.6 platform device的调用过程导致的,只需在/arch/arm/mach-s3c2410.c中添加s3c_device_usbgadget的结构体就可以了。

//add end

static struct platform_device *smdk2410_devices[] __initdata = {

        &s3c_device_usb,

              …….

        &s3c_device_usbgadget,

        &s3c_device_ts,

        &s3c_device_ds,

        &s3c_device_gpio,

        //&s3c_device_ad7655,

};

//add by lyj

其中大多数的结构体platform_device定义都在arch/arm/plat-s3c24xx/devs.c文件里,小部分定义在/arch/arm/mach-s3c2410.c文件中,自己在添加新的platform_device的时候,建议添加到mach-s3c2410.c文件中,因为devs.c文件中大部分是自带的,可以直接使用的设备。

// arch/arm/plat-s3c24xx/devs.c

#if 1

/* USB Device (Gadget)*/

 

static struct resource s3c_usbgadget_resource[] = {

        [0] = {

                .start = S3C24XX_PA_USBDEV,

                .end   = S3C24XX_PA_USBDEV + S3C24XX_SZ_USBDEV - 1,

                .flags = IORESOURCE_MEM,

        },

        [1] = {

                .start = IRQ_USBD,

                .end   = IRQ_USBD,

                .flags = IORESOURCE_IRQ,

        }

 

};

#endif

struct platform_device s3c_device_usbgadget = {

        .name             = "s3c2410-usbgadget",

        .id               = -1,

        .num_resources    = ARRAY_SIZE(s3c_usbgadget_resource),

        .resource         = s3c_usbgadget_resource,

};

 

EXPORT_SYMBOL(s3c_device_usbgadget);

4、  时序问题

偶的驱动程序加载之后,没有出现任何的问题,在电脑端也可以看到对应的usb设备显示为U盘设备,但是内容是空的!这个让人很头痛。

       后来在高人的指点下,使用抓包工具(原来只知道调试网络要用抓包工具,原来调试usb设备也要用这玩意儿)。发现在最开是的时候报头是完整的,但是后面的内容不再进行传输。正常的USB设备在挂载时,首先是传递整个USB的存盘信息,然后会有不断的一收一发的交互信息,这个在2410连接的时候也是没有的,也就是在连接显示了u盘之后,就不再有任何的交互信息。

       在高人的启发下,在drivers/usb/gadget/file_storage.c文件中添加了无数的调试信息,终于定位到是usb传输的时序问题。在整个gadget的修改中只改了一行代码:

/* Use this for bulk or interrupt transfers, not ep0 */

static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,

                struct usb_request *req, int *pbusy,

                enum fsg_buffer_state *state)

{

        int     rc;

//      printk("start_transfer\n");

        udelay(1000);

        if (ep == fsg->bulk_in)

 

这是我打的一个补丁:usb_gadget.patch

--- linux-2.6.24.4/drivers/usb/gadget/file_storage.c  2008-03-25 02:49:18.000000000 +0800

+++ drivers/usb/gadget/file_storage.c   2016-11-25 15:35:23.000000000 +0800

@@ -1090,7 +1090,7 @@

 static int ep0_queue(struct fsg_dev *fsg)

 {     

        int     rc;

-

+//     printk("ep0_queue \ n");

        rc = usb_ep_queue(fsg->ep0, fsg->ep0req, GFP_ATOMIC);

        if (rc != 0 && rc != -ESHUTDOWN) {

 

@@ -1098,6 +1098,7 @@

                WARN(fsg, "error in submission: %s --> %d\n",

                                fsg->ep0->name, rc);

        }              

+//     printk("ep0_queue end \ n");

        return rc;     

 }             

       

@@ -1452,7 +1453,7 @@

        struct fsg_dev          *fsg = get_gadget_data(gadget);

        int                     rc;

        int                     w_length = le16_to_cpu(ctrl->wLength);

-              

+//     printk("fsg_steup \n");

        ++fsg->ep0_req_tag;             // Record arrival of a new request

        fsg->ep0req->context = NULL;

        fsg->ep0req->length = 0;

@@ -1489,7 +1490,8 @@

                enum fsg_buffer_state *state)

 {     

        int     rc;

-

+//     printk("start_transfer\n");

+       udelay(1000);

        if (ep == fsg->bulk_in)

                dump_msg(fsg, "bulk-in", req->buf, req->length);

        else if (ep == fsg->intr_in)

@@ -2573,7 +2575,7 @@

                start_transfer(fsg, fsg->intr_in, fsg->intreq,

                                &fsg->intreq_busy, &bh->state);

        }

-

+//     printk("send_status \n");

        fsg->next_buffhd_to_fill = bh->next;

        return 0;

 }

@@ -2961,7 +2963,7 @@

                halt_bulk_in_endpoint(fsg);

                return -EINVAL;

        }

-

+//     printk("received_cbw \n");     

        /* Is the CBW meaningful? */

        if (cbw->Lun >= MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG ||

                        cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) {

@@ -3245,7 +3247,7 @@

                        raise_exception(fsg, FSG_STATE_EXIT);

                }

        }

-

+       printk("handle_exception \n");

        /* Cancel all the pending transfers */

        if (fsg->intreq_busy)

                usb_ep_dequeue(fsg->intr_in, fsg->intreq);

@@ -4031,7 +4033,6 @@

 static void fsg_suspend(struct usb_gadget *gadget)

 {

        struct fsg_dev          *fsg = get_gadget_data(gadget);

-

        DBG(fsg, "suspend\n");

        set_bit(SUSPENDED, &fsg->atomic_bitflags);

 }

5、  关于硬件连接的问题

说明一下VBUS管脚会在usb从口有设备插入式由主口端的电平将它拉高,从而产生插入中断。但是在s3c2410中有一个USBD的中断信号,是由s3c2410芯片产生的,所以这个插入中断并没有起到实际的左右。真正作用的是USBD信号!多说几句其实那么对应USB host的中断也是采用的USBH,都是在数据收发的时候起作用。

       实际上对于s3c2410来讲,usb设备的使用很简单,用的时候,直接就有D+D-信号几乎不需要做任何事情!

参考了一些其他的电路,发现一些不同的地方

这里他把VBUS信号断开,也就是不要插入中断的信号,但是这里的外部中断17还连接到D+上,我一时就搞糊涂了,而且进过验证这个电路不能正常工作,原因在于EINT17的电平直接影响到D+的信号,导致不能正常通信,在实际的测试过程中,连USBD中断信号都搞没了。实在是可恶之极,索性不管他浪费的这个外部中断,把他当作普通的GPIO输出,输出为高电平时,所有的现象又恢复正常,所以说这种连接方法是一种浪费,不过2410的外中断引脚很多,洒洒水啦!

       这里说上几句,在实际的嵌入式编程的过程中会碰到很多的硬件问题,当然这有的时候和硬件工程师的素质有关,但是作为资深的嵌入式工程师,还是要有一定的硬件功底的,如果在软件上是在是行不通,找不到毛病,最好是转头怀疑下硬件。当然有的朋友说,硬件严格测试过了我们才才开始开发的,呵呵,这个在国内还比较少吧!有时候还要考虑是不是器件问题,还要考虑是不是焊接问题,哈哈,想起这些就头大!

6、  加载驱动之后才可以插入设备,如果你是事先插入的USB那么是不会被识别的!因为没有产生所有的插入中断。

7、  如何正确使用

来看一把我是使用方法,呵呵,并不推荐这么使用

2410-S:~/usb_slave #cat liu.sh

#!/bin/sh

#insmod g_file_storage.ko file=/dev/mmc/blk0/part1 stall=0 removable=1

insmod s3c2410_udc.ko

#insmod g_file_storage.ko file=/dev/mtdblock3 stall=0 removable=1

insmod g_file_storage.ko file=/dev/sda1 stall=0 removable=1

8、  后来又想到一种好的方法

cd /tmp
dd if=/dev/zero of=img bs=1k count=12k ;

insmod /usr/g_file_storage.ko file=img

在主设备那边访问直接就与U盘一样

在从设备这边访问

cd /tmp/
rmmod g_file_storage
mkdir udisk
mount –o loop –t vfat img udisk/
mount
ls udisk/

 

9、  另外,2410上挂载的usb设备也是可以被访问的,曾经有端时间自己的sd卡转接口不见了,就是那2410来当作sd卡转接口用的,哈哈

对应的语句就是这个了

insmod g_file_storage.ko file=/dev/sda1 stall=0 removable=1

 

全文完

MSN

于北京

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

chinaunix网友2010-05-14 15:48:50

文章写的很细致,不过我想请教的是“想虚拟出真正的串口”怎么改INF文件。可不可以发表一篇这方面的文章。谢谢!

chinaunix网友2009-03-26 10:41:04

写的不错。。。。呵呵