Linux USB驱动工作流程

合集下载

LINUX操作系统USB驱动程序

LINUX操作系统USB驱动程序

一、引言USB(Universal Serial Bus)即通用串行总线,是一种全新的双向同步传输的支持热插拔的数据传输总线,其目的是为了提供一种兼容不同速度的、可扩充的并且使用方便的外围设备接口,同时也是为了解决计算机接口的太多的弊端而设计的。

一个USB系统主要有三部分组成:USB互连、USB主机、USB设备三部分组成的,其结构如图1所示。

在编写USB设备驱动程序设计时,可以分为三部分编写:主机端设备驱动程序、主机控制器驱动程序设计和设备端驱动程序三部分,在本文中重点介绍主机端驱动程序的设计。

二、USB设备驱动程序的设计USB设备驱动程序的设计包括主机端设备驱动程序设计、主机控制器驱动程序设计和设备端驱动程序设计三部分组成。

主机端设备驱动程序就是通常说的设备驱动程序,它是主机环境中为用户应用程序提供一个访问USB外设的接口。

Linux为这部分驱动程序提供编程接口,驱动程序设计者只要按照需求编写驱动程序框架,通过调用操作系统提供的API接口函数可以完成对USB外设的特定访问。

主机控制驱动主要是对USB主机控制器的驱动,在大多数PC环境下,主机控制器都是由操作系统提供。

嵌入式设备一般都没有USB主机控制器,只是工作在Slave模式下。

如果要使USB具有主机功能,那么设备中需要选用一个带主机控制器的USB接口控制芯片,同时自己还要有实现该主机控制器的驱动程序。

目前Linux内核中只提供USB主机控制器的开放主机控制器和通用主机控制器接口两种规格,而这两种规格主要用在PC架构中。

USB主机端驱动程序与主机控制器的结构如图2所示。

其中USB核是Linux的一个子模块,集中定义了一组USB相关的数据结构、宏以及API函数。

USB设备驱动程序是常说的设备固件程序的一部分,提供设备信息与主机的通信接口。

设备端USB驱动程序设计由以下几部分处理程序组成。

初始化例程:完成描述符指针、端点、配置改变等操作。

数据传输例程:完成控制传输、批量传输、中断传输及同步传输等传输方式下的数据收发工作。

Linux下的硬件驱动——USB设备(转载)

Linux下的硬件驱动——USB设备(转载)

Linux下的硬件驱动——USB设备(转载)usb_bulk_msg函数当对usb设备进⾏⼀次读或者写时,usb_bulk_msg 函数是⾮常有⽤的; 然⽽, 当你需要连续地对设备进⾏读/写时,建议你建⽴⼀个⾃⼰的urbs,同时将urbs 提交给usb⼦系统。

转载于此/a2003/0630/1056/000001056933.shtml Linux下的硬件驱动——USB设备(上)(驱动配置部分)USB设备越来越多,⽽Linux在硬件配置上仍然没有做到完全即插即⽤,对于Linux怎样配置和使⽤他们,也越来越成为困扰我们的⼀⼤问题。

本⽂着⼒从Linux系统下设备驱动的架构,去阐述怎样去使⽤和配置以及怎样编制USB设备驱动。

对于⼀般⽤户,可以使我们明晰Linux 设备驱动⽅式,为更好地配置和使⽤USB设备提供了⽅便;⽽对于希望开发Linux系统下USB设备驱动的程序员,提供了初步学习USB驱动架构的机会。

(), 联想软件设计中⼼嵌⼊式研发处系统设计⼯程师前⾔USB是英⽂"Universal Serial Bus"的缩写,意为"通⽤串⾏总线"。

是由Compaq(康柏)、DEC、IBM、Intel、NEC、微软以及Northern Telecom(北⽅电讯)等公司于1994年11⽉共同提出的,主要⽬的就是为了解决接⼝标准太多的弊端。

USB使⽤⼀个4针插头作为标准插头,并通过这个标准接头,采⽤菊花瓣形式把所有外设连接起来,它采⽤串⾏⽅式传输数据,⽬前最⼤数据传输率为12Mbps, ⽀持多数据流和多个设备并⾏操作,允许外设热插拔。

⽬前USB接⼝虽然只发展了2代(USB1.0/1.1,USB2.0),但是USB综合了⼀个多平台标准的所有优点 -- 包括降低成本,增加兼容性,可连接⼤量的外部设备,融合先进的功能和品质。

使其逐步成为PC接⼝标准,进⼊了⾼速发展期。

那么对于使⽤Linux系统,正确⽀持和配置常见的USB设备,就是其使⽤必不可少的关键⼀步。

Linux内核USB驱动架构 USB设备驱动架构

Linux内核USB驱动架构 USB设备驱动架构
} bLength:#define USB_DT_ENDPOINT_SIZE 7 或 #define USB_DT_ENDPOINT_AUDIO_SIZE 那是针对音频设备扩展的scriptorType:#define USB_DT_ENDPOINT 0x05 bEndpointAddress:bit0~bit3 表示的就是端点号,使用 0x0f 和它相与就可以得
1
五者关系图如下:
usb_device{} .descriptor *config *ep_in[] *ep_out[] ...... 设备
usb_device_descriptor{} 设备描述符
.bNumConfigurations usb_host_config{}[]
......
多个配置
usb_host_endpoint{}*[] ......
.bNumEndpoints
usb_host_endpoint{}[] ......
多个端点
usb_host_endpoint{} .desc .urb_list ...... 一个端点
usb_endpoint_descriptor{} 端点描述符
.bEndpointAddress .bmAttributes
3
成员变量及意义解说:
设备描述符 usb_device_descriptor{}
.bLength .bDescriptorType .bDeviceClass .bDeviceSubClass .bDeviceProtocol .bMaxPacketSize0 .idVendor .idProduct
1.每个端点拥有一个 urb_list 2.urb 的数据从端点进行收发 3.HCD 每收到一个 urb,就

Linux下USB设备驱动程序设计

