blob: 60d6bae7d195b2a351814ce1abb0249af7e61b5f [file] [edit]
/*
* 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 "nfcc_coex_trace.h"
#include "llhw-ops.h"
#include "mcps802154_qorvo.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_access_done(struct mcps802154_access *access, bool error)
{
struct nfcc_coex_local *local = access_to_local(access);
struct nfcc_coex_session *session = &local->session;
/* Stop on error because the next timestamps is unknown.
* Stop in V2, because the vendor stop is not supported by NFC. */
if ((error || (session->state == NFCC_COEX_STATE_STOPPING &&
session->params.version == 2)) &&
!session->get_access_info.watchdog_timeout) {
const struct llhw_vendor_cmd_nfcc_coex_get_access_info stop = {
.stop = true,
};
local->session.get_access_info = stop;
}
if (session->get_access_info.stop ||
session->get_access_info.watchdog_timeout)
nfcc_coex_set_state(local, NFCC_COEX_STATE_IDLE);
nfcc_coex_report(local);
}
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 llhw_vendor_cmd_nfcc_coex_handle_access handle_access = {};
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;
handle_access.version = session->params.version;
if (session->state == NFCC_COEX_STATE_STOPPING &&
session->params.version == 3) {
/* Stop processing : stop the nfcc coex */
if (local->session.first_access) {
struct mcps802154_region_demand *rd =
&session->region_demand;
struct llhw_vendor_cmd_nfcc_coex_stop stop = {
.timestamp_dtu = rd->timestamp_dtu,
.duration_dtu = rd->max_duration_dtu,
.version = session->params.version,
};
return mcps802154_vendor_cmd(
local->llhw, VENDOR_QORVO_OUI,
LLHW_VENDOR_CMD_NFCC_COEX_STOP, &stop,
sizeof(stop));
} else
return mcps802154_vendor_cmd(
local->llhw, VENDOR_QORVO_OUI,
LLHW_VENDOR_CMD_NFCC_COEX_STOP, NULL, 0);
}
return mcps802154_vendor_cmd(local->llhw, VENDOR_QORVO_OUI,
LLHW_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS,
&handle_access, sizeof(handle_access));
}
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 llhw_vendor_cmd_nfcc_coex_get_access_info *get_access_info =
&session->get_access_info;
struct mcps802154_region_demand *rd = &session->region_demand;
int r;
session->first_access = false;
r = mcps802154_vendor_cmd(
local->llhw, VENDOR_QORVO_OUI,
LLHW_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION,
get_access_info, sizeof(*get_access_info));
if (r)
return r;
/* Update region demand for next access. */
rd->timestamp_dtu = get_access_info->next_timestamp_dtu;
rd->max_duration_dtu = get_access_info->next_duration_dtu;
/* Request end of current access. */
return 1;
}
static int nfcc_coex_broken(struct mcps802154_access *access)
{
struct nfcc_coex_local *local = access_to_local(access);
const struct llhw_vendor_cmd_nfcc_coex_get_access_info
watchdog_timeout = {
.watchdog_timeout = true,
};
local->session.get_access_info = watchdog_timeout;
/* Request end of current access. */
return -ETIME;
}
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,
.broken = nfcc_coex_broken,
};
static struct mcps802154_access *
nfcc_coex_access_controller(struct nfcc_coex_local *local,
struct nfcc_coex_session *session)
{
struct mcps802154_access *access = &local->access;
const struct mcps802154_region_demand *rd = &session->region_demand;
access->method = MCPS802154_ACCESS_METHOD_VENDOR;
access->vendor_ops = &nfcc_coex_ops;
access->duration_dtu = rd->max_duration_dtu;
access->timestamp_dtu = rd->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 = &local->session;
if (session->state == NFCC_COEX_STATE_STARTED ||
session->state == NFCC_COEX_STATE_STOPPING) {
nfcc_coex_session_update(local, session, next_timestamp_dtu,
region_duration_dtu);
return nfcc_coex_access_controller(local, session);
}
return NULL;
}