--- a/local-symbols +++ b/local-symbols @@ -39,6 +39,7 @@ LIB80211_CRYPT_CCMP= LIB80211_CRYPT_TKIP= LIB80211_DEBUG= MAC80211= +MAC80211_NSS_SUPPORT= MAC80211_HAS_RC= MAC80211_RC_MINSTREL= MAC80211_RC_DEFAULT_MINSTREL= --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -19,6 +19,13 @@ comment "CFG80211 needs to be enabled fo if MAC80211 != n +config MAC80211_NSS_SUPPORT + bool "Enable NSS support for IPQ platform" + default n + ---help--- + This option enables support for NSS in boards + like AP148. + config MAC80211_HAS_RC bool --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -36,6 +36,10 @@ extern const struct cfg80211_ops mac80211_config_ops; +#ifdef CPTCFG_MAC80211_NSS_SUPPORT +#include +#endif + struct ieee80211_local; /* Maximum number of broadcast/multicast frames to buffer when some of the @@ -1034,6 +1038,12 @@ struct ieee80211_sub_if_data { } debugfs; #endif +#ifdef CPTCFG_MAC80211_NSS_SUPPORT + struct nss_virt_if_handle *nssctx; + struct sk_buff_head rx_queue; + struct work_struct rx_work; +#endif + /* must be last, dynamically sized area in this! */ struct ieee80211_vif vif; }; --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -664,6 +664,15 @@ static int ieee80211_stop(struct net_dev { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +#ifdef CPTCFG_MAC80211_NSS_SUPPORT + if (sdata->nssctx) { + cancel_work_sync(&sdata->rx_work); + skb_queue_purge(&sdata->rx_queue); + nss_virt_if_destroy_sync(sdata->nssctx); + sdata->nssctx = NULL; + sdata_info(sdata, "Destroyed NSS virtual interface\n"); + } +#endif /* close dependent VLAN and MBSSID interfaces before locking wiphy */ if (sdata->vif.type == NL80211_IFTYPE_AP) { struct ieee80211_sub_if_data *vlan, *tmpsdata; @@ -1188,6 +1197,89 @@ void ieee80211_del_virtual_monitor(struc kfree(sdata); } +#ifdef CPTCFG_MAC80211_NSS_SUPPORT +#define case_rtn_string(val) case val: return #val + +static const char *nss_tx_status_str(nss_tx_status_t status) +{ + switch (status) { + case_rtn_string(NSS_TX_SUCCESS); + case_rtn_string(NSS_TX_FAILURE); + case_rtn_string(NSS_TX_FAILURE_QUEUE); + case_rtn_string(NSS_TX_FAILURE_NOT_READY); + case_rtn_string(NSS_TX_FAILURE_TOO_LARGE); + case_rtn_string(NSS_TX_FAILURE_TOO_SHORT); + case_rtn_string(NSS_TX_FAILURE_NOT_SUPPORTED); + case_rtn_string(NSS_TX_FAILURE_BAD_PARAM); + case_rtn_string(NSS_TX_FAILURE_NOT_ENABLED); + case_rtn_string(NSS_TX_FAILURE_SYNC_BAD_PARAM); + case_rtn_string(NSS_TX_FAILURE_SYNC_TIMEOUT); + case_rtn_string(NSS_TX_FAILURE_SYNC_FW_ERR); + default: + return "Unknown NSS TX status"; + } +} + +static void netif_rx_nss_work(struct work_struct *work) +{ + int ret; + unsigned long flags; + struct sk_buff *skb; + struct sk_buff_head *list; + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, rx_work); + + list = &sdata->rx_queue; + skb = skb_dequeue(list); + + while (skb != NULL) { + skb_push(skb, ETH_HLEN); + ret = nss_virt_if_tx_buf(sdata->nssctx, skb); + if (unlikely(ret)) { + if (net_ratelimit() && ret != NSS_TX_FAILURE_TOO_SHORT) { + sdata_err(sdata, "NSS TX failed with error: %s\n", + nss_tx_status_str(ret)); + } + skb_pull(skb, ETH_HLEN); + netif_receive_skb(skb); + } + + skb = skb_dequeue(list); + } + + /* Check if we there are more packet to process and resched */ + skb = skb_peek(list); + if (skb) + schedule_work(&sdata->rx_work); +} + +/* This callback is registered for nss redirect to receive packet exceptioned from nss in Rx path. + * When packet does not match any of the ecm rules is redirected back here. + */ +void receive_from_nss(struct net_device *dev, struct sk_buff *sk_buff, struct napi_struct *napi) +{ + struct net_device *netdev; + struct sk_buff *skb; + struct ieee80211_sub_if_data *sdata; + + if (!dev) { + kfree(sk_buff); + return; + } + + netdev = (struct net_device *)dev; + sdata = netdev_priv(netdev); + if (sdata->dev != dev) { + kfree(sk_buff); + return; + } + skb = (struct sk_buff *)sk_buff; + skb->dev = netdev; + skb->protocol = eth_type_trans(skb, netdev); + napi_gro_receive(napi, skb); +} +#endif + /* * NOTE: Be very careful when changing this function, it must NOT return * an error on interface type changes that have been pre-checked, so most @@ -1419,6 +1511,17 @@ int ieee80211_do_open(struct wireless_de ieee80211_recalc_ps(local); +#ifdef CPTCFG_MAC80211_NSS_SUPPORT + sdata->nssctx = nss_virt_if_create_sync(dev); + if (sdata->nssctx) { + sdata_info(sdata, "Created a NSS virtual interface\n"); + nss_virt_if_register(sdata->nssctx, receive_from_nss, sdata->dev); + skb_queue_head_init(&sdata->rx_queue); + INIT_WORK(&sdata->rx_work, &netif_rx_nss_work); + } else + sdata_err(sdata, "Failed to create a NSS virtual interface\n"); +#endif + if (sdata->vif.type == NL80211_IFTYPE_MONITOR || sdata->vif.type == NL80211_IFTYPE_AP_VLAN || local->ops->wake_tx_queue) { --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2573,6 +2573,13 @@ static bool ieee80211_frame_allowed(stru return true; } +static inline void netif_rx_nss(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) +{ + skb_queue_tail(&sdata->rx_queue, skb); + + schedule_work(&sdata->rx_work); +} + static void ieee80211_deliver_skb_to_local_stack(struct sk_buff *skb, struct ieee80211_rx_data *rx) { @@ -2593,6 +2600,12 @@ static void ieee80211_deliver_skb_to_loc memset(skb->cb, 0, sizeof(skb->cb)); +#ifdef CPTCFG_MAC80211_NSS_SUPPORT + if (sdata->nssctx) { + netif_rx_nss(skb, sdata); + return; + } +#endif /* * 802.1X over 802.11 requires that the authenticator address * be used for EAPOL frames. However, 802.1X allows the use of @@ -4489,6 +4502,7 @@ static void ieee80211_rx_8023(struct iee { struct ieee80211_sta_rx_stats *stats; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); + struct ieee80211_sub_if_data *sdata = rx->sdata; struct sta_info *sta = rx->sta; struct sk_buff *skb = rx->skb; void *sa = skb->data + ETH_ALEN; @@ -4574,6 +4588,13 @@ static void ieee80211_rx_8023(struct iee /* deliver to local stack */ skb->protocol = eth_type_trans(skb, fast_rx->dev); ieee80211_deliver_skb_to_local_stack(skb, rx); + +#ifdef CPTCFG_MAC80211_NSS_SUPPORT + if (sdata->nssctx) { + netif_rx_nss(skb, sdata); + return; + } +#endif } static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx, --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -36,6 +36,11 @@ #include "wme.h" #include "rate.h" +#ifdef CPTCFG_MAC80211_NSS_SUPPORT +#include +#include +#endif + /* misc utils */ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, @@ -1687,6 +1692,16 @@ static bool ieee80211_tx_frags(struct ie return true; } } else { +#ifdef CPTCFG_MAC80211_NSS_SUPPORT + if (skb_queue_len(&local->pending[q]) >= 1000) { + spin_unlock_irqrestore( + &local->queue_stop_reason_lock, + flags); + ieee80211_purge_tx_queue(&local->hw, + skbs); + return false; + } +#endif /* * Since queue is stopped, queue up frames for @@ -4296,6 +4311,31 @@ out: netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev) { + +#ifdef CPTCFG_MAC80211_NSS_SUPPORT + /* Packets from NSS does not have valid protocol, priority and other + * network stack values. Derive required parameters (priority + * and network_header) from payload for QoS header. + * XXX: Here the assumption is that packet are in 802.3 format. + * As of now priority is handled only for IPv4 and IPv6. + */ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->nssctx && likely(!skb->protocol)) { + skb_set_network_header(skb, 14); + switch (((struct ethhdr *)skb->data)->h_proto) { + case htons(ETH_P_IP): + skb->priority = (ipv4_get_dsfield(ip_hdr(skb)) & + 0xfc) >> 5; + break; + case htons(ETH_P_IPV6): + skb->priority = (ipv6_get_dsfield(ipv6_hdr(skb)) & + 0xfc) >> 5; + break; + } + } +#endif + if (unlikely(ieee80211_multicast_to_unicast(skb, dev))) { struct sk_buff_head queue;