explorer 直接写 framebuffer 会与显示器扫描并发,造成撕裂/闪烁;而它在 Ring3 没有 IO 端口权限,不能像历史内核桌面那样自己 inb 0x3DA。所以在 /dev/fb0 上加 FB_WAIT_VBLANK(0x4601)由内核代等垂直回扫(带超时,无 VGA 时序时不死等),vpsdk 暴露 fb_wait_vblank(),desk_server.render() 写屏前调用一次。
95 lines
3.7 KiB
Plaintext
95 lines
3.7 KiB
Plaintext
import vpsdk.fs as fs
|
||
import vpsdk.syscall as syscall
|
||
import string
|
||
from stdint import *
|
||
|
||
|
||
# ============================================================================
|
||
# devfs 设备层:设备节点路径 + 各设备的专用读写/控制助手。
|
||
#
|
||
# 设备节点由内核在启动时注册(见 VKernel/Kernel/main.vp 的 devfs_register_*),
|
||
# 用户态用普通 open/read/write/ioctl 访问 —— 不再有"设备专用 syscall"。
|
||
# ============================================================================
|
||
|
||
# 路径必须与内核注册名逐字一致
|
||
define DEV_SERIAL0 = "/dev/serial0"
|
||
define DEV_SERIAL1 = "/dev/serial1"
|
||
define DEV_FB0 = "/dev/fb0"
|
||
define DEV_KBD = "/dev/input/keyboard"
|
||
define DEV_MOUSE = "/dev/input/mouse"
|
||
define DEV_DESK = "/dev/desk"
|
||
|
||
# ioctl 命令与返回结构:必须与 VKernel/Kernel/drivers/devfs/devfs.vp 的
|
||
# FB_GET_INFO / FbInfo 逐字段一致(packed;4×u32 + 2×u64 = 32 字节)。
|
||
define FB_GET_INFO = 0x4600
|
||
define FB_WAIT_VBLANK = 0x4601
|
||
|
||
@__attribute__(packed)
|
||
class FbInfo:
|
||
width: uint32
|
||
height: uint32
|
||
pitch: uint32
|
||
format: uint32
|
||
size: uint64
|
||
phys: uint64
|
||
|
||
|
||
def fb_open() -> int:
|
||
return fs.open(DEV_FB0, uint32(0))
|
||
|
||
def fb_get_info(fd: int, out: FbInfo*) -> int:
|
||
"""查 framebuffer 几何 / 格式 / 物理区间,写进调用者给出的 out。
|
||
|
||
返回 ioctl 的返回码(0 = 成功,非 0 = 失败,此时 out 内容不可信 —— 调用方必须判失败)。
|
||
|
||
为什么改为**出参**而不是按值返回 FbInfo:按值返回 32 字节结构体在现场表现为
|
||
"前 16 字节对、后 16 字节是调用方栈残留"(size/phys 打出 1802724708 / 0x130c30 这类值),
|
||
即结构体返回值只回传了一部分。出参写内存不依赖该 ABI,稳定可靠。
|
||
"""
|
||
if not out:
|
||
return -1
|
||
string.memset(out, 0, FbInfo.__sizeof__())
|
||
return fs.ioctl(fd, uint32(FB_GET_INFO), out)
|
||
|
||
def fb_wait_vblank(fd: int) -> int:
|
||
"""等一次垂直回扫(合成前调用,减轻 tearing)。用户态无 IO 权限,由内核代做。"""
|
||
return fs.ioctl(fd, uint32(FB_WAIT_VBLANK), None)
|
||
|
||
def fb_map(fd: int, size: uint64) -> uint64:
|
||
"""把 framebuffer 映射进本进程,返回线性地址(0 = 失败)。"""
|
||
return syscall.mmap(0, size, uint64(syscall.PROT_READ | syscall.PROT_WRITE),
|
||
uint64(syscall.MAP_SHARED), uint64(fd), 0)
|
||
|
||
def kbd_open() -> int:
|
||
return fs.open(DEV_KBD, uint32(0))
|
||
|
||
def mouse_open() -> int:
|
||
return fs.open(DEV_MOUSE, uint32(0))
|
||
|
||
def shm_map(fd: int, size: uint64) -> uint64:
|
||
"""把共享内存设备映射进本进程,返回线性起始地址(0 = 失败)。
|
||
|
||
与 fb_map 同一机制(设备 mmap_info 给出的物理区间);不同点在于 /dev/desk
|
||
的区间对**所有**打开者都是同一块物理内存,因此天然可跨进程共享。
|
||
"""
|
||
return syscall.mmap(0, size, uint64(syscall.PROT_READ | syscall.PROT_WRITE),
|
||
uint64(syscall.MAP_SHARED), uint64(fd), 0)
|
||
|
||
# /dev/serial0 的 fd 惰性打开一次(-1 = 还没开;-2 = 开失败过,不再重试)
|
||
_serial_fd: int = -1
|
||
|
||
def serial_puts(msg: const str) -> int:
|
||
"""把字符串写到 /dev/serial0 —— 替代已删除的 SERIAL_PUTS syscall。
|
||
|
||
内核侧 serial0 设备的 write 按 C 字符串输出(内部就是 serial.puts),
|
||
所以这里只需把指针与长度递下去。
|
||
"""
|
||
global _serial_fd
|
||
if _serial_fd == -1:
|
||
_serial_fd = fs.open(DEV_SERIAL0, uint32(0))
|
||
if _serial_fd < 0:
|
||
_serial_fd = -2
|
||
if _serial_fd < 0: return -1
|
||
if msg is None: return -1
|
||
return fs.write(_serial_fd, msg, uint32(string.strlen(msg)))
|