您的位置:首页 > 编程语言 > PHP开发

tp 驱动分析记录 cyttsp_i2c.c

2012-03-15 15:59 162 查看
自己的备忘记录 : 2012/03/15

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进入工作状态,所以用了

//这个接口。

// 有兴趣的可以看看这个
/article/8046873.html

/* 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;

}

恩 。。。 到这里,也就差不多了 ,没有提到的下次在说,有点累了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: