FUCKKKK
This commit is contained in:
@@ -0,0 +1,542 @@
|
||||
# Deshab SDK 使用文档
|
||||
|
||||
## 1. 概述
|
||||
|
||||
Deshab SDK 是 Deshab 操作系统的应用与驱动统一开发工具包。在 SAS-R0 单地址空间 Ring0 模型下,应用(`.elf`)与驱动(`.drv`)共享同一套基础类型、ABI 契约和工具函数,仅链接形式不同:
|
||||
|
||||
- **应用**:PIE ELF,由 DSK 加载器加载执行,入口 `dsk_entry`。
|
||||
- **驱动**:ET_REL 可重定位对象,由 DKM loader 装载,导出 `driver_desc` / `driver_init` / `driver_exit`。
|
||||
|
||||
所有 SDK 头文件均为 `static inline` 实现,无库依赖,包含即用。
|
||||
|
||||
### 目录结构
|
||||
|
||||
```
|
||||
CODE/sdk/
|
||||
├── include/deshab/ SDK 头文件(单一包含入口)
|
||||
│ ├── deshab.h 伞头:基础层 + ABI 层 + 应用层(不含 fat32/net)
|
||||
│ ├── types.h 基础类型 u8/u16/u32/u64/i8/i16/i32/i64
|
||||
│ ├── portio.h 端口 I/O(dsb_inb/dsb_outb/inw/outw/inl/outl)
|
||||
│ ├── serial.h COM1 串口日志(dsb_slog/dsb_swrite/dsb_sputc)
|
||||
│ ├── tsc.h TSC 校准与延时(dsb_tsc_calibrate/dsb_delay_ms)
|
||||
│ ├── string.h 基础内存操作(dsb_memset/dsb_memcpy/dsb_strlen)
|
||||
│ ├── kernel_api.h dkm_kernel_api ABI 结构体(字段顺序不可变)
|
||||
│ ├── boot_context.h dsk_boot_context 启动上下文与入口契约
|
||||
│ ├── driver.h DKM 驱动描述符与状态机定义
|
||||
│ ├── block.h block provider 便捷读写层
|
||||
│ ├── app.h dsb_app_context 应用上下文初始化
|
||||
│ ├── fb.h 帧缓冲绘制原语(pixel/fill_rect/blend/rounded_rect)
|
||||
│ ├── font.h ASCII 位图字体渲染(dsb_draw_char/dsb_draw_string)
|
||||
│ ├── cursor.h 鼠标光标 save/restore/draw
|
||||
│ ├── input.h PS/2 键盘扫描码转 ASCII + 鼠标初始化与轮询
|
||||
│ ├── ui.h 色板常量 + 标题栏/状态栏装饰 + LINUXAPP.CNF 解析
|
||||
│ ├── rtc.h CMOS RTC 时分读取
|
||||
│ ├── fat32.h FAT32 读写库 + LFN 长文件名(含 256KB+4KB+256KB BSS)
|
||||
│ └── net.h 用户态网络协议栈 ARP/ICMP/DNS/TCP(含 64KB BSS)
|
||||
├── scaffold/ 脚手架模板
|
||||
│ ├── new-module.ps1 一键创建新应用/驱动模块
|
||||
│ ├── app/ 应用模板(main.c / MAKEFILE / linker.ld)
|
||||
│ └── driver/ 驱动模板(main.c)
|
||||
├── examples/ 示例代码
|
||||
│ ├── hello-app/ 最小应用示例
|
||||
│ └── hello-driver/ 最小驱动示例
|
||||
└── docs/
|
||||
└── SDK.md 本文档
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 快速开始
|
||||
|
||||
### 2.1 使用脚手架创建新模块
|
||||
|
||||
```powershell
|
||||
# 创建应用(默认输出到 CODE/tools/<Name>,产物到 SYSTEM/system/deshab64/tools/<Name>.elf)
|
||||
cd d:\Code\Deshab\CODE\sdk\scaffold
|
||||
.\new-module.ps1 -Name myapp
|
||||
|
||||
# 创建驱动(默认输出到 CODE/DKM/<Name>)
|
||||
.\new-module.ps1 -Name mydriver -Type driver -Class DKM_CLASS_NET -Stage 3
|
||||
```
|
||||
|
||||
脚本自动计算相对路径并替换模板占位符,生成 `main.c` / `MAKEFILE` / `linker.ld`(应用)或 `main.c`(驱动)。
|
||||
|
||||
可选参数:
|
||||
|
||||
| 参数 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| `-Name` | 模块名(必填) | — |
|
||||
| `-Type` | `app` 或 `driver` | `app` |
|
||||
| `-OutDir` | 输出目录 | app→`CODE/tools/$Name`,driver→`CODE/DKM/$Name` |
|
||||
| `-Class` | 驱动类别 `DKM_CLASS_*` | `DKM_CLASS_MISC` |
|
||||
| `-Stage` | 驱动加载阶段 0-3 | `3` |
|
||||
|
||||
### 2.2 构建命令
|
||||
|
||||
**应用**(需要 clang + ld.lld + make,make 需 `sh.exe` 在 PATH 中):
|
||||
|
||||
```powershell
|
||||
cd CODE\tools\myapp # 或 CODE\sdk\examples\hello-app
|
||||
make # 构建到 SYSTEM/system/deshab64/tools/myapp.elf
|
||||
make clean # 清理
|
||||
```
|
||||
|
||||
**驱动**(两步:编译 + 链接为可重定位对象):
|
||||
|
||||
```powershell
|
||||
cd CODE\DKM\mydriver
|
||||
clang -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin `
|
||||
-fno-stack-protector -fno-pic -fno-pie -mcmodel=kernel `
|
||||
-mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 `
|
||||
-Wall -Wextra -I..\sdk\include -O2 -c main.c -o mydriver.o
|
||||
ld.lld -r -o mydriver.drv mydriver.o
|
||||
```
|
||||
|
||||
> 提示:项目根目录的 `build.ps1` 会批量构建所有驱动并打包镜像,日常开发只需 `.\build.bat`。
|
||||
|
||||
---
|
||||
|
||||
## 3. 应用开发
|
||||
|
||||
### 3.1 入口契约
|
||||
|
||||
DSK 加载 `.elf` 后调用 `dsk_entry(dsk_boot_context *ctx)`。应用必须:
|
||||
|
||||
1. **校验 magic**:`ctx->magic == DSK_BOOT_MAGIC`(`0x44534B31424F4F54`,即 `"DSK1BOOT"`)。
|
||||
2. **初始化上下文**:调用 `dsb_app_init(&ac, ctx)` 提取 framebuffer、block、kernel_api。
|
||||
3. (可选)校验 `ctx->abi_version == DSK_BOOT_ABI_VERSION`。
|
||||
|
||||
```c
|
||||
#include "../../../firstInit/ascii_bitmaps.c" /* 提供字体位图数据 */
|
||||
#include "deshab/deshab.h"
|
||||
#include "deshab/font.h" /* 需先有位图数据源 */
|
||||
|
||||
void dsk_entry(const dsk_boot_context *ctx) {
|
||||
if (ctx->magic != DSK_BOOT_MAGIC) return;
|
||||
dsb_app_context ac;
|
||||
dsb_app_init(&ac, ctx);
|
||||
/* ... 你的逻辑 ... */
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 dsb_app_context
|
||||
|
||||
`dsb_app_init` 从 boot context 一次性提取应用常用数据:
|
||||
|
||||
| 字段 | 类型 | 来源 |
|
||||
|------|------|------|
|
||||
| `fb` | `u32*` | `ctx->framebuffer_address`(32bpp BGRA) |
|
||||
| `fb_w` | `u64` | `ctx->framebuffer_width` |
|
||||
| `fb_h` | `u64` | `ctx->framebuffer_height` |
|
||||
| `fb_pitch` | `u64` | `ctx->framebuffer_pitch` |
|
||||
| `block` | `dsb_block` | 从 `ctx->dkm_kernel_api + DSB_KAPI_OFF_BLOCK` 解析 |
|
||||
| `api` | `const dkm_kernel_api*` | `ctx->dkm_kernel_api` |
|
||||
|
||||
### 3.3 绘制 API
|
||||
|
||||
所有绘制函数以 `dsb_app_context*` 为首参,假设 32bpp BGRA 帧缓冲:
|
||||
|
||||
| 函数 | 说明 |
|
||||
|------|------|
|
||||
| `dsb_fill_bg(ac, color)` | 全屏填充 |
|
||||
| `dsb_fill_rect(ac, x, y, w, h, color)` | 矩形填充 |
|
||||
| `dsb_fill_rounded_rect(ac, x, y, w, h, color, radius)` | 圆角矩形填充 |
|
||||
| `dsb_rect_outline(ac, x, y, w, h, color, radius)` | 矩形描边 |
|
||||
| `dsb_pixel(ac, x, y, color)` | 单像素(自动越界保护) |
|
||||
| `dsb_pixel_read(ac, x, y)` | 读单像素 |
|
||||
| `dsb_blend(bg, fg, alpha)` | Alpha 混合(0-255) |
|
||||
| `dsb_draw_char(ac, ch, x, y, fg, bg)` | 单字符(8 级灰度抗锯齿) |
|
||||
| `dsb_draw_string(ac, s, x, y, fg, bg, step)` | 字符串(step=字间距,12 为常规) |
|
||||
| `dsb_draw_titlebar(ac, title, w)` | 标题栏(含关闭按钮 + 分隔线) |
|
||||
| `dsb_draw_statusbar(ac, text, w, h)` | 底部状态栏 |
|
||||
|
||||
**色板常量**(`ui.h`):
|
||||
|
||||
| 常量 | 值 | 用途 |
|
||||
|------|----|------|
|
||||
| `DSB_BG_PRIMARY` | `0xFF0A1428` | 主背景(深蓝) |
|
||||
| `DSB_BG_SECONDARY` | `0xFF0F1E38` | 次背景(标题栏/状态栏) |
|
||||
| `DSB_TEXT_PRIMARY` | `0xFFE8E8F0` | 主文字 |
|
||||
| `DSB_TEXT_DIM` | `0xFF6A6A88` | 暗淡文字 |
|
||||
| `DSB_ACCENT` | `0xFF00A8CC` | 强调色 |
|
||||
| `DSB_ERROR` | `0xFFFF4466` | 错误/关闭按钮 |
|
||||
| `DSB_SUCCESS` | `0xFF40C880` | 成功 |
|
||||
| `DSB_WARNING` | `0xFFF0A030` | 警告 |
|
||||
|
||||
**字体前置条件**:`font.h` 依赖 `g_ascii[95]` / `g_ascii_w` / `g_ascii_h` 三个符号,必须先 `#include "ascii_bitmaps.c"`(或自定义位图源)再包含 `font.h`。
|
||||
|
||||
### 3.4 输入 API
|
||||
|
||||
**键盘**(轮询模式,scan code set 1):
|
||||
|
||||
```c
|
||||
u8 st = dsb_inb(0x64);
|
||||
if (st & 1) {
|
||||
u8 sc = dsb_inb(0x60);
|
||||
char ch = dsb_scan_to_ascii(sc, shift_pressed);
|
||||
/* sc == 1 为 Esc,sc == 0x36/0x2A 为 Shift */
|
||||
}
|
||||
```
|
||||
|
||||
**鼠标**(PS/2 轮询模式):
|
||||
|
||||
```c
|
||||
dsb_mouse mouse;
|
||||
dsb_cursor cursor;
|
||||
dsb_mouse_init(); /* 初始化 AUX 端口 + 流模式 */
|
||||
dsb_cursor_init(&cursor, ac.fb_w, ac.fb_h);
|
||||
|
||||
/* 主循环中 */
|
||||
if (dsb_mouse_poll(&mouse, &cursor, ac.fb_w, ac.fb_h)) {
|
||||
/* cursor.mx / cursor.my / cursor.btn 已更新 */
|
||||
dsb_cursor_save(&ac, &cursor); /* 先恢复旧位置背景 */
|
||||
dsb_cursor_restore(&ac, &cursor); /* (需先 save 过) */
|
||||
dsb_cursor_draw(&ac, &cursor, DSB_CURSOR_COLOR);
|
||||
}
|
||||
```
|
||||
|
||||
> 注意:`dsb_mouse_poll` 先检查 AUX 位(`0x20`)再读数据,避免吞掉键盘扫描码。
|
||||
|
||||
### 3.5 FAT32 与网络(按需引入)
|
||||
|
||||
`fat32.h` 和 `net.h` 因含较大 static 缓冲(分别约 516KB 和 64KB BSS),**不在** `deshab.h` 伞头中默认包含,需显式 `#include`:
|
||||
|
||||
```c
|
||||
#include "deshab/deshab.h"
|
||||
#include "deshab/fat32.h" /* 需要文件读写时 */
|
||||
#include "deshab/net.h" /* 需要网络时 */
|
||||
```
|
||||
|
||||
**FAT32 用法**:
|
||||
|
||||
```c
|
||||
/* 从 block API 初始化(block_read/write 来自 kernel_api) */
|
||||
dsb_block blk;
|
||||
dsb_block_init(&blk, ctx->dkm_kernel_api);
|
||||
f32_init(blk.read, blk.write);
|
||||
|
||||
/* 读根目录文件 */
|
||||
u8 *data; u32 size;
|
||||
char name11[11];
|
||||
f32_name_to_83("CONFIG.TXT", name11);
|
||||
if (f32_read_root_file(name11, &data, &size) == 0) {
|
||||
/* data 指向 f32_data 内部缓冲,size 为字节数 */
|
||||
}
|
||||
|
||||
/* 写文件 */
|
||||
f32_write_root_file(name11, my_data, my_size);
|
||||
|
||||
/* 长路径 LFN 读取(支持子目录) */
|
||||
if (f32_read_path_lfn("system/deshab64/FUCK", &data, &size) == 0) { ... }
|
||||
```
|
||||
|
||||
**网络用法**(QEMU slirp 默认拓扑):
|
||||
|
||||
```c
|
||||
ns_init(ctx->dkm_kernel_api); /* 绑定第一个有线 tx/rx ready 设备 */
|
||||
ns_apply_lease_from_ctx((u64)ctx); /* 应用 netman DHCP 租约(若有) */
|
||||
|
||||
u32 ip;
|
||||
if (ns_dns_resolve("example.com", &ip) == 0) {
|
||||
u32 rtt; u8 ttl;
|
||||
if (ns_ping(ip, 1, 3000, &rtt, &ttl) == 0) { /* ping 成功 */ }
|
||||
}
|
||||
|
||||
/* TCP client */
|
||||
if (ns_tcp_connect(ip, 80, 5000) == 0) {
|
||||
ns_tcp_send((const u8*)"GET / HTTP/1.0\r\n\r\n", 18);
|
||||
u8 buf[4096];
|
||||
int n = ns_tcp_recv(buf, sizeof(buf), 5000);
|
||||
ns_tcp_close();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 驱动开发
|
||||
|
||||
### 4.1 三符号契约
|
||||
|
||||
DKM 驱动为 ET_REL 可重定位对象(`.drv`),必须导出三个符号:
|
||||
|
||||
| 符号 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `driver_desc` | `const dkm_driver_desc` | 描述符(magic/类别/阶段/依赖/提供) |
|
||||
| `driver_init` | `int (*)(const dkm_kernel_api *api, dkm_driver_handle *handle)` | 初始化,返回 0 成功 |
|
||||
| `driver_exit` | `int (*)(dkm_driver_handle *handle)` | 卸载清理,返回 0 成功 |
|
||||
|
||||
三个符号均需 `__attribute__((visibility("default")))` 以确保不被隐藏。
|
||||
|
||||
### 4.2 driver_desc 字段
|
||||
|
||||
```c
|
||||
const dkm_driver_desc driver_desc = {
|
||||
.magic = DKM_DRIVER_MAGIC, /* 0x444B4D31 "DKM1",必须 */
|
||||
.abi_version = DKM_ABI_VERSION, /* 1 */
|
||||
.desc_size = sizeof(dkm_driver_desc),
|
||||
.name = "hello",
|
||||
.version = "1.0",
|
||||
.vendor = "Deshab",
|
||||
.driver_class = DKM_CLASS_MISC, /* 类别,见下表 */
|
||||
.stage = 3, /* 加载阶段 0-3 */
|
||||
.flags = 0, /* DKM_F_* 标志 */
|
||||
.priority = 100,
|
||||
.depends = g_depends, /* 依赖的 provides 名 */
|
||||
.depends_count = 0,
|
||||
.provides = g_provides, /* 本驱动提供的能力名 */
|
||||
.provides_count = 1,
|
||||
.min_kernel_abi = DKM_KERNEL_API_VERSION,
|
||||
.feature_bits = 0,
|
||||
};
|
||||
```
|
||||
|
||||
**驱动类别**(`driver.h`):
|
||||
|
||||
| 常量 | 值 | 说明 |
|
||||
|------|----|------|
|
||||
| `DKM_CLASS_PLATFORM` | 1 | 平台发现 |
|
||||
| `DKM_CLASS_BUS` | 2 | 总线(PCI) |
|
||||
| `DKM_CLASS_INTERRUPT` | 3 | 中断控制器 |
|
||||
| `DKM_CLASS_TIMER` | 4 | 计时器 |
|
||||
| `DKM_CLASS_CONSOLE` | 5 | 控制台/帧缓冲 |
|
||||
| `DKM_CLASS_STORAGE` | 6 | 存储(AHCI/NVMe) |
|
||||
| `DKM_CLASS_FS` | 7 | 文件系统 |
|
||||
| `DKM_CLASS_NET` | 8 | 网络 |
|
||||
| `DKM_CLASS_INPUT` | 9 | 输入(键鼠) |
|
||||
| `DKM_CLASS_GPU` | 10 | GPU |
|
||||
| `DKM_CLASS_MISC` | 11 | 杂项 |
|
||||
|
||||
**驱动标志**(`flags`):
|
||||
|
||||
| 常量 | 说明 |
|
||||
|------|------|
|
||||
| `DKM_F_REQUIRED` | 当前 stage 失败则 DRR recovery 或 panic |
|
||||
| `DKM_F_BOOT_MODULE` | 可由 bootloader 预加载 |
|
||||
| `DKM_F_NO_UNLOAD` | 不允许卸载 |
|
||||
| `DKM_F_STRONG_RECOVERY` | data 段使用 UTSM 强恢复策略 |
|
||||
| `DKM_F_DMA_REQUIRED` | 需要 DMA API |
|
||||
| `DKM_F_EARLY_LOG` | init 期间允许 early log |
|
||||
|
||||
### 4.3 driver_init / driver_exit
|
||||
|
||||
```c
|
||||
int driver_init(const dkm_kernel_api *api, dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
/* 校验 kernel_api 可用性 */
|
||||
if (!api || !api->log) return -1; /* 返回负数=失败 */
|
||||
|
||||
/* 通过 kernel_api 调用内核服务,不直接依赖内核符号 */
|
||||
api->log->info("[hello] init");
|
||||
|
||||
/* 注册 IRQ、映射 MMIO、注册 block/net 设备等 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int driver_exit(dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
/* 清理资源 */
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
> `required` 驱动 init 失败会触发 DRR recovery 或 panic;非 required 驱动失败仅标记为 `DKM_STATE_FAILED` 并继续。
|
||||
|
||||
### 4.4 dkm_kernel_api 使用
|
||||
|
||||
驱动通过 `dkm_kernel_api` 调用内核服务,**不直接依赖任意内核符号**。主要能力:
|
||||
|
||||
| 字段 | 类型 | 用途 |
|
||||
|------|------|------|
|
||||
| `log` | `const dkm_log_api*` | 日志(info/warn/error/panic) |
|
||||
| `dma` | `const dkm_dma_api*` | DMA 缓冲分配 |
|
||||
| `net` | `const dkm_net_api*` | 网络设备注册/收发 |
|
||||
| `block` | `const dkm_block_api*` | block 设备注册/读写 |
|
||||
| `mmio` | `const dkm_mmio_api*` | MMIO 映射查询 |
|
||||
| `mm_map_mmio` | `fn` | MMIO 独立窗口映射(PCD\|PWT) |
|
||||
| `mm_unmap_mmio` | `fn` | 解除 MMIO 窗口映射 |
|
||||
| `irq_register` | `fn` | 注册 IRQ handler |
|
||||
| `register_apic_eoi` | `fn` | 注册 LAPIC EOI 钩子 |
|
||||
| `irq_vector_alloc/free` | `fn` | 动态 IDT 向量分配(MSI/MSI-X) |
|
||||
| `rsdp_address` | `const void*` | ACPI RSDP 物理地址 |
|
||||
| `fb_address` / `fb_*` | — | 帧缓冲信息 |
|
||||
| `hhdm_offset` | `u64` | 物理→虚拟偏移 |
|
||||
| `boot_modules_response` | `const void*` | Limine boot module 响应 |
|
||||
|
||||
### 4.5 manifest 注册
|
||||
|
||||
驱动构建为 `.drv` 后,需在 `SYSTEM/driver/manifest.json` 注册:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "hello",
|
||||
"path": "driver/misc/hello.drv",
|
||||
"class": "misc",
|
||||
"required": false,
|
||||
"depends": [],
|
||||
"provides": ["hello"]
|
||||
}
|
||||
```
|
||||
|
||||
条目放在对应 stage 的 `drivers` 数组中(stage 0-3)。`required: true` 的驱动 init 失败会触发 DRR recovery 或 panic。
|
||||
|
||||
如需 bootloader 预加载(stage0/stage1),还需在 `SYSTEM/boot/limine.conf` 添加 `module_path`。
|
||||
|
||||
---
|
||||
|
||||
## 5. 头文件索引
|
||||
|
||||
### 基础层
|
||||
|
||||
| 头文件 | 用途 | 关键 API |
|
||||
|--------|------|----------|
|
||||
| `types.h` | 基础类型 | `u8/u16/u32/u64/i8/i16/i32/i64/NULL` |
|
||||
| `portio.h` | 端口 I/O | `dsb_inb/dsb_outb/dsb_inw/dsb_outw/dsb_inl/dsb_outl` |
|
||||
| `serial.h` | COM1 串口日志 | `dsb_slog(tag,msg)` / `dsb_swrite(s)` / `dsb_sputc(c)` |
|
||||
| `tsc.h` | TSC 校准与延时 | `dsb_tsc_calibrate()` / `dsb_rdtsc()` / `dsb_delay_ms/us()` |
|
||||
| `string.h` | 基础内存操作 | `dsb_memset/dsb_memcpy/dsb_strlen` |
|
||||
|
||||
### ABI 层
|
||||
|
||||
| 头文件 | 用途 | 关键 API |
|
||||
|--------|------|----------|
|
||||
| `kernel_api.h` | kernel_api ABI 结构体 | `dkm_kernel_api` / `dkm_log_api` / `dkm_dma_api` / `dkm_net_api` / `dkm_block_api` / `dkm_mmio_api` |
|
||||
| `boot_context.h` | 启动上下文 | `dsk_boot_context` / `DSK_BOOT_MAGIC` / `dsk_entry_fn` / `probe_info` / `net_lease_info` |
|
||||
| `driver.h` | 驱动描述符 | `dkm_driver_desc` / `DKM_DRIVER_MAGIC` / `DKM_CLASS_*` / `DKM_F_*` / `dkm_driver_handle` |
|
||||
| `block.h` | block 便捷层 | `dsb_block` / `dsb_block_init()` / `dsb_block_read/write()` |
|
||||
|
||||
### 应用层
|
||||
|
||||
| 头文件 | 用途 | 关键 API |
|
||||
|--------|------|----------|
|
||||
| `app.h` | 应用上下文 | `dsb_app_context` / `dsb_app_init()` |
|
||||
| `fb.h` | 帧缓冲绘制 | `dsb_pixel/fill_rect/fill_bg/fill_rounded_rect/rect_outline/blend` |
|
||||
| `font.h` | 字体渲染 | `dsb_draw_char/dsb_draw_string`(需先提供 `g_ascii` 位图源) |
|
||||
| `cursor.h` | 鼠标光标 | `dsb_cursor` / `dsb_cursor_init/save/restore/draw` |
|
||||
| `input.h` | 键鼠输入 | `dsb_scan_to_ascii()` / `dsb_mouse` / `dsb_mouse_init/poll()` |
|
||||
| `ui.h` | 色板与装饰 | `DSB_BG_PRIMARY` 等色板 / `dsb_draw_titlebar/statusbar` / `dsb_linuxapp_parse()` |
|
||||
| `rtc.h` | RTC 时钟 | `dsb_rtc_time(h, m)` |
|
||||
|
||||
### 领域层(按需包含,含大 static 缓冲)
|
||||
|
||||
| 头文件 | 用途 | BSS 占用 | 关键 API |
|
||||
|--------|------|----------|----------|
|
||||
| `fat32.h` | FAT32 读写 + LFN | ~516KB | `f32_init/read_root_file/write_root_file/delete_root_file/list_root/list_dir/read_path_lfn/write_path_lfn` |
|
||||
| `net.h` | 网络协议栈 | ~64KB | `ns_init/ping/dns_resolve/tcp_connect/tcp_send/tcp_recv/tcp_close` |
|
||||
|
||||
### 伞头
|
||||
|
||||
| 头文件 | 包含范围 |
|
||||
|--------|----------|
|
||||
| `deshab.h` | 基础层 + ABI 层 + 应用层(不含 `fat32.h` / `net.h` / `font.h`) |
|
||||
|
||||
> `font.h` 需先提供位图数据源(`#include "ascii_bitmaps.c"`),故不在伞头中默认包含。
|
||||
|
||||
---
|
||||
|
||||
## 6. ABI 约束
|
||||
|
||||
### 6.1 kernel_api 布局不可变
|
||||
|
||||
`dkm_kernel_api` 结构体的**字段顺序和类型不可调整**。UTSM 端按固定偏移填充各 API 指针,DKM loader 和 DSK 按相同偏移读取。修改字段顺序会破坏所有已编译的驱动和应用。
|
||||
|
||||
### 6.2 字段只追加
|
||||
|
||||
新增能力只允许在结构体**末尾追加**字段(尾部追加保持 ABI 兼容)。旧驱动访问不到新字段不受影响;新驱动可通过 `api->size` 判断字段是否存在。
|
||||
|
||||
### 6.3 偏移常量
|
||||
|
||||
供不便通过结构体解引用的场景(如早期 boot context 解析、汇编代码)使用,消除散落的硬编码偏移:
|
||||
|
||||
| 常量 | 值 | 对应字段 |
|
||||
|------|----|----------|
|
||||
| `DSB_KAPI_OFF_NET` | `0x48` | `const dkm_net_api *net` |
|
||||
| `DSB_KAPI_OFF_BLOCK` | `0xA8` | `const dkm_block_api *block` |
|
||||
|
||||
常规代码应直接通过 `api->net` / `api->block` 访问,仅在早期启动或指针不可解引用时使用偏移常量。
|
||||
|
||||
### 6.4 dsk_boot_context 同理
|
||||
|
||||
`dsk_boot_context` 的 `reserved[8]` 槽位有约定索引:
|
||||
|
||||
| 索引 | 常量 | 用途 |
|
||||
|------|------|------|
|
||||
| 4 | `DSB_BOOT_RESERVED_PE_SERVICE` | PE/EXE 兼容层服务指针 |
|
||||
| 5 | `DSB_BOOT_RESERVED_LINUX_COMPAT` | Linux 兼容层服务指针 |
|
||||
| 6 | `DSB_BOOT_RESERVED_PROBE_INFO` | 插桩 probe 缓冲指针 |
|
||||
| 7 | `DSB_BOOT_RESERVED_NET_LEASE` | netman 网络租约指针 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 构建参数速查
|
||||
|
||||
### 7.1 应用(PIE ELF)
|
||||
|
||||
应用编译为 PIE ELF,由 DSK 的 PIE ELF loader 加载(支持 `R_X86_64_RELATIVE` 重定位)。
|
||||
|
||||
**clang 编译参数**:
|
||||
|
||||
```
|
||||
-target x86_64-unknown-none
|
||||
-std=c11
|
||||
-ffreestanding
|
||||
-fno-builtin
|
||||
-fno-stack-protector
|
||||
-fpie # 位置无关可执行文件
|
||||
-mno-red-zone
|
||||
-mno-sse -mno-sse2 -mno-mmx -msoft-float # 未启用 FPU/SSE 前必须
|
||||
-m64
|
||||
-Wall -Wextra
|
||||
-I<sdk/include> # SDK 头文件路径
|
||||
-O2
|
||||
```
|
||||
|
||||
**ld.lld 链接参数**:
|
||||
|
||||
```
|
||||
-nostdlib
|
||||
-static
|
||||
-pie
|
||||
-z max-page-size=0x1000
|
||||
-T linker.ld # PIE ELF 布局,ENTRY(dsk_entry)
|
||||
```
|
||||
|
||||
**linker.ld 布局**:text(PF_R\|PF_X)→ rodata(PF_R)→ data(PF_R\|PF_W)→ .rela.dyn → .dynamic → .bss(NOBITS)。
|
||||
|
||||
### 7.2 驱动(ET_REL)
|
||||
|
||||
驱动编译为可重定位对象(ET_REL),由 DKM loader 装载并做 ELF64 relocation(`R_X86_64_64/32/32S/PC32`)。
|
||||
|
||||
**clang 编译参数**(与应用的差异):
|
||||
|
||||
```
|
||||
-fno-pic -fno-pie # 不可重定位代码(非 PIE)
|
||||
-mcmodel=kernel # 内核代码模型
|
||||
# 无 -fpie,无 linker.ld,无链接步骤
|
||||
```
|
||||
|
||||
其余参数(`-target -std -ffreestanding -fno-builtin -mno-red-zone -mno-sse ...`)与应用一致。
|
||||
|
||||
**ld.lld 链接参数**:
|
||||
|
||||
```
|
||||
-r # 合并为单个可重定位对象(不生成可执行文件)
|
||||
-o <name>.drv
|
||||
```
|
||||
|
||||
### 7.3 参数差异对照
|
||||
|
||||
| 参数 | 应用 | 驱动 |
|
||||
|------|------|------|
|
||||
| 位置无关 | `-fpie` | `-fno-pic -fno-pie` |
|
||||
| 代码模型 | (默认 small) | `-mcmodel=kernel` |
|
||||
| 链接 | `-nostdlib -static -pie -T linker.ld` | `-r`(仅合并) |
|
||||
| 产物 | PIE ELF(`.elf`) | ET_REL(`.drv`) |
|
||||
| 入口 | `dsk_entry` | `driver_desc` / `driver_init` / `driver_exit` |
|
||||
| 重定位 | `R_X86_64_RELATIVE`(加载时) | `R_X86_64_64/32/32S/PC32`(装载时) |
|
||||
|
||||
### 7.4 通用约束
|
||||
|
||||
- **进入 C 前必须 `cli/cld`**(由 loader 保证)。
|
||||
- **未启用 FPU/SSE 前编译必须** `-mno-sse -mno-sse2 -mno-mmx -msoft-float`。
|
||||
- **DATA/BSS 段使用 `PF_R|PF_W|PF_X`**(SAS-R0 下驱动模块内存需可执行)。
|
||||
- **无 libc**:所有内存操作使用 `dsb_memset/dsb_memcpy/dsb_strlen`,不可调用标准库函数。
|
||||
@@ -0,0 +1,25 @@
|
||||
CC := clang
|
||||
LD := ld.lld
|
||||
|
||||
ROOT := ../../../..
|
||||
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/hello.elf
|
||||
|
||||
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I../../include -O2
|
||||
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
|
||||
|
||||
C_SOURCES := main.c
|
||||
OBJECTS := $(C_SOURCES:.c=.o)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(OUT)
|
||||
|
||||
$(OUT): $(OBJECTS) linker.ld
|
||||
@mkdir -p "$(dir $(OUT))"
|
||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
|
||||
Binary file not shown.
@@ -0,0 +1,26 @@
|
||||
ENTRY(dsk_entry)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(5);
|
||||
rodata PT_LOAD FLAGS(4);
|
||||
data PT_LOAD FLAGS(6);
|
||||
dynamic PT_DYNAMIC FLAGS(4);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x1000;
|
||||
.text : ALIGN(4K) { *(.text*) } :text
|
||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
||||
.data : ALIGN(4K) {
|
||||
*(.data*)
|
||||
*(.data.rel.ro*)
|
||||
*(COMMON)
|
||||
} :data
|
||||
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
|
||||
.dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic
|
||||
.bss : ALIGN(4K) {
|
||||
*(.bss*)
|
||||
} :data
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/* hello-app - Deshab SDK 最小应用示例
|
||||
*
|
||||
* 展示完整的应用入口契约:校验 magic -> 初始化上下文 -> 绘制 UI -> 等待退出。
|
||||
* 构建产物:SYSTEM/system/deshab64/tools/hello.elf
|
||||
*/
|
||||
#include "../../../firstInit/ascii_bitmaps.c"
|
||||
#include "deshab/deshab.h"
|
||||
#include "deshab/font.h"
|
||||
|
||||
static dsb_app_context g_ac;
|
||||
|
||||
void dsk_entry(const dsk_boot_context *ctx) {
|
||||
/* 1. 校验启动上下文 magic */
|
||||
if (ctx->magic != DSK_BOOT_MAGIC) return;
|
||||
|
||||
/* 2. 初始化应用上下文(framebuffer + block + kernel_api) */
|
||||
dsb_app_init(&g_ac, ctx);
|
||||
|
||||
/* 3. 清屏 */
|
||||
dsb_fill_bg(&g_ac, DSB_BG_PRIMARY);
|
||||
|
||||
/* 4. 标题栏 + 状态栏 */
|
||||
dsb_draw_titlebar(&g_ac, "HelloApp", (i64)g_ac.fb_w);
|
||||
dsb_draw_statusbar(&g_ac, "Esc to exit", (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
||||
|
||||
/* 5. 居中显示欢迎文字 */
|
||||
const char *msg = "Hello, Deshab!";
|
||||
i64 msg_w = (i64)dsb_strlen(msg) * 12;
|
||||
i64 x = ((i64)g_ac.fb_w - msg_w) / 2;
|
||||
i64 y = ((i64)g_ac.fb_h) / 2;
|
||||
dsb_draw_string(&g_ac, msg, x, y, DSB_TEXT_PRIMARY, DSB_BG_PRIMARY, 12);
|
||||
|
||||
/* 6. 等待 Esc 键退出 */
|
||||
for (;;) {
|
||||
u8 st = dsb_inb(0x64);
|
||||
if (st & 1) {
|
||||
u8 sc = dsb_inb(0x60);
|
||||
if (sc == 1) break; /* Esc make code */
|
||||
}
|
||||
__asm__ volatile("pause");
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,50 @@
|
||||
/* hello-driver - Deshab SDK 最小驱动示例
|
||||
*
|
||||
* 展示 DKM 驱动三符号契约:driver_desc + driver_init + driver_exit。
|
||||
* 构建方式:clang -c main.c -o hello.o && ld.lld -r -o hello.drv hello.o
|
||||
* 注册方式:在 SYSTEM/driver/manifest.json 添加条目
|
||||
*/
|
||||
#include "deshab/driver.h"
|
||||
#include "deshab/serial.h"
|
||||
|
||||
static const dkm_log_api *g_log = 0;
|
||||
|
||||
static const char *const g_depends[] = { 0 };
|
||||
static const char *const g_provides[] = { "hello" };
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
const dkm_driver_desc driver_desc = {
|
||||
.magic = DKM_DRIVER_MAGIC,
|
||||
.abi_version = DKM_ABI_VERSION,
|
||||
.desc_size = sizeof(dkm_driver_desc),
|
||||
.name = "hello",
|
||||
.version = "1.0",
|
||||
.vendor = "Deshab",
|
||||
.driver_class = DKM_CLASS_MISC,
|
||||
.stage = 3,
|
||||
.flags = 0,
|
||||
.priority = 100,
|
||||
.depends = g_depends,
|
||||
.depends_count = 0,
|
||||
.provides = g_provides,
|
||||
.provides_count = 1,
|
||||
.min_kernel_abi = DKM_KERNEL_API_VERSION,
|
||||
.feature_bits = 0,
|
||||
};
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const dkm_kernel_api *api, dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
if (!api || !api->log) return -1;
|
||||
g_log = api->log;
|
||||
g_log->info("[hello] init");
|
||||
g_log->info("[hello] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
if (g_log) g_log->info("[hello] exit");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/* deshab/app.h - 应用上下文与初始化
|
||||
*
|
||||
* 整合 desktop_app.h 的 da_app_context / da_init,提供从 dsk_boot_context
|
||||
* 提取常用数据(framebuffer + block + kernel_api)的便捷封装。
|
||||
*
|
||||
* 应用入口模式:
|
||||
* void dsk_entry(const dsk_boot_context *ctx) {
|
||||
* dsb_app_context ac;
|
||||
* dsb_app_init(&ac, ctx);
|
||||
* // 使用 ac.fb / ac.block / ac.api ...
|
||||
* }
|
||||
*/
|
||||
#ifndef DESHAB_APP_H
|
||||
#define DESHAB_APP_H
|
||||
|
||||
#include "types.h"
|
||||
#include "boot_context.h"
|
||||
#include "kernel_api.h"
|
||||
#include "block.h"
|
||||
|
||||
typedef struct dsb_app_context {
|
||||
u32 *fb; /* framebuffer 基址(32bpp BGRA) */
|
||||
u64 fb_w; /* 宽度(像素) */
|
||||
u64 fb_h; /* 高度(像素) */
|
||||
u64 fb_pitch; /* 行跨度(字节) */
|
||||
dsb_block block; /* block 便捷读写 */
|
||||
const dkm_kernel_api *api; /* kernel_api 指针 */
|
||||
} dsb_app_context;
|
||||
|
||||
/* 从 dsk_boot_context 初始化 dsb_app_context。
|
||||
* block API 从 kernel_api + DSB_KAPI_OFF_BLOCK 获取(封装在 dsb_block_init)。 */
|
||||
static inline void dsb_app_init(dsb_app_context *ac, const dsk_boot_context *ctx) {
|
||||
ac->fb = (u32 *)ctx->framebuffer_address;
|
||||
ac->fb_w = ctx->framebuffer_width;
|
||||
ac->fb_h = ctx->framebuffer_height;
|
||||
ac->fb_pitch = ctx->framebuffer_pitch;
|
||||
ac->api = (const dkm_kernel_api *)ctx->dkm_kernel_api;
|
||||
dsb_block_init(&ac->block, ctx->dkm_kernel_api);
|
||||
}
|
||||
|
||||
#endif /* DESHAB_APP_H */
|
||||
@@ -0,0 +1,53 @@
|
||||
/* deshab/block.h - block provider 便捷访问层
|
||||
*
|
||||
* 封装从 dkm_kernel_api 获取 block API 并调用 read/write 的样板代码,
|
||||
* 消除散落在各应用的硬编码偏移(+0xA8 / +0x10 / +0x18)。
|
||||
*
|
||||
* 用法:
|
||||
* dsb_block blk;
|
||||
* if (dsb_block_init(&blk, ctx->dkm_kernel_api) == 0) {
|
||||
* dsb_block_read(&blk, 0, 256, buf);
|
||||
* dsb_block_write(&blk, 0, 1, buf);
|
||||
* }
|
||||
*/
|
||||
#ifndef DESHAB_BLOCK_H
|
||||
#define DESHAB_BLOCK_H
|
||||
|
||||
#include "types.h"
|
||||
#include "kernel_api.h"
|
||||
|
||||
typedef struct dsb_block {
|
||||
const dkm_block_api *api;
|
||||
/* 便捷函数指针(从 block_api 解析)。index 固定 0,与现有应用一致。 */
|
||||
int (*read)(u32 index, u64 lba, u32 count, void *buffer);
|
||||
int (*write)(u32 index, u64 lba, u32 count, const void *buffer);
|
||||
} dsb_block;
|
||||
|
||||
/* 从 kernel_api 指针初始化 block 便捷结构。
|
||||
* kernel_api_u64 为 dsk_boot_context.dkm_kernel_api 的值(u64 指针)。
|
||||
* 返回 0 成功,-1 无 block API。 */
|
||||
static inline int dsb_block_init(dsb_block *b, u64 kernel_api_u64) {
|
||||
b->api = 0;
|
||||
b->read = 0;
|
||||
b->write = 0;
|
||||
if (!kernel_api_u64) return -1;
|
||||
/* block API 指针位于 kernel_api + DSB_KAPI_OFF_BLOCK */
|
||||
const dkm_block_api *blk_api = *(const dkm_block_api **)(kernel_api_u64 + DSB_KAPI_OFF_BLOCK);
|
||||
if (!blk_api) return -1;
|
||||
b->api = blk_api;
|
||||
/* read @ +0x10, write @ +0x18(dkm_block_api 结构内偏移) */
|
||||
b->read = (int (*)(u32, u64, u32, void *))(*(u64 *)((u64)blk_api + 0x10));
|
||||
b->write = (int (*)(u32, u64, u32, const void *))(*(u64 *)((u64)blk_api + 0x18));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 便捷读写(index 固定 0,与现有应用一致)。返回 0 成功。 */
|
||||
static inline int dsb_block_read(dsb_block *b, u64 lba, u32 count, void *buf) {
|
||||
return b->read ? b->read(0, lba, count, buf) : -1;
|
||||
}
|
||||
|
||||
static inline int dsb_block_write(dsb_block *b, u64 lba, u32 count, const void *buf) {
|
||||
return b->write ? b->write(0, lba, count, buf) : -1;
|
||||
}
|
||||
|
||||
#endif /* DESHAB_BLOCK_H */
|
||||
@@ -0,0 +1,99 @@
|
||||
/* deshab/boot_context.h - DSK 启动上下文与入口契约
|
||||
*
|
||||
* 整合 CODE/UTSM/include/utsm/dsk.h,为应用提供自包含的 boot context 定义。
|
||||
* DSK 加载 .elf 应用时,通过 dsk_entry(dsk_boot_context *ctx) 传递平台信息。
|
||||
*
|
||||
* reserved 槽位约定(消除散落的硬编码索引):
|
||||
* [4] pe_service PE/EXE 兼容层服务指针
|
||||
* [5] linux_compat_service Linux 兼容层服务指针
|
||||
* [6] probe_info 插桩 probe 缓冲指针
|
||||
* [7] net_lease_info netman 网络租约指针
|
||||
*/
|
||||
#ifndef DESHAB_BOOT_CONTEXT_H
|
||||
#define DESHAB_BOOT_CONTEXT_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define DSK_BOOT_MAGIC 0x44534B31424F4F54ULL /* "DSK1BOOT" */
|
||||
#define DSK_BOOT_ABI_VERSION 1u
|
||||
|
||||
#define DSK_BOOT_FLAG_FROM_UTSM (1ULL << 0)
|
||||
#define DSK_BOOT_FLAG_DKM_READY (1ULL << 1)
|
||||
#define DSK_BOOT_FLAG_FAT32_PATH (1ULL << 2)
|
||||
#define DSK_BOOT_FLAG_PROBE_INFO (1ULL << 3)
|
||||
|
||||
/* ---- reserved 槽位索引(dsk_boot_context.reserved[]) ---- */
|
||||
#define DSB_BOOT_RESERVED_PE_SERVICE 4u
|
||||
#define DSB_BOOT_RESERVED_LINUX_COMPAT 5u
|
||||
#define DSB_BOOT_RESERVED_PROBE_INFO 6u
|
||||
#define DSB_BOOT_RESERVED_NET_LEASE 7u
|
||||
|
||||
/* ---- Probe 信息结构(通过 reserved[6] 传递) ---- */
|
||||
#define PROBE_INFO_MAGIC 0x50524F42u /* "PRFB" */
|
||||
|
||||
typedef struct probe_info {
|
||||
u32 magic; /* PROBE_INFO_MAGIC */
|
||||
u32 version; /* ABI 版本,当前 1 */
|
||||
u64 probe_buf_addr; /* probe_entry 缓冲基地址 */
|
||||
u32 probe_cap; /* 缓冲容量(条目数) */
|
||||
u32 probe_head; /* 当前写入位置(递增取模) */
|
||||
u32 probe_count; /* 已写入总条目数 */
|
||||
u32 probe_dropped; /* 因缓冲满而覆盖的条目数 */
|
||||
u32 instr_enabled; /* g_instr_enabled 当前值 */
|
||||
u32 instr_probe_enable; /* g_instr_probe_enable 当前值 */
|
||||
u64 reserved[4];
|
||||
} probe_info;
|
||||
|
||||
/* ---- 网络租约信息(netman 写入,经 reserved[7] 指针传递) ---- */
|
||||
#define NET_LEASE_MAGIC 0x4E45544Cu /* "NETL" */
|
||||
#define NET_LEASE_F_VALID (1u << 0) /* ip/gateway/dns 有效 */
|
||||
#define NET_LEASE_F_STATIC (1u << 1) /* 静态配置(非 DHCP) */
|
||||
|
||||
typedef struct net_lease_info {
|
||||
u32 magic; /* NET_LEASE_MAGIC */
|
||||
u32 flags; /* NET_LEASE_F_* */
|
||||
u32 ip; /* guest IP(网络序) */
|
||||
u32 gateway; /* 网关(网络序) */
|
||||
u32 dns; /* DNS(网络序) */
|
||||
u32 server; /* DHCP server(网络序,调试) */
|
||||
u32 reserved[2];
|
||||
} net_lease_info;
|
||||
|
||||
typedef struct dsk_boot_context {
|
||||
u64 magic;
|
||||
u32 abi_version;
|
||||
u32 size;
|
||||
|
||||
u64 flags;
|
||||
|
||||
u64 hhdm_offset;
|
||||
u64 rsdp_address;
|
||||
|
||||
u64 framebuffer_address;
|
||||
u64 framebuffer_width;
|
||||
u64 framebuffer_height;
|
||||
u64 framebuffer_pitch;
|
||||
u32 framebuffer_bpp;
|
||||
u32 reserved_fb;
|
||||
|
||||
u64 boot_modules_response;
|
||||
|
||||
u64 dkm_kernel_api;
|
||||
u64 dkm_driver_table;
|
||||
u64 dkm_driver_count;
|
||||
|
||||
u64 utsm_state;
|
||||
u64 drr_state;
|
||||
|
||||
u64 memory_map;
|
||||
u64 memory_map_count;
|
||||
u64 memory_map_entry_size;
|
||||
|
||||
u64 kernel_stack_top;
|
||||
u64 reserved[8];
|
||||
} dsk_boot_context;
|
||||
|
||||
/* 应用入口点签名。DSK 加载 .elf 后调用 dsk_entry(context)。 */
|
||||
typedef void (*dsk_entry_fn)(const dsk_boot_context *context);
|
||||
|
||||
#endif /* DESHAB_BOOT_CONTEXT_H */
|
||||
@@ -0,0 +1,88 @@
|
||||
/* deshab/cursor.h - 鼠标光标管理
|
||||
*
|
||||
* 整合 desktop_app.h 的 da_cursor 结构与 save/restore/draw 操作,统一前缀为 dsb_。
|
||||
* 采用 save/restore 模式实现光标移动:绘制前保存背景,移动后恢复。
|
||||
*/
|
||||
#ifndef DESHAB_CURSOR_H
|
||||
#define DESHAB_CURSOR_H
|
||||
|
||||
#include "types.h"
|
||||
#include "app.h"
|
||||
#include "fb.h"
|
||||
|
||||
#define DSB_CURSOR_SIZE 24
|
||||
|
||||
static const u8 dsb_cursor_shape[24][24] = {
|
||||
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
};
|
||||
|
||||
typedef struct dsb_cursor {
|
||||
u32 save[DSB_CURSOR_SIZE * DSB_CURSOR_SIZE];
|
||||
int saved;
|
||||
int old_x, old_y;
|
||||
int mx, my;
|
||||
int btn;
|
||||
} dsb_cursor;
|
||||
|
||||
static inline void dsb_cursor_init(dsb_cursor *c, i64 fb_w, i64 fb_h) {
|
||||
for (int i = 0; i < DSB_CURSOR_SIZE * DSB_CURSOR_SIZE; i++) c->save[i] = 0;
|
||||
c->saved = 0;
|
||||
c->old_x = -1;
|
||||
c->old_y = -1;
|
||||
c->mx = (int)(fb_w / 2);
|
||||
c->my = (int)(fb_h / 2);
|
||||
c->btn = 0;
|
||||
}
|
||||
|
||||
static inline void dsb_cursor_save(dsb_app_context *ac, dsb_cursor *c) {
|
||||
for (int r = 0; r < DSB_CURSOR_SIZE; r++)
|
||||
for (int col = 0; col < DSB_CURSOR_SIZE; col++) {
|
||||
int x = c->mx + col, y = c->my + r;
|
||||
c->save[r * DSB_CURSOR_SIZE + col] = dsb_pixel_read(ac, x, y);
|
||||
}
|
||||
c->saved = 1;
|
||||
c->old_x = c->mx;
|
||||
c->old_y = c->my;
|
||||
}
|
||||
|
||||
static inline void dsb_cursor_restore(dsb_app_context *ac, dsb_cursor *c) {
|
||||
if (!c->saved) return;
|
||||
for (int r = 0; r < DSB_CURSOR_SIZE; r++)
|
||||
for (int col = 0; col < DSB_CURSOR_SIZE; col++) {
|
||||
int x = c->old_x + col, y = c->old_y + r;
|
||||
dsb_pixel(ac, x, y, c->save[r * DSB_CURSOR_SIZE + col]);
|
||||
}
|
||||
c->saved = 0;
|
||||
}
|
||||
|
||||
static inline void dsb_cursor_draw(dsb_app_context *ac, dsb_cursor *c, u32 color) {
|
||||
for (int r = 0; r < DSB_CURSOR_SIZE; r++)
|
||||
for (int col = 0; col < DSB_CURSOR_SIZE; col++)
|
||||
if (dsb_cursor_shape[r][col])
|
||||
dsb_pixel(ac, c->mx + col, c->my + r, color);
|
||||
}
|
||||
|
||||
#endif /* DESHAB_CURSOR_H */
|
||||
@@ -0,0 +1,48 @@
|
||||
/* deshab.h - Deshab SDK 伞头文件
|
||||
*
|
||||
* 一站式包含 SDK 基础层 + ABI 层 + 应用层轻量头文件。
|
||||
* fat32.h 与 net.h 因含较大 static 缓冲(256KB/64KB BSS),
|
||||
* 不在此默认包含,按需 #include "deshab/fat32.h" 或 "deshab/net.h"。
|
||||
*
|
||||
* 用法:
|
||||
* #include "deshab/deshab.h" // 基础+ABI+应用层
|
||||
* #include "deshab/fat32.h" // 需要 FAT32 读写时
|
||||
* #include "deshab/net.h" // 需要网络协议栈时
|
||||
*
|
||||
* 字体渲染需在 #include "deshab/font.h" 前提供位图数据源
|
||||
* (#include "ascii_bitmaps.c" 或自定义 g_ascii/g_ascii_w/g_ascii_h)。
|
||||
*/
|
||||
#ifndef DESHAB_H
|
||||
#define DESHAB_H
|
||||
|
||||
/* ---- 基础层 ---- */
|
||||
#include "types.h"
|
||||
#include "portio.h"
|
||||
#include "serial.h"
|
||||
#include "tsc.h"
|
||||
#include "string.h"
|
||||
|
||||
/* ---- ABI 层 ---- */
|
||||
#include "kernel_api.h"
|
||||
#include "boot_context.h"
|
||||
#include "driver.h"
|
||||
#include "block.h"
|
||||
|
||||
/* ---- 应用层 ---- */
|
||||
#include "app.h"
|
||||
#include "fb.h"
|
||||
#include "cursor.h"
|
||||
#include "input.h"
|
||||
#include "ui.h"
|
||||
#include "rtc.h"
|
||||
/* font.h 需先提供 g_ascii 位图数据源,不在此默认包含:
|
||||
* #include "ascii_bitmaps.c"
|
||||
* #include "deshab/font.h"
|
||||
*/
|
||||
|
||||
/* ---- 领域层(按需包含,含大 static 缓冲)----
|
||||
* #include "fat32.h"
|
||||
* #include "net.h"
|
||||
*/
|
||||
|
||||
#endif /* DESHAB_H */
|
||||
@@ -0,0 +1,97 @@
|
||||
/* deshab/driver.h - DKM 驱动模块 ABI
|
||||
*
|
||||
* 整合 CODE/UTSM/include/utsm/dkm.h 与 CODE/DKM/dkm_shared.h 的驱动描述符定义。
|
||||
* 驱动模块(.drv,ET_REL)导出 driver_desc / driver_init / driver_exit 三个符号,
|
||||
* 由 DKM loader 校验 magic、调用 driver_init(api, handle) 完成初始化。
|
||||
*
|
||||
* 约定:
|
||||
* - driver_init 返回 0 成功,负数失败。
|
||||
* - required 驱动 init 失败触发 DRR recovery 或 panic。
|
||||
* - driver_exit 仅用于非 NO_UNLOAD 驱动。
|
||||
* - 驱动通过 dkm_kernel_api 调用内核服务,不直接依赖任意内核符号。
|
||||
*/
|
||||
#ifndef DESHAB_DRIVER_H
|
||||
#define DESHAB_DRIVER_H
|
||||
|
||||
#include "types.h"
|
||||
#include "kernel_api.h"
|
||||
|
||||
#define DKM_DRIVER_MAGIC 0x444B4D31u /* "DKM1" */
|
||||
#define DKM_ABI_VERSION 1u
|
||||
|
||||
/* ---- 驱动类别(driver_desc.driver_class) ---- */
|
||||
#define DKM_CLASS_PLATFORM 1u
|
||||
#define DKM_CLASS_BUS 2u
|
||||
#define DKM_CLASS_INTERRUPT 3u
|
||||
#define DKM_CLASS_TIMER 4u
|
||||
#define DKM_CLASS_CONSOLE 5u
|
||||
#define DKM_CLASS_STORAGE 6u
|
||||
#define DKM_CLASS_FS 7u
|
||||
#define DKM_CLASS_NET 8u
|
||||
#define DKM_CLASS_INPUT 9u
|
||||
#define DKM_CLASS_GPU 10u
|
||||
#define DKM_CLASS_MISC 11u
|
||||
|
||||
/* ---- 驱动标志(driver_desc.flags) ---- */
|
||||
#define DKM_F_REQUIRED (1u << 0) /* 当前 stage 失败则 DRR recovery 或 panic */
|
||||
#define DKM_F_BOOT_MODULE (1u << 1) /* 可由 bootloader 预加载 */
|
||||
#define DKM_F_NO_UNLOAD (1u << 2) /* 不允许卸载 */
|
||||
#define DKM_F_STRONG_RECOVERY (1u << 3) /* 驱动 data 段使用 UTSM 强恢复策略 */
|
||||
#define DKM_F_DMA_REQUIRED (1u << 4) /* 驱动需要 DMA API */
|
||||
#define DKM_F_EARLY_LOG (1u << 5) /* 驱动 init 期间允许使用 early log */
|
||||
|
||||
typedef struct dkm_driver_desc {
|
||||
u32 magic; /* DKM_DRIVER_MAGIC */
|
||||
u16 abi_version; /* DKM_ABI_VERSION */
|
||||
u16 desc_size;
|
||||
|
||||
const char *name;
|
||||
const char *version;
|
||||
const char *vendor;
|
||||
|
||||
u32 driver_class; /* DKM_CLASS_* */
|
||||
u32 stage; /* 加载阶段 0-3 */
|
||||
u32 flags; /* DKM_F_* */
|
||||
u32 priority;
|
||||
|
||||
const char *const *depends;
|
||||
u32 depends_count;
|
||||
|
||||
const char *const *provides;
|
||||
u32 provides_count;
|
||||
|
||||
u64 min_kernel_abi;
|
||||
u64 feature_bits;
|
||||
|
||||
u64 reserved0;
|
||||
u64 reserved1;
|
||||
} dkm_driver_desc;
|
||||
|
||||
struct dkm_driver_handle;
|
||||
|
||||
typedef int (*dkm_driver_init_fn)(const dkm_kernel_api *api, struct dkm_driver_handle *handle);
|
||||
typedef int (*dkm_driver_exit_fn)(struct dkm_driver_handle *handle);
|
||||
|
||||
typedef enum dkm_driver_state {
|
||||
DKM_STATE_DISCOVERED = 0,
|
||||
DKM_STATE_ABI_CHECKED,
|
||||
DKM_STATE_INITING,
|
||||
DKM_STATE_ACTIVE,
|
||||
DKM_STATE_FAILED
|
||||
} dkm_driver_state;
|
||||
|
||||
typedef struct dkm_driver_handle {
|
||||
const dkm_driver_desc *desc;
|
||||
dkm_driver_state state;
|
||||
i32 init_status;
|
||||
u32 load_stage;
|
||||
u32 flags;
|
||||
} dkm_driver_handle;
|
||||
|
||||
typedef struct dkm_builtin_driver {
|
||||
const dkm_driver_desc *desc;
|
||||
dkm_driver_init_fn init;
|
||||
dkm_driver_exit_fn exit;
|
||||
} dkm_builtin_driver;
|
||||
|
||||
#endif /* DESHAB_DRIVER_H */
|
||||
@@ -0,0 +1,1233 @@
|
||||
/* deshab/fat32.h - Deshab SDK FAT32 读写库 + VFAT LFN 长文件名解析(static inline,无库)
|
||||
*
|
||||
* 本头为 Deshab SDK 的一部分,整合自:
|
||||
* - CODE/tools/fat32_lfn.h(LFN 长文件名解析)
|
||||
* - CODE/tools/fat32_io.h(FAT32 读写库)
|
||||
* 合并为单一头文件,基础类型 u8/u16/u32/u64/i64 由 "types.h" 提供。
|
||||
*/
|
||||
|
||||
#ifndef DESHAB_FAT32_H
|
||||
#define DESHAB_FAT32_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/* =====================================================================
|
||||
* 第一部分:LFN 长文件名解析(原 fat32_lfn.h)
|
||||
* =====================================================================
|
||||
*
|
||||
* FAT32 LFN 原理:
|
||||
* 目录项中 attr==0x0F 的是 LFN 片段项。每项含 13 个 UTF-16 LE 字符
|
||||
* (name1[5] + name2[6] + name3[2]),按逆序存储——序号最高(带 0x40
|
||||
* 标志)的项最先出现,序号递减,最后紧跟一个 8.3 短目录项作为“主项”。
|
||||
* 短名项的 checksum(由 name11 计算)用于校验整条 LFN 链完整性。
|
||||
*
|
||||
* 用法(在目录遍历循环中):
|
||||
* fat32_lfn_buf lfn;
|
||||
* fat32_lfn_init(&lfn);
|
||||
* // 外层簇遍历循环——lfn 必须跨簇存活(LFN 链可能跨簇边界)
|
||||
* while (遍历目录簇) {
|
||||
* for (每个 32 字节目录项 e) {
|
||||
* if (name[0]==0) 目录结束;
|
||||
* if (name[0]==0xE5) { fat32_lfn_init(&lfn); continue; }
|
||||
* int is_short = fat32_lfn_process(&lfn, entry_ptr);
|
||||
* if (is_short) {
|
||||
* // lfn.valid==1 时有完整 LFN,可 fat32_lfn_to_ascii 取名
|
||||
* // 用 fat32_lfn_match 匹配长名或 8.3 短名
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* 注意:fat32_lfn_buf 必须在簇遍历循环**外**声明,跨簇切换时不清空,
|
||||
* 以支持跨簇边界的 LFN 链。
|
||||
*/
|
||||
|
||||
/* LFN 最大字符数(FAT32 规范最多 20 个 LFN 项 = 260 字符) */
|
||||
#define FAT32_LFN_MAX 260
|
||||
|
||||
/* LFN 收集缓冲(栈/局部变量分配) */
|
||||
typedef struct {
|
||||
u16 chars[FAT32_LFN_MAX]; /* UTF-16 LE 累积字符(正序) */
|
||||
int count; /* 有效字符数(不含 0x0000 终止符/0xFFFF 填充) */
|
||||
u8 checksum; /* LFN 链 checksum(来自 0x40 标志项) */
|
||||
int valid; /* 1=LFN 链完整且 checksum 与短名项匹配 */
|
||||
} fat32_lfn_buf;
|
||||
|
||||
/* 初始化/清空 LFN 缓冲 */
|
||||
static inline void fat32_lfn_init(fat32_lfn_buf *buf) {
|
||||
buf->count = 0;
|
||||
buf->valid = 0;
|
||||
buf->checksum = 0;
|
||||
for (int i = 0; i < FAT32_LFN_MAX; i++) buf->chars[i] = 0;
|
||||
}
|
||||
|
||||
/* 计算 8.3 短名(11 字节)的 LFN checksum。
|
||||
* 标准 FAT32 算法:cksum = ((cksum & 1) << 7) + (cksum >> 1) + name[i] */
|
||||
static inline u8 fat32_lfn_checksum(const u8 name11[11]) {
|
||||
u8 cksum = 0;
|
||||
for (int i = 0; i < 11; i++) {
|
||||
cksum = (u8)(((cksum & 1) << 7) + (cksum >> 1) + name11[i]);
|
||||
}
|
||||
return cksum;
|
||||
}
|
||||
|
||||
/* 处理一个 32 字节目录项。自动判断 LFN 项 / 短名项 / 已删除项。
|
||||
*
|
||||
* 调用方对目录遍历中的每个条目(跳过 name[0]==0 结束符后)调用此函数。
|
||||
* - LFN 项(attr==0x0F):收集 13 个 UTF-16 字符到 buf,返回 0
|
||||
* - 已删除项(name[0]==0xE5):清空 buf,返回 0
|
||||
* - 短名项:校验 checksum,设 buf->valid,返回 1
|
||||
*
|
||||
* 返回 1=刚处理的是短名项(此时可检查 buf->valid 判断是否有有效 LFN),
|
||||
* 0=LFN 项或已删除项。
|
||||
*
|
||||
* 注意:遇到带 0x40 标志的 LFN 项(链开始)时自动重置 buf。
|
||||
* 遇到已删除项时自动清空 buf(防止残留 LFN 污染下一个文件)。 */
|
||||
static inline int fat32_lfn_process(fat32_lfn_buf *buf, const u8 *entry) {
|
||||
u8 attr = entry[11];
|
||||
|
||||
/* 已删除项:清空 LFN 缓冲 */
|
||||
if (entry[0] == 0xE5) {
|
||||
fat32_lfn_init(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* LFN 项(attr == 0x0F) */
|
||||
if (attr == 0x0F) {
|
||||
u8 seq = entry[0];
|
||||
u8 ord = (u8)(seq & 0x3F); /* 序号 1-20 */
|
||||
if (ord == 0 || ord > 20) {
|
||||
fat32_lfn_init(buf);
|
||||
return 0;
|
||||
}
|
||||
/* 带 0x40 标志 = LFN 链开始(最高序号项),重置缓冲 */
|
||||
if (seq & 0x40) {
|
||||
int prev_cksum = buf->checksum;
|
||||
fat32_lfn_init(buf);
|
||||
buf->checksum = entry[13];
|
||||
(void)prev_cksum;
|
||||
}
|
||||
/* 提取 13 个 UTF-16 LE 字符,放到 (ord-1)*13 位置(正序) */
|
||||
int base = (ord - 1) * 13;
|
||||
if (base + 13 > FAT32_LFN_MAX) {
|
||||
fat32_lfn_init(buf);
|
||||
return 0;
|
||||
}
|
||||
/* name1: entry[1..10] = 5 个 UTF-16 LE 字符 */
|
||||
for (int i = 0; i < 5; i++) {
|
||||
u16 c = (u16)entry[1 + i * 2] | ((u16)entry[2 + i * 2] << 8);
|
||||
buf->chars[base + i] = c;
|
||||
}
|
||||
/* name2: entry[14..25] = 6 个 UTF-16 LE 字符 */
|
||||
for (int i = 0; i < 6; i++) {
|
||||
u16 c = (u16)entry[14 + i * 2] | ((u16)entry[15 + i * 2] << 8);
|
||||
buf->chars[base + 5 + i] = c;
|
||||
}
|
||||
/* name3: entry[28..31] = 2 个 UTF-16 LE 字符 */
|
||||
for (int i = 0; i < 2; i++) {
|
||||
u16 c = (u16)entry[28 + i * 2] | ((u16)entry[29 + i * 2] << 8);
|
||||
buf->chars[base + 11 + i] = c;
|
||||
}
|
||||
return 0; /* LFN 项,还不是短名项 */
|
||||
}
|
||||
|
||||
/* 短名项:校验 checksum */
|
||||
u8 cksum = fat32_lfn_checksum(entry);
|
||||
if (buf->checksum != 0 && buf->checksum == cksum) {
|
||||
/* LFN 链有效——计算实际字符数(截断于 0x0000 终止符或 0xFFFF 填充符) */
|
||||
int n = 0;
|
||||
for (int i = 0; i < FAT32_LFN_MAX; i++) {
|
||||
if (buf->chars[i] == 0x0000 || buf->chars[i] == 0xFFFF) break;
|
||||
n++;
|
||||
}
|
||||
buf->count = n;
|
||||
buf->valid = 1;
|
||||
} else {
|
||||
/* checksum 不匹配或无 LFN 链:清空,回退 8.3 */
|
||||
fat32_lfn_init(buf);
|
||||
}
|
||||
return 1; /* 短名项处理完毕 */
|
||||
}
|
||||
|
||||
/* 从 LFN 缓冲提取 ASCII 名(UTF-16->ASCII 缩窄,仅 BMP U+0000..U+007F)。
|
||||
* out_cap 为 out 缓冲容量(含 NUL)。
|
||||
* 返回字符串长度(不含 NUL),-1=无有效 LFN 或含非 ASCII 字符。 */
|
||||
static inline int fat32_lfn_to_ascii(const fat32_lfn_buf *buf, char *out, int out_cap) {
|
||||
if (!buf->valid || buf->count == 0 || out_cap <= 0) return -1;
|
||||
int n = buf->count;
|
||||
if (n >= out_cap) n = out_cap - 1;
|
||||
for (int i = 0; i < n; i++) {
|
||||
u16 c = buf->chars[i];
|
||||
if (c > 0x7F) return -1; /* 含非 ASCII 字符 */
|
||||
out[i] = (char)(u8)c;
|
||||
}
|
||||
out[n] = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* ASCII 大小写不敏感字符串比较 */
|
||||
static inline int fat32_lfn_streq_ci(const char *a, const char *b) {
|
||||
if (!a || !b) return 0;
|
||||
while (*a && *b) {
|
||||
char ca = *a, cb = *b;
|
||||
if (ca >= 'a' && ca <= 'z') ca = (char)(ca - 32);
|
||||
if (cb >= 'a' && cb <= 'z') cb = (char)(cb - 32);
|
||||
if (ca != cb) return 0;
|
||||
a++; b++;
|
||||
}
|
||||
return *a == *b;
|
||||
}
|
||||
|
||||
/* 判断 ASCII 字符串是否以指定后缀结尾(大小写不敏感)。
|
||||
* 例如 fat32_lfn_ends_with_ci("libtest.so", ".so") -> 1 */
|
||||
static inline int fat32_lfn_ends_with_ci(const char *s, const char *suffix) {
|
||||
if (!s || !suffix) return 0;
|
||||
int sl = 0, xl = 0;
|
||||
while (s[sl]) sl++;
|
||||
while (suffix[xl]) xl++;
|
||||
if (xl > sl || xl == 0) return 0;
|
||||
for (int i = 0; i < xl; i++) {
|
||||
char a = s[sl - xl + i];
|
||||
char b = suffix[i];
|
||||
if (a >= 'a' && a <= 'z') a = (char)(a - 32);
|
||||
if (b >= 'a' && b <= 'z') b = (char)(b - 32);
|
||||
if (a != b) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 匹配目录项:优先匹配 LFN 长名(大小写不敏感),回退 8.3 短名。
|
||||
* buf : LFN 收集缓冲(短名项处理后)
|
||||
* name11 : 短名项的 11 字节 8.3 名
|
||||
* target_long: 目标长名(ASCII,可为 NULL=不匹配长名)
|
||||
* target83 : 目标 8.3 名(11 字节,可为 NULL=不匹配 8.3)
|
||||
* 返回 1=匹配,0=不匹配。
|
||||
* FAT32 规范:文件名查找大小写不敏感。 */
|
||||
static inline int fat32_lfn_match(const fat32_lfn_buf *buf, const u8 name11[11],
|
||||
const char *target_long, const char *target83) {
|
||||
/* 1. 优先匹配 LFN 长名 */
|
||||
if (buf->valid && buf->count > 0 && target_long) {
|
||||
char ascii[FAT32_LFN_MAX];
|
||||
int n = fat32_lfn_to_ascii(buf, ascii, sizeof(ascii));
|
||||
if (n >= 0 && fat32_lfn_streq_ci(ascii, target_long)) return 1;
|
||||
}
|
||||
/* 2. 回退 8.3 短名匹配(精确比较 11 字节) */
|
||||
if (target83 && name11) {
|
||||
for (int i = 0; i < 11; i++) {
|
||||
if (name11[i] != (u8)target83[i]) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 从 8.3 短名(11 字节)生成可显示 ASCII 字符串(如 "README.TXT")。
|
||||
* 与 fat32_io.h 的 f32_name_from_83 功能相同,独立提供以避免循环依赖。 */
|
||||
static inline void fat32_lfn_short_to_str(const u8 name11[11], char out[13]) {
|
||||
int p = 0;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (name11[j] == ' ') break;
|
||||
out[p++] = (char)name11[j];
|
||||
}
|
||||
if (name11[8] != ' ') {
|
||||
out[p++] = '.';
|
||||
for (int j = 8; j < 11; j++) {
|
||||
if (name11[j] == ' ') break;
|
||||
out[p++] = (char)name11[j];
|
||||
}
|
||||
}
|
||||
out[p] = 0;
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
* 第二部分:FAT32 读写库(原 fat32_io.h)
|
||||
* =====================================================================
|
||||
*
|
||||
* 通过 f32_init() 注入 block_read/block_write 函数指针(来自 dkm_kernel_api + 0xA8)。
|
||||
*
|
||||
* 能力:FAT32 根目录文件的读/写/删/列举,支持 FAT 链跟随、多簇文件。
|
||||
* 限制:仅根目录(不支持子目录,Phase 2.2 fileman 将扩展);单文件最大 256KB。
|
||||
*
|
||||
* 用法:
|
||||
* f32_init(block_read, block_write) - 初始化(block_write 可为 NULL,仅读场景)
|
||||
* f32_name_to_83("file.txt", name11) - "file.txt" -> 11 字符 8.3 名
|
||||
* f32_read_root_file(name11, &data, &size) - 读根目录文件到 f32_data 缓冲
|
||||
* f32_write_root_file(name11, data, size) - 写/替换根目录文件
|
||||
* f32_delete_root_file(name11) - 删除根目录文件
|
||||
* f32_list_root(emit_cb, user_data) - 列举根目录
|
||||
*
|
||||
* 缓冲区:每个包含本头的 .elf 独立持有 256KB g_disk + 4KB g_cluster + 256KB g_fdata(共 ~516KB BSS)。
|
||||
* 与 net_stack.h 同模式,接受 static 缓冲的代价以换取无库依赖。
|
||||
*/
|
||||
|
||||
/* ---- block 设备函数类型(与 shell/DSK 一致:index 参数固定 0) ---- */
|
||||
typedef int (*f32_block_read_fn)(u32 index, u64 lba, u32 count, void *buf);
|
||||
typedef int (*f32_block_write_fn)(u32 index, u64 lba, u32 count, const void *buf);
|
||||
|
||||
static f32_block_read_fn f32_blk_read = 0;
|
||||
static f32_block_write_fn f32_blk_write = 0;
|
||||
|
||||
/* ---- FAT32 BPB / 目录项结构(packed,与 DSK/shell 一致) ---- */
|
||||
typedef struct __attribute__((packed)) {
|
||||
u8 jmp[3]; char oem[8]; u16 bps; u8 spc; u16 rsvd; u8 fc; u16 root_ent;
|
||||
u16 ts16; u8 media; u16 spf16; u16 spt; u16 heads; u32 hidden; u32 ts32;
|
||||
u32 spf; u16 flags; u16 ver; u32 root_clus; u16 fsi; u16 bkboot;
|
||||
u8 res[12]; u8 drv; u8 ntfl; u8 sig; u32 ser; char lbl[11]; char typ[8];
|
||||
u8 code[420]; u16 boot_sig;
|
||||
} f32_bpb;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
char name[11]; u8 attr; u8 ntr; u8 ctenth;
|
||||
u16 ctime; u16 cdate; u16 adate; u16 chigh;
|
||||
u16 wtime; u16 wdate; u16 clow; u32 fsize;
|
||||
} f32_dirent;
|
||||
|
||||
/* ---- 内部小工具 ---- */
|
||||
static inline u32 f32_r32(const u8 *p) { return (u32)p[0]|((u32)p[1]<<8)|((u32)p[2]<<16)|((u32)p[3]<<24); }
|
||||
static inline u16 f32_r16(const u8 *p) { return (u16)p[0]|((u16)p[1]<<8); }
|
||||
static inline int f32_neq11(const char *a, const char *b) {
|
||||
for (int i = 0; i < 11; i++) if (a[i] != b[i]) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ---- 批量读写缓冲区(与 DSK/shell 对齐:256 扇区覆盖 BPB+FAT+根目录+小文件) ---- */
|
||||
/* 文件数据缓冲大小可由包含者在 include 前 #define F32_DATA_BYTES 覆盖
|
||||
* (cmd.elf 需要装载 2MB+ 的 PE64 程序,定义为 4MB;其余默认 256KB)。 */
|
||||
#ifndef F32_DATA_BYTES
|
||||
#define F32_DATA_BYTES 262144
|
||||
#endif
|
||||
static u8 f32_disk[131072]; /* 256 扇区 BPB+FAT+根目录缓存 */
|
||||
static u8 f32_cluster[4096]; /* 单簇缓冲 */
|
||||
static u8 f32_data[F32_DATA_BYTES]; /* 文件数据缓冲 */
|
||||
static int f32_disk_loaded = 0; /* f32_disk 是否已加载 BPB+FAT */
|
||||
|
||||
/* 初始化:注入 block_read/block_write 函数指针 */
|
||||
static inline void f32_init(f32_block_read_fn rd, f32_block_write_fn wr) {
|
||||
f32_blk_read = rd;
|
||||
f32_blk_write = wr;
|
||||
f32_disk_loaded = 0;
|
||||
}
|
||||
|
||||
static inline int f32_read_sectors(u32 lba, u32 count, u8 *out) {
|
||||
return f32_blk_read ? f32_blk_read(0, lba, count, out) : -1;
|
||||
}
|
||||
static inline int f32_write_sectors(u32 lba, u32 count, const u8 *buf) {
|
||||
return f32_blk_write ? f32_blk_write(0, lba, count, buf) : -1;
|
||||
}
|
||||
|
||||
/* 确保 f32_disk 已加载 BPB+FAT+根目录(256 扇区) */
|
||||
static inline int f32_disk_load(void) {
|
||||
if (f32_disk_loaded) return 0;
|
||||
if (f32_read_sectors(0, 256, f32_disk) != 0) return -1;
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
if (bpb->boot_sig != 0xAA55 || bpb->bps != 512) return -2;
|
||||
if (bpb->spf == 0 || bpb->root_clus < 2) return -3;
|
||||
f32_disk_loaded = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- 8.3 名字转换 ---- */
|
||||
|
||||
/* "file.txt" / "NAME" -> 11 字符 8.3 名(大写,空格填充)
|
||||
* 返回 0 成功,-1 失败(过长或非法字符) */
|
||||
static inline int f32_name_to_83(const char *in, char out[11]) {
|
||||
for (int i = 0; i < 11; i++) out[i] = ' ';
|
||||
int inlen = 0;
|
||||
while (in[inlen] && inlen < 13) inlen++;
|
||||
if (inlen == 0 || inlen > 12) return -1;
|
||||
int dot = -1;
|
||||
for (int i = 0; i < inlen; i++) if (in[i] == '.') dot = i;
|
||||
int base_end = (dot >= 0) ? dot : inlen;
|
||||
if (base_end == 0 || base_end > 8) return -1;
|
||||
for (int j = 0; j < base_end; j++) {
|
||||
char c = in[j];
|
||||
if (c >= 'a' && c <= 'z') c -= 32;
|
||||
if (c == '.' || c == ' ' || c == '/' || c == '\\') return -1;
|
||||
out[j] = c;
|
||||
}
|
||||
if (dot >= 0) {
|
||||
int extlen = inlen - dot - 1;
|
||||
if (extlen > 3) return -1;
|
||||
for (int j = 0; j < extlen; j++) {
|
||||
char c = in[dot + 1 + j];
|
||||
if (c >= 'a' && c <= 'z') c -= 32;
|
||||
if (c == '.' || c == ' ' || c == '/' || c == '\\') return -1;
|
||||
out[8 + j] = c;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 11 字符 8.3 名 -> 可显示字符串(如 "README.TXT") */
|
||||
static inline void f32_name_from_83(const char in[11], char out[13]) {
|
||||
int p = 0;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (in[j] == ' ') break;
|
||||
out[p++] = in[j];
|
||||
}
|
||||
if (in[8] != ' ') {
|
||||
out[p++] = '.';
|
||||
for (int j = 8; j < 11; j++) {
|
||||
if (in[j] == ' ') break;
|
||||
out[p++] = in[j];
|
||||
}
|
||||
}
|
||||
out[p] = 0;
|
||||
}
|
||||
|
||||
/* ---- 根目录文件查找 ---- */
|
||||
|
||||
/* 在根目录簇链中查找 11 字符名,输出首簇号与文件大小 */
|
||||
static inline int f32_find_in_root(const u8 *clus, u32 clus_sectors, const char *target,
|
||||
u32 *out_clus, u32 *out_size, u32 *out_idx) {
|
||||
const f32_dirent *dir = (const f32_dirent *)clus;
|
||||
for (u32 e = 0; e * 32 < clus_sectors * 512; e++) {
|
||||
if (dir[e].name[0] == 0) break;
|
||||
if ((u8)dir[e].name[0] == 0xE5) continue;
|
||||
if (dir[e].attr == 0x0F) continue;
|
||||
if (dir[e].attr & 0x08) continue;
|
||||
if (f32_neq11(dir[e].name, target)) {
|
||||
*out_clus = (u32)f32_r16((const u8*)&dir[e].clow) | ((u32)f32_r16((const u8*)&dir[e].chigh) << 16);
|
||||
*out_size = dir[e].fsize;
|
||||
if (out_idx) *out_idx = e;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 读取根目录下指定 8.3 名文件到 f32_data。
|
||||
* 返回 0 成功,*out_data 指向 f32_data,*out_size 为字节数;非 0 失败。 */
|
||||
static inline int f32_read_root_file(const char *name11, u8 **out_data, u32 *out_size) {
|
||||
if (f32_disk_load() != 0) return -1;
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
||||
u32 fat_byte_off = bpb->rsvd * 512;
|
||||
u32 spc = bpb->spc;
|
||||
|
||||
u32 clus = bpb->root_clus;
|
||||
u32 found_clus = 0, found_size = 0;
|
||||
int found = 0;
|
||||
while (clus >= 2 && clus < 0x0FFFFFF8 && !found) {
|
||||
u32 lba = data_lba + (clus - 2) * spc;
|
||||
const u8 *cb;
|
||||
if ((u64)lba * 512 + (u64)spc * 512 <= 256ULL * 512)
|
||||
cb = f32_disk + (u64)lba * 512;
|
||||
else {
|
||||
if (f32_read_sectors(lba, 8, f32_cluster) != 0) return -4;
|
||||
cb = f32_cluster;
|
||||
}
|
||||
if (f32_find_in_root(cb, spc, name11, &found_clus, &found_size, 0) == 0) { found = 1; break; }
|
||||
u32 fo = fat_byte_off + clus * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
clus = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
}
|
||||
if (!found) return -5;
|
||||
if (found_size > sizeof(f32_data)) return -6;
|
||||
|
||||
u8 *dst = f32_data; u32 remaining = found_size; u32 fc = found_clus;
|
||||
while (fc >= 2 && fc < 0x0FFFFFF8 && remaining > 0) {
|
||||
u32 fc_lba = data_lba + (fc - 2) * spc;
|
||||
u32 fc_bytes = spc * 512;
|
||||
if (fc_bytes > remaining) fc_bytes = remaining;
|
||||
const u8 *fb;
|
||||
if ((u64)fc_lba * 512 + (u64)spc * 512 <= 256ULL * 512)
|
||||
fb = f32_disk + (u64)fc_lba * 512;
|
||||
else {
|
||||
if (f32_read_sectors(fc_lba, 8, f32_cluster) != 0) return -7;
|
||||
fb = f32_cluster;
|
||||
}
|
||||
for (u32 b = 0; b < fc_bytes; b++) dst[b] = fb[b];
|
||||
dst += fc_bytes; remaining -= fc_bytes;
|
||||
u32 fo = fat_byte_off + fc * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
fc = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
}
|
||||
*out_data = f32_data; *out_size = found_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 写入根目录下指定 8.3 名文件(存在则替换,不存在则新建)。
|
||||
* 依赖 f32_disk 已加载 BPB+FAT。返回 0 成功。 */
|
||||
static inline int f32_write_root_file(const char *name11, const u8 *data, u32 size) {
|
||||
if (f32_disk_load() != 0) return -1;
|
||||
if (!f32_blk_write) return -10;
|
||||
/* 写入前重新加载 BPB+FAT,保证与盘上一致 */
|
||||
if (f32_read_sectors(0, 256, f32_disk) != 0) return -2;
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
u32 spc = bpb->spc;
|
||||
u32 fat_lba = bpb->rsvd;
|
||||
u32 fat_sectors = bpb->spf;
|
||||
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
||||
u32 fat_byte_off = bpb->rsvd * 512;
|
||||
u32 root_clus = bpb->root_clus;
|
||||
u32 cluster_bytes = spc * 512;
|
||||
|
||||
u32 root_lba = data_lba + (root_clus - 2) * spc;
|
||||
u8 *root_buf = f32_disk + (u64)root_lba * 512;
|
||||
u32 max_entries = cluster_bytes / 32;
|
||||
f32_dirent *dir = (f32_dirent *)root_buf;
|
||||
int free_entry = -1;
|
||||
u32 existing_clus = 0;
|
||||
int found = 0;
|
||||
u32 existing_idx = 0;
|
||||
for (u32 e = 0; e < max_entries; e++) {
|
||||
if (dir[e].name[0] == 0) { if (free_entry < 0) free_entry = (int)e; break; }
|
||||
if ((u8)dir[e].name[0] == 0xE5) { if (free_entry < 0) free_entry = (int)e; continue; }
|
||||
if (dir[e].attr == 0x0F) continue;
|
||||
if (dir[e].attr & 0x08) continue;
|
||||
if (f32_neq11(dir[e].name, name11)) {
|
||||
existing_clus = (u32)f32_r16((const u8*)&dir[e].clow) | ((u32)f32_r16((const u8*)&dir[e].chigh) << 16);
|
||||
found = 1; existing_idx = e;
|
||||
if (free_entry < 0) free_entry = (int)e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u32 bytes_needed = size > 0 ? size : 1;
|
||||
u32 clusters_needed = (bytes_needed + cluster_bytes - 1) / cluster_bytes;
|
||||
u32 first_clus = 0;
|
||||
u32 prev_clus = 0;
|
||||
|
||||
if (found && existing_clus >= 2) {
|
||||
first_clus = existing_clus;
|
||||
u32 cur = existing_clus;
|
||||
u32 count = 0;
|
||||
while (cur >= 2 && cur < 0x0FFFFFF8 && count < clusters_needed) {
|
||||
prev_clus = cur;
|
||||
u32 fo = fat_byte_off + cur * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
cur = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
count++;
|
||||
}
|
||||
while (count < clusters_needed) {
|
||||
u32 newc = 0;
|
||||
for (u32 c = 2; c < (fat_sectors * 512) / 4; c++) {
|
||||
u32 fo = fat_byte_off + c * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
if ((f32_r32(f32_disk + fo) & 0x0FFFFFFF) == 0) { newc = c; break; }
|
||||
}
|
||||
if (newc == 0) return -3;
|
||||
u32 pfo = fat_byte_off + prev_clus * 4;
|
||||
if (pfo + 4 <= sizeof(f32_disk)) {
|
||||
f32_disk[pfo] = (u8)(newc & 0xFF);
|
||||
f32_disk[pfo+1] = (u8)((newc >> 8) & 0xFF);
|
||||
f32_disk[pfo+2] = (u8)((newc >> 16) & 0xFF);
|
||||
f32_disk[pfo+3] = (u8)((newc >> 24) & 0x0F);
|
||||
}
|
||||
prev_clus = newc;
|
||||
count++;
|
||||
}
|
||||
if (prev_clus >= 2) {
|
||||
u32 fo = fat_byte_off + prev_clus * 4;
|
||||
if (fo + 4 <= sizeof(f32_disk)) {
|
||||
f32_disk[fo] = 0xF8; f32_disk[fo+1] = 0xFF; f32_disk[fo+2] = 0xFF; f32_disk[fo+3] = 0x0F;
|
||||
}
|
||||
}
|
||||
/* 释放多余旧簇 */
|
||||
u32 next = 0;
|
||||
u32 fo = fat_byte_off + prev_clus * 4;
|
||||
if (fo + 4 <= sizeof(f32_disk)) next = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
while (next >= 2 && next < 0x0FFFFFF8) {
|
||||
u32 nfo = fat_byte_off + next * 4;
|
||||
u32 nn = 0;
|
||||
if (nfo + 4 <= sizeof(f32_disk)) nn = f32_r32(f32_disk + nfo) & 0x0FFFFFFF;
|
||||
f32_disk[nfo] = 0; f32_disk[nfo+1] = 0; f32_disk[nfo+2] = 0; f32_disk[nfo+3] = 0;
|
||||
next = nn;
|
||||
}
|
||||
} else {
|
||||
if (free_entry < 0) return -4;
|
||||
for (u32 i = 0; i < clusters_needed; i++) {
|
||||
u32 newc = 0;
|
||||
for (u32 c = 2; c < (fat_sectors * 512) / 4; c++) {
|
||||
u32 fo = fat_byte_off + c * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
if ((f32_r32(f32_disk + fo) & 0x0FFFFFFF) == 0) { newc = c; break; }
|
||||
}
|
||||
if (newc == 0) return -3;
|
||||
if (i == 0) first_clus = newc;
|
||||
if (prev_clus >= 2) {
|
||||
u32 pfo = fat_byte_off + prev_clus * 4;
|
||||
f32_disk[pfo] = (u8)(newc & 0xFF);
|
||||
f32_disk[pfo+1] = (u8)((newc >> 8) & 0xFF);
|
||||
f32_disk[pfo+2] = (u8)((newc >> 16) & 0xFF);
|
||||
f32_disk[pfo+3] = (u8)((newc >> 24) & 0x0F);
|
||||
}
|
||||
prev_clus = newc;
|
||||
}
|
||||
if (prev_clus >= 2) {
|
||||
u32 fo = fat_byte_off + prev_clus * 4;
|
||||
f32_disk[fo] = 0xF8; f32_disk[fo+1] = 0xFF; f32_disk[fo+2] = 0xFF; f32_disk[fo+3] = 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
/* 写数据到簇 */
|
||||
u32 remaining = size;
|
||||
const u8 *src = data;
|
||||
u32 cur = first_clus;
|
||||
u32 ci = 0;
|
||||
while (cur >= 2 && cur < 0x0FFFFFF8 && ci < clusters_needed) {
|
||||
u32 clba = data_lba + (cur - 2) * spc;
|
||||
u32 chunk = remaining < cluster_bytes ? remaining : cluster_bytes;
|
||||
if ((u64)clba * 512 + cluster_bytes <= 256ULL * 512) {
|
||||
u8 *dst = f32_disk + (u64)clba * 512;
|
||||
for (u32 b = 0; b < chunk; b++) dst[b] = src[b];
|
||||
for (u32 b = chunk; b < cluster_bytes; b++) dst[b] = 0;
|
||||
} else {
|
||||
if (f32_read_sectors(clba, spc, f32_cluster) != 0) return -5;
|
||||
for (u32 b = 0; b < chunk; b++) f32_cluster[b] = src[b];
|
||||
for (u32 b = chunk; b < cluster_bytes; b++) f32_cluster[b] = 0;
|
||||
if (f32_write_sectors(clba, spc, f32_cluster) != 0) return -6;
|
||||
}
|
||||
src += chunk; remaining -= chunk; ci++;
|
||||
u32 fo = fat_byte_off + cur * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
cur = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
}
|
||||
|
||||
/* 更新目录项 */
|
||||
u32 entry_idx = found ? existing_idx : (u32)free_entry;
|
||||
f32_dirent *e = &dir[entry_idx];
|
||||
if (!found) {
|
||||
for (int i = 0; i < 11; i++) e->name[i] = name11[i];
|
||||
e->attr = 0x20; e->ntr = 0; e->ctenth = 0; e->ctime = 0; e->cdate = 0; e->adate = 0;
|
||||
}
|
||||
e->chigh = (u16)((first_clus >> 16) & 0xFFFF);
|
||||
e->clow = (u16)(first_clus & 0xFFFF);
|
||||
e->fsize = size;
|
||||
|
||||
/* 回写 FAT(两份)+ 根目录 + 缓冲区内的数据簇 */
|
||||
for (u32 f = 0; f < bpb->fc; f++) {
|
||||
u32 flba = fat_lba + f * fat_sectors;
|
||||
if (f32_write_sectors(flba, fat_sectors, f32_disk + (u64)flba * 512) != 0) return -7;
|
||||
}
|
||||
u32 root_dir_lba = data_lba + (root_clus - 2) * spc;
|
||||
if ((u64)root_dir_lba * 512 + cluster_bytes <= 256ULL * 512) {
|
||||
if (f32_write_sectors(root_dir_lba, spc, f32_disk + (u64)root_dir_lba * 512) != 0) return -8;
|
||||
}
|
||||
cur = first_clus; ci = 0;
|
||||
while (cur >= 2 && cur < 0x0FFFFFF8 && ci < clusters_needed) {
|
||||
u32 clba = data_lba + (cur - 2) * spc;
|
||||
if ((u64)clba * 512 + cluster_bytes <= 256ULL * 512) {
|
||||
if (f32_write_sectors(clba, spc, f32_disk + (u64)clba * 512) != 0) return -9;
|
||||
}
|
||||
u32 fo = fat_byte_off + cur * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
cur = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
ci++;
|
||||
}
|
||||
f32_disk_loaded = 0; /* 下次操作重新加载,避免使用脏缓存 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 在指定目录簇中写入文件(存在则替换,不存在则新建)。
|
||||
* 泛化自 f32_write_root_file:目录可跨簇链,目录项回写到其所在簇。
|
||||
* dir_clus : 目标目录首簇(0 = 根目录)
|
||||
* name11 : 11 字符 8.3 名
|
||||
* 返回 0 成功;负值失败(-4 = 目录链无空闲槽,不扩展目录簇链)。
|
||||
* 注意:仅 8.3 短名匹配/创建,不生成 LFN 项。 */
|
||||
static inline int f32_write_file_in_dir(u32 dir_clus, const char *name11, const u8 *data, u32 size) {
|
||||
if (f32_disk_load() != 0) return -1;
|
||||
if (!f32_blk_write) return -10;
|
||||
/* 写入前重新加载 BPB+FAT,保证与盘上一致 */
|
||||
if (f32_read_sectors(0, 256, f32_disk) != 0) return -2;
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
u32 spc = bpb->spc;
|
||||
u32 fat_lba = bpb->rsvd;
|
||||
u32 fat_sectors = bpb->spf;
|
||||
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
||||
u32 fat_byte_off = bpb->rsvd * 512;
|
||||
u32 cluster_bytes = spc * 512;
|
||||
if (dir_clus == 0) dir_clus = bpb->root_clus;
|
||||
|
||||
/* 第一遍:沿目录簇链查找现有项与空闲槽,记录其所在簇与索引 */
|
||||
u32 found_clus = 0, existing_clus = 0;
|
||||
int found = 0;
|
||||
u32 found_idx = 0, free_idx = 0;
|
||||
u32 free_slot_clus = 0;
|
||||
int have_free = 0;
|
||||
u32 dclus = dir_clus;
|
||||
while (dclus >= 2 && dclus < 0x0FFFFFF8 && !found) {
|
||||
u32 dlba = data_lba + (dclus - 2) * spc;
|
||||
const u8 *db;
|
||||
if ((u64)dlba * 512 + cluster_bytes <= 256ULL * 512)
|
||||
db = f32_disk + (u64)dlba * 512;
|
||||
else {
|
||||
if (f32_read_sectors(dlba, spc, f32_cluster) != 0) return -11;
|
||||
db = f32_cluster;
|
||||
}
|
||||
const f32_dirent *dir = (const f32_dirent *)db;
|
||||
u32 max_entries = cluster_bytes / 32;
|
||||
for (u32 e = 0; e < max_entries; e++) {
|
||||
if (dir[e].name[0] == 0) {
|
||||
if (!have_free) { have_free = 1; free_slot_clus = dclus; free_idx = e; }
|
||||
goto scan_done; /* 0x00 = 目录结尾 */
|
||||
}
|
||||
if ((u8)dir[e].name[0] == 0xE5) {
|
||||
if (!have_free) { have_free = 1; free_slot_clus = dclus; free_idx = e; }
|
||||
continue;
|
||||
}
|
||||
if (dir[e].attr == 0x0F) continue;
|
||||
if (dir[e].attr & 0x08) continue;
|
||||
if (f32_neq11(dir[e].name, name11)) {
|
||||
existing_clus = (u32)f32_r16((const u8*)&dir[e].clow) | ((u32)f32_r16((const u8*)&dir[e].chigh) << 16);
|
||||
found = 1; found_idx = e; found_clus = dclus;
|
||||
if (!have_free) { have_free = 1; free_slot_clus = dclus; free_idx = e; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) break;
|
||||
u32 fo = fat_byte_off + dclus * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
dclus = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
}
|
||||
scan_done:
|
||||
; /* C89: 标签后必须是语句 */
|
||||
|
||||
u32 bytes_needed = size > 0 ? size : 1;
|
||||
u32 clusters_needed = (bytes_needed + cluster_bytes - 1) / cluster_bytes;
|
||||
u32 first_clus = 0;
|
||||
u32 prev_clus = 0;
|
||||
|
||||
if (found && existing_clus >= 2) {
|
||||
first_clus = existing_clus;
|
||||
u32 cur = existing_clus;
|
||||
u32 count = 0;
|
||||
while (cur >= 2 && cur < 0x0FFFFFF8 && count < clusters_needed) {
|
||||
prev_clus = cur;
|
||||
u32 fo = fat_byte_off + cur * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
cur = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
count++;
|
||||
}
|
||||
while (count < clusters_needed) {
|
||||
u32 newc = 0;
|
||||
for (u32 c = 2; c < (fat_sectors * 512) / 4; c++) {
|
||||
u32 fo = fat_byte_off + c * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
if ((f32_r32(f32_disk + fo) & 0x0FFFFFFF) == 0) { newc = c; break; }
|
||||
}
|
||||
if (newc == 0) return -3;
|
||||
u32 pfo = fat_byte_off + prev_clus * 4;
|
||||
if (pfo + 4 <= sizeof(f32_disk)) {
|
||||
f32_disk[pfo] = (u8)(newc & 0xFF);
|
||||
f32_disk[pfo+1] = (u8)((newc >> 8) & 0xFF);
|
||||
f32_disk[pfo+2] = (u8)((newc >> 16) & 0xFF);
|
||||
f32_disk[pfo+3] = (u8)((newc >> 24) & 0x0F);
|
||||
}
|
||||
prev_clus = newc;
|
||||
count++;
|
||||
}
|
||||
if (prev_clus >= 2) {
|
||||
u32 fo = fat_byte_off + prev_clus * 4;
|
||||
if (fo + 4 <= sizeof(f32_disk)) {
|
||||
f32_disk[fo] = 0xF8; f32_disk[fo+1] = 0xFF; f32_disk[fo+2] = 0xFF; f32_disk[fo+3] = 0x0F;
|
||||
}
|
||||
}
|
||||
/* 释放多余旧簇 */
|
||||
u32 next = 0;
|
||||
u32 fo = fat_byte_off + prev_clus * 4;
|
||||
if (fo + 4 <= sizeof(f32_disk)) next = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
while (next >= 2 && next < 0x0FFFFFF8) {
|
||||
u32 nfo = fat_byte_off + next * 4;
|
||||
u32 nn = 0;
|
||||
if (nfo + 4 <= sizeof(f32_disk)) nn = f32_r32(f32_disk + nfo) & 0x0FFFFFFF;
|
||||
f32_disk[nfo] = 0; f32_disk[nfo+1] = 0; f32_disk[nfo+2] = 0; f32_disk[nfo+3] = 0;
|
||||
next = nn;
|
||||
}
|
||||
} else {
|
||||
if (!have_free) return -4; /* 目录链满,不扩展 */
|
||||
for (u32 i = 0; i < clusters_needed; i++) {
|
||||
u32 newc = 0;
|
||||
for (u32 c = 2; c < (fat_sectors * 512) / 4; c++) {
|
||||
u32 fo = fat_byte_off + c * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
if ((f32_r32(f32_disk + fo) & 0x0FFFFFFF) == 0) { newc = c; break; }
|
||||
}
|
||||
if (newc == 0) return -3;
|
||||
if (i == 0) first_clus = newc;
|
||||
if (prev_clus >= 2) {
|
||||
u32 pfo = fat_byte_off + prev_clus * 4;
|
||||
f32_disk[pfo] = (u8)(newc & 0xFF);
|
||||
f32_disk[pfo+1] = (u8)((newc >> 8) & 0xFF);
|
||||
f32_disk[pfo+2] = (u8)((newc >> 16) & 0xFF);
|
||||
f32_disk[pfo+3] = (u8)((newc >> 24) & 0x0F);
|
||||
}
|
||||
prev_clus = newc;
|
||||
}
|
||||
if (prev_clus >= 2) {
|
||||
u32 fo = fat_byte_off + prev_clus * 4;
|
||||
f32_disk[fo] = 0xF8; f32_disk[fo+1] = 0xFF; f32_disk[fo+2] = 0xFF; f32_disk[fo+3] = 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
/* 写数据到簇(窗口内直接改 f32_disk,窗口外 RMW) */
|
||||
u32 remaining = size;
|
||||
const u8 *src = data;
|
||||
u32 cur = first_clus;
|
||||
u32 ci = 0;
|
||||
while (cur >= 2 && cur < 0x0FFFFFF8 && ci < clusters_needed) {
|
||||
u32 clba = data_lba + (cur - 2) * spc;
|
||||
u32 chunk = remaining < cluster_bytes ? remaining : cluster_bytes;
|
||||
if ((u64)clba * 512 + cluster_bytes <= 256ULL * 512) {
|
||||
u8 *dst = f32_disk + (u64)clba * 512;
|
||||
for (u32 b = 0; b < chunk; b++) dst[b] = src[b];
|
||||
for (u32 b = chunk; b < cluster_bytes; b++) dst[b] = 0;
|
||||
} else {
|
||||
if (f32_read_sectors(clba, spc, f32_cluster) != 0) return -5;
|
||||
for (u32 b = 0; b < chunk; b++) f32_cluster[b] = src[b];
|
||||
for (u32 b = chunk; b < cluster_bytes; b++) f32_cluster[b] = 0;
|
||||
if (f32_write_sectors(clba, spc, f32_cluster) != 0) return -6;
|
||||
}
|
||||
src += chunk; remaining -= chunk; ci++;
|
||||
u32 fo = fat_byte_off + cur * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
cur = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
}
|
||||
|
||||
/* 更新目录项(所在簇可能不在窗口内,需 RMW) */
|
||||
u32 entry_clus = found ? found_clus : free_slot_clus;
|
||||
u32 entry_idx = found ? found_idx : free_idx;
|
||||
u32 elba = data_lba + (entry_clus - 2) * spc;
|
||||
u8 *ebuf;
|
||||
int ebuf_windowed = ((u64)elba * 512 + cluster_bytes <= 256ULL * 512);
|
||||
if (ebuf_windowed) ebuf = f32_disk + (u64)elba * 512;
|
||||
else {
|
||||
if (f32_read_sectors(elba, spc, f32_cluster) != 0) return -12;
|
||||
ebuf = f32_cluster;
|
||||
}
|
||||
f32_dirent *e = &((f32_dirent *)ebuf)[entry_idx];
|
||||
if (!found) {
|
||||
for (int i = 0; i < 11; i++) e->name[i] = name11[i];
|
||||
e->attr = 0x20; e->ntr = 0; e->ctenth = 0; e->ctime = 0; e->cdate = 0; e->adate = 0;
|
||||
e->wtime = 0; e->wdate = 0;
|
||||
}
|
||||
e->chigh = (u16)((first_clus >> 16) & 0xFFFF);
|
||||
e->clow = (u16)(first_clus & 0xFFFF);
|
||||
e->fsize = size;
|
||||
|
||||
/* 回写 FAT(两份)+ 目录项簇 + 窗口内数据簇 */
|
||||
for (u32 f = 0; f < bpb->fc; f++) {
|
||||
u32 flba = fat_lba + f * fat_sectors;
|
||||
if (f32_write_sectors(flba, fat_sectors, f32_disk + (u64)flba * 512) != 0) return -7;
|
||||
}
|
||||
if (ebuf_windowed) {
|
||||
if (f32_write_sectors(elba, spc, f32_disk + (u64)elba * 512) != 0) return -8;
|
||||
} else {
|
||||
if (f32_write_sectors(elba, spc, f32_cluster) != 0) return -8;
|
||||
}
|
||||
cur = first_clus; ci = 0;
|
||||
while (cur >= 2 && cur < 0x0FFFFFF8 && ci < clusters_needed) {
|
||||
u32 clba = data_lba + (cur - 2) * spc;
|
||||
if ((u64)clba * 512 + cluster_bytes <= 256ULL * 512) {
|
||||
if (f32_write_sectors(clba, spc, f32_disk + (u64)clba * 512) != 0) return -9;
|
||||
}
|
||||
u32 fo = fat_byte_off + cur * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
cur = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
ci++;
|
||||
}
|
||||
f32_disk_loaded = 0; /* 下次操作重新加载,避免使用脏缓存 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 删除根目录下指定 8.3 名文件:清空簇链 + 标记目录项为 0xE5。
|
||||
* 返回 0 成功,-1 未找到,其他负值失败。 */
|
||||
static inline int f32_delete_root_file(const char *name11) {
|
||||
if (f32_disk_load() != 0) return -2;
|
||||
if (!f32_blk_write) return -10;
|
||||
if (f32_read_sectors(0, 256, f32_disk) != 0) return -3;
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
u32 spc = bpb->spc;
|
||||
u32 fat_lba = bpb->rsvd;
|
||||
u32 fat_sectors = bpb->spf;
|
||||
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
||||
u32 fat_byte_off = bpb->rsvd * 512;
|
||||
u32 root_clus = bpb->root_clus;
|
||||
|
||||
u32 root_lba = data_lba + (root_clus - 2) * spc;
|
||||
u8 *root_buf = f32_disk + (u64)root_lba * 512;
|
||||
u32 max_entries = (spc * 512) / 32;
|
||||
f32_dirent *dir = (f32_dirent *)root_buf;
|
||||
u32 found_idx = 0xFFFFFFFFu;
|
||||
for (u32 e = 0; e < max_entries; e++) {
|
||||
if (dir[e].name[0] == 0) break;
|
||||
if ((u8)dir[e].name[0] == 0xE5) continue;
|
||||
if (dir[e].attr == 0x0F) continue;
|
||||
if (dir[e].attr & 0x08) continue;
|
||||
if (f32_neq11(dir[e].name, name11)) { found_idx = e; break; }
|
||||
}
|
||||
if (found_idx == 0xFFFFFFFFu) return -1;
|
||||
|
||||
/* 释放簇链 */
|
||||
u32 cur = (u32)f32_r16((const u8*)&dir[found_idx].clow) | ((u32)f32_r16((const u8*)&dir[found_idx].chigh) << 16);
|
||||
while (cur >= 2 && cur < 0x0FFFFFF8) {
|
||||
u32 fo = fat_byte_off + cur * 4;
|
||||
u32 next = 0;
|
||||
if (fo + 4 <= sizeof(f32_disk)) next = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
f32_disk[fo] = 0; f32_disk[fo+1] = 0; f32_disk[fo+2] = 0; f32_disk[fo+3] = 0;
|
||||
cur = next;
|
||||
}
|
||||
/* 标记目录项为已删除 */
|
||||
dir[found_idx].name[0] = (char)0xE5;
|
||||
|
||||
/* 回写 FAT + 根目录 */
|
||||
for (u32 f = 0; f < bpb->fc; f++) {
|
||||
u32 flba = fat_lba + f * fat_sectors;
|
||||
if (f32_write_sectors(flba, fat_sectors, f32_disk + (u64)flba * 512) != 0) return -4;
|
||||
}
|
||||
if (f32_write_sectors(root_lba, spc, f32_disk + (u64)root_lba * 512) != 0) return -5;
|
||||
f32_disk_loaded = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 列举根目录条目回调签名:返回 0 继续,非 0 停止 */
|
||||
typedef int (*f32_list_cb)(const char *name, u32 size, u8 attr, void *user_data);
|
||||
|
||||
/* 列出根目录所有文件/目录,调用回调输出。
|
||||
* 回调返回非 0 立即停止。返回 0 成功(含回调提前停止),负值失败。 */
|
||||
static inline int f32_list_root(f32_list_cb emit, void *user_data) {
|
||||
if (f32_disk_load() != 0) return -1;
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
||||
u32 fat_byte_off = bpb->rsvd * 512;
|
||||
u32 spc = bpb->spc;
|
||||
u32 clus = bpb->root_clus;
|
||||
while (clus >= 2 && clus < 0x0FFFFFF8) {
|
||||
u32 lba = data_lba + (clus - 2) * spc;
|
||||
const u8 *cb;
|
||||
if ((u64)lba * 512 + (u64)spc * 512 <= 256ULL * 512)
|
||||
cb = f32_disk + (u64)lba * 512;
|
||||
else {
|
||||
if (f32_read_sectors(lba, 8, f32_cluster) != 0) return -4;
|
||||
cb = f32_cluster;
|
||||
}
|
||||
const f32_dirent *dir = (const f32_dirent *)cb;
|
||||
for (u32 e = 0; e * 32 < spc * 512; e++) {
|
||||
if (dir[e].name[0] == 0) goto done;
|
||||
if ((u8)dir[e].name[0] == 0xE5) continue;
|
||||
if (dir[e].attr == 0x0F) continue;
|
||||
if (dir[e].attr & 0x08) continue;
|
||||
char disp[13];
|
||||
f32_name_from_83(dir[e].name, disp);
|
||||
if (emit(disp, dir[e].fsize, dir[e].attr, user_data) != 0) goto done;
|
||||
}
|
||||
u32 fo = fat_byte_off + clus * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
clus = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
}
|
||||
done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- 子目录遍历支持(Phase 2.2 fileman 扩展) ---- */
|
||||
|
||||
/* 目录条目(供 fileman 等工具使用) */
|
||||
typedef struct {
|
||||
char name[13]; /* 8.3 显示名 */
|
||||
u32 clus; /* 首簇号 */
|
||||
u32 size; /* 文件大小(目录为 0) */
|
||||
u8 attr;
|
||||
u8 is_dir;
|
||||
u8 has_lfn; /* 1=long_name 字段有效 */
|
||||
char long_name[260]; /* LFN 长文件名(ASCII) */
|
||||
} f32_entry;
|
||||
|
||||
typedef int (*f32_list_entry_cb)(const f32_entry *e, void *user_data);
|
||||
|
||||
/* 列举指定目录簇的内容(含 LFN 长文件名)。
|
||||
* dir_clus = 0 表示根目录(自动从 BPB 取 root_clus)。
|
||||
* 回调返回非 0 立即停止。返回 0 成功,负值失败。 */
|
||||
static inline int f32_list_dir(u32 dir_clus, f32_list_entry_cb emit, void *user_data) {
|
||||
if (f32_disk_load() != 0) return -1;
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
||||
u32 fat_byte_off = bpb->rsvd * 512;
|
||||
u32 spc = bpb->spc;
|
||||
u32 clus = (dir_clus == 0) ? bpb->root_clus : dir_clus;
|
||||
/* LFN 缓冲必须在簇循环外声明:跨簇 LFN 链需要持续存活 */
|
||||
fat32_lfn_buf lfn;
|
||||
fat32_lfn_init(&lfn);
|
||||
while (clus >= 2 && clus < 0x0FFFFFF8) {
|
||||
u32 lba = data_lba + (clus - 2) * spc;
|
||||
const u8 *cb;
|
||||
if ((u64)lba * 512 + (u64)spc * 512 <= 256ULL * 512)
|
||||
cb = f32_disk + (u64)lba * 512;
|
||||
else {
|
||||
if (f32_read_sectors(lba, 8, f32_cluster) != 0) return -4;
|
||||
cb = f32_cluster;
|
||||
}
|
||||
const u8 *entries = (const u8 *)cb;
|
||||
u32 entry_count = (spc * 512) / 32;
|
||||
for (u32 e = 0; e < entry_count; e++) {
|
||||
const u8 *entry = entries + e * 32;
|
||||
if (entry[0] == 0) goto done;
|
||||
if ((u8)entry[0] == 0xE5) {
|
||||
/* 已删除项:清空 LFN 缓冲防止残留污染 */
|
||||
fat32_lfn_init(&lfn);
|
||||
continue;
|
||||
}
|
||||
const f32_dirent *de = (const f32_dirent *)entry;
|
||||
int is_short = fat32_lfn_process(&lfn, entry);
|
||||
if (!is_short) continue; /* LFN 片段,等待短名项 */
|
||||
if (de->attr & 0x08) continue; /* 卷标 */
|
||||
/* 跳过 "." 和 ".." 条目 */
|
||||
if (de->name[0] == '.' && (de->name[1] == ' ' || de->name[1] == '.'))
|
||||
continue;
|
||||
f32_entry fe;
|
||||
f32_name_from_83(de->name, fe.name);
|
||||
fe.clus = (u32)f32_r16((const u8*)&de->clow) | ((u32)f32_r16((const u8*)&de->chigh) << 16);
|
||||
fe.size = de->fsize;
|
||||
fe.attr = de->attr;
|
||||
fe.is_dir = (de->attr & 0x10) ? 1 : 0;
|
||||
fe.has_lfn = 0;
|
||||
fe.long_name[0] = 0;
|
||||
if (lfn.valid) {
|
||||
int ln = fat32_lfn_to_ascii(&lfn, fe.long_name, sizeof(fe.long_name));
|
||||
if (ln >= 0) fe.has_lfn = 1;
|
||||
}
|
||||
if (emit(&fe, user_data) != 0) goto done;
|
||||
/* 短名项处理完毕,重置 LFN 缓冲等待下一条链 */
|
||||
fat32_lfn_init(&lfn);
|
||||
}
|
||||
u32 fo = fat_byte_off + clus * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
clus = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
}
|
||||
done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 按首簇号 + 大小读取文件内容到 out_buf。
|
||||
* out_buf 至少 size 字节容量。返回 0 成功,负值失败。 */
|
||||
static inline int f32_read_file_by_clus(u32 clus, u32 size, u8 *out_buf, u32 buf_cap) {
|
||||
if (f32_disk_load() != 0) return -1;
|
||||
if (size > buf_cap) size = buf_cap;
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
||||
u32 fat_byte_off = bpb->rsvd * 512;
|
||||
u32 spc = bpb->spc;
|
||||
u32 remaining = size;
|
||||
u8 *dst = out_buf;
|
||||
u32 fc = clus;
|
||||
while (fc >= 2 && fc < 0x0FFFFFF8 && remaining > 0) {
|
||||
u32 fc_lba = data_lba + (fc - 2) * spc;
|
||||
u32 fc_bytes = spc * 512;
|
||||
if (fc_bytes > remaining) fc_bytes = remaining;
|
||||
const u8 *fb;
|
||||
if ((u64)fc_lba * 512 + (u64)spc * 512 <= 256ULL * 512)
|
||||
fb = f32_disk + (u64)fc_lba * 512;
|
||||
else {
|
||||
if (f32_read_sectors(fc_lba, 8, f32_cluster) != 0) return -7;
|
||||
fb = f32_cluster;
|
||||
}
|
||||
for (u32 b = 0; b < fc_bytes; b++) dst[b] = fb[b];
|
||||
dst += fc_bytes; remaining -= fc_bytes;
|
||||
u32 fo = fat_byte_off + fc * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
fc = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- LFN 长文件名支持(Phase 1 SYSTEM/lib 兼容层) ---- */
|
||||
|
||||
/* 在指定目录簇中按长文件名查找文件/子目录。
|
||||
* dir_clus : 起始目录簇(0=根目录)
|
||||
* long_name : 目标长文件名(ASCII,大小写不敏感);为 NULL 时仅按 8.3 短名匹配
|
||||
* is_dir : 1 只匹配子目录,0 只匹配文件
|
||||
* out_clus : 输出首簇号
|
||||
* out_size : 输出文件大小
|
||||
* 返回 0 成功,-1 未找到 / 错误。
|
||||
* LFN 缓冲跨簇遍历持续存活,支持跨簇边界的 LFN 链。 */
|
||||
static inline int f32_find_in_dir_lfn(u32 dir_clus, const char *long_name,
|
||||
int is_dir, u32 *out_clus, u32 *out_size) {
|
||||
if (f32_disk_load() != 0) return -1;
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
||||
u32 fat_byte_off = bpb->rsvd * 512;
|
||||
u32 spc = bpb->spc;
|
||||
u32 clus = (dir_clus == 0) ? bpb->root_clus : dir_clus;
|
||||
|
||||
/* LFN 缓冲必须在簇循环外声明:跨簇 LFN 链需要持续存活 */
|
||||
fat32_lfn_buf lfn;
|
||||
fat32_lfn_init(&lfn);
|
||||
|
||||
while (clus >= 2 && clus < 0x0FFFFFF8) {
|
||||
u32 lba = data_lba + (clus - 2) * spc;
|
||||
const u8 *cb;
|
||||
if ((u64)lba * 512 + (u64)spc * 512 <= 256ULL * 512)
|
||||
cb = f32_disk + (u64)lba * 512;
|
||||
else {
|
||||
if (f32_read_sectors(lba, 8, f32_cluster) != 0) return -4;
|
||||
cb = f32_cluster;
|
||||
}
|
||||
const u8 *entries = (const u8 *)cb;
|
||||
u32 entry_count = (spc * 512) / 32;
|
||||
for (u32 e = 0; e < entry_count; e++) {
|
||||
const u8 *entry = entries + e * 32;
|
||||
if (entry[0] == 0) goto not_found;
|
||||
if ((u8)entry[0] == 0xE5) {
|
||||
fat32_lfn_init(&lfn);
|
||||
continue;
|
||||
}
|
||||
const f32_dirent *de = (const f32_dirent *)entry;
|
||||
int is_short = fat32_lfn_process(&lfn, entry);
|
||||
if (!is_short) continue; /* LFN 片段 */
|
||||
if (de->attr & 0x08) {
|
||||
fat32_lfn_init(&lfn);
|
||||
continue; /* 卷标 */
|
||||
}
|
||||
int entry_is_dir = (de->attr & 0x10) ? 1 : 0;
|
||||
if (entry_is_dir != is_dir) {
|
||||
fat32_lfn_init(&lfn);
|
||||
continue;
|
||||
}
|
||||
/* 先尝试 LFN 长名匹配,再回退 8.3 匹配 */
|
||||
int matched = 0;
|
||||
if (lfn.valid && long_name) {
|
||||
char ascii[FAT32_LFN_MAX];
|
||||
int n = fat32_lfn_to_ascii(&lfn, ascii, sizeof(ascii));
|
||||
if (n >= 0 && fat32_lfn_streq_ci(ascii, long_name)) matched = 1;
|
||||
}
|
||||
if (!matched && long_name) {
|
||||
/* 用短名构造 8.3 显示串与目标比较(大小写不敏感) */
|
||||
char short_disp[13];
|
||||
fat32_lfn_short_to_str((const u8 *)de->name, short_disp);
|
||||
if (fat32_lfn_streq_ci(short_disp, long_name)) matched = 1;
|
||||
}
|
||||
if (matched) {
|
||||
*out_clus = (u32)f32_r16((const u8 *)&de->clow) |
|
||||
((u32)f32_r16((const u8 *)&de->chigh) << 16);
|
||||
*out_size = de->fsize;
|
||||
return 0;
|
||||
}
|
||||
fat32_lfn_init(&lfn);
|
||||
}
|
||||
u32 fo = fat_byte_off + clus * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
clus = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
}
|
||||
not_found:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 按 '/' 分隔的路径定位最终目录簇。
|
||||
* path : 路径(如 "lib" 或 "system/deshab64"),每段为长文件名
|
||||
* out_clus : 输出目录簇号
|
||||
* 返回 0 成功,-1 失败。空路径返回根目录簇。 */
|
||||
static inline int f32_find_path_dir_lfn(const char *path, u32 *out_clus) {
|
||||
if (!path || !out_clus) return -1;
|
||||
if (f32_disk_load() != 0) return -1;
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
u32 cur = bpb->root_clus;
|
||||
|
||||
u32 pos = 0;
|
||||
u32 path_len = 0;
|
||||
while (path[path_len]) path_len++;
|
||||
|
||||
/* 空路径或仅 "/" -> 根目录 */
|
||||
if (path_len == 0 || (path_len == 1 && path[0] == '/')) {
|
||||
*out_clus = cur;
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (pos < path_len) {
|
||||
/* 跳过前导 '/' */
|
||||
while (pos < path_len && path[pos] == '/') pos++;
|
||||
if (pos >= path_len) break;
|
||||
/* 提取路径组件(到下一个 '/' 或结尾) */
|
||||
char comp[260];
|
||||
u32 ci = 0;
|
||||
while (pos < path_len && path[pos] != '/' && ci + 1 < sizeof(comp)) {
|
||||
comp[ci++] = path[pos++];
|
||||
}
|
||||
comp[ci] = 0;
|
||||
if (ci == 0) continue;
|
||||
|
||||
u32 sub_clus = 0, sub_size = 0;
|
||||
if (f32_find_in_dir_lfn(cur, comp, 1, &sub_clus, &sub_size) != 0) return -1;
|
||||
cur = sub_clus;
|
||||
}
|
||||
*out_clus = cur;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 按长路径读取文件到 f32_data 缓冲。
|
||||
* path : '/' 分隔的长文件名路径(如 "lib/libtest.so")
|
||||
* out_data : 返回 f32_data 内的指针
|
||||
* out_size : 返回文件大小
|
||||
* 返回 0 成功,负值失败。 */
|
||||
static inline int f32_read_path_lfn(const char *path, u8 **out_data, u32 *out_size) {
|
||||
if (!path || !out_data || !out_size) return -1;
|
||||
if (f32_disk_load() != 0) return -1;
|
||||
|
||||
/* 分离目录部分与文件名 */
|
||||
u32 path_len = 0;
|
||||
while (path[path_len]) path_len++;
|
||||
u32 last_slash = 0xFFFFFFFFu;
|
||||
for (u32 i = 0; i < path_len; i++) {
|
||||
if (path[i] == '/') last_slash = i;
|
||||
}
|
||||
const char *fname = (last_slash == 0xFFFFFFFFu) ? path : path + last_slash + 1;
|
||||
|
||||
/* 定位父目录簇 */
|
||||
u32 dir_clus = 0;
|
||||
if (last_slash == 0xFFFFFFFFu) {
|
||||
/* 无 '/' -> 根目录 */
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
dir_clus = bpb->root_clus;
|
||||
} else {
|
||||
char dirpath[260];
|
||||
u32 dn = last_slash;
|
||||
if (dn >= sizeof(dirpath)) dn = sizeof(dirpath) - 1;
|
||||
for (u32 i = 0; i < dn; i++) dirpath[i] = path[i];
|
||||
dirpath[dn] = 0;
|
||||
if (f32_find_path_dir_lfn(dirpath, &dir_clus) != 0) return -2;
|
||||
}
|
||||
|
||||
/* 在父目录中查找文件 */
|
||||
u32 file_clus = 0, file_size = 0;
|
||||
if (f32_find_in_dir_lfn(dir_clus, fname, 0, &file_clus, &file_size) != 0) return -3;
|
||||
if (file_size > sizeof(f32_data)) return -4;
|
||||
|
||||
/* 读取文件数据 */
|
||||
if (f32_read_file_by_clus(file_clus, file_size, f32_data, sizeof(f32_data)) != 0) return -5;
|
||||
*out_data = f32_data;
|
||||
*out_size = file_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 按长路径写入文件(存在则替换,不存在则在父目录中新建 8.3 项)。
|
||||
* path : '/' 分隔路径(如 "system/deshab64/FUCK"),文件名段必须可转 8.3
|
||||
* 返回 0 成功,负值失败(-6 = 文件名不是合法 8.3)。 */
|
||||
static inline int f32_write_path_lfn(const char *path, const u8 *data, u32 size) {
|
||||
if (!path || !data) return -1;
|
||||
if (f32_disk_load() != 0) return -1;
|
||||
|
||||
u32 path_len = 0;
|
||||
while (path[path_len]) path_len++;
|
||||
u32 last_slash = 0xFFFFFFFFu;
|
||||
for (u32 i = 0; i < path_len; i++) {
|
||||
if (path[i] == '/') last_slash = i;
|
||||
}
|
||||
const char *fname = (last_slash == 0xFFFFFFFFu) ? path : path + last_slash + 1;
|
||||
|
||||
u32 dir_clus = 0;
|
||||
if (last_slash == 0xFFFFFFFFu) {
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
dir_clus = bpb->root_clus;
|
||||
} else {
|
||||
char dirpath[260];
|
||||
u32 dn = last_slash;
|
||||
if (dn >= sizeof(dirpath)) dn = sizeof(dirpath) - 1;
|
||||
for (u32 i = 0; i < dn; i++) dirpath[i] = path[i];
|
||||
dirpath[dn] = 0;
|
||||
if (f32_find_path_dir_lfn(dirpath, &dir_clus) != 0) return -2;
|
||||
}
|
||||
|
||||
char name11[11];
|
||||
if (f32_name_to_83(fname, name11) != 0) return -6;
|
||||
return f32_write_file_in_dir(dir_clus, name11, data, size);
|
||||
}
|
||||
|
||||
#endif /* DESHAB_FAT32_H */
|
||||
@@ -0,0 +1,91 @@
|
||||
/* deshab/fb.h - 帧缓冲绘制原语
|
||||
*
|
||||
* 整合 desktop_app.h 的 da_pixel/pixel_read/blend/fill_rect/fill_bg/
|
||||
* fill_rounded_rect/rect_outline,统一前缀为 dsb_。
|
||||
* 假设 32bpp BGRA 帧缓冲(Limine framebuffer 标准格式)。
|
||||
*/
|
||||
#ifndef DESHAB_FB_H
|
||||
#define DESHAB_FB_H
|
||||
|
||||
#include "types.h"
|
||||
#include "app.h"
|
||||
|
||||
static inline void dsb_pixel(dsb_app_context *ac, i64 x, i64 y, u32 color) {
|
||||
if (x < 0 || (u64)x >= ac->fb_w || y < 0 || (u64)y >= ac->fb_h) return;
|
||||
u32 *line = (u32 *)((u8 *)ac->fb + (u64)y * ac->fb_pitch);
|
||||
line[x] = color;
|
||||
}
|
||||
|
||||
static inline u32 dsb_pixel_read(dsb_app_context *ac, i64 x, i64 y) {
|
||||
if (x < 0 || (u64)x >= ac->fb_w || y < 0 || (u64)y >= ac->fb_h) return 0;
|
||||
u32 *line = (u32 *)((u8 *)ac->fb + (u64)y * ac->fb_pitch);
|
||||
return line[x];
|
||||
}
|
||||
|
||||
static inline u32 dsb_blend(u32 bg, u32 fg, u32 alpha) {
|
||||
u32 na = 256 - alpha;
|
||||
u32 r = ((bg & 0xFF) * na + (fg & 0xFF) * alpha) >> 8;
|
||||
u32 g = (((bg >> 8) & 0xFF) * na + ((fg >> 8) & 0xFF) * alpha) >> 8;
|
||||
u32 b = (((bg >> 16) & 0xFF) * na + ((fg >> 16) & 0xFF) * alpha) >> 8;
|
||||
return 0xFF000000u | (b << 16) | (g << 8) | r;
|
||||
}
|
||||
|
||||
static inline void dsb_fill_rect(dsb_app_context *ac, i64 x, i64 y, i64 w, i64 h, u32 color) {
|
||||
for (i64 r = 0; r < h; r++) {
|
||||
i64 yy = y + r;
|
||||
if (yy < 0 || (u64)yy >= ac->fb_h) continue;
|
||||
u32 *line = (u32 *)((u8 *)ac->fb + (u64)yy * ac->fb_pitch);
|
||||
for (i64 c = 0; c < w; c++) {
|
||||
i64 xx = x + c;
|
||||
if (xx < 0 || (u64)xx >= ac->fb_w) continue;
|
||||
line[xx] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dsb_fill_bg(dsb_app_context *ac, u32 color) {
|
||||
dsb_fill_rect(ac, 0, 0, (i64)ac->fb_w, (i64)ac->fb_h, color);
|
||||
}
|
||||
|
||||
static inline void dsb_fill_rounded_rect(dsb_app_context *ac, i64 x, i64 y, i64 w, i64 h, u32 color, i64 radius) {
|
||||
i64 r = radius;
|
||||
if (r > w / 2) r = w / 2;
|
||||
if (r > h / 2) r = h / 2;
|
||||
dsb_fill_rect(ac, x + r, y, w - 2 * r, h, color);
|
||||
dsb_fill_rect(ac, x, y + r, r, h - 2 * r, color);
|
||||
dsb_fill_rect(ac, x + w - r, y + r, r, h - 2 * r, color);
|
||||
for (i64 dy = 0; dy < r; dy++)
|
||||
for (i64 dx = 0; dx < r; dx++)
|
||||
if (dx * dx + dy * dy <= r * r) {
|
||||
dsb_pixel(ac, x + r - 1 - dx, y + r - 1 - dy, color);
|
||||
dsb_pixel(ac, x + w - r + dx, y + r - 1 - dy, color);
|
||||
dsb_pixel(ac, x + r - 1 - dx, y + h - r + dy, color);
|
||||
dsb_pixel(ac, x + w - r + dx, y + h - r + dy, color);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dsb_rect_outline(dsb_app_context *ac, i64 x, i64 y, i64 w, i64 h, u32 color, i64 radius) {
|
||||
i64 r = radius;
|
||||
if (r > w / 2) r = w / 2;
|
||||
if (r > h / 2) r = h / 2;
|
||||
for (i64 c = r; c < w - r; c++) {
|
||||
dsb_pixel(ac, x + c, y, color);
|
||||
dsb_pixel(ac, x + c, y + h - 1, color);
|
||||
}
|
||||
for (i64 rr = r; rr < h - r; rr++) {
|
||||
dsb_pixel(ac, x, y + rr, color);
|
||||
dsb_pixel(ac, x + w - 1, y + rr, color);
|
||||
}
|
||||
for (i64 dy = 0; dy <= r; dy++)
|
||||
for (i64 dx = 0; dx <= r; dx++) {
|
||||
i64 d2 = dx * dx + dy * dy;
|
||||
if (d2 <= r * r && d2 > (r - 1) * (r - 1)) {
|
||||
dsb_pixel(ac, x + r - dx, y + r - dy, color);
|
||||
dsb_pixel(ac, x + w - 1 - r + dx, y + r - dy, color);
|
||||
dsb_pixel(ac, x + r - dx, y + h - 1 - r + dy, color);
|
||||
dsb_pixel(ac, x + w - 1 - r + dx, y + h - 1 - r + dy, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DESHAB_FB_H */
|
||||
@@ -0,0 +1,51 @@
|
||||
/* deshab/font.h - ASCII 位图字体渲染
|
||||
*
|
||||
* 整合 desktop_app.h 的 da_draw_char/draw_string,统一前缀为 dsb_。
|
||||
*
|
||||
* 前置条件:包含者需先提供位图数据源。两种方式:
|
||||
* 1. #include "ascii_bitmaps.c"(定义 static g_ascii/g_ascii_w/g_ascii_h)
|
||||
* 2. 自行定义 const u8 *g_ascii[95]; i64 g_ascii_w, g_ascii_h;
|
||||
* 再 #include "font.h"。
|
||||
*
|
||||
* 与 deshab_ui.h 使用相同的包含顺序约定。
|
||||
*/
|
||||
#ifndef DESHAB_FONT_H
|
||||
#define DESHAB_FONT_H
|
||||
|
||||
#include "types.h"
|
||||
#include "app.h"
|
||||
#include "fb.h"
|
||||
|
||||
/* 由包含者在 include 本头前提供:
|
||||
* static const u8 *g_ascii[95]; // 95 字符 ' '..'~'
|
||||
* static i64 g_ascii_w, g_ascii_h;
|
||||
*/
|
||||
static inline void dsb_draw_char(dsb_app_context *ac, char ch, i64 x, i64 y, u32 fg, u32 bg) {
|
||||
u32 idx = (u32)(ch - ' ');
|
||||
if (idx > 94) idx = 0;
|
||||
const u8 *glyph = g_ascii[idx];
|
||||
i64 gw = g_ascii_w, gh = g_ascii_h;
|
||||
for (i64 r = 0; r < gh; r++) {
|
||||
i64 yy = y + r;
|
||||
if (yy < 0 || (u64)yy >= ac->fb_h) continue;
|
||||
u32 *line = (u32 *)((u8 *)ac->fb + (u64)yy * ac->fb_pitch);
|
||||
for (i64 c = 0; c < gw; c++) {
|
||||
u8 a = glyph[r * gw + c];
|
||||
if (a == 0) continue;
|
||||
i64 xx = x + c;
|
||||
if (xx < 0 || (u64)xx >= ac->fb_w) continue;
|
||||
line[xx] = (a == 255) ? fg : dsb_blend(bg, fg, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dsb_draw_string(dsb_app_context *ac, const char *s, i64 x, i64 y, u32 fg, u32 bg, i64 step) {
|
||||
i64 cx = x;
|
||||
while (*s) {
|
||||
dsb_draw_char(ac, *s, cx, y, fg, bg);
|
||||
cx += step;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DESHAB_FONT_H */
|
||||
@@ -0,0 +1,115 @@
|
||||
/* deshab/input.h - PS/2 键盘与鼠标输入
|
||||
*
|
||||
* 整合 desktop_app.h 的 da_scan_to_ascii / da_mouse / da_mouse_init /
|
||||
* da_mouse_poll / da_ps2_drain,统一前缀为 dsb_。
|
||||
*
|
||||
* PS/2 鼠标初始化序列与 desktop/main.c:ps2_mouse_init 保持一致,
|
||||
* 必须先冲刷输出缓冲再发命令,避免残留字节污染配置字
|
||||
* (BUG-20260801-008)。
|
||||
*/
|
||||
#ifndef DESHAB_INPUT_H
|
||||
#define DESHAB_INPUT_H
|
||||
|
||||
#include "types.h"
|
||||
#include "portio.h"
|
||||
#include "cursor.h"
|
||||
|
||||
/* ---- 键盘 scan code set 1 -> ASCII ---- */
|
||||
static inline char dsb_scan_to_ascii(u8 sc, int shift) {
|
||||
static const char normal[58] = {
|
||||
0, 27, '1','2','3','4','5','6','7','8','9','0','-','=', 8, '\t',
|
||||
'q','w','e','r','t','y','u','i','o','p','[',']','\n',0,
|
||||
'a','s','d','f','g','h','j','k','l',';','\'', '`',0,'\\',
|
||||
'z','x','c','v','b','n','m',',','.','/',0,'*',0,' '
|
||||
};
|
||||
static const char shifted[58] = {
|
||||
0, 27, '!','@','#','$','%','^','&','*','(',')','_','+', 8, '\t',
|
||||
'Q','W','E','R','T','Y','U','I','O','P','{','}','\n',0,
|
||||
'A','S','D','F','G','H','J','K','L',':','"','~',0,'|',
|
||||
'Z','X','C','V','B','N','M','<','>','?',0,'*',0,' '
|
||||
};
|
||||
if (sc >= 58) return 0;
|
||||
return shift ? shifted[sc] : normal[sc];
|
||||
}
|
||||
|
||||
/* ---- PS/2 鼠标 ---- */
|
||||
typedef struct dsb_mouse {
|
||||
u8 buf[3];
|
||||
int idx;
|
||||
int has_pkt;
|
||||
} dsb_mouse;
|
||||
|
||||
/* 冲刷 PS/2 控制器输出缓冲(最多 16 字节)。 */
|
||||
static inline void dsb_ps2_drain(void) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (!(dsb_inb(0x64) & 1)) break;
|
||||
dsb_inb(0x60);
|
||||
}
|
||||
}
|
||||
|
||||
/* 初始化 PS/2 鼠标:启用 AUX 端口 + 流模式 + 数据报告。
|
||||
* 序列与 desktop/main.c:ps2_mouse_init 一致。 */
|
||||
static inline void dsb_mouse_init(void) {
|
||||
dsb_ps2_drain();
|
||||
for (int t = 0; t < 100000; t++) { if (!(dsb_inb(0x64) & 2)) break; }
|
||||
dsb_outb(0x64, 0xA8);
|
||||
for (int t = 0; t < 100000; t++) { if (!(dsb_inb(0x64) & 2)) break; }
|
||||
dsb_outb(0x64, 0x20);
|
||||
for (int t = 0; t < 100000; t++) { if (dsb_inb(0x64) & 1) break; }
|
||||
u8 cfg = dsb_inb(0x60);
|
||||
cfg &= ~0x20; cfg |= 0x02; cfg |= 0x40; /* AUX 时钟启用 + AUX IRQ + 键盘翻译 */
|
||||
for (int t = 0; t < 100000; t++) { if (!(dsb_inb(0x64) & 2)) break; }
|
||||
dsb_outb(0x64, 0x60);
|
||||
for (int t = 0; t < 100000; t++) { if (!(dsb_inb(0x64) & 2)) break; }
|
||||
dsb_outb(0x60, cfg);
|
||||
for (int t = 0; t < 100000; t++) { if (!(dsb_inb(0x64) & 2)) break; }
|
||||
dsb_outb(0x64, 0xD4);
|
||||
for (int t = 0; t < 100000; t++) { if (!(dsb_inb(0x64) & 2)) break; }
|
||||
dsb_outb(0x60, 0xFF);
|
||||
for (int i = 0; i < 8; i++) { /* 读完复位全部响应(FA AA 00) */
|
||||
int got = 0;
|
||||
for (int t = 0; t < 100000; t++) { if (dsb_inb(0x64) & 1) { got = 1; break; } }
|
||||
if (!got) break;
|
||||
dsb_inb(0x60);
|
||||
}
|
||||
for (int t = 0; t < 100000; t++) { if (!(dsb_inb(0x64) & 2)) break; }
|
||||
dsb_outb(0x64, 0xD4);
|
||||
for (int t = 0; t < 100000; t++) { if (!(dsb_inb(0x64) & 2)) break; }
|
||||
dsb_outb(0x60, 0xF4);
|
||||
for (int i = 0; i < 4; i++) { /* 读完使能 ACK */
|
||||
int got = 0;
|
||||
for (int t = 0; t < 100000; t++) { if (dsb_inb(0x64) & 1) { got = 1; break; } }
|
||||
if (!got) break;
|
||||
dsb_inb(0x60);
|
||||
}
|
||||
dsb_ps2_drain();
|
||||
}
|
||||
|
||||
/* 轮询鼠标:3 字节包解码,更新光标坐标与按键状态。
|
||||
* 返回 1=收到完整包,0=无数据/未完成。
|
||||
* 必须先查 AUX 位(bit5)再读数据端口,避免吞掉键盘扫描码
|
||||
* (BUG-20260801-003)。 */
|
||||
static inline int dsb_mouse_poll(dsb_mouse *m, dsb_cursor *c, i64 fb_w, i64 fb_h) {
|
||||
u8 st = dsb_inb(0x64);
|
||||
if (!(st & 1)) return 0;
|
||||
if (!(st & 0x20)) return 0; /* 非 AUX 数据,留给键盘路径 */
|
||||
u8 data = dsb_inb(0x60);
|
||||
m->buf[m->idx++] = data;
|
||||
if (m->idx < 3) return 0;
|
||||
m->idx = 0;
|
||||
if (!(m->buf[0] & 0x08)) return 0; /* 同步位验证 */
|
||||
int dx = (int)(signed char)m->buf[1];
|
||||
int dy = (int)(signed char)m->buf[2];
|
||||
dy = -dy; /* PS/2 Y 向上为正,屏幕 Y 向下为正 */
|
||||
c->mx += dx;
|
||||
c->my += dy;
|
||||
if (c->mx < 0) c->mx = 0;
|
||||
if (c->my < 0) c->my = 0;
|
||||
if (c->mx >= (int)fb_w - DSB_CURSOR_SIZE) c->mx = (int)fb_w - DSB_CURSOR_SIZE;
|
||||
if (c->my >= (int)fb_h - DSB_CURSOR_SIZE) c->my = (int)fb_h - DSB_CURSOR_SIZE;
|
||||
c->btn = (m->buf[0] & 0x01) ? 1 : 0;
|
||||
m->has_pkt = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* DESHAB_INPUT_H */
|
||||
@@ -0,0 +1,176 @@
|
||||
/* deshab/kernel_api.h - dkm_kernel_api ABI 单一定义来源
|
||||
*
|
||||
* 整合 CODE/UTSM/include/utsm/dkm.h 与 CODE/DKM/dkm_shared.h 的 ABI 结构体,
|
||||
* 消除驱动本地重复定义 dkm_kernel_api 的隐患(CONTRIBUTING.md 硬约束)。
|
||||
*
|
||||
* 约定:
|
||||
* - 字段顺序/类型不可调整,UTSM 端按固定偏移填充。
|
||||
* - 新增字段只允许追加在结构末尾(尾部追加保持 ABI 兼容)。
|
||||
* - 结构体同时提供 typedef 名与 struct 标签,兼容两种引用方式。
|
||||
*
|
||||
* 偏移常量(供不便解引用 kernel_api 指针的场景使用,消除硬编码):
|
||||
* DSB_KAPI_OFF_NET 0x48 net API 指针
|
||||
* DSB_KAPI_OFF_BLOCK 0xA8 block API 指针
|
||||
*/
|
||||
#ifndef DESHAB_KERNEL_API_H
|
||||
#define DESHAB_KERNEL_API_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define DKM_KERNEL_API_VERSION 1u
|
||||
|
||||
/* ---- 网卡链路/能力标志 ---- */
|
||||
#define DKM_NET_F_LINK_UP (1u << 0)
|
||||
#define DKM_NET_F_TX_READY (1u << 1)
|
||||
#define DKM_NET_F_RX_READY (1u << 2)
|
||||
#define DKM_NET_F_WIRELESS (1u << 3)
|
||||
|
||||
/* ---- WiFi 扫描加密类型 ---- */
|
||||
#define DKM_NET_SEC_OPEN 0u
|
||||
#define DKM_NET_SEC_WEP 1u
|
||||
#define DKM_NET_SEC_WPA 2u
|
||||
#define DKM_NET_SEC_WPA2 3u
|
||||
#define DKM_NET_SEC_WPA3 4u
|
||||
|
||||
/* ---- log API ---- */
|
||||
typedef struct dkm_log_api {
|
||||
void (*info)(const char *msg);
|
||||
void (*warn)(const char *msg);
|
||||
void (*error)(const char *msg);
|
||||
void (*panic)(const char *msg);
|
||||
} dkm_log_api;
|
||||
|
||||
/* ---- DMA API ---- */
|
||||
typedef struct dkm_dma_buffer {
|
||||
void *virt;
|
||||
u64 phys;
|
||||
u64 size;
|
||||
} dkm_dma_buffer;
|
||||
|
||||
typedef struct dkm_dma_api {
|
||||
int (*alloc_pages)(u64 page_count, u64 alignment, u64 max_phys, dkm_dma_buffer *out);
|
||||
} dkm_dma_api;
|
||||
|
||||
/* ---- net API ---- */
|
||||
typedef struct dkm_net_scan_result {
|
||||
char ssid[33];
|
||||
u8 bssid[6];
|
||||
u8 channel;
|
||||
i8 rssi;
|
||||
u8 security;
|
||||
} dkm_net_scan_result;
|
||||
|
||||
typedef struct dkm_net_device_desc {
|
||||
const char *name;
|
||||
u8 mac[6];
|
||||
u32 flags;
|
||||
void *ctx;
|
||||
int (*tx)(void *ctx, const void *packet, u32 length);
|
||||
int (*rx_poll)(void *ctx, void *buffer, u32 capacity, u32 *out_length);
|
||||
/* 无线扩展回调(有线驱动置 NULL,向后兼容)。 */
|
||||
int (*scan_start)(void *ctx);
|
||||
int (*scan_count)(void *ctx);
|
||||
int (*scan_result)(void *ctx, u32 n, dkm_net_scan_result *out);
|
||||
int (*is_wireless)(void *ctx);
|
||||
} dkm_net_device_desc;
|
||||
|
||||
typedef struct dkm_net_stats {
|
||||
u64 tx_ok;
|
||||
u64 tx_err;
|
||||
u64 rx_ok;
|
||||
u64 rx_err;
|
||||
u64 rx_overflow;
|
||||
u64 link_changes;
|
||||
} dkm_net_stats;
|
||||
|
||||
typedef struct dkm_net_api {
|
||||
int (*register_device)(const dkm_net_device_desc *desc);
|
||||
u32 (*device_count)(void);
|
||||
int (*device_info)(u32 index, void *out);
|
||||
int (*tx)(u32 index, const void *packet, u32 length);
|
||||
int (*rx_poll)(u32 index, void *buffer, u32 capacity, u32 *out_length);
|
||||
int (*scan_start)(u32 index);
|
||||
int (*scan_count)(u32 index);
|
||||
int (*scan_result)(u32 index, u32 n, dkm_net_scan_result *out);
|
||||
int (*is_wireless)(u32 index);
|
||||
int (*device_stats)(u32 index, dkm_net_stats *out);
|
||||
} dkm_net_api;
|
||||
|
||||
/* ---- block API ---- */
|
||||
typedef int (*dkm_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer);
|
||||
typedef int (*dkm_block_write_fn)(void *ctx, u64 lba, u32 count, const void *buffer);
|
||||
|
||||
typedef struct dkm_block_device_desc {
|
||||
const char *name;
|
||||
u64 sector_size;
|
||||
u64 sector_count;
|
||||
void *ctx;
|
||||
int (*read)(void *ctx, u64 lba, u32 count, void *buffer);
|
||||
int (*write)(void *ctx, u64 lba, u32 count, const void *buffer);
|
||||
} dkm_block_device_desc;
|
||||
|
||||
typedef struct dkm_block_api {
|
||||
int (*register_device)(const dkm_block_device_desc *desc);
|
||||
u32 (*device_count)(void);
|
||||
int (*read)(u32 index, u64 lba, u32 count, void *buffer);
|
||||
int (*write)(u32 index, u64 lba, u32 count, const void *buffer);
|
||||
u64 (*sector_size)(u32 index);
|
||||
const char *(*device_name)(u32 index);
|
||||
int (*set_write_fn)(u32 index, dkm_block_write_fn fn);
|
||||
} dkm_block_api;
|
||||
|
||||
/* ---- MMIO / 页表 API ----
|
||||
* 与 UTSM/include/utsm/paging.h 布局一致。 */
|
||||
typedef struct dkm_mmio_api {
|
||||
int (*is_mapped)(u64 vaddr); /* 返回 0=未映射;1=4K PTE;2=2M PDE;3=1G PDPTE */
|
||||
int (*map_mmio)(u64 phys, u64 size); /* 映射到 hhdm_offset + phys,返回 0 成功 */
|
||||
u64 (*cr3)(void); /* 当前 CR3 物理地址(诊断用) */
|
||||
} dkm_mmio_api;
|
||||
|
||||
/* ---- kernel_api 主结构 ----
|
||||
* 字段顺序/类型不可调整 - UTSM 端按固定偏移填充。
|
||||
* DSK 以 api+0xA8 硬偏移读取 block,net_stack 以 api+0x48 读取 net。 */
|
||||
typedef struct dkm_kernel_api {
|
||||
u32 version;
|
||||
u32 size;
|
||||
u64 feature_bits;
|
||||
|
||||
const dkm_log_api *log;
|
||||
const void *mem;
|
||||
const void *utsm;
|
||||
const void *irq;
|
||||
const void *pci;
|
||||
const dkm_dma_api *dma;
|
||||
const void *vfs;
|
||||
const dkm_net_api *net;
|
||||
const void *timer;
|
||||
const void *drr;
|
||||
|
||||
const void *rsdp_address; /* ACPI RSDP physical address */
|
||||
const void *fb_address; /* framebuffer base */
|
||||
u64 fb_width;
|
||||
u64 fb_height;
|
||||
u64 fb_pitch;
|
||||
u16 fb_bpp;
|
||||
const void *boot_modules_response; /* limine_module_response* */
|
||||
int (*irq_register)(u8 irq, void *handler); /* kernel irq_register */
|
||||
u64 hhdm_offset; /* physical -> virtual */
|
||||
const dkm_block_api *block; /* block provider registry */
|
||||
const dkm_mmio_api *mmio; /* 页表/高位 MMIO 映射服务 */
|
||||
/* -- 新增字段只允许追加在末尾 -- */
|
||||
void *(*mm_map_mmio)(u64 phys, u64 size); /* MMIO 独立窗口映射(PCD|PWT),失败返回 0 */
|
||||
void (*mm_unmap_mmio)(void *virt, u64 size); /* 解除 MMIO 独立窗口映射 */
|
||||
/* B7 阶段2: LAPIC EOI 钩子注册(apic.drv LAPIC 接管时调用)。 */
|
||||
void (*register_apic_eoi)(void (*eoi_fn)(void));
|
||||
/* B7 阶段3: 动态 IDT 向量分配器(MSI/MSI-X 用),池 0x40-0xDF。 */
|
||||
int (*irq_vector_alloc)(void);
|
||||
void (*irq_vector_free)(int vector);
|
||||
} dkm_kernel_api;
|
||||
|
||||
/* ---- kernel_api 字段偏移常量(消除散落的硬编码偏移) ----
|
||||
* 供不便通过结构体解引用的场景(如早期 boot context 解析)使用。
|
||||
* 常规代码应直接通过 api->net / api->block 访问。 */
|
||||
#define DSB_KAPI_OFF_NET 0x48u /* const dkm_net_api *net */
|
||||
#define DSB_KAPI_OFF_BLOCK 0xA8u /* const dkm_block_api *block */
|
||||
|
||||
#endif /* DESHAB_KERNEL_API_H */
|
||||
@@ -0,0 +1,713 @@
|
||||
/* deshab/net.h - Deshab SDK 用户态共享网络协议栈(static inline,无库)
|
||||
*
|
||||
* Deshab SDK 的一部分。基础类型与端口 I/O / TSC / 内存操作由 SDK 头提供:
|
||||
* types.h -> u8/u16/u32/u64/i64/NULL
|
||||
* portio.h -> dsb_outb/dsb_inb
|
||||
* tsc.h -> dsb_rdtsc/dsb_tsc_per_ms/dsb_tsc_calibrate/dsb_deadline_reached
|
||||
* string.h -> dsb_memcpy/dsb_memset/dsb_strlen
|
||||
* 通过 dkm_kernel_api + 0x48 的 net API 收发原始以太帧(轮询模型,无 IRQ)。
|
||||
*
|
||||
* 能力:eth II / ARP(含应答)/ IPv4 / ICMP echo / UDP DNS / TCP 最小 client。
|
||||
* 默认拓扑:QEMU slirp - guest 10.0.2.15 / gw 10.0.2.2 / dns 10.0.2.3。
|
||||
*
|
||||
* 用法:
|
||||
* ns_init(ctx->dkm_kernel_api) - 初始化,选第一个有线 tx/rx ready 设备
|
||||
* ns_ping(ip, seq, timeout, &rtt, &ttl) - 单次 ICMP echo
|
||||
* ns_dns_resolve(host, &ip) - A 记录查询(数字 IP 直接返回)
|
||||
* ns_tcp_connect(ip, port, timeout)
|
||||
* ns_tcp_send(data, len)
|
||||
* ns_tcp_recv(buf, cap, idle_timeout) - 收至 FIN/超时,返回累计长度
|
||||
* ns_tcp_close()
|
||||
*
|
||||
* IP 以 u32 网络序数值表示(NS_IP4(10,0,2,15))。
|
||||
*/
|
||||
|
||||
#ifndef DESHAB_NET_H
|
||||
#define DESHAB_NET_H
|
||||
|
||||
#include "types.h"
|
||||
#include "portio.h"
|
||||
#include "tsc.h"
|
||||
#include "string.h"
|
||||
|
||||
/* ---- 默认网络配置(QEMU user-mode slirp) ---- */
|
||||
#define NS_IP4(a,b,c,d) ((u32)((((u32)(a))<<24)|(((u32)(b))<<16)|(((u32)(c))<<8)|((u32)(d))))
|
||||
#define NS_GUEST_IP NS_IP4(10,0,2,15)
|
||||
#define NS_GATEWAY_IP NS_IP4(10,0,2,2)
|
||||
#define NS_DNS_IP NS_IP4(10,0,2,3)
|
||||
#define NS_NETMASK NS_IP4(255,255,255,0)
|
||||
|
||||
/* 当前生效 IP 配置:默认 slirp 硬编码;netman 租约经 ns_apply_lease 覆盖。
|
||||
* 租约约定:dsk_boot_context.reserved[7] 存放指向 net_lease_info(dsk.h)的
|
||||
* 指针,结构由 DSK 常驻持有,netman 在 DHCP/静态配置成功后写入。 */
|
||||
static u32 ns_guest_ip = NS_GUEST_IP;
|
||||
static u32 ns_gateway_ip = NS_GATEWAY_IP;
|
||||
static u32 ns_dns_ip = NS_DNS_IP;
|
||||
|
||||
static inline void ns_apply_lease(u32 ip, u32 gw, u32 dns) {
|
||||
if (ip) ns_guest_ip = ip;
|
||||
if (gw) ns_gateway_ip = gw;
|
||||
if (dns) ns_dns_ip = dns;
|
||||
}
|
||||
|
||||
/* 从 boot context 读取 netman 租约(存在且 valid 则应用)。
|
||||
* ctx_u64 为 dsk_boot_context 指针值。reserved[8] 位于偏移 160
|
||||
* (第 20 个 u64 起),reserved[7] = 第 27 个 u64,存 net_lease_info 指针:
|
||||
* +0 magic("NETL" 0x4E45544C) +4 flags(bit0 valid) +8 ip +12 gw +16 dns */
|
||||
static inline void ns_apply_lease_from_ctx(u64 ctx_u64) {
|
||||
if (!ctx_u64) return;
|
||||
const u64 *ctx = (const u64 *)ctx_u64;
|
||||
u64 lease_ptr = ctx[27]; /* reserved[7] */
|
||||
if (!lease_ptr) return;
|
||||
const u32 *li = (const u32 *)lease_ptr;
|
||||
if (li[0] != 0x4E45544Cu) return; /* NET_LEASE_MAGIC */
|
||||
if (!(li[1] & 1u)) return; /* NET_LEASE_F_VALID */
|
||||
ns_apply_lease(li[2], li[3], li[4]);
|
||||
}
|
||||
|
||||
#define NS_ETH_ARP 0x0806
|
||||
#define NS_ETH_IPV4 0x0800
|
||||
#define NS_IP_ICMP 1
|
||||
#define NS_IP_TCP 6
|
||||
#define NS_IP_UDP 17
|
||||
|
||||
#define NS_ARP_CACHE_SIZE 8
|
||||
#define NS_TCP_RX_CAP 65536
|
||||
|
||||
/* dkm_net_device_desc.flags 位(与 utsm/net.h 一致) */
|
||||
#define NS_NET_F_TX_READY 0x2
|
||||
#define NS_NET_F_RX_READY 0x4
|
||||
#define NS_NET_F_WIRELESS 0x8
|
||||
|
||||
/* ---- 字节序/校验和 ---- */
|
||||
static inline void ns_w16(u8 *p, u16 v) { p[0] = (u8)(v >> 8); p[1] = (u8)v; }
|
||||
static inline void ns_w32(u8 *p, u32 v) { p[0] = (u8)(v >> 24); p[1] = (u8)(v >> 16); p[2] = (u8)(v >> 8); p[3] = (u8)v; }
|
||||
static inline u16 ns_r16(const u8 *p) { return (u16)(((u16)p[0] << 8) | p[1]); }
|
||||
static inline u32 ns_r32(const u8 *p) { return ((u32)p[0] << 24) | ((u32)p[1] << 16) | ((u32)p[2] << 8) | p[3]; }
|
||||
|
||||
static inline u16 ns_checksum(const void *data, u32 len) {
|
||||
const u8 *p = (const u8 *)data;
|
||||
u32 sum = 0;
|
||||
while (len > 1) { sum += ((u16)p[0] << 8) | p[1]; p += 2; len -= 2; }
|
||||
if (len) sum += ((u16)p[0] << 8);
|
||||
while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
return (u16)~sum;
|
||||
}
|
||||
|
||||
/* ---- 十进制/IP 字符串 ---- */
|
||||
static inline void ns_u32_dec(char *buf, u32 v) {
|
||||
char tmp[12]; int n = 0;
|
||||
if (!v) { buf[0] = '0'; buf[1] = 0; return; }
|
||||
while (v && n < 11) { tmp[n++] = (char)('0' + v % 10); v /= 10; }
|
||||
for (int i = 0; i < n; i++) buf[i] = tmp[n - 1 - i];
|
||||
buf[n] = 0;
|
||||
}
|
||||
|
||||
static inline void ns_fmt_ip(u32 ip, char *out) {
|
||||
int p = 0;
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
char d[4]; ns_u32_dec(d, (ip >> (i * 8)) & 0xFF);
|
||||
for (int j = 0; d[j]; j++) out[p++] = d[j];
|
||||
if (i) out[p++] = '.';
|
||||
}
|
||||
out[p] = 0;
|
||||
}
|
||||
|
||||
/* 解析 "a.b.c.d" -> 网络序 u32。返回 0 成功。 */
|
||||
static inline int ns_parse_ip(const char *s, u32 *out) {
|
||||
u32 v = 0;
|
||||
for (int part = 0; part < 4; part++) {
|
||||
u32 n = 0; int digits = 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
n = n * 10 + (u32)(*s - '0');
|
||||
if (n > 255) return -1;
|
||||
s++; digits++;
|
||||
}
|
||||
if (!digits) return -1;
|
||||
v = (v << 8) | n;
|
||||
if (part < 3) {
|
||||
if (*s != '.') return -1;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
if (*s && *s != ' ') return -1;
|
||||
*out = v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- net API 绑定 ---- */
|
||||
typedef int (*ns_net_tx_fn)(u32 index, const void *packet, u32 length);
|
||||
typedef int (*ns_net_rx_poll_fn)(u32 index, void *buffer, u32 capacity, u32 *out_length);
|
||||
typedef u32 (*ns_net_count_fn)(void);
|
||||
typedef int (*ns_net_info_fn)(u32 index, void *out);
|
||||
|
||||
static int ns_ready = 0;
|
||||
static u32 ns_dev_index = 0;
|
||||
static u8 ns_mac[6] = {0};
|
||||
static ns_net_tx_fn ns_net_tx = 0;
|
||||
static ns_net_rx_poll_fn ns_net_rx_poll_fn_ptr = 0;
|
||||
static u16 ns_ip_id = 1;
|
||||
|
||||
/* dkm_net_device_info 布局:name ptr @0, mac[6] @8, flags @16(对齐填充后 size 24) */
|
||||
typedef struct __attribute__((packed)) {
|
||||
u64 name;
|
||||
u8 mac[6];
|
||||
u16 pad;
|
||||
u32 flags;
|
||||
} ns_dev_info;
|
||||
|
||||
static inline int ns_init(u64 dkm_kernel_api) {
|
||||
if (ns_ready) return 0;
|
||||
if (!dkm_kernel_api) return -1;
|
||||
u64 net = *(u64 *)(dkm_kernel_api + 0x48);
|
||||
if (!net) return -2;
|
||||
ns_net_count_fn device_count = (ns_net_count_fn)*(u64 *)(net + 0x08);
|
||||
ns_net_info_fn device_info = (ns_net_info_fn)*(u64 *)(net + 0x10);
|
||||
ns_net_tx = (ns_net_tx_fn)*(u64 *)(net + 0x18);
|
||||
ns_net_rx_poll_fn_ptr = (ns_net_rx_poll_fn)*(u64 *)(net + 0x20);
|
||||
if (!device_count || !device_info || !ns_net_tx || !ns_net_rx_poll_fn_ptr) return -3;
|
||||
u32 n = device_count();
|
||||
if (!n) return -4;
|
||||
|
||||
int picked = -1;
|
||||
ns_dev_info info;
|
||||
for (u32 i = 0; i < n && i < 8; i++) {
|
||||
dsb_memset(&info, 0, sizeof(info));
|
||||
if (device_info(i, &info) != 0) continue;
|
||||
if (info.flags & NS_NET_F_WIRELESS) continue;
|
||||
if ((info.flags & NS_NET_F_TX_READY) && (info.flags & NS_NET_F_RX_READY)) {
|
||||
picked = (int)i;
|
||||
for (int k = 0; k < 6; k++) ns_mac[k] = info.mac[k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (picked < 0) {
|
||||
dsb_memset(&info, 0, sizeof(info));
|
||||
if (device_info(0, &info) != 0) return -5;
|
||||
picked = 0;
|
||||
for (int k = 0; k < 6; k++) ns_mac[k] = info.mac[k];
|
||||
}
|
||||
ns_dev_index = (u32)picked;
|
||||
dsb_tsc_calibrate();
|
||||
ns_ready = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- 帧缓冲 ---- */
|
||||
static u8 ns_txbuf[2048];
|
||||
static u8 ns_rxframe[2048];
|
||||
|
||||
/* 发送以太帧 */
|
||||
static inline int ns_send_eth(const u8 dst_mac[6], u16 ethertype, const u8 *payload, u32 len) {
|
||||
if (len + 14 > sizeof(ns_txbuf)) return -1;
|
||||
for (int i = 0; i < 6; i++) ns_txbuf[i] = dst_mac[i];
|
||||
for (int i = 0; i < 6; i++) ns_txbuf[6 + i] = ns_mac[i];
|
||||
ns_w16(ns_txbuf + 12, ethertype);
|
||||
dsb_memcpy(ns_txbuf + 14, payload, len);
|
||||
u32 total = len + 14;
|
||||
if (total < 60) { /* 以太网最小帧 */
|
||||
dsb_memset(ns_txbuf + total, 0, 60 - total);
|
||||
total = 60;
|
||||
}
|
||||
return ns_net_tx(ns_dev_index, ns_txbuf, total);
|
||||
}
|
||||
|
||||
/* ---- ARP ---- */
|
||||
typedef struct {
|
||||
u32 ip;
|
||||
u8 mac[6];
|
||||
u8 valid;
|
||||
} ns_arp_entry;
|
||||
|
||||
static ns_arp_entry ns_arp_cache[NS_ARP_CACHE_SIZE];
|
||||
static u8 ns_arp_reply_mac[6];
|
||||
static volatile int ns_arp_reply_valid = 0;
|
||||
|
||||
static inline void ns_arp_cache_put(u32 ip, const u8 mac[6]) {
|
||||
int slot = -1;
|
||||
for (int i = 0; i < NS_ARP_CACHE_SIZE; i++) {
|
||||
if (ns_arp_cache[i].valid && ns_arp_cache[i].ip == ip) { slot = i; break; }
|
||||
}
|
||||
if (slot < 0) {
|
||||
for (int i = 0; i < NS_ARP_CACHE_SIZE; i++) {
|
||||
if (!ns_arp_cache[i].valid) { slot = i; break; }
|
||||
}
|
||||
}
|
||||
if (slot < 0) slot = 0;
|
||||
ns_arp_cache[slot].ip = ip;
|
||||
for (int k = 0; k < 6; k++) ns_arp_cache[slot].mac[k] = mac[k];
|
||||
ns_arp_cache[slot].valid = 1;
|
||||
}
|
||||
|
||||
static inline int ns_arp_cache_get(u32 ip, u8 mac[6]) {
|
||||
for (int i = 0; i < NS_ARP_CACHE_SIZE; i++) {
|
||||
if (ns_arp_cache[i].valid && ns_arp_cache[i].ip == ip) {
|
||||
for (int k = 0; k < 6; k++) mac[k] = ns_arp_cache[i].mac[k];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 构造 ARP 包到 out(42 字节),oper 1=request 2=reply */
|
||||
static inline void ns_arp_build(u8 *out, u16 oper,
|
||||
const u8 sha[6], u32 spa,
|
||||
const u8 tha[6], u32 tpa) {
|
||||
ns_w16(out + 0, 1); /* htype eth */
|
||||
ns_w16(out + 2, NS_ETH_IPV4); /* ptype */
|
||||
out[4] = 6; out[5] = 4;
|
||||
ns_w16(out + 6, oper);
|
||||
for (int i = 0; i < 6; i++) out[8 + i] = sha[i];
|
||||
ns_w32(out + 14, spa);
|
||||
for (int i = 0; i < 6; i++) out[18 + i] = tha[i];
|
||||
ns_w32(out + 24, tpa);
|
||||
}
|
||||
|
||||
/* 处理收到的 ARP 包:应答对我们的请求,记录 reply。 */
|
||||
static inline void ns_arp_handle(const u8 *pkt, u32 len) {
|
||||
if (len < 28) return;
|
||||
u16 oper = ns_r16(pkt + 6);
|
||||
u32 spa = ns_r32(pkt + 14);
|
||||
u32 tpa = ns_r32(pkt + 24);
|
||||
if (oper == 1 && tpa == ns_guest_ip) {
|
||||
/* 请求我们的 IP -> 回复 */
|
||||
u8 reply[28];
|
||||
ns_arp_build(reply, 2, ns_mac, ns_guest_ip, pkt + 8, spa);
|
||||
ns_send_eth(pkt + 8, NS_ETH_ARP, reply, 28);
|
||||
} else if (oper == 2) {
|
||||
ns_arp_cache_put(spa, pkt + 8);
|
||||
for (int k = 0; k < 6; k++) ns_arp_reply_mac[k] = pkt[8 + k];
|
||||
ns_arp_reply_valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 轮询一帧;ARP 内部处理(不返回给调用者)。
|
||||
* 返回 0=收到非 ARP 帧(*out_et 为 ethertype),1=超时。 */
|
||||
static inline int ns_wait_frame(u8 *buf, u32 cap, u32 *out_len, u16 *out_et, u32 timeout_ms) {
|
||||
u64 deadline = dsb_rdtsc() + dsb_tsc_per_ms * timeout_ms;
|
||||
for (;;) {
|
||||
u32 len = 0;
|
||||
int rc = ns_net_rx_poll_fn_ptr(ns_dev_index, buf, cap, &len);
|
||||
if (rc == 0 && len >= 14) {
|
||||
u16 et = ns_r16(buf + 12);
|
||||
if (et == NS_ETH_ARP) {
|
||||
ns_arp_handle(buf + 14, len - 14);
|
||||
/* 继续等待 */
|
||||
} else {
|
||||
*out_len = len;
|
||||
*out_et = et;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (dsb_deadline_reached(deadline)) return 1;
|
||||
__asm__ volatile("pause");
|
||||
}
|
||||
}
|
||||
|
||||
/* ARP 解析 ip -> mac,3 次重传。返回 0 成功。 */
|
||||
static inline int ns_arp_resolve(u32 ip, u8 out_mac[6]) {
|
||||
if (ns_arp_cache_get(ip, out_mac) == 0) return 0;
|
||||
static const u8 bcast[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
|
||||
static const u8 zero_mac[6] = {0,0,0,0,0,0};
|
||||
for (int attempt = 0; attempt < 3; attempt++) {
|
||||
u8 req[28];
|
||||
ns_arp_build(req, 1, ns_mac, ns_guest_ip, zero_mac, ip);
|
||||
ns_arp_reply_valid = 0;
|
||||
if (ns_send_eth(bcast, NS_ETH_ARP, req, 28) != 0) continue;
|
||||
u64 deadline = dsb_rdtsc() + dsb_tsc_per_ms * 1000;
|
||||
while (!dsb_deadline_reached(deadline)) {
|
||||
u32 len = 0;
|
||||
int rc = ns_net_rx_poll_fn_ptr(ns_dev_index, ns_rxframe, sizeof(ns_rxframe), &len);
|
||||
if (rc == 0 && len >= 14 && ns_r16(ns_rxframe + 12) == NS_ETH_ARP) {
|
||||
ns_arp_handle(ns_rxframe + 14, len - 14);
|
||||
if (ns_arp_reply_valid) {
|
||||
u32 spa = ns_r32(ns_rxframe + 14 + 14);
|
||||
if (spa == ip) {
|
||||
for (int k = 0; k < 6; k++) out_mac[k] = ns_arp_reply_mac[k];
|
||||
return 0;
|
||||
}
|
||||
ns_arp_reply_valid = 0;
|
||||
}
|
||||
}
|
||||
__asm__ volatile("pause");
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 目标 IP 的下一跳 MAC(同子网直连,否则走网关)。返回 0 成功。 */
|
||||
static inline int ns_route_mac(u32 dst_ip, u8 out_mac[6]) {
|
||||
u32 next = dst_ip;
|
||||
if ((dst_ip & NS_NETMASK) != (ns_guest_ip & NS_NETMASK)) next = ns_gateway_ip;
|
||||
return ns_arp_resolve(next, out_mac);
|
||||
}
|
||||
|
||||
/* ---- IPv4 发送 ---- */
|
||||
static inline void ns_ipv4_build(u8 *out, u8 proto, u32 src, u32 dst, u32 payload_len) {
|
||||
out[0] = 0x45; /* ver=4 ihl=5 */
|
||||
out[1] = 0; /* tos */
|
||||
ns_w16(out + 2, (u16)(20 + payload_len));
|
||||
ns_w16(out + 4, ns_ip_id++);
|
||||
ns_w16(out + 6, 0x4000); /* DF */
|
||||
out[8] = 64; /* ttl */
|
||||
out[9] = proto;
|
||||
ns_w16(out + 10, 0);
|
||||
ns_w32(out + 12, src);
|
||||
ns_w32(out + 16, dst);
|
||||
ns_w16(out + 10, ns_checksum(out, 20));
|
||||
}
|
||||
|
||||
/* ---- ICMP echo(ping) ----
|
||||
* 返回 0 成功(*out_rtt_ms / *out_ttl 有效),-1 ARP 失败,-2 超时。 */
|
||||
static inline int ns_ping(u32 dst_ip, u16 seq, u32 timeout_ms, u32 *out_rtt_ms, u8 *out_ttl) {
|
||||
u8 dst_mac[6];
|
||||
if (ns_route_mac(dst_ip, dst_mac) != 0) return -1;
|
||||
|
||||
u8 pkt[20 + 8 + 32];
|
||||
ns_ipv4_build(pkt, NS_IP_ICMP, ns_guest_ip, dst_ip, 8 + 32);
|
||||
u8 *icmp = pkt + 20;
|
||||
icmp[0] = 8; /* echo request */
|
||||
icmp[1] = 0;
|
||||
ns_w16(icmp + 2, 0);
|
||||
ns_w16(icmp + 4, 0xD5AB); /* identifier */
|
||||
ns_w16(icmp + 6, seq);
|
||||
for (int i = 0; i < 32; i++) icmp[8 + i] = (u8)(0x61 + (i % 26));
|
||||
ns_w16(icmp + 2, ns_checksum(icmp, 8 + 32));
|
||||
|
||||
if (ns_send_eth(dst_mac, NS_ETH_IPV4, pkt, sizeof(pkt)) != 0) return -3;
|
||||
u64 t0 = dsb_rdtsc();
|
||||
u64 deadline = t0 + dsb_tsc_per_ms * timeout_ms;
|
||||
while (!dsb_deadline_reached(deadline)) {
|
||||
u32 len = 0;
|
||||
int rc = ns_net_rx_poll_fn_ptr(ns_dev_index, ns_rxframe, sizeof(ns_rxframe), &len);
|
||||
if (rc == 0 && len >= 14) {
|
||||
u16 et = ns_r16(ns_rxframe + 12);
|
||||
if (et == NS_ETH_ARP) {
|
||||
ns_arp_handle(ns_rxframe + 14, len - 14);
|
||||
} else if (et == NS_ETH_IPV4 && len >= 14 + 20 + 8) {
|
||||
const u8 *ip = ns_rxframe + 14;
|
||||
u8 ihl = (u8)((ip[0] & 0x0F) * 4);
|
||||
if (ihl < 20 || len < 14 + ihl + 8) continue;
|
||||
if (ip[9] != NS_IP_ICMP) continue;
|
||||
const u8 *ic = ip + ihl;
|
||||
if (ic[0] == 0 && ns_r16(ic + 4) == 0xD5AB && ns_r16(ic + 6) == seq) {
|
||||
u64 dt = dsb_rdtsc() - t0;
|
||||
if (out_rtt_ms) {
|
||||
u32 ms = (u32)(dt / dsb_tsc_per_ms);
|
||||
*out_rtt_ms = ms;
|
||||
}
|
||||
if (out_ttl) *out_ttl = ip[8];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
__asm__ volatile("pause");
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* ---- UDP DNS A 查询 ----
|
||||
* host 为数字 IP 时直接解析返回。返回 0 成功。 */
|
||||
static inline int ns_dns_resolve(const char *host, u32 *out_ip) {
|
||||
if (ns_parse_ip(host, out_ip) == 0) return 0;
|
||||
|
||||
u8 dst_mac[6];
|
||||
if (ns_route_mac(ns_dns_ip, dst_mac) != 0) return -1;
|
||||
|
||||
/* 构造查询:header + qname + qtype/qclass */
|
||||
u8 q[512];
|
||||
u16 txid = (u16)(dsb_rdtsc() & 0xFFFF);
|
||||
if (!txid) txid = 0x1234;
|
||||
ns_w16(q + 0, txid);
|
||||
ns_w16(q + 2, 0x0100); /* RD */
|
||||
ns_w16(q + 4, 1); /* qdcount */
|
||||
ns_w16(q + 6, 0); ns_w16(q + 8, 0); ns_w16(q + 10, 0);
|
||||
u32 p = 12;
|
||||
u32 i = 0;
|
||||
while (host[i]) {
|
||||
u32 start = p++;
|
||||
u8 n = 0;
|
||||
while (host[i] && host[i] != '.') {
|
||||
if (n >= 63 || p >= 500) return -2;
|
||||
q[p++] = (u8)host[i++]; n++;
|
||||
}
|
||||
q[start] = n;
|
||||
if (host[i] == '.') i++;
|
||||
}
|
||||
q[p++] = 0;
|
||||
ns_w16(q + p, 1); p += 2; /* A */
|
||||
ns_w16(q + p, 1); p += 2; /* IN */
|
||||
u32 qlen = p;
|
||||
|
||||
u8 pkt[20 + 8 + 512];
|
||||
ns_ipv4_build(pkt, NS_IP_UDP, ns_guest_ip, ns_dns_ip, 8 + qlen);
|
||||
u8 *udp = pkt + 20;
|
||||
ns_w16(udp + 0, 49153);
|
||||
ns_w16(udp + 2, 53);
|
||||
ns_w16(udp + 4, (u16)(8 + qlen));
|
||||
ns_w16(udp + 6, 0); /* 不校验 UDP checksum */
|
||||
dsb_memcpy(udp + 8, q, qlen);
|
||||
|
||||
for (int attempt = 0; attempt < 2; attempt++) {
|
||||
if (ns_send_eth(dst_mac, NS_ETH_IPV4, pkt, 20 + 8 + qlen) != 0) continue;
|
||||
u64 deadline = dsb_rdtsc() + dsb_tsc_per_ms * 3000;
|
||||
while (!dsb_deadline_reached(deadline)) {
|
||||
u32 len = 0;
|
||||
int rc = ns_net_rx_poll_fn_ptr(ns_dev_index, ns_rxframe, sizeof(ns_rxframe), &len);
|
||||
if (rc == 0 && len >= 14) {
|
||||
u16 et = ns_r16(ns_rxframe + 12);
|
||||
if (et == NS_ETH_ARP) {
|
||||
ns_arp_handle(ns_rxframe + 14, len - 14);
|
||||
} else if (et == NS_ETH_IPV4 && len >= 14 + 20 + 8 + 12) {
|
||||
const u8 *ip = ns_rxframe + 14;
|
||||
u8 ihl = (u8)((ip[0] & 0x0F) * 4);
|
||||
if (ihl < 20 || ip[9] != NS_IP_UDP) continue;
|
||||
const u8 *ru = ip + ihl;
|
||||
if (ns_r16(ru + 2) != 49153) continue;
|
||||
const u8 *d = ru + 8;
|
||||
if (ns_r16(d + 0) != txid) continue;
|
||||
u16 qd = ns_r16(d + 4);
|
||||
u16 an = ns_r16(d + 6);
|
||||
u32 off = 12;
|
||||
/* 跳过问题段 */
|
||||
for (u16 qq = 0; qq < qd; qq++) {
|
||||
while (off < len) {
|
||||
u8 b = d[off];
|
||||
if (b == 0) { off++; break; }
|
||||
if ((b & 0xC0) == 0xC0) { off += 2; break; }
|
||||
off += 1 + b;
|
||||
}
|
||||
off += 4;
|
||||
}
|
||||
/* 遍历应答 RR */
|
||||
for (u16 rr = 0; rr < an; rr++) {
|
||||
while (off < len) { /* 跳过 name */
|
||||
u8 b = d[off];
|
||||
if (b == 0) { off++; break; }
|
||||
if ((b & 0xC0) == 0xC0) { off += 2; break; }
|
||||
off += 1 + b;
|
||||
}
|
||||
if (off + 10 > len) break;
|
||||
u16 rtype = ns_r16(d + off);
|
||||
u16 rdlen = ns_r16(d + off + 8);
|
||||
off += 10;
|
||||
if (rtype == 1 && rdlen == 4 && off + 4 <= len) {
|
||||
*out_ip = ns_r32(d + off);
|
||||
return 0;
|
||||
}
|
||||
off += rdlen;
|
||||
}
|
||||
}
|
||||
}
|
||||
__asm__ volatile("pause");
|
||||
}
|
||||
}
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* ---- TCP 最小 client ---- */
|
||||
#define NS_TCP_CLOSED 0
|
||||
#define NS_TCP_SYN_SENT 1
|
||||
#define NS_TCP_ESTAB 2
|
||||
#define NS_TCP_FIN_WAIT 3
|
||||
|
||||
static struct {
|
||||
u32 state;
|
||||
u32 dst_ip;
|
||||
u16 dst_port;
|
||||
u16 src_port;
|
||||
u32 snd_nxt;
|
||||
u32 rcv_nxt;
|
||||
u8 dst_mac[6];
|
||||
} ns_tc = {0, 0, 0, 0, 0, 0, {0}};
|
||||
|
||||
static u8 ns_tcp_rx[NS_TCP_RX_CAP];
|
||||
static u32 ns_tcp_rx_len = 0;
|
||||
static volatile int ns_tcp_got_fin = 0;
|
||||
static volatile int ns_tcp_got_rst = 0;
|
||||
|
||||
#define NS_TCP_FIN 0x01
|
||||
#define NS_TCP_SYN 0x02
|
||||
#define NS_TCP_RST 0x04
|
||||
#define NS_TCP_PSH 0x08
|
||||
#define NS_TCP_ACK 0x10
|
||||
|
||||
/* 构造并发送 TCP 段(data 可为 0)。 */
|
||||
static inline int ns_tcp_emit(u8 flags, const u8 *data, u32 len, u32 seq) {
|
||||
u8 pkt[20 + 20 + 1460];
|
||||
if (len > 1460) return -1;
|
||||
u32 tcp_len = 20 + len;
|
||||
ns_ipv4_build(pkt, NS_IP_TCP, ns_guest_ip, ns_tc.dst_ip, tcp_len);
|
||||
u8 *t = pkt + 20;
|
||||
ns_w16(t + 0, ns_tc.src_port);
|
||||
ns_w16(t + 2, ns_tc.dst_port);
|
||||
ns_w32(t + 4, seq);
|
||||
ns_w32(t + 8, (flags & NS_TCP_ACK) ? ns_tc.rcv_nxt : 0);
|
||||
t[12] = (5 << 4); /* data offset */
|
||||
t[13] = flags;
|
||||
ns_w16(t + 14, 64240); /* window */
|
||||
ns_w16(t + 16, 0);
|
||||
ns_w16(t + 18, 0);
|
||||
if (len) dsb_memcpy(t + 20, data, len);
|
||||
/* 伪头部校验和 */
|
||||
u8 ph[12];
|
||||
ns_w32(ph + 0, ns_guest_ip);
|
||||
ns_w32(ph + 4, ns_tc.dst_ip);
|
||||
ph[8] = 0; ph[9] = NS_IP_TCP;
|
||||
ns_w16(ph + 10, (u16)tcp_len);
|
||||
u32 sum = 0;
|
||||
for (u32 k = 0; k < 12; k += 2) sum += ((u16)ph[k] << 8) | ph[k + 1];
|
||||
for (u32 k = 0; k + 1 < tcp_len; k += 2) sum += ((u16)t[k] << 8) | t[k + 1];
|
||||
if (tcp_len & 1) sum += ((u16)t[tcp_len - 1] << 8);
|
||||
while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
ns_w16(t + 16, (u16)~sum);
|
||||
return ns_send_eth(ns_tc.dst_mac, NS_ETH_IPV4, pkt, 20 + tcp_len);
|
||||
}
|
||||
|
||||
/* 处理收到的 IPv4/TCP 段。返回 1 表示属于本连接并已处理。 */
|
||||
static inline int ns_tcp_process(const u8 *ip, u32 ip_len) {
|
||||
if (ip_len < 20) return 0;
|
||||
u8 ihl = (u8)((ip[0] & 0x0F) * 4);
|
||||
if (ihl < 20 || ip_len < ihl + 20) return 0;
|
||||
if (ip[9] != NS_IP_TCP) return 0;
|
||||
if (ns_r32(ip + 12) != ns_tc.dst_ip) return 0;
|
||||
const u8 *t = ip + ihl;
|
||||
if (ns_r16(t + 0) != ns_tc.dst_port) return 0;
|
||||
if (ns_r16(t + 2) != ns_tc.src_port) return 0;
|
||||
|
||||
u32 seq = ns_r32(t + 4);
|
||||
u32 ack = ns_r32(t + 8);
|
||||
u8 doff = (u8)((t[12] >> 4) * 4);
|
||||
u8 flags = t[13];
|
||||
u16 ip_total = ns_r16(ip + 2);
|
||||
u32 data_len = (u32)ip_total - ihl - doff;
|
||||
const u8 *data = t + doff;
|
||||
|
||||
if (flags & NS_TCP_RST) { ns_tcp_got_rst = 1; ns_tc.state = NS_TCP_CLOSED; return 1; }
|
||||
|
||||
if (ns_tc.state == NS_TCP_SYN_SENT) {
|
||||
if ((flags & (NS_TCP_SYN | NS_TCP_ACK)) == (NS_TCP_SYN | NS_TCP_ACK)) {
|
||||
ns_tc.rcv_nxt = seq + 1;
|
||||
ns_tc.snd_nxt = ack;
|
||||
ns_tc.state = NS_TCP_ESTAB;
|
||||
ns_tcp_emit(NS_TCP_ACK, 0, 0, ns_tc.snd_nxt);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ns_tc.state == NS_TCP_ESTAB || ns_tc.state == NS_TCP_FIN_WAIT) {
|
||||
if (data_len > 0) {
|
||||
if (seq == ns_tc.rcv_nxt) {
|
||||
u32 room = NS_TCP_RX_CAP - ns_tcp_rx_len;
|
||||
u32 take = data_len < room ? data_len : room;
|
||||
dsb_memcpy(ns_tcp_rx + ns_tcp_rx_len, data, take);
|
||||
ns_tcp_rx_len += take;
|
||||
ns_tc.rcv_nxt += data_len;
|
||||
ns_tcp_emit(NS_TCP_ACK, 0, 0, ns_tc.snd_nxt);
|
||||
} else {
|
||||
/* 乱序超前或重复段:重 ACK 当前期望序号 */
|
||||
ns_tcp_emit(NS_TCP_ACK, 0, 0, ns_tc.snd_nxt);
|
||||
}
|
||||
}
|
||||
if (flags & NS_TCP_FIN) {
|
||||
if (seq + data_len == ns_tc.rcv_nxt || data_len == 0) {
|
||||
ns_tc.rcv_nxt = seq + data_len + 1;
|
||||
ns_tcp_emit(NS_TCP_ACK, 0,0, ns_tc.snd_nxt);
|
||||
ns_tcp_got_fin = 1;
|
||||
ns_tc.state = NS_TCP_CLOSED;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 在超时窗口内收帧并喂给 TCP;返回 0=收到本连接段,1=超时。 */
|
||||
static inline int ns_tcp_wait(u32 timeout_ms) {
|
||||
u64 deadline = dsb_rdtsc() + dsb_tsc_per_ms * timeout_ms;
|
||||
while (!dsb_deadline_reached(deadline)) {
|
||||
u32 len = 0;
|
||||
int rc = ns_net_rx_poll_fn_ptr(ns_dev_index, ns_rxframe, sizeof(ns_rxframe), &len);
|
||||
if (rc == 0 && len >= 14) {
|
||||
u16 et = ns_r16(ns_rxframe + 12);
|
||||
if (et == NS_ETH_ARP) {
|
||||
ns_arp_handle(ns_rxframe + 14, len - 14);
|
||||
} else if (et == NS_ETH_IPV4) {
|
||||
if (ns_tcp_process(ns_rxframe + 14, len - 14)) return 0;
|
||||
}
|
||||
}
|
||||
__asm__ volatile("pause");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 返回 0 成功(state=ESTAB),-1 ARP 失败,-2 超时/RST。 */
|
||||
static inline int ns_tcp_connect(u32 dst_ip, u16 dst_port, u32 timeout_ms) {
|
||||
if (ns_route_mac(dst_ip, ns_tc.dst_mac) != 0) return -1;
|
||||
ns_tc.dst_ip = dst_ip;
|
||||
ns_tc.dst_port = dst_port;
|
||||
ns_tc.src_port = (u16)(49152 + (dsb_rdtsc() & 0x0FFF));
|
||||
u32 iss = (u32)dsb_rdtsc();
|
||||
ns_tcp_rx_len = 0;
|
||||
ns_tcp_got_fin = 0;
|
||||
ns_tcp_got_rst = 0;
|
||||
|
||||
for (int attempt = 0; attempt < 3; attempt++) {
|
||||
ns_tc.state = NS_TCP_SYN_SENT;
|
||||
if (ns_tcp_emit(NS_TCP_SYN, 0, 0, iss) != 0) continue;
|
||||
u64 deadline = dsb_rdtsc() + dsb_tsc_per_ms * timeout_ms;
|
||||
while (!dsb_deadline_reached(deadline)) {
|
||||
if (ns_tcp_got_rst) { ns_tc.state = NS_TCP_CLOSED; return -2; }
|
||||
if (ns_tc.state == NS_TCP_ESTAB) return 0;
|
||||
ns_tcp_wait(50);
|
||||
}
|
||||
}
|
||||
ns_tc.state = NS_TCP_CLOSED;
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* 发送数据并等待 ACK(数据段可能在等待期间到达,已入缓冲)。 */
|
||||
static inline int ns_tcp_send(const u8 *data, u32 len) {
|
||||
if (ns_tc.state != NS_TCP_ESTAB) return -1;
|
||||
for (int attempt = 0; attempt < 3; attempt++) {
|
||||
u32 expect = ns_tc.snd_nxt + len;
|
||||
if (ns_tcp_emit((u8)(NS_TCP_PSH | NS_TCP_ACK), data, len, ns_tc.snd_nxt) != 0) return -2;
|
||||
u64 deadline = dsb_rdtsc() + dsb_tsc_per_ms * 2000;
|
||||
while (!dsb_deadline_reached(deadline)) {
|
||||
if (ns_tcp_got_rst) { ns_tc.state = NS_TCP_CLOSED; return -3; }
|
||||
/* 检查 snd_una 是否被 ACK 推进:ns_tcp_process 不记录 una,
|
||||
* 简单起见此处认为发出后收到任何本连接段即推进。
|
||||
* 对 HTTP/1.0 请求-响应模式足够(对端 ACK+数据一起回来)。 */
|
||||
if (ns_tcp_wait(100) == 0) {
|
||||
ns_tc.snd_nxt = expect;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -4;
|
||||
}
|
||||
|
||||
/* 收至 FIN 或空闲超时;把累计数据拷到 buf(最多 cap)。返回长度或负值。 */
|
||||
static inline int ns_tcp_recv(u8 *buf, u32 cap, u32 idle_timeout_ms) {
|
||||
u64 idle_deadline = dsb_rdtsc() + dsb_tsc_per_ms * idle_timeout_ms;
|
||||
for (;;) {
|
||||
if (ns_tcp_got_rst) return -1;
|
||||
if (ns_tcp_got_fin) break;
|
||||
if (dsb_deadline_reached(idle_deadline)) break;
|
||||
if (ns_tcp_wait(100) == 0) {
|
||||
idle_deadline = dsb_rdtsc() + dsb_tsc_per_ms * idle_timeout_ms;
|
||||
}
|
||||
}
|
||||
u32 n = ns_tcp_rx_len < cap ? ns_tcp_rx_len : cap;
|
||||
if (buf && n) dsb_memcpy(buf, ns_tcp_rx, n);
|
||||
return (int)n;
|
||||
}
|
||||
|
||||
static inline void ns_tcp_close(void) {
|
||||
if (ns_tc.state == NS_TCP_ESTAB) {
|
||||
ns_tcp_emit((u8)(NS_TCP_FIN | NS_TCP_ACK), 0, 0, ns_tc.snd_nxt);
|
||||
}
|
||||
ns_tc.state = NS_TCP_CLOSED;
|
||||
}
|
||||
|
||||
#endif /* DESHAB_NET_H */
|
||||
@@ -0,0 +1,40 @@
|
||||
/* deshab/portio.h - 端口 I/O 内联函数
|
||||
*
|
||||
* 统一 dkm_shared.h 的 dkm_outb/inb/outl/inl 与 desktop_app.h 的 outb/inb 两套实现。
|
||||
* 宿主已自带端口 I/O 声明时,包含前 #define DSB_NO_PORTIO 跳过本段。
|
||||
*/
|
||||
#ifndef DESHAB_PORTIO_H
|
||||
#define DESHAB_PORTIO_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#ifndef DSB_NO_PORTIO
|
||||
|
||||
static inline void dsb_outb(u16 port, u8 val) {
|
||||
__asm__ volatile("outb %0, %1" :: "a"(val), "Nd"(port));
|
||||
}
|
||||
static inline u8 dsb_inb(u16 port) {
|
||||
u8 v;
|
||||
__asm__ volatile("inb %1, %0" : "=a"(v) : "Nd"(port));
|
||||
return v;
|
||||
}
|
||||
static inline void dsb_outw(u16 port, u16 val) {
|
||||
__asm__ volatile("outw %0, %1" :: "a"(val), "Nd"(port));
|
||||
}
|
||||
static inline u16 dsb_inw(u16 port) {
|
||||
u16 v;
|
||||
__asm__ volatile("inw %1, %0" : "=a"(v) : "Nd"(port));
|
||||
return v;
|
||||
}
|
||||
static inline void dsb_outl(u16 port, u32 val) {
|
||||
__asm__ volatile("outl %0, %1" :: "a"(val), "Nd"(port));
|
||||
}
|
||||
static inline u32 dsb_inl(u16 port) {
|
||||
u32 v;
|
||||
__asm__ volatile("inl %1, %0" : "=a"(v) : "Nd"(port));
|
||||
return v;
|
||||
}
|
||||
|
||||
#endif /* DSB_NO_PORTIO */
|
||||
|
||||
#endif /* DESHAB_PORTIO_H */
|
||||
@@ -0,0 +1,20 @@
|
||||
/* deshab/rtc.h - CMOS RTC 时钟读取
|
||||
*
|
||||
* 整合 desktop_app.h 的 da_rtc_time。读取 CMOS RTC 时/分(BCD -> 二进制)。
|
||||
*/
|
||||
#ifndef DESHAB_RTC_H
|
||||
#define DESHAB_RTC_H
|
||||
|
||||
#include "types.h"
|
||||
#include "portio.h"
|
||||
|
||||
static inline void dsb_rtc_time(u8 *h, u8 *m) {
|
||||
dsb_outb(0x70, 4);
|
||||
*h = dsb_inb(0x71);
|
||||
dsb_outb(0x70, 2);
|
||||
*m = dsb_inb(0x71);
|
||||
*h = (u8)((*h >> 4) * 10 + (*h & 0xF));
|
||||
*m = (u8)((*m >> 4) * 10 + (*m & 0xF));
|
||||
}
|
||||
|
||||
#endif /* DESHAB_RTC_H */
|
||||
@@ -0,0 +1,36 @@
|
||||
/* deshab/serial.h - COM1 串口日志
|
||||
*
|
||||
* 整合 desktop_app.h 的 da_sputc/swrite/slog,统一前缀为 dsb_。
|
||||
* 用于无内核 log API 可用的早期阶段(应用启动、驱动 init 前)。
|
||||
*/
|
||||
#ifndef DESHAB_SERIAL_H
|
||||
#define DESHAB_SERIAL_H
|
||||
|
||||
#include "types.h"
|
||||
#include "portio.h"
|
||||
|
||||
#define DSB_COM1 0x3F8
|
||||
|
||||
static inline void dsb_sputc(char c) {
|
||||
for (unsigned i = 0; i < 100000; i++) {
|
||||
if (dsb_inb(DSB_COM1 + 5) & 0x20) break;
|
||||
}
|
||||
dsb_outb(DSB_COM1, (u8)c);
|
||||
}
|
||||
|
||||
static inline void dsb_swrite(const char *s) {
|
||||
while (*s) {
|
||||
if (*s == '\n') dsb_sputc('\r');
|
||||
dsb_sputc(*s++);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dsb_slog(const char *tag, const char *msg) {
|
||||
dsb_swrite("[");
|
||||
dsb_swrite(tag);
|
||||
dsb_swrite("] ");
|
||||
dsb_swrite(msg);
|
||||
dsb_swrite("\n");
|
||||
}
|
||||
|
||||
#endif /* DESHAB_SERIAL_H */
|
||||
@@ -0,0 +1,30 @@
|
||||
/* deshab/string.h - 基础内存操作(无 libc 依赖)
|
||||
*
|
||||
* 整合 desktop_app.h 的 da_memset/memcpy,统一前缀为 dsb_。
|
||||
* SAS-R0 单地址空间模型下应用/驱动无 libc,需自备基础内存操作。
|
||||
*/
|
||||
#ifndef DESHAB_STRING_H
|
||||
#define DESHAB_STRING_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
static inline void *dsb_memset(void *d, int c, u64 n) {
|
||||
u8 *p = (u8 *)d;
|
||||
while (n--) *p++ = (u8)c;
|
||||
return d;
|
||||
}
|
||||
|
||||
static inline void *dsb_memcpy(void *d, const void *s, u64 n) {
|
||||
u8 *dd = (u8 *)d;
|
||||
const u8 *ss = (const u8 *)s;
|
||||
while (n--) *dd++ = *ss++;
|
||||
return d;
|
||||
}
|
||||
|
||||
static inline u64 dsb_strlen(const char *s) {
|
||||
u64 n = 0;
|
||||
while (s[n]) n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif /* DESHAB_STRING_H */
|
||||
@@ -0,0 +1,89 @@
|
||||
/* deshab/tsc.h - TSC 时钟校准与延时
|
||||
*
|
||||
* 整合 dkm_shared.h 的 dkm_tsc_calibrate/delay_ms/delay_us/serial_wait_tx
|
||||
* 与 net_stack.h 的 ns_tsc_calibrate/rdtsc,统一前缀为 dsb_。
|
||||
*
|
||||
* 用法:
|
||||
* dsb_tsc_calibrate() - 在模块入口开头调用,PIT ch0 ~10ms 校准 TSC
|
||||
* dsb_rdtsc() - 读取 TSC 时间戳
|
||||
* dsb_delay_ms/us() - 基于 TSC 的忙等延时
|
||||
* dsb_deadline_reached()- 截止时间判定
|
||||
* dsb_serial_wait_tx() - 串口发送就绪等待(100us 超时)
|
||||
*
|
||||
* 每个 .elf/.drv 包含本头后获得独立 file-local dsb_tsc_per_ms 副本。
|
||||
*/
|
||||
#ifndef DESHAB_TSC_H
|
||||
#define DESHAB_TSC_H
|
||||
|
||||
#include "types.h"
|
||||
#include "portio.h"
|
||||
|
||||
static u64 dsb_tsc_per_ms = 0;
|
||||
|
||||
static inline u64 dsb_rdtsc(void) {
|
||||
u32 lo, hi;
|
||||
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
|
||||
return ((u64)hi << 32) | lo;
|
||||
}
|
||||
|
||||
/* PIT ch0 + TSC 校准 ~10ms 区间,推算每毫秒 TSC 周期数。 */
|
||||
static inline void dsb_tsc_calibrate(void) {
|
||||
if (dsb_tsc_per_ms) return;
|
||||
dsb_outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */
|
||||
dsb_outb(0x40, 0x7C); /* 11932 low = ~10ms */
|
||||
dsb_outb(0x40, 0x2E); /* 11932 high */
|
||||
u64 tsc_start = dsb_rdtsc();
|
||||
u16 prev = 0;
|
||||
u64 loops = 0;
|
||||
for (;;) {
|
||||
dsb_outb(0x43, 0x00);
|
||||
u16 cur = (u16)dsb_inb(0x40) | ((u16)dsb_inb(0x40) << 8);
|
||||
if (cur > prev && loops > 10) break;
|
||||
prev = cur;
|
||||
loops++;
|
||||
}
|
||||
dsb_tsc_per_ms = (dsb_rdtsc() - tsc_start) / 10;
|
||||
if (!dsb_tsc_per_ms) dsb_tsc_per_ms = 3000; /* 兜底 ~3GHz */
|
||||
}
|
||||
|
||||
static inline int dsb_deadline_reached(u64 deadline) {
|
||||
return (i64)(dsb_rdtsc() - deadline) >= 0;
|
||||
}
|
||||
|
||||
static inline void dsb_delay_ms(u32 ms) {
|
||||
if (!dsb_tsc_per_ms) {
|
||||
for (volatile u32 i = 0; i < 100000 * ms; i++) __asm__ volatile("pause");
|
||||
return;
|
||||
}
|
||||
u64 target = dsb_tsc_per_ms * ms;
|
||||
u64 start = dsb_rdtsc();
|
||||
while (dsb_rdtsc() - start < target) __asm__ volatile("pause");
|
||||
}
|
||||
|
||||
static inline void dsb_delay_us(u32 us) {
|
||||
if (!dsb_tsc_per_ms) {
|
||||
for (volatile u32 i = 0; i < 100 * us; i++) __asm__ volatile("pause");
|
||||
return;
|
||||
}
|
||||
u64 target = dsb_tsc_per_ms * us / 1000;
|
||||
u64 start = dsb_rdtsc();
|
||||
while (dsb_rdtsc() - start < target) __asm__ volatile("pause");
|
||||
}
|
||||
|
||||
/* 串口发送就绪等待(100us 超时)。com_port = 0x3F8 (COM1)。 */
|
||||
static inline void dsb_serial_wait_tx(u16 com_port) {
|
||||
if (!dsb_tsc_per_ms) {
|
||||
for (volatile u32 i = 0; i < 100000; i++) {
|
||||
if (dsb_inb((u16)(com_port + 5)) & 0x20) break;
|
||||
__asm__ volatile("pause");
|
||||
}
|
||||
return;
|
||||
}
|
||||
u64 deadline = dsb_rdtsc() + dsb_tsc_per_ms / 10;
|
||||
while (dsb_rdtsc() < deadline) {
|
||||
if (dsb_inb((u16)(com_port + 5)) & 0x20) break;
|
||||
__asm__ volatile("pause");
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DESHAB_TSC_H */
|
||||
@@ -0,0 +1,23 @@
|
||||
/* deshab/types.h - Deshab SDK 基础类型定义
|
||||
*
|
||||
* 单一来源:所有 SDK 头文件与应用/驱动均通过本头获取基础类型,
|
||||
* 消除 dkm_shared.h / desktop_app.h / net_stack.h 各自重复定义的碎片化。
|
||||
* 布局与 CODE/UTSM/include/utsm/types.h 保持一致。
|
||||
*/
|
||||
#ifndef DESHAB_TYPES_H
|
||||
#define DESHAB_TYPES_H
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef signed char i8;
|
||||
typedef short i16;
|
||||
typedef int i32;
|
||||
typedef long long i64;
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void *)0)
|
||||
#endif
|
||||
|
||||
#endif /* DESHAB_TYPES_H */
|
||||
@@ -0,0 +1,124 @@
|
||||
/* deshab/ui.h - 窗口装饰与色板
|
||||
*
|
||||
* 整合 desktop_app.h 的色板常量与 da_draw_titlebar/draw_statusbar,
|
||||
* 以及 LINUXAPP.CNF 解析契约,统一前缀为 dsb_。
|
||||
*
|
||||
* 独立工具应用全屏运行时的窗口装饰:标题栏 + 关闭按钮 + 状态栏。
|
||||
*/
|
||||
#ifndef DESHAB_UI_H
|
||||
#define DESHAB_UI_H
|
||||
|
||||
#include "types.h"
|
||||
#include "app.h"
|
||||
#include "fb.h"
|
||||
#include "font.h"
|
||||
|
||||
/* ---- 色板(与 deshab_ui.h DP_* 一致) ---- */
|
||||
#define DSB_BG_PRIMARY 0xFF0A1428u /* DP_ABYSS_900 */
|
||||
#define DSB_BG_SECONDARY 0xFF0F1E38u /* DP_ABYSS_800 */
|
||||
#define DSB_BG_TERTIARY 0xFF162848u /* DP_ABYSS_700 */
|
||||
#define DSB_TEXT_PRIMARY 0xFFE8E8F0u /* DP_NEUTRAL_700 */
|
||||
#define DSB_TEXT_DIM 0xFF6A6A88u /* DP_NEUTRAL_300 */
|
||||
#define DSB_ACCENT 0xFF00A8CCu /* DP_SEAL_500 */
|
||||
#define DSB_ACCENT_LIGHT 0xFF44CCF0u /* DP_SEAL_300 */
|
||||
#define DSB_BORDER 0xFF284888u /* DP_ABYSS_500 */
|
||||
#define DSB_BORDER_FOCUS 0xFF44CCF0u /* DP_SEAL_300 */
|
||||
#define DSB_CURSOR_COLOR 0xFF00A8CCu /* DP_SEAL_500 */
|
||||
#define DSB_PROMPT_COLOR 0xFF44CCF0u /* DP_SEAL_300 */
|
||||
#define DSB_SUCCESS 0xFF40C880u /* DP_SUCCESS */
|
||||
#define DSB_ERROR 0xFFFF4466u /* DP_ERROR */
|
||||
#define DSB_WARNING 0xFFF0A030u /* DP_WARNING */
|
||||
|
||||
/* ---- 窗口装饰尺寸 ---- */
|
||||
#define DSB_TITLEBAR_H 28
|
||||
#define DSB_BORDER_W 2
|
||||
#define DSB_STATUSBAR_H 20
|
||||
|
||||
static inline void dsb_draw_titlebar(dsb_app_context *ac, const char *title, i64 w) {
|
||||
dsb_fill_rect(ac, 0, 0, w, DSB_TITLEBAR_H, DSB_BG_SECONDARY);
|
||||
dsb_draw_string(ac, title, 8, (DSB_TITLEBAR_H - 18) / 2, DSB_TEXT_PRIMARY, DSB_BG_SECONDARY, 12);
|
||||
/* 关闭按钮 */
|
||||
dsb_fill_rounded_rect(ac, w - 28, 4, 24, 20, DSB_ERROR, 4);
|
||||
dsb_draw_string(ac, "X", w - 20, 5, 0xFF0A1428u, DSB_ERROR, 12);
|
||||
/* 底部分隔线 */
|
||||
dsb_fill_rect(ac, 0, DSB_TITLEBAR_H, w, 1, DSB_ACCENT);
|
||||
}
|
||||
|
||||
static inline void dsb_draw_statusbar(dsb_app_context *ac, const char *text, i64 w, i64 h) {
|
||||
i64 y = h - DSB_STATUSBAR_H;
|
||||
dsb_fill_rect(ac, 0, y, w, DSB_STATUSBAR_H, DSB_BG_SECONDARY);
|
||||
dsb_draw_string(ac, text, 4, y + 1, DSB_TEXT_DIM, DSB_BG_SECONDARY, 12);
|
||||
/* 顶部分隔线 */
|
||||
dsb_fill_rect(ac, 0, y, w, 1, DSB_BORDER);
|
||||
}
|
||||
|
||||
/* ---- Linux 桌面应用配置契约(LINUXAPP.CNF) ----
|
||||
*
|
||||
* desktop.elf 启动时从 FAT32 根目录读取 LINUXAPP.CNF(8.3 名 LINUXAPPCNF),
|
||||
* 每行注册一个 Linux 应用图标:
|
||||
* 显示名|linux命令
|
||||
* 示例:
|
||||
* NEOFETCH|/usr/bin/neofetch
|
||||
* HTOP|/usr/bin/htop
|
||||
* 规则:空行与 # 开头的行被忽略;显示名 <= 11 字符,命令 <= 63 字符。 */
|
||||
|
||||
#define DSB_LINUXAPP_CNF_83 "LINUXAPPCNF" /* FAT32 8.3 名 */
|
||||
#define DSB_LINUXAPP_MAX 8
|
||||
#define DSB_LINUXAPP_NAME_CAP 12
|
||||
#define DSB_LINUXAPP_CMD_CAP 64
|
||||
|
||||
typedef struct {
|
||||
char name[DSB_LINUXAPP_NAME_CAP];
|
||||
char cmd[DSB_LINUXAPP_CMD_CAP];
|
||||
} dsb_linux_app;
|
||||
|
||||
/* 解析 LINUXAPP.CNF 文本(允许非 NUL 结尾,按 size 截断)。
|
||||
* 返回有效条目数(<= max)。 */
|
||||
static inline int dsb_linuxapp_parse(const char *text, u32 size, dsb_linux_app *out, int max) {
|
||||
int count = 0;
|
||||
u32 pos = 0;
|
||||
while (pos < size && count < max) {
|
||||
char line[96];
|
||||
int ll = 0;
|
||||
while (pos < size && text[pos] != '\n') {
|
||||
char c = text[pos++];
|
||||
if (c == '\r') continue;
|
||||
if (ll < 95) line[ll++] = c;
|
||||
}
|
||||
if (pos < size && text[pos] == '\n') pos++;
|
||||
line[ll] = 0;
|
||||
|
||||
while (ll > 0 && (line[ll - 1] == ' ' || line[ll - 1] == '\t')) line[--ll] = 0;
|
||||
|
||||
int s = 0;
|
||||
while (line[s] == ' ' || line[s] == '\t') s++;
|
||||
if (line[s] == 0 || line[s] == '#') continue;
|
||||
|
||||
int bar = s;
|
||||
while (line[bar] && line[bar] != '|') bar++;
|
||||
if (line[bar] != '|') continue;
|
||||
|
||||
int ne = bar;
|
||||
while (ne > s && (line[ne - 1] == ' ' || line[ne - 1] == '\t')) ne--;
|
||||
if (ne <= s) continue;
|
||||
|
||||
int cs = bar + 1;
|
||||
while (line[cs] == ' ' || line[cs] == '\t') cs++;
|
||||
if (line[cs] == 0) continue;
|
||||
|
||||
int ni = 0;
|
||||
for (int k = s; k < ne && ni < DSB_LINUXAPP_NAME_CAP - 1; k++)
|
||||
out[count].name[ni++] = line[k];
|
||||
out[count].name[ni] = 0;
|
||||
|
||||
int ci = 0;
|
||||
for (int k = cs; line[k] && ci < DSB_LINUXAPP_CMD_CAP - 1; k++)
|
||||
out[count].cmd[ci++] = line[k];
|
||||
out[count].cmd[ci] = 0;
|
||||
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
#endif /* DESHAB_UI_H */
|
||||
@@ -0,0 +1,35 @@
|
||||
# Deshab SDK 应用 MAKEFILE 模板
|
||||
#
|
||||
# 由 scaffold/new-module.ps1 生成时自动替换:
|
||||
# {{MODULE_NAME}} -> 模块名 (如 myapp)
|
||||
# {{SDK_INC}} -> SDK include 目录相对路径 (如 ../../sdk/include)
|
||||
# {{OUT_DIR}} -> 输出目录相对路径 (如 $(ROOT)/SYSTEM/system/deshab64/tools)
|
||||
# {{BITMAPS}} -> ascii_bitmaps.c 相对路径 (如 ../../firstInit/ascii_bitmaps.c)
|
||||
#
|
||||
# 手动使用时替换上述占位符。
|
||||
|
||||
CC := clang
|
||||
LD := ld.lld
|
||||
|
||||
ROOT := ../../..
|
||||
OUT := {{OUT_DIR}}/{{MODULE_NAME}}.elf
|
||||
|
||||
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I{{SDK_INC}} -O2
|
||||
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
|
||||
|
||||
C_SOURCES := main.c
|
||||
OBJECTS := $(C_SOURCES:.c=.o)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(OUT)
|
||||
|
||||
$(OUT): $(OBJECTS) linker.ld
|
||||
@mkdir -p "$(dir $(OUT))"
|
||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Deshab SDK 应用 linker.ld 模板
|
||||
*
|
||||
* PIE ELF 布局,ENTRY(dsk_entry)。
|
||||
* BSS 分离为独立 NOBITS 段,避免占文件空间。
|
||||
* 与现有应用 (ping/editor/...) linker.ld 一致。
|
||||
*/
|
||||
ENTRY(dsk_entry)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(5);
|
||||
rodata PT_LOAD FLAGS(4);
|
||||
data PT_LOAD FLAGS(6);
|
||||
dynamic PT_DYNAMIC FLAGS(4);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x1000;
|
||||
.text : ALIGN(4K) { *(.text*) } :text
|
||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
||||
.data : ALIGN(4K) {
|
||||
*(.data*)
|
||||
*(.data.rel.ro*)
|
||||
*(COMMON)
|
||||
} :data
|
||||
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
|
||||
.dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic
|
||||
.bss : ALIGN(4K) {
|
||||
*(.bss*)
|
||||
} :data
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/* Deshab SDK 应用入口模板
|
||||
*
|
||||
* 由 scaffold/new-module.ps1 生成时自动替换:
|
||||
* {{MODULE_NAME}} -> 模块名
|
||||
* {{BITMAPS}} -> ascii_bitmaps.c 相对路径
|
||||
*
|
||||
* 展示最小应用框架: 初始化上下文 -> 清屏 -> 标题栏 -> Esc 退出。
|
||||
*/
|
||||
#include "{{BITMAPS}}"
|
||||
#include "deshab/deshab.h"
|
||||
#include "deshab/font.h"
|
||||
|
||||
static dsb_app_context g_ac;
|
||||
|
||||
void dsk_entry(const dsk_boot_context *ctx) {
|
||||
if (ctx->magic != DSK_BOOT_MAGIC) return;
|
||||
dsb_app_init(&g_ac, ctx);
|
||||
|
||||
/* 清屏 */
|
||||
dsb_fill_bg(&g_ac, DSB_BG_PRIMARY);
|
||||
|
||||
/* 标题栏 + 状态栏 */
|
||||
dsb_draw_titlebar(&g_ac, "{{MODULE_NAME}}", (i64)g_ac.fb_w);
|
||||
dsb_draw_statusbar(&g_ac, "Esc to exit", (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
||||
|
||||
/* 居中欢迎文字 */
|
||||
const char *msg = "Hello from {{MODULE_NAME}}!";
|
||||
i64 msg_w = (i64)dsb_strlen(msg) * 12;
|
||||
i64 x = ((i64)g_ac.fb_w - msg_w) / 2;
|
||||
i64 y = ((i64)g_ac.fb_h) / 2;
|
||||
dsb_draw_string(&g_ac, msg, x, y, DSB_TEXT_PRIMARY, DSB_BG_PRIMARY, 12);
|
||||
|
||||
/* 等待 Esc 键退出 */
|
||||
for (;;) {
|
||||
u8 st = dsb_inb(0x64);
|
||||
if (st & 1) {
|
||||
u8 sc = dsb_inb(0x60);
|
||||
if (sc == 1) break; /* Esc make code */
|
||||
}
|
||||
__asm__ volatile("pause");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/* Deshab SDK 驱动入口模板
|
||||
*
|
||||
* 由 scaffold/new-module.ps1 生成时自动替换:
|
||||
* {{MODULE_NAME}} -> 驱动名 (如 mydriver)
|
||||
* {{CLASS}} -> DKM_CLASS_* 类别 (如 DKM_CLASS_MISC)
|
||||
* {{STAGE}} -> 加载阶段 0-3
|
||||
*
|
||||
* 展示最小 DKM 驱动框架: driver_desc + driver_init + driver_exit。
|
||||
* 构建方式: clang -c main.c -> ld.lld -r -o {{MODULE_NAME}}.drv main.o
|
||||
* (见 scaffold/driver/build.ps1 片段或 build.ps1 的 Build-DkmDriver)
|
||||
*/
|
||||
#include "deshab/driver.h"
|
||||
#include "deshab/serial.h"
|
||||
|
||||
static const dkm_log_api *g_log = 0;
|
||||
|
||||
static const char *const g_depends[] = { 0 };
|
||||
static const char *const g_provides[] = { "{{MODULE_NAME}}" };
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
const dkm_driver_desc driver_desc = {
|
||||
.magic = DKM_DRIVER_MAGIC,
|
||||
.abi_version = DKM_ABI_VERSION,
|
||||
.desc_size = sizeof(dkm_driver_desc),
|
||||
.name = "{{MODULE_NAME}}",
|
||||
.version = "1.0",
|
||||
.vendor = "Deshab",
|
||||
.driver_class = {{CLASS}},
|
||||
.stage = {{STAGE}},
|
||||
.flags = 0,
|
||||
.priority = 100,
|
||||
.depends = g_depends,
|
||||
.depends_count = 0,
|
||||
.provides = g_provides,
|
||||
.provides_count = 1,
|
||||
.min_kernel_abi = DKM_KERNEL_API_VERSION,
|
||||
.feature_bits = 0,
|
||||
};
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_init(const dkm_kernel_api *api, dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
if (!api || !api->log) return -1;
|
||||
g_log = api->log;
|
||||
g_log->info("[{{MODULE_NAME}}] init begin");
|
||||
/* TODO: 驱动初始化逻辑 */
|
||||
g_log->info("[{{MODULE_NAME}}] driver ready");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int driver_exit(dkm_driver_handle *handle) {
|
||||
(void)handle;
|
||||
if (g_log) g_log->info("[{{MODULE_NAME}}] exit");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Deshab SDK 一键创建新应用/驱动模块脚手架。
|
||||
|
||||
.DESCRIPTION
|
||||
从 CODE/sdk/scaffold/ 模板生成新模块,自动替换占位符并计算相对路径。
|
||||
|
||||
.PARAMETER Name
|
||||
模块名(如 myapp、mydriver)。
|
||||
|
||||
.PARAMETER Type
|
||||
模块类型:app(应用 .elf)或 driver(驱动 .drv)。默认 app。
|
||||
|
||||
.PARAMETER OutDir
|
||||
输出目录。默认 app -> CODE/tools/$Name,driver -> CODE/DKM/$Name。
|
||||
|
||||
.PARAMETER Class
|
||||
驱动类别(DKM_CLASS_*)。仅 Type=driver 时使用。默认 DKM_CLASS_MISC。
|
||||
|
||||
.PARAMETER Stage
|
||||
驱动加载阶段 0-3。仅 Type=driver 时使用。默认 3。
|
||||
|
||||
.EXAMPLE
|
||||
.\new-module.ps1 -Name myapp
|
||||
.\new-module.ps1 -Name mydriver -Type driver -Class DKM_CLASS_NET -Stage 3
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$Name,
|
||||
[ValidateSet('app','driver')][string]$Type = 'app',
|
||||
[string]$OutDir,
|
||||
[string]$Class = 'DKM_CLASS_MISC',
|
||||
[int]$Stage = 3
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$SdkDir = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
||||
$CodeDir = [System.IO.Path]::GetFullPath((Join-Path $SdkDir '..'))
|
||||
$RootDir = [System.IO.Path]::GetFullPath((Join-Path $CodeDir '..'))
|
||||
|
||||
if (-not $OutDir) {
|
||||
if ($Type -eq 'app') {
|
||||
$OutDir = Join-Path $CodeDir "tools\$Name"
|
||||
} else {
|
||||
$OutDir = Join-Path $CodeDir "DKM\$Name"
|
||||
}
|
||||
}
|
||||
$OutDir = [System.IO.Path]::GetFullPath($OutDir)
|
||||
if (Test-Path $OutDir) { throw "目录已存在: $OutDir" }
|
||||
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||
|
||||
# 计算从 $from 到 $to 的相对路径(反斜杠)
|
||||
function Get-RelPath($from, $to) {
|
||||
$fromUri = [System.Uri]::new($from.TrimEnd('\') + '\')
|
||||
$toUri = [System.Uri]::new($to.TrimEnd('\') + '\')
|
||||
if (Test-Path $to -PathType Leaf) {
|
||||
$toUri = [System.Uri]::new($to)
|
||||
}
|
||||
return $fromUri.MakeRelativeUri($toUri).ToString().Replace('/', '\')
|
||||
}
|
||||
|
||||
if ($Type -eq 'app') {
|
||||
$SdkInc = Get-RelPath $OutDir (Join-Path $SdkDir 'include')
|
||||
$Bitmaps = Get-RelPath $OutDir (Join-Path $CodeDir 'firstInit\ascii_bitmaps.c')
|
||||
$OutPath = Join-Path $RootDir "SYSTEM\system\deshab64\tools\$Name.elf"
|
||||
$OutDirR = Get-RelPath $OutDir (Split-Path $OutPath -Parent)
|
||||
|
||||
# MAKEFILE
|
||||
$mf = Get-Content (Join-Path $SdkDir 'scaffold\app\MAKEFILE.tmpl') -Raw
|
||||
$mf = $mf.Replace('{{MODULE_NAME}}', $Name).Replace('{{SDK_INC}}', $SdkInc).Replace('{{OUT_DIR}}', $OutDirR)
|
||||
Set-Content (Join-Path $OutDir 'MAKEFILE') $mf -NoNewline
|
||||
|
||||
# linker.ld
|
||||
Copy-Item (Join-Path $SdkDir 'scaffold\app\linker.ld.tmpl') (Join-Path $OutDir 'linker.ld')
|
||||
|
||||
# main.c
|
||||
$mc = Get-Content (Join-Path $SdkDir 'scaffold\app\main.c.tmpl') -Raw
|
||||
$mc = $mc.Replace('{{MODULE_NAME}}', $Name).Replace('{{BITMAPS}}', $Bitmaps)
|
||||
Set-Content (Join-Path $OutDir 'main.c') $mc -NoNewline
|
||||
|
||||
Write-Host "[sdk] 应用 '$Name' 已创建: $OutDir" -ForegroundColor Green
|
||||
Write-Host "[sdk] 构建: cd $OutDir ; make" -ForegroundColor Cyan
|
||||
Write-Host "[sdk] 输出: $OutPath" -ForegroundColor Cyan
|
||||
} else {
|
||||
# main.c
|
||||
$mc = Get-Content (Join-Path $SdkDir 'scaffold\driver\main.c.tmpl') -Raw
|
||||
$mc = $mc.Replace('{{MODULE_NAME}}', $Name).Replace('{{CLASS}}', $Class).Replace('{{STAGE}}', [string]$Stage)
|
||||
Set-Content (Join-Path $OutDir 'main.c') $mc -NoNewline
|
||||
|
||||
Write-Host "[sdk] 驱动 '$Name' 已创建: $OutDir" -ForegroundColor Green
|
||||
Write-Host "[sdk] 构建: clang -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I$(Join-Path $SdkDir 'include') -O2 -c main.c -o $Name.o" -ForegroundColor Cyan
|
||||
Write-Host " ld.lld -r -o $Name.drv $Name.o" -ForegroundColor Cyan
|
||||
Write-Host "[sdk] 注册: 在 SYSTEM/driver/manifest.json 添加条目" -ForegroundColor Yellow
|
||||
Write-Host "[sdk] 预加载: 在 SYSTEM/boot/limine.conf 添加 module_path" -ForegroundColor Yellow
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Deshab SDK 编译验证脚本。
|
||||
.DESCRIPTION
|
||||
验证所有 SDK 头文件可编译(自洽性),示例和工具可构建。
|
||||
用法: .\sdk-test.ps1 [-LlvmBin <path>]
|
||||
#>
|
||||
param(
|
||||
[string]$LlvmBin = 'C:\Program Files\LLVM\bin'
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$SdkDir = $PSScriptRoot
|
||||
$RootDir = [System.IO.Path]::GetFullPath((Join-Path $SdkDir '..\..'))
|
||||
|
||||
$Clang = Join-Path $LlvmBin 'clang.exe'
|
||||
if (-not (Test-Path $Clang)) { throw "clang not found: $Clang" }
|
||||
$env:PATH = "$LlvmBin;$env:PATH"
|
||||
|
||||
$global:pass = 0
|
||||
$global:fail = 0
|
||||
|
||||
function Test-Step($name, [scriptblock]$sb) {
|
||||
Write-Host -NoNewline "[sdk-test] $name ... "
|
||||
try {
|
||||
& $sb
|
||||
if ($LASTEXITCODE -ne 0) { throw "exit code $LASTEXITCODE" }
|
||||
Write-Host "PASS" -ForegroundColor Green
|
||||
$global:pass++
|
||||
} catch {
|
||||
Write-Host "FAIL: $_" -ForegroundColor Red
|
||||
$global:fail++
|
||||
}
|
||||
}
|
||||
|
||||
# ---- 1. 头文件自洽性测试 ----
|
||||
$tmpDir = Join-Path $env:TEMP 'deshab-sdk-test'
|
||||
New-Item -ItemType Directory -Force -Path $tmpDir | Out-Null
|
||||
$testC = @'
|
||||
#include "deshab/types.h"
|
||||
/* 提供字体位图 dummy 定义,使 font.h/ui.h 可编译 */
|
||||
static const u8 *g_ascii[95] = {0};
|
||||
static i64 g_ascii_w = 8;
|
||||
static i64 g_ascii_h = 16;
|
||||
#include "deshab/portio.h"
|
||||
#include "deshab/serial.h"
|
||||
#include "deshab/tsc.h"
|
||||
#include "deshab/string.h"
|
||||
#include "deshab/kernel_api.h"
|
||||
#include "deshab/boot_context.h"
|
||||
#include "deshab/driver.h"
|
||||
#include "deshab/block.h"
|
||||
#include "deshab/app.h"
|
||||
#include "deshab/fb.h"
|
||||
#include "deshab/cursor.h"
|
||||
#include "deshab/input.h"
|
||||
#include "deshab/ui.h"
|
||||
#include "deshab/rtc.h"
|
||||
#include "deshab/fat32.h"
|
||||
#include "deshab/net.h"
|
||||
|
||||
/* 触发 static inline 实例化,确保头文件无编译错误 */
|
||||
void sdk_selftest(void) {
|
||||
dsb_app_context ac; dsb_cursor cur; dsb_mouse ms; dsb_block blk;
|
||||
(void)ac; (void)cur; (void)ms; (void)blk;
|
||||
}
|
||||
'@
|
||||
$testFile = Join-Path $tmpDir 'sdk_selftest.c'
|
||||
Set-Content $testFile $testC
|
||||
|
||||
Test-Step "headers self-consistency" {
|
||||
& $Clang -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin `
|
||||
-fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx `
|
||||
-msoft-float -m64 -Wall -Wextra "-I$(Join-Path $SdkDir 'include')" -O2 `
|
||||
-c $testFile -o (Join-Path $tmpDir 'sdk_selftest.o') 2>&1
|
||||
}
|
||||
|
||||
# ---- 2. hello-app 编译+链接 ----
|
||||
Test-Step "hello-app build" {
|
||||
Push-Location (Join-Path $SdkDir 'examples\hello-app')
|
||||
& $Clang -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin `
|
||||
-fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx `
|
||||
-msoft-float -m64 -Wall -Wextra -I../../include -O2 -c main.c -o main.o 2>&1
|
||||
& (Join-Path $LlvmBin 'ld.lld.exe') -nostdlib -static -pie -z max-page-size=0x1000 `
|
||||
-T linker.ld -o hello.elf main.o 2>&1
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
# ---- 3. hello-driver 编译+链接 ----
|
||||
Test-Step "hello-driver build" {
|
||||
Push-Location (Join-Path $SdkDir 'examples\hello-driver')
|
||||
& $Clang -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin `
|
||||
-fno-stack-protector -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone `
|
||||
-mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I../../include -O2 `
|
||||
-c main.c -o hello.o 2>&1
|
||||
& (Join-Path $LlvmBin 'ld.lld.exe') -r -o hello.drv hello.o 2>&1
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
# ---- 4. sysinfo 编译+链接 ----
|
||||
Test-Step "sysinfo build" {
|
||||
Push-Location (Join-Path $RootDir 'CODE\tools\sysinfo')
|
||||
& $Clang -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin `
|
||||
-fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx `
|
||||
-msoft-float -m64 -Wall -Wextra -I../../sdk/include -O2 -c main.c -o main.o 2>&1
|
||||
& (Join-Path $LlvmBin 'ld.lld.exe') -nostdlib -static -pie -z max-page-size=0x1000 `
|
||||
-T linker.ld -o sysinfo.elf main.o 2>&1
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
# ---- 汇总 ----
|
||||
Write-Host ""
|
||||
Write-Host "[sdk-test] Result: $global:pass passed, $global:fail failed" `
|
||||
-ForegroundColor $(if ($global:fail -eq 0) {'Green'} else {'Red'})
|
||||
exit $global:fail
|
||||
Reference in New Issue
Block a user