FL2440——Gadget驱动实现模拟U盘功能

由于项目需要,需要将Fl2440实现模拟U盘功能。这种功能在生活中很常见,比如我们的手机用USB线连接上电脑的时候,电脑会自动识别为U盘,读取手机(Nandflash)里的文件。

Gadget驱动

在做移植之前我们需要先了解一个驱动——gadget驱动

USB驱动分为主机侧驱动(USB host驱动)和设备侧驱动(gadget驱动)。顾名思义,主机侧驱动一般是将开发板作为主机,可以外接USB设备如U盘,鼠标,键盘等外设。其接口通常为USB typeA接口。同样,设备侧驱动就是将开发板当做一个外设使用,比如我们即将要做的将开发板模拟成为一个U盘,可以在PC上读写开发板 Nandflash 的文件。其接口一般为USB typeB,miniUSB或microUSB接口。由于USB device驱动在在内核中已经有定义了,所以将USB设备侧驱动叫做gadget驱动。
这里写图片描述

USB gadget驱动的三层

如图,Linux内核中usb设备侧驱动程序分成3个层次:UDC驱动程序、Gadget API和Gadget驱动程序。UDC驱动程序(USB控制器)直接访问硬件,控制USB设备和主机间的底层通信,向上层提供与硬件相关操作的回调函数。Gadget API是UDC驱动程序回调函数的简单包装,这部分程序内核都已经写好。Gadget驱动程序具体控制USB设备功能的实现,使设备表现出“U盘”、“虚拟串口”等特性。理解这部分含义非常重要,对于驱动的调试和查错非常有用。
这里写图片描述

修改内核

对gadget驱动有了一定了解之后,开始移植工作。Linux内核对于各种驱动已经支持得比较完善,所以我们只需要将内核稍作修改并且在 make menuconfig 选项中添加相应的配置选项就可以了。

修改内核源码

1.修改drivers/usb/gadget/file_storage.c


...
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;
     udelay(800); //增加延时800 us
    ……
 }
 ......

 /*修改mod_data初始值为如下*/
 mod_data = {                           
    ……
       .removable           = 1,
       .can_stall           = 0,
     ……
       };

2.在mach-smdk2440.c中添加gadget设备结构体初始化和 USB device上拉电阻控制,从原理图可以看到GPG9引脚控制上拉电阻。
这里写图片描述

/*添加udc头文件支持*/
#include <plat/udc.h>
...
static struct platform_device *smdk2440_devices[] __initdata 
{
  ...
  &s3c_device_usbgadget,/*Add usb gadget by liwannneg*/
  ...
}
...
/*设置上拉引脚为GPG9*/
static struct s3c2410_udc_mach_info s3c_udc_cfg __initdata = {
    .pullup_pin = S3C2410_GPG(9),
};

static void __init smdk2440_machine_init(void)
{
  s3c24xx_fb_set_platdata(&smdk2440_fb_info);
  s3c_i2c0_set_platdata(NULL);
  /* modify usb gad my liwanneng 2017-4-30 15:40:08 */
  s3c24xx_udc_set_platdata(&s3c_udc_cfg);/*添加上拉电阻控制*/
  platform_add_devices(smdk2440_devices,ARRAY_SIZE(smdk2440_devices));
  smdk_machine_init();
}

3.配置 make menuconfig 选项

因为加载file-backed驱动的时候,要添加镜像文件,所以这里我们要选择模块编译

 Device Drivers  --->   
      [*] USB support  --->
           <*>   USB Gadget Support  --->    
                 [*]       S3C2410 udc debug messages(打印内核调试信息)
                 <M>   USB Gadget Drivers (必须选)
                 <M>     Gadget Zero (DEVELOPMENT) 
                 < >     Audio Gadget (EXPERIMENTAL) 
                 < >     Ethernet Gadget (with CDC Ethernet support)
                 < >     Network Control Model (NCM) support
                 < >     Gadget Filesystem (EXPERIMENTAL) 
                 < >     Function Filesystem (EXPERIMENTAL)
                 <M>     File-backed Storage Gadget(必须选)
                 [ ]       File-backed Storage Gadget testing version
                 < >     Mass Storage Gadget
                 < >     Serial Gadget (with CDC ACM and CDC OBEX support)

编译测试

完成以上修改之后重新编译内核,将在driver/usb/gadget目录下生成g_file_storage.ko模块文件,将其和内核烧录到开发板,测试运行。


  1. 制作FAT32文件系统镜像,可以直接在开发板上执行,亦可在虚拟机创建镜像文件后下载到开发板

dd if=/dev/zero of=udisk32M.img bs=1k count = 32768
mkfs.vfat udisk32M.img
这里写图片描述

2 . 加载g_file_storage.ko驱动,与镜像文件建立关联,挂载loop设备
我们将其挂载到media目录下,如果没有该目录则新建一个media目录

insmod g_file_storage.ko file=udisk32M.img stall=0 removable=1
mount -o loop /udisk32M.img /media/

这里写图片描述

完成上述操作在,现在我们用USB线将开发板接上PC,在开发板上会有打印信息:

g_file_storage gadget: full speed config #1

在windows的资源管理器中可以看到有一个大小为32M的U盘
这里写图片描述

  • 测试U盘是否正常工作

    1. 开发板往模拟U盘写文件,windows系统上可以访问该文件;
      在开发板上往 /media目录创建test 1.txt 文件,重新拔插USB线之后,可以在windows上看到新文件
      这里写图片描述

    2.windows系统往模拟U盘写文件,开发板可以访问该文件;
    在windows系统上往U盘写test2.txt文件,重新挂载文件系统映像到 /media目录
    这里写图片描述
    到此 我们的模拟U盘已经制作完成,现在可以将FL2440的Nandflash当做U盘使用。

