Linux内核学习:手动编写模拟虚拟设备

目录

1 FIFO与虚拟设备

2 编写虚拟设备驱动

2.1 简单的虚拟设备驱动

2.2 编译字节驱动模块

2.3 插入到QEMU上的Linux内核中

2.4 写用户层测试代码测试插入的内核模块

3 KFIFO改进设备驱动

3.1 改进原因

3.2 带有KFIFO环形缓冲的虚拟驱动模块

3.3 写用户层测试代码测试插入的内核模块

4 阻塞I/O与非阻塞I/O

4.1 非阻塞I/O和阻塞I/O的概念

4.2 把虚拟设备驱动改成非阻塞模式

4.2.1 非阻塞模式下的虚拟设备驱动

4.2.2 编译非阻塞模式下的虚拟设备驱动

4.2.3  写用户层测试代码测试插入的内核模块


1 FIFO与虚拟设备

字符硬件设备内部有一个缓冲区(buffer),芯片内部提供了寄存器来访问这些FIFO,可以通过读寄存器把FIFO的内容读取出来,或者通过写寄存器把数据写入FIFO,或者通过寄存器写入FIFO,一般外设芯片支持中断模式,FIFO有数据到达时,外设芯片通过中断线告诉CPU。

下面做的实验就是模拟设备从用户空间写入内核空间FIFO,再从内核空间FIFO读数据到用户层读缓存区的。

2 编写虚拟设备驱动

2.1 简单的虚拟设备驱动

虚拟驱动代码如下:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/slab.h>
 
#define DEMO_NAME "my_demo_dev"
static struct device *mydemodrv_device;
 
/*virtual FIFO device's buffer*/
static char *device_buffer;
#define MAX_DEVICE_BUFFER_SIZE 64
 
static int demodrv_open(struct inode *inode, struct file *file)
{
        int major = MAJOR(inode->i_rdev);
        int minor = MINOR(inode->i_rdev);
 
        printk("%s: major=%d, minor=%d\n", __func__, major, minor);
 
        return 0;
}
 
static int demodrv_release(struct inode *inode, struct file *file)
{
        return 0;
}
 
static ssize_t
demodrv_read(struct file *file, char __user *buf, size_t lbuf, loff_t *ppos)
{
        int actual_readed;
        int max_free;
        int need_read;
        int ret;
 
        printk("%s enter\n", __func__);
 
        max_free = MAX_DEVICE_BUFFER_SIZE - *ppos;
        need_read = max_free > lbuf ? lbuf : max_free;
        if (need_read == 0)
                dev_warn(mydemodrv_device, "no space for read");
 
        ret = copy_to_user(buf, device_buffer + *ppos, need_read);
        if (ret == need_read)
                return -EFAULT;
 
        actual_readed = need_read - ret;
        *ppos += actual_readed;
 
        printk("%s, actual_readed=%d, pos=%d\n",__func__, actual_readed, *ppos);
        return actual_readed;
}
 
static ssize_t
demodrv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
        int actual_write;
        int free;
        int need_write;
        int ret;
 
        printk("%s enter\n", __func__);
 
        free = MAX_DEVICE_BUFFER_SIZE - *ppos;
        need_write = free > count ? count : free;
        if (need_write == 0)
                dev_warn(mydemodrv_device, "no space for write");
 
 
        ret = copy_from_user(device_buffer + *ppos, buf, need_write);
        if (ret == need_write)
                return -EFAULT;
 
        actual_write = need_write - ret;
        *ppos += actual_write;
        printk("%s: actual_write =%d, ppos=%d\n", __func__, actual_write, *ppos);
 
        return actual_write;
}
 
static const struct file_operations demodrv_fops = {
        .owner = THIS_MODULE,
        .open = demodrv_open,
        .release = demodrv_release,
        .read = demodrv_read,
        .write = demodrv_write
};
 
static struct miscdevice mydemodrv_misc_device = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = DEMO_NAME,
        .fops = &demodrv_fops,
};
 
static int __init simple_char_init(void)
{
        int ret;
 
        device_buffer = kmalloc(MAX_DEVICE_BUFFER_SIZE, GFP_KERNEL);
        if (!device_buffer)
                return -ENOMEM;
 
        ret = misc_register(&mydemodrv_misc_device);
        if (ret) {
                printk("failed register misc device\n");
                kfree(device_buffer);
                return ret;
        }
 
        mydemodrv_device = mydemodrv_misc_device.this_device;
 
        printk("succeeded register char device: %s\n", DEMO_NAME);
 
        return 0;
}
 
