关机动作从按键触发中断,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();            }        }    };

调用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);     }  

shutdownInner函數如下,其調用了beginShutdownSequence(context);

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()方法     }  

在run()方法中也會做一些reboot之前的清除工作,关掉要关的服务等。

基本最终会调用

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   }

我們繞來繞去其實最終還是要通過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_OSreboot(RB_POWER_OFF);#endif}



其中,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



系统最后会执行系统调用sys_reboot(reboot)。reboot通常定义在内核kernel/kernel/sys.c

4 。内核部分。 kernel/sys.c

其中,

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;


当pm_power_off为空时, 由上层传入的命令LINUX_REBOOT_CMD_POWER_OFF会变为

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;

编译内核 ,OK

android层可以正常关闭电源了


***************************************************************************************************************************

参考文章1:

Andriod关机&重启分析

1jni

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_OFFRB_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…...

2 、通过 jni 层,最后会执行系统调用 sys_reboot ,即 reboot 。以 imx51 平台为例, reboot 定义在 kernel_imx/kernel/sys.c


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中关机方式,具体要看硬件支持。

3sys_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才能实现正常的关机。

4reboot

reboot较简单,通过命令LINUX_REBOOT_CMD_RESTART执行kernel_restart(NULL)即可。


*****************************************************************************************************************

参考文章2:

gpio框架及处理流程分析

----------------------------------------------------------------------------------------------
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分配规则及触发原理。


更多相关文章

  1. Android(安卓)的OpenGL ES与EGL
  2. Android实现为GridView添加边框效果
  3. Android(安卓)NDK c调用java代码
  4. Android中Dialog的使用
  5. Android(安卓)打开设置的各个页面
  6. Android权限设置android.permission完整列表
  7. 调用Android自带的联系人选择界面
  8. Android布局(layout)
  9. 箭头函数的基础使用

随机推荐

  1. Android(安卓)项目实例config.gradle配置
  2. Android中WebView和JavaScript通信
  3. android dns设置(无法解析域名问题)
  4. 2011.10.19(3)——— android 一个便签的Wi
  5. Android多种方法获取系统时间
  6. Android多种数据库存储方式对比
  7. gdb 远程调试android进程
  8. Android(安卓)的动作、广播、类别等标识
  9. Android(安卓)-- TypedArray
  10. Android控件开发之一----TextView