fix test hung

Signed-off-by: HiFiPhile <admin@hifiphile.com>
diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py
index ed9ebbf..527679d 100755
--- a/test/hil/hil_test.py
+++ b/test/hil/hil_test.py
@@ -134,6 +134,9 @@
 
 CMD_TIMEOUT = int(os.getenv('HIL_CMD_TIMEOUT', '180'))
 POOL_TIMEOUT = int(os.getenv('HIL_POOL_TIMEOUT', '3000'))
+SERIAL_READ_TIMEOUT = float(os.getenv('HIL_SERIAL_READ_TIMEOUT', '5'))
+SERIAL_WRITE_TIMEOUT = float(os.getenv('HIL_SERIAL_WRITE_TIMEOUT', '2'))
+SERIAL_WRITE_DEADLINE = float(os.getenv('HIL_SERIAL_WRITE_DEADLINE', '10'))
 
 
 def cmd_stdout_text(out: Any) -> str:
@@ -218,7 +221,8 @@
     while timeout > 0:
         if os.path.exists(port):
             try:
-                ser = serial.Serial(port, baudrate=115200, timeout=5)
+                ser = serial.Serial(port, baudrate=115200, timeout=SERIAL_READ_TIMEOUT,
+                                    write_timeout=SERIAL_WRITE_TIMEOUT)
                 break
             except serial.SerialException:
                 print(f'serial {port} not reaady {timeout} sec')
@@ -231,6 +235,26 @@
     return ser
 
 
+def serial_write_all(ser: serial.Serial, data: bytes, deadline: float = SERIAL_WRITE_DEADLINE):
+    total = 0
+    end = time.monotonic() + deadline
+
+    while total < len(data):
+        try:
+            written = ser.write(data[total:])
+        except serial.SerialTimeoutException:
+            written = 0
+
+        if written:
+            total += written
+            continue
+
+        if time.monotonic() >= end:
+            raise AssertionError(f'Serial write timeout after {deadline:.1f}s')
+
+        time.sleep(0.01)
+
+
 def read_disk_file(uid: str, lun: int, fname: str) -> bytes:
     # open_fs("fat://{dev}) require 'pip install pyfatfs'
     dev = get_disk_dev(uid, 'TinyUSB', lun)
@@ -698,8 +722,7 @@
     offset = 0
     while offset < echo_len:
         chunk_size = min(random.randint(1, packet_size), echo_len - offset)
-        ser.write(echo_data[offset:offset + chunk_size])
-        ser.flush()
+        serial_write_all(ser, echo_data[offset:offset + chunk_size])
         # wait until this chunk is echoed back
         echo = b''
         t_end = time.monotonic() + 1.0
@@ -748,8 +771,7 @@
     time.sleep(1)
     ser.reset_input_buffer()
     for ch in 'cat README.TXT\r':
-        ser.write(ch.encode())
-        ser.flush()
+        serial_write_all(ser, ch.encode())
         time.sleep(0.002)
 
     resp = b''
@@ -771,8 +793,7 @@
     time.sleep(0.5)
     ser.reset_input_buffer()
     for ch in 'dd 1024\r':
-        ser.write(ch.encode())
-        ser.flush()
+        serial_write_all(ser, ch.encode())
         time.sleep(0.002)
 
     # Read dd output until prompt
@@ -827,8 +848,7 @@
         # Write in chunks of random 1-64 bytes (device has 64-byte buffer)
         while offset < payload_len:
             chunk_size = min(random.randint(1, 64), payload_len - offset)
-            ser[writer].write(payload[offset:offset + chunk_size])
-            ser[writer].flush()
+            serial_write_all(ser[writer], payload[offset:offset + chunk_size])
             rd0 += ser[0].read(chunk_size)
             rd1 += ser[1].read(chunk_size)
             offset += chunk_size
@@ -862,8 +882,7 @@
         # Write in chunks of random 1-64 bytes (device has 64-byte buffer)
         while offset < size:
             chunk_size = min(random.randint(1, 64), size - offset)
-            ser.write(test_str[offset:offset + chunk_size])
-            ser.flush()
+            serial_write_all(ser, test_str[offset:offset + chunk_size])
             rd_str += ser.read(chunk_size)
             offset += chunk_size
         assert rd_str == test_str, f'CDC wrong data ({size} bytes):\n  expected: {test_str}\n  received: {rd_str}'
@@ -1124,8 +1143,7 @@
         offset = 0
         while offset < size:
             chunk_size = min(random.randint(1, 64), size - offset)
-            ser.write(test_data[offset:offset + chunk_size])
-            ser.flush()
+            serial_write_all(ser, test_data[offset:offset + chunk_size])
             time.sleep(0.01)
             offset += chunk_size