# 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.519   -> 1.520  
#	drivers/usb/net/kaweth.c	1.19    -> 1.20   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/06/18	oliver@neukum.name	1.520
# [PATCH] make kaweth use the sk_buff directly on tx
# 
# this change set against 2.5 will make kaweth put its private header
# into the sk_buff directly if possible or else allocate a temporary sk_buff.
# It saves memory and usually a copy.
# --------------------------------------------
#
diff -Nru a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c
--- a/drivers/usb/net/kaweth.c	Tue Jun 18 17:01:55 2002
+++ b/drivers/usb/net/kaweth.c	Tue Jun 18 17:01:55 2002
@@ -220,10 +220,11 @@
 	struct urb *rx_urb;
 	struct urb *tx_urb;
 	struct urb *irq_urb;
+	
+	struct sk_buff *tx_skb;
 
 	__u8 *firmware_buf;
 	__u8 scratch[KAWETH_SCRATCH_SIZE];
-	__u8 tx_buf[KAWETH_BUF_SIZE];
 	__u8 rx_buf[KAWETH_BUF_SIZE];
 	__u8 intbuffer[INTBUFFERSIZE];
 	__u16 packet_filter_bitmap;
@@ -650,11 +651,13 @@
 static void kaweth_usb_transmit_complete(struct urb *urb)
 {
 	struct kaweth_device *kaweth = urb->context;
+	struct sk_buff *skb = kaweth->tx_skb;
 
 	if (unlikely(urb->status != 0))
 		kaweth_dbg("%s: TX status %d.", kaweth->net->name, urb->status);
 
 	netif_wake_queue(kaweth->net);
+	dev_kfree_skb(skb);
 }
 
 /****************************************************************
@@ -663,7 +666,7 @@
 static int kaweth_start_xmit(struct sk_buff *skb, struct net_device *net)
 {
 	struct kaweth_device *kaweth = net->priv;
-	int count = skb->len;
+	char *private_header;
 
 	int res;
 
@@ -679,15 +682,30 @@
 	kaweth_async_set_rx_mode(kaweth);
 	netif_stop_queue(net);
 
-	*((__u16 *)kaweth->tx_buf) = cpu_to_le16(skb->len);
+	/* We now decide whether we can put our special header into the sk_buff */
+	if (skb_cloned(skb) || skb_headroom(skb) < 2) {
+		/* no such luck - we make our own */
+		struct sk_buff *copied_skb;
+		copied_skb = skb_copy_expand(skb, 2, 0, GFP_ATOMIC);
+		dev_kfree_skb_any(skb);
+		skb = copied_skb;
+		if (!copied_skb) {
+			kaweth->stats.tx_errors++;
+			netif_start_queue(net);
+			spin_unlock(&kaweth->device_lock);
+			return 0;
+		}
+	}
 
-	memcpy(kaweth->tx_buf + 2, skb->data, skb->len);
+	private_header = __skb_push(skb, 2);
+	*private_header = cpu_to_le16(skb->len);
+	kaweth->tx_skb = skb;
 
 	FILL_BULK_URB(kaweth->tx_urb,
 		      kaweth->dev,
 		      usb_sndbulkpipe(kaweth->dev, 2),
-		      kaweth->tx_buf,
-		      count + 2,
+		      private_header,
+		      skb->len,
 		      kaweth_usb_transmit_complete,
 		      kaweth);
 	kaweth->end = 0;
@@ -699,6 +717,7 @@
 		kaweth->stats.tx_errors++;
 
 		netif_start_queue(net);
+		dev_kfree_skb(skb);
 	}
 	else
 	{
@@ -706,8 +725,6 @@
 		kaweth->stats.tx_bytes += skb->len;
 		net->trans_start = jiffies;
 	}
-
-	dev_kfree_skb(skb);
 
 	spin_unlock(&kaweth->device_lock);
 
