mirror of
https://github.com/google/bumble.git
synced 2026-04-16 00:25:31 +00:00
Cleanup legacy L2CAP API
This commit is contained in:
@@ -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'))
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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")}')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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}')
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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}')
|
||||
|
||||
Reference in New Issue
Block a user