Linux下USB设备驱动程序设计
[ 2 】 U n i v e r s a 1 S e r i a 1 B u s S p e c i f i c a t i o n
C Om P a q,I nt e1,Mi— C r o s oft,NEC R e vi Si o n 1 . 1 . S e p t e mb e r 2 3 . 1 9 9 8 .
1 U S B 总线原理
US B协 议是 1 9 9 4年底 由康柏 、I B M、英 特 尔等几家公司联合提 出来 的外部 总线接 口协 US B设 备 的设 备描 述 符 在 L i n u x系 统 中 用u s b d e v i c e d e s c i r p t o r结构 体 表示 ,它 描述
参考文献
[ 1 ] J o n a t h a n C o r b e t , A l e s s a n d r o R u b i n i , G r e g
K r o a h - H a r t m a n等 . L I N U X设 备 驱 动 程 序
[ M 】 . 北京 :中国电力 出 版社 , 2 0 0 6 .
表示。
i n u x系统 中由 u s b e n d p o i n t d e s c i r p t o r 结构体 个 US B系统 一般 是 由一个 US B主机 控 L 编写一个 US B驱动程 序,是从 u s b d r i v e r
_
【 3 ] 温卡特斯 瓦兰.精通 L i n u x 驱动程序 开发
[ M ] .北京 :人 民邮电出版 , 2 0 0 9 . [ 4 ]胡晓 军 ,张爱成 . U S B接 口卡发 技 术 [ M ] .
系 统中被广 泛使用。

Linux下的硬件驱动——USB设备

Linux下的硬件驱动——USB设备

Linux下的硬件驱动——USB设备什么是USB设备?USB即Universal Serial Bus,翻译过来就是通用串行总线。

它是一种规范化的、快速的、热插拔的串行输入/输出接口。

USB接口常被用于连接鼠标、键盘、打印机、扫描仪、音频设备、存储设备等外围设备。

Linux下的USB驱动在Linux系统中,每个USB设备都需要一个相应的驱动程序来驱动。

从Linux 2.4开始,内核提供了完整的USB设备支持。

对于每个USB设备,内核都会自动加载对应的驱动程序。

Linux下的USB设备驱动程序主要分为以下几个部分:USB核心驱动程序USB核心驱动程序是操作系统内核中处理USB设备的核心模块,负责与各种类型的USB设备进行通信,包括主机控制器、USB总线、USB设备等。

它与驱动程序和应用程序之间起到了桥梁的作用,为驱动程序提供了USB设备的基础支持。

USB设备驱动程序USB设备驱动程序是与特定USB设备相对应的驱动程序,为USB设备提供具体的读写功能和其他控制功能。

USB核心驱动程序和USB设备驱动程序之间的接口USB核心驱动程序和USB设备驱动程序之间的接口是指USB层和应用程序层之间的接口,负责传递各种USB操作的命令和数据。

如何编译一个USB设备驱动编译一个USB设备驱动程序需要按照以下步骤进行:步骤一:安装必要的软件包首先需要安装编译和调试USB设备驱动所需的软件包,包括编译工具链、内核源代码、内核头文件等。

sudo apt-get install build-essential linux-source linux-headers-`una me -r`步骤二:编写代码现在可以编写USB设备驱动程序的代码,此处不做详细介绍。

步骤三:编译代码在终端窗口中进入USB设备驱动程序所在的目录下,输入以下命令进行编译:make此命令将会编译USB设备驱动程序,并生成一个将驱动程序与内核进行连接的模块文件。

Linux设备驱动之USBhub驱动

Linux设备驱动之USBhub驱动

Linux设备驱动之USB hub驱动Linux设备驱动之USB hub驱动------------------------------------------本文系本站原创,欢迎!请注明出处:------------------------------------------一:前言继UHCI的驱动之后,我们对USB Control的运作有了一定的了解.在接下来的分析中,我们对USB设备的驱动做一个全面的分析,我们先从HUB的驱动说起.关于HUB,usb2.0 spec上有详细的定义,基于这部份的代码位于linux-2.6.25/drivers/usb/core下,也就是说,这部份代码是位于core下,和具体设备是无关的,因为各厂商的hub都是按照spec的要求来设计的.二:UHCI驱动中的root hub记得在分析UHCI驱动的时候,曾详细分析过root hub的初始化操作.为了分析方便,将代码片段列出如下:usb_add_hcd() à usb_alloc_dev():struct usb_device *usb_alloc_dev(struct usb_device *parent,struct usb_bus *bus, unsigned port1){…………//usb_device,内嵌有struct device结构,对这个结构进行初始化device_initialize(&dev->dev);dev->dev.bus = &usb_bus_type;dev->dev.type = &usb_device_type;…………}一看到前面对dev的赋值,根据我们对设备模型的理解,一旦这个device进行注册,就会发生driver和device的匹配过程了.不过,现在还不是分析这个过程的时候,我们先来看一下,USB子系统中的两种驱动.三:USB子系统中的两种驱动linux-2.6.25/drivers/usb/core/driver.c中,我们可以找到两种register driver的方式,分别为usb_register_driver()和usb_register_device_driver().分别来分析一下这两个接口.usb_register_device_driver()接口的代码如下:int usb_register_device_driver(struct usb_device_driver *new_udriver,struct module *owner){int retval = 0;if (usb_disabled())return -ENODEV;new_udriver->drvwrap.for_devices = 1;new_udriver-> = (char *) new_udriver->name;new_udriver->drvwrap.driver.bus = &usb_bus_type;new_udriver->drvwrap.driver.probe = usb_probe_device;new_udriver->drvwrap.driver.remove = usb_unbind_device;new_udriver->drvwrap.driver.owner = owner;retval = driver_register(&new_udriver->drvwrap.driver);if (!retval) {pr_info(“%s: registered new device driver %s\n”,usbcore_name, new_udriver->name);usbfs_update_special();} else {printk(KERN_ERR “%s: error %d registering device ““ driver %s\n”,usbcore_name, retval, new_udriver->name);}return retval;}首先,通过usb_disabled()来判断一下usb是否被禁用,如果被禁用,当然就不必执行下面的流程了,直接退出即可.从上面的代码,很明显可以看到, struct usb_device_driver 对struct device_driver进行了一次封装,我们注意一下这里的赋值操作:new_udriver->drvwrap.for_devices = 1.等等.这些在后面都是用派上用场的.usb_register_driver()的代码如下:int usb_register_driver(struct usb_driver *new_driver, struct module *owner, const char *mod_name){int retval = 0;if (usb_disabled())return -ENODEV;new_driver->drvwrap.for_devices = 0;new_driver-> = (char *) new_driver->name;new_driver->drvwrap.driver.bus = &usb_bus_type;new_driver->drvwrap.driver.probe = usb_probe_interface;new_driver->drvwrap.driver.remove = usb_unbind_interface;new_driver->drvwrap.driver.owner = owner;new_driver->drvwrap.driver.mod_name = mod_name;spin_lock_init(&new_driver->dynids.lock);INIT_LIST_HEAD(&new_driver->dynids.list);retval = driver_register(&new_driver->drvwrap.driver);if (!retval) {pr_info(“%s: registered new interface dr iver %s\n”,usbcore_name, new_driver->name);usbfs_update_special();usb_create_newid_file(new_driver);} else {printk(KERN_ERR “%s: error %d registering interface ““ driver %s\n”,usbcore_name, retval, new_driver->name);}return retval;}很明显,在这里接口里,将new_driver->drvwrap.for_devices设为了0.而且两个接口的porbe()函数也不一样.其实,对于usb_register_driver()可以看作是usb设备中的接口驱动,而usb_register_device_driver()是一个单纯的USB设备驱动.四: hub的驱动分析4.1: usb_bus_type->match()的匹配过程usb_bus_type->match()用来判断驱动和设备是否匹配,它的代码如下:static int usb_device_match(struct device *dev, struct device_driver *drv){/* 整理by *///usb device的情况if (is_usb_device(dev)) {/* interface drivers never match devices */ if (!is_usb_device_driver(drv))return 0;/* TODO: Add real matching code */ return 1;}//interface的情况else {struct usb_interface *intf;struct usb_driver *usb_drv;const struct usb_device_id *id;/*整理by */if (is_usb_device_driver(drv))return 0;intf = to_usb_interface(dev);usb_drv = to_usb_driver(drv);id = usb_match_id(intf, usb_drv->id_table);if (id)return 1;id = usb_match_dynamic_id(intf, usb_drv);if (id)return 1;}return 0;}这里的match会区分上面所说的两种驱动,即设备的驱动和接口的驱动. is_usb_device()的代码如下:static inline int is_usb_device(const struct device *dev){return dev->type == &usb_device_type;}很明显,对于root hub来说,这个判断是肯定会满足的.static inline int is_usb_device_driver(struct device_driver *drv){return container_of(drv, struct usbdrv_wrap, driver)->for_devices;}回忆一下,我们在分析usb_register_device_driver()的时候,不是将new_udriver->drvwrap.for_devices置为了1么?所以对于usb_register_device_driver()注册的驱动来说,这里也是会满足的.因此,对应root hub的情况,从第一个if就会匹配到usb_register_device_driver()注册的驱动.对于接口的驱动,我们等遇到的时候再来进行分析.4.2:root hub的驱动入口既然我们知道,root hub会匹配到usb_bus_type->match()的驱动,那这个驱动到底是什么呢?我们从usb子系统的初始化开始说起.在linux-2.6.25/drivers/usb/core/usb.c中.有这样的一段代码:subsys_initcall(usb_init);对于subsys_initcall()我们已经不陌生了,在很多地方都会遇到它.在系统初始化的时候,会调用到它对应的函数.在这里,即为usb_init().在usb_init()中,有这样的代码片段:static int __init usb_init(void){…………retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);if (!retval)goto out;……}在这里终于看到usb_register_device_driver()了. usb_generic_driver会匹配到所有usb 设备.定义如下:struct usb_device_driver usb_generic_driver = {.name = “usb”,.probe = generic_probe,.disconnect = generic_disconnect,#ifdefCONFIG_PM.suspend = generic_suspend,.resume = generic_resume,#endif.supports_autosuspend = 1,};现在是到分析probe()的时候了.我们这里说的并不是usb_generic_driver中的probe,而是封装在struct usb_device_driver中的driver对应的probe函数.在上面的分析, usb_register_device_driver()将封装的driver的probe()函数设置为了usb_probe_device().代码如下:static int usb_probe_device(struct device *dev){struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);struct usb_device *udev;int error = -ENODEV;dev_dbg(dev, “%s\n”, __FUNCTION__);//再次判断dev是否是usb deviceif (!is_usb_device(dev)) /* Sanity check */return error;udev = to_usb_device(dev);/* TODO: Add real matching code *//* The device should always appear to be in use* unless the driver suports autosuspend.*///pm_usage_t: autosuspend计数.如果此计数为1,则不允许autosuspendudev->pm_usage_t = !(udriver->supports_autosuspend);error = udriver->probe(udev);return error;}首先,可以通过container_of()将封装的struct device, struct device_driver转换为struct usb_device和struct usb_device_driver.然后,再执行一次安全检查,判断dev是否是属于一个usb device.在这里,我们首次接触到了hub suspend.如果不支持suspend(udriver->supports_autosuspend为0),则udev->pm_usage_t被设为1,也就是说,它不允许设备suspend.否则,将其初始化为0. 最后,正如你所看到的,流程转入到了usb_device_driver->probe().对应到root hub,流程会转入到generic_probe().代码如下:static int generic_probe(struct usb_device *udev){int err, c;/* put device-specific files into sysfs */usb_create_sysfs_dev_files(udev);/* Choose and set the configuration.This registers the interfaces* with the driver core and lets interface drivers bind to them.*/if (udev->authorized == 0)dev_err(&udev->dev, “Device is not authorized for usage\n”);else {//选择和设定一个配置c = usb_choose_configuration(udev);if (c >= 0) {err = usb_set_configuration(udev, c);if (err) {dev_err(&udev->dev, “can’t set config #%d, error %d\n”,c, err);/* This need not be fatal.The user can try to* set other configurations. */}}}/* USB device state == configured ... usable */usb_notify_add_device(udev);return 0;}usb_create_sysfs_dev_files()是在sysfs中显示几个属性文件,不进行详细分析,有兴趣的可以结合之前分析的>来看下代码.usb_notify_add_device()是有关notify链表的操作,这里也不做详细分析.至于udev->authorized,在root hub的初始化中,是会将其初始化为1的.后面的逻辑就更简单了.为root hub 选择一个配置然后再设定这个配置.还记得我们在分析root hub的时候,在usb_new_device()中,会将设备的所有配置都取出来,然后将它们放到了usb_device-> config.现在这些信息终于会派上用场了.不太熟悉的,可以看下本站之前有关usb控制器驱动的文档.Usb2.0 spec上规定,对于hub设备,只能有一个config,一个interface,一个endpoint.实际上,在这里,对hub的选择约束不大,反正就一个配置,不管怎么样,选择和设定都是这个配置.不过,为了方便以后的分析,我们还是跟进去看下usb_choose_configuration()和usb_set_configuration()的实现.实际上,经过这两个函数之后,设备的probe()过程也就会结束了.4.2.1:usb_choose_configuration()函数分析usb_choose_configuration()的代码如下://为usb device选择一个合适的配置int usb_choose_configuration(struct usb_device *udev){int i;int num_configs;int insufficient_power = 0;struct usb_host_config *c, *best;best = NULL;//config数组c = udev->config;//config项数num_configs = udev->descriptor.bNumConfigurations;//遍历所有配置项for (i = 0; istruct usb_interface_descriptor *desc = NULL;/* It’s possible that a config has no interfaces! *///配置项的接口数目//取配置项的第一个接口if (c->desc.bNumInterfaces > 0)desc = &c->intf_cache[0]->altsetting->desc;/** HP’s USB bus-powered keyboard has only one configuration * and it claims to be self-powered; other devices may have* similar errors in their descriptors.If the next test* were allowed to execute, such configurations would always* be rejected and the devices would not work as expected.* In the meantime, we run the risk of selecting a config* that requires external power at a time when that power* isn’t available.It seems to be the lesser of two evils.** Bugzilla #6448 reports a device that appears to crash* when it receives a GET_DEVICE_STATUS request!We don’t * have any other way to tell whether a device is self-powered,* but since we don’t use that information anywhere but here,* the call has been removed.** Maybe the GET_DEVICE_STATUS call and the test below can* be reinstated when device firmwares bee more reliable.* Don’t hold your breath.*/#if 0/* Rule out self-powered configs for a bus-powered device */ if (bus_powered && (c->desc.bmAttributes &USB_CONFIG_ATT_SELFPOWER))continue;#endif/** The next test may not be as effective as it should be.* Some hubs have errors in their descriptor, claiming* to be self-powered when they are really bus-powered.* We will overestimate the amount of current such hubs* make available for each port.** This is a fairly benign sort of failure.It won’t* cause us to reject configurations that we should have* accepted.*//* Rule out configs that draw too much bus current *///电源不足.配置描述符中的电力是所需电力的1/2if (c->desc.bMaxPower * 2 > udev->bus_mA) {insufficient_power++;continue;}/* When the first config’s first interface is one of Microsoft’s* pet nonstandard Ethernet-over-USB protocols, ignore it unless* this kernel has enabled the necessary host side driver.*/if (i == 0 && desc && (is_rndis(desc) || is_activesync(desc))) {#if !defined(CONFIG_USB_NET_RNDIS_HOST) && !defined(CONFIG_USB_NET_RNDIS_HOST_MODULE)continue;#elsebest = c;#endif}/* From the remaining configs, choose the first one whose* first interface is for a non-vendor-specific class.* Reason: Linux is more likely to have a class driver* than a vendor-specific driver. *///选择一个不是USB_CLASS_VENDOR_SPEC的配置else if (udev->descriptor.bDeviceClass !=USB_CLASS_VENDOR_SPEC &&(!desc || desc->bInterfaceClass !=USB_CLASS_VENDOR_SPEC)) {best = c;break;}/* If all the remaining configs are vendor-specific,* choose the first one. */else if (!best)best = c;}if (insufficient_power > 0)dev_info(&udev->dev, “rejected %d configuration%s ““due to insufficient available bus power\n”,insufficient_power, plural(insufficient_power));//如果选择好了配置,返回配置的序号,否则,返回-1if (best) {i = best->desc.bConfigurationValue;dev_info(&udev->dev,“configuration #%d chosen from %d choice%s\n”,i, num_configs, plural(num_configs));} else {i = -1;dev_warn(&udev->dev,“no configuration chosen from %d choice%s\n”,num_configs, plural(num_configs));}return i;}Linux按照自己的喜好选择好了配置之后,返回配置的序号.不过对于HUB来说,它有且仅有一个配置.4.2.2:usb_set_configuration()函数分析既然已经选好配置了,那就告诉设备选好的配置,这个过程是在usb_set_configuration()中完成的.它的代码如下:int usb_set_configuration(struct usb_device *dev, int configuration){int i, ret;struct usb_host_config *cp = NULL;struct usb_interface **new_interfaces = NULL;int n, nintf;if (dev->authorized == 0 || configuration == -1) configuration = 0;else {for (i = 0; i descriptor.bNumConfigurations; i++) {if (dev->config.desc.bConfigurationValue ==configuration) {cp = &dev->config;break;}}}if ((!cp && configuration != 0))return -EINV AL;/* The USB spec says configuration 0 means unconfigured. * But if a device includes a configuration numbered 0,* we will accept it as a correctly configured state.* Use -1 if you really want to unconfigure the device.*/if (cp && configuration == 0)dev_warn(&dev->dev, “config 0 descriptor??\n”);首先,根据选择好的配置号找到相应的配置,在这里要注意了, dev->config[]数组中的配置并不是按照配置的序号来存放的,而是按照遍历到顺序来排序的.因为有些设备在发送配置描述符的时候,并不是按照配置序号来发送的,例如,配置2可能在第一次GET_CONFIGURATION 就被发送了,而配置1可能是在第二次GET_CONFIGURATION才能发送.取得配置描述信息之后,要对它进行有效性判断,注意一下本段代码的最后几行代码:usb2.0 spec上规定,0号配置是无效配置,但是可能有些厂商的设备并末按照这一约定,所以在linux 中,遇到这种情况只是打印出警告信息,然后尝试使用这一配置./* Allocate memory for new interfaces before doing anything else,* so that if we run out then nothing will have changed. */n = nintf = 0;if (cp) {//接口总数nintf = cp->desc.bNumInterfaces;//interface指针数组,new_interfaces = kmalloc(nintf * sizeof(*new_interfaces),GFP_KERNEL);if (!new_interfaces) {dev_err(&dev->dev, “Out of memory\n”);return -ENOMEM;}for (; nnew_interfaces[n] = kzalloc(sizeof(struct usb_interface),GFP_KERNEL);if (!new_interfaces[n]) {dev_err(&dev->dev, “Out of memory\n”);ret = -ENOMEM;free_interfaces:while (--n >= 0)kfree(new_interfaces[n]);kfree(new_interfaces);return ret;}}//如果总电源小于所需电流,打印警告信息i = dev->bus_mA - cp->desc.bMaxPower * 2;if (idev_warn(&dev->dev, “new config #%d exceeds power ““limit by %dmA\n”,configuration, -i);}在这里,注要是为new_interfaces分配空间,要这意的是, new_interfaces是一个二级指针,它的最终指向是struct usb_interface结构.特别的,如果总电流数要小于配置所需电流,则打印出警告消息.实际上,这种情况在usb_choose_configuration()中已经进行了过滤./* Wake up the device so we can send it the Set-Config request */ //要对设备进行配置了,先唤醒它ret = usb_autoresume_device(dev);if (ret)goto free_interfaces;/* if it’s already configured, clear out old state first.* getting rid of old interfaces means unbinding their drivers.*///不是处于ADDRESS状态,先清除设备的状态if (dev->state != USB_STATE_ADDRESS)usb_disable_device(dev, 1); /* Skip ep0 *///发送控制消息,选取配置ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),USB_REQ_SET_CONFIGURATION, 0, configuration, 0,NULL, 0, USB_CTRL_SET_TIMEOUT);if (ret/* All the old state is gone, so what else can we do?* The device is probably useless now anyway.*/cp = NULL;}//dev->actconfig存放的是当前设备选取的配置dev->actconfig = cp;if (!cp) {usb_set_device_state(dev, USB_STA TE_ADDRESS);usb_autosuspend_device(dev);goto free_interfaces;}//将状态设为CONFIGUREDusb_set_device_state(dev, USB_STA TE_CONFIGURED);接下来,就要对设备进行配置了,首先,将设备唤醒.回忆一下我们在分析UHCI驱动时,列出来的设备状态图.只有在ADDRESS状态才能转入到CONFIG状态.(SUSPEND状态除外). 所以,如果设备当前不是处于ADDRESS状态,就需要将设备的状态初始化.usb_disable_device()函数是个比较重要的操作,在接下来再对它进行详细分析.接着,发送SET_CONFIGURA TION的Control消息给设备,用来选择配置最后,将dev->actconfig指向选定的配置,将设备状态设为CONFIG/* Initialize the new interface structures and the* hc/hcd/usbcore interface/endpoint state.*///遍历所有的接口for (i = 0; istruct usb_interface_cache *intfc;struct usb_interface *intf;struct usb_host_interface *alt;cp->interface = intf = new_interfaces;intfc = cp->intf_cache;intf->altsetting = intfc->altsetting;intf->num_altsetting = intfc->num_altsetting;//是否关联的接口描述符,定义在minor usb 2.0 spec中intf->intf_assoc = find_iad(dev, cp, i);kref_get(&intfc->ref);//选择0号设置alt = usb_altnum_to_altsetting(intf, 0);/* No altsetting 0?We’ll assume the first altsetting.* We could use a GetInterface call, but if a device is* so non-pliant that it doesn’t have altsetting 0* then I would n’t trust its reply anyway.*///如果0号设置不存在,选排在第一个设置if (!alt)alt = &intf->altsetting[0];//当前的配置intf->cur_altsetting = alt;usb_enable_interface(dev, intf);intf->dev.parent = &dev->dev;intf->dev.driver = NULL;intf->dev.bus = &usb_bus_type;intf->dev.type = &usb_if_device_type;intf->dev.dma_mask = dev->dev.dma_mask;device_initialize(&intf->dev);mark_quiesced(intf);sprintf(&intf->dev.bus_id[0], “%d-%s:%d.%d”,dev->bus->busnum, dev->devpath,configuration, alt->desc.bInterfaceNumber);}kfree(new_interfaces);if (cp->string == NULL)cp->string = usb_cache_string(dev, cp->desc.iConfiguration);之前初始化的new_interfaces在这里终于要派上用场了.初始化各接口,从上面的初始化过程中,我们可以看出:Intf->altsetting,表示接口的各种设置Intf->num_altsetting:表示接口的设置数目Intf->intf_assoc:接口的关联接口(定义于minor usb 2.0 spec)Intf->cur_altsetting:接口的当前设置.结合之前在UHCI中的分析,我们总结一下:Usb_dev->config,其实是一个数组,存放设备的配置.usb_dev->config[m]-> interface[n]表示第m个配置的第n个接口的intercace结构.(m,bsp; dev->bus->busnum, dev->devpath,configuration, alt->desc.bInterfaceNumber);dev指的是这个接口所属的usb_dev,结合我们之前在UHCI中关于usb设备命名方式的描述.可得出它的命令方式如下:USB总线号-设备路径:配置号.接口号.例如,在我的虚拟机上:[rootlocalhost devices]# pwd/sys/bus/usb/devices[rootlocalhost devices]# ls1-0:1.0usb1[rootlocalhost devices]#可以得知,系统只有一个usb control.1-0:1.0:表示,第一个usb cont意思上看来,它是标记接口为停止状态.它的”反函数”是mark_active().两个函数如下示:static inline void mark_active(struct usb_interface *f){f->is_active = 1;f->dev.power.power_state.event = PM_EVENT_ON;}static inline void mark_quiesced(struct usb_interface *f){f->is_active = 0;f->dev.power.power_state.event = PM_EVENT_SUSPEND; }从代码看来,它只是对接口的活动标志(is_active)进行了设置./* Now that all the interfaces are set up, register them* to trigger binding of drivers to interfaces.probe()* routines may install different altsettings and may* claim() any interfaces not yet bound.Many class drivers* need that: CDC, audio, video, etc.*///注册每一个接口?for (i = 0; istruct usb_interface *intf = cp->interface;dev_dbg(&dev->dev,“addi ng %s (config #%d, interface %d)\n”,intf->dev.bus_id, configuration,intf->cur_altsetting->desc.bInterfaceNumber);ret = device_add(&intf->dev);if (ret != 0) {dev_err(&dev->dev, “device_add(%s) --> %d\n”,intf->dev.bus_id, ret);continue;}usb_create_sysfs_intf_files(intf);}//使设备suspendusb_autosuspend_device(dev);return 0;}最后,注册intf内嵌的device结构.设备配置完成了,为了省电,可以将设备置为SUSPEND状态.这个函数中还有几个比较重要的子函数,依次分析如下:1: usb_disable_device()函数.顾名思义,这个函数是将设备disable掉.代码如下:void usb_disable_device(struct usb_device *dev, int skip_ep0){int i;dev_dbg(&dev->dev, “%s nuking %s URBs\n”, __FUNCTION__, skip_ep0 ? “non-ep0” : “all”);for (i = skip_ep0; iusb_disable_endpoint(dev, i);usb_disable_endpoint(dev, i + USB_DIR_IN);}dev->toggle[0] = dev->toggle[1] = 0;/* getting rid of interfaces will disconnect* any drivers bound to them (a key side effect)*/if (dev->actconfig) {for (i = 0; i actconfig->desc.bNumInterfaces; i++) {struct usb_interface *interface;/* remove this interface if it has been registered */interface = dev->actconfig->interface;if (!device_is_registered(&interface->dev))continue;dev_dbg(&dev->dev, “unregistering interface %s\n”,interface->dev.bus_id);usb_remove_sysfs_intf_files(interface);device_del(&interface->dev);}/* Now that the interfaces are unbound, nobody should* try to access them.*/for (i = 0; i actconfig->desc.bNumInterfaces; i++) {put_device(&dev->actconfig->interface->dev);dev->actconfig->interface = NULL;}dev->actconfig = NULL;if (dev->state == USB_STATE_CONFIGURED)usb_set_device_state(dev, USB_STATE_ADDRESS);}}第二个参数是skip_ep0.是表示是否跳过ep0.为1表示跳过,为0表示清除掉设备中的所有endpoint.这个函数可以分为两个部份,一部份是对usb_dev中的endpoint进行操作,一方面是释放usb_dev的选定配置项.对于第一部份:从代码中可能看到,如果skip_ep0为1,那就是从1开始循环,所以,就跳过了ep0.另外,一个端点号对应了两个端点,一个IN,一个OUT.IN端点比OUT端点要大USB_DIR_IN.另外,既然设备都已经被禁用了,那toggle也应该回归原位了.因些将两个方向的toggle都设为0. usb_disable_endpoint()是一个很有意思的处理.它的代码如下:void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr){unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;struct usb_host_endpoint *ep;if (!dev)return;//在dev->ep_out和dev->ep_in删除endpointif (usb_endpoint_out(epaddr)) {ep = dev->ep_out[epnum];dev->ep_out[epnum] = NULL;} else {ep = dev->ep_in[epnum];dev->ep_in[epnum] = NULL;}//禁用掉此ep.包括删除ep上提交的urb 和ep上的QHif (ep) {ep->enabled = 0;usb_hcd_flush_endpoint(dev, ep);usb_hcd_disable_endpoint(dev, ep);}}在dev->ep_in[]/dev->ep_out[]中删除endpoint,这点很好理解.比较难以理解的是后面的两个操作,即usb_hcd_flush_endpoint()和usb_hcd_disable_endpoint().根据之前分析的UHCI的驱动,我们得知,对于每个endpoint都有一个传输的qh,这个qh上又挂上了要传输的urb.因此,这两个函数一个是删除urb,一个是删除qh.usb_hcd_flush_endpoint()的代码如下:void usb_hcd_flush_endpoint(struct usb_device *udev,struct usb_host_endpoint *ep){struct usb_hcd *hcd;struct urb *urb;if (!ep)return;might_sleep();hcd = bus_to_hcd(udev->bus);/* No more submits can occur *///在提交urb时,将urb加到ep->urb_list上的时候要持锁//因此,这里持锁的话,无法发生中断和提交urbspin_lock_irq(&hcd_urb_list_lock);rescan://将挂在ep->urb_list上的所有urb unlink.注意这里unlink一般只会设置urb->unlinked的//值,不会将urb从ep->urb_list上删除.只有在UHCI的中断处理的时候,才会调用//uhci_giveback_urb()将其从ep->urb_list中删除list_for_each_entry (urb, &ep->urb_list, urb_list) {int is_in;if (urb->unlinked)continue;usb_get_urb (urb);is_in = usb_urb_dir_in(urb);spin_unlock(&hcd_urb_list_lock);/* kick hcd */unlink1(hcd, urb, -ESHUTDOWN);dev_dbg (hcd->self.controller,“shutdown urb %p ep%d%s%s\n”,urb, usb_endpoint_num(&ep->desc),is_in ? “in” : “out”,({char *s;switch (usb_endpoint_type(&ep->desc)) {case USB_ENDPOINT_XFER_CONTROL:s = ““; break;case USB_ENDPOINT_XFER_BULK:s = “-bulk”; break;case USB_ENDPOINT_XFER_INT:s = “-intr”; break;default:s = “-iso”; break;};s;}));usb_put_urb (urb);/* list contents may have changed *///在这里解开锁了,对应ep->urb_list上又可以提交urb. //这里释放释的话,主要是为了能够产生中断spin_lock(&hcd_urb_list_lock);goto rescan;}spin_unlock_irq(&hcd_urb_list_lock);/* Wait until the endpoint queue is pletely empty *///等待urb被调度完while (!list_empty (&ep->urb_list)) {spin_lock_irq(&hcd_urb_list_lock);/* The list may have changed while we acquired the spinlock */urb = NULL;if (!list_empty (&ep->urb_list)) {urb = list_entry (ep->urb_list.prev, struct urb,urb_list);usb_get_urb (urb);}spin_unlock_irq(&hcd_urb_list_lock);if (urb) {usb_kill_urb (urb);usb_put_urb (urb);}}}仔细体会这里的代码,为什么在前一个循环中,要使用goto rescan重新开始这个循环呢?这是因为在后面已经将自旋锁释放了,因此,就会有这样的可能,在函数中操作的urb,可能已经被调度完释放了.因此,再对这个urb操作就会产生错误.所以,需要重新开始这个循环.那后一个循环又是干什么的呢?后一个循环就是等待urb被调度完.有人就会有这样的疑问了,这里一边等待,然后endpoint一边还提交urb,那这个函数岂不是要耗掉很长时间?在这里,不要忘记了前面的操作,在调这个函数之前, usb_disable_endpoint()已经将这个endpoint禁用了,也就是说该endpoint不会产生新的urb.因为,在后一个循环中,只需要等待那些被unlink的urb调度完即可.在usb_kill_urb()中,会一直等待,直到这个urb被调度完成为止.可能有人又会有这样的疑问:Usb_kill_urb()中也有unlink urb的操作,为什么这里要分做两个循环呢?另外的一个函数是usb_hcd_disable_endpoint().代码如下:void usb_hcd_disable_endpoint(struct usb_device *udev,struct usb_host_endpoint *ep){struct usb_hcd *hcd;might_sleep();hcd = bus_to_hcd(udev->bus);if (hcd->driver->endpoint_disable)hcd->driver->endpoint_disable(hcd, ep);}从上面的代码可以看到,操作转向了hcd->driver的endpoint_disable()接口.以UHCI为例.在UHCI中,对应的接口为:static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd,struct usb_host_endpoint *hep){struct uhci_hcd *uhci = hcd_to_uhci(hcd); struct uhci_qh *qh;spin_lock_irq(&uhci->lock);qh = (struct uhci_qh *) hep->hcpriv;if (qh == NULL)goto done;while (qh->state != QH_STA TE_IDLE) { ++uhci->num_waiting;spin_unlock_irq(&uhci->lock);wait_event_interruptible(uhci->waitqh, qh->state == QH_STATE_IDLE); spin_lock_irq(&uhci->lock);--uhci->num_waiting;}uhci_free_qh(uhci, qh);done:spin_unlock_irq(&uhci->lock);}这个函数没啥好说的,就是在uhci->waitqh上等待队列状态变为QH_STATE_IDLE.来回忆一下,qh在什么情况下才会变为QH_STATE_IDLE呢? 是在qh没有待传输的urb的时候.然后,将qh释放.现在我们来接着看usb_disable_device()的第二个部份.第二部份主要是针对dev->actconfig进行的操作, dev->actconfig存放的是设备当前的配置,现在要将设备设回Address状态.就些东西当然是用不了上的了.释放dev->actconfig->interface[]中的各元素,注意不要将dev->actconfig->interface[]所指向的信息释放了,它都是指向dev->config[]-> intf_cache[]中的东西,这些东西一释放,usb device在Get_ Configure所获得的信息就会部丢失了.就这样, usb_disable_device()函数也走到了尾声.2: usb_cache_string()函数这个函数我们在分析UHCI的时候已经接触过,但末做详细的分析.首先了解一下这个函数的作用,有时候,为了形象的说明,会提供一个字符串形式的说明.例如,对于配置描述符来说,它的iConfiguration就表示一个字符串索引,然后用Get_String就可以取得这个索引所对应的字串了.不过,事情并不是这么简单.因为字符串对应不同的编码,所以这里还会对应有编码的处理.来看具体的代码:char *usb_cache_string(struct usb_device *udev, int index){char *buf;char *smallbuf = NULL;int len;if (indexreturn NULL;//不知道字符到底有多长,就按最长256字节处理buf = kmalloc(256, GFP_KERNEL);if (buf) {len = usb_string(udev, index, buf, 256);//取到字符了,分配合适的长度if (len > 0) {smallbuf = kmalloc(++len, GFP_KERNEL);if (!smallbuf)return buf;//将字符copy过去memcpy(smallbuf, buf, len);}//释放旧空间kfree(buf);}return smallbuf;}这个函数没啥好说的,流程转入到usb_string中.代码如下:int usb_string(struct usb_device *dev, int index, char *buf, size_t size) {unsigned char *tbuf;int err;unsigned int u, idx;if (dev->state == USB_STATE_SUSPENDED)return -EHOSTUNREACH;if (sizereturn -EINV AL;buf[0] = 0;tbuf = kmalloc(256, GFP_KERNEL);if (!tbuf)return -ENOMEM;/* get langid for strings if it’s not yet known *///先取得设备支持的编码IDif (!dev->have_langid) {//以0号序号和编码0,Get_String就可得到设备所支持的编码列表err = usb_string_sub(dev, 0, 0, tbuf);//如果发生了错误,或者是取得的数据超短(最短为4字节)if (errdev_err(&dev->dev,“string descriptor 0 read error: %d\n”,err);goto errout;} else if (errdev_err(&dev->dev, “string desc riptor 0 too short\n”);err = -EINV AL;goto errout;}//取设备支持的第一个编码else {dev->have_langid = 1;dev->string_langid = tbuf[2] | (tbuf[3]/* always use the first langid listed */dev_dbg(&dev->dev, “default language 0x%04x\n”,dev->string_langid);}}//以编码ID和序号Index作为参数Get_String取得序号对应的字串err = usb_string_sub(dev, dev->string_langid, index, tbuf);if (errgoto errout;//空一个字符来用来存放结束符size--; /* leave room for trailing NULL char in output buffer */ //两字节一组,(Unicode编码的)for (idx = 0, u = 2; uif (idx >= size)break;//如果高字节有值,说明它不是ISO-8859-1编码的,将它置为? //否则,就将低位的值存放到buf中if (tbuf[u+1]) /* high byte */buf[idx++] = ‘?’;/* non ISO-8859-1 character */elsebuf[idx++] = tbuf;}//在最后一位赋0,字串结尾buf[idx] = 0;//返回字串的长度,(算上了最后的结尾字符)err = idx;//如果该描述符不是STRING描述符,打印出错误提示if (tbuf[1] != USB_DT_STRING)dev_dbg(&dev->dev,“wrong descriptor type %02x for string %d (\”%s\”)\n”,tbuf[1], index, buf);。

linux usb wifi驱动开发原理

linux usb wifi驱动开发原理Linux USB WiFi驱动开发原理一、引言随着无线网络的普及,WiFi成为了人们生活中不可或缺的一部分。

而在Linux操作系统中,为了支持各种WiFi设备,需要进行对应的驱动开发。

本文将介绍Linux USB WiFi驱动开发的原理和过程。

二、USB WiFi驱动开发的基本原理1. USB接口USB(Universal Serial Bus)是一种通用的串行总线标准,用于连接计算机与外部设备。

USB WiFi设备通过USB接口与计算机通信,传输数据和控制命令。

2. 驱动程序驱动程序是用于操作和控制硬件设备的软件。

USB WiFi驱动程序负责与USB WiFi设备进行通信,实现数据的传输和接收。

驱动程序需要与操作系统紧密结合,通过操作系统提供的API接口与设备进行交互。

三、USB WiFi驱动开发的过程1. 设备识别与初始化USB WiFi设备插入计算机后,操作系统会通过USB子系统进行设备的识别和初始化。

在Linux系统中,USB设备的识别和初始化由USB核心驱动完成。

核心驱动会根据设备的VID(Vendor ID)和PID (Product ID)来匹配对应的驱动程序。

2. 驱动程序注册驱动程序需要在Linux系统中进行注册,以便系统能够正确识别和加载驱动。

注册过程通常包括向系统注册设备类型、设备ID等信息。

3. 设备操作接口的实现驱动程序需要实现设备操作接口,包括设备的打开、关闭、读取数据、写入数据等功能。

这些操作接口是通过USB子系统提供的API 来实现的。

4. 数据传输与控制USB WiFi驱动程序需要实现数据的传输和控制功能。

数据传输主要包括从设备读取数据和向设备写入数据,而控制功能包括设置设备参数、配置网络等操作。

5. 错误处理与调试在USB WiFi驱动开发中,错误处理和调试是非常重要的一部分。

驱动程序需要处理各种异常情况,如设备断开连接、传输错误等。

Linux内核USB驱动程序框架

25.2 USB驱动程序框架Linux内核提供了完整的USB驱动程序框架。

USB总线采用树形结构,在一条总线上只能有唯一的主机设备。

Linux内核从主机和设备两个角度观察USB总线结构。

本节介绍Linux内核USB驱动程序框架。

25.2.1 Linux内核USB驱动框架图25-2是Linux内核从主机和设备两个角度观察USB总线结构的示意图。

从图25-2中可以看出,Linux内核USB驱动是按照主机驱动和设备驱动两套体系实现的,下面介绍两套体系的结构和特点。

1.基本结构图25-2的左侧是主机驱动结构。

主机驱动的最底层是USB主机控制器,提供了OHCI/EHCI/UHCI这3种类型的总线控制功能。

在USB控制器的上一层是主机控制器的驱动,分别对应OHCI/EHCI/UHCI这3种类型的总线接口。

USB核心部分连接了USB控制器驱动和设备驱动,是两者之间的转换接口。

USB设备驱动层提供了各种设备的驱动程序。

USB主机部分的设计结构完全是从USB总线特点出发的。

在USB总线上可以连接各种不同类型的设备,包括字符设备、块设备和网络设备。

所有类型的USB设备都是用相同的电气接口,使用的传输协议也基本相同。

向用户提供某种特定类型的USB设备时,需要处理USB总线协议。

内核完成所有的USB总线协议处理,并且向用户提供编程接口。

图25-2 Linux内核USB总线结构图25-2右侧是设备驱动结构。

与USB主机类似,USB设备提供了相同的层次结构与之对应。

但是在USB设备一侧使用名为Gadget API的结构作为核心。

Gadget API是Linux内核实现的对应USB设备的核心结构。

Gadget API屏蔽了USB设备控制器的细节,控制具体的USB设备实现。

2.设备每个USB设备提供了不同级别的配置信息。

一个USB设备可以包含一个或多个配置,不同的配置使设备表现出不同的特点。

其中,设备的配置是通过接口组成的。

Linux内核定义了USB设备描述结构如下:struct usb_device_descriptor {__u8 bLength; // 设备描述符长度__u8 bDescriptorType; // 设备类型__le16 bcdUSB; // USB版本号(使用BCD编码)__u8 bDeviceClass; // USB设备类型__u8 bDeviceSubClass; // USB设备子类型__u8 bDeviceProtocol; // USB设备协议号__u8 bMaxPacketSize0; // 传输数据的最大包长__le16 idVendor; // 厂商编号__le16 idProduct; // 产品编号__le16 bcdDevice; // 设备出厂号__u8 iManufacturer; // 厂商字符串索引__u8 iProduct; // 产品字符串索引__u8 iSerialNumber; // 产品序列号索引__u8 bNumConfigurations; // 最大的配置数量} __attribute__ ((packed));从usb_device_descriptor结构定义看出,一个设备描述符定义了与USB设备有关的所有信息。

linux设备驱动(28)usb驱动开发过程总结

linux设备驱动(28)usb驱动开发过程总结设备驱动程序是操作系统内核和机器硬件之间的接⼝,由⼀组函数和⼀些私有数据组成,是应⽤程序和硬件设备之间的桥梁。

在应⽤程序看来,硬件设备只是⼀个设备⽂件,应⽤程序可以像操作普通⽂件⼀样对硬件设备进⾏操作。

设备驱动程序是内核的⼀部分,主要完成以下功能:对设备的初始化和释放;把数据从内核传送到硬件设备和从硬件设备读取数据;读取应⽤程序数据传送给设备⽂件和回送应⽤程序请求的数据;检测和处理硬件设备出现的错误。

1 Linux USB⼦系统分析在Linux系统中,USB主机驱动程序由3部分组成:USB主机控制器驱动(HCD)、USB核⼼驱动(USBD)和不同种类的USB设备类驱动,如下所⽰。

其中HCD和USBD被称为协议软件或者协议栈,这两部分共同处理与协议相关的操作。

USB设备类驱动可以包含多个,不同的功能接⼝对应不同的驱动程序,它们不直接与USB设备硬件打交道,⽽是通过协议软件的抽象处理来完成与设备的不同功能接⼝之间的通信。

在Linux USB⼦系统中,HCD是直接和硬件进⾏交互的软件模块,是USB协议栈的最底层部分,是USB主机控制器硬件和数据传输的⼀种抽象。

HCD向上仅对USB总线驱动程序服务,HCD提供了⼀个软件接⼝,即HCDI,使得各种USB主机控制器的硬件特性都被软件化,并受USB总线驱动程序的调⽤和管理。

HCD向下则直接管理和检测主控制器硬件的各种⾏为。

HCD提供的功能主要有:主机控制器硬件初始化;为USBD层提供相应的接⼝函数;提供根HUB(ROOT HUB)设备配置、控制功能;完成4种类型的数据传输等。

USBD部分是整个USB主机驱动的核⼼,主要实现的功能有:USB总线管理;USB总线设备管理、USB总线带宽管理、USB的4种类型数据传输、USB HUB驱动、为USB设备驱动提供相关接⼝、提供应⽤程序访问USB系统的⽂件接⼝等。

其中USB HUB作为⼀类特殊的USB设备,其驱动程序被包含在USBD层。

linux设备驱动程序编写流程

linux设备驱动程序编写流程编写Linux设备驱动程序的流程可以概括为以下几个步骤:1.了解设备及其硬件接口:在编写设备驱动程序之前,首先需要了解要驱动的设备及其硬件接口。

这包括设备的功能、操作方式、寄存器映射、中断、DMA等信息。

还需要查阅相关文档,如设备手册、硬件规格等,以了解设备的详细信息。

2.确定设备的类型:根据设备的特点,确定设备驱动程序的类型。

常见的设备类型包括字符设备、块设备、网络设备、USB设备等。

根据设备类型的不同,编写设备驱动程序的方式和要点也会有所不同。

3.创建设备驱动的数据结构:在Linux内核中,每个设备驱动都有一个对应的数据结构,用于描述设备驱动程序的属性和操作函数。

这个数据结构通常是一个结构体,其中包含设备名、设备ID、设备操作函数指针等。

4.分配和注册设备号:每个设备驱动程序在Linux系统中都需要有一个唯一的设备号,用于标识该设备。

设备号的分配可以使用动态方式,也可以使用静态方式,具体选择取决于需求。

获取设备号后,需要通过相应的函数将设备号与设备驱动程序关联起来。

5.实现设备的打开和关闭函数:设备的打开和关闭函数在设备被打开和关闭时调用,用于初始化和释放设备所需的资源。

这些函数通常包括初始化硬件,申请和释放I/O端口、中断、DMA等资源的操作。

6.实现设备读写函数:根据设备的特点和功能,实现设备的读和写操作函数。

读函数用于从设备读取数据,写函数用于向设备写入数据。

这些函数通常包括和硬件交互的操作,如读写寄存器、发送接收数据等。

7.处理设备中断:对于需要处理中断的设备,需要实现中断处理函数。

中断处理函数在设备产生中断时自动调用,用于响应和处理中断事件。

中断处理函数通常需要执行与中断相关的操作,如读写寄存器、处理数据等。

8.实现设备控制函数:设备控制函数用于处理设备的特殊操作,如配置设备参数、控制设备行为等。

这些函数通常被应用程序调用,用于与设备进行交互和控制。

9.注册设备驱动程序:将设备驱动程序注册到Linux内核中,使其能够被系统识别和使用。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Linux USB驱动工作流程标签:linux工作structdescriptor数据结构2012-06-15 10:18 3988人阅读评论(1) 收藏举报分类:Linux driver(35)USB(7)版权声明:本文为博主原创文章,未经博主允许不得转载。

1. USB主机在Linux驱动中,USB驱动处于最底层的是USB主机控制器硬件,在其之上运行的是USB主机控制器驱动,主机控制器之上为USB核心层,再上层为USB设备驱动层(插入主机上的U盘、鼠标、USB转串口等设备驱动)。

因此,在主机侧的层次结构中,要实现的USB驱动包括两类:USB主机控制器驱动和USB设备驱动,前者控制插入其中的USB设备,后者控制USB设备如何与主机通信。

Linux内核USB核心负责USB驱动管理和协议处理的主要工作。

主机控制器驱动和设备驱动之间的USB核心非常重要,其功能包括:通过定义一些数据结构、宏和功能函数,向上为设备驱动提供编程接口,向下为USB主机控制器驱动提供编程接口;通过全局变量维护整个系统的USB设备信息;完成设备热插拔控制、总线数据传输控制等。

2. USB设备Linux内核中USB设备侧驱动程序分为3个层次:UDC驱动程序、Gadget API和Gadget驱动程序。

UDC驱动程序直接访问硬件,控制USB设备和主机间的底层通信,向上层提供与硬件相关操作的回调函数。

当前Gadget API是UDC驱动程序回调函数的简单包装。

Gadget驱动程序具体控制USB设备功能的实现,使设备表现出“网络连接”、“打印机”或“USB Mass Storage”等特性,它使用Gadget API控制UDC实现上述功能。

Gadget API把下层的UDC驱动程序和上层的Gadget驱动程序隔离开,使得在Linux系统中编写USB设备侧驱动程序时能够把功能的实现和底层通信分离。

3. 在USB设备组织结构中,从上到下分为设备(device)、配置(config)、接口(interface)和端点(endpoint)四个层次。

USB设备程序绑定到接口上。

对于这四个层次的简单描述如下:设备通常具有一个或多个的配置配置经常具有一个或多个的接口接口没有或具有一个以上的端点4. USB通信最基本的形式是通过端点(USB端点分中断(Interrupt)、批量(Bulk)、等时(ISO)、控制(Control)四种,每种用途不同),USB 端点只能往一个方向传送数据,从主机到设备或者从设备到主机,端点可以看作是单向的管道(pipe)。

驱动程序把驱动程序对象注册到USB 子系统中,稍后再使用制造商和设备标识来判断是否已经安装了硬件。

USB核心使用一个列表(是一个包含制造商ID和设备号ID的一个结构体)来判断对于一个设备该使用哪一个驱动程序,热插拨脚本使用它来确定当一个特定的设备插入到系统时该自动执行哪一个驱动程序的Probe。

5. 数据结构1) USB设备:对应数据结构structusb_device2) 配置:structusb_host_config (任一时刻,只能有一个配置生效)3)USB接口:structusb_interface (USB 核心将其传递给USB设备驱动,并由USB设备驱动负责后续的控制。

一个USB接口代表一个基本功能,每个USB驱动控制一个接口。

所以一个物理上的硬件设备可能需要一个以上的驱动程序。

)4)端点: structusb_host_endpoint ,它所包含的真实端点信息在另一个结构中:structusb_endpoint_descriptor(端点描述符,包含所有的USB特定数据)。

