linux子系統分析及觸控螢幕驅動淺析

來源:互聯網
上載者:User

 

 前段時間負責項目的觸控螢幕和光電滑鼠,都是輸入裝置,看了會這方面的資料,結合項目代碼,做點總結,基本上來自個人理解和互連網

 

在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子系統的例子,並附帶相應的說明。初學者可以以此例學習

 

 

#include <linux/input.h><br />#include <linux/module.h><br />#include <linux/init.h><br />#include <asm/irq.h><br />#include <asm/io.h><br />static struct input_dev *button_dev;<br />static irqreturn_t button_interrupt(int irq, void *dummy)<br />{<br />input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);<br />input_sync(button_dev);<br />return IRQ_HANDLED;<br />}<br />static int __init button_init(void)<br />{<br />int error;<br />if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {<br /> printk(KERN_ERR "button.c: Can't allocate irq %d/n", button_irq);<br /> return -EBUSY;<br /> }<br />button_dev = input_allocate_device();<br />if (!button_dev) {<br />printk(KERN_ERR "button.c: Not enough memory/n");<br />error = -ENOMEM;<br />goto err_free_irq;<br />}<br />button_dev->evbit[0] = BIT_MASK(EV_KEY);<br />button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);<br />error = input_register_device(button_dev);<br />if (error) {<br />printk(KERN_ERR "button.c: Failed to register device/n");<br />goto err_free_dev;<br />}<br />return 0;<br /> err_free_dev:<br />input_free_device(button_dev);<br /> err_free_irq:<br />free_irq(BUTTON_IRQ, button_interrupt);<br />return error;<br />}<br />static void __exit button_exit(void)<br />{<br /> input_unregister_device(button_dev);<br />free_irq(BUTTON_IRQ, button_interrupt);<br />}<br />module_init(button_init);<br />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.分別用表示裝置所支援的動作和按鍵類型。 

 

5)input_report_key() 

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

      input_sync() 

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

三.input core和Event handler

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

四.G15項目中的觸控螢幕驅動

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

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

 

初始化函數如下:

