blob: adcbcfb789ba66d672f0f2af9f04bf5a9022a711 [file] [log] [blame]
/*
* This file is part of the UWB stack for linux.
*
* Copyright (c) 2020-2021 Qorvo US, Inc.
*
* This software is provided under the GNU General Public License, version 2
* (GPLv2), as well as under a Qorvo commercial license.
*
* You may choose to use this software under the terms of the GPLv2 License,
* version 2 ("GPLv2"), as published by the Free Software Foundation.
* You should have received a copy of the GPLv2 along with this program. If
* not, see <http://www.gnu.org/licenses/>.
*
* This program is distributed under the GPLv2 in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GPLv2 for more
* details.
*
* If you cannot meet the requirements of the GPLv2, you may not use this
* software for any purpose without first obtaining a commercial license from
* Qorvo. Please contact Qorvo to inquire about licensing terms.
*/
#include "fira_access.h"
#include "fira_session.h"
#include "fira_frame.h"
#include "fira_trace.h"
#include <asm/unaligned.h>
#include <linux/bitops.h>
#include <linux/string.h>
#include <linux/ieee802154.h>
#include <net/mcps802154_frame.h>
#include "warn_return.h"
#include "utils_fixed_point.h"
#define FIRA_FRAME_MAX_SIZE 127
#define FIRA_STS_FOM_THRESHOLD 153
static void fira_update_antennas_id(struct fira_session *session);
static struct mcps802154_access *
fira_access_controlee(struct fira_local *local, struct fira_session *session);
/**
* fira_access_setup_frame() - Fill an access frame from a FiRa slot.
* @local: FiRa context.
* @session: Session.
* @frame: Access frame.
* @sts_params: Where to store STS parameters.
* @slot: Corresponding slot.
* @frame_dtu: frame transmission or reception date.
* @is_tx: true for TX.
*/
static void fira_access_setup_frame(struct fira_local *local,
struct fira_session *session,
struct mcps802154_access_frame *frame,
struct mcps802154_sts_params *sts_params,
const struct fira_slot *slot, u32 frame_dtu,
bool is_tx)
{
const struct fira_session_params *params = &session->params;
struct mcps802154_sts_params *sts_params_for_access = NULL;
int rframe_config = session->params.rframe_config;
bool is_rframe = slot->message_id <= FIRA_MESSAGE_ID_RFRAME_MAX;
bool is_last_rframe = slot->message_id == FIRA_MESSAGE_ID_RANGING_FINAL;
bool is_first_frame = slot->message_id == FIRA_MESSAGE_ID_CONTROL;
if (is_rframe) {
memcpy(sts_params->v, session->crypto.sts_v, AES_BLOCK_SIZE);
put_unaligned_be32(slot->index,
sts_params->v + AES_BLOCK_SIZE / 2);
memcpy(sts_params->key,
session->crypto.derived_authentication_key,
AES_KEYSIZE_128);
/* Constant for the moment. */
sts_params->n_segs = 1;
sts_params->seg_len = 64;
sts_params->sp2_tx_gap_4chips = 0;
sts_params->sp2_rx_gap_4chips[0] = 0;
sts_params->sp2_rx_gap_4chips[1] = 0;
sts_params->sp2_rx_gap_4chips[2] = 0;
sts_params->sp2_rx_gap_4chips[3] = 0;
sts_params_for_access = sts_params;
}
if (is_tx) {
u8 flags = MCPS802154_TX_FRAME_TIMESTAMP_DTU;
/* Add a small margin to the Tx timestamps. */
if (!is_first_frame)
frame_dtu += FIRA_TX_MARGIN_US *
local->llhw->dtu_freq_hz / 1000000;
if (is_rframe) {
struct fira_ranging_info *ranging_info;
ranging_info =
&local->ranging_info[slot->ranging_index];
ranging_info->timestamps_rctu[slot->message_id] =
mcps802154_tx_timestamp_dtu_to_rmarker_rctu(
local->llhw, frame_dtu, slot->tx_ant);
flags |= MCPS802154_TX_FRAME_RANGING;
if (rframe_config == FIRA_RFRAME_CONFIG_SP3)
flags |= MCPS802154_TX_FRAME_SP3;
else
flags |= MCPS802154_TX_FRAME_SP1;
if (!is_last_rframe)
flags |= MCPS802154_TX_FRAME_KEEP_RANGING_CLOCK;
}
*frame = (struct mcps802154_access_frame){
.is_tx = true,
.tx_frame_info = {
.timestamp_dtu = frame_dtu,
.flags = flags,
.ant_id = slot->tx_ant,
},
.sts_params = sts_params_for_access,
};
} else {
u8 flags = MCPS802154_RX_INFO_TIMESTAMP_DTU;
u16 request = 0;
if (is_rframe) {
flags |= MCPS802154_RX_INFO_RANGING;
if (rframe_config == FIRA_RFRAME_CONFIG_SP3)
flags |= MCPS802154_RX_INFO_SP3;
else
flags |= MCPS802154_RX_INFO_SP1;
if (!is_last_rframe)
flags |= MCPS802154_RX_INFO_KEEP_RANGING_CLOCK;
request = MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU |
MCPS802154_RX_FRAME_INFO_RANGING_STS_FOM;
if (params->aoa_result_req) {
flags |= MCPS802154_RX_INFO_RANGING_PDOA;
request |=
MCPS802154_RX_FRAME_INFO_RANGING_PDOA |
MCPS802154_RX_FRAME_INFO_RANGING_PDOA_FOM;
}
}
*frame = (struct mcps802154_access_frame){
.is_tx = false,
.rx = {
.info = {
.timestamp_dtu = frame_dtu,
.flags = flags,
.ant_pair_id = slot->rx_ant_pair,
},
.frame_info_flags_request = request,
},
.sts_params = sts_params_for_access,
};
}
}
static bool fira_rx_sts_good(struct fira_local *local,
const struct mcps802154_rx_frame_info *info)
{
if (!(info->flags & MCPS802154_RX_FRAME_INFO_RANGING_STS_FOM))
return false;
/* Only one segment for the moment. */
if (info->ranging_sts_fom[0] < FIRA_STS_FOM_THRESHOLD)
return false;
return true;
}
static void fira_ranging_info_set_status(struct fira_ranging_info *ranging_info,
enum fira_ranging_status status,
u8 slot_index)
{
/* Report first error. */
if (ranging_info->status)
return;
ranging_info->status = status;
ranging_info->slot_index = slot_index;
}
static void fira_rx_frame_ranging(struct fira_local *local,
const struct fira_slot *slot,
struct sk_buff *skb,
const struct mcps802154_rx_frame_info *info)
{
const struct fira_session *session = local->current_session;
const struct fira_session_params *params = &session->params;
struct fira_ranging_info *ranging_info =
&local->ranging_info[slot->ranging_index];
struct mcps802154_ie_get_context ie_get = {};
bool pdoa_info_present;
if (!fira_rx_sts_good(local, info)) {
fira_ranging_info_set_status(
ranging_info, FIRA_STATUS_RANGING_RX_PHY_STS_FAILED,
slot->index);
return;
}
if (!(info->flags & MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU)) {
fira_ranging_info_set_status(
ranging_info, FIRA_STATUS_RANGING_RX_PHY_TOA_FAILED,
slot->index);
return;
}
ranging_info->timestamps_rctu[slot->message_id] = info->timestamp_rctu;
pdoa_info_present = info->flags & MCPS802154_RX_FRAME_INFO_RANGING_PDOA;
if (pdoa_info_present) {
struct fira_local_aoa_info *local_aoa;
bool pdoa_fom_info_present =
info->flags & MCPS802154_RX_FRAME_INFO_RANGING_PDOA_FOM;
s16 local_pdoa_q11 = info->ranging_pdoa_rad_q11;
s16 local_aoa_q11 = info->ranging_aoa_rad_q11;
if (params->rx_antenna_pair_azimuth == slot->rx_ant_pair) {
local_aoa = &ranging_info->local_aoa_azimuth;
} else if (params->rx_antenna_pair_elevation ==
slot->rx_ant_pair) {
local_aoa = &ranging_info->local_aoa_elevation;
} else {
local_aoa = &ranging_info->local_aoa;
}
local_aoa->present = true;
local_aoa->rx_ant_pair = slot->rx_ant_pair;
local_aoa->pdoa_2pi = map_q11_to_2pi(local_pdoa_q11);
local_aoa->aoa_2pi = map_q11_to_2pi(local_aoa_q11);
if (pdoa_fom_info_present)
local_aoa->aoa_fom = info->ranging_pdoa_fom;
}
if (skb) {
if (fira_frame_header_check_decrypt(local, slot, skb, &ie_get,
NULL, NULL) ||
!fira_frame_rframe_payload_check(local, slot, skb,
&ie_get)) {
fira_ranging_info_set_status(
ranging_info,
FIRA_STATUS_RANGING_RX_MAC_IE_DEC_FAILED,
slot->index);
}
}
}
static void fira_rx_frame_control(struct fira_local *local,
const struct fira_slot *slot,
struct sk_buff *skb,
const struct mcps802154_rx_frame_info *info)
{
struct fira_ranging_info *ranging_info =
&local->ranging_info[slot->ranging_index];
struct fira_session *session = local->current_session;
struct fira_session *allow_resync_session = NULL;
struct mcps802154_ie_get_context ie_get = {};
u32 sts_index;
unsigned int n_slots;
int last_slot_index;
int i;
bool stop_ranging;
if (!(info->flags & MCPS802154_RX_FRAME_INFO_TIMESTAMP_DTU)) {
fira_ranging_info_set_status(
ranging_info, FIRA_STATUS_RANGING_RX_PHY_DEC_FAILED,
slot->index);
return;
}
if (fira_frame_header_check_decrypt(
local, slot, skb, &ie_get, &sts_index,
session->synchronised ? NULL : &allow_resync_session))
goto failed;
if (allow_resync_session) {
session = local->current_session = allow_resync_session;
fira_access_controlee(local, session);
}
if (!fira_frame_control_payload_check(local, skb, &ie_get, &n_slots,
&stop_ranging))
goto failed;
fira_session_resync(local, session, sts_index, info->timestamp_dtu);
if (stop_ranging) {
session->stop_inband = true;
return;
}
last_slot_index = 0;
for (i = 1; i < n_slots; i++) {
const struct fira_slot *slot = &local->slots[i];
struct mcps802154_access_frame *frame = &local->frames[i];
struct mcps802154_sts_params *sts_params =
&local->sts_params[i];
bool is_tx;
u32 frame_dtu;
is_tx = slot->tx_controlee_index != -1;
frame_dtu = info->timestamp_dtu +
session->params.slot_duration_dtu * slot->index;
last_slot_index = slot->index;
fira_access_setup_frame(local, session, frame, sts_params, slot,
frame_dtu, is_tx);
}
local->access.timestamp_dtu = info->timestamp_dtu;
local->access.duration_dtu =
session->params.slot_duration_dtu * (last_slot_index + 1);
local->access.n_frames = n_slots;
return;
failed:
fira_ranging_info_set_status(ranging_info,
FIRA_STATUS_RANGING_RX_MAC_IE_DEC_FAILED,
slot->index);
local->access.timestamp_dtu = info->timestamp_dtu;
local->access.duration_dtu = session->params.slot_duration_dtu;
}
static void fira_rx_frame_measurement_report(
struct fira_local *local, const struct fira_slot *slot,
struct sk_buff *skb, const struct mcps802154_rx_frame_info *info)
{
struct fira_ranging_info *ranging_info =
&local->ranging_info[slot->ranging_index];
struct mcps802154_ie_get_context ie_get = {};
if (fira_frame_header_check_decrypt(local, slot, skb, &ie_get, NULL,
NULL))
goto failed;
if (!fira_frame_measurement_report_payload_check(local, slot, skb,
&ie_get))
goto failed;
return;
failed:
fira_ranging_info_set_status(ranging_info,
FIRA_STATUS_RANGING_RX_MAC_IE_DEC_FAILED,
slot->index);
}
static void
fira_rx_frame_result_report(struct fira_local *local,
const struct fira_slot *slot, struct sk_buff *skb,
const struct mcps802154_rx_frame_info *info)
{
struct fira_ranging_info *ranging_info =
&local->ranging_info[slot->ranging_index];
struct mcps802154_ie_get_context ie_get = {};
if (fira_frame_header_check_decrypt(local, slot, skb, &ie_get, NULL,
NULL))
goto failed;
if (!fira_frame_result_report_payload_check(local, slot, skb, &ie_get))
goto failed;
return;
failed:
fira_ranging_info_set_status(ranging_info,
FIRA_STATUS_RANGING_RX_MAC_IE_DEC_FAILED,
slot->index);
}
static bool fira_do_process_rx_frame(enum mcps802154_rx_error_type error,
struct fira_ranging_info *ranging_info,
u8 slot_index)
{
enum fira_ranging_status status;
switch (error) {
case MCPS802154_RX_ERROR_NONE:
return true;
case MCPS802154_RX_ERROR_SFD_TIMEOUT:
case MCPS802154_RX_ERROR_TIMEOUT:
status = FIRA_STATUS_RANGING_RX_TIMEOUT;
break;
case MCPS802154_RX_ERROR_FILTERED:
case MCPS802154_RX_ERROR_BAD_CKSUM:
status = FIRA_STATUS_RANGING_RX_MAC_DEC_FAILED;
break;
case MCPS802154_RX_ERROR_UNCORRECTABLE:
case MCPS802154_RX_ERROR_OTHER:
status = FIRA_STATUS_RANGING_RX_PHY_DEC_FAILED;
break;
}
fira_ranging_info_set_status(ranging_info, status, slot_index);
return false;
}
static void fira_rx_frame(struct mcps802154_access *access, int frame_idx,
struct sk_buff *skb,
const struct mcps802154_rx_frame_info *info,
enum mcps802154_rx_error_type error)
{
struct fira_local *local = access_to_local(access);
const struct fira_slot *slot = &local->slots[frame_idx];
struct fira_session *session = local->current_session;
struct fira_ranging_info *ranging_info =
&local->ranging_info[slot->ranging_index];
if (fira_do_process_rx_frame(error, ranging_info, slot->index)) {
switch (slot->message_id) {
case FIRA_MESSAGE_ID_RANGING_INITIATION:
case FIRA_MESSAGE_ID_RANGING_RESPONSE:
case FIRA_MESSAGE_ID_RANGING_FINAL:
fira_rx_frame_ranging(local, slot, skb, info);
break;
case FIRA_MESSAGE_ID_CONTROL:
fira_rx_frame_control(local, slot, skb, info);
break;
case FIRA_MESSAGE_ID_MEASUREMENT_REPORT:
fira_rx_frame_measurement_report(local, slot, skb,
info);
break;
case FIRA_MESSAGE_ID_RESULT_REPORT:
fira_rx_frame_result_report(local, slot, skb, info);
break;
case FIRA_MESSAGE_ID_CONTROL_UPDATE:
break;
default:
WARN_UNREACHABLE_DEFAULT();
}
}
if (skb)
kfree_skb(skb);
trace_region_fira_rx_message_type(
session, slot->message_id,
local->ranging_info[slot->ranging_index].status);
/* Controlee: Stop round on error.
Controller: Stop when all ranging fails. */
if (local->ranging_info[slot->ranging_index].status)
if (session->params.device_type == FIRA_DEVICE_TYPE_CONTROLEE ||
(slot->message_id <= FIRA_MESSAGE_ID_RFRAME_MAX &&
--local->n_ranging_valid == 0))
access->n_frames = frame_idx + 1;
if (session->params.device_type == FIRA_DEVICE_TYPE_CONTROLEE &&
session->stop_inband) {
access->n_frames = frame_idx + 1;
}
}
static struct sk_buff *fira_tx_get_frame(struct mcps802154_access *access,
int frame_idx)
{
struct fira_local *local = access_to_local(access);
const struct fira_slot *slot = &local->slots[frame_idx];
struct sk_buff *skb;
int rframe = local->current_session->params.rframe_config;
trace_region_fira_tx_message_type(local->current_session,
slot->message_id);
if (rframe == FIRA_RFRAME_CONFIG_SP3 &&
slot->message_id <= FIRA_MESSAGE_ID_RFRAME_MAX)
return NULL;
skb = mcps802154_frame_alloc(local->llhw, FIRA_FRAME_MAX_SIZE,
GFP_KERNEL);
WARN_RETURN_ON(!skb, NULL);
fira_frame_header_put(local, slot, skb);
switch (slot->message_id) {
case FIRA_MESSAGE_ID_RANGING_INITIATION:
case FIRA_MESSAGE_ID_RANGING_RESPONSE:
fira_frame_rframe_payload_put(local, skb);
break;
case FIRA_MESSAGE_ID_RANGING_FINAL:
break;
case FIRA_MESSAGE_ID_CONTROL:
fira_frame_control_payload_put(local, slot, skb);
break;
case FIRA_MESSAGE_ID_MEASUREMENT_REPORT:
fira_frame_measurement_report_payload_put(local, slot, skb);
break;
case FIRA_MESSAGE_ID_RESULT_REPORT:
fira_frame_result_report_payload_put(local, slot, skb);
break;
case FIRA_MESSAGE_ID_CONTROL_UPDATE:
break;
default: /* LCOV_EXCL_START */
kfree_skb(skb);
WARN_UNREACHABLE_DEFAULT();
return NULL;
/* LCOV_EXCL_STOP */
}
if (fira_frame_encrypt(local, slot, skb)) {
kfree_skb(skb);
return NULL;
}
return skb;
}
static void fira_tx_return(struct mcps802154_access *access, int frame_idx,
struct sk_buff *skb,
enum mcps802154_access_tx_return_reason reason)
{
struct fira_local *local = access_to_local(access);
int i;
kfree_skb(skb);
/* Error on TX. */
if (reason == MCPS802154_ACCESS_TX_RETURN_REASON_CANCEL) {
for (i = 0; i < local->n_ranging_info; i++) {
local->ranging_info[i].status =
FIRA_STATUS_RANGING_TX_FAILED;
}
}
}
static void fira_access_done(struct mcps802154_access *access, int error)
{
struct fira_local *local = access_to_local(access);
struct fira_session *session = local->current_session;
int i;
if (error)
for (i = 0; i < local->n_ranging_info; i++)
local->ranging_info[i].status =
FIRA_STATUS_RANGING_INTERNAL_ERROR;
fira_session_access_done(local, session, true);
local->current_session = NULL;
}
struct mcps802154_access_ops fira_access_ops = {
.common = {
.access_done = fira_access_done,
},
.rx_frame = fira_rx_frame,
.tx_get_frame = fira_tx_get_frame,
.tx_return = fira_tx_return,
};
static int fira_next_in_bitfield_u8(u8 bitfield, int prev)
{
u32 padded_bitfield = 1 << 16 | bitfield << 8 | bitfield;
u32 rotated_bitfield = padded_bitfield >> (prev + 1);
return (ffs(rotated_bitfield) + prev) % 8;
}
static void fira_update_antennas_id(struct fira_session *session)
{
const struct fira_session_params *p = &session->params;
session->tx_ant = fira_next_in_bitfield_u8(p->tx_antenna_selection,
session->tx_ant);
switch (p->rx_antenna_switch) {
default:
case FIRA_RX_ANTENNA_SWITCH_BETWEEN_ROUND:
/* Switch pairs between round. */
session->rx_ant_pair[0] = session->rx_ant_pair[1] =
fira_next_in_bitfield_u8(p->rx_antenna_selection,
session->rx_ant_pair[0]);
break;
case FIRA_RX_ANTENNA_SWITCH_DURING_ROUND:
session->rx_ant_pair[0] = p->rx_antenna_pair_azimuth;
session->rx_ant_pair[1] = p->rx_antenna_pair_elevation;
break;
case FIRA_RX_ANTENNA_SWITCH_TWO_RANGING:
/* Switch from one ranging to another. */
if (session->rx_ant_pair[0] == p->rx_antenna_pair_azimuth)
session->rx_ant_pair[0] = p->rx_antenna_pair_elevation;
else
session->rx_ant_pair[0] = p->rx_antenna_pair_azimuth;
session->rx_ant_pair[1] = session->rx_ant_pair[0];
break;
}
}
static __le16 fira_access_set_short_address(struct fira_local *local,
struct fira_session *session,
struct mcps802154_access *access)
{
__le16 src_short_addr = mcps802154_get_short_addr(local->llhw);
if (session->params.short_addr != IEEE802154_ADDR_SHORT_BROADCAST &&
src_short_addr != session->params.short_addr) {
access->hw_addr_filt = (struct ieee802154_hw_addr_filt){
.short_addr = session->params.short_addr,
};
access->hw_addr_filt_changed = IEEE802154_AFILT_SADDR_CHANGED;
return session->params.short_addr;
} else {
access->hw_addr_filt_changed = 0;
return src_short_addr;
}
}
static const struct mcps802154_channel *
fira_access_channel(struct fira_local *local,
const struct fira_session *session)
{
if (session->params.channel_number ||
session->params.preamble_code_index) {
const struct mcps802154_channel *channel =
mcps802154_get_current_channel(local->llhw);
local->channel = *channel;
if (session->params.channel_number)
local->channel.channel = session->params.channel_number;
if (session->params.preamble_code_index)
local->channel.preamble_code =
session->params.preamble_code_index;
return &local->channel;
}
return NULL;
}
static struct mcps802154_access *
fira_access_controller(struct fira_local *local, struct fira_session *session)
{
struct mcps802154_access *access;
struct mcps802154_access_frame *frame;
struct mcps802154_sts_params *sts_params;
struct fira_slot *s;
struct fira_ranging_info *ri;
struct fira_controlees_array *controlees_array =
&session->current_controlees;
int i, j;
u32 frame_dtu;
int index = 0;
access = &local->access;
local->src_short_addr =
fira_access_set_short_address(local, session, access);
local->dst_short_addr = (controlees_array->size == 1) ?
controlees_array->data[0].short_addr :
IEEE802154_ADDR_SHORT_BROADCAST;
access->method = MCPS802154_ACCESS_METHOD_MULTI;
access->ops = &fira_access_ops;
access->timestamp_dtu = session->block_start_dtu +
fira_session_get_round_slot(session) *
session->params.slot_duration_dtu;
access->frames = local->frames;
access->channel = fira_access_channel(local, session);
fira_update_antennas_id(session);
local->n_stopped_controlees_short_addr = 0;
for (i = 0; i < controlees_array->size; i++) {
const struct fira_controlee *c = &controlees_array->data[i];
if (c->state != FIRA_CONTROLEE_STATE_RUNNING) {
local->stopped_controlees_short_addr
[local->n_stopped_controlees_short_addr] =
c->short_addr;
local->n_stopped_controlees_short_addr++;
}
}
local->n_ranging_valid = local->n_ranging_info =
controlees_array->size - local->n_stopped_controlees_short_addr;
ri = local->ranging_info;
memset(ri, 0, local->n_ranging_info * sizeof(*ri));
s = local->slots;
s->index = index++;
s->tx_controlee_index = -1;
s->ranging_index = 0;
s->tx_ant = session->tx_ant;
s->message_id = FIRA_MESSAGE_ID_CONTROL;
s++;
if (local->n_ranging_info) {
s->index = index++;
s->tx_controlee_index = -1;
s->ranging_index = 0;
s->tx_ant = session->tx_ant;
s->message_id = FIRA_MESSAGE_ID_RANGING_INITIATION;
s++;
for (i = 0, j = 0; i < controlees_array->size; i++) {
if (controlees_array->data[i].state !=
FIRA_CONTROLEE_STATE_RUNNING)
continue;
ri->short_addr = controlees_array->data[i].short_addr;
/* Requested in fira_report_aoa function. */
ri++;
s->index = index++;
s->tx_controlee_index = i;
s->ranging_index = j++;
s->rx_ant_pair = session->rx_ant_pair[0];
s->message_id = FIRA_MESSAGE_ID_RANGING_RESPONSE;
s++;
}
s->index = index++;
s->tx_controlee_index = -1;
s->ranging_index = 0;
s->tx_ant = session->tx_ant;
s->message_id = FIRA_MESSAGE_ID_RANGING_FINAL;
s++;
s->index = index++;
s->tx_controlee_index = -1;
s->ranging_index = 0;
s->tx_ant = session->tx_ant;
s->message_id = FIRA_MESSAGE_ID_MEASUREMENT_REPORT;
s++;
if (session->params.result_report_phase) {
for (i = 0, j = 0; i < controlees_array->size; i++) {
if (controlees_array->data[i].state !=
FIRA_CONTROLEE_STATE_RUNNING)
continue;
s->index = index++;
s->tx_controlee_index = i;
s->ranging_index = j++;
s->rx_ant_pair = session->rx_ant_pair[0];
s->message_id = FIRA_MESSAGE_ID_RESULT_REPORT;
s++;
}
}
}
access->n_frames = index;
frame_dtu = access->timestamp_dtu;
for (i = 0; i < access->n_frames; i++) {
bool is_tx;
s = &local->slots[i];
frame = &local->frames[i];
sts_params = &local->sts_params[i];
is_tx = s->tx_controlee_index == -1;
fira_access_setup_frame(local, session, frame, sts_params, s,
frame_dtu, is_tx);
frame_dtu += session->params.slot_duration_dtu;
}
access->duration_dtu = frame_dtu - access->timestamp_dtu;
return access;
}
static struct mcps802154_access *
fira_access_controlee(struct fira_local *local, struct fira_session *session)
{
struct mcps802154_access *access;
struct mcps802154_access_frame *frame;
struct fira_slot *s;
struct fira_ranging_info *ri;
int timeout_dtu;
access = &local->access;
local->src_short_addr =
fira_access_set_short_address(local, session, access);
local->dst_short_addr = session->params.controller_short_addr;
access->method = MCPS802154_ACCESS_METHOD_MULTI;
access->ops = &fira_access_ops;
access->timestamp_dtu = session->last_access_timestamp_dtu;
access->duration_dtu = session->last_access_duration_dtu;
access->frames = local->frames;
access->n_frames = 1;
access->channel = fira_access_channel(local, session);
fira_update_antennas_id(session);
ri = local->ranging_info;
memset(ri, 0, sizeof(*ri));
ri->short_addr = session->params.controller_short_addr;
local->n_ranging_info = 1;
local->n_stopped_controlees_short_addr = 0;
s = local->slots;
s->index = 0;
s->tx_controlee_index = -1;
s->ranging_index = 0;
s->rx_ant_pair = session->rx_ant_pair[0];
s->message_id = FIRA_MESSAGE_ID_CONTROL;
if (session->synchronised)
timeout_dtu = 2 * fira_session_get_block_duration_margin(
local, session);
else if (!access->duration_dtu)
timeout_dtu = -1;
else
timeout_dtu = access->duration_dtu -
session->params.round_duration_slots *
session->params.slot_duration_dtu;
frame = local->frames;
*frame = (struct mcps802154_access_frame){
.is_tx = false,
.rx = {
.info = {
.timestamp_dtu = access->timestamp_dtu,
.timeout_dtu = timeout_dtu,
.flags = MCPS802154_RX_INFO_TIMESTAMP_DTU,
.ant_pair_id = s->rx_ant_pair,
},
.frame_info_flags_request
= MCPS802154_RX_FRAME_INFO_TIMESTAMP_DTU,
},
};
return access;
}
struct mcps802154_access *fira_get_access(struct mcps802154_region *region,
u32 next_timestamp_dtu,
int next_in_region_dtu,
int region_duration_dtu)
{
struct fira_local *local = region_to_local(region);
struct fira_session *session;
if (!local->current_session) {
session = fira_session_next(
local, next_timestamp_dtu + local->llhw->anticip_dtu,
region_duration_dtu - next_in_region_dtu);
local->current_session = session;
} else {
session = local->current_session;
}
if (!session)
return NULL;
return fira_compute_access(local, session);
}
struct mcps802154_access *fira_compute_access(struct fira_local *local,
struct fira_session *session)
{
if (session->params.device_type == FIRA_DEVICE_TYPE_CONTROLLER)
return fira_access_controller(local, session);
else
return fira_access_controlee(local, session);
}
void fira_session_get_demand(struct fira_local *local,
struct fira_session *session,
struct mcps802154_region_demand *demand)
{
u32 base_timestamp_dtu = session->block_start_dtu +
fira_session_get_round_slot(session) *
session->params.slot_duration_dtu;
if (session->params.device_type == FIRA_DEVICE_TYPE_CONTROLLER) {
demand->timestamp_dtu = base_timestamp_dtu;
demand->duration_dtu =
(4 + 2 * session->current_controlees.size) *
session->params.slot_duration_dtu;
} else {
int block_duration_margin_dtu =
fira_session_get_block_duration_margin(local, session);
demand->timestamp_dtu =
base_timestamp_dtu - block_duration_margin_dtu;
demand->duration_dtu =
session->params.round_duration_slots *
session->params.slot_duration_dtu +
2 * block_duration_margin_dtu;
}
}