Linux字符设备驱动入门

Linux 应用程序对驱动程序的调用如图所示

open函数调用过程:

驱动的加载与卸载

    Linux 驱动有两种运行方式,第一种就是将驱动编译进 Linux 内核中,这样当 Linux 内核启
动的时候就会自动运行驱动程序。第二种就是将驱动编译成模块 (Linux 下模块扩展名为 .ko) ,在
Linux 内核启动以后使用“ insmod ”命令加载驱动模块。在调试驱动的时候一般都选择将其编译
为模块,这样我们修改驱动以后只需要编译一下驱动代码即可,不需要编译整个 Linux 代码。
而且在调试的时候只需要加载或者卸载驱动模块即可,不需要重启整个系统。
    模块的加载与卸载函数:
module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数
    module_init 函数用来向 Linux 内核注册一个模块加载函数,参数 xxx_init 就是需要注册的
具体函数,当使用“ insmod ”命令加载驱动的时候, xxx_init 这个函数就会被调用。 module_exit()
函数用来向 Linux 内核注册一个模块卸载函数,参数 xxx_exit 就是需要注册的具体函数,当使
用“ rmmod ”命令卸载具体驱动的时候 xxx_exit 函数就会被调用。字符设备驱动模块加载和卸
载模板如下所示:
    
    驱动编译完成以后扩展名为 .ko ,有两种命令可以加载驱动模块: insmod modprobe。
insmod 命令不能解决模块的依赖关系,使用insmod加载多个ko要注意依赖关系先后顺序,modprobe 会分析模块的依赖关系,然后会将所有的依赖模块都加载到内核中。
    驱动模块的卸载使用命令“ rmmod ”即可,比如要卸载 drv.ko ,使用如下命令即可:
rmmod drv.ko 也可以使用“modprobe -r ”命令卸载驱动,比如要卸载 drv.ko ,命令如下:
modprobe -r drv.ko。但是modprobe -r会解除多个ko之间的依赖关系,所以推荐使用rmmod。

字符设备注册与注销

字符设备的注册和注销函数原型如下所示 :
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
static inline void unregister_chrdev(unsigned int major, const char *name)

函数样板

1 /* 打开设备 */
2 static int chrtest_open ( struct inode * inode , struct file * filp )
3 {
4     /* 用户实现具体功能 */
5     return 0 ;
6 }
7
8 /* 从设备读取 */
9 static ssize_t chrtest_read ( struct file * filp , char __user * buf size_t cnt , loff_t * offt )
10 {
11     /* 用户实现具体功能 */
12     return 0 ;
13 }
14
15 /* 向设备写数据 */
16 static ssize_t chrtest_write ( struct file * filp ,   const char __user * buf ,   size_t cnt , loff_t * offt )
17 {
18     /* 用户实现具体功能 */
19     return 0 ;
20 }
22 /* 关闭 / 释放设备 */
23 static int chrtest_release ( struct inode * inode , struct file * filp )
24 {
25     /* 用户实现具体功能 */
26     return 0 ;
27 }
28
29 static struct file_operations test_fops = {
30         . owner = THIS_MODULE ,
31         . open = chrtest_open ,
32         . read = chrtest_read ,
33         . write = chrtest_write ,
34         . release = chrtest_release ,
35 };
36
37 /* 驱动入口函数 */
38 static int __init xxx_init ( void )
39 {
40     /* 入口函数具体内容 */
41     int retvalue = 0 ;
42
43     /* 注册字符设备驱动 */
44     retvalue = register_chrdev ( 200 , "chrtest" , & test_fops );
45     if ( retvalue < 0 ){
46     /* 字符设备注册失败 , 自行处理 */
47     }
48     return 0 ;
49 }
50
51 /* 驱动出口函数 */
52 static void __exit xxx_exit ( void )
53 {
54     /* 注销字符设备驱动 */
55     unregister_chrdev ( 200 , "chrtest" );
56 }
57
58 /* 将上面两个函数指定为驱动的入口和出口函数 */
59 module_init ( xxx_init );//注册模块加载函数
60 module_exit ( xxx_exit );//注册模块卸载函数
62 MODULE_LICENSE ( "GPL" ); //添加证书
63 MODULE_AUTHOR ( "zipeng" ); //添加作者

编译设备驱动模块

使用如下Makefile文件

1 KERNELDIR := /home/zipeng/linux/myKernel/linux-imx_rel_imx_4.1.15_2.1.0_ga
2 CURRENT_PATH := $(shell pwd)
3 obj-m := chrdevbase.o
4
5 build : kernel_modules
6
7 kernel_modules :
8 $(MAKE) -C $(KERNELDIR) M = $(CURRENT_PATH) modules
9 clean :
10 $(MAKE) -C $(KERNELDIR) M = $(CURRENT_PATH) clean
命令:make

编写编译测试App

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

static char usrdata[] = {"usr data!"};

/*
* @description : main 主程序
* @param - argc : argv 数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
     int fd, retvalue;
     char *filename;
     char readbuf[100], writebuf[100];

     if(argc != 3){
         printf("Error Usage!\r\n");
         return -1;
     }

     filename = argv[1];

     /* 打开驱动文件 */
     fd = open(filename, O_RDWR);
     if(fd < 0){
         printf("Can't open file %s\r\n", filename);
         return -1;
     }

     if(atoi(argv[2]) == 1){ /* 从驱动文件读取数据 */
         retvalue = read(fd, readbuf, 50);
         if(retvalue < 0){
             printf("read file %s failed!\r\n", filename);
         }else{
         /* 读取成功,打印出读取成功的数据 */
             printf("read data:%s\r\n",readbuf);
        }
      }
       if(atoi(argv[2]) == 2){
         /* 向设备驱动写数据 */
             memcpy(writebuf, usrdata, sizeof(usrdata));
             retvalue = write(fd, writebuf, 50);
             if(retvalue < 0){
                 printf("write file %s failed!\r\n", filename);
             }
         }

     /* 关闭设备 */
     retvalue = close(fd);
     if(retvalue < 0){
         printf("Can't close file %s\r\n", filename);
         return -1;
     }

     return 0;
}

编译:
arm-linux-gnueabihf-gcc chrdevbaseApp.c -o chrdevbaseApp
生成在ARM运行的程序chrdevbaseApp
将编译生成的.ko文件和APP可执行文件拷贝到nfs/rootfs指定目录下
sudo cp chrdevbase.ko chrdevbaseApp /home/zuozhongkai/linux/nfs/rootfs/lib/modules/4.1.15/ -f

挂载根文件系统测试

拷贝完成之后重新启动ARM开发板,可以在对应目录下看到新添加的ko模块文件和APP可执行文件。

加载驱动模块

输入如下命令加 chrdevbase.ko:
insmod chrdevbase.ko 或 modprobe chrdevbase.ko
输入lsmod查看当前系统存在的模块
输入cat /proc/devices查看系统所有设备

创建设备节点文件

mknod /dev/chrdevbase c 200 0

chrdevbase设备操作测试

./chrdevbaseApp /dev/chrdevbase 1 //通过执行chrdevbaseApp对/dev/chrdevbase设备写1
./chrdevbaseApp /dev/chrdevbase 2

卸载驱动模块

rmmod chrdevbase.ko
lsmod命令查看是否还有该驱动
  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值