因为在Ubuntu环境下写的文章和做的实验,没有安装linux下比较好用的截图工具,所以没有附带太多截屏,还望海涵,不过该描述的都到位了。
曾经还一直处于应用程序开发的我,以为驱动开发者是那么的厉害,以为只有牛人才能走到这一步,随着知识的积累,发现并非如此,驱动开发并不像想象中那么特别,俗话说术业有专攻,开发者只是使用的工具不同,且从事的领域不同,产品不同罢了。只要能作出好的产品,你就是一个”牛人”。
从这里开始进行系统化的驱动学习,主线是《Linux设备驱动开发详解》,之前大致看过这本书,起初感觉有些晦涩,但看了两本内核的书籍以后,重新回来读起来就比较顺流了,一口气读了好几章(主要是前几章是知识介绍性文章)。所以这里顺便推荐两本内核的书籍:
《Linux内核设计与实现》,我看的是第二版,写的非常棒,简单易懂,要在介绍内核原理与实现机制,广度到了,深度不够,所以最好还得配合下边这本书一块儿看。
《UnderstandingLinux kernel》——深入理解linux内核,这本书的第三版是基于2.6内核的,06年出版。我看的是英文原版的,所以看得速度比较满,不过正好和《Linux设备驱动开发详解》对接上了,刚看过《Understanding Linux kernel》中的同步异步,应该是第五章那里,然后《Linux设备驱动开发详解》就在第七章也讲到了,这样,可能那么多机制:锁,读写锁,顺序锁,信号量,读写信号量……一下出来这么多东西的话有些接受不了,但如果你之前看了《Linux内核设计与实现》后,最起码不会感觉恐慌,其实学习新的知识就是这样,一回生两回熟,再难理解的东西,功夫到了,也就理解了。
好的,开始第一个驱动程序的学习,实例来自 Linux设备驱动开发详解,这里是创建了一个虚拟的字符设备globalmem,也就是一片内核空间的内存区域,来实现内核空间和用户空间的信息传递。(确实,我举不出来比这更好的例子来作第一个例子了,不过请相信,哪怕就是这个例子也是我一个字母一个字母敲出来的,并未直接取材自隋书的源码,主要是看我的注释,和我遇到的问题以及解决它的全过程,成功者找方法,失败者找借口!呵呵)
不废话,上代码,别心急,看注释。。。
下边是驱动的源码,附上了详尽的注释:
globalmem.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#define GLOBALMEM_SIZE 0x1000 /*4k的空间*/
#define MEM_CLEAR 0x1 /*清空全局内存*/
#define GLOBALMEM_MAJOR 250 /*预设的主设备号*/
static int globalmem_major = GLOBALMEM_MAJOR;
/*用面向对象思想对cdev重新封装,以便于方便我们的操作*/
struct globalmem_dev{
struct cdev cdev;
unsigned char mem[GLOBALMEM_SIZE];
};
struct globalmem_dev *globalmem_devp; /*声明一个全局的设备结构体*/
/*用来注册到file_operations结构中open */
int globalmem_open(struct inode *inode,struct file *filp)
{
filp->private_data = globalmem_devp; /*当有多个同类设备时,用私有变量访问很有必要也很方便*/
return 0;
}
/*用来注册到file_operations结构中release */
int globalmem_release(struct inode *inode,struct file *filp)
{
return 0;
}
/*用来注册到file_operations结构中ioctl */
static int globalmem_ioctl(struct inode *inodep,struct file *filp,unsigned int cmd,unsigned long arg)
{
struct globalmem_dev *dev = filp->private_data; /*从私有数据获取设备结构体指针*/
switch (cmd){
case MEM_CLEAR:
memset(dev->mem,0,GLOBALMEM_SIZE);
printk(KERN_INFO"globalmem is set to zero\n");
break;
default:
return -EINVAL;
}
return 0;
}
/*用来注册到file_operations结构中read */
static ssize_t globalmem_read(str