blob: bd4f90a3d57158a3c582d21cf4297a468edb0081 [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 "nfcc_coex_access.h"
#include "nfcc_coex_session.h"
#include "nfcc_coex_region.h"
#include "llhw-ops.h"
#include <linux/string.h>
#include <linux/ieee802154.h>
#include <net/mcps802154_frame.h>
#include <net/vendor_cmd.h>
#include "warn_return.h"
static void nfcc_coex_stop_by_vendor_cmd_failure(struct nfcc_coex_local *local)
{
static const struct dw3000_vendor_cmd_nfcc_coex_get_access_info error = {
.stop = 1
};
local->session.get_access_info = error;
local->state = NFCC_COEX_STATE_STOPPING;
}
static int nfcc_coex_vendor_cmd(struct mcps802154_llhw *llhw,
enum dw3000_vendor_cmd subcmd, void *data,
size_t data_len)
{
struct mcps802154_local *local = llhw_to_local(llhw);
/* Qorvo OUI in big endian. */
static const u32 qorvo_oui = 0xc8b1ee00;
return llhw_vendor_cmd(local, qorvo_oui, subcmd, data, data_len);
}
static void nfcc_coex_access_done(struct mcps802154_access *access, int error)
{
struct nfcc_coex_local *local = access_to_local(access);
struct nfcc_coex_session *session = &local->session;
switch (local->state) {
case NFCC_COEX_STATE_STOPPING:
nfcc_coex_vendor_cmd(local->llhw,
DW3000_VENDOR_CMD_NFCC_COEX_STOP, NULL, 0);
nfcc_coex_report(local);
break;
case NFCC_COEX_STATE_ACCESSING:
if (session->get_access_info.stop ||
session->get_access_info.watchdog_timeout)
local->state = NFCC_COEX_STATE_STOPPING;
nfcc_coex_report(local);
break;
default:
WARN_UNREACHABLE_DEFAULT();
}
}
static int nfcc_coex_handle(struct mcps802154_access *access)
{
struct nfcc_coex_local *local = access_to_local(access);
struct nfcc_coex_session *session = &local->session;
struct dw3000_vendor_cmd_nfcc_coex_handle_access handle_access = {};
int r;
handle_access.start = session->first_access;
handle_access.timestamp_dtu = access->timestamp_dtu;
handle_access.duration_dtu = access->duration_dtu;
handle_access.chan = session->params.channel_number;
session->first_access = false;
r = nfcc_coex_vendor_cmd(local->llhw,
DW3000_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS,
&handle_access, sizeof(handle_access));
if (r)
nfcc_coex_stop_by_vendor_cmd_failure(local);
return r;
}
static int nfcc_coex_tx_done(struct mcps802154_access *access)
{
struct nfcc_coex_local *local = access_to_local(access);
struct nfcc_coex_session *session = &local->session;
struct dw3000_vendor_cmd_nfcc_coex_get_access_info *get_access_info =
&session->get_access_info;
struct mcps802154_region_demand *rd = &session->region_demand;
int r;
r = nfcc_coex_vendor_cmd(
local->llhw, DW3000_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION,
get_access_info, sizeof(*get_access_info));
if (r) {
nfcc_coex_stop_by_vendor_cmd_failure(local);
return r;
}
rd->timestamp_dtu = get_access_info->next_timestamp_dtu;
rd->duration_dtu = get_access_info->next_duration_dtu;
/* Request end of current access. */
return 1;
}
struct mcps802154_access_vendor_ops nfcc_coex_ops = {
.common = {
.access_done = nfcc_coex_access_done,
},
.handle = nfcc_coex_handle,
.tx_done = nfcc_coex_tx_done,
};
static struct mcps802154_access *
nfcc_coex_access_controller(struct nfcc_coex_local *local,
struct nfcc_coex_session *session)
{
struct mcps802154_access *access = &local->access;
access->method = MCPS802154_ACCESS_METHOD_VENDOR;
access->vendor_ops = &nfcc_coex_ops;
access->duration_dtu = session->region_demand.duration_dtu;
access->timestamp_dtu = session->region_demand.timestamp_dtu;
access->n_frames = 0;
access->frames = NULL;
return access;
}
struct mcps802154_access *nfcc_coex_get_access(struct mcps802154_region *region,
u32 next_timestamp_dtu,
int next_in_region_dtu,
int region_duration_dtu)
{
struct nfcc_coex_local *local = region_to_local(region);
struct nfcc_coex_session *session;
/* Get unique session. */
session = nfcc_coex_session_next(local, next_timestamp_dtu,
region_duration_dtu);
if (!session) {
local->state = NFCC_COEX_STATE_UNUSED;
return NULL;
} else {
local->state = NFCC_COEX_STATE_ACCESSING;
return nfcc_coex_access_controller(local, session);
}
}