歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Mini2440 USB gadget --使用與測試

Mini2440 USB gadget --使用與測試

日期:2017/3/1 10:23:17   编辑:Linux編程
USB Gadget驅動又稱USB器件驅動。主要用於運行linux的嵌入式系統中,使得系統擁有普通USB設備的功能。mini2440具有USB1.1設備控制器,所以可以使用USB Gadget功能。但是linux2.6.32.2內核對於mini2440的支持不是很完全。開啟USB Gadget功能之後,不能使得主機發現USB硬件。這個問題主要是USB接口的上拉電阻的問題,mini2440使用GPC5來上拉USB,使得主機集線器發現有USB設備鏈接從而枚舉設備。但是在linux2.6.32.2內核中,沒有設置GPC5的代碼。所以導致不能使用Gadget功能。解決辦法網上也有一些,就是增加額外的模塊置位GPC5,但是我認為這樣不是最好的辦法。認真分析s3c2410_udc.c以及g_zero.c的代碼後,發現在注冊Gadget功能驅動的時候會調用s3c2410_udc.c提供的usb_gadget_register_driver函數,而這個函數最後會調用s3c2410_udc_enable。這個函數就是使能UDC的。代碼如下:
  1. static void s3c2410_udc_enable(struct s3c2410_udc *dev)
  2. {
  3. int i;
  4. dprintk(DEBUG_NORMAL, "s3c2410_udc_enable called\n");
  5. /* dev->gadget.speed = USB_SPEED_UNKNOWN; */
  6. dev->gadget.speed = USB_SPEED_FULL;
  7. /* Set MAXP for all endpoints */
  8. for (i = 0; i < S3C2410_ENDPOINTS; i++) {
  9. udc_write(i, S3C2410_UDC_INDEX_REG);
  10. udc_write((dev->ep[i].ep.maxpacket & 0x7ff) >> 3,
  11. S3C2410_UDC_MAXP_REG);
  12. }
  13. /* Set default power state */
  14. udc_write(DEFAULT_POWER_STATE, S3C2410_UDC_PWR_REG);
  15. /* Enable reset and suspend interrupt interrupts */
  16. udc_write(S3C2410_UDC_USBINT_RESET | S3C2410_UDC_USBINT_SUSPEND,
  17. S3C2410_UDC_USB_INT_EN_REG);
  18. /* Enable ep0 interrupt */
  19. udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_EN_REG);
  20. /* time to say "hello, world" */
  21. if (udc_info && udc_info->udc_command) {
  22. udc_info->udc_command(S3C2410_UDC_P_ENABLE);
  23. }
  24. }
我們發現這個函數除了前面使能中斷的操作後,最後有個判斷語句,判斷udc_info以及udc_info->command是否有值。然後調用udc_command,這個函數的調用參數為S3C2410_UDC_P_ENABLE。很顯然這個就是使能UDC的關鍵操作。我們看一下udc_info又是什麼,在s3c2410_udc.c的最開始有這樣的定義:

static struct s3c2410_udc_mach_info *udc_info;

說明這是一個指向s3c2410_udc_mach_info結構的指針。s3c2410_udc_mach_info結構在udc.h中定義:

  1. struct s3c2410_udc_mach_info {
  2. void (*udc_command)(enum s3c2410_udc_cmd_e);
  3. void (*vbus_draw)(unsigned int ma);
  4. unsigned int vbus_pin;
  5. unsigned char vbus_pin_inverted;
  6. };
那麼這個指針又是什麼時候賦值的,是在s3c2410_udc_probe函數中。這就說明在注冊s3c2410_udc驅動的時候,由platform總線找到相應的設備匹配後,調用的。如下:
udc_info = pdev->dev.platform_data;
那麼什麼又是platfom_data呢,這個又是在什麼時候賦值的呢。要理解這個還得需要平台驅動的只是,也就是platform driver的知識。s3c2410的udc驅動是一個platform驅動,所以USB設備控制器是platform device。那麼這個platform_data又是在哪賦的值。一般而言platform device在系統板級初始化的時候初始化的。也就是板級初始化的時候賦值。但是用Kscope怎麼也找不到給他賦值的語句。說明根本就沒人給他賦值。所以在注冊g_zero功能驅動的時候udc_info是空的,沒有執行udc_info->udc_command()。我們要做的就是給usb gadget platform device的platform_data初始化。在mach-mini2440.c中增加如下代碼:
  1. static void s3c2410_udc_pullup(enum s3c2410_udc_cmd_e cmd)
  2. {
  3. switch (cmd) {
  4. case S3C2410_UDC_P_ENABLE :
  5. s3c2410_gpio_setpin(S3C2410_GPC(5), 1);
  6. break;
  7. case S3C2410_UDC_P_DISABLE :
  8. s3c2410_gpio_setpin(S3C2410_GPC(5), 0);
  9. break;
  10. case S3C2410_UDC_P_RESET :
  11. break;
  12. default:
  13. break;
  14. }
  15. }
