blob: 65b60f3f23f1cbbd1555c1190e1bcf2f5c1fe8bb [file] [log] [blame]
/*
* This file is part of the UWB stack for linux.
*
* Copyright (c) 2021-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 <linux/errno.h>
#include <linux/ieee802154.h>
#include <net/pctt_region_nl.h>
#include <net/mcps802154_frame.h>
#include "pctt_access.h"
#include "pctt_region.h"
#include "pctt_region_call.h"
#include "pctt_session.h"
#include "pctt_trace.h"
static const struct nla_policy pctt_call_nla_policy[PCTT_CALL_ATTR_MAX + 1] = {
[PCTT_CALL_ATTR_CMD_ID] = { .type = NLA_U8 },
[PCTT_CALL_ATTR_RESULT_DATA] = { .type = NLA_NESTED },
[PCTT_CALL_ATTR_SESSION_ID] = { .type = NLA_U32 },
[PCTT_CALL_ATTR_SESSION_STATE] = { .type = NLA_U8 },
[PCTT_CALL_ATTR_SESSION_PARAMS] = { .type = NLA_NESTED },
};
static const struct nla_policy pctt_session_param_nla_policy[PCTT_SESSION_PARAM_ATTR_MAX +
1] = {
[PCTT_SESSION_PARAM_ATTR_DEVICE_ROLE] = {
.type = NLA_U8, .validation_type = NLA_VALIDATE_MAX,
.max = PCTT_DEVICE_ROLE_INITIATOR,
},
[PCTT_SESSION_PARAM_ATTR_SHORT_ADDR] = { .type = NLA_U16 },
[PCTT_SESSION_PARAM_ATTR_DESTINATION_SHORT_ADDR] = { .type = NLA_U16 },
[PCTT_SESSION_PARAM_ATTR_SLOT_DURATION_RSTU] = { .type = NLA_U32 },
[PCTT_SESSION_PARAM_ATTR_RX_ANTENNA_SELECTION] = { .type = NLA_U8 },
[PCTT_SESSION_PARAM_ATTR_TX_ANTENNA_SELECTION] = { .type = NLA_U8 },
[PCTT_SESSION_PARAM_ATTR_CHANNEL_NUMBER] = { .type = NLA_U8 },
[PCTT_SESSION_PARAM_ATTR_PREAMBLE_CODE_INDEX] = { .type = NLA_U8 },
[PCTT_SESSION_PARAM_ATTR_RFRAME_CONFIG] = {
.type = NLA_U8, .validation_type = NLA_VALIDATE_MAX,
.max = PCTT_RFRAME_CONFIG_SP3,
},
[PCTT_SESSION_PARAM_ATTR_PRF_MODE] = {
.type = NLA_U8, .validation_type = NLA_VALIDATE_MAX,
.max = PCTT_PRF_MODE_HPRF_HIGH_RATE,
},
[PCTT_SESSION_PARAM_ATTR_PREAMBLE_DURATION] = {
.type = NLA_U8, .validation_type = NLA_VALIDATE_MAX,
.max = PCTT_PREAMBLE_DURATION_64,
},
[PCTT_SESSION_PARAM_ATTR_SFD_ID] = {
.type = NLA_U8, .validation_type = NLA_VALIDATE_MAX,
.max = PCTT_SFD_ID_4,
},
[PCTT_SESSION_PARAM_ATTR_NUMBER_OF_STS_SEGMENTS] = {
.type = NLA_U8, .validation_type = NLA_VALIDATE_MAX,
.max = PCTT_NUMBER_OF_STS_SEGMENTS_4_SEGMENTS,
},
[PCTT_SESSION_PARAM_ATTR_PSDU_DATA_RATE] = {
.type = NLA_U8, .validation_type = NLA_VALIDATE_MAX,
.max = PCTT_PSDU_DATA_RATE_31M2,
},
[PCTT_SESSION_PARAM_ATTR_BPRF_PHR_DATA_RATE] = {
.type = NLA_U8, .validation_type = NLA_VALIDATE_MAX,
.max = PCTT_PHR_DATA_RATE_6M81,
},
[PCTT_SESSION_PARAM_ATTR_MAC_FCS_TYPE] = {
.type = NLA_U8, .validation_type = NLA_VALIDATE_MAX,
.max = PCTT_MAC_FCS_TYPE_CRC_32,
},
[PCTT_SESSION_PARAM_ATTR_TX_ADAPTIVE_PAYLOAD_POWER] = {
.type = NLA_U8, .validation_type = NLA_VALIDATE_MAX,
.max = PCTT_BOOLEAN_MAX,
},
[PCTT_SESSION_PARAM_ATTR_STS_INDEX] = { .type = NLA_U32 },
[PCTT_SESSION_PARAM_ATTR_STS_LENGTH] = {
.type = NLA_U8, .validation_type = NLA_VALIDATE_MAX,
.max = PCTT_STS_LENGTH_128,
},
[PCTT_SESSION_PARAM_ATTR_NUM_PACKETS] = { .type = NLA_U32 },
[PCTT_SESSION_PARAM_ATTR_T_GAP] = { .type = NLA_U32 },
[PCTT_SESSION_PARAM_ATTR_T_START] = { .type = NLA_U32 },
[PCTT_SESSION_PARAM_ATTR_T_WIN] = { .type = NLA_U32 },
[PCTT_SESSION_PARAM_ATTR_RANDOMIZE_PSDU] = { .type = NLA_U8 },
[PCTT_SESSION_PARAM_ATTR_PHR_RANGING_BIT] = { .type = NLA_U8 },
[PCTT_SESSION_PARAM_ATTR_RMARKER_TX_START] = { .type = NLA_U32 },
[PCTT_SESSION_PARAM_ATTR_RMARKER_RX_START] = { .type = NLA_U32 },
[PCTT_SESSION_PARAM_ATTR_STS_INDEX_AUTO_INCR] = { .type = NLA_U8 },
[PCTT_SESSION_PARAM_ATTR_DATA_PAYLOAD] = {
.type = NLA_BINARY,
.len = PCTT_PAYLOAD_MAX_LEN
},
};
int pctt_call_session_get_state(struct pctt_local *local)
{
struct pctt_session *session = &local->session;
struct sk_buff *msg;
msg = mcps802154_region_call_alloc_reply_skb(
local->llhw, &local->region, PCTT_CALL_SESSION_GET_STATE,
NLMSG_DEFAULT_SIZE);
if (!msg)
return -ENOMEM;
if (nla_put_u32(msg, PCTT_CALL_ATTR_SESSION_ID, PCTT_SESSION_ID))
goto nla_put_failure;
if (nla_put_u8(msg, PCTT_CALL_ATTR_SESSION_STATE, session->state))
goto nla_put_failure;
return mcps802154_region_call_reply(local->llhw, msg);
nla_put_failure:
kfree_skb(msg);
return -ENOBUFS;
}
int pctt_call_session_get_params(struct pctt_local *local)
{
struct pctt_session *session = &local->session;
const struct pctt_session_params *p = &session->params;
struct nlattr *params;
struct sk_buff *msg;
msg = mcps802154_region_call_alloc_reply_skb(
local->llhw, &local->region, PCTT_CALL_SESSION_GET_PARAMS,
NLMSG_DEFAULT_SIZE);
if (!msg)
return -ENOMEM;
if (nla_put_u32(msg, PCTT_CALL_ATTR_SESSION_ID, PCTT_SESSION_ID))
goto nla_put_failure;
params = nla_nest_start(msg, PCTT_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, PCTT_SESSION_PARAM_ATTR_##attr, conv)) \
goto nla_put_failure; \
} while (0)
P(DEVICE_ROLE, device_role, u8, x);
P(SHORT_ADDR, short_addr, u16, x);
P(DESTINATION_SHORT_ADDR, dst_short_addr, u16, x);
P(RX_ANTENNA_SELECTION, rx_antenna_selection, u8, x);
P(TX_ANTENNA_SELECTION, tx_antenna_selection, u8, x);
P(SLOT_DURATION_RSTU, slot_duration_dtu, u32,
x / local->llhw->rstu_dtu);
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);
P(TX_ADAPTIVE_PAYLOAD_POWER, tx_adaptive_payload_power, u8, x);
P(STS_INDEX, sts_index, u32, x);
P(STS_LENGTH, sts_length, u8, x);
P(NUM_PACKETS, num_packets, u32, x);
P(T_GAP, gap_duration_dtu, u32,
(((u64)x * 1000) / (local->llhw->dtu_freq_hz / 1000)));
P(T_START, t_start, u32, x);
P(T_WIN, t_win, u32, x);
P(RANDOMIZE_PSDU, randomize_psdu, u8, x);
P(PHR_RANGING_BIT, phr_ranging_bit, u8, x);
P(RMARKER_TX_START, rmarker_tx_start, u32, x);
P(RMARKER_RX_START, rmarker_rx_start, u32, x);
P(STS_INDEX_AUTO_INCR, sts_index_auto_incr, u8, x);
#undef P
nla_nest_end(msg, params);
return mcps802154_region_call_reply(local->llhw, msg);
nla_put_failure:
kfree_skb(msg);
return -ENOBUFS;
}
static int pctt_call_session_set_params(struct pctt_local *local,
const struct nlattr *params,
const struct genl_info *info)
{
struct nlattr *attrs[PCTT_SESSION_PARAM_ATTR_MAX + 1];
struct pctt_session *session = &local->session;
struct pctt_session_params *p = &session->params;
int r;
if (!params)
return -EINVAL;
if (session->test_on_going)
return -EBUSY;
r = nla_parse_nested(attrs, PCTT_SESSION_PARAM_ATTR_MAX, params,
pctt_session_param_nla_policy, info->extack);
if (r)
return r;
#define P(attr, member, type, conv) \
do { \
int x; \
if (attrs[PCTT_SESSION_PARAM_ATTR_##attr]) { \
x = nla_get_##type( \
attrs[PCTT_SESSION_PARAM_ATTR_##attr]); \
p->member = conv; \
} \
} while (0)
#define PMEMNCPY(attr, member, size) \
do { \
if (attrs[PCTT_SESSION_PARAM_ATTR_##attr]) { \
struct nlattr *attr = \
attrs[PCTT_SESSION_PARAM_ATTR_##attr]; \
int len = nla_len(attr); \
memcpy(p->member, nla_data(attr), len); \
p->size = len; \
} \
} while (0)
P(DEVICE_ROLE, device_role, u8, x);
P(SHORT_ADDR, short_addr, u16, x);
P(DESTINATION_SHORT_ADDR, dst_short_addr, u16, x);
P(RX_ANTENNA_SELECTION, rx_antenna_selection, u8, x);
P(TX_ANTENNA_SELECTION, tx_antenna_selection, u8, x);
P(SLOT_DURATION_RSTU, slot_duration_dtu, u32,
x * local->llhw->rstu_dtu);
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);
P(TX_ADAPTIVE_PAYLOAD_POWER, tx_adaptive_payload_power, u8, x);
P(STS_INDEX, sts_index, u32, x);
P(STS_LENGTH, sts_length, u8, x);
P(NUM_PACKETS, num_packets, u32, x);
P(T_GAP, gap_duration_dtu, u32,
((u64)x * (local->llhw->dtu_freq_hz / 1000)) / 1000);
P(T_START, t_start, u32, x);
P(T_WIN, t_win, u32, x);
P(RANDOMIZE_PSDU, randomize_psdu, u8, x);
P(PHR_RANGING_BIT, phr_ranging_bit, u8, x);
P(RMARKER_TX_START, rmarker_tx_start, u32, x);
P(RMARKER_RX_START, rmarker_rx_start, u32, x);
P(STS_INDEX_AUTO_INCR, sts_index_auto_incr, u8, x);
PMEMNCPY(DATA_PAYLOAD, data_payload, data_payload_len);
#undef PMEMNCPY
#undef P
return 0;
}
static int pctt_call_cmd(struct pctt_local *local,
const struct nlattr *cmd_id_attr,
const struct genl_info *info)
{
struct pctt_session *session = &local->session;
enum pctt_id_attrs cmd_id;
if (!cmd_id_attr)
return -EINVAL;
cmd_id = nla_get_u8(cmd_id_attr);
if (session->test_on_going) {
if (cmd_id == PCTT_ID_ATTR_STOP_TEST &&
!session->stop_request) {
session->stop_request = true;
mcps802154_reschedule(local->llhw);
return 0;
}
return -EBUSY;
} else {
u32 now_dtu;
int r;
if (cmd_id == PCTT_ID_ATTR_STOP_TEST)
return 0;
/* FIXME: Used only to detect dw3000_is_active. */
r = mcps802154_get_current_timestamp_dtu(local->llhw, &now_dtu);
if (r)
return r;
r = pctt_session_start_test(local, cmd_id, info);
if (r)
return r;
}
mcps802154_reschedule(local->llhw);
return 0;
}
int pctt_call_session_control(struct pctt_local *local, enum pctt_call call_id,
const struct nlattr *params,
const struct genl_info *info)
{
struct pctt_session *session = &local->session;
struct nlattr *attrs[PCTT_CALL_ATTR_MAX + 1];
int r;
if (session->state == PCTT_SESSION_STATE_DEINIT)
return -EPERM;
if (!params || !info)
return -EINVAL;
r = nla_parse_nested(attrs, PCTT_CALL_ATTR_MAX, params,
pctt_call_nla_policy, info->extack);
if (r)
return r;
if (call_id == PCTT_CALL_SESSION_SET_PARAMS)
return pctt_call_session_set_params(
local, attrs[PCTT_CALL_ATTR_SESSION_PARAMS], info);
else
return pctt_call_cmd(local, attrs[PCTT_CALL_ATTR_CMD_ID], info);
}