1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /****************************************************************************** |
3 | * |
4 | * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved. |
5 | * |
6 | ******************************************************************************/ |
7 | #include <drv_types.h> |
8 | #include <rtw_debug.h> |
9 | |
10 | |
11 | uint rtw_remainder_len(struct pkt_file *pfile) |
12 | { |
13 | return (pfile->buf_len - ((SIZE_PTR)(pfile->cur_addr) - (SIZE_PTR)(pfile->buf_start))); |
14 | } |
15 | |
16 | void _rtw_open_pktfile(struct sk_buff *pktptr, struct pkt_file *pfile) |
17 | { |
18 | pfile->pkt = pktptr; |
19 | pfile->cur_addr = pfile->buf_start = pktptr->data; |
20 | pfile->pkt_len = pfile->buf_len = pktptr->len; |
21 | |
22 | pfile->cur_buffer = pfile->buf_start; |
23 | } |
24 | |
25 | uint _rtw_pktfile_read(struct pkt_file *pfile, u8 *rmem, uint rlen) |
26 | { |
27 | uint len = 0; |
28 | |
29 | len = rtw_remainder_len(pfile); |
30 | len = (rlen > len) ? len : rlen; |
31 | |
32 | if (rmem) |
33 | skb_copy_bits(skb: pfile->pkt, offset: pfile->buf_len - pfile->pkt_len, to: rmem, len); |
34 | |
35 | pfile->cur_addr += len; |
36 | pfile->pkt_len -= len; |
37 | return len; |
38 | } |
39 | |
40 | signed int rtw_endofpktfile(struct pkt_file *pfile) |
41 | { |
42 | if (pfile->pkt_len == 0) |
43 | return true; |
44 | return false; |
45 | } |
46 | |
47 | int rtw_os_xmit_resource_alloc(struct adapter *padapter, struct xmit_buf *pxmitbuf, u32 alloc_sz, u8 flag) |
48 | { |
49 | if (alloc_sz > 0) { |
50 | pxmitbuf->pallocated_buf = rtw_zmalloc(alloc_sz); |
51 | if (!pxmitbuf->pallocated_buf) |
52 | return _FAIL; |
53 | |
54 | pxmitbuf->pbuf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(pxmitbuf->pallocated_buf), XMITBUF_ALIGN_SZ); |
55 | } |
56 | |
57 | return _SUCCESS; |
58 | } |
59 | |
60 | void rtw_os_xmit_resource_free(struct adapter *padapter, struct xmit_buf *pxmitbuf, u32 free_sz, u8 flag) |
61 | { |
62 | if (free_sz > 0) |
63 | kfree(objp: pxmitbuf->pallocated_buf); |
64 | } |
65 | |
66 | #define WMM_XMIT_THRESHOLD (NR_XMITFRAME * 2 / 5) |
67 | |
68 | void rtw_os_pkt_complete(struct adapter *padapter, struct sk_buff *pkt) |
69 | { |
70 | u16 queue; |
71 | struct xmit_priv *pxmitpriv = &padapter->xmitpriv; |
72 | |
73 | queue = skb_get_queue_mapping(skb: pkt); |
74 | if (padapter->registrypriv.wifi_spec) { |
75 | if (__netif_subqueue_stopped(dev: padapter->pnetdev, queue_index: queue) && |
76 | (pxmitpriv->hwxmits[queue].accnt < WMM_XMIT_THRESHOLD)) |
77 | netif_wake_subqueue(dev: padapter->pnetdev, queue_index: queue); |
78 | } else { |
79 | if (__netif_subqueue_stopped(dev: padapter->pnetdev, queue_index: queue)) |
80 | netif_wake_subqueue(dev: padapter->pnetdev, queue_index: queue); |
81 | } |
82 | |
83 | dev_kfree_skb_any(skb: pkt); |
84 | } |
85 | |
86 | void rtw_os_xmit_complete(struct adapter *padapter, struct xmit_frame *pxframe) |
87 | { |
88 | if (pxframe->pkt) |
89 | rtw_os_pkt_complete(padapter, pkt: pxframe->pkt); |
90 | |
91 | pxframe->pkt = NULL; |
92 | } |
93 | |
94 | void rtw_os_xmit_schedule(struct adapter *padapter) |
95 | { |
96 | struct adapter *pri_adapter = padapter; |
97 | |
98 | if (!padapter) |
99 | return; |
100 | |
101 | if (!list_empty(head: &padapter->xmitpriv.pending_xmitbuf_queue.queue)) |
102 | complete(&pri_adapter->xmitpriv.xmit_comp); |
103 | } |
104 | |
105 | static void rtw_check_xmit_resource(struct adapter *padapter, struct sk_buff *pkt) |
106 | { |
107 | struct xmit_priv *pxmitpriv = &padapter->xmitpriv; |
108 | u16 queue; |
109 | |
110 | queue = skb_get_queue_mapping(skb: pkt); |
111 | if (padapter->registrypriv.wifi_spec) { |
112 | /* No free space for Tx, tx_worker is too slow */ |
113 | if (pxmitpriv->hwxmits[queue].accnt > WMM_XMIT_THRESHOLD) |
114 | netif_stop_subqueue(dev: padapter->pnetdev, queue_index: queue); |
115 | } else { |
116 | if (pxmitpriv->free_xmitframe_cnt <= 4) { |
117 | if (!netif_tx_queue_stopped(dev_queue: netdev_get_tx_queue(dev: padapter->pnetdev, index: queue))) |
118 | netif_stop_subqueue(dev: padapter->pnetdev, queue_index: queue); |
119 | } |
120 | } |
121 | } |
122 | |
123 | static int rtw_mlcst2unicst(struct adapter *padapter, struct sk_buff *skb) |
124 | { |
125 | struct sta_priv *pstapriv = &padapter->stapriv; |
126 | struct xmit_priv *pxmitpriv = &padapter->xmitpriv; |
127 | struct list_head *phead, *plist; |
128 | struct sk_buff *newskb; |
129 | struct sta_info *psta = NULL; |
130 | u8 chk_alive_num = 0; |
131 | char chk_alive_list[NUM_STA]; |
132 | u8 bc_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
133 | u8 null_addr[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
134 | |
135 | int i; |
136 | s32 res; |
137 | |
138 | spin_lock_bh(lock: &pstapriv->asoc_list_lock); |
139 | phead = &pstapriv->asoc_list; |
140 | /* free sta asoc_queue */ |
141 | list_for_each(plist, phead) { |
142 | int stainfo_offset; |
143 | |
144 | psta = list_entry(plist, struct sta_info, asoc_list); |
145 | |
146 | stainfo_offset = rtw_stainfo_offset(stapriv: pstapriv, sta: psta); |
147 | if (stainfo_offset_valid(stainfo_offset)) { |
148 | chk_alive_list[chk_alive_num++] = stainfo_offset; |
149 | } |
150 | } |
151 | spin_unlock_bh(lock: &pstapriv->asoc_list_lock); |
152 | |
153 | for (i = 0; i < chk_alive_num; i++) { |
154 | psta = rtw_get_stainfo_by_offset(stapriv: pstapriv, offset: chk_alive_list[i]); |
155 | if (!(psta->state & _FW_LINKED)) |
156 | continue; |
157 | |
158 | /* avoid come from STA1 and send back STA1 */ |
159 | if (!memcmp(p: psta->hwaddr, q: &skb->data[6], size: 6) || |
160 | !memcmp(p: psta->hwaddr, q: null_addr, size: 6) || |
161 | !memcmp(p: psta->hwaddr, q: bc_addr, size: 6)) |
162 | continue; |
163 | |
164 | newskb = rtw_skb_copy(skb); |
165 | |
166 | if (newskb) { |
167 | memcpy(newskb->data, psta->hwaddr, 6); |
168 | res = rtw_xmit(padapter, pkt: &newskb); |
169 | if (res < 0) { |
170 | pxmitpriv->tx_drop++; |
171 | dev_kfree_skb_any(skb: newskb); |
172 | } |
173 | } else { |
174 | pxmitpriv->tx_drop++; |
175 | /* dev_kfree_skb_any(skb); */ |
176 | return false; /* Caller shall tx this multicast frame via normal way. */ |
177 | } |
178 | } |
179 | |
180 | dev_kfree_skb_any(skb); |
181 | return true; |
182 | } |
183 | |
184 | void _rtw_xmit_entry(struct sk_buff *pkt, struct net_device *pnetdev) |
185 | { |
186 | struct adapter *padapter = rtw_netdev_priv(netdev: pnetdev); |
187 | struct xmit_priv *pxmitpriv = &padapter->xmitpriv; |
188 | struct mlme_priv *pmlmepriv = &padapter->mlmepriv; |
189 | s32 res = 0; |
190 | |
191 | if (rtw_if_up(padapter) == false) |
192 | goto drop_packet; |
193 | |
194 | rtw_check_xmit_resource(padapter, pkt); |
195 | |
196 | if (!rtw_mc2u_disable |
197 | && check_fwstate(pmlmepriv, WIFI_AP_STATE) == true |
198 | && (IP_MCAST_MAC(pkt->data) |
199 | || ICMPV6_MCAST_MAC(pkt->data) |
200 | ) |
201 | && padapter->registrypriv.wifi_spec == 0) { |
202 | if (pxmitpriv->free_xmitframe_cnt > (NR_XMITFRAME / 4)) { |
203 | res = rtw_mlcst2unicst(padapter, skb: pkt); |
204 | if (res) |
205 | return; |
206 | } |
207 | } |
208 | |
209 | res = rtw_xmit(padapter, pkt: &pkt); |
210 | if (res < 0) |
211 | goto drop_packet; |
212 | |
213 | return; |
214 | |
215 | drop_packet: |
216 | pxmitpriv->tx_drop++; |
217 | dev_kfree_skb_any(skb: pkt); |
218 | } |
219 | |
220 | netdev_tx_t rtw_xmit_entry(struct sk_buff *pkt, struct net_device *pnetdev) |
221 | { |
222 | if (pkt) |
223 | _rtw_xmit_entry(pkt, pnetdev); |
224 | |
225 | return NETDEV_TX_OK; |
226 | } |
227 | |