【Android驱动04】触摸屏touchscreen驱动移植以及调试方法 ...

打印 上一主题 下一主题

主题 1031|帖子 1031|积分 3093

1. 硬件层
触摸屏硬件:触摸屏通常包括电容式或电阻式触摸屏,它们通过感应手指的触摸来产生信号。电容式触摸屏利用人体的电流感应工作,而电阻式触摸屏则通过压力改变电阻值来检测触摸。
传感器与接口:触摸屏包含多个触摸传感器,这些传感器通过总线接口(如I2C、SPI等)与主控芯片通信,将触摸数据(如位置、压力等)传输给软件层。
2. 软件层
输入子体系:Android内核中的输入子体系是一个关键组件,负责处理来自各种输入装备的数据,包括触摸屏。输入子体系从触摸屏驱动程序吸取触摸数据,并将其转换为标准的输入事故,然后通报给上层应用程序。
触摸屏驱动程序:触摸屏驱动程序是Android内核中实现触摸屏功能的具体实现。它负责与硬件层通信,读取触摸传感器的数据,并通过输入子体系将其上报给体系。
3 . 触摸屏驱动流程

  • 初始化与配置
    装备树(DTS)配置:在装备树文件中配置触摸屏相关的硬件参数,如GPIO、I2C地址等。
    驱动程序加载:在体系启动时,根据装备树配置加载触摸屏驱动程序。驱动程序会进行须要的初始化工作,如设置GPIO引脚、配置I2C接口等。
    1.1 设置GPIO引脚:
    打开 vendor\mediatek\proprietary\scripts\DrvGen.exe
    选择 cendor\mediatek\proprietary\bootable\bootloader\lk\target\ $ (project) \dct\dct\codegen.dws 配置文件
    1.2 创建ft5x16,将供应商提供的驱动驱动资料拷贝到该目录下;

    1.3 修改配置文件:CUSTOM_KERNEL_TOUCHPANEL其值由改为ft5x16,表明对应ft5x16子目录;
    打开ft5x16.c文件,修改一下:
  1. static struct i2c_board_info __initdata ft5x16_i2c_tpd={ I2C_BOARD_INFO("ft5x16", (0x70>>1))}; //"ft5x16"为设备名 ,设备地址为高7位  
  2.   
  3. static struct tpd_driver_t tpd_device_driver = {  
  4.     .tpd_device_name = "FT5x16",  
  5.     .tpd_local_init = tpd_local_init,   
  6.     .suspend = tpd_suspend,  
  7.     .resume = tpd_resume,  
  8. #ifdef TPD_HAVE_BUTTON   
  9.     .tpd_have_button = 1,  
  10. #else  
  11.     .tpd_have_button = 0,  
  12. #endif        
  13. };  
  14.   
  15. /* called when loaded into kernel */  
  16. static int __init tpd_driver_init(void) {  
  17.     printk("MediaTek FT5x16 touch panel driver init\n");  
  18.     /* 注册板级设备信息 */  
  19.     i2c_register_board_info(IIC_PORT, &ft5x16_i2c_tpd, 1);  //IIC_PORT表示i2c控制器号,由电路原理图可知TP设备连接到i2c控制器0,ft5x16_i2c_tpd为i2c设备结构,1表示该i2c_board_info个数  
  20.     if(tpd_driver_add(&tpd_device_driver) < 0)  
  21.         printk("add FT5x16 driver failed\n");  
  22.     return 0;  
  23. }  
复制代码
1.4 I2C通信
新驱动编译进内核,启动内核后,我们怎样验证i2c接口能够正常通信呢?
体系启动后通过串口或adb shell进入体系命令行窗口,查询/sys/bus/i2c/devices目录下是否有0-0038信息,查询/sys/bus/i2c/drivers目录下是否存在‘ft5x16’装备名;先包管i2c能够正常通信;
1.5 中断触发
中断注册函数:
  1. mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_TYPE, tpd_eint_interrupt_handler, 1);
  2. //tpd_eint_interrupt_handler函数为中断回调函数
