mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-03-25 16:43:28 +00:00
619 lines
24 KiB
C
619 lines
24 KiB
C
/*
|
|
* Copyright (C) 2014 BlueKitchen GmbH
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holders nor the names of
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
* 4. Any redistribution, use, or modification is done solely for
|
|
* personal benefit and not for any commercial purpose or for
|
|
* monetary gain.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
|
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* Please inquire about commercial licensing options at
|
|
* contact@bluekitchen-gmbh.com
|
|
*
|
|
*/
|
|
|
|
#define __BTSTACK_FILE__ "pb_adv.c"
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "mesh/adv_bearer.h"
|
|
#include "mesh/beacon.h"
|
|
#include "provisioning.h"
|
|
#include "btstack_util.h"
|
|
#include "btstack_debug.h"
|
|
#include "btstack_event.h"
|
|
|
|
#define PB_ADV_LINK_OPEN_RETRANSMIT_MS 1000
|
|
|
|
static void pb_adv_run(void);
|
|
|
|
/* taps: 32 31 29 1; characteristic polynomial: x^32 + x^31 + x^29 + x + 1 */
|
|
#define LFSR(a) ((a >> 1) ^ (uint32_t)((0 - (a & 1u)) & 0xd0000001u))
|
|
|
|
// PB-ADV - Provisioning Bearer using Advertisement Bearer
|
|
|
|
#define MESH_GENERIC_PROVISIONING_LINK_OPEN 0x00
|
|
#define MESH_GENERIC_PROVISIONING_LINK_ACK 0x01
|
|
#define MESH_GENERIC_PROVISIONING_LINK_CLOSE 0x02
|
|
|
|
#define MESH_GENERIC_PROVISIONING_TRANSACTION_TIMEOUT_MS 30000
|
|
|
|
#define MESH_PB_ADV_MAX_PDU_SIZE 100
|
|
#define MESH_PB_ADV_MAX_SEGMENTS 8
|
|
#define MESH_PB_ADV_START_PAYLOAD 20
|
|
#define MESH_PB_ADV_CONT_PAYLOAD 23
|
|
|
|
typedef enum mesh_gpcf_format {
|
|
MESH_GPCF_TRANSACTION_START = 0,
|
|
MESH_GPCF_TRANSACTION_ACK,
|
|
MESH_GPCF_TRANSACTION_CONT,
|
|
MESH_GPCF_PROV_BEARER_CONTROL,
|
|
} mesh_gpcf_format_t;
|
|
|
|
typedef enum {
|
|
LINK_STATE_W4_OPEN,
|
|
LINK_STATE_W2_SEND_ACK,
|
|
LINK_STATE_W4_ACK,
|
|
LINK_STATE_OPEN,
|
|
LINK_STATE_CLOSING,
|
|
} link_state_t;
|
|
static link_state_t link_state;
|
|
|
|
static const uint8_t * pb_adv_own_device_uuid;
|
|
|
|
#ifdef ENABLE_MESH_PROVISIONER
|
|
static const uint8_t * pb_adv_peer_device_uuid;
|
|
#endif
|
|
|
|
static uint8_t pb_adv_msg_in_buffer[MESH_PB_ADV_MAX_PDU_SIZE]; // TODO: how large are prov messages?
|
|
|
|
// single adv link
|
|
static uint16_t pb_adv_cid = 1;
|
|
static uint8_t pb_adv_provisioner_role;
|
|
|
|
// link state
|
|
static uint32_t pb_adv_link_id;
|
|
static uint8_t pb_adv_link_close_reason;
|
|
static uint8_t pb_adv_link_close_countdown;
|
|
|
|
// random delay for outgoing packets
|
|
static uint32_t pb_adv_lfsr;
|
|
static uint8_t pb_adv_random_delay_active;
|
|
static btstack_timer_source_t pb_adv_random_delay_timer;
|
|
|
|
// incoming message
|
|
static uint8_t pb_adv_msg_in_transaction_nr_prev;
|
|
static uint16_t pb_adv_msg_in_len; //
|
|
static uint8_t pb_adv_msg_in_fcs;
|
|
static uint8_t pb_adv_msg_in_last_segment;
|
|
static uint8_t pb_adv_msg_in_segments_missing; // bitfield for segmentes 1-n
|
|
static uint8_t pb_adv_msg_in_transaction_nr;
|
|
static uint8_t pb_adv_msg_in_send_ack;
|
|
|
|
// oputgoing message
|
|
static uint8_t pb_adv_msg_out_active;
|
|
static uint8_t pb_adv_msg_out_transaction_nr;
|
|
static uint8_t pb_adv_msg_out_completed_transaction_nr;
|
|
static uint16_t pb_adv_msg_out_len;
|
|
static uint16_t pb_adv_msg_out_pos;
|
|
static uint8_t pb_adv_msg_out_seg;
|
|
static uint32_t pb_adv_msg_out_start;
|
|
static const uint8_t * pb_adv_msg_out_buffer;
|
|
|
|
static btstack_packet_handler_t pb_adv_packet_handler;
|
|
|
|
// poor man's random number generator
|
|
static uint32_t pb_adv_random(void){
|
|
pb_adv_lfsr = LFSR(pb_adv_lfsr);
|
|
return pb_adv_lfsr;
|
|
}
|
|
|
|
static void pb_adv_emit_pdu_sent(uint8_t status){
|
|
uint8_t event[] = { HCI_EVENT_MESH_META, 2, MESH_SUBEVENT_PB_TRANSPORT_PDU_SENT, status};
|
|
pb_adv_packet_handler(HCI_EVENT_PACKET, 0, event, sizeof(event));
|
|
}
|
|
|
|
static void pb_adv_emit_link_open(uint8_t status, uint16_t pb_adv_cid){
|
|
uint8_t event[7] = { HCI_EVENT_MESH_META, 5, MESH_SUBEVENT_PB_TRANSPORT_LINK_OPEN, status};
|
|
little_endian_store_16(event, 4, pb_adv_cid);
|
|
event[6] = PB_TYPE_ADV;
|
|
pb_adv_packet_handler(HCI_EVENT_PACKET, 0, event, sizeof(event));
|
|
}
|
|
|
|
static void pb_adv_emit_link_close(uint16_t pb_adv_cid, uint8_t reason){
|
|
uint8_t event[5] = { HCI_EVENT_MESH_META, 3, MESH_SUBEVENT_PB_TRANSPORT_LINK_CLOSED};
|
|
little_endian_store_16(event, 4, pb_adv_cid);
|
|
pb_adv_packet_handler(HCI_EVENT_PACKET, 0, event, sizeof(event));
|
|
}
|
|
|
|
static void pb_adv_handle_bearer_control(uint32_t link_id, uint8_t transaction_nr, const uint8_t * pdu, uint16_t size){
|
|
uint8_t bearer_opcode = pdu[0] >> 2;
|
|
uint8_t reason;
|
|
switch (bearer_opcode){
|
|
case MESH_GENERIC_PROVISIONING_LINK_OPEN: // Open a session on a bearer with a device
|
|
// does it match our device_uuid?
|
|
if (!pb_adv_own_device_uuid) break;
|
|
if (memcmp(&pdu[1], pb_adv_own_device_uuid, 16) != 0) break;
|
|
switch(link_state){
|
|
case LINK_STATE_W4_OPEN:
|
|
pb_adv_link_id = link_id;
|
|
pb_adv_provisioner_role = 0;
|
|
pb_adv_msg_in_transaction_nr = 0xff; // first transaction nr will be 0x00
|
|
pb_adv_msg_in_transaction_nr_prev = 0xff;
|
|
log_info("link open, id %08x", pb_adv_link_id);
|
|
printf("PB-ADV: Link Open %08x\n", pb_adv_link_id);
|
|
link_state = LINK_STATE_W2_SEND_ACK;
|
|
adv_bearer_request_can_send_now_for_pb_adv();
|
|
pb_adv_emit_link_open(0, pb_adv_cid);
|
|
break;
|
|
case LINK_STATE_OPEN:
|
|
if (pb_adv_link_id != link_id) break;
|
|
log_info("link open, resend ACK");
|
|
link_state = LINK_STATE_W2_SEND_ACK;
|
|
adv_bearer_request_can_send_now_for_pb_adv();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
#ifdef ENABLE_MESH_PROVISIONER
|
|
case MESH_GENERIC_PROVISIONING_LINK_ACK: // Acknowledge a session on a bearer
|
|
if (link_state != LINK_STATE_W4_ACK) break;
|
|
link_state = LINK_STATE_OPEN;
|
|
pb_adv_msg_out_transaction_nr = 0;
|
|
pb_adv_msg_in_transaction_nr = 0x7f; // first transaction nr will be 0x80
|
|
pb_adv_msg_in_transaction_nr_prev = 0x7f;
|
|
btstack_run_loop_remove_timer(&pb_adv_random_delay_timer);
|
|
log_info("link open, id %08x", pb_adv_link_id);
|
|
printf("PB-ADV: Link Open %08x\n", pb_adv_link_id);
|
|
pb_adv_emit_link_open(0, pb_adv_cid);
|
|
break;
|
|
#endif
|
|
case MESH_GENERIC_PROVISIONING_LINK_CLOSE: // Close a session on a bearer
|
|
// does it match link id
|
|
if (link_id != pb_adv_link_id) break;
|
|
reason = pdu[1];
|
|
link_state = LINK_STATE_W4_OPEN;
|
|
log_info("link close, reason %x", reason);
|
|
pb_adv_emit_link_close(pb_adv_cid, reason);
|
|
break;
|
|
default:
|
|
log_info("BearerOpcode %x reserved for future use\n", bearer_opcode);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void pb_adv_pdu_complete(void){
|
|
|
|
// Verify FCS
|
|
uint8_t pdu_crc = btstack_crc8_calc((uint8_t*)pb_adv_msg_in_buffer, pb_adv_msg_in_len);
|
|
if (pdu_crc != pb_adv_msg_in_fcs){
|
|
printf("Incoming PDU: fcs %02x, calculated %02x -> drop packet\n", pb_adv_msg_in_fcs, btstack_crc8_calc(pb_adv_msg_in_buffer, pb_adv_msg_in_len));
|
|
return;
|
|
}
|
|
|
|
printf("PB-ADV: %02x complete\n", pb_adv_msg_in_transaction_nr);
|
|
|
|
// transaction complete
|
|
pb_adv_msg_in_transaction_nr_prev = pb_adv_msg_in_transaction_nr;
|
|
if (pb_adv_provisioner_role){
|
|
pb_adv_msg_in_transaction_nr = 0x7f; // invalid
|
|
} else {
|
|
pb_adv_msg_in_transaction_nr = 0xff; // invalid
|
|
}
|
|
|
|
// Ack Transaction
|
|
pb_adv_msg_in_send_ack = 1;
|
|
pb_adv_run();
|
|
|
|
// Forward to Provisioning
|
|
pb_adv_packet_handler(PROVISIONING_DATA_PACKET, 0, pb_adv_msg_in_buffer, pb_adv_msg_in_len);
|
|
}
|
|
|
|
static void pb_adv_handle_transaction_start(uint8_t transaction_nr, const uint8_t * pdu, uint16_t size){
|
|
|
|
// resend ack if packet from previous transaction received
|
|
if (transaction_nr != 0xff && transaction_nr == pb_adv_msg_in_transaction_nr_prev){
|
|
printf("PB_ADV: %02x transaction complete, resending ack \n", transaction_nr);
|
|
pb_adv_msg_in_send_ack = 1;
|
|
return;
|
|
}
|
|
|
|
// new transaction?
|
|
if (transaction_nr != pb_adv_msg_in_transaction_nr){
|
|
|
|
// check len
|
|
uint16_t msg_len = big_endian_read_16(pdu, 1);
|
|
if (msg_len > MESH_PB_ADV_MAX_PDU_SIZE){
|
|
// abort transaction
|
|
return;
|
|
}
|
|
|
|
// check num segments
|
|
uint8_t last_segment = pdu[0] >> 2;
|
|
if (last_segment >= MESH_PB_ADV_MAX_SEGMENTS){
|
|
// abort transaction
|
|
return;
|
|
}
|
|
|
|
printf("PB-ADV: %02x started\n", transaction_nr);
|
|
|
|
pb_adv_msg_in_transaction_nr = transaction_nr;
|
|
pb_adv_msg_in_len = msg_len;
|
|
pb_adv_msg_in_fcs = pdu[3];
|
|
pb_adv_msg_in_last_segment = last_segment;
|
|
|
|
// set bits for segments 1..n (segment 0 already received in this message)
|
|
pb_adv_msg_in_segments_missing = (1 << last_segment) - 1;
|
|
|
|
// store payload
|
|
uint16_t payload_len = size - 4;
|
|
memcpy(pb_adv_msg_in_buffer, &pdu[4], payload_len);
|
|
|
|
// complete?
|
|
if (pb_adv_msg_in_segments_missing == 0){
|
|
pb_adv_pdu_complete();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void pb_adv_handle_transaction_cont(uint8_t transaction_nr, const uint8_t * pdu, uint16_t size){
|
|
|
|
// check transaction nr
|
|
if (transaction_nr != 0xff && transaction_nr == pb_adv_msg_in_transaction_nr_prev){
|
|
printf("PB_ADV: %02x transaction complete, resending resending ack\n", transaction_nr);
|
|
pb_adv_msg_in_send_ack = 1;
|
|
return;
|
|
}
|
|
|
|
if (transaction_nr != pb_adv_msg_in_transaction_nr){
|
|
printf("PB-ADV: %02x received msg for transaction nr %x\n", pb_adv_msg_in_transaction_nr, transaction_nr);
|
|
return;
|
|
}
|
|
|
|
// validate seg nr
|
|
uint8_t seg = pdu[0] >> 2;
|
|
if (seg >= MESH_PB_ADV_MAX_SEGMENTS || seg == 0){
|
|
return;
|
|
}
|
|
|
|
// check if segment already received
|
|
uint8_t seg_mask = 1 << (seg-1);
|
|
if ((pb_adv_msg_in_segments_missing & seg_mask) == 0){
|
|
printf("PB-ADV: %02x, segment %u already received\n", transaction_nr, seg);
|
|
return;
|
|
}
|
|
printf("PB-ADV: %02x, segment %u stored\n", transaction_nr, seg);
|
|
|
|
// calculate offset and fragment size
|
|
uint16_t msg_pos = MESH_PB_ADV_START_PAYLOAD + (seg-1) * MESH_PB_ADV_CONT_PAYLOAD;
|
|
uint16_t fragment_size = size - 1;
|
|
|
|
// check size if last segment
|
|
if (seg == pb_adv_msg_in_last_segment && (msg_pos + fragment_size) != pb_adv_msg_in_len){
|
|
// last segment has invalid size
|
|
return;
|
|
}
|
|
|
|
// store segment and mark as received
|
|
memcpy(&pb_adv_msg_in_buffer[msg_pos], &pdu[1], fragment_size);
|
|
pb_adv_msg_in_segments_missing &= ~seg_mask;
|
|
|
|
// last segment
|
|
if (pb_adv_msg_in_segments_missing == 0){
|
|
pb_adv_pdu_complete();
|
|
}
|
|
}
|
|
|
|
static void pb_adv_outgoing_transation_complete(uint8_t status){
|
|
// stop sending
|
|
pb_adv_msg_out_active = 0;
|
|
// emit done
|
|
pb_adv_emit_pdu_sent(status);
|
|
// keep track of ack'ed transactions
|
|
pb_adv_msg_out_completed_transaction_nr = pb_adv_msg_out_transaction_nr;
|
|
// increment outgoing transaction nr
|
|
pb_adv_msg_out_transaction_nr++;
|
|
if (pb_adv_msg_out_transaction_nr == 0x00){
|
|
// Device role
|
|
pb_adv_msg_out_transaction_nr = 0x80;
|
|
}
|
|
if (pb_adv_msg_out_transaction_nr == 0x80){
|
|
// Provisioner role
|
|
pb_adv_msg_out_transaction_nr = 0x00;
|
|
}
|
|
}
|
|
|
|
static void pb_adv_handle_transaction_ack(uint8_t transaction_nr, const uint8_t * pdu, uint16_t size){
|
|
if (transaction_nr == pb_adv_msg_out_transaction_nr){
|
|
printf("PB-ADV: %02x ACK received\n", transaction_nr);
|
|
pb_adv_outgoing_transation_complete(ERROR_CODE_SUCCESS);
|
|
} else if (transaction_nr == pb_adv_msg_out_completed_transaction_nr){
|
|
// Transaction ack received again
|
|
} else {
|
|
printf("PB-ADV: %02x unexpected Transaction ACK %x recevied\n", pb_adv_msg_out_transaction_nr, transaction_nr);
|
|
}
|
|
}
|
|
|
|
static int pb_adv_packet_to_send(void){
|
|
return pb_adv_msg_in_send_ack || pb_adv_msg_out_active || (link_state == LINK_STATE_W4_ACK);
|
|
}
|
|
|
|
static void pb_adv_timer_handler(btstack_timer_source_t * ts){
|
|
pb_adv_random_delay_active = 0;
|
|
if (!pb_adv_packet_to_send()) return;
|
|
adv_bearer_request_can_send_now_for_pb_adv();
|
|
}
|
|
|
|
static void pb_adv_run(void){
|
|
if (!pb_adv_packet_to_send()) return;
|
|
if (pb_adv_random_delay_active) return;
|
|
|
|
// spec recommends 20-50 ms, we use 20-51 ms
|
|
pb_adv_random_delay_active = 1;
|
|
uint16_t random_delay_ms = 20 + (pb_adv_random() & 0x1f);
|
|
log_info("random delay %u ms", random_delay_ms);
|
|
btstack_run_loop_set_timer_handler(&pb_adv_random_delay_timer, &pb_adv_timer_handler);
|
|
btstack_run_loop_set_timer(&pb_adv_random_delay_timer, random_delay_ms);
|
|
btstack_run_loop_add_timer(&pb_adv_random_delay_timer);
|
|
}
|
|
|
|
static void pb_adv_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
|
if (packet_type != HCI_EVENT_PACKET) return;
|
|
const uint8_t * data;
|
|
uint8_t length;
|
|
uint32_t link_id;
|
|
uint8_t transaction_nr;
|
|
uint8_t generic_provisioning_control;
|
|
switch(packet[0]){
|
|
case GAP_EVENT_ADVERTISING_REPORT:
|
|
|
|
data = gap_event_advertising_report_get_data(packet);
|
|
// PDB ADV PDU
|
|
length = data[0];
|
|
link_id = big_endian_read_32(data, 2);
|
|
transaction_nr = data[6];
|
|
// generic provision PDU
|
|
generic_provisioning_control = data[7];
|
|
mesh_gpcf_format_t generic_provisioning_control_format = (mesh_gpcf_format_t) generic_provisioning_control & 3;
|
|
|
|
// unless, we're waiting for LINK_OPEN, check link_id
|
|
if (link_state != LINK_STATE_W4_OPEN){
|
|
if (link_id != pb_adv_link_id) break;
|
|
}
|
|
|
|
if (generic_provisioning_control_format == MESH_GPCF_PROV_BEARER_CONTROL){
|
|
pb_adv_handle_bearer_control(link_id, transaction_nr, &data[7], length-6);
|
|
break;
|
|
}
|
|
|
|
// verify link id and link state
|
|
if (link_state != LINK_STATE_OPEN) break;
|
|
|
|
switch (generic_provisioning_control_format){
|
|
case MESH_GPCF_TRANSACTION_START:
|
|
pb_adv_handle_transaction_start(transaction_nr, &data[7], length-6);
|
|
break;
|
|
case MESH_GPCF_TRANSACTION_CONT:
|
|
pb_adv_handle_transaction_cont(transaction_nr, &data[7], length-6);
|
|
break;
|
|
case MESH_GPCF_TRANSACTION_ACK:
|
|
pb_adv_handle_transaction_ack(transaction_nr, &data[7], length-6);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
pb_adv_run();
|
|
break;
|
|
case HCI_EVENT_MESH_META:
|
|
switch(packet[2]){
|
|
case MESH_SUBEVENT_CAN_SEND_NOW:
|
|
if (link_state == LINK_STATE_W4_ACK){
|
|
// build packet
|
|
uint8_t buffer[22];
|
|
big_endian_store_32(buffer, 0, pb_adv_link_id);
|
|
buffer[4] = 0; // Transaction ID = 0
|
|
buffer[5] = (0 << 2) | 3; // Link Open | Provisioning Bearer Control
|
|
memcpy(&buffer[6], pb_adv_peer_device_uuid, 16);
|
|
adv_bearer_send_pb_adv(buffer, sizeof(buffer));
|
|
log_info("link open %08x", pb_adv_link_id);
|
|
printf("PB-ADV: Sending Link Open for device uuid: ");
|
|
printf_hexdump(pb_adv_peer_device_uuid, 16);
|
|
btstack_run_loop_set_timer_handler(&pb_adv_random_delay_timer, &pb_adv_timer_handler);
|
|
btstack_run_loop_set_timer(&pb_adv_random_delay_timer, PB_ADV_LINK_OPEN_RETRANSMIT_MS);
|
|
btstack_run_loop_add_timer(&pb_adv_random_delay_timer);
|
|
break;
|
|
}
|
|
if (link_state == LINK_STATE_CLOSING){
|
|
log_info("link close %08x", pb_adv_link_id);
|
|
printf("PB-ADV: Sending Link Close\n");
|
|
// build packet
|
|
uint8_t buffer[7];
|
|
big_endian_store_32(buffer, 0, pb_adv_link_id);
|
|
buffer[4] = 0; // Transaction ID = 0
|
|
buffer[5] = (2 << 2) | 3; // Link Close | Provisioning Bearer Control
|
|
buffer[6] = pb_adv_link_close_reason;
|
|
adv_bearer_send_pb_adv(buffer, sizeof(buffer));
|
|
pb_adv_link_close_countdown--;
|
|
if (pb_adv_link_close_countdown) {
|
|
adv_bearer_request_can_send_now_for_pb_adv();
|
|
} else {
|
|
link_state = LINK_STATE_W4_OPEN;
|
|
}
|
|
break;
|
|
}
|
|
if (link_state == LINK_STATE_W2_SEND_ACK){
|
|
link_state = LINK_STATE_OPEN;
|
|
pb_adv_msg_out_transaction_nr = 0x80;
|
|
// build packet
|
|
uint8_t buffer[6];
|
|
big_endian_store_32(buffer, 0, pb_adv_link_id);
|
|
buffer[4] = 0;
|
|
buffer[5] = (1 << 2) | 3; // Link Ack | Provisioning Bearer Control
|
|
adv_bearer_send_pb_adv(buffer, sizeof(buffer));
|
|
log_info("link ack %08x", pb_adv_link_id);
|
|
printf("PB-ADV: Sending Link Open Ack\n");
|
|
break;
|
|
}
|
|
if (pb_adv_msg_in_send_ack){
|
|
pb_adv_msg_in_send_ack = 0;
|
|
uint8_t buffer[6];
|
|
big_endian_store_32(buffer, 0, pb_adv_link_id);
|
|
buffer[4] = pb_adv_msg_in_transaction_nr_prev;
|
|
buffer[5] = MESH_GPCF_TRANSACTION_ACK;
|
|
adv_bearer_send_pb_adv(buffer, sizeof(buffer));
|
|
log_info("transaction ack %08x", pb_adv_link_id);
|
|
printf("PB-ADV: %02x sending ACK\n", pb_adv_msg_in_transaction_nr_prev);
|
|
pb_adv_run();
|
|
break;
|
|
}
|
|
if (pb_adv_msg_out_active){
|
|
|
|
// check timeout for outgoing message
|
|
// since uint32_t is used and time now must be greater than pb_adv_msg_out_start,
|
|
// this claculation is correct even when the run loop time overruns
|
|
uint32_t transaction_time_ms = btstack_run_loop_get_time_ms() - pb_adv_msg_out_start;
|
|
if (transaction_time_ms >= MESH_GENERIC_PROVISIONING_TRANSACTION_TIMEOUT_MS){
|
|
pb_adv_outgoing_transation_complete(ERROR_CODE_CONNECTION_TIMEOUT);
|
|
return;
|
|
}
|
|
|
|
uint8_t buffer[29]; // ADV MTU
|
|
big_endian_store_32(buffer, 0, pb_adv_link_id);
|
|
buffer[4] = pb_adv_msg_out_transaction_nr;
|
|
uint16_t bytes_left;
|
|
uint16_t pos;
|
|
if (pb_adv_msg_out_pos == 0){
|
|
// Transaction start
|
|
int seg_n = pb_adv_msg_out_len / 24;
|
|
pb_adv_msg_out_seg = 0;
|
|
buffer[5] = seg_n << 2 | MESH_GPCF_TRANSACTION_START;
|
|
big_endian_store_16(buffer, 6, pb_adv_msg_out_len);
|
|
buffer[8] = btstack_crc8_calc((uint8_t*)pb_adv_msg_out_buffer, pb_adv_msg_out_len);
|
|
pos = 9;
|
|
bytes_left = 24 - 4;
|
|
printf("PB-ADV: %02x Sending Start: ", pb_adv_msg_out_transaction_nr);
|
|
} else {
|
|
// Transaction continue
|
|
buffer[5] = pb_adv_msg_out_seg << 2 | MESH_GPCF_TRANSACTION_CONT;
|
|
pos = 6;
|
|
bytes_left = 24 - 1;
|
|
printf("PB-ADV: %02x Sending Cont: ", pb_adv_msg_out_transaction_nr);
|
|
}
|
|
pb_adv_msg_out_seg++;
|
|
uint16_t bytes_to_copy = btstack_min(bytes_left, pb_adv_msg_out_len - pb_adv_msg_out_pos);
|
|
memcpy(&buffer[pos], &pb_adv_msg_out_buffer[pb_adv_msg_out_pos], bytes_to_copy);
|
|
pos += bytes_to_copy;
|
|
printf("bytes %02u, pos %02u, len %02u: ", bytes_to_copy, pb_adv_msg_out_pos, pb_adv_msg_out_len);
|
|
printf_hexdump(buffer, pos);
|
|
pb_adv_msg_out_pos += bytes_to_copy;
|
|
|
|
if (pb_adv_msg_out_pos == pb_adv_msg_out_len){
|
|
// done
|
|
pb_adv_msg_out_pos = 0;
|
|
}
|
|
adv_bearer_send_pb_adv(buffer, pos);
|
|
pb_adv_run();
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void pb_adv_init(uint8_t * device_uuid){
|
|
pb_adv_own_device_uuid = device_uuid;
|
|
adv_bearer_register_for_pb_adv(&pb_adv_handler);
|
|
pb_adv_lfsr = 0x12345678;
|
|
pb_adv_random();
|
|
}
|
|
|
|
void pb_adv_register_packet_handler(btstack_packet_handler_t packet_handler){
|
|
pb_adv_packet_handler = packet_handler;
|
|
}
|
|
|
|
void pb_adv_send_pdu(uint16_t pb_adv_cid, const uint8_t * pdu, uint16_t size){
|
|
UNUSED(pb_adv_cid);
|
|
printf("PB-ADV: Send packet ");
|
|
printf_hexdump(pdu, size);
|
|
pb_adv_msg_out_buffer = pdu;
|
|
pb_adv_msg_out_len = size;
|
|
pb_adv_msg_out_pos = 0;
|
|
pb_adv_msg_out_start = btstack_run_loop_get_time_ms();
|
|
pb_adv_msg_out_active = 1;
|
|
pb_adv_run();
|
|
}
|
|
|
|
/**
|
|
* Close Link
|
|
* @param pb_adv_cid
|
|
*/
|
|
void pb_adv_close_link(uint16_t pb_adv_cid, uint8_t reason){
|
|
switch (link_state){
|
|
case LINK_STATE_W4_ACK:
|
|
case LINK_STATE_OPEN:
|
|
case LINK_STATE_W2_SEND_ACK:
|
|
pb_adv_emit_link_close(pb_adv_cid, 0);
|
|
link_state = LINK_STATE_CLOSING;
|
|
pb_adv_link_close_countdown = 3;
|
|
pb_adv_link_close_reason = reason;
|
|
adv_bearer_request_can_send_now_for_pb_adv();
|
|
break;
|
|
case LINK_STATE_W4_OPEN:
|
|
case LINK_STATE_CLOSING:
|
|
// nothing to do
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef ENABLE_MESH_PROVISIONER
|
|
uint16_t pb_adv_create_link(const uint8_t * device_uuid){
|
|
if (link_state != LINK_STATE_W4_OPEN) return 0;
|
|
|
|
pb_adv_peer_device_uuid = device_uuid;
|
|
pb_adv_provisioner_role = 1;
|
|
|
|
// create new 32-bit link id
|
|
pb_adv_link_id = pb_adv_random();
|
|
|
|
// after sending OPEN, we wait for an ACK
|
|
link_state = LINK_STATE_W4_ACK;
|
|
|
|
// request outgoing
|
|
adv_bearer_request_can_send_now_for_pb_adv();
|
|
|
|
// dummy pb_adv_cid
|
|
return pb_adv_cid;
|
|
}
|
|
#endif
|
|
|