转自:http://blog.chinaunix.net/uid-28320320-id-3389196.html

  1. 、参考文章
  2. Andoridinput系统的事件处理
  3.  
  4. 、源码分析 linux 3.6.
  5. )查看linux-3.6./drivers/inputMakefile
  6.  
  7. 点击(此处)折叠或打开
  8.  
  9. obj-$(CONFIG_INPUT) += input-core.o
  10. input-core-y := input.o input-compat.o input-mt.o ff-core.o
  11.  
  12. )查看文件input.c
  13.  
  14. 点击(此处)折叠或打开
  15.  
  16. /* input subsystem entry */
  17. subsys_initcall(input_init);
  18. module_exit(input_exit);
  19.  
  20. )input.c搞啥子
  21.  
  22. 点击(此处)折叠或打开
  23.  
  24. /* sysfs/procfs/devfs show */
  25. |-----------|
  26. | \/
  27. | err = class_register(&input_class);
  28. |
  29. |-----------|
  30. | \/
  31. | err = input_proc_init();
  32. |
  33. |-----------|
  34. | \/
  35. | err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
  36.  
  37. 根据下面的方法,发现手机的tp 对应event1
  38.  
  39. 点击(此处)折叠或打开
  40.  
  41. # getevent
  42. add device : /dev/input/event0
  43. name: "fluid-keypad"
  44. add device : /dev/input/event3
  45. name: "7k_handset"
  46. add device : /dev/input/event2
  47. name: "sensors"
  48. add device : /dev/input/event1
  49. name: "Synaptics RMI4"
  50.  
  51. # pwd
  52. /sys/class/input
  53. # ls
  54. event0 event1 event2 event3 input0 input1 input2 input3
  55.  
  56. # cat /proc/bus/input/devices
  57. I: Bus= Vendor= Product= Version=
  58. N: Name="fluid-keypad"
  59. P: Phys=fluid-keypad/input0
  60. S: Sysfs=/devices/i2c-/-/pm8058-keypad/input/input0
  61. U: Uniq=
  62. H: Handlers=kbd event0
  63. B: EV=
  64. B: KEY= c0000
  65. B: MSC=
  66.  
  67. I: Bus= Vendor= Product= Version=
  68. N: Name="Synaptics RMI4"
  69. P: Phys=Synaptics_rmi
  70. S: Sysfs=/devices/virtual/input/input1
  71. U: Uniq=
  72. H: Handlers=event1
  73. B: EV=b
  74. B: KEY=
  75. B: ABS=
  76.  
  77. I: Bus= Vendor= Product= Version=
  78. N: Name="sensors"
  79. P: Phys=
  80. S: Sysfs=/devices/virtual/input/input2
  81. U: Uniq=
  82. H: Handlers=event2
  83. B: EV=
  84. B: ABS= 20304bf
  85.  
  86. I: Bus= Vendor= Product= Version=
  87. N: Name="7k_handset"
  88. P: Phys=
  89. S: Sysfs=/devices/virtual/input/input3
  90. U: Uniq=
  91. H: Handlers=kbd event3
  92. B: EV=
  93. B: KEY= 1c0800
  94. B: SW=
  95.  
  96. )touch panel驱动源码
  97.  
  98. 点击(此处)折叠或打开
  99.  
  100. #include <linux/module.h>
  101. #include <linux/kernel.h>
  102. #include <linux/slab.h>
  103. #include <linux/platform_device.h>
  104. #include <linux/i2c.h>
  105. #include <linux/input.h>
  106.  
  107. #include "gsl1680.h"
  108.  
  109. #define gsl_pr(fmt, arg...) \
  110. printk(KERN_ERR "[GSL]%s: \033[32m" fmt "\033[0m\n", __FUNCTION__, ##arg)
  111.  
  112. #define GSL_ADAPTER_INDEX 0
  113. #define GSL1680D0_ID 0
  114. #define GSL_DEV_NAME "gsl"
  115.  
  116. /**
  117. * Description : global var
  118. */
  119. static struct gsl_ts_data *ddata = NULL;
  120. static const struct i2c_device_id gsl_id[] = {
  121. {GSL_DEV_NAME, GSL1680D0_ID},
  122. {},
  123. };
  124. MODULE_DEVICE_TABLE(silead, gsl_id);
  125.  
  126. static struct i2c_board_info gsl_i2c_info = {
  127. .type = GSL_DEV_NAME,
  128. .addr = 0x40,
  129. };
  130.  
  131. /**
  132. * Description : gsl soc operation
  133. */
  134. static int gsl_hw_init(void)
  135. {
  136. return ;
  137. }
  138.  
  139. static int gsl_sw_init(void)
  140. {
  141. return ;
  142. }
  143.  
  144. /**
  145. * Description : touch panel driver
  146. */
  147. static void gsl_report_work(struct work_struct *work)
  148. {
  149. }
  150.  
  151. static int gsl_request_input(void)
  152. {
  153. int ret = ;
  154.  
  155. ddata->idev = input_allocate_device();
  156. if (!ddata->idev) {
  157. dev_err(&ddata->idev->dev, "could not allocate device\n");
  158. return -ENODEV;
  159. }
  160.  
  161. ddata->idev->name = GSL_DEV_NAME;
  162. ddata->idev->id.bustype = BUS_I2C;
  163. ddata->idev->dev.parent = &ddata->client->dev;
  164. input_set_drvdata(ddata->idev, ddata);
  165.  
  166. __set_bit(EV_ABS, ddata->idev->evbit);
  167.  
  168. input_set_abs_params(ddata->idev, ABS_MT_POSITION_X,
  169. DIS_MIN_X, DIS_MAX_X, , );
  170. input_set_abs_params(ddata->idev, ABS_MT_POSITION_Y,
  171. DIS_MIN_Y, DIS_MAX_Y, , );
  172. input_set_abs_params(ddata->idev, ABS_MT_TOUCH_MAJOR,
  173. MIN_TOUCH, MAX_TOUCH, , );
  174. input_set_abs_params(ddata->idev, ABS_MT_WIDTH_MAJOR,
  175. MIN_WIDTH, MAX_WIDTH, , );
  176. input_set_abs_params(ddata->idev, ABS_MT_TRACKING_ID,
  177. MIN_TRCKID, MAX_TRCKID, , );
  178.  
  179. INIT_WORK(&ddata->work, gsl_report_work);
  180.  
  181. ddata->wq = create_singlethread_workqueue(GSL_DEV_NAME);
  182. if (!ddata->wq) {
  183. dev_err(&ddata->idev->dev, "could not create workqueue\n");
  184. ret = -ENOMEM;
  185. goto error_wq_create;
  186. }
  187. #if 0
  188. ddata->pm.suspend = gsl_suspend;
  189. ddata->pm.resume = gsl_resume;
  190. register_early_suspend(&ddata->pm);
  191. #endif
  192. ret = input_register_device(ddata->idev);
  193. if (ret) {
  194. dev_err(&ddata->idev->dev, "ret = %d : could not register input device\n", ret);
  195. goto error_unreg_device;
  196. }
  197. return ;
  198.  
  199. error_unreg_device:
  200. destroy_workqueue(ddata->wq);
  201. error_wq_create:
  202. input_free_device(ddata->idev);
  203. return ret;
  204. }
  205.  
  206. static __devinit int gsl_probe(struct i2c_client *client,
  207. const struct i2c_device_id *id)
  208. {
  209. int ret = ;
  210.  
  211. gsl_pr();
  212.  
  213. ddata->ti = kzalloc(sizeof(union gsl_touch_info), GFP_KERNEL);
  214. if (!ddata->ti) {
  215. dev_err(&client->dev, "failed to alloc ddata->ti memory!\n");
  216. ret = -ENOMEM;
  217. goto error_alloc_mem;
  218. }
  219.  
  220. /* regist a input dev */
  221. ret = gsl_request_input();
  222. if (ret) {
  223. dev_err(&client->dev, "failed to regist input dev!\n");
  224. goto error_regist_input;
  225. }
  226.  
  227. /* setup the gpio -- irq & rst */
  228. ret = gsl_hw_init();
  229. if (ret) {
  230. dev_err(&client->dev, "failed to init hw!\n");
  231. goto error_init_hw;
  232. }
  233.  
  234. /* setup client data & download fw */
  235. ret = gsl_sw_init();
  236. if (ret) {
  237. dev_err(&client->dev, "failed to init sw!\n");
  238. goto error_init_sw;
  239. }
  240.  
  241. return ;
  242.  
  243. error_init_sw:
  244. error_init_hw:
  245. destroy_workqueue(ddata->wq);
  246. input_free_device(ddata->idev);
  247. error_regist_input:
  248. kfree(ddata->ti);
  249. input_unregister_device(ddata->idev);
  250. destroy_workqueue(ddata->wq);
  251. input_free_device(ddata->idev);
  252. error_alloc_mem:
  253. kfree(ddata);
  254. return ret;
  255. }
  256.  
  257. static __devexit int gsl_remove(struct i2c_client *client)
  258. {
  259. return ;
  260. }
  261.  
  262. static struct i2c_driver gsl_driver = {
  263. .driver = {
  264. .name = GSL_DEV_NAME,
  265. .owner = THIS_MODULE,
  266. },
  267. .probe = gsl_probe,
  268. .remove = gsl_remove,
  269. .id_table = gsl_id,
  270. };
  271.  
  272. /**
  273. * Description : module operation
  274. */
  275. static __init int gsl_init(void)
  276. {
  277. int ret = ;
  278. struct i2c_adapter *adapter;
  279.  
  280. gsl_pr();
  281.  
  282. ddata = kzalloc(sizeof(struct gsl_ts_data), GFP_KERNEL);
  283. if (!ddata) {
  284. gsl_pr("alloc mem error");
  285. goto err_mem;
  286. }
  287.  
  288. /* tips : try_module_get */
  289. adapter = i2c_get_adapter(GSL_ADAPTER_INDEX);
  290. if (!(adapter)) {
  291. gsl_pr("get %d adapter failed", GSL_ADAPTER_INDEX);
  292. ret = -ENODEV;
  293. goto err_adap;
  294. }
  295.  
  296. ddata->client = i2c_new_device(adapter, &gsl_i2c_info);
  297. if (!(ddata->client)) {
  298. gsl_pr("get i2c device error");
  299. ret = -ENODEV;
  300. goto err_dev;
  301. }
  302.  
  303. /* release the module */
  304. i2c_put_adapter(adapter);
  305.  
  306. ret = i2c_add_driver(&gsl_driver);
  307. if (ret) {
  308. gsl_pr("i2c add driver failed");
  309. goto err_driver;
  310. }
  311.  
  312. return ;
  313.  
  314. err_driver:
  315. i2c_unregister_device(ddata->client);
  316. i2c_put_adapter(adapter);
  317. err_dev:
  318. err_adap:
  319. kfree(ddata);
  320. err_mem:
  321. return ret;
  322. }
  323.  
  324. static __exit void gsl_exit(void)
  325. {
  326. gsl_pr();
  327.  
  328. /* reverse effect of i2c_new_device() */
  329. i2c_del_driver(&gsl_driver);
  330. i2c_unregister_device(ddata->client);
  331. kfree(ddata);
  332.  
  333. return;
  334. }
  335.  
  336. module_init(gsl_init);
  337. module_exit(gsl_exit);
  338.  
  339. MODULE_LICENSE("GPL");
  340. MODULE_AUTHOR("mark");
  341.  
  342. 点击(此处)折叠或打开
  343.  
  344. //touch coordinate range
  345. #define TP_WIDTH 480
  346. #define TP_LENTH 800
  347.  
  348. //coordinate direction
  349. #define TP_DIREC 1 // if 1 is (1,1), then 2(1,-1), 3(-1,-1), 4(-1,1)
  350.  
  351. //touch threshold
  352. #define MAI_TRSD 200
  353. #define SUB_TRSD 40
  354. #define SUM_TRSD (MAI_TRSD + SUB_TRSD + 20)
  355.  
  356. //touch tigger condition
  357. #define TRIG_MOD 1 // 1 is edge, 0 is level
  358. #define VOLT_LEV 0 // if trig mode is edge,
  359. // 0 is IRQF_TRIGGER_RISING, 1 is IRQF_TRIGGER_FALLING
  360. // if trig mode is level,
  361. // 0 is IRQF_TRIGGER_HIGH, 1 is IRQF_TRIGGER_LOW
  362.  
  363. //touch sensitivity
  364. #define TP_DACG 0x00100010 //9f/30
  365. #define DAC_STEP 0x8e //if TP_DACG=0x00180018,TP_DAC_STEP=0x61
  366. //if TP_DACG=0x00100010,TP_DAC_STEP=0x8e
  367. //if TP_DACG=0x000c000c,TP_DAC_STEP=0xbb
  368. //if TP_DACG=0x000a000a,TP_DAC_STEP=0xdf
  369. //if TP_DACG=0x00080008,TP_DAC_STEP=0x114
  370. //if TP_DACG=0x00060006,TP_DAC_STEP=0x16e
  371. #define CHANGE_CONDITION 0x0 //0--use average,1--use max
  372.  
  373. #define GSL_PAGE_REG 0xf0
  374. #define GSL_CLOCK_REG 0xe4
  375. #define GSL_START_REG 0xe0
  376. #define GSL_CLOCK_REG 0xe4
  377. #define POWE_FAIL_REG 0xbc
  378. #define TOUCH_INFO_REG 0x80
  379.  
  380. #define DIS_MIN_X 0
  381. #define DIS_MAX_X TP_WIDTH
  382. #define DIS_MIN_Y 0
  383. #define DIS_MAX_Y TP_LENTH
  384.  
  385. #define MIN_TOUCH 0
  386. #define MAX_TOUCH 1
  387. #define MIN_WIDTH 0
  388. #define MAX_WIDTH 1
  389. #define MIN_TRCKID 1
  390. #define MAX_TRCKID 5
  391.  
  392. /* the data format of one point */
  393. union gsl_point_data {
  394. struct {
  395. u16 y;
  396. u16 x : ;
  397. u16 id : ;
  398. };
  399. u8 all[];
  400. };
  401.  
  402. /* the 24-byte data of read once */
  403. union gsl_touch_info {
  404. struct {
  405. u32 finger_num : ;
  406. u32 : ;
  407. union gsl_point_data point[];
  408. };
  409. u8 all[];
  410. };
  411.  
  412. struct gsl_ts_data {
  413. union gsl_touch_info *ti;
  414. struct i2c_client *client;
  415. struct input_dev *idev;
  416. struct workqueue_struct *wq;
  417. struct work_struct work;
  418. unsigned int irq;
  419. };
  420.  
  421. /* Fixme mem Alig */
  422. struct fw_data {
  423. u32 offset : ;
  424. u32 : ;
  425. u32 val;
  426. };
  427.  
  428. static const struct fw_data GSL1680_D0_FW[] = {
  429. /* void */
  430. { },
  431. };
  432.  
  433. )input_report_abs上报流程
  434.  
  435. 点击(此处)折叠或打开
  436.  
  437. /* 观察linux-3.6.3/drivers/input/Kconfig, 对应/dev/input/eventX */
  438. config INPUT_EVDEV
  439. tristate "Event interface"
  440. help
  441. Say Y here if you want your input device events be accessible
  442. under char device :+ - /dev/input/eventX in a generic way.
  443.  
  444. To compile this driver as a module, choose M here: the
  445. module will be called evdev.
  446.  
  447. android 4.0 一般报点序列:
  448.  
  449. 点击(此处)折叠或打开
  450.  
  451. input_mt_slot(ts->input, id);
  452. input_report_abs(ts->input, ABS_MT_TRACKING_ID, id);
  453. input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, );
  454. input_report_abs(ts->input, ABS_MT_POSITION_X, x);
  455. input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
  456. input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, );
  457. input_mt_sync(ts->input);
  458.  
  459. 点击(此处)折叠或打开
  460.  
  461. input_sync(ts->input);
  462.  
  463. input_handle_event(dev, type, code, value)上报流程:
  464.  
  465. 点击(此处)折叠或打开
  466.  
  467. ...
  468. switch (type) {
  469.  
  470. case EV_SYN:
  471. switch (code) {
  472. ...
  473. case SYN_REPORT:
  474. if (!dev->sync) {
  475. dev->sync = true;
  476. disposition = INPUT_PASS_TO_HANDLERS;
  477. }
  478. break;
  479. case SYN_MT_REPORT:
  480. dev->sync = false;
  481. disposition = INPUT_PASS_TO_HANDLERS;
  482. break;
  483. }
  484. break;
  485.  
  486. case EV_KEY:
  487. if (is_event_supported(code, dev->keybit, KEY_MAX) &&
  488. !!test_bit(code, dev->key) != value) {
  489.  
  490. if (value != ) {
  491. __change_bit(code, dev->key);
  492. if (value)
  493. input_start_autorepeat(dev, code);
  494. else
  495. input_stop_autorepeat(dev);
  496. }
  497.  
  498. disposition = INPUT_PASS_TO_HANDLERS;
  499. }
  500. break;
  501. ...
  502. case EV_ABS:
  503. if (is_event_supported(code, dev->absbit, ABS_MAX))
  504. disposition = input_handle_abs_event(dev, code, &value);
  505.  
  506. break;
  507. ...
  508. }
  509.  
  510. ...
  511. if (disposition & INPUT_PASS_TO_HANDLERS)
  512. input_pass_event(dev, type, code, value);
  513.  
  514. 点击(此处)折叠或打开
  515.  
  516. static void input_pass_event(struct input_dev *dev,
  517. unsigned int type, unsigned int code, int value)
  518. {
  519. struct input_handler *handler;
  520. struct input_handle *handle;
  521.  
  522. rcu_read_lock();
  523.  
  524. /* 获得一个被RCU保护的指针 */
  525. handle = rcu_dereference(dev->grab);
  526. /* (1) 如果是设备专有handle, 仅将事件传给该handler. */
  527. if (handle)
  528. handle->handler->event(handle, type, code, value);
  529. else {
  530. bool filtered = false;
  531.  
  532. /* (2)遍历与此设备连接的每一个handle. */
  533. list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
  534. /* (3)如果hnadle已经被打开. */
  535. if (!handle->open)
  536. continue;
  537.  
  538. handler = handle->handler;
  539. if (!handler->filter) {
  540. if (filtered)
  541. break;
  542. /* (4)将事件分发给handler的事件处理函数. */
  543. handler->event(handle, type, code, value);
  544.  
  545. } else if (handler->filter(handle, type, code, value))
  546. filtered = true;
  547. }
  548. }
  549.  
  550. rcu_read_unlock();
  551. }
  552.  
  553. 附上android .0上报方式(linux必须2..38以上)
  554.  
  555. 点击(此处)折叠或打开
  556.  
  557. #include <linux/input/mt.h>
  558.  
  559. 点击(此处)折叠或打开
  560.  
  561. //down
  562. input_mt_slot(ts->input_dev, id);
  563. //input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
  564. input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true);
  565. input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
  566. input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
  567. input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
  568.  
  569. 点击(此处)折叠或打开
  570.  
  571. //up
  572. input_mt_slot(ts->input_dev, id);
  573. //input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
  574. input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
  575.  
  576. 点击(此处)折叠或打开
  577.  
  578. //init
  579. //__set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
  580. input_mt_init_slots(ts->input_dev, );

