input子系统一 i2c设备
来源:互联网 发布:mysql 重启 编辑:程序博客网 时间:2024/06/05 06:58
增加/删除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
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文无所谓版权,欢迎转载。
- input子系统一 i2c设备
- input子系统——i2c设备
- Linux设备驱动子系统- I2C
- Linux I2C子系统分析-I2C设备驱动
- Linux I2C子系统分析-I2C设备驱动
- Linux设备模型之input子系统详解(一)
- Linux设备模型之input子系统详解(一)
- Linux设备模型之input子系统详解(一)
- Linux设备驱动之——input子系统(一)
- Linux i2c子系统(一) _动手写一个i2c设备驱动
- Linux i2c子系统(一) _动手写一个i2c设备驱动
- input子系统分析一
- input子系统(一)
- linux input子系统(一)
- input子系统(一)
- input子系统分析一
- Linux设备驱动 --- input子系统
- linux ------ input 子系统设备驱动
- 搭建自己的ngrok服务
- c#数组
- 架构设计:负载均衡层设计方案(6)——Nginx + Keepalived构建高可用的负载层
- 使用ngrok的总结
- https SSL 自建证书的制作
- input子系统一 i2c设备
- 关于netty的一些文章收集
- jQuery学习——DOM篇
- angullar ui-href 子元素和父元素的跳转互不影响——阻止冒泡
- C++ websocket库
- Mysql 1293 - Incorrect table definition; there can be only one TIMESTAMP column with C
- letsencrypt证书自动续期问题
- Rails学习笔记(5) ————Model的关联
- 注解参数