static void __exit simple_char_exit(void)
{
        printk("removing device\n");
 
        kfree(device_buffer);
        misc_deregister(&mydemodrv_misc_device);
}
 
module_init(simple_char_init);
module_exit(simple_char_exit);
 
MODULE_AUTHOR("Henry_Fordham");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("character device read & write");
 

2.2 编译字节驱动模块

这里要注意,我们需要在QEMU+ARM的环境下插入内核模块,那么就要在QEMU+ARM的环境下编译模块,为了节省时间,可以先下载一个基础环境,如下:

https://github.com/figozhang/runninglinuxkernel_4.0

直接git clone这个环境下来,下载按照README安装,安装完后,编写刚才内核模块的Makefile,注意第一行BASEINCLUDE刚才下载下来的runninglinuxkernel_4.0,Makefile如下。

BASEINCLUDE ?= ~/tools/runninglinuxkernel_4.0                                 
 
mytest-objs  :=my_demodev.o
obj-m  :=mydemo.o
 
all :
    $(MAKE) -C      $(BASEINCLUDE)  M=$(PWD)        modules;
clean:
    $(MAKE)  -C $(BASEINCLUDE)  SUBDIRS=$(PWD) clean;
    rm -f *.ko;

写完之后,make,可以看到my_demo.ko

2.3 插入到QEMU上的Linux内核中

首先,还是挂载一块ext4磁盘到Linux内核中,通过QEMU启动:

​qemu-system-arm -M vexpress-a9 -smp 4 \
 -m 1024M \
 -kernel arch/arm/boot/zImage \
 -append "rdinit=/linuxrc console=ttyAMA0 loglevel=8" \
 -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb  \
 -nographic \
 -sd ext4.img

如果不知道怎么创建一个ext4的磁盘,还是参考第一篇文章。

挂载我们的ext4磁盘到QEMU的mnt:

mount -t ext4 /dev/mmcblk0 /mnt/

同时在主机上将ext4挂载到一个文件目录,使之同步。具体参考第一篇文章。

QEMU中mnt下将mydemo.ko插入进去:

/mnt # ls
lost+found  mydemo.ko
/mnt # insmod mydemo.ko
successed register char device : my_demo_dev
Major numbner =252,minor number =0

2.4 写用户层测试代码测试插入的内核模块

如下是用户级代码: test.c

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define DEMO_DEV_NAME "/dev/my_demo_dev"

int main(){
    char buffer[64];
    int fd;
    int ret;
    size_t len;
    char message[] = "Testing the virtual FIFO device";
    char *read_buffer;
    len = sizeof(message);
    fd = open(DEMO_DEV_NAME, O_RDWR);
    if (fd < 0){
        printf("open device %s failed\n", DEMO_DEV_NAME);
        return -1;
    }

    ret = write(fd, message, len);
    if (ret != len){
        printf("cannot write on device %d, ret=%d", fd, ret);
        return -1;
    }

    read_buffer = malloc(2*len);
    memset(read_buffer, 0, 2*len);
    close(fd);
    fd = open(DEMO_DEV_NAME, O_RDWR);
    if (fd < 0){ 
        printf("open device %s failed\n", DEMO_DEV_NAME);
        return -1; 
    }
    ret = read(fd, read_buffer, 2*len);
    printf("read %d bytes\n", ret);
    printf("read buffer=%s\n",read_buffer);
    close(fd);
    return 0;
}

 编译:

arm-linux-gnueabi-gcc test.c -o test --static

得到一个可执行文件test,将这个文件还是同步到qemu的内核文件系统ext4.img中,然后执行:

Please press Enter to activate this console. 
/ # ls
bin      etc      mnt      sbin     tmp
dev      linuxrc  proc     sys      usr
/ # mount -t ext4 /dev/mmcblk0 /mnt/
EXT4-fs (mmcblk0): recovery complete
EXT4-fs (mmcblk0): mounted filesystem with ordered data mode. Opts: (null)
/ # cd mnt/
/mnt # ls
lost+found    mydemo.ko     test_virtual
/mnt # insmod  mydemo.ko 
succeeded register char device: my_demo_dev
/mnt # mknod /dev/demo_drv c 252 0
/mnt # ./test_virtual
random: nonblocking pool is initialized
demodrv_open: major=10, minor=58
demodrv_write enter
demodrv_write: actual_write =32, ppos=0
demodrv_open: major=10, minor=58
demodrv_read enter
demodrv_read, actual_readed=64, pos=0
read 64 bytes
read buffer=Testing the virtual FIFO device

