本文是基于韦东山视频的学习笔记
用户程序和驱动程序
举个栗子,用户怎么控制开发板上的led灯呢?
用户控制应用程序,应用程序调用应用程序里的open、read、write
函数,通过库函数发出swi异常指令,再调用驱动程序里面的open、read、write
等函数去操作硬件设备led。
在linux下设备是分为字符设备和块设备的,我想都说linux下一切皆文件,是因为linux用设备时就是把设备当作文件一样读写吧。
驱动程序框架
框架其实很简单,框架来
- 定义一个
file_operations
结构体,这个结构体包括open、read、write
函数,等下会用register_chrdev
注册这个结构体 - 前面定义了
open、read、write
函数,那当然要填充这些函数了,都说是最简单的驱动函数了,这里的函数就打印啥也不做。 - 刚刚提到的需要注册
file_operations
结构体,还要卸载file_operations
结构体
1. 定义operation结构体
/* 定义结构体,传参给内核 */
static const struct file_operations first_drv = {
.owner = THIS_MODULE,
.open = first_drv_open,
.write = first_drv_write,
};
2. open、read、write 函数
ssize_t first_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
int first_drv_open (struct inode *node, struct file *file)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* 定义结构体,传参给内核 */
static const struct file_operations first_drv = {
.owner = THIS_MODULE,
.open = first_drv_open,
.write = first_drv_write,
};
3. 注册、卸载驱动函数
注册时注册为111号设备。
/* 初始化(注册)驱动 */
static int __init first_drv_init(void)
{
register_chrdev(111, "first_drv", &first_drv);
return 0;
}
/* 卸载驱动 */
static void __exit first_drv_exit(void)
{
unregister_chrdev(111, "first_drv");
}
4. Makefile
需要在内核目录下编译,内核需要成功编译。
# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH, 比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
# 请参考各开发板的高级用户使用手册
KERN_DIR = /home/root/work/system/linux-2.6.22.6
CROSS_COMPILE = arm-linux-
all:
make -C $(KERN_DIR) M=`pwd` modules
$(CROSS_COMPILE)gcc -o first_drv_test first_drv_test.c
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
rm -f first_drv_test
obj-m += first_drv.o
5. 测试程序
这里的测试程序也很简单,首先打开这个叫xxx
设备,名字是乱起的,因为设备可以等下自己创建,打不开的话打印can't open file first_drv
这句话,打开了只是随便写个数值。
int main(int argc, char **argv)
{
int val = 1;
int fd = 0;
fd = open("/dev/xxx", O_RDWR);
if (fd<0) printf("can't open file first_drv\n");
write(fd, &val, 4);
return 0;
}
成功编译,啪的一下烧进去后,很快啊!居然显示can't open file first_drv
,我说这没用,因为根本现在没有/dev/xxx
这个设备节点。
不信的话执行命令ls /dev/xxx
看一下,反馈ls: /dev/xxx: No such file or directory,
再执行命令cat /proc/devices
,发现first_drv
是111号设备,当然了,这是我们自己注册的。
如果想成功测试,就自己创建一个/dev/xxx
这个设备,用命令mknod /dev/xxx c 111 0
创建了一个111号叫/dev/xxx
的设备节点,再执行驱动测试程序,就会有想要的结果了。
这里可以看出Linux是靠设备号来对应设备 open、read、write
函数的,设备节点的名字随自己写。
总结
驱动程序
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/mm.h>
#include <linux/highmem.h>
#include <linux/blkdev.h>
#include <linux/module.h>
#include <linux/backing-dev.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
unsigned int *pGPFCON = NULL; //指针变量pGPFCON设置为地址0x56000050
unsigned int *pGPFDAT = NULL; //指针变量pGPFDAT设置为地址0x56000054
static int major = 0;
static struct class *first_class;
static struct class_device *first_drv_class;
ssize_t first_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
int first_drv_open (struct inode *node, struct file *file)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* 定义结构体,传参给内核 */
static const struct file_operations first_drv = {
.owner = THIS_MODULE,
.open = first_drv_open,
.write = first_drv_write,
};
/* 初始化(注册)驱动 */
static int __init first_drv_init(void)
{
register_chrdev(111, "first_drv", &first_drv);
return 0;
}
/* 卸载驱动 */
static void __exit first_drv_exit(void)
{
unregister_chrdev(111, "first_drv");
}
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");
测试程序
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
int val = 1;
int fd = 0;
fd = open("/dev/xxx", O_RDWR);
if (fd<0) printf("can't open file first_drv\n");
write(fd, &val, 4);
return 0;
}
下一篇 -> 稍简单的字符驱动程序-操作led