最简单的字符驱动程序


本文是基于韦东山视频的学习笔记

汇总点这

用户程序和驱动程序

举个栗子,用户怎么控制开发板上的led灯呢?

用户控制应用程序,应用程序调用应用程序里的open、read、write 函数,通过库函数发出swi异常指令,再调用驱动程序里面的open、read、write等函数去操作硬件设备led。

在linux下设备是分为字符设备块设备的,我想都说linux下一切皆文件,是因为linux用设备时就是把设备当作文件一样读写吧。图取自嵌入式Linux应用开发完全手册

驱动程序框架

框架其实很简单,框架来

  1. 定义一个file_operations结构体,这个结构体包括open、read、write 函数,等下会用register_chrdev注册这个结构体
  2. 前面定义了open、read、write 函数,那当然要填充这些函数了,都说是最简单的驱动函数了,这里的函数就打印啥也不做。
  3. 刚刚提到的需要注册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

汇总点这

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值