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盤成功完成。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章