Linux3.5—IIC学习分析

I2C控制器的设备对象内核已经实现并关联到platform总线。node

 

I2C控制器的驱动对象内核已经实现。linux

 

 

看mach-tiny4412.hios

/plat-samsung/目录下算法

/drivers/i2c/   看 *.o 文件数组

看i2c-s3c2410.c   从下往上看。app

.id_table ide

匹配成功后看 probe函数:函数

 

一个I2C控制器对应一个struct s3c24xx_i2c结构体对象:ui

  struct s3c24xx_i2c *i2c; this

struct s3c24xx_i2c { wait_queue_head_t wait; unsigned int quirks; unsigned int            suspended:1; struct i2c_msg          *msg;       //IIC要传输的数据, unsigned int msg_num;     //数组元素格式 unsigned int msg_idx; unsigned int msg_ptr; unsigned int tx_setup; unsigned int irq;        //中断号 enum s3c24xx_i2c_state state; unsigned long clkrate; void __iomem            *regs;      //经过platform_get_resource拿到物理基地址,映射完后赋值 struct clk              *clk; struct device           *dev; struct resource         *ioarea; struct i2c_adapter adap;      //读写数据的算法 struct s3c2410_platform_i2c     *pdata; int                     gpios[2]; #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; #endif };

 

struct i2c_msg { __u16 addr; /* slave address */ __u16 flags; #define I2C_M_TEN               0x0010  /* this is a ten bit chip address */
#define I2C_M_RD                0x0001  /* read data, from slave to master */
#define I2C_M_NOSTART           0x4000  /* if I2C_FUNC_NOSTART */
#define I2C_M_REV_DIR_ADDR      0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK        0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK         0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN          0x0400  /* length will be first received byte */ __u16 len; /* msg length */ __u8 *buf;              /* pointer to msg data */ };

 

/* * i2c_adapter is the structure used to identify a physical i2c bus along * with the access algorithms necessary to access it. */
struct i2c_adapter { struct module *owner; unsigned int class;               /* classes to allow probing for */
        const struct i2c_algorithm *algo; /* the algorithm to access the bus */
        void *algo_data; /* data fields that are valid for all devices */
        struct rt_mutex bus_lock; int timeout;                    /* in jiffies */
        int retries; struct device dev;              /* the adapter device */

        int nr; char name[48]; struct completion dev_released; struct mutex userspace_clients_lock; struct list_head userspace_clients; };

 

 

/* * The following structs are for those who like to implement new bus drivers: * i2c_algorithm is the interface to a class of hardware solutions which can * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 * to name two of the most common. */
struct i2c_algorithm { /* If an adapter algorithm can't do I2C-level access, set master_xfer to NULL. If an adapter algorithm can do SMBus access, set smbus_xfer. If set to NULL, the SMBus protocol is simulated using common I2C messages */
        /* master_xfer should return the number of messages successfully processed, or a negative value on error */
        int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); /* To determine what the adapter supports */ u32 (*functionality) (struct i2c_adapter *); };

 

tiny4412一共是9个IIC控制器接口,若是都加入的话,probe函数最多能够被调用9次。

来自exynos4412数据手册:

29.2特性12C总线接口的特色是:9频道多主机。

从12C总线接口(通用频道8个,高清多媒体接口专用频道1个)

7位寻址模式串行、8位定向和双向数据传输

支持高达100千位在标准模式支持高达400千位在快速模式。

支持主发送、主接收、从发送和从接收操做

支持中断或轮询事件

probe.c 部分代码:   I2C控制器的初始化,访问总线的读写算法的实现。

strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name)); i2c->adap.owner   = THIS_MODULE; i2c->adap.algo    = &s3c24xx_i2c_algorithm; //I2C控制访问总线的读写算法 i2c->adap.retries = 2;               //尝试次数,最多两次 i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD; i2c->tx_setup     = 50;


    

      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  //获取资源

      

     

      i2c->ioarea = request_mem_region(res->start, resource_size(res),
                  pdev->name);

      i2c->regs = ioremap(res->start, resource_size(res));

      

      ret = s3c24xx_i2c_init(i2c);

      

