怎样写linux下的USB设备linux驱动程序开发

Linux系统下USB2.0设备驱动程序的开发 - 中国学术期刊网络出版总库
中国学术期刊网络出版总库
Linux系统下USB2.0设备驱动程序的开发
【Author】
AI Ni,ZENG Cheng-bin
【摘要】 USB2.0版本的发布使USB已逐步成为一种标准接口,Linux系统下的USB设备驱动程序的开发是一项比较繁琐的工作。本文介绍了USB2.0总线规范、LinuxUSB子系统及设备驱动程序框架,并重点描述了LinuxUSB设备驱动程序编程方法和开发实例。
【关键词】 ;
【DOI】10.ki.52-1046/th.
【分类号】TP311.11
【被引频次】8
【下载频次】338
本文链接的文献网络图示:
本文的引文网络
浏览历史:
下载历史:您现在的位置: &
Linux下的硬件驱动――USB设备(下)(驱动开发部分)
Linux下的硬件驱动――USB设备(下)(驱动开发部分)
  USB骨架程序(usb-skeleton),是USB驱动程序的基础,通过对它源码的学习和理解,可以使我们迅速地了解USB驱动架构,迅速地开发我们自己的USB硬件的驱动。   前言
  在上篇《Linux下的硬件驱动--USB设备(上)(驱动配制部分)》中,我们知道了在Linux下如何去使用一些最常见的USB设备。但对于做系统设计的程序员来说,这是远远不够的,我们还需要具有驱动程序的阅读、修改和开发能力。在此下篇中,就是要通过简单的USB驱动的例子,随您一起进入USB驱动开发的世界。
  USB驱动开发
  在掌握了USB设备的配置后,对于程序员,我们就可以尝试进行一些简单的USB驱动的修改和开发了。这一段落,我们会讲解一个最基础USB框架的基础上,做两个小的USB驱动的例子。
  USB骨架
  在Linux kernel源码目录中driver/usb/usb-skeleton.c为我们提供了一个最基础的USB驱动程序。我们称为USB骨架。通过它我们仅需要修改极少的部分,就可以完成一个USB设备的驱动。我们的USB驱动开发也是从她开始的。
  那些linux下不支持的USB设备几乎都是生产厂商特定的产品。如果生产厂商在他们的产品中使用自己定义的协议,他们就需要为此设备创建特定的驱动程序。当然我们知道,有些生产厂商公开他们的USB协议,并帮助Linux驱动程序的开发,然而有些生产厂商却根本不公开他们的USB协议。因为每一个不同的协议都会产生一个新的驱动程序,所以就有了这个通用的USB驱动骨架程序, 它是以pci 骨架为模板的。
  如果你准备写一个linux驱动程序,首先要熟悉USB协议规范。USB主页上有它的帮助。一些比较典型的驱动可以在上面发现,同时还介绍了USB urbs的概念,而这个是usb驱动程序中最基本的。
  Linux USB 驱动程序需要做的第一件事情就是在Linux USB 子系统里注册,并提供一些相关信息,例如这个驱动程序支持那种设备,当被支持的设  备从系统插入或拔出时,会有哪些动作。所有这些信息都传送到USB 子系统中,在usb骨架驱动程序中是这样来表示的:
  static struct usb_driver skel_driver = {   name: "skeleton",   probe: skel_probe,   disconnect: skel_disconnect,   fops: &skel_fops,   minor: USB_SKEL_MINOR_BASE,   id_table: skel_table,   };
  变量name是一个字符串,它对驱动程序进行描述。probe 和disconnect 是函数指针,当设备与在id_table 中变量信息匹配时,此函数被调用。
  fops和minor变量是可选的。大多usb驱动程序钩住另外一个驱动系统,例如SCSI,网络或者tty子系统。这些驱动程序在其他驱动系统中注册,同时任何用户空间的交互操作通过那些接口提供,比如我们把SCSI设备驱动作为我们USB驱动所钩住的另外一个驱动系统,那么我们此USB设备的read、write等操作,就相应按SCSI设备的read、write函数进行访问。但是对于扫描仪等驱动程序来说,并没有一个匹配的驱动系统可以使用,那我们就要自己处理与用户空间的read、write等交互函数。Usb子系统提供一种方法去注册一个次设备号和file_operations函数指针,这样就可以与用户空间实现方便地交互。
  USB骨架程序的关键几点如下:
  USB驱动的注册和注销     Usb驱动程序在注册时会发送一个命令给usb_register,通常在驱动程序的初始化函数里。当要从系统卸载驱动程序时,需要注销usb子系统。即需要usb_unregister 函数处理:
  static void __exit usb_skel_exit(void)   {    /* deregister this driver with the USB subsystem */    usb_deregister(&skel_driver);   }   module_exit(usb_skel_exit);
  当usb设备插入时,为了使linux-hotplug(Linux中PCI、USB等设备热插拔支持)系统自动装载驱动程序,你需要创建一个MODULE_DEVICE_TABLE。代码如下(这个模块仅支持某一特定设备):
  /* table of devices that work with this driver */   static struct usb_device_id skel_table [] = {   { USB_DEVICE(USB_SKEL_VENDOR_ID,   USB_SKEL_PRODUCT_ID) },   { } /* Terminating entry */   };
  MODULE_DEVICE_TABLE (usb, skel_table);
  USB_DEVICE宏利用厂商ID和产品ID为我们提供了一个设备的唯一标识。当系统插入一个ID匹配的USB设备到USB总线时,驱动会在USB core中注册。驱动程序中probe 函数也就会被调用。usb_device 结构指针、接口号和接口ID都会被传递到函数中。
  static void * skel_probe(struct usb_device *dev,   unsigned int ifnum, const struct usb_device_id *id)
  驱动程序需要确认插入的设备是否可以被接受,如果不接受,或者在初始化的过程中发生任何错误,probe函数返回一个NULL值。否则返回一个含有设备驱动程序状态的指针。通过这个指针,就可以访问所有结构中的回调函数。
  在骨架驱动程序里,最后一点是我们要注册devfs。我们创建一个缓冲用来保存那些被发送给usb设备的数据和那些从设备上接受的数据,同时USB urb 被初始化,并且我们在devfs子系统中注册设备,允许devfs用户访问我们的设备。注册过程如下:
  /* initialize the devfs node for this device and register it */   sprintf(name, "skel%d", skel-&minor);   skel-&devfs = devfs_register   (usb_devfs_handle, name,   DEVFS_FL_DEFAULT, USB_MAJOR,   USB_SKEL_MINOR_BASE + skel-&minor,   S_IFCHR | S_IRUSR | S_IWUSR |   S_IRGRP | S_IWGRP | S_IROTH,   &skel_fops, NULL);
  如果devfs_register函数失败,不用担心,devfs子系统会将此情况报告给用户。
  当然最后,如果设备从usb总线拔掉,设备指针会调用disconnect 函数。驱动程序就需要清除那些被分配了的所有私有数据、关闭urbs,并且从devfs上注销调自己。
  /* remove our devfs node */   devfs_unregister(skel-&devfs);
  现在,skeleton驱动就已经和设备绑定上了,任何用户态程序要操作此设备都可以通过file_operations结构所定义的函数进行了。首先,我们要open此设备。在open函数中MODULE_INC_USE_COUNT 宏是一个关键,它的作用是起到一个计数的作用,有一个用户态程序打开一个设备,计数器就加一,例如,我们以模块方式加入一个驱动,若计数器不为零,就说明仍然有用户程序在使用此驱动,这时候,你就不能通过rmmod命令卸载驱动模块了。
  /* increment our usage count for the module */   MOD_INC_USE_COUNT;   ++skel-&open_   /* save our object in the file's private structure */   file-&private_data =
  当open完设备后,read、write函数就可以收、发数据了。
  skel的write、和read函数他们是完成驱动对读写等操作的响应。
  在skel_write中,一个FILL_BULK_URB函数,就完成了urb 系统callbak和我们自己的skel_write_bulk_callback之间的联系。注意skel_write_bulk_callback是中断方式,所以要注意时间不能太久,本程序中它就只是报告一些urb的状态等。
  read 函数与write 函数稍有不同在于:程序并没有用urb 将数据从设备传送到驱动程序,而是我们用usb_bulk_msg 函数代替,这个函数能够不需要创建urbs 和操作urb函数的情况下,来发送数据给设备,或者从设备来接收数据。我们调用usb_bulk_msg函数并传提一个存储空间,用来缓冲和放置驱动收到的数据,若没有收到数据,就失败并返回一个错误信息。
  usb_bulk_msg函数   当对usb设备进行一次读或者写时,usb_bulk_msg 函数是非常有用的; 然而, 当你需要连续地对设备进行读/写时,建议你建立一个自己的urbs,同时将urbs 提交给usb子系统。
  skel_disconnect函数   当我们释放设备文件句柄时,这个函数会被调用。MOD_DEC_USE_COUNT宏会被用到(和MOD_INC_USE_COUNT刚好对应,它减少一个计数器),首先确认当前是否有其它的程序正在访问这个设备,如果是最后一个用户在使用,我们可以关闭任何正在发生的写,操作如下:
  /* decrement our usage count for the device */   --skel-&open_   if (skel-&open_count &= 0) {   /* shutdown any bulk writes that might be   going on */   usb_unlink_urb (skel-&write_urb);   skel-&open_count = 0;   }   /* decrement our usage count for the module */   MOD_DEC_USE_COUNT;
  最困难的是,usb 设备可以在任何时间点从系统中取走,即使程序目前正在访问它。usb驱动程序必须要能够很好地处理解决此问题,它需要能够切断任何当前的读写,同时通知用户空间程序:usb设备已经被取走。
  如果程序有一个打开的设备句柄,在当前结构里,我们只要把它赋值为空,就像它已经消失了。对于每一次设备读写等其它函数操作,我们都要检查usb_device结构是否存在。如果不存在,就表明设备已经消失,并返回一个-ENODEV错误给用户程序。当最终我们调用release 函数时,在没有文件打开这个设备时,无论usb_device结构是否存在、它都会清空skel_disconnect函数所作工作。
  Usb 骨架驱动程序,提供足够的例子来帮助初始人员在最短的时间里开发一个驱动程序。更多信息你可以到linux usb开发新闻组去寻找。
