最最基础的linux驱动程序

来源:互联网 发布:海康门禁软件 编辑:程序博客网 时间:2024/05/16 12:56

使用最原始最简单的代码说明linux驱动的格式.

基本概念:

Q: 什么是 主 / 次 设备号

A: 主设备号对应驱动程序, 次设备号对应具体的设备. 例如: 有四个按键, 主设备号就对应按键的驱动程序, 通过主设备号可以找到按键的驱动程序, 而次设备号就对应使用按键驱动程序的各个设备


Q: 应用层的open如何找到驱动层的open函数

A: open等系统调用执行的时候会产生软中断异常. 执行 swi val来进入内核态, 通过预设不同的val 就可以区分各个系统调用


Q: register_chrdev(unsigned int major, const char *name, struct file_operations *fops)

A: 参数含义: major 主设备号(重要, 0表示由系统自动创建)   name 设备名称(不重要)  fops驱动里实例化的file_operations变量(重要) 如果注册成功则返回0


Q: linux系统怎么知道去调用哪个file_operations 结构体里的 open/close/read/write函数呢

A: 设备的 主/次设备号, 在驱动程序的初始化函数中有个 注册函数  register_chrdev 他将当前驱动程序 实例化的file_operations结构体 和 主设备号告诉给linux内核. 当应用程序在open/close等的时候就会先根据设备类型(字符设备/块设备)以及主设备号来找到file_operations结构体


Q: 创建设备节点

A: 手工:  mknod /dev/xxx c 252 0


Q:  创建文件节点时候的 xxx 跟 register_chrdev(unsigned int major, const char *name, struct file_operations *fops); 里的 name 是什么关系

A:  mknod 时候创建设备节点文件, 文件的名称为xxx, 同时指定他为字符设备还是块设备 主设备号 次设备号,  这个设备节点是可以在应用层用 open 打开的   ls -l /dev/xxx 可以查看

在register_chrdev的时候name是驱动程序的名称, 比如字符设备可以有n个驱动程序, 每个驱动程序都有个名称, cat proc/devices 可以查看

因为应用层的open去寻找file_operations中的open是根据主设备号来寻找的, 所以这里名字可以不一样,名字无关紧要


字符设备驱动编写步骤:

1. 查看板子的原理图, 芯片手册, 熟悉设备的操作方法

2. 在内核中查找相似的代码, 以它为模板来编写驱动, 如有需要则从0编写

3. 包含必要的头文件 GPL协议

4. 编写入口与出口函数, 在入口函数中注册驱动

5. 实例化 file_operations 结构体,并实现其成员. 如 open , close, read, write等. 需要注意的是 引脚的初始化等要在open里完成, 而不能在初始化函数中完成. 因为这个模块虽然被加载但是却不一定会立刻使用, 如果初始化之后别的模块就无法使用, 所以必须在打开的时候再初始化

6. 如果需要的话实现中断服务函数

7. insmod / 编译模块 / 测试


驱动代码:

#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <asm/uaccess.h>#include <asm/irq.h>#include <asm/io.h>#include <linux/module.h>int major;static int first_drv_open(struct inode * inode, struct file * filp){printk("first_drv_open\n");return 0;}static int first_drv_write(struct file * file, const char __user * buffer, size_t count, loff_t * ppos){printk("first_drv_write\n");return 0;}/* File operations struct for character device */static const struct file_operations first_drv_fops = {.owner= THIS_MODULE,.open= first_drv_open,.write      = first_drv_write,};/* 驱动入口函数 */static int first_drv_init(void){/* 主设备号设置为0表示由系统自动分配主设备号 */major = register_chrdev(0, "first_drv", &first_drv_fops);return 0;}/* 驱动出口函数 */static void first_drv_exit(void){unregister_chrdev(major, "first_drv");}module_init(first_drv_init);  //用于修饰入口函数module_exit(first_drv_exit);  //用于修饰出口函数MODULE_AUTHOR("LWJ");MODULE_DESCRIPTION("Just for Demon");MODULE_LICENSE("GPL");  //遵循GPL协议

makefile:

下边的linux目录必须是编译好的linux内核目录, 而且pwd左右的不是 ' 而是 1左边的 ` ,一定要注意这个区别.

obj-m := first_drv.oKDIR = /home/cxh/work/kernel/linux-2.6.22.6/all:make -C  $(KDIR)  M=`pwd` modules # ARCH=arm CROSS_COMPILE=arm-linux-  #这里arch和cross_compile如果在kernel的Makefile中指定过的话就不需要写出clean:                                                                                                                                      make -C $(KDIR) M=`pwd` modules clean                                                                                               rm -rf modules.order


测试代码:

这里的这个 /dev/xxx 用来说明驱动和名字没有什么关系, 这里这个xxx就是用来手动创建节点的名字. mkmod /dev/xxx c 252 0

ssize_t write(file fd, const void *buf,  size_t count); 将buf指向的count字节内容写入fd中, 文件指针也跟着后移

#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>int main(void){int fd;int val = 1;fd = open("/dev/xxx",O_RDWR);if(fd < 0){printf("open error\n");}write(fd,&val,4);return 0;}

测试方法:

cat /proc/devices   查看设备

ls -l /dev/xxx  查看手动创建的设备

这里是先insmod之后 通过查看生成的设备文件主设备号 然后再mknod 该主设备号的节点

# insmod first_drv.ko # ./first_test open error# cat proc/devices Character devices:  1 mem  4 /dev/vc/0  4 tty  5 /dev/tty  5 /dev/console  5 /dev/ptmx  7 vcs 10 misc 13 input 14 sound 29 fb 81 video4linux 89 i2c 90 mtd116 alsa128 ptm136 pts180 usb188 ttyUSB189 usb_device204 tq2440_serial252 first_drv253 usb_endpoint254 rtcBlock devices:259 blkext  7 loop  8 sd 31 mtdblock 65 sd 66 sd 67 sd 68 sd 69 sd 70 sd 71 sd128 sd129 sd130 sd131 sd132 sd133 sd134 sd135 sd179 mmc                                                                                                                             <pre name="code" class="cpp">通过查看可知系统自动产生了主设备号, 只需要手动创建节点的时候使用该主设备号即可
# mknod /dev/xxx c 252 0# ls -l /dev/xxx crw-r--r--    1 root     root      252,   0 Jan  1 20:49 /dev/xxx# ./first_test first_drv_openfirst_drv_write# 
                                             
0 0
原创粉丝点击