/* find the IRQ for this unit (note, this relies on the init call to
* ensure no current IRQs pending
*/

    i2c->irq = ret = platform_get_irq(pdev, 0);
    if (ret <= 0) {
      dev_err(&pdev->dev, "cannot find IRQ\n");
      goto err_iomap;
    }

    ret = request_irq(i2c->irq, s3c24xx_i2c_irq, 0,
                  dev_name(&pdev->dev), i2c);

 

 

    

    ret = i2c_add_numbered_adapter(&i2c->adap); //很是重要,下面有分析
    if (ret < 0) {
      dev_err(&pdev->dev, "failed to add bus to i2c core\n");
      goto err_cpufreq;
    }

 

 

s3c24xx_i2c_algorithm中的 .master_xfer
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data; int retry; int ret; pm_runtime_get_sync(&adap->dev); clk_enable(i2c->clk); for (retry = 0; retry < adap->retries; retry++) { ret = s3c24xx_i2c_doxfer(i2c, msgs, num); //真正的从总线上收发数据 if (ret != -EAGAIN) { clk_disable(i2c->clk); pm_runtime_put_sync(&adap->dev); return ret; } dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry); udelay(100); } clk_disable(i2c->clk); pm_runtime_put_sync(&adap->dev); return -EREMOTEIO; }

 

 

/* s3c24xx_i2c_doxfer * * this starts an i2c transfer */

static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num) { unsigned long timeout; int ret; if (i2c->suspended) return -EIO; ret = s3c24xx_i2c_set_master(i2c); if (ret != 0) { dev_err(i2c->dev, "cannot get bus (error %d)\n", ret); ret = -EAGAIN; goto out; } i2c->msg     = msgs; i2c->msg_num = num; i2c->msg_ptr = 0; i2c->msg_idx = 0; i2c->state   = STATE_START; s3c24xx_i2c_enable_irq(i2c);      //使能I2C中断 s3c24xx_i2c_message_start(i2c, msgs); timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); ret = i2c->msg_idx; /* having these next two as dev_err() makes life very * noisy when doing an i2cdetect */

        if (timeout == 0) dev_dbg(i2c->dev, "timeout\n"); else if (ret != num) dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); /* For QUIRK_HDMIPHY, bus is already disabled */
        if (i2c->quirks & QUIRK_HDMIPHY) goto out; s3c24xx_i2c_wait_idle(i2c); out: return ret; }

 

 

/* s3c24xx_i2c_set_master * * get the i2c bus for a master transaction */

static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c) { unsigned long iicstat; int timeout = 400;        //检查400次 while (timeout-- > 0) { iicstat = readl(i2c->regs + S3C2410_IICSTAT); if (!(iicstat & S3C2410_IICSTAT_BUSBUSY)) return 0; msleep(1); } return -ETIMEDOUT; }

 

 
/* s3c24xx_i2c_message_start * * put the start of a message onto the bus */

static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg) { unsigned int addr = (msg->addr & 0x7f) << 1;    //7位地址,左移一位。 unsigned long stat; unsigned long iiccon; stat = 0; stat |= S3C2410_IICSTAT_TXRXEN; if (msg->flags & I2C_M_RD) { stat |= S3C2410_IICSTAT_MASTER_RX;    // 2<<6,对应配置。
 addr |= 1; } else stat |= S3C2410_IICSTAT_MASTER_TX;  //3<<6 对应上图数据手册截图 if (msg->flags & I2C_M_REV_DIR_ADDR) addr ^= 1; /* todo - check for whether ack wanted or not */ s3c24xx_i2c_enable_ack(i2c);  //使能ACK iiccon = readl(i2c->regs + S3C2410_IICCON); writel(stat, i2c->regs + S3C2410_IICSTAT); dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr); writeb(addr, i2c->regs + S3C2410_IICDS); /* delay here to ensure the data byte has gotten onto the bus * before the transaction is started */ ndelay(i2c->tx_setup); dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon); writel(iiccon, i2c->regs + S3C2410_IICCON); stat |= S3C2410_IICSTAT_START; //1<<5
 writel(stat, i2c->regs + S3C2410_IICSTAT); }

