Linux USB 驱动开发(三)—— 编写USB 驱动程序

前面学习了USB驱动的一些基础概念与重要的数据结构,那么究竟如何编写一个USB 驱动程序呢?编写与一个USB设备驱动程序的方法和其他总线驱动方式类似,驱动程序把驱动程序对象注册到USB子系统中,稍后再使用制造商和设备标识来判断是否安装了硬件。当然,这些制造商和设备标识需要我们编写进USB 驱动程序中。

USB 驱动程序依然遵循设备模型 —— 总线、设备、驱动。和I2C 总线设备驱动编写一样,所有的USB驱动程序都必须创建的主要结构体是 struct usb_driver,它们向USB 核心代码描述了USB 驱动程序。但这是个外壳,只是实现设备和总线的挂接,具体的USB 设备是什么样的,如何实现的,比如一个字符设备,我们还需填写相应的文件操作接口 ,下面我们从外到里进行剖析,学习如何搭建这样的一个USB驱动外壳框架:

一、注册USB驱动程序

Linux的设备驱动,特别是这种hotplug的USB设备驱动,会被编译成模块,然后在需要时挂在到内核。所以USB驱动和注册与正常的模块注册、卸载是一样的,下面是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");

USB设备驱动的模块加载函数通用的方法是在I2C设备驱动的模块加载函数中使用usb_register(struct *usb_driver)函数添加usb_driver的工作,而在模块卸载函数中利用usb_deregister(struct *usb_driver)做相反的工作。 对比I2C设备驱动中的 i2c_add_driver(&i2c_driver)i2c_del_driver(&i2c_driver)

struct usb_driver是USB设备驱动,我们需要实现其成员函数:

static struct usb_driver skel_driver = {
     .owner = THIS_MODULE,
     .name = "skeleton",
     .id_table = skel_table,
     .probe = skel_probe,
     .disconnect = skel_disconnect,
};    

从代码看来,usb_driver需要初始化五个字段:

模块的所有者 THIS_MODULE

模块的名字  skeleton

probe函数   skel_probe

disconnect函数skel_disconnect

id_table

最重要的当然是probe函数与disconnect函数,这个在后面详细介绍,先谈一下id_table:

id_table 是struct usb_device_id 类型,包含了一列该驱动程序可以支持的所有不同类型的USB设备。如果没有设置该变量,USB驱动程序中的探测回调该函数将不会被调用。对比I2C中struct i2c_device_id *id_table,一个驱动程序可以对应多个设备,i2c 示例:

static const struct i2c_device_id mpu6050_id[] = {
    { "mpu6050", 0},
    {}
};  

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

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。后面一个参数是设备表,这个设备表的最后一个元素是空的,用于标识结束。代码定义了USB_SKEL_VENDOR_ID是0xfff0,USB_SKEL_PRODUCT_ID是0xfff0,也就是说,当有一个设备接到集线器时,usb子系统就会检查这个设备的vendor
ID和product ID,如果它们的值是0xfff0时,那么子系统就会调用这个skeleton模块作为设备的驱动。

当USB设备接到USB控制器接口时,usb_core就检测该设备的一些信息,例如生产厂商ID和产品的ID,或者是设备所属的class、subclass跟protocol,以便确定应该调用哪一个驱动处理该设备。

我们下面所要做的就是对probe函数与disconnect函数的填充了,但是在对probe函数与disconnect函数填充之前,有必要先学习三个重要的数据结构,这在我们后面probe函数与disconnect函数中有很大的作用:

二、USB驱动程序中重要数据结构

1、usb-skeleton

usb-skeleton 是一个局部结构体,用于与端点进行通信。下面先看一下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设备的结构体udev

一个接口interface

用于并发访问控制的semaphore(信号量) limit_sem

用于接收数据的缓冲bulk_in_buffer

用于接收数据的缓冲尺寸bulk_in_size

