| /* |
| * 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); |
| } |