当待机流程就会完成一些硬件驱动的现场操作,灯也是如此。
kernel\fusion\4.9\kernel\reboot.c
/*
* Reboot system call: for obvious reasons only root may call it,
* and even root needs to set up some magic numbers in the registers
* so that some mistake won't make this reboot the whole machine.
* You can also set the meaning of the ctrl-alt-del-key here.
*
* reboot doesn't sync: do that yourself before calling this.
*/
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
void __user *, arg)
{
struct pid_namespace *pid_ns = task_active_pid_ns(current);
char buffer[256];
int ret = 0;
/* We only trust the superuser with rebooting the system. */
if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
return -EPERM;
/* For safety, we require "magic" arguments. */
if (magic1 != LINUX_REBOOT_MAGIC1 ||
(magic2 != LINUX_REBOOT_MAGIC2 &&
magic2 != LINUX_REBOOT_MAGIC2A &&
magic2 != LINUX_REBOOT_MAGIC2B &&
magic2 != LINUX_REBOOT_MAGIC2C))
return -EINVAL;
/*
* If pid namespaces are enabled and the current task is in a child
* pid_namespace, the command is handled by reboot_pid_ns() which will
* call do_exit().
*/
ret = reboot_pid_ns(pid_ns, cmd);
if (ret)
return ret;
/* Instead of trying to make the power_off code look like
* halt when pm_power_off is not set do it the easy way.
*/
#if (MP_PLATFORM_PM == 0)
if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
cmd = LINUX_REBOOT_CMD_HALT;
#endif
mutex_lock(&reboot_mutex);
switch (cmd) {
case LINUX_REBOOT_CMD_RESTART:
#ifdef CONFIG_MP_AMAZON_SIGN_OF_LIFE
life_cycle_set_boot_reason(WARMBOOT_BY_SW);
#endif
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();
do_exit(0);
panic("cannot halt");
case LINUX_REBOOT_CMD_POWER_OFF:
#ifdef CONFIG_MP_AMAZON_SIGN_OF_LIFE
life_cycle_set_shutdown_reason(SHUTDOWN_BY_SW);
#endif
kernel_power_off();
do_exit(0);
break;
case LINUX_REBOOT_CMD_RESTART2:
#ifdef CONFIG_MP_AMAZON_SIGN_OF_LIFE
life_cycle_set_boot_reason(WARMBOOT_BY_SW);
#endif
ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
if (ret < 0) {
ret = -EFAULT;
break;
}
buffer[sizeof(buffer) - 1] = '\0';
kernel_restart(buffer);
break;
#ifdef CONFIG_KEXEC_CORE
case LINUX_REBOOT_CMD_KEXEC:
ret = kernel_kexec();
break;
#endif
#ifdef CONFIG_HIBERNATION
case LINUX_REBOOT_CMD_SW_SUSPEND:
ret = hibernate();
break;
#endif
default:
ret = -EINVAL;
break;
}
mutex_unlock(&reboot_mutex);
return ret;
}
当收到上层reboot 指令时,根据不同的cmd 来决定是关机,重启,挂起等等。
/*
* Commands accepted by the _reboot() system call.
*
* RESTART Restart system using default command and mode.
* HALT Stop OS and give system control to ROM monitor, if any.
* CAD_ON Ctrl-Alt-Del sequence causes RESTART command.
* CAD_OFF Ctrl-Alt-Del sequence sends SIGINT to init task.
* POWER_OFF Stop OS and remove all power from system, if possible.
* RESTART2 Restart system using given command string.
* SW_SUSPEND Suspend system using software suspend if compiled in.
* KEXEC Restart system using a previously loaded Linux kernel
*/
#define LINUX_REBOOT_CMD_RESTART 0x01234567
#define LINUX_REBOOT_CMD_HALT 0xCDEF0123
#define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF
#define LINUX_REBOOT_CMD_CAD_OFF 0x00000000
#define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC
#define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4
#define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2
#define LINUX_REBOOT_CMD_KEXEC 0x45584543
本篇我们关注关机流程,收到 cmd “LINUX_REBOOT_CMD_POWER_OFF ” 时,就会进入kernel_power_off.
void kernel_power_off(void)
{
#if defined(CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND) || defined(CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE) || defined(CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL)
#if defined(CONFIG_MSTAR_CPU_CLUSTER_CALIBRATING)
atomic_set(&disable_dvfs_reboot, 1);
#endif
#endif
#if defined(CONFIG_MSTAR_PM)
check_boot_mode = 0;
#endif
kernel_shutdown_prepare(SYSTEM_POWER_OFF);
if (pm_power_off_prepare)
pm_power_off_prepare();
migrate_to_reboot_cpu();
syscore_shutdown();
pr_emerg("Power down\n");
kmsg_dump(KMSG_DUMP_POWEROFF);
machine_power_off();
#ifdef CONFIG_MSTAR_MBX
#if (MP_PLATFORM_PM == 1)
mstar_str_notifypmmaxcnt_off();
#endif
#endif
}
pm_power_off_prepare 做关机前PM的预处理,
migrate_to_reboot_cpu
syscore_shutdown 调用各个驱动注册的shutdown 回调函数
machine_power_off 停止CPU任务
/*
* Power-off simply requires that the secondary CPUs stop performing any
* activity (executing tasks, handling interrupts). smp_send_stop()
* achieves this. When the system power is turned off, it will take all CPUs
* with it.
*/
void machine_power_off(void)
{
local_irq_disable();
smp_send_stop();
if (pm_power_off)
pm_power_off();
}
pm_power_off PM开始断电。
下面我们主要看和MTK平台相关的部分,其它kernel 的暂不关注。
pm_power_off_prepare 和 pm_power_off 都是在mstar2\hal\m7332\cpu\arm\pm.c 里面注册
static void mstar_pm_power_off_prepare(void)
{
printk("mstar set pm power off param\n");
MDrv_PM_Reboot(E_PM_STATE_POWER_OFF_PRE);
mstar_sleep_cur_cpu_flush();
}
static void mstar_pm_power_off(void)
{
printk("mstar pm power down\n");
MDrv_PM_Reboot(E_PM_STATE_POWER_OFF);
_mstar_str_enter_tee(NULL);
}
static int __init mstar_pm_init(void)
{
pm_power_off_prepare = mstar_pm_power_off_prepare;
pm_power_off = mstar_pm_power_off;
/* set operation function of suspend */
suspend_set_ops(&mstar_pm_ops);
return 0;
}
mstar_pm_power_off_prepare 和 mstar_pm_power_off 调用 MDrv_PM_Reboot 发送了E_PM_STATE_POWER_OFF_PRE, E_PM_STATE_POWER_OFF 两个cmd,但是E_PM_STATE_POWER_OFF没有做处理。
PM_Result MDrv_PM_Reboot(PM_STATE eState)
{
PM_Result eRet = E_PM_OK;
switch (eState)
{
case E_PM_STATE_REBOOT_NOTIFY:
if (!is_mstar_str())
{
// MDrv_PM_Mapping() via mi notify callback.
eRet &= _MDrv_PM_Store_Fw();
}
break;
case E_PM_STATE_POWER_OFF_PRE:
if (!is_mstar_str())
{
/* TODO: Remove. */
_MDrv_PM_Set_Ir();
#ifdef CONFIG_MSTAR_SYSFS_BACKLIGHT
MDrv_PM_TurnoffBacklight();
#endif
#if (CONFIG_MSTAR_GPIO)
_MDrv_PM_Set_Led();
#endif
// TODO: Remove patch for reboot recovery case.
_MDrv_PM_Patch_Mapping();
_MDrv_PM_Set_Mode(PM_DC_MODE);
_MDrv_PM_Flush_Config();
eRet &= _MDrv_PM_BringUp();
}
break;
default:
eRet = E_PM_FAIL;
break;
}
return eRet;
}
接着 _MDrv_PM_Set_Led 开始控制灯
#if (CONFIG_MSTAR_GPIO)
static void _MDrv_PM_Set_Led(void)
{
u8 u8LedPad = 0;
u8LedPad = MDrv_PM_Get_PowerOffLed();
printk("Get LedPadNum = %d.\n", u8LedPad);
if (u8LedPad != PM_SOURCE_DISABLE)
{
printk("======= set red led on (set PAD_PWM_PM to High) ========\n");
MDrv_GPIO_Set_High(u8LedPad);
}
}
#endif
而这个LedPad 是在PM 注册时设置
static int _mstar_drv_pm_probe(struct platform_device *pdev)
{
int retval = 0;
U8 u8LedPad = 0xFF;
struct device_node *node = pdev->dev.of_node;
const struct of_device_id *match = of_match_device(of_match_ptr(mstar_pm_of_device_ids), &pdev->dev);
if (!(pdev->name) || strcmp(pdev->name, MDRV_PM_DD_NAME) || (pdev->id != 0))
{
retval = -ENXIO;
}
if (match != NULL)
{
if (of_property_read_u8(node, "poweroff_led", &u8LedPad) >= 0)
{
if (u8LedPad != 0xFF)
{
MDrv_PM_Set_PowerOffLed(u8LedPad);
}
}
}
return retval;
}
poweroff_led 即是从设备树中配置。
在m7332_an.dts 中配置mstar-pm 驱动的poweroff_led 属性
mstar-pm {
compatible = "mstar-pm";
poweroff_led = /bits/ 8 <4>;
};
其中4 为 PAD_PWM_PM 脚,在mhal_gpio_reg.h 文件中定义
#define PAD_PWM_PM 4
在现在框架里面根据,在dts 配置上Led 的pin 即可。也可以在mstar_pm_power_off_prepare或者mstar_pm_power_off自行添加。
拓展
system\core\reboot\reboot.c 收到reboot -p 指令时,会设置系统属性"sys.powerctl", system\core\init\init.cpp property_changed 检测到属性变化时,会处理
bool HandlePowerctlMessage(const std::string& command) {
unsigned int cmd = 0;
std::vector<std::string> cmd_params = android::base::Split(command, ",");
std::string reboot_target = "";
bool run_fsck = false;
bool command_invalid = false;
if (cmd_params.size() > 3) {
command_invalid = true;
} else if (cmd_params[0] == "shutdown") {
cmd = ANDROID_RB_POWEROFF;
if (cmd_params.size() == 2 && cmd_params[1] == "userrequested") {
// The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
// Run fsck once the file system is remounted in read-only mode.
run_fsck = true;
}
} else if (cmd_params[0] == "reboot") {
cmd = ANDROID_RB_RESTART2;
if (cmd_params.size() >= 2) {
reboot_target = cmd_params[1];
// When rebooting to the bootloader notify the bootloader writing
// also the BCB.
if (reboot_target == "bootloader") {
std::string err;
if (!write_reboot_bootloader(&err)) {
LOG(ERROR) << "reboot-bootloader: Error writing "
"bootloader_message: "
<< err;
}
}
// If there is an additional bootloader parameter, pass it along
if (cmd_params.size() == 3) {
reboot_target += "," + cmd_params[2];
}
}
} else if (command == "thermal-shutdown") { // no additional parameter allowed
cmd = ANDROID_RB_THERMOFF;
} else {
command_invalid = true;
}
if (command_invalid) {
LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
return false;
}
DoReboot(cmd, command, reboot_target, run_fsck);
return true;
}
doReboot 进一步处理
void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
bool runFsck) {
Timer t;
LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
android::base::WriteStringToFile(StringPrintf("%s\n", reason.c_str()), LAST_REBOOT_REASON_FILE,
S_IRUSR | S_IWUSR, AID_SYSTEM, AID_SYSTEM);
if (cmd == ANDROID_RB_THERMOFF) { // do not wait if it is thermal
DoThermalOff();
abort();
}
constexpr unsigned int shutdownTimeoutDefault = 6;
unsigned int shutdownTimeout = shutdownTimeoutDefault;
if (SHUTDOWN_ZERO_TIMEOUT) { // eng build
shutdownTimeout = 0;
} else {
shutdownTimeout =
android::base::GetUintProperty("ro.build.shutdown_timeout", shutdownTimeoutDefault);
}
LOG(INFO) << "Shutdown timeout: " << shutdownTimeout;
// keep debugging tools until non critical ones are all gone.
const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
const std::set<std::string> to_starts{"watchdogd", "vold", "ueventd"};
ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
if (kill_after_apps.count(s->name())) {
s->SetShutdownCritical();
} else if (to_starts.count(s->name())) {
s->Start();
s->SetShutdownCritical();
}
});
Service* bootAnim = ServiceManager::GetInstance().FindServiceByName("bootanim");
Service* surfaceFlinger = ServiceManager::GetInstance().FindServiceByName("surfaceflinger");
if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
ServiceManager::GetInstance().ForEachServiceInClass("animation", [](Service* s) {
s->SetShutdownCritical(); // will not check animation class separately
});
}
// optional shutdown step
// 1. terminate all services except shutdown critical ones. wait for delay to finish
if (shutdownTimeout > 0) {
LOG(INFO) << "terminating init services";
// Ask all services to terminate except shutdown critical ones.
ServiceManager::GetInstance().ForEachService([](Service* s) {
if (!s->IsShutdownCritical()) s->Terminate();
});
int service_count = 0;
// Up to half as long as shutdownTimeout or 3 seconds, whichever is lower.
unsigned int terminationWaitTimeout = std::min<unsigned int>((shutdownTimeout + 1) / 2, 3);
while (t.duration_s() < terminationWaitTimeout) {
ServiceManager::GetInstance().ReapAnyOutstandingChildren();
service_count = 0;
ServiceManager::GetInstance().ForEachService([&service_count](Service* s) {
// Count the number of services running except shutdown critical.
// Exclude the console as it will ignore the SIGTERM signal
// and not exit.
// Note: SVC_CONSOLE actually means "requires console" but
// it is only used by the shell.
if (!s->IsShutdownCritical() && s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
service_count++;
}
});
if (service_count == 0) {
// All terminable services terminated. We can exit early.
break;
}
// Wait a bit before recounting the number or running services.
std::this_thread::sleep_for(50ms);
}
LOG(INFO) << "Terminating running services took " << t
<< " with remaining services:" << service_count;
}
// minimum safety steps before restarting
// 2. kill all services except ones that are necessary for the shutdown sequence.
ServiceManager::GetInstance().ForEachService([](Service* s) {
if (!s->IsShutdownCritical()) s->Stop();
});
ServiceManager::GetInstance().ReapAnyOutstandingChildren();
// 3. send volume shutdown to vold
Service* voldService = ServiceManager::GetInstance().FindServiceByName("vold");
if (voldService != nullptr && voldService->IsRunning()) {
ShutdownVold();
voldService->Stop();
} else {
LOG(INFO) << "vold not running, skipping vold shutdown";
}
// logcat stopped here
ServiceManager::GetInstance().ForEachService([&kill_after_apps](Service* s) {
if (kill_after_apps.count(s->name())) s->Stop();
});
// 4. sync, try umount, and optionally run fsck for user shutdown
sync();
UmountStat stat = TryUmountAndFsck(runFsck, shutdownTimeout * 1000 - t.duration_ms());
// Follow what linux shutdown is doing: one more sync with little bit delay
sync();
std::this_thread::sleep_for(100ms);
LogShutdownTime(stat, &t);
// Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
RebootSystem(cmd, rebootTarget);
abort();
}
系统关闭一些上层服务后,会进入内核部分
static void __attribute__((noreturn))
RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
LOG(INFO) << "Reboot ending, jumping to kernel";
switch (cmd) {
case ANDROID_RB_POWEROFF:
reboot(RB_POWER_OFF);
break;
case ANDROID_RB_RESTART2:
syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
break;
case ANDROID_RB_THERMOFF:
reboot(RB_POWER_OFF);
break;
}
// In normal case, reboot should not return.
PLOG(FATAL) << "reboot call returned";
abort();
}
reboot(RB_POWER_OFF); 其中RB_POWER_OFF在libc 库里面定义的值为#define RB_POWER_OFF 0x4321fedc
与内核LINUX_REBOOT_CMD_POWER_OFF 是同一个cmd,接着会进入内核的SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg) 函数。