完善例子中的字符设备程序,使之满足以下功能: i.安装设备后从设备中读出字符串为自己学号的后3位; ii. 设备支持每次写入字符不超过1024个,超过部分被丢弃,并且能够保存最近一次修改前的rwbuf里的内容。 iii. 设备支持系统调用ioctl(int d, int req,…),共支持设置三种模式:a. 清除设备中写入的字符串; b. 从设备中读时,读出的是最近一次修改前的内容。 c. 从设备中读时,读出的是最新内容 iv. 设备关闭前不能被多次打开; v. 自己编写测试程序,验证以上功能 vi. 在上述基础上增加mmap接口,使其能够通过mmap读写rwbuf中的内容。给出驱动代码和测试代码例子
时间: 2024-02-12 14:08:06 浏览: 106
驱动代码:
```c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/mman.h>
#define DEV_NAME "mydev"
static int major;
static struct cdev my_dev;
static char *rwbuf;
static int rlen;
static int wlen;
struct mydev_ioctl_args {
int mode;
};
#define MYDEV_IOCTL_CLEAR 0
#define MYDEV_IOCTL_READ_OLD 1
#define MYDEV_IOCTL_READ_NEW 2
static long mydev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct mydev_ioctl_args args;
if (copy_from_user(&args, (void *)arg, sizeof(args))) {
return -EFAULT;
}
switch (args.mode) {
case MYDEV_IOCTL_CLEAR:
memset(rwbuf, 0, rlen);
wlen = 0;
break;
case MYDEV_IOCTL_READ_OLD:
wlen = 0;
break;
case MYDEV_IOCTL_READ_NEW:
break;
default:
return -EINVAL;
}
return 0;
}
static int mydev_mmap(struct file *filp, struct vm_area_struct *vma)
{
unsigned long pfn_start, pfn_end;
unsigned long start = vma->vm_start;
unsigned long size = vma->vm_end - vma->vm_start;
if (size > rlen) {
return -EINVAL;
}
pfn_start = virt_to_phys((void *)rwbuf) >> PAGE_SHIFT;
pfn_end = virt_to_phys((void *)(rwbuf + rlen - 1)) >> PAGE_SHIFT;
while (size > 0) {
if (remap_pfn_range(vma, start, pfn_start, PAGE_SIZE, vma->vm_page_prot)) {
return -EAGAIN;
}
start += PAGE_SIZE;
pfn_start++;
size -= PAGE_SIZE;
}
return 0;
}
static ssize_t mydev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
int len;
if (*f_pos >= rlen) {
return 0;
}
if (count > rlen - *f_pos) {
count = rlen - *f_pos;
}
if (wlen == 0) {
len = snprintf(rwbuf, rlen, "123");
wlen = len;
}
if (copy_to_user(buf, rwbuf + *f_pos, count)) {
return -EFAULT;
}
*f_pos += count;
return count;
}
static ssize_t mydev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
int len;
if (count > 1024) {
count = 1024;
}
if (*f_pos + count > rlen) {
return -EINVAL;
}
len = copy_from_user(rwbuf + *f_pos, buf, count);
if (len > 0) {
return -EFAULT;
}
*f_pos += count;
if (*f_pos > wlen) {
wlen = *f_pos;
}
return count;
}
static int mydev_open(struct inode *inode, struct file *filp)
{
if (filp->f_flags & O_EXCL) {
return -EBUSY;
}
return 0;
}
static int mydev_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations mydev_fops = {
.owner = THIS_MODULE,
.read = mydev_read,
.write = mydev_write,
.unlocked_ioctl = mydev_ioctl,
.mmap = mydev_mmap,
.open = mydev_open,
.release = mydev_release,
};
static int __init mydev_init(void)
{
dev_t dev;
int ret;
ret = alloc_chrdev_region(&dev, 0, 1, DEV_NAME);
if (ret < 0) {
return ret;
}
major = MAJOR(dev);
cdev_init(&my_dev, &mydev_fops);
my_dev.owner = THIS_MODULE;
ret = cdev_add(&my_dev, dev, 1);
if (ret < 0) {
unregister_chrdev_region(MKDEV(major, 0), 1);
return ret;
}
rwbuf = kzalloc(1024, GFP_KERNEL);
if (!rwbuf) {
cdev_del(&my_dev);
unregister_chrdev_region(MKDEV(major, 0), 1);
return -ENOMEM;
}
rlen = 1024;
return 0;
}
static void __exit mydev_exit(void)
{
kfree(rwbuf);
cdev_del(&my_dev);
unregister_chrdev_region(MKDEV(major, 0), 1);
}
module_init(mydev_init);
module_exit(mydev_exit);
MODULE_LICENSE("GPL");
```
测试代码:
```c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#define DEV_NAME "/dev/mydev"
#define MYDEV_IOCTL_CLEAR 0
#define MYDEV_IOCTL_READ_OLD 1
#define MYDEV_IOCTL_READ_NEW 2
int main()
{
int fd;
char buf[1024];
int len;
char *ptr;
int i;
fd = open(DEV_NAME, O_RDWR);
if (fd < 0) {
perror("open");
return -1;
}
len = read(fd, buf, sizeof(buf));
if (len < 0) {
perror("read");
return -1;
}
printf("read: %.*s\n", len, buf);
len = write(fd, "hello", 5);
if (len < 0) {
perror("write");
return -1;
}
len = read(fd, buf, sizeof(buf));
if (len < 0) {
perror("read");
return -1;
}
printf("read: %.*s\n", len, buf);
len = write(fd, "world", 5);
if (len < 0) {
perror("write");
return -1;
}
len = read(fd, buf, sizeof(buf));
if (len < 0) {
perror("read");
return -1;
}
printf("read: %.*s\n", len, buf);
len = ioctl(fd, MYDEV_IOCTL_READ_OLD);
if (len < 0) {
perror("ioctl");
return -1;
}
len = read(fd, buf, sizeof(buf));
if (len < 0) {
perror("read");
return -1;
}
printf("read: %.*s\n", len, buf);
ptr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) {
perror("mmap");
return -1;
}
printf("mmap: %.*s\n", 3, ptr);
memcpy(ptr, "abc", 3);
len = read(fd, buf, sizeof(buf));
if (len < 0) {
perror("read");
return -1;
}
printf("read: %.*s\n", len, buf);
munmap(ptr, 1024);
len = ioctl(fd, MYDEV_IOCTL_READ_NEW);
if (len < 0) {
perror("ioctl");
return -1;
}
len = read(fd, buf, sizeof(buf));
if (len < 0) {
perror("read");
return -1;
}
printf("read: %.*s\n", len, buf);
len = ioctl(fd, MYDEV_IOCTL_CLEAR);
if (len < 0) {
perror("ioctl");
return -1;
}
len = read(fd, buf, sizeof(buf));
if (len < 0) {
perror("read");
return -1;
}
printf("read: %.*s\n", len, buf);
close(fd);
return 0;
}
```
阅读全文