blob: de3eb2dc9af701708eb063f78394dbb97cdd4eb1 [file] [log] [blame]
/*
* Copyright (c) 2016-2017 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr.h>
#include "util/util.h"
#include "pdu.h"
#include "ctrl.h"
#include "ll.h"
static struct {
u16_t interval;
u8_t pdu_adv_type:4;
u8_t tx_addr:1;
u8_t rx_addr:1;
u8_t filter_policy:2;
u8_t chl_map:3;
u8_t adv_addr[BDADDR_SIZE];
u8_t direct_addr[BDADDR_SIZE];
} ll_adv;
void ll_adv_params_set(u16_t interval, u8_t adv_type,
u8_t own_addr_type, u8_t direct_addr_type,
u8_t const *const direct_addr, u8_t chl_map,
u8_t filter_policy)
{
u8_t const pdu_adv_type[] = {PDU_ADV_TYPE_ADV_IND,
PDU_ADV_TYPE_DIRECT_IND,
PDU_ADV_TYPE_SCAN_IND,
PDU_ADV_TYPE_NONCONN_IND,
PDU_ADV_TYPE_DIRECT_IND};
struct radio_adv_data *radio_adv_data;
struct pdu_adv *pdu;
/* TODO: check and fail if adv state active else
* update (implemented below) current index elements for
* both adv and scan data.
*/
/* remember params so that set adv/scan data and adv enable
* interface can correctly update adv/scan data in the
* double buffer between caller and controller context.
*/
/* Set interval for Undirected or Low Duty Cycle Directed Advertising */
if (adv_type != 0x01) {
ll_adv.interval = interval;
} else {
ll_adv.interval = 0;
}
ll_adv.chl_map = chl_map;
ll_adv.filter_policy = filter_policy;
ll_adv.pdu_adv_type = pdu_adv_type[adv_type];
ll_adv.tx_addr = own_addr_type;
ll_adv.rx_addr = 0;
/* update the current adv data */
radio_adv_data = radio_adv_data_get();
pdu = (struct pdu_adv *)&radio_adv_data->data[radio_adv_data->last][0];
pdu->type = ll_adv.pdu_adv_type;
pdu->rfu = 0;
if (IS_ENABLED(CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2) &&
((ll_adv.pdu_adv_type == PDU_ADV_TYPE_ADV_IND) ||
(ll_adv.pdu_adv_type == PDU_ADV_TYPE_DIRECT_IND))) {
pdu->chan_sel = 1;
} else {
pdu->chan_sel = 0;
}
pdu->tx_addr = ll_adv.tx_addr;
if (ll_adv.pdu_adv_type == PDU_ADV_TYPE_DIRECT_IND) {
ll_adv.rx_addr = direct_addr_type;
memcpy(&ll_adv.direct_addr[0], direct_addr, BDADDR_SIZE);
memcpy(&pdu->payload.direct_ind.tgt_addr[0], direct_addr,
BDADDR_SIZE);
pdu->len = sizeof(struct pdu_adv_payload_direct_ind);
} else if (pdu->len == 0) {
pdu->len = BDADDR_SIZE;
}
pdu->rx_addr = ll_adv.rx_addr;
/* update the current scan data */
radio_adv_data = radio_scan_data_get();
pdu = (struct pdu_adv *)&radio_adv_data->data[radio_adv_data->last][0];
pdu->type = PDU_ADV_TYPE_SCAN_RSP;
pdu->rfu = 0;
pdu->chan_sel = 0;
pdu->tx_addr = ll_adv.tx_addr;
pdu->rx_addr = 0;
if (pdu->len == 0) {
pdu->len = BDADDR_SIZE;
}
}
void ll_adv_data_set(u8_t len, u8_t const *const data)
{
struct radio_adv_data *radio_adv_data;
struct pdu_adv *pdu;
u8_t last;
/* TODO: dont update data if directed adv type. */
/* use the last index in double buffer, */
radio_adv_data = radio_adv_data_get();
if (radio_adv_data->first == radio_adv_data->last) {
last = radio_adv_data->last + 1;
if (last == DOUBLE_BUFFER_SIZE) {
last = 0;
}
} else {
last = radio_adv_data->last;
}
/* update adv pdu fields. */
pdu = (struct pdu_adv *)&radio_adv_data->data[last][0];
pdu->type = ll_adv.pdu_adv_type;
pdu->rfu = 0;
if (IS_ENABLED(CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2) &&
((ll_adv.pdu_adv_type == PDU_ADV_TYPE_ADV_IND) ||
(ll_adv.pdu_adv_type == PDU_ADV_TYPE_DIRECT_IND))) {
pdu->chan_sel = 1;
} else {
pdu->chan_sel = 0;
}
pdu->tx_addr = ll_adv.tx_addr;
pdu->rx_addr = ll_adv.rx_addr;
memcpy(&pdu->payload.adv_ind.addr[0], &ll_adv.adv_addr[0], BDADDR_SIZE);
if (ll_adv.pdu_adv_type == PDU_ADV_TYPE_DIRECT_IND) {
memcpy(&pdu->payload.direct_ind.tgt_addr[0],
&ll_adv.direct_addr[0], BDADDR_SIZE);
pdu->len = sizeof(struct pdu_adv_payload_direct_ind);
} else {
memcpy(&pdu->payload.adv_ind.data[0], data, len);
pdu->len = BDADDR_SIZE + len;
}
/* commit the update so controller picks it. */
radio_adv_data->last = last;
}
void ll_scan_data_set(u8_t len, u8_t const *const data)
{
struct radio_adv_data *radio_scan_data;
struct pdu_adv *pdu;
u8_t last;
/* use the last index in double buffer, */
radio_scan_data = radio_scan_data_get();
if (radio_scan_data->first == radio_scan_data->last) {
last = radio_scan_data->last + 1;
if (last == DOUBLE_BUFFER_SIZE) {
last = 0;
}
} else {
last = radio_scan_data->last;
}
/* update scan pdu fields. */
pdu = (struct pdu_adv *)&radio_scan_data->data[last][0];
pdu->type = PDU_ADV_TYPE_SCAN_RSP;
pdu->rfu = 0;
pdu->chan_sel = 0;
pdu->tx_addr = ll_adv.tx_addr;
pdu->rx_addr = 0;
pdu->len = BDADDR_SIZE + len;
memcpy(&pdu->payload.scan_rsp.addr[0], &ll_adv.adv_addr[0],
BDADDR_SIZE);
memcpy(&pdu->payload.scan_rsp.data[0], data, len);
/* commit the update so controller picks it. */
radio_scan_data->last = last;
}
u32_t ll_adv_enable(u8_t enable)
{
struct radio_adv_data *radio_scan_data;
struct radio_adv_data *radio_adv_data;
struct pdu_adv *pdu_scan;
struct pdu_adv *pdu_adv;
u32_t status;
if (!enable) {
status = radio_adv_disable();
return status;
}
/* TODO: move the addr remembered into controller
* this way when implementing Privacy 1.2, generated
* new resolvable addresses can be used instantly.
*/
/* remember addr to use and also update the addr in
* both adv and scan response PDUs.
*/
radio_adv_data = radio_adv_data_get();
radio_scan_data = radio_scan_data_get();
pdu_adv = (struct pdu_adv *)&radio_adv_data->data
[radio_adv_data->last][0];
pdu_scan = (struct pdu_adv *)&radio_scan_data->data
[radio_scan_data->last][0];
ll_addr_get(ll_adv.tx_addr, ll_adv.adv_addr);
memcpy(&pdu_adv->payload.adv_ind.addr[0], ll_adv.adv_addr, BDADDR_SIZE);
memcpy(&pdu_scan->payload.scan_rsp.addr[0], ll_adv.adv_addr,
BDADDR_SIZE);
status = radio_adv_enable(ll_adv.interval, ll_adv.chl_map,
ll_adv.filter_policy);
return status;
}