在驱动里用数据缓冲区代替文本文件的数据存取.
实现代码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");