Chinaunix首页 | 论坛 | 博客
  • 博客访问: 102042
  • 博文数量: 76
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 164
  • 用 户 组: 普通用户
  • 注册时间: 2014-11-25 10:26
个人简介

码农的生涯!

文章分类
文章存档

2018年(1)

2017年(2)

2016年(32)

2015年(40)

2014年(1)

我的朋友

分类: LINUX

2015-12-09 09:37:08

原文链接:http://blog.csdn.net/wuyuwei45/article/details/8926858
Linux kernel2.6以上的版本中,USB设备驱动的接口改为了gadget,在kernel/driver/usb/gadget目录下主要包含了平台USB UDC驱动和gadget接口驱动。

        kernel/driver/usb/gadget目下的serial.c是一个常用的驱动文件,它可以配置为bulk传输驱动或CDC ACM驱动(USB转串口驱动)。配置的方式有两种,一可以在编译前手动更改驱动文件中的变量“use_acm"的默认值,现在默认值为”true“,对 应为CDC ACM驱动,改为”false"后对应为bulk驱动;二可以将驱动编译成模块,然后在insmod时传递一个参数就行了。

       运行make menuconfig看一下:

         将“USB Gadget Support -->"选择编译为模块,方便动态加载驱动。

        进行“USB Gadget Support -->"配置子菜单:

        配置红色方框标注的模块。

      运行命令: make M=driver/usb/gagdet modules

      编译后在driver/usb/gagdet 下生产g_file_storage.ko和g_serial.ko,分别对应为U盘驱动和USB转串口驱动。

      笔者的嵌入式板上有个SD卡,linux驱动后挂载主目录是/dev/mmcblk0,分区目录是/dev/mmcblk0p1,/dev/mmcblk0p2,/dev/mmcblk0p3,有三个分区。

      执行:

[plain] view plaincopy
  1. root@rfODNCC:/mnt# insmod g_file_storage.ko file=/dev/mmcblk0 stall=0 removable=1  
  2. [   69.783477] g_file_storage gadget: No serial-number string provided!  
  3. [   69.798980] g_file_storage gadget: File-backed Storage Gadget, version: 1 September 2010  
  4. [   69.807495] g_file_storage gadget: Number of LUNs=1  
  5. [   69.812622] g_file_storage gadget-lun0: ro=0, nofua=0, file: /dev/mmcblk0  
  6. [   69.819763] musb-hdrc musb-hdrc.0: MUSB HDRC host driver  
  7. [   69.825622] musb-hdrc musb-hdrc.0: new USB bus registered, assigned bus number 2  
  8. [   69.833801] usb usb2: New USB device found, idVendor=1d6b, idProduct=0002  
  9. [   69.840942] usb usb2: New USB device strings: Mfr=3, Product=2, SerialNumber=1  
  10. [   69.848510] usb usb2: Product: MUSB HDRC host driver  
  11. [   69.853668] usb usb2: Manufacturer: Linux 2.6.37-05branch musb-hcd  
  12. [   69.860137] usb usb2: SerialNumber: musb-hdrc.0  
  13. [   69.872558] hub 2-0:1.0: USB hub found  
  14. [   69.876647] hub 2-0:1.0: 1 port detected  
  15. root@rfODNCC:/mnt# [   70.169555] g_file_storage gadget: high speed config #1  

连接USB到PC,可以看到PC端出现SD卡的分区目录。

PS:insmod g_file_storage.ko file=/dev/mmcblk0 stall=0 removable=1 和 insmod g_file_storage.ko file=/dev/mmcblk0有点区别,后续可以查找理解stall 和 removable的具体作用。

接着制作一个嵌入式设备端fat32文件分区,然后挂载到PC上:

1.在主机ubuntu上建立fat32文件映像,大小为2M。

    #dd if=/dev/zero of=fat32.img bs=1k count=2048
    #mkfs.vfat fat32.img

2.向fat32.img中写入一些文件,以用来测试:
    #mkdir fat32
    #sudo mount -t vfat -o loop fat32.img fat32  
    #cd fat32
    #touch hello.txt
    #echo hello,wolrd>hello.txt
    #sync

3.把主机ubuntu上的fat32.img 拷贝到嵌入式设备的根文件系统中。

