linux子系統分析及觸摸屏驅動淺析

前段時間負責項目的觸摸屏和光電鼠標,都是輸入設備,看了會這方面的資料,結合項目代碼,做點總結,基本上來自個人理解和互聯網

 

linux2.6以後,linux對輸入設備進行了抽象,抽象出了輸入子系統,該系統(Input子系統)是所有I/O設備驅動的中間層,爲上層提供了一個統一的界面,將事件的上報和處理分離開,採用了分層模式,在我們的driver中,我們只需要關注事件的上報,其他的都由linux自己處理。在上層系統中,它不需要知道底層有多少鍵盤,鼠標,軌跡球,觸摸屏等設備,只需要把上報上來的input事件做相應的處理就行了。

 

一.Input輸入子系統的框架

 

Input子系統將整個將輸入驅動分成三塊: driver,input core和Event handler. 一個輸入事件,如鼠標移動,鍵盤按鍵按下,joystick的移動,觸摸屏的點擊滑動等等通過 Driver -> InputCore -> Eventhandler -> userspace 的順序到達用戶空間傳給應用程序

而在我們的平時的驅動開發中,往往需要做的只是input driver這一層,其他的基本上都由linux做好了,不需要任何改動,除非需要加入新的功能支持之類的(如想把多點觸摸的支持添加進來等)

 

二.一個簡單使用input子系統的例子

在內核自帶的文檔Documentation/input/input-programming.txt中。有一個使用input子系統的例子,並附帶相應的說明。初學者可以以此例學習

 

 

  1. #include <linux/input.h>  
  2. #include <linux/module.h>  
  3. #include <linux/init.h>  
  4. #include <asm/irq.h>  
  5. #include <asm/io.h>  
  6. static struct input_dev *button_dev;  
  7. static irqreturn_t button_interrupt(int irq, void *dummy)  
  8. {  
  9. input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);  
  10. input_sync(button_dev);  
  11. return IRQ_HANDLED;  
  12. }  
  13. static int __init button_init(void)  
  14. {  
  15. int error;  
  16. if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {  
  17.                 printk(KERN_ERR "button.c: Can't allocate irq %d/n", button_irq);  
  18.                 return -EBUSY;  
  19.         }  
  20. button_dev = input_allocate_device();  
  21. if (!button_dev) {  
  22. printk(KERN_ERR "button.c: Not enough memory/n");  
  23. error = -ENOMEM;  
  24. goto err_free_irq;  
  25. }  
  26. button_dev->evbit[0] = BIT_MASK(EV_KEY);  
  27. button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);  
  28. error = input_register_device(button_dev);  
  29. if (error) {  
  30. printk(KERN_ERR "button.c: Failed to register device/n");  
  31. goto err_free_dev;  
  32. }  
  33. return 0;  
  34.  err_free_dev:  
  35. input_free_device(button_dev);  
  36.  err_free_irq:  
  37. free_irq(BUTTON_IRQ, button_interrupt);  
  38. return error;  
  39. }  
  40. static void __exit button_exit(void)  
  41. {  
  42.     input_unregister_device(button_dev);  
  43. free_irq(BUTTON_IRQ, button_interrupt);  
  44. }  
  45. module_init(button_init);  
  46. module_exit(button_exit);   
 

 

這個例子比較簡單,沒有具體的硬件設備,但程序裏包含了最基本的input子系統驅動的註冊流程。

1)在初始化函數中申請了一個input device

   button_dev = input_allocate_device();

2)註冊

      input_register_device(button_dev);

3)還需要申請一箇中斷處理例程,在中斷處理例程中將收到的按鍵信息上報給input子系統

       request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)

4)還有一個需要注意的就是上報數據的參數設置,告知input子系統它支持上報的事件

    button_dev->evbit[0] = BIT_MASK(EV_KEY);

button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); 

分別用來設置設備所產生的事件以及上報的按鍵值。Struct iput_dev中有兩個成員,一個是evbit.一個是keybit.分別用表示設備所支持的動作和按鍵類型。 

 

5input_report_key() 

  用於給上層上報一個按鍵動作

      input_sync() 

用來告訴上層,本次的上報事件已經完成了.

三.input core和Event handler

另外在input子系統中還有很多關於input core和Event handler等方面的介紹,這些主要是linux內核的工作,在我們driver開發中用的不多,也不是我們所關注的重點,我在這裏不做多的描述,如果感興趣的話可以去網上去找,很很多這方面的資源

四.G15項目中的觸摸屏驅動

  驅動程序流程和上述流程基本一致,但由於涉及到了具體的硬件設備,在一些細節處理方面會不一樣,也會複雜一些

  Host端通過IIC總線,從芯片讀出需要的數據,一般爲X,Y的絕對座標,還有數據的標誌位(單點,多點,未點擊)等,這些就夠了。有些芯片還有手勢之類的寄存器值,但在linux/android系統中一般都沒有使用,在上層處理都是用座標值的變化來算出手勢的。

 

