--- usb-eth.c.orig	Sun Feb 18 19:47:51 2001
+++ usb-eth.c	Mon Feb 19 00:53:13 2001
@@ -11,6 +11,8 @@
  *
  * This is still work in progress...
  * 
+ * 19/02/2001 - Now we are compatible with generic usbnet driver. green@iXcelerator.com
+ * 
  */
 
 #include <linux/module.h>
@@ -23,14 +25,42 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#include <asm/unaligned.h>
 
 #include "usb_ctl.h"
 
 
 #define ETHERNET_PRODUCT_ID 0x505A
 
-struct usb_info_t usbd_info;
+/* FIXME - this should go into common include, once usbnet.c goes into 
+   standart kernel */
+// packets are always ethernet inside
+// ... except they can be bigger (up to 64K with this framing)
+#define MIN_PACKET      sizeof(struct ethhdr)
+#define MAX_PACKET      32768
+
+struct nc_header {		// packed:
+	u16	hdr_len;		// sizeof nc_header (LE, all)
+	u16	packet_len;		// payload size (including ethhdr)
+	u16	packet_id;		// detects dropped packets
+#define MIN_HEADER	6
+} __attribute__((__packed__));
+
+#define PAD_BYTE	((unsigned char)0xAC)
+struct nc_trailer {
+	u16	packet_id;
+} __attribute__((__packed__));
+
+#define FRAMED_SIZE(mtu) (sizeof (struct nc_header) \
+				+ sizeof (struct ethhdr) \
+				+ (mtu) \
+				+ 1 \
+				+ sizeof (struct nc_trailer))
+
+#define MIN_FRAMED	FRAMED_SIZE(0)
+
 
+struct usb_info_t usbd_info;
 
 static char usb_eth_name[16] = "usbf";
 static struct net_device usb_eth_device;
@@ -38,19 +68,22 @@
 static struct sk_buff *cur_rx_skb, *next_rx_skb;
 static volatile int terminating;
 
