关机动作从按键触发中断,linux kernel层给android framework层返回按键事件进入 framework层,
再从 framework层到kernel层执行kernel层关机任务。长按键对应的handler代码:
frameworks\policies\base\phone\com\android\internal\policy\impl\phonewindowmanager.java
Runnable mPowerLongPress; private final Runnable mPowerLongPress = new Runnable() { public void run() { if (!mPowerKeyHandled) { mPowerKeyHandled = true; performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); showGlobalActionsDialog(); } } };
选项的对话框。
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned())该函数是
dialog的具体实现:它具体在
frameworks\policies\base\phone\com\android\internal\policy\impl\globalactions.java
其中会调用createDialog()
private AlertDialog createDialog()中
根据平台不同这里有差异,createDialog中方框位置一般会调用 ShutdownThread.reboot(mContext,finalReason,false);//調用ShutdownThread的reboot
函数
我們跟進/frameworks/base/services/java/com/android/server/pm/ShutdownThread.java
我們看到其調用了shutdownInner函數
public static void reboot(final Context context, String reason, boolean confirm) { mReboot = true; mRebootSafeMode = false; mRebootReason = reason; shutdownInner(context, confirm); }
static void shutdownInner(final Context context, boolean confirm) { .... if (confirm) { final CloseDialogReceiver closer = new CloseDialogReceiver(context); final AlertDialog dialog = new AlertDialog.Builder(context) .setTitle(mRebootSafeMode ? com.android.internal.R.string.reboot_safemode_title : com.android.internal.R.string.power_off) .setMessage(resourceId) .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { beginShutdownSequence(context);//調用beginShutdownSequence } }) .setNegativeButton(com.android.internal.R.string.no, null) .create(); closer.dialog = dialog; dialog.setOnDismissListener(closer); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); dialog.show(); } else { beginShutdownSequence(context); } }
beginShutdownSequence如下
private static void beginShutdownSequence(Context context) { ...... sInstance.start();//從這兒就會開始調用到run()方法 }
基本最终会调用
public void run() { BroadcastReceiver br = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // We don't allow apps to cancel this, so ignore the result. actionDone(); } }; ......省略 // Set initial variables and time out time. mActionDone = false; final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME; synchronized (mActionDoneSync) { try { final IMountService mount = IMountService.Stub.asInterface( ServiceManager.checkService("mount")); if (mount != null) { mount.shutdown(observer); } else { Log.w(TAG, "MountService unavailable for shutdown"); } } catch (Exception e) { Log.e(TAG, "Exception during MountService shutdown", e); } while (!mActionDone) { long delay = endShutTime - SystemClock.elapsedRealtime(); if (delay <= 0) { Log.w(TAG, "Shutdown wait timed out"); break; } try { mActionDoneSync.wait(delay); } catch (InterruptedException e) { } } } rebootOrShutdown(mReboot, mRebootReason);//又繞到rebootOrShutdown函數
在rebootOrShutdown函數中會根據reboot參數來決定是重啟還是關機
public static void rebootOrShutdown(boolean reboot, String reason) { if (reboot) { Log.i(TAG, "Rebooting, reason: " + reason); try { PowerManagerService.lowLevelReboot(reason);//我們偉進來的reboot參數為true,所以走這一支 } catch (Exception e) { Log.e(TAG, "Reboot failed, will attempt shutdown instead", e); } } else if (SHUTDOWN_VIBRATE_MS > 0) { // vibrate before shutting down Vibrator vibrator = new SystemVibrator(); try { vibrator.vibrate(SHUTDOWN_VIBRATE_MS); } catch (Exception e) { // Failure to vibrate shouldn't interrupt shutdown. Just log it. Log.w(TAG, "Failed to vibrate during shutdown.", e); } 505 // vibrator is asynchronous so we need to wait to avoid shutting down too soon. 506 try { Thread.sleep(SHUTDOWN_VIBRATE_MS); } catch (InterruptedException unused) { } } // Shutdown power Log.i(TAG, "Performing low-level shutdown..."); PowerManagerService.lowLevelShutdown();//如果我們偉進來的reboot為false那就是關機 } }
從上面的代碼我們又回到了PowerManagerService.
其實就是要開一個線程來在reboot之前把要做的一些工作先做完。
好吧,我們來看一下lowLevelReboot.
public static void lowLevelReboot(String reason) throws IOException { nativeReboot(reason);//哈哈,還是要進入到時JNI }
下/frameworks/base/services/jni/com_android_server_PowerManagerService.cpp中的
natievReboot是怎麼樣的
static void nativeReboot(JNIEnv *env, jobject clazz, jstring reason) { if (reason == NULL) { android_reboot(ANDROID_RB_RESTART, 0, 0);//這兒是直接重啟,因為沒有偉reason, } else { const char *chars = env->GetStringUTFChars(reason, NULL); android_reboot(ANDROID_RB_RESTART2, 0, (char *) chars);//這兒一般是上面偉入了recovery而重啟進入recovery,主要是oat或手機重置 env->ReleaseStringUTFChars(reason, chars); // In case it fails. } jniThrowIOException(env, errno); }
上面的anddroid_reboot在/system/core/libcutils/android_reboot.c中。
接下來我們就看一下android_reboot()又做些什麽。
int android_reboot(int cmd, int flags, char *arg) 105{ 106 int ret; 107 108 if (!(flags & ANDROID_RB_FLAG_NO_SYNC)) 109 sync(); 110 111 if (!(flags & ANDROID_RB_FLAG_NO_REMOUNT_RO)) 112 remount_ro(); 113 114 switch (cmd) { 115 case ANDROID_RB_RESTART://我們一般的重啟就是偉一這個,從上面的代碼有體現 116 ret = reboot(RB_AUTOBOOT); 117 break; 118 119 case ANDROID_RB_POWEROFF://shutdown走的是這兒,其實reboot和shutdown就很相似,從powermanagerservice中才都是call了nativeReboot 120 ret = reboot(RB_POWER_OFF); 121 break; 122 123 case ANDROID_RB_RESTART2://當一開始偉入了recovery時會走這兒 124 ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, 125 LINUX_REBOOT_CMD_RESTART2, arg); 126 break; 127 128 default: 129 ret = -1; 130 } 131 132 return ret; 133} 134
上面的reboot(RB_AUTOBOOT)又會call到/bionic/libc/unistd/reboot.c
int reboot (int mode) { return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL ); //這兒的__reboot和上面的case ANDROID_RB_RESTART2的__reboot就是一樣的 }
__reboot就到了/bionic/libc/arch-arm/syscalls/__reboot.S
這兒是一個內聯彙編的函數。通過swi軟中斷來重啟設備。到此android部份的代碼重啟流程就大致完成。其實我們剛才也注意到shutdown和reboot差不多走一樣的路線下來。
5ENTRY(__reboot) 6 .save {r4, r7} 7 stmfd sp!, {r4, r7} 8 ldr r7, =__NR_reboot 9 swi #0 10 ldmfd sp!, {r4, r7} 11 movs r0, r0 12 bxpl lr 13 b __set_syscall_errno 14END(__reboot)
2. Power.shutdown();
找这个 android.os.Power 模块
frameworks/base/core/java/android/os/power.java 中有调用shutdown()jni接口
frameworks/base/core/jni/android_os_Power.cpp
static void android_os_Power_shutdown(JNIEnv *env, jobject clazz) { sync(); #ifdef HAVE_ANDROID_OS reboot(RB_POWER_OFF); #endif }
#define RB_AUTOBOOT LINUX_REBOOT_CMD_RESTART #define RB_HALT_SYSTEM LINUX_REBOOT_CMD_HALT #define RB_POWER_OFF LINUX_REBOOT_CMD_POWER_OFF
if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off) cmd = LINUX_REBOOT_CMD_HALT; lock_kernel(); switch (cmd) { case LINUX_REBOOT_CMD_RESTART: kernel_restart(NULL); break; case LINUX_REBOOT_CMD_CAD_ON: C_A_D = 1; break; case LINUX_REBOOT_CMD_CAD_OFF: C_A_D = 0; break; case LINUX_REBOOT_CMD_HALT: kernel_halt(); unlock_kernel(); do_exit(0); break; case LINUX_REBOOT_CMD_POWER_OFF: printk("test powe down in %s(%d)\n", __FILE__, __LINE__); kernel_power_off(); unlock_kernel(); do_exit(0); break;
LINUX_REBOOT_CMD_HALT,从而执行kernel_halt()函数。
在该函数中,会执行函数machine_halt()。错误原因就在这儿(pm_power_off为空)。
函数指针pm_power_off是与平台相关的指针,
添加函数
static void lpc32xx_power_off(void) { __raw_writel((0x1<<9),GPIO_P2_OUTP_CLR(GPIO_IOBASE)); }
具体平台都不一样,但一般都是拉低power halt的gpio管脚即可。
用到的函数都在<driver/gpio/gpiolib.c>
static int gpiod_request(struct gpio_desc *desc, const char *label); static void gpiod_free(struct gpio_desc *desc); static int gpiod_direction_input(struct gpio_desc *desc); static int gpiod_direction_output(struct gpio_desc *desc, int value); static int gpiod_get_direction(const struct gpio_desc *desc); static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce); static int gpiod_get_value_cansleep(const struct gpio_desc *desc); static void gpiod_set_value_cansleep(struct gpio_desc *desc, int value); static int gpiod_get_value(const struct gpio_desc *desc); static void gpiod_set_value(struct gpio_desc *desc, int value); static int gpiod_cansleep(const struct gpio_desc *desc); static int gpiod_to_irq(const struct gpio_desc *desc); static int gpiod_export(struct gpio_desc *desc, bool direction_may_change); static int gpiod_export_link(struct device *dev, const char *name, struct gpio_desc *desc); static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value); static void gpiod_unexport(struct gpio_desc *desc);
先
gpiod_request // 一个GPIO引脚
int gpio_direction_output//设置为输出,并初始化值为value.
gpio_set_value //拉低GPIO,下电即可
GPIO操作见参考文章2
在ea3250_board_init中添加
pm_power_off = lpc32xx_power_off;
***************************************************************************************************************************
参考文章1:
Andriod关机&重启分析
1、jni层
frameworks/base/core/jni/android_os_Power.cpp
static void android_os_Power_shutdown(JNIEnv *env, jobject clazz) { sync(); #ifdef HAVE_ANDROID_OS reboot(RB_POWER_OFF); #endif }
static void android_os_Power_reboot(JNIEnv *env, jobject clazz, jstring reason) { sync(); #ifdef HAVE_ANDROID_OS if (reason == NULL) { reboot(RB_AUTOBOOT); } else { const char *chars = env->GetStringUTFChars(reason, NULL); __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, (char*) chars); env->ReleaseStringUTFChars(reason, chars); // In case it fails. } jniThrowIOException(env, errno); #endif }
static JNINativeMethod method_table[] = { …... { "shutdown", "()V", (void*)android_os_Power_shutdown }, { "rebootNative", "(Ljava/lang/String;)V", (void*)android_os_Power_reboot }, };
其中,RB_POWER_OFF及RB_AUTOBOOT定义bionic/libc/include/sys/reboot.h
中。
#define RB_AUTOBOOT LINUX_REBOOT_CMD_RESTART #define RB_HALT_SYSTEM LINUX_REBOOT_CMD_HALT #define RB_POWER_OFF LINUX_REBOOT_CMD_POWER_OFF …...
if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
cmd = LINUX_REBOOT_CMD_HALT;
lock_kernel();
switch (cmd) {
case LINUX_REBOOT_CMD_RESTART:
kernel_restart(NULL);
break;
…...
case LINUX_REBOOT_CMD_HALT:
kernel_halt();
unlock_kernel();
do_exit(0);
break;
case LINUX_REBOOT_CMD_POWER_OFF:
kernel_power_off();
unlock_kernel();
do_exit(0);
break;
其中,函数指针pm_power_off是与平台相关的指针,以imx51平台为例,其定义于kernel_imx/arch/arm/mach-mx5/mx51_baggage.c中。
staticvoidmxc_power_off(void)
{
…...
/*Setthepowergatebitstopowerdown*/
pmic_write_reg(REG_POWER_MISC,(PWGT1SPIEN|PWGT2SPIEN),
(PWGT1SPIEN|PWGT2SPIEN));
}
另外,在staticvoid__initmxc_board_init(void)函数中,有
pm_power_off=mxc_power_off;
另外,在pmic中,也定义了pm_power_off=pmic_power_off;
这是因为imx51支持2中关机方式,具体要看硬件支持。
3、sys_reboot分析
当pm_power_off为空时,由上层传入的命令LINUX_REBOOT_CMD_POWER_OFF会变为LINUX_REBOOT_CMD_HALT,从而执行kernel_halt()函数。在该函数中,会执行函数machine_halt(),其定义在kernel_imx/arch/arm/kernel/process.c中。
voidmachine_halt(void)
{
}
由于该函数为空,因此,当pm_power_off指针为空时,执行关机操作会失败。
当pm_power_off不为空时,上层命令LINUX_REBOOT_CMD_POWER_OFF,执行的函数是kernel_power_off()。在该函数中,会执行函数machine_power_off,其于machine_halt定义于同一文件中。
voidmachine_power_off(void)
{
if(pm_power_off)
pm_power_off();
}
因此,只有当pm_power_off指针不为空,且实现代码符合该cpu体系时,andriod才能实现正常的关机。
4、reboot
reboot较简单,通过命令LINUX_REBOOT_CMD_RESTART执行kernel_restart(NULL)即可。
*****************************************************************************************************************
参考文章2:
----------------------------------------------------------------------------------------------
gpio作为一种通用的IO接口,使用方法主要如下:
----------------------------------------------------------------------------------------------
Gpio:每个 GPIO 都代表一个连接到特定引脚或球栅阵列(BGA)封装中“球珠”的一个位
标准头文件 <linux/gpio.h> [对外接口]
其中根据是否定义CONFIG_GENERIC_GPIO判断系统是否支持gpio
头文件为 <asm/gpio.h>;实现文件为 <driver/gpio/gpiolib.c>
步骤
1. gpio_request(gpio_num, "xx gpio"); 申请GPIO, 返回0为申请成功,否则失败。
2. 设置gpio方向:
int gpio_direction_input(unsigned gpio); //设置为输入
int gpio_direction_output(unsigned gpio, int value); //设置为输出,并初始化值为value.
3. 获取/设置gpio值: int gpio_cansleep(unsigned gpio);
a.不可睡眠:
gpio_get_value(unsigned gpio); //返回value
gpio_set_value(unsigned gpio, int value); //设置值
b.可睡眠:(对于有些挂载在I2C,SPI总线上的扩展GPIO,读写操作可能会导致睡眠,因此不能在中断函数中使用。使用下面的函数以区别于正常的GPIO)
int gpio_get_value_cansleep(unsigned gpio); //输入端口:返回零或非零,可能睡眠
void gpio_set_value_cansleep(unsigned gpio, int value); //输出端口:可能睡眠
4. void gpio_free(unsigned gpio); //释放GPIO
5. int gpio_is_valid(int number); //检测此gpio口是否有效
批量初始化方法:
申请:
err = gpio_request_array(leds_gpios, ARRAY_SIZE(leds_gpios));
释放:
gpio_free_array(leds_gpios, ARRAY_SIZE(leds_gpios));
导出gpio到用户空间:int gpio_export(unsigned gpio, bool direction_may_change);
创建一个sysfs连接到已导出的GPIO节点:
int gpio_export_link(struct device *dev, const char *name, unsigned gpio)
取消导出:void gpio_unexport();
Gpio设置中断:
gpio ---> irq int gpio_to_irq(unsigned gpio);
首先应该设置此gpio为输入状态,然后获取对应的中断号(或错误吗)。返回编号调用:
request_irq()和free_irq()。
Irq ---> gpio int irq_to_gpio(unsigned irq);
返回gpio编号,再调用gpio_get_value()获取相应的值。(避免使用反向映射,不支持)
-----------------------------------------------------------------------------------------
gpiolib.c (gpio框架) drivers/gpio/gpiolib.c + include/asm-generic/gpio.h [对gpio chip接口]
-----------------------------------------------------------------------------------------
gpio_chip作为一个接口负责框架层与控制器层的通讯,主要关注点有:
其申请/释放/方向/获取输入/设置输出/转irq/base+ngpio[见第三部分控制器驱动]
在框架层的主要关注点在:
1. 如何分配不同chip的gpio域
2. 如何管理隶属与不同chip的gpio,并反向追溯到chip以调用控制器的具体寄存器操作
3. 统一提前管理了哪些gpio状态以及是否有必要
在gpiochip_add()中是对gpio chip的注册,并插入到框架gpio的管理中,
全局变量 static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
gpio_desc作为整个系统的gpio的管理者,主要包含两个成员:chip 与 flags.
flags为框架层对gpio的整体管理标识,起MASK的作用。[有:是否已申请/是否是输出/是否保留等]
插入的规则实现在:gpiochip_find_base(int ngpio)
从后往前遍历全局gpio desc, 只要是非保留gpio且无宿主chip的连续gpio的空间起址作为base, ngpio则依次向下扩展。
简单的初始化:挂上对应的chip,检查是否有设置输入函数,没有则设置 IS_OUT位到flags.
[其中关于gpio的设备树管理暂时不予关注]
request流程: 检查对应gpio的flags是否FLAG_REQUESTED,如果未被request则调用chip的request接口实现芯片级别的调用。
free流程: 确认已经被REQUESTED, 后启用芯片级的free.
从系统gpio接口传递下来的gpio均是以base为基址,而传递到芯片上都是回归到原始gpio号
------------------------------------------------------------------------------------------
SC8810 gpio控制器驱动
-------------------------------------------------------------------------------------------
这里是整个gpio系统的核心,初步总结需要关注以下几点:
chip支持的gpio section如何划分
gpio如何配置使能即芯片如何管理众多gpio口的多个标识位的功能选项
申请/释放/方向/获取输入/设置输出/转irq/base+ngpio的处理流程及原理
gpio对应irq号的分配及映射规则
配置一个系统gpio需要的必要步骤
section:(GPIO_BASE:0xE0031000/SPRD_MISC_BASE:0xE0037000)
{ (GPIO_BASE + 0*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 1*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 2*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 3*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 4*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 5*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 6*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 7*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 8*0x80), 0x10, GPIO_SECTION_GPIO },
{ (SPRD_MISC_BASE + 0x480), 0x10, GPIO_SECTION_GPIO },
{ (SPRD_MISC_BASE + 0x4c0), 0xe, GPIO_SECTION_GPIO },
当获取一个gpio号后,需要获取的基本信息为:在哪个section/偏移量是多大/是何种芯片gpio
获取方法:
a.((gpio_id>>4) -1) * 0x80 + (u32) GPIO_BASE;
b.gpio_id & 0xF
c.gpio是在数字芯片上还是模拟芯片上:
#define NR_D_DIE_GPIOS 147
即:芯片上的gpio号小于147即位于数字芯片,否则位于模拟芯片
在一个芯片的整个寄存器内存中,采取以section + 功能的管理方式,也就是说对于同一个gpio的不同功能配置需要通过三个步骤,第一首先
需要找到段寄存器(section的基址);第二步是功能偏移的管理寄存器(功能偏移),第三步是根据段内偏移定位到某一位(bit)来配置。
注:
根据不同类型的gpio类型:
enum gpio_section_type {
GPIO_SECTION_GPI = 0x0,
GPIO_SECTION_GPO,
GPIO_SECTION_GPIO,
GPIO_SECTION_INVALID
};
其相应的功能偏移页不同。
申请:设置其功能寄存器的GPIO_DMSK功能偏移,在对应段内偏移处置位。
释放:清除对应MASK位
输出方向:三个步骤,1.设置GPIO_DIR对应位为1;2.清除GPIO_INEN对应位;3.设置GPIO_DATA对应位为需要输出的值。
输入方向:第一二步相反,无第三步。
设置及获取值:直接设置/读取GPIO_DATA功能寄存器[需要检查一些相关的方向等信息]
to irq
全局的映射数组来管理所有的gpio与irq的映射
static struct gpio_irq_map gpio_irq_table[NR_GPIO_IRQS];
映射规则是:从0-10[仅限映射10个中断号],遍历映射表找到第一个gpio offset相同的表项,返回其对应的irq值。
方向 to gpio 一般kernel不支持使用,但实现原理与上相同。
在gpio芯片管理中,最重要的一块就是irq的管理,包括irq的所有属性管理,如:irq屏蔽使能/触发条件等等。
下一篇笔记将详细描述 kernel irq的管理框架以及gpio的irq分配规则及触发原理。