初始化函數如下:

  1. static int micco_ts_probe(struct platform_device *pdev)  
  2. {  
  3. int ret;  
  4. struct proc_dir_entry *micco_ts_proc_entry;  
  5. micco_td = kzalloc(sizeof(struct micco_ts_data), GFP_KERNEL);  
  6. if (!micco_td) {  
  7. ret = -ENOMEM;  
  8. goto micco_ts_out;  
  9. }  
  10. //micco_td->pen_state = TSI_PEN_UP;  
  11. pen_state = TSI_PEN_UP;  
  12. micco_td->suspended = 0;  
  13. micco_td->use_count = 0;  
  14. mutex_init(&mutex);  
  15. /* register input device */  
  16. micco_ts_input_dev = input_allocate_device();  //申請一個input設備  
  17. if (micco_ts_input_dev == NULL) {  
  18. printk(KERN_ERR "%s: failed to allocate input dev/n",__FUNCTION__);  
  19. return -ENOMEM;  
  20. }  
  21.      //填充input 結構體,這些是對input設備的一些屬性描述,名稱,總線等  
  22. micco_ts_input_dev->name = "micco-ts";  
  23. micco_ts_input_dev->phys = "micco-ts/input0";  
  24. micco_ts_input_dev->dev.parent = &pdev->dev;  
  25. micco_ts_input_dev->open = new_ts_open;  
  26. micco_ts_input_dev->close = new_ts_close;  
  27. micco_ts_input_dev->id.bustype = BUS_I2C;  
  28.     //所支持的事件  
  29. set_bit(EV_SYN, micco_ts_input_dev->evbit);  
  30. set_bit(EV_KEY, micco_ts_input_dev->evbit);  
  31. set_bit(BTN_TOUCH, micco_ts_input_dev->keybit);  
  32. set_bit(BTN_TOUCH2, micco_ts_input_dev->keybit);  
  33. set_bit(EV_ABS, micco_ts_input_dev->evbit);  
  34. input_set_abs_params(micco_ts_input_dev, ABS_MT_POSITION_X, 0, 0x31f, 0, 0);  
  35. input_set_abs_params(micco_ts_input_dev, ABS_MT_POSITION_Y, 0, 0x1dF, 0, 0);  
  36. input_set_abs_params(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);  
  37. input_set_abs_params(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0);  
  38. //初始化一個工作隊列,用於調度中斷處理例程的底半部  
  39. INIT_WORK(&work, new_ts_work);  
  40.     //向系統註冊input設備  
  41. ret = input_register_device(micco_ts_input_dev);  
  42. if (ret) {  
  43. printk(KERN_ERR  
  44. "%s: unabled to register input device, ret = %d/n",  
  45. __FUNCTION__, ret);  
  46. return ret;  
  47. }  
  48. init_ts();  //芯片的一些初始化工作  
  49.    if (gpio_request(mfp_to_gpio(MFP_CFG_PIN(TP_INT)), "TP  INT")) {  
  50. gpio_free(mfp_to_gpio(MFP_CFG_PIN(TP_INT)));  
  51. printk(KERN_ERR "Request GPIO failed,"  
  52.        "gpio: %d/n", TP_INT);  
  53. return 0;  
  54. }  
  55. gpio_direction_input(mfp_to_gpio(MFP_CFG_PIN(TP_INT)));  
  56.     //用TP_INT腳申請一箇中斷,當手指觸摸到屏,將產生中斷,進入中斷處理程序,讀取數據並上報  
  57. if (request_irq(TP_IRQ_NO, new_ts_isr,IRQF_TRIGGER_FALLING, "micco-ts", NULL)) {  
  58.                 printk(KERN_ERR "micco_touch.c: Can't allocate irq %d/n", TP_IRQ_NO);  
  59.                 return -EBUSY;  
  60.         }  
  61. //屏蔽中斷,在open時再打開  
  62. disable_irq_nosync(TP_IRQ_NO);  
  63. //printk("disable_irq_nosync(TP_IRQ_NO)mark2/n");  
  64. enable_irq_flag = 0;  
  65. if (ret < 0)  
  66. goto pmic_cb_out;  
  67. micco_ts_proc_entry = create_proc_entry("driver/micc_ts", 0, NULL);  
  68. if (micco_ts_proc_entry) {   
  69. micco_ts_proc_entry->write_proc = micco_ts_proc_write;  
  70. }   
  71. return 0;  
  72. pmic_cb_out:  
  73. input_unregister_device(micco_ts_input_dev);  
  74. micco_ts_out:  
  75. kfree(micco_td);  
  76. return ret;  
  77. }  
 

 

中斷處理頂半部

static irqreturn_t new_ts_isr(int irq, void *dev_data)

{

schedule_work(&work);

return IRQ_HANDLED;

}

只是簡單的調度了工作隊列,以下才是真正的中斷處理(底半部)

  1. static void new_ts_work(struct work_struct *work)  
  2. {  
  3. u16 tem_x1, tem_y1;  
  4. u16 tem_x2, tem_y2;  
  5. u8 pen_down;  
  6. int pen_up_already_reported = 0;  
  7. mutex_lock(&mutex);  
  8. pen_down=tsi2c_byte_read(SLAVE_ADDR,CAP_TP_MODE);  
  9. //printk("new_ts_work pen_down is %x/n",pen_down);  
  10.    if(129==pen_down)  
  11. {  
  12.   pen_state = TSI_PEN_DOWN;  
  13.    cap_tp_read(&tem_x1, &tem_y1,1);  
  14.           // TOUCHSCREEN_CONSOLE_PRINT_XY1;  
  15.            input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 1);  
  16.            input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR,1);  
  17.            input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_X, tem_x1&0x3ff);  
  18.            input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_Y, tem_y1&0x3ff);  
  19.            input_mt_sync(micco_ts_input_dev);  
  20.            input_sync(micco_ts_input_dev);  
  21.     pen_up_already_reported = 0;  
  22. }  
  23. else if(130==pen_down)  
  24. {  
  25.   pen_state = TSI_PEN_DOWN;  
  26.   cap_tp_read(&tem_x1, &tem_y1,1);  
  27.   cap_tp_read(&tem_x2, &tem_y2,2);  
  28.   //TOUCHSCREEN_CONSOLE_PRINT_XY1;  
  29.   //TOUCHSCREEN_CONSOLE_PRINT_XY2;  
  30.         input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 1);  
  31.         input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR,1);  
  32.         input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_X, tem_x1&0x3ff);  
  33.         input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_Y, tem_y1&0x3ff);  
  34.         input_mt_sync(micco_ts_input_dev);  
  35.         input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 100);  
  36.         input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR,1);  
  37.         input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_X, tem_x2&0x3ff);  
  38.         input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_Y, tem_y2&0x3ff);  
  39.         input_mt_sync(micco_ts_input_dev);  
  40.         input_sync(micco_ts_input_dev);  
  41.  pen_up_already_reported = 0;  
  42. }  
  43. else  
  44.         {  
  45.  if((pen_state != TSI_PEN_UP) && !pen_up_already_reported) {  
  46.    input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 0);  
  47.            input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR, 0);  
  48.            input_mt_sync(micco_ts_input_dev);  
  49.            input_sync(micco_ts_input_dev);  
  50.    pen_up_already_reported = 1;  
  51.   }  
  52.  pen_state = TSI_PEN_UP;  
  53. }  
  54. mutex_unlock(&mutex);  
  55. }  
 

 

五.單點觸摸&多點觸摸

   如上面的例子所示,已經包含了多點觸摸的功能,現在簡單介紹一下單點觸摸和多點觸摸在驅動實現時的區別

  要實現多點觸摸,必須要有內核的支持,也就是說input子系統中,input core和Event handler層要支持多點,在linux官方2.6.30中才加入了多點支持,但現在很多2.6.29的BSP中也給多點觸摸打了補丁,也是可以用的,G15項目中的就是這樣

   首先,在probe函數的設備初始化階段

input_set_abs_params()函數,設置方式不同

單點:

input_set_abs_params(micco_ts_input_dev, ABS_X, 0, 0x31f, 0, 0);

input_set_abs_params(micco_ts_input_dev, ABS_Y, 0, 0x1df, 0, 0);

input_set_abs_params(micco_ts_input_dev, ABS_PRESSURE, 0, 255, 0, 0);

input_set_abs_params(micco_ts_input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);

多點:

input_set_abs_params(micco_ts_input_dev, ABS_MT_POSITION_X, 0, 0x31f, 0, 0);

input_set_abs_params(micco_ts_input_dev, ABS_MT_POSITION_Y, 0, 0x1dF, 0, 0);

input_set_abs_params(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);

    //相當於單點屏的 ABX_PRESSURE

input_set_abs_params(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0);

    //相當於單點屏的 ABS_TOOL_WIDTH

其次,就是上報數據的方式

單點上報:

input_report_abs(micco_ts_input_dev,ABS_X,tem_x&0x3ff);

input_report_abs(micco_ts_input_dev,ABS_Y,tem_y&0x3ff);

input_report_key(micco_ts_input_dev, BTN_TOUCH, 1);

input_sync(micco_ts_input_dev);

ABS_X

ABS_Y

SYN_REPORT

 

多點上報:

        input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 100);

        input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR,1);

        input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_X, tem_x1&0x3ff);

        input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_Y, tem_y1&0x3ff);

        input_mt_sync(micco_ts_input_dev);

        input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 100);

        input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR,1);

        input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_X, tem_x2&0x3ff);

        input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_Y, tem_y2&0x3ff);

        input_mt_sync(micco_ts_input_dev);

        input_sync(micco_ts_input_dev);

  

 for(int i;i<n;i++)

  {

          ABS_MT_POSITION_X

  ABS_MT_POSITION_Y

  SYN_MT_REPORT

  }

      SYN_REPORT

 

以上只是單點和多點的在驅動開發時的主要區別,當然還有其他很多方面的差異,如硬件支持等,如果對多點觸摸感興趣的話可以去仔細閱讀一下linux的內核文檔Documentation / input / multi-touch-protocol.txt

發佈了12 篇原創文章 · 獲贊 6 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章