簡體   English   中英

向linux char設備驅動寫長?

[英]Write long to linux char device driver?

我正在嘗試在 linux 中編寫字符設備驅動程序。 不幸的是,它不適用於任何大於 255 的數字。

我希望這個驅動程序專門使用long類型的值。 每當我輸入一個大於 255 的值時,數字就會出錯。 256 變為 0 等等。

我編寫了一個簡單的字符設備驅動程序來顯示問題,可能有很多未使用的包含語句,因為我復制了完整的驅動程序並刪除了幾乎所有內容:

圖表.c

#include <linux/init.h>
#include <linux/module.h> /* I mean this is a module after all! */
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/sched.h> /* For current task information */
#include <linux/fs.h> /* For file operations */
#include <linux/types.h> /* dev_t: device number data type */
#include <linux/cdev.h> /* cdev is the module data type that the kernel sees */
#include <asm/uaccess.h> /* For routines to copy data to/from user space */
#include <linux/uaccess.h>
#include <linux/slab.h> /* kmalloc/kfree */


MODULE_LICENSE("GPL");

#define DRIVER_NAME "chartest"

#define MAJOR_NUM 230
#define MINOR_NUM 0

struct cdev *cdev;

int test_device_open(struct inode *inode, struct file *fp) {
    return 0;
}


int test_device_release(struct inode *inode, struct file *fp) {
    return 0;
}


ssize_t test_read(struct file *fp, char __user *buffer, size_t count, loff_t *f_pos) {
    return count;
}

ssize_t test_write(struct file *fp, const char __user *buffer, size_t count, loff_t *fpos) {
    // We must validate the user's buffer and convert it to a long long
    long userOperand;
    unsigned char *userInput = NULL;

    printk(KERN_NOTICE "Write Function Entered.\n");
    printk(KERN_ALERT "Write count: %ld, Write fp: %lld\n", count, *fpos);

    userInput = kmalloc(count, GFP_KERNEL); 
    get_user(*userInput, buffer);

    printk(KERN_NOTICE "Value before cast: %ld\n", (long) *userInput);

    userOperand = (long) *userInput;

    printk(KERN_NOTICE "Value after cast: %ld\n", userOperand);

    // Increment the file position pointer (in our case, always by 8)
    *fpos += count;

    kfree(userInput);
    return count;
}


/*
* Declaration of function for open file operations
*/
static struct file_operations test_fops = {
    .owner = THIS_MODULE,
    .read = test_read,
    .write = test_write,
    .open = test_device_open,
    .release = test_device_release,
};



// Initialization function
static int __init test_init(void)
{
    // Register device number:
    int err = 0;
    dev_t device_number = MKDEV(MAJOR_NUM, MINOR_NUM);

    err = register_chrdev_region(device_number, 1, DRIVER_NAME);

    if (err < 0) {
        printk(KERN_ALERT "Could not allocate device number.\n");
        return err;
    }

    cdev = cdev_alloc();
    cdev->owner = THIS_MODULE;
    cdev->ops = &test_fops;

    err = cdev_add(cdev, device_number, 1);
    if (err) {
        printk("Error allocating cdev.\n");
    }

    printk(KERN_ALERT "Test Initialized. Major Number: %d\n", MAJOR_NUM);

    return 0;
}

// Exit function:
static void __exit test_exit(void)
{
    dev_t device_number = MKDEV(MAJOR_NUM, MINOR_NUM);

    // Remove char device */
    cdev_del(cdev);

    /* Unregister Device Number: */
    unregister_chrdev_region(device_number, 1);
    printk(KERN_ALERT "TestDriver %d destroyed.\n", MAJOR_NUM);
}

module_init(test_init);
module_exit(test_exit);

小測試程序:

maintest.c:

#include <unistd.h>
#include <fcntl.h>



int main(void) {
    long input = 256;


    int fd = open("/dev/chartest0", O_RDWR);

    write(fd, &input, sizeof(long));

    close(fd);

    return 0;

}

