到了这里,就是vivi启动的最后阶段了。
#define DEFAULT_BOOT_DELAY 0x3000
void boot_or_vivi(void)
{
char c;
int ret;
ulong boot_delay;
#if 0
boot_delay = get_param_value("boot_delay", &ret);
if (ret) boot_delay = DEFAULT_BOOT_DELAY;
#else
boot_delay = DEFAULT_BOOT_DELAY;
#endif
/* If a value of boot_delay is zero,
* unconditionally call vivi shell */
if (boot_delay == 0) vivi_shell();
/*
* wait for a keystroke (or a button press if you want.)
*/
printk("Press Return to start the LINUX now, any other key for vivi\n");
c = awaitkey(boot_delay, NULL);
if (((c != '\r') && (c != '\n') && (c != '\0'))) {
printk("type \"help\" for help.\n");
vivi_shell();
}
run_autoboot();
return;
}
|
boot_or_vivi()---->vivi_shell() ---->run_autoboot()
我们可以看到,在这个函数中,要不就是运行vivi命令模式(vivi_shell),或者是自动启动(run_autoboot)
因为我们是定义了延时:#define DEFAULT_BOOT_DELAY 0x3000
先来看看awaitkey(boot_delay, NULL);函数:
/*
* Reads and returns a character from the serial port
* - Times out after delay iterations checking for presence of character
* - Sets *error_p to UART error bits or - on timeout
* - On timeout, sets *error_p to -1 and returns 0
*/
char awaitkey(unsigned long delay, int* error_p)
{
return (do_getc(NULL, delay, error_p));
}
|
__u8 do_getc(vfuncp idler, unsigned long timeout, int *statp)
{
__u8 c, rxstat;
int do_timeout = timeout != 0;
getc_errno = 0; /* reste errno */
while(!SERIAL_CHAR_READY()) {
if (do_timeout) {
if (!timeout)
break;
timeout--;
}
if (idler)
idler();
}
if (do_timeout && timeout == 0) {
c = 0;
rxstat = -1;
} else {
c = SERIAL_READ_CHAR();
rxstat = SERIAL_READ_STATUS();
}
//如果有错误则进入下面的if语句,而且将错误返回给*statp
if (rxstat) {
getc_errno = rxstat;
/*printk("RXSTAT error. status = 0x%08lx", rxstat);*/
if (statp)
*statp = rxstat;
}
return (c);
}
|
其中他们的作用就是第一个函数的英文部分。
就是在一定的时间内输入一个信号,要是超时没有输入,则返回0,如果在时间内有输入,则获取输入的字符,且检查错误。
分析完上面的细节,我们来看看vivi_shell();
vivi_shell的流程就是:
1:用户输入命令;
2:调用get_cmd()函数将命令保存在一个数组中,并反输出到串口终端,是用户能够看到;
3:在获取了命令之后则是要解析获取的命令,通过调用parseargs(buf, &argc, argv, &resid)函数
4:解析完成之后则是按照用户输入的命令,指向相应的函数execcmd(argc, (const char **)argv)
void
vivi_shell(void)
{
#ifdef CONFIG_SERIAL_TERM
serial_term();
#else
#error there is no terminal.
#endif
}
|
对于vivi而言,串口是必不可少的。几乎所有的信号都是通过串口来传送的
void serial_term(void) { char cmd_buf[MAX_CMDBUF_SIZE];
for (;;) { printk("%s> ", prompt);
getcmd(cmd_buf, MAX_CMDBUF_SIZE);
/* execute a user command */ if (cmd_buf[0]) exec_string(cmd_buf); } }
|
serial_term()--->getcmd();
下面这个函数正好帮我们复习ASCII码表,还有c语言中的转义符。
我在c语言文件夹中已经添加了ASCII表和转义符的含义:
void getcmd(char *cmd_buf, unsigned int len)
{
char curpos = 0; /* current position - index into cmd_buf */
char c;
int cmd_echo = 1;
/* Clear out the buffer */
memset(cmd_buf, 0, MAX_CMDBUF_SIZE);
for (;;) {
c = getc();
switch (c) {
case 0x08: //退一格
case 0x06: //ACK
case 0x07: //BELL
case 0x7E: //~
case 0x7F: /* backspace or delete */
/* we're not at the beginning of the line */
if (curpos) {
curpos--;
putc(0x08); /* go backwards */
putc(' '); /* overwrite the char */
putc(0x08); /* go back again */
}
cmd_buf[curpos] = '\0';
break;
case '\r': //回车
case '\n': //换行
case '\0': //NULL
putc('\r'); //输出回车
putc('\n'); //输出换行
goto end_cmd;
case CTL_CH('x'):
curpos = 0;
break;
default:
if (curpos < MAX_CMDBUF_SIZE) {
cmd_buf[curpos] = c;
/* echo it back out to the screen */
if (cmd_echo)
putc(c);
curpos++;
}
break;
}
}
end_cmd:
DPRINTK("COMMAND: %s\n", cmd_buf);
}
|
这个函数的作用就是获取用户键入的命令,并且保存在cmd_buf数组中,而且把这个命令通过终端显示出来
serial_term()--->exec_string();(vivi/lib/command.c)
/* parse and execute a string */
void exec_string(char *buf)
{
int argc;
char *argv[128];
char *resid;
while (*buf) {
memset(argv, 0, sizeof(argv));
parseargs(buf, &argc, argv, &resid);
if (argc > 0)
execcmd(argc, (const char **)argv);
buf = resid;
}
}
|
serial_term()--->exec_string()--->parseargs()
--->execcmd()
void parseargs(char *argstr, int *argc_p, char **argv, char** resid)
{
int argc = 0;
char c;
enum ParseState lastState = PS_WHITESPACE;
/* tokenize the argstr */
while ((c = *argstr) != 0) {
enum ParseState newState;
if (c == ';' && lastState != PS_STRING && lastState != PS_ESCAPE)
break;
if (lastState == PS_ESCAPE) {
newState = stackedState;
} else if (lastState == PS_STRING) {
if (c == '"') {
newState = PS_WHITESPACE;
*argstr = 0;
} else {
newState = PS_STRING;
}
} else if ((c == ' ') || (c == '\t')) {
/* whitespace character */
*argstr = 0;
newState = PS_WHITESPACE;
} else if (c == '"') {
newState = PS_STRING;
*argstr++ = 0;
argv[argc++] = argstr;
} else if (c == '\\') {
stackedState = lastState;
newState = PS_ESCAPE;
} else {
/* token */
if (lastState == PS_WHITESPACE) {
argv[argc++] = argstr;
}
newState = PS_TOKEN;
}
lastState = newState;
argstr++;
}
argv[argc] = NULL;
if (argc_p != NULL)
*argc_p = argc;
if (*argstr == ';') {
*argstr++ = '\0';
}
*resid = argstr;
}
|
在解析完命令以后则是执行:exec_cmd(argc, const char ** argv)
/* execute a function */
void execcmd(int argc, const char **argv)
{
user_command_t *cmd = find_cmd(argv[0]);
if (cmd == NULL) {
printk("Could not found '%s' command\n", argv[0]);
printk("If you want to konw available commands, type 'help'\n");
return;
}
/*printk("execcmd: cmd=%s, argc=%d\n", argv[0], argc);*/
cmd->cmdfunc(argc, argv);
}
|
上面的执行函数需要调用find_cmd(const char *cmdname)来遍历整个命令结构数组,找到我们需要的命令结构
/* find command */
user_command_t *find_cmd(const char *cmdname)
{
user_command_t *curr;
/* do da string compare for the first offset character of cmdstr
against each number of the cmdlist */
curr = head_cmd;
while(curr != NULL) {
if (strncmp(curr->name, cmdname, strlen(cmdname)) == 0)
return curr;
curr = curr->next_cmd;
}
return NULL;
}
|
再来看vivi_or_boot的第二个部分
run_autoboot():
void run_autoboot(void) {
while (1) {
exec_string("boot");
printk("Failed 'boot' command. reentering vivi shell\n");
/* if default boot fails, drop into the shell */
vivi_shell();
} }
|
其实就是执行boot命令。类似于利用vivi_shell的方式,用户输入boot命令!
到这里整个vivi启动的部分已经完成,很多细节的东西还没讲到,比如vivi向内核传递参数还有就是每个命令函数的具体实现,这些都要具体去看,不仅可以增加C语言的水平,而且对启动也有一个更整体和清晰的认识!!!在后面我会陆续补上。
阅读(1172) | 评论(0) | 转发(0) |