static int cyttsp_init(void)
{
int ret;
cyttsp_info("Cypress TrueTouch(R) Standard Product\n");
cyttsp_info("I2C Touchscreen Driver (Built %s @ %s)\n", \
__DATE__, __TIME__);
cyttsp_ts_wq = create_singlethread_workqueue("cyttsp_ts_wq");
/*
create_workqueue 用于创建一个workqueue 队列,为系统中的每个CPU 都创建一个内核线程
create_singlethread_workqueue 用于创建workqueue ,只创建一个内核线程
destroy_workqueue 释放workqueue 队列
schedule_work 调度执行一个具体的任务,执行的任务将会被挂入Linux 系统提供的workqueue ——keventd_wq
schedule_delayed_work 延迟一定时间去执行一个具体的任务,功能与schedule_work 类似,多了一个延迟时间
*/
if (cyttsp_ts_wq == NULL) {
cyttsp_debug("No memory for cyttsp_ts_wq\n");
return -ENOMEM;
}
ret = i2c_add_driver(&cyttsp_driver); //添加I2C驱动
return ret;
}
===================================================
static struct i2c_driver cyttsp_driver = {
.driver = {
.name = CY_I2C_NAME,
.owner = THIS_MODULE,
.pm = &cyttsp_pm_ops, //电源管理接口
},
.probe = cyttsp_probe,
.remove = __devexit_p(cyttsp_remove),
.id_table = cyttsp_id,
};
===================================================
static int __devinit cyttsp_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct cyttsp *ts;
int error;
int retval = CY_OK;
cyttsp_info("Start Probe 1.2\n");
/* allocate and clear memory */
ts = kzalloc(sizeof(struct cyttsp), GFP_KERNEL);
if (ts == NULL) {
cyttsp_xdebug1("err kzalloc for cyttsp\n");
retval = -ENOMEM;
}
//前面有觉得奇怪,在cyttsp_driver中
----------------------
static struct i2c_driver cyttsp_driver = {
.driver = {
.name = CY_I2C_NAME,
.owner = THIS_MODULE,
.pm = &cyttsp_pm_ops, // 为什么是用这个
},
// .suspend = xxx_suspend, //而不是用这两个,这之间有什么区别
// .resume = xxx_resume,
.probe = cyttsp_probe,
.remove = __devexit_p(cyttsp_remove),
.id_table = cyttsp_id,
};
------------------------
// 到这里差不多明白了
.suspend = xxx_suspend,
.resume = xxx_resume,
//是系统的睡眠唤醒接口,是不可控的,也就是说:在整个系统睡眠和唤醒时会自动掉用,
//你无法在其他时候进行控制。而
.pm = &cyttsp_pm_ops,
// 是在系统睡眠和唤醒的其他时间,你可以用这个接口对设备的睡眠唤醒进行控制。
//具体来讲,因为TP的功耗一般挺大的,如果TP一直处在工作状态,对系统功耗是一个很大的负担,
//一般的做法是在无触碰的情况下让TP睡眠,有触碰时,用中断唤醒TP进入工作状态,所以用了
//这个接口。
// 有兴趣的可以看看这个 Linux的电源管理架构_曼云-->孤峰-CSDN博客
/* Enable runtime PM ops, start in ACTIVE mode */
// 启用运行时电源管理操作接口,在主动模式启动
error = pm_runtime_set_active(&client->dev);
if (error < 0)
dev_dbg(&client->dev, "unable to set runtime pm state\n");
pm_runtime_enable(&client->dev);
if (!(retval < CY_OK)) {
/* register driver_data */
ts->client = client;
ts->platform_data = client->dev.platform_data;
if (ts->platform_data->fw_fname)
strncpy(ts->fw_fname, ts->platform_data->fw_fname,
FW_FNAME_LEN - 1);
else
strncpy(ts->fw_fname, "cyttsp.hex", FW_FNAME_LEN - 1);
i2c_set_clientdata(client, ts);
error = cyttsp_initialize(client, ts);
// 驱动程序初始化,这个函数做一下事情;
// 1 创建并注册一个输入层的输入设备
// 2 使CYTTSP 脱离引导模式
// 3 开启定时器,工作队列
if (error) {
cyttsp_xdebug1("err cyttsp_initialize\n");
if (ts != NULL) {
/* deallocate memory */
kfree(ts);
}
/*
i2c_del_driver(&cyttsp_driver);
*/
retval = -ENODEV;
} else
cyttsp_openlog();
}
#ifdef CONFIG_HAS_EARLYSUSPEND
if (!(retval < CY_OK)) {
ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
ts->early_suspend.suspend = cyttsp_early_suspend;
ts->early_suspend.resume = cyttsp_late_resume;
register_early_suspend(&ts->early_suspend);
}
#endif /* CONFIG_HAS_EARLYSUSPEND */
device_init_wakeup(&client->dev, ts->platform_data->wakeup);
----------------------------------
static inline int device_init_wakeup(struct device *dev, bool val)
{
device_set_wakeup_capable(dev, val); //设置设备能不能唤醒
device_set_wakeup_enable(dev, val); //设置设备使不使用唤醒,这都与前面runtime电源管理模式相关
return 0;
}
// 设备模型中的所有设备都有两个标志来控制唤醒事件(可使得设备或系统退出低功耗状态)。
// 设两个标志位由总线或者设备驱动用 device_set_wakeup_capable()和
// device_set_wakeup_enable()来初始化。
----------------------------------
mutex_init(&ts->mutex); //初始化互斥锁
cyttsp_info("Start Probe %s\n", \
(retval < CY_OK) ? "FAIL" : "PASS");
return retval;
}
===================================================
static int cyttsp_initialize(struct i2c_client *client, struct cyttsp *ts)
{
struct input_dev *input_device;
int error = 0;
int retval = CY_OK;
u8 id;
/* Create the input device and register it. */
input_device = input_allocate_device(); // 为input_device分配内存
if (!input_device) {
error = -ENOMEM;
cyttsp_xdebug1("err input allocate device\n");
goto error_free_device;
}
if (!client) {
error = ~ENODEV;
cyttsp_xdebug1("err client is Null\n");
goto error_free_device;
}
if (!ts) {
error = ~ENODEV;
cyttsp_xdebug1("err context is Null\n");
goto error_free_device;
}
ts->input = input_device;
input_device->name = CY_I2C_NAME;
input_device->phys = ts->phys;
input_device->dev.parent = &client->dev;
/* init the touch structures */ //初始化触摸屏结构
ts->num_prv_st_tch = CY_NTCH;
for (id = 0; id < CY_NUM_TRK_ID; id++) {
ts->act_trk[id] = CY_NTCH;
ts->prv_mt_pos[id][CY_XPOS] = 0;
ts->prv_mt_pos[id][CY_YPOS] = 0;
}
for (id = 0; id < CY_NUM_MT_TCH_ID; id++)
ts->prv_mt_tch[id] = CY_IGNR_TCH;
for (id = 0; id < CY_NUM_ST_TCH_ID; id++)
ts->prv_st_tch[id] = CY_IGNR_TCH;
set_bit(EV_SYN, input_device->evbit); //设置事件类型,添加TP能够上传的事件类型
set_bit(EV_KEY, input_device->evbit);
set_bit(EV_ABS, input_device->evbit);
set_bit(BTN_TOUCH, input_device->keybit);
set_bit(BTN_2, input_device->keybit);
if (ts->platform_data->use_gestures)
set_bit(BTN_3, input_device->keybit);
input_set_abs_params(input_device, ABS_X, ts->platform_data->disp_minx,
ts->platform_data->disp_maxx, 0, 0);
//设置上传数据标志和参数
input_set_abs_params(input_device, ABS_Y, ts->platform_data->disp_miny,
ts->platform_data->disp_maxy, 0, 0);
input_set_abs_params(input_device,
ABS_TOOL_WIDTH, 0, CY_LARGE_TOOL_WIDTH, 0 , 0);
input_set_abs_params(input_device,
ABS_PRESSURE, 0, CY_MAXZ, 0, 0);
input_set_abs_params(input_device,
ABS_HAT0X, 0, ts->platform_data->panel_maxx, 0, 0);
input_set_abs_params(input_device,
ABS_HAT0Y, 0, ts->platform_data->panel_maxy, 0, 0);
if (ts->platform_data->use_gestures) {
input_set_abs_params(input_device,
ABS_HAT1X, 0, CY_MAXZ, 0, 0);
input_set_abs_params(input_device,
ABS_HAT1Y, 0, CY_MAXZ, 0, 0);
}
if (ts->platform_data->use_mt) {
input_set_abs_params(input_device, ABS_MT_POSITION_X,
ts->platform_data->disp_minx,
ts->platform_data->disp_maxx, 0, 0);
input_set_abs_params(input_device, ABS_MT_POSITION_Y,
ts->platform_data->disp_miny,
ts->platform_data->disp_maxy, 0, 0);
input_set_abs_params(input_device,
ABS_MT_TOUCH_MAJOR, 0, CY_MAXZ, 0, 0);
input_set_abs_params(input_device,
ABS_MT_WIDTH_MAJOR, 0, CY_LARGE_TOOL_WIDTH, 0, 0);
if (ts->platform_data->use_trk_id) {
input_set_abs_params(input_device,
ABS_MT_TRACKING_ID, 0, CY_NUM_TRK_ID, 0, 0);
}
}
/* set dummy key to make driver work with virtual keys */ //设置input_device 具有虚拟键盘功能
input_set_capability(input_device, EV_KEY, KEY_PROG1);
cyttsp_info("%s: Register input device\n", CY_I2C_NAME);
error = input_register_device(input_device); //注册input_device
if (error) {
cyttsp_alert("%s: Failed to register input device\n", \
CY_I2C_NAME);
retval = error;
goto error_free_device;
}
/* Prepare our worker structure prior to setting up the timer/ISR */
INIT_WORK(&ts->work, cyttsp_xy_worker); //初始话一个工作队列
(gpio_is_valid(ts->platform_data->resout_gpio)) { //检查一个GPIO端口的合法性,也就是说,检查这个端口是
//否被用作其他用途。
/* configure touchscreen reset out gpio */
retval = gpio_request(ts->platform_data->resout_gpio,
"cyttsp_resout_gpio");
if (retval) {
pr_err("%s: unable to request reset gpio %d\n",
__func__, ts->platform_data->resout_gpio);
goto error_free_device;
}
retval = gpio_direction_output(
ts->platform_data->resout_gpio, 1);
if (retval) {
pr_err("%s: unable to set direction for gpio %d\n",
__func__, ts->platform_data->resout_gpio);
goto error_resout_gpio_dir;
}
}
if (gpio_is_valid(ts->platform_data->sleep_gpio)) {
/* configure touchscreen sleep gpio */
retval = gpio_request(ts->platform_data->sleep_gpio,
"cy8c_sleep_gpio");
if (retval) {
pr_err("%s: unable to request sleep gpio %d\n",
__func__, ts->platform_data->sleep_gpio);
goto error_sleep_gpio_req;
}
retval = gpio_direction_output(
ts->platform_data->sleep_gpio, 0);
if (retval) {
pr_err("%s: unable to set direction for gpio %d\n",
__func__, ts->platform_data->resout_gpio);
goto error_sleep_gpio_dir;
}
}
if (gpio_is_valid(ts->platform_data->irq_gpio)) {
/* configure touchscreen irq gpio */
retval = gpio_request(ts->platform_data->irq_gpio,
"ts_irq_gpio");
if (retval) {
pr_err("%s: unable to request gpio [%d]\n", __func__,
ts->platform_data->irq_gpio);
goto error_irq_gpio_req;
}
retval = gpio_direction_input(ts->platform_data->irq_gpio);
if (retval) {
pr_err("%s: unable to set_direction for gpio [%d]\n",
__func__, ts->platform_data->irq_gpio);
goto error_irq_gpio_dir;
}
}
if (ts->platform_data->regulator_info) {
retval = cyttsp_power_device(ts, true);
if (retval) {
pr_err("%s: Unable to power device %d\n",
__func__, retval);
goto error_irq_gpio_dir;
}
}
/* Power on the chip and make sure that I/Os are set as specified
* in the platform */
if (ts->platform_data->init) {
retval = ts->platform_data->init(client);
if (retval) {
pr_err("%s: ts init failed\n", __func__);
goto error_power_device;
}
}
//cyttsp_power_device和ts->platform_data->init 总结起来就是给TP上电
msleep(100); //等待上电稳定,TP进入稳定工作状态
/* check this device active by reading first byte/register */
retval = i2c_smbus_read_byte_data(ts->client, 0x01);
if (retval < 0) {
pr_err("%s: i2c sanity check failed\n", __func__);
goto error_power_device;
}
retval = cyttsp_power_on(ts);
if (retval < 0) {
pr_err("%s: cyttsp_power_on failed\n", __func__);
goto error_power_device;
}
/* Timer or Interrupt setup */ //这里提供了两种模式供选择,中断和轮询,这里我们看中断模式
if (ts->client->irq == 0) {
cyttsp_info("Setting up timer\n");
setup_timer(&ts->timer, cyttsp_timer, (unsigned long) ts);
mod_timer(&ts->timer, jiffies + TOUCHSCREEN_TIMEOUT);
} else {
cyttsp_info("Setting up interrupt\n");
/* request_irq() will also call enable_irq() */
error = request_irq(client->irq, cyttsp_irq,
IRQF_TRIGGER_FALLING,
client->dev.driver->name, ts);
if (error) {
cyttsp_alert("error: could not request irq\n");
retval = error;
goto error_power_device;
}
}
irq_cnt = 0;
irq_cnt_total = 0;
irq_err_cnt = 0;
atomic_set(&ts->irq_enabled, 1); //原子赋值,使能IRQ
retval = device_create_file(&ts->client->dev, &dev_attr_irq_enable); //创建sys/下的接口文件
if (retval < CY_OK) {
cyttsp_alert("File device creation failed: %d\n", retval);
retval = -ENODEV;
goto error_free_irq;
}
retval = device_create_file(&client->dev, &dev_attr_cyttsp_fw_ver);
if (retval) {
cyttsp_alert("sysfs entry for firmware version failed\n");
goto error_rm_dev_file_irq_en;
}
ts->cyttsp_fwloader_mode = 0;
retval = device_create_file(&client->dev, &dev_attr_cyttsp_update_fw);
if (retval) {
cyttsp_alert("sysfs entry for firmware update failed\n");
goto error_rm_dev_file_fw_ver;
}
retval = device_create_file(&client->dev,
&dev_attr_cyttsp_force_update_fw);
if (retval) {
cyttsp_alert("sysfs entry for force firmware update failed\n");
goto error_rm_dev_file_update_fw;
}
if (ts->platform_data->correct_fw_ver) {
if (g_bl_data.appid_lo != ts->platform_data->correct_fw_ver)
printk(KERN_INFO "Please update touchscreen firmware\n");
}
retval = device_create_file(&client->dev,
&dev_attr_cyttsp_fw_name);
if (retval) {
cyttsp_alert("sysfs entry for file name selection failed\n");
goto error_rm_dev_file_fupdate_fw;
}
cyttsp_info("%s: Successful registration\n", CY_I2C_NAME);
goto success;
error_rm_dev_file_fupdate_fw:
device_remove_file(&client->dev, &dev_attr_cyttsp_force_update_fw);
error_rm_dev_file_update_fw:
device_remove_file(&client->dev, &dev_attr_cyttsp_update_fw);
error_rm_dev_file_fw_ver:
device_remove_file(&client->dev, &dev_attr_cyttsp_fw_ver);
error_rm_dev_file_irq_en:
device_remove_file(&client->dev, &dev_attr_irq_enable);
error_free_irq:
if (ts->client->irq)
free_irq(client->irq, ts);
error_power_device:
if (ts->platform_data->regulator_info)
cyttsp_power_device(ts, false);
error_irq_gpio_dir:
if (gpio_is_valid(ts->platform_data->irq_gpio))
gpio_free(ts->platform_data->irq_gpio);
error_irq_gpio_req:
if (gpio_is_valid(ts->platform_data->sleep_gpio))
gpio_direction_output(ts->platform_data->sleep_gpio, 1);
error_sleep_gpio_dir:
if (gpio_is_valid(ts->platform_data->sleep_gpio))
gpio_free(ts->platform_data->sleep_gpio);
error_sleep_gpio_req:
if (gpio_is_valid(ts->platform_data->resout_gpio))
gpio_direction_output(ts->platform_data->resout_gpio, 0);
error_resout_gpio_dir:
if (gpio_is_valid(ts->platform_data->resout_gpio))
gpio_free(ts->platform_data->resout_gpio);
error_free_device:
if (input_device)
input_free_device(input_device);
success:
return retval;
}
===================================================
static irqreturn_t cyttsp_irq(int irq, void *handle)
{
struct cyttsp *ts = (struct cyttsp *) handle;
cyttsp_xdebug("%s: Got IRQ\n", CY_I2C_NAME);
/* disable further interrupts until this interrupt is processed */
disable_irq_nosync(ts->client->irq);
/* schedule motion signal handling */
queue_work(cyttsp_ts_wq, &ts->work);
--------------------------------------------
//前面已经初始化
INIT_WORK(&ts->work, cyttsp_xy_worker);
--------------------------------------------
return IRQ_HANDLED;
}
===================================================
//这一段还真TMD长 ,一句话,就是上报数据给 驱动中注册的INPUT,上层可以通过他读取数据。
void cyttsp_xy_worker(struct work_struct *work)
{
。。。。。。
if (cur_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) {
input_report_abs(ts->input,
ABS_X, st_x1);
input_report_abs(ts->input,
ABS_Y, st_y1);
input_report_abs(ts->input,
ABS_PRESSURE, st_z1);
input_report_key(ts->input,
BTN_TOUCH,
CY_TCH);
input_report_abs(ts->input,
ABS_TOOL_WIDTH,
curr_tool_width);
cyttsp_debug("ST->F1:%3d X:%3d Y:%3d Z:%3d\n", \
cur_st_tch[CY_ST_FNGR1_IDX], \
st_x1, st_y1, st_z1);
if (cur_st_tch[CY_ST_FNGR2_IDX] < CY_NUM_TRK_ID) {
input_report_key(ts->input, BTN_2, CY_TCH);
input_report_abs(ts->input, ABS_HAT0X, st_x2);
input_report_abs(ts->input, ABS_HAT0Y, st_y2);
cyttsp_debug("ST->F2:%3d X:%3d Y:%3d Z:%3d\n", \
cur_st_tch[CY_ST_FNGR2_IDX],
st_x2, st_y2, st_z2);
} else {
input_report_key(ts->input,
BTN_2,
CY_NTCH);
}
} else {
input_report_abs(ts->input, ABS_PRESSURE, CY_NTCH);
input_report_key(ts->input, BTN_TOUCH, CY_NTCH);
input_report_key(ts->input, BTN_2, CY_NTCH);
}
/* update platform data for the current single touch info */
ts->prv_st_tch[CY_ST_FNGR1_IDX] = cur_st_tch[CY_ST_FNGR1_IDX];
ts->prv_st_tch[CY_ST_FNGR2_IDX] = cur_st_tch[CY_ST_FNGR2_IDX];
}
/* handle Multi-touch signals */
if (ts->platform_data->use_mt) {
if (ts->platform_data->use_trk_id) {
/* terminate any previous touch where the track
* is missing from the current event */
for (id = 0; id < CY_NUM_TRK_ID; id++) {
if ((ts->act_trk[id] != CY_NTCH) &&
(cur_trk[id] == CY_NTCH)) {
input_report_abs(ts->input,
ABS_MT_TRACKING_ID,
id);
input_report_abs(ts->input,
ABS_MT_TOUCH_MAJOR,
CY_NTCH);
input_report_abs(ts->input,
ABS_MT_WIDTH_MAJOR,
curr_tool_width);
input_report_abs(ts->input,
ABS_MT_POSITION_X,
ts->prv_mt_pos[id][CY_XPOS]);
input_report_abs(ts->input,
ABS_MT_POSITION_Y,
ts->prv_mt_pos[id][CY_YPOS]);
CY_MT_SYNC(ts->input);
ts->act_trk[id] = CY_NTCH;
ts->prv_mt_pos[id][CY_XPOS] = 0;
ts->prv_mt_pos[id][CY_YPOS] = 0;
}
}
/* set Multi-Touch current event signals */
。。。。。。
return;
}