blob: 33df46a6238011f1042d2d838397cd9e6a4e095c [file] [log] [blame]
Sage Ahn247e9cf2012-05-15 13:20:36 +09001/*
2 * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
3 *
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
YAMANE Toshiaki2d839022012-10-29 20:04:42 +090014#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15
Sage Ahn247e9cf2012-05-15 13:20:36 +090016#include <linux/etherdevice.h>
17#include <asm/byteorder.h>
18
19#include <linux/ip.h>
20#include <linux/tcp.h>
21#include <linux/if_ether.h>
22
23#include "gdm_wimax.h"
24#include "hci.h"
25#include "gdm_qos.h"
26
27#define B2H(x) __be16_to_cpu(x)
28
Sage Ahn247e9cf2012-05-15 13:20:36 +090029#define MAX_FREE_LIST_CNT 32
30static struct {
31 struct list_head head;
32 int cnt;
33 spinlock_t lock;
34} qos_free_list;
35
36static void init_qos_entry_list(void)
37{
38 qos_free_list.cnt = 0;
39 INIT_LIST_HEAD(&qos_free_list.head);
40 spin_lock_init(&qos_free_list.lock);
41}
42
43static void *alloc_qos_entry(void)
44{
45 struct qos_entry_s *entry;
46 unsigned long flags;
47
48 spin_lock_irqsave(&qos_free_list.lock, flags);
49 if (qos_free_list.cnt) {
Michalis Pappas39c511f2014-05-09 18:08:27 +080050 entry = list_entry(qos_free_list.head.prev, struct qos_entry_s, list);
Sage Ahn247e9cf2012-05-15 13:20:36 +090051 list_del(&entry->list);
52 qos_free_list.cnt--;
53 spin_unlock_irqrestore(&qos_free_list.lock, flags);
54 return entry;
55 }
56 spin_unlock_irqrestore(&qos_free_list.lock, flags);
57
Michalis Pappasbaad5ee2014-05-09 18:08:22 +080058 entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
Sage Ahn247e9cf2012-05-15 13:20:36 +090059 return entry;
60}
61
62static void free_qos_entry(void *entry)
63{
Michalis Pappas39c511f2014-05-09 18:08:27 +080064 struct qos_entry_s *qentry = (struct qos_entry_s *)entry;
Sage Ahn247e9cf2012-05-15 13:20:36 +090065 unsigned long flags;
66
67 spin_lock_irqsave(&qos_free_list.lock, flags);
68 if (qos_free_list.cnt < MAX_FREE_LIST_CNT) {
69 list_add(&qentry->list, &qos_free_list.head);
70 qos_free_list.cnt++;
71 spin_unlock_irqrestore(&qos_free_list.lock, flags);
72 return;
73 }
74 spin_unlock_irqrestore(&qos_free_list.lock, flags);
75
76 kfree(entry);
77}
78
79static void free_qos_entry_list(struct list_head *free_list)
80{
81 struct qos_entry_s *entry, *n;
82 int total_free = 0;
83
84 list_for_each_entry_safe(entry, n, free_list, list) {
85 list_del(&entry->list);
86 kfree(entry);
87 total_free++;
88 }
89
YAMANE Toshiaki2d839022012-10-29 20:04:42 +090090 pr_debug("%s: total_free_cnt=%d\n", __func__, total_free);
Sage Ahn247e9cf2012-05-15 13:20:36 +090091}
92
93void gdm_qos_init(void *nic_ptr)
94{
95 struct nic *nic = nic_ptr;
96 struct qos_cb_s *qcb = &nic->qos;
97 int i;
98
Masanari Iida0f4a97f2013-12-14 03:32:53 +090099 for (i = 0; i < QOS_MAX; i++) {
Sage Ahn247e9cf2012-05-15 13:20:36 +0900100 INIT_LIST_HEAD(&qcb->qos_list[i]);
Macpaul Lin13b26632012-09-11 15:55:31 +0800101 qcb->csr[i].qos_buf_count = 0;
102 qcb->csr[i].enabled = 0;
Sage Ahn247e9cf2012-05-15 13:20:36 +0900103 }
104
105 qcb->qos_list_cnt = 0;
106 qcb->qos_null_idx = QOS_MAX-1;
107 qcb->qos_limit_size = 255;
108
109 spin_lock_init(&qcb->qos_lock);
110
111 init_qos_entry_list();
112}
113
114void gdm_qos_release_list(void *nic_ptr)
115{
116 struct nic *nic = nic_ptr;
117 struct qos_cb_s *qcb = &nic->qos;
118 unsigned long flags;
119 struct qos_entry_s *entry, *n;
120 struct list_head free_list;
121 int i;
122
123 INIT_LIST_HEAD(&free_list);
124
125 spin_lock_irqsave(&qcb->qos_lock, flags);
126
127 for (i = 0; i < QOS_MAX; i++) {
Macpaul Lin13b26632012-09-11 15:55:31 +0800128 qcb->csr[i].qos_buf_count = 0;
129 qcb->csr[i].enabled = 0;
Sage Ahn247e9cf2012-05-15 13:20:36 +0900130 }
131
132 qcb->qos_list_cnt = 0;
133 qcb->qos_null_idx = QOS_MAX-1;
134
135 for (i = 0; i < QOS_MAX; i++) {
136 list_for_each_entry_safe(entry, n, &qcb->qos_list[i], list) {
137 list_move_tail(&entry->list, &free_list);
138 }
139 }
140 spin_unlock_irqrestore(&qcb->qos_lock, flags);
141 free_qos_entry_list(&free_list);
142}
143
Michalis Pappas633c8a22014-05-09 18:08:24 +0800144static u32 chk_ipv4_rule(struct gdm_wimax_csr_s *csr, u8 *stream, u8 *port)
Sage Ahn247e9cf2012-05-15 13:20:36 +0900145{
146 int i;
147
Macpaul Lin13b26632012-09-11 15:55:31 +0800148 if (csr->classifier_rule_en&IPTYPEOFSERVICE) {
Michalis Pappas633c8a22014-05-09 18:08:24 +0800149 if (((stream[1] & csr->ip2s_mask) < csr->ip2s_lo) ||
Michalis Pappas39c511f2014-05-09 18:08:27 +0800150 ((stream[1] & csr->ip2s_mask) > csr->ip2s_hi))
Sage Ahn247e9cf2012-05-15 13:20:36 +0900151 return 1;
152 }
153
Macpaul Lin13b26632012-09-11 15:55:31 +0800154 if (csr->classifier_rule_en&PROTOCOL) {
Michalis Pappas633c8a22014-05-09 18:08:24 +0800155 if (stream[9] != csr->protocol)
Sage Ahn247e9cf2012-05-15 13:20:36 +0900156 return 1;
157 }
158
Macpaul Lin13b26632012-09-11 15:55:31 +0800159 if (csr->classifier_rule_en&IPMASKEDSRCADDRESS) {
Sage Ahn247e9cf2012-05-15 13:20:36 +0900160 for (i = 0; i < 4; i++) {
Michalis Pappas633c8a22014-05-09 18:08:24 +0800161 if ((stream[12 + i] & csr->ipsrc_addrmask[i]) !=
Macpaul Lin13b26632012-09-11 15:55:31 +0800162 (csr->ipsrc_addr[i] & csr->ipsrc_addrmask[i]))
Sage Ahn247e9cf2012-05-15 13:20:36 +0900163 return 1;
164 }
165 }
166
Macpaul Lin13b26632012-09-11 15:55:31 +0800167 if (csr->classifier_rule_en&IPMASKEDDSTADDRESS) {
Sage Ahn247e9cf2012-05-15 13:20:36 +0900168 for (i = 0; i < 4; i++) {
Michalis Pappas633c8a22014-05-09 18:08:24 +0800169 if ((stream[16 + i] & csr->ipdst_addrmask[i]) !=
Macpaul Lin13b26632012-09-11 15:55:31 +0800170 (csr->ipdst_addr[i] & csr->ipdst_addrmask[i]))
Sage Ahn247e9cf2012-05-15 13:20:36 +0900171 return 1;
172 }
173 }
174
Macpaul Lin13b26632012-09-11 15:55:31 +0800175 if (csr->classifier_rule_en&PROTOCOLSRCPORTRANGE) {
Sage Ahn247e9cf2012-05-15 13:20:36 +0900176 i = ((port[0]<<8)&0xff00)+port[1];
Macpaul Lin13b26632012-09-11 15:55:31 +0800177 if ((i < csr->srcport_lo) || (i > csr->srcport_hi))
Sage Ahn247e9cf2012-05-15 13:20:36 +0900178 return 1;
179 }
180
Macpaul Lin13b26632012-09-11 15:55:31 +0800181 if (csr->classifier_rule_en&PROTOCOLDSTPORTRANGE) {
Sage Ahn247e9cf2012-05-15 13:20:36 +0900182 i = ((port[2]<<8)&0xff00)+port[3];
Macpaul Lin13b26632012-09-11 15:55:31 +0800183 if ((i < csr->dstport_lo) || (i > csr->dstport_hi))
Sage Ahn247e9cf2012-05-15 13:20:36 +0900184 return 1;
185 }
186
187 return 0;
188}
189
Devendra Naga9b90b712012-07-12 11:59:22 +0545190static u32 get_qos_index(struct nic *nic, u8 *iph, u8 *tcpudph)
Sage Ahn247e9cf2012-05-15 13:20:36 +0900191{
Michalis Pappas39c511f2014-05-09 18:08:27 +0800192 u32 IP_ver, i;
Sage Ahn247e9cf2012-05-15 13:20:36 +0900193 struct qos_cb_s *qcb = &nic->qos;
194
195 if (iph == NULL || tcpudph == NULL)
196 return -1;
197
Michalis Pappas633c8a22014-05-09 18:08:24 +0800198 IP_ver = (iph[0]>>4)&0xf;
Sage Ahn247e9cf2012-05-15 13:20:36 +0900199
Michalis Pappas39c511f2014-05-09 18:08:27 +0800200 if (IP_ver != 4)
201 return -1;
202
203 for (i = 0; i < QOS_MAX; i++) {
204 if (!qcb->csr[i].enabled)
205 continue;
206 if (!qcb->csr[i].classifier_rule_en)
207 continue;
208 if (chk_ipv4_rule(&qcb->csr[i], iph, tcpudph) == 0)
209 return i;
Sage Ahn247e9cf2012-05-15 13:20:36 +0900210 }
211
212 return -1;
213}
214
215static u32 extract_qos_list(struct nic *nic, struct list_head *head)
216{
217 struct qos_cb_s *qcb = &nic->qos;
218 struct qos_entry_s *entry;
219 int i;
220
221 INIT_LIST_HEAD(head);
222
223 for (i = 0; i < QOS_MAX; i++) {
Michalis Pappas39c511f2014-05-09 18:08:27 +0800224 if (!qcb->csr[i].enabled)
225 continue;
226 if (qcb->csr[i].qos_buf_count >= qcb->qos_limit_size)
227 continue;
228 if (list_empty(&qcb->qos_list[i]))
229 continue;
Sage Ahn247e9cf2012-05-15 13:20:36 +0900230
Michalis Pappas39c511f2014-05-09 18:08:27 +0800231 entry = list_entry(qcb->qos_list[i].prev, struct qos_entry_s, list);
232
233 list_move_tail(&entry->list, head);
234 qcb->csr[i].qos_buf_count++;
235
236 if (!list_empty(&qcb->qos_list[i]))
237 netdev_warn(nic->netdev, "Index(%d) is piled!!\n", i);
Sage Ahn247e9cf2012-05-15 13:20:36 +0900238 }
239
240 return 0;
241}
242
243static void send_qos_list(struct nic *nic, struct list_head *head)
244{
245 struct qos_entry_s *entry, *n;
246
247 list_for_each_entry_safe(entry, n, head, list) {
248 list_del(&entry->list);
Sage Ahn247e9cf2012-05-15 13:20:36 +0900249 gdm_wimax_send_tx(entry->skb, entry->dev);
Dan Carpenterf2a6fed2013-07-22 11:00:53 +0300250 free_qos_entry(entry);
Sage Ahn247e9cf2012-05-15 13:20:36 +0900251 }
252}
253
254int gdm_qos_send_hci_pkt(struct sk_buff *skb, struct net_device *dev)
255{
256 struct nic *nic = netdev_priv(dev);
257 int index;
258 struct qos_cb_s *qcb = &nic->qos;
259 unsigned long flags;
Michalis Pappas39c511f2014-05-09 18:08:27 +0800260 struct ethhdr *ethh = (struct ethhdr *)(skb->data + HCI_HEADER_SIZE);
261 struct iphdr *iph = (struct iphdr *)((char *)ethh + ETH_HLEN);
Sage Ahn247e9cf2012-05-15 13:20:36 +0900262 struct tcphdr *tcph;
263 struct qos_entry_s *entry = NULL;
264 struct list_head send_list;
265 int ret = 0;
266
Michalis Pappas39c511f2014-05-09 18:08:27 +0800267 tcph = (struct tcphdr *)iph + iph->ihl*4;
Sage Ahn247e9cf2012-05-15 13:20:36 +0900268
269 if (B2H(ethh->h_proto) == ETH_P_IP) {
270 if (qcb->qos_list_cnt && !qos_free_list.cnt) {
271 entry = alloc_qos_entry();
272 entry->skb = skb;
273 entry->dev = dev;
YAMANE Toshiaki2d839022012-10-29 20:04:42 +0900274 netdev_dbg(dev, "qcb->qos_list_cnt=%d\n",
275 qcb->qos_list_cnt);
Sage Ahn247e9cf2012-05-15 13:20:36 +0900276 }
277
278 spin_lock_irqsave(&qcb->qos_lock, flags);
279 if (qcb->qos_list_cnt) {
Michalis Pappas39c511f2014-05-09 18:08:27 +0800280 index = get_qos_index(nic, (u8 *)iph, (u8 *)tcph);
Sage Ahn247e9cf2012-05-15 13:20:36 +0900281 if (index == -1)
282 index = qcb->qos_null_idx;
283
284 if (!entry) {
285 entry = alloc_qos_entry();
286 entry->skb = skb;
287 entry->dev = dev;
288 }
289
290 list_add_tail(&entry->list, &qcb->qos_list[index]);
291 extract_qos_list(nic, &send_list);
292 spin_unlock_irqrestore(&qcb->qos_lock, flags);
293 send_qos_list(nic, &send_list);
294 goto out;
295 }
296 spin_unlock_irqrestore(&qcb->qos_lock, flags);
297 if (entry)
298 free_qos_entry(entry);
299 }
300
301 ret = gdm_wimax_send_tx(skb, dev);
302out:
303 return ret;
304}
305
306static u32 get_csr(struct qos_cb_s *qcb, u32 SFID, int mode)
307{
308 int i;
309
310 for (i = 0; i < qcb->qos_list_cnt; i++) {
311 if (qcb->csr[i].SFID == SFID)
312 return i;
313 }
314
315 if (mode) {
316 for (i = 0; i < QOS_MAX; i++) {
Macpaul Lin13b26632012-09-11 15:55:31 +0800317 if (qcb->csr[i].enabled == 0) {
318 qcb->csr[i].enabled = 1;
Sage Ahn247e9cf2012-05-15 13:20:36 +0900319 qcb->qos_list_cnt++;
320 return i;
321 }
322 }
323 }
324 return -1;
325}
326
327#define QOS_CHANGE_DEL 0xFC
328#define QOS_ADD 0xFD
329#define QOS_REPORT 0xFE
330
331void gdm_recv_qos_hci_packet(void *nic_ptr, u8 *buf, int size)
332{
333 struct nic *nic = nic_ptr;
334 u32 i, SFID, index, pos;
Michalis Pappas633c8a22014-05-09 18:08:24 +0800335 u8 sub_cmd_evt;
Sage Ahn247e9cf2012-05-15 13:20:36 +0900336 struct qos_cb_s *qcb = &nic->qos;
337 struct qos_entry_s *entry, *n;
338 struct list_head send_list;
339 struct list_head free_list;
340 unsigned long flags;
341
Michalis Pappas633c8a22014-05-09 18:08:24 +0800342 sub_cmd_evt = (u8)buf[4];
Sage Ahn247e9cf2012-05-15 13:20:36 +0900343
Michalis Pappas633c8a22014-05-09 18:08:24 +0800344 if (sub_cmd_evt == QOS_REPORT) {
Sage Ahn247e9cf2012-05-15 13:20:36 +0900345 spin_lock_irqsave(&qcb->qos_lock, flags);
346 for (i = 0; i < qcb->qos_list_cnt; i++) {
347 SFID = ((buf[(i*5)+6]<<24)&0xff000000);
348 SFID += ((buf[(i*5)+7]<<16)&0xff0000);
349 SFID += ((buf[(i*5)+8]<<8)&0xff00);
350 SFID += (buf[(i*5)+9]);
351 index = get_csr(qcb, SFID, 0);
352 if (index == -1) {
353 spin_unlock_irqrestore(&qcb->qos_lock, flags);
YAMANE Toshiaki2d839022012-10-29 20:04:42 +0900354 netdev_err(nic->netdev, "QoS ERROR: No SF\n");
Sage Ahn247e9cf2012-05-15 13:20:36 +0900355 return;
356 }
Macpaul Lin13b26632012-09-11 15:55:31 +0800357 qcb->csr[index].qos_buf_count = buf[(i*5)+10];
Sage Ahn247e9cf2012-05-15 13:20:36 +0900358 }
359
360 extract_qos_list(nic, &send_list);
361 spin_unlock_irqrestore(&qcb->qos_lock, flags);
362 send_qos_list(nic, &send_list);
363 return;
Peter Huewed3852912013-02-19 18:50:20 +0100364 }
Sage Ahn247e9cf2012-05-15 13:20:36 +0900365
Michalis Pappas633c8a22014-05-09 18:08:24 +0800366 /* sub_cmd_evt == QOS_ADD || sub_cmd_evt == QOS_CHANG_DEL */
Peter Huewed3852912013-02-19 18:50:20 +0100367 pos = 6;
368 SFID = ((buf[pos++]<<24)&0xff000000);
369 SFID += ((buf[pos++]<<16)&0xff0000);
370 SFID += ((buf[pos++]<<8)&0xff00);
371 SFID += (buf[pos++]);
Sage Ahn247e9cf2012-05-15 13:20:36 +0900372
Peter Huewed3852912013-02-19 18:50:20 +0100373 index = get_csr(qcb, SFID, 1);
374 if (index == -1) {
375 netdev_err(nic->netdev,
Michalis Pappas39c511f2014-05-09 18:08:27 +0800376 "QoS ERROR: csr Update Error / Wrong index (%d)\n",
Peter Huewed3852912013-02-19 18:50:20 +0100377 index);
378 return;
379 }
Sage Ahn247e9cf2012-05-15 13:20:36 +0900380
Michalis Pappas633c8a22014-05-09 18:08:24 +0800381 if (sub_cmd_evt == QOS_ADD) {
YAMANE Toshiaki2d839022012-10-29 20:04:42 +0900382 netdev_dbg(nic->netdev, "QOS_ADD SFID = 0x%x, index=%d\n",
383 SFID, index);
Sage Ahn247e9cf2012-05-15 13:20:36 +0900384
385 spin_lock_irqsave(&qcb->qos_lock, flags);
386 qcb->csr[index].SFID = SFID;
Macpaul Lin13b26632012-09-11 15:55:31 +0800387 qcb->csr[index].classifier_rule_en = ((buf[pos++]<<8)&0xff00);
388 qcb->csr[index].classifier_rule_en += buf[pos++];
389 if (qcb->csr[index].classifier_rule_en == 0)
Sage Ahn247e9cf2012-05-15 13:20:36 +0900390 qcb->qos_null_idx = index;
Macpaul Lin13b26632012-09-11 15:55:31 +0800391 qcb->csr[index].ip2s_mask = buf[pos++];
392 qcb->csr[index].ip2s_lo = buf[pos++];
393 qcb->csr[index].ip2s_hi = buf[pos++];
394 qcb->csr[index].protocol = buf[pos++];
395 qcb->csr[index].ipsrc_addrmask[0] = buf[pos++];
396 qcb->csr[index].ipsrc_addrmask[1] = buf[pos++];
397 qcb->csr[index].ipsrc_addrmask[2] = buf[pos++];
398 qcb->csr[index].ipsrc_addrmask[3] = buf[pos++];
399 qcb->csr[index].ipsrc_addr[0] = buf[pos++];
400 qcb->csr[index].ipsrc_addr[1] = buf[pos++];
401 qcb->csr[index].ipsrc_addr[2] = buf[pos++];
402 qcb->csr[index].ipsrc_addr[3] = buf[pos++];
403 qcb->csr[index].ipdst_addrmask[0] = buf[pos++];
404 qcb->csr[index].ipdst_addrmask[1] = buf[pos++];
405 qcb->csr[index].ipdst_addrmask[2] = buf[pos++];
406 qcb->csr[index].ipdst_addrmask[3] = buf[pos++];
407 qcb->csr[index].ipdst_addr[0] = buf[pos++];
408 qcb->csr[index].ipdst_addr[1] = buf[pos++];
409 qcb->csr[index].ipdst_addr[2] = buf[pos++];
410 qcb->csr[index].ipdst_addr[3] = buf[pos++];
411 qcb->csr[index].srcport_lo = ((buf[pos++]<<8)&0xff00);
412 qcb->csr[index].srcport_lo += buf[pos++];
413 qcb->csr[index].srcport_hi = ((buf[pos++]<<8)&0xff00);
414 qcb->csr[index].srcport_hi += buf[pos++];
415 qcb->csr[index].dstport_lo = ((buf[pos++]<<8)&0xff00);
416 qcb->csr[index].dstport_lo += buf[pos++];
417 qcb->csr[index].dstport_hi = ((buf[pos++]<<8)&0xff00);
418 qcb->csr[index].dstport_hi += buf[pos++];
Sage Ahn247e9cf2012-05-15 13:20:36 +0900419
420 qcb->qos_limit_size = 254/qcb->qos_list_cnt;
421 spin_unlock_irqrestore(&qcb->qos_lock, flags);
Michalis Pappas633c8a22014-05-09 18:08:24 +0800422 } else if (sub_cmd_evt == QOS_CHANGE_DEL) {
YAMANE Toshiaki2d839022012-10-29 20:04:42 +0900423 netdev_dbg(nic->netdev, "QOS_CHANGE_DEL SFID = 0x%x, index=%d\n",
424 SFID, index);
Sage Ahn247e9cf2012-05-15 13:20:36 +0900425
426 INIT_LIST_HEAD(&free_list);
427
428 spin_lock_irqsave(&qcb->qos_lock, flags);
Macpaul Lin13b26632012-09-11 15:55:31 +0800429 qcb->csr[index].enabled = 0;
Sage Ahn247e9cf2012-05-15 13:20:36 +0900430 qcb->qos_list_cnt--;
431 qcb->qos_limit_size = 254/qcb->qos_list_cnt;
432
Michalis Pappas39c511f2014-05-09 18:08:27 +0800433 list_for_each_entry_safe(entry, n, &qcb->qos_list[index], list) {
Sage Ahn247e9cf2012-05-15 13:20:36 +0900434 list_move_tail(&entry->list, &free_list);
435 }
436 spin_unlock_irqrestore(&qcb->qos_lock, flags);
437 free_qos_entry_list(&free_list);
438 }
439}