【问题标题】:Linux kernel character device driver returns empty string to userspaceLinux内核字符设备驱动向用户空间返回空字符串
【发布时间】:2016-07-18 12:31:37
【问题描述】:
#include <linux/init.h>           
#include <linux/module.h>         
#include <linux/device.h>         
#include <linux/kernel.h>         
#include <linux/fs.h>             
#include <asm/uaccess.h>         
#include <linux/interrupt.h>
#include <asm/io.h>

#define  DEVICE_NAME "kbdozgur"   
#define  CLASS_NAME  "kbdozgur"  
MODULE_AUTHOR("Mehmet Ozgur Bayhan");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Interrupt buffering skeleton");
MODULE_VERSION("0.2");

#define BUFFER_SIZE 20
static unsigned char bfr[BUFFER_SIZE]; 
static int bufferCounter = 0; 

static int majorNumber; 
static char message[BUFFER_SIZE] = { 0 }; 
static short size_of_message; 
static int numberOpens = 0; 
static struct class* kbdozgurcharClass = NULL; 
static struct device* kbdozgurcharDevice = NULL; 


static int dev_release(struct inode *, struct file *);
static ssize_t dev_read(struct file *, char *, size_t, loff_t *);
static ssize_t dev_write(struct file *, const char *, size_t, loff_t *);

static struct file_operations fops = { .open = dev_open, .read = dev_read, .write = dev_write, .release = dev_release, };

irq_handler_t irq_handler(int irq, void *dev_id, struct pt_regs *regs) {
    static unsigned char scancode;
    //Read keyboard status
    scancode = inb(0x60);

    if (scancode == 0x01) {
        printk(KERN_INFO "MOB: Inputs are > %s\n", bfr);
        bufferCounter = 0;
        memset(&bfr[0], 0, sizeof(bfr));
    }
    else if (scancode == 0x1E) {
        bfr[bufferCounter] = 'a';
        bufferCounter++;
    }
    else if (scancode == 0x1F) {
        bfr[bufferCounter] = 's';
        bufferCounter++;
    }
    else if (scancode == 0x20) {
        bfr[bufferCounter] = 'd';
        bufferCounter++;
    }
    else if (scancode == 0x21) {
        bfr[bufferCounter] = 'f';
        bufferCounter++;
    }
    else if (scancode == 0x22) {
        bfr[bufferCounter] = 'g';
        bufferCounter++;
    }
    else if (scancode == 0x23) {
        bfr[bufferCounter] = 'h';
        bufferCounter++;
    }
    else if (scancode == 0x24) {
        bfr[bufferCounter] = 'j';
        bufferCounter++;
    }
    if (bufferCounter >= BUFFER_SIZE) {
        bufferCounter = 0;
        memset(&bfr[0], 0, sizeof(bfr));
    }

    return (irq_handler_t) IRQ_HANDLED;
}

static int init_mod(void) {
    int result;

    /*
     *****************************
     * Create Character device
     *****************************
     */

    // Try to dynamically allocate a major number for the device
    majorNumber = register_chrdev(0, DEVICE_NAME, &fops);
    if (majorNumber < 0) {
        printk(KERN_ALERT "MOB: kbdozgurcharClass failed to register a major number\n");
        return majorNumber;
    }
    printk(KERN_INFO "MOB: registered correctly with major number %d\n", majorNumber);
    // Register the device class
    kbdozgurcharClass = class_create(THIS_MODULE, CLASS_NAME);
    if (IS_ERR(kbdozgurcharClass)) { // Check for error and clean up if there is
        unregister_chrdev(majorNumber, DEVICE_NAME);
        printk(KERN_ALERT "MOB: Failed to register device class\n");
        return PTR_ERR(kbdozgurcharClass); // Correct way to return an error on a pointer
    }
    printk(KERN_INFO "MOB: device class registered correctly\n");

    // Register the device driver
    kbdozgurcharDevice = device_create(kbdozgurcharClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
    if (IS_ERR(kbdozgurcharDevice)) { // Clean up if there is an error
        class_destroy(kbdozgurcharClass); // Repeated code but the alternative is goto statements
        unregister_chrdev(majorNumber, DEVICE_NAME);
        printk(KERN_ALERT "MOB: Failed to create the device\n");
        return PTR_ERR(kbdozgurcharDevice);
    }
    printk(KERN_INFO "MOB: device class created correctly\n"); // Made it! device was initialized
    /*
     *****************************
     * Bind interrupt
     *****************************
     */

    result = request_irq(1, (irq_handler_t) irq_handler, IRQF_SHARED, "kbdozgur", (void *) (irq_handler));
    if (result) printk(KERN_INFO "MOB: can't get shared interrupt for keyboard\n");

    printk(KERN_INFO "MOB: kbdozgur loaded.\n");
    return result;

}

static void exit_mod(void) {
    /*
     * ****************************
     * Destroy Character Device
     * ****************************
     */
    device_unregister(kbdozgurcharDevice);
    device_destroy(kbdozgurcharClass, MKDEV(majorNumber, 0)); // remove the device
    class_unregister(kbdozgurcharClass); // unregister the device class
    class_destroy(kbdozgurcharClass); // remove the device class
    unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number
    printk(KERN_INFO "MOB: Goodbye from the LKM!\n");

    /*
     * ****************************
     * Free IRQ bind
     * ****************************
     */
    free_irq(1, (void *) (irq_handler));
    printk(KERN_INFO "MOB: kbdozgur unloaded.\n");
}

static int dev_open(struct inode *inodep, struct file *filep) {
    numberOpens++;
    printk(KERN_INFO "MOB: Device has been opened %d time(s)\n", numberOpens);
    return 0;
}


static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset) {
    int error_count = 0;
    // copy_to_user has the format ( * to, *from, size) and returns 0 on success
//  error_count = copy_to_user(buffer, message, size_of_message);
    error_count = copy_to_user(buffer, "test", 4);

    if (error_count == 0) { // if true then have success
//      printk(KERN_INFO "MOB: Sent %d characters to the user >> %s\n", size_of_message, message);
        printk(KERN_INFO "MOB: Sent %d characters to the user >> %s\n", 4, "test");
        return (size_of_message = 0); // clear the position to the start and return 0
    }
    else {
        printk(KERN_INFO "MOB: Failed to send %d characters to the user\n", error_count);
        return -EFAULT; // Failed -- return a bad address message (i.e. -14)
    }
}


