嵌入式Linux驱动程序设计

Unix like 系统中,正是有了驱动程序才使得用户可以完全透明的使用计算机系统。设备驱动隐藏了硬件设备的具体的细节和功能,对于不同的硬件设备都提供了一致的接口。比如在 Linux 系统中,为了便于用户的使用,系统把计算机系统的各种设备映射成一系列的特殊的设备文件,叫设备文件节点。用户可以任意的使用它来协助自己完成任何工作。在 Linux 系统中,所有的设备都可归为三类之列,一是字符设备,如键盘等;二是块设备,如磁盘等;第三种就是用于网络的网络设备,这类设备主要是用于网络通讯中,在其他的地方没有什么特别使用。

 

一、              设备驱动程序的三个主要组成部分

1、  自动配置和初始化子程序;

2、  I/O 提供服务的子程序;

3、  中断子程序;

 

二、              设备驱动入口点分析

此处以字符设备为例列举设备驱动程序的入口点

1、  open 入口点:设备 I/O 操作之前进行的操作,打开了设备后准备 I/O 操作;

2、  close 入口点:对设备操作完成之后对设备的关闭操作;

3、  read 入口点:完成对设备的 open 操作之后就可以以此入口点在设备上读取数据;

4、  write 入口点:完成对设备的 open 操作之后就可以以此入口点在设备上写入数据;

5、  ioctl 入口点:在设备上执行 I/O 控制操作;

6、  select 入口点:在数据写入或读取之前对设备的检测操作。

 

三、              Linux 系统下的设备驱动程序

以下借用 Linux 系统内核的代码来分析字符设备驱动程序

1 、以下是入口点的结构体定义:

#/usr/include/linux/fs.h

struct file_operations {

    struct module *owner;

    loff_t (*llseek) (struct file *, loff_t, int);

    ssize_t (*read) (struct file *, char *, size_t, loff_t *);

    ssize_t (*write) (struct file *, const char *, size_t, loff_t *);

    int (*readdir) (struct file *, void *, filldir_t);

    unsigned int (*poll) (struct file *, struct poll_table_struct *);

    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

    int (*mmap) (struct file *, struct vm_area_struct *);

    int (*open) (struct inode *, struct file *);

    int (*flush) (struct file *);

     int (*release) (struct inode *, struct file *);

    int (*fsync) (struct file *, struct dentry *, int datasync);

    int (*fasync) (int, struct file *, int);

    int (*lock) (struct file *, int, struct file_lock *);

    ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);

    ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);

    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

};

2 、入口点 file_operations 分析:

       1>  lseek ,移动文件指针的位置,用于可随机存取的设备;

2>       read ,设备读操作,参数 buf 指向的是存放读取结果的缓冲池, count 为将要读取的数据的长度。若函数返回值为负,表示读取操作出错;若操作成功,则返回实际的读取的字节;

3>       write ,设备写操作;

4>       ioctl ,进行除设备的读写之外的控制操作;

5>       open ,打开一个设备,并对其 I/O 操作准备。函数返回值为 0 表示设备打开成功,若返回负,表示操作失败;

6>       release ,设备的 close 操作;

4、  Linux 系统下,字符设备驱动的注册:

Linux 里通过调用 register_chrdev 向操作系统注册字符型的设备驱动,其定义为:

int register_chrdev(unsigned int major, const char *name,

                const struct file_operations *fops)

参数说明:

major 是设备驱动程序注册的主设备号,若为 0 则系统为此驱动程序动态地分配一个主设备号。

name 是设备文件节点名。此函数返回 0 表示成功。返回 -EINVAL 表示申请的主设备号不合法,一般来说是因为主设备号大于系统所允许的最大设备号。返回 -EBUSY 表示所申请的主设备号正在被其它设备驱动程序使用。如果是动态分配主设备号成功,此函数将返回所分配的主设备号。如果 register_chrdev 操作成功,设备名就可以在 /proc/devices 文件里看到。

这里看到的就是一段设备驱动经过注册之后在 /proc/devices 下面看到的结果

 

四、              字符设备驱动程序示例及其分析

//#define CONFIG_DEVFS_FS  /* 系统是否支持 devfs*/

#ifndef __KERNEL__             /* 编译进内核 */

# define __KERNEL__

#endif

#ifndef MODULE

#  define MODULE          /* 以模块方式编译 */

#endif

#include <linux/config.h>            /* 配置头文件   */

#include <linux/module.h>           /* 驱动模型头文件 */

#include <linux/devfs_fs_kernel.h>   /* 设备文件系统头文件 */

#include <linux/init.h>         /* 初始化相关头文件 */

#include <linux/kernel.h>   /* printk() 等函数有关的头文件 */

#include <linux/slab.h>     /* kmalloc() 等函数有关的头文件 */

#include <linux/fs.h>       /* 与文件系统有关的头文件 everything... */

#include <linux/errno.h>    /* 错误代码处理头文件 error codes */

#include <linux/types.h>    /* 数据类型头文件 size_t */

#include <linux/proc_fs.h>    /* 与进程调度相关的头文件 */

#include <linux/fcntl.h>     /* O_ACCMODE */

#include <linux/poll.h>     /* COPY_TO_USER */

#include <asm/system.h>     /* cli(), *_flags */

#define DEVICE_NAME             " demo"  /* 该驱动的设备名 */

#define demo_MAJOR 254               /* 驱动主设备号 */

#define demo_MINOR 0                  /* 驱动次设备号 */

static int MAX_BUF_LEN=1024;           /* 定义一缓冲区最大长度 */

static char drv_buf[1024];                  /* 定义一缓冲区 */

static int WRI_LENGTH=0;

/***********************************************************************

* 名称: demo_write ()

* 功能:对应用户空间的 write 系统调用,从用户空间拷贝给定长度缓冲区数据到内核空间

* 入口参数: *filp 操作设备文件的 ID *buffer 对应用户空间的缓冲区的起始地址, count 用户空间数据缓冲区长度

* 出口参数:返回用户空间数据缓冲区长度

**********************************************************************/

static ssize_t  demo_write(struct file *filp,const char *buffer, size_t count)

{

       if(count > MAX_BUF_LEN)count = MAX_BUF_LEN;

       copy_from_user(drv_buf , buffer, count);   /* 从用户空间拷贝缓冲区数据到内核空间的关键函数,把用户空间的 buffer 数据传递给 drv_buf 数组 */

       WRI_LENGTH = count;

       printk("user write data to driver/n");

       do_write();    

       return count;

}

/***********************************************************************

* 名称: static void do_write()

* 功能:

* 入口参数:无

* 出口参数:无

**********************************************************************/

static void do_write()

{

       int i;

       int len = WRI_LENGTH;

       char tmp;

       for(i = 0; i < (len>>1); i++,len--){

              tmp = drv_buf[len-1];

              drv_buf[len-1] = drv_buf[i];

              drv_buf[i] = tmp;

       }

}

/*************************************************************

* 名称: demo_ read ()

* 功能:对应用户空间的 read 系统调用,从内核空间拷贝给定长度缓冲区数据到用户空间

* 入口参数: *filp 操作设备文件的 ID *buffer 对应用户空间的缓冲区的起始地址, count 用户空间数据缓冲区长度, *ppos 用户在文件中进行存储操作的位置

* 出口参数:返回用户空间数据缓冲区长度

**********************************************************************/

 

static ssize_t  demo_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)

{

       if(count > MAX_BUF_LEN)

              count=MAX_BUF_LEN;

         copy_to_user(buffer, drv_buf,count);        /* 从内核空间拷贝缓冲区数据到用户空间的关键函数,把内核空间经过逆序排列后的 drv_buf 数组传递给用户空间的 buffer 数组 */

       printk("user read data from driver/n");

       return count;

}

/***********************************************************************

* 名称: demo_ioctl ()

* 功能:对应用户空间的 ioctl 系统调用,对用户空间传递过来的命令进行 swith 判断,并进行相应处理,本函数只对用户空间传递过来的 1 2 做简单的处理,打印一条信息

* 入口参数: *filp 操作设备文件的 ID cmd 对应用户空间的 cmd arg 对应用户空间传递过来的参数

列表

* 出口参数:正确返回 0 ,错误命令返回 default 的提示内容

**********************************************************************/

static int demo_ioctl(struct inode *inode, struct file *file,

                 unsigned int cmd, unsigned long arg)

{

       switch(cmd){

              case 1:printk("runing command 1 /n");break;

              case 2:printk("runing command 2 /n");break;

              default:

                     printk("error cmd number/n");break;

       }

       return 0;

}

/***********************************************************************

* 名称: static void demo_open ()

* 功能:设备文件打开函数,对应用户空间 open 系统调用,

* 入口参数:设备文件节点

* 出口参数:无

**********************************************************************/

static int demo_open(struct inode *inode, struct file *file)

{

MOD_INC_USE_COUNT;   

       printk("device open sucess!/n");

       return 0;

}

* 名称: static int  demo_release

* 功能:设备文件释放函数,对应用户空间 close 系统调用,

* 入口参数:设备文件节点

* 出口参数:无

**********************************************************************/

static int  demo_release(struct inode *inode, struct file *filp)

{

       MOD_DEC_USE_COUNT;

       printk("device release/n");

       return 0;

}

posted @ 2011-06-05 15:11  Podevor  阅读(159)  评论(0编辑  收藏  举报