input上报流程分析【转】的更多相关文章

  1. Linux input系统数据上报流程【转】

    转自:https://segmentfault.com/a/1190000017255939 作为鸡生蛋系列文章,这里主要关注Linux input系统,主要为触摸事件上报流程. 读该文章最好有对li ...

  2. Hogp连接流程分析

    当BLE设备已经完成配对,并且完成GATT服务的搜索,下一步就开始profile 的连接流程了,一般LE设备都是走的HOGP的流程,我们这篇文章就分析一下hogp的连接流程. 连接是从framewor ...

  3. thttpd和cgilua安装与运行流程分析

    安装 参考如下博文安装thttpd软件 http://blog.csdn.net/21aspnet/article/details/7045845 http://blog.csdn.net/drago ...

  4. Android7.0 Phone应用源码分析(一) phone拨号流程分析

    1.1 dialer拨号 拨号盘点击拨号DialpadFragment的onClick方法会被调用 public void onClick(View view) { int resId = view. ...

  5. Android 4.4KitKat AudioRecord 流程分析

    Android是架构分为三层: 底层      Linux Kernel 中间层  主要由C++实现 (Android 60%源码都是C++实现) 应用层  主要由JAVA开发的应用程序 应用程序执行 ...

  6. HDFS2.x之RPC流程分析

    HDFS2.x之RPC流程分析 1 概述 Hadoop提供了一个统一的RPC机制来处理client-namenode, namenode-dataname,client-dataname之间的通信.R ...

  7. hadoop运行流程分析源代码级

    前言: 最近一直在分析hadoop的运行流程,我们查阅了大量的资料,虽然从感性上对这个流程有了一个认识但是我总是感觉对mapreduce的运行还是没有一个全面的认识,所以决定从源代码级别对mapred ...

  8. openstack之nova-api服务流程分析

    nova-api公布api服务没实用到一个些框架,基本都是从头写的.在不了解它时,以为它很复杂,难以掌握.花了两三天的时间把它分析一遍后,发现它本身的结构比較简单,主要难点在于对它所使用的一些类库不了 ...

  9. Android7.0 Phone应用源码分析(四) phone挂断流程分析

    电话挂断分为本地挂断和远程挂断,下面我们就针对这两种情况各做分析 先来看下本地挂断电话的时序图: 步骤1:点击通话界面的挂断按钮,会调用到CallCardPresenter的endCallClicke ...

随机推荐

  1. phpstudy配置SSL证书的步骤(Apache环境)以及一些注意事项

    准备工具(我自己的): 腾讯云的域名和云主机,还有SSL证书,以及phpstudy 首先要下载自己的SSL证书,会得到一个压缩包,解压以后会得到四个文件夹和一个csr文件, Apache文件夹内三个文 ...

  2. python3 练习题100例 (十五)

    这个比较难,主要难在考虑的问题太多,有好几个还没写出来.有空再来改进.请高手指教! #!/usr/bin/env python3 # -*- coding: utf-8 -*- __author__ ...

  3. Codeforces Round #460 (Div. 2)-B. Perfect Number

    B. Perfect Number time limit per test2 seconds memory limit per test256 megabytes Problem Descriptio ...

  4. PAT basic 1086

    1086 就不告诉你 (15 分) 做作业的时候,邻座的小盆友问你:“五乘以七等于多少?”你应该不失礼貌地围笑着告诉他:“五十三.”本题就要求你,对任何一对给定的正整数,倒着输出它们的乘积. 输入格式 ...

  5. UVa 1455 Kingdom 线段树 并查集

    题意: 平面上有\(n\)个点,有一种操作和一种查询: \(road \, A \, B\):在\(a\),\(b\)两点之间加一条边 \(line C\):询问直线\(y=C\)经过的连通分量的个数 ...

  6. js中基础数据类型

    变量声明 undefined //未定义只声明   var age; alert(name);function fc(a1,a2,a3) { //alert(a1); //alert(a2); //a ...

  7. AppDOMain(摘录)

    AppDomain是CLR的运行单元,它可以加载Assembly.创建对象以及执行程序. AppDomain是CLR实现 代码隔离 的基本机制. 每一个AppDomain可以单独运行.停止:每个App ...

  8. Windows核心编程小结3

    内存映射和堆栈 内存映射文件 内存映射文件可以用于3个不同的目的: 系统使用内存映射文件,以便加载和执行.exe和DLL文件.这可以大大节省页文件空间和应用程序启动运行所需的时间. 可以使用内存映射文 ...

  9. load_file()与into outfile函数详解

    load_file()函数的使用: 1.使用条件 ①有读取文件的权限 r and (select count(*) from mysql.user)>0 如果返回正常则说明有权限,反之没有 ②文 ...

  10. Python+Selenium练习篇之3-利用tag name定位元素

    前一篇文章介绍了如何通过元素的id值来定位web元素,本文介绍如何通过tag name来定位元素.个人认为,通过tag name来定位还是有很大缺陷,定位不够精确.主要是tag name有很多重复的, ...