Linux下I2C接口触摸屏驱动分析

  linux下触摸屏驱动的移植主要包括这几个步骤:

    (1)确定触摸屏IC接口,了解对应接口的API函数,注册设备并加入到相应总线上

    (2)关联设备与驱动,并编写具体的驱动代码

    (3)熟悉linux 输入设备驱动,在驱动代码中分配一个输入设备并初始化相应数据结构,在驱动实现中引用

    这里对应上面几部分,分析I2C接口下触摸屏驱动的实现。先介绍linux下I2C接口与输入子系统架构,然后基于FT5x0x源码逐层展开整个移植过程的实现。

一、I2C驱动架构

linux I2C驱动架构

    具体的实现可分为四个层次:

    1、提供adapter的硬件驱动,探测、初始化i2c adapter(如申请i2c的io地址和中断号),驱动soc控制的i2c adapter在硬件上产生信号(start、stop、ack)以及处理i2c中断。覆盖图中的硬件实现层。主要数据结构struct i2c_adapter,这里adapter的驱动在文件/drivers/i2c/busses/i2c-omap.c中

[cpp]  view plain copy
  1. struct i2c_adapter {    
  2.     struct module *owner;    
  3.     unsigned int id;    
  4.     unsigned int class;    
  5.     struct i2c_algorithm *algo;/* the algorithm to access the bus   */    
  6.     void *algo_data;    
  7.     
  8.     /* --- administration stuff. */    
  9.     int (*client_register)(struct i2c_client *);    
  10.     int (*client_unregister)(struct i2c_client *);    
  11.     
  12.     /* data fields that are valid for all devices   */    
  13.     struct mutex bus_lock;    
  14.     struct mutex clist_lock;    
  15.     
  16.     int timeout;    
  17.     int retries;    
  18.     struct device dev;      /* the adapter device */    
  19.     struct class_device class_dev;  /* the class device */    
  20.     
  21.     int nr;    
  22.     struct list_head clients;    
  23.     struct list_head list;    
  24.     char name[I2C_NAME_SIZE];    
  25.     struct completion dev_released;    
  26.     struct completion class_dev_released;    
  27. };  

     这个结构体对应一个控制器。其中包含了控制器名称,algorithm数据,控制器设备等。

     在模块加载后,会调用驱动的probe函数寻找对应的adapter完成驱动与adapter的匹配。这里的probe函数为

[cpp]  view plain copy
  1. static int __devinit omap_i2c_probe(struct platform_device *pdev)  
  2. {  
  3.          struct omap_i2c_dev     *dev;  
  4.          struct i2c_adapter      *adap;  
  5.          struct resource         *mem, *irq, *ioarea;  
  6.          struct omap_i2c_bus_platform_data *pdata = pdev->dev.platform_data;  
  7.          irq_handler_t isr;  
  8.          int r;  
  9.          u32 speed = 0;  
  10.    
  11.          /* NOTE: driver uses the static register mapping */  
  12.          mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  13.          if (!mem) {  
  14.                  dev_err(&pdev->dev, "no mem resource?\n");  
  15.                  return -ENODEV;  
  16.          }  
  17.          irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  
  18.          if (!irq) {  
  19.                  dev_err(&pdev->dev, "no irq resource?\n");  
  20.                  return -ENODEV;  
  21.          }  
  22.    
  23.          ioarea = request_mem_region(mem->start, resource_size(mem),  
  24.                          pdev->name);  
  25.          if (!ioarea) {  
  26.                  dev_err(&pdev->dev, "I2C region already claimed\n");  
  27.                  return -EBUSY;  
  28.          }  
  29.    
  30.          dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL);  
  31.          if (!dev) {  
  32.                  r = -ENOMEM;  
  33.                  goto err_release_region;  
  34.          }  
  35.    
  36.          if (pdata != NULL) {  
  37.                  speed = pdata->clkrate;  
  38.                  dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;  
  39.          } else {  
  40.                  speed = 100;    /* Default speed */  
  41.                  dev->set_mpu_wkup_lat = NULL;  
  42.          }  
  43.    
  44.          dev->speed = speed;  
  45.          dev->dev = &pdev->dev;  
  46.          dev->irq = irq->start;  
  47.          dev->base = ioremap(mem->start, resource_size(mem));  
  48.          if (!dev->base) {  
  49.                  r = -ENOMEM;  
  50.                  goto err_free_mem;  
  51.          }  
  52.    
  53.          platform_set_drvdata(pdev, dev);  
  54.    
  55.          dev->reg_shift = (pdata->flags >> OMAP_I2C_FLAG_BUS_SHIFT__SHIFT) & 3;  
  56.    
  57.          if (pdata->rev == OMAP_I2C_IP_VERSION_2)  
  58.                  dev->regs = (u8 *)reg_map_ip_v2;  
  59.          else  
  60.                  dev->regs = (u8 *)reg_map_ip_v1;  
  61.    
  62.          pm_runtime_enable(dev->dev);  
  63.          pm_runtime_get_sync(dev->dev);  
  64.    
  65.          dev->rev = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff;  
  66.    
  67.          if (dev->rev <= OMAP_I2C_REV_ON_3430)  
  68.                  dev->errata |= I2C_OMAP3_1P153;  
  69.    
  70.          if (!(pdata->flags & OMAP_I2C_FLAG_NO_FIFO)) {  
  71.                  u16 s;  
  72.    
  73.                  /* Set up the fifo size - Get total size */  
  74.                  s = (omap_i2c_read_reg(dev, OMAP_I2C_BUFSTAT_REG) >> 14) & 0x3;  
  75.                  dev->fifo_size = 0x8 << s;  
  76.    
  77.                  /* 
  78.                   * Set up notification threshold as half the total available 
  79.                   * size. This is to ensure that we can handle the status on int 
  80.                   * call back latencies. 
  81.                   */  
  82.    
  83.                  dev->fifo_size = (dev->fifo_size / 2);  
  84.    
  85.                  if (dev->rev >= OMAP_I2C_REV_ON_3530_4430)  
  86.                          dev->b_hw = 0; /* Disable hardware fixes */  
  87.                  else  
  88.                          dev->b_hw = 1; /* Enable hardware fixes */  
  89.    
  90.                  /* calculate wakeup latency constraint for MPU */  
  91.                  if (dev->set_mpu_wkup_lat != NULL)  
  92.                          dev->latency = (1000000 * dev->fifo_size) /  
  93.                                         (1000 * speed / 8);  
  94.          }  
  95.    
  96.          /* reset ASAP, clearing any IRQs */  
  97.          omap_i2c_init(dev);  
  98.    
  99.          isr = (dev->rev < OMAP_I2C_OMAP1_REV_2) ? omap_i2c_omap1_isr :  
  100.                                                                     omap_i2c_isr;  
  101.          r = request_irq(dev->irq, isr, 0, pdev->name, dev);  
  102.    
  103.          if (r) {  
  104.                  dev_err(dev->dev, "failure requesting irq %i\n", dev->irq);  
  105.                  goto err_unuse_clocks;  
  106.          }  
  107.    
  108.          dev_info(dev->dev, "bus %d rev%d.%d.%d at %d kHz\n", pdev->id,  
  109.                   pdata->rev, dev->rev >> 4, dev->rev & 0xf, dev->speed);  
  110.    
  111.          pm_runtime_put(dev->dev);  
  112.    
  113.          adap = &dev->adapter;  
  114.          i2c_set_adapdata(adap, dev);  
  115.          adap->owner = THIS_MODULE;  
  116.          adap->class = I2C_CLASS_HWMON;  
  117.          strlcpy(adap->name, "OMAP I2C adapter"sizeof(adap->name));  
  118.          adap->algo = &omap_i2c_algo;  
  119.          adap->dev.parent = &pdev->dev;  
  120.    
  121.          /* i2c device drivers may be active on return from add_adapter() */  
  122.          adap->nr = pdev->id;  
  123.          r = i2c_add_numbered_adapter(adap);  
  124.          if (r) {  
  125.                  dev_err(dev->dev, "failure adding adapter\n");  
  126.                  goto err_free_irq;  
  127.          }  
  128.    
  129.          return 0;  
  130.    
  131. err_free_irq:  
  132.          free_irq(dev->irq, dev);  
  133. err_unuse_clocks:  
  134.          omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);  
  135.          pm_runtime_put(dev->dev);  
  136.          iounmap(dev->base);  
  137. err_free_mem:  
  138.          platform_set_drvdata(pdev, NULL);  
  139.          kfree(dev);  
  140. err_release_region:  
  141.          release_mem_region(mem->start, resource_size(mem));  
  142.    
  143.          return r;  
  144. }  

    2、提供adapter的algorithm,用具体适配器的xxx_xferf()函数来填充i2c_algorithm的master_xfer函数指针,并把赋值后的i2c_algorithm再赋值给i2c_adapter的algo指针。覆盖图中的访问抽象层、i2c核心层。主要数据结构struct i2c_algorithm

[cpp]  view plain copy
  1. struct i2c_algorithm {    
  2.     int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,int num);    
  3.     int (*slave_send)(struct i2c_adapter *,char*,int);    
  4.     int (*slave_recv)(struct i2c_adapter *,char*,int);    
  5.     u32 (*functionality) (struct i2c_adapter *);    
  6. };   

    这个结构体中定义了一套控制器使用的通信方法。其中关键函数是master_xfer(),实现了在物理层面上了数据传输的过程。omap平台的实现函数为omap_i2c_xfer()

