当前位置:网站首页>通过字符设备虚拟文件系统实现kernel和userspace数据交换(基于kernel 5.8测试通过)

通过字符设备虚拟文件系统实现kernel和userspace数据交换(基于kernel 5.8测试通过)

2022-08-11 05:23:00 丶随心

kernel space代码

 

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/io.h> //ioremap()
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h> //kmalloc

#define DEVICE_NAME "icdev"
#define BUF_SIZE        (1024)
#define ICT_IOCTL_MAGIC_NUM    'K'
#define ICTIOC_GETDEV_INFO       _IOR(ICT_IOCTL_MAGIC_NUM, 0, int)
#define ICTIOC_SETDEV_INFO       _IOWR(ICT_IOCTL_MAGIC_NUM, 1, int)

#ifndef NULL
#define NULL (0)
#endif

typedef struct {
    unsigned char *rw_buf;
    unsigned char *ioctl_buf;
} user_data_type;

static struct cdev *pdev = NULL;
static dev_t dev;
static int major = -1;
static int minor = 2;
static int device_count = 2;

static struct class* led_class;

static struct semaphore sem;

static int icdev_open(struct inode * inode, struct file * file) {
    user_data_type* userData = NULL;

    printk("%s (pid=%d, comm=%s)\n", __func__, current->pid, current->comm);
    userData = (user_data_type*)kmalloc(sizeof(user_data_type), GFP_KERNEL);
    if(NULL == userData) {
        return -ENOMEM;
    }
    userData->rw_buf =
           (unsigned char *)kmalloc(BUF_SIZE + 1, GFP_KERNEL);
    ((user_data_type*)userData)->ioctl_buf =
           (unsigned char *)kmalloc(BUF_SIZE + 1, GFP_KERNEL);
    
    if(NULL == userData->rw_buf
        || NULL == userData->ioctl_buf) {
        return -ENOMEM;
    }
    
    userData->rw_buf[0] = (unsigned char)(0xFF & current->pid);
    userData->ioctl_buf[0] = (unsigned char)(0xFF & (current->pid >> 0));
    file->private_data = (void*)userData;
    return 0;
}

static int icdev_release(struct inode *inode, struct file * file)
{
    user_data_type* userData = NULL;
    printk("%s,%d\n",__func__,__LINE__);
    
    userData = (user_data_type*)(file->private_data);
    
    if(NULL != userData->ioctl_buf) {
       kfree(userData->ioctl_buf);
    }
    if(NULL != userData->rw_buf) {
       kfree(userData->rw_buf);
    }
    kfree(userData);

    return 0;
}

static ssize_t icdev_read(struct file * file, char __user * buffer,
                          size_t size, loff_t * loff) {
    int ret = 0;
    char * kbuf = ((user_data_type*)(file->private_data))->rw_buf;

    do {
    
        if(size > BUF_SIZE) {
            ret = -EFAULT;
            break;
        }

        if(down_interruptible(&sem)) {
            printk("no semaphore.\n");
            ret = -ERESTARTSYS;
        }

        if(copy_to_user(buffer, kbuf, size)) {
            printk("failed to copy_to_user\n");
            ret = -EFAULT;
            up(&sem);
            break;
        } else {
            ret = size;
        }
        up(&sem);

    } while(0);

    printk("read: size=%d,return: %d\n", (int)size, ret);
    return ret;
}

static ssize_t icdev_write(struct file * file, const char __user * buffer,
                   size_t size, loff_t * loff) {
    int ret = size;
    char * kbuf = ((user_data_type*)(file->private_data))->rw_buf;

    do {
        if(size > BUF_SIZE) {
            ret = -ENOMEM;
        }

        if(down_interruptible(&sem)) {
            ret = -ERESTARTSYS;
        }

        if(copy_from_user(kbuf,buffer,size)) {
            ret = -EFAULT;
            up(&sem);
            break;
        }
        up(&sem);
    } while (0);

    printk("write: size=%d,return: %d\n", (int)size, ret);
    return ret;
}

static long icdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
    int ret = 0;
    void __user *argp = (void __user *)arg;
    char * kbuf = ((user_data_type*)filp->private_data)->ioctl_buf;
    
    if(down_interruptible(&sem)) {
            ret = -ERESTARTSYS;
    }
 
    switch(cmd) {
        case ICTIOC_GETDEV_INFO:
            put_user(kbuf[0], (int *)arg);
            printk(KERN_INFO"copy to user: %c\n", kbuf[0]);
            break;
 
        case ICTIOC_SETDEV_INFO:
            get_user(kbuf[0], (char __user *)argp);
            printk(KERN_INFO"Set as: %c\n", kbuf[0]);
            break;
            
        default:
            break;
    }

    up(&sem);
    return ret;
}

