| /****************************************************************************** |
| * |
| * Copyright (c) 2012 - 2020 Samsung Electronics Co., Ltd. All rights reserved |
| * |
| *****************************************************************************/ |
| |
| #include "dev.h" |
| #include "debug.h" |
| #include "mgt.h" |
| #include "mlme.h" |
| #include "netif.h" |
| #include "log_clients.h" |
| #include "hip4_sampler.h" |
| #include "traffic_monitor.h" |
| |
| static bool msdu_enable = true; |
| module_param(msdu_enable, bool, S_IRUGO | S_IWUSR); |
| MODULE_PARM_DESC(msdu_enable, "MSDU frame format, Y: enable (default), N: disable"); |
| |
| #ifdef CONFIG_SCSC_WLAN_ANDROID |
| #include "scsc_wifilogger_rings.h" |
| #endif |
| |
| /** |
| * Needed to get HIP4_DAT)SLOTS...should be part |
| * of initialization and callbacks registering |
| */ |
| #include "hip4.h" |
| |
| #include <linux/spinlock.h> |
| |
| int slsi_get_dwell_time_for_wps(struct slsi_dev *sdev, struct netdev_vif *ndev_vif, u8 *eapol, u16 eap_length) |
| { |
| /** |
| * Note that Message should not be M8.This check is to identify only |
| * WSC_START message or M1-M7 |
| * Return 100ms If opcode type WSC msg and Msg Type M1-M7 or if |
| * opcode is WSC start. |
| */ |
| if (eapol[SLSI_EAP_CODE_POS] == SLSI_EAP_PACKET_REQUEST || |
| eapol[SLSI_EAP_CODE_POS] == SLSI_EAP_PACKET_RESPONSE) { |
| if (eapol[SLSI_EAP_TYPE_POS] == SLSI_EAP_TYPE_EXPANDED && eap_length >= SLSI_EAP_OPCODE_POS - 3 && |
| ((eapol[SLSI_EAP_OPCODE_POS] == SLSI_EAP_OPCODE_WSC_MSG && eap_length >= SLSI_EAP_MSGTYPE_POS - 3 && |
| eapol[SLSI_EAP_MSGTYPE_POS] != SLSI_EAP_MSGTYPE_M8) || |
| eapol[SLSI_EAP_OPCODE_POS] == SLSI_EAP_OPCODE_WSC_START)) |
| return SLSI_EAP_WPS_DWELL_TIME; |
| /** |
| * This is to check if a frame is EAP request identity and on |
| * P2P vif.If yes then set dwell time to 100ms |
| */ |
| if (SLSI_IS_VIF_INDEX_P2P_GROUP(sdev, ndev_vif) && |
| eapol[SLSI_EAP_CODE_POS] == SLSI_EAP_PACKET_REQUEST && |
| eapol[SLSI_EAP_TYPE_POS] == SLSI_EAP_TYPE_IDENTITY) |
| return SLSI_EAP_WPS_DWELL_TIME; |
| } |
| return 0; |
| } |
| |
| static int slsi_tx_eapol(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| struct slsi_peer *peer; |
| u8 *eapol = NULL; |
| u16 msg_type = 0; |
| u16 proto = ntohs(skb->protocol); |
| int ret = 0; |
| u32 dwell_time = sdev->fw_dwell_time; |
| u64 tx_bytes_tmp = 0; |
| u16 eap_length = 0; |
| |
| slsi_spinlock_lock(&ndev_vif->peer_lock); |
| peer = slsi_get_peer_from_mac(sdev, dev, eth_hdr(skb)->h_dest); |
| if (!peer) { |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| SLSI_NET_WARN(dev, "no peer record for " MACSTR ", drop EAP frame\n", MAC2STR(eth_hdr(skb)->h_dest)); |
| return -EINVAL; |
| } |
| |
| switch (proto) { |
| case ETH_P_PAE: |
| /** |
| * Detect if this is an EAPOL key frame. If so detect if |
| * it is an EAPOL-Key M4 packet |
| * In M4 packet, |
| * - Key type bit set in key info (pairwise=1, Group=0) |
| * - ACK bit will not be set |
| * - Secure bit will be set in key type RSN (WPA2/WPA3 |
| * Personal/WPA3 Enterprise) |
| * - Key Data length check for Zero is for WPA as Secure |
| * bit will not be set, MIC bit set in key info |
| */ |
| if ((skb->len - sizeof(struct ethhdr)) >= 99) |
| eapol = skb->data + sizeof(struct ethhdr); |
| if (eapol && eapol[SLSI_EAPOL_IEEE8021X_TYPE_POS] == SLSI_IEEE8021X_TYPE_EAPOL_KEY) { |
| msg_type = FAPI_MESSAGETYPE_EAPOL_KEY_M123; |
| |
| if ((!(eapol[SLSI_EAPOL_KEY_INFO_LOWER_BYTE_POS] & SLSI_EAPOL_KEY_INFO_ACK_BIT_IN_LOWER_BYTE)) && |
| eapol[SLSI_EAPOL_KEY_INFO_LOWER_BYTE_POS] & SLSI_EAPOL_KEY_INFO_KEY_TYPE_BIT_IN_LOWER_BYTE && |
| ((eapol[SLSI_EAPOL_TYPE_POS] == SLSI_EAPOL_TYPE_RSN_KEY && |
| eapol[SLSI_EAPOL_KEY_INFO_HIGHER_BYTE_POS] & SLSI_EAPOL_KEY_INFO_SECURE_BIT_IN_HIGHER_BYTE) || |
| (eapol[SLSI_EAPOL_TYPE_POS] == SLSI_EAPOL_TYPE_WPA_KEY && |
| eapol[SLSI_EAPOL_KEY_INFO_HIGHER_BYTE_POS] & SLSI_EAPOL_KEY_INFO_MIC_BIT_IN_HIGHER_BYTE && |
| eapol[SLSI_EAPOL_KEY_DATA_LENGTH_HIGHER_BYTE_POS] == 0 && eapol[SLSI_EAPOL_KEY_DATA_LENGTH_LOWER_BYTE_POS] == 0))){ |
| msg_type = FAPI_MESSAGETYPE_EAPOL_KEY_M4; |
| dwell_time = 0; |
| } |
| } else { |
| msg_type = FAPI_MESSAGETYPE_EAP_MESSAGE; |
| if ((skb->len - sizeof(struct ethhdr)) >= 9) |
| eapol = skb->data + sizeof(struct ethhdr); |
| |
| dwell_time = 0; |
| if (eapol && eapol[SLSI_EAPOL_IEEE8021X_TYPE_POS] == SLSI_IEEE8021X_TYPE_EAP_PACKET) { |
| eap_length = (skb->len - sizeof(struct ethhdr)) - 4; |
| if (eapol[SLSI_EAP_CODE_POS] == SLSI_EAP_PACKET_REQUEST) |
| SLSI_INFO(sdev, "Send EAP-Request (%d)\n", eap_length); |
| else if (eapol[SLSI_EAP_CODE_POS] == SLSI_EAP_PACKET_RESPONSE) |
| SLSI_INFO(sdev, "Send EAP-Response (%d)\n", eap_length); |
| else if (eapol[SLSI_EAP_CODE_POS] == SLSI_EAP_PACKET_SUCCESS) |
| SLSI_INFO(sdev, "Send EAP-Success (%d)\n", eap_length); |
| else if (eapol[SLSI_EAP_CODE_POS] == SLSI_EAP_PACKET_FAILURE) |
| SLSI_INFO(sdev, "Send EAP-Failure (%d)\n", eap_length); |
| /** |
| * Need to set dwell time for wps exchange and |
| * EAP identity frame for P2P |
| */ |
| dwell_time = slsi_get_dwell_time_for_wps(sdev, ndev_vif, eapol, eap_length); |
| } |
| } |
| break; |
| case ETH_P_WAI: |
| SLSI_NET_DBG1(dev, SLSI_MLME, "WAI protocol frame\n"); |
| msg_type = FAPI_MESSAGETYPE_WAI_MESSAGE; |
| /* subtype 9 refers to unicast negotiation response */ |
| if (skb->data[17] != 9) |
| dwell_time = 0; |
| break; |
| default: |
| SLSI_NET_WARN(dev, "protocol NOT supported\n"); |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| return -EOPNOTSUPP; |
| } |
| |
| /* EAPOL/WAI frames are send via the MLME */ |
| tx_bytes_tmp = skb->len; /*len copy to avoid null pointer of skb*/ |
| ret = slsi_mlme_send_frame_data(sdev, dev, skb, msg_type, 0, dwell_time, 0); |
| if (!ret) |
| peer->sinfo.tx_bytes += tx_bytes_tmp; //skb->len; |
| |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| return ret; |
| } |
| |
| static int slsi_tx_arp(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| struct slsi_peer *peer; |
| u8 *frame; |
| u16 arp_opcode; |
| int ret = 0; |
| u32 dwell_time = 0; |
| |
| slsi_spinlock_lock(&ndev_vif->peer_lock); |
| |
| peer = slsi_get_peer_from_mac(sdev, dev, eth_hdr(skb)->h_dest); |
| if (!peer && !is_broadcast_ether_addr(eth_hdr(skb)->h_dest)) { |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| SLSI_NET_WARN(dev, "no peer record for " MACSTR ", drop ARP frame\n", MAC2STR(eth_hdr(skb)->h_dest)); |
| return -EINVAL; |
| } |
| |
| if (peer) { |
| spin_lock_bh(&peer->data_qs.cp_lock); |
| /* Controlled port is not yet open; so can't send ARP frame */ |
| if (peer->data_qs.controlled_port_state == SCSC_WIFI_FCQ_8021x_STATE_BLOCKED) { |
| SLSI_DBG1_NODEV(SLSI_MLME, " ARP 8021x_STATE_BLOCKED\n"); |
| spin_unlock_bh(&peer->data_qs.cp_lock); |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| return -EPERM; |
| } |
| spin_unlock_bh(&peer->data_qs.cp_lock); |
| } |
| |
| frame = skb->data + sizeof(struct ethhdr); |
| arp_opcode = frame[SLSI_ARP_OPCODE_OFFSET] << 8 | frame[SLSI_ARP_OPCODE_OFFSET + 1]; |
| if (arp_opcode == SLSI_ARP_REQUEST_OPCODE && |
| !SLSI_IS_GRATUITOUS_ARP(frame)) { |
| #ifdef CONFIG_SCSC_WLAN_STA_ENHANCED_ARP_DETECT |
| if (ndev_vif->enhanced_arp_detect_enabled && |
| !memcmp(&frame[SLSI_ARP_DEST_IP_ADDR_OFFSET], &ndev_vif->target_ip_addr, 4)) { |
| ndev_vif->enhanced_arp_stats.arp_req_count_from_netdev++; |
| } |
| #endif |
| dwell_time = sdev->fw_dwell_time; |
| } |
| ret = slsi_mlme_send_frame_data(sdev, dev, skb, FAPI_MESSAGETYPE_ARP, 0, dwell_time, 0); |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| return ret; |
| } |
| |
| void slsi_dump_msgtype(struct slsi_dev *sdev, u32 dhcp_message_type) |
| { |
| if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_DISCOVER) |
| SLSI_INFO(sdev, "Send DHCP [DISCOVER]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_OFFER) |
| SLSI_INFO(sdev, "Send DHCP [OFFER]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_REQUEST) |
| SLSI_INFO(sdev, "Send DHCP [REQUEST]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_DECLINE) |
| SLSI_INFO(sdev, "Send DHCP [DECLINE]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_ACK) |
| SLSI_INFO(sdev, "Send DHCP [ACK]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_NAK) |
| SLSI_INFO(sdev, "Send DHCP [NAK]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_RELEASE) |
| SLSI_INFO(sdev, "Send DHCP [RELEASE]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_INFORM) |
| SLSI_INFO(sdev, "Send DHCP [INFORM]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_FORCERENEW) |
| SLSI_INFO(sdev, "Send DHCP [FORCERENEW]\n"); |
| else |
| SLSI_INFO(sdev, "Send DHCP [INVALID]\n"); |
| } |
| |
| static int slsi_tx_dhcp(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| struct slsi_peer *peer; |
| int ret = 0; |
| u32 dwell_time = 0; |
| u32 dhcp_message_type = SLSI_DHCP_MESSAGE_TYPE_INVALID; |
| |
| slsi_spinlock_lock(&ndev_vif->peer_lock); |
| peer = slsi_get_peer_from_mac(sdev, dev, eth_hdr(skb)->h_dest); |
| if (!peer && !is_broadcast_ether_addr(eth_hdr(skb)->h_dest)) { |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| SLSI_NET_WARN(dev, "no peer record for " MACSTR ", drop DHCP frame\n", MAC2STR(eth_hdr(skb)->h_dest)); |
| return -EINVAL; |
| } |
| |
| if (peer) { |
| spin_lock_bh(&peer->data_qs.cp_lock); |
| /* Controlled port is not yet open; so can't send DHCP frame */ |
| if (peer->data_qs.controlled_port_state == SCSC_WIFI_FCQ_8021x_STATE_BLOCKED) { |
| SLSI_DBG1_NODEV(SLSI_MLME, "dhcp 8021x_STATE_BLOCKED\n"); |
| spin_unlock_bh(&peer->data_qs.cp_lock); |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| return -EPERM; |
| } |
| spin_unlock_bh(&peer->data_qs.cp_lock); |
| } |
| |
| if (skb->len >= 285 && slsi_is_dhcp_packet(skb->data) != SLSI_TX_IS_NOT_DHCP) { |
| /* opcode 1 refers to DHCP discover/request */ |
| if (skb->data[42] == 1) |
| dwell_time = sdev->fw_dwell_time; |
| dhcp_message_type = skb->data[284]; |
| slsi_dump_msgtype(sdev, dhcp_message_type); |
| ret = slsi_mlme_send_frame_data(sdev, dev, skb, FAPI_MESSAGETYPE_DHCP, 0, dwell_time, 0); |
| } else { |
| /* IP frame can have only DHCP packet in SLSI_NETIF_Q_PRIORITY */ |
| SLSI_NET_ERR(dev, "Bad IP frame in SLSI_NETIF_Q_PRIORITY\n"); |
| } |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| return ret; |
| } |
| |
| uint slsi_sg_host_align_mask; /* TODO -- this needs to be resolved! */ |
| |
| /** |
| * This function deals with TX of data frames. |
| * On success, skbs are properly FREED; on error skb is NO MORE freed. |
| * |
| * NOTE THAT currently ONLY the following set of err-codes will trigger |
| * a REQUEUE and RETRY by upper layers in Kernel NetStack: |
| * |
| * -ENOSPC |
| */ |
| int slsi_tx_data(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct slsi_skb_cb *cb; |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| struct slsi_peer *peer; |
| struct sk_buff *original_skb = NULL; |
| u16 len = skb->len; |
| int ret = 0; |
| u8 vif_index = 0; |
| u8 peer_index = 0; |
| enum slsi_traffic_q tq; |
| |
| if (slsi_is_test_mode_enabled()) { |
| /* This signals is in XML file because parts of the Firmware |
| * need the symbols defined by them |
| * but this is actually not handled in wlanlite firmware. |
| */ |
| SLSI_NET_WARN(dev, "WlanLite: NOT supported\n"); |
| return -EOPNOTSUPP; |
| } |
| |
| if (!ndev_vif->activated) { |
| SLSI_NET_WARN(dev, "vif NOT activated\n"); |
| return -EINVAL; |
| } |
| |
| if (ndev_vif->vif_type == FAPI_VIFTYPE_AP && !ndev_vif->peer_sta_records) { |
| SLSI_NET_DBG3(dev, SLSI_TX, |
| "AP with no STAs associated, drop Tx frame\n"); |
| return -EINVAL; |
| } |
| |
| /* check if it is an high important frame? At the moment EAPOL, DHCP |
| * and ARP are treated as high important frame and are sent over |
| * MLME for applying special rules in transmission. |
| */ |
| if (skb->queue_mapping == SLSI_NETIF_Q_PRIORITY) { |
| int proto = be16_to_cpu(eth_hdr(skb)->h_proto); |
| |
| switch (proto) { |
| default: |
| /* Only EAP packets and IP frames with DHCP are stored in SLSI_NETIF_Q_PRIORITY */ |
| SLSI_NET_ERR(dev, |
| "Bad h_proto=0x%x SLSI_NETIF_Q_PRIORITY\n", proto); |
| return -EINVAL; |
| case ETH_P_PAE: |
| case ETH_P_WAI: |
| SLSI_NET_DBG2(dev, SLSI_MLME, |
| "tx EAP pkt from SLSI_NETIF_Q_PRIORITY\n"); |
| return slsi_tx_eapol(sdev, dev, skb); |
| case ETH_P_IP: |
| SLSI_NET_DBG2(dev, SLSI_MLME, |
| "tx DHCP pkt from SLSI_NETIF_Q_PRIORITY\n"); |
| return slsi_tx_dhcp(sdev, dev, skb); |
| } |
| } |
| |
| if (skb->queue_mapping == SLSI_NETIF_Q_ARP) { |
| int proto = be16_to_cpu(eth_hdr(skb)->h_proto); |
| |
| switch (proto) { |
| case ETH_P_ARP: |
| SLSI_NET_DBG2(dev, SLSI_MLME, |
| "tx ARP pkt from SLSI_NETIF_Q_ARP\n"); |
| return slsi_tx_arp(sdev, dev, skb); |
| default: |
| /* Only ARP packets are stored in SLSI_NETIF_Q_ARP */ |
| SLSI_NET_ERR(dev, |
| "Bad h_proto=0x%x SLSI_NETIF_Q_ARP\n", proto); |
| return -EINVAL; |
| } |
| } |
| |
| if (skb_headroom(skb) < (fapi_sig_size(ma_unitdata_req) + 160)) { |
| struct sk_buff *skb2 = NULL; |
| |
| skb2 = skb_realloc_headroom(skb, fapi_sig_size(ma_unitdata_req) + 160); |
| if (!skb2) { |
| SLSI_NET_WARN(dev, |
| "alloc SKB headroom failed, drop Tx pkt\n"); |
| return -EINVAL; |
| } |
| /* Keep track of this copy...*/ |
| original_skb = skb; |
| skb = skb2; |
| } |
| |
| /* Align mac_header with skb->data */ |
| if (skb_headroom(skb) != skb->mac_header) |
| skb_pull(skb, skb->mac_header - skb_headroom(skb)); |
| |
| if (msdu_enable) |
| ethr_ii_to_subframe_msdu(skb); |
| |
| len = skb->len; |
| |
| (void)skb_push(skb, fapi_sig_size(ma_unitdata_req)); |
| tq = slsi_frame_priority_to_ac_queue(skb->priority); |
| vif_index = ndev_vif->ifnum; |
| peer_index = MAP_QS_TO_AID(slsi_netif_get_qs_from_queue(skb->queue_mapping, tq)); |
| fapi_set_u16(skb, id, MA_UNITDATA_REQ); |
| fapi_set_u16(skb, receiver_pid, 0); |
| fapi_set_u16(skb, sender_pid, SLSI_TX_PROCESS_ID_MIN); |
| fapi_set_u32(skb, fw_reference, 0); |
| fapi_set_u16(skb, u.ma_unitdata_req.vif, vif_index); |
| fapi_set_u16(skb, u.ma_unitdata_req.host_tag, slsi_tx_host_tag(sdev, tq)); |
| fapi_set_u16(skb, u.ma_unitdata_req.peer_index, peer_index); |
| |
| SCSC_HIP4_SAMPLER_PKT_TX(sdev->minor_prof, fapi_get_u16(skb, u.ma_unitdata_req.host_tag)); |
| |
| /* by default the priority is set to contention. It is overridden and set appropriate |
| * priority if peer supports QoS. The broadcast/multicast frames are sent in non-QoS . |
| */ |
| fapi_set_u16(skb, u.ma_unitdata_req.priority, FAPI_PRIORITY_CONTENTION); |
| |
| if (msdu_enable) |
| fapi_set_u16(skb, u.ma_unitdata_req.data_unit_descriptor, FAPI_DATAUNITDESCRIPTOR_AMSDU_SUBFRAME); |
| else |
| fapi_set_u16(skb, u.ma_unitdata_req.data_unit_descriptor, FAPI_DATAUNITDESCRIPTOR_IEEE802_3_FRAME); |
| |
| SLSI_NET_DBG_HEX(dev, SLSI_TX, skb->data, skb->len < 128 ? skb->len : 128, "\n"); |
| |
| cb = slsi_skb_cb_init(skb); |
| cb->sig_length = fapi_sig_size(ma_unitdata_req); |
| cb->data_length = skb->len; |
| |
| #if IS_ENABLED(CONFIG_SCSC_WIFILOGGER) |
| /* Log only the linear skb chunk ... unidata anywya will be truncated to 100.*/ |
| SCSC_WLOG_PKTFATE_LOG_TX_DATA_FRAME(fapi_get_u16(skb, u.ma_unitdata_req.host_tag), |
| skb->data, skb_headlen(skb)); |
| #endif |
| |
| /* ACCESS POINT MODE */ |
| if (ndev_vif->vif_type == FAPI_VIFTYPE_AP) { |
| struct ethhdr *ehdr = eth_hdr(skb); |
| |
| if (is_multicast_ether_addr(ehdr->h_dest)) { |
| ret = scsc_wifi_fcq_transmit_data(dev, |
| &ndev_vif->ap.group_data_qs, |
| slsi_frame_priority_to_ac_queue(skb->priority), |
| sdev, |
| vif_index, |
| peer_index); |
| if (ret < 0) { |
| SLSI_NET_WARN(dev, "no fcq for groupcast, drop Tx frame\n"); |
| /* Free the local copy here ..if any */ |
| if (original_skb) |
| consume_skb(skb); |
| return ret; |
| } |
| |
| ret = scsc_wifi_transmit_frame(&sdev->hip4_inst, skb, false, vif_index, peer_index, slsi_frame_priority_to_ac_queue(skb->priority)); |
| if (ret == NETDEV_TX_OK) { |
| /** |
| * Frees the original since the copy has already |
| * been freed downstream |
| */ |
| if (original_skb) |
| consume_skb(original_skb); |
| return ret; |
| } else if (ret < 0) { |
| /* scsc_wifi_transmit_frame failed, decrement BoT counters */ |
| scsc_wifi_fcq_receive_data(dev, |
| &ndev_vif->ap.group_data_qs, |
| slsi_frame_priority_to_ac_queue(skb->priority), |
| sdev, |
| vif_index, |
| peer_index); |
| if (original_skb) |
| consume_skb(skb); |
| return ret; |
| } |
| if (original_skb) |
| consume_skb(skb); |
| return -EIO; |
| } |
| } |
| slsi_spinlock_lock(&ndev_vif->peer_lock); |
| |
| peer = slsi_get_peer_from_mac(sdev, dev, eth_hdr(skb)->h_dest); |
| if (!peer) { |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| SLSI_NET_WARN(dev, "no peer record for " MACSTR ", drop Tx frame\n", MAC2STR(eth_hdr(skb)->h_dest)); |
| if (original_skb) |
| consume_skb(skb); |
| return -EINVAL; |
| } |
| |
| /** |
| * skb->priority will contain the priority obtained from the IP Diff/Serv field. |
| * The skb->priority field is defined in terms of the FAPI_PRIORITY_* definitions. |
| * For QoS enabled associations, this is the tid and is the value required in |
| * the ma_unitdata_req.priority field. For non-QoS assocations, the ma_unitdata_req. |
| * priority field requires FAPI_PRIORITY_CONTENTION. |
| */ |
| if (peer->qos_enabled) |
| fapi_set_u16(skb, u.ma_unitdata_req.priority, skb->priority); |
| |
| #ifdef CONFIG_SCSC_WIFI_NAN_ENABLE |
| /* For NAN vif_index is set to ndl_vif */ |
| if (ndev_vif->ifnum >= SLSI_NAN_DATA_IFINDEX_START) { |
| u8 i = 0; |
| |
| if (is_multicast_ether_addr(eth_hdr(skb)->h_dest)) { |
| SLSI_NET_DBG1(dev, SLSI_TX, "multicast on NAN interface: Source=%pM\n", eth_hdr(skb)->h_source); |
| /** |
| * The driver shall send (duplicate) frames to all VIFs that |
| * have the same source address (SA). i.e. find other peers on same |
| * netdevice interface and duplicate the packet for each peer |
| */ |
| for (i = 0; i < SLSI_PEER_INDEX_MAX; i++) { |
| if (ndev_vif->peer_sta_record[i] && |
| ndev_vif->peer_sta_record[i]->valid && |
| ndev_vif->peer_sta_record[i]->ndl_vif != peer->ndl_vif) { |
| /* duplicate the SKB and send to |
| * peer that has a different NDL |
| */ |
| struct sk_buff *duplicate_skb = skb_copy(skb, GFP_ATOMIC); |
| |
| if (!duplicate_skb) { |
| SLSI_NET_WARN(dev, "NAN: Tx: multicast: failed to alloc duplicate SKB\n"); |
| continue; |
| } |
| fapi_set_u16(duplicate_skb, u.ma_unitdata_req.vif, ndev_vif->peer_sta_record[i]->ndl_vif); |
| |
| ret = scsc_wifi_fcq_transmit_data(dev, &ndev_vif->peer_sta_record[i]->data_qs, |
| slsi_frame_priority_to_ac_queue(duplicate_skb->priority), |
| sdev, |
| vif_index, |
| ndev_vif->peer_sta_record[i]->aid); |
| if (ret < 0) { |
| SLSI_NET_WARN(dev, "duplicate SKB: no fcq for " MACSTR ", drop Tx frame\n", MAC2STR(ndev_vif->peer_sta_record[i]->address)); |
| kfree_skb(duplicate_skb); |
| continue; |
| } |
| |
| ret = scsc_wifi_transmit_frame(&sdev->hip4_inst, |
| duplicate_skb, |
| false, |
| vif_index, |
| ndev_vif->peer_sta_record[i]->aid, |
| slsi_frame_priority_to_ac_queue(duplicate_skb->priority)); |
| if (ret != NETDEV_TX_OK) { |
| /* scsc_wifi_transmit_frame failed, decrement BoT counters */ |
| scsc_wifi_fcq_receive_data(dev, &ndev_vif->peer_sta_record[i]->data_qs, |
| slsi_frame_priority_to_ac_queue(duplicate_skb->priority), |
| sdev, |
| vif_index, |
| ndev_vif->peer_sta_record[i]->aid); |
| kfree_skb(duplicate_skb); |
| } |
| } |
| } |
| } |
| /* For a NAN VIF, override the local VIF index with NDL VIF for that peer */ |
| fapi_set_u16(skb, u.ma_unitdata_req.vif, peer->ndl_vif); |
| } |
| #endif |
| |
| slsi_debug_frame(sdev, dev, skb, "TX"); |
| |
| ret = scsc_wifi_fcq_transmit_data(dev, &peer->data_qs, |
| slsi_frame_priority_to_ac_queue(skb->priority), |
| sdev, |
| vif_index, |
| peer_index); |
| if (ret < 0) { |
| SLSI_NET_WARN(dev, "no fcq for " MACSTR ", drop Tx frame\n", MAC2STR(eth_hdr(skb)->h_dest)); |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| if (original_skb) |
| consume_skb(skb); |
| return ret; |
| } |
| |
| /* SKB is owned by scsc_wifi_transmit_frame() unless the transmission is |
| * unsuccesful. |
| */ |
| slsi_traffic_mon_event_tx(sdev, dev, skb); |
| ret = scsc_wifi_transmit_frame(&sdev->hip4_inst, skb, false, vif_index, peer_index, slsi_frame_priority_to_ac_queue(skb->priority)); |
| if (ret != NETDEV_TX_OK) { |
| /* scsc_wifi_transmit_frame failed, decrement BoT counters */ |
| scsc_wifi_fcq_receive_data(dev, &peer->data_qs, slsi_frame_priority_to_ac_queue(skb->priority), |
| sdev, |
| vif_index, |
| peer_index); |
| |
| if (ret == -ENOSPC) { |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| if (original_skb) |
| consume_skb(skb); |
| return ret; |
| } |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| if (original_skb) |
| consume_skb(skb); |
| return -EIO; |
| } |
| /* Frame has been successfully sent, and freed by lower layers */ |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| /* What about the original if we passed in a copy ? */ |
| if (original_skb) |
| consume_skb(original_skb); |
| peer->sinfo.tx_bytes += len; |
| return ret; |
| } |
| |
| int slsi_tx_data_lower(struct slsi_dev *sdev, struct sk_buff *skb) |
| { |
| struct net_device *dev; |
| struct netdev_vif *ndev_vif; |
| struct slsi_peer *peer; |
| u16 vif; |
| u8 peer_index; |
| u8 *dest; |
| int ret; |
| |
| vif = fapi_get_vif(skb); |
| peer_index = fapi_get_u16(skb, u.ma_unitdata_req.peer_index); |
| |
| switch (fapi_get_u16(skb, u.ma_unitdata_req.data_unit_descriptor)) { |
| case FAPI_DATAUNITDESCRIPTOR_IEEE802_3_FRAME: |
| if (ntohs(eth_hdr(skb)->h_proto) == ETH_P_PAE || ntohs(eth_hdr(skb)->h_proto) == ETH_P_WAI) |
| return slsi_tx_control(sdev, NULL, skb); |
| dest = eth_hdr(skb)->h_dest; |
| break; |
| |
| case FAPI_DATAUNITDESCRIPTOR_AMSDU: |
| /* The AMSDU frame type is an AMSDU payload ready to be prepended by |
| * an 802.11 frame header by the firmware. The AMSDU subframe header |
| * is identical to an Ethernet header in terms of addressing, so it |
| * is safe to access the destination address through the ethernet |
| * structure. |
| */ |
| dest = eth_hdr(skb)->h_dest; |
| break; |
| case FAPI_DATAUNITDESCRIPTOR_IEEE802_11_FRAME: |
| dest = ieee80211_get_DA((struct ieee80211_hdr *)fapi_get_data(skb)); |
| break; |
| default: |
| SLSI_ERR(sdev, "data_unit_descriptor incorrectly set (0x%02x), dropping TX frame\n", |
| fapi_get_u16(skb, u.ma_unitdata_req.data_unit_descriptor)); |
| return -EINVAL; |
| } |
| |
| rcu_read_lock(); |
| dev = slsi_get_netdev_rcu(sdev, vif); |
| if (!dev) { |
| SLSI_ERR(sdev, "netdev(%d) No longer exists\n", vif); |
| rcu_read_unlock(); |
| return -EINVAL; |
| } |
| |
| ndev_vif = netdev_priv(dev); |
| rcu_read_unlock(); |
| |
| if (is_multicast_ether_addr(dest) && ndev_vif->vif_type == FAPI_VIFTYPE_AP) { |
| if (scsc_wifi_fcq_transmit_data(dev, &ndev_vif->ap.group_data_qs, |
| slsi_frame_priority_to_ac_queue(skb->priority), |
| sdev, |
| vif, |
| peer_index) < 0) { |
| SLSI_NET_DBG3(dev, SLSI_TX, "no fcq for groupcast, dropping TX frame\n"); |
| return -EINVAL; |
| } |
| ret = scsc_wifi_transmit_frame(&sdev->hip4_inst, skb, false, vif, peer_index, slsi_frame_priority_to_ac_queue(skb->priority)); |
| if (ret == NETDEV_TX_OK) |
| return ret; |
| /** |
| * This should be NEVER RETRIED/REQUEUED and its' handled |
| * by the caller in UDI cdev_write |
| */ |
| if (ret == -ENOSPC) |
| SLSI_NET_DBG1(dev, SLSI_TX, "TX_LOWER...Queue Full... BUT Dropping packet\n"); |
| else |
| SLSI_NET_DBG1(dev, SLSI_TX, "TX_LOWER...Generic Error...Dropping packet\n"); |
| /* scsc_wifi_transmit_frame failed, decrement BoT counters */ |
| scsc_wifi_fcq_receive_data(dev, &ndev_vif->ap.group_data_qs, |
| slsi_frame_priority_to_ac_queue(skb->priority), |
| sdev, |
| vif, |
| peer_index); |
| return ret; |
| } |
| |
| slsi_spinlock_lock(&ndev_vif->peer_lock); |
| peer = slsi_get_peer_from_mac(sdev, dev, dest); |
| if (!peer) { |
| SLSI_ERR(sdev, "no peer record for " MACSTR ", dropping TX frame\n", |
| MAC2STR(dest)); |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| return -EINVAL; |
| } |
| slsi_debug_frame(sdev, dev, skb, "TX"); |
| |
| if (fapi_get_u16(skb, u.ma_unitdata_req.priority) == FAPI_PRIORITY_CONTENTION) |
| skb->priority = FAPI_PRIORITY_QOS_UP0; |
| else |
| skb->priority = fapi_get_u16(skb, u.ma_unitdata_req.priority); |
| |
| if (scsc_wifi_fcq_transmit_data(dev, &peer->data_qs, |
| slsi_frame_priority_to_ac_queue(skb->priority), |
| sdev, |
| vif, |
| peer_index) < 0) { |
| SLSI_NET_DBG3(dev, SLSI_TX, "no fcq for %02x:%02x:%02x:%02x:%02x:%02x, dropping TX frame\n", |
| eth_hdr(skb)->h_dest[0], eth_hdr(skb)->h_dest[1], eth_hdr(skb)->h_dest[2], eth_hdr(skb)->h_dest[3], eth_hdr(skb)->h_dest[4], eth_hdr(skb)->h_dest[5]); |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| return -EINVAL; |
| } |
| |
| /* SKB is owned by scsc_wifi_transmit_frame() unless the transmission is |
| * unsuccesful. |
| */ |
| ret = scsc_wifi_transmit_frame(&sdev->hip4_inst, skb, false, vif, peer_index, slsi_frame_priority_to_ac_queue(skb->priority)); |
| if (ret < 0) { |
| SLSI_NET_DBG1(dev, SLSI_TX, "%s (signal: %d)\n", ret == -ENOSPC ? "Queue is full. Flow control" : "Failed to transmit", fapi_get_sigid(skb)); |
| /* scsc_wifi_transmit_frame failed, decrement BoT counters */ |
| scsc_wifi_fcq_receive_data(dev, &ndev_vif->ap.group_data_qs, |
| slsi_frame_priority_to_ac_queue(skb->priority), |
| sdev, |
| vif, |
| peer_index); |
| if (ret == -ENOSPC) |
| SLSI_NET_DBG1(dev, SLSI_TX, |
| "TX_LOWER...Queue Full...BUT Dropping packet\n"); |
| else |
| SLSI_NET_DBG1(dev, SLSI_TX, |
| "TX_LOWER...Generic Error...Dropping packet\n"); |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| return ret; |
| } |
| |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| return 0; |
| } |
| |
| /** |
| * NOTE: |
| * 1. dev can be NULL |
| * 2. On error the SKB is NOT freed, NOR retried (ENOSPC dropped). |
| * Callers should take care to free the SKB eventually. |
| */ |
| int slsi_tx_control(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct slsi_skb_cb *cb; |
| int res = 0; |
| struct fapi_signal_header *hdr; |
| |
| if (WARN_ON(!skb)) { |
| res = -EINVAL; |
| goto exit; |
| } |
| |
| /** |
| * Sanity check of the skb - if it's not an MLME, MA, debug or test |
| * signal it will be discarded. |
| * Skip if test mode (wlanlite) is enabled. |
| */ |
| if (!slsi_is_test_mode_enabled()) |
| if (!fapi_is_mlme(skb) && !fapi_is_ma(skb) && !fapi_is_debug(skb) && !fapi_is_test(skb)) { |
| SLSI_NET_WARN(dev, "Discarding skb because it has type: 0x%04X\n", fapi_get_sigid(skb)); |
| return -EINVAL; |
| } |
| |
| cb = slsi_skb_cb_init(skb); |
| cb->sig_length = fapi_get_expected_size(skb); |
| cb->data_length = skb->len; |
| /* F/w will panic if fw_reference is not zero. */ |
| hdr = (struct fapi_signal_header *)skb->data; |
| hdr->fw_reference = 0; |
| |
| #if IS_ENABLED(CONFIG_SCSC_WIFILOGGER) |
| /* Log only the linear skb chunk */ |
| SCSC_WLOG_PKTFATE_LOG_TX_CTRL_FRAME(fapi_get_u16(skb, u.mlme_frame_transmission_ind.host_tag), |
| skb->data, skb_headlen(skb)); |
| #endif |
| |
| slsi_debug_frame(sdev, dev, skb, "TX"); |
| res = scsc_wifi_transmit_frame(&sdev->hip4_inst, skb, true, 0, 0, 0); |
| if (res != NETDEV_TX_OK) { |
| char reason[80]; |
| |
| SLSI_NET_ERR(dev, "%s (signal %d)\n", res == -ENOSPC ? "Queue is full. Flow control" : "Failed to transmit", fapi_get_sigid(skb)); |
| |
| if (!in_interrupt()) { |
| snprintf(reason, sizeof(reason), "Failed to transmit signal 0x%04X (err:%d)", fapi_get_sigid(skb), res); |
| slsi_sm_service_failed(sdev, reason, false); |
| |
| res = -EIO; |
| } |
| } |
| exit: |
| return res; |
| } |
| |
| void slsi_tx_pause_queues(struct slsi_dev *sdev) |
| { |
| if (!sdev) |
| return; |
| |
| scsc_wifi_fcq_pause_queues(sdev); |
| } |
| |
| void slsi_tx_unpause_queues(struct slsi_dev *sdev) |
| { |
| if (!sdev) |
| return; |
| |
| scsc_wifi_fcq_unpause_queues(sdev); |
| } |