linux字符设备驱动+fops应用测试程序

linux字符设备驱动

mychar.c,支持fops和/proc下的proc_fops接口:

#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/fs.h>  
#include <linux/cdev.h>  
#include <linux/uaccess.h>  
#include <linux/slab.h>  // 包含这个头文件来使用 kmalloc 和 kfree
#include <linux/proc_fs.h>  
#include <linux/seq_file.h>

#define DEVICE_NAME "mychar"  
#define CLASS_NAME  "mychar_class"  
#define BUFFER_SIZE 1024  
  
static int major;  
static struct cdev *my_cdev;  
static char *buffer;  
static int buffer_size = BUFFER_SIZE;  
  
static int mychar_open(struct inode *inode, struct file *file) {  
    printk(KERN_INFO "mychar_open\n");  
    return 0;  
}  
  
static ssize_t mychar_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {  
    unsigned long p = *ppos;  
    unsigned int copy;  
  
    if (p >= buffer_size)  
        return 0;  
  
    if (count > buffer_size - p)  
        count = buffer_size - p;  
  
    if (copy_to_user(buf, buffer + p, count))  
        return -EFAULT;  
  
    *ppos += count;  
    return count;  
}  
  
static ssize_t mychar_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {  
    unsigned long p = *ppos;  
    unsigned int copy;  
  
    if (p >= buffer_size)  
        return 0;  
  
    if (count > buffer_size - p)  
        count = buffer_size - p;  
  
    if (copy_from_user(buffer + p, buf, count))  
        return -EFAULT;  
  
    *ppos += count;  
    return count;  
}  
  
static loff_t mychar_llseek(struct file *file, loff_t offset, int whence) {  
    loff_t newpos = file->f_pos;  
  
    switch (whence) {  
    case SEEK_SET:  
        newpos = offset;  
        break;  
    case SEEK_CUR:  
        newpos += offset;  
        break;  
    case SEEK_END:  
        newpos = buffer_size + offset;  
        break;  
    default:  
        return -EINVAL;  
    }  
  
    if (newpos < 0 || newpos > buffer_size)  
        return -EINVAL;  
  
    file->f_pos = newpos;  
    return newpos;  
}  
  
static struct file_operations mychar_fops = {  
    .owner = THIS_MODULE,  
    .open = mychar_open,  
    .read = mychar_read,  
    .write = mychar_write,  
//    .llseek = mychar_llseek,  
};  
  
// 假设你有一个字符设备的私有数据结构
struct mychar_dev {
    // ... 设备特定的数据 ...
    char buffer[4096]; // 示例缓冲区
    size_t buffer_pos; // 示例缓冲区中的当前位置
};
static struct mychar_dev mychar_device;

// /proc 文件的读取处理函数  
static ssize_t mychar_proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {  
    size_t len;  
    if (*ppos >= sizeof(mychar_device.buffer) || count == 0)  
        return 0;  
  
    len = min(count, sizeof(mychar_device.buffer) - (size_t)*ppos);  
    if (copy_to_user(buf, mychar_device.buffer + *ppos, len))  
        return -EFAULT;  
  
    *ppos += len;  
    return len;  
}  
  
// /proc 文件的写入处理函数  
static ssize_t mychar_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {  
    size_t len;  
    if (*ppos >= sizeof(mychar_device.buffer) || count == 0)  
        return 0;  
  
    len = min(count, sizeof(mychar_device.buffer) - (size_t)*ppos);  
    if (copy_from_user(mychar_device.buffer + *ppos, buf, len))  
        return -EFAULT;  
  
    // 更新设备缓冲区的位置(如果需要的话)  
    // 这里我们简单地增加偏移量,但你可以根据需要进行更复杂的处理  
    *ppos += len;  
    // 注意:在实际的设备驱动中,你可能需要同步缓冲区到硬件或执行其他操作  
  
    return len;  
}  
  