批量输入端口地址bulk_in_endpointAddr

批量输出端口地址bulk_out_endpointAddr

内核使用的引用计数器

从开发人员的角度看,每一个usb设备有若干个配置(configuration)组成,每个配置又可以有多个接口(interface)(我理解就是USB设备的一项功能),每个接口又有多个设置,而接口本身可能没有端点或者多个端点(end point)

2、USB 接口数据结构 struct usb_interface

struct usb_interface
{
         struct usb_host_interface *altsetting;
         struct usb_host_interface *cur_altsetting;
         unsigned num_altsetting;
         int minor;
         enum usb_interface_condition condition;
         unsigned is_active:1;
         unsigned needs_remote_wakeup:1;
         struct device dev;
         struct device *usb_dev;
         int pm_usage_cnt;
};  

在逻辑上,一个USB设备的功能划分是通过接口来完成的。比如说一个USB扬声器,可能会包括有两个接口:一个用于键盘控制,另外一个用于音频流传输。而事实上,这种设备需要用到不同的两个驱动程序来操作,一个控制键盘,一个控制音频流。但也有例外,比如蓝牙设备,要求有两个接口,第一用于ACL跟EVENT的传输,另外一个用于SCO链路,但两者通过一个驱动控制。在Linux上,接口使用struct usb_interface来描述,以下是该结构体中比较重要的字段:

a -- struct usb_host_interface *altsetting(注意不是usb_interface)

其实据我理解,他应该是每个接口的设置,虽然名字上有点奇怪。该字段是一个设置的数组(一个接口可以有多个设置),每个usb_host_interface都包含一套由struct usb_host_endpoint定义的端点配置。但这些配置次序是不定的。

b -- struct usb_host_interface *cur_altsetting

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

struct usb_host_interface数据结构:

struct usb_host_interface
{
         struct usb_interface_descriptor desc;//usb描述符,主要有四种usb描述符,设备描述符,配置描述符,接口描述符和端点描述符,协议里规定一个usb设备是必须支持这四大描述符的。
                                 //usb描述符放在usb设备的eeprom里边
         /* array of desc.bNumEndpoint endpoints associated with this
          * interface setting. these will be in no particular order.
          */
         struct usb_host_endpoint *endpoint;//这个设置所使用的端点  

         char *string;           /* iInterface string, if present */
         unsigned char *extra;   /* Extra descriptors */关于额外描述符
         int extralen;
};  

c -- unsigned num_altstting

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

d -- int minor

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

3、USB 端点 struct usb_host_endpoint

Linux中用struct usb_host_endpoint 来描述USB端点

struct usb_host_endpoint
{
         struct usb_endpoint_descriptor desc;
         struct list_head                urb_list;//端点要处理的urb队列.urb是usb通信的主角,设备中的每个端点都可以处理一个urb队列.要想和你的usb通信,就得创建一个urb,并且为它赋好值,
                                   //交给咱们的usb core,它会找到合适的host controller,从而进行具体的数据传输
         void                            *hcpriv;//这是提供给HCD(host controller driver)用的
         struct ep_device                *ep_dev;        /* For sysfs info */  

         unsigned char *extra;   /* Extra descriptors */
         int extralen;
};  

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

a -- bEndpointAddress(b for byte)

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

b -- bmAttributes

端点的类型,结合USB_ENDPOINT_XFERTYPE_MASK可以确定端点是USB_ENDPOINT_XFER_ISOC(等时)、USB_ENDPOINT_XFER_BULK(批量)还是USB_ENDPOINT_XFER_INT(中断)。

c -- wMaxPacketSize

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

d -- bInterval

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

三、探测和断开函数分析

USB驱动程序指定了两个USB核心在适当时间调用的函数。

1、探测函数

当一个设备被安装而USB核心认为该驱动程序应该处理时,探测函数被调用;

        探测函数应该检查传递给他的设备信息确定驱动程序是否真的适合该设备。当驱动程序因为某种原因不应控制设备时,断开函数被调用,它可以做一些清洁的工作。

