Linux USB驱动框架分析

Linux USB驱动框架分析
Linux USB驱动框架分析

Linux USB驱动框架分析

初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结当然不可缺,更何况我决定为嵌入式卖命了。好,言归正传,我 说一说这段时间的收获,跟大家分享一下Linux的驱动开发。但这次只先针对Linux的USB子系统作分析,因为周五研讨老板催货。当然,还会顺带提一 下其他的驱动程序写法。

事实上,Linux的设备驱动都遵循一个惯例——表征驱动程序(用driver更贴切一些,应该称为驱动器比较好吧)的结构体,结构体里面应该包含了驱动 程序所需要的所有资源。用OO的术语来说,就是这个驱动器对象所拥有的属性及成员。由于Linux的内核用c来编写,所以我们也按照这种结构化的思想来分 析代码,但我还是希望从OO的角度来阐述这些细节。这个结构体的名字有驱动开发人员决定,比如说,鼠标可能有一个叫做mouse_dev的struct, 键盘可能由一个keyboard_dev的struct(dev for device,我们做的只是设备驱动)。而这次我们来分析一下Linux内核源码中的一个usb-skeleton(就是usb驱动的骨架咯),自然,他 定义的设备结构体就叫做usb-skel:

struct usb_skel {

struct usb_device * udev; /* the usb device for this device */

struct usb_interface * interface; /* the interface for this device */

struct semaphore limit_sem; /* limiting the number of writes in progress */

unsigned char * bulk_in_buffer; /* the buffer to receive data */

size_t bulk_in_size; /* the size of the

receive buffer */

__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */

__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */

struct kref kref;

};

这里我们得补充说明一下一些USB的协议规范细节。USB能够自动监测设备,并调用相应得驱 动程序处理设备,所以其规范实际上是相当复杂的,幸好,我们不必理会大部分细节问题,因为Linux已经提供相应的解决方案。就我现在的理解来说,USB 的驱动分为两块,一块是USB的bus驱动,这个东西,Linux内核已经做好了,我们可以不管,但我们至少要了解他的功能。形象得说,USB的bus驱 动相当于铺出一条路来,让所有的信息都可以通过这条USB通道到达该到的地方,这部分工作由usb_core来完成。当设备接到USB接口 是,usb_core就检测该设备的一些信息,例如生产厂商ID和产品的ID,或者是设备所属的class、subclass跟protocol,以便确定应该调用哪一个驱动处理该设备。里面复杂细节我们不用管,我们要做的是另一块工作——usb的设备驱动。也就是说,我们就等着usb_core告诉我们 要工作了,我们才工作。对于usb规范定义的设备,他们有一个设备的框架,对于开发人员来说,他大概如图所示:

从开发人员的角度看,每一个usb设备有若干个配置(configuration)组成,每 个配置又可以有多个接口(interface),每个接口又有多个设置(setting图中没有给出),而接口本身可能没有端点或者多个端点(end point)。USB的数据交换通过端点来进行,主机与各个端点之间建立起单向的管道来传输数据。而这些接口可以分为四类:

控制(control)

用于配置设备、获取设备信息、发送命令或者获取设备的状态报告

中断(interrupt)

当USB宿主要求设备传输数据时,中断端点会以一个固定的速率传送少量数据,还用于发送数据到USB设备以控制设备,一般不用于传送大量数据。

批量(bulk)

用于大量数据的可靠传输,如果总线上的空间不足以发送整个批量包,它会被分割成多个包传输。

等时(isochronous)

大量数据的不可靠传输,不保证数据的到达,但保证恒定的数据流,多用于数据采集。

Linux中用struct usb_host_endpoint来描述USB端点,每个usb_host_endpoint中包含一个struct usb_endpoint_descriptor结构体,当中包含该端点的信息以及设备自定义的各种信息,这些信息包括:

bEndpointAddress(b for byte)

8位端点地址,其地址还隐藏了端点方向的信息(之前说过,端点是单向的),可以用掩码USB_DIR_OUT和USB_DIR_IN来确定。

bmAttributes

端点的类型,结合USB_ENDPOINT_XFERTYPE_MASK可以确定端点是

USB_ENDPOINT_XFER_ISOC(等时)、USB_ENDPOINT_XFER_BULK(批量)还是USB_ENDPOINT_XFER_INT(中断)。

wMaxPacketSize

端点一次处理的最大字节数。发送的BULK包可以大于这个数值,但会被分割传送。

bInterval

如果端点是中断类型,该值是端点的间隔设置,以毫秒为单位。

在逻辑上,一个USB设备的功能划分是通过接口来完成的。比如说一个USB扬声器,可能会包括有两个接口:一个用于键盘控制,另外一个用于音频流传输。而 事实上,这种设备需要用到不同的两个驱动程序来操作,

一个控制键盘,一个控制音频流。但也有例外,比如蓝牙设备,要求有两个接口,第一用于ACL跟 EVENT的传输,另外一个用于SCO链路,但两者通过一个驱动控制。在Linux上,接口使用struct usb_interface来描述,以下是该结构体中比较重要的字段:

struct usb_host_interface *altsetting(注意不是usb_interface)

其实据我理解,他应该是每个接口的设置,虽然名字上有点奇怪。该字段是一个设置的数组(一个接口可以有多个设置),每个

usb_host_interface都包含一套由struct usb_host_endpoint定义的端点配置。但这些配置次序是不定的。

unsigned num_altstting

可选设置的数量,即altsetting所指数组的元素个数。

struct usb_host_interface *cur_altsetting

当前活动的设置,指向altsetting数组中的一个。

int minor

当捆绑到该接口的USB驱动程序使用USB主设备号时,USB core分配的次设备号。仅在成功调用usb_register_dev之后才有效。

除了它可以用struct usb_host_config来描述之外,到现在为止,我对配置的了解不多。而整个USB设备则可以用struct usb_device来描述,但基本上只会用它来初始化函数的接口,真正用到的应该是我们之前所提到的自定义的一个结构体。

Linux USB驱动框架分析(三)

下面我们来分析一下usb-skeleton的源码吧。这个范例程序可以在linux-2.6.17/drivers/usb下找到,其他版本的内核程序源码可能有所不同,但相差不大。大家可以先找到源码看一看,先有个整体印象。 之前已经提到,模块先要向内核注册初始化跟销毁函数:

static int __init usb_skel_init(void)

{

int result;

/* register this driver with the USB subsystem */

result = usb_register(&skel_driver);

if (result)

err("usb_register failed. Error number %d", result);

return result;

}

static void __exit usb_skel_exit(void)

{

/* deregister this driver with the USB subsystem */

usb_deregister(&skel_driver);

}

module_init (usb_skel_init);

module_exit (usb_skel_exit);

