2193 lines
		
	
	
		
			80 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			2193 lines
		
	
	
		
			80 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| #include <linux/kernel.h>
 | |
| #include <linux/version.h>
 | |
| #include <linux/etherdevice.h>
 | |
| #include <linux/module.h>
 | |
| #include <net/rtnetlink.h>
 | |
| #include <net/ieee80211_radiotap.h>
 | |
| 
 | |
| #include "nvt_wlan_linux.h"
 | |
| #include "nvt_wlan_priv.h"
 | |
| #include "nvt_util_dbg.h"
 | |
| #include "nvt_bus_usbif.h"
 | |
| #include "nvt_bus_sdioif.h"
 | |
| #include "nvt_cfg80211.h"
 | |
| #include "nvt_iw.h"
 | |
| #include "nvt_wlan_priv.h"
 | |
| #include "nvt_icfg.h"
 | |
| #include "nvt_ver.h"
 | |
| 
 | |
| static s32 nvt_if_idx_counter;
 | |
| 
 | |
| static s32 nvt_p2p_enable;
 | |
| module_param_named(p2p, nvt_p2p_enable, int, S_IRUSR | S_IWUSR);
 | |
| MODULE_PARM_DESC(p2p, "enable P2P");
 | |
| 
 | |
| u32 nvt_txmq = 1;
 | |
| module_param_named(txmq, nvt_txmq, uint, S_IRUSR | S_IWUSR);
 | |
| MODULE_PARM_DESC(txmq, "enable txmq");
 | |
| 
 | |
| /*AT add for testing 4way offload*/
 | |
| s32 nvt_ptk_offload_enable = 0;
 | |
| module_param_named(ptk_offload, nvt_ptk_offload_enable, int, S_IRUSR | S_IWUSR);
 | |
| MODULE_PARM_DESC(ptk_offload, "enable ptk offload");
 | |
| 
 | |
| #define RX_RADIOTAP_PRESENT (                   \
 | |
|          (1 << IEEE80211_RADIOTAP_FLAGS) |       \
 | |
|          (1 << IEEE80211_RADIOTAP_RATE) |        \
 | |
|          (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\
 | |
|          0)
 | |
| 
 | |
| #define NVT_SKB_CB(__skb) ((struct _nvt_skb_cb *)&((__skb)->cb[0]))
 | |
| 
 | |
| #ifdef CONFIG_WIFI_TUNING_PHASE_II
 | |
| //#define NVTWLAN_IOCTL_TP_MONI (SIOCIWFIRSTPRIV+0xa)   //0x89F5
 | |
| #define NVTWLAN_IOCTL_TP_MONI (SIOCIWFIRSTPRIV + 28)
 | |
| #endif
 | |
| 
 | |
| /* Mapping 802.1d priority */
 | |
| static const int nvt_tx_prio2fifo[] = {
 | |
|         NV_TX_FIFO_AC_BE,
 | |
|         NV_TX_FIFO_AC_BK,
 | |
|         NV_TX_FIFO_AC_BK,
 | |
|         NV_TX_FIFO_AC_BE,
 | |
|         NV_TX_FIFO_AC_VI,
 | |
|         NV_TX_FIFO_AC_VI,
 | |
|         NV_TX_FIFO_AC_VO,
 | |
|         NV_TX_FIFO_AC_VO
 | |
| };
 | |
| 
 | |
| static bool nvt_fc_active(struct _tx_info *tx_info)
 | |
| {
 | |
|         return tx_info->fcmode;
 | |
| }
 | |
| 
 | |
| static u32 nvt_get_total_skb_counts(struct _tx_info *tx_info, u32 fifo)
 | |
| {
 | |
|         u32 pkt_counts = 0;
 | |
| 
 | |
|         if (nvt_txmq == 0) {
 | |
|                 pkt_counts += tx_info->bk_queue_list.qlen;
 | |
|                 pkt_counts += tx_info->be_queue_list.qlen;
 | |
|                 pkt_counts += tx_info->vi_queue_list.qlen;
 | |
|                 pkt_counts += tx_info->vo_queue_list.qlen;
 | |
|         } else {
 | |
|                 switch (fifo) {
 | |
|                 case NV_TX_FIFO_AC_BK:
 | |
|                         pkt_counts += tx_info->bk_queue_list.qlen;
 | |
|                         break;
 | |
|                 case NV_TX_FIFO_AC_BE:
 | |
|                         pkt_counts += tx_info->be_queue_list.qlen;
 | |
|                         break;
 | |
|                 case NV_TX_FIFO_AC_VI:
 | |
|                         pkt_counts += tx_info->vi_queue_list.qlen;
 | |
|                         break;
 | |
|                 case NV_TX_FIFO_AC_VO:
 | |
|                         pkt_counts += tx_info->vo_queue_list.qlen;
 | |
|                         break;
 | |
|                 default:
 | |
|                         break;
 | |
|                 }
 | |
|         }
 | |
|         return pkt_counts;
 | |
| }
 | |
| 
 | |
| #ifndef CONFIG_WIFI_TUNING_PHASE_I
 | |
| static bool nvt_fc_block(struct _tx_info *tx_info,
 | |
|                 bool is_check_stop, struct _nvt_if *nvt_if, u32 fifo)
 | |
| {
 | |
|         u32 pkt_counts = nvt_get_total_skb_counts(tx_info, fifo);
 | |
|         u32 fc_high_watermark = tx_info->fc_hi_watermark;
 | |
|         u32 fc_low_watermark = tx_info->fc_low_watermark;
 | |
|         struct netdev_queue *txq;
 | |
|         /* check stop when enqueue */
 | |
|         if (is_check_stop) {
 | |
|                 if (pkt_counts >= fc_high_watermark) {
 | |
|                         nvt_dbg(TX, "%s, pkt > highwatermark\n", __func__);
 | |
|                         if (nvt_txmq == 0) {
 | |
|                                 if (tx_info->is_netif_stop == 0) {
 | |
|                                         /* do nothing now */
 | |
|                                         /* netif_stop_queue(nvt_if->ndev); */
 | |
|                                         /* tx_info->is_netif_stop = 1; */
 | |
|                                 }
 | |
|                         } else {
 | |
|                                 txq = netdev_get_tx_queue(nvt_if->ndev, fifo);
 | |
|                                 if (!netif_tx_queue_stopped(txq)) {
 | |
|                                         netif_tx_stop_queue(txq);
 | |
|                                         nvt_dbg(TX,
 | |
|                                                 "stop fifo=%d queue\n", fifo);
 | |
|                                 }
 | |
|                         }
 | |
|                         return true;
 | |
|                 } else {
 | |
|                         return false;
 | |
|                 }
 | |
|         /* check wakeup when dequeue */
 | |
|         } else {
 | |
|                 if (pkt_counts < fc_low_watermark) {
 | |
|                         if (nvt_txmq == 0) {
 | |
|                                 if (tx_info->is_netif_stop == 1) {
 | |
|                                         /* do nothing now */
 | |
|                                         /* netif_wake_queue(nvt_if->ndev); */
 | |
|                                         /* tx_info->is_netif_stop = 0; */
 | |
|                                 }
 | |
|                         } else {
 | |
|                                 txq = netdev_get_tx_queue(nvt_if->ndev, fifo);
 | |
|                                 if (netif_tx_queue_stopped(txq)) {
 | |
|                                         netif_tx_wake_queue(txq);
 | |
|                                         nvt_dbg(TX,
 | |
|                                                 "wake fifo=%d queue\n", fifo);
 | |
|                                 }
 | |
|                         }
 | |
|                         return true;
 | |
|                 } else {
 | |
|                         return false;
 | |
|                 }
 | |
|         }
 | |
| }
 | |
| #else   /* CONFIG_WIFI_TUNING_PHASE_I == 1 */
 | |
| static bool nvt_fc_drop_check(struct _tx_info *tx_info, u32 fifo, bool eap)
 | |
| {
 | |
| 
 | |
|         u32 pkt_counts = nvt_get_total_skb_counts(tx_info, fifo);
 | |
|         #if 0  /* debug */
 | |
|         u32 pkt_counts_1 = tx_info->fifo_enqpkt[fifo];
 | |
| 
 | |
|         nvt_dbg(ERROR, "(%d,%d,%d)\n",
 | |
|                 pkt_counts, pkt_counts_1, tx_info->fc_hi_watermark[fifo]);
 | |
|         #endif
 | |
| 
 | |
|         if (eap)
 | |
|             return true;
 | |
| 
 | |
|         if (pkt_counts >= tx_info->fc_hi_watermark[fifo]) {   /* queue full */
 | |
|             //tx_info->stats[fifo].drop++;
 | |
|             return true;
 | |
|         } else
 | |
|             return false;
 | |
| 
 | |
| }
 | |
| 
 | |
| static void nvt_tx_block_check(struct _tx_info *tx_info,
 | |
|                         struct _nvt_if *nvt_if, u32 fifo)
 | |
