简单字符设备模块中的错误

时间:2018-09-01 18:41:15

标签: linux linux-device-driver device-driver

我正在尝试编写一个简单的字符设备内核模块,该模块在内核空间中分配100字节的内存,并具有读取,写入和查找的功能,该模块可对分配的100字节的内核内存进行操作。我尝试将文本“ Hello World”回显到其相应的设备文件中,但是收到“ Invalid Argument”错误和dmesg警告。

设备驱动程序c文件名为“ kernelbuffer.c”

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h>   /* printk() */
#include <linux/slab.h>     /* kmalloc() */
#include <linux/fs.h>       /* everything... */
#include <linux/errno.h>    /* error codes */
#include <linux/types.h>    /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h>    /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>    /* copy_*_user */

static int major;

struct buffer_dev{
    char data[100];
    struct semaphore sem;
    struct cdev cdev;
};

struct buffer_dev *buff_device;

static void buffer_open(struct inode *inode, struct file *filep){
    struct buffer_dev *dev;
    printk(KERN_NOTICE "Opening buffer.\n");
    dev = container_of(inode->i_cdev, struct buffer_dev, cdev);
    filep->private_data = dev;
    printk(KERN_NOTICE "Buffer opened.\n");
    return 0;
}

ssize_t buffer_read(struct file *filep, char __user *buf, size_t count,
                loff_t *f_pos)
{
    printk(KERN_NOTICE "Reading from buffer.\n");
    struct buffer_dev *dev = filep->private_data;
    ssize_t retval = 0;
    if (down_interruptible(&dev->sem))
        return -ERESTARTSYS;
    if (*f_pos >= 100)
        goto out;
    if (*f_pos + count > 100)
        count = 100 - *f_pos;
    printk(KERN_NOTICE "First stage fine.\n");
    if(copy_to_user(buf, dev->data + *f_pos, count)){
        retval = -EFAULT;
        goto out;
    }
    *f_pos += count;
    retval = count;

    out:
        up(&dev->sem);
    return retval;
}

ssize_t buffer_write(struct file *filep, char __user *buf, size_t count,
                 loff_t *f_pos)
{
    printk(KERN_NOTICE "Writing to buffer.\n");
    struct buffer_dev *dev = filep->private_data;
    ssize_t retval = -ENOMEM; /* value used in "goto out" statements */

    if (down_interruptible(&dev->sem))
        return -ERESTARTSYS;

    if (*f_pos >= 100)
        goto out;
    if (*f_pos + count > 100)
        count = 100 - *f_pos;

    if (copy_from_user(dev->data + *f_pos, buf, count)) {
        retval = -EFAULT;
        goto out;
    }
    printk(KERN_NOTICE "Write success.\n");
    *f_pos += count;
    retval = count;

    out:
        up(&dev->sem);
    return retval;

}

loff_t buffer_llseek(struct file *filp, loff_t off, int whence)
{
    struct buffer_dev *dev = filp->private_data;
    loff_t newpos;

    switch(whence) {
        case 0: /* SEEK_SET */
            newpos = off;
            break;

        case 1: /* SEEK_CUR */
             newpos = filp->f_pos + off;
             break;

        case 2: /* SEEK_END */
            newpos = 100 + off;
            break;

        default: /* can't happen */
            return -EINVAL;
     }
    if (newpos < 0 || newpos >= 100) return -EINVAL;
    filp->f_pos = newpos;
    return newpos;
}

int buffer_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static struct file_operations buffer_fops = {
    .owner =    THIS_MODULE,
    .llseek =   buffer_llseek,
    .read =     buffer_read,
    .write =    buffer_write,
    .open =     buffer_open,
    .release =  buffer_release,
};

