switch: added ignore_hpd quirk for bad csi boards

This commit is contained in:
Maxim Devaev 2025-02-16 01:00:38 +02:00
parent 02740aef37
commit 6a08fab818
8 changed files with 64 additions and 13 deletions

View File

@ -508,6 +508,7 @@ def _get_config_scheme() -> dict:
"switch": { "switch": {
"device": Option("/dev/kvmd-switch", type=valid_abs_path, unpack_as="device_path"), "device": Option("/dev/kvmd-switch", type=valid_abs_path, unpack_as="device_path"),
"default_edid": Option("/etc/kvmd/switch-edid.hex", type=valid_abs_path, unpack_as="default_edid_path"), "default_edid": Option("/etc/kvmd/switch-edid.hex", type=valid_abs_path, unpack_as="default_edid_path"),
"ignore_hpd_on_top": Option(False, type=valid_bool),
}, },
}, },

View File

@ -84,11 +84,12 @@ class Switch: # pylint: disable=too-many-public-methods
device_path: str, device_path: str,
default_edid_path: str, default_edid_path: str,
pst_unix_path: str, pst_unix_path: str,
ignore_hpd_on_top: bool,
) -> None: ) -> None:
self.__default_edid_path = default_edid_path self.__default_edid_path = default_edid_path
self.__chain = Chain(device_path) self.__chain = Chain(device_path, ignore_hpd_on_top)
self.__cache = StateCache() self.__cache = StateCache()
self.__storage = Storage(pst_unix_path) self.__storage = Storage(pst_unix_path)

View File

@ -177,8 +177,14 @@ class UnitAtxLedsEvent(BaseEvent):
# ===== # =====
class Chain: # pylint: disable=too-many-instance-attributes class Chain: # pylint: disable=too-many-instance-attributes
def __init__(self, device_path: str) -> None: def __init__(
self,
device_path: str,
ignore_hpd_on_top: bool,
) -> None:
self.__device = Device(device_path) self.__device = Device(device_path)
self.__ignore_hpd_on_top = ignore_hpd_on_top
self.__actual = False self.__actual = False
@ -293,6 +299,7 @@ class Chain: # pylint: disable=too-many-instance-attributes
if self.__select(): if self.__select():
for resp in self.__device.read_all(): for resp in self.__device.read_all():
self.__update_units(resp) self.__update_units(resp)
self.__adjust_quirks()
self.__adjust_start_port() self.__adjust_start_port()
self.__finish_changing_request(resp) self.__finish_changing_request(resp)
self.__consume_commands() self.__consume_commands()
@ -364,6 +371,15 @@ class Chain: # pylint: disable=too-many-instance-attributes
self.__units[resp.header.unit].atx_leds = resp.body self.__units[resp.header.unit].atx_leds = resp.body
self.__queue_event(UnitAtxLedsEvent(resp.header.unit, resp.body)) self.__queue_event(UnitAtxLedsEvent(resp.header.unit, resp.body))
def __adjust_quirks(self) -> None:
for (unit, ctx) in enumerate(self.__units):
if ctx.state is not None and (ctx.state.version.sw_dev or ctx.state.version.sw >= 7):
ignore_hpd = (unit == 0 and self.__ignore_hpd_on_top)
if ctx.state.quirks.ignore_hpd != ignore_hpd:
get_logger().info("Applying quirk ignore_hpd=%s to [%d] ...",
ignore_hpd, unit)
self.__device.request_set_quirks(unit, ignore_hpd)
def __adjust_start_port(self) -> None: def __adjust_start_port(self) -> None:
if self.__active_port < 0: if self.__active_port < 0:
for (unit, ctx) in enumerate(self.__units): for (unit, ctx) in enumerate(self.__units):

View File

