第一个Linux驱动


Linux系统将驱动映射成文件,这些文件称为设备文件或驱动文件,
都保存在/dev/目录中。这种设计理念使得与Linux驱动进行交互就像
与普通文件进行交互一样容易。

---编写Linux驱动程序的步骤

Linux驱动程序和其他类型的Linux程序一样,也有自己的规则。以
下是编写一个基本Linux驱动的一般步骤。

1)建立Linux驱动骨架(装载和卸载Linux驱动)
任何类型的程序都有个基本的结构,就像C程序都需要一个main函
数一样。内核在使用驱动的时候也会需要先进行初始化、建立设备
文件、分配内存等工作,当驱动卸载时需要释放资源、删除文件等
工作。在Linux驱动中需要提供两个函数来进行初始化和退出操作。
分别是module_init和module_exit,驱动程序都会具备这两个函数

2)注册和销毁设备文件
Linux下所有的设备都是文件,所以每个驱动都需要一个设备文件,
建立设备文件的工作通常放在初始化的位置。删除设备文件通常放
在驱动退出的位置。

3)指定驱动相关信息
驱动程序是自描述的。可以使用modinfo来获取驱动程序的作者、遵
循协议、驱动描述等信息。这些信息都需要在驱动代码中指定。
MODULE_AUTHOR MODULE_LICENSE MODULE_ALIAS等

4)指定回调函数
Linux指定了多种动作,也可以成为事件。例如,向设备文件写入数据
时会触发"写"事件,Linux体统会调用对应驱动程序的write回调函数,从
设备文件读数据时,会触发读事件,系统会调用驱动程序的read回调函数
一个驱动程序并不需要指定所有的毁掉函数。回调函数会通过相关机制
进行注册。

5)编写业务逻辑
这是驱动程序的核心部分,以上4部分只有骨架是没有意义的。任何一个
完整的Linux驱动都会做一些与其功能相关的工作。
6)编写Makefile文件
7)编译驱动程序
8)安装和卸载Linux驱动

---驱动分析
设备文件和普通文件不同,不能使用IO函数建立,而是需要进行专门的设备注册
函数和设备销毁函数。在初始化驱动时执行建立设备文件,在卸载驱动时删除
设备文件。并且设备还需要一个结构体来描述与其相关的信息。设备结构体中有
个重要的成员变量fops,用于描述设备文件在各种可触发时间的函数指针。这样如
何实现对设备文件进行操作的过程就清楚了。
---代码实现
下面的代码参考和整理了《Android深度探索HAL与驱动开发》(-李宁)的书籍。

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>

#define DEVICE_NAME "worldcount"

static unsigned char mem[1000];
static char read_flag = 'y';
static int written_count = 0;


static ssize_t wordCountRead( struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
if( read_flag == 'n')
{
copy_to_user( buf, (void *)mem, written_count);
printk ("read count:%d", written_count);
read_flag = 'y';
return written_count;
}
else
return 0;
}

static ssize_t wordCountWrite (struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
copy_from_user (mem, buf, count);
read_flag = 'n';
written_count = count;
printk ("written count :%d", count);
return count;
}

static struct file_operations dev_fops =
{.owner = THIS_MODULE, .read = wordCountRead, .write = wordCountWrite};

static struct miscdevice misc =
{.minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &dev_fops};


static int wordCount_init( void)
{
int ret;
ret = misc_register (&misc);
printk ("word count init success \n");
return ret;
}

static void wordCount_exit (void)
{
misc_deregister (&misc);
printk (" word count exit success \n");
}

module_init (wordCount_init);
module_exit (wordCount_exit);


MODULE_LICENSE ("GPL");
MODULE_ALIAS ("word count module");
MODULE_AUTHOR ("lining");

---代码分析
1)本驱动是一个简单字符设备驱动MISC设备,该种设备便于注册而且操作也不复杂
2)除了引入必要的头文件和定义相关的变量,代码中也专门实现了设备文件的读写操作
3)所有的函数和变量都定义为static,这样便于调高驱动效率。
4)code中制定了读写回调函数并且指定了文件操作结构体,在模块加载卸载函数中进行
了设备的安装和移除工作,这样我们就会在/dev/下发现wordcount设备文件。
5)最后指定了设备文件的相关信息
---错误总结
总结编写该驱动过程中遇到的错误。
1)由于忘记指定设备安装和卸载,并且错误的写成MODULE_INIT(wordCount_init);
MODULE_EXIT(wordCount_exit).这会导致在/dev/下无法生成设备文件,但是使用命令对
驱动进行安装的时候仍然能够成功,也能modinfo查看信息。
2)使用sudo echo 'hello world' > /dev/wordCount能够操作成功,而且使用cat查看能够显示字符
但是之后对模块进行卸载的时候会提示资源繁忙,无法卸载。
PS-->以上测试错误是我在Ubuntu系统中测试的,当我切换到win7下使用虚拟机测试的时候

则不会出现这种问题,上述问题可能是一个意外。

---驱动测试代码

#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

void main()
{
int fd;
char *buf;
buf = (char *)malloc(10);
puts("hello world\n");
fd = open("/dev/worldcount", O_RDWR);
if (fd > 0)
printf(" open success %d\n", fd);
else
printf("open failed \n");

write (fd,"test", sizeof("test") );
read (fd, buf,1);
buf[1] = '\0';
puts(buf);
}

---代码效果
便已执行代码显示结果如下
hello world

open success 3
t

证明驱动是工作的,但是我们去cat /dev/worldcout则不会看到其中的内容
(这个我也很好奇为什么不会看到字符串test。)
基本上这个简单的MISC设备驱动就完成了。
O(∩_∩)O谢谢,记录自己学习的点点滴滴。

相关内容