diff -urN linux-2.4.20/arch/alpha/config.in linux-2.4.20-atm/arch/alpha/config.in --- linux-2.4.20/arch/alpha/config.in Wed May 28 01:53:41 2003 +++ linux-2.4.20-atm/arch/alpha/config.in Wed May 28 01:58:41 2003 @@ -355,7 +355,7 @@ bool 'Network device support' CONFIG_NETDEVICES if [ "$CONFIG_NETDEVICES" = "y" ]; then source drivers/net/Config.in - if [ "$CONFIG_ATM" = "y" ]; then + if [ "$CONFIG_ATM" != "n" ]; then source drivers/atm/Config.in fi fi diff -urN linux-2.4.20/arch/cris/config.in linux-2.4.20-atm/arch/cris/config.in --- linux-2.4.20/arch/cris/config.in Wed May 28 01:53:41 2003 +++ linux-2.4.20-atm/arch/cris/config.in Wed May 28 01:58:41 2003 @@ -199,7 +199,7 @@ bool 'Network device support' CONFIG_NETDEVICES if [ "$CONFIG_NETDEVICES" = "y" ]; then source drivers/net/Config.in - if [ "$CONFIG_ATM" = "y" ]; then + if [ "$CONFIG_ATM" != "n" ]; then source drivers/atm/Config.in fi fi diff -urN linux-2.4.20/arch/i386/config.in linux-2.4.20-atm/arch/i386/config.in --- linux-2.4.20/arch/i386/config.in Wed May 28 01:53:41 2003 +++ linux-2.4.20-atm/arch/i386/config.in Wed May 28 01:58:41 2003 @@ -399,7 +399,7 @@ bool 'Network device support' CONFIG_NETDEVICES if [ "$CONFIG_NETDEVICES" = "y" ]; then source drivers/net/Config.in - if [ "$CONFIG_ATM" = "y" ]; then + if [ "$CONFIG_ATM" != "n" ]; then source drivers/atm/Config.in fi fi diff -urN linux-2.4.20/arch/parisc/config.in linux-2.4.20-atm/arch/parisc/config.in --- linux-2.4.20/arch/parisc/config.in Fri Nov 29 00:53:10 2002 +++ linux-2.4.20-atm/arch/parisc/config.in Wed May 28 01:58:41 2003 @@ -136,7 +136,7 @@ if [ "$CONFIG_NETDEVICES" = "y" ]; then source drivers/net/Config.in - if [ "$CONFIG_ATM" = "y" ]; then + if [ "$CONFIG_ATM" != "n" ]; then source drivers/atm/Config.in fi fi diff -urN linux-2.4.20/arch/ppc/config.in linux-2.4.20-atm/arch/ppc/config.in --- linux-2.4.20/arch/ppc/config.in Wed May 28 01:53:43 2003 +++ linux-2.4.20-atm/arch/ppc/config.in Wed May 28 01:58:41 2003 @@ -297,7 +297,7 @@ bool 'Network device support' CONFIG_NETDEVICES if [ "$CONFIG_NETDEVICES" = "y" ]; then source drivers/net/Config.in - if [ "$CONFIG_ATM" = "y" ]; then + if [ "$CONFIG_ATM" != "n" ]; then source drivers/atm/Config.in fi fi diff -urN linux-2.4.20/arch/ppc64/config.in linux-2.4.20-atm/arch/ppc64/config.in --- linux-2.4.20/arch/ppc64/config.in Wed May 28 01:53:44 2003 +++ linux-2.4.20-atm/arch/ppc64/config.in Wed May 28 01:58:41 2003 @@ -136,7 +136,7 @@ bool 'Network device support' CONFIG_NETDEVICES if [ "$CONFIG_NETDEVICES" = "y" ]; then source drivers/net/Config.in - if [ "$CONFIG_ATM" = "y" ]; then + if [ "$CONFIG_ATM" != "n" ]; then source drivers/atm/Config.in fi fi diff -urN linux-2.4.20/arch/sh/config.in linux-2.4.20-atm/arch/sh/config.in --- linux-2.4.20/arch/sh/config.in Wed May 28 01:53:44 2003 +++ linux-2.4.20-atm/arch/sh/config.in Wed May 28 01:58:41 2003 @@ -259,7 +259,7 @@ bool 'Network device support' CONFIG_NETDEVICES if [ "$CONFIG_NETDEVICES" = "y" ]; then source drivers/net/Config.in - if [ "$CONFIG_ATM" = "y" ]; then + if [ "$CONFIG_ATM" != "n" ]; then source drivers/atm/Config.in fi fi diff -urN linux-2.4.20/arch/sparc/config.in linux-2.4.20-atm/arch/sparc/config.in --- linux-2.4.20/arch/sparc/config.in Wed May 28 01:53:44 2003 +++ linux-2.4.20-atm/arch/sparc/config.in Wed May 28 01:58:41 2003 @@ -209,7 +209,7 @@ dep_tristate ' PPP BSD-Compress compression' CONFIG_PPP_BSDCOMP m if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' PPP over Ethernet (EXPERIMENTAL)' CONFIG_PPPOE $CONFIG_PPP - if [ "$CONFIG_ATM" = "y" ]; then + if [ "$CONFIG_ATM" != "n" ]; then dep_tristate ' PPP over ATM (EXPERIMENTAL)' CONFIG_PPPOATM $CONFIG_PPP fi fi @@ -235,7 +235,7 @@ # if [ "$CONFIG_FDDI" = "y" ]; then # fi - if [ "$CONFIG_ATM" = "y" ]; then + if [ "$CONFIG_ATM" != "n" ]; then source drivers/atm/Config.in fi fi diff -urN linux-2.4.20/arch/sparc64/config.in linux-2.4.20-atm/arch/sparc64/config.in --- linux-2.4.20/arch/sparc64/config.in Fri Nov 29 00:53:12 2002 +++ linux-2.4.20-atm/arch/sparc64/config.in Wed May 28 01:58:41 2003 @@ -234,7 +234,7 @@ bool 'Network device support' CONFIG_NETDEVICES if [ "$CONFIG_NETDEVICES" = "y" ]; then source drivers/net/Config.in - if [ "$CONFIG_ATM" = "y" ]; then + if [ "$CONFIG_ATM" != "n" ]; then source drivers/atm/Config.in fi fi diff -urN linux-2.4.20/arch/x86_64/config.in linux-2.4.20-atm/arch/x86_64/config.in --- linux-2.4.20/arch/x86_64/config.in Wed May 28 01:53:44 2003 +++ linux-2.4.20-atm/arch/x86_64/config.in Wed May 28 01:58:41 2003 @@ -172,10 +172,9 @@ bool 'Network device support' CONFIG_NETDEVICES if [ "$CONFIG_NETDEVICES" = "y" ]; then source drivers/net/Config.in -# seems to be largely not 64bit safe -# if [ "$CONFIG_ATM" = "y" ]; then -# source drivers/atm/Config.in -# fi + if [ "$CONFIG_ATM" != "n" ]; then + source drivers/atm/Config.in + fi fi endmenu fi diff -urN linux-2.4.20/drivers/atm/Config.in linux-2.4.20-atm/drivers/atm/Config.in --- linux-2.4.20/drivers/atm/Config.in Tue Nov 13 18:19:41 2001 +++ linux-2.4.20-atm/drivers/atm/Config.in Wed May 28 01:58:41 2003 @@ -4,11 +4,11 @@ mainmenu_option next_comment comment 'ATM drivers' if [ "$CONFIG_INET" = "y" ]; then - tristate 'ATM over TCP' CONFIG_ATM_TCP + dep_tristate 'ATM over TCP' CONFIG_ATM_TCP $CONFIG_ATM fi if [ "$CONFIG_PCI" = "y" ]; then - tristate 'Efficient Networks Speedstream 3010' CONFIG_ATM_LANAI - tristate 'Efficient Networks ENI155P' CONFIG_ATM_ENI + dep_tristate 'Efficient Networks Speedstream 3010' CONFIG_ATM_LANAI $CONFIG_ATM + dep_tristate 'Efficient Networks ENI155P' CONFIG_ATM_ENI $CONFIG_ATM if [ "$CONFIG_ATM_ENI" != "n" ]; then bool ' Enable extended debugging' CONFIG_ATM_ENI_DEBUG bool ' Fine-tune burst settings' CONFIG_ATM_ENI_TUNE_BURST @@ -23,8 +23,8 @@ bool ' Enable 2W RX bursts (optional)' CONFIG_ATM_ENI_BURST_RX_2W fi fi - tristate 'Fujitsu FireStream (FS50/FS155) ' CONFIG_ATM_FIRESTREAM - tristate 'ZeitNet ZN1221/ZN1225' CONFIG_ATM_ZATM + dep_tristate 'Fujitsu FireStream (FS50/FS155) ' CONFIG_ATM_FIRESTREAM $CONFIG_ATM + dep_tristate 'ZeitNet ZN1221/ZN1225' CONFIG_ATM_ZATM $CONFIG_ATM if [ "$CONFIG_ATM_ZATM" != "n" ]; then bool ' Enable extended debugging' CONFIG_ATM_ZATM_DEBUG if [ "$CONFIG_X86" = "y" ]; then @@ -35,32 +35,32 @@ # if [ "$CONFIG_ATM_TNETA1570" = "y" ]; then # bool ' Enable extended debugging' CONFIG_ATM_TNETA1570_DEBUG n # fi - tristate 'IDT 77201 (NICStAR) (ForeRunnerLE)' CONFIG_ATM_NICSTAR + dep_tristate 'IDT 77201 (NICStAR) (ForeRunnerLE)' CONFIG_ATM_NICSTAR $CONFIG_ATM if [ "$CONFIG_ATM_NICSTAR" != "n" ]; then bool ' Use suni PHY driver (155Mbps)' CONFIG_ATM_NICSTAR_USE_SUNI bool ' Use IDT77015 PHY driver (25Mbps)' CONFIG_ATM_NICSTAR_USE_IDT77105 fi - tristate 'IDT 77252 (NICStAR II)' CONFIG_ATM_IDT77252 + dep_tristate 'IDT 77252 (NICStAR II)' CONFIG_ATM_IDT77252 $CONFIG_ATM if [ "$CONFIG_ATM_IDT77252" != "n" ]; then bool ' Enable debugging messages' CONFIG_ATM_IDT77252_DEBUG bool ' Receive ALL cells in raw queue' CONFIG_ATM_IDT77252_RCV_ALL define_bool CONFIG_ATM_IDT77252_USE_SUNI y fi - tristate 'Madge Ambassador (Collage PCI 155 Server)' CONFIG_ATM_AMBASSADOR + dep_tristate 'Madge Ambassador (Collage PCI 155 Server)' CONFIG_ATM_AMBASSADOR $CONFIG_ATM if [ "$CONFIG_ATM_AMBASSADOR" != "n" ]; then bool ' Enable debugging messages' CONFIG_ATM_AMBASSADOR_DEBUG fi - tristate 'Madge Horizon [Ultra] (Collage PCI 25 and Collage PCI 155 Client)' CONFIG_ATM_HORIZON + dep_tristate 'Madge Horizon [Ultra] (Collage PCI 25 and Collage PCI 155 Client)' CONFIG_ATM_HORIZON $CONFIG_ATM if [ "$CONFIG_ATM_HORIZON" != "n" ]; then bool ' Enable debugging messages' CONFIG_ATM_HORIZON_DEBUG fi - tristate 'Interphase ATM PCI x575/x525/x531' CONFIG_ATM_IA - if [ "$CONFIG_ATM_IA" != "n" ]; then - bool ' Enable debugging messages' CONFIG_ATM_IA_DEBUG - fi + dep_tristate 'Interphase ATM PCI x575/x525/x531' CONFIG_ATM_IA $CONFIG_ATM + if [ "$CONFIG_ATM_IA" != "n" ]; then + bool ' Enable debugging messages' CONFIG_ATM_IA_DEBUG + fi fi if [ "$CONFIG_PCI" = "y" -o "$CONFIG_SBUS" = "y" ]; then - tristate 'FORE Systems 200E-series' CONFIG_ATM_FORE200E_MAYBE + dep_tristate 'FORE Systems 200E-series' CONFIG_ATM_FORE200E_MAYBE $CONFIG_ATM if [ "$CONFIG_ATM_FORE200E_MAYBE" != "n" ]; then if [ "$CONFIG_PCI" = "y" ]; then bool ' PCA-200E support' CONFIG_ATM_FORE200E_PCA @@ -90,6 +90,10 @@ else define_tristate CONFIG_ATM_FORE200E m fi + fi + dep_tristate 'ForeRunnerHE (155/622)' CONFIG_ATM_HE $CONFIG_ATM + if [ "$CONFIG_ATM_HE" != "n" ] ; then + bool ' Use S/UNI PHY driver' CONFIG_ATM_HE_USE_SUNI $CONFIG_ATM_HE fi fi endmenu diff -urN linux-2.4.20/drivers/atm/Makefile linux-2.4.20-atm/drivers/atm/Makefile --- linux-2.4.20/drivers/atm/Makefile Wed Nov 7 23:39:36 2001 +++ linux-2.4.20-atm/drivers/atm/Makefile Wed May 28 01:58:41 2003 @@ -32,6 +32,10 @@ obj-$(CONFIG_ATM_IA) += iphase.o suni.o obj-$(CONFIG_ATM_FIRESTREAM) += firestream.o obj-$(CONFIG_ATM_LANAI) += lanai.o +obj-$(CONFIG_ATM_HE) += he.o +ifeq ($(CONFIG_ATM_HE_USE_SUNI),y) + obj-$(CONFIG_ATM_HE) += suni.o +endif ifeq ($(CONFIG_ATM_FORE200E_PCA),y) FORE200E_FW_OBJS += fore200e_pca_fw.o diff -urN linux-2.4.20/drivers/atm/ambassador.c linux-2.4.20-atm/drivers/atm/ambassador.c --- linux-2.4.20/drivers/atm/ambassador.c Fri Sep 14 00:21:32 2001 +++ linux-2.4.20-atm/drivers/atm/ambassador.c Wed May 28 01:58:41 2003 @@ -1149,14 +1149,6 @@ } #endif - // deal with possibly wildcarded VCs - error = atm_find_ci (atm_vcc, &vpi, &vci); - if (error) { - PRINTD (DBG_WARN|DBG_VCC, "atm_find_ci failed!"); - return error; - } - PRINTD (DBG_VCC, "atm_find_ci gives %x %x", vpi, vci); - if (!(0 <= vpi && vpi < (1< +#include #include #include #include @@ -125,8 +125,6 @@ msg.addr.sap_addr.vpi = vpi; msg.hdr.vci = htons(vci); msg.addr.sap_addr.vci = vci; - error = atm_find_ci(vcc,&msg.addr.sap_addr.vpi,&msg.addr.sap_addr.vci); - if (error) return error; if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) return 0; msg.type = ATMTCP_CTRL_OPEN; msg.qos = vcc->qos; @@ -155,6 +153,7 @@ { struct atm_cirange ci; struct atm_vcc *vcc; + struct sock *s; if (cmd != ATM_SETCIRANGE) return -ENOIOCTLCMD; if (copy_from_user(&ci,(void *) arg,sizeof(ci))) return -EFAULT; @@ -162,9 +161,17 @@ if (ci.vci_bits == ATM_CI_MAX) ci.vci_bits = MAX_VCI_BITS; if (ci.vpi_bits > MAX_VPI_BITS || ci.vpi_bits < 0 || ci.vci_bits > MAX_VCI_BITS || ci.vci_bits < 0) return -EINVAL; - for (vcc = dev->vccs; vcc; vcc = vcc->next) + read_lock(&vcc_sklist_lock); + for (s = vcc_sklist; s; s = s->next) { + vcc = atm_sk(s); + if (vcc->dev != dev) continue; if ((vcc->vpi >> ci.vpi_bits) || - (vcc->vci >> ci.vci_bits)) return -EBUSY; + (vcc->vci >> ci.vci_bits)) { + read_unlock(&vcc_sklist_lock); + return -EBUSY; + } + } + read_unlock(&vcc_sklist_lock); dev->ci_range = ci; return 0; } @@ -230,6 +237,7 @@ struct atm_dev *atmtcp_dev; struct atmtcp_dev_data *dev_data; struct atm_vcc *walk; + struct sock *s; atmtcp_dev = (struct atm_dev *) vcc->dev_data; dev_data = PRIV(atmtcp_dev); @@ -239,8 +247,12 @@ kfree(dev_data); shutdown_atm_dev(atmtcp_dev); vcc->dev_data = NULL; - for (walk = atmtcp_dev->vccs; walk; walk = walk->next) - wake_up(&walk->sleep); + read_lock(&vcc_sklist_lock); + for (s = vcc_sklist; s; s = s->next) { + walk = atm_sk(s); + if (walk->dev == atmtcp_dev) wake_up(&walk->sleep); + } + read_unlock(&vcc_sklist_lock); } @@ -248,7 +260,8 @@ { struct atm_dev *dev; struct atmtcp_hdr *hdr; - struct atm_vcc *out_vcc; + struct atm_vcc *out_vcc = NULL; + struct sock *s; struct sk_buff *new_skb; int result = 0; @@ -260,11 +273,17 @@ (struct atmtcp_control *) skb->data); goto done; } - for (out_vcc = dev->vccs; out_vcc; out_vcc = out_vcc->next) + read_lock(&vcc_sklist_lock); + for (s = vcc_sklist; s; s = s->next) { + out_vcc = atm_sk(s); + if (out_vcc->dev != dev) continue; if (out_vcc->vpi == ntohs(hdr->vpi) && out_vcc->vci == ntohs(hdr->vci) && out_vcc->qos.rxtp.traffic_class != ATM_NONE) break; + out_vcc = NULL; + } + read_unlock(&vcc_sklist_lock); if (!out_vcc) { atomic_inc(&vcc->stats->tx_err); goto done; @@ -315,15 +334,10 @@ static struct atm_dev atmtcp_control_dev = { - &atmtcp_c_dev_ops, - NULL, /* no PHY */ - "atmtcp", /* type */ - 999, /* dummy device number */ - NULL,NULL, /* pretend not to have any VCCs */ - NULL,NULL, /* no data */ - { 0 }, /* no flags */ - NULL, /* no local address */ - { 0 } /* no ESI, no statistics */ + .ops = &atmtcp_c_dev_ops, + .type = "atmtcp", + .number = 999, + .lock = SPIN_LOCK_UNLOCKED }; @@ -356,9 +370,12 @@ struct atm_dev *dev; dev = NULL; - if (itf != -1) dev = atm_find_dev(itf); + if (itf != -1) dev = atm_dev_lookup(itf); if (dev) { - if (dev->ops != &atmtcp_v_dev_ops) return -EMEDIUMTYPE; + if (dev->ops != &atmtcp_v_dev_ops) { + atm_dev_release(dev); + return -EMEDIUMTYPE; + } if (PRIV(dev)->vcc) return -EBUSY; } else { @@ -368,7 +385,7 @@ if (error) return error; } PRIV(dev)->vcc = vcc; - bind_vcc(vcc,&atmtcp_control_dev); + vcc_insert_socket(&atmtcp_control_dev, vcc->sk); set_bit(ATM_VF_META,&vcc->flags); set_bit(ATM_VF_READY,&vcc->flags); vcc->dev_data = dev; @@ -389,44 +406,42 @@ struct atm_dev *dev; struct atmtcp_dev_data *dev_data; - dev = atm_find_dev(itf); + dev = atm_dev_lookup(itf); if (!dev) return -ENODEV; - if (dev->ops != &atmtcp_v_dev_ops) return -EMEDIUMTYPE; + if (dev->ops != &atmtcp_v_dev_ops) { + atm_dev_release(dev); + return -EMEDIUMTYPE; + } dev_data = PRIV(dev); if (!dev_data->persist) return 0; dev_data->persist = 0; if (PRIV(dev)->vcc) return 0; kfree(dev_data); + atm_dev_release(dev); shutdown_atm_dev(dev); return 0; } -#ifdef MODULE +static struct atm_tcp_ops __atm_tcp_ops = { + atmtcp_attach, /* attach */ + atmtcp_create_persistent, /* create_persistent */ + atmtcp_remove_persistent /* remove_persistent */ +}; + -int init_module(void) +static int __init atmtcp_init(void) { - atm_tcp_ops.attach = atmtcp_attach; - atm_tcp_ops.create_persistent = atmtcp_create_persistent; - atm_tcp_ops.remove_persistent = atmtcp_remove_persistent; + atm_tcp_ops = &__atm_tcp_ops; return 0; } - -void cleanup_module(void) +static void __exit atmtcp_cleanup(void) { - atm_tcp_ops.attach = NULL; - atm_tcp_ops.create_persistent = NULL; - atm_tcp_ops.remove_persistent = NULL; + atm_tcp_ops = NULL; } -MODULE_LICENSE("GPL"); -#else +module_init(atmtcp_init); +module_exit(atmtcp_cleanup); -struct atm_tcp_ops atm_tcp_ops = { - atmtcp_attach, /* attach */ - atmtcp_create_persistent, /* create_persistent */ - atmtcp_remove_persistent /* remove_persistent */ -}; - -#endif +MODULE_LICENSE("GPL"); diff -urN linux-2.4.20/drivers/atm/eni.c linux-2.4.20-atm/drivers/atm/eni.c --- linux-2.4.20/drivers/atm/eni.c Fri Dec 21 18:41:53 2001 +++ linux-2.4.20-atm/drivers/atm/eni.c Wed May 28 01:58:41 2003 @@ -829,6 +829,7 @@ if (eni_dev->rx_map[vcc->vci]) printk(KERN_CRIT DEV_LABEL "(itf %d): BUG - VCI %d already " "in use\n",vcc->dev->number,vcc->vci); + vcc_hold(vcc); eni_dev->rx_map[vcc->vci] = vcc; /* now it counts */ writel(((vcc->qos.aal != ATM_AAL5 ? MID_MODE_RAW : MID_MODE_AAL5) << MID_VCI_MODE_SHIFT) | MID_VCI_PTI_MODE | @@ -858,7 +859,11 @@ /* discard pending cell */ writel(readl(here) & ~MID_VCI_IN_SERVICE,here); /* don't accept any new ones */ + tasklet_disable(&eni_dev->task); eni_dev->rx_map[vcc->vci] = NULL; + barrier(); + tasklet_enable(&eni_dev->task); + vcc_put(vcc); /* wait for RX queue to drain */ DPRINTK("eni_close: waiting for RX ...\n"); EVENT("RX closing\n",0,0); @@ -1101,9 +1106,9 @@ dma_rd = eni_in(MID_DMA_RD_TX); dma_size = 3; /* JK for descriptor and final fill, plus final size mis-alignment fix */ -DPRINTK("iovcnt = %d\n",ATM_SKB(skb)->iovcnt); - if (!ATM_SKB(skb)->iovcnt) dma_size += 5; - else dma_size += 5*ATM_SKB(skb)->iovcnt; +DPRINTK("nr_frags = %d\n", skb_shinfo(skb)->nr_frags); + if (!skb_shinfo(skb)->nr_frags) dma_size += 5; + else dma_size += 5*skb_shinfo(skb)->nr_frags; 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); @@ -1124,15 +1129,15 @@ MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) | MID_DT_JK; j++; - if (!ATM_SKB(skb)->iovcnt) + if (!skb_shinfo(skb)->nr_frags) if (aal5) put_dma(tx->index,eni_dev->dma,&j,paddr,skb->len); else put_dma(tx->index,eni_dev->dma,&j,paddr+4,skb->len-4); else { DPRINTK("doing direct send\n"); /* @@@ well, this doesn't work anyway */ - for (i = 0; i < ATM_SKB(skb)->iovcnt; i++) + for (i = 0; i < skb_shinfo(skb)->nr_frags; 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); + skb_shinfo(skb)->frags[i].page + skb_shinfo(skb)->frags[i].page_offset, + skb_shinfo(skb)->frags[i].size); } if (skb->len & 3) put_dma(tx->index,eni_dev->dma,&j,zeroes,4-(skb->len & 3)); @@ -1880,43 +1885,6 @@ } -static int get_ci(struct atm_vcc *vcc,short *vpi,int *vci) -{ - struct atm_vcc *walk; - - if (*vpi == ATM_VPI_ANY) *vpi = 0; - if (*vci == ATM_VCI_ANY) { - for (*vci = ATM_NOT_RSV_VCI; *vci < NR_VCI; (*vci)++) { - if (vcc->qos.rxtp.traffic_class != ATM_NONE && - ENI_DEV(vcc->dev)->rx_map[*vci]) - continue; - if (vcc->qos.txtp.traffic_class != ATM_NONE) { - for (walk = vcc->dev->vccs; walk; - walk = walk->next) - if (test_bit(ATM_VF_ADDR,&walk->flags) - && walk->vci == *vci && - walk->qos.txtp.traffic_class != - ATM_NONE) - break; - if (walk) continue; - } - break; - } - return *vci == NR_VCI ? -EADDRINUSE : 0; - } - if (*vci == ATM_VCI_UNSPEC) return 0; - if (vcc->qos.rxtp.traffic_class != ATM_NONE && - ENI_DEV(vcc->dev)->rx_map[*vci]) - return -EADDRINUSE; - if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0; - for (walk = vcc->dev->vccs; walk; walk = walk->next) - if (test_bit(ATM_VF_ADDR,&walk->flags) && walk->vci == *vci && - walk->qos.txtp.traffic_class != ATM_NONE) - return -EADDRINUSE; - return 0; -} - - static int eni_open(struct atm_vcc *vcc,short vpi,int vci) { struct eni_dev *eni_dev; @@ -1927,8 +1895,6 @@ EVENT("eni_open\n",0,0); if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) ENI_VCC(vcc) = NULL; eni_dev = ENI_DEV(vcc->dev); - error = get_ci(vcc,&vpi,&vci); - if (error) return error; vcc->vpi = vpi; vcc->vci = vci; if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC) @@ -2122,8 +2088,8 @@ { static const char *signal[] = { "LOST","unknown","okay" }; struct eni_dev *eni_dev = ENI_DEV(dev); - struct atm_vcc *vcc; int left,i; + struct sock *s; left = *pos; if (!left) @@ -2192,10 +2158,13 @@ return sprintf(page,"%10sbacklog %u packets\n","", skb_queue_len(&tx->backlog)); } - for (vcc = dev->vccs; vcc; vcc = vcc->next) { + read_lock(&vcc_sklist_lock); + for(s = vcc_sklist; s; s = s->next) { + struct atm_vcc *vcc = atm_sk(s); struct eni_vcc *eni_vcc = ENI_VCC(vcc); int length; + if (vcc->dev != dev) continue; if (--left) continue; length = sprintf(page,"vcc %4d: ",vcc->vci); if (eni_vcc->rx) { @@ -2210,8 +2179,10 @@ length += sprintf(page+length,"tx[%d], txing %d bytes", eni_vcc->tx->index,eni_vcc->txing); page[length] = '\n'; + spin_unlock(&dev->lock); return length+1; } + read_unlock(&vcc_sklist_lock); for (i = 0; i < eni_dev->free_len; i++) { struct eni_free *fe = eni_dev->free_list+i; unsigned long offset; diff -urN linux-2.4.20/drivers/atm/firestream.c linux-2.4.20-atm/drivers/atm/firestream.c --- linux-2.4.20/drivers/atm/firestream.c Fri Nov 29 00:53:12 2002 +++ linux-2.4.20-atm/drivers/atm/firestream.c Wed May 28 01:58:41 2003 @@ -874,12 +874,6 @@ fs_dprintk (FS_DEBUG_OPEN, "fs: open on dev: %p, vcc at %p\n", dev, atm_vcc); - error = atm_find_ci(atm_vcc, &vpi, &vci); - if (error) { - fs_dprintk (FS_DEBUG_OPEN, "fs: find_ci failed.\n"); - return error; - } - atm_vcc->vpi = vpi; atm_vcc->vci = vci; if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC) diff -urN linux-2.4.20/drivers/atm/fore200e.c linux-2.4.20-atm/drivers/atm/fore200e.c --- linux-2.4.20/drivers/atm/fore200e.c Wed May 28 01:53:45 2003 +++ linux-2.4.20-atm/drivers/atm/fore200e.c Wed May 28 01:58:41 2003 @@ -65,7 +65,7 @@ #define FORE200E_52BYTE_AAL0_SDU #endif -#define FORE200E_VERSION "0.2d" +#define FORE200E_VERSION "0.2f" #define FORE200E "fore200e: " @@ -1077,15 +1077,7 @@ static struct atm_vcc* fore200e_find_vcc(struct fore200e* fore200e, struct rpd* rpd) { - struct atm_vcc* vcc; - - for (vcc = fore200e->atm_dev->vccs; vcc; vcc = vcc->next) { - - if (vcc->vpi == rpd->atm_header.vpi && vcc->vci == rpd->atm_header.vci) - break; - } - - return vcc; + return vcc_lookup(fore200e->atm_dev, rpd->atm_header.vpi, rpd->atm_header.vci); } @@ -1132,6 +1124,7 @@ printk(FORE200E "unable to alloc new skb, rx PDU length = %d\n", pdu_len); atomic_inc(&vcc->stats->rx_drop); + vcc_put(vcc); return; } @@ -1166,14 +1159,16 @@ if (atm_charge(vcc, skb->truesize) == 0) { DPRINTK(2, "receive buffers saturated for %d.%d.%d - PDU dropped\n", - vcc->itf, vcc->vpi, vcc->vci); + vcc->sk->bound_dev_if, vcc->vpi, vcc->vci); dev_kfree_skb_irq(skb); + vcc_put(vcc); return; } vcc->push(vcc, skb); atomic_inc(&vcc->stats->rx); + vcc_put(vcc); } @@ -1215,8 +1210,8 @@ fore200e_push_rpd(fore200e, entry->rpd); } else { - printk(FORE200E "damaged PDU on %d.%d.%d\n", - fore200e->atm_dev->number, entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci); + DPRINTK(1, "damaged PDU on %d.%d.%d\n", + fore200e->atm_dev->number, entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci); } fore200e_collect_rpd(fore200e, entry->rpd); @@ -1281,7 +1276,7 @@ #endif DPRINTK(1, "vpvc %d.%d.%d uses the %s buffer scheme\n", - vcc->itf, vcc->vpi, vcc->vci, scheme == BUFFER_SCHEME_ONE ? "first" : "second"); + vcc->sk->bound_dev_if, vcc->vpi, vcc->vci, scheme == BUFFER_SCHEME_ONE ? "first" : "second"); return scheme; } @@ -1351,39 +1346,6 @@ } -static int -fore200e_walk_vccs(struct atm_vcc *vcc, short *vpi, int *vci) -{ - struct atm_vcc* walk; - - /* find a free VPI */ - if (*vpi == ATM_VPI_ANY) { - - for (*vpi = 0, walk = vcc->dev->vccs; walk; walk = walk->next) { - - if ((walk->vci == *vci) && (walk->vpi == *vpi)) { - (*vpi)++; - walk = vcc->dev->vccs; - } - } - } - - /* find a free VCI */ - if (*vci == ATM_VCI_ANY) { - - for (*vci = ATM_NOT_RSV_VCI, walk = vcc->dev->vccs; walk; walk = walk->next) { - - if ((walk->vpi = *vpi) && (walk->vci == *vci)) { - *vci = walk->vci + 1; - walk = vcc->dev->vccs; - } - } - } - - return 0; -} - - #define FORE200E_MAX_BACK2BACK_CELLS 255 /* XXX depends on CDVT */ static void @@ -1408,9 +1370,6 @@ struct fore200e* fore200e = FORE200E_DEV(vcc->dev); struct fore200e_vcc* fore200e_vcc; - /* find a free VPI/VCI */ - fore200e_walk_vccs(vcc, &vpi, &vci); - vcc->vpi = vpi; vcc->vci = vci; @@ -1419,11 +1378,11 @@ return 0; set_bit(ATM_VF_ADDR, &vcc->flags); - vcc->itf = vcc->dev->number; + vcc->sk->bound_dev_if = vcc->dev->number; DPRINTK(2, "opening %d.%d.%d:%d QoS = (tx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d; " "rx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d)\n", - vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal), + vcc->sk->bound_dev_if, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal), fore200e_traffic_class[ vcc->qos.txtp.traffic_class ], vcc->qos.txtp.min_pcr, vcc->qos.txtp.max_pcr, vcc->qos.txtp.max_cdv, vcc->qos.txtp.max_sdu, fore200e_traffic_class[ vcc->qos.rxtp.traffic_class ], @@ -1469,7 +1428,7 @@ fore200e_rate_ctrl(&vcc->qos, &fore200e_vcc->rate); DPRINTK(3, "tx on %d.%d.%d:%d, tx PCR = %d, rx PCR = %d, data_cells = %u, idle_cells = %u\n", - vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal), + vcc->sk->bound_dev_if, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal), vcc->qos.txtp.max_pcr, vcc->qos.rxtp.max_pcr, fore200e_vcc->rate.data_cells, fore200e_vcc->rate.idle_cells); } @@ -1488,7 +1447,7 @@ { struct fore200e* fore200e = FORE200E_DEV(vcc->dev); - DPRINTK(2, "closing %d.%d.%d:%d\n", vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal)); + DPRINTK(2, "closing %d.%d.%d:%d\n", vcc->sk->bound_dev_if, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal)); fore200e_activate_vcin(fore200e, 0, vcc, 0); @@ -1543,7 +1502,7 @@ retry_here: - tasklet_disable(&fore200e->tasklet); + spin_lock_bh(&fore200e->tx_lock); entry = &txq->host_entry[ txq->head ]; @@ -1553,8 +1512,8 @@ fore200e_irq_tx(fore200e); if (*entry->status != STATUS_FREE) { - - tasklet_enable(&fore200e->tasklet); + + spin_unlock_bh(&fore200e->tx_lock); /* retry once again? */ if(--retry > 0) @@ -1593,8 +1552,9 @@ entry->data = kmalloc(tx_len, GFP_ATOMIC | GFP_DMA); if (entry->data == NULL) { - - tasklet_enable(&fore200e->tasklet); + + spin_unlock_bh(&fore200e->tx_lock); + if (vcc->pop) vcc->pop(vcc, skb); else @@ -1618,13 +1578,13 @@ FORE200E_NEXT_ENTRY(txq->head, QUEUE_SIZE_TX); txq->txing++; - tasklet_enable(&fore200e->tasklet); + spin_unlock_bh(&fore200e->tx_lock); /* ensure DMA synchronisation */ fore200e->bus->dma_sync(fore200e, tpd->tsd[ 0 ].buffer, tpd->tsd[ 0 ].length, FORE200E_DMA_TODEVICE); DPRINTK(3, "tx on %d.%d.%d:%d, len = %u (%u)\n", - vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal), + vcc->sk->bound_dev_if, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal), tpd->tsd[0].length, skb_len); if (skb_len < fore200e_vcc->tx_min_pdu) @@ -1690,14 +1650,14 @@ dev_kfree_skb(skb); if (ok == 0) { - printk(FORE200E "synchronous tx on %d:%d:%d failed\n", vcc->itf, vcc->vpi, vcc->vci); + printk(FORE200E "synchronous tx on %d:%d:%d failed\n", vcc->sk->bound_dev_if, vcc->vpi, vcc->vci); atomic_inc(&entry->vcc->stats->tx_err); return -EIO; } atomic_inc(&entry->vcc->stats->tx); - DPRINTK(3, "synchronous tx on %d:%d:%d succeeded\n", vcc->itf, vcc->vpi, vcc->vci); + DPRINTK(3, "synchronous tx on %d:%d:%d succeeded\n", vcc->sk->bound_dev_if, vcc->vpi, vcc->vci); } #endif @@ -1755,7 +1715,7 @@ // struct fore200e* fore200e = FORE200E_DEV(vcc->dev); DPRINTK(2, "getsockopt %d.%d.%d, level = %d, optname = 0x%x, optval = 0x%p, optlen = %d\n", - vcc->itf, vcc->vpi, vcc->vci, level, optname, optval, optlen); + vcc->sk->bound_dev_if, vcc->vpi, vcc->vci, level, optname, optval, optlen); return -EINVAL; } @@ -1767,7 +1727,7 @@ // struct fore200e* fore200e = FORE200E_DEV(vcc->dev); DPRINTK(2, "setsockopt %d.%d.%d, level = %d, optname = 0x%x, optval = 0x%p, optlen = %d\n", - vcc->itf, vcc->vpi, vcc->vci, level, optname, optval, optlen); + vcc->sk->bound_dev_if, vcc->vpi, vcc->vci, level, optname, optval, optlen); return -EINVAL; } @@ -1963,7 +1923,7 @@ "(tx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d; " "rx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d), flags = 0x%x\n" "available_cell_rate = %u", - vcc->itf, vcc->vpi, vcc->vci, + vcc->sk->bound_dev_if, vcc->vpi, vcc->vci, fore200e_traffic_class[ qos->txtp.traffic_class ], qos->txtp.min_pcr, qos->txtp.max_pcr, qos->txtp.max_cdv, qos->txtp.max_sdu, fore200e_traffic_class[ qos->rxtp.traffic_class ], @@ -2355,6 +2315,7 @@ DPRINTK(2, "device %s being initialized\n", fore200e->name); + spin_lock_init(&fore200e->tx_lock); init_MUTEX(&fore200e->rate_sf); cpq = fore200e->cp_queues = (struct cp_queues*) (fore200e->virt_base + FORE200E_CP_QUEUES_OFFSET); @@ -2642,6 +2603,7 @@ { struct fore200e* fore200e = FORE200E_DEV(dev); int len, left = *pos; + struct sock *s; if (!left--) { @@ -2886,7 +2848,11 @@ len = sprintf(page,"\n" " VCCs:\n address\tVPI.VCI:AAL\t(min/max tx PDU size) (min/max rx PDU size)\n"); - for (vcc = fore200e->atm_dev->vccs; vcc; vcc = vcc->next) { + read_lock(&vcc_sklist_lock); + for(s = vcc_sklist; s; s = s->next) { + vcc = atm_sk(s); + + if (vcc->dev != fore200e->atm_dev) continue; fore200e_vcc = FORE200E_VCC(vcc); @@ -2900,6 +2866,7 @@ fore200e_vcc->rx_max_pdu ); } + read_unlock(&vcc_sklist_lock); return len; } diff -urN linux-2.4.20/drivers/atm/fore200e.h linux-2.4.20-atm/drivers/atm/fore200e.h --- linux-2.4.20/drivers/atm/fore200e.h Mon Dec 11 22:22:12 2000 +++ linux-2.4.20-atm/drivers/atm/fore200e.h Wed May 28 01:58:41 2003 @@ -879,6 +879,7 @@ struct stats* stats; /* last snapshot of the stats */ struct semaphore rate_sf; /* protects rate reservation ops */ + spinlock_t tx_lock; /* protects tx ops */ struct tasklet_struct tasklet; /* performs interrupt work */ } fore200e_t; diff -urN linux-2.4.20/drivers/atm/he.c linux-2.4.20-atm/drivers/atm/he.c --- linux-2.4.20/drivers/atm/he.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-atm/drivers/atm/he.c Wed May 28 01:58:41 2003 @@ -0,0 +1,3282 @@ +/* $Id$ */ + +/* + + he.c + + ForeRunnerHE ATM Adapter driver for ATM on Linux + Copyright (C) 1999-2001 Naval Research Laboratory + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +/* + + he.c + + ForeRunnerHE ATM Adapter driver for ATM on Linux + Copyright (C) 1999-2001 Naval Research Laboratory + + Permission to use, copy, modify and distribute this software and its + documentation is hereby granted, provided that both the copyright + notice and this permission notice appear in all copies of the software, + derivative works or modified versions, and any portions thereof, and + that both notices appear in supporting documentation. + + NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND + DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER + RESULTING FROM THE USE OF THIS SOFTWARE. + + This driver was written using the "Programmer's Reference Manual for + ForeRunnerHE(tm)", MANU0361-01 - Rev. A, 08/21/98. + + AUTHORS: + chas williams + eric kinzie + + NOTES: + 4096 supported 'connections' + group 0 is used for all traffic + interrupt queue 0 is used for all interrupts + aal0 support for receive only + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifndef ATM_OC12_PCR +#define ATM_OC12_PCR (622080000/1080*1040/8/53) +#endif + +#ifdef BUS_INT_WAR +void sn_add_polled_interrupt(int irq, int interval); +void sn_delete_polled_interrupt(int irq); +#endif + +#define USE_TASKLET +#define USE_HE_FIND_VCC +#define USE_SCATTERGATHER +#undef USE_CHECKSUM_HW /* still confused about this */ +#define USE_RBPS /* probably broken */ +#define USE_RBPL_POOL + +#ifdef CONFIG_ATM_HE_USE_SUNI_MODULE +#define CONFIG_ATM_HE_USE_SUNI +#endif + +/* 2.2 kernel support */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,43) +#define dev_kfree_skb_irq(skb) dev_kfree_skb(skb) +#define dev_kfree_skb_any(skb) dev_kfree_skb(skb) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) +#define set_current_state(x) current->state = (x); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,43) +#undef USE_TASKLET +#endif + +#include "he.h" + +#include "suni.h" + +#include + +#define hprintk(fmt,args...) printk(DEV_LABEL "%d: " fmt, he_dev->number, args) +#define hprintk1(fmt) printk(DEV_LABEL "%d: " fmt, he_dev->number) + +#undef DEBUG +#ifdef DEBUG +#define HPRINTK(fmt,args...) hprintk(fmt,args) +#define HPRINTK1(fmt) hprintk1(fmt) +#else +#define HPRINTK(fmt,args...) +#define HPRINTK1(fmt,args...) +#endif /* DEBUG */ + + +/* version definition */ + +static char *version = "$Id$"; + +/* defines */ +#define ALIGN_ADDRESS(addr, alignment) \ + ((((unsigned long) (addr)) + (((unsigned long) (alignment)) - 1)) & ~(((unsigned long) (alignment)) - 1)) + +/* declarations */ + +static int he_open(struct atm_vcc *vcc, short vpi, int vci); +static void he_close(struct atm_vcc *vcc); +static int he_send(struct atm_vcc *vcc, struct sk_buff *skb); +static int he_sg_send(struct atm_vcc *vcc, unsigned long start, unsigned long size); +static int he_ioctl(struct atm_dev *dev, unsigned int cmd, void *arg); +static void he_irq_handler(int irq, void *dev_id, struct pt_regs *regs); +static void he_tasklet(unsigned long data); +static int he_proc_read(struct atm_dev *dev,loff_t *pos,char *page); +static int he_start(struct atm_dev *dev); +static void he_stop(struct he_dev *dev); +static void he_phy_put(struct atm_dev *, unsigned char, unsigned long); +static unsigned char he_phy_get(struct atm_dev *, unsigned long); + +static u8 read_prom_byte(struct he_dev *he_dev, int addr); + +/* globals */ + +struct he_dev *he_devs = NULL; +static short disable64 = -1; +static short nvpibits = -1; +static short nvcibits = -1; +static short rx_skb_reserve = 16; +static short irq_coalesce = 1; +static short sdh = 1; + +static struct atmdev_ops he_ops = +{ + open: he_open, + close: he_close, + ioctl: he_ioctl, + send: he_send, + sg_send: he_sg_send, + phy_put: he_phy_put, + phy_get: he_phy_get, + proc_read: he_proc_read, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,1) + owner: THIS_MODULE +#endif +}; + +/* see the comments in he.h about global_lock */ + +#ifdef CONFIG_SMP +#define HE_SPIN_LOCK(dev, flags) spin_lock_irqsave(&(dev)->global_lock, flags) +#define HE_SPIN_UNLOCK(dev, flags) spin_unlock_irqrestore(&(dev)->global_lock, flags) +#else +#define HE_SPIN_LOCK(dev, flags) do { flags = 0; } while(0) +#define HE_SPIN_UNLOCK(dev, flags) do { flags = 0; } while(0) +#endif + + +#define he_writel(dev, val, reg) writel(val, (dev)->membase + (reg)) +#define he_readl(dev, reg) readl((dev)->membase + (reg)) + +/* section 2.12 connection memory access */ + +static __inline__ void +he_writel_internal(struct he_dev *he_dev, unsigned val, unsigned addr, + unsigned flags) +{ + while(he_readl(he_dev, CON_CTL) & CON_CTL_BUSY); + he_writel(he_dev, val, CON_DAT); + he_writel(he_dev, flags | CON_CTL_WRITE | CON_CTL_ADDR(addr), CON_CTL); +} + +#define he_writel_rcm(dev, val, reg) \ + he_writel_internal(dev, val, reg, CON_CTL_RCM) + +#define he_writel_tcm(dev, val, reg) \ + he_writel_internal(dev, val, reg, CON_CTL_TCM) + +#define he_writel_mbox(dev, val, reg) \ + he_writel_internal(dev, val, reg, CON_CTL_MBOX) + +static unsigned +he_readl_internal(struct he_dev *he_dev, unsigned addr, unsigned flags) +{ + while(he_readl(he_dev, CON_CTL) & CON_CTL_BUSY); + he_writel(he_dev, flags | CON_CTL_READ | CON_CTL_ADDR(addr), CON_CTL); + return he_readl(he_dev, CON_DAT); +} + +#define he_readl_rcm(dev, reg) \ + he_readl_internal(dev, reg, CON_CTL_RCM) + +#define he_readl_tcm(dev, reg) \ + he_readl_internal(dev, reg, CON_CTL_TCM) + +#define he_readl_mbox(dev, reg) \ + he_readl_internal(dev, reg, CON_CTL_MBOX) + + +/* figure 2.2 connection id */ + +#define he_mkcid(dev, vpi, vci) (((vpi<<(dev)->vcibits) | vci) & 0x1fff) + +/* 2.5.1 per connection transmit state registers */ + +#define he_writel_tsr0(dev, val, cid) \ + he_writel_tcm(dev, val, CONFIG_TSRA | (cid<<3) | 0) +#define he_readl_tsr0(dev, cid) \ + he_readl_tcm(dev, CONFIG_TSRA | (cid<<3) | 0) + +#define he_writel_tsr1(dev, val, cid) \ + he_writel_tcm(dev, val, CONFIG_TSRA | (cid<<3) | 1) + +#define he_writel_tsr2(dev, val, cid) \ + he_writel_tcm(dev, val, CONFIG_TSRA | (cid<<3) | 2) + +#define he_writel_tsr3(dev, val, cid) \ + he_writel_tcm(dev, val, CONFIG_TSRA | (cid<<3) | 3) + +#define he_writel_tsr4(dev, val, cid) \ + he_writel_tcm(dev, val, CONFIG_TSRA | (cid<<3) | 4) + + /* from page 2-20 + * + * NOTE While the transmit connection is active, bits 23 through 0 + * of this register must not be written by the host. Byte + * enables should be used during normal operation when writing + * the most significant byte. + */ + +#define he_writel_tsr4_upper(dev, val, cid) \ + he_writel_internal(dev, val, CONFIG_TSRA | (cid<<3) | 4, \ + CON_CTL_TCM \ + | CON_BYTE_DISABLE_2 \ + | CON_BYTE_DISABLE_1 \ + | CON_BYTE_DISABLE_0) + +#define he_readl_tsr4(dev, cid) \ + he_readl_tcm(dev, CONFIG_TSRA | (cid<<3) | 4) + +#define he_writel_tsr5(dev, val, cid) \ + he_writel_tcm(dev, val, CONFIG_TSRA | (cid<<3) | 5) + +#define he_writel_tsr6(dev, val, cid) \ + he_writel_tcm(dev, val, CONFIG_TSRA | (cid<<3) | 6) + +#define he_writel_tsr7(dev, val, cid) \ + he_writel_tcm(dev, val, CONFIG_TSRA | (cid<<3) | 7) + + +#define he_writel_tsr8(dev, val, cid) \ + he_writel_tcm(dev, val, CONFIG_TSRB | (cid<<1) | 0) + +#define he_writel_tsr9(dev, val, cid) \ + he_writel_tcm(dev, val, CONFIG_TSRB | (cid<<1) | 1) + + +#define he_writel_tsr10(dev, val, cid) \ + he_writel_tcm(dev, val, CONFIG_TSRC | (cid<<2) | 0) + +#define he_writel_tsr11(dev, val, cid) \ + he_writel_tcm(dev, val, CONFIG_TSRC | (cid<<2) | 1) + +#define he_writel_tsr12(dev, val, cid) \ + he_writel_tcm(dev, val, CONFIG_TSRC | (cid<<2) | 2) + +#define he_writel_tsr13(dev, val, cid) \ + he_writel_tcm(dev, val, CONFIG_TSRC | (cid<<2) | 3) + + +#define he_writel_tsr14(dev, val, cid) \ + he_writel_tcm(dev, val, CONFIG_TSRD | cid) + +#define he_writel_tsr14_upper(dev, val, cid) \ + he_writel_internal(dev, val, CONFIG_TSRD | cid, \ + CON_CTL_TCM \ + | CON_BYTE_DISABLE_2 \ + | CON_BYTE_DISABLE_1 \ + | CON_BYTE_DISABLE_0) + +/* 2.7.1 per connection receive state registers */ + +#define he_writel_rsr0(dev, val, cid) \ + he_writel_rcm(dev, val, 0x00000 | (cid<<3) | 0) +#define he_readl_rsr0(dev, cid) \ + he_readl_rcm(dev, 0x00000 | (cid<<3) | 0) + +#define he_writel_rsr1(dev, val, cid) \ + he_writel_rcm(dev, val, 0x00000 | (cid<<3) | 1) + +#define he_writel_rsr2(dev, val, cid) \ + he_writel_rcm(dev, val, 0x00000 | (cid<<3) | 2) + +#define he_writel_rsr3(dev, val, cid) \ + he_writel_rcm(dev, val, 0x00000 | (cid<<3) | 3) + +#define he_writel_rsr4(dev, val, cid) \ + he_writel_rcm(dev, val, 0x00000 | (cid<<3) | 4) + +#define he_writel_rsr5(dev, val, cid) \ + he_writel_rcm(dev, val, 0x00000 | (cid<<3) | 5) + +#define he_writel_rsr6(dev, val, cid) \ + he_writel_rcm(dev, val, 0x00000 | (cid<<3) | 6) + +#define he_writel_rsr7(dev, val, cid) \ + he_writel_rcm(dev, val, 0x00000 | (cid<<3) | 7) + +static __inline__ struct atm_vcc* +he_find_vcc(struct he_dev *he_dev, unsigned cid) +{ + short vpi; + int vci; + + vpi = cid >> he_dev->vcibits; + vci = cid & ((1<vcibits)-1); + + return __vcc_lookup(he_dev->atm_dev, vpi, vci); +} + +static int __devinit +he_init_one(struct pci_dev *pci_dev, const struct pci_device_id *pci_ent) +{ + struct atm_dev *atm_dev; + struct he_dev *he_dev; + + printk(KERN_INFO "he: %s\n", version); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,43) + if (pci_enable_device(pci_dev)) return -EIO; +#endif + if (pci_set_dma_mask(pci_dev, HE_DMA_MASK) != 0) + { + printk(KERN_WARNING "he: no suitable dma available\n"); + return -EIO; + } + + atm_dev = atm_dev_register(DEV_LABEL, &he_ops, -1, 0); + if (!atm_dev) return -ENODEV; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,3) + pci_set_drvdata(pci_dev, atm_dev); +#else + pci_dev->driver_data = atm_dev; +#endif + + he_dev = (struct he_dev *) kmalloc(sizeof(struct he_dev), + GFP_KERNEL); + if (!he_dev) return -ENOMEM; + memset(he_dev, 0, sizeof(struct he_dev)); + + he_dev->pci_dev = pci_dev; + he_dev->atm_dev = atm_dev; + he_dev->atm_dev->dev_data = he_dev; + HE_DEV(atm_dev) = he_dev; + he_dev->number = atm_dev->number; /* was devs */ + if (he_start(atm_dev)) { + atm_dev_deregister(atm_dev); + he_stop(he_dev); + kfree(he_dev); + return -ENODEV; + } + he_dev->next = NULL; + if (he_devs) he_dev->next = he_devs; + he_devs = he_dev; + + return 0; +} + +static void __devexit +he_remove_one (struct pci_dev *pci_dev) +{ + struct atm_dev *atm_dev; + struct he_dev *he_dev; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,3) + atm_dev = pci_get_drvdata(pci_dev); +#else + atm_dev = pci_dev->driver_data; +#endif + he_dev = HE_DEV(atm_dev); + + /* need to remove from he_devs */ + + he_stop(he_dev); + atm_dev_deregister(atm_dev); + kfree(he_dev); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,3) + pci_set_drvdata(pci_dev, NULL); +#else + pci_dev->driver_data = NULL; +#endif +} + + +static unsigned +rate_to_atmf(unsigned rate) /* cps to atm forum format */ +{ +#define NONZERO (1<<14) + + unsigned exp = 0; + + if (rate == 0) return(0); + + rate <<= 9; + while (rate > 0x3ff) + { + ++exp; + rate >>= 1; + } + + return (NONZERO | (exp << 9) | (rate & 0x1ff)); +} + +static void __init +he_init_rx_lbfp0(struct he_dev *he_dev) +{ + unsigned i, lbm_offset, lbufd_index, lbuf_addr, lbuf_count; + unsigned lbufs_per_row = he_dev->cells_per_row / he_dev->cells_per_lbuf; + unsigned lbuf_bufsize = he_dev->cells_per_lbuf * ATM_CELL_PAYLOAD; + unsigned row_offset = he_dev->r0_startrow * he_dev->bytes_per_row; + + lbufd_index = 0; + lbm_offset = he_readl(he_dev, RCMLBM_BA); + + he_writel(he_dev, lbufd_index, RLBF0_H); + + for (i = 0, lbuf_count = 0; i < he_dev->r0_numbuffs; ++i) + { + lbufd_index += 2; + lbuf_addr = (row_offset + (lbuf_count * lbuf_bufsize)) / 32; + + he_writel_rcm(he_dev, lbuf_addr, lbm_offset); + he_writel_rcm(he_dev, lbufd_index, lbm_offset + 1); + + if (++lbuf_count == lbufs_per_row) + { + lbuf_count = 0; + row_offset += he_dev->bytes_per_row; + } + lbm_offset += 4; + } + + he_writel(he_dev, lbufd_index - 2, RLBF0_T); + he_writel(he_dev, he_dev->r0_numbuffs, RLBF0_C); +} + +static void __init +he_init_rx_lbfp1(struct he_dev *he_dev) +{ + unsigned i, lbm_offset, lbufd_index, lbuf_addr, lbuf_count; + unsigned lbufs_per_row = he_dev->cells_per_row / he_dev->cells_per_lbuf; + unsigned lbuf_bufsize = he_dev->cells_per_lbuf * ATM_CELL_PAYLOAD; + unsigned row_offset = he_dev->r1_startrow * he_dev->bytes_per_row; + + lbufd_index = 1; + lbm_offset = he_readl(he_dev, RCMLBM_BA) + (2 * lbufd_index); + + he_writel(he_dev, lbufd_index, RLBF1_H); + + for (i = 0, lbuf_count = 0; i < he_dev->r1_numbuffs; ++i) + { + lbufd_index += 2; + lbuf_addr = (row_offset + (lbuf_count * lbuf_bufsize)) / 32; + + he_writel_rcm(he_dev, lbuf_addr, lbm_offset); + he_writel_rcm(he_dev, lbufd_index, lbm_offset + 1); + + if (++lbuf_count == lbufs_per_row) + { + lbuf_count = 0; + row_offset += he_dev->bytes_per_row; + } + lbm_offset += 4; + } + + he_writel(he_dev, lbufd_index - 2, RLBF1_T); + he_writel(he_dev, he_dev->r1_numbuffs, RLBF1_C); +} + +static void __init +he_init_tx_lbfp(struct he_dev *he_dev) +{ + unsigned i, lbm_offset, lbufd_index, lbuf_addr, lbuf_count; + unsigned lbufs_per_row = he_dev->cells_per_row / he_dev->cells_per_lbuf; + unsigned lbuf_bufsize = he_dev->cells_per_lbuf * ATM_CELL_PAYLOAD; + unsigned row_offset = he_dev->tx_startrow * he_dev->bytes_per_row; + + lbufd_index = he_dev->r0_numbuffs + he_dev->r1_numbuffs; + lbm_offset = he_readl(he_dev, RCMLBM_BA) + (2 * lbufd_index); + + he_writel(he_dev, lbufd_index, TLBF_H); + + for (i = 0, lbuf_count = 0; i < he_dev->tx_numbuffs; ++i) + { + lbufd_index += 1; + lbuf_addr = (row_offset + (lbuf_count * lbuf_bufsize)) / 32; + + he_writel_rcm(he_dev, lbuf_addr, lbm_offset); + he_writel_rcm(he_dev, lbufd_index, lbm_offset + 1); + + if (++lbuf_count == lbufs_per_row) + { + lbuf_count = 0; + row_offset += he_dev->bytes_per_row; + } + lbm_offset += 2; + } + + he_writel(he_dev, lbufd_index - 1, TLBF_T); +} + +static int __init +he_init_tpdrq(struct he_dev *he_dev) +{ + he_dev->tpdrq_base = pci_alloc_consistent(he_dev->pci_dev, + CONFIG_TPDRQ_SIZE * sizeof(struct he_tpdrq), &he_dev->tpdrq_phys); + if (he_dev->tpdrq_base == NULL) + { + hprintk1("failed to alloc tpdrq\n"); + return -ENOMEM; + } + memset(he_dev->tpdrq_base, 0, + CONFIG_TPDRQ_SIZE * sizeof(struct he_tpdrq)); + + he_dev->tpdrq_tail = he_dev->tpdrq_base; + he_dev->tpdrq_head = he_dev->tpdrq_base; + + he_writel(he_dev, he_dev->tpdrq_phys, TPDRQ_B_H); + he_writel(he_dev, 0, TPDRQ_T); + he_writel(he_dev, CONFIG_TPDRQ_SIZE - 1, TPDRQ_S); + + return 0; +} + +static void __init +he_init_cs_block(struct he_dev *he_dev) +{ + unsigned clock, rate, delta; + int reg; + + /* 5.1.7 cs block initialization */ + + for(reg = 0; reg < 0x20; ++reg) + he_writel_mbox(he_dev, 0x0, CS_STTIM0 + reg); + + /* rate grid timer reload values */ + + clock = he_is622(he_dev) ? 66667000 : 50000000; + rate = he_dev->atm_dev->link_rate; + delta = rate / 16 / 2; + + for(reg = 0; reg < 0x10; ++reg) + { + /* 2.4 internal transmit function + * + * we initialize the first row in the rate grid. + * values are period (in clock cycles) of timer + */ + unsigned period = clock / rate; + + he_writel_mbox(he_dev, period, CS_TGRLD0 + reg); + rate -= delta; + } + + if (he_is622(he_dev)) + { + /* table 5.2 (4 cells per lbuf) */ + he_writel_mbox(he_dev, 0x000800fa, CS_ERTHR0); + he_writel_mbox(he_dev, 0x000c33cb, CS_ERTHR1); + he_writel_mbox(he_dev, 0x0010101b, CS_ERTHR2); + he_writel_mbox(he_dev, 0x00181dac, CS_ERTHR3); + he_writel_mbox(he_dev, 0x00280600, CS_ERTHR4); + + /* table 5.3, 5.4, 5.5, 5.6, 5.7 */ + he_writel_mbox(he_dev, 0x023de8b3, CS_ERCTL0); + he_writel_mbox(he_dev, 0x1801, CS_ERCTL1); + he_writel_mbox(he_dev, 0x68b3, CS_ERCTL2); + he_writel_mbox(he_dev, 0x1280, CS_ERSTAT0); + he_writel_mbox(he_dev, 0x68b3, CS_ERSTAT1); + he_writel_mbox(he_dev, 0x14585, CS_RTFWR); + + he_writel_mbox(he_dev, 0x4680, CS_RTATR); + + /* table 5.8 */ + he_writel_mbox(he_dev, 0x00159ece, CS_TFBSET); + he_writel_mbox(he_dev, 0x68b3, CS_WCRMAX); + he_writel_mbox(he_dev, 0x5eb3, CS_WCRMIN); + he_writel_mbox(he_dev, 0xe8b3, CS_WCRINC); + he_writel_mbox(he_dev, 0xdeb3, CS_WCRDEC); + he_writel_mbox(he_dev, 0x68b3, CS_WCRCEIL); + + /* table 5.9 */ + he_writel_mbox(he_dev, 0x5, CS_OTPPER); + he_writel_mbox(he_dev, 0x14, CS_OTWPER); + } + else + { + /* table 5.1 (4 cells per lbuf) */ + he_writel_mbox(he_dev, 0x000400ea, CS_ERTHR0); + he_writel_mbox(he_dev, 0x00063388, CS_ERTHR1); + he_writel_mbox(he_dev, 0x00081018, CS_ERTHR2); + he_writel_mbox(he_dev, 0x000c1dac, CS_ERTHR3); + he_writel_mbox(he_dev, 0x0014051a, CS_ERTHR4); + + /* table 5.3, 5.4, 5.5, 5.6, 5.7 */ + he_writel_mbox(he_dev, 0x0235e4b1, CS_ERCTL0); + he_writel_mbox(he_dev, 0x4701, CS_ERCTL1); + he_writel_mbox(he_dev, 0x64b1, CS_ERCTL2); + he_writel_mbox(he_dev, 0x1280, CS_ERSTAT0); + he_writel_mbox(he_dev, 0x64b1, CS_ERSTAT1); + he_writel_mbox(he_dev, 0xf424, CS_RTFWR); + + he_writel_mbox(he_dev, 0x4680, CS_RTATR); + + /* table 5.8 */ + he_writel_mbox(he_dev, 0x000563b7, CS_TFBSET); + he_writel_mbox(he_dev, 0x64b1, CS_WCRMAX); + he_writel_mbox(he_dev, 0x5ab1, CS_WCRMIN); + he_writel_mbox(he_dev, 0xe4b1, CS_WCRINC); + he_writel_mbox(he_dev, 0xdab1, CS_WCRDEC); + he_writel_mbox(he_dev, 0x64b1, CS_WCRCEIL); + + /* table 5.9 */ + he_writel_mbox(he_dev, 0x6, CS_OTPPER); + he_writel_mbox(he_dev, 0x1e, CS_OTWPER); + + } + + he_writel_mbox(he_dev, 0x8, CS_OTTLIM); + + for(reg = 0; reg < 0x8; ++reg) + he_writel_mbox(he_dev, 0x0, CS_HGRRT0 + reg); + +} + +static void __init +he_init_cs_block_rcm(struct he_dev *he_dev) +{ + unsigned rategrid[16][16]; + unsigned rate, delta; + int i, j, reg; + + unsigned rate_atmf, exp, man; + unsigned long long rate_cps; + int mult, buf, buf_limit = 4; + + /* initialize rate grid group table */ + + for (reg = 0x0; reg < 0xff; ++reg) + he_writel_rcm(he_dev, 0x0, CONFIG_RCMABR + reg); + + /* initialize rate controller groups */ + + for (reg = 0x100; reg < 0x1ff; ++reg) + he_writel_rcm(he_dev, 0x0, CONFIG_RCMABR + reg); + + /* initialize tNrm lookup table */ + + /* the manual makes reference to a routine in a sample driver + for proper configuration; fortunately, we only need this + in order to support abr connection */ + + /* initialize rate to group table */ + + rate = he_dev->atm_dev->link_rate; + delta = rate / 32; + + /* + * 2.4 transmit internal functions + * + * we construct a copy of the rate grid used by the scheduler + * in order to construct the rate to group table below + */ + + for (j = 0; j < 16; j++) + { + rategrid[0][j] = rate; + rate -= delta; + } + + for (i = 1; i < 16; i++) + for (j = 0; j < 16; j++) + if (i > 14) + rategrid[i][j] = rategrid[i - 1][j] / 4; + else + rategrid[i][j] = rategrid[i - 1][j] / 2; + + /* + * 2.4 transmit internal function + * + * this table maps the upper 5 bits of exponent and mantissa + * of the atm forum representation of the rate into an index + * on rate grid + */ + + rate_atmf = 0; + while (rate_atmf < 0x400) + { + man = (rate_atmf & 0x1f) << 4; + exp = rate_atmf >> 5; + + /* + instead of '/ 512', use '>> 9' to prevent a call + to divdu3 on x86 platforms + */ + rate_cps = (unsigned long long) (1 << exp) * (man + 512) >> 9; + + if (rate_cps < 10) rate_cps = 10; + /* 2.2.1 minimum payload rate is 10 cps */ + + for (i = 255; i > 0; i--) + if (rategrid[i/16][i%16] >= rate_cps) break; + /* pick nearest rate instead? */ + + /* + * each table entry is 16 bits: (rate grid index (8 bits) + * and a buffer limit (8 bits) + * there are two table entries in each 32-bit register + */ + +#ifdef notdef + buf = rate_cps * he_dev->tx_numbuffs / + (he_dev->atm_dev->link_rate * 2); +#else + /* this is pretty, but avoids _divdu3 and is mostly correct */ + buf = 0; + mult = he_dev->atm_dev->link_rate / ATM_OC3_PCR; + if (rate_cps > (68 * mult)) buf = 1; + if (rate_cps > (136 * mult)) buf = 2; + if (rate_cps > (204 * mult)) buf = 3; + if (rate_cps > (272 * mult)) buf = 4; +#endif + if (buf > buf_limit) buf = buf_limit; + reg = (reg<<16) | ((i<<8) | buf); + +#define RTGTBL_OFFSET 0x400 + + if (rate_atmf & 0x1) + he_writel_rcm(he_dev, reg, + CONFIG_RCMABR + RTGTBL_OFFSET + (rate_atmf>>1)); + + ++rate_atmf; + } +} + + +static int __init +he_init_group(struct he_dev *he_dev, int group) +{ + int i; + +#ifdef USE_RBPS + /* small buffer pool */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44) + he_dev->rbps_pool = pci_pool_create("rbps", he_dev->pci_dev, + CONFIG_RBPS_BUFSIZE, 8, 0, SLAB_KERNEL); +#else + he_dev->rbps_pool = pci_pool_create("rbps", he_dev->pci_dev, + CONFIG_RBPS_BUFSIZE, 8, 0); +#endif + if (he_dev->rbps_pool == NULL) + { + hprintk1("unable to create rbpl pool\n"); + return -ENOMEM; + } + + he_dev->rbps_base = pci_alloc_consistent(he_dev->pci_dev, + CONFIG_RBPS_SIZE * sizeof(struct he_rbp), &he_dev->rbps_phys); + if (he_dev->rbps_base == NULL) + { + hprintk1("failed to alloc rbps\n"); + return -ENOMEM; + } + memset(he_dev->rbps_base, 0, CONFIG_RBPS_SIZE * sizeof(struct he_rbp)); + he_dev->rbps_virt = kmalloc(CONFIG_RBPS_SIZE * sizeof(struct he_virt), GFP_KERNEL); + + for (i = 0; i < CONFIG_RBPS_SIZE; ++i) + { + dma_addr_t dma_handle; + void *cpuaddr; + + cpuaddr = pci_pool_alloc(he_dev->rbps_pool, SLAB_KERNEL|SLAB_DMA, &dma_handle); + if (cpuaddr == NULL) + return -ENOMEM; + + he_dev->rbps_virt[i].virt = cpuaddr; + he_dev->rbps_base[i].status = RBP_LOANED | RBP_SMALLBUF | (i << RBP_INDEX_OFF); + he_dev->rbps_base[i].phys = dma_handle; + + } + he_dev->rbps_tail = &he_dev->rbps_base[CONFIG_RBPS_SIZE-1]; + + he_writel(he_dev, he_dev->rbps_phys, G0_RBPS_S + (group * 32)); + he_writel(he_dev, RBPS_MASK(he_dev->rbps_tail), + G0_RBPS_T + (group * 32)); + he_writel(he_dev, CONFIG_RBPS_BUFSIZE/4, + G0_RBPS_BS + (group * 32)); + he_writel(he_dev, + RBP_THRESH(CONFIG_RBPS_THRESH) | + RBP_QSIZE(CONFIG_RBPS_SIZE-1) | + RBP_INT_ENB, + G0_RBPS_QI + (group * 32)); +#else /* !USE_RBPS */ + he_writel(he_dev, 0x0, G0_RBPS_S + (group * 32)); + he_writel(he_dev, 0x0, G0_RBPS_T + (group * 32)); + he_writel(he_dev, 0x0, G0_RBPS_QI + (group * 32)); + he_writel(he_dev, RBP_THRESH(0x1) | RBP_QSIZE(0x0), + G0_RBPS_BS + (group * 32)); +#endif /* USE_RBPS */ + + /* large buffer pool */ +#ifdef USE_RBPL_POOL +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44) + he_dev->rbpl_pool = pci_pool_create("rbpl", he_dev->pci_dev, + CONFIG_RBPL_BUFSIZE, 8, 0, SLAB_KERNEL); +#else + he_dev->rbpl_pool = pci_pool_create("rbpl", he_dev->pci_dev, + CONFIG_RBPL_BUFSIZE, 8, 0); +#endif + if (he_dev->rbpl_pool == NULL) + { + hprintk1("unable to create rbpl pool\n"); + return -ENOMEM; + } +#endif + + he_dev->rbpl_base = pci_alloc_consistent(he_dev->pci_dev, + CONFIG_RBPL_SIZE * sizeof(struct he_rbp), &he_dev->rbpl_phys); + if (he_dev->rbpl_base == NULL) + { + hprintk1("failed to alloc rbpl\n"); + return -ENOMEM; + } + memset(he_dev->rbpl_base, 0, CONFIG_RBPL_SIZE * sizeof(struct he_rbp)); + he_dev->rbpl_virt = kmalloc(CONFIG_RBPL_SIZE * sizeof(struct he_virt), GFP_KERNEL); + + for (i = 0; i < CONFIG_RBPL_SIZE; ++i) + { + dma_addr_t dma_handle; + void *cpuaddr; + +#ifdef USE_RBPL_POOL + cpuaddr = pci_pool_alloc(he_dev->rbpl_pool, SLAB_KERNEL|SLAB_DMA, &dma_handle); +#else + cpuaddr = pci_alloc_consistent(he_dev->pci_dev, CONFIG_RBPL_BUFSIZE, &dma_handle); +#endif + if (cpuaddr == NULL) + return -ENOMEM; + + he_dev->rbpl_virt[i].virt = cpuaddr; + he_dev->rbpl_base[i].status = RBP_LOANED | (i << RBP_INDEX_OFF); + he_dev->rbpl_base[i].phys = dma_handle; + + } + he_dev->rbpl_tail = &he_dev->rbpl_base[CONFIG_RBPL_SIZE-1]; + + he_writel(he_dev, he_dev->rbpl_phys, G0_RBPL_S + (group * 32)); + he_writel(he_dev, RBPL_MASK(he_dev->rbpl_tail), + G0_RBPL_T + (group * 32)); + he_writel(he_dev, CONFIG_RBPL_BUFSIZE/4, + G0_RBPL_BS + (group * 32)); + he_writel(he_dev, + RBP_THRESH(CONFIG_RBPL_THRESH) | + RBP_QSIZE(CONFIG_RBPL_SIZE-1) | + RBP_INT_ENB, + G0_RBPL_QI + (group * 32)); + + /* rx buffer ready queue */ + + he_dev->rbrq_base = pci_alloc_consistent(he_dev->pci_dev, + CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq), &he_dev->rbrq_phys); + if (he_dev->rbrq_base == NULL) + { + hprintk1("failed to allocate rbrq\n"); + return -ENOMEM; + } + memset(he_dev->rbrq_base, 0, CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq)); + + he_dev->rbrq_head = he_dev->rbrq_base; + he_writel(he_dev, he_dev->rbrq_phys, G0_RBRQ_ST + (group * 16)); + he_writel(he_dev, 0, G0_RBRQ_H + (group * 16)); + he_writel(he_dev, + RBRQ_THRESH(CONFIG_RBRQ_THRESH) | RBRQ_SIZE(CONFIG_RBRQ_SIZE-1), + G0_RBRQ_Q + (group * 16)); + if (irq_coalesce) + { + hprintk1("coalescing interrupts\n"); + he_writel(he_dev, RBRQ_TIME(768) | RBRQ_COUNT(7), + G0_RBRQ_I + (group * 16)); + } + else + he_writel(he_dev, RBRQ_TIME(0) | RBRQ_COUNT(1), + G0_RBRQ_I + (group * 16)); + + /* tx buffer ready queue */ + + he_dev->tbrq_base = pci_alloc_consistent(he_dev->pci_dev, + CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq), &he_dev->tbrq_phys); + if (he_dev->tbrq_base == NULL) + { + hprintk1("failed to allocate tbrq\n"); + return -ENOMEM; + } + memset(he_dev->tbrq_base, 0, CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq)); + + he_dev->tbrq_head = he_dev->tbrq_base; + + he_writel(he_dev, he_dev->tbrq_phys, G0_TBRQ_B_T + (group * 16)); + he_writel(he_dev, 0, G0_TBRQ_H + (group * 16)); + he_writel(he_dev, CONFIG_TBRQ_SIZE - 1, G0_TBRQ_S + (group * 16)); + he_writel(he_dev, CONFIG_TBRQ_THRESH, G0_TBRQ_THRESH + (group * 16)); + + return 0; +} + +static int __init +he_init_irq(struct he_dev *he_dev) +{ + int i; + + /* 2.9.3.5 tail offset for each interrupt queue is located after the + end of the interrupt queue */ + + he_dev->irq_base = pci_alloc_consistent(he_dev->pci_dev, + (CONFIG_IRQ_SIZE+1) * sizeof(struct he_irq), &he_dev->irq_phys); + if (he_dev->irq_base == NULL) + { + hprintk1("failed to allocate irq\n"); + return -ENOMEM; + } + he_dev->irq_tailoffset = (unsigned *) + &he_dev->irq_base[CONFIG_IRQ_SIZE]; + *he_dev->irq_tailoffset = 0; + he_dev->irq_head = he_dev->irq_base; + he_dev->irq_tail = he_dev->irq_base; + + for(i=0; i < CONFIG_IRQ_SIZE; ++i) + he_dev->irq_base[i].isw = ITYPE_INVALID; + + he_writel(he_dev, he_dev->irq_phys, IRQ0_BASE); + he_writel(he_dev, + IRQ_SIZE(CONFIG_IRQ_SIZE) | IRQ_THRESH(CONFIG_IRQ_THRESH), + IRQ0_HEAD); + he_writel(he_dev, IRQ_INT_A | IRQ_TYPE_LINE, IRQ0_CNTL); + he_writel(he_dev, 0x0, IRQ0_DATA); + + he_writel(he_dev, 0x0, IRQ1_BASE); + he_writel(he_dev, 0x0, IRQ1_HEAD); + he_writel(he_dev, 0x0, IRQ1_CNTL); + he_writel(he_dev, 0x0, IRQ1_DATA); + + he_writel(he_dev, 0x0, IRQ2_BASE); + he_writel(he_dev, 0x0, IRQ2_HEAD); + he_writel(he_dev, 0x0, IRQ2_CNTL); + he_writel(he_dev, 0x0, IRQ2_DATA); + + he_writel(he_dev, 0x0, IRQ3_BASE); + he_writel(he_dev, 0x0, IRQ3_HEAD); + he_writel(he_dev, 0x0, IRQ3_CNTL); + he_writel(he_dev, 0x0, IRQ3_DATA); + + /* 2.9.3.2 interrupt queue mapping registers */ + + he_writel(he_dev, 0x0, GRP_10_MAP); + he_writel(he_dev, 0x0, GRP_32_MAP); + he_writel(he_dev, 0x0, GRP_54_MAP); + he_writel(he_dev, 0x0, GRP_76_MAP); + + if (request_irq(he_dev->pci_dev->irq, he_irq_handler, SA_INTERRUPT|SA_SHIRQ, DEV_LABEL, he_dev)) + { + hprintk("irq %d already in use\n", he_dev->pci_dev->irq); + return -EINVAL; + } + + he_dev->irq = he_dev->pci_dev->irq; + +#ifdef BUS_INT_WAR + HPRINTK("sn_add_polled_interrupt(irq %d, 1)\n", he_dev->irq); + sn_add_polled_interrupt(he_dev->irq, 1); +#endif + + return 0; +} + +static int __init +he_start(struct atm_dev *dev) +{ + struct he_dev *he_dev; + struct pci_dev *pci_dev; + + u16 command; + u32 gen_cntl_0, host_cntl, lb_swap; + u8 cache_size, timer; + + unsigned err; + unsigned int status, reg; + int i, group; + + he_dev = HE_DEV(dev); + pci_dev = he_dev->pci_dev; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,3) + he_dev->membase = pci_dev->resource[0].start; +#else + he_dev->membase = pci_dev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK; +#endif + HPRINTK("membase = 0x%x irq = %d.\n", he_dev->membase, pci_dev->irq); + + /* + * pci bus controller initialization + */ + + /* 4.3 pci bus controller-specific initialization */ + if (pci_read_config_dword(pci_dev, GEN_CNTL_0, &gen_cntl_0) != 0) + { + hprintk1("can't read GEN_CNTL_0\n"); + return -EINVAL; + } + gen_cntl_0 |= (MRL_ENB | MRM_ENB | IGNORE_TIMEOUT); + if (pci_write_config_dword(pci_dev, GEN_CNTL_0, gen_cntl_0) != 0) + { + hprintk1("can't write GEN_CNTL_0.\n"); + return -EINVAL; + } + + if (pci_read_config_word(pci_dev, PCI_COMMAND, &command) != 0) + { + hprintk1("can't read PCI_COMMAND.\n"); + return -EINVAL; + } + + command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE); + if (pci_write_config_word(pci_dev, PCI_COMMAND, command) != 0) + { + hprintk1("can't enable memory.\n"); + return -EINVAL; + } + + if (pci_read_config_byte(pci_dev, PCI_CACHE_LINE_SIZE, &cache_size)) + { + hprintk1("can't read cache line size?\n"); + return -EINVAL; + } + + if (cache_size < 16) + { + cache_size = 16; + if (pci_write_config_byte(pci_dev, PCI_CACHE_LINE_SIZE, cache_size)) + hprintk("can't set cache line size to %d\n", cache_size); + } + + if (pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &timer)) + { + hprintk1("can't read latency timer?\n"); + return -EINVAL; + } + + /* from table 3.9 + * + * LAT_TIMER = 1 + AVG_LAT + BURST_SIZE/BUS_SIZE + * + * AVG_LAT: The average first data read/write latency [maximum 16 clock cycles] + * BURST_SIZE: 1536 bytes (read) for 622, 768 bytes (read) for 155 [192 clock cycles] + * + */ +#define LAT_TIMER 209 + if (timer < LAT_TIMER) + { + HPRINTK("latency timer was %d, setting to %d\n", timer, LAT_TIMER); + timer = LAT_TIMER; + if (pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, timer)) + hprintk("can't set latency timer to %d\n", timer); + } + + if (!(he_dev->membase = (unsigned long) ioremap(he_dev->membase, HE_REGMAP_SIZE))) { + hprintk1("can't set up page mapping\n"); + return -EINVAL; + } + + /* 4.4 card reset */ + he_writel(he_dev, 0x0, RESET_CNTL); + he_writel(he_dev, 0xff, RESET_CNTL); + + udelay(16*1000); /* 16 ms */ + status = he_readl(he_dev, RESET_CNTL); + if ((status & BOARD_RST_STATUS) == 0) + { + hprintk1("reset failed\n"); + return -EINVAL; + } + + /* 4.5 set bus width */ + host_cntl = he_readl(he_dev, HOST_CNTL); + if (host_cntl & PCI_BUS_SIZE64) + gen_cntl_0 |= ENBL_64; + else + gen_cntl_0 &= ~ENBL_64; + + if (disable64 == 1) + { + hprintk1("disabling 64-bit pci bus transfers\n"); + gen_cntl_0 &= ~ENBL_64; + } + + if (gen_cntl_0 & ENBL_64) hprintk1("64-bit transfers enabled\n"); + + pci_write_config_dword(pci_dev, GEN_CNTL_0, gen_cntl_0); + + /* 4.7 read prom contents */ + for(i=0; iprod_id[i] = read_prom_byte(he_dev, PROD_ID + i); + + he_dev->media = read_prom_byte(he_dev, MEDIA); + + for(i=0; i<6; ++i) + dev->esi[i] = read_prom_byte(he_dev, MAC_ADDR + i); + + hprintk("%s%s, %x:%x:%x:%x:%x:%x\n", + he_dev->prod_id, + he_dev->media & 0x40 ? "SM" : "MM", + dev->esi[0], + dev->esi[1], + dev->esi[2], + dev->esi[3], + dev->esi[4], + dev->esi[5]); + he_dev->atm_dev->link_rate = he_is622(he_dev) ? + ATM_OC12_PCR : ATM_OC3_PCR; + + /* 4.6 set host endianess */ + lb_swap = he_readl(he_dev, LB_SWAP); + if (he_is622(he_dev)) + lb_swap &= ~XFER_SIZE; /* 4 cells */ + else + lb_swap |= XFER_SIZE; /* 8 cells */ +#ifdef __BIG_ENDIAN + lb_swap |= DESC_WR_SWAP | INTR_SWAP | BIG_ENDIAN_HOST; +#else + lb_swap &= ~(DESC_WR_SWAP | INTR_SWAP | BIG_ENDIAN_HOST | + DATA_WR_SWAP | DATA_RD_SWAP | DESC_RD_SWAP); +#endif /* __BIG_ENDIAN */ + he_writel(he_dev, lb_swap, LB_SWAP); + + /* 4.8 sdram controller initialization */ + he_writel(he_dev, he_is622(he_dev) ? LB_64_ENB : 0x0, SDRAM_CTL); + + /* 4.9 initialize rnum value */ + lb_swap |= SWAP_RNUM_MAX(0xf); + he_writel(he_dev, lb_swap, LB_SWAP); + + /* 4.10 initialize the interrupt queues */ + if ((err = he_init_irq(he_dev)) != 0) return err; + +#ifdef USE_TASKLET + tasklet_init(&he_dev->tasklet, he_tasklet, (unsigned long) he_dev); +#endif + spin_lock_init(&he_dev->global_lock); + spin_lock_init(&he_dev->tpdrq_lock); + + /* 4.11 enable pci bus controller state machines */ + host_cntl |= (OUTFF_ENB | CMDFF_ENB | + QUICK_RD_RETRY | QUICK_WR_RETRY | PERR_INT_ENB); + he_writel(he_dev, host_cntl, HOST_CNTL); + + gen_cntl_0 |= INT_PROC_ENBL|INIT_ENB; + pci_write_config_dword(pci_dev, GEN_CNTL_0, gen_cntl_0); + + /* + * atm network controller initialization + */ + + /* 5.1.1 generic configuration state */ + + /* + * local (cell) buffer memory map + * + * HE155 HE622 + * + * 0 ____________1023 bytes 0 _______________________2047 bytes + * | | | | | + * | utility | | rx0 | | + * 5|____________| 255|___________________| u | + * 6| | 256| | t | + * | | | | i | + * | rx0 | row | tx | l | + * | | | | i | + * | | 767|___________________| t | + * 517|____________| 768| | y | + * row 518| | | rx1 | | + * | | 1023|___________________|___| + * | | + * | tx | + * | | + * | | + * 1535|____________| + * 1536| | + * | rx1 | + * 2047|____________| + * + */ + + /* total 4096 connections */ + he_dev->vcibits = CONFIG_DEFAULT_VCIBITS; + he_dev->vpibits = CONFIG_DEFAULT_VPIBITS; + + if (nvpibits != -1 && nvcibits != -1 && nvpibits+nvcibits != HE_MAXCIDBITS) + { + hprintk("nvpibits + nvcibits != %d\n", HE_MAXCIDBITS); + return -ENODEV; + } + + if (nvpibits != -1) + { + he_dev->vpibits = nvpibits; + he_dev->vcibits = HE_MAXCIDBITS - nvpibits; + } + + if (nvcibits != -1) + { + he_dev->vcibits = nvcibits; + he_dev->vpibits = HE_MAXCIDBITS - nvcibits; + } + + + if (he_is622(he_dev)) + { + he_dev->cells_per_row = 40; + he_dev->bytes_per_row = 2048; + he_dev->r0_numrows = 256; + he_dev->tx_numrows = 512; + he_dev->r1_numrows = 256; + he_dev->r0_startrow = 0; + he_dev->tx_startrow = 256; + he_dev->r1_startrow = 768; + } + else + { + he_dev->cells_per_row = 20; + he_dev->bytes_per_row = 1024; + he_dev->r0_numrows = 512; + he_dev->tx_numrows = 1018; + he_dev->r1_numrows = 512; + he_dev->r0_startrow = 6; + he_dev->tx_startrow = 518; + he_dev->r1_startrow = 1536; + } + + he_dev->cells_per_lbuf = 4; + he_dev->buffer_limit = 4; + he_dev->r0_numbuffs = he_dev->r0_numrows * + he_dev->cells_per_row / he_dev->cells_per_lbuf; + if (he_dev->r0_numbuffs > 2560) he_dev->r0_numbuffs = 2560; + + he_dev->r1_numbuffs = he_dev->r1_numrows * + he_dev->cells_per_row / he_dev->cells_per_lbuf; + if (he_dev->r1_numbuffs > 2560) he_dev->r1_numbuffs = 2560; + + he_dev->tx_numbuffs = he_dev->tx_numrows * + he_dev->cells_per_row / he_dev->cells_per_lbuf; + if (he_dev->tx_numbuffs > 5120) he_dev->tx_numbuffs = 5120; + + /* 5.1.2 configure hardware dependent registers */ + + he_writel(he_dev, + SLICE_X(0x2) | ARB_RNUM_MAX(0xf) | TH_PRTY(0x3) | + RH_PRTY(0x3) | TL_PRTY(0x2) | RL_PRTY(0x1) | + (he_is622(he_dev) ? BUS_MULTI(0x28) : BUS_MULTI(0x46)) | + (he_is622(he_dev) ? NET_PREF(0x50) : NET_PREF(0x8c)), + LBARB); + + he_writel(he_dev, BANK_ON | + (he_is622(he_dev) ? (REF_RATE(0x384) | WIDE_DATA) : REF_RATE(0x150)), + SDRAMCON); + + he_writel(he_dev, + (he_is622(he_dev) ? RM_BANK_WAIT(1) : RM_BANK_WAIT(0)) | + RM_RW_WAIT(1), RCMCONFIG); + he_writel(he_dev, + (he_is622(he_dev) ? TM_BANK_WAIT(2) : TM_BANK_WAIT(1)) | + TM_RW_WAIT(1), TCMCONFIG); + + he_writel(he_dev, he_dev->cells_per_lbuf * ATM_CELL_PAYLOAD, LB_CONFIG); + + he_writel(he_dev, + (he_is622(he_dev) ? UT_RD_DELAY(8) : UT_RD_DELAY(0)) | + (he_is622(he_dev) ? RC_UT_MODE(0) : RC_UT_MODE(1)) | + RX_VALVP(he_dev->vpibits) | + RX_VALVC(he_dev->vcibits), RC_CONFIG); + + he_writel(he_dev, DRF_THRESH(0x20) | + (he_is622(he_dev) ? TX_UT_MODE(0) : TX_UT_MODE(1)) | + TX_VCI_MASK(he_dev->vcibits) | + LBFREE_CNT(he_dev->tx_numbuffs), TX_CONFIG); + + he_writel(he_dev, 0x0, TXAAL5_PROTO); + + he_writel(he_dev, PHY_INT_ENB | + (he_is622(he_dev) ? PTMR_PRE(67-1) : PTMR_PRE(50-1)), + RH_CONFIG); + + /* 5.1.3 initialize connection memory */ + + for(i=0; i < TCM_MEM_SIZE; ++i) + he_writel_tcm(he_dev, 0, i); + + for(i=0; i < RCM_MEM_SIZE; ++i) + he_writel_rcm(he_dev, 0, i); + + /* + * transmit connection memory map + * + * tx memory + * 0x0 ___________________ + * | | + * | | + * | TSRa | + * | | + * | | + * 0x8000|___________________| + * | | + * | TSRb | + * 0xc000|___________________| + * | | + * | TSRc | + * 0xe000|___________________| + * | TSRd | + * 0xf000|___________________| + * | tmABR | + * 0x10000|___________________| + * | | + * | tmTPD | + * |___________________| + * | | + * .... + * 0x1ffff|___________________| + * + * + */ + + he_writel(he_dev, CONFIG_TSRB, TSRB_BA); + he_writel(he_dev, CONFIG_TSRC, TSRC_BA); + he_writel(he_dev, CONFIG_TSRD, TSRD_BA); + he_writel(he_dev, CONFIG_TMABR, TMABR_BA); + he_writel(he_dev, CONFIG_TPDBA, TPD_BA); + + + /* + * receive connection memory map + * + * 0x0 ___________________ + * | | + * | | + * | RSRa | + * | | + * | | + * 0x8000|___________________| + * | | + * | rx0/1 | + * | LBM | link lists of local + * | tx | buffer memory + * | | + * 0xd000|___________________| + * | | + * | rmABR | + * 0xe000|___________________| + * | | + * | RSRb | + * |___________________| + * | | + * .... + * 0xffff|___________________| + */ + + he_writel(he_dev, 0x08000, RCMLBM_BA); + he_writel(he_dev, 0x0e000, RCMRSRB_BA); + he_writel(he_dev, 0x0d800, RCMABR_BA); + + /* 5.1.4 initialize local buffer free pools linked lists */ + + he_init_rx_lbfp0(he_dev); + he_init_rx_lbfp1(he_dev); + + he_writel(he_dev, 0x0, RLBC_H); + he_writel(he_dev, 0x0, RLBC_T); + he_writel(he_dev, 0x0, RLBC_H2); + + he_writel(he_dev, 512, RXTHRSH); /* 10% of r0+r1 buffers */ + he_writel(he_dev, 256, LITHRSH); /* 5% of r0+r1 buffers */ + + he_init_tx_lbfp(he_dev); + + he_writel(he_dev, he_is622(he_dev) ? 0x104780 : 0x800, UBUFF_BA); + + /* 5.1.5 initialize intermediate receive queues */ + + if (he_is622(he_dev)) + { + he_writel(he_dev, 0x000f, G0_INMQ_S); + he_writel(he_dev, 0x200f, G0_INMQ_L); + + he_writel(he_dev, 0x001f, G1_INMQ_S); + he_writel(he_dev, 0x201f, G1_INMQ_L); + + he_writel(he_dev, 0x002f, G2_INMQ_S); + he_writel(he_dev, 0x202f, G2_INMQ_L); + + he_writel(he_dev, 0x003f, G3_INMQ_S); + he_writel(he_dev, 0x203f, G3_INMQ_L); + + he_writel(he_dev, 0x004f, G4_INMQ_S); + he_writel(he_dev, 0x204f, G4_INMQ_L); + + he_writel(he_dev, 0x005f, G5_INMQ_S); + he_writel(he_dev, 0x205f, G5_INMQ_L); + + he_writel(he_dev, 0x006f, G6_INMQ_S); + he_writel(he_dev, 0x206f, G6_INMQ_L); + + he_writel(he_dev, 0x007f, G7_INMQ_S); + he_writel(he_dev, 0x207f, G7_INMQ_L); + } + else + { + he_writel(he_dev, 0x0000, G0_INMQ_S); + he_writel(he_dev, 0x0008, G0_INMQ_L); + + he_writel(he_dev, 0x0001, G1_INMQ_S); + he_writel(he_dev, 0x0009, G1_INMQ_L); + + he_writel(he_dev, 0x0002, G2_INMQ_S); + he_writel(he_dev, 0x000a, G2_INMQ_L); + + he_writel(he_dev, 0x0003, G3_INMQ_S); + he_writel(he_dev, 0x000b, G3_INMQ_L); + + he_writel(he_dev, 0x0004, G4_INMQ_S); + he_writel(he_dev, 0x000c, G4_INMQ_L); + + he_writel(he_dev, 0x0005, G5_INMQ_S); + he_writel(he_dev, 0x000d, G5_INMQ_L); + + he_writel(he_dev, 0x0006, G6_INMQ_S); + he_writel(he_dev, 0x000e, G6_INMQ_L); + + he_writel(he_dev, 0x0007, G7_INMQ_S); + he_writel(he_dev, 0x000f, G7_INMQ_L); + } + + /* 5.1.6 application tunable parameters */ + + he_writel(he_dev, 0x0, MCC); + he_writel(he_dev, 0x0, OEC); + he_writel(he_dev, 0x0, DCC); + he_writel(he_dev, 0x0, CEC); + + /* 5.1.7 cs block initialization */ + + he_init_cs_block(he_dev); + + /* 5.1.8 cs block connection memory initialization */ + + he_init_cs_block_rcm(he_dev); + + /* 5.1.10 initialize host structures */ + + he_init_tpdrq(he_dev); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44) + he_dev->tpd_pool = pci_pool_create("tpd", he_dev->pci_dev, + sizeof(struct he_tpd), TPD_ALIGNMENT, 0, SLAB_KERNEL); +#else + he_dev->tpd_pool = pci_pool_create("tpd", he_dev->pci_dev, + sizeof(struct he_tpd), TPD_ALIGNMENT, 0); +#endif + if (he_dev->tpd_pool == NULL) + { + hprintk1("unable to create tpd pci_pool\n"); + return -ENOMEM; + } + INIT_LIST_HEAD(&he_dev->outstanding_tpds); + + if (he_init_group(he_dev, 0) != 0) + return -ENOMEM; + + for (group = 1; group < HE_NUM_GROUPS; ++group) + { + he_writel(he_dev, 0x0, G0_RBPS_S + (group * 32)); + he_writel(he_dev, 0x0, G0_RBPS_T + (group * 32)); + he_writel(he_dev, 0x0, G0_RBPS_QI + (group * 32)); + he_writel(he_dev, RBP_THRESH(0x1) | RBP_QSIZE(0x0), + G0_RBPS_BS + (group * 32)); + + he_writel(he_dev, 0x0, G0_RBPL_S + (group * 32)); + he_writel(he_dev, 0x0, G0_RBPL_T + (group * 32)); + he_writel(he_dev, RBP_THRESH(0x1) | RBP_QSIZE(0x0), + G0_RBPL_QI + (group * 32)); + he_writel(he_dev, 0x0, G0_RBPL_BS + (group * 32)); + + he_writel(he_dev, 0x0, G0_RBRQ_ST + (group * 16)); + he_writel(he_dev, 0x0, G0_RBRQ_H + (group * 16)); + he_writel(he_dev, RBRQ_THRESH(0x1) | RBRQ_SIZE(0x0), + G0_RBRQ_Q + (group * 16)); + he_writel(he_dev, 0x0, G0_RBRQ_I + (group * 16)); + + he_writel(he_dev, 0x0, G0_TBRQ_B_T + (group * 16)); + he_writel(he_dev, 0x0, G0_TBRQ_H + (group * 16)); + he_writel(he_dev, TBRQ_THRESH(0x1), + G0_TBRQ_THRESH + (group * 16)); + he_writel(he_dev, 0x0, G0_TBRQ_S + (group * 16)); + } + + /* host status page */ + + he_dev->hsp = pci_alloc_consistent(he_dev->pci_dev, + sizeof(struct he_hsp), &he_dev->hsp_phys); + if (he_dev->hsp == NULL) + { + hprintk1("failed to allocate host status page\n"); + return -ENOMEM; + } + memset(he_dev->hsp, 0, sizeof(struct he_hsp)); + he_writel(he_dev, he_dev->hsp_phys, HSP_BA); + + /* initialize framer */ + +#ifdef CONFIG_ATM_HE_USE_SUNI + suni_init(he_dev->atm_dev); + if (he_dev->atm_dev->phy && he_dev->atm_dev->phy->start) + he_dev->atm_dev->phy->start(he_dev->atm_dev); +#endif /* CONFIG_ATM_HE_USE_SUNI */ + + if (sdh) + { + /* this really should be in suni.c but for now... */ + + int val; + + val = he_phy_get(he_dev->atm_dev, SUNI_TPOP_APM); + val = (val & ~SUNI_TPOP_APM_S) | ( 0x2 << SUNI_TPOP_APM_S_SHIFT); + he_phy_put(he_dev->atm_dev, val, SUNI_TPOP_APM); + } + + /* 5.1.12 enable transmit and receive */ + + reg = he_readl_mbox(he_dev, CS_ERCTL0); + reg |= TX_ENABLE|ER_ENABLE; + he_writel_mbox(he_dev, reg, CS_ERCTL0); + + reg = he_readl(he_dev, RC_CONFIG); + reg |= RX_ENABLE; + he_writel(he_dev, reg, RC_CONFIG); + +#ifndef USE_HE_FIND_VCC + he_dev->he_vcc_table = kmalloc(sizeof(struct he_vcc_table) * + (1 << (he_dev->vcibits + he_dev->vpibits)), GFP_KERNEL); + if (he_dev->he_vcc_table == NULL) + { + hprintk1("failed to alloc he_vcc_table\n"); + return -ENOMEM; + } + memset(he_dev->he_vcc_table, 0, sizeof(struct he_vcc_table) * + (1 << (he_dev->vcibits + he_dev->vpibits))); +#endif + + for (i = 0; i < HE_NUM_CS_STPER; ++i) + { + he_dev->cs_stper[i].inuse = 0; + he_dev->cs_stper[i].pcr = -1; + } + he_dev->total_bw = 0; + + + /* atm linux initialization */ + + he_dev->atm_dev->ci_range.vpi_bits = he_dev->vpibits; + he_dev->atm_dev->ci_range.vci_bits = he_dev->vcibits; + + he_dev->irq_peak = 0; + he_dev->rbrq_peak = 0; + he_dev->rbpl_peak = 0; + he_dev->tbrq_peak = 0; + + hprintk1("hell bent for leather!\n"); + + return 0; +} + +static void +he_stop(struct he_dev *he_dev) +{ + u16 command; + u32 gen_cntl_0, reg; + struct pci_dev *pci_dev; + int i; + + pci_dev = he_dev->pci_dev; + + /* disable interrupts */ + + if (he_dev->membase) + { + pci_read_config_dword(pci_dev, GEN_CNTL_0, &gen_cntl_0); + gen_cntl_0 &= ~(INT_PROC_ENBL | INIT_ENB); + pci_write_config_dword(pci_dev, GEN_CNTL_0, gen_cntl_0); + +#ifdef USE_TASKLET + tasklet_disable(&he_dev->tasklet); +#endif + + /* disable recv and transmit */ + + reg = he_readl_mbox(he_dev, CS_ERCTL0); + reg &= ~(TX_ENABLE|ER_ENABLE); + he_writel_mbox(he_dev, reg, CS_ERCTL0); + + reg = he_readl(he_dev, RC_CONFIG); + reg &= ~(RX_ENABLE); + he_writel(he_dev, reg, RC_CONFIG); + } + +#ifdef CONFIG_ATM_HE_USE_SUNI + if (he_dev->atm_dev->phy && he_dev->atm_dev->phy->stop) + he_dev->atm_dev->phy->stop(he_dev->atm_dev); +#endif /* CONFIG_ATM_HE_USE_SUNI */ + + if (he_dev->irq) + { +#ifdef BUS_INT_WAR + sn_delete_polled_interrupt(he_dev->irq); +#endif + free_irq(he_dev->irq, he_dev); + } + + if (he_dev->irq_base) + pci_free_consistent(he_dev->pci_dev, (CONFIG_IRQ_SIZE+1) + * sizeof(struct he_irq), he_dev->irq_base, he_dev->irq_phys); + + if (he_dev->hsp) + pci_free_consistent(he_dev->pci_dev, sizeof(struct he_hsp), + he_dev->hsp, he_dev->hsp_phys); + + if (he_dev->rbpl_base) + { + for (i=0; irbpl_virt[i].virt; + dma_addr_t dma_handle = he_dev->rbpl_base[i].phys; + +#ifdef USE_RBPL_POOL + pci_pool_free(he_dev->rbpl_pool, cpuaddr, dma_handle); +#else + pci_free_consistent(he_dev->pci_dev, CONFIG_RBPL_BUFSIZE, cpuaddr, dma_handle); +#endif + } + pci_free_consistent(he_dev->pci_dev, CONFIG_RBPL_SIZE + * sizeof(struct he_rbp), he_dev->rbpl_base, he_dev->rbpl_phys); + } + +#ifdef USE_RBPL_POOL + if (he_dev->rbpl_pool) + pci_pool_destroy(he_dev->rbpl_pool); +#endif + +#ifdef USE_RBPS + if (he_dev->rbps_base) + { + for (i=0; irbps_virt[i].virt; + dma_addr_t dma_handle = he_dev->rbps_base[i].phys; + + pci_pool_free(he_dev->rbps_pool, cpuaddr, dma_handle); + } + pci_free_consistent(he_dev->pci_dev, CONFIG_RBPS_SIZE + * sizeof(struct he_rbp), he_dev->rbps_base, he_dev->rbps_phys); + } +#endif /* USE_RBPS */ + + if (he_dev->rbrq_base) + pci_free_consistent(he_dev->pci_dev, CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq), + he_dev->rbrq_base, he_dev->rbrq_phys); + + if (he_dev->tbrq_base) + pci_free_consistent(he_dev->pci_dev, CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq), + he_dev->tbrq_base, he_dev->tbrq_phys); + + if (he_dev->tpdrq_base) + pci_free_consistent(he_dev->pci_dev, CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq), + he_dev->tpdrq_base, he_dev->tpdrq_phys); + + if (he_dev->tpd_pool) + pci_pool_destroy(he_dev->tpd_pool); + +#ifndef USE_HE_FIND_VCC + if (he_dev->he_vcc_table) + kfree(he_dev->he_vcc_table); +#endif + + if (he_dev->pci_dev) + { + pci_read_config_word(he_dev->pci_dev, PCI_COMMAND, &command); + command &= ~(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + pci_write_config_word(he_dev->pci_dev, PCI_COMMAND, command); + } + + if (he_dev->membase) iounmap((void *) he_dev->membase); +} + +static __inline__ struct he_tpd * +he_alloc_tpd(struct he_dev *he_dev) +{ + struct he_tpd *tpd; + dma_addr_t dma_handle; + + tpd = pci_pool_alloc(he_dev->tpd_pool, SLAB_ATOMIC|SLAB_DMA, &dma_handle); + if (tpd == NULL) + return NULL; + + tpd->status = TPD_ADDR(dma_handle); + tpd->reserved = 0; + tpd->iovec[0].addr = 0; tpd->iovec[0].len = 0; + tpd->iovec[1].addr = 0; tpd->iovec[1].len = 0; + tpd->iovec[2].addr = 0; tpd->iovec[2].len = 0; + + return tpd; +} + + +#define AAL5_LEN(buf,len) \ + ((((unsigned char *)(buf))[(len)-6]<<8) | \ + (((unsigned char *)(buf))[(len)-5])) + +/* 2.10.1.2 receive + * + * aal5 packets can optionally return the tcp checksum in the lower + * 16 bits of the crc (RSR0_TCP_CKSUM) + */ + +#define TCP_CKSUM(buf,len) \ + ((((unsigned char *)(buf))[(len)-2]<<8) | \ + (((unsigned char *)(buf))[(len-1)])) + +static int +he_service_rbrq(struct he_dev *he_dev, int group) +{ + struct he_rbrq *rbrq_tail = (struct he_rbrq *) + ((unsigned long)he_dev->rbrq_base | + he_dev->hsp->group[group].rbrq_tail); + struct he_rbp *rbp = NULL; + unsigned cid, lastcid = -1; + unsigned buf_len = 0; + struct sk_buff *skb; + struct atm_vcc *vcc = NULL; + struct he_vcc *he_vcc; + struct iovec *iov; + int pdus_assembled = 0; + int updated = 0; + + read_lock(&vcc_sklist_lock); + while (he_dev->rbrq_head != rbrq_tail) + { + ++updated; + + HPRINTK("%p rbrq%d 0x%x len=%d cid=0x%x %s%s%s%s%s%s\n", + he_dev->rbrq_head, group, + RBRQ_ADDR(he_dev->rbrq_head), + RBRQ_BUFLEN(he_dev->rbrq_head), + RBRQ_CID(he_dev->rbrq_head), + RBRQ_CRC_ERR(he_dev->rbrq_head) ? " CRC_ERR" : "", + RBRQ_LEN_ERR(he_dev->rbrq_head) ? " LEN_ERR" : "", + RBRQ_END_PDU(he_dev->rbrq_head) ? " END_PDU" : "", + RBRQ_AAL5_PROT(he_dev->rbrq_head) ? " AAL5_PROT" : "", + RBRQ_CON_CLOSED(he_dev->rbrq_head) ? " CON_CLOSED" : "", + RBRQ_HBUF_ERR(he_dev->rbrq_head) ? " HBUF_ERR" : ""); + +#ifdef USE_RBPS + if (RBRQ_ADDR(he_dev->rbrq_head) & RBP_SMALLBUF) + rbp = &he_dev->rbps_base[RBP_INDEX(RBRQ_ADDR(he_dev->rbrq_head))]; + else +#endif + rbp = &he_dev->rbpl_base[RBP_INDEX(RBRQ_ADDR(he_dev->rbrq_head))]; + + buf_len = RBRQ_BUFLEN(he_dev->rbrq_head) * 4; + cid = RBRQ_CID(he_dev->rbrq_head); + +#ifdef USE_HE_FIND_VCC + if (cid != lastcid) + vcc = he_find_vcc(he_dev, cid); + lastcid = cid; +#else + vcc = HE_LOOKUP_VCC(he_dev, cid); +#endif + if (vcc == NULL) + { + hprintk("vcc == NULL (cid 0x%x)\n", cid); + if (!RBRQ_HBUF_ERR(he_dev->rbrq_head)) + rbp->status &= ~RBP_LOANED; + + goto next_rbrq_entry; + } + + he_vcc = HE_VCC(vcc); + if (he_vcc == NULL) + { + hprintk("he_vcc == NULL (cid 0x%x)\n", cid); + if (!RBRQ_HBUF_ERR(he_dev->rbrq_head)) + rbp->status &= ~RBP_LOANED; + goto next_rbrq_entry; + } + + if (RBRQ_HBUF_ERR(he_dev->rbrq_head)) + { + hprintk("HBUF_ERR! (cid 0x%x)\n", cid); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,99) + ++vcc->stats->rx_drop; +#else + atomic_inc(&vcc->stats->rx_drop); +#endif + goto return_host_buffers; + } + + he_vcc->iov_tail->iov_base = (void *) RBRQ_ADDR(he_dev->rbrq_head); + he_vcc->iov_tail->iov_len = buf_len; + he_vcc->pdu_len += buf_len; + ++he_vcc->iov_tail; + + if (RBRQ_CON_CLOSED(he_dev->rbrq_head)) + { + lastcid = -1; + HPRINTK("wake_up rx_waitq (cid 0x%x)\n", cid); + wake_up(&he_vcc->rx_waitq); + goto return_host_buffers; + } + +#ifdef notdef + if (he_vcc->iov_tail - he_vcc->iov_head > 32) + { + hprintk("iovec full! cid 0x%x\n", cid); + goto return_host_buffers; + } +#endif + if (!RBRQ_END_PDU(he_dev->rbrq_head)) goto next_rbrq_entry; + + if (RBRQ_LEN_ERR(he_dev->rbrq_head) + || RBRQ_CRC_ERR(he_dev->rbrq_head)) + { + HPRINTK("%s%s (%d.%d)\n", + RBRQ_CRC_ERR(he_dev->rbrq_head) + ? "CRC_ERR " : "", + RBRQ_LEN_ERR(he_dev->rbrq_head) + ? "LEN_ERR" : "", + vcc->vpi, vcc->vci); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,99) + ++vcc->stats->rx_err; +#else + atomic_inc(&vcc->stats->rx_err); +#endif + goto return_host_buffers; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,15) + skb = atm_alloc_charge(vcc, he_vcc->pdu_len + rx_skb_reserve, + GFP_ATOMIC); +#else + if (!atm_charge(vcc, atm_pdu2truesize(he_vcc->pdu_len + rx_skb_reserve))) + skb = NULL; + else + { + skb = alloc_skb(he_vcc->pdu_len + rx_skb_reserve, GFP_ATOMIC); + if (!skb) atm_return(vcc, + atm_pdu2truesize(he_vcc->pdu_len + rx_skb_reserve)); + } +#endif + if (!skb) + { + HPRINTK("charge failed (%d.%d)\n", vcc->vpi, vcc->vci); + goto return_host_buffers; + } + + if (rx_skb_reserve > 0) skb_reserve(skb, rx_skb_reserve); + + do_gettimeofday(&skb->stamp); + + for(iov = he_vcc->iov_head; + iov < he_vcc->iov_tail; ++iov) + { +#ifdef USE_RBPS + if ((u32)iov->iov_base & RBP_SMALLBUF) + memcpy(skb_put(skb, iov->iov_len), + he_dev->rbps_virt[RBP_INDEX(iov->iov_base)].virt, iov->iov_len); + else +#endif + memcpy(skb_put(skb, iov->iov_len), + he_dev->rbpl_virt[RBP_INDEX(iov->iov_base)].virt, iov->iov_len); + } + + switch(vcc->qos.aal) + { + case ATM_AAL0: + /* 2.10.1.5 raw cell receive */ + skb->len = ATM_AAL0_SDU; + skb->tail = skb->data + skb->len; + break; + case ATM_AAL5: + /* 2.10.1.2 aal5 receive */ + + skb->len = AAL5_LEN(skb->data, he_vcc->pdu_len); + skb->tail = skb->data + skb->len; +#ifdef USE_CHECKSUM_HW + if (vcc->vpi == 0 && vcc->vci >= ATM_NOT_RSV_VCI) + { + skb->ip_summed = CHECKSUM_HW; + skb->csum = TCP_CKSUM(skb->data, + he_vcc->pdu_len); + } +#endif + break; + } + +#ifdef should_never_happen + if (skb->len > vcc->qos.rxtp.max_sdu) + hprintk("pdu_len (%d) > vcc->qos.rxtp.max_sdu (%d)! cid 0x%x\n", skb->len, vcc->qos.rxtp.max_sdu, cid); +#endif + +#ifdef notdef + ATM_SKB(skb)->vcc = vcc; +#endif + vcc->push(vcc, skb); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,99) + ++vcc->stats->rx; +#else + atomic_inc(&vcc->stats->rx); +#endif + +return_host_buffers: + ++pdus_assembled; + + for(iov = he_vcc->iov_head; + iov < he_vcc->iov_tail; ++iov) + { +#ifdef USE_RBPS + if ((u32)iov->iov_base & RBP_SMALLBUF) + rbp = &he_dev->rbps_base[RBP_INDEX(iov->iov_base)]; + else +#endif + rbp = &he_dev->rbpl_base[RBP_INDEX(iov->iov_base)]; + + rbp->status &= ~RBP_LOANED; + } + + he_vcc->iov_tail = he_vcc->iov_head; + he_vcc->pdu_len = 0; + +next_rbrq_entry: + he_dev->rbrq_head = (struct he_rbrq *) + ((unsigned long) he_dev->rbrq_base | + RBRQ_MASK(++he_dev->rbrq_head)); + + } + read_unlock(&vcc_sklist_lock); + + if (updated) + { + if (updated > he_dev->rbrq_peak) he_dev->rbrq_peak = updated; + + he_writel(he_dev, RBRQ_MASK(he_dev->rbrq_head), + G0_RBRQ_H + (group * 16)); + } + + return pdus_assembled; +} + +static void +he_service_tbrq(struct he_dev *he_dev, int group) +{ + struct he_tbrq *tbrq_tail = (struct he_tbrq *) + ((unsigned long)he_dev->tbrq_base | + he_dev->hsp->group[group].tbrq_tail); + struct he_tpd *tpd; + int slot, updated = 0; + struct list_head *list; + + /* 2.1.6 transmit buffer return queue */ + + while (he_dev->tbrq_head != tbrq_tail) + { + ++updated; + + HPRINTK("tbrq%d 0x%x%s%s\n", + group, + TBRQ_TPD(he_dev->tbrq_head), + TBRQ_EOS(he_dev->tbrq_head) ? " EOS" : "", + TBRQ_MULTIPLE(he_dev->tbrq_head) ? " MULTIPLE" : ""); + + tpd = NULL; + list = &he_dev->outstanding_tpds; + while ((list = list->next) != &he_dev->outstanding_tpds) + { + struct he_tpd *_tpd = list_entry(list, struct he_tpd, entry); + if (TPD_ADDR(_tpd->status) == TBRQ_TPD(he_dev->tbrq_head)) + { + tpd = _tpd; + list_del(&_tpd->entry); + break; + } + } + + if (tpd == NULL) + { + hprintk("unable to locate tpd for dma buffer %x\n", + TBRQ_TPD(he_dev->tbrq_head)); + goto next_tbrq_entry; + } + + if (TBRQ_EOS(he_dev->tbrq_head)) + { + HPRINTK("wake_up(tx_waitq) cid 0x%x\n", + he_mkcid(he_dev, tpd->vcc->vpi, tpd->vcc->vci)); + if (tpd->vcc) + wake_up(&HE_VCC(tpd->vcc)->tx_waitq); + + goto next_tbrq_entry; + } + + for(slot = 0; slot < TPD_MAXIOV; ++slot) + { + if (tpd->iovec[slot].addr) + pci_unmap_single(he_dev->pci_dev, + tpd->iovec[slot].addr, + tpd->iovec[slot].len & TPD_LEN_MASK, + PCI_DMA_TODEVICE); + if (tpd->iovec[slot].len & TPD_LST) break; + + } + + if (tpd->skb) /* && !TBRQ_MULTIPLE(he_dev->tbrq_head) */ + { + if (tpd->vcc && tpd->vcc->pop) + tpd->vcc->pop(tpd->vcc, tpd->skb); + else + dev_kfree_skb_any(tpd->skb); + } + + +next_tbrq_entry: + pci_pool_free(he_dev->tpd_pool, tpd, TPD_ADDR(tpd->status)); + + he_dev->tbrq_head = (struct he_tbrq *) + ((unsigned long) he_dev->tbrq_base | + TBRQ_MASK(++he_dev->tbrq_head)); + } + + if (updated) + { + if (updated > he_dev->tbrq_peak) he_dev->tbrq_peak = updated; + + he_writel(he_dev, TBRQ_MASK(he_dev->tbrq_head), + G0_TBRQ_H + (group * 16)); + } +} + + +static void +he_service_rbpl(struct he_dev *he_dev, int group) +{ + struct he_rbp *newtail; + struct he_rbp *rbpl_head; + int moved = 0; + + rbpl_head = (struct he_rbp *) ((unsigned long)he_dev->rbpl_base | + RBPL_MASK(he_readl(he_dev, G0_RBPL_S))); + + for(;;) + { + newtail = (struct he_rbp *) ((unsigned long)he_dev->rbpl_base | + RBPL_MASK(he_dev->rbpl_tail+1)); + + /* table 3.42 -- rbpl_tail should never be set to rbpl_head */ + if ((newtail == rbpl_head) || (newtail->status & RBP_LOANED)) + break; + + newtail->status |= RBP_LOANED; + he_dev->rbpl_tail = newtail; + ++moved; + + } + + if (moved) + he_writel(he_dev, RBPL_MASK(he_dev->rbpl_tail), G0_RBPL_T); +} + +#ifdef USE_RBPS +static void +he_service_rbps(struct he_dev *he_dev, int group) +{ + struct he_rbp *newtail; + struct he_rbp *rbps_head; + int moved = 0; + + rbps_head = (struct he_rbp *) ((unsigned long)he_dev->rbps_base | + RBPS_MASK(he_readl(he_dev, G0_RBPS_S))); + + for(;;) + { + newtail = (struct he_rbp *) ((unsigned long)he_dev->rbps_base | + RBPS_MASK(he_dev->rbps_tail+1)); + + /* table 3.42 -- rbps_tail should never be set to rbps_head */ + if ((newtail == rbps_head) || (newtail->status & RBP_LOANED)) + break; + + newtail->status |= RBP_LOANED; + he_dev->rbps_tail = newtail; + ++moved; + + } + + if (moved) + he_writel(he_dev, RBPS_MASK(he_dev->rbps_tail), G0_RBPS_T); +} +#endif /* USE_RBPS */ + +static void +he_tasklet(unsigned long data) +{ + unsigned long flags; + struct he_dev *he_dev = (struct he_dev *) data; + int group, type; + int updated = 0; + + HPRINTK("tasklet (0x%lx)\n", data); +#ifdef USE_TASKLET + HE_SPIN_LOCK(he_dev, flags); +#endif + + while(he_dev->irq_head != he_dev->irq_tail) + { + ++updated; + + type = ITYPE_TYPE(he_dev->irq_head->isw); + group = ITYPE_GROUP(he_dev->irq_head->isw); + + switch (type) + { + case ITYPE_RBRQ_THRESH: + hprintk("rbrq%d threshold\n", group); + case ITYPE_RBRQ_TIMER: + if (he_service_rbrq(he_dev, group)) + { + he_service_rbpl(he_dev, group); +#ifdef USE_RBPS + he_service_rbps(he_dev, group); +#endif /* USE_RBPS */ + } + break; + case ITYPE_TBRQ_THRESH: + hprintk("tbrq%d threshold\n", group); + case ITYPE_TPD_COMPLETE: + he_service_tbrq(he_dev, group); + break; + case ITYPE_RBPL_THRESH: + he_service_rbpl(he_dev, group); + break; + case ITYPE_RBPS_THRESH: +#ifdef USE_RBPS + he_service_rbps(he_dev, group); +#endif /* USE_RBPS */ + break; + case ITYPE_PHY: +#ifdef CONFIG_ATM_HE_USE_SUNI + if (he_dev->atm_dev->phy && he_dev->atm_dev->phy->interrupt) + he_dev->atm_dev->phy->interrupt(he_dev->atm_dev); +#endif + HPRINTK1("phy interrupt\n"); + break; + case ITYPE_OTHER: + switch (type|group) + { + case ITYPE_PARITY: + hprintk1("parity error\n"); + break; + case ITYPE_ABORT: + hprintk("abort 0x%x\n", he_readl(he_dev, ABORT_ADDR)); + break; + } + break; + default: + if (he_dev->irq_head->isw == ITYPE_INVALID) + { + /* see 8.1.1 -- check all queues */ + + HPRINTK("isw not updated 0x%x\n", + he_dev->irq_head->isw); + + he_service_rbrq(he_dev, 0); + he_service_rbpl(he_dev, 0); +#ifdef USE_RBPS + he_service_rbps(he_dev, 0); +#endif /* USE_RBPS */ + he_service_tbrq(he_dev, 0); + } + else + hprintk("bad isw = 0x%x?\n", + he_dev->irq_head->isw); + } + + he_dev->irq_head->isw = ITYPE_INVALID; + + he_dev->irq_head = (struct he_irq *) NEXT_ENTRY(he_dev->irq_base, he_dev->irq_head, IRQ_MASK); + } + + if (updated) + { + if (updated > he_dev->irq_peak) he_dev->irq_peak = updated; + + he_writel(he_dev, + IRQ_SIZE(CONFIG_IRQ_SIZE) | + IRQ_THRESH(CONFIG_IRQ_THRESH) | + IRQ_TAIL(he_dev->irq_tail), IRQ0_HEAD); + (void) he_readl(he_dev, INT_FIFO); /* 8.1.2 controller errata */ + } +#ifdef USE_TASKLET + HE_SPIN_UNLOCK(he_dev, flags); +#endif +} + +static void +he_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long flags; + struct he_dev *he_dev = (struct he_dev * )dev_id; + + if (he_dev == NULL) return; + + HE_SPIN_LOCK(he_dev, flags); + + he_dev->irq_tail = (struct he_irq *) (((unsigned long)he_dev->irq_base) | + (*he_dev->irq_tailoffset << 2)); + + if (he_dev->irq_tail == he_dev->irq_head) + { + HPRINTK1("tailoffset not updated?\n"); + he_dev->irq_tail = (struct he_irq *) ((unsigned long)he_dev->irq_base | + ((he_readl(he_dev, IRQ0_BASE) & IRQ_MASK) << 2)); + (void) he_readl(he_dev, INT_FIFO); /* 8.1.2 controller errata */ + } + +#ifdef notdef + if (he_dev->irq_head == he_dev->irq_tail /* && !IRQ_PENDING */) + hprintk1("spurious interrupt?\n"); +#endif + + if (he_dev->irq_head != he_dev->irq_tail) + { +#ifdef USE_TASKLET + tasklet_schedule(&he_dev->tasklet); +#else + he_tasklet((unsigned long) he_dev); +#endif + he_writel(he_dev, INT_CLEAR_A, INT_FIFO); + /* clear interrupt */ + } + + HE_SPIN_UNLOCK(he_dev, flags); +} + +static __inline__ void +he_enqueue_tpd(struct he_dev *he_dev, struct he_tpd *tpd, unsigned cid) +{ + struct he_tpdrq *new_tail; + unsigned flags; + + spin_lock_irqsave(&he_dev->tpdrq_lock, flags); + + HPRINTK("tpdrq %p cid 0x%x -> tpdrq_tail %p\n", + tpd, cid, he_dev->tpdrq_tail); + + new_tail = he_dev->tpdrq_tail; + new_tail = (struct he_tpdrq *) ((unsigned long) he_dev->tpdrq_base | + TPDRQ_MASK(++new_tail)); + + /* + * check to see if we are about to set the tail == head + * if true, update the head pointer from the adapter + * to see if this is really the case (reading the queue + * head for every enqueue would be unnecessarily slow) + */ + + if (new_tail == he_dev->tpdrq_head) + { + he_dev->tpdrq_head = (struct he_tpdrq *) + (((unsigned long)he_dev->tpdrq_base) | + TPDRQ_MASK(he_readl(he_dev, TPDRQ_B_H))); + + if (new_tail == he_dev->tpdrq_head) + { + hprintk("tpdrq full (cid 0x%x)\n", cid); + /* + * FIXME + * push tpd onto a transmit backlog queue + * after service_tbrq, service the backlog + * for now, we just drop the pdu + */ + if (tpd->skb) + { + if (tpd->vcc->pop) + tpd->vcc->pop(tpd->vcc, tpd->skb); + else + dev_kfree_skb_any(tpd->skb); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,99) + ++tpd->vcc->stats->tx_err; +#else + atomic_inc(&tpd->vcc->stats->tx_err); +#endif + } + pci_pool_free(he_dev->tpd_pool, tpd, TPD_ADDR(tpd->status)); + spin_unlock_irqrestore(&he_dev->tpdrq_lock, flags); + return; + } + } + + /* 2.1.5 transmit packet descriptor ready queue */ + he_dev->tpdrq_tail->tpd = TPD_ADDR(tpd->status); + he_dev->tpdrq_tail->cid = cid; + mb(); + list_add_tail(&tpd->entry, &he_dev->outstanding_tpds); + + he_dev->tpdrq_tail = new_tail; + + he_writel(he_dev, TPDRQ_MASK(he_dev->tpdrq_tail), TPDRQ_T); + spin_unlock_irqrestore(&he_dev->tpdrq_lock, flags); +} + +static int +he_open(struct atm_vcc *vcc, short vpi, int vci) +{ + unsigned long flags; + struct he_dev *he_dev = HE_DEV(vcc->dev); + struct he_vcc *he_vcc; + int err = 0; + unsigned cid, rsr0, rsr1, rsr4, tsr0, period, reg, clock; + int first_open = 0; + + if (vci == ATM_VCI_UNSPEC || vpi == ATM_VPI_UNSPEC) return 0; + vcc->vpi = vpi; + vcc->vci = vci; + + HPRINTK("open vcc %p %d.%d\n", vcc, vpi, vci); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + vcc->flags |= ATM_VF_ADDR; +#else + set_bit(ATM_VF_ADDR, &vcc->flags); +#endif + + cid = he_mkcid(he_dev, vpi, vci); + + he_vcc = (struct he_vcc *) kmalloc(sizeof(struct he_vcc), GFP_KERNEL); + if (he_vcc == NULL) + { + hprintk1("unable to allocate he_vcc during open\n"); + return -ENOMEM; + } + + he_vcc->iov_tail = he_vcc->iov_head; + he_vcc->pdu_len = 0; + he_vcc->rc_index = -1; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + init_waitqueue(&he_vcc->rx_waitq); + init_waitqueue(&he_vcc->tx_waitq); +#else + init_waitqueue_head(&he_vcc->rx_waitq); + init_waitqueue_head(&he_vcc->tx_waitq); +#endif + + HE_VCC(vcc) = he_vcc; + + if (vcc->qos.txtp.traffic_class != ATM_NONE) + { + int pcr_goal; + + pcr_goal = atm_pcr_goal(&vcc->qos.txtp); + if (pcr_goal == 0) + pcr_goal = he_dev->atm_dev->link_rate; + if (pcr_goal < 0) /* means round down, technically */ + pcr_goal = -pcr_goal; + + HPRINTK("open tx cid 0x%x pcr_goal %d\n", cid, pcr_goal); + + /* no transmit support for AAL0 -- FIXME */ + + if (vcc->qos.aal != ATM_AAL5) + { + err = -EINVAL; + goto open_failed; + } + + HE_SPIN_LOCK(he_dev, flags); + tsr0 = he_readl_tsr0(he_dev, cid); + HE_SPIN_UNLOCK(he_dev, flags); + + if (tsr0 == 0 && vcc->qos.aal == ATM_AAL5) first_open = 1; + + if (TSR0_CONN_STATE(tsr0) != 0) + { + hprintk("cid 0x%x not idle (tsr0 = 0x%x)\n", cid, tsr0); +#ifdef notdef + err = -EBUSY; + goto open_failed; +#endif + } + + switch(vcc->qos.txtp.traffic_class) + { + case ATM_UBR: + /* 2.3.3.1 open connection ubr */ + + tsr0 = TSR0_UBR | TSR0_GROUP(0) | TSR0_AAL5 | + TSR0_USE_WMIN | TSR0_UPDATE_GER; + break; + + case ATM_CBR: + /* 2.3.3.2 open connection cbr */ + + clock = he_is622(he_dev) ? 66667000 : 50000000; + period = clock / pcr_goal; + + /* find an unused cs_stper register */ + + for(reg = 0; reg < HE_NUM_CS_STPER; ++reg) + if (he_dev->cs_stper[reg].inuse == 0 || + he_dev->cs_stper[reg].pcr == pcr_goal) + break; + + if (reg == HE_NUM_CS_STPER) + { + err = -EBUSY; + goto open_failed; + } + + /* 8.2.3 cbr scheduler wrap problem */ + if ((he_dev->total_bw + pcr_goal) + > (he_dev->atm_dev->link_rate * 10 / 9)) + { + err = -EBUSY; + goto open_failed; + } + he_dev->total_bw += pcr_goal; + + he_vcc->rc_index = reg; + ++he_dev->cs_stper[reg].inuse; + he_dev->cs_stper[reg].pcr = pcr_goal; + + HPRINTK("rc_index = %d period = %d\n", + reg, period); + + HE_SPIN_LOCK(he_dev, flags); + he_writel_mbox(he_dev, rate_to_atmf(period/2), + CS_STPER0 + reg); + HE_SPIN_UNLOCK(he_dev, flags); + + tsr0 = TSR0_CBR | TSR0_GROUP(0) | TSR0_AAL5 | + TSR0_RC_INDEX(reg); + + break; + default: + err = -EINVAL; + goto open_failed; + } + + HE_SPIN_LOCK(he_dev, flags); + + he_writel_tsr0(he_dev, tsr0, cid); + he_writel_tsr4(he_dev, TSR4_AAL5 | 1, cid); + he_writel_tsr1(he_dev, TSR1_MCR(rate_to_atmf(0)) | + TSR1_PCR(rate_to_atmf(pcr_goal)), cid); + he_writel_tsr2(he_dev, TSR2_ACR(rate_to_atmf(pcr_goal)), cid); + he_writel_tsr9(he_dev, TSR9_OPEN_CONN, cid); + + he_writel_tsr3(he_dev, 0x0, cid); + he_writel_tsr5(he_dev, 0x0, cid); + he_writel_tsr6(he_dev, 0x0, cid); + he_writel_tsr7(he_dev, 0x0, cid); + he_writel_tsr8(he_dev, 0x0, cid); + he_writel_tsr10(he_dev, 0x0, cid); + he_writel_tsr11(he_dev, 0x0, cid); + he_writel_tsr12(he_dev, 0x0, cid); + he_writel_tsr13(he_dev, 0x0, cid); + he_writel_tsr14(he_dev, 0x0, cid); + + /* work around: + * + * the first aal5 pdu sent always seems to have a bad + * crc. no idea why. probably initializing the card + * wrong? if tsr0 == 0 this is the first open after + * a reset. + */ + + if (first_open) + { +#define BADCRCMESG "BAD CRC WORKAROUND" + static char *badcrc = NULL; + struct he_tpd *tpd; + + tpd = he_alloc_tpd(he_dev); + + if (!badcrc) + { + badcrc = kmalloc(strlen(BADCRCMESG), GFP_DMA); + memcpy(badcrc, BADCRCMESG, strlen(BADCRCMESG)); + } + + HPRINTK("bad crc workaround cid = 0x%x\n", cid); + + tpd->address0 = pci_map_single(he_dev->pci_dev, + badcrc, strlen(BADCRCMESG), PCI_DMA_TODEVICE); + tpd->length0 = strlen(BADCRCMESG) | TPD_LST; + tpd->status |= TPD_USERCELL | TPD_INT; + mb(); + tpd->skb = NULL; + tpd->vcc = vcc; + + he_enqueue_tpd(he_dev, tpd, cid); + } + + HE_SPIN_UNLOCK(he_dev, flags); + } + + if (vcc->qos.rxtp.traffic_class != ATM_NONE) + { + unsigned aal; + + HPRINTK("open rx cid 0x%x (rx_waitq %p)\n", cid, + &HE_VCC(vcc)->rx_waitq); + + switch (vcc->qos.aal) + { + case ATM_AAL5: + aal = RSR0_AAL5; + break; + case ATM_AAL0: + aal = RSR0_RAWCELL; + break; + default: + err = -EINVAL; + goto open_failed; + } + + HE_SPIN_LOCK(he_dev, flags); + + rsr0 = he_readl_rsr0(he_dev, cid); + if (rsr0 & RSR0_OPEN_CONN) + { + HE_SPIN_UNLOCK(he_dev, flags); + + hprintk("cid 0x%x not idle (rsr0 = 0x%x)\n", cid, rsr0); + err = -EBUSY; + goto open_failed; + } + +#ifdef USE_RBPS + rsr1 = RSR1_GROUP(0); + rsr4 = RSR4_GROUP(0); +#else /* !USE_RBPS */ + rsr1 = RSR1_GROUP(0)|RSR1_RBPL_ONLY; + rsr4 = RSR4_GROUP(0)|RSR4_RBPL_ONLY; +#endif /* USE_RBPS */ + rsr0 = vcc->qos.rxtp.traffic_class == ATM_UBR ? + (RSR0_EPD_ENABLE|RSR0_PPD_ENABLE) : 0; + +#ifdef USE_CHECKSUM_HW + if (vpi == 0 && vci >= ATM_NOT_RSV_VCI) rsr0 |= RSR0_TCP_CKSUM; +#endif + + he_writel_rsr4(he_dev, rsr4, cid); + he_writel_rsr1(he_dev, rsr1, cid); + /* 5.1.11 last parameter initialized should be + the open/closed indication in rsr0 */ + he_writel_rsr0(he_dev, + rsr0 | RSR0_START_PDU | RSR0_OPEN_CONN | aal, cid); + + HE_SPIN_UNLOCK(he_dev, flags); + +#ifndef USE_HE_FIND_VCC + HE_LOOKUP_VCC(he_dev, cid) = vcc; +#endif + } + +open_failed: + + if (err) + { + if (he_vcc) kfree(he_vcc); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + vcc->flags &= ~ATM_VF_ADDR; +#else + clear_bit(ATM_VF_ADDR, &vcc->flags); +#endif + } + else + { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + vcc->flags |= ATM_VF_READY; +#else + set_bit(ATM_VF_READY, &vcc->flags); +#endif + } + + return err; +} + +static void +he_close(struct atm_vcc *vcc) +{ + unsigned long flags; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,1) + DECLARE_WAITQUEUE(wait, current); +#else + struct wait_queue wait = { current, NULL }; +#endif + struct he_dev *he_dev = HE_DEV(vcc->dev); + struct he_tpd *tpd; + unsigned cid; + struct he_vcc *he_vcc = HE_VCC(vcc); +#define MAX_RETRY 30 + int retry = 0, sleep = 1, tx_inuse; + + HPRINTK("close vcc %p %d.%d\n", vcc, vcc->vpi, vcc->vci); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + vcc->flags &= ~ATM_VF_READY; +#else + clear_bit(ATM_VF_READY, &vcc->flags); +#endif + cid = he_mkcid(he_dev, vcc->vpi, vcc->vci); + + HE_SPIN_LOCK(he_dev, flags); + + if (vcc->qos.rxtp.traffic_class != ATM_NONE) + { + int timeout; + + HPRINTK("close rx cid 0x%x\n", cid); + + /* 2.7.2.2 close receive operation */ + + /* wait for previous close (if any) to finish */ + + while(he_readl(he_dev, RCC_STAT) & RCC_BUSY) + { + HPRINTK("close cid 0x%x RCC_BUSY\n", cid); + udelay(250); + } + + add_wait_queue(&he_vcc->rx_waitq, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + + he_writel_rsr0(he_dev, RSR0_CLOSE_CONN, cid); + he_writel_mbox(he_dev, cid, RXCON_CLOSE); + + HE_SPIN_UNLOCK(he_dev, flags); + timeout = schedule_timeout(30*HZ); + HE_SPIN_LOCK(he_dev, flags); + remove_wait_queue(&he_vcc->rx_waitq, &wait); + set_current_state(TASK_RUNNING); + + if (timeout == 0) + hprintk("close rx timeout cid 0x%x\n", cid); + +#ifndef USE_HE_FIND_VCC + HE_LOOKUP_VCC(he_dev, cid) = NULL; +#endif + HPRINTK("close rx cid 0x%x complete\n", cid); + + } + + if (vcc->qos.txtp.traffic_class != ATM_NONE) + { + volatile unsigned tsr4, tsr0; + int timeout; + + HPRINTK("close tx cid 0x%x\n", cid); + + /* 2.1.2 + * + * ... the host must first stop queueing packets to the TPDRQ + * on the connection to be closed, then wait for all outstanding + * packets to be transmitted and their buffers returned to the + * TBRQ. When the last packet on the connection arrives in the + * TBRQ, the host issues the close command to the adapter. + */ + + HE_SPIN_UNLOCK(he_dev, flags); + while (((tx_inuse = atomic_read(&vcc->sk->wmem_alloc)) > 0) + && (retry < MAX_RETRY)) + { + set_current_state(TASK_UNINTERRUPTIBLE); + (void) schedule_timeout(sleep); + set_current_state(TASK_RUNNING); + if (sleep < HZ) sleep = sleep * 2; + + ++retry; + } + + if (tx_inuse) hprintk("close tx cid 0x%x tx_inuse = %d\n", + cid, tx_inuse); + HE_SPIN_LOCK(he_dev, flags); + + /* 2.3.1.1 generic close operations with flush */ + + he_writel_tsr4_upper(he_dev, TSR4_FLUSH_CONN, cid); + /* also clears TSR4_SESSION_ENDED */ + + switch(vcc->qos.txtp.traffic_class) + { + case ATM_UBR: + he_writel_tsr1(he_dev, + TSR1_MCR(rate_to_atmf(200000)) + | TSR1_PCR(0), cid); + break; + case ATM_CBR: + he_writel_tsr14_upper(he_dev, TSR14_DELETE, cid); + break; + } + + + tpd = he_alloc_tpd(he_dev); + if (tpd == NULL) + { + hprintk("close tx he_alloc_tpd failed cid 0x%x\n", cid); + goto close_tx_incomplete; + } + tpd->status |= TPD_EOS | TPD_INT; + tpd->skb = NULL; + tpd->vcc = vcc; + mb(); + + add_wait_queue(&he_vcc->tx_waitq, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + he_enqueue_tpd(he_dev, tpd, cid); + HE_SPIN_UNLOCK(he_dev, flags); + timeout = schedule_timeout(30*HZ); + HE_SPIN_LOCK(he_dev, flags); + remove_wait_queue(&he_vcc->tx_waitq, &wait); + set_current_state(TASK_RUNNING); + + if (timeout == 0) + { + hprintk("close tx timeout cid 0x%x\n", cid); + goto close_tx_incomplete; + } + + while (!((tsr4 = he_readl_tsr4(he_dev, cid)) + & TSR4_SESSION_ENDED)) + { + HPRINTK("close tx cid 0x%x !TSR4_SESSION_ENDED (tsr4 = 0x%x)\n", cid, tsr4); + udelay(250); + } + + while (TSR0_CONN_STATE(tsr0 = he_readl_tsr0(he_dev, cid)) != 0) + { + HPRINTK("close tx cid 0x%x TSR0_CONN_STATE != 0 (tsr0 = 0x%x)\n", cid, tsr0); + udelay(250); + } + +close_tx_incomplete: + + if (vcc->qos.txtp.traffic_class == ATM_CBR) + { + int reg = he_vcc->rc_index; + + HPRINTK("cs_stper reg = %d\n", reg); + + if (he_dev->cs_stper[reg].inuse == 0) + hprintk("cs_stper[%d].inuse = 0!\n", reg); + else + --he_dev->cs_stper[reg].inuse; + + he_dev->total_bw -= he_dev->cs_stper[reg].pcr; + } + + HPRINTK("close tx cid 0x%x complete\n", cid); + } + + kfree(he_vcc); + HE_SPIN_UNLOCK(he_dev, flags); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + vcc->flags &= ~ATM_VF_ADDR; +#else + clear_bit(ATM_VF_ADDR, &vcc->flags); +#endif +} + +static int +he_sg_send(struct atm_vcc *vcc, unsigned long start, unsigned long size) +{ +#ifdef USE_SCATTERGATHER + return 1; +#else + return 0; +#endif +} + +static int +he_send(struct atm_vcc *vcc, struct sk_buff *skb) +{ + unsigned long flags; + struct he_dev *he_dev = HE_DEV(vcc->dev); + unsigned cid = he_mkcid(he_dev, vcc->vpi, vcc->vci); + struct he_tpd *tpd; +#ifdef USE_SCATTERGATHER + int i, slot = 0; +#endif + +#define HE_TPD_BUFSIZE 0xffff + + HPRINTK("send %d.%d\n", vcc->vpi, vcc->vci); + + if (skb->len > HE_TPD_BUFSIZE ) + { + hprintk("buffer too large (%d bytes)\n", skb->len ); + if (vcc->pop) + vcc->pop(vcc, skb); + else + dev_kfree_skb_any(skb); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,99) + ++vcc->stats->tx_err; +#else + atomic_inc(&vcc->stats->tx_err); +#endif + return -EINVAL; + } + +#ifndef USE_SCATTERGATHER + if (skb_shinfo(skb)->nr_frags) + { + hprintk1("no scatter/gather support\n"); + if (vcc->pop) + vcc->pop(vcc, skb); + else + dev_kfree_skb_any(skb); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,99) + ++vcc->stats->tx_err; +#else + atomic_inc(&vcc->stats->tx_err); +#endif + return -EINVAL; + } +#endif + + tpd = he_alloc_tpd(he_dev); + if (tpd == NULL) + { + if (vcc->pop) + vcc->pop(vcc, skb); + else + dev_kfree_skb_any(skb); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,99) + ++vcc->stats->tx_err; +#else + atomic_inc(&vcc->stats->tx_err); +#endif + return -ENOMEM; + } + tpd->status |= TPD_USERCELL; + +#ifdef USE_SCATTERGATHER + tpd->iovec[slot].addr = pci_map_single(he_dev->pci_dev, skb->data, + skb->len - skb->data_len, PCI_DMA_TODEVICE); + tpd->iovec[slot].len = skb->len - skb->data_len; + ++slot; + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) + { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + if (slot == TPD_MAXIOV) /* send tpd; start new tpd */ + { + tpd->vcc = vcc; + tpd->skb = NULL; /* not the last fragment + so dont ->push() yet */ + mb(); + + HE_SPIN_LOCK(he_dev, flags); + he_enqueue_tpd(he_dev, tpd, cid); + HE_SPIN_UNLOCK(he_dev, flags); + tpd = he_alloc_tpd(he_dev); + if (tpd == NULL) + { + if (vcc->pop) + vcc->pop(vcc, skb); + else + dev_kfree_skb_any(skb); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,99) + ++vcc->stats->tx_err; +#else + atomic_inc(&vcc->stats->tx_err); +#endif + return -ENOMEM; + } + tpd->status |= TPD_USERCELL; + slot = 0; + } + + tpd->iovec[slot].addr = pci_map_single(he_dev->pci_dev, + (void *) page_address(frag->page) + frag->page_offset, + frag->size, PCI_DMA_TODEVICE); + tpd->iovec[slot].len = frag->size; + ++slot; + + } + + tpd->iovec[slot-1].len |= TPD_LST; +#else + tpd->address0 = pci_map_single(he_dev->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE); + tpd->length0 = skb->len | TPD_LST; +#endif + tpd->status |= TPD_INT; + + tpd->vcc = vcc; + tpd->skb = skb; + mb(); + ATM_SKB(skb)->vcc = vcc; + + HE_SPIN_LOCK(he_dev, flags); + he_enqueue_tpd(he_dev, tpd, cid); + HE_SPIN_UNLOCK(he_dev, flags); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,99) + ++vcc->stats->tx; +#else + atomic_inc(&vcc->stats->tx); +#endif + + return 0; +} + +static int +he_ioctl(struct atm_dev *atm_dev, unsigned int cmd, void *arg) +{ + unsigned long flags; + struct he_dev *he_dev = HE_DEV(atm_dev); + struct he_ioctl_reg reg; + int err = 0; + + switch (cmd) + { + case HE_GET_REG: + if (!capable(CAP_NET_ADMIN)) return -EPERM; + + copy_from_user(®, (struct he_ioctl_reg *) arg, + sizeof(struct he_ioctl_reg)); + HE_SPIN_LOCK(he_dev, flags); + switch (reg.type) + { + case HE_REGTYPE_PCI: + reg.val = he_readl(he_dev, reg.addr); + break; + case HE_REGTYPE_RCM: + reg.val = + he_readl_rcm(he_dev, reg.addr); + break; + case HE_REGTYPE_TCM: + reg.val = + he_readl_tcm(he_dev, reg.addr); + break; + case HE_REGTYPE_MBOX: + reg.val = + he_readl_mbox(he_dev, reg.addr); + break; + default: + HE_SPIN_UNLOCK(he_dev, flags); + return -EINVAL; + } + HE_SPIN_UNLOCK(he_dev, flags); + copy_to_user((struct he_ioctl_reg *) arg, ®, + sizeof(struct he_ioctl_reg)); + break; + default: +#ifdef CONFIG_ATM_HE_USE_SUNI + HE_SPIN_LOCK(he_dev, flags); + if (atm_dev->phy && atm_dev->phy->ioctl) + err = atm_dev->phy->ioctl(atm_dev, cmd, arg); + HE_SPIN_UNLOCK(he_dev, flags); +#else /* CONFIG_ATM_HE_USE_SUNI */ + return -EINVAL; +#endif /* CONFIG_ATM_HE_USE_SUNI */ + break; + } + + return err; +} + +static void +he_phy_put(struct atm_dev *atm_dev, unsigned char val, unsigned long addr) +{ + struct he_dev *he_dev = HE_DEV(atm_dev); + + HPRINTK("phy_put(val 0x%x, addr 0x%lx)\n", val, addr); + + he_writel(he_dev, val, FRAMER + (addr*4)); +} + + +static unsigned char +he_phy_get(struct atm_dev *atm_dev, unsigned long addr) +{ + struct he_dev *he_dev = HE_DEV(atm_dev); + unsigned reg; + + reg = he_readl(he_dev, FRAMER + (addr*4)); + + HPRINTK("phy_get(addr 0x%lx) =0x%lx\n", addr, reg); + return reg; +} + +static int +he_proc_read(struct atm_dev *dev, loff_t *pos, char *page) +{ + unsigned long flags; + struct he_dev *he_dev = HE_DEV(dev); + int left, i; +#ifdef notdef + struct he_rbrq *rbrq_tail; + struct he_tpdrq *tpdrq_head; + int rbpl_head, rbpl_tail; +#endif + static long mcc = 0, oec = 0, dcc = 0, cec = 0; + + + left = *pos; + if (!left--) + return sprintf(page, "%s\n", version); + + if (!left--) + return sprintf(page, "%s%s\n\n", + he_dev->prod_id, he_dev->media & 0x40 ? "SM" : "MM"); + + if (!left--) + return sprintf(page, "Mismatched Cells VPI/VCI Not Open Dropped Cells RCM Dropped Cells\n"); + + HE_SPIN_LOCK(he_dev, flags); + mcc += he_readl(he_dev, MCC); + oec += he_readl(he_dev, OEC); + dcc += he_readl(he_dev, DCC); + cec += he_readl(he_dev, CEC); + HE_SPIN_UNLOCK(he_dev, flags); + + if (!left--) + return sprintf(page, "%16ld %16ld %13ld %17ld\n\n", + mcc, oec, dcc, cec); + + if (!left--) + return sprintf(page, "irq_size = %d inuse = ? peak = %d\n", + CONFIG_IRQ_SIZE, he_dev->irq_peak); + + if (!left--) + return sprintf(page, "tpdrq_size = %d inuse = ?\n", + CONFIG_TPDRQ_SIZE); + + if (!left--) + return sprintf(page, "rbrq_size = %d inuse = ? peak = %d\n", + CONFIG_RBRQ_SIZE, he_dev->rbrq_peak); + + if (!left--) + return sprintf(page, "tbrq_size = %d peak = %d\n", + CONFIG_TBRQ_SIZE, he_dev->tbrq_peak); + + +#ifdef notdef + rbpl_head = RBPL_MASK(he_readl(he_dev, G0_RBPL_S)); + rbpl_tail = RBPL_MASK(he_readl(he_dev, G0_RBPL_T)); + + inuse = rbpl_head - rbpl_tail; + if (inuse < 0) inuse += CONFIG_RBPL_SIZE * sizeof(struct he_rbp); + inuse /= sizeof(struct he_rbp); + + if (!left--) + return sprintf(page, "rbpl_size = %d inuse = %d\n\n", + CONFIG_RBPL_SIZE, inuse); +#endif + + if (!left--) + return sprintf(page, "rate controller periods (cbr)\n pcr #vc\n"); + + for (i = 0; i < HE_NUM_CS_STPER; ++i) + if (!left--) + return sprintf(page, "cs_stper%-2d %8ld %3d\n", i, + he_dev->cs_stper[i].pcr, + he_dev->cs_stper[i].inuse); + + if (!left--) + return sprintf(page, "total bw (cbr): %d (limit %d)\n", + he_dev->total_bw, he_dev->atm_dev->link_rate * 10 / 9); + + return 0; +} + +/* eeprom routines -- see 4.7 */ + +u8 +read_prom_byte(struct he_dev *he_dev, int addr) +{ + u32 val = 0, tmp_read = 0; + int i, j = 0; + u8 byte_read = 0; + + val = readl(he_dev->membase + HOST_CNTL); + val &= 0xFFFFE0FF; + + /* Turn on write enable */ + val |= 0x800; + writel(val, he_dev->membase + HOST_CNTL); + + /* Send READ instruction */ + for (i=0; imembase + HOST_CNTL); + udelay(EEPROM_DELAY); + } + + /* Next, we need to send the byte address to read from */ + for (i=7; i>=0; i--) { + writel(val | clocktab[j++] | (((addr >> i) & 1) << 9), + he_dev->membase + HOST_CNTL); + udelay(EEPROM_DELAY); + writel(val | clocktab[j++] | (((addr >> i) & 1) << 9), + he_dev->membase + HOST_CNTL); + udelay(EEPROM_DELAY); + } + + j=0; + + val &= 0xFFFFF7FF; /* Turn off write enable */ + writel(val, he_dev->membase + HOST_CNTL); + + /* Now, we can read data from the EEPROM by clocking it in */ + for (i=7; i>=0; i--) { + writel(val | clocktab[j++], he_dev->membase + HOST_CNTL); + udelay(EEPROM_DELAY); + tmp_read = readl(he_dev->membase + HOST_CNTL); + byte_read |= (unsigned char) + ((tmp_read & ID_DOUT) + >> ID_DOFFSET << i); + writel(val | clocktab[j++], + he_dev->membase + HOST_CNTL); + udelay(EEPROM_DELAY); + } + + writel(val | clocktab[j++], he_dev->membase + HOST_CNTL); + udelay(EEPROM_DELAY); + + return (byte_read); +} + +MODULE_AUTHOR("chas williams "); +MODULE_DESCRIPTION("ForeRunnerHE ATM Adapter driver"); +MODULE_PARM(disable64, "h"); +MODULE_PARM_DESC(disable64, "disable 64-bit pci bus transfers"); +MODULE_PARM(nvpibits, "i"); +MODULE_PARM_DESC(nvpibits, "numbers of bits for vpi (default 0)"); +MODULE_PARM(nvcibits, "i"); +MODULE_PARM_DESC(nvcibits, "numbers of bits for vci (default 12)"); +MODULE_PARM(rx_skb_reserve, "i"); +MODULE_PARM_DESC(rx_skb_reserve, "padding for receive skb (default 16)"); +MODULE_PARM(irq_coalesce, "i"); +MODULE_PARM_DESC(irq_coalesce, "use interrupt coalescing (default 1)"); +MODULE_PARM(sdh, "i"); +MODULE_PARM_DESC(sdh, "use SDH framing (default 0)"); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,1) +static struct pci_device_id he_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_FORE, PCI_DEVICE_ID_FORE_HE, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0 }, + { 0, } +}; + +static struct pci_driver he_driver = { + .name = "he", + .probe = he_init_one, + .remove = __devexit_p(he_remove_one), + .id_table = he_pci_tbl, +}; + +static int __init he_init(void) +{ + return pci_module_init(&he_driver); +} + +static void __exit he_cleanup(void) +{ + pci_unregister_driver(&he_driver); +} + +module_init(he_init); +module_exit(he_cleanup); +#else +static int __init +he_init() +{ + if (!pci_present()) + return -EIO; + +#ifdef CONFIG_ATM_HE_USE_SUNI_MODULE + /* request_module("suni"); */ +#endif + + pci_dev = NULL; + while ((pci_dev = pci_find_device(PCI_VENDOR_ID_FORE, + PCI_DEVICE_ID_FORE_HE, pci_dev)) != NULL) + if (he_init_one(pci_dev, NULL) == 0) + ++ndevs; + + return (ndevs ? 0 : -ENODEV); +} + +static void __devexit +he_cleanup(void) +{ + while (he_devs) + { + struct he_dev *next = he_devs->next; + he_stop(he_devs); + atm_dev_deregister(he_devs->atm_dev); + kfree(he_devs); + + he_devs = next; + } + +} + +int init_module(void) +{ + return he_init(); +} + +void cleanup_module(void) +{ + he_cleanup(); +} +#endif + +MODULE_LICENSE("GPL"); diff -urN linux-2.4.20/drivers/atm/he.h linux-2.4.20-atm/drivers/atm/he.h --- linux-2.4.20/drivers/atm/he.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-atm/drivers/atm/he.h Wed May 28 01:58:41 2003 @@ -0,0 +1,910 @@ +/* $Id$ */ + +/* + + he.h + + ForeRunnerHE ATM Adapter driver for ATM on Linux + Copyright (C) 1999-2001 Naval Research Laboratory + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +/* + + he.h + + ForeRunnerHE ATM Adapter driver for ATM on Linux + Copyright (C) 1999-2000 Naval Research Laboratory + + Permission to use, copy, modify and distribute this software and its + documentation is hereby granted, provided that both the copyright + notice and this permission notice appear in all copies of the software, + derivative works or modified versions, and any portions thereof, and + that both notices appear in supporting documentation. + + NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND + DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER + RESULTING FROM THE USE OF THIS SOFTWARE. + + */ + +#ifndef _HE_H_ +#define _HE_H_ + +#define DEV_LABEL "he" + +#define CONFIG_DEFAULT_VCIBITS 12 +#define CONFIG_DEFAULT_VPIBITS 0 + +#define CONFIG_IRQ_SIZE 128 +#define CONFIG_IRQ_THRESH (CONFIG_IRQ_SIZE/2) + +#define CONFIG_TPDRQ_SIZE 512 +#define TPDRQ_MASK(x) (((unsigned long)(x))&((CONFIG_TPDRQ_SIZE<<3)-1)) + +#define CONFIG_RBRQ_SIZE 512 +#define CONFIG_RBRQ_THRESH 400 +#define RBRQ_MASK(x) (((unsigned long)(x))&((CONFIG_RBRQ_SIZE<<3)-1)) + +#define CONFIG_TBRQ_SIZE 512 +#define CONFIG_TBRQ_THRESH 400 +#define TBRQ_MASK(x) (((unsigned long)(x))&((CONFIG_TBRQ_SIZE<<2)-1)) + +#define CONFIG_RBPL_SIZE 512 +#define CONFIG_RBPL_THRESH 64 +#define CONFIG_RBPL_BUFSIZE 4096 +#define RBPL_MASK(x) (((unsigned long)(x))&((CONFIG_RBPL_SIZE<<3)-1)) + +#define CONFIG_RBPS_SIZE 1024 +#define CONFIG_RBPS_THRESH 64 +#define CONFIG_RBPS_BUFSIZE 128 +#define RBPS_MASK(x) (((unsigned long)(x))&((CONFIG_RBPS_SIZE<<3)-1)) + +/* 5.1.3 initialize connection memory */ + +#define CONFIG_RSRA 0x00000 +#define CONFIG_RCMLBM 0x08000 +#define CONFIG_RCMABR 0x0d800 +#define CONFIG_RSRB 0x0e000 + +#define CONFIG_TSRA 0x00000 +#define CONFIG_TSRB 0x08000 +#define CONFIG_TSRC 0x0c000 +#define CONFIG_TSRD 0x0e000 +#define CONFIG_TMABR 0x0f000 +#define CONFIG_TPDBA 0x10000 + +#define HE_MAXCIDBITS 12 + +/* 2.9.3.3 interrupt encodings */ + +struct he_irq { + volatile u32 isw; +}; + +#define IRQ_ALIGNMENT 0x1000 + +#define NEXT_ENTRY(base, tail, mask) \ + (((unsigned long)base)|(((unsigned long)(tail+1))&mask)) + +#define ITYPE_INVALID 0xffffffff +#define ITYPE_TBRQ_THRESH (0<<3) +#define ITYPE_TPD_COMPLETE (1<<3) +#define ITYPE_RBPS_THRESH (2<<3) +#define ITYPE_RBPL_THRESH (3<<3) +#define ITYPE_RBRQ_THRESH (4<<3) +#define ITYPE_RBRQ_TIMER (5<<3) +#define ITYPE_PHY (6<<3) +#define ITYPE_OTHER 0x80 +#define ITYPE_PARITY 0x81 +#define ITYPE_ABORT 0x82 + +#define ITYPE_GROUP(x) (x & 0x7) +#define ITYPE_TYPE(x) (x & 0xf8) + +#define HE_NUM_GROUPS 8 + +/* 2.1.4 transmit packet descriptor */ + +struct he_tpd { + + /* read by the adapter */ + + volatile u32 status; + volatile u32 reserved; + +#define TPD_MAXIOV 3 + struct { + u32 addr, len; + } iovec[TPD_MAXIOV]; + + +#define address0 iovec[0].addr +#define length0 iovec[0].len + + /* linux-atm extensions */ + + struct sk_buff *skb; + struct atm_vcc *vcc; + + struct list_head entry; +}; + +#define TPD_ALIGNMENT 64 +#define TPD_LEN_MASK 0xffff + +#define TPD_ADDR(x) ((x) & 0xffffffc0) + + +/* table 2.3 transmit buffer return elements */ + +struct he_tbrq { + volatile u32 tbre; +}; + +#define TBRQ_ALIGNMENT CONFIG_TBRQ_SIZE + +#define TBRQ_TPD(tbrq) ((tbrq)->tbre & 0xffffffc0) +#define TBRQ_EOS(tbrq) ((tbrq)->tbre & (1<<3)) +#define TBRQ_MULTIPLE(tbrq) ((tbrq)->tbre & (1)) + +/* table 2.21 receive buffer return queue element field organization */ + +struct he_rbrq { + volatile u32 addr; + volatile u32 cidlen; +}; + +#define RBRQ_ALIGNMENT CONFIG_RBRQ_SIZE + +#define RBRQ_ADDR(rbrq) ((rbrq)->addr & 0xffffffc0) +#define RBRQ_CRC_ERR(rbrq) ((rbrq)->addr & (1<<5)) +#define RBRQ_LEN_ERR(rbrq) ((rbrq)->addr & (1<<4)) +#define RBRQ_END_PDU(rbrq) ((rbrq)->addr & (1<<3)) +#define RBRQ_AAL5_PROT(rbrq) ((rbrq)->addr & (1<<2)) +#define RBRQ_CON_CLOSED(rbrq) ((rbrq)->addr & (1<<1)) +#define RBRQ_HBUF_ERR(rbrq) ((rbrq)->addr & 1) +#define RBRQ_CID(rbrq) (((rbrq)->cidlen >> 16) & 0x1fff) +#define RBRQ_BUFLEN(rbrq) ((rbrq)->cidlen & 0xffff) + +/* figure 2.3 transmit packet descriptor ready queue */ + +struct he_tpdrq { + volatile u32 tpd; + volatile u32 cid; +}; + +#define TPDRQ_ALIGNMENT CONFIG_TPDRQ_SIZE + +/* table 2.30 host status page detail */ + +#define HSP_ALIGNMENT 0x400 /* must align on 1k boundary */ + +struct he_hsp { + struct he_hsp_entry { + volatile u32 tbrq_tail; + volatile u32 reserved1[15]; + volatile u32 rbrq_tail; + volatile u32 reserved2[15]; + } group[HE_NUM_GROUPS]; +}; + +/* figure 2.9 receive buffer pools */ + +struct he_rbp { + volatile u32 phys; + volatile u32 status; +}; + +/* NOTE: it is suggested that virt be the virtual address of the host + buffer. on a 64-bit machine, this would not work. Instead, we + store the real virtual address in another list, and store an index + (and buffer status) in the virt member. +*/ + +#define RBP_INDEX_OFF 6 +#define RBP_INDEX(x) (((long)(x) >> RBP_INDEX_OFF) & 0xffff) +#define RBP_LOANED 0x80000000 +#define RBP_SMALLBUF 0x40000000 + +struct he_virt { + void *virt; +}; + +#define RBPL_ALIGNMENT CONFIG_RBPL_SIZE +#define RBPS_ALIGNMENT CONFIG_RBPS_SIZE + +#ifdef notyet +struct he_group { + u32 rpbs_size, rpbs_qsize; + struct he_rbp rbps_ba; + + u32 rpbl_size, rpbl_qsize; + struct he_rpb_entry *rbpl_ba; +}; +#endif + +#define HE_LOOKUP_VCC(dev, cid) ((dev)->he_vcc_table[(cid)].vcc) + +struct he_vcc_table +{ + struct atm_vcc *vcc; +}; + +struct he_cs_stper +{ + long pcr; + int inuse; +}; + +#define HE_NUM_CS_STPER 16 + +struct he_dev { + unsigned int number; + unsigned int irq; + unsigned long membase; + + char prod_id[30]; + char mac_addr[6]; + int media; /* + * 0x26 = HE155 MM + * 0x27 = HE622 MM + * 0x46 = HE155 SM + * 0x47 = HE622 SM + */ + + + unsigned int vcibits, vpibits; + unsigned int cells_per_row; + unsigned int bytes_per_row; + unsigned int cells_per_lbuf; + unsigned int r0_numrows, r0_startrow, r0_numbuffs; + unsigned int r1_numrows, r1_startrow, r1_numbuffs; + unsigned int tx_numrows, tx_startrow, tx_numbuffs; + unsigned int buffer_limit; + + struct he_vcc_table *he_vcc_table; + +#ifdef notyet + struct he_group group[HE_NUM_GROUPS]; +#endif + struct he_cs_stper cs_stper[HE_NUM_CS_STPER]; + unsigned total_bw; + + dma_addr_t irq_phys; + struct he_irq *irq_base, *irq_head, *irq_tail; + volatile unsigned *irq_tailoffset; + int irq_peak; + +#ifdef USE_TASKLET + struct tasklet_struct tasklet; +#endif + + struct pci_pool *tpd_pool; + struct list_head outstanding_tpds; + dma_addr_t tpdrq_phys; + struct he_tpdrq *tpdrq_base, *tpdrq_tail, *tpdrq_head; + spinlock_t tpdrq_lock; /* serialize access to tpdrq */ + + spinlock_t global_lock; /* 8.1.5 pci transaction ordering + error problem */ + dma_addr_t rbrq_phys; + struct he_rbrq *rbrq_base, *rbrq_head; + int rbrq_peak; + + struct pci_pool *rbpl_pool; + dma_addr_t rbpl_phys; + struct he_rbp *rbpl_base, *rbpl_tail; + struct he_virt *rbpl_virt; + int rbpl_peak; + + struct pci_pool *rbps_pool; + dma_addr_t rbps_phys; + struct he_rbp *rbps_base, *rbps_tail; + struct he_virt *rbps_virt; + int rbps_peak; + + dma_addr_t tbrq_phys; + struct he_tbrq *tbrq_base, *tbrq_head; + int tbrq_peak; + + dma_addr_t hsp_phys; + struct he_hsp *hsp; + + struct pci_dev *pci_dev; + struct atm_dev *atm_dev; + struct he_dev *next; +}; + +struct he_vcc +{ + struct iovec iov_head[32]; + struct iovec *iov_tail; + int pdu_len; + + int rc_index; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + struct wait_queue *rx_waitq; + atruct wait_queue *tx_waitq; +#else + wait_queue_head_t rx_waitq; + wait_queue_head_t tx_waitq; +#endif +}; + +#define HE_VCC(vcc) ((struct he_vcc *)(vcc->dev_data)) + +#define PCI_VENDOR_ID_FORE 0x1127 +#define PCI_DEVICE_ID_FORE_HE 0x400 + +#define HE_DMA_MASK 0xffffffff + +#define GEN_CNTL_0 0x40 +#define INT_PROC_ENBL (1<<25) +#define SLAVE_ENDIAN_MODE (1<<16) +#define MRL_ENB (1<<5) +#define MRM_ENB (1<<4) +#define INIT_ENB (1<<2) +#define IGNORE_TIMEOUT (1<<1) +#define ENBL_64 (1<<0) + +#define MIN_PCI_LATENCY 32 /* errata 8.1.3 */ + +#define HE_DEV(dev) ((struct he_dev *) (dev)->dev_data) + +#define he_is622(dev) ((dev)->media & 0x1) + +#define HE_REGMAP_SIZE 0x100000 + +#define RESET_CNTL 0x80000 +#define BOARD_RST_STATUS (1<<6) + +#define HOST_CNTL 0x80004 +#define PCI_BUS_SIZE64 (1<<27) +#define DESC_RD_STATIC_64 (1<<26) +#define DATA_RD_STATIC_64 (1<<25) +#define DATA_WR_STATIC_64 (1<<24) +#define ID_CS (1<<12) +#define ID_WREN (1<<11) +#define ID_DOUT (1<<10) +#define ID_DOFFSET 10 +#define ID_DIN (1<<9) +#define ID_CLOCK (1<<8) +#define QUICK_RD_RETRY (1<<7) +#define QUICK_WR_RETRY (1<<6) +#define OUTFF_ENB (1<<5) +#define CMDFF_ENB (1<<4) +#define PERR_INT_ENB (1<<2) +#define IGNORE_INTR (1<<0) + +#define LB_SWAP 0x80008 +#define SWAP_RNUM_MAX(x) (x<<27) +#define DATA_WR_SWAP (1<<20) +#define DESC_RD_SWAP (1<<19) +#define DATA_RD_SWAP (1<<18) +#define INTR_SWAP (1<<17) +#define DESC_WR_SWAP (1<<16) +#define SDRAM_INIT (1<<15) +#define BIG_ENDIAN_HOST (1<<14) +#define XFER_SIZE (1<<7) + +#define LB_MEM_ADDR 0x8000c +#define LB_MEM_DATA 0x80010 + +#define LB_MEM_ACCESS 0x80014 +#define LB_MEM_HNDSHK (1<<30) +#define LM_MEM_WRITE (0x7) +#define LM_MEM_READ (0x3) + +#define SDRAM_CTL 0x80018 +#define LB_64_ENB (1<<3) +#define LB_TWR (1<<2) +#define LB_TRP (1<<1) +#define LB_TRAS (1<<0) + +#define INT_FIFO 0x8001c +#define INT_MASK_D (1<<15) +#define INT_MASK_C (1<<14) +#define INT_MASK_B (1<<13) +#define INT_MASK_A (1<<12) +#define INT_CLEAR_D (1<<11) +#define INT_CLEAR_C (1<<10) +#define INT_CLEAR_B (1<<9) +#define INT_CLEAR_A (1<<8) + +#define ABORT_ADDR 0x80020 + +#define IRQ0_BASE 0x80080 +#define IRQ_BASE(x) (x<<12) +#define IRQ_MASK ((CONFIG_IRQ_SIZE<<2)-1) /* was 0x3ff */ +#define IRQ_TAIL(x) (((unsigned long)(x)) & IRQ_MASK) +#define IRQ0_HEAD 0x80084 +#define IRQ_SIZE(x) (x<<22) +#define IRQ_THRESH(x) (x<<12) +#define IRQ_HEAD(x) (x<<2) +/* #define IRQ_PENDING (1) conflict with linux/irq.h */ +#define IRQ0_CNTL 0x80088 +#define IRQ_ADDRSEL(x) (x<<2) +#define IRQ_INT_A (0<<2) +#define IRQ_INT_B (1<<2) +#define IRQ_INT_C (2<<2) +#define IRQ_INT_D (3<<2) +#define IRQ_TYPE_ADDR 0x1 +#define IRQ_TYPE_LINE 0x0 +#define IRQ0_DATA 0x8008c + +#define IRQ1_BASE 0x80090 +#define IRQ1_HEAD 0x80094 +#define IRQ1_CNTL 0x80098 +#define IRQ1_DATA 0x8009c + +#define IRQ2_BASE 0x800a0 +#define IRQ2_HEAD 0x800a4 +#define IRQ2_CNTL 0x800a8 +#define IRQ2_DATA 0x800ac + +#define IRQ3_BASE 0x800b0 +#define IRQ3_HEAD 0x800b4 +#define IRQ3_CNTL 0x800b8 +#define IRQ3_DATA 0x800bc + +#define GRP_10_MAP 0x800c0 +#define GRP_32_MAP 0x800c4 +#define GRP_54_MAP 0x800c8 +#define GRP_76_MAP 0x800cc + +#define G0_RBPS_S 0x80400 +#define G0_RBPS_T 0x80404 +#define RBP_TAIL(x) ((x)<<3) +#define RBP_MASK(x) ((x)|0x1fff) +#define G0_RBPS_QI 0x80408 +#define RBP_QSIZE(x) ((x)<<14) +#define RBP_INT_ENB (1<<13) +#define RBP_THRESH(x) (x) +#define G0_RBPS_BS 0x8040c +#define G0_RBPL_S 0x80410 +#define G0_RBPL_T 0x80414 +#define G0_RBPL_QI 0x80418 +#define G0_RBPL_BS 0x8041c + +#define G1_RBPS_S 0x80420 +#define G1_RBPS_T 0x80424 +#define G1_RBPS_QI 0x80428 +#define G1_RBPS_BS 0x8042c +#define G1_RBPL_S 0x80430 +#define G1_RBPL_T 0x80434 +#define G1_RBPL_QI 0x80438 +#define G1_RBPL_BS 0x8043c + +#define G2_RBPS_S 0x80440 +#define G2_RBPS_T 0x80444 +#define G2_RBPS_QI 0x80448 +#define G2_RBPS_BS 0x8044c +#define G2_RBPL_S 0x80450 +#define G2_RBPL_T 0x80454 +#define G2_RBPL_QI 0x80458 +#define G2_RBPL_BS 0x8045c + +#define G3_RBPS_S 0x80460 +#define G3_RBPS_T 0x80464 +#define G3_RBPS_QI 0x80468 +#define G3_RBPS_BS 0x8046c +#define G3_RBPL_S 0x80470 +#define G3_RBPL_T 0x80474 +#define G3_RBPL_QI 0x80478 +#define G3_RBPL_BS 0x8047c + +#define G4_RBPS_S 0x80480 +#define G4_RBPS_T 0x80484 +#define G4_RBPS_QI 0x80488 +#define G4_RBPS_BS 0x8048c +#define G4_RBPL_S 0x80490 +#define G4_RBPL_T 0x80494 +#define G4_RBPL_QI 0x80498 +#define G4_RBPL_BS 0x8049c + +#define G5_RBPS_S 0x804a0 +#define G5_RBPS_T 0x804a4 +#define G5_RBPS_QI 0x804a8 +#define G5_RBPS_BS 0x804ac +#define G5_RBPL_S 0x804b0 +#define G5_RBPL_T 0x804b4 +#define G5_RBPL_QI 0x804b8 +#define G5_RBPL_BS 0x804bc + +#define G6_RBPS_S 0x804c0 +#define G6_RBPS_T 0x804c4 +#define G6_RBPS_QI 0x804c8 +#define G6_RBPS_BS 0x804cc +#define G6_RBPL_S 0x804d0 +#define G6_RBPL_T 0x804d4 +#define G6_RBPL_QI 0x804d8 +#define G6_RBPL_BS 0x804dc + +#define G7_RBPS_S 0x804e0 +#define G7_RBPS_T 0x804e4 +#define G7_RBPS_QI 0x804e8 +#define G7_RBPS_BS 0x804ec + +#define G7_RBPL_S 0x804f0 +#define G7_RBPL_T 0x804f4 +#define G7_RBPL_QI 0x804f8 +#define G7_RBPL_BS 0x804fc + +#define G0_RBRQ_ST 0x80500 +#define G0_RBRQ_H 0x80504 +#define G0_RBRQ_Q 0x80508 +#define RBRQ_THRESH(x) ((x)<<13) +#define RBRQ_SIZE(x) (x) +#define G0_RBRQ_I 0x8050c +#define RBRQ_TIME(x) ((x)<<8) +#define RBRQ_COUNT(x) (x) + +/* fill in 1 ... 7 later */ + +#define G0_TBRQ_B_T 0x80600 +#define G0_TBRQ_H 0x80604 +#define G0_TBRQ_S 0x80608 +#define G0_TBRQ_THRESH 0x8060c +#define TBRQ_THRESH(x) (x) + +/* fill in 1 ... 7 later */ + +#define RH_CONFIG 0x805c0 +#define PHY_INT_ENB (1<<10) +#define OAM_GID(x) (x<<7) +#define PTMR_PRE(x) (x) + +#define G0_INMQ_S 0x80580 +#define G0_INMQ_L 0x80584 +#define G1_INMQ_S 0x80588 +#define G1_INMQ_L 0x8058c +#define G2_INMQ_S 0x80590 +#define G2_INMQ_L 0x80594 +#define G3_INMQ_S 0x80598 +#define G3_INMQ_L 0x8059c +#define G4_INMQ_S 0x805a0 +#define G4_INMQ_L 0x805a4 +#define G5_INMQ_S 0x805a8 +#define G5_INMQ_L 0x805ac +#define G6_INMQ_S 0x805b0 +#define G6_INMQ_L 0x805b4 +#define G7_INMQ_S 0x805b8 +#define G7_INMQ_L 0x805bc + +#define TPDRQ_B_H 0x80680 +#define TPDRQ_T 0x80684 +#define TPDRQ_S 0x80688 + +#define UBUFF_BA 0x8068c + +#define RLBF0_H 0x806c0 +#define RLBF0_T 0x806c4 +#define RLBF1_H 0x806c8 +#define RLBF1_T 0x806cc +#define RLBC_H 0x806d0 +#define RLBC_T 0x806d4 +#define RLBC_H2 0x806d8 +#define TLBF_H 0x806e0 +#define TLBF_T 0x806e4 +#define RLBF0_C 0x806e8 +#define RLBF1_C 0x806ec +#define RXTHRSH 0x806f0 +#define LITHRSH 0x806f4 + +#define LBARB 0x80700 +#define SLICE_X(x) (x<<28) +#define ARB_RNUM_MAX(x) (x<<23) +#define TH_PRTY(x) (x<<21) +#define RH_PRTY(x) (x<<19) +#define TL_PRTY(x) (x<<17) +#define RL_PRTY(x) (x<<15) +#define BUS_MULTI(x) (x<<8) +#define NET_PREF(x) (x) + +#define SDRAMCON 0x80704 +#define BANK_ON (1<<14) +#define WIDE_DATA (1<<13) +#define TWR_WAIT (1<<12) +#define TRP_WAIT (1<<11) +#define TRAS_WAIT (1<<10) +#define REF_RATE(x) (x) + +#define LBSTAT 0x80708 + +#define RCC_STAT 0x8070c +#define RCC_BUSY (1) + +#define TCMCONFIG 0x80740 +#define TM_DESL2 (1<<10) +#define TM_BANK_WAIT(x) (x<<6) +#define TM_ADD_BANK4(x) (x<<4) +#define TM_PAR_CHECK(x) (x<<3) +#define TM_RW_WAIT(x) (x<<2) +#define TM_SRAM_TYPE(x) (x) + +#define TSRB_BA 0x80744 +#define TSRC_BA 0x80748 +#define TMABR_BA 0x8074c +#define TPD_BA 0x80750 +#define TSRD_BA 0x80758 + +#define TX_CONFIG 0x80760 +#define DRF_THRESH(x) (x<<22) +#define TX_UT_MODE(x) (x<<21) +#define TX_VCI_MASK(x) (x<<17) +#define LBFREE_CNT(x) (x) + +#define TXAAL5_PROTO 0x80764 +#define CPCS_UU(x) (x<<8) +#define CPI(x) (x) + +#define RCMCONFIG 0x80780 +#define RM_DESL2(x) (x<<10) +#define RM_BANK_WAIT(x) (x<<6) +#define RM_ADD_BANK(x) (x<<4) +#define RM_PAR_CHECK(x) (x<<3) +#define RM_RW_WAIT(x) (x<<2) +#define RM_SRAM_TYPE(x) (x) + +#define RCMRSRB_BA 0x80784 +#define RCMLBM_BA 0x80788 +#define RCMABR_BA 0x8078c + +#define RC_CONFIG 0x807c0 +#define UT_RD_DELAY(x) (x<<11) +#define WRAP_MODE(x) (x<<10) +#define RC_UT_MODE(x) (x<<9) +#define RX_ENABLE (1<<8) +#define RX_VALVP(x) (x<<4) +#define RX_VALVC(x) (x) + +#define MCC 0x807c4 +#define OEC 0x807c8 +#define DCC 0x807cc +#define CEC 0x807d0 + +#define HSP_BA 0x807f0 + +#define LB_CONFIG 0x807f4 +#define LB_SIZE(x) (x) + +#define CON_DAT 0x807f8 +#define CON_CTL 0x807fc +#define CON_CTL_MBOX (2<<30) +#define CON_CTL_TCM (1<<30) +#define CON_CTL_RCM (0<<30) +#define CON_CTL_WRITE (1<<29) +#define CON_CTL_READ (0<<29) +#define CON_CTL_BUSY (1<<28) +#define CON_BYTE_DISABLE_3 (1<<22) /* 24..31 */ +#define CON_BYTE_DISABLE_2 (1<<21) /* 16..23 */ +#define CON_BYTE_DISABLE_1 (1<<20) /* 8..15 */ +#define CON_BYTE_DISABLE_0 (1<<19) /* 0..7 */ +#define CON_CTL_ADDR(x) (x) + +#define FRAMER 0x80800 /* to 0x80bfc */ + +/* 3.3 network controller (internal) mailbox registers */ + +#define CS_STPER0 0x0 + /* ... */ +#define CS_STPER31 0x01f + +#define CS_STTIM0 0x020 + /* ... */ +#define CS_STTIM31 0x03f + +#define CS_TGRLD0 0x040 + /* ... */ +#define CS_TGRLD15 0x04f + +#define CS_ERTHR0 0x050 +#define CS_ERTHR1 0x051 +#define CS_ERTHR2 0x052 +#define CS_ERTHR3 0x053 +#define CS_ERTHR4 0x054 +#define CS_ERCTL0 0x055 +#define TX_ENABLE (1<<28) +#define ER_ENABLE (1<<27) +#define CS_ERCTL1 0x056 +#define CS_ERCTL2 0x057 +#define CS_ERSTAT0 0x058 +#define CS_ERSTAT1 0x059 + +#define CS_RTCCT 0x060 +#define CS_RTFWC 0x061 +#define CS_RTFWR 0x062 +#define CS_RTFTC 0x063 +#define CS_RTATR 0x064 + +#define CS_TFBSET 0x070 +#define CS_TFBADD 0x071 +#define CS_TFBSUB 0x072 +#define CS_WCRMAX 0x073 +#define CS_WCRMIN 0x074 +#define CS_WCRINC 0x075 +#define CS_WCRDEC 0x076 +#define CS_WCRCEIL 0x077 +#define CS_BWDCNT 0x078 + +#define CS_OTPPER 0x080 +#define CS_OTWPER 0x081 +#define CS_OTTLIM 0x082 +#define CS_OTTCNT 0x083 + +#define CS_HGRRT0 0x090 + /* ... */ +#define CS_HGRRT7 0x097 + +#define CS_ORPTRS 0x0a0 + +#define RXCON_CLOSE 0x100 + + +#define RCM_MEM_SIZE 0x10000 /* 1M of 32-bit registers */ +#define TCM_MEM_SIZE 0x20000 /* 2M of 32-bit registers */ + +/* 2.5 transmit connection memory registers */ + +#define TSR0_CONN_STATE(x) ((x>>28) & 0x7) +#define TSR0_USE_WMIN (1<<23) +#define TSR0_GROUP(x) ((x & 0x7)<<18) +#define TSR0_ABR (2<<16) +#define TSR0_UBR (1<<16) +#define TSR0_CBR (0<<16) +#define TSR0_PROT (1<<15) +#define TSR0_AAL0_SDU (2<<12) +#define TSR0_AAL0 (1<<12) +#define TSR0_AAL5 (0<<12) +#define TSR0_HALT_ER (1<<11) +#define TSR0_MARK_CI (1<<10) +#define TSR0_MARK_ER (1<<9) +#define TSR0_UPDATE_GER (1<<8) +#define TSR0_RC_INDEX(x) (x & 0x1F) + +#define TSR1_PCR(x) ((x & 0x7FFF)<<16) +#define TSR1_MCR(x) (x & 0x7FFF) + +#define TSR2_ACR(x) ((x & 0x7FFF)<<16) + +#define TSR3_NRM_CNT(x) ((x & 0xFF)<<24) +#define TSR3_CRM_CNT(x) (x & 0xFFFF) + +#define TSR4_FLUSH_CONN (1<<31) +#define TSR4_SESSION_ENDED (1<<30) +#define TSR4_CRC10 (1<<28) +#define TSR4_NULL_CRC10 (1<<27) +#define TSR4_PROT (1<<26) +#define TSR4_AAL0_SDU (2<<23) +#define TSR4_AAL0 (1<<23) +#define TSR4_AAL5 (0<<23) + +#define TSR9_OPEN_CONN (1<<20) + +#define TSR11_ICR(x) ((x & 0x7FFF)<<16) +#define TSR11_TRM(x) ((x & 0x7)<<13) +#define TSR11_NRM(x) ((x & 0x7)<<10) +#define TSR11_ADTF(x) (x & 0x3FF) + +#define TSR13_RDF(x) ((x & 0xF)<<23) +#define TSR13_RIF(x) ((x & 0xF)<<19) +#define TSR13_CDF(x) ((x & 0x7)<<16) +#define TSR13_CRM(x) (x & 0xFFFF) + +#define TSR14_DELETE (1<<31) +#define TSR14_ABR_CLOSE (1<<16) + +/* 2.7.1 per connection receieve state registers */ + +#define RSR0_START_PDU (1<<10) +#define RSR0_OPEN_CONN (1<<6) +#define RSR0_CLOSE_CONN (0<<6) +#define RSR0_PPD_ENABLE (1<<5) +#define RSR0_EPD_ENABLE (1<<4) +#define RSR0_TCP_CKSUM (1<<3) +#define RSR0_AAL5 (0) +#define RSR0_AAL0 (1) +#define RSR0_AAL0_SDU (2) +#define RSR0_RAWCELL (3) +#define RSR0_RAWCELL_CRC10 (4) + +#define RSR1_AQI_ENABLE (1<<20) +#define RSR1_RBPL_ONLY (1<<19) +#define RSR1_GROUP(x) ((x)<<16) + +#define RSR4_AQI_ENABLE (1<<30) +#define RSR4_GROUP(x) ((x)<<27) +#define RSR4_RBPL_ONLY (1<<26) + +/* 2.1.4 transmit packet descriptor */ + +#define TPD_USERCELL 0x0 +#define TPD_SEGMENT_OAMF5 0x4 +#define TPD_END2END_OAMF5 0x5 +#define TPD_RMCELL 0x6 +#define TPD_CELLTYPE(x) (x<<3) +#define TPD_EOS (1<<2) +#define TPD_CLP (1<<1) +#define TPD_INT (1) +#define TPD_LST (1<<31) + +/* table 4.3 serial eeprom information */ + +#define PROD_ID 0x08 /* char[] */ +#define PROD_ID_LEN 30 +#define HW_REV 0x26 /* char[] */ +#define M_SN 0x3a /* integer */ +#define MEDIA 0x3e /* integer */ +#define HE155MM 0x26 +#define HE155SM 0x27 +#define HE622MM 0x46 +#define HE622SM 0x47 +#define MAC_ADDR 0x42 /* char[] */ + +#define CS_LOW 0x0 +#define CS_HIGH ID_CS /* HOST_CNTL_ID_PROM_SEL */ +#define CLK_LOW 0x0 +#define CLK_HIGH ID_CLOCK /* HOST_CNTL_ID_PROM_CLOCK */ +#define SI_HIGH ID_DIN /* HOST_CNTL_ID_PROM_DATA_IN */ +#define EEPROM_DELAY 400 /* microseconds */ + +/* Read from EEPROM = 0000 0011b */ +unsigned int readtab[] = { + CS_HIGH | CLK_HIGH, + CS_LOW | CLK_LOW, + CLK_HIGH, /* 0 */ + CLK_LOW, + CLK_HIGH, /* 0 */ + CLK_LOW, + CLK_HIGH, /* 0 */ + CLK_LOW, + CLK_HIGH, /* 0 */ + CLK_LOW, + CLK_HIGH, /* 0 */ + CLK_LOW, + CLK_HIGH, /* 0 */ + CLK_LOW | SI_HIGH, + CLK_HIGH | SI_HIGH, /* 1 */ + CLK_LOW | SI_HIGH, + CLK_HIGH | SI_HIGH /* 1 */ +}; + +/* Clock to read from/write to the EEPROM */ +unsigned int clocktab[] = { + CLK_LOW, + CLK_HIGH, + CLK_LOW, + CLK_HIGH, + CLK_LOW, + CLK_HIGH, + CLK_LOW, + CLK_HIGH, + CLK_LOW, + CLK_HIGH, + CLK_LOW, + CLK_HIGH, + CLK_LOW, + CLK_HIGH, + CLK_LOW, + CLK_HIGH, + CLK_LOW +}; + + +#endif /* _HE_H_ */ diff -urN linux-2.4.20/drivers/atm/horizon.c linux-2.4.20-atm/drivers/atm/horizon.c --- linux-2.4.20/drivers/atm/horizon.c Sat Aug 3 02:39:43 2002 +++ linux-2.4.20-atm/drivers/atm/horizon.c Wed May 28 01:58:41 2003 @@ -1765,13 +1765,13 @@ { unsigned int tx_len = skb->len; - unsigned int tx_iovcnt = ATM_SKB(skb)->iovcnt; + unsigned int tx_nr_frags = skb_shinfo(skb)->nr_frags; // remember this so we can free it later dev->tx_skb = skb; - if (tx_iovcnt) { + if (tx_nr_frags) { // scatter gather transfer - dev->tx_regions = tx_iovcnt; + dev->tx_regions = tx_nr_frags; dev->tx_iovec = (struct iovec *) skb->data; dev->tx_bytes = 0; PRINTD (DBG_TX|DBG_BUS, "TX start scatter-gather transfer (iovec %p, len %d)", @@ -2189,14 +2189,6 @@ } #endif - // deal with possibly wildcarded VCs - error = atm_find_ci (atm_vcc, &vpi, &vci); - if (error) { - PRINTD (DBG_WARN|DBG_VCC, "atm_find_ci failed!"); - return error; - } - PRINTD (DBG_VCC, "atm_find_ci gives %x %x", vpi, vci); - error = vpivci_to_channel (&channel, vpi, vci); if (error) { PRINTD (DBG_WARN|DBG_VCC, "VPI/VCI out of range: %hd/%d", vpi, vci); diff -urN linux-2.4.20/drivers/atm/idt77252.c linux-2.4.20-atm/drivers/atm/idt77252.c --- linux-2.4.20/drivers/atm/idt77252.c Wed May 28 01:53:45 2003 +++ linux-2.4.20-atm/drivers/atm/idt77252.c Wed May 28 01:58:41 2003 @@ -1054,15 +1054,17 @@ return; } + read_lock(&vcc_sklist_lock); vc = card->vcs[VPCI2VC(card, vpi, vci)]; - if (!vc || !test_bit(VCF_RX, &vc->flags)) { + if (!vc || !test_bit(VCF_RX, &vc->flags) || !(vcc = vc->rx_vcc)) { + read_unlock(&vcc_sklist_lock); printk("%s: SDU received on non RX vc %u.%u\n", card->name, vpi, vci); recycle_rx_skb(card, skb); return; } - - vcc = vc->rx_vcc; + vcc_hold(vcc); + read_unlock(&vcc_sklist_lock); pci_dma_sync_single(card->pcidev, IDT77252_PRV_PADDR(skb), skb->end - skb->data, PCI_DMA_FROMDEVICE); @@ -1106,6 +1108,7 @@ cell += ATM_CELL_PAYLOAD; } + vcc_put(vcc); recycle_rx_skb(card, skb); return; } @@ -1113,6 +1116,7 @@ printk("%s: Unexpected AAL type in dequeue_rx(): %d.\n", card->name, vcc->qos.aal); recycle_rx_skb(card, skb); + vcc_put(vcc); return; } skb->len = (stat & SAR_RSQE_CELLCNT) * ATM_CELL_PAYLOAD; @@ -1142,12 +1146,14 @@ card->name, len, rpp->len, readl(SAR_REG_CDC)); recycle_rx_pool_skb(card, rpp); atomic_inc(&vcc->stats->rx_err); + vcc_put(vcc); return; } if (stat & SAR_RSQE_CRC) { RXPRINTK("%s: AAL5 CRC error.\n", card->name); recycle_rx_pool_skb(card, rpp); atomic_inc(&vcc->stats->rx_err); + vcc_put(vcc); return; } if (rpp->count > 1) { @@ -1159,9 +1165,11 @@ card->name); recycle_rx_pool_skb(card, rpp); atomic_inc(&vcc->stats->rx_err); + vcc_put(vcc); return; } if (!atm_charge(vcc, skb->truesize)) { + vcc_put(vcc); recycle_rx_pool_skb(card, rpp); dev_kfree_skb(skb); return; @@ -1181,6 +1189,7 @@ vcc->push(vcc, skb); atomic_inc(&vcc->stats->rx); + vcc_put(vcc); return; } @@ -1189,6 +1198,7 @@ flush_rx_pool(card, rpp); if (!atm_charge(vcc, skb->truesize)) { + vcc_put(vcc); recycle_rx_skb(card, skb); return; } @@ -1212,8 +1222,10 @@ add_rx_skb(card, 1, SAR_FB_SIZE_1, 1); else add_rx_skb(card, 0, SAR_FB_SIZE_0, 1); + vcc_put(vcc); return; } + vcc_put(vcc); } static void @@ -1988,7 +2000,7 @@ return -EINVAL; } - if (ATM_SKB(skb)->iovcnt != 0) { + if (skb_shinfo(skb)->nr_frags != 0) { printk("%s: No scatter-gather yet.\n", card->name); atomic_inc(&vcc->stats->tx_err); dev_kfree_skb(skb); @@ -2025,8 +2037,7 @@ atomic_inc(&vcc->stats->tx_err); return -ENOMEM; } - atomic_add(skb->truesize + ATM_PDU_OVHD, &vcc->sk->wmem_alloc); - ATM_SKB(skb)->iovcnt = 0; + atomic_add(skb->truesize, &vcc->sk->wmem_alloc); memcpy(skb_put(skb, 52), cell, 52); @@ -2349,6 +2360,7 @@ if (test_bit(VCF_RX, &vc->flags)) return -EBUSY; + vcc_hold(vcc); vc->rx_vcc = vcc; set_bit(VCF_RX, &vc->flags); @@ -2403,40 +2415,6 @@ } static int -idt77252_find_vcc(struct atm_vcc *vcc, short *vpi, int *vci) -{ - struct atm_vcc *walk; - - if (*vpi == ATM_VPI_ANY) { - *vpi = 0; - walk = vcc->dev->vccs; - while (walk) { - if ((walk->vci == *vci) && (walk->vpi == *vpi)) { - (*vpi)++; - walk = vcc->dev->vccs; - continue; - } - walk = walk->next; - } - } - - if (*vci == ATM_VCI_ANY) { - *vci = ATM_NOT_RSV_VCI; - walk = vcc->dev->vccs; - while (walk) { - if ((walk->vci == *vci) && (walk->vpi == *vpi)) { - (*vci)++; - walk = vcc->dev->vccs; - continue; - } - walk = walk->next; - } - } - - return 0; -} - -static int idt77252_open(struct atm_vcc *vcc, short vpi, int vci) { struct atm_dev *dev = vcc->dev; @@ -2446,8 +2424,6 @@ unsigned int inuse; int error; - idt77252_find_vcc(vcc, &vpi, &vci); - if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) return 0; @@ -2565,6 +2541,8 @@ spin_lock_irqsave(&vc->lock, flags); clear_bit(VCF_RX, &vc->flags); vc->rx_vcc = NULL; + barrier(); + vcc_put(vcc); spin_unlock_irqrestore(&vc->lock, flags); if ((vcc->vci == 3) || (vcc->vci == 4)) diff -urN linux-2.4.20/drivers/atm/iphase.c linux-2.4.20-atm/drivers/atm/iphase.c --- linux-2.4.20/drivers/atm/iphase.c Wed May 28 01:53:45 2003 +++ linux-2.4.20-atm/drivers/atm/iphase.c Wed May 28 01:58:41 2003 @@ -1122,13 +1122,17 @@ IF_ERR(printk("IA: bad descriptor desc = %d \n", desc);) return -1; } + read_lock(&vcc_sklist_lock); vcc = iadev->rx_open[buf_desc_ptr->vc_index & 0xffff]; if (!vcc) { + read_unlock(&vcc_sklist_lock); free_desc(dev, desc); printk("IA: null vcc, drop PDU\n"); return -1; } + vcc_hold(vcc); + read_unlock(&vcc_sklist_lock); /* might want to check the status bits for errors */ @@ -1185,8 +1189,6 @@ } skb_put(skb,len); // pwang_test - ATM_SKB(skb)->vcc = vcc; - ATM_SKB(skb)->iovcnt = 0; ATM_DESC(skb) = desc; skb_queue_tail(&iadev->rx_dma_q, skb); @@ -1205,7 +1207,8 @@ udelay(1); /* Increment transaction counter */ writel(1, iadev->dma+IPHASE5575_RX_COUNTER); -out: return 0; +out: vcc_put(vcc); + return 0; out_free_desc: free_desc(dev, desc); goto out; @@ -1283,6 +1286,7 @@ struct dle *dle, *cur_dle; u_int dle_lp; int len; + struct rx_buf_desc *buf_desc_ptr; iadev = INPH_IA_DEV(dev); /* free all the dles done, that is just update our own dle read pointer @@ -1293,6 +1297,7 @@ dle = iadev->rx_dle_q.read; dle_lp = readl(iadev->dma+IPHASE5575_RX_LIST_ADDR) & (sizeof(struct dle)*DLE_ENTRIES - 1); cur_dle = (struct dle*)(iadev->rx_dle_q.start + (dle_lp >> 4)); + read_lock(&vcc_sklist_lock); while(dle != cur_dle) { /* free the DMAed skb */ @@ -1300,6 +1305,9 @@ if (!skb) goto INCR_DLE; desc = ATM_DESC(skb); + buf_desc_ptr = (struct rx_buf_desc *)iadev->RX_DESC_BASE_ADDR; + buf_desc_ptr += desc; + vcc = iadev->rx_open[buf_desc_ptr->vc_index & 0xffff]; free_desc(dev, desc); if (!(len = skb->len)) @@ -1316,7 +1324,6 @@ pci_unmap_single(iadev->pci, iadev->rx_dle_q.write->sys_pkt_addr, len, PCI_DMA_FROMDEVICE); /* no VCC related housekeeping done as yet. lets see */ - vcc = ATM_SKB(skb)->vcc; if (!vcc) { printk("IA: null vcc\n"); dev_kfree_skb_any(skb); @@ -1360,6 +1367,7 @@ printk("\n");) IF_RX(printk("rx_dle_intr: skb push");) + ATM_SKB(skb)->vcc = vcc; vcc->push(vcc,skb); atomic_inc(&vcc->stats->rx); iadev->rx_pkt_cnt++; @@ -1368,6 +1376,7 @@ if (++dle == iadev->rx_dle_q.end) dle = iadev->rx_dle_q.start; } + read_unlock(&vcc_sklist_lock); iadev->rx_dle_q.read = dle; /* if the interrupts are masked because there were no free desc available, @@ -1425,6 +1434,7 @@ if (iadev->rx_open[vcc->vci]) printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %d already open\n", vcc->dev->number, vcc->vci); + vcc_hold(vcc); iadev->rx_open[vcc->vci] = vcc; return 0; } @@ -1725,15 +1735,14 @@ printk("tx_dle_intr: vcc is null\n"); spin_unlock_irqrestore(&iadev->tx_lock, flags); dev_kfree_skb_any(skb); - - return; + goto cont_loop; } iavcc = INPH_IA_VCC(vcc); if (!iavcc) { printk("tx_dle_intr: iavcc is null\n"); spin_unlock_irqrestore(&iadev->tx_lock, flags); dev_kfree_skb_any(skb); - return; + goto cont_loop; } if (vcc->qos.txtp.pcr >= iadev->rate_limit) { if ((vcc->pop) && (skb->len != 0)) @@ -1749,6 +1758,7 @@ skb_queue_tail(&iavcc->txing_skb, skb); } IF_EVENT(printk("tx_dle_intr: enque skb = 0x%x \n", (u32)skb);) +cont_loop: if (++dle == iadev->tx_dle_q.end) dle = iadev->tx_dle_q.start; } @@ -2705,6 +2715,8 @@ // Drain the packets rx_dle_intr(vcc->dev); iadev->rx_open[vcc->vci] = 0; + barrier(); + vcc_put(vcc); } kfree(INPH_IA_VCC(vcc)); ia_vcc = NULL; @@ -2724,12 +2736,6 @@ INPH_IA_VCC(vcc) = NULL; } iadev = INPH_IA_DEV(vcc->dev); - error = atm_find_ci(vcc, &vpi, &vci); - if (error) - { - printk("iadev: atm_find_ci returned error %d\n", error); - return error; - } vcc->vpi = vpi; vcc->vci = vci; if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC) diff -urN linux-2.4.20/drivers/atm/lanai.c linux-2.4.20-atm/drivers/atm/lanai.c --- linux-2.4.20/drivers/atm/lanai.c Fri Nov 29 00:53:12 2002 +++ linux-2.4.20-atm/drivers/atm/lanai.c Wed May 28 01:58:41 2003 @@ -1820,10 +1820,10 @@ { vci_t vci = SERVICE_GET_VCI(s); struct lanai_vcc *lvcc; - vcclist_read_lock(); + read_lock(&vcc_sklist_lock); lvcc = lanai->vccs[vci]; if (lvcc == NULL) { - vcclist_read_unlock(); + read_unlock(&vcc_sklist_lock); DPRINTK("(itf %d) got service entry 0x%X for nonexistent " "vcc %d\n", lanai->number, s, vci); if (s & SERVICE_TX) @@ -1834,7 +1834,7 @@ } if (s & SERVICE_TX) { /* segmentation interrupt */ if (lvcc->tx.atmvcc == NULL) { - vcclist_read_unlock(); + read_unlock(&vcc_sklist_lock); DPRINTK("(itf %d) got service entry 0x%X for non-TX " "vcc %d\n", lanai->number, s, vci); lanai->stats.service_notx++; @@ -1842,18 +1842,18 @@ } vci_bitfield_set(&lanai->transmit_ready, vci); lvcc->tx.endptr = SERVICE_GET_END(s); - vcclist_read_unlock(); + read_unlock(&vcc_sklist_lock); return 1; } if (lvcc->rx.atmvcc == NULL) { - vcclist_read_unlock(); + read_unlock(&vcc_sklist_lock); DPRINTK("(itf %d) got service entry 0x%X for non-RX " "vcc %d\n", lanai->number, s, vci); lanai->stats.service_norx++; return 0; } if (lvcc->rx.atmvcc->qos.aal != ATM_AAL5) { - vcclist_read_unlock(); + read_unlock(&vcc_sklist_lock); DPRINTK("(itf %d) got RX service entry 0x%X for non-AAL5 " "vcc %d\n", lanai->number, s, vci); lanai->stats.service_rxnotaal5++; @@ -1862,12 +1862,12 @@ } if ((s & (SERVICE_TRASH | SERVICE_STREAM | SERVICE_CRCERR)) == 0) { vcc_rx_aal5(lvcc, SERVICE_GET_END(s)); - vcclist_read_unlock(); + read_unlock(&vcc_sklist_lock); return 0; } if (s & SERVICE_TRASH) { int bytes; - vcclist_read_unlock(); + read_unlock(&vcc_sklist_lock); DPRINTK("got trashed rx pdu on vci %d\n", vci); atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); lvcc->stats.x.aal5.service_trash++; @@ -1880,7 +1880,7 @@ return 0; } if (s & SERVICE_STREAM) { - vcclist_read_unlock(); + read_unlock(&vcc_sklist_lock); atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); lvcc->stats.x.aal5.service_stream++; printk(KERN_ERR DEV_LABEL "(itf %d): Got AAL5 stream " @@ -1893,7 +1893,7 @@ lvcc->stats.x.aal5.service_rxcrc++; lvcc->rx.buf.ptr = &lvcc->rx.buf.start[SERVICE_GET_END(s) * 4]; cardvcc_write(lvcc, SERVICE_GET_END(s), vcc_rxreadptr); - vcclist_read_unlock(); + read_unlock(&vcc_sklist_lock); return 0; } @@ -1924,11 +1924,11 @@ reg_write(lanai, wreg, ServRead_Reg); if (ntx != 0) { spin_lock(&lanai->txlock); - vcclist_read_lock(); + read_lock(&vcc_sklist_lock); vci_bitfield_iterate(lanai, &lanai->transmit_ready, iter_transmit); vci_bitfield_init(&lanai->transmit_ready); - vcclist_read_unlock(); + read_unlock(&vcc_sklist_lock); spin_unlock(&lanai->txlock); } } @@ -2785,7 +2785,7 @@ return sprintf(page, "resets: dma=%d, card=%d\n", lanai->stats.dma_reenable, lanai->stats.card_reset); /* At this point, "left" should be the VCI we're looking for */ - vcclist_read_lock(); + read_lock(&vcc_sklist_lock); for (; ; left++) { if (left >= NUM_VCI) { left = 0; @@ -2821,7 +2821,7 @@ page[left++] = '\n'; page[left] = '\0'; out: - vcclist_read_unlock(); + read_unlock(&vcc_sklist_lock); return left; } #endif /* CONFIG_PROC_FS */ @@ -2842,7 +2842,6 @@ phy_get: NULL, feedback: NULL, change_qos: lanai_change_qos, - free_rx_skb: NULL, proc_read: lanai_proc_read }; diff -urN linux-2.4.20/drivers/atm/nicstar.c linux-2.4.20-atm/drivers/atm/nicstar.c --- linux-2.4.20/drivers/atm/nicstar.c Wed May 28 01:53:45 2003 +++ linux-2.4.20-atm/drivers/atm/nicstar.c Wed May 28 01:58:41 2003 @@ -941,9 +941,15 @@ return error; } - if (ns_parse_mac(mac[i], card->atmdev->esi)) + if (ns_parse_mac(mac[i], card->atmdev->esi)) { nicstar_read_eprom(card->membase, NICSTAR_EPROM_MAC_ADDR_OFFSET, card->atmdev->esi, 6); + if (!memcmp(card->atmdev->esi, "\x00\x00\x00\x00\x00\x00", 6)) { + nicstar_read_eprom(card->membase, NICSTAR_EPROM_MAC_ADDR_OFFSET_ALT, + 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], @@ -1433,7 +1439,6 @@ { ns_dev *card; vc_map *vc; - int error; unsigned long tmpl, modl; int tcr, tcra; /* target cell rate, and absolute value */ int n = 0; /* Number of entries in the TST. Initialized to remove @@ -1453,11 +1458,6 @@ 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; @@ -1597,6 +1597,7 @@ u32 status; vc->rx = 1; + vcc_hold(vcc); vc->rx_vcc = vcc; vc->rx_iov = NULL; @@ -1646,6 +1647,9 @@ spin_unlock_irqrestore(&card->res_lock, flags); vc->rx = 0; + vc->rx_vcc = NULL; + barrier(); + vcc_put(vcc); if (vc->rx_iov != NULL) { struct sk_buff *iovb; @@ -1659,9 +1663,9 @@ 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; + NS_SKB(iovb)->iovcnt); + NS_SKB(iovb)->iovcnt = 0; + NS_SKB(iovb)->vcc = NULL; ns_grab_int_lock(card, flags); recycle_iov_buf(card, iovb); spin_unlock_irqrestore(&card->int_lock, flags); @@ -1859,7 +1863,7 @@ return -EINVAL; } - if (ATM_SKB(skb)->iovcnt != 0) + if (skb_shinfo(skb)->nr_frags != 0) { printk("nicstar%d: No scatter-gather yet.\n", card->index); atomic_inc(&vcc->stats->tx_err); @@ -2203,16 +2207,18 @@ return; } + read_lock(&vcc_sklist_lock); vc = &(card->vcmap[vpi << card->vcibits | vci]); - if (!vc->rx) + if (!vc->rx || !(vcc = vc->rx_vcc)) { + read_unlock(&vcc_sklist_lock); RXPRINTK("nicstar%d: SDU received on non-rx vc %d.%d.\n", card->index, vpi, vci); recycle_rx_buf(card, skb); return; } - - vcc = vc->rx_vcc; + vcc_hold(vcc); + read_unlock(&vcc_sklist_lock); if (vcc->qos.aal == ATM_AAL0) { @@ -2252,7 +2258,7 @@ atomic_inc(&vcc->stats->rx); cell += ATM_CELL_PAYLOAD; } - + vcc_put(vcc); recycle_rx_buf(card, skb); return; } @@ -2269,6 +2275,7 @@ { printk("nicstar%d: Out of iovec buffers.\n", card->index); atomic_inc(&vcc->stats->rx_drop); + vcc_put(vcc); recycle_rx_buf(card, skb); return; } @@ -2284,30 +2291,30 @@ } } vc->rx_iov = iovb; - ATM_SKB(iovb)->iovcnt = 0; + NS_SKB(iovb)->iovcnt = 0; iovb->len = 0; iovb->tail = iovb->data = iovb->head; - ATM_SKB(iovb)->vcc = vcc; + NS_SKB(iovb)->vcc = vcc; /* IMPORTANT: a pointer to the sk_buff containing the small or large buffer is stored as iovec base, NOT a pointer to the small or large buffer itself. */ } - else if (ATM_SKB(iovb)->iovcnt >= NS_MAX_IOVECS) + else if (NS_SKB(iovb)->iovcnt >= NS_MAX_IOVECS) { printk("nicstar%d: received too big AAL5 SDU.\n", card->index); atomic_inc(&vcc->stats->rx_err); recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data, NS_MAX_IOVECS); - ATM_SKB(iovb)->iovcnt = 0; + NS_SKB(iovb)->iovcnt = 0; iovb->len = 0; iovb->tail = iovb->data = iovb->head; - ATM_SKB(iovb)->vcc = vcc; + NS_SKB(iovb)->vcc = vcc; } - iov = &((struct iovec *) iovb->data)[ATM_SKB(iovb)->iovcnt++]; + iov = &((struct iovec *) iovb->data)[NS_SKB(iovb)->iovcnt++]; iov->iov_base = (void *) skb; iov->iov_len = ns_rsqe_cellcount(rsqe) * 48; iovb->len += iov->iov_len; - if (ATM_SKB(iovb)->iovcnt == 1) + if (NS_SKB(iovb)->iovcnt == 1) { if (skb->list != &card->sbpool.queue) { @@ -2317,11 +2324,12 @@ atomic_inc(&vcc->stats->rx_err); recycle_rx_buf(card, skb); vc->rx_iov = NULL; + vcc_put(vcc); recycle_iov_buf(card, iovb); return; } } - else /* ATM_SKB(iovb)->iovcnt >= 2 */ + else /* NS_SKB(iovb)->iovcnt >= 2 */ { if (skb->list != &card->lbpool.queue) { @@ -2330,8 +2338,9 @@ which_list(card, skb); atomic_inc(&vcc->stats->rx_err); recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data, - ATM_SKB(iovb)->iovcnt); + NS_SKB(iovb)->iovcnt); vc->rx_iov = NULL; + vcc_put(vcc); recycle_iov_buf(card, iovb); return; } @@ -2354,15 +2363,16 @@ printk(".\n"); atomic_inc(&vcc->stats->rx_err); recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data, - ATM_SKB(iovb)->iovcnt); + NS_SKB(iovb)->iovcnt); vc->rx_iov = NULL; + vcc_put(vcc); recycle_iov_buf(card, iovb); return; } /* By this point we (hopefully) have a complete SDU without errors. */ - if (ATM_SKB(iovb)->iovcnt == 1) /* Just a small buffer */ + if (NS_SKB(iovb)->iovcnt == 1) /* Just a small buffer */ { /* skb points to a small buffer */ if (!atm_charge(vcc, skb->truesize)) @@ -2384,7 +2394,7 @@ atomic_inc(&vcc->stats->rx); } } - else if (ATM_SKB(iovb)->iovcnt == 2) /* One small plus one large buffer */ + else if (NS_SKB(iovb)->iovcnt == 2) /* One small plus one large buffer */ { struct sk_buff *sb; @@ -2461,8 +2471,9 @@ printk("nicstar%d: Out of huge buffers.\n", card->index); atomic_inc(&vcc->stats->rx_drop); recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data, - ATM_SKB(iovb)->iovcnt); + NS_SKB(iovb)->iovcnt); vc->rx_iov = NULL; + vcc_put(vcc); recycle_iov_buf(card, iovb); return; } @@ -2499,7 +2510,7 @@ if (!atm_charge(vcc, hb->truesize)) { - recycle_iovec_rx_bufs(card, iov, ATM_SKB(iovb)->iovcnt); + recycle_iovec_rx_bufs(card, iov, NS_SKB(iovb)->iovcnt); if (card->hbpool.count < card->hbnr.max) { skb_queue_tail(&card->hbpool.queue, hb); @@ -2522,7 +2533,7 @@ 0, 0); /* Copy all large buffers to the huge buffer and free them */ - for (j = 1; j < ATM_SKB(iovb)->iovcnt; j++) + for (j = 1; j < NS_SKB(iovb)->iovcnt; j++) { lb = (struct sk_buff *) iov->iov_base; tocopy = MIN(remaining, iov->iov_len); @@ -2550,7 +2561,7 @@ vc->rx_iov = NULL; recycle_iov_buf(card, iovb); } - + vcc_put(vcc); } diff -urN linux-2.4.20/drivers/atm/nicstar.h linux-2.4.20-atm/drivers/atm/nicstar.h --- linux-2.4.20/drivers/atm/nicstar.h Thu Oct 25 22:53:46 2001 +++ linux-2.4.20-atm/drivers/atm/nicstar.h Wed May 28 01:58:41 2003 @@ -96,6 +96,7 @@ /* ESI stuff ******************************************************************/ #define NICSTAR_EPROM_MAC_ADDR_OFFSET 0x6C +#define NICSTAR_EPROM_MAC_ADDR_OFFSET_ALT 0xF6 /* #defines *******************************************************************/ @@ -748,6 +749,14 @@ int tbd_count; } vc_map; + +struct ns_skb_data +{ + struct atm_vcc *vcc; + int iovcnt; +}; + +#define NS_SKB(skb) (((struct ns_skb_data *) (skb)->cb)) typedef struct ns_dev { diff -urN linux-2.4.20/drivers/atm/zatm.c linux-2.4.20-atm/drivers/atm/zatm.c --- linux-2.4.20/drivers/atm/zatm.c Fri Sep 14 23:40:00 2001 +++ linux-2.4.20-atm/drivers/atm/zatm.c Wed May 28 01:58:41 2003 @@ -827,10 +827,10 @@ vcc = ATM_SKB(skb)->vcc; zatm_dev = ZATM_DEV(vcc->dev); zatm_vcc = ZATM_VCC(vcc); - EVENT("iovcnt=%d\n",ATM_SKB(skb)->iovcnt,0); + EVENT("nr_frags=%d\n",skb_shinfo(skb)->nr_frags,0); save_flags(flags); cli(); - if (!ATM_SKB(skb)->iovcnt) { + if (!skb_shinfo(skb)->nr_frags) { if (zatm_vcc->txing == RING_ENTRIES-1) { restore_flags(flags); return RING_BUSY; @@ -1579,8 +1579,6 @@ DPRINTK(">zatm_open\n"); zatm_dev = ZATM_DEV(vcc->dev); if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) ZATM_VCC(vcc) = NULL; - error = atm_find_ci(vcc,&vpi,&vci); - if (error) return error; vcc->vpi = vpi; vcc->vci = vci; if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC) diff -urN linux-2.4.20/include/linux/atm_he.h linux-2.4.20-atm/include/linux/atm_he.h --- linux-2.4.20/include/linux/atm_he.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.20-atm/include/linux/atm_he.h Wed May 28 01:58:41 2003 @@ -0,0 +1,20 @@ +/* atm_he.h */ + +#ifndef LINUX_ATM_HE_H +#define LINUX_ATM_HE_H + +#include + +#define HE_GET_REG _IOW('a', ATMIOC_SARPRV, struct atmif_sioc) + +#define HE_REGTYPE_PCI 1 +#define HE_REGTYPE_RCM 2 +#define HE_REGTYPE_TCM 3 +#define HE_REGTYPE_MBOX 4 + +struct he_ioctl_reg { + unsigned addr, val; + char type; +}; + +#endif /* LINUX_ATM_HE_H */ diff -urN linux-2.4.20/include/linux/atm_tcp.h linux-2.4.20-atm/include/linux/atm_tcp.h --- linux-2.4.20/include/linux/atm_tcp.h Fri Dec 29 23:35:47 2000 +++ linux-2.4.20-atm/include/linux/atm_tcp.h Wed May 28 01:58:41 2003 @@ -68,7 +68,7 @@ struct module *owner; }; -extern struct atm_tcp_ops atm_tcp_ops; +extern struct atm_tcp_ops *atm_tcp_ops; #endif diff -urN linux-2.4.20/include/linux/atmdev.h linux-2.4.20-atm/include/linux/atmdev.h --- linux-2.4.20/include/linux/atmdev.h Wed May 28 01:54:12 2003 +++ linux-2.4.20-atm/include/linux/atmdev.h Wed May 28 01:58:41 2003 @@ -30,10 +30,9 @@ #define ATM_DS3_PCR (8000*12) /* DS3: 12 cells in a 125 usec time slot */ -#define ATM_PDU_OVHD 0 /* number of bytes to charge against buffer - quota per PDU */ -#define ATM_SD(s) ((s)->sk->protinfo.af_atm) +#define atm_sk(__sk) ((struct atm_vcc *)(__sk)->protinfo.af_atm) +#define ATM_SD(s) (atm_sk((s)->sk)) #define __AAL_STAT_ITEMS \ @@ -292,10 +291,6 @@ struct atm_sap sap; /* SAP */ void (*push)(struct atm_vcc *vcc,struct sk_buff *skb); void (*pop)(struct atm_vcc *vcc,struct sk_buff *skb); /* optional */ - struct sk_buff *(*alloc_tx)(struct atm_vcc *vcc,unsigned int size); - /* TX allocation routine - can be */ - /* modified by protocol or by driver.*/ - /* NOTE: this interface will change */ int (*push_oam)(struct atm_vcc *vcc,void *cell); int (*send)(struct atm_vcc *vcc,struct sk_buff *skb); void *dev_data; /* per-device data */ @@ -303,15 +298,10 @@ struct k_atm_aal_stats *stats; /* pointer to AAL stats group */ wait_queue_head_t sleep; /* if socket is busy */ struct sock *sk; /* socket backpointer */ - struct atm_vcc *prev,*next; /* SVC part --- may move later ------------------------------------- */ - short itf; /* interface number */ struct sockaddr_atmsvc local; struct sockaddr_atmsvc remote; void (*callback)(struct atm_vcc *vcc); - struct sk_buff_head listenq; - int backlog_quota; /* number of connection requests we */ - /* can still accept */ int reply; /* also used by ATMTCP */ /* Multipoint part ------------------------------------------------- */ struct atm_vcc *session; /* session VCC descriptor */ @@ -337,8 +327,6 @@ /* (NULL) */ const char *type; /* device type name */ int number; /* device index */ - struct atm_vcc *vccs; /* VCC table (or NULL) */ - struct atm_vcc *last; /* last VCC (or undefined) */ void *dev_data; /* per-device data */ void *phy_data; /* private PHY date */ atm_dev_flags_t flags; /* device flags (ATM_DF_*) */ @@ -348,6 +336,8 @@ struct k_atm_dev_stats stats; /* statistics */ char signal; /* signal status (ATM_PHY_SIG_*) */ int link_rate; /* link rate (default: OC3) */ + atomic_t refcnt; /* reference count */ + spinlock_t lock; /* protect internal members */ #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_entry; /* proc entry */ char *proc_name; /* proc entry name */ @@ -388,8 +378,6 @@ void (*feedback)(struct atm_vcc *vcc,struct sk_buff *skb, unsigned long start,unsigned long dest,int len); int (*change_qos)(struct atm_vcc *vcc,struct atm_qos *qos,int flags); - void (*free_rx_skb)(struct atm_vcc *vcc, struct sk_buff *skb); - /* @@@ temporary hack */ int (*proc_read)(struct atm_dev *dev,loff_t *pos,char *page); struct module *owner; }; @@ -404,53 +392,94 @@ struct atm_skb_data { struct atm_vcc *vcc; /* ATM VCC */ - int iovcnt; /* 0 for "normal" operation */ unsigned long atm_options; /* ATM layer options */ }; +extern struct sock *vcc_sklist; +extern rwlock_t vcc_sklist_lock; + #define ATM_SKB(skb) (((struct atm_skb_data *) (skb)->cb)) struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops, int number,atm_dev_flags_t *flags); /* number == -1: pick first available */ -struct atm_dev *atm_find_dev(int number); void atm_dev_deregister(struct atm_dev *dev); +struct atm_dev *atm_dev_lookup(int number); +void atm_dev_hold(struct atm_dev *dev); +void atm_dev_release(struct atm_dev *dev); void shutdown_atm_dev(struct atm_dev *dev); -void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev); +void vcc_insert_socket(struct atm_dev *dev, struct sock *sk); +void vcc_remove_socket(struct atm_dev *dev, struct sock *sk); -/* - * This is approximately the algorithm used by alloc_skb. - * - */ +static inline int atm_guess_pdu2truesize(int pdu_size) +{ + /* must match allocation in alloc_skb */ + return SKB_DATA_ALIGN(pdu_size) + sizeof(struct sk_buff); +} + -static __inline__ int atm_guess_pdu2truesize(int pdu_size) +static inline void atm_force_charge(struct atm_vcc *vcc,int truesize) { - return ((pdu_size+15) & ~15) + sizeof(struct sk_buff); + atomic_add(truesize, &vcc->sk->rmem_alloc); } -static __inline__ void atm_force_charge(struct atm_vcc *vcc,int truesize) +static inline void atm_return(struct atm_vcc *vcc,int truesize) { - atomic_add(truesize+ATM_PDU_OVHD,&vcc->sk->rmem_alloc); + atomic_sub(truesize, &vcc->sk->rmem_alloc); +} + + +static inline int atm_may_send(struct atm_vcc *vcc,unsigned int size) +{ + return (size+atomic_read(&vcc->sk->wmem_alloc)) < vcc->sk->sndbuf; +} + + +static inline struct atm_vcc *__vcc_lookup(struct atm_dev *dev, short vpi, int vci) +{ + struct atm_vcc *vcc; + struct sock *s; + + for(s = vcc_sklist; s; s = s->next) { + vcc = atm_sk(s); + if (vcc->vci == vci && vcc->vpi == vpi && vcc->dev == dev) return vcc; + } + + return NULL; +} + + +static inline struct atm_vcc *vcc_lookup(struct atm_dev *dev, short vpi, int vci) +{ + struct atm_vcc *vcc; + + read_lock(&vcc_sklist_lock); + if ((vcc = __vcc_lookup(dev, vpi, vci))) { + sock_hold(vcc->sk); + read_unlock(&vcc_sklist_lock); + return vcc; + } + read_unlock(&vcc_sklist_lock); + return NULL; } -static __inline__ void atm_return(struct atm_vcc *vcc,int truesize) +static inline void vcc_hold(struct atm_vcc *vcc) { - atomic_sub(truesize+ATM_PDU_OVHD,&vcc->sk->rmem_alloc); + sock_hold(vcc->sk); } -static __inline__ int atm_may_send(struct atm_vcc *vcc,unsigned int size) +static inline void vcc_put(struct atm_vcc *vcc) { - return size+atomic_read(&vcc->sk->wmem_alloc)+ATM_PDU_OVHD < vcc->sk->sndbuf; + sock_put(vcc->sk); } int atm_charge(struct atm_vcc *vcc,int truesize); struct sk_buff *atm_alloc_charge(struct atm_vcc *vcc,int pdu_size, int gfp_flags); -int atm_find_ci(struct atm_vcc *vcc,short *vpi,int *vci); int atm_pcr_goal(struct atm_trafprm *tp); void atm_async_release_vcc(struct atm_vcc *vcc,int reply); diff -urN linux-2.4.20/include/linux/net.h linux-2.4.20-atm/include/linux/net.h --- linux-2.4.20/include/linux/net.h Thu Nov 22 20:46:19 2001 +++ linux-2.4.20-atm/include/linux/net.h Wed May 28 01:58:41 2003 @@ -139,6 +139,7 @@ extern int sock_recvmsg(struct socket *, struct msghdr *m, int len, int flags); extern int sock_readv_writev(int type, struct inode * inode, struct file * file, const struct iovec * iov, long count, long size); +extern struct socket *sockfd_lookup(int fd, int *err); extern int net_ratelimit(void); extern unsigned long net_random(void); diff -urN linux-2.4.20/include/net/atmclip.h linux-2.4.20-atm/include/net/atmclip.h --- linux-2.4.20/include/net/atmclip.h Thu Nov 22 20:49:31 2001 +++ linux-2.4.20-atm/include/net/atmclip.h Wed May 28 01:58:41 2003 @@ -55,13 +55,27 @@ }; -extern struct atm_vcc *atmarpd; /* ugly */ extern struct neigh_table clip_tbl; +extern struct atm_vcc *atmarpd; /* ugly */ int clip_create(int number); int clip_mkip(struct atm_vcc *vcc,int timeout); int clip_setentry(struct atm_vcc *vcc,u32 ip); int clip_encap(struct atm_vcc *vcc,int mode); -void atm_clip_init(void); + +struct atm_clip_ops { + int (*clip_create)(int number); + int (*clip_mkip)(struct atm_vcc *vcc,int timeout); + int (*clip_setentry)(struct atm_vcc *vcc,u32 ip); + int (*clip_encap)(struct atm_vcc *vcc,int mode); + void (*clip_push)(struct atm_vcc *vcc,struct sk_buff *skb); + int (*atm_init_atmarp)(struct atm_vcc *vcc); + struct module *owner; +}; + +#ifdef __KERNEL__ +extern struct neigh_table *clip_tbl_hook; +extern struct atm_clip_ops *atm_clip_ops; +#endif #endif diff -urN linux-2.4.20/net/Config.in linux-2.4.20-atm/net/Config.in --- linux-2.4.20/net/Config.in Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-atm/net/Config.in Wed May 28 01:58:41 2003 @@ -31,19 +31,19 @@ fi fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool 'Asynchronous Transfer Mode (ATM) (EXPERIMENTAL)' CONFIG_ATM - if [ "$CONFIG_ATM" = "y" ]; then + tristate 'Asynchronous Transfer Mode (ATM) (EXPERIMENTAL)' CONFIG_ATM + if [ "$CONFIG_ATM" != "n" ]; then if [ "$CONFIG_INET" = "y" ]; then - bool ' Classical IP over ATM' CONFIG_ATM_CLIP + dep_tristate ' Classical IP over ATM' CONFIG_ATM_CLIP $CONFIG_ATM if [ "$CONFIG_ATM_CLIP" = "y" ]; then bool ' Do NOT send ICMP if no neighbour' CONFIG_ATM_CLIP_NO_ICMP fi fi - tristate ' LAN Emulation (LANE) support' CONFIG_ATM_LANE + dep_tristate ' LAN Emulation (LANE) support' CONFIG_ATM_LANE $CONFIG_ATM if [ "$CONFIG_INET" = "y" -a "$CONFIG_ATM_LANE" != "n" ]; then - tristate ' Multi-Protocol Over ATM (MPOA) support' CONFIG_ATM_MPOA + dep_tristate ' Multi-Protocol Over ATM (MPOA) support' CONFIG_ATM_MPOA $CONFIG_ATM fi - tristate ' RFC1483/2684 Bridged protocols' CONFIG_ATM_BR2684 + dep_tristate ' RFC1483/2684 Bridged protocols' CONFIG_ATM_BR2684 $CONFIG_ATM if [ "$CONFIG_ATM_BR2684" != "n" ]; then bool ' Per-VC IP filter kludge' CONFIG_ATM_BR2684_IPFILTER fi diff -urN linux-2.4.20/net/atm/Makefile linux-2.4.20-atm/net/atm/Makefile --- linux-2.4.20/net/atm/Makefile Sat Aug 3 02:39:46 2002 +++ linux-2.4.20-atm/net/atm/Makefile Wed May 28 01:58:41 2003 @@ -14,32 +14,24 @@ list-multi := mpoa.o mpoa-objs := mpc.o mpoa_caches.o mpoa_proc.o -obj-$(CONFIG_ATM) := addr.o pvc.o signaling.o svc.o common.o atm_misc.o raw.o resources.o +obj-y := addr.o pvc.o signaling.o svc.o common.o atm_misc.o raw.o resources.o +obj-m := $(O_TARGET) -ifeq ($(CONFIG_ATM_CLIP),y) -obj-y += clip.o -NEED_IPCOM = ipcommon.o -endif - -ifeq ($(CONFIG_ATM_BR2684),y) - NEED_IPCOM = ipcommon.o -else - ifeq ($(CONFIG_ATM_BR2684),m) - NEED_IPCOM = ipcommon.o - endif -endif +obj-$(CONFIG_ATM_CLIP) += clip.o obj-$(CONFIG_ATM_BR2684) += br2684.o -ifeq ($(CONFIG_NET_SCH_ATM),y) -NEED_IPCOM = ipcommon.o +ifneq ($(CONFIG_ATM_CLIP),n) + ip-common-obj-y = ipcommon.o endif - -obj-y += $(NEED_IPCOM) - -ifeq ($(CONFIG_PROC_FS),y) -obj-y += proc.o +ifneq ($(CONFIG_ATM_BR2684),n) + ip-common-obj-y = ipcommon.o +endif +ifneq ($(CONFIG_NET_SCH_ATM),n) + ip-common-obj-y = ipcommon.o endif +obj-y += $(ip-common-obj-y) +obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_ATM_LANE) += lec.o obj-$(CONFIG_ATM_MPOA) += mpoa.o obj-$(CONFIG_PPPOATM) += pppoatm.o diff -urN linux-2.4.20/net/atm/addr.c linux-2.4.20-atm/net/atm/addr.c --- linux-2.4.20/net/atm/addr.c Fri Apr 6 19:51:19 2001 +++ linux-2.4.20-atm/net/atm/addr.c Wed May 28 01:58:41 2003 @@ -36,14 +36,6 @@ } -/* - * Avoid modification of any list of local interfaces while reading it - * (which may involve page faults and therefore rescheduling) - */ - -static DECLARE_MUTEX(local_lock); -extern spinlock_t atm_dev_lock; - static void notify_sigd(struct atm_dev *dev) { struct sockaddr_atmpvc pvc; @@ -60,13 +52,13 @@ { struct atm_dev_addr *this; - down(&local_lock); + spin_lock(&dev->lock); while (dev->local) { this = dev->local; dev->local = this->next; kfree(this); } - up(&local_lock); + spin_unlock(&dev->lock); notify_sigd(dev); } @@ -78,20 +70,20 @@ error = check_addr(addr); if (error) return error; - down(&local_lock); + spin_lock(&dev->lock); for (walk = &dev->local; *walk; walk = &(*walk)->next) if (identical(&(*walk)->addr,addr)) { - up(&local_lock); + spin_unlock(&dev->lock); return -EEXIST; } - *walk = kmalloc(sizeof(struct atm_dev_addr),GFP_KERNEL); + *walk = kmalloc(sizeof(struct atm_dev_addr),GFP_ATOMIC); if (!*walk) { - up(&local_lock); + spin_unlock(&dev->lock); return -ENOMEM; } (*walk)->addr = *addr; (*walk)->next = NULL; - up(&local_lock); + spin_unlock(&dev->lock); notify_sigd(dev); return 0; } @@ -104,17 +96,17 @@ error = check_addr(addr); if (error) return error; - down(&local_lock); + spin_lock(&dev->lock); for (walk = &dev->local; *walk; walk = &(*walk)->next) if (identical(&(*walk)->addr,addr)) break; if (!*walk) { - up(&local_lock); + spin_unlock(&dev->lock); return -ENOENT; } this = *walk; *walk = this->next; + spin_unlock(&dev->lock); kfree(this); - up(&local_lock); notify_sigd(dev); return 0; } @@ -125,21 +117,21 @@ struct atm_dev_addr *walk; int total; - down(&local_lock); + spin_lock(&dev->lock); total = 0; for (walk = dev->local; walk; walk = walk->next) { total += sizeof(struct sockaddr_atmsvc); if (total > size) { - up(&local_lock); + spin_unlock(&dev->lock); return -E2BIG; } if (copy_to_user(u_buf,&walk->addr, sizeof(struct sockaddr_atmsvc))) { - up(&local_lock); + spin_unlock(&dev->lock); return -EFAULT; } u_buf++; } - up(&local_lock); + spin_unlock(&dev->lock); return total; } diff -urN linux-2.4.20/net/atm/atm_misc.c linux-2.4.20-atm/net/atm/atm_misc.c --- linux-2.4.20/net/atm/atm_misc.c Wed May 28 01:54:25 2003 +++ linux-2.4.20-atm/net/atm/atm_misc.c Wed May 28 01:58:41 2003 @@ -43,63 +43,6 @@ } -static int check_ci(struct atm_vcc *vcc,short vpi,int vci) -{ - struct atm_vcc *walk; - - for (walk = vcc->dev->vccs; walk; walk = walk->next) - if (test_bit(ATM_VF_ADDR,&walk->flags) && walk->vpi == vpi && - walk->vci == vci && ((walk->qos.txtp.traffic_class != - ATM_NONE && vcc->qos.txtp.traffic_class != ATM_NONE) || - (walk->qos.rxtp.traffic_class != ATM_NONE && - vcc->qos.rxtp.traffic_class != ATM_NONE))) - return -EADDRINUSE; - /* allow VCCs with same VPI/VCI iff they don't collide on - TX/RX (but we may refuse such sharing for other reasons, - e.g. if protocol requires to have both channels) */ - return 0; -} - - -int atm_find_ci(struct atm_vcc *vcc,short *vpi,int *vci) -{ - static short p = 0; /* poor man's per-device cache */ - static int c = 0; - short old_p; - int old_c; - - if (*vpi != ATM_VPI_ANY && *vci != ATM_VCI_ANY) - return check_ci(vcc,*vpi,*vci); - /* last scan may have left values out of bounds for current device */ - if (*vpi != ATM_VPI_ANY) p = *vpi; - else if (p >= 1 << vcc->dev->ci_range.vpi_bits) p = 0; - if (*vci != ATM_VCI_ANY) c = *vci; - else if (c < ATM_NOT_RSV_VCI || c >= 1 << vcc->dev->ci_range.vci_bits) - c = ATM_NOT_RSV_VCI; - old_p = p; - old_c = c; - do { - if (!check_ci(vcc,p,c)) { - *vpi = p; - *vci = c; - return 0; - } - if (*vci == ATM_VCI_ANY) { - c++; - if (c >= 1 << vcc->dev->ci_range.vci_bits) - c = ATM_NOT_RSV_VCI; - } - if ((c == ATM_NOT_RSV_VCI || *vci != ATM_VCI_ANY) && - *vpi == ATM_VPI_ANY) { - p++; - if (p >= 1 << vcc->dev->ci_range.vpi_bits) p = 0; - } - } - while (old_p != p || old_c != c); - return -EADDRINUSE; -} - - /* * atm_pcr_goal returns the positive PCR if it should be rounded up, the * negative PCR if it should be rounded down, and zero if the maximum available @@ -155,7 +98,6 @@ EXPORT_SYMBOL(atm_charge); EXPORT_SYMBOL(atm_alloc_charge); -EXPORT_SYMBOL(atm_find_ci); EXPORT_SYMBOL(atm_pcr_goal); EXPORT_SYMBOL(sonet_copy_stats); EXPORT_SYMBOL(sonet_subtract_stats); diff -urN linux-2.4.20/net/atm/br2684.c linux-2.4.20-atm/net/atm/br2684.c --- linux-2.4.20/net/atm/br2684.c Wed May 28 01:54:25 2003 +++ linux-2.4.20-atm/net/atm/br2684.c Wed May 28 01:58:41 2003 @@ -189,7 +189,6 @@ return 0; } atomic_add(skb->truesize, &atmvcc->sk->wmem_alloc); - ATM_SKB(skb)->iovcnt = 0; ATM_SKB(skb)->atm_options = atmvcc->atm_options; brdev->stats.tx_packets++; brdev->stats.tx_bytes += skb->len; diff -urN linux-2.4.20/net/atm/clip.c linux-2.4.20-atm/net/atm/clip.c --- linux-2.4.20/net/atm/clip.c Wed May 28 01:54:25 2003 +++ linux-2.4.20-atm/net/atm/clip.c Wed May 28 01:58:41 2003 @@ -7,6 +7,7 @@ #include #include #include /* for UINT_MAX */ +#include #include #include #include @@ -48,7 +49,6 @@ static struct timer_list idle_timer; static int start_timer = 1; - static int to_atmarpd(enum atmarp_ctrl_type type,int itf,unsigned long ip) { struct atmarp_ctrl *ctrl; @@ -127,6 +127,8 @@ struct atmarp_entry *entry = NEIGH2ENTRY(n); struct clip_vcc *clip_vcc; + write_lock(&n->lock); + for (clip_vcc = entry->vccs; clip_vcc; clip_vcc = clip_vcc->next) if (clip_vcc->idle_timeout && @@ -140,6 +142,7 @@ } if (entry->vccs || time_before(jiffies, entry->expires)) { + write_unlock(&n->lock); np = &n->next; continue; } @@ -151,12 +154,14 @@ while ((skb = skb_dequeue(&n->arp_queue)) != NULL) dev_kfree_skb(skb); + write_unlock(&n->lock); np = &n->next; continue; } *np = n->next; DPRINTK("expired neigh %p\n",n); n->dead = 1; + write_unlock(&n->lock); neigh_release(n); } } @@ -218,6 +223,7 @@ clip_vcc->last_use = jiffies; PRIV(skb->dev)->stats.rx_packets++; PRIV(skb->dev)->stats.rx_bytes += skb->len; + memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data)); netif_rx(skb); } @@ -427,7 +433,6 @@ ((u16 *) here)[3] = skb->protocol; } atomic_add(skb->truesize,&vcc->sk->wmem_alloc); - ATM_SKB(skb)->iovcnt = 0; ATM_SKB(skb)->atm_options = vcc->atm_options; entry->vccs->last_use = jiffies; DPRINTK("atm_skb(%p)->vcc(%p)->dev(%p)\n",skb,vcc,vcc->dev); @@ -694,25 +699,21 @@ printk(KERN_ERR "atmarpd_close: closing with requests " "pending\n"); skb_queue_purge(&vcc->sk->receive_queue); + MOD_DEC_USE_COUNT; DPRINTK("(done)\n"); } static struct atmdev_ops atmarpd_dev_ops = { - close: atmarpd_close, + .close = atmarpd_close, }; static struct atm_dev atmarpd_dev = { - &atmarpd_dev_ops, - NULL, /* no PHY */ - "arpd", /* type */ - 999, /* dummy device number */ - NULL,NULL, /* pretend not to have any VCCs */ - NULL,NULL, /* no data */ - { 0 }, /* no flags */ - NULL, /* no local address */ - { 0 } /* no ESI, no statistics */ + .ops = &atmarpd_dev_ops, + .type = "arpd", + .number = 999, + .lock = SPIN_LOCK_UNLOCKED }; @@ -732,7 +733,7 @@ set_bit(ATM_VF_META,&vcc->flags); set_bit(ATM_VF_READY,&vcc->flags); /* allow replies and avoid getting closed if signaling dies */ - bind_vcc(vcc,&atmarpd_dev); + vcc_insert_socket(&atmarpd_dev, vcc->sk); vcc->push = NULL; vcc->pop = NULL; /* crash */ vcc->push_oam = NULL; /* crash */ @@ -746,10 +747,59 @@ return 0; } +static struct atm_clip_ops __atm_clip_ops = { + .clip_create = clip_create, + .clip_mkip = clip_mkip, + .clip_setentry = clip_setentry, + .clip_encap = clip_encap, + .clip_push = clip_push, + .atm_init_atmarp = atm_init_atmarp, + .owner = THIS_MODULE +}; -void atm_clip_init(void) +static int __init atm_clip_init(void) { + /* we should use neigh_table_init() */ clip_tbl.lock = RW_LOCK_UNLOCKED; clip_tbl.kmem_cachep = kmem_cache_create(clip_tbl.id, clip_tbl.entry_size, 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + + + /* so neigh_ifdown() doesn't complain */ + clip_tbl.proxy_timer.data = 0; + clip_tbl.proxy_timer.function = 0; + init_timer(&clip_tbl.proxy_timer); + skb_queue_head_init(&clip_tbl.proxy_queue); + + + atm_clip_ops = &__atm_clip_ops; + + clip_tbl_hook = &clip_tbl; + + return 0; } + +static void __exit atm_clip_exit(void) +{ + struct net_device *dev, *next; + + atm_clip_ops = NULL; + clip_tbl_hook = NULL; + + neigh_ifdown(&clip_tbl, NULL); + dev = clip_devs; + while (dev) { + next = PRIV(dev)->next; + unregister_netdev(dev); + kfree(dev); + dev = next; + } + if (start_timer == 0) del_timer(&idle_timer); + + kmem_cache_destroy(clip_tbl.kmem_cachep); +} + +module_init(atm_clip_init); +module_exit(atm_clip_exit); + +MODULE_LICENSE("GPL"); diff -urN linux-2.4.20/net/atm/common.c linux-2.4.20-atm/net/atm/common.c --- linux-2.4.20/net/atm/common.c Wed May 28 01:54:25 2003 +++ linux-2.4.20-atm/net/atm/common.c Wed May 28 02:03:31 2003 @@ -5,6 +5,7 @@ #include #include +#include #include #include /* struct socket, struct net_proto, struct proto_ops */ @@ -27,21 +27,22 @@ #include #include #include +#include #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) #include #include "lec.h" #include "lec_arpc.h" -struct atm_lane_ops atm_lane_ops; -#endif +struct atm_lane_ops *atm_lane_ops; #ifdef CONFIG_ATM_LANE_MODULE EXPORT_SYMBOL(atm_lane_ops); #endif +#endif #if defined(CONFIG_ATM_MPOA) || defined(CONFIG_ATM_MPOA_MODULE) #include #include "mpc.h" -struct atm_mpoa_ops atm_mpoa_ops; +struct atm_mpoa_ops *atm_mpoa_ops; #endif #ifdef CONFIG_ATM_MPOA_MODULE EXPORT_SYMBOL(atm_mpoa_ops); @@ -52,12 +53,20 @@ #if defined(CONFIG_ATM_TCP) || defined(CONFIG_ATM_TCP_MODULE) #include +struct atm_tcp_ops *atm_tcp_ops; #ifdef CONFIG_ATM_TCP_MODULE -struct atm_tcp_ops atm_tcp_ops; EXPORT_SYMBOL(atm_tcp_ops); #endif #endif +#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE) +#include +struct atm_clip_ops *atm_clip_ops; +#ifdef CONFIG_ATM_CLIP_MODULE +EXPORT_SYMBOL(atm_clip_ops); +#endif +#endif + #if defined(CONFIG_PPPOATM) || defined(CONFIG_PPPOATM_MODULE) int (*pppoatm_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long); EXPORT_SYMBOL(pppoatm_ioctl_hook); @@ -74,9 +83,6 @@ #include "common.h" /* prototypes */ #include "protocols.h" /* atm_init_ */ #include "addr.h" /* address registry */ -#ifdef CONFIG_ATM_CLIP -#include /* for clip_create */ -#endif #include "signaling.h" /* for WAITING and sigd_attach */ @@ -86,8 +92,6 @@ #define DPRINTK(format,args...) #endif -spinlock_t atm_dev_lock = SPIN_LOCK_UNLOCKED; - static struct sk_buff *alloc_tx(struct atm_vcc *vcc,unsigned int size) { struct sk_buff *skb; @@ -99,78 +103,94 @@ } while (!(skb = alloc_skb(size,GFP_KERNEL))) schedule(); DPRINTK("AlTx %d += %d\n",atomic_read(&vcc->sk->wmem_alloc),skb->truesize); - atomic_add(skb->truesize+ATM_PDU_OVHD,&vcc->sk->wmem_alloc); + atomic_add(skb->truesize,&vcc->sk->wmem_alloc); return skb; } -int atm_create(struct socket *sock,int protocol,int family) +static void vcc_destruct(struct sock *sk) +{ + if (atomic_read(&sk->rmem_alloc)) + printk(KERN_WARNING "rmem leakage (%d bytes) detected.\n", atomic_read(&sk->rmem_alloc)); + + if (atomic_read(&sk->wmem_alloc)) + printk(KERN_WARNING "wmem leakage (%d bytes) detected.\n", atomic_read(&sk->wmem_alloc)); + + kfree(sk->protinfo.af_atm); +} + + +int vcc_create(struct socket *sock,int protocol,int family) { struct sock *sk; struct atm_vcc *vcc; sock->sk = NULL; if (sock->type == SOCK_STREAM) return -EINVAL; - if (!(sk = alloc_atm_vcc_sk(family))) return -ENOMEM; - vcc = sk->protinfo.af_atm; + sk = sk_alloc(family, GFP_KERNEL, 1); + if (!sk) + return -ENOMEM; + sock_init_data(NULL, sk); + + vcc = atm_sk(sk) = kmalloc(sizeof(*vcc), GFP_KERNEL); + if (!vcc) { + sk_free(sk); + return -ENOMEM; + } + memset(vcc, 0, sizeof(*vcc)); + + vcc->sk = sk; memset(&vcc->flags,0,sizeof(vcc->flags)); vcc->dev = NULL; - vcc->alloc_tx = alloc_tx; vcc->callback = NULL; memset(&vcc->local,0,sizeof(struct sockaddr_atmsvc)); memset(&vcc->remote,0,sizeof(struct sockaddr_atmsvc)); vcc->qos.txtp.max_sdu = 1 << 16; /* for meta VCs */ - atomic_set(&vcc->sk->wmem_alloc,0); - atomic_set(&vcc->sk->rmem_alloc,0); vcc->push = NULL; vcc->pop = NULL; vcc->push_oam = NULL; vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */ vcc->atm_options = vcc->aal_options = 0; init_waitqueue_head(&vcc->sleep); - skb_queue_head_init(&vcc->listenq); sk->sleep = &vcc->sleep; + + sk->destruct = vcc_destruct; sock->sk = sk; return 0; } -void atm_release_vcc_sk(struct sock *sk,int free_sk) +void vcc_destroy_socket(struct sock *sk) { - struct atm_vcc *vcc; + struct atm_vcc *vcc = atm_sk(sk); + struct atm_dev *dev = vcc->dev; struct sk_buff *skb; - vcc = sk->protinfo.af_atm; clear_bit(ATM_VF_READY,&vcc->flags); - if (vcc->dev) { - if (vcc->dev->ops->close) vcc->dev->ops->close(vcc); + if (dev) { + if (dev->ops->close) dev->ops->close(vcc); if (vcc->push) vcc->push(vcc,NULL); /* atmarpd has no push */ + + vcc_remove_socket(dev, sk); /* no more receive */ + while ((skb = skb_dequeue(&vcc->sk->receive_queue))) { atm_return(vcc,skb->truesize); - if (vcc->dev->ops->free_rx_skb) - vcc->dev->ops->free_rx_skb(vcc,skb); - else kfree_skb(skb); + kfree_skb(skb); } - spin_lock (&atm_dev_lock); - fops_put (vcc->dev->ops); - if (atomic_read(&vcc->sk->rmem_alloc)) - printk(KERN_WARNING "atm_release_vcc: strange ... " - "rmem_alloc == %d after closing\n", - atomic_read(&vcc->sk->rmem_alloc)); - bind_vcc(vcc,NULL); - } else - spin_lock (&atm_dev_lock); - if (free_sk) free_atm_vcc_sk(sk); + fops_put (dev->ops); + atm_dev_release(dev); + } - spin_unlock (&atm_dev_lock); + sock_put(sk); } - -int atm_release(struct socket *sock) +int vcc_release(struct socket *sock) { - if (sock->sk) - atm_release_vcc_sk(sock->sk,1); + struct sock *sk = sock->sk; + if (sk) { + vcc_destroy_socket(sk); + } return 0; } @@ -212,7 +232,105 @@ } -static int atm_do_connect_dev(struct atm_vcc *vcc,struct atm_dev *dev,int vpi, +static int check_ci(struct atm_dev *dev, struct atm_vcc *vcc, short vpi, int vci) +{ + struct atm_vcc *walk; + struct sock *s; + + for(s = vcc_sklist; s; s = s->next) { + walk = atm_sk(s); + if (test_bit(ATM_VF_ADDR,&walk->flags) && walk->dev == dev && walk->vpi == vpi && + walk->vci == vci && ((walk->qos.txtp.traffic_class != + ATM_NONE && vcc->qos.txtp.traffic_class != ATM_NONE) || + (walk->qos.rxtp.traffic_class != ATM_NONE && + vcc->qos.rxtp.traffic_class != ATM_NONE))) { + return -EADDRINUSE; + } + /* allow VCCs with same VPI/VCI iff they don't collide on + TX/RX (but we may refuse such sharing for other reasons, + e.g. if protocol requires to have both channels) */ + } + return 0; +} + + +static int claim_ci(struct atm_dev *dev, struct atm_vcc *vcc, short *vpi, int *vci) +{ + static short p = 0; /* poor man's per-device cache */ + static int c = 0; + short old_p; + int old_c; + int error; + + if (*vpi != ATM_VPI_ANY && *vci != ATM_VCI_ANY) { + error = check_ci(dev, vcc, *vpi, *vci); + return error; + } + /* last scan may have left values out of bounds for current device */ + if (*vpi != ATM_VPI_ANY) p = *vpi; + else if (p >= 1 << dev->ci_range.vpi_bits) p = 0; + if (*vci != ATM_VCI_ANY) c = *vci; + else if (c < ATM_NOT_RSV_VCI || c >= 1 << dev->ci_range.vci_bits) + c = ATM_NOT_RSV_VCI; + old_p = p; + old_c = c; + do { + if (!check_ci(dev, vcc, p, c)) { + *vpi = p; + *vci = c; + return 0; + } + if (*vci == ATM_VCI_ANY) { + c++; + if (c >= 1 << dev->ci_range.vci_bits) + c = ATM_NOT_RSV_VCI; + } + if ((c == ATM_NOT_RSV_VCI || *vci != ATM_VCI_ANY) && + *vpi == ATM_VPI_ANY) { + p++; + if (p >= 1 << dev->ci_range.vpi_bits) p = 0; + } + } + while (old_p != p || old_c != c); + return -EADDRINUSE; +} + + +static void __vcc_insert_socket(struct atm_dev *dev, struct sock *sk) +{ + atm_sk(sk)->dev = dev; + sk->next = vcc_sklist; + if (sk->next) + vcc_sklist->pprev = &sk->next; + vcc_sklist = sk; + sk->pprev = &vcc_sklist; +} + + +void vcc_insert_socket(struct atm_dev *dev, struct sock *sk) +{ + write_lock_irq(&vcc_sklist_lock); + __vcc_insert_socket(dev, sk); + write_unlock_irq(&vcc_sklist_lock); +} +EXPORT_SYMBOL(vcc_insert_socket); + + +void vcc_remove_socket(struct atm_dev *dev, struct sock *sk) +{ + write_lock_irq(&vcc_sklist_lock); + if (sk->pprev) { + if (sk->next) + sk->next->pprev = sk->pprev; + *sk->pprev = sk->next; + sk->pprev = NULL; + } + write_unlock_irq(&vcc_sklist_lock); +} +EXPORT_SYMBOL(vcc_remove_socket); + + +static int atm_connect_dev(struct atm_vcc *vcc, struct atm_dev *dev, short vpi, int vci) { int error; @@ -223,8 +341,16 @@ return -EINVAL; if (vci > 0 && vci < ATM_NOT_RSV_VCI && !capable(CAP_NET_BIND_SERVICE)) return -EPERM; - error = 0; - bind_vcc(vcc,dev); + write_lock_irq(&vcc_sklist_lock); + error = claim_ci(dev, vcc, &vpi, &vci); + if (error) { + write_unlock_irq(&vcc_sklist_lock); + return error; + } + vcc->vpi = vpi; + vcc->vci = vci; + __vcc_insert_socket(dev, vcc->sk); + write_unlock_irq(&vcc_sklist_lock); switch (vcc->qos.aal) { case ATM_AAL0: error = atm_init_aal0(vcc); @@ -248,7 +374,7 @@ if (!error) error = adjust_tp(&vcc->qos.txtp,vcc->qos.aal); if (!error) error = adjust_tp(&vcc->qos.rxtp,vcc->qos.aal); if (error) { - bind_vcc(vcc,NULL); + vcc_remove_socket(dev, vcc->sk); return error; } DPRINTK("VCC %d.%d, AAL %d\n",vpi,vci,vcc->qos.aal); @@ -256,43 +382,24 @@ vcc->qos.txtp.min_pcr,vcc->qos.txtp.max_pcr,vcc->qos.txtp.max_sdu); DPRINTK(" RX: %d, PCR %d..%d, SDU %d\n",vcc->qos.rxtp.traffic_class, vcc->qos.rxtp.min_pcr,vcc->qos.rxtp.max_pcr,vcc->qos.rxtp.max_sdu); - fops_get (dev->ops); + if (dev->ops->owner) try_inc_mod_count(dev->ops->owner); if (dev->ops->open) { error = dev->ops->open(vcc,vpi,vci); if (error) { - fops_put (dev->ops); - bind_vcc(vcc,NULL); + if (dev->ops->owner) __MOD_DEC_USE_COUNT(dev->ops->owner); + vcc_remove_socket(dev, vcc->sk); return error; } } return 0; } - -static int atm_do_connect(struct atm_vcc *vcc,int itf,int vpi,int vci) -{ - struct atm_dev *dev; - int return_val; - - spin_lock (&atm_dev_lock); - dev = atm_find_dev(itf); - if (!dev) - return_val = -ENODEV; - else - return_val = atm_do_connect_dev(vcc,dev,vpi,vci); - - spin_unlock (&atm_dev_lock); - - return return_val; -} - - int atm_connect_vcc(struct atm_vcc *vcc,int itf,short vpi,int vci) { if (vpi != ATM_VPI_UNSPEC && vci != ATM_VCI_UNSPEC) clear_bit(ATM_VF_PARTIAL,&vcc->flags); else if (test_bit(ATM_VF_PARTIAL,&vcc->flags)) return -EINVAL; - printk(KERN_DEBUG "atm_connect (TX: cl %d,bw %d-%d,sdu %d; " + DPRINTK("atm_connect (TX: cl %d,bw %d-%d,sdu %d; " "RX: cl %d,bw %d-%d,sdu %d,AAL %s%d)\n", vcc->qos.txtp.traffic_class,vcc->qos.txtp.min_pcr, vcc->qos.txtp.max_pcr,vcc->qos.txtp.max_sdu, @@ -306,21 +413,28 @@ return -EINVAL; if (itf != ATM_ITF_ANY) { int error; + struct atm_dev *dev; - error = atm_do_connect(vcc,itf,vpi,vci); - if (error) return error; - } - else { + dev = atm_dev_lookup(itf); + if (!dev) return -ENODEV; + + error = atm_connect_dev(vcc,dev,vpi,vci); + if (error) { + atm_dev_release(dev); + return error; + } + } else { struct atm_dev *dev = NULL; - struct list_head *p; + struct list_head *p, *next; - spin_lock (&atm_dev_lock); - list_for_each(p, &atm_devs) { + list_for_each_safe(p, next, &atm_devs) { dev = list_entry(p, struct atm_dev, dev_list); - if (!atm_do_connect_dev(vcc,dev,vpi,vci)) break; + /* i suppose it could race in here CHAS */ + atm_dev_hold(dev); + if (!atm_connect_dev(vcc,dev,vpi,vci)) break; + atm_dev_release(dev); dev = NULL; } - spin_unlock (&atm_dev_lock); if (!dev) return -ENODEV; } if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) @@ -397,31 +511,8 @@ (unsigned long) buff,eff_len); DPRINTK("RcvM %d -= %d\n",atomic_read(&vcc->sk->rmem_alloc),skb->truesize); atm_return(vcc,skb->truesize); - if (ATM_SKB(skb)->iovcnt) { /* @@@ hack */ - /* iovcnt set, use scatter-gather for receive */ - int el, cnt; - struct iovec *iov = (struct iovec *)skb->data; - unsigned char *p = (unsigned char *)buff; - - el = eff_len; - error = 0; - for (cnt = 0; (cnt < ATM_SKB(skb)->iovcnt) && el; cnt++) { -/*printk("s-g???: %p -> %p (%d)\n",iov->iov_base,p,iov->iov_len);*/ - error = copy_to_user(p,iov->iov_base, - (iov->iov_len > el) ? el : iov->iov_len) ? - -EFAULT : 0; - if (error) break; - p += iov->iov_len; - el -= (iov->iov_len > el)?el:iov->iov_len; - iov++; - } - if (!vcc->dev->ops->free_rx_skb) kfree_skb(skb); - else vcc->dev->ops->free_rx_skb(vcc, skb); - return error ? error : eff_len; - } error = copy_to_user(buff,skb->data,eff_len) ? -EFAULT : 0; - if (!vcc->dev->ops->free_rx_skb) kfree_skb(skb); - else vcc->dev->ops->free_rx_skb(vcc, skb); + kfree_skb(skb); return error ? error : eff_len; } @@ -453,7 +544,7 @@ add_wait_queue(&vcc->sleep,&wait); set_current_state(TASK_INTERRUPTIBLE); error = 0; - while (!(skb = vcc->alloc_tx(vcc,eff))) { + while (!(skb = alloc_tx(vcc,eff))) { if (m->msg_flags & MSG_DONTWAIT) { error = -EAGAIN; break; @@ -478,7 +569,6 @@ remove_wait_queue(&vcc->sleep,&wait); if (error) return error; skb->dev = NULL; /* for paths shared with net_device interfaces */ - ATM_SKB(skb)->iovcnt = 0; ATM_SKB(skb)->atm_options = vcc->atm_options; if (copy_from_user(skb_put(skb,size),buff,size)) { kfree_skb(skb); @@ -498,15 +588,14 @@ vcc = ATM_SD(sock); poll_wait(file,&vcc->sleep,wait); mask = 0; - if (skb_peek(&vcc->sk->receive_queue) || skb_peek(&vcc->listenq)) + if (skb_peek(&vcc->sk->receive_queue)) mask |= POLLIN | POLLRDNORM; if (test_bit(ATM_VF_RELEASED,&vcc->flags) || test_bit(ATM_VF_CLOSE,&vcc->flags)) mask |= POLLHUP; if (sock->state != SS_CONNECTING) { if (vcc->qos.txtp.traffic_class != ATM_NONE && - vcc->qos.txtp.max_sdu+atomic_read(&vcc->sk->wmem_alloc)+ - ATM_PDU_OVHD <= vcc->sk->sndbuf) + vcc->qos.txtp.max_sdu+atomic_read(&vcc->sk->wmem_alloc) <= vcc->sk->sndbuf) mask |= POLLOUT | POLLWRNORM; } else if (vcc->reply != WAITING) { @@ -555,7 +644,7 @@ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg) { - struct atm_dev *dev; + struct atm_dev *dev = NULL; struct list_head *p; struct atm_vcc *vcc; int *tmp_buf, *tmp_p; @@ -563,7 +652,6 @@ int error,len,size,number, ret_val; ret_val = 0; - spin_lock (&atm_dev_lock); vcc = ATM_SD(sock); switch (cmd) { case SIOCOUTQ: @@ -581,7 +661,7 @@ goto done; } ret_val = put_user(vcc->sk->sndbuf- - atomic_read(&vcc->sk->wmem_alloc)-ATM_PDU_OVHD, + atomic_read(&vcc->sk->wmem_alloc), (int *) arg) ? -EFAULT : 0; goto done; case SIOCINQ: @@ -601,13 +689,14 @@ goto done; } size = 0; + read_lock(&atm_dev_lock); list_for_each(p, &atm_devs) size += sizeof(int); if (size > len) { ret_val = -E2BIG; goto done; } - tmp_buf = kmalloc(size,GFP_KERNEL); + tmp_buf = kmalloc(size, GFP_ATOMIC); if (!tmp_buf) { ret_val = -ENOMEM; goto done; @@ -617,6 +706,8 @@ dev = list_entry(p, struct atm_dev, dev_list); *tmp_p++ = dev->number; } + read_unlock(&atm_dev_lock); + dev = NULL; ret_val = ((copy_to_user(buf, tmp_buf, size)) || put_user(size, &((struct atm_iobuf *) arg)->length) ) ? -EFAULT : 0; @@ -654,39 +745,50 @@ if (!error) sock->state = SS_CONNECTED; ret_val = error; goto done; -#ifdef CONFIG_ATM_CLIP +#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE) case SIOCMKCLIP: if (!capable(CAP_NET_ADMIN)) ret_val = -EPERM; else - ret_val = clip_create(arg); + ret_val = atm_clip_ops->clip_create(arg); goto done; case ATMARPD_CTRL: if (!capable(CAP_NET_ADMIN)) { ret_val = -EPERM; goto done; } - error = atm_init_atmarp(vcc); - if (!error) sock->state = SS_CONNECTED; +#if defined(CONFIG_ATM_CLIP_MODULE) + if (atm_clip_ops == NULL) + request_module("clip"); +#endif + if (atm_clip_ops && !try_inc_mod_count(atm_clip_ops->owner)) { + ret_val = -ENOSYS; + goto done; + } + error = atm_clip_ops->atm_init_atmarp(vcc); + if (!error) + sock->state = SS_CONNECTED; + else + __MOD_DEC_USE_COUNT(atm_clip_ops->owner); ret_val = error; goto done; case ATMARP_MKIP: if (!capable(CAP_NET_ADMIN)) ret_val = -EPERM; else - ret_val = clip_mkip(vcc,arg); + ret_val = atm_clip_ops->clip_mkip(vcc,arg); goto done; case ATMARP_SETENTRY: if (!capable(CAP_NET_ADMIN)) ret_val = -EPERM; else - ret_val = clip_setentry(vcc,arg); + ret_val = atm_clip_ops->clip_setentry(vcc,arg); goto done; case ATMARP_ENCAP: if (!capable(CAP_NET_ADMIN)) ret_val = -EPERM; else - ret_val = clip_encap(vcc,arg); + ret_val = atm_clip_ops->clip_encap(vcc,arg); goto done; #endif #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) @@ -695,27 +797,32 @@ ret_val = -EPERM; goto done; } - if (atm_lane_ops.lecd_attach == NULL) - atm_lane_init(); - if (atm_lane_ops.lecd_attach == NULL) { /* try again */ +#if defined(CONFIG_ATM_LANE_MODULE) + if (atm_lane_ops == NULL) + request_module("lec"); +#endif + if (atm_lane_ops && !try_inc_mod_count(atm_lane_ops->owner)) { ret_val = -ENOSYS; goto done; } - error = atm_lane_ops.lecd_attach(vcc, (int)arg); - if (error >= 0) sock->state = SS_CONNECTED; + error = atm_lane_ops->lecd_attach(vcc, (int)arg); + if (error >= 0) + sock->state = SS_CONNECTED; + else + __MOD_DEC_USE_COUNT(atm_lane_ops->owner); ret_val = error; goto done; case ATMLEC_MCAST: if (!capable(CAP_NET_ADMIN)) ret_val = -EPERM; else - ret_val = atm_lane_ops.mcast_attach(vcc, (int)arg); + ret_val = atm_lane_ops->mcast_attach(vcc, (int)arg); goto done; case ATMLEC_DATA: if (!capable(CAP_NET_ADMIN)) ret_val = -EPERM; else - ret_val = atm_lane_ops.vcc_attach(vcc, (void*)arg); + ret_val = atm_lane_ops->vcc_attach(vcc, (void*)arg); goto done; #endif #if defined(CONFIG_ATM_MPOA) || defined(CONFIG_ATM_MPOA_MODULE) @@ -724,21 +831,26 @@ ret_val = -EPERM; goto done; } - if (atm_mpoa_ops.mpoad_attach == NULL) - atm_mpoa_init(); - if (atm_mpoa_ops.mpoad_attach == NULL) { /* try again */ +#if defined(CONFIG_ATM_MPOA_MODULE) + if (atm_mpoa_ops == NULL) + request_module("mpoa"); +#endif + if (atm_mpoa_ops && !try_inc_mod_count(atm_mpoa_ops->owner)) { ret_val = -ENOSYS; goto done; } - error = atm_mpoa_ops.mpoad_attach(vcc, (int)arg); - if (error >= 0) sock->state = SS_CONNECTED; + error = atm_mpoa_ops->mpoad_attach(vcc, (int)arg); + if (error >= 0) + sock->state = SS_CONNECTED; + else + __MOD_DEC_USE_COUNT(atm_mpoa_ops->owner); ret_val = error; goto done; case ATMMPC_DATA: if (!capable(CAP_NET_ADMIN)) ret_val = -EPERM; else - ret_val = atm_mpoa_ops.vcc_attach(vcc, arg); + ret_val = atm_mpoa_ops->vcc_attach(vcc, arg); goto done; #endif #if defined(CONFIG_ATM_TCP) || defined(CONFIG_ATM_TCP_MODULE) @@ -747,14 +859,14 @@ ret_val = -EPERM; goto done; } - if (!atm_tcp_ops.attach) { + if (!atm_tcp_ops) { ret_val = -ENOPKG; goto done; } - fops_get (&atm_tcp_ops); - error = atm_tcp_ops.attach(vcc,(int) arg); + fops_get(atm_tcp_ops); + error = atm_tcp_ops->attach(vcc,(int) arg); if (error >= 0) sock->state = SS_CONNECTED; - else fops_put (&atm_tcp_ops); + else fops_put(atm_tcp_ops); ret_val = error; goto done; case ATMTCP_CREATE: @@ -762,12 +874,12 @@ ret_val = -EPERM; goto done; } - if (!atm_tcp_ops.create_persistent) { + if (!atm_tcp_ops) { ret_val = -ENOPKG; goto done; } - error = atm_tcp_ops.create_persistent((int) arg); - if (error < 0) fops_put (&atm_tcp_ops); + error = atm_tcp_ops->create_persistent((int) arg); + if (error < 0) fops_put(atm_tcp_ops); ret_val = error; goto done; case ATMTCP_REMOVE: @@ -775,12 +887,12 @@ ret_val = -EPERM; goto done; } - if (!atm_tcp_ops.remove_persistent) { + if (!atm_tcp_ops) { ret_val = -ENOPKG; goto done; } - error = atm_tcp_ops.remove_persistent((int) arg); - fops_put (&atm_tcp_ops); + error = atm_tcp_ops->remove_persistent((int) arg); + fops_put(atm_tcp_ops); ret_val = error; goto done; #endif @@ -814,7 +926,7 @@ ret_val = -EFAULT; goto done; } - if (!(dev = atm_find_dev(number))) { + if (!(dev = atm_dev_lookup(number))) { ret_val = -ENODEV; goto done; } @@ -963,7 +1075,7 @@ ret_val = 0; done: - spin_unlock (&atm_dev_lock); + if (dev) atm_dev_release(dev); return ret_val; } @@ -1034,17 +1146,21 @@ switch (optname) { case SO_ATMQOS: { - struct atm_qos qos; + struct atm_qos *qos; - if (copy_from_user(&qos,optval,sizeof(qos))) + qos = kmalloc(sizeof(*qos), GFP_KERNEL); + if (!qos) + return -ENOMEM; + if (copy_from_user(qos,optval,sizeof(*qos))) return -EFAULT; - error = check_qos(&qos); + error = check_qos(qos); if (error) return error; if (sock->state == SS_CONNECTED) - return atm_change_qos(vcc,&qos); + return atm_change_qos(vcc,qos); if (sock->state != SS_UNCONNECTED) return -EBADFD; - vcc->qos = qos; + vcc->qos = *qos; + kfree(qos); set_bit(ATM_VF_HASQOS,&vcc->flags); return 0; } @@ -1122,40 +1238,6 @@ } -/* - * lane_mpoa_init.c: A couple of helper functions - * to make modular LANE and MPOA client easier to implement - */ - -/* - * This is how it goes: - * - * if xxxx is not compiled as module, call atm_xxxx_init_ops() - * from here - * else call atm_mpoa_init_ops() from init_module() within - * the kernel when xxxx module is loaded - * - * In either case function pointers in struct atm_xxxx_ops - * are initialized to their correct values. Either they - * point to functions in the module or in the kernel - */ - -extern struct atm_mpoa_ops atm_mpoa_ops; /* in common.c */ -extern struct atm_lane_ops atm_lane_ops; /* in common.c */ - -#if defined(CONFIG_ATM_MPOA) || defined(CONFIG_ATM_MPOA_MODULE) -void atm_mpoa_init(void) -{ -#ifndef CONFIG_ATM_MPOA_MODULE /* not module */ - atm_mpoa_init_ops(&atm_mpoa_ops); -#else - request_module("mpoa"); -#endif - - return; -} -#endif - #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br, @@ -1166,15 +1248,33 @@ EXPORT_SYMBOL(br_fdb_put_hook); #endif /* defined(CONFIG_ATM_LANE_MODULE) || defined(CONFIG_BRIDGE_MODULE) */ #endif /* defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) */ +#endif /* defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) */ + -void atm_lane_init(void) +static int __init atm_init(void) { -#ifndef CONFIG_ATM_LANE_MODULE /* not module */ - atm_lane_init_ops(&atm_lane_ops); -#else - request_module("lec"); + int error = 0; + + if (atmpvc_init() < 0) + return -1; + if (atmsvc_init() < 0) + return -1; +#ifdef CONFIG_PROC_FS + error = atm_proc_init(); + if (error) printk("atm_proc_init fails with %d\n",error); #endif + return error; +} - return; -} +static void __exit atm_exit(void) +{ +#ifdef CONFIG_PROC_FS + atm_proc_exit(); #endif + atmsvc_exit(); + atmpvc_exit(); +} + +module_init(atm_init); +module_exit(atm_exit); +MODULE_LICENSE("GPL"); diff -urN linux-2.4.20/net/atm/common.h linux-2.4.20-atm/net/atm/common.h --- linux-2.4.20/net/atm/common.h Mon Dec 11 22:33:43 2000 +++ linux-2.4.20-atm/net/atm/common.h Wed May 28 01:58:41 2003 @@ -10,8 +10,8 @@ #include /* for poll_table */ -int atm_create(struct socket *sock,int protocol,int family); -int atm_release(struct socket *sock); +int vcc_create(struct socket *sock,int protocol,int family); +int vcc_release(struct socket *sock); int atm_connect(struct socket *sock,int itf,short vpi,int vci); int atm_recvmsg(struct socket *sock,struct msghdr *m,int total_len, int flags,struct scm_cookie *scm); @@ -25,10 +25,17 @@ int *optlen); int atm_connect_vcc(struct atm_vcc *vcc,int itf,short vpi,int vci); -void atm_release_vcc_sk(struct sock *sk,int free_sk); + void atm_shutdown_dev(struct atm_dev *dev); +int atmsvc_init(void); +void atmsvc_exit(void); + +int atmpvc_init(void); +void atmpvc_exit(void); + int atm_proc_init(void); +void atm_proc_exit(void); /* SVC */ diff -urN linux-2.4.20/net/atm/ipcommon.c linux-2.4.20-atm/net/atm/ipcommon.c --- linux-2.4.20/net/atm/ipcommon.c Fri Jul 7 06:37:24 2000 +++ linux-2.4.20-atm/net/atm/ipcommon.c Wed May 28 01:58:41 2003 @@ -66,5 +66,5 @@ spin_unlock_irqrestore(&from->lock,flags); } - +EXPORT_SYMBOL(llc_oui); EXPORT_SYMBOL(skb_migrate); diff -urN linux-2.4.20/net/atm/ipcommon.h linux-2.4.20-atm/net/atm/ipcommon.h --- linux-2.4.20/net/atm/ipcommon.h Mon Dec 11 22:33:46 2000 +++ linux-2.4.20-atm/net/atm/ipcommon.h Wed May 28 01:58:41 2003 @@ -13,8 +13,6 @@ #include -extern struct net_device *clip_devs; - /* * Appends all skbs from "from" to "to". The operation is atomic with respect * to all other skb operations on "from" or "to". diff -urN linux-2.4.20/net/atm/lec.c linux-2.4.20-atm/net/atm/lec.c --- linux-2.4.20/net/atm/lec.c Wed May 28 01:54:25 2003 +++ linux-2.4.20-atm/net/atm/lec.c Wed May 28 02:12:42 2003 @@ -20,6 +20,7 @@ #include #include #include +#include /* TokenRing if needed */ #ifdef CONFIG_TR @@ -35,6 +36,10 @@ #include #include "../bridge/br_private.h" static unsigned char bridge_ula_lec[] = {0x01, 0x80, 0xc2, 0x00, 0x00}; + +extern struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br, + unsigned char *addr); +extern void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent); #endif /* Modular too */ @@ -43,7 +48,7 @@ #include "lec.h" #include "lec_arpc.h" -#include "resources.h" /* for bind_vcc() */ +#include "resources.h" #if 0 #define DPRINTK printk @@ -51,10 +56,7 @@ #define DPRINTK(format,args...) #endif -extern struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br, - unsigned char *addr); -extern void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent); - +static spinlock_t lec_arp_spinlock = SPIN_LOCK_UNLOCKED; #define DUMP_PACKETS 0 /* 0 = None, * 1 = 30 first bytes @@ -196,6 +198,22 @@ return 0; } +static __inline__ void +lec_send(struct atm_vcc *vcc, struct sk_buff *skb, struct lec_priv *priv) +{ + if (atm_may_send(vcc, skb->len)) { + atomic_add(skb->truesize, &vcc->sk->wmem_alloc); + ATM_SKB(skb)->vcc = vcc; + ATM_SKB(skb)->atm_options = vcc->atm_options; + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + vcc->send(vcc, skb); + } else { + priv->stats.tx_dropped++; + dev_kfree_skb(skb); + } +} + static int lec_send_packet(struct sk_buff *skb, struct net_device *dev) { @@ -340,34 +358,10 @@ DPRINTK("MAC address 0x%02x:%02x:%02x:%02x:%02x:%02x\n", lec_h->h_dest[0], lec_h->h_dest[1], lec_h->h_dest[2], lec_h->h_dest[3], lec_h->h_dest[4], lec_h->h_dest[5]); - ATM_SKB(skb2)->vcc = send_vcc; - ATM_SKB(skb2)->iovcnt = 0; - ATM_SKB(skb2)->atm_options = send_vcc->atm_options; - DPRINTK("%s:sending to vpi:%d vci:%d\n", dev->name, - send_vcc->vpi, send_vcc->vci); - if (atm_may_send(send_vcc, skb2->len)) { - atomic_add(skb2->truesize, &send_vcc->sk->wmem_alloc); - priv->stats.tx_packets++; - priv->stats.tx_bytes += skb2->len; - send_vcc->send(send_vcc, skb2); - } else { - priv->stats.tx_dropped++; - dev_kfree_skb(skb2); - } + lec_send(send_vcc, skb2, priv); } - ATM_SKB(skb)->vcc = send_vcc; - ATM_SKB(skb)->iovcnt = 0; - ATM_SKB(skb)->atm_options = send_vcc->atm_options; - if (atm_may_send(send_vcc, skb->len)) { - atomic_add(skb->truesize, &send_vcc->sk->wmem_alloc); - priv->stats.tx_packets++; - priv->stats.tx_bytes += skb->len; - send_vcc->send(send_vcc, skb); - } else { - priv->stats.tx_dropped++; - dev_kfree_skb(skb); - } + lec_send(send_vcc, skb, priv); #if 0 /* Should we wait for card's device driver to notify us? */ @@ -404,7 +398,7 @@ int i; char *tmp; /* FIXME */ - atomic_sub(skb->truesize+ATM_PDU_OVHD, &vcc->sk->wmem_alloc); + atomic_sub(skb->truesize, &vcc->sk->wmem_alloc); mesg = (struct atmlec_msg *)skb->data; tmp = skb->data; tmp += sizeof(struct atmlec_msg); @@ -548,24 +542,19 @@ } printk("%s: Shut down!\n", dev->name); - MOD_DEC_USE_COUNT; + __MOD_DEC_USE_COUNT(THIS_MODULE); } static struct atmdev_ops lecdev_ops = { - close: lec_atm_close, - send: lec_atm_send + .close = lec_atm_close, + .send = lec_atm_send }; static struct atm_dev lecatm_dev = { - &lecdev_ops, - NULL, /*PHY*/ - "lec", /*type*/ - 999, /*dummy device number*/ - NULL,NULL, /*no VCCs*/ - NULL,NULL, /*no data*/ - { 0 }, /*no flags*/ - NULL, /* no local address*/ - { 0 } /*no ESI or rest of the atm_dev struct things*/ + .ops = &lecdev_ops, + .type = "lec", + .number = 999, + .lock = SPIN_LOCK_UNLOCKED, }; /* @@ -726,6 +723,7 @@ skb->protocol = eth_type_trans(skb, dev); priv->stats.rx_packets++; priv->stats.rx_bytes += skb->len; + memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data)); netif_rx(skb); } } @@ -812,7 +810,7 @@ lec_arp_init(priv); priv->itfnum = i; /* LANE2 addition */ priv->lecd = vcc; - bind_vcc(vcc, &lecatm_dev); + vcc_insert_socket(&lecatm_dev, vcc->sk); vcc->proto_data = dev_lec[i]; set_bit(ATM_VF_META,&vcc->flags); @@ -833,27 +831,23 @@ if (dev_lec[i]->flags & IFF_UP) { netif_start_queue(dev_lec[i]); } - MOD_INC_USE_COUNT; return i; } -void atm_lane_init_ops(struct atm_lane_ops *ops) -{ - ops->lecd_attach = lecd_attach; - ops->mcast_attach = lec_mcast_attach; - ops->vcc_attach = lec_vcc_attach; - ops->get_lecs = get_dev_lec; - - printk("lec.c: " __DATE__ " " __TIME__ " initialized\n"); - - return; -} +static struct atm_lane_ops __atm_lane_ops = { + .lecd_attach = lecd_attach, + .mcast_attach = lec_mcast_attach, + .vcc_attach = lec_vcc_attach, + .get_lecs = get_dev_lec, + .owner = THIS_MODULE +}; static int __init lane_module_init(void) { - extern struct atm_lane_ops atm_lane_ops; + extern struct atm_lane_ops *atm_lane_ops; - atm_lane_init_ops(&atm_lane_ops); + atm_lane_ops = &__atm_lane_ops; + printk("lec.c: " __DATE__ " " __TIME__ " initialized\n"); return 0; } @@ -861,13 +855,10 @@ static void __exit lane_module_cleanup(void) { int i; - extern struct atm_lane_ops atm_lane_ops; + extern struct atm_lane_ops *atm_lane_ops; struct lec_priv *priv; - atm_lane_ops.lecd_attach = NULL; - atm_lane_ops.mcast_attach = NULL; - atm_lane_ops.vcc_attach = NULL; - atm_lane_ops.get_lecs = NULL; + atm_lane_ops = NULL; for (i = 0; i < MAX_LEC_ITF; i++) { if (dev_lec[i] != NULL) { @@ -880,7 +871,7 @@ unregister_netdev(dev_lec[i]); kfree(dev_lec[i]); dev_lec[i] = NULL; - } + } } return; @@ -1030,7 +1021,7 @@ #include #include #include -#include +#include #include @@ -1055,15 +1046,15 @@ #define HASH(ch) (ch & (LEC_ARP_TABLE_SIZE -1)) static __inline__ void -lec_arp_lock(struct lec_priv *priv) +lec_arp_get(struct lec_priv *priv) { - atomic_inc(&priv->lec_arp_lock_var); + atomic_inc(&priv->lec_arp_users); } static __inline__ void -lec_arp_unlock(struct lec_priv *priv) +lec_arp_put(struct lec_priv *priv) { - atomic_dec(&priv->lec_arp_lock_var); + atomic_dec(&priv->lec_arp_users); } /* @@ -1114,33 +1105,33 @@ * LANE2: Add to the end of the list to satisfy 8.1.13 */ static __inline__ void -lec_arp_put(struct lec_arp_table **lec_arp_tables, - struct lec_arp_table *to_put) +lec_arp_add(struct lec_arp_table **lec_arp_tables, + struct lec_arp_table *to_add) { - unsigned short place; unsigned long flags; + unsigned short place; struct lec_arp_table *tmp; - save_flags(flags); - cli(); + spin_lock_irqsave(&lec_arp_spinlock, flags); - place = HASH(to_put->mac_addr[ETH_ALEN-1]); + place = HASH(to_add->mac_addr[ETH_ALEN-1]); tmp = lec_arp_tables[place]; - to_put->next = NULL; + to_add->next = NULL; if (tmp == NULL) - lec_arp_tables[place] = to_put; + lec_arp_tables[place] = to_add; else { /* add to the end */ while (tmp->next) tmp = tmp->next; - tmp->next = to_put; + tmp->next = to_add; } - restore_flags(flags); + spin_unlock_irqrestore(&lec_arp_spinlock, flags); + DPRINTK("LEC_ARP: Added entry:%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n", - 0xff&to_put->mac_addr[0], 0xff&to_put->mac_addr[1], - 0xff&to_put->mac_addr[2], 0xff&to_put->mac_addr[3], - 0xff&to_put->mac_addr[4], 0xff&to_put->mac_addr[5]); + 0xff&to_add->mac_addr[0], 0xff&to_add->mac_addr[1], + 0xff&to_add->mac_addr[2], 0xff&to_add->mac_addr[3], + 0xff&to_add->mac_addr[4], 0xff&to_add->mac_addr[5]); } /* @@ -1150,16 +1141,15 @@ lec_arp_remove(struct lec_arp_table **lec_arp_tables, struct lec_arp_table *to_remove) { + unsigned long flags; unsigned short place; struct lec_arp_table *tmp; - unsigned long flags; int remove_vcc=1; - save_flags(flags); - cli(); + spin_lock_irqsave(&lec_arp_spinlock, flags); if (!to_remove) { - restore_flags(flags); + spin_unlock_irqrestore(&lec_arp_spinlock, flags); return -1; } place = HASH(to_remove->mac_addr[ETH_ALEN-1]); @@ -1171,7 +1161,7 @@ tmp = tmp->next; } if (!tmp) {/* Entry was not found */ - restore_flags(flags); + spin_unlock_irqrestore(&lec_arp_spinlock, flags); return -1; } } @@ -1197,7 +1187,9 @@ lec_arp_clear_vccs(to_remove); } skb_queue_purge(&to_remove->tx_wait); /* FIXME: good place for this? */ - restore_flags(flags); + + spin_unlock_irqrestore(&lec_arp_spinlock, flags); + DPRINTK("LEC_ARP: Removed entry:%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n", 0xff&to_remove->mac_addr[0], 0xff&to_remove->mac_addr[1], 0xff&to_remove->mac_addr[2], 0xff&to_remove->mac_addr[3], @@ -1382,12 +1374,8 @@ lec_arp_destroy(struct lec_priv *priv) { struct lec_arp_table *entry, *next; - unsigned long flags; int i; - save_flags(flags); - cli(); - del_timer(&priv->lec_arp_timer); /* @@ -1430,7 +1418,6 @@ priv->mcast_vcc = NULL; memset(priv->lec_arp_tables, 0, sizeof(struct lec_arp_table*)*LEC_ARP_TABLE_SIZE); - restore_flags(flags); } @@ -1447,18 +1434,18 @@ DPRINTK("LEC_ARP: lec_arp_find :%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n", mac_addr[0]&0xff, mac_addr[1]&0xff, mac_addr[2]&0xff, mac_addr[3]&0xff, mac_addr[4]&0xff, mac_addr[5]&0xff); - lec_arp_lock(priv); + lec_arp_get(priv); place = HASH(mac_addr[ETH_ALEN-1]); to_return = priv->lec_arp_tables[place]; while(to_return) { if (memcmp(mac_addr, to_return->mac_addr, ETH_ALEN) == 0) { - lec_arp_unlock(priv); + lec_arp_put(priv); return to_return; } to_return = to_return->next; } - lec_arp_unlock(priv); + lec_arp_put(priv); return NULL; } @@ -1585,11 +1572,11 @@ del_timer(&priv->lec_arp_timer); DPRINTK("lec_arp_check_expire %p,%d\n",priv, - priv->lec_arp_lock_var.counter); + atomic_read(&priv->lec_arp_users)); DPRINTK("expire: eo:%p nf:%p\n",priv->lec_arp_empty_ones, priv->lec_no_forward); - if (!priv->lec_arp_lock_var.counter) { - lec_arp_lock(priv); + if (!atomic_read(&priv->lec_arp_users)) { + lec_arp_get(priv); now = jiffies; for(i=0;itimestamp+ priv->path_switching_delay)) { + struct sk_buff *skb; + + while ((skb = skb_dequeue(&entry->tx_wait))) + lec_send(entry->vcc, skb, entry->priv); entry->last_used = jiffies; entry->status = ESI_FORWARD_DIRECT; @@ -1635,7 +1626,7 @@ } } } - lec_arp_unlock(priv); + lec_arp_put(priv); } priv->lec_arp_timer.expires = jiffies + LEC_ARP_REFRESH_INTERVAL; add_timer(&priv->lec_arp_timer); @@ -1697,7 +1688,7 @@ if (!entry) { return priv->mcast_vcc; } - lec_arp_put(priv->lec_arp_tables, entry); + lec_arp_add(priv->lec_arp_tables, entry); /* We want arp-request(s) to be sent */ entry->packets_flooded =1; entry->status = ESI_ARP_PENDING; @@ -1722,7 +1713,7 @@ struct lec_arp_table *entry, *next; int i; - lec_arp_lock(priv); + lec_arp_get(priv); DPRINTK("lec_addr_delete\n"); for(i=0;ilec_arp_tables[i];entry != NULL; entry=next) { @@ -1733,11 +1724,11 @@ lec_arp_remove(priv->lec_arp_tables, entry); kfree(entry); } - lec_arp_unlock(priv); + lec_arp_put(priv); return 0; } } - lec_arp_unlock(priv); + lec_arp_put(priv); return -1; } @@ -1762,7 +1753,7 @@ return; /* LANE2: ignore targetless LE_ARPs for which * we have no entry in the cache. 7.1.30 */ - lec_arp_lock(priv); + lec_arp_get(priv); if (priv->lec_arp_empty_ones) { entry = priv->lec_arp_empty_ones; if (!memcmp(entry->atm_addr, atm_addr, ATM_ESA_LEN)) { @@ -1796,13 +1787,13 @@ entry->status = ESI_FORWARD_DIRECT; memcpy(entry->mac_addr, mac_addr, ETH_ALEN); entry->last_used = jiffies; - lec_arp_put(priv->lec_arp_tables, entry); + lec_arp_add(priv->lec_arp_tables, entry); } if (remoteflag) entry->flags|=LEC_REMOTE_FLAG; else entry->flags&=~LEC_REMOTE_FLAG; - lec_arp_unlock(priv); + lec_arp_put(priv); DPRINTK("After update\n"); dump_arp_table(priv); return; @@ -1812,11 +1803,11 @@ if (!entry) { entry = make_entry(priv, mac_addr); if (!entry) { - lec_arp_unlock(priv); + lec_arp_put(priv); return; } entry->status = ESI_UNKNOWN; - lec_arp_put(priv->lec_arp_tables, entry); + lec_arp_add(priv->lec_arp_tables, entry); /* Temporary, changes before end of function */ } memcpy(entry->atm_addr, atm_addr, ATM_ESA_LEN); @@ -1851,7 +1842,7 @@ } DPRINTK("After update2\n"); dump_arp_table(priv); - lec_arp_unlock(priv); + lec_arp_put(priv); } /* @@ -1865,7 +1856,7 @@ struct lec_arp_table *entry; int i, found_entry=0; - lec_arp_lock(priv); + lec_arp_get(priv); if (ioc_data->receive == 2) { /* Vcc for Multicast Forward. No timer, LANEv2 7.1.20 and 2.3.5.3 */ @@ -1874,7 +1865,7 @@ entry = lec_arp_find(priv, bus_mac); if (!entry) { printk("LEC_ARP: Multicast entry not found!\n"); - lec_arp_unlock(priv); + lec_arp_put(priv); return; } memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN); @@ -1883,7 +1874,7 @@ #endif entry = make_entry(priv, bus_mac); if (entry == NULL) { - lec_arp_unlock(priv); + lec_arp_put(priv); return; } del_timer(&entry->timer); @@ -1892,7 +1883,7 @@ entry->old_recv_push = old_push; entry->next = priv->mcast_fwds; priv->mcast_fwds = entry; - lec_arp_unlock(priv); + lec_arp_put(priv); return; } else if (ioc_data->receive == 1) { /* Vcc which we don't want to make default vcc, attach it @@ -1910,7 +1901,7 @@ ioc_data->atm_addr[18],ioc_data->atm_addr[19]); entry = make_entry(priv, bus_mac); if (entry == NULL) { - lec_arp_unlock(priv); + lec_arp_put(priv); return; } memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN); @@ -1923,7 +1914,7 @@ add_timer(&entry->timer); entry->next = priv->lec_no_forward; priv->lec_no_forward = entry; - lec_arp_unlock(priv); + lec_arp_put(priv); dump_arp_table(priv); return; } @@ -1982,7 +1973,7 @@ } } if (found_entry) { - lec_arp_unlock(priv); + lec_arp_put(priv); DPRINTK("After vcc was added\n"); dump_arp_table(priv); return; @@ -1991,7 +1982,7 @@ this vcc */ entry = make_entry(priv, bus_mac); if (!entry) { - lec_arp_unlock(priv); + lec_arp_put(priv); return; } entry->vcc = vcc; @@ -2004,7 +1995,7 @@ entry->timer.expires = jiffies + priv->vcc_timeout_period; entry->timer.function = lec_arp_expire_vcc; add_timer(&entry->timer); - lec_arp_unlock(priv); + lec_arp_put(priv); DPRINTK("After vcc was added\n"); dump_arp_table(priv); } @@ -2020,6 +2011,10 @@ for (entry=priv->lec_arp_tables[i];entry;entry=entry->next) { if (entry->flush_tran_id == tran_id && entry->status == ESI_FLUSH_PENDING) { + struct sk_buff *skb; + + while ((skb = skb_dequeue(&entry->tx_wait))) + lec_send(entry->vcc, skb, entry->priv); entry->status = ESI_FORWARD_DIRECT; DPRINTK("LEC_ARP: Flushed\n"); } @@ -2050,10 +2045,10 @@ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; struct lec_arp_table *to_add; - lec_arp_lock(priv); + lec_arp_get(priv); to_add = make_entry(priv, mac_addr); if (!to_add) { - lec_arp_unlock(priv); + lec_arp_put(priv); return -ENOMEM; } memcpy(to_add->atm_addr, vcc->remote.sas_addr.prv, ATM_ESA_LEN); @@ -2063,8 +2058,8 @@ to_add->old_push = vcc->push; vcc->push = lec_push; priv->mcast_vcc = vcc; - lec_arp_put(priv->lec_arp_tables, to_add); - lec_arp_unlock(priv); + lec_arp_add(priv->lec_arp_tables, to_add); + lec_arp_put(priv); return 0; } @@ -2076,7 +2071,7 @@ DPRINTK("LEC_ARP: lec_vcc_close vpi:%d vci:%d\n",vcc->vpi,vcc->vci); dump_arp_table(priv); - lec_arp_lock(priv); + lec_arp_get(priv); for(i=0;ilec_arp_tables[i];entry; entry=next) { next = entry->next; @@ -2138,7 +2133,7 @@ entry = next; } - lec_arp_unlock(priv); + lec_arp_put(priv); dump_arp_table(priv); } @@ -2146,9 +2141,9 @@ lec_arp_check_empties(struct lec_priv *priv, struct atm_vcc *vcc, struct sk_buff *skb) { + unsigned long flags; struct lec_arp_table *entry, *prev; struct lecdatahdr_8023 *hdr = (struct lecdatahdr_8023 *)skb->data; - unsigned long flags; unsigned char *src; #ifdef CONFIG_TR struct lecdatahdr_8025 *tr_hdr = (struct lecdatahdr_8025 *)skb->data; @@ -2158,50 +2153,49 @@ #endif src = hdr->h_source; - lec_arp_lock(priv); + lec_arp_get(priv); entry = priv->lec_arp_empty_ones; if (vcc == entry->vcc) { - save_flags(flags); - cli(); + spin_lock_irqsave(&lec_arp_spinlock, flags); del_timer(&entry->timer); memcpy(entry->mac_addr, src, ETH_ALEN); entry->status = ESI_FORWARD_DIRECT; entry->last_used = jiffies; priv->lec_arp_empty_ones = entry->next; - restore_flags(flags); + spin_unlock_irqrestore(&lec_arp_spinlock, flags); /* We might have got an entry */ if ((prev=lec_arp_find(priv,src))) { lec_arp_remove(priv->lec_arp_tables, prev); kfree(prev); } - lec_arp_put(priv->lec_arp_tables, entry); - lec_arp_unlock(priv); + lec_arp_add(priv->lec_arp_tables, entry); + lec_arp_put(priv); return; } + spin_lock_irqsave(&lec_arp_spinlock, flags); prev = entry; entry = entry->next; while (entry && entry->vcc != vcc) { - prev= entry; + prev = entry; entry = entry->next; } if (!entry) { DPRINTK("LEC_ARP: Arp_check_empties: entry not found!\n"); - lec_arp_unlock(priv); + lec_arp_put(priv); + spin_unlock_irqrestore(&lec_arp_spinlock, flags); return; } - save_flags(flags); - cli(); del_timer(&entry->timer); memcpy(entry->mac_addr, src, ETH_ALEN); entry->status = ESI_FORWARD_DIRECT; entry->last_used = jiffies; prev->next = entry->next; - restore_flags(flags); + spin_unlock_irqrestore(&lec_arp_spinlock, flags); if ((prev = lec_arp_find(priv, src))) { lec_arp_remove(priv->lec_arp_tables,prev); kfree(prev); } - lec_arp_put(priv->lec_arp_tables,entry); - lec_arp_unlock(priv); + lec_arp_add(priv->lec_arp_tables,entry); + lec_arp_put(priv); } MODULE_LICENSE("GPL"); diff -urN linux-2.4.20/net/atm/lec.h linux-2.4.20-atm/net/atm/lec.h --- linux-2.4.20/net/atm/lec.h Wed May 28 01:54:25 2003 +++ linux-2.4.20-atm/net/atm/lec.h Wed May 28 01:58:41 2003 @@ -65,6 +65,7 @@ int (*mcast_attach)(struct atm_vcc *vcc, int arg); int (*vcc_attach)(struct atm_vcc *vcc, void *arg); struct net_device **(*get_lecs)(void); + struct module *owner; }; /* @@ -101,7 +102,7 @@ establishes multiple Multicast Forward VCCs to us. This list collects all those VCCs. LANEv1 client has only one item in this list. These entries are not aged out. */ - atomic_t lec_arp_lock_var; + atomic_t lec_arp_users; struct atm_vcc *mcast_vcc; /* Default Multicast Send VCC */ struct atm_vcc *lecd; struct timer_list lec_arp_timer; @@ -155,7 +156,5 @@ unsigned char *atm_addr, struct sk_buff *data); void lec_push(struct atm_vcc *vcc, struct sk_buff *skb); -void atm_lane_init(void); -void atm_lane_init_ops(struct atm_lane_ops *ops); #endif /* _LEC_H_ */ diff -urN linux-2.4.20/net/atm/mpc.c linux-2.4.20-atm/net/atm/mpc.c --- linux-2.4.20/net/atm/mpc.c Wed May 28 01:54:25 2003 +++ linux-2.4.20-atm/net/atm/mpc.c Wed May 28 02:14:40 2003 @@ -28,7 +28,7 @@ #include "lec.h" #include "mpc.h" -#include "resources.h" /* for bind_vcc() */ +#include "resources.h" /* * mpc.c: Implementation of MPOA client kernel part @@ -521,7 +521,6 @@ } atomic_add(skb->truesize, &entry->shortcut->sk->wmem_alloc); - ATM_SKB(skb)->iovcnt = 0; /* just to be safe ... */ ATM_SKB(skb)->atm_options = entry->shortcut->atm_options; entry->shortcut->send(entry->shortcut, skb); entry->packets_fwded++; @@ -730,6 +729,7 @@ eg->packets_rcvd++; mpc->eg_ops->put(eg); + memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data)); netif_rx(new_skb); return; @@ -741,18 +741,10 @@ }; static struct atm_dev mpc_dev = { - &mpc_ops, /* device operations */ - NULL, /* PHY operations */ - "mpc", /* device type name */ - 42, /* device index (dummy) */ - NULL, /* VCC table */ - NULL, /* last VCC */ - NULL, /* per-device data */ - NULL, /* private PHY data */ - { 0 }, /* device flags */ - NULL, /* local ATM address */ - { 0 } /* no ESI */ - /* rest of the members will be 0 */ + .ops = &mpc_ops, + .type = "mpc", + .number = 42, + .lock = SPIN_LOCK_UNLOCKED }; int atm_mpoa_mpoad_attach (struct atm_vcc *vcc, int arg) @@ -791,7 +783,7 @@ } mpc->mpoad_vcc = vcc; - bind_vcc(vcc, &mpc_dev); + vcc_insert_socket(&mpc_dev, vcc->sk); set_bit(ATM_VF_META,&vcc->flags); set_bit(ATM_VF_READY,&vcc->flags); @@ -807,7 +799,6 @@ send_set_mps_ctrl_addr(mpc->mps_ctrl_addr, mpc); } - MOD_INC_USE_COUNT; return arg; } @@ -856,7 +847,7 @@ printk("mpoa: (%s) going down\n", (mpc->dev) ? mpc->dev->name : ""); - MOD_DEC_USE_COUNT; + __MOD_DEC_USE_COUNT(THIS_MODULE); return; } @@ -869,7 +860,7 @@ struct mpoa_client *mpc = find_mpc_by_vcc(vcc); struct k_message *mesg = (struct k_message*)skb->data; - atomic_sub(skb->truesize+ATM_PDU_OVHD, &vcc->sk->wmem_alloc); + atomic_sub(skb->truesize, &vcc->sk->wmem_alloc); if (mpc == NULL) { printk("mpoa: msg_from_mpoad: no mpc found\n"); @@ -1398,11 +1389,17 @@ return; } -void atm_mpoa_init_ops(struct atm_mpoa_ops *ops) +struct atm_mpoa_ops __atm_mpoa_ops = { + .mpoad_attach = atm_mpoa_mpoad_attach, + .vcc_attach = atm_mpoa_vcc_attach, + .owner = THIS_MODULE +}; + +static int __init atm_mpoa_init(void) { - ops->mpoad_attach = atm_mpoa_mpoad_attach; - ops->vcc_attach = atm_mpoa_vcc_attach; + extern struct atm_mpoa_ops *atm_mpoa_ops; + atm_mpoa_ops = &__atm_mpoa_ops; #ifdef CONFIG_PROC_FS if(mpc_proc_init() != 0) printk(KERN_INFO "mpoa: failed to initialize /proc/mpoa\n"); @@ -1412,22 +1409,12 @@ printk("mpc.c: " __DATE__ " " __TIME__ " initialized\n"); - return; -} - -#ifdef MODULE -int init_module(void) -{ - extern struct atm_mpoa_ops atm_mpoa_ops; - - atm_mpoa_init_ops(&atm_mpoa_ops); - return 0; } -void cleanup_module(void) +static void __exit atm_mpoa_cleanup(void) { - extern struct atm_mpoa_ops atm_mpoa_ops; + extern struct atm_mpoa_ops *atm_mpoa_ops; struct mpoa_client *mpc, *tmp; struct atm_mpoa_qos *qos, *nextqos; struct lec_priv *priv; @@ -1442,8 +1429,7 @@ del_timer(&mpc_timer); unregister_netdevice_notifier(&mpoa_notifier); - atm_mpoa_ops.mpoad_attach = NULL; - atm_mpoa_ops.vcc_attach = NULL; + atm_mpoa_ops = NULL; mpc = mpcs; mpcs = NULL; @@ -1475,8 +1461,9 @@ kfree(qos); qos = nextqos; } - - return; } -#endif /* MODULE */ + +module_init(atm_mpoa_init); +module_exit(atm_mpoa_cleanup); + MODULE_LICENSE("GPL"); diff -urN linux-2.4.20/net/atm/mpc.h linux-2.4.20-atm/net/atm/mpc.h --- linux-2.4.20/net/atm/mpc.h Mon Dec 11 22:33:43 2000 +++ linux-2.4.20-atm/net/atm/mpc.h Wed May 28 01:58:41 2003 @@ -48,11 +48,8 @@ struct atm_mpoa_ops { int (*mpoad_attach)(struct atm_vcc *vcc, int arg); /* attach mpoa daemon */ int (*vcc_attach)(struct atm_vcc *vcc, long arg); /* attach shortcut vcc */ + struct module *owner; }; - -/* Boot/module initialization function */ -void atm_mpoa_init(void); -void atm_mpoa_init_ops(struct atm_mpoa_ops *ops); /* MPOA QoS operations */ struct atm_mpoa_qos *atm_mpoa_add_qos(uint32_t dst_ip, struct atm_qos *qos); diff -urN linux-2.4.20/net/atm/pppoatm.c linux-2.4.20-atm/net/atm/pppoatm.c --- linux-2.4.20/net/atm/pppoatm.c Wed May 28 01:54:25 2003 +++ linux-2.4.20-atm/net/atm/pppoatm.c Wed May 28 02:15:02 2003 @@ -232,7 +232,6 @@ return 1; } atomic_add(skb->truesize, &ATM_SKB(skb)->vcc->sk->wmem_alloc); - ATM_SKB(skb)->iovcnt = 0; ATM_SKB(skb)->atm_options = ATM_SKB(skb)->vcc->atm_options; DPRINTK("(unit %d): atm_skb(%p)->vcc(%p)->dev(%p)\n", pvcc->chan.unit, skb, ATM_SKB(skb)->vcc, diff -urN linux-2.4.20/net/atm/proc.c linux-2.4.20-atm/net/atm/proc.c --- linux-2.4.20/net/atm/proc.c Wed May 28 01:54:25 2003 +++ linux-2.4.20-atm/net/atm/proc.c Wed May 28 02:46:02 2003 @@ -39,16 +39,15 @@ #include "common.h" /* atm_proc_init prototype */ #include "signaling.h" /* to get sigd - ugly too */ -#ifdef CONFIG_ATM_CLIP +#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE) #include -#include "ipcommon.h" -extern void clip_push(struct atm_vcc *vcc,struct sk_buff *skb); +extern struct atm_clip_ops *atm_clip_ops; /* in common.c */ #endif #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) #include "lec.h" #include "lec_arpc.h" -extern struct atm_lane_ops atm_lane_ops; /* in common.c */ +extern struct atm_lane_ops *atm_lane_ops; /* in common.c */ #endif static ssize_t proc_dev_atm_read(struct file *file,char *buf,size_t count, @@ -85,11 +84,12 @@ add_stats(buf,"0",&dev->stats.aal0); strcat(buf," "); add_stats(buf,"5",&dev->stats.aal5); + sprintf(strchr(buf,0), " %d", atomic_read(&dev->refcnt)); strcat(buf,"\n"); } -#ifdef CONFIG_ATM_CLIP +#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE) static int svc_addr(char *buf,struct sockaddr_atmsvc *addr) @@ -178,8 +178,8 @@ aal_name[vcc->qos.aal],vcc->qos.rxtp.min_pcr, class_name[vcc->qos.rxtp.traffic_class],vcc->qos.txtp.min_pcr, class_name[vcc->qos.txtp.traffic_class]); -#ifdef CONFIG_ATM_CLIP - if (vcc->push == clip_push) { +#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE) + if (atm_clip_ops && (vcc->push == atm_clip_ops->clip_push)) { struct clip_vcc *clip_vcc = CLIP_VCC(vcc); struct net_device *dev; @@ -220,10 +220,11 @@ default: here += sprintf(here,"%3d",vcc->sk->family); } - here += sprintf(here," %04lx %5d %7d/%7d %7d/%7d\n",vcc->flags.bits, + here += sprintf(here," %04lx %5d %7d/%7d %7d/%7d %d\n",vcc->flags.bits, vcc->reply, atomic_read(&vcc->sk->wmem_alloc),vcc->sk->sndbuf, - atomic_read(&vcc->sk->rmem_alloc),vcc->sk->rcvbuf); + atomic_read(&vcc->sk->rmem_alloc),vcc->sk->rcvbuf, + atomic_read(&vcc->sk->refcnt)); } @@ -311,26 +312,24 @@ "AAL(TX,err,RX,err,drop) ...\n"); } left = pos-1; + read_lock(&atm_dev_lock); list_for_each(p, &atm_devs) { dev = list_entry(p, struct atm_dev, dev_list); if (left-- == 0) { dev_info(dev,buf); + read_unlock(&atm_dev_lock); return strlen(buf); } } + read_unlock(&atm_dev_lock); return 0; } -/* - * FIXME: it isn't safe to walk the VCC list without turning off interrupts. - * What is really needed is some lock on the devices. Ditto for ATMARP. - */ static int atm_pvc_info(loff_t pos,char *buf) { - struct atm_dev *dev; - struct list_head *p; struct atm_vcc *vcc; + struct sock *s; int left; if (!pos) { @@ -338,24 +337,25 @@ "TX(PCR,Class)\n"); } left = pos-1; - list_for_each(p, &atm_devs) { - dev = list_entry(p, struct atm_dev, dev_list); - for (vcc = dev->vccs; vcc; vcc = vcc->next) - if (vcc->sk->family == PF_ATMPVC && - vcc->dev && !left--) { - pvc_info(vcc,buf); - return strlen(buf); - } + read_lock(&vcc_sklist_lock); + for(s = vcc_sklist; s; s = s->next) { + vcc = atm_sk(s); + if (vcc->sk->family == PF_ATMPVC && + vcc->dev && !left--) { + pvc_info(vcc,buf); + read_unlock(&vcc_sklist_lock); + return strlen(buf); + } } + read_unlock(&vcc_sklist_lock); return 0; } static int atm_vc_info(loff_t pos,char *buf) { - struct atm_dev *dev; - struct list_head *p; struct atm_vcc *vcc; + struct sock *s; int left; if (!pos) @@ -363,19 +363,16 @@ "Address"," Itf VPI VCI Fam Flags Reply Send buffer" " Recv buffer\n"); left = pos-1; - list_for_each(p, &atm_devs) { - dev = list_entry(p, struct atm_dev, dev_list); - for (vcc = dev->vccs; vcc; vcc = vcc->next) - if (!left--) { - vc_info(vcc,buf); - return strlen(buf); - } - } - for (vcc = nodev_vccs; vcc; vcc = vcc->next) + read_lock(&vcc_sklist_lock); + for(s = vcc_sklist; s; s = s->next) { + vcc = atm_sk(s); if (!left--) { vc_info(vcc,buf); + read_unlock(&vcc_sklist_lock); return strlen(buf); } + } + read_unlock(&vcc_sklist_lock); return 0; } @@ -383,31 +380,28 @@ static int atm_svc_info(loff_t pos,char *buf) { - struct atm_dev *dev; - struct list_head *p; struct atm_vcc *vcc; + struct sock *s; int left; if (!pos) return sprintf(buf,"Itf VPI VCI State Remote\n"); left = pos-1; - list_for_each(p, &atm_devs) { - dev = list_entry(p, struct atm_dev, dev_list); - for (vcc = dev->vccs; vcc; vcc = vcc->next) - if (vcc->sk->family == PF_ATMSVC && !left--) { - svc_info(vcc,buf); - return strlen(buf); - } - } - for (vcc = nodev_vccs; vcc; vcc = vcc->next) + read_lock(&vcc_sklist_lock); + for(s = vcc_sklist; s; s = s->next) { + vcc = atm_sk(s); if (vcc->sk->family == PF_ATMSVC && !left--) { svc_info(vcc,buf); + read_unlock(&vcc_sklist_lock); return strlen(buf); } + } + read_unlock(&vcc_sklist_lock); + return 0; } -#ifdef CONFIG_ATM_CLIP +#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE) static int atm_arp_info(loff_t pos,char *buf) { struct neighbour *n; @@ -417,28 +411,30 @@ return sprintf(buf,"IPitf TypeEncp Idle IP address " "ATM address\n"); } + if (!clip_tbl_hook) + return 0; count = pos; - read_lock_bh(&clip_tbl.lock); + read_lock_bh(&clip_tbl_hook->lock); for (i = 0; i <= NEIGH_HASHMASK; i++) - for (n = clip_tbl.hash_buckets[i]; n; n = n->next) { + for (n = clip_tbl_hook->hash_buckets[i]; n; n = n->next) { struct atmarp_entry *entry = NEIGH2ENTRY(n); struct clip_vcc *vcc; if (!entry->vccs) { if (--count) continue; atmarp_info(n->dev,entry,NULL,buf); - read_unlock_bh(&clip_tbl.lock); + read_unlock_bh(&clip_tbl_hook->lock); return strlen(buf); } for (vcc = entry->vccs; vcc; vcc = vcc->next) { if (--count) continue; atmarp_info(n->dev,entry,vcc,buf); - read_unlock_bh(&clip_tbl.lock); + read_unlock_bh(&clip_tbl_hook->lock); return strlen(buf); } } - read_unlock_bh(&clip_tbl.lock); + read_unlock_bh(&clip_tbl_hook->lock); return 0; } #endif @@ -456,10 +452,13 @@ " Status Flags " "VPI/VCI Recv VPI/VCI\n"); } - if (atm_lane_ops.get_lecs == NULL) + if (!atm_lane_ops) return 0; /* the lane module is not there yet */ - else - dev_lec = atm_lane_ops.get_lecs(); + + if (!try_inc_mod_count(atm_lane_ops->owner)) + return 0; + + dev_lec = atm_lane_ops->get_lecs(); count = pos; for(d=0;dname); lec_info(entry,buf+e); + __MOD_DEC_USE_COUNT(atm_lane_ops->owner); return strlen(buf); } } @@ -480,6 +480,7 @@ if (--count) continue; e=sprintf(buf,"%s ",dev_lec[d]->name); lec_info(entry, buf+e); + __MOD_DEC_USE_COUNT(atm_lane_ops->owner); return strlen(buf); } for(entry=priv->lec_no_forward; entry; @@ -487,6 +488,7 @@ if (--count) continue; e=sprintf(buf,"%s ",dev_lec[d]->name); lec_info(entry, buf+e); + __MOD_DEC_USE_COUNT(atm_lane_ops->owner); return strlen(buf); } for(entry=priv->mcast_fwds; entry; @@ -494,9 +496,11 @@ if (--count) continue; e=sprintf(buf,"%s ",dev_lec[d]->name); lec_info(entry, buf+e); + __MOD_DEC_USE_COUNT(atm_lane_ops->owner); return strlen(buf); } } + __MOD_DEC_USE_COUNT(atm_lane_ops->owner); return 0; } #endif @@ -566,7 +570,7 @@ for (num = dev->number; num; num /= 10) digits++; if (!digits) digits++; - dev->proc_name = kmalloc(strlen(dev->type) + digits + 2, GFP_ATOMIC); + dev->proc_name = kmalloc(strlen(dev->type) + digits + 2, GFP_KERNEL); if (!dev->proc_name) goto fail1; sprintf(dev->proc_name,"%s:%d",dev->type, dev->number); @@ -599,12 +603,11 @@ name->proc_fops = &proc_spec_atm_operations; \ name->owner = THIS_MODULE +struct proc_dir_entry *devices = NULL, *pvc = NULL, *svc = NULL; +struct proc_dir_entry *arp = NULL, *lec = NULL, *vc = NULL; int __init atm_proc_init(void) { - struct proc_dir_entry *devices = NULL,*pvc = NULL,*svc = NULL; - struct proc_dir_entry *arp = NULL,*lec = NULL,*vc = NULL; - atm_proc_root = proc_mkdir("net/atm",NULL); if (!atm_proc_root) return -ENOMEM; @@ -612,7 +615,7 @@ CREATE_ENTRY(pvc); CREATE_ENTRY(svc); CREATE_ENTRY(vc); -#ifdef CONFIG_ATM_CLIP +#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE) CREATE_ENTRY(arp); #endif #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) @@ -629,4 +632,15 @@ if (vc) remove_proc_entry("vc",atm_proc_root); remove_proc_entry("net/atm",NULL); return -ENOMEM; +} + +void __exit atm_proc_exit(void) +{ + if (vc) remove_proc_entry("vc",atm_proc_root); + if (lec) remove_proc_entry("lec",atm_proc_root); + if (arp) remove_proc_entry("arp",atm_proc_root); + if (svc) remove_proc_entry("svc",atm_proc_root); + if (pvc) remove_proc_entry("pvc",atm_proc_root); + if (devices) remove_proc_entry("devices",atm_proc_root); + remove_proc_entry("net/atm",NULL); } diff -urN linux-2.4.20/net/atm/pvc.c linux-2.4.20-atm/net/atm/pvc.c --- linux-2.4.20/net/atm/pvc.c Thu Apr 12 21:11:39 2001 +++ linux-2.4.20-atm/net/atm/pvc.c Wed May 28 01:58:41 2003 @@ -15,17 +15,10 @@ #include #include #include /* for sock_no_* */ -#ifdef CONFIG_ATM_CLIP -#include -#endif #include "resources.h" /* devs and vccs */ #include "common.h" /* common for PVCs and SVCs */ -#ifndef NULL -#define NULL 0 -#endif - static int pvc_shutdown(struct socket *sock,int how) { @@ -77,45 +70,38 @@ } -static struct proto_ops SOCKOPS_WRAPPED(pvc_proto_ops) = { - family: PF_ATMPVC, +static struct proto_ops pvc_proto_ops = { + .family = PF_ATMPVC, - release: atm_release, - bind: pvc_bind, - connect: pvc_connect, - socketpair: sock_no_socketpair, - accept: sock_no_accept, - getname: pvc_getname, - poll: atm_poll, - ioctl: atm_ioctl, - listen: sock_no_listen, - shutdown: pvc_shutdown, - setsockopt: atm_setsockopt, - getsockopt: atm_getsockopt, - sendmsg: atm_sendmsg, - recvmsg: atm_recvmsg, - mmap: sock_no_mmap, - sendpage: sock_no_sendpage, + .release = vcc_release, + .bind = pvc_bind, + .connect = pvc_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = pvc_getname, + .poll = atm_poll, + .ioctl = atm_ioctl, + .listen = sock_no_listen, + .shutdown = pvc_shutdown, + .setsockopt = atm_setsockopt, + .getsockopt = atm_getsockopt, + .sendmsg = atm_sendmsg, + .recvmsg = atm_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, }; -#include -SOCKOPS_WRAP(pvc_proto, PF_ATMPVC); - - static int pvc_create(struct socket *sock,int protocol) { sock->ops = &pvc_proto_ops; - return atm_create(sock,protocol,PF_ATMPVC); + return vcc_create(sock,protocol,PF_ATMPVC); } static struct net_proto_family pvc_family_ops = { - PF_ATMPVC, - pvc_create, - 0, /* no authentication */ - 0, /* no encryption */ - 0 /* no encrypt_net */ + .family = PF_ATMPVC, + .create = pvc_create, }; @@ -124,23 +110,20 @@ */ -static int __init atmpvc_init(void) +int __init atmpvc_init(void) { int error; error = sock_register(&pvc_family_ops); if (error < 0) { - printk(KERN_ERR "ATMPVC: can't register (%d)",error); + printk(KERN_ERR "ATMPVC: can't register (%d)", error); return error; } -#ifdef CONFIG_ATM_CLIP - atm_clip_init(); -#endif -#ifdef CONFIG_PROC_FS - error = atm_proc_init(); - if (error) printk("atm_proc_init fails with %d\n",error); -#endif - return 0; + + return error; } -module_init(atmpvc_init); +void __exit atmpvc_exit(void) +{ + sock_unregister(PF_ATMPVC); +} diff -urN linux-2.4.20/net/atm/raw.c linux-2.4.20-atm/net/atm/raw.c --- linux-2.4.20/net/atm/raw.c Wed May 28 01:54:25 2003 +++ linux-2.4.20-atm/net/atm/raw.c Wed May 28 01:58:41 2003 @@ -37,7 +37,7 @@ static void atm_pop_raw(struct atm_vcc *vcc,struct sk_buff *skb) { DPRINTK("APopR (%d) %d -= %d\n",vcc->vci,vcc->sk->wmem_alloc,skb->truesize); - atomic_sub(skb->truesize+ATM_PDU_OVHD,&vcc->sk->wmem_alloc); + atomic_sub(skb->truesize,&vcc->sk->wmem_alloc); dev_kfree_skb_any(skb); wake_up(&vcc->sleep); } diff -urN linux-2.4.20/net/atm/resources.c linux-2.4.20-atm/net/atm/resources.c --- linux-2.4.20/net/atm/resources.c Wed May 28 01:54:25 2003 +++ linux-2.4.20-atm/net/atm/resources.c Wed May 28 02:38:44 2003 @@ -2,7 +2,6 @@ /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - #include #include #include @@ -11,198 +10,185 @@ #include #include #include /* for struct sock */ -#include /* for get_fs_long and put_fs_long */ #include "common.h" #include "resources.h" - -#ifndef NULL -#define NULL 0 -#endif - +struct sock *vcc_sklist = NULL; +rwlock_t vcc_sklist_lock = RW_LOCK_UNLOCKED; LIST_HEAD(atm_devs); -struct atm_vcc *nodev_vccs = NULL; -extern spinlock_t atm_dev_lock; +rwlock_t atm_dev_lock = RW_LOCK_UNLOCKED; -static struct atm_dev *alloc_atm_dev(const char *type) +static struct atm_dev *__alloc_atm_dev(const char *type) { struct atm_dev *dev; dev = kmalloc(sizeof(*dev), GFP_ATOMIC); - if (!dev) return NULL; - memset(dev,0,sizeof(*dev)); + if (!dev) + return NULL; + memset(dev, 0, sizeof(*dev)); dev->type = type; dev->signal = ATM_PHY_SIG_UNKNOWN; dev->link_rate = ATM_OC3_PCR; - list_add_tail(&dev->dev_list, &atm_devs); + dev->dev_data = NULL; + memset(&dev->stats, 0, sizeof(dev->stats)); + atomic_set(&dev->refcnt, 0); + spin_lock_init(&dev->lock); return dev; } -static void free_atm_dev(struct atm_dev *dev) +static void __free_atm_dev(struct atm_dev *dev) { - list_del(&dev->dev_list); kfree(dev); } -struct atm_dev *atm_find_dev(int number) +static struct atm_dev *__atm_dev_lookup(int number) { struct atm_dev *dev; struct list_head *p; list_for_each(p, &atm_devs) { dev = list_entry(p, struct atm_dev, dev_list); - if (dev->ops && dev->number == number) + if (dev->ops && dev->number == number) { + atm_dev_hold(dev); return dev; + } } return NULL; } -struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops, - int number,atm_dev_flags_t *flags) +struct atm_dev *atm_dev_lookup(int number) { - struct atm_dev *dev = NULL; + struct atm_dev *dev; + + read_lock(&atm_dev_lock); + dev = __atm_dev_lookup(number); + read_unlock(&atm_dev_lock); + return dev; +} - spin_lock(&atm_dev_lock); +void atm_dev_hold(struct atm_dev *dev) +{ + atomic_inc(&dev->refcnt); +} - dev = alloc_atm_dev(type); +void atm_dev_release(struct atm_dev *dev) +{ + atomic_dec(&dev->refcnt); + + if ((atomic_read(&dev->refcnt) == 1) && + test_bit(ATM_DF_CLOSE,&dev->flags)) + shutdown_atm_dev(dev); +} + + +struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops, + int number, atm_dev_flags_t *flags) +{ + struct atm_dev *dev, *inuse; + + dev = __alloc_atm_dev(type); if (!dev) { printk(KERN_ERR "atm_dev_register: no space for dev %s\n", type); - goto done; + return NULL; } + write_lock(&atm_dev_lock); if (number != -1) { - if (atm_find_dev(number)) { - free_atm_dev(dev); + if ((inuse = __atm_dev_lookup(number))) { + atm_dev_release(inuse); + __free_atm_dev(dev); + write_unlock(&atm_dev_lock); return NULL; } dev->number = number; } else { dev->number = 0; - while (atm_find_dev(dev->number)) dev->number++; + while ((inuse = __atm_dev_lookup(dev->number))) { + atm_dev_release(inuse); + dev->number++; + } } - dev->vccs = dev->last = NULL; - dev->dev_data = NULL; - barrier(); + dev->ops = ops; - if (flags) + if (flags) dev->flags = *flags; - else - memset(&dev->flags,0,sizeof(dev->flags)); - memset((void *) &dev->stats,0,sizeof(dev->stats)); + else + memset(&dev->flags, 0, sizeof(dev->flags)); + atomic_inc(&dev->refcnt); + list_add_tail(&dev->dev_list, &atm_devs); + write_unlock(&atm_dev_lock); + barrier(); + #ifdef CONFIG_PROC_FS - if (ops->proc_read) + if (ops->proc_read) { if (atm_proc_dev_register(dev) < 0) { printk(KERN_ERR "atm_dev_register: " - "atm_proc_dev_register failed for dev %s\n",type); - free_atm_dev(dev); - goto done; + "atm_proc_dev_register failed for dev %s\n", + type); + write_lock(&atm_dev_lock); + list_del(&dev->dev_list); + write_unlock(&atm_dev_lock); + __free_atm_dev(dev); + return NULL; } + } #endif -done: - spin_unlock(&atm_dev_lock); return dev; } void atm_dev_deregister(struct atm_dev *dev) { + unsigned long warning_time; + #ifdef CONFIG_PROC_FS - if (dev->ops->proc_read) atm_proc_dev_deregister(dev); + if (dev->ops->proc_read) + atm_proc_dev_deregister(dev); #endif - spin_lock(&atm_dev_lock); - free_atm_dev(dev); - spin_unlock(&atm_dev_lock); -} - -void shutdown_atm_dev(struct atm_dev *dev) -{ - if (dev->vccs) { - set_bit(ATM_DF_CLOSE,&dev->flags); - return; - } - if (dev->ops->dev_close) dev->ops->dev_close(dev); - atm_dev_deregister(dev); -} - - -/* Handler for sk->destruct, invoked by sk_free() */ -static void atm_free_sock(struct sock *sk) -{ - kfree(sk->protinfo.af_atm); -} - -struct sock *alloc_atm_vcc_sk(int family) -{ - struct sock *sk; - struct atm_vcc *vcc; - - sk = sk_alloc(family, GFP_KERNEL, 1); - if (!sk) return NULL; - vcc = sk->protinfo.af_atm = kmalloc(sizeof(*vcc),GFP_KERNEL); - if (!vcc) { - sk_free(sk); - return NULL; - } - sock_init_data(NULL,sk); - sk->destruct = atm_free_sock; - memset(vcc,0,sizeof(*vcc)); - vcc->sk = sk; - if (nodev_vccs) nodev_vccs->prev = vcc; - vcc->prev = NULL; - vcc->next = nodev_vccs; - nodev_vccs = vcc; - return sk; -} - - -static void unlink_vcc(struct atm_vcc *vcc,struct atm_dev *hold_dev) -{ - if (vcc->prev) vcc->prev->next = vcc->next; - else if (vcc->dev) vcc->dev->vccs = vcc->next; - else nodev_vccs = vcc->next; - if (vcc->next) vcc->next->prev = vcc->prev; - else if (vcc->dev) vcc->dev->last = vcc->prev; - if (vcc->dev && vcc->dev != hold_dev && !vcc->dev->vccs && - test_bit(ATM_DF_CLOSE,&vcc->dev->flags)) - shutdown_atm_dev(vcc->dev); -} + write_lock(&atm_dev_lock); + list_del(&dev->dev_list); + write_unlock(&atm_dev_lock); + warning_time = jiffies; + while (atomic_read(&dev->refcnt) != 1) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ / 4); + current->state = TASK_RUNNING; + if ((jiffies - warning_time) > 10 * HZ) { + printk(KERN_EMERG "atm_dev_deregister: waiting for " + "dev %d to become free. Usage count = %d\n", + dev->number, atomic_read(&dev->refcnt)); + warning_time = jiffies; + } + } -void free_atm_vcc_sk(struct sock *sk) -{ - unlink_vcc(sk->protinfo.af_atm,NULL); - sk_free(sk); + __free_atm_dev(dev); } -void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev) +void shutdown_atm_dev(struct atm_dev *dev) { - unlink_vcc(vcc,dev); - vcc->dev = dev; - if (dev) { - vcc->next = NULL; - vcc->prev = dev->last; - if (dev->vccs) dev->last->next = vcc; - else dev->vccs = vcc; - dev->last = vcc; - } - else { - if (nodev_vccs) nodev_vccs->prev = vcc; - vcc->next = nodev_vccs; - vcc->prev = NULL; - nodev_vccs = vcc; + if (atomic_read(&dev->refcnt) > 1) { + set_bit(ATM_DF_CLOSE, &dev->flags); + return; } + if (dev->ops->dev_close) + dev->ops->dev_close(dev); + atm_dev_deregister(dev); } EXPORT_SYMBOL(atm_dev_register); EXPORT_SYMBOL(atm_dev_deregister); -EXPORT_SYMBOL(atm_find_dev); +EXPORT_SYMBOL(atm_dev_lookup); +EXPORT_SYMBOL(atm_dev_hold); +EXPORT_SYMBOL(atm_dev_release); EXPORT_SYMBOL(shutdown_atm_dev); -EXPORT_SYMBOL(bind_vcc); +EXPORT_SYMBOL(vcc_sklist); +EXPORT_SYMBOL(vcc_sklist_lock); diff -urN linux-2.4.20/net/atm/resources.h linux-2.4.20-atm/net/atm/resources.h --- linux-2.4.20/net/atm/resources.h Wed May 28 01:54:25 2003 +++ linux-2.4.20-atm/net/atm/resources.h Wed May 28 02:23:53 2003 @@ -11,11 +11,7 @@ extern struct list_head atm_devs; -extern struct atm_vcc *nodev_vccs; /* VCCs not linked to any device */ - - -struct sock *alloc_atm_vcc_sk(int family); -void free_atm_vcc_sk(struct sock *sk); +extern rwlock_t atm_dev_lock; #ifdef CONFIG_PROC_FS diff -urN linux-2.4.20/net/atm/signaling.c linux-2.4.20-atm/net/atm/signaling.c --- linux-2.4.20/net/atm/signaling.c Wed May 28 01:54:25 2003 +++ linux-2.4.20-atm/net/atm/signaling.c Wed May 28 02:28:19 2003 @@ -33,7 +33,6 @@ struct atm_vcc *sigd = NULL; static DECLARE_WAIT_QUEUE_HEAD(sigd_sleep); -extern spinlock_t atm_dev_lock; static void sigd_put_skb(struct sk_buff *skb) { @@ -98,7 +97,7 @@ struct atm_vcc *session_vcc; msg = (struct atmsvc_msg *) skb->data; - atomic_sub(skb->truesize+ATM_PDU_OVHD,&vcc->sk->wmem_alloc); + atomic_sub(skb->truesize,&vcc->sk->wmem_alloc); DPRINTK("sigd_send %d (0x%lx)\n",(int) msg->type, (unsigned long) msg->vcc); vcc = *(struct atm_vcc **) &msg->vcc; @@ -115,7 +114,7 @@ } session_vcc = vcc->session ? vcc->session : vcc; if (session_vcc->vpi || session_vcc->vci) break; - session_vcc->itf = msg->pvc.sap_addr.itf; + session_vcc->sk->bound_dev_if = msg->pvc.sap_addr.itf; session_vcc->vpi = msg->pvc.sap_addr.vpi; session_vcc->vci = msg->pvc.sap_addr.vci; if (session_vcc->vpi || session_vcc->vci) @@ -129,12 +128,12 @@ case as_indicate: vcc = *(struct atm_vcc **) &msg->listen_vcc; DPRINTK("as_indicate!!!\n"); - if (!vcc->backlog_quota) { + if (vcc->sk->ack_backlog == vcc->sk->max_ack_backlog) { sigd_enq(0,as_reject,vcc,NULL,NULL); return 0; } - vcc->backlog_quota--; - skb_queue_tail(&vcc->listenq,skb); + vcc->sk->ack_backlog++; + skb_queue_tail(&vcc->sk->receive_queue,skb); if (vcc->callback) { DPRINTK("waking vcc->sleep 0x%p\n", &vcc->sleep); @@ -195,16 +194,13 @@ } -static void purge_vccs(struct atm_vcc *vcc) +static void purge_vcc(struct atm_vcc *vcc) { - while (vcc) { - if (vcc->sk->family == PF_ATMSVC && - !test_bit(ATM_VF_META,&vcc->flags)) { - set_bit(ATM_VF_RELEASED,&vcc->flags); - vcc->reply = -EUNATCH; - wake_up(&vcc->sleep); - } - vcc = vcc->next; + if (vcc->sk->family == PF_ATMSVC && + !test_bit(ATM_VF_META,&vcc->flags)) { + set_bit(ATM_VF_RELEASED,&vcc->flags); + vcc->reply = -EUNATCH; + wake_up(&vcc->sleep); } } @@ -213,39 +209,42 @@ { struct atm_dev *dev; struct list_head *p; + struct sock *s; DPRINTK("sigd_close\n"); sigd = NULL; if (skb_peek(&vcc->sk->receive_queue)) printk(KERN_ERR "sigd_close: closing with requests pending\n"); skb_queue_purge(&vcc->sk->receive_queue); - purge_vccs(nodev_vccs); - spin_lock (&atm_dev_lock); + read_lock(&atm_dev_lock); list_for_each(p, &atm_devs) { dev = list_entry(p, struct atm_dev, dev_list); - purge_vccs(dev->vccs); + + read_lock(&vcc_sklist_lock); + for(s = vcc_sklist; s; s = s->next) { + struct atm_vcc *vcc = atm_sk(s); + + if (vcc->dev == dev) purge_vcc(vcc); + } + read_unlock(&vcc_sklist_lock); } - spin_unlock (&atm_dev_lock); + read_unlock(&atm_dev_lock); } static struct atmdev_ops sigd_dev_ops = { - close: sigd_close, - send: sigd_send + .close = sigd_close, + .send = sigd_send }; static struct atm_dev sigd_dev = { - &sigd_dev_ops, - NULL, /* no PHY */ - "sig", /* type */ - 999, /* dummy device number */ - NULL,NULL, /* pretend not to have any VCCs */ - NULL,NULL, /* no data */ - { 0 }, /* no flags */ - NULL, /* no local address */ - { 0 } /* no ESI, no statistics */ + .ops = &sigd_dev_ops, + .phy = NULL, + .type = "sig", + .number = 999, + .lock = SPIN_LOCK_UNLOCKED }; @@ -254,7 +253,7 @@ if (sigd) return -EADDRINUSE; DPRINTK("sigd_attach\n"); sigd = vcc; - bind_vcc(vcc,&sigd_dev); + vcc_insert_socket(&sigd_dev, vcc->sk); set_bit(ATM_VF_META,&vcc->flags); set_bit(ATM_VF_READY,&vcc->flags); wake_up(&sigd_sleep); diff -urN linux-2.4.20/net/atm/svc.c linux-2.4.20-atm/net/atm/svc.c --- linux-2.4.20/net/atm/svc.c Thu Apr 12 21:11:39 2001 +++ linux-2.4.20-atm/net/atm/svc.c Wed May 28 01:58:41 2003 @@ -65,8 +65,8 @@ DPRINTK("svc_disconnect %p\n",vcc); if (test_bit(ATM_VF_REGIS,&vcc->flags)) { - sigd_enq(vcc,as_close,NULL,NULL,NULL); add_wait_queue(&vcc->sleep,&wait); + sigd_enq(vcc,as_close,NULL,NULL,NULL); while (!test_bit(ATM_VF_RELEASED,&vcc->flags) && sigd) { set_current_state(TASK_UNINTERRUPTIBLE); schedule(); @@ -75,7 +75,7 @@ } /* beware - socket is still in use by atmsigd until the last as_indicate has been answered */ - while ((skb = skb_dequeue(&vcc->listenq))) { + while ((skb = skb_dequeue(&vcc->sk->receive_queue))) { DPRINTK("LISTEN REL\n"); sigd_enq2(NULL,as_reject,vcc,NULL,NULL,&vcc->qos,0); dev_kfree_skb(skb); @@ -90,17 +90,19 @@ static int svc_release(struct socket *sock) { struct atm_vcc *vcc; + struct sock *sk = sock->sk; - if (!sock->sk) return 0; - vcc = ATM_SD(sock); - DPRINTK("svc_release %p\n",vcc); - clear_bit(ATM_VF_READY,&vcc->flags); - atm_release_vcc_sk(sock->sk,0); - svc_disconnect(vcc); - /* VCC pointer is used as a reference, so we must not free it - (thereby subjecting it to re-use) before all pending connections - are closed */ - free_atm_vcc_sk(sock->sk); + if (sk) { + vcc = atm_sk(sk); + DPRINTK("svc_release %p\n",vcc); + sock_hold(sk); + vcc_release(sock); + svc_disconnect(vcc); + /* VCC pointer is used as a reference, so we must not free it + (thereby subjecting it to re-use) before all pending + connections are closed */ + sock_put(sk); + } return 0; } @@ -125,8 +127,8 @@ if (!test_bit(ATM_VF_HASQOS,&vcc->flags)) return -EBADFD; vcc->local = *addr; vcc->reply = WAITING; - sigd_enq(vcc,as_bind,NULL,NULL,&vcc->local); add_wait_queue(&vcc->sleep,&wait); + sigd_enq(vcc,as_bind,NULL,NULL,&vcc->local); while (vcc->reply == WAITING && sigd) { set_current_state(TASK_UNINTERRUPTIBLE); schedule(); @@ -169,13 +171,14 @@ if (!vcc->qos.txtp.traffic_class && !vcc->qos.rxtp.traffic_class) return -EINVAL; vcc->remote = *addr; + add_wait_queue(&vcc->sleep,&wait); vcc->reply = WAITING; sigd_enq(vcc,as_connect,NULL,NULL,&vcc->remote); if (flags & O_NONBLOCK) { + remove_wait_queue(&vcc->sleep,&wait); sock->state = SS_CONNECTING; return -EINPROGRESS; } - add_wait_queue(&vcc->sleep,&wait); error = 0; while (vcc->reply == WAITING && sigd) { set_current_state(TASK_INTERRUPTIBLE); @@ -228,7 +231,7 @@ /* * #endif */ - if (!(error = atm_connect(sock,vcc->itf,vcc->vpi,vcc->vci))) + if (!(error = atm_connect(sock,vcc->sk->bound_dev_if,vcc->vpi,vcc->vci))) sock->state = SS_CONNECTED; else (void) svc_disconnect(vcc); return error; @@ -243,9 +246,9 @@ DPRINTK("svc_listen %p\n",vcc); /* let server handle listen on unbound sockets */ if (test_bit(ATM_VF_SESSION,&vcc->flags)) return -EINVAL; + add_wait_queue(&vcc->sleep,&wait); vcc->reply = WAITING; sigd_enq(vcc,as_listen,NULL,NULL,&vcc->local); - add_wait_queue(&vcc->sleep,&wait); while (vcc->reply == WAITING && sigd) { set_current_state(TASK_UNINTERRUPTIBLE); schedule(); @@ -253,7 +256,7 @@ remove_wait_queue(&vcc->sleep,&wait); if (!sigd) return -EUNATCH; set_bit(ATM_VF_LISTEN,&vcc->flags); - vcc->backlog_quota = backlog > 0 ? backlog : ATM_BACKLOG_DEFAULT; + vcc->sk->max_ack_backlog = backlog > 0 ? backlog : ATM_BACKLOG_DEFAULT; return vcc->reply; } @@ -277,7 +280,7 @@ DECLARE_WAITQUEUE(wait,current); add_wait_queue(&old_vcc->sleep,&wait); - while (!(skb = skb_dequeue(&old_vcc->listenq)) && sigd) { + while (!(skb = skb_dequeue(&old_vcc->sk->receive_queue)) && sigd) { if (test_bit(ATM_VF_RELEASED,&old_vcc->flags)) break; if (test_bit(ATM_VF_CLOSE,&old_vcc->flags)) { error = old_vcc->reply; @@ -306,16 +309,16 @@ error = atm_connect(newsock,msg->pvc.sap_addr.itf, msg->pvc.sap_addr.vpi,msg->pvc.sap_addr.vci); dev_kfree_skb(skb); - old_vcc->backlog_quota++; + old_vcc->sk->ack_backlog--; if (error) { sigd_enq2(NULL,as_reject,old_vcc,NULL,NULL, &old_vcc->qos,error); return error == -EAGAIN ? -EBUSY : error; } /* wait should be short, so we ignore the non-blocking flag */ + add_wait_queue(&new_vcc->sleep,&wait); new_vcc->reply = WAITING; sigd_enq(new_vcc,as_accept,old_vcc,NULL,NULL); - add_wait_queue(&new_vcc->sleep,&wait); while (new_vcc->reply == WAITING && sigd) { set_current_state(TASK_UNINTERRUPTIBLE); schedule(); @@ -347,9 +350,9 @@ { DECLARE_WAITQUEUE(wait,current); + add_wait_queue(&vcc->sleep,&wait); vcc->reply = WAITING; sigd_enq2(vcc,as_modify,NULL,NULL,&vcc->local,qos,0); - add_wait_queue(&vcc->sleep,&wait); while (vcc->reply == WAITING && !test_bit(ATM_VF_RELEASED,&vcc->flags) && sigd) { set_current_state(TASK_UNINTERRUPTIBLE); @@ -390,37 +393,34 @@ } -static struct proto_ops SOCKOPS_WRAPPED(svc_proto_ops) = { - family: PF_ATMSVC, +static struct proto_ops svc_proto_ops = { + .family = PF_ATMSVC, - release: svc_release, - bind: svc_bind, - connect: svc_connect, - socketpair: sock_no_socketpair, - accept: svc_accept, - getname: svc_getname, - poll: atm_poll, - ioctl: atm_ioctl, - listen: svc_listen, - shutdown: svc_shutdown, - setsockopt: svc_setsockopt, - getsockopt: svc_getsockopt, - sendmsg: atm_sendmsg, - recvmsg: atm_recvmsg, - mmap: sock_no_mmap, - sendpage: sock_no_sendpage, + .release = svc_release, + .bind = svc_bind, + .connect = svc_connect, + .socketpair = sock_no_socketpair, + .accept = svc_accept, + .getname = svc_getname, + .poll = atm_poll, + .ioctl = atm_ioctl, + .listen = svc_listen, + .shutdown = svc_shutdown, + .setsockopt = svc_setsockopt, + .getsockopt = svc_getsockopt, + .sendmsg = atm_sendmsg, + .recvmsg = atm_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, }; -#include -SOCKOPS_WRAP(svc_proto, PF_ATMSVC); - static int svc_create(struct socket *sock,int protocol) { int error; sock->ops = &svc_proto_ops; - error = atm_create(sock,protocol,AF_ATMSVC); + error = vcc_create(sock,protocol,AF_ATMSVC); if (error) return error; ATM_SD(sock)->callback = svc_callback; ATM_SD(sock)->local.sas_family = AF_ATMSVC; @@ -430,11 +430,8 @@ static struct net_proto_family svc_family_ops = { - PF_ATMSVC, - svc_create, - 0, /* no authentication */ - 0, /* no encryption */ - 0 /* no encrypt_net */ + .family = PF_ATMSVC, + .create = svc_create, }; @@ -442,13 +439,19 @@ * Initialize the ATM SVC protocol family */ -static int __init atmsvc_init(void) +int __init atmsvc_init(void) { - if (sock_register(&svc_family_ops) < 0) { - printk(KERN_ERR "ATMSVC: can't register"); + int error; + + error = sock_register(&svc_family_ops); + if (error < 0) { + printk(KERN_ERR "ATMSVC: can't register (%d)\n", error); return -1; } return 0; } -module_init(atmsvc_init); +void __exit atmsvc_exit(void) +{ + sock_unregister(PF_ATMSVC); +} diff -urN linux-2.4.20/net/ipv4/arp.c linux-2.4.20-atm/net/ipv4/arp.c --- linux-2.4.20/net/ipv4/arp.c Fri Nov 29 00:53:15 2002 +++ linux-2.4.20-atm/net/ipv4/arp.c Wed May 28 01:58:41 2003 @@ -105,8 +105,9 @@ #include #endif #endif -#ifdef CONFIG_ATM_CLIP +#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE) #include +struct neigh_table *clip_tbl_hook = NULL; #endif #include @@ -438,8 +439,8 @@ if (dev->flags&(IFF_LOOPBACK|IFF_POINTOPOINT)) nexthop = 0; n = __neigh_lookup_errno( -#ifdef CONFIG_ATM_CLIP - dev->type == ARPHRD_ATM ? &clip_tbl : +#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE) + dev->type == ARPHRD_ATM ? clip_tbl_hook : #endif &arp_tbl, &nexthop, dev); if (IS_ERR(n)) diff -urN linux-2.4.20/net/netsyms.c linux-2.4.20-atm/net/netsyms.c --- linux-2.4.20/net/netsyms.c Wed May 28 01:54:31 2003 +++ linux-2.4.20-atm/net/netsyms.c Wed May 28 01:58:41 2003 @@ -45,6 +45,9 @@ #include #include #include +#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE) +#include +#endif #include #include #include @@ -160,6 +163,7 @@ EXPORT_SYMBOL(put_cmsg); EXPORT_SYMBOL(sock_kmalloc); EXPORT_SYMBOL(sock_kfree_s); +EXPORT_SYMBOL(sockfd_lookup); #ifdef CONFIG_FILTER EXPORT_SYMBOL(sk_run_filter); @@ -461,6 +465,9 @@ EXPORT_SYMBOL(ip_rcv); EXPORT_SYMBOL(arp_rcv); EXPORT_SYMBOL(arp_tbl); +#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE) +EXPORT_SYMBOL(clip_tbl_hook); +#endif EXPORT_SYMBOL(arp_find); #endif /* CONFIG_INET */ diff -urN linux-2.4.20/net/sched/Config.in linux-2.4.20-atm/net/sched/Config.in --- linux-2.4.20/net/sched/Config.in Fri Nov 29 00:53:16 2002 +++ linux-2.4.20-atm/net/sched/Config.in Wed May 28 01:58:41 2003 @@ -6,8 +6,8 @@ tristate ' CSZ packet scheduler' CONFIG_NET_SCH_CSZ #tristate ' H-PFQ packet scheduler' CONFIG_NET_SCH_HPFQ #tristate ' H-FSC packet scheduler' CONFIG_NET_SCH_HFCS -if [ "$CONFIG_ATM" = "y" ]; then - bool ' ATM pseudo-scheduler' CONFIG_NET_SCH_ATM +if [ "$CONFIG_ATM" != "n" ]; then + dep_tristate ' ATM pseudo-scheduler' CONFIG_NET_SCH_ATM $CONFIG_ATM fi tristate ' The simplest PRIO pseudoscheduler' CONFIG_NET_SCH_PRIO tristate ' RED queue' CONFIG_NET_SCH_RED diff -urN linux-2.4.20/net/sched/sch_atm.c linux-2.4.20-atm/net/sched/sch_atm.c --- linux-2.4.20/net/sched/sch_atm.c Wed May 28 01:54:31 2003 +++ linux-2.4.20-atm/net/sched/sch_atm.c Wed May 28 01:59:30 2003 @@ -512,7 +512,6 @@ memcpy(skb_push(skb,flow->hdr_len),flow->hdr, flow->hdr_len); atomic_add(skb->truesize,&flow->vcc->sk->wmem_alloc); - ATM_SKB(skb)->iovcnt = 0; /* atm.atm_options are already set by atm_tc_enqueue */ (void) flow->vcc->send(flow->vcc,skb); }