1 /*
2 * Linux OS Independent Layer
3 *
4 * Copyright (C) 1999-2010, Broadcom Corporation
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
23 *
24 * $Id: linux_osl.c,v 1.125.12.3.8.7 2010/05/04 21:10:04 Exp $
25 */
26
27
28 #define LINUX_OSL
29
30 #include <typedefs.h>
31 #include <bcmendian.h>
32 #include <linuxver.h>
33 #include <bcmdefs.h>
34 #include <osl.h>
35 #include <bcmutils.h>
36 #include <linux/delay.h>
37 #include <pcicfg.h>
38
39 #define PCI_CFG_RETRY 10
40
41 #define OS_HANDLE_MAGIC 0x1234abcd
42 #define BCM_MEM_FILENAME_LEN 24
43
44 #ifdef DHD_USE_STATIC_BUF
45 #define MAX_STATIC_BUF_NUM 16
46 #define STATIC_BUF_SIZE (PAGE_SIZE*2)
47 #define STATIC_BUF_TOTAL_LEN (MAX_STATIC_BUF_NUM*STATIC_BUF_SIZE)
48 typedef struct bcm_static_buf {
49 struct semaphore static_sem;
50 unsigned char *buf_ptr;
51 unsigned char buf_use[MAX_STATIC_BUF_NUM];
52 } bcm_static_buf_t;
53
54 static bcm_static_buf_t *bcm_static_buf = 0;
55
56 #define MAX_STATIC_PKT_NUM 8
57 typedef struct bcm_static_pkt {
58 struct sk_buff *skb_4k[MAX_STATIC_PKT_NUM];
59 struct sk_buff *skb_8k[MAX_STATIC_PKT_NUM];
60 struct semaphore osl_pkt_sem;
61 unsigned char pkt_use[MAX_STATIC_PKT_NUM*2];
62 } bcm_static_pkt_t;
63 static bcm_static_pkt_t *bcm_static_skb = 0;
64
65 #endif
66 typedef struct bcm_mem_link {
67 struct bcm_mem_link *prev;
68 struct bcm_mem_link *next;
69 uint size;
70 int line;
71 char file[BCM_MEM_FILENAME_LEN];
72 } bcm_mem_link_t;
73
74 struct osl_info {
75 osl_pubinfo_t pub;
76 uint magic;
77 void *pdev;
78 uint malloced;
79 uint failed;
80 uint bustype;
81 bcm_mem_link_t *dbgmem_list;
82 };
83
84 static int16 linuxbcmerrormap[] =
85 { 0,
86 -EINVAL,
87 -EINVAL,
88 -EINVAL,
89 -EINVAL,
90 -EINVAL,
91 -EINVAL,
92 -EINVAL,
93 -EINVAL,
94 -EINVAL,
95 -EINVAL,
96 -EINVAL,
97 -EINVAL,
98 -EINVAL,
99 -E2BIG,
100 -E2BIG,
101 -EBUSY,
102 -EINVAL,
103 -EINVAL,
104 -EINVAL,
105 -EINVAL,
106 -EFAULT,
107 -ENOMEM,
108 -EOPNOTSUPP,
109 -EMSGSIZE,
110 -EINVAL,
111 -EPERM,
112 -ENOMEM,
113 -EINVAL,
114 -ERANGE,
115 -EINVAL,
116 -EINVAL,
117 -EINVAL,
118 -EINVAL,
119 -EINVAL,
120 -EIO,
121 -ENODEV,
122 -EINVAL,
123 -EIO,
124 -EIO,
125 -EINVAL,
126 -EINVAL,
127
128
129
130 #if BCME_LAST != -41
131 #error "You need to add a OS error translation in the linuxbcmerrormap \
132 for new error code defined in bcmutils.h"
133 #endif
134 };
135
136
137 int
osl_error(int bcmerror)138 osl_error(int bcmerror)
139 {
140 if (bcmerror > 0)
141 bcmerror = 0;
142 else if (bcmerror < BCME_LAST)
143 bcmerror = BCME_ERROR;
144
145
146 return linuxbcmerrormap[-bcmerror];
147 }
148
149 void * dhd_os_prealloc(int section, unsigned long size);
150 osl_t *
osl_attach(void * pdev,uint bustype,bool pkttag)151 osl_attach(void *pdev, uint bustype, bool pkttag)
152 {
153 osl_t *osh;
154 gfp_t flags;
155
156 flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
157 osh = kmalloc(sizeof(osl_t), flags);
158 ASSERT(osh);
159
160 bzero(osh, sizeof(osl_t));
161
162
163 ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(linuxbcmerrormap) - 1));
164
165 osh->magic = OS_HANDLE_MAGIC;
166 osh->malloced = 0;
167 osh->failed = 0;
168 osh->dbgmem_list = NULL;
169 osh->pdev = pdev;
170 osh->pub.pkttag = pkttag;
171 osh->bustype = bustype;
172
173 switch (bustype) {
174 case PCI_BUS:
175 case SI_BUS:
176 case PCMCIA_BUS:
177 osh->pub.mmbus = TRUE;
178 break;
179 case JTAG_BUS:
180 case SDIO_BUS:
181 case USB_BUS:
182 case SPI_BUS:
183 osh->pub.mmbus = FALSE;
184 break;
185 default:
186 ASSERT(FALSE);
187 break;
188 }
189
190 #ifdef DHD_USE_STATIC_BUF
191
192
193 if (!bcm_static_buf) {
194 if (!(bcm_static_buf = (bcm_static_buf_t *)dhd_os_prealloc(3, STATIC_BUF_SIZE+
195 STATIC_BUF_TOTAL_LEN))) {
196 printk("can not alloc static buf!\n");
197 }
198 else {
199 /* printk("alloc static buf at %x!\n", (unsigned int)bcm_static_buf); */
200 }
201
202 init_MUTEX(&bcm_static_buf->static_sem);
203
204
205 bcm_static_buf->buf_ptr = (unsigned char *)bcm_static_buf + STATIC_BUF_SIZE;
206
207 }
208
209 if (!bcm_static_skb)
210 {
211 int i;
212 void *skb_buff_ptr = 0;
213 bcm_static_skb = (bcm_static_pkt_t *)((char *)bcm_static_buf + 2048);
214 skb_buff_ptr = dhd_os_prealloc(4, 0);
215
216 bcopy(skb_buff_ptr, bcm_static_skb, sizeof(struct sk_buff *)*16);
217 for (i = 0; i < MAX_STATIC_PKT_NUM*2; i++)
218 bcm_static_skb->pkt_use[i] = 0;
219
220 init_MUTEX(&bcm_static_skb->osl_pkt_sem);
221 }
222 #endif
223 return osh;
224 }
225
226 void
osl_detach(osl_t * osh)227 osl_detach(osl_t *osh)
228 {
229 if (osh == NULL)
230 return;
231
232 #ifdef DHD_USE_STATIC_BUF
233 if (bcm_static_buf) {
234 bcm_static_buf = 0;
235 }
236 if (bcm_static_skb) {
237 bcm_static_skb = 0;
238 }
239 #endif
240 ASSERT(osh->magic == OS_HANDLE_MAGIC);
241 kfree(osh);
242 }
243
244
245 void*
osl_pktget(osl_t * osh,uint len)246 osl_pktget(osl_t *osh, uint len)
247 {
248 struct sk_buff *skb;
249
250 if ((skb = dev_alloc_skb(len))) {
251 skb_put(skb, len);
252 skb->priority = 0;
253
254
255 osh->pub.pktalloced++;
256 }
257
258 return ((void*) skb);
259 }
260
261
262 void
osl_pktfree(osl_t * osh,void * p,bool send)263 osl_pktfree(osl_t *osh, void *p, bool send)
264 {
265 struct sk_buff *skb, *nskb;
266
267 skb = (struct sk_buff*) p;
268
269 if (send && osh->pub.tx_fn)
270 osh->pub.tx_fn(osh->pub.tx_ctx, p, 0);
271
272
273 while (skb) {
274 nskb = skb->next;
275 skb->next = NULL;
276
277
278 if (skb->destructor) {
279
280 dev_kfree_skb_any(skb);
281 } else {
282
283 dev_kfree_skb(skb);
284 }
285
286 osh->pub.pktalloced--;
287
288 skb = nskb;
289 }
290 }
291
292 #ifdef DHD_USE_STATIC_BUF
293 void*
osl_pktget_static(osl_t * osh,uint len)294 osl_pktget_static(osl_t *osh, uint len)
295 {
296 int i = 0;
297 struct sk_buff *skb;
298
299
300 if (len > (PAGE_SIZE*2))
301 {
302 printk("Do we really need this big skb??\n");
303 return osl_pktget(osh, len);
304 }
305
306
307 down(&bcm_static_skb->osl_pkt_sem);
308 if (len <= PAGE_SIZE)
309 {
310
311 for (i = 0; i < MAX_STATIC_PKT_NUM; i++)
312 {
313 if (bcm_static_skb->pkt_use[i] == 0)
314 break;
315 }
316
317 if (i != MAX_STATIC_PKT_NUM)
318 {
319 bcm_static_skb->pkt_use[i] = 1;
320 up(&bcm_static_skb->osl_pkt_sem);
321
322 skb = bcm_static_skb->skb_4k[i];
323 skb->tail = skb->data + len;
324 skb->len = len;
325
326 return skb;
327 }
328 }
329
330
331 for (i = 0; i < MAX_STATIC_PKT_NUM; i++)
332 {
333 if (bcm_static_skb->pkt_use[i+MAX_STATIC_PKT_NUM] == 0)
334 break;
335 }
336
337 if (i != MAX_STATIC_PKT_NUM)
338 {
339 bcm_static_skb->pkt_use[i+MAX_STATIC_PKT_NUM] = 1;
340 up(&bcm_static_skb->osl_pkt_sem);
341 skb = bcm_static_skb->skb_8k[i];
342 skb->tail = skb->data + len;
343 skb->len = len;
344
345 return skb;
346 }
347
348
349
350 up(&bcm_static_skb->osl_pkt_sem);
351 printk("all static pkt in use!\n");
352 return osl_pktget(osh, len);
353 }
354
355
356 void
osl_pktfree_static(osl_t * osh,void * p,bool send)357 osl_pktfree_static(osl_t *osh, void *p, bool send)
358 {
359 int i;
360
361 for (i = 0; i < MAX_STATIC_PKT_NUM*2; i++)
362 {
363 if (p == bcm_static_skb->skb_4k[i])
364 {
365 down(&bcm_static_skb->osl_pkt_sem);
366 bcm_static_skb->pkt_use[i] = 0;
367 up(&bcm_static_skb->osl_pkt_sem);
368
369
370 return;
371 }
372 }
373 return osl_pktfree(osh, p, send);
374 }
375 #endif
376 uint32
osl_pci_read_config(osl_t * osh,uint offset,uint size)377 osl_pci_read_config(osl_t *osh, uint offset, uint size)
378 {
379 uint val = 0;
380 uint retry = PCI_CFG_RETRY;
381
382 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
383
384
385 ASSERT(size == 4);
386
387 do {
388 pci_read_config_dword(osh->pdev, offset, &val);
389 if (val != 0xffffffff)
390 break;
391 } while (retry--);
392
393
394 return (val);
395 }
396
397 void
osl_pci_write_config(osl_t * osh,uint offset,uint size,uint val)398 osl_pci_write_config(osl_t *osh, uint offset, uint size, uint val)
399 {
400 uint retry = PCI_CFG_RETRY;
401
402 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
403
404
405 ASSERT(size == 4);
406
407 do {
408 pci_write_config_dword(osh->pdev, offset, val);
409 if (offset != PCI_BAR0_WIN)
410 break;
411 if (osl_pci_read_config(osh, offset, size) == val)
412 break;
413 } while (retry--);
414
415 }
416
417
418 uint
osl_pci_bus(osl_t * osh)419 osl_pci_bus(osl_t *osh)
420 {
421 ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
422
423 return ((struct pci_dev *)osh->pdev)->bus->number;
424 }
425
426
427 uint
osl_pci_slot(osl_t * osh)428 osl_pci_slot(osl_t *osh)
429 {
430 ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
431
432 return PCI_SLOT(((struct pci_dev *)osh->pdev)->devfn);
433 }
434
435 static void
osl_pcmcia_attr(osl_t * osh,uint offset,char * buf,int size,bool write)436 osl_pcmcia_attr(osl_t *osh, uint offset, char *buf, int size, bool write)
437 {
438 }
439
440 void
osl_pcmcia_read_attr(osl_t * osh,uint offset,void * buf,int size)441 osl_pcmcia_read_attr(osl_t *osh, uint offset, void *buf, int size)
442 {
443 osl_pcmcia_attr(osh, offset, (char *) buf, size, FALSE);
444 }
445
446 void
osl_pcmcia_write_attr(osl_t * osh,uint offset,void * buf,int size)447 osl_pcmcia_write_attr(osl_t *osh, uint offset, void *buf, int size)
448 {
449 osl_pcmcia_attr(osh, offset, (char *) buf, size, TRUE);
450 }
451
452
453
454 void*
osl_malloc(osl_t * osh,uint size)455 osl_malloc(osl_t *osh, uint size)
456 {
457 void *addr;
458 gfp_t flags;
459
460 if (osh)
461 ASSERT(osh->magic == OS_HANDLE_MAGIC);
462
463 #ifdef DHD_USE_STATIC_BUF
464 if (bcm_static_buf)
465 {
466 int i = 0;
467 if ((size >= PAGE_SIZE)&&(size <= STATIC_BUF_SIZE))
468 {
469 down(&bcm_static_buf->static_sem);
470
471 for (i = 0; i < MAX_STATIC_BUF_NUM; i++)
472 {
473 if (bcm_static_buf->buf_use[i] == 0)
474 break;
475 }
476
477 if (i == MAX_STATIC_BUF_NUM)
478 {
479 up(&bcm_static_buf->static_sem);
480 printk("all static buff in use!\n");
481 goto original;
482 }
483
484 bcm_static_buf->buf_use[i] = 1;
485 up(&bcm_static_buf->static_sem);
486
487 bzero(bcm_static_buf->buf_ptr+STATIC_BUF_SIZE*i, size);
488 if (osh)
489 osh->malloced += size;
490
491 return ((void *)(bcm_static_buf->buf_ptr+STATIC_BUF_SIZE*i));
492 }
493 }
494 original:
495 #endif
496 flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
497 if ((addr = kmalloc(size, flags)) == NULL) {
498 if (osh)
499 osh->failed++;
500 return (NULL);
501 }
502 if (osh)
503 osh->malloced += size;
504
505 return (addr);
506 }
507
508 void
osl_mfree(osl_t * osh,void * addr,uint size)509 osl_mfree(osl_t *osh, void *addr, uint size)
510 {
511 #ifdef DHD_USE_STATIC_BUF
512 if (bcm_static_buf)
513 {
514 if ((addr > (void *)bcm_static_buf) && ((unsigned char *)addr
515 <= ((unsigned char *)bcm_static_buf + STATIC_BUF_TOTAL_LEN)))
516 {
517 int buf_idx = 0;
518
519 buf_idx = ((unsigned char *)addr - bcm_static_buf->buf_ptr)/STATIC_BUF_SIZE;
520
521 down(&bcm_static_buf->static_sem);
522 bcm_static_buf->buf_use[buf_idx] = 0;
523 up(&bcm_static_buf->static_sem);
524
525 if (osh) {
526 ASSERT(osh->magic == OS_HANDLE_MAGIC);
527 osh->malloced -= size;
528 }
529 return;
530 }
531 }
532 #endif
533 if (osh) {
534 ASSERT(osh->magic == OS_HANDLE_MAGIC);
535 osh->malloced -= size;
536 }
537 kfree(addr);
538 }
539
540 uint
osl_malloced(osl_t * osh)541 osl_malloced(osl_t *osh)
542 {
543 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
544 return (osh->malloced);
545 }
546
547 uint
osl_malloc_failed(osl_t * osh)548 osl_malloc_failed(osl_t *osh)
549 {
550 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
551 return (osh->failed);
552 }
553
554 void*
osl_dma_alloc_consistent(osl_t * osh,uint size,ulong * pap)555 osl_dma_alloc_consistent(osl_t *osh, uint size, ulong *pap)
556 {
557 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
558
559 return (pci_alloc_consistent(osh->pdev, size, (dma_addr_t*)pap));
560 }
561
562 void
osl_dma_free_consistent(osl_t * osh,void * va,uint size,ulong pa)563 osl_dma_free_consistent(osl_t *osh, void *va, uint size, ulong pa)
564 {
565 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
566
567 pci_free_consistent(osh->pdev, size, va, (dma_addr_t)pa);
568 }
569
570 uint
osl_dma_map(osl_t * osh,void * va,uint size,int direction)571 osl_dma_map(osl_t *osh, void *va, uint size, int direction)
572 {
573 int dir;
574
575 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
576 dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
577 return (pci_map_single(osh->pdev, va, size, dir));
578 }
579
580 void
osl_dma_unmap(osl_t * osh,uint pa,uint size,int direction)581 osl_dma_unmap(osl_t *osh, uint pa, uint size, int direction)
582 {
583 int dir;
584
585 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
586 dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
587 pci_unmap_single(osh->pdev, (uint32)pa, size, dir);
588 }
589
590
591 void
osl_delay(uint usec)592 osl_delay(uint usec)
593 {
594 uint d;
595
596 while (usec > 0) {
597 d = MIN(usec, 1000);
598 udelay(d);
599 usec -= d;
600 }
601 }
602
603
604
605 void *
osl_pktdup(osl_t * osh,void * skb)606 osl_pktdup(osl_t *osh, void *skb)
607 {
608 void * p;
609 gfp_t flags;
610
611 flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
612 if ((p = skb_clone((struct sk_buff*)skb, flags)) == NULL)
613 return NULL;
614
615
616 if (osh->pub.pkttag)
617 bzero((void*)((struct sk_buff *)p)->cb, OSL_PKTTAG_SZ);
618
619
620 osh->pub.pktalloced++;
621 return (p);
622 }
623