# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.604.1.7 -> 1.604.1.8 # drivers/usb/net/rtl8150.c 1.8 -> 1.9 # drivers/usb/net/pegasus.h 1.16 -> 1.17 # drivers/usb/net/pegasus.c 1.28 -> 1.29 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/07/04 petkan@users.sourceforge.net 1.604.1.8 # [PATCH] pegasus & rtl8150 # # I chose a little bit more restrictive license for my drivers. # Rx skb pool introduced in pegasus driver and the pool locking in rtl8150 # is refined. # -------------------------------------------- # diff -Nru a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c --- a/drivers/usb/net/pegasus.c Fri Jul 5 14:51:09 2002 +++ b/drivers/usb/net/pegasus.c Fri Jul 5 14:51:09 2002 @@ -1,46 +1,34 @@ /* -** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller -** -** Copyright (c) 1999-2002 Petko Manolov (petkan@users.sourceforge.net) -** -** -** ChangeLog: -** .... Most of the time spend reading sources & docs. -** v0.2.x First official release for the Linux kernel. -** v0.3.0 Beutified and structured, some bugs fixed. -** v0.3.x URBifying bulk requests and bugfixing. First relatively -** stable release. Still can touch device's registers only -** from top-halves. -** v0.4.0 Control messages remained unurbified are now URBs. -** Now we can touch the HW at any time. -** v0.4.9 Control urbs again use process context to wait. Argh... -** Some long standing bugs (enable_net_traffic) fixed. -** Also nasty trick about resubmiting control urb from -** interrupt context used. Please let me know how it -** behaves. Pegasus II support added since this version. -** TODO: suppressing HCD warnings spewage on disconnect. -** v0.4.13 Ethernet address is now set at probe(), not at open() -** time as this seems to break dhcpd. -** v0.5.0 branch to 2.5.x kernels -** v0.5.1 ethtool support added -*/ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * Copyright (c) 1999-2002 Petko Manolov (petkan@users.sourceforge.net) * - * This program 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 General Public License for more details. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * ChangeLog: + * .... Most of the time spent on reading sources & docs. + * v0.2.x First official release for the Linux kernel. + * v0.3.0 Beutified and structured, some bugs fixed. + * v0.3.x URBifying bulk requests and bugfixing. First relatively + * stable release. Still can touch device's registers only + * from top-halves. + * v0.4.0 Control messages remained unurbified are now URBs. + * Now we can touch the HW at any time. + * v0.4.9 Control urbs again use process context to wait. Argh... + * Some long standing bugs (enable_net_traffic) fixed. + * Also nasty trick about resubmiting control urb from + * interrupt context used. Please let me know how it + * behaves. Pegasus II support added since this version. + * TODO: suppressing HCD warnings spewage on disconnect. + * v0.4.13 Ethernet address is now set at probe(), not at open() + * time as this seems to break dhcpd. + * v0.5.0 branch to 2.5.x kernels + * v0.5.1 ethtool support added + * v0.5.5 rx socket buffers are in a pool and the their allocation + * is out of the interrupt routine. */ + #include #include #include @@ -58,14 +46,13 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.5.4 (2002/04/11)" +#define DRIVER_VERSION "v0.5.6 (2002/06/23)" #define DRIVER_AUTHOR "Petko Manolov " #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" static const char driver_name[] = "pegasus"; -#define PEGASUS_USE_INTR -#define PEGASUS_WRITE_EEPROM +#undef PEGASUS_WRITE_EEPROM #define BMSR_MEDIA (BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | \ BMSR_100FULL | BMSR_ANEGCAPABLE) @@ -499,13 +486,57 @@ return 0; } +static void fill_skb_pool(pegasus_t *pegasus) +{ + int i; + + for (i=0; i < RX_SKBS; i++) { + if (pegasus->rx_pool[i]) + continue; + pegasus->rx_pool[i] = dev_alloc_skb(PEGASUS_MTU + 2); + /* + ** we give up if the allocation fail. the tasklet will be + ** rescheduled again anyway... + */ + if (pegasus->rx_pool[i] == NULL) + return; + pegasus->rx_pool[i]->dev = pegasus->net; + skb_reserve(pegasus->rx_pool[i], 2); + } +} + +static void free_skb_pool(pegasus_t *pegasus) +{ + int i; + + for (i=0; i < RX_SKBS; i++) { + if (pegasus->rx_pool[i]) { + dev_kfree_skb(pegasus->rx_pool[i]); + pegasus->rx_pool[i] = NULL; + } + } +} + +static inline struct sk_buff *pull_skb(pegasus_t *pegasus) +{ + int i; + struct sk_buff *skb; + + for (i=0; i < RX_SKBS; i++) { + if (likely(pegasus->rx_pool[i] != NULL)) { + skb = pegasus->rx_pool[i]; + pegasus->rx_pool[i] = NULL; + return skb; + } + } + return NULL; +} + static void read_bulk_callback(struct urb *urb) { pegasus_t *pegasus = urb->context; struct net_device *net; - int count = urb->actual_length; - int rx_status; - struct sk_buff *skb; + int rx_status, count = urb->actual_length; __u16 pkt_len; if (!pegasus || !(pegasus->flags & PEGASUS_RUNNING)) @@ -519,7 +550,7 @@ case 0: break; case -ETIMEDOUT: - dbg("reset MAC"); + dbg("%s: reset MAC", net->name); pegasus->flags &= ~PEGASUS_RX_BUSY; break; case -ENOENT: @@ -546,23 +577,24 @@ } pkt_len = (rx_status & 0xfff) - 8; - if (!pegasus->rx_skb) - goto tl_sched; - + if (pegasus->rx_skb == NULL) + printk("%s: rx_skb == NULL\n", __FUNCTION__); + /* + ** we are sure at this point pegasus->rx_skb != NULL + ** so we go ahead and pass up the packet. + */ skb_put(pegasus->rx_skb, pkt_len); pegasus->rx_skb->protocol = eth_type_trans(pegasus->rx_skb, net); netif_rx(pegasus->rx_skb); - - if (!(skb = dev_alloc_skb(PEGASUS_MTU + 2))) { - pegasus->rx_skb = NULL; - goto tl_sched; - } - - skb->dev = net; - skb_reserve(skb, 2); - pegasus->rx_skb = skb; pegasus->stats.rx_packets++; pegasus->stats.rx_bytes += pkt_len; + + spin_lock(&pegasus->rx_pool_lock); + pegasus->rx_skb = pull_skb(pegasus); + spin_unlock(&pegasus->rx_pool_lock); + + if (pegasus->rx_skb == NULL) + goto tl_sched; goon: FILL_BULK_URB(pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), @@ -587,11 +619,19 @@ pegasus = (pegasus_t *)data; + spin_lock_irq(&pegasus->rx_pool_lock); + fill_skb_pool(pegasus); + spin_unlock_irq(&pegasus->rx_pool_lock); if (pegasus->flags & PEGASUS_RX_URB_FAIL) if (pegasus->rx_skb) goto try_again; - - if (!(pegasus->rx_skb = dev_alloc_skb(PEGASUS_MTU + 2))) { + if (pegasus->rx_skb == NULL) { + spin_lock_irq(&pegasus->rx_pool_lock); + pegasus->rx_skb = pull_skb(pegasus); + spin_unlock_irq(&pegasus->rx_pool_lock); + } + if (pegasus->rx_skb == NULL) { + warn("wow, low on memory"); tasklet_schedule(&pegasus->rx_tl); return; } @@ -625,7 +665,6 @@ netif_wake_queue(pegasus->net); } -#ifdef PEGASUS_USE_INTR static void intr_callback(struct urb *urb) { pegasus_t *pegasus = urb->context; @@ -662,7 +701,6 @@ } } } -#endif static void pegasus_tx_timeout(struct net_device *net) { @@ -748,15 +786,62 @@ } +static void free_all_urbs(pegasus_t *pegasus) +{ + usb_free_urb(pegasus->intr_urb); + usb_free_urb(pegasus->tx_urb); + usb_free_urb(pegasus->rx_urb); + usb_free_urb(pegasus->ctrl_urb); +} + +static void unlink_all_urbs(pegasus_t *pegasus) +{ + usb_unlink_urb(pegasus->intr_urb); + usb_unlink_urb(pegasus->tx_urb); + usb_unlink_urb(pegasus->rx_urb); + usb_unlink_urb(pegasus->ctrl_urb); +} + +static int alloc_urbs(pegasus_t *pegasus) +{ + pegasus->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pegasus->ctrl_urb) { + return 0; + } + pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pegasus->rx_urb) { + usb_free_urb(pegasus->ctrl_urb); + return 0; + } + pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pegasus->tx_urb) { + usb_free_urb(pegasus->rx_urb); + usb_free_urb(pegasus->ctrl_urb); + return 0; + } + pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pegasus->intr_urb) { + usb_free_urb(pegasus->tx_urb); + usb_free_urb(pegasus->rx_urb); + usb_free_urb(pegasus->ctrl_urb); + return 0; + } + + return 1; +} + static int pegasus_open(struct net_device *net) { pegasus_t *pegasus = (pegasus_t *) net->priv; int res; - if (!(pegasus->rx_skb = dev_alloc_skb(PEGASUS_MTU + 2))) + if (pegasus->rx_skb == NULL) + pegasus->rx_skb = pull_skb(pegasus); + /* + ** Note: no point to free the pool. it is empty :-) + */ + if (!pegasus->rx_skb) return -ENOMEM; - pegasus->rx_skb->dev = net; - skb_reserve(pegasus->rx_skb, 2); down(&pegasus->sem); FILL_BULK_URB(pegasus->rx_urb, pegasus->usb, @@ -765,19 +850,20 @@ read_bulk_callback, pegasus); if ((res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL))) warn("%s: failed rx_urb %d", __FUNCTION__, res); -#ifdef PEGASUS_USE_INTR FILL_INT_URB(pegasus->intr_urb, pegasus->usb, usb_rcvintpipe(pegasus->usb, 3), pegasus->intr_buff, sizeof(pegasus->intr_buff), intr_callback, pegasus, pegasus->intr_interval); if ((res = usb_submit_urb(pegasus->intr_urb, GFP_KERNEL))) warn("%s: failed intr_urb %d", __FUNCTION__, res); -#endif netif_start_queue(net); pegasus->flags |= PEGASUS_RUNNING; if ((res = enable_net_traffic(net, pegasus->usb))) { err("can't enable_net_traffic() - %d", res); res = -EIO; + usb_unlink_urb(pegasus->rx_urb); + usb_unlink_urb(pegasus->intr_urb); + free_skb_pool(pegasus); goto exit; } set_carrier(net); @@ -797,13 +883,7 @@ netif_stop_queue(net); if (!(pegasus->flags & PEGASUS_UNPLUG)) disable_net_traffic(pegasus); - - usb_unlink_urb(pegasus->rx_urb); - usb_unlink_urb(pegasus->tx_urb); - usb_unlink_urb(pegasus->ctrl_urb); -#ifdef PEGASUS_USE_INTR - usb_unlink_urb(pegasus->intr_urb); -#endif + unlink_all_urbs(pegasus); up(&pegasus->sem); return 0; @@ -986,38 +1066,14 @@ pegasus->dev_index = dev_index; init_waitqueue_head(&pegasus->ctrl_wait); - pegasus->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pegasus->ctrl_urb) { - kfree(pegasus); - return NULL; - } - pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pegasus->rx_urb) { - usb_free_urb(pegasus->ctrl_urb); - kfree(pegasus); - return NULL; - } - pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pegasus->tx_urb) { - usb_free_urb(pegasus->rx_urb); - usb_free_urb(pegasus->ctrl_urb); - kfree(pegasus); - return NULL; - } - pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pegasus->intr_urb) { - usb_free_urb(pegasus->tx_urb); - usb_free_urb(pegasus->rx_urb); - usb_free_urb(pegasus->ctrl_urb); + if (!alloc_urbs(pegasus)) { kfree(pegasus); return NULL; } net = init_etherdev(NULL, 0); if (!net) { - usb_free_urb(pegasus->tx_urb); - usb_free_urb(pegasus->rx_urb); - usb_free_urb(pegasus->ctrl_urb); + free_all_urbs(pegasus); kfree(pegasus); return NULL; } @@ -1039,32 +1095,26 @@ net->set_multicast_list = pegasus_set_multicast; net->get_stats = pegasus_netdev_stats; net->mtu = PEGASUS_MTU; + spin_lock_init(&pegasus->rx_pool_lock); pegasus->features = usb_dev_id[dev_index].private; -#ifdef PEGASUS_USE_INTR get_interrupt_interval(pegasus); -#endif if (reset_mac(pegasus)) { err("can't reset MAC"); unregister_netdev(pegasus->net); - usb_free_urb(pegasus->tx_urb); - usb_free_urb(pegasus->rx_urb); - usb_free_urb(pegasus->ctrl_urb); + free_all_urbs(pegasus); kfree(pegasus->net); kfree(pegasus); pegasus = NULL; goto exit; } - - info("%s: %s", net->name, usb_dev_id[dev_index].name); - set_ethernet_addr(pegasus); - + fill_skb_pool(pegasus); + printk("%s: %s\n", net->name, usb_dev_id[dev_index].name); if (pegasus->features & PEGASUS_II) { info("setup Pegasus II specific registers"); setup_pegasus_II(pegasus); } - pegasus->phy = mii_phy_probe(pegasus); if (pegasus->phy == 0xff) { warn("can't locate MII phy, using default"); @@ -1087,14 +1137,9 @@ pegasus->flags |= PEGASUS_UNPLUG; unregister_netdev(pegasus->net); usb_put_dev(dev); - usb_unlink_urb(pegasus->intr_urb); - usb_unlink_urb(pegasus->tx_urb); - usb_unlink_urb(pegasus->rx_urb); - usb_unlink_urb(pegasus->ctrl_urb); - usb_free_urb(pegasus->intr_urb); - usb_free_urb(pegasus->tx_urb); - usb_free_urb(pegasus->rx_urb); - usb_free_urb(pegasus->ctrl_urb); + unlink_all_urbs(pegasus); + free_all_urbs(pegasus); + free_skb_pool(pegasus); if (pegasus->rx_skb) dev_kfree_skb(pegasus->rx_skb); kfree(pegasus->net); diff -Nru a/drivers/usb/net/pegasus.h b/drivers/usb/net/pegasus.h --- a/drivers/usb/net/pegasus.h Fri Jul 5 14:51:09 2002 +++ b/drivers/usb/net/pegasus.h Fri Jul 5 14:51:09 2002 @@ -2,18 +2,8 @@ * Copyright (c) 1999-2002 Petko Manolov - Petkan (petkan@users.sourceforge.net) * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. */ @@ -23,6 +13,7 @@ #define HAS_HOME_PNA 0x40000000 #define PEGASUS_MTU 1536 +#define RX_SKBS 4 #define EPROM_WRITE 0x01 #define EPROM_READ 0x02 @@ -100,10 +91,12 @@ int intr_interval; struct tasklet_struct rx_tl; struct urb *ctrl_urb, *rx_urb, *tx_urb, *intr_urb; + struct sk_buff *rx_pool[RX_SKBS]; struct sk_buff *rx_skb; struct usb_ctrlrequest dr; wait_queue_head_t ctrl_wait; struct semaphore sem; + spinlock_t rx_pool_lock; unsigned char intr_buff[8]; __u8 tx_buff[PEGASUS_MTU]; __u8 eth_regs[4]; diff -Nru a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c --- a/drivers/usb/net/rtl8150.c Fri Jul 5 14:51:09 2002 +++ b/drivers/usb/net/rtl8150.c Fri Jul 5 14:51:09 2002 @@ -1,15 +1,14 @@ /* - * Copyright (c) 2002 Petko Manolov (petkan@users.sourceforge.net) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. + * Copyright (c) 2002 Petko Manolov (petkan@users.sourceforge.net) * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. */ #include #include +#include #include #include #include @@ -19,7 +18,6 @@ #include #include #include -#include #include /* Version Information */ @@ -106,7 +104,7 @@ static void fill_skb_pool(rtl8150_t *); static void free_skb_pool(rtl8150_t *); -static struct sk_buff *pull_skb(rtl8150_t *); +static inline struct sk_buff *pull_skb(rtl8150_t *); static void rtl8150_disconnect(struct usb_device *dev, void *ptr); static void *rtl8150_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id); @@ -312,7 +310,7 @@ case -ENOENT: return; /* the urb is in unlink state */ case -ETIMEDOUT: - warn("reset needed may be?.."); + warn("may be reset is needed?.."); goto goon; default: warn("Rx status %d", urb->status); @@ -331,13 +329,13 @@ netif_rx(dev->rx_skb); dev->stats.rx_packets++; dev->stats.rx_bytes += pkt_len; - + + spin_lock(&dev->rx_pool_lock); skb = pull_skb(dev); + spin_unlock(&dev->rx_pool_lock); if (!skb) goto resched; - skb->dev = netdev; - skb_reserve(skb, 2); dev->rx_skb = skb; goon: FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), @@ -361,11 +359,16 @@ dev = (rtl8150_t *)data; + spin_lock_irq(&dev->rx_pool_lock); fill_skb_pool(dev); + spin_unlock_irq(&dev->rx_pool_lock); if (test_bit(RX_URB_FAIL, &dev->flags)) if (dev->rx_skb) goto try_again; - if (!(skb = pull_skb(dev))) + spin_lock_irq(&dev->rx_pool_lock); + skb = pull_skb(dev); + spin_unlock_irq(&dev->rx_pool_lock); + if (skb == NULL) goto tlsched; dev->rx_skb = skb; FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), @@ -426,51 +429,41 @@ { struct sk_buff *skb; int i; - unsigned long flags; - spin_lock_irqsave(&dev->rx_pool_lock, flags); for (i = 0; i < RX_SKB_POOL_SIZE; i++) { if (dev->rx_skb_pool[i]) continue; skb = dev_alloc_skb(RTL8150_MTU + 2); if (!skb) { - spin_unlock_irqrestore(&dev->rx_pool_lock, flags); return; } skb->dev = dev->netdev; skb_reserve(skb, 2); dev->rx_skb_pool[i] = skb; } - spin_unlock_irqrestore(&dev->rx_pool_lock, flags); } static void free_skb_pool(rtl8150_t *dev) { int i; - spin_lock_irq(&dev->rx_pool_lock); for (i = 0; i < RX_SKB_POOL_SIZE; i++) if (dev->rx_skb_pool[i]) dev_kfree_skb(dev->rx_skb_pool[i]); - spin_unlock_irq(&dev->rx_pool_lock); } -static struct sk_buff *pull_skb(rtl8150_t *dev) +static inline struct sk_buff *pull_skb(rtl8150_t *dev) { struct sk_buff *skb; int i; - unsigned long flags; - spin_lock_irqsave(&dev->rx_pool_lock, flags); for (i = 0; i < RX_SKB_POOL_SIZE; i++) { if (dev->rx_skb_pool[i]) { skb = dev->rx_skb_pool[i]; dev->rx_skb_pool[i] = NULL; - spin_unlock_irqrestore(&dev->rx_pool_lock, flags); return skb; } } - spin_unlock_irqrestore(&dev->rx_pool_lock, flags); return NULL; } @@ -578,8 +571,8 @@ if (dev == NULL) { return -ENODEV; } - - dev->rx_skb = pull_skb(dev); + if (dev->rx_skb == NULL) + dev->rx_skb = pull_skb(dev); if (!dev->rx_skb) return -ENOMEM; @@ -816,13 +809,13 @@ dev = NULL; } -static int __init usb_rtl8150_init(void) +int __init usb_rtl8150_init(void) { info(DRIVER_DESC " " DRIVER_VERSION); return usb_register(&rtl8150_driver); } -static void __exit usb_rtl8150_exit(void) +void __exit usb_rtl8150_exit(void) { usb_deregister(&rtl8150_driver); }