4.在嵌入式设备中加载g_file_storage驱动

  insmod g_file_storage.ko file=/opt/fat32.img stall=0 removable=1   //我将fat32.img放在/opt下

5.USB线连接设备和PC,弹出发现移动磁盘,打开盘,里面有个hello.txt文件。

6.在PC端打开U盘,增加一个文件系统,如text.txt,然后在设备端通过命令挂载映像,命令为:

mount -t vfat -o loop /opt/fat32.img /media      //挂载到media目录下

到media目录,cd /media

看到/media目下有两个文件hello.txt和test.txt,且内容和在PC端看到的一致。

注意:如果此时在PC端再次修改了文件。这设备端不能立即看到PC修改的结果。即PC和设备端不能同步。

此时可以在设备进行umount /media,然后重新mount一次就可以看到PC更改的结果。同样在设备端修改的文件,PC端也不能立即看到修改结果,需重新插拔USB线才能看到更新。至于原因,暂时不知道。

卸载g_file_storage

执行加载USB转串口命令

[plain] view plaincopy
  1. root@rfODNCC:/mnt# insmod g_serial.ko   
  2. [  239.675933] g_serial gadget: Gadget Serial v2.4  
  3. [  239.680786] g_serial gadget: g_serial ready  
  4. [  239.685241] musb-hdrc musb-hdrc.0: MUSB HDRC host driver  
  5. [  239.691009] musb-hdrc musb-hdrc.0: new USB bus registered, assigned bus number 2  
  6. [  239.707153] usb usb2: New USB device found, idVendor=1d6b, idProduct=0002  
  7. [  239.714324] usb usb2: New USB device strings: Mfr=3, Product=2, SerialNumber=1  
  8. [  239.721893] usb usb2: Product: MUSB HDRC host driver  
  9. [  239.727111] usb usb2: Manufacturer: Linux 2.6.37-05branch musb-hcd  
  10. [  239.733581] usb usb2: SerialNumber: musb-hdrc.0  
  11. [  239.747467] hub 2-0:1.0: USB hub found  
  12. [  239.751434] hub 2-0:1.0: 1 port detected  
  13. root@rfODNCC:/mnt# [  240.044830] g_serial gadget: high speed config #2: CDC ACM config  
PC端安装USB转串口驱动后,可以从PC设备管理器上看到多出了一个串口。

要使得设备端能与PC端通过USB转串口进行通信,设备端还需要手动创建设备文件节点,参考内核文档(Documents/usb/gagdet_serial.txt)中的部分内容:

This will also automatically load the underlying gadget peripheral
controller driver.  This must be done each time you reboot the gadget
side Linux system.  You can add this to the start up scripts, if


desired.


Your system should use mdev (from busybox) or udev to make the
device nodes.  After this gadget driver has been set up you should
then see a /dev/ttyGS0 node:


  # ls -l /dev/ttyGS0 | cat
  crw-rw----    1 root     root     253,   0 May  8 14:10 /dev/ttyGS0
  #


Note that the major number (253, above) is system-specific.  If
you need to create /dev nodes by hand, the right numbers to use
will be in the /sys/class/tty/ttyGS0/dev file.

作者创建设备节点为:先cat /sys/class/tty/ttyGS0/dev,得到主设备号后,mknod /dev/ttyGS0 c major_num 0

然后PC打开一个串口软件,打开对应的串口端口。

设备端执行 echo “hello” > /dev/ttyGS0,PC端串口软件收到相应数据。

PS:UDC(设备控制器)驱动主要是与硬件平台相关的,它会实现gadget功能驱动所需要的接口,一般的UDC仅支持注册一个gadget功能驱动,所以上面是在将g_file_storage驱动卸载后才能重新加载g_serial.ko。否则会出现失败。

看看UDC驱动代码中的注册gadget驱动函数usb_gadget_probe_driver中的部分程序:

[plain] view plaincopy
  1. spin_lock_irqsave(&udc->lock, flags);  
  2. if (udc->driver) {  
  3.     spin_unlock_irqrestore(&udc->lock, flags);  
  4.     return -EBUSY;  
  5. }  
可见第一次注册gadget驱动后udc->driver为true,在不卸载情况下再次注册gadget时出现无法注册的错误。

【以下为转载:http://blog.csdn.net/embededswordman/article/details/6689593】

