admin管理员组

文章数量:1130349

一般情况下在Android上层恢复出厂设置比较简单,可以直接发广播或调用相应的接口,如写cache/recovery/command这个文件后重启。OTA升级就是写的这个文件。但这样也有弊端,就是系统无法开启到上层或上层死机了就无法处理这个事件。

那我们可以在kernel内实现,这样只要可以开机到kernel就可以处理,RK平台在第一次开机时就会恢复出厂设置一次,原理就是misc这个文件里有boot-recovery                recovery --wipe_all,恢复出厂设置后清空misc分区,下次开机就不会进入recovery。我们可以借助这个原理来实现。

第一步,实现按键长按功能

原理GPIO和ADC按键都是在驱动里面做防抖的(即延时一段时间),我们借助这个原理去计数,按下多长时间后就触发恢复出厂设置。

#define DEBOUNCE_JIFFIES	(10 / (MSEC_PER_SEC / HZ))	/* 10ms */
#define ADC_SAMPLE_JIFFIES	(100 / (MSEC_PER_SEC / HZ))	/* 100ms */
#define WAKE_LOCK_JIFFIES	(1 * HZ)			/* 1s */

+  #define LONG_PRESS_TIMES 1000 //10s = DEBOUNCE_JIFFIES*1000 ms
+  unsigned int resetKey_press = 0;


static void keys_timer(unsigned long _data)
{
	struct rk_keys_button *button = (struct rk_keys_button *)_data;
	struct rk_keys_drvdata *pdata = dev_get_drvdata(button->dev);
	struct input_dev *input = pdata->input;
	int state;
	//char *cmd_buf;
	
	if (button->type == TYPE_GPIO)
		state = !!((gpio_get_value(button->gpio) ? 1 : 0) ^
			   button->active_low);
	else
		state = !!button->adc_state;

	if (button->state != state) {
		button->state = state;
	+	if(button->code == KEY_POWER2){
	+		resetKey_press = 0;
	+		printk("resetkey short press!\n");
	+	}
		input_event(input, EV_KEY, button->code, button->state);
		key_dbg(pdata, "%skey[%s]: report event[%d] state[%d]\n",
			button->type == TYPE_ADC ? "adc" : "gpio",
			button->desc, button->code, button->state);
		input_event(input, EV_KEY, button->code, button->state);
		input_sync(input);
	}

	if (state)
	{
		mod_timer(&button->timer, jiffies + DEBOUNCE_JIFFIES);
	+	if(button->code == KEY_POWER2){
	+		resetKey_press++;
	+		if(resetKey_press > LONG_PRESS_TIMES){
	+			resetKey_press = 0;
	+			printk("Retkey press 10s!\n");
	+			if(reset_wq)
	+				queue_work(reset_wq, &work);
	+		}
	+	}
	}
}

第二步,kernel层重启接口

kernel重启的接口函数

9  	EXPORT_SYMBOL_GPL(kernel_restart);          --不能在中断上下文中使用,可以接收cmd  ,如recovery
10  	EXPORT_SYMBOL_GPL(kernel_halt);
11  	EXPORT_SYMBOL_GPL(kernel_power_off);
12  	EXPORT_SYMBOL_GPL(emergency_restart);        --可以在中断上下文中使用,类似断电重启

 

三、实现kernel里写flash分区

flash分区可以直接在adb shell 里面像操作文件一样读写,如 cat /data/misc.img > /dev/block/by-name/misc

在kernel里面也按照这个原理去写分区,参考代码

#define  NODE "/dev/block/mmcblk0p19"

int get_partition_info(const char *filename, char *buf, loff_t offset,int length,bool flag){

    struct file *filep;
    mm_segment_t old_fs;

    filep= filp_open(filename, O_RDONLY, 0);
    if(IS_ERR(filep)){
        printk(KERN_CRIT"open %s err!\n",filename);
        return -1;
    }
 
    old_fs = get_fs();
    set_fs(KERNEL_DS);
    filep->f_op->llseek(filep, offset, SEEK_SET);
    if(flag==0)
        length=filep->f_op->read (filep, buf, length, &filep->f_pos);
    else
        length=filep->f_op->write(filep, buf, length, &filep->f_pos);
    set_fs(old_fs);
    filp_close(filep, 0);
    return length;
}

上面的函数是同步操作,可以放在中断上下文中,但是RK3399却无法使用,因为driver没有提供read write接口,提供的是异步操作接口read_iter   write_iter,直接使用这个函数接口比较麻烦,可以直接使用内核提供的vfs操作接口vfs_read和vfs_write这两个接口会自动判断是否支持同步读写和异步读写,比较安全。

前面说到RK3399不支持同步读写分区,那就无法放在中断上下文,就需要放在一个工作队列中来实现

#define LONG_PRESS_TIMES 1000 //10s = DEBOUNCE_JIFFIES*1000 ms
//add  to write misc to reboot recovery
+ #define  MISC "/dev/block/by-name/misc"
//#define MISC "/dev/block/mmcblk1p3"
unsigned int resetKey_press = 0;
+ char cmd_buf[100];
+ struct work_struct work;
+ static struct workqueue_struct *reset_wq;



