| /* |
| * Copyright (c) 2014 Redpine Signals Inc. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include "rsi_mgmt.h" |
| #include "rsi_common.h" |
| #include "rsi_hal.h" |
| #include "rsi_coex.h" |
| |
| /** |
| * rsi_determine_min_weight_queue() - This function determines the queue with |
| * the min weight. |
| * @common: Pointer to the driver private structure. |
| * |
| * Return: q_num: Corresponding queue number. |
| */ |
| static u8 rsi_determine_min_weight_queue(struct rsi_common *common) |
| { |
| struct wmm_qinfo *tx_qinfo = common->tx_qinfo; |
| u32 q_len = 0; |
| u8 ii = 0; |
| |
| for (ii = 0; ii < NUM_EDCA_QUEUES; ii++) { |
| q_len = skb_queue_len(&common->tx_queue[ii]); |
| if ((tx_qinfo[ii].pkt_contended) && q_len) { |
| common->min_weight = tx_qinfo[ii].weight; |
| break; |
| } |
| } |
| return ii; |
| } |
| |
| /** |
| * rsi_recalculate_weights() - This function recalculates the weights |
| * corresponding to each queue. |
| * @common: Pointer to the driver private structure. |
| * |
| * Return: recontend_queue bool variable |
| */ |
| static bool rsi_recalculate_weights(struct rsi_common *common) |
| { |
| struct wmm_qinfo *tx_qinfo = common->tx_qinfo; |
| bool recontend_queue = false; |
| u8 ii = 0; |
| u32 q_len = 0; |
| |
| for (ii = 0; ii < NUM_EDCA_QUEUES; ii++) { |
| q_len = skb_queue_len(&common->tx_queue[ii]); |
| /* Check for the need of contention */ |
| if (q_len) { |
| if (tx_qinfo[ii].pkt_contended) { |
| tx_qinfo[ii].weight = |
| ((tx_qinfo[ii].weight > common->min_weight) ? |
| tx_qinfo[ii].weight - common->min_weight : 0); |
| } else { |
| tx_qinfo[ii].pkt_contended = 1; |
| tx_qinfo[ii].weight = tx_qinfo[ii].wme_params; |
| recontend_queue = true; |
| } |
| } else { /* No packets so no contention */ |
| tx_qinfo[ii].weight = 0; |
| tx_qinfo[ii].pkt_contended = 0; |
| } |
| } |
| |
| return recontend_queue; |
| } |
| |
| /** |
| * rsi_get_num_pkts_dequeue() - This function determines the number of |
| * packets to be dequeued based on the number |
| * of bytes calculated using txop. |
| * |
| * @common: Pointer to the driver private structure. |
| * @q_num: the queue from which pkts have to be dequeued |
| * |
| * Return: pkt_num: Number of pkts to be dequeued. |
| */ |
| static u32 rsi_get_num_pkts_dequeue(struct rsi_common *common, u8 q_num) |
| { |
| struct rsi_hw *adapter = common->priv; |
| struct sk_buff *skb; |
| u32 pkt_cnt = 0; |
| s16 txop = common->tx_qinfo[q_num].txop * 32; |
| __le16 r_txop; |
| struct ieee80211_rate rate; |
| struct ieee80211_hdr *wh; |
| struct ieee80211_vif *vif; |
| |
| rate.bitrate = RSI_RATE_MCS0 * 5 * 10; /* Convert to Kbps */ |
| if (q_num == VI_Q) |
| txop = ((txop << 5) / 80); |
| |
| if (skb_queue_len(&common->tx_queue[q_num])) |
| skb = skb_peek(&common->tx_queue[q_num]); |
| else |
| return 0; |
| |
| do { |
| wh = (struct ieee80211_hdr *)skb->data; |
| vif = rsi_get_vif(adapter, wh->addr2); |
| r_txop = ieee80211_generic_frame_duration(adapter->hw, |
| vif, |
| common->band, |
| skb->len, &rate); |
| txop -= le16_to_cpu(r_txop); |
| pkt_cnt += 1; |
| /*checking if pkts are still there*/ |
| if (skb_queue_len(&common->tx_queue[q_num]) - pkt_cnt) |
| skb = skb->next; |
| else |
| break; |
| |
| } while (txop > 0); |
| |
| return pkt_cnt; |
| } |
| |
| /** |
| * rsi_core_determine_hal_queue() - This function determines the queue from |
| * which packet has to be dequeued. |
| * @common: Pointer to the driver private structure. |
| * |
| * Return: q_num: Corresponding queue number on success. |
| */ |
| static u8 rsi_core_determine_hal_queue(struct rsi_common *common) |
| { |
| bool recontend_queue = false; |
| u32 q_len = 0; |
| u8 q_num = INVALID_QUEUE; |
| u8 ii = 0; |
| |
| if (skb_queue_len(&common->tx_queue[MGMT_BEACON_Q])) { |
| q_num = MGMT_BEACON_Q; |
| return q_num; |
| } |
| if (skb_queue_len(&common->tx_queue[MGMT_SOFT_Q])) { |
| if (!common->mgmt_q_block) |
| q_num = MGMT_SOFT_Q; |
| return q_num; |
| } |
| |
| if (common->hw_data_qs_blocked) |
| return q_num; |
| |
| if (common->pkt_cnt != 0) { |
| --common->pkt_cnt; |
| return common->selected_qnum; |
| } |
| |
| get_queue_num: |
| recontend_queue = false; |
| |
| q_num = rsi_determine_min_weight_queue(common); |
| |
| ii = q_num; |
| |
| /* Selecting the queue with least back off */ |
| for (; ii < NUM_EDCA_QUEUES; ii++) { |
| q_len = skb_queue_len(&common->tx_queue[ii]); |
| if (((common->tx_qinfo[ii].pkt_contended) && |
| (common->tx_qinfo[ii].weight < common->min_weight)) && |
| q_len) { |
| common->min_weight = common->tx_qinfo[ii].weight; |
| q_num = ii; |
| } |
| } |
| |
| if (q_num < NUM_EDCA_QUEUES) |
| common->tx_qinfo[q_num].pkt_contended = 0; |
| |
| /* Adjust the back off values for all queues again */ |
| recontend_queue = rsi_recalculate_weights(common); |
| |
| q_len = skb_queue_len(&common->tx_queue[q_num]); |
| if (!q_len) { |
| /* If any queues are freshly contended and the selected queue |
| * doesn't have any packets |
| * then get the queue number again with fresh values |
| */ |
| if (recontend_queue) |
| goto get_queue_num; |
| |
| return INVALID_QUEUE; |
| } |
| |
| common->selected_qnum = q_num; |
| q_len = skb_queue_len(&common->tx_queue[q_num]); |
| |
| if (q_num == VO_Q || q_num == VI_Q) { |
| common->pkt_cnt = rsi_get_num_pkts_dequeue(common, q_num); |
| common->pkt_cnt -= 1; |
| } |
| |
| return q_num; |
| } |
| |
| /** |
| * rsi_core_queue_pkt() - This functions enqueues the packet to the queue |
| * specified by the queue number. |
| * @common: Pointer to the driver private structure. |
| * @skb: Pointer to the socket buffer structure. |
| * |
| * Return: None. |
| */ |
| static void rsi_core_queue_pkt(struct rsi_common *common, |
| struct sk_buff *skb) |
| { |
| u8 q_num = skb->priority; |
| if (q_num >= NUM_SOFT_QUEUES) { |
| rsi_dbg(ERR_ZONE, "%s: Invalid Queue Number: q_num = %d\n", |
| __func__, q_num); |
| dev_kfree_skb(skb); |
| return; |
| } |
| |
| skb_queue_tail(&common->tx_queue[q_num], skb); |
| } |
| |
| /** |
| * rsi_core_dequeue_pkt() - This functions dequeues the packet from the queue |
| * specified by the queue number. |
| * @common: Pointer to the driver private structure. |
| * @q_num: Queue number. |
| * |
| * Return: Pointer to sk_buff structure. |
| */ |
| static struct sk_buff *rsi_core_dequeue_pkt(struct rsi_common *common, |
| u8 q_num) |
| { |
| if (q_num >= NUM_SOFT_QUEUES) { |
| rsi_dbg(ERR_ZONE, "%s: Invalid Queue Number: q_num = %d\n", |
| __func__, q_num); |
| return NULL; |
| } |
| |
| return skb_dequeue(&common->tx_queue[q_num]); |
| } |
| |
| /** |
| * rsi_core_qos_processor() - This function is used to determine the wmm queue |
| * based on the backoff procedure. Data packets are |
| * dequeued from the selected hal queue and sent to |
| * the below layers. |
| * @common: Pointer to the driver private structure. |
| * |
| * Return: None. |
| */ |
| void rsi_core_qos_processor(struct rsi_common *common) |
| { |
| struct rsi_hw *adapter = common->priv; |
| struct sk_buff *skb; |
| unsigned long tstamp_1, tstamp_2; |
| u8 q_num; |
| int status; |
| |
| tstamp_1 = jiffies; |
| while (1) { |
| q_num = rsi_core_determine_hal_queue(common); |
| rsi_dbg(DATA_TX_ZONE, |
| "%s: Queue number = %d\n", __func__, q_num); |
| |
| if (q_num == INVALID_QUEUE) { |
| rsi_dbg(DATA_TX_ZONE, "%s: No More Pkt\n", __func__); |
| break; |
| } |
| if (common->hibernate_resume) |
| break; |
| |
| mutex_lock(&common->tx_lock); |
| |
| status = adapter->check_hw_queue_status(adapter, q_num); |
| if ((status <= 0)) { |
| mutex_unlock(&common->tx_lock); |
| break; |
| } |
| |
| if ((q_num < MGMT_SOFT_Q) && |
| ((skb_queue_len(&common->tx_queue[q_num])) <= |
| MIN_DATA_QUEUE_WATER_MARK)) { |
| if (ieee80211_queue_stopped(adapter->hw, WME_AC(q_num))) |
| ieee80211_wake_queue(adapter->hw, |
| WME_AC(q_num)); |
| } |
| |
| skb = rsi_core_dequeue_pkt(common, q_num); |
| if (skb == NULL) { |
| rsi_dbg(ERR_ZONE, "skb null\n"); |
| mutex_unlock(&common->tx_lock); |
| break; |
| } |
| if (q_num == MGMT_BEACON_Q) { |
| status = rsi_send_pkt_to_bus(common, skb); |
| dev_kfree_skb(skb); |
| } else { |
| #ifdef CONFIG_RSI_COEX |
| if (common->coex_mode > 1) { |
| status = rsi_coex_send_pkt(common, skb, |
| RSI_WLAN_Q); |
| } else { |
| #endif |
| if (q_num == MGMT_SOFT_Q) |
| status = rsi_send_mgmt_pkt(common, skb); |
| else |
| status = rsi_send_data_pkt(common, skb); |
| #ifdef CONFIG_RSI_COEX |
| } |
| #endif |
| } |
| |
| if (status) { |
| mutex_unlock(&common->tx_lock); |
| break; |
| } |
| |
| common->tx_stats.total_tx_pkt_send[q_num]++; |
| |
| tstamp_2 = jiffies; |
| mutex_unlock(&common->tx_lock); |
| |
| if (time_after(tstamp_2, tstamp_1 + (300 * HZ) / 1000)) |
| schedule(); |
| } |
| } |
| |
| struct rsi_sta *rsi_find_sta(struct rsi_common *common, u8 *mac_addr) |
| { |
| int i; |
| |
| for (i = 0; i < common->max_stations; i++) { |
| if (!common->stations[i].sta) |
| continue; |
| if (!(memcmp(common->stations[i].sta->addr, |
| mac_addr, ETH_ALEN))) |
| return &common->stations[i]; |
| } |
| return NULL; |
| } |
| |
| struct ieee80211_vif *rsi_get_vif(struct rsi_hw *adapter, u8 *mac) |
| { |
| struct ieee80211_vif *vif; |
| int i; |
| |
| for (i = 0; i < RSI_MAX_VIFS; i++) { |
| vif = adapter->vifs[i]; |
| if (!vif) |
| continue; |
| if (!memcmp(vif->addr, mac, ETH_ALEN)) |
| return vif; |
| } |
| return NULL; |
| } |
| |
| /** |
| * rsi_core_xmit() - This function transmits the packets received from mac80211 |
| * @common: Pointer to the driver private structure. |
| * @skb: Pointer to the socket buffer structure. |
| * |
| * Return: None. |
| */ |
| void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb) |
| { |
| struct rsi_hw *adapter = common->priv; |
| struct ieee80211_tx_info *info; |
| struct skb_info *tx_params; |
| struct ieee80211_hdr *wh = NULL; |
| struct ieee80211_vif *vif; |
| u8 q_num, tid = 0; |
| struct rsi_sta *rsta = NULL; |
| |
| if ((!skb) || (!skb->len)) { |
| rsi_dbg(ERR_ZONE, "%s: Null skb/zero Length packet\n", |
| __func__); |
| goto xmit_fail; |
| } |
| if (common->fsm_state != FSM_MAC_INIT_DONE) { |
| rsi_dbg(ERR_ZONE, "%s: FSM state not open\n", __func__); |
| goto xmit_fail; |
| } |
| if (common->wow_flags & RSI_WOW_ENABLED) { |
| rsi_dbg(ERR_ZONE, |
| "%s: Blocking Tx_packets when WOWLAN is enabled\n", |
| __func__); |
| goto xmit_fail; |
| } |
| |
| info = IEEE80211_SKB_CB(skb); |
| tx_params = (struct skb_info *)info->driver_data; |
| /* info->driver_data and info->control part of union so make copy */ |
| tx_params->have_key = !!info->control.hw_key; |
| wh = (struct ieee80211_hdr *)&skb->data[0]; |
| tx_params->sta_id = 0; |
| |
| vif = rsi_get_vif(adapter, wh->addr2); |
| if (!vif) |
| goto xmit_fail; |
| tx_params->vif = vif; |
| tx_params->vap_id = ((struct vif_priv *)vif->drv_priv)->vap_id; |
| if ((ieee80211_is_mgmt(wh->frame_control)) || |
| (ieee80211_is_ctl(wh->frame_control)) || |
| (ieee80211_is_qos_nullfunc(wh->frame_control))) { |
| if (ieee80211_is_assoc_req(wh->frame_control) || |
| ieee80211_is_reassoc_req(wh->frame_control)) { |
| struct ieee80211_bss_conf *bss = &vif->bss_conf; |
| |
| common->eapol4_confirm = false; |
| rsi_hal_send_sta_notify_frame(common, |
| RSI_IFTYPE_STATION, |
| STA_CONNECTED, bss->bssid, |
| bss->qos, bss->aid, 0, |
| vif); |
| } |
| |
| q_num = MGMT_SOFT_Q; |
| skb->priority = q_num; |
| |
| if (rsi_prepare_mgmt_desc(common, skb)) { |
| rsi_dbg(ERR_ZONE, "Failed to prepare desc\n"); |
| goto xmit_fail; |
| } |
| } else { |
| if (ieee80211_is_data_qos(wh->frame_control)) { |
| u8 *qos = ieee80211_get_qos_ctl(wh); |
| |
| tid = *qos & IEEE80211_QOS_CTL_TID_MASK; |
| skb->priority = TID_TO_WME_AC(tid); |
| } else { |
| tid = IEEE80211_NONQOS_TID; |
| skb->priority = BE_Q; |
| } |
| |
| q_num = skb->priority; |
| tx_params->tid = tid; |
| |
| if (((vif->type == NL80211_IFTYPE_AP) || |
| (vif->type == NL80211_IFTYPE_P2P_GO)) && |
| (!is_broadcast_ether_addr(wh->addr1)) && |
| (!is_multicast_ether_addr(wh->addr1))) { |
| rsta = rsi_find_sta(common, wh->addr1); |
| if (!rsta) |
| goto xmit_fail; |
| tx_params->sta_id = rsta->sta_id; |
| } else { |
| tx_params->sta_id = 0; |
| } |
| |
| if (rsta) { |
| /* Start aggregation if not done for this tid */ |
| if (!rsta->start_tx_aggr[tid]) { |
| rsta->start_tx_aggr[tid] = true; |
| ieee80211_start_tx_ba_session(rsta->sta, |
| tid, 0); |
| } |
| } |
| if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { |
| q_num = MGMT_SOFT_Q; |
| skb->priority = q_num; |
| } |
| if (rsi_prepare_data_desc(common, skb)) { |
| rsi_dbg(ERR_ZONE, "Failed to prepare data desc\n"); |
| goto xmit_fail; |
| } |
| } |
| |
| if ((q_num < MGMT_SOFT_Q) && |
| ((skb_queue_len(&common->tx_queue[q_num]) + 1) >= |
| DATA_QUEUE_WATER_MARK)) { |
| rsi_dbg(ERR_ZONE, "%s: sw queue full\n", __func__); |
| if (!ieee80211_queue_stopped(adapter->hw, WME_AC(q_num))) |
| ieee80211_stop_queue(adapter->hw, WME_AC(q_num)); |
| rsi_set_event(&common->tx_thread.event); |
| goto xmit_fail; |
| } |
| |
| rsi_core_queue_pkt(common, skb); |
| rsi_dbg(DATA_TX_ZONE, "%s: ===> Scheduling TX thread <===\n", __func__); |
| rsi_set_event(&common->tx_thread.event); |
| |
| return; |
| |
| xmit_fail: |
| rsi_dbg(ERR_ZONE, "%s: Failed to queue packet\n", __func__); |
| /* Dropping pkt here */ |
| ieee80211_free_txskb(common->priv->hw, skb); |
| } |