20150216简单的Linux字符设备驱动程序

20150216简单的Linux字符设备驱动程序

2015-02-16 李海沿

关于字符设备驱动程序详细的知识点,本文就不再介绍了,很多同志,看了知识点,还是一头雾水,写不出来,所以,本文从实战出发,带领各位同胞们来实现一个字符设备驱动程序,改程序可作为字符设备的通用模板。

好了废话不多说,先上驱动程序,在驱动程序中加入详细注释:

 

 

技术分享
  1 /******************************
  2     linux 字符设备驱动程序
  3  *****************************/
  4 #include <linux/module.h>
  5 #include <linux/init.h>
  6 #include <linux/kernel.h>
  7 #include <linux/delay.h>
  8 #include <linux/types.h>
  9 #include <linux/ioctl.h>    
 10 #include <linux/gpio.h>
 11 #include <linux/fs.h>
 12 #include <linux/device.h>    //包含了用于自动创建设备节点的函数device_create
 13 #include <linux/uaccess.h>  //包含了copy_to_user 函数等
 14 
 15 #define Driver_NAME "key_query"
 16 #define DEVICE_NAME "key_query"
 17 
 18 //command in ioctl        
 19 #define version        0
 20 
 21 //用于保存主设备号
 22 static int major=0;
 23 
 24 //用于自动创建设备节点 代替了手动敲mknod命令
 25 static struct class *drv_class = NULL;
 26 static struct class_device *drv_class_dev = NULL;
 27 
 28 
 29 /* 应用程序对设备文件/dev/key_query执行open(...)时,
 30  * 就会调用key_open函数*/
 31 static int key_open(struct inode *inode, struct file *file)
 32 {
 33     printk("<0>function open!\n\n");
 34     
 35     return 0;
 36 }
 37 
 38 /*当应用程序中read(fd,buff,sizeof(buff))时调用此key_read函数*/
 39 static int key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
 40 {
 41     printk("<0>function read!\n\n");
 42     return 0;
 43 }
 44 
 45 /* 当应用程序中使用write函数时,调用此函数**/
 46 static ssize_t key_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
 47 {
 48     printk("<0>function write!\n\n");
 49     
 50     return 1;
 51 }
 52 
 53 static int  key_release(struct inode *inode, struct file *filp)
 54 {
 55     printk("<0>function release!\n\n");
 56     return 0;
 57 }
 58 /* 当用户调用ioctl(fd,version,NULL);时,会进入此函数,
 59  * 在SWITCH中配对command,然后执行相应的语句 
 60  * 注意command 一定为整数,需在前面定义*/
 61 static int key_ioctl(struct inode *inode,struct file *flip,unsigned int command,unsigned long arg)
 62 {
 63     printk("<0>function ioctl!\n\n");
 64     switch (command) {
 65         case version:
 66             printk("<0>hello,the driver version is 0.1.0\n\n");
 67             break;
 68         default:
 69               printk("<0>command error \n");
 70             printk("<0>ioctl(fd, (unsigned int)command, (unsigned long) arg;\n");
 71             printk("<0>command: <version>\n\n");
 72             return -1;
 73     }
 74     return 0;    
 75 }
 76 
 77 /* 这个结构是字符设备驱动程序的核心
 78  * 当应用程序操作设备文件时所调用的open、read、write等函数,
 79  * 最终会调用这个结构中指定的对应函数
 80  */
 81 static struct file_operations key_fops = {
 82     .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
 83     .open   =   key_open,     
 84     .read    =    key_read,       
 85     .write    =    key_write,       
 86     .release=   key_release,
 87     .ioctl  =   key_ioctl,    
 88 };
 89     
 90 /*
 91  * 执行insmod命令时就会调用这个函数 
 92  */
 93 static int __init key_init(void)
 94 {
 95     printk("<0>\nHello,this is %s module!\n\n",Driver_NAME);
 96     //register and mknod
 97     //注册字符设备,系统会自动分配一个主设备号,保存在major中
 98     major = register_chrdev(0,Driver_NAME,&key_fops);
 99     //自动在/dev/目录下创建设备节点
100     drv_class = class_create(THIS_MODULE,Driver_NAME);
101     drv_class_dev = device_create(drv_class,NULL,MKDEV(major,0),NULL,DEVICE_NAME);    /*/dev/key_query*/
102 
103     return 0;
104 }
105 
106 /*
107  * 执行rmmod命令时就会调用这个函数 
108  */
109 static void __exit key_exit(void)
110 {
111     printk("<0>\nGoodbye,%s!\n\n",Driver_NAME);
112 
113     //卸载字符设备,释放主设备号
114     unregister_chrdev(major,Driver_NAME);
115     //卸载字符设备的设备节点
116     device_unregister(drv_class_dev);
117     class_destroy(drv_class);
118 
119 }
120 
121 /* 这两行指定驱动程序的初始化函数和卸载函数 */
122 module_init(key_init);
123 module_exit(key_exit);
124 
125 /* 描述驱动程序的一些信息,不是必须的 */
126 MODULE_AUTHOR("Lover雪儿");
127 MODULE_VERSION("0.1.0");
128 MODULE_DESCRIPTION("IMX257 key Driver");
129 MODULE_LICENSE("GPL");
View Code

 

