首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Linux内核驱动程序中的回调,以隐藏设备的底层协议

Linux内核驱动程序中的回调,以隐藏设备的底层协议
EN

Code Review用户
提问于 2015-08-08 19:20:25
回答 1查看 1.9K关注 1票数 11

我正在为通过HD44780总线连接的I2C LCD编写一个Linux内核驱动程序。在最后一次更改中,我尝试将低级代码(通过I2C与设备对话)与设备的逻辑(打印字符、解析特殊字符、管理屏幕几何等)解耦。我试图通过回调来解决这个问题,它似乎奏效了:

LCD数据:

代码语言:javascript
复制
#define BUF_SIZE    64

struct hd44780_geometry {
    int cols;
    int rows;
    int start_addrs[];
};

struct hd44780 {
    struct cdev cdev;
    struct device *device;
    struct i2c_client *i2c_client;
    struct hd44780_geometry *geometry;
    int addr;
    char buf[BUF_SIZE];

    struct {
        void (*f)(struct hd44780 *, int data);
        void *arg;
    } raw_callback;

    struct mutex lock;
    struct list_head list;
};

extern struct hd44780_geometry hd44780_geometry_20x4;

void hd44780_write(struct hd44780 *, char *, size_t);
void hd44780_init_lcd(struct hd44780 *);
void hd44780_print(struct hd44780 *, char *);

初始化和回调代码:

代码语言:javascript
复制
static void pcf8574_raw_write(struct hd44780 *lcd, int data)
{
    i2c_smbus_write_byte(lcd->raw_callback.arg, data);
}

static void hd44780_write_nibble(struct hd44780 *lcd, int data)
{
    pcf8574_raw_write(lcd, data);
    /* Theoretically wait for tAS = 40ns, practically it's already elapsed */

    pcf8574_raw_write(lcd, data | E);
    /* Again, "wait" for pwEH = 230ns */

    pcf8574_raw_write(lcd, data);
    /* And again, "wait" for about tCYC_E - pwEH = 270ns */
}

static int hd44780_file_open(struct inode *inode, struct file *filp)
{
    filp->private_data = container_of(inode->i_cdev, struct hd44780, cdev);
    return 0;
}

static int hd44780_file_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static ssize_t hd44780_file_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp)
{
    struct hd44780 *lcd;
    size_t n;

    lcd = filp->private_data;
    n = min(count, (size_t)BUF_SIZE);

    // TODO: Consider using an interruptible lock
    mutex_lock(&lcd->lock);

    // TODO: Support partial writes during errors?
    if (copy_from_user(lcd->buf, buf, n))
        return -EFAULT;

    hd44780_write(lcd, lcd->buf, n);

    mutex_unlock(&lcd->lock);

    return n;
}

static void hd44780_init(struct hd44780 *lcd, struct hd44780_geometry *geometry,
        struct i2c_client *i2c_client)
{
    lcd->geometry = geometry;
    lcd->i2c_client = i2c_client;
    lcd->raw_callback.f = hd44780_write_nibble;
    lcd->raw_callback.arg = i2c_client;
    lcd->addr = 0x00;
    mutex_init(&lcd->lock); 
}

static struct file_operations fops = {
    .open = hd44780_file_open,
    .release = hd44780_file_release,
    .write = hd44780_file_write,
};

static int hd44780_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    dev_t devt;
    struct hd44780 *lcd;
    struct device *device;
    int ret, minor;

    minor = atomic_inc_return(&next_minor);
    devt = MKDEV(MAJOR(dev_no), minor);

    lcd = (struct hd44780 *)kmalloc(sizeof(struct hd44780), GFP_KERNEL);
    if (!lcd) {
        return -ENOMEM;
    }

    hd44780_init(lcd, &hd44780_geometry_20x4, client);

    spin_lock(&hd44780_list_lock);
    list_add(&lcd->list, &hd44780_list);
    spin_unlock(&hd44780_list_lock);

    cdev_init(&lcd->cdev, &fops);
    ret = cdev_add(&lcd->cdev, devt, 1);
    if (ret) {
        pr_warn("Can't add cdev\n");
        goto exit;
    }

    device = device_create(hd44780_class, NULL, devt, NULL, "lcd%d", MINOR(devt));
    if (IS_ERR(device)) {
        ret = PTR_ERR(device);
        pr_warn("Can't create device\n");
        goto del_exit;
    }
    lcd->device = device;

    hd44780_init_lcd(lcd);
    hd44780_print(lcd, "Hello, world!");

    return 0;

del_exit:
    cdev_del(&lcd->cdev);

    spin_lock(&hd44780_list_lock);
    list_del(&lcd->list);
    spin_unlock(&hd44780_list_lock);
exit:
    kfree(lcd);

    return ret;
}

以及一段代码,它显示了回调是如何调用的:

代码语言:javascript
复制
static void hd44780_write_nibble(struct hd44780 *lcd, int data) {
    lcd->raw_callback.f(lcd, data);
}

static void hd44780_write_command(struct hd44780 *lcd, int data)
{
    int h = (data >> 4) & 0x0F;
    int l = data & 0x0F;
    int cmd_h, cmd_l;

    cmd_h = (h << 4) | (RS & 0x00) | (RW & 0x00) | BL;
    hd44780_write_nibble(lcd, cmd_h);

    cmd_l = (l << 4) | (RS & 0x00) | (RW & 0x00) | BL;
    hd44780_write_nibble(lcd, cmd_l);

    udelay(37);
}

这里的关键是将i2c_client传递给hd44780结构,以便回调以后可以使用它。

有更好的/推荐的方法来解决这个问题吗?

您可以找到完整的源代码这里。这是最初问这里的问题。

EN

回答 1

Code Review用户

发布于 2015-08-09 09:09:04

Pass arg取代lcd

现在,您将lcd传递给回调函数,但回调函数不需要lcd。它需要的是存储在lcd->raw_callback.arg中的回调参数。我认为您应该将这个参数直接传递给回调函数,如下所示:

代码语言:javascript
复制
static void hd44780_write_nibble(struct hd44780 *lcd, int data)
{
    lcd->raw_callback.f(lcd->raw_callback.arg, data);
}

回调函数如下所示:

代码语言:javascript
复制
static void hd44780_write_nibble(void *arg, int data)
{
    pcf8574_raw_write(arg, data);
    /* Theoretically wait for tAS = 40ns, practically it's already elapsed */

    pcf8574_raw_write(arg, data | E);
    /* Again, "wait" for pwEH = 230ns */

    pcf8574_raw_write(arg, data);
    /* And again, "wait" for about tCYC_E - pwEH = 270ns */
}

pcf8574_raw_write()看起来是这样的:

代码语言:javascript
复制
static void pcf8574_raw_write(void *arg, int data)
{
    i2c_smbus_write_byte(arg, data);
}

命名

我会把raw_callback重命名为write_nibble_callback。当我打电话给raw_callback.f()时,我不知道该期待什么。

奇怪的是,您有两个名为hd44780_write_nibble()的静态函数。我意识到它们在你的程序的两个不同的层次上,但是如果我在调试你的程序,我会对我在哪个功能上感到困惑。

票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/100364

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档