[cpp]  view plain copy
  1. omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)  
  2. {  
  3.          struct omap_i2c_dev *dev = i2c_get_adapdata(adap);  
  4.          int i;  
  5.          int r;  
  6.    
  7.          pm_runtime_get_sync(dev->dev);  
  8.    
  9.          r = omap_i2c_wait_for_bb(dev);  
  10.          if (r < 0)  
  11.                  goto out;  
  12.    
  13.          if (dev->set_mpu_wkup_lat != NULL)  
  14.                  dev->set_mpu_wkup_lat(dev->dev, dev->latency);  
  15.    
  16.          for (i = 0; i < num; i++) {  
  17.                  r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));  
  18.                  if (r != 0)  
  19.                          break;  
  20.          }  
  21.    
  22.          if (dev->set_mpu_wkup_lat != NULL)  
  23.                  dev->set_mpu_wkup_lat(dev->dev, -1);  
  24.    
  25.          if (r == 0)  
  26.                  r = num;  
  27.    
  28.          omap_i2c_wait_for_bb(dev);  
  29. out:     pm_runtime_put(dev->dev);  
  30.   
  31.          return r;  
  32. }  
   可以看到,最终用于传输的是omap_i2c_xfer_msg()函数,函数定义在drivers/i2c/busses/i2c-omap.c文件中,具体的实现可以先不管。

    3、实现i2c设备驱动中的i2c_driver接口,由结构i2c_client中的数据填充,覆盖图中的driver驱动层。主要数据结构

[cpp]  view plain copy
  1. struct i2c_client {    
  2.     unsigned int flags;     /* div., see below      */    
  3.     unsigned short addr;        /* chip address - NOTE: 7bit    */    
  4.                     /* addresses are stored in the  */    
  5.                     /* _LOWER_ 7 bits       */    
  6.     struct i2c_adapter *adapter;    /* the adapter we sit on    */    
  7.     struct i2c_driver *driver;  /* and our access routines  */    
  8.     int usage_count;        /* How many accesses currently  */    
  9.                     /* to the client        */    
  10.     struct device dev;      /* the device structure     */    
  11.     struct list_head list;    
  12.     char name[I2C_NAME_SIZE];    
  13.     struct completion released;    
  14. };  
    这个结构体中的内容是描述设备的。包含了芯片地址,设备名称,设备使用的中断号,设备所依附的控制器,设备所依附的驱动等内容。

    4、实现i2c设备所对应的具体device的驱动。覆盖图中的driver驱动层。主要数据结构struct i2c_driver 

[cpp]  view plain copy
  1. struct i2c_driver {    
  2.     int id;    
  3.     unsigned int class;    
  4.     
  5.     int (*attach_adapter)(struct i2c_adapter *);    
  6.     int (*detach_adapter)(struct i2c_adapter *);    
  7.     
  8.     int (*detach_client)(struct i2c_client *);    
  9.     
  10.     int (*command)(struct i2c_client *client,unsigned int cmd, void *arg);    
  11.     struct device_driver driver;    
  12.     struct list_head list;    
  13. };  
    这个结构体对应了驱动方法,重要成员函数有probe,remove,suspend,resume与中断处理函数,也是我们需要是实现的函数。另外包括一个重要的数据结构: struct i2c_device_id *id_table; 如果驱动可以支持好几个设备,那么这里面就要包含这些设备的ID。

    第一层和第二层是i2c总线驱动,属于芯片内部的驱动,在linux驱动架构中已经实现,不需要我们编写或更改。第三第四属于i2c设备驱动,需要我们根据内核提供的接口去完成具体的代码。

    另外就是i2c_core层,起到了承上启下的作用,包含在开发中需要用到的核心函数。源代码位于drivers/i2c/i2c-core.c中。在这里可以看到几个重要的函数。

    (1)增加/删除i2c控制器的函数

[cpp]  view plain copy
  1. int i2c_add_adapter(struct i2c_adapter *adapter)    
  2. int i2c_del_adapter(struct i2c_adapter *adap)      

    (2)增加/删除i2c设备的函数

[cpp]  view plain copy
  1. struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);    
  2. void i2c_unregister_device(struct i2c_client *client)  

    (3)增加/删除设备驱动的函数

[cpp]  view plain copy
  1. int i2c_register_driver(struct module *owner, struct i2c_driver *driver)    
  2. void i2c_del_driver(struct i2c_driver *driver)   

    (4)I2C传输、发送和接收函数

[cpp]  view plain copy
  1. int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);    
  2. int i2c_master_send(struct i2c_client *client,const char *buf ,int count);    
  3. int i2c_master_recv(struct i2c_client *client, char *buf ,int count);  

    send和receive分别都调用了transfer函数,而transfer也不是直接和硬件交互,而是调用algorithm中的master_xfer()函数。

二、linux input输入子系统架构


linux输入系统框架

    linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(Event Handler)、输入子系统核心层(Input Core)和输入子系统设备驱动层。在图中可以看出输入设备不止包括触摸屏,还有很多其他种类的输入设备,如键盘,鼠标等等,所以linux内核已经把各式各样的输入设备进行了抽象,提供了统一的接口,让我们把设备接入到系统中。而我们要做的工作就是申请一个输入设备结构体对象,并填充其里面的数据。

    1,输入子系统事件处理层:与userspace衔接,提供处理设备事件的接口,以模块编译进内核,数据结构为input_handler

[cpp]  view plain copy
  1. struct input_handler {  
  2.    
  3.          void *private;  
  4.    
  5.          void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);  
  6.          bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);  
  7.          bool (*match)(struct input_handler *handler, struct input_dev *dev);  
  8.          int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);  
  9.          void (*disconnect)(struct input_handle *handle);  
  10.          void (*start)(struct input_handle *handle);  
  11.    
  12.          const struct file_operations *fops;  
  13.          int minor;  
  14.          const char *name;  
  15.    
  16.          const struct input_device_id *id_table;  
  17.    
  18.          struct list_head        h_list;  
  19.   
  20.          struct list_head        node;  
  21. };  
    其中,struct input_device_id在后面讲到handler与input_dev匹配时将用到,还分别用到match与connect函数,具体过程下面再说。
    struct file_operations提供用户空间的访问方法,这里的evdev形式的fops为:
[cpp]  view plain copy
  1. static const struct file_operations evdev_fops = {  
  2.          .owner          = THIS_MODULE,  
  3.          .read           = evdev_read,  
  4.          .write          = evdev_write,  
  5.          .poll           = evdev_poll,  
  6.          .open           = evdev_open,  
  7.          .release        = evdev_release,  
  8.          .unlocked_ioctl = evdev_ioctl,  
  9. #ifdef CONFIG_COMPAT  
  10.          .compat_ioctl   = evdev_ioctl_compat,  
  11. #endif  
  12.          .fasync         = evdev_fasync,  
  13.          .flush          = evdev_flush,  
  14.          .llseek         = no_llseek,  
  15. };  
    event函数记录输入事件的值,驱动input_report_xxx()上报的值最终会通过这个函数保存到内核区,供用户空间访问,存储这个数值的数据结构为input_event.

[cpp]  view plain copy
  1. struct input_event {  
  2.          struct timeval time;  
  3.          __u16 type;  
  4.          __u16 code;  
  5.          __s32 value;  
  6. };  
    2,输入子系统核心层: 提供构建输入设备核心方法实现与数据结构定义,在文件inpu.h与input.c文件,包括

     (1)注册/注销设备

         input_register_device()
         input_unregister_device()

     (2)注册/注销handler


        input_register_handler()
        input_unregister_handler()

     (3)注册注销handle

         input_register_handle()
         input_unregister_handle()

     handle是device与handler链接的纽带。其定义为