6. USB端点分类USB 通讯的最基本形式是通过一个称为端点的东西。

一个USB端点只能向一个方向传输数据(从主机到设备(称为输出端点)或者从设备到主机(称为输入端点))。

端点可被看作一个单向的管道。

USB 端点有 4 种不同类型, 分别具有不同的数据传送方式:1) 控制CONTROL控制端点被用来控制对USB设备的不同部分访问. 通常用作配置设备、获取设备信息、发送命令到设备或获取设备状态报告。

这些端点通常较小。

每个 USB 设备都有一个控制端点称为"端点 0", 被 USB 核心用来在插入时配置设备。

USB协议保证总有足够的带宽留给控制端点传送数据到设备.2)中断INTERRUPT每当 USB 主机向设备请求数据时,中断端点以固定的速率传送小量的数据。

此为USB 键盘和鼠标的主要的数据传送方法。

它还用以传送数据到USB设备来控制设备。

通常不用来传送大量数据。

USB协议保证总有足够的带宽留给中断端点传送数据到设备.3) 批量BULK批量端点用以传送大量数据。

这些端点通常比中断端点大得多. 它们普遍用于不能有任何数据丢失的情况。

USB 协议不保证传输在特定时间范围内完成。

如果总线上没有足够的空间来发送整个BULK包,它被分为多个包进行传输。

