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");

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

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

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

时间: 2024-12-20 01:18:44

20150216简单的Linux字符设备驱动程序的相关文章

linux字符设备驱动程序框架

#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/a

linux字符设备驱动程序框架(老方法)

#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/a

linux字符设备驱动程序源码分析

本文主要分析linux-2.6.28内核版本的字符设备抽象层源码文件char_dev.c.该文件代码量不大,但其为linux应用程序访问实际字符型硬件设备搭建了桥梁,进一步限定了linux字符设备驱动的设计框架. void __init chrdev_init(void) {  cdev_map = kobj_map_init(base_probe, &chrdevs_lock);  bdi_init(&directly_mappable_cdev_bdi); } 该函数在系统初始化启动时

浅析Linux字符设备驱动程序内核机制

前段时间在学习linux设备驱动的时候,看了陈学松著的<深入Linux设备驱动程序内核机制>一书. 说实话.这是一本非常好的书,作者不但给出了在设备驱动程序开发过程中的所须要的知识点(如对应的函数和数据结构),还深入到linux内核里去分析了这些函数或数据结构的原理.对设备驱动开发的整个过程和原理都分析的非常到位.但可能是因为知识点太多.原理也比較深的原因,这本书在知识点的排版上跨度有些大.所以读起来显得有点吃力,可是假设第一遍看的比較认真的话,再回头看第二次就真的可以非常好地理解作者的写作思

Linux 简单字符设备驱动程序 (自顶向下)

第零章:扯扯淡 特此总结一下写的一个简单字符设备驱动程序的过程,我要强调一下“自顶向下”这个介绍方法,因为我觉得这样更容易让没有接触过设备驱动程序的童鞋更容易理解,“自顶向下”最初从<计算机网络 自顶向下方法>这本书学到的,我觉得有时候这是一种很好的方式. 第一章:测试程序 咦?你怎么跟别人的思路不一样???自顶向下嘛,我就直接从测试程序来说啦,这样那个不是更熟悉吗?看看下面的测试程序的代码,是不是很熟悉? 1 #include <stdio.h> 2 #include <u

linux 驱动学习(一)简单的字符设备驱动程序

linux 系统将设备分为三种类型:字符设备.块设备和网络接口设备. 文章将先给出字符设备驱动程序,参照程序记录知识点,可能会不全,以后会慢慢加 .知识点记录完成后,会贴出字符设备驱动程序的测试程序并记录测试过程. 注释版 1 #include "linux/kernel.h" //内核头文件,含有一些内核常用函数的原形定义 2 #include "linux/module.h" //包含大量加载模块需要的函数和符号的定义 3 #include "linu

0915-----Linux设备驱动 学习笔记----------一个简单的字符设备驱动程序

0.前言 研究生生活一切都在步入正轨,我也开始了新的学习,因为实在不想搞存储,所以就决定跟师兄学习设备驱动,看了两星期书,终于有点头绪了,开始记录吧! 1.准备工作 a)查看内核版本 uname -r b)安装内核源码树(http://www.cnblogs.com/Jezze/archive/2011/12/23/2299871.html) 在www.linux.org上下载源码编译,这里是.xz格式,需要安装解压工具,xz-utils: 解压方法示例:xz -d linux-3.1-rc4.

LINUX设备驱动程序笔记(三)字符设备驱动程序

      <一>.主设备号和次设备号        对字符设备的访问时通过文件系统内的设备名称进行的.那些设备名称简单称之为文件系统树的节点,它们通常位于/dev目录.字符设备驱动程序的设备文件可通过ls -l命令输出的第一列中的'c'来识别.块设备同样位于/dev下,由字符'b'标识 crw-rw----  1 root root    253,   0 2013-09-11 20:33 usbmon0 crw-rw----  1 root root    253,   1 2013-09

简单字符设备驱动程序

linux驱动程序开发步骤 1)查看原理图.数据手册,了解设备的操作方法. 2)在内核中找到相近的驱动程序,以它为模板进行开发,有时候需要从零开始. 3)实现驱动程序的初始化:比如向内核注册驱动程序,这样应用程序传入文件名时,内核才能找到相应的驱动程序. 4)设计所要实现的操作,比如open,read,write,close. 5)实现中断服务(不是必需的) 6)编译该驱动程序到中,或者用insmod命令加载. 7)测试驱动程序.  驱动框架 应用程序API接口read,open,write 是