Messages in this thread | | | Subject | PROBLEM: <AT1700/FMV-18x hangs up> | From | Yutaka Tamiya <> | Date | Wed, 26 Jul 2000 11:40:41 +0900 |
| |
I'm a writer of "drivers/net/fmv18x.c" and and find FMV-181 hangs up with glibc-2.1. Thus I made a patch both for kernel 2.2.16 and 2.4.0-test1. Please include my patches into the next kernel releases.
[1.] AT1700/FMV-18x hangs up [2.] The Allied Telesys LAN card AT1700 and Fujitsu LAN card FMV-181/182/183/184 hang up on a system with glibc-2.1 installed (RedHat 6.2, Debian 2.2, Vine-2.0, etc). [3.] networking, Ethernet, at1700 fmv18x [4.] Kernel 2.2.16, 2.4.0-test1 [5.] eth0: transmit timed out with status XXXX, network cable problem? [X.] I made a patch for "at1700.c" not for "fmv18x.c", because fmv18x.c is a code duplication of at1700.c, and at1700.c supports FMV-18x LAN cards. Thus I want to obsolete fmv18x.c and want be a maintainer of at1700.c.
Sincerely, -------- Yutaka TAMIYA (tamy@flab.fujitsu.co.jp) FUJITSU LABS LTD. Phone: +81-44-754-2663, Fax: +81-44-754-2664
diff -cr ../linux-2.2.16.orig/drivers/net/at1700.c ./drivers/net/at1700.c *** ../linux-2.2.16.orig/drivers/net/at1700.c Wed Jul 26 00:48:42 2000 --- ./drivers/net/at1700.c Thu Jul 20 23:07:33 2000 *************** *** 35,41 **** */ static const char *version = ! "at1700.c:v1.15 4/7/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; #include <linux/config.h> #include <linux/module.h> --- 35,42 ---- */ static const char *version = ! "at1700.c:v2.2.16 Jul/20/00 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n" ! " Yutaka Tamiya (tamy@flab.fujitsu.co.jp)\n"; #include <linux/config.h> #include <linux/module.h> *************** *** 111,120 **** /* Information that need to be kept for each board. */ struct net_local { struct enet_statistics stats; unsigned char mc_filter[8]; uint jumpered:1; /* Set iff the board has jumper config. */ ! uint tx_started:1; /* Packets are on the Tx queue. */ ! uint tx_queue_ready:1; /* Tx queue is ready to be sent. */ uint rx_started:1; /* Packets are Rxing. */ uint invalid_irq:1; uchar tx_queue; /* Number of packet on the Tx queue. */ --- 112,122 ---- /* Information that need to be kept for each board. */ struct net_local { struct enet_statistics stats; + spinlock_t lock; unsigned char mc_filter[8]; uint jumpered:1; /* Set iff the board has jumper config. */ ! uint tx_started:1; /* Transmitting packets to network. */ ! uint tx_full:1; /* Set iff the Tx queue is full. */ uint rx_started:1; /* Packets are Rxing. */ uint invalid_irq:1; uchar tx_queue; /* Number of packet on the Tx queue. */ *************** *** 433,438 **** --- 435,441 ---- { struct net_local *lp = (struct net_local *)dev->priv; + lp->lock = SPIN_LOCK_UNLOCKED; lp->jumpered = is_fmv18x; lp->mca_slot = slot; /* Snarf the interrupt vector now. */ *************** *** 511,517 **** outb(0xe8, ioaddr + CONFIG_1); lp->tx_started = 0; ! lp->tx_queue_ready = 1; lp->rx_started = 0; lp->tx_queue = 0; lp->tx_queue_len = 0; --- 514,520 ---- outb(0xe8, ioaddr + CONFIG_1); lp->tx_started = 0; ! lp->tx_full = 0; lp->rx_started = 0; lp->tx_queue = 0; lp->tx_queue_len = 0; *************** *** 539,562 **** { struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; ! if (dev->tbusy) { /* If we get here, some higher level has decided we are broken. There should really be a "kick me" function call instead. */ int tickssofar = jiffies - dev->trans_start; if (tickssofar < 10) return 1; printk("%s: transmit timed out with status %04x, %s?\n", dev->name, ! inw(ioaddr + STATUS), inb(ioaddr + TX_STATUS) & 0x80 ? "IRQ conflict" : "network cable problem"); printk("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n", ! dev->name, inw(ioaddr + 0), inw(ioaddr + 2), inw(ioaddr + 4), inw(ioaddr + 6), inw(ioaddr + 8), inw(ioaddr + 10), inw(ioaddr + 12), inw(ioaddr + 14)); lp->stats.tx_errors++; /* ToDo: We should try to restart the adaptor... */ outw(0xffff, ioaddr + 24); ! outw(0xffff, ioaddr + TX_STATUS); outw(0xe85a, ioaddr + CONFIG_0); outw(0x8182, ioaddr + TX_INTR); outb(0x00, ioaddr + TX_START); --- 542,575 ---- { struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; + ushort status; + unsigned long flags; ! if (net_debug > 4) ! printk("%s: Tx, queue = %d, tx_full = %d, tbusy = %d, tx_started = %d.\n", dev->name, lp->tx_queue, lp->tx_full, (int) dev->tbusy, lp->tx_started); ! ! /* Block a timer-based transmit from overlapping. This could better be ! done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ ! if (lp->tx_full ! || test_and_set_bit(0, (void*)&dev->tbusy) != 0) { /* If we get here, some higher level has decided we are broken. There should really be a "kick me" function call instead. */ int tickssofar = jiffies - dev->trans_start; if (tickssofar < 10) return 1; + status = inw(ioaddr + STATUS); /* inw(ioaddr+8) will change STATUS */ printk("%s: transmit timed out with status %04x, %s?\n", dev->name, ! status, status & 0x80 ? "IRQ conflict" : "network cable problem"); printk("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n", ! dev->name, status, inw(ioaddr + 2), inw(ioaddr + 4), inw(ioaddr + 6), inw(ioaddr + 8), inw(ioaddr + 10), inw(ioaddr + 12), inw(ioaddr + 14)); + spin_lock_irqsave(&lp->lock, flags); lp->stats.tx_errors++; /* ToDo: We should try to restart the adaptor... */ outw(0xffff, ioaddr + 24); ! outw(0xffff, ioaddr + STATUS); outw(0xe85a, ioaddr + CONFIG_0); outw(0x8182, ioaddr + TX_INTR); outb(0x00, ioaddr + TX_START); *************** *** 564,609 **** dev->tbusy=0; dev->trans_start = jiffies; lp->tx_started = 0; ! lp->tx_queue_ready = 1; lp->rx_started = 0; lp->tx_queue = 0; lp->tx_queue_len = 0; ! } ! ! /* Block a timer-based transmit from overlapping. This could better be ! done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ ! if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) ! printk("%s: Transmitter access conflict.\n", dev->name); ! else { short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; unsigned char *buf = skb->data; ! /* We may not start transmitting unless we finish transferring ! a packet into the Tx queue. During executing the following ! codes we possibly catch a Tx interrupt. Thus we flag off ! tx_queue_ready, so that we prevent the interrupt routine ! (net_interrupt) to start transmitting. */ ! lp->tx_queue_ready = 0; ! { ! outw(length, ioaddr + DATAPORT); ! outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); ! ! lp->tx_queue++; ! lp->tx_queue_len += length + 2; } ! lp->tx_queue_ready = 1; if (lp->tx_started == 0) { /* If the Tx is idle, always trigger a transmit. */ outb(0x80 | lp->tx_queue, ioaddr + TX_START); lp->tx_queue = 0; lp->tx_queue_len = 0; dev->trans_start = jiffies; lp->tx_started = 1; ! dev->tbusy = 0; ! } else if (lp->tx_queue_len < 4096 - 1502) ! /* Yes, there is room for one more packet. */ ! dev->tbusy = 0; } dev_kfree_skb (skb); --- 577,628 ---- dev->tbusy=0; dev->trans_start = jiffies; lp->tx_started = 0; ! lp->tx_full = 0; lp->rx_started = 0; lp->tx_queue = 0; lp->tx_queue_len = 0; ! spin_unlock_irqrestore(&lp->lock, flags); ! return 1; ! } else { short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; unsigned char *buf = skb->data; ! /* Somtimes Tx/Rx interrupts are lost. In order to prevent ! our driver from hung up, we call net_interrupt() directly. ! Since net_interupt() is reentrant, we can call it at any ! time. */ ! status = inw(ioaddr + TX_STATUS); ! if (dev->interrupt == 0 && (status & 0x8082) != 0) { ! if (net_debug > 4) ! printk("%s: call net_interrupt in Tx, status = %04x.\n", ! dev->name, status); ! net_interrupt(dev->irq, dev, NULL); } ! ! spin_lock_irqsave(&lp->lock, flags); ! ! outw(length, ioaddr + DATAPORT); ! outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); ! ! lp->tx_queue++; ! lp->tx_queue_len += length + 2; ! lp->tx_full = 0; if (lp->tx_started == 0) { + if (net_debug > 4) + printk("%s: Start Txing in send, queue = %d.\n", dev->name, lp->tx_queue); /* If the Tx is idle, always trigger a transmit. */ outb(0x80 | lp->tx_queue, ioaddr + TX_START); lp->tx_queue = 0; lp->tx_queue_len = 0; dev->trans_start = jiffies; lp->tx_started = 1; ! } else if (lp->tx_queue_len >= 4096 - 1502) { ! /* No, there is no room for any more packets. */ ! lp->tx_full = 1; ! } ! clear_bit(0, (void*) &dev->tbusy); ! spin_unlock_irqrestore(&lp->lock, flags); } dev_kfree_skb (skb); *************** *** 618,623 **** --- 637,643 ---- struct device *dev = dev_id; struct net_local *lp; int ioaddr, status; + unsigned long flags; if (dev == NULL) { printk ("at1700_interrupt(): irq %d for unknown device.\n", irq); *************** *** 627,639 **** ioaddr = dev->base_addr; lp = (struct net_local *)dev->priv; ! status = inw(ioaddr + TX_STATUS); ! outw(status, ioaddr + TX_STATUS); if (net_debug > 4) printk("%s: Interrupt with status %04x.\n", dev->name, status); if (lp->rx_started == 0 && ! (status & 0xff00 || (inb(ioaddr + RX_MODE) & 0x40) == 0)) { /* Got a packet(s). We cannot execute net_rx more than once at the same time for the same device. During executing net_rx, we possibly catch a --- 647,662 ---- ioaddr = dev->base_addr; lp = (struct net_local *)dev->priv; ! ! spin_lock_irqsave(&lp->lock, flags); ! ! status = inw(ioaddr + STATUS); ! outw(status, ioaddr + STATUS); if (net_debug > 4) printk("%s: Interrupt with status %04x.\n", dev->name, status); if (lp->rx_started == 0 && ! ((status & 0xff00) != 0 || (inb(ioaddr + RX_MODE) & 0x40) == 0)) { /* Got a packet(s). We cannot execute net_rx more than once at the same time for the same device. During executing net_rx, we possibly catch a *************** *** 647,653 **** lp->rx_started = 0; } if (status & 0x00ff) { ! if (status & 0x02) { /* More than 16 collisions occurred */ if (net_debug > 4) printk("%s: 16 Collision occur during Txing.\n", dev->name); --- 670,676 ---- lp->rx_started = 0; } if (status & 0x00ff) { ! if (status & 0x0002) { /* More than 16 collisions occurred */ if (net_debug > 4) printk("%s: 16 Collision occur during Txing.\n", dev->name); *************** *** 655,681 **** outb(0x03, ioaddr + COL16CNTL); lp->stats.collisions++; } ! if (status & 0x82) { lp->stats.tx_packets++; /* The Tx queue has any packets and is not being transferred a packet from the host, start transmitting. */ ! if (lp->tx_queue && lp->tx_queue_ready) { outb(0x80 | lp->tx_queue, ioaddr + TX_START); lp->tx_queue = 0; lp->tx_queue_len = 0; dev->trans_start = jiffies; ! dev->tbusy = 0; mark_bh(NET_BH); /* Inform upper layers. */ } else { lp->tx_started = 0; ! dev->tbusy = 0; mark_bh(NET_BH); /* Inform upper layers. */ } } } dev->interrupt = 0; return; } --- 678,713 ---- outb(0x03, ioaddr + COL16CNTL); lp->stats.collisions++; } ! if (status & 0x0082) { lp->stats.tx_packets++; + lp->tx_full = 0; /* The Tx queue has any packets and is not being transferred a packet from the host, start transmitting. */ ! if (lp->tx_queue ! && test_and_set_bit(0, (void*) &dev->tbusy) == 0) { ! if (net_debug > 4) ! printk("%s: Start Txing in intr, queue = %d.\n", dev->name, lp->tx_queue); outb(0x80 | lp->tx_queue, ioaddr + TX_START); lp->tx_queue = 0; lp->tx_queue_len = 0; dev->trans_start = jiffies; ! clear_bit(0, (void*) &dev->tbusy); mark_bh(NET_BH); /* Inform upper layers. */ } else { lp->tx_started = 0; ! /*clear_bit(0, (void*) &dev->tbusy);*/ mark_bh(NET_BH); /* Inform upper layers. */ } } } + spin_unlock_irqrestore(&lp->lock, flags); dev->interrupt = 0; + + if (net_debug > 4) + printk("%s: End Interrupt with status %04x.\n", dev->name, status); + return; } diff -cr ../linux-2.4.0-test1.orig/Documentation/Configure.help ./Documentation/Configure.help *** ../linux-2.4.0-test1.orig/Documentation/Configure.help Wed Jul 26 01:02:09 2000 --- ./Documentation/Configure.help Wed Jul 26 01:25:49 2000 *************** *** 8497,8529 **** Documentation/networking/net-modules.txt. The module will be called ewrk3.o. ! AT1700/1720 support CONFIG_AT1700 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from http://www.linuxdoc.org/docs.html#howto . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. The module will be called at1700.o. - FMV-181/182/183/184 support - CONFIG_FMV18X - If you have a Fujitsu FMV-181/182/183/184 network (Ethernet) card, - say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . - - If you use an FMV-183 or FMV-184 and it is not working, you may need - to disable Plug & Play mode of the card. - - This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called fmv18x.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt.
EtherExpress PRO support CONFIG_EEXPRESS_PRO --- 8497,8518 ---- Documentation/networking/net-modules.txt. The module will be called ewrk3.o. ! AT1700/1720, FMV-181/182/183/184 support CONFIG_AT1700 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from http://www.linuxdoc.org/docs.html#howto . + If you use an FMV-183 or FMV-184 and it is not working, you may neeed + to use isapnptools, or disable Plug & Play mode of the card with its + DOS utility program. + This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. The module will be called at1700.o. EtherExpress PRO support CONFIG_EEXPRESS_PRO diff -cr ../linux-2.4.0-test1.orig/drivers/net/Config.in ./drivers/net/Config.in *** ../linux-2.4.0-test1.orig/drivers/net/Config.in Wed Jul 26 01:00:47 2000 --- ./drivers/net/Config.in Wed Jul 26 01:06:24 2000 *************** *** 93,101 **** tristate ' NI5210 support' CONFIG_NI52 tristate ' NI6510 support' CONFIG_NI65 fi ! if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then ! tristate ' AT1700/1720 support (EXPERIMENTAL)' CONFIG_AT1700 ! fi tristate ' DEPCA, DE10x, DE200, DE201, DE202, DE422 support' CONFIG_DEPCA bool ' Other ISA cards' CONFIG_NET_ISA if [ "$CONFIG_NET_ISA" = "y" ]; then --- 93,99 ---- tristate ' NI5210 support' CONFIG_NI52 tristate ' NI6510 support' CONFIG_NI65 fi ! tristate ' AT1700/1720, FMV-181/182/183/184 support' CONFIG_AT1700 tristate ' DEPCA, DE10x, DE200, DE201, DE202, DE422 support' CONFIG_DEPCA bool ' Other ISA cards' CONFIG_NET_ISA if [ "$CONFIG_NET_ISA" = "y" ]; then *************** *** 105,113 **** fi tristate ' EtherExpress 16 support' CONFIG_EEXPRESS tristate ' EtherExpressPro support' CONFIG_EEXPRESS_PRO - if [ "$CONFIG_OBSOLETE" = "y" ]; then - tristate ' FMV-181/182/183/184 support' CONFIG_FMV18X - fi tristate ' HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS tristate ' HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN tristate ' HP 10/100VG PCLAN (ISA, EISA, PCI) support' CONFIG_HP100 --- 103,108 ---- diff -cr ../linux-2.4.0-test1.orig/drivers/net/Space.c ./drivers/net/Space.c *** ../linux-2.4.0-test1.orig/drivers/net/Space.c Wed Jul 26 01:00:33 2000 --- ./drivers/net/Space.c Wed Jul 26 01:05:07 2000 *************** *** 257,265 **** #ifdef CONFIG_AT1700 {at1700_probe, 0}, #endif - #ifdef CONFIG_FMV18X /* Fujitsu FMV-181/182 */ - {fmv18x_probe, 0}, - #endif #ifdef CONFIG_ETH16I {eth16i_probe, 0}, /* ICL EtherTeam 16i/32 */ #endif --- 257,262 ---- diff -cr ../linux-2.4.0-test1.orig/drivers/net/at1700.c ./drivers/net/at1700.c *** ../linux-2.4.0-test1.orig/drivers/net/at1700.c Wed Jul 26 01:00:12 2000 --- ./drivers/net/at1700.c Thu Jul 20 23:48:34 2000 *************** *** 35,41 **** */ static const char *version = ! "at1700.c:v1.15 4/7/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; #include <linux/config.h> #include <linux/module.h> --- 35,42 ---- */ static const char *version = ! "at1700.c:v2.4.0 Jul/20/00 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n" ! " Yutaka Tamiya (tamy@flab.fujitsu.co.jp)\n"; #include <linux/config.h> #include <linux/module.h> *************** *** 114,124 **** spinlock_t lock; unsigned char mc_filter[8]; uint jumpered:1; /* Set iff the board has jumper config. */ ! uint tx_started:1; /* Packets are on the Tx queue. */ ! uint tx_queue_ready:1; /* Tx queue is ready to be sent. */ uint rx_started:1; /* Packets are Rxing. */ uint invalid_irq:1; ! uchar tx_queue; /* Number of packet on the Tx queue. */ char mca_slot; /* -1 means ISA */ ushort tx_queue_len; /* Current length of the Tx queue. */ }; --- 115,127 ---- spinlock_t lock; unsigned char mc_filter[8]; uint jumpered:1; /* Set iff the board has jumper config. */ ! uint tx_started:1; /* Transmitting packets to network. */ ! uint tx_full:1; /* Set iff the Tx queue is full. */ uint rx_started:1; /* Packets are Rxing. */ + uint interrupt:1; /* net_interrupt() is executing. */ uint invalid_irq:1; ! uint tx_busy; /* Transferring packets to the Tx queue. */ ! uchar tx_queue; /* Number of packets on the Tx queue. */ char mca_slot; /* -1 means ISA */ ushort tx_queue_len; /* Current length of the Tx queue. */ }; *************** *** 438,444 **** dev->watchdog_timeo = TX_TIMEOUT; lp = (struct net_local *)dev->priv; ! lp->lock = SPIN_LOCK_UNLOCKED; /* Fill in the fields of 'dev' with ethernet-generic values. */ ether_setup(dev); --- 441,447 ---- dev->watchdog_timeo = TX_TIMEOUT; lp = (struct net_local *)dev->priv; ! spin_lock_init(&lp->lock); /* Fill in the fields of 'dev' with ethernet-generic values. */ ether_setup(dev); *************** *** 520,529 **** outb(0xe8, ioaddr + CONFIG_1); lp->tx_started = 0; ! lp->tx_queue_ready = 1; lp->rx_started = 0; lp->tx_queue = 0; lp->tx_queue_len = 0; /* Turn on hardware Tx and Rx interrupts. */ outb(0x82, ioaddr + TX_INTR); --- 523,534 ---- outb(0xe8, ioaddr + CONFIG_1); lp->tx_started = 0; ! lp->tx_full = 0; lp->rx_started = 0; + lp->tx_busy = 0; lp->tx_queue = 0; lp->tx_queue_len = 0; + lp->interrupt = 0; /* Turn on hardware Tx and Rx interrupts. */ outb(0x82, ioaddr + TX_INTR); *************** *** 545,562 **** { struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; printk ("%s: transmit timed out with status %04x, %s?\n", dev->name, ! inw (ioaddr + STATUS), inb (ioaddr + TX_STATUS) & 0x80 ? "IRQ conflict" : "network cable problem"); printk ("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n", ! dev->name, inw (ioaddr + 0), inw (ioaddr + 2), inw (ioaddr + 4), inw (ioaddr + 6), inw (ioaddr + 8), inw (ioaddr + 10), inw (ioaddr + 12), inw (ioaddr + 14)); lp->stats.tx_errors++; /* ToDo: We should try to restart the adaptor... */ outw (0xffff, ioaddr + 24); ! outw (0xffff, ioaddr + TX_STATUS); outw (0xe85a, ioaddr + CONFIG_0); outw (0x8182, ioaddr + TX_INTR); outb (0x00, ioaddr + TX_START); --- 550,568 ---- { struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; + ushort status = inw(ioaddr + STATUS); /* inw(ioaddr+8) will change STATUS */ printk ("%s: transmit timed out with status %04x, %s?\n", dev->name, ! status, status & 0x80 ? "IRQ conflict" : "network cable problem"); printk ("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n", ! dev->name, status, inw (ioaddr + 2), inw (ioaddr + 4), inw (ioaddr + 6), inw (ioaddr + 8), inw (ioaddr + 10), inw (ioaddr + 12), inw (ioaddr + 14)); lp->stats.tx_errors++; /* ToDo: We should try to restart the adaptor... */ outw (0xffff, ioaddr + 24); ! outw (0xffff, ioaddr + STATUS); outw (0xe85a, ioaddr + CONFIG_0); outw (0x8182, ioaddr + TX_INTR); outb (0x00, ioaddr + TX_START); *************** *** 565,571 **** dev->trans_start = jiffies; lp->tx_started = 0; ! lp->tx_queue_ready = 1; lp->rx_started = 0; lp->tx_queue = 0; lp->tx_queue_len = 0; --- 571,578 ---- dev->trans_start = jiffies; lp->tx_started = 0; ! lp->tx_full = 0; ! lp->tx_busy = 0; lp->rx_started = 0; lp->tx_queue = 0; lp->tx_queue_len = 0; *************** *** 580,614 **** int ioaddr = dev->base_addr; short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; unsigned char *buf = skb->data; ! netif_stop_queue (dev); ! /* We may not start transmitting unless we finish transferring ! a packet into the Tx queue. During executing the following ! codes we possibly catch a Tx interrupt. Thus we flag off ! tx_queue_ready, so that we prevent the interrupt routine ! (net_interrupt) to start transmitting. */ ! lp->tx_queue_ready = 0; ! { ! outw (length, ioaddr + DATAPORT); ! outsw (ioaddr + DATAPORT, buf, (length + 1) >> 1); ! lp->tx_queue++; ! lp->tx_queue_len += length + 2; } ! lp->tx_queue_ready = 1; if (lp->tx_started == 0) { /* If the Tx is idle, always trigger a transmit. */ outb (0x80 | lp->tx_queue, ioaddr + TX_START); lp->tx_queue = 0; lp->tx_queue_len = 0; dev->trans_start = jiffies; lp->tx_started = 1; netif_start_queue (dev); ! } else if (lp->tx_queue_len < 4096 - 1502) /* Yes, there is room for one more packet. */ netif_start_queue (dev); dev_kfree_skb (skb); return 0; --- 587,644 ---- int ioaddr = dev->base_addr; short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; unsigned char *buf = skb->data; + ushort status; + unsigned long flags; ! if (net_debug > 4) ! printk("%s: Tx, queue = %d, tx_full = %d, tx_busy = %d, tx_started = %d.\n", dev->name, lp->tx_queue, lp->tx_full, lp->tx_busy, lp->tx_started); ! netif_stop_queue (dev); ! /* If Tx queue is full or we cannot get the lock, return imediately. */ ! if (lp->tx_full ! || test_and_set_bit(0, (void*) &lp->tx_busy) !=0) ! return 1; ! ! /* Somtimes Tx/Rx interrupts are lost. In order to prevent our driver ! from hung up, we call net_interrupt() directly. Since net_interupt() ! is reentrant, we can call it at any time. */ ! status = inw(ioaddr + TX_STATUS); ! if (lp->interrupt == 0 && (status & 0x8082) != 0) { ! if (net_debug > 4) ! printk("%s: call net_interrupt in Tx, status = %04x.\n", dev->name, status); ! net_interrupt(dev->irq, dev, NULL); } ! ! spin_lock_irqsave(&lp->lock, flags); ! ! outw (length, ioaddr + DATAPORT); ! outsw (ioaddr + DATAPORT, buf, (length + 1) >> 1); ! ! lp->tx_queue++; ! lp->tx_queue_len += length + 2; if (lp->tx_started == 0) { + if (net_debug > 4) + printk("%s: Start Txing in send, queue = %d.\n", dev->name, lp->tx_queue); /* If the Tx is idle, always trigger a transmit. */ outb (0x80 | lp->tx_queue, ioaddr + TX_START); lp->tx_queue = 0; lp->tx_queue_len = 0; dev->trans_start = jiffies; lp->tx_started = 1; + lp->tx_full = 0; netif_start_queue (dev); ! } else if (lp->tx_queue_len < 4096 - 1502) { /* Yes, there is room for one more packet. */ netif_start_queue (dev); + lp->tx_full = 0; + } else { + lp->tx_full = 1; + } + + clear_bit(0, (void*) &lp->tx_busy); + spin_unlock_irqrestore(&lp->lock, flags); dev_kfree_skb (skb); return 0; *************** *** 622,645 **** struct net_device *dev = dev_id; struct net_local *lp; int ioaddr, status; if (dev == NULL) { printk ("at1700_interrupt(): irq %d for unknown device.\n", irq); return; } ioaddr = dev->base_addr; lp = (struct net_local *)dev->priv;
! spin_lock (&lp->lock); ! ! status = inw(ioaddr + TX_STATUS); ! outw(status, ioaddr + TX_STATUS);
if (net_debug > 4) printk("%s: Interrupt with status %04x.\n", dev->name, status); if (lp->rx_started == 0 && ! (status & 0xff00 || (inb(ioaddr + RX_MODE) & 0x40) == 0)) { /* Got a packet(s). We cannot execute net_rx more than once at the same time for the same device. During executing net_rx, we possibly catch a --- 652,676 ---- struct net_device *dev = dev_id; struct net_local *lp; int ioaddr, status; + unsigned long flags; if (dev == NULL) { printk ("at1700_interrupt(): irq %d for unknown device.\n", irq); return; } ioaddr = dev->base_addr; lp = (struct net_local *)dev->priv; + lp->interrupt = 1; ! spin_lock_irqsave(&lp->lock, flags); ! ! status = inw(ioaddr + STATUS); ! outw(status, ioaddr + STATUS); if (net_debug > 4) printk("%s: Interrupt with status %04x.\n", dev->name, status); if (lp->rx_started == 0 && ! ((status & 0xff00) != 0 || (inb(ioaddr + RX_MODE) & 0x40) == 0)) { /* Got a packet(s). We cannot execute net_rx more than once at the same time for the same device. During executing net_rx, we possibly catch a *************** *** 653,659 **** lp->rx_started = 0; } if (status & 0x00ff) { ! if (status & 0x02) { /* More than 16 collisions occurred */ if (net_debug > 4) printk("%s: 16 Collision occur during Txing.\n", dev->name); --- 684,690 ---- lp->rx_started = 0; } if (status & 0x00ff) { ! if (status & 0x0002) { /* More than 16 collisions occurred */ if (net_debug > 4) printk("%s: 16 Collision occur during Txing.\n", dev->name); *************** *** 661,676 **** outb(0x03, ioaddr + COL16CNTL); lp->stats.collisions++; } ! if (status & 0x82) { lp->stats.tx_packets++; /* The Tx queue has any packets and is not being transferred a packet from the host, start transmitting. */ ! if (lp->tx_queue && lp->tx_queue_ready) { outb(0x80 | lp->tx_queue, ioaddr + TX_START); lp->tx_queue = 0; lp->tx_queue_len = 0; dev->trans_start = jiffies; netif_wake_queue (dev); } else { lp->tx_started = 0; --- 692,712 ---- outb(0x03, ioaddr + COL16CNTL); lp->stats.collisions++; } ! if (status & 0x0082) { lp->stats.tx_packets++; + lp->tx_full = 0; /* The Tx queue has any packets and is not being transferred a packet from the host, start transmitting. */ ! if (lp->tx_queue ! && test_and_set_bit(0, (void*) &lp->tx_busy) == 0) { ! if (net_debug > 4) ! printk("%s: Start Txing in intr, queue = %d.\n", dev->name, lp->tx_queue); outb(0x80 | lp->tx_queue, ioaddr + TX_START); lp->tx_queue = 0; lp->tx_queue_len = 0; dev->trans_start = jiffies; + clear_bit(0, (void*) &lp->tx_busy); netif_wake_queue (dev); } else { lp->tx_started = 0; *************** *** 679,685 **** } } ! spin_unlock (&lp->lock); return; } --- 715,725 ---- } } ! spin_unlock_irqrestore(&lp->lock, flags); ! lp->interrupt = 0; ! ! if (net_debug > 4) ! printk("%s: End Interrupt with status %04x.\n", dev->name, status); return; } *************** *** 781,789 **** /* Disable the IRQ on boards of fmv18x where it is feasible. */ if (lp->jumpered) { outb(0x00, ioaddr + IOCONFIG1); - free_irq(dev->irq, dev); } /* Power-down the chip. Green, green, green! */ outb(0x00, ioaddr + CONFIG_1); --- 821,830 ---- /* Disable the IRQ on boards of fmv18x where it is feasible. */ if (lp->jumpered) { outb(0x00, ioaddr + IOCONFIG1); } + free_irq(dev->irq, dev); + /* Power-down the chip. Green, green, green! */ outb(0x00, ioaddr + CONFIG_1); *************** *** 918,924 **** dev_at1700.priv = NULL; /* If we don't do this, we can't re-insmod it later. */ ! free_irq(dev_at1700.irq, NULL); release_region(dev_at1700.base_addr, AT1700_IO_EXTENT); } #endif /* MODULE */ --- 959,965 ---- dev_at1700.priv = NULL; /* If we don't do this, we can't re-insmod it later. */ ! /*free_irq(dev_at1700.irq, NULL);*/ release_region(dev_at1700.base_addr, AT1700_IO_EXTENT); } #endif /* MODULE */
| |