| /* |
| * Copyright (c) 2010 Broadcom Corporation |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include <linux/delay.h> |
| #include <linux/fs.h> |
| #ifdef mips |
| #include <asm/paccess.h> |
| #endif /* mips */ |
| #include <bcmendian.h> |
| #include <linux/module.h> |
| #include <linux/pci.h> |
| #include <linux/netdevice.h> |
| #include <linux/sched.h> |
| #include <bcmdefs.h> |
| #include <osl.h> |
| #include <bcmutils.h> |
| #include <pcicfg.h> |
| |
| |
| #define OS_HANDLE_MAGIC 0x1234abcd /* Magic # to recognise osh */ |
| #define BCM_MEM_FILENAME_LEN 24 /* Mem. filename length */ |
| |
| /* Global ASSERT type flag */ |
| u32 g_assert_type; |
| |
| struct osl_info *osl_attach(void *pdev, uint bustype) |
| { |
| struct osl_info *osh; |
| |
| osh = kmalloc(sizeof(struct osl_info), GFP_ATOMIC); |
| ASSERT(osh); |
| |
| memset(osh, 0, sizeof(struct osl_info)); |
| |
| osh->magic = OS_HANDLE_MAGIC; |
| osh->pdev = pdev; |
| osh->bustype = bustype; |
| |
| switch (bustype) { |
| case PCI_BUS: |
| case SI_BUS: |
| case PCMCIA_BUS: |
| osh->mmbus = true; |
| break; |
| case JTAG_BUS: |
| case SDIO_BUS: |
| case USB_BUS: |
| case SPI_BUS: |
| case RPC_BUS: |
| osh->mmbus = false; |
| break; |
| default: |
| ASSERT(false); |
| break; |
| } |
| |
| return osh; |
| } |
| |
| void osl_detach(struct osl_info *osh) |
| { |
| if (osh == NULL) |
| return; |
| |
| ASSERT(osh->magic == OS_HANDLE_MAGIC); |
| kfree(osh); |
| } |
| |
| struct sk_buff *BCMFASTPATH pkt_buf_get_skb(struct osl_info *osh, uint len) |
| { |
| struct sk_buff *skb; |
| |
| skb = dev_alloc_skb(len); |
| if (skb) { |
| skb_put(skb, len); |
| skb->priority = 0; |
| |
| osh->pktalloced++; |
| } |
| |
| return skb; |
| } |
| |
| /* Free the driver packet. Free the tag if present */ |
| void BCMFASTPATH pkt_buf_free_skb(struct osl_info *osh, struct sk_buff *skb, bool send) |
| { |
| struct sk_buff *nskb; |
| int nest = 0; |
| |
| ASSERT(skb); |
| |
| /* perversion: we use skb->next to chain multi-skb packets */ |
| while (skb) { |
| nskb = skb->next; |
| skb->next = NULL; |
| |
| 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->pktalloced--; |
| nest++; |
| skb = nskb; |
| } |
| } |
| |
| /* return bus # for the pci device pointed by osh->pdev */ |
| uint osl_pci_bus(struct osl_info *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(struct osl_info *osh) |
| { |
| ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev); |
| |
| return PCI_SLOT(((struct pci_dev *)osh->pdev)->devfn); |
| } |
| |
| void *osl_dma_alloc_consistent(struct osl_info *osh, uint size, u16 align_bits, |
| uint *alloced, unsigned long *pap) |
| { |
| ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); |
| |
| if (align_bits) { |
| u16 align = (1 << align_bits); |
| if (!IS_ALIGNED(PAGE_SIZE, align)) |
| size += align; |
| *alloced = size; |
| } |
| return pci_alloc_consistent(osh->pdev, size, (dma_addr_t *) pap); |
| } |
| |
| void osl_dma_free_consistent(struct osl_info *osh, void *va, uint size, |
| unsigned long pa) |
| { |
| ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); |
| |
| pci_free_consistent(osh->pdev, size, va, (dma_addr_t) pa); |
| } |
| |
| uint BCMFASTPATH osl_dma_map(struct osl_info *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 BCMFASTPATH osl_dma_unmap(struct osl_info *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, (u32) pa, size, dir); |
| } |
| |
| #if defined(BCMDBG_ASSERT) |
| void osl_assert(char *exp, char *file, int line) |
| { |
| char tempbuf[256]; |
| char *basename; |
| |
| basename = strrchr(file, '/'); |
| /* skip the '/' */ |
| if (basename) |
| basename++; |
| |
| if (!basename) |
| basename = file; |
| |
| #ifdef BCMDBG_ASSERT |
| snprintf(tempbuf, 256, |
| "assertion \"%s\" failed: file \"%s\", line %d\n", exp, |
| basename, line); |
| |
| /* Print assert message and give it time to be written to /var/log/messages */ |
| if (!in_interrupt()) { |
| const int delay = 3; |
| printk(KERN_ERR "%s", tempbuf); |
| printk(KERN_ERR "panic in %d seconds\n", delay); |
| set_current_state(TASK_INTERRUPTIBLE); |
| schedule_timeout(delay * HZ); |
| } |
| |
| switch (g_assert_type) { |
| case 0: |
| panic(KERN_ERR "%s", tempbuf); |
| break; |
| case 1: |
| printk(KERN_ERR "%s", tempbuf); |
| BUG(); |
| break; |
| case 2: |
| printk(KERN_ERR "%s", tempbuf); |
| break; |
| default: |
| break; |
| } |
| #endif /* BCMDBG_ASSERT */ |
| |
| } |
| #endif /* defined(BCMDBG_ASSERT) */ |
| |