GNU Linux-libre 4.14.290-gnu1
[releases.git] / drivers / staging / rtl8723bs / os_dep / xmit_linux.c
1 /******************************************************************************
2  *
3  * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of version 2 of the GNU General Public License as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  ******************************************************************************/
15 #define _XMIT_OSDEP_C_
16
17 #include <drv_types.h>
18 #include <rtw_debug.h>
19
20
21 uint rtw_remainder_len(struct pkt_file *pfile)
22 {
23         return (pfile->buf_len - ((SIZE_PTR)(pfile->cur_addr) - (SIZE_PTR)(pfile->buf_start)));
24 }
25
26 void _rtw_open_pktfile (_pkt *pktptr, struct pkt_file *pfile)
27 {
28         pfile->pkt = pktptr;
29         pfile->cur_addr = pfile->buf_start = pktptr->data;
30         pfile->pkt_len = pfile->buf_len = pktptr->len;
31
32         pfile->cur_buffer = pfile->buf_start ;
33 }
34
35 uint _rtw_pktfile_read (struct pkt_file *pfile, u8 *rmem, uint rlen)
36 {
37         uint    len = 0;
38
39         len =  rtw_remainder_len(pfile);
40         len = (rlen > len) ? len : rlen;
41
42         if (rmem)
43                 skb_copy_bits(pfile->pkt, pfile->buf_len-pfile->pkt_len, rmem, len);
44
45         pfile->cur_addr += len;
46         pfile->pkt_len -= len;
47         return len;
48 }
49
50 sint rtw_endofpktfile(struct pkt_file *pfile)
51 {
52         if (pfile->pkt_len == 0)
53                 return true;
54         return false;
55 }
56
57 void rtw_set_tx_chksum_offload(_pkt *pkt, struct pkt_attrib *pattrib)
58 {
59
60 }
61
62 int rtw_os_xmit_resource_alloc(struct adapter *padapter, struct xmit_buf *pxmitbuf, u32 alloc_sz, u8 flag)
63 {
64         if (alloc_sz > 0) {
65                 pxmitbuf->pallocated_buf = rtw_zmalloc(alloc_sz);
66                 if (pxmitbuf->pallocated_buf == NULL)
67                 {
68                         return _FAIL;
69                 }
70
71                 pxmitbuf->pbuf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(pxmitbuf->pallocated_buf), XMITBUF_ALIGN_SZ);
72         }
73
74         return _SUCCESS;
75 }
76
77 void rtw_os_xmit_resource_free(struct adapter *padapter, struct xmit_buf *pxmitbuf, u32 free_sz, u8 flag)
78 {
79         if (free_sz > 0)
80                 kfree(pxmitbuf->pallocated_buf);
81 }
82
83 #define WMM_XMIT_THRESHOLD      (NR_XMITFRAME*2/5)
84
85 void rtw_os_pkt_complete(struct adapter *padapter, _pkt *pkt)
86 {
87         u16 queue;
88         struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
89
90         queue = skb_get_queue_mapping(pkt);
91         if (padapter->registrypriv.wifi_spec) {
92                 if (__netif_subqueue_stopped(padapter->pnetdev, queue) &&
93                         (pxmitpriv->hwxmits[queue].accnt < WMM_XMIT_THRESHOLD))
94                 {
95                         netif_wake_subqueue(padapter->pnetdev, queue);
96                 }
97         } else {
98                 if (__netif_subqueue_stopped(padapter->pnetdev, queue))
99                         netif_wake_subqueue(padapter->pnetdev, queue);
100         }
101
102         dev_kfree_skb_any(pkt);
103 }
104
105 void rtw_os_xmit_complete(struct adapter *padapter, struct xmit_frame *pxframe)
106 {
107         if (pxframe->pkt)
108                 rtw_os_pkt_complete(padapter, pxframe->pkt);
109
110         pxframe->pkt = NULL;
111 }
112
113 void rtw_os_xmit_schedule(struct adapter *padapter)
114 {
115         struct adapter *pri_adapter = padapter;
116
117         if (!padapter)
118                 return;
119
120         if (!list_empty(&padapter->xmitpriv.pending_xmitbuf_queue.queue))
121                 up(&pri_adapter->xmitpriv.xmit_sema);
122 }
123
124 static void rtw_check_xmit_resource(struct adapter *padapter, _pkt *pkt)
125 {
126         struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
127         u16 queue;
128
129         queue = skb_get_queue_mapping(pkt);
130         if (padapter->registrypriv.wifi_spec) {
131                 /* No free space for Tx, tx_worker is too slow */
132                 if (pxmitpriv->hwxmits[queue].accnt > WMM_XMIT_THRESHOLD) {
133                         /* DBG_871X("%s(): stop netif_subqueue[%d]\n", __func__, queue); */
134                         netif_stop_subqueue(padapter->pnetdev, queue);
135                 }
136         } else {
137                 if (pxmitpriv->free_xmitframe_cnt <= 4) {
138                         if (!netif_tx_queue_stopped(netdev_get_tx_queue(padapter->pnetdev, queue)))
139                                 netif_stop_subqueue(padapter->pnetdev, queue);
140                 }
141         }
142 }
143
144 static int rtw_mlcst2unicst(struct adapter *padapter, struct sk_buff *skb)
145 {
146         struct  sta_priv *pstapriv = &padapter->stapriv;
147         struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
148         struct list_head        *phead, *plist;
149         struct sk_buff *newskb;
150         struct sta_info *psta = NULL;
151         u8 chk_alive_num = 0;
152         char chk_alive_list[NUM_STA];
153         u8 bc_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
154         u8 null_addr[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
155
156         int i;
157         s32     res;
158
159         DBG_COUNTER(padapter->tx_logs.os_tx_m2u);
160
161         spin_lock_bh(&pstapriv->asoc_list_lock);
162         phead = &pstapriv->asoc_list;
163         plist = get_next(phead);
164
165         /* free sta asoc_queue */
166         while (phead != plist) {
167                 int stainfo_offset;
168                 psta = LIST_CONTAINOR(plist, struct sta_info, asoc_list);
169                 plist = get_next(plist);
170
171                 stainfo_offset = rtw_stainfo_offset(pstapriv, psta);
172                 if (stainfo_offset_valid(stainfo_offset)) {
173                         chk_alive_list[chk_alive_num++] = stainfo_offset;
174                 }
175         }
176         spin_unlock_bh(&pstapriv->asoc_list_lock);
177
178         for (i = 0; i < chk_alive_num; i++) {
179                 psta = rtw_get_stainfo_by_offset(pstapriv, chk_alive_list[i]);
180                 if (!(psta->state & _FW_LINKED))
181                 {
182                         DBG_COUNTER(padapter->tx_logs.os_tx_m2u_ignore_fw_linked);
183                         continue;
184                 }
185
186                 /* avoid come from STA1 and send back STA1 */
187                 if (!memcmp(psta->hwaddr, &skb->data[6], 6)
188                         || !memcmp(psta->hwaddr, null_addr, 6)
189                         || !memcmp(psta->hwaddr, bc_addr, 6)
190                 )
191                 {
192                         DBG_COUNTER(padapter->tx_logs.os_tx_m2u_ignore_self);
193                         continue;
194                 }
195
196                 DBG_COUNTER(padapter->tx_logs.os_tx_m2u_entry);
197
198                 newskb = rtw_skb_copy(skb);
199
200                 if (newskb) {
201                         memcpy(newskb->data, psta->hwaddr, 6);
202                         res = rtw_xmit(padapter, &newskb);
203                         if (res < 0) {
204                                 DBG_COUNTER(padapter->tx_logs.os_tx_m2u_entry_err_xmit);
205                                 DBG_871X("%s()-%d: rtw_xmit() return error!\n", __func__, __LINE__);
206                                 pxmitpriv->tx_drop++;
207                                 dev_kfree_skb_any(newskb);
208                         }
209                 } else {
210                         DBG_COUNTER(padapter->tx_logs.os_tx_m2u_entry_err_skb);
211                         DBG_871X("%s-%d: rtw_skb_copy() failed!\n", __func__, __LINE__);
212                         pxmitpriv->tx_drop++;
213                         /* dev_kfree_skb_any(skb); */
214                         return false;   /*  Caller shall tx this multicast frame via normal way. */
215                 }
216         }
217
218         dev_kfree_skb_any(skb);
219         return true;
220 }
221
222 int _rtw_xmit_entry(_pkt *pkt, _nic_hdl pnetdev)
223 {
224         struct adapter *padapter = (struct adapter *)rtw_netdev_priv(pnetdev);
225         struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
226         struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
227         s32 res = 0;
228
229         DBG_COUNTER(padapter->tx_logs.os_tx);
230         RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("+xmit_enry\n"));
231
232         if (rtw_if_up(padapter) == false) {
233                 DBG_COUNTER(padapter->tx_logs.os_tx_err_up);
234                 RT_TRACE(_module_xmit_osdep_c_, _drv_err_, ("rtw_xmit_entry: rtw_if_up fail\n"));
235                 #ifdef DBG_TX_DROP_FRAME
236                 DBG_871X("DBG_TX_DROP_FRAME %s if_up fail\n", __func__);
237                 #endif
238                 goto drop_packet;
239         }
240
241         rtw_check_xmit_resource(padapter, pkt);
242
243         if (!rtw_mc2u_disable
244                 && check_fwstate(pmlmepriv, WIFI_AP_STATE) == true
245                 && (IP_MCAST_MAC(pkt->data)
246                         || ICMPV6_MCAST_MAC(pkt->data)
247                         #ifdef CONFIG_TX_BCAST2UNI
248                         || is_broadcast_mac_addr(pkt->data)
249                         #endif
250                         )
251                 && (padapter->registrypriv.wifi_spec == 0)
252                 )
253         {
254                 if (pxmitpriv->free_xmitframe_cnt > (NR_XMITFRAME/4)) {
255                         res = rtw_mlcst2unicst(padapter, pkt);
256                         if (res == true) {
257                                 goto exit;
258                         }
259                 } else {
260                         /* DBG_871X("Stop M2U(%d, %d)! ", pxmitpriv->free_xmitframe_cnt, pxmitpriv->free_xmitbuf_cnt); */
261                         /* DBG_871X("!m2u); */
262                         DBG_COUNTER(padapter->tx_logs.os_tx_m2u_stop);
263                 }
264         }
265
266         res = rtw_xmit(padapter, &pkt);
267         if (res < 0) {
268                 #ifdef DBG_TX_DROP_FRAME
269                 DBG_871X("DBG_TX_DROP_FRAME %s rtw_xmit fail\n", __func__);
270                 #endif
271                 goto drop_packet;
272         }
273
274         RT_TRACE(_module_xmit_osdep_c_, _drv_info_, ("rtw_xmit_entry: tx_pkts =%d\n", (u32)pxmitpriv->tx_pkts));
275         goto exit;
276
277 drop_packet:
278         pxmitpriv->tx_drop++;
279         dev_kfree_skb_any(pkt);
280         RT_TRACE(_module_xmit_osdep_c_, _drv_notice_, ("rtw_xmit_entry: drop, tx_drop =%d\n", (u32)pxmitpriv->tx_drop));
281
282 exit:
283         return 0;
284 }
285
286 int rtw_xmit_entry(_pkt *pkt, _nic_hdl pnetdev)
287 {
288         int ret = 0;
289
290         if (pkt) {
291                 rtw_mstat_update(MSTAT_TYPE_SKB, MSTAT_ALLOC_SUCCESS, pkt->truesize);
292                 ret = _rtw_xmit_entry(pkt, pnetdev);
293         }
294
295         return ret;
296 }