printk語句給出了以下 output ,給定輸入為 256:

Write Eunction Entered.
Write count: 8, Write fp: 0
Value before cast: 0
Value after cast: 0

給定輸入大小為 8 字節的copy_from_user也會失敗。 當一次遍歷一個字節並復制數據時,它也會失敗。 我什么都試過了。

如果您願意提供幫助,請編譯:MakeFile

ifeq ($(KERNELRELEASE),)

    # Assume the source tree is where the running kernel was built
    # You should set KERNELDIR in the environment if it's elsewhere
    KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    # The current directory is passed to sub-makes as argument
    PWD := $(shell pwd)

modules:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

.PHONY: modules modules_install clean

else
    # called from kernel build system: just declare what our modules are
    obj-m := chartest.o

endif

然后在同一目錄中:

sudo insmod chartest.ko

最后:

sudo mknod -m 777 /dev/chartest0 c 230 0

然后可以編譯maindriver.c並運行它來測試。

有人可以幫我解決這個問題嗎?

你不能像你一樣使用get_user

來自get_user 文檔

此宏將單個簡單變量從用戶空間復制到 kernel 空間。 它支持 char 和 int 等簡單類型,但不支持結構或 arrays 等更大的數據類型
ptr 必須具有指向簡單變量的指針類型,並且取消引用 ptr 的結果必須可分配給 x 而無需強制轉換。

使用get_user ,您只會復制第一個字符。

您需要使用copy_from_user ,這個 function 可以復制數組和結構,而不僅僅是簡單的類型:

ssize_t test_write(struct file *fp, const char __user *buffer, size_t count, loff_t *fpos) {
    // We must validate the user's buffer and convert it to a long long
    long userOperand;
    unsigned char *userInput = NULL;

    userInput = kmalloc(count, GFP_KERNEL); 

    printk(KERN_NOTICE "Write Function Entered.\n");
    printk(KERN_ALERT "Write count: %ld, Write fp: %lld\n", count, *fpos);

    /* warning, here you should test that count is exactly sizeof userInput */
    copy_from_user(userInput, buffer, count);

    userOperand =  *(long*)userInput;

    printk(KERN_NOTICE "Value after cast: %ld\n", userOperand);

    // Increment the file position pointer (in our case, always by 8)
    *fpos += count;

    kfree(userInput);
    return count;
}

您還可以在copy_from_user中從char *復制到long (在這種情況下沒有 memory alloc):

ssize_t test_write(struct file *fp, const char __user *buffer, size_t count, loff_t *fpos) {
    // We must validate the user's buffer and convert it to a long long
    long userOperand;

    printk(KERN_NOTICE "Write Function Entered.\n");
    printk(KERN_ALERT "Write count: %ld, Write fp: %lld\n", count, *fpos);

    /* warning, here you should test that count is exactly sizeof userOperand */
    copy_from_user(&userOperand, buffer, sizeof userOperand);

    printk(KERN_NOTICE "Value after reading: %ld\n", userOperand);

    // Increment the file position pointer (in our case, always by 8)
    *fpos += count;

    return count;
}

您錯誤地使用了宏get_user

它的第一個參數實際上應該是一個變量名,而不是它的地址

它的第二個參數應該是指向用戶空間數據的類型指針。 該指針的確切類型用於確定要讀取的數據的大小。

正確的:

    long userOperand;
    ...
    get_user(userOperand, (const long __user*)buffer);

    printk(KERN_NOTICE "Value written: %ld\n", userOperand);

注意第二個參數的情況: read long而不是char需要它(因為buffer參數是指向char的指針)。

注意,上面的代碼沒有使用你的userInput變量,所以你不需要定義它,也不需要分配 memory。


請注意,可以使用任何表示從用戶傳遞的字節數的count參數調用.write方法。 由於get_user始終嘗試讀取預定義的字節數(在您的情況下,此數字等於long的大小),因此檢查count是否等於該數字(或者至少是不少於給定的數字):

if (count != sizeof(long)) {
  return -EINVAL;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM