增加/删除i2c_adapter
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_del_adapter(struct i2c_adapter *adapter)
增加/删除i2c_driver
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver(struct i2c_driver *driver) //调用i2c_register_driver
void i2c_del_driver(struct i2c_driver *driver)
增加/删除i2c_client
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
void i2c_unregister_device(struct i2c_client *client)
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)
i2c_transfer()函数用于进行I2C 适配器和I2C 设备之间的一组消息交互,i2c_master_send()函数和i2c_master_recv()函数内部会调用i2c_transfer()函数分别完成一条写消息和一条读消息,i2c_transfer()本身不能和硬件完成消息交互,它寻找i2c_adapter对应的i2c_algorithm,要实现数据传送就要实现i2c_algorithm的master_xfer(),在总线驱动中就是重点。static int i2c_adapter_xxx_xfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num)
{
......
for (i = 0; i < num; i++) {
i2c_adapter_xxx_start(); /*产生起始位*/
if (msgs[i]->flags & I2C_M_RD) { /*读取*/
i2c_adapter_xxx_setaddr((msg->addr << 1) | 1);/*发送从设备地址*/
i2c_adapter_xxx_wait_ack(); /*获得从设备的ACK*/
i2c_adapter_xxx_readbytes(msgs[i]->buf,msgs[i]->len);
/*读取len长度的数据到buf中*/
} else {
i2c_adapter_xxx_setaddr(msg->addr << 1);
i2c_adapter_xxx_wait_ack();
i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len);
}
}
i2c_adapter_xxx_stop(); /*产生停止位*/
}
单开始信号情况:
i2c结构体系主要由以下四个结构体描叙:
1、i2c_adapter适配器通俗一点说就是一种起中间连接作用的配件:
struct i2c_adapter {
struct module *owner; /*所属模块*/
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus 总线通信方法结构体指针*/
void *algo_data; /*algorithm数据 */
/* data fields that are valid for all devices */
struct rt_mutex bus_lock; /*控制并发访问的自旋锁*/
int timeout; /* in jiffies */
int retries;/* 重试次数 */
struct device dev; /* the adapter device 适配器设备*/
int nr;
char name[48]; /*适配器名称*/
struct completion dev_released; /*用于同步*/
struct mutex userspace_clients_lock;
struct list_head userspace_clients; /*client链表头*/
struct i2c_bus_recovery_info *bus_recovery_info;
};
2、 i2c_driver
struct i2c_driver {
unsigned int class;
/* Notifies the driver that a new bus has appeared. You should avoid
* using this, it will be removed in a near future.
*/
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
/* Alert callback, for example for the SMBus alert protocol.
* The format and meaning of the data value depends on the protocol.
* For the SMBus alert protocol, there is a single bit of data passed
* as the alert response's low bit ("event flag").
*/
void (*alert)(struct i2c_client *, unsigned int data);
/* a ioctl like command that can be used to perform specific functions
* with the device.
*/
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id *id_table; //驱动所支持的i2c设备的ID表
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit 芯片地址 */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct i2c_driver *driver; /* and our access routines */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
};
4、 struct i2c_algorithm
struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer
to NULL. If an adapter algorithm can do SMBus access, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common I2C messages */
/* master_xfer should return the number of messages successfully
processed, or a negative value on error */
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
};
struct device {
struct device * parent; //父设备,一般一个bus也对应一个设备。
struct kobject kobj;//代表自身
char bus_id[BUS_ID_SIZE];
struct bus_type * bus; /* 所属的总线 */
struct device_driver *driver; /* 匹配的驱动*/
struct device_node *of_node;
void *driver_data; /* data private to the driver 指向驱动 */
void *platform_data; /* Platform specific data,由驱动定义并使用*/
///更多字段忽略了
};
5、四者之间的关系
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);
}
i2c_add_driver 调用 i2c_register_driver函数:
i2c_register_driver会调用driver_register() 来将设备驱动添加到总线的设备驱动链表中:
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
........
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
res = driver_register(&driver->driver);
INIT_LIST_HEAD(&driver->clients); //初始化链表
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver);
........
}
int driver_register(struct device_driver *drv)
{
...........
other = driver_find(drv->name, drv->bus); //判断驱动是否已经注册
ret = bus_add_driver(drv); //将设备驱动添加到bus(总线)上
ret = driver_add_groups(drv, drv->groups);
//如果grop不为空的话,将在驱动文件夹下创建以group名字的子文件夹,然后在子文件夹下
添加group的属性文件,在sysfs下表现为同一个目录
...........
}
i2c总线注册好后将会有如下文件夹结构/sys/bus/i2c/,在/sys/bus/i2c/文件夹下会有如下文件夹uevent、
devices、drivers、drivers_probe、drivers_autoprobe,当你注册驱动的时候,将会在/sys/bus/i2c/drivers/下注册一个改驱动的文件夹,比如ov7675,那么它将会注册成
/sys/bus/i2c/drivers/ov7675/,其实这些文件夹都对应一个kobject,通过kset容器组成一个很清晰的层次结构。当driver中的.compatible与dts中的匹配上就开始执行probe函数。
ft5x0x_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct ft5x0x_ts_platform_data *pdata = client->dev.platform_data;//保存数据
pdata = ft5x0x_ts_parse_dt(&client->dev);//读取设备树dts中的配置信息
if (np && !pdata){
pdata = ft5x0x_ts_parse_dt(&client->dev); //接收返回的dts配置信息
if(pdata){ //获取成功
client->dev.platform_data = pdata; //将获得的数据传输给client->device->dev->platform
}
else{ //获取失败
err = -ENOMEM;
goto exit_alloc_platform_data_failed;
}
}
//检查适配器通信协议是否匹配I2C_FUNC_I2C
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
goto exit_check_functionality_failed;
}
ft5x0x_ts->platform_data = pdata;//将获取的dts资源赋给驱动程序
this_client = client;//将client结构体赋给全局变量
ft5x0x_ts_hw_init(ft5x0x_ts);//初始化引脚,开启电源等
i2c_set_clientdata(client, ft5x0x_ts);//设置设备的私有信息
//将上面ft5x0x_ts_parse_dt定义的中断引脚设置为中断模式
client->irq = gpio_to_irq(pdata->irq_gpio_number);
err = ft5x0x_read_reg(FT5X0X_REG_CIPHER, &uc_reg_value);//检查tp型号
ft5x0x_write_reg(FT5X0X_REG_PERIODACTIVE, 7); //设置报点率
INIT_WORK(&ft5x0x_ts->resume_work, ft5x0x_ts_resume_work);//初始化工作队列:用于唤醒
tp,开启中断等工作;
input_dev = input_allocate_device(); //申请一个Input_dev设备
__set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);//设置键值类型
__set_bit(ABS_MT_POSITION_X, input_dev->absbit);
//给设备的input_dev结构体初始化
input_set_abs_params(input_dev,ABS_MT_POSITION_X, 0, pdata->TP_MAX_X, 0, 0);
input_set_abs_params(input_dev,ABS_MT_POSITION_Y, 0, pdata->TP_MAX_Y, 0, 0);
err = input_register_device(input_dev); //注册输入Input_dev设备
// request_irq() 函数来注册中断服务函数
err = request_irq(client->irq, ft5x0x_ts_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT
| IRQF_NO_SUSPEND, client->name, ft5x0x_ts);
thread = kthread_run(touch_event_handler, 0, "focal-wait-queue");//创建线程执行中断函数
.........
}
static inline void i2c_set_clientdata(struct i2c_client *dev, void *data)
{
//driver_data是驱动特殊信息的私有指针,i2c_set_clientdata(client, dev)就是将自定义的设备结构dev赋给设备
//驱动client的私有指针,我猜测是用来区别其他驱动client,
dev_set_drvdata(&dev->dev, data);
}
static void ft5x0x_ts_hw_init(struct ft5x0x_ts_data *ft5x0x_ts)
{
struct regulator *reg_vdd;
struct i2c_client *client = ft5x0x_ts->client;
struct ft5x0x_ts_platform_data *pdata = ft5x0x_ts->platform_data;
pr_info("[FST] %s [irq=%d];[rst=%d]\n",__func__,
pdata->irq_gpio_number,pdata->reset_gpio_number); //获取初始化引脚
gpio_request(pdata->irq_gpio_number, "ts_irq_pin"); //获取中断引脚
gpio_request(pdata->reset_gpio_number, "ts_rst_pin");
gpio_direction_output(pdata->reset_gpio_number, 1); //引脚输出1
gpio_direction_input(pdata->irq_gpio_number); //引脚输入
reg_vdd = regulator_get(&client->dev, pdata->vdd_name);
if (!WARN(IS_ERR(reg_vdd), "[FST] ft5x0x_ts_hw_init regulator: failed to get %s.\n", pdata->vdd_name))
{
regulator_set_voltage(reg_vdd, 2800000, 2800000);
regulator_enable(reg_vdd);
}
msleep(100);
ft5x0x_ts_reset();//复位操作
}
static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
{
return (func & i2c_get_functionality(adap)) == func;
}
static struct ft5x0x_ts_platform_data *ft5x0x_ts_parse_dt(struct device *dev)
{
struct ft5x0x_ts_platform_data *pdata;
struct device_node *np = dev->of_node;
int ret;
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dev_err(dev, "Could not allocate struct ft5x0x_ts_platform_data");
return NULL;
}
pdata->reset_gpio_number = of_get_gpio(np, 0); //获得复位引脚
if(pdata->reset_gpio_number < 0){
dev_err(dev, "fail to get reset_gpio_number\n");
goto fail;
}
pdata->irq_gpio_number = of_get_gpio(np, 1); //获得中断引脚信息
if(pdata->reset_gpio_number < 0){
dev_err(dev, "fail to get reset_gpio_number\n");
goto fail;
}
ret = of_property_read_string(np, "vdd_name", &pdata->vdd_name); //读取供电电压
if(ret){
dev_err(dev, "fail to get vdd_name\n");
goto fail;
}
ret = of_property_read_u32_array(np, "virtualkeys", &pdata->virtualkeys,12); //读取虚拟按键信息
if(ret){
dev_err(dev, "fail to get virtualkeys\n");
goto fail;
}
ret = of_property_read_u32(np, "TP_MAX_X", &pdata->TP_MAX_X); //读取X的最大值
if(ret){
dev_err(dev, "fail to get TP_MAX_X\n");
goto fail;
}
return pdata; //返回获得的数据
fail:
kfree(pdata);
return NULL;
}
struct ft5x0x_ts_platform_data{//描述tp硬件信息的结构体
int irq_gpio_number;
int reset_gpio_number;
const char *vdd_name;
int virtualkeys[12];
int TP_MAX_X;
int TP_MAX_Y;
};
5、TP键值读取到上报的过程
作者:frank_zyp
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文无所谓版权,欢迎转载。