diff --git a/goodix.py b/goodix.py index b98009d..37c355e 100644 --- a/goodix.py +++ b/goodix.py @@ -152,18 +152,52 @@ def __init__(self, print(f"__init__({product}, {proto}, {timeout})") self.protocol: protocol.Protocol = proto(0x27c6, product, timeout) + self._pending: bytes | None = None # FIXME Empty device's reply buffer # (Current patch while waiting for a fix) if isinstance(self.protocol, protocol.USBProtocol): self.empty_buffer() + def read(self, size: int = 0x10000, timeout: float | None = 5): + """Read a frame, returning any frame put back by _expect_ack() first.""" + if self._pending is not None: + frame, self._pending = self._pending, None + return frame + + return self.protocol.read(size, timeout) + + def _expect_ack(self, command: int): + """Consume the ACK for `command`, if this sensor sends one. + + Not every sensor ACKs every command. 27c6:533c (GF5288_GM168SEC) ACKs + reset and read_sensor_register but answers firmware_version and + enable_chip with the reply alone. Requiring an ACK unconditionally + consumes that reply and then fails to parse it as an ACK, so those + commands error out on a perfectly healthy device. When the frame we + get is not an ACK, put it back for the caller to read. + """ + if not isinstance(self.protocol, protocol.USBProtocol): + return + + frame = self.read() + message = check_message_pack(frame) + payload, reply_command, _ = decode_message_protocol(message) + + if reply_command != COMMAND_ACK: + self._pending = frame + return + + check_ack(payload, command) + def empty_buffer(self): print("empty_buffer()") + self._pending = None + try: while True: - self.protocol.read(timeout=0.1) + self.read(timeout=0.1) except usb.core.USBTimeoutError as error: if error.backend_error_code == -7: @@ -186,7 +220,8 @@ def nop(self): checksum=False))) try: - message = self.protocol.read(timeout=0.1) + # some sensors do not answer NOP at all; a timeout here is fine + self._pending = self.read(timeout=0.1) except usb.core.USBTimeoutError as error: if error.backend_error_code == -7: @@ -194,10 +229,7 @@ def nop(self): raise error - if isinstance(self.protocol, protocol.USBProtocol): - check_ack( - check_message_protocol(check_message_pack(message), - COMMAND_ACK), COMMAND_NOP) + self._expect_ack(COMMAND_NOP) def mcu_get_image(self, payload: bytes, flags: int): print("mcu_get_image()") @@ -206,13 +238,9 @@ def mcu_get_image(self, payload: bytes, flags: int): encode_message_pack( encode_message_protocol(payload, COMMAND_MCU_GET_IMAGE))) - if isinstance(self.protocol, protocol.USBProtocol): - check_ack( - check_message_protocol( - check_message_pack(self.protocol.read()), COMMAND_ACK), - COMMAND_MCU_GET_IMAGE) + self._expect_ack(COMMAND_MCU_GET_IMAGE) - return check_message_pack(self.protocol.read(), flags) + return check_message_pack(self.read(), flags) def mcu_switch_to_fdt_down(self, mode: bytes, reply: bool): print(f"mcu_switch_to_fdt_down({mode}, {reply})") @@ -221,17 +249,13 @@ def mcu_switch_to_fdt_down(self, mode: bytes, reply: bool): encode_message_pack( encode_message_protocol(mode, COMMAND_MCU_SWITCH_TO_FDT_DOWN))) - if isinstance(self.protocol, protocol.USBProtocol): - check_ack( - check_message_protocol( - check_message_pack(self.protocol.read()), COMMAND_ACK), - COMMAND_MCU_SWITCH_TO_FDT_DOWN) + self._expect_ack(COMMAND_MCU_SWITCH_TO_FDT_DOWN) if not reply: return None return check_message_protocol( - check_message_pack(self.protocol.read(timeout=None)), + check_message_pack(self.read(timeout=None)), COMMAND_MCU_SWITCH_TO_FDT_DOWN) def mcu_switch_to_fdt_up(self, mode: bytes): @@ -241,14 +265,10 @@ def mcu_switch_to_fdt_up(self, mode: bytes): encode_message_pack( encode_message_protocol(mode, COMMAND_MCU_SWITCH_TO_FDT_UP))) - if isinstance(self.protocol, protocol.USBProtocol): - check_ack( - check_message_protocol( - check_message_pack(self.protocol.read()), COMMAND_ACK), - COMMAND_MCU_SWITCH_TO_FDT_UP) + self._expect_ack(COMMAND_MCU_SWITCH_TO_FDT_UP) return check_message_protocol( - check_message_pack(self.protocol.read(timeout=None)), + check_message_pack(self.read(timeout=None)), COMMAND_MCU_SWITCH_TO_FDT_UP) def mcu_switch_to_fdt_mode(self, mode: bytes, reply: bool): @@ -258,16 +278,12 @@ def mcu_switch_to_fdt_mode(self, mode: bytes, reply: bool): encode_message_pack( encode_message_protocol(mode, COMMAND_MCU_SWITCH_TO_FDT_MODE))) - if isinstance(self.protocol, protocol.USBProtocol): - check_ack( - check_message_protocol( - check_message_pack(self.protocol.read()), COMMAND_ACK), - COMMAND_MCU_SWITCH_TO_FDT_MODE) + self._expect_ack(COMMAND_MCU_SWITCH_TO_FDT_MODE) if not reply: return None - return check_message_protocol(check_message_pack(self.protocol.read()), + return check_message_protocol(check_message_pack(self.read()), COMMAND_MCU_SWITCH_TO_FDT_MODE) def nav(self): @@ -277,13 +293,9 @@ def nav(self): encode_message_pack( encode_message_protocol(b"\x01\x00", COMMAND_NAV))) - if isinstance(self.protocol, protocol.USBProtocol): - check_ack( - check_message_protocol( - check_message_pack(self.protocol.read()), COMMAND_ACK), - COMMAND_NAV) + self._expect_ack(COMMAND_NAV) - return check_message_protocol(check_message_pack(self.protocol.read()), + return check_message_protocol(check_message_pack(self.read()), COMMAND_NAV, False) def mcu_switch_to_sleep_mode(self): @@ -294,11 +306,7 @@ def mcu_switch_to_sleep_mode(self): encode_message_protocol(b"\x01\x00", COMMAND_MCU_SWITCH_TO_SLEEP_MODE))) - if isinstance(self.protocol, protocol.USBProtocol): - check_ack( - check_message_protocol( - check_message_pack(self.protocol.read()), COMMAND_ACK), - COMMAND_MCU_SWITCH_TO_SLEEP_MODE) + self._expect_ack(COMMAND_MCU_SWITCH_TO_SLEEP_MODE) def mcu_switch_to_idle_mode(self, sleep_time: int): print(f"mcu_switch_to_idle_mode({sleep_time})") @@ -309,11 +317,7 @@ def mcu_switch_to_idle_mode(self, sleep_time: int): struct.pack("