// /proc 文件的 file_operations 结构体  
static const struct file_operations mychar_proc_fops = {  
    .owner = THIS_MODULE,  
    .read = mychar_proc_read,  
    .write = mychar_proc_write,  
    // 注意:没有 open 和 release,因为 /proc 文件系统的处理是自动的  
    // 也没有 llseek,因为 /proc 文件通常不支持随机访问  
};  

static struct class *mychar_class;
static struct proc_dir_entry *mychar_proc_entry;

static int __init mychar_init(void) {  
    int err;  
    dev_t devno = MKDEV(major, 0);  
  
    if (major) {  
        my_cdev = cdev_alloc();  
        my_cdev->owner = THIS_MODULE;  
        my_cdev->ops = &mychar_fops;  
        my_cdev->dev = devno;  
        err = cdev_add(my_cdev, MKDEV(major, 0), 1);  
  
        if (err) {  
            printk(KERN_NOTICE "Error %d adding mychar", err);  
            return err;  
        }  
    } else {  
        err = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);  
        if (err < 0) {  
            printk(KERN_NOTICE "Error %d registering character device", err);  
            return err;  
        }  
        major = MAJOR(devno);  
        my_cdev = cdev_alloc();  
        my_cdev->owner = THIS_MODULE;  
        my_cdev->ops = &mychar_fops;  
        my_cdev->dev = devno;  
        err = cdev_add(my_cdev, MKDEV(major, 0), 1);
	if (err) {  
            unregister_chrdev_region(MKDEV(major, 0), 1);  
            printk(KERN_NOTICE "Error %d adding mychar", err);  
            return err;  
        }  
    }  
  
    buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);  
    if (!buffer) {  
        cdev_del(my_cdev);  
        unregister_chrdev_region(MKDEV(major, 0), 1);  
        printk(KERN_NOTICE "Failed to allocate device buffer\n");  
        return -ENOMEM;  
    }  
  
    // 创建设备类  
    mychar_class = class_create(THIS_MODULE, CLASS_NAME);  
    if (IS_ERR(mychar_class)) {  
        printk(KERN_WARNING "Failed to register device class\n");  
        cdev_del(my_cdev);  
        unregister_chrdev_region(MKDEV(major, 0), 1);  
        kfree(buffer);  
        return PTR_ERR(mychar_class);  
    }  
  
    // 创建设备  
    device_create(mychar_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME);  

    // 创建 /proc 文件
    mychar_proc_entry = proc_create("mychar", 0666, NULL, &mychar_proc_fops);  
    if (!mychar_proc_entry) {  
        pr_err("Failed to create /proc/mychar\n");  
        return -ENOMEM; // 或者其他适当的错误码  
    }  


    printk(KERN_INFO "mychar device registered with major number %d\n", major);  
    return 0;  
}  
  
static void __exit mychar_exit(void) {  
    device_destroy(mychar_class, MKDEV(major, 0));  
    class_destroy(mychar_class);  
    cdev_del(my_cdev);  
    unregister_chrdev_region(MKDEV(major, 0), 1);  
    kfree(buffer);  
    if (mychar_proc_entry) {
        remove_proc_entry("mychar", NULL);
    }
    printk(KERN_INFO "mychar device unregistered\n");  
}  
  
module_init(mychar_init);  
module_exit(mychar_exit);  
  
MODULE_LICENSE("GPL");  
MODULE_AUTHOR("Your Name");  
MODULE_DESCRIPTION("A simple character device driver");

Makefile:


KERN_DIR = /home/book/demo/linux-5.4

obj-m += mychar.o  
  
all:  
	make -C $(KERN_DIR) M=`pwd` modules
  
clean:  
	make -C $(KERN_DIR) M=`pwd` modules clean

lseek.c应用:

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <fcntl.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
  
#define BUFFER_SIZE 2048  // 定义缓冲区大小  
  