probe中添加

+	reset_wq = create_singlethread_workqueue("reset_wq");
+	if (!reset_wq)
+   {
+        pr_err("Creat reset_wq workqueue failed.");
+        //return -ENOMEM;
+    }
+	INIT_WORK(&work, reset_work_func);


timer中断处理函数中
	if (state)
	{
		mod_timer(&button->timer, jiffies + DEBOUNCE_JIFFIES);
		if(button->code == KEY_POWER2){
			resetKey_press++;
			if(resetKey_press > LONG_PRESS_TIMES){
				resetKey_press = 0;
				printk("Retkey press 10s!\n");
	+			if(reset_wq)
	+				queue_work(reset_wq, &work);
			}
		}
	}

工作队列函数
static void reset_work_func(struct work_struct *work)
{
	//get_partition_info(MISC,cmd_buf,0,strlen(cmd_buf),0);
	strcpy(cmd_buf,"boot-recovery");
	get_partition_info(MISC,cmd_buf,0x4000,strlen(cmd_buf),1);
	strcpy(cmd_buf,"recovery\n--wipe_all");
	get_partition_info(MISC,cmd_buf,0x4040,strlen(cmd_buf),1);
	//emergency_restart();
	kernel_restart("recovery");
}

注意,不能使用emergency_restart,因为可能写操作还没结束,就重启了

 

如果只是实现长按重启,直接在timer中断中调用emergency_restart即可。

 

参考文档:

kernel中对文件的读写【学习笔记】

https://blog.csdn/weixin_33730836/article/details/85963708

kernel里读写一个分区 函数定义

https://blog.csdn/qq_34040053/article/details/80244404

内核中读写分区的例子

https://blog.csdn/mike8825/article/details/51944771

linux内核中操作文件的方法--使用get_fs()和set_fs(KERNEL_DS)

https://blog.csdn/mike8825/article/details/50906429

MTK平台,kernel中写EMMC指定分区

https://blog.csdn/qq_30025621/article/details/88379041

[RK3288][Android6.0] 调试笔记 --- 系统第一次开机进入Recovery模式原因

https://blog.csdn/kris_fei/article/details/53464461

Recovery启动流程(1)--- 应用层到开机进入recovery详解

https://wwwblogs/xiaolei-kaiyuan/p/5434445.html

基于RK3399的Linux驱动开发 -- EMMC驱动框架

https://blog.csdn/qq_28515331/article/details/93062795

一般情况下在Android上层恢复出厂设置比较简单,可以直接发广播或调用相应的接口,如写cache/recovery/command这个文件后重启。OTA升级就是写的这个文件。但这样也有弊端,就是系统无法开启到上层或上层死机了就无法处理这个事件。

那我们可以在kernel内实现,这样只要可以开机到kernel就可以处理,RK平台在第一次开机时就会恢复出厂设置一次,原理就是misc这个文件里有boot-recovery                recovery --wipe_all,恢复出厂设置后清空misc分区,下次开机就不会进入recovery。我们可以借助这个原理来实现。

第一步,实现按键长按功能

原理GPIO和ADC按键都是在驱动里面做防抖的(即延时一段时间),我们借助这个原理去计数,按下多长时间后就触发恢复出厂设置。

#define DEBOUNCE_JIFFIES	(10 / (MSEC_PER_SEC / HZ))	/* 10ms */
#define ADC_SAMPLE_JIFFIES	(100 / (MSEC_PER_SEC / HZ))	/* 100ms */
#define WAKE_LOCK_JIFFIES	(1 * HZ)			/* 1s */

+  #define LONG_PRESS_TIMES 1000 //10s = DEBOUNCE_JIFFIES*1000 ms
+  unsigned int resetKey_press = 0;


static void keys_timer(unsigned long _data)
{
	struct rk_keys_button *button = (struct rk_keys_button *)_data;
	struct rk_keys_drvdata *pdata = dev_get_drvdata(button->dev);
	struct input_dev *input = pdata->input;
	int state;
	//char *cmd_buf;
	
	if (button->type == TYPE_GPIO)
		state = !!((gpio_get_value(button->gpio) ? 1 : 0) ^
			   button->active_low);
	else
		state = !!button->adc_state;

	if (button->state != state) {
		button->state = state;
	+	if(button->code == KEY_POWER2){
	+		resetKey_press = 0;
	+		printk("resetkey short press!\n");
	+	}
		input_event(input, EV_KEY, button->code, button->state);
		key_dbg(pdata, "%skey[%s]: report event[%d] state[%d]\n",
			button->type == TYPE_ADC ? "adc" : "gpio",
			button->desc, button->code, button->state);
		input_event(input, EV_KEY, button->code, button->state);
		input_sync(input);
	}

	if (state)
	{
		mod_timer(&button->timer, jiffies + DEBOUNCE_JIFFIES);
	+	if(button->code == KEY_POWER2){
	+		resetKey_press++;
	+		if(resetKey_press > LONG_PRESS_TIMES){
	+			resetKey_press = 0;
	+			printk("Retkey press 10s!\n");
	+			if(reset_wq)
	+				queue_work(reset_wq, &work);
	+		}
	+	}
	}
}

