From: 
Subject: Debian changes

The Debian packaging of python-serialx is maintained in git, using a workflow
similar to the one described in dgit-maint-merge(7).
The Debian delta is represented by this one combined patch; there isn't a
patch queue that can be represented as a quilt series.

A detailed breakdown of the changes is available from their canonical
representation -- git commits in the packaging repository.
For example, to see the changes made by the Debian maintainer in the first
upload of upstream version 1.2.3, you could use:

    % git clone https://git.dgit.debian.org/python-serialx
    % cd python-serialx
    % git log --oneline 1.2.3..debian/1.2.3-1 -- . ':!debian'

(If you have dgit, use `dgit clone python-serialx`, rather than plain `git clone`.)

We don't use debian/source/options single-debian-patch because it has bugs.
Therefore, NMUs etc. may nevertheless have made additional patches.

---

diff --git a/Cargo.toml b/Cargo.toml
index 3cce8f1..d59aeea 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,12 +8,4 @@ name = "_serialx_rust"
 crate-type = ["cdylib"]
 
 [dependencies]
-pyo3 = { version = "0.23", features = ["extension-module", "abi3-py310"] }
-
-[target.'cfg(target_os = "macos")'.dependencies]
-core-foundation = "0.10"
-core-foundation-sys = "0.8"
-io-kit-sys = "0.4"
-
-[target.'cfg(target_os = "windows")'.dependencies]
-serialport = { version = "4", features = ["usbportinfo-interface"] }
+pyo3 = { version = ">=0.27,<0.29", features = ["extension-module", "abi3-py310"] }
diff --git a/serialx/descriptor_transport.py b/serialx/descriptor_transport.py
index 875fad5..b93eb27 100644
--- a/serialx/descriptor_transport.py
+++ b/serialx/descriptor_transport.py
@@ -80,7 +80,7 @@ class DescriptorTransport(BaseSerialTransport):
         )
 
         try:
-            self._fileno = await self._open_fut
+            self._fileno = await asyncio.shield(self._open_fut)
         except asyncio.CancelledError:
             # `os.open` may still finish in the executor after cancellation. If that
             # happens, close the resulting fd to avoid leaks.
diff --git a/serialx/platforms/serial_linux.py b/serialx/platforms/serial_linux.py
index 5df26c5..87dbfa3 100644
--- a/serialx/platforms/serial_linux.py
+++ b/serialx/platforms/serial_linux.py
@@ -10,6 +10,7 @@ import ctypes
 import errno
 import fcntl
 import logging
+import os
 from pathlib import Path
 import sys
 import termios
@@ -29,13 +30,44 @@ PORT_UNKNOWN = 0
 ASYNC_LOW_LATENCY = 1 << 13
 CMSPAR = 0o10000000000
 TCGETS = 0x5401
-TCGETS2 = 0x802C542A
-TCSETS2 = 0x402C542B
+
+IS_POWERPC = os.uname().machine.startswith("ppc")
+
+_IOC_NRBITS = 8
+_IOC_TYPEBITS = 8
+_IOC_SIZEBITS = 14
+_IOC_WRITE = 1
+_IOC_READ = 2
+
+if IS_POWERPC:
+    _IOC_SIZEBITS = 13
+    _IOC_WRITE = 4
+
+_IOC_NRSHIFT = 0
+_IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS
+_IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS
+_IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS
+
+
+def _ioc(direction: int, request_type: int, number: int, size: int) -> int:
+    return (
+        (direction << _IOC_DIRSHIFT)
+        | (request_type << _IOC_TYPESHIFT)
+        | (number << _IOC_NRSHIFT)
+        | (size << _IOC_SIZESHIFT)
+    )
+
 
 TIOCGSERIAL = getattr(termios, "TIOCGSERIAL", None)
 TIOCSSERIAL = getattr(termios, "TIOCSSERIAL", None)
-CBAUD = getattr(termios, "CBAUD", 0o00010017)
-CBAUDEX = getattr(termios, "CBAUDEX", 0o00010000)
+if IS_POWERPC:
+    CBAUD = 0x000000FF
+    CBAUDEX = 0x00000000
+    BOTHER = 0x0000001F
+else:
+    CBAUD = getattr(termios, "CBAUD", 0o00010017)
+    CBAUDEX = getattr(termios, "CBAUDEX", 0o00010000)
+    BOTHER = getattr(termios, "BOTHER", CBAUDEX)
 
 # When we need to set a non-POSIX baudrate, we set the baudrates to a known default and
 # then override
@@ -52,16 +84,37 @@ class Termios2Struct(ctypes.Structure):
 
     _pack_ = 1
     _layout_ = "ms"
