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.pswp.cn/web/14510.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/14510.shtml
英文地址,請注明出處:http://en.pswp.cn/web/14510.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

docker命令詳解大全

Docker是一種流行的容器化平臺&#xff0c;用于快速部署應用程序并管理容器的生命周期。以下是一些常用的Docker命令及其用途的概述&#xff1a; docker run&#xff1a;創建一個新容器并運行一個命令。docker ps&#xff1a;列出當前運行的容器。docker stop&#xff1a;停止…

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…

在云計算環境中,如何實現資源的高效分配和調度?

在云計算環境中&#xff0c;可以通過以下幾種方法實現資源的高效分配和調度&#xff1a; 負載均衡&#xff1a;通過負載均衡算法&#xff0c;將云計算集群的負載均勻地分配到各個節點上。常見的負載均衡算法有輪詢、最小連接數、最短響應時間等。 資源調度算法&#xff1a;為了…

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. 網絡結構小結參考文獻參考文…

Vue 2 和 Vue 3 中同步和異步

Vue 2 和 Vue 3 中同步和異步 Vue 2 同步和異步 同步更新 (Synchronous Updates) Vue 2 在數據更新后會進行同步渲染更新,但為了性能優化,Vue 會在內部隊列中異步地進行 DOM 更新。這意味著數據變化會立即被捕捉到,但實際的 DOM 更新會被推遲到下一個事件循環隊列中。new V…

基礎3 探索JAVA圖形編程桌面:邏輯圖形組件實現

在一個寬敞明亮的培訓教室里&#xff0c;陽光透過窗戶柔和地灑在地上&#xff0c;教室里擺放著整齊的桌椅。臥龍站在講臺上&#xff0c;面帶微笑&#xff0c;手里拿著激光筆&#xff0c;他的眼神中充滿了熱情和期待。他的聲音清晰而洪亮&#xff0c;傳遍了整個教室&#xff1a;…

Linux模擬考試

注意&#xff0c;以下答案僅供參考 1、某CentOS系統空間不夠&#xff0c;現加一塊100G的硬盤(是系統的第二塊硬盤&#xff09;&#xff0c;分為一個區99G&#xff0c;掛載點是/data&#xff0c;請寫出從分區到掛載并使用的整個步驟及相關命令。 1.創建分區&#xff1a; sudo f…

HTML5 通信方式及應用

目錄 postMessage APIWebSocketsServer-Sent Events (SSE)Fetch API / XMLHttpRequest (XHR)Web Workers & Service WorkersHTML5 提供了多種通信方式,使得瀏覽器中的Web應用能夠實現頁面間、窗口間、甚至與外部服務的有效通信。這些通信方式大大提升了Web應用的交互性和復…

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…