blob: 2ed37f244a884426105924d6e1c1638e04ba29c5 [file] [edit]
/*
* This file is part of the UWB stack for linux.
*
* Copyright (c) 2020-2022 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 <asm/unaligned.h>
#include <linux/errno.h>
#include <linux/ieee802154.h>
#include <linux/string.h>
#include <net/fira_region_nl.h>
#include <net/mcps802154_frame.h>
#include "fira_session.h"
#include "fira_access.h"
#include "fira_region_call.h"
#include "fira_trace.h"
#include "fira_sts.h"
static const struct nla_policy fira_call_nla_policy[FIRA_CALL_ATTR_MAX + 1] = {
[FIRA_CALL_ATTR_SESSION_ID] = { .type = NLA_U32 },
[FIRA_CALL_ATTR_SESSION_PARAMS] = { .type = NLA_NESTED },
[FIRA_CALL_ATTR_CONTROLEES] = { .type = NLA_NESTED_ARRAY },
};
static const struct nla_policy fira_session_param_nla_policy[FIRA_SESSION_PARAM_ATTR_MAX +
1] = {
[FIRA_SESSION_PARAM_ATTR_DEVICE_TYPE] =
NLA_POLICY_MAX(NLA_U8, FIRA_DEVICE_TYPE_CONTROLLER),
[FIRA_SESSION_PARAM_ATTR_DEVICE_ROLE] =
NLA_POLICY_MAX(NLA_U8, FIRA_DEVICE_ROLE_INITIATOR),
[FIRA_SESSION_PARAM_ATTR_RANGING_ROUND_USAGE] =
NLA_POLICY_MAX(NLA_U8, FIRA_RANGING_ROUND_USAGE_DSTWR),
[FIRA_SESSION_PARAM_ATTR_MULTI_NODE_MODE] =
NLA_POLICY_MAX(NLA_U8, FIRA_MULTI_NODE_MODE_MANY_TO_MANY),
[FIRA_SESSION_PARAM_ATTR_SHORT_ADDR] = { .type = NLA_U16 },
[FIRA_SESSION_PARAM_ATTR_DESTINATION_SHORT_ADDR] = { .type = NLA_U16 },
[FIRA_SESSION_PARAM_ATTR_INITIATION_TIME_MS] = { .type = NLA_U32 },
[FIRA_SESSION_PARAM_ATTR_SLOT_DURATION_RSTU] = { .type = NLA_U32 },
[FIRA_SESSION_PARAM_ATTR_BLOCK_DURATION_MS] = { .type = NLA_U32 },
[FIRA_SESSION_PARAM_ATTR_ROUND_DURATION_SLOTS] = { .type = NLA_U32 },
[FIRA_SESSION_PARAM_ATTR_BLOCK_STRIDE_LENGTH] =
NLA_POLICY_MAX(NLA_U32, FIRA_BLOCK_STRIDE_LEN_MAX),
[FIRA_SESSION_PARAM_ATTR_MAX_NUMBER_OF_MEASUREMENTS] = { .type = NLA_U32 },
[FIRA_SESSION_PARAM_ATTR_MAX_RR_RETRY] = { .type = NLA_U32 },
[FIRA_SESSION_PARAM_ATTR_ROUND_HOPPING] =
NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX),
[FIRA_SESSION_PARAM_ATTR_PRIORITY] =
NLA_POLICY_MAX(NLA_U8, FIRA_PRIORITY_MAX),
[FIRA_SESSION_PARAM_ATTR_RESULT_REPORT_PHASE] =
NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX),
[FIRA_SESSION_PARAM_ATTR_MR_AT_INITIATOR] =
NLA_POLICY_MAX(NLA_U8, FIRA_MEASUREMENT_REPORT_AT_INITIATOR),
[FIRA_SESSION_PARAM_ATTR_EMBEDDED_MODE] =
NLA_POLICY_MAX(NLA_U8, FIRA_EMBEDDED_MODE_NON_DEFERRED),
[FIRA_SESSION_PARAM_ATTR_IN_BAND_TERMINATION_ATTEMPT_COUNT] =
NLA_POLICY_RANGE(NLA_U32,
FIRA_IN_BAND_TERMINATION_ATTEMPT_COUNT_MIN,
FIRA_IN_BAND_TERMINATION_ATTEMPT_COUNT_MAX),
[FIRA_SESSION_PARAM_ATTR_CHANNEL_NUMBER] = { .type = NLA_U8 },
[FIRA_SESSION_PARAM_ATTR_PREAMBLE_CODE_INDEX] = { .type = NLA_U8 },
[FIRA_SESSION_PARAM_ATTR_RFRAME_CONFIG] =
NLA_POLICY_MAX(NLA_U8, FIRA_RFRAME_CONFIG_SP3),
[FIRA_SESSION_PARAM_ATTR_PRF_MODE] =
NLA_POLICY_MAX(NLA_U8, FIRA_PRF_MODE_HPRF_HIGH_RATE),
[FIRA_SESSION_PARAM_ATTR_PREAMBLE_DURATION] =
NLA_POLICY_MAX(NLA_U8, FIRA_PREAMBULE_DURATION_64),
[FIRA_SESSION_PARAM_ATTR_SFD_ID] =
NLA_POLICY_MAX(NLA_U8, FIRA_SFD_ID_4),
[FIRA_SESSION_PARAM_ATTR_NUMBER_OF_STS_SEGMENTS] =
NLA_POLICY_MAX(NLA_U8, FIRA_STS_SEGMENTS_4),
[FIRA_SESSION_PARAM_ATTR_PSDU_DATA_RATE] =
NLA_POLICY_MAX(NLA_U8, FIRA_PSDU_DATA_RATE_31M2),
[FIRA_SESSION_PARAM_ATTR_BPRF_PHR_DATA_RATE] =
NLA_POLICY_MAX(NLA_U8, FIRA_PHR_DATA_RATE_6M81),
[FIRA_SESSION_PARAM_ATTR_MAC_FCS_TYPE] =
NLA_POLICY_MAX(NLA_U8, FIRA_MAC_FCS_TYPE_CRC_32),
[FIRA_SESSION_PARAM_ATTR_TX_ADAPTIVE_PAYLOAD_POWER] =
NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX),
[FIRA_SESSION_PARAM_ATTR_MEASUREMENT_SEQUENCE] = { .type = NLA_NESTED_ARRAY },
[FIRA_SESSION_PARAM_ATTR_STS_CONFIG] =
NLA_POLICY_MAX(NLA_U8, FIRA_STS_MODE_PROVISIONED_INDIVIDUAL_KEY),
[FIRA_SESSION_PARAM_ATTR_SUB_SESSION_ID] = { .type = NLA_U32 },
[FIRA_SESSION_PARAM_ATTR_VUPPER64] =
NLA_POLICY_EXACT_LEN(FIRA_VUPPER64_SIZE),
[FIRA_SESSION_PARAM_ATTR_SESSION_KEY] = {
.type = NLA_BINARY, .len = FIRA_KEY_SIZE_MAX, },
[FIRA_SESSION_PARAM_ATTR_SUB_SESSION_KEY] = {
.type = NLA_BINARY, .len = FIRA_KEY_SIZE_MAX, },
[FIRA_SESSION_PARAM_ATTR_KEY_ROTATION] =
NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX),
[FIRA_SESSION_PARAM_ATTR_KEY_ROTATION_RATE] = { .type = NLA_U8 },
[FIRA_SESSION_PARAM_ATTR_AOA_RESULT_REQ] =
NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX),
[FIRA_SESSION_PARAM_ATTR_REPORT_TOF] =
NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX),
[FIRA_SESSION_PARAM_ATTR_REPORT_AOA_AZIMUTH] =
NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX),
[FIRA_SESSION_PARAM_ATTR_REPORT_AOA_ELEVATION] =
NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX),
[FIRA_SESSION_PARAM_ATTR_REPORT_AOA_FOM] =
NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX),
[FIRA_SESSION_PARAM_ATTR_REPORT_RSSI] =
NLA_POLICY_MAX(NLA_U8, FIRA_RSSI_REPORT_AVERAGE),
[FIRA_SESSION_PARAM_ATTR_DATA_VENDOR_OUI] = { .type = NLA_U32 },
[FIRA_SESSION_PARAM_ATTR_DATA_PAYLOAD] = {
.type = NLA_BINARY, .len = FIRA_DATA_PAYLOAD_SIZE_MAX, },
[FIRA_SESSION_PARAM_ATTR_DIAGNOSTICS] =
NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX),
[FIRA_SESSION_PARAM_ATTR_DIAGNOSTICS_FRAME_REPORTS_FIELDS] = {.type = NLA_U32},
[FIRA_SESSION_PARAM_ATTR_STS_LENGTH] =
NLA_POLICY_MAX(NLA_U8, FIRA_STS_LENGTH_128),
[FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_CONFIG] =
NLA_POLICY_MAX(NLA_U8, FIRA_RANGE_DATA_NTF_PROXIMITY_AND_AOA_CROSSING),
[FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_PROXIMITY_NEAR_MM] =
{ .type = NLA_U32 },
[FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_PROXIMITY_FAR_MM] =
{ .type = NLA_U32 },
[FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_LOWER_BOUND_AOA_AZIMUTH_2PI] =
NLA_POLICY_RANGE(NLA_S16,
FIRA_SESSION_DATA_NTF_LOWER_BOUND_AOA_AZIMUTH_2PI_MIN,
FIRA_SESSION_DATA_NTF_LOWER_BOUND_AOA_AZIMUTH_2PI_MAX),
[FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_UPPER_BOUND_AOA_AZIMUTH_2PI] =
NLA_POLICY_RANGE(NLA_S16,
FIRA_SESSION_DATA_NTF_UPPER_BOUND_AOA_AZIMUTH_2PI_MIN,
FIRA_SESSION_DATA_NTF_UPPER_BOUND_AOA_AZIMUTH_2PI_MAX),
[FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_LOWER_BOUND_AOA_ELEVATION_2PI] =
NLA_POLICY_RANGE(NLA_S16,
FIRA_SESSION_DATA_NTF_LOWER_BOUND_AOA_ELEVATION_2PI_MIN,
FIRA_SESSION_DATA_NTF_LOWER_BOUND_AOA_ELEVATION_2PI_MAX),
[FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_UPPER_BOUND_AOA_ELEVATION_2PI] =
NLA_POLICY_RANGE(NLA_S16,
FIRA_SESSION_DATA_NTF_UPPER_BOUND_AOA_ELEVATION_2PI_MIN,
FIRA_SESSION_DATA_NTF_UPPER_BOUND_AOA_ELEVATION_2PI_MAX),
};
/**
* fira_get_state_by_session_id() - Get state of the session.
* @local: FiRa context.
* @session_id: FiRa session id.
*
* Return: current session state.
*/
static enum fira_session_state_id
fira_get_state_by_session_id(struct fira_local *local, u32 session_id)
{
struct fira_session *session =
fira_get_session_by_session_id(local, session_id);
if (session)
return fira_session_get_state_id(session);
return FIRA_SESSION_STATE_ID_DEINIT;
}
/**
* fira_session_init() - Initialize FiRa session.
* @local: FiRa context.
* @session_id: FiRa session id.
*
* Return: 0 or error.
*/
static int fira_session_init(struct fira_local *local, u32 session_id)
{
struct fira_session *session =
fira_get_session_by_session_id(local, session_id);
if (session)
return -EBUSY;
session = fira_session_new(local, session_id);
if (!session)
return -ENOMEM;
return 0;
}
/**
* fira_session_start() - Start FiRa session.
* @local: FiRa context.
* @session_id: FiRa session id.
* @info: Request information.
*
* Return: 0 or error.
*/
static int fira_session_start(struct fira_local *local, u32 session_id,
const struct genl_info *info)
{
struct fira_session *session;
session = fira_get_session_by_session_id(local, session_id);
if (!session)
return -ENOENT;
return fira_session_fsm_start(local, session, info);
}
/**
* fira_session_stop() - Stop FiRa session.
* @local: FiRa context.
* @session_id: FiRa session id.
*
* Return: 0 or error.
*/
static int fira_session_stop(struct fira_local *local, u32 session_id)
{
struct fira_session *session;
session = fira_get_session_by_session_id(local, session_id);
if (!session)
return -ENOENT;
return fira_session_fsm_stop(local, session);
}
/**
* fira_session_deinit() - Deinitialize FiRa session.
* @local: FiRa context.
* @session_id: FiRa session id.
*
* Return: 0 or error.
*/
static int fira_session_deinit(struct fira_local *local, u32 session_id)
{
struct fira_session *session =
fira_get_session_by_session_id(local, session_id);
if (!session)
return -ENOENT;
if (fira_session_is_active(session))
return -EBUSY;
fira_session_free(local, session);
return 0;
}
/**
* fira_session_params_set_measurement_sequence_step() - Retrieve a
* measurement sequence step from a NL message and store it in the session
* parameters.
* @params: The parameters contained in the NL message.
* @info: NL context info.
* @step: The step where to store the information.
*
* Return: 0 or error.
*/
static int fira_session_params_set_measurement_sequence_step(
const struct nlattr *params, const struct genl_info *info,
struct fira_measurement_sequence_step *step)
{
#define STEP_ATTR(x) FIRA_SESSION_PARAM_MEAS_SEQ_STEP_ATTR_##x
#define ASR_ATTR(x) \
FIRA_SESSION_PARAM_MEAS_SEQ_STEP_RX_ANT_SETS_RANGING_ATTR_##x
static const struct nla_policy meas_seq_step_policy[STEP_ATTR(MAX) +
1] = {
[STEP_ATTR(MEASUREMENT_TYPE)] = { .type = NLA_U8 },
[STEP_ATTR(N_MEASUREMENTS)] = { .type = NLA_U8 },
[STEP_ATTR(RX_ANT_SET_NONRANGING)] = { .type = NLA_U8 },
[STEP_ATTR(RX_ANT_SETS_RANGING)] = { .type = NLA_NESTED },
[STEP_ATTR(TX_ANT_SET_NONRANGING)] = { .type = NLA_U8 },
[STEP_ATTR(TX_ANT_SET_RANGING)] = { .type = NLA_U8 },
};
static const struct nla_policy
rx_ant_sets_ranging_policy[ASR_ATTR(MAX) + 1] = {
[ASR_ATTR(0)] = { .type = NLA_U8 },
[ASR_ATTR(1)] = { .type = NLA_U8 },
};
struct nlattr *step_attrs[STEP_ATTR(MAX) + 1];
struct nlattr *rx_ant_sets_attrs[ASR_ATTR(MAX) + 1];
int r = 0;
u8 n_measurements = 0;
enum fira_measurement_type type = __FIRA_MEASUREMENT_TYPE_AFTER_LAST;
r = nla_parse_nested(step_attrs, STEP_ATTR(MAX), params,
meas_seq_step_policy, info->extack);
/* LCOV_EXCL_START */
if (r)
return r;
/* LCOV_EXCL_STOP */
memset(step, 0, sizeof(struct fira_measurement_sequence_step));
if (!step_attrs[STEP_ATTR(MEASUREMENT_TYPE)] ||
!step_attrs[STEP_ATTR(N_MEASUREMENTS)])
return -EINVAL;
type = nla_get_u8(step_attrs[STEP_ATTR(MEASUREMENT_TYPE)]);
if (type >= __FIRA_MEASUREMENT_TYPE_AFTER_LAST)
return -EINVAL;
step->type = type;
n_measurements = nla_get_u8(step_attrs[STEP_ATTR(N_MEASUREMENTS)]);
if (n_measurements == 0)
return -EINVAL;
step->n_measurements = n_measurements;
#define GET_ANTENNA(nl_attr, ant_set) \
(ant_set) = !(nl_attr) ? -1 : nla_get_u8(nl_attr);
GET_ANTENNA(step_attrs[STEP_ATTR(RX_ANT_SET_NONRANGING)],
step->rx_ant_set_nonranging);
GET_ANTENNA(step_attrs[STEP_ATTR(TX_ANT_SET_NONRANGING)],
step->tx_ant_set_nonranging);
GET_ANTENNA(step_attrs[STEP_ATTR(TX_ANT_SET_RANGING)],
step->tx_ant_set_ranging);
if (!step_attrs[STEP_ATTR(RX_ANT_SETS_RANGING)])
return -EINVAL;
r = nla_parse_nested(rx_ant_sets_attrs, ASR_ATTR(MAX),
step_attrs[STEP_ATTR(RX_ANT_SETS_RANGING)],
rx_ant_sets_ranging_policy, info->extack);
/* LCOV_EXCL_START */
if (r)
return r;
/* LCOV_EXCL_STOP */
GET_ANTENNA(rx_ant_sets_attrs[ASR_ATTR(0)],
step->rx_ant_sets_ranging[0]);
GET_ANTENNA(rx_ant_sets_attrs[ASR_ATTR(1)],
step->rx_ant_sets_ranging[1]);
#undef GET_ANTENNA
#undef STEP_ATTR
#undef ASR_ATTR
if (step->rx_ant_sets_ranging[0] == -1)
step->rx_ant_sets_ranging[0] = 0;
if (step->rx_ant_sets_ranging[1] == -1)
step->rx_ant_sets_ranging[1] = 0;
if (step->rx_ant_set_nonranging == -1)
step->rx_ant_set_nonranging = step->rx_ant_sets_ranging[0];
if (step->tx_ant_set_ranging == -1)
step->tx_ant_set_ranging = 0;
if (step->tx_ant_set_nonranging == -1)
step->tx_ant_set_nonranging = step->tx_ant_set_ranging;
return 0;
}
/**
* fira_session_params_set_measurement_sequence() - Retrieve the measurement
* schedule from a NL message and store it in the session parameters.
* @params: The parameters contained in the NL message.
* @info: NL context info.
* @meas_seq: The measurement sequence where to store the information.
*
* Return: 0 or error.
*/
static int fira_session_params_set_measurement_sequence(
const struct nlattr *params, const struct genl_info *info,
struct fira_measurement_sequence *meas_seq)
{
struct nlattr *request;
int r, rem = 0;
size_t n_steps = 0;
nla_for_each_nested (request, params, rem) {
if (n_steps >= FIRA_MEASUREMENT_SEQUENCE_STEP_MAX)
return -EINVAL;
r = fira_session_params_set_measurement_sequence_step(
request, info, &meas_seq->steps[n_steps]);
if (r)
return r;
n_steps++;
}
if (!n_steps)
return -EINVAL;
meas_seq->n_steps = n_steps;
return 0;
}
/**
* fira_session_set_parameters() - Set FiRa session parameters.
* @local: FiRa context.
* @session_id: FiRa session id.
* @params: Nested attribute containing session parameters.
* @info: Request information.
*
* Return: 0 or error.
*/
static int fira_session_set_parameters(struct fira_local *local, u32 session_id,
const struct nlattr *params,
const struct genl_info *info)
{
struct nlattr *attrs[FIRA_SESSION_PARAM_ATTR_MAX + 1];
struct fira_session *session;
struct fira_session_params *p;
struct fira_measurement_sequence meas_seq = {};
int r;
if (!params)
return -EINVAL;
session = fira_get_session_by_session_id(local, session_id);
if (!session)
return -ENOENT;
r = nla_parse_nested(attrs, FIRA_SESSION_PARAM_ATTR_MAX, params,
fira_session_param_nla_policy, info->extack);
if (r)
return r;
/* Check attribute validity. */
if (attrs[FIRA_SESSION_PARAM_ATTR_MEASUREMENT_SEQUENCE]) {
r = fira_session_params_set_measurement_sequence(
attrs[FIRA_SESSION_PARAM_ATTR_MEASUREMENT_SEQUENCE],
info, &meas_seq);
if (r)
return r;
}
r = fira_session_fsm_check_parameters(session, attrs);
if (r)
return r;
p = &session->params;
#define P(attr, member, type, conv) \
do { \
int x; \
if (attrs[FIRA_SESSION_PARAM_ATTR_##attr]) { \
x = nla_get_##type( \
attrs[FIRA_SESSION_PARAM_ATTR_##attr]); \
p->member = conv; \
} \
} while (0)
#define PMEMCPY(attr, member) \
do { \
if (attrs[FIRA_SESSION_PARAM_ATTR_##attr]) { \
struct nlattr *attr = \
attrs[FIRA_SESSION_PARAM_ATTR_##attr]; \
memcpy(p->member, nla_data(attr), nla_len(attr)); \
} \
} while (0)
#define PMEMNCPY(attr, member, size) \
do { \
if (attrs[FIRA_SESSION_PARAM_ATTR_##attr]) { \
struct nlattr *attr = \
attrs[FIRA_SESSION_PARAM_ATTR_##attr]; \
int len = nla_len(attr); \
memcpy(p->member, nla_data(attr), len); \
p->size = len; \
} \
} while (0)
/* Main session parameters. */
P(DEVICE_TYPE, device_type, u8, x);
P(RANGING_ROUND_USAGE, ranging_round_usage, u8, x);
P(MULTI_NODE_MODE, multi_node_mode, u8, x);
P(SHORT_ADDR, short_addr, u16, x);
P(DESTINATION_SHORT_ADDR, controller_short_addr, u16, x);
/* Timings parameters. */
P(INITIATION_TIME_MS, initiation_time_ms, u32, x);
P(SLOT_DURATION_RSTU, slot_duration_dtu, u32,
x * local->llhw->rstu_dtu);
P(BLOCK_DURATION_MS, block_duration_dtu, u32,
x * (local->llhw->dtu_freq_hz / 1000));
P(ROUND_DURATION_SLOTS, round_duration_slots, u32, x);
/* Behaviour parameters. */
P(BLOCK_STRIDE_LENGTH, block_stride_len, u32, x);
P(MAX_NUMBER_OF_MEASUREMENTS, max_number_of_measurements, u32, x);
P(MAX_RR_RETRY, max_rr_retry, u32, x);
P(ROUND_HOPPING, round_hopping, u8, !!x);
P(PRIORITY, priority, u8, x);
P(RESULT_REPORT_PHASE, result_report_phase, u8, !!x);
/* Radio parameters. */
P(CHANNEL_NUMBER, channel_number, u8, x);
P(PREAMBLE_CODE_INDEX, preamble_code_index, u8, x);
P(RFRAME_CONFIG, rframe_config, u8, x);
P(PREAMBLE_DURATION, preamble_duration, u8, x);
P(SFD_ID, sfd_id, u8, x);
P(NUMBER_OF_STS_SEGMENTS, number_of_sts_segments, u8, x);
P(PSDU_DATA_RATE, psdu_data_rate, u8, x);
P(MAC_FCS_TYPE, mac_fcs_type, u8, x);
P(PRF_MODE, prf_mode, u8, x);
P(BPRF_PHR_DATA_RATE, phr_data_rate, u8, x);
/* Measurement Sequence */
if (attrs[FIRA_SESSION_PARAM_ATTR_MEASUREMENT_SEQUENCE]) {
p->meas_seq = meas_seq;
session->measurements.reset = true;
}
/* STS and crypto parameters. */
P(STS_CONFIG, sts_config, u8, x);
PMEMCPY(VUPPER64, vupper64);
if (attrs[FIRA_SESSION_PARAM_ATTR_SESSION_KEY]) {
struct nlattr *attr =
attrs[FIRA_SESSION_PARAM_ATTR_SESSION_KEY];
memcpy(p->session_key, nla_data(attr), nla_len(attr));
p->session_key_len = nla_len(attr);
}
P(SUB_SESSION_ID, sub_session_id, u32, x);
if (attrs[FIRA_SESSION_PARAM_ATTR_SUB_SESSION_KEY]) {
struct nlattr *attr =
attrs[FIRA_SESSION_PARAM_ATTR_SUB_SESSION_KEY];
memcpy(p->sub_session_key, nla_data(attr), nla_len(attr));
p->sub_session_key_len = nla_len(attr);
}
P(KEY_ROTATION, key_rotation, u8, !!x);
P(KEY_ROTATION_RATE, key_rotation_rate, u8, x);
/* Report parameters. */
P(AOA_RESULT_REQ, aoa_result_req, u8, !!x);
P(REPORT_TOF, report_tof, u8, !!x);
P(REPORT_AOA_AZIMUTH, report_aoa_azimuth, u8, !!x);
P(REPORT_AOA_ELEVATION, report_aoa_elevation, u8, !!x);
P(REPORT_AOA_FOM, report_aoa_fom, u8, !!x);
P(REPORT_RSSI, report_rssi, u8, x);
/* Custom data */
P(DATA_VENDOR_OUI, data_vendor_oui, u32, x);
PMEMNCPY(DATA_PAYLOAD, data_payload, data_payload_len);
/* Increment payload sequence number if a new data is received. */
if (attrs[FIRA_SESSION_PARAM_ATTR_DATA_PAYLOAD])
p->data_payload_seq++;
/* Diagnostics */
P(DIAGNOSTICS, report_diagnostics, u8, x);
P(DIAGNOSTICS_FRAME_REPORTS_FIELDS, diagnostic_report_flags, u32, x);
/* Misc */
P(STS_LENGTH, sts_length, u8, x);
P(RANGE_DATA_NTF_CONFIG, range_data_ntf_config, u8, x);
P(RANGE_DATA_NTF_PROXIMITY_NEAR_MM, range_data_ntf_proximity_near_rctu,
u32, fira_mm_to_rctu(local, x));
P(RANGE_DATA_NTF_PROXIMITY_FAR_MM, range_data_ntf_proximity_far_rctu,
u32, fira_mm_to_rctu(local, x));
P(RANGE_DATA_NTF_LOWER_BOUND_AOA_AZIMUTH_2PI,
range_data_ntf_lower_bound_aoa_azimuth_2pi, s16, x);
P(RANGE_DATA_NTF_UPPER_BOUND_AOA_AZIMUTH_2PI,
range_data_ntf_upper_bound_aoa_azimuth_2pi, s16, x);
P(RANGE_DATA_NTF_LOWER_BOUND_AOA_ELEVATION_2PI,
range_data_ntf_lower_bound_aoa_elevation_2pi, s16, x);
P(RANGE_DATA_NTF_UPPER_BOUND_AOA_ELEVATION_2PI,
range_data_ntf_upper_bound_aoa_elevation_2pi, s16, x);
#undef PMEMNCPY
#undef PMEMCPY
#undef P
fira_session_fsm_parameters_updated(local, session);
return 0;
}
/**
* fira_session_get_state() - Get state of the session.
* @local: FiRa context.
* @session_id: FiRa session id.
*
* Return: 0 or error.
*/
static int fira_session_get_state(struct fira_local *local, u32 session_id)
{
struct sk_buff *msg;
enum fira_session_state_id state;
state = fira_get_state_by_session_id(local, session_id);
msg = mcps802154_region_call_alloc_reply_skb(
local->llhw, &local->region, FIRA_CALL_SESSION_GET_STATE,
NLMSG_DEFAULT_SIZE);
if (!msg)
return -ENOMEM;
if (nla_put_u32(msg, FIRA_CALL_ATTR_SESSION_ID, session_id))
goto nla_put_failure;
if (nla_put_u8(msg, FIRA_CALL_ATTR_SESSION_STATE, state))
goto nla_put_failure;
return mcps802154_region_call_reply(local->llhw, msg);
nla_put_failure:
kfree_skb(msg);
return -ENOBUFS;
}
/**
* fira_session_params_get_measurement_sequence_step() - Retrieve a
* measurement sequence step in a NL message.
* @step: The measurement sequence step to add to the message.
* @msg_buf: NL message buffer.
* @idx: Index of the step.
*
* Return: 0 or error.
*/
static int fira_session_params_get_measurement_sequence_step(
const struct fira_measurement_sequence_step *step,
struct sk_buff *msg_buf, size_t idx)
{
#define P(attr, member, type, conv) \
do { \
type x = member; \
if (nla_put_##type(msg_buf, attr, conv)) \
return -ENOBUFS; \
} while (0)
#define STEP_ATTR(x) FIRA_SESSION_PARAM_MEAS_SEQ_STEP_ATTR_##x
#define ASR_ATTR(x) \
FIRA_SESSION_PARAM_MEAS_SEQ_STEP_RX_ANT_SETS_RANGING_ATTR_##x
struct nlattr *step_params = NULL;
struct nlattr *rx_ant_sets_ranging_params = NULL;
step_params = nla_nest_start(msg_buf, idx);
if (!step_params)
return -ENOBUFS;
P(STEP_ATTR(MEASUREMENT_TYPE), step->type, u8, x);
P(STEP_ATTR(N_MEASUREMENTS), step->n_measurements, u8, x);
P(STEP_ATTR(RX_ANT_SET_NONRANGING), step->rx_ant_set_nonranging, u8, x);
rx_ant_sets_ranging_params =
nla_nest_start(msg_buf, STEP_ATTR(RX_ANT_SETS_RANGING));
if (!rx_ant_sets_ranging_params)
return -ENOBUFS;
P(ASR_ATTR(0), step->rx_ant_sets_ranging[0], u8, x);
P(ASR_ATTR(1), step->rx_ant_sets_ranging[1], u8, x);
nla_nest_end(msg_buf, rx_ant_sets_ranging_params);
P(STEP_ATTR(TX_ANT_SET_NONRANGING), step->tx_ant_set_nonranging, u8, x);
P(STEP_ATTR(TX_ANT_SET_RANGING), step->tx_ant_set_ranging, u8, x);
nla_nest_end(msg_buf, step_params);
#undef STEP_ATTR
#undef ASR_ATTR
#undef P
return 0;
}
/**
* fira_session_params_get_measurement_sequence() - Retrieve a
* measurement sequence in a NL message.
* @meas_seq: The measurement sequence to add to the message.
* @msg_buf: NL message buffer.
*
* Return: 0 or error.
*/
static int fira_session_params_get_measurement_sequence(
const struct fira_measurement_sequence *meas_seq,
struct sk_buff *msg_buf)
{
struct nlattr *meas_seq_params = NULL;
size_t i;
meas_seq_params = nla_nest_start(
msg_buf, FIRA_SESSION_PARAM_ATTR_MEASUREMENT_SEQUENCE);
if (!meas_seq_params)
return -ENOBUFS;
for (i = 0; i < meas_seq->n_steps; ++i) {
int r = 0;
r = fira_session_params_get_measurement_sequence_step(
meas_seq->steps + i, msg_buf, i);
if (r)
return r;
}
nla_nest_end(msg_buf, meas_seq_params);
return 0;
}
/**
* fira_session_get_parameters() - Get FiRa session parameters.
* @local: FiRa context.
* @session_id: FiRa session id.
*
* Return: 0 or error.
*/
static int fira_session_get_parameters(struct fira_local *local, u32 session_id)
{
const struct fira_session *session;
const struct fira_session_params *p;
struct sk_buff *msg;
struct nlattr *params;
session = fira_get_session_by_session_id(local, session_id);
if (!session)
return -ENOENT;
p = &session->params;
msg = mcps802154_region_call_alloc_reply_skb(
local->llhw, &local->region, FIRA_CALL_SESSION_GET_PARAMS,
NLMSG_DEFAULT_SIZE);
if (!msg)
return -ENOMEM;
if (nla_put_u32(msg, FIRA_CALL_ATTR_SESSION_ID, session->id))
goto nla_put_failure;
params = nla_nest_start(msg, FIRA_CALL_ATTR_SESSION_PARAMS);
if (!params)
goto nla_put_failure;
#define P(attr, member, type, conv) \
do { \
type x = p->member; \
if (nla_put_##type(msg, FIRA_SESSION_PARAM_ATTR_##attr, conv)) \
goto nla_put_failure; \
} while (0)
#define PMEMCPY(attr, member) \
do { \
if (nla_put(msg, FIRA_SESSION_PARAM_ATTR_##attr, \
sizeof(p->member), p->member)) \
goto nla_put_failure; \
} while (0)
/* Main session parameters. */
P(DEVICE_TYPE, device_type, u8, x);
P(RANGING_ROUND_USAGE, ranging_round_usage, u8, x);
P(MULTI_NODE_MODE, multi_node_mode, u8, x);
if (p->short_addr != IEEE802154_ADDR_SHORT_BROADCAST)
P(SHORT_ADDR, short_addr, u16, x);
if (p->controller_short_addr != IEEE802154_ADDR_SHORT_BROADCAST)
P(DESTINATION_SHORT_ADDR, controller_short_addr, u16, x);
/* Timings parameters. */
P(INITIATION_TIME_MS, initiation_time_ms, u32, x);
P(SLOT_DURATION_RSTU, slot_duration_dtu, u32,
x / local->llhw->rstu_dtu);
P(BLOCK_DURATION_MS, block_duration_dtu, u32,
x / (local->llhw->dtu_freq_hz / 1000));
P(ROUND_DURATION_SLOTS, round_duration_slots, u32, x);
/* Behaviour parameters. */
P(BLOCK_STRIDE_LENGTH, block_stride_len, u32, x);
P(MAX_NUMBER_OF_MEASUREMENTS, max_number_of_measurements, u32, x);
P(MAX_RR_RETRY, max_rr_retry, u32, x);
P(ROUND_HOPPING, round_hopping, u8, !!x);
P(PRIORITY, priority, u8, x);
P(RESULT_REPORT_PHASE, result_report_phase, u8, !!x);
/* Radio parameters. */
if (p->channel_number)
P(CHANNEL_NUMBER, channel_number, u8, x);
if (p->preamble_code_index)
P(PREAMBLE_CODE_INDEX, preamble_code_index, u8, x);
P(RFRAME_CONFIG, rframe_config, u8, x);
P(PREAMBLE_DURATION, preamble_duration, u8, x);
P(SFD_ID, sfd_id, u8, x);
P(NUMBER_OF_STS_SEGMENTS, number_of_sts_segments, u8, x);
P(PSDU_DATA_RATE, psdu_data_rate, u8, x);
P(PRF_MODE, prf_mode, u8, x);
P(BPRF_PHR_DATA_RATE, phr_data_rate, u8, x);
P(MAC_FCS_TYPE, mac_fcs_type, u8, x);
/* Measurement Sequence */
if (fira_session_params_get_measurement_sequence(
&session->measurements.sequence, msg))
goto nla_put_failure;
/* STS and crypto parameters. */
PMEMCPY(VUPPER64, vupper64);
P(SUB_SESSION_ID, sub_session_id, u32, x);
P(STS_CONFIG, sts_config, u8, x);
P(KEY_ROTATION, key_rotation, u8, x);
P(KEY_ROTATION_RATE, key_rotation_rate, u8, x);
/* Report parameters. */
P(AOA_RESULT_REQ, aoa_result_req, u8, !!x);
P(REPORT_TOF, report_tof, u8, !!x);
P(REPORT_AOA_AZIMUTH, report_aoa_azimuth, u8, !!x);
P(REPORT_AOA_ELEVATION, report_aoa_elevation, u8, !!x);
P(REPORT_AOA_FOM, report_aoa_fom, u8, !!x);
P(REPORT_RSSI, report_rssi, u8, !!x);
/* Custom data */
if (p->data_vendor_oui)
P(DATA_VENDOR_OUI, data_vendor_oui, u32, x);
/* Diagnostics */
P(DIAGNOSTICS, report_diagnostics, u8, x);
P(DIAGNOSTICS_FRAME_REPORTS_FIELDS, diagnostic_report_flags, u32, x);
/* Misc */
P(STS_LENGTH, sts_length, u8, x);
P(RANGE_DATA_NTF_CONFIG, range_data_ntf_config, u8, x);
P(RANGE_DATA_NTF_PROXIMITY_NEAR_MM, range_data_ntf_proximity_near_rctu,
u32, fira_rctu_to_mm((s64)local->llhw->dtu_freq_hz * local->llhw->dtu_rctu, x));
P(RANGE_DATA_NTF_PROXIMITY_FAR_MM, range_data_ntf_proximity_far_rctu,
u32, fira_rctu_to_mm((s64)local->llhw->dtu_freq_hz * local->llhw->dtu_rctu, x));
P(RANGE_DATA_NTF_LOWER_BOUND_AOA_AZIMUTH_2PI,
range_data_ntf_lower_bound_aoa_azimuth_2pi, s16, x);
P(RANGE_DATA_NTF_UPPER_BOUND_AOA_AZIMUTH_2PI,
range_data_ntf_upper_bound_aoa_azimuth_2pi, s16, x);
P(RANGE_DATA_NTF_LOWER_BOUND_AOA_ELEVATION_2PI,
range_data_ntf_lower_bound_aoa_elevation_2pi, s16, x);
P(RANGE_DATA_NTF_UPPER_BOUND_AOA_ELEVATION_2PI,
range_data_ntf_upper_bound_aoa_elevation_2pi, s16, x);
#undef P
#undef PMEMCPY
nla_nest_end(msg, params);
return mcps802154_region_call_reply(local->llhw, msg);
nla_put_failure:
kfree_skb(msg);
return -ENOBUFS;
}
/**
* fira_manage_controlees() - Manage controlees.
* @local: FiRa context.
* @call_id: FiRa call id.
* @session_id: FiRa session id.
* @params: Nested attribute containing controlee parameters.
* @info: Request information.
* Return: 0 or error.
*/
static int fira_manage_controlees(struct fira_local *local,
enum fira_call call_id, u32 session_id,
const struct nlattr *params,
const struct genl_info *info)
{
static const struct nla_policy new_controlee_nla_policy[FIRA_CALL_CONTROLEE_ATTR_MAX +
1] = {
[FIRA_CALL_CONTROLEE_ATTR_SHORT_ADDR] = { .type = NLA_U16 },
[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_ID] = { .type = NLA_U32 },
[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_KEY] = { .type = NLA_BINARY,
.len = FIRA_KEY_SIZE_MAX },
};
struct nlattr *request;
struct nlattr *attrs[FIRA_CALL_CONTROLEE_ATTR_MAX + 1];
int r, rem, i, slot_duration_us, n_controlees = 0;
struct fira_session *session;
struct fira_controlee *controlee = NULL, *tmp_controlee;
bool is_active;
struct list_head controlees;
if (!params)
return -EINVAL;
session = fira_get_session_by_session_id(local, session_id);
if (!session)
return -ENOENT;
INIT_LIST_HEAD(&controlees);
nla_for_each_nested (request, params, rem) {
if (n_controlees >= FIRA_CONTROLEES_MAX) {
r = -EINVAL;
goto end;
}
r = nla_parse_nested(attrs, FIRA_CALL_CONTROLEE_ATTR_MAX,
request, new_controlee_nla_policy,
info->extack);
if (r)
goto end;
controlee = kzalloc(sizeof(struct fira_controlee), GFP_KERNEL);
if (!controlee) {
r = -ENOMEM;
goto end;
}
if (!attrs[FIRA_CALL_CONTROLEE_ATTR_SHORT_ADDR] ||
(!attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_ID] &&
attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_KEY])) {
kfree(controlee);
r = -EINVAL;
goto end;
}
controlee->short_addr = nla_get_le16(
attrs[FIRA_CALL_CONTROLEE_ATTR_SHORT_ADDR]);
if (attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_ID]) {
if (call_id == FIRA_CALL_DEL_CONTROLEE) {
kfree(controlee);
r = -EINVAL;
goto end;
}
controlee->sub_session = true;
controlee->sub_session_id = nla_get_u32(
attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_ID]);
if (attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_KEY]) {
memcpy(controlee->sub_session_key,
nla_data(
attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_KEY]),
nla_len(attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_KEY]));
controlee->sub_session_key_len = nla_len(
attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_KEY]);
}
} else {
controlee->sub_session = false;
}
controlee->range_data_ntf_status = FIRA_RANGE_DATA_NTF_NONE;
controlee->state = FIRA_CONTROLEE_STATE_RUNNING;
/* Check and reject a duplication of short_addr. */
list_for_each_entry (tmp_controlee, &controlees, entry) {
if (controlee->short_addr ==
tmp_controlee->short_addr) {
kfree(controlee);
r = -EINVAL;
goto end;
}
}
list_add_tail(&controlee->entry, &controlees);
n_controlees++;
}
if (list_empty(&controlees)) {
r = -EINVAL;
goto end;
}
/*
* Be careful, active is not equal to
* 'local->current_session == session'.
*/
is_active = fira_session_is_active(session);
if (is_active) {
/* If unicast refuse to add more than one controlee. */
switch (call_id) {
case FIRA_CALL_NEW_CONTROLEE:
if (session->params.multi_node_mode ==
FIRA_MULTI_NODE_MODE_UNICAST) {
r = -EPERM;
goto end;
}
break;
case FIRA_CALL_SET_CONTROLEE:
r = -EBUSY;
goto end;
default:
break;
}
}
slot_duration_us = (session->params.slot_duration_dtu * 1000) /
(local->llhw->dtu_freq_hz / 1000);
switch (call_id) {
case FIRA_CALL_SET_CONTROLEE:
r = fira_session_set_controlees(local, session, &controlees,
slot_duration_us, n_controlees);
break;
case FIRA_CALL_DEL_CONTROLEE:
if (n_controlees > 1) {
r = -EINVAL;
goto end;
} else {
/* 'controlee' points the unique entry in the list. */
r = fira_session_del_controlee(session, controlee,
is_active);
}
break;
/* FIRA_CALL_NEW_CONTROLEE. */
default:
if (n_controlees > 1) {
r = -EINVAL;
goto end;
} else {
/* 'controlee' points the unique entry in the list. */
r = fira_session_new_controlee(local, session,
controlee,
slot_duration_us,
is_active);
}
}
if (r)
goto end;
if (!is_active && local->llhw->rx_ctx_size) {
for (i = 0; i < session->n_current_controlees; i++) {
memset(session->rx_ctx[i], 0, local->llhw->rx_ctx_size);
}
}
fira_session_fsm_controlee_list_updated(local, session);
end:
list_for_each_entry_safe (controlee, tmp_controlee, &controlees,
entry) {
kfree(controlee);
}
return r;
}
/**
* fira_session_get_controlees() - Get list of controlees.
* @local: FiRa context.
* @session_id: FiRa session id.
* @info: Request information.
*
* Return: 0 or error.
*/
static int fira_session_get_controlees(struct fira_local *local, u32 session_id,
const struct genl_info *info)
{
const struct fira_session *session;
struct sk_buff *msg;
struct nlattr *controlees, *controlee_attr;
struct fira_controlee *controlee;
session = fira_get_session_by_session_id(local, session_id);
if (!session)
return -ENOENT;
msg = mcps802154_region_call_alloc_reply_skb(local->llhw,
&local->region,
FIRA_CALL_GET_CONTROLEES,
NLMSG_DEFAULT_SIZE);
if (!msg)
return -ENOMEM;
if (nla_put_u32(msg, FIRA_CALL_ATTR_SESSION_ID, session->id))
goto nla_put_failure;
controlees = nla_nest_start(msg, FIRA_CALL_ATTR_CONTROLEES);
if (!controlees)
goto nla_put_failure;
list_for_each_entry (controlee, &session->current_controlees, entry) {
controlee_attr = nla_nest_start(msg, 1);
if (!controlee_attr)
goto nla_put_failure;
if (nla_put_le16(msg, FIRA_CALL_CONTROLEE_ATTR_SHORT_ADDR,
controlee->short_addr))
goto nla_put_failure;
if (controlee->sub_session &&
nla_put_u32(msg, FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_ID,
controlee->sub_session_id))
goto nla_put_failure;
nla_nest_end(msg, controlee_attr);
}
nla_nest_end(msg, controlees);
return mcps802154_region_call_reply(local->llhw, msg);
nla_put_failure:
kfree_skb(msg);
return -ENOBUFS;
}
int fira_get_capabilities(struct fira_local *local,
const struct genl_info *info)
{
struct sk_buff *msg;
struct nlattr *capabilities;
u64 hw_flags = local->llhw->flags;
u32 sts_caps = fira_crypto_get_capabilities();
if (!info)
return 0;
msg = mcps802154_region_call_alloc_reply_skb(local->llhw,
&local->region,
FIRA_CALL_GET_CAPABILITIES,
NLMSG_DEFAULT_SIZE);
if (!msg)
return -ENOMEM;
capabilities = nla_nest_start(msg, FIRA_CALL_ATTR_CAPABILITIES);
if (!capabilities) {
goto nla_put_failure;
}
#define P(name, type, value) \
do { \
if (nla_put_##type(msg, FIRA_CAPABILITY_ATTR_##name, value)) { \
goto nla_put_failure; \
} \
} while (0)
#define F(name) \
do { \
if (nla_put_flag(msg, FIRA_CAPABILITY_ATTR_##name)) { \
goto nla_put_failure; \
} \
} while (0)
#define C(name, hw_flag) \
do { \
if (hw_flags & (hw_flag)) \
F(name); \
} while (0)
#define S(mode) \
do { \
if (sts_caps & (1 << FIRA_STS_MODE_##mode)) \
F(STS_##mode); \
} while (0)
/* Main session capabilities. */
P(FIRA_PHY_VERSION_RANGE, u32, 0x01010101);
P(FIRA_MAC_VERSION_RANGE, u32, 0x01010101);
P(DEVICE_CLASS, u8, 1);
F(DEVICE_TYPE_CONTROLEE_RESPONDER);
F(DEVICE_TYPE_CONTROLLER_INITIATOR);
F(MULTI_NODE_MODE_UNICAST);
F(MULTI_NODE_MODE_ONE_TO_MANY);
F(RANGING_ROUND_USAGE_DS_TWR);
P(NUMBER_OF_CONTROLEES_MAX, u32, FIRA_CONTROLEES_MAX);
/* Behaviour. */
F(ROUND_HOPPING);
F(BLOCK_STRIDING);
/* Radio. */
P(CHANNEL_NUMBER, u16,
local->llhw->hw->phy->supported
.channels[local->llhw->hw->phy->current_page]);
C(RFRAME_CONFIG_SP1,
MCPS802154_LLHW_STS_SEGMENT_1 | MCPS802154_LLHW_STS_SEGMENT_2);
C(RFRAME_CONFIG_SP3,
MCPS802154_LLHW_STS_SEGMENT_1 | MCPS802154_LLHW_STS_SEGMENT_2);
C(PRF_MODE_BPRF, MCPS802154_LLHW_BPRF);
C(PRF_MODE_HPRF, MCPS802154_LLHW_HPRF);
C(PREAMBLE_DURATION_32, MCPS802154_LLHW_PSR_32);
C(PREAMBLE_DURATION_64, MCPS802154_LLHW_PSR_64);
C(SFD_ID_0, MCPS802154_LLHW_SFD_4A);
C(SFD_ID_1, MCPS802154_LLHW_SFD_4Z_4);
C(SFD_ID_2, MCPS802154_LLHW_SFD_4Z_8);
C(SFD_ID_3, MCPS802154_LLHW_SFD_4Z_16);
C(SFD_ID_4, MCPS802154_LLHW_SFD_4Z_32);
F(NUMBER_OF_STS_SEGMENTS_0);
C(NUMBER_OF_STS_SEGMENTS_1, MCPS802154_LLHW_STS_SEGMENT_1);
C(NUMBER_OF_STS_SEGMENTS_2, MCPS802154_LLHW_STS_SEGMENT_2);
C(NUMBER_OF_STS_SEGMENTS_3, MCPS802154_LLHW_STS_SEGMENT_3);
C(NUMBER_OF_STS_SEGMENTS_4, MCPS802154_LLHW_STS_SEGMENT_4);
C(PSDU_DATA_RATE_6M81, MCPS802154_LLHW_DATA_RATE_6M81);
C(PSDU_DATA_RATE_7M80, MCPS802154_LLHW_DATA_RATE_7M80);
C(PSDU_DATA_RATE_27M2, MCPS802154_LLHW_DATA_RATE_27M2);
C(PSDU_DATA_RATE_31M2, MCPS802154_LLHW_DATA_RATE_31M2);
C(BPRF_PHR_DATA_RATE_850K, MCPS802154_LLHW_PHR_DATA_RATE_850K);
C(BPRF_PHR_DATA_RATE_6M81, MCPS802154_LLHW_PHR_DATA_RATE_6M81);
F(TX_ADAPTIVE_PAYLOAD_POWER);
/* Antenna. */
P(RX_ANTENNA_PAIRS, u32, local->llhw->rx_antenna_pairs);
P(TX_ANTENNAS, u32, local->llhw->tx_antennas);
/* STS and crypto capabilities. */
S(STATIC);
S(DYNAMIC);
S(DYNAMIC_INDIVIDUAL_KEY);
S(PROVISIONED);
S(PROVISIONED_INDIVIDUAL_KEY);
/* Report. */
C(AOA_AZIMUTH, MCPS802154_LLHW_AOA_AZIMUTH);
C(AOA_AZIMUTH_FULL, MCPS802154_LLHW_AOA_AZIMUTH_FULL);
C(AOA_ELEVATION, MCPS802154_LLHW_AOA_ELEVATION);
C(AOA_FOM, MCPS802154_LLHW_AOA_FOM);
#undef C
#undef F
#undef P
nla_nest_end(msg, capabilities);
return mcps802154_region_call_reply(local->llhw, msg);
nla_put_failure:
kfree_skb(msg);
return -ENOBUFS;
}
int fira_session_get_count(struct fira_local *local)
{
struct fira_session *session;
struct sk_buff *msg;
u32 count = 0;
list_for_each_entry (session, &local->inactive_sessions, entry) {
count++;
}
list_for_each_entry (session, &local->active_sessions, entry) {
count++;
}
msg = mcps802154_region_call_alloc_reply_skb(
local->llhw, &local->region, FIRA_CALL_SESSION_GET_COUNT,
NLMSG_DEFAULT_SIZE);
if (!msg)
return -ENOMEM;
if (nla_put_u32(msg, FIRA_CALL_ATTR_SESSION_COUNT, count))
goto nla_put_failure;
return mcps802154_region_call_reply(local->llhw, msg);
nla_put_failure:
kfree_skb(msg);
return -ENOBUFS;
}
int fira_session_control(struct fira_local *local, enum fira_call call_id,
const struct nlattr *params,
const struct genl_info *info)
{
u32 session_id;
struct nlattr *attrs[FIRA_CALL_ATTR_MAX + 1];
int r;
/* Special case of get count that doesn't need params. */
if (call_id == FIRA_CALL_SESSION_GET_COUNT)
return fira_session_get_count(local);
if (!params)
return -EINVAL;
r = nla_parse_nested(attrs, FIRA_CALL_ATTR_MAX, params,
fira_call_nla_policy, info->extack);
if (r)
return r;
if (!attrs[FIRA_CALL_ATTR_SESSION_ID])
return -EINVAL;
session_id = nla_get_u32(attrs[FIRA_CALL_ATTR_SESSION_ID]);
trace_region_fira_session_control(local, session_id, call_id);
switch (call_id) {
case FIRA_CALL_SESSION_INIT:
return fira_session_init(local, session_id);
case FIRA_CALL_SESSION_START:
return fira_session_start(local, session_id, info);
case FIRA_CALL_SESSION_STOP:
return fira_session_stop(local, session_id);
case FIRA_CALL_SESSION_DEINIT:
return fira_session_deinit(local, session_id);
case FIRA_CALL_SESSION_SET_PARAMS:
return fira_session_set_parameters(
local, session_id, attrs[FIRA_CALL_ATTR_SESSION_PARAMS],
info);
case FIRA_CALL_SET_CONTROLEE:
case FIRA_CALL_NEW_CONTROLEE:
case FIRA_CALL_DEL_CONTROLEE:
return fira_manage_controlees(local, call_id, session_id,
attrs[FIRA_CALL_ATTR_CONTROLEES],
info);
case FIRA_CALL_GET_CONTROLEES:
return fira_session_get_controlees(local, session_id, info);
case FIRA_CALL_SESSION_GET_PARAMS:
return fira_session_get_parameters(local, session_id);
case FIRA_CALL_SESSION_GET_STATE:
return fira_session_get_state(local, session_id);
default:
return -EINVAL;
}
}