- Changes done to 3c59x.c driver (my test machine has these) are "a bit" shaky -- the change_mtu() function clearly needs to poke at the card at selected moments, but it is *VERY* timing sensitive! Doing it at system startup with present code may get the system up just fine, or it may start spewing lots and lots of network card diagnostics to the console. (Doing "ifconfig vlan0123 mtu 3000" at active traffic interface is presently a surefire way to cause spewage.. Ball to driver specialists.) diff -u -r linux-2400t10vl14/drivers/net/3c59x.c linux-2400t10vl14m/drivers/net/3c59x.c --- linux-2400t10vl14/drivers/net/3c59x.c Mon Oct 16 22:58:51 2000 +++ linux-2400t10vl14m/drivers/net/3c59x.c Sat Nov 4 00:33:32 2000 @@ -663,6 +663,7 @@ has_nway:1, open:1, must_free_region:1; /* Flag: if zero, Cardbus owns the I/O region */ + int pkt_buf_sz; /* MTU + 36 ??? (MAX_HEADER ?), plus alignment */ int drv_flags; u16 status_enable; u16 intr_enable; @@ -729,6 +730,7 @@ static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static void vortex_tx_timeout(struct net_device *dev); static void acpi_set_WOL(struct net_device *dev); +static void wait_for_completion(struct net_device *, int); /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ /* Option count limit only -- unlimited interfaces are supported. */ @@ -777,6 +779,54 @@ } } +static int vortex_set_mtu(struct net_device *dev, int mtu) +{ + struct vortex_private *vp = dev->priv; + unsigned int old_mtu = dev->mtu; + + if (mtu < 68 || mtu > 4000) + return -EINVAL; /* FIXME: MODEL SPECIFIC LIMITATIONS ??? */ + + dev->mtu = mtu; + vp->pkt_buf_sz = mtu + 36; /* FIXME: What is this MAGIC 36 ??? */ + + /* Size transition under or above the magic 1500 byte marker. */ + + if ((old_mtu <= 1500 && mtu > 1500) || + (old_mtu > 1500 && mtu <= 1500)) { + + /* FIXME: IS THIS RACE SAFE ? DEADLOCK SAFE ? */ + + unsigned long flags; + long ioaddr = dev->base_addr; + unsigned int config; + + local_irq_save(flags); + + EL3WINDOW(3); + + /* Set the full-duplex bit. */ + config = (((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) | + ((dev->mtu > 1500) ? 0x40 : 0) | + ((vp->full_duplex && vp->flow_ctrl && + vp->partner_flow_ctrl) ? 0x100 : 0)); + outw(config, ioaddr + Wn3_MAC_Ctrl); + + wait_for_completion(dev, TxReset); + wait_for_completion(dev, RxReset); + + outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); + + local_irq_restore(flags); + + if (vortex_debug > 0) + printk(KERN_DEBUG "%s: vortex_set_mtu() New mtu=%d Wn3MACCtrl=%4.4x.\n", + dev->name, mtu, config); + } + return mtu; +} + + /* returns count found (>= 0), or negative on error */ static int __init vortex_eisa_init (void) { @@ -881,6 +931,7 @@ dev->base_addr = ioaddr; dev->irq = irq; dev->mtu = mtu; + vp->pkt_buf_sz = mtu + 36; /* FIXME: What is this MAGIC 36 ??? */ vp->drv_flags = vci->drv_flags; vp->has_nway = (vci->drv_flags & HAS_NWAY) ? 1 : 0; vp->io_size = vci->io_size; @@ -1109,9 +1160,10 @@ dev->hard_start_xmit = vp->full_bus_master_tx ? boomerang_start_xmit : vortex_start_xmit; dev->stop = vortex_close; - dev->get_stats = vortex_get_stats; - dev->do_ioctl = vortex_ioctl; + dev->get_stats = vortex_get_stats; + dev->do_ioctl = vortex_ioctl; dev->set_multicast_list = set_rx_mode; + dev->change_mtu = vortex_set_mtu; dev->tx_timeout = vortex_tx_timeout; dev->watchdog_timeo = (watchdog * HZ) / 1000; @@ -1227,7 +1279,7 @@ /* Set the full-duplex bit. */ outw( ((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) | - (dev->mtu > 1500 ? 0x40 : 0) | + ((dev->mtu > 1500) ? 0x40 : 0) | ((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ? 0x100 : 0), ioaddr + Wn3_MAC_Ctrl); @@ -1298,7 +1350,7 @@ if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */ vp->cur_tx = vp->dirty_tx = 0; if (vp->drv_flags & IS_BOOMERANG) - outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); /* Room for a packet. */ + outb(vp->pkt_buf_sz>>8, ioaddr + TxFreeThreshold); /* Room for a packet. */ /* Clear the Rx, Tx rings. */ for (i = 0; i < RX_RING_SIZE; i++) /* AKPM: this is done in vortex_open, too */ vp->rx_ring[i].status = 0; @@ -1354,14 +1406,14 @@ struct sk_buff *skb; vp->rx_ring[i].next = cpu_to_le32(vp->rx_ring_dma + sizeof(struct boom_rx_desc) * (i+1)); vp->rx_ring[i].status = 0; /* Clear complete bit. */ - vp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ | LAST_FRAG); - skb = dev_alloc_skb(PKT_BUF_SZ); + vp->rx_ring[i].length = cpu_to_le32(vp->pkt_buf_sz | LAST_FRAG); + skb = dev_alloc_skb(vp->pkt_buf_sz); vp->rx_skbuff[i] = skb; if (skb == NULL) break; /* Bad news! */ skb->dev = dev; /* Mark as being used by this device. */ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - vp->rx_ring[i].addr = cpu_to_le32(pci_map_single(vp->pdev, skb->tail, PKT_BUF_SZ, PCI_DMA_FROMDEVICE)); + vp->rx_ring[i].addr = cpu_to_le32(pci_map_single(vp->pdev, skb->tail, vp->pkt_buf_sz, PCI_DMA_FROMDEVICE)); } if (i != RX_RING_SIZE) { int j; @@ -1443,12 +1495,15 @@ /* Set the full-duplex bit. */ EL3WINDOW(3); /* AKPM: this was missing from 2.3.99 3c59x.c! */ outw( (vp->full_duplex ? 0x20 : 0) | - (dev->mtu > 1500 ? 0x40 : 0) | + ((dev->mtu > 1500) ? 0x40 : 0) | ((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ? 0x100 : 0), ioaddr + Wn3_MAC_Ctrl); if (vortex_debug > 1) printk(KERN_DEBUG "Setting duplex in Wn3_MAC_Ctrl\n"); /* AKPM: bug: should reset Tx and Rx after setting Duplex. Page 180 */ + + wait_for_completion(dev, TxReset); + wait_for_completion(dev, RxReset); } } } @@ -1558,7 +1613,7 @@ if (vp->tx_full) netif_stop_queue (dev); if (vp->drv_flags & IS_BOOMERANG) - outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); + outb(vp->pkt_buf_sz>>8, ioaddr + TxFreeThreshold); outw(DownUnstall, ioaddr + EL3_CMD); } else { vp->stats.tx_dropped++; @@ -2053,10 +2108,19 @@ (pkt_len + 3) >> 2); } outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ + + if (skb->data[0] & 1) { + /* Destination MAC address has at its first octet + the lowest bit set -> multicast or broadcast.. */ + if (memcmp(skb->data, dev->broadcast, ETH_ALEN) != 0) + /* Multicast! */ + vp->stats.multicast++; + } + skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); dev->last_rx = jiffies; vp->stats.rx_packets++; + netif_rx(skb); /* Wait a limited time to go to next packet. */ for (i = 200; i >= 0; i--) if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) @@ -2114,7 +2178,7 @@ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != 0) { skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - pci_dma_sync_single(vp->pdev, dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE); + pci_dma_sync_single(vp->pdev, dma, vp->pkt_buf_sz, PCI_DMA_FROMDEVICE); /* 'skb_put()' points to the start of sk_buff data area. */ memcpy(skb_put(skb, pkt_len), vp->rx_skbuff[entry]->tail, @@ -2125,9 +2189,18 @@ skb = vp->rx_skbuff[entry]; vp->rx_skbuff[entry] = NULL; skb_put(skb, pkt_len); - pci_unmap_single(vp->pdev, dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE); + pci_unmap_single(vp->pdev, dma, vp->pkt_buf_sz, PCI_DMA_FROMDEVICE); rx_nocopy++; } + + if (skb->data[0] & 1) { + /* Destination MAC address has at its first octet + the lowest bit set -> multicast or broadcast.. */ + if (memcmp(skb->data, dev->broadcast, ETH_ALEN) != 0) + /* Multicast! */ + vp->stats.multicast++; + } + skb->protocol = eth_type_trans(skb, dev); { /* Use hardware checksum info. */ int csum_bits = rx_status & 0xee000000; @@ -2138,9 +2211,9 @@ rx_csumhits++; } } - netif_rx(skb); dev->last_rx = jiffies; vp->stats.rx_packets++; + netif_rx(skb); } entry = (++vp->cur_rx) % RX_RING_SIZE; } @@ -2149,7 +2222,7 @@ struct sk_buff *skb; entry = vp->dirty_rx % RX_RING_SIZE; if (vp->rx_skbuff[entry] == NULL) { - skb = dev_alloc_skb(PKT_BUF_SZ); + skb = dev_alloc_skb(vp->pkt_buf_sz); if (skb == NULL) { static unsigned long last_jif; if ((jiffies - last_jif) > 10 * HZ) { @@ -2162,7 +2235,7 @@ } skb->dev = dev; /* Mark as being used by this device. */ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - vp->rx_ring[entry].addr = cpu_to_le32(pci_map_single(vp->pdev, skb->tail, PKT_BUF_SZ, PCI_DMA_FROMDEVICE)); + vp->rx_ring[entry].addr = cpu_to_le32(pci_map_single(vp->pdev, skb->tail, vp->pkt_buf_sz, PCI_DMA_FROMDEVICE)); vp->rx_skbuff[entry] = skb; } vp->rx_ring[entry].status = 0; /* Clear complete bit. */ @@ -2249,7 +2322,7 @@ for (i = 0; i < RX_RING_SIZE; i++) if (vp->rx_skbuff[i]) { pci_unmap_single( vp->pdev, le32_to_cpu(vp->rx_ring[i].addr), - PKT_BUF_SZ, PCI_DMA_FROMDEVICE); + vp->pkt_buf_sz, PCI_DMA_FROMDEVICE); dev_kfree_skb(vp->rx_skbuff[i]); vp->rx_skbuff[i] = 0; } @@ -2402,16 +2475,23 @@ static void set_rx_mode(struct net_device *dev) { long ioaddr = dev->base_addr; - int new_mode; + int new_mode = SetRxFilter | RxStation; + + if (dev->flags & IFF_BROADCAST) + new_mode |= RxBroadcast; if (dev->flags & IFF_PROMISC) { if (vortex_debug > 0) - printk(KERN_NOTICE "%s: Setting promiscuous mode.\n", dev->name); - new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm; - } else if ((dev->mc_list) || (dev->flags & IFF_ALLMULTI)) { - new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast; - } else - new_mode = SetRxFilter | RxStation | RxBroadcast; + printk(KERN_NOTICE "%s: Setting promiscuous mode; cnt=%d\n", dev->name, dev->promiscuity); + new_mode |= RxProm; + } + + /* FIXME: Some of the 3c905 family cards actually have a WORKING + multicast reception filter, take it into use! */ + + if ((dev->mc_list) || (dev->flags & IFF_ALLMULTI)) { + new_mode |= RxMulticast; + } outw(new_mode, ioaddr + EL3_CMD); }