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