MTK 增加Factory模式命令

因为客户需要增加factory mode,而且只能用命令进入,而不让用户感知这个模式的存在,所以决定在系统中增加命令: reboot factory,从而进入factory mode

先看一下reboot命令是如何实现的。

我们在串口输入或者在cmd命令行输入 reboot后,系统会重启,如果后面跟的参数是 bootloader,机器会进入fastboot模式,而如果后面跟的参数是 recovery,则机器进入recovery模式。

一、用户空间部分

查看下reboot命令的代码:

#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    int ret;
    size_t prop_len;
    char property_val[PROPERTY_VALUE_MAX];
    const char *cmd = "reboot";
    char *optarg = "";

    opterr = 0;
    do {
        int c;

        c = getopt(argc, argv, "p");

        if (c == -1) {
            break;
        }

        switch (c) {
        case 'p':
            cmd = "shutdown";
            break;
        case '?':
            fprintf(stderr, "usage: %s [-p] [rebootcommand]\n", argv[0]);
            exit(EXIT_FAILURE);
        }
    } while (1);

    if(argc > optind + 1) {
        fprintf(stderr, "%s: too many arguments\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    if (argc > optind)
        optarg = argv[optind];

    prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg);
    if (prop_len >= sizeof(property_val)) {
        fprintf(stderr, "reboot command too long: %s\n", optarg);
        exit(EXIT_FAILURE);
    }

    ret = property_set(ANDROID_RB_PROPERTY, property_val);
    if(ret < 0) {
        perror("reboot");
        exit(EXIT_FAILURE);
    }

    // Don't return early. Give the reboot command time to take effect
    // to avoid messing up scripts which do "adb shell reboot && adb wait-for-device"
    while(1) { pause(); }

    fprintf(stderr, "Done\n");
    return 0;
}

 

代码比较简单,而且在代码中没有看到直接调用reboot函数或者其他函数来实现reboot。我们可以看到它只是设置了一个属性,这个属性为宏 “ANDROID_RB_PROPERTY”,而这个宏定义为:

#define ANDROID_RB_PROPERTY "sys.powerctl"

由此可见,只是设置了sys.powerctl这个属性,而这个属性会触发什么事情呢?

我们可以再init.rc中找到答案:

on property:sys.powerctl=*
    powerctl ${sys.powerctl}

这里调用了powerct方法,而我们得知init.rc中,这些叫做关键字,所以我们去init的源码中,找到对应的关键字处理:init/keywords.h

 KEYWORD(powerctl,    COMMAND, 1, do_powerctl)

进而找到do_powerctl函数: 定义在init/builtin.cpp中

int do_powerctl(int nargs, char **args)
{
    char command[PROP_VALUE_MAX];
    int res;
    int len = 0;
    int cmd = 0;
    const char *reboot_target;

    res = expand_props(command, args[1], sizeof(command));
    if (res) {
        ERROR("powerctl: cannot expand '%s'\n", args[1]);
        return -EINVAL;
    }

    if (strncmp(command, "shutdown", 8) == 0) {
        cmd = ANDROID_RB_POWEROFF;
        len = 8;
    } else if (strncmp(command, "reboot", 6) == 0) {
        cmd = ANDROID_RB_RESTART2;
        len = 6;
    } else {
        ERROR("powerctl: unrecognized command '%s'\n", command);
        return -EINVAL;
    }

    if (command[len] == ',') {
        reboot_target = &command[len + 1];
    } else if (command[len] == '\0') {
        reboot_target = "";
    } else {
        ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);
        return -EINVAL;
    }

    return android_reboot(cmd, 0, reboot_target);
}

 

通过传入的值分别处理:

reboot 和 shutdown 对应的处理不同,最后执行android_reboot函数

/system/core/libcutils.c

int android_reboot(int cmd, int flags UNUSED, const char *arg)
{
    int ret;

    sync();
    remount_ro();

    switch (cmd) {
        case ANDROID_RB_RESTART:
            ret = reboot(RB_AUTOBOOT);
            break;

        case ANDROID_RB_POWEROFF:
            ret = reboot(RB_POWER_OFF);
            break;

        case ANDROID_RB_RESTART2:
            ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                           LINUX_REBOOT_CMD_RESTART2, arg);
            break;

        default:
            ret = -1;
    }

    return ret;
}

 

