最最基础的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#
- 最最基础的linux驱动程序
- 最最最最简单的Linux后门~~, HOHOHO~~
- 最最基础的最小二乘法优化
- JAVA-最最最最基础的一些知识,你知道吗?
- linux系统最最最最常用的操作指令
- Maven的使用-最最基础的操作
- 两道最最基础的题:
- 最最基础的Android倒计时应用
- 最最最基础的软件测试
- Linux USB驱动程序基础
- linux驱动程序基础
- 最最实用的30个Linux命令
- 最最实用的30个Linux命令!
- java最最基础
- YAML最最基础语法
- Xlistview最最基础操作
- 最最最基础js
- 链式前向星的最最基础的模板
- POJ 2299 Ultra-QuickSort (逆序数)
- UVa10075 - Airlines(所有点对之间的最短距离)
- SDUT 2878 Circle(概率dp)
- si4463跳频功能简介
- poj 2676 dfs/DLX(数独)
- 最最基础的linux驱动程序
- 实例:常用数据类型之间的相互转换
- U-boot 中的 .balignl 16 0xdeadbeef 说明
- android中得Sqlite
- 旋转矩阵R 的构成过程(欧拉角的各种表示方法)
- MongoDB学习笔记之副本集概述
- 敏捷开发初探
- 人生
- C++链接和运行相关错误