llseek не работает в драйвере ядра

Я пишу драйвер ядра ЖК-дисплея для ЖК-модуля. Все шло хорошо, я могу писать на дисплей, создавать узел /dev/lcd, в который я могу писать, и он будет отображать результаты на экране. Я подумал, что использование обратного вызова llseek fops для позиционирования курсора на ЖК-дисплее было бы хорошо, таким образом, я мог бы использовать перемотку 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 (независимо от того, что я указал в модульном тесте) и сумасшедшее большое число для выключенный?

Кто-нибудь знает, что происходит, пожалуйста?

Кстати, я собираю ядро ​​​​2.6.29 на наборе для разработки рук


person othane    schedule 07.08.2013    source источник
comment
Попробуйте открыть как чтение + запись и реализовать обработчик чтения-заглушки. Кроме того, как выглядит ваш обработчик fops open()?   -  person Peter L.    schedule 08.08.2013
comment
Я добавил lcd_open к исходному сообщению (в основном он просто ограничивает открытие, поэтому только 1 программа за раз может использовать устройство, ЖК-дисплей не будет вести себя хорошо, если на него одновременно будут писать более 1 человека).... Я попытался добавить фиктивный метод чтения, но это не имело никакого значения, выглядело это так... static struct file_operations fops = { .owner = THIS_MODULE, .read= lcd_read, .write = lcd_write, .llseek = lcd_llseek, .open = lcd_open, .release = lcd_release, }; ssize_t lcd_read (файл структуры *filp, char __user *buff, size_t count, loff_t *offp) { return 0; }   -  person othane    schedule 08.08.2013
comment
Попробуйте посмотреть и, возможно, создать новый драйвер на основе черепа. oreilly.com/catalog/linuxdrive2/sample/ch03.html и здесь github.com/starpos/scull/blob/master/scull/main.c   -  person Peter L.    schedule 08.08.2013
comment
хорошо, упс, я обнаружил, что проблема заключалась в том, что я компилировал с неправильной конфигурацией ядра (я отвечу на свой вопрос, когда мне будет разрешено, то есть через 8 часов) ... кстати, я изначально основывал код scull, но проходил движения при попытке компиляции привели меня к проблеме, так что спасибо :)   -  person othane    schedule 08.08.2013


Ответы (1)


ОК, извините, ребята, после попыток отладить это всю прошлую ночь, это сводится к компиляции с неправильным ядром (у меня был KPATH, оставленный для другой конфигурации ядра, чем на SD-карте)

извините за потраченное впустую время, но, надеюсь, если кто-то увидит что-то похожее на сумасшедший стек в своем драйвере ядра, это может их исправить.

ох и спасибо всем за помощь :)

person othane    schedule 09.08.2013