static struct file_operations fops = {
    .owner   = THIS_MODULE,
    .open    = icdev_open,
    .release = icdev_release,
    .read    = icdev_read,
    .write   = icdev_write,
    .unlocked_ioctl   = icdev_ioctl,
};

static int __init icdev_init(void) {
    int ret = 0;
    if(dev) {
        ret = register_chrdev_region(dev, device_count, DEVICE_NAME);
    } else {
        ret = alloc_chrdev_region(&dev, minor, device_count, DEVICE_NAME);
    }
    if(ret) {
            printk("alloc_chrdev_region failed.\n");
            goto ERROR_CDEV;
    }

    pdev = cdev_alloc();
    if(NULL == pdev) {
        printk("cdev_alloc failed.\n");
        return -ENOMEM;
    }
    cdev_init(pdev,&fops);
    ret = cdev_add(pdev, dev, device_count);
    if(ret) {
        printk("cdev_add failed.\n");
        goto  ERROR_ADD;
    }
    
    led_class = class_create(THIS_MODULE, "module_test3");
    if ( NULL == device_create(led_class, NULL, dev, NULL, DEVICE_NAME)) {
        printk("device_create failed.\n");
        ret = -1;
        goto ERROR_DEVICE;
    }

    sema_init(&sem, 1);

    major = MAJOR(dev);

    printk("cdev_demo_init ok. MAJOR is %d\n",MAJOR(dev));
    return ret;

ERROR_DEVICE:
    cdev_del(pdev);
ERROR_ADD:
    unregister_chrdev_region(dev, device_count);
ERROR_CDEV:
    cdev_del(pdev);
    return ret;
}

static void __exit icdev_exit(void) {
    printk("%s,%d\n",__func__,__LINE__);
    device_destroy(led_class, dev);	
    class_destroy(led_class);
    unregister_chrdev_region(MKDEV(major,minor), device_count);
    cdev_del(pdev);
}

module_init(icdev_init);
module_exit(icdev_exit);
MODULE_LICENSE("GPL");

用户空间代码

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h> //alarm()
#include<stdlib.h> //exit()

#include <sys/ioctl.h>

#define BUFFER_SIZE  (1024)
#define DEVICE_NAME "/dev/icdev"

#define ICT_SUCCESS            0
#define ICT_ERROR_DEV        1
#define ICT_ERROR_PARAM   2
#define ICT_ERROR_IOCTL     3
 
#define ICT_IOCTL_MAGIC_NUM    'K'
#define ICTIOC_GETDEV_INFO       _IOR(ICT_IOCTL_MAGIC_NUM, 0, int)
#define ICTIOC_SETDEV_INFO       _IOWR(ICT_IOCTL_MAGIC_NUM, 1, int)
 
#define ICT_DEV_FILE        "/dev/ictdev"

void timer(int sig) {
    if(SIGALRM == sig) {
        printf("SIGALRM\n");
        exit(0);
    }
    return ;
}

int main(int argc,char* argv[]) {
    int ret = -10086;
    int devInfo;

    signal(SIGALRM, timer);
    alarm(10);

    int fd = open(DEVICE_NAME, O_RDWR /*| O_NONBLOCK*/);
    if(0 > fd) {
        perror("open\n");
        return -1;
    }

    printf("open %s ,fd=%d\n",DEVICE_NAME, fd);

    char rbuf[BUFFER_SIZE + 1] = {0};
    ret  = read(fd,rbuf,BUFFER_SIZE);
    printf("read ret = %d,errno = %d\n", ret, errno);

    char buf[] = "1234567891111111111111111";
    ret = write(fd,buf,sizeof(buf));
    printf("write ret = %d,errno = %d\n", ret, errno);

    // ioctl test
    int ict_fd = fd;

    if(ioctl(ict_fd, ICTIOC_GETDEV_INFO, &devInfo) < 0) {
        printf("Get ICT device info fail.\n");
        return ICT_ERROR_IOCTL;
    }
    printf("ICT device info is: %d\n", devInfo);
    
    devInfo = 99;

    if(ioctl(ict_fd, ICTIOC_SETDEV_INFO, &devInfo)) {
        printf("Set ICT device info fail.\n");
        return ICT_ERROR_IOCTL;
    }

   if(ioctl(ict_fd, ICTIOC_GETDEV_INFO, &devInfo) < 0) {
        printf("Re-get ICT device info fail.\n");
        return ICT_ERROR_IOCTL;
    }
    printf("Re-get ICT after set: %d\n", devInfo);

    close(fd);
    return  0;
}

内核空间和用户空间的编译Makefile书写,可以参考我的另外一篇文章:

https://blog.csdn.net/suixin______/article/details/117130033?spm=1001.2014.3001.5501

原网站

版权声明
本文为[丶随心]所创,转载请带上原文链接,感谢
https://blog.csdn.net/suixin______/article/details/117228495