Linux支持连接各种USB从设备,同时也支持自己作为设备插入到其他主机当中。最典型的例子就是Android OS的手机,插入电脑可以被识别为U盘之类的设备。

对于SOC来说,这部分直接对应了USB Device部分的操作。

为了避免与作为主机时支持的"设备驱动 (USB Device Driver)"一词混淆, Linux给这部分的实现取名为"Gadget",小玩具。内核源码的目录为\drivers\usb\gadget,里面包含了内核所支持的不同类型的 USB Device Controller (UDC)驱动的实现,以及框架和不同gadget的实现。

以AT91 ARM9为例,最底层的驱动为at91_udc.c(对于支持高速USB 2.0的SOC是atmel_usba_udc.c),它实现了gadget.h定义的统一接口,然后上层的各种gadget driver(如serial.c等)调用这一套统一的接口去实现不同类型的功能,如USB串口、U盘、USB以太网等等。

Atmel USB串口的框架图:

USB串口的描述和使用方法在内核目录\Documentation\usb\gadget_serial.txt中有详细且清晰的描述,就不再这里重 复。一旦加载模块后会自动在/dev/下创建设备文件,程序就可以open它并且write,即使USB线没有连接上。之前一直纠结在为什么 g_serial没有向上层提供USB cable connect/disconnect的事件,后来想想在使用串口的时候也是一样的情况,打开一个即使没有连接线的串口,也可以发送数据,只是没有人会收 到而已。不过与串口唯一的不同在于,如果通讯正在进行中拔掉USB线,那么再次连接USB线后需要重新open一次ttyGS0设备文件才可以重新发送, 否则write不能向串口一样正常工作而返回出错。
另外一篇:
来源:http://www.xuebuyuan.com/1128551.html

继Linux Gadget的一点研究后 陆续续关注了很久Android USB Gadget,一直想写点什么记录一些认识,可是Linux USB实在是太复杂,让人有点无从下笔的感觉。它的复杂并不是说不可以被人理解,而且涉及的面很广。USB Host的驱动关注比较少,就对USB device驱动的认识来说,需从两大方面去对它进行认识和了解,一方面是USB协议本身,如果连USB端点、设备描述符、接口、复合设备等概念都不清楚 的话,那是无法理解Linux
USB device驱动的;另外方面就是USB Gadget架构。而本文就是基于对USB Gadget架构的一点认识而记录的。

2.Android USB 

Android设备的USB口实现了OTG的功能,也就是一般Android设备可以充当Host和Device角色,而我们使用得最多的就是 它的Device角色。Android系统开发者更改传统了Linux USB Gadget实现,在传统Linux Gadget架构上实现了一个复合设备:adb和mtp。其中adb就不用多说,玩过Android都懂,mtp就是常见的U盘功能。Android USB Gadget设备驱动就是Linux的USB Gadget设备驱动。

3.USB Gadget的三层架构

一般网上关于介绍USB Gadget的资料都是基于Linux2.6.32或在这之前的版本,作者在关注了Linux2.6.37和Linux3.0.4版本的内核,USB Gadget的一些API已经与Linux2.6.32的不同了。但是那些关键的数据结构还是一样滴。
Linux USB Gadget分三层架构:
层次关系从上到下
一层:USB Gadget功能层。BSP/Driver开发者通常是要实现这一层,从而实现一个具体的设备驱动,如Anddroid在此层实现了adb,mtp,mass_storage等。浏览参考关注此层代码时, 会发现“composite”是此层的关键字,此层中关键的数据结构是:struct  usb_composite_driver。这一层的驱动文件一般为:driver/usb/gadget/android.c(android实现 的)或driver/usb/gadget/serial.c(传统Linux实现的USB转串口)。
二层:USB设备层。这一层是Linux内核开发维护者实现的,与我们没太大关系,不用我们操心,我们只关心其的一些接口就行。浏览参考关注此 层时,会发现“gadget”是此层的关键字,此层的关键数据结构是:usb_gadget_driver,usb_composite_dev。这层主 要的一个驱动文件为:driver/usb/gadget/composite.c
三层:USB设备控制器驱动层。这一层主要是与CPU、CPU USB控制器有关,与硬件紧密相关,这一层也比较头痛,主要它和USB控制器牵扯在一起,涉及有寄存器、时钟、DMA等等。但是这一层往往是由芯片厂商去 实现。我们一般仅需在板级文件中处理好所需要的USB接口即可。这层的关键字就是“UDC”,主要驱动文件命名含“udc”关键字,一般与CPU或芯片厂 商有关,如driver/usb/gadget/xxx_udc.c。

