Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test update #23

Merged
merged 6 commits into from
May 2, 2024
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: 0 additions & 2 deletions .github/rodbot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,3 @@ on:
steps:
- run: |
gh workflow run tests.yaml -R embassy-rs/trouble -F prNr=${{ github.event.issue.number }}
- run: |
gh pr comment ${{ github.event.issue.number }} -b "Aye, aye, captain! … Triggered Test worklow!"
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ See `examples` for example applications. Currently there are two examples:
* `nrf-sdc` for the nRF52 based using the [`nrf-sdc`](https://github.com/alexmoon/nrf-sdc) crate.
* `serial-hci` which runs on a PC using a HCI controller attached via a serial port (Such as [this Zephyr sample](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/samples/bluetooth/hci_uart/README.html)).


## License

Trouble is licensed under either of
Expand Down
5 changes: 3 additions & 2 deletions examples/serial-hci/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use tokio_serial::{DataBits, Parity, SerialStream, StopBits};
use trouble_host::adapter::{Adapter, HostResources};
use trouble_host::advertise::{AdStructure, Advertisement, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE};
use trouble_host::attribute::{AttributeTable, CharacteristicProp, Service, Uuid};
use trouble_host::PacketQos;
use trouble_host::{Address, PacketQos};

#[tokio::main]
async fn main() {
Expand Down Expand Up @@ -58,8 +58,9 @@ async fn main() {
static HOST_RESOURCES: StaticCell<HostResources<NoopRawMutex, 4, 32, 27>> = StaticCell::new();
let host_resources = HOST_RESOURCES.init(HostResources::new(PacketQos::None));

let adapter: Adapter<'_, NoopRawMutex, _, 2, 4, 27, 1, 1> = Adapter::new(controller, host_resources);
let mut adapter: Adapter<'_, NoopRawMutex, _, 2, 4, 27, 1, 1> = Adapter::new(controller, host_resources);

adapter.set_random_address(Address::random([0xff, 0x9f, 0x1a, 0x05, 0xe4, 0xff]));
let mut table: AttributeTable<'_, NoopRawMutex, 10> = AttributeTable::new();

// Generic Access Service (mandatory)
Expand Down
30 changes: 20 additions & 10 deletions host/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use bt_hci::{ControllerToHostPacket, FromHciBytes, WriteHci};
use embassy_futures::select::{select, Either};
use embassy_sync::blocking_mutex::raw::{NoopRawMutex, RawMutex};
use embassy_sync::channel::Channel;
use embassy_sync::once_lock::OnceLock;
use embassy_sync::semaphore::{GreedySemaphore, Semaphore as _};
use futures::pin_mut;

Expand Down Expand Up @@ -73,6 +74,7 @@ pub struct Adapter<
M: RawMutex,
{
address: Option<Address>,
initialized: OnceLock<()>,
pub(crate) controller: T,
pub(crate) connections: ConnectionManager<M, CONNS>,
pub(crate) reassembly: PacketReassembly<'d, CONNS>,
Expand Down Expand Up @@ -108,6 +110,7 @@ where
) -> Self {
Self {
address: None,
initialized: OnceLock::new(),
controller,
connections: ConnectionManager::new(),
reassembly: PacketReassembly::new(),
Expand Down Expand Up @@ -145,6 +148,7 @@ where
C: SyncCmd,
T: ControllerCmdSync<C>,
{
let _ = self.initialized.get().await;
let ret = cmd.exec(&self.controller).await?;
Ok(ret)
}
Expand All @@ -155,6 +159,10 @@ where
C: SyncCmd,
T: ControllerCmdSync<C>,
{
if self.initialized.try_get().is_none() {
return Err(Error::Busy.into());
}

let fut = cmd.exec(&self.controller);
match embassy_futures::poll_once(fut) {
Poll::Ready(result) => match result {
Expand All @@ -172,6 +180,7 @@ where
C: AsyncCmd,
T: ControllerCmdAsync<C>,
{
let _ = self.initialized.get().await;
cmd.exec(&self.controller).await?;
Ok(())
}
Expand Down Expand Up @@ -200,9 +209,9 @@ where
config.scan_config.interval.into(),
config.scan_config.window.into(),
true,
AddrKind::RANDOM,
AddrKind::PUBLIC,
BdAddr::default(),
self.address.map(|a| a.kind).unwrap_or(AddrKind::RANDOM),
self.address.map(|a| a.kind).unwrap_or(AddrKind::PUBLIC),
config.connect_params.min_connection_interval.into(),
config.connect_params.max_connection_interval.into(),
config.connect_params.max_latency,
Expand Down Expand Up @@ -250,8 +259,8 @@ where
let phy_params = Self::create_phy_params(initiating, config.scan_config.phys);
self.async_command(LeExtCreateConn::new(
true,
self.address.map(|a| a.kind).unwrap_or(AddrKind::RANDOM),
AddrKind::RANDOM,
self.address.map(|a| a.kind).unwrap_or(AddrKind::PUBLIC),
AddrKind::PUBLIC,
BdAddr::default(),
phy_params,
))
Expand Down Expand Up @@ -295,7 +304,7 @@ where
},
config.interval.into(),
config.interval.into(),
bt_hci::param::AddrKind::RANDOM,
bt_hci::param::AddrKind::PUBLIC,
if config.filter_accept_list.is_empty() {
bt_hci::param::ScanningFilterPolicy::BasicUnfiltered
} else {
Expand Down Expand Up @@ -323,7 +332,7 @@ where
};
let phy_params = Self::create_phy_params(scanning, config.phys);
self.command(LeSetExtScanParams::new(
self.address.map(|s| s.kind).unwrap_or(AddrKind::RANDOM),
self.address.map(|s| s.kind).unwrap_or(AddrKind::PUBLIC),
if config.filter_accept_list.is_empty() {
bt_hci::param::ScanningFilterPolicy::BasicUnfiltered
} else {
Expand Down Expand Up @@ -436,15 +445,15 @@ where
(false, false) => AdvKind::AdvNonconnInd,
};
let peer = params.peer.unwrap_or(Address {
kind: AddrKind::RANDOM,
kind: AddrKind::PUBLIC,
addr: BdAddr::default(),
});

self.command(LeSetAdvParams::new(
config.interval_min.into(),
config.interval_max.into(),
kind,
self.address.map(|a| a.kind).unwrap_or(AddrKind::RANDOM),
self.address.map(|a| a.kind).unwrap_or(AddrKind::PUBLIC),
peer.kind,
peer.addr,
config.channel_map.unwrap_or(AdvChannelMap::ALL),
Expand Down Expand Up @@ -505,7 +514,7 @@ where
params.set.max_ext_adv_events = max_events;

let peer = params.peer.unwrap_or(Address {
kind: AddrKind::RANDOM,
kind: AddrKind::PUBLIC,
addr: BdAddr::default(),
});
self.command(LeSetExtAdvParams::new(
Expand All @@ -514,7 +523,7 @@ where
config.interval_min.into(),
config.interval_max.into(),
config.channel_map.unwrap_or(AdvChannelMap::ALL),
self.address.map(|a| a.kind).unwrap_or(AddrKind::RANDOM),
self.address.map(|a| a.kind).unwrap_or(AddrKind::PUBLIC),
peer.kind,
peer.addr,
config.filter_policy,
Expand Down Expand Up @@ -720,6 +729,7 @@ where
self.permits.set(ret.total_num_le_acl_data_packets as usize);
// TODO: Configure ACL max buffer size as well?

let _ = self.initialized.init(());
// Never return
let _ = pending::<Result<(), AdapterError<T>>>().await;
Ok(())
Expand Down
109 changes: 57 additions & 52 deletions host/tests/l2cap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use trouble_host::advertise::{AdStructure, Advertisement, BR_EDR_NOT_SUPPORTED,
use trouble_host::connection::ConnectConfig;
use trouble_host::l2cap::{L2capChannel, L2capChannelConfig};
use trouble_host::scan::ScanConfig;
use trouble_host::PacketQos;
use trouble_host::{Address, PacketQos};

const CONNECTIONS_MAX: usize = 1;
const L2CAP_CHANNELS_MAX: usize = 3;
Expand Down Expand Up @@ -59,34 +59,41 @@ async fn l2cap_connection_oriented_channels() {
let peripheral = std::env::var("TEST_ADAPTER_ONE").unwrap();
let central = std::env::var("TEST_ADAPTER_TWO").unwrap();

let peripheral_address: Address = Address::random([0xff, 0x9f, 0x1a, 0x05, 0xe4, 0xff]);

let local = tokio::task::LocalSet::new();

const PAYLOAD_LEN: usize = 4;

// Spawn peripheral
let peripheral = local.spawn_local(async move {
let controller_peripheral = create_controller(&peripheral).await;

let mut host_resources: HostResources<NoopRawMutex, L2CAP_CHANNELS_MAX, 32, 27> =
HostResources::new(PacketQos::Guaranteed(4));

let adapter: Adapter<'_, NoopRawMutex, _, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, 27> =
let mut adapter: Adapter<'_, NoopRawMutex, _, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, 27> =
Adapter::new(controller_peripheral, &mut host_resources);

adapter.set_random_address(peripheral_address);

select! {
r = adapter.run() => {
r
}
r = async {
let mut adv_data = [0; 31];
AdStructure::encode_slice(
&[AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),],
&[AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED)],
&mut adv_data[..],
).unwrap();

let mut scan_data = [0; 31];
AdStructure::encode_slice(
&[AdStructure::CompleteLocalName(b"trouble-l2cap-int"),],
&[AdStructure::CompleteLocalName(b"trouble-l2cap-int")],
&mut scan_data[..],
).unwrap();

loop {
println!("[peripheral] advertising");
let conn = adapter.advertise(&Default::default(), Advertisement::ConnectableScannableUndirected {
Expand All @@ -102,7 +109,6 @@ async fn l2cap_connection_oriented_channels() {
println!("[peripheral] channel created");

// Size of payload we're expecting
const PAYLOAD_LEN: usize = 27;
let mut rx = [0; PAYLOAD_LEN];
for i in 0..10 {
let len = ch1.receive(&adapter, &mut rx).await?;
Expand Down Expand Up @@ -140,51 +146,37 @@ async fn l2cap_connection_oriented_channels() {
r
}
r = async {
println!("[central] scanning");
let config = ConnectConfig {
connect_params: Default::default(),
scan_config: ScanConfig {
active: true,
filter_accept_list: &[(peripheral_address.kind, &peripheral_address.addr)],
..Default::default()
},
};

println!("[central] connecting");
loop {
let reports = adapter.scan(&Default::default()).await?;
let mut found = None;
for report in reports.iter() {
let report = report.unwrap();
for adv in AdStructure::decode(report.data) {
if let Ok(AdStructure::CompleteLocalName(b"trouble-l2cap-int")) = adv {
found.replace((report.addr_kind, report.addr));
break;
}
}
let conn = adapter.connect(&config).await.unwrap();
println!("[central] connected");
let mut ch1 = L2capChannel::create(&adapter, &conn, 0x2349, &L2capChannelConfig {
mtu: PAYLOAD_LEN as u16,
..Default::default()
}).await?;
println!("[central] channel created");
for i in 0..10 {
let tx = [i; PAYLOAD_LEN];
ch1.send(&adapter, &tx).await?;
}

if let Some((kind, addr)) = found {
let config = ConnectConfig {
connect_params: Default::default(),
scan_config: ScanConfig {
filter_accept_list: &[(kind, &addr)],
..Default::default()
},
};
println!("[central] connecting");
let conn = adapter.connect(&config).await.unwrap();
println!("[central] connected");
const PAYLOAD_LEN: usize = 27;
let mut ch1 = L2capChannel::create(&adapter, &conn, 0x2349, &L2capChannelConfig {
mtu: PAYLOAD_LEN as u16,
..Default::default()
}).await?;
println!("[central] channel created");
for i in 0..10 {
let tx = [i; PAYLOAD_LEN];
ch1.send(&adapter, &tx).await?;
}
println!("[central] data sent");
let mut rx = [0; PAYLOAD_LEN];
for i in 0..10 {
let len = ch1.receive(&adapter, &mut rx).await?;
assert_eq!(len, rx.len());
assert_eq!(rx, [i; PAYLOAD_LEN]);
}
println!("[central] data received");
break;
println!("[central] data sent");
let mut rx = [0; PAYLOAD_LEN];
for i in 0..10 {
let len = ch1.receive(&adapter, &mut rx).await?;
assert_eq!(len, rx.len());
assert_eq!(rx, [i; PAYLOAD_LEN]);
}
println!("[central] data received");
break;
}
Ok(())
} => {
Expand All @@ -194,11 +186,24 @@ async fn l2cap_connection_oriented_channels() {
});

match tokio::time::timeout(Duration::from_secs(30), local).await {
Ok(_) => {
central.await.unwrap().unwrap();
peripheral.await.unwrap().unwrap();
println!("Test completed successfully");
}
Ok(_) => match tokio::join!(central, peripheral) {
(Err(e1), Err(e2)) => {
println!("Central error: {:?}", e1);
println!("Peripheral error: {:?}", e2);
assert!(false);
}
(Err(e), _) => {
println!("Central error: {:?}", e);
assert!(false)
}
(_, Err(e)) => {
println!("Peripheral error: {:?}", e);
assert!(false)
}
_ => {
println!("Test completed successfully");
}
},
Err(e) => {
println!("Test timed out: {:?}", e);
assert!(false);
Expand Down