diff options
Diffstat (limited to 'package/broadcom-wl-old/src/driver/linux_osl.c')
-rw-r--r-- | package/broadcom-wl-old/src/driver/linux_osl.c | 889 |
1 files changed, 889 insertions, 0 deletions
diff --git a/package/broadcom-wl-old/src/driver/linux_osl.c b/package/broadcom-wl-old/src/driver/linux_osl.c new file mode 100644 index 000000000..467756b0e --- /dev/null +++ b/package/broadcom-wl-old/src/driver/linux_osl.c @@ -0,0 +1,889 @@ +/* + * Linux OS Independent Layer + * + * Copyright 2007, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + */ + +#define LINUX_OSL + +#include <typedefs.h> +#include <bcmendian.h> +#include <linuxver.h> +#include <bcmdefs.h> +#include <osl.h> +#include "linux_osl.h" +#include "bcmutils.h" +#include <linux/delay.h> +#ifdef mips +#include <asm/paccess.h> +#endif /* mips */ +#include <pcicfg.h> + +#define PCI_CFG_RETRY 10 + +#define OS_HANDLE_MAGIC 0x1234abcd /* Magic # to recognise osh */ +#define BCM_MEM_FILENAME_LEN 24 /* Mem. filename length */ + +typedef struct bcm_mem_link +{ + struct bcm_mem_link *prev; + struct bcm_mem_link *next; + uint size; + int line; + char file[BCM_MEM_FILENAME_LEN]; +} bcm_mem_link_t; + +#if 0 +struct osl_info +{ + osl_pubinfo_t pub; + uint magic; + void *pdev; + uint malloced; + uint failed; + uint bustype; + bcm_mem_link_t *dbgmem_list; +#ifdef BCMDBG_PKT /* pkt logging for debugging */ + pktlist_info_t pktlist; +#endif /* BCMDBG_PKT */ +}; +#endif + +static int16 linuxbcmerrormap[] = { 0, /* 0 */ + -EINVAL, /* BCME_ERROR */ + -EINVAL, /* BCME_BADARG */ + -EINVAL, /* BCME_BADOPTION */ + -EINVAL, /* BCME_NOTUP */ + -EINVAL, /* BCME_NOTDOWN */ + -EINVAL, /* BCME_NOTAP */ + -EINVAL, /* BCME_NOTSTA */ + -EINVAL, /* BCME_BADKEYIDX */ + -EINVAL, /* BCME_RADIOOFF */ + -EINVAL, /* BCME_NOTBANDLOCKED */ + -EINVAL, /* BCME_NOCLK */ + -EINVAL, /* BCME_BADRATESET */ + -EINVAL, /* BCME_BADBAND */ + -E2BIG, /* BCME_BUFTOOSHORT */ + -E2BIG, /* BCME_BUFTOOLONG */ + -EBUSY, /* BCME_BUSY */ + -EINVAL, /* BCME_NOTASSOCIATED */ + -EINVAL, /* BCME_BADSSIDLEN */ + -EINVAL, /* BCME_OUTOFRANGECHAN */ + -EINVAL, /* BCME_BADCHAN */ + -EFAULT, /* BCME_BADADDR */ + -ENOMEM, /* BCME_NORESOURCE */ + -EOPNOTSUPP, /* BCME_UNSUPPORTED */ + -EMSGSIZE, /* BCME_BADLENGTH */ + -EINVAL, /* BCME_NOTREADY */ + -EPERM, /* BCME_NOTPERMITTED */ + -ENOMEM, /* BCME_NOMEM */ + -EINVAL, /* BCME_ASSOCIATED */ + -ERANGE, /* BCME_RANGE */ + -EINVAL, /* BCME_NOTFOUND */ + -EINVAL, /* BCME_WME_NOT_ENABLED */ + -EINVAL, /* BCME_TSPEC_NOTFOUND */ + -EINVAL, /* BCME_ACM_NOTSUPPORTED */ + -EINVAL, /* BCME_NOT_WME_ASSOCIATION */ + -EIO, /* BCME_SDIO_ERROR */ + -ENODEV, /* BCME_DONGLE_DOWN */ + -EINVAL /* BCME_VERSION */ +/* When an new error code is added to bcmutils.h, add os + * spcecific error translation here as well + */ +/* check if BCME_LAST changed since the last time this function was updated */ +#if BCME_LAST != -37 +#error "You need to add a OS error translation in the linuxbcmerrormap \ + for new error code defined in bcmuitls.h" +#endif /* BCME_LAST != -37 */ +}; + +/* translate bcmerrors into linux errors */ +int +osl_error (int bcmerror) +{ + if (bcmerror > 0) + bcmerror = 0; + else if (bcmerror < BCME_LAST) + bcmerror = BCME_ERROR; + + /* Array bounds covered by ASSERT in osl_attach */ + return linuxbcmerrormap[-bcmerror]; +} + +osl_t * +osl_attach (void *pdev, uint bustype, bool pkttag) +{ + osl_t *osh; + + osh = kmalloc (sizeof (osl_t), GFP_ATOMIC); + ASSERT (osh); + + bzero (osh, sizeof (osl_t)); + + /* Check that error map has the right number of entries in it */ + ASSERT (ABS (BCME_LAST) == (ARRAYSIZE (linuxbcmerrormap) - 1)); + + osh->magic = OS_HANDLE_MAGIC; + osh->malloced = 0; + osh->failed = 0; + osh->dbgmem_list = NULL; + osh->pdev = pdev; + osh->pub.pkttag = pkttag; + osh->bustype = bustype; + + switch (bustype) + { + case PCI_BUS: + case SB_BUS: + case PCMCIA_BUS: + osh->pub.mmbus = TRUE; + break; + case JTAG_BUS: + case SDIO_BUS: + break; + default: + ASSERT (FALSE); + break; + } + +#ifdef BCMDBG + if (pkttag) + { + struct sk_buff *skb; + ASSERT (OSL_PKTTAG_SZ <= sizeof (skb->cb)); + } +#endif + return osh; +} + +void +osl_detach (osl_t * osh) +{ + if (osh == NULL) + return; + + ASSERT (osh->magic == OS_HANDLE_MAGIC); + kfree (osh); +} + +/* Return a new packet. zero out pkttag */ +void * +osl_pktget (osl_t * osh, uint len) +{ + struct sk_buff *skb; + + if ((skb = dev_alloc_skb (len))) + { + skb_put (skb, len); + skb->priority = 0; + +#ifdef BCMDBG_PKT + pktlist_add (&(osh->pktlist), (void *) skb); +#endif /* BCMDBG_PKT */ + + osh->pub.pktalloced++; + } + + return ((void *) skb); +} + +/* Free the driver packet. Free the tag if present */ +void +osl_pktfree (osl_t * osh, void *p, bool send) +{ + struct sk_buff *skb, *nskb; + + skb = (struct sk_buff *) p; + + if (send && osh->pub.tx_fn) + osh->pub.tx_fn (osh->pub.tx_ctx, p, 0); + + /* perversion: we use skb->next to chain multi-skb packets */ + while (skb) + { + nskb = skb->next; + skb->next = NULL; + +#ifdef BCMDBG_PKT + pktlist_remove (&(osh->pktlist), (void *) skb); +#endif /* BCMDBG_PKT */ + + if (skb->destructor) + { + /* cannot kfree_skb() on hard IRQ (net/core/skbuff.c) if destructor exists + */ + dev_kfree_skb_any (skb); + } + else + { + /* can free immediately (even in_irq()) if destructor does not exist */ + dev_kfree_skb (skb); + } + + osh->pub.pktalloced--; + + skb = nskb; + } +} + +uint32 +osl_pci_read_config (osl_t * osh, uint offset, uint size) +{ + uint val; + uint retry = PCI_CFG_RETRY; + + ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC))); + + /* only 4byte access supported */ + ASSERT (size == 4); + + do + { + pci_read_config_dword (osh->pdev, offset, &val); + if (val != 0xffffffff) + break; + } + while (retry--); + +#ifdef BCMDBG + if (retry < PCI_CFG_RETRY) + printk ("PCI CONFIG READ access to %d required %d retries\n", offset, + (PCI_CFG_RETRY - retry)); +#endif /* BCMDBG */ + + return (val); +} + +void +osl_pci_write_config (osl_t * osh, uint offset, uint size, uint val) +{ + uint retry = PCI_CFG_RETRY; + + ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC))); + + /* only 4byte access supported */ + ASSERT (size == 4); + + do + { + pci_write_config_dword (osh->pdev, offset, val); + if (offset != PCI_BAR0_WIN) + break; + if (osl_pci_read_config (osh, offset, size) == val) + break; + } + while (retry--); + +#ifdef BCMDBG + if (retry < PCI_CFG_RETRY) + printk ("PCI CONFIG WRITE access to %d required %d retries\n", offset, + (PCI_CFG_RETRY - retry)); +#endif /* BCMDBG */ +} + +/* return bus # for the pci device pointed by osh->pdev */ +uint +osl_pci_bus (osl_t * osh) +{ + ASSERT (osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev); + + return ((struct pci_dev *) osh->pdev)->bus->number; +} + +/* return slot # for the pci device pointed by osh->pdev */ +uint +osl_pci_slot (osl_t * osh) +{ + ASSERT (osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev); + + return PCI_SLOT (((struct pci_dev *) osh->pdev)->devfn); +} + +static void +osl_pcmcia_attr (osl_t * osh, uint offset, char *buf, int size, bool write) +{ +} + +void +osl_pcmcia_read_attr (osl_t * osh, uint offset, void *buf, int size) +{ + osl_pcmcia_attr (osh, offset, (char *) buf, size, FALSE); +} + +void +osl_pcmcia_write_attr (osl_t * osh, uint offset, void *buf, int size) +{ + osl_pcmcia_attr (osh, offset, (char *) buf, size, TRUE); +} + + +#ifdef BCMDBG_MEM + +void * +osl_debug_malloc (osl_t * osh, uint size, int line, char *file) +{ + bcm_mem_link_t *p; + char *basename; + + ASSERT (size); + + if ((p = + (bcm_mem_link_t *) osl_malloc (osh, + sizeof (bcm_mem_link_t) + size)) == + NULL) + return (NULL); + + p->size = size; + p->line = line; + + basename = strrchr (file, '/'); + /* skip the '/' */ + if (basename) + basename++; + + if (!basename) + basename = file; + + strncpy (p->file, basename, BCM_MEM_FILENAME_LEN); + p->file[BCM_MEM_FILENAME_LEN - 1] = '\0'; + + /* link this block */ + p->prev = NULL; + p->next = osh->dbgmem_list; + if (p->next) + p->next->prev = p; + osh->dbgmem_list = p; + + return p + 1; +} + +void +osl_debug_mfree (osl_t * osh, void *addr, uint size, int line, char *file) +{ + bcm_mem_link_t *p = + (bcm_mem_link_t *) ((int8 *) addr - sizeof (bcm_mem_link_t)); + + ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC))); + + if (p->size == 0) + { + printk + ("osl_debug_mfree: double free on addr %p size %d at line %d file %s\n", + addr, size, line, file); + ASSERT (p->size); + return; + } + + if (p->size != size) + { + printk + ("osl_debug_mfree: dealloc size %d does not match alloc size %d on addr %p" + " at line %d file %s\n", size, p->size, addr, line, file); + ASSERT (p->size == size); + return; + } + + /* unlink this block */ + if (p->prev) + p->prev->next = p->next; + if (p->next) + p->next->prev = p->prev; + if (osh->dbgmem_list == p) + osh->dbgmem_list = p->next; + p->next = p->prev = NULL; + + osl_mfree (osh, p, size + sizeof (bcm_mem_link_t)); +} + +int +osl_debug_memdump (osl_t * osh, struct bcmstrbuf *b) +{ + bcm_mem_link_t *p; + + ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC))); + + bcm_bprintf (b, " Address\tSize\tFile:line\n"); + for (p = osh->dbgmem_list; p; p = p->next) + bcm_bprintf (b, "0x%08x\t%5d\t%s:%d\n", + (uintptr) p + sizeof (bcm_mem_link_t), p->size, p->file, + p->line); + + return 0; +} + +#endif /* BCMDBG_MEM */ + +void * +osl_malloc (osl_t * osh, uint size) +{ + void *addr; + + /* only ASSERT if osh is defined */ + if (osh) + ASSERT (osh->magic == OS_HANDLE_MAGIC); + + if ((addr = kmalloc (size, GFP_ATOMIC)) == NULL) + { + if (osh) + osh->failed++; + return (NULL); + } + if (osh) + osh->malloced += size; + + return (addr); +} + +void +osl_mfree (osl_t * osh, void *addr, uint size) +{ + if (osh) + { + ASSERT (osh->magic == OS_HANDLE_MAGIC); + osh->malloced -= size; + } + kfree (addr); +} + +uint +osl_malloced (osl_t * osh) +{ + ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC))); + return (osh->malloced); +} + +uint +osl_malloc_failed (osl_t * osh) +{ + ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC))); + return (osh->failed); +} + +void * +osl_dma_alloc_consistent (osl_t * osh, uint size, ulong * pap) +{ + ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC))); + + return (pci_alloc_consistent (osh->pdev, size, (dma_addr_t *) pap)); +} + +void +osl_dma_free_consistent (osl_t * osh, void *va, uint size, ulong pa) +{ + ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC))); + + pci_free_consistent (osh->pdev, size, va, (dma_addr_t) pa); +} + +uint +osl_dma_map (osl_t * osh, void *va, uint size, int direction) +{ + int dir; + + ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC))); + dir = (direction == DMA_TX) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE; + return (pci_map_single (osh->pdev, va, size, dir)); +} + +void +osl_dma_unmap (osl_t * osh, uint pa, uint size, int direction) +{ + int dir; + + ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC))); + dir = (direction == DMA_TX) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE; + pci_unmap_single (osh->pdev, (uint32) pa, size, dir); +} + +#if defined(BINOSL) || defined(BCMDBG_ASSERT) +void +osl_assert (char *exp, char *file, int line) +{ + char tempbuf[255]; + + sprintf (tempbuf, "assertion \"%s\" failed: file \"%s\", line %d\n", exp, + file, line); + panic (tempbuf); +} +#endif /* BCMDBG_ASSERT || BINOSL */ + +void +osl_delay (uint usec) +{ + uint d; + + while (usec > 0) + { + d = MIN (usec, 1000); + udelay (d); + usec -= d; + } +} + +/* Clone a packet. + * The pkttag contents are NOT cloned. + */ +void * +osl_pktdup (osl_t * osh, void *skb) +{ + void *p; + + if ((p = skb_clone ((struct sk_buff *) skb, GFP_ATOMIC)) == NULL) + return NULL; + + /* skb_clone copies skb->cb.. we don't want that */ + if (osh->pub.pkttag) + bzero ((void *) ((struct sk_buff *) p)->cb, OSL_PKTTAG_SZ); + + /* Increment the packet counter */ + osh->pub.pktalloced++; +#ifdef BCMDBG_PKT + pktlist_add (&(osh->pktlist), (void *) p); +#endif /* BCMDBG_PKT */ + return (p); +} + +uint +osl_pktalloced (osl_t * osh) +{ + return (osh->pub.pktalloced); +} + +#ifdef BCMDBG_PKT +char * +osl_pktlist_dump (osl_t * osh, char *buf) +{ + pktlist_dump (&(osh->pktlist), buf); + return buf; +} + +void +osl_pktlist_add (osl_t * osh, void *p) +{ + pktlist_add (&(osh->pktlist), p); +} + +void +osl_pktlist_remove (osl_t * osh, void *p) +{ + pktlist_remove (&(osh->pktlist), p); +} +#endif /* BCMDBG_PKT */ + +/* + * BINOSL selects the slightly slower function-call-based binary compatible osl. + */ +#ifdef BINOSL + +int +osl_printf (const char *format, ...) +{ + va_list args; + char buf[1024]; + int len; + + /* sprintf into a local buffer because there *is* no "vprintk()".. */ + va_start (args, format); + len = vsnprintf (buf, 1024, format, args); + va_end (args); + + if (len > sizeof (buf)) + { + printk ("osl_printf: buffer overrun\n"); + return (0); + } + + return (printk (buf)); +} + +int +osl_sprintf (char *buf, const char *format, ...) +{ + va_list args; + int rc; + + va_start (args, format); + rc = vsprintf (buf, format, args); + va_end (args); + return (rc); +} + +int +osl_strcmp (const char *s1, const char *s2) +{ + return (strcmp (s1, s2)); +} + +int +osl_strncmp (const char *s1, const char *s2, uint n) +{ + return (strncmp (s1, s2, n)); +} + +int +osl_strlen (const char *s) +{ + return (strlen (s)); +} + +char * +osl_strcpy (char *d, const char *s) +{ + return (strcpy (d, s)); +} + +char * +osl_strncpy (char *d, const char *s, uint n) +{ + return (strncpy (d, s, n)); +} + +void +bcopy (const void *src, void *dst, int len) +{ + memcpy (dst, src, len); +} + +int +bcmp (const void *b1, const void *b2, int len) +{ + return (memcmp (b1, b2, len)); +} + +void +bzero (void *b, int len) +{ + memset (b, '\0', len); +} + +uint32 +osl_readl (volatile uint32 * r) +{ + return (readl (r)); +} + +uint16 +osl_readw (volatile uint16 * r) +{ + return (readw (r)); +} + +uint8 +osl_readb (volatile uint8 * r) +{ + return (readb (r)); +} + +void +osl_writel (uint32 v, volatile uint32 * r) +{ + writel (v, r); +} + +void +osl_writew (uint16 v, volatile uint16 * r) +{ + writew (v, r); +} + +void +osl_writeb (uint8 v, volatile uint8 * r) +{ + writeb (v, r); +} + +void * +osl_uncached (void *va) +{ +#ifdef mips + return ((void *) KSEG1ADDR (va)); +#else + return ((void *) va); +#endif /* mips */ +} + +uint +osl_getcycles (void) +{ + uint cycles; + +#if defined(mips) + cycles = read_c0_count () * 2; +#elif defined(__i386__) + rdtscl (cycles); +#else + cycles = 0; +#endif /* defined(mips) */ + return cycles; +} + +void * +osl_reg_map (uint32 pa, uint size) +{ + return (ioremap_nocache ((unsigned long) pa, (unsigned long) size)); +} + +void +osl_reg_unmap (void *va) +{ + iounmap (va); +} + +int +osl_busprobe (uint32 * val, uint32 addr) +{ +#ifdef mips + return get_dbe (*val, (uint32 *) addr); +#else + *val = readl ((uint32 *) (uintptr) addr); + return 0; +#endif /* mips */ +} + +bool +osl_pktshared (void *skb) +{ + return (((struct sk_buff *) skb)->cloned); +} + +uchar * +osl_pktdata (osl_t * osh, void *skb) +{ + return (((struct sk_buff *) skb)->data); +} + +uint +osl_pktlen (osl_t * osh, void *skb) +{ + return (((struct sk_buff *) skb)->len); +} + +uint +osl_pktheadroom (osl_t * osh, void *skb) +{ + return (uint) skb_headroom ((struct sk_buff *) skb); +} + +uint +osl_pkttailroom (osl_t * osh, void *skb) +{ + return (uint) skb_tailroom ((struct sk_buff *) skb); +} + +void * +osl_pktnext (osl_t * osh, void *skb) +{ + return (((struct sk_buff *) skb)->next); +} + +void +osl_pktsetnext (void *skb, void *x) +{ + ((struct sk_buff *) skb)->next = (struct sk_buff *) x; +} + +void +osl_pktsetlen (osl_t * osh, void *skb, uint len) +{ + __skb_trim ((struct sk_buff *) skb, len); +} + +uchar * +osl_pktpush (osl_t * osh, void *skb, int bytes) +{ + return (skb_push ((struct sk_buff *) skb, bytes)); +} + +uchar * +osl_pktpull (osl_t * osh, void *skb, int bytes) +{ + return (skb_pull ((struct sk_buff *) skb, bytes)); +} + +void * +osl_pkttag (void *skb) +{ + return ((void *) (((struct sk_buff *) skb)->cb)); +} + +void * +osl_pktlink (void *skb) +{ + return (((struct sk_buff *) skb)->prev); +} + +void +osl_pktsetlink (void *skb, void *x) +{ + ((struct sk_buff *) skb)->prev = (struct sk_buff *) x; +} + +uint +osl_pktprio (void *skb) +{ + return (((struct sk_buff *) skb)->priority); +} + +void +osl_pktsetprio (void *skb, uint x) +{ + ((struct sk_buff *) skb)->priority = x; +} + +/* Convert a driver packet to native(OS) packet + * In the process, packettag is zeroed out before sending up + * IP code depends on skb->cb to be setup correctly with various options + * In our case, that means it should be 0 + */ +struct sk_buff * +osl_pkt_tonative (osl_t * osh, void *pkt) +{ + struct sk_buff *nskb; + + if (osh->pub.pkttag) + bzero ((void *) ((struct sk_buff *) pkt)->cb, OSL_PKTTAG_SZ); + + /* Decrement the packet counter */ + for (nskb = (struct sk_buff *) pkt; nskb; nskb = nskb->next) + { +#ifdef BCMDBG_PKT + pktlist_remove (&(osh->pktlist), (void *) nskb); +#endif /* BCMDBG_PKT */ + osh->pub.pktalloced--; + } + + return (struct sk_buff *) pkt; +} + +/* Convert a native(OS) packet to driver packet. + * In the process, native packet is destroyed, there is no copying + * Also, a packettag is zeroed out + */ +void * +osl_pkt_frmnative (osl_t * osh, struct sk_buff *skb) +{ + struct sk_buff *nskb; + + if (osh->pub.pkttag) + bzero ((void *) skb->cb, OSL_PKTTAG_SZ); + + /* Increment the packet counter */ + for (nskb = skb; nskb; nskb = nskb->next) + { +#ifdef BCMDBG_PKT + pktlist_add (&(osh->pktlist), (void *) nskb); +#endif /* BCMDBG_PKT */ + osh->pub.pktalloced++; + } + + return (void *) skb; +} + +#endif /* BINOSL */ |