blob: cf72a077bd9089a249a5332cbd05af2f969fe6aa [file] [log] [blame]
Dmitry Shmidtda65eba2010-05-19 18:53:11 -07001/*
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>
Dima Zavinf23ba502011-01-05 16:04:43 -080038#include <linux/mutex.h>
Dmitry Shmidtda65eba2010-05-19 18:53:11 -070039
40#define PCI_CFG_RETRY 10
41
42#define OS_HANDLE_MAGIC 0x1234abcd
43#define BCM_MEM_FILENAME_LEN 24
44
45#ifdef DHD_USE_STATIC_BUF
46#define MAX_STATIC_BUF_NUM 16
47#define STATIC_BUF_SIZE (PAGE_SIZE*2)
48#define STATIC_BUF_TOTAL_LEN (MAX_STATIC_BUF_NUM*STATIC_BUF_SIZE)
49typedef struct bcm_static_buf {
Dima Zavinf23ba502011-01-05 16:04:43 -080050 struct mutex static_sem;
Dmitry Shmidtda65eba2010-05-19 18:53:11 -070051 unsigned char *buf_ptr;
52 unsigned char buf_use[MAX_STATIC_BUF_NUM];
53} bcm_static_buf_t;
54
55static bcm_static_buf_t *bcm_static_buf = 0;
56
57#define MAX_STATIC_PKT_NUM 8
58typedef struct bcm_static_pkt {
59 struct sk_buff *skb_4k[MAX_STATIC_PKT_NUM];
60 struct sk_buff *skb_8k[MAX_STATIC_PKT_NUM];
Dima Zavinf23ba502011-01-05 16:04:43 -080061 struct mutex osl_pkt_sem;
Dmitry Shmidtda65eba2010-05-19 18:53:11 -070062 unsigned char pkt_use[MAX_STATIC_PKT_NUM*2];
63} bcm_static_pkt_t;
64static bcm_static_pkt_t *bcm_static_skb = 0;
65
66#endif
67typedef struct bcm_mem_link {
68 struct bcm_mem_link *prev;
69 struct bcm_mem_link *next;
70 uint size;
71 int line;
72 char file[BCM_MEM_FILENAME_LEN];
73} bcm_mem_link_t;
74
75struct osl_info {
76 osl_pubinfo_t pub;
77 uint magic;
78 void *pdev;
79 uint malloced;
80 uint failed;
81 uint bustype;
82 bcm_mem_link_t *dbgmem_list;
83};
84
85static int16 linuxbcmerrormap[] =
86{ 0,
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 -EINVAL,
100 -E2BIG,
101 -E2BIG,
102 -EBUSY,
103 -EINVAL,
104 -EINVAL,
105 -EINVAL,
106 -EINVAL,
107 -EFAULT,
108 -ENOMEM,
109 -EOPNOTSUPP,
110 -EMSGSIZE,
111 -EINVAL,
112 -EPERM,
113 -ENOMEM,
114 -EINVAL,
115 -ERANGE,
116 -EINVAL,
117 -EINVAL,
118 -EINVAL,
119 -EINVAL,
120 -EINVAL,
121 -EIO,
122 -ENODEV,
123 -EINVAL,
124 -EIO,
125 -EIO,
126 -EINVAL,
127 -EINVAL,
128
129
130
131#if BCME_LAST != -41
132#error "You need to add a OS error translation in the linuxbcmerrormap \
133 for new error code defined in bcmutils.h"
134#endif
135};
136
137
138int
139osl_error(int bcmerror)
140{
141 if (bcmerror > 0)
142 bcmerror = 0;
143 else if (bcmerror < BCME_LAST)
144 bcmerror = BCME_ERROR;
145
146
147 return linuxbcmerrormap[-bcmerror];
148}
149
150void * dhd_os_prealloc(int section, unsigned long size);
151osl_t *
152osl_attach(void *pdev, uint bustype, bool pkttag)
153{
154 osl_t *osh;
155 gfp_t flags;
156
157 flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
158 osh = kmalloc(sizeof(osl_t), flags);
159 ASSERT(osh);
160
161 bzero(osh, sizeof(osl_t));
162
163
164 ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(linuxbcmerrormap) - 1));
165
166 osh->magic = OS_HANDLE_MAGIC;
167 osh->malloced = 0;
168 osh->failed = 0;
169 osh->dbgmem_list = NULL;
170 osh->pdev = pdev;
171 osh->pub.pkttag = pkttag;
172 osh->bustype = bustype;
173
174 switch (bustype) {
175 case PCI_BUS:
176 case SI_BUS:
177 case PCMCIA_BUS:
178 osh->pub.mmbus = TRUE;
179 break;
180 case JTAG_BUS:
181 case SDIO_BUS:
182 case USB_BUS:
183 case SPI_BUS:
184 osh->pub.mmbus = FALSE;
185 break;
186 default:
187 ASSERT(FALSE);
188 break;
189 }
190
191#ifdef DHD_USE_STATIC_BUF
192
193
194 if (!bcm_static_buf) {
195 if (!(bcm_static_buf = (bcm_static_buf_t *)dhd_os_prealloc(3, STATIC_BUF_SIZE+
196 STATIC_BUF_TOTAL_LEN))) {
197 printk("can not alloc static buf!\n");
198 }
199 else {
200 /* printk("alloc static buf at %x!\n", (unsigned int)bcm_static_buf); */
201 }
202
Dima Zavinf23ba502011-01-05 16:04:43 -0800203 mutex_init(&bcm_static_buf->static_sem);
Dmitry Shmidtda65eba2010-05-19 18:53:11 -0700204
205
206 bcm_static_buf->buf_ptr = (unsigned char *)bcm_static_buf + STATIC_BUF_SIZE;
207
208 }
209
210 if (!bcm_static_skb)
211 {
212 int i;
213 void *skb_buff_ptr = 0;
214 bcm_static_skb = (bcm_static_pkt_t *)((char *)bcm_static_buf + 2048);
215 skb_buff_ptr = dhd_os_prealloc(4, 0);
216
217 bcopy(skb_buff_ptr, bcm_static_skb, sizeof(struct sk_buff *)*16);
218 for (i = 0; i < MAX_STATIC_PKT_NUM*2; i++)
219 bcm_static_skb->pkt_use[i] = 0;
220
Dima Zavinf23ba502011-01-05 16:04:43 -0800221 mutex_init(&bcm_static_skb->osl_pkt_sem);
Dmitry Shmidtda65eba2010-05-19 18:53:11 -0700222 }
223#endif
224 return osh;
225}
226
227void
228osl_detach(osl_t *osh)
229{
230 if (osh == NULL)
231 return;
232
233#ifdef DHD_USE_STATIC_BUF
234 if (bcm_static_buf) {
235 bcm_static_buf = 0;
236 }
237 if (bcm_static_skb) {
238 bcm_static_skb = 0;
239 }
240#endif
241 ASSERT(osh->magic == OS_HANDLE_MAGIC);
242 kfree(osh);
243}
244
245
246void*
247osl_pktget(osl_t *osh, uint len)
248{
249 struct sk_buff *skb;
Dmitry Shmidtd31005f2011-03-10 10:18:39 -0800250 gfp_t flags;
Dmitry Shmidtda65eba2010-05-19 18:53:11 -0700251
Dmitry Shmidtd31005f2011-03-10 10:18:39 -0800252 flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
253 if ((skb = __dev_alloc_skb(len, flags))) {
Dmitry Shmidtda65eba2010-05-19 18:53:11 -0700254 skb_put(skb, len);
255 skb->priority = 0;
256
257
258 osh->pub.pktalloced++;
259 }
260
261 return ((void*) skb);
262}
263
264
265void
266osl_pktfree(osl_t *osh, void *p, bool send)
267{
268 struct sk_buff *skb, *nskb;
269
270 skb = (struct sk_buff*) p;
271
272 if (send && osh->pub.tx_fn)
273 osh->pub.tx_fn(osh->pub.tx_ctx, p, 0);
274
275
276 while (skb) {
277 nskb = skb->next;
278 skb->next = NULL;
279
280
281 if (skb->destructor) {
282
283 dev_kfree_skb_any(skb);
284 } else {
285
286 dev_kfree_skb(skb);
287 }
288
289 osh->pub.pktalloced--;
290
291 skb = nskb;
292 }
293}
294
295#ifdef DHD_USE_STATIC_BUF
296void*
297osl_pktget_static(osl_t *osh, uint len)
298{
299 int i = 0;
300 struct sk_buff *skb;
301
302
303 if (len > (PAGE_SIZE*2))
304 {
305 printk("Do we really need this big skb??\n");
306 return osl_pktget(osh, len);
307 }
308
309
Dima Zavinf23ba502011-01-05 16:04:43 -0800310 mutex_lock(&bcm_static_skb->osl_pkt_sem);
Dmitry Shmidtda65eba2010-05-19 18:53:11 -0700311 if (len <= PAGE_SIZE)
312 {
313
314 for (i = 0; i < MAX_STATIC_PKT_NUM; i++)
315 {
316 if (bcm_static_skb->pkt_use[i] == 0)
317 break;
318 }
319
320 if (i != MAX_STATIC_PKT_NUM)
321 {
322 bcm_static_skb->pkt_use[i] = 1;
Dima Zavinf23ba502011-01-05 16:04:43 -0800323 mutex_unlock(&bcm_static_skb->osl_pkt_sem);
Dmitry Shmidtda65eba2010-05-19 18:53:11 -0700324
325 skb = bcm_static_skb->skb_4k[i];
326 skb->tail = skb->data + len;
327 skb->len = len;
328
329 return skb;
330 }
331 }
332
333
334 for (i = 0; i < MAX_STATIC_PKT_NUM; i++)
335 {
336 if (bcm_static_skb->pkt_use[i+MAX_STATIC_PKT_NUM] == 0)
337 break;
338 }
339
340 if (i != MAX_STATIC_PKT_NUM)
341 {
342 bcm_static_skb->pkt_use[i+MAX_STATIC_PKT_NUM] = 1;
Dima Zavinf23ba502011-01-05 16:04:43 -0800343 mutex_unlock(&bcm_static_skb->osl_pkt_sem);
Dmitry Shmidtda65eba2010-05-19 18:53:11 -0700344 skb = bcm_static_skb->skb_8k[i];
345 skb->tail = skb->data + len;
346 skb->len = len;
347
348 return skb;
349 }
350
351
352
Dima Zavinf23ba502011-01-05 16:04:43 -0800353 mutex_unlock(&bcm_static_skb->osl_pkt_sem);
Dmitry Shmidtda65eba2010-05-19 18:53:11 -0700354 printk("all static pkt in use!\n");
355 return osl_pktget(osh, len);
356}
357
358
359void
360osl_pktfree_static(osl_t *osh, void *p, bool send)
361{
362 int i;
363
364 for (i = 0; i < MAX_STATIC_PKT_NUM*2; i++)
365 {
366 if (p == bcm_static_skb->skb_4k[i])
367 {
Dima Zavinf23ba502011-01-05 16:04:43 -0800368 mutex_lock(&bcm_static_skb->osl_pkt_sem);
Dmitry Shmidtda65eba2010-05-19 18:53:11 -0700369 bcm_static_skb->pkt_use[i] = 0;
Dima Zavinf23ba502011-01-05 16:04:43 -0800370 mutex_unlock(&bcm_static_skb->osl_pkt_sem);
Dmitry Shmidtda65eba2010-05-19 18:53:11 -0700371
372
373 return;
374 }
375 }
376 return osl_pktfree(osh, p, send);
377}
378#endif
379uint32
380osl_pci_read_config(osl_t *osh, uint offset, uint size)
381{
382 uint val = 0;
383 uint retry = PCI_CFG_RETRY;
384
385 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
386
387
388 ASSERT(size == 4);
389
390 do {
391 pci_read_config_dword(osh->pdev, offset, &val);
392 if (val != 0xffffffff)
393 break;
394 } while (retry--);
395
396
397 return (val);
398}
399
400void
401osl_pci_write_config(osl_t *osh, uint offset, uint size, uint val)
402{
403 uint retry = PCI_CFG_RETRY;
404
405 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
406
407
408 ASSERT(size == 4);
409
410 do {
411 pci_write_config_dword(osh->pdev, offset, val);
412 if (offset != PCI_BAR0_WIN)
413 break;
414 if (osl_pci_read_config(osh, offset, size) == val)
415 break;
416 } while (retry--);
417
418}
419
420
421uint
422osl_pci_bus(osl_t *osh)
423{
424 ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
425
426 return ((struct pci_dev *)osh->pdev)->bus->number;
427}
428
429
430uint
431osl_pci_slot(osl_t *osh)
432{
433 ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
434
435 return PCI_SLOT(((struct pci_dev *)osh->pdev)->devfn);
436}
437
438static void
439osl_pcmcia_attr(osl_t *osh, uint offset, char *buf, int size, bool write)
440{
441}
442
443void
444osl_pcmcia_read_attr(osl_t *osh, uint offset, void *buf, int size)
445{
446 osl_pcmcia_attr(osh, offset, (char *) buf, size, FALSE);
447}
448
449void
450osl_pcmcia_write_attr(osl_t *osh, uint offset, void *buf, int size)
451{
452 osl_pcmcia_attr(osh, offset, (char *) buf, size, TRUE);
453}
454
455
456
457void*
458osl_malloc(osl_t *osh, uint size)
459{
460 void *addr;
461 gfp_t flags;
462
463 if (osh)
464 ASSERT(osh->magic == OS_HANDLE_MAGIC);
465
466#ifdef DHD_USE_STATIC_BUF
467 if (bcm_static_buf)
468 {
469 int i = 0;
470 if ((size >= PAGE_SIZE)&&(size <= STATIC_BUF_SIZE))
471 {
Dima Zavinf23ba502011-01-05 16:04:43 -0800472 mutex_lock(&bcm_static_buf->static_sem);
Dmitry Shmidtda65eba2010-05-19 18:53:11 -0700473
474 for (i = 0; i < MAX_STATIC_BUF_NUM; i++)
475 {
476 if (bcm_static_buf->buf_use[i] == 0)
477 break;
478 }
479
480 if (i == MAX_STATIC_BUF_NUM)
481 {
Dima Zavinf23ba502011-01-05 16:04:43 -0800482 mutex_unlock(&bcm_static_buf->static_sem);
Dmitry Shmidtda65eba2010-05-19 18:53:11 -0700483 printk("all static buff in use!\n");
484 goto original;
485 }
486
487 bcm_static_buf->buf_use[i] = 1;
Dima Zavinf23ba502011-01-05 16:04:43 -0800488 mutex_unlock(&bcm_static_buf->static_sem);
Dmitry Shmidtda65eba2010-05-19 18:53:11 -0700489
490 bzero(bcm_static_buf->buf_ptr+STATIC_BUF_SIZE*i, size);
491 if (osh)
492 osh->malloced += size;
493
494 return ((void *)(bcm_static_buf->buf_ptr+STATIC_BUF_SIZE*i));
495 }
496 }
497original:
498#endif
499 flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
500 if ((addr = kmalloc(size, flags)) == NULL) {
501 if (osh)
502 osh->failed++;
503 return (NULL);
504 }
505 if (osh)
506 osh->malloced += size;
507
508 return (addr);
509}
510
511void
512osl_mfree(osl_t *osh, void *addr, uint size)
513{
514#ifdef DHD_USE_STATIC_BUF
515 if (bcm_static_buf)
516 {
517 if ((addr > (void *)bcm_static_buf) && ((unsigned char *)addr
518 <= ((unsigned char *)bcm_static_buf + STATIC_BUF_TOTAL_LEN)))
519 {
520 int buf_idx = 0;
521
522 buf_idx = ((unsigned char *)addr - bcm_static_buf->buf_ptr)/STATIC_BUF_SIZE;
523
Dima Zavinf23ba502011-01-05 16:04:43 -0800524 mutex_lock(&bcm_static_buf->static_sem);
Dmitry Shmidtda65eba2010-05-19 18:53:11 -0700525 bcm_static_buf->buf_use[buf_idx] = 0;
Dima Zavinf23ba502011-01-05 16:04:43 -0800526 mutex_unlock(&bcm_static_buf->static_sem);
Dmitry Shmidtda65eba2010-05-19 18:53:11 -0700527
528 if (osh) {
529 ASSERT(osh->magic == OS_HANDLE_MAGIC);
530 osh->malloced -= size;
531 }
532 return;
533 }
534 }
535#endif
536 if (osh) {
537 ASSERT(osh->magic == OS_HANDLE_MAGIC);
538 osh->malloced -= size;
539 }
540 kfree(addr);
541}
542
543uint
544osl_malloced(osl_t *osh)
545{
546 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
547 return (osh->malloced);
548}
549
550uint
551osl_malloc_failed(osl_t *osh)
552{
553 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
554 return (osh->failed);
555}
556
557void*
558osl_dma_alloc_consistent(osl_t *osh, uint size, ulong *pap)
559{
560 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
561
562 return (pci_alloc_consistent(osh->pdev, size, (dma_addr_t*)pap));
563}
564
565void
566osl_dma_free_consistent(osl_t *osh, void *va, uint size, ulong pa)
567{
568 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
569
570 pci_free_consistent(osh->pdev, size, va, (dma_addr_t)pa);
571}
572
573uint
574osl_dma_map(osl_t *osh, void *va, uint size, int direction)
575{
576 int dir;
577
578 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
579 dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
580 return (pci_map_single(osh->pdev, va, size, dir));
581}
582
583void
584osl_dma_unmap(osl_t *osh, uint pa, uint size, int direction)
585{
586 int dir;
587
588 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
589 dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
590 pci_unmap_single(osh->pdev, (uint32)pa, size, dir);
591}
592
593
594void
595osl_delay(uint usec)
596{
597 uint d;
598
599 while (usec > 0) {
600 d = MIN(usec, 1000);
601 udelay(d);
602 usec -= d;
603 }
604}
605
606
607
608void *
609osl_pktdup(osl_t *osh, void *skb)
610{
611 void * p;
612 gfp_t flags;
613
614 flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
615 if ((p = skb_clone((struct sk_buff*)skb, flags)) == NULL)
616 return NULL;
617
618
619 if (osh->pub.pkttag)
620 bzero((void*)((struct sk_buff *)p)->cb, OSL_PKTTAG_SZ);
621
622
623 osh->pub.pktalloced++;
624 return (p);
625}