1:struct i2c_board_info xx = {};

   i2c_register_board_info();

2:匹配busnum ,

 

3: 生成:struct i2c_client{

      .name = xxx

     }/**

 * i2c_add_numbered_adapter - declare i2c adapter, use static bus number * @adap: the adapter to register (with adap->nr initialized) * Context: can sleep * * This routine is used to declare an I2C adapter when its bus number * matters. For example, use it for I2C adapters from system-on-chip CPUs, * or otherwise built in to the system's mainboard, and where i2c_board_info * is used to properly configure I2C devices. * * If the requested bus number is set to -1, then this function will behave * identically to i2c_add_adapter, and will dynamically assign a bus number. * * If no devices have pre-been declared for this bus, then be sure to * register the adapter before any dynamically allocated ones. Otherwise * the required bus ID may not be available. * * When this returns zero, the specified adapter became available for * clients using the bus number provided in adap->nr. Also, the table * of I2C devices pre-declared using i2c_register_board_info() is scanned, * and the appropriate driver model device nodes are created. Otherwise, a * negative errno value is returned. */
int i2c_add_numbered_adapter(struct i2c_adapter *adap) { int id; int status; if (adap->nr == -1) /* -1 means dynamically assign bus id */
                return i2c_add_adapter(adap); if (adap->nr & ~MAX_IDR_MASK) return -EINVAL; retry: if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) return -ENOMEM; mutex_lock(&core_lock); /* "above" here means "above or equal to", sigh; * we need the "equal to" result to force the result */ status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id); if (status == 0 && id != adap->nr) { status = -EBUSY; idr_remove(&i2c_adapter_idr, id); } mutex_unlock(&core_lock); if (status == -EAGAIN) goto retry; if (status == 0) status = i2c_register_adapter(adap);      //下面看这个
return status; }

 

 

static int i2c_register_adapter(struct i2c_adapter *adap) { int res = 0; /* Can't register until after driver model init */
        if (unlikely(WARN_ON(!i2c_bus_type.p))) { res = -EAGAIN; goto out_list; } /* Sanity checks */
        if (unlikely(adap->name[0] == '\0')) { pr_err("i2c-core: Attempt to register an adapter with "
                       "no name!\n"); return -EINVAL; } if (unlikely(!adap->algo)) { pr_err("i2c-core: Attempt to register adapter '%s' with "
                       "no algo!\n", adap->name); return -EINVAL; } rt_mutex_init(&adap->bus_lock); mutex_init(&adap->userspace_clients_lock); INIT_LIST_HEAD(&adap->userspace_clients); /* Set default timeout to 1 second if not already set */
        if (adap->timeout == 0) adap->timeout = HZ; dev_set_name(&adap->dev, "i2c-%d", adap->nr); adap->dev.bus = &i2c_bus_type; adap->dev.type = &i2c_adapter_type; res = device_register(&adap->dev); if (res) goto out_list; dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); #ifdef CONFIG_I2C_COMPAT res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev, adap->dev.parent); if (res) dev_warn(&adap->dev, "Failed to create compatibility class link\n"); #endif

        /* create pre-declared device nodes */
        if (adap->nr < __i2c_first_dynamic_bus_num) i2c_scan_static_board_info(adap);    //接下来是这个 /* Notify drivers */ mutex_lock(&core_lock); bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); mutex_unlock(&core_lock); return 0; out_list: mutex_lock(&core_lock); idr_remove(&i2c_adapter_idr, adap->nr); mutex_unlock(&core_lock); return res; }

 

 