可以看到,从用户层代码读取到了内核模块的信息。

3 KFIFO改进设备驱动

3.1 改进原因

可以从刚才的驱动程序代码中观察到,在读之前调用了一次open函数,在写之前也调用了一次open函数,这正是因为没有考虑到读和写的并行管理问题,所以在对应的测试程序中,需要重启设备将数据读出来。

所以如果要改进的地点就是能够并行的管理读和写进程。Linux内核实现了一个称为KFIFO的环形缓冲机制,它可以在一个读者线程和一个写者进程并发执行的场景下,无需使用额外的加锁来保证环形缓冲区的数据安全。KFIFO提供的接口函数定义在 include/linux/kfifo.h 文件中。

#define kfifo_from_user(fifo, from, len, copied)
#define kfifo_to_user(fifo, to, len, copied)

fifo表示使用的哪个环形缓存区。

from表示用户空间缓冲区的起始地址,len表示要复制多少个元素,copied保存了成功复制元素的数量。

to表示从fifo缓存空间读出到用户空间缓冲区的起始地址,其他不变。

所以,要改动的地方只有字符模块的 demodrv_read 和 demodrv_write 模块,将主要输入输出函数改为kfifo_from_user和kfifo_to_user。

3.2 带有KFIFO环形缓冲的虚拟驱动模块

带有KFIFO环形缓冲的虚拟驱动模块代码如下:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/kfifo.h>

DEFINE_KFIFO(mydemo_fifo, char, 64);
#define DEMO_NAME "my_demo_dev"
static struct device *mydemodrv_device;
/*virtual FIFO device's buffer*/
static char *device_buffer;
#define MAX_DEVICE_BUFFER_SIZE 64

static int demodrv_open(struct inode *inode, struct file *file)
{
        int major = MAJOR(inode->i_rdev);
        int minor = MINOR(inode->i_rdev);

        printk("%s: major=%d, minor=%d\n", __func__, major, minor);

        return 0;
}

static int demodrv_release(struct inode *inode, struct file *file)
{
        return 0;
}


static ssize_t
demodrv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
        int actual_readed;
        int ret;
        ret = kfifo_to_user(&mydemo_fifo, buf, count, &actual_readed);
        if (ret)
                return -EIO;

        printk("%s, actual_readed=%d, pos=%lld\n",__func__, actual_readed, *ppos);
        return actual_readed;
}

static ssize_t
demodrv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
        unsigned int actual_write;
        int ret;
        ret = kfifo_from_user(&mydemo_fifo, buf, count, &actual_write);
        if (ret)
                return -EIO;

        printk("%s: actual_write =%d, ppos=%d\n", __func__, actual_write, *ppos);
        return actual_write;
}


static const struct file_operations demodrv_fops = {
        .owner = THIS_MODULE,
        .open = demodrv_open,
        .release = demodrv_release,
        .read = demodrv_read,
        .write = demodrv_write
};

static struct miscdevice mydemodrv_misc_device = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = DEMO_NAME,
        .fops = &demodrv_fops,
};

static int __init simple_char_init(void)
{
        int ret;

        device_buffer = kmalloc(MAX_DEVICE_BUFFER_SIZE, GFP_KERNEL);
        if (!device_buffer)
                return -ENOMEM;

        ret = misc_register(&mydemodrv_misc_device);
        if (ret) {
                printk("failed register misc device\n");
                kfree(device_buffer);
                return ret;
        }

        mydemodrv_device = mydemodrv_misc_device.this_device;
        printk("succeeded register char device: %s\n", DEMO_NAME);

        return 0;
}


static void __exit simple_char_exit(void)
{
        printk("removing device\n");

        kfree(device_buffer);
        misc_deregister(&mydemodrv_misc_device);
}

module_init(simple_char_init);
module_exit(simple_char_exit);

MODULE_AUTHOR("Henry_Fordham");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("character device read & write");

之后重新编译,make

3.3 写用户层测试代码测试插入的内核模块

