diff -ur --new-file old/atm/BUGS new/atm/BUGS
--- old/atm/BUGS	Wed Sep 23 19:29:03 1998
+++ new/atm/BUGS	Thu Oct  1 21:59:54 1998
@@ -1,4 +1,4 @@
-Known bugs and restrictions in version 0.44
+Known bugs and restrictions in version 0.45
 ===========================================
 
  - 2.1.117 kernel may not compile properly with sound enabled
@@ -10,3 +10,6 @@
    seems to be a bug in RedHat's glibc
  - CLIP interfaces must not be reconfigured while "up"
  - the "new" atmsigd leaks memory when tracing
+ - swc show  only displays manually configured VCs
+ - invoking a queuing discipline that overwrites skb->cb from within sch_atm
+   will yield unpredictable results if CONFIG_ATM_SKB is enabled
diff -ur --new-file old/atm/CHANGES new/atm/CHANGES
--- old/atm/CHANGES	Thu Sep 24 19:26:47 1998
+++ new/atm/CHANGES	Thu Oct  1 21:46:57 1998
@@ -1,3 +1,33 @@
+Version 0.44 to 0.45 (1-OCT-1998)
+====================
+
+Bug fixes
+---------
+
+ - ENI driver didn't do four-word bursts on RX for sizes < 8 words
+ - arequipad, atmarpd, bus, lecs, les, mpcd, sw_*, and zeppelin silently
+   ignored extra command-line arguments instead of complaining
+
+New features
+------------
+
+ - ENI: added configuration options to fine-tune burst sizes (in reponse to
+   incompatibility found by Dave Airlie)
+
+Other changes
+-------------
+
+ - changed the way how ATM-specific data is stored in skbs. Tentatively updated
+   the the stack, including drivers. Use CONFIG_ATM_SKB to enable the new-style
+   skbs.
+ - Rules.make no longer discards the previous value of LDLIBS
+ - sw_tcp now only establishes bi-directional VCs if both directions are really
+   requested in the QoS structure
+ - moved manual switch control from sw_tcp to the generic switch code; "tcpswc"
+   is now called "swc", the corresponding switch.conf clause is now
+   'control <path>' instead of 'option control "<path>"'
+
+
 Version 0.43 to 0.44 (24-SEP-1998)
 ====================
 
diff -ur --new-file old/atm/README new/atm/README
--- old/atm/README	Thu Sep 24 15:49:22 1998
+++ new/atm/README	Thu Oct  1 19:57:50 1998
@@ -1,4 +1,4 @@
-ATM on Linux, release 0.44 (alpha)        by Werner Almesberger, EPFL ICA
+ATM on Linux, release 0.45 (alpha)        by Werner Almesberger, EPFL ICA
 ============================================== Werner.Almesberger@epfl.ch
 
 This is experimental software. There are known major bugs and certainly
diff -ur --new-file old/atm/Rules.make new/atm/Rules.make
--- old/atm/Rules.make	Thu Sep 24 17:58:59 1998
+++ new/atm/Rules.make	Thu Oct  1 20:05:21 1998
@@ -56,7 +56,7 @@
 CFLAGS_LEX=$(CFLAGS_NOWARN) $(CFLAGS_OPT)
 CFLAGS_YACC=$(CFLAGS_NOWARN) $(CFLAGS_OPT) -DYY_USE_CONST
 LDFLAGS=
-LDLIBS=-L$(TOPDIR)/lib -latm -larequipa
+LDLIBS += -L$(TOPDIR)/lib -latm -larequipa
 LIBDEPS += $(TOPDIR)/lib/libatm.a $(TOPDIR)/lib/libarequipa.a
 YACC=bison -y -d #-v
 INSTROOT=
diff -ur --new-file old/atm/USAGE new/atm/USAGE
--- old/atm/USAGE	Thu Sep 24 18:47:31 1998
+++ new/atm/USAGE	Thu Oct  1 22:03:40 1998
@@ -1,4 +1,4 @@
-Usage instructions  -  ATM on Linux, release 0.44
+Usage instructions  -  ATM on Linux, release 0.45
 -------------------------------------------------
 
 For updates of ATM on Linux, please check the Web page at  
@@ -17,7 +17,7 @@
 In order to install this package, you need 
 
   - the package itself  
-    ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.44.tar.gz  
+    ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.45.tar.gz  
   - the Linux kernel, version 2.1.117, e.g. from  
     ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.1.117.tar.gz  
   - Perl, version 4 or 5 
@@ -33,7 +33,7 @@
 all the files listed above there. Then extract the ATM on Linux 
 distribution:
 
-tar xfz atm-0.44.tar.gz
+tar xfz atm-0.45.tar.gz
 
 and the kernel source:
 
@@ -84,6 +84,7 @@
 You should find the following new options:
 
 Asynchronous Transfer Mode (ATM, EXPERIMENTAL) (CONFIG_ATM)
+  Use "new" skb structure (CONFIG_ATM_SKB)
   Classical IP over ATM (CONFIG_ATM_CLIP)
     Do NOT send ICMP if no neighbour (CONFIG_ATM_CLIP_NO_ICMP)
   LAN Emulation (LANE) support (CONFIG_ATM_LANE)
@@ -91,16 +92,28 @@
 ATM over TCP (CONFIG_ATM_TCP)
 Efficient Networks ENI155P (CONFIG_ATM_ENI)
   Enable extended debugging (CONFIG_ATM_ENI_DEBUG)
+  Fine-tune burst settings (CONFIG_ATM_ENI_TUNE_BURST)
+    Enable 8W TX bursts (recommended) (CONFIG_ATM_ENI_BURST_TX_8W)
+    Enable 4W TX bursts (optional) (CONFIG_ATM_ENI_BURST_TX_4W)
+    Enable 2W TX bursts (optional) (CONFIG_ATM_ENI_BURST_TX_2W)
+    Enable 8W RX bursts (discouraged) (CONFIG_ATM_ENI_BURST_RX_8W)
+    Enable 4W RX bursts (recommended) (CONFIG_ATM_ENI_BURST_RX_4W)
+    Enable 2W RX bursts (optional) (CONFIG_ATM_ENI_BURST_RX_2W)
 ZeitNet ZN1221/ZN1225 (CONFIG_ATM_ZATM)
   Enable extended debugging (CONFIG_ATM_ZATM_DEBUG)
   Enable usec resolution timestamps (CONFIG_ATM_ZATM_EXACT_TS)
 IDT 77201 (NICStAR) (CONFIG_ATM_NICSTAR)
 
+The burst settings of the ENI driver can be fine-tuned. This may be 
+necessary if the default settings lead to buffer overruns in the PCI 
+chipset. See the on-line help on CONFIG_ATM_ENI_TUNE_BURST for a detailed 
+discussion of the implications of changing the burst settings.
+
 Note that the file drivers/atm/nicstar.h contains a few configurable 
 settings for the IDT 77201 driver.
 
 Some drivers can also be used with certain compatible cards. The latest 
-information about compatibile cards can be found at  
+information about compatible cards can be found at  
 http://lrcwww.epfl.ch/linux-atm/info.html 
 
 Then build your kernel and reboot.
diff -ur --new-file old/atm/VERSION new/atm/VERSION
--- old/atm/VERSION	Thu Sep 24 18:48:35 1998
+++ new/atm/VERSION	Thu Oct  1 20:09:08 1998
@@ -1 +1 @@
-0.44
+0.45
diff -ur --new-file old/atm/aqd/arequipad.c new/atm/aqd/arequipad.c
--- old/atm/aqd/arequipad.c	Fri Apr  4 16:59:27 1997
+++ new/atm/aqd/arequipad.c	Thu Oct  1 19:51:36 1998
@@ -59,6 +59,7 @@
 	    default:
 		usage(argv[0]);
 	}
+    if (argc != optind) usage(argv[0]);
     diag(COMPONENT,DIAG_INFO,"Linux Arequipa, version " VERSION);
     open_all();
     if (background) {
diff -ur --new-file old/atm/arpd/atmarpd.c new/atm/arpd/atmarpd.c
--- old/atm/arpd/atmarpd.c	Fri Jul 17 14:38:47 1998
+++ new/atm/arpd/atmarpd.c	Thu Oct  1 19:43:13 1998
@@ -78,6 +78,7 @@
 	    default:
 		usage(argv[0]);
 	}
+    if (argc != optind) usage(argv[0]);
     diag(COMPONENT,DIAG_INFO,"Linux ATM ARP, version " VERSION);
     if (chdir(dump_dir) < 0)
 	diag(COMPONENT,DIAG_ERROR,"chdir %s: %s",dump_dir,strerror(errno));
diff -ur --new-file old/atm/atm.patch new/atm/atm.patch
--- old/atm/atm.patch	Thu Sep 24 18:47:12 1998
+++ new/atm/atm.patch	Thu Oct  1 22:19:49 1998
@@ -20,8 +20,8 @@
  	if [ -f FS_MODULES    ]; then inst_mod FS_MODULES    fs;    fi; \
  	if [ -f NLS_MODULES   ]; then inst_mod NLS_MODULES   fs;    fi; \
 --- ref/Documentation/Configure.help	Wed Aug 19 23:37:58 1998
-+++ work/Documentation/Configure.help	Thu Sep 24 17:06:37 1998
-@@ -3166,6 +3166,93 @@
++++ work/Documentation/Configure.help	Thu Oct  1 17:21:27 1998
+@@ -3166,6 +3166,150 @@
    This is a backward compatibility option, choose Y for now.
    This option will be removed soon.
  
@@ -31,6 +31,11 @@
 +  to actually make use of ATM. See http://lrcwww.epfl.ch/linux-atm/ for
 +  further details.
 +
++Use "new" skb structure
++CONFIG_ATM_SKB
++  Store ATM-specific data in a "more compatible" way in socket buffers.
++  Use this options only with updated drivers.
++
 +Enable single-copy
 +CONFIG_MMU_HACKS
 +  Single-copy avoids intermediate buffering of AAL5 PDUs when using the
@@ -84,6 +89,58 @@
 +  extended debugging may create certain race conditions itself. Enable
 +  this ONLY if you suspect problems with the driver.
 +
++Fine-tune burst settings
++CONFIG_ATM_ENI_TUNE_BURST
++  In order to obtain good throughput, the ENI NIC can transfer multiple
++  words of data per PCI bus access cycle. Such a multi-word transfer is
++  called a burst.
++
++  The default settings for the burst sizes are suitable for most PCI
++  chipsets. However, in some cases, large bursts may overrun buffers in
++  the PCI chipset and cause data corruption. In such cases, large bursts
++  must be disabled and only (slower) small bursts can be used. The burst
++  sizes can be set independently in the send (TX) and receive (RX)
++  direction.
++
++  Note that enabling many different burst sizes in the same direction
++  may increase the cost of setting up a transfer such that the resulting
++  throughput is lower than when using only the largest available burst
++  size.
++
++Enable 8W TX bursts (recommended)
++CONFIG_ATM_ENI_BURST_TX_8W
++  Burst eight words at once in the send direction. This is the default
++  setting.
++
++Enable 4W TX bursts (optional)
++CONFIG_ATM_ENI_BURST_TX_4W
++  Burst four words at once in the send direction. You may want to try this
++  if you have disabled 8W bursts. Enabling 4W if 8W is also set may or may
++  not improve throughput.
++  
++Enable 2W TX bursts (optional)
++CONFIG_ATM_ENI_BURST_TX_2W
++  Burst two words at once in the send direction. You may want to try this
++  if you have disabled 4W and 8W bursts. Enabling 2W if 4W or 8W are also
++  set may or may not improve throughput.
++
++Enable 8W RX bursts (discouraged)
++CONFIG_ATM_ENI_BURST_RX_8W
++  Burst eight words at once in the receive direction. This may work with
++  recent PCI chipsets, but is known to fail with older chipsets, such as
++  the Intel Neptune series.
++
++Enable 4W RX bursts (recommended)
++CONFIG_ATM_ENI_BURST_RX_4W
++  Burst four words at once in the receive direction. This is the default
++  setting. Enabling 4W if 8W is also set may or may not improve throughput.
++
++Enable 2W RX bursts (optional)
++CONFIG_ATM_ENI_BURST_RX_2W
++  Burst two words at once in the receive direction. You may want to try
++  this if you have disabled 4W and 8W bursts. Enabling 2W if 4W or 8W are
++  also set may or may not improve throughput.
++
 +ZeitNet ZN1221/ZN1225
 +CONFIG_ATM_ZATM
 +  Driver for the ZeitNet ZN1221 (MMF) and ZN1225 (UTP-5) 155 Mbps ATM