第二步,kernel层重启接口

kernel重启的接口函数

9  	EXPORT_SYMBOL_GPL(kernel_restart);          --不能在中断上下文中使用,可以接收cmd  ,如recovery
10  	EXPORT_SYMBOL_GPL(kernel_halt);
11  	EXPORT_SYMBOL_GPL(kernel_power_off);
12  	EXPORT_SYMBOL_GPL(emergency_restart);        --可以在中断上下文中使用,类似断电重启

 

三、实现kernel里写flash分区

flash分区可以直接在adb shell 里面像操作文件一样读写,如 cat /data/misc.img > /dev/block/by-name/misc

在kernel里面也按照这个原理去写分区,参考代码

#define  NODE "/dev/block/mmcblk0p19"

int get_partition_info(const char *filename, char *buf, loff_t offset,int length,bool flag){

    struct file *filep;
    mm_segment_t old_fs;

    filep= filp_open(filename, O_RDONLY, 0);
    if(IS_ERR(filep)){
        printk(KERN_CRIT"open %s err!\n",filename);
        return -1;
    }
 
    old_fs = get_fs();
    set_fs(KERNEL_DS);
    filep->f_op->llseek(filep, offset, SEEK_SET);
    if(flag==0)
        length=filep->f_op->read (filep, buf, length, &filep->f_pos);
    else
        length=filep->f_op->write(filep, buf, length, &filep->f_pos);
    set_fs(old_fs);
    filp_close(filep, 0);
    return length;
}

上面的函数是同步操作,可以放在中断上下文中,但是RK3399却无法使用,因为driver没有提供read write接口,提供的是异步操作接口read_iter   write_iter,直接使用这个函数接口比较麻烦,可以直接使用内核提供的vfs操作接口vfs_read和vfs_write这两个接口会自动判断是否支持同步读写和异步读写,比较安全。

前面说到RK3399不支持同步读写分区,那就无法放在中断上下文,就需要放在一个工作队列中来实现

#define LONG_PRESS_TIMES 1000 //10s = DEBOUNCE_JIFFIES*1000 ms
//add  to write misc to reboot recovery
+ #define  MISC "/dev/block/by-name/misc"
//#define MISC "/dev/block/mmcblk1p3"
unsigned int resetKey_press = 0;
+ char cmd_buf[100];
+ struct work_struct work;
+ static struct workqueue_struct *reset_wq;



probe中添加

+	reset_wq = create_singlethread_workqueue("reset_wq");
+	if (!reset_wq)
+   {
+        pr_err("Creat reset_wq workqueue failed.");
+        //return -ENOMEM;
+    }
+	INIT_WORK(&work, reset_work_func);


timer中断处理函数中
	if (state)
	{
		mod_timer(&button->timer, jiffies + DEBOUNCE_JIFFIES);
		if(button->code == KEY_POWER2){
			resetKey_press++;
			if(resetKey_press > LONG_PRESS_TIMES){
				resetKey_press = 0;
				printk("Retkey press 10s!\n");
	+			if(reset_wq)
	+				queue_work(reset_wq, &work);
			}
		}
	}

工作队列函数
static void reset_work_func(struct work_struct *work)
{
	//get_partition_info(MISC,cmd_buf,0,strlen(cmd_buf),0);
	strcpy(cmd_buf,"boot-recovery");
	get_partition_info(MISC,cmd_buf,0x4000,strlen(cmd_buf),1);
	strcpy(cmd_buf,"recovery\n--wipe_all");
	get_partition_info(MISC,cmd_buf,0x4040,strlen(cmd_buf),1);
	//emergency_restart();
	kernel_restart("recovery");
}

注意,不能使用emergency_restart,因为可能写操作还没结束,就重启了

 

如果只是实现长按重启,直接在timer中断中调用emergency_restart即可。

 

参考文档:

kernel中对文件的读写【学习笔记】

https://blog.csdn/weixin_33730836/article/details/85963708

kernel里读写一个分区 函数定义

https://blog.csdn/qq_34040053/article/details/80244404

内核中读写分区的例子

https://blog.csdn/mike8825/article/details/51944771

linux内核中操作文件的方法--使用get_fs()和set_fs(KERNEL_DS)

https://blog.csdn/mike8825/article/details/50906429

MTK平台,kernel中写EMMC指定分区

https://blog.csdn/qq_30025621/article/details/88379041

[RK3288][Android6.0] 调试笔记 --- 系统第一次开机进入Recovery模式原因

https://blog.csdn/kris_fei/article/details/53464461

Recovery启动流程(1)--- 应用层到开机进入recovery详解

https://wwwblogs/xiaolei-kaiyuan/p/5434445.html

基于RK3399的Linux驱动开发 -- EMMC驱动框架

https://blog.csdn/qq_28515331/article/details/93062795

本文标签: 按键Kernel