patch-2.2.16 linux/drivers/net/wanxl.c
Next file: linux/drivers/net/wanxl.h
Previous file: linux/drivers/net/via-rhine.c
Back to the patch index
Back to the overall index
- Lines: 1234
- Date:
Wed Jun 7 14:26:43 2000
- Orig file:
v2.2.15/linux/drivers/net/wanxl.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -urN v2.2.15/linux/drivers/net/wanxl.c linux/drivers/net/wanxl.c
@@ -0,0 +1,1233 @@
+/* wanxl.c
+ * -*- linux-c -*-
+ *
+ * (C) 1999 RG Studio s.c., http://www.rgstudio.com.pl/
+ * Written by Krzysztof Halasa <khc@rgstudio.com.pl>
+ *
+ * Portions (C) SBE Inc., used by permission.
+ *
+ * Sources:
+ * wanXL technical reference manuals
+ * wanXL UNIXware X.25 driver
+ * Donald Becker's skeleton.c driver
+ * "Linux Kernel Module Programming" by Ori Pomerantz
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/if_frad.h>
+
+#include <linux/pci.h>
+
+#include "syncppp.h"
+#include <linux/hdlc.h>
+#include "plx9060.h"
+#include "wanxl.h"
+
+static const char* version = "SBE wanXL driver revision: 1.0-pre9";
+static const char* irqdevname = "SBE wanXL";
+
+#ifdef __LITTLE_ENDIAN
+#define SWAP_OPTION MBX_CMD_BSWAP_1
+#else
+#error "big endian untested yet"
+#define SWAP_OPTION MBX_CMD_BSWAP_0
+#endif
+
+
+#define WANXL_DEVICE_IDS 3 /* number of possible device IDs we will look
+ for on the PCI bus */
+
+static int SBEdeviceIDs[WANXL_DEVICE_IDS] = { PCI_DEVICE_ID_SBE_WANXL100,
+ PCI_DEVICE_ID_SBE_WANXL200,
+ PCI_DEVICE_ID_SBE_WANXL400 };
+
+#define MAX_PUTS_WAIT 20 /* in seconds */
+#define MAX_QUERY_WAIT 1
+#define WANXL_HOST_MEM_SIZE 0x10000000
+#define WANXL_BUS2HOST_MAP_ADDR 0x00000000
+
+#define PCI_COMM_MAE 0x0002 /* memory access enable */
+#define PCI_COMM_ME 0x0004 /* master enable */
+#define PCI_CONF_COMM 0x4 /* command register, 2 bytes */
+
+
+static sbe_card *first_card=NULL;
+static sbe_card **new_card=&first_card;
+
+static const char *cable_types[]={ "undefined0",
+ "undefined1",
+ "V.35",
+ "X.21",
+ "RS232",
+ "EIA530",
+ "no port",
+ "none" };
+
+
+#define max(x, y) (x > y ? x : y)
+
+
+
+static int wanxl_run_board(sbe_card *card, char *data, u32 *length);
+#ifdef WANXL_QUERY
+static int wanxl_query_port(sbe_port *port, char *data, u32 *length);
+#endif
+
+static inline sbe_port* hdlc_to_port(hdlc_device *hdlc)
+{
+ return (sbe_port*)hdlc;
+}
+
+
+
+static inline sbe_port* dev_to_port(struct device *dev)
+{
+ return hdlc_to_port(dev_to_hdlc(dev));
+}
+
+
+
+static inline char *port_name(sbe_port *port)
+{
+ return port->hdlc.name;
+}
+
+
+
+static inline char *card_name(sbe_card *card)
+{
+ return card->first_port->hdlc.name;
+}
+
+
+
+static inline int wanxl_card_running(sbe_card *card)
+{
+ return card->running;
+}
+
+
+
+static inline void wanxl_send_interrupt(sbe_port *port, int transmit)
+{
+ if ((transmit && ((port->tx_ring->r_flags & BF_IREQ) == 0)) ||
+ (!transmit && ((port->rx_ring->r_flags & BF_IREQ) == 0)))
+ return; /* No need for interrupt signaling */
+
+#ifdef DEBUG_INTR
+ printk(KERN_DEBUG "%s: wanxl_send_interrupt (%s, node=%u)\n",
+ port_name(port), transmit ? "TX" : "RX", port->node);
+#endif
+ if (transmit)
+ transmit=TX_IRQ_BIAS;
+
+ writel(1<<(transmit+port->node), &port->card->wx_plx_viraddr->dbr_in);
+}
+
+
+
+static void wanxl_parse_injector(sbe_port *port, void *data, u32 len)
+{
+ int i;
+
+ printk(KERN_INFO "%s: got injector (len=%X):", port->hdlc.name, len);
+
+ for (i=0; i<(len+3)/4; i++)
+ printk(" %08X", ((u32*)data)[i]);
+ printk("\n");
+}
+
+
+
+static int wanxl_submit_injector(sbe_port *port, void *packet, u32 size,
+ int wait)
+{
+#ifdef DEBUG_INJ
+ int i;
+#endif
+ pkt_desc *tx_pkt=port->tx_ring->v_inject;
+ pkt_desc *rx_pkt=port->rx_ring->v_inject;
+ bio_wrap *bio_tx=tx_pkt->v_data;
+ bio_wrap *bio_rx=rx_pkt->v_data;
+ u32 cmd;
+
+ if (tx_pkt->pd_flags & PD_READY) {
+ sleep_on_timeout(&port->tx_inj, HZ*MAX_QUERY_WAIT);
+ if (tx_pkt->pd_flags & PD_READY)
+ return -EBUSY; /* TX injector still in use */
+ }
+
+ memcpy(bio_tx, packet, size);
+ cmd=bio_tx->type;
+ tx_pkt->pd_length=size;
+#ifdef DEBUG_INJ
+ printk(KERN_DEBUG "%s: Sending injector:", port_name(port));
+ for (i=0; i<size/4; i++)
+ printk(" %08X", ((u32*)packet)[i]);
+ printk("\n");
+#endif
+ tx_pkt->pd_flags=PD_FLAGS_INJ|PD_READY;
+ wanxl_send_interrupt(port, 1);
+
+ if (!wait)
+ return 0;
+
+ while(1) {
+ sleep_on_timeout(&port->rx_inj, HZ*MAX_QUERY_WAIT);
+ if ((rx_pkt->pd_flags & PD_READY)==0) {
+ printk(KERN_WARNING "%s: Timeout when waiting for "
+ "injector %X\n", port_name(port), cmd);
+ return -EIO;
+ }
+
+ if (bio_rx->type==cmd && rx_pkt->pd_length==size)
+ break;
+
+ wanxl_parse_injector(port, bio_rx, rx_pkt->pd_length);
+ rx_pkt->pd_flags=PD_FLAGS_INJ;
+ wanxl_send_interrupt(port, 0);
+ }
+
+ memcpy(packet, bio_rx, size);
+ rx_pkt->pd_flags=PD_FLAGS_INJ;
+ wanxl_send_interrupt(port, 0);
+
+ if (bio_rx->status) {
+ printk(KERN_WARNING "%s: injector %X failed, status=%X\n",
+ port_name(port), cmd, bio_rx->status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+
+static void wanxl_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ sbe_card *card=dev_id;
+ sbe_port *port;
+ u32 int_mask;
+
+ if (!wanxl_card_running(card))
+ return;
+
+ int_mask=readl(&card->wx_plx_viraddr->dbr_out);
+ writel(int_mask, &card->wx_plx_viraddr->dbr_out); /* Confirm IRQ */
+
+ for (port=card->first_port; port!=NULL; port=port->next_port) {
+ pkt_desc *pkt;
+
+ if (port->rx_ring->v_inject->pd_flags & PD_READY)
+ if (waitqueue_active(&port->rx_inj))
+ wake_up(&port->rx_inj); /* Got injector response */
+
+ if ((port->tx_ring->v_inject->pd_flags & PD_READY) == 0)
+ if (waitqueue_active(&port->tx_inj))
+ wake_up(&port->tx_inj); /* Got free injector */
+
+ pkt=port->next_rx_pkt;
+ while (pkt->pd_flags & PD_READY) {
+#ifdef DEBUG_PKT
+ int i;
+#endif
+ if (pkt->pd_flags & PD_ERR)
+ port->hdlc.stats.rx_errors++; /* RX error */
+ else {
+ struct sk_buff *skb=pkt->v_data;
+#ifdef DEBUG_PKT
+ printk(KERN_DEBUG "%s RX (PCI=%08x, "
+ "proto=%x):", port_name(port),
+ pkt->p_data, pkt->mux.fr.protid);
+ for (i=0; i<pkt->pd_length; i++)
+ printk(" %02X", skb->data[i]);
+ printk("\n");
+#endif
+ if ((port->mode & MODE_FR) && pkt->mux.fr.protid!=IP_PROTID &&
+ pkt->mux.fr.protid!=ARP_PROTID)
+ port->hdlc.stats.rx_errors++; /* Ignore it for now */
+ else {
+ struct sk_buff *newskb=dev_alloc_skb(HDLC_MAX_MTU);
+ if (newskb==NULL)
+ port->hdlc.stats.rx_dropped++;
+ else {
+ skb_put(skb, pkt->pd_length);
+ if ((port->mode & MODE_FR) && pkt->mux.fr.protid==ARP_PROTID) {
+ skb_pull(skb, 2); /* Remove 2 byte header left by PDM */
+ skb->protocol=htons(ETH_P_ARP);
+ } else
+ skb->protocol=htons(ETH_P_IP);
+
+ hdlc_netif_rx(&port->hdlc, skb, pkt->mux.fr.dlci);
+ pkt->v_data=newskb;
+ pkt->p_data=virt_to_bus(newskb->data);
+ }
+ }
+ }
+
+ pkt->pd_length=0;
+ pkt->pd_flags = PD_FLAGS_RX | (pkt->pd_flags & PD_LAST);
+
+ if (pkt->pd_flags & PD_LAST)
+ pkt=port->rx_ring->v_ring; /* Rewind */
+ else
+ pkt++;
+ } /* while */
+
+ port->next_rx_pkt=pkt;
+ wanxl_send_interrupt(port, 0); /* RX ring serviced */
+
+ pkt=port->next_tx_pkt_done;
+ while (pkt->pd_flags & PD_DONE) {
+ if (pkt->pd_flags & PD_ERR)
+ printk(KERN_WARNING "%s: PD_ERROR set on TX packet\n",
+ port_name(port));
+
+ port->hdlc.stats.tx_packets++;
+ port->hdlc.stats.tx_bytes+=pkt->pd_length;
+
+ dev_kfree_skb(pkt->v_data);
+ pkt->pd_flags = PD_FLAGS_TX | (pkt->pd_flags & PD_LAST);
+
+ if (pkt->pd_flags & PD_LAST)
+ pkt=port->tx_ring->v_ring; /* Rewind */
+ else
+ pkt++;
+ } /* while */
+ port->next_tx_pkt_done=pkt;
+ wanxl_send_interrupt(port, 1); /* TX ring serviced */
+ }
+}
+
+
+
+static int wanxl_set_mode(hdlc_device *hdlc, int mode)
+{
+ sbe_port *port=hdlc_to_port(hdlc);
+
+ if (wanxl_card_running(port->card)==0)
+ return -ENOSYS; /* Card not initialized */
+
+ if (mode!=MODE_HDLC && mode!=MODE_FR_ANSI && mode!=MODE_FR_CCITT)
+ return -ENOSYS; /* Not supported on this hardware yet */
+
+ port->mode=mode;
+ return 0;
+}
+
+
+
+static int wanxl_create_pvc(pvc_device *pvc)
+{
+ sbe_port *port=hdlc_to_port(pvc->master);
+ u16 dlci=netdev_dlci(&pvc->netdev);
+ b_tnioc packet;
+ int result;
+
+ if (wanxl_card_running(port->card)==0)
+ return -ENOSYS; /* Card not initialized */
+
+ if (!(hdlc_to_dev(pvc->master)->flags & IFF_UP))
+ return -ENOSYS; /* HDLC port down */
+
+ memset(&packet, 0, sizeof(packet));
+ packet.bio.type=B_SETTUNE;
+ packet.tune.fr_pvc.dlci=dlci;
+ packet.tune.fr_pvc.cir=16000;
+ packet.tune.fr_pvc.Bc=16000;
+ packet.tune.fr_pvc.Be=2048000;
+ packet.tune.fr_pvc.stepcount=8;
+ packet.tune.fr_pvc.flowstyle=FECN;
+ /* FIXME - must be set ? why ? */
+ packet.tune.fr_pvc.protid=IP_PROTID|ARP_PROTID;
+ packet.type=FR_PVC_TYPE;
+ result = wanxl_submit_injector(port, &packet, sizeof(packet), 0);
+ if (result)
+ return result;
+
+ return 0;
+}
+
+
+
+static int wanxl_open_pvc(pvc_device *pvc)
+{
+ pvc->state |= PVC_STATE_ACTIVE; /* FIXME */
+ return 0;
+}
+
+
+
+static int wanxl_open(hdlc_device *hdlc)
+{
+ sbe_port *port=hdlc_to_port(hdlc);
+ b_tnioc packet;
+ int result;
+
+ memset(&packet, 0, sizeof(packet));
+ packet.bio.type=B_SETTUNE;
+ packet.tune.wan.WAN_baud=2048000;
+ packet.tune.wan.WAN_maxframe=HDLC_MAX_MTU;
+ packet.type=HDLC_TYPE;
+ result=wanxl_submit_injector(port, &packet, sizeof(packet), 1);
+ if (result)
+ return result;
+
+
+ memset(&packet.bio, 0, sizeof(packet.bio));
+ packet.bio.type=B_OPEN;
+ result=wanxl_submit_injector(port, &packet.bio, sizeof(packet.bio), 1);
+ if (result)
+ return result;
+
+
+ if (port->mode==MODE_FR_ANSI || port->mode==MODE_FR_CCITT) {
+ memset(&packet, 0, sizeof(packet));
+ packet.bio.type=B_SETTUNE;
+ packet.tune.fr.T391=hdlc->lmi.T391;
+ packet.tune.fr.T392=hdlc->lmi.T392;
+ packet.tune.fr.N391=hdlc->lmi.N391;
+ packet.tune.fr.N392=hdlc->lmi.N392;
+ packet.tune.fr.N393=hdlc->lmi.N393;
+ packet.tune.fr.maxframesize=HDLC_MAX_MTU;
+ packet.tune.fr.accessrate=2048000;
+ if (port->mode==MODE_FR_CCITT)
+ packet.tune.fr.standard=ITU;
+ else
+ packet.tune.fr.standard=ANSI;
+ packet.tune.fr.conform=NONE;
+ packet.type=FR_TYPE;
+ result=wanxl_submit_injector(port, &packet, sizeof(packet), 0);
+ if (result)
+ return result;
+ }
+
+
+ memset(&packet.bio, 0, sizeof(packet.bio));
+ packet.bio.type=B_START;
+ result=wanxl_submit_injector(port, &packet.bio, sizeof(packet.bio), 1);
+ if (result)
+ return result;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+
+
+static void wanxl_close(hdlc_device *hdlc)
+{
+ sbe_port *port=hdlc_to_port(hdlc);
+ bio_wrap packet;
+
+ memset(&packet, 0, sizeof(packet));
+ packet.type=B_CLOSE;
+ wanxl_submit_injector(port, &packet, sizeof(packet), 1);
+
+ MOD_DEC_USE_COUNT;
+}
+
+
+
+static int wanxl_xmit(struct sk_buff *skb, struct device *dev)
+{
+ u16 dlci=0;
+ u8 proto=0;
+ sbe_port *port=dev_to_port(dev);
+ pkt_desc *pkt=port->next_tx_pkt;
+#ifdef DEBUG_PKT
+ int i;
+#endif
+
+ if (pkt->pd_flags & (PD_READY|PD_DONE)) {
+#ifdef DEBUG_PKT
+ printk(KERN_DEBUG "%s: Unable to send packet\n",
+ port_name(port));
+#endif
+ port->hdlc.stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ return 0; /* Out of card buffers - drop the skb */
+ }
+
+ if (port->mode & MODE_FR) {
+ dlci=q922_to_dlci(skb->data);
+ switch(skb->protocol) {
+ case __constant_htons(ETH_P_IP):
+ proto=IP_PROTID;
+ skb_pull(skb, 4);
+ break;
+
+ case __constant_htons(ETH_P_ARP):
+ proto=ARP_PROTID;
+ skb_pull(skb, 10);
+ break;
+
+ default:
+ port->hdlc.stats.tx_errors++;
+ dev_kfree_skb(skb);
+ return 0; /* Ignore packet */
+ }
+ }
+
+ /* Align buffer at 4-byte boundary */
+ if (virt_to_bus(skb->data) % 4) {
+ struct sk_buff *new=alloc_skb(skb->len, GFP_ATOMIC);
+ if (!new) {
+ port->hdlc.stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ return 0; /* Out of memory - drop packet */
+ }
+
+ memcpy(new->data, skb->data, skb->len);
+ skb_put(new, skb->len);
+ dev_kfree_skb(skb);
+ skb=new;
+ }
+
+ pkt->v_data=skb;
+ pkt->p_data=virt_to_bus(skb->data);
+ pkt->pd_length=skb->len;
+ pkt->mux.fr.dlci=dlci;
+ pkt->mux.fr.protid = (skb->protocol == __constant_htons(ETH_P_ARP) ?
+ ARP_PROTID : IP_PROTID);
+
+ if (pkt->pd_flags & PD_LAST)
+ port->next_tx_pkt=port->tx_ring->v_ring; /* Rewind */
+ else
+ port->next_tx_pkt=pkt+1;
+
+#ifdef DEBUG_PKT
+ printk(KERN_DEBUG "%s TX (PCI=%08x, proto=%x):",
+ dev->name, pkt->p_data, pkt->mux.fr.protid);
+ for (i=0; i<skb->len; i++)
+ printk(" %02X", skb->data[i]);
+ printk("\n");
+#endif
+
+ pkt->pd_flags=PD_FLAGS_TX|PD_READY | (pkt->pd_flags&PD_LAST);
+ wanxl_send_interrupt(port, 1);
+ return 0;
+}
+
+
+
+static int wanxl_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd)
+{
+ sbe_port *port=hdlc_to_port(hdlc);
+ u32 length;
+ int result;
+
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (ifr->ifr_data==NULL)
+ return -EFAULT;
+
+ if (copy_from_user(&length, ifr->ifr_data, 4)!=0)
+ return -EFAULT;
+
+ switch(cmd) {
+ case HDLCRUN:
+ if (port->card->first_port != port) /* Not initial port */
+ result=-EINVAL;
+ else if (port->card->rx_ring || port->card->tx_ring)
+ result=-EBUSY; /* Already running */
+ else
+ result=wanxl_run_board(port->card, ifr->ifr_data+4,
+ &length);
+ break;
+
+#ifdef WANXLQUERY
+ case WANXLQUERY:
+ if (wanxl_card_running(port->card)==0)
+ result=-EIO; /* Board must be run first */
+ else
+ result=wanxl_query_port(port, ifr->ifr_data+4,
+ &length);
+ break;
+#endif
+ default:
+ result=-EINVAL;
+ length=0;
+ }
+
+ if (copy_to_user(ifr->ifr_data, &length, 4)!=0)
+ return -EFAULT;
+
+ return result;
+}
+
+
+
+
+static inline int wanxl_init_hdlc_port(sbe_port *port)
+{
+ port->hdlc.set_mode=wanxl_set_mode;
+ port->hdlc.open=wanxl_open;
+ port->hdlc.close=wanxl_close;
+ port->hdlc.ioctl=wanxl_ioctl;
+ port->hdlc.create_pvc=wanxl_create_pvc;
+ port->hdlc.open_pvc=wanxl_open_pvc;
+ hdlc_to_dev(&port->hdlc)->hard_start_xmit=wanxl_xmit;
+ return register_hdlc_device(&port->hdlc);
+}
+
+
+
+static void wanxl_destroy_ring(desc_ring **root)
+{
+ int cnt;
+ desc_ring *node;
+ pkt_desc *pkts;
+
+ if (*root==NULL)
+ return;
+
+ node=*root;
+ while(node) {
+ pkts=node->v_ring;
+ if (pkts)
+ for (cnt=0; cnt<node->ndesc; cnt++) {
+ if (pkts->v_data)
+ dev_kfree_skb(pkts->v_data);
+ pkts++;
+ }
+ node=node->next_node ? bus_to_virt(node->next_node) : NULL;
+ }
+
+ if ((*root)->v_inject) {
+ if ((*root)->v_inject->v_data)
+ /* Delete injector buffers */
+ kfree((*root)->v_inject->v_data);
+
+ kfree((*root)->v_inject); /* Delete pkts descriptors */
+ }
+
+
+ kfree(*root); /* Delete ring descriptors */
+ *root=NULL;
+}
+
+
+
+static int wanxl_create_ring(desc_ring **root, int transmit, int ports)
+{
+ int node, cnt;
+ desc_ring *ring;
+ pkt_desc *pkts;
+ char *inj;
+ int descs=transmit ? WANXL_TX_BUFFERS : WANXL_RX_BUFFERS;
+
+ u32 ring_size=sizeof(desc_ring) * ports;
+ u32 pkts_size=sizeof(pkt_desc) * (descs+1) * ports;
+ u32 inj_size=MAX_IOCTL_PACKET * ports;
+
+ ring=kmalloc(ring_size, GFP_KERNEL);
+ if (ring==NULL)
+ return -ENOBUFS;
+ memset(ring, 0, ring_size);
+
+ pkts=kmalloc(pkts_size, GFP_KERNEL);
+ if (pkts==NULL) {
+ kfree(ring);
+ return -ENOBUFS;
+ }
+ memset(pkts, 0, pkts_size);
+
+ inj=kmalloc(inj_size, GFP_KERNEL);
+ if (inj==NULL) {
+ kfree(ring);
+ kfree(pkts);
+ return -ENOBUFS;
+ }
+ memset(inj, 0, inj_size);
+
+ *root=ring;
+
+ for (node=0; node<ports; node++) {
+ ring->r_flags = HF_IREQ | HF_ACTIVE | node;
+ ring->ndesc=descs;
+ ring->pdm_offset=0;
+ ring->cpu=0;
+ ring->next_cpu=NULL;
+ ring->next_node = (node==ports-1 ? 0 : virt_to_bus(ring+1));
+
+ ring->p_inject=virt_to_bus(pkts); /* Injector */
+ ring->v_inject=pkts;
+ pkts->pd_flags=PD_FLAGS_INJ;
+ pkts->p_data=virt_to_bus(inj);
+ pkts->v_data=inj;
+ pkts->pd_max_len=MAX_IOCTL_PACKET;
+ pkts++;
+ inj+=MAX_IOCTL_PACKET;
+
+ ring->p_ring=virt_to_bus(pkts); /* Buffers */
+ ring->v_ring=pkts; /* for host usage only */
+ for (cnt=0; cnt<descs; cnt++) {
+ if (transmit)
+ pkts->pd_flags=PD_FLAGS_TX;
+ else {
+ struct sk_buff *skb=dev_alloc_skb(HDLC_MAX_MTU);
+ if (skb==NULL) {
+ wanxl_destroy_ring(root);
+ return -ENOBUFS;
+ }
+
+ pkts->pd_flags=PD_FLAGS_RX;
+ pkts->p_data=virt_to_bus(skb->data);
+ pkts->v_data=skb;
+ }
+
+ if (cnt==descs-1)
+ pkts->pd_flags|=PD_LAST;
+ pkts->pd_max_len=HDLC_MAX_MTU;
+ pkts++;
+ }
+ ring++;
+ }
+
+ return 0;
+}
+
+
+
+static inline int wanxl_wait_on_box(volatile u32 *box_addr,
+ u32 *store_value, /* store value here */
+ u32 break_mask, /* breaks */
+ u32 equal, /* equal breaks */
+ long ms) /* ms delay count */
+{
+ u32 result;
+ unsigned long timeout=jiffies+max(2, ms * HZ / 1000);
+ do {
+ result=readl(box_addr);
+
+ if ((result & break_mask) || (result==equal))
+ break;
+
+ schedule();
+ }while(jiffies<timeout);
+
+ if (store_value!=NULL)
+ *store_value=result;
+
+ if ((result & break_mask) || (result==equal))
+ return 0;
+
+ return -1;
+}
+
+
+
+
+/*****************************************************************************
+ * wx_setmbx() -- board setup, set server's hardware mailbox
+ *
+ * Configuration of certain server parameters is accomplished via writing to
+ * that board's mailbox. This routine writes into the board's Mailbox
+ * Command register, then polls for completion. Completion is signaled by
+ * clearing of the Mailbox Command register by on/board code. Should the
+ * clearing fail to occur or take excessive time, the polling will time-out.
+ *
+ * The delay count parameter, wcnt, should specifiy the maximum number
+ * of milliseconds (ms) to wait. There are 1000 milliseconds per second.
+ *****************************************************************************/
+
+static int wanxl_setmbx(sbe_card *card,
+ u32 cmd, /* Mailbox Command */
+ long wcnt) /* ms delay */
+{
+ u32 result;
+ writel(cmd, &card->wx_plx_viraddr->mbox_1);
+
+ if (wanxl_wait_on_box(&card->wx_plx_viraddr->mbox_1, &result,
+ 0, 0x0, wcnt)!=0) {
+ printk(KERN_WARNING "%s: wanxl_setmbx: timeout processing "
+ "command 0x%x, result=0x%x\n",
+ card_name(card), cmd, result);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+
+
+/* issue a reset to the board */
+static int wanxl_reset_board(sbe_card *card, int wait)
+{
+ u32 result;
+ unsigned long timeout;
+
+ printk(KERN_DEBUG "%s: resetting board\n", card_name(card));
+ writel(MBX_STS_PCIRESET, &card->wx_plx_viraddr->mbox_0);
+
+ result=readl(&card->wx_plx_viraddr->control) | CTL_RESET;
+ writel(result, &card->wx_plx_viraddr->control);
+
+ timeout = jiffies + max(2, HZ / 500);
+ do
+ schedule();
+ while(jiffies<timeout);
+
+ result=readl(&card->wx_plx_viraddr->control) & ~CTL_RESET;
+ writel(result, &card->wx_plx_viraddr->control);
+
+ if (!wait)
+ return 0;
+
+ /* wait for successful completion of self-tests */
+ timeout = jiffies + MAX_PUTS_WAIT * HZ;
+ do {
+ result=readl(&card->wx_plx_viraddr->mbox_0);
+ if (result) {
+ if (result & MBX_STS_ERROR) {
+ printk(KERN_WARNING "%s: self-test failed, "
+ "status=0x%x.\n",
+ card_name(card), result);
+ return -ENODEV;
+ }
+ schedule();
+ } else
+ break;
+ }while(jiffies<timeout);
+
+ if (result) {
+ printk(KERN_WARNING "%s: self-test failed to complete\n",
+ card_name(card));
+ return -ENODEV;
+ }
+
+ printk(KERN_DEBUG "%s: self-test done.\n", card_name(card));
+ return 0;
+}
+
+
+
+#ifdef WANXL_QUERY
+static int wanxl_query_port(sbe_port *port, char *data, u32 *length)
+{
+ int result;
+ char packet[MAX_IOCTL_PACKET];
+
+ if (wanxl_card_running(port->card)==0)
+ return -ENOSYS; /* Card not initialized */
+
+ if (copy_from_user(packet, data, *length))
+ return -EFAULT;
+
+ result=wanxl_submit_injector(port, packet, *length, 1);
+ if (result)
+ return result;
+
+ if (copy_to_user(data, packet, *length))
+ return -EFAULT;
+
+ return 0;
+}
+#endif
+
+
+
+static void wanxl_destroy_card(sbe_card *card, int destroy_all)
+{
+ sbe_port *port=card->first_port;
+
+ wanxl_reset_board(card, 0);
+
+ if (!destroy_all && port) {
+ port=port->next_port; /* Omit first port */
+ card->first_port->next_port=NULL;
+ }
+
+ while (port) {
+ sbe_port *ptr=port;
+
+ unregister_hdlc_device(&port->hdlc);
+ port=port->next_port;
+ kfree(ptr);
+ }
+
+ if (card->rx_ring)
+ wanxl_destroy_ring(&card->rx_ring);
+
+ if (card->tx_ring)
+ wanxl_destroy_ring(&card->tx_ring);
+
+ if (destroy_all) {
+ if (card->irq)
+ free_irq(card->irq, card);
+
+ if (card->wx_plx_viraddr)
+ iounmap(card->wx_plx_viraddr);
+ if (card->wx_mem_viraddr)
+ iounmap(card->wx_mem_viraddr);
+ kfree(card);
+ }
+}
+
+
+
+static int wanxl_run_board(sbe_card *card, char *data, u32 *length)
+{
+ sbe_port *port=card->first_port, **prev_port;
+ desc_ring *tx_ring, *rx_ring;
+ u32 len=*length;
+ int result, cnt;
+ u32 bdsize;
+ ulong mt;
+ char *ms;
+
+ if (len>MAX_PDM_LEN || len<=sizeof(PCI360Header) || len%4) {
+ printk(KERN_WARNING "%s: Invalid firmware length\n",
+ port_name(port));
+ return -EFAULT;
+ }
+
+
+ /* reset board and wait for PUTS to complete */
+ result=wanxl_reset_board(card, 1);
+ if (result)
+ return result;
+
+ /* get on-board memory size */
+ bdsize = readl(&card->wx_plx_viraddr->mbox_2) & MBX_MEMSZ_MASK;
+
+ /* sanity check the board's reported memory size */
+ if (bdsize < MBX_MEMSZ_1MB || bdsize > MBX_MEMSZ_4MB) {
+ printk(KERN_WARNING "%s: illegal board memory size 0x%x",
+ port_name(port), bdsize);
+ return -ENODEV;
+ }
+
+ /* now, calculate the timeout value that will be added to a delay later
+ this is to allow time for clearing of on-board memory */
+ switch (bdsize) {
+ case MBX_MEMSZ_1MB: ms = "1MB"; mt = 2800; break;
+ case MBX_MEMSZ_2MB: ms = "2MB"; mt = 3600; break;
+ case MBX_MEMSZ_4MB: ms = "4MB"; mt = 5200; break;
+ case MBX_MEMSZ_8MB: ms = "8MB"; mt = 8400; break;
+ default: ms = "(size?)"; mt = 3600;
+ }
+ printk(KERN_INFO "%s: board memory size 0x%x (%cMB)\n",
+ port_name(port),
+ bdsize, *ms);
+
+ /*
+ * The processing of the BSWAP command is estimated at 50ms (board),
+ * there we set timeout value to 2 seconds, and add in the 0.8
+ * seconds per MB of on/board memory.
+ */
+
+ result=wanxl_setmbx(card, SWAP_OPTION, mt);
+ if (result) {
+ printk(KERN_WARNING "%s: unable to set byte-swap option, "
+ "aborting.\n", port_name(port));
+ return result;
+ }
+
+ /*
+ * The processing of the HBA and HMS Mailbox Commands is estimated at
+ * 0.5 seconds each (wanXL), and we select 1 second for the timeout
+ * value.
+ *
+ * Host Memory Base Address and Host Memory Size setup are Host
+ * dependent. HMS should be set first to avoid PUTS Warning msg.
+ */
+
+ result=wanxl_setmbx(card, MBX_CMD_SETHMS+(WANXL_HOST_MEM_SIZE>>8),
+ 1000);
+ if (result) {
+ printk(KERN_WARNING "%s: mbx SETHMS command failure\n",
+ port_name(port));
+ return result;
+ }
+
+ result=wanxl_setmbx(card, MBX_CMD_SETHBA|(WANXL_BUS2HOST_MAP_ADDR>>8),
+ 1000);
+ if (result) {
+ printk(KERN_WARNING "%s: mbx SETHBA command failure\n",
+ port_name(port));
+ return result;
+ }
+
+ if (copy_from_user((char*)card->wx_mem_viraddr_order+PDM_OFFSET,
+ data+sizeof(PCI360Header),
+ len-sizeof(PCI360Header)))
+ return -EFAULT;
+
+ writel(PDM_OFFSET, card->wx_mem_viraddr);
+
+ writel(0, &card->wx_plx_viraddr->mbox_5);
+ writel(0, &card->wx_plx_viraddr->mbox_6);
+ writel(0, &card->wx_plx_viraddr->mbox_7);
+
+ result=wanxl_setmbx(card, MBX_CMD_ABORTJ, 1000);
+ if (result) {
+ printk(KERN_WARNING "%s: unable to send AbortAndJump "
+ "command\n", port_name(port));
+ return result;
+ }
+
+ if (wanxl_wait_on_box(&card->wx_plx_viraddr->mbox_5, &result,
+ 0xFFFFFFFF, 0xFFFFFFFF, 1000)!=0) {
+ printk(KERN_WARNING "%s: board never became ready after "
+ "AbortAndJump command\n", port_name(port));
+ return -EFAULT;
+ }
+
+ card->config=(board_cfg*)(result+card->wx_mem_viraddr);
+
+ printk(KERN_INFO "%s: serial number %d\n", port_name(port),
+ card->config->serial_num);
+ printk(KERN_INFO "%s: %d port%s, RAM=0x%x len=0x%x\n", port_name(port),
+ card->config->num_ports, card->config->num_ports>1 ? "s" : "",
+ card->config->mem_base, card->config->mem_size);
+ printk(KERN_DEBUG "%s: firmware version: %s\n", port_name(port),
+ card->wx_mem_viraddr_order+card->config->fw_version);
+ printk(KERN_DEBUG "%s: PDM version: %s\n", port_name(port),
+ card->wx_mem_viraddr_order+card->config->pdm_version);
+ printk(KERN_DEBUG "%s: PDM compiled: %s\n", port_name(port),
+ card->wx_mem_viraddr_order+card->config->pdm_compiled);
+
+ if (card->config->num_ports > MAX_PORTS_PER_CARD) {
+ card->config->num_ports = MAX_PORTS_PER_CARD;
+ printk(KERN_WARNING "%s: Using only %i ports\n", port_name(port),
+ MAX_PORTS_PER_CARD);
+ }
+
+ if (wanxl_create_ring(&card->rx_ring, 0, MAX_PORTS_PER_CARD)!=0)
+ return -EFAULT;
+
+ if (wanxl_create_ring(&card->tx_ring, 1, MAX_PORTS_PER_CARD)!=0) {
+ wanxl_destroy_ring(&card->rx_ring);
+ return -EFAULT;
+ }
+
+ card->config->rx_ring_head=virt_to_bus(card->rx_ring);
+ card->config->tx_ring_head=virt_to_bus(card->tx_ring);
+ card->config->rx_max_pkts=10;
+ card->config->tx_max_pkts=10;
+
+ prev_port = &port->next_port; /* Back pointer to next_port address */
+ rx_ring = card->rx_ring;
+ tx_ring = card->tx_ring;
+
+ len=0;
+ for (cnt=0; cnt<MAX_PORTS_PER_CARD && len<card->config->num_ports;
+ cnt++) {
+ if (((card->config->port_info[cnt]&0xE0)>>5) != 6) {
+ /* port exist */
+ if (len!=0) {
+ /* Create next ports */
+ port=kmalloc(sizeof(sbe_port), GFP_KERNEL);
+ if (port==NULL) {
+ printk(KERN_WARNING "%s: Unable to create port structure\n",
+ card_name(card));
+ wanxl_destroy_card(card, 0);
+ }
+ memset(port, 0, sizeof(sbe_port));
+
+ hdlc_to_dev(&port->hdlc)->irq=card->irq;
+ port->card=card;
+ *prev_port=port; /* Connect port to chain */
+ prev_port=&port->next_port;
+
+ result=wanxl_init_hdlc_port(port);
+ if (result) {
+ printk(KERN_WARNING "%s: Unable to register net device\n",
+ port_name(port));
+ wanxl_destroy_card(card, 0);
+ return result;
+ }
+ }
+
+ port->node=cnt;
+ printk(KERN_INFO "%s: cable=%s, PM=%s\n",
+ port_name(port),
+ cable_types[card->config->port_info[cnt]&0x7],
+ cable_types[(card->config->port_info[cnt]&0xE0)>>5]);
+ len++; /* port found */
+ }
+ port->rx_ring=rx_ring++;
+ port->tx_ring=tx_ring++;
+ port->next_rx_pkt = port->rx_ring->v_ring;
+ port->next_tx_pkt = port->next_tx_pkt_done = port->tx_ring->v_ring;
+ }
+
+ writel(HDLC_MAX_MTU, &card->config->max_frame_size);
+ writel(HDLC_MAX_MTU, &card->config->avg_msg_size);
+ writel(HDLC_MAX_MTU, &card->config->scc_rx_mblk_sz);
+ writel(100, &card->config->tx_intr_tmo);
+
+ writel(1, &card->config->valid);
+
+ copy_to_user(data, card->config, sizeof(board_cfg));
+ *length=sizeof(board_cfg);
+ card->running=1;
+ return 0;
+}
+
+
+
+/*****************************************************************
+ *
+ * Module initialization and destruction
+ *
+ ****************************************************************/
+
+__initfunc(int wanxl_pci_init(struct pci_dev *pdev))
+{
+ int result;
+ u16 command;
+ sbe_card *card;
+ sbe_port *port;
+ u32 memsize;
+
+ card=kmalloc(sizeof(sbe_card), GFP_KERNEL);
+ if (card==NULL)
+ return -ENOBUFS;
+ memset(card, 0, sizeof(sbe_card));
+
+ port=kmalloc(sizeof(sbe_port), GFP_KERNEL);
+ if (port==NULL) {
+ kfree(card);
+ return -ENOBUFS;
+ }
+ memset(port, 0, sizeof(sbe_port));
+
+ card->first_port=port;
+ port->card=card;
+ port->node=0;
+ result=wanxl_init_hdlc_port(port);
+ if (result) {
+ printk(KERN_WARNING "wanXL: Unable to register hdlc device\n");
+ kfree(card);
+ kfree(port);
+ return result;
+ }
+
+ pci_read_config_word(pdev, PCI_CONF_COMM, &command);
+ if ((command & (PCI_COMM_ME|PCI_COMM_MAE))==0) {
+ command|=(PCI_COMM_ME|PCI_COMM_MAE);
+ printk(KERN_WARNING "%s: updating PCI config register %d to 0x%x\n",
+ port_name(port), PCI_CONF_COMM, command);
+ pci_write_config_word(pdev, PCI_CONF_COMM, command);
+ }
+
+ /* set up addresses and mappings */
+ card->wx_plx_phyaddr=pdev->base_address[0];
+ card->wx_mem_phyaddr=pdev->base_address[2];
+ card->wx_plx_viraddr=ioremap_nocache(card->wx_plx_phyaddr,
+ sizeof(plx9060));
+
+ memsize = card->wx_plx_viraddr->mbox_2 & MBX_MEMSZ_MASK;
+ card->wx_mem_viraddr = ioremap_nocache(card->wx_mem_phyaddr,
+ memsize*2);
+ card->wx_mem_viraddr_order = card->wx_mem_viraddr + memsize;
+
+ printk(KERN_INFO "%s: plx9060/9080 at 0x%x, RAM at 0x%x, irq %u\n",
+ port_name(port),
+ card->wx_plx_phyaddr, card->wx_mem_phyaddr, pdev->irq);
+
+ /* allocate IRQ for the board */
+
+ if (request_irq(pdev->irq, &wanxl_intr, SA_SHIRQ, irqdevname, card)) {
+ printk(KERN_WARNING "%s: could not allocate IRQ%d.\n",
+ port_name(port), pdev->irq);
+ wanxl_destroy_card(card, 1);
+ return(-ENODEV);
+ }
+ card->irq=hdlc_to_dev(&port->hdlc)->irq=pdev->irq;
+
+ *new_card=card;
+ new_card=&card->next_card;
+
+ return 0;
+}
+
+
+
+__initfunc(int wanxl_init(void))
+{
+ int i, devs=0, rep=0;
+
+ if(!pci_present())
+ return -ENODEV;
+
+ for(i=0; i<WANXL_DEVICE_IDS; i++) {
+ struct pci_dev *pdev=NULL;
+ do {
+ pdev=pci_find_device(PCI_VENDOR_ID_SBE,
+ SBEdeviceIDs[i], pdev);
+ if (pdev) {
+ int result;
+
+ if (!rep) {
+ printk(KERN_INFO "%s\n", version);
+ rep=1;
+ }
+
+ result=wanxl_pci_init(pdev);
+ if (result==0)
+ devs++; /* Initialized a card */
+ else
+ printk(KERN_WARNING "wanXL: Error initializing card, result=%i\n",
+ result);
+ }
+ }while (pdev!=NULL);
+ }
+
+ if(devs==0)
+ return -ENODEV; /* No cards found */
+
+ return 0;
+}
+
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ return wanxl_init();
+}
+
+
+void cleanup_module(void)
+{
+ sbe_card *card=first_card;
+
+ while (card) {
+ sbe_card *ptr=card;
+ card=card->next_card;
+ wanxl_destroy_card(ptr, 1);
+ }
+}
+
+#endif /* MODULE */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)