复制代码
1.6 数据上报
当触摸屏产生中断的时间就会调用到该接口;然后在中断处理函数中唤醒运行在子线程中的等待队列,再通过子线程获取TP数据并上报到体系;
  1.     static DECLARE_WAIT_QUEUE_HEAD(waiter);  //初始化等待队列  
  2.       
  3.     thread = kthread_run(touch_event_handler, 0, TPD_DEVICE);  //新建线程  
  4.       
  5.     static int touch_event_handler(void *unused)  
  6.     {   
  7.         ......  
  8.         do  
  9.         {  
  10.             mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);   
  11.             set_current_state(TASK_INTERRUPTIBLE);   
  12.             wait_event_interruptible(waiter,tpd_flag!=0);  //等待队列进入休眠,等待唤醒  
  13.             tpd_flag = 0;  
  14.             set_current_state(TASK_RUNNING);  
  15.             ......  
  16.             if (tpd_touchinfo(&cinfo, &pinfo))   //获取TP数据  
  17.             {  
  18.                 //TPD_DEBUG("point_num = %d\n",point_num);  
  19.                 TPD_DEBUG_SET_TIME;  
  20.                 if(point_num >0)   
  21.                 {  
  22.                     for(i =0; i<point_num; i++)//only support 3 point  
  23.                     {            
  24.                         cinfo.x[i] = cinfo.x[i];  
  25.                         cinfo.y[i] = cinfo.y[i];  
  26.                           
  27.                         tpd_down(cinfo.x[i], cinfo.y[i], cinfo.id[i]); //上报按下数据  
  28.                         printk(KERN_DEBUG"----calibration----- X:%4d, Y:%4d, P:%4d \n", cinfo.x[i], cinfo.y[i], cinfo.id[i]);  
  29.                     }  
  30.                     input_sync(tpd->dev);  
  31.                 }  
  32.                 else   
  33.                 {  
  34.                     tpd_up(cinfo.x[0], cinfo.y[0]);   //上报弹起数据  
  35.                     //TPD_DEBUG("release --->\n");   
  36.                     //input_mt_sync(tpd->dev);  
  37.                     input_sync(tpd->dev);  
  38.                 }  
  39.             }  
  40.         ......  
  41.       
  42.         }while(!kthread_should_stop());  
  43.       
  44.         return 0;  
  45.     }  
复制代码
注:如果TP获取到的数据比力乱的时间发起通过打开‘指针位置’功能进行检察,清除TP固件分辨与LCD没对应等标题;
4. TP驱动扼要分析
MTK自己编写了一套TP框架,通过该框架管理TP装备,tpd_driver_add为框架的接口之一;体系通过tpd_driver_add添加驱动后会回调tpd_local_init函数;
  1.     static struct tpd_driver_t tpd_device_driver = {  
  2.         .tpd_device_name = FT5x16,  
  3.         .tpd_local_init = tpd_local_init,  //初始化函数  
  4.         .suspend = tpd_suspend,  
  5.         .resume = tpd_resume,  
  6.     #ifdef TPD_HAVE_BUTTON  
  7.         .tpd_have_button = 1,  
  8.     #else  
  9.         .tpd_have_button = 0,  
  10.     #endif        
  11.     };  
  12.       
  13.     /* called when loaded into kernel */  
  14.     static int __init tpd_driver_init(void) {  
  15.         printk("MediaTek FT5x16 touch panel driver init\n");  
  16.         i2c_register_board_info(0, &ft5x16_i2c_tpd, 1);  //注册板级设备信息  
  17.         if(tpd_driver_add(&tpd_device_driver) < 0)  //添加驱动  
  18.             printk("add FT5x16 driver failed\n");  
  19.         return 0;  
  20.     }  
