`

Linux环境下USB的原理、驱动和配置

 
阅读更多

随着生活水平的提高,人们对USB设备的使用也越来越多,鉴于Linux在硬件配置上尚不能全部即插即用,因此关于Linux如何配置和使用,成为困扰我们的一大问题。

什么是USB

USB是英文Universal Serial Bus的缩写,意为通用串行总线。USB最初是为了替代许多不同的低速总线(包括并行、串行和键盘连接)而设计的,它以单一类型的总线连接各种不同的类型的设备。USB的发展已经超越了这些低速的连接方式,它现在可以支持几乎所有可以连接到PC上的设备。最新的USB规范修订了理论上高达480Mbps的高速连接。Linux内核支持两种主要类型的USB驱动程序:宿主系统上的驱动程序和设备上的驱动程序,从宿主的观点来看(一个普通的宿主也就是一个PC机),宿主系统的USB设备驱动程序控制插入其中的USB设备,而USB设备的驱动程序控制该设备如何作为一个USB设备和主机通信。

USB的具体构成

在动手写USB驱动程序这前,让我们先看看写的USB驱动程序在内核中的结构,如下图:

USB驱动程序存在于不同的内核子系统和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以下内核中是用.o作模块文件,我们以2.6的内核为准,以下同)。当应用程序需要时再加载到内核空间运行。所以我们所说的一个硬件的驱动程序,通常指的就是一个驱动模块。

设备文件:对于一个设备,它可以在/dev下面存在一个对应的逻辑设备节点,这个节点以文件的形式存在,但它不是普通意义上的文件,它是设备文件,更确切的说,它是设备节点。这个节点是通过mknod命令建立的,其中指定了主设备号和次设备号。主设备号表明了某一类设备,一般对应着确定的驱动程序;次设备号一般是区分不同属性,例如不同的使用方法,不同的位置,不同的操作。这个设备号是从/proc/devices文件中获得的,所以一般是先有驱动程序在内核中,才有设备节点在目录中。这个设备号(特指主设备号)的主要作用,就是声明设备所使用的驱动程序。驱动程序和设备号是一一对应的,当你打开一个设备文件时,操作系统就已经知道这个设备所对应的驱动程序。对于一个硬件,Linux是这样来进行驱动的:首先,我们必须提供一个.ko的驱动模块文件。我们要使用这个驱动程序,首先要加载它,我们可以用insmod xxx.ko,这样驱动就会根据自己的类型(字符设备类型或块设备类型,例如鼠标就是字符设备而硬盘就是块设备)向系统注册,注册成功系统会反馈一个主设备号,这个主设备号就是系统对它的唯一标识。驱动就是根据此主设备号来创建一个一般放置在/dev目录下的设备文件。在我们要访问此硬件时,就可以对设备文件通过open、read、write、close等命令进行。而驱动就会接收到相应的read、write操作而根据自己的模块中的相应函数进行操作了。

USB驱动程序如何应用

了解了上述理论后,我们就可以动手写驱动程序,如果你基本功好,而且写过linux下的硬件驱动,USB的硬件驱动和pci_driver很类似,那么写USB的驱动就比较简单了,如果你只是大体了解了linux的硬件驱动,那也不要紧,因为在linux的内核源码中有一个框架程序可以拿来借用一下,这个框架程序在/usr/src/~(你的内核版本,以下同)/drivers/usb下,文件名为usb-skeleton.c。写一个USB的驱动程序最基本的要做四件事:驱动程序要支持的设备、注册USB驱动程序、探测和断开、提交和控制urb(USB请求块)(当然也可以不用urb来传输数据,下文我们会说到)。

驱动程序支持的设备:有一个结构体struct usb_device_id,这个结构体提供了一列不同类型的该驱动程序支持的USB设备,对于一个只控制一个特定的USB设备的驱动程序来说,struct usb_device_id表被定义为:

/* 驱动程序支持的设备列表*/

static struct usb_device_id skel_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号*/

#define USB_SKEL_VENDOR_ID 0x1234

#define USB_SKEL_PRODUCT_ID 0x2345

这两个值可以通过命令lsusb,当然你得先把USB设备先插到主机上了。或者查看厂商的USB设备的手册也能得到,在我机器上运行lsusb是这样的结果:

Bus 004 Device 001: ID 0000:0000

Bus 003 Device 002: ID 1234:2345 Abc Corp.

Bus 002 Device 001: ID 0000:0000

Bus 001 Device 001: ID 0000:0000

得到这两个值后把它定义到程序里就可以了。

注册USB驱动程序:所有的USB驱动程序都必须创建的结构体是struct usb_driver。这个结构体必须由USB驱动程序来填写,包括许多回调函数和变量,它们向USB核心代码描述USB驱动程序。创建一个有效的struct usb_driver结构体,只须要初始化五个字段就可以了,在框架程序中是这样的:

static struct usb_driver skel_driver = {

.owner =THIS_MODULE,

.name = "skeleton",

.probe = skel_probe,

.disconnect = skel_disconnect,

.id_table = skel_table,

};

struct module *owner :指向该驱动程序的模块所有者的批针。USB核心使用它来正确地对该USB驱动程序进行引用计数,使它不会在不合适的时刻被卸载掉,这个变量应该被设置为THIS_MODULE宏。

const char *name:指向驱动程序名字的指针,在内核的所有USB驱动程序中它必须是唯一的,通常被设置为和驱动程序模块名相同的名字。

int (*probe) (struct usb_interface *intf,const struct usb_device_id *id):这个是指向USB驱动程序中的探测函数的指针。当USB核心认为它有一个接口(usb_interface)可以由该驱动程序处理时,这个函数被调用。

void (disconnect)(struct usb_interface *intf):指向USB驱动程序中的断开函数的指针,当一个USB接口(usb_interface)被从系统中移除或者驱动程序正在从USB核心中卸载时,USB核心将调用这个函数。

const struct usb_device_id *id_table:指向ID设备表的指针,这个表包含了一列该驱动程序可以支持的USB设备,如果没有设置这个变量,USB驱动程序中的探测回调函数就不会被调用。

在这个结构体中还有其它的几个回调函数不是很常用,这里就不一一说明了。以struct usb_driver 指针为参数的usb_register_driver函数调用把struct usb_driver注册到USB核心。一般是在USB驱动程序的模块初始化代码中完成这个工作的:

static int __init usb_skel_init(void)

{

int result;

/* 驱动程序注册到USB子系统中*/

result = usb_register(&skel_driver);

if (result)

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

return result;

}

当USB驱动程序将要被卸开时,需要把struct usb_driver从内核中注销。通过调用usb_deregister_driver来完成这个工作,当调用发生时,当前绑定到该驱动程序上的任何USB接口都被断开,断开函数将被调用:

static void __exit usb_skel_exit(void)

{

/* 从子系统注销驱动程序*/

usb_deregister(&skel_driver);

}

探测和断开:当一个设备被安装而USB核心认为该驱动程序应该处理时,探测函数被调用,探测函数检查传递给它的设备信息,确定驱动程序是否真的适合该设备。当驱动程序因为某种原因不应该控制设备时,断开函数被调用,它可以做一些清理工作。探测回调函数中,USB驱动程序初始化任何可能用于控制USB设备的局部结构体,它还把所需的任何设备相关信息保存到一个局部结构体中,下面是探测函数的部分源码,我们加以分析。

/* 设置端点信息*/

/* 只使用第一个批量IN和批量OUT端点*/

iface_desc = interface->cur_altsetting;

for (i = 0; i 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);

if (!dev->bulk_in_buffer) {

err("Could not allocate bulk_in_buffer");

goto error;

}

}

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("Could not find both bulk-in and bulk-out endpoints");

goto error;

}

在探测函数里,这个循环首先访问该接口中存在的每一个端点,给该端点一个局部指针以便以后访问:

for (i = 0; i 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);

if (!dev->bulk_in_buffer) {

err("Could not allocate bulk_in_buffer");

goto error;

}

因为USB驱动程序要在设备的生命周期的稍后时间获取和接口相关联的局部数据结构体,所以调用了usb_set_intfdata函数,把它保存到struct usb_interface结构体中以便后面的访问

/* 把数据指针保存到这个接口设备中*/

usb_set_intfdata(interface, dev);

我们以后调用usb_set_intfdata函数来获取数据。当这一切都完成后,USB驱动程序必须在探测函数中调用usb_register_dev函数来把该设备注册到USB核心里:

/* 注册设备到USB核心*/

retval = usb_register_dev(interface, &skel_class);

if (retval) {

/* 有些情况下是不允许注册驱动程序的*/

err("Not able to get a minor for this device.");

usb_set_intfdata(interface, NULL);

goto error;

}

当一个USB设备被断开时,和该设备相关联的所有资源都应该被尽可能的清理掉,在此时,如果已在在探测函数中调用了注册函数来为该USB设备分配了一个次设备号话,必须调用usb_deregister_dev函数来把次设备号交还给USB核心。在断开函数中,从接口获取之前调用usb_set_intfdata设置的任何数据也是很重要的。然后设置struct usb_interface结构体中的数据指针为NULL,以防任何不适当的对该数据的错误访问。

在探测函数中会对每一个接口进行一次探测,所以我们在写USB驱动程序的时候,只要做好第一个端点,其它的端点就会自动完成探测。在探测函数中我们要注意的是在内核中用结构体struct usb_host_endpoint来描述USB端点,这个结构体在另一个名为struct usb_endpoint_descriptor的结构体中包含了真正的端点信息,struct usb_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:这个是端点一次可以处理的最大字节数,驱动程序可以发送数量大于此值的数据到端点,在实际传输中,数据量如果大于此值会被分割。

bInterval:这个值只有在端点类型是中断类型时才起作用,它是端点中断请求的间隔时间,以毫秒为单位。

提交和控制urb:当驱动程序有数据要发送到USB设备时(大多数情况是在驱动程序的写函数中),要分配一个urb来把数据传输给设备:

/* 创建一个urb,并且给它分配一个缓存*/

urb = usb_alloc_urb(0, GFP_KERNEL);

if (!urb) {

retval = -ENOMEM;

goto error;

}

当urb被成功分配后,还要创建一个DMA缓冲区来以高效的方式发送数据到设备,传递给驱动程序的数据要复制到这块缓冲中去:

buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);

if (!buf) {

retval = -ENOMEM;

goto error;

}

if (copy_from_user(buf, user_buffer, count)) {

retval = -EFAULT;

goto error;

}

当数据从用户空间正确复制到局部缓冲区后,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_flags |= URB_NO_TRANSFER_DMA_MAP;

然后urb就可以被提交给USB核心以传输到设备了:

/* 把数据从批量OUT端口发出*/

retval = usb_submit_urb(urb, GFP_KERNEL);

if (retval) {

err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);

goto error;

}

当urb被成功传输到USB设备之后,urb回调函数将被USB核心调用,在我们的例子中,我们初始化urb,使它指向skel_write_bulk_callback函数,以下就是该函数:

static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs)

{

struct usb_skel *dev;

dev = (struct usb_skel *)urb->context;

if (urb->status &&

!(urb->status == -ENOENT ||

urb->status == -ECONNRESET ||

urb->status == -ESHUTDOWN)) {

dbg("%s - nonzero write bulk status received: %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);

/*如果读成功,复制到用户空间*/

if (!retval) {

if (copy_to_user(buffer, dev->bulk_in_buffer, count))

retval = -EFAULT;

else

retval = count;

}

usb_bulk_msg接口函数的定义如下:

int usb_bulk_msg(struct usb_device *usb_dev,unsigned int pipe,

void *data,int len,int *actual_length,int timeout);

其参数为:

struct usb_device *usb_dev:指向批量消息所发送的目标USB设备指针。

unsigned int pipe:批量消息所发送目标USB设备的特定端点,此值是调用usb_sndbulkpipe或者usb_rcvbulkpipe来创建的。

void *data:如果是一个OUT端点,它是指向即将发送到设备的数据的指针。如果是IN端点,它是指向从设备读取的数据应该存放的位置的指针。

int len:data参数所指缓冲区的大小。

int *actual_length:指向保存实际传输字节数的位置的指针,至于是传输到设备还是从设备接收取决于端点的方向。

int timeout:以Jiffies为单位的等待的超时时间,如果该值为0,该函数一直等待消息的结束。

如果该接口函数调用成功,返回值为0,否则返回一个负的错误值。

usb_control_msg接口函数定义如下:

int usb_control_msg(struct usb_device *dev,unsigned int pipe,__u8 request,__u8requesttype,__u16 value,__u16 index,void *data,__u16 size,int timeout)

除了允许驱动程序发送和接收USB控制消息之外,usb_control_msg函数的运作和usb_bulk_msg函数类似,其参数和usb_bulk_msg的参数有几个重要区别:

struct usb_device *dev:指向控制消息所发送的目标USB设备的指针。

unsigned int pipe:控制消息所发送的目标USB设备的特定端点,该值是调用usb_sndctrlpipe或usb_rcvctrlpipe来创建的。

__u8 request:控制消息的USB请求值。

__u8 requesttype:控制消息的USB请求类型值。

__u16 value:控制消息的USB消息值。

__u16 index:控制消息的USB消息索引值。

void *data:如果是一个OUT端点,它是指身即将发送到设备的数据的指针。如果是一个IN端点,它是指向从设备读取的数据应该存放的位置的指针。

__u16 size:data参数所指缓冲区的大小。

int timeout:以Jiffies为单位的应该等待的超时时间,如果为0,该函数将一直等待消息结束。

如果该接口函数调用成功,返回传输到设备或者从设备读取的字节数;如果不成功它返回一个负的错误值。

这两个接口函数都不能在一个中断上下文中或者持有自旋锁的情况下调用,同样,该函数也不能被任何其它函数取消,使用时要谨慎。

我们要给未知的USB设备写驱动程序,只需要把这个框架程序稍做修改就可以用了,前面我们已经说过要修改制造商和产品的ID号,把0xfff0这两个值改为未知USB的ID号。

#define USB_SKEL_VENDOR_ID 0xfff0

#define USB_SKEL_PRODUCT_ID 0xfff0

还有就是在探测函数中把需要探测的接口端点类型写好,在这个框架程序中只探测了批量(USB_ENDPOINT_XFER_BULK)IN和OUT端点,可以在此处使用掩码(USB_ENDPOINT_XFERTYPE_MASK)让其探测其它的端点类型,驱动程序会对USB设备的每一个接口进行一次探测,当探测成功后,驱动程序就被绑定到这个接口上。再有就是urb的初始化问题,如果你只写简单的USB驱动,这块不用多加考虑,框架程序里的东西已经够用了,这里我们简单介绍三个初始化urb的辅助函数:

usb_fill_int_urb 它的函数原型是这样的:

void usb_fill_int_urb(struct urb *urb,struct usb_device *dev,

unsigned int pipe,void *transfer_buff,

int buffer_length,usb_complete_t complete,

void *context,int interval);

这个函数用来正确的初始化即将被发送到USB设备的中断端点的urb。

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

这个函数是用来正确的初始化批量urb端点的。

usb_fill_control_urb 它的函数原型是这样的:

void usb_fill_control_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);

这个函数是用来正确初始化控制urb端点的。

还有一个初始化等时urb的,它现在还没有初始化函数,所以它们在被提交到USB核心前,必须在驱动程序中手工地进行初始化,可以参考内核源代码树下的/usr/src/~/drivers/usb/media下的konicawc.c文件。

驱动模块的编译、配置和使用

现在我们的驱动程序已经大体写好了,然后在linux下把它编译成模块就可以把驱动模块插入到内核中运行了,编译的Makefile文件可以这样来写:

ifneq ($(KERNELRELEASE),)

obj-m := xxx.o

else

KERNELDIR ?= /lib/modules/$(shell uname -r)/build

PWD := $(shell pwd)

default:

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

endif

clean:

rm -rf *.mod.* *.o *.ko .*.ko.* .tmp* .*.mod.o.* .*.o.*

其中xxx是源文件的文件名,在linux下直接执行make就可以生成驱动模块(xxx.ko)了。生成驱动模块后使用insmod xxx.ko就可以插入到内核中运行了,用lsmod可以看到你插入到内核中的模块,也可以从系统中用命令rmmod xxx把模块卸载掉;如果把编译出来的驱动模块拷贝到/lib/modules/~/kernel/drivers/usb/下,然后depmod一下,那么你在插入USB设备的时候,系统就会自动为你加载驱动模块的;当然这个得有hotplug的支持;加载驱动模块成功后就会在/dev/下生成设备文件了,如果用命令cat /proc/bus/usb/devices,我们可以看到驱动程序已经绑定到接口上了:

T: Bus=03 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 2 Spd=12 MxCh= 0

D: Ver= 1.10 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1

P: Vendor=1234 ProdID=2345 Rev= 1.10

C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr= 0mA

I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=test_usb_driver /*我们的驱动*/

E: Ad=01(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms

E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms

此框架程序生成的是skel0(可以自由修改)的设备文件,现在就可以对这个设备文件进行打开、读写、关闭等的操作了。

面对层出不穷的新的USB设备,必须有人不断编写新的驱动程序以便让这些设备能够在linux下正常的工作,从这个意义上讲,驱动程序的编写本身就是一件非常有意义的工作,本文只是起到一个抛砖引玉的作用,帮助那些有志于写驱动程序的开发人员进一步了解USB驱动程序的设计思路,从而吸引更多的人加入到这个队伍中来。linux不仅为我们提供了一个顶级质量的操作系统,而且也为我们提供了参与到其未来开发过程的机会,我们完全可以从中得到无尽的快乐!

分享到:
评论

相关推荐

    Linux_USB.rar_USB LINUX_linux usb_linux usb驱动_usb_usb linux

    Linux环境下USB的原理、驱动和配置, 很不错

    Linux ARM下的USB驱动开发.pdf

    本文介绍了嵌入式Linux下的USB驱动开发原理,并阐述了在Linux下的Z228 USB驱动程序的具体实现。文章首先介绍了USB的发展历史和特点,然后讨论了USB驱动程序的编写技术。 USB是一种新的接口标准,自1994年由Compaq、...

    嵌入式Linux中USB摄像头驱动的研究.pdf

    本文介绍了嵌入式Linux系统中USB摄像头驱动程序的开发和应用,涵盖了USB设备驱动程序的基本原理、USB主机驱动程序的组成、USB摄像头驱动程序的实现和应用等方面的内容,为嵌入式Linux系统开发和应用提供了有价值的...

    Linux系统中USB设备驱动的开发.pdf

    本文主要讨论Linux系统中USB设备驱动的开发,包括USB系统的组成、特点、Linux中使用的USB工作原理、USB驱动程序开发、USB子系统的驱动结构和设备初始化及配置等方面的内容。 一、USB系统组成及特点 USB(Universal...

    嵌入式linux下基于libusb的USB驱动开发.pdf

    本文将介绍如何在嵌入式 Linux 系统下使用 libusb 库开发 USB 驱动程序,并详细讨论 libusb 库的使用方法和 USB 驱动开发的实现原理。 1. libusb 库简介 libusb 是一个跨平台的 USB 编程库,提供了一个统一的 API...

    介绍Linux操作系统下的USB硬件设备驱动.doc

    Linux 操作系统下的 USB 硬件设备驱动 Linux 操作系统下的 USB ...Linux 操作系统下的 USB 硬件设备驱动是非常复杂的,需要了解 Linux 操作系统的基本原理和 USB 硬件设备的特点,才能正确地配置和驱动 USB 硬件设备。

    嵌入式设计及linux驱动开发指南——基于ARM9处理器.pdf

    9.2 Linux下USB系统文件结点 9.3 USB主机驱动结构 9.3.1 USB数据传输时序 9.3.2 USB设备连接/断开时序 9.4 主要数据结构及接口函数 9.4.1 数据传输管道 9.4.2 统一的USB数据传输块 9.4.3 USBD数据描述 9.4.4...

    Linux USB子系统集线器驱动程序研究.pdf

    同时,文章还介绍了USB设备的客户端驱动程序的编写原理和实现机制,分析了Linux USB子系统的客户端驱动程序的功能和作用。 一、USB子系统概述 USB(Universal Serial Bus)是一种计算机外围串行通信接口标准,...

    Linux下无线网络终端驱动的开发.pdf

    本文将详细介绍 Linux 下无线网络终端驱动的开发,包括 Linux 下网络设备的工作原理、无线网卡驱动程序结构、设备初始化和卸载、数据发送等方面的知识点。 Linux 下网络设备的工作原理 在 Linux 中,所有外围的...

    Linux驱动开发步骤

    Linux驱动开发步骤 Linux驱动开发是指针对Linux操作系统开发设备驱动程序的过程。...只有了解设备的操作方法、内核中的驱动程序结构、驱动程序的配置和编译过程等信息,开发者才能更好地设计和实现驱动程序。

    嵌入式linux android驱动工程师 面试题总汇.pdf

    本文档总结了嵌入式 Linux Android 驱动工程师面试中的常见问题和答案,涵盖了嵌入式系统、中断服务子程序、volatile 变量、Linux 内核内存申请、IRQ 和 FIQ、中断上半部分和下半部分、内核函数 mmap 实现原理、并发...

    在Linux上设置激活声卡.pdf

    Linux音频系统配置指南 本文将详细介绍 Linux 操作系统中的音频系统配置过程。...Linux 音频系统配置需要了解音频系统的基本原理和技术参数,正确地配置音频设备和音频服务器,可以让音卡在操作系统中正常工作。

    嵌入式Linux之我行系列

    ·嵌入式Linux之我行——PWM在ARM Linux中的原理和蜂鸣器驱动实例开发 ·嵌入式Linux之我行——S3C2440上RTC时钟驱动开发实例讲解 ·嵌入式Linux之我行——S3C2440上看门狗(Watchdog)驱动开发实例讲解 ·嵌入式Linux...

    NVIDIA Jetson Linux驱动程序包开发人员指南 - 快速入门指南.pdf

    【NVIDIA Jetson Linux驱动程序包...开发者需要熟悉Linux操作系统,了解嵌入式系统的原理,并具备一定的软件编译和调试能力。通过遵循指南的步骤,可以顺利地在各种Jetson设备上搭建开发环境并开始驱动程序的开发工作。

    CP2104—USB转串口芯片中文手册

    CP2104提供了详细的软件支持,包括Windows、Mac、Linux等多种操作系统的驱动程序和开发包。 结论 CP2104是一款功能强大且灵活的USB转串口芯片,广泛应用于各种设备和应用场景。它提供了详细的开发和设计指南、软件...

    libusb-win32 环境安装

    对于数据偏少的应用, 我们可以利用USB虚拟串口的方式来完成这样的任务, 虚拟串口的驱动和实例, 对于下位机来说也非常常见. 有个问题: 很多应用无法用虚拟串口的方式来得到满足, 只能按照USB的方式来解决问题. 这个...

    USB学习笔记-USB Video Class及其实现.pdf

    在Linux操作系统环境下,尽管目前对Video Class的支持相对较少,尚未有广泛可用的开源主机端驱动,但设备端的驱动实现已有所讨论。开发这样的驱动需要理解USB协议、Video Class规范以及Linux内核的驱动模型。尽管...

Global site tag (gtag.js) - Google Analytics