static void i2c_scan_static_board_info(struct i2c_adapter *adapter) { struct i2c_devinfo      *devinfo; down_read(&__i2c_board_lock);

     //
LIST_HEAD(__i2c_board_list); 全局可访问,头结点
 list_for_each_entry(devinfo, &__i2c_board_list, list) { //遍历链表
      //遍历一个一个从机准备的信息,匹配busnum ,成功后调用
i2c_new_device来建立 i2c_client
      //i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
      //s3c24xx_i2c_probe 函数中赋值 i2c->adap.nr = i2c->pdata->bus_num;if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter, &devinfo->board_info)) dev_err(&adapter->dev, "Can't create device at 0x%02x\n", devinfo->board_info.addr); } up_read(&__i2c_board_lock); /**
 * i2c_new_device - instantiate an i2c device * @adap: the adapter managing the device * @info: describes one I2C device; bus_num is ignored * Context: can sleep * * Create an i2c device. Binding is handled through driver model * probe()/remove() methods. A driver may be bound to this device when we * return from this function, or any later moment (e.g. maybe hotplugging will * load the driver module). This call is not appropriate for use by mainboard * initialization logic, which usually runs during an arch_initcall() long * before any i2c_adapter could exist. * * This returns the new i2c client, which may be saved for later use with * i2c_unregister_device(); or NULL to indicate an error. */

struct i2c_client {
  unsigned short flags;    /* div., see below */
  unsigned short addr;   /* chip address - NOTE: 7bit */
                /* addresses are stored in the */
                /* _LOWER_ 7 bits */
  char name[I2C_NAME_SIZE];
  struct i2c_adapter *adapter; /* the adapter we sit on */
  struct i2c_driver *driver; /* and our access routines */
  struct device dev; /* the device structure */
  int irq; /* irq issued by device */
  struct list_head detected;
};

 
 struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) { struct i2c_client *client; int status; client = kzalloc(sizeof *client, GFP_KERNEL); if (!client) return NULL; client->adapter = adap;                       //info 从机真正的信息             client->dev.platform_data = info->platform_data; if (info->archdata) client->dev.archdata = *info->archdata; client->flags = info->flags; client->addr = info->addr;  //从机地址 client->irq = info->irq;    //从机对应的外部中断号或者外部中断对应的GPIO strlcpy(client->name, info->type, sizeof(client->name));  //与匹配相关的名字 /* Check for address validity */ status = i2c_check_client_addr_validity(client); if (status) { dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n", client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr); goto out_err_silent; } /* Check for address business */ status = i2c_check_addr_busy(adap, client->addr); if (status) goto out_err; client->dev.parent = &client->adapter->dev;

/* struct bus_type i2c_bus_type = {
  .name = "i2c",
  .match = i2c_device_match,
  .probe = i2c_device_probe,
  .remove = i2c_device_remove,
  .shutdown = i2c_device_shutdown,
  .pm = &i2c_device_pm_ops,
  };   */

 client->dev.bus = &i2c_bus_type;    //确实是“i2c” 下面查看bus_type i2c_bus_type中的i2c_device_probe 查看匹配规则 后面列出 client->dev.type = &i2c_client_type; client->dev.of_node = info->of_node; /* For 10-bit clients, add an arbitrary offset to avoid collisions */ dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), client->addr | ((client->flags & I2C_CLIENT_TEN) ? 0xa000 : 0)); status = device_register(&client->dev);    //注册 if (status) goto out_err; dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n", client->name, dev_name(&client->dev)); return client; out_err: dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x " "(%d)\n", client->name, client->addr, status); out_err_silent: kfree(client); return NULL; }

 

 

//使得i2c_client 和 i2c_driver 关联起来
static
int i2c_device_probe(struct device *dev) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; int status; if (!client) return 0; driver = to_i2c_driver(dev->driver); if (!driver->probe || !driver->id_table) return -ENODEV; client->driver = driver; if (!device_can_wakeup(&client->dev)) device_init_wakeup(&client->dev, client->flags & I2C_CLIENT_WAKE); dev_dbg(dev, "probe\n"); status = driver->probe(client, i2c_match_id(driver->id_table, client)); //匹配的最后一个有个哨兵 if (status) { client->driver = NULL; i2c_set_clientdata(client, NULL); }

 

static int i2c_device_match(struct device *dev, struct device_driver *drv) { struct i2c_client       *client = i2c_verify_client(dev); struct i2c_driver       *driver; if (!client) return 0; /* Attempt an OF style match */
        if (of_driver_match_device(dev, drv)) return 1; driver = to_i2c_driver(drv); /* match on an id table if there is one */
        if (driver->id_table) return i2c_match_id(driver->id_table, client) != NULL; return 0; }

 

总结:维护了一个__i2c_board_list为头结点的双向循环链表

下面以我tiny4412上使用的S70LCD触摸屏驱动为例:

  从机硬件信息往全局的循环链表__i2c_board_list里注册的时刻必须在 i2c 控制器的设备对象和 i2c控制器的驱动对象由platform总线的匹配规则匹配以前成功注册好。

这里在mach-tiny4412.c里的smdk4x12_machine_init() 来完成。

经过查看原理图,链接的是IIC控制器1;

//触摸屏控制模块ft5206从机的硬件信息的注册在这里。

s3c_i2c1_set_platdata(&tiny4412_i2c1_data);
    i2c_register_board_info(1, smdk4x12_i2c_devs1, ARRAY_SIZE(smdk4x12_i2c_devs1));

 

 
 

#include <plat/ft5x0x_touch.h>
static struct ft5x0x_i2c_platform_data ft5x0x_pdata = {
    .gpio_irq = EXYNOS4_GPX1(6),    //中断外部引脚为EINT14的GPX1(6)
    .irq_cfg = S3C_GPIO_SFN(0xf),   //配置引脚为中断模式
    .screen_max_x = 800,
    .screen_max_y = 1280,
    .pressure_max = 255,
};


static
struct i2c_board_info smdk4x12_i2c_devs1[] __initdata = { {                       //0x38 I2C_BOARD_INFO("ft5x0x_ts", (0x70 >> 1)), .platform_data = &ft5x0x_pdata, }, };

忘了说,先取消厂家提供的驱动程序。

具体目录如上图;

注意:咱们的 从机信息在 i2c 设备注册以前已经注册。

 

 涉及从机也就是触摸屏控制模块对应的驱动对象(struct i2c_driver),当其和从机的对象匹配成功则调用probe,probe完成:

@1 触摸屏做为输入设备,则利用input子系统的机制实现驱动的编写。

@2 外部中断的注册。

@3 中断上下半部的实现。

@4 中断的下半部调用I2C控制器的读写算法所完成的访问总线的函数读取ft5206
所准备好的触摸数据。

 

 拿到数据利用input子系统的上报函数上报便可。       

 

驱动代码:

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/input.h>
 4 #include <linux/interrupt.h>
 5 #include <linux/slab.h>
 6 #include <linux/i2c.h>
 7 #include <linux/gpio.h>
 8 
 9 #include <plat/ft5x0x_touch.h>
 10 #include <plat/gpio-cfg.h>
 11 
 12 struct prislavedata {  13     struct  input_dev *inputdev;  14     struct i2c_client *cli;  15     int gpio;  16     int irqnum;  17     struct work_struct work;  18 };  19 
 20 /*中断的下半部处理函数*/
 21 static void do_ts_bh(struct work_struct *work)  22 {  23 #define LEN 31
 24     struct prislavedata *tsdev = container_of(work, struct prislavedata, work);  25     char kbuf[LEN];  26     struct input_dev *idev = tsdev->inputdev;  27     int x, y;  28 
 29     /*读取从机ft5206内部寄存器的值*/
 30 
 31     if (i2c_master_recv(tsdev->cli, kbuf, LEN) < 0) {  32         return;  33  }  34 
 35     if (kbuf[2] < 0) {  36         return;  37  }  38 
 39     if (!((kbuf[3] >> 6) & 0x3)) {  40         x = ((kbuf[3]&0xf) << 8) | kbuf[4];  41         y = ((kbuf[5]&0xf) << 8) | kbuf[6];  42 
 43  input_report_abs(idev, ABS_X, x);  44  input_report_abs(idev, ABS_Y, y);  45         input_report_abs(idev, ABS_PRESSURE, 1);  46         input_report_key(idev, BTN_TOUCH, 1);  47  input_sync(idev);  48     } else if (((kbuf[3] >> 6) & 0x3) == 0x1){  49         input_report_abs(idev, ABS_PRESSURE, 0);  50         input_report_key(idev, BTN_TOUCH, 0);  51  input_sync(idev);  52     } else {  53     
 54  }  55 
 56 
 57     enable_irq(tsdev->irqnum);  58 }  59 
 60 /*中断的上半部处理函数*/
 61 static irqreturn_t do_ts_top(int irqnum, void *data)  62 {  63     struct prislavedata *tsdev = data;  64 
 65     schedule_work(&tsdev->work);  66 
 67     disable_irq_nosync(tsdev->irqnum);  68 
 69     return IRQ_HANDLED;  70 }  71 
 72 static int ts_probe(struct i2c_client *cli, const struct i2c_device_id *devid)  73 {  74     int ret;  75     struct input_dev *idev;  76     struct prislavedata *tsdev;  77     struct ft5x0x_i2c_platform_data *platdat;  78 
 79     /*
 80  1. 获取到从机的信息的GPIO引脚的编号后设置GPIO为外部中断专用。  81  2. 将GPIO引脚的编号转换为中断号,注册中断,初始化中断的下半部。  82  3. 为输入设备分配空间,设置事件分类、编码、注册输入设备驱动。  83      */
 84 
 85         
 86     tsdev = kzalloc(sizeof(struct prislavedata), GFP_KERNEL);  87 
 88     if (NULL == tsdev) {  89         return -ENOMEM;  90  }  91 
 92     platdat = cli->dev.platform_data;  93 
 94     tsdev->cli    = cli;  95     tsdev->gpio   = platdat->gpio_irq;  96     tsdev->irqnum = gpio_to_irq(platdat->gpio_irq);  97 
 98     ret = gpio_request(tsdev->gpio, "ft5206irq");  99     if (ret < 0) { 100         goto error0; 101  } 102 
103     /*将GPX1_6设置为外部中断专用*/
104     s3c_gpio_cfgpin(tsdev->gpio, platdat->irq_cfg); 105 
106     /*注册触摸屏中断*/
107     ret = request_irq(tsdev->irqnum, do_ts_top, IRQF_TRIGGER_FALLING, 108             "ft5206", tsdev); 109     if (ret < 0) { 110         goto error1; 111  } 112 
113     /*初始化中断的下半部任务*/
114     INIT_WORK(&tsdev->work, do_ts_bh); 115 
116     tsdev->inputdev = idev = input_allocate_device(); 117     if (!tsdev->inputdev) { 118         goto error2; 119  } 120 
121     /*设置事件分类及编码*/
122     set_bit(EV_ABS, idev->evbit); 123     set_bit(EV_KEY, idev->evbit); 124 
125     set_bit(ABS_X, idev->absbit); 126     set_bit(ABS_Y, idev->absbit); 127     set_bit(ABS_PRESSURE, idev->absbit); 128 
129     set_bit(BTN_TOUCH, idev->keybit); 130 
131     input_set_abs_params(idev, ABS_X, 0, 799, 0, 0); 132     input_set_abs_params(idev, ABS_Y, 0, 479, 0, 0); 133     input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0); 134 
135     ret = input_register_device(idev); 136     if (ret < 0) { 137         goto error3; 138  } 139 
140  i2c_set_clientdata(cli, tsdev); 141 
142     return 0; 143 error3: 144  input_free_device(idev); 145 error2: 146     free_irq(tsdev->irqnum, tsdev); 147 error1: 148     gpio_free(tsdev->gpio); 149 error0: 150  kfree(tsdev); 151 
152     return ret; 153 } 154 
155 static int ts_remove (struct i2c_client *cli) 156 { 157     struct prislavedata *tsdev = i2c_get_clientdata(cli); 158 
159     input_unregister_device(tsdev->inputdev); 160     free_irq(tsdev->irqnum, tsdev); 161     gpio_free(tsdev->gpio); 162  kfree(tsdev); 163 
164     return 0; 165 } 166 
167 const struct i2c_device_id tables[] = { 168     {"ft5206", }, 169     {"ft5206_ts", }, 170  { }, 171 }; 172 
173 static struct i2c_driver ft5206slav = { 174     .probe        = ts_probe, 175     .remove        = ts_remove, 176     .driver        = { 177         .name    =   "ft5206", 178  }, 179     .id_table     = tables, 180 }; 181 
182 module_i2c_driver(ft5206slav); 183 
184 MODULE_LICENSE("GPL");
ts.c

 

 

至此:i2c 基本了解。

相关文章
相关标签/搜索