static bool mwifiex_bypass_tx_queue(struct mwifiex_private *priv, struct sk_buff *skb) { struct ethhdr *eth_hdr = (struct ethhdr *)skb->data; if (ntohs(eth_hdr->h_proto) == ETH_P_PAE || mwifiex_is_skb_mgmt_frame(skb) || (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && (ntohs(eth_hdr->h_proto) == ETH_P_TDLS))) { mwifiex_dbg(priv->adapter, DATA, "bypass txqueue; eth type %#x, mgmt %d\n", ntohs(eth_hdr->h_proto), mwifiex_is_skb_mgmt_frame(skb)); return true; } return false; }
static void mwifiex_process_sta_tx_pause(struct mwifiex_private *priv, struct mwifiex_ie_types_header *tlv) { struct mwifiex_tx_pause_tlv *tp; struct mwifiex_sta_node *sta_ptr; int status; unsigned long flags; tp = (void *)tlv; mwifiex_dbg(priv->adapter, EVENT, "sta tx_pause: %pM pause=%d, pkts=%d\n", tp->peermac, tp->tx_pause, tp->pkt_cnt); if (ether_addr_equal(tp->peermac, priv->cfg_bssid)) { if (tp->tx_pause) priv->port_open = false; else priv->port_open = true; } else { if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) return; status = mwifiex_get_tdls_link_status(priv, tp->peermac); if (mwifiex_is_tdls_link_setup(status)) { spin_lock_irqsave(&priv->sta_list_spinlock, flags); sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac); spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) { sta_ptr->tx_pause = tp->tx_pause; mwifiex_update_ralist_tx_pause(priv, tp->peermac, tp->tx_pause); } } } }
/* * This function resets the connection state. * * The function is invoked after receiving a disconnect event from firmware, * and performs the following actions - * - Set media status to disconnected * - Clean up Tx and Rx packets * - Resets SNR/NF/RSSI value in driver * - Resets security configurations in driver * - Enables auto data rate * - Saves the previous SSID and BSSID so that they can * be used for re-association, if required * - Erases current SSID and BSSID information * - Sends a disconnect event to upper layers/applications. */ void mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code, bool from_ap) { struct mwifiex_adapter *adapter = priv->adapter; if (!priv->media_connected) return; mwifiex_dbg(adapter, INFO, "info: handles disconnect event\n"); priv->media_connected = false; priv->scan_block = false; priv->port_open = false; if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) { mwifiex_disable_all_tdls_links(priv); if (priv->adapter->auto_tdls) mwifiex_clean_auto_tdls(priv); } /* Free Tx and Rx packets, report disconnect to upper layer */ mwifiex_clean_txrx(priv); /* Reset SNR/NF/RSSI values */ priv->data_rssi_last = 0; priv->data_nf_last = 0; priv->data_rssi_avg = 0; priv->data_nf_avg = 0; priv->bcn_rssi_last = 0; priv->bcn_nf_last = 0; priv->bcn_rssi_avg = 0; priv->bcn_nf_avg = 0; priv->rxpd_rate = 0; priv->rxpd_htinfo = 0; priv->sec_info.wpa_enabled = false; priv->sec_info.wpa2_enabled = false; priv->wpa_ie_len = 0; priv->sec_info.wapi_enabled = false; priv->wapi_ie_len = 0; priv->sec_info.wapi_key_on = false; priv->sec_info.encryption_mode = 0; /* Enable auto data rate */ priv->is_data_rate_auto = true; priv->data_rate = 0; priv->assoc_resp_ht_param = 0; priv->ht_param_present = false; if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && priv->hist_data) mwifiex_hist_data_reset(priv); if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { priv->adhoc_state = ADHOC_IDLE; priv->adhoc_is_link_sensed = false; } /* * Memorize the previous SSID and BSSID so * it could be used for re-assoc */ mwifiex_dbg(adapter, INFO, "info: previous SSID=%s, SSID len=%u\n", priv->prev_ssid.ssid, priv->prev_ssid.ssid_len); mwifiex_dbg(adapter, INFO, "info: current SSID=%s, SSID len=%u\n", priv->curr_bss_params.bss_descriptor.ssid.ssid, priv->curr_bss_params.bss_descriptor.ssid.ssid_len); memcpy(&priv->prev_ssid, &priv->curr_bss_params.bss_descriptor.ssid, sizeof(struct cfg80211_ssid)); memcpy(priv->prev_bssid, priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN); /* Need to erase the current SSID and BSSID info */ memset(&priv->curr_bss_params, 0x00, sizeof(priv->curr_bss_params)); adapter->tx_lock_flag = false; adapter->pps_uapsd_mode = false; if (test_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags) && adapter->curr_cmd) return; priv->media_connected = false; mwifiex_dbg(adapter, MSG, "info: successfully disconnected from %pM: reason code %d\n", priv->cfg_bssid, reason_code); if (priv->bss_mode == NL80211_IFTYPE_STATION || priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { cfg80211_disconnected(priv->netdev, reason_code, NULL, 0, !from_ap, GFP_KERNEL); } eth_zero_addr(priv->cfg_bssid); mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); if (!ISSUPP_FIRMWARE_SUPPLICANT(priv->adapter->fw_cap_info)) return; mwifiex_send_cmd(priv, HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG, HostCmd_ACT_GEN_REMOVE, 0, NULL, false); }
/* * CFG802.11 network device handler for data transmission. */ static int mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); struct sk_buff *new_skb; struct mwifiex_txinfo *tx_info; bool multicast; dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n", jiffies, priv->bss_type, priv->bss_num); if (priv->adapter->surprise_removed) { kfree_skb(skb); priv->stats.tx_dropped++; return 0; } if (!skb->len || (skb->len > ETH_FRAME_LEN)) { dev_err(priv->adapter->dev, "Tx: bad skb len %d\n", skb->len); kfree_skb(skb); priv->stats.tx_dropped++; return 0; } if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) { dev_dbg(priv->adapter->dev, "data: Tx: insufficient skb headroom %d\n", skb_headroom(skb)); /* Insufficient skb headroom - allocate a new skb */ new_skb = skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN); if (unlikely(!new_skb)) { dev_err(priv->adapter->dev, "Tx: cannot alloca new_skb\n"); kfree_skb(skb); priv->stats.tx_dropped++; return 0; } kfree_skb(skb); skb = new_skb; dev_dbg(priv->adapter->dev, "info: new skb headroomd %d\n", skb_headroom(skb)); } tx_info = MWIFIEX_SKB_TXCB(skb); memset(tx_info, 0, sizeof(*tx_info)); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; tx_info->pkt_len = skb->len; multicast = is_multicast_ether_addr(skb->data); if (unlikely(!multicast && skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS && priv->adapter->fw_api_ver == MWIFIEX_FW_V15)) skb = mwifiex_clone_skb_for_tx_status(priv, skb, MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL); /* Record the current time the packet was queued; used to * determine the amount of time the packet was queued in * the driver before it was sent to the firmware. * The delay is then sent along with the packet to the * firmware for aggregate delay calculation for stats and * MSDU lifetime expiry. */ __net_timestamp(skb); if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && priv->bss_type == MWIFIEX_BSS_TYPE_STA && !ether_addr_equal_unaligned(priv->cfg_bssid, skb->data)) { if (priv->adapter->auto_tdls && priv->check_tdls_tx) mwifiex_tdls_check_tx(priv, skb); } mwifiex_queue_tx_pkt(priv, skb); return 0; }
/* * This function resets the connection state. * * The function is invoked after receiving a disconnect event from firmware, * and performs the following actions - * - Set media status to disconnected * - Clean up Tx and Rx packets * - Resets SNR/NF/RSSI value in driver * - Resets security configurations in driver * - Enables auto data rate * - Saves the previous SSID and BSSID so that they can * be used for re-association, if required * - Erases current SSID and BSSID information * - Sends a disconnect event to upper layers/applications. */ void mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code) { struct mwifiex_adapter *adapter = priv->adapter; if (!priv->media_connected) return; dev_dbg(adapter->dev, "info: handles disconnect event\n"); priv->media_connected = false; priv->scan_block = false; if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) mwifiex_disable_all_tdls_links(priv); /* Free Tx and Rx packets, report disconnect to upper layer */ mwifiex_clean_txrx(priv); /* Reset SNR/NF/RSSI values */ priv->data_rssi_last = 0; priv->data_nf_last = 0; priv->data_rssi_avg = 0; priv->data_nf_avg = 0; priv->bcn_rssi_last = 0; priv->bcn_nf_last = 0; priv->bcn_rssi_avg = 0; priv->bcn_nf_avg = 0; priv->rxpd_rate = 0; priv->rxpd_htinfo = 0; priv->sec_info.wpa_enabled = false; priv->sec_info.wpa2_enabled = false; priv->wpa_ie_len = 0; priv->sec_info.wapi_enabled = false; priv->wapi_ie_len = 0; priv->sec_info.wapi_key_on = false; priv->sec_info.encryption_mode = 0; /* Enable auto data rate */ priv->is_data_rate_auto = true; priv->data_rate = 0; if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { priv->adhoc_state = ADHOC_IDLE; priv->adhoc_is_link_sensed = false; } /* * Memorize the previous SSID and BSSID so * it could be used for re-assoc */ dev_dbg(adapter->dev, "info: previous SSID=%s, SSID len=%u\n", priv->prev_ssid.ssid, priv->prev_ssid.ssid_len); dev_dbg(adapter->dev, "info: current SSID=%s, SSID len=%u\n", priv->curr_bss_params.bss_descriptor.ssid.ssid, priv->curr_bss_params.bss_descriptor.ssid.ssid_len); memcpy(&priv->prev_ssid, &priv->curr_bss_params.bss_descriptor.ssid, sizeof(struct cfg80211_ssid)); memcpy(priv->prev_bssid, priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN); /* Need to erase the current SSID and BSSID info */ memset(&priv->curr_bss_params, 0x00, sizeof(priv->curr_bss_params)); adapter->tx_lock_flag = false; adapter->pps_uapsd_mode = false; if (adapter->is_cmd_timedout && adapter->curr_cmd) return; priv->media_connected = false; dev_dbg(adapter->dev, "info: successfully disconnected from %pM: reason code %d\n", priv->cfg_bssid, reason_code); if (priv->bss_mode == NL80211_IFTYPE_STATION || priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { cfg80211_disconnected(priv->netdev, reason_code, NULL, 0, GFP_KERNEL); } memset(priv->cfg_bssid, 0, ETH_ALEN); mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); }
/* * This function processes the received packet and forwards it * to kernel/upper layer. * * This function parses through the received packet and determines * if it is a debug packet or normal packet. * * For non-debug packets, the function chops off unnecessary leading * header bytes, reconstructs the packet as an ethernet frame or * 802.2/llc/snap frame as required, and sends it to kernel/upper layer. * * The completion callback is called after processing in complete. */ int mwifiex_process_rx_packet(struct mwifiex_private *priv, struct sk_buff *skb) { int ret; struct rx_packet_hdr *rx_pkt_hdr; struct rxpd *local_rx_pd; int hdr_chop; struct ethhdr *eth; u16 rx_pkt_off, rx_pkt_len; u8 *offset; local_rx_pd = (struct rxpd *) (skb->data); rx_pkt_off = le16_to_cpu(local_rx_pd->rx_pkt_offset); rx_pkt_len = le16_to_cpu(local_rx_pd->rx_pkt_length); rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off; if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, sizeof(bridge_tunnel_header))) || (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header, sizeof(rfc1042_header)) && ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP && ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) { /* * Replace the 803 header and rfc1042 header (llc/snap) with an * EthernetII header, keep the src/dst and snap_type * (ethertype). * The firmware only passes up SNAP frames converting * all RX Data from 802.11 to 802.2/LLC/SNAP frames. * To create the Ethernet II, just move the src, dst address * right before the snap_type. */ eth = (struct ethhdr *) ((u8 *) &rx_pkt_hdr->eth803_hdr + sizeof(rx_pkt_hdr->eth803_hdr) + sizeof(rx_pkt_hdr->rfc1042_hdr) - sizeof(rx_pkt_hdr->eth803_hdr.h_dest) - sizeof(rx_pkt_hdr->eth803_hdr.h_source) - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type)); memcpy(eth->h_source, rx_pkt_hdr->eth803_hdr.h_source, sizeof(eth->h_source)); memcpy(eth->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, sizeof(eth->h_dest)); /* Chop off the rxpd + the excess memory from the 802.2/llc/snap header that was removed. */ hdr_chop = (u8 *) eth - (u8 *) local_rx_pd; } else { /* Chop off the rxpd */ hdr_chop = (u8 *) &rx_pkt_hdr->eth803_hdr - (u8 *) local_rx_pd; } /* Chop off the leading header bytes so the it points to the start of either the reconstructed EthII frame or the 802.2/llc/snap frame */ skb_pull(skb, hdr_chop); if (priv->hs2_enabled && mwifiex_discard_gratuitous_arp(priv, skb)) { dev_dbg(priv->adapter->dev, "Bypassed Gratuitous ARP\n"); dev_kfree_skb_any(skb); return 0; } if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && ntohs(rx_pkt_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) { offset = (u8 *)local_rx_pd + rx_pkt_off; mwifiex_process_tdls_action_frame(priv, offset, rx_pkt_len); } priv->rxpd_rate = local_rx_pd->rx_rate; priv->rxpd_htinfo = local_rx_pd->ht_info; ret = mwifiex_recv_packet(priv, skb); if (ret == -1) dev_err(priv->adapter->dev, "recv packet failed\n"); return ret; }
/* * This function processes the received buffer. * * The function looks into the RxPD and performs sanity tests on the * received buffer to ensure its a valid packet, before processing it * further. If the packet is determined to be aggregated, it is * de-aggregated accordingly. Non-unicast packets are sent directly to * the kernel/upper layers. Unicast packets are handed over to the * Rx reordering routine if 11n is enabled. * * The completion callback is called after processing in complete. */ int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv, struct sk_buff *skb) { struct mwifiex_adapter *adapter = priv->adapter; int ret = 0; struct rxpd *local_rx_pd; struct rx_packet_hdr *rx_pkt_hdr; u8 ta[ETH_ALEN]; u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num; struct mwifiex_sta_node *sta_ptr; local_rx_pd = (struct rxpd *) (skb->data); rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type); rx_pkt_offset = le16_to_cpu(local_rx_pd->rx_pkt_offset); rx_pkt_length = le16_to_cpu(local_rx_pd->rx_pkt_length); seq_num = le16_to_cpu(local_rx_pd->seq_num); rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_offset; if ((rx_pkt_offset + rx_pkt_length) > (u16) skb->len) { dev_err(adapter->dev, "wrong rx packet: len=%d, rx_pkt_offset=%d, rx_pkt_length=%d\n", skb->len, rx_pkt_offset, rx_pkt_length); priv->stats.rx_dropped++; dev_kfree_skb_any(skb); return ret; } if (rx_pkt_type == PKT_TYPE_MGMT) { ret = mwifiex_process_mgmt_packet(priv, skb); if (ret) dev_err(adapter->dev, "Rx of mgmt packet failed"); dev_kfree_skb_any(skb); return ret; } /* * If the packet is not an unicast packet then send the packet * directly to os. Don't pass thru rx reordering */ if ((!IS_11N_ENABLED(priv) && !(ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && !(local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET))) || !ether_addr_equal_unaligned(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest)) { mwifiex_process_rx_packet(priv, skb); return ret; } if (mwifiex_queuing_ra_based(priv) || (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET)) { memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN); if (local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET && local_rx_pd->priority < MAX_NUM_TID) { sta_ptr = mwifiex_get_sta_entry(priv, ta); if (sta_ptr) sta_ptr->rx_seq[local_rx_pd->priority] = le16_to_cpu(local_rx_pd->seq_num); } } else { if (rx_pkt_type != PKT_TYPE_BAR) priv->rx_seq[local_rx_pd->priority] = seq_num; memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN); } /* Reorder and send to OS */ ret = mwifiex_11n_rx_reorder_pkt(priv, seq_num, local_rx_pd->priority, ta, (u8) rx_pkt_type, skb); if (ret || (rx_pkt_type == PKT_TYPE_BAR)) dev_kfree_skb_any(skb); if (ret) priv->stats.rx_dropped++; return ret; }