遇到的问题及解决

下面来分享分享做gadget驱动的『艰难历程』

问题1.在使用命令mount -o loop /udisk32M.img /media 挂载loop设备的时候提示挂载不成功。
分析:在开发板挂载loop设备失败,于是在虚拟机上尝试着挂载试试,惊讶的发现在虚拟机能够挂载成功。上网了解了一下loop设备。下面摘自某网友博客的片段。

在类Unix系统中,/dev/loop(或称vnd (vnode disk)、lofi(循环文件接口))是一种伪设备,这种设备使得文件可以如同块设备一般被访问。在使用之前,循环设备必须与现存文件系统上的文件相关联。这种关联将提供给用户一个应用程序接口,接口将允许文件视为块特殊文件(参见设备文件系统)使用。因此,如果文件中包含一个完整的文件系统,那么这个文件就能如同磁盘设备一般被挂载。

上面提到的文件就是前面在开发板上制作的FAT32文件系统映像udisk32M.img,我们将该镜像文件与loop伪设备关联,从而使得该镜像文件能够像磁盘一样被访问。
根据这一点,我到开发板的dev目录下去查看没有发现有loop设备(虚拟机的dev目录有loop0设备),可以知道是我的内核没有添加支持loop设备。
解决办法:在内核的make menconfig中添加loop支持

     Device Drivers  --->   
          [*] Block devices  --->   
               <*>   Loopback device support  

内核添加loop支持,重新编译加载内核,在开发板dev目录下发现有了loop设备,挂载成功,问题得到解决。

问题2.修改完内核,添加相关make menuconfig配置选项之后,连接上USB嵌入式设备和电脑都没有任何反应,即没有识别到USB设备。
分析.通过最开始对于gadget驱动的学习了解到gadget驱动分为三层,与硬件信息建立关系的在最底层udc驱动。在内核启动信息中发现有如下信息,说明gadget驱动加载成功。初步猜测问题出现在底层UDC驱动。

s3c2440-usbgadget s3c2440-usbgadget: S3C2440: increasing FIFO to 128 bytes

linux3.0内核对于FL2440的gadget驱动支持不是很完全。开启USB Gadget功能之后,不能使得主机发现USB硬件。这个问题主要是USB接口的上拉电阻的问题,从上面的原理图可以看到,Fl2440使用GPG9来上拉USB,使得主机集线器发现有USB设备链接从而枚举设备。但是在linux3.0内核中,没有设置GPG9的代码。所以导致不能识别到USB设备。
因此参考网上的代码,在内核中添加了上拉控制的程序。但是问题依然存在,待会儿来分析为什么会依然不能识别。
高能预警:添加以下代码在linux3.0中不能解决该问题,在此仅作分析使用。

 /*  USB device(gadget) UDC support add by liwanneng */
static void s3c_udc_pullup(enum s3c2410_udc_cmd_e cmd)
{
 switch (cmd)
  {
   case S3C2410_UDC_P_ENABLE :
      (S3C2410_GPG(9), S3C2410_GPIO_OUTPUT);
      s3c2410_gpio_setpin(S3C2410_GPG(9), 1);  
      break;
   case S3C2410_UDC_P_DISABLE :
      s3c2410_gpio_cfgpin(S3C2410_GPG(9), S3C2410_GPIO_OUTPUT);
      s3c2410_gpio_setpin(S3C2410_GPG(9), 0);  
      break;
   case S3C2410_UDC_P_RESET :
      break;
   default:
      break;
 }
}
static struct s3c2410_udc_mach_info s3c_udc_cfg __initdata = {
 .udc_command        = s3c_udc_pullup,
};

最初我的上拉程序是上面这样写的


static int s3c2410_udc_set_pullup(struct s3c2410_udc *udc, int is_on)
 {
     dprintk(DEBUG_NORMAL, "%s()\n", __func__);

     if (udc_info && (udc_info->udc_command ||
         gpio_is_valid(udc_info->pullup_pin))) {

         if (is_on)
             s3c2410_udc_enable(udc);
         else {
             if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
                 if (udc->driver && udc->driver->disconnect)
                     udc->driver->disconnect(&udc->gadget);

             }
             s3c2410_udc_disable(udc);
         }
     }
     else
         return -EOPNOTSUPP;
   return 0;
  }

在s3c_udc.c中有上面这样一段代码,可以看到,当udc_info并且udc_info->udc_command有值或者udc_info->pullup_pin有值的情况下,调用s3c2410_udc_enable(udc)使能UDC。 udc_info在代码中被初始化为如下

udc_info = pdev->dev.platform_data

很显然udc_command我已经初始化为s3c_udc_pullup,说明条件成立。但是通调试发现上拉没有使能。通过追踪代码发现问题出现在s3c2410_gpio_cfgpin函数,在调用该函数设置GPG9引脚为输出的时候,并没有申请该GPIO。所以导致上拉控制失败。
解决办法
换一种上拉设置方式,直接设置pullup_pin引脚为GPG9。

/*设置上拉引脚为GPG9*/
static struct s3c2410_udc_mach_info s3c_udc_cfg __initdata = {
    .pullup_pin = S3C2410_GPG(9),
};

更改为上面的程序后,问题得到了解决。USB模拟U盘成功完成。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值