-    _fields_ = (
-        ("c_iflag", ctypes.c_uint32),
-        ("c_oflag", ctypes.c_uint32),
-        ("c_cflag", ctypes.c_uint32),
-        ("c_lflag", ctypes.c_uint32),
-        ("c_line", ctypes.c_uint8),
-        ("c_cc", ctypes.c_uint8 * NCCS),
-        ("c_ispeed", ctypes.c_uint32),
-        ("c_ospeed", ctypes.c_uint32),
-    )
+
+    if IS_POWERPC:
+        _fields_ = (
+            ("c_iflag", ctypes.c_uint32),
+            ("c_oflag", ctypes.c_uint32),
+            ("c_cflag", ctypes.c_uint32),
+            ("c_lflag", ctypes.c_uint32),
+            ("c_cc", ctypes.c_uint8 * NCCS),
+            ("c_line", ctypes.c_uint8),
+            ("c_ispeed", ctypes.c_uint32),
+            ("c_ospeed", ctypes.c_uint32),
+        )
+    else:
+        _fields_ = (
+            ("c_iflag", ctypes.c_uint32),
+            ("c_oflag", ctypes.c_uint32),
+            ("c_cflag", ctypes.c_uint32),
+            ("c_lflag", ctypes.c_uint32),
+            ("c_line", ctypes.c_uint8),
+            ("c_cc", ctypes.c_uint8 * NCCS),
+            ("c_ispeed", ctypes.c_uint32),
+            ("c_ospeed", ctypes.c_uint32),
+        )
+
+
+if IS_POWERPC:
+    TCGETS2 = _ioc(_IOC_READ, ord("t"), 19, ctypes.sizeof(Termios2Struct))
+    TCSETS2 = _ioc(_IOC_WRITE, ord("t"), 20, ctypes.sizeof(Termios2Struct))
+else:
+    TCGETS2 = _ioc(_IOC_READ, ord("T"), 0x2A, ctypes.sizeof(Termios2Struct))
+    TCSETS2 = _ioc(_IOC_WRITE, ord("T"), 0x2B, ctypes.sizeof(Termios2Struct))
 
 
 class LinuxSerial(ExtendedPosixSerial):
@@ -86,13 +139,17 @@ class LinuxSerial(ExtendedPosixSerial):
 
         termios2 = Termios2Struct.from_buffer(buffer)
 
-        # Sanity check that our struct layout matches the kernel's
-        if termios2.c_ispeed == 0 or termios2.c_ospeed == 0:
+        # A zero-filled readback means the ioctl/struct ABI is not the one we expect.
+        # PowerPC can report zero speed fields until BOTHER is written, so only the
+        # all-zero case is invalid there.
+        if not any(buffer) or (
+            not IS_POWERPC and (termios2.c_ispeed == 0 or termios2.c_ospeed == 0)
+        ):
             raise RuntimeError(f"termios2 speed fields are zero: {buffer.hex()}")
 
         # The POSIX baudrates are stored in the lower bits of `c_cflag`. We clear them.
         termios2.c_cflag &= ~CBAUD
-        termios2.c_cflag |= CBAUDEX
+        termios2.c_cflag |= BOTHER
 
         termios2.c_ispeed = baudrate
         termios2.c_ospeed = baudrate
diff --git a/serialx/platforms/serial_posix.py b/serialx/platforms/serial_posix.py
index 8eef54d..5a84b40 100644
--- a/serialx/platforms/serial_posix.py
+++ b/serialx/platforms/serial_posix.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import asyncio
+import array
 import errno
 import fcntl
 import logging
@@ -320,8 +321,7 @@ class PosixSerial(BaseSerial):
         """Get current modem control bits."""
         assert self._fileno is not None
 
-        # A `bytearray` is critical here: `bytes` will not be mutated
-        buffer = bytearray((0x00000000).to_bytes(4, "little"))
+        buffer = array.array("i", [0x00000000])
 
         try:
             fcntl.ioctl(self._fileno, termios.TIOCMGET, buffer)
@@ -330,7 +330,7 @@ class PosixSerial(BaseSerial):
                 LOGGER.debug("Device is not a serial port, cannot get modem pins")
                 return ModemPins()
 
