U-Boot menu菜单分析

文章目录

  • 前言
  • 目标
  • 环境背景
  • U-Boot如何自动调起菜单
  • U-Boot添加自定义命令
  • 实践

前言

在某个厂家的开发板中,在进入它的U-Boot后,会自动弹出一个菜单页面,输入对应的选项就会执行对应的功能。如SD卡镜像更新、显示设置等:
image.png

目标

本文主要分析U-Boot在程序中的执行顺序,又如何在U-Boot阶段调起菜单?相信大家都试过,在U-Boot倒数结束前按任意按键后,会进入U-Boot命令行模式。
这里先留一个问题:如何做到按键按下后,调启的是自己的U-Boot菜单,而不再是进入冷冰冰的命令行模式?

环境背景

本文介绍所用的U-Boot版本:2018

U-Boot如何自动调起菜单

U-Boot的入口程序文件是<u-boot>/common/main.c,入口函数main_loop()

/* We come here after U-Boot is initialised and ready to process commands */
/* 在U-Boot初始化并准备好处理命令之后,我们来到这里。 */
void main_loop(void)
{const char *s;bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");	#ifdef CONFIG_VERSION_VARIABLEenv_set("ver", version_string);  /* set version variable */		#endif /* CONFIG_VERSION_VARIABLE */cli_init();							//命令初始化有关,初始化 hush shell 相关的变量run_preboot_environment_command();	//获取环境变量 perboot 的内容#if defined(CONFIG_UPDATE_TFTP)update_tftp(0UL, NULL, NULL);		#endif /* CONFIG_UPDATE_TFTP */s = bootdelay_process();	//此函数会读取环境变量 bootdelay 和 bootcmd 的内容if (cli_process_fdt(&s))cli_secure_boot_cmd(s);autoboot_command(s);		//开启倒计时,并在倒计时结束前检测是否有按键按下cli_loop();					//命令行处理函数(即进入U-Boot命令行)panic("No CLI available");
}

关键函数是autoboot_command(),该函数的实现在<u-boot>/common/autoboot.c

void autoboot_command(const char *s)
{debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {	// 倒计时过程中,没有按键按下
#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)int prev = disable_ctrlc(1);    /* disable Control C checking */
#endifrun_command_list(s, -1, 0);		// 倒计时结束后,启动内核#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)disable_ctrlc(prev);    /* restore Control C checking */
#endif}#ifdef CONFIG_MENUKEYif (menukey == CONFIG_MENUKEY) {s = env_get("menucmd");if (s)run_command_list(s, -1, 0);}
#endif /* CONFIG_MENUKEY */
}

进入autoboot_command()后,先看第一个if

void autoboot_command(const char *s)
{...if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay))...
}

这里有三个条件:

  • stored_bootdelay != -1:stored_bootdelay是倒数的总时间,就是常见的3秒、5秒不等;
  • s:传进来的参数s不能为空;
  • !abortboot(stored_bootdelay):该函数会从stored_bootdelay开始倒计时,期间判断是否有按键按下。函数实现如下,倒计时过程中若检测到按键按下,则令abort=1。无按键按下,则abort=0。最后返回abort
static int __abortboot(int bootdelay)
{int abort = 0;unsigned long ts;#ifdef CONFIG_MENUPROMPTprintf(CONFIG_MENUPROMPT);
#elseprintf("Hit any key to stop autoboot: %2d ", bootdelay);
#endif/** Check if key already pressed*/if (tstc()) {   /* we got a key press   */(void) getc();  /* consume input        */puts("\b\b\b 0");abort = 1;      /* don't auto boot      */}while ((bootdelay > 0) && (!abort)) {--bootdelay;/* delay 1000 ms */ts = get_timer(0);do {if (tstc()) {   /* we got a key press   */abort  = 1;     /* don't auto boot      */bootdelay = 0;  /* no more delay        */
# ifdef CONFIG_MENUKEYmenukey = getc();
# else(void) getc();  /* consume input        */
# endifbreak;}udelay(10000);} while (!abort && get_timer(ts) < 1000);printf("\b\b\b%2d ", bootdelay);}putc('\n');return abort;
}

刚刚说了,abortboot()函数执行期间有按键按下的话,abortboot()会返回1,那就不会进入第一个if,程序会接着往下运行直至该函数运行结束。autoboot_command()结束后继续返回到main_loop(),随后立刻执行cli_loop(),进入我们所熟悉的U-Boot命令行模式。

void main_loop(void)
{...autoboot_command(s);		//检查倒计时是否结束cli_loop();					//命令行处理函数
}