static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) {
    sprintf(message, "%s(%d letters)", buffer, len); // appending received string with its length
    size_of_message = strlen(message); // store the length of the stored message
    printk(KERN_INFO "MOB: Received %d characters from the user\n", len);
    return len;
}

static int dev_release(struct inode *inodep, struct file *filep) {
    printk(KERN_INFO "MOB: Device successfully closed\n");
    return 0;
}
module_init(init_mod);
module_exit(exit_mod);

我正在尝试构建一个用于中断缓冲和服务于用户空间的骨架驱动程序。

但是,我的字符设备将空字符串返回到用户空间。我在 shell 中尝试了 cat,在 python 中尝试了 openread。两者都返回空字符串。顺便说一句,它正常地从用户空间获取字符数组,正如我所料。

相关部分>>

static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset) {
    int error_count = 0;
    // copy_to_user has the format ( * to, *from, size) and returns 0 on success
    // error_count = copy_to_user(buffer, message, size_of_message);
    error_count = copy_to_user(buffer, "test", 4);

    if (error_count == 0) { // if true then have success
//      printk(KERN_INFO "MOB: Sent %d characters to the user >> %s\n", size_of_message, message);
        printk(KERN_INFO "MOB: Sent %d characters to the user >> %s\n", 4, "test");
        return (size_of_message = 0); // clear the position to the start and return 0
    }
    else {
        printk(KERN_INFO "MOB: Failed to send %d characters to the user\n", error_count);
        return -EFAULT; // Failed -- return a bad address message (i.e. -14)
    }
}

首先我尝试过:

error_count = copy_to_user(buffer, message, size_of_message);

然后我尝试检查:

error_count = copy_to_user(buffer, "test", 4);

同样的故事。两者都返回空字符串。没有错误。 dmesg 中没有相关内容。

我以 root 用户身份进行试验,并且该文件具有该权限:

crw------- 1 root root 250, 0 Mar 30 14:43 /dev/kbdozgur

那我哪里做错了?

【问题讨论】:

  • 为什么你的dev_read返回0?
  • 你不应该复制超过len字节到用户缓冲区。
  • 是的,我现在知道了 :) 谢谢。

标签: c linux-kernel kernel driver


【解决方案1】:

read 应该返回读取的字节数。在您的情况下,您返回 0。

size_message = 0

您应该执行以下操作

size_t  size_requested;
...
if (len >= size_of_message) {
   size_requested  = size_of_message;
} else {
   size_requested = len;
}
if (copy_to_user (buf, message, size_requested)) {
    retval = -EFAULT;
    return retval
}
return size_requested;

【讨论】:

  • 感谢您的解决方案。它可以工作,但现在我对“sprintf(message, "%s(%d letters)", buffer, len);" 有一个不同的问题部分:)
  • 试试snprintf(message, sizeof(message), "%*s(%d letters)", len, buffer, len);
  • 稍加修正=> 下面的代码会导致无限循环,我不知道为什么,但我通过删除“retval = -EFAULT;”解决了它并始终返回“size_requested”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-29
  • 1970-01-01
  • 1970-01-01
  • 2012-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多