这里调用了syscall,这个会触发系统进入内核调用,关于syscall如何陷入内核进行处理,我们参考别的资料

https://blog.csdn.net/rikeyone/article/details/91047118

 

二、内核部分

我们这里直接贴出内核中reboot的实现:

kernel/reboot.c

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 ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
		cmd = LINUX_REBOOT_CMD_HALT;

	mutex_lock(&reboot_mutex);
	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();
		do_exit(0);
		panic("cannot halt");

	case LINUX_REBOOT_CMD_POWER_OFF:
		kernel_power_off();
		do_exit(0);
		break;

	case LINUX_REBOOT_CMD_RESTART2:
		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
	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;
}

1、检查用户空间传入的magic值

2、根据传入的cmd,分别处理

我们传入的cmd为 LINUX_REBOOT_CMD_RESTART2,接着调用kernel_restart(buffer)函数,buff中的内容为附加的参数 :“bootloader”或者“recovery”,

void kernel_restart(char *cmd)
{
	kernel_restart_prepare(cmd);
	migrate_to_reboot_cpu();
	syscore_shutdown();
	if (!cmd)
		pr_emerg("Restarting system\n");
	else
		pr_emerg("Restarting system with command '%s'\n", cmd);
	kmsg_dump(KMSG_DUMP_RESTART);
	machine_restart(cmd);
}

这里做kernel restart之前的准备,最后调用machine_restart

arch/arm/process.c

void machine_restart(char *cmd)
{
	/* Disable interrupts first */
	local_irq_disable();
	smp_send_stop();

	/* Now call the architecture specific reboot code. */
	pr_emerg("machine_restart, arm_pm_restart(%p)\n", arm_pm_restart);
	if (arm_pm_restart)
		arm_pm_restart(reboot_mode, cmd);
	else
		do_kernel_restart(cmd);

	/*
	 * Whoops - the architecture was unable to reboot.
	 */
	printk("Reboot failed -- System halted\n");
	while (1);
}

当定义了arm_pm_restart函数,就执行arm_pm_restart函数,如果没有定义,则执行do_kernel_restart函数。

我们这里没有定义arm_pm_restart,所以执行do_kernel_restart

void do_kernel_restart(char *cmd)
{
	atomic_notifier_call_chain(&restart_handler_list, reboot_mode, cmd);
}

这个函数主要是发送通知给到各个注册了reboot通知的模块,我们这里主要看wtd中的通知处理

drivers/watchdog/mediatek/wtd/wd_api.c

这里注册notifier,设置notifier handler

static int __init mtk_arch_reset_init(void)
{
	int ret;

	mtk_restart_handler.notifier_call = mtk_arch_reset_handle;
	mtk_restart_handler.priority = 128;
	pr_alert("\n register_restart_handler- 0x%p, Notify call: - 0x%p\n",
		 &mtk_restart_handler, mtk_restart_handler.notifier_call);
	ret = register_restart_handler(&mtk_restart_handler);
	if (ret)
		pr_err("ARCH_RESET cannot register mtk_restart_handler!!!!\n");
	pr_alert("ARCH_RESET register mtk_restart_handler  ok!!!!\n");
	return ret;
}

pure_initcall(mtk_arch_reset_init);

当有通知过来后,执行mtk_restart_handler

static int mtk_arch_reset_handle(struct notifier_block *this, unsigned long mode, void *cmd)
{
	pr_alert("ARCH_RESET happen!!!\n");
	arch_reset(mode, cmd);
	pr_alert("ARCH_RESET end!!!!\n");
	return NOTIFY_DONE;
}

接下来就是调用arch_reset函数了,也是很重要的函数,可以看到我们传入的“bootloader”或者“recovery”等参数的处理过程