這個函數就是udc_info->udc_command()執行的函數,在這裡使得GPC5為高電平,使能USB設備。
  1. static struct s3c2410_udc_mach_info s3c2410_udc_cfg __initdata = {
  2. .udc_command = s3c2410_udc_pullup,
  3. };
這個結構體定義了platform_data的初始值。

修改mini2440_machine_init函數,增加s3c24xx_udc_set_platdata(&s3c2410_udc_cfg);如下

  1. static void __init mini2440_machine_init(void)
  2. {
  3. #if defined (LCD_WIDTH)
  4. s3c24xx_fb_set_platdata(&mini2440_fb_info);
  5. #endif
  6. s3c_i2c0_set_platdata(NULL);
  7. s3c2410_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND);
  8. s3c_device_nand.dev.platform_data = &friendly_arm_nand_info;
  9. s3c_device_sdi.dev.platform_data = &mini2440_mmc_cfg;
  10. s3c24xx_udc_set_platdata(&s3c2410_udc_cfg); //增加的代碼
  11. platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
  12. s3c_pm_init();
  13. }
s3c24xx_udc_set_platdata()這個函數定義與devs.c,如下:
  1. void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd)
  2. {
  3. struct s3c2410_udc_mach_info *npd;
  4. npd = kmalloc(sizeof(*npd), GFP_KERNEL);
  5. if (npd) {
  6. memcpy(npd, pd, sizeof(*npd));
  7. s3c_device_usbgadget.dev.platform_data = npd;
  8. } else {
  9. printk(KERN_ERR "no memory for udc platform data\n");
  10. }
  11. }
最後還要在頭文件中包含plat/udc.h。這樣我們注冊Gadget功能驅動的時候自動使能了USB設備功能。但是在卸載驅動的時候發生了問題,內核打印出一大堆調試信息。問題出在composite.c中的composite_unbind函數中。這個函數開頭有一行代碼WARN_ON(cdev->config);就是如果cdev-config不為0,那麼內核就會打印出調試信息。上面還給了注釋
/* composite_disconnect() must already have been called
* by the underlying peripheral controller driver!
* so there's no i/o concurrency that could affect the
* state protected by cdev->lock.
*/
這個composite_unbind是在卸載udc功能驅動的時候調用的,調用關系如下:usb_composite_unregister 調用 usb_gadget_unregister_driver而usb_gadget_unregister_driver如下定義:
  1. int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
  2. {
  3. struct s3c2410_udc *udc = the_controller;
  4. if (!udc)
  5. return -ENODEV;
  6. if (!driver || driver != udc->driver || !driver->unbind)
  7. return -EINVAL;
  8. dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n",
  9. driver->driver.name);
  10. driver->disconnect(&udc->gadget);
  11. //此處為新加語句,這條語句調用 composite_disconnect,然後使得cdev->config為NULL
  12. driver->unbind(&udc->gadget);
  13. //這裡就是composite_unbind
  14. device_del(&udc->gadget.dev);
  15. udc->driver = NULL;
  16. /* Disable udc */
  17. s3c2410_udc_disable(udc);
  18. return 0;
  19. }
從注釋上可以看出調用composite_unbind的前提是要首先保證composite_disconnect被調用。這樣才不會出現警告的內核信息。在增加上面的代碼後重新編譯內核,將USB Gadget設置稱為模塊。然後make modules 在通過ftp將s3c2410_udc.ko與g_zero.ko傳入開發板,先後加載這兩個模塊。在主機上lsusb會發現出現新設備
Bus 005 Device 023: ID 0525:a4a0 Netchip Technology, Inc. Linux-USB "Gadget Zero"
卸載g_zero.ko後,新設備就會消失。這樣基本的USB Gadget驅動功能就開啟了。類似的還可以測試其他的USB Gagget。
Copyright © Linux教程網 All Rights Reserved