blob: c3845e9220c135476c52cba7149229e254c3ed32 [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 <linux/slab.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <net/mcps802154_schedule.h>
#include "mcps802154_i.h"
#include "default_region.h"
#include "warn_return.h"
struct mcps802154_default_local {
struct mcps802154_scheduler scheduler;
struct mcps802154_llhw *llhw;
struct mcps802154_region region;
struct mcps802154_access access;
};
static inline struct mcps802154_default_local *
scheduler_to_dlocal(const struct mcps802154_scheduler *scheduler)
{
return container_of(scheduler, struct mcps802154_default_local,
scheduler);
}
static inline struct mcps802154_default_local *
region_to_dlocal(struct mcps802154_region *region)
{
return container_of(region, struct mcps802154_default_local, region);
}
static inline struct mcps802154_default_local *
access_to_dlocal(const struct mcps802154_access *access)
{
return container_of(access, struct mcps802154_default_local, access);
}
static void simple_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 mcps802154_default_local *dlocal = access_to_dlocal(access);
struct mcps802154_local *local = llhw_to_local(dlocal->llhw);
ieee802154_rx_irqsafe(local->hw, skb, info->lqi);
}
static struct sk_buff *simple_tx_get_frame(struct mcps802154_access *access,
int frame_idx)
{
struct mcps802154_default_local *dlocal = access_to_dlocal(access);
struct mcps802154_local *local = llhw_to_local(dlocal->llhw);
return skb_dequeue(&local->ca.queue);
}
static void simple_tx_return(struct mcps802154_access *access, int frame_idx,
struct sk_buff *skb,
enum mcps802154_access_tx_return_reason reason)
{
struct mcps802154_default_local *dlocal = access_to_dlocal(access);
struct mcps802154_local *local = llhw_to_local(dlocal->llhw);
if (reason == MCPS802154_ACCESS_TX_RETURN_REASON_FAILURE) {
local->ca.retries++;
if (local->ca.retries <= local->pib.mac_max_frame_retries) {
/* Retry the frame. */
skb_queue_head(&local->ca.queue, skb);
} else {
local->ca.retries = 0;
ieee802154_wake_queue(local->hw);
dev_kfree_skb_any(skb);
atomic_dec(&local->ca.n_queued);
}
} else if (reason == MCPS802154_ACCESS_TX_RETURN_REASON_CANCEL) {
skb_queue_head(&local->ca.queue, skb);
} else {
local->ca.retries = 0;
ieee802154_xmit_complete(local->hw, skb, false);
atomic_dec(&local->ca.n_queued);
}
}
struct mcps802154_access_ops simple_access_ops = {
.rx_frame = simple_rx_frame,
.tx_get_frame = simple_tx_get_frame,
.tx_return = simple_tx_return,
};
static struct mcps802154_access *
simple_default_get_access(struct mcps802154_region *region,
u32 next_timestamp_dtu, int next_in_region_dtu,
int region_duration_dtu)
{
struct mcps802154_default_local *dlocal = region_to_dlocal(region);
struct mcps802154_local *local = llhw_to_local(dlocal->llhw);
dlocal->access.method = skb_queue_empty(&local->ca.queue) ?
MCPS802154_ACCESS_METHOD_IMMEDIATE_RX :
MCPS802154_ACCESS_METHOD_IMMEDIATE_TX;
dlocal->access.ops = &simple_access_ops;
return &dlocal->access;
}
static struct mcps802154_region_ops mcps802154_default_simple_region_ops = {
.owner = THIS_MODULE,
.name = "simple",
.get_access = simple_default_get_access,
};
static struct mcps802154_scheduler *
mcps802154_default_scheduler_open(struct mcps802154_llhw *llhw)
{
struct mcps802154_default_local *dlocal;
dlocal = kmalloc(sizeof(*dlocal), GFP_KERNEL);
if (!dlocal)
return NULL;
dlocal->llhw = llhw;
dlocal->region.ops = &mcps802154_default_simple_region_ops;
return &dlocal->scheduler;
}
static void
mcps802154_default_scheduler_close(struct mcps802154_scheduler *scheduler)
{
struct mcps802154_default_local *dlocal =
scheduler_to_dlocal(scheduler);
kfree(dlocal);
}
static int mcps802154_default_scheduler_update_schedule(
struct mcps802154_scheduler *scheduler,
const struct mcps802154_schedule_update *schedule_update,
u32 next_timestamp_dtu)
{
struct mcps802154_default_local *dlocal =
scheduler_to_dlocal(scheduler);
int r;
r = mcps802154_schedule_set_start(
schedule_update, schedule_update->expected_start_timestamp_dtu);
/* Can not fail, only possible error is invalid parameters. */
WARN_RETURN(r);
r = mcps802154_schedule_recycle(schedule_update, 0,
MCPS802154_DURATION_NO_CHANGE);
/* Can not fail, only possible error is invalid parameters. */
WARN_RETURN(r);
r = mcps802154_schedule_add_region(schedule_update, &dlocal->region, 0,
0);
return r;
}
static struct mcps802154_scheduler_ops mcps802154_default_region_scheduler = {
.owner = THIS_MODULE,
.name = "default",
.open = mcps802154_default_scheduler_open,
.close = mcps802154_default_scheduler_close,
.update_schedule = mcps802154_default_scheduler_update_schedule,
};
int __init mcps802154_default_region_init(void)
{
return mcps802154_scheduler_register(
&mcps802154_default_region_scheduler);
}
void __exit mcps802154_default_region_exit(void)
{
mcps802154_scheduler_unregister(&mcps802154_default_region_scheduler);
}