MODULE_LICENSE("GPL");

从代码开来,这个init跟exit函数的作用只是用来注册驱动程序,这个描述驱动程序的结 构体是系统定义的标准结构struct usb_driver,注册和注销的方法很简单,usb_register(struct *usb_driver), usb_unregister(struct *usb_driver)。那这个结构体需要做些什么呢?他要向系统提供几个函数入口,跟驱动的名字:

static struct usb_driver skel_driver = {

.name = "skeleton",

.probe = skel_probe,

.disconnect = skel_disconnect,

.id_table = skel_table,

};

从代码看来,usb_driver需要初始化四个东西:模块的名字skeleton,probe函数skel_probe,disconnect函数skel_disconnect,id_table。

在解释skel_driver各个成员之前,我们先来看看另外一个结构体。这个结构体的名字有开发人员自定义,它描述的是该驱动拥有的所有资源及状态:

struct usb_skel {

struct usb_device * udev; /* the usb device for this device */

struct usb_interface * interface; /* the interface for this device */

struct semaphore limit_sem; /* limiting the number of writes in progress */

unsigned char * bulk_in_buffer; /* the buffer to receive data */

size_t bulk_in_size; /* the size of the receive buffer */

__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */

__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */

struct kref kref;

};

我们先来对这个usb_skel作个简单分析,他拥有一个描述usb设备的结构体udev,一个接口interface,用于并发访问控制的semaphore(信号量) limit_sem,用于接收数据的缓冲bulk_in_buffer 及其尺寸bulk_in_size,然后是批量输入输出端口地址

bulk_in_endpointAddr、bulk_out_endpointAddr,最后是一个内核使

用的引用计数器。他们的作用我们将在后面的代 码中看到。

我们在回过头来看看skel_driver。

Name用来告诉内核模块的名字是什么,这个注册之后有系统来使用,跟我们关系不大。

id_table用来告诉内核该模块支持的设备。Usb子系统通过设备的production ID和vendor ID的组合或者设备的class、subclass跟protocol的组合来识别设备,并调用相关的驱动程序作处理。我们可以看看这个id_table 到底是什么东西:

/* Define these values to match your devices */

#define USB_SKEL_VENDOR_ID 0xfff0

#define USB_SKEL_PRODUCT_ID 0xfff0

/* 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);

MODULE_DEVICE_TABLE的第一个参是设备的类型,如果是USB设备,那自然是usb(如果是PCI设备,那将是pci,这两个子系统用同 一个宏来注册所支持的设备。这设计PCI设备的驱动了,在此先不深究)。后面一个参数是设备表,这个设备表的最后一个元素是空的,用于标识结束。代码定义 了USB_SKEL_VENDOR_ID是0xfff0,USB_SKEL_PRODUCT_ID是0xfff0,也就是说,当有一个设备接到集线器时,usb子系统就会检查这个设备的vendor ID和product ID,如果它们的值是0xfff0时,那么子系统就会调用这个skeleton模块作为设备的驱动。

下面我们来分析一下usb-skeleton的源码吧。这个范例程序可以在linux-2.6.17/drivers/usb下找到,其他版本的内核程序源码可能有所不同,但相差不大。大家可以先找到源码看一看,先有个整体印象。 之前已经提到,模块先要向内核注册初始化跟销毁函数:

static int __init usb_skel_init(void)

{

int result;

/* register this driver with the USB subsystem */

result = usb_register(&skel_driver);

if (result)

err("usb_register failed. Error number %d", result);

return result;

}

static void __exit usb_skel_exit(void)

{

/* deregister this driver with the USB subsystem */

usb_deregister(&skel_driver);

}

module_init (usb_skel_init);

module_exit (usb_skel_exit);

MODULE_LICENSE("GPL");

从代码开来,这个init跟exit函数的作用只是用来注册驱动程序,这个描述驱动程序的结 构体是系统定义的标准结构struct usb_driver,注

册和注销的方法很简单,usb_register(struct *usb_driver), usb_unregister(struct *usb_driver)。那这个结构体需要做些什么呢?他要向系统提供几个函数入口,跟驱动的名字:

static struct usb_driver skel_driver = {

.name = "skeleton",

.probe = skel_probe,

.disconnect = skel_disconnect,

.id_table = skel_table,

};

从代码看来,usb_driver需要初始化四个东西:模块的名字skeleton,probe函数skel_probe,disconnect函数skel_disconnect,id_table。

在解释skel_driver各个成员之前,我们先来看看另外一个结构体。这个结构体的名字有开发人员自定义,它描述的是该驱动拥有的所有资源及状态:

struct usb_skel {

struct usb_device * udev; /* the usb device for this device */

struct usb_interface * interface; /* the interface for this device */

struct semaphore limit_sem; /* limiting the number of writes in progress */

unsigned char * bulk_in_buffer; /* the buffer to receive data */

size_t bulk_in_size; /* the size of the receive buffer */

__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */

__u8 bulk_out_endpointAddr; /* the address of

the bulk out endpoint */

struct kref kref;

};

我们先来对这个usb_skel作个简单分析,他拥有一个描述usb设备的结构体udev,一个接口interface,用于并发访问控制的

semaphore(信号量) limit_sem,用于接收数据的缓冲bulk_in_buffer 及其尺寸bulk_in_size,然后是批量输入输出端口地址

bulk_in_endpointAddr、bulk_out_endpointAddr,最后是一个内核使用的引用计数器。他们的作用我们将在后面的代 码中看到。

我们在回过头来看看skel_driver。

Name用来告诉内核模块的名字是什么,这个注册之后有系统来使用,跟我们关系不大。

id_table用来告诉内核该模块支持的设备。Usb子系统通过设备的production ID和vendor ID的组合或者设备的class、subclass跟protocol的组合来识别设备,并调用相关的驱动程序作处理。我们可以看看这个id_table 到底是什么东西:

/* Define these values to match your devices */

#define USB_SKEL_VENDOR_ID 0xfff0

#define USB_SKEL_PRODUCT_ID 0xfff0