系统会传递给探测函数的信息是什么呢?一个usb_interface * 跟一个struct usb_device_id *作为参数。他们分别是该USB设备的接口描述(一般会是该设备的第0号接口,该接口的默认设置也是第0号设置)跟它的设备ID描述(包括Vendor ID、Production ID等)。

USB驱动程序应该初始化任何可能用于控制USB设备的局部结构体,它还应该把所需的任何设备相关信息保存到局部结构体中。例如,USB驱动程序通常需要探测设备对的端点地址和缓冲区大小,因为需要他们才能和端点通信

下面具体分析探测函数做了哪些事情:

a -- 探测设备的端点地址、缓冲区大小,初始化任何可能用于控制USB设备的数据结构

下面是一个实例代码,他们探测批量类型的IN和OUT端点,把相关信息保存到一个局部设备结构体中:

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

具体流程如下:

该代码块首先循环访问该接口中存在的每一个端点,赋予该端点结构体的局部指针以使稍后的访问更加容易

for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
         endpoint = &iface_desc->endpoint[i].desc; 

然后,我们有了一个端点,而还没有发现批量IN类型的端点时,查看该端点的方向是否为IN。这可以通过检查位掩码 USB_DIR_IN 是否包含在bEndpointAddress 端点变量中来确定。如果是的话,我们测定该端点类型是否批量,这首先通过USB_ENDPOINT_XFERTYPE_MASK 位掩码来取bmAttributes变量的值,然后检查它是否和USB_ENDPOINT_XFER_BULK 的值匹配来完成

 if ( !dev->bulk_in_endpointAddr &&
                ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) = = USB_DIR_IN) &&
             ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) = = USB_ENDPOINT_XFER_BULK)) {   

如果这些都通过了,驱动程序就知道它已经发现了正确的端点类型,可以把该端点相关的信息保存到一个局部结构体中,就是我们前面的usb_skel ,以便稍后使用它和端点进行通信:

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

b -- 把已经初始化数据结构的指针保存到接口设备中

接下来的工作是向系统注册一些以后会用的的信息。首先我们来说明一下usb_set_intfdata()他向内核注册一个data,这个data的结构可以是任意的,这段程序向内核注册了一个usb_skel结构,就是我们刚刚看到的被初始化的那个,这个data可以在以后用usb_get_intfdata来得到

usb_set_intfdata(interface, dev);

c -- 注册USB设备

       如果USB驱动程序没有和处理设备与用户交互(例如输入、tty、视频等)的另一种类型的子系统相关联,驱动程序可以使用USB主设备号,以便在用户空间使用传统的字符驱动程序接口。如果要这样做,USB驱动程序必须在探测函数中调用 usb_resgister_dev 函数来把设备注册到USB核心。只要该函数被调用,就要确保设备和驱动陈旭都处于可以处理用户访问设备的要求的恰当状态

retval = usb_register_dev(interface, &skel_class);

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,而不是device,因此,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,
};  

2、断开函数

当设备被拔出集线器时,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);  

四、USB请求块

usb request block,简称urb。事实上,可以打一个这样的比喻,usb总线就像一条高速公路,货物、人流之类的可以看成是系统与设备交互的数据,而urb就可以看成是汽车。在一开始对USB规范细节的介绍,我们就说过USB的endpoint有4种不同类型,也就是说能在这条高速公路上流动的数据就有四种。但是这对汽车是没有要求的,所以urb可以运载四种数据,不过你要先告诉司机你要运什么,目的地是什么。我们现在就看看struct
urb的具体内容。

struct urb
{
	/* 私有的:只能由usb核心和主机控制器访问的字段 */
	struct kref kref; /*urb引用计数 */
	spinlock_t lock; /* urb锁 */
	void *hcpriv; /* 主机控制器私有数据 */
	int bandwidth; /* int/iso请求的带宽 */
	atomic_t use_count; /* 并发传输计数 */
	u8 reject; /* 传输将失败*/

