Linux driver development--copy_to_user, copy_from_user functions to achieve mutual access between kernel space data and user space data

https://yq.aliyun.com/articles/30152

device read operation

If the operation is empty, the read system call will fail to return a negative EINVAL, and the actual number of bytes read will be returned normally.

ssize_t (*read)(struct file *filp, char __user *buf, size_t  count, lofft *f_pos);
filp: the device file file structure pointer to be operated
buf: the user space buffer pointer to which the read data is to be written
count: the number of bytes of data to be read
f_pos: The position of the data file to be read, relocated according to the actual number of bytes read after the reading is completed
Returns: the number of bytes actually read successfully, or a negative value on failure


Device write operation
If the operation is empty, the write system call will fail to return negative EINVAL, and the actual number of bytes written will be returned normally
ssize_t (*write)(struct file *filp, const char __user *buf, size_t count, loff_t * f_pos);

filp: the device file file structure pointer to be operated
buf: the user space buffer pointer to which the read data is to be written
count: the number of bytes of data to be read
f_pos: The position of the data file to be read, relocated according to the actual number of bytes written after the writing is completed
Returns: the number of bytes actually written on success, negative value on failure


The kernel provides a method for the driver to pass data in the kernel space and user space. It is
defined in arch/arm/include/asm/uaccess.h
User space --> kernel space
copy_from_user function
unsigned long copy_from_user(void *to, const void *from , unsigned long n);
to: target address (kernel space)
from: source address (user space)
n: number of bytes of data to be copied
Return: return 0 for success, return the number of data bytes that were not copied successfully
get_user macro
int get_user(data, ptr);
data: can be a kernel variable of type byte, half word, word, or double word
ptr: user space memory pointer
Return: return 0 on success, non-zero on failure

-----------------------------------------------------------------------------------------------

Kernel space --> user space
copy_to_user function
unsigned long copy_to_user(void *to, const void *from, unsigned long n)
to: target address (user space)
from: source address (kernel space)
n: number of bytes of data to be copied
Return: 0 is returned on success, and the number of data bytes that have not been copied successfully is returned on failure.
Put_user macro:
int put_user(data, prt)
data: can be a kernel variable of type byte, half word, word, or double word
ptr: user space memory pointer
Return: return 0 on success, non-zero on failure