[cpp]  view plain copy
  1. <pre name="code" class="cpp">struct input_handle {  
  2.    
  3.          void *private;  
  4.    
  5.          int open;  
  6.          const char *name;  
  7.    
  8.          struct input_dev *dev;  
  9.          struct input_handler *handler;  
  10.    
  11.          struct list_head        d_node;  
  12.          struct list_head        h_node;  
  13. };</pre>  
  14. <pre></pre>  
  15.     详细过程如图所示:  
  16. <p></p>  
  17. <p align="left"><img src="https://img-my.csdn.net/uploads/201302/06/1360140115_9994.jpg" alt=""><br>  
  18. </p>  
  19. <p align="left">    当新生成一个input_dev时,需要匹配一个input_handler,即特定输入设备相应的处理方法.使用input_attach_handler()<br>  
  20. </p>  
  21. <p align="left"></p>  
  22. <pre name="code" class="cpp"><pre name="code" class="cpp">static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)  
  23.  {  
  24.          const struct input_device_id *id;  
  25.          int error;  
  26.    
  27.          id = input_match_device(handler, dev);  
  28.          if (!id)  
  29.                  return -ENODEV;  
  30.    
  31.          error = handler->connect(handler, dev, id);  
  32.          if (error && error != -ENODEV)  
  33.                  pr_err("failed to attach handler %s to device %s, error: %d\n",  
  34.                         handler->name, kobject_name(&dev->dev.kobj), error);  
  35.    
  36.          return error;  
  37. }</pre>  
  38. <pre></pre>  
  39.     (1) 使用input_match_device()函数匹配,采用dev匹配handler,handler被匹配。首先调用input_match_device()函数。  
  40. <p></p>  
  41. <p align="left"></p>  
  42. <pre name="code" class="cpp"><pre name="code" class="cpp">static const struct input_device_id *input_match_device(struct input_handler *handler,struct input_dev *dev)  
  43. {  
  44.          const struct input_device_id *id;  
  45.          int i;  
  46.    
  47.          for (id = handler->id_table; id->flags || id->driver_info; id++) {  
  48.    
  49.                  if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)  
  50.                          if (id->bustype != dev->id.bustype)  
  51.                                  continue;  
  52.    
  53.                  if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)  
  54.                          if (id->vendor != dev->id.vendor)  
  55.                                  continue;  
  56.    
  57.                  if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)  
  58.                          if (id->product != dev->id.product)  
  59.                                  continue;  
  60.    
  61.                  if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)  
  62.                          if (id->version != dev->id.version)  
  63.                                  continue;  
  64.    
  65.                  MATCH_BIT(evbit,  EV_MAX);  
  66.                  MATCH_BIT(keybit, KEY_MAX);  
  67.                  MATCH_BIT(relbit, REL_MAX);  
  68.                  MATCH_BIT(absbit, ABS_MAX);  
  69.                  MATCH_BIT(mscbit, MSC_MAX);  
  70.                  MATCH_BIT(ledbit, LED_MAX);  
  71.                  MATCH_BIT(sndbit, SND_MAX);  
  72.                  MATCH_BIT(ffbit,  FF_MAX);  
  73.                  MATCH_BIT(swbit,  SW_MAX);  
  74.    
  75.                  if (!handler->match || handler->match(handler, dev))  
  76.                          return id;  
  77.          }  
  78.    
  79.          return NULL;  
  80. }</pre>  
  81. <p><br>  
  82. </p>  
  83. <pre></pre>  
  84.     handler有某一个flag设置了,input设备对应的条件必须具备。使用MATCH_BIT宏进行匹配。<br>  
  85. <br>  
  86.     (2)匹配成功,调用handler->connect()函数关联.触摸屏是事件设备,所以将匹配一个input_handler evdev_handler,对应的函数为evdev_connect,代码存放在evdev.c中。  
  87. <p></p>  
  88. <p align="left"></p>  
  89. <pre name="code" class="cpp"><pre name="code" class="cpp">static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)  
  90. {  
  91.          struct evdev *evdev;  
  92.          int minor;  
  93.          int error;  
  94.    
  95.          for (minor = 0; minor < EVDEV_MINORS; minor++)  
  96.                  if (!evdev_table[minor])  
  97.                          break;  
  98.    
  99.          if (minor == EVDEV_MINORS) {  
  100.                  pr_err("no more free evdev devices\n");  
  101.                  return -ENFILE;  
  102.          }  
  103.    
  104.          evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);  
  105.          if (!evdev)  
  106.                  return -ENOMEM;  
  107.    
  108.          INIT_LIST_HEAD(&evdev->client_list);  
  109.          spin_lock_init(&evdev->client_lock);  
  110.          mutex_init(&evdev->mutex);  
  111.          init_waitqueue_head(&evdev->wait);  
  112.    
  113.          dev_set_name(&evdev->dev, "event%d", minor);  
  114.          evdev->exist = true;  
  115.          evdev->minor = minor;  
  116.    
  117.          evdev->handle.dev = input_get_device(dev);  
  118.          evdev->handle.name = dev_name(&evdev->dev);  
  119.          evdev->handle.handler = handler;  
  120.          evdev->handle.private = evdev;  
  121.    
  122.          evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);  
  123.          evdev->dev.class = &input_class;  
  124.          evdev->dev.parent = &dev->dev;  
  125.          evdev->dev.release = evdev_free;  
  126.          device_initialize(&evdev->dev);  
  127.    
  128.          error = input_register_handle(&evdev->handle);  
  129.          if (error)  
  130.                  goto err_free_evdev;  
  131.    
  132.          error = evdev_install_chrdev(evdev);  
  133.          if (error)  
  134.                  goto err_unregister_handle;  
  135.    
  136.          error = device_add(&evdev->dev);  
  137.          if (error)  
  138.                  goto err_cleanup_evdev;  
  139.    
  140.          return 0;  
  141.    
  142.   err_cleanup_evdev:  
  143.          evdev_cleanup(evdev);  
  144.   err_unregister_handle:  
  145.          input_unregister_handle(&evdev->handle);  
  146.   err_free_evdev:  
  147.          put_device(&evdev->dev);  
  148.          return error;  
  149. }</pre>  
  150. <pre></pre>  
  151.     (1)定义一个比输入设备更为具体的设备——事件设备evdev,触摸屏接入到用户空间也就是事件设备。  
  152. <p></p>  
  153. <p align="left"></p>  
  154. <pre name="code" class="cpp"><pre name="code" class="cpp">struct evdev {  
  155.           int open;  
  156.           int minor;  
  157.           struct input_handle handle;  
  158.           wait_queue_head_t wait;  
  159.           struct evdev_client __rcu *grab;  
  160.           struct list_head client_list;  
  161.           spinlock_t client_lock; /* protects client_list */  
  162.           struct mutex mutex;  
  163.           struct device dev;  
  164.           bool exist;  
  165.  };</pre>  
  166. <p><br>  
  167. </p>  
  168. <pre></pre>  
  169. <p></p>  
  170. <p align="left">   (2)初始化里面的数据。</p>  
  171. <p align="left">   (3)设置evdev里面的值.</p>  
  172. <p align="left">   (4)注册handle.</p>  
  173. <p align="left">   (5)因为evdev_handler可以匹配多个evdev,所以有一个数据是专门用来存储这些evdev的,所以用evdev_install_chrdev()函数把它放入数组。</p>  
  174. <pre name="code" class="cpp"><pre name="code" class="cpp">static int evdev_install_chrdev(struct evdev *evdev)  
  175. {  
  176.          /* 
  177.           * No need to do any locking here as calls to connect and 
  178.           * disconnect are serialized by the input core 
  179.           */  
  180.          evdev_table[evdev->minor] = evdev;  
  181.          return 0;  
  182. }</pre>  
  183. <pre></pre>  
  184.     (6)把设备加入内核。  
  185. <p align="left">    3,输入子系统设备驱动层:是真正设备驱动层,有具体的硬件接口,数据结构为input_dev</p>  
  186. <p align="left"></p>  
  187. <pre name="code" class="cpp"><pre name="code" class="cpp">struct input_dev {  
  188.          const char *name;  
  189.          const char *phys;  
  190.          const char *uniq;  
  191.          struct input_id id;  
  192.    
  193.          unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];  
  194.    
  195.          unsigned long evbit[BITS_TO_LONGS(EV_CNT)];  
  196.          unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];  
  197.          unsigned long relbit[BITS_TO_LONGS(REL_CNT)];  
  198.          unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];  
  199.          unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];  
  200.          unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];  
  201.          unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];  
  202.          unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];  
  203.          unsigned long swbit[BITS_TO_LONGS(SW_CNT)];  
  204.    
  205.          unsigned int hint_events_per_packet;  
  206.    
  207.          unsigned int keycodemax;  
  208.          unsigned int keycodesize;  
  209.          void *keycode;  
  210.    
  211.          int (*setkeycode)(struct input_dev *dev,const struct input_keymap_entry *ke,unsigned int *old_keycode);  
  212.          int (*getkeycode)(struct input_dev *dev,struct input_keymap_entry *ke);  
  213.    
  214.          struct ff_device *ff;  
  215.    
  216.          unsigned int repeat_key;  
  217.          struct timer_list timer;  
  218.    
  219.          int rep[REP_CNT];  
  220.    
  221.          struct input_mt *mt;  
  222.    
  223.          struct input_absinfo *absinfo;  
  224.    
  225.          unsigned long key[BITS_TO_LONGS(KEY_CNT)];  
  226.          unsigned long led[BITS_TO_LONGS(LED_CNT)];  
  227.          unsigned long snd[BITS_TO_LONGS(SND_CNT)];  
  228.          unsigned long sw[BITS_TO_LONGS(SW_CNT)];  
  229.    
  230.          int (*open)(struct input_dev *dev);  
  231.          void (*close)(struct input_dev *dev);  
  232.          int (*flush)(struct input_dev *dev, struct file *file);  
  233.          int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);  
  234.    
  235.          struct input_handle __rcu *grab;  
  236.    
  237.          spinlock_t event_lock;  
  238.          struct mutex mutex;  
  239.    
  240.          unsigned int users;  
  241.          bool going_away;  
  242.    
  243.          struct device dev;  
  244.    
  245.          struct list_head        h_list;  
  246.          struct list_head        node;  
  247.    
  248.          unsigned int num_vals;  
  249.          unsigned int max_vals;  
  250.          struct input_value *vals;  
  251. };</pre>  
  252. <pre></pre>  
  253.     这里包含了所有可能输入设备的数据域, 我们驱动要定义的输入设备的参数就包含在这个数据结构中。<br>  
  254. <br>  
  255.      4,把设备注册进输入子系统里一般要经过以下步骤:<br>  
  256. <br>  
  257.      (1)申请一个输入设备,定义事件集合。关于输入事件的介绍可以看这里:Linux 内核点触摸接口协议<br>  
  258. <br>  
  259.      (2)上报从设备中读到的数据。可使用函数  
  260. <p></p>  
  261. <p align="left"></p>  
  262. <pre name="code" class="cpp"><pre name="code" class="cpp">void input_report_key(struct input_dev *dev, unsigned int code, int value);      //上报按键事件  
  263. void input_report_rel(struct input_dev *dev, unsigned int code, int value);       //上报相对坐标事件  
  264. void input_report_abs(struct input_dev *dev, unsigned int code, int value);       //上报绝对坐标事件</pre>  
  265. <pre></pre>  
  266. 三、基于OMAP FT5x0x驱动源码分析<br>  
  267. <br>  
  268. 1、板级初始化代码<br>  
  269. <br>  
  270.      板级初始化代码与具体的平台有关,负责相应平台的初始化工作,这里使用的pandaboard平台的代码,在目录arch/arm/mach-omap2/board-omap4panda.c中。触摸屏设备当然需要在这里先声明并注册进系统。具体代码如下:  
  271. <p></p>  
  272. <p align="left"></p>  
  273. <pre name="code" class="cpp"><pre name="code" class="cpp">static struct i2c_board_info __initdata panda_i2c2_boardinfo[] = {  
  274. {  
  275.     I2C_BOARD_INFO("ft5x06_ts", 0x38),  
  276.     .irq = OMAP_GPIO_IRQ(34),  
  277. },  
  278. };  
  279. omap_register_i2c_bus(2, 100, panda_i2c2_boardinfo,ARRAY_SIZE(panda_i2c2_boardinfo));</pre>  
  280. <pre></pre>  
  281. <p></p>  
  282. <p align="left">    声明一个i2c_board_info结构的数组,这个数组包含pandaboard板上i2c接口的信息。先使用宏I2C_BOARD_INFO宏声明设备名为ft5x06_ts,地址为0x38。要说明的是这里的地址并不是指处理器中i2c寄存器的硬件地址,而是在系统加载后,位于/sys/bus/i2c/devices下的设备名。再申请这个i2c接口的中断口为GPIO34,这从板的硬件连接图可以看出。<br>  
  283. <br>  
  284.      然后把这个结构体注册进i2c总线,这里跟具体的平台有关,使用omap_register_i2c_bus函数注册。函数定义在arch/arm/plat-omap/i2c.c。尝试跟踪中断信息存储的位置,我们首先看omap_register_i2c_bus()</p>  
  285. <p align="left"></p>  
  286. <pre name="code" class="cpp"><pre name="code" class="cpp">int __init omap_register_i2c_bus(int bus_id, u32 clkrate,struct i2c_board_info const *info,unsigned len)  
  287. {  
  288.          int err;  
  289.    
  290.          BUG_ON(bus_id < 1 || bus_id > omap_i2c_nr_ports());  
  291.    
  292.          if (info) {  
  293.                  err = i2c_register_board_info(bus_id, info, len);  
  294.                  if (err)  
  295.                          return err;  
  296.          }  
  297.    
  298.          if (!i2c_pdata[bus_id - 1].clkrate)  
  299.                  i2c_pdata[bus_id - 1].clkrate = clkrate;  
  300.    
  301.          i2c_pdata[bus_id - 1].clkrate &= ~OMAP_I2C_CMDLINE_SETUP;  
  302.    
  303.          return omap_i2c_add_bus(bus_id);  
  304. }</pre>  
  305. <pre></pre>  
  306.     信息info跳入i2c_register_board_info()函数  
  307. <p></p>  
  308. <p align="left"></p>  
  309. <pre name="code" class="cpp">i2c_register_board_info(int busnum,struct i2c_board_info const *info, unsigned len)  
  310.  {  
  311.          int status;  
  312.    
  313.          down_write(&__i2c_board_lock);  
  314.     
  315.          /* dynamic bus numbers will be assigned after the last static one */  
  316.          if (busnum >= __i2c_first_dynamic_bus_num)  
  317.                  __i2c_first_dynamic_bus_num = busnum + 1;  
  318.    
  319.          for (status = 0; len; len--, info++) {  
  320.                  struct i2c_devinfo      *devinfo;  
  321.    
  322.                  devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);  
  323.                  if (!devinfo) {  
  324.                          pr_debug("i2c-core: can't register boardinfo!\n");  
  325.                          status = -ENOMEM;  
  326.                          break;  
  327.                  }  
  328.    
  329.                   devinfo->busnum = busnum;  
  330.                   devinfo->board_info = *info;  
  331.                   list_add_tail(&devinfo->list, &__i2c_board_list);  
  332.           }  
  333.     
  334.           up_write(&__i2c_board_lock);  
  335.     
  336.           return status;  
  337.  }</pre>    可以看到用链表存储这些设别信息,存储节点为i2c_decinfo结构  
  338. <p></p>  
  339. <p align="left"></p>  
  340. <pre name="code" class="cpp"><pre name="code" class="cpp">struct i2c_devinfo {  
  341.          struct list_head        list;  
  342.          int                     busnum;  
  343.          struct i2c_board_info   board_info;  
  344. };</pre>  
  345. <pre></pre>  
  346.     这个结构会在i2c_scan_static_board_info()函数被引用  
  347. <p></p>  
  348. <p align="left"></p>  
  349. <pre name="code" class="cpp"><pre name="code" class="cpp">static void i2c_scan_static_board_info(struct i2c_adapter *adapter)  
  350. {  
  351.          struct i2c_devinfo      *devinfo;  
  352.    
  353.          down_read(&__i2c_board_lock);  
  354.          list_for_each_entry(devinfo, &__i2c_board_list, list) {  
  355.                  if (devinfo->busnum == adapter->nr  
  356.                                  && !i2c_new_device(adapter,  
  357.                                                  &devinfo->board_info))  
  358.                          dev_err(&adapter->dev,  
  359.                                  "Can't create device at 0x%02x\n",  
  360.                                  devinfo->board_info.addr);  
  361.          }  
  362.          up_read(&__i2c_board_lock);  
  363. }</pre>  
  364. <pre></pre>  
  365.     这个函数遍历设备信息链表,找出与adapter匹配的设备,并使用i2c_new_device()添加进去。这个函数在注册adapter时被i2c_register_adapter()调用。  
  366. <p></p>  
  367. <p align="left"></p>  
  368. <pre name="code" class="cpp"><pre name="code" class="cpp">struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)  
  369. {  
  370.          struct i2c_client       *client;  
  371.          int                     status;  
  372.    
  373.          client = kzalloc(sizeof *client, GFP_KERNEL);  
  374.          if (!client)  
  375.                  return NULL;  
  376.    
  377.          client->adapter = adap;  
  378.    
  379.          client->dev.platform_data = info->platform_data;  
  380.    
  381.          if (info->archdata)  
  382.                  client->dev.archdata = *info->archdata;  
  383.    
  384.          client->flags = info->flags;  
  385.          client->addr = info->addr;  
  386.          client->irq = info->irq;  
  387.    
  388.          strlcpy(client->name, info->type, sizeof(client->name));  
  389.    
  390.          /* Check for address validity */  
  391.          status = i2c_check_client_addr_validity(client);  
  392.          if (status) {  
  393.                  dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",  
  394.                          client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);  
  395.                  goto out_err_silent;  
  396.          }  
  397.    
  398.          /* Check for address business */  
  399.          status = i2c_check_addr_busy(adap, client->addr);  
  400.          if (status)  
  401.                  goto out_err;  
  402.    
  403.          client->dev.parent = &client->adapter->dev;  
  404.          client->dev.bus = &i2c_bus_type;  
  405.          client->dev.type = &i2c_client_type;  
  406.          client->dev.of_node = info->of_node;  
  407.    
  408.          /* For 10-bit clients, add an arbitrary offset to avoid collisions */  
  409.          dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),  
  410.                      client->addr | ((client->flags & I2C_CLIENT_TEN)  
  411.                                       ? 0xa000 : 0));  
  412.          status = device_register(&client->dev);  
  413.          if (status)  
  414.                  goto out_err;  
  415.    
  416.          dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",  
  417.                  client->name, dev_name(&client->dev));  
  418.    
  419.          return client;  
  420.    
  421. out_err:  
  422.          dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "  
  423.                  "(%d)\n", client->name, client->addr, status);  
  424. out_err_silent:  
  425.          kfree(client);  
  426.          return NULL;  
  427. }</pre>  
  428. <pre></pre>  
  429.     这里创建了i2c_client对象,并把devinfo内的信息存储在它里面,下面设备驱动调用的probe函数传进的i2c_client就是这里的client。<br>  
  430. <br>  
  431.     2、设备与驱动绑定<br>  
  432. <br>  
  433.      设备与驱动绑定的代码位于目录drivers/input/touchscreen/ft5x0x.c中,这个文件包含了i2c设备的具体代码,以模块的形式编译进内核。  
  434. <p></p>  
  435. <p align="left"></p>  
  436. <pre name="code" class="cpp"><pre name="code" class="cpp">static const struct i2c_device_id ft5x0x_ts_id[] =  
  437. {  
  438.     { FT5X0X_NAME, 0 },{ }  
  439. };  
  440. MODULE_DEVICE_TABLE(i2c, ft5x0x_ts_id);  
  441.   
  442. static struct i2c_driver ft5x0x_ts_driver =  
  443. {  
  444.     .probe      = ft5x0x_ts_probe,  
  445.     .remove     = __devexit_p(ft5x0x_ts_remove),  
  446.     .id_table   = ft5x0x_ts_id,  
  447.     .driver =  
  448.     {  
  449.         .name   = FT5X0X_NAME,  
  450.         .owner  = THIS_MODULE,  
  451.     },  
  452. };  
  453.   
  454. static int __init ft5x0x_ts_init(void)  
  455. {  
  456.     return i2c_add_driver(&ft5x0x_ts_driver);  
  457. }  
  458.   
  459. static void __exit ft5x0x_ts_exit(void)  
  460. {  
  461.     i2c_del_driver(&ft5x0x_ts_driver);  
  462. }  
  463.   
  464. module_init(ft5x0x_ts_init);  
  465. module_exit(ft5x0x_ts_exit);  
  466.   
  467. MODULE_AUTHOR("<wenfs@Focaltech-systems.com>");  
  468. MODULE_DESCRIPTION("FocalTech ft5x0x TouchScreen driver");  
  469. MODULE_LICENSE("GPL");</pre>  
  470. <pre></pre>  
  471. <p></p>  
  472. <p align="left">     首先建立设备与驱动的映射表ft5x0x_ts_id,通过宏MODULE_DEVICE_TABLE关联起来,MODULE_DEVICE_TABLE第一个参数表示这个设备id是i2c总线上了,第二个参数是指设备,当系统找到这个设备时,就会通过FT5X0X_NAME把设备与这个模块关联起来。<br>  
  473.      这是个i2c驱动模块,当然需要定义一个i2c_driver结构体来标识这个驱动,并说明实现的驱动函数有probe()和remove()函数。<br>  
  474. <br>  
  475.      之后就是添加模块初始化和退出函数,分别是在模块初始化时调用i2c_add_driver()宏加入i2c驱动,i2c_add_driver()实际上调用到的是 i2c_register_driver()函数。</p>  
  476. <p align="left"></p>  
  477. <pre name="code" class="cpp"><pre name="code" class="cpp">int i2c_register_driver(struct module *owner, struct i2c_driver *driver)  
  478. {  
  479.          int res;  
  480.    
  481.          /* Can't register until after driver model init */  
  482.          if (unlikely(WARN_ON(!i2c_bus_type.p)))  
  483.                  return -EAGAIN;  
  484.    
  485.          /* add the driver to the list of i2c drivers in the driver core */  
  486.          driver->driver.owner = owner;  
  487.          driver->driver.bus = &i2c_bus_type;  
  488.    
  489.          /* When registration returns, the driver core 
  490.           * will have called probe() for all matching-but-unbound devices. 
  491.           */  
  492.          res = driver_register(&driver->driver);  
  493.          if (res)  
  494.                  return res;  
  495.    
  496.          /* Drivers should switch to dev_pm_ops instead. */  
  497.          if (driver->suspend)  
  498.                  pr_warn("i2c-core: driver [%s] using legacy suspend method\n",  
  499.                          driver->driver.name);  
  500.          if (driver->resume)  
  501.                  pr_warn("i2c-core: driver [%s] using legacy resume method\n",  
  502.                          driver->driver.name);  
  503.    
  504.          pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);  
  505.    
  506.          INIT_LIST_HEAD(&driver->clients);  
  507.          /* Walk the adapters that are already present */  
  508.          i2c_for_each_dev(driver, __process_new_driver);  
  509.    
  510.          return 0;  
  511. }</pre>  
  512. <pre></pre>  
  513. <p></p>  
  514. <p align="left">    在模块退出时调用i2c_del_driver删除i2c驱动。</p>  
  515. <p align="left">    3、设备驱动程序实现<br>  
  516.      从上面i2c_driver结构可以看出,ft5x0x实现了驱动的两个接口函数,分别为probe()和remove()函数。<br>  
  517.      probe()具体实现函数为ft5x0x_ts_probe(),这个函数在内核初始化时,如果设备和驱动匹配,将调用,实现i2c设备的初始化,具体实现:</p>  
  518. <p align="left"></p>  
  519. <pre name="code" class="cpp"><pre name="code" class="cpp">static int ft5x0x_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)  
  520. {  
  521.     struct ft5x0x_ts_data *ft5x0x_ts;  
  522.     struct input_dev *input_dev;  
  523.     int err = 0;  
  524.     int rev_id = 0;  
  525.       
  526.     if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {  
  527.         err = -ENODEV;  
  528.         goto err_out;  
  529.     }  
  530.   
  531.     ft5x0x_ts = kzalloc(sizeof(*ft5x0x_ts), GFP_KERNEL);  
  532.     if (!ft5x0x_ts) {  
  533.         err = -ENOMEM;  
  534.         goto err_free_mem;  
  535.     }  
  536.   
  537.     this_client = client;  
  538.         i2c_set_clientdata(client, ft5x0x_ts);  
  539.   
  540.     rev_id = i2c_smbus_read_byte_data(client, FT5X0X_REG_FT5201ID);  
  541.     if (rev_id != FT5X06_ID)  
  542.     {  
  543.         err = -ENODEV;  
  544.         dev_err(&client->dev, "failed to probe FT5X0X touchscreen device\n");  
  545.         goto err_free_mem;  
  546.     }  
  547.   
  548.     INIT_WORK(&ft5x0x_ts->pen_event_work, ft5x0x_ts_pen_irq_work);  
  549.     ft5x0x_ts->ts_workqueue = create_singlethread_workqueue(dev_name(&client->dev));  
  550.     if (!ft5x0x_ts->ts_workqueue)  
  551.     {  
  552.         err = -ESRCH;  
  553.         goto err_free_thread;  
  554.     }  
  555.   
  556.     err = request_irq(client->irq, ft5x0x_ts_interrupt, IRQF_DISABLED | IRQF_TRIGGER_RISING, "ft5x0x_ts", ft5x0x_ts);  
  557.     if (err < 0)  
  558.     {  
  559.         dev_err(&client->dev, "request irq failed\n");  
  560.         goto err_free_irq;  
  561.     }  
  562.   
  563.     input_dev = input_allocate_device();  
  564.     if (!input_dev)  
  565.     {  
  566.         err = -ENOMEM;  
  567.         dev_err(&client->dev, "failed to allocate input device\n");  
  568.         goto err_free_input;  
  569.     }  
  570.       
  571.     ft5x0x_ts->input_dev = input_dev;  
  572.   
  573. #ifdef CONFIG_FT5X0X_MULTITOUCH  
  574.     set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);  
  575.     set_bit(ABS_MT_POSITION_X, input_dev->absbit);  
  576.     set_bit(ABS_MT_POSITION_Y, input_dev->absbit);  
  577.     set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);  
  578.         set_bit(ABS_PRESSURE, input_dev->absbit);  
  579.   
  580.     input_set_abs_params(input_dev,  
  581.                  ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);  
  582.     input_set_abs_params(input_dev,  
  583.                  ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);  
  584.     input_set_abs_params(input_dev,  
  585.                  ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0);  
  586.     input_set_abs_params(input_dev,  
  587.                  ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);  
  588. #else  
  589.     set_bit(ABS_X, input_dev->absbit);  
  590.     set_bit(ABS_Y, input_dev->absbit);  
  591.     set_bit(ABS_PRESSURE, input_dev->absbit);  
  592.     set_bit(BTN_TOUCH, input_dev->keybit);  
  593.   
  594.     input_set_abs_params(input_dev, ABS_X, 0, SCREEN_MAX_X, 0, 0);  
  595.     input_set_abs_params(input_dev, ABS_Y, 0, SCREEN_MAX_Y, 0, 0);  
  596.     input_set_abs_params(input_dev, ABS_PRESSURE, 0, PRESS_MAX, 0 , 0);  
  597. #endif  
  598.   
  599.     set_bit(EV_ABS, input_dev->evbit);  
  600.     set_bit(EV_KEY, input_dev->evbit);  
  601.   
  602.     input_dev->name  = FT5X0X_NAME;  
  603.     err = input_register_device(input_dev);  
  604.     if (err)  
  605.     {  
  606.         dev_err(&client->dev, "failed to register input device: %s\n",  
  607.         dev_name(&client->dev));  
  608.         goto err_free_input;  
  609.     }  
  610.   
  611. #ifdef CONFIG_HAS_EARLYSUSPEND  
  612.     ft5x0x_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;  
  613.     ft5x0x_ts->early_suspend.suspend = ft5x0x_ts_suspend;  
  614.     ft5x0x_ts->early_suspend.resume  = ft5x0x_ts_resume;  
  615.     register_early_suspend(&ft5x0x_ts->early_suspend);  
  616. #endif  
  617.   
  618.     return 0;  
  619.   
  620. err_free_input:  
  621.     input_free_device(input_dev);  
  622. err_free_irq:  
  623.     free_irq(client->irq, ft5x0x_ts);  
  624. err_free_thread:  
  625.     cancel_work_sync(&ft5x0x_ts->pen_event_work);  
  626.     destroy_workqueue(ft5x0x_ts->ts_workqueue);  
  627.     i2c_set_clientdata(client, NULL);  
  628. err_free_mem:  
  629.     kfree(ft5x0x_ts);  
  630. err_out:  
  631.     return err;  
  632. }</pre>  
  633. <pre></pre>  
  634.     这个函数代码比较长,但是可以分部分去看,每一部分对应不同的初始化工作.大概可以分为这几个部分:<br>  
  635. <br>  
  636.      (1)检测适配器是否支持I2C_FUNC_I2C的通信方式 :i2c_check_functionality(client->adapter, I2C_FUNC_I2C)<br>  
  637. <br>  
  638.      (2)申请内存存储驱动的数据:ft5x0x_ts = kzalloc(sizeof(*ft5x0x_ts), GFP_KERNEL);并把驱动数据赋值给系统传过来的i2c_client数据:this_client = client; i2c_set_clientdata(client, ft5x0x_ts);<br>  
  639. <br>  
  640.      (3)验证将要通信的设备ID:rev_id = i2c_smbus_read_byte_data(client, FT5X0X_REG_FT5201ID);<br>  
  641. <br>  
  642.      (4)创建触摸事件的工作队列并初始化工作队列的处理函数:INIT_WORK(&ft5x0x_ts->pen_event_work, ft5x0x_ts_pen_irq_work); ft5x0x_ts->ts_workqueue = create_singlethread_workqueue(dev_name(&client->dev));<br>  
  643. <br>  
  644.      (5)申请系统中断并声明中断处理函数;request_irq(client->irq, ft5x0x_ts_interrupt, IRQF_DISABLED | IRQF_TRIGGER_RISING, "ft5x0x_ts", ft5x0x_ts);<br>  
  645. <br>  
  646.      (6)分配一个输入设备实例,初始化数据并注册进系统:input_dev = input_allocate_device(); input_register_device(input_dev);<br>  
  647. <br>  
  648.      (7)如果定义了earlysuspend,声明其处理函数。earlysuspend用于对触摸屏类设备的电源管理,降低功耗。<br>  
  649. <br>  
  650.      (8)最后对执行过程中可能出现的错误进行处理。<br>  
  651. <br>  
  652.      当有触摸动作时,触摸屏将会执行(5)中声明的中断处理函数ft5x0x_ts_interrupt,具体实现:<br>  
  653. <pre name="code" class="cpp"><pre name="code" class="cpp">static irqreturn_t ft5x0x_ts_interrupt(int irq, void *dev_id)  
  654. {  
  655.     struct ft5x0x_ts_data *ft5x0x_ts = dev_id;  
  656.   
  657.     if (!work_pending(&ft5x0x_ts->pen_event_work))  
  658.         queue_work(ft5x0x_ts->ts_workqueue, &ft5x0x_ts->pen_event_work);  
  659.   
  660.     return IRQ_HANDLED;  
  661. }</pre>  
  662. <pre></pre>  
  663.     可以看到,中断处理就是在判断工作队列在没有被挂起的情况下在触摸时间加入到工作队列中去,等待工作队列处理函数ft5x0x_ts_pen_irq_work()的处理。ft5x0x_ts_pen_irq_work()的实现:<br>  
  664. <pre name="code" class="cpp"><pre name="code" class="cpp">static void ft5x0x_ts_pen_irq_work(struct work_struct *work)  
  665. {  
  666.     int ret = -1;  
  667.   
  668.     ret = ft5x0x_read_data();     
  669.   
  670.     if (ret == 0)  
  671.         ft5x0x_report_value();  
  672. }</pre>  
  673. <pre></pre>  
  674.     概括来说这里只做了两件事:从设备中读取数据 ;把数据上报到输入子系统中。<br>  
  675.      (1)从设备中读取数据由函数ft5x0x_read_data()实现<br>  
  676. <pre name="code" class="cpp">static int ft5x0x_read_data(void)  
  677. {  
  678.     struct ft5x0x_ts_data *data = i2c_get_clientdata(this_client);  
  679.     struct ts_event *event = &data->event;  
  680.     u8 buf[32] = {0};  
  681.     int ret = -1;  
  682.     int status = 0;  
  683.   
  684. #ifdef CONFIG_FT5X0X_MULTITOUCH  
  685.     ret = ft5x0x_i2c_rxdata(buf, 31);  
  686. #else  
  687.     ret = ft5x0x_i2c_rxdata(buf, 7);  
  688. #endif  
  689.     if (ret < 0)  
  690.     {  
  691.         printk("%s read_data i2c_rxdata failed: %d\n", __func__, ret);  
  692.         return ret;  
  693.     }  
  694.   
  695.     memset(event, 0, sizeof(struct ts_event));  
  696.     event->touch_point = buf[2] & 0x07;  
  697.   
  698.     if (event->touch_point == 0)  
  699.     {  
  700.         ft5x0x_ts_inactivate();  
  701.         return 1;   
  702.     }  
  703.   
  704. #ifdef CONFIG_FT5X0X_MULTITOUCH  
  705.     switch (event->touch_point)  
  706.     {  
  707.         case 5:  
  708.             event->x5 = (s16)(buf[0x1b] & 0x0F)<<8 | (s16)buf[0x1c];  
  709.             event->y5 = (s16)(buf[0x1d] & 0x0F)<<8 | (s16)buf[0x1e];  
  710.             status = (s16)((buf[0x1b] & 0xc0) >> 6);  
  711.             event->touch_ID5=(s16)(buf[0x1D] & 0xF0)>>4;  
  712.             if (status == 1) ft5x0x_ts_release();  
  713.   
  714.         case 4:  
  715.             event->x4 = (s16)(buf[0x15] & 0x0F)<<8 | (s16)buf[0x16];  
  716.             event->y4 = (s16)(buf[0x17] & 0x0F)<<8 | (s16)buf[0x18];  
  717.             status = (s16)((buf[0x15] & 0xc0) >> 6);  
  718.             event->touch_ID4=(s16)(buf[0x17] & 0xF0)>>4;  
  719.             if (status == 1) ft5x0x_ts_release();  
  720.   
  721.         case 3:  
  722.             event->x3 = (s16)(buf[0x0f] & 0x0F)<<8 | (s16)buf[0x10];  
  723.             event->y3 = (s16)(buf[0x11] & 0x0F)<<8 | (s16)buf[0x12];  
  724.             status = (s16)((buf[0x0f] & 0xc0) >> 6);  
  725.             event->touch_ID3=(s16)(buf[0x11] & 0xF0)>>4;  
  726.             if (status == 1) ft5x0x_ts_release();  
  727.   
  728.         case 2:  
  729.             event->x2 = (s16)(buf[9] & 0x0F)<<8 | (s16)buf[10];  
  730.             event->y2 = (s16)(buf[11] & 0x0F)<<8 | (s16)buf[12];  
  731.             status = (s16)((buf[0x9] & 0xc0) >> 6);  
  732.             event->touch_ID2=(s16)(buf[0x0b] & 0xF0)>>4;  
  733.             if (status == 1) ft5x0x_ts_release();  
  734.   
  735.         case 1:  
  736.             event->x1 = (s16)(buf[3] & 0x0F)<<8 | (s16)buf[4];  
  737.             event->y1 = (s16)(buf[5] & 0x0F)<<8 | (s16)buf[6];  
  738.             status = (s16)((buf[0x3] & 0xc0) >> 6);  
  739.             event->touch_ID1=(s16)(buf[0x05] & 0xF0)>>4;  
  740.             if (status == 1) ft5x0x_ts_release();  
  741.                     break;  
  742.   
  743.         default:  
  744.             return -1;  
  745.     }  
  746. #else  
  747.     if (event->touch_point == 1)  
  748.     {  
  749.         event->x1 = (s16)(buf[3] & 0x0F)<<8 | (s16)buf[4];  
  750.         event->y1 = (s16)(buf[5] & 0x0F)<<8 | (s16)buf[6];  
  751.     }  
  752. #endif  
  753.     event->pressure = 200;  
  754.   
  755.     dev_dbg(&this_client->dev, "%s: 1:%d %d 2:%d %d \n", __func__,  
  756.         event->x1, event->y1, event->x2, event->y2);  
  757.   
  758.     return 0;  
  759. }</pre>    从设备中读取数据使用ft5x0x_i2c_rxdata()函数:  
  760. <p></p>  
  761. <p align="left"></p>  
  762. <pre name="code" class="cpp"><pre name="code" class="cpp">static int ft5x0x_i2c_rxdata(char *rxdata, int length)  
  763. {  
  764.     int ret;  
  765.   
  766.     struct i2c_msg msgs[] =  
  767.     {  
  768.         {  
  769.             .addr   = this_client->addr,  
  770.             .flags  = 0,  
  771.             .len    = 1,  
  772.             .buf    = rxdata,  
  773.         },  
  774.         {  
  775.             .addr   = this_client->addr,  
  776.             .flags  = I2C_M_RD,  
  777.             .len    = length,  
  778.             .buf    = rxdata,  
  779.         },  
  780.     };  
  781.   
  782.     ret = i2c_transfer(this_client->adapter, msgs, 2);  
  783.     if (ret < 0)  
  784.         pr_err("msg %s i2c read error: %d\n", __func__, ret);  
  785.       
  786.     return ret;  
  787. }</pre>  
  788. <pre></pre>  
  789.     这个函数里面会构建一个i2c_msg msg结构去调用i2c_transfer()函数读取数据,在前面已经说过,i2c_transfer()函数最终调用的就是i2c_algorithm中master_xfer()函数进行实际的数据传输,这个函数在adapter驱动中实现。<br>  
  790. 对读取到的数据处理涉及到IC里面的数据格式,查找芯片的数据手册可以看到寄存器的数据分步。  
  791. <p></p>  
  792. <p align="left"><img src="https://img-my.csdn.net/uploads/201302/06/1360139432_9000.png" alt="" height="437" align="middle" width="681"><br>  
  793. </p>  
  794. <p align="left">    ft5x0x寄存器映射表<br>  
  795. <br>  
  796.      这里为缩小篇幅,只截取一部分寄存器,其余寄存器格式可参照既给出的寄存器信息。 结合图片可以看出,代码先根据是否多点触摸,从寄存器读取数据长度,若是只需要单点触摸就读取7个字节信息,若是多点触摸,就读取31个字节的信息,从代码可以看出最多支持五点触摸。读出信息的第三个字节的低4位为触摸点数,所以event->touch_point = buf[2] & 0x07;判断触摸点数之后便分别计算触摸点的坐标,这里以第一个点为例,其余点可以依此类推。第一个点的坐标信息包含在地址03h~06h中,坐标信息由12bit组成,分布在两个字节中,另外还包含flag标志信息,用于表示触摸点的状态,还包含触摸点的ID识别。<br>  
  797. <br>  
  798.      (2)把数据上报到输入子系统中由函数ft5x0x_report_value()实现</p>  
  799. <p align="left"></p>  
  800. <pre name="code" class="cpp"><pre name="code" class="cpp">static void ft5x0x_report_value(void)  
  801. {  
  802.     struct ft5x0x_ts_data *data = i2c_get_clientdata(this_client);  
  803.     struct ts_event *event = &data->event;  
  804.   
  805. #ifdef CONFIG_FT5X0X_MULTITOUCH  
  806.     switch(event->touch_point)  
  807.     {  
  808.         case 5:  
  809.             input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->touch_ID5);  
  810.             input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);  
  811.             input_report_abs(data->input_dev, ABS_MT_POSITION_X, SCREEN_MAX_X - event->x5);  
  812.             input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y5);  
  813.             input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);  
  814.             input_mt_sync(data->input_dev);  
  815.         case 4:  
  816.             input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->touch_ID4);  
  817.             input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);  
  818.             input_report_abs(data->input_dev, ABS_MT_POSITION_X, SCREEN_MAX_X - event->x4);  
  819.             input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y4);  
  820.             input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);  
  821.             input_mt_sync(data->input_dev);  
  822.         case 3:  
  823.             input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->touch_ID3);  
  824.             input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);  
  825.             input_report_abs(data->input_dev, ABS_MT_POSITION_X, SCREEN_MAX_X - event->x3);  
  826.             input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y3);  
  827.             input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);  
  828.             input_mt_sync(data->input_dev);  
  829.         case 2:  
  830.             input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->touch_ID2);  
  831.             input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);  
  832.             input_report_abs(data->input_dev, ABS_MT_POSITION_X, SCREEN_MAX_X - event->x2);  
  833.             input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y2);  
  834.             input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);  
  835.             input_mt_sync(data->input_dev);  
  836.         case 1:  
  837.             input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->touch_ID1);  
  838.             input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);  
  839.             input_report_abs(data->input_dev, ABS_MT_POSITION_X, SCREEN_MAX_X - event->x1);  
  840.             input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y1);  
  841.             input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);  
  842.             input_mt_sync(data->input_dev);  
  843.         default:  
  844.             break;  
  845.     }  
  846. #else   /* CONFIG_FT5X0X_MULTITOUCH*/  
  847.     if (event->touch_point == 1)  
  848.     {  
  849.         input_report_abs(data->input_dev, ABS_X, SCREEN_MAX_X - event->x1);  
  850.         input_report_abs(data->input_dev, ABS_Y, event->y1);  
  851.         input_report_abs(data->input_dev, ABS_PRESSURE, event->pressure);  
  852.     }  
  853.     input_report_key(data->input_dev, BTN_TOUCH, 1);  
  854. #endif  /* CONFIG_FT5X0X_MULTITOUCH*/  
  855.     input_sync(data->input_dev);  
  856.   
  857.     dev_dbg(&this_client->dev, "%s: 1:%d %d 2:%d %d \n", __func__,  
  858.         event->x1, event->y1, event->x2, event->y2);  
  859. }</pre>  
  860. <pre></pre>  
  861.     因为触摸屏使用的是绝对坐标系,上报数据使用input_report_abs()函数,参数要注意选择合适的事件集。上报玩数据要记得同步,使用input_mt_sync()表示单个手指信息结束,使用input_sync()表示整个触摸动作的结束。<br>  
  862. <br>  
  863.      remove()函数的具体实现是ft5x0x_ts_remove(),做与probe()相反的工作,释放内存。  
  864. <p></p>  
  865. <p align="left"></p>  
  866. <pre name="code" class="cpp"><pre name="code" class="cpp">static int __devexit ft5x0x_ts_remove(struct i2c_client *client)  
  867. {  
  868.     struct ft5x0x_ts_data *ft5x0x_ts = i2c_get_clientdata(client);  
  869.   
  870.     unregister_early_suspend(&ft5x0x_ts->early_suspend);  
  871.     free_irq(client->irq, ft5x0x_ts);  
  872.     input_unregister_device(ft5x0x_ts->input_dev);  
  873.     kfree(ft5x0x_ts);  
  874.     cancel_work_sync(&ft5x0x_ts->pen_event_work);  
  875.     destroy_workqueue(ft5x0x_ts->ts_workqueue);  
  876.     i2c_set_clientdata(client, NULL);  
  877.   
  878.     return 0;  
  879. }</pre>  
  880. <pre></pre>  
  881.     至此,我们从i2c接口及输入子系统的接口清楚了整个触摸屏驱动的结构,并详细分析了触摸屏驱动中的具体实现。
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
目  录 第1章 引言 1 1.1 演进 1 1.2 gnu copyleft 2 1.3 kernel.org 2 1.4 邮件列表和论坛 3 1.5 linux发行版 3 1.6 查看源代码 4 1.7 编译内核 7 1.8 可加载的模块 8 1.9 整装待发 9 第2章 内核 11 2.1 启动过程 11 2.1.1 bios-provided physical ram map 12 2.1.2 758mb lowmem available 14 2.1.3 kernel command line: ro root=/dev/hda1 14 2.1.4 calibrating delay...1197.46 .bogomips (lpj=2394935) 15 2.1.5 checking hlt instruction 16 2.1.6 net: registered protocol family 2 17 2.1.7 freeing initrd memory: 387k freed 17 2.1.8 io scheduler anticipatory registered (default) 18 2.1.9 setting up standard pci resources 18 2.1.10 ext3-fs: mounted filesystem 19 2.1.11 init: version 2.85 booting 19 2.2 内核模式和用户模式 20 2.3 进程上下文和中断上下文 20 2.4 内核定时器 21 2.4.1 hz和jiffies 21 2.4.2 长延时 22 2.4.3 短延时 24 2.4.4 pentium时间戳计数器 24 2.4.5 实时钟 25 2.5 内核中的并发 26 2.5.1 自旋锁和互斥体 26 2.5.2 原子操作 30 2.5.3 读—写锁 31 2.5.4 调试 32 2.6 proc文件系统 32 2.7 内存分配 33 2.8 查看源代码 34 第3章 内核组件 37 3.1 内核线程 37 3.1.1 创建内核线程 37 3.1.2 进程状态和等待队列 41 3.1.3 用户模式辅助程序 42 3.2 辅助接口 43 3.2.1 链表 44 3.2.2 散列链表 49 3.2.3 工作队列 49 3.2.4 通知链 51 3.2.5 完成接口 54 3.2.6 kthread辅助接口 56 3.2.7 错误处理助手 57 3.3 查看源代码 58 第4章 基本概念 61 4.1 设备和驱动程序介绍 61 4.2 中断处理 63 4.2.1 中断上下文 63 4.2.2 分配irq号 64 4.2.3 设备实例:导航杆 65 4.2.4 softirq和tasklet 68 4.3 linux设备模型 71 4.3.1 udev 71 4.3.2 sysfs、kobject和设备类 73 4.3.3 热插拔和冷插拔 76 4.3.4 微码下载 76 4.3.5 模块自动加载 77 4.4 内存屏障 78 4.5 电源管理 79 4.6 查看源代码 79 第5章 字符设备驱动程序 81 5.1 字符设备驱动程序基础 81 5.2 设备实例:系统cmos 82 5.2.1 驱动程序初始化 83 5.2.2 打开与释放 86 5.2.3 数据交换 88 5.2.4 查找 92 5.2.5 控制 94 5.3 检测数据可用性 95 5.3.1 轮询 95 5.3.2 fasync 98 5.4 和并行端口交互 99 5.5 rtc子系统 108 5.6 伪字符驱动程序 109 5.7 混杂驱动程序 110 5.8 字符设备驱动程序注意事项 115 5.9 查看源代码 115 第6章 串行设备驱动程序 118 6.1 层次架构 119 6.2 uart驱动程序 121 6.2.1 设备实例:手机 122 6.2.2 rs-485 132 6.3 tty驱动程序 132 6.4 线路规程 134 6.5 查看源代码 141 第7章 输入设备驱动程序 143 7.1 输入事件驱动程序 144 7.2 输入设备驱动程序 150 7.2.1 serio 150 7.2.2 键盘 150 7.2.3 鼠标 152 7.2.4 触摸控制器 157 7.2.5 加速度传感器 158 7.2.6 输出事件 158 7.3 调试 159 7.4 查看源代码 160 第8章 i2c协议 161 8.1 i2c/smbus是什么 161 8.2 i2c核心 162 8.3 总线事务 164 8.4 设备实例:eeprom 164 8.4.1 初始化 165 8.4.2 探测设备 167 8.4.3 检查适配器的功能 169 8.4.4 访问设备 169 8.4.5 其他函数 170 8.5 设备实例:实时时钟 171 8.6 i2c-dev 174 8.7 使用lm-sensors监控硬件 174 8.8 spi总线 174 8.9 1-wire总线 176 8.10 调试 176 8.11 查看源代码 176 第9章 pcmcia和cf 179 9.1 pcmcia/cf是什么 179 9.2 linux-pcmcia子系统 181 9.3 主机控制器驱动程序 183 9.4 pcmcia核心 183 9.5 驱动程序服务 183 9.6 客户驱动程序 183 9.6.1 数据结构 184 9.6.2 设备实例:pcmcia卡 185 9.7 将零件组装在一起 188 9.8 pcmcia存储 189 9.9 串行pcmcia 189 9.10 调试 191 9.11 查看源代码 191 第10章 pci 193 10.1 pci系列 193 10.2 寻址和识别 195 10.3 访问pci 198 10.3.1 配置区 198 10.3.2 i/o和内存 199 10.4 dma 200 10.5 设备实例:以太网—调制解调器卡 203 10.5.1 初始化和探测 203 10.5.2 数据传输 209 10.6 调试 214 10.7 查看源代码 214 第11章 usb 216 11.1 usb体系架构 216 11.1.1 总线速度 218 11.1.2 主机控制器 218 11.1.3 传输模式 219 11.1.4 寻址 219 11.2 linux-usb子系统 220 11.3 驱动程序的数据结构 221 11.3.1 usb_device结构体 221 11.3.2 urb 222 11.3.3 管道 223 11.3.4 描述符结构 223 11.4 枚举 225 11.5 设备实例:遥测卡 225 11.5.1 初始化和探测过程 226 11.5.2 卡寄存器的访问 230 11.5.3 数据传输 233 11.6 类驱动程序 236 11.6.1 大容量存储设备 236 11.6.2 usb-串行端口转换器 241 11.6.3 人机接口设备 243 11.6.4 蓝牙 243 11.7 gadget驱动程序 243 11.8 调试 244 11.9 查看源代码 245 第12章 视频驱动程序 247 12.1 显示架构 247 12.2 linux视频子系统 249 12.3 显示参数 251 12.4 帧缓冲api 252 12.5 帧缓冲驱动程序 254 12.6 控制台驱动程序 265 12.6.1 设备实例:手机 266 12.6.2 启动logo 270 12.7 调试 270 12.8 查看源代码 271 第13章 音频驱动程序 273 13.1 音频架构 273 13.2 linux声音子系统 275 13.3 设备实例:mp3播放器 277 13.3.1 驱动程序函数和结构体 278 13.3.2 alsa编程 287 13.4 调试 288 13.5 查看源代码 289 第14章 块设备驱动程序 291 14.1 存储技术 291 14.2 linux块i/o层 295 14.3 i/o调度器 295 14.4 块驱动程序数据结构和方法 296 14.5 设备实例:简单存储控制器 298 14.5.1 初始化 299 14.5.2 块设备操作 301 14.5.3 磁盘访问 302 14.6 高级主题 304 14.7 调试 306 14.8 查看源代码 306 第15章 网络接口卡 308 15.1 驱动程序数据结构 308 15.1.1 套接字缓冲区 309 15.1.2 网络设备接口 310 15.1.3 激活 311 15.1.4 数据传输 311 15.1.5 看门狗 311 15.1.6 统计 312 15.1.7 配置 313 15.1.8 总线相关内容 314 15.2 与协议层会话 314 15.2.1 接收路径 314 15.2.2 发送路径 315 15.2.3 流量控制 315 15.3 缓冲区管理和并发控制 315 15.4 设备实例:以太网nic 316 15.5 isa网络驱动程序 321 15.6 atm 321 15.7 网络吞吐量 322 15.7.1 驱动程序性能 322 15.7.2 协议性能 323 15.8 查看源代码 324 第16章 linux无线设备驱动 326 16.1 蓝牙 327 16.1.1 bluez 328 16.1.2 设备实例:cf卡 329 16.1.3 设备实例:usb适配器 330 16.1.4 rfcomm 331 16.1.5 网络 332 16.1.6 hid 334 16.1.7 音频 334 16.1.8 调试 334 16.1.9 关于源代码 334 16.2 红外 335 16.2.1 linux-irda 335 16.2.2 设备实例:超级i/o芯片 337 16.2.3 设备实例:ir dongle 338 16.2.4 ircomm 340 16.2.5 联网 340 16.2.6 irda套接字 341 16.2.7 lirc 341 16.2.8 查看源代码 342 16.3 wifi 343 16.3.1 配置 343 16.3.2 设备驱动程序 346 16.3.3 查看源代码 347 16.4 蜂窝网络 347 16.4.1 gprs 347 16.4.2 cdma 349 16.5 当前趋势 350 第17章 存储技术设备 352 17.1 什么是闪存 352 17.2 linux-mtd子系统 353 17.3 映射驱动程序 353 17.4 nor芯片驱动程序 358 17.5 nand芯片驱动程序 359 17.6 用户模块 361 17.6.1 块设备模拟 361 17.6.2 字符设备模拟 361 17.6.3 jffs2 362 17.6.4 yaffs2 363 17.7 mtd工具 363 17.8 配置mtd 363 17.9 xip 364 17.10 fwh 364 17.11 调试 367 17.12 查看源代码 367 第18章 嵌入式linux 369 18.1 挑战 369 18.2 元器件选择 370 18.3 工具链 371 18.4 bootloader 372 18.5 内存布局 374 18.6 内核移植 375 18.7 嵌入式驱动程序 376 18.7.1 闪存 377 18.7.2 uart 377 18.7.3 按钮和滚轮 378 18.7.4 pcmcia/cf 378 18.7.5 sd/mmc 378 18.7.6 usb 378 18.7.7 rtc 378 18.7.8 音频 378 18.7.9 触摸屏 379 18.7.10 视频 379 18.7.11 cpld/fpga 379 18.7.12 连接性 379 18.7.13 专用领域电子器件 380 18.7.14 更多驱动程序 380 18.8 根文件系统 380 18.8.1 nfs挂载的根文件系统 381 18.8.2 紧凑型中间件 382 18.9 测试基础设施 383 18.10 调试 383 18.10.1 电路板返工 384 18.10.2 调试器 385 第19章 用户空间的驱动程序 386 19.1 进程调度和响应时间 387 19.1.1 原先的调度器 387 19.1.2 o(1)调度器 387 19.1.3 cfs 388 19.1.4 响应时间 388 19.2 访问i/o区域 390 19.3 访问内存区域 393 19.4 用户模式scsi 395 19.5 用户模式usb 397 19.6 用户模式i2c 400 19.7 uio 401 19.8 查看源代码 402 第20章 其他设备和驱动程序 403 20.1 ecc报告 403 20.2 频率调整 407 20.3 嵌入式控制器 408 20.4 acpi 408 20.5 isa与mca 410 20.6 火线 410 20.7 智能输入/输出 411 20.8 业余无线电 411 20.9 voip 411 20.10 高速互联 412 20.10.1 infiniband 413 20.10.2 rapidio 413 20.10.3 光纤通道 413 20.10.4 iscsi 413 第21章 调试设备驱动程序 414 21.1 kdb 414 21.1.1 进入调试器 415 21.1.2 kdb 415 21.1.3 kgdb 417 21.1.4 gdb 420 21.1.5 jtag调试器 421 21.1.6 下载 423 21.2 内核探测器 423 21.2.1 kprobe 423 21.2.2 jprobe 427 21.2.3 返回探针 429 21.2.4 局限性 431 21.2.5 查看源代码 431 21.3 kexec与kdump 431 21.3.1 kexec 432 21.3.2 kdump与kexec协同工作 432 21.3.3 kdump 433 21.3.4 查看源代码 437 21.4 性能剖析 437 21.4.1 利用oprofile剖析内核性能 438 21.4.2 利用gprof剖析应用程序性能 440 21.5 跟踪 441 21.6 ltp 444 21.7 uml 444 21.8 诊断工具 444 21.9 内核修改配置选项 444 21.10 测试设备 445 第22章 维护与发布 446 22.1 代码风格 446 22.2 修改标记 446 22.3 版本控制 447 22.4 一致性检查 447 22.5 构建脚本 448 22.6 可移植代码 450 第23章 结束语 451 23.1 流程一览表 451 23.2 下一步该做什么 452 附录a linux汇编 453 附录b linux与bios 457 附录c seq文件 461

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值