1

我一直在为 LCD 模块编写 lcd 内核驱动程序。一切顺利,我可以写入显示器,创建一个可以写入的 /dev/lcd 节点,它将在屏幕上显示结果。我认为使用 llseek fops 回调将光标定位在 lcd 上会很好,这样我可以使用 rewind fseek 等。但是它没有按我的预期工作,下面是我所看到的摘要:

驱动程序端的相关代码行是:

loff_t lcd_llseek(struct file *filp, loff_t off, int whence)
{
    switch (whence) {
        case 0: // SEEK_SET
            if (off > 4*LINE_LENGTH || off < 0) {
                printk(KERN_ERR "unsupported SEEK_SET offset %llx\n", off);
                return -EINVAL;
            }
            lcd_gotoxy(&lcd, off, 0, WHENCE_ABS);
            break;
        case 1: // SEEK_CUR 
            if (off > 4*LINE_LENGTH || off < -4*LINE_LENGTH) {
                printk(KERN_ERR "unsupported SEEK_CUR offset %llx\n", off);
                return -EINVAL;
            }
            lcd_gotoxy(&lcd, off, 0, WHENCE_REL);
            break;
        case 2: // SEEK_END (not supported, hence fall though)
        default:
            // how did we get here !
            printk(KERN_ERR "unsupported seek operation\n");
            return -EINVAL;
    }
    filp->f_pos = lcd.pos;
    printk(KERN_INFO "lcd_llseek complete\n");
    return lcd.pos;
}

int lcd_open(struct inode *inode, struct file *filp)
{
    if (!atomic_dec_and_test(&lcd_available)) {
        atomic_inc(&lcd_available);
        return -EBUSY; // already open
    }

    return 0;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .write = lcd_write,
    .llseek = lcd_llseek,
    .open = lcd_open,
    .release = lcd_release,
};

int lcd_init(void)
{
    ...

    // allocate a new dev number (this can be dynamic or
    // static if passed in as a module param)
    if (major) {
        devno = MKDEV(major, 0);
        ret = register_chrdev_region(devno, 1, MODULE_NAME);
    } else {
        ret = alloc_chrdev_region(&devno, 0, 1, MODULE_NAME);
        major = MAJOR(devno);
    }
    if (ret < 0) {
        printk(KERN_ERR "alloc_chrdev_region failed\n");
        goto fail;
    }

    // create a dummy class for the lcd
    cl = class_create(THIS_MODULE, "lcd");
    if (IS_ERR(cl)) {
        printk(KERN_ERR "class_simple_create for class lcd failed\n");
        goto fail1;
    }

    // create cdev interface
    cdev_init(&cdev, &fops);
    cdev.owner = THIS_MODULE;
    ret = cdev_add(&cdev, devno, 1);
    if (ret) {
        printk(KERN_ERR "cdev_add failed\n");
        goto fail2;
    }

    // create /sys/lcd/fplcd/dev so udev will add our device to /dev/fplcd
    device = device_create(cl, NULL, devno, NULL, "lcd");
    if (IS_ERR(device)) {
        printk(KERN_ERR "device_create for fplcd failed\n");
        goto fail3;
    }
    ...
}

为了测试 lseek 调用,我进行了以下单元测试:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define log(msg, ...) fprintf(stdout, __FILE__ ":%s():[%d]:" msg, __func__, __LINE__, __VA_ARGS__)

int lcd;

void test(void)
{
    int k;

    // a lot of hello's
    log("hello world test\n",1);
    if (lseek(lcd, 0, SEEK_CUR) == -1) {
        log("failed to seek\n", 1);
    }
}

int main(int argc, char **argv)
{
    lcd = open("/dev/lcd", O_WRONLY);
    if (lcd == -1) {
        perror("unable to open lcd");
        exit(EXIT_FAILURE);
    }

    test();

    close(lcd);
    return 0;
}

这些文件是这样交叉编译的:

~/Workspace/ts4x00/lcd-module$ cat Makefile 
obj-m += fls_lcd.o

all:
    make -C $(KPATH) M=$(PWD) modules
    $(CROSS_COMPILE)gcc -g -fPIC $(CFLAGS) lcd_unit_test.c -o lcd_unit_test

clean:
    make -C $(KPATH) M=$(PWD) clean
    rm -rf lcd_unit_test
~/Workspace/ts4x00/lcd-module$ make CFLAGS+="-march=armv4 -ffunction-sections -fdata-sections"
make -C ~/Workspace/ts4x00/linux-2.6.29 M=~/Workspace/ts4x00/lcd-module modules
make[1]: Entering directory `~/Workspace/ts4x00/linux-2.6.29'
  CC [M]  ~/Workspace/ts4x00/lcd-module/fls_lcd.o
~/Workspace/ts4x00/lcd-module/fls_lcd.c:443: warning: 'lcd_entry_mode' defined but not used
  Building modules, stage 2.
  MODPOST 1 modules
  CC      ~/Workspace/ts4x00/lcd-module/fls_lcd.mod.o
  LD [M]  ~/Workspace/ts4x00/lcd-module/fls_lcd.ko
make[1]: Leaving directory `~/Workspace/ts4x00/linux-2.6.29'
~/Workspace/ts4x00/arm-2008q3/bin/arm-none-linux-gnueabi-gcc -g -fPIC -march=armv4 -ffunction-sections -fdata-sections lcd_unit_test.c -o lcd_unit_test

这是使用单元测试运行驱动程序的输出是:

root@ts4700:~/devel# insmod ./fls_lcd.ko 
root@ts4700:~/devel# ./lcd_unit_test 
lcd_unit_test.c:test():[61]:hello world test
lcd_unit_test.c:test():[63]:failed to seek
root@ts4700:~/devel# dmesg
FLS LCD driver started
unsupported SEEK_SET offset bf0a573c

我无法弄清楚为什么参数在内核方面被弄得如此糟糕,我尝试将 SEEK_CUR 设置为位置 0,在驱动程序中我得到一个 SEEK_SET(无论我在单元测试中放了什么)和一个疯狂的大数字离开?

有谁知道发生了什么?

顺便说一句,我正在为 arm 开发套件上的内核 2.6.29 编译

4

1 回答 1

0

好吧对不起,昨晚尝试调试后,归结为针对错误的内核进行编译(我将 KPATH 留给了与 sdcard 上不同的内核配置)

很抱歉浪费了大家的时间,但希望如果有人在他们的内核驱动程序中看到看起来像一个疯狂的堆栈,这可能会让他们变得直截了当。

哦,感谢所有的帮助:)

于 2013-08-09T13:10:19.387 回答