复制代码
向体系注册i2c驱动后,如果找到对应的装备就会调用tpd_probe函数;
  1. static int tpd_local_init(void)  
  2. {  
  3.     TPD_DMESG("FTS I2C Touchscreen Driver (Built %s @ %s)\n", __DATE__, __TIME__);  
  4.   
  5.     if(i2c_add_driver(&tpd_i2c_driver)!=0)  //注册i2c驱动  
  6.     {  
  7.         TPD_DMESG("FTS unable to add i2c driver.\n");  
  8.         return -1;  
  9.     }  
  10.     TPD_DMESG("end %s, %d\n", __FUNCTION__, __LINE__);   
  11.     tpd_type_cap = 1;  
  12.     return 0;   
  13. }  
复制代码
  1.     static const struct i2c_device_id ft5x16_tpd_id[] = {{TPD_NAME,0},{}};  
  2.       
  3.     static struct i2c_driver tpd_i2c_driver = {  
  4.         .driver = {  
  5.             .name   = TPD_NAME,  
  6.         },  
  7.         .probe      = tpd_prob,  
  8.         .remove     = __devexit_p(tpd_remove),  
  9.         .id_table   = ft5x16_tpd_id,  
  10.         .detect     = tpd_detect,  
  11.     };  
  12.       
  13.     static int __devinit tpd_probe(struct i2c_client *client, const struct i2c_device_id *id)  
  14.     {      
  15.         int retval = TPD_OK;  
  16.         char data;  
  17.         u8 report_rate=0;  
  18.         int err=0;  
  19.         int reset_count = 0;  
  20.         u8 chip_id,i;  
  21.       
  22.     reset_proc:     
  23.         i2c_client = client;  
  24.     #ifdef MAIERXUN_TP_COM  
  25.         if(touchpanel_flag){  
  26.             return 0;  
  27.         }  
  28.     #endif  
  29.         //复位   
  30.         //power on, need confirm with SA  
  31.         mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);  
  32.         mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);  
  33.         mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO);   
  34.         msleep(5);  
  35.         TPD_DMESG(" fts ic reset\n");  
  36.       
  37.         //打开TP电源  
  38.     #ifdef TPD_POWER_SOURCE_CUSTOM  
  39.         hwPowerOn(TPD_POWER_SOURCE_CUSTOM, VOL_3300, "TP");  
  40.     #else  
  41.         hwPowerOn(MT65XX_POWER_LDO_VGP2, VOL_3300, "TP");  
  42.     #endif  
  43.       
  44.         mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);  
  45.         mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);  
  46.         mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE);  
  47.       
  48.     #ifdef TPD_CLOSE_POWER_IN_SLEEP   
  49.         hwPowerDown(TPD_POWER_SOURCE,"TP");  
  50.         hwPowerOn(TPD_POWER_SOURCE,VOL_3300,"TP");  
  51.         msleep(100);  
  52.       
  53.     #else  /* 结束复位 */  
  54.         mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);  
  55.         mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);  
  56.         mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO);   
  57.         msleep(5);  
  58.         TPD_DMESG(" fts ic reset\n");  
  59.         mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);  
  60.         mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);  
  61.         mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE);  
  62.     #endif  
  63.       
  64.         /* 初始化中断引脚 */  
  65.         mt_set_gpio_mode(GPIO_CTP_EINT_PIN, GPIO_CTP_EINT_PIN_M_EINT);  
  66.         mt_set_gpio_dir(GPIO_CTP_EINT_PIN, GPIO_DIR_IN);  
  67.         mt_set_gpio_pull_enable(GPIO_CTP_EINT_PIN, GPIO_PULL_ENABLE);  
  68.         mt_set_gpio_pull_select(GPIO_CTP_EINT_PIN, GPIO_PULL_UP);  
  69.       
  70.         /* 中断配置和注册 */  
  71.         mt_eint_set_hw_debounce(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_DEBOUNCE_CN);  
  72.         mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_TYPE, tpd_eint_interrupt_handler, 1);  //注册中断处理函数,TP产生中断时就会回调tpd_eint_interrupt函数  
  73.         mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);   
  74.       
  75.         msleep(400);  
  76.       
  77.         err=i2c_smbus_read_i2c_block_data(i2c_client, 0x00, 1, &data);  
  78.       
  79.         TPD_DMESG("gao_i2c:err %d,data:%d\n", err,data);  
  80.         if(err< 0 || data!=0)// reg0 data running state is 0; other state is not 0  
  81.         {  
  82.             TPD_DMESG("I2C transfer error, line: %d\n", __LINE__);  
  83.     #ifdef TPD_RESET_ISSUE_WORKAROUND  
  84.             if ( reset_count < TPD_MAX_RESET_COUNT )  
  85.             {  
  86.                 reset_count++;  
  87.                 goto reset_proc;  
  88.             }  
  89.     #endif  
  90.             //add at 20150330 by zhu  
  91.     #ifdef MAIERXUN_TP_COM  
  92.             touchpanel_flag=false;  
  93.     #endif  
  94.             return -1;   
  95.         }  
  96.       
  97.         ......  
  98.       
  99.     #ifdef VELOCITY_CUSTOM_FT5206  
  100.         if((err = misc_register(&tpd_misc_device)))  //注册混杂设备驱动  
  101.         {  
  102.             printk("mtk_tpd: tpd_misc_device register failed\n");  
  103.       
  104.         }  
  105.     #endif  
  106.       
  107.     #ifdef TPD_AUTO_UPGRADE  
  108.         printk("********************Enter CTP Auto Upgrade********************\n");  
  109.         fts_ctpm_auto_upgrade(i2c_client);  
  110.     #endif  
  111.         thread = kthread_run(touch_event_handler, 0, TPD_DEVICE);  //创建子线程,通过该子线程获取和上报数据  
  112.         if (IS_ERR(thread))  
  113.         {   
  114.             retval = PTR_ERR(thread);  
  115.             TPD_DMESG(TPD_DEVICE " failed to create kernel thread: %d\n", retval);  
  116.         }  
  117.       
  118.         TPD_DMESG("FTS Touch Panel Device Probe %s\n", (retval < TPD_OK) ? "FAIL" : "PASS");  
  119.       
  120.         /* 初始化TP的P-sensor功能,暂不分析 */  
  121.     #ifdef TPD_PROXIMITY  
  122.         struct hwmsen_object obj_ps;  
  123.       
  124.         obj_ps.polling = 0;//interrupt mode  
  125.         obj_ps.sensor_operate = tpd_ps_operate;  
  126.         if((err = hwmsen_attach(ID_PROXIMITY, &obj_ps)))  
  127.         {  
  128.             APS_ERR("proxi_fts attach fail = %d\n", err);  
  129.         }  
  130.         else  
  131.         {  
  132.             APS_ERR("proxi_fts attach ok = %d\n", err);  
  133.         }         
  134.     #endif  
  135.          
  136.     #ifdef MAIERXUN_TP_COM  
  137.         touchpanel_flag=true;  
  138.     #endif  
  139.          
  140.         return 0;  
  141.       
  142.     }  
复制代码
中断处理函数
中断处理遵照中断上下文的筹划原则,使得中断子程序只是简朴唤醒等待队列就可以了,没有多余的操作;
  1.     static void tpd_eint_interrupt_handler(void)  
  2.     {  
  3.         //TPD_DEBUG("TPD interrupt has been triggered\n");  
  4.         TPD_DEBUG_PRINT_INT;  
  5.         tpd_flag = 1;   
  6.         wake_up_interruptible(&waiter);  //唤醒等待队列  
  7.     }  
复制代码
子线程处理函数
中断与轮询:触摸屏驱动程序可以通过中断或轮询的方式从硬件层读取触摸数据。当触摸屏被触摸时,硬件会触发一个中断,驱动程序响应中断并读取数据;或者驱动程序定期轮询硬件以获取数据。
数据处理:驱动程序将读取到的原始触摸数据进行处理,转换为标准的输入事故格式(如ABS_X、ABS_Y等),并准备将其上报给输入子体系。
  1.     static int touch_event_handler(void *unused)  
  2.     {   
  3.         struct touch_info cinfo, pinfo;  
  4.         int i=0;  
  5.       
  6.         struct sched_param param = { .sched_priority = RTPM_PRIO_TPD };  
  7.         sched_setscheduler(current, SCHED_RR, ¶m);  
  8.       
  9.     #ifdef TPD_PROXIMITY  
  10.         int err;  
  11.         hwm_sensor_data sensor_data;  
  12.         u8 proximity_status;  
  13.       
  14.     #endif  
  15.         u8 state;  
  16.       
  17.         do  //进入while循环进行睡眠-等待唤醒的操作  
  18.         {  
  19.             mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);  //中断使能(解除屏蔽)  
  20.             set_current_state(TASK_INTERRUPTIBLE);   
  21.             wait_event_interruptible(waiter,tpd_flag!=0);  //进入睡眠等待唤醒  
  22.       
  23.             tpd_flag = 0;  
  24.       
  25.             set_current_state(TASK_RUNNING);  
  26.             ......  
  27.       
  28.     #ifdef TPD_PROXIMITY  //TP的P-sensor功能,暂不分析  
  29.             if (tpd_proximity_flag == 1)  
  30.             {  
  31.                 i2c_smbus_read_i2c_block_data(i2c_client, 0xB0, 1, &state);  
  32.                 TPD_PROXIMITY_DEBUG("proxi_5206 0xB0 state value is 1131 0x%02X\n", state);  
  33.       
  34.                 if(!(state&0x01))  
  35.                 {  
  36.                     tpd_enable_ps(1);  
  37.                 }  
  38.       
  39.                 i2c_smbus_read_i2c_block_data(i2c_client, 0x01, 1, &proximity_status);  
  40.                 TPD_PROXIMITY_DEBUG("proxi_5206 0x01 value is 1139 0x%02X\n", proximity_status);  
  41.       
  42.                 if (proximity_status == 0xC0)  
  43.                 {  
  44.                     tpd_proximity_detect = 0;     
  45.                 }  
  46.                 else if(proximity_status == 0xE0)  
  47.                 {  
  48.                     tpd_proximity_detect = 1;  
  49.                 }  
  50.       
  51.                 TPD_PROXIMITY_DEBUG("tpd_proximity_detect 1149 = %d\n", tpd_proximity_detect);  
  52.       
  53.                 if ((err = tpd_read_ps()))  
  54.                 {  
  55.                     TPD_PROXIMITY_DMESG("proxi_5206 read ps data 1156: %d\n", err);   
  56.                 }  
  57.                 sensor_data.values[0] = tpd_get_ps_value();  
  58.                 sensor_data.value_divide = 1;  
  59.                 sensor_data.status = SENSOR_STATUS_ACCURACY_MEDIUM;  
  60.                 if ((err = hwmsen_get_interrupt_data(ID_PROXIMITY, &sensor_data)))  
  61.                 {  
  62.                     TPD_PROXIMITY_DMESG(" proxi_5206 call hwmsen_get_interrupt_data failed= %d\n", err);      
  63.                 }  
  64.             }   
  65.     #endif  
  66.       
  67.             if (tpd_touchinfo(&cinfo, &pinfo))   //获取TP设备数据,并把数据保存在cinfob buf中  
  68.             {  
  69.                 //TPD_DEBUG("point_num = %d\n",point_num);  
  70.                 TPD_DEBUG_SET_TIME;  
  71.                 if(point_num >0)   
  72.                 {  
  73.                     for(i =0; i<point_num; i++)//only support 3 point  
  74.                     {  
  75.                         printk(KERN_DEBUG"X:%4d, Y:%4d, P:%4d \n", cinfo.x[i], cinfo.y[i], cinfo.id[i]);  
  76.                           
  77.                         cinfo.x[i] = cinfo.x[i];  
  78.                         cinfo.y[i] = cinfo.y[i];  
  79.                           
  80.                         tpd_down(cinfo.x[i], cinfo.y[i], cinfo.id[i]);  //按下数据处理  
  81.                         printk(KERN_DEBUG"----calibration----- X:%4d, Y:%4d, P:%4d \n", cinfo.x[i], cinfo.y[i], cinfo.id[i]);  
  82.                     }  
  83.                     input_sync(tpd->dev);  
  84.                 }  
  85.                 else   
  86.                 {  
  87.                     tpd_up(cinfo.x[0], cinfo.y[0]); //弹起数据处理  
  88.                     //TPD_DEBUG("release --->\n");   
  89.                     //input_mt_sync(tpd->dev);  
  90.                     input_sync(tpd->dev);  
  91.                 }  
  92.             }  
  93.             ......  
  94.               
  95.         }while(!kthread_should_stop());  
  96.       
  97.         return 0;  
  98.     }  
