EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
3、实现i2c设备驱动中的i2c_driver接口,由结构i2c_client中的数据填充,覆盖图中的driver驱动层。主要数据结构 [hide]- struct i2c_client {
- unsigned int flags; /* div., see below */
- unsigned short addr; /* chip address - NOTE: 7bit */
- /* addresses are stored in the */
- /* _LOWER_ 7 bits */
- struct i2c_adapter *adapter; /* the adapter we sit on */
- struct i2c_driver *driver; /* and our access routines */
- int usage_count; /* How many accesses currently */
- /* to the client */
- struct device dev; /* the device structure */
- struct list_head list;
- char name[I2C_NAME_SIZE];
- struct completion released;
- }; 9 {$ v1 A1 @1 z
这个结构体中的内容是描述设备的。包含了芯片地址,设备名称,设备使用的中断号,设备所依附的控制器,设备所依附的驱动等内容。
: ], }8 Y5 I$ _6 U- d. J
4、实现i2c设备所对应的具体device的驱动。覆盖图中的driver驱动层。主要数据结构struct i2c_driver - struct i2c_driver {
- int id;
- unsigned int class;
-
- int (*attach_adapter)(struct i2c_adapter *);
- int (*detach_adapter)(struct i2c_adapter *);
-
- int (*detach_client)(struct i2c_client *);
-
- int (*command)(struct i2c_client *client,unsigned int cmd, void *arg);
- struct device_driver driver;
- struct list_head list;
- };
/ Y4 e1 P) D+ ]& b
这个结构体对应了驱动方法,重要成员函数有probe,remove,suspend,resume与中断处理函数,也是我们需要是实现的函数。另外包括一个重要的数据结构: struct i2c_device_id *id_table; 如果驱动可以支持好几个设备,那么这里面就要包含这些设备的ID。 第一层和第二层是i2c总线驱动,属于芯片内部的驱动,在linux驱动架构中已经实现,不需要我们编写或更改。第三第四属于i2c设备驱动,需要我们根据内核提供的接口去完成具体的代码。 另外就是i2c_core层,起到了承上启下的作用,包含在开发中需要用到的核心函数。源代码位于drivers/i2c/i2c-core.c中。在这里可以看到几个重要的函数。 (1)增加/删除i2c控制器的函数 - int i2c_add_adapter(struct i2c_adapter *adapter)
- int i2c_del_adapter(struct i2c_adapter *adap) 0 A) s- t$ P0 G& x. x
(2)增加/删除i2c设备的函数 - struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);
- void i2c_unregister_device(struct i2c_client *client) 4 o9 A) M2 x3 X7 f/ ^2 k! Z, c% o
(3)增加/删除设备驱动的函数 - int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
- void i2c_del_driver(struct i2c_driver *driver) - l( t+ U# Z% s4 U+ N
(4)I2C传输、发送和接收函数 - int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
- int i2c_master_send(struct i2c_client *client,const char *buf ,int count);
- int i2c_master_recv(struct i2c_client *client, char *buf ,int count); 8 c% J' \" ^; D) P
send和receive分别都调用了transfer函数,而transfer也不是直接和硬件交互,而是调用algorithm中的master_xfer()函数。 二、linux input输入子系统架构 linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(Event Handler)、输入子系统核心层(Input Core)和输入子系统设备驱动层。输入设备不止包括触摸屏,还有很多其他种类的输入设备,如键盘,鼠标等等,所以linux内核已经把各式各样的输入设备进行了抽象,提供了统一的接口,让我们把设备接入到系统中。而我们要做的工作就是申请一个输入设备结构体对象,并填充其里面的数据。 1,输入子系统事件处理层:与userspace衔接,提供处理设备事件的接口,以模块编译进内核,数据结构为input_handler - struct input_handler {
-
- void *private;
-
- void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
- bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
- bool (*match)(struct input_handler *handler, struct input_dev *dev);
- int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
- void (*disconnect)(struct input_handle *handle);
- void (*start)(struct input_handle *handle);
-
- const struct file_operations *fops;
- int minor;
- const char *name;
-
- const struct input_device_id *id_table;
-
- struct list_head h_list;
-
- struct list_head node;
- }; ; V+ Y- ^1 X; L: V) X: I1 F
其中,struct input_device_id在后面讲到handler与input_dev匹配时将用到,还分别用到match与connect函数,具体过程下面再说。! f m' P. p$ T8 {9 Q( m" o1 @
struct file_operations提供用户空间的访问方法,这里的evdev形式的fops为: - static const struct file_operations evdev_fops = {
- .owner = THIS_MODULE,
- .read = evdev_read,
- .write = evdev_write,
- .poll = evdev_poll,
- .open = evdev_open,
- .release = evdev_release,
- .unlocked_ioctl = evdev_ioctl,
- #ifdef CONFIG_COMPAT
- .compat_ioctl = evdev_ioctl_compat,
- #endif
- .fasync = evdev_fasync,
- .flush = evdev_flush,
- .llseek = no_llseek,
- }; - c$ g N' f0 f" P- V; P$ d+ M- x' h u
6 h0 h$ o- @0 u( H. H/ k) m% ~. N! I9 R8 E, `) R
event函数记录输入事件的值,驱动input_report_xxx()上报的值最终会通过这个函数保存到内核区,供用户空间访问,存储这个数值的数据结构为input_event. - struct input_event {
- struct timeval time;
- __u16 type;
- __u16 code;
- __s32 value;
- }; 1 G* K0 M% [6 X. s0 p3 r% f9 z
2,输入子系统核心层: 提供构建输入设备核心方法实现与数据结构定义,在文件inpu.h与input.c文件,包括% o) y& L/ ?% K. R
- c: w/ G% P! t) Q
(1)注册/注销设备0 l1 D3 R5 d; C3 t" ~& y
9 {5 l" G7 U0 Z, C
input_register_device()% A. `0 ?: E) _" J
input_unregister_device()
# c, ~ I2 ~2 h: i$ H
1 Y' V, q7 A1 E( J+ m (2)注册/注销handler input_register_handler()! B9 t; T9 u2 d& L1 C# T0 t( d% J7 H
input_unregister_handler()7 w; J! w$ S; a7 A3 h
0 H6 Y/ C, o. M' d/ W" G8 a: {# a2 P
(3)注册注销handle
k+ D/ Z5 v: h8 a6 u3 ~( T/ T6 e/ v; O+ Z: w R8 n6 Z% J* H4 u
input_register_handle()9 j7 A; [5 X' z5 M
input_unregister_handle()
! _9 {# M9 D3 J! `7 H6 T, B' I" w+ f+ Z
handle是device与handler链接的纽带。其定义为 - <pre name="code" class="cpp">struct input_handle {
-
- void *private;
-
- int open;
- const char *name;
-
- struct input_dev *dev;
- struct input_handler *handler;
-
- struct list_head d_node;
- struct list_head h_node;
- };</pre>
- <pre></pre>
- 详细过程如图所示:
- <p></p>
- <p align="left"><img src="https://img-my.csdn.net/uploads/201302/06/1360140115_9994.jpg" alt=""><br>
- </p>
- <p align="left"> 当新生成一个input_dev时,需要匹配一个input_handler,即特定输入设备相应的处理方法.使用input_attach_handler()<br>
- </p>
- <p align="left"></p>
- <pre name="code" class="cpp"><pre name="code" class="cpp">static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
- {
- const struct input_device_id *id;
- int error;
-
- id = input_match_device(handler, dev);
- if (!id)
- return -ENODEV;
-
- error = handler->connect(handler, dev, id);
- if (error && error != -ENODEV)
- pr_err("failed to attach handler %s to device %s, error: %d\n",
- handler->name, kobject_name(&dev->dev.kobj), error);
-
- return error;
- }</pre>
- <pre></pre>
- (1) 使用input_match_device()函数匹配,采用dev匹配handler,handler被匹配。首先调用input_match_device()函数。
- <p></p>
- <p align="left"></p>
- <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)
- {
- const struct input_device_id *id;
- int i;
-
- for (id = handler->id_table; id->flags || id->driver_info; id++) {
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
- if (id->bustype != dev->id.bustype)
- continue;
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
- if (id->vendor != dev->id.vendor)
- continue;
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
- if (id->product != dev->id.product)
- continue;
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
- if (id->version != dev->id.version)
- continue;
-
- MATCH_BIT(evbit, EV_MAX);
- MATCH_BIT(keybit, KEY_MAX);
- MATCH_BIT(relbit, REL_MAX);
- MATCH_BIT(absbit, ABS_MAX);
- MATCH_BIT(mscbit, MSC_MAX);
- MATCH_BIT(ledbit, LED_MAX);
- MATCH_BIT(sndbit, SND_MAX);
- MATCH_BIT(ffbit, FF_MAX);
- MATCH_BIT(swbit, SW_MAX);
-
- if (!handler->match || handler->match(handler, dev))
- return id;
- }
-
- return NULL;
- }</pre>
- <p><br>
- </p>
- <pre></pre>
- handler有某一个flag设置了,input设备对应的条件必须具备。使用MATCH_BIT宏进行匹配。<br>
- <br>
- (2)匹配成功,调用handler->connect()函数关联.触摸屏是事件设备,所以将匹配一个input_handler evdev_handler,对应的函数为evdev_connect,代码存放在evdev.c中。
- <p></p>
- <p align="left"></p>
- <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)
- {
- struct evdev *evdev;
- int minor;
- int error;
-
- for (minor = 0; minor < EVDEV_MINORS; minor++)
- if (!evdev_table[minor])
- break;
-
- if (minor == EVDEV_MINORS) {
- pr_err("no more free evdev devices\n");
- return -ENFILE;
- }
-
- evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
- if (!evdev)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&evdev->client_list);
- spin_lock_init(&evdev->client_lock);
- mutex_init(&evdev->mutex);
- init_waitqueue_head(&evdev->wait);
-
- dev_set_name(&evdev->dev, "event%d", minor);
- evdev->exist = true;
- evdev->minor = minor;
-
- evdev->handle.dev = input_get_device(dev);
- evdev->handle.name = dev_name(&evdev->dev);
- evdev->handle.handler = handler;
- evdev->handle.private = evdev;
-
- evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
- evdev->dev.class = &input_class;
- evdev->dev.parent = &dev->dev;
- evdev->dev.release = evdev_free;
- device_initialize(&evdev->dev);
-
- error = input_register_handle(&evdev->handle);
- if (error)
- goto err_free_evdev;
-
- error = evdev_install_chrdev(evdev);
- if (error)
- goto err_unregister_handle;
-
- error = device_add(&evdev->dev);
- if (error)
- goto err_cleanup_evdev;
-
- return 0;
-
- err_cleanup_evdev:
- evdev_cleanup(evdev);
- err_unregister_handle:
- input_unregister_handle(&evdev->handle);
- err_free_evdev:
- put_device(&evdev->dev);
- return error;
- }</pre>
- <pre></pre>
- (1)定义一个比输入设备更为具体的设备——事件设备evdev,触摸屏接入到用户空间也就是事件设备。
- <p></p>
- <p align="left"></p>
- <pre name="code" class="cpp"><pre name="code" class="cpp">struct evdev {
- int open;
- int minor;
- struct input_handle handle;
- wait_queue_head_t wait;
- struct evdev_client __rcu *grab;
- struct list_head client_list;
- spinlock_t client_lock; /* protects client_list */
- struct mutex mutex;
- struct device dev;
- bool exist;
- };</pre>
- <p><br>
- </p>
- <pre></pre>
- <p></p>
- <p align="left"> (2)初始化里面的数据。</p>
- <p align="left"> (3)设置evdev里面的值.</p>
- <p align="left"> (4)注册handle.</p>
- <p align="left"> (5)因为evdev_handler可以匹配多个evdev,所以有一个数据是专门用来存储这些evdev的,所以用evdev_install_chrdev()函数把它放入数组。</p>
- <pre name="code" class="cpp"><pre name="code" class="cpp">static int evdev_install_chrdev(struct evdev *evdev)
- {
- /*
- * No need to do any locking here as calls to connect and
- * disconnect are serialized by the input core
- */
- evdev_table[evdev->minor] = evdev;
- return 0;
- }</pre>
- <pre></pre>
- (6)把设备加入内核。
- <p align="left"> 3,输入子系统设备驱动层:是真正设备驱动层,有具体的硬件接口,数据结构为input_dev</p>
- <p align="left"></p>
- <pre name="code" class="cpp"><pre name="code" class="cpp">struct input_dev {
- const char *name;
- const char *phys;
- const char *uniq;
- struct input_id id;
-
- unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
-
- unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
- unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
- unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
- unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
- unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
- unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
- unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
- unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
- unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
-
- unsigned int hint_events_per_packet;
-
- unsigned int keycodemax;
- unsigned int keycodesize;
- void *keycode;
-
- int (*setkeycode)(struct input_dev *dev,const struct input_keymap_entry *ke,unsigned int *old_keycode);
- int (*getkeycode)(struct input_dev *dev,struct input_keymap_entry *ke);
-
- struct ff_device *ff;
-
- unsigned int repeat_key;
- struct timer_list timer;
-
- int rep[REP_CNT];
-
- struct input_mt *mt;
-
- struct input_absinfo *absinfo;
-
- unsigned long key[BITS_TO_LONGS(KEY_CNT)];
- unsigned long led[BITS_TO_LONGS(LED_CNT)];
- unsigned long snd[BITS_TO_LONGS(SND_CNT)];
- unsigned long sw[BITS_TO_LONGS(SW_CNT)];
-
- int (*open)(struct input_dev *dev);
- void (*close)(struct input_dev *dev);
- int (*flush)(struct input_dev *dev, struct file *file);
- int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
-
- struct input_handle __rcu *grab;
-
- spinlock_t event_lock;
- struct mutex mutex;
-
- unsigned int users;
- bool going_away;
-
- struct device dev;
-
- struct list_head h_list;
- struct list_head node;
-
- unsigned int num_vals;
- unsigned int max_vals;
- struct input_value *vals;
- };</pre>
- <pre></pre>
- 这里包含了所有可能输入设备的数据域, 我们驱动要定义的输入设备的参数就包含在这个数据结构中。<br>
- <br>
- 4,把设备注册进输入子系统里一般要经过以下步骤:<br>
- <br>
- (1)申请一个输入设备,定义事件集合。关于输入事件的介绍可以看这里:Linux 内核点触摸接口协议<br>
- <br>
- (2)上报从设备中读到的数据。可使用函数
- <p></p>
- <p align="left"></p>
- <pre name="code" class="cpp"><pre name="code" class="cpp">void input_report_key(struct input_dev *dev, unsigned int code, int value); //上报按键事件
- void input_report_rel(struct input_dev *dev, unsigned int code, int value); //上报相对坐标事件
- void input_report_abs(struct input_dev *dev, unsigned int code, int value); //上报绝对坐标事件</pre>
- <pre></pre>
- 三、基于OMAP FT5x0x驱动源码分析<br>
- <br>
- 1、板级初始化代码<br>
- <br>
- 板级初始化代码与具体的平台有关,负责相应平台的初始化工作,这里使用的pandaboard平台的代码,在目录arch/ARM/mach-omap2/board-omap4panda.c中。触摸屏设备当然需要在这里先声明并注册进系统。具体代码如下:
- <p></p>
- <p align="left"></p>
- <pre name="code" class="cpp"><pre name="code" class="cpp">static struct i2c_board_info __initdata panda_i2c2_boardinfo[] = {
- {
- I2C_BOARD_INFO("ft5x06_ts", 0x38),
- .irq = OMAP_GPIO_IRQ(34),
- },
- };
- omap_register_i2c_bus(2, 100, panda_i2c2_boardinfo,ARRAY_SIZE(panda_i2c2_boardinfo));</pre>
- <pre></pre>
- <p></p>
- <p align="left"> 声明一个i2c_board_info结构的数组,这个数组包含pandaboard板上i2c接口的信息。先使用宏I2C_BOARD_INFO宏声明设备名为ft5x06_ts,地址为0x38。要说明的是这里的地址并不是指处理器中i2c寄存器的硬件地址,而是在系统加载后,位于/sys/bus/i2c/devices下的设备名。再申请这个i2c接口的中断口为GPIO34,这从板的硬件连接图可以看出。<br>
- <br>
- 然后把这个结构体注册进i2c总线,这里跟具体的平台有关,使用omap_register_i2c_bus函数注册。函数定义在arch/arm/plat-omap/i2c.c。尝试跟踪中断信息存储的位置,我们首先看omap_register_i2c_bus()</p>
- <p align="left"></p>
- <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)
- {
- int err;
-
- BUG_ON(bus_id < 1 || bus_id > omap_i2c_nr_ports());
-
- if (info) {
- err = i2c_register_board_info(bus_id, info, len);
- if (err)
- return err;
- }
-
- if (!i2c_pdata[bus_id - 1].clkrate)
- i2c_pdata[bus_id - 1].clkrate = clkrate;
-
- i2c_pdata[bus_id - 1].clkrate &= ~OMAP_I2C_CMDLINE_SETUP;
-
- return omap_i2c_add_bus(bus_id);
- }</pre>
- <pre></pre>
- 信息info跳入i2c_register_board_info()函数
- <p></p>
- <p align="left"></p>
- <pre name="code" class="cpp">i2c_register_board_info(int busnum,struct i2c_board_info const *info, unsigned len)
- {
- int status;
-
- down_write(&__i2c_board_lock);
-
- /* dynamic bus numbers will be assigned after the last static one */
- if (busnum >= __i2c_first_dynamic_bus_num)
- __i2c_first_dynamic_bus_num = busnum + 1;
-
- for (status = 0; len; len--, info++) {
- struct i2c_devinfo *devinfo;
-
- devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
- if (!devinfo) {
- pr_debug("i2c-core: can't register boardinfo!\n");
- status = -ENOMEM;
- break;
- }
-
- devinfo->busnum = busnum;
- devinfo->board_info = *info;
- list_add_tail(&devinfo->list, &__i2c_board_list);
- }
-
- up_write(&__i2c_board_lock);
-
- return status;
- }</pre> 可以看到用链表存储这些设别信息,存储节点为i2c_decinfo结构
- <p></p>
- <p align="left"></p>
- <pre name="code" class="cpp"><pre name="code" class="cpp">struct i2c_devinfo {
- struct list_head list;
- int busnum;
- struct i2c_board_info board_info;
- };</pre>
- <pre></pre>
- 这个结构会在i2c_scan_static_board_info()函数被引用
- <p></p>
- <p align="left"></p>
- <pre name="code" class="cpp"><pre name="code" class="cpp">static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
- {
- struct i2c_devinfo *devinfo;
-
- down_read(&__i2c_board_lock);
- list_for_each_entry(devinfo, &__i2c_board_list, list) {
- 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);
- }</pre>
- <pre></pre>
- 这个函数遍历设备信息链表,找出与adapter匹配的设备,并使用i2c_new_device()添加进去。这个函数在注册adapter时被i2c_register_adapter()调用。
- <p></p>
- <p align="left"></p>
- <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)
- {
- struct i2c_client *client;
- int status;
-
- client = kzalloc(sizeof *client, GFP_KERNEL);
- if (!client)
- return NULL;
-
- client->adapter = adap;
-
- 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;
-
- 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;
- client->dev.bus = &i2c_bus_type;
- 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;
- }</pre>
- <pre></pre>
- 这里创建了i2c_client对象,并把devinfo内的信息存储在它里面,下面设备驱动调用的probe函数传进的i2c_client就是这里的client。<br>
- <br>
- 2、设备与驱动绑定<br>
- <br>
- 设备与驱动绑定的代码位于目录drivers/input/touchscreen/ft5x0x.c中,这个文件包含了i2c设备的具体代码,以模块的形式编译进内核。
- <p></p>
- <p align="left"></p>
- <pre name="code" class="cpp"><pre name="code" class="cpp">static const struct i2c_device_id ft5x0x_ts_id[] =
- {
- { FT5X0X_NAME, 0 },{ }
- };
- MODULE_DEVICE_TABLE(i2c, ft5x0x_ts_id);
-
- static struct i2c_driver ft5x0x_ts_driver =
- {
- .probe = ft5x0x_ts_probe,
- .remove = __devexit_p(ft5x0x_ts_remove),
- .id_table = ft5x0x_ts_id,
- .driver =
- {
- .name = FT5X0X_NAME,
- .owner = THIS_MODULE,
- },
- };
-
- static int __init ft5x0x_ts_init(void)
- {
- return i2c_add_driver(&ft5x0x_ts_driver);
- }
-
- static void __exit ft5x0x_ts_exit(void)
- {
- i2c_del_driver(&ft5x0x_ts_driver);
- }
-
- module_init(ft5x0x_ts_init);
- module_exit(ft5x0x_ts_exit);
-
- MODULE_AUTHOR("<wenfs@Focaltech-systems.com>");
- MODULE_DESCRIPTION("FocalTech ft5x0x TouchScreen driver");
- MODULE_LICENSE("GPL");</pre>
- <pre></pre>
- <p></p>
- <p align="left"> 首先建立设备与驱动的映射表ft5x0x_ts_id,通过宏MODULE_DEVICE_TABLE关联起来,MODULE_DEVICE_TABLE第一个参数表示这个设备id是i2c总线上了,第二个参数是指设备,当系统找到这个设备时,就会通过FT5X0X_NAME把设备与这个模块关联起来。<br>
- 这是个i2c驱动模块,当然需要定义一个i2c_driver结构体来标识这个驱动,并说明实现的驱动函数有probe()和remove()函数。<br>
- <br>
- 之后就是添加模块初始化和退出函数,分别是在模块初始化时调用i2c_add_driver()宏加入i2c驱动,i2c_add_driver()实际上调用到的是 i2c_register_driver()函数。</p>
- <p align="left"></p>
- <pre name="code" class="cpp"><pre name="code" class="cpp">int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
- {
- int res;
-
- /* Can't register until after driver model init */
- if (unlikely(WARN_ON(!i2c_bus_type.p)))
- return -EAGAIN;
-
- /* add the driver to the list of i2c drivers in the driver core */
- driver->driver.owner = owner;
- driver->driver.bus = &i2c_bus_type;
-
- /* When registration returns, the driver core
- * will have called probe() for all matching-but-unbound devices.
- */
- res = driver_register(&driver->driver);
- if (res)
- return res;
-
- /* Drivers should switch to dev_pm_ops instead. */
- if (driver->suspend)
- pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
- driver->driver.name);
- if (driver->resume)
- pr_warn("i2c-core: driver [%s] using legacy resume method\n",
- driver->driver.name);
-
- pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
-
- INIT_LIST_HEAD(&driver->clients);
- /* Walk the adapters that are already present */
- i2c_for_each_dev(driver, __process_new_driver);
-
- return 0;
- }</pre>
- <pre></pre>
- <p></p>
- <p align="left"> 在模块退出时调用i2c_del_driver删除i2c驱动。</p>
- <p align="left"> 3、设备驱动程序实现<br>
- 从上面i2c_driver结构可以看出,ft5x0x实现了驱动的两个接口函数,分别为probe()和remove()函数。<br>
- probe()具体实现函数为ft5x0x_ts_probe(),这个函数在内核初始化时,如果设备和驱动匹配,将调用,实现i2c设备的初始化,具体实现:</p>
- <p align="left"></p>
- <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)
- {
- struct ft5x0x_ts_data *ft5x0x_ts;
- struct input_dev *input_dev;
- int err = 0;
- int rev_id = 0;
-
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- err = -ENODEV;
- goto err_out;
- }
-
- ft5x0x_ts = kzalloc(sizeof(*ft5x0x_ts), GFP_KERNEL);
- if (!ft5x0x_ts) {
- err = -ENOMEM;
- goto err_free_mem;
- }
-
- this_client = client;
- i2c_set_clientdata(client, ft5x0x_ts);
-
- rev_id = i2c_smbus_read_byte_data(client, FT5X0X_REG_FT5201ID);
- if (rev_id != FT5X06_ID)
- {
- err = -ENODEV;
- dev_err(&client->dev, "failed to probe FT5X0X touchscreen device\n");
- goto err_free_mem;
- }
-
- INIT_WORK(&ft5x0x_ts->pen_event_work, ft5x0x_ts_pen_irq_work);
- ft5x0x_ts->ts_workqueue = create_singlethread_workqueue(dev_name(&client->dev));
- if (!ft5x0x_ts->ts_workqueue)
- {
- err = -ESRCH;
- goto err_free_thread;
- }
-
- err = request_irq(client->irq, ft5x0x_ts_interrupt, IRQF_DISABLED | IRQF_TRIGGER_RISING, "ft5x0x_ts", ft5x0x_ts);
- if (err < 0)
- {
- dev_err(&client->dev, "request irq failed\n");
- goto err_free_irq;
- }
-
- input_dev = input_allocate_device();
- if (!input_dev)
- {
- err = -ENOMEM;
- dev_err(&client->dev, "failed to allocate input device\n");
- goto err_free_input;
- }
-
- ft5x0x_ts->input_dev = input_dev;
-
- #ifdef CONFIG_FT5X0X_MULTITOUCH
- set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);
- set_bit(ABS_MT_POSITION_X, input_dev->absbit);
- set_bit(ABS_MT_POSITION_Y, input_dev->absbit);
- set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);
- set_bit(ABS_PRESSURE, input_dev->absbit);
-
- input_set_abs_params(input_dev,
- ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);
- input_set_abs_params(input_dev,
- ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);
- input_set_abs_params(input_dev,
- ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0);
- input_set_abs_params(input_dev,
- ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);
- #else
- set_bit(ABS_X, input_dev->absbit);
- set_bit(ABS_Y, input_dev->absbit);
- set_bit(ABS_PRESSURE, input_dev->absbit);
- set_bit(BTN_TOUCH, input_dev->keybit);
-
- input_set_abs_params(input_dev, ABS_X, 0, SCREEN_MAX_X, 0, 0);
- input_set_abs_params(input_dev, ABS_Y, 0, SCREEN_MAX_Y, 0, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE, 0, PRESS_MAX, 0 , 0);
- #endif
-
- set_bit(EV_ABS, input_dev->evbit);
- set_bit(EV_KEY, input_dev->evbit);
-
- input_dev->name = FT5X0X_NAME;
- err = input_register_device(input_dev);
- if (err)
- {
- dev_err(&client->dev, "failed to register input device: %s\n",
- dev_name(&client->dev));
- goto err_free_input;
- }
-
- #ifdef CONFIG_HAS_EARLYSUSPEND
- ft5x0x_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
- ft5x0x_ts->early_suspend.suspend = ft5x0x_ts_suspend;
- ft5x0x_ts->early_suspend.resume = ft5x0x_ts_resume;
- register_early_suspend(&ft5x0x_ts->early_suspend);
- #endif
-
- return 0;
-
- err_free_input:
- input_free_device(input_dev);
- err_free_irq:
- free_irq(client->irq, ft5x0x_ts);
- err_free_thread:
- cancel_work_sync(&ft5x0x_ts->pen_event_work);
- destroy_workqueue(ft5x0x_ts->ts_workqueue);
- i2c_set_clientdata(client, NULL);
- err_free_mem:
- kfree(ft5x0x_ts);
- err_out:
- return err;
- }</pre>
- <pre></pre>
- 这个函数代码比较长,但是可以分部分去看,每一部分对应不同的初始化工作.大概可以分为这几个部分:<br>
- <br>
- (1)检测适配器是否支持I2C_FUNC_I2C的通信方式 :i2c_check_functionality(client->adapter, I2C_FUNC_I2C)<br>
- <br>
- (2)申请内存存储驱动的数据:ft5x0x_ts = kzalloc(sizeof(*ft5x0x_ts), GFP_KERNEL);并把驱动数据赋值给系统传过来的i2c_client数据:this_client = client; i2c_set_clientdata(client, ft5x0x_ts);<br>
- <br>
- (3)验证将要通信的设备ID:rev_id = i2c_smbus_read_byte_data(client, FT5X0X_REG_FT5201ID);<br>
- <br>
- (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>
- <br>
- (5)申请系统中断并声明中断处理函数;request_irq(client->irq, ft5x0x_ts_interrupt, IRQF_DISABLED | IRQF_TRIGGER_RISING, "ft5x0x_ts", ft5x0x_ts);<br>
- <br>
- (6)分配一个输入设备实例,初始化数据并注册进系统:input_dev = input_allocate_device(); input_register_device(input_dev);<br>
- <br>
- (7)如果定义了earlysuspend,声明其处理函数。earlysuspend用于对触摸屏类设备的电源管理,降低功耗。<br>
- <br>
- (8)最后对执行过程中可能出现的错误进行处理。<br>
- <br>
- 当有触摸动作时,触摸屏将会执行(5)中声明的中断处理函数ft5x0x_ts_interrupt,具体实现:<br>
- <pre name="code" class="cpp"><pre name="code" class="cpp">static irqreturn_t ft5x0x_ts_interrupt(int irq, void *dev_id)
- {
- struct ft5x0x_ts_data *ft5x0x_ts = dev_id;
-
- if (!work_pending(&ft5x0x_ts->pen_event_work))
- queue_work(ft5x0x_ts->ts_workqueue, &ft5x0x_ts->pen_event_work);
-
- return IRQ_HANDLED;
- }</pre>
- <pre></pre>
- 可以看到,中断处理就是在判断工作队列在没有被挂起的情况下在触摸时间加入到工作队列中去,等待工作队列处理函数ft5x0x_ts_pen_irq_work()的处理。ft5x0x_ts_pen_irq_work()的实现:<br>
- <pre name="code" class="cpp"><pre name="code" class="cpp">static void ft5x0x_ts_pen_irq_work(struct work_struct *work)
- {
- int ret = -1;
-
- ret = ft5x0x_read_data();
-
- if (ret == 0)
- ft5x0x_report_value();
- }</pre>
- <pre></pre>
- 概括来说这里只做了两件事:从设备中读取数据 ;把数据上报到输入子系统中。<br>
- (1)从设备中读取数据由函数ft5x0x_read_data()实现<br>
- <pre name="code" class="cpp">static int ft5x0x_read_data(void)
- {
- struct ft5x0x_ts_data *data = i2c_get_clientdata(this_client);
- struct ts_event *event = &data->event;
- u8 buf[32] = {0};
- int ret = -1;
- int status = 0;
-
- #ifdef CONFIG_FT5X0X_MULTITOUCH
- ret = ft5x0x_i2c_rxdata(buf, 31);
- #else
- ret = ft5x0x_i2c_rxdata(buf, 7);
- #endif
- if (ret < 0)
- {
- printk("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
- return ret;
- }
-
- memset(event, 0, sizeof(struct ts_event));
- event->touch_point = buf[2] & 0x07;
-
- if (event->touch_point == 0)
- {
- ft5x0x_ts_inactivate();
- return 1;
- }
-
- #ifdef CONFIG_FT5X0X_MULTITOUCH
- switch (event->touch_point)
- {
- case 5:
- event->x5 = (s16)(buf[0x1b] & 0x0F)<<8 | (s16)buf[0x1c];
- event->y5 = (s16)(buf[0x1d] & 0x0F)<<8 | (s16)buf[0x1e];
- status = (s16)((buf[0x1b] & 0xc0) >> 6);
- event->touch_ID5=(s16)(buf[0x1D] & 0xF0)>>4;
- if (status == 1) ft5x0x_ts_release();
-
- case 4:
- event->x4 = (s16)(buf[0x15] & 0x0F)<<8 | (s16)buf[0x16];
- event->y4 = (s16)(buf[0x17] & 0x0F)<<8 | (s16)buf[0x18];
- status = (s16)((buf[0x15] & 0xc0) >> 6);
- event->touch_ID4=(s16)(buf[0x17] & 0xF0)>>4;
- if (status == 1) ft5x0x_ts_release();
-
- case 3:
- event->x3 = (s16)(buf[0x0f] & 0x0F)<<8 | (s16)buf[0x10];
- event->y3 = (s16)(buf[0x11] & 0x0F)<<8 | (s16)buf[0x12];
- status = (s16)((buf[0x0f] & 0xc0) >> 6);
- event->touch_ID3=(s16)(buf[0x11] & 0xF0)>>4;
- if (status == 1) ft5x0x_ts_release();
-
- case 2:
- event->x2 = (s16)(buf[9] & 0x0F)<<8 | (s16)buf[10];
- event->y2 = (s16)(buf[11] & 0x0F)<<8 | (s16)buf[12];
- status = (s16)((buf[0x9] & 0xc0) >> 6);
- event->touch_ID2=(s16)(buf[0x0b] & 0xF0)>>4;
- if (status == 1) ft5x0x_ts_release();
-
- case 1:
- event->x1 = (s16)(buf[3] & 0x0F)<<8 | (s16)buf[4];
- event->y1 = (s16)(buf[5] & 0x0F)<<8 | (s16)buf[6];
- status = (s16)((buf[0x3] & 0xc0) >> 6);
- event->touch_ID1=(s16)(buf[0x05] & 0xF0)>>4;
- if (status == 1) ft5x0x_ts_release();
- break;
-
- default:
- return -1;
- }
- #else
- if (event->touch_point == 1)
- {
- event->x1 = (s16)(buf[3] & 0x0F)<<8 | (s16)buf[4];
- event->y1 = (s16)(buf[5] & 0x0F)<<8 | (s16)buf[6];
- }
- #endif
- event->pressure = 200;
-
- dev_dbg(&this_client->dev, "%s: 1:%d %d 2:%d %d \n", __func__,
- event->x1, event->y1, event->x2, event->y2);
-
- return 0;
- }</pre> 从设备中读取数据使用ft5x0x_i2c_rxdata()函数:
- <p></p>
- <p align="left"></p>
- <pre name="code" class="cpp"><pre name="code" class="cpp">static int ft5x0x_i2c_rxdata(char *rxdata, int length)
- {
- int ret;
-
- struct i2c_msg msgs[] =
- {
- {
- .addr = this_client->addr,
- .flags = 0,
- .len = 1,
- .buf = rxdata,
- },
- {
- .addr = this_client->addr,
- .flags = I2C_M_RD,
- .len = length,
- .buf = rxdata,
- },
- };
-
- ret = i2c_transfer(this_client->adapter, msgs, 2);
- if (ret < 0)
- pr_err("msg %s i2c read error: %d\n", __func__, ret);
-
- return ret;
- }</pre>
- <pre></pre>
- 这个函数里面会构建一个i2c_msg msg结构去调用i2c_transfer()函数读取数据,在前面已经说过,i2c_transfer()函数最终调用的就是i2c_algorithm中maste/size]
- int ret = -1;
- int status = 0;
-
- #ifdef CONFIG_FT5X0X_MULTITOUCH
- ret = ft5x0x_i2c_rxdata(buf, 31);
- #else
- ret = ft5x0x_i2c_rxdata(buf, 7);
- #endif
- if (ret < 0)
- {
- printk("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
- return ret;
- }
-
- memset(event, 0, sizeof(struct ts_event));
- event->touch_point = buf[2] & 0x07;
-
- if (event->touch_point == 0)
- {
- ft5x0x_ts_inactivate();
- return 1;
- }
-
- #ifdef CONFIG_FT5X0X_MULTITOUCH
- switch (event->touch_point)
- {
- case 5:
- event->x5 = (s16)(buf[0x1b] & 0x0F)<<8 | (s16)buf[0x1c];
- event->y5 = (s16)(buf[0x1d] & 0x0F)<<8 | (s16)buf[0x1e];
- status = (s16)((buf[0x1b] & 0xc0) >> 6);
- event->touch_ID5=(s16)(buf[0x1D] & 0xF0)>>4;
- if (status == 1) ft5x0x_ts_release();
-
- case 4:
- event->x4 = (s16)(buf[0x15] & 0x0F)<<8 | (s16)buf[0x16];
- event->y4 = (s16)(buf[0x17] & 0x0F)<<8 | (s16)buf[0x18];
- status = (s16)((buf[0x15] & 0xc0) >> 6);
- event->touch_ID4=(s16)(buf[0x17] & 0xF0)>>4;
- if (status == 1) ft5x0x_ts_release();
-
- case 3:
- event->x3 = (s16)(buf[0x0f] & 0x0F)<<8 | (s16)buf[0x10];
- event->y3 = (s16)(buf[0x11] & 0x0F)<<8 | (s16)buf[0x12];
- status = (s16)((buf[0x0f] & 0xc0) >> 6);
- event->touch_ID3=(s16)(buf[0x11] & 0xF0)>>4;
- if (status == 1) ft5x0x_ts_release();
-
- case 2:
- event->x2 = (s16)(buf[9] & 0x0F)<<8 | (s16)buf[10];
- event->y2 = (s16)(buf[11] & 0x0F)<<8 | (s16)buf[12];
- status = (s16)((buf[0x9] & 0xc0) >> 6);
- event->touch_ID2=(s16)(buf[0x0b] & 0xF0)>>4;
- if (status == 1) ft5x0x_ts_release();
-
- case 1:
- event->x1 = (s16)(buf[3] & 0x0F)<<8 | (s16)buf[4];
- event->y1 = (s16)(buf[5] & 0x0F)<<8 | (s16)buf[6];
- status = (s16)((buf[0x3] & 0xc0) >> 6);
- event->touch_ID1=(s16)(buf[0x05] & 0xF0)>>4;
- if (status == 1) ft5x0x_ts_release();
- break;
-
- default:
- return -1;
- }
- #else
- if (event->touch_point == 1)
- {
- event->x1 = (s16)(buf[3] & 0x0F)<<8 | (s16)buf[4];
- event->y1 = (s16)(buf[5] & 0x0F)<<8 | (s16)buf[6];
- }
- #endif
- event->pressure = 200;
-
- dev_dbg(&this_client->dev, "%s: 1:%d %d 2:%d %d \n", __func__,
- event->x1, event->y1, event->x2, event->y2);
-
- return 0;
- }</pre> 从设备中读取数据使用ft5x0x_i2c_rxdata()函数:
- <p></p>
- <p align="left"></p>
- <pre name="code" class="cpp"><pre name="code" class="cpp">static int ft5x0x_i2c_rxdata(char *rxdata, int length)
- {
- int ret;
-
- struct i2c_msg msgs[] =
- {
- {
- .addr = this_client->addr,
- .flags = 0,
- .len = 1,
- .buf = rxdata,
- },
- {
- .addr = this_client->addr,
- .flags = I2C_M_RD,
- .len = length,
- .buf = rxdata,
- },
- };
-
- ret = i2c_transfer(this_client->adapter, msgs, 2);
- if (ret < 0)
- pr_err("msg %s i2c read error: %d\n", __func__, ret);
-
- return ret;
- }</pre>
- <pre></pre>
- 这个函数里面会构建一个i2c_msg msg结构去调用i2c_transfer()函数读取数据,在前面已经说过,i2c_transfer()函数最终调用的就是i2c_algorithm中maste/size]
- int ret = -1;
- int status = 0;
-
- #ifdef CONFIG_FT5X0X_MULTITOUCH
- ret = ft5x0x_i2c_rxdata(buf, 31);
- #else
- ret = ft5x0x_i2c_rxdata(buf, 7);
- #endif
- if (ret < 0)
- {
- printk("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
- return ret;
- }
-
- memset(event, 0, sizeof(struct ts_event));
- event->touch_point = buf[2] & 0x07;
-
- if (event->touch_point == 0)
- {
- ft5x0x_ts_inactivate();
- return 1;
- }
-
- #ifdef CONFIG_FT5X0X_MULTITOUCH
- switch (event->touch_point)
- {
- case 5:
- event->x5 = (s16)(buf[0x1b] & 0x0F)<<8 | (s16)buf[0x1c];
- event->y5 = (s16)(buf[0x1d] & 0x0F)<<8 | (s16)buf[0x1e];
- status = (s16)((buf[0x1b] & 0xc0) >> 6);
- event->touch_ID5=(s16)(buf[0x1D] & 0xF0)>>4;
- if (status == 1) ft5x0x_ts_release();
-
- case 4:
- event->x4 = (s16)(buf[0x15] & 0x0F)<<8 | (s16)buf[0x16];
- event->y4 = (s16)(buf[0x17] & 0x0F)<<8 | (s16)buf[0x18];
- status = (s16)((buf[0x15] & 0xc0) >>
|