	/* 公共的: 可以被驱动使用的字段 */
	struct list_head urb_list; /* 链表头*/
	struct usb_device *dev; /* 关联的usb设备 */
	unsigned int pipe; /* 管道信息 */
	int status; /* urb的当前状态 */
	unsigned int transfer_flags; /* urb_short_not_ok | ...*/
	void *transfer_buffer; /* 发送数据到设备或从设备接收数据的缓冲区 */
	dma_addr_t transfer_dma; /*用来以dma方式向设备传输数据的缓冲区 */
	int transfer_buffer_length;/*transfer_buffer或transfer_dma 指向缓冲区的大小 */

	int actual_length; /* urb结束后,发送或接收数据的实际长度 */
	unsigned char *setup_packet; /* 指向控制urb的设置数据包的指针*/
	dma_addr_t setup_dma; /*控制urb的设置数据包的dma缓冲区*/
	int start_frame; /*等时传输中用于设置或返回初始帧*/
	int number_of_packets; /*等时传输中等时缓冲区数据 */
	int interval; /* urb被轮询到的时间间隔(对中断和等时urb有效) */
	int error_count;  /* 等时传输错误数量 */
	void *context; /* completion函数上下文 */
	usb_complete_t complete; /* 当urb被完全传输或发生错误时,被调用 */
	struct usb_iso_packet_descriptor iso_frame_desc[0];
	/*单个urb一次可定义多个等时传输时,描述各个等时传输 */
};

1、urb申请

函数定义如下:

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

usb_alloc_urb开辟一个urb空间并对其部分的成员初始化。

iso_packets:等时传输的包数

mem_flags:开辟urb空间的mem旗标,一般为GFP_KERNEL

struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{
         struct urb *urb;
         urb = kmalloc(sizeof(struct urb) +iso_packets * sizeof(struct usb_iso_packet_descriptor),
                           mem_flags);  --开辟空间
         if (!urb) {
                   return NULL;
         }
         usb_init_urb(urb);   --初始化部分成员
         return urb;
}
void usb_init_urb(struct urb *urb)
{
         if (urb) {
                   memset(urb, 0, sizeof(*urb));
                   kref_init(&urb->kref);              --初始化urb计数器
                   INIT_LIST_HEAD(&urb->anchor_list);  --初始化锁定链表
         }
}

2、usb_submit_urb 提交urb

