| /* |
| * This file is part of the UWB stack for linux. |
| * |
| * Copyright (c) 2020 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. |
| * |
| * 802.15.4 mac common part sublayer, frame processing. |
| * |
| */ |
| #include <linux/module.h> |
| |
| #include "mcps802154_i.h" |
| #include "llhw-ops.h" |
| |
| void mcps802154_fproc_init(struct mcps802154_local *local) |
| { |
| local->fproc.state = &mcps802154_fproc_stopped; |
| WARN_ON(!local->fproc.state->enter); |
| local->fproc.state->enter(local); |
| } |
| |
| void mcps802154_fproc_uninit(struct mcps802154_local *local) |
| { |
| WARN_ON(local->fproc.access); |
| WARN_ON(local->fproc.tx_skb); |
| WARN_ON(local->started); |
| } |
| |
| void mcps802154_fproc_change_state( |
| struct mcps802154_local *local, |
| const struct mcps802154_fproc_state *new_state) |
| { |
| if (local->fproc.state->leave) |
| local->fproc.state->leave(local); |
| local->fproc.state = new_state; |
| if (local->fproc.state->enter) |
| local->fproc.state->enter(local); |
| } |
| |
| void mcps802154_fproc_access(struct mcps802154_local *local, |
| u32 next_timestamp_dtu) |
| { |
| struct mcps802154_access *access; |
| int r; |
| |
| if (!local->start_stop_request) { |
| mcps802154_fproc_stopped_handle(local); |
| return; |
| } |
| |
| access = mcps802154_ca_get_access(local, next_timestamp_dtu); |
| if (unlikely(!access)) { |
| mcps802154_fproc_broken_handle(local); |
| return; |
| } |
| |
| local->fproc.access = access; |
| local->fproc.frame_idx = 0; |
| |
| switch (access->method) { |
| case MCPS802154_ACCESS_METHOD_NOTHING: |
| mcps802154_fproc_nothing_handle(local); |
| r = 0; |
| break; |
| case MCPS802154_ACCESS_METHOD_IMMEDIATE_RX: |
| r = mcps802154_fproc_rx_handle(local, access); |
| break; |
| case MCPS802154_ACCESS_METHOD_IMMEDIATE_TX: |
| r = mcps802154_fproc_tx_handle(local, access); |
| break; |
| case MCPS802154_ACCESS_METHOD_MULTI: |
| r = mcps802154_fproc_multi_handle(local, access); |
| break; |
| case MCPS802154_ACCESS_METHOD_VENDOR: |
| r = mcps802154_fproc_vendor_handle(local, access); |
| break; |
| default: |
| r = -1; |
| } |
| |
| if (r) { |
| mcps802154_fproc_access_done(local); |
| mcps802154_fproc_broken_handle(local); |
| } |
| } |
| |
| void mcps802154_fproc_access_now(struct mcps802154_local *local) |
| { |
| int r = 0; |
| u32 next_timestamp_dtu = 0; |
| |
| if (local->start_stop_request) |
| r = llhw_get_current_timestamp_dtu(local, &next_timestamp_dtu); |
| |
| if (r) |
| mcps802154_fproc_broken_handle(local); |
| else |
| mcps802154_fproc_access(local, next_timestamp_dtu + |
| local->llhw.anticip_dtu); |
| } |
| |
| void mcps802154_fproc_access_done(struct mcps802154_local *local) |
| { |
| struct mcps802154_access *access = local->fproc.access; |
| |
| /* access_done handler is declared in common and unamed structure of |
| * all type of access ops */ |
| access->ops->access_done(access); |
| local->fproc.access = NULL; |
| } |
| |
| void mcps802154_fproc_access_reset(struct mcps802154_local *local) |
| { |
| struct mcps802154_access *access = local->fproc.access; |
| |
| if (access) { |
| if (local->fproc.tx_skb) { |
| WARN_ON(access->method == |
| MCPS802154_ACCESS_METHOD_VENDOR); |
| access->ops->tx_return( |
| access, local->fproc.frame_idx, |
| local->fproc.tx_skb, |
| MCPS802154_ACCESS_TX_RETURN_REASON_CANCEL); |
| local->fproc.tx_skb = NULL; |
| } |
| mcps802154_fproc_access_done(local); |
| local->fproc.access = NULL; |
| } |
| } |
| |
| static void mcps802154_broken_safe(struct mcps802154_local *local) |
| { |
| if (local->fproc.state->broken) |
| local->fproc.state->broken(local); |
| else |
| mcps802154_fproc_broken_handle(local); |
| } |
| |
| void mcps802154_rx_frame(struct mcps802154_llhw *llhw) |
| { |
| struct mcps802154_local *local = llhw_to_local(llhw); |
| |
| mutex_lock(&local->fsm_lock); |
| trace_llhw_event_rx_frame(local); |
| if (local->fproc.state->rx_frame) |
| local->fproc.state->rx_frame(local); |
| else |
| mcps802154_broken_safe(local); |
| trace_llhw_event_done(local); |
| mutex_unlock(&local->fsm_lock); |
| } |
| EXPORT_SYMBOL(mcps802154_rx_frame); |
| |
| void mcps802154_rx_timeout(struct mcps802154_llhw *llhw) |
| { |
| struct mcps802154_local *local = llhw_to_local(llhw); |
| |
| mutex_lock(&local->fsm_lock); |
| trace_llhw_event_rx_timeout(local); |
| if (local->fproc.state->rx_timeout) |
| local->fproc.state->rx_timeout(local); |
| else |
| mcps802154_broken_safe(local); |
| trace_llhw_event_done(local); |
| mutex_unlock(&local->fsm_lock); |
| } |
| EXPORT_SYMBOL(mcps802154_rx_timeout); |
| |
| void mcps802154_rx_error(struct mcps802154_llhw *llhw, |
| enum mcps802154_rx_error_type error) |
| { |
| struct mcps802154_local *local = llhw_to_local(llhw); |
| |
| mutex_lock(&local->fsm_lock); |
| trace_llhw_event_rx_error(local, error); |
| if (local->fproc.state->rx_error) |
| local->fproc.state->rx_error(local, error); |
| else |
| mcps802154_broken_safe(local); |
| trace_llhw_event_done(local); |
| mutex_unlock(&local->fsm_lock); |
| } |
| EXPORT_SYMBOL(mcps802154_rx_error); |
| |
| void mcps802154_tx_done(struct mcps802154_llhw *llhw) |
| { |
| struct mcps802154_local *local = llhw_to_local(llhw); |
| |
| mutex_lock(&local->fsm_lock); |
| trace_llhw_event_tx_done(local); |
| if (local->fproc.state->tx_done) |
| local->fproc.state->tx_done(local); |
| else |
| mcps802154_broken_safe(local); |
| trace_llhw_event_done(local); |
| mutex_unlock(&local->fsm_lock); |
| } |
| EXPORT_SYMBOL(mcps802154_tx_done); |
| |
| void mcps802154_broken(struct mcps802154_llhw *llhw) |
| { |
| struct mcps802154_local *local = llhw_to_local(llhw); |
| |
| mutex_lock(&local->fsm_lock); |
| trace_llhw_event_broken(local); |
| mcps802154_broken_safe(local); |
| trace_llhw_event_done(local); |
| mutex_unlock(&local->fsm_lock); |
| } |
| EXPORT_SYMBOL(mcps802154_broken); |