@@ -170,8 +227,8 @@
  
  ifeq ($(CONFIG_AP1000),y)
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/Config.in	Fri Aug 21 02:32:42 1998
-@@ -0,0 +1,23 @@
++++ work/drivers/atm/Config.in	Mon Sep 28 16:51:29 1998
+@@ -0,0 +1,32 @@
 +#
 +# ATM device configuration
 +#
@@ -183,6 +240,15 @@
 +  tristate 'Efficient Networks ENI155P' CONFIG_ATM_ENI y
 +  if [ ! "$CONFIG_ATM_ENI" = "n" ]; then
 +    bool '  Enable extended debugging' CONFIG_ATM_ENI_DEBUG n
++    bool '  Fine-tune burst settings' CONFIG_ATM_ENI_TUNE_BURST n
++    if [ "$CONFIG_ATM_ENI_TUNE_BURST" = "y" ]; then
++      bool '    Enable 8W TX bursts (recommended)' CONFIG_ATM_ENI_BURST_TX_8W y
++      bool '    Enable 4W TX bursts (optional)' CONFIG_ATM_ENI_BURST_TX_4W n
++      bool '    Enable 2W TX bursts (optional)' CONFIG_ATM_ENI_BURST_TX_2W n
++      bool '    Enable 8W RX bursts (discouraged)' CONFIG_ATM_ENI_BURST_RX_8W n
++      bool '    Enable 4W RX bursts (recommended)' CONFIG_ATM_ENI_BURST_RX_4W y
++      bool '    Enable 2W RX bursts (optional)' CONFIG_ATM_ENI_BURST_RX_2W n
++    fi
 +  fi
 +  tristate 'ZeitNet ZN1221/ZN1225' CONFIG_ATM_ZATM y
 +  if [ ! "$CONFIG_ATM_ZATM" = "n" ]; then
@@ -649,8 +715,8 @@
 +#endif
 +    
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/eni.c	Wed Sep 16 15:49:46 1998
-@@ -0,0 +1,2144 @@
++++ work/drivers/atm/eni.c	Thu Oct  1 23:22:02 1998
+@@ -0,0 +1,2187 @@
 +/* drivers/atm/eni.c - Efficient Networks ENI155P device driver */
 + 
 +/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */
@@ -703,7 +769,6 @@
 + * - buffer space allocation algorithm is stupid
 + *   (RX: should be maxSDU+maxdelay*rate
 + *    TX: should be maxSDU+min(maxSDU,maxdelay*rate) )
-+ * - ATM DD interface doesn't know about CLP
 + * - doesn't support OAM cells
 + * - eni_put_free may hang if not putting memory fragments that _complete_
 + *   2^n block (never happens in real life, though)
@@ -717,6 +782,13 @@
 +#define DPRINTK(format,args...)
 +#endif
 +
++
++#ifndef CONFIG_ATM_ENI_TUNE_BURST
++#define CONFIG_ATM_ENI_BURST_TX_8W
++#define CONFIG_ATM_ENI_BURST_RX_4W
++#endif
++
++
 +#ifndef CONFIG_ATM_ENI_DEBUG
 +
 +
@@ -999,7 +1071,7 @@
 +			    vcc->vci,paddr);
 +		ENI_PRV_SIZE(skb) = size+skip;
 +		    /* PDU plus descriptor */
-+		skb->atm.vcc = vcc;
++		ATM_SKB(skb)->vcc = vcc;
 +	}
 +	j = 0;
 +	if ((eff && skip) || 1) { /* @@@ actually, skip is always == 1 ... */
@@ -1030,23 +1102,36 @@
 +			paddr += init << 2;
 +			words -= init;
 +		}
++#ifdef CONFIG_ATM_ENI_BURST_RX_8W  /* works only with *some* PCI chipsets ... */
 +		if (words & ~7) {
-+#if 0 /* works with *some* PCI chipsets ... */
 +			dma[j++] = MID_DT_8W | ((words >> 3) <<
 +			    MID_DMA_COUNT_SHIFT) | (vcc->vci <<
 +			    MID_DMA_VCI_SHIFT);
-+			dma[j++] = virt_to_bus(paddr);
++			dma[j++] = virt_to_bus((void *) paddr);
 +			paddr += (words & ~7) << 2;
 +			words &= 7;
-+#else
++		}
++#endif
++#ifdef CONFIG_ATM_ENI_BURST_RX_4W /* recommended */
++		if (words & ~3) {
 +			dma[j++] = MID_DT_4W | ((words >> 2) <<
 +			    MID_DMA_COUNT_SHIFT) | (vcc->vci <<
 +			    MID_DMA_VCI_SHIFT);
 +			dma[j++] = virt_to_bus((void *) paddr);
 +			paddr += (words & ~3) << 2;
 +			words &= 3;
++		}
 +#endif
++#ifdef CONFIG_ATM_ENI_BURST_RX_2W /* probably useless if RX_4W or RX_8W */
++		if (words & ~1) {
++			dma[j++] = MID_DT_2W | ((words >> 1) <<
++			    MID_DMA_COUNT_SHIFT) | (vcc->vci <<
++			    MID_DMA_VCI_SHIFT);
++			dma[j++] = virt_to_bus((void *) paddr);
++			paddr += (words & ~1) << 2;
++			words &= 1;
 +		}
++#endif
 +		if (words) {
 +			dma[j++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT)
 +			    | (vcc->vci << MID_DMA_VCI_SHIFT);
@@ -1360,7 +1445,7 @@
 +		EVENT("dequeued (size=%ld,pos=0x%lx)\n",ENI_PRV_SIZE(skb),
 +		    ENI_PRV_POS(skb));
 +rx_dequeued++;
-+		vcc = skb->atm.vcc;
++		vcc = ATM_SKB(skb)->vcc;
 +		eni_vcc = ENI_VCC(vcc);
 +		first = 0;
 +		vci_dsc = eni_dev->vci+(vcc->vci << 2);
@@ -1560,6 +1645,7 @@
 +		paddr += init << 2;
 +		words -= init;
 +	}
++#ifdef CONFIG_ATM_ENI_BURST_TX_8W /* recommended */
 +	if (words & ~7) {
 +		DPRINTK("put_dma: %lx DMA: %d*8/%d words\n",paddr,words >> 3,
 +		    words);
@@ -1569,6 +1655,29 @@
 +		paddr += (words & ~7) << 2;
 +		words &= 7;
 +	}
