/* 用来和设备树中的节点匹配 */
struct of_device_id {
char name[32];
char type[32];
char compatible[128];
const void *data;
};
struct device_driver {
/* 驱动名称 */
const char *name;
/* 支持的device所依附的bus */
struct bus_type *bus;
/* 一般都是THIS_MODULE */
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
/* 支持的设备列表,用于匹配设备树节点 */
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
/* 私有数据指针 */
struct driver_private *p;
}
描述了一套i2c设备驱动方法。
struct i2c_driver {
unsigned int class;
/* 依附attach_adapter函数指针 */
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
/* device和driver匹配上时执行 */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
void (*shutdown)(struct i2c_client *);
void (*alert)(struct i2c_client *, unsigned int data);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
/* 该驱动支持的设备ID列表 */
const struct i2c_device_id *id_table;
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
描述了一个真实的i2c设备,包括名称、地址等。
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 device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
}
struct tsc2007 {
/* input_dev */
struct input_dev *input;
char phys[32];
/* 设备 */
struct i2c_client *client;
u16 model;
u16 x_plate_ohms;
/* 最大压力 */
u16 max_rt;
unsigned long poll_period; /* in jiffies */
int fuzzx;
int fuzzy;
int fuzzz;
unsigned gpio;
/* 中断号 */
int irq;
/* 等待队列头 */
wait_queue_head_t wait;
/* 驱动是否停止标记 */
bool stopped;
/* 获取触屏按下状态函数指针 */
int (*get_pendown_state)(struct device *);
};
probe函数在device和driver匹配上的时候执行,主要负责申请驱动需要的内存、中断等资源,初始化驱动相关结构体成员,最后申请并注册input_dev设备。
/* 设备树读取 */
static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
{
struct device_node *np = client->dev.of_node;
u32 val32;
u64 val64;
if (!np)
return -EINVAL;
if (!of_property_read_u32(np, "ti,max-rt", &val32))
ts->max_rt = val32;
else
ts->max_rt = MAX_12BIT;
if (!of_property_read_u32(np, "ti,fuzzx", &val32))
ts->fuzzx = val32;
if (!of_property_read_u32(np, "ti,fuzzy", &val32))
ts->fuzzy = val32;
if (!of_property_read_u32(np, "ti,fuzzz", &val32))
ts->fuzzz = val32;
if (!of_property_read_u64(np, "ti,poll-period", &val64))
ts->poll_period = msecs_to_jiffies(val64);
else
ts->poll_period = msecs_to_jiffies(1);
if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32))
ts->x_plate_ohms = val32;
else
return -EINVAL;
/* 获取GPIO */
ts->gpio = of_get_gpio(np, 0);
if (gpio_is_valid(ts->gpio))
ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
return 0;
}
/* probe function */
static int tsc2007_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tsc2007 *ts;
struct input_dev *input_dev;
int err;
/* 给ts分配内存,devm_kzalloc分配的内存不需要手动释放 */
ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
if (!ts)
return -ENOMEM;
/* 使用设备树属性配置ts */
err = tsc2007_probe_dt(client, ts);
if (err)
return err;
/* 为input_dev分配内存 */
input_dev = devm_input_allocate_device(&client->dev);
if (!input_dev)
return -ENOMEM;
i2c_set_clientdata(client, ts);
ts->client = client;
ts->irq = client->irq;
ts->input = input_dev;
/* 初始化等待队列头 */
init_waitqueue_head(&ts->wait);
input_dev->name = "TSC2007 TouchScreen";
input_dev->phys = ts->phys;
input_dev->id.bustype = BUS_I2C;
input_dev->open = tsc2007_open;
input_dev->close = tsc2007_close;
input_set_drvdata(input_dev, ts);
// 支持的事件类型
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
// 支持的按键值
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
/* 绝对坐标 */
/* 设置上报数据类型,范围 */
input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, ts->fuzzx, 0);
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, ts->fuzzz, 0);
/* 请求软中断 */
err = devm_request_threaded_irq(&client->dev, ts->irq, tsc2007_hard_irq,
tsc2007_soft_irq, IRQF_ONESHOT,
client->dev.driver->name, ts);
if (err)
return err;
tsc2007_stop(ts);
/* 注册input设备 */
err = input_register_device(input_dev);
if (err)
return err;
return 0;
}
open和close函数负责驱动的打开和关闭。
/* open函数 */
static int tsc2007_open(struct input_dev *input_dev)
{
struct tsc2007 *ts = input_get_drvdata(input_dev);
int err;
/* 标记为启动状态 */
ts->stopped = false;
mb();
/* 开启外部中断 */
enable_irq(ts->irq);
/* 使tsc2007掉电,并开启中断 */
err = tsc2007_xfer(ts, PWRDOWN);
if (err < 0) {
tsc2007_stop(ts);
return err;
}
return 0;
}
/* close函数 */
static void tsc2007_close(struct input_dev *input_dev)
{
struct tsc2007 *ts = input_get_drvdata(input_dev);
tsc2007_stop(ts);
}
/* stop函数 */
static void tsc2007_stop(struct tsc2007 *ts)
{
/* 标记为停止状态 */
ts->stopped = true;
mb();
wake_up(&ts->wait);
/* 关闭外部中断 */
disable_irq(ts->irq);
}
当触摸按下时会进入外部中断硬中断,在硬中断中激活软中断,而读取并上报ad值等操作在软中断中进行。
/* 软中断 */
static irqreturn_t tsc2007_soft_irq(int irq, void *handler)
{
struct tsc2007 *ts = handler;
struct input_dev *input = ts->input;
struct ts_event tc;
u32 rt;
/* 未stop且按下 ,如果一直按下会在这里循环*/
while (!ts->stopped && tsc2007_is_pen_down(ts)) {
/* 读取tsc2007原始数据 */
tsc2007_read_values(ts, &tc);
/* 计算压力 */
rt = tsc2007_calculate_pressure(ts, &tc);
if (!rt && !ts->get_pendown_state)
break;
if (rt <= ts->max_rt) {
input_report_key(input, BTN_TOUCH, 1);
input_report_abs(input, ABS_X, tc.x);
input_report_abs(input, ABS_Y, tc.y);
input_report_abs(input, ABS_PRESSURE, rt);
input_sync(input);
}
/* 等待一段时 */
wait_event_timeout(ts->wait, ts->stopped, ts->poll_period);
}
input_report_key(input, BTN_TOUCH, 0);
input_report_abs(input, ABS_PRESSURE, 0);
input_sync(input);
return IRQ_HANDLED;
}
/* 硬中断 */
static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
{
struct tsc2007 *ts = handle;
/* 按下 */
if (tsc2007_is_pen_down(ts))
return IRQ_WAKE_THREAD;
return IRQ_HANDLED;
}
/* 计算压力 */
static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
{
u32 rt = 0;
if (tc->x == MAX_12BIT)
tc->x = 0;
if (likely(tc->x && tc->z1)) {
rt = tc->z2 - tc->z1;
rt *= tc->x;
rt *= tsc->x_plate_ohms;
rt /= tc->z1;
rt = (rt + 2047) >> 12;
}
return rt;
}
/* i2c传输数据 */
static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
{
s32 data;
u16 val;
data = i2c_smbus_read_word_data(tsc->client, cmd);
if (data < 0) {
return data;
}
val = swab16(data) >> 4;
return val;
}
/* 从tsc2007中读取数据坐标 */
static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
{
tc->y = tsc2007_xfer(tsc, READ_Y);
tc->x = tsc2007_xfer(tsc, READ_X);
tc->z1 = tsc2007_xfer(tsc, READ_Z1);
tc->z2 = tsc2007_xfer(tsc, READ_Z2);
tsc2007_xfer(tsc, PWRDOWN);
}
/* 触摸按下状态获取 */
static int tsc2007_get_pendown_state_gpio(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct tsc2007 *ts = i2c_get_clientdata(client);
/* 按下返回1 */
return !gpio_get_value(ts->gpio);
}
/* 获取触屏按下状态 */
static bool tsc2007_is_pen_down(struct tsc2007 *ts)
{
/* 没有此函数,函数指针为空,返回true */
if(!ts->get_pendown_state)
return true;
else
/* 获取按下状态 */
return ts->get_pendown_state(&ts->client->dev);
}