/**
*Copyright (c) 2013. TianYuan
*All rights reserved.
*
*File name: char_device_driver08.c
*File identification: use of copy_to_user and copy_from_user functions
*
*Current version: 1.0
*Author: wuyq
*
*Superseded version: xxx
*Original author: xxx
*Completion date: 2013-11-28
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/uaccess.h>


MODULE_LICENSE("GPL");
#define CDD_MAJOR 200//cat /proc/devices find an unused one
#define CDD_MINOR	0
#define CDD_COUNT	10
dev_t dev = 0;
u32 cdd_major = 0;
u32 cdd_minor = 0;

struct class *dev_class = NULL;
#define BUF_SIZE	100
struct cdd_cdev{
	struct cdev cdev;
	struct device *dev_device;
	u8 led;
	
	char kbuf[BUF_SIZE];
};

struct cdd_cdev *cdd_cdevp = NULL;

int cdd_open(struct inode* inode, struct file *filp)
{
	struct cdd_cdev *pcdevp = NULL;
	printk("enter cdd_open!\n");
	
	pcdevp = container_of(inode->i_cdev, struct cdd_cdev, cdev);
	printk("led = %d\n", pcdevp->led);
	
	filp->private_data = pcdevp;
	
	return 0;
}

int cdd_read(struct file *filp, char __user *buf, size_t count, loff_t *offset)
{
	int ret = 0;
	
	struct cdd_cdev *cdevp = filp->private_data;
	printk("enter cdd_read!\n");
	ret = copy_to_user(buf, cdevp->kbuf, count);
	printk("kernel kbuf content:%s\n", cdevp->kbuf);
	return ret;
}

int cdd_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset)
{
	int ret = 0;
	struct cdd_cdev *cdevp = filp->private_data;	
	printk("enter cdd_write!\n");
	
	ret = copy_from_user(cdevp->kbuf, buf, count);
	
	return ret;
}

int cdd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data)
{
	printk("enter cdd_ioctl!\n");
	return 0;
}

int cdd_release(struct inode *inode, struct file *filp)
{
	printk("enter cdd_release!\n");
	return 0;
}

struct file_operations cdd_fops = {
	.owner = THIS_MODULE,
	.open = cdd_open,
	.read = cdd_read,
	.write = cdd_write,
	.ioctl = cdd_ioctl,
	.release = cdd_release,
	};

int __init cdd_init(void)
{
	int ret = 0;
	int i = 0;
	
	if(cdd_major){
		dev = MKDEV(CDD_MAJOR, CDD_MINOR);//Generate device number
		//Register the device number; 1, the starting device number to be registered 2, the number of consecutively registered device numbers 3, the name
		ret = register_chrdev_region(dev, CDD_COUNT, "cdd_demo");
	}else{
		// Dynamically assign the device number
		ret = alloc_chrdev_region(&dev, cdd_minor, CDD_COUNT, "cdd_demo02");
	}
	
	if(ret < 0){
		printk("register_chrdev_region failed!\n");
		goto failure_register_chrdev;
	}
	//Get the main device number
	cdd_major = MAJOR(dev);
	printk("cdd_major = %d\n", cdd_major);
	
	cdd_cdevp = kzalloc(sizeof(struct cdd_cdev)*CDD_COUNT, GFP_KERNEL);
	if(IS_ERR(cdd_cdevp)){
		printk("kzalloc failed!\n");
		goto failure_kzalloc;
	}
	/*Create device class*/
	dev_class = class_create(THIS_MODULE, "cdd_class");
	if(IS_ERR(dev_class)){
		printk("class_create failed!\n");
		goto failure_dev_class;
	}
	for(i=0; i<CDD_COUNT; i++){
		/*Initialize cdev*/
		cdev_init(&(cdd_cdevp[i].cdev), &cdd_fops);
		/* add cdev to the kernel */
		cdev_add(&(cdd_cdevp[i].cdev), dev+i, 1);
		/* “/dev/xxx” */
		device_create(dev_class, NULL, dev+i, NULL, "cdd%d", i);
		
		cdd_cdevp [i] .led = i;
		
	}
	
	return 0;
failure_dev_class:
	kfree(cdd_cdevp);
failure_kzalloc:
	unregister_chrdev_region(dev, CDD_COUNT);
failure_register_chrdev:
	return ret;
}

void __exit cdd_exit(void)
{
/*Reverse order elimination*/
	int i = 0;
	for(; i < CDD_COUNT; i++){
		device_destroy(dev_class, dev+i);
		cdev_del(&(cdd_cdevp[i].cdev));
		//cdev_del(&((cdd_cdevp+i)->cdev));
	}
	class_destroy(dev_class);
	kfree(cdd_cdevp);
	unregister_chrdev_region(dev, CDD_COUNT);
	
}	

module_init(cdd_init);
module_exit(cdd_exit);

/**
*Copyright (c) 2013. TianYuan
*All rights reserved.
*
*File name: char_device_driver08_test.c
*File identification: Test program: copy_to_user, copy_frome_user of test kernel space
*	执行:先./char_device_driver08_test	再cat /dev/cdd0 cat /dev/cdd5观察两者的区别
*当前版本:1.0
*作者:wuyq 
*
*取代版本:xxx
*原作者:xxx
*完成日期:2013-11-28
*/
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
/*手工创建设备节点文件
mknod /dev/cdd c 248 0
*/
int fd = 0;
char rbuf[100];
char wbuf[100] = "nihao!\n";


int main()
{
	char ch;
	
	fd = open("/dev/cdd5", O_RDWR);
	if(fd < 0){
		printf("open failed!\n");
		return -1;
	}
	printf("open successed fd = %d\n", fd);
	while(1)
	{
		printf("starting to test /dev/cdd...\n");
		ch = getchar();
		getchar();//取走回车
		if(ch == 'q'){
			break;
		}
		switch(ch){
			case 'r':
				read(fd, rbuf, 100);
				printf("user space from kernel: %s\n", rbuf);
				break;
			case 'w':
				write(fd, wbuf, 100);
				break;
			case 'o':
				ioctl(fd, 0, 0);
				break;
			default:
				break;
		}
		sleep(1);
	}
	
	close(fd);
	return 0;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325577342&siteId=291194637