static void setup_cdev(struct buffer_dev *dev){
    int err, devno = MKDEV(major, 0);

    cdev_init(&dev->cdev, &buffer_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &buffer_fops;
    err = cdev_add (&dev->cdev, devno, 1);

    if (err)
        printk(KERN_NOTICE "Error %d adding device\n", err);

}

void cleanup_module_buffer(void)
{
    printk(KERN_NOTICE "Removing buffer device.\n");
    dev_t devno = MKDEV(major, 0);
    if(buff_device){
        cdev_del(&buff_device->cdev);
        kfree(buff_device);
    }
    unregister_chrdev_region(devno,1);
}

int module_init_buffer(void)
{
    printk(KERN_NOTICE "Initializing buffer device.\n");
    int result, i;
    dev_t dev = 0;

    result = alloc_chrdev_region(&dev, 0, 1,
            "buffer");
    major = MAJOR(dev);
    if (result < 0) {
        printk(KERN_WARNING "Can't get major %d\n", major);
        return result;
    }

    buff_device = kmalloc(sizeof(struct buffer_dev), GFP_KERNEL);
    if (!buff_device) {
        result = -ENOMEM;
        goto fail;  /* Make this more graceful */
    }
    memset(buff_device, 0, sizeof(struct buffer_dev));

    sema_init(&(buff_device->sem), 1);
    setup_cdev(&buff_device);

    return 0; /* succeed */

  fail:
      cleanup_module_buffer();
  return result;
}


module_init(module_init_buffer);
module_exit(cleanup_module_buffer);

MODULE_AUTHOR("Gautam Ramakrishnan");
MODULE_LICENSE("GPL");

用于编译它的makefile是:

obj-m := kernelbuffer.o 

KDIR  := /lib/modules/$(shell uname -r)/build

all:    $(MAKE) -C $(KDIR) M=$(shell pwd) modules

clean:
    $(MAKE) -C $(KDIR) M=$(shell pwd) clean
    $(RM) Module.markers modules.order

我在kernelbuffer.ko文件上成功运行了insmod。

在运行cat /proc/devices时 最多显示247。

然后我执行sudo mknod /dev/kbuffer c 247 0以创建相应的驱动程序文件。

以超级用户身份运行echo "Hello World > /dev/kbuffer时,我得到回复bash: /dev/kbuffer: Invalid argument

在运行dmesg时,我得到以下输出:

[  583.814364] Opening buffer.
[  583.814367] Buffer opened.
[  583.814369] ------------[ cut here ]------------
[  583.814392] WARNING: CPU: 0 PID: 2673 at /build/linux-dcxD3m/linux-4.4.0/fs/namei.c:3206 path_openat+0xb19/0x1330()
[  583.814393] Modules linked in: kernelbuffer(OE) nls_utf8 isofs vboxsf(OE) crct10dif_pclmul crc32_pclmul snd_intel8x0 snd_ac97_codec ac97_bus aesni_intel joydev aes_x86_64 snd_pcm lrw gf128mul snd_seq_midi glue_helper ablk_helper snd_seq_midi_event input_leds vboxvideo(OE) cryptd snd_rawmidi ttm snd_seq serio_raw snd_seq_device drm_kms_helper snd_timer drm snd soundcore fb_sys_fops syscopyarea sysfillrect 8250_fintek sysimgblt i2c_piix4 vboxguest(OE) mac_hid parport_pc ppdev lp parport autofs4 hid_generic usbhid hid psmouse ahci libahci e1000 pata_acpi fjes video
[  583.814427] CPU: 0 PID: 2673 Comm: bash Tainted: G           OE   4.4.0-31-generic #50-Ubuntu
[  583.814429] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[  583.814430]  0000000000000286 00000000c588cd8a ffff8800593ebcb8 ffffffff813f1143
[  583.814433]  0000000000000000 ffffffff81cd8430 ffff8800593ebcf0 ffffffff81081102
[  583.814435]  ffff8800593ebdd0 0000000000008241 ffff8800593ebef4 00000000ffffffea
[  583.814437] Call Trace:
[  583.814442]  [<ffffffff813f1143>] dump_stack+0x63/0x90
[  583.814445]  [<ffffffff81081102>] warn_slowpath_common+0x82/0xc0
[  583.814447]  [<ffffffff8108124a>] warn_slowpath_null+0x1a/0x20
[  583.814449]  [<ffffffff8121bfb9>] path_openat+0xb19/0x1330
[  583.814452]  [<ffffffff814f0a41>] ? copy_termios+0x71/0x80
[  583.814455]  [<ffffffff8121d9c1>] do_filp_open+0x91/0x100
[  583.814457]  [<ffffffff810ca961>] ? __raw_callee_save___pv_queued_spin_unlock+0x11/0x20
[  583.814459]  [<ffffffff8122b2d8>] ? __alloc_fd+0xc8/0x190
[  583.814462]  [<ffffffff8120c248>] do_sys_open+0x138/0x2a0
[  583.814464]  [<ffffffff81092230>] ? SyS_rt_sigaction+0xa0/0xd0
[  583.814466]  [<ffffffff8120c3ce>] SyS_open+0x1e/0x20
[  583.814469]  [<ffffffff8182db32>] entry_SYSCALL_64_fastpath+0x16/0x71
[  583.814470] ---[ end trace ad5061abc2861b8c ]---

我无法找到解决此问题的任何方法。如果有人可以提供原因说明我的代码无法正常工作的原因以及可能的任何修复方法,将非常有帮助。

0 个答案:

没有答案