[驱动文件内容]
对设备的操作函数
- 打开设备
- 从设备文件中读取数据
- 向设备文件内写入数据
- 关闭设备文件
三个组件
- 设备操作函数结构体
- 设备初始化(作为驱动的入口函数)
- 设备注销(作为驱动的出口函数)
其他信息
- LICENSE
- 作者信息
…
驱动文件包含的函数
- 打开设备
static int chrdevbase_open(struct inode *inode, struct file *filp) - 从设备读取数据
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) - 向设备写数据
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) - 关闭设备
static int chrdevbase_release(struct inode *inode, struct file *filp) - 驱动入口函数(注册)
static int __init chrdevbase_init(void) - 驱动出口函数(注销)
static void __exit chrdevbase_exit(void)
驱动文件包含的设备操作函数结构体
这个结构体内的成员函数被赋值为我们在驱动文件里编写的相对应的函数 static struct file_operations chrdevbase_fops = { .owner = THIS_MODULE, .open = chrdevbase_open, .read = chrdevbase_read, .write = chrdevbase_write, .release = chrdevbase_release, };
注意事项
-
关于从设备读取数据的函数(xxx_read()) 在编写 xxx_read 函数的时候,不要直接读取在内核中定义的数据,即 kerneldata 的内容, 而是要先用 memcpy 函数拷贝一份数据到发送缓冲区 readbuf 中,然后用 copy_to_user() 函数(该 函数的功能是复制一段数据给用户)将缓冲区的数据复制给用户空间的数据缓冲区 buf 中,这样做的 目的主要是为了保证内核的独立性。而在应用程序中读取buf内的数据就行了。 -
关于向设备写数据的函数(xxx_write()) 直接利用 copy_from_user() 函数将数据搬到 writebuf 中就行。 -
关于驱动初始化(chrdevbase_init()) 利用函数 register_chrdev() 注册设备驱动,该函数有3个参数,分别是:
- 主设备号: CHRDEVBASE_MAJOR
- 设备名: CHRDEVBASE_NAME
- 设备操作函数结构体: &chrdevbase_fops
[应用文件内容]
对驱动文件的操作过程
- 获取驱动文件的文件名
- 用open()函数打开驱动文件
- 用read()函数从驱动文件内读取数据(optional)
- 用write()函数向驱动文件写入数据(optional)
- 读写完毕后用close()函数关闭文件
参数说明
- fd: 文件描述符
- retvalue: 文件操作返回值
- *filename: 指针,用来存放文件的名字
- argc: 应用程序参数个数
- argv[]: 应用程序的具体参数集合,字符串的形式
argv[0]: 应用程序名 argv[1]: 要操作的驱动文件名 argv[2]: 操作标志 - readbuf[100]: 数据接收缓冲区
- writebuf[100]: 数据发送缓冲区
应用程序的主函数流程
- 先判断应用程序参数是否齐全,齐全则继续,不齐全则返回 -1;
- 利用 filename 接收驱动文件的名字,并放入argv[1]中;
- 用 open 函数打开文件,并用 fd 接收函数的返回值,
如果 fd = -1 ,则说明文件打开失败; - 文件打开成功后,用 read 函数读取文件,用 retvalue 接收函数的返回值,
如果 retvalue < 0 则说明读取文件内容失败,否则成功读取数据,并打印出来, read(fd, readbuf, 50) 的意思是读取指定的文件描述符的文件中50个字节数据, 并放入 readbuf 即接收缓冲区中; - 再利用 write 函数将数据写入驱动文件中,需要注意的是,向驱动文件写入数据时,
不能直接写入,而应该利用 memcpy 函数拷贝一份用户数据存放到临时的发送缓冲区中, 以此来保证内核的独立性。因为 writebuf 在函数执行时才会临时在堆栈中申请内存空间, 当函数执行完后就会立即被释放掉; - 当读写操作完成后,就用 close 函数关闭驱动文件。
- 注意事项:读写操作要通过判断argv[2]的数值来决定是读数据还是写数据,可以利用
函数 atoi() 将字符串类型转化为整数型数据。
代码
chrdevbase.c文件(驱动文件)
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#define CHRDEVBASE_MAJOR 200
#define CHRDEVBASE_NAME "chrdevbase"
static char readbuf[100];
static char writebuf[100];
static char kerneldata[] = {"kernel data!"};
static int chrdevbase_open(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue = 0;
memcpy(readbuf, kerneldata, sizeof(kerneldata));
retvalue = copy_to_user(buf, readbuf, cnt);
if(retvalue == 0){
printk("kernel senddata ok!\r\n");
}
else{
printk("kernel senddata failed!\r\n");
}
return 0;
}
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue = 0;
retvalue = copy_from_user(writebuf, buf, cnt);
if(retvalue == 0){
printk("kernel recevdata:%s\r\n", writebuf);
}
else{
printk("kernel recevdata failed!\r\n");
}
return 0;
}
static int chrdevbase_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations chrdevbase_fops = {
.owner = THIS_MODULE,
.open = chrdevbase_open,
.read = chrdevbase_read,
.write = chrdevbase_write,
.release = chrdevbase_release,
};
static int __init chrdevbase_init(void)
{
int retvalue = 0;
retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);
if(retvalue < 0){
printk("chrdevbase driver register failed\r\n");
}
printk("chrdevbase init!\r\n");
return 0;
}
static void __exit chrdevbase_exit(void)
{
unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);
printk("chrdevbase exit!\r\n");
}
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("swiler");
chrdevApp.c文件(应用文件)
#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!"};
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;
}
Makefile文件
KERNELDIR := /home/swiler/linux_core/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENT_PATH := $(shell pwd)
obj-m := chrdevbase.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
终端执行命令
编译
make clean
make
arm-linux-gnueabihf-gcc chrdevbaseApp.c -o chrdevbaseApp
cp chrdevbase.ko chrdevbaseApp /home/swiler/workdir/rootfs/lib/modules/4.1.15 -f
启动
开发板在u-boot模式下,通过tftp启动内核,进入 /lib/modules/4.1.15/ 目录下;
- 先加载驱动文件,用 insmod chrdevbase.ko 或 modprobe chrdevbase.ko 命令。
(首次使用modprobe命令要先执行depmod命令)。 - 再创建设备节点文件
命令:mknod /dev/chrdevbase c 200 0 - 执行应用程序 chrdevbaseApp,测试驱动是否正常运行。
正常运行则会出现如图所示:
|