Files
ViperOS/vpsdk/devfs.vp
T
TermiNexus e41701e201 feat(fb0): 新增 FB_WAIT_VBLANK ioctl,合成前等垂直回扫
explorer 直接写 framebuffer 会与显示器扫描并发,造成撕裂/闪烁;而它在 Ring3 没有 IO 端口权限,不能像历史内核桌面那样自己 inb 0x3DA。所以在 /dev/fb0 上加 FB_WAIT_VBLANK(0x4601)由内核代等垂直回扫(带超时,无 VGA 时序时不死等),vpsdk 暴露 fb_wait_vblank(),desk_server.render() 写屏前调用一次。
2026-10-16 21:37:58 +08:00

95 lines
3.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)))