int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
{
         int                                xfertype, max;
         struct usb_device             *dev;
         struct usb_host_endpoint       *ep;
         int                                is_out;

         if (!urb || urb->hcpriv || !urb->complete)
                   return -EINVAL;
         dev = urb->dev;
         if ((!dev) || (dev->state < USB_STATE_DEFAULT))
                   return -ENODEV;
         ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out) [usb_pipeendpoint(urb->pipe)]; --查找设备端点
         if (!ep)
                   return -ENOENT;
         urb->ep = ep;
         urb->status = -EINPROGRESS;
         urb->actual_length = 0;
         xfertype = usb_endpoint_type(&ep->desc);
         if (xfertype == USB_ENDPOINT_XFER_CONTROL) {  --如果是控制类型
                   struct usb_ctrlrequest *setup = (struct usb_ctrlrequest *) urb->setup_packet;
                   if (!setup)
                            return -ENOEXEC;
                   is_out = !(setup->bRequestType & USB_DIR_IN) ||!setup->wLength;
         } else {
                   is_out = usb_endpoint_dir_out(&ep->desc);
         }
         urb->transfer_flags = (urb->transfer_flags & ~URB_DIR_MASK) | (is_out ? URB_DIR_OUT : URB_DIR_IN);  --缓存的方向留待后用
         if (xfertype != USB_ENDPOINT_XFER_CONTROL &&dev->state < USB_STATE_CONFIGURED)  --在设备没有配置前,传输的类型必为控制类型,唯有设备配置完成后才能传输其他类型的urb
                   return -ENODEV;
         max = le16_to_cpu(ep->desc.wMaxPacketSize);
         if (max <= 0) {
                   return -EMSGSIZE;
         }
         if (xfertype == USB_ENDPOINT_XFER_ISOC) {  --等时传输
                   int    n, len;
                   if (dev->speed == USB_SPEED_HIGH) {
                            int    mult = 1 + ((max >> 11) & 0x03);
                            max &= 0x07ff;
                            max *= mult;
                   }
                   if (urb->number_of_packets <= 0)
                            return -EINVAL;
                   for (n = 0; n < urb->number_of_packets; n++) {
                            len = urb->iso_frame_desc[n].length;
                            if (len < 0 || len > max)
                                     return -EMSGSIZE;
                            urb->iso_frame_desc[n].status = -EXDEV;
                            urb->iso_frame_desc[n].actual_length = 0;
                   }
         }
         if (urb->transfer_buffer_length < 0)
                   return -EMSGSIZE;
         switch (xfertype) {  --对等时传输和中断传输的等待时间进行检测
         case USB_ENDPOINT_XFER_ISOC:
         case USB_ENDPOINT_XFER_INT:
                   if (urb->interval <= 0)
                            return -EINVAL;
                   switch (dev->speed) {
                   case USB_SPEED_HIGH:
                            /* NOTE usb handles 2^15 */
                            if (urb->interval > (1024 * 8))
                                     urb->interval = 1024 * 8;
                            max = 1024 * 8;
                            break;
                   case USB_SPEED_FULL:
                   case USB_SPEED_LOW:
                            if (xfertype == USB_ENDPOINT_XFER_INT) {
                                     if (urb->interval > 255)
                                               return -EINVAL;
                                     max = 128;
                            } else {
                                     if (urb->interval > 1024)
                                               urb->interval = 1024;
                                     max = 1024;
                            }
                            break;
                   default:
                            return -EINVAL;
                   }
                   urb->interval = min(max, 1 << ilog2(urb->interval));
         }
         return usb_hcd_submit_urb(urb, mem_flags);
}
时间: 2024-10-15 22:31:22

Linux USB 驱动开发(三)—— 编写USB 驱动程序的相关文章

Linux USB 驱动开发(五)—— USB驱动程序开发过程简单总结

设备驱动程序是操作系统内核和机器硬件之间的接口,由一组函数和一些私有数据组成,是应用程序和硬件设备之间的桥梁.在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作. 设备驱动程序是内核的一部分,主要完成以下功能:对设备的初始化和释放:把数据从内核传送到硬件设备和从硬件设备读取数据:读取应用程序数据传送给设备文件和回送应用程序请求的数据:检测和处理硬件设备出现的错误. 一. Linux USB子系统分析 在Linux系统中,USB主机驱动程序由3部分组成:US

Linux设备驱动开发基础

1.驱动概述和开发环境搭建 1.1驱动设备的作用 对设备驱动最通俗的解释就是"驱动硬件设备行动".驱动与底层硬件直接打交道,按照硬件设备的具体工作方式,读写设备的寄存器,完成设备的轮训.中断处理.DMA通信,进行物理内存向虚拟内存的映射等,最终让通信设备能收发数据,让显示设备能显示文字和画面,让存储设备能记录文件和数据. 由此可见,设备驱动充当了硬件和应用软件之间的纽带,他使得应用软件只需要调用系统软件的应用编程接口(API)就可让硬件去完成要求的工作.在系统中没有操作系统的情况下,工

《Linux设备驱动开发详解(第3版)》海量更新总结

