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