static int micco_ts_probe(struct platform_device *pdev)<br />{<br />int ret;<br />struct proc_dir_entry *micco_ts_proc_entry;<br />micco_td = kzalloc(sizeof(struct micco_ts_data), GFP_KERNEL);<br />if (!micco_td) {<br />ret = -ENOMEM;<br />goto micco_ts_out;<br />}<br />//micco_td->pen_state = TSI_PEN_UP;<br />pen_state = TSI_PEN_UP;<br />micco_td->suspended = 0;<br />micco_td->use_count = 0;<br />mutex_init(&mutex);<br />/* register input device */<br />micco_ts_input_dev = input_allocate_device(); //申請一個input裝置<br />if (micco_ts_input_dev == NULL) {<br />printk(KERN_ERR "%s: failed to allocate input dev/n",__FUNCTION__);<br />return -ENOMEM;<br />}<br /> //填充input 結構體,這些是對input裝置的一些屬性描述,名稱,匯流排等<br />micco_ts_input_dev->name = "micco-ts";<br />micco_ts_input_dev->phys = "micco-ts/input0";<br />micco_ts_input_dev->dev.parent = &pdev->dev;<br />micco_ts_input_dev->open = new_ts_open;<br />micco_ts_input_dev->close = new_ts_close;<br />micco_ts_input_dev->id.bustype = BUS_I2C;<br /> //所支援的事件<br />set_bit(EV_SYN, micco_ts_input_dev->evbit);<br />set_bit(EV_KEY, micco_ts_input_dev->evbit);<br />set_bit(BTN_TOUCH, micco_ts_input_dev->keybit);<br />set_bit(BTN_TOUCH2, micco_ts_input_dev->keybit);<br />set_bit(EV_ABS, micco_ts_input_dev->evbit);<br />input_set_abs_params(micco_ts_input_dev, ABS_MT_POSITION_X, 0, 0x31f, 0, 0);<br />input_set_abs_params(micco_ts_input_dev, ABS_MT_POSITION_Y, 0, 0x1dF, 0, 0);<br />input_set_abs_params(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);<br />input_set_abs_params(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0);<br />//初始化一個工作隊列,用於調度中斷處理常式的底半部<br />INIT_WORK(&work, new_ts_work);<br /> //向系統註冊input裝置<br />ret = input_register_device(micco_ts_input_dev);<br />if (ret) {<br />printk(KERN_ERR<br />"%s: unabled to register input device, ret = %d/n",<br />__FUNCTION__, ret);<br />return ret;<br />}<br />init_ts(); //晶片的一些初始化工作<br /> if (gpio_request(mfp_to_gpio(MFP_CFG_PIN(TP_INT)), "TP INT")) {<br />gpio_free(mfp_to_gpio(MFP_CFG_PIN(TP_INT)));<br />printk(KERN_ERR "Request GPIO failed,"<br /> "gpio: %d/n", TP_INT);<br />return 0;<br />}<br />gpio_direction_input(mfp_to_gpio(MFP_CFG_PIN(TP_INT)));<br /> //用TP_INT腳申請一個中斷,當手指觸摸到屏,將產生中斷,進入中斷處理常式,讀取資料並上報<br />if (request_irq(TP_IRQ_NO, new_ts_isr,IRQF_TRIGGER_FALLING, "micco-ts", NULL)) {<br /> printk(KERN_ERR "micco_touch.c: Can't allocate irq %d/n", TP_IRQ_NO);<br /> return -EBUSY;<br /> }<br />//屏蔽中斷,在open時再開啟<br />disable_irq_nosync(TP_IRQ_NO);<br />//printk("disable_irq_nosync(TP_IRQ_NO)mark2/n");<br />enable_irq_flag = 0;<br />if (ret < 0)<br />goto pmic_cb_out;<br />micco_ts_proc_entry = create_proc_entry("driver/micc_ts", 0, NULL);<br />if (micco_ts_proc_entry) {<br />micco_ts_proc_entry->write_proc = micco_ts_proc_write;<br />}<br />return 0;<br />pmic_cb_out:<br />input_unregister_device(micco_ts_input_dev);<br />micco_ts_out:<br />kfree(micco_td);<br />return ret;<br />} 

 

中斷處理頂半部

static irqreturn_t new_ts_isr(int irq, void *dev_data)

{

schedule_work(&work);

return IRQ_HANDLED;

}

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

static void new_ts_work(struct work_struct *work)<br />{<br />u16 tem_x1, tem_y1;<br />u16 tem_x2, tem_y2;<br />u8 pen_down;<br />int pen_up_already_reported = 0;<br />mutex_lock(&mutex);<br />pen_down=tsi2c_byte_read(SLAVE_ADDR,CAP_TP_MODE);<br />//printk("new_ts_work pen_down is %x/n",pen_down);<br /> if(129==pen_down)<br />{<br /> pen_state = TSI_PEN_DOWN;<br /> cap_tp_read(&tem_x1, &tem_y1,1);<br /> // TOUCHSCREEN_CONSOLE_PRINT_XY1;<br /> input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 1);<br /> input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR,1);<br /> input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_X, tem_x1&0x3ff);<br /> input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_Y, tem_y1&0x3ff);<br /> input_mt_sync(micco_ts_input_dev);<br /> input_sync(micco_ts_input_dev);<br /> pen_up_already_reported = 0;<br />}<br />else if(130==pen_down)<br />{<br /> pen_state = TSI_PEN_DOWN;<br /> cap_tp_read(&tem_x1, &tem_y1,1);<br /> cap_tp_read(&tem_x2, &tem_y2,2);<br /> //TOUCHSCREEN_CONSOLE_PRINT_XY1;<br /> //TOUCHSCREEN_CONSOLE_PRINT_XY2;<br /> input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 1);<br /> input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR,1);<br /> input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_X, tem_x1&0x3ff);<br /> input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_Y, tem_y1&0x3ff);<br /> input_mt_sync(micco_ts_input_dev);<br /> input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 100);<br /> input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR,1);<br /> input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_X, tem_x2&0x3ff);<br /> input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_Y, tem_y2&0x3ff);<br /> input_mt_sync(micco_ts_input_dev);<br /> input_sync(micco_ts_input_dev);<br /> pen_up_already_reported = 0;<br />}<br />else<br /> {<br /> if((pen_state != TSI_PEN_UP) && !pen_up_already_reported) {<br /> input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 0);<br /> input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR, 0);<br /> input_mt_sync(micco_ts_input_dev);<br /> input_sync(micco_ts_input_dev);<br /> pen_up_already_reported = 1;<br /> }<br /> pen_state = TSI_PEN_UP;<br />}<br />mutex_unlock(&mutex);<br />} 

 

五.單點觸摸&多點觸摸

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

  要實現多點觸摸,必要要有核心的支援,也就是說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

   

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.