-        n = int.from_bytes(buffer, "little")
+        n = buffer[0]
         return ModemPins(
             **{
                 name: PinState.HIGH if n & bit else PinState.LOW
@@ -353,7 +353,9 @@ class PosixSerial(BaseSerial):
             if all_pins_set:
                 value = modem_pins_as_int(modem_pins)
                 LOGGER.debug("Setting all with TIOCMSET: 0x%08X", value)
-                fcntl.ioctl(self._fileno, termios.TIOCMSET, value.to_bytes(4, "little"))
+                fcntl.ioctl(
+                    self._fileno, termios.TIOCMSET, array.array("i", [value])
+                )
             else:
                 to_set = modem_pins_mask_of_value(modem_pins, PinState.HIGH)
                 to_clear = modem_pins_mask_of_value(modem_pins, PinState.LOW)
@@ -361,13 +363,13 @@ class PosixSerial(BaseSerial):
                 if to_set:
                     LOGGER.debug("Setting TIOCMBIS: 0x%08X", to_set)
                     fcntl.ioctl(
-                        self._fileno, termios.TIOCMBIS, to_set.to_bytes(4, "little")
+                        self._fileno, termios.TIOCMBIS, array.array("i", [to_set])
                     )
 
                 if to_clear:
                     LOGGER.debug("TIOCMBIC: 0x%08X", to_clear)
                     fcntl.ioctl(
-                        self._fileno, termios.TIOCMBIC, to_clear.to_bytes(4, "little")
+                        self._fileno, termios.TIOCMBIC, array.array("i", [to_clear])
                     )
         except OSError as exc:
             if exc.errno == errno.ENOTTY:
@@ -461,20 +463,20 @@ class PosixSerial(BaseSerial):
     def num_unread_bytes(self) -> int:
         """Return the number of bytes waiting to be read."""
         assert self._fileno is not None
-        buffer = bytearray((0x00000000).to_bytes(4, "little"))
+        buffer = array.array("i", [0x00000000])
 
         fcntl.ioctl(self._fileno, termios.FIONREAD, buffer)
 
-        return int.from_bytes(buffer, "little")
+        return buffer[0]
 
     def num_unwritten_bytes(self) -> int:
         """Return the number of bytes waiting to be written."""
         assert self._fileno is not None
-        buffer = bytearray((0x00000000).to_bytes(4, "little"))
+        buffer = array.array("i", [0x00000000])
 
         fcntl.ioctl(self._fileno, termios.TIOCOUTQ, buffer)
 
-        return int.from_bytes(buffer, "little")
+        return buffer[0]
 
     def _reset_read_buffer(self) -> None:
         """Reset the read buffer."""
diff --git a/serialx/platforms/serial_win32.py b/serialx/platforms/serial_win32.py
index 52fcae7..637aaad 100644
--- a/serialx/platforms/serial_win32.py
+++ b/serialx/platforms/serial_win32.py
@@ -557,7 +557,7 @@ class Win32SerialTransport(BaseSerialTransport):
         self._open_fut = cast(asyncio.Future[int], open_fut)
 
         try:
-            handle = await self._open_fut
+            handle = await asyncio.shield(self._open_fut)
         except asyncio.CancelledError:
             self._open_fut.add_done_callback(self._on_cancelled_open_done)
             raise
diff --git a/tests/test_serial_linux.py b/tests/test_serial_linux.py
index 69d44e4..7b1679e 100644
--- a/tests/test_serial_linux.py
+++ b/tests/test_serial_linux.py
@@ -19,8 +19,8 @@ from typing import Any
 from unittest.mock import ANY, call, patch
 
 from serialx.platforms.serial_linux import (
+    BOTHER,
     CBAUD,
-    CBAUDEX,
     TCGETS2,
     TCSETS2,
     LinuxSerial,
@@ -57,7 +57,7 @@ def test_set_non_posix_baudrate_handles_actual_hardware_rate() -> None:
     # the requested 115200.
     initial = Termios2Struct(
         c_cflag=(
-            termios.CS8 | termios.CREAD | termios.HUPCL | termios.CLOCAL | CBAUDEX
+            termios.CS8 | termios.CREAD | termios.HUPCL | termios.CLOCAL | BOTHER
         ),
         c_ispeed=115384,
         c_ospeed=115384,
@@ -77,8 +77,8 @@ def test_set_non_posix_baudrate_handles_actual_hardware_rate() -> None:
     written = Termios2Struct.from_buffer_copy(captured[0])
     assert written.c_ispeed == 250000
     assert written.c_ospeed == 250000
-    # CBAUDEX should be the only CBAUD bit set, signalling "use ispeed/ospeed"
-    assert written.c_cflag & CBAUD == CBAUDEX
+    # BOTHER should be the only CBAUD bit set, signalling "use ispeed/ospeed"
+    assert written.c_cflag & CBAUD == BOTHER
 
 
 def test_set_non_posix_baudrate_zero_speed_raises() -> None:
