aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/skfp/skfddi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/skfp/skfddi.c')
-rw-r--r--drivers/net/skfp/skfddi.c2293
1 files changed, 2293 insertions, 0 deletions
diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c
new file mode 100644
index 00000000000..c88aad6edd7
--- /dev/null
+++ b/drivers/net/skfp/skfddi.c
@@ -0,0 +1,2293 @@
+/*
+ * File Name:
+ * skfddi.c
+ *
+ * Copyright Information:
+ * Copyright SysKonnect 1998,1999.
+ *
+ * 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.
+ *
+ * The information in this file is provided "AS IS" without warranty.
+ *
+ * Abstract:
+ * A Linux device driver supporting the SysKonnect FDDI PCI controller
+ * familie.
+ *
+ * Maintainers:
+ * CG Christoph Goos (cgoos@syskonnect.de)
+ *
+ * Contributors:
+ * DM David S. Miller
+ *
+ * Address all question to:
+ * linux@syskonnect.de
+ *
+ * The technical manual for the adapters is available from SysKonnect's
+ * web pages: www.syskonnect.com
+ * Goto "Support" and search Knowledge Base for "manual".
+ *
+ * Driver Architecture:
+ * The driver architecture is based on the DEC FDDI driver by
+ * Lawrence V. Stefani and several ethernet drivers.
+ * I also used an existing Windows NT miniport driver.
+ * All hardware dependent fuctions are handled by the SysKonnect
+ * Hardware Module.
+ * The only headerfiles that are directly related to this source
+ * are skfddi.c, h/types.h, h/osdef1st.h, h/targetos.h.
+ * The others belong to the SysKonnect FDDI Hardware Module and
+ * should better not be changed.
+ *
+ * Modification History:
+ * Date Name Description
+ * 02-Mar-98 CG Created.
+ *
+ * 10-Mar-99 CG Support for 2.2.x added.
+ * 25-Mar-99 CG Corrected IRQ routing for SMP (APIC)
+ * 26-Oct-99 CG Fixed compilation error on 2.2.13
+ * 12-Nov-99 CG Source code release
+ * 22-Nov-99 CG Included in kernel source.
+ * 07-May-00 DM 64 bit fixes, new dma interface
+ * 31-Jul-03 DB Audit copy_*_user in skfp_ioctl
+ * Daniele Bellucci <bellucda@tiscali.it>
+ * 03-Dec-03 SH Convert to PCI device model
+ *
+ * Compilation options (-Dxxx):
+ * DRIVERDEBUG print lots of messages to log file
+ * DUMPPACKETS print received/transmitted packets to logfile
+ *
+ * Tested cpu architectures:
+ * - i386
+ * - sparc64
+ */
+
+/* Version information string - should be updated prior to */
+/* each new release!!! */
+#define VERSION "2.07"
+
+static const char *boot_msg =
+ "SysKonnect FDDI PCI Adapter driver v" VERSION " for\n"
+ " SK-55xx/SK-58xx adapters (SK-NET FDDI-FP/UP/LP)";
+
+/* Include files */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/fddidevice.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include "h/types.h"
+#undef ADDR // undo Linux definition
+#include "h/skfbi.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+#include "h/smtstate.h"
+
+
+// Define module-wide (static) routines
+static int skfp_driver_init(struct net_device *dev);
+static int skfp_open(struct net_device *dev);
+static int skfp_close(struct net_device *dev);
+static irqreturn_t skfp_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static struct net_device_stats *skfp_ctl_get_stats(struct net_device *dev);
+static void skfp_ctl_set_multicast_list(struct net_device *dev);
+static void skfp_ctl_set_multicast_list_wo_lock(struct net_device *dev);
+static int skfp_ctl_set_mac_address(struct net_device *dev, void *addr);
+static int skfp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int skfp_send_pkt(struct sk_buff *skb, struct net_device *dev);
+static void send_queued_packets(struct s_smc *smc);
+static void CheckSourceAddress(unsigned char *frame, unsigned char *hw_addr);
+static void ResetAdapter(struct s_smc *smc);
+
+
+// Functions needed by the hardware module
+void *mac_drv_get_space(struct s_smc *smc, u_int size);
+void *mac_drv_get_desc_mem(struct s_smc *smc, u_int size);
+unsigned long mac_drv_virt2phys(struct s_smc *smc, void *virt);
+unsigned long dma_master(struct s_smc *smc, void *virt, int len, int flag);
+void dma_complete(struct s_smc *smc, volatile union s_fp_descr *descr,
+ int flag);
+void mac_drv_tx_complete(struct s_smc *smc, volatile struct s_smt_fp_txd *txd);
+void llc_restart_tx(struct s_smc *smc);
+void mac_drv_rx_complete(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd,
+ int frag_count, int len);
+void mac_drv_requeue_rxd(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd,
+ int frag_count);
+void mac_drv_fill_rxd(struct s_smc *smc);
+void mac_drv_clear_rxd(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd,
+ int frag_count);
+int mac_drv_rx_init(struct s_smc *smc, int len, int fc, char *look_ahead,
+ int la_len);
+void dump_data(unsigned char *Data, int length);
+
+// External functions from the hardware module
+extern u_int mac_drv_check_space(void);
+extern void read_address(struct s_smc *smc, u_char * mac_addr);
+extern void card_stop(struct s_smc *smc);
+extern int mac_drv_init(struct s_smc *smc);
+extern void hwm_tx_frag(struct s_smc *smc, char far * virt, u_long phys,
+ int len, int frame_status);
+extern int hwm_tx_init(struct s_smc *smc, u_char fc, int frag_count,
+ int frame_len, int frame_status);
+extern int init_smt(struct s_smc *smc, u_char * mac_addr);
+extern void fddi_isr(struct s_smc *smc);
+extern void hwm_rx_frag(struct s_smc *smc, char far * virt, u_long phys,
+ int len, int frame_status);
+extern void mac_drv_rx_mode(struct s_smc *smc, int mode);
+extern void mac_drv_clear_rx_queue(struct s_smc *smc);
+extern void enable_tx_irq(struct s_smc *smc, u_short queue);
+extern void mac_drv_clear_txd(struct s_smc *smc);
+
+static struct pci_device_id skfddi_pci_tbl[] = {
+ { PCI_VENDOR_ID_SK, PCI_DEVICE_ID_SK_FP, PCI_ANY_ID, PCI_ANY_ID, },
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, skfddi_pci_tbl);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mirko Lindner <mlindner@syskonnect.de>");
+
+// Define module-wide (static) variables
+
+static int num_boards; /* total number of adapters configured */
+
+#ifdef DRIVERDEBUG
+#define PRINTK(s, args...) printk(s, ## args)
+#else
+#define PRINTK(s, args...)
+#endif // DRIVERDEBUG
+
+/*
+ * =================
+ * = skfp_init_one =
+ * =================
+ *
+ * Overview:
+ * Probes for supported FDDI PCI controllers
+ *
+ * Returns:
+ * Condition code
+ *
+ * Arguments:
+ * pdev - pointer to PCI device information
+ *
+ * Functional Description:
+ * This is now called by PCI driver registration process
+ * for each board found.
+ *
+ * Return Codes:
+ * 0 - This device (fddi0, fddi1, etc) configured successfully
+ * -ENODEV - No devices present, or no SysKonnect FDDI PCI device
+ * present for this device name
+ *
+ *
+ * Side Effects:
+ * Device structures for FDDI adapters (fddi0, fddi1, etc) are
+ * initialized and the board resources are read and stored in
+ * the device structure.
+ */
+static int skfp_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *dev;
+ struct s_smc *smc; /* board pointer */
+ void __iomem *mem;
+ int err;
+
+ PRINTK(KERN_INFO "entering skfp_init_one\n");
+
+ if (num_boards == 0)
+ printk("%s\n", boot_msg);
+
+ err = pci_enable_device(pdev);
+ if (err)
+ return err;
+
+ err = pci_request_regions(pdev, "skfddi");
+ if (err)
+ goto err_out1;
+
+ pci_set_master(pdev);
+
+#ifdef MEM_MAPPED_IO
+ if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+ printk(KERN_ERR "skfp: region is not an MMIO resource\n");
+ err = -EIO;
+ goto err_out2;
+ }
+
+ mem = ioremap(pci_resource_start(pdev, 0), 0x4000);
+#else
+ if (!(pci_resource_flags(pdev, 1) & IO_RESOURCE_IO)) {
+ printk(KERN_ERR "skfp: region is not PIO resource\n");
+ err = -EIO;
+ goto err_out2;
+ }
+
+ mem = ioport_map(pci_resource_start(pdev, 1), FP_IO_LEN);
+#endif
+ if (!mem) {
+ printk(KERN_ERR "skfp: Unable to map register, "
+ "FDDI adapter will be disabled.\n");
+ err = -EIO;
+ goto err_out2;
+ }
+
+ dev = alloc_fddidev(sizeof(struct s_smc));
+ if (!dev) {
+ printk(KERN_ERR "skfp: Unable to allocate fddi device, "
+ "FDDI adapter will be disabled.\n");
+ err = -ENOMEM;
+ goto err_out3;
+ }
+
+ dev->irq = pdev->irq;
+ dev->get_stats = &skfp_ctl_get_stats;
+ dev->open = &skfp_open;
+ dev->stop = &skfp_close;
+ dev->hard_start_xmit = &skfp_send_pkt;
+ dev->set_multicast_list = &skfp_ctl_set_multicast_list;
+ dev->set_mac_address = &skfp_ctl_set_mac_address;
+ dev->do_ioctl = &skfp_ioctl;
+ dev->header_cache_update = NULL; /* not supported */
+
+ SET_MODULE_OWNER(dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* Initialize board structure with bus-specific info */
+ smc = netdev_priv(dev);
+ smc->os.dev = dev;
+ smc->os.bus_type = SK_BUS_TYPE_PCI;
+ smc->os.pdev = *pdev;
+ smc->os.QueueSkb = MAX_TX_QUEUE_LEN;
+ smc->os.MaxFrameSize = MAX_FRAME_SIZE;
+ smc->os.dev = dev;
+ smc->hw.slot = -1;
+ smc->hw.iop = mem;
+ smc->os.ResetRequested = FALSE;
+ skb_queue_head_init(&smc->os.SendSkbQueue);
+
+ dev->base_addr = (unsigned long)mem;
+
+ err = skfp_driver_init(dev);
+ if (err)
+ goto err_out4;
+
+ err = register_netdev(dev);
+ if (err)
+ goto err_out5;
+
+ ++num_boards;
+ pci_set_drvdata(pdev, dev);
+
+ if ((pdev->subsystem_device & 0xff00) == 0x5500 ||
+ (pdev->subsystem_device & 0xff00) == 0x5800)
+ printk("%s: SysKonnect FDDI PCI adapter"
+ " found (SK-%04X)\n", dev->name,
+ pdev->subsystem_device);
+ else
+ printk("%s: FDDI PCI adapter found\n", dev->name);
+
+ return 0;
+err_out5:
+ if (smc->os.SharedMemAddr)
+ pci_free_consistent(pdev, smc->os.SharedMemSize,
+ smc->os.SharedMemAddr,
+ smc->os.SharedMemDMA);
+ pci_free_consistent(pdev, MAX_FRAME_SIZE,
+ smc->os.LocalRxBuffer, smc->os.LocalRxBufferDMA);
+err_out4:
+ free_netdev(dev);
+err_out3:
+#ifdef MEM_MAPPED_IO
+ iounmap(mem);
+#else
+ ioport_unmap(mem);
+#endif
+err_out2:
+ pci_release_regions(pdev);
+err_out1:
+ pci_disable_device(pdev);
+ return err;
+}
+
+/*
+ * Called for each adapter board from pci_unregister_driver
+ */
+static void __devexit skfp_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *p = pci_get_drvdata(pdev);
+ struct s_smc *lp = netdev_priv(p);
+
+ unregister_netdev(p);
+
+ if (lp->os.SharedMemAddr) {
+ pci_free_consistent(&lp->os.pdev,
+ lp->os.SharedMemSize,
+ lp->os.SharedMemAddr,
+ lp->os.SharedMemDMA);
+ lp->os.SharedMemAddr = NULL;
+ }
+ if (lp->os.LocalRxBuffer) {
+ pci_free_consistent(&lp->os.pdev,
+ MAX_FRAME_SIZE,
+ lp->os.LocalRxBuffer,
+ lp->os.LocalRxBufferDMA);
+ lp->os.LocalRxBuffer = NULL;
+ }
+#ifdef MEM_MAPPED_IO
+ iounmap(lp->hw.iop);
+#else
+ ioport_unmap(lp->hw.iop);
+#endif
+ pci_release_regions(pdev);
+ free_netdev(p);
+
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+/*
+ * ====================
+ * = skfp_driver_init =
+ * ====================
+ *
+ * Overview:
+ * Initializes remaining adapter board structure information
+ * and makes sure adapter is in a safe state prior to skfp_open().
+ *
+ * Returns:
+ * Condition code
+ *
+ * Arguments:
+ * dev - pointer to device information
+ *
+ * Functional Description:
+ * This function allocates additional resources such as the host memory
+ * blocks needed by the adapter.
+ * The adapter is also reset. The OS must call skfp_open() to open
+ * the adapter and bring it on-line.
+ *
+ * Return Codes:
+ * 0 - initialization succeeded
+ * -1 - initialization failed
+ */
+static int skfp_driver_init(struct net_device *dev)
+{
+ struct s_smc *smc = netdev_priv(dev);
+ skfddi_priv *bp = &smc->os;
+ int err = -EIO;
+
+ PRINTK(KERN_INFO "entering skfp_driver_init\n");
+
+ // set the io address in private structures
+ bp->base_addr = dev->base_addr;
+
+ // Get the interrupt level from the PCI Configuration Table
+ smc->hw.irq = dev->irq;
+
+ spin_lock_init(&bp->DriverLock);
+
+ // Allocate invalid frame
+ bp->LocalRxBuffer = pci_alloc_consistent(&bp->pdev, MAX_FRAME_SIZE, &bp->LocalRxBufferDMA);
+ if (!bp->LocalRxBuffer) {
+ printk("could not allocate mem for ");
+ printk("LocalRxBuffer: %d byte\n", MAX_FRAME_SIZE);
+ goto fail;
+ }
+
+ // Determine the required size of the 'shared' memory area.
+ bp->SharedMemSize = mac_drv_check_space();
+ PRINTK(KERN_INFO "Memory for HWM: %ld\n", bp->SharedMemSize);
+ if (bp->SharedMemSize > 0) {
+ bp->SharedMemSize += 16; // for descriptor alignment
+
+ bp->SharedMemAddr = pci_alloc_consistent(&bp->pdev,
+ bp->SharedMemSize,
+ &bp->SharedMemDMA);
+ if (!bp->SharedMemSize) {
+ printk("could not allocate mem for ");
+ printk("hardware module: %ld byte\n",
+ bp->SharedMemSize);
+ goto fail;
+ }
+ bp->SharedMemHeap = 0; // Nothing used yet.
+
+ } else {
+ bp->SharedMemAddr = NULL;
+ bp->SharedMemHeap = 0;
+ } // SharedMemSize > 0
+
+ memset(bp->SharedMemAddr, 0, bp->SharedMemSize);
+
+ card_stop(smc); // Reset adapter.
+
+ PRINTK(KERN_INFO "mac_drv_init()..\n");
+ if (mac_drv_init(smc) != 0) {
+ PRINTK(KERN_INFO "mac_drv_init() failed.\n");
+ goto fail;
+ }
+ read_address(smc, NULL);
+ PRINTK(KERN_INFO "HW-Addr: %02x %02x %02x %02x %02x %02x\n",
+ smc->hw.fddi_canon_addr.a[0],
+ smc->hw.fddi_canon_addr.a[1],
+ smc->hw.fddi_canon_addr.a[2],
+ smc->hw.fddi_canon_addr.a[3],
+ smc->hw.fddi_canon_addr.a[4],
+ smc->hw.fddi_canon_addr.a[5]);
+ memcpy(dev->dev_addr, smc->hw.fddi_canon_addr.a, 6);
+
+ smt_reset_defaults(smc, 0);
+
+ return (0);
+
+fail:
+ if (bp->SharedMemAddr) {
+ pci_free_consistent(&bp->pdev,
+ bp->SharedMemSize,
+ bp->SharedMemAddr,
+ bp->SharedMemDMA);
+ bp->SharedMemAddr = NULL;
+ }
+ if (bp->LocalRxBuffer) {
+ pci_free_consistent(&bp->pdev, MAX_FRAME_SIZE,
+ bp->LocalRxBuffer, bp->LocalRxBufferDMA);
+ bp->LocalRxBuffer = NULL;
+ }
+ return err;
+} // skfp_driver_init
+
+
+/*
+ * =============
+ * = skfp_open =
+ * =============
+ *
+ * Overview:
+ * Opens the adapter
+ *
+ * Returns:
+ * Condition code
+ *
+ * Arguments:
+ * dev - pointer to device information
+ *
+ * Functional Description:
+ * This function brings the adapter to an operational state.
+ *
+ * Return Codes:
+ * 0 - Adapter was successfully opened
+ * -EAGAIN - Could not register IRQ
+ */
+static int skfp_open(struct net_device *dev)
+{
+ struct s_smc *smc = netdev_priv(dev);
+ int err;
+
+ PRINTK(KERN_INFO "entering skfp_open\n");
+ /* Register IRQ - support shared interrupts by passing device ptr */
+ err = request_irq(dev->irq, (void *) skfp_interrupt, SA_SHIRQ,
+ dev->name, dev);
+ if (err)
+ return err;
+
+ /*
+ * Set current address to factory MAC address
+ *
+ * Note: We've already done this step in skfp_driver_init.
+ * However, it's possible that a user has set a node
+ * address override, then closed and reopened the
+ * adapter. Unless we reset the device address field
+ * now, we'll continue to use the existing modified
+ * address.
+ */
+ read_address(smc, NULL);
+ memcpy(dev->dev_addr, smc->hw.fddi_canon_addr.a, 6);
+
+ init_smt(smc, NULL);
+ smt_online(smc, 1);
+ STI_FBI();
+
+ /* Clear local multicast address tables */
+ mac_clear_multicast(smc);
+
+ /* Disable promiscuous filter settings */
+ mac_drv_rx_mode(smc, RX_DISABLE_PROMISC);
+
+ netif_start_queue(dev);
+ return (0);
+} // skfp_open
+
+
+/*
+ * ==============
+ * = skfp_close =
+ * ==============
+ *
+ * Overview:
+ * Closes the device/module.
+ *
+ * Returns:
+ * Condition code
+ *
+ * Arguments:
+ * dev - pointer to device information
+ *
+ * Functional Description:
+ * This routine closes the adapter and brings it to a safe state.
+ * The interrupt service routine is deregistered with the OS.
+ * The adapter can be opened again with another call to skfp_open().
+ *
+ * Return Codes:
+ * Always return 0.
+ *
+ * Assumptions:
+ * No further requests for this adapter are made after this routine is
+ * called. skfp_open() can be called to reset and reinitialize the
+ * adapter.
+ */
+static int skfp_close(struct net_device *dev)
+{
+ struct s_smc *smc = netdev_priv(dev);
+ skfddi_priv *bp = &smc->os;
+
+ CLI_FBI();
+ smt_reset_defaults(smc, 1);
+ card_stop(smc);
+ mac_drv_clear_tx_queue(smc);
+ mac_drv_clear_rx_queue(smc);
+
+ netif_stop_queue(dev);
+ /* Deregister (free) IRQ */
+ free_irq(dev->irq, dev);
+
+ skb_queue_purge(&bp->SendSkbQueue);
+ bp->QueueSkb = MAX_TX_QUEUE_LEN;
+
+ return (0);
+} // skfp_close
+
+
+/*
+ * ==================
+ * = skfp_interrupt =
+ * ==================
+ *
+ * Overview:
+ * Interrupt processing routine
+ *
+ * Returns:
+ * None
+ *
+ * Arguments:
+ * irq - interrupt vector
+ * dev_id - pointer to device information
+ * regs - pointer to registers structure
+ *
+ * Functional Description:
+ * This routine calls the interrupt processing routine for this adapter. It
+ * disables and reenables adapter interrupts, as appropriate. We can support
+ * shared interrupts since the incoming dev_id pointer provides our device
+ * structure context. All the real work is done in the hardware module.
+ *
+ * Return Codes:
+ * None
+ *
+ * Assumptions:
+ * The interrupt acknowledgement at the hardware level (eg. ACKing the PIC
+ * on Intel-based systems) is done by the operating system outside this
+ * routine.
+ *
+ * System interrupts are enabled through this call.
+ *
+ * Side Effects:
+ * Interrupts are disabled, then reenabled at the adapter.
+ */
+
+irqreturn_t skfp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct s_smc *smc; /* private board structure pointer */
+ skfddi_priv *bp;
+
+ if (dev == NULL) {
+ printk("%s: irq %d for unknown device\n", dev->name, irq);
+ return IRQ_NONE;
+ }
+
+ smc = netdev_priv(dev);
+ bp = &smc->os;
+
+ // IRQs enabled or disabled ?
+ if (inpd(ADDR(B0_IMSK)) == 0) {
+ // IRQs are disabled: must be shared interrupt
+ return IRQ_NONE;
+ }
+ // Note: At this point, IRQs are enabled.
+ if ((inpd(ISR_A) & smc->hw.is_imask) == 0) { // IRQ?
+ // Adapter did not issue an IRQ: must be shared interrupt
+ return IRQ_NONE;
+ }
+ CLI_FBI(); // Disable IRQs from our adapter.
+ spin_lock(&bp->DriverLock);
+
+ // Call interrupt handler in hardware module (HWM).
+ fddi_isr(smc);
+
+ if (smc->os.ResetRequested) {
+ ResetAdapter(smc);
+ smc->os.ResetRequested = FALSE;
+ }
+ spin_unlock(&bp->DriverLock);
+ STI_FBI(); // Enable IRQs from our adapter.
+
+ return IRQ_HANDLED;
+} // skfp_interrupt
+
+
+/*
+ * ======================
+ * = skfp_ctl_get_stats =
+ * ======================
+ *
+ * Overview:
+ * Get statistics for FDDI adapter
+ *
+ * Returns:
+ * Pointer to FDDI statistics structure
+ *
+ * Arguments:
+ * dev - pointer to device information
+ *
+ * Functional Description:
+ * Gets current MIB objects from adapter, then
+ * returns FDDI statistics structure as defined
+ * in if_fddi.h.
+ *
+ * Note: Since the FDDI statistics structure is
+ * still new and the device structure doesn't
+ * have an FDDI-specific get statistics handler,
+ * we'll return the FDDI statistics structure as
+ * a pointer to an Ethernet statistics structure.
+ * That way, at least the first part of the statistics
+ * structure can be decoded properly.
+ * We'll have to pay attention to this routine as the
+ * device structure becomes more mature and LAN media
+ * independent.
+ *
+ */
+struct net_device_stats *skfp_ctl_get_stats(struct net_device *dev)
+{
+ struct s_smc *bp = netdev_priv(dev);
+
+ /* Fill the bp->stats structure with driver-maintained counters */
+
+ bp->os.MacStat.port_bs_flag[0] = 0x1234;
+ bp->os.MacStat.port_bs_flag[1] = 0x5678;
+// goos: need to fill out fddi statistic
+#if 0
+ /* Get FDDI SMT MIB objects */
+
+/* Fill the bp->stats structure with the SMT MIB object values */
+
+ memcpy(bp->stats.smt_station_id, &bp->cmd_rsp_virt->smt_mib_get.smt_station_id, sizeof(bp->cmd_rsp_virt->smt_mib_get.smt_station_id));
+ bp->stats.smt_op_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_op_version_id;
+ bp->stats.smt_hi_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_hi_version_id;
+ bp->stats.smt_lo_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_lo_version_id;
+ memcpy(bp->stats.smt_user_data, &bp->cmd_rsp_virt->smt_mib_get.smt_user_data, sizeof(bp->cmd_rsp_virt->smt_mib_get.smt_user_data));
+ bp->stats.smt_mib_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_mib_version_id;
+ bp->stats.smt_mac_cts = bp->cmd_rsp_virt->smt_mib_get.smt_mac_ct;
+ bp->stats.smt_non_master_cts = bp->cmd_rsp_virt->smt_mib_get.smt_non_master_ct;
+ bp->stats.smt_master_cts = bp->cmd_rsp_virt->smt_mib_get.smt_master_ct;
+ bp->stats.smt_available_paths = bp->cmd_rsp_virt->smt_mib_get.smt_available_paths;
+ bp->stats.smt_config_capabilities = bp->cmd_rsp_virt->smt_mib_get.smt_config_capabilities;
+ bp->stats.smt_config_policy = bp->cmd_rsp_virt->smt_mib_get.smt_config_policy;
+ bp->stats.smt_connection_policy = bp->cmd_rsp_virt->smt_mib_get.smt_connection_policy;
+ bp->stats.smt_t_notify = bp->cmd_rsp_virt->smt_mib_get.smt_t_notify;
+ bp->stats.smt_stat_rpt_policy = bp->cmd_rsp_virt->smt_mib_get.smt_stat_rpt_policy;
+ bp->stats.smt_trace_max_expiration = bp->cmd_rsp_virt->smt_mib_get.smt_trace_max_expiration;
+ bp->stats.smt_bypass_present = bp->cmd_rsp_virt->smt_mib_get.smt_bypass_present;
+ bp->stats.smt_ecm_state = bp->cmd_rsp_virt->smt_mib_get.smt_ecm_state;
+ bp->stats.smt_cf_state = bp->cmd_rsp_virt->smt_mib_get.smt_cf_state;
+ bp->stats.smt_remote_disconnect_flag = bp->cmd_rsp_virt->smt_mib_get.smt_remote_disconnect_flag;
+ bp->stats.smt_station_status = bp->cmd_rsp_virt->smt_mib_get.smt_station_status;
+ bp->stats.smt_peer_wrap_flag = bp->cmd_rsp_virt->smt_mib_get.smt_peer_wrap_flag;
+ bp->stats.smt_time_stamp = bp->cmd_rsp_virt->smt_mib_get.smt_msg_time_stamp.ls;
+ bp->stats.smt_transition_time_stamp = bp->cmd_rsp_virt->smt_mib_get.smt_transition_time_stamp.ls;
+ bp->stats.mac_frame_status_functions = bp->cmd_rsp_virt->smt_mib_get.mac_frame_status_functions;
+ bp->stats.mac_t_max_capability = bp->cmd_rsp_virt->smt_mib_get.mac_t_max_capability;
+ bp->stats.mac_tvx_capability = bp->cmd_rsp_virt->smt_mib_get.mac_tvx_capability;
+ bp->stats.mac_available_paths = bp->cmd_rsp_virt->smt_mib_get.mac_available_paths;
+ bp->stats.mac_current_path = bp->cmd_rsp_virt->smt_mib_get.mac_current_path;
+ memcpy(bp->stats.mac_upstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_upstream_nbr, FDDI_K_ALEN);
+ memcpy(bp->stats.mac_downstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_downstream_nbr, FDDI_K_ALEN);
+ memcpy(bp->stats.mac_old_upstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_old_upstream_nbr, FDDI_K_ALEN);
+ memcpy(bp->stats.mac_old_downstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_old_downstream_nbr, FDDI_K_ALEN);
+ bp->stats.mac_dup_address_test = bp->cmd_rsp_virt->smt_mib_get.mac_dup_address_test;
+ bp->stats.mac_requested_paths = bp->cmd_rsp_virt->smt_mib_get.mac_requested_paths;
+ bp->stats.mac_downstream_port_type = bp->cmd_rsp_virt->smt_mib_get.mac_downstream_port_type;
+ memcpy(bp->stats.mac_smt_address, &bp->cmd_rsp_virt->smt_mib_get.mac_smt_address, FDDI_K_ALEN);
+ bp->stats.mac_t_req = bp->cmd_rsp_virt->smt_mib_get.mac_t_req;
+ bp->stats.mac_t_neg = bp->cmd_rsp_virt->smt_mib_get.mac_t_neg;
+ bp->stats.mac_t_max = bp->cmd_rsp_virt->smt_mib_get.mac_t_max;
+ bp->stats.mac_tvx_value = bp->cmd_rsp_virt->smt_mib_get.mac_tvx_value;
+ bp->stats.mac_frame_error_threshold = bp->cmd_rsp_virt->smt_mib_get.mac_frame_error_threshold;
+ bp->stats.mac_frame_error_ratio = bp->cmd_rsp_virt->smt_mib_get.mac_frame_error_ratio;
+ bp->stats.mac_rmt_state = bp->cmd_rsp_virt->smt_mib_get.mac_rmt_state;
+ bp->stats.mac_da_flag = bp->cmd_rsp_virt->smt_mib_get.mac_da_flag;
+ bp->stats.mac_una_da_flag = bp->cmd_rsp_virt->smt_mib_get.mac_unda_flag;
+ bp->stats.mac_frame_error_flag = bp->cmd_rsp_virt->smt_mib_get.mac_frame_error_flag;
+ bp->stats.mac_ma_unitdata_available = bp->cmd_rsp_virt->smt_mib_get.mac_ma_unitdata_available;
+ bp->stats.mac_hardware_present = bp->cmd_rsp_virt->smt_mib_get.mac_hardware_present;
+ bp->stats.mac_ma_unitdata_enable = bp->cmd_rsp_virt->smt_mib_get.mac_ma_unitdata_enable;
+ bp->stats.path_tvx_lower_bound = bp->cmd_rsp_virt->smt_mib_get.path_tvx_lower_bound;
+ bp->stats.path_t_max_lower_bound = bp->cmd_rsp_virt->smt_mib_get.path_t_max_lower_bound;
+ bp->stats.path_max_t_req = bp->cmd_rsp_virt->smt_mib_get.path_max_t_req;
+ memcpy(bp->stats.path_configuration, &bp->cmd_rsp_virt->smt_mib_get.path_configuration, sizeof(bp->cmd_rsp_virt->smt_mib_get.path_configuration));
+ bp->stats.port_my_type[0] = bp->cmd_rsp_virt->smt_mib_get.port_my_type[0];
+ bp->stats.port_my_type[1] = bp->cmd_rsp_virt->smt_mib_get.port_my_type[1];
+ bp->stats.port_neighbor_type[0] = bp->cmd_rsp_virt->smt_mib_get.port_neighbor_type[0];
+ bp->stats.port_neighbor_type[1] = bp->cmd_rsp_virt->smt_mib_get.port_neighbor_type[1];
+ bp->stats.port_connection_policies[0] = bp->cmd_rsp_virt->smt_mib_get.port_connection_policies[0];
+ bp->stats.port_connection_policies[1] = bp->cmd_rsp_virt->smt_mib_get.port_connection_policies[1];
+ bp->stats.port_mac_indicated[0] = bp->cmd_rsp_virt->smt_mib_get.port_mac_indicated[0];
+ bp->stats.port_mac_indicated[1] = bp->cmd_rsp_virt->smt_mib_get.port_mac_indicated[1];
+ bp->stats.port_current_path[0] = bp->cmd_rsp_virt->smt_mib_get.port_current_path[0];
+ bp->stats.port_current_path[1] = bp->cmd_rsp_virt->smt_mib_get.port_current_path[1];
+ memcpy(&bp->stats.port_requested_paths[0 * 3], &bp->cmd_rsp_virt->smt_mib_get.port_requested_paths[0], 3);
+ memcpy(&bp->stats.port_requested_paths[1 * 3], &bp->cmd_rsp_virt->smt_mib_get.port_requested_paths[1], 3);
+ bp->stats.port_mac_placement[0] = bp->cmd_rsp_virt->smt_mib_get.port_mac_placement[0];
+ bp->stats.port_mac_placement[1] = bp->cmd_rsp_virt->smt_mib_get.port_mac_placement[1];
+ bp->stats.port_available_paths[0] = bp->cmd_rsp_virt->smt_mib_get.port_available_paths[0];
+ bp->stats.port_available_paths[1] = bp->cmd_rsp_virt->smt_mib_get.port_available_paths[1];
+ bp->stats.port_pmd_class[0] = bp->cmd_rsp_virt->smt_mib_get.port_pmd_class[0];
+ bp->stats.port_pmd_class[1] = bp->cmd_rsp_virt->smt_mib_get.port_pmd_class[1];
+ bp->stats.port_connection_capabilities[0] = bp->cmd_rsp_virt->smt_mib_get.port_connection_capabilities[0];
+ bp->stats.port_connection_capabilities[1] = bp->cmd_rsp_virt->smt_mib_get.port_connection_capabilities[1];
+ bp->stats.port_bs_flag[0] = bp->cmd_rsp_virt->smt_mib_get.port_bs_flag[0];
+ bp->stats.port_bs_flag[1] = bp->cmd_rsp_virt->smt_mib_get.port_bs_flag[1];
+ bp->stats.port_ler_estimate[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_estimate[0];
+ bp->stats.port_ler_estimate[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_estimate[1];
+ bp->stats.port_ler_cutoff[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_cutoff[0];
+ bp->stats.port_ler_cutoff[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_cutoff[1];
+ bp->stats.port_ler_alarm[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_alarm[0];
+ bp->stats.port_ler_alarm[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_alarm[1];
+ bp->stats.port_connect_state[0] = bp->cmd_rsp_virt->smt_mib_get.port_connect_state[0];
+ bp->stats.port_connect_state[1] = bp->cmd_rsp_virt->smt_mib_get.port_connect_state[1];
+ bp->stats.port_pcm_state[0] = bp->cmd_rsp_virt->smt_mib_get.port_pcm_state[0];
+ bp->stats.port_pcm_state[1] = bp->cmd_rsp_virt->smt_mib_get.port_pcm_state[1];
+ bp->stats.port_pc_withhold[0] = bp->cmd_rsp_virt->smt_mib_get.port_pc_withhold[0];
+ bp->stats.port_pc_withhold[1] = bp->cmd_rsp_virt->smt_mib_get.port_pc_withhold[1];
+ bp->stats.port_ler_flag[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_flag[0];
+ bp->stats.port_ler_flag[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_flag[1];
+ bp->stats.port_hardware_present[0] = bp->cmd_rsp_virt->smt_mib_get.port_hardware_present[0];
+ bp->stats.port_hardware_present[1] = bp->cmd_rsp_virt->smt_mib_get.port_hardware_present[1];
+
+
+ /* Fill the bp->stats structure with the FDDI counter values */
+
+ bp->stats.mac_frame_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.frame_cnt.ls;
+ bp->stats.mac_copied_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.copied_cnt.ls;
+ bp->stats.mac_transmit_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.transmit_cnt.ls;
+ bp->stats.mac_error_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.error_cnt.ls;
+ bp->stats.mac_lost_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.lost_cnt.ls;
+ bp->stats.port_lct_fail_cts[0] = bp->cmd_rsp_virt->cntrs_get.cntrs.lct_rejects[0].ls;
+ bp->stats.port_lct_fail_cts[1] = bp->cmd_rsp_virt->cntrs_get.cntrs.lct_rejects[1].ls;
+ bp->stats.port_lem_reject_cts[0] = bp->cmd_rsp_virt->cntrs_get.cntrs.lem_rejects[0].ls;
+ bp->stats.port_lem_reject_cts[1] = bp->cmd_rsp_virt->cntrs_get.cntrs.lem_rejects[1].ls;
+ bp->stats.port_lem_cts[0] = bp->cmd_rsp_virt->cntrs_get.cntrs.link_errors[0].ls;
+ bp->stats.port_lem_cts[1] = bp->cmd_rsp_virt->cntrs_get.cntrs.link_errors[1].ls;
+
+#endif
+ return ((struct net_device_stats *) &bp->os.MacStat);
+} // ctl_get_stat
+
+
+/*
+ * ==============================
+ * = skfp_ctl_set_multicast_list =
+ * ==============================
+ *
+ * Overview:
+ * Enable/Disable LLC frame promiscuous mode reception
+ * on the adapter and/or update multicast address table.
+ *
+ * Returns:
+ * None
+ *
+ * Arguments:
+ * dev - pointer to device information
+ *
+ * Functional Description:
+ * This function acquires the driver lock and only calls
+ * skfp_ctl_set_multicast_list_wo_lock then.
+ * This routine follows a fairly simple algorithm for setting the
+ * adapter filters and CAM:
+ *
+ * if IFF_PROMISC flag is set
+ * enable promiscuous mode
+ * else
+ * disable promiscuous mode
+ * if number of multicast addresses <= max. multicast number
+ * add mc addresses to adapter table
+ * else
+ * enable promiscuous mode
+ * update adapter filters
+ *
+ * Assumptions:
+ * Multicast addresses are presented in canonical (LSB) format.
+ *
+ * Side Effects:
+ * On-board adapter filters are updated.
+ */
+static void skfp_ctl_set_multicast_list(struct net_device *dev)
+{
+ struct s_smc *smc = netdev_priv(dev);
+ skfddi_priv *bp = &smc->os;
+ unsigned long Flags;
+
+ spin_lock_irqsave(&bp->DriverLock, Flags);
+ skfp_ctl_set_multicast_list_wo_lock(dev);
+ spin_unlock_irqrestore(&bp->DriverLock, Flags);
+ return;
+} // skfp_ctl_set_multicast_list
+
+
+
+static void skfp_ctl_set_multicast_list_wo_lock(struct net_device *dev)
+{
+ struct s_smc *smc = netdev_priv(dev);
+ struct dev_mc_list *dmi; /* ptr to multicast addr entry */
+ int i;
+
+ /* Enable promiscuous mode, if necessary */
+ if (dev->flags & IFF_PROMISC) {
+ mac_drv_rx_mode(smc, RX_ENABLE_PROMISC);
+ PRINTK(KERN_INFO "PROMISCUOUS MODE ENABLED\n");
+ }
+ /* Else, update multicast address table */
+ else {
+ mac_drv_rx_mode(smc, RX_DISABLE_PROMISC);
+ PRINTK(KERN_INFO "PROMISCUOUS MODE DISABLED\n");
+
+ // Reset all MC addresses
+ mac_clear_multicast(smc);
+ mac_drv_rx_mode(smc, RX_DISABLE_ALLMULTI);
+
+ if (dev->flags & IFF_ALLMULTI) {
+ mac_drv_rx_mode(smc, RX_ENABLE_ALLMULTI);
+ PRINTK(KERN_INFO "ENABLE ALL MC ADDRESSES\n");
+ } else if (dev->mc_count > 0) {
+ if (dev->mc_count <= FPMAX_MULTICAST) {
+ /* use exact filtering */
+
+ // point to first multicast addr
+ dmi = dev->mc_list;
+
+ for (i = 0; i < dev->mc_count; i++) {
+ mac_add_multicast(smc,
+ (struct fddi_addr *)dmi->dmi_addr,
+ 1);
+
+ PRINTK(KERN_INFO "ENABLE MC ADDRESS:");
+ PRINTK(" %02x %02x %02x ",
+ dmi->dmi_addr[0],
+ dmi->dmi_addr[1],
+ dmi->dmi_addr[2]);
+ PRINTK("%02x %02x %02x\n",
+ dmi->dmi_addr[3],
+ dmi->dmi_addr[4],
+ dmi->dmi_addr[5]);
+ dmi = dmi->next;
+ } // for
+
+ } else { // more MC addresses than HW supports
+
+ mac_drv_rx_mode(smc, RX_ENABLE_ALLMULTI);
+ PRINTK(KERN_INFO "ENABLE ALL MC ADDRESSES\n");
+ }
+ } else { // no MC addresses
+
+ PRINTK(KERN_INFO "DISABLE ALL MC ADDRESSES\n");
+ }
+
+ /* Update adapter filters */
+ mac_update_multicast(smc);
+ }
+ return;
+} // skfp_ctl_set_multicast_list_wo_lock
+
+
+/*
+ * ===========================
+ * = skfp_ctl_set_mac_address =
+ * ===========================
+ *
+ * Overview:
+ * set new mac address on adapter and update dev_addr field in device table.
+ *
+ * Returns:
+ * None
+ *
+ * Arguments:
+ * dev - pointer to device information
+ * addr - pointer to sockaddr structure containing unicast address to set
+ *
+ * Assumptions:
+ * The address pointed to by addr->sa_data is a valid unicast
+ * address and is presented in canonical (LSB) format.
+ */
+static int skfp_ctl_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct s_smc *smc = netdev_priv(dev);
+ struct sockaddr *p_sockaddr = (struct sockaddr *) addr;
+ skfddi_priv *bp = &smc->os;
+ unsigned long Flags;
+
+
+ memcpy(dev->dev_addr, p_sockaddr->sa_data, FDDI_K_ALEN);
+ spin_lock_irqsave(&bp->DriverLock, Flags);
+ ResetAdapter(smc);
+ spin_unlock_irqrestore(&bp->DriverLock, Flags);
+
+ return (0); /* always return zero */
+} // skfp_ctl_set_mac_address
+
+
+/*
+ * ==============
+ * = skfp_ioctl =
+ * ==============
+ *
+ * Overview:
+ *
+ * Perform IOCTL call functions here. Some are privileged operations and the
+ * effective uid is checked in those cases.
+ *
+ * Returns:
+ * status value
+ * 0 - success
+ * other - failure
+ *
+ * Arguments:
+ * dev - pointer to device information
+ * rq - pointer to ioctl request structure
+ * cmd - ?
+ *
+ */
+
+
+static int skfp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct s_smc *smc = netdev_priv(dev);
+ skfddi_priv *lp = &smc->os;
+ struct s_skfp_ioctl ioc;
+ int status = 0;
+
+ if (copy_from_user(&ioc, rq->ifr_data, sizeof(struct s_skfp_ioctl)))
+ return -EFAULT;
+
+ switch (ioc.cmd) {
+ case SKFP_GET_STATS: /* Get the driver statistics */
+ ioc.len = sizeof(lp->MacStat);
+ status = copy_to_user(ioc.data, skfp_ctl_get_stats(dev), ioc.len)
+ ? -EFAULT : 0;
+ break;
+ case SKFP_CLR_STATS: /* Zero out the driver statistics */
+ if (!capable(CAP_NET_ADMIN)) {
+ memset(&lp->MacStat, 0, sizeof(lp->MacStat));
+ } else {
+ status = -EPERM;
+ }
+ break;
+ default:
+ printk("ioctl for %s: unknow cmd: %04x\n", dev->name, ioc.cmd);
+ status = -EOPNOTSUPP;
+
+ } // switch
+
+ return status;
+} // skfp_ioctl
+
+
+/*
+ * =====================
+ * = skfp_send_pkt =
+ * =====================
+ *
+ * Overview:
+ * Queues a packet for transmission and try to transmit it.
+ *
+ * Returns:
+ * Condition code
+ *
+ * Arguments:
+ * skb - pointer to sk_buff to queue for transmission<