]> Pileus Git - ~andy/linux/blob - drivers/staging/gdm72xx/gdm_qos.c
Merge 3.5-rc4 into staging-next
[~andy/linux] / drivers / staging / gdm72xx / gdm_qos.c
1 /*
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/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
37 static struct {
38         struct list_head head;
39         int cnt;
40         spinlock_t lock;
41 } qos_free_list;
42
43 static 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
50 static 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
70 static 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
87 static 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
101 void 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]);
109                 qcb->csr[i].QoSBufCount = 0;
110                 qcb->csr[i].Enabled = 0;
111         }
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
122 void 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++) {
136                 qcb->csr[i].QoSBufCount = 0;
137                 qcb->csr[i].Enabled = 0;
138         }
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
152 static u32 chk_ipv4_rule(struct gdm_wimax_csr_s *csr, u8 *Stream, u8 *port)
153 {
154         int i;
155
156         if (csr->ClassifierRuleEnable&IPTYPEOFSERVICE) {
157                 if (((Stream[1] & csr->IPToSMask) < csr->IPToSLow) ||
158                 ((Stream[1] & csr->IPToSMask) > csr->IPToSHigh))
159                         return 1;
160         }
161
162         if (csr->ClassifierRuleEnable&PROTOCOL) {
163                 if (Stream[9] != csr->Protocol)
164                         return 1;
165         }
166
167         if (csr->ClassifierRuleEnable&IPMASKEDSRCADDRESS) {
168                 for (i = 0; i < 4; i++) {
169                         if ((Stream[12 + i] & csr->IPSrcAddrMask[i]) !=
170                         (csr->IPSrcAddr[i] & csr->IPSrcAddrMask[i]))
171                                 return 1;
172                 }
173         }
174
175         if (csr->ClassifierRuleEnable&IPMASKEDDSTADDRESS) {
176                 for (i = 0; i < 4; i++) {
177                         if ((Stream[16 + i] & csr->IPDstAddrMask[i]) !=
178                         (csr->IPDstAddr[i] & csr->IPDstAddrMask[i]))
179                                 return 1;
180                 }
181         }
182
183         if (csr->ClassifierRuleEnable&PROTOCOLSRCPORTRANGE) {
184                 i = ((port[0]<<8)&0xff00)+port[1];
185                 if ((i < csr->SrcPortLow) || (i > csr->SrcPortHigh))
186                         return 1;
187         }
188
189         if (csr->ClassifierRuleEnable&PROTOCOLDSTPORTRANGE) {
190                 i = ((port[2]<<8)&0xff00)+port[3];
191                 if ((i < csr->DstPortLow) || (i > csr->DstPortHigh))
192                         return 1;
193         }
194
195         return 0;
196 }
197
198 static u32 get_qos_index(struct nic *nic, u8* iph, u8* tcpudph)
199 {
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++) {
211                         if (qcb->csr[i].Enabled) {
212                                 if (qcb->csr[i].ClassifierRuleEnable) {
213                                         if (chk_ipv4_rule(&qcb->csr[i], iph,
214                                         tcpudph) == 0)
215                                                 return i;
216                                 }
217                         }
218                 }
219         }
220
221         return -1;
222 }
223
224 static 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++) {
233                 if (qcb->csr[i].Enabled) {
234                         if (qcb->csr[i].QoSBufCount < qcb->qos_limit_size) {
235                                 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);
240                                         qcb->csr[i].QoSBufCount++;
241
242                                         if (!list_empty(&qcb->qos_list[i]))
243                                                 wprintk("QoS Index(%d) "
244                                                         "is piled!!\n", i);
245                                 }
246                         }
247                 }
248         }
249
250         return 0;
251 }
252
253 static void send_qos_list(struct nic *nic, struct list_head *head)
254 {
255         struct qos_entry_s *entry, *n;
256
257         list_for_each_entry_safe(entry, n, head, list) {
258                 list_del(&entry->list);
259                 free_qos_entry(entry);
260                 gdm_wimax_send_tx(entry->skb, entry->dev);
261         }
262 }
263
264 int gdm_qos_send_hci_pkt(struct sk_buff *skb, struct net_device *dev)
265 {
266         struct nic *nic = netdev_priv(dev);
267         int index;
268         struct qos_cb_s *qcb = &nic->qos;
269         unsigned long flags;
270         struct ethhdr *ethh = (struct ethhdr *) (skb->data + HCI_HEADER_SIZE);
271         struct iphdr *iph = (struct iphdr *) ((char *) ethh + ETH_HLEN);
272         struct tcphdr *tcph;
273         struct qos_entry_s *entry = NULL;
274         struct list_head send_list;
275         int ret = 0;
276
277         tcph = (struct tcphdr *) iph + iph->ihl*4;
278
279         if (B2H(ethh->h_proto) == ETH_P_IP) {
280                 if (qcb->qos_list_cnt && !qos_free_list.cnt) {
281                         entry = alloc_qos_entry();
282                         entry->skb = skb;
283                         entry->dev = dev;
284                         dprintk("qcb->qos_list_cnt=%d\n", qcb->qos_list_cnt);
285                 }
286
287                 spin_lock_irqsave(&qcb->qos_lock, flags);
288                 if (qcb->qos_list_cnt) {
289                         index = get_qos_index(nic, (u8 *)iph, (u8 *) tcph);
290                         if (index == -1)
291                                 index = qcb->qos_null_idx;
292
293                         if (!entry) {
294                                 entry = alloc_qos_entry();
295                                 entry->skb = skb;
296                                 entry->dev = dev;
297                         }
298
299                         list_add_tail(&entry->list, &qcb->qos_list[index]);
300                         extract_qos_list(nic, &send_list);
301                         spin_unlock_irqrestore(&qcb->qos_lock, flags);
302                         send_qos_list(nic, &send_list);
303                         goto out;
304                 }
305                 spin_unlock_irqrestore(&qcb->qos_lock, flags);
306                 if (entry)
307                         free_qos_entry(entry);
308         }
309
310         ret = gdm_wimax_send_tx(skb, dev);
311 out:
312         return ret;
313 }
314
315 static u32 get_csr(struct qos_cb_s *qcb, u32 SFID, int mode)
316 {
317         int i;
318
319         for (i = 0; i < qcb->qos_list_cnt; i++) {
320                 if (qcb->csr[i].SFID == SFID)
321                         return i;
322         }
323
324         if (mode) {
325                 for (i = 0; i < QOS_MAX; i++) {
326                         if (qcb->csr[i].Enabled == 0) {
327                                 qcb->csr[i].Enabled = 1;
328                                 qcb->qos_list_cnt++;
329                                 return i;
330                         }
331                 }
332         }
333         return -1;
334 }
335
336 #define QOS_CHANGE_DEL  0xFC
337 #define QOS_ADD         0xFD
338 #define QOS_REPORT      0xFE
339
340 void gdm_recv_qos_hci_packet(void *nic_ptr, u8 *buf, int size)
341 {
342         struct nic *nic = nic_ptr;
343         u32 i, SFID, index, pos;
344         u8 subCmdEvt;
345         u8 len;
346         struct qos_cb_s *qcb = &nic->qos;
347         struct qos_entry_s *entry, *n;
348         struct list_head send_list;
349         struct list_head free_list;
350         unsigned long flags;
351
352         subCmdEvt = (u8)buf[4];
353
354         if (subCmdEvt == QOS_REPORT) {
355                 len = (u8)buf[5];
356
357                 spin_lock_irqsave(&qcb->qos_lock, flags);
358                 for (i = 0; i < qcb->qos_list_cnt; i++) {
359                         SFID = ((buf[(i*5)+6]<<24)&0xff000000);
360                         SFID += ((buf[(i*5)+7]<<16)&0xff0000);
361                         SFID += ((buf[(i*5)+8]<<8)&0xff00);
362                         SFID += (buf[(i*5)+9]);
363                         index = get_csr(qcb, SFID, 0);
364                         if (index == -1) {
365                                 spin_unlock_irqrestore(&qcb->qos_lock, flags);
366                                 eprintk("QoS ERROR: No SF\n");
367                                 return;
368                         }
369                         qcb->csr[index].QoSBufCount = buf[(i*5)+10];
370                 }
371
372                 extract_qos_list(nic, &send_list);
373                 spin_unlock_irqrestore(&qcb->qos_lock, flags);
374                 send_qos_list(nic, &send_list);
375                 return;
376         } else if (subCmdEvt == QOS_ADD) {
377                 pos = 5;
378                 len = (u8)buf[pos++];
379
380                 SFID = ((buf[pos++]<<24)&0xff000000);
381                 SFID += ((buf[pos++]<<16)&0xff0000);
382                 SFID += ((buf[pos++]<<8)&0xff00);
383                 SFID += (buf[pos++]);
384
385                 index = get_csr(qcb, SFID, 1);
386                 if (index == -1) {
387                         eprintk("QoS ERROR: csr Update Error\n");
388                         return;
389                 }
390
391                 dprintk("QOS_ADD SFID = 0x%x, index=%d\n", SFID, index);
392
393                 spin_lock_irqsave(&qcb->qos_lock, flags);
394                 qcb->csr[index].SFID = SFID;
395                 qcb->csr[index].ClassifierRuleEnable = ((buf[pos++]<<8)&0xff00);
396                 qcb->csr[index].ClassifierRuleEnable += buf[pos++];
397                 if (qcb->csr[index].ClassifierRuleEnable == 0)
398                         qcb->qos_null_idx = index;
399                 qcb->csr[index].IPToSMask = buf[pos++];
400                 qcb->csr[index].IPToSLow = buf[pos++];
401                 qcb->csr[index].IPToSHigh = buf[pos++];
402                 qcb->csr[index].Protocol = buf[pos++];
403                 qcb->csr[index].IPSrcAddrMask[0] = buf[pos++];
404                 qcb->csr[index].IPSrcAddrMask[1] = buf[pos++];
405                 qcb->csr[index].IPSrcAddrMask[2] = buf[pos++];
406                 qcb->csr[index].IPSrcAddrMask[3] = buf[pos++];
407                 qcb->csr[index].IPSrcAddr[0] = buf[pos++];
408                 qcb->csr[index].IPSrcAddr[1] = buf[pos++];
409                 qcb->csr[index].IPSrcAddr[2] = buf[pos++];
410                 qcb->csr[index].IPSrcAddr[3] = buf[pos++];
411                 qcb->csr[index].IPDstAddrMask[0] = buf[pos++];
412                 qcb->csr[index].IPDstAddrMask[1] = buf[pos++];
413                 qcb->csr[index].IPDstAddrMask[2] = buf[pos++];
414                 qcb->csr[index].IPDstAddrMask[3] = buf[pos++];
415                 qcb->csr[index].IPDstAddr[0] = buf[pos++];
416                 qcb->csr[index].IPDstAddr[1] = buf[pos++];
417                 qcb->csr[index].IPDstAddr[2] = buf[pos++];
418                 qcb->csr[index].IPDstAddr[3] = buf[pos++];
419                 qcb->csr[index].SrcPortLow = ((buf[pos++]<<8)&0xff00);
420                 qcb->csr[index].SrcPortLow += buf[pos++];
421                 qcb->csr[index].SrcPortHigh = ((buf[pos++]<<8)&0xff00);
422                 qcb->csr[index].SrcPortHigh += buf[pos++];
423                 qcb->csr[index].DstPortLow = ((buf[pos++]<<8)&0xff00);
424                 qcb->csr[index].DstPortLow += buf[pos++];
425                 qcb->csr[index].DstPortHigh = ((buf[pos++]<<8)&0xff00);
426                 qcb->csr[index].DstPortHigh += buf[pos++];
427
428                 qcb->qos_limit_size = 254/qcb->qos_list_cnt;
429                 spin_unlock_irqrestore(&qcb->qos_lock, flags);
430         } else if (subCmdEvt == QOS_CHANGE_DEL) {
431                 pos = 5;
432                 len = (u8)buf[pos++];
433                 SFID = ((buf[pos++]<<24)&0xff000000);
434                 SFID += ((buf[pos++]<<16)&0xff0000);
435                 SFID += ((buf[pos++]<<8)&0xff00);
436                 SFID += (buf[pos++]);
437                 index = get_csr(qcb, SFID, 1);
438                 if (index == -1) {
439                         eprintk("QoS ERROR: Wrong index(%d)\n", index);
440                         return;
441                 }
442
443                 dprintk("QOS_CHANGE_DEL SFID = 0x%x, index=%d\n", SFID, index);
444
445                 INIT_LIST_HEAD(&free_list);
446
447                 spin_lock_irqsave(&qcb->qos_lock, flags);
448                 qcb->csr[index].Enabled = 0;
449                 qcb->qos_list_cnt--;
450                 qcb->qos_limit_size = 254/qcb->qos_list_cnt;
451
452                 list_for_each_entry_safe(entry, n, &qcb->qos_list[index],
453                                         list) {
454                         list_move_tail(&entry->list, &free_list);
455                 }
456                 spin_unlock_irqrestore(&qcb->qos_lock, flags);
457                 free_qos_entry_list(&free_list);
458         }
459 }