int main(int argc, char *argv[]) {  
    int fd;  
    ssize_t ret;  
    off_t pos;  
    char buffer[BUFFER_SIZE];  
  
    // 假设 fd 已经被正确打开  
    fd = open(argv[1], O_RDONLY);  
    if (fd < 0) {  
        perror("open failed");  
        return EXIT_FAILURE;  
    }  
  
    // 首次读取(可能不需要,但根据原始代码保留)  
    ret = read(fd, buffer, 0);  // 这里传递 0 字节,实际上不会读取任何内容  
    if (ret != 0) {  
        perror("Unexpected read return");  
        close(fd);  
        return EXIT_FAILURE;  
    }  
    printf("***********read ret=%d\n", ret);
  
    // 重置文件位置  
    pos = lseek(fd, 0, SEEK_SET);  
    if (pos < 0) {  
        perror("lseek failed");  
        close(fd);  
        return EXIT_FAILURE;  
    }  
    printf("***********lseek pos=%d\n", (int)pos);
  
    // 清理缓冲区(虽然在这个场景下可能不是必需的)  
    memset(buffer, 0, BUFFER_SIZE);  
  
    // 读取文件内容到缓冲区  
    ret = read(fd, buffer, BUFFER_SIZE);  
    if (ret < 0) {  
        perror("read failed");  
        close(fd);  
        return EXIT_FAILURE;  
    }  
    printf("***********read ret=%d\n", ret);
  
    // 检查是否读取了足够的数据来检查换行符  
    if (ret < (ssize_t)(BUFFER_SIZE >> 1)) {  
        char *bptr = buffer;  
  
        // 查找换行符  
        while (*bptr && *bptr != '\n') {  
            bptr++;  
        }  
  
        if (*bptr == '\n') {  
            const off_t offset = 2 + (bptr - buffer);  
  	    printf("***********offset=%d\n", (int)offset);
            // 跳转到换行符后的位置  
            pos = lseek(fd, offset, SEEK_SET);  
            if (pos == (off_t)-1) {  
                perror("lseek failed");  
                close(fd);  
                return EXIT_FAILURE;  
            }  
	    printf("***********lseek pos=%d\n", (int)pos);
  
            // 从新位置读取(如果需要)  
            // 注意:这里可能需要额外的逻辑来决定是否再次读取  
            // 这里仅作为示例,不实际读取  
            ret = read(fd, buffer, BUFFER_SIZE);
	    printf("***********read ret=%d\n", ret);  
            // (void)ret;  
        }  
    }  

	off_t dec;

	pos = lseek(fd, 0, SEEK_END);
	printf("***********lseek pos=%d\n", (int)pos);
	if (pos < 0)
		return EXIT_FAILURE;
	dec = pos / 13;
	printf("***********dec=%d\n", (int)dec);
	if (dec < 1)
		dec = 1;
	while (pos > 0) {
		off_t seek_pos;

		seek_pos = lseek(fd, pos, SEEK_SET);
		printf("***********lseek seek_pos=%d\n", (int)seek_pos);
		if (seek_pos < 0)
			break;
		ret = read(fd, buffer, 5);
		printf("***********read ret=%d\n", ret);
		(void)ret;

		if (dec > pos)
			dec = pos;
		pos -= dec;
	}


    // 关闭文件描述符  
    close(fd);  
  
    return EXIT_SUCCESS;  
}


scandir.c应用:

#include <stdio.h>
#include <dirent.h>

int  main(int argc,char* argv[],char* envp[]) {
    struct dirent **namelist;
    printf("argc=%d, argv[0]=%s, argv[1]=%s\n", argc, argv[0], argv[1]);
    int numEntries = scandir(argv[1], &namelist, NULL, alphasort);
    if (numEntries < 0) {
        perror("scandir");
    } else {
        for (int i = 0; i < numEntries; i++) {
            printf("File %d: %s\n", i, namelist[i]->d_name);
        }
    }

    // 释放动态分配的内存
    for (int i = 0; i < numEntries; i++) {
        free(namelist[i]);
    }
    free(namelist);

    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值