@ -42,6 +42,7 @@ from .proto import BodyAtxClick
from .proto import BodySetEdid from .proto import BodySetEdid
from .proto import BodyClearEdid from .proto import BodyClearEdid
from .proto import BodySetColors from .proto import BodySetColors
from .proto import BodySetQuirks
# ===== # =====
@ -166,6 +167,9 @@ class Device:
def request_set_colors(self, unit: int, ch: int, colors: Colors) -> int: def request_set_colors(self, unit: int, ch: int, colors: Colors) -> int:
return self.__send_request(Header.SET_COLORS, unit, BodySetColors(ch, colors)) return self.__send_request(Header.SET_COLORS, unit, BodySetColors(ch, colors))
def request_set_quirks(self, unit: int, ignore_hpd: bool) -> int:
return self.__send_request(Header.SET_QUIRKS, unit, BodySetQuirks(ignore_hpd))
def __send_request(self, op: int, unit: int, body: (Packable | None)) -> int: def __send_request(self, op: int, unit: int, body: (Packable | None)) -> int:
assert self.__tty is not None assert self.__tty is not None
req = Request(Header( req = Request(Header(

View File

@ -60,6 +60,7 @@ class Header(Packable, Unpackable):
SET_EDID = 9 SET_EDID = 9
CLEAR_EDID = 10 CLEAR_EDID = 10
SET_COLORS = 12 SET_COLORS = 12
SET_QUIRKS = 13
__struct = struct.Struct("<BHBB") __struct = struct.Struct("<BHBB")
@ -89,6 +90,13 @@ class Nak(Unpackable):
return Nak(*cls.__struct.unpack_from(data, offset=offset)) return Nak(*cls.__struct.unpack_from(data, offset=offset))
@dataclasses.dataclass(frozen=True)
class UnitVersion:
hw: int
sw: int
sw_dev: bool
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class UnitFlags: class UnitFlags:
changing_busy: bool changing_busy: bool
@ -97,10 +105,14 @@ class UnitFlags:
has_hpd: bool has_hpd: bool
@dataclasses.dataclass(frozen=True)
class UnitQuirks:
ignore_hpd: bool
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class UnitState(Unpackable): # pylint: disable=too-many-instance-attributes class UnitState(Unpackable): # pylint: disable=too-many-instance-attributes
sw_version: int version: UnitVersion
hw_version: int
flags: UnitFlags flags: UnitFlags
ch: int ch: int
beacons: tuple[bool, bool, bool, bool, bool, bool] beacons: tuple[bool, bool, bool, bool, bool, bool]
@ -111,8 +123,9 @@ class UnitState(Unpackable): # pylint: disable=too-many-instance-attributes
video_crc: tuple[int, int, int, int] video_crc: tuple[int, int, int, int]
usb_5v_sens: tuple[bool, bool, bool, bool] usb_5v_sens: tuple[bool, bool, bool, bool]
atx_busy: tuple[bool, bool, bool, bool] atx_busy: tuple[bool, bool, bool, bool]
quirks: UnitQuirks
__struct = struct.Struct("<HHHBBHHHHHHBBBHHHHBxB30x") __struct = struct.Struct("<HHHBBHHHHHHBBBHHHHBxBB29x")
def compare_edid(self, ch: int, edid: Optional["Edid"]) -> bool: def compare_edid(self, ch: int, edid: Optional["Edid"]) -> bool:
if edid is None: if edid is None:
@ -129,11 +142,14 @@ class UnitState(Unpackable): # pylint: disable=too-many-instance-attributes
sw_version, hw_version, flags, ch, sw_version, hw_version, flags, ch,
beacons, nc0, nc1, nc2, nc3, nc4, nc5, beacons, nc0, nc1, nc2, nc3, nc4, nc5,
video_5v_sens, video_hpd, video_edid, vc0, vc1, vc2, vc3, video_5v_sens, video_hpd, video_edid, vc0, vc1, vc2, vc3,
usb_5v_sens, atx_busy, usb_5v_sens, atx_busy, quirks,
) = cls.__struct.unpack_from(data, offset=offset) ) = cls.__struct.unpack_from(data, offset=offset)
return UnitState( return UnitState(
sw_version, version=UnitVersion(
hw_version, hw=hw_version,
sw=(sw_version & 0x7FFF),
sw_dev=bool(sw_version & 0x8000),
),
flags=UnitFlags( flags=UnitFlags(
changing_busy=bool(flags & 0x80), changing_busy=bool(flags & 0x80),
flashing_busy=bool(flags & 0x40), flashing_busy=bool(flags & 0x40),
@ -149,6 +165,7 @@ class UnitState(Unpackable): # pylint: disable=too-many-instance-attributes
video_crc=(vc0, vc1, vc2, vc3), video_crc=(vc0, vc1, vc2, vc3),
usb_5v_sens=cls.__make_flags4(usb_5v_sens), usb_5v_sens=cls.__make_flags4(usb_5v_sens),
atx_busy=cls.__make_flags4(atx_busy), atx_busy=cls.__make_flags4(atx_busy),
quirks=UnitQuirks(ignore_hpd=bool(quirks & 0x01)),
) )
@classmethod @classmethod
@ -265,6 +282,14 @@ class BodySetColors(Packable):
return self.ch.to_bytes() + self.colors.pack() return self.ch.to_bytes() + self.colors.pack()
@dataclasses.dataclass(frozen=True)
class BodySetQuirks(Packable):
ignore_hpd: bool
def pack(self) -> bytes:
return self.ignore_hpd.to_bytes()
# ===== # =====
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class Request: class Request:

View File

@ -49,7 +49,7 @@ class _UnitInfo:
# ===== # =====
class StateCache: # pylint: disable=too-many-instance-attributes class StateCache: # pylint: disable=too-many-instance-attributes
__FW_VERSION = 6 __FW_VERSION = 7
__FULL = 0xFFFF __FULL = 0xFFFF
__SUMMARY = 0x01 __SUMMARY = 0x01
@ -195,7 +195,10 @@ class StateCache: # pylint: disable=too-many-instance-attributes
assert ui.state is not None assert ui.state is not None
assert ui.atx_leds is not None assert ui.atx_leds is not None
if x_model: if x_model:
state["model"]["units"].append({"firmware": {"version": ui.state.sw_version}}) state["model"]["units"].append({"firmware": {
"version": ui.state.version.sw,
"devbuild": ui.state.version.sw_dev,
}})
if x_video: if x_video:
state["video"]["links"].extend(ui.state.video_5v_sens[:4]) state["video"]["links"].extend(ui.state.video_5v_sens[:4])
if x_usb: if x_usb:

Binary file not shown.

View File

@ -408,7 +408,8 @@ export function Switch() {
$("switch-chain").innerHTML = content; $("switch-chain").innerHTML = content;
if (model.units.length > 0) { if (model.units.length > 0) {
tools.hidden.setVisible($("switch-message-update"), (model.firmware.version > model.units[0].firmware.version)); let fw = model.units[0].firmware;
tools.hidden.setVisible($("switch-message-update"), (fw.devbuild || fw.version < model.firmware.version));
} }
for (let unit = 0; unit < model.units.length; ++unit) { for (let unit = 0; unit < model.units.length; ++unit) {