Files
ViperOS/VKernel/Kernel/drivers/input/mouse/mouse.vp
T
TermiNexus da4b0e47b7 fix(input): 修正 PS/2 滚轮方向
IntelliMouse 第 4 字节的 Z 符号与“向上 = 正值”的事件约定相反,
在驱动层统一翻符号;下游按正值表示滚轮向上处理,Terminal 的
scroll_offset -= w * 3 无需改动。
2026-10-27 16:19:37 +08:00

189 lines
5.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 asm
import intr.idt as idt
import platform.pch.pic as pic
define MOUSE_DATA = 0x60
define MOUSE_STATUS = 0x64
define MOUSE_COMMAND = 0x64
define MOUSE_BUF_SIZE = 1024
define MOUSE_LEFT = 0x01
define MOUSE_RIGHT = 0x02
define MOUSE_MIDDLE = 0x04
class mouse_packet:
flags: uint8
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
static pack_tail: uint32 = 0
static pack_count: uint32 = 0
static mse_irq_cnt: uint64 = 0
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
static mouse_buttons: uint8 = 0
def _wait_read():
timeout: uint32 = 100000
for _ in range(timeout, 0, -1):
if (asm.inb(MOUSE_STATUS) & 0x01) != 0: return
def _wait_write():
timeout: uint32 = 100000
for _ in range(timeout, 0, -1):
if (asm.inb(MOUSE_STATUS) & 0x02) == 0: return
def _write_cmd(cmd: uint8):
_wait_write()
asm.outb(MOUSE_COMMAND, 0xD4)
_wait_write()
asm.outb(MOUSE_DATA, cmd)
_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, byte4, pack_tail, pack_count, mse_irq_cnt
mse_irq_cnt += 1
status: uint8 = asm.inb(MOUSE_STATUS)
if (status & 0x20) == 0: return 0
data: uint8 = asm.inb(MOUSE_DATA)
if cycle == 0:
if (data & 0x08) != 0:
byte1 = data
cycle = 1
return 0
if cycle == 1:
byte2 = data
cycle = 2
return 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)。
# 实测该 Z 轴符号与“向上 = 正值”的事件约定相反,这里统一翻符号:
# 下游(Explorer/应用)按正值表示滚轮向上处理。
w: int = 0
if wheel_mode != 0:
w = int(byte4 & uint8(0x0F))
if w >= 8: w = w - 16
w = 0 - w
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
pack_count += 1
return 0
def read_packet(out: mouse_packet*) -> int:
global pack_head, pack_count
asm.cli()
if pack_count == 0:
asm.sti()
out.flags = 0
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
pack_count -= 1
asm.sti()
return 0
def has_data() -> int:
return pack_count
def get_x() -> int32:
return mouse_x
def get_y() -> int32:
return mouse_y
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 值放进
# 0x60 输出缓冲并拉高 IRQ1,键盘 ISR 一旦运行就会把它当扫描码抢走(现场:sc=0x47
# 正是这里清 bit5 后的 CCB),于是下面的 inb(0x60) 只能拿到残留值 ⇒ 可能把错误 CCB 写回。
pic.setMask(1)
_wait_write()
asm.outb(MOUSE_COMMAND, 0x20)
_wait_read()
status: uint8 = asm.inb(MOUSE_DATA)
status = status | 0x02
status = status & 0xDF
_wait_write()
asm.outb(MOUSE_COMMAND, 0x60)
_wait_write()
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)
_write_cmd(0x03)
_write_cmd(0xF4)
pic.clearMask(12)
idt.irqInstallHandler(12, irq_handler, "mouse")