【问题标题】:Linux Device Driver -- file operations not workingLinux 设备驱动程序——文件操作不起作用
【发布时间】:2021-04-09 06:09:39
【问题描述】:

我最近更新了我的内核,我不再能够使用以前编写的设备驱动程序。我的驱动程序的 initexit 函数工作正常,并将消息记录到内核日志中。但是,我不再能够 writereadioctlopenrelease 文件。这些函数不会将任何内容打印到日志文件中。我正在针对 linux-headers-5.4.79-v7l+ 进行编译。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/slab.h>


MODULE_LICENSE("Dual BSD/GPL");

#define DEVICE_NAME "device"

static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
static long int device_ioctl(struct file *, unsigned int, unsigned long);

static struct file_operations fops =
{
    owner: THIS_MODULE,
    read: device_read,
    write:device_write,
    unlocked_ioctl: device_ioctl,
    open: device_open,
    release: device_release

};

struct cdev *device_cdev;
dev_t deviceNumbers;

static int init(void)
{
    int ret = alloc_chrdev_region(&deviceNumbers, 0, 1, DEVICE_NAME);

    if (ret < 0) {
        printk(KERN_ALERT "Error registering: %d", ret);
        return -1;
    }

    device_cdev = cdev_alloc();

    cdev_init(device_cdev, &fops);

    ret = cdev_add(device_cdev, deviceNumbers, 1);

    printk(KERN_INFO "Device initialized");

    return 0;
}

static void cleanup(void)
{
    unregister_chrdev_region(deviceNumbers, 1);

    cdev_del(device_cdev);

    printk(KERN_INFO "Device unloaded");
}

static int device_open(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "Device open");
    return 0;
}


static int device_release(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "Device released");
    return 0;
}


static ssize_t device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
    printk(KERN_INFO "Device write");
    return 0;
}

static ssize_t device_read(struct file *filp, char *buff, size_t len, loff_t * off)
{
    printk(KERN_INFO "Device read");
    return 0;
}

static long int device_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param)
{
    printk(KERN_INFO "Device IOCTL");
    return 0;
}


module_init(init);
module_exit(cleanup);

【问题讨论】:

    标签: c linux linux-kernel linux-device-driver


    【解决方案1】:

    您应该在打印的末尾添加终止“\n”以强制将它们刷新到内核日志缓冲区中。这是您的模块以及一些增强建议:

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/kdev_t.h>
    #include <linux/cdev.h>
    #include <linux/uaccess.h>
    #include <linux/slab.h>
    
    
    MODULE_LICENSE("Dual BSD/GPL");
    
    #define DEVICE_NAME "device"
    
    static int device_open(struct inode *, struct file *);
    static int device_release(struct inode *, struct file *);
    static ssize_t device_read(struct file *, char *, size_t, loff_t *);
    static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
    static long int device_ioctl(struct file *, unsigned int, unsigned long);
    
    static const struct file_operations fops =
    {
        .owner= THIS_MODULE,
        .read= device_read,
        .write=device_write,
        .unlocked_ioctl= device_ioctl,
        .open= device_open,
        .release= device_release
    
    };
    
    struct cdev *device_cdev;
    dev_t deviceNumbers;
    
    static  int __init init(void)  // <------ Add __init keyword for kernel cleanups
    {
        // This returns the major number chosen dynamically in deviceNumbers
        int ret = alloc_chrdev_region(&deviceNumbers, 0, 1, DEVICE_NAME);
    
        if (ret < 0) {
            printk(KERN_ALERT "Error registering: %d\n", ret);
            return -1;
        }
    
        device_cdev = cdev_alloc();
    
        cdev_init(device_cdev, &fops);
    
        ret = cdev_add(device_cdev, deviceNumbers, 1);
    
        printk(KERN_INFO "Device initialized (major number is %d)\n", MAJOR(deviceNumbers));
    
        return 0;
    }
    
    static void __exit cleanup(void)  // <------ Add __exit keyword for kernel cleanups
    {
        unregister_chrdev_region(deviceNumbers, 1);
    
        cdev_del(device_cdev);
    
        printk(KERN_INFO "Device unloaded\n");
    }
    
    static int device_open(struct inode *inode, struct file *file)
    {
        printk(KERN_INFO "Device open\n");
        return 0;
    }
    
    
    static int device_release(struct inode *inode, struct file *file)
    {
        printk(KERN_INFO "Device released\n");
        return 0;
    }
    
    
    static ssize_t device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
    {
        printk(KERN_INFO "Device write\n");
        return len;  // <-------------- To stop the write
    }
    
    static ssize_t device_read(struct file *filp, char *buff, size_t len, loff_t * off)
    {
        printk(KERN_INFO "Device read\n");
        return len; // <-------------- To stop the read
    }
    
    static long int device_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param)
    {
        printk(KERN_INFO "Device IOCTL\n");
        return 0;
    }
    
    
    module_init(init);
    module_exit(cleanup);
    

    检查kernel log level 的当前设置。

    $ cat /proc/sys/kernel/printk
    4   4   1   7
    

    在前面,第一列指定只打印日志级别低于 4 的消息。

    printk()接受的值是:

       KERN_EMERG             0        System is unusable
       KERN_ALERT             1        Action must be taken immediately
       KERN_CRIT              2        Critical conditions
       KERN_ERR               3        Error conditions
       KERN_WARNING           4        Warning conditions
       KERN_NOTICE            5        Normal but significant condition
       KERN_INFO              6        Informational
       KERN_DEBUG             7        Debug-level messages
    

    因此,KERN_INFO 级别为 6,大于 4!

    我们修改配置:

    $ sudo sh -c "echo 7 4 1 7 > /proc/sys/kernel/printk"
    $ cat /proc/sys/kernel/printk
    7   4   1   7
    

    我根据建议的修改构建了您的模块,并在 Linux 5.4.0-58 上进行了尝试:

    $ unname -a Linux xxxx 5.4.0-58-generic #64-Ubuntu SMP Wed Dec 9 08:16:25 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux $ sudo insmod ./device.ko $ dmesg [...] [ 7244.516706] 设备已初始化(主要数字为 235) $ lsmod 使用的模块大小 设备 16384 0 [...] $ 猫 /proc/设备 字符设备: [...] 235设备 $ sudo mknod /dev/device c 235 0 $ ls -l /dev/设备 crw-r--r-- 1 根根 235, 0 janv。 3 10:33 /dev/设备 $ sudo sh -c "echo foo > /dev/device" $ dmesg [...] [7244.516706]设备初始化(主要数字为235) [ 7311.507652] 设备打开 [7311.507672] 设备写入 [7311.507677] 设备发布 $ sudo rmmod 设备 $ dmesg [...] [7244.516706]设备初始化(主要数字为235) [7311.507652]设备打开 [7311.507672]设备写入 [7311.507677]设备发布 [ 7361.523964] 设备已卸载 $ sudo rm /dev/设备

    【讨论】:

    • 感谢@Rachid 的帮助和编辑。最终,新内核分配了一个不同的主编号,而我在创建节点时没有分配正确的编号。再次感谢您。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-07
    • 2020-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多