如下是用户级代码: test_virtual.c

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define DEMO_DEV_NAME "/dev/my_demo_dev"

int main(){
    char buffer[64];
    int fd;
    int ret;
    size_t len;
    char message[] = "Testing the virtual FIFO device";
    char *read_buffer;
    len = sizeof(message);
    fd = open(DEMO_DEV_NAME, O_RDWR);
    if (fd < 0){
        printf("open device %s failed\n", DEMO_DEV_NAME);
        return -1;
    }

    ret = write(fd, message, len);
    if (ret != len){
        printf("cannot write on device %d, ret=%d", fd, ret);
        return -1;
    }

    read_buffer = malloc(2*len);
    memset(read_buffer, 0, 2*len);
    close(fd);
    fd = open(DEMO_DEV_NAME, O_RDWR);
    if (fd < 0){ 
        printf("open device %s failed\n", DEMO_DEV_NAME);
        return -1; 
    }
    ret = read(fd, read_buffer, 2*len);
    printf("read %d bytes\n", ret);
    printf("read buffer=%s\n",read_buffer);
    close(fd);
    return 0;
}

 编译:

arm-linux-gnueabi-gcc test_virtual.c -o test --static

得到一个可执行文件test,将这个文件还是同步到qemu的内核文件系统ext4.img中,然后执行:

Please press Enter to activate this console. 
/ # ls
bin      etc      mnt      sbin     tmp
dev      linuxrc  proc     sys      usr
/ # mount -t ext4 /dev/mmcblk0 /mnt/
EXT4-fs (mmcblk0): recovery complete
EXT4-fs (mmcblk0): mounted filesystem with ordered data mode. Opts: (null)
/ # cd mnt/
/mnt # ls
lost+found    mydemo.ko     test_virtual
/mnt # insmod mydemo.ko 
succeeded register char device: my_demo_dev
/mnt # ./test_virtual 
random: nonblocking pool is initialized
demodrv_open: major=10, minor=58
demodrv_write: actual_write =32, ppos=0
demodrv_open: major=10, minor=58
demodrv_read, actual_readed=32, pos=0
read 32 bytes
read buffer=Testing the virtual FIFO device

4 阻塞I/O与非阻塞I/O

4.1 非阻塞I/O和阻塞I/O的概念

非阻塞:进程发起I/O系统调用后,如果设备驱动的缓冲区没有数据,那么进程会返回一个错误而不会被阻塞。如果驱动缓冲区有数据,那么设备驱动把数据直接返回给用户进程。

阻塞:进程发起I/O系统调用后,如果设备的缓冲区没有数据,那么需要到硬件I/O中重新获取数据,进程会被阻塞,也就是休眠等待。直到数据准备好,进程才会被唤醒,并重新把数据返回给用户空间。

4.2 把虚拟设备驱动改成非阻塞模式

4.2.1 非阻塞模式下的虚拟设备驱动

驱动代码如下:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/kfifo.h>

DEFINE_KFIFO(mydemo_fifo, char, 64);
#define DEMO_NAME "my_demo_dev"
static struct device *mydemodrv_device;
/*virtual FIFO device's buffer*/
static char *device_buffer;
#define MAX_DEVICE_BUFFER_SIZE 64

static int demodrv_open(struct inode *inode, struct file *file)
{
        int major = MAJOR(inode->i_rdev);
        int minor = MINOR(inode->i_rdev);

        printk("%s: major=%d, minor=%d\n", __func__, major, minor);

        return 0;
}

static int demodrv_release(struct inode *inode, struct file *file)
{
        return 0;
}


static ssize_t
demodrv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
        int actual_readed;
        int ret;
        if (kfifo_is_empty(&mydemo_fifo)){
            if (file->f_flags & O_NONBLOCK)
                return -EAGAIN;
        }
        ret = kfifo_to_user(&mydemo_fifo, buf, count, &actual_readed);
        if (ret)
                return -EIO;

        printk("%s, actual_readed=%d, pos=%lld\n",__func__, actual_readed, *ppos);
        return actual_readed;
}

static ssize_t
demodrv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
        unsigned int actual_write;
        int ret;
        if (kfifo_is_full(&mydemo_fifo)){
            if (file->f_flags & O_NONBLOCK)
                return -EAGAIN;
        }
        ret = kfifo_from_user(&mydemo_fifo, buf, count, &actual_write);
        if (ret)
                return -EIO;

        printk("%s: actual_write =%d, ppos=%d\n", __func__, actual_write, *ppos);
        return actual_write;
}


