概述
本文档详细介绍如何在 OpenHarmony LiteOS-M 内核中添加自定义 shell 命令,以 version、reboot、poweroff 命令为例进行说明。
目录结构
kernel/liteos_m/components/shell/
├── include/shcmd.h # 命令声明头文件
├── src/base/shcmd.c # 命令注册实现
├── src/cmds/ # 命令实现目录
│ ├── version_shellcmd.c # version命令实现
│ ├── reboot_shellcmd.c # reboot命令实现
│ └── poweroff_shellcmd.c # poweroff命令实现
└── BUILD.gn # 构建配置文件
开发步骤
第一步:创建命令实现文件
在 kernel/liteos_m/components/shell/src/cmds/ 目录下创建命令实现文件。
示例:version_shellcmd.c
c
#include "los_compiler.h"
#include "los_debug.h"
#include "syspara/parameter.h"
INT32 OsShellCmdVersion(INT32 argc, const CHAR **argv)
{
char value[128] = {0};
// 检查参数个数
if (argc != 0) {
PRINTK("Usage: version\n");
return -1;
}
// 输出系统信息
PRINTK("OpenHarmony System\n");
PRINTK("Kernel: LiteOS-M\n");
// 从参数系统获取版本信息
if (GetParameter("const.ohos.fullname", "", value, sizeof(value)) > 0) {
PRINTK("Version: %s\n", value);
} else {
PRINTK("Version: Unknown\n");
}
return 0;
}
示例:reboot_shellcmd.c
c
#include "los_compiler.h"
#include "los_debug.h"
// LS2K300 PMC寄存器地址
#define PMC_BASE_ADDR 0x16124000
#define PMC_RESET_REG_OFFSET 0x00
INT32 OsShellCmdReboot(INT32 argc, const CHAR **argv)
{
if (argc != 0) {
PRINTK("Usage: reboot\n");
return -1;
}
PRINTK("System rebooting...\n");
// 写入复位寄存器
volatile UINT32 *reg = (volatile UINT32 *)(PMC_BASE_ADDR + PMC_RESET_REG_OFFSET);
*reg = 0x1; // 第0位写1触发复位
// 内存屏障,确保写入完成
__asm__ volatile("dbar 0" ::: "memory");
return 0;
}
示例:poweroff_shellcmd.c
c
#include "los_compiler.h"
#include "los_debug.h"
// LS2K300 PMC寄存器地址
#define PMC_BASE_ADDR 0x16124000
#define PMC_SHUTDOWN_REG_OFFSET 0x14
INT32 OsShellCmdPoweroff(INT32 argc, const CHAR **argv)
{
if (argc != 0) {
PRINTK("Usage: poweroff\n");
return -1;
}
PRINTK("System shutting down...\n");
// 写入关机寄存器
volatile UINT32 *reg = (volatile UINT32 *)(PMC_BASE_ADDR + PMC_SHUTDOWN_REG_OFFSET);
*reg = 0x3c00; // 写入0x3c00触发关机
// 内存屏障
__asm__ volatile("dbar 0" ::: "memory");
return 0;
}
第二步:声明命令函数
在 kernel/liteos_m/components/shell/include/shcmd.h 文件中添加命令函数声明。
c
#ifndef SHCMD_H
#define SHCMD_H
// ... 其他声明 ...
// 添加新命令声明
extern INT32 OsShellCmdVersion(INT32 argc, const CHAR **argv);
extern INT32 OsShellCmdReboot(INT32 argc, const CHAR **argv);
extern INT32 OsShellCmdPoweroff(INT32 argc, const CHAR **argv);
#endif
第三步:注册命令
在 kernel/liteos_m/components/shell/src/base/shcmd.c 文件中注册命令。
找到 g_shellcmdAll 数组,添加新命令的注册信息:
c
STATIC const SHELL_CMD_INFO_S g_shellcmdAll[] = {
// ... 其他命令 ...
// version 命令
{
.cmdKey = "version",
.handler = (SHELL_CMD_HANDLER)OsShellCmdVersion,
.help = "Display system version information",
},
// reboot 命令
{
.cmdKey = "reboot",
.handler = (SHELL_CMD_HANDLER)OsShellCmdReboot,
.help = "Reboot the system",
},
// poweroff 命令
{
.cmdKey = "poweroff",
.handler = (SHELL_CMD_HANDLER)OsShellCmdPoweroff,
.help = "Power off the system",
},
};
第四步:添加构建配置
在 kernel/liteos_m/components/shell/BUILD.gn 文件中添加新源文件。
gn
sources = [
# ... 其他源文件 ...
"src/cmds/version_shellcmd.c",
"src/cmds/reboot_shellcmd.c",
"src/cmds/poweroff_shellcmd.c",
]
如果需要使用参数系统(如 version 命令),还需要添加依赖:
gn
external_deps = [
# ... 其他依赖 ...
"init:parameterbase",
]
第五步:编译测试
执行编译命令:
bash
cd /home/vm/oh/oh61
hb build -p ls2k300_mini_dp --gn-args build_xts=true
关键概念说明
1. 命令函数原型
c
INT32 OsShellCmdXXX(INT32 argc, const CHAR **argv)
argc: 参数个数(不包括命令本身)argv: 参数数组- 返回值: 0 表示成功,非0表示失败
2. 输出函数
使用 PRINTK 宏进行输出:
c
PRINTK("Hello World\n");
PRINTK("Value: %d\n", value);
3. 参数解析示例
c
INT32 OsShellCmdExample(INT32 argc, const CHAR **argv)
{
// 无参数命令
if (argc != 0) {
PRINTK("Usage: example\n");
return -1;
}
// 带参数命令
if (argc != 1) {
PRINTK("Usage: example <param>\n");
return -1;
}
PRINTK("Param: %s\n", argv[0]);
return 0;
}
4. 硬件寄存器访问
c
// 定义寄存器地址
#define REG_BASE 0x16124000
#define REG_OFFSET 0x00
// 访问寄存器
volatile UINT32 *reg = (volatile UINT32 *)(REG_BASE + REG_OFFSET);
UINT32 value = *reg; // 读寄存器
*reg = 0x1; // 写寄存器
// 内存屏障(LoongArch)
__asm__ volatile("dbar 0" ::: "memory");
常见问题
1. 编译错误:函数未定义
错误信息:
undefined reference to `OsShellCmdXXX'
解决方法:
- 检查函数是否只在 .c 文件中定义一次
- 检查头文件中是否使用了
#ifndef保护 - 检查是否有内联函数重复定义
2. 运行时显示 "Unknown"
问题: version 命令显示 "Version: Unknown"
原因: 参数系统未正确加载参数配置
解决方法:
在设备配置文件中启用参数加载:
gn
# device/board/loongson/ls2k300_mini_dp/liteos_m/config.gni
init_feature_begetctl_liteos = true
3. 编译错误:头文件找不到
错误信息:
fatal error: xxx.h: No such file or directory
解决方法:
- 检查头文件路径是否正确
- 检查 BUILD.gn 中是否添加了正确的 include_dirs
完整示例代码
version_shellcmd.c
c
#include "los_compiler.h"
#include "los_debug.h"
#include "syspara/parameter.h"
INT32 OsShellCmdVersion(INT32 argc, const CHAR **argv)
{
char value[128] = {0};
if (argc != 0) {
PRINTK("Usage: version\n");
return -1;
}
PRINTK("OpenHarmony System\n");
PRINTK("Kernel: LiteOS-M\n");
if (GetParameter("const.ohos.fullname", "", value, sizeof(value)) > 0) {
PRINTK("Version: %s\n", value);
} else {
PRINTK("Version: Unknown\n");
}
return 0;
}
reboot_shellcmd.c
c
#include "los_compiler.h"
#include "los_debug.h"
#define PMC_BASE_ADDR 0x16124000
#define PMC_RESET_REG_OFFSET 0x00
INT32 OsShellCmdReboot(INT32 argc, const CHAR **argv)
{
if (argc != 0) {
PRINTK("Usage: reboot\n");
return -1;
}
PRINTK("System rebooting...\n");
volatile UINT32 *reg = (volatile UINT32 *)(PMC_BASE_ADDR + PMC_RESET_REG_OFFSET);
*reg = 0x1;
__asm__ volatile("dbar 0" ::: "memory");
return 0;
}
poweroff_shellcmd.c
c
#include "los_compiler.h"
#include "los_debug.h"
#define PMC_BASE_ADDR 0x16124000
#define PMC_SHUTDOWN_REG_OFFSET 0x14
INT32 OsShellCmdPoweroff(INT32 argc, const CHAR **argv)
{
if (argc != 0) {
PRINTK("Usage: poweroff\n");
return -1;
}
PRINTK("System shutting down...\n");
volatile UINT32 *reg = (volatile UINT32 *)(PMC_BASE_ADDR + PMC_SHUTDOWN_REG_OFFSET);
*reg = 0x3c00;
__asm__ volatile("dbar 0" ::: "memory");
return 0;
}
调试技巧
1. 使用 PRINTK 调试
c
INT32 OsShellCmdTest(INT32 argc, const CHAR **argv)
{
PRINTK("Debug: argc = %d\n", argc);
for (int i = 0; i < argc; i++) {
PRINTK("Debug: argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
2. 检查参数系统状态
c
#include "init_param.h"
void CheckParamStatus(void)
{
int ret = SystemGetParameter("const.ohos.fullname", value, &len);
PRINTK("GetParameter ret = %d\n", ret);
}
参考资料
- OpenHarmony 官方文档
- LiteOS-M 内核开发指南
- LoongArch 架构参考手册