void arch_reset(char mode, const char *cmd)
{
#ifdef CONFIG_FPGA_EARLY_PORTING
	return;
#else
	char reboot = 0;
	int res = 0;
	struct wd_api *wd_api = NULL;

	res = get_wd_api(&wd_api);
	pr_alert("arch_reset: cmd = %s\n", cmd ? : "NULL");
	dump_stack();
	if (cmd && !strcmp(cmd, "charger")) {
		/* do nothing */
	} else if (cmd && !strcmp(cmd, "recovery")) {
		rtc_mark_recovery();
	} else if (cmd && !strcmp(cmd, "bootloader")) {
		rtc_mark_fast();
	} else if (cmd && !strcmp(cmd, "kpoc")) {
#ifdef CONFIG_MTK_KERNEL_POWER_OFF_CHARGING
		rtc_mark_kpoc();
#endif
#if defined(CONFIG_ARCH_MT8163) || defined(CONFIG_ARCH_MT8173)
	} else if (cmd && !strcmp(cmd, "rpmbpk")) {
		mtk_wd_SetNonResetReg2(0x0, 1);
#endif
	} else {
		reboot = 1;
	}

	if (res)
		pr_err("arch_reset, get wd api error %d\n", res);
	else
		wd_api->wd_sw_reset(reboot);
 #endif
}

这里对传入的参数分别进行处理,如果参数为 “recovery”,则执行rtc_mark_recovery函数,

drivers/watchdog/mediatek/wtd/mtk_rtc_common.c

void rtc_mark_recovery(void)
{
	unsigned long flags;
	struct rtc_time defaulttm;

	rtc_xinfo("rtc_mark_recovery\n");
	spin_lock_irqsave(&rtc_lock, flags);
	hal_rtc_set_spare_register(RTC_FAC_RESET, 0x1);
	/* Clear alarm setting when doing factory reset. */
	defaulttm.tm_year = RTC_DEFAULT_YEA - RTC_MIN_YEAR;
	defaulttm.tm_mon = RTC_DEFAULT_MTH;
	defaulttm.tm_mday = RTC_DEFAULT_DOM;
	defaulttm.tm_wday = 1;
	defaulttm.tm_hour = 0;
	defaulttm.tm_min = 0;
	defaulttm.tm_sec = 0;
	rtc_save_pwron_time(false, &defaulttm, false);
	hal_rtc_clear_alarm(&defaulttm);
	spin_unlock_irqrestore(&rtc_lock, flags);
}

 

这里主要是设置RTC_FAC_RESET寄存器的值,等到重启后,机器执行bootloader的时候,取出对应的值来判断该执行哪种模式。

所以,到此之后,我们知道reboot bootloader和reboot recovery在kernel中的执行过程。所以我们如果要想新建一个参数 reboot factory

来使机器进入factory mode,我们只需要在arch_reset中增加对应的处理,并且在RTC中找到一个合适的寄存器来保存值即可

 

修改代码后的补丁:

diff --git a/drivers/misc/mediatek/include/mt-plat/mtk_rtc.h b/drivers/misc/mediatek/include/mt-plat/mtk_rtc.h
old mode 100644
new mode 100755
index 2181e99..131ea65
--- a/drivers/misc/mediatek/include/mt-plat/mtk_rtc.h
+++ b/drivers/misc/mediatek/include/mt-plat/mtk_rtc.h
@@ -51,6 +51,7 @@ extern void rtc_mark_recovery(void);
 extern void rtc_mark_kpoc(void);
 #endif/*if defined(CONFIG_MTK_KERNEL_POWER_OFF_CHARGING)*/
 extern void rtc_mark_fast(void);
+extern void rtc_mark_factory_mode(void);
 extern u16 rtc_rdwr_uart_bits(u16 *val);
 extern void rtc_bbpu_power_down(void);
 extern void rtc_read_pwron_alarm(struct rtc_wkalrm *alm);
diff --git a/drivers/misc/mediatek/rtc/mtk_rtc_common.c b/drivers/misc/mediatek/rtc/mtk_rtc_common.c
old mode 100644
new mode 100755
index 87fe9d7..6de7d77
--- a/drivers/misc/mediatek/rtc/mtk_rtc_common.c
+++ b/drivers/misc/mediatek/rtc/mtk_rtc_common.c
@@ -386,6 +386,25 @@ void rtc_mark_fast(void)
        spin_unlock_irqrestore(&rtc_lock, flags);
 }
 
