22 虚拟文件的实现

在驱动里用数据缓冲区代替文本文件的数据存取.
实现代码test.c:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

#define MYMA  1234
#define MYMI  7788
#define COUNT 1

dev_t devid;
struct cdev mycdev;
u8 *data; //驱动数据缓冲区
int dlen = 26; //驱动数据缓冲区长度

//当用户进程读时,应把驱动里的缓冲区数据复制给用户进程
ssize_t myread(struct file *fl, char __user *buf, size_t len, loff_t *off)
{
    int len_copy, ret;

// fl->f_pos表示当前文件描述符对文件的偏移, len表示用户进程想要读的大小

    if ((fl->f_pos + len) > strlen(data)) //如果剩下没读的数据长度少于len,则只复制出剩下没读部分
            len_copy = strlen(data) - fl->f_pos;
    else
            len_copy = len; //如果剩下的数据长度超出len,则本次复制len字节

    ret = copy_to_user(buf, data+fl->f_pos, len_copy);

    //内容复制后,需要改变文件描述符的位置偏移
    *off += len_copy - ret;  //在read/write函数里必须通过off来改变
    return len_copy - ret;
}

//当用户进程write时,驱动里应把用户进程的数据存放起来
ssize_t mywrite(struct file *fl, const char __user *buf, size_t len, loff_t *off)
{
    int len_copy, ret;

    if ((fl->f_pos + len) > dlen) //如果要复制的内容超出数据缓冲区的大小      
        len_copy = dlen - fl->f_pos;  //只复制剩下空间大小的内容
    else
        len_copy = len;

    ret = copy_from_user(data+ fl->f_pos, buf, len_copy);

    *off += len_copy - ret;

    return len_copy - ret;
}

//当用户进程lseek(fd, 54, SEEK_SET)时触发此函数
loff_t myllseek(struct file *fl, loff_t offset, int whence)
{
    //除了read/write函数,可以通过fl->f_pos来改变文件描述符的偏移
    u8 *tmp;
    int len;

    switch (whence)
    {
    case SEEK_SET:
        if (offset < dlen)
            fl->f_pos = offset;
        break;

    case SEEK_CUR:
        if ((fl->f_pos + offset) < dlen)
            fl->f_pos += offset;
        break;

    case SEEK_END:
        if (0 == offset)
            fl->f_pos = dlen - 1; //数据缓冲区的最后位置 
        else if (offset > 0)
        {
            //表示需要把缓冲区变为(原大小+offset)
            len = dlen;
            dlen += offset;

            tmp = kzalloc(dlen, GFP_KERNEL); //分配出新大小的缓冲区
            memcpy(tmp, data, len); //把原缓冲区上的数据复制到新缓冲区
            kfree(data); //回收原空间    
            data = tmp; //取代原缓冲区

            fl->f_pos = dlen - 1;
        }
        break;
    }
    return fl->f_pos;
}


struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = myread,
    .write = mywrite,
    .llseek = myllseek, 
};

static int __init test_init(void)
{
    int ret, i;

    devid = MKDEV(MYMA, MYMI);
    ret = register_chrdev_region(devid, COUNT, "mydev");
    if (ret < 0)
        goto err0;

    cdev_init(&mycdev, &fops);
    mycdev.owner = THIS_MODULE;
    ret = cdev_add(&mycdev, devid, COUNT);
    if (ret < 0)
        goto err1;

    data = kzalloc(dlen, GFP_KERNEL); //申请数据缓冲区, 此缓冲区用于与用户进程数据交互.
    if (NULL == data)
        goto err2;

    for (i = 0; i < 26; i++)
        data[i] = 'A' + i;

    return 0;
err2:
    cdev_del(&mycdev);
err1:
    unregister_chrdev_region(devid, COUNT);
err0:
    return ret;
}

static void __exit test_exit(void)
{
    unregister_chrdev_region(devid, COUNT);
    cdev_del(&mycdev);
    kfree(data);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

app.c:


#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>


int main(void)
{
    int fd = open("/dev/mydev", O_RDWR);
    int ret;
    char ch;

    if (fd < 0)
        return 1;

    lseek(fd, 2, SEEK_SET);
    while (1)   
    {
        ret = read(fd, &ch, 1);
        if (ret <= 0)
            break;
        printf("%c\n", ch);
    }

    lseek(fd, 100, SEEK_END);
    lseek(fd, 26, SEEK_SET);

    write(fd, "what what what", 15);
    lseek(fd, 26, SEEK_SET);
    printf("################################\n");
    while (1)   
    {
        ret = read(fd, &ch, 1);
        if (ret <= 0)
        {
            perror("read");
            break;
        }
        printf("%c\n", ch);
    }


    close(fd);
    return 0;
}

/
一个驱动支持多个虚拟文件的实现
test.c:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

#define MYMA  1234
#define MYMI  7788
#define COUNT 3 //三个设备号,每个设备号对应一个设备文件和一个数据缓冲区

dev_t devid;
struct cdev mycdev;
u8 *data[COUNT]; //驱动数据缓冲区
int dlen = 1024; //驱动数据缓冲区长度

ssize_t myread(struct file *fl, char __user *buf, size_t len, loff_t *off)
{
    int mi = MINOR(fl->f_path.dentry->d_inode->i_rdev);
    int len_copy, ret, n;

    n = mi - MYMI;
    if (n >= COUNT)
        return -ENODEV;

    if ((fl->f_pos + len) > strlen(data[n])) 
            len_copy = strlen(data[n]) - fl->f_pos;
    else
            len_copy = len; 

    ret = copy_to_user(buf, data[n]+fl->f_pos, len_copy);

    *off += len_copy - ret; 
    return len_copy - ret;
}

ssize_t mywrite(struct file *fl, const char __user *buf, size_t len, loff_t *off)
{
    int mi = MINOR(fl->f_path.dentry->d_inode->i_rdev);
    int len_copy, ret, n;

    n = mi - MYMI;
    if (n >= COUNT)
        return -ENODEV;
    if ((fl->f_pos + len) > dlen) 
        len_copy = dlen - fl->f_pos; 
    else
        len_copy = len;

    ret = copy_from_user(data[n] + fl->f_pos, buf, len_copy);

    *off += len_copy - ret;

    return len_copy - ret;
}

struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = myread,
    .write = mywrite,
};

static int __init test_init(void)
{
    int ret, i, j;

    devid = MKDEV(MYMA, MYMI);
    ret = register_chrdev_region(devid, COUNT, "mydev");
    if (ret < 0)
        goto err0;

    cdev_init(&mycdev, &fops);
    mycdev.owner = THIS_MODULE;
    ret = cdev_add(&mycdev, devid, COUNT);
    if (ret < 0)
        goto err1;

    for (i = 0; i < COUNT; i++)
    {
        data[i] = kzalloc(dlen, GFP_KERNEL); 
        for (j = 0; j < 26; j++)
            data[i][j] = 'A' + j;
    }

    return 0;
err1:
    unregister_chrdev_region(devid, COUNT);
err0:
    return ret;
}

static void __exit test_exit(void)
{
    int i;

    unregister_chrdev_region(devid, COUNT);
    cdev_del(&mycdev);

    for (i = 0; i < COUNT; i++)
        kfree(data[i]);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值