复制代码
事故上报与响应
事故上报:触摸屏驱动程序通过输入子体系将处理后的触摸事故上报给体系。这些事故包括触摸点的位置、压力等信息。
事故响应:上层应用程序通过监听输入事故来响应用户的触摸操作。例如,当用户用手指在屏幕上滑动时,应用程序会吸取到相应的滑动事故,并据此执行相应的操作(如页面滚动)。
5. 触摸屏调试本领
1,利用体系自带的调试工具:
开辟者选项:Android装备通常有一个“开辟者选项”,此中包含了许多用于调试和性能优化的工具。通过开启“显示触摸操作”等选项,可以在屏幕上看到触摸的轨迹,帮助直观相识触摸操作的效果。
ADB(Android Debug Bridge):ADB是一个多功能的命令行工具,答应用户与连接的Android装备进行通信。通过ADB,可以执行各种调试命令,如检察日志、安装和调试应用等。
编写和运行测试程序:
开辟者可以编写专门的测试程序来检测触摸屏的响应情况,如绘制触摸轨迹、检测触摸点的坐标和压力等。这些测试程序可以帮助辨认触摸屏的硬件或软件标题。
检察和修改触摸屏配置文件:
在某些情况下,触摸屏的性能标题可能与配置文件有关。开辟者可以检察和修改这些配置文件,以调解触摸屏的灵敏度、响应速度等参数。
2,getevent调试方法
getevent是Android体系中用于获取输入装备事故信息的工具,它可以捕获触摸屏、键盘等输入装备的原始事故数据。以下是利用getevent进行触摸屏调试的根本方法:
打开终端或命令提示符:
在PC上,可以通过ADB连接到Android装备,并在终端或命令提示符中执行getevent命令。
检察所有输入装备:
利用getevent -p命令列出所有可用的输入装备及其支持的事故类型。找到触摸屏对应的装备文件(如/dev/input/eventX)。
监督触摸屏事故:
利用getevent -lt /dev/input/eventX命令(此中X是触摸屏装备文件的编号)来及时监督触摸屏事故。这个命令会显示触摸屏的触摸、滑动、抬起等事故,以及相应的时间戳和事故代码。
分析事故数据:
通过分析getevent输出的数据,可以相识触摸屏的响应情况,包括触摸点的坐标、压力值、事故类型等。这有助于辨认触摸屏的硬件或软件标题。
发送模拟事故:
固然getevent主要用于获取事故信息,但可以利用sendevent命令向装备发送模拟的触摸、按键等事故。这在进行主动化测试或模拟用户操作时非常有用。
3,ADB 提供了一个更高级的 input 命令,用于模拟键盘和触摸屏输入,而不需要知道底层的装备文件和事故代码。例如:
模拟触摸按下(在屏幕的500,500位置):
  1. adb shell input tap 500 500
复制代码
模拟滑动(从屏幕的500,500位置滑动到1000,1000位置):
  1. adb shell input swipe 500 500 1000 1000
复制代码
input 命令是模拟触摸事故的一种更简朴、更直观的方法,通常对于大多数开辟者来说已经富足了。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

数据人与超自然意识

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表