+void rtc_mark_factory_mode(void)
+{
+       unsigned long flags;
+
+       printk("rtc_mark_dbgt\n");
+       spin_lock_irqsave(&rtc_lock, flags);
+       hal_rtc_set_spare_register(RTC_ANDROID, 0x5);
+       spin_unlock_irqrestore(&rtc_lock, flags);
+}
+
+void rtc_clr_factory_mode(void)
+{
+       unsigned long flags;
+       printk("rtc_mark_dbgt\n");
+       spin_lock_irqsave(&rtc_lock, flags);
+       hal_rtc_set_spare_register(RTC_ANDROID, 0x0);
+       spin_unlock_irqrestore(&rtc_lock, flags);
+}
+
 u16 rtc_rdwr_uart_bits(u16 *val)
 {
        u16 ret = 0;
diff --git a/drivers/watchdog/mediatek/wdk/wd_api.c b/drivers/watchdog/mediatek/wdk/wd_api.c
old mode 100644
new mode 100755
index 7043b25..8aaca5b
--- a/drivers/watchdog/mediatek/wdk/wd_api.c
+++ b/drivers/watchdog/mediatek/wdk/wd_api.c
@@ -606,7 +606,10 @@ void arch_reset(char mode, const char *cmd)
        } else if (cmd && !strcmp(cmd, "rpmbpk")) {
                mtk_wd_SetNonResetReg2(0x0, 1);
 #endif
-       } else {
+       }else if(cmd && !strcmp(cmd, "factory")){
+               rtc_mark_factory_mode();
+       } 
+       else {
                reboot = 1;
        }

三、bootloader部分

diff --git a/platform/mt8163/boot_mode.c b/platform/mt8163/boot_mode.c
old mode 100644
new mode 100755
index ba857f1..a75a11a
--- a/platform/mt8163/boot_mode.c
+++ b/platform/mt8163/boot_mode.c
@@ -96,7 +96,7 @@ void boot_mode_select(void)
       return;
     }
     mrdump_check();
-
+       
 #if defined (HAVE_LK_TEXT_MENU)
 /*Check RTC to know if system want to reboot to Fastboot*/
     if(Check_RTC_PDN1_bit13())
@@ -129,6 +129,13 @@ void boot_mode_select(void)
           g_boot_mode = RECOVERY_BOOT;
           return;
     }
+       
+       if(Check_RTC_FAC_Mode())
+       {
+               Set_Clr_RTC_FAC_mode(false);
+               g_boot_mode = FACTORY_BOOT;
+               return;
+       }
     /*If MISC Write has not completed  in recovery mode
      before system reboot, go to recovery mode to
     finish remain tasks*/
diff --git a/platform/mt8163/mt_rtc.c b/platform/mt8163/mt_rtc.c
old mode 100644
new mode 100755
index 9a76aa4..1255a19
--- a/platform/mt8163/mt_rtc.c
+++ b/platform/mt8163/mt_rtc.c
@@ -249,6 +249,34 @@ bool Check_RTC_PDN1_bit13(void)
                return false;
 }
 
+#define RTC_PDN1_FAC_MODE 0x000f
+bool Check_RTC_FAC_Mode(void)
+{
+       U16 pdn1;
+
+       pdn1 = RTC_Read(RTC_PDN1);
+       if( (pdn1 & RTC_PDN1_ANDROID_MASK)== 0x05 )
+               return true;
+       else
+               return false;
+}
+
+void Set_Clr_RTC_FAC_mode(bool flag)
+{
+       U16 pdn1;
+       
+       rtc_writeif_unlock();
+       //use PDN1 bit13 for LK
+       pdn1 = RTC_Read(RTC_PDN1);
+       if(flag==true)
+               pdn1 = pdn1 | RTC_PDN1_FAC_MODE;
+       else if(flag==false)
+               pdn1 = pdn1 & ~RTC_PDN1_FAC_MODE;
+       RTC_Write(RTC_PDN1, pdn1);
+       rtc_write_trigger();
+}
+
+
 bool Check_RTC_Recovery_Mode(void)
 {
        U16 pdn1;

 

 

 

你可能感兴趣的:(MTK)