Search Linux Wireless

[RFC v1 123/256] cl8k: add ops.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Viktor Barna <viktor.barna@xxxxxxxxxx>

(Part of the split. Please, take a look at the cover letter for more
details).

Signed-off-by: Viktor Barna <viktor.barna@xxxxxxxxxx>
---
 drivers/net/wireless/celeno/cl8k/ops.c | 889 +++++++++++++++++++++++++
 1 file changed, 889 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/ops.c

diff --git a/drivers/net/wireless/celeno/cl8k/ops.c b/drivers/net/wireless/celeno/cl8k/ops.c
new file mode 100644
index 000000000000..16934984b7cd
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/ops.c
@@ -0,0 +1,889 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include <linux/log2.h>
+#include "ops.h"
+#include "utils/ip.h"
+#include "chip.h"
+#include "ampdu.h"
+#include "fw/msg_tx.h"
+#include "tx/tx.h"
+#include "tx/tx_queue.h"
+#include "radio.h"
+#include "recovery.h"
+#include "rate_ctrl.h"
+#include "temperature.h"
+#include "band.h"
+#include "rx/rx.h"
+#include "edca.h"
+#include "utils/math.h"
+#include "utils/utils.h"
+#include "ext/dyn_mcast_rate.h"
+#include "ext/dyn_bcast_rate.h"
+#include "vns.h"
+#include "dfs/dfs.h"
+#include "key.h"
+#include "temperature.h"
+#include "calib.h"
+#include "wrs/wrs_api.h"
+#include "chandef.h"
+#include "version.h"
+#include "power.h"
+#include "tx/tx_inject.h"
+#include "stats.h"
+#include "netlink.h"
+#include "calib.h"
+#ifdef CONFIG_CL_PCIE
+#include "bus/pci/ipc.h"
+#endif
+
+static const int cl_ac2hwq[AC_MAX] = {
+       [NL80211_TXQ_Q_VO] = CL_HWQ_VO,
+       [NL80211_TXQ_Q_VI] = CL_HWQ_VI,
+       [NL80211_TXQ_Q_BE] = CL_HWQ_BE,
+       [NL80211_TXQ_Q_BK] = CL_HWQ_BK
+};
+
+static const int cl_ac2edca[AC_MAX] = {
+       [NL80211_TXQ_Q_VO] = EDCA_AC_VO,
+       [NL80211_TXQ_Q_VI] = EDCA_AC_VI,
+       [NL80211_TXQ_Q_BE] = EDCA_AC_BE,
+       [NL80211_TXQ_Q_BK] = EDCA_AC_BK
+};
+
+static void cl_ops_tx_agg(struct cl_hw *cl_hw,
+                         struct sk_buff *skb,
+                         struct ieee80211_tx_info *tx_info,
+                         struct cl_sta *cl_sta)
+{
+       cl_hw->tx_packet_cntr.forward.from_mac_agg++;
+
+       if (!cl_sta) {
+               kfree_skb(skb);
+               cl_dbg_err(cl_hw, "cl_sta null in agg packet\n");
+               cl_hw->tx_packet_cntr.drop.sta_null_in_agg++;
+               return;
+       }
+
+       /* AMSDU in HW can work only with header conversion. */
+       tx_info->control.flags &= ~IEEE80211_TX_CTRL_AMSDU;
+       cl_tx_agg(cl_hw, cl_sta, skb, false, true);
+}
+
+static void cl_ops_tx_single(struct cl_hw *cl_hw,
+                            struct sk_buff *skb,
+                            struct ieee80211_tx_info *tx_info,
+                            struct cl_sta *cl_sta)
+{
+       bool is_vns = cl_vns_is_very_near(cl_hw, cl_sta, skb);
+
+       cl_hw->tx_packet_cntr.forward.from_mac_single++;
+
+       cl_tx_single(cl_hw, cl_sta, skb, is_vns, true);
+}
+
+void cl_ops_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb)
+{
+       /*
+        * Almost all traffic passing here is singles.
+        * Only when opening a BA session some packets with
+        * IEEE80211_TX_CTL_AMPDU set can pass here.
+        * All skbs passing here did header conversion.
+        */
+       struct cl_hw *cl_hw = (struct cl_hw *)hw->priv;
+       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_sta *sta = control->sta;
+       struct cl_sta *cl_sta = NULL;
+
+       if (sta) {
+               cl_sta = IEEE80211_STA_TO_CL_STA(sta);
+
+               /*
+                * Prior to STA connection sta can be set but we don't
+                * want cl_sta to be used since it's not initialized yet
+                */
+               if (cl_sta->sta_idx == STA_IDX_INVALID)
+                       cl_sta = NULL;
+       }
+
+       if (cl_recovery_in_progress(cl_hw)) {
+               cl_hw->tx_packet_cntr.drop.in_recovery++;
+               cl_tx_drop_dkb(skb);
+               return;
+       }
+
+       if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
+               cl_ops_tx_agg(cl_hw, skb, tx_info, cl_sta);
+       else
+               cl_ops_tx_single(cl_hw, skb, tx_info, cl_sta);
+}
+
+int cl_ops_start(struct ieee80211_hw *hw)
+{
+       /*
+        * Called before the first netdevice attached to the hardware
+        * is enabled. This should turn on the hardware and must turn on
+        * frame reception (for possibly enabled monitor interfaces.)
+        * Returns negative error codes, these may be seen in userspace,
+        * or zero.
+        * When the device is started it should not have a MAC address
+        * to avoid acknowledging frames before a non-monitor device
+        * is added.
+        * Must be implemented and can sleep.
+        * It does not return until the firmware is up and running.
+        */
+       int error = 0;
+       struct cl_hw *cl_hw = hw->priv;
+       struct cl_tcv_conf *conf = cl_hw->conf;
+
+#ifdef CONFIG_CL_PCIE
+       if (!cl_hw->ipc_env) {
+               CL_DBG_ERROR(cl_hw, "ipc_env is NULL! 'no_dhcpcd' is missing in nvram folder\n");
+               return -1;
+       }
+#endif
+
+       /* Exits if device is already started */
+       if (WARN_ON(test_bit(CL_DEV_STARTED, &cl_hw->drv_flags)))
+               return -EBUSY;
+
+       /* Start firmware */
+       error = cl_msg_tx_start(cl_hw);
+       if (error)
+               return error;
+
+       /* Device is now started.
+        * Set CL_DEV_STARTED bit before the calls to other messages sent to
+        * firmware, to prevent them from being blocked*
+        */
+       set_bit(CL_DEV_STARTED, &cl_hw->drv_flags);
+
+       if (!cl_recovery_in_progress(cl_hw)) {
+               /* Read version */
+               error = cl_version_update(cl_hw);
+               if (error)
+                       return error;
+
+               error = cl_temperature_diff_e2p_read(cl_hw);
+               if (error)
+                       return error;
+       }
+
+       /* Set firmware debug module filter */
+       error = cl_msg_tx_dbg_set_ce_mod_filter(cl_hw, conf->ci_fw_dbg_module);
+       if (error)
+               return error;
+
+       /* Set firmware debug severity level */
+       error = cl_msg_tx_dbg_set_sev_filter(cl_hw, conf->ci_fw_dbg_severity);
+       if (error)
+               return error;
+
+       /* Set firmware rate fallbacks */
+       error = cl_msg_tx_set_rate_fallback(cl_hw);
+       if (error)
+               return error;
+
+       error = cl_msg_tx_ndp_tx_control(cl_hw,
+                                        conf->ci_ndp_tx_chain_mask,
+                                        conf->ci_ndp_tx_bw,
+                                        conf->ci_ndp_tx_format,
+                                        conf->ci_ndp_tx_num_ltf);
+       if (error)
+               return error;
+
+       /* Set default, multicast, broadcast rate */
+       cl_rate_ctrl_set_default(cl_hw);
+       cl_dyn_mcast_rate_set(cl_hw);
+       cl_dyn_bcast_rate_set(cl_hw, 0);
+
+       ieee80211_wake_queues(hw);
+
+       cl_calib_start(cl_hw);
+
+       clear_bit(CL_DEV_INIT, &cl_hw->drv_flags);
+
+       cl_edca_hw_conf(cl_hw);
+
+       return error;
+}
+
+void cl_ops_stop(struct ieee80211_hw *hw)
+{
+       /*
+        * Called after last netdevice attached to the hardware
+        * is disabled. This should turn off the hardware (at least
+        * it must turn off frame reception.)
+        * May be called right after add_interface if that rejects
+        * an interface. If you added any work onto the mac80211 workqueue
+        * you should ensure to cancel it on this callback.
+        * Must be implemented and can sleep.
+        */
+       struct cl_hw *cl_hw = hw->priv;
+
+       /* Go to idle */
+       cl_msg_tx_set_idle(cl_hw, MAC_IDLE_SYNC);
+
+       /*
+        * Clear CL_DEV_STARTED to prevent message to be sent (besides reset and start).
+        * It also blocks transmission of new packets
+        */
+       clear_bit(CL_DEV_STARTED, &cl_hw->drv_flags);
+
+       /* Stop mac80211 queues */
+       ieee80211_stop_queues(hw);
+
+       /* Send reset message to firmware */
+       cl_msg_tx_reset(cl_hw);
+
+#ifdef CONFIG_CL_PCIE
+       /* Reset IPC */
+       cl_ipc_reset(cl_hw);
+#endif
+
+       cl_hw->num_ap_started = 0;
+       cl_hw->radio_status = RADIO_STATUS_OFF;
+}
+
+static int add_interface_to_firmware(struct cl_hw *cl_hw, struct ieee80211_vif *vif, u8 vif_index)
+{
+       struct mm_add_if_cfm *add_if_cfm;
+       int ret = 0;
+
+       /* Forward the information to the firmware */
+       ret = cl_msg_tx_add_if(cl_hw, vif, vif_index);
+       if (ret)
+               return ret;
+
+       add_if_cfm = (struct mm_add_if_cfm *)(cl_hw->msg_cfm_params[MM_ADD_IF_CFM]);
+       if (!add_if_cfm)
+               return -ENOMSG;
+
+       if (add_if_cfm->status != 0) {
+               cl_dbg_verbose(cl_hw, "Status Error (%u)\n", add_if_cfm->status);
+               ret = -EIO;
+       }
+
+       cl_msg_tx_free_cfm_params(cl_hw, MM_ADD_IF_CFM);
+
+       return ret;
+}
+
+int cl_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+       /*
+        * Called when a netdevice attached to the hardware is
+        * enabled. Because it is not called for monitor mode devices, start
+        * and stop must be implemented.
+        * The driver should perform any initialization it needs before
+        * the device can be enabled. The initial configuration for the
+        * interface is given in the conf parameter.
+        * The callback may refuse to add an interface by returning a
+        * negative error code (which will be seen in userspace.)
+        * Must be implemented and can sleep.
+        */
+       struct cl_hw *cl_hw = hw->priv;
+       struct cl_chip *chip = cl_hw->chip;
+       struct cl_vif *cl_vif = (struct cl_vif *)vif->drv_priv;
+       struct ieee80211_sub_if_data *sdata = container_of(vif, struct ieee80211_sub_if_data, vif);
+       struct net_device *dev = sdata->dev;
+       u8 ac;
+
+       if (!dev)
+               return -1;
+
+       /*
+        * In recovery just send the message to firmware and exit
+        * (also make sure cl_vif already exists).
+        */
+       if (cl_recovery_in_progress(cl_hw) && cl_vif_get_by_dev(cl_hw, dev))
+               return add_interface_to_firmware(cl_hw, vif, cl_vif->vif_index);
+
+       cl_vif->cl_hw = cl_hw;
+       cl_vif->vif = vif;
+       cl_vif->dev = dev;
+
+       if (chip->conf->ce_production_mode)
+               cl_vif->tx_en = true;
+
+       /* Copy dev ops and change ndo_start_xmit to point at cl_tx_start() */
+       cl_vif->orig_dev_ops = dev->netdev_ops;
+       memcpy(&cl_vif->dev_ops, dev->netdev_ops, sizeof(struct net_device_ops));
+       cl_vif->dev_ops.ndo_start_xmit = cl_tx_start;
+       dev->netdev_ops = &cl_vif->dev_ops;
+
+       if (chip->idx == CHIP0) {
+               if (cl_hw_is_tcv0(cl_hw))
+                       sscanf(dev->name, CL_IFACE_PREFIX "0_%hhu", &cl_vif->vif_index);
+               else
+                       sscanf(dev->name, CL_IFACE_PREFIX "1_%hhu", &cl_vif->vif_index);
+       } else {
+               if (cl_hw_is_tcv0(cl_hw))
+                       sscanf(dev->name, CL_IFACE_PREFIX "2_%hhu", &cl_vif->vif_index);
+               else
+                       sscanf(dev->name, CL_IFACE_PREFIX "3_%hhu", &cl_vif->vif_index);
+       }
+
+       if (add_interface_to_firmware(cl_hw, vif, cl_vif->vif_index))
+               return -1;
+
+       if (vif->type != NL80211_IFTYPE_STATION)
+               vif->cab_queue = CL_HWQ_VO;
+
+       cl_vif_add(cl_hw, cl_vif);
+
+       for (ac = 0; ac < AC_MAX; ac++)
+               vif->hw_queue[ac] = cl_ac2hwq[ac];
+
+       if (vif->type == NL80211_IFTYPE_MESH_POINT) {
+               tasklet_init(&cl_hw->tx_mesh_bcn_task, cl_tx_bcn_mesh_task,
+                            (unsigned long)cl_vif);
+               cl_radio_on(cl_hw);
+               cl_vif->tx_en = true;
+       }
+
+       /* Set active state in station mode after ifconfig down and up */
+       if (cl_radio_is_on(cl_hw) && vif->type == NL80211_IFTYPE_STATION)
+               cl_msg_tx_set_idle(cl_hw, MAC_ACTIVE);
+
+       return 0;
+}
+
+void cl_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+       /*
+        * Notifies a driver that an interface is going down.
+        * The stop callback is called after this if it is the last interface
+        * and no monitor interfaces are present.
+        * When all interfaces are removed, the MAC address in the hardware
+        * must be cleared so the device no longer acknowledges packets,
+        * the mac_addr member of the conf structure is, however, set to the
+        * MAC address of the device going away.
+        * Hence, this callback must be implemented. It can sleep.
+        */
+       struct cl_hw *cl_hw = hw->priv;
+       struct cl_vif *cl_vif = (struct cl_vif *)vif->drv_priv;
+
+       if (vif->type == NL80211_IFTYPE_MESH_POINT)
+               tasklet_kill(&cl_hw->tx_mesh_bcn_task);
+
+       if (!cl_recovery_in_progress(cl_hw)) {
+               cl_vif_remove(cl_hw, cl_vif);
+               cl_msg_tx_remove_if(cl_hw, cl_vif->vif_index);
+       } else {
+               cl_vif_remove(cl_hw, cl_vif);
+       }
+
+       /* Return netdev_ops back to it's original configuration */
+       cl_vif->dev->netdev_ops = cl_vif->orig_dev_ops;
+
+       cl_vif->cl_hw = NULL;
+       cl_vif->vif = NULL;
+       cl_vif->dev = NULL;
+}
+
+static int cl_ops_conf_change_channel(struct ieee80211_hw *hw)
+{
+       struct cl_hw *cl_hw = hw->priv;
+       struct cl_chip *chip = cl_hw->chip;
+       struct cfg80211_chan_def *chandef = &hw->conf.chandef;
+       enum nl80211_chan_width width = chandef->width;
+       u32 primary = chandef->chan->center_freq;
+       u32 center = chandef->center_freq1;
+       u8 channel = ieee80211_frequency_to_channel(primary);
+       u8 bw;
+
+       if (!test_bit(CL_DEV_STARTED, &cl_hw->drv_flags) ||
+           test_bit(CL_DEV_INIT, &cl_hw->drv_flags))
+               return -EBUSY;
+
+       /* WA: for the first set-channel in production mode use the nvram values */
+       if (chip->conf->ce_production_mode && !cl_hw->chandef_set) {
+               cl_hw->chandef_set = true;
+               cl_chandef_get_default(cl_hw, &width, &primary, &center);
+               channel = cl_hw->conf->ha_channel;
+       }
+
+       bw = width_to_bw(width);
+
+       if (cl_hw->channel == channel &&
+           cl_hw->bw == bw &&
+           cl_hw->primary_freq == primary &&
+           cl_hw->center_freq == center)
+               goto check_cac;
+
+       /*
+        * Flush the pending data to ensure that we will finish the pending
+        * transmissions before changing the channel
+        */
+       cl_ops_flush(hw, NULL, -1, false);
+
+       if (cl_band_is_6g(cl_hw))
+               cl_netlink_send_event_co_locate_update(cl_hw);
+
+       if (cl_msg_tx_set_channel(cl_hw, channel, bw, primary, center))
+               return -EIO;
+
+check_cac:
+       /*
+        * TODO: This callback is being spawned even in STA mode, moreover,
+        * "start_ap" comes later - it is unclear whether we are an AP at this
+        * stage. Likely, may be solved by moving "force_cac_*" states to beginning
+        * of "start_ap", but the request should stay in current callback
+        */
+       if (!cl_band_is_5g(cl_hw))
+               return 0;
+
+       /*
+        * Radar listening may occur at DFS channels during in-service mode,
+        * so CAC may clear the channels, but radar listening should be
+        * still active, and should start it as soon as we can.
+        */
+       if (hw->conf.radar_enabled) {
+               /* If channel policy demans to be in CAC - need to request it */
+               if (!cl_dfs_is_in_cac(cl_hw) &&
+                   chandef->chan->dfs_state == NL80211_DFS_USABLE)
+                       cl_dfs_request_cac(cl_hw, true);
+
+               if (!cl_dfs_radar_listening(cl_hw))
+                       cl_dfs_radar_listen_start(cl_hw);
+       } else {
+               /*
+                * No sense to continue be in silent mode if the channel was
+                * cleared
+                */
+               if (cl_dfs_is_in_cac(cl_hw) &&
+                   chandef->chan->dfs_state == NL80211_DFS_AVAILABLE)
+                       cl_dfs_request_cac(cl_hw, false);
+
+               if (cl_dfs_radar_listening(cl_hw))
+                       cl_dfs_radar_listen_end(cl_hw);
+       }
+
+       /*
+        * We have just finished channel switch.
+        * Now, check what to do with CAC.
+        */
+       if (cl_dfs_requested_cac(cl_hw))
+               cl_dfs_force_cac_start(cl_hw);
+       else if (cl_dfs_is_in_cac(cl_hw))
+               cl_dfs_force_cac_end(cl_hw);
+
+       return 0;
+}
+
+int cl_ops_config(struct ieee80211_hw *hw, u32 changed)
+{
+       /*
+        * Handler for configuration requests. IEEE 802.11 code calls this
+        * function to change hardware configuration, e.g., channel.
+        * This function should never fail but returns a negative error code
+        * if it does. The callback can sleep
+        */
+       int error = 0;
+
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL)
+               error = cl_ops_conf_change_channel(hw);
+
+       return error;
+}
+
+/*
+ * @bss_info_changed: Handler for configuration requests related to BSS
+ *  parameters that may vary during BSS's lifespan, and may affect low
+ *  level driver (e.g. assoc/disassoc status, erp parameters).
+ *  This function should not be used if no BSS has been set, unless
+ *  for association indication. The @changed parameter indicates which
+ *  of the bss parameters has changed when a call is made. The callback
+ *  can sleep.
+ */
+void cl_ops_bss_info_changed(struct ieee80211_hw *hw,
+                            struct ieee80211_vif *vif,
+                            struct ieee80211_bss_conf *info,
+                            u32 changed)
+{
+       struct cl_hw *cl_hw = hw->priv;
+       struct cl_vif *cl_vif = (struct cl_vif *)vif->drv_priv;
+
+       if (changed & BSS_CHANGED_ASSOC) {
+               if (cl_msg_tx_set_associated(cl_hw, info))
+                       return;
+       }
+
+       if (changed & BSS_CHANGED_BSSID) {
+               if (cl_msg_tx_set_bssid(cl_hw, info->bssid, cl_vif->vif_index))
+                       return;
+       }
+
+       if (changed & BSS_CHANGED_BEACON_INT) {
+               if (vif->type == NL80211_IFTYPE_AP ||
+                   cl_hw->iface_conf == CL_IFCONF_MESH_ONLY) {
+                       if (cl_msg_tx_set_beacon_int(cl_hw, info->beacon_int,
+                                                    cl_vif->vif_index))
+                               return;
+                       if (cl_msg_tx_dtim(cl_hw, info->dtim_period))
+                               return;
+               }
+
+               if (vif->type == NL80211_IFTYPE_MESH_POINT &&
+                   cl_hw->iface_conf == CL_IFCONF_MESH_AP)
+                       cl_hw->mesh_tbtt_div = (info->beacon_int /
+                                           cl_hw->conf->ha_beacon_int);
+       }
+
+       if (changed & BSS_CHANGED_BASIC_RATES) {
+               int shift = hw->wiphy->bands[hw->conf.chandef.chan->band]->bitrates[0].hw_value;
+
+               if (cl_msg_tx_set_basic_rates(cl_hw, info->basic_rates << shift))
+                       return;
+               /* TODO: check if cl_msg_tx_set_mode() should be called */
+       }
+
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               /*
+                * We must be in 11g mode here
+                * TODO: we can add a check on the mode
+                */
+               if (cl_msg_tx_set_slottime(cl_hw, info->use_short_slot))
+                       return;
+       }
+
+       if (changed & BSS_CHANGED_BANDWIDTH)
+               cl_wrs_api_bss_set_bw(cl_hw, width_to_bw(info->chandef.width));
+}
+
+int cl_ops_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+       struct cl_hw *cl_hw = hw->priv;
+       u8 num_ap = cl_tcv_config_get_num_ap(cl_hw);
+
+       /*
+        * Increase num_ap_started counter and turn radio on only after
+        * all AP's were started.
+        */
+       cl_hw->num_ap_started++;
+
+       if (num_ap == cl_hw->num_ap_started &&
+           cl_hw->conf->ce_radio_on) {
+               cl_radio_on(cl_hw);
+
+               return 0;
+       }
+
+       /*
+        * Set active state when cl_ops_start_ap() is called not during first driver start
+        * but rather after removing all interfaces and then doing up again to one interface.
+        */
+       if (cl_radio_is_on(cl_hw) && !cl_recovery_in_progress(cl_hw))
+               cl_msg_tx_set_idle(cl_hw, MAC_ACTIVE);
+
+       return 0;
+}
+
+void cl_ops_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+       struct cl_hw *cl_hw = hw->priv;
+
+       cl_hw->num_ap_started--;
+}
+
+u64 cl_ops_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list)
+{
+       return netdev_hw_addr_list_count(mc_list);
+}
+
+void cl_ops_configure_filter(struct ieee80211_hw *hw, u32 changed_flags,
+                            u32 *total_flags, u64 multicast)
+{
+       /*
+        * configure_filter: Configure the device's RX filter.
+        * See the section "Frame filtering" for more information.
+        * This callback must be implemented and can sleep.
+        */
+       struct cl_hw *cl_hw = hw->priv;
+
+       cl_dbg_trace(cl_hw, "total_flags = 0x%08x\n", *total_flags);
+
+       /*
+        * Reset our filter flags since our start/stop ops reset
+        * the programmed settings
+        */
+       if (!test_bit(CL_DEV_STARTED, &cl_hw->drv_flags)) {
+               *total_flags = 0;
+               return;
+       }
+
+       if (multicast)
+               *total_flags |= FIF_ALLMULTI;
+       else
+               *total_flags &= ~FIF_ALLMULTI;
+
+       /* TODO optimize with changed_flags vs multicast */
+       cl_msg_tx_set_filter(cl_hw, *total_flags, false);
+
+       /* TODO update total_flags with truly set flags */
+       *total_flags &= ~(1 << 31);
+}
+
+int cl_ops_set_key(struct ieee80211_hw *hw,
+                  enum set_key_cmd cmd,
+                  struct ieee80211_vif *vif,
+                  struct ieee80211_sta *sta,
+                  struct ieee80211_key_conf *key)
+{
+       struct cl_hw *cl_hw = hw->priv;
+
+       return cl_key_set(cl_hw, cmd, vif, sta, key);
+}
+
+void cl_ops_sw_scan_start(struct ieee80211_hw *hw,
+                         struct ieee80211_vif *vif,
+                         const u8 *mac_addr)
+{
+       struct cl_hw *cl_hw = hw->priv;
+
+       if (cl_hw->conf->ce_radio_on &&
+           cl_radio_is_off(cl_hw) &&
+           vif->type == NL80211_IFTYPE_STATION)
+               cl_radio_on(cl_hw);
+}
+
+int cl_ops_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+                    enum ieee80211_sta_state old_state, enum ieee80211_sta_state new_state)
+{
+       struct cl_hw *cl_hw = hw->priv;
+       int error = 0;
+
+       if (old_state == new_state)
+               return 0;
+
+       if (old_state == IEEE80211_STA_NOTEXIST &&
+           new_state == IEEE80211_STA_NONE) {
+               struct sta_info *stainfo = container_of(sta, struct sta_info, sta);
+
+               cl_sta_init_stainfo(cl_hw, stainfo);
+       } else if (old_state == IEEE80211_STA_AUTH &&
+                  new_state == IEEE80211_STA_ASSOC) {
+               error = cl_sta_add(cl_hw, vif, sta);
+       } else if (old_state == IEEE80211_STA_ASSOC &&
+                  new_state == IEEE80211_STA_AUTH) {
+               cl_sta_remove(cl_hw, vif, sta);
+       } else if (old_state == IEEE80211_STA_ASSOC &&
+                  new_state == IEEE80211_STA_AUTHORIZED) {
+               /* Do nothing, yet */
+       }
+
+       return error;
+}
+
+void cl_ops_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                      enum sta_notify_cmd cmd, struct ieee80211_sta *sta)
+{
+       struct cl_hw *cl_hw = (struct cl_hw *)hw->priv;
+       struct cl_sta *cl_sta = IEEE80211_STA_TO_CL_STA(sta);
+       bool is_ps = (bool)!cmd;
+
+       cl_sta_ps_notify(cl_hw, cl_sta, is_ps);
+}
+
+int cl_ops_conf_tx(struct ieee80211_hw *hw,
+                  struct ieee80211_vif *vif,
+                  u16 ac_queue,
+                  const struct ieee80211_tx_queue_params *params)
+{
+       /*
+        * Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
+        * bursting) for a hardware TX queue.
+        * Returns a negative error code on failure.
+        * The callback can sleep.
+        */
+
+       /* We only handle STA edca here */
+       if (vif->type == NL80211_IFTYPE_STATION) {
+               struct cl_hw *cl_hw = hw->priv;
+               struct ieee80211_he_mu_edca_param_ac_rec mu_edca = {0};
+               struct edca_params edca_params = {
+                       .aifsn = (u8)(params->aifs),
+                       .cw_min = (u8)(ilog2(params->cw_min + 1)),
+                       .cw_max = (u8)(ilog2(params->cw_max + 1)),
+                       .txop = (u8)(params->txop)
+               };
+
+               if (cl_hw->conf->ce_wireless_mode > WIRELESS_MODE_HT_VHT)
+                       memcpy(&mu_edca, &params->mu_edca_param_rec, sizeof(mu_edca));
+
+               cl_edca_set(cl_hw, cl_ac2edca[ac_queue], &edca_params, &mu_edca);
+       }
+       return 0;
+}
+
+void cl_ops_sta_rc_update(struct ieee80211_hw *hw,
+                         struct ieee80211_vif *vif,
+                         struct ieee80211_sta *sta,
+                         u32 changed)
+{
+       struct cl_hw *cl_hw = (struct cl_hw *)hw->priv;
+
+       if (changed & IEEE80211_RC_BW_CHANGED)
+               cl_wrs_api_bw_changed(cl_hw, sta, sta->bandwidth);
+
+       if (changed & IEEE80211_RC_SMPS_CHANGED) {
+               struct sta_info *stainfo = container_of(sta, struct sta_info, sta);
+
+               cl_wrs_api_set_smps_mode(cl_hw, sta, stainfo->cur_max_bandwidth);
+       }
+
+       WARN_ON(sta->rx_nss == 0);
+       if (changed & IEEE80211_RC_NSS_CHANGED) {
+               u8 nss = min_t(u8, sta->rx_nss, WRS_SS_MAX) - 1;
+
+               cl_wrs_api_nss_changed(cl_hw, sta, nss);
+       }
+}
+
+int cl_ops_ampdu_action(struct ieee80211_hw *hw,
+                       struct ieee80211_vif *vif,
+                       struct ieee80211_ampdu_params *params)
+{
+       struct cl_hw *cl_hw = (struct cl_hw *)hw->priv;
+       struct cl_sta *cl_sta = IEEE80211_STA_TO_CL_STA(params->sta);
+       int ret = 0;
+
+       switch (params->action) {
+       case IEEE80211_AMPDU_RX_START:
+               ret = cl_ampdu_rx_start(cl_hw, cl_sta, params->tid,
+                                       params->ssn, params->buf_size);
+               break;
+       case IEEE80211_AMPDU_RX_STOP:
+               cl_ampdu_rx_stop(cl_hw, cl_sta, params->tid);
+               break;
+       case IEEE80211_AMPDU_TX_START:
+               ret = cl_ampdu_tx_start(cl_hw, vif, cl_sta, params->tid,
+                                       params->ssn);
+               break;
+       case IEEE80211_AMPDU_TX_OPERATIONAL:
+               ret = cl_ampdu_tx_operational(cl_hw, cl_sta, params->tid,
+                                             params->buf_size, params->amsdu);
+               break;
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+               ret = cl_ampdu_tx_stop(cl_hw, vif, params->action, cl_sta,
+                                      params->tid);
+               break;
+       default:
+               pr_warn("Error: Unknown AMPDU action (%d)\n", params->action);
+       }
+
+       return ret;
+}
+
+int cl_ops_post_channel_switch(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif)
+{
+       /* TODO: Handle event */
+
+       return 0;
+}
+
+void cl_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop)
+{
+       struct cl_hw *cl_hw = hw->priv;
+       int flush_duration;
+
+       if (test_bit(CL_DEV_HW_RESTART, &cl_hw->drv_flags)) {
+               cl_dbg_verbose(cl_hw, ": bypassing (CL_DEV_HW_RESTART set)\n");
+               return;
+       }
+
+       /* Wait for a maximum time of 200ms until all pending frames are flushed */
+       for (flush_duration = 0; flush_duration < 200; flush_duration++) {
+               if (!cl_txq_frames_pending(cl_hw))
+                       return;
+
+               /* Lets sleep and hope for the best */
+               usleep_range(1000, 2000);
+       }
+}
+
+bool cl_ops_tx_frames_pending(struct ieee80211_hw *hw)
+{
+       struct cl_hw *cl_hw = hw->priv;
+
+       return cl_txq_frames_pending(cl_hw);
+}
+
+void cl_ops_reconfig_complete(struct ieee80211_hw *hw,
+                             enum ieee80211_reconfig_type reconfig_type)
+{
+       struct cl_hw *cl_hw = hw->priv;
+
+       cl_recovery_reconfig_complete(cl_hw);
+}
+
+int cl_ops_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int *dbm)
+{
+       struct cl_hw *cl_hw = hw->priv;
+
+       if (cl_hw->phy_data_info.data)
+               *dbm = cl_power_get_max(cl_hw);
+       else
+               *dbm = 0;
+
+       return 0;
+}
+
+int cl_ops_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+       return 0;
+}
+
+static void cl_ops_mgd_assoc(struct cl_hw *cl_hw, struct ieee80211_vif *vif)
+{
+       struct ieee80211_sub_if_data *sdata = container_of(vif, struct ieee80211_sub_if_data, vif);
+       struct cl_vif *cl_vif = (struct cl_vif *)vif->drv_priv;
+       struct ieee80211_sta *sta = ieee80211_find_sta(vif, sdata->u.mgd.bssid);
+
+       if (!sta) {
+               /* Should never happen */
+               cl_dbg_verbose(cl_hw, "sta is NULL !!!\n");
+               return;
+       }
+
+       cl_sta_mgd_add(cl_hw, cl_vif, sta);
+
+       if (cl_hw->iface_conf == CL_IFCONF_REPEATER) {
+               cl_vif_ap_tx_enable(cl_hw, true);
+               set_bit(CL_DEV_REPEATER, &cl_hw->drv_flags);
+       }
+}
+
+static void cl_ops_mgd_disassoc(struct cl_hw *cl_hw)
+{
+       if (cl_hw->iface_conf == CL_IFCONF_REPEATER) {
+               cl_sta_disassociate_ap_iface(cl_hw);
+               cl_vif_ap_tx_enable(cl_hw, false);
+               clear_bit(CL_DEV_REPEATER, &cl_hw->drv_flags);
+       }
+}
+
+void cl_ops_event_callback(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                          const struct ieee80211_event *event)
+{
+       struct cl_hw *cl_hw = hw->priv;
+
+       if (event->type == MLME_EVENT) {
+               if (event->u.mlme.data == ASSOC_EVENT &&
+                   event->u.mlme.status == MLME_SUCCESS)
+                       cl_ops_mgd_assoc(cl_hw, vif);
+               else if (event->u.mlme.data == DEAUTH_TX_EVENT ||
+                        event->u.mlme.data == DEAUTH_RX_EVENT)
+                       cl_ops_mgd_disassoc(cl_hw);
+       }
+}
+
+/* This function is required for PS flow - do not remove */
+int cl_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set)
+{
+       return 0;
+}
--
2.30.0

________________________________
The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any retransmission, dissemination, copying or other use of, or taking of any action in reliance upon this information is prohibited. If you received this in error, please contact the sender and delete the material from any computer. Nothing contained herein shall be deemed as a representation, warranty or a commitment by Celeno. No warranties are expressed or implied, including, but not limited to, any implied warranties of non-infringement, merchantability and fitness for a particular purpose.
________________________________





[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux