I have been writing a lcd kernel driver for a LCD module. All was going well, I can write to the display, create a /dev/lcd node that I can write into and it will display the results on the screen. I thought using the llseek fops callback to position the cursor on the lcd would be good, this way I could use rewind fseek etc. However it is not working as I expected, below is a summary of what I am seeing:

The relevant lines of code from the driver side are:

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;
    }
    ...
}

To test the lseek call I have the following unit test:

#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;
}

The files are cross compiled like so:

~/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

This is the output of running the driver with the unit test is:

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

I cannot figure out why the parameters are being mucked up so badly on the kernel side, I tried to SEEK_CUR to position 0 and in the driver I get a SEEK_SET (no matter what I put in the unit test) and a crazy big number for off?

Does anyone know what is going on please ?

btw I am compiling for kernel 2.6.29 on a arm dev kit

有帮助吗?

解决方案

OK sorry guys after trying to debug this all last night it comes down to compiling against the wrong kernel (I had KPATH left to a different config of the kernel than was on the sdcard)

sorry for wasting everyones time, but hopefully if someone is seeing what looks like a crazy stack in their kernel driver this might set them straight.

oh and thanks for all the help :)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top