本期主题:
linux字符驱动之read、write部分
往期链接:
1.copy_to_user / copy_from_user
由于用户空间并不能直接访问内核空间,所以内核提供了以下两个接口来操作:
copy_from_user():完成从用户空间缓存区到内核空间的复制
copy_to_user():完成从内核空间到用户空间缓存区的复制
函数源码:
static inline int copy_from_user(void *to, const void __user volatile *from,
unsigned long n)
{
__chk_user_ptr(from, n);
volatile_memcpy(to, from, n);
return 0;
}
static inline int copy_to_user(void __user volatile *to, const void *from,
unsigned long n)
{
__chk_user_ptr(to, n);
volatile_memcpy(to, from, n);
return 0;
}
2.测试代码
写一个驱动测试模块,驱动中写write和read接口,在测试的时候,使用echo/cat来测试从用户空间到内核空间的读写
示例代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("PD");
#define HELLO_TEST_BUFF_SIZE 0x10
//static char hello_test_buf[HELLO_TEST_BUFF_SIZE];
struct hello_dev
{
char hello_dev_buf[HELLO_TEST_BUFF_SIZE];
};
struct hello_dev *hello_dev_p;
static int hello_open (struct inode *inode, struct file *filep)
{
filep->private_data = hello_dev_p; //作为驱动的private数据
printk("hello_open()\n");
return 0;
}
//param:
//file: 文件结构体指针
//buf: 用户空间内存的地址
//count: 要读的字节数
//ppos: 读的位置相对于文件开头的偏移
static ssize_t hello_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
unsigned long p = *ppos;
int ret;
size_t size = count;
struct hello_dev *dev = file->private_data;
printk("read func, p is 0x%ld\r\n", p);
if (p > HELLO_TEST_BUFF_SIZE)
{
return 0;
}
if (size > HELLO_TEST_BUFF_SIZE - p)
{
size = HELLO_TEST_BUFF_SIZE - p;
}
if (copy_to_user(buf, dev->hello_dev_buf + p, size))
{
ret = -EFAULT;
printk("read failed\r\n");
}
else
{
*ppos += count;
ret = count;
printk("read %u bytes from kernel\r\n", size);
}
return ret;
}
static ssize_t hello_write(struct file *file, const char __user *buf, size_t count,
loff_t *ppos)
{
unsigned long p = *ppos;
int ret;
struct hello_dev *dev = file->private_data;
printk("write func, p is 0x%ld\r\n", p);
if (copy_from_user(dev->hello_dev_buf + p, buf, count))
{
ret = -EFAULT;
printk("write failed\r\n");
}
else
{
*ppos += count;
ret = count;
printk("write %u bytes to kernel\r\n", count);
}
return ret;
}
static struct file_operations hello_ops =
{
.open = hello_open,
.read = hello_read,
.write = hello_write,
};
static int major = 230;
static int minor = 0;
static dev_t dev_num = 0;
static struct cdev cdev;
static int hello_init(void)
{
int result;
int error;
dev_num = MKDEV(major, minor);
result = register_chrdev_region(dev_num, 1, "test");
if (result < 0)
{
printk("register_chrdev_region fail \n");
return result;
}
printk("hello_init, register_chrdev_region OK \n");
cdev_init(&cdev,&hello_ops);
error = cdev_add(&cdev,dev_num,1);
//添加hello_dev_p的kzalloc
hello_dev_p = kzalloc(sizeof(struct hello_dev), GFP_KERNEL);
if (!hello_dev_p)
{
result = -ENOMEM;
printk("kzalloc fail \n");
return result;
}
if(error < 0)
{
printk("cdev_add fail \n");
unregister_chrdev_region(dev_num,1);
return error;
}
return 0;
}
static void hello_exit(void)
{
printk("hello_exit \n");
cdev_del(&cdev);
unregister_chrdev_region(dev_num, 1);
return;
}
module_init(hello_init); //insmod
module_exit(hello_exit);//rmmod
测试结果:
![在这里插入图片描述](https://img-blog.csdnimg.cn/f0276b911cff40d6b4eb89ef212f3ef9.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/03c240d8f96740cdaae8748129baa267.png)