4.USB Gadget的三层架构的关系

可以用一句简单的话去概括三层的关系:USB Gadget功能层调用USB设备层的接口,USB设备层调用USB设备控制器驱动层的接口,然后USB设备控制器驱动层回调USB设备层,USB设备层回调USB Gadget功能层。
【强调本文只是想捋清楚层次的关系而已,对里面的一些函数,甚至USB gadget如果运作、整个驱动如何处理USB协议等等不做太多说明,后面会在其他文章中陆续补充。】

4.1从Android的gadget功能层去看三层架构的关系

浏览driver/usb/gadget/android.c源码。
先看init函数,
static int __init init(void)
{
	struct android_dev *dev;
	int err;

	android_class = class_create(THIS_MODULE, "android_usb");
	if (IS_ERR(android_class))
		return PTR_ERR(android_class);

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev)
		return -ENOMEM;

	dev->functions = supported_functions;
	INIT_LIST_HEAD(&dev->enabled_functions);
	INIT_WORK(&dev->work, android_work);
	mutex_init(&dev->mutex);

	err = android_create_device(dev);
	if (err) {
		class_destroy(android_class);
		kfree(dev);
		return err;
	}

	_android_dev = dev;

	/* Override composite driver functions */
	composite_driver.setup = android_setup;
	composite_driver.disconnect = android_disconnect;

	return usb_composite_probe(&android_usb_driver, android_bind);
}

composite_driver是定义在driver/usb/gadget/composite.c中,

static struct usb_gadget_driver composite_driver = {
	.speed		= USB_SPEED_HIGH,

	.unbind		= composite_unbind,

	.setup		= composite_setup,
	.disconnect	= composite_disconnect,

	.suspend	= composite_suspend,
	.resume		= composite_resume,

	.driver	= {
		.owner		= THIS_MODULE,
	},
};

可见它是一个全局的结构体,android.c中重新实现了它的setup和disconnect方法。然后调用函数 usb_gadget_probe_driver(&android_usb_driver,android_bind);向USB设备层进行探 测和注册。

【PS:2.6.32内核在init函数后面调用usb_composite_register(&android_usb_driver)进行注册,2.6.37内核以后统一改为usb_gadget_probe_driver。】
usb_gadget_probe_driver(&composite_driver, composite_bind)函数定制在driver/usb/gadget/composite.c
extern int usb_composite_probe(struct usb_composite_driver *driver,
			       int (*bind)(struct usb_composite_dev *cdev))
{
	if (!driver || !driver->dev || !bind || composite)
		return -EINVAL;

	if (!driver->iProduct)
		driver->iProduct = driver->name;
	if (!driver->name)
		driver->name = "composite";
	composite_driver.function =  (char *) driver->name;
	composite_driver.driver.name = driver->name;
	composite = driver;
	composite_gadget_bind = bind;

	return usb_gadget_probe_driver(&composite_driver, composite_bind);
}

函数usb_gadget_probe_driver调用usb_gadget_probe_driver相关USB设备控制器驱动层进行注册。

【PS:2.6.32内核在init函数后面调用usb_gadget_register_driver(&composite_driver)进行注册,2.6.37内核以后统一改为usb_gadget_probe_driver(&composite_driver,
composite_bind);】
usb_gadget_probe_driver()函数是每一个USB设备控制器驱动要实现的,这个函数与硬件紧密相关。
至此可以看到三层的关系:USB Gadget功能层调用USB设备层的接口,USB设备层调用USB设备控制器驱动层的接口。

4.2从传统Linux的gadget功能层去看三层架构的关系

发现打字好累,分析传统Linux的gadget功能层代码(如:driver/usb/gadget/serial.c)发现其实和android一样,所以不写了。

后面会继续分析gadget层中对USB协议的实现,如怎么枚举设备,USB描述符怎么传递,数据怎么收发等


阅读(1993) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册