实验十 Shell
新建 src/include/prt_shell.h 头文件
1#ifndef _HWLITEOS_SHELL_H
2#define _HWLITEOS_SHELL_H
3
4#include "prt_typedef.h"
5
6#define SHELL_SHOW_MAX_LEN 272
7#define PATH_MAX 1024
8
9typedef struct {
10 U32 consoleID;
11 U32 shellTaskHandle;
12 U32 shellEntryHandle;
13 void *cmdKeyLink;
14 void *cmdHistoryKeyLink;
15 void *cmdMaskKeyLink;
16 U32 shellBufOffset;
17 U32 shellBufReadOffset;
18 U32 shellKeyType;
19 char shellBuf[SHELL_SHOW_MAX_LEN];
20 char shellWorkingDirectory[PATH_MAX];
21} ShellCB;
22
23#endif /* _HWLITEOS_SHELL_H */
接收输入
QEMU的virt机器默认没有键盘作为输入设备,但当我们执行QEMU使用 -nographic 参数(disable graphical output and redirect serial I/Os to console)时QEMU会将串口重定向到控制台,因此我们可以使用UART作为输入设备。
在 src/bsp/print.c 中的 PRT_UartInit 添加初始化代码,使其支持接收数据中断。 同时定义了用于串口接收的信号量 sem_uart_rx。
1#include "prt_sem.h"
2#include "prt_shell.h"
3
4
5#define UARTCR_UARTEN (1 << 0)
6#define UARTCR_TXE (1 << 8)
7#define UARTCR_RXE (1 << 9)
8
9#define UARTICR_ALL (1 << 0)
10
11#define UARTIMSC_RXIM (1 << 4)
12
13#define UARTIBRD_IBRD_MASK 0xFFFF
14#define UARTFBRD_FBRD_MASK 0x3F
15
16#define UARTLCR_H_WLEN_MASK (3 << 5)
17#define UARTLCR_H_PEN (1 << 1)
18#define UARTLCR_H_STP1 (0 << 3)
19
20SemHandle sem_uart_rx;
21
22extern void OsGicIntSetConfig(uint32_t interrupt, uint32_t config);
23extern void OsGicIntSetPriority(uint32_t interrupt, uint32_t priority);
24extern void OsGicEnableInt(U32 intId);
25extern void OsGicClearInt(uint32_t interrupt);
26extern U32 PRT_Printf(const char *format, ...);
27U32 PRT_UartInit(void)
28{
29 U32 result = 0;
30 U32 reg_base = UART_0_REG_BASE;
31
32 UART_REG_WRITE(0, (unsigned long)(reg_base + 0x30));// 禁用pl011
33 UART_REG_WRITE(0x7ff, (unsigned long)(reg_base + 0x44));// 清空中断状态
34 UART_REG_WRITE(UARTIMSC_RXIM, (unsigned long)(reg_base + 0x38));// 设定中断mask,需要使能的中断
35 UART_REG_WRITE(13, (unsigned long)(reg_base + 0x24));
36 UART_REG_WRITE(1, (unsigned long)(reg_base + 0x28));
37
38 // https://developer.arm.com/documentation/ddi0183/g/programmers-model/register-descriptions/line-control-register--uartlcr-h?lang=en
39 result = UART_REG_READ((unsigned long)(reg_base + DW_UART_LCR_HR));
40 result = result | UARTLCR_H_WLEN_MASK | UARTLCR_H_PEN | UARTLCR_H_STP1 | DW_FIFO_ENABLE;
41 UART_REG_WRITE(result, (unsigned long)(reg_base + DW_UART_LCR_HR)); // 8N1 FIFO enable
42
43 UART_REG_WRITE(UARTCR_UARTEN | UARTCR_RXE | UARTCR_TXE, (unsigned long)(reg_base + 0x30));// 启用pl011
44
45
46 // 启用UART 接收中断
47 OsGicIntSetConfig(33, 0); //可省略
48 OsGicIntSetPriority(33, 0);
49 OsGicClearInt(33); //可省略
50 OsGicEnableInt(33);
51
52 // 创建uart数据接收信号量
53 U32 ret;
54 ret = PRT_SemCreate(0, &sem_uart_rx);
55 if (ret != OS_OK) {
56 PRT_Printf("failed to create uart_rx sem\n");
57 return 1;
58 }
59
60 return OS_OK;
61}
简单起见,在 src/bsp/print.c 中实现 OsUartRxHandle() 处理接收中断。
1extern ShellCB g_shellCB;
2void OsUartRxHandle(void)
3{
4 U32 flag = 0;
5 U32 result = 0;
6 U32 reg_base = UART_0_REG_BASE;
7
8 flag = UART_REG_READ((unsigned long)(reg_base + 0x18));
9 while((flag & (1<<4)) == 0)
10 {
11 result = UART_REG_READ((unsigned long)(reg_base + 0x0));
12 // PRT_Printf("%c", result);
13
14 // 将收到的字符存到g_shellCB的缓冲区
15 g_shellCB.shellBuf[g_shellCB.shellBufOffset] = (char) result;
16 g_shellCB.shellBufOffset++;
17 if (g_shellCB.shellBufOffset == SHELL_SHOW_MAX_LEN)
18 g_shellCB.shellBufOffset = 0;
19
20 PRT_SemPost(sem_uart_rx);
21 flag = UART_REG_READ((unsigned long)(reg_base + 0x18));
22 }
23 return;
24}
在 src/bsp/prt_exc.c 中OsHwiHandleActive() 链接中断和处理函数OsUartRxHandle()
1extern void OsTickDispatcher(void);
2extern void OsUartRxHandle(void);
3OS_SEC_ALW_INLINE INLINE void OsHwiHandleActive(U32 irqNum)
4{
5 switch(irqNum){
6 case 30:
7 OsTickDispatcher();
8 // PRT_Printf(".");
9 break;
10 case 33:
11 OsUartRxHandle();
12 default:
13 break;
14 }
15}
在 src/kernel/task/prt_task.c 中加入函数
1extern U32 PRT_Printf(const char *format, ...);
2OS_SEC_TEXT void OsDisplayTasksInfo(void)
3{
4 struct TagTskCb *taskCb = NULL;
5 U32 cnt = 0;
6
7 PRT_Printf("\nPID\t\tPriority\tStack Size\n");
8 // 遍历g_runQueue队列,查找优先级最高的任务
9 LIST_FOR_EACH(taskCb, &g_runQueue, struct TagTskCb, pendList) {
10 cnt++;
11 PRT_Printf("%d\t\t%d\t\t%d\n", taskCb->taskPid, taskCb->priority, taskCb->stackSize);
12 }
13 PRT_Printf("Total %d tasks", cnt);
14
15}
在 src/kernel/tick/prt_tick.c 中加入函数
1extern U32 PRT_Printf(const char *format, ...);
2OS_SEC_TEXT void OsDisplayCurTick(void)
3{
4 PRT_Printf("\nCurrent Tick: %d", PRT_TickGetCount());
5}
shell 处理
新建 src/shell/shmsg.c 文件。
1#include "prt_typedef.h"
2#include "prt_shell.h"
3#include "os_attr_armv8_external.h"
4#include "prt_task.h"
5#include "prt_sem.h"
6
7extern SemHandle sem_uart_rx;
8extern U32 PRT_Printf(const char *format, ...);
9extern void OsDisplayTasksInfo(void);
10extern void OsDisplayCurTick(void);
11
12
13OS_SEC_TEXT void ShellTask(uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4)
14{
15 U32 ret;
16 char ch;
17 char cmd[SHELL_SHOW_MAX_LEN];
18 U32 idx;
19 ShellCB *shellCB = (ShellCB *)param1;
20
21 while (1) {
22 PRT_Printf("\nminiEuler # ");
23 idx = 0;
24 for(int i = 0; i < SHELL_SHOW_MAX_LEN; i++)
25 {
26 cmd[i] = 0;
27 }
28
29 while (1){
30 PRT_SemPend(sem_uart_rx, OS_WAIT_FOREVER);
31
32 // 读取shellCB缓冲区的字符
33 ch = shellCB->shellBuf[shellCB->shellBufReadOffset];
34 cmd[idx] = ch;
35 idx++;
36 shellCB->shellBufReadOffset++;
37 if(shellCB->shellBufReadOffset == SHELL_SHOW_MAX_LEN)
38 shellCB->shellBufReadOffset = 0;
39
40 PRT_Printf("%c", ch); //回显
41 if (ch == '\r'){
42 // PRT_Printf("\n");
43 if(cmd[0]=='t' && cmd[1]=='o' && cmd[2]=='p'){
44 OsDisplayTasksInfo();
45 } else if(cmd[0]=='t' && cmd[1]=='i' && cmd[2]=='c' && cmd[3]=='k'){
46 OsDisplayCurTick();
47 }
48 break;
49 }
50
51 }
52 }
53}
54
55OS_SEC_TEXT U32 ShellTaskInit(ShellCB *shellCB)
56{
57 U32 ret = 0;
58 struct TskInitParam param = {0};
59
60 // task 1
61 // param.stackAddr = 0;
62 param.taskEntry = (TskEntryFunc)ShellTask;
63 param.taskPrio = 9;
64 // param.name = "Test1Task";
65 param.stackSize = 0x1000; //固定4096,参见prt_task_init.c的OsMemAllocAlign
66 param.args[0] = (uintptr_t)shellCB;
67
68 TskHandle tskHandle1;
69 ret = PRT_TaskCreate(&tskHandle1, ¶m);
70 if (ret) {
71 return ret;
72 }
73
74 ret = PRT_TaskResume(tskHandle1);
75 if (ret) {
76 return ret;
77 }
78}
提示
将新增文件加入构建系统
lab9 作业
作业1
实现一条有用的 shell 指令。