这些端点普遍用于打印机、USB Mass Storage和USB 网络设备上。

4) 等时ISOCHRONOUS等时端点也批量传送大量数据, 但是这个数据不被保证能送达。

这些端点用在可以处理数据丢失的设备中,并且更多依赖于保持持续的数据流。

如音频和视频设备等等。

控制和批量端点用于异步数据传送,而中断和等时端点是周期性的。

这意味着这些端点被设置来在固定的时间连续传送数据,USB 核心为它们保留了相应的带宽。

7. endpointstruct usb_host_endpoint{struct usb_endpoint_descriptordesc;//端点描述符struct list_headurb_list;//此端点的URB对列,由USB核心维护void*hcpriv;struct ep_device*ep_dev;/* For sysfs info */unsignedchar*extra;/* Extra descriptors */int extralen;int enabled;};当调用USB设备驱动调用usb_submit_urb提交urb请求时,将调用intusb_hcd_link_urb_to_ep(structusb_hcd *hcd, structurb *urb)把此urb增加到urb_list的尾巴上。

(hcd: Host Controller Driver,对应数据结构structusb_hcd )8. urb所有USB通讯均为请求-->响应模式,USB设备不会主动向Host发送数据。

写数据:USB设备驱动发送urb请求给USB设备,USB 设备不需要回数据。

读数据:USB设备驱动发送urb请求给USB设备,USB设备需要回数据。

USB 设备驱动通过urb和所有的 USB 设备通讯。

urb用structurb 结构描述(include/linux/usb.h )。

urb 以一种异步的方式同一个特定USB设备的特定端点发送或接受数据。

一个 USB 设备驱动可根据驱动的需要,分配多个 urb 给一个端点或重用单个 urb 给多个不同的端点。

设备中的每个端点都处理一个urb 队列, 所以多个 urb 可在队列清空之前被发送到相同的端点。

一个 urb 的典型生命循环如下:(1)被创建;(2)被分配给一个特定 USB 设备的特定端点;(3)被提交给 USB 核心;(4)被 USB 核心提交给特定设备的特定 USB 主机控制器驱动;(5)被 USB 主机控制器驱动处理, 并传送到设备;(6)以上操作完成后,USB主机控制器驱动通知 USB 设备驱动。

urb 也可被提交它的驱动在任何时间取消;如果设备被移除,urb 可以被USB核心取消。

urb 被动态创建并包含一个内部引用计数,使它们可以在最后一个用户释放它们时被自动释放。

8.1 提交 urb一旦 urb 被正确地创建并初始化, 它就可以提交给 USB 核心以发送出到 USB 设备. 这通过调用函数sb_submit_urb 实现.intusb_submit_urb(structurb *urb, gfp_tmem_flags);参数:structurb *urb :指向被提交的 urb 的指针gfp_tmem_flags :使用传递给 kmalloc 调用同样的参数, 用来告诉USB 核心如何及时分配内存缓冲因为函数 usb_submit_urb 可被在任何时候被调用(包括从一个中断上下文), mem_flags 变量必须正确设置. 根据 usb_submit_urb 被调用的时间,只有 3 个有效值可用:GFP_ATOMIC只要满足以下条件,就应当使用此值:1) 调用者处于一个 urb 结束处理例程,中断处理例程,底半部,tasklet或者一个定时器回调函数.2) 调用者持有自旋锁或者读写锁. 注意如果正持有一个信号量, 这个值不必要.3) current->state 不是 TASK_RUNNING. 除非驱动已自己改变current 状态,否则状态应该一直是TASK_RUNNING .GFP_NOIO驱动处于块 I/O 处理过程中. 它还应当用在所有的存储类型的错误处理过程中.GFP_KERNEL所有不属于之前提到的其他情况在 urb 被成功提交给 USB 核心之后, 直到结束处理例程函数被调用前,都不能访问 urb 结构的任何成员8.2 urb结束处理例程如果 usb_submit_urb 被成功调用, 并把对 urb 的控制权传递给 USB 核心, 函数返回 0; 否则返回一个负的错误代码. 如果函数调用成功, 当 urb 被结束的时候结束处理例程会被调用一次.当这个函数被调用时, USB 核心就完成了这个urb, 并将它的控制权返回给设备驱动.只有3 种结束urb并调用结束处理例程的情况:(1)urb 被成功发送给设备, 且设备返回正确的确认.如果这样, urb 中的status变量被设置为 0.(2)发生错误, 错误值记录在 urb 结构中的 status 变量.(3)urb 从 USB 核心unlink. 这发生在要么当驱动通过调用usb_unlink_urb 或者 usb_kill_urb告知 USB 核心取消一个已提交的urb,或者在一个 urb 已经被提交给它时设备从系统中去除.9. 探测和断开在 structusb_driver 结构中, 有 2 个 USB 核心在适当的时候调用的函数:(1)当设备插入时, 如果 USB 核心认为这个驱动可以处理(USB核心使用一个列表(是一个包含制造商ID和设备号ID的一个结构体)来判断对于一个设备该使用哪一个驱动程序),则调用探测(probe)函数,探测函数检查传递给它的设备信息, 并判断驱动是否真正合适这个设备.(2)由于某些原因,设备被移除或驱动不再控制设备时,调用断开(disconnect)函数,做适当清理.探测和断开回调函数都在USB集线器内核线程上下文中被调用, 因此它们休眠是合法的. 为了缩短 USB 探测时间,大部分工作尽可能在设备打开时完成.这是因为 USB 核心是在一个线程中处理 USB 设备的添加和移除, 因此任何慢设备驱动都可能使 USB 设备探测时间变长。

相关文档
最新文档