Cleanup legacy L2CAP API

This commit is contained in:
Josh Wu
2023-10-11 13:49:02 +08:00
parent 7255a09705
commit 5a85765360
14 changed files with 98 additions and 54 deletions

View File

@@ -24,6 +24,7 @@ import time
import click
from bumble import l2cap
from bumble.core import (
BT_BR_EDR_TRANSPORT,
BT_LE_TRANSPORT,
@@ -85,6 +86,7 @@ DEFAULT_LINGER_TIME = 1.0
DEFAULT_RFCOMM_CHANNEL = 8
# -----------------------------------------------------------------------------
# Utils
# -----------------------------------------------------------------------------
@@ -197,6 +199,7 @@ class PacketType(enum.IntEnum):
PACKET_FLAG_LAST = 1
# -----------------------------------------------------------------------------
# Sender
# -----------------------------------------------------------------------------
@@ -659,17 +662,19 @@ class L2capClient(StreamedPacketIO):
self.mps = mps
self.ready = asyncio.Event()
async def on_connection(self, connection):
async def on_connection(self, connection: Connection) -> None:
connection.on('disconnection', self.on_disconnection)
# Connect a new L2CAP channel
print(color(f'>>> Opening L2CAP channel on PSM = {self.psm}', 'yellow'))
try:
l2cap_channel = await connection.open_l2cap_channel(
psm=self.psm,
max_credits=self.max_credits,
mtu=self.mtu,
mps=self.mps,
l2cap_channel = await connection.create_l2cap_channel(
spec=l2cap.LeCreditBasedChannelSpec(
psm=self.psm,
max_credits=self.max_credits,
mtu=self.mtu,
mps=self.mps,
)
)
print(color('*** L2CAP channel:', 'cyan'), l2cap_channel)
except Exception as error:
@@ -695,7 +700,7 @@ class L2capClient(StreamedPacketIO):
class L2capServer(StreamedPacketIO):
def __init__(
self,
device,
device: Device,
psm=DEFAULT_L2CAP_PSM,
max_credits=DEFAULT_L2CAP_MAX_CREDITS,
mtu=DEFAULT_L2CAP_MTU,
@@ -706,12 +711,11 @@ class L2capServer(StreamedPacketIO):
self.ready = asyncio.Event()
# Listen for incoming L2CAP CoC connections
device.register_l2cap_channel_server(
psm=psm,
server=self.on_l2cap_channel,
max_credits=max_credits,
mtu=mtu,
mps=mps,
device.create_l2cap_server(
spec=l2cap.LeCreditBasedChannelSpec(
psm=psm, mtu=mtu, mps=mps, max_credits=max_credits
),
handler=self.on_l2cap_channel,
)
print(color(f'### Listening for CoC connection on PSM {psm}', 'yellow'))

View File

@@ -21,6 +21,7 @@ import struct
import logging
import click
from bumble import l2cap
from bumble.colors import color
from bumble.device import Device, Peer
from bumble.core import AdvertisingData
@@ -204,7 +205,7 @@ class GattlinkHubBridge(GattlinkL2capEndpoint, Device.Listener):
# -----------------------------------------------------------------------------
class GattlinkNodeBridge(GattlinkL2capEndpoint, Device.Listener):
def __init__(self, device):
def __init__(self, device: Device):
super().__init__()
self.device = device
self.peer = None
@@ -218,7 +219,12 @@ class GattlinkNodeBridge(GattlinkL2capEndpoint, Device.Listener):
# Listen for incoming L2CAP CoC connections
psm = 0xFB
device.register_l2cap_channel_server(0xFB, self.on_coc)
device.create_l2cap_server(
spec=l2cap.LeCreditBasedChannelSpec(
psm=0xFB,
),
handler=self.on_coc,
)
print(f'### Listening for CoC connection on PSM {psm}')
# Setup the Gattlink service

View File

@@ -20,6 +20,7 @@ import logging
import os
import click
from bumble import l2cap
from bumble.colors import color
from bumble.transport import open_transport_or_link
from bumble.device import Device
@@ -47,14 +48,13 @@ class ServerBridge:
self.tcp_host = tcp_host
self.tcp_port = tcp_port
async def start(self, device):
async def start(self, device: Device) -> None:
# Listen for incoming L2CAP CoC connections
device.register_l2cap_channel_server(
psm=self.psm,
server=self.on_coc,
max_credits=self.max_credits,
mtu=self.mtu,
mps=self.mps,
device.create_l2cap_server(
spec=l2cap.LeCreditBasedChannelSpec(
psm=self.psm, mtu=self.mtu, mps=self.mps, max_credits=self.max_credits
),
handler=self.on_coc,
)
print(color(f'### Listening for CoC connection on PSM {self.psm}', 'yellow'))
@@ -195,11 +195,13 @@ class ClientBridge:
# Connect a new L2CAP channel
print(color(f'>>> Opening L2CAP channel on PSM = {self.psm}', 'yellow'))
try:
l2cap_channel = await connection.open_l2cap_channel(
psm=self.psm,
max_credits=self.max_credits,
mtu=self.mtu,
mps=self.mps,
l2cap_channel = await connection.create_l2cap_channel(
spec=l2cap.LeCreditBasedChannelSpec(
psm=self.psm,
max_credits=self.max_credits,
mtu=self.mtu,
mps=self.mps,
)
)
print(color('*** L2CAP channel:', 'cyan'), l2cap_channel)
except Exception as error:

View File

@@ -641,7 +641,7 @@ class Speaker:
self.device.on('connection', self.on_bluetooth_connection)
# Create a listener to wait for AVDTP connections
self.listener = Listener(Listener.create_registrar(self.device))
self.listener = Listener.for_device(self.device)
self.listener.on('connection', self.on_avdtp_connection)
print(f'Speaker ready to play, codec={color(self.codec, "cyan")}')

View File

@@ -1036,9 +1036,11 @@ class LeCreditBasedChannel(EventEmitter):
out_queue: Deque[bytes]
connection_result: Optional[asyncio.Future[LeCreditBasedChannel]]
disconnection_result: Optional[asyncio.Future[None]]
in_sdu: Optional[bytes]
out_sdu: Optional[bytes]
state: State
connection: Connection
sink: Optional[Callable[[bytes], Any]]
def __init__(
self,
@@ -1525,7 +1527,7 @@ class ChannelManager:
if cid in self.fixed_channels:
del self.fixed_channels[cid]
@deprecated("Please use create_classic_channel_server")
@deprecated("Please use create_classic_server")
def register_server(
self,
psm: int,
@@ -1540,7 +1542,7 @@ class ChannelManager:
spec: ClassicChannelSpec,
handler: Optional[Callable[[ClassicChannel], Any]] = None,
) -> ClassicChannelServer:
if spec.psm is None:
if not spec.psm:
# Find a free PSM
for candidate in range(
L2CAP_PSM_DYNAMIC_RANGE_START, L2CAP_PSM_DYNAMIC_RANGE_END + 1, 2
@@ -1592,7 +1594,7 @@ class ChannelManager:
spec: LeCreditBasedChannelSpec,
handler: Optional[Callable[[LeCreditBasedChannel], Any]] = None,
) -> LeCreditBasedChannelServer:
if spec.psm is None:
if not spec.psm:
# Find a free PSM
for candidate in range(
L2CAP_LE_PSM_DYNAMIC_RANGE_START, L2CAP_LE_PSM_DYNAMIC_RANGE_END + 1

View File

@@ -19,6 +19,8 @@
import struct
import logging
from typing import List
from bumble import l2cap
from ..core import AdvertisingData
from ..device import Device, Connection
from ..gatt import (
@@ -149,7 +151,10 @@ class AshaService(TemplateService):
channel.sink = on_data
# let the server find a free PSM
self.psm = self.device.register_l2cap_channel_server(self.psm, on_coc, 8)
self.psm = device.create_l2cap_server(
spec=l2cap.LeCreditBasedChannelSpec(psm=self.psm, max_credits=8),
handler=on_coc,
).psm
self.le_psm_out_characteristic = Characteristic(
GATT_ASHA_LE_PSM_OUT_CHARACTERISTIC,
Characteristic.Properties.READ,

View File

@@ -898,8 +898,8 @@ class Client:
async def start(self) -> Multiplexer:
# Create a new L2CAP connection
try:
self.l2cap_channel = await self.device.l2cap_channel_manager.connect(
self.connection, RFCOMM_PSM
self.l2cap_channel = await self.connection.create_l2cap_channel(
spec=l2cap.ClassicChannelSpec(RFCOMM_PSM)
)
except ProtocolError as error:
logger.warning(f'L2CAP connection failed: {error}')
@@ -936,7 +936,9 @@ class Server(EventEmitter):
self.acceptors = {}
# Register ourselves with the L2CAP channel manager
device.register_l2cap_server(RFCOMM_PSM, self.on_connection)
device.create_l2cap_server(
spec=l2cap.ClassicChannelSpec(psm=RFCOMM_PSM), handler=self.on_connection
)
def listen(self, acceptor: Callable[[DLC], None], channel: int = 0) -> int:
if channel:

View File

@@ -766,8 +766,9 @@ class Client:
self.channel = None
async def connect(self, connection: Connection) -> None:
result = await self.device.l2cap_channel_manager.connect(connection, SDP_PSM)
self.channel = result
self.channel = await connection.create_l2cap_channel(
spec=l2cap.ClassicChannelSpec(SDP_PSM)
)
async def disconnect(self) -> None:
if self.channel:
@@ -933,7 +934,9 @@ class Server:
self.current_response = None
def register(self, l2cap_channel_manager: l2cap.ChannelManager) -> None:
l2cap_channel_manager.register_server(SDP_PSM, self.on_connection)
l2cap_channel_manager.create_classic_server(
spec=l2cap.ClassicChannelSpec(psm=SDP_PSM), handler=self.on_connection
)
def send_response(self, response):
logger.debug(f'{color(">>> Sending SDP Response", "blue")}: {response}')

View File

@@ -131,7 +131,7 @@ async def main():
await device.power_on()
# Create a listener to wait for AVDTP connections
listener = Listener(Listener.create_registrar(device))
listener = Listener.for_device(device)
listener.on('connection', on_avdtp_connection)
if len(sys.argv) >= 5:

View File

@@ -179,7 +179,7 @@ async def main():
await stream_packets(read, protocol)
else:
# Create a listener to wait for AVDTP connections
listener = Listener(Listener.create_registrar(device), version=(1, 2))
listener = Listener.for_device(device=device, version=(1, 2))
listener.on(
'connection', lambda protocol: on_avdtp_connection(read, protocol)
)

View File

@@ -21,6 +21,7 @@ import sys
import os
import logging
from bumble import l2cap
from bumble.core import AdvertisingData
from bumble.device import Device
from bumble.transport import open_transport_or_link
@@ -95,8 +96,10 @@ async def main():
channel.sink = on_data
psm = device.register_l2cap_channel_server(0, on_coc, 8)
print(f'### LE_PSM_OUT = {psm}')
server = device.create_l2cap_server(
spec=l2cap.LeCreditBasedChannelSpec(max_credits=8), handler=on_coc
)
print(f'### LE_PSM_OUT = {server.psm}')
# Add the ASHA service to the GATT server
read_only_properties_characteristic = Characteristic(
@@ -147,7 +150,7 @@ async def main():
ASHA_LE_PSM_OUT_CHARACTERISTIC,
Characteristic.Properties.READ,
Characteristic.READABLE,
struct.pack('<H', psm),
struct.pack('<H', server.psm),
)
device.add_service(
Service(

View File

@@ -185,7 +185,7 @@ async def test_source_sink_1():
sink.on('rtp_packet', on_rtp_packet)
# Create a listener to wait for AVDTP connections
listener = Listener(Listener.create_registrar(two_devices.devices[1]))
listener = Listener.for_device(two_devices.devices[1])
listener.on('connection', on_avdtp_connection)
async def make_connection():

View File

@@ -22,7 +22,11 @@ import random
import pytest
from bumble.core import ProtocolError
from bumble.l2cap import L2CAP_Connection_Request
from bumble.l2cap import (
L2CAP_Connection_Request,
ClassicChannelSpec,
LeCreditBasedChannelSpec,
)
from .test_utils import TwoDevices
@@ -80,7 +84,9 @@ async def test_basic_connection():
# Check that if there's no one listening, we can't connect
with pytest.raises(ProtocolError):
l2cap_channel = await devices.connections[0].open_l2cap_channel(psm)
l2cap_channel = await devices.connections[0].create_l2cap_channel(
spec=LeCreditBasedChannelSpec(psm)
)
# Now add a listener
incoming_channel = None
@@ -95,8 +101,12 @@ async def test_basic_connection():
channel.sink = on_data
devices.devices[1].register_l2cap_channel_server(psm, on_coc)
l2cap_channel = await devices.connections[0].open_l2cap_channel(psm)
devices.devices[1].create_l2cap_server(
spec=LeCreditBasedChannelSpec(psm=1234), handler=on_coc
)
l2cap_channel = await devices.connections[0].create_l2cap_channel(
spec=LeCreditBasedChannelSpec(psm)
)
messages = (bytes([1, 2, 3]), bytes([4, 5, 6]), bytes(10000))
for message in messages:
@@ -138,10 +148,13 @@ async def transfer_payload(max_credits, mtu, mps):
channel.sink = on_data
psm = devices.devices[1].register_l2cap_channel_server(
psm=0, server=on_coc, max_credits=max_credits, mtu=mtu, mps=mps
server = devices.devices[1].create_l2cap_server(
spec=LeCreditBasedChannelSpec(max_credits=max_credits, mtu=mtu, mps=mps),
handler=on_coc,
)
l2cap_channel = await devices.connections[0].create_l2cap_channel(
spec=LeCreditBasedChannelSpec(server.psm)
)
l2cap_channel = await devices.connections[0].open_l2cap_channel(psm)
messages = [bytes([1, 2, 3, 4, 5, 6, 7]) * x for x in (3, 10, 100, 789)]
for message in messages:
@@ -189,8 +202,12 @@ async def test_bidirectional_transfer():
def on_client_data(data):
client_received.append(data)
psm = devices.devices[1].register_l2cap_channel_server(psm=0, server=on_server_coc)
client_channel = await devices.connections[0].open_l2cap_channel(psm)
server = devices.devices[1].create_l2cap_server(
spec=LeCreditBasedChannelSpec(), handler=on_server_coc
)
client_channel = await devices.connections[0].create_l2cap_channel(
spec=LeCreditBasedChannelSpec(server.psm)
)
client_channel.sink = on_client_data
messages = [bytes([1, 2, 3, 4, 5, 6, 7]) * x for x in (3, 10, 100)]

View File

@@ -296,7 +296,7 @@ class Speaker:
self.device.on('key_store_update', self.on_key_store_update)
# Create a listener to wait for AVDTP connections
self.listener = Listener(Listener.create_registrar(self.device))
self.listener = Listener.for_device(self.device)
self.listener.on('connection', self.on_avdtp_connection)
print(f'Speaker ready to play, codec={self.codec}')