image.png
至此,就实现了U-Boot倒数期间,有按键按下,则进入U-Boot的命令行模式。
现在继续回到第一个if

void autoboot_command(const char *s)
{...if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {	// 倒计时过程中,没有按键按下
#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)int prev = disable_ctrlc(1);    /* disable Control C checking */
#endifrun_command_list(s, -1, 0);		// 倒计时结束后,启动内核#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)disable_ctrlc(prev);    /* restore Control C checking */
#endif}...
}

如果在autoboot倒计时结束前,一直没有按键按下呢?那abortboot()最后会返回0,第一个if的三个条件全部满足。进入ifrun_command_list()执行一系列命令后,启动内核。注意,这里的现象是直接启动内核,run_command_list()后的程序不再执行。


解析到这里,我们得出一个结论:在autoboot倒计时中,如果有按键按下的话,会进入U-Boot的命令行模式。无按键按下则在倒计时结束后直接启动内核。
那现在可以回答第一个问题,如何做到按下按键后,是自启动U-Boot菜单,而不是进入U-Boot命令行呢?答案是在执行cli_loop()之前,我们可以在autoboot检测到按键按下后,调用run_command()函数执行menu命令,从而调起菜单。

void autoboot_command(const char *s)
{debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)int prev = disable_ctrlc(1);    /* disable Control C checking */
#endifrun_command_list(s, -1, 0);#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)disable_ctrlc(prev);    /* restore Control C checking */
#endif}//启动菜单run_command("menu", 0);#ifdef CONFIG_MENUKEYif (menukey == CONFIG_MENUKEY) {s = env_get("menucmd");if (s)run_command_list(s, -1, 0);}
#endif /* CONFIG_MENUKEY */
}

U-Boot添加自定义命令

难道通过run_command()执行menu命令后,菜单就自己出来了?这是一个理所当然的猜想。实际上U-Boot根本不认识menu命令:
image.png
接下来看看如何添加U-Boot命令,参考一下别人的代码:

int do_brightness(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{int rcode = 0;ulong side;ulong bright;switch (argc) {case 3:side = simple_strtoul(argv[1], NULL, 10);bright = simple_strtoul(argv[2], NULL, 10);if ((side >= 0) && (side <= 3) &&(bright >= 0) && (bright <= 1000)) {vcxk_setbrightness(side, bright);rcode = 0;} else {printf("parameters out of range\n");printf("Usage:\n%s\n", cmdtp->usage);rcode = 1;}break;default:printf("Usage:\n%s\n", cmdtp->usage);rcode = 1;break;}return rcode;
}U_BOOT_CMD(bright,	3,	0,	do_brightness,"sets the display brightness\n"," <side> <0..1000>\n        side: 0/3=both; 1=first; 2=second\n"
);

先看最底下的U_BOOT_CMD,这是一个宏,用来添加U-Boot命令:

#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)		\U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)
  • _name:命令的名字
  • _maxargs:添加的命令最多有几个参数
  • _rep:是否重复(1重复,0不重复),指在U-Boot命令行按下Enter键的时候,重复执行上次的命令
  • _cmd:执行函数(即执行该命令后,运行哪个函数)
  • _usage:短帮助信息
  • _help:长帮助信息

再来看看执行函数do_brightness的声名:

int (*cmd)(struct cmd_tbl_s *cmdtp, int flag, int argc, char *const argv[]);
  • cmdtp:Table entry describing the command (see above).
  • flag:A bitmap which may contain the following bit
    • CMD_FLAG_REPEAT - The last command is repeated.
    • CMD_FLAG_BOOTD - The command is called by the bootd command.
    • CMD_FLAG_ENV - The command is called by the run command.
  • argc:执行命令时,传入的参数数量
  • argv:传入的参数

实践

下面,添加一个U-Boot菜单,不过只作打印,没有实际功能。
<u-boot>/drivers下创建一个名为mymenu的文件夹:
image.png
mymenu文件夹下创建mymenu.c,内容如下:

#include <common.h>
#include <command.h>
#include <linux/ctype.h>
#include <cli.h>
#include <fs.h>static int do_mymenu(struct cmd_tbl_s *cmdtp, int flag, int argc, char *const argv[])
{if(argc != 2)return 0;if(!strcmp(argv[1], "pageone"))printf("\n======== pageone ========\n");            else if(!strcmp(argv[1], "pagetwo"))printf("\n======== pagetwo ========\n"); elseprintf("\n======== pageone ========\n"); printf("== [1] xxxxxx\n");printf("== [2] xxxxxx\n");printf("== [3] xxxxxx\n");printf("== [4] xxxxxx\n");printf("=========================\n\n");return 0;
}U_BOOT_CMD(mymenu,	2,	1, do_mymenu,"here is uboot mymenu\n","here is uboot mymenu, make in 2024-05-15\n"
);

还需在mymenu文件夹下创建一个Makefile文件,内容如下:

obj-y += mymenu.o

最后修改<u-boot>/drivers/下的Makefile,在结尾加上如下内容,表示要编译mymenu路径下的文件:
image.png
编译U-Boot,更新U-Boot,重启单板,在U-Boot倒计时结束前,按任意按键进入U-Boot命令行,输入mymenu后,可以看到命令被正确识别,对应的函数也执行成功:
image.png
那如何做到按任意按键后直接调起菜单呢?上面有说过,可以在autoboot检测到按键按下后,调用run_command()函数执行mymenu命令,从而调起菜单。

void autoboot_command(const char *s)
{debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)int prev = disable_ctrlc(1);    /* disable Control C checking */
#endifrun_command_list(s, -1, 0);#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)disable_ctrlc(prev);    /* restore Control C checking */
#endif}//启动菜单run_command("mymenu", 0);#ifdef CONFIG_MENUKEYif (menukey == CONFIG_MENUKEY) {s = env_get("menucmd");if (s)run_command_list(s, -1, 0);}
#endif /* CONFIG_MENUKEY */
}