本博实时更新<Linux设备驱动开发详解(第3版)>的最新进展. 2015.2.26 几乎完成初稿. [F]是修正或升级:[N]是新增知识点:[D]是删除的内容 第1章 <Linux设备驱动概述及开发环境构建>[D]删除关于LDD6410开发板的介绍[F]更新新的Ubuntu虚拟机[N]添加关于QEMU模拟vexpress板的描述 第2章 <驱动设计的硬件基础> [N]增加关于SoC的介绍:[N]增加关于eFuse的内容:[D]删除ISA总线的内容了:[N]增加关于SP

《Linux设备驱动开发具体解释(第3版)》进展同步更新

本博实时更新<Linux设备驱动开发具体解释(第3版)>的最新进展. 2015.2.26 差点儿完毕初稿. 本书已经rebase到开发中的Linux 4.0内核,案例多数基于多核CORTEX-A9平台. [F]是修正或升级:[N]是新增知识点:[D]是删除的内容 第1章 <Linux设备驱动概述及开发环境构建>[D]删除关于LDD6410开发板的介绍[F]更新新的Ubuntu虚拟机[N]加入关于QEMU模拟vexpress板的描写叙述 第2章 <驱动设计的硬件基础> [

Hasen的linux设备驱动开发学习之旅--异步I/O

/** * Author:hasen * 参考 :<linux设备驱动开发详解> * 简介:android小菜鸟的linux * 设备驱动开发学习之旅 * 主题:异步I/O * Date:2014-11-11 */ linux中最常用的输入/输出(I/O)模型是同步I/O.在这个模型中,请求发出后,应用就会阻塞,知道请求满足 为止.但是在某些情况下,I/O请求可能需要与其他的进程进行交叠.可移植操作系统接口(POSIX)异步I/O(AIO) 应用程序接口(API)就提供了这种功能. AIO基本

Linux 设备驱动开发 —— platform设备驱动应用实例解析

前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 -- platform 设备驱动 ,下面将通过一个实例来深入我们的学习. 一.platform 驱动的工作过程 platform模型驱动编程,platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳. 在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中.那么我们编写platform模型驱动时,需要完成两个工作: a -- 实现platform驱动 架构就

linux设备驱动第三篇:写一个简单的字符设备驱动

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存. 下面就开始学习如何写一个简单的字符设备驱动.首先我们来分解一下字符设备驱动都有那些结构或者方法组成,也就是说实现一个可以使用的字符设备驱动我们必须做些什么工作. 1.主设备号和次设备号 对于字符设备的访问是通过文件系统中的设备名称进行的.他们通常位于/dev目录下.如下: [plain] vie

linux设备驱动第三篇:如何写一个简单的字符设备驱动?

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存. 下面就开始学习如何写一个简单的字符设备驱动.首先我们来分解一下字符设备驱动都有那些结构或者方法组成,也就是说实现一个可以使用的字符设备驱动我们必须做些什么工作. 1.主设备号和次设备号 对于字符设备的访问是通过文件系统中的设备名称进行的.他们通常位于/dev目录下.如下: [email prot

linux设备驱动第三篇:如何实现简单的字符设备驱动

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存. 下面就开始学习如何写一个简单的字符设备驱动.首先我们来分解一下字符设备驱动 都有那些结构或者方法组成,也就是说实现一个可以使用的字符设备驱动我们必须做些什么工作. 1.主设备号和次设备号 对于字符设备的访问是通过文件系统中的设备名称进行的.他们通常位于/dev目录下.如下: [email pro

Hasen的linux设备驱动开发学习之旅--异步通知

/** * Author:hasen * 参考 :<linux设备驱动开发详解> * 简介:android小菜鸟的linux * 设备驱动开发学习之旅 * 主题:异步通知 * Date:2014-11-05 */ 一.异步通知的概念和作用 阻塞和非阻塞访问.poll()函数提供了较好地解决设备访问的机制,但是如果有了异步通知整套机制就更 加完整了. 异步通知的意思是:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这 一点非常类似于硬件上"中断"的概