版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
课程设计专业综合课程设计报告班级:通信10--02班姓名:王瑾学号:1006030218指导教师:杨春玲成绩:电子与信息工程学院
信息与通信工程系摘要随着人们生活水平的提高,我们用到的USB设备也越来越多,但是Linux在硬件配置上仍然没有做到完全即插即用,对于Linux怎样配置和使用他们,也越来越成为困扰我们的一大问题;本文的目地是使大家了解怎样编制USB设备驱动,为更好地配置和使用USB设备提供方便;对于希望开发Linux系统下USB设备驱动的人员,也可作为进一步学习USB驱动的大体架构进而编写出特殊USB设备的驱动程序。关键词:S3C2440A微处理器,Linux系统,USB目录TOC\o"1-5"\h\zUSB基础知识1硬件电路的设计1USB马驱动程序基础1USB马驱动程序4驱动模块的编译、配置和使用14总结157•参考文献15#1.USB基础知识USB是英文UniversalSerialBus的缩写,意为通用串行总线。USB最初是为了替代许多不同的低速总线(包括并行、串行和键盘连接)而设计的,它以单一类型的总线连接各种不同的类型的设备。USB的发展已经超越了这些低速的连接方式,它现在可以支持几乎所有可以连接到PC上的设备。最新的USB规范修订了理论上高达480Mbps的高速连接。Linux内核支持两种主要类型的USB驱动程序:宿主系统上的驱动程序和设备上的驱动程序,从宿主的观点来看(一个普通的宿主也就是一个PC机),宿主系统的USB设备驱动程序控制插入其中的USB设备,而USB设备的驱动程序控制该设备如何作为一个USB设备和主机通信。2•硬件电路的设计步进电机开环控制系统主要由中央控制器、步进电机驱动器、传感器以及步进电机四大部分组成。本系统采用基于ARM920t内核的S3C2440A微处理器作为控制系统的中央控制器,该芯片主频400MHz,最高可达到533MHz,内含多种设备接口,存储器使用64MB的NandFlash和64MB的SDRAM。图1所示为控制系统框图。幣1幣1漱比雕刻押制系统桩图图2坏席脉冲渝程图3.USB驱动程序基础USB驱动程序在内核中的结构,如下图:VFSj^网络设需层TTYE卫它.…USHUSB驱动程序存在于不同的内核子系统和USB硬件控制器之间,USB核心为USB驱动程序提供了一个用于访问和控制USB硬件的接口,而不必考虑系统当前存在的各种不同类型的USB硬件控制器。USB是一个非常复杂的设备,linux内核为我们提供了一个称为USB的核心的子系统来处理大部分的复杂性,USB设备包括配置(configuration)、接口(interface)和端点(endpoint),USB设备绑定到接口上,而不是整个USB设备。如下图所示:USB通信最基本的形式是通过端点(USB端点分中断、批量、等时、控制四种,每种用途不同),USB端点只能往一个方向传送数据,从主机到设备或者从设备到主机,端点可以看作是单向的管道(pipe)。所以我们可以这样认为:设备通常具有一个或者更多的配置,配置经常具有一个或者更多的接口,接口通常具有一个或者更多的设置,接口没有或具有一个以上的端点。驱动程序把驱动程序对象注册到USB子系统中,稍后再使用制造商和设备标识来判断是否已经安装了硬件。USB核心使用一个列表(是一个包含制造商ID和设备号ID的一个结构体)来判断对于一个设备该使用哪一个驱动程序,热插拨脚本使用它来确定当一个特定的设备插入到系统时该自动装载哪一个驱动程序。上面我们简要说明了驱动程序的基本理论,在写一个设备驱动程序之前,我们还要了解以下两个概念:模块和设备文件。模块:是在内核空间运行的程序,实际上是一种目标对象文件,没有链接,不能独立运行,但是可以装载到系统中作为内核的一部分运行,从而可以动态扩充内核的功能。模块最主要的用处就是用来实现设备驱动程序。Linux下对于一个硬件的驱动,可以有两种方式:直接加载到内核代码中,启动内核时就会驱动此硬件设备。另一种就是以模块方式,编译生成一个.ko文件(在2.4以下内核中是用.0作模块文件,我们以2.6的内核为准,以下同)。当应用程序需要时再加载到内核空间运行。所以我们所说的一个硬件的驱动程序,通常指的就是一个驱动模块。4.USB驱动程序驱动程序支持的设备:有一个结构体structusb_device_id,这个结构体提供了一列不同类型的该驱动程序支持的USB设备,对于一个只控制一个特定的USB设备的驱动程序来说,structusb_device_id表被定义为:/*驱动程序支持的设备列表*/staticstructusb_device_idskel_table[]={{USB_DEVICE(USB_SKEL_VENDOR_ID,USB_SKEL_PRODUCT_ID)},/*终止入口*/};MODULE_DEVICE_TABLE(usb,skel_table);对于PC驱动程序,MODULE_DEVICE_TABLE是必需的,而且usb必需为该宏的第一个值,而USB_SKEL_VENDOR_ID和USB_SKEL_PRODUCT_ID就是这个特殊设备的制造商和产品的ID了,我们在程序中把定义的值改为我们这款USB的,如:/*定义制造商和产品的ID号*/#defineUSB_SKEL_VENDOR_ID0x1234#defineUSB_SKEL_PRODUCT_ID0x2345这两个值可以通过命令lsusb,当然你得先把USB设备先插到主机上了。或者查看厂商的USB设备的手册也能得到,在我机器上运行lsusb是这样的结果:Bus004Device001:ID0000:0000Bus003Device002:ID1234:2345AbcCorp.Bus002Device001:ID0000:0000Bus001Device001:ID0000:0000得到这两个值后把它定义到程序里就可以了。注册USB驱动程序:所有的USB驱动程序都必须创建的结构体是structusb_driver。这个结构体必须由USB驱动程序来填写,包括许多回调函数和变量,它们向USB核心代码描述USB驱动程序。创建一个有效的structusb_driver结构体,只须要初始化五个字段就可以了,在框架程序中是这样的:staticstructusb_driverskel_driver={.owner=THIS_MODULE,.name="skeleton",.probe=skel_probe,.disconnect=skel_disconnect,.id_table=skel_table,};structmodule*owner:指向该驱动程序的模块所有者的指针。USB核心使用它来正确地对该USB驱动程序进行引用计数,使它不会在不合适的时刻被卸载掉,这个变量应该被设置为THIS_MODULE宏。constchar*name:指向驱动程序名字的指针,在内核的所有USB驱动程序中它必须是唯一的,通常被设置为和驱动程序模块名相同的名字。int(*probe)(structusb_interface*intf,conststructusb_device_id*id):这个是指向USB驱动程序中的探测函数的指针。当USB核心认为它有一个接口(usb_interface)可以由该驱动程序处理时,这个函数被调用。void(disconnect)(structusb_interface*intf):指向USB驱动程序中的断开函数的指针,当一个USB接口(usb_interface)被从系统中移除或者驱动程序正在从USB核心中卸载时,USB核心将调用这个函数。conststructusb_device_id*id_table:指向ID设备表的指针,这个表包含了一列该驱动程序可以支持的USB设备,如果没有设置这个变量,USB驱动程序中的探测回调函数就不会被调用。在这个结构体中还有其它的几个回调函数不是很常用,这里就不一一说明了。以structusb_driver指针为参数的usb_register_driver函数调用把structusb_driver注册到USB核心。一般是在USB驱动程序的模块初始化代码中完成这个工作的:staticint__initusb_skel_init(void){intresult;/*驱动程序注册到USB子系统中*/result=usb_register(&skel_driver);f(result)err("usb_registerfailed.Errornumber%d",result);returnresult;}当USB驱动程序将要被卸开时,需要把structusb_driver从内核中注销。通过调用usb_deregister_driver来完成这个工作,当调用发生时,当前绑定到该驱动程序上的任何USB接口都被断开,断开函数将被调用:staticvoid__exitusb_skel_exit(void){/*从子系统注销驱动程序*/usb_deregister(&skel_driver);}探测和断开:当一个设备被安装而USB核心认为该驱动程序应该处理时,探测函数被调用,探测函数检查传递给它的设备信息,确定驱动程序是否真的适合该设备。当驱动程序因为某种原因不应该控制设备时,断开函数被调用,它可以做一些清理工作。探测回调函数中,USB驱动程序初始化任何可能用于控制USB设备的局部结构体,它还把所需的任何设备相关信息保存到一个局部结构体中,下面是探测函数的部分源码,我们加以分析。/*设置端点信息*//*只使用第一个批量IN和批量OUT端点*/iface_desc=interface->cur_altsetting;for(i=0;i<iface_desc->desc.bNumEndpoints;++i){endpoint=&iface_desc->endpoint[i].desc;if(!dev->bulk_in_endpointAddr&&(endpoint->bEndpointAddress&USB_DIR_IN)&&((endpoint->bmAttributes&USB_ENDPOINT_XFERTYPE_MASK)==USB_ENDPOINT_XFER_BULK)){/*找到一个批量IN端点*/buffer_size=endpoint->wMaxPacketSize;dev->bulk_in_size=buffer_size;dev->bulk_in_endpointAddr=endpoint->bEndpointAddress;dev->bulk_in_buffer=kmalloc(buffer_size,GFP_KERNEL);讦(!dev->bulk_in_buffer){err("Couldnotallocatebulk_in_buffer");gotoerror;}}if(!dev->bulk_out_endpointAddr&&!(endpoint->bEndpointAddress&USB_DIR_IN)&&((endpoint->bmAttributes&USB_ENDPOINT_XFERTYPE_MASK)==USB_ENDPOINT_XFER_BULK)){/*找到一个批量OUT端点*/dev->bulk_out_endpointAddr=endpoint->bEndpointAddress;}}if(!(dev->bulk_in_endpointAddr&&dev->bulk_out_endpointAddr)){err("Couldnotfindbothbulk-inandbulk-outendpoints");gotoerror;}在探测函数里,这个循环首先访问该接口中存在的每一个端点,给该端点一个局部指针以便以后访问:for(i=0;i<iface_desc->desc.bNumEndpoints;++i){endpoint=&iface_desc->endpoint[i].desc;在一轮探测过后,我们就有了一个端点,在还没有发现批量IN类型的端点时,探测该端点方向是否为IN,这可以通过检查USB_DIR_IN是否包含在bEndpointAddress端点变量有确定,如果是的话,我们在探测该端点类型是否为批量,先用USB_ENDPOINT_XFERTYPE_MASK位掩码来取bmAttributes变量的值,然后探测它是否和USB_ENDPOINT_XFER_BULK值匹配:if(!dev->bulk_out_endpointAddr&&!(endpoint->bEndpointAddress&USB_DIR_IN)&&((endpoint->bmAttributes&USB_ENDPOINT_XFERTYPE_MASK)==USB_ENDPOINT_XFER_BULK))如果所有这些探测都通过了,驱动程序就知道它已经发现了正确的端点类型,可以把该端点的相关信息保存到一个局部结构体中以便稍后用它来和端点进行通信:/*找到一个批量IN类型的端点*/buffer_size=endpoint->wMaxPacketSize;dev->bulk_in_size=buffer_size;dev->bulk_in_endpointAddr=endpoint->bEndpointAddress;dev->bulk_in_buffer=kmalloc(buffer_size,GFP_KERNEL);讦(!dev->bulk_in_buffer){err("Couldnotallocatebulk_in_buffer");gotoerror;}因为USB驱动程序要在设备的生命周期的稍后时间获取和接口相关联的局部数据结构体,所以调用了usb_set_intfdata函数,把它保存到structusb_interface结构体中以便后面的访问/*把数据指针保存到这个接口设备中*/usb_set_intfdata(interface,dev);我们以后调用usb_set_intfdata函数来获取数据。当这一切都完成后,USB驱动程序必须在探测函数中调用usb_register_dev函数来把该设备注册到USB核心里:/*注册设备到USB核心*/retval=usb_register_dev(interface,&skel_class);讦(retval){/*有些情况下是不允许注册驱动程序的*/err("Notabletogetaminorforthisdevice.");usb_set_intfdata(interface,NULL);gotoerror;当一个USB设备被断开时,和该设备相关联的所有资源都应该被尽可能的清理掉,在此时,如果已在在探测函数中调用了注册函数来为该USB设备分配了一个次设备号话,必须调用usb_deregister_dev函数来把次设备号交还给USB核心。在断开函数中,从接口获取之前调用usb_set_intfdata设置的任何数据也是很重要的。然后设置structusb_interface结构体中的数据指针为NULL,以防任何不适当的对该数据的错误访问。在探测函数中会对每一个接口进行一次探测,所以我们在写USB驱动程序的时候,只要做好第一个端点,其它的端点就会自动完成探测。在探测函数中我们要注意的是在内核中用结构体structusb_host_endpoint来描述USB端点,这个结构体在另一个名为structusb_endpoint_descriptor的结构体中包含了真正的端点信息,structusb_endpoint_descriptor结构体包含了所有的USB特定的数据,该结构体中我们要关心的几个字段是:bEndpointAddress:这个是特定的USB地址,可以结合USB_DIR_IN和USB_DIR_OUT来使用,以确定该端点的数据是传向设备还是主机。bmAttributes:这个是端点的类型,这个值可以结合位掩码USB_ENDPOINT_XFERTYPE_MASK来使用,以确定此端点的类型是USB_ENDPOINT_XFER_ISOC(等时)、USB_ENDPOINT_XFER_BULK(批量)、USB_ENDPOINT_XFER_INT的哪一种。wMaxPacketSize:这个是端点一次可以处理的最大字节数,驱动程序可以发送数量大于此值的数据到端点,在实际传输中,数据量如果大于此值会被分割。blnterval:这个值只有在端点类型是中断类型时才起作用,它是端点中断请求的间隔时间,以毫秒为单位。提交和控制urb:当驱动程序有数据要发送到USB设备时(大多数情况是在驱动程序的写函数中),要分配一个urb来把数据传输给设备:/*创建一个urb,并且给它分配一个缓存*/urb=usb_alloc_urb(0,GFP_KERNEL);if(!urb){retval=-ENOMEM;gotoerror;}当urb被成功分配后,还要创建一个DMA缓冲区来以高效的方式发送数据到设备,传递给驱动程序的数据要复制到这块缓冲中去:buf=usb_buffer_alloc(dev->udev,count,GFP_KERNEL,&urb->transfer_dma);讦(!buf){retval=-ENOMEM;gotoerror;}讦(copy_from_user(buf,user_buffer,count)){retval=-EFAULT;gotoerror;}当数据从用户空间正确复制到局部缓冲区后,urb必须在可以被提交给USB核心之前被正确初始化:/*初始化urb*/usb_fill_bulk_urb(urb,dev->udev,usb_sndbulkpipe(dev->udev,dev->bulk_out_endpointAddr),buf,count,skel_write_bulk_callback,dev);urb->transfer_flags1=URB_NO_TRANSFER_DMA_MAP;然后urb就可以被提交给USB核心以传输到设备了:/*把数据从批量OUT端口发出*/retval=usb_submit_urb(urb,GFP_KERNEL);讦(retval){err("%s-failedsubmittingwriteurb,error%d",__FUNCTION__,retval);gotoerror;}当urb被成功传输到USB设备之后,urb回调函数将被USB核心调用,在我们的例子中,我们初始化urb,使它指向skel_write_bulk_callback函数,以下就是该函数:staticvoidskel_write_bulk_callback(structurb*urb,structpt_regs*regs){structusb_skel*dev;dev=(structusb_skel*)urb->context;if(urb->status&&!(urb->status==-ENOENTIIurb->status==-ECONNRESET||urb->status==-ESHUTDOWN)){dbg("%s-nonzerowritebulkstatusreceived:%d",__FUNCTION__,urb->status);}/*释放已分配的缓冲区*/usb_buffer_free(urb->dev,urb->transfer_buffer_length,urb->transfer_buffer,urb->transfer_dma);}有时候USB驱动程序只是要发送或者接收一些简单的数据,驱动程序也可以不用urb来进行数据的传输,这是里涉及到两个简单的接口函数:usb_bulk_msg和usb_control_msg,在这个USB框架程序里读操作就是这样的一个应用:/*进行阻塞的批量读以从设备获取数据*/retval=usb_bulk_msg(dev->udev,usb_rcvbulkpipe(dev->udev,dev->bulk_in_endpointAddr),dev->bulk_in_buffer,min(dev->bulk_in_size,count),&count,HZ*10);/*如果读成功,复制到用户空间*/讦(!retval){if(copy_to_user(buffer,dev->bulk_in_buffer,count))retval=-EFAULT;elseretval=count;}usb_bulk_msg接口函数的定义如下:intusb_bulk_msg(structusb_device*usb_dev,unsignedintpipe,void*data,intlen,int*actual_length,inttimeout);其参数为:structusb_device*usb_dev:指向批量消息所发送的目标USB设备指针。unsignedintpipe:批量消息所发送目标USB设备的特定端点,此值是调用usb_sndbulkpipe或者usb_rcvbulkpipe来创建的。void*data:如果是一个OUT端点,它是指向即将发送到设备的数据的指针。如果是IN端点,它是指向从设备读取的数据应该存放的位置的指针。intlen:data参数所指缓冲区的大小。int*actual_length:指向保存实际传输字节数的位置的指针,至于是传输到设备还是从设备接收取决于端点的方向。inttimeout:以Jiffies为单位的等待的超时时间,如果该值为0该函数一直等待消息的结束。如果该接口函数调用成功,返回值为0否则返回一个负的错误值。usb_control_msg接口函数定义如下:intusb_control_msg(structusb_device*dev,unsignedintpipe,__u8request,__u8requesttype,__ul6value,__ul6index,void*data,__ul6size,inttimeout)除了允许驱动程序发送和接收USB控制消息之外,usb_control_msg函数的运作和usb_bulk_msg函数类似,其参数和usb_bulk_msg的参数有几个重要区别:structusb_device*dev:指向控制消息所发送的目标USB设备的指针。unsignedintpipe:控制消息所发送的目标USB设备的特定端点,该值是调用usb_sndctrlpipe或usb_rcvctrlpipe来创建的。—u8request:控制消息的USB请求值。—u8requesttype:控制消息的USB请求类型值。—u16value:控制消息的USB消息值。—u16index:控制消息的USB消息索引值。void*data:如果是一个OUT端点,它是指身即将发送到设备的数据的指针。如果是一个IN端点,它是指向从设备读取的数据应该存放的位置的指针。—u16size:data参数所指缓冲区的大小。inttimeout:以Jiffies为单位的应该等待的超时时间,如果为0该函数将一直等待消息结束。如果该接口函数调用成功,返回传输到设备或者从设备读取的字节数;如果不成功它返回一个负的错误值。这两个接口函数都不能在一个中断上下文中或者持有自旋锁的情况下调用,同样,该函数也不能被任何其它函数取消,使用时要谨慎。我们要给未知的USB设备写驱动程序,只需要把这个框架程序稍做修改就可以用了,前面我们已经说过要修改制造商和产品的ID号,把OxfffO这两个值改为未知USB的ID号。#defineUSB_SKEL_VENDOR_ID0xfff0#defineUSB_SKEL_PRODUCT_ID0xfff0还有就是在探测函数中把需要探测的接口端点类型写好,在这个框架程序中只探测了批量(USB_ENDPOINT_XFER_BULK)IN和OUT端点,可以在此处使用掩码(USB_ENDPOINT_XFERTYPE_MASK)让其探测其它的端点类型,驱动程序会对USB设备的每一个接口进行一次探测,当探测成功后,驱动程序就被绑定到这个接口上。再有就是urb的初始化问题,如果你只写简单的USB驱动,这块不用多加考虑,框架程序里的东西已经够用了,这里我们简单介绍三个初始化urb的辅助函数:usb_fill_int_urb:它的函数原型是这样的:voidusb_fill_int_urb(structurb*urb,structusb_device*dev,unsignedintpipe,void*transfer_buff,intbuffer_length,usb_complete_tcomplete,void*context,intinterval);这个函数用来正确的初始化即将被发送到USB设备的中断端点的urb。usb_fill_bulk_urb:它的函数原型是这样的:voidusb_fill_bulk_urb(structurb*urb,structusb_device*dev,unsignedintpipe,void*transfer_buffer,intbuffer_length,usb_complete_tcomplete)这个函数是用来正确的初始化批量urb端点的。usb_fill_control_urb:它的函数原型是这样的:voidusb_fill_control_urb(structurb*urb,structusb_device*dev,unsignedintpipe,unsignedchar*setup_packet,void*transfer_buffer,intbuffer_length,usb_complete_tcomplete,void*context);这个函数是用来正确初始化控制urb端点的。还有一个初始化等时urb的,它现在还没有初始化函数,所以它们在被提交到USB核心前,必须在驱动程序中手工地进行初始化,可以参考内核源代码树下的/usr/src/〜/drivers/usb/media下的konicawc.c文件。5•
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026贵州乌江石林旅游发展有限公司招聘2人笔试备考试题及答案详解
- 2026江铜国际贸易有限公司招聘4人笔试备考试题及答案详解
- 雅安市市场监督管理局2026年面向社会公开招聘特种设备检验编外聘用人员笔试备考题库及答案详解
- 2025年星展银行校园招聘笔试考试试题及答案详解
- 2026云南云核地质环境测试有限公司招聘7人笔试备考题库及答案详解
- 2025年农业发展银行(青海省分行)人员招聘笔试考试试题及答案详解
- 2026重庆招商局交通科技有限公司招聘15人笔试备考题库及答案详解
- 2026四川凉山州布拖县总工会招聘工会社会工作者1名笔试模拟试题及答案详解
- 泸县教育和体育局2026年公开考调下属事业单位教师(99人)笔试参考题库及答案详解
- 2026年兰州银行校园招聘考试备考试题及答案详解
- 城轨安全用电-触电急救
- JJG539-2016数字指示秤检定记录格式
- 慢性肾脏病健康宣教
- 氩气安全技术说明书MSDS
- 银行保安服务投标方案(完整技术标)
- 拒绝文身主题班会课件
- 北京版八年级数学下册全册课件【完整版】
- 汽车行走的艺术学习通课后章节答案期末考试题库2023年
- 常微分方程一阶微分方程的初等解法公开课一等奖市赛课获奖课件
- 上海市临检中心 临床微生物学检验新技术及质量控制学习班课件 微生物检验新技术、新趋势
- 颈椎病的正骨推拿治疗
评论
0/150
提交评论