image.png
剩下的菜单程序编写就是根据实际功能来开发了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/14510.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity射击游戏开发教程:(20)增加护盾强度

在本文中,我们将增强护盾,使其在受到超过 1 次攻击后才会被禁用。 Player 脚本具有 Shield PowerUp 方法,我们需要调整盾牌在被摧毁之前可以承受的数量,因此我们将声明一个 int 变量来设置盾牌可以承受的击中数量。

微信小程序画布显示图片绘制矩形选区

wxml <view class"page-body"><!-- 画布 --><view class"page-body-wrapper"><canvas canvas-id"myCanvas" type"2d" id"myCanvas" classmyCanvas bindtouchstart"touchStart" bindtouchmo…

OpenFeign快速入门 替代RestTemplate

1.引入依赖 <!--openFeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--负载均衡器--><dependency><groupId>org.spr…

【全网最全】2024电工杯数学建模B题问题一14页论文+19建模过程代码+py代码+2种保奖思路+数据等(后续会更新成品论文等)

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片链接&#xff0c;那是获取资料的入口&#xff01; 【全网最全】2024电工杯数学建模B题问一论文19建模过程代码py代码2种保奖思路数据等&#xff08;后续会更新成品论文等&#xff09;「首先来看看目前已…

C++中的四种类型转换运算符

隐式类型转换是安全的&#xff0c;显式类型转换是有风险的&#xff0c;C语言之所以增加强制类型转换的语法&#xff0c;就是为了强调风险&#xff0c;让程序员意识到自己在做什么。但是&#xff0c;这种强调风险的方式还是比较粗放&#xff0c;粒度比较大&#xff0c;它并没有表…

MySQL中如何知道数据库表中所有表的字段的排序规则是什么?

查看所有表的字段及其排序规则&#xff1a; 你可以查询 information_schema 数据库中的 COLUMNS 表&#xff0c;来获取所有表的字段及其排序规则。以下是一个示例查询&#xff1a; SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLLATION_NAME FROM information_schema.COL…

【设计模式深度剖析】【5】【创建型】【原型模式】| 类比群发邮件,加深理解

&#x1f448;️上一篇:建造者模式 | 下一篇:创建型设计模式对比&#x1f449;️ 目录 原型模式(Prototype Pattern)概览定义英文原话直译 3个角色类图1. 抽象原型&#xff08;Prototype&#xff09;角色2. 具体原型&#xff08;Concrete Prototype&#xff09;角色3. 客户…

必示科技参与智能运维国家标准预研线下编写会议并做主题分享

近日&#xff0c;《信息技术服务 智能运维 第3部分&#xff1a;算法治理》&#xff08;拟定名&#xff09;国家标准预研阶段第一次编写工作会议在杭州举行。本次会议由浙商证券承办。 此次编写有来自银行、证券、保险、通信、高校研究机构、互联网以及技术方等29家单位&#xf…

Linux基础(四):Linux系统文件类型与文件权限

各位看官&#xff0c;好久不见&#xff0c;在正式介绍Linux的基本命令之前&#xff0c;我们首先了解一下&#xff0c;关于文件的知识。 目录 一、文件类型 二、文件权限 2.1 文件访问者的分类 2.2 文件权限 2.2.1 文件的基本权限 2.2.2 文件权限值的表示方法 三、修改文…