static const struct file_operations demodrv_fops = {
        .owner = THIS_MODULE,
        .open = demodrv_open,
        .release = demodrv_release,
        .read = demodrv_read,
        .write = demodrv_write
};

static struct miscdevice mydemodrv_misc_device = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = DEMO_NAME,
        .fops = &demodrv_fops,
};

static int __init simple_char_init(void)
{
        int ret;

        device_buffer = kmalloc(MAX_DEVICE_BUFFER_SIZE, GFP_KERNEL);
        if (!device_buffer)
                return -ENOMEM;

        ret = misc_register(&mydemodrv_misc_device);
        if (ret) {
                printk("failed register misc device\n");
                kfree(device_buffer);
                return ret;
        }

        mydemodrv_device = mydemodrv_misc_device.this_device;
        printk("succeeded register char device: %s\n", DEMO_NAME);

        return 0;
}


static void __exit simple_char_exit(void)
{
        printk("removing device\n");

        kfree(device_buffer);
        misc_deregister(&mydemodrv_misc_device);
}

module_init(simple_char_init);
module_exit(simple_char_exit);

MODULE_AUTHOR("Henry_Fordham");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("character device read & write");

4.2.2 编译非阻塞模式下的虚拟设备驱动

这里要注意,我们需要在QEMU+ARM的环境下插入内核模块,那么就要在QEMU+ARM的环境下编译模块,为了节省时间,可以先下载一个基础环境,如下:

https://github.com/figozhang/runninglinuxkernel_4.0

直接git clone这个环境下来,下载按照README安装,安装完后,编写刚才内核模块的Makefile,注意第一行BASEINCLUDE刚才下载下来的runninglinuxkernel_4.0,Makefile如下。

BASEINCLUDE ?= ~/tools/runninglinuxkernel_4.0                                 
 
mytest-objs  :=my_demodev.o
obj-m  :=mydemo.o
 
all :
    $(MAKE) -C      $(BASEINCLUDE)  M=$(PWD)        modules;
clean:
    $(MAKE)  -C $(BASEINCLUDE)  SUBDIRS=$(PWD) clean;
    rm -f *.ko;

写完之后,make,可以看到my_demo.ko

4.2.3  写用户层测试代码测试插入的内核模块

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define DEMO_DEV_NAME "/dev/my_demo_dev"

int main(){
    int fd; 
    int ret;
    size_t len;
    char message[] = "Testing the virtual FIFO device";
    char *read_buffer;
    len = sizeof(message);
    read_buffer = malloc(2*len);
    memset(read_buffer, 0, 2*len);

    fd = open(DEMO_DEV_NAME, O_RDWR | O_NONBLOCK);
    if (fd < 0){ 
        printf("open device %s failed\n", DEMO_DEV_NAME);
        return -1; 
    }   
    //First read the data
    ret = read(fd, read_buffer, 2*len);
    printf("read %d bytes\n", ret);
    printf("read buffer=%s\n", read_buffer);

    //Second, write to the device
    ret = write(fd, message, len);
    if (ret != len)
        printf("have write %d bytes\n", ret);

    //Third, write to the device
        ret = write(fd, message, len);
    if (ret != len)
        printf("have write %d bytes\n", ret);
    
    //First read the data
    ret = read(fd, read_buffer, 2*len);
    printf("read %d bytes\n", ret);
    printf("read buffer=%s\n", read_buffer);

    close(fd);
    return 0;
}

 编译:

arm-linux-gnueabi-gcc test_virtual.c -o test --static

得到一个可执行文件test,将这个文件还是同步到qemu的内核文件系统ext4.img中,然后执行:

Please press Enter to activate this console. 
/ # mount -t ext4 /dev/mmcblk0 /mnt/
EXT4-fs (mmcblk0): recovery complete
EXT4-fs (mmcblk0): mounted filesystem with ordered data mode. Opts: (null)
/ # cd mnt/
/mnt # ls
lost+found    mydemo.ko     test_virtual
/mnt # insmod mydemo.ko 
succeeded register char device: my_demo_dev
/mnt # ./test_virtual 
random: nonblocking pool is initialized
demodrv_open: major=10, minor=58
read -1 bytes
read buffer=
demodrv_write: actual_write =32, ppos=0
demodrv_write: actual_write =32, ppos=0
demodrv_read, actual_readed=64, pos=0
read 64 bytes
read buffer=Testing the virtual FIFO device
// mydemo.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/kfifo.h>

