- 用户态共享网络协议栈 CODE/tools/net_stack.h(eth/ARP/IPv4/ICMP/UDP-DNS/TCP static inline,轮询模型) - shell 内建 ping/curl/run 命令;ping.elf/curl.elf 对齐 net_stack.h 真实协议栈 - shell dev_mode 串口镜像(g_serial_mirror)便于自动化验证 - DSK 启动流程:firstInit.txt 双行格式(firstInit + dev_mode),dev_mode=1 触发 shell 自动回归测试 - FirstInit 完成→持久化 USER.CONF + 翻转 firstInit 0->1(保留 dev_mode) - firstInit=1 时 DSK 加载 login.elf 进行用户选择 + 密码校验后再进 desktop - 新增 CODE/login/ 登录应用、CODE/DKM/dkm_shared.h、Logo.png - ahci/e1000/virtio_net/fat32/console_fb 驱动更新;utsm serial/block/dsk_loader 调整 - build.ps1 打包 ping.elf/curl.elf 进 SATA FAT32 根目录 - 移除 CODE/tools/bash(与 shell.elf 重复)
391 lines
13 KiB
C
391 lines
13 KiB
C
/* Deshab ping — 终端风格 ICMP Echo 工具
|
||
* 命令行输入 IP 地址,发送 ICMP Echo Request(net_stack.h 共享协议栈)。
|
||
* Esc 键退出返回 DSK。
|
||
*/
|
||
|
||
#include "../../firstInit/ascii_bitmaps.c"
|
||
#include "../desktop_app.h"
|
||
#include "../net_stack.h"
|
||
|
||
/* ---- 终端参数 ---- */
|
||
#define TERM_COLS 120
|
||
#define TERM_ROWS 40
|
||
#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS)
|
||
#define CHAR_STEP 12
|
||
#define CHAR_H 18
|
||
#define PROMPT_STR "ping> "
|
||
|
||
static da_app_context g_ac;
|
||
|
||
/* 终端缓冲区 */
|
||
static u8 term_ch[TERM_MAX_CHARS];
|
||
static u32 term_fg[TERM_MAX_CHARS];
|
||
static int term_w = TERM_COLS;
|
||
static int term_h = TERM_ROWS;
|
||
static int cur_col = 0;
|
||
static int cur_row = 0;
|
||
|
||
/* 输入缓冲 */
|
||
static char input_buf[256];
|
||
static int input_len = 0;
|
||
static int input_cursor = 0;
|
||
|
||
/* 命令历史 */
|
||
#define HIST_SIZE 16
|
||
static char history[HIST_SIZE][256];
|
||
static int hist_count = 0;
|
||
static int hist_idx = 0;
|
||
|
||
/* 坐标 */
|
||
static int term_x, term_y;
|
||
|
||
static void term_init_layout(void) {
|
||
term_x = 8;
|
||
term_y = DA_TITLEBAR_H + 4;
|
||
int avail_w = (int)g_ac.fb_w - 16;
|
||
int avail_h = (int)g_ac.fb_h - term_y - DA_STATUSBAR_H - 8;
|
||
term_w = avail_w / CHAR_STEP;
|
||
term_h = avail_h / CHAR_H;
|
||
if (term_w < 10) term_w = 10;
|
||
if (term_h < 5) term_h = 5;
|
||
if (term_w > TERM_COLS) term_w = TERM_COLS;
|
||
if (term_h > TERM_ROWS) term_h = TERM_ROWS;
|
||
}
|
||
|
||
static void term_clear(void) {
|
||
for (int i = 0; i < TERM_MAX_CHARS; i++) {
|
||
term_ch[i] = ' ';
|
||
term_fg[i] = DA_TEXT_PRIMARY;
|
||
}
|
||
cur_col = 0; cur_row = 0;
|
||
}
|
||
|
||
static void term_scroll(void) {
|
||
for (int r = 1; r < term_h; r++) {
|
||
for (int c = 0; c < term_w; c++) {
|
||
term_ch[(r-1)*TERM_COLS+c] = term_ch[r*TERM_COLS+c];
|
||
term_fg[(r-1)*TERM_COLS+c] = term_fg[r*TERM_COLS+c];
|
||
}
|
||
}
|
||
for (int c = 0; c < term_w; c++) {
|
||
term_ch[(term_h-1)*TERM_COLS+c] = ' ';
|
||
term_fg[(term_h-1)*TERM_COLS+c] = DA_TEXT_PRIMARY;
|
||
}
|
||
cur_row = term_h - 1; cur_col = 0;
|
||
}
|
||
|
||
static void term_newline(void) {
|
||
cur_col = 0; cur_row++;
|
||
if (cur_row >= term_h) term_scroll();
|
||
}
|
||
|
||
static void term_putc_color(char c, u32 color) {
|
||
if (c == '\n') { term_newline(); return; }
|
||
if (c == '\r') { cur_col = 0; return; }
|
||
if (c == '\t') { int sp = 4 - (cur_col % 4); for (int i = 0; i < sp; i++) term_putc_color(' ', color); return; }
|
||
if (c < ' ' || c > '~') c = '?';
|
||
if (cur_col >= term_w) term_newline();
|
||
int idx = cur_row * TERM_COLS + cur_col;
|
||
term_ch[idx] = (u8)c; term_fg[idx] = color;
|
||
cur_col++;
|
||
}
|
||
|
||
static void term_putc(char c) { term_putc_color(c, DA_TEXT_PRIMARY); }
|
||
static void term_puts_color(const char *s, u32 color) { while (*s) term_putc_color(*s++, color); }
|
||
static void term_puts(const char *s) { term_puts_color(s, DA_TEXT_PRIMARY); }
|
||
|
||
/* ---- 绘制 ---- */
|
||
static void redraw_all(void) {
|
||
da_fill_bg(&g_ac, DA_BG_PRIMARY);
|
||
da_draw_titlebar(&g_ac, "Ping", (i64)g_ac.fb_w);
|
||
/* 终端字符 */
|
||
for (int r = 0; r < term_h; r++) {
|
||
for (int c = 0; c < term_w; c++) {
|
||
int idx = r * TERM_COLS + c;
|
||
if (term_ch[idx] == ' ') continue;
|
||
da_draw_char(&g_ac, term_ch[idx],
|
||
term_x + c * CHAR_STEP, term_y + r * CHAR_H,
|
||
term_fg[idx], DA_BG_PRIMARY);
|
||
}
|
||
}
|
||
/* 光标 */
|
||
if (cur_col < term_w && cur_row < term_h) {
|
||
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP,
|
||
term_y + cur_row * CHAR_H + CHAR_H - 3, 11, 2, DA_ACCENT);
|
||
}
|
||
/* 状态栏 */
|
||
char status[64];
|
||
int p = 0;
|
||
const char *s1 = "ICMP Echo | Esc 退出 ";
|
||
while (*s1) status[p++] = *s1++;
|
||
u8 hh, mm; da_rtc_time(&hh, &mm);
|
||
status[p++] = '0' + hh/10; status[p++] = '0' + hh%10;
|
||
status[p++] = ':'; status[p++] = '0' + mm/10; status[p++] = '0' + mm%10;
|
||
status[p] = 0;
|
||
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
||
}
|
||
|
||
/* ---- IP 格式校验 ---- */
|
||
static int is_valid_ip(const char *s) {
|
||
int dots = 0, digits = 0;
|
||
while (*s) {
|
||
if (*s == '.') { dots++; digits = 0; }
|
||
else if (*s >= '0' && *s <= '9') { digits++; if (digits > 3) return 0; }
|
||
else return 0;
|
||
s++;
|
||
}
|
||
return dots == 3;
|
||
}
|
||
|
||
/* ---- Ping 实现(net_stack.h) ---- */
|
||
static int g_ns_status = 0; /* 0=未尝试, 1=就绪, <0=失败 */
|
||
|
||
static int ping_net_ready(void) {
|
||
if (g_ns_status == 1) return 1;
|
||
if (g_ns_status < 0) return 0;
|
||
int rc = ns_init((u64)g_ac.kernel_api);
|
||
g_ns_status = (rc == 0) ? 1 : -1;
|
||
if (rc != 0) {
|
||
term_puts_color(" [错误] 网络初始化失败,无可用网卡或 net API\n", DA_ERROR);
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static void do_ping(const char *ip_str) {
|
||
u32 ip;
|
||
if (ns_parse_ip(ip_str, &ip) != 0) {
|
||
term_puts_color(" [错误] 无法解析 IP\n", DA_ERROR);
|
||
return;
|
||
}
|
||
if (!ping_net_ready()) return;
|
||
|
||
char ipstr[24];
|
||
ns_fmt_ip(ip, ipstr);
|
||
term_puts_color("PING ", DA_PROMPT_COLOR);
|
||
term_puts_color(ipstr, DA_ACCENT_LIGHT);
|
||
term_puts_color(" 32 data bytes\n", DA_TEXT_DIM);
|
||
|
||
int sent = 0, recv = 0;
|
||
u32 sum = 0, min = 0xFFFFFFFFu, max = 0;
|
||
for (u16 seq = 1; seq <= 4; seq++) {
|
||
u32 rtt = 0; u8 ttl = 0;
|
||
int rc = ns_ping(ip, seq, 2000, &rtt, &ttl);
|
||
sent++;
|
||
if (rc == 0) {
|
||
recv++;
|
||
sum += rtt;
|
||
if (rtt < min) min = rtt;
|
||
if (rtt > max) max = rtt;
|
||
term_puts("reply from ");
|
||
term_puts(ipstr);
|
||
term_puts(": bytes=32 time=");
|
||
char nb[12]; ns_u32_dec(nb, rtt);
|
||
term_puts(nb);
|
||
term_puts("ms TTL=");
|
||
ns_u32_dec(nb, ttl);
|
||
term_puts(nb);
|
||
term_putc('\n');
|
||
} else if (rc == -1) {
|
||
term_puts_color("ARP 解析失败,目标不可达\n", DA_ERROR);
|
||
break;
|
||
} else {
|
||
term_puts_color("request timed out\n", DA_TEXT_DIM);
|
||
}
|
||
redraw_all();
|
||
}
|
||
char nb[12];
|
||
ns_u32_dec(nb, (u32)sent);
|
||
term_puts_color("--- ", DA_TEXT_DIM);
|
||
term_puts_color(ipstr, DA_TEXT_DIM);
|
||
term_puts_color(" ping statistics ---\n ", DA_TEXT_DIM);
|
||
term_puts(nb);
|
||
term_puts(" transmitted, ");
|
||
ns_u32_dec(nb, (u32)recv);
|
||
term_puts(nb);
|
||
term_puts(" received, ");
|
||
u32 loss = sent ? (u32)(sent - recv) * 100 / (u32)sent : 100;
|
||
ns_u32_dec(nb, loss);
|
||
term_puts(nb);
|
||
term_puts("% loss");
|
||
if (recv > 0) {
|
||
term_puts(", min/avg/max = ");
|
||
ns_u32_dec(nb, min); term_puts(nb); term_puts("/");
|
||
ns_u32_dec(nb, sum / (u32)recv); term_puts(nb); term_puts("/");
|
||
ns_u32_dec(nb, max); term_puts(nb); term_puts(" ms");
|
||
}
|
||
term_putc('\n');
|
||
}
|
||
|
||
/* ---- 内置命令 ---- */
|
||
static int str_eq(const char *a, const char *b) {
|
||
while (*a && *b) { if (*a != *b) return 0; a++; b++; }
|
||
return *a == *b;
|
||
}
|
||
|
||
static void cmd_help(void) {
|
||
term_puts_color("ping — ICMP Echo 请求工具\n", DA_PROMPT_COLOR);
|
||
term_puts(" <IP> 发送 ICMP Echo Request\n");
|
||
term_puts(" help 显示此帮助\n");
|
||
term_puts(" clear 清屏\n");
|
||
term_puts(" version 显示版本\n");
|
||
term_puts(" Esc 退出\n");
|
||
}
|
||
|
||
static void cmd_clear(void) { term_clear(); }
|
||
|
||
static void cmd_version(void) {
|
||
term_puts_color("Deshab ping v1.0\n", DA_SUCCESS);
|
||
term_puts_color(" ICMP Echo via net_stack (e1000 + slirp)\n", DA_TEXT_DIM);
|
||
}
|
||
|
||
static void execute_command(const char *cmd) {
|
||
while (*cmd == ' ') cmd++;
|
||
if (*cmd == 0) return;
|
||
|
||
if (str_eq(cmd, "help") || str_eq(cmd, "?")) { cmd_help(); return; }
|
||
if (str_eq(cmd, "clear") || str_eq(cmd, "cls")) { cmd_clear(); return; }
|
||
if (str_eq(cmd, "version") || str_eq(cmd, "ver")) { cmd_version(); return; }
|
||
|
||
/* 非内置命令视为 IP 地址 */
|
||
if (is_valid_ip(cmd)) {
|
||
do_ping(cmd);
|
||
} else {
|
||
term_puts_color("无效 IP 地址: ", DA_ERROR);
|
||
term_puts_color(cmd, DA_ERROR);
|
||
term_putc('\n');
|
||
term_puts_color("请输入有效的 IPv4 地址 (如 192.168.1.1)\n", DA_TEXT_DIM);
|
||
}
|
||
}
|
||
|
||
static void add_history(const char *cmd) {
|
||
if (hist_count < HIST_SIZE) {
|
||
int i;
|
||
for (i = 0; cmd[i] && i < 255; i++) history[hist_count][i] = cmd[i];
|
||
history[hist_count][i] = 0;
|
||
hist_count++;
|
||
}
|
||
hist_idx = hist_count;
|
||
}
|
||
|
||
/* ---- 输入行重绘 ---- */
|
||
static void redraw_input_line(void) {
|
||
int plen = 0;
|
||
while (PROMPT_STR[plen]) plen++;
|
||
i64 y = term_y + (i64)cur_row * CHAR_H;
|
||
da_fill_rect(&g_ac, term_x, y, (i64)term_w * CHAR_STEP, CHAR_H, DA_BG_PRIMARY);
|
||
for (int i = 0; i < plen; i++)
|
||
da_draw_char(&g_ac, PROMPT_STR[i], term_x + i * CHAR_STEP, y, DA_PROMPT_COLOR, DA_BG_PRIMARY);
|
||
for (int i = 0; i < input_len; i++)
|
||
da_draw_char(&g_ac, input_buf[i], term_x + (plen + i) * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_PRIMARY);
|
||
cur_col = plen + input_cursor;
|
||
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP, y + CHAR_H - 3, 11, 2, DA_ACCENT);
|
||
}
|
||
|
||
/* ---- 主入口 ---- */
|
||
__attribute__((visibility("default")))
|
||
void dsk_entry(const da_boot_context *ctx) {
|
||
__asm__ volatile("cli");
|
||
da_slog("ping", "boot");
|
||
|
||
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
|
||
da_slog("ping", "bad context");
|
||
for(;;) __asm__("hlt");
|
||
}
|
||
|
||
da_init(&g_ac, ctx);
|
||
term_init_layout();
|
||
term_clear();
|
||
|
||
/* 启动 banner */
|
||
term_puts_color("=== Deshab ping v1.0 ===\n", DA_ACCENT);
|
||
term_puts_color("ICMP Echo 请求工具\n", DA_SUCCESS);
|
||
term_puts_color("输入 IP 地址发送 ping | 输入 'help' 查看帮助 | Esc 退出\n", DA_TEXT_DIM);
|
||
term_putc('\n');
|
||
|
||
redraw_all();
|
||
|
||
int shift = 0, e0 = 0;
|
||
for (;;) {
|
||
term_puts_color(PROMPT_STR, DA_PROMPT_COLOR);
|
||
input_len = 0; input_cursor = 0; input_buf[0] = 0;
|
||
int plen = 0; while (PROMPT_STR[plen]) plen++;
|
||
cur_col = plen;
|
||
redraw_all();
|
||
|
||
int cmd_done = 0;
|
||
while (!cmd_done) {
|
||
u8 st = inb(0x64);
|
||
if (!(st & 1)) { __asm__("pause"); continue; }
|
||
u8 data = inb(0x60);
|
||
if (st & 0x20) continue;
|
||
u8 sc = data;
|
||
if (sc == 0xE0) { e0 = 1; continue; }
|
||
if (sc == 0x2A || sc == 0x36) { shift = 1; continue; }
|
||
if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; }
|
||
if (sc & 0x80) { e0 = 0; continue; }
|
||
|
||
/* Esc 退出 */
|
||
if (sc == 0x01 && !e0) {
|
||
da_slog("ping", "exit");
|
||
return;
|
||
}
|
||
|
||
if (e0) {
|
||
if (sc == 0x4B) { if (input_cursor > 0) input_cursor--; }
|
||
else if (sc == 0x4D) { if (input_cursor < input_len) input_cursor++; }
|
||
else if (sc == 0x47) { input_cursor = 0; }
|
||
else if (sc == 0x4F) { input_cursor = input_len; }
|
||
else if (sc == 0x48) {
|
||
if (hist_idx > 0) {
|
||
hist_idx--;
|
||
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
|
||
input_len = i; input_cursor = i; input_buf[i] = 0;
|
||
}
|
||
}
|
||
else if (sc == 0x50) {
|
||
if (hist_idx < hist_count - 1) {
|
||
hist_idx++;
|
||
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
|
||
input_len = i; input_cursor = i; input_buf[i] = 0;
|
||
} else if (hist_idx < hist_count) {
|
||
hist_idx = hist_count;
|
||
input_len = 0; input_cursor = 0; input_buf[0] = 0;
|
||
}
|
||
}
|
||
e0 = 0;
|
||
redraw_input_line();
|
||
continue;
|
||
}
|
||
|
||
if (sc == 0x1C) { /* Enter */
|
||
input_buf[input_len] = 0;
|
||
term_putc('\n');
|
||
if (input_len > 0) add_history(input_buf);
|
||
execute_command(input_buf);
|
||
cmd_done = 1;
|
||
continue;
|
||
}
|
||
if (sc == 0x0E) { /* Backspace */
|
||
if (input_cursor > 0) {
|
||
for (int i = input_cursor - 1; i < input_len - 1; i++)
|
||
input_buf[i] = input_buf[i+1];
|
||
input_len--; input_cursor--;
|
||
input_buf[input_len] = 0;
|
||
}
|
||
redraw_input_line();
|
||
continue;
|
||
}
|
||
char c = da_scan_to_ascii(sc, shift);
|
||
if (c && c >= 32 && c <= 126 && input_len < 255) {
|
||
for (int i = input_len; i > input_cursor; i--)
|
||
input_buf[i] = input_buf[i-1];
|
||
input_buf[input_cursor++] = c;
|
||
input_len++;
|
||
redraw_input_line();
|
||
}
|
||
}
|
||
redraw_all();
|
||
}
|
||
}
|