您好,欢迎来到九壹网。
搜索
您的当前位置:首页嵌入式LinuxUSB驱动程序

嵌入式LinuxUSB驱动程序

来源:九壹网
维普资讯 http://www.cqvip.com ‘…… ‘…‘……… ’……………… …………………… 实用第一/智慧密集 … …  .… … . 嵌兀式LinuxUSB驱动程序 杨德芳 摘 要 在嵌入式系统的开发中,编写设备驱动程序是必须要做的X-作。本文给出了USB 驱动程序的编写的架构,包括发现设备、读取设备信息、编写设备操作函数和注 册、注销设备等操作,并给出了键盘飞梭驱动程序完整实例。 iver结构 关键词 USB,探测函数,usb_df一、引言 USB(Universal Serial Bus)即通用串行总线,是一种全新的 编写驱动程序框架,通过调用操作系统提供的API接口函数可 以完成对USB外设的特定访问。 主机控制驱动主要是对USB主机控制器的驱动。在大多 双向同步传输的支持热插拔的数据传输总线,其目的是为了提 供一种兼容不同速度的、可扩充的并且使用方便的外围设备接 口,同时也是为了解决计算机接口的太多的弊端而设计的。一 个USB系统主要有三部分组成:USB互连、USB主机、USB设 数PC环境下,主机控制器都是由操作系统提供。嵌入式设备 一般都没有USB主机控制器,只是工作在Slave模式下。如果 要使USB具有主机功能,那么设备中需要选用一个带主机控 制器的USB接口控制芯片,同时自己还要有实现该主机控制 器的驱动程序。目前Linux内核中只提供USB主机控制器的开 放主机控制器和通用主机控制器接口两种规格,而这两种规格 备三部分组成的,其结构如图1所示。在编写USB设备驱动 程序设计时,可以分为三部分编写:主机端设备驱动程序、主 机控制器驱动程序设计和设备端驱动程序三部分,在本文中重 点介绍主机端驱动程序的设计。 主要用在PC架构中。USB主机端驱动程序与主机控制器的结 构如图2所示。其中USB核是Linux的一个子模块,集中定义 了一组USB相关的数据结构、宏以及API函数。 USB设备驱动程序{ I USB设备驱动程序 USB设备驱动程序 上层API函数 USB核 下层^PI函 USB主机控制器驱动程序 USB主机控制器驱动程序 图2 USB主机端驱动程序结构 USB设备驱动程序是常说的设备固件程序的一部分,提供 设备信息与主机的通信接口。设备端USB驱动程序设计由以 下几部分处理程序组成。初始化例程:完成描述符指针、端 点、配置改变等操作。数据传输例程:完成控制传输、批量传 输、中断传输及同步传输等传输方式下的数据收发工作。标准 图1 USB系统拓扑结构 设备处理请求:处理标准设备请求。厂商请求处理:处理生产 商指定请求。其他操作:处理主机发出的端口复位、配置改变 等操作。 1.程序框架 USB驱动程序首先要向Linux内核注册自己,并告诉系统 它所支持的设备类型以及它所支持的操作。这些信息通过一个 usbdriver结构来传递。usb_driver结构如下: _二、USB设备驱动程序 USB设备驱动程序的设计包括主机端设备驱动程序设计、 主机控制器驱动程序设计和设备端驱动程序设计三部分组成。 主机端设备驱动程序就是通常说的设备驱动程序,它是主机环 境中为用户应用程序提供一个访问USB外设的接口。Linux为 这部分驱动程序提供编程接口,驱动程序设计者只要按照需求 static struct usb_driver skeldriver={ _● 维普资讯 http://www.cqvip.com …….E■BEDDEDSYSTIEMPROG删MING……………一… -………………………、……………一……………… name: skeleton :/}驱动程序的名称}/ probe:skel_probe;/}设备列举时被调用}/ disconnect:skel disconnect;/}设备被卸载时被调用 / fops:&skel fops;/}指向一个file_operation结构,内核通过 它来访问驱动程序的文件操作函数。与用户程序的read、write 等操作进行交互 / minor USB_SKELMINOR_BASE;/}指向设备的次设备号, 用于系统识别主设备号相同的设备(即一个驱动程序可以同时 支持多个USB设备 / . id table:ske1._table;/}保存设备的厂商ID和产品ID,作为该 设备的唯一标识。驱动程序向系统注册后。当下次插入时,系统 根据这个标识查找正确的驱动程序,实现设备的即插即用 / ): static struct file_operation skel_fops=I I owner:THIS_MODULE, read:skel read, write:skel_write。 ioctl:skeljoctl, ・ open:skel_open, release:skel_release. ): (1)注册和注销 USB驱动程序注册,就是把在初始化函数中填好的 use_dfiver结构作为参数传递给use register()函数即可,函数的 调用方法为: result:usb_register(&skel_driver); 当要从系统卸载驱动程序时,也是将use_driver结构作为 参数传递给usb_deregister函数处理。函数的调用格式为: 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【】:I {USB_DEVICE(USB_SKEL_VENDORJD, USB SKEL_PRODUCTJD)). I)/}Terminating entry /): MODULE DEVICEJABLE(usb,skel_table): USB DEVICE宏利用厂商lD和产品lD提供了一个设备的 唯一标识。当系统插入一个lD匹配的USB设备到USB总线 时,驱动会在USB core中注册,驱动程序中probe函数也就会 被调用。usb_deviee结构指针、接口号和接口ID都会被传递到 函数中。 (2)probe()函数 probe()函数的编写格式为:static void}skel_probe (struct usb_deviee }dev, unsigned int ifnum, const struct usb__device_jd id);驱动程序需要确认插人的设备是否可以 被接受,如果不接受,或者在初始化的过程中发生任何错 误,probe()函数返回一个NULL值。否则返回一个含有设备 驱动程序状态的指针,通过这个指针,就可以访问所有结构 中的回调函数。 在驱动程序里。最后~点是要注册devfs(设备文件系 统)。首先创建一个缓冲用来保存那些被发送给USB设备的 数据和那些从设备上接受的数据,并为设备传输创建一个 USB请求块(URB)以向设备写入数据,同时USB urb被初 始化,然后在devfs子系统中注册设备,允许devfs用户访问 USB的设备。注册过程如下: /¥initialize the devfs node for this device and register it / sprintf(name, ske1%d ,skel一>minor): skel一>devfs=devfs_register (usb_devfs_bandle,name. DEVFS—FL EFAULT,USB_MAJOR,USB SKELJⅥINOR— BASE+skel一>minor,S/FCHR I S/RUSR I SJWUSR I S/RGRP I S_IWGRP I SJROTH,&skel_fops,NULL): 如果devfs_register函数失败,devfs子系统会将此情况报 告给用户。如果设备从USB总线拔掉,设备指针会调用dis. connect函数。驱动程序就需要清除那些被分配了的所有私有 数据、关闭urbs,并且从devfs上注销掉自己。调用函数的格 式为: / remove our devfs node / devfs unregister(skel一>devfs) 现在,skeleton驱动就已经和设备绑定上了,任何用户态 程序要操作此设备都可以通过file_operations结构所定义的函数 进行了。 3)open()、write()和read()函数 首先,要打开此设备。在open()函数中MOD— ULE_INC USE—COUNT宏是~个关键,它起到一个计数的作 用,有一个用户态程序打开一个设备,计数器就加1。例如, 以模块方式加入一个驱动,若计数器不为零,就说明仍然有用 户程序在使用此驱动,这时候,就不能通过rmmod命令卸载驱 动模块了。 /}increment our usage count for the module}/ MODJNC_USE COUNT; ++skel一>open_count; / save our object in the file S private structure}/ file一>private_data=skel; 当open完设备后,read()、write()函数就可以收、发数 据了。 read()函数首先从open()函数中保存fi。 Write()函数和read()函数是完成驱动对读写等操作的响 应。在skel_write中,一个FILL_BULK_JJRB函数,就完成了 urb系统eallbak和skel_write bulk_callback之间的联系。注意 skel_write ̄,ulkcallback是中断方式,所以要注意时间不能太 维普资讯 http://www.cqvip.com ………… ……………………………………………… 夹用第-/智慧密集 … … …… … 久,本程序中它就只是报告一些urh的状态等。read函数与 write函数稍有不同在于:程序并没有用urb将数据从设备传送 到驱动程序,而是用usb_bulk_msg函数代替,这个函数能够不 的USB总线上的设备信息.它包括Vendor、ProdlD、Product 等 / MODULE_AUTHOR(DRIVER_AUTHOR): MODULE ESCRIPTION(DRIVER DESC): 需要创建urhs和操作urb函数的情况下,来发送数据给设备, 或者从设备来接收数据。调用usb_bulk_msg函数并传到一个存 储空间,用来缓冲和放置驱动收到的数据,若没有收到数据表 / 此结构来自内核中 vers/usb/usbkbd.c / struct usb_kbd( struct inputdev dev; _ce usbdev; struct usbdevi_示失败并返回一个错误信息。 usbbulkmsg函数:当对usb设备进行一次读或者写时, __unsigned char new【8】: unsigned char old【8】: struct urb irq.Ied; usbbulkmsg函数是非常有用的;然而,当需要连续地对设备 __进行读/写时,应建立一个自己的urbs,同时将urbs提交给 USB子系统。 skeLdisconnect函数:当释放设备文件句柄时,这个函数 会被调用。MOD_DEC—USE—COUNT宏也会被调用到(和 MOD_INC USE COUNT刚好对应,它减少一个计数器),首先 确认当前是否有其他的程序正在访问这个设备,如果是最后一 个用户在使用,可以关闭任何正在发生的写,操作如下: / decrement our usage count for the device / 一一skel一>open_count; -f(skel一>open_count<=0)( / shutdown any bulk writes that might be going on / usb_unlink_urb(skel一>wr Jte_urb): sket一>open count=O: ) / decrement our usage count for the module / MOD ECjJSE_j:OUNT; USB设备可以在任何时间点从系统中取走,即使程序目前 正在访问它。USB驱动程序必须要能够很好地处理解决此问 题,它需要能够切断任何当前的读写,同时通知用户空间程 序:USB设备已经被取走。 2.设计实例 下面通过介绍键盘飞梭驱动程序的实例来让读者更好地理 解USB驱动程序的工作原理,实现代码如下: / 需要的头文件 / 稍nclude<linux/kerne1.h> #include<linux/slab.h> #include<linux/module.h> #include<linux/input.h> 锅nclude<linux/init.h> #include<linux/usb.h> #include<linux/kbdJI.h> / 驱动程序版本信息 / #define DRIVER_VERSION #define DRIVER_AUTH0R TGE H0TKEY 拌define DRIVER_DESC USB HID Tge hotkey driver #define USB_HOTKEY_VENDORJD OxO7e4 } efine USB. OTKE丫_PRODUC1。JD 0x9473 / 厂商和产品ID信息就是/proc/bus/usb/devices中看到 的值,通过cat/proc/bus/usb/devices得到当前系统探测到 与 struct usb_ctrlrequest dr; unsigned char Ieds,newleds; char name【1 28】: intopen; ): static void usb_kbdjrq(struct urb urb)/ urb为USB请求 块 / ( struct usb_kbd kbd=urb一>context; int new; new=lint )kbd一>new; if(kbd一>new【0】==(char)Ox01) ( |f(((kbd一>new【1】>>4)&OxOf)!=Ox7) ( handle_scancode(OxeO.1): handle_scancode(Ox4b.1): handle_scancode(OxeO,0): handle_scancode l Ox4b.0): ) else (handle_scancode(OxeO,1): handle_scancode(Ox4d,1): handle_scancode(OxeO,0): handle_scancode(Ox4d.0): }} printkl new=%x%x%x%x%x%x%x%x . kbd一> new【0】,kbd一>new【1】,kbd一>new【2】,kbd一>new【3】, kbd一>new【4】,kbd一>new【5】,kbd一>new【6】.kbd一> new【7】): } static void usb_kbdjorobe(struct usb_device dev. unsigned int ifnum,const struct usb_devicejd id) ( struct usb.interface}iface; struct usbjnterface_descriptor interface; struct usb_endpoint_descriptor endpoint; struct usb kbd kbd; int pipe,maxp; iface=&dev一>actconfig一>interface【ifnum】: interface=&iface一>altsetting【iface一>act_altsetting】: if((dev一>descriptor.idVendor!= USB HOTKE ENDOR_ID)II(dev一>descriptor.idProduct !=USB HOTKE RODUCTJD)II(_fnum!=1))( return NULL; } |f(dev一>actconfig一>bNumlnterfaces!=2) ( return NULL; } if(interface一>bNumEndpoints!=1)return NULL; endpoint=interface一>endpoint十0: pipe=usb_rcvintpipe(dev,endpoint一>bEndpointAddress): 维普资讯 http://www.cqvip.com …… E■BEDDEDSYSTE■PROG舢MING……… …………………………………………………^。…… H… … MODULE:DEVICE_TABLE(usb.usb__kbdjd_table) static struct usb driver usbkbd^driver:{ _maxp:usb_maxpacket(dev,pipe,usb_pipeout(pipe)): usb__set_protocol(dev,interface一>blnterfaceNumber,0); usb__setjdie(dev interface一>blnterfaceNumber,0,0): printk(KERN/NFO GUO:Vid:%.4x Pid:%.4x,De・ vice:%.2x.ifnum:%.2x,bufCount:%.8x\\n , dev一>descriptor.idVendor.dev一>descriptor.idProduct name: Hotkey . probe:usb__kbdprobe, _disconnect:usbkbd_disconnect, _e:usb__kbdidtable idtabl__dev一>descriptor.bcdDevice,ifnum.maxp): if(!(kbd:kmalloc(sizeof(struct usb_kbd). GFP__KERNEL)))return NULL; memset(kbd 0 sizeof(struct usb_kbd)): kbd一>usbdev:dev; NUL L. ); static int_jinit usb_kbdinit(void) { . usb_register(&usb__kbd-driver): info(DRIVER ERSION : DRIVER_DESC): return O: ) static void__exit usb_kbdexit(void) _FlLUNT.uRB(&kbd一>irq,dev.pipe,kbd一>new.maxp >8 7 8:maxp,usbkbdirq.kbd,endpoint一>blnterva1): _{ usb deregister(&usb_kbd_driver): ) moduleinit(usb_kbdjnit): module_exit l usb__kbd_exit): kbd一>irq.dev:kbd一>usbdev, if l dev一>descriptor.iManufacturer)usb string l dev. dev一>descriptor.iManufacturer,kbd一>name 63): if(usb_submitjJrb(&kbd一>irq)){ kfree(kbd):return NULL;) printk(KERN/NFO input%d:%s on usb%d:%d.%d\\n kbd一>dev.number. kbd一>name.dev一>bus一>bus— 三、结语 USB规范是一门比较新的技术,接口使用方便,但是驱动 程序的设计较复杂。上面介绍了USB设备驱动程序的设计, 主要分析了主机端驱动程序的设计,并且给出了一个编写USB num,dev一>devnum.ifnum): return kbd;) static void usbkbddisconnect(struct usb evice}dev,void __驱动程序的实例。 _ptr) 参考文献 1.刘峥嵘.嵌入式Linux应用开发详界解.机械工业出版 社,2004 _{ struct usbkbd}kbd=ptr; usb_unlink_urb(&kbd一>irq): kfree(kbd): 2.周立功.ARM嵌入式Linux系统构件与驱动开发范例. 北京航天航空大学出版社,2006 3.刘淼.嵌入式系统接口设计与Linux驱动程序开发.北 京航天航空大学出版社,2006 (收稿日期:2007年5月26日) __) static struct usbdevicejd usb_kbdjd_l:able【】:{ { USB EVICE(USB_HOTKEY_ ENDORJD, USB_HOTKE、, RODUCTJD)), {)/¥Terminating entry}/ ): (上接第65页) 在一个短的时间内(比如坐公车、排队、等人时)消遣时间, 这一点和电脑游戏不同。如果游戏操作过于复杂难免会失去很 多的玩家。手机游戏的操作最好控制在方向键、4、6、8、2 法通常是循环检测屏幕图像是否在某个部分发生了改变,如果 没有,就不用对那部分的屏幕进行更新。另一个方法就是增加 绘制图像的尺寸来减少单独的调用绘制的次数。 提高游戏的可玩性。第一,避免屏幕的闪烁。绘制图像通 常就是在paint方法中加入代码。先获得一个图像上下文,然 后在显示设备上面绘图。但是,在高速的游戏循环中这么做就 会遇到些实际问题,屏幕会看起来闪烁,也就不真实了。因 此,需要在屏幕外准备好一切,当确信准备好了后,再依次把 它绘制出来。为了进行这个操作,需要在屏幕外增加一个区域 ——和左右软键上。第三,有继续上次游戏的功能。玩家在玩手机 游戏的时候很可能收到短信或接电话,这时如果手机有暂停游 戏或继续上次游戏的功能是非常人性化的。具体代码到 http://www.comprg.eom.en下载。 参考文献 1. (美)Martin J.Wells著.,陈炜,任俊伟译. J2ME游戏编程.清华大学出版社,2005. 2.http://www.1inuxmine.eom 缓冲区,可以在屏幕外单独绘图。这样就有了两个缓冲 区,一个在手机屏幕上,代表上次渲染的图像,另一个在屏幕 外,可以在任何时间在上面绘图。这就是它被称为双缓冲的原 因。第二,避免复杂的操作。手机游戏的设计初衷就是让玩家 (收稿日期:2007年5月19日) 

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- 91gzw.com 版权所有 湘ICP备2023023988号-2

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务