tp 驱动分析记

tp 驱动分析记

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

//这个接口。

// 有兴趣的可以看看这个 http://blog.csdn.net/lbmygf/article/details/7356214

  

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

你可能感兴趣的:(tp 驱动分析记)