linux輸入子系統(linux input subsystem)從上到下由三層實現,分別為:輸入子系統事件處理層(EventHandler)、輸入子系統核心層(InputCore)和輸入子系統裝置驅動層。
linux輸入子系統的事件處理機制Linux輸入子系統支援的資料型別:
EV_SYN 0x00 同步事件
EV_KEY 0x01 按鍵事件
EV_REL 0x02 相對座標(如:滑鼠移動,報告相對最後一次位置的偏移)
EV_ABS 0x03 絕對座標(如:觸控式螢幕或操作杆,報告絕對的座標位置)
EV_MSC 0x04 其它
EV_SW 0x05 開關
EV_LED 0x11 按鍵/裝置燈
EV_SND 0x12 聲音/警報
EV_REP 0x14 重複
EV_FF 0x15 力反饋
EV_PWR 0x16 電源
EV_FF_STATUS 0x17 力反饋狀態
EV_MAX 0x1f 事件型別最大個數和提供位掩碼支援
注:參考核心:/include/linux/input.h
程式設計思路總體程式設計思路驅動程式//touch.c#include <linux/errno.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/input.h>#include <linux/init.h>#include <linux/serio.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/clk.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/plat-s3c24xx/ts.h>#include <asm/arch/regs-adc.h>#include <asm/arch/regs-gpio.h>#define Stylus_up_state (1<<15)#define Start_up_adc (1<<0) #define Wait_down_mode 0xd3#define Wait_up_mode 0x1d3struct s3c_ts_regs { unsigned long adccon; unsigned long adctsc; unsigned long adcdly; unsigned long adcdat0; unsigned long adcdat1; unsigned long adcupdn;};//static struct input_dev * mytouch_dev;static struct input_handle mytouch_handle;static struct timer_list ts_timer;static volatile struct s3c_ts_regs *s3c_ts_regs;//函式宣告static void s3c_ts_timer_function(unsigned long data);static void enter_wait_down_mode(void);static void enter_wait_up_mode(void);static void configurate_event(void);static void configurate_hardware(void);static int s3c_filter_ts(int *x, int *y);static void enter_measure_xy_mode(void){ s3c_ts_regs->adctsc = 0x0c;}static void start_adc(void){ s3c_ts_regs->adccon |= Start_up_adc;}static irqreturn_t pen_down_up_irq(int irq, void *dev_id){ if(s3c_ts_regs->adcdat0 & Stylus_up_state )//up { printk("touch up"); enter_wait_down_mode(); } else//down { printk("touch down"); enter_measure_xy_mode(); start_adc(); } return IRQ_HANDLED;}static irqreturn_t adc_irq(int irq, void *dev_id){ static int cnt = 0; static int x[4], y[4]; int adcdat0, adcdat1; adcdat0 = s3c_ts_regs->adcdat0; adcdat1 = s3c_ts_regs->adcdat1; if (s3c_ts_regs->adcdat0 & Stylus_up_state) { cnt = 0; input_report_abs(mytouch_handle.dev, ABS_PRESSURE, 0); input_report_key(mytouch_handle.dev, BTN_TOUCH, 0); input_sync(mytouch_handle.dev); enter_wait_down_mode(); } else { x[cnt] = adcdat0 & 0x3ff; y[cnt] = adcdat1 & 0x3ff; ++cnt; if (cnt == 4) { if (s3c_filter_ts(x, y)) { printk("x = %d, y = %d\n", (x[0]+x[1]+x[2]+x[3])/4, (y[0]+y[1]+y[2]+y[3])/4); input_report_abs(mytouch_handle.dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4); input_report_abs(mytouch_handle.dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4); input_report_abs(mytouch_handle.dev, ABS_PRESSURE, 1); input_report_key(mytouch_handle.dev, BTN_TOUCH, 1); input_sync(mytouch_handle.dev); } cnt = 0; enter_wait_up_mode(); mod_timer(&ts_timer, jiffies + HZ/100); } else { enter_measure_xy_mode(); start_adc(); } } return IRQ_HANDLED;}static int s3c_filter_ts(int x[], int y[])//filter{#define ERR_LIMIT 5 int avr_x, avr_y; int det_x, det_y; avr_x = (x[0] + x[1])/2; avr_y = (y[0] + y[1])/2; det_x = (x[2] > avr_x) ? (x[2] - avr_x) : (avr_x - x[2]); det_y = (y[2] > avr_y) ? (y[2] - avr_y) : (avr_y - y[2]); if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT)) return 0; avr_x = (x[1] + x[2])/2; avr_y = (y[1] + y[2])/2; det_x = (x[3] > avr_x) ? (x[3] - avr_x) : (avr_x - x[3]); det_y = (y[3] > avr_y) ? (y[3] - avr_y) : (avr_y - y[3]); if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT)) return 0; return 1;}static int mytouch_init(void){ mytouch_handle.dev = input_allocate_device(); configurate_event(); input_register_device(mytouch_handle.dev); configurate_hardware(); return 0;}static void mytouch_exit(void){ free_irq(IRQ_TC, NULL); free_irq(IRQ_ADC, NULL); iounmap(s3c_ts_regs); input_free_device(mytouch_handle.dev); input_unregister_device(mytouch_handle.dev); del_timer(&ts_timer);}static void configurate_event(void){ set_bit(EV_KEY,mytouch_handle.dev->evbit); set_bit(EV_ABS,mytouch_handle.dev->evbit); set_bit(BTN_TOUCH,mytouch_handle.dev->keybit); input_set_abs_params(mytouch_handle.dev, ABS_X, 0, 0X3FF,0,0); input_set_abs_params(mytouch_handle.dev, ABS_Y, 0, 0X3FF,0,0); input_set_abs_params(mytouch_handle.dev, ABS_PRESSURE, 0, 1,0,0);}static void configurate_hardware(void){ struct clk *adc_clk; //開啟時鐘 adc_clk = clk_get(NULL, "adc_clk"); clk_enable(adc_clk); //io重對映 s3c_ts_regs = ioremap(0x58000000, sizeof(struct s3c_ts_regs)); //ADC配置 //ADCCLK=PCLK/(49+1)=50MHz/(49+1)=1MHz s3c_ts_regs->adccon = (1<<14)|(49<<6); s3c_ts_regs->adcdly = 0xffff; //註冊中斷 request_irq(IRQ_TC, pen_down_up_irq, IRQF_SAMPLE_RANDOM, "ts_pen", NULL); request_irq(IRQ_ADC, adc_irq, IRQF_SAMPLE_RANDOM, "adc_clk", NULL); //註冊定時器 init_timer(&ts_timer); ts_timer.function = s3c_ts_timer_function; add_timer(&ts_timer); //進入等待按下模式 enter_wait_down_mode(); }static void s3c_ts_timer_function(unsigned long data){ if (s3c_ts_regs->adcdat0 & Stylus_up_state) { input_report_abs(mytouch_handle.dev, ABS_PRESSURE, 0); input_report_key(mytouch_handle.dev, BTN_TOUCH, 0); input_sync(mytouch_handle.dev); enter_wait_down_mode(); } else { enter_measure_xy_mode(); start_adc(); }}static void enter_wait_down_mode(void){ s3c_ts_regs->adctsc = Wait_down_mode;}static void enter_wait_up_mode(void){ s3c_ts_regs->adctsc = Wait_up_mode; }module_init(mytouch_init);module_exit(mytouch_exit);MODULE_LICENSE("GPL");
Makefile
#compile regularKERN_DIR = /work/system/linux-2.6.22.6all: make -C $(KERN_DIR) M=`pwd` modules rm -rf modules.order Module.symvers .PHONY:clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order Module.symversobj-m += touch.o
測試方法:
1. make menuconfig 去掉原來的觸控式螢幕驅動程式
-> Device Drivers -> Input device support -> Generic input layer -> Touchscreens <> S3C2410/S3C2440 touchscreens2,make uImage 使用新核心啟動
3. insmod touch.ko
4,按下/鬆開觸控筆
最新評論