diff --git a/examples/ll-broadcaster/main.c b/examples/ll-broadcaster/main.c index 5a0eadf..91b5300 100644 --- a/examples/ll-broadcaster/main.c +++ b/examples/ll-broadcaster/main.c @@ -28,6 +28,7 @@ #include #include +#include #include "ll.h" diff --git a/examples/ll-connection-master/Makefile b/examples/ll-connection-master/Makefile new file mode 100644 index 0000000..ed8817f --- /dev/null +++ b/examples/ll-connection-master/Makefile @@ -0,0 +1,7 @@ +# Makefile for ll-connection-master example + +PROJECT_TARGET = ll-connection-master-example +PROJECT_SOURCE_FILES = main.c +PROJECT_CFLAGS = -g -O0 + +include ../Makefile.common diff --git a/examples/ll-connection-master/main.c b/examples/ll-connection-master/main.c new file mode 100644 index 0000000..3cbfda2 --- /dev/null +++ b/examples/ll-connection-master/main.c @@ -0,0 +1,174 @@ +/** + * The MIT License (MIT) + * + * 2014 - Tom Magnier + * + * Adapted from ll-initiator example + * + * This example demonstrates the connection state, in Central role. + * A GATT characteristic (handle 0x000E) is written regularly and notifications + * from the battery service (characteristic : handle 0x0015) are received. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "delay.h" + +#include "ll.h" + +#define SCAN_WINDOW 200000 +#define SCAN_INTERVAL 500000 + +/* L2CAP specification, Section 3.1, Core 4.1 p.1683 + * L2CAP specification, Section 1.1, Core 4.1 p.1677 + * ATT specification, Section 3.4.5.3, Core 4.1 p. 2148 */ +static uint8_t out_buffer[] = { + 0x04, 0x00, //L2CAP payload length + 0x04, 0x00, //L2CAP Channel ID : ATT + 0x52, //ATT opcode : write cmd + 0x0E, 0x00, //ATT handle (0x000E) + 0x00}; //ATT value to write + +static uint8_t in_buffer[LL_DATA_MTU_PAYLOAD]; + +/* PDU to send to enable notifications on battery service */ +static uint8_t cccd_out_buffer[] = { + 0x05, 0x00, //L2CAP payload length + 0x04, 0x00, //L2CAP Channel ID : ATT + 0x52, //ATT opcode : write cmd + 0x16, 0x00, //ATT handle (0x0016) + 0x01, 0x00}; //ATT value to write + +/* Expected packet on handle value notification ; the 8th byte is the value */ +static const uint8_t notification_buffer_value[] = { + 0x04, 0x00, //L2CAP payload length + 0x04, 0x00, //L2CAP CID : ATT + 0x1B, //ATT opcode : handle value notification + 0x15, 0x00}; //ATT handle (0x0015) + +static const bdaddr_t addr = { { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + BDADDR_TYPE_RANDOM }; + +static bdaddr_t peer_addr; +uint8_t led_value = 0x00; + + +static __inline const char *format_address(const uint8_t *data) +{ + static char address[18]; + uint8_t i; + + for (i = 0; i < 5; i++) + sprintf(address + 3*i, "%02x:", data[5-i]); + sprintf(address + 3*i, "%02x", data[5-i]); + + return address; +} + +static __inline const char *format_data(const uint8_t *data, uint8_t len) +{ + static char output[3 * LL_ADV_MTU_DATA + 1]; + uint8_t i; + + for (i = 0; i < len; i++) + sprintf(output + 3*i, "%02x ", data[i]); + + output[3*i] = '\0'; + + return output; +} + +void conn_evt_cb(ble_evt_t type, const void *data) +{ + const ble_evt_ll_connection_complete_t* conn_compl = data; + const ble_evt_ll_disconnect_complete_t* disconn_compl = data; + const ble_evt_ll_packets_received_t* packets_rx = data; + + switch (type) { + case BLE_EVT_LL_CONNECTION_COMPLETE: + DBG("Connection complete, index %u, address %s", + conn_compl->index, format_address(conn_compl->peer_addr.addr)); + + /*Enable notifications on battery service (write 0x0001 on CCCD + * characteristic, handle 0x0016) */ + ll_conn_send(cccd_out_buffer, 9); + break; + + case BLE_EVT_LL_DISCONNECT_COMPLETE: + DBG("Disconnect complete, index %u, reason %02x", + disconn_compl->index, disconn_compl->reason); + + evt_loop_run(); + break; + + case BLE_EVT_LL_PACKETS_SENT: + out_buffer[7] = led_value; + ll_conn_send(out_buffer, 8); + break; + + case BLE_EVT_LL_PACKETS_RECEIVED: + if (!memcmp(in_buffer, notification_buffer_value, 7)) + DBG("Battery value : %u %%", in_buffer[7]); + else + DBG("Received packet : %s", format_data(in_buffer, + packets_rx->length)); + break; + } +} + +void adv_report_cb(struct adv_report *report) +{ + DBG("adv type %02x, addr type %02x", report->type, report->addr.type); + DBG("address %s, data %s", format_address(report->addr.addr), + format_data(report->data, report->len)); + + memcpy(peer_addr.addr, report->addr.addr, BDADDR_LEN); + peer_addr.type = report->addr.type; + + ll_scan_stop(); + ll_conn_create(SCAN_INTERVAL, SCAN_WINDOW, &peer_addr, 1, in_buffer, + conn_evt_cb); +} + +int main(void) +{ + log_init(); + ll_init(&addr); + + DBG("End init, connection + battery notification"); + + ll_scan_start(LL_SCAN_PASSIVE, SCAN_INTERVAL, SCAN_WINDOW, + adv_report_cb); + while (1) + { + led_value++; + delay(10000); + } + + return 0; +} diff --git a/examples/ll-initiator/main.c b/examples/ll-initiator/main.c index f2a69e8..4935d8a 100644 --- a/examples/ll-initiator/main.c +++ b/examples/ll-initiator/main.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "nrf_delay.h" @@ -44,6 +45,8 @@ static const bdaddr_t addr = { { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, static bdaddr_t peer_addr; +static uint8_t in_buffer[LL_DATA_MTU_PAYLOAD]; + static __inline const char *format_address(const uint8_t *data) { static char address[18]; @@ -69,6 +72,22 @@ static __inline const char *format_data(const uint8_t *data, uint8_t len) return output; } + +void conn_evt_cb(ble_evt_t type, const void *data) +{ + const ble_evt_ll_connection_complete_t* conn_compl = data; + + switch (type) { + case BLE_EVT_LL_CONNECTION_COMPLETE: + DBG("Connection complete, index %u, address %s", + conn_compl->index, format_address(conn_compl->peer_addr.addr)); + break; + default: + break; + } +} + + void adv_report_cb(struct adv_report *report) { DBG("adv type %02x, addr type %02x", report->type, report->addr.type); @@ -78,7 +97,8 @@ void adv_report_cb(struct adv_report *report) memcpy(&peer_addr, &report->addr, sizeof(bdaddr_t)); ll_scan_stop(); - ll_conn_create(SCAN_INTERVAL, SCAN_WINDOW, &peer_addr, 1); + ll_conn_create(SCAN_INTERVAL, SCAN_WINDOW, &peer_addr, 1, in_buffer, + conn_evt_cb); } diff --git a/examples/ll-scanner/main.c b/examples/ll-scanner/main.c index 6ab99dd..61a3545 100644 --- a/examples/ll-scanner/main.c +++ b/examples/ll-scanner/main.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "ll.h" #include "delay.h" diff --git a/include/blessed/errcodes.h b/include/blessed/errcodes.h index 4c614eb..5b9eb3f 100644 --- a/include/blessed/errcodes.h +++ b/include/blessed/errcodes.h @@ -30,3 +30,11 @@ #define ENOREADY 0x04 /* Not ready */ #define EBUSY 0x05 /* Device or resource busy */ #define EINTERN 0x06 /* Internal error */ + +/* Reasons for disconnection + * Error codes, Section 1.3, Core 4.1 p. 666 */ +#define BLE_HCI_CONNECTION_TIMEOUT 0x08 +#define BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION 0x13 +#define BLE_HCI_REMOTE_DEV_TERMINATION_DUE_TO_LOW_RESOURCES 0x14 +#define BLE_HCI_REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF 0x15 +#define BLE_HCI_LOCAL_HOST_TERMINATED_CONNECTION 0x16 diff --git a/include/blessed/events.h b/include/blessed/events.h new file mode 100644 index 0000000..f461836 --- /dev/null +++ b/include/blessed/events.h @@ -0,0 +1,61 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013 Paulo B. de Oliveira Filho + * Copyright (c) 2013 Claudio Takahasi + * Copyright (c) 2013 João Paulo Rechi Vita + * Copyright (c) 2014 Tom Magnier + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Maximum size of parameters structure (to allocate a static buffer) */ +#define BLE_EVT_PARAMS_MAX_SIZE 8 + +/* HCI Specification, Section 7.7, Core 4.1 p.1132-1239 */ +typedef enum ble_evt { + BLE_EVT_LL_CONNECTION_COMPLETE, /* Sec 7.7.3 p.1135 */ + BLE_EVT_LL_DISCONNECT_COMPLETE, /* Sec 7.7.5 p.1138 */ + BLE_EVT_LL_PACKETS_SENT, + BLE_EVT_LL_PACKETS_RECEIVED +}ble_evt_t; + +/* Events parameters structure for BLE_EVT_LL_CONNECTION_COMPLETE event */ +typedef struct ble_evt_ll_connection_complete { + uint8_t index; /* Connection index */ + bdaddr_t peer_addr; /* Peer address */ +} ble_evt_ll_connection_complete_t; + +/* Events parameters structure for BLE_EVT_LL_DISCONNECT_COMPLETE event */ +typedef struct ble_evt_ll_disconnect_complete { + uint8_t index; /* Connection index */ + uint8_t reason; /* Reason for disconnection */ +} ble_evt_ll_disconnect_complete_t; + +/* Events parameters structure for BLE_EVT_LL_PACKETS_SENT event */ +typedef struct ble_evt_ll_packets_sent { + uint8_t index; /* Connection index */ +} ble_evt_ll_packets_sent_t; + +/* Events parameters structure for BLE_EVT_LL_PACKETS_RECEIVED event */ +typedef struct ble_evt_ll_packets_received { + uint8_t index; /* Connection index */ + uint8_t length; /* Number of bytes received */ +} ble_evt_ll_packets_received_t; + diff --git a/platform/nrf51822/ll-plat.c b/platform/nrf51822/ll-plat.c index b921766..514a1f1 100644 --- a/platform/nrf51822/ll-plat.c +++ b/platform/nrf51822/ll-plat.c @@ -29,6 +29,7 @@ #include #include +#include #include "ll.h" #include "nrf51822.h" diff --git a/stack/bci.c b/stack/bci.c index 334fffd..dd6148d 100644 --- a/stack/bci.c +++ b/stack/bci.c @@ -32,6 +32,7 @@ #include #include +#include #include #include "ll.h" diff --git a/stack/ll.c b/stack/ll.c index 7ce1a9b..480d516 100644 --- a/stack/ll.c +++ b/stack/ll.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "radio.h" #include "timer.h" @@ -58,7 +59,8 @@ typedef enum ll_states { LL_STATE_ADVERTISING, LL_STATE_SCANNING, LL_STATE_INITIATING, - LL_STATE_CONNECTION, + LL_STATE_CONNECTION_MASTER, + LL_STATE_CONNECTION_SLAVE, } ll_states_t; /* Link Layer specification Section 2.3, Core 4.1 pages 2504-2505 */ @@ -74,6 +76,29 @@ struct __attribute__ ((packed)) ll_pdu_adv { uint8_t payload[LL_ADV_MTU_PAYLOAD]; }; +/* Link Layer specification Section 2.4.2, Core 4.1 page 2512 */ +typedef enum ll_llid { + LL_PDU_DATA_FRAG_EMPTY = 1, /* Data PDU fragment or Empty PDU */ + LL_PDU_DATA_START_COMPLETE, /* Complete Data PDU / 1st fragment */ + LL_PDU_CONTROL /* LL Control PDU */ +} ll_llid_t; + +/* Link Layer specification Section 2.4, Core 4.1 page 2511 */ +struct __attribute__ ((packed)) ll_pdu_data { + uint8_t llid:2; /* See ll_llid_t */ + uint8_t nesn:1; /* Next expected sequence number */ + uint8_t sn:1; /* Sequence number */ + uint8_t md:1; /* More Data bit */ + uint8_t _rfu_0:3; /* Reserved for future use */ + + uint8_t length:5; /* 0 <= payload length <= 31 */ + uint8_t _rfu_1:3; /* Reserved for future use */ + + uint8_t payload[LL_DATA_MTU_PAYLOAD]; + + uint8_t mic[LL_DATA_MIC_LEN]; /* Message Integrity Check */ +}; + /* Link Layer specification Section 2.3, Core 4.1 pages 2508 */ struct __attribute__ ((packed)) ll_pdu_scan_req { uint8_t scana[BDADDR_LEN]; @@ -97,6 +122,37 @@ struct __attribute__ ((packed)) ll_pdu_connect_payload { uint8_t sca:3; /* Master sleep clock accuracy */ }; +/* Connection flags, used to keep track of various events and procedures in + * a connection */ +#define LL_CONN_FLAGS_ESTABLISHED 1 /* conn. created/established */ +#define LL_CONN_FLAGS_TERM_LOCAL 2 /* termination req by Host */ +#define LL_CONN_FLAGS_TERM_PEER 4 /* term. req by peer device */ + +/** This structure contains all the fields needed to establish and maintain a + * connection, on Master or Slave side. For a Master involved in multiple + * simultaneous connections, there must be one structure per connection. + * + * Note that several parameters : conn interval, slave latency, supervision + * timeout and channel map are defined for all connections in ll_conn_params + * structure. + * + * See Link Layer specification Section 4.5, Core 4.1 pages 2537-2547*/ +struct ll_conn_context { + uint32_t aa; /**< Access Address */ + uint32_t crcinit; /**< CRC init. value (3 bytes) */ + uint8_t hop; /**< hopIncrement for ch. selection */ + uint8_t last_unmap_ch; /**< last unmapped channel used */ + uint16_t conn_evt_cnt; /**< Connection Event counter */ + uint16_t superv_tmr; /**< Connection supervision timer */ + uint8_t sn; /**< transmitSeqNum for ack. */ + uint8_t nesn; /**< nextExpectedSeqNum for ack. */ + uint8_t * txbuf; /**< TX buffer, handled in app. */ + uint8_t txlen; /**< Nb of used bytes in TX buffer */ + uint8_t * rxbuf; /**< RX buffer, handled in app. */ + uint8_t rxlen; /**< Nb of used bytes in RX buffer */ + uint32_t flags; /**< conn. flags, see LL_CONN_FLAGS_* */ +}; + static const bdaddr_t *laddr; static ll_states_t current_state; @@ -126,9 +182,11 @@ static uint32_t t_scan_window; static struct ll_pdu_adv pdu_adv; static struct ll_pdu_adv pdu_scan_rsp; static struct ll_pdu_adv pdu_connect_req; +static struct ll_pdu_data pdu_data_tx; static bool rx = false; static ll_conn_params_t ll_conn_params; +static struct ll_conn_context conn_context; /* Internal pointer to an array of accepted peer addresses */ static bdaddr_t *ll_peer_addresses; static uint16_t ll_num_peer_addresses; /* Size of the accepted peers array */ @@ -156,6 +214,10 @@ static void t_ll_ifs_cb(void) static adv_report_cb_t ll_adv_report_cb = NULL; static struct adv_report ll_adv_report; +/** Callback function to report connection events (CONNECTION state) */ +static conn_evt_cb_t ll_conn_evt_cb = NULL; +static uint8_t ll_conn_evt_params[BLE_EVT_PARAMS_MAX_SIZE]; + static __inline void send_scan_rsp(const struct ll_pdu_adv *pdu) { struct ll_pdu_scan_req *scn; @@ -220,6 +282,110 @@ static uint32_t generate_access_address(void) return aa; } +/**@brief Prepare the next Data Channel PDU to send in a connection. + * + * Use the tx_buffer and tx_len in conn_context. + * + * @param[in] control_pdu: if true, will issue a LL Control PDU with opCode + * LL_UNKNOWN_RSP (to answer to a received LL Control PDU) instead of a + * LL Data PDU + * @param[in] control_pdu_opcode: the opCode of the received LL Control PDU + */ +static void prepare_next_data_pdu(bool control_pdu, uint8_t control_pdu_opcode) +{ + pdu_data_tx.nesn = conn_context.nesn; + pdu_data_tx.sn = conn_context.sn; + /* We assume that the master will send only 1 packet in every CE */ + pdu_data_tx.md = 0UL; + + if (conn_context.flags & LL_CONN_FLAGS_TERM_LOCAL) { + /* If a termination has been requested locally, send only + * LL_TERMINATE_IND PDUs until receiving an ack */ + pdu_data_tx.llid = LL_PDU_CONTROL; + pdu_data_tx.length = 2; + pdu_data_tx.payload[0] = LL_TERMINATE_IND; + pdu_data_tx.payload[1] = + BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION; + + } + else if (control_pdu) { + /* Reply immediately to LL Control PDUs + * Link Layer spec, Section 2.4.2, Core 4.1 p. 2512-2521 + * Link Layer spec, Section 5.1, Core 4.1 p. 2549-2568 */ + pdu_data_tx.llid = LL_PDU_CONTROL; + switch(control_pdu_opcode) { + case LL_VERSION_IND: + /* TODO test endianness */ + pdu_data_tx.length = 6; + pdu_data_tx.payload[0] = LL_VERSION_IND; + pdu_data_tx.payload[1] = LL_VERS_NR; + pdu_data_tx.payload[2] = (uint8_t)(0xFF & LL_COMP_ID); + pdu_data_tx.payload[3] = + (uint8_t)(0xFF & (LL_COMP_ID>>8)); + pdu_data_tx.payload[4] = + (uint8_t)(0xFF & LL_SUB_VERS_NR); + pdu_data_tx.payload[5] = + (uint8_t)(0xFF & (LL_SUB_VERS_NR>>8)); + break; + case LL_TERMINATE_IND: + /* Send Empty Data PDU then stop connection */ + pdu_data_tx.llid = LL_PDU_DATA_FRAG_EMPTY; + pdu_data_tx.length = 0; + + conn_context.flags |= LL_CONN_FLAGS_TERM_PEER; + break; + default: + /* Reply with an LL_UNKNOWN_RSP PDU to all other, + * unsupported LL Control PDUs */ + pdu_data_tx.length = 2; + pdu_data_tx.payload[0] = LL_UNKNOWN_RSP; + pdu_data_tx.payload[1] = control_pdu_opcode; + } + } + else if (conn_context.txlen > 0) { + /* There is new data to send */ + pdu_data_tx.llid = LL_PDU_DATA_START_COMPLETE; + pdu_data_tx.length = conn_context.txlen; + memcpy(pdu_data_tx.payload, conn_context.txbuf, + conn_context.txlen); + + /* Data in the TX buffer is now outdated */ + conn_context.txlen = 0; + + /* Notify upper layers that data has been sent */ + ble_evt_ll_packets_sent_t *packets_tx_params = + (ble_evt_ll_packets_sent_t*)ll_conn_evt_params; + packets_tx_params->index = 0; + ll_conn_evt_cb(BLE_EVT_LL_PACKETS_SENT, ll_conn_evt_params); + } + else { + /* No new data => send Empty Data PDU */ + pdu_data_tx.llid = LL_PDU_DATA_FRAG_EMPTY; + pdu_data_tx.length = 0; + } +} + +/**@brief Handle the end of a connection, which can be caused by various reasons + * + * @param[in] reason: an error code indicating why the connection is being + * closed. + */ +static void end_connection(uint8_t reason) +{ + current_state = LL_STATE_STANDBY; + + timer_stop(t_ll_interval); + timer_stop(t_ll_single_shot); + timer_stop(t_ll_ifs); + + /* Notify upper layers */ + ble_evt_ll_disconnect_complete_t *disconn_params = + (ble_evt_ll_disconnect_complete_t*)ll_conn_evt_params; + disconn_params->index = 0; + disconn_params->reason = reason; + ll_conn_evt_cb(BLE_EVT_LL_DISCONNECT_COMPLETE, ll_conn_evt_params); +} + static __inline uint8_t first_adv_ch_idx(void) { if (adv_ch_map & LL_ADV_CH_37) @@ -242,6 +408,35 @@ static __inline int16_t inc_adv_ch_idx(void) return 0; } +/**@brief Function that implement the Data channel index selection + * Used in connection states to determine the BLE channel to use for the next + * connection event. + * + * See Link Layer specification Section 4.5.8, Core v4.1 p.2544 + * + * @param[in,out] unmapped_channel: a pointer to a variable containing the + * lastUnmappedChannel defined in LL spec. This variable will be updated + * to store the new unmappedChannel. + * @param[in] hop: the hopIncrement defined in LL spec (increment between 2 + * unmapped channels) + * + * @return the data channel index to use for the next connection event, according + * to the global channel map data_ch_map. + */ +static __inline__ uint8_t data_ch_idx_selection(uint8_t *unmapped_channel, + uint8_t hop) +{ + *unmapped_channel = (*unmapped_channel + hop) % 37; + + /* Return unmapped_channel if it is an used channel */ + if ( data_ch_map.mask & (1ULL << (*unmapped_channel)) ) + return (*unmapped_channel); + + else + return data_ch_map.used[(*unmapped_channel) % data_ch_map.cnt]; +} + + static void adv_radio_recv_cb(const uint8_t *pdu, bool crc, bool active) { struct ll_pdu_adv *rcvd_pdu = (struct ll_pdu_adv*) pdu; @@ -466,6 +661,35 @@ static void init_connect_req_pdu() } +/**@brief Initialize the connection context structure at the beginning of a new + * connection, based on the data present in the CONNECT_REQ PDU + * + * See Link Layer specification Section 4.5, Core 4.1 pages 2537-2547 + */ +static void init_conn_context() +{ + struct ll_conn_context *context = &conn_context; + struct ll_pdu_connect_payload *connect_req = + (struct ll_pdu_connect_payload*)(pdu_connect_req.payload); + + context->aa = connect_req->aa; + context->crcinit = connect_req->crc_init; + context->hop = connect_req->hop; + context->last_unmap_ch = 0; + /* The CE counter is incremented at each CE and must be 0 on the first + * CE */ + context->conn_evt_cnt = 0xFFFF; + context->superv_tmr = 0; + context->sn = 0; + context->nesn = 0; + context->flags = 0; + + context->txbuf = NULL; + context->txlen = 0; + context->rxbuf = NULL; + context->rxlen = 0; +} + int16_t ll_init(const bdaddr_t *addr) { int16_t err_code; @@ -686,9 +910,128 @@ int16_t ll_set_data_ch_map(uint64_t ch_map) return 0; } +static void conn_master_radio_recv_cb(const uint8_t *pdu, bool crc, bool active) +{ + struct ll_pdu_data *rcvd_pdu = (struct ll_pdu_data *)(pdu); + ble_evt_ll_packets_received_t *packets_rx_params = + (ble_evt_ll_packets_received_t*)ll_conn_evt_params; + + timer_stop(t_ll_ifs); + + conn_context.superv_tmr = 0; + conn_context.flags |= LL_CONN_FLAGS_ESTABLISHED; + + /* Hypothesis for simplification : the connection event is closed when + * the slave has sent a packet, regardless of CRC match or MD bit. + * The master will only send one packet in each CE. + * See LL spec, section 4.5.6, Core 4.1 p.2542 */ + + /* Ack/flow control according to : + * LL spec, section 4.5.9, Core 4.1 p. 2545 */ + if (!crc) { + /* ignore incoming data + * equivalent to a NACK => resend the old data */ + DBG("Packet with bad CRC received"); + return; + } + + if (rcvd_pdu->sn == (conn_context.nesn & 0x01)) { + /* New incoming Data Channel PDU + * local NESN *may* be incremented to allow flow control */ + conn_context.nesn++; + + /* Get data if the received packet wasn't a LL Control PDU or + * an Empty PDU */ + if (rcvd_pdu->llid != LL_PDU_CONTROL && rcvd_pdu->length > 0) { + conn_context.rxlen = rcvd_pdu->length; + memcpy(conn_context.rxbuf, rcvd_pdu->payload, + rcvd_pdu->length); + + /* Send an event to upper layers to notify that new + * data is available */ + packets_rx_params->index = 0; + packets_rx_params->length = conn_context.rxlen; + ll_conn_evt_cb(BLE_EVT_LL_PACKETS_RECEIVED, + ll_conn_evt_params); + } + } + else { + /* Old incoming Data Channel PDU => ignore */ + } + + if (rcvd_pdu->nesn == (conn_context.sn & 0x01)) { + /* NACK => resend old data (do nothing) */ + DBG("NACK received"); + } + else { + /* ACK => send new data */ + conn_context.sn++; + + /* If a terminating procedure has been requested we can exit + * connection state now */ + if (conn_context.flags & LL_CONN_FLAGS_TERM_PEER) { + end_connection( + BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); + return; + } else if (conn_context.flags & LL_CONN_FLAGS_TERM_LOCAL) { + end_connection( + BLE_HCI_LOCAL_HOST_TERMINATED_CONNECTION); + return; + } + + /* Prepare a new packet according to what was just received */ + if (rcvd_pdu->llid == LL_PDU_CONTROL) + prepare_next_data_pdu(true, rcvd_pdu->payload[0]); + else + prepare_next_data_pdu(false, 0x00); + } +} + +static void conn_master_radio_send_cb(bool active) +{ + timer_start(t_ll_ifs, T_IFS, t_ll_ifs_cb); +} + +static void conn_master_interval_cb(void) +{ + /* LL spec, section 4.5, Core v4.1 p.2537-2547 + * LL spec, Section 2.4, Core 4.1 page 2511 */ + + /* Handle supervision timer which is reset every time a new packet is + * received + * NOTE: this doesn't respect the spec as the timer's unit is + * connInterval */ + if ((!(conn_context.flags & LL_CONN_FLAGS_ESTABLISHED) && + conn_context.superv_tmr >= 6) || + ((conn_context.flags & LL_CONN_FLAGS_ESTABLISHED) && + conn_context.superv_tmr >= + (ll_conn_params.supervision_timeout / + ll_conn_params.conn_interval_min))) { + end_connection(BLE_HCI_CONNECTION_TIMEOUT); + return; + } + + radio_stop(); + radio_prepare(data_ch_idx_selection( &(conn_context.last_unmap_ch), + conn_context.hop), + conn_context.aa, + conn_context.crcinit); + + radio_send((uint8_t *)(&pdu_data_tx), RADIO_FLAGS_RX_NEXT); + + conn_context.conn_evt_cnt++; + conn_context.superv_tmr++; + + /* Next step : conn_master_radio_recv_cb (receive a packet from the + * slave) or t_ll_ifs_cb (RX timeout) */ +} + static void init_radio_recv_cb(const uint8_t *pdu, bool crc, bool active) { struct ll_pdu_adv *rcvd_pdu = (struct ll_pdu_adv*) pdu; + ble_evt_ll_connection_complete_t *conn_complete_params = + (ble_evt_ll_connection_complete_t*)ll_conn_evt_params; + /* Answer to ADV_IND (connectable undirected advertising event) and * ADV_DIRECT_IND (connectable directed advertising event) PDUs from @@ -706,8 +1049,28 @@ static void init_radio_recv_cb(const uint8_t *pdu, bool crc, bool active) memcpy(pdu_connect_req.payload+BDADDR_LEN, rcvd_pdu->payload, BDADDR_LEN); - /* TODO go to CONNECTION_MASTER state - TODO notify application (cb function) */ + current_state = LL_STATE_CONNECTION_MASTER; + + timer_stop(t_ll_interval); + timer_stop(t_ll_single_shot); + timer_start(t_ll_interval, + ll_conn_params.conn_interval_min*1250, + conn_master_interval_cb); + + radio_set_callbacks(conn_master_radio_recv_cb, + conn_master_radio_send_cb); + + /* Prepare the Data PDU that will be sent */ + prepare_next_data_pdu(false, 0x00); + + /* Send an event to the upper layers */ + conn_complete_params->index = 0; + conn_complete_params->peer_addr.type = rcvd_pdu->tx_add; + memcpy(conn_complete_params->peer_addr.addr, rcvd_pdu->payload, + BDADDR_LEN); + + ll_conn_evt_cb(BLE_EVT_LL_CONNECTION_COMPLETE, + ll_conn_evt_params); } else { radio_stop(); @@ -741,9 +1104,14 @@ static void init_interval_cb(void) * @param [in] peer_addresses: a pointer to an array of Bluetooth addresses * to try to connect * @param [in] num_addresses: the size of the peer_addresses array + * @param [out] rx_buf: a pointer to an array to store incoming data from the + * peer device + * @param [out] conn_evt_cb: the function to call on connection events */ + int16_t ll_conn_create(uint32_t interval, uint32_t window, - bdaddr_t* peer_addresses, uint16_t num_addresses) + bdaddr_t* peer_addresses, uint16_t num_addresses, uint8_t* rx_buf, + conn_evt_cb_t conn_evt_cb) { int16_t err_code; @@ -762,9 +1130,12 @@ int16_t ll_conn_create(uint32_t interval, uint32_t window, ll_peer_addresses = peer_addresses; ll_num_peer_addresses = num_addresses; + ll_conn_evt_cb = conn_evt_cb; /* Generate new connection parameters and init CONNECT_REQ PDU */ init_connect_req_pdu(); + init_conn_context(); + conn_context.rxbuf = rx_buf; radio_set_callbacks(init_radio_recv_cb, NULL); @@ -802,3 +1173,39 @@ int16_t ll_conn_cancel(void) return 0; } + +/**@brief Terminate the current connection + * + */ +int16_t ll_conn_terminate(void) +{ + if (current_state != LL_STATE_CONNECTION_MASTER) + return -ENOREADY; + + conn_context.flags |= LL_CONN_FLAGS_TERM_LOCAL; + /* Force the preparation of the next data PDU, discarding current + * operations */ + prepare_next_data_pdu(false, 0); + + return 0; +} + +/**@brief Set new data to send to the peer when in connection state + * + * @param[in] data: Pointer to a buffer containing the data to send + * @param[in] len: The number of bytes to send. Must be <= 27 + * See Link Layer specification, Section 2.4, Core v4.1 p.2511 + */ +int16_t ll_conn_send(uint8_t *data, uint8_t len) +{ + if (len > LL_DATA_MTU_PAYLOAD) { + ERROR("Max payload length : %u bytes in connection state", + LL_DATA_MTU_PAYLOAD); + return -EINVAL; + } + + conn_context.txbuf = data; + conn_context.txlen = len; + + return 0; +} diff --git a/stack/ll.h b/stack/ll.h index bbf583e..c5ffe64 100644 --- a/stack/ll.h +++ b/stack/ll.h @@ -38,6 +38,13 @@ /* Link Layer specification Section 2.3.1, Core 4.1 page 2506 */ #define LL_ADV_MTU_DATA (LL_ADV_MTU_PAYLOAD - BDADDR_LEN) +/* Link Layer specification Section 2.4, Core 4.1 page 2511 */ +#define LL_DATA_MIC_LEN 4 + +/* Link Layer specification Section 2.4, Core 4.1 page 2511 */ +#define LL_DATA_MTU_PAYLOAD (LL_MTU - LL_HEADER_LEN - \ + LL_DATA_MIC_LEN) + /* Link Layer specification Section 4.4.2.2, Core 4.1 page 2528 */ #define LL_ADV_INTERVAL_MIN_CONN 20000 /* 20 ms */ #define LL_ADV_INTERVAL_MIN_NONCONN 100000 /* 100 ms */ @@ -63,6 +70,14 @@ #define LL_SCAN_PASSIVE 0x00 #define LL_SCAN_ACTIVE 0x01 +/* Values used for LL Version exchange. + * https://www.bluetooth.org/en-us/specification/assigned-numbers/link-layer + * https://www.bluetooth.org/en-us/specification/assigned-numbers/company-identifiers + */ +#define LL_VERS_NR 0x07 /* Core v4.1 */ +#define LL_COMP_ID 0xFFFF /* Unassigned Company ID */ +#define LL_SUB_VERS_NR 0x0000 /* Implementation rev. nr */ + /* Link Layer specification Section 2.3, Core 4.1 page 2505 */ typedef enum ll_pdu { LL_PDU_ADV_IND, @@ -74,6 +89,30 @@ typedef enum ll_pdu { LL_PDU_ADV_SCAN_IND } ll_pdu_t; +/* Link Layer specification, Section 2.4.2, Core 4.1 p. 2512-2521 */ +typedef enum ll_ctrl_pdu { + LL_CONNECTION_UPDATE_REQ, + LL_CHANNEL_MAP_REQ, + LL_TERMINATE_IND, + LL_ENC_REQ, + LL_ENC_RSP, + LL_START_ENC_REQ, + LL_START_ENC_RSP, + LL_UNKNOWN_RSP, + LL_FEATURE_REQ, + LL_FEATURE_RSP, + LL_PAUSE_ENC_REQ, + LL_PAUSE_ENC_RSP, + LL_VERSION_IND, + LL_REJECT_IND, + LL_SLAVE_FEATURE_REQ, + LL_CONNECTION_PARAM_REQ, + LL_CONNECTION_PARAM_RSP, + LL_REJECT_IND_EXT, + LL_PING_REQ, + LL_PING_RSP +} ll_ctrl_pdu_t; + /**@brief Connection parameters structure */ /* TODO helper macros to convert to and from us */ typedef struct { @@ -102,6 +141,9 @@ struct adv_report { * See HCI Funcional Specification Section 7.7.65.2, Core 4.1 page 1220 */ typedef void (*adv_report_cb_t)(struct adv_report *report); +/* Callback function for connection events */ +typedef void (*conn_evt_cb_t)(ble_evt_t type, const void *data); + int16_t ll_init(const bdaddr_t *addr); /* Advertising */ @@ -119,8 +161,11 @@ int16_t ll_scan_stop(void); int16_t ll_set_conn_params(ll_conn_params_t* conn_params); int16_t ll_set_data_ch_map(uint64_t ch_map); int16_t ll_conn_create(uint32_t interval, uint32_t window, - bdaddr_t* peer_addresses, uint16_t num_addresses); + bdaddr_t* peer_addresses, uint16_t num_addresses, uint8_t* rx_buf, + conn_evt_cb_t conn_evt_cb); int16_t ll_conn_cancel(void); +int16_t ll_conn_terminate(void); +int16_t ll_conn_send(uint8_t *data, uint8_t len); /* LL "platform" interface */ int16_t ll_plat_init(void);