1.HALL开关原理及手机应用
手机中HALL传感器由一个开关型HALL元件和两个电源开关控制管组成。其导通与否完全受到手机CPU输
出的HALL高电平信号控制,电源则来自于电池。当翻盖合上时装在翻盖中的磁铁的磁场作用于HALL传感器
(一般翻盖/折叠手机都把磁铁安装在翻盖上),HALL电路中的三极管导通,从传感器的引脚输出低电平,如果
是在通话后则作为“挂机”信号送给CPU挂机。(这也就是为什么合上翻盖后手机就挂断的道理)。
2.硬件连接
#define GN_HALL_KEY_OPEN 111
#define GN_HALL_KEY_CLOSE 112 -------给系统上报的键值
#define GN_GPIO_HALL_EINT_PIN 40
u8 hall_key_state_bak;
spinlock_t hall_lock;
struct hall_irq_info
{
int irq;
struct work_struct work;
};
struct hall_irq_info hall_info;
enum hall_state
{
HALL_CLOSE = 0,
HALL_OPEN =1
};
INIT_WORK(&(hall_info.work), work_func);
---------------初始化工作队列
err = gpio_request(GN_GPIO_HALL_EINT_PIN, "Hall Key IRQ GPIO");
if (err) {
printk(KERN_ERR "%s: Failed to request GPIO %d\n",
__func__, GN_GPIO_HALL_EINT_PIN);
goto err_request_gpio_failed;
}
gpio_tlmm_config(GPIO_CFG(GN_GPIO_HALL_EINT_PIN, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_6MA),GPIO_CFG_ENABLE);
gpio_free(GN_GPIO_HALL_EINT_PIN);
hall_info.irq= gpio_to_irq(GN_GPIO_HALL_EINT_PIN);
if (__gpio_get_value(GN_GPIO_HALL_EINT_PIN) == 0) {
pr_info("HALL_KEY: init the request irq for RISING\n");
hall_key_state_bak = 0;
switch_set_state((struct switch_dev *)&hall_data, HALL_CLOSE);
err = request_irq(hall_info.irq, hall_irq, IRQF_TRIGGER_HIGH, "Hall_Key", NULL);
} else {
pr_info("HALL_KEY: init the request irq for FALLING\n");
hall_key_state_bak = 1;
switch_set_state((struct switch_dev *)&hall_data, HALL_OPEN);
err = request_irq(hall_info.irq, hall_irq, IRQF_TRIGGER_LOW, "Hall_Key", NULL);
}
enable_irq_wake(hall_info.irq);
if (err < 0) {
pr_err("HALL_KEY: enable_irq_wake failed...\n");
goto err_request_irq_failed;
}
-------------中断的配置
static void work_func(struct work_struct *work)
{
-
-
if (kpd_hallkey_state==1) { //hall open
input_report_key(hall_input_dev, GN_HALL_KEY_OPEN, 1);
input_sync(hall_input_dev);
input_report_key(hall_input_dev, GN_HALL_KEY_OPEN, 0);
input_sync(hall_input_dev);
switch_set_state((struct switch_dev *)&hall_data, HALL_OPEN);
} else {//hall close
input_report_key(hall_input_dev, GN_HALL_KEY_CLOSE, 1);
input_sync(hall_input_dev);
input_report_key(hall_input_dev, GN_HALL_KEY_CLOSE, 0);
input_sync(hall_input_dev);
switch_set_state((struct switch_dev *)&hall_data, HALL_CLOSE);
}
-
-
}
static irqreturn_t hall_irq(int irq, void *dev_id)
{
disable_irq_nosync(hall_info.irq);
schedule_work(&hall_info.work);
return IRQ_HANDLED;
}
4.按键唤醒系统
要实现按键唤醒系统,首先,在驱动中,设置此键会被上报。如:
__set_bit(KEY_2, kpd_input_dev->keybit);
__set_bit(KEY_3, kpd_input_dev->keybit);
这样,才能确保按键被注册处理,否则,上层收不到按键事件。
另外,在7x27a_kp.kl文件中,设置devices\qcom\msm7627a_sku3\7x27a_kpd.kl
key 111 GN_HALL_KEY_OPEN WAKE
key 112 GN_HALL_KEY_CLOSE
这样就可实现按键唤醒系统了。
5.新增两个按键实现hall key
1. 首先修改底层gpio_event.c文件中的按键定义:
#define GN_HALL_KEY_OPEN 111
#define GN_HALL_KEY_CLOSE 112
2. 修改devices\qcom\msm7627a_sku3\7x27a_kpd.kl文件,增加
key 111 GN_HALL_KEY_OPEN WAKE (打开可以唤醒)
key 112 GN_HALL_KEY_CLOSE
3. 在frameworks\base\include\ui\keycodelabels.h KEYCODES结构体中增加
{ "GN_HALL_KEY_OPEN", 111 },
{ "GN_HALL_KEY_CLOSE", 112 },
4. 在frameworks\base\core\java\android\view\keyevent.java构造函数中增加:
public static final int KEYCODE_GN_HALL_KEY_OPEN = 111;
public static final int KEYCODE_ GN_HALL_KEY_CLOSE = 112;
5. 修改: private static final int LAST_KEYCODE = KEYCODE_BUTTON_MODE;
为private static final int LAST_KEYCODE =KEYCODE_ GN_HALL_KEY_CLOSE;
在frameworks\base\native\include\android\keycodes.h的enum中增加:
AKEYCODE_GN_HALL_KEY_CLOSE = 250,
AKEYCODE_GN_HALL_KEY_CLOSE = 251,
6. 在frameworks\base\libs\ui\input.cpp的issystemkey中增加:
case AKEYCODE_GN_HALL_KEY_CLOSE:
case AKEYCODE_GN_HALL_KEY_CLOSE:
这样bool KeyEvent::isSystemKey() const {
return isSystemKey(getKeyCode());
才能由scancode转换为keycode供上层应用截取。
7. 修改development/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java
private static final String[] KEY_NAMES = {
…
…
"KEYCODE_GN_HALL_KEY_OPEN",
"KEYCODE_GN_HALL_KEY_CLOSE",
"TAG_LAST_KEYCODE"
}
6.展讯平台【SC6820 ANDROID2.3.5】驱动实现部分
在sc8800g-keys.c文件中,包括了GPIO KEY, 中断KEY的实现机制,包括POWER KEY的实现。
1. pinma_cfg.c
MFP_CFG_X(RFCTL5, AF3,DS1,
F_PULL_UP,S_PULL_UP,
IO_IE) ----配置GPIO为中断模式
2. key_cfg.c
int sprd_3rdparty_gpio_key1 = 95;
/*note: now driver support max 3 gpio keys*/ ----- SC6820 最多支持3个GPIO
static struct sprd_gpio_keys sprd_gpio_keymap[] =
{
{&sprd_3rdparty_gpio_key1,ANDROID_KEY_HALL}, ----- 配置GPIO_NUM, KEY_VALUE
};
3. 关键部分修改:sc8800g-keys.c
用展讯默认代码,会出现一直有eic中断,导致系统无法睡眠的问题,因此,需要对系统gpio按键机制进行修改
1>修改函数 gpio_key_init
屏蔽原来代码,添加如下代码
INIT_WORK(&hallwork, hall_work);
err = gpio_request(gpio, "hall key");
if (err)
printk("can not alloc gpio for %d Key\n", gpio);
else
printk("alloc gpio for %d Key\n", gpio);
gpio_direction_input(gpio);
hallirq = sprd_alloc_gpio_irq(gpio);
ret = request_threaded_irq(hallirq, NULL, hall_interrupt, IRQF_TRIGGER_LOW| IRQF_ONESHOT, NULL, (void*)gpio_id);
2>实现中断处理函数
static irqreturn_t hall_interrupt(int irq, void *dev_id)
{
int irq_type,gpio,ret=0;
printk("%s\n", __func__);
disable_irq_nosync(irq);
gpio = irq_to_gpio(irq);
ret = gpio_get_value(gpio);
schedule_work(&hallwork);
printk("hall interrupt %d\n",ret);
if(ret)
{
irq_type = IRQF_TRIGGER_LOW;
mHallStatus = 1;
printk("hall interrupt ret:%d,irq_type:%d\n",ret,irq_type);
set_irq_type(irq,irq_type);
}
else
{
irq_type = IRQF_TRIGGER_HIGH;
mHallStatus = 0;
printk("hall interrupt ret:%d,irq_type:%d\n",ret,irq_type);
set_irq_type(irq,irq_type);
}
return IRQ_HANDLED;
}
3>中断消息队列处理
static void hall_work(struct work_struct *work)
{
int ret = 0;
int gpio;
gpio = irq_to_gpio(hallirq);
ret = gpio_get_value(gpio);
printk("%s : %d : %d \n",__func__,ANDROID_KEY_HALL, ret);
if(ret)
{
//release
input_report_key(sprd_kpad->input, ANDROID_KEY_HALL, 0); --- 上报键值,事件
input_sync(sprd_kpad->input);
}
else
{
//press
input_report_key(sprd_kpad->input, ANDROID_KEY_HALL, 1);
input_sync(sprd_kpad->input);
}
enable_irq(hallirq);
}
到此为止,GPIO按键驱动部分已经OK,上层就可以根据上报的键值进行处理了。如何定义一个新增加的键值,看第一部分,有介绍。最重要的就是
第一部分中的第5点LAST_KEYCODE,这个很关键,需要修改为你新添加的键值,不然,上层的检查会有问题。
GPIO按键作为系统的唤醒、睡眠处理部分
1.按键WAKE部分定义在 sprd-keypad.kl
key 216 HALL WAKE
2.关键部分修改:interceptKeyBeforeQueueing
case KeyEvent.KEYCODE_HALL:
{
result &= ~ACTION_PASS_TO_USER;
if (down)
{
Log.d(TAG, "runyee hall down: screenIsOn=" + isScreenOn);
if(isScreenOn)
{
Log.d(TAG, "runyee hall down: goToSleep" );
pm.goToSleep(SystemClock.uptimeMillis());
}
}
else
{
Log.d(TAG, "runyee hall up: screenIsOn=" + isScreenOn);
//mWL.acquire();
if(!isScreenOn)
{
Log.d(TAG, "runyee hall up: wakeUp" );
mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode);
}
}
}
break;
对应键值的处理,上面就是睡眠和唤醒的关键部分。细节部分就是上面的条件判断,会对应屏的亮灭进行控制。同样,因为我们定义的按键
带WAKE属性,因此,在这个函数中还有个细节需要进行处理:
if (down && isWakeKey) {
if (keyguardActive) {
// If the keyguard is showing, let it decide what to do with the wake key.
if(111 != keyCode) //------这就起到屏蔽作用,各位可以先不加此条件判断,看看效果。
{
Log.d(TAG, "runyee hall down: wakekey" );
mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode);
}
} else {
// Otherwise, wake the device ourselves.
result |= ACTION_POKE_USER_ACTIVITY;
}
}
到此,按键对系统的唤醒,睡眠实现完成。如果还要根据这个GPIO状态做进一步的开发的话,还需要修改sc8800g-key.c,注册设备文件节点,
通过JNI获取状态,返回给相应的服务,做进一步处理。