Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bumble/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ async def reset(self, driver_factory=drivers.get_driver_for_host):
logger.debug(
'HCI LE flow control: '
f'le_acl_data_packet_length={le_acl_data_packet_length},'
f'total_num_le_acl_data_packets={total_num_le_acl_data_packets}'
f'total_num_le_acl_data_packets={total_num_le_acl_data_packets},'
f'iso_data_packet_length={iso_data_packet_length},'
f'total_num_iso_data_packets={total_num_iso_data_packets}'
)
Expand Down
6 changes: 5 additions & 1 deletion bumble/transport/android_netsim.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,11 @@ def publish_grpc_port(grpc_port: int, instance_number: int) -> bool:

def cleanup():
logger.debug("removing .ini file")
ini_file.unlink()
try:
ini_file.unlink()
except OSError as error:
# Don't log at exception level, since this may happen normally.
logger.debug(f'failed to remove .ini file ({error})')

atexit.register(cleanup)
return True
Expand Down
58 changes: 56 additions & 2 deletions bumble/transport/serial.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# -----------------------------------------------------------------------------
import asyncio
import logging
from typing import Optional

import serial_asyncio

Expand All @@ -28,25 +29,56 @@
logger = logging.getLogger(__name__)


# -----------------------------------------------------------------------------
# Constants
# -----------------------------------------------------------------------------
DEFAULT_POST_OPEN_DELAY = 0.5 # in seconds

# -----------------------------------------------------------------------------
# Classes and Functions
# -----------------------------------------------------------------------------


# -----------------------------------------------------------------------------
class SerialPacketSource(StreamPacketSource):
def __init__(self) -> None:
super().__init__()
self._ready = asyncio.Event()

async def wait_until_ready(self) -> None:
await self._ready.wait()

def connection_made(self, transport: asyncio.BaseTransport) -> None:
logger.debug('connection made')
self._ready.set()

def connection_lost(self, exc: Optional[Exception]) -> None:
logger.debug('connection lost')
self.on_transport_lost()


# -----------------------------------------------------------------------------
async def open_serial_transport(spec: str) -> Transport:
'''
Open a serial port transport.
The parameter string has this syntax:
<device-path>[,<speed>][,rtscts][,dsrdtr]
<device-path>[,<speed>][,rtscts][,dsrdtr][,delay]
When <speed> is omitted, the default value of 1000000 is used
When "rtscts" is specified, RTS/CTS hardware flow control is enabled
When "dsrdtr" is specified, DSR/DTR hardware flow control is enabled
When "delay" is specified, a short delay is added after opening the port

Examples:
/dev/tty.usbmodem0006839912172
/dev/tty.usbmodem0006839912172,1000000
/dev/tty.usbmodem0006839912172,rtscts
/dev/tty.usbmodem0006839912172,rtscts,delay
'''

speed = 1000000
rtscts = False
dsrdtr = False
delay = 0.0
if ',' in spec:
parts = spec.split(',')
device = parts[0]
Expand All @@ -55,18 +87,40 @@ async def open_serial_transport(spec: str) -> Transport:
rtscts = True
elif part == 'dsrdtr':
dsrdtr = True
elif part == 'delay':
delay = DEFAULT_POST_OPEN_DELAY
elif part.isnumeric():
speed = int(part)
else:
device = spec

serial_transport, packet_source = await serial_asyncio.create_serial_connection(
asyncio.get_running_loop(),
StreamPacketSource,
SerialPacketSource,
device,
baudrate=speed,
rtscts=rtscts,
dsrdtr=dsrdtr,
)
packet_sink = StreamPacketSink(serial_transport)

logger.debug('waiting for the port to be ready')
await packet_source.wait_until_ready()
logger.debug('port is ready')

# Try to assert DTR
assert serial_transport.serial is not None
try:
serial_transport.serial.dtr = True
logger.debug(
f"DSR={serial_transport.serial.dsr}, DTR={serial_transport.serial.dtr}"
)
except Exception as e:
logger.warning(f'could not assert DTR: {e}')

# Wait a bit after opening the port, if requested
if delay > 0.0:
logger.debug(f'waiting {delay} seconds after opening the port')
await asyncio.sleep(delay)

return Transport(packet_source, packet_sink)
17 changes: 13 additions & 4 deletions docs/mkdocs/src/transports/serial.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,18 @@ SERIAL TRANSPORT
The serial transport implements sending/receiving HCI packets over a UART (a.k.a serial port).

## Moniker
The moniker syntax for a serial transport is: `serial:<device-path>[,<speed>]`
When `<speed>` is omitted, the default value of 1000000 is used
The moniker syntax for a serial transport is:
`<device-path>[,<speed>][,rtscts][,dsrdtr][,delay]`

When `<speed>` is omitted, the default value of 1000000 is used.
When `rtscts` is specified, RTS/CTS hardware flow control is enabled.
When `dsrdtr` is specified, DSR/DTR hardware flow control is enabled.
When `delay` is specified, a short delay is added after opening the port.

!!! example
`serial:/dev/tty.usbmodem0006839912172,1000000`
Opens the serial port `/dev/tty.usbmodem0006839912172` at `1000000`bps
```
/dev/tty.usbmodem0006839912172
/dev/tty.usbmodem0006839912172,1000000
/dev/tty.usbmodem0006839912172,rtscts
/dev/tty.usbmodem0006839912172,rtscts,delay
```
Loading