feat(input): PS/2 鼠标启用 IntelliMouse 滚轮

- init 发送 200/100/80 魔数并读取设备 ID;ID=3/4 时切到 4 字节包
- IRQ 的 4 字节模式解析第 4 字节低 4 位为有符号 Z 增量;
  标准 3 字节模式 wheel 恒 0
- mouse_packet 增加 wheel:int8
- /dev/input/mouse 布局扩展:[9] = wheel(int8),read 返回 10;
  调用方缓冲不足 10 字节时仍返回旧的 9 字节布局
This commit is contained in:
TermiNexus committed 2026-10-27 14:50:03 +08:00
1 parent 5e2598a992
commit 5405a34a24
2 files changed
+48 -3

No files matched your search

+5
View File
@@ -466,6 +466,11 @@ virtual class mouse_device(devfs_file):
dy = 0 - dy
int32*(buf)[0] = int32(dx)
int32*(buf)[1] = int32(dy)
# [9] = 滚轮增量(int8,IntelliMouse;标准 3 字节鼠标恒 0)
if size >= 10:
wbuf: int8* = int8*(uint64(buffer) + 9)
wbuf[0] = pkt.wheel
return 10
return 9
return 1
+43 -3
View File
@@ -18,6 +18,7 @@ class mouse_packet:
dx: uint8 # 原始幅值字节(符号在 flags.bit4)
dy: uint8 # 原始幅值字节(符号在 flags.bit5)
buttons: uint8
wheel: int8 # IntelliMouse 第 4 字节低 4 位的 Z 增量(3 字节模式恒 0)
packets: [MOUSE_BUF_SIZE]mouse_packet
static pack_head: uint32 = 0
@@ -29,6 +30,9 @@ static cycle: uint8 = 0
static byte1: uint8 = 0
static byte2: uint8 = 0
static byte3: uint8 = 0
static byte4: uint8 = 0
# 0 = 标准 3 字节 PS/2 包;1 = IntelliMouse 4 字节包(第 4 字节带滚轮)
static wheel_mode: int = 0
static mouse_x: int32 = 0
static mouse_y: int32 = 0
@@ -52,8 +56,12 @@ def _write_cmd(cmd: uint8):
_wait_read()
asm.inb(MOUSE_DATA)
def _read_data() -> uint8:
_wait_read()
return asm.inb(MOUSE_DATA)
def irq_handler() -> int:
global cycle, byte1, byte2, byte3, pack_tail, pack_count, mse_irq_cnt
global cycle, byte1, byte2, byte3, byte4, pack_tail, pack_count, mse_irq_cnt
mse_irq_cnt += 1
status: uint8 = asm.inb(MOUSE_STATUS)
if (status & 0x20) == 0: return 0
@@ -67,17 +75,30 @@ def irq_handler() -> int:
byte2 = data
cycle = 2
return 0
byte3 = data
cycle = 0
if cycle == 2:
byte3 = data
if wheel_mode != 0:
cycle = 3
return 0
cycle = 0
else:
byte4 = data
cycle = 0
# 丢弃带溢出位的包(bit6/bit7 = X/Y overflow):此时设备给的是饱和值,
# 直接采用会产生 ±127 级跳变(表现为光标“飞”)。
if (byte1 & 0xC0) != 0:
return 0
# IntelliMouse 第 4 字节低 4 位是有符号 Z 增量(4-bit two's complement)。
w: int = 0
if wheel_mode != 0:
w = int(byte4 & uint8(0x0F))
if w >= 8: w = w - 16
if pack_count < MOUSE_BUF_SIZE:
packets[pack_tail].flags = byte1
packets[pack_tail].dx = byte2
packets[pack_tail].dy = byte3
packets[pack_tail].buttons = byte1 & uint8(0x07)
packets[pack_tail].wheel = int8(w)
pack_tail = pack_tail + 1
if pack_tail >= MOUSE_BUF_SIZE:
pack_tail = 0
@@ -93,11 +114,13 @@ def read_packet(out: mouse_packet*) -> int:
out.dx = 0
out.dy = 0
out.buttons = 0
out.wheel = 0
return -1
out.flags = packets[pack_head].flags
out.dx = packets[pack_head].dx
out.dy = packets[pack_head].dy
out.buttons = packets[pack_head].buttons
out.wheel = packets[pack_head].wheel
pack_head = pack_head + 1
if pack_head >= MOUSE_BUF_SIZE:
pack_head = 0
@@ -118,6 +141,8 @@ def get_buttons() -> uint8:
return mouse_buttons
def init():
global wheel_mode
wheel_mode = 0
_wait_write()
asm.outb(MOUSE_COMMAND, 0xA8)
# 读-改-写 CCB 期间屏蔽 IRQ1:与 keyboard.init 同理 —— 0x20 读 CCB 会把 CCB 值放进
@@ -136,6 +161,21 @@ def init():
asm.outb(MOUSE_DATA, status)
pic.clearMask(1)
_write_cmd(0xF6)
# IntelliMouse 魔数:采样率 200 → 100 → 80。成功后设备 ID 变成 3(滚轮)
# 或 4(滚轮 + 侧键),之后每个包变成 4 字节(第 4 字节低 4 位是 Z 增量)。
_write_cmd(0xF3)
_write_cmd(200)
_write_cmd(0xF3)
_write_cmd(100)
_write_cmd(0xF3)
_write_cmd(80)
_write_cmd(0xF2)
dev_id: uint8 = _read_data()
if dev_id == uint8(3) or dev_id == uint8(4):
wheel_mode = 1
# 魔数序列会把采样率停在 80;恢复正常采样率 100 后再设分辨率/使能。
_write_cmd(0xF3)
_write_cmd(100)
# 分辨率设为 8 counts/mm:0xF6(Set Defaults) 会把它重置为最粗的 4 counts/mm,
# 于是每个计数对应的位移偏大。设细一档后光标更稳、不易“飞”。
_write_cmd(0xE8)