&&&主编推荐
&&&热门试卷
&&&最新视频
&&&热门阅读
&&&最新问答
&&&&&&&&&&&&&&&
希赛网 版权所有 & &&&&湘教QS2-164&&增值电信业务经营许可证湘B2-> Linux USB gadget设备驱动解析(4)--编写一个gadget驱动
管理员:&&&人数:216小组积分:2830简介:本小组致力于为所有热爱嵌入式的朋友提供一个资源丰富的学习与沟通平台,包括嵌入式学习视频、嵌入式电子书、原创技术文章、免费活动信息、行业动态等等...小组话题,每日更新,欢迎分享,欢迎加入!小组公告小组暂无公告.&&|&&&&|&&
需要确认注册邮箱后才能下载,
楼主&&[其他]&
Re:Linux USB gadget设备驱动解析(4)--编写一个gadget驱动
积分:350分
总积分:659分
发表于 16:53
完整的一个系列 共4篇文章 希望对大家的学习有所帮助
1楼&&[其他]&
Linux USB gadget设备驱动解析(4)--编写一个gadget驱动
积分:350分
总积分:659分
发表于 16:53
作者:刘洪涛,讲师。一、编写计划通过前面几节的基础,本节计划编写一个简单的gadget驱动。重在让大家快速了解gadget驱动结构。上节中简单介绍了zero.c程序。这个程序考虑到了多配置、高速传输、USB OTG等因素。应该说写的比较清楚,是我们了解gadget驱动架构的一个非常好的途径。但把这些东西都放在一起,对很多初学人员来说还是不能快速理解。那就再把它简化一些,针对S3C2410平台,只实现一个配置、一个接口、一个端点,不考虑高速及OTG的情况。只完成单向从host端接收数据的功能,但要把字符设备驱动结合在里面。这需要有一个host端的驱动,来完成向device端发送数据。关于在主机端编写一个简单的USB设备驱动程序,有很多的资料。相信大家很快就会完成的。二、功能展示1、PC端编写了一个usbtransfer.ko,能够向device端发送数据2、对目标平台编写一个gadget驱动,名称是g_zero.ko3、测试步骤在目标平台(基于S3C2410)上加载gadget驱动# insmod g_zero.ko &&&&&&&&name=ep1-bulk&&&&&&&&smdk2410_udc: Pull-up enable&&&&&&&&# mknod /dev/usb_rcv c 251 0&&&&&&&&#在PC主机上加载驱动usbtransfer.ko#insmod usbtransfer.ko&&&&&&&&#mknod /dev/usbtransfer c 266 0连接设备,目标平台的终端显示:connected目标平台读取数据# cat /dev/usb_rcvPC端发送数据#echo “12345” & /dev/usbtransfer&&&&&&&&#echo “abcd” & /dev/usbtransfer设备端会显示收到的数据# cat /dev/usb_rcv&&&&&&&&12345&&&&&&&&abcd三、代码分析下面的代码是在原有的zero.c基础上做了精简、修改的。一些结构的名称还是保留以前的,但含义有所变化。如:loopback_config,不再表示loopback,而只是单向的接收数据。/** zero.c -- Gadget Zero, for simple USB development&&&&&&&&* .cn&&&&&&&&* All rights reserved.*/&&&&&&&&/* #define VERBOSE_DEBUG */#include &linux/kernel.h&&&&&&&&&#include &linux/utsname.h&&&&&&&&&#include &linux/device.h&&&&&&&&&#include &linux/usb/ch9.h&&&&&&&&&#include &linux/usb/gadget.h&&&&&&&&&#include "gadget_chips.h"&&&&&&&&#include &linux/slab.h&&&&&&&&&#include &linux/module.h&&&&&&&&&#include &linux/init.h&&&&&&&&&#include &linux/usb/input.h&&&&&&&&&#include &linux/cdev.h&&&&&&&&&#include &asm/uaccess.h&&&&&&&&&#include &linux/fs.h&&&&&&&&&#include &linux/poll.h&&&&&&&&&#include &linux/types.h& /* size_t */&&&&&&&&#include &linux/errno.h& /* error codes */&&&&&&&&#include &asm/system.h&&&&&&&&&#include &asm/io.h&&&&&&&&&#include &linux/sched.h&/*-------------------------------------------------------------------------*/&&&&&&&&static const char shortname[] = "zero";&&&&&&&&static const char loopback[] = "loop input to output";&&&&&&&&static const char longname[] = "Gadget Zero";&&&&&&&&static const char source_sink[] = "source and sink data";&&&&&&&&#define STRING_MANUFACTURER 25&&&&&&&&#define STRING_PRODUCT 42&&&&&&&&#define STRING_SERIAL 101&&&&&&&&#define STRING_SOURCE_SINK 250&&&&&&&&#define STRING_LOOPBACK 251//#define DRIVER_VENDOR_NUM 0x0525 /* NetChip */&&&&&&&&//#define DRIVER_PRODUCT_NUM 0xa4a0 /* Linux-USB "Gadget Zero" */&&&&&&&&#define DRIVER_VENDOR_NUM 0x5345 /* NetChip */&&&&&&&&#define DRIVER_PRODUCT_NUM 0x1234 /* Linux-USB "Gadget Zero" */static int usb_zero_major = 251;&&&&&&&&/*-------------------------------------------------------------------------*/&&&&&&&&static const char *EP_OUT_NAME; /* sink */&&&&&&&&/*-------------------------------------------------------------------------*//* big enough to hold our biggest descriptor */&&&&&&&&#define USB_BUFSIZ 256&&&&&&&&struct zero_dev { //zero设备结构&&&&&&&&&&&&&&&&&&&&spinlock_&&&&&&&&&&&&&&&&&&&&struct usb_gadget *&&&&&&&&&&&&&&&&&&&&struct usb_request * /* for control responses */&&&&&&&&&&&&&&&&&&&&struct usb_ep *out_&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&unsigned char data[128];&&&&&&&&&&&&&&&&&&&&unsigned int data_&&&&&&&&&&&&&&&&&&&&wait_queue_head_ &&&&&&&&};&&&&&&&&#define CONFIG_LOOPBACK 2&&&&&&&&static struct usb_device_descriptor device_desc = { //设备描述符&&&&&&&&&&&&&&&&&&&&.bLength = sizeof device_desc,&&&&&&&&&&&&&&&&&&&&.bDescriptorType = USB_DT_DEVICE,&&&&&&&&&&&&&&&&&&&&.bcdUSB = __constant_cpu_to_le16(0x0110),&&&&&&&&&&&&&&&&&&&&.bDeviceClass = USB_CLASS_VENDOR_SPEC,&&&&&&&&&&&&&&&&&&&&.idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM),&&&&&&&&&&&&&&&&&&&&.idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM),&&&&&&&&&&&&&&&&&&&&.iManufacturer = STRING_MANUFACTURER,&&&&&&&&&&&&&&&&&&&&.iProduct = STRING_PRODUCT,&&&&&&&&&&&&&&&&&&&&.iSerialNumber = STRING_SERIAL,&&&&&&&&&&&&&&&&&&&&.bNumConfigurations = 1,&&&&&&&&};&&&&&&&&static struct usb_endpoint_descriptor fs_sink_desc = { //端点描述符&&&&&&&&&&&&&&&&&&&&.bLength = USB_DT_ENDPOINT_SIZE,&&&&&&&&&&&&&&&&&&&&.bDescriptorType = USB_DT_ENDPOINT,&&&.bEndpointAddress = USB_DIR_OUT, //对主机端来说,输出&&&&&&&&&&&&&&&&&&&&.bmAttributes = USB_ENDPOINT_XFER_BULK,&&&&&&&&};static struct usb_config_descriptor loopback_config = { //配置描述符&&&&&&&&&&&&&&&&&&&&.bLength = sizeof loopback_config,&&&&&&&&&&&&&&&&&&&&.bDescriptorType = USB_DT_CONFIG,&&&&&&&&&&&&&&&&&&&&/* compute wTotalLength on the fly */&&&&&&&&&&&&&&&&&&&&.bNumInterfaces = 1,&&&&&&&&&&&&&&&&&&&&.bConfigurationValue = CONFIG_LOOPBACK,&&&&&&&&&&&&&&&&&&&&.iConfiguration = STRING_LOOPBACK,&&&&&&&&&&&&&&&&&&&&.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,&&&&&&&&&&&&&&&&&&&&.bMaxPower = 1, /* self-powered */&&&&&&&&};&&&&&&&&static const struct usb_interface_descriptor loopback_intf = { //接口描述符&&&&&&&&&&&&&&&&&&&&.bLength = sizeof loopback_intf,&&&&&&&&&&&&&&&&&&&&.bDescriptorType = USB_DT_INTERFACE,&&&&.bNumEndpoints = 1,&&&&&&&&&&&&&&&&&&&&.bInterfaceClass = USB_CLASS_VENDOR_SPEC,&&&&&&&&&&&&&&&&&&&&.iInterface = STRING_LOOPBACK,&&&&&&&&};&&&&&&&&/* static strings, in UTF-8 */&&&&&&&&#define STRING_MANUFACTURER 25&&&&&&&&#define STRING_PRODUCT 42&&&&&&&&#define STRING_SERIAL 101&&&&&&&&#define STRING_SOURCE_SINK 250&&&&&&&&#define STRING_LOOPBACK 251&&&&&&&&static char manufacturer[50];&&&&&&&&/* default serial number takes at least two packets */&&&&&&&&static char serial[] = "..";&&&&&&&&static struct usb_string strings[] = { //字符串描述符&&&&&&&&&&&&&&&&&&&&{ STRING_MANUFACTURER, manufacturer, },&&&&&&&&&&&&&&&&&&&&{ STRING_PRODUCT, longname, },&&&&&&&&&&&&&&&&&&&&{ STRING_SERIAL, serial, },&&&&&&&&&&&&&&&&&&&&{ STRING_LOOPBACK, loopback, },&&&&&&&&&&&&&&&&&&&&{ STRING_SOURCE_SINK, source_sink, },&&&&&&&&&&&&&&&&&&&&{ } /* end of list */&&&&&&&&};static struct usb_gadget_strings stringtab = {&&&&&&&&&&&&&&&&&&&&.language = 0x0409, /* en-us */&&&&&&&&&&&&&&&&&&&&.strings = strings,&&&&&&&&};static const struct usb_descriptor_header *fs_loopback_function[] = {&&&&&&&&&&&&&&&&&&&&(struct usb_descriptor_header *) &loopback_intf,&&&&&&&&&&&&&&&&&&&&(struct usb_descriptor_header *) &fs_sink_desc,&&&&&&&&&&&&&&&&&&&&NULL,&&&&&&&&};static int&&&&&&&&usb_zero_open (struct inode *inode, struct file *file) //打开设备&&&&&&&&{&&&&&&&&&&&&&&&&&&struct zero_dev *dev =&&&&&&&&&&&&&&&&&&&&container_of (inode-&i_cdev, struct zero_dev, cdev);&&&&&&&&&&&&&&&&&&&&file-&private_data =&&&&&&&&&&&&&&&&&&init_waitqueue_head (&dev-&bulkrq);&&&&&&&& return 0;&&&&&&&&}static int&&&&&&&&usb_zero_release (struct inode *inode, struct file *file) //关闭设备&&&&&&&&{&&&&&&&&&&&&&&&&&&return 0;&&&&&&&&}&&&&&&&&static void free_ep_req(struct usb_ep *ep, struct usb_request *req)&&&&&&&&{&&&&&&&&&&&&&&&&&&&&kfree(req-&buf);&&&&&&&&&&&&&&&&&&&&usb_ep_free_request(ep, req);&&&&&&&&}&&&&&&&&static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)//分配请求&&&&&&&&{&&&&&&&&&&&&&&&&&&&&struct usb_request *&&&&&&&&&&&&req = usb_ep_alloc_request(ep, GFP_ATOMIC);&&&&&&&&&&&&&&&&&&&&if (req) {&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&req-&length =&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&req-&buf = kmalloc(length, GFP_ATOMIC);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&if (!req-&buf) {&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&usb_ep_free_request(ep, req);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&req = NULL;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)//请求完成函数&&&&&&&&{&&&&&&&&&&&&&&&&&&&&struct zero_dev *dev = ep-&driver_&&&&&&&&&&&&&&&&&&&&int status = req-&&&&&&&&&&&&&&&&&&&&&switch (status) {&&&&&&&&&&&&&&&&&&&&case 0: /* normal completion */&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&if (ep == dev-&out_ep) {&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&memcpy(dev-&data, req-&buf, req-& actual);//返回数据拷贝到req-&buf中, &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&//dev-&data_size=req-& &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&dev-&data_size=req-& //实际长度为req-& actual;需要确认&&&&&&&&req –&short_not_ok为0。参考gadget.h中关于usb_request结构的注释&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&} &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&/* this endpoint is normally active while we're configured */&&&&&&&&&&&&&&&&&&&&case -ECONNABORTED: /* hardware forced ep reset */&&&&&&&&&&&&&&&&&&&&case -ECONNRESET: /* request dequeued */&&&&&&&&&&&&&&&&&&&&case -ESHUTDOWN: /* disconnect from host */&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&printk("%s gone (%d), %d/%d\n", ep-&name, status,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&req-&actual, req-&length);&&&&&&&&&&&&&&&&&&&&case -EOVERFLOW: /* buffer overrun on read means that&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&* we didn't provide a big enough&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&* buffer.&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/&&&&&&&&&&&&&&&&&&&&default:&&&&&&&&#if 1&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&printk("%s complete --& %d, %d/%d\n", ep-&name,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&status, req-&actual, req-&length);&&&&&&&&#endif&&&&&&&&&&&&&&&&&&&&case -EREMOTEIO: /* short read */&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&free_ep_req(ep, req);&&&&&&&&&&&&&&&&&&&&wake_up_interruptible (&dev-&bulkrq); //唤醒读函数&&&&&&&&}static struct usb_request *source_sink_start_ep(struct usb_ep *ep)//构造并发送读请求&&&&&&&&{&&&&&&&&&&&&&&&&&&&&struct usb_request *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&//printk("in %s\n",__FUNCTION__);&&&&&&&&&&&&&&&&&&&&req = alloc_ep_req(ep, 128);&&&&&&&&&&&&&&&&&&&&if (!req)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&return NULL;&&&&&&&&&&&&&&&&&&&&memset(req-&buf, 0, req-&length);&&&&&&&&&&&&&&&&&&&&req-&complete = source_sink_ //请求完成函数&&&&&&&&&&&&&&&&&&&&status = usb_ep_queue(ep, req, GFP_ATOMIC); //递交请求&&&&&&&&&&&&&&&&&&&&if (status) {&&&&&&&&&&&&&&&&&&&&&&&&&&&&&struct zero_dev *dev = ep-&driver_&&&&&&&&&&&&&&&&&&&&&&&&&&&&&printk("start %s --& %d\n", ep-&name, status);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&free_ep_req(ep, req);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&req = NULL;&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&ssize_t&&&&&&&&usb_zero_read (struct file * file, const char __user * buf, size_t count,loff_t * f_pos) //读设备&&&&&&&&{&&&&&&&&&&&&&&&&&&&&struct zero_dev *dev =file-&private_&&&&&&&&&&&&&&&&&&&&struct usb_request *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&struct usb_ep *&&&&&&&&&&&&&&&&&&&&struct usb_gadget *gadget = dev-&&&&&&&&&&&&&&&&&&&&&ssize_t ret = 0;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&ep=dev-&out_&&&&&&&&&&&&&&&&&&&&source_sink_start_ep(ep);//构造、递交读请求&&&&&&&&&&&&&&&&&&&&if (count & 0)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&return -EINVAL;&&&&&&&&&&&&&&&&&&&&interruptible_sleep_on (&dev-&bulkrq);//睡眠,等到请求完成&&&&&&&&&&&&&&&&&&&&if (copy_to_user (buf,dev-&data,dev-&data_size)) //拷贝读取的数据到用户空间&&&&&&&&&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&&&&&ret = -EFAULT;&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&else&&&&&&&&&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&&&&ret = dev-&data_&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&&&&&&&&&}struct file_operations usb_zero_fops = {&&&&&&&&&&&&&&&&.owner = THIS_MODULE,&&&&&&&&&&&&&&&&.read = usb_zero_read,&&&&&&&&&&&&&&&&.open = usb_zero_open,&&&&&&&&&&&&&&&&.release = usb_zero_release,&&&&&&&&};static void&&&&&&&&usb_zero_setup_cdev (struct zero_dev *dev, int minor)//注册字符设备驱动&&&&&&&&{&&&&&&&&&&&&&&&&int err, devno = MKDEV (usb_zero_major, minor);&&&&&cdev_init(&dev-&cdev, &usb_zero_fops);&&&&&&&&&&&&&&&&dev-&cdev.owner = THIS_MODULE;&&&&&&&&&&&&&&&&err = cdev_add (&dev-&cdev, devno, 1);&&&&&&&&&&&&&&&&if (err)&&&&&&&&&&&&&&&&&&&printk ("Error adding usb_rcv\n");&&&&&&&&}static void zero_setup_complete(struct usb_ep *ep, struct usb_request *req)//配置端点0的请求完成处理&&&&&&&&{&&&&&&&&&&&&&&&&&&&if (req-&status || req-&actual != req-&length)&&&&&&&&&&&&&&&&&&&&&&printk("setup complete --& %d, %d/%d\n",&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&req-&status, req-&actual, req-&length);&&&&&&&&}&&&&&&&&static void zero_reset_config(struct zero_dev *dev) //复位配置&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&&usb_ep_disable(dev-&out_ep);&&&&&&&&&&&&&&&&&&&&&&dev-&out_ep = NULL;&&&&&&&&}&&&&&&&&static void zero_disconnect(struct usb_gadget *gadget)//卸载驱动时被调用,做一些注销工作&&&&&&&&{&&&&&&&&&&&&&&&&&&&struct zero_dev *dev = get_gadget_data(gadget);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&unregister_chrdev_region (MKDEV (usb_zero_major, 0), 1);&&&&&&&&&&&&&&&&&&&cdev_del (&(dev-&cdev));&&&&&&&&&&&&&&&&&&&zero_reset_config(dev);&&&&&&&&&&&&&&&&&&&printk("in %s\n",__FUNCTION__);&&&&&&&&}static int config_buf(struct usb_gadget *gadget,&&&&&&&&&&&&&&&&&&&&&&u8 *buf, u8 type, unsigned index)&&&&&&&&{&&&&&&&&&&&&&&&&&&&//int is_source_&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&const struct usb_descriptor_header **&&&&&&&&&&&&&&&&&&&int hs = 0;&&&&&&&&&&&&&&&&&&&function =fs_loopback_//根据fs_loopback_function,得到长度,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&//此处len=配置(9)+1个接口(9)+1个端点(7)=25&&&&&&&&&&&&&&&&&&&len = usb_gadget_config_buf(&loopback_config,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&buf, USB_BUFSIZ, function);&&&&&&&&&&&&&&&&&&&if (len & 0)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&((struct usb_config_descriptor *) buf)-&bDescriptorType =&&&&&&&&&&&&&&&&&&&&&&&&&&&}static int set_loopback_config(struct zero_dev *dev)&&&&&&&&{&&&&&&&&&&&&&&&&int result = 0;&&&&&&&&&&&&&&&&struct usb_ep *&&&&&&&&&&&&&&&&struct usb_gadget *gadget = dev-&&&&&&&&&&&&&&&&&ep=dev-&out_&&&&&&&&&&&&&&&&const struct usb_endpoint_descriptor *d;&&&&&&&&&&&&&&&&d = &fs_sink_&&&&&&&&&&&&&&&&result = usb_ep_enable(ep, d); //激活端点&&&&&&&&&&&&&&&&//printk("");&&&&&&&&&&&&&&&&if (result == 0) {&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&printk("connected\n"); //如果成功,打印“connected”&&&&&&&&&&&&&&&&} &&&&&&&&&&&&&&&&else&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&printk("can't enable %s, result %d\n", ep-&name, result);&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&static int zero_set_config(struct zero_dev *dev, unsigned number)&&&&&&&&{&&&&&&&&&&&&&&&&int result = 0;&&&&&&&&&&&&&&&&struct usb_gadget *gadget = dev-&&&&&&&&&&&&&&&&&result = set_loopback_config(dev);//激活设备&&&&&&&&&&&&&&&&if (result)&&&&&&&&&&&&&&&&&&&&&&&&zero_reset_config(dev); //复位设备&&&&&&&&&&&&&&&&else {&&&&&&&&&&&&&&&&&&&&&&&&char *switch (gadget-&speed) {&&&&&&&&&&&&&&&&&&&&&&&&case USB_SPEED_LOW: speed = "low";&&&&&&&&&&&&&&&&&&&&&&&&case USB_SPEED_FULL: speed = "full";&&&&&&&&&&&&&&&&&&&&&&&&case USB_SPEED_HIGH: speed = "high";&&&&&&&&&&&&&&&&&&&&&&&&default: speed = " ";&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&/***&&&&&&&&zero_setup完成USB设置阶段和具体功能相关的交互部分&&&&&&&&***/&&&&&&&&static int&&&&&&&&zero_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)&&&&&&&&{&&&&&&&&&&&&&&&&struct zero_dev *dev = get_gadget_data(gadget);&&&&&&&&&&&&&&&&struct usb_request *req = dev-&&&&&&&&&&&&&&&&&int value = -EOPNOTSUPP;&&&&&&&&&&&&&&&&u16 w_index = le16_to_cpu(ctrl-&wIndex);&&&&&&&&&&&&&&&&u16 w_value = le16_to_cpu(ctrl-&wValue);&&&&&&&&&&&&&&&&u16 w_length = le16_to_cpu(ctrl-&wLength);/* usually this stores reply data in the pre-allocated ep0 buffer,&&&&&&&&&&&&&&&&&&&* but config change events will reconfigure hardware.&&&&&&&&&&&&&&&&&&&*/&&&&&&&&&&&&&&&&req-&zero = 0;switch (ctrl-&bRequest) {&&&&&&&&&&&&&&&&case USB_REQ_GET_DESCRIPTOR: //获取描述符&&&&&&&&&&&&&&&&&&&&&&&&if (ctrl-&bRequestType != USB_DIR_IN)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&switch (w_value && 8) {&&&&&&&&&&&&&&&&&&&&&&&&case USB_DT_DEVICE: //获取设备描述符&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&value = min(w_length, (u16) sizeof device_desc);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&memcpy(req-&buf, &device_desc, value);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&case USB_DT_CONFIG: //获取配置,注意:会根据fs_loopback_function读取到接口、端点描述符,注意通过config_buf完成读取数据及数量的统计。&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&value = config_buf(gadget, req-&buf,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&w_value && 8,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&w_value & 0xff);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&if (value &= 0)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&value = min(w_length, (u16) value);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&case USB_DT_STRING:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&value = usb_gadget_get_string(&stringtab,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&w_value & 0xff, req-&buf);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&if (value &= 0)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&value = min(w_length, (u16) value);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&&&&&case USB_REQ_SET_CONFIGURATION:&&&&&&&&&&&&&&&&&&&&&&&&if (ctrl-&bRequestType != 0)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&spin_lock(&dev-&lock);&&&&&&&&&&&&&&&&&&&&&&&&value = zero_set_config(dev, w_value);//激活相应的端点&&&&&&&&&&&&&&&&&&&&&&&&spin_unlock(&dev-&lock);&&&&&&&&&&&&&&&&&&&&&&&&default:&&&&&&&&unknown:&&&&&&&&&&&&&&&&&&&&&&&&printk(&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"unknown control req%02x.%02x v%04x i%04x l%d\n",&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&ctrl-&bRequestType, ctrl-&bRequest,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&w_value, w_index, w_length);&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&/* respond with data transfer before status phase */&&&&&&&&&&&&&&&&& if (value &= 0) {&&&&&&&&&&&&&&&&&&&&&&&&req-&length =&&&&&&&&&&&&&&&&&&&&&&&&req-&zero = value & w_&&&&&&&&&&&&&&&&&&&&&&&&value = usb_ep_queue(gadget-&ep0, req, GFP_ATOMIC);//通过端点0完成setup&&&&&&&&&&&&&&&&&&&&&&&&if (value & 0) {&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&printk("ep_queue --& %d\n", value);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&req-&status = 0;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&zero_setup_complete(gadget-&ep0, req);&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&& }&&&&&&&&&&&&&&&&& /* device either stalls (value & 0) or reports success */&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&static void zero_unbind(struct usb_gadget *gadget) //解除绑定&&&&&&&&{&&&&&&&&&&&&&&&&struct zero_dev *dev = get_gadget_data(gadget);printk("unbind\n");&&&&&&&&&&&&&&&&unregister_chrdev_region (MKDEV (usb_zero_major, 0), 1);&&&&&&&&&&&&&&&&cdev_del (&(dev-&cdev));&&&&&&&&&&&&&&&&/* we've already been disconnected ... no i/o is active */&&&&&&&&&&&&&&&&if (dev-&req) {&&&&&&&&&&&&&&&&&&&&&&&&dev-&req-&length = USB_BUFSIZ;&&&&&&&&&&&&&&&&&&&&&&&&free_ep_req(gadget-&ep0, dev-&req);&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&kfree(dev);&&&&&&&&&&&&&&&&set_gadget_data(gadget, NULL);&&&&&&&&}&&&&&&&&static int __init zero_bind(struct usb_gadget *gadget) //绑定过程 &&&&&&&&{&&&&&&&&&&&&&&&&struct zero_dev *&&&&&&&&&&&&&&&&struct usb_ep *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&usb_ep_autoconfig_reset(gadget);&&&&&&&&&&&&&&&&ep = usb_ep_autoconfig(gadget, &fs_sink_desc);//根据端点描述符及控制器端点情况,分配一个合适的端点。&&&&&&&&&&&&&&&&if (!ep)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&EP_OUT_NAME = ep-& //记录名称&&&&&&&&&&&&&&&&gcnum = usb_gadget_controller_number(gadget);//获得控制器代号&&&&&&&&&&&&&&&&if (gcnum &= 0)&&&&&&&&&&&&&&&&&&&&&&&&device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);//赋值设备描述符&&&&&&&&&&&&&&&&else {&&&&&&&&&&&&&&&&&&&&&&&&pr_warning("%s: controller '%s' not recognized\n",&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&shortname, gadget-&name);&&&&&&&&&&&&&&&&&&&&&&&&device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);&&&&&&&&}&&&&&&&&dev = kzalloc(sizeof(*dev), GFP_KERNEL); //分配设备结构体&&&&&&&&if (!dev)&&&&&&&&&&&&&&&&return -ENOMEM;&&&&&&&&spin_lock_init(&dev-&lock);&&&&&&&&dev-&gadget =&&&&&&&&set_gadget_data(gadget, dev);&&&&&&&&dev-&req = usb_ep_alloc_request(gadget-&ep0, GFP_KERNEL);//分配一个请求&&&&&&&&if (!dev-&req)&&&&&&&&&&&&&&&&&&&&&&&&dev-&req-&buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);&&&&&&&&if (!dev-&req-&buf)&&&&&&&&&&&&&&&&&&&&&&&&dev-&req-&complete = zero_setup_&&&&&&&&dev-&out_ep= //记录端点(就是接收host端数据的端点)&&&&&&&&printk("name=%s\n",dev-&out_ep-&name); //打印出这个端点的名称&&&&&&&&ep-&driver_data=&&&&&&&&device_desc.bMaxPacketSize0 = gadget-&ep0-&&&&&&&&&usb_gadget_set_selfpowered(gadget);&&&&&&&&gadget-&ep0-&driver_data =&&&&&&&&snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",&&&&&&&&&&&&&&&&init_utsname()-&sysname, init_utsname()-&release,&&&&&&&&gadget-&name);/**************************字符设备注册*******************/&&&&&&&&dev_t usb_zero_dev = MKDEV (usb_zero_major, 0); &&&&&&&&int result = register_chrdev_region (usb_zero_dev, 1, "usb_zero");&&&&&&&&if (result & 0)&&&&&&&&{&&&&&&&&&&&&&&&&printk (KERN_NOTICE "Unable to get usb_transfer region, error %d\n",result);&&&&&&&&&&&&&&&&return 0;&&&&&&&&}&&&&&&&&usb_zero_setup_cdev (dev, 0); &&&&&&&&return 0;&&&&enomem:&&&&&&&&zero_unbind(gadget);&&&&&&&&return -ENOMEM;&&&&}/*-------------------------------------------------------------------------*/&&&&&&&&static struct usb_gadget_driver zero_driver = { //gadget驱动的核心数据结构&&&&&&&&#ifdef CONFIG_USB_GADGET_DUALSPEED&&&&&&&&&&&&&&&&&&&&&&&&.speed = USB_SPEED_HIGH,&&&&&&&&#else&&&&&&&&&&&&&&&&&&&&&&&&.speed = USB_SPEED_FULL,&&&&&&&&#endif&&&&&&&&&&&&&&&&&&&&&&&&.function = (char *) longname,&&&&&&&&&&&&&&&&&&&&&&&&.bind = zero_bind,&&&&&&&&&&&&&&&&&&&&&&&&.unbind = __exit_p(zero_unbind),&&&&&&&&&&&&&&&&&&&&&&&&.setup = zero_setup,&&&&&&&&&&&&&&&&&&&&&&&&.disconnect = zero_disconnect,&&&&&&&&&&&&&&&&&&&&&&&&//.suspend = zero_suspend, //不考虑电源管理的功能 &&&&&&&&&&&&&&&&&&&&&&&&//.resume = zero_resume,&&&&&&&&&&&&&&&&&&&&&&&&.driver = {&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&.name = (char *) shortname,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&.owner = THIS_MODULE,&&&&&&&&&&&&&&&&&&&&&&&&},&&&&&&&&};&&&&&&&&MODULE_AUTHOR("David Brownell");&&&&&&&&MODULE_LICENSE("GPL");&&&&&&&&static int __init init(void)&&&&&&&&{&&&&&&&&&&&&&&&&return usb_gadget_register_driver(&zero_driver); //注册驱动,调用bind绑定到控制器 &&&&&&&&}&&&&&&&&module_init(init);static void __exit cleanup(void)&&&&&&&&{&&&&&&&&&&&&&&&&usb_gadget_unregister_driver(&zero_driver); //注销驱动,通常会调用到unbind解除绑定, //在s3c2410_udc.c中调用的是disconnect方法 &&&&&&&&}&&&&&&&&module_exit(cleanup);三、总结 &&&&时间关系,上面的代码没有做太多的优化,但功能都是测试通过。希望能给大家的学习提供一点帮助。最后想谈谈学习USB驱动的一些方法。 &&&&USB驱动比较难掌握,主要原因是: &&&&复杂的USB协议,包括USB基本协议、类规范等 &&&&控制器包括主机端、设备端。控制器本身相对复杂,其对应的主、从控制器驱动比较复杂 &&&&Hub功能及驱动、管理程序比较复杂 &&&&需要专业的硬件测试工具,硬件信号调试较困难 &&&&主、从端上层驱动程序本身不难,但由于对硬件不理解,及不好编写测试程序。所以往往望而却步。 我觉得学习USB驱动前应该有一个比较好的思路,个人建议可以按下面的过程学习&&&&熟悉USB协议。不用看完所有的协议,重点关注一些概念、配置过程及数据包格式
liuhongtao 编辑于 08:59
--- 现有 2个主题,共 1页,目前第 1页 ---&&
点击,立即回复。
IC厂商小组
在线研讨会
赞助商链接
2015 EDN China -- Use of this website is subject to its terms of use.
京ICP备号-1 |
京公网安备 |

我要回帖

更多关于 linux查看usb设备信息 的文章

 

随机推荐