/* 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);

MODULE_DEVICE_TABLE的第一个参是设备的类型,如果是USB设备,那自然是usb(如果是PCI设备,那将是pci,这两个子系统用同 一

个宏来注册所支持的设备。这设计PCI设备的驱动了,在此先不深究)。后面一个参数是设备表,这个设备表的最后一个元素是空的,用于标识结束。代码定义 了USB_SKEL_VENDOR_ID是0xfff0,USB_SKEL_PRODUCT_ID是0xfff0,也就是说,当有一个设备接到集线器时,usb子系统就会检查这个设备的vendor ID和product ID,如果它们的值是0xfff0时,那么子系统就会调用这个skeleton模块作为设备的驱动。

probe是usb子系统自动调用的一个函数,有USB设备接到硬件集线器时,usb子系统 会根据production ID和vendor ID的组合或者设备的class、subclass跟protocol的组合来识别设备调用相应驱动程序的probe(探测)函数,对于skeleton 来说,就是skel_probe。系统会传递给探测函数一个usb_interface *跟一个struct usb_device_id *作为参数。他们分别是该USB设备的接口描述(一般会是该设备的第0号接口,该接口的默认设置也是第0号设置)跟它的设备ID描述(包括Vendor ID、Production ID等)。Probe函数比较长,我们分段来分析这个函数:

dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface;

在初始化了一些资源之后,我们可以看到第一个关键的函数调用——interface_to_usbdev。他同uo一个usb_interface来得到该接口所在设备的设备描述结构。本来,要得到一个 usb_device只要用interface_to_usbdev就够了,但因为要增加对该usb_device的引用计数,我们应该在做一个 usb_get_dev的操作,来增加引用计数,并在释放设备时用usb_put_dev来减少引用计数。这里要解释的是,该引用计数值是对该 usb_device的计数,并不是对本模块的计数,本模块的计数要由kref来维护。所以,probe一开始就有初始化kref。事实 上,kref_init操作不单只初始化kref,还将其置设成1。所以在出错处理代码中有kref_put,它把kref的计数减1,如果kref计数 已经为0,那么

kref会被释放。Kref_put的第二个参数是一个函数指针,指向一个清理函数。注意,该指针不能位空,或者kfree。该函数会在最 后一个对kref的引用释放时被调用(如果我的理解不准确,请指正)。下面是内核源码中的一段注释及代码:

/**

* kref_put - decrement refcount for object.

* @kref: object.

* @release: pointer to the function that will clean up the object when the

* last reference to the object is released.

* This pointer is required, and it is not acceptable to pass kfree

* in as this function.

*

* Decrement the refcount, and if 0, call release().

* Return 1 if the object was removed, otherwise return 0. Beware, if this

* function returns 0, you still can not count on the kref from remaining in

* memory. Only use the return value if you want to see if the kref is now

* gone, not present.

*/

int kref_put(struct kref *kref, void (*release)(struct kref *kref))

{

WARN_ON(release == NULL);

WARN_ON(release == (void (*)(struct kref *))kfree);

/*

* if current count is one, we are the last user and can release object

* right now, avoiding an atomic operation on 'refcount' */

if ((atomic_read(&kref->refcount) == 1) ||

(atomic_dec_and_test(&kref->refcount))) {

release(kref);

return 1;

}

return 0;

}

当我们执行打开操作时,我们要增加kref的计数,我们可以用

kref_get,来完成。所有对struct kref的操作都有内核代码确保其原子性。

得到了该usb_device之后,我们要对我们自定义的usb_skel各个状态跟资源作 初始化。这部分工作的任务主要是向usb_skel注册该usb设备的端点。这里可能要补充以下一些关于 usb_interface_descriptor的知识,但因为内核源码对该结构体的注释不多,所以只能靠个人猜测。在一个 usb_host_interface结构里面有一个usb_interface_descriptor 叫做desc的成员,他应该是用于描述该 interface的一些属性,其中bNubEndpoints一个8位(b for byte)的数字,他代表了该接口的端点数。Probe然后遍历所有的端点,检查他们的类型跟方向,注册到

usb_skel中。

/* set up the endpoint information */

/* use only the first bulk-in and bulk-out endpoints */

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_ENDPOINT_DIR_MASK)

== USB_DIR_IN) &&

((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)

== USB_ENDPOINT_XFER_BULK)) {

/* we found a bulk in endpoint */

buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);

dev->bulk_in_size = buffer_size;

dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;

dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);

if (!dev->bulk_in_buffer) {

err("Could not allocate bulk_in_buffer");

goto error;

}

}

if (!dev->bulk_out_endpointAddr &&

((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)

== USB_DIR_OUT) &&

((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)

== USB_ENDPOINT_XFER_BULK)) {

/* we found a bulk out endpoint */

dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;

}

}

if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {

err("Could not find both bulk-in and bulk-out endpoints");

goto error;

}

接下来的工作是向系统注册一些以后会用的的信息。首先我们来说明一下usb- set_intfdata(),他向内核注册一个data,这个data结构可以是任意的,在这段程序用向内核注册了一个usb_skel结构,就是我们

刚刚看到的被初始化的那个,这个data可以在以后用usb_get_intfdata 来得到。

usb_set_intfdata(interface, dev);

retval = usb_register_dev(interface, &skel_class);

然后我们向这个interface注册一个skel_class结构。这个结构又是什么?我们就来看看这到底是个什么东西:

static struct usb_class_driver skel_class = {

.name = "skel%d",

.fops = &skel_fops,

.minor_base = USB_SKEL_MINOR_BASE,

};

它其实是一个系统定义的结构,里面包含了一名字、一个文件操作结构体还有一个次设备号的基准 值。事实上它定义真正完成对设备IO操作的函数。所以他的核心内容应该是skel_fops。这里补充一些我个人的估计:因为usb设备可以有多个 interface,每个interface所定义的IO 操作可能不一样,所以想系统注册的usb_class_driver要求注册到某一个 interface,因此usb_register_dev的第一个参数是interface,而第二个参数就是某一个 usb_class_driver。通常情况下,linux系统用主设备好来识别某类设备的的驱动程序,用次设备号管理识别具体的设备,驱动程序可以依照 次设备好来区分不同的设备,所以,这里的次设备好其实是用来管理不同的interface的,但由于这个范例只有一个interface,在代码上无法求 证这个猜想。

static struct file_operations skel_fops = {

.owner = THIS_MODULE,

.read = skel_read,

.write = skel_write,

.open = skel_open,

.release = skel_release,

};

这个文件操作结构中定义了对设备的读写、打开释放(USB设备通常使用这个术语release)。他们都是函数指针,分别指向skel_read、

skel_write、skel_open、skel_release这四个函数,这四个函数应该有开发人员自己实现。

当设备被拔出集线器时,usb子系统会自动地调用disconnect,他做的事情不多,最重要的是注销class_driver(交还次设备号)和interface的data:

dev = usb_get_intfdata(interface);

usb_set_intfdata(interface, NULL);

/* give back our minor */

usb_deregister_dev(interface, &skel_class);

然后他会用kref_put(&dev->kref, skel_delete)进行清理,kref_put 的细节参见前文。

到目前为止,我们已经分析完usb子系统要求的各个主要操作,下一部分我们在讨论一下对USB设备的IO操作。

