| /* |
| ** |
| ** RCpci45.c |
| ** |
| ** |
| ** |
| ** --------------------------------------------------------------------- |
| ** --- Copyright (c) 1998, 1999, RedCreek Communications Inc. --- |
| ** --- All rights reserved. --- |
| ** --------------------------------------------------------------------- |
| ** |
| ** Written by Pete Popov and Brian Moyle. |
| ** |
| ** Known Problems |
| ** |
| ** None known at this time. |
| ** |
| ** This program is free software; you can redistribute it and/or modify |
| ** it under the terms of the GNU General Public License as published by |
| ** the Free Software Foundation; either version 2 of the License, or |
| ** (at your option) any later version. |
| |
| ** This program is distributed in the hope that it will be useful, |
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| ** GNU General Public License for more details. |
| |
| ** You should have received a copy of the GNU General Public License |
| ** along with this program; if not, write to the Free Software |
| ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| ** |
| ** |
| ** Rasmus Andersen, December 2000: Converted to new PCI API and general |
| ** cleanup. |
| ** |
| ** Pete Popov, January 11,99: Fixed a couple of 2.1.x problems |
| ** (virt_to_bus() not called), tested it under 2.2pre5 (as a module), and |
| ** added a #define(s) to enable the use of the same file for both, the 2.0.x |
| ** kernels as well as the 2.1.x. |
| ** |
| ** Ported to 2.1.x by Alan Cox 1998/12/9. |
| ** |
| ** Sometime in mid 1998, written by Pete Popov and Brian Moyle. |
| ** |
| ***************************************************************************/ |
| |
| #error Please convert me to Documentation/DMA-mapping.txt |
| |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/sched.h> |
| #include <linux/string.h> |
| #include <linux/ptrace.h> |
| #include <linux/errno.h> |
| #include <linux/in.h> |
| #include <linux/init.h> |
| #include <linux/ioport.h> |
| #include <linux/slab.h> |
| #include <linux/interrupt.h> |
| #include <linux/pci.h> |
| #include <linux/timer.h> |
| #include <asm/irq.h> /* For NR_IRQS only. */ |
| #include <asm/bitops.h> |
| #include <asm/uaccess.h> |
| |
| static char version[] __initdata = |
| "RedCreek Communications PCI linux driver version 2.03\n"; |
| |
| #define RC_LINUX_MODULE |
| #include "rclanmtl.h" |
| #include "rcif.h" |
| |
| #define RUN_AT(x) (jiffies + (x)) |
| |
| #define NEW_MULTICAST |
| |
| /* PCI/45 Configuration space values */ |
| #define RC_PCI45_VENDOR_ID 0x4916 |
| #define RC_PCI45_DEVICE_ID 0x1960 |
| |
| #define MAX_ETHER_SIZE 1520 |
| #define MAX_NMBR_RCV_BUFFERS 96 |
| #define RC_POSTED_BUFFERS_LOW_MARK MAX_NMBR_RCV_BUFFERS-16 |
| #define BD_SIZE 3 /* Bucket Descriptor size */ |
| #define BD_LEN_OFFSET 2 /* Bucket Descriptor offset to length field */ |
| |
| /* RedCreek LAN device Target ID */ |
| #define RC_LAN_TARGET_ID 0x10 |
| /* RedCreek's OSM default LAN receive Initiator */ |
| #define DEFAULT_RECV_INIT_CONTEXT 0xA17 |
| |
| static U32 DriverControlWord; |
| |
| static void rc_timer (unsigned long); |
| |
| static int RCinit (struct net_device *); |
| |
| static int RCopen (struct net_device *); |
| static int RC_xmit_packet (struct sk_buff *, struct net_device *); |
| static void RCinterrupt (int, void *, struct pt_regs *); |
| static int RCclose (struct net_device *dev); |
| static struct net_device_stats *RCget_stats (struct net_device *); |
| static int RCioctl (struct net_device *, struct ifreq *, int); |
| static int RCconfig (struct net_device *, struct ifmap *); |
| static void RCxmit_callback (U32, U16, PU32, struct net_device *); |
| static void RCrecv_callback (U32, U8, U32, PU32, struct net_device *); |
| static void RCreset_callback (U32, U32, U32, struct net_device *); |
| static void RCreboot_callback (U32, U32, U32, struct net_device *); |
| static int RC_allocate_and_post_buffers (struct net_device *, int); |
| |
| static struct pci_device_id rcpci45_pci_table[] __devinitdata = { |
| {RC_PCI45_VENDOR_ID, RC_PCI45_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, |
| {} |
| }; |
| MODULE_DEVICE_TABLE (pci, rcpci45_pci_table); |
| MODULE_LICENSE("GPL"); |
| |
| static void __exit |
| rcpci45_remove_one (struct pci_dev *pdev) |
| { |
| struct net_device *dev = pci_get_drvdata (pdev); |
| PDPA pDpa = dev->priv; |
| |
| if (!dev) { |
| printk (KERN_ERR |
| "(rcpci45 driver:) remove non-existent device\n"); |
| return; |
| } |
| |
| dprintk ("remove_one: IOP reset: 0x%x\n", RCResetIOP (dev)); |
| |
| /* RAA Inspired by starfire.c and yellowfin.c we keep these |
| * here. */ |
| unregister_netdev (dev); |
| free_irq (dev->irq, dev); |
| iounmap ((void *) dev->base_addr); |
| pci_release_regions (pdev); |
| kfree (pDpa->PLanApiPA); |
| kfree (pDpa->pPab); |
| kfree (pDpa); |
| kfree (dev); |
| pci_set_drvdata (pdev, NULL); |
| } |
| |
| static int |
| RCinit (struct net_device *dev) |
| { |
| dev->open = &RCopen; |
| dev->hard_start_xmit = &RC_xmit_packet; |
| dev->stop = &RCclose; |
| dev->get_stats = &RCget_stats; |
| dev->do_ioctl = &RCioctl; |
| dev->set_config = &RCconfig; |
| return 0; |
| } |
| |
| static int |
| rcpci45_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) |
| { |
| unsigned long *vaddr; |
| PDPA pDpa; |
| int error; |
| static int card_idx = -1; |
| struct net_device *dev; |
| unsigned long pci_start, pci_len; |
| |
| card_idx++; |
| |
| /* |
| * Allocate and fill new device structure. |
| * We need enough for struct net_device plus DPA plus the LAN API private |
| * area, which requires a minimum of 16KB. The top of the allocated |
| * area will be assigned to struct net_device; the next chunk will be |
| * assigned to DPA; and finally, the rest will be assigned to the |
| * the LAN API layer. |
| */ |
| |
| dev = init_etherdev (NULL, sizeof (*pDpa)); |
| if (!dev) { |
| printk (KERN_ERR |
| "(rcpci45 driver:) unable to allocate in init_etherdev\n"); |
| error = -ENOMEM; |
| goto err_out; |
| } |
| |
| error = pci_enable_device (pdev); |
| if (error) { |
| printk (KERN_ERR |
| "(rcpci45 driver:) %d: unable to enable pci device, aborting\n", |
| card_idx); |
| goto err_out; |
| } |
| error = -ENOMEM; |
| pci_start = pci_resource_start (pdev, 0); |
| pci_len = pci_resource_len (pdev, 0); |
| |
| pci_set_drvdata (pdev, dev); |
| |
| pDpa = dev->priv; |
| pDpa->id = card_idx; |
| pDpa->pci_addr = pci_start; |
| |
| if (!pci_start || !(pci_resource_flags (pdev, 0) & IORESOURCE_MEM)) { |
| printk (KERN_ERR |
| "(rcpci45 driver:) No PCI memory resources! Aborting.\n"); |
| error = -EBUSY; |
| goto err_out_free_dev; |
| } |
| |
| /* |
| * Save the starting address of the LAN API private area. We'll |
| * pass that to RCInitI2OMsgLayer(). |
| */ |
| /* RAA FIXME: This size should be a #define somewhere after I |
| * clear up some questions: What flags are neeeded in the alloc below |
| * and what needs to be done before the memarea is long word aligned? |
| * (Look in old code for an approach.) (Also note that the 16K below |
| * is substantially less than the 32K allocated before (even though |
| * some of the spacce was used for data structures.) */ |
| pDpa->msgbuf = kmalloc (16384, GFP_KERNEL); |
| if (!pDpa->msgbuf) { |
| printk (KERN_ERR "(rcpci45 driver:) Could not allocate %d byte memory for the private msgbuf!\n", 16384); /* RAA FIXME not hardcoded! */ |
| goto err_out_free_dev; |
| } |
| pDpa->PLanApiPA = (void *) (((long) pDpa->msgbuf + 0xff) & ~0xff); |
| |
| dprintk ("pDpa->PLanApiPA = 0x%x\n", (uint) pDpa->PLanApiPA); |
| |
| /* The adapter is accessible through memory-access read/write, not |
| * I/O read/write. Thus, we need to map it to some virtual address |
| * area in order to access the registers as normal memory. |
| */ |
| error = pci_request_regions (pdev, dev->name); |
| if (error) |
| goto err_out_free_msgbuf; |
| |
| vaddr = (ulong *) ioremap (pci_start, pci_len); |
| if (!vaddr) { |
| printk (KERN_ERR |
| "(rcpci45 driver:) Unable to remap address range from %lu to %lu\n", |
| pci_start, pci_start + pci_len); |
| goto err_out_free_region; |
| } |
| |
| dprintk ("rcpci45_init_one: 0x%x, priv = 0x%x, vaddr = 0x%x\n", |
| (uint) dev, (uint) dev->priv, (uint) vaddr); |
| dev->base_addr = (unsigned long) vaddr; |
| dev->irq = pdev->irq; |
| |
| dev->init = &RCinit; |
| |
| return 0; /* success */ |
| |
| err_out_free_region: |
| pci_release_regions (pdev); |
| err_out_free_msgbuf: |
| kfree (pDpa->msgbuf); |
| err_out_free_dev: |
| unregister_netdev (dev); |
| kfree (dev); |
| err_out: |
| card_idx--; |
| return error; |
| } |
| |
| static struct pci_driver rcpci45_driver = { |
| name: "rcpci45", |
| id_table: rcpci45_pci_table, |
| probe: rcpci45_init_one, |
| remove: rcpci45_remove_one, |
| }; |
| |
| static int __init |
| rcpci_init_module (void) |
| { |
| int rc = pci_module_init (&rcpci45_driver); |
| |
| if (!rc) |
| printk (KERN_INFO "%s", version); |
| return rc; |
| } |
| |
| static int |
| RCopen (struct net_device *dev) |
| { |
| int post_buffers = MAX_NMBR_RCV_BUFFERS; |
| PDPA pDpa = dev->priv; |
| int count = 0; |
| int requested = 0; |
| int error; |
| |
| dprintk ("(rcpci45 driver:) RCopen\n"); |
| |
| /* Request a shared interrupt line. */ |
| error = request_irq (dev->irq, RCinterrupt, SA_SHIRQ, dev->name, dev); |
| if (error) { |
| printk (KERN_ERR "(rcpci45 driver:) %s: unable to get IRQ %d\n", |
| dev->name, dev->irq); |
| goto err_out; |
| } |
| |
| error = RCInitI2OMsgLayer (dev, (PFNTXCALLBACK) RCxmit_callback, |
| (PFNRXCALLBACK) RCrecv_callback, |
| (PFNCALLBACK) RCreboot_callback); |
| if (error) { |
| printk (KERN_ERR |
| "(rcpci45 driver:) Unable to initialize msg layer\n"); |
| goto err_out_free_irq; |
| } |
| if ((error = RCGetMAC (dev, NULL))) { |
| printk (KERN_ERR |
| "(rcpci45 driver:) Unable to get adapter MAC\n"); |
| goto err_out_free_irq; |
| } |
| |
| DriverControlWord |= WARM_REBOOT_CAPABLE; |
| RCReportDriverCapability (dev, DriverControlWord); |
| |
| printk (KERN_INFO "%s: RedCreek Communications IPSEC VPN adapter\n", |
| dev->name); |
| |
| /* RAA: Old RCopen starts here */ |
| RCEnableI2OInterrupts (dev); |
| |
| /* RAA Hmm, how does the comment below jibe with the newly imported |
| * code above? A FIXME!!*/ |
| if (pDpa->nexus) { |
| /* This is not the first time RCopen is called. Thus, |
| * the interface was previously opened and later closed |
| * by RCclose(). RCclose() does a Shutdown; to wake up |
| * the adapter, a reset is mandatory before we can post |
| * receive buffers. However, if the adapter initiated |
| * a reboot while the interface was closed -- and interrupts |
| * were turned off -- we need will need to reinitialize |
| * the adapter, rather than simply waking it up. |
| */ |
| dprintk (KERN_INFO "Waking up adapter...\n"); |
| RCResetLANCard (dev, 0, 0, 0); |
| } else |
| pDpa->nexus = 1; |
| |
| while (post_buffers) { |
| if (post_buffers > MAX_NMBR_POST_BUFFERS_PER_MSG) |
| requested = MAX_NMBR_POST_BUFFERS_PER_MSG; |
| else |
| requested = post_buffers; |
| count = RC_allocate_and_post_buffers (dev, requested); |
| |
| if (count < requested) { |
| /* |
| * Check to see if we were able to post any buffers at all. |
| */ |
| if (post_buffers == MAX_NMBR_RCV_BUFFERS) { |
| printk (KERN_ERR |
| "(rcpci45 driver:) Error RCopen: not able to allocate any buffers\r\n"); |
| return (-ENOMEM); |
| } |
| printk (KERN_WARNING |
| "(rcpci45 driver:) Warning RCopen: not able to allocate all requested buffers\r\n"); |
| break; /* we'll try to post more buffers later */ |
| } else |
| post_buffers -= count; |
| } |
| pDpa->numOutRcvBuffers = MAX_NMBR_RCV_BUFFERS - post_buffers; |
| pDpa->shutdown = 0; /* just in case */ |
| dprintk ("RCopen: posted %d buffers\n", (uint) pDpa->numOutRcvBuffers); |
| MOD_INC_USE_COUNT; |
| netif_start_queue (dev); |
| return 0; |
| |
| err_out_free_irq: |
| free_irq (dev->irq, dev); |
| err_out: |
| return error; |
| } |
| |
| static int |
| RC_xmit_packet (struct sk_buff *skb, struct net_device *dev) |
| { |
| |
| PDPA pDpa = dev->priv; |
| singleTCB tcb; |
| psingleTCB ptcb = &tcb; |
| RC_RETURN status = 0; |
| |
| netif_stop_queue (dev); |
| |
| if (pDpa->shutdown || pDpa->reboot) { |
| dprintk ("RC_xmit_packet: tbusy!\n"); |
| return 1; |
| } |
| |
| /* |
| * The user is free to reuse the TCB after RCI2OSendPacket() returns, since |
| * the function copies the necessary info into its own private space. Thus, |
| * our TCB can be a local structure. The skb, on the other hand, will be |
| * freed up in our interrupt handler. |
| */ |
| |
| ptcb->bcount = 1; |
| |
| /* |
| * we'll get the context when the adapter interrupts us to tell us that |
| * the transmission is done. At that time, we can free skb. |
| */ |
| ptcb->b.context = (U32) skb; |
| ptcb->b.scount = 1; |
| ptcb->b.size = skb->len; |
| ptcb->b.addr = virt_to_bus ((void *) skb->data); |
| |
| dprintk ("RC xmit: skb = 0x%x, pDpa = 0x%x, id = %d, ptcb = 0x%x\n", |
| (uint) skb, (uint) pDpa, (uint) pDpa->id, (uint) ptcb); |
| if ((status = RCI2OSendPacket (dev, (U32) NULL, (PRCTCB) ptcb)) |
| != RC_RTN_NO_ERROR) { |
| dprintk ("RC send error 0x%x\n", (uint) status); |
| return 1; |
| } else { |
| dev->trans_start = jiffies; |
| netif_wake_queue (dev); |
| } |
| /* |
| * That's it! |
| */ |
| return 0; |
| } |
| |
| /* |
| * RCxmit_callback() |
| * |
| * The transmit callback routine. It's called by RCProcI2OMsgQ() |
| * because the adapter is done with one or more transmit buffers and |
| * it's returning them to us, or we asked the adapter to return the |
| * outstanding transmit buffers by calling RCResetLANCard() with |
| * RC_RESOURCE_RETURN_PEND_TX_BUFFERS flag. |
| * All we need to do is free the buffers. |
| */ |
| static void |
| RCxmit_callback (U32 Status, |
| U16 PcktCount, PU32 BufferContext, struct net_device *dev) |
| { |
| struct sk_buff *skb; |
| PDPA pDpa = dev->priv; |
| |
| if (!pDpa) { |
| printk (KERN_ERR |
| "(rcpci45 driver:) Fatal error: xmit callback, !pDpa\n"); |
| return; |
| } |
| |
| /* dprintk("xmit_callback: Status = 0x%x\n", (uint)Status); */ |
| if (Status != I2O_REPLY_STATUS_SUCCESS) |
| dprintk ("xmit_callback: Status = 0x%x\n", (uint) Status); |
| if (pDpa->shutdown || pDpa->reboot) |
| dprintk ("xmit callback: shutdown||reboot\n"); |
| |
| dprintk ("xmit_callback: PcktCount = %d, BC = 0x%x\n", |
| (uint) PcktCount, (uint) BufferContext); |
| |
| while (PcktCount--) { |
| skb = (struct sk_buff *) (BufferContext[0]); |
| dprintk ("skb = 0x%x\n", (uint) skb); |
| BufferContext++; |
| dev_kfree_skb_irq (skb); |
| } |
| netif_wake_queue (dev); |
| } |
| |
| static void |
| RCreset_callback (U32 Status, U32 p1, U32 p2, struct net_device *dev) |
| { |
| PDPA pDpa = dev->priv; |
| |
| dprintk ("RCreset_callback Status 0x%x\n", (uint) Status); |
| /* |
| * Check to see why we were called. |
| */ |
| if (pDpa->shutdown) { |
| printk (KERN_INFO |
| "(rcpci45 driver:) Shutting down interface\n"); |
| pDpa->shutdown = 0; |
| pDpa->reboot = 0; |
| MOD_DEC_USE_COUNT; |
| } else if (pDpa->reboot) { |
| printk (KERN_INFO |
| "(rcpci45 driver:) reboot, shutdown adapter\n"); |
| /* |
| * We don't set any of the flags in RCShutdownLANCard() |
| * and we don't pass a callback routine to it. |
| * The adapter will have already initiated the reboot by |
| * the time the function returns. |
| */ |
| RCDisableI2OInterrupts (dev); |
| RCShutdownLANCard (dev, 0, 0, 0); |
| printk (KERN_INFO "(rcpci45 driver:) scheduling timer...\n"); |
| init_timer (&pDpa->timer); |
| pDpa->timer.expires = RUN_AT ((40 * HZ) / 10); /* 4 sec. */ |
| pDpa->timer.data = (unsigned long) dev; |
| pDpa->timer.function = &rc_timer; /* timer handler */ |
| add_timer (&pDpa->timer); |
| } |
| } |
| |
| static void |
| RCreboot_callback (U32 Status, U32 p1, U32 p2, struct net_device *dev) |
| { |
| PDPA pDpa = dev->priv; |
| |
| dprintk ("RCreboot: rcv buffers outstanding = %d\n", |
| (uint) pDpa->numOutRcvBuffers); |
| |
| if (pDpa->shutdown) { |
| printk (KERN_INFO |
| "(rcpci45 driver:) skipping reboot sequence -- shutdown already initiated\n"); |
| return; |
| } |
| pDpa->reboot = 1; |
| /* |
| * OK, we reset the adapter and ask it to return all |
| * outstanding transmit buffers as well as the posted |
| * receive buffers. When the adapter is done returning |
| * those buffers, it will call our RCreset_callback() |
| * routine. In that routine, we'll call RCShutdownLANCard() |
| * to tell the adapter that it's OK to start the reboot and |
| * schedule a timer callback routine to execute 3 seconds |
| * later; this routine will reinitialize the adapter at that time. |
| */ |
| RCResetLANCard (dev, RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | |
| RC_RESOURCE_RETURN_PEND_TX_BUFFERS, 0, |
| (PFNCALLBACK) RCreset_callback); |
| } |
| |
| int |
| broadcast_packet (unsigned char *address) |
| { |
| int i; |
| for (i = 0; i < 6; i++) |
| if (address[i] != 0xff) |
| return 0; |
| |
| return 1; |
| } |
| |
| /* |
| * RCrecv_callback() |
| * |
| * The receive packet callback routine. This is called by |
| * RCProcI2OMsgQ() after the adapter posts buffers which have been |
| * filled (one ethernet packet per buffer). |
| */ |
| static void |
| RCrecv_callback (U32 Status, |
| U8 PktCount, |
| U32 BucketsRemain, |
| PU32 PacketDescBlock, struct net_device *dev) |
| { |
| |
| U32 len, count; |
| PDPA pDpa = dev->priv; |
| struct sk_buff *skb; |
| singleTCB tcb; |
| psingleTCB ptcb = &tcb; |
| |
| ptcb->bcount = 1; |
| |
| dprintk ("RCrecv_callback: 0x%x, 0x%x, 0x%x\n", |
| (uint) PktCount, (uint) BucketsRemain, (uint) PacketDescBlock); |
| |
| if ((pDpa->shutdown || pDpa->reboot) && !Status) |
| dprintk ("shutdown||reboot && !Status: PktCount = %d\n", |
| PktCount); |
| |
| if ((Status != I2O_REPLY_STATUS_SUCCESS) || pDpa->shutdown) { |
| /* |
| * Free whatever buffers the adapter returned, but don't |
| * pass them to the kernel. |
| */ |
| |
| if (!pDpa->shutdown && !pDpa->reboot) |
| printk (KERN_INFO |
| "(rcpci45 driver:) RCrecv error: status = 0x%x\n", |
| (uint) Status); |
| else |
| dprintk ("Returning %d buffers, status = 0x%x\n", |
| PktCount, (uint) Status); |
| /* |
| * TO DO: check the nature of the failure and put the adapter in |
| * failed mode if it's a hard failure. Send a reset to the adapter |
| * and free all outstanding memory. |
| */ |
| if (Status == I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER) |
| dprintk ("RCrecv status ABORT NO DATA TRANSFER\n"); |
| |
| /* check for reset status: I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER */ |
| if (PacketDescBlock) { |
| while (PktCount--) { |
| skb = (struct sk_buff *) PacketDescBlock[0]; |
| dprintk ("free skb 0x%p\n", skb); |
| dev_kfree_skb (skb); |
| pDpa->numOutRcvBuffers--; |
| PacketDescBlock += BD_SIZE; /* point to next context field */ |
| } |
| } |
| return; |
| } else { |
| while (PktCount--) { |
| skb = (struct sk_buff *) PacketDescBlock[0]; |
| if (pDpa->shutdown) |
| dprintk ("shutdown: skb=0x%x\n", (uint) skb); |
| |
| dprintk ("skb = 0x%x: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", |
| (uint) skb, (uint) skb->data[0], |
| (uint) skb->data[1], (uint) skb->data[2], |
| (uint) skb->data[3], (uint) skb->data[4], |
| (uint) skb->data[5]); |
| |
| #ifdef PROMISCUOUS_BY_DEFAULT /* early 2.x firmware */ |
| if ((memcmp (dev->dev_addr, skb->data, 6)) && |
| (!broadcast_packet (skb->data))) { |
| /* |
| * Re-post the buffer to the adapter. Since the adapter usually |
| * return 1 to 2 receive buffers at a time, it's not too inefficient |
| * post one buffer at a time but ... may be that should be |
| * optimized at some point. |
| */ |
| ptcb->b.context = (U32) skb; |
| ptcb->b.scount = 1; |
| ptcb->b.size = MAX_ETHER_SIZE; |
| ptcb->b.addr = virt_to_bus ((void *) skb->data); |
| |
| if (RCPostRecvBuffers (dev, (PRCTCB) ptcb) != |
| RC_RTN_NO_ERROR) { |
| printk (KERN_WARNING |
| "(rcpci45 driver:) RCrecv_callback: post buffer failed!\n"); |
| dev_kfree_skb (skb); |
| } else |
| pDpa->numOutRcvBuffers++; |
| } else |
| #endif /* PROMISCUOUS_BY_DEFAULT */ |
| { |
| len = PacketDescBlock[2]; |
| skb->dev = dev; |
| skb_put (skb, len); /* adjust length and tail */ |
| skb->protocol = eth_type_trans (skb, dev); |
| netif_rx (skb); /* send the packet to the kernel */ |
| dev->last_rx = jiffies; |
| } |
| pDpa->numOutRcvBuffers--; |
| PacketDescBlock += BD_SIZE; /* point to next context field */ |
| } |
| } |
| |
| /* |
| * Replenish the posted receive buffers. |
| * DO NOT replenish buffers if the driver has already |
| * initiated a reboot or shutdown! |
| */ |
| |
| if (!pDpa->shutdown && !pDpa->reboot) { |
| count = RC_allocate_and_post_buffers (dev, |
| MAX_NMBR_RCV_BUFFERS - |
| pDpa->numOutRcvBuffers); |
| pDpa->numOutRcvBuffers += count; |
| } |
| |
| } |
| |
| /* |
| * RCinterrupt() |
| * |
| * Interrupt handler. |
| * This routine sets up a couple of pointers and calls |
| * RCProcI2OMsgQ(), which in turn process the message and |
| * calls one of our callback functions. |
| */ |
| static void |
| RCinterrupt (int irq, void *dev_id, struct pt_regs *regs) |
| { |
| |
| PDPA pDpa; |
| struct net_device *dev = dev_id; |
| |
| pDpa = dev->priv; |
| |
| if (pDpa->shutdown) |
| dprintk ("shutdown: service irq\n"); |
| |
| dprintk ("RC irq: pDpa = 0x%x, dev = 0x%x, id = %d\n", |
| (uint) pDpa, (uint) dev, (uint) pDpa->id); |
| dprintk ("dev = 0x%x\n", (uint) dev); |
| |
| RCProcI2OMsgQ (dev); |
| } |
| |
| #define REBOOT_REINIT_RETRY_LIMIT 4 |
| static void |
| rc_timer (unsigned long data) |
| { |
| struct net_device *dev = (struct net_device *) data; |
| PDPA pDpa = dev->priv; |
| int init_status; |
| static int retry; |
| int post_buffers = MAX_NMBR_RCV_BUFFERS; |
| int count = 0; |
| int requested = 0; |
| |
| if (pDpa->reboot) { |
| init_status = |
| RCInitI2OMsgLayer (dev, (PFNTXCALLBACK) RCxmit_callback, |
| (PFNRXCALLBACK) RCrecv_callback, |
| (PFNCALLBACK) RCreboot_callback); |
| |
| switch (init_status) { |
| case RC_RTN_NO_ERROR: |
| |
| pDpa->reboot = 0; |
| pDpa->shutdown = 0; /* just in case */ |
| RCReportDriverCapability (dev, DriverControlWord); |
| RCEnableI2OInterrupts (dev); |
| |
| if (dev->flags & IFF_UP) { |
| while (post_buffers) { |
| if (post_buffers > |
| MAX_NMBR_POST_BUFFERS_PER_MSG) |
| requested = |
| MAX_NMBR_POST_BUFFERS_PER_MSG; |
| else |
| requested = post_buffers; |
| count = |
| RC_allocate_and_post_buffers (dev, |
| requested); |
| post_buffers -= count; |
| if (count < requested) |
| break; |
| } |
| pDpa->numOutRcvBuffers = |
| MAX_NMBR_RCV_BUFFERS - post_buffers; |
| dprintk ("rc: posted %d buffers \r\n", |
| (uint) pDpa->numOutRcvBuffers); |
| } |
| dprintk ("Initialization done.\n"); |
| netif_wake_queue (dev); |
| retry = 0; |
| return; |
| case RC_RTN_FREE_Q_EMPTY: |
| retry++; |
| printk (KERN_WARNING |
| "(rcpci45 driver:) inbound free q empty\n"); |
| break; |
| default: |
| retry++; |
| printk (KERN_WARNING |
| "(rcpci45 driver:) bad status after reboot: %d\n", |
| init_status); |
| break; |
| } |
| |
| if (retry > REBOOT_REINIT_RETRY_LIMIT) { |
| printk (KERN_WARNING |
| "(rcpci45 driver:) unable to reinitialize adapter after reboot\n"); |
| printk (KERN_WARNING |
| "(rcpci45 driver:) decrementing driver and closing interface\n"); |
| RCDisableI2OInterrupts (dev); |
| dev->flags &= ~IFF_UP; |
| MOD_DEC_USE_COUNT; |
| } else { |
| printk (KERN_INFO |
| "(rcpci45 driver:) rescheduling timer...\n"); |
| init_timer (&pDpa->timer); |
| pDpa->timer.expires = RUN_AT ((40 * HZ) / 10); /* 3 sec. */ |
| pDpa->timer.data = (unsigned long) dev; |
| pDpa->timer.function = &rc_timer; /* timer handler */ |
| add_timer (&pDpa->timer); |
| } |
| } else |
| printk (KERN_WARNING "(rcpci45 driver:) timer??\n"); |
| } |
| |
| static int |
| RCclose (struct net_device *dev) |
| { |
| PDPA pDpa = dev->priv; |
| |
| netif_stop_queue (dev); |
| |
| dprintk ("RCclose\r\n"); |
| |
| if (pDpa->reboot) { |
| printk (KERN_INFO |
| "(rcpci45 driver:) skipping reset -- adapter already in reboot mode\n"); |
| dev->flags &= ~IFF_UP; |
| pDpa->shutdown = 1; |
| return 0; |
| } |
| dprintk ("receive buffers outstanding: %d\n", |
| (uint) pDpa->numOutRcvBuffers); |
| |
| pDpa->shutdown = 1; |
| |
| /* |
| * We can't allow the driver to be unloaded until the adapter returns |
| * all posted receive buffers. It doesn't hurt to tell the adapter |
| * to return all posted receive buffers and outstanding xmit buffers, |
| * even if there are none. |
| */ |
| |
| RCShutdownLANCard (dev, RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | |
| RC_RESOURCE_RETURN_PEND_TX_BUFFERS, 0, |
| (PFNCALLBACK) RCreset_callback); |
| |
| dev->flags &= ~IFF_UP; |
| return 0; |
| } |
| |
| static struct net_device_stats * |
| RCget_stats (struct net_device *dev) |
| { |
| RCLINKSTATS RCstats; |
| |
| PDPA pDpa = dev->priv; |
| |
| if (!pDpa) { |
| dprintk ("RCget_stats: !pDpa\n"); |
| return 0; |
| } else if (!(dev->flags & IFF_UP)) { |
| dprintk ("RCget_stats: device down\n"); |
| return 0; |
| } |
| |
| memset (&RCstats, 0, sizeof (RCLINKSTATS)); |
| if ((RCGetLinkStatistics (dev, &RCstats, (void *) 0)) == |
| RC_RTN_NO_ERROR) { |
| dprintk ("TX_good 0x%x\n", (uint) RCstats.TX_good); |
| dprintk ("TX_maxcol 0x%x\n", (uint) RCstats.TX_maxcol); |
| dprintk ("TX_latecol 0x%x\n", (uint) RCstats.TX_latecol); |
| dprintk ("TX_urun 0x%x\n", (uint) RCstats.TX_urun); |
| dprintk ("TX_crs 0x%x\n", (uint) RCstats.TX_crs); |
| dprintk ("TX_def 0x%x\n", (uint) RCstats.TX_def); |
| dprintk ("TX_singlecol 0x%x\n", (uint) RCstats.TX_singlecol); |
| dprintk ("TX_multcol 0x%x\n", (uint) RCstats.TX_multcol); |
| dprintk ("TX_totcol 0x%x\n", (uint) RCstats.TX_totcol); |
| |
| dprintk ("Rcv_good 0x%x\n", (uint) RCstats.Rcv_good); |
| dprintk ("Rcv_CRCerr 0x%x\n", (uint) RCstats.Rcv_CRCerr); |
| dprintk ("Rcv_alignerr 0x%x\n", (uint) RCstats.Rcv_alignerr); |
| dprintk ("Rcv_reserr 0x%x\n", (uint) RCstats.Rcv_reserr); |
| dprintk ("Rcv_orun 0x%x\n", (uint) RCstats.Rcv_orun); |
| dprintk ("Rcv_cdt 0x%x\n", (uint) RCstats.Rcv_cdt); |
| dprintk ("Rcv_runt 0x%x\n", (uint) RCstats.Rcv_runt); |
| |
| pDpa->stats.rx_packets = RCstats.Rcv_good; /* total packets received */ |
| pDpa->stats.tx_packets = RCstats.TX_good; /* total packets transmitted */ |
| |
| pDpa->stats.rx_errors = RCstats.Rcv_CRCerr + RCstats.Rcv_alignerr + RCstats.Rcv_reserr + RCstats.Rcv_orun + RCstats.Rcv_cdt + RCstats.Rcv_runt; /* bad packets received */ |
| |
| pDpa->stats.tx_errors = RCstats.TX_urun + RCstats.TX_crs + RCstats.TX_def + RCstats.TX_totcol; /* packet transmit problems */ |
| |
| /* |
| * This needs improvement. |
| */ |
| pDpa->stats.rx_dropped = 0; /* no space in linux buffers */ |
| pDpa->stats.tx_dropped = 0; /* no space available in linux */ |
| pDpa->stats.multicast = 0; /* multicast packets received */ |
| pDpa->stats.collisions = RCstats.TX_totcol; |
| |
| /* detailed rx_errors: */ |
| pDpa->stats.rx_length_errors = 0; |
| pDpa->stats.rx_over_errors = RCstats.Rcv_orun; /* receiver ring buff overflow */ |
| pDpa->stats.rx_crc_errors = RCstats.Rcv_CRCerr; /* recved pkt with crc error */ |
| pDpa->stats.rx_frame_errors = 0; /* recv'd frame alignment error */ |
| pDpa->stats.rx_fifo_errors = 0; /* recv'r fifo overrun */ |
| pDpa->stats.rx_missed_errors = 0; /* receiver missed packet */ |
| |
| /* detailed tx_errors */ |
| pDpa->stats.tx_aborted_errors = 0; |
| pDpa->stats.tx_carrier_errors = 0; |
| pDpa->stats.tx_fifo_errors = 0; |
| pDpa->stats.tx_heartbeat_errors = 0; |
| pDpa->stats.tx_window_errors = 0; |
| |
| return ((struct net_device_stats *) &(pDpa->stats)); |
| } |
| return 0; |
| } |
| |
| static int |
| RCioctl (struct net_device *dev, struct ifreq *rq, int cmd) |
| { |
| RCuser_struct RCuser; |
| PDPA pDpa = dev->priv; |
| |
| dprintk ("RCioctl: cmd = 0x%x\n", cmd); |
| |
| if (!capable (CAP_NET_ADMIN)) |
| return -EPERM; |
| |
| switch (cmd) { |
| |
| case RCU_PROTOCOL_REV: |
| /* |
| * Assign user protocol revision, to tell user-level |
| * controller program whether or not it's in sync. |
| */ |
| rq->ifr_ifru.ifru_data = (caddr_t) USER_PROTOCOL_REV; |
| break; |
| |
| case RCU_COMMAND: |
| { |
| if (copy_from_user |
| (&RCuser, rq->ifr_data, sizeof (RCuser))) |
| return -EFAULT; |
| |
| dprintk ("RCioctl: RCuser_cmd = 0x%x\n", RCuser.cmd); |
| |
| switch (RCuser.cmd) { |
| case RCUC_GETFWVER: |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC GETFWVER\n"); |
| RCUD_GETFWVER = &RCuser.RCUS_GETFWVER; |
| RCGetFirmwareVer (dev, |
| (PU8) & RCUD_GETFWVER-> |
| FirmString, NULL); |
| break; |
| case RCUC_GETINFO: |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC GETINFO\n"); |
| RCUD_GETINFO = &RCuser.RCUS_GETINFO; |
| RCUD_GETINFO->mem_start = dev->base_addr; |
| RCUD_GETINFO->mem_end = |
| dev->base_addr + pDpa->pci_addr_len; |
| RCUD_GETINFO->base_addr = pDpa->pci_addr; |
| RCUD_GETINFO->irq = dev->irq; |
| break; |
| case RCUC_GETIPANDMASK: |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC GETIPANDMASK\n"); |
| RCUD_GETIPANDMASK = &RCuser.RCUS_GETIPANDMASK; |
| RCGetRavlinIPandMask (dev, |
| (PU32) & |
| RCUD_GETIPANDMASK->IpAddr, |
| (PU32) & |
| RCUD_GETIPANDMASK-> |
| NetMask, NULL); |
| break; |
| case RCUC_GETLINKSTATISTICS: |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC GETLINKSTATISTICS\n"); |
| RCUD_GETLINKSTATISTICS = |
| &RCuser.RCUS_GETLINKSTATISTICS; |
| RCGetLinkStatistics (dev, |
| (P_RCLINKSTATS) & |
| RCUD_GETLINKSTATISTICS-> |
| StatsReturn, NULL); |
| break; |
| case RCUC_GETLINKSTATUS: |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC GETLINKSTATUS\n"); |
| RCUD_GETLINKSTATUS = &RCuser.RCUS_GETLINKSTATUS; |
| RCGetLinkStatus (dev, |
| (PU32) & RCUD_GETLINKSTATUS-> |
| ReturnStatus, NULL); |
| break; |
| case RCUC_GETMAC: |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC GETMAC\n"); |
| RCUD_GETMAC = &RCuser.RCUS_GETMAC; |
| RCGetMAC (dev, NULL); |
| break; |
| case RCUC_GETPROM: |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC GETPROM\n"); |
| RCUD_GETPROM = &RCuser.RCUS_GETPROM; |
| RCGetPromiscuousMode (dev, |
| (PU32) & RCUD_GETPROM-> |
| PromMode, NULL); |
| break; |
| case RCUC_GETBROADCAST: |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC GETBROADCAST\n"); |
| RCUD_GETBROADCAST = &RCuser.RCUS_GETBROADCAST; |
| RCGetBroadcastMode (dev, |
| (PU32) & RCUD_GETBROADCAST-> |
| BroadcastMode, NULL); |
| break; |
| case RCUC_GETSPEED: |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC GETSPEED\n"); |
| if (!(dev->flags & IFF_UP)) { |
| printk (KERN_ERR |
| "(rcpci45 driver:) RCioctl, GETSPEED error: interface down\n"); |
| return -ENODATA; |
| } |
| RCUD_GETSPEED = &RCuser.RCUS_GETSPEED; |
| RCGetLinkSpeed (dev, |
| (PU32) & RCUD_GETSPEED-> |
| LinkSpeedCode, NULL); |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC speed = 0x%u\n", |
| RCUD_GETSPEED->LinkSpeedCode); |
| break; |
| case RCUC_SETIPANDMASK: |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC SETIPANDMASK\n"); |
| RCUD_SETIPANDMASK = &RCuser.RCUS_SETIPANDMASK; |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC New IP Addr = %d.%d.%d.%d, ", |
| (U8) ((RCUD_SETIPANDMASK-> |
| IpAddr) & 0xff), |
| (U8) ((RCUD_SETIPANDMASK-> |
| IpAddr >> 8) & 0xff), |
| (U8) ((RCUD_SETIPANDMASK-> |
| IpAddr >> 16) & 0xff), |
| (U8) ((RCUD_SETIPANDMASK-> |
| IpAddr >> 24) & 0xff)); |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC New Mask = %d.%d.%d.%d\n", |
| (U8) ((RCUD_SETIPANDMASK-> |
| NetMask) & 0xff), |
| (U8) ((RCUD_SETIPANDMASK-> |
| NetMask >> 8) & 0xff), |
| (U8) ((RCUD_SETIPANDMASK-> |
| NetMask >> 16) & 0xff), |
| (U8) ((RCUD_SETIPANDMASK-> |
| NetMask >> 24) & 0xff)); |
| RCSetRavlinIPandMask (dev, |
| (U32) RCUD_SETIPANDMASK-> |
| IpAddr, |
| (U32) RCUD_SETIPANDMASK-> |
| NetMask); |
| break; |
| case RCUC_SETMAC: |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC SETMAC\n"); |
| RCUD_SETMAC = &RCuser.RCUS_SETMAC; |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC New MAC addr = %02X:%02X:%02X:%02X:%02X:%02X\n", |
| (U8) (RCUD_SETMAC->mac[0]), |
| (U8) (RCUD_SETMAC->mac[1]), |
| (U8) (RCUD_SETMAC->mac[2]), |
| (U8) (RCUD_SETMAC->mac[3]), |
| (U8) (RCUD_SETMAC->mac[4]), |
| (U8) (RCUD_SETMAC->mac[5])); |
| RCSetMAC (dev, (PU8) & RCUD_SETMAC->mac); |
| break; |
| case RCUC_SETSPEED: |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC SETSPEED\n"); |
| RCUD_SETSPEED = &RCuser.RCUS_SETSPEED; |
| RCSetLinkSpeed (dev, |
| (U16) RCUD_SETSPEED-> |
| LinkSpeedCode); |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC New speed = 0x%x\n", |
| RCUD_SETSPEED->LinkSpeedCode); |
| break; |
| case RCUC_SETPROM: |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC SETPROM\n"); |
| RCUD_SETPROM = &RCuser.RCUS_SETPROM; |
| RCSetPromiscuousMode (dev, |
| (U16) RCUD_SETPROM-> |
| PromMode); |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC New prom mode = 0x%x\n", |
| RCUD_SETPROM->PromMode); |
| break; |
| case RCUC_SETBROADCAST: |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC SETBROADCAST\n"); |
| RCUD_SETBROADCAST = &RCuser.RCUS_SETBROADCAST; |
| RCSetBroadcastMode (dev, |
| (U16) RCUD_SETBROADCAST-> |
| BroadcastMode); |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC New broadcast mode = 0x%x\n", |
| RCUD_SETBROADCAST->BroadcastMode); |
| break; |
| default: |
| printk (KERN_INFO |
| "(rcpci45 driver:) RC command default\n"); |
| RCUD_DEFAULT = &RCuser.RCUS_DEFAULT; |
| RCUD_DEFAULT->rc = 0x11223344; |
| break; |
| } |
| if (copy_to_user |
| (rq->ifr_data, &RCuser, sizeof (RCuser))) |
| return -EFAULT; |
| break; |
| } /* RCU_COMMAND */ |
| |
| default: |
| rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678; |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| static int |
| RCconfig (struct net_device *dev, struct ifmap *map) |
| { |
| /* |
| * To be completed ... |
| */ |
| dprintk ("RCconfig\n"); |
| return 0; |
| if (dev->flags & IFF_UP) /* can't act on a running interface */ |
| return -EBUSY; |
| |
| /* Don't allow changing the I/O address */ |
| if (map->base_addr != dev->base_addr) { |
| printk (KERN_WARNING |
| "(rcpci45 driver:) Change I/O address not implemented\n"); |
| return -EOPNOTSUPP; |
| } |
| return 0; |
| } |
| |
| static void __exit |
| rcpci_cleanup_module (void) |
| { |
| pci_unregister_driver (&rcpci45_driver); |
| } |
| |
| module_init (rcpci_init_module); |
| module_exit (rcpci_cleanup_module); |
| |
| static int |
| RC_allocate_and_post_buffers (struct net_device *dev, int numBuffers) |
| { |
| |
| int i; |
| PU32 p; |
| psingleB pB; |
| struct sk_buff *skb; |
| RC_RETURN status; |
| U32 res; |
| |
| if (!numBuffers) |
| return 0; |
| else if (numBuffers > MAX_NMBR_POST_BUFFERS_PER_MSG) { |
| dprintk ("Too many buffers requested!\n"); |
| dprintk ("attempting to allocate only 32 buffers\n"); |
| numBuffers = 32; |
| } |
| |
| p = (PU32) kmalloc (sizeof (U32) + numBuffers * sizeof (singleB), |
| GFP_KERNEL); |
| |
| dprintk ("TCB = 0x%x\n", (uint) p); |
| |
| if (!p) { |
| printk (KERN_WARNING |
| "(rcpci45 driver:) RCopen: unable to allocate TCB\n"); |
| return 0; |
| } |
| |
| p[0] = 0; /* Buffer Count */ |
| pB = (psingleB) ((U32) p + sizeof (U32)); /* point to the first buffer */ |
| |
| dprintk ("p[0] = 0x%x, p = 0x%x, pB = 0x%x\n", (uint) p[0], (uint) p, |
| (uint) pB); |
| dprintk ("pB = 0x%x\n", (uint) pB); |
| |
| for (i = 0; i < numBuffers; i++) { |
| skb = dev_alloc_skb (MAX_ETHER_SIZE + 2); |
| if (!skb) { |
| dprintk |
| ("Doh! RCopen: unable to allocate enough skbs!\n"); |
| if (*p != 0) { /* did we allocate any buffers at all? */ |
| dprintk ("will post only %d buffers \n", |
| (uint) (*p)); |
| break; |
| } else { |
| kfree (p); /* Free the TCB */ |
| return 0; |
| } |
| } |
| dprintk ("post 0x%x\n", (uint) skb); |
| skb_reserve (skb, 2); /* Align IP on 16 byte boundaries */ |
| pB->context = (U32) skb; |
| pB->scount = 1; /* segment count */ |
| pB->size = MAX_ETHER_SIZE; |
| pB->addr = virt_to_bus ((void *) skb->data); |
| p[0]++; |
| pB++; |
| } |
| |
| if ((status = RCPostRecvBuffers (dev, (PRCTCB) p)) != RC_RTN_NO_ERROR) { |
| printk (KERN_WARNING |
| "(rcpci45 driver:) Post buffer failed with error code 0x%x!\n", |
| status); |
| pB = (psingleB) ((U32) p + sizeof (U32)); /* point to the first buffer */ |
| while (p[0]) { |
| skb = (struct sk_buff *) pB->context; |
| dprintk ("freeing 0x%x\n", (uint) skb); |
| dev_kfree_skb (skb); |
| p[0]--; |
| pB++; |
| } |
| dprintk ("freed all buffers, p[0] = %d\n", (uint) p[0]); |
| } |
| res = p[0]; |
| kfree (p); |
| return (res); /* return the number of posted buffers */ |
| } |