zoukankan      html  css  js  c++  java
  • Linux的I2C 设备驱动 mini2440 上i2c接口触摸屏驱动

    本篇记录在友善之臂 mini2440 平台上挂载I2C接口触摸屏的驱动开发过程。

    内核版本linux-2.6.32.2,
    平台是ARM9 S3C2440+I2C接口的触摸屏

    如上篇Linux的I2C驱动体系结构讲述
    http://www.lupaworld.com/273398/viewspace-204237.html

    要挂载新的I2C设备,需要实现3部分:

    1) 适配器的硬件驱动:
    内核中已经实现mini2440,i2c适配器驱动,可以在如下目录i2c-s3c2410.c中看到相关代码
    linux-2.6.32.2/drivers/i2c/busses/i2c-s3c2410.c

    2) I2C 设配器的algorithm
    同样在inux-2.6.32.2/drivers/i2c/busses/i2c-s3c2410.c文件中实现。

    以上两部分无须做任何更改

    3) I2C设备驱动,可以以linux-2.6.32.2/drivers/input/touchscreen/migor_ts.c为例,分析如下:
    //-------------------------------------------------------------------//
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/input.h>
    #include <linux/interrupt.h>
    #include <asm/io.h>
    #include <linux/i2c.h>
    #include <linux/timer.h>
    #include <linux/delay.h>

    /*resolution definion according to touch screen */
    #define MIN_X_COORDINATE    0
    #define MAX_X_COORDINATE    1024   
    #define MIN_Y_COORDINATE    0
    #define MAX_Y_COORDINATE    768

    /* touch screen data structure */
    struct i2c_ts_priv {
        struct i2c_client *client;
        struct input_dev *input;
        struct delayed_work work;
        int irq;
    };

    static void i2c_ts_poscheck(struct work_struct *work)
    {
        struct i2c_ts_priv *priv = container_of(work, struct i2c_ts_priv, work.work);
        /* buffer for storing data */
        char buf[6];
        int number;
        int xpos, ypos;

        memset(buf, 0, sizeof(buf));

        /* Now do Page Read */
        if (i2c_master_recv(priv->client, buf,6) != 6) {
            dev_err(&priv->client->dev, "Unable to read i2c page\n");
            goto out;
        }
           
        /* convert coordinate */
        number = buf[0]&0x07;
        xpos = ((buf[3] << 8) | buf[2]);
        ypos = ((buf[5] << 8) | buf[4]);
       
         /* report input event */
        if ((number != 0) && (xpos != 0) && (ypos != 0)) {
            input_report_key(priv->input, BTN_TOUCH, 1);
            input_report_abs(priv->input, ABS_X, xpos);
            input_report_abs(priv->input, ABS_Y, ypos);
            input_sync(priv->input);
        } else if (number == 0) {
            input_report_key(priv->input, BTN_TOUCH, 0);
            input_sync(priv->input);
        }

     out:
        enable_irq(priv->irq);
    }

    /* read finger numbers and coordinate and report input event */
    static irqreturn_t i2c_ts_isr(int irq, void *dev_id)
    {
        struct i2c_ts_priv *priv = dev_id;
          
        /* disable irq */
        disable_irq_nosync(irq);
        schedule_delayed_work(&priv->work, HZ/100);   
        return IRQ_HANDLED;
    }


    static int i2c_ts_open(struct input_dev *dev)
    {
        return 0;
    }

    static void i2c_ts_close(struct input_dev *dev)
    {

    }

    static int i2c_ts_probe(struct i2c_client *client,
                  const struct i2c_device_id *idp)
    {
        struct i2c_ts_priv *priv;
        struct input_dev *input;
        int error;
        char buf[2];   

        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv) {
            dev_err(&client->dev, "failed to allocate driver data\n");
            error = -ENOMEM;
            goto err0;
        }

        dev_set_drvdata(&client->dev, priv);

        input = input_allocate_device();
        if (!input) {
            dev_err(&client->dev, "Failed to allocate input device.\n");
            error = -ENOMEM;
            goto err1;
        }

        input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
        input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);

        input_set_abs_params(input, ABS_X, MIN_X_COORDINATE, MAX_X_COORDINATE, 0, 0);
        input_set_abs_params(input, ABS_Y, MIN_Y_COORDINATE, MAX_Y_COORDINATE, 0, 0);

        input->name = client->name;
        input->id.bustype = BUS_I2C;
        input->dev.parent = &client->dev;

        input->open = i2c_ts_open;
        input->close = i2c_ts_close;

        input_set_drvdata(input, priv);

        priv->client = client;
        priv->input = input;
        INIT_DELAYED_WORK(&priv->work, i2c_ts_poscheck);
        priv->irq = client->irq;

        error = input_register_device(input);
        if (error)
            goto err1;

        error = request_irq(priv->irq, i2c_ts_isr, IRQF_TRIGGER_FALLING,
                    client->name, priv);
        if (error) {
            dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
            goto err2;
        }

        device_init_wakeup(&client->dev,1);
       
        return 0;

     err2:
        input_unregister_device(input);
        input = NULL; /* so we dont try to free it below */
     err1:
        input_free_device(input);
        kfree(priv);
     err0:
        dev_set_drvdata(&client->dev, NULL);
        return error;
    }

    static int i2c_ts_remove(struct i2c_client *client)
    {
        struct i2c_ts_priv *priv = dev_get_drvdata(&client->dev);

        free_irq(priv->irq, priv);
        input_unregister_device(priv->input);
        kfree(priv);

        dev_set_drvdata(&client->dev, NULL);

        return 0;
    }

    static int i2c_ts_suspend(struct i2c_client *client, pm_message_t mesg)
    {
        struct i2c_ts_priv *priv = dev_get_drvdata(&client->dev);
        if(device_may_wakeup(&client->dev))
        enable_irq_wake(priv->irq);
        return 0;
    }

    static int i2c_ts_resume(struct i2c_client *client)
    {
        struct i2c_ts_priv *priv = dev_get_drvdata(&client->dev);
        if(device_may_wakeup(&client->dev))
        disable_irq_wake(priv->irq);
        return 0;
    }

    static const struct i2c_device_id i2c_ts_id[] = {
        { "i2c-ts", 0 },
        { }
    };
    MODULE_DEVICE_TABLE(i2c, i2c_ts_id);

    static struct i2c_driver i2c_ts_driver = {
        .driver = {
            .name = "i2c-ts",
        },
        .probe = i2c_ts_probe,
        .remove = i2c_ts_remove,
        .suspend = i2c_ts_suspend,
        .resume = i2c_ts_resume,
        .id_table = i2c_ts_id,
    };

    static int __init i2c_ts_init(void)
    {
        return i2c_add_driver(&i2c_ts_driver);
    }

    static void __exit i2c_ts_exit(void)
    {
        i2c_del_driver(&i2c_ts_driver);
    }

    MODULE_DESCRIPTION("i2c Touchscreen driver");
    MODULE_AUTHOR("ALlen <allen.p.wang@gmail.com>");
    MODULE_LICENSE("GPL");

    module_init(i2c_ts_init);
    module_exit(i2c_ts_exit);

    4).实现如上步骤后,还需要创建和配置I2C 设备,设置文件位于
    linux-2.6.32.2/arch/arm/mach-s3c2440/mach-mini2440.c中,
    添加如下代码:
    ..................................................
    +/* I2C touch screen devices. */
    +/* bus configuration */
    +static struct s3c2410_platform_i2c i2c_touchscreen_cfg __initdata = {
    +   .flags        = 0,
    +    .slave_addr    = 0x5c,
    +   .frequency    = 100*1000,
    +    .sda_delay    = 2,
    +};

    +/* i2c device name is "i2c_ts", address is 0x5c, interrupt is eint20 */
    +static struct i2c_board_info touchscreen_i2c_devs[] __initdata = {
    +    {
    +        I2C_BOARD_INFO("i2c-ts", 0x5c),
    +        .irq    = IRQ_EINT20,
    +    },
    +};
    ...................................................

    static void __init mini2440_machine_init(void)
    {
    ..................................................
     +   /* i2c touch screen devices */
     +   s3c_i2c0_set_platdata(&i2c_touchscreen_cfg);
     +  i2c_register_board_info(0, touchscreen_i2c_devs,  +ARRAY_SIZE(touchscreen_i2c_devs));
    ...................................................
    }

    此处I2C_BOARD_INFO("i2c-ts",0x5c), “i2c-ts” 要和i2c设备驱动中i2c_ts_id一致。
    才能保证i2c设备驱动成功加载。

  • 相关阅读:
    SQL之mysql常用操作语句(入门级)
    总结了一些指针易出错的常见问题(七)
    C++之类和对象课后习题1
    SQL之50个常用的SQL语句
    SQL之经典SQL语句大全
    C语言之计算字符串最后一个单词的长度,单词以空格隔开
    spring之HttpInvoker
    Java之解压流(ZipInputStream)
    jetty之嵌入式开发
    java之压缩流(ZipOutputStream)
  • 原文地址:https://www.cnblogs.com/ljf181275034/p/2782410.html
Copyright © 2011-2022 走看看