说的usb子系统的IO操作,不得不说usb request block,简称urb。事实上,可以打一个这样的比喻,usb总线就像一条高速公路,货物、人流之类的可以看成是系统与设备交互的数据,而urb就可以 看成是交通工具。在一开始对USB规范细节的介绍,我们就说过USB的endpoint有4种不同类型,于是能在这条高速公路上流动的数据也就有四种。但 对车是没有要求的,urb可以运载四种数据,不过你要先告诉司机你要运什么,目的地是什么。我们现在就看看struct urb的具体内容。它的内容很多,为了不让我的理解误导各位,大家最好还是看一看内核源码的注释,具体内容参见源码树下include/linux /usb.h。

在这里我们重点介绍程序中出现的几个关键字段:

struct usb_device *dev

urb所发送的目标设备。

unsigned int pipe

一个管道号码,该管道记录了目标设备的端点以及管道的类型。每个管道只有一种类型和一个方向,它与他的目标设备的端点向对应,我们可以通过以下几个函数来获得管道号并设置管道类型:

unsigned int usb_sndctrlpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备指定端点设置为一个控制OUT端点。

unsigned int usb_rcvctrlpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备指定端点设置为一个控制IN端点。

unsigned int usb_sndbulkpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备指定端点设置为一个批量OUT端点。

unsigned int usb_rcvbulkpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备指定端点设置为一个批量OUT端点。

unsigned int usb_sndintpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备指定端点设置为一个中断OUT端点。

unsigned int usb_rcvintpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备指定端点设置为一个中断OUT端点。

unsigned int usb_sndisocpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备指定端点设置为一个等时OUT端点。

unsigned int usb_rcvisocpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备指定端点设置为一个等时OUT端点。

unsigned int transfer_flags

当不使用DMA时,应该transfer_flags |= URB_NO_TRANSFER_DMA_MAP(按照代码的理解,希望没有错)。

Int status

当一个urb把数据送到设备时,这个urb会由系统返回给驱动程序,并调用驱动程序的urb完成回调函数处理。这时,status记录了这次数据传输的有关状态,例如传送成功与否。成功的话会是0。

要能够运货当然首先要有车,所以第一步当然要创建urb:

struct urb *usb_alloc_urb(int isoc_packets, int mem_flags);

第一个参数是等时包的数量,如果不是乘载等时包,应该为0,第二个参数与kmalloc的标志相同。

要释放一个urb可以用:

void usb_free_urb(struct urb *urb);

要承载数据,还要告诉司机目的地信息跟要运的货物,对于不同的数据,系统提供了不同的函数,对于中断urb,我们用

void usb_fill_int_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,

void *transfer_buffer, int buffer_length,

usb_complete_t complete, void *context, int interval);

这里要解释一下,transfer_buffer是一要送/收的数据的缓冲,buffer_length是它的长度,complete是urb完成回调函数的入口,context有用户定义,可能会在回调函数中使用的数据,interval就是urb被调度的间隔。

对于批量urb和控制urb,我们用:

void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,

void *transfer_buffer, int buffer_length, usb_complete_t complete,

void *context);

void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,

unsigned char* setup_packet,void *transfer_buffer,

int buffer_length, usb_complete_t complete,void *context);

控制包有一个特殊参数setup_packet,它指向即将被发送到端点的设置数据报的数据。

对于等时urb,系统没有专门的fill函数,只能对各urb字段显示赋值。

usb驱动程序教程

编写Windows https://www.360docs.net/doc/9f6424024.html,的usb驱动程序教程 Windows https://www.360docs.net/doc/9f6424024.html, 是微软推出的功能强大的嵌入式操作系统,国内采用此操作系统的厂商已经很多了,本文就以windows https://www.360docs.net/doc/9f6424024.html,为例,简单介绍一下如何开发windows https://www.360docs.net/doc/9f6424024.html, 下的USB驱动程序。 Windows https://www.360docs.net/doc/9f6424024.html, 的USB系统软件分为两层: USB Client设备驱动程序和底层的Windows CE实现的函数层。USB设备驱动程序主要负责利用系统提供的底层接口配置设备,和设备进行通讯。底层的函数提本身又由两部分组成,通用串行总线驱动程序(USBD)模块和较低的主控制器驱动程序(HCD)模块。HCD负责最最底层的处理,USBD模块实现较高的USBD函数接口。USB设备驱动主要利用 USBD接口函数和他们的外围设备打交道。 USB设备驱动程序主要和USBD打交道,所以我们必须详细的了解USBD提供的函数。 主要的传输函数有: abourttransfer issuecontroltransfer closetransfer issuein te rruptransfer getisochresult issueisochtransfer gettransferstatus istransfercomplete issuebulktransfer issuevendortransfer 主要的用于打开和关闭usbd和usb设备之间的通信通道的函数有: abortpipetransfers closepipe isdefaultpipehalted ispipehalted openpipe resetdefaultpipe resetpipe 相应的打包函数接口有: getframelength getframenumber releaseframelengthcontrol setframelength takeframelengthcontrol 取得设置设备配置函数: clearfeature setdescriptor getdescriptor setfeature

Linux设备驱动程序举例

Linux设备驱动程序设计实例2007-03-03 23:09 Linux系统中,设备驱动程序是操作系统内核的重要组成部分,在与硬件设备之间 建立了标准的抽象接口。通过这个接口,用户可以像处理普通文件一样,对硬件设 备进行打开(open)、关闭(close)、读写(read/write)等操作。通过分析和设计设 备驱动程序,可以深入理解Linux系统和进行系统开发。本文通过一个简单的例子 来说明设备驱动程序的设计。 1、程序清单 //MyDev.c 2000年2月7日编写 #ifndef __KERNEL__ #define __KERNEL__//按内核模块编译 #endif #ifndef MODULE #define MODULE//设备驱动程序模块编译 #endif #define DEVICE_NAME "MyDev" #define OPENSPK 1 #define CLOSESPK 2 //必要的头文件 #include //同kernel.h,最基本的内核模块头文件 #include //同module.h,最基本的内核模块头文件 #include //这里包含了进行正确性检查的宏 #include //文件系统所必需的头文件 #include //这里包含了内核空间与用户空间进行数据交换时的函数宏 #include //I/O访问 int my_major=0; //主设备号 static int Device_Open=0; static char Message[]="This is from device driver"; char *Message_Ptr; int my_open(struct inode *inode, struct file *file) {//每当应用程序用open打开设备时,此函数被调用 printk ("\ndevice_open(%p,%p)\n", inode, file); if (Device_Open) return -EBUSY;//同时只能由一个应用程序打开 Device_Open++; MOD_INC_USE_COUNT;//设备打开期间禁止卸载 return 0; } static void my_release(struct inode *inode, struct file *file)

PCI驱动编程基本框架