以下是个人理解,仅代表个人思想:

 

static int __init key_init(void)

如果单片机程序的角度来理解字符设备,那么上面程序的其实就是相当于我们的main函数。

在linux系统中,我们使用insmod 加载驱动时,

最先调用的,最先进入的就是__init key_init函数,所以我们所有初始化的代码都可以放在这个函数里,

比如注册设备,创建设备驱动,申请内存,如果说是功能有关GPIO的话,那么GPIO的引脚的初始化也可以放在此函数中,所以总结的一句话,

__init key_init函数就是负责初始化的函数。

 

static void __exit key_exit(void)

相应的,与__init key_init函数的功能相反,当我们不要用这个驱动的时候,系统就会调用这个函数。

前面我们的__init key_init函数实现了初始化,注册设备,申请的内存等功能,为了资源的合理利用,所有的系统资源,当我们不要用的时候,我们就应该释放。好比说,借了别人的东西暂时使用,不用的时候,就应该归还别人。

所有我们要释放的代码,那就是放在这个函数中执行。

比较简单理解。

 

module_init(key_init);

module_exit(key_exit);

当我们写两个函数key_init key_exit 函数时,操作系统没有那么智能的就知道这两个就是初始化和退出函数

这两行代码就是指定我们的初始化和退出的函数,

也就是把我们前面写的两个函数告诉linux操作系统,我们初始化和退出的函数

 

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

但我们在应用程序中使用代码

open("dev/xxx",O_RDWR);

打开设备时,就会调用这个函数,这个函数中可以实现我们设备被打开时要执行的一些操作。如果没什么操作,可以不写。

 

static int key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)

当应用程序中read(fd,buff,sizeof(buff))时调用此key_read函数

所以,在此函数中,我们可以使用copy_to_user函数,来把内核空间中的数据传递到我们应用程序中。

 

static ssize_t key_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)

当我们应用程序中执行写入操作时,这个函数就发挥作用了,当然

在此函数中还需要使用Copy_from_user来配合,将应用程序中的数据传递到内核空间,

因为应用程序空间和内核空间是相互独立的,不能互相访问,必须借助于copy_from_use 和 copy_to_user 这两个函数。

 

static int key_ioctl(struct inode *inode,struct file *flip,unsigned int command,unsigned long arg)

当我们的驱动程序可以实现多中会互相冲突的功能时,可以在此函数中使用command来区分不同的功能,

然后再switch函数中实现不同功能的代码。

应用程序中使用ioctl(fd,version,NULL); 会进入此函数。

比如说,当我们的驱动程序可以实现流水灯,花样灯等不同的功能时,我们就可以定义两个整形COMMAND,在此函数实现该功能。

 

static struct file_operations key_fops = {

    .owner = THIS_MODULE,

    .open = key_open,

    .read    =    key_read,    

    .write    =    key_write,    

.release= key_release,

.ioctl = key_ioctl,    

};

这个结构是字符设备驱动程序的核心

当应用程序操作设备文件时所调用的open、read、write等函数,

最终会调用这个结构中指定的对应函数

通俗来讲,这个结构体就是告诉linux操作系统,我们哪些已经实现的函数分别是驱动程序的 open,write,read,ioctl。

 

当我们实现了上面的几个函数时,也就是实现了一个简单的驱动程序了。

 

 

下面我们附上 Makefile的代码

 

技术分享
 1 ifeq ($(KERNELRELEASE),)
 2     KERNELDIR ?= /home/study/system/linux-2.6.31
 3     PWD := $(shell pwd)
 4 modules:
 5     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
 6 modules_install:
 7     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
 8 clean:
 9     rm -rf *.o *~ core .depend  *cmd *.ko *.mod.c .tmp_versions *.markers *.order *.symvers
10 
11 else
12     obj-m := key.o
13 endif
View Code

 

 

 接下来,我们就在此基础上实现一个gpio的按键输入程序。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。