您的位置:首页 > 运维架构 > Linux

第一个Linux驱动

2014-04-15 06:31 183 查看
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谢谢,记录自己学习的点点滴滴。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux驱动 linux