Linux将所有外部设备看成是一类特殊文件,称之为“设备文件”,如果说系统调用是Linux 内核和应用程序之间的接口,那么设备驱动程序则可以看成是Linux内核与外部设备之间的接口。设备驱动程序向应用程序屏蔽了硬件在实现上的细节,使得应用程序可以像操作普通文件一样来操作外部设备。 1. 字符设备和块设备 Linux抽象了对硬件的处理,所有的硬件设备都可以像普通文件一样来看待:它们可以使用和操作文件相同的、标准的系统调用接口来完成打开、关闭、读写和I/O控制操作,而驱动程序的主要任务也就是要实现这些系统调用函数。Linux系统中的所有硬件设备都使用一个特殊的设备文件来表示,例如,系统中的第一个IDE硬盘使用/dev/hda表示。每个设备文件对应有两个设备号:一个是主设备号,标识该设备的种类,也标识了该设备所使用的驱动程序;另一个是次设备号,标识使用同一设备驱动程序的不同硬件设备。设备文件的主设备号必须与设备驱动程序在登录该设备时申请的主设备号一致,否则用户进程将无法访问到设备驱动程序。 在Linux操作系统下有两类主要的设备文件:一类是字符设备,另一类则是块设备。字符设备是以字节为单位逐个进行I/O操作的设备,在对字符设备发出读写请求时,实际的硬件I/O紧接着就发生了,一般来说字符设备中的缓存是可有可无的,而且也不支持随机访问。块设备则是利用一块系统内存作为缓冲区,当用户进程对设备进行读写请求时,驱动程序先查看缓冲区中的内容,如果缓冲区中的数据能满足用户的要求就返回相应的数据,否则就调用相应的请求函数来进行实际的I/O操作。块设备主要是针对磁盘等慢速设备设计的,其目的是避免耗费过多的CPU时间来等待操作的完成。一般说来,PCI卡通常都属于字符设备。 2. 设备驱动程序接口 Linux中的I/O子系统向内核中的其他部分提供了一个统一的标准设备接口,这是通过include/linux/fs.h中的数据结构file_operations来完成的: struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

USB驱动程序源代码

项目报告7 USB驱动程序源代码作者:罗仕波 一.头文件go7007sb.h /* *go7007sb.h - this file includes all relative header files that *will be used in go7007sb vedio usb interface driver, and it *also defines all relative driver private data structures and *it's io control commands. */ #ifndef _GO7007SB_H #define _GO7007SB_H #include #include #include #include #include #include #include #include #include #include #include #include //#define DEBUG #define DRIVER_VERSION "1.0.0" #define DRIVER_DESC "USB GO7007SB Driver" #include MODULE_AUTHOR("Luo Shibo"); MODULE_DESCRIPTION(DRIVER_DESC" "DRIVER_VERSION); MODULE_LICENSE("GPL"); /* *io control commands definition,these commands will be *used to control the device in function iocntl_go7007sb */ #define GO7007SB_IOC_MAGIC 'U' //command magic number #define GO7007SB_IOC_RESET _IO(GO7007SB_IOC_MAGIC,0) //software reset the device

Linux驱动程序工作原理简介

Linux驱动程序工作原理简介 一、linux驱动程序的数据结构 (1) 二、设备节点如何产生? (2) 三、应用程序是如何访问设备驱动程序的? (2) 四、为什么要有设备文件系统? (3) 五、设备文件系统如何实现? (4) 六、如何使用设备文件系统? (4) 七、具体设备驱动程序分析 (5) 1、驱动程序初始化时,要注册设备节点,创建子设备文件 (5) 2、驱动程序卸载时要注销设备节点,删除设备文件 (7) 参考书目 (8) 一、linux驱动程序的数据结构 设备驱动程序实质上是提供一组供应用程序操作设备的接口函数。 各种设备由于功能不同,驱动程序提供的函数接口也不相同,但linux为了能够统一管理,规定了linux下设备驱动程序必须使用统一的接口函数file_operations 。 所以,一种设备的驱动程序主要内容就是提供这样的一组file_operations 接口函数。 那么,linux是如何管理种类繁多的设备驱动程序呢? linux下设备大体分为块设备和字符设备两类。 内核中用2个全局数组存放这2类驱动程序。 #define MAX_CHRDEV 255 #define MAX_BLKDEV 255 struct device_struct { const char * name; struct file_operations * fops; }; static struct device_struct chrdevs[MAX_CHRDEV]; static struct { const char *name; struct block_device_operations *bdops; } blkdevs[MAX_BLKDEV]; //此处说明一下,struct block_device_operations是块设备驱动程序内部的接口函数,上层文件系统还是通过struct file_operations访问的。

一个简单的演示用的Linux字符设备驱动程序.

