]> Pileus Git - ~andy/linux/blob - drivers/staging/gdm72xx/gdm_qos.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending
[~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/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
38 static struct {
39         struct list_head head;
40         int cnt;
41         spinlock_t lock;
42 } qos_free_list;
43
44 static 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
51 static 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
71 static 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
88 static 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
102 void 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
123 void 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
153 static 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
199 static 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
225 static 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
254 static 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
265 int 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);
312 out:
313         return ret;
314 }
315
316 static 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
341 void 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 }