| /* |
| * Copyright (c) 2022 Nordic Semiconductor |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include "common.h" |
| |
| #include <zephyr/bluetooth/bluetooth.h> |
| #include <zephyr/bluetooth/l2cap.h> |
| |
| extern enum bst_result_t bst_result; |
| |
| static struct bt_conn *default_conn; |
| |
| #define PSM 0x80 |
| |
| CREATE_FLAG(is_connected); |
| CREATE_FLAG(chan_connected); |
| |
| static void chan_connected_cb(struct bt_l2cap_chan *l2cap_chan) |
| { |
| (void)l2cap_chan; |
| |
| SET_FLAG(chan_connected); |
| } |
| |
| static void chan_disconnected_cb(struct bt_l2cap_chan *l2cap_chan) |
| { |
| (void)l2cap_chan; |
| |
| UNSET_FLAG(chan_connected); |
| } |
| |
| static int chan_recv_cb(struct bt_l2cap_chan *chan, struct net_buf *buf) |
| { |
| (void)chan; |
| (void)buf; |
| |
| return 0; |
| } |
| |
| static const struct bt_l2cap_chan_ops l2cap_ops = { |
| .connected = chan_connected_cb, |
| .disconnected = chan_disconnected_cb, |
| .recv = chan_recv_cb, |
| }; |
| |
| static struct bt_l2cap_le_chan channel; |
| |
| static int accept(struct bt_conn *conn, struct bt_l2cap_chan **l2cap_chan) |
| { |
| channel.chan.ops = &l2cap_ops; |
| *l2cap_chan = &channel.chan; |
| |
| return 0; |
| } |
| |
| static struct bt_l2cap_server server = { |
| .accept = accept, |
| .sec_level = BT_SECURITY_L1, |
| .psm = PSM, |
| }; |
| |
| static void connect_l2cap_channel(void) |
| { |
| struct bt_l2cap_chan *chans[] = {&channel.chan, NULL}; |
| int err; |
| |
| channel.chan.ops = &l2cap_ops; |
| |
| err = bt_l2cap_ecred_chan_connect(default_conn, chans, server.psm); |
| if (err) { |
| FAIL("Failed to send ecred connection request (err %d)\n", err); |
| } |
| } |
| |
| static void register_l2cap_server(void) |
| { |
| int err; |
| |
| err = bt_l2cap_server_register(&server); |
| if (err < 0) { |
| FAIL("Failed to get free server (err %d)\n"); |
| return; |
| } |
| } |
| |
| static void connected(struct bt_conn *conn, uint8_t err) |
| { |
| if (err) { |
| FAIL("Failed to connect (err %d)\n", err); |
| bt_conn_unref(default_conn); |
| default_conn = NULL; |
| return; |
| } |
| |
| default_conn = bt_conn_ref(conn); |
| |
| SET_FLAG(is_connected); |
| } |
| |
| static void disconnected(struct bt_conn *conn, uint8_t reason) |
| { |
| if (default_conn != conn) { |
| FAIL("Connection mismatch %p %p)\n", default_conn, conn); |
| return; |
| } |
| |
| bt_conn_unref(default_conn); |
| default_conn = NULL; |
| UNSET_FLAG(is_connected); |
| } |
| |
| BT_CONN_CB_DEFINE(conn_callbacks) = { |
| .connected = connected, |
| .disconnected = disconnected, |
| }; |
| |
| static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, |
| struct net_buf_simple *ad) |
| { |
| struct bt_le_conn_param *param; |
| int err; |
| |
| err = bt_le_scan_stop(); |
| if (err) { |
| FAIL("Failed to stop scanning (err %d)\n", err); |
| return; |
| } |
| |
| param = BT_LE_CONN_PARAM_DEFAULT; |
| err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, param, &default_conn); |
| if (err) { |
| FAIL("Failed to create connection (err %d)\n", err); |
| return; |
| } |
| } |
| |
| static void test_peripheral_main(void) |
| { |
| int err; |
| const struct bt_data ad[] = { |
| BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), |
| }; |
| |
| err = bt_enable(NULL); |
| if (err != 0) { |
| FAIL("Bluetooth init failed (err %d)\n", err); |
| return; |
| } |
| |
| err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0); |
| if (err != 0) { |
| FAIL("Advertising failed to start (err %d)\n", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG_SET(is_connected); |
| |
| register_l2cap_server(); |
| |
| WAIT_FOR_FLAG_UNSET(is_connected); |
| |
| PASS("Test passed\n"); |
| } |
| |
| #define FILL 0xAA |
| #define DATA_SIZE BT_L2CAP_BUF_SIZE(BT_L2CAP_SDU_RX_MTU) + 1 |
| #define USER_DATA_SIZE 10 |
| |
| /* Pool to allocate a buffer that is too large to send */ |
| NET_BUF_POOL_DEFINE(buf_pool, 1, DATA_SIZE, USER_DATA_SIZE, NULL); |
| |
| static void test_central_main(void) |
| { |
| struct net_buf *buf; |
| int err; |
| |
| err = bt_enable(NULL); |
| if (err != 0) { |
| FAIL("Bluetooth discover failed (err %d)\n", err); |
| } |
| |
| err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); |
| if (err != 0) { |
| FAIL("Scanning failed to start (err %d)\n", err); |
| } |
| |
| WAIT_FOR_FLAG_SET(is_connected); |
| |
| connect_l2cap_channel(); |
| WAIT_FOR_FLAG_SET(chan_connected); |
| |
| buf = net_buf_alloc(&buf_pool, K_NO_WAIT); |
| if (!buf) { |
| FAIL("Buffer allcation failed\n"); |
| } |
| |
| /* Don't care about the content of the packet itself, just the length */ |
| (void)net_buf_add(buf, DATA_SIZE); |
| /* Fill the user data with a pattern to more easily see any changes */ |
| (void)memset(buf->user_data, FILL, buf->user_data_size); |
| |
| /* Try to send a buffer larger than the MTU */ |
| err = bt_l2cap_chan_send(&channel.chan, buf); |
| if (err != -EMSGSIZE) { |
| FAIL("Expected error code -EMSGSIZE, got %d\n", err); |
| } |
| |
| printk("Buffer user data (Expecting all bytes to be " STRINGIFY(FILL) "): "); |
| for (int i = 0; i < USER_DATA_SIZE; i++) { |
| printk("%02X", buf->user_data[i]); |
| } |
| |
| printk("\n"); |
| |
| /* Validate that the user data is unchanged */ |
| for (int i = 0; i < USER_DATA_SIZE; i++) { |
| if (buf->user_data[i] != FILL) { |
| FAIL("Buffer user data should not change.\n"); |
| } |
| } |
| |
| err = bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); |
| if (err) { |
| FAIL("Failed to disconnect (err %d)\n", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG_UNSET(is_connected); |
| |
| PASS("Test passed\n"); |
| } |
| |
| static const struct bst_test_instance test_def[] = { |
| { |
| .test_id = "peripheral", |
| .test_descr = "Peripheral", |
| .test_post_init_f = test_init, |
| .test_tick_f = test_tick, |
| .test_main_f = test_peripheral_main, |
| }, |
| { |
| .test_id = "central", |
| .test_descr = "Central", |
| .test_post_init_f = test_init, |
| .test_tick_f = test_tick, |
| .test_main_f = test_central_main, |
| }, |
| BSTEST_END_MARKER, |
| }; |
| |
| struct bst_test_list *test_main_l2cap_ecred_install(struct bst_test_list *tests) |
| { |
| return bst_add_tests(tests, test_def); |
| } |