CSS3 新增背景属性 + 新增边框属性(如果想知道CSS3新增背景属性和新增边框属性的知识点,那么只看这一篇就够了!)

前言&#xff1a;CSS3在CSS2的基础上&#xff0c;新增了很多强大的新功能&#xff0c;从而解决一些实际面临的问题&#xff0c;本篇文章主要讲解的为CSS3新增背景属性和新增边框属性。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSD…

视觉SLAM十四讲:从理论到实践(Chapter5:相机与图像)

前言 学习笔记&#xff0c;仅供学习&#xff0c;不做商用&#xff0c;如有侵权&#xff0c;联系我删除即可 目标 理解针孔相机的模型、内参与径向畸变参数。理解一个空间点是如何投影到相机成像平面的。掌握OpenCV的图像存储与表达方式。学会基本的摄像头标定方法。 一、相…

机器学习第四十周周报 WDN GGNN

文章目录 week40 WDN GGNN摘要Abstract一、文献阅读1. 题目2. abstract3. 网络架构3.1 问题提出3.2 GNN3.3 CSI GGNN 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过程4.3.1 数据获取4.3.2 参数设置4.3.3 实验结果 5. 结论二、GGNN1. 代码解释2. 网络结构小结参考文献参考文…

基础3 探索JAVA图形编程桌面:逻辑图形组件实现

在一个宽敞明亮的培训教室里&#xff0c;阳光透过窗户柔和地洒在地上&#xff0c;教室里摆放着整齐的桌椅。卧龙站在讲台上&#xff0c;面带微笑&#xff0c;手里拿着激光笔&#xff0c;他的眼神中充满了热情和期待。他的声音清晰而洪亮&#xff0c;传遍了整个教室&#xff1a;…

Hsql每日一题 | day02

前言 就一直向前走吧&#xff0c;沿途的花终将绽放~ 题目&#xff1a;主播同时在线人数问题 如下为某直播平台主播开播及关播时间&#xff0c;根据该数据计算出平台最高峰同时在线的主播人数。 id stt edt 1001,2021-06-14 12:12:12,2021-06-14 18:1…

【错误解决】使用HuggingFaceInstructEmbeddings时的一个错误

起因&#xff1a;使用huggingface构建一个问答程序时出现的问题。 错误内容&#xff1a; 分析&#xff1a; 查看代码发现&#xff0c;HuggingFaceInstructEmbeddings和sentence-transformers模块版本不兼容导致。 可以明显看到方法参数不同。 解决&#xff1a; 安装sentenc…

element-ui的Form 表单有些项的参数校验

项目场景&#xff1a; 提示&#xff1a;项目相关背景&#xff1a; 项目场景&#xff1a;有时候自己的Form 表单中的某几项引入的一些项不好去校验 这样的咋去校验呢&#xff1f; 解决方案&#xff1a; 提示&#xff1a;问题的具体解决方案&#xff1a; 例如&#xff1a;写一…

【pyspark速成专家】3_Spark之RDD编程1

目录 ​编辑 一&#xff0c;创建RDD 二&#xff0c;常用Action操作 三&#xff0c;常用Transformation操作 一&#xff0c;创建RDD 创建RDD主要有两种方式&#xff0c;一个是textFile加载本地或者集群文件系统中的数据&#xff0c; 第二个是用parallelize方法将Driver中的…

fortran77 初始化矩阵 打印矩阵 模版 备拷

1&#xff0c;源码 SUBROUTINE INIT_MATRIX(A, m, n, lda)DOUBLE PRECISION A(*)CALL SRAND(2024)DO i1, mDO j1, nA(i lda*(j-1)) RAND() RAND() C WRITE(*, (F8.4)) A(i)END DOEND DOENDSUBROUTINE PRINT_MATRIX(A, m, n, lda)DOUBLE PREC…

【Vue3】封装axios请求(cli和vite)

原文作者&#xff1a;我辈李想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 Vue 【Vue3】env环境变量的配置和使用&#xff08;区分cli和vite&#xff09; 文章目录 Vue前言一、常见用法二、vue3cli封装接口1..env配置2..dev(开…

ADC协议详解

文章目录 简介工作流程原理图时序图 优点与缺点 简介 模数转换器&#xff08;ADC&#xff0c;Analog-to-Digital Converter&#xff09;是一种将模拟信号转换为数字信号的电子设备。模拟信号通常表示物理测量的连续变化&#xff0c;如声音、温度、压力等&#xff0c;而数字信号…