8 、 应用层与内核层进行数据交互
还是以上节的杂项设备为例
因为linux一切皆文件、所以驱动设备在linux中也是以文件的方式存在的。
应用层与内核层数据交互也是以文件方式进行
struct file_operations 文件操作
当应用层使用open打开设备文件时,就会触发驱动设备中的open函数
int (*open) (struct inode *, struct file *);
当应用层使用close关闭设备文件时,就会触发驱动设备中的release函数
int (*release) (struct inode *, struct file *);
当应用层使用read读取设备文件时,就会触发驱动设备中的read函数
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
当应用层使用write写入设备文件时,就会触发驱动设备中的write函数
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
当应用层使用ioctl操作设备文件时,就会触发驱动设备中的unlocked_ioctl函数
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
内核空间数据copy到用户空间
copy_from_user函数目的是从用户空间拷贝数据到内核空间,失败返回没有被拷贝的字节数,成功返回0。从用户空间拷贝数据到内核中时必须非常小心,如果用户空间的数据地址是个非法的地址,或是超出用户空间的范围,或是那些地址还没有被映射到,都可能对内核产生很大的影响。copy_from_user函数的功能就不只是从用户空间拷贝数据那样简单了,它还要做一些指针检查以及处理这些问题的方法。
copy_from_user 原型
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
{
if (likely(access_ok(VERIFY_READ, from, n)))
n = __copy_from_user(to, from, n);
else
memset(to, 0, n);
return n;
}
用户空间copy到内核空间
copy_to_user函数则是从内核空间拷贝内容到用户空间,用户空间的进程无法直接访问内核空间的内容。这个函数做了数据合法判断。然后进行拷贝。 成功返回0, 失败返回非0
copy_to_user
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)
{
if (likely(access_ok(VERIFY_WRITE, to, n)))
n = __copy_to_user(to, from, n);
return n;
}
实例
驱动程序
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h> //杂项设备的头文件struct miscdevice
#include <linux/fs.h> //文件操作的头文件 struct file_operations
#include <linux/uaccess.h> //这个是copy_to/from_user 的头文件
//第二步填充file_operations结构体
ssize_t misc_read (struct file *file, char __user * user, size_t size, loff_t *loff_t)
{
char buf[64] = "hello world";
printk("misc_read!\n");
// copy_to_user(void __user * to, const void * from, unsigned long n) 将数据由内核空间copy到用户空间
// 成返回0 失败返回非0
if (copy_to_user(user, buf, strlen(buf)) != 0) //将内核空间数据copy到user空间
{
printk("copy to user is error!\n");
return -1;
}
printk("copy to user is success!\n");
return 0;
}
ssize_t misc_write (struct file * file, const char __user *user, size_t n, loff_t *loff_t)
{
char buf[64] = {0};
printk("misc_write!\n");
if (copy_from_user(buf, user, n) != 0) //从user空间copy数据到内核空间
{
printk("copy from user is error!\n");
return -1;
}
printk("copy from user is success!\n");
printk("misc write :%s\n", buf);
return 0;
}
int misc_open (struct inode *inode, struct file *file)
{
printk("misc_open!\n");
return 0;
}
int misc_release (struct inode *inode, struct file *file)
{
printk("misc_release!\n");
return 0;
}
struct file_operations misc_fops = {
.owner = THIS_MODULE, // 当前用户拥有
.open = misc_open, //open
.write = misc_write, //write
.read = misc_read //read
};
//第一步填充miscdevice结构体
struct miscdevice miscdev = {
.minor = MISC_DYNAMIC_MINOR, //自动获取次设备号
.name = "hello_misc", //杂项设备名称
.fops = &misc_fops //文件操作集
};
static int misc_init(void)
{
int ret;
//第三步 注册杂项设备并生成设备节点
ret = misc_register(&miscdev);
if (0 > ret)
{
printk("misc register is error!\n");
return -1;
}
printk("misc register is success!\n");
return 0;
}
static void misc_exit(void)
{
misc_deregister(&miscdev); //卸载
printk("byb byb\n"); // 这里打印函数需要用printk 因为printf是C库函数
}
module_init(misc_init); //入口
module_exit(misc_exit); //出口
MODULE_LICENSE("GPL"); //协议声明
makefile
# -m 就是说要把驱动编译成模块
obj-m += misc.o
# 内核路径
KDIR := /home/now/kernel3.0/iTop4412_Kernel_3.0
#获取当前路径
PWD = $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
应用层程序
#include<stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main(int argc,char *argv[])
{
int fd;
char buf[64] ={0};
char *str = "hello world";
fd = open(argv[1], O_RDWR);
if (fd == -1)
{
perror("open");
return -1;
}
int ret = read(fd, buf, sizeof(buf));
if (ret == -1)
{
perror("read");
return -1;
}
printf("read:%s\n", buf);
ret = write(fd, str, strlen(str));
if (ret == -1)
{
perror("write");
return -1;
}
close(fd);
return 0;
}
首先在ubuntu上执行make, 编译出.ko文件,然后将ko文件copy到开发板,执行insmod加载驱动