轉載android key 和觸摸屏的文章 按鍵觸摸屏流程分析:
WindowManagerService類的構造函數
WindowManagerService()
mQueue = new KeyQ();
因為 WindowManagerService.java (frameworks/base/services/java/com/android/server)中有:
private class KeyQ extends KeyInputQueue
KeyQ 是抽象類 KeyInputQueue 的實作,是以 new KeyQ類的時候實際上在 KeyInputQueue 類中建立了
一個線程 InputDeviceReader 專門用來沖裝置讀取按鍵事件,代碼:
Thread mThread = new Thread("InputDeviceReader") {
public void run()
{
在循環中調用:readEvent(ev);
...
send = preprocessEvent(di, ev);
實際調用的是 KeyQ 類的 preprocessEvent 函數
...
int keycode = rotateKeyCodeLocked(ev.keycode);
int[] map = mKeyRotationMap;
for (int i=0; i<N; i+=2)
{
if (map[i] == keyCode)
return map[i+1];
} //
addLocked(di, curTime, ev.flags,RawInputEvent.CLASS_KEYBOARD,newKeyEvent(di, di.mDownTime, curTime, down,keycode, 0, scancode,...));
QueuedEvent ev = obtainLocked(device, when, flags, classType, event);
}
}
readEvent() 實際上調用的是 com_android_server_KeyInputQueue.cpp (frameworks/base/services/jni)中的:
static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,jobject event)
bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,&flags, &value, &when);
調用的是 EventHub.cpp (frameworks/base/libs/ui)中的:
bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen)
在函數中調用了讀裝置操作:res = read(mFDs[i].fd, &iev, sizeof(iev));
在構造函數 WindowManagerService()調用 new KeyQ() 以後接着調用了:
mInputThread = new InputDispatcherThread();
...
mInputThread.start();
來啟動一個線程 InputDispatcherThread
run()
process();
QueuedEvent ev = mQueue.getEvent(...)
因為WindowManagerService類中: final KeyQ mQueue;
是以實際上 InputDispatcherThread 線程實際上從 KeyQ 的事件隊列中讀取按鍵事件。
switch (ev.classType)
case RawInputEvent.CLASS_KEYBOARD:
...
dispatchKey((KeyEvent)ev.event, 0, 0);
mQueue.recycleEvent(ev);
break;
case RawInputEvent.CLASS_TOUCHSCREEN:
//Log.i(TAG, "Read next event " + ev);
dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
break;
setRotationUnchecked
littonton 觸摸螢幕驅動修改記錄
修改
核心代碼/drivers/input/touchscreen/wm9713_touch.c
static int codec_ts_evt_add(codec_ts_t *ts, u16 pressure, u16 x, u16 y)
{
...
input_report_abs(ts->idev, ABS_PRESSURE, pressure & 0xfff);
//針對android加上的
input_report_key(ts->idev, BTN_TOUCH,1);
return 0;
}
static void codec_ts_evt_release(codec_ts_t *ts)
{
input_report_abs(ts->idev, ABS_PRESSURE, 0);
//針對android加上的
input_report_abs(ts->idev,BTN_TOUCH,0);
wm9713_event_ack(EVENT_TYPE_PDN);
}
static int ts_init(struct platform_device *pdev)
{
...
//針對android加上的
__set_bit(EV_KEY, codec_ts_input->evbit);
__set_bit(BTN_TOUCH, codec_ts_input->keybit);
__set_bit(EV_SYN,codec_ts_input->evbit);
//針對android加上的 end
...
}
input_report_key() 報按鍵
input_report_abs() 報絕對坐标 函數分析:
Input.h (kernel/include/linux):
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
input_event(dev, EV_ABS, code, value)
調用的是 Input.c (kernel/drivers/input)中的函數
void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
unsigned long flags;
檢測是否為支援的事件
if (is_event_supported(type, dev->evbit, EV_MAX)) {
//鎖中斷
spin_lock_irqsave(&dev->event_lock, flags);
add_input_randomness(type, code, value);
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
is_event_supported() 函數檢測目前事件類型,輸入子系統支援的事件類型在檔案
kernel/include/linux/input.h 中定義:
#define EV_SYN 0x00
#define EV_KEY 0x01
#define EV_REL 0x02
#define EV_ABS 0x03
#define EV_MSC 0x04
#define EV_SW 0x05
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
static void input_handle_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
int disposition = INPUT_IGNORE_EVENT;
...
switch (type) {
case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX)) {
value = input_defuzz_abs_event(value,
dev->abs[code], dev->absfuzz[code]);
if (dev->abs[code] != value) {
dev->abs[code] = value;
disposition = INPUT_PASS_TO_HANDLERS;
}
}
break;
...
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = 0;
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}
函數的code是
ABS_PRESSURE
BTN_TOUCH
static void input_pass_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;
rcu_read_lock();
handle = rcu_dereference(dev->grab);
if (handle)
handle->handler->event(handle, type, code, value);
else
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle,
type, code, value);
rcu_read_unlock();
}
Apm-power.c (kernel/drivers/input): error = input_register_handle(handle);
Evbug.c (kernel/drivers/input): error = input_register_handle(handle);
Evdev.c (kernel/drivers/input): error = input_register_handle(&evdev->handle);
Input.c (kernel/drivers/input): * input_register_handle - register a new input handle
Input.c (kernel/drivers/input):int input_register_handle(struct input_handle *handle)
Input.c (kernel/drivers/input):EXPORT_SYMBOL(input_register_handle);
Input.h (kernel/include/linux):int input_register_handle(struct input_handle *);
Joydev.c (kernel/drivers/input): error = input_register_handle(&joydev->handle);
Keyboard.c (kernel/drivers/char): error = input_register_handle(handle);
Keychord.c (kernel/drivers/input/misc): ret = input_register_handle(handle);
Keyreset.c (kernel/drivers/input): ret = input_register_handle(handle);
Mousedev.c (kernel/drivers/input): error = input_register_handle(&mousedev->handle);
Rfkill-input.c (kernel/net/rfkill): error = input_register_handle(handle);
==================================================================
Littleton 開發闆相關驅動:
Littleton.c (kernel/arch/arm/mach-pxa)
static void __init littleton_init(void)
{
pxa3xx_mfp_config(ARRAY_AND_SIZE(littleton_mfp_cfg));
platform_device_register(&smc91x_device);
littleton_init_lcd();
littleton_init_keypad();
littleton_init_nand();
}
Littleton.c (kernel/arch/arm/mach-pxa)
鍵盤初始化
#if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE)
static unsigned int littleton_matrix_key_map[] = {
KEY(1, 3, KEY_0), KEY(0, 0, KEY_1), KEY(1, 0, KEY_2), KEY(2, 0, KEY_3),
KEY(0, 1, KEY_4), KEY(1, 1, KEY_5), KEY(2, 1, KEY_6), KEY(0, 2, KEY_7),
KEY(1, 2, KEY_8), KEY(2, 2, KEY_9),
KEY(0, 3, KEY_KPASTERISK),
KEY(2, 3, KEY_KPDOT),
KEY(5, 4, KEY_ENTER),
KEY(5, 0, KEY_UP),
KEY(5, 1, KEY_DOWN),
KEY(5, 2, KEY_LEFT),
KEY(5, 3, KEY_RIGHT),
KEY(3, 2, KEY_HOME),
KEY(4, 1, KEY_END),
KEY(3, 3, KEY_BACK),
KEY(4, 0, KEY_SEND),
KEY(4, 2, KEY_VOLUMEUP),
KEY(4, 3, KEY_VOLUMEDOWN),
KEY(3, 0, KEY_F22),
KEY(3, 1, KEY_F23),
};
static struct pxa27x_keypad_platform_data littleton_keypad_info = {
.matrix_key_rows = 6,
.matrix_key_cols = 5,
.matrix_key_map = littleton_matrix_key_map,
.matrix_key_map_size = ARRAY_SIZE(littleton_matrix_key_map),
.enable_rotary0 = 1,
.rotary0_up_key = KEY_UP,
.rotary0_down_key = KEY_DOWN,
.debounce_interval = 30,
};
static void __init littleton_init_keypad(void)
{
pxa_set_keypad_info(&littleton_keypad_info);
}
#else
static inline void littleton_init_keypad(void) {}
#endif
=========================================
大闆子添加了lifeng觸摸屏驅動
linux-2.6.25/drivers/input/touchscreen/bbk_lifeng_ts.c
需要修改的配置檔案
linux-2.6.25/drivers-r8/input/touchscreen/Kconfig
在 if INPUT_TOUCHSCREEN 後面加上下面内容:
config TOUCHSCREEN_LIFENG
tristate "LIFENG based touchscreen"
depends on INPUT_EVDEV
default y
help
Say Y here if you want to enable lifeng touch driver
修改 linux-2.6.25/drivers-r8/input/touchscreen/Makefine 檔案,添加:
obj-$(CONFIG_TOUCHSCREEN_LIFENG) += bbk_lifeng_ts.o
讓起編譯 linux-2.6.25/drivers/input/touchscreen/bbk_lifeng_ts.c 檔案
檔案 linux-2.6.25/arch/arm/mach-pxa/littleton.c 中需要修改的地方:
static unsigned int littleton_matrix_key_map[] = {
//KEY(1, 3, KEY_0), KEY(0, 0, KEY_1), KEY(1, 0, KEY_2), KEY(2, 0, KEY_3),
//KEY(0, 1, KEY_4), KEY(1, 1, KEY_5), KEY(2, 1, KEY_6), KEY(0, 2, KEY_7),
//KEY(1, 2, KEY_8), KEY(2, 2, KEY_9),
KEY(0, 2, KEY_1), KEY(0, 3, KEY_2), KEY(0, 4, KEY_3), KEY(1, 2, KEY_4),
KEY(1, 3, KEY_5), KEY(1, 4, KEY_6), KEY(2, 2, KEY_7), KEY(2, 3, KEY_8),
KEY(2, 4, KEY_9), KEY(0, 1, KEY_0),
KEY(0, 0, KEY_MENU),
//KEY(0, 3, KEY_KPASTERISK),
//KEY(2, 3, KEY_KPDOT),
KEY(1, 0, KEY_KPASTERISK),
KEY(1, 1, KEY_KPDOT),
}
static struct pxa27x_keypad_platform_data littleton_keypad_info = {
.matrix_key_rows = 5,
.matrix_key_cols = 4,
...
};
//添加
static struct platform_device lifeng_ts_device = {
.name = "lifeng-ts",
.id = -1,
};
static struct platform_device *devices[] __initdata = {
&smc91x_device,
&lifeng_ts_device,//添加
&micco_ts_device,
&micco_bl_device,
&micco_kp_bl_device,
&pxa3xx_device_imm,
&micco_charger_device,
};
========================================
一篇介紹linux輸入子系統非常好的文章
linux輸入子系統
http://hi.baidu.com/licett/blog/item/bf0102f934ebcd5c252df2b3.html
KeyEvent newKeyEvent(InputDevice device, long downTime,long eventTime, boolean down, int keycode, int repeatCount,int scancode, int flags)
mThread.start();
Thread mThread = new Thread("InputDeviceReader")
readEvent(ev);
readEvent
android_server_KeyInputQueue_readEvent
hub->getEvent(&deviceId, &type, &scancode, &keycode,
ViewRoot()
mWindow = new W(this, context);
static class W extends IWindow.Stub
public void dispatchKey(KeyEvent event)
public void dispatchKey(KeyEvent event)
Message msg = obtainMessage(DISPATCH_KEY);
sendMessageAtTime(msg, event.getEventTime());
ViewRoot.java (frameworks/base/core/java/android/view)
public void handleMessage(Message msg)
case DISPATCH_KEY:
deliverKeyEvent((KeyEvent)msg.obj, true);
如果是輸入框
if (mLastWasImTarget)
{
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null && mView != null) {
int seq = enqueuePendingEvent(event, sendDone);
if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME:
AC 97 AUDIO + TOUCHPANEL CODEC
WM9713 - AC 97 AUDIO TOUCHPANEL CODEC - Wolfson Microelectronics plc
WM9713,它的主要功能其實就是A/D轉換和D/A轉換,播放時把數字信号轉換成模拟信号,錄音時把模拟信号轉換成數字信号。因為觸摸屏也用到了A/D轉換,為了重用這個功能,是以WM9713內建了音頻處理和觸摸屏處理兩部分功能。
WM9713支援AC97标準,通過64個寄存器對它進行控制,這個在WM9713的datasheet裡有詳細的描述。
WM9713與PXA300之間通過同步串号SSP通信。PXA300在硬體上對AC97有支援,它提供了FIFO,是以音頻資料可以通過DMA方式讀寫。
它旁邊是WM9713音頻晶片,主要功能是A/D轉換和D/A轉換,播放時把數字信号轉換成模拟信号,錄音時把模拟信号轉換成數字信号。因為觸摸屏也用到了A/D轉換,為了重用這個功能,是以WM9713內建了音頻處理和觸摸屏處理兩部分功能;
三星智能機i900 音頻和觸摸使用的是 wolfson WM9713
9713觸摸問題
我現在用9713采樣觸摸屏的點,用的是polling的模式,一次采樣5個點!但是發現如果觸摸筆不離開屏(不停的采樣),會降低系統的性能!是以想改用 DMA的模式(Continuous mode),嘗試了幾次沒有成功(點選第一次之後就沒反應了),請問有沒有使用過9713 DMA模式采樣的兄弟們,給點提示!謝謝!環境是:wince6,pxa310, wm9713和4-wire觸摸屏。bsp是Mavell的.
文檔說,dma模式可以節約cpu幹預而損失的時間,比polling方式少大概一半時間(我的實驗結果),其他都是一樣的
Audio:1個AC97,采用wolf公司的WM9713,1個II2S,采用wolf公司的WM8580A;
WM8580 介紹
http://www.wolfsonmicro.cn/products/WM8580/
WM8580是一款帶有S/PDIF收發器的多路音頻編碼解碼器(CODEC),特别适用于DVD以及需要有環繞立體聲處理的家用高保真音響、汽車和其它視聽裝置等應用。
該器件內建了一個内置的立體聲24位多比特模數轉換器(ADC),支援16~32位字長的數字音頻輸出及8kHz~192kHz的采樣率。
此外,該器件還包含三個立體聲24位多比特數模轉換器,每個都帶有各自的超采樣數字内插濾波器,并支援16~32位字長的數字音頻輸入及8kHz~192kHz的采樣率。每路DAC通道都有獨立的數字音量和靜音控制。
兩個獨立的音頻資料接口支援I2S、左對齊、右對齊及DSP數字音頻格式。每個音頻接口都可以工作在主時鐘模式或從時鐘模式。
S/PDIF收發器可與IEC-60958-3相容,支援32k/s~192k/s的幀頻。它帶有四個多路複用輸入端及一個輸出端。它還有内置的狀态和錯誤監控功能,其結果可以通過串行接口或者GPO接腳進行傳輸。同時,還提供S/PDIF通道塊配置功能。
該器件帶有兩個鎖相環(PLL),可以對它們進行單獨配置,以生成兩個系統時鐘作為内部或外部使用。
對該器件的控制和設定是通過一個兩線制或三線制(可與SPI相容)的串行接口來實作的。串行接口為所有功能的應用提供了管道,這些功能包括通道選擇、音量控制、靜音、去/加重、S/PDIF控制/狀态,以及電源管理工具。另外,該器件的硬體管理模式還可以通過選擇管腳來激活或禁止器件的功能。
WM8580是采用48引腳TQFP封裝,現可供貨。
WM9713
http://www.wolfsonmicro.cn/products/WM9713
帶有觸摸屏控制器的聲音和音頻編碼解碼器
WM9713L是一款高度內建的輸入/輸出器件,專為移動計算和通信而設計。
該晶片采用了雙編解碼器運作的架構,通過AC連接配接接口支援高保真(Hi-Fi)立體聲編解碼器功能,同時還通過一個PCM型同步串行端口(SSP)額外支援聲音編碼解碼器功能。該晶片還提供了一個第三輔助數字模拟轉換器,用于采用與主編碼解碼器不同的采樣率,支援産生監控音調(supervisory tones)或鈴聲等。
該器件能夠直接連接配接到一個4線或5線觸摸屏、單聲道或立體聲麥克風、立體聲耳機以及立體聲揚聲器,進而降低了系統元器件總數。與耳機、揚聲器以及聽筒的無電容連接配接,可節省成本和印刷電路闆面積。另外,還提供了多個模拟輸入和輸出引腳,與無線通訊裝置模拟連接配接無縫內建在一起。
通過一個符合AC-97标準的單獨的AC-Link接口,可以連接配接和控制所有的晶片功能。可以直接輸入24.576MHz的主時鐘,或者由闆上的鎖相環從一個13MHz (或其他頻率)時鐘從内部産生,該鎖相環支援從2.048Mhz 到78.6Mhz的大範圍輸入時鐘。
WM9713L運作的電源電壓範圍為1.8V-3.6V;晶片上的任何部分都可以通過軟體控制實作關斷來降低功耗。該器件采用了小型的無引線7×7mm封裝,是掌上和便攜系統應用的理想方案。
音頻處理介紹(Linux手機) 李先靜
http://blog.csdn.net/absurd/archive/2007/11/07/1872258.aspx
淺析linux下鍵盤裝置工作和注冊流程[轉]
http://qgjie456.blog.163.com/blog/static/35451367200810361449524/
linux裝置驅動之控制台驅動
http://blog.chinaunix.net/u2/79779/showart_1211518.html
========================
KeyInputQueue.java (frameworks/base/services/java/com/android/server):
的線程 Thread mThread = new Thread("InputDeviceReader") 本地調用:
readEvent(ev);讀取按鍵。readEvent 調用的是檔案:
com_android_server_KeyInputQueue.cpp (frameworks/base/services/jni)中的函數:
static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,
jobject event)
android_server_KeyInputQueue_readEvent中有:
hub = new EventHub;
bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,
&flags, &value, &when);
hub->getEvent 調用的是
EventHub.cpp (frameworks/base/libs/ui) 檔案中的函數:
bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen)
讀取按鍵。
class RefBase::weakref_impl : public RefBase::weakref_type
在系統啟動後,android 會通過
static const char *device_path = "/dev/input";
bool EventHub::openPlatformInput(void)
res = scan_dir(device_path);
通過下面的函數打開裝置。
int EventHub::open_device(const char *deviceName)
{
...
fd = open(deviceName, O_RDWR);
...
mFDs[mFDCount].fd = fd;
mFDs[mFDCount].events = POLLIN;
...
ioctl(mFDs[mFDCount].fd, EVIOCGNAME(sizeof(devname)-1), devname);
...
const char* root = getenv("ANDROID_ROOT");
snprintf(keylayoutFilename, sizeof(keylayoutFilename),
"%s/usr/keylayout/%s.kl", root, tmpfn);
...
device->layoutMap->load(keylayoutFilename);
...
}
打開裝置的時候,如果 device->classes&CLASS_KEYBOARD 不等于 0 表明是鍵盤。
常用輸入裝置的定義有:
enum {
CLASS_KEYBOARD = 0x00000001, //鍵盤
CLASS_ALPHAKEY = 0x00000002, //
CLASS_TOUCHSCREEN = 0x00000004, //觸摸屏
CLASS_TRACKBALL = 0x00000008 //軌迹球
};
打開鍵盤裝置的時候通過上面的 ioctl 獲得裝置名稱,指令字 EVIOCGNAME 的定義在檔案:
kernel/include/linux/input.h 中。
#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len)
在核心鍵盤驅動檔案 drivers/input/keyboard/pxa27x_keypad.c 中定義了裝置名稱:pxa27x-keypad
static struct platform_driver pxa27x_keypad_driver = {
.probe = pxa27x_keypad_probe,
.remove = __devexit_p(pxa27x_keypad_remove),
.suspend = pxa27x_keypad_suspend,
.resume = pxa27x_keypad_resume,
.driver = {
.name = "pxa27x-keypad",
.owner = THIS_MODULE,
},
};
ANDROID_ROOT 為環境變量,在android的指令模式下通過 printenv 可以知道它為: system
是以 keylayoutFilename 為:/system/usr/keylayout/pxa27x-keypad.kl
pxa27x-keypad.kl 定義了按鍵映射,具體内容如下:
----------------------
# NUMERIC KEYS 3x4
key 2 1
key 3 2
key 4 3
key 5 4
key 6 5
key 7 6
key 8 7
key 9 8
key 10 9
key 11 0
key 83 POUND
key 55 STAR
# FUNCTIONAL KEYS
key 231 MENU WAKE_DROPPED
key 192 BACK WAKE_DROPPED
key 193 HOME WAKE
key 107 DEL WAKE
key 102 CALL WAKE_DROPPED
key 158 ENDCALL WAKE_DROPPED
key 28 DPAD_CENTER WAKE
key 115 VOLUME_UP
key 114 VOLUME_DOWN
----------------------
如果沒有定義鍵盤映射檔案,那麼預設使用系統的 /system/usr/keylayout/qwerty.kl
可以修改 /system/usr/keylayout/qwerty.kl 檔案改變Android公司的按鍵映射。
device->layoutMap->load(keylayoutFilename) 調用的是檔案:
KeyLayoutMap.cpp (frameworks/base/libs/ui)中的函數:
status_t KeyLayoutMap::load(const char* filename)通過解析 pxa27x-keypad.kl
把按鍵的映射關系儲存在 :KeyedVector<int32_t,Key> m_keys; 中。
當獲得按鍵事件以後調用:
status_t KeyLayoutMap::map(int32_t scancode, int32_t *keycode, uint32_t *flags)
由映射關系 KeyedVector<int32_t,Key> m_keys 把掃描碼轉換成andorid上層可以識别的按鍵。