实现如下的功能: --字符设备驱动程序的结构及驱动程序需要实现的系统调用 --可以使用cat命令或者自编的readtest命令读出"设备"里的内容 --以8139网卡为例,演示了I/O端口和I/O内存的使用 本文中的大部分内容在Linux Device Driver这本书中都可以找到, 这本书是Linux驱动开发者的唯一圣经。 ================================================== ===== 先来看看整个驱动程序的入口,是char8139_init(这个函数 如果不指定MODULE_LICENSE("GPL", 在模块插入内核的 时候会出错,因为将非"GPL"的模块插入内核就沾污了内核的 "GPL"属性。 module_init(char8139_init; module_exit(char8139_exit; MODULE_LICENSE("GPL"; MODULE_AUTHOR("ypixunil"; MODULE_DESCRIPTION("Wierd char device driver for Realtek 8139 NIC"; 接着往下看char8139_init( static int __init char8139_init(void {

int result; PDBG("hello. init.\n"; /* register our char device */ result=register_chrdev(char8139_major, "char8139", &char8139_fops; if(result<0 { PDBG("Cannot allocate major device number!\n"; return result; } /* register_chrdev( will assign a major device number and return if it called * with "major" parameter set to 0 */ if(char8139_major == 0 char8139_major=result; /* allocate some kernel memory we need */ buffer=(unsigned char*(kmalloc(CHAR8139_BUFFER_SIZE, GFP_KERNEL; if(!buffer { PDBG("Cannot allocate memory!\n"; result= -ENOMEM;

NDIS驱动框架探究

NDIS驱动框架探究 By AntBean Fearless,Passion,Endure 在我看来NDIS驱动有几个难点:1.框架难,NDIS驱动的整体框架跟接触的文件过滤驱动很不一样,你需要考虑的比较多,整个框架的堆叠不再是向文件过滤驱动那样,使用AttachDevice堆叠设备,然后IoCallDriver传递请求;2.硬件操作的敏感性,网卡设备的热插拔,电源状态的变化都会影响到驱动程序;3.系统资源使用的敏感性,NDIS驱动本身是要收包发包,网络包的数据量是非常大的,如果在分配网络封包时没有及时释放,很有可能导致系统资源不足。本篇文章主要对NDIS驱动框架进行探究。 在介绍内容之前,先说明一些概念。 1.面向连接的网络和无连接的网络与面向连接的协议和无连接的协议 面向连接的网络是指通过电路交换进行的局域网。一台机器如果需要给另外一台机器发送信息,就需要先像打电话一样呼叫另外一台机器,另外一台机器可以接受或者拒绝。一旦接受,就会使用一个专用的线路维持该连接。该连接的容量是固定的,而且只归该连接所有。这种局域网最典型的例子是A TM网络。 无连接的网络是指通过分组交换传输信息的局域网。一台机器如果给另外一台机器发送信息,需要传输的数据组织成一个分组,发送出去。所有正在通信的计算机共用一个连接。典型的例子是以太网和FDDI。 (参见《用TCP/IP进行网际互联第四版》第一卷第二章底层网络技术回顾) 以上的面向连接和无连接针对的是物理上的网络硬件,属于OSI七层模型中的物理层;而面向连接的协议和无连接的协议则是针对协议的,与硬件是无关的,属于协议层。常见的面向连接的协议有TCP协议,无连接的协议有UDP协议。 2. 电源状态D0 D1 D2 D3 ACPI规定,设备处于四种状态之一,D0到D3。D0是完全打开,D3是完全关闭。D1和D2由驱动程序自己定义。 (参见《深入解析windows操作系统第四版》第九章第五小节电源管理器) 3.序列化NDIS驱动和非序列化NDIS驱动 对NDIS驱动的请求不再是IRP的形式,而是Packet的形式。其他驱动例如TDI驱动通过NDIS库函数如NdisSend向NDIS驱动发送请求,事实上NDIS库会对Packet进行排队,保证对NDIS驱动的调用是串行的,一个Packet处理完才会发送另一个Packet给NDIS驱动。这样的NDIS驱动就叫做序列化的NDIS驱动。 而一个非序列化的NDIS驱动则是指,NDIS库不负责对Packet进行排队和串行化,需要NDIS驱动自己处理同时多个Packet请求的情形。排队和管理多个并发请求的任务落到了NDIS驱动程序的头上。 (参见《深入解析windows操作系统第四版》第十三章第六小节NDIS驱动程序) NDIS驱动本身分为两种,Protocol Driver和Miniport Driver。至于NDIS中间层驱动,则是由前两种演化而来。Miniport Driver用来驱动网卡,Protocol Driver是协议驱动,负责将要

最新开发usb驱动程序的方法连载一

最新开发usb驱动程序的方法连载一 开发usb驱动程序的方法(连载二) NT还有更多其他的对象,例如中断对象、Controller对象、定时器对象等等,但在我们开发的驱动程序中并没有用到,因此在这里不做介绍。 I/O缓冲策略 很明显的,驱动程序和客户应用程序经常需要进行数据交换,但我们知道驱动程序和客户应用程序可能不在同一个地址空间,因此操作系统必须解决两者之间的数据交换。这就就设计到设备的I/O缓冲策略。 读写请求的I/O缓冲策略 前面说到通过设置Device对象的Flag可以选择控制处理读写请求的I/O缓冲策略。下面对这些缓冲策略分别做一介绍。 1、缓冲I/O(DO_BUFFERED_IO) 在读写请求的一开始,I/O管理器检查用户缓冲区的可访问性,然后分配与调用者的缓冲区一样大的非分页池,并把它的地址放在IRP的AssociatedIrp.SystemBuffer域中。驱动程序就利用这个域来进行实际数据的传输。 对于IRP_MJ_READ读请求,I/O管理器还把IRP的UserBuffer域设置成调用者缓冲区的用户空间地址。当请求完成时,I/O管理器利用这个地址将数据从驱动程序的系统空间拷贝回调用者的缓冲区。对于IRP_MJ_WRITE写请求,UserBuffer被设置为NULL,并把用户缓冲区的数据拷贝到系统缓冲区中。 2、直接I/O(DO_DIRECT_IO) I/O管理器首先检查用户缓冲区的可访问性,并在物理内存中锁定它。然后它为该缓冲区创建一个内存描述表(MDL),并把MDL的地址存放在IRP的MdlAddress域中。AssociatedIrp.SystemBuffer和 UserBuffer 都被设置为NULL。驱动程序可以调用函数 MmGetSystemAddressForMdl得到用户缓冲区的系统空间地址,从而进行数据操作。这个函数将调用者的缓冲区映射到非份页的地址空间。驱动程序完成I/O请求后,系统自动从系统空间解除缓冲区的映射。 3、这两种方法都不是 这种情况比较少用,因为这需要驱动程序自己来处理缓冲问题。 I/O管理器仅把调用者缓冲区的用户空间地址放到IRP的UserBuffer 域中。我们并不推荐这种方式。 IOCTL缓冲区的缓冲策略 IOCTL请求涉及来自调用者的输入缓冲区和返回到调用者的输出缓冲区。为了理解IOCTL请求,我们先来看看WIN32 API DeviceIoControl函数的原型。 BOOL DeviceIoControl ( HANDLE hDevice, // 设备句柄 DWORD dwIoControlCode, // IOCTL请求操作代码 LPVOID lpInBuffer, // 输入缓冲区地址 DWORD nInBufferSize, // 输入缓冲区大小 LPVOID lpOutBuffer, // 输出缓冲区地址 DWORD nOutBufferSize, // 输出缓冲区大小 LPDWORD lpBytesReturned, // 存放返回字节数的指针

一个简单字符设备驱动实例

如何编写Linux设备驱动程序 Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel中的函数,有些常用的操作要自己来编写,而且调试也不方便。本文是在编写一块多媒体卡编制的驱动程序后的总结,获得了一些经验,愿与Linux fans共享,有不当之处,请予指正。 以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennan's Guide to Inline Assembly,The Linux A-Z,还有清华BBS上的有关device driver的一些资料. 这些资料有的已经过时,有的还有一些错误,我依据自己的试验结果进行了修正. 一、Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能: 1)对设备初始化和释放; 2)把数据从内核传送到硬件和从硬件读取数据; 3)读取应用程序传送给设备文件的数据和回送应用程序请求的数据; 4)检测和处理设备出现的错误。 在Linux操作系统下有两类主要的设备文件类型,一种是字符设备,另一种是块设备。字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待. 已经提到,用户进程是通过设备文件来与实际的硬件打交道。每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备。另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们。设备文件的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序. 最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck。 二、实例剖析 我们来写一个最简单的字符设备驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理.把下面的C代码输入机器,你就会获得一个真正的设备

USB键盘驱动程序

/* * $Id: usbkbd.c,v 1.27 2001/12/27 10:37:41 vojtech Exp $ * * Copyright (c) 1999-2001 Vojtech Pavlik * * USB HIDBP Keyboard support */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to <>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include #include #include #include #include #include #include /* * Version Information */ #define DRIVER_VERSION "" #define DRIVER_AUTHOR "Vojtech Pavlik <>" #define DRIVER_DESC "USB HID Boot Protocol keyboard driver" #define DRIVER_LICENSE "GPL"

Windows驱动程序开发环境配置

Windows驱动程序开发笔记 一、WDK与DDK环境 最新版的WDK 微软已经不提供下载了这里:https://https://www.360docs.net/doc/9f6424024.html,/ 可以下并且这里有好多好东东! 不要走进一个误区:下最新版的就好,虽然最新版是Windows Driver Kit (WDK) 7_0_0,支持windows7,vista 2003 xp等但是它的意思是指在windows7操作系统下安装能编写针对windows xp vista的驱动程序, 但是不能在xp 2003环境下安装Windows Driver Kit (WDK) 7_0_0这个高版本,否则你在build的时候会有好多好多的问题. 上文build指:首先安装好WDK/DDK,然后进入"开始"->"所有程序"->"Windows Driver Kits"->"WDK XXXX.XXXX.X" ->"Windows XP"->"x86 Checked Build Environment"在弹出来的命令行窗口中输入"Build",让它自动生成所需要的库 如果你是要给xp下的开发环境还是老老实实的找针对xp的老版DDK吧,并且xp无WDK 版只有DDK版build自己的demo 有个常见问题: 'jvc' 不是内部或外部命令,也不是可运行的程序。 解决办法:去掉build路径中的空格。 二、下载 WDK 开发包的步骤 1、访问Microsoft Connect Web site站点 2、使用微软 Passport 账户登录站点 3、登录进入之后,点击站点目录链接 4、在左侧的类别列表中选择开发人员工具,在右侧打开的类别:开发人员工具目录中找到Windows Driver Kit (WDK) and Windows Driver Framework (WDF)并添加到您的控制面板中 5、添加该项完毕后,选择您的控制面板,就可以看到新添加进来的项了。 6、点击Windows Driver Kit (WDK) and Windows Driver Framework (WDF),看到下面有下载链接,OK,下载开始。下载后的文件名为: 6.1.6001.18002.081017-1400_wdksp-WDK18002SP_EN_DVD.iso将近600M大小。

linux简单的gpio驱动实例

今天完成了嵌入式linux的第一个驱动的编写和测试,虽然是个简单的程序,但是麻雀虽小,五脏俱全,希望可以给刚开始接触驱动编写的人一些提示,共同进步。 源代码: 分析如下: 下面是我的驱动程序: #include //配置头文件 #include /*内核头文件,作为系统核心的一部分,设备驱动程序在申请和释放内存时,不是调用malloc和free,而是调用kmalloc和 kfree*/ #include //调度,进程睡眠,唤醒,中断申请,中断释放 #include //时钟头文件 #include //用户定义模块初始函数名需引用的头文件 #include //模块加载的头文件 #include #include //这个是2440的寄存器头文件,asm/srch只是个链接 //实际根据自己的情况查找,一般 是../../linux2.*.*/include/asm/arch-s3c2440里编译器 //自己会查询链接,以前不知道,找了半天 // GPIO_LED DEVICE MAJOR #define GPIO_LED_MAJOR 97 //定义主设备号 //define LED STATUS 我的板子 LED在GPB0 与GPB1 处大家根据自己情况改 #define LED_ON 0 //定义LED灯的状态开 #define LED_OFF 1 // // ------------------- READ ------------------------ 这个前面要加static 否则警告 static ssize_t GPIO_LED_read (struct file * file ,char * buf, size_t count, loff_t * f_ops) {

Linux驱动框架及驱动加载

本讲主要概述Linux设备驱动框架、驱动程序的配置文件及常用的加载驱动程序的方法;并且介绍Red Hat Linux安装程序是如何加载驱动的,通过了解这个过程,我们可以自己将驱动程序放到引导盘中;安装完系统后,使用kudzu自动配置硬件程序。 Linux设备驱动概述 1. 内核和驱动模块 操作系统是通过各种驱动程序来驾驭硬件设备,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并且提供统一的操作方式。正如我们查看屏幕上的文档时,不用去管到底使用nVIDIA芯片,还是ATI芯片的显示卡,只需知道输入命令后,需要的文字就显示在屏幕上。硬件驱动程序是操作系统最基本的组成部分,在Linux内核源程序中也占有较高的比例。 Linux内核中采用可加载的模块化设计(LKMs ,Loadable Kernel Modules),一般情况下编译的Linux内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中,其它的代码可以选择是在内核中,或者编译为内核的模块文件。 如果需要某种功能,比如需要访问一个NTFS分区,就加载相应的NTFS模块。这种设计可以使内核文件不至于太大,但是又可以支持很多的功能,必要时动态地加载。这是一种跟微内核设计不太一样,但却是切实可行的内核设计方案。 我们常见的驱动程序就是作为内核模块动态加载的,比如声卡驱动和网卡驱动等,而Linux最基础的驱动,如CPU、PCI总线、TCP/IP协议、APM(高级电源管理)、VFS等驱动程序则编译在内核文件中。有时也把内核模块就叫做驱动程序,只不过驱动的内容不一定是硬件罢了,比如ext3文件系统的驱动。 理解这一点很重要。因此,加载驱动时就是加载内核模块。下面来看一下有关模块的命令,在加载驱动程序要用到它们:lsmod、modprob、insmod、rmmod、modinfo。 lsmod

USB驱动程序安装说明

USB编程电缆驱动程序安装说明 概述 USB编程电缆或USB接口产品是通过将电脑的USB接口模拟成传统的串行口(通常为COM3),从而使用现有的编程软件或通信软件,通过编程电缆与PLC 等设备的传统接口进行通信。 功能 ●支持的操作系统Windows2000/Windows XP ●完全兼容USB 2.0规范 ●USB总线供电(非隔离产品)、或USB总线供电与PLC的编程口同时供 电(隔离型产品) ●波特率:300bps~1Mbps自动适应 ●每台PC只支持一个USB接口转换产品(如一台电脑需使用多个USB 接口产品,需为各个USB接口产品设置不同的序列号,请咨询生产厂家 索取序列号设置工具软件) 系统要求 请在使用USB编程电缆之前确认你的电脑是IBM PC兼容型并具备以下最低系统要求: ●Intel兼容586DX4-100MHz中央处理器或更高 ●一个标准的USB接口(4-pin A型插座) ●运行操作系统为Windows2000或Windows XP 驱动程序的安装 驱动程序的安装非常简单,只需按提示进行即可,以Windows XP为例,按以下步骤进行: 1、打开将要连接USB编程电缆或USB接口产品的电脑电源,并确认电脑 的USB口已经启动并正常工作。 2、将USB编程电缆或USB接口产品插入电脑的USB接口,Windows将检 测到设备并运行添加新硬件向导帮助你设置新设备,插入驱动程序光盘 并单击下一步继续。 如果Windows没有提示找到新硬件,那么在设备管理器的硬件列表中, 展开“通用串行总线控制器”,选择带问号的USB设备,单击鼠标右键 并运行更新驱动程序。

Linux设备驱动程序学习(18)-USB 驱动程序(三)

Linux设备驱动程序学习(18)-USB 驱动程序(三) (2009-07-14 11:45) 分类:Linux设备驱动程序 USB urb (USB request block) 内核使用2.6.29.4 USB 设备驱动代码通过urb和所有的 USB 设备通讯。urb用 struct urb 结构描述(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 被动态创建并包含一个内部引用计数,使它们可以在最后一个用户释放它们时被自动释放。 struct urb

struct list_head urb_list;/* list head for use by the urb's * current owner */ struct list_head anchor_list;/* the URB may be anchored */ struct usb_anchor *anchor; struct usb_device *dev;/* 指向这个 urb 要发送的目标 struct usb_device 的指针,这个变量必须在这个 urb 被发送到 USB 核心之前被USB 驱动初始化.*/ struct usb_host_endpoint *ep;/* (internal) pointer to endpoint */ unsigned int pipe;/* 这个 urb 所要发送到的特定struct usb_device 的端点消息,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化.必须由下面的函数生成*/ int status;/*当 urb开始由 USB 核心处理或处理结束, 这个变量被设置为 urb 的当前状态. USB 驱动可安全访问这个变量的唯一时间是在 urb 结束处理例程函数中. 这个限制是为防止竞态. 对于等时 urb, 在这个变量中成功值(0)只表示这个 urb 是否已被去链. 为获得等时 urb 的详细状态, 应当检查 iso_frame_desc 变量. */ unsigned int transfer_flags;/* 传输设置*/ void*transfer_buffer;/* 指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)的缓冲区指针。为了主机控制器驱动正确访问这个缓冲, 它必须使用 kmalloc 调用来创建, 不是在堆栈或者静态内存中。对控制端点, 这个缓冲区用于数据中转*/ dma_addr_t transfer_dma;/* 用于以 DMA 方式传送数据到 USB 设备的缓冲区*/ int transfer_buffer_length;/* transfer_buffer 或者 transfer_dma 变量指向的缓冲区大小。如果这是 0, 传送缓冲没有被 USB 核心所使用。对于一个 OUT 端点, 如果这个端点大小比这个变量指定的值小, 对这个USB 设备的传输将被分成更小的块,以正确地传送数据。这种大的传送以连续的 USB 帧进行。在一个 urb 中提交一个大块数据, 并且使 USB 主机控制器去划分为更小的块, 比以连续地顺序发送小缓冲的速度快得多*/

Windows驱动程序框架理解_经典入门

标题: 【原创】Windows驱动程序框架 windows驱动程序入门比较坑爹一点,本文旨在降低入门的门槛。注:下面的主要以NT式驱动为例,部分涉及到WDM驱动的差别会有特别说明。 首先,肯定是配置好对应的开发环境啦,不懂的就百度下吧,这里不再次描述了。 在Console控制台下,我们的有一个入口函数main;在Windows图形界面平台下,有另外一个入口函数Winmain。我们只要在这入口函数里面调用其他相关的函数,程序就会按照我们的意愿跑起来了。在我们用IDE开发的时候,也许你不会发现这些细微之处是如何配置出来的,一般来说我们也不用理会,因为在新建工程的时候,IDE已经帮我们把编译器(Compiler)以及连接器(Linker)的相关参数设置好,在正式编程的时候,我们只要按照规定的框架编程就行了。 同样,在驱动程序也有一个入口函数DriverEntry,这并不是一定的,但这是微软默认的、推荐使用的。在我们配置开发环境的时候我们有机会指定入口函数,这是链接器的参数/entry:"DriverEntry"。 入口函数的声明 代码: DriverEntry主要是对驱动程序进行初始化工作,它由系统进程(System)创建,系统启动的时候System系统进程就被创建了。 驱动加载的时候,系统进程将会创建新的线程,然后调用执行体组件中的对象管理器,创建一个驱动对象(DRIVER_OBJECT)。另外,系统进程还得调用执行体组件中的配置管理程序,查询此驱动程序在注册表中对应项。系统进程在调用驱动程序的Driv erEntry的时候就会将这两个值传到pDriverObject和pRegistryPath。 接下来,我们介绍下上面出现的几个数据结构: typedef LONG NTSTATUS 在驱动开发中,我们应习惯于用NTSTATUS返回信息,NTSTATUS各个位有不同的含义,我们可以也应该用宏NT_SUCCESS来判断是否返回成功。 代码: NTSTAUS的编码意义: 其中 Ser是Serviity的缩写,代表严重程度。 00:成功01:信息10:警告11:错误 C是Customer的缩写,代表自定义的位。

linux驱动程序的编写

linux驱动程序的编写 一、实验目的 1.掌握linux驱动程序的编写方法 2.掌握驱动程序动态模块的调试方法 3.掌握驱动程序填加到内核的方法 二、实验内容 1. 学习linux驱动程序的编写流程 2. 学习驱动程序动态模块的调试方法 3. 学习驱动程序填加到内核的流程 三、实验设备 PentiumII以上的PC机,LINUX操作系统,EL-ARM860实验箱 四、linux的驱动程序的编写 嵌入式应用对成本和实时性比较敏感,而对linux的应用主要体现在对硬件的驱动程序的编写和上层应用程序的开发上。 嵌入式linux驱动程序的基本结构和标准Linux的结构基本一致,也支持模块化模式,所以,大部分驱动程序编成模块化形式,而且,要求可以在不同的体系结构上安装。linux是可以支持模块化模式的,但由于嵌入式应用是针对具体的应用,所以,一般不采用该模式,而是把驱动程序直接编译进内核之中。但是这种模式是调试驱动模块的极佳方法。 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。同时,设备驱动程序是内核的一部分,它完成以下的功能:对设备初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。在linux操作系统下有字符设备和块设备,网络设备三类主要的设备文件类型。 字符设备和块设备的主要区别是:在对字符设备发出读写请求时,实际的硬件I/O一般就紧接着发生了;块设备利用一块系统内存作为缓冲区,当用户进程对设备请求满足用户要求时,就返回请求的数据。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。 1 字符设备驱动结构 Linux字符设备驱动的关键数据结构是cdev和file_operations结构体。

相关文档
最新文档