blob: e26c6a8b26271d1559effcaf9966748cbfdcbf95 [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
Sage Ahn247e9cf2012-05-15 13:20:36 +090014#include <linux/etherdevice.h>
15#include <asm/byteorder.h>
16
17#include <linux/ip.h>
18#include <linux/tcp.h>
19#include <linux/if_ether.h>
20
21#include "gdm_wimax.h"
22#include "hci.h"
23#include "gdm_qos.h"
24
25#define B2H(x) __be16_to_cpu(x)
26
27#undef dprintk
28#define dprintk(fmt, args ...) printk(KERN_DEBUG "[QoS] " fmt, ## args)
29#undef wprintk
30#define wprintk(fmt, args ...) \
31 printk(KERN_WARNING "[QoS WARNING] " fmt, ## args)
32#undef eprintk
33#define eprintk(fmt, args ...) printk(KERN_ERR "[QoS ERROR] " fmt, ## args)
34
35
36#define MAX_FREE_LIST_CNT 32
37static struct {
38 struct list_head head;
39 int cnt;
40 spinlock_t lock;
41} qos_free_list;
42
43static void init_qos_entry_list(void)
44{
45 qos_free_list.cnt = 0;
46 INIT_LIST_HEAD(&qos_free_list.head);
47 spin_lock_init(&qos_free_list.lock);
48}
49
50static void *alloc_qos_entry(void)
51{
52 struct qos_entry_s *entry;
53 unsigned long flags;
54
55 spin_lock_irqsave(&qos_free_list.lock, flags);
56 if (qos_free_list.cnt) {
57 entry = list_entry(qos_free_list.head.prev, struct qos_entry_s,
58 list);
59 list_del(&entry->list);
60 qos_free_list.cnt--;
61 spin_unlock_irqrestore(&qos_free_list.lock, flags);
62 return entry;
63 }
64 spin_unlock_irqrestore(&qos_free_list.lock, flags);
65
66 entry = kmalloc(sizeof(struct qos_entry_s), GFP_ATOMIC);
67 return entry;
68}
69
70static void free_qos_entry(void *entry)
71{
72 struct qos_entry_s *qentry = (struct qos_entry_s *) entry;
73 unsigned long flags;
74
75 spin_lock_irqsave(&qos_free_list.lock, flags);
76 if (qos_free_list.cnt < MAX_FREE_LIST_CNT) {
77 list_add(&qentry->list, &qos_free_list.head);
78 qos_free_list.cnt++;
79 spin_unlock_irqrestore(&qos_free_list.lock, flags);
80 return;
81 }
82 spin_unlock_irqrestore(&qos_free_list.lock, flags);
83
84 kfree(entry);
85}
86
87static void free_qos_entry_list(struct list_head *free_list)
88{
89 struct qos_entry_s *entry, *n;
90 int total_free = 0;
91
92 list_for_each_entry_safe(entry, n, free_list, list) {
93 list_del(&entry->list);
94 kfree(entry);
95 total_free++;
96 }
97
98 dprintk("%s: total_free_cnt=%d\n", __func__, total_free);
99}
100
101void gdm_qos_init(void *nic_ptr)
102{
103 struct nic *nic = nic_ptr;
104 struct qos_cb_s *qcb = &nic->qos;
105 int i;
106
107 for (i = 0 ; i < QOS_MAX; i++) {
108 INIT_LIST_HEAD(&qcb->qos_list[i]);
Macpaul Lin13b26632012-09-11 15:55:31 +0800109 qcb->csr[i].qos_buf_count = 0;
110 qcb->csr[i].enabled = 0;
Sage Ahn247e9cf2012-05-15 13:20:36 +0900111 }
112
113 qcb->qos_list_cnt = 0;
114 qcb->qos_null_idx = QOS_MAX-1;
115 qcb->qos_limit_size = 255;
116
117 spin_lock_init(&qcb->qos_lock);
118
119 init_qos_entry_list();
120}
121
122void gdm_qos_release_list(void *nic_ptr)
123{
124 struct nic *nic = nic_ptr;
125 struct qos_cb_s *qcb = &nic->qos;
126 unsigned long flags;
127 struct qos_entry_s *entry, *n;
128 struct list_head free_list;
129 int i;
130
131 INIT_LIST_HEAD(&free_list);
132
133 spin_lock_irqsave(&qcb->qos_lock, flags);
134
135 for (i = 0; i < QOS_MAX; i++) {
Macpaul Lin13b26632012-09-11 15:55:31 +0800136 qcb->csr[i].qos_buf_count = 0;
137 qcb->csr[i].enabled = 0;
Sage Ahn247e9cf2012-05-15 13:20:36 +0900138 }
139
140 qcb->qos_list_cnt = 0;
141 qcb->qos_null_idx = QOS_MAX-1;
142
143 for (i = 0; i < QOS_MAX; i++) {
144 list_for_each_entry_safe(entry, n, &qcb->qos_list[i], list) {
145 list_move_tail(&entry->list, &free_list);
146 }
147 }
148 spin_unlock_irqrestore(&qcb->qos_lock, flags);
149 free_qos_entry_list(&free_list);
150}
151
152static u32 chk_ipv4_rule(struct gdm_wimax_csr_s *csr, u8 *Stream, u8 *port)
153{
154 int i;
155
Macpaul Lin13b26632012-09-11 15:55:31 +0800156 if (csr->classifier_rule_en&IPTYPEOFSERVICE) {
157 if (((Stream[1] & csr->ip2s_mask) < csr->ip2s_lo) ||
158 ((Stream[1] & csr->ip2s_mask) > csr->ip2s_hi))
Sage Ahn247e9cf2012-05-15 13:20:36 +0900159 return 1;
160 }
161
Macpaul Lin13b26632012-09-11 15:55:31 +0800162 if (csr->classifier_rule_en&PROTOCOL) {
163 if (Stream[9] != csr->protocol)
Sage Ahn247e9cf2012-05-15 13:20:36 +0900164 return 1;
165 }
166
Macpaul Lin13b26632012-09-11 15:55:31 +0800167 if (csr->classifier_rule_en&IPMASKEDSRCADDRESS) {
Sage Ahn247e9cf2012-05-15 13:20:36 +0900168 for (i = 0; i < 4; i++) {
Macpaul Lin13b26632012-09-11 15:55:31 +0800169 if ((Stream[12 + i] & csr->ipsrc_addrmask[i]) !=
170 (csr->ipsrc_addr[i] & csr->ipsrc_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&IPMASKEDDSTADDRESS) {
Sage Ahn247e9cf2012-05-15 13:20:36 +0900176 for (i = 0; i < 4; i++) {
Macpaul Lin13b26632012-09-11 15:55:31 +0800177 if ((Stream[16 + i] & csr->ipdst_addrmask[i]) !=
178 (csr->ipdst_addr[i] & csr->ipdst_addrmask[i]))
Sage Ahn247e9cf2012-05-15 13:20:36 +0900179 return 1;
180 }
181 }
182
Macpaul Lin13b26632012-09-11 15:55:31 +0800183 if (csr->classifier_rule_en&PROTOCOLSRCPORTRANGE) {
Sage Ahn247e9cf2012-05-15 13:20:36 +0900184 i = ((port[0]<<8)&0xff00)+port[1];
Macpaul Lin13b26632012-09-11 15:55:31 +0800185 if ((i < csr->srcport_lo) || (i > csr->srcport_hi))
Sage Ahn247e9cf2012-05-15 13:20:36 +0900186 return 1;
187 }
188
Macpaul Lin13b26632012-09-11 15:55:31 +0800189 if (csr->classifier_rule_en&PROTOCOLDSTPORTRANGE) {
Sage Ahn247e9cf2012-05-15 13:20:36 +0900190 i = ((port[2]<<8)&0xff00)+port[3];
Macpaul Lin13b26632012-09-11 15:55:31 +0800191 if ((i < csr->dstport_lo) || (i > csr->dstport_hi))
Sage Ahn247e9cf2012-05-15 13:20:36 +0900192 return 1;
193 }
194
195 return 0;
196}
197
Devendra Naga9b90b712012-07-12 11:59:22 +0545198static u32 get_qos_index(struct nic *nic, u8 *iph, u8 *tcpudph)
Sage Ahn247e9cf2012-05-15 13:20:36 +0900199{
200 u32 IP_Ver, Header_Len, i;
201 struct qos_cb_s *qcb = &nic->qos;
202
203 if (iph == NULL || tcpudph == NULL)
204 return -1;
205
206 IP_Ver = (iph[0]>>4)&0xf;
207 Header_Len = iph[0]&0xf;
208
209 if (IP_Ver == 4) {
210 for (i = 0; i < QOS_MAX; i++) {
Macpaul Lin13b26632012-09-11 15:55:31 +0800211 if (qcb->csr[i].enabled) {
212 if (qcb->csr[i].classifier_rule_en) {
Sage Ahn247e9cf2012-05-15 13:20:36 +0900213 if (chk_ipv4_rule(&qcb->csr[i], iph,
214 tcpudph) == 0)
215 return i;
216 }
217 }
218 }
219 }
220
221 return -1;
222}
223
224static u32 extract_qos_list(struct nic *nic, struct list_head *head)
225{
226 struct qos_cb_s *qcb = &nic->qos;
227 struct qos_entry_s *entry;
228 int i;
229
230 INIT_LIST_HEAD(head);
231
232 for (i = 0; i < QOS_MAX; i++) {
Macpaul Lin13b26632012-09-11 15:55:31 +0800233 if (qcb->csr[i].enabled) {
234 if (qcb->csr[i].qos_buf_count < qcb->qos_limit_size) {
Sage Ahn247e9cf2012-05-15 13:20:36 +0900235 if (!list_empty(&qcb->qos_list[i])) {
236 entry = list_entry(
237 qcb->qos_list[i].prev,
238 struct qos_entry_s, list);
239 list_move_tail(&entry->list, head);
Macpaul Lin13b26632012-09-11 15:55:31 +0800240 qcb->csr[i].qos_buf_count++;
Sage Ahn247e9cf2012-05-15 13:20:36 +0900241
242 if (!list_empty(&qcb->qos_list[i]))
Devendra Naga9b90b712012-07-12 11:59:22 +0545243 wprintk("QoS Index(%d) is piled!!\n", i);
Sage Ahn247e9cf2012-05-15 13:20:36 +0900244 }
245 }
246 }
247 }
248
249 return 0;
250}
251
252static void send_qos_list(struct nic *nic, struct list_head *head)
253{
254 struct qos_entry_s *entry, *n;
255
256 list_for_each_entry_safe(entry, n, head, list) {
257 list_del(&entry->list);
258 free_qos_entry(entry);
259 gdm_wimax_send_tx(entry->skb, entry->dev);
260 }
261}
262
263int gdm_qos_send_hci_pkt(struct sk_buff *skb, struct net_device *dev)
264{
265 struct nic *nic = netdev_priv(dev);
266 int index;
267 struct qos_cb_s *qcb = &nic->qos;
268 unsigned long flags;
269 struct ethhdr *ethh = (struct ethhdr *) (skb->data + HCI_HEADER_SIZE);
270 struct iphdr *iph = (struct iphdr *) ((char *) ethh + ETH_HLEN);
271 struct tcphdr *tcph;
272 struct qos_entry_s *entry = NULL;
273 struct list_head send_list;
274 int ret = 0;
275
276 tcph = (struct tcphdr *) iph + iph->ihl*4;
277
278 if (B2H(ethh->h_proto) == ETH_P_IP) {
279 if (qcb->qos_list_cnt && !qos_free_list.cnt) {
280 entry = alloc_qos_entry();
281 entry->skb = skb;
282 entry->dev = dev;
283 dprintk("qcb->qos_list_cnt=%d\n", qcb->qos_list_cnt);
284 }
285
286 spin_lock_irqsave(&qcb->qos_lock, flags);
287 if (qcb->qos_list_cnt) {
288 index = get_qos_index(nic, (u8 *)iph, (u8 *) tcph);
289 if (index == -1)
290 index = qcb->qos_null_idx;
291
292 if (!entry) {
293 entry = alloc_qos_entry();
294 entry->skb = skb;
295 entry->dev = dev;
296 }
297
298 list_add_tail(&entry->list, &qcb->qos_list[index]);
299 extract_qos_list(nic, &send_list);
300 spin_unlock_irqrestore(&qcb->qos_lock, flags);
301 send_qos_list(nic, &send_list);
302 goto out;
303 }
304 spin_unlock_irqrestore(&qcb->qos_lock, flags);
305 if (entry)
306 free_qos_entry(entry);
307 }
308
309 ret = gdm_wimax_send_tx(skb, dev);
310out:
311 return ret;
312}
313
314static u32 get_csr(struct qos_cb_s *qcb, u32 SFID, int mode)
315{
316 int i;
317
318 for (i = 0; i < qcb->qos_list_cnt; i++) {
319 if (qcb->csr[i].SFID == SFID)
320 return i;
321 }
322
323 if (mode) {
324 for (i = 0; i < QOS_MAX; i++) {
Macpaul Lin13b26632012-09-11 15:55:31 +0800325 if (qcb->csr[i].enabled == 0) {
326 qcb->csr[i].enabled = 1;
Sage Ahn247e9cf2012-05-15 13:20:36 +0900327 qcb->qos_list_cnt++;
328 return i;
329 }
330 }
331 }
332 return -1;
333}
334
335#define QOS_CHANGE_DEL 0xFC
336#define QOS_ADD 0xFD
337#define QOS_REPORT 0xFE
338
339void gdm_recv_qos_hci_packet(void *nic_ptr, u8 *buf, int size)
340{
341 struct nic *nic = nic_ptr;
342 u32 i, SFID, index, pos;
343 u8 subCmdEvt;
344 u8 len;
345 struct qos_cb_s *qcb = &nic->qos;
346 struct qos_entry_s *entry, *n;
347 struct list_head send_list;
348 struct list_head free_list;
349 unsigned long flags;
350
351 subCmdEvt = (u8)buf[4];
352
353 if (subCmdEvt == QOS_REPORT) {
354 len = (u8)buf[5];
355
356 spin_lock_irqsave(&qcb->qos_lock, flags);
357 for (i = 0; i < qcb->qos_list_cnt; i++) {
358 SFID = ((buf[(i*5)+6]<<24)&0xff000000);
359 SFID += ((buf[(i*5)+7]<<16)&0xff0000);
360 SFID += ((buf[(i*5)+8]<<8)&0xff00);
361 SFID += (buf[(i*5)+9]);
362 index = get_csr(qcb, SFID, 0);
363 if (index == -1) {
364 spin_unlock_irqrestore(&qcb->qos_lock, flags);
365 eprintk("QoS ERROR: No SF\n");
366 return;
367 }
Macpaul Lin13b26632012-09-11 15:55:31 +0800368 qcb->csr[index].qos_buf_count = buf[(i*5)+10];
Sage Ahn247e9cf2012-05-15 13:20:36 +0900369 }
370
371 extract_qos_list(nic, &send_list);
372 spin_unlock_irqrestore(&qcb->qos_lock, flags);
373 send_qos_list(nic, &send_list);
374 return;
375 } else if (subCmdEvt == QOS_ADD) {
376 pos = 5;
377 len = (u8)buf[pos++];
378
379 SFID = ((buf[pos++]<<24)&0xff000000);
380 SFID += ((buf[pos++]<<16)&0xff0000);
381 SFID += ((buf[pos++]<<8)&0xff00);
382 SFID += (buf[pos++]);
383
384 index = get_csr(qcb, SFID, 1);
385 if (index == -1) {
386 eprintk("QoS ERROR: csr Update Error\n");
387 return;
388 }
389
390 dprintk("QOS_ADD SFID = 0x%x, index=%d\n", SFID, index);
391
392 spin_lock_irqsave(&qcb->qos_lock, flags);
393 qcb->csr[index].SFID = SFID;
Macpaul Lin13b26632012-09-11 15:55:31 +0800394 qcb->csr[index].classifier_rule_en = ((buf[pos++]<<8)&0xff00);
395 qcb->csr[index].classifier_rule_en += buf[pos++];
396 if (qcb->csr[index].classifier_rule_en == 0)
Sage Ahn247e9cf2012-05-15 13:20:36 +0900397 qcb->qos_null_idx = index;
Macpaul Lin13b26632012-09-11 15:55:31 +0800398 qcb->csr[index].ip2s_mask = buf[pos++];
399 qcb->csr[index].ip2s_lo = buf[pos++];
400 qcb->csr[index].ip2s_hi = buf[pos++];
401 qcb->csr[index].protocol = buf[pos++];
402 qcb->csr[index].ipsrc_addrmask[0] = buf[pos++];
403 qcb->csr[index].ipsrc_addrmask[1] = buf[pos++];
404 qcb->csr[index].ipsrc_addrmask[2] = buf[pos++];
405 qcb->csr[index].ipsrc_addrmask[3] = buf[pos++];
406 qcb->csr[index].ipsrc_addr[0] = buf[pos++];
407 qcb->csr[index].ipsrc_addr[1] = buf[pos++];
408 qcb->csr[index].ipsrc_addr[2] = buf[pos++];
409 qcb->csr[index].ipsrc_addr[3] = buf[pos++];
410 qcb->csr[index].ipdst_addrmask[0] = buf[pos++];
411 qcb->csr[index].ipdst_addrmask[1] = buf[pos++];
412 qcb->csr[index].ipdst_addrmask[2] = buf[pos++];
413 qcb->csr[index].ipdst_addrmask[3] = buf[pos++];
414 qcb->csr[index].ipdst_addr[0] = buf[pos++];
415 qcb->csr[index].ipdst_addr[1] = buf[pos++];
416 qcb->csr[index].ipdst_addr[2] = buf[pos++];
417 qcb->csr[index].ipdst_addr[3] = buf[pos++];
418 qcb->csr[index].srcport_lo = ((buf[pos++]<<8)&0xff00);
419 qcb->csr[index].srcport_lo += buf[pos++];
420 qcb->csr[index].srcport_hi = ((buf[pos++]<<8)&0xff00);
421 qcb->csr[index].srcport_hi += buf[pos++];
422 qcb->csr[index].dstport_lo = ((buf[pos++]<<8)&0xff00);
423 qcb->csr[index].dstport_lo += buf[pos++];
424 qcb->csr[index].dstport_hi = ((buf[pos++]<<8)&0xff00);
425 qcb->csr[index].dstport_hi += buf[pos++];
Sage Ahn247e9cf2012-05-15 13:20:36 +0900426
427 qcb->qos_limit_size = 254/qcb->qos_list_cnt;
428 spin_unlock_irqrestore(&qcb->qos_lock, flags);
429 } else if (subCmdEvt == QOS_CHANGE_DEL) {
430 pos = 5;
431 len = (u8)buf[pos++];
432 SFID = ((buf[pos++]<<24)&0xff000000);
433 SFID += ((buf[pos++]<<16)&0xff0000);
434 SFID += ((buf[pos++]<<8)&0xff00);
435 SFID += (buf[pos++]);
436 index = get_csr(qcb, SFID, 1);
437 if (index == -1) {
438 eprintk("QoS ERROR: Wrong index(%d)\n", index);
439 return;
440 }
441
442 dprintk("QOS_CHANGE_DEL SFID = 0x%x, index=%d\n", SFID, index);
443
444 INIT_LIST_HEAD(&free_list);
445
446 spin_lock_irqsave(&qcb->qos_lock, flags);
Macpaul Lin13b26632012-09-11 15:55:31 +0800447 qcb->csr[index].enabled = 0;
Sage Ahn247e9cf2012-05-15 13:20:36 +0900448 qcb->qos_list_cnt--;
449 qcb->qos_limit_size = 254/qcb->qos_list_cnt;
450
451 list_for_each_entry_safe(entry, n, &qcb->qos_list[index],
452 list) {
453 list_move_tail(&entry->list, &free_list);
454 }
455 spin_unlock_irqrestore(&qcb->qos_lock, flags);
456 free_qos_entry_list(&free_list);
457 }
458}