+static int usb_change_mtu (struct net_device *net, int new_mtu)
+{
+	if (new_mtu <= sizeof (struct ethhdr) || new_mtu > MAX_PACKET)
+		return -EINVAL;
+	if (FRAMED_SIZE (new_mtu) > MAX_PACKET)
+		return -EINVAL;
+	// no second zero-length packet read wanted after mtu-sized packets
+	net->mtu = new_mtu;
+	return 0;
+}
 
 static struct sk_buff * 
 usb_new_recv_skb(void)
 {
-	struct sk_buff *skb = dev_alloc_skb(1518);
+	struct sk_buff *skb = dev_alloc_skb((12 + FRAMED_SIZE (usb_eth_device.mtu)));
 	if (skb) {
-		/*
-		 * In order to align IP addresses to a word, we should 
-		 * reserve 2 bytes in addition to the 14-byte ethernet 
-		 * header. Since our packets come in with a 2-byte header
-		 * instead of the 12-byte MAC addresses, then we need: 
-		 * 	reserved = 14 + 2 - (14 - 12 + 2) = 12
-		 */
 		skb_reserve(skb, 12);
 	}
 	return skb;
@@ -60,8 +93,9 @@
 usb_recv_callback(int flag, int size)
 {
 	struct sk_buff *skb;
-	int pktlen, i;
-	char *mac_header;
+	int pktlen, templen;
+	struct nc_header	*header = NULL;
+	struct nc_trailer	*trailer = NULL;
 	
 	if (terminating) 
 		return;
@@ -72,9 +106,14 @@
 	/* flag validation */
 	if (flag == 0) {
 		skb_put(skb, size);
-		if (skb->len > 2) {
-			pktlen = le16_to_cpu(*(u16*)skb->data);
-			if (pktlen < 2 || pktlen > 1504) {
+		if (skb->len > MIN_FRAMED) {
+			header = (struct nc_header *) skb->data;
+			le16_to_cpus (&header->hdr_len);
+			le16_to_cpus (&header->packet_len);
+			if (header->hdr_len == MIN_HEADER ) {
+				pktlen = header->packet_len;
+			}
+			if (pktlen < MIN_FRAMED || pktlen > FRAMED_SIZE(usb_eth_device.mtu)) {
 				pktlen = 0;
 				usbd_info.eth_stats.rx_frame_errors++;
 			}
@@ -83,23 +122,16 @@
 		usbd_info.eth_stats.rx_errors++;
 	}
 
+	templen = pktlen + header->hdr_len + sizeof (struct nc_trailer);
+	templen += (templen & 0x01) ? 0:1;
 	/* validate packet length */
-	if (skb->len < pktlen) {
+	if (skb->len < templen ) {
 		/* packet not complete yet */
 		skb = NULL;
-	} else if (pktlen != 0 && skb->len > pktlen) {
-		pktlen = 0;
-		usbd_info.eth_stats.rx_over_errors++;
 	}
 	
 	if (pktlen == 0) {
-		/* 
-		 * Error due to bad frame, bad pktlen, etc.
-		 * Recycle the current skb and reset USB reception.
-		 */
-		skb_trim(skb, 0);
-		skb = NULL;
-		sa1100_usb_recv_reset();
+		goto error;
 	}
 	
 	/* 
@@ -128,24 +160,55 @@
 		next_rx_skb = skb;
 		return;
 	}
-	
-	usbd_info.eth_stats.rx_packets++;
-	usbd_info.eth_stats.rx_bytes += skb->len;
 
-	/* 
-	 * Put the mac addresses back in, over the top of the
-	 * size that was sent over.
-	 */
-	mac_header = skb_push(skb, (2*ETH_ALEN)-2);
-	for (i = 0; i < ETH_ALEN; i++) {
-		mac_header[i] = usbd_info.dev->dev_addr[i];
-		mac_header[i+ETH_ALEN] = usbd_info.host_addr[i];
+	if (FRAMED_SIZE (header->packet_len) > MAX_PACKET) {
+		usbd_info.eth_stats.rx_frame_errors++;
+		goto error;
+	}
+	skb_pull (skb, header->hdr_len);
+	trailer = (struct nc_trailer *)
+		(skb->data + skb->len - sizeof *trailer);
+	skb_trim (skb, skb->len - sizeof *trailer);
+
+	if ((header->packet_len & 0x01) == 0) {
+		if (skb->data [header->packet_len] != PAD_BYTE) {
+			usbd_info.eth_stats.rx_frame_errors++;
+			goto error;
+		}
+		skb_trim (skb, skb->len - 1);
+	}
+	if (skb->len != header->packet_len) {
+		usbd_info.eth_stats.rx_frame_errors++;
+		goto error;
+	}
+
+	if (header->packet_id != get_unaligned (&trailer->packet_id)) {
+		usbd_info.eth_stats.rx_fifo_errors++;
+		goto error;
 	}
 
-	skb->dev = usbd_info.dev;
-	skb->protocol = eth_type_trans(skb, skb->dev);
-	skb->ip_summed = CHECKSUM_UNNECESSARY;
-	netif_rx(skb);
+	if (skb->len) {
+		int     status;
+// FIXME: eth_copy_and_csum "small" packets to new SKB (small < ~200 bytes) ?
+
+		skb->dev = &usb_eth_device;
+		skb->protocol = eth_type_trans (skb, &usb_eth_device);
+		usbd_info.eth_stats.rx_packets++;
+		usbd_info.eth_stats.rx_bytes += skb->len;
+		status = netif_rx (skb);
+	} else {
+error:
+		usbd_info.eth_stats.rx_errors++;
+		/* 
+		 * Error due to bad frame, bad pktlen, etc.
+		 * Recycle the current skb and reset USB reception.
+		 */
+		skb_trim(cur_rx_skb, 0);
+		if (flag != -EINTR)
+			sa1100_usb_recv_reset();
+		sa1100_usb_recv(cur_rx_skb->tail, skb_tailroom(cur_rx_skb),
+			usb_recv_callback);
+	}
 }
 
 
@@ -191,24 +254,80 @@
 	netif_wake_queue(dev);
 }
 
+
+static inline struct sk_buff *fixup_skb (struct sk_buff *skb, int flags)
+{
+	int			padlen;
+	struct sk_buff		*skb2;
+
+	padlen = ((skb->len + sizeof (struct nc_header)
+			+ sizeof (struct nc_trailer)) & 0x01) ? 0 : 1;
+	if (!skb_cloned (skb)) {
+		int	headroom = skb_headroom (skb);
+		int	tailroom = skb_tailroom (skb);
+
+		if ((padlen + sizeof (struct nc_trailer)) <= tailroom
+			    && sizeof (struct nc_header) <= headroom)
+			return skb;
+
+		if ((sizeof (struct nc_header) + padlen
+					+ sizeof (struct nc_trailer)) <
+				(headroom + tailroom)) {
+			skb->data = memmove (skb->head
+						+ sizeof (struct nc_header),
+					    skb->data, skb->len);
+			skb->tail = skb->data + skb->len;
+			return skb;
+		}
+	}
+	skb2 = skb_copy_expand (skb,
+				sizeof (struct nc_header),
+				sizeof (struct nc_trailer) + padlen,
+				flags);
+	dev_kfree_skb_any (skb);
+	return skb2;
+}
+
 static int 
 usb_eth_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	int ret;
+	struct sk_buff  *skb2;
 	long flags;
+	int length = skb->len;
+	struct nc_header *header = 0;
+	struct nc_trailer *trailer = 0;
 	
 	if (next_tx_skb) {
 		printk("%s: called with next_tx_skb != NULL\n", __FUNCTION__);
 		return 1;
 	}
 
-	skb = skb_unshare(skb, GFP_ATOMIC);
-	if (!skb)
+	if (skb_shared (skb)) {
+		skb2 = skb_unshare(skb, GFP_ATOMIC);
+		if (!skb2) {
+			usbd_info.eth_stats.tx_dropped++;
+			dev_kfree_skb(skb);
+			return 1;
+		}
+		skb = skb2;
+	}
+
+	skb2 = fixup_skb (skb, GFP_ATOMIC);
+	if (!skb2) {
+		usbd_info.eth_stats.tx_dropped++;
+		dev_kfree_skb(skb);
 		return 1;
+	}
 	
-	skb_pull(skb, 10);
-	skb->data[0] = skb->len;
-	skb->data[1] = skb->len >> 8;
+	header = (struct nc_header *) skb_push (skb, sizeof *header);
+	header->hdr_len = cpu_to_le16 (sizeof (*header));
+	header->packet_len = cpu_to_le16 (length);
+	if (!((skb->len + sizeof *trailer) & 0x01))
+		*skb_put (skb, 1) = PAD_BYTE;
+	trailer = (struct nc_trailer *) skb_put (skb, sizeof *trailer);
+	header->packet_id = cpu_to_le16 (usbd_info.packet_id++);
+	put_unaligned (header->packet_id, &trailer->packet_id);
 
 	save_flags_cli(flags);
 	if (cur_tx_skb) {
@@ -316,6 +435,7 @@
 	usbd_info.host_addr[5] = 1;/*usbd_info.address;*/
 
 	dev->open = usb_eth_open;
+	dev->change_mtu = usb_change_mtu;
 	dev->stop = usb_eth_release;
 	dev->hard_start_xmit = usb_eth_xmit;
 	dev->get_stats = usb_eth_stats;
--- usb_ctl.c.orig	Sun Feb 18 19:47:51 2001
+++ usb_ctl.c	Mon Feb 19 00:43:15 2001
@@ -372,7 +372,8 @@
     buf[USB_ENDPDESC_BADDRESS] = USB_EP_ADDRESS(2, USB_IN);
     buf[USB_ENDPDESC_WMAXPKTSZE_LO] = (usbd_info.tx_pktsize & 0x00ff);
     buf[USB_ENDPDESC_WMAXPKTSZE_HI] = (usbd_info.tx_pktsize & 0xff00) >> 8;;
-#if defined(CONFIG_SA1100_USB_NETLINK) || defined(CONFIG_SA1100_USB_NETLINK_MODULE)
+#if 0
+//defined(CONFIG_SA1100_USB_NETLINK) || defined(CONFIG_SA1100_USB_NETLINK_MODULE)
     buf[USB_ENDPDESC_BMATTR] = USB_EP_INT;
     buf[USB_ENDPDESC_BINTERVAL] = 0x1; /* every 1ms */
 #else
@@ -522,14 +523,13 @@
    udc_enable();    
 
    /* clear stall - receiver seems to start stalled? 19Jan01ww */
-   UDC_clear(Ser0UDCCS1, UDCCS1_FST);
-   UDC_clear(Ser0UDCCS2, UDCCS2_FST);
+//   UDC_clear(Ser0UDCCS1, UDCCS1_FST);
+//   UDC_clear(Ser0UDCCS2, UDCCS2_FST);
 
    /* EXPERIMENT 10Feb01ww */
    /* clear all top-level sources */
    Ser0UDCSR = UDCSR_RSTIR | UDCSR_RESIR | UDCSR_EIR   |
 			   UDCSR_RIR   | UDCSR_TIR   | UDCSR_SUSIR ;
-   Ser0UDCCR  |= UDCCR_RESIM;	  // up and...
    /* END EXPERIMENT 10Feb01ww */
 
    /* enable interrupts */
--- usb_ctl.h.orig	Sun Feb 18 19:47:51 2001
+++ usb_ctl.h	Sun Feb 18 19:48:26 2001
@@ -80,6 +80,7 @@
     int rx_pktsize;
     struct usb_stats_t usb_stats;
 	struct net_device_stats eth_stats;  /* does not belong here 18Jan01ww */
+    u16 packet_id;
 };
 
 extern struct usb_info_t usbd_info;
--- usb_recv.c.orig	Sun Feb 18 19:47:51 2001
+++ usb_recv.c	Mon Feb 19 00:34:35 2001
@@ -117,16 +117,17 @@
 		}
 		
 		if (!ep1_curdmalen) {
-			UDC_flip(Ser0UDCCS1, UDCCS1_RPC);
 			printk("usb_recv: RPC for non-existent buffer\n");
+			naking=1;
 			return;
 		}
 		
 		sa1100_dma_stop(dmachn_rx);
 
 		if (status & UDCCS1_RPE) {
-		    // printk("usb_recv: RPError %x\n", status);
+		        printk("usb_recv: RPError %x\n", status);
 			UDC_flip(Ser0UDCCS1, UDCCS1_RPC);
+			pci_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen, PCI_DMA_FROMDEVICE);
 			ep1_done(-EIO);
 			return;
 		}
--- usb_send.c.orig	Wed Feb 14 13:29:18 2001
+++ usb_send.c	Mon Feb 19 00:22:19 2001
@@ -106,8 +106,8 @@
 			printk("usb_send: transmit error %x\n", status);
 			ep2_done(-EIO);
 		} else {
-			ep2_curdmapos += ep2_curdmalen;
-			ep2_remain -= ep2_curdmalen;
+			ep2_curdmapos += Ser0UDCIMP + 1; // this is workaround
+			ep2_remain -= Ser0UDCIMP + 1;    // for case when setting of Ser0UDCIMP was failed
 			if (ep2_remain != 0) {
 				ep2_start();
 			} else {