++#endif
++#ifdef CONFIG_ATM_ENI_BURST_TX_4W /* probably useless if TX_8W */
++	if (words & ~3) {
++		DPRINTK("put_dma: %lx DMA: %d*4/%d words\n",paddr,words >> 2,
++		    words);
++		dma[(*j)++] = MID_DT_4W | ((words >> 2) << MID_DMA_COUNT_SHIFT)
++		    | (chan << MID_DMA_CHAN_SHIFT);
++		dma[(*j)++] = virt_to_bus((void *) paddr);
++		paddr += (words & ~3) << 2;
++		words &= 3;
++	}
++#endif
++#ifdef CONFIG_ATM_ENI_BURST_TX_2W /* probably useless if TX_4W or TX_8W */
++	if (words & ~1) {
++		DPRINTK("put_dma: %lx DMA: %d*2/%d words\n",paddr,words >> 1,
++		    words);
++		dma[(*j)++] = MID_DT_2W | ((words >> 1) << MID_DMA_COUNT_SHIFT)
++		    | (chan << MID_DMA_CHAN_SHIFT);
++		dma[(*j)++] = virt_to_bus((void *) paddr);
++		paddr += (words & ~1) << 2;
++		words &= 1;
++	}
++#endif
 +	if (words) {
 +		DPRINTK("put_dma: %lx DMA: %d words\n",paddr,words);
 +		dma[(*j)++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT) |
@@ -1598,7 +1707,7 @@
 +	DPRINTK(">do_tx\n");
 +	NULLCHECK(skb);
 +	EVENT("do_tx: skb=0x%lx, %ld bytes\n",(unsigned long) skb,skb->len);
-+	vcc = skb->atm.vcc;
++	vcc = ATM_SKB(skb)->vcc;
 +	NULLCHECK(vcc);
 +	eni_dev = ENI_DEV(vcc->dev);
 +	NULLCHECK(eni_dev);
@@ -1653,9 +1762,9 @@
 +	dma_rd = readl(eni_dev->reg+MID_DMA_RD_TX);
 +	dma_size = 3; /* JK for descriptor and final fill, plus final size
 +			 mis-alignment fix */
-+DPRINTK("iovcnt = %d\n",skb->atm.iovcnt);
-+	if (!skb->atm.iovcnt) dma_size += 5;
-+	else dma_size += 5*skb->atm.iovcnt;
++DPRINTK("iovcnt = %d\n",ATM_SKB(skb)->iovcnt);
++	if (!ATM_SKB(skb)->iovcnt) dma_size += 5;
++	else dma_size += 5*ATM_SKB(skb)->iovcnt;
 +	if (dma_size > TX_DMA_BUF) {
 +		printk(KERN_CRIT DEV_LABEL "(itf %d): needs %d DMA entries "
 +		    "(got only %d)\n",vcc->dev->number,dma_size,TX_DMA_BUF);
@@ -1673,7 +1782,7 @@
 +	     MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) |
 +	     MID_DT_JK;
 +	j++;
-+	if (!skb->atm.iovcnt)
++	if (!ATM_SKB(skb)->iovcnt)
 +		if (aal5)
 +			put_dma(tx->index,eni_dev->dma,&j,
 +			    (unsigned long) skb->data,skb->len);
@@ -1681,7 +1790,7 @@
 +			    (unsigned long) skb->data+4,skb->len-4);
 +	else {
 +DPRINTK("doing direct send\n");
-+		for (i = 0; i < skb->atm.iovcnt; i++)
++		for (i = 0; i < ATM_SKB(skb)->iovcnt; i++)
 +			put_dma(tx->index,eni_dev->dma,&j,(unsigned long)
 +			    ((struct iovec *) skb->data)[i].iov_base,
 +			    ((struct iovec *) skb->data)[i].iov_len);
@@ -1703,7 +1812,7 @@
 +/*printk("dsc = 0x%08lx\n",tx->send[tx->tx_pos]);*/
 +	tx->send[(tx->tx_pos+1) & (tx->words-1)] = (vcc->vci <<
 +	    MID_SEG_VCI_SHIFT) | (aal5 ? 0 : (skb->data[3] & 0xf)) |
-+	    (skb->atm.atm_options & ATM_ATMOPT_CLP ? MID_SEG_CLP : 0);
++	    (ATM_SKB(skb)->atm_options & ATM_ATMOPT_CLP ? MID_SEG_CLP : 0);
 +	DPRINTK("size: %ld, len:%d\n",size,skb->len);
 +	if (aal5)
 +		tx->send[(tx->tx_pos+size-AAL5_TRAILER) & (tx->words-1)] =
@@ -1762,7 +1871,7 @@
 +	eni_dev = ENI_DEV(dev);
 +	NULLCHECK(eni_dev);
 +	while ((skb = skb_dequeue(&eni_dev->tx_queue))) {
-+		vcc = skb->atm.vcc;
++		vcc = ATM_SKB(skb)->vcc;
 +		NULLCHECK(vcc);
 +		tx = ENI_VCC(vcc)->tx;
 +		NULLCHECK(ENI_VCC(vcc)->tx);
@@ -2526,7 +2635,7 @@
 +	    (struct sk_buff *) &eni_dev->tx_queue; skb = skb->next) {
 +		u32 *dsc;
 +
-+		if (skb->atm.vcc != vcc) continue;
++		if (ATM_SKB(skb)->vcc != vcc) continue;
 +		dsc = tx->send+ENI_PRV_POS(skb);
 +		*dsc = (*dsc & ~(MID_SEG_RATE | MID_SEG_PR)) |
 +		    (tx->prescaler << MID_SEG_PR_SHIFT) |
@@ -2614,7 +2723,7 @@
 +		*(u32 *) skb->data = htonl(*(u32 *) skb->data);
 +	}
 +submitted++;
-+	skb->atm.vcc = vcc;
++	ATM_SKB(skb)->vcc = vcc;
 +	save_flags(flags);
 +	cli(); /* brute force */
 +	if (skb_peek(&ENI_VCC(vcc)->tx->backlog) || do_tx(skb)) {
@@ -2796,8 +2905,8 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/eni.h	Thu Sep 17 17:08:54 1998
-@@ -0,0 +1,114 @@
++++ work/drivers/atm/eni.h	Thu Oct  1 17:09:22 1998
+@@ -0,0 +1,115 @@
 +/* drivers/atm/eni.h - Efficient Networks ENI155P device driver declarations */
 + 
 +/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */
@@ -2821,8 +2930,8 @@
 +
 +#define UBR_BUFFER	(128*1024)	/* UBR buffer size */
 +
-+#define RX_DMA_BUF	 5		/* burst and skip a few things */
-+#define TX_DMA_BUF	60		/* should be enough for 64 kB */
++#define RX_DMA_BUF	  8		/* burst and skip a few things */
++#define TX_DMA_BUF	100		/* should be enough for 64 kB */
 +
 +
 +struct eni_free {
@@ -2904,6 +3013,7 @@
 +
 +
 +struct eni_skb_prv {
++	struct atm_skb_data _;		/* reserved */
 +	unsigned long pos;		/* position of next descriptor */
 +	int size;			/* PDU size in reassembly buffer */
 +};
@@ -3181,7 +3291,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/nicstar.h	Wed Sep  9 21:02:13 1998
++++ work/drivers/atm/nicstar.h	Thu Oct  1 17:09:30 1998
 @@ -0,0 +1,753 @@
 +/******************************************************************************
 + *
@@ -3819,125 +3929,3012 @@
 +
 +
 +
-+/* Device driver structures ***************************************************/
++/* Device driver structures ***************************************************/
++
++
++typedef struct tsq_info
++{
++   void *org;
++   ns_tsi *base;
++   ns_tsi *next;
++   ns_tsi *last;
++} tsq_info;
++
++
++typedef struct scq_info
++{
++   void *org;
++   ns_scqe *base;
++   ns_scqe *last;
++   ns_scqe *next;
++   volatile ns_scqe *tail;		/* Not related to the nicstar register */
++   unsigned num_entries;
++   struct sk_buff **skb;		/* Pointer to an array of pointers
++                                           to the sk_buffs used for tx */
++   u32 scd;				/* SRAM address of the corresponding
++                                           SCD */
++   int tbd_count;			/* Only meaningful on variable rate */
++   struct wait_queue *scqfull_waitq;
++   volatile char full;			/* SCQ full indicator */
++} scq_info;
++
++
++
++typedef struct rsq_info
++{
++   void *org;
++   ns_rsqe *base;
++   ns_rsqe *next;
++   ns_rsqe *last;
++} rsq_info;
++
++
++typedef struct skb_pool
++{
++   volatile int count;			/* number of buffers in the queue */
++   struct sk_buff_head queue;
++} skb_pool;
++
++/* NOTE: for small and large buffer pools, the count is not used, as the
++         actual value used for buffer management is the one read from the
++	 card. */
++
++
++typedef struct vc_map
++{
++   volatile int tx:1;				/* TX vc? */
++   volatile int rx:1;				/* RX vc? */
++   struct atm_vcc *tx_vcc, *rx_vcc;
++   struct sk_buff *rx_iov;		/* RX iovector skb */
++   scq_info *scq;			/* To keep track of the SCQ */
++   u32 cbr_scd;				/* SRAM address of the corresponding
++               				   SCD. 0x00000000 for UBR/VBR/ABR */
++   int tbd_count;
++} vc_map;
++
++
++typedef struct ns_dev
++{
++   int index;				/* Card ID to the device driver */
++   int sram_size;			/* In k x 32bit words. 32 or 128 */
++   u32 membase;				/* Card's memory base address */
++   unsigned long max_pcr;
++   int rct_size;			/* Number of entries */
++   int vpibits;
++   int vcibits;
++   struct pci_dev *pcidev;
++   struct atm_dev *atmdev;
++   tsq_info tsq;
++   rsq_info rsq;
++   scq_info *scq0, *scq1, *scq2;	/* VBR SCQs */
++   skb_pool sbpool;			/* Small buffers */
++   skb_pool lbpool;			/* Large buffers */
++   skb_pool hbpool;			/* Pre-allocated huge buffers */
++   skb_pool iovpool;			/* iovector buffers */
++   volatile int efbie;			/* Empty free buf. queue int. enabled */
++   volatile u32 tst_addr;		/* SRAM address of the TST in use */
++   volatile int tst_free_entries;
++   vc_map vcmap[NS_MAX_RCTSIZE];
++   vc_map *tste2vc[NS_TST_NUM_ENTRIES];
++   vc_map *scd2vc[NS_FRSCD_NUM];
++   buf_nr sbnr;
++   buf_nr lbnr;
++   buf_nr hbnr;
++   buf_nr iovnr;
++   int sbfqc;
++   int lbfqc;
++   u32 sm_handle;
++   u32 sm_addr;
++   u32 lg_handle;
++   u32 lg_addr;
++   struct sk_buff *rcbuf;		/* Current raw cell buffer */
++   u32 rawch;				/* Raw cell queue head */
++   unsigned intcnt;			/* Interrupt counter */
++   volatile int in_handler: 1;
++   volatile int in_poll: 1;
++} ns_dev;
++
++
++   /* NOTE: Each tste2vc entry relates a given TST entry to the corresponding
++            CBR vc. If the entry is not allocated, it must be NULL.
++	    
++	    There are two TSTs so the driver can modify them on the fly
++	    without stopping the transmission.
++	    
++	    scd2vc allows us to find out unused fixed rate SCDs, because
++	    they must have a NULL pointer here. */
++
++
++#endif /* _LINUX_NICSTAR_H_ */
+--- /dev/null	Tue Jan  1 05:00:00 1980
++++ work/drivers/atm/nicstar.c	Thu Oct  1 16:26:36 1998
+@@ -0,0 +1,2884 @@
++/******************************************************************************
++ *
++ * nicstar.c
++ *
++ * Device driver supporting CBR for NICStAR based cards.
++ *
++ * IMPORTANT: The included file nicstarmac.c was NOT WRITTEN BY ME.
++ *            It was taken from the frle-0.22 device driver.
++ *            As the file doesn't have a copyright notice, in the file
++ *            nicstarmac.copyright I put the copyright notice from the
++ *            frle-0.22 device driver.
++ *            Some code is based on the nicstar driver by M. Welsh.
++ *
++ * Author: Rui Prior
++ *
++ * (C) INESC 1998
++ *
++ ******************************************************************************/
++
++
++/* Header files ***************************************************************/
++
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/skbuff.h>
++#include <linux/atmdev.h>
++#include <linux/atm.h>
++#include <linux/pci.h>
++#include <linux/types.h>
++#include <linux/string.h>
++#include <linux/delay.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include "nicstar.h"
++#include "nicstarmac.h"
++
++
++/* Additional code ************************************************************/
++
++#include "nicstarmac.c"
++
++
++/* Configurable parameters ****************************************************/
++
++#undef PHY_LOOPBACK
++#undef TX_DEBUG
++#undef RX_DEBUG
++#undef GENERAL_DEBUG
++#undef EXTRA_DEBUG
++
++#undef NS_USE_DESTRUCTORS /* For now keep this undefined unless you know
++                             you're going to use only raw ATM */
++
++
++/* Do not touch these *********************************************************/
++
++#ifdef TX_DEBUG
++#define TXPRINTK(args...) printk(args)
++#else
++#define TXPRINTK(args...)
++#endif /* TX_DEBUG */
++
++#ifdef RX_DEBUG
++#define RXPRINTK(args...) printk(args)
++#else
++#define RXPRINTK(args...)
++#endif /* RX_DEBUG */
++
++#ifdef GENERAL_DEBUG
++#define PRINTK(args...) printk(args)
++#else
++#define PRINTK(args...)
++#endif /* GENERAL_DEBUG */
++
++#ifdef EXTRA_DEBUG
++#define XPRINTK(args...) printk(args)
++#else
++#define XPRINTK(args...)
++#endif /* EXTRA_DEBUG */
++
++
++/* Macros *********************************************************************/
++
++#define MAX(a,b) ((a) > (b) ? (a) : (b))
++#define MIN(a,b) ((a) < (b) ? (a) : (b))
++
++#define CMD_BUSY(card) (readl((card)->membase + STAT) & NS_STAT_CMDBZ)
++
++#define NS_DELAY mdelay(1)
++
++#define ALIGN_ADDRESS(addr, alignment) \
++        ((((u32) (addr)) + (((u32) (alignment)) - 1)) & ~(((u32) (alignment)) - 1))
++
++#undef CEIL(d)
++
++
++/* Version definition *********************************************************/
++/*
++#include <linux/version.h>
++char kernel_version[] = UTS_RELEASE;
++*/
++
++/* Function declarations ******************************************************/
++
++static u32 ns_read_sram(ns_dev *card, u32 sram_address);
++static void ns_write_sram(ns_dev *card, u32 sram_address, u32 *value, int count);
++static int ns_init_card(int i, struct pci_dev *pcidev);
++static void ns_init_card_error(ns_dev *card, int error);
++static scq_info *get_scq(int size, u32 scd);
++static void free_scq(scq_info *scq, struct atm_vcc *vcc);
++static void push_rxbufs(ns_dev *card, u32 type, u32 handle1, u32 addr1,
++                       u32 handle2, u32 addr2);
++static void ns_irq_handler(int irq, void *dev_id, struct pt_regs *regs);
++static int ns_open(struct atm_vcc *vcc, short vpi, int vci);
++static void ns_close(struct atm_vcc *vcc);
++static void fill_tst(ns_dev *card, int n, vc_map *vc);
++static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb);
++static int push_scqe(ns_dev *card, vc_map *vc, scq_info *scq, ns_scqe *tbd,
++                     struct sk_buff *skb);
++static void process_tsq(ns_dev *card);
++static void drain_scq(ns_dev *card, scq_info *scq, int pos);
++static void process_rsq(ns_dev *card);
++static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe);
++#ifdef NS_USE_DESTRUCTORS
++static void ns_sb_destructor(struct sk_buff *sb);
++static void ns_lb_destructor(struct sk_buff *lb);
++static void ns_hb_destructor(struct sk_buff *hb);
++#endif /* NS_USE_DESTRUCTORS */
++static void recycle_rx_buf(ns_dev *card, struct sk_buff *skb);
++static void recycle_iovec_rx_bufs(ns_dev *card, struct iovec *iov, int count);
++static void recycle_iov_buf(ns_dev *card, struct sk_buff *iovb);
++static void dequeue_sm_buf(ns_dev *card, struct sk_buff *sb);
++static void dequeue_lg_buf(ns_dev *card, struct sk_buff *lb);
++static int ns_proc_read(struct atm_dev *dev, loff_t *pos, char *page);
++static int ns_ioctl(struct atm_dev *dev, unsigned int cmd, void *arg);
++static void which_list(ns_dev *card, struct sk_buff *skb);
++static void ns_poll(unsigned long arg);
++
++
++/* Global variables ***********************************************************/
++
++static struct ns_dev *cards[NS_MAX_CARDS];
++static unsigned num_cards = 0;
++static struct atmdev_ops atm_ops =
++{
++   NULL,		/* dev_close */
++   ns_open,		/* open */
++   ns_close,		/* close */
++   ns_ioctl,		/* ioctl */
++   NULL,		/* getsockopt */
++   NULL,		/* setsockopt */
++   ns_send,		/* send */
++   NULL,		/* sg_send */
++   NULL,		/* send_oam */
++   NULL,		/* phy_put */
++   NULL,		/* phy_get */
++   NULL,		/* feedback */
++   NULL,		/* change_qos */
++   NULL,		/* free_rx_skb */
++   ns_proc_read		/* proc_read */
++};
++static struct timer_list ns_timer;
++
++
++/* Functions*******************************************************************/
++
++#ifdef MODULE
++
++int init_module(void)
++{
++   int i;
++   unsigned error = 0;	/* Initialized to remove compile warning */
++   struct pci_dev *pcidev;
++
++   XPRINTK("nicstar: init_module() called.\n");
++   if(!pci_present())
++   {
++      printk("nicstar: no PCI subsystem found.\n");
++      return -EIO;
++   }
++
++   for(i = 0; i < NS_MAX_CARDS; i++)
++      cards[i] = NULL;
++
++   pcidev = NULL;
++   for(i = 0; i < NS_MAX_CARDS; i++)
++   {
++      if ((pcidev = pci_find_device(PCI_VENDOR_ID_IDT,
++                                    PCI_DEVICE_ID_IDT_IDT77201,
++                                    pcidev)) == NULL)
++         break;
++
++      error = ns_init_card(i, pcidev);
++      if (error)
++         i--;		/* Try to find another card but don't increment index */
++   }
++
++   if (i == 0)
++   {
++      if (!error)
++      {
++         printk("nicstar: no cards found.\n");
++         return -ENXIO;
++      }
++      else
++         return -EIO;
++   }
++   TXPRINTK("nicstar: TX debug enabled.\n");
++   RXPRINTK("nicstar: RX debug enabled.\n");
++   PRINTK("nicstar: General debug enabled.\n");
++#ifdef PHY_LOOPBACK
++   printk("nicstar: using PHY loopback.\n");
++#endif /* PHY_LOOPBACK */
++   XPRINTK("nicstar: init_module() returned.\n");
++
++   ns_timer.next = NULL;
++   ns_timer.prev = NULL;
++   ns_timer.expires = jiffies + NS_POLL_PERIOD;
++   ns_timer.data = 0UL;
++   ns_timer.function = ns_poll;
++   add_timer(&ns_timer);
++   return 0;
++}
++
++
++
++void cleanup_module(void)
++{
++   int i, j;
++   unsigned short pci_command;
++   ns_dev *card;
++   struct sk_buff *hb;
++   struct sk_buff *iovb;
++   struct sk_buff *lb;
++   struct sk_buff *sb;
++   
++   XPRINTK("nicstar: cleanup_module() called.\n");
++
++   if (MOD_IN_USE)
++      printk("nicstar: module in use, remove delayed.\n");
++
++   del_timer(&ns_timer);
++
++   for (i = 0; i < NS_MAX_CARDS; i++)
++   {
++      if (cards[i] == NULL)
++         continue;
++
++      card = cards[i];
++
++      /* Stop everything */
++      writel(0x00000000, card->membase + CFG);
++
++      /* De-register device */
++      atm_dev_deregister(card->atmdev);
++
++      /* Disable memory mapping and busmastering */
++      if (pci_read_config_word(card->pcidev, PCI_COMMAND, &pci_command) != 0)
++      {
++         printk("nicstar%d: can't read PCI_COMMAND.\n", i);
++      }
++      pci_command &= ~(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
++      if (pci_write_config_word(card->pcidev, PCI_COMMAND, pci_command) != 0)
++      {
++         printk("nicstar%d: can't write PCI_COMMAND.\n", i);
++      }
++      
++      /* Free up resources */
++      j = 0;
++      PRINTK("nicstar%d: freeing %d huge buffers.\n", i, card->hbpool.count);
++      while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL)
++      {
++         kfree_skb(hb);
++	 j++;
++      }
++      PRINTK("nicstar%d: %d huge buffers freed.\n", i, j);
++      j = 0;
++      PRINTK("nicstar%d: freeing %d iovec buffers.\n", i, card->iovpool.count);
++      while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL)
++      {
++         kfree_skb(iovb);
++         j++;
++      }
++      PRINTK("nicstar%d: %d iovec buffers freed.\n", i, j);
++      while ((lb = skb_dequeue(&card->lbpool.queue)) != NULL)
++         kfree_skb(lb);
++      while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL)
++         kfree_skb(sb);
++      free_scq(card->scq0, NULL);
++      for (j = 0; j < NS_FRSCD_NUM; j++)
++      {
++         if (card->scd2vc[j] != NULL)
++	    free_scq(card->scd2vc[j]->scq, card->scd2vc[j]->tx_vcc);
++      }
++      kfree(card->rsq.org);
++      kfree(card->tsq.org);
++      free_irq(card->pcidev->irq, card);
++      iounmap((void *) card->membase);
++      kfree(card);
++      
++   }
++   XPRINTK("nicstar: cleanup_module() returned.\n");
++}
++
++
++#else
++
++__initfunc(int nicstar_detect(void))
++{
++   int i;
++   unsigned error = 0;	/* Initialized to remove compile warning */
++   struct pci_dev *pcidev;
++
++   if(!pci_present())
++   {
++      printk("nicstar: no PCI subsystem found.\n");
++      return -EIO;
++   }
++
++   for(i = 0; i < NS_MAX_CARDS; i++)
++      cards[i] = NULL;
++
++   pcidev = NULL;
++   for(i = 0; i < NS_MAX_CARDS; i++)
++   {
++      if ((pcidev = pci_find_device(PCI_VENDOR_ID_IDT,
++                                    PCI_DEVICE_ID_IDT_IDT77201,
++                                    pcidev)) == NULL)
++         break;
++
++      error = ns_init_card(i, pcidev);
++      if (error)
++         i--;		/* Try to find another card but don't increment index */
++   }
++
++   if (i == 0 && error)
++      return -EIO;
++
++   TXPRINTK("nicstar: TX debug enabled.\n");
++   RXPRINTK("nicstar: RX debug enabled.\n");
++   PRINTK("nicstar: General debug enabled.\n");
++#ifdef PHY_LOOPBACK
++   printk("nicstar: using PHY loopback.\n");
++#endif /* PHY_LOOPBACK */
++   XPRINTK("nicstar: init_module() returned.\n");
++
++   return i;
++}
++
++
++#endif /* MODULE */
++
++
++static u32 ns_read_sram(ns_dev *card, u32 sram_address)
++{
++   unsigned long flags;
++   u32 data;
++   sram_address <<= 2;
++   sram_address &= 0x0007FFFC;	/* address must be dword aligned */
++   sram_address |= 0x50000000;	/* SRAM read command */
++   save_flags(flags); cli();
++   while (CMD_BUSY(card));
++   writel(sram_address, card->membase + CMD);
++   while (CMD_BUSY(card));
++   data = readl(card->membase + DR0);
++   restore_flags(flags);
++   return data;
++}
++
++
++   
++static void ns_write_sram(ns_dev *card, u32 sram_address, u32 *value, int count)
++{
++   unsigned long flags;
++   int i, c;
++   count--;	/* count range now is 0..3 instead of 1..4 */
++   c = count;
++   c <<= 2;	/* to use increments of 4 */
++   save_flags(flags); cli();
++   while (CMD_BUSY(card));
++   for (i = 0; i <= c; i += 4)
++      writel(*(value++), card->membase + i);
++   /* Note: DR# registers are the first 4 dwords in nicstar's memspace,
++            so card->membase + DR0 == card->membase */
++   sram_address <<= 2;
++   sram_address &= 0x0007FFFC;
++   sram_address |= (0x40000000 | count);
++   writel(sram_address, card->membase + CMD);
++   restore_flags(flags);
++}
++
++
++static int ns_init_card(int i, struct pci_dev *pcidev)
++{
++   int j;
++   struct ns_dev *card;
++   unsigned short pci_command;
++   unsigned char pci_latency;
++   unsigned error;
++   u32 data;
++   u32 u32d[4];
++   u32 ns_cfg_rctsize;
++   int bcount;
++
++   error = 0;
++
++   if ((card = kmalloc(sizeof(ns_dev), GFP_KERNEL)) == NULL)
++   {
++      printk("nicstar%d: can't allocate memory for device structure.\n", i);
++      error = 2;
++      ns_init_card_error(card, error);
++      return error;
++   }
++   cards[i] = card;
++      
++   card->index = i;
++   card->pcidev = pcidev;
++   card->membase = (u32) (pcidev->base_address[1] & PCI_BASE_ADDRESS_MEM_MASK);
++   card->membase = (u32) ioremap(card->membase, NS_IOREMAP_SIZE);
++   if (card->membase == (u32) (NULL))
++   {
++      printk("nicstar%d: can't ioremap() membase.\n",i);
++      error = 3;
++      ns_init_card_error(card, error);
++      return error;
++   }
++   PRINTK("nicstar%d: membase at 0x%x.\n", i, card->membase);
++
++   if (pci_read_config_word(pcidev, PCI_COMMAND, &pci_command) != 0)
++   {
++      printk("nicstar%d: can't read PCI_COMMAND.\n", i);
++      error = 4;
++      ns_init_card_error(card, error);
++      return error;
++   }
++   pci_command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
++   if (pci_write_config_word(pcidev, PCI_COMMAND, pci_command) != 0)
++   {
++      printk("nicstar%d: can't write PCI_COMMAND.\n", i);
++      error = 5;
++      ns_init_card_error(card, error);
++      return error;
++   }
++
++   if (pci_read_config_byte(pcidev, PCI_LATENCY_TIMER, &pci_latency) != 0)
++   {
++      printk("nicstar%d: can't read PCI latency timer.\n", i);
++      error = 6;
++      ns_init_card_error(card, error);
++      return error;
++   }
++   if (pci_latency < NS_PCI_LATENCY)
++   {
++      PRINTK("nicstar%d: setting PCI latency timer to %d.\n", i, NS_PCI_LATENCY);
++      for (j = 1; j < 4; j++)
++      {
++         if (pci_write_config_byte(pcidev, PCI_LATENCY_TIMER, NS_PCI_LATENCY) != 0);
++	    break;
++      }
++      if (j == 10)
++      {
++         printk("nicstar%d: can't set PCI latency timer to %d.\n", i, NS_PCI_LATENCY);
++         error = 7;
++         ns_init_card_error(card, error);
++	 return error;
++      }
++   }
++      
++   /* Clear timer overflow */
++   data = readl(card->membase + STAT);
++   if (data & NS_STAT_TMROF)
++      writel(NS_STAT_TMROF, card->membase + STAT);
++
++   /* Software reset */
++   writel(NS_CFG_SWRST, card->membase + CFG);
++   NS_DELAY;
++   writel(0x00000000, card->membase + CFG);
++
++   /* PHY reset */
++   writel(0x00000008, card->membase + GP);
++   NS_DELAY;
++   writel(0x00000001, card->membase + GP);
++   NS_DELAY;
++   while (CMD_BUSY(card));
++   writel(NS_CMD_WRITE_UTILITY | 0x00000100, card->membase + CMD);	/* Sync UTOPIA with SAR clock */
++   NS_DELAY;
++      
++   /* Detect PHY type */
++   while (CMD_BUSY(card));
++   writel(NS_CMD_READ_UTILITY | 0x00000200, card->membase + CMD);
++   while (CMD_BUSY(card));
++   data = readl(card->membase + DR0);
++   if (data == 0x00000009)
++   {
++      printk("nicstar%d: PHY seems to be 25 Mbps.\n", i);
++      card->max_pcr = IDT_25_PCR;
++      while(CMD_BUSY(card));
++      writel(0x00000008, card->membase + DR0);
++      writel(NS_CMD_WRITE_UTILITY | 0x00000200, card->membase + CMD);
++      /* Clear an eventual pending interrupt */
++      writel(NS_STAT_SFBQF, card->membase + STAT);
++#ifdef PHY_LOOPBACK
++      while(CMD_BUSY(card));
++      writel(0x00000022, card->membase + DR0);
++      writel(NS_CMD_WRITE_UTILITY | 0x00000202, card->membase + CMD);
++#endif /* PHY_LOOPBACK */
++   }
++   else if (data == 0x00000030)
++   {
++      printk("nicstar%d: PHY seems to be 155 Mbps.\n", i);
++      card->max_pcr = ATM_OC3_PCR;
++#ifdef PHY_LOOPBACK
++      while(CMD_BUSY(card));
++      writel(0x00000002, card->membase + DR0);
++      writel(NS_CMD_WRITE_UTILITY | 0x00000205, card->membase + CMD);
++#endif /* PHY_LOOPBACK */
++   }
++   else
++   {
++      printk("nicstar%d: can't determine PHY type.\n", i);
++      error = 8;
++      ns_init_card_error(card, error);
++      return error;
++   }
++   writel(0x00000000, card->membase + GP);
++
++   /* Determine SRAM size */
++   data = 0x76543210;
++   ns_write_sram(card, 0x1C003, &data, 1);
++   data = 0x89ABCDEF;
++   ns_write_sram(card, 0x14003, &data, 1);
++   if (ns_read_sram(card, 0x14003) == 0x89ABCDEF &&
++       ns_read_sram(card, 0x1C003) == 0x76543210)
++       card->sram_size = 128;
++   else
++      card->sram_size = 32;
++   PRINTK("nicstar%d: %dK x 32bit SRAM size.\n", i, card->sram_size);
++
++   card->rct_size = NS_MAX_RCTSIZE;
++
++#if (NS_MAX_RCTSIZE == 4096)
++   if (card->sram_size == 128)
++      printk("nicstar%d: limiting maximum VCI. See NS_MAX_RCTSIZE in nicstar.h\n", i);
++#elif (NS_MAX_RCTSIZE == 16384)
++   if (card->sram_size == 32)
++   {
++      printk("nicstar%d: wasting memory. See NS_MAX_RCTSIZE in nicstar.h\n", i);
++      card->rct_size = 4096;
++   }
++#else
++#error NS_MAX_RCTSIZE must be either 4096 or 16384 in nicstar.c
++#endif
++
++   card->vpibits = NS_VPIBITS;
++   if (card->rct_size == 4096)
++      card->vcibits = 12 - NS_VPIBITS;
++   else /* card->rct_size == 16384 */
++      card->vcibits = 14 - NS_VPIBITS;
++
++#ifdef ESI_FROM_EPROM
++   /* Initialize the nicstar eeprom/eprom stuff, for the MAC addr */
++   nicstar_init_eprom(card->membase);
++#endif /* ESI_FROM_EPROM */
++
++   if (request_irq(pcidev->irq, &ns_irq_handler, SA_INTERRUPT, "nicstar", card) != 0)
++   {
++      printk("nicstar%d: can't allocate IRQ.\n", i);
++      error = 9;
++      ns_init_card_error(card, error);
++      return error;
++   }
++
++   /* Set the VPI/VCI MSb mask to zero so we can receive OAM cells */
++   writel(0x00000000, card->membase + VPM);
++      
++   /* Initialize TSQ */
++   card->tsq.org = kmalloc(NS_TSQSIZE + NS_TSQ_ALIGNMENT, GFP_KERNEL);
++   if (card->tsq.org == NULL)
++   {
++      printk("nicstar%d: can't allocate TSQ.\n", i);
++      error = 10;
++      ns_init_card_error(card, error);
++      return error;
++   }
++   card->tsq.base = (ns_tsi *) ALIGN_ADDRESS(card->tsq.org, NS_TSQ_ALIGNMENT);
++   card->tsq.next = card->tsq.base;
++   card->tsq.last = card->tsq.base + (NS_TSQ_NUM_ENTRIES - 1);
++   for (j = 0; j < NS_TSQ_NUM_ENTRIES; j++)
++      ns_tsi_init(card->tsq.base + j);
++   writel(0x00000000, card->membase + TSQH);
++   writel((u32) virt_to_bus(card->tsq.base), card->membase + TSQB);
++   PRINTK("nicstar%d: TSQ base at 0x%x  0x%x  0x%x.\n", i, (u32) card->tsq.base,
++          (u32) virt_to_bus(card->tsq.base), readl(card->membase + TSQB));
++      
++   /* Initialize RSQ */
++   card->rsq.org = kmalloc(NS_RSQSIZE + NS_RSQ_ALIGNMENT, GFP_KERNEL);
++   if (card->rsq.org == NULL)
++   {
++      printk("nicstar%d: can't allocate RSQ.\n", i);
++      error = 11;
++      ns_init_card_error(card, error);
++      return error;
++   }
++   card->rsq.base = (ns_rsqe *) ALIGN_ADDRESS(card->rsq.org, NS_RSQ_ALIGNMENT);
++   card->rsq.next = card->rsq.base;
++   card->rsq.last = card->rsq.base + (NS_RSQ_NUM_ENTRIES - 1);
++   for (j = 0; j < NS_RSQ_NUM_ENTRIES; j++)
++      ns_rsqe_init(card->rsq.base + j);
++   writel(0x00000000, card->membase + RSQH);
++   writel((u32) virt_to_bus(card->rsq.base), card->membase + RSQB);
++   PRINTK("nicstar%d: RSQ base at 0x%x.\n", i, (u32) card->rsq.base);
++      
++   /* Initialize SCQ0, the only VBR SCQ used */
++   card->scq1 = (scq_info *) NULL;
++   card->scq2 = (scq_info *) NULL;
++   card->scq0 = get_scq(VBR_SCQSIZE, NS_VRSCD0);
++   if (card->scq0 == (scq_info *) NULL)
++   {
++      printk("nicstar%d: can't get SCQ0.\n", i);
++      error = 12;
++      ns_init_card_error(card, error);
++      return error;
++   }
++   u32d[0] = (u32) virt_to_bus(card->scq0->base);
++   u32d[1] = (u32) 0x00000000;
++   u32d[2] = (u32) 0xffffffff;
++   u32d[3] = (u32) 0x00000000;
++   ns_write_sram(card, NS_VRSCD0, u32d, 4);
++   ns_write_sram(card, NS_VRSCD1, u32d, 4);	/* These last two won't be used */
++   ns_write_sram(card, NS_VRSCD2, u32d, 4);	/* but are initialized, just in case... */
++   card->scq0->scd = NS_VRSCD0;
++   PRINTK("nicstar%d: VBR-SCQ0 base at 0x%x.\n", i, (u32) card->scq0->base);
++
++   /* Initialize TSTs */
++   card->tst_addr = NS_TST0;
++   card->tst_free_entries = NS_TST_NUM_ENTRIES;
++   data = NS_TST_OPCODE_VARIABLE;
++   for (j = 0; j < NS_TST_NUM_ENTRIES; j++)
++      ns_write_sram(card, NS_TST0 + j, &data, 1);
++   data = ns_tste_make(NS_TST_OPCODE_END, NS_TST0);
++   ns_write_sram(card, NS_TST0 + NS_TST_NUM_ENTRIES, &data, 1);
++   for (j = 0; j < NS_TST_NUM_ENTRIES; j++)
++      ns_write_sram(card, NS_TST1 + j, &data, 1);
++   data = ns_tste_make(NS_TST_OPCODE_END, NS_TST1);
++   ns_write_sram(card, NS_TST1 + NS_TST_NUM_ENTRIES, &data, 1);
++   for (j = 0; j < NS_TST_NUM_ENTRIES; j++)
++      card->tste2vc[j] = NULL;
++   writel(NS_TST0 << 2, card->membase + TSTB);
++
++
++   /* Initialize RCT. AAL type is set on opening the VC. */
++#ifdef RCQ_SUPPORT
++   u32d[0] = NS_RCTE_RAWCELLINTEN;
++#else
++   u32d[0] = 0x00000000;
++#endif RCQ_SUPPORT
++   u32d[1] = 0x00000000;
++   u32d[2] = 0x00000000;
++   u32d[3] = 0xFFFFFFFF;
++   for (j = 0; j < card->rct_size; j++)
++      ns_write_sram(card, j * 4, u32d, 4);      
++      
++   memset(card->vcmap, 0, NS_MAX_RCTSIZE * sizeof(vc_map));
++      
++   for (j = 0; j < NS_FRSCD_NUM; j++)
++      card->scd2vc[j] = NULL;
++
++   /* Initialize buffer levels */
++   card->sbnr.min = MIN_SB;
++   card->sbnr.init = NUM_SB;
++   card->sbnr.max = MAX_SB;
++   card->lbnr.min = MIN_LB;
++   card->lbnr.init = NUM_LB;
++   card->lbnr.max = MAX_LB;
++   card->iovnr.min = MIN_IOVB;
++   card->iovnr.init = NUM_IOVB;
++   card->iovnr.max = MAX_IOVB;
++   card->hbnr.min = MIN_HB;
++   card->hbnr.init = NUM_HB;
++   card->hbnr.max = MAX_HB;
++   
++   card->sm_handle = 0x00000000;
++   card->sm_addr = 0x00000000;
++   card->lg_handle = 0x00000000;
++   card->lg_addr = 0x00000000;
++   
++   card->efbie = 1;	/* To prevent push_rxbufs from enabling the interrupt */
++
++   /* Allocate small buffers */
++   skb_queue_head_init(&card->sbpool.queue);
++   card->sbpool.count = 0;			/* Not used */
++   for (j = 0; j < NUM_SB; j++)
++   {
++      struct sk_buff *sb;
++      sb = alloc_skb(NS_SMSKBSIZE, GFP_KERNEL);
++      if (sb == NULL)
++      {
++         printk("nicstar%d: can't allocate %dth of %d small buffers.\n",
++                i, j, NUM_SB);
++         error = 13;
++         ns_init_card_error(card, error);
++	 return error;
++      }
++      skb_queue_tail(&card->sbpool.queue, sb);
++      skb_reserve(sb, NS_AAL0_HEADER);
++      push_rxbufs(card, BUF_SM, (u32) sb, (u32) virt_to_bus(sb->data), 0, 0);
++   }
++   /* Test for strange behaviour which leads to crashes */
++   if ((bcount = ns_stat_sfbqc_get(readl(card->membase + STAT))) < card->sbnr.min)
++   {
++      printk("nicstar%d: Strange... Just allocated %d small buffers and sfbqc = %d.\n",
++             i, j, bcount);
++      error = 13;
++      ns_init_card_error(card, error);
++      return error;
++   }
++      
++
++   /* Allocate large buffers */
++   skb_queue_head_init(&card->lbpool.queue);
++   card->lbpool.count = 0;			/* Not used */
++   for (j = 0; j < NUM_LB; j++)
++   {
++      struct sk_buff *lb;
++      lb = alloc_skb(NS_LGSKBSIZE, GFP_KERNEL);
++      if (lb == NULL)
++      {
++         printk("nicstar%d: can't allocate %dth of %d large buffers.\n",
++                i, j, NUM_LB);
++         error = 14;
++         ns_init_card_error(card, error);
++	 return error;
++      }
++      skb_queue_tail(&card->lbpool.queue, lb);
++      skb_reserve(lb, NS_SMBUFSIZE);
++      push_rxbufs(card, BUF_LG, (u32) lb, (u32) virt_to_bus(lb->data), 0, 0);
++      /* Due to the implementation of push_rxbufs() this is 1, not 0 */
++      if (j == 1)
++      {
++         card->rcbuf = lb;
++         card->rawch = (u32) virt_to_bus(lb->data);
++      }
++   }
++   /* Test for strange behaviour which leads to crashes */
++   if ((bcount = ns_stat_lfbqc_get(readl(card->membase + STAT))) < card->lbnr.min)
++   {
++      printk("nicstar%d: Strange... Just allocated %d large buffers and lfbqc = %d.\n",
++             i, j, bcount);
++      error = 14;
++      ns_init_card_error(card, error);
++      return error;
++   }
++      
++
++   /* Allocate iovec buffers */
++   skb_queue_head_init(&card->iovpool.queue);
++   card->iovpool.count = 0;
++   for (j = 0; j < NUM_IOVB; j++)
++   {
++      struct sk_buff *iovb;
++      iovb = alloc_skb(NS_IOVBUFSIZE, GFP_KERNEL);
++      if (iovb == NULL)
++      {
++         printk("nicstar%d: can't allocate %dth of %d iovec buffers.\n",
++                i, j, NUM_IOVB);
++         error = 15;
++         ns_init_card_error(card, error);
++	 return error;
++      }
++      skb_queue_tail(&card->iovpool.queue, iovb);
++      card->iovpool.count++;
++   }
++
++
++   /* Pre-allocate some huge buffers */
++   skb_queue_head_init(&card->hbpool.queue);
++   card->hbpool.count = 0;
++   for (j = 0; j < NUM_HB; j++)
++   {
++      struct sk_buff *hb;
++      hb = alloc_skb(NS_HBUFSIZE, GFP_KERNEL);
++      if (hb == NULL)
++      {
++         printk("nicstar%d: can't allocate %dth of %d huge buffers.\n",
++                i, j, NUM_HB);
++         error = 16;
++         ns_init_card_error(card, error);
++	 return error;
++      }
++      skb_queue_tail(&card->hbpool.queue, hb);
++      card->hbpool.count++;
++   }
++
++   card->in_handler = 0;
++   card->in_poll = 0;
++   card->intcnt = 0;
++
++   /* Configure NICStAR */
++   if (card->rct_size == 4096)
++      ns_cfg_rctsize = NS_CFG_RCTSIZE_4096_ENTRIES;
++   else /* (card->rct_size == 16384) */
++      ns_cfg_rctsize = NS_CFG_RCTSIZE_16384_ENTRIES;
++
++   card->efbie = 1;
++   writel(NS_CFG_RXPATH |
++          NS_CFG_SMBUFSIZE |
++          NS_CFG_LGBUFSIZE |
++          NS_CFG_EFBIE |
++          NS_CFG_RSQSIZE |
++          NS_CFG_VPIBITS |
++          ns_cfg_rctsize |
++          NS_CFG_RXINT_NODELAY |
++          NS_CFG_RAWIE |		/* Only enabled if RCQ_SUPPORT */
++          NS_CFG_RSQAFIE |
++          NS_CFG_TXEN |
++          NS_CFG_TXIE |
++          NS_CFG_TSQFIE_OPT,		/* Only enabled if ENABLE_TSQFIE */ 
++          card->membase + CFG);
++
++   /* Register device */
++   card->atmdev = atm_dev_register("nicstar", &atm_ops, -1, 0UL);
++   if (card->atmdev == NULL)
++   {
++      printk("nicstar%d: can't register device.\n", i);
++      error = 17;
++      ns_init_card_error(card, error);
++      return error;
++   }
++      
++#ifdef ESI_FROM_EPROM
++   nicstar_read_eprom(card->membase, NICSTAR_EPROM_MAC_ADDR_OFFSET,
++                      card->atmdev->esi, 6);
++   printk("nicstar%d: MAC address %02X:%02X:%02X:%02X:%02X:%02X\n", i,
++          card->atmdev->esi[0], card->atmdev->esi[1], card->atmdev->esi[2],
++          card->atmdev->esi[3], card->atmdev->esi[4], card->atmdev->esi[5]);
++#else
++   card->atmdev->esi[0] = NS_ESI0;
++   card->atmdev->esi[1] = NS_ESI1;
++   card->atmdev->esi[2] = NS_ESI2;
++   card->atmdev->esi[3] = NS_ESI3;
++   card->atmdev->esi[4] = NS_ESI4;
++   card->atmdev->esi[5] = NS_ESI5;
++#endif /* ESI_FROM_EPROM */
++
++   card->atmdev->dev_data = card;
++   card->atmdev->ci_range.vpi_bits = card->vpibits;
++   card->atmdev->ci_range.vci_bits = card->vcibits;
++
++   num_cards++;
++
++   return error;
++}
++
++
++
++static void ns_init_card_error(ns_dev *card, int error)
++{
++   if (error >= 17)
++   {
++      writel(0x00000000, card->membase + CFG);
++   }
++   if (error >= 16)
++   {
++      struct sk_buff *hb;
++      while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL)
++         kfree_skb(hb);
++   }
++   if (error >= 15)
++   {
++      struct sk_buff *iovb;
++      while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL)
++         kfree_skb(iovb);
++   }
++   if (error >= 14)
++   {
++      struct sk_buff *lb;
++      while ((lb = skb_dequeue(&card->lbpool.queue)) != NULL)
++         kfree_skb(lb);
++   }
++   if (error >= 13)
++   {
++      struct sk_buff *sb;
++      while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL)
++         kfree_skb(sb);
++      free_scq(card->scq0, NULL);
++   }
++   if (error >= 12)
++   {
++      kfree(card->rsq.org);
++   }
++   if (error >= 11)
++   {
++      kfree(card->tsq.org);
++   }
++   if (error >= 10)
++   {
++      free_irq(card->pcidev->irq, card);
++   }
++   if (error >= 4)
++   {
++      iounmap((void *) card->membase);
++   }
++   if (error >= 3)
++   {
++      kfree(card);
++   }
++}
++
++
++
++static scq_info *get_scq(int size, u32 scd)
++{
++   scq_info *scq;
++   int i;
++
++   if (size != VBR_SCQSIZE && size != CBR_SCQSIZE)
++      return (scq_info *) NULL;
++
++   scq = (scq_info *) kmalloc(sizeof(scq_info), GFP_KERNEL);
++   if (scq == (scq_info *) NULL)
++      return (scq_info *) NULL;
++   scq->org = kmalloc(2 * size, GFP_KERNEL);
++   if (scq->org == NULL)
++   {
++      kfree(scq);
++      return (scq_info *) NULL;
++   }
++   scq->skb = (struct sk_buff **) kmalloc(sizeof(struct sk_buff *) *
++                                          (size / NS_SCQE_SIZE), GFP_KERNEL);
++   if (scq->skb == (struct sk_buff **) NULL)
++   {
++      kfree(scq->org);
++      kfree(scq);
++      return (scq_info *) NULL;
++   }
++   scq->num_entries = size / NS_SCQE_SIZE;
++   scq->base = (ns_scqe *) ALIGN_ADDRESS(scq->org, size);
++   scq->next = scq->base;
++   scq->last = scq->base + (scq->num_entries - 1);
++   scq->tail = scq->last;
++   scq->scd = scd;
++   scq->num_entries = size / NS_SCQE_SIZE;
++   scq->tbd_count = 0;
++   scq->scqfull_waitq = NULL;
++   scq->full = 0;
++
++   for (i = 0; i < scq->num_entries; i++)
++      scq->skb[i] = NULL;
++
++   return scq;
++}
++
++
++
++/* For variable rate SCQ vcc must be NULL */
++static void free_scq(scq_info *scq, struct atm_vcc *vcc)
++{
++   int i;
++
++   if (scq->num_entries == VBR_SCQ_NUM_ENTRIES)
++      for (i = 0; i < scq->num_entries; i++)
++      {
++         if (scq->skb[i] != NULL)
++	 {
++            vcc = ATM_SKB(scq->skb[i])->vcc;
++            if (vcc->pop != NULL)
++	       vcc->pop(vcc, scq->skb[i]);
++	    else
++               dev_kfree_skb(scq->skb[i]);
++         }
++      }
++   else /* vcc must be != NULL */
++   {
++      if (vcc == NULL)
++      {
++         printk("nicstar: free_scq() called with vcc == NULL for fixed rate scq.");
++         for (i = 0; i < scq->num_entries; i++)
++            dev_kfree_skb(scq->skb[i]);
++      }
++      else
++         for (i = 0; i < scq->num_entries; i++)
++         {
++            if (scq->skb[i] != NULL)
++            {
++               if (vcc->pop != NULL)
++                  vcc->pop(vcc, scq->skb[i]);
++               else
++                  dev_kfree_skb(scq->skb[i]);
++            }
++         }
++   }
++   kfree(scq->skb);
++   kfree(scq->org);
++   kfree(scq);
++}
++
++
++
++/* The handles passed must be pointers to the sk_buff containing the small
++   or large buffer(s) cast to u32. */
++static void push_rxbufs(ns_dev *card, u32 type, u32 handle1, u32 addr1,
++                       u32 handle2, u32 addr2)
++{
++   u32 stat;
++   unsigned long flags;
++   
++
++#ifdef GENERAL_DEBUG
++   if (!addr1)
++      printk("nicstar%d: push_rxbufs called with addr1 = 0.\n", card->index);
++#endif /* GENERAL_DEBUG */
++
++   stat = readl(card->membase + STAT);
++   card->sbfqc = ns_stat_sfbqc_get(stat);
++   card->lbfqc = ns_stat_lfbqc_get(stat);
++   if (type == BUF_SM)
++   {
++      if (!addr2)
++      {
++         if (card->sm_addr)
++	 {
++	    addr2 = card->sm_addr;
++	    handle2 = card->sm_handle;
++	    card->sm_addr = 0x00000000;
++	    card->sm_handle = 0x00000000;
++	 }
++	 else /* (!sm_addr) */
++	 {
++	    card->sm_addr = addr1;
++	    card->sm_handle = handle1;
++	 }
++      }      
++   }
++   else /* type == BUF_LG */
++   {
++      if (!addr2)
++      {
++         if (card->lg_addr)
++	 {
++	    addr2 = card->lg_addr;
++	    handle2 = card->lg_handle;
++	    card->lg_addr = 0x00000000;
++	    card->lg_handle = 0x00000000;
++	 }
++	 else /* (!lg_addr) */
++	 {
++	    card->lg_addr = addr1;
++	    card->lg_handle = handle1;
++	 }
++      }      
++   }
++
++   if (addr2)
++   {
++      if (type == BUF_SM)
++      {
++         if (card->sbfqc >= card->sbnr.max)
++         {
++            skb_unlink((struct sk_buff *) handle1);
++            kfree_skb((struct sk_buff *) handle1);
++            skb_unlink((struct sk_buff *) handle2);
++            kfree_skb((struct sk_buff *) handle2);
++            return;
++         }
++	 else
++            card->sbfqc += 2;
++      }
++      else /* (type == BUF_LG) */
++      {
++         if (card->lbfqc >= card->lbnr.max)
++         {
++            skb_unlink((struct sk_buff *) handle1);
++            kfree_skb((struct sk_buff *) handle1);
++            skb_unlink((struct sk_buff *) handle2);
++            kfree_skb((struct sk_buff *) handle2);
++            return;
++         }
++         else
++            card->lbfqc += 2;
++      }
++
++      save_flags(flags); cli();
++
++      while (CMD_BUSY(card));
++      writel(handle1, card->membase + DR0);
++      writel(addr1, card->membase + DR1);
++      writel(handle2, card->membase + DR2);
++      writel(addr2, card->membase + DR3);
++      writel(NS_CMD_WRITE_FREEBUFQ | (u32) type, card->membase + CMD);
++ 
++      restore_flags(flags);
++
++      XPRINTK("nicstar%d: Pushing %s buffers at 0x%x and 0x%x.\n", card->index,
++              (type == BUF_SM ? "small" : "large"), addr1, addr2);
++   }
++
++   if (!card->efbie && card->sbfqc >= card->sbnr.min &&
++       card->lbfqc >= card->lbnr.min)
++   {
++      card->efbie = 1;
++      writel((readl(card->membase + CFG) | NS_CFG_EFBIE), card->membase + CFG);
++   }
++
++   return;
++}
++
++
++
++static void ns_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
++{
++   u32 stat_r;
++   ns_dev *card;
++
++   card = (ns_dev *) dev_id;
++   card->intcnt++;
++
++   PRINTK("nicstar%d: NICStAR generated an interrupt\n", card->index);
++
++   if (card->in_handler)
++   {
++      printk("nicstar%d: Re-entering ns_irq_handler()???\n", card->index);
++      return;
++   }
++   card->in_handler = 1;
++   if (card->in_poll)
++   {
++      card->in_handler = 0;
++      printk("nicstar%d: Called irq handler while in ns_poll()!?\n",
++             card->index);
++      return;
++   }
++   
++   stat_r = readl(card->membase + STAT);
++
++   /* Transmit Status Indicator has been written to T. S. Queue */
++   if (stat_r & NS_STAT_TSIF)
++   {
++      TXPRINTK("nicstar%d: TSI interrupt\n", card->index);
++      process_tsq(card);
++      writel(NS_STAT_TSIF, card->membase + STAT);
++   }
++   
++   /* Incomplete CS-PDU has been transmitted */
++   if (stat_r & NS_STAT_TXICP)
++   {
++      writel(NS_STAT_TXICP, card->membase + STAT);
++      TXPRINTK("nicstar%d: Incomplete CS-PDU transmitted.\n",
++               card->index);
++   }
++   
++   /* Transmit Status Queue 7/8 full */
++   if (stat_r & NS_STAT_TSQF)
++   {
++      writel(NS_STAT_TSQF, card->membase + STAT);
++      PRINTK("nicstar%d: TSQ full.\n", card->index);
++      process_tsq(card);
++   }
++   
++   /* Timer overflow */
++   if (stat_r & NS_STAT_TMROF)
++   {
++      writel(NS_STAT_TMROF, card->membase + STAT);
++      PRINTK("nicstar%d: Timer overflow.\n", card->index);
++   }
++   
++   /* PHY device interrupt signal active */
++   if (stat_r & NS_STAT_PHYI)
++   {
++      writel(NS_STAT_PHYI, card->membase + STAT);
++      printk("nicstar%d: PHY interrupt.\n", card->index);
++   }
++
++   /* Small Buffer Queue is full */
++   if (stat_r & NS_STAT_SFBQF)
++   {
++      writel(NS_STAT_SFBQF, card->membase + STAT);
++      printk("nicstar%d: Small free buffer queue is full.\n", card->index);
++   }
++   
++   /* Large Buffer Queue is full */
++   if (stat_r & NS_STAT_LFBQF)
++   {
++      writel(NS_STAT_LFBQF, card->membase + STAT);
++      printk("nicstar%d: Large free buffer queue is full.\n", card->index);
++   }
++
++   /* Receive Status Queue is full */
++   if (stat_r & NS_STAT_RSQF)
++   {
++      writel(NS_STAT_RSQF, card->membase + STAT);
++      printk("nicstar%d: RSQ full.\n", card->index);
++      process_rsq(card);
++   }
++
++   /* Complete CS-PDU received */
++   if (stat_r & NS_STAT_EOPDU)
++   {
++      RXPRINTK("nicstar%d: End of CS-PDU received.\n", card->index);
++      process_rsq(card);
++      writel(NS_STAT_EOPDU, card->membase + STAT);
++   }
++
++   /* Raw cell received */
++   if (stat_r & NS_STAT_RAWCF)
++   {
++      writel(NS_STAT_RAWCF, card->membase + STAT);
++#ifndef RCQ_SUPPORT
++      printk("nicstar%d: Raw cell received and no support yet...\n",
++             card->index);
++#endif /* RCQ_SUPPORT */
++      /* NOTE: the following procedure may keep a raw cell pending untill the
++               next interrupt. As this preliminary support is only meant to
++               avoid buffer leakage, this is not an issue. */
++      while (readl(card->membase + RAWCT) != card->rawch)
++      {
++         ns_rcqe *rawcell;
++
++         rawcell = (ns_rcqe *) bus_to_virt(card->rawch);
++         if (ns_rcqe_islast(rawcell))
++         {
++            struct sk_buff *oldbuf;
++
++            oldbuf = card->rcbuf;
++            card->rcbuf = (struct sk_buff *) ns_rcqe_nextbufhandle(rawcell);
++            card->rawch = (u32) virt_to_bus(card->rcbuf->data);
++            recycle_rx_buf(card, oldbuf);
++         }
++         else
++            card->rawch += NS_RCQE_SIZE;
++      }
++   }
++
++   /* Small buffer queue is empty */
++   if (stat_r & NS_STAT_SFBQE)
++   {
++      int i;
++      struct sk_buff *sb;
++
++      writel(NS_STAT_SFBQE, card->membase + STAT);
++      printk("nicstar%d: Small free buffer queue empty.\n",
++             card->index);
++      for (i = 0; i < card->sbnr.min; i++)
++      {
++         sb = alloc_skb(NS_SMSKBSIZE, GFP_ATOMIC);
++         if (sb == NULL)
++         {
++            writel(readl(card->membase + CFG) & ~NS_CFG_EFBIE, card->membase + CFG);
++            card->efbie = 0;
++            break;
++         }
++         skb_queue_tail(&card->sbpool.queue, sb);
++         skb_reserve(sb, NS_AAL0_HEADER);
++         push_rxbufs(card, BUF_SM, (u32) sb, (u32) virt_to_bus(sb->data), 0, 0);
++      }
++      card->sbfqc = i;
++      process_rsq(card);
++   }
++
++   /* Large buffer queue empty */
++   if (stat_r & NS_STAT_LFBQE)
++   {
++      int i;
++      struct sk_buff *lb;
++
++      writel(NS_STAT_LFBQE, card->membase + STAT);
++      printk("nicstar%d: Large free buffer queue empty.\n",
++             card->index);
++      for (i = 0; i < card->lbnr.min; i++)
++      {
++         lb = alloc_skb(NS_LGSKBSIZE, GFP_ATOMIC);
++         if (lb == NULL)
++         {
++            writel(readl(card->membase + CFG) & ~NS_CFG_EFBIE, card->membase + CFG);
++            card->efbie = 0;
++            break;
++         }
++         skb_queue_tail(&card->lbpool.queue, lb);
++         skb_reserve(lb, NS_SMBUFSIZE);
++         push_rxbufs(card, BUF_LG, (u32) lb, (u32) virt_to_bus(lb->data), 0, 0);
++      }
++      card->lbfqc = i;
++      process_rsq(card);
++   }
++
++   /* Receive Status Queue is 7/8 full */
++   if (stat_r & NS_STAT_RSQAF)
++   {
++      writel(NS_STAT_RSQAF, card->membase + STAT);
++      RXPRINTK("nicstar%d: RSQ almost full.\n", card->index);
++      process_rsq(card);
++   }
++   
++   card->in_handler = 0;
++   PRINTK("nicstar%d: end of interrupt service\n", card->index);
++}
++
++
++
++static int ns_open(struct atm_vcc *vcc, short vpi, int vci)
++{
++   ns_dev *card;
++   vc_map *vc;
++   int error;
++   double tmpd;
++   int tcr, tcra;	/* target cell rate, and absolute value */
++   int n = 0;		/* Number of entries in the TST. Initialized to remove
++                           the compiler warning. */
++   u32 u32d[4];
++   int frscdi = 0;	/* Index of the SCD. Initialized to remove the compiler
++                           warning. How I wish compilers were clever enough to
++			   tell which variables can truly be used
++			   uninitialized... */
++   int inuse;		/* tx or rx vc already in use by another vcc */
++
++   card = (ns_dev *) vcc->dev->dev_data;
++   PRINTK("nicstar%d: opening vpi.vci %d.%d \n", card->index, (int) vpi, vci);
++   if (vcc->qos.aal != ATM_AAL5 && vcc->qos.aal != ATM_AAL0)
++   {
++      PRINTK("nicstar%d: unsupported AAL.\n", card->index);
++      return -EINVAL;
++   }
++
++   if ((error = atm_find_ci(vcc, &vpi, &vci)))
++   {
++      PRINTK("nicstar%d: error in atm_find_ci().\n", card->index);
++      return error;
++   }
++   vc = &(card->vcmap[vpi << card->vcibits | vci]);
++   vcc->vpi = vpi;
++   vcc->vci = vci;
++   vcc->dev_data = vc;
++
++   inuse = 0;
++   if (vcc->qos.txtp.traffic_class != ATM_NONE && vc->tx)
++      inuse = 1;
++   if (vcc->qos.rxtp.traffic_class != ATM_NONE && vc->rx)
++      inuse += 2;
++   if (inuse)
++   {
++      printk("nicstar%d: %s vci already in use.\n", card->index,
++             inuse == 1 ? "tx" : inuse == 2 ? "rx" : "tx and rx");
++      return -EINVAL;
++   }
++
++   vcc->flags |= ATM_VF_ADDR;
++
++   /* NOTE: You are not allowed to modify an open connection's QOS. To change
++      that, remove the ATM_VF_PARTIAL flag checking. There may be other changes
++      needed to do that. */
++   if (!(vcc->flags & ATM_VF_PARTIAL))
++   {
++      scq_info *scq;
++      
++      vcc->flags |= ATM_VF_PARTIAL;
++      if (vcc->qos.txtp.traffic_class == ATM_CBR)
++      {
++         /* Check requested cell rate and availability of SCD */
++         if (vcc->qos.txtp.max_pcr == 0 && vcc->qos.txtp.pcr == 0 &&
++             vcc->qos.txtp.min_pcr == 0)
++         {
++            PRINTK("nicstar%d: trying to open a CBR vc with cell rate = 0 \n",
++	           card->index);
++            vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL);
++            return -EINVAL;
++         }
++
++         tcr = atm_pcr_goal(&(vcc->qos.txtp));
++         tcra = tcr >= 0 ? tcr : -tcr;
++      
++         PRINTK("nicstar%d: target cell rate = %d.\n", card->index,
++                vcc->qos.txtp.max_pcr);
++
++         tmpd = ((double) tcra) * ((double) NS_TST_NUM_ENTRIES) /
++                ((double) card->max_pcr);      
++
++         n = (int) tmpd;
++         if (tcr > 0)
++         {
++            if (tmpd > (double) n) n++;
++         }
++         else if (tcr < 0)
++         {
++            if (tmpd < (double) n) n--;
++         }
++         else /* tcr == 0 */
++         {
++            if ((n = (card->tst_free_entries - NS_TST_RESERVED)) <= 0)
++	    {
++               PRINTK("nicstar%d: no CBR bandwidth free.\n", card->index);
++	       vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL);
++               return -EINVAL;
++            }
++         }
++
++         if (n == 0)
++         {
++            printk("nicstar%d: selected bandwidth < granularity.\n", card->index);
++            vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL);
++            return -EINVAL;
++         }
++
++         if (vcc->qos.txtp.max_pcr > 0)
++         {
++            tmpd = (double) n * (double) card->max_pcr /
++	           (double) NS_TST_NUM_ENTRIES;
++            if (tmpd > PCR_TOLERANCE * (double) vcc->qos.txtp.max_pcr)
++            {
++               PRINTK("nicstar%d: target cell rate exceeded requested max_pcr.\n",
++                      card->index);
++            }
++         }
++
++         if (n > (card->tst_free_entries - NS_TST_RESERVED))
++         {
++            PRINTK("nicstar%d: not enough free CBR bandwidth.\n", card->index);
++	    vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL);
++            return -EINVAL;
++         }
++         else
++            card->tst_free_entries -= n;
++
++         XPRINTK("nicstar%d: writing %d tst entries.\n", card->index, n);
++         for (frscdi = 0; frscdi < NS_FRSCD_NUM; frscdi++)
++         {
++            if (card->scd2vc[frscdi] == NULL)
++            {
++               card->scd2vc[frscdi] = vc;
++               break;
++	    }
++         }
++         if (frscdi == NS_FRSCD_NUM)
++         {
++            PRINTK("nicstar%d: no SCD available for CBR channel.\n", card->index);
++            card->tst_free_entries += n;
++            vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL);
++	    return -EBUSY;
++         }
++
++         vc->cbr_scd = NS_FRSCD + frscdi * NS_FRSCD_SIZE;
++
++         scq = get_scq(CBR_SCQSIZE, vc->cbr_scd);
++         if (scq == (scq_info *) NULL)
++         {
++            PRINTK("nicstar%d: can't get fixed rate SCQ.\n", card->index);
++            card->scd2vc[frscdi] = NULL;
++            card->tst_free_entries += n;
++            vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL);
++            return -ENOMEM;
++         }
++	 vc->scq = scq;
++         u32d[0] = (u32) virt_to_bus(scq->base);
++         u32d[1] = (u32) 0x00000000;
++         u32d[2] = (u32) 0xffffffff;
++         u32d[3] = (u32) 0x00000000;
++         ns_write_sram(card, vc->cbr_scd, u32d, 4);
++         
++	 fill_tst(card, n, vc);
++      }
++      else /* not CBR */
++      {
++         vc->cbr_scd = 0x00000000;
++	 vc->scq = card->scq0;
++      }
++      
++      if (vcc->qos.txtp.traffic_class != ATM_NONE)
++      {
++         vc->tx = 1;
++	 vc->tx_vcc = vcc;
++	 vc->tbd_count = 0;
++      }
++      if (vcc->qos.rxtp.traffic_class != ATM_NONE)
++      {
++         u32 status;
++      
++         vc->rx = 1;
++         vc->rx_vcc = vcc;
++         vc->rx_iov = NULL;
++
++	 /* Open the connection in hardware */
++	 if (vcc->qos.aal == ATM_AAL5)
++	    status = NS_RCTE_AAL5 | NS_RCTE_CONNECTOPEN;
++	 else /* vcc->qos.aal == ATM_AAL0 */
++	    status = NS_RCTE_AAL0 | NS_RCTE_CONNECTOPEN;
++#ifdef RCQ_SUPPORT
++         status |= NS_RCTE_RAWCELLINTEN;
++#endif /* RCQ_SUPPORT */
++         ns_write_sram(card, NS_RCT + (vpi << card->vcibits | vci) *
++	               NS_RCT_ENTRY_SIZE, &status, 1);
++      }
++      
++   }
++   
++   vcc->flags |= ATM_VF_READY;
++   return 0;
++}
++
++
++
++static void ns_close(struct atm_vcc *vcc)
++{
++   vc_map *vc;
++   ns_dev *card;
++   u32 data;
++   int i;
++   
++   vc = vcc->dev_data;
++   card = vcc->dev->dev_data;
++   PRINTK("nicstar%d: closing vpi.vci %d.%d \n", card->index,
++          (int) vcc->vpi, vcc->vci);
++
++   vcc->flags &= ~(ATM_VF_READY);
++   
++   if (vcc->qos.rxtp.traffic_class != ATM_NONE)
++   {
++      u32 addr;
++      unsigned long flags;
++      
++      addr = NS_RCT + (vcc->vpi << card->vcibits | vcc->vci) * NS_RCT_ENTRY_SIZE;
++      save_flags(flags); cli();
++      while(CMD_BUSY(card));
++      writel(NS_CMD_CLOSE_CONNECTION | addr << 2, card->membase + CMD);
++      restore_flags(flags);
++      
++      vc->rx = 0;
++      if (vc->rx_iov != NULL)
++      {
++	 struct sk_buff *iovb;
++	 u32 stat;
++   
++         stat = readl(card->membase + STAT);
++         card->sbfqc = ns_stat_sfbqc_get(stat);   
++         card->lbfqc = ns_stat_lfbqc_get(stat);
++
++         PRINTK("nicstar%d: closing a VC with pending rx buffers.\n",
++	        card->index);
++         iovb = vc->rx_iov;
++         recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data,
++	                       ATM_SKB(iovb)->iovcnt);
++         ATM_SKB(iovb)->iovcnt = 0;
++         ATM_SKB(iovb)->vcc = NULL;
++         save_flags(flags); cli();
++         recycle_iov_buf(card, iovb);
++         restore_flags(flags);
++         vc->rx_iov = NULL;
++      }
++   }
++
++   if (vcc->qos.txtp.traffic_class != ATM_NONE)
++   {
++      vc->tx = 0;
++   }
++
++   if (vcc->qos.txtp.traffic_class == ATM_CBR)
++   {
++      unsigned long flags;
++      ns_scqe *scqep;
++      scq_info *scq;
++
++      scq = vc->scq;
++
++      for (;;)
++      {
++         save_flags(flags); cli();
++         scqep = scq->next;
++         if (scqep == scq->base)
++            scqep = scq->last;
++         else
++            scqep--;
++         if (scqep == scq->tail)
++         {
++            restore_flags(flags);
++            break;
++         }
++         /* If the last entry is not a TSR, place one in the SCQ in order to
++            be able to completely drain it and then close. */
++         if (!ns_scqe_is_tsr(scqep) && scq->tail != scq->next)
++         {
++            ns_scqe tsr;
++            u32 scdi, scqi;
++            u32 data;
++            int index;
++
++            tsr.word_1 = ns_tsr_mkword_1(NS_TSR_INTENABLE);
++            scdi = (vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE;
++            scqi = scq->next - scq->base;
++            tsr.word_2 = ns_tsr_mkword_2(scdi, scqi);
++            tsr.word_3 = 0x00000000;
++            tsr.word_4 = 0x00000000;
++            *scq->next = tsr;
++            index = (int) scqi;
++            scq->skb[index] = NULL;
++            if (scq->next == scq->last)
++               scq->next = scq->base;
++            else
++               scq->next++;
++            data = (u32) virt_to_bus(scq->next);
++            ns_write_sram(card, scq->scd, &data, 1);
++         }
++         schedule();
++         restore_flags(flags);
++      }
++
++      /* Free all TST entries */
++      data = NS_TST_OPCODE_VARIABLE;
++      for (i = 0; i < NS_TST_NUM_ENTRIES; i++)
++      {
++         if (card->tste2vc[i] == vc)
++	 {
++            ns_write_sram(card, card->tst_addr + i, &data, 1);
++            card->tste2vc[i] = NULL;
++            card->tst_free_entries++;
++	 }
++      }
++      
++      card->scd2vc[(vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE] = NULL;
++      free_scq(vc->scq, vcc);
++   }
++
++   vcc->dev_data = NULL;
++   vcc->flags &= ~(ATM_VF_PARTIAL | ATM_VF_ADDR);
++
++#ifdef RX_DEBUG
++   {
++      u32 stat, cfg;
++      stat = readl(card->membase + STAT);
++      cfg = readl(card->membase + CFG);
++      printk("STAT = 0x%08X  CFG = 0x%08X  \n", stat, cfg);
++      printk("TSQ: base = 0x%08X  next = 0x%08X  last = 0x%08X  TSQT = 0x%08X \n",
++             (u32) card->tsq.base, (u32) card->tsq.next,(u32) card->tsq.last,
++	     readl(card->membase + TSQT));
++      printk("RSQ: base = 0x%08X  next = 0x%08X  last = 0x%08X  RSQT = 0x%08X \n",
++             (u32) card->rsq.base, (u32) card->rsq.next,(u32) card->rsq.last,
++	     readl(card->membase + RSQT));
++      printk("Empty free buffer queue interrupt %s \n",
++             card->efbie ? "enabled" : "disabled");
++      printk("SBCNT = %d  count = %d   LBCNT = %d count = %d \n",
++             ns_stat_sfbqc_get(stat), card->sbpool.count,
++	     ns_stat_lfbqc_get(stat), card->lbpool.count);
++      printk("hbpool.count = %d  iovpool.count = %d \n",
++             card->hbpool.count, card->iovpool.count);
++   }
++#endif /* RX_DEBUG */
++}
++
++
++
++static void fill_tst(ns_dev *card, int n, vc_map *vc)
++{
++   u32 new_tst;
++   double c, q;
++   int e, r;
++   u32 data;
++      
++   /* It would be very complicated to keep the two TSTs synchronized while
++      assuring that writes are only made to the inactive TST. So, for now I
++      will use only one TST. If problems occur, I will change this again */
++   
++   new_tst = card->tst_addr;
++
++   /* Fill procedure */
++
++   for (e = 0; e < NS_TST_NUM_ENTRIES; e++)
++   {
++      if (card->tste2vc[e] == NULL)
++         break;
++   }
++   if (e == NS_TST_NUM_ENTRIES)
++      printk("nicstar%d: No free TST entries found. \n", card->index);
++
++   r = n;
++   c = 1.0;
++   q = (double) n / (double) NS_TST_NUM_ENTRIES;
++
++   data = ns_tste_make(NS_TST_OPCODE_FIXED, vc->cbr_scd);
++      
++   while (e < NS_TST_NUM_ENTRIES)
++   {
++      if (c >= 1.0 && card->tste2vc[e] == NULL)
++      {
++         card->tste2vc[e] = vc;
++         ns_write_sram(card, new_tst + e, &data, 1);
++	 c -= 1.0;
++	 if (--r == 0)
++	    break;
++      }
++
++      e++;
++      c += q;
++   }
++   if (r != 0)
++      printk("nicstar%d: Not enough free TST entries. CBR lower than requested.\n",
++             card->index);
++   
++   /* End of fill procedure */
++   
++   data = ns_tste_make(NS_TST_OPCODE_END, new_tst);
++   ns_write_sram(card, new_tst + NS_TST_NUM_ENTRIES, &data, 1);
++   ns_write_sram(card, card->tst_addr + NS_TST_NUM_ENTRIES, &data, 1);
++   card->tst_addr = new_tst;
++}
++
++
++
++static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb)
++{
++   ns_dev *card;
++   vc_map *vc;
++   scq_info *scq;
++   unsigned long buflen;
++   ns_scqe scqe;
++   u32 flags;		/* TBD flags, not CPU flags */
++   
++   card = vcc->dev->dev_data;
++   TXPRINTK("nicstar%d: ns_send() called.\n", card->index);
++   if ((vc = (vc_map *) vcc->dev_data) == NULL)
++   {
++      printk("nicstar%d: vcc->dev_data == NULL on ns_send().\n", card->index);
++      vcc->stats->tx_err++;
++      dev_kfree_skb(skb);
++      return -EINVAL;
++   }
++   
++   if (!vc->tx)
++   {
++      printk("nicstar%d: Trying to transmit on a non-tx VC.\n", card->index);
++      vcc->stats->tx_err++;
++      dev_kfree_skb(skb);
++      return -EINVAL;
++   }
++   
++   if (vcc->qos.aal != ATM_AAL5 && vcc->qos.aal != ATM_AAL0)
++   {
++      printk("nicstar%d: Only AAL0 and AAL5 are supported.\n", card->index);
++      vcc->stats->tx_err++;
++      dev_kfree_skb(skb);
++      return -EINVAL;
++   }
++   
++   if (ATM_SKB(skb)->iovcnt != 0)
++   {
++      printk("nicstar%d: No scatter-gather yet.\n", card->index);
++      vcc->stats->tx_err++;
++      dev_kfree_skb(skb);
++      return -EINVAL;
++   }
++   
++   ATM_SKB(skb)->vcc = vcc;
++
++   if (vcc->qos.aal == ATM_AAL5)
++   {
++      buflen = (skb->len + 47 + 8) / 48 * 48;	/* Multiple of 48 */
++      flags = NS_TBD_AAL5;
++      scqe.word_2 = (u32) virt_to_bus(skb->data);
++      scqe.word_3 = (u32) skb->len;
++      scqe.word_4 = ((u32) vcc->vpi) << NS_TBD_VPI_SHIFT |
++                    ((u32) vcc->vci) << NS_TBD_VCI_SHIFT;
++      flags |= NS_TBD_EOPDU;
++   }
++   else /* (vcc->qos.aal == ATM_AAL0) */
++   {
++      buflen = ATM_CELL_PAYLOAD;	/* i.e., 48 bytes */
++      flags = NS_TBD_AAL0;
++      scqe.word_2 = (u32) virt_to_bus(skb->data) + NS_AAL0_HEADER;
++      scqe.word_3 = 0x00000000;
++      if (*skb->data & 0x02)	/* Payload type 1 - end of pdu */
++         flags |= NS_TBD_EOPDU;
++      scqe.word_4 = *((u32 *) skb->data) & ~NS_TBD_VC_MASK;
++      /* Force the VPI/VCI to be the same as in VCC struct */
++      scqe.word_4 |= (((u32) vcc->vpi) << NS_TBD_VPI_SHIFT |
++                      ((u32) vcc->vci) << NS_TBD_VCI_SHIFT) & NS_TBD_VC_MASK;
++   }
++
++   if (vcc->qos.txtp.traffic_class == ATM_CBR)
++   {
++      scqe.word_1 = ns_tbd_mkword_1_novbr(flags, (u32) buflen);
++      scq = ((vc_map *) vcc->dev_data)->scq;
++   }
++   else
++   {
++      scqe.word_1 = ns_tbd_mkword_1(flags, (u32) 1, (u32) 1, (u32) buflen);
++      scq = card->scq0;
++   }
++
++   if (push_scqe(card, vc, scq, &scqe, skb) != 0)	/* Timeout pushing the TBD */
++   {
++      printk("nicstar%d: Timeout pushing TBD.\n", card->index);
++      vcc->stats->tx_err++;
++      dev_kfree_skb(skb);
++      return -EIO;
++   }
++   vcc->stats->tx++;
++
++   return 0;
++}
++
++
++
++static int push_scqe(ns_dev *card, vc_map *vc, scq_info *scq, ns_scqe *tbd,
++                     struct sk_buff *skb)
++{
++   unsigned long flags;
++   ns_scqe tsr;
++   u32 scdi, scqi;
++   int scq_is_vbr;
++   u32 data;
++   int index;
++   
++   if (scq->tail == scq->next)
++   {
++      save_flags(flags); cli();
++      scq->full = 1;
++      current->timeout = jiffies + SCQFULL_TIMEOUT;
++      interruptible_sleep_on(&scq->scqfull_waitq);
++      restore_flags(flags);
++
++      if (scq->full)
++         return 1;
++   }
++   *scq->next = *tbd;
++   index = (int) (scq->next - scq->base);
++   scq->skb[index] = skb;
++   XPRINTK("nicstar%d: sending skb at 0x%x (pos %d).\n",
++           card->index, (u32) skb, index);
++   XPRINTK("nicstar%d: TBD written:\n0x%x\n0x%x\n0x%x\n0x%x\n at 0x%x.\n",
++           card->index, tbd->word_1, tbd->word_2, tbd->word_3, tbd->word_4,
++           (u32) scq->next);
++   if (scq->next == scq->last)
++      scq->next = scq->base;
++   else
++      scq->next++;
++
++   vc->tbd_count++;
++   if (scq->num_entries == VBR_SCQ_NUM_ENTRIES)
++   {
++      scq->tbd_count++;
++      scq_is_vbr = 1;
++   }
++   else
++      scq_is_vbr = 0;
++
++   if (vc->tbd_count >= MAX_TBD_PER_VC || scq->tbd_count >= MAX_TBD_PER_SCQ)
++   {
++      if (scq->tail == scq->next)
++      {
++         save_flags(flags); cli();
++         scq->full = 1;
++         current->timeout = jiffies + SCQFULL_TIMEOUT;
++         interruptible_sleep_on(&scq->scqfull_waitq);
++         restore_flags(flags);
++      }
++
++      if (!scq->full)
++      {
++         tsr.word_1 = ns_tsr_mkword_1(NS_TSR_INTENABLE);
++         if (scq_is_vbr)
++            scdi = NS_TSR_SCDISVBR;
++         else
++            scdi = (vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE;
++         scqi = scq->next - scq->base;
++         tsr.word_2 = ns_tsr_mkword_2(scdi, scqi);
++         tsr.word_3 = 0x00000000;
++         tsr.word_4 = 0x00000000;
++
++         *scq->next = tsr;
++         index = (int) scqi;
++         scq->skb[index] = NULL;
++         XPRINTK("nicstar%d: TSR written:\n0x%x\n0x%x\n0x%x\n0x%x\n at 0x%x.\n",
++	         card->index, tsr.word_1, tsr.word_2, tsr.word_3, tsr.word_4,
++		 (u32) scq->next);
++         if (scq->next == scq->last)
++            scq->next = scq->base;
++         else
++            scq->next++;
++         vc->tbd_count = 0;
++         scq->tbd_count = 0;
++      }
++      else
++         PRINTK("nicstar%d: Could not write TSI.\n", card->index);
++   }
++   
++   data = (u32) virt_to_bus(scq->next);
++   ns_write_sram(card, scq->scd, &data, 1);
++   
++   return 0;
++}
++
++
++
++static void process_tsq(ns_dev *card)
++{
++   u32 scdi;
++   scq_info *scq;
++   ns_tsi *previous;
++   
++   if (ns_tsi_isempty(card->tsq.next))
++      return;
++   while (!ns_tsi_isempty(card->tsq.next))
++   {
++      if (!ns_tsi_tmrof(card->tsq.next))
++      {
++         scdi = ns_tsi_getscdindex(card->tsq.next);
++	 if (scdi == NS_TSI_SCDISVBR)
++	    scq = card->scq0;
++	 else
++	 {
++	    if (card->scd2vc[scdi] == NULL)
++	    {
++	       printk("nicstar%d: could not find VC from SCD index.\n",
++	              card->index);
++               ns_tsi_init(card->tsq.next);
++               return;
++            }
++	    scq = card->scd2vc[scdi]->scq;
++         }
++         drain_scq(card, scq, ns_tsi_getscqpos(card->tsq.next));
++         scq->full = 0;
++         wake_up_interruptible(&(scq->scqfull_waitq));
++      }
++
++      ns_tsi_init(card->tsq.next);
++      previous = card->tsq.next;
++      if (card->tsq.next == card->tsq.last)
++         card->tsq.next = card->tsq.base;
++      else
++         card->tsq.next++;
++   }
++   writel((((u32) previous) - ((u32) card->tsq.base)),
++          card->membase + TSQH);
++}
++
++
++
++static void drain_scq(ns_dev *card, scq_info *scq, int pos)
++{
++   struct atm_vcc *vcc;
++   struct sk_buff *skb;
++   int i;
++   
++   XPRINTK("n