blob: f0da3ae04334d8414b14d0fd88cbd12a7a560586 [file] [log] [blame]
/******************************************************************************
*
* Copyright (c) 2012 - 2019 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 "scsc_wifilogger_rings.h"
/**
* 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_WARN(sdev, "no peer record for %pM, dropping EAP frame\n", 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;
if ((skb->data[17]) != 9) /*subtype 9 refers to unicast negotiation response*/
dwell_time = 0;
break;
default:
SLSI_NET_DBG1(dev, SLSI_MLME, "unsupported protocol\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;
}
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;
enum slsi_traffic_q tq;
u32 dwell_time = 0;
u8 *frame;
u16 arp_opcode;
u32 dhcp_message_type = SLSI_DHCP_MESSAGE_TYPE_INVALID;
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_INFO(dev, "Skip sending signal, WlanLite FW does not support MA_UNITDATA.request\n");
return -EOPNOTSUPP;
}
#ifdef CONFIG_SCSC_WIFI_NAN_ENABLE
if (ndev_vif->ifnum < SLSI_NAN_DATA_IFINDEX_START) {
#endif
if (!ndev_vif->activated) {
SLSI_NET_WARN(dev, "vif NOT activated\n");
return -EINVAL;
}
#ifdef CONFIG_SCSC_WIFI_NAN_ENABLE
}
#endif
if ((ndev_vif->vif_type == FAPI_VIFTYPE_AP) && !ndev_vif->peer_sta_records) {
SLSI_NET_DBG3(dev, SLSI_TX, "AP with no STAs associated, ignore 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 in SLSI_NETIF_Q_PRIORITY\n", proto);
return -EINVAL;
case ETH_P_PAE:
case ETH_P_WAI:
SLSI_NET_DBG2(dev, SLSI_MLME, "transmit EAP packet from SLSI_NETIF_Q_PRIORITY\n");
return slsi_tx_eapol(sdev, dev, skb);
case ETH_P_ARP:
SLSI_NET_DBG2(dev, SLSI_MLME, "transmit ARP frame from SLSI_NETIF_Q_PRIORITY\n");
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;
}
return slsi_mlme_send_frame_data(sdev, dev, skb, FAPI_MESSAGETYPE_ARP, 0, dwell_time, 0);
case ETH_P_IP:
if (skb->len >= 285 && slsi_is_dhcp_packet(skb->data) != SLSI_TX_IS_NOT_DHCP) {
if (skb->data[42] == 1) /*opcode 1 refers to DHCP discover/request*/
dwell_time = sdev->fw_dwell_time;
dhcp_message_type = skb->data[284];
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");
return slsi_mlme_send_frame_data(sdev, dev, skb, FAPI_MESSAGETYPE_DHCP, 0, dwell_time,
0);
}
/* 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");
return -EINVAL;
}
}
if (skb_headroom(skb) < (fapi_sig_size(ma_unitdata_req) + 160)) {
struct sk_buff *skb2 = NULL;
skb2 = slsi_skb_realloc_headroom(skb, fapi_sig_size(ma_unitdata_req) + 160);
if (!skb2)
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));
len = skb->len;
(void)skb_push(skb, fapi_sig_size(ma_unitdata_req));
tq = slsi_frame_priority_to_ac_queue(skb->priority);
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, ndev_vif->ifnum);
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, MAP_QS_TO_AID(slsi_netif_get_qs_from_queue
(skb->queue_mapping, tq)));
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 TCP Ack suppression and robustness is enabled, TCP Acks are marked in SKB control block.
* If it is a TCP Ack, mark them so in FAPI signal
*/
if (slsi_skb_cb_get(skb)->frame_format == SLSI_NETIF_FRAME_TCP_ACK)
fapi_set_u16(skb, u.ma_unitdata_req.data_unit_descriptor, FAPI_DATAUNITDESCRIPTOR_TCP_ACK);
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;
/* colour is defined as: */
/* u16 register bits:
* 0 - do not use
* [2:1] - vif
* [7:3] - peer_index
* [10:8] - ac queue
*/
cb->colour = (slsi_frame_priority_to_ac_queue(skb->priority) << 8) |
(fapi_get_u16(skb, u.ma_unitdata_req.peer_index) << 3) | ndev_vif->ifnum << 1;
/* 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));
/* 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,
(cb->colour & 0x6) >> 1,
(cb->colour & 0xf8) >> 3);
if (ret < 0) {
SLSI_NET_DBG3(dev, SLSI_TX, "no fcq for groupcast, dropping TX frame\n");
/* Free the local copy here ..if any */
if (original_skb)
slsi_kfree_skb(skb);
return ret;
}
ret = scsc_wifi_transmit_frame(&sdev->hip4_inst, false, skb);
if (ret == NETDEV_TX_OK) {
/**
* Frees the original since the copy has already
* been freed downstream
*/
if (original_skb)
slsi_kfree_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,
(cb->colour & 0x6) >> 1,
(cb->colour & 0xf8) >> 3);
if (original_skb)
slsi_kfree_skb(skb);
return ret;
}
if (original_skb)
slsi_kfree_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_DBG3(dev, SLSI_TX, "no peer record 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]);
if (original_skb)
slsi_kfree_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);
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,
(cb->colour & 0x6) >> 1,
(cb->colour & 0xf8) >> 3);
if (ret < 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);
if (original_skb)
slsi_kfree_skb(skb);
return ret;
}
/* SKB is owned by scsc_wifi_transmit_frame() unless the transmission is
* unsuccesful.
*/
ret = scsc_wifi_transmit_frame(&sdev->hip4_inst, false, skb);
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,
(cb->colour & 0x6) >> 1,
(cb->colour & 0xf8) >> 3);
if (ret == -ENOSPC) {
slsi_spinlock_unlock(&ndev_vif->peer_lock);
if (original_skb)
slsi_kfree_skb(skb);
return ret;
}
slsi_spinlock_unlock(&ndev_vif->peer_lock);
if (original_skb)
slsi_kfree_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)
slsi_kfree_skb(original_skb);
peer->sinfo.tx_bytes += len;
slsi_offline_dbg_printf(sdev, 4, false, "%llu : %-8s : ucast_tx", ktime_to_ns(ktime_get()), "HIP TX");
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 *dest;
int ret;
struct slsi_skb_cb *cb = slsi_skb_cb_get(skb);
vif = fapi_get_vif(skb);
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,
(cb->colour & 0x6) >> 1,
(cb->colour & 0xf8) >> 3) < 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, false, skb);
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,
(cb->colour & 0x6) >> 1,
(cb->colour & 0xf8) >> 3);
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 %02x:%02x:%02x:%02x:%02x:%02x, dropping TX frame\n",
dest[0], dest[1], dest[2], dest[3], dest[4], dest[5]);
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,
(cb->colour & 0x6) >> 1,
(cb->colour & 0xf8) >> 3) < 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, false, skb);
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,
(cb->colour & 0x6) >> 1,
(cb->colour & 0xf8) >> 3);
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_offline_dbg_printf(sdev, 4, false, "%llu : %-8s : ucast_tx", ktime_to_ns(ktime_get()), "HIP TX");
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;
}
slsi_debug_frame(sdev, dev, skb, "TX");
/**
* 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;
/* 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));
res = scsc_wifi_transmit_frame(&sdev->hip4_inst, true, skb);
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);
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);
}