| {
 | |
|         u32 pkt_counts = nvt_get_total_skb_counts(tx_info, fifo);
 | |
|         //u32 fc_high_watermark = tx_info->fc_hi_watermark;    // 128
 | |
|         //u32 fc_low_watermark = tx_info->fc_low_watermark;    // 64
 | |
|         struct netdev_queue *txq;
 | |
|         //int i=0;
 | |
| 
 | |
|         /* check stop when enqueue */
 | |
|         if (pkt_counts >= tx_info->fc_hi_watermark[fifo]) {
 | |
|              nvt_dbg(TX, "%s, pkt > highwatermark\n", __func__);
 | |
|              if (nvt_txmq == 0) {
 | |
|                   if (tx_info->is_netif_stop == 0) {
 | |
|                       /* do nothing now */
 | |
|                       /* netif_stop_queue(nvt_if->ndev); */
 | |
|                       /* tx_info->is_netif_stop = 1; */
 | |
|                   }
 | |
|              } else {
 | |
|                   txq = netdev_get_tx_queue(nvt_if->ndev, fifo);
 | |
|                   if (!netif_tx_queue_stopped(txq)) {
 | |
|                        netif_tx_stop_queue(txq);
 | |
|                   }
 | |
|              }
 | |
|         } else if (pkt_counts < tx_info->fc_hi_watermark[fifo]) {
 | |
|              nvt_dbg(TX, "%s, pkt < highwatermark\n", __func__);
 | |
|              if (nvt_txmq == 0) {
 | |
|                   if (tx_info->is_netif_stop == 1) {
 | |
|                       /* do nothing now */
 | |
|                       /* netif_wake_queue(nvt_if->ndev); */
 | |
|                       /* tx_info->is_netif_stop = 0; */
 | |
|                   }
 | |
|              } else {
 | |
|                   txq = netdev_get_tx_queue(nvt_if->ndev, fifo);
 | |
|                   if (netif_tx_queue_stopped(txq)) {
 | |
|                        netif_tx_wake_queue(txq);
 | |
|                   }
 | |
|              }
 | |
|         }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static int nvt_tx_enqueue(struct _tx_info *tx_info, u32 fifo,
 | |
|                 struct sk_buff *skb, struct _nvt_if *nvt_if, bool eap)
 | |
| {
 | |
|         int err = 0;
 | |
|         /* Do not handle forward packet here */
 | |
|         u8 forward_magic_pat = NVT_SKB_CB(skb)->forward_magic_pat;
 | |
|         if (forward_magic_pat != 0x7f) {
 | |
|                 /* Avoid stop BE queue, because of special frame, e.g. eapol */
 | |
|                 #ifndef CONFIG_WIFI_TUNING_PHASE_I
 | |
|                 if (nvt_txmq == 0 || fifo == NV_TX_FIFO_AC_BE) {
 | |
|                 #else
 | |
|                 if (nvt_txmq == 0 || eap) {
 | |
|                 #endif
 | |
|                         /* do nothing now */
 | |
|                         /* err = nvt_fc_block(tx_info, true, nvt_if, fifo); */
 | |
|                 } else {
 | |
|                         #ifndef CONFIG_WIFI_TUNING_PHASE_I
 | |
|                         err = nvt_fc_block(tx_info, true, nvt_if, fifo);
 | |
|                         #else
 | |
|                         if (fifo != NV_TX_FIFO_AC_VO)
 | |
|                             err = nvt_fc_drop_check(tx_info, fifo, eap);
 | |
|                         #endif
 | |
|                 }
 | |
|         }
 | |
|         /* If special frame, enqueue it in vo_list_head unconditionally */
 | |
|         if (eap == 1) {
 | |
|                 skb_queue_head(&tx_info->vo_queue_list, skb);
 | |
|                 goto done;
 | |
|         }
 | |
|         if (err == true)
 | |
|                 return -EINVAL;
 | |
|         switch (fifo) {
 | |
|         case NV_TX_FIFO_AC_BK:
 | |
|                 #ifndef CONFIG_WIFI_TUNING_PHASE_I
 | |
|                 if (forward_magic_pat != 0x7f) {
 | |
|                         if (tx_info->bk_queue_list.qlen > tx_info->max_qcnt) {
 | |
|                                 err = true;
 | |
|                                 goto done;
 | |
|                         }
 | |
|                 } else {
 | |
|                         if (tx_info->bk_queue_list.qlen > tx_info->max_fqcnt) {
 | |
|                                 err = true;
 | |
|                                 goto done;
 | |
|                         }
 | |
|                 }
 | |
|                 #endif
 | |
|                 skb_queue_tail(&tx_info->bk_queue_list, skb);
 | |
|                 break;
 | |
|         case NV_TX_FIFO_AC_BE:
 | |
|                 #ifndef CONFIG_WIFI_TUNING_PHASE_I
 | |
|                 if (forward_magic_pat != 0x7f) {
 | |
|                         if (tx_info->be_queue_list.qlen > tx_info->max_qcnt) {
 | |
|                                 err = true;
 | |
|                                 goto done;
 | |
|                         }
 | |
|                 } else {
 | |
|                         if (tx_info->be_queue_list.qlen > tx_info->max_fqcnt) {
 | |
|                                 err = true;
 | |
|                                 goto done;
 | |
|                         }
 | |
|                 }
 | |
|                 #endif
 | |
|                 skb_queue_tail(&tx_info->be_queue_list, skb);
 | |
|                 break;
 | |
|         case NV_TX_FIFO_AC_VI:
 | |
|                 #ifndef CONFIG_WIFI_TUNING_PHASE_I
 | |
|                 if (forward_magic_pat != 0x7f) {
 | |
|                         if (tx_info->vi_queue_list.qlen > tx_info->max_qcnt) {
 | |
|                                 err = true;
 | |
|                                 goto done;
 | |
|                         }
 | |
|                 } else {
 | |
|                         if (tx_info->vi_queue_list.qlen > tx_info->max_fqcnt) {
 | |
|                                 err = true;
 | |
|                                 goto done;
 | |
|                         }
 | |
|                 }
 | |
|                 #endif
 | |
|                 skb_queue_tail(&tx_info->vi_queue_list, skb);
 | |
|                 break;
 | |
|         case NV_TX_FIFO_AC_VO:
 | |
|                 #ifndef CONFIG_WIFI_TUNING_PHASE_I
 | |
|                 if (forward_magic_pat != 0x7f) {
 | |
|                         if (tx_info->vo_queue_list.qlen > tx_info->max_qcnt) {
 | |
|                                 err = true;
 | |
|                                 goto done;
 | |
|                         }
 | |
|                 } else {
 | |
|                         if (tx_info->vo_queue_list.qlen > tx_info->max_fqcnt) {
 | |
|                                 err = true;
 | |
|                                 goto done;
 | |
|                         }
 | |
|                 }
 | |
|                 #endif
 | |
|                 skb_queue_tail(&tx_info->vo_queue_list, skb);
 | |
|                 break;
 | |
|         default:
 | |
|                 err = -EINVAL;
 | |
|                 /* Do nothing */
 | |
|                 break;
 | |
|         }
 | |
| done:
 | |
|         return err;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * nvt_shedule_tx_dequeue - schedule to dequeue tx skb or not
 | |
|  * @tx_info: tx structrue
 | |
|  *
 | |
|  * if flow control is enable
 | |
|  *     1. if we have the credit or public credit, and then we call
 | |
|  *        schedule work to dequeue tx skb
 | |
|  *     2. if we don't have credit or public credit, and we do not call schedule
 | |
|  *        work to dequeue tx skb
 | |
|  * if flow control is disable
 | |
|  *        we always call schedule work once we have tx skb
 | |
|  *
 | |
|  * Return: NULL
 | |
|  */
 | |
| void nvt_shedule_tx_dequeue(struct _tx_info *tx_info)
 | |
| {
 | |
|         u8 fifo_deq = 0;
 | |
|         nvt_dbg(TX, "%s\n", __func__);
 | |
|         nvt_dbg(TX, "fifo_credit_map=0x%x, tx_queue=0x%x\n",
 | |
|                 tx_info->fifo_credit_map, tx_info->fifo_txqueue_map);
 | |
|         /* If some packets in a queue and that queue has credit */
 | |
|         if (tx_info->fifo_credit_map & tx_info->fifo_txqueue_map) {
 | |
|                 fifo_deq = 1;
 | |
|         /* If some packet in a queue and public has credit */
 | |
|         } else if ((tx_info->fifo_txqueue_map > 0) &&
 | |
|                    (tx_info->fifo_credit_map & (1 << 4))) {
 | |
|                 fifo_deq = 1;
 | |
|         } else {
 | |
|                 fifo_deq = 0;
 | |
|         }
 | |
|         /* fifo_deq = 1 or some packets in queue without flow control */
 | |
|         if ((fifo_deq == 1) ||
 | |
|             (!nvt_fc_active(tx_info) && tx_info->fifo_txqueue_map)) {
 | |
|                 schedule_work(&tx_info->tx_queue_work);
 | |
|         }
 | |
| }
 | |
| 
 | |
| static struct sk_buff *nvt_tx_dequeue(struct _tx_info *tx_info, u32 fifo)
 | |
| {
 | |
|         struct sk_buff *skb = NULL;
 | |
| 
 | |
|         switch (fifo) {
 | |
|         case NV_TX_FIFO_AC_BK:
 | |
|                 skb = skb_dequeue(&tx_info->bk_queue_list);
 | |
|                 break;
 | |
|         case NV_TX_FIFO_AC_BE:
 | |
|                 skb = skb_dequeue(&tx_info->be_queue_list);
 | |
|                 break;
 | |
|         case NV_TX_FIFO_AC_VI:
 | |
|                 skb = skb_dequeue(&tx_info->vi_queue_list);
 | |
|                 break;
 | |
|         case NV_TX_FIFO_AC_VO:
 | |
|                 skb = skb_dequeue(&tx_info->vo_queue_list);
 | |
|                 break;
 | |
|         default:
 | |
|                 /* Do nothing */
 | |
|                 break;
 | |
|         }
 | |
|         return skb;
 | |
| }
 | |
| 
 | |
| static void nvt_tx_hosthdrpush(struct sk_buff *skb,
 | |
|         u32 fifo, bool is_public, s32 credit_lender)
 | |
| {
 | |
|         struct _nvt_hwinfo_tx *nvt_hwinfo_tx;
 | |
|         struct ethhdr *eh = (struct ethhdr *)(skb->data);
 | |
|         //bool pae = eh->h_proto == htons(ETH_P_PAE);
 | |
|         bool pae = 0;
 | |
|         u8 q_num = 0;
 | |
|         s32 q_lender_num = 0;
 | |
|         skb_push(skb, NVT_TX_HOSTHEADER);
 | |
| 
 | |
|         nvt_hwinfo_tx = (struct _nvt_hwinfo_tx *)(skb->data);
 | |
|         memset(nvt_hwinfo_tx, 0x0, sizeof(struct _nvt_hwinfo_tx));
 | |
| 
 | |
|         nvt_hwinfo_tx->word1.pktlen = cpu_to_be16(skb->len - NVT_TX_HOSTHEADER);
 | |
| 
 | |
|         if ((fifo & 0x07) == 0x00) {
 | |
|                 q_num = 1; /* VO */
 | |
|         } else if ((fifo & 0x07) == 0x01) {
 | |
|                 q_num = 2; /* VI */
 | |
|         } else if ((fifo & 0x07) == 0x02) {
 | |
|                 q_num = 3; /* BE */
 | |
|         } else {
 | |
|                 q_num = 4; /* BK */
 | |
|         }
 | |
|         nvt_hwinfo_tx->word0 = (q_num & 0x07) | ((is_public & 0x01) << 29);
 | |
| 
 | |
|         /*get and fill the q_lender in host header*/
 | |
|         if (credit_lender != -1) {
 | |
|                 if ((credit_lender & 0x07) == 0x00) {
 | |
|                         q_lender_num = 0; /* VO */
 | |
|                 } else if ((credit_lender & 0x07) == 0x01) {
 | |
|                         q_lender_num = 1; /* VI */
 | |
|                 } else if ((credit_lender & 0x07) == 0x02) {
 | |
|                         q_lender_num = 2; /* BE */
 | |
|                 } else {
 | |
|                         q_lender_num = 3; /* BK */
 | |
|                 }
 | |
|                 nvt_hwinfo_tx->word0 |= (0x00c00000 & (q_lender_num<<22));
 | |
|                 /*set the bit24 to indicate this skb is borrowed by other AC*/
 | |
|                 nvt_hwinfo_tx->word0 |= (1 << 24);
 | |
|                 nvt_dbg(TX, "q_lender_num: %d\n", q_lender_num + 1);
 | |
|         }
 | |
| 
 | |
|         /* disable control bit of word0 */
 | |
|         nvt_hwinfo_tx->word0 = nvt_hwinfo_tx->word0 & 0x7fffffff;
 | |
|         /* enable ethernet bit of word0 */
 | |
|         nvt_hwinfo_tx->word0 = nvt_hwinfo_tx->word0 | 0x00080000;
 | |
| 
 | |
|         if (eh->h_proto == htons(ETH_P_PAE) ||
 | |
|                 eh->h_proto == htons(ETH_P_WAPI)) {
 | |
|                 pae = 1;
 | |
|         }
 | |
|         if (pae) {
 | |
|                 nvt_dbg(TX, "This is TX EAPOL\n");
 | |
|                 nvt_hwinfo_tx->word0 = nvt_hwinfo_tx->word0 | 0x08000000;
 | |
|         }
 | |
| 
 | |
|         nvt_hwinfo_tx->word0 = cpu_to_be32(nvt_hwinfo_tx->word0);
 | |
|         nvt_hwinfo_tx->word1.offset = 8;
 | |
| }
 | |
| 
 | |
| s32 nvt_get_credit_lender(struct _tx_info *tx_info, u8 fifo_borrower)
 | |
| {
 | |
|         s8 fifo;
 | |
| 
 | |
|         /*Check traffic before get credit lender*/
 | |
|         for (fifo = NV_TX_FIFO_AC_BK; fifo > -1; fifo--) {
 | |
|                 if (fifo != fifo_borrower) {
 | |
|                         if (tx_info->fifo_enqpkt[fifo])
 | |
|                                 return -1;
 | |
|                 }
 | |
| 
 | |
|         }
 | |
| 
 | |
|         for (fifo = NV_TX_FIFO_AC_BK; fifo > -1; fifo--) {
 | |
|                 /*check if overflow*/
 | |
|                 if (fifo > NV_TX_FIFO_AC_BK)
 | |
|                         break;
 | |
|                 /*check if potential lender has no enqueue packets
 | |
|                  * and has credit*/
 | |
|                 if (fifo != fifo_borrower && (!tx_info->fifo_enqpkt[fifo]) &&
 | |
|                                 tx_info->fifo_credit[fifo]) {
 | |
| 
 | |
|                         nvt_dbg(TX, "Credit lender:%d\n", fifo);
 | |
|                         return fifo;
 | |
|                 }
 | |
|         }
 | |
|         return -1;
 | |
| }
 | |
| 
 | |
| bool is_remain_lender_credits(struct _tx_info *tx_info, u8 fifo_borrower)
 | |
| {
 | |
|         u8 vo_remains = tx_info->fifo_credit[NV_TX_FIFO_AC_VO];
 | |
|         u8 vi_remains = tx_info->fifo_credit[NV_TX_FIFO_AC_VI];
 | |
|         u8 be_remains = tx_info->fifo_credit[NV_TX_FIFO_AC_BK];
 | |
|         u8 remain_credits = vo_remains + vi_remains + be_remains;
 | |
|         if (tx_info->fifo_credit[fifo_borrower] == 0 && remain_credits > 0) {
 | |
|                 return 1;
 | |
|         } else {
 | |
|             return 0;
 | |
|         }
 | |
| }
 | |
| 
 | |
| /* nvt_tx_dequeue_by_borrow_credit should be used cautionly
 | |
|  * Make sure there is tx_info lock protection outside this
 | |
|  * function
 | |
|  */
 | |
| void nvt_tx_dequeue_by_borrow_credit(struct _tx_info *tx_info,
 | |
|                 u8 fifo_borrower)
 | |
| {
 | |
|         struct sk_buff *skb;
 | |
|         struct _nvt_adapter *nvt_adapter;
 | |
|         s32 fifo_lender;
 | |
|         s32 err = 0;
 | |
|         nvt_adapter = tx_info->nvt_adapter;
 | |
| 
 | |
|         //spin_lock_irqsave(&tx_info->lock, flags);
 | |
|         while (is_remain_lender_credits(tx_info, fifo_borrower)) {
 | |
|                 /*find the lender*/
 | |
|                 fifo_lender = nvt_get_credit_lender(tx_info,
 | |
|                                 fifo_borrower);
 | |
|                 if (fifo_lender > -1) {
 | |
|                         skb = nvt_tx_dequeue(tx_info, fifo_borrower);
 | |
|                         nvt_dbg(TX, "credit borrow: %d\n", fifo_borrower);
 | |
|                         if (skb == NULL) {
 | |
|                                 break;
 | |
|                         }
 | |
|                         if (tx_info->fifo_enqpkt[fifo_borrower] == 0) {
 | |
|                                nvt_dbg(ERROR,
 | |
|                                      "fifo borrower has no enqued pkts\n");
 | |
|                                break;
 | |
|                         } else {
 | |
|                                tx_info->fifo_enqpkt[fifo_borrower]--;
 | |
|                                #if 1   /* tx statistic */
 | |
|                                tx_info->stats[fifo_borrower].out++;
 | |
|                                tx_info->stats[fifo_borrower].out_sz += skb->len;
 | |
|                                #endif
 | |
|                         }
 | |
|                         if (tx_info->fifo_enqpkt[fifo_borrower] == 0) {
 | |
|                                 tx_info->fifo_txqueue_map &=
 | |
|                                         ~(1 << fifo_borrower);
 | |
|                         }
 | |
| 
 | |
|                         tx_info->fifo_credit[fifo_lender]--;
 | |
|                         if (tx_info->fifo_credit[fifo_lender] == 0) {
 | |
|                                 tx_info->fifo_credit_map &= ~(1 << fifo_lender);
 | |
|                         }
 | |
| 
 | |
|                         nvt_tx_hosthdrpush(skb, fifo_borrower, 0, fifo_lender);
 | |
|                         tx_info->nvt_tx_test.tx_deque_cnt++;
 | |
|                         tx_info->nvt_tx_test.end_time = jiffies;
 | |
|                         //spin_unlock_irqrestore(&tx_info->lock, flags);
 | |
|                         if ((tx_info->nvt_tx_test.tx_test_mode &
 | |
|                                                 NVT_TX_DEQUE_POINT) != 0) {
 | |
|                                 err = -1;
 | |
|                         } else {
 | |
|                                 err = nvt_bus_txdata(nvt_adapter->nvt_bus, skb);
 | |
|                         }
 | |
|                         if (err < 0) {
 | |
|                                 tx_info->nvt_tx_test.tx_deque_drop_cnt++;
 | |
|                                 dev_kfree_skb(skb);
 | |
|                         }
 | |
|                 } else {
 | |
|                         break;
 | |
|                 }
 | |
|         }
 | |
|         nvt_dbg(TX, "End of nvt_tx_dequeue_by_borrow: %d\n", fifo_borrower);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * nvt_tx_dequeue_worker - dequeue tx skb and send it to usb
 | |
|  * @work: work queue structrue
 | |
|  *
 | |
|  * we always dequeue tx queue from high priority to low priority
 | |
|  * if flow control is enable
 | |
|  *     1. First, we check own credit. If we ran out of the own credit,
 | |
|  *        we check public credit
 | |
|  *     2. if we don't have own credit and public credit, and we do not
 | |
|  *        dequeue tx skb which is enqueued.
 | |
|  * if flow control is disable
 | |
|  *        we always dequeue skb once we have tx skb which is enqueued
 | |
|  *
 | |
|  * Return: NULL
 | |
|  */
 | |
| void nvt_tx_dequeue_worker(struct work_struct *work)
 | |
| {
 | |
|         struct _tx_info *tx_info;
 | |
|         struct _nvt_adapter *nvt_adapter;
 | |
|         struct _nvt_if *nvt_if = NULL;
 | |
|         struct sk_buff *skb;
 | |
|         u32 fifo;
 | |
|         ulong flags;
 | |
|         s32 err = 0;
 | |
|         u8 ifidx = 0;
 | |
| 
 | |
|         tx_info = container_of(work, struct _tx_info, tx_queue_work);
 | |
|         nvt_adapter = tx_info->nvt_adapter;
 | |
|         /* Get nvt_if pointer */
 | |
|         nvt_if = nvt_get_if_by_index(nvt_adapter, ifidx);
 | |
|         if (!nvt_if) {
 | |
|                 nvt_dbg(TX, "%s, nvt_if is NULL, return\n", __func__);
 | |
|                 return;
 | |
|         }
 | |
| 
 | |
|         nvt_dbg(TX, "%s, tx_info->bus_flow_blocked=%d\n",
 | |
|                 __func__, tx_info->bus_flow_blocked);
 | |
| 
 | |
|         /* Strictly Priority Dequeue Tx form VO -> VI -> BE -> BK. */
 | |
|         nvt_dbg(TX, "Strictly Priority Dequeue Algorithm\n");
 | |
|         spin_lock_irqsave(&tx_info->lock, flags);
 | |
|         //for (fifo = NV_TX_FIFO_AC_VO; fifo <= 3 &&
 | |
|         //        !tx_info->bus_flow_blocked; fifo++) {
 | |
|         for (fifo = NV_TX_FIFO_AC_VO; fifo <= 3; fifo++) {
 | |
|                 nvt_dbg(TX, "fifo=%d is prepare Dequeueing\n", fifo);
 | |
|                 /* Handle Tx packet without flow control here */
 | |
|                 if (!nvt_fc_active(tx_info)) {
 | |
|                         while ((skb = nvt_tx_dequeue(tx_info, fifo))) {
 | |
|                                 nvt_dbg(TX, "fifo=%d, fifo_enqpkt=%d\n",
 | |
|                                         fifo, tx_info->fifo_enqpkt[fifo]);
 | |
|                                 /* prevent enqpkt overflow */
 | |
|                                 if (tx_info->fifo_enqpkt[fifo] == 0) {
 | |
|                                         nvt_dbg(TX,
 | |
|                                                 "fifo_enqpkt=0, keep zero\n");
 | |
|                                 } else {
 | |
|                                         tx_info->fifo_enqpkt[fifo]--;
 | |
|                                         #if 1  /* tx statistic */
 | |
|                                         tx_info->stats[fifo].out++;
 | |
|                                         tx_info->stats[fifo].out_sz += skb->len;
 | |
|                                         #endif
 | |
|                                 }
 | |
|                                 if (tx_info->fifo_enqpkt[fifo] == 0) {
 | |
|                                         tx_info->fifo_txqueue_map &=
 | |
|                                                 ~(1 << fifo);
 | |
|                                 }
 | |
|                                 #ifndef CONFIG_WIFI_TUNING_PHASE_I
 | |
|                                 nvt_fc_block(tx_info, false, nvt_if, fifo);
 | |
|                                 #else
 | |
|                                 if (fifo == NV_TX_FIFO_AC_VO)
 | |
|                                      nvt_tx_block_check(tx_info, nvt_if, fifo);
 | |
|                                 #endif
 | |
|                                 /* Insert Tx Host Header */
 | |
|                                 nvt_tx_hosthdrpush(skb, fifo, 0, -1);
 | |
|                                 tx_info->nvt_tx_test.tx_deque_cnt++;
 | |
|                                 tx_info->nvt_tx_test.end_time = jiffies;
 | |
|                                 spin_unlock_irqrestore(&tx_info->lock, flags);
 | |
|                                 if ((tx_info->nvt_tx_test.tx_test_mode &        
 | |
|                                                 NVT_TX_DEQUE_POINT) != 0) {
 | |
|                                         err = -1;
 | |
|                                 } else {
 | |
|                                         err = nvt_bus_txdata(nvt_adapter->nvt_bus,
 | |
|                                                 skb);
 | |
|                                 }
 | |
|                                 spin_lock_irqsave(&tx_info->lock, flags);
 | |
|                                 if (err < 0) {
 | |
|                                         tx_info->nvt_tx_test.tx_deque_drop_cnt++;
 | |
|                                         dev_kfree_skb(skb);
 | |
|                                 }
 | |
|                                 //if (tx_info->bus_flow_blocked) {
 | |
|                                 //        nvt_dbg(TX, "bus_flow_blocked on\n");
 | |
|                                 //        break;
 | |
|                                 //}
 | |
|                         }
 | |
|                         continue;
 | |
|                 }
 | |
|                 /* Handle Tx packet with flow control here */
 | |
|                 /* Using available fifo credit */
 | |
|                 while (tx_info->fifo_credit[fifo]) {
 | |
|                         skb = nvt_tx_dequeue(tx_info, fifo);
 | |
|                         if (skb == NULL) {
 | |
|                                 break;
 | |
|                         }
 | |
|                         nvt_dbg(TX, "fifo=%d, fifo_enqpkt=%d, fifo_credit=%d\n",
 | |
|                                 fifo,
 | |
|                                 tx_info->fifo_enqpkt[fifo],
 | |
|                                 tx_info->fifo_credit[fifo]);
 | |
|                         /* prevent enqpkt overflow */
 | |
|                         if (tx_info->fifo_enqpkt[fifo] == 0) {
 | |
|                                 nvt_dbg(TX, "fifo_enqpkt=0, keep zero\n");
 | |
|                         } else {
 | |
|                                 tx_info->fifo_enqpkt[fifo]--;
 | |
|                         }
 | |
|                         if (tx_info->fifo_enqpkt[fifo] == 0) {
 | |
|                                 tx_info->fifo_txqueue_map &= ~(1 << fifo);
 | |
|                         }
 | |
|                         tx_info->fifo_credit[fifo]--;
 | |
|                         #if 1  /* tx statistic */
 | |
|                         tx_info->stats[fifo].out++;
 | |
|                         tx_info->stats[fifo].out_sz += skb->len;
 | |
|                         #endif
 | |
|                         if (tx_info->fifo_credit[fifo] == 0) {
 | |
|                                 tx_info->fifo_credit_map &= ~(1 << fifo);
 | |
|                         }
 | |
|                         #ifndef CONFIG_WIFI_TUNING_PHASE_I
 | |
|                         nvt_fc_block(tx_info, false, nvt_if, fifo);
 | |
|                         #else
 | |
|                         if (fifo == NV_TX_FIFO_AC_VO)
 | |
|                             nvt_tx_block_check(tx_info, nvt_if, fifo);
 | |
|                         #endif
 | |
|                         /* Insert Tx Host Header */
 | |
|                         nvt_tx_hosthdrpush(skb, fifo, 0, -1);
 | |
|                         tx_info->nvt_tx_test.tx_deque_cnt++;
 | |
|                         tx_info->nvt_tx_test.end_time = jiffies;
 | |
|                         spin_unlock_irqrestore(&tx_info->lock, flags);
 | |
|                         if ((tx_info->nvt_tx_test.tx_test_mode & NVT_TX_DEQUE_POINT) != 0) {
 | |
|                                 err = -1;
 | |
|                         } else {
 | |
|                                 err = nvt_bus_txdata(nvt_adapter->nvt_bus, skb);
 | |
|                         }
 | |
|                         spin_lock_irqsave(&tx_info->lock, flags);
 | |
|                         if (err < 0) {
 | |
|                                 tx_info->nvt_tx_test.tx_deque_drop_cnt++;
 | |
|                                 dev_kfree_skb(skb);
 | |
|                         }
 | |
|                         //if (tx_info->bus_flow_blocked) {
 | |
|                         //        nvt_dbg(TX, "bus_flow_blocked is ongoing\n");
 | |
|                         //        break;
 | |
|                         //}
 | |
|                 }
 | |
|                 /* Handle Tx packet with flow control here */
 | |
|                 /* Using public credit */
 | |
|                 while ((tx_info->fifo_credit[fifo] == 0) &&
 | |
|                        (tx_info->fifo_credit[NV_TX_FIFO_PUBLIC] != 0) &&
 | |
|                        (!tx_info->bus_flow_blocked)) {
 | |
|                         if ((100 * (tx_info->fifo_public_inuse[fifo])) >
 | |
|                                 (PUBLIC_WEIGHT *
 | |
|                                 tx_info->fifo_max_credit[NV_TX_FIFO_PUBLIC])) {
 | |
|                                 nvt_dbg(TX, "over public credit\n");
 | |
|                                 break;
 | |
|                         }
 | |
|                         skb = nvt_tx_dequeue(tx_info, fifo);
 | |
|                         if (skb == NULL) {
 | |
|                                 break;
 | |
|                         }
 | |
|                         nvt_dbg(TX, "fifo=%d,fifo_enqpkt=%d,public_credit=%d\n",
 | |
|                                 fifo,
 | |
|                                 tx_info->fifo_enqpkt[fifo],
 | |
|                                 tx_info->fifo_credit[NV_TX_FIFO_PUBLIC]);
 | |
|                         /* prevent enqpkt overflow */
 | |
|                         if (tx_info->fifo_enqpkt[fifo] == 0) {
 | |
|                                 nvt_dbg(TX, "fifo_enqpkt=0, keep zero\n");
 | |
|                         } else {
 | |
|                                 tx_info->fifo_enqpkt[fifo]--;
 | |
|                                 #if 1  /* tx statistic */
 | |
|                                 tx_info->stats[fifo].out++;
 | |
|                                 tx_info->stats[fifo].out_sz += skb->len;
 | |
|                                 #endif
 | |
|                         }
 | |
|                         if (tx_info->fifo_enqpkt[fifo] == 0) {
 | |
|                                 tx_info->fifo_txqueue_map &= ~(1 << fifo);
 | |
|                         }
 | |
|                         tx_info->fifo_credit[NV_TX_FIFO_PUBLIC]--;
 | |
|                         tx_info->fifo_public_inuse[fifo]++;
 | |
|                         nvt_dbg(TX, "fifo_public_inuse=%d\n",
 | |
|                                 tx_info->fifo_public_inuse[fifo]);
 | |
|                         if (tx_info->fifo_credit[NV_TX_FIFO_PUBLIC] == 0) {
 | |
|                                 tx_info->fifo_credit_map &=
 | |
|                                         ~(1 << NV_TX_FIFO_PUBLIC);
 | |
|                         }
 | |
|                         #ifndef CONFIG_WIFI_TUNING_PHASE_I
 | |
|                         nvt_fc_block(tx_info, false, nvt_if, fifo);
 | |
|                         #else
 | |
|                         if (fifo == NV_TX_FIFO_AC_VO)
 | |
|                             nvt_tx_block_check(tx_info, nvt_if, fifo);
 | |
|                         #endif
 | |
|                         /* Insert Tx Host Header */
 | |
|                         nvt_tx_hosthdrpush(skb, fifo, 1, -1);
 | |
|                         tx_info->nvt_tx_test.tx_deque_cnt++;
 | |
|                         tx_info->nvt_tx_test.end_time = jiffies;
 | |
|                         spin_unlock_irqrestore(&tx_info->lock, flags);
 | |
|                         if ((tx_info->nvt_tx_test.tx_test_mode &
 | |
|                                                 NVT_TX_DEQUE_POINT) != 0) {
 | |
|                                 err = -1;
 | |
|                         } else {
 | |
|                                 err = nvt_bus_txdata(nvt_adapter->nvt_bus, skb);
 | |
|                         }
 | |
|                         spin_lock_irqsave(&tx_info->lock, flags);
 | |
|                         if (err < 0) {
 | |
|                                 tx_info->nvt_tx_test.tx_deque_drop_cnt++;
 | |
|                                 dev_kfree_skb(skb);
 | |
|                         }
 | |
|                         //if (tx_info->bus_flow_blocked) {
 | |
|                         //        nvt_dbg(TX, "bus_flow_blocked is on\n");
 | |
|                         //        break;
 | |
|                         //}
 | |
|                 }
 | |
| 
 | |
|                 if (fifo == NV_TX_FIFO_AC_BE &&
 | |
|                                 tx_info->is_credit_borrow) {
 | |
|                         nvt_tx_dequeue_by_borrow_credit(tx_info, fifo);
 | |
|                 }
 | |
|         }
 | |
|         spin_unlock_irqrestore(&tx_info->lock, flags);
 | |
| }
 | |
| 
 | |
| static void nvt_process_tx_frame(struct _nvt_if *nvt_if,
 | |
|         struct sk_buff *skb)
 | |
| {
 | |
|         struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
 | |
|         struct _tx_info *tx_info = nvt_adapter->nvt_priv.tx_info;
 | |
|         struct ethhdr *eh = (struct ethhdr *)(skb->data);
 | |
|         //bool eap = eh->h_proto == htons(ETH_P_PAE);
 | |
|         //u8 *tmp = (u8 *)(skb->data);
 | |
|         bool eap = 0;
 | |
|         u32 fifo;
 | |
|         ulong flags;
 | |
|         u32 i = 0;
 | |
| 
 | |
|         nvt_dbg(TX, "%s\n", __func__);
 | |
| 
 | |
|         tx_info->nvt_tx_test.tx_total_cnt++;
 | |
|         if ((tx_info->nvt_tx_test.tx_test_mode & NVT_TX_START_POINT) != 0) {
 | |
|                 dev_kfree_skb(skb);
 | |
|                 return;
 | |
|         }
 | |
| 
 | |
|         /* match magic pattern */
 | |
|         if (NVT_SKB_CB(skb)->forward_magic_pat != 0x7f) {
 | |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
 | |
|                 skb->priority = cfg80211_classify8021d(skb);
 | |
| #else
 | |
|                 skb->priority = cfg80211_classify8021d(skb, NULL);
 | |
| #endif
 | |
|         } else {
 | |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
 | |
|                 nvt_dbg(TX, "Forward packet tos_prio=%d, wifi_prio=%d\n",
 | |
|                         cfg80211_classify8021d(skb), skb->priority);
 | |
| #else
 | |
|                 nvt_dbg(TX, "Forward packet tos_prio=%d, wifi_prio=%d\n",
 | |
|                         cfg80211_classify8021d(skb, NULL), skb->priority);
 | |
| #endif
 | |
|         }
 | |
|         fifo = nvt_tx_prio2fifo[skb->priority];
 | |
| 
 | |
|         /* special frame force to highest priority queue */
 | |
|         if (eh->h_proto == htons(ETH_P_PAE) ||
 | |
|                 eh->h_proto == htons(ETH_P_WAPI)) {
 | |
|                 eap = 1;
 | |
|                 fifo = NV_TX_FIFO_AC_VO;
 | |
|         }
 | |
| 
 | |
|         if (eap) {
 | |
|                 nvt_dbg(TX, "This is Tx EAPOL\n");
 | |
|                 for (i = 0; i < skb->len; i++)
 | |
|                         //printk("%02x:", *(tmp + i));
 | |
|                 nvt_dbg(TX, "\n");
 | |
|                 if (nvt_if->mode == NVT_FW_STA) {
 | |
|                         atomic_inc(&nvt_if->eapol_cnt);
 | |
|                 }
 | |
|                 nvt_dbg(TX, "pend_8021x_cnt = %d\n",
 | |
|                         atomic_read(&nvt_if->eapol_cnt));
 | |
|         }
 | |
| 
 | |
|         nvt_dbg(TX, "fifo=%d skb->priority=%d\n", fifo, skb->priority);
 | |
|         nvt_dbg(TX, "skb->queue_mapping=%d\n", skb->queue_mapping);
 | |
| 
 | |
|         if (tx_info->nvt_tx_test.start_time == 0) {
 | |
|                 tx_info->nvt_tx_test.start_time = jiffies;
 | |
|         }
 | |
| 
 | |
|         spin_lock_irqsave(&tx_info->lock, flags);
 | |
|         /* Enqueue Tx packet */
 | |
|         if (nvt_tx_enqueue(tx_info, fifo, skb, nvt_if, eap)) {
 | |
|                 tx_info->nvt_tx_test.rx_enque_drop_cnt++;
 | |
|                 nvt_dbg(TX, "tx_enqueue fail, drop fifo=%d SKB\n", fifo);
 | |
|                 #if 1  /* tx statistic */
 | |
|                 tx_info->stats[fifo].drop++;
 | |
|                 #endif
 | |
|                 dev_kfree_skb(skb);
 | |
|         } else {
 | |
|                 tx_info->nvt_tx_test.tx_enque_cnt++;
 | |
|                 tx_info->fifo_enqpkt[fifo]++;
 | |
|                 tx_info->fifo_txqueue_map |= 1 << fifo;
 | |
|                 #if 1  /* tx statistic */
 | |
|                 tx_info->stats[fifo].en++;
 | |
|                 #endif
 | |
|                 nvt_dbg(TX, "fifo_enqpkt=%d, fifo_txqueue=0x%x, credit=%d\n",
 | |
|                         tx_info->fifo_enqpkt[fifo], tx_info->fifo_txqueue_map,
 | |
|                         tx_info->fifo_credit[fifo]);
 | |
|                 #ifdef CONFIG_WIFI_TUNING_PHASE_I
 | |
|                 if (fifo == NV_TX_FIFO_AC_VO)
 | |
|                     nvt_tx_block_check(tx_info, nvt_if, fifo);
 | |
|                 #endif
 | |
|                 nvt_shedule_tx_dequeue(tx_info);
 | |
|         }
 | |
|         spin_unlock_irqrestore(&tx_info->lock, flags);
 | |
| }
 | |
| 
 | |
| static int nvt_wdev_open(struct net_device *ndev)
 | |
| {
 | |
|         struct _nvt_if *nvt_if = netdev_priv(ndev);
 | |
|         struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
 | |
| 
 | |
|         nvt_dbg(INFO, "%s: do net dev open\n", __func__);
 | |
| 
 | |
|         if (nvt_bus->state != NVT_BUS_STATE_UP) {
 | |
|                 nvt_dbg(ERROR, "%s: bus is not ready!\n", __func__);
 | |
|         }
 | |
| 
 | |
|         ndev->features &= ~NETIF_F_IP_CSUM;
 | |
| 
 | |
|         set_bit(NVT_IF_ENABLED, &nvt_if->state_flags);
 | |
| 
 | |
|         if (nvt_txmq == 0) {
 | |
|                 netif_start_queue(ndev);
 | |
|         } else {
 | |
|                 netif_tx_start_all_queues(ndev);
 | |
|         }
 | |
|         return 0;
 | |
| }
 | |
| 
 | |
| static s32 nvt_wdev_stop(struct net_device *ndev)
 | |
| {
 | |
|         struct _nvt_if *nvt_if = netdev_priv(ndev);
 | |
|         struct _nvt_cfg80211 *nvt_cfg80211 = nvt_if->nvt_adapter->nvt_cfg80211;
 | |
| 
 | |
|         nvt_dbg(INFO, "%s: do net dev stop\n", __func__);
 | |
| 
 | |
|         nvt_abort_scanning(nvt_cfg80211);
 | |
| 
 | |
|         clear_bit(NVT_IF_ENABLED, &nvt_if->state_flags);
 | |
| 
 | |
|         if (nvt_txmq == 0) {
 | |
|                 netif_stop_queue(ndev);
 | |
|         } else {
 | |
|                 netif_tx_stop_all_queues(ndev);
 | |
|         }
 | |
| 
 | |
|         return 0;
 | |
| }
 | |
| 
 | |
| struct net_device_stats *nvt_wdev_get_stats(struct net_device *ndev)
 | |
| {
 | |
|         struct _nvt_if *nvt_if = netdev_priv(ndev);
 | |
| 
 | |
|         //nvt_dbg(INFO, "%s: get stats from if:%d, mode=%d\n", __func__,
 | |
|         //        nvt_if->fw_if_idx, nvt_if->mode);
 | |
| 
 | |
|         return &nvt_if->net_stats;
 | |
| }
 | |
| 
 | |
| #ifdef CONFIG_WIFI_TUNING_PHASE_II
 | |
| static s32 wlan_io_wifi_tuning(struct _nvt_if *nvt_if, void *req_msg)
 | |
| {
 | |
|         s32 ret = 0;
 | |
| 
 | |
|         u8   found, rsp_idx;
 | |
|         u8   mac_addr[6], rsp[18], rsp_sz = 18;
 | |
|         //u8   clr_after_get;
 | |
|         u8   start;
 | |
|         //s32  qid;
 | |
|         u32  wid, wid_set_val, wid_set_len, interval;
 | |
|         //unsigned int mac_addr_tmp;
 | |
|         struct _sta_sts_in_ap *sta_sts_in_ap;
 | |
|         struct iwreq *wrq = (struct	iwreq *)req_msg;
 | |
| 
 | |
|         struct _nvt_priv *nvt_priv = &(nvt_if->nvt_adapter->nvt_priv);
 | |
|         struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
 | |
| 
 | |
|         struct _wifi_tuning_cmd *wifi_tuning_cmd;
 | |
|         wifi_tuning_cmd = (struct _wifi_tuning_cmd *)wrq->u.data.pointer;
 | |
| 
 | |
|         if (wifi_tuning_cmd->type == WIFI_TUNING_SET) {
 | |
| 
 | |
|             wid = wid_set_val = wid_set_len = 0;
 | |
|             if (wifi_tuning_cmd->cmd_code.set == WIFI_TUNING_SET_INTERVAL) {
 | |
|                 if (wifi_tuning_cmd->buf_len != 4) {
 | |
|                     ret = -1;
 | |
|                     goto exit;
 | |
|                 }
 | |
|                 interval = *((u32 *)(wifi_tuning_cmd->buf));
 | |
|                 if (interval < 1 || interval > 5000) {
 | |
|                     ret = -1;
 | |
|                     goto exit;
 | |
|                 }
 | |
|                 wid = WID_TP_MIN_INTERVAL;
 | |
|                 wid_set_val = (interval & 0xffffffff);
 | |
|                 wid_set_len = 4;
 | |
|                 nvt_dbg(ERROR, "%s: interval:%d\n", __func__, interval);
 | |
|             } else if (wifi_tuning_cmd->cmd_code.set == WIFI_TUNING_SET_START) {
 | |
|                 if (wifi_tuning_cmd->buf_len != 1) {
 | |
|                     ret = -1;
 | |
|                     goto exit;
 | |
|                 }
 | |
|                 start = *((u8 *)(wifi_tuning_cmd->buf));
 | |
|                 if (start != 1 && start != 0) {
 | |
|                     ret = -1;
 | |
|                     goto exit;
 | |
|                 }
 | |
|                 nvt_dbg(ERROR, "%s: start:%d\n", __func__, start);
 | |
| 
 | |
|                 wid = WID_TP_MONITOR;
 | |
|                 wid_set_val = (start & 0xff);
 | |
|                 wid_set_len = 1;
 | |
|             } else {
 | |
|                 ret = -1;
 | |
|                 goto exit;
 | |
|             }
 | |
| 
 | |
|             // Send wid to MAC
 | |
|             if (wid || wid_set_val || wid_set_len) {
 | |
|                 ret = nvt_icfg_lock(nvt_bus);
 | |
|                 if (ret < 0) {
 | |
|                     goto exit;
 | |
|                 }
 | |
|                 ret = nvt_icfg_reset(nvt_bus);
 | |
|                 if (ret < 0) {
 | |
|                     nvt_icfg_unlock(nvt_bus);
 | |
|                     goto exit;
 | |
|                 }
 | |
|                 ret = nvt_icfg_add(nvt_bus, NVT_ICFG_SET, wid,
 | |
|                                    (u8 *)&wid_set_val, wid_set_len);
 | |
|                 if (ret < 0) {
 | |
|                     nvt_icfg_unlock(nvt_bus);
 | |
|                     goto exit;
 | |
|                 }
 | |
|                 ret = nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0);
 | |
|                 if (ret < 0) {
 | |
|                     nvt_icfg_unlock(nvt_bus);
 | |
|                     goto exit;
 | |
|                 }
 | |
| 
 | |
|                 nvt_icfg_unlock(nvt_bus);
 | |
|             }
 | |
|         } else if (wifi_tuning_cmd->type == WIFI_TUNING_GET) {
 | |
|             if (wifi_tuning_cmd->cmd_code.get == WIFI_TUNING_GET_STATUS) {
 | |
|                 if (wifi_tuning_cmd->buf_len != ETH_ALEN + 1) {
 | |
|                     ret = -1;
 | |
|                     goto exit;
 | |
|                 }
 | |
| 
 | |
|                 memcpy(mac_addr, wifi_tuning_cmd->buf, ETH_ALEN);
 | |
| 
 | |
|                 #if 0  /* debug */
 | |
|                 memcpy((void *)&clr_after_get,
 | |
|                        (void *)((u8 *)(wifi_tuning_cmd->buf) + ETH_ALEN), 1);
 | |
| 
 | |
|                 nvt_dbg(ERROR, "%s: MAC %02x:%02x:%02x:%02x:%02x:%02x ",
 | |
|                         __func__, mac_addr[0], mac_addr[1], mac_addr[2],
 | |
|                         mac_addr[3], mac_addr[4], mac_addr[5]);
 | |
|                 nvt_dbg(ERROR, "clr:%d\n", clr_after_get);
 | |
|                 #endif
 | |
| 
 | |
|                 found = 0;
 | |
|                 list_for_each_entry(sta_sts_in_ap, &nvt_priv->sta_list_in_ap,
 | |
|                                   list)
 | |
|                 {
 | |
|                     if (!memcmp(&sta_sts_in_ap->mac_addr, mac_addr, ETH_ALEN)) {
 | |
|                         found = 1;
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 if (found == 1) {
 | |
| 
 | |
|                     nvt_dbg(ERROR,
 | |
|                            "%s: found MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
 | |
|                            __func__, mac_addr[0], mac_addr[1], mac_addr[2],
 | |
|                            mac_addr[3], mac_addr[4], mac_addr[5]);
 | |
| 
 | |
|                     rsp_idx = 0;
 | |
|                     memcpy(&rsp[rsp_idx], sta_sts_in_ap->mac_addr, ETH_ALEN);
 | |
| 
 | |
|                     rsp_idx += ETH_ALEN;
 | |
|                     #if 0
 | |
|                     memcpy(&rsp[rsp_idx], &sta_sts_in_ap->phyrate,
 | |
|                         sizeof(sta_sts_in_ap->phyrate));
 | |
|                     #else
 | |
|                     memcpy(&rsp[rsp_idx], &sta_sts_in_ap->t_put,
 | |
|                         sizeof(sta_sts_in_ap->t_put));
 | |
|                     #endif
 | |
| 
 | |
|                     rsp_idx += sizeof(sta_sts_in_ap->phyrate);
 | |
|                     memcpy(&rsp[rsp_idx], &sta_sts_in_ap->enq_in_vo_be,
 | |
|                         sizeof(sta_sts_in_ap->enq_in_vo_be));
 | |
| 
 | |
|                     rsp_idx += sizeof(sta_sts_in_ap->enq_in_vo_be);
 | |
|                     #if 0
 | |
|                     memcpy(&rsp[rsp_idx], &sta_sts_in_ap->drop_in_be,
 | |
|                         sizeof(sta_sts_in_ap->drop_in_be));
 | |
|                     #else
 | |
|                     memcpy(&rsp[rsp_idx], &sta_sts_in_ap->mac_vodropframe,
 | |
|                         sizeof(sta_sts_in_ap->mac_vodropframe));
 | |
|                     #endif
 | |
| 
 | |
|                 } else {
 | |
|                     nvt_dbg(ERROR,
 | |
|                         "%s: No info for MAC:%02x:%02x:%02x:%02x:%02x:%02x\n",
 | |
|                         __func__, mac_addr[0], mac_addr[1], mac_addr[2],
 | |
|                         mac_addr[3], mac_addr[4], mac_addr[5]);
 | |
|                     memset(rsp, 0, rsp_sz);
 | |
|                 }
 | |
| 
 | |
|                 memcpy(wifi_tuning_cmd->buf, rsp, rsp_sz);
 | |
|                 wifi_tuning_cmd->buf_len = rsp_sz;
 | |
|             } else {
 | |
| 
 | |
|             }
 | |
|         }
 | |
| exit:
 | |
|     return ret;
 | |
| 
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static s32 nvt_wdev_ioctl(struct net_device *ndev, struct ifreq *ifr, s32 cmd)
 | |
| {
 | |
|         #ifdef CONFIG_WIFI_TUNING_PHASE_II
 | |
|         struct _nvt_if *nvt_if = netdev_priv(ndev);
 | |
|         //struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
 | |
|         #endif
 | |
| 
 | |
|         int result = 0;
 | |
| 
 | |
|         nvt_dbg(ERROR, "%s: cmd:%x\n", __func__, cmd);
 | |
| 
 | |
|         switch (cmd) {
 | |
|         #ifdef CONFIG_WIFI_TUNING_PHASE_II
 | |
|         case NVTWLAN_IOCTL_TP_MONI:
 | |
|              result = wlan_io_wifi_tuning(nvt_if, ifr);
 | |
|              break;
 | |
|         #endif
 | |
|         default:
 | |
|              result = -1;
 | |
|              break;
 | |
|         }
 | |
| 
 | |
|         return result;
 | |
| 
 | |
| }
 | |
| 
 | |
| static int nvt_tx_xmit(struct sk_buff *skb,
 | |
|                         struct net_device *ndev)
 | |
| {
 | |
|         struct _nvt_if *nvt_if = netdev_priv(ndev);
 | |
|         struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
 | |
|         struct _nvt_cfg80211 *cfg = nvt_adapter->nvt_cfg80211;
 | |
|         struct ethhdr *eh;
 | |
| 
 | |
|         /* Check Interface is OK */
 | |
|         if (!(ndev->flags & IFF_UP)) {
 | |
|                 nvt_dbg(TX, "Interface is not ready for TX\n");
 | |
|                 if (nvt_txmq == 0) {
 | |
|                         netif_stop_queue(ndev);
 | |
|                 } else {
 | |
|                         netif_tx_stop_all_queues(ndev);
 | |
|                 }
 | |
|                 dev_kfree_skb(skb);
 | |
|                 goto done;
 | |
|         }
 | |
| 
 | |
|         /* Make sure there's enough room for any header */
 | |
|         if (skb_headroom(skb) < nvt_if->hdrlen) {
 | |
|                 struct sk_buff *skb2;
 | |
| 
 | |
|                 nvt_dbg(TX, "insufficient headroom, %d\n", skb_headroom(skb));
 | |
|                 skb2 = skb_realloc_headroom(skb, ndev->hard_header_len);
 | |
|                 dev_kfree_skb(skb);
 | |
|                 skb = skb2;
 | |
|                 if (skb == NULL) {
 | |
|                         nvt_dbg(TX, "skb_realloc_headroom failed\n");
 | |
|                         goto done;
 | |
|                 }
 | |
|         }
 | |
| 
 | |
|         /* Check length for ethernet packet */
 | |
|         if (skb->len < sizeof(*eh)) {
 | |
|                 dev_kfree_skb(skb);
 | |
|                 goto done;
 | |
|         }
 | |
| 
 | |
|         /* Check STA mode can send data */
 | |
|         if (!test_bit(NVT_IF_CONNECTED, &nvt_if->state_flags)) {
 | |
|                 nvt_dbg(TX, "device is not in connected state\n");
 | |
|                 dev_kfree_skb(skb);
 | |
|                 goto done;
 | |
|         } else {
 | |
|                 /* Check Scanning or not */
 | |
|                 if (test_bit(SCAN_PROCESS, &cfg->scan_sts)) {
 | |
|                         nvt_dbg(TX, "Scanning is process now\n");
 | |
|                         dev_kfree_skb(skb);
 | |
|                         goto done;
 | |
|                 }
 | |
|                 nvt_process_tx_frame(nvt_if, skb);
 | |
|         }
 | |
|         nvt_if->net_stats.tx_packets++;
 | |
|         nvt_if->net_stats.tx_bytes += skb->len;
 | |
| done:
 | |
|         return NETDEV_TX_OK;
 | |
| }
 | |
| 
 | |
| static int nvt_wdev_xmit_forward(struct sk_buff *skb,
 | |
|         struct net_device *ndev)
 | |
| {
 | |
|         nvt_dbg(TX, "%s\n", __func__);
 | |
|         memset(skb->cb, 0, sizeof(skb->cb));
 | |
|         //set the forward magic pattern
 | |
|         NVT_SKB_CB(skb)->forward_magic_pat = 0x7f;
 | |
|         return nvt_tx_xmit(skb, ndev);
 | |
| }
 | |
| 
 | |
| static int nvt_wdev_start_xmit(struct sk_buff *skb,
 | |
|         struct net_device *ndev)
 | |
| {
 | |
|         nvt_dbg(TX, "%s\n", __func__);
 | |
|         //Clear skb->cb
 | |
|         memset(skb->cb, 0, sizeof(skb->cb));
 | |
|         return nvt_tx_xmit(skb, ndev);
 | |
| }
 | |
| 
 | |
| static s32 nvt_wdev_set_mac_addr(struct net_device *ndev, void *addr)
 | |
| {
 | |
|         s32 ret;
 | |
|         struct _nvt_if *nvt_if = netdev_priv(ndev);
 | |
|         struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
 | |
|         struct sockaddr *saddr = addr;
 | |
| 
 | |
|         memcpy(nvt_adapter->mac_addr, saddr->sa_data, ETH_ALEN);
 | |
| 
 | |
|         ret = nvt_set_macaddress_to_fw(nvt_adapter);
 | |
|         if (ret < 0) {
 | |
|                 nvt_dbg(INFO, "%s: set MAC address fail\n", __func__);
 | |
|                 return -EPERM;
 | |
|         }
 | |
| 
 | |
|         memcpy(ndev->dev_addr, nvt_if->nvt_adapter->mac_addr, ETH_ALEN);
 | |
| 
 | |
|         return 0;
 | |
| }
 | |
| 
 | |
| /* Not supported yet */
 | |
| #if 0
 | |
| static void nvt_wdev_set_rx_mode(struct net_device *ndev)
 | |
| {
 | |
|         //nash:TODO
 | |
|         nvt_dbg(ERROR, "%s: not supported yet\n", __func__);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
 | |
| static u16 nvt_wdev_select_wmm_queue(struct net_device *dev,
 | |
|                                      struct sk_buff *skb,
 | |
|                                      void *accesl_priv,
 | |
|                                      select_queue_fallback_t fallback)
 | |
| #elif LINUX_VERSION_CODE > KERNEL_VERSION(3, 12, 0)
 | |
| static u16 nvt_wdev_select_wmm_queue(struct net_device *dev,
 | |
|                                      struct sk_buff *skb,
 | |
|                                      void *accel_priv)
 | |
| #else
 | |
| static u16 nvt_wdev_select_wmm_queue(struct net_device *dev,
 | |
|         struct sk_buff *skb)
 | |
| #endif
 | |
| {
 | |
|         if (nvt_txmq == 0) {
 | |
|                 return 0;
 | |
|         } else {
 | |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
 | |
|                 skb->priority = cfg80211_classify8021d(skb);
 | |
| #else
 | |
|                 skb->priority = cfg80211_classify8021d(skb, NULL);
 | |
| #endif
 | |
|                 return nvt_tx_prio2fifo[skb->priority];
 | |
|         }
 | |
| }
 | |
| 
 | |
| static const struct net_device_ops nvt_wdev_ops = {
 | |
|         .ndo_open = nvt_wdev_open,
 | |
|         .ndo_stop = nvt_wdev_stop,
 | |
|         .ndo_get_stats = nvt_wdev_get_stats,
 | |
|         .ndo_do_ioctl = nvt_wdev_ioctl,
 | |
|         .ndo_start_xmit = nvt_wdev_start_xmit,
 | |
|         .ndo_set_mac_address = nvt_wdev_set_mac_addr,
 | |
|         //20150716 nash: not support multicast list filtering
 | |
|         //.ndo_set_rx_mode = nvt_wdev_set_rx_mode
 | |
|         .ndo_select_queue = nvt_wdev_select_wmm_queue,
 | |
| };
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * nvt_deaggr_security_amsdu - de-aggregate amsdu packet
 | |
|  * @pkt     : skb buffer list
 | |
|  * @info    : nvt rx hostheader
 | |
|  * @nvt_if  : interface structure
 | |
|  * @pn      : pn_start_ptr
 | |
|  * this function will process skb
 | |
|  *      we do de-aggregate it, and send them to kernel one by one.
 | |
|  *
 | |
|  * Return: NULL
 | |
|  */
 | |
| 
 | |
| void nvt_deaggr_security_amsdu(struct sk_buff *pkt,
 | |
|         struct _nvt_hwinfo_rx *info, struct _nvt_if *nvt_if,
 | |
|         u8 *pn_start_ptr, enum _cipher_t ct)
 | |
| {
 | |
|         u8 amsdu_flags = info->word4.amsdu_flags;
 | |
|         //u8 buff_num_curr_mpdu = info->word4.buff_num_curr_mpdu;
 | |
|         struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
 | |
|         //check whether this buffer is the starting amsdu buffer
 | |
|         if ((amsdu_flags & 0x2) && (amsdu_flags & 0x1)) {
 | |
|                 if (nvt_check_sec_rx_mpdu(nvt_adapter, ct, pkt,
 | |
|                         pn_start_ptr)) {
 | |
|                         dev_kfree_skb_any(pkt);
 | |
|                 } else {
 | |
|                         nvt_deaggr_amsdu(pkt, info, nvt_if);
 | |
|                 }
 | |
|         } else {
 | |
|                 nvt_deaggr_amsdu(pkt, info, nvt_if);
 | |
|         }
 | |
| }
 | |
| /**
 | |
|  * nvt_deaggr_amsdu - de-aggregate amsdu packet
 | |
|  * @pkt: skb buffer list
 | |
|  * @info: nvt rx hostheader
 | |
|  * @nvt_if: interface structure
 | |
|  *
 | |
|  * this function will process skb
 | |
|  *      we do de-aggregate it, and send them to kernel one by one.
 | |
|  *
 | |
|  * Return: NULL
 | |
|  */
 | |
| void nvt_deaggr_amsdu(struct sk_buff *pkt,
 | |
|         struct _nvt_hwinfo_rx *info, struct _nvt_if *nvt_if)
 | |
| {
 | |
|         struct sk_buff *buf;
 | |
|         struct _nvt_hwinfo_rx info_record;
 | |
|         int num_msdu = 0;
 | |
|         int cnt = 0;
 | |
|         int buf_len = 0;
 | |
|         u16 msdu_len = 0;
 | |
|         u8 msdu_padlen = 0;
 | |
|         u8 submsdu_gap = 16;
 | |
|         u8 *da;
 | |
|         u8 *sa;
 | |
|         u8 *payload;
 | |
|         u8 rfc1042_header[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
 | |
| 
 | |
|         nvt_dbg(REORDER, "%s\n", __func__);
 | |
| 
 | |
|         /* De-aggregate AMSDU skb */
 | |
|         num_msdu = info->word5.num_amsdu;
 | |
|         nvt_dbg(REORDER, "AMSDU frame, num_msdu=%d\n", num_msdu);
 | |
|         memcpy(&info_record, info, sizeof(struct _nvt_hwinfo_rx));
 | |
|         info_record.word1.offset = 32;
 | |
| 
 | |
|         for (cnt = num_msdu; cnt > 0; cnt--) {
 | |
|                 if (cnt == num_msdu) {
 | |
|                         payload = pkt->data + info->word1.offset;
 | |
|                         skb_pull(pkt, info->word1.offset);
 | |
|                 } else {
 | |
|                         payload = pkt->data;
 | |
|                 }
 | |
|                 nvt_dbg(REORDER, "rx skb_len=%d\n", pkt->len);
 | |
|                 da = payload;
 | |
|                 sa = payload + ETH_ALEN;
 | |
|                 msdu_len = (*(payload + 12) << 8) + (*(payload + 13));
 | |
| 
 | |
|                 if ((14 + msdu_len) & 0x3) {
 | |
|                         msdu_padlen = 4 - ((14 + msdu_len) & 0x3);
 | |
|                 } else {
 | |
|                         msdu_padlen = 0;
 | |
|                 }
 | |
|                 skb_pull(pkt, ETH_HLEN + sizeof(rfc1042_header));
 | |
|                 memcpy(skb_push(pkt, ETH_ALEN), sa, ETH_ALEN);
 | |
|                 memcpy(skb_push(pkt, ETH_ALEN), da, ETH_ALEN);
 | |
|                 memcpy(skb_push(pkt, sizeof(struct _nvt_hwinfo_rx)),
 | |
|                         &info_record, sizeof(struct _nvt_hwinfo_rx));
 | |
| 
 | |
|                 if (cnt != 1) {
 | |
|                         buf_len = pkt->len - msdu_len - ETH_HLEN -
 | |
|                                 sizeof(struct _nvt_hwinfo_rx) + 8;
 | |
| 
 | |
|                         if (buf_len <= 0) {
 | |
|                                 nvt_dbg(ERROR, "Remain len error\n");
 | |
|                                 nvt_dbg(ERROR, "skb_len=%d\n", pkt->len);
 | |
|                                 nvt_dbg(ERROR, "msdu_len=%d\n", msdu_len);
 | |
|                                 break;
 | |
|                         }
 | |
| 
 | |
|                         buf = dev_alloc_skb(buf_len);
 | |
|                         skb_split(pkt, buf, msdu_len + ETH_HLEN +
 | |
|                                 sizeof(struct _nvt_hwinfo_rx) - 8);
 | |
|                         nvt_dbg(REORDER,
 | |
|                                 "part msdu[%d],skb_len=%d,msdu_len=%d\n",
 | |
|                                 cnt, pkt->len, msdu_len);
 | |
|                         nvt_netif_rx(nvt_if, pkt);
 | |
|                         /* pull out sub-msdu gap */
 | |
|                         skb_pull(buf, submsdu_gap + msdu_padlen);
 | |
|                         pkt = buf;
 | |
|                         nvt_dbg(REORDER, "remain skb_len=%d\n", pkt->len);
 | |
|                 } else {
 | |
|                         skb_trim(pkt, sizeof(struct _nvt_hwinfo_rx) +
 | |
|                                 ETH_HLEN + msdu_len - 8);
 | |
|                         nvt_dbg(REORDER,
 | |
|                                 "last msdu[%d],skb_len=%d,msdu_len=%d\n",
 | |
|                                 cnt, pkt->len, msdu_len);
 | |
|                         nvt_netif_rx(nvt_if, pkt);
 | |
|                 }
 | |
|         }
 | |
| }
 | |
| 
 | |
| static u8 nvt_frame_get_retry(u8* header)
 | |
| {
 | |
|     return (header[1] & 0x08) >> 3;
 | |
| }
 | |
| 
 | |
| /*handle the broadcast packet or MPDU datas*/
 | |
| void nvt_handle_bcmc_mpdu_skb(struct _nvt_hwinfo_rx *info,
 | |
|         struct sk_buff *skb, struct _nvt_if *nvt_if,
 | |
|         u8 ba_idx)
 | |
| {
 | |
|         u8 is_retry;
 | |
|         u8 is_BC;
 | |
|         u8 is_MC;
 | |
|         u8 is_QoS;
 | |
|         u8 tid;
 | |
|         u8 sta_index;
 | |
|         u8 *pn_start_ptr;
 | |
|         u16 seq_num;
 | |
|         u16 last_seq;
 | |
|         enum _cipher_t ct;
 | |
|         struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
 | |
| 
 | |
|         is_BC = (ntohl(info->word0) & 0x10000000) >> 28;
 | |
|         is_MC = (ntohl(info->word0) & 0x20000000) >> 29;
 | |
|         is_QoS = (ntohl(info->word0) & 0x00000004) >> 2;
 | |
|         sta_index = info->word4.sta_idx;
 | |
|         tid = (u8)(ntohl(info->word0) & 0x000000F0) >> 4;
 | |
|         ct = (enum _cipher_t)((ntohl(info->word7.value) & 0x0000f000) >> 12);
 | |
|         /* Duplicate detection */
 | |
|         if ((is_BC == 1) || (is_MC == 1)) {
 | |
|                 /* do nothing */
 | |
|         } else {
 | |
|                 seq_num = (u16)(ntohl(info->word7.value) & 0x00000fff);
 | |
|                 is_retry = nvt_frame_get_retry(skb->data + NVT_RX_HOSTHEADER);
 | |
|                 if (is_QoS == 1) {
 | |
|                         last_seq = nvt_adapter->nvt_priv.last_seq_ctl[ba_idx];
 | |
|                         if (is_retry && (last_seq == seq_num)) {
 | |
|                                 nvt_dbg(REORDER, "duplicate QoS seq[%d]\n",
 | |
|                                                                 seq_num);
 | |
|                                 dev_kfree_skb_any(skb);
 | |
|                                 return;
 | |
|                         }
 | |
|                         nvt_adapter->nvt_priv.last_seq_ctl[ba_idx] = seq_num;
 | |
|                 } else {
 | |
|                         last_seq = nvt_adapter->nvt_priv.last_seq_ctl[
 | |
|                                         ASSOC_NUM * NUM_TIDS + (sta_index - 1)];
 | |
|                         if (is_retry && (last_seq == seq_num)) {
 | |
|                                 nvt_dbg(REORDER, "duplicate non-QoS seq[%d]\n",
 | |
|                                                                 seq_num);
 | |
|                                 dev_kfree_skb_any(skb);
 | |
|                                 return;
 | |
|                         }
 | |
|                         nvt_adapter->
 | |
|                                 nvt_priv.last_seq_ctl[ASSOC_NUM * NUM_TIDS +
 | |
|                                                 (sta_index - 1)] = seq_num;
 | |
|                 }
 | |
|         }
 | |
|         if ((ct != TKIP) && (ct != CCMP)) {
 | |
|                 /* check this skb is AMSDU or not */
 | |
|                 if ((ntohl(info->word0) & 0x08000000) == 0) {
 | |
|                         nvt_netif_rx(nvt_if, skb);
 | |
|                 } else {
 | |
|                         nvt_deaggr_amsdu(skb, info, nvt_if);
 | |
|                 }
 | |
|         } else {
 | |
|                 if (((is_BC == 1) || (is_MC == 1)) &&
 | |
|                         (nvt_if->mode == NVT_FW_STA)) {
 | |
|                         //nvt_netif_rx(nvt_if, skb);
 | |
|                         /* sanity check for bcmc pn if ptk offload is enable */
 | |
|                         if (nvt_ptk_offload_enable && !nvt_adapter->nvt_priv.pn_bcmc) {
 | |
|                                 nvt_dbg(ERROR, "BCMC PN is NULL!!\n");
 | |
|                                 /* allocate 6 bytes bcmc pn */
 | |
|                                 nvt_adapter->nvt_priv.pn_bcmc = kzalloc(6, GFP_KERNEL);
 | |
|                                 if (nvt_adapter->nvt_priv.pn_bcmc == NULL) {
 | |
|                                         nvt_dbg(ERROR, "BC PN allocate fail!!\n");
 | |
|                                         dev_kfree_skb_any(skb);
 | |
|                                         return;
 | |
|                                 }
 | |
|                                 memset(nvt_adapter->nvt_priv.pn_bcmc, 0, 6);
 | |
|                         }
 | |
|                         pn_start_ptr = nvt_adapter->nvt_priv.pn_bcmc;
 | |
|                 } else {
 | |
| 
 | |
|                         pn_start_ptr = nvt_adapter->
 | |
|                                 nvt_priv.pn_unicast[ba_idx];
 | |
|                         if (pn_start_ptr == NULL) {
 | |
|                                 nvt_adapter->nvt_priv.pn_unicast[ba_idx] =
 | |
|                                     kzalloc(6, GFP_ATOMIC);
 | |
|                                  pn_start_ptr = nvt_adapter->
 | |
|                                          nvt_priv.pn_unicast[ba_idx];
 | |
|                         }
 | |
|                 }
 | |
|                 //check this skb is AMSDU or not
 | |
|                 if ((ntohl(info->word0) & 0x08000000) == 0) {
 | |
|                         if (nvt_check_sec_rx_mpdu(nvt_adapter, ct,
 | |
|                                 skb, pn_start_ptr)) {
 | |
|                                 dev_kfree_skb_any(skb);
 | |
|                         } else {
 | |
|                                 nvt_netif_rx(nvt_if, skb);
 | |
|                         }
 | |
|                 } else {
 | |
|                         nvt_deaggr_security_amsdu(skb, info, nvt_if,
 | |
|                                 pn_start_ptr, ct);
 | |
|                 }
 | |
|         }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * nvt_process_rx_list - process rx_list
 | |
|  * @ndev: net device structure
 | |
|  * @skb_list: rx skb list
 | |
|  *
 | |
|  * this function will process skb one by one through rx_skb_list.
 | |
|  *
 | |
|  * Return: NULL
 | |
|  */
 | |
| void nvt_process_rx_list(struct _nvt_bus *nvt_bus,
 | |
|         struct sk_buff_head *skb_list)
 | |
| {
 | |
|         struct _nvt_adapter *nvt_adapter = nvt_bus->nvt_adapter;
 | |
|         struct _nvt_if *nvt_if = NULL;
 | |
|         struct sk_buff *skb;
 | |
|         struct sk_buff *pnext;
 | |
|         struct _nvt_hwinfo_rx *info;
 | |
|         ulong flags;
 | |
|         u8 ifidx = 0;
 | |
|         s32 ba_index = 0;
 | |
| 
 | |
|         struct _ba_struct_t *ba_ctxt;
 | |
|         nvt_dbg(REORDER, "%s\n", __func__);
 | |
| 
 | |
|         if (skb_queue_empty(skb_list)) {
 | |
|                 //20150921 nash:
 | |
|                 //nvt_dbg(ERROR, "skb_list is empty\n");
 | |
|                 nvt_dbg(REORDER, "skb_list is empty\n");
 | |
|                 return;
 | |
|         }
 | |
| 
 | |
|         skb_queue_walk_safe(skb_list, skb, pnext) {
 | |
|                 skb_unlink(skb, skb_list);
 | |
| 
 | |
|                 if (nvt_is_priv_pkt(nvt_bus, skb)) {
 | |
|                         nvt_dbg(REORDER, "This is Iconfig/Event packet\n");
 | |
|                         if (nvt_process_priv_pkt(nvt_bus, skb)) {
 | |
|                                 nvt_dbg(ERROR, "Iconfig/Event handle error!\n");
 | |
|                                 continue;
 | |
|                         } else {
 | |
|                                 continue;
 | |
|                         }
 | |
|                 } else {
 | |
|                         info = (struct _nvt_hwinfo_rx *)skb->data;
 | |
| 
 | |
|                         /* Get nvt_if pointer */
 | |
|                         nvt_if = nvt_get_if_by_index(nvt_adapter, ifidx);
 | |
|                         nvt_is_trigger_pkt(nvt_bus, skb);
 | |
|                         if (!nvt_if || !nvt_if->ndev) {
 | |
|                                 dev_kfree_skb_any(skb);
 | |
|                                 continue;
 | |
|                         }
 | |
| 
 | |
|                         if (nvt_if->wdev.iftype == NL80211_IFTYPE_MONITOR) {
 | |
|                                 nvt_netif_rx(nvt_if, skb);
 | |
|                                 continue;
 | |
|                         }
 | |
| 
 | |
|                         /*Sanity check for TID and sta index*/
 | |
|                         ba_index = get_ba_index_by_hosthdr(info, skb);
 | |
|                         if (ba_index < 0) {
 | |
|                                 continue;
 | |
|                         }
 | |
|                         if (nvt_ba_process(nvt_if, info, skb, ba_index)
 | |
|                                 == false) {
 | |
|                                 ba_ctxt = &(nvt_adapter->
 | |
|                                         nvt_priv.ba_rx[ba_index]);
 | |
|                                 spin_lock_irqsave(&ba_ctxt->ba_lock, flags);
 | |
|                                 nvt_handle_bcmc_mpdu_skb(info, skb,
 | |
|                                                 nvt_if, ba_index);
 | |
|                                 spin_unlock_irqrestore(&ba_ctxt->ba_lock,
 | |
|                                                 flags);
 | |
|                         } else {
 | |
|                                 continue;
 | |
|                         }
 | |
| 
 | |
|                 }
 | |
|         }
 | |
| }
 | |
| 
 | |
| 
 | |
| void nvt_is_trigger_pkt(struct _nvt_bus *nvt_bus, struct sk_buff *skb)
 | |
| {
 | |
|         struct _nvt_hwinfo_rx *info;
 | |
|         u8 flag;
 | |
| 
 | |
|         nvt_dbg(REORDER, "%s\n", __func__);
 | |
| 
 | |
|         info = (struct _nvt_hwinfo_rx *)skb->data;
 | |
|         /* look at the second byte in word 0       */
 | |
|         /* BIT8 in that byte is magic-frame flag   */
 | |
|         /* BIT9 in that byte is pattern-frame flag */
 | |
|         flag = ((ntohl(info->word0)) >> 8) & 0x03;
 | |
|         if (flag == 1) {
 | |
|                 nvt_dbg(CFG80211, "%s: this is magic frame\n",
 | |
|                                 __func__);
 | |
|         } else if (flag == 2) {
 | |
|                 nvt_dbg(CFG80211, "%s: this is pattern frame\n",
 | |
|                                 __func__);
 | |
|         } else {
 | |
|                 nvt_dbg(CFG80211, "%s: neither magic nor pattern frame\n",
 | |
|                                 __func__);
 | |
|         }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * nvt_netif_rx - dequeue tx skb and send it to usb
 | |
|  * @nvt_if: interface structrue
 | |
|  * @skb: rx skb buffer
 | |
|  *
 | |
|  * this function will process skb
 | |
|  *     1. it may be forwarded to tx(bridge)
 | |
|  *     2. it may be sent to kernel(higher layer)
 | |
|  *
 | |
|  * Return: NULL
 | |
|  */
 | |
| void nvt_netif_rx(struct _nvt_if *nvt_if, struct sk_buff *skb)
 | |
| {
 | |
|         struct _nvt_hwinfo_rx *info = NULL;
 | |
|         struct sk_buff *skb2 = NULL;
 | |
|         struct nvt_rx_radiotap_hdr radiotap_hdr;
 | |
|         struct nvt_rx_radiotap_hdr *pradiotap_hdr;
 | |
| 
 | |
|         skb->dev = nvt_if->ndev;
 | |
| 
 | |
|         nvt_dbg(REORDER, "%s\n", __func__);
 | |
| 
 | |
|         info = (struct _nvt_hwinfo_rx *)skb->data;
 | |
|         if ((ntohl(info->word0) & DESTI_MASK) == TO_WIFI) {
 | |
|                 skb->priority = (u8)(ntohl(info->word0) & 0x000000F0) >> 4;
 | |
|         }
 | |
|         /* pull out Rx Host Header */
 | |
|         skb_pull(skb, info->word1.offset);
 | |
|         nvt_dbg(REORDER, "skb->protocol=0x%x, skb->len=%d\n",
 | |
|                 ntohs(skb->protocol), skb->len);
 | |
| 
 | |
|         nvt_if->net_stats.rx_bytes += skb->len;
 | |
|         nvt_if->net_stats.rx_packets++;
 | |
|         if (nvt_if->mode == NVT_FW_AP) {
 | |
|                 if ((ntohl(info->word0) & DESTI_MASK) == TO_WIFI) {
 | |
|                         nvt_dbg(REORDER, "Forward Rx packet to Tx Directly\n");
 | |
|                         skb->protocol = cpu_to_be16(ETH_P_802_3);
 | |
|                         if (in_interrupt()) {
 | |
|                                 nvt_wdev_xmit_forward(skb, skb->dev);
 | |
|                         } else {
 | |
|                                 nvt_wdev_xmit_forward(skb, skb->dev);
 | |
|                                 /* dev_queue_xmit(skb); */
 | |
|                         }
 | |
|                 } else {
 | |
|                         if (is_multicast_ether_addr(skb->data)) {
 | |
|                                 skb2 = skb_copy(skb, GFP_ATOMIC);
 | |
|                         }
 | |
|                         if (skb2 == NULL) {
 | |
|                                 if (is_multicast_ether_addr(skb->data)) {
 | |
|                                         nvt_dbg(REORDER,
 | |
|                                                 "SKB2 NULL,can't forward tx\n");
 | |
|                                 }
 | |
|                         } else {
 | |
|                                 nvt_dbg(REORDER,
 | |
|                                         "Forward Rx to Tx & to kernel\n");
 | |
|                                 skb2->dev = nvt_if->ndev;
 | |
|                                 skb2->protocol = cpu_to_be16(ETH_P_802_3);
 | |
|                                 if (in_interrupt()) {
 | |
|                                         nvt_wdev_start_xmit(skb2, skb2->dev);
 | |
|                                 } else {
 | |
|                                         nvt_wdev_start_xmit(skb2, skb2->dev);
 | |
|                                         /* dev_queue_xmit(skb2); */
 | |
|                                 }
 | |
|                         }
 | |
|                         skb->protocol = eth_type_trans(skb, skb->dev);
 | |
| 
 | |
|                         nvt_dbg(REORDER, "skb->protocol=0x%x\n",
 | |
|                                 ntohs(skb->protocol));
 | |
|                         if (in_interrupt()) {
 | |
|                                 netif_rx(skb);
 | |
|                         } else {
 | |
|                                 netif_rx_ni(skb);
 | |
|                         }
 | |
|                 }
 | |
|         } else {
 | |
|                 if (nvt_if->wdev.iftype == NL80211_IFTYPE_MONITOR) {
 | |
|                         u8 rx_radio_len = sizeof(struct nvt_rx_radiotap_hdr);
 | |
|                         memset(&radiotap_hdr, 0, sizeof(radiotap_hdr));
 | |
|                         radiotap_hdr.hdr.it_len =
 | |
|                                 cpu_to_le16(sizeof(struct nvt_rx_radiotap_hdr));
 | |
|                         radiotap_hdr.hdr.it_present =
 | |
|                                 cpu_to_le32(RX_RADIOTAP_PRESENT);
 | |
|                         radiotap_hdr.rate = 2;
 | |
|                         radiotap_hdr.antsignal = 10;
 | |
|                         if (skb_headroom(skb) < rx_radio_len) {
 | |
|                                 nvt_dbg(ERROR, "monitor, size too small\n");
 | |
|                                 dev_kfree_skb(skb);
 | |
|                                 return;
 | |
|                         }
 | |
| 
 | |
|                         pradiotap_hdr = (void *)skb_push(skb, rx_radio_len);
 | |
|                         if (pradiotap_hdr != NULL) {
 | |
|                                 memcpy(pradiotap_hdr, &radiotap_hdr,
 | |
|                                         rx_radio_len);
 | |
|                         }
 | |
|                         skb->ip_summed = CHECKSUM_NONE;
 | |
|                         skb->protocol = eth_type_trans(skb, skb->dev);
 | |
|                         netif_rx(skb);
 | |
|                         return;
 | |
|                 }
 | |
| 
 | |
|                 skb->protocol = eth_type_trans(skb, skb->dev);
 | |
| 
 | |
|                 nvt_dbg(REORDER, "skb->protocol=0x%x,\n", ntohs(skb->protocol));
 | |
| 
 | |
|                 if (in_interrupt()) {
 | |
|                         netif_rx(skb);
 | |
|                 } else {
 | |
|                         netif_rx_ni(skb);
 | |
|                 }
 | |
|         }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * nvt_get_if_by_index - get the nvt_if structure
 | |
|  * @nvt_adapter: adapter structrue
 | |
|  * @ifidx: interface index
 | |
|  *
 | |
|  * this function will get the nvt_if according to
 | |
|  *     1. nvt_adapter pointer
 | |
|  *     2. interface index which is received from FW.
 | |
|  *
 | |
|  * Return: nvt_if
 | |
|  */
 | |
| struct _nvt_if *nvt_get_if_by_index(struct _nvt_adapter *nvt_adapter,
 | |
|         u8 ifidx)
 | |
| {
 | |
|         struct _nvt_if *nvt_if, *nvt_found = NULL;
 | |
|         ulong flags;
 | |
| 
 | |
|         //if (WARN_ON(ifidx > (nvt_adapter->if_max - 1)))
 | |
|         //      return NULL;
 | |
| 
 | |
|         //20150806 nash: NULL check
 | |
|         if (nvt_adapter == NULL) {
 | |
|                 nvt_dbg(ERROR, "%s: nvt_adapter is NULL\n", __func__);
 | |
|                 return NULL;
 | |
|         }
 | |
|         /* Locking if_list */
 | |
|         spin_lock_irqsave(&nvt_adapter->list_lock, flags);
 | |
|         list_for_each_entry(nvt_if, &nvt_adapter->if_list, list) {
 | |
|                 if (nvt_if->fw_if_idx == ifidx) {
 | |
|                         nvt_found = nvt_if;
 | |
|                         break;
 | |
|                 }
 | |
|         }
 | |
|         spin_unlock_irqrestore(&nvt_adapter->list_lock, flags);
 | |
| 
 | |
|         return nvt_found;
 | |
| 
 | |
| }
 | |
| 
 | |
| struct _nvt_if *nvt_create_if(struct _nvt_adapter *nvt_adapter, u8 *name,
 | |
|         bool is_p2p)
 | |
| {
 | |
|         struct _nvt_if *nvt_if = NULL;
 | |
|         struct net_device *ndev;
 | |
| 
 | |
|         if (nvt_p2p_enable && is_p2p) {
 | |
|                 /* this is P2P_DEVICE interface */
 | |
|                 nvt_dbg(INFO, "%s: allocate p2p interface\n", __func__);
 | |
|                 nvt_if = kzalloc(sizeof(struct _nvt_if), GFP_KERNEL);
 | |
|                 if (nvt_if == NULL) {
 | |
|                         return ERR_PTR(-ENOMEM);
 | |
|                 }
 | |
|         } else {
 | |
|                 nvt_dbg(INFO, "%s: allocate netdev interface\n", __func__);
 | |
|                 /* Allocate netdev, including space for private structure */
 | |
|                 if (nvt_txmq == 0) {
 | |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
 | |
|                         ndev = alloc_netdev(sizeof(struct _nvt_if),
 | |
|                                 name, ether_setup);
 | |
| #else
 | |
|                         ndev = alloc_netdev(sizeof(struct _nvt_if),
 | |
|                                 name, NET_NAME_UNKNOWN, ether_setup);
 | |
| #endif
 | |
|                 } else {
 | |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
 | |
|                         ndev = alloc_netdev_mqs(sizeof(struct _nvt_if), name,
 | |
|                                 ether_setup, IEEE80211_NUM_ACS, 1);
 | |
| #else
 | |
|                         ndev = alloc_netdev_mqs(sizeof(struct _nvt_if), name,
 | |
|                                     NET_NAME_UNKNOWN, ether_setup,
 | |
|                                     IEEE80211_NUM_ACS, 1);
 | |
| #endif
 | |
|                 }
 | |
|                 if (!ndev) {
 | |
|                         nvt_dbg(ERROR, "%s: alloc_netdev fail\n", __func__);
 | |
|                         return ERR_PTR(-ENOMEM);
 | |
|                 }
 | |
|                 nvt_if = netdev_priv(ndev);
 | |
|                 nvt_if->ndev = ndev;
 | |
|         }
 | |
| 
 | |
|         nvt_if->nvt_adapter = nvt_adapter;
 | |
|         nvt_if->fw_if_idx = nvt_if_idx_counter;
 | |
|         nvt_if->rts_val = RTS_INIT_VAL;
 | |
|         nvt_if->frag_val = FRAG_INIT_VAL;
 | |
|         nvt_if->retry_short_val = SHORT_RETRY_INIT_VAL;
 | |
|         nvt_if->retry_long_val = LONG_RETRY_INIT_VAL;
 | |
|         nvt_if_idx_counter++;
 | |
|         nvt_if->is_p2p = is_p2p;
 | |
|         nvt_if->hdrlen = NVT_TX_HOSTHEADER;
 | |
| 
 | |
|         INIT_LIST_HEAD(&nvt_if->list);
 | |
|         memset(&nvt_if->nvt_wconf, 0x00, sizeof(struct _nvt_wlan_conf));
 | |
|         memset(&nvt_if->nvt_wconf_ap, 0x00, sizeof(struct _nvt_wlan_conf_ap));
 | |
|         nvt_if->nvt_wconf_ap.acs_channel = 0xFF;
 | |
|         nvt_if->nvt_wconf_ap.acs_weight[0] = 0xFF;
 | |
|         init_waitqueue_head(&nvt_if->addkey_wait);
 | |
|         init_waitqueue_head(&nvt_if->disconnect_wait);
 | |
|         mutex_init(&nvt_if->disconnect_lock);
 | |
|         atomic_set(&nvt_if->eapol_cnt, 0);
 | |
|         atomic_set(&nvt_if->disconnect_cnt, 0);
 | |
| 
 | |
|         list_add_tail(&nvt_if->list, &nvt_adapter->if_list);
 | |
| 
 | |
|         nvt_dbg(INFO, "%s: add if:%d\n", __func__, nvt_if->fw_if_idx);
 | |
| 
 | |
|         return nvt_if;
 | |
| }
 | |
| 
 | |
| struct _nvt_if *nvt_vif_first(struct _nvt_adapter *nvt_adapter)
 | |
| {
 | |
|         struct _nvt_if *vif;
 | |
|         spin_lock_bh(&nvt_adapter->list_lock);
 | |
|         if (list_empty(&nvt_adapter->if_list)) {
 | |
|                 spin_unlock_bh(&nvt_adapter->list_lock);
 | |
|                 return NULL;
 | |
|         }
 | |
| 
 | |
|         vif = list_first_entry(&nvt_adapter->if_list, struct _nvt_if, list);
 | |
| 
 | |
|         spin_unlock_bh(&nvt_adapter->list_lock);
 | |
| 
 | |
|         return vif;
 | |
| 
 | |
| }
 | |
| 
 | |
| void nvt_del_if(struct _nvt_if *nvt_if)
 | |
| {
 | |
|         if (nvt_if == NULL) {
 | |
|                 return;
 | |
|         }
 | |
| 
 | |
|         nvt_dbg(INFO, "%s:delete if_idx=%d\n", __func__, nvt_if->fw_if_idx);
 | |
|         if (nvt_if->ndev) {
 | |
|                 if (nvt_if->is_p2p) {
 | |
|                         if (nvt_txmq == 0) {
 | |
|                                 netif_stop_queue(nvt_if->ndev);
 | |
|                         } else {
 | |
|                                 netif_tx_stop_all_queues(nvt_if->ndev);
 | |
|                         }
 | |
|                 } else {
 | |
|                         rtnl_lock();
 | |
|                         //if usb was disconnected, we should not send urb
 | |
|                         //through usb sub-system
 | |
|                         //nash:TODO: 1. do link down
 | |
|                         //nash:TODO: 2. abort scan
 | |
|                         //nvt_abort_scanning(
 | |
|                         //nvt_if->nvt_adapter->nvt_cfg80211);
 | |
| 
 | |
|                         if (nvt_txmq == 0) {
 | |
|                                 netif_stop_queue(nvt_if->ndev);
 | |
|                         } else {
 | |
|                                 netif_tx_stop_all_queues(nvt_if->ndev);
 | |
|                         }
 | |
|                         rtnl_unlock();
 | |
|                 }
 | |
| 
 | |
|                 //cancel_work_sync(&nvt_if->setfwdownload_work);
 | |
|                 if (nvt_if->ndev->netdev_ops != NULL) {
 | |
|                         unregister_netdev(nvt_if->ndev);
 | |
|                         nvt_cfg80211_deinit(nvt_if->nvt_adapter);
 | |
|                         nvt_priv_deinit(nvt_if->nvt_adapter);
 | |
|                         free_netdev(nvt_if->ndev);
 | |
|                 } else {
 | |
|                         nvt_cfg80211_deinit(nvt_if->nvt_adapter);
 | |
|                         nvt_priv_deinit(nvt_if->nvt_adapter);
 | |
|                 }
 | |
| 
 | |
| 
 | |
|         } else {
 | |
|                 kfree(nvt_if);
 | |
|         }
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * nvt_del_all_if - delete all interface
 | |
|  * @nvt_adapter: struct _nvt_adapter
 | |
|  *
 | |
|  *
 | |
|  *
 | |
|  * Return: 0:success, a negative value:fail
 | |
|  */
 | |
| s32 nvt_del_all_if(struct _nvt_adapter *nvt_adapter)
 | |
| {
 | |
|         struct _nvt_if *nvt_if;
 | |
|         struct _nvt_if *tmp;
 | |
|         list_for_each_entry_safe(nvt_if, tmp, &nvt_adapter->if_list, list) {
 | |
|                 list_del(&nvt_if->list);
 | |
|                 nvt_del_if(nvt_if);
 | |
|         }
 | |
|         //20150709 nash:
 | |
|         nvt_if_idx_counter = 0;
 | |
| 
 | |
|         return 0;
 | |
| }
 | |
| 
 | |
| static void nvt_work_set_fw_download(struct work_struct *work)
 | |
| {
 | |
|         s32 ret;
 | |
|         struct _nvt_if *nvt_if;
 | |
|         nvt_if = container_of(work, struct _nvt_if, setfwdownload_work);
 | |
|         //20151223 nash: coverity#48928
 | |
|         ret = nvt_fw_change_opmode(nvt_if, nvt_if->fw_download_type);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s change opmode fail\n", __func__);
 | |
|         }
 | |
| }
 | |
| 
 | |
| static s32 nvt_get_macaddr_from_fw(struct _nvt_adapter *nvt_adapter)
 | |
| {
 | |
|         s32 ret;
 | |
|         s32 pkt_len = 0;
 | |
|         struct _nvt_bus *nvt_bus = nvt_adapter->nvt_bus;
 | |
|         struct _nvt_diag_req  *diag_req = NULL;
 | |
|         struct _nvt_diag_resp *diag_resp = NULL;
 | |
| 
 | |
|         diag_req = kzalloc(sizeof(struct _nvt_diag_req), GFP_KERNEL);
 | |
|         if (diag_req == NULL) {
 | |
|                 nvt_dbg(ERROR, "%s: kzalloc for req is failed\n", __func__);
 | |
|                 ret = -1;
 | |
|                 goto fail;
 | |
|         }
 | |
| 
 | |
|         diag_resp = kzalloc(sizeof(struct _nvt_diag_resp), GFP_KERNEL);
 | |
|         if (diag_resp == NULL) {
 | |
|                 nvt_dbg(ERROR, "%s: kzalloc for resp is failed\n", __func__);
 | |
|                 ret = -1;
 | |
|                 goto fail;
 | |
|         }
 | |
| 
 | |
|         nvt_diag_pack_req(NVT_DIAG_CMD_CLASS_0, NVT_DIAG_GET_MAC_ADDR_CMD, 0, 0,
 | |
|                 NULL, diag_req, &pkt_len);
 | |
| 
 | |
|         ret = nvt_bus->nvt_wdev_bus_ops.tx_ctrl(nvt_bus,
 | |
|                 (u8 *)diag_req, pkt_len);
 | |
|         if (ret < 0) {
 | |
|                 goto fail;
 | |
|         }
 | |
| 
 | |
|         ret = nvt_bus->nvt_wdev_bus_ops.rx_ctrl(nvt_bus, (u8 *)diag_resp,
 | |
|                 sizeof(struct _nvt_diag_resp));
 | |
|         if (ret < 0) {
 | |
|                 goto fail;
 | |
|         }
 | |
| 
 | |
|         memcpy(nvt_adapter->mac_addr, diag_resp->sel.get_mac_addr.mac_addr,
 | |
|                 sizeof(nvt_adapter->mac_addr));
 | |
| 
 | |
|  fail:
 | |
| 
 | |
|         kfree(diag_req);
 | |
|         kfree(diag_resp);
 | |
| 
 | |
|         return ret;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * nvt_set_macaddress_to_fw - set MAC address to firmware
 | |
|  * @nvt_adapter: _nvt_adapter structure
 | |
|  *
 | |
|  *
 | |
|  * Return: 0:success, -1:fail
 | |
|  */
 | |
| s32 nvt_set_macaddress_to_fw(struct _nvt_adapter *nvt_adapter)
 | |
| {
 | |
|         s32 ret = 0;
 | |
|         struct _nvt_bus *nvt_bus = nvt_adapter->nvt_bus;
 | |
| 
 | |
|         ret = nvt_icfg_lock(nvt_bus);
 | |
|         if (ret < 0) {
 | |
|                 return -EPERM;
 | |
|         }
 | |
| 
 | |
|         ret = nvt_icfg_reset(nvt_bus);
 | |
|         if (ret < 0) {
 | |
|                 return -EPERM;
 | |
|         }
 | |
| 
 | |
|         ret = nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_MAC_ADDR,
 | |
|                 nvt_adapter->mac_addr, ETH_ALEN);
 | |
|         if (ret < 0) {
 | |
|                 return -EPERM;
 | |
|         }
 | |
| 
 | |
|         ret = nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0);
 | |
|         if (ret < 0) {
 | |
|                 return -EPERM;
 | |
|         }
 | |
| 
 | |
|         nvt_icfg_unlock(nvt_bus);
 | |
| 
 | |
|         return 0;
 | |
| }
 | |
| 
 | |
| static s32 nvt_netdev_register(struct _nvt_if *nvt_if)
 | |
| {
 | |
|         s32 ret;
 | |
|         struct net_device *ndev;
 | |
|         struct _nvt_cfg80211 *nvt_cfg80211 = nvt_if->nvt_adapter->nvt_cfg80211;
 | |
| 
 | |
|         /* setup wireless_dev */
 | |
|         nvt_if->wdev.wiphy = nvt_cfg80211->wiphy;
 | |
|         nvt_if->wdev.iftype = NL80211_IFTYPE_STATION;
 | |
| 
 | |
|         ndev = nvt_if->ndev;
 | |
|         ndev->netdev_ops = &nvt_wdev_ops;
 | |
| 
 | |
|         //20150710 nash:
 | |
|         ndev->hard_header_len += nvt_if->hdrlen;
 | |
| 
 | |
|         /* assign ieee80211_ptr before registering */
 | |
|         ndev->ieee80211_ptr = &nvt_if->wdev;
 | |
| 
 | |
|         SET_NETDEV_DEV(ndev, wiphy_dev(nvt_cfg80211->wiphy));
 | |
| 
 | |
|         ret = nvt_get_macaddr_from_fw(nvt_if->nvt_adapter);
 | |
|         if (ret < 0) {
 | |
|                 nvt_dbg(ERROR, "%s: mac addr retrieve fail\n", __func__);
 | |
|                 goto fail;
 | |
|         }
 | |
|         memcpy(ndev->dev_addr, nvt_if->nvt_adapter->mac_addr, ETH_ALEN);
 | |
| 
 | |
|         ret = nvt_iw_register_handler(ndev);
 | |
|         if (ret < 0) {
 | |
|                 nvt_dbg(ERROR, "%s: iw_handler registration fail!\n", __func__);
 | |
|                 goto fail;
 | |
|         }
 | |
| 
 | |
|         INIT_WORK(&nvt_if->setfwdownload_work, nvt_work_set_fw_download);
 | |
| 
 | |
|         ret = register_netdev(ndev);
 | |
|         if (ret != 0) {
 | |
|                 nvt_dbg(ERROR, "%s: couldn't register the net device\n",
 | |
|                         __func__);
 | |
|                 goto fail;
 | |
|         } else {
 | |
|                 nvt_dbg(INFO, "%s:register net device sucessfully!!\n",
 | |
|                         __func__);
 | |
|         }
 | |
| 
 | |
|         nvt_dbg(INFO, "%s: netdev name=%s\n", __func__, ndev->name);
 | |
| 
 | |
|         return 0;
 | |
| 
 | |
| fail:
 | |
|         ndev->netdev_ops = NULL;
 | |
|         return -EBADE;
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * nvt_register_to_system - do related initialization and setup
 | |
|  * @dev: struct device
 | |
|  *
 | |
|  *
 | |
|  *
 | |
|  * Return:
 | |
|  */
 | |
| s32 nvt_register_to_system(struct device *dev)
 | |
| {
 | |
|         s32 ret;
 | |
|         struct _nvt_adapter *nvt_adapter;
 | |
|         struct _nvt_bus *bus;
 | |
|         struct _nvt_if *nvt_if = NULL;
 | |
|         struct _nvt_if *p2p_if = NULL;
 | |
| 
 | |
|         nvt_dbg(INFO, "%s:\n", __func__);
 | |
| 
 | |
|         nvt_adapter = kzalloc(sizeof(struct _nvt_adapter), GFP_ATOMIC);
 | |
|         if (!nvt_adapter) {
 | |
|                 return -ENOMEM;
 | |
|         }
 | |
|         bus = (struct _nvt_bus *)dev_get_drvdata(dev);
 | |
|         if (!bus) {
 | |
|                 //20160104 nash: coverity#49515
 | |
|                 kfree(nvt_adapter);
 | |
|                 return -EPERM;
 | |
|         }
 | |
|         bus->nvt_adapter = nvt_adapter;
 | |
|         nvt_adapter->nvt_bus = bus;
 | |
|         INIT_LIST_HEAD(&nvt_adapter->if_list);
 | |
|         spin_lock_init(&nvt_adapter->list_lock);
 | |
|         nvt_if = nvt_create_if(nvt_adapter, "wlan%d", false);
 | |
|         if (IS_ERR(nvt_if)) {
 | |
|                 return PTR_ERR(nvt_if);
 | |
|         }
 | |
|         //20150709 nash: record mode
 | |
|         nvt_if->mode = NVT_FW_DEFAULT_FW_MODE;
 | |
| 
 | |
|         //20151110 nash:
 | |
|         nvt_if->sleep_mode = NVT_SLEEP_MODE_DISABLE;
 | |
| 
 | |
|         nvt_dbg(INFO, "%s: create if:%d ok\n", __func__, nvt_if->fw_if_idx);
 | |
| 
 | |
|         if (nvt_p2p_enable) {
 | |
|                 p2p_if = nvt_create_if(nvt_adapter, "p2p%d", true);
 | |
|         }
 | |
| 
 | |
|         //20160601 nash: coverity #61618
 | |
|         #if 0
 | |
|         if (IS_ERR(p2p_if)) {
 | |
|                 p2p_if = NULL;
 | |
|         }
 | |
|         #endif
 | |
| 
 | |
|         nvt_adapter->nvt_bus->state = NVT_BUS_STATE_UP;
 | |
| 
 | |
|         ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
 | |
|         if (ret < 0) {
 | |
|                 nvt_dbg(ERROR, "%s: icfg reset fail\n", __func__);
 | |
|                 goto fail;
 | |
|         }
 | |
| 
 | |
|         ret = nvt_cfg80211_init(nvt_adapter);
 | |
|         if (ret < 0) {
 | |
|                 nvt_dbg(ERROR, "%s: cfg80211 register fail\n", __func__);
 | |
|                 goto fail;
 | |
|         }
 | |
| 
 | |
|         ret = nvt_priv_init(nvt_adapter);
 | |
|         if (ret < 0) {
 | |
|                 nvt_dbg(ERROR, "%s: nvt_priv_init fail\n", __func__);
 | |
|                 goto fail;
 | |
|         }
 | |
| 
 | |
|         ret = nvt_netdev_register(nvt_if);
 | |
|         if (ret < 0) {
 | |
|                 nvt_dbg(ERROR, "%s: net attach fail!!\n", __func__);
 | |
|                 goto fail;
 | |
|         }
 | |
| fail:
 | |
|         //nash:TODO:check
 | |
|         if (ret < 0) {
 | |
|                 nvt_del_all_if(nvt_adapter);
 | |
|                 #if 0
 | |
|                 if (nvt_if) {
 | |
|                         nvt_del_if(nvt_if);
 | |
|                         list_del(&nvt_if->list);
 | |
|                 }
 | |
|                 if (p2p_if) {
 | |
|                         nvt_del_if(p2p_if);
 | |
|                         list_del(&p2p_if->list);
 | |
|                 }
 | |
|                 if (nvt_adapter->nvt_cfg80211) {
 | |
|                         nvt_cfg80211_deinit(nvt_if->nvt_adapter);
 | |
|                         nvt_priv_deinit(nvt_if->nvt_adapter);
 | |
|                 }
 | |
|                 #endif
 | |
|                 return ret;
 | |
|         }
 | |
|         //nash:TODO
 | |
|         #if 0
 | |
|         if (p2p_if) {
 | |
|                 if (nvt_net_p2p_attach(p2p_ifp) < 0)
 | |
|                         nvt_p2p_enable = 0;
 | |
|         }
 | |
|         #endif
 | |
|         return ret;
 | |
| 
 | |
| }
 | |
| 
 | |
| static s32 __init nvt_module_init(void)
 | |
| {
 | |
|         nvt_dbg(INFO, "%s\n", __func__);
 | |
|         //nvtfmac_usb_init();
 | |
| 
 | |
|         nvtfmac_sdio_init();
 | |
|         return 0;
 | |
| }
 | |
| 
 | |
| static void __exit nvt_module_exit(void)
 | |
| {
 | |
|         nvt_dbg(INFO, "%s\n", __func__);
 | |
|         //nvtfmac_usb_exit();
 | |
|         nvtfmac_sdio_exit();
 | |
| }
 | |
| 
 | |
| u32 nvt_dbg_level = (NVT_DBG_ERROR | NVT_DBG_WARN | NVT_DBG_INFO);
 | |
| module_param_named(debug, nvt_dbg_level, uint, S_IRUSR | S_IWUSR);
 | |
| MODULE_PARM_DESC(debug, "debug level");
 | |
| u32 nvt_data_mode = stream_mode;
 | |
| module_param_named(streaming, nvt_data_mode, int, S_IRUSR | S_IWUSR);
 | |
| MODULE_LICENSE("Dual BSD/GPL");
 | |
| MODULE_VERSION(NVT_VERSION);
 | |
| 
 | |
| 
 | |
| module_init(nvt_module_init);
 | |
| module_exit(nvt_module_exit);
 | 
