我一直在为 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 编译