实验十 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, &param);
70    if (ret) {
71        return ret;
72    }
73
74    ret = PRT_TaskResume(tskHandle1);
75    if (ret) {
76        return ret;
77    }
78}

提示

将新增文件加入构建系统

lab9 作业

作业1

实现一条有用的 shell 指令。