DEFINE_KFIFO(mydemo_fifo, char, 64);
#define DEMO_NAME "my_demo_dev"
static struct device *mydemodrv_device;
/*virtual FIFO device's buffer*/
static char *device_buffer;
#define MAX_DEVICE_BUFFER_SIZE 64

struct mydemo_device {
    const char *name;
    struct device *dev;
    wait_queue_head_t read_queue;
    wait_queue_head_t write_queue;
};

struct mydemo_private_data {
        struct mydemo_device *device;
        char name[64];
};


static int demodrv_open(struct inode *inode, struct file *file)
{
        int major = MAJOR(inode->i_rdev);
        int minor = MINOR(inode->i_rdev);

        printk("%s: major=%d, minor=%d\n", __func__, major, minor);

        return 0;
}

static int demodrv_release(struct inode *inode, struct file *file)
{
        return 0;
}

static ssize_t
demodrv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    struct mydemo_private_data *data = file->private_data;
        struct mydemo_device *device = data->device;
        int actual_readed;
    int ret;
    if (kfifo_is_empty(&mydemo_fifo)){
        if (file->f_flags & O_NONBLOCK)
            return -EAGAIN;
        printk("%s pid=%d, going to sleep\n",__func__, current->pid);
        ret = wait_event_interruptible(device->read_queue,
                !kfifo_is_empty(&mydemo_fifo));
        if (ret)
            return ret;
    }
    
    ret = kfifo_to_user(&mydemo_fifo, buf, count, &actual_readed);
    if (ret)
                return -EIO;
    if (!kfifo_is_full(&mydemo_fifo)){
                wake_up_interruptible(&device->write_queue);
        }

        printk("%s, actual_readed=%d, pos=%lld\n",__func__, actual_readed, *ppos);
        return actual_readed;
}
static ssize_t
demodrv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    struct mydemo_private_data *data = file->private_data;
        struct mydemo_device *device = data->device;
        unsigned int actual_write;
        int ret;
    if (kfifo_is_full(&mydemo_fifo)){
        if (file->f_flags & O_NONBLOCK)
            return -EAGAIN;
        printk("%s pid=%d, going to sleep\n",__func__, current->pid);
        ret = wait_event_interruptible(device->write_queue,
                  !kfifo_is_full(&mydemo_fifo));
        if (ret)
            return ret;
    }
        ret = kfifo_from_user(&mydemo_fifo, buf, count, &actual_write);
    if (ret)
                return -EIO;
    if (!kfifo_is_full(&mydemo_fifo)){
        wake_up_interruptible(&device->read_queue);
    }
        printk("%s: actual_write =%d, ppos=%lld\n", __func__, actual_write, *ppos);
        return actual_write;
}

static const struct file_operations demodrv_fops = {
        .owner = THIS_MODULE,
        .open = demodrv_open,
        .release = demodrv_release,
        .read = demodrv_read,
        .write = demodrv_write
};

static struct miscdevice mydemodrv_misc_device = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = DEMO_NAME,
        .fops = &demodrv_fops,
};

static int __init simple_char_init(void)
{
    struct mydemo_device *device;
        int ret;

        device_buffer = kmalloc(MAX_DEVICE_BUFFER_SIZE, GFP_KERNEL);
        if (!device_buffer)
                return -ENOMEM;

        ret = misc_register(&mydemodrv_misc_device);
        if (ret) {
                printk("failed register misc device\n");
                kfree(device_buffer);
                return ret;
        }

        mydemodrv_device = mydemodrv_misc_device.this_device;
        printk("succeeded register char device: %s\n", DEMO_NAME);
        init_waitqueue_head(&device->read_queue);
        init_waitqueue_head(&device->write_queue);
        return 0;
}

static void __exit simple_char_exit(void)
{
        printk("removing device\n");

        kfree(device_buffer);
        misc_deregister(&mydemodrv_misc_device);
}

module_init(simple_char_init);
module_exit(simple_char_exit);

MODULE_AUTHOR("Henry_Fordham");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("character device read & write");

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值