## Automatically generated incremental diff
## From:   linux-2.5.69-bk5
## To:     linux-2.5.69-bk6
## Robot:  $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $

diff -urN linux-2.5.69-bk5/Makefile linux-2.5.69-bk6/Makefile
--- linux-2.5.69-bk5/Makefile	2003-05-11 04:43:30.000000000 -0700
+++ linux-2.5.69-bk6/Makefile	2003-05-11 04:43:33.000000000 -0700
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 5
 SUBLEVEL = 69
-EXTRAVERSION = -bk5
+EXTRAVERSION = -bk6
 
 # *DOCUMENTATION*
 # To see a list of typical targets execute "make help"
diff -urN linux-2.5.69-bk5/drivers/atm/he.c linux-2.5.69-bk6/drivers/atm/he.c
--- linux-2.5.69-bk5/drivers/atm/he.c	2003-05-11 04:43:30.000000000 -0700
+++ linux-2.5.69-bk6/drivers/atm/he.c	2003-05-11 04:43:33.000000000 -0700
@@ -362,43 +362,56 @@
 static int __devinit
 he_init_one(struct pci_dev *pci_dev, const struct pci_device_id *pci_ent)
 {
-	struct atm_dev *atm_dev;
-	struct he_dev *he_dev;
+	struct atm_dev *atm_dev = NULL;
+	struct he_dev *he_dev = NULL;
+	int err = 0;
 
 	printk(KERN_INFO "he: %s\n", version);
 
 	if (pci_enable_device(pci_dev)) return -EIO;
-	if (pci_set_dma_mask(pci_dev, HE_DMA_MASK) != 0)
-	{
+	if (pci_set_dma_mask(pci_dev, HE_DMA_MASK) != 0) {
 		printk(KERN_WARNING "he: no suitable dma available\n");
-		return -EIO;
+		err = -EIO;
+		goto init_one_failure;
 	}
 
 	atm_dev = atm_dev_register(DEV_LABEL, &he_ops, -1, 0);
-	if (!atm_dev) return -ENODEV;
+	if (!atm_dev) {
+		err = -ENODEV;
+		goto init_one_failure;
+	}
 	pci_set_drvdata(pci_dev, atm_dev);
 
 	he_dev = (struct he_dev *) kmalloc(sizeof(struct he_dev),
 							GFP_KERNEL);
-	if (!he_dev) return -ENOMEM;
+	if (!he_dev) {
+		err = -ENOMEM;
+		goto init_one_failure;
+	}
 	memset(he_dev, 0, sizeof(struct he_dev));
 
 	he_dev->pci_dev = pci_dev;
 	he_dev->atm_dev = atm_dev;
 	he_dev->atm_dev->dev_data = he_dev;
 	HE_DEV(atm_dev) = he_dev;
-	he_dev->number = atm_dev->number;	/* was devs */
+	he_dev->number = atm_dev->number;
 	if (he_start(atm_dev)) {
-		atm_dev_deregister(atm_dev);
 		he_stop(he_dev);
-		kfree(he_dev);
-		return -ENODEV;
+		err = -ENODEV;
+		goto init_one_failure;
 	}
 	he_dev->next = NULL;
 	if (he_devs) he_dev->next = he_devs;
 	he_devs = he_dev;
-
 	return 0;
+
+init_one_failure:
+	if (atm_dev)
+		atm_dev_deregister(atm_dev);
+	if (he_dev)
+		kfree(he_dev);
+	pci_disable_device(pci_dev);
+	return err;
 }
 
 static void __devexit
@@ -417,6 +430,7 @@
 	kfree(he_dev);
 
 	pci_set_drvdata(pci_dev, NULL);
+	pci_disable_device(pci_dev);
 }
 
 
diff -urN linux-2.5.69-bk5/drivers/atm/iphase.c linux-2.5.69-bk6/drivers/atm/iphase.c
--- linux-2.5.69-bk5/drivers/atm/iphase.c	2003-05-11 04:43:30.000000000 -0700
+++ linux-2.5.69-bk6/drivers/atm/iphase.c	2003-05-11 04:43:33.000000000 -0700
@@ -2777,7 +2777,7 @@
 	     if (!capable(CAP_NET_ADMIN)) return -EPERM;
              tmps = (u16 *)ia_cmds.buf;
              for(i=0; i<0x80; i+=2, tmps++)
-                if(put_user(*(u16*)(iadev->seg_reg+i), tmps)) return -EFAULT;
+                if(put_user((u16)(readl(iadev->seg_reg+i) & 0xffff), tmps)) return -EFAULT;
              ia_cmds.status = 0;
              ia_cmds.len = 0x80;
              break;
@@ -2785,7 +2785,7 @@
 	     if (!capable(CAP_NET_ADMIN)) return -EPERM;
              tmps = (u16 *)ia_cmds.buf;
              for(i=0; i<0x80; i+=2, tmps++)
-                if(put_user(*(u16*)(iadev->reass_reg+i), tmps)) return -EFAULT;
+                if(put_user((u16)(readl(iadev->reass_reg+i) & 0xffff), tmps)) return -EFAULT;
              ia_cmds.status = 0;
              ia_cmds.len = 0x80;
              break;
@@ -2802,10 +2802,10 @@
 	     rfL = &regs_local->rfredn;
              /* Copy real rfred registers into the local copy */
  	     for (i=0; i<(sizeof (rfredn_t))/4; i++)
-                ((u_int *)rfL)[i] = ((u_int *)iadev->reass_reg)[i] & 0xffff;
+                ((u_int *)rfL)[i] = readl(iadev->reass_reg + i) & 0xffff;
              	/* Copy real ffred registers into the local copy */
 	     for (i=0; i<(sizeof (ffredn_t))/4; i++)
-                ((u_int *)ffL)[i] = ((u_int *)iadev->seg_reg)[i] & 0xffff;
+                ((u_int *)ffL)[i] = readl(iadev->seg_reg + i) & 0xffff;
 
              if (copy_to_user(ia_cmds.buf, regs_local,sizeof(ia_regs_t))) {
                 kfree(regs_local);
diff -urN linux-2.5.69-bk5/drivers/base/class.c linux-2.5.69-bk6/drivers/base/class.c
--- linux-2.5.69-bk5/drivers/base/class.c	2003-05-04 16:53:56.000000000 -0700
+++ linux-2.5.69-bk6/drivers/base/class.c	2003-05-11 04:43:33.000000000 -0700
@@ -133,6 +133,21 @@
 		sysfs_remove_link(&class_dev->kobj, "device");
 }
 
+static int class_device_driver_link(struct class_device * class_dev)
+{
+	if ((class_dev->dev) && (class_dev->dev->driver))
+		return sysfs_create_link(&class_dev->kobj,
+					 &class_dev->dev->driver->kobj, "driver");
+	return 0;
+}
+
+static void class_device_driver_unlink(struct class_device * class_dev)
+{
+	if ((class_dev->dev) && (class_dev->dev->driver))
+		sysfs_remove_link(&class_dev->kobj, "driver");
+}
+
+
 #define to_class_dev(obj) container_of(obj,struct class_device,kobj)
 #define to_class_dev_attr(_attr) container_of(_attr,struct class_device_attribute,attr)
 
@@ -244,7 +259,6 @@
 
 	/* first, register with generic layer. */
 	strncpy(class_dev->kobj.name, class_dev->class_id, KOBJ_NAME_LEN);
-	kobj_set_kset_s(class_dev, class_subsys);
 	kobj_set_kset_s(class_dev, class_obj_subsys);
 	if (parent)
 		class_dev->kobj.parent = &parent->subsys.kset.kobj;
@@ -265,6 +279,7 @@
 	}
 
 	class_device_dev_link(class_dev);
+	class_device_driver_link(class_dev);
 
  register_done:
 	if (error && parent)
@@ -298,6 +313,7 @@
 
 	if (class_dev->dev) {
 		class_device_dev_unlink(class_dev);
+		class_device_driver_unlink(class_dev);
 		put_device(class_dev->dev);
 	}
 	
diff -urN linux-2.5.69-bk5/drivers/block/deadline-iosched.c linux-2.5.69-bk6/drivers/block/deadline-iosched.c
--- linux-2.5.69-bk5/drivers/block/deadline-iosched.c	2003-05-04 16:53:37.000000000 -0700
+++ linux-2.5.69-bk6/drivers/block/deadline-iosched.c	2003-05-11 04:43:33.000000000 -0700
@@ -28,7 +28,7 @@
 static int fifo_batch = 16;       /* # of sequential requests treated as one
 				     by the above parameters. For throughput. */
 
-static const int deadline_hash_shift = 10;
+static const int deadline_hash_shift = 5;
 #define DL_HASH_BLOCK(sec)	((sec) >> 3)
 #define DL_HASH_FN(sec)		(hash_long(DL_HASH_BLOCK((sec)), deadline_hash_shift))
 #define DL_HASH_ENTRIES		(1 << deadline_hash_shift)
@@ -71,6 +71,8 @@
 	int fifo_batch;
 	int writes_starved;
 	int front_merges;
+
+	mempool_t *drq_pool;
 };
 
 /*
@@ -130,6 +132,21 @@
 	list_add(&drq->hash, &dd->hash[DL_HASH_FN(rq_hash_key(rq))]);
 }
 
+/*
+ * move hot entry to front of chain
+ */
+static inline void
+deadline_hot_drq_hash(struct deadline_data *dd, struct deadline_rq *drq)
+{
+	struct request *rq = drq->request;
+	struct list_head *head = &dd->hash[DL_HASH_FN(rq_hash_key(rq))];
+
+	if (ON_HASH(drq) && drq->hash.prev != head) {
+		list_del(&drq->hash);
+		list_add(&drq->hash, head);
+	}
+}
+
 static struct request *
 deadline_find_drq_hash(struct deadline_data *dd, sector_t offset)
 {
@@ -353,6 +370,8 @@
 out:
 	q->last_merge = &__rq->queuelist;
 out_insert:
+	if (ret)
+		deadline_hot_drq_hash(dd, RQ_DATA(__rq));
 	*insert = &__rq->queuelist;
 	return ret;
 }
@@ -673,28 +692,11 @@
 static void deadline_exit(request_queue_t *q, elevator_t *e)
 {
 	struct deadline_data *dd = e->elevator_data;
-	struct deadline_rq *drq;
-	struct request *rq;
-	int i;
 
 	BUG_ON(!list_empty(&dd->fifo_list[READ]));
 	BUG_ON(!list_empty(&dd->fifo_list[WRITE]));
 
-	for (i = READ; i <= WRITE; i++) {
-		struct request_list *rl = &q->rq[i];
-		struct list_head *entry;
-
-		list_for_each(entry, &rl->free) {
-			rq = list_entry_rq(entry);
-
-			if ((drq = RQ_DATA(rq)) == NULL)
-				continue;
-
-			rq->elevator_private = NULL;
-			kmem_cache_free(drq_pool, drq);
-		}
-	}
-
+	mempool_destroy(dd->drq_pool);
 	kfree(dd->hash);
 	kfree(dd);
 }
@@ -706,9 +708,7 @@
 static int deadline_init(request_queue_t *q, elevator_t *e)
 {
 	struct deadline_data *dd;
-	struct deadline_rq *drq;
-	struct request *rq;
-	int i, ret = 0;
+	int i;
 
 	if (!drq_pool)
 		return -ENOMEM;
@@ -724,6 +724,13 @@
 		return -ENOMEM;
 	}
 
+	dd->drq_pool = mempool_create(BLKDEV_MIN_RQ, mempool_alloc_slab, mempool_free_slab, drq_pool);
+	if (!dd->drq_pool) {
+		kfree(dd->hash);
+		kfree(dd);
+		return -ENOMEM;
+	}
+
 	for (i = 0; i < DL_HASH_ENTRIES; i++)
 		INIT_LIST_HEAD(&dd->hash[i]);
 
@@ -739,33 +746,41 @@
 	dd->front_merges = 1;
 	dd->fifo_batch = fifo_batch;
 	e->elevator_data = dd;
+	return 0;
+}
 
-	for (i = READ; i <= WRITE; i++) {
-		struct request_list *rl = &q->rq[i];
-		struct list_head *entry;
-
-		list_for_each(entry, &rl->free) {
-			rq = list_entry_rq(entry);
-
-			drq = kmem_cache_alloc(drq_pool, GFP_KERNEL);
-			if (!drq) {
-				ret = -ENOMEM;
-				break;
-			}
+static void deadline_put_request(request_queue_t *q, struct request *rq)
+{
+	struct deadline_data *dd = q->elevator.elevator_data;
+	struct deadline_rq *drq = RQ_DATA(rq);
 
-			memset(drq, 0, sizeof(*drq));
-			INIT_LIST_HEAD(&drq->fifo);
-			INIT_LIST_HEAD(&drq->hash);
-			RB_CLEAR(&drq->rb_node);
-			drq->request = rq;
-			rq->elevator_private = drq;
-		}
+	if (drq) {
+		mempool_free(drq, dd->drq_pool);
+		rq->elevator_private = NULL;
 	}
+}
 
-	if (ret)
-		deadline_exit(q, e);
+static int
+deadline_set_request(request_queue_t *q, struct request *rq, int gfp_mask)
+{
+	struct deadline_data *dd = q->elevator.elevator_data;
+	struct deadline_rq *drq;
 
-	return ret;
+	drq = mempool_alloc(dd->drq_pool, gfp_mask);
+	if (drq) {
+		RB_CLEAR(&drq->rb_node);
+		drq->request = rq;
+
+		INIT_LIST_HEAD(&drq->hash);
+		drq->hash_valid_count = 0;
+
+		INIT_LIST_HEAD(&drq->fifo);
+
+		rq->elevator_private = drq;
+		return 0;
+	}
+
+	return 1;
 }
 
 /*
@@ -916,6 +931,8 @@
 	.elevator_queue_empty_fn =	deadline_queue_empty,
 	.elevator_former_req_fn =	deadline_former_request,
 	.elevator_latter_req_fn =	deadline_latter_request,
+	.elevator_set_req_fn =		deadline_set_request,
+	.elevator_put_req_fn = 		deadline_put_request,
 	.elevator_init_fn =		deadline_init,
 	.elevator_exit_fn =		deadline_exit,
 
diff -urN linux-2.5.69-bk5/drivers/block/elevator.c linux-2.5.69-bk6/drivers/block/elevator.c
--- linux-2.5.69-bk5/drivers/block/elevator.c	2003-05-04 16:53:37.000000000 -0700
+++ linux-2.5.69-bk6/drivers/block/elevator.c	2003-05-11 04:43:33.000000000 -0700
@@ -408,6 +408,25 @@
 	return NULL;
 }
 
+int elv_set_request(request_queue_t *q, struct request *rq, int gfp_mask)
+{
+	elevator_t *e = &q->elevator;
+
+	if (e->elevator_set_req_fn)
+		return e->elevator_set_req_fn(q, rq, gfp_mask);
+
+	rq->elevator_private = NULL;
+	return 0;
+}
+
+void elv_put_request(request_queue_t *q, struct request *rq)
+{
+	elevator_t *e = &q->elevator;
+
+	if (e->elevator_put_req_fn)
+		e->elevator_put_req_fn(q, rq);
+}
+
 int elv_register_queue(struct gendisk *disk)
 {
 	request_queue_t *q = disk->queue;
diff -urN linux-2.5.69-bk5/drivers/block/ll_rw_blk.c linux-2.5.69-bk6/drivers/block/ll_rw_blk.c
--- linux-2.5.69-bk5/drivers/block/ll_rw_blk.c	2003-05-04 16:53:02.000000000 -0700
+++ linux-2.5.69-bk6/drivers/block/ll_rw_blk.c	2003-05-11 04:43:33.000000000 -0700
@@ -48,12 +48,6 @@
  */
 static int queue_nr_requests;
 
-/*
- * How many free requests must be available before we wake a process which
- * is waiting for a request?
- */
-static int batch_requests;
-
 unsigned long blk_max_low_pfn, blk_max_pfn;
 int blk_nohighio = 0;
 
@@ -419,7 +413,7 @@
 {
 	struct blk_queue_tag *bqt = q->queue_tags;
 
-	if(unlikely(bqt == NULL || bqt->max_depth < tag))
+	if (unlikely(bqt == NULL || bqt->max_depth < tag))
 		return NULL;
 
 	return bqt->tag_index[tag];
@@ -1122,26 +1116,6 @@
 	spin_unlock_irq(&blk_plug_lock);
 }
 
-static int __blk_cleanup_queue(struct request_list *list)
-{
-	struct list_head *head = &list->free;
-	struct request *rq;
-	int i = 0;
-
-	while (!list_empty(head)) {
-		rq = list_entry(head->next, struct request, queuelist);
-		list_del_init(&rq->queuelist);
-		kmem_cache_free(request_cachep, rq);
-		i++;
-	}
-
-	if (i != list->count)
-		printk("request list leak!\n");
-
-	list->count = 0;
-	return i;
-}
-
 /**
  * blk_cleanup_queue: - release a &request_queue_t when it is no longer needed
  * @q:    the request queue to be released
@@ -1158,18 +1132,14 @@
  **/
 void blk_cleanup_queue(request_queue_t * q)
 {
-	int count = (queue_nr_requests*2);
+	struct request_list *rl = &q->rq;
 
 	elevator_exit(q);
 
-	count -= __blk_cleanup_queue(&q->rq[READ]);
-	count -= __blk_cleanup_queue(&q->rq[WRITE]);
-
 	del_timer_sync(&q->unplug_timer);
 	flush_scheduled_work();
 
-	if (count)
-		printk("blk_cleanup_queue: leaked requests (%d)\n", count);
+	mempool_destroy(rl->rq_pool);
 
 	if (blk_queue_tagged(q))
 		blk_queue_free_tags(q);
@@ -1179,42 +1149,16 @@
 
 static int blk_init_free_list(request_queue_t *q)
 {
-	struct request_list *rl;
-	struct request *rq;
-	int i;
+	struct request_list *rl = &q->rq;
 
-	INIT_LIST_HEAD(&q->rq[READ].free);
-	INIT_LIST_HEAD(&q->rq[WRITE].free);
-	q->rq[READ].count = 0;
-	q->rq[WRITE].count = 0;
+	rl->count[READ] = rl->count[WRITE] = 0;
 
-	/*
-	 * Divide requests in half between read and write
-	 */
-	rl = &q->rq[READ];
-	for (i = 0; i < (queue_nr_requests*2); i++) {
-		rq = kmem_cache_alloc(request_cachep, SLAB_KERNEL);
-		if (!rq)
-			goto nomem;
+	rl->rq_pool = mempool_create(BLKDEV_MIN_RQ, mempool_alloc_slab, mempool_free_slab, request_cachep);
 
-		/*
-		 * half way through, switch to WRITE list
-		 */
-		if (i == queue_nr_requests)
-			rl = &q->rq[WRITE];
-
-		memset(rq, 0, sizeof(struct request));
-		rq->rq_status = RQ_INACTIVE;
-		list_add(&rq->queuelist, &rl->free);
-		rl->count++;
-	}
+	if (!rl->rq_pool)
+		return -ENOMEM;
 
-	init_waitqueue_head(&q->rq[READ].wait);
-	init_waitqueue_head(&q->rq[WRITE].wait);
 	return 0;
-nomem:
-	blk_cleanup_queue(q);
-	return 1;
 }
 
 static int __make_request(request_queue_t *, struct bio *);
@@ -1276,41 +1220,79 @@
 	blk_queue_max_hw_segments(q, MAX_HW_SEGMENTS);
 	blk_queue_max_phys_segments(q, MAX_PHYS_SEGMENTS);
 
-	INIT_LIST_HEAD(&q->plug_list);
-
 	return 0;
 }
 
+static inline void blk_free_request(request_queue_t *q, struct request *rq)
+{
+	elv_put_request(q, rq);
+	mempool_free(rq, q->rq.rq_pool);
+}
+
+static inline struct request *blk_alloc_request(request_queue_t *q,int gfp_mask)
+{
+	struct request *rq = mempool_alloc(q->rq.rq_pool, gfp_mask);
+
+	if (!rq)
+		return NULL;
+
+	if (!elv_set_request(q, rq, gfp_mask))
+		return rq;
+
+	mempool_free(rq, q->rq.rq_pool);
+	return NULL;
+}
+
 #define blkdev_free_rq(list) list_entry((list)->next, struct request, queuelist)
 /*
- * Get a free request. queue lock must be held and interrupts
- * disabled on the way in.
+ * Get a free request, queue_lock must not be held
  */
-static struct request *get_request(request_queue_t *q, int rw)
+static struct request *get_request(request_queue_t *q, int rw, int gfp_mask)
 {
 	struct request *rq = NULL;
-	struct request_list *rl = q->rq + rw;
+	struct request_list *rl = &q->rq;
 
-	if (!list_empty(&rl->free)) {
-		rq = blkdev_free_rq(&rl->free);
-		list_del_init(&rq->queuelist);
-		rq->ref_count = 1;
-		rl->count--;
-		if (rl->count < queue_congestion_on_threshold())
-			set_queue_congested(q, rw);
-		rq->flags = 0;
-		rq->rq_status = RQ_ACTIVE;
-		rq->errors = 0;
-		rq->special = NULL;
-		rq->buffer = NULL;
-		rq->data = NULL;
-		rq->sense = NULL;
-		rq->waiting = NULL;
-		rq->bio = rq->biotail = NULL;
-		rq->q = q;
-		rq->rl = rl;
+	spin_lock_irq(q->queue_lock);
+	if (rl->count[rw] == BLKDEV_MAX_RQ) {
+		spin_unlock_irq(q->queue_lock);
+		goto out;
 	}
+	rl->count[rw]++;
+	if ((BLKDEV_MAX_RQ - rl->count[rw]) < queue_congestion_on_threshold())
+		set_queue_congested(q, rw);
+	spin_unlock_irq(q->queue_lock);
 
+	rq = blk_alloc_request(q, gfp_mask);
+	if (!rq) {
+		spin_lock_irq(q->queue_lock);
+		rl->count[rw]--;
+		if ((BLKDEV_MAX_RQ - rl->count[rw]) >= queue_congestion_off_threshold())
+                        clear_queue_congested(q, rw);
+		spin_unlock_irq(q->queue_lock);
+		goto out;
+	}
+	
+	INIT_LIST_HEAD(&rq->queuelist);
+
+	/*
+	 * first three bits are identical in rq->flags and bio->bi_rw,
+	 * see bio.h and blkdev.h
+	 */
+	rq->flags = rw;
+
+	rq->errors = 0;
+	rq->rq_status = RQ_ACTIVE;
+	rq->bio = rq->biotail = NULL;
+	rq->buffer = NULL;
+	rq->ref_count = 1;
+	rq->q = q;
+	rq->rl = rl;
+	rq->waiting = NULL;
+	rq->special = NULL;
+	rq->data = NULL;
+	rq->sense = NULL;
+
+out:
 	return rq;
 }
 
@@ -1319,31 +1301,16 @@
  */
 static struct request *get_request_wait(request_queue_t *q, int rw)
 {
-	DEFINE_WAIT(wait);
-	struct request_list *rl = &q->rq[rw];
 	struct request *rq;
 
-	spin_lock_prefetch(q->queue_lock);
-
 	generic_unplug_device(q);
 	do {
-		int block = 0;
-
-		prepare_to_wait_exclusive(&rl->wait, &wait,
-					TASK_UNINTERRUPTIBLE);
-		spin_lock_irq(q->queue_lock);
-		if (!rl->count)
-			block = 1;
-		spin_unlock_irq(q->queue_lock);
+		rq = get_request(q, rw, GFP_NOIO);
 
-		if (block)
-			io_schedule();
-		finish_wait(&rl->wait, &wait);
+		if (!rq)
+			blk_congestion_wait(rw, HZ / 50);
+	} while (!rq);
 
-		spin_lock_irq(q->queue_lock);
-		rq = get_request(q, rw);
-		spin_unlock_irq(q->queue_lock);
-	} while (rq == NULL);
 	return rq;
 }
 
@@ -1353,39 +1320,11 @@
 
 	BUG_ON(rw != READ && rw != WRITE);
 
-	spin_lock_irq(q->queue_lock);
-	rq = get_request(q, rw);
-	spin_unlock_irq(q->queue_lock);
+	rq = get_request(q, rw, gfp_mask);
 
 	if (!rq && (gfp_mask & __GFP_WAIT))
 		rq = get_request_wait(q, rw);
 
-	if (rq) {
-		rq->flags = 0;
-		rq->buffer = NULL;
-		rq->bio = rq->biotail = NULL;
-		rq->waiting = NULL;
-	}
-	return rq;
-}
-
-/*
- * Non-locking blk_get_request variant, for special requests from drivers.
- */
-struct request *__blk_get_request(request_queue_t *q, int rw)
-{
-	struct request *rq;
-
-	BUG_ON(rw != READ && rw != WRITE);
-
-	rq = get_request(q, rw);
-
-	if (rq) {
-		rq->flags = 0;
-		rq->buffer = NULL;
-		rq->bio = rq->biotail = NULL;
-		rq->waiting = NULL;
-	}
 	return rq;
 }
 
@@ -1503,14 +1442,17 @@
 	disk->stamp_idle = now;
 }
 
+/*
+ * queue lock must be held
+ */
 void __blk_put_request(request_queue_t *q, struct request *req)
 {
 	struct request_list *rl = req->rl;
 
-	if (unlikely(--req->ref_count))
-		return;
 	if (unlikely(!q))
 		return;
+	if (unlikely(--req->ref_count))
+		return;
 
 	req->rq_status = RQ_INACTIVE;
 	req->q = NULL;
@@ -1521,24 +1463,15 @@
 	 * it didn't come out of our reserved rq pools
 	 */
 	if (rl) {
-		int rw = 0;
+		int rw = rq_data_dir(req);
 
 		BUG_ON(!list_empty(&req->queuelist));
 
-		list_add(&req->queuelist, &rl->free);
+		blk_free_request(q, req);
 
-		if (rl == &q->rq[WRITE])
-			rw = WRITE;
-		else if (rl == &q->rq[READ])
-			rw = READ;
-		else
-			BUG();
-
-		rl->count++;
-		if (rl->count >= queue_congestion_off_threshold())
+		rl->count[rw]--;
+		if ((BLKDEV_MAX_RQ - rl->count[rw]) >= queue_congestion_off_threshold())
 			clear_queue_congested(q, rw);
-		if (rl->count >= batch_requests && waitqueue_active(&rl->wait))
-			wake_up(&rl->wait);
 	}
 }
 
@@ -1605,24 +1538,23 @@
 	 * will have updated segment counts, update sector
 	 * counts here.
 	 */
-	if (q->merge_requests_fn(q, req, next)) {
-		req->biotail->bi_next = next->bio;
-		req->biotail = next->biotail;
+	if (!q->merge_requests_fn(q, req, next))
+		return 0;
 
-		req->nr_sectors = req->hard_nr_sectors += next->hard_nr_sectors;
+	req->biotail->bi_next = next->bio;
+	req->biotail = next->biotail;
 
-		elv_merge_requests(q, req, next);
+	req->nr_sectors = req->hard_nr_sectors += next->hard_nr_sectors;
 
-		if (req->rq_disk) {
-			disk_round_stats(req->rq_disk);
-			disk_stat_dec(req->rq_disk, in_flight);
-		}
+	elv_merge_requests(q, req, next);
 
-		__blk_put_request(q, next);
-		return 1;
+	if (req->rq_disk) {
+		disk_round_stats(req->rq_disk);
+		disk_stat_dec(req->rq_disk, in_flight);
 	}
 
-	return 0;
+	__blk_put_request(q, next);
+	return 1;
 }
 
 static inline int attempt_back_merge(request_queue_t *q, struct request *rq)
@@ -1698,9 +1630,9 @@
 
 	barrier = test_bit(BIO_RW_BARRIER, &bio->bi_rw);
 
-	spin_lock_irq(q->queue_lock);
 again:
 	insert_here = NULL;
+	spin_lock_irq(q->queue_lock);
 
 	if (elv_queue_empty(q)) {
 		blk_plug_device(q);
@@ -1776,17 +1708,17 @@
 	if (freereq) {
 		req = freereq;
 		freereq = NULL;
-	} else if ((req = get_request(q, rw)) == NULL) {
+	} else {
 		spin_unlock_irq(q->queue_lock);
-
-		/*
-		 * READA bit set
-		 */
-		if (bio_flagged(bio, BIO_RW_AHEAD))
-			goto end_io;
-
-		freereq = get_request_wait(q, rw);
-		spin_lock_irq(q->queue_lock);
+		if ((freereq = get_request(q, rw, GFP_ATOMIC)) == NULL) {
+			/*
+			 * READA bit set
+			 */
+			if (bio_flagged(bio, BIO_RW_AHEAD))
+				goto end_io;
+	
+			freereq = get_request_wait(q, rw);
+		}
 		goto again;
 	}
 
@@ -1813,14 +1745,15 @@
 	req->bio = req->biotail = bio;
 	req->rq_disk = bio->bi_bdev->bd_disk;
 	req->start_time = jiffies;
+
 	add_request(q, req, insert_here);
 out:
 	if (freereq)
 		__blk_put_request(q, freereq);
 
 	if (blk_queue_plugged(q)) {
-		int nr_queued = (queue_nr_requests - q->rq[0].count) +
-				(queue_nr_requests - q->rq[1].count);
+		int nr_queued = q->rq.count[0] + q->rq.count[1];
+
 		if (nr_queued == q->unplug_thresh)
 			__generic_unplug_device(q);
 	}
@@ -2213,7 +2146,6 @@
 
 int __init blk_dev_init(void)
 {
-	int total_ram = nr_free_pages() << (PAGE_SHIFT - 10);
 	int i;
 
 	request_cachep = kmem_cache_create("blkdev_requests",
@@ -2221,24 +2153,11 @@
 	if (!request_cachep)
 		panic("Can't create request pool slab cache\n");
 
-	/*
-	 * Free request slots per queue.  One per quarter-megabyte.
-	 * We use this many requests for reads, and this many for writes.
-	 */
-	queue_nr_requests = (total_ram >> 9) & ~7;
-	if (queue_nr_requests < 16)
-		queue_nr_requests = 16;
-	if (queue_nr_requests > 128)
-		queue_nr_requests = 128;
-
-	batch_requests = queue_nr_requests / 8;
-	if (batch_requests > 8)
-		batch_requests = 8;
+	queue_nr_requests = BLKDEV_MAX_RQ;
 
 	printk("block request queues:\n");
-	printk(" %d requests per read queue\n", queue_nr_requests);
-	printk(" %d requests per write queue\n", queue_nr_requests);
-	printk(" %d requests per batch\n", batch_requests);
+	printk(" %d/%d requests per read queue\n", BLKDEV_MIN_RQ, queue_nr_requests);
+	printk(" %d/%d requests per write queue\n", BLKDEV_MIN_RQ, queue_nr_requests);
 	printk(" enter congestion at %d\n", queue_congestion_on_threshold());
 	printk(" exit congestion at %d\n", queue_congestion_off_threshold());
 
@@ -2281,7 +2200,6 @@
 EXPORT_SYMBOL(blk_phys_contig_segment);
 EXPORT_SYMBOL(blk_hw_contig_segment);
 EXPORT_SYMBOL(blk_get_request);
-EXPORT_SYMBOL(__blk_get_request);
 EXPORT_SYMBOL(blk_put_request);
 EXPORT_SYMBOL(blk_insert_request);
 
diff -urN linux-2.5.69-bk5/drivers/i2c/chips/it87.c linux-2.5.69-bk6/drivers/i2c/chips/it87.c
--- linux-2.5.69-bk5/drivers/i2c/chips/it87.c	2003-05-11 04:43:30.000000000 -0700
+++ linux-2.5.69-bk6/drivers/i2c/chips/it87.c	2003-05-11 04:43:33.000000000 -0700
@@ -80,17 +80,17 @@
 
 /* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */
 
-#define IT87_REG_FAN(nr)       (0x0c + (nr))
-#define IT87_REG_FAN_MIN(nr)   (0x0f + (nr))
+#define IT87_REG_FAN(nr)       (0x0d + (nr))
+#define IT87_REG_FAN_MIN(nr)   (0x10 + (nr))
 #define IT87_REG_FAN_CTRL      0x13
 
 #define IT87_REG_VIN(nr)       (0x20 + (nr))
-#define IT87_REG_TEMP(nr)      (0x28 + (nr))
+#define IT87_REG_TEMP(nr)      (0x29 + (nr))
 
 #define IT87_REG_VIN_MAX(nr)   (0x30 + (nr) * 2)
 #define IT87_REG_VIN_MIN(nr)   (0x31 + (nr) * 2)
-#define IT87_REG_TEMP_HIGH(nr) (0x3e + (nr) * 2)
-#define IT87_REG_TEMP_LOW(nr)  (0x3f + (nr) * 2)
+#define IT87_REG_TEMP_HIGH(nr) (0x40 + ((nr) * 2))
+#define IT87_REG_TEMP_LOW(nr)  (0x41 + ((nr) * 2))
 
 #define IT87_REG_I2C_ADDR      0x48
 
@@ -99,46 +99,8 @@
 
 #define IT87_REG_CHIPID        0x58
 
-static inline u8 IN_TO_REG(long val, int inNum)
-{
-	/* to avoid floating point, we multiply everything by 100.
-	 val is guaranteed to be positive, so we can achieve the effect of 
-	 rounding by (...*10+5)/10.  Note that the *10 is hidden in the 
-	 /250 (which should really be /2500).
-	 At the end, we need to /100 because we *100 everything and we need
-	 to /10 because of the rounding thing, so we /1000.   */
-	if (inNum <= 1)
-		return (u8)
-		    SENSORS_LIMIT(((val * 210240 - 13300) / 250 + 5) / 1000, 
-				  0, 255);
-	else if (inNum == 2)
-		return (u8)
-		    SENSORS_LIMIT(((val * 157370 - 13300) / 250 + 5) / 1000, 
-				  0, 255);
-	else if (inNum == 3)
-		return (u8)
-		    SENSORS_LIMIT(((val * 101080 - 13300) / 250 + 5) / 1000, 
-				  0, 255);
-	else
-		return (u8) SENSORS_LIMIT(((val * 41714 - 13300) / 250 + 5)
-					  / 1000, 0, 255);
-}
-
-static inline long IN_FROM_REG(u8 val, int inNum)
-{
-	/* to avoid floating point, we multiply everything by 100.
-	 val is guaranteed to be positive, so we can achieve the effect of
-	 rounding by adding 0.5.  Or, to avoid fp math, we do (...*10+5)/10.
-	 We need to scale with *100 anyway, so no need to /100 at the end. */
-	if (inNum <= 1)
-		return (long) (((250000 * val + 13300) / 210240 * 10 + 5) /10);
-	else if (inNum == 2)
-		return (long) (((250000 * val + 13300) / 157370 * 10 + 5) /10);
-	else if (inNum == 3)
-		return (long) (((250000 * val + 13300) / 101080 * 10 + 5) /10);
-	else
-		return (long) (((250000 * val + 13300) / 41714 * 10 + 5) /10);
-}
+#define IN_TO_REG(val)  (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255))
+#define IN_FROM_REG(val) (((val) *  16) / 10)
 
 static inline u8 FAN_TO_REG(long rpm, int div)
 {
@@ -159,7 +121,14 @@
 				205-(val)*5)
 #define ALARMS_FROM_REG(val) (val)
 
-#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+static int log2(int val)
+{
+	int answer = 0;
+	while ((val >>= 1))
+		answer++;
+	return answer;
+}
+#define DIV_TO_REG(val) log2(val)
 #define DIV_FROM_REG(val) (1 << (val))
 
 /* Initial limits. Use the config file to set better limits. */
@@ -272,7 +241,7 @@
 	struct i2c_client *client = to_i2c_client(dev);
 	struct it87_data *data = i2c_get_clientdata(client);
 	it87_update_client(client);
-	return sprintf(buf, "%ld\n", IN_FROM_REG(data->in[nr], nr)*10 );
+	return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr])*10 );
 }
 
 static ssize_t show_in_min(struct device *dev, char *buf, int nr)
@@ -280,7 +249,7 @@
 	struct i2c_client *client = to_i2c_client(dev);
 	struct it87_data *data = i2c_get_clientdata(client);
 	it87_update_client(client);
-	return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_min[nr], nr)*10 );
+	return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr])*10 );
 }
 
 static ssize_t show_in_max(struct device *dev, char *buf, int nr)
@@ -288,7 +257,7 @@
 	struct i2c_client *client = to_i2c_client(dev);
 	struct it87_data *data = i2c_get_clientdata(client);
 	it87_update_client(client);
-	return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_max[nr], nr)*10 );
+	return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr])*10 );
 }
 
 static ssize_t set_in_min(struct device *dev, const char *buf, 
@@ -297,7 +266,7 @@
 	struct i2c_client *client = to_i2c_client(dev);
 	struct it87_data *data = i2c_get_clientdata(client);
 	unsigned long val = simple_strtoul(buf, NULL, 10)/10;
-	data->in_min[nr] = IN_TO_REG(val,nr);
+	data->in_min[nr] = IN_TO_REG(val);
 	it87_write_value(client, IT87_REG_VIN_MIN(nr), 
 			data->in_min[nr]);
 	return count;
@@ -308,7 +277,7 @@
 	struct i2c_client *client = to_i2c_client(dev);
 	struct it87_data *data = i2c_get_clientdata(client);
 	unsigned long val = simple_strtoul(buf, NULL, 10)/10;
-	data->in_max[nr] = IN_TO_REG(val,nr);
+	data->in_max[nr] = IN_TO_REG(val);
 	it87_write_value(client, IT87_REG_VIN_MAX(nr), 
 			data->in_max[nr]);
 	return count;
@@ -320,6 +289,9 @@
 {								\
 	return show_in(dev, buf, 0x##offset);			\
 }								\
+static DEVICE_ATTR(in_input##offset, S_IRUGO, show_in##offset, NULL)
+
+#define limit_in_offset(offset)					\
 static ssize_t							\
 	show_in##offset##_min (struct device *dev, char *buf)	\
 {								\
@@ -340,20 +312,27 @@
 {								\
 	return set_in_max(dev, buf, count, 0x##offset);		\
 }								\
-static DEVICE_ATTR(in_input##offset, S_IRUGO, show_in##offset, NULL) 	\
 static DEVICE_ATTR(in_min##offset, S_IRUGO | S_IWUSR, 		\
 		show_in##offset##_min, set_in##offset##_min)	\
 static DEVICE_ATTR(in_max##offset, S_IRUGO | S_IWUSR, 		\
 		show_in##offset##_max, set_in##offset##_max)
 
 show_in_offset(0);
+limit_in_offset(0);
 show_in_offset(1);
+limit_in_offset(1);
 show_in_offset(2);
+limit_in_offset(2);
 show_in_offset(3);
+limit_in_offset(3);
 show_in_offset(4);
+limit_in_offset(4);
 show_in_offset(5);
+limit_in_offset(5);
 show_in_offset(6);
+limit_in_offset(6);
 show_in_offset(7);
+limit_in_offset(7);
 show_in_offset(8);
 
 /* 3 temperatures */
@@ -362,7 +341,7 @@
 	struct i2c_client *client = to_i2c_client(dev);
 	struct it87_data *data = i2c_get_clientdata(client);
 	it87_update_client(client);
-	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr])*10 );
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr])*100 );
 }
 /* more like overshoot temperature */
 static ssize_t show_temp_max(struct device *dev, char *buf, int nr)
@@ -370,7 +349,7 @@
 	struct i2c_client *client = to_i2c_client(dev);
 	struct it87_data *data = i2c_get_clientdata(client);
 	it87_update_client(client);
-	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[nr])*10);
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[nr])*100);
 }
 /* more like hysteresis temperature */
 static ssize_t show_temp_min(struct device *dev, char *buf, int nr)
@@ -378,14 +357,14 @@
 	struct i2c_client *client = to_i2c_client(dev);
 	struct it87_data *data = i2c_get_clientdata(client);
 	it87_update_client(client);
-	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[nr])*10);
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[nr])*100);
 }
 static ssize_t set_temp_max(struct device *dev, const char *buf, 
 		size_t count, int nr)
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct it87_data *data = i2c_get_clientdata(client);
-	int val = simple_strtol(buf, NULL, 10)/10;
+	int val = simple_strtol(buf, NULL, 10)/100;
 	data->temp_high[nr] = TEMP_TO_REG(val);
 	it87_write_value(client, IT87_REG_TEMP_HIGH(nr), data->temp_high[nr]);
 	return count;
@@ -395,7 +374,7 @@
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct it87_data *data = i2c_get_clientdata(client);
-	int val = simple_strtol(buf, NULL, 10)/10;
+	int val = simple_strtol(buf, NULL, 10)/100;
 	data->temp_low[nr] = TEMP_TO_REG(val);
 	it87_write_value(client, IT87_REG_TEMP_LOW(nr), data->temp_low[nr]);
 	return count;
@@ -511,7 +490,7 @@
 	struct it87_data *data = i2c_get_clientdata(client);
 	int val = simple_strtol(buf, NULL, 10);
 	data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
-	it87_write_value(client, IT87_REG_FAN_MIN(nr+1), data->fan_min[nr]);
+	it87_write_value(client, IT87_REG_FAN_MIN(nr), data->fan_min[nr]);
 	return count;
 }
 static ssize_t set_fan_div(struct device *dev, const char *buf, 
@@ -520,10 +499,34 @@
 	struct i2c_client *client = to_i2c_client(dev);
 	struct it87_data *data = i2c_get_clientdata(client);
 	int val = simple_strtol(buf, NULL, 10);
-	int old = it87_read_value(client, IT87_REG_FAN_DIV);
-	data->fan_div[nr] = DIV_TO_REG(val);
-	old = (old & 0x0f) | (data->fan_div[1] << 6) | (data->fan_div[0] << 4);
-	it87_write_value(client, IT87_REG_FAN_DIV, old);
+	int i, min[3];
+	u8 old = it87_read_value(client, IT87_REG_FAN_DIV);
+
+	for (i = 0; i < 3; i++)
+		min[i] = FAN_FROM_REG(data->fan_min[i], DIV_FROM_REG(data->fan_div[i]));
+
+	switch (nr) {
+	case 0:
+	case 1:
+		data->fan_div[nr] = DIV_TO_REG(val);
+		break;
+	case 2:
+		if (val < 8)
+			data->fan_div[nr] = 1;
+		else
+			data->fan_div[nr] = 3;
+	}
+	val = old & 0x100;
+	val |= (data->fan_div[0] & 0x07);
+	val |= (data->fan_div[1] & 0x07) << 3;
+	if (data->fan_div[2] == 3)
+		val |= 0x1 << 6;
+	it87_write_value(client, IT87_REG_FAN_DIV, val);
+
+	for (i = 0; i < 3; i++) {
+		data->fan_min[i]=FAN_TO_REG(min[i], DIV_FROM_REG(data->fan_div[i]));
+		it87_write_value(client, IT87_REG_FAN_MIN(i), data->fan_min[i]);
+	}
 	return count;
 }
 
@@ -720,7 +723,6 @@
 	device_create_file(&new_client->dev, &dev_attr_in_min5);
 	device_create_file(&new_client->dev, &dev_attr_in_min6);
 	device_create_file(&new_client->dev, &dev_attr_in_min7);
-	device_create_file(&new_client->dev, &dev_attr_in_min8);
 	device_create_file(&new_client->dev, &dev_attr_in_max0);
 	device_create_file(&new_client->dev, &dev_attr_in_max1);
 	device_create_file(&new_client->dev, &dev_attr_in_max2);
@@ -729,7 +731,6 @@
 	device_create_file(&new_client->dev, &dev_attr_in_max5);
 	device_create_file(&new_client->dev, &dev_attr_in_max6);
 	device_create_file(&new_client->dev, &dev_attr_in_max7);
-	device_create_file(&new_client->dev, &dev_attr_in_max8);
 	device_create_file(&new_client->dev, &dev_attr_temp_input1);
 	device_create_file(&new_client->dev, &dev_attr_temp_input2);
 	device_create_file(&new_client->dev, &dev_attr_temp_input3);
@@ -833,55 +834,55 @@
 	   This sets fan-divs to 2, among others */
 	it87_write_value(client, IT87_REG_CONFIG, 0x80);
 	it87_write_value(client, IT87_REG_VIN_MIN(0),
-			 IN_TO_REG(IT87_INIT_IN_MIN_0, 0));
+			 IN_TO_REG(IT87_INIT_IN_MIN_0));
 	it87_write_value(client, IT87_REG_VIN_MAX(0),
-			 IN_TO_REG(IT87_INIT_IN_MAX_0, 0));
+			 IN_TO_REG(IT87_INIT_IN_MAX_0));
 	it87_write_value(client, IT87_REG_VIN_MIN(1),
-			 IN_TO_REG(IT87_INIT_IN_MIN_1, 1));
+			 IN_TO_REG(IT87_INIT_IN_MIN_1));
 	it87_write_value(client, IT87_REG_VIN_MAX(1),
-			 IN_TO_REG(IT87_INIT_IN_MAX_1, 1));
+			 IN_TO_REG(IT87_INIT_IN_MAX_1));
 	it87_write_value(client, IT87_REG_VIN_MIN(2),
-			 IN_TO_REG(IT87_INIT_IN_MIN_2, 2));
+			 IN_TO_REG(IT87_INIT_IN_MIN_2));
 	it87_write_value(client, IT87_REG_VIN_MAX(2),
-			 IN_TO_REG(IT87_INIT_IN_MAX_2, 2));
+			 IN_TO_REG(IT87_INIT_IN_MAX_2));
 	it87_write_value(client, IT87_REG_VIN_MIN(3),
-			 IN_TO_REG(IT87_INIT_IN_MIN_3, 3));
+			 IN_TO_REG(IT87_INIT_IN_MIN_3));
 	it87_write_value(client, IT87_REG_VIN_MAX(3),
-			 IN_TO_REG(IT87_INIT_IN_MAX_3, 3));
+			 IN_TO_REG(IT87_INIT_IN_MAX_3));
 	it87_write_value(client, IT87_REG_VIN_MIN(4),
-			 IN_TO_REG(IT87_INIT_IN_MIN_4, 4));
+			 IN_TO_REG(IT87_INIT_IN_MIN_4));
 	it87_write_value(client, IT87_REG_VIN_MAX(4),
-			 IN_TO_REG(IT87_INIT_IN_MAX_4, 4));
+			 IN_TO_REG(IT87_INIT_IN_MAX_4));
 	it87_write_value(client, IT87_REG_VIN_MIN(5),
-			 IN_TO_REG(IT87_INIT_IN_MIN_5, 5));
+			 IN_TO_REG(IT87_INIT_IN_MIN_5));
 	it87_write_value(client, IT87_REG_VIN_MAX(5),
-			 IN_TO_REG(IT87_INIT_IN_MAX_5, 5));
+			 IN_TO_REG(IT87_INIT_IN_MAX_5));
 	it87_write_value(client, IT87_REG_VIN_MIN(6),
-			 IN_TO_REG(IT87_INIT_IN_MIN_6, 6));
+			 IN_TO_REG(IT87_INIT_IN_MIN_6));
 	it87_write_value(client, IT87_REG_VIN_MAX(6),
-			 IN_TO_REG(IT87_INIT_IN_MAX_6, 6));
+			 IN_TO_REG(IT87_INIT_IN_MAX_6));
 	it87_write_value(client, IT87_REG_VIN_MIN(7),
-			 IN_TO_REG(IT87_INIT_IN_MIN_7, 7));
+			 IN_TO_REG(IT87_INIT_IN_MIN_7));
 	it87_write_value(client, IT87_REG_VIN_MAX(7),
-			 IN_TO_REG(IT87_INIT_IN_MAX_7, 7));
+			 IN_TO_REG(IT87_INIT_IN_MAX_7));
 	/* Note: Battery voltage does not have limit registers */
-	it87_write_value(client, IT87_REG_FAN_MIN(1),
+	it87_write_value(client, IT87_REG_FAN_MIN(0),
 			 FAN_TO_REG(IT87_INIT_FAN_MIN_1, 2));
-	it87_write_value(client, IT87_REG_FAN_MIN(2),
+	it87_write_value(client, IT87_REG_FAN_MIN(1),
 			 FAN_TO_REG(IT87_INIT_FAN_MIN_2, 2));
-	it87_write_value(client, IT87_REG_FAN_MIN(3),
+	it87_write_value(client, IT87_REG_FAN_MIN(2),
 			 FAN_TO_REG(IT87_INIT_FAN_MIN_3, 2));
-	it87_write_value(client, IT87_REG_TEMP_HIGH(1),
+	it87_write_value(client, IT87_REG_TEMP_HIGH(0),
 			 TEMP_TO_REG(IT87_INIT_TEMP_HIGH_1));
-	it87_write_value(client, IT87_REG_TEMP_LOW(1),
+	it87_write_value(client, IT87_REG_TEMP_LOW(0),
 			 TEMP_TO_REG(IT87_INIT_TEMP_LOW_1));
-	it87_write_value(client, IT87_REG_TEMP_HIGH(2),
+	it87_write_value(client, IT87_REG_TEMP_HIGH(1),
 			 TEMP_TO_REG(IT87_INIT_TEMP_HIGH_2));
-	it87_write_value(client, IT87_REG_TEMP_LOW(2),
+	it87_write_value(client, IT87_REG_TEMP_LOW(1),
 			 TEMP_TO_REG(IT87_INIT_TEMP_LOW_2));
-	it87_write_value(client, IT87_REG_TEMP_HIGH(3),
+	it87_write_value(client, IT87_REG_TEMP_HIGH(2),
 			 TEMP_TO_REG(IT87_INIT_TEMP_HIGH_3));
-	it87_write_value(client, IT87_REG_TEMP_LOW(3),
+	it87_write_value(client, IT87_REG_TEMP_LOW(2),
 			 TEMP_TO_REG(IT87_INIT_TEMP_LOW_3));
 
 	/* Enable voltage monitors */
@@ -934,18 +935,18 @@
 		data->in_min[8] = 0;
 		data->in_max[8] = 255;
 
-		for (i = 1; i <= 3; i++) {
-			data->fan[i - 1] =
+		for (i = 0; i < 3; i++) {
+			data->fan[i] =
 			    it87_read_value(client, IT87_REG_FAN(i));
-			data->fan_min[i - 1] =
+			data->fan_min[i] =
 			    it87_read_value(client, IT87_REG_FAN_MIN(i));
 		}
-		for (i = 1; i <= 3; i++) {
-			data->temp[i - 1] =
+		for (i = 0; i < 3; i++) {
+			data->temp[i] =
 			    it87_read_value(client, IT87_REG_TEMP(i));
-			data->temp_high[i - 1] =
+			data->temp_high[i] =
 			    it87_read_value(client, IT87_REG_TEMP_HIGH(i));
-			data->temp_low[i - 1] =
+			data->temp_low[i] =
 			    it87_read_value(client, IT87_REG_TEMP_LOW(i));
 		}
 
@@ -961,7 +962,7 @@
 		i = it87_read_value(client, IT87_REG_FAN_DIV);
 		data->fan_div[0] = i & 0x07;
 		data->fan_div[1] = (i >> 3) & 0x07;
-		data->fan_div[2] = 1;
+		data->fan_div[2] = (i & 0x40) ? 3 : 1;
 
 		data->alarms =
 			it87_read_value(client, IT87_REG_ALARM1) |
diff -urN linux-2.5.69-bk5/drivers/i2c/i2c-core.c linux-2.5.69-bk6/drivers/i2c/i2c-core.c
--- linux-2.5.69-bk5/drivers/i2c/i2c-core.c	2003-05-11 04:43:30.000000000 -0700
+++ linux-2.5.69-bk6/drivers/i2c/i2c-core.c	2003-05-11 04:43:33.000000000 -0700
@@ -55,13 +55,17 @@
 	return 0;
 }
 
-static struct device_driver i2c_generic_driver = {
-	.name =	"i2c",
+static struct device_driver i2c_adapter_driver = {
+	.name =	"i2c_adapter",
 	.bus = &i2c_bus_type,
 	.probe = i2c_device_probe,
 	.remove = i2c_device_remove,
 };
 
+static struct class i2c_adapter_class = {
+	.name =		"i2c-adapter"
+};
+
 
 /* ---------------------------------------------------
  * registering functions 
@@ -94,9 +98,16 @@
 	if (adap->dev.parent == NULL)
 		adap->dev.parent = &legacy_bus;
 	sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
-	adap->dev.driver = &i2c_generic_driver;
+	adap->dev.driver = &i2c_adapter_driver;
 	device_register(&adap->dev);
 
+	/* Add this adapter to the i2c_adapter class */
+	memset(&adap->class_dev, 0x00, sizeof(struct class_device));
+	adap->class_dev.dev = &adap->dev;
+	adap->class_dev.class = &i2c_adapter_class;
+	strncpy(adap->class_dev.class_id, adap->dev.bus_id, BUS_ID_SIZE);
+	class_device_register(&adap->class_dev);
+
 	/* inform drivers of new adapters */
 	list_for_each(item,&drivers) {
 		driver = list_entry(item, struct i2c_driver, list);
@@ -150,6 +161,7 @@
 	}
 
 	/* clean up the sysfs representation */
+	class_device_unregister(&adap->class_dev);
 	device_unregister(&adap->dev);
 	list_del(&adap->list);
 
@@ -443,14 +455,23 @@
 	.match =	i2c_device_match,
 };
 
-
 static int __init i2c_init(void)
 {
-	return bus_register(&i2c_bus_type);
+	int retval;
+
+	retval = bus_register(&i2c_bus_type);
+	if (retval)
+		return retval;
+	retval = driver_register(&i2c_adapter_driver);
+	if (retval)
+		return retval;
+	return class_register(&i2c_adapter_class);
 }
 
 static void __exit i2c_exit(void)
 {
+	class_unregister(&i2c_adapter_class);
+	driver_unregister(&i2c_adapter_driver);
 	bus_unregister(&i2c_bus_type);
 }
 
@@ -475,7 +496,7 @@
 
 		return ret;
 	} else {
-		dev_err(&adap->dev, "I2C level transfers not supported\n");
+		DEB2(dev_dbg(&adap->dev, "I2C level transfers not supported\n"));
 		return -ENOSYS;
 	}
 }
diff -urN linux-2.5.69-bk5/drivers/ide/ide-cd.c linux-2.5.69-bk6/drivers/ide/ide-cd.c
--- linux-2.5.69-bk5/drivers/ide/ide-cd.c	2003-05-04 16:53:14.000000000 -0700
+++ linux-2.5.69-bk6/drivers/ide/ide-cd.c	2003-05-11 04:43:33.000000000 -0700
@@ -2871,6 +2871,11 @@
 	struct atapi_capabilities_page cap;
 	int nslots = 1;
 
+	if (drive->media == ide_optical) {
+		printk("%s: ATAPI magneto-optical drive\n", drive->name);
+		return nslots;
+	}
+
 	if (CDROM_CONFIG_FLAGS(drive)->nec260) {
 		CDROM_CONFIG_FLAGS(drive)->no_eject = 0;                       
 		CDROM_CONFIG_FLAGS(drive)->audio_play = 1;       
@@ -3337,7 +3342,7 @@
 		goto failed;
 	if (!drive->present)
 		goto failed;
-	if (drive->media != ide_cdrom)
+	if (drive->media != ide_cdrom && drive->media != ide_optical)
 		goto failed;
 	/* skip drives that we were told to ignore */
 	if (ignore != NULL) {
diff -urN linux-2.5.69-bk5/drivers/ide/ide-probe.c linux-2.5.69-bk6/drivers/ide/ide-probe.c
--- linux-2.5.69-bk5/drivers/ide/ide-probe.c	2003-05-04 16:53:32.000000000 -0700
+++ linux-2.5.69-bk6/drivers/ide/ide-probe.c	2003-05-11 04:43:33.000000000 -0700
@@ -1235,7 +1235,7 @@
 			(void) request_module("ide-disk");
 		if (drive->scsi)
 			(void) request_module("ide-scsi");
-		if (drive->media == ide_cdrom)
+		if (drive->media == ide_cdrom || drive->media == ide_optical)
 			(void) request_module("ide-cd");
 		if (drive->media == ide_tape)
 			(void) request_module("ide-tape");
diff -urN linux-2.5.69-bk5/drivers/usb/Kconfig linux-2.5.69-bk6/drivers/usb/Kconfig
--- linux-2.5.69-bk5/drivers/usb/Kconfig	2003-05-04 16:53:03.000000000 -0700
+++ linux-2.5.69-bk6/drivers/usb/Kconfig	2003-05-11 04:43:33.000000000 -0700
@@ -91,5 +91,7 @@
 
 source "drivers/usb/misc/Kconfig"
 
+source "drivers/usb/gadget/Kconfig"
+
 endmenu
 
diff -urN linux-2.5.69-bk5/drivers/usb/Makefile linux-2.5.69-bk6/drivers/usb/Makefile
--- linux-2.5.69-bk5/drivers/usb/Makefile	2003-05-04 16:53:41.000000000 -0700
+++ linux-2.5.69-bk6/drivers/usb/Makefile	2003-05-11 04:43:33.000000000 -0700
@@ -58,3 +58,7 @@
 obj-$(CONFIG_USB_TEST)		+= misc/
 obj-$(CONFIG_USB_TIGL)		+= misc/
 obj-$(CONFIG_USB_USS720)	+= misc/
+
+obj-$(CONFIG_USB_NET2280)	+= gadget/
+obj-$(CONFIG_USB_ZERO)		+= gadget/
+obj-$(CONFIG_USB_ETH)		+= gadget/
diff -urN linux-2.5.69-bk5/drivers/usb/gadget/Kconfig linux-2.5.69-bk6/drivers/usb/gadget/Kconfig
--- linux-2.5.69-bk5/drivers/usb/gadget/Kconfig	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.69-bk6/drivers/usb/gadget/Kconfig	2003-05-11 04:43:33.000000000 -0700
@@ -0,0 +1,153 @@
+#
+# USB Gadget support on a system involves
+#    (a) a peripheral controller, and
+#    (b) the gadget driver using it.
+#
+# for 2.5 kbuild, drivers/usb/gadget/Kconfig
+# source this at the end of drivers/usb/Kconfig
+#
+menuconfig USB_GADGET
+	tristate "Support for USB Gadgets"
+	depends on EXPERIMENTAL
+	help
+	   USB is a master/slave protocol, organized with with one master
+	   host (such as a PC) controlling up to 127 peripheral devices.
+	   The USB hardware is asymmetric, which makes it easier to set up:
+	   you can't connect two "to-the-host" connectors to each other.
+
+	   Linux can run in the host, or in the peripheral.  In both cases
+	   you need a low level bus controller driver, and some software
+	   talking to it.  Peripheral controllers are often discrete silicon,
+	   or are integrated with the CPU in a microcontroller.  The more
+	   familiar host side controllers have names like like "EHCI", "OHCI",
+	   or "UHCI", and are usually integrated into southbridges on PC
+	   motherboards.
+
+	   Enable this configuration option if you want to run Linux inside
+	   a USB peripheral device.  Configure one hardware driver for your
+	   peripheral/device side bus controller, and a "gadget driver" for
+	   your peripheral protocol.  (If you use modular gadget drivers,
+	   you may configure more than one.)
+
+	   If in doubt, say "N" and don't enable these drivers; most people
+	   don't have this kind of hardware (except maybe inside Linux PDAs).
+
+#
+# USB Peripheral Controller Support
+#
+# FIXME convert to tristate choice when "choice" behaves as specified
+#
+comment "USB Peripheral Controller Support"
+	depends on USB_GADGET
+
+config USB_NET2280
+	tristate "NetChip 2280 USB Peripheral Controller"
+	depends on PCI && USB_GADGET
+	help
+	   NetChip 2280 is a PCI based USB peripheral controller which
+	   supports both full and high speed USB 2.0 data transfers.  
+	   
+	   It has six configurable endpoints, as well as endpoint zero
+	   (for control transfers) and several endpoints with dedicated
+	   functions.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "net2280" and force all
+	   gadget drivers to also be dynamically linked.
+
+#
+# USB Gadget Drivers
+#
+# FIXME only one of these may be statically linked; choice/endchoice.
+#
+comment "USB Gadget Drivers"
+	depends on USB_GADGET
+
+# FIXME want better dependency/config approach for drivers.  with only
+# two knobs to tweak (driver y/m/n, and a hardware symbol) there's no
+# good excuse for Kconfig to cause such trouble here.  there are clear
+# bugs (coredumps, multiple choices enabled, and more) in its (boolean)
+# "choice" logic too ...
+
+config USB_ZERO
+	tristate "Gadget Zero (DEVELOPMENT)"
+	depends on USB_GADGET && (USB_DUMMY_HCD || USB_NET2280 || USB_PXA250 || USB_SA1100)
+	help
+	  Gadget Zero is a two-configuration device.  It either sinks and
+	  sources bulk data; or it loops back a configurable number of
+	  transfers.  It also implements control requests, for "chapter 9"
+	  conformance.  The driver needs only two bulk-capable endpoints, so
+	  it can work on top of most device-side usb controllers.  It's
+	  useful for testing, and is also a working example showing how
+	  USB "gadget drivers" can be written.
+
+	  Make this be the first driver you try using on top of any new
+	  USB peripheral controller driver.  Then you can use host-side
+	  test software, like the "usbtest" driver, to put your hardware
+	  and its driver through a basic set of functional tests.
+
+	  Gadget Zero also works with the host-side "usb-skeleton" driver,
+	  and with many kinds of host-side test software.  You may need
+	  to tweak product and vendor IDs before host software knows about
+	  this device, and arrange to select an appropriate configuration.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "g_zero".
+
+config USB_ZERO_NET2280
+	bool
+	# for now, treat the "dummy" hcd as if it were a net2280
+	depends on USB_ZERO && (USB_NET2280 || USB_DUMMY_HCD)
+	default y
+
+config USB_ZERO_PXA250
+	bool
+	depends on USB_ZERO && USB_PXA250
+	default y
+
+config USB_ZERO_SA1100
+	bool
+	depends on USB_ZERO && USB_SA1100
+	default y
+
+
+config USB_ETH
+	tristate "Ethernet Gadget"
+	depends on USB_GADGET && (USB_DUMMY_HCD || USB_NET2280 || USB_PXA250 || USB_SA1100)
+	help
+	  This driver implements the "Communication Device Class" (CDC)
+	  Ethernet Control Model.  That protocol is often avoided with pure
+	  Ethernet adapters, in favor of simpler vendor-specific hardware,
+	  but is widely supported by firmware for smart network devices.
+
+	  Within the USB device, this gadget driver exposes a network device
+	  "usbX", where X depends on what other networking devices you have.
+	  Treat it like a two-node Ethernet link:  host, and gadget.
+
+	  The Linux-USB host-side "usbnet" driver interoperates with this
+	  driver, so that deep I/O queues can be supported.  (On 2.4 kernels,
+	  use "CDCEther" instead.)  Deep queues are especially important with
+	  high speed devices.  It should also interoperate with standard CDC
+	  Ethernet class drivers on other host operating systems.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "g_ether".
+
+
+config USB_ETH_NET2280
+	bool
+	# for now, treat the "dummy" hcd as if it were a net2280
+	depends on USB_ETH && (USB_NET2280 || USB_DUMMY_HCD)
+	default y
+
+config USB_ETH_PXA250
+	bool
+	depends on USB_ETH && USB_PXA250
+	default y
+
+config USB_ETH_SA1100
+	bool
+	depends on USB_ETH && USB_SA1100
+	default y
+
+# endmenuconfig
diff -urN linux-2.5.69-bk5/drivers/usb/gadget/Makefile linux-2.5.69-bk6/drivers/usb/gadget/Makefile
--- linux-2.5.69-bk5/drivers/usb/gadget/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.69-bk6/drivers/usb/gadget/Makefile	2003-05-11 04:43:33.000000000 -0700
@@ -0,0 +1,14 @@
+#
+# USB peripheral controller drivers
+#
+obj-$(CONFIG_USB_NET2280)	+= net2280.o
+
+#
+# USB gadget drivers
+#
+g_zero-objs			:= zero.o usbstring.o
+g_ether-objs			:= ether.o usbstring.o
+ 
+obj-$(CONFIG_USB_ZERO)		+= g_zero.o
+obj-$(CONFIG_USB_ETH)		+= g_ether.o
+
diff -urN linux-2.5.69-bk5/drivers/usb/gadget/ether.c linux-2.5.69-bk6/drivers/usb/gadget/ether.c
--- linux-2.5.69-bk5/drivers/usb/gadget/ether.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.69-bk6/drivers/usb/gadget/ether.c	2003-05-11 04:43:33.000000000 -0700
@@ -0,0 +1,1640 @@
+/*
+ * ether.c -- CDC 1.1 Ethernet gadget driver
+ *
+ * Copyright (C) 2003 David Brownell
+ *
+ * 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
+ */
+
+
+#define DEBUG 1
+// #define VERBOSE
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/uts.h>
+#include <linux/version.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+
+#include <linux/random.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * "Communications Device Class" (CDC) Ethernet class driver
+ *
+ * CDC Ethernet is the standard USB solution for sending Ethernet frames
+ * using USB.  Real hardware tends to use the same framing protocol but look
+ * different for control features.  And Microsoft pushes their own approach
+ * (RNDIS) instead of the standard.
+ */
+
+#define DRIVER_DESC		"CDC Ethernet Gadget"
+#define DRIVER_VERSION		"29 April 2003"
+
+static const char shortname [] = "ether";
+static const char driver_desc [] = DRIVER_DESC;
+static const char control_name [] = "Communications Control";
+static const char data_name [] = "CDC Ethernet Data";
+
+#define MIN_PACKET	sizeof(struct ethhdr)
+#define	MAX_PACKET	ETH_DATA_LEN	/* biggest packet we'll rx/tx */
+
+/* FIXME allow high speed jumbograms */
+
+/*-------------------------------------------------------------------------*/
+
+struct eth_dev {
+	spinlock_t		lock;
+	struct usb_gadget	*gadget;
+	struct usb_request	*req;		/* for control responses */
+
+	u8			config;
+	struct usb_ep		*in_ep, *out_ep, *status_ep;
+	const struct usb_endpoint_descriptor
+				*in, *out, *status;
+
+	struct semaphore	mutex;
+	struct net_device	net;
+	struct net_device_stats	stats;
+	atomic_t		tx_qlen;
+
+	struct work_struct	work;
+	unsigned long		todo;
+#define	WORK_RX_MEMORY		0
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* This driver keeps a variable number of requests queued, more at
+ * high speeds.  (Numbers are just educated guesses, untuned.)
+ * Shrink the queue if memory is tight, or make it bigger to
+ * handle bigger traffic bursts between IRQs.
+ */
+
+static unsigned qmult = 4;
+
+#define HS_FACTOR	15
+
+#define qlen(gadget) \
+	(qmult*((gadget->speed == USB_SPEED_HIGH) ? HS_FACTOR : 1))
+
+/* defer IRQs on highspeed TX */
+#define TX_DELAY	8
+
+
+module_param (qmult, uint, S_IRUGO|S_IWUSR);
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with any other driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+#define DRIVER_VENDOR_NUM	0x0525		/* NetChip */
+#define DRIVER_PRODUCT_NUM	0xa4a1		/* Linux-USB Ethernet Gadget */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * hardware-specific configuration, controlled by which device
+ * controller driver was configured.
+ *
+ * CHIP ... hardware identifier
+ * DRIVER_VERSION_NUM ... alerts the host side driver to differences
+ * EP0_MAXPACKET ... controls packetization of control requests
+ * EP_*_NAME ... which endpoints do we use for which purpose?
+ * EP_*_NUM ... numbers for them (often limited by hardware)
+ * HIGHSPEED ... define if ep0 and descriptors need high speed support
+ * MAX_USB_POWER ... define if we use other than 100 mA bus current
+ * SELFPOWER ... unless we can run on bus power, USB_CONFIG_ATT_SELFPOWER
+ * WAKEUP ... if hardware supports remote wakeup AND we will issue the
+ * 	usb_gadget_wakeup() call to initiate it, USB_CONFIG_ATT_WAKEUP
+ *
+ * hw_optimize(gadget) ... for any hardware tweaks we want to kick in
+ * 	before we enable our endpoints
+ *
+ * add other defines for other portability issues, like hardware that
+ * for some reason doesn't handle full speed bulk maxpacket of 64.
+ */
+
+/*
+ * NetChip 2280, PCI based.
+ *
+ * use DMA with fat fifos for all data traffic, PIO for the status channel
+ * where its 64 byte maxpacket ceiling is no issue.
+ *
+ * performance note:  only PIO needs per-usb-packet IRQs (ep0, ep-e, ep-f)
+ * otherwise IRQs are per-Ethernet-packet unless TX_DELAY and chaining help.
+ */
+#ifdef	CONFIG_USB_ETH_NET2280
+#define CHIP			"net2280"
+#define DRIVER_VERSION_NUM	cpu_to_le16(0x0101)
+#define EP0_MAXPACKET		64
+static const char EP_OUT_NAME [] = "ep-a";
+#define EP_OUT_NUM	2
+static const char EP_IN_NAME [] = "ep-b";
+#define EP_IN_NUM	2
+static const char EP_STATUS_NAME [] = "ep-f";
+#define EP_STATUS_NUM	3
+#define HIGHSPEED
+/* specific hardware configs could be bus-powered */
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+/* supports remote wakeup, but this driver doesn't */
+
+extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode);
+
+static inline void hw_optimize (struct usb_gadget *gadget)
+{
+	/* we can have bigger ep-a/ep-b fifos (2KB each, 4 USB packets
+	 * for highspeed bulk) because we're not using ep-c/ep-d.
+	 */
+	net2280_set_fifo_mode (gadget, 1);
+}
+#endif
+
+/*
+ * PXA-250 UDC:  widely used in second gen Linux-capable PDAs.
+ *
+ * no limitations except from set_interface: docs say "no" to a third
+ * interface. and the interrupt-only endpoints don't toggle, so we'll
+ * just use a bulk-capable one instead.
+ */
+#ifdef	CONFIG_USB_ETH_PXA250
+#define CHIP			"pxa250"
+#define DRIVER_VERSION_NUM	cpu_to_le16(0x0103)
+#define EP0_MAXPACKET		16
+static const char EP_OUT_NAME [] = "ep12out-bulk";
+#define EP_OUT_NUM	12
+static const char EP_IN_NAME [] = "ep11in-bulk";
+#define EP_IN_NUM	11
+static const char EP_STATUS_NAME [] = "ep6in-bulk";
+#define EP_STATUS_NUM	6
+/* doesn't support bus-powered operation */
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+/* supports remote wakeup, but this driver doesn't */
+
+/* no hw optimizations to apply */
+#define hw_optimize(g) do {} while (0);
+#endif
+
+/*
+ * SA-1100 UDC:  widely used in first gen Linux-capable PDAs.
+ *
+ * can't have a notification endpoint, since there are only the two
+ * bulk-capable ones.  the CDC spec allows that.
+ */
+#ifdef	CONFIG_USB_ETH_SA1100
+#define CHIP			"sa1100"
+#define DRIVER_VERSION_NUM	cpu_to_le16(0x0105)
+#define EP0_MAXPACKET		8
+static const char EP_OUT_NAME [] = "ep1out-bulk";
+#define EP_OUT_NUM	1
+static const char EP_IN_NAME [] = "ep2in-bulk";
+#define EP_IN_NUM	2
+// EP_STATUS_NUM is undefined
+/* doesn't support bus-powered operation */
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+/* doesn't support remote wakeup? */
+
+/* no hw optimizations to apply */
+#define hw_optimize(g) do {} while (0);
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef EP0_MAXPACKET
+#	error Configure some USB peripheral controller driver!
+#endif
+
+/* power usage is config specific.
+ * hardware that supports remote wakeup defaults to disabling it.
+ */
+
+#ifndef	SELFPOWER
+/* default: say we rely on bus power */
+#define SELFPOWER	0
+/* else:
+ * - SELFPOWER value must be USB_CONFIG_ATT_SELFPOWER
+ * - MAX_USB_POWER may be nonzero.
+ */
+#endif
+
+#ifndef	MAX_USB_POWER
+/* any hub supports this steady state bus power consumption */
+#define MAX_USB_POWER	100	/* mA */
+#endif
+
+#ifndef	WAKEUP
+/* default: this driver won't do remote wakeup */
+#define WAKEUP		0
+/* else value must be USB_CONFIG_ATT_WAKEUP */
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define xprintk(d,level,fmt,args...) \
+	dev_printk(level , &(d)->gadget->dev , fmt , ## args)
+
+#ifdef DEBUG
+#undef DEBUG
+#define DEBUG(dev,fmt,args...) \
+	xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DEBUG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDEBUG	DEBUG
+#else
+#define VDEBUG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#define ERROR(dev,fmt,args...) \
+	xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARN(dev,fmt,args...) \
+	xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+	xprintk(dev , KERN_INFO , fmt , ## args)
+
+/*-------------------------------------------------------------------------*/
+
+/* USB DRIVER HOOKUP (to the hardware driver, below us), mostly
+ * ep0 implementation:  descriptors, config management, setup().
+ * also optional class-specific notification interrupt transfer.
+ */
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full) configuration
+ * descriptors are built on demand.  Notice how most of the cdc descriptors
+ * add no value to simple (typical) configurations.
+ */
+
+#define STRING_MANUFACTURER		1
+#define STRING_PRODUCT			2
+#define STRING_ETHADDR			3
+#define STRING_DATA			4
+#define STRING_CONTROL			5
+
+#define USB_BUFSIZ	256		/* holds our biggest descriptor */
+
+/*
+ * This device advertises one configuration.
+ */
+#define	CONFIG_CDC_ETHER	3
+
+static const struct usb_device_descriptor
+device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	.bcdUSB =		cpu_to_le16 (0x0200),
+	.bDeviceClass =		USB_CLASS_COMM,
+	.bDeviceSubClass =	0,
+	.bDeviceProtocol =	0,
+	.bMaxPacketSize0 =	EP0_MAXPACKET,
+
+	.idVendor =		cpu_to_le16 (DRIVER_VENDOR_NUM),
+	.idProduct =		cpu_to_le16 (DRIVER_PRODUCT_NUM),
+	.bcdDevice =		cpu_to_le16 (DRIVER_VERSION_NUM),
+	.iManufacturer =	STRING_MANUFACTURER,
+	.iProduct =		STRING_PRODUCT,
+	.bNumConfigurations =	1,
+};
+
+static const struct usb_config_descriptor
+eth_config = {
+	.bLength =		sizeof eth_config,
+	.bDescriptorType =	USB_DT_CONFIG,
+
+	/* compute wTotalLength on the fly */
+	.bNumInterfaces =	2,
+	.bConfigurationValue =	CONFIG_CDC_ETHER,
+	.iConfiguration =	STRING_PRODUCT,
+	.bmAttributes =		USB_CONFIG_ATT_ONE | SELFPOWER | WAKEUP,
+	.bMaxPower =		(MAX_USB_POWER + 1) / 2,
+};
+
+/* master comm interface optionally has a status notification endpoint */
+
+static const struct usb_interface_descriptor
+control_intf = {
+	.bLength =		sizeof control_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bInterfaceNumber =	0,
+#ifdef	EP_STATUS_NUM
+	.bNumEndpoints =	1,
+#else
+	.bNumEndpoints =	0,
+#endif
+	.bInterfaceClass =	USB_CLASS_COMM,
+	.bInterfaceSubClass =	6,	/* ethernet control model */
+	.bInterfaceProtocol =	0,
+	.iInterface =		STRING_CONTROL,
+};
+
+/* "Header Functional Descriptor" from CDC spec  5.2.3.1 */
+struct header_desc {
+	u8	bLength;
+	u8	bDescriptorType;
+	u8	bDescriptorSubType;
+
+	u16	bcdCDC;
+} __attribute__ ((packed));
+
+static const struct header_desc header_desc = {
+	.bLength =		sizeof header_desc,
+	.bDescriptorType =	0x24,
+	.bDescriptorSubType =	0,
+
+	.bcdCDC =		cpu_to_le16 (0x0110),
+};
+
+/* "Union Functional Descriptor" from CDC spec 5.2.3.X */
+struct union_desc {
+	u8	bLength;
+	u8	bDescriptorType;
+	u8	bDescriptorSubType;
+
+	u8	bMasterInterface0;
+	u8	bSlaveInterface0;
+	/* ... and there could be other slave interfaces */
+} __attribute__ ((packed));
+
+static const struct union_desc union_desc = {
+	.bLength =		sizeof union_desc,
+	.bDescriptorType =	0x24,
+	.bDescriptorSubType =	6,
+
+	.bMasterInterface0 =	0,	/* index of control interface */
+	.bSlaveInterface0 =	1,	/* index of DATA interface */
+};
+
+/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */
+struct ether_desc {
+	u8	bLength;
+	u8	bDescriptorType;
+	u8	bDescriptorSubType;
+
+	u8	iMACAddress;
+	u32	bmEthernetStatistics;
+	u16	wMaxSegmentSize;
+	u16	wNumberMCFilters;
+	u8	bNumberPowerFilters;
+} __attribute__ ((packed));
+
+static const struct ether_desc ether_desc = {
+	.bLength =		sizeof ether_desc,
+	.bDescriptorType =	0x24,
+	.bDescriptorSubType =	0x0f,
+
+	/* this descriptor actually adds value, surprise! */
+	.iMACAddress =		STRING_ETHADDR,
+	.bmEthernetStatistics =	cpu_to_le32 (0),	/* no statistics */
+	.wMaxSegmentSize =	cpu_to_le16 (MAX_PACKET + ETH_HLEN),
+	.wNumberMCFilters =	cpu_to_le16 (0),
+	.bNumberPowerFilters =	0,
+};
+
+#ifdef	EP_STATUS_NUM
+
+/* include the status endpoint if we can, even though it's optional.
+ *
+ * some drivers (like current Linux cdc-ether!) "need" it to exist even
+ * if they ignore the connect/disconnect notifications that real aether
+ * can provide.  more advanced cdc configurations might want to support
+ * encapsulated commands.
+ */
+ 
+#define LOG2_STATUS_INTERVAL_MSEC	6
+#define STATUS_BYTECOUNT		16	/* 8 byte header + data */
+static const struct usb_endpoint_descriptor
+fs_status_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_STATUS_NUM | USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16 (STATUS_BYTECOUNT),
+	.bInterval =		1 << LOG2_STATUS_INTERVAL_MSEC,
+};
+#endif
+
+/* the default data interface has no endpoints ... */
+
+static const struct usb_interface_descriptor
+data_nop_intf = {
+	.bLength =		sizeof data_nop_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bInterfaceNumber =	1,
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+	.iInterface =		STRING_DATA,
+};
+
+/* ... but the "real" data interface has two full speed bulk endpoints */
+
+static const struct usb_interface_descriptor
+data_intf = {
+	.bLength =		sizeof data_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bInterfaceNumber =	1,
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+	.iInterface =		STRING_DATA,
+};
+
+static const struct usb_endpoint_descriptor
+fs_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_IN_NUM | USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16 (64),
+};
+
+static const struct usb_endpoint_descriptor
+fs_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_OUT_NUM,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16 (64),
+};
+
+#ifdef	HIGHSPEED
+
+/*
+ * usb 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ */
+
+#ifdef	EP_STATUS_NUM
+static const struct usb_endpoint_descriptor
+hs_status_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_STATUS_NUM | USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16 (STATUS_BYTECOUNT),
+	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 3,
+};
+#endif
+
+static const struct usb_endpoint_descriptor
+hs_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_IN_NUM | USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16 (512),
+	.bInterval =		1,
+};
+
+static const struct usb_endpoint_descriptor
+hs_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_OUT_NUM,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16 (512),
+	.bInterval =		1,
+};
+
+static const struct usb_qualifier_descriptor
+dev_qualifier = {
+	.bLength =		sizeof dev_qualifier,
+	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER,
+
+	.bcdUSB =		cpu_to_le16 (0x0200),
+	.bDeviceClass =		USB_CLASS_VENDOR_SPEC,
+
+	/* assumes ep0 uses the same value for both speeds ... */
+	.bMaxPacketSize0 =	EP0_MAXPACKET,
+
+	.bNumConfigurations =	2,
+};
+
+/* maxpacket and other transfer characteristics vary by speed. */
+#define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs))
+
+#else
+
+/* if there's no high speed support, maxpacket doesn't change. */
+#define ep_desc(g,hs,fs) fs
+
+#endif	/* !HIGHSPEED */
+
+/* address that the host will use ... usually assigned at random */
+static char				ethaddr [2 * ETH_ALEN + 1];
+
+/* static strings, in iso 8859/1 */
+static struct usb_string		strings [] = {
+	{ STRING_MANUFACTURER,	UTS_SYSNAME " " UTS_RELEASE "/" CHIP, },
+	{ STRING_PRODUCT,	driver_desc, },
+	{ STRING_ETHADDR,	ethaddr, },
+	{ STRING_CONTROL,	control_name, },
+	{ STRING_DATA,		data_name, },
+	{  }		/* end of list */
+};
+
+static struct usb_gadget_strings	stringtab = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings,
+};
+
+/*
+ * one config, two interfaces:  control, data.
+ * complications: class descriptors, and an altsetting.
+ */
+static int
+config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index)
+{
+	const unsigned	config_len = USB_DT_CONFIG_SIZE
+				+ 3 * USB_DT_INTERFACE_SIZE
+				+ sizeof header_desc
+				+ sizeof union_desc
+				+ sizeof ether_desc
+#ifdef	EP_STATUS_NUM
+				+ USB_DT_ENDPOINT_SIZE
+#endif
+				+ 2 * USB_DT_ENDPOINT_SIZE;
+#ifdef HIGHSPEED
+	int		hs;
+#endif
+	/* a single configuration must always be index 0 */
+	if (index > 0)
+		return -EINVAL;
+	if (config_len > USB_BUFSIZ)
+		return -EDOM;
+
+	/* config (or other speed config) */
+	memcpy (buf, &eth_config, USB_DT_CONFIG_SIZE);
+	buf [1] = type;
+	((struct usb_config_descriptor *) buf)->wTotalLength
+		= cpu_to_le16 (config_len);
+	buf += USB_DT_CONFIG_SIZE;
+#ifdef	HIGHSPEED
+	hs = (speed == USB_SPEED_HIGH);
+	if (type == USB_DT_OTHER_SPEED_CONFIG)
+		hs = !hs;
+#endif
+
+	/* control interface, class descriptors, optional status endpoint */
+	memcpy (buf, &control_intf, USB_DT_INTERFACE_SIZE);
+	buf += USB_DT_INTERFACE_SIZE;
+
+	memcpy (buf, &header_desc, sizeof header_desc);
+	buf += sizeof header_desc;
+	memcpy (buf, &union_desc, sizeof union_desc);
+	buf += sizeof union_desc;
+	memcpy (buf, &ether_desc, sizeof ether_desc);
+	buf += sizeof ether_desc;
+
+#ifdef	EP_STATUS_NUM
+#ifdef	HIGHSPEED
+	if (hs)
+		memcpy (buf, &hs_status_desc, USB_DT_ENDPOINT_SIZE);
+	else
+#endif	/* HIGHSPEED */
+		memcpy (buf, &fs_status_desc, USB_DT_ENDPOINT_SIZE);
+	buf += USB_DT_ENDPOINT_SIZE;
+#endif	/* EP_STATUS_NUM */
+
+	/* default data altsetting has no endpoints */
+	memcpy (buf, &data_nop_intf, USB_DT_INTERFACE_SIZE);
+	buf += USB_DT_INTERFACE_SIZE;
+
+	/* the "real" data interface has two endpoints */
+	memcpy (buf, &data_intf, USB_DT_INTERFACE_SIZE);
+	buf += USB_DT_INTERFACE_SIZE;
+#ifdef HIGHSPEED
+	if (hs) {
+		memcpy (buf, &hs_source_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+		memcpy (buf, &hs_sink_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+	} else
+#endif
+	{
+		memcpy (buf, &fs_source_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+		memcpy (buf, &fs_sink_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+	}
+
+	return config_len;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+set_ether_config (struct eth_dev *dev, int gfp_flags)
+{
+	int			result = 0;
+	struct usb_ep		*ep;
+	struct usb_gadget	*gadget = dev->gadget;
+
+	gadget_for_each_ep (ep, gadget) {
+		const struct usb_endpoint_descriptor	*d;
+
+		/* NOTE:  the host isn't allowed to use these two data
+		 * endpoints in the default altsetting for the interface.
+		 * so we don't activate them yet.
+		 */
+
+		/* one endpoint writes data back IN to the host */
+		if (strcmp (ep->name, EP_IN_NAME) == 0) {
+			d = ep_desc (gadget, &hs_source_desc, &fs_source_desc);
+			ep->driver_data = dev;
+			dev->in_ep = ep;
+			dev->in = d;
+			continue;
+
+		/* one endpoint just reads OUT packets */
+		} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
+			d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc);
+			ep->driver_data = dev;
+			dev->out_ep = ep;
+			dev->out = d;
+			continue;
+
+#ifdef	EP_STATUS_NUM
+		/* optional status/notification endpoint */
+		} else if (strcmp (ep->name, EP_STATUS_NAME) == 0) {
+			d = ep_desc (gadget, &hs_status_desc, &fs_status_desc);
+			result = usb_ep_enable (ep, d);
+			if (result == 0) {
+				ep->driver_data = dev;
+				dev->status_ep = ep;
+				dev->status = d;
+				continue;
+			}
+#endif
+
+		/* ignore any other endpoints */
+		} else
+			continue;
+
+		/* stop on error */
+		ERROR (dev, "can't enable %s, result %d\n", ep->name, result);
+		break;
+	}
+
+	if (result == 0)
+		DEBUG (dev, "qlen %d\n", qlen (gadget));
+
+	/* caller is responsible for cleanup on error */
+	return result;
+}
+
+static void eth_reset_config (struct eth_dev *dev)
+{
+	if (dev->config == 0)
+		return;
+
+	DEBUG (dev, "%s\n", __FUNCTION__);
+
+	netif_stop_queue (&dev->net);
+	netif_carrier_off (&dev->net);
+
+	/* just disable endpoints, forcing completion of pending i/o.
+	 * all our completion handlers free their requests in this case.
+	 */
+	if (dev->in_ep) {
+		usb_ep_disable (dev->in_ep);
+		dev->in_ep = 0;
+	}
+	if (dev->out_ep) {
+		usb_ep_disable (dev->out_ep);
+		dev->out_ep = 0;
+	}
+#ifdef	EP_STATUS_NUM
+	if (dev->status_ep) {
+		usb_ep_disable (dev->status_ep);
+		dev->status_ep = 0;
+	}
+#endif
+	dev->config = 0;
+}
+
+/* change our operational config.  must agree with the code
+ * that returns config descriptors, and altsetting code.
+ */
+static int
+eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags)
+{
+	int			result = 0;
+	struct usb_gadget	*gadget = dev->gadget;
+
+	if (number == dev->config)
+		return 0;
+
+#ifdef CONFIG_USB_ETH_SA1100
+	if (dev->config && atomic_read (&dev->tx_qlen) != 0) {
+		/* tx fifo is full, but we can't clear it...*/
+		INFO (dev, "can't change configurations\n");
+		return -ESPIPE;
+	}
+#endif
+	eth_reset_config (dev);
+	hw_optimize (gadget);
+
+	switch (number) {
+	case CONFIG_CDC_ETHER:
+		result = set_ether_config (dev, gfp_flags);
+		break;
+	default:
+		result = -EINVAL;
+		/* FALL THROUGH */
+	case 0:
+		return result;
+	}
+
+	if (!result && (!dev->in_ep || !dev->out_ep))
+		result = -ENODEV;
+	if (result)
+		eth_reset_config (dev);
+	else {
+		char *speed;
+
+		switch (gadget->speed) {
+		case USB_SPEED_FULL:	speed = "full"; break;
+#ifdef HIGHSPEED
+		case USB_SPEED_HIGH:	speed = "high"; break;
+#endif
+		default: 		speed = "?"; break;
+		}
+
+		dev->config = number;
+		INFO (dev, "%s speed config #%d: %s\n", speed, number,
+				driver_desc);
+	}
+	return result;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	EP_STATUS_NUM
+
+/* section 3.8.2 table 11 of the CDC spec lists Ethernet notifications */
+#define CDC_NOTIFY_NETWORK_CONNECTION	0x00	/* required; 6.3.1 */
+#define CDC_NOTIFY_SPEED_CHANGE		0x2a	/* required; 6.3.8 */
+
+struct cdc_notification {
+	u8	bmRequestType;
+	u8	bNotificationType;
+	u16	wValue;
+	u16	wIndex;
+	u16	wLength;
+
+	/* SPEED_CHANGE data looks like this */
+	u32	data [2];
+};
+
+static void eth_status_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	struct cdc_notification	*event = req->buf;
+	int			value = req->status;
+	struct eth_dev		*dev = ep->driver_data;
+
+	/* issue the second notification if host reads the first */
+	if (event->bNotificationType == CDC_NOTIFY_NETWORK_CONNECTION
+			&& value == 0) {
+		event->bmRequestType = 0xA1;
+		event->bNotificationType = CDC_NOTIFY_SPEED_CHANGE;
+		event->wValue = cpu_to_le16 (0);
+		event->wIndex = cpu_to_le16 (1);
+		event->wLength = cpu_to_le16 (8);
+
+		/* SPEED_CHANGE data is up/down speeds in bits/sec */
+		event->data [0] = event->data [1] =
+			(dev->gadget->speed == USB_SPEED_HIGH)
+				? (13 * 512 * 8 * 1000 * 8)
+				: (19 *  64 * 1 * 1000 * 8);
+
+		req->length = 16;
+		value = usb_ep_queue (ep, req, GFP_ATOMIC);
+		DEBUG (dev, "send SPEED_CHANGE --> %d\n", value);
+		if (value == 0)
+			return;
+	} else
+		DEBUG (dev, "event %02x --> %d\n",
+			event->bNotificationType, value);
+
+	/* free when done */
+	usb_ep_free_buffer (ep, req->buf, req->dma, 16);
+	usb_ep_free_request (ep, req);
+}
+
+static void issue_start_status (struct eth_dev *dev)
+{
+	struct usb_request	*req;
+	struct cdc_notification	*event;
+	int			value;
+ 
+	DEBUG (dev, "%s, flush old status first\n", __FUNCTION__);
+
+	/* flush old status
+	 *
+	 * FIXME ugly idiom, maybe we'd be better with just
+	 * a "cancel the whole queue" primitive since any
+	 * unlink-one primitive has way too many error modes.
+	 */
+	usb_ep_disable (dev->status_ep);
+	usb_ep_enable (dev->status_ep, dev->status);
+
+	/* FIXME make these allocations static like dev->req */
+	req = usb_ep_alloc_request (dev->status_ep, GFP_ATOMIC);
+	if (req == 0) {
+		DEBUG (dev, "status ENOMEM\n");
+		return;
+	}
+	req->buf = usb_ep_alloc_buffer (dev->status_ep, 16,
+				&dev->req->dma, GFP_ATOMIC);
+	if (req->buf == 0) {
+		DEBUG (dev, "status buf ENOMEM\n");
+free_req:
+		usb_ep_free_request (dev->status_ep, req);
+		return;
+	}
+
+	/* 3.8.1 says to issue first NETWORK_CONNECTION, then
+	 * a SPEED_CHANGE.  could be useful in some configs.
+	 */
+	event = req->buf;
+	event->bmRequestType = 0xA1;
+	event->bNotificationType = CDC_NOTIFY_NETWORK_CONNECTION;
+	event->wValue = cpu_to_le16 (1);	/* connected */
+	event->wIndex = cpu_to_le16 (1);
+	event->wLength = 0;
+
+	req->length = 8;
+	req->complete = eth_status_complete;
+	value = usb_ep_queue (dev->status_ep, req, GFP_ATOMIC);
+	if (value < 0) {
+		DEBUG (dev, "status buf queue --> %d\n", value);
+		usb_ep_free_buffer (dev->status_ep,
+				req->buf, dev->req->dma, 16);
+		goto free_req;
+	}
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static void eth_setup_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	if (req->status || req->actual != req->length)
+		DEBUG ((struct eth_dev *) ep->driver_data,
+				"setup complete --> %d, %d/%d\n",
+				req->status, req->actual, req->length);
+}
+
+/* see section 3.8.2 table 10 of the CDC spec for more ethernet
+ * requests, mostly for filters (multicast, pm) and statistics
+ */
+#define CDC_SET_ETHERNET_PACKET_FILTER	0x43	/* required */
+
+static void eth_start (struct eth_dev *dev, int gfp_flags);
+
+/*
+ * The setup() callback implements all the ep0 functionality that's not
+ * handled lower down.  CDC has a number of less-common features:
+ *
+ *  - two interfaces:  control, and ethernet data
+ *  - data interface has two altsettings:  default, and active
+ *  - class-specific descriptors for the control interface
+ *  - a mandatory class-specific control request
+ */
+static int
+eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+	struct eth_dev		*dev = get_gadget_data (gadget);
+	struct usb_request	*req = dev->req;
+	int			value = -EOPNOTSUPP;
+
+	/* descriptors just go into the pre-allocated ep0 buffer,
+	 * while config change events may enable network traffic.
+	 */
+	switch (ctrl->bRequest) {
+
+	case USB_REQ_GET_DESCRIPTOR:
+		if (ctrl->bRequestType != USB_DIR_IN)
+			break;
+		switch (ctrl->wValue >> 8) {
+
+		case USB_DT_DEVICE:
+			value = min (ctrl->wLength, (u16) sizeof device_desc);
+			memcpy (req->buf, &device_desc, value);
+			break;
+#ifdef HIGHSPEED
+		case USB_DT_DEVICE_QUALIFIER:
+			value = min (ctrl->wLength, (u16) sizeof dev_qualifier);
+			memcpy (req->buf, &dev_qualifier, value);
+			break;
+
+		case USB_DT_OTHER_SPEED_CONFIG:
+			// FALLTHROUGH
+#endif /* HIGHSPEED */
+		case USB_DT_CONFIG:
+			value = config_buf (gadget->speed, req->buf,
+					ctrl->wValue >> 8,
+					ctrl->wValue & 0xff);
+			if (value >= 0)
+				value = min (ctrl->wLength, (u16) value);
+			break;
+
+		case USB_DT_STRING:
+			value = usb_gadget_get_string (&stringtab,
+					ctrl->wValue & 0xff, req->buf);
+			if (value >= 0)
+				value = min (ctrl->wLength, (u16) value);
+			break;
+		}
+		break;
+
+	case USB_REQ_SET_CONFIGURATION:
+		if (ctrl->bRequestType != 0)
+			break;
+		spin_lock (&dev->lock);
+		value = eth_set_config (dev, ctrl->wValue, GFP_ATOMIC);
+		spin_unlock (&dev->lock);
+		break;
+	case USB_REQ_GET_CONFIGURATION:
+		if (ctrl->bRequestType != USB_DIR_IN)
+			break;
+		*(u8 *)req->buf = dev->config;
+		value = min (ctrl->wLength, (u16) 1);
+		break;
+
+	case USB_REQ_SET_INTERFACE:
+		if (ctrl->bRequestType != USB_RECIP_INTERFACE
+				|| !dev->config
+				|| ctrl->wIndex > 1)
+			break;
+		spin_lock (&dev->lock);
+		switch (ctrl->wIndex) {
+		case 0:		/* control/master intf */
+			if (ctrl->wValue != 0)
+				break;
+#ifdef	EP_STATUS_NUM
+			if (dev->status_ep) {
+				usb_ep_disable (dev->status_ep);
+				usb_ep_enable (dev->status_ep, dev->status);
+			}
+#endif
+			value = 0;
+			break;
+		case 1:		/* data intf */
+			if (ctrl->wValue > 1)
+				break;
+			usb_ep_disable (dev->in_ep);
+			usb_ep_disable (dev->out_ep);
+
+			/* CDC requires the data transfers not be done from
+			 * the default interface setting ... also, setting
+			 * the non-default interface clears filters etc.
+			 */
+			if (ctrl->wValue == 1) {
+				usb_ep_enable (dev->in_ep, dev->in);
+				usb_ep_enable (dev->out_ep, dev->out);
+				netif_carrier_on (&dev->net);
+#ifdef	EP_STATUS_NUM
+				issue_start_status (dev);
+#endif
+				if (netif_running (&dev->net))
+					eth_start (dev, GFP_ATOMIC);
+			} else {
+				netif_stop_queue (&dev->net);
+				netif_carrier_off (&dev->net);
+			}
+			value = 0;
+			break;
+		}
+		spin_unlock (&dev->lock);
+		break;
+	case USB_REQ_GET_INTERFACE:
+		if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)
+				|| !dev->config
+				|| ctrl->wIndex > 1)
+			break;
+
+		/* if carrier is on, data interface is active. */
+		*(u8 *)req->buf =
+			((ctrl->wIndex == 1) && netif_carrier_ok (&dev->net))
+				? 1
+				: 0,
+		value = min (ctrl->wLength, (u16) 1);
+		break;
+
+	case CDC_SET_ETHERNET_PACKET_FILTER:
+		/* see 6.2.30: no data, wIndex = interface,
+		 * wValue = packet filter bitmap
+		 */
+		if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
+				|| ctrl->wLength != 0
+				|| ctrl->wIndex > 1)
+		DEBUG (dev, "NOP packet filter %04x\n", ctrl->wValue);
+		/* NOTE: table 62 has 5 filter bits to reduce traffic,
+		 * and we "must" support multicast and promiscuous.
+		 * this NOP implements a bad filter...
+		 */
+		value = 0;
+		break;
+
+	default:
+		VDEBUG (dev,
+			"unknown control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			ctrl->wValue, ctrl->wIndex, ctrl->wLength);
+	}
+
+	/* respond with data transfer before status phase? */
+	if (value > 0) {
+		req->length = value;
+		value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0) {
+			DEBUG (dev, "ep_queue --> %d\n", value);
+			req->status = 0;
+			eth_setup_complete (gadget->ep0, req);
+		}
+	}
+
+	/* host either stalls (value < 0) or reports success */
+	return value;
+}
+
+static void
+eth_disconnect (struct usb_gadget *gadget)
+{
+	struct eth_dev		*dev = get_gadget_data (gadget);
+	unsigned long		flags;
+
+	spin_lock_irqsave (&dev->lock, flags);
+	netif_stop_queue (&dev->net);
+	netif_carrier_off (&dev->net);
+	eth_reset_config (dev);
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	/* next we may get setup() calls to enumerate new connections;
+	 * or an unbind() during shutdown (including removing module).
+	 */
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* NETWORK DRIVER HOOKUP (to the layer above this driver) */
+
+static int eth_change_mtu (struct net_device *net, int new_mtu)
+{
+	struct eth_dev	*dev = (struct eth_dev *) net->priv;
+
+	if (new_mtu <= MIN_PACKET || new_mtu > MAX_PACKET)
+		return -ERANGE;
+	/* no zero-length packet read wanted after mtu-sized packets */
+	if (((new_mtu + sizeof (struct ethhdr)) % dev->in_ep->maxpacket) == 0)
+		return -EDOM;
+	net->mtu = new_mtu;
+	return 0;
+}
+
+static struct net_device_stats *eth_get_stats (struct net_device *net)
+{
+	return &((struct eth_dev *) net->priv)->stats;
+}
+
+static int eth_ethtool_ioctl (struct net_device *net, void *useraddr)
+{
+	struct eth_dev	*dev = (struct eth_dev *) net->priv;
+	u32		cmd;
+
+	if (get_user (cmd, (u32 *)useraddr))
+		return -EFAULT;
+	switch (cmd) {
+
+	case ETHTOOL_GDRVINFO: {	/* get driver info */
+		struct ethtool_drvinfo		info;
+
+		memset (&info, 0, sizeof info);
+		info.cmd = ETHTOOL_GDRVINFO;
+		strncpy (info.driver, shortname, sizeof info.driver);
+		strncpy (info.version, DRIVER_VERSION, sizeof info.version);
+		strncpy (info.fw_version, CHIP, sizeof info.fw_version);
+		strncpy (info.bus_info, dev->gadget->dev.bus_id,
+			sizeof info.bus_info);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+		}
+
+	case ETHTOOL_GLINK: {		/* get link status */
+		struct ethtool_value	edata = { ETHTOOL_GLINK };
+
+		edata.data = (dev->gadget->speed != USB_SPEED_UNKNOWN);
+		if (copy_to_user (useraddr, &edata, sizeof (edata)))
+			return -EFAULT;
+		return 0;
+		}
+
+	}
+	/* Note that the ethtool user space code requires EOPNOTSUPP */
+	return -EOPNOTSUPP;
+}
+
+static int eth_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
+{
+	switch (cmd) {
+	case SIOCETHTOOL:
+		return eth_ethtool_ioctl (net, (void *)rq->ifr_data);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static void defer_kevent (struct eth_dev *dev, int flag)
+{
+	set_bit (flag, &dev->todo);
+	if (!schedule_work (&dev->work))
+		ERROR (dev, "kevent %d may have been dropped\n", flag);
+	else
+		DEBUG (dev, "kevent %d scheduled\n", flag);
+}
+
+static void rx_complete (struct usb_ep *ep, struct usb_request *req);
+
+static int
+rx_submit (struct eth_dev *dev, struct usb_request *req, int gfp_flags)
+{
+	struct sk_buff		*skb;
+	int			retval = 0;
+	size_t			size;
+
+	size = (sizeof (struct ethhdr) + dev->net.mtu);
+
+	if ((skb = alloc_skb (size, gfp_flags)) == 0) {
+		DEBUG (dev, "no rx skb\n");
+		defer_kevent (dev, WORK_RX_MEMORY);
+		usb_ep_free_request (dev->out_ep, req);
+		return -ENOMEM;
+	}
+
+	req->buf = skb->data;
+	req->length = size;
+	req->complete = rx_complete;
+	req->context = skb;
+
+	if (netif_running (&dev->net)) {
+		retval = usb_ep_queue (dev->out_ep, req, gfp_flags);
+		if (retval == -ENOMEM)
+			defer_kevent (dev, WORK_RX_MEMORY);
+		if (retval)
+			DEBUG (dev, "%s %d\n", __FUNCTION__, retval);
+	} else {
+		DEBUG (dev, "%s stopped\n", __FUNCTION__);
+		retval = -ENOLINK;
+	}
+	if (retval) {
+		DEBUG (dev, "rx submit --> %d\n", retval);
+		dev_kfree_skb_any (skb);
+		usb_ep_free_request (dev->out_ep, req);
+	}
+	return retval;
+}
+
+static void rx_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	struct sk_buff	*skb = req->context;
+	struct eth_dev	*dev = ep->driver_data;
+	int		status = req->status;
+
+	switch (status) {
+
+	/* normal completion */
+	case 0:
+		skb_put (skb, req->actual);
+		if (MIN_PACKET > skb->len
+				|| skb->len > (MAX_PACKET + ETH_HLEN)) {
+			dev->stats.rx_errors++;
+			dev->stats.rx_length_errors++;
+			DEBUG (dev, "rx length %d\n", skb->len);
+			break;
+		}
+
+		skb->dev = &dev->net;
+		skb->protocol = eth_type_trans (skb, &dev->net);
+		dev->stats.rx_packets++;
+		dev->stats.rx_bytes += skb->len;
+
+		/* no buffer copies needed, unless hardware can't
+		 * use skb buffers.
+		 */
+		status = netif_rx (skb);
+		skb = 0;
+		break;
+
+	/* software-driven interface shutdown */
+	case -ECONNRESET:		// unlink
+	case -ESHUTDOWN:		// disconnect etc
+		VDEBUG (dev, "rx shutdown, code %d\n", status);
+		usb_ep_free_request (dev->out_ep, req);
+		req = 0;
+		break;
+
+	/* data overrun */
+	case -EOVERFLOW:
+		dev->stats.rx_over_errors++;
+		// FALLTHROUGH
+	    
+	default:
+		dev->stats.rx_errors++;
+		DEBUG (dev, "rx status %d\n", status);
+		break;
+	}
+
+	if (skb)
+		dev_kfree_skb_any (skb);
+
+	if (req)
+		rx_submit (dev, req, GFP_ATOMIC);
+}
+
+static void eth_work (void *_dev)
+{
+	struct eth_dev		*dev = _dev;
+
+	if (test_bit (WORK_RX_MEMORY, &dev->todo)) {
+		struct usb_request	*req = 0;
+
+		if (netif_running (&dev->net))
+			req = usb_ep_alloc_request (dev->in_ep, GFP_KERNEL);
+		else
+			clear_bit (WORK_RX_MEMORY, &dev->todo);
+		if (req != 0) {
+			clear_bit (WORK_RX_MEMORY, &dev->todo);
+			rx_submit (dev, req, GFP_KERNEL);
+		}
+	}
+
+	if (dev->todo)
+		DEBUG (dev, "work done, flags = 0x%lx\n", dev->todo);
+}
+
+static void tx_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	struct sk_buff	*skb = req->context;
+	struct eth_dev	*dev = ep->driver_data;
+
+	if (req->status)
+		dev->stats.tx_errors++;
+	else
+		dev->stats.tx_bytes += skb->len;
+	dev->stats.tx_packets++;
+
+	usb_ep_free_request (ep, req);
+	dev_kfree_skb_any (skb);
+
+	atomic_inc (&dev->tx_qlen);
+	if (netif_carrier_ok (&dev->net))
+		netif_wake_queue (&dev->net);
+}
+
+static int eth_start_xmit (struct sk_buff *skb, struct net_device *net)
+{
+	struct eth_dev		*dev = (struct eth_dev *) net->priv;
+	int			length = skb->len;
+	int			retval;
+	struct usb_request	*req = 0;
+
+	if (!(req = usb_ep_alloc_request (dev->in_ep, GFP_ATOMIC))) {
+		DEBUG (dev, "no request\n");
+		goto drop;
+	}
+
+	/* no buffer copies needed, unless the network stack did it
+	 * or the hardware can't use skb buffers.
+	 */
+	req->buf = skb->data;
+	req->context = skb;
+	req->complete = tx_complete;
+
+#ifdef	CONFIG_USB_ETH_SA1100
+	/* don't demand zlp (req->zero) support from all hardware */
+	if ((length % dev->in_ep->maxpacket) == 0)
+		length++;
+#else
+	/* use zlp framing on tx for strict CDC-Ether conformance,
+	 * though any robust network rx path ignores extra padding.
+	 */
+	req->zero = 1;
+#endif
+	req->length = length;
+
+#ifdef	HIGHSPEED
+	/* throttle highspeed IRQ rate back slightly */
+	req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH)
+		? ((atomic_read (&dev->tx_qlen) % TX_DELAY) != 0)
+		: 0;
+#endif
+
+	retval = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC);
+	switch (retval) {
+	default:
+		DEBUG (dev, "tx queue err %d\n", retval);
+		break;
+	case 0:
+		net->trans_start = jiffies;
+		if (atomic_dec_and_test (&dev->tx_qlen))
+			netif_stop_queue (net);
+	}
+
+	if (retval) {
+		DEBUG (dev, "drop, code %d\n", retval);
+drop:
+		dev->stats.tx_dropped++;
+		dev_kfree_skb_any (skb);
+		usb_ep_free_request (dev->in_ep, req);
+	}
+	return 0;
+}
+
+static void eth_start (struct eth_dev *dev, int gfp_flags)
+{
+	struct usb_request	*req;
+	int			retval = 0;
+	unsigned		i;
+	int			size = qlen (dev->gadget);
+
+	DEBUG (dev, "%s\n", __FUNCTION__);
+
+	/* fill the rx queue */
+	for (i = 0; retval == 0 && i < size; i++) {
+		req = usb_ep_alloc_request (dev->in_ep, gfp_flags);
+		if (req)
+			retval = rx_submit (dev, req, gfp_flags);
+		else if (i > 0)
+			defer_kevent (dev, WORK_RX_MEMORY);
+		else
+			retval = -ENOMEM;
+	}
+
+	/* and open the tx floodgates */ 
+	atomic_set (&dev->tx_qlen, size);
+	netif_wake_queue (&dev->net);
+}
+
+static int eth_open (struct net_device *net)
+{
+	struct eth_dev		*dev = (struct eth_dev *) net->priv;
+
+	DEBUG (dev, "%s\n", __FUNCTION__);
+	down (&dev->mutex);
+	if (netif_carrier_ok (&dev->net))
+		eth_start (dev, GFP_KERNEL);
+	up (&dev->mutex);
+	return 0;
+}
+
+static int eth_stop (struct net_device *net)
+{
+	struct eth_dev		*dev = (struct eth_dev *) net->priv;
+
+	DEBUG (dev, "%s\n", __FUNCTION__);
+	down (&dev->mutex);
+	netif_stop_queue (net);
+
+	DEBUG (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
+		dev->stats.rx_packets, dev->stats.tx_packets, 
+		dev->stats.rx_errors, dev->stats.tx_errors
+		);
+
+	/* ensure there are no more active requests */
+	if (dev->gadget->speed != USB_SPEED_UNKNOWN) {
+		usb_ep_disable (dev->in_ep);
+		usb_ep_disable (dev->out_ep);
+		if (netif_carrier_ok (&dev->net)) {
+			DEBUG (dev, "host still using in/out endpoints\n");
+			usb_ep_enable (dev->in_ep, dev->in);
+			usb_ep_enable (dev->out_ep, dev->out);
+		}
+#ifdef	EP_STATUS_NUM
+		usb_ep_disable (dev->status_ep);
+		usb_ep_enable (dev->status_ep, dev->status);
+#endif
+	}
+
+	up (&dev->mutex);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+eth_unbind (struct usb_gadget *gadget)
+{
+	struct eth_dev		*dev = get_gadget_data (gadget);
+
+	DEBUG (dev, "unbind\n");
+	down (&dev->mutex);
+
+	/* we've already been disconnected ... no i/o is active */
+	if (dev->req) {
+		usb_ep_free_buffer (gadget->ep0,
+				dev->req->buf, dev->req->dma,
+				USB_BUFSIZ);
+		usb_ep_free_request (gadget->ep0, dev->req);
+	}
+
+	unregister_netdev (&dev->net);
+	up (&dev->mutex);
+
+	/* assuming we used keventd, it must quiesce too */
+	flush_scheduled_work ();
+
+	kfree (dev);
+	set_gadget_data (gadget, 0);
+}
+
+static int
+eth_bind (struct usb_gadget *gadget)
+{
+	struct eth_dev		*dev;
+	struct net_device	*net;
+	u8			node_id [ETH_ALEN];
+
+	/* just one upstream link at a time */
+	if (ethaddr [0] != 0)
+		return -ENODEV;
+
+	dev = kmalloc (sizeof *dev, SLAB_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	memset (dev, 0, sizeof *dev);
+	spin_lock_init (&dev->lock);
+	init_MUTEX_LOCKED (&dev->mutex);
+	INIT_WORK (&dev->work, eth_work, dev);
+
+	/* network device setup */
+	net = &dev->net;
+	SET_MODULE_OWNER (net);
+	net->priv = dev;
+	strcpy (net->name, "usb%d");
+	ether_setup (net);
+
+	/* one random address for the gadget device ... both of these could
+	 * reasonably come from an id prom or a module parameter.
+	 */
+	get_random_bytes (net->dev_addr, ETH_ALEN);
+	net->dev_addr [0] &= 0xfe;	// clear multicast bit
+	net->dev_addr [0] |= 0x02;	// set local assignment bit (IEEE802)
+
+	/* ... another address for the host, on the other end of the
+	 * link, gets exported through CDC (see CDC spec table 41)
+	 */
+	get_random_bytes (node_id, sizeof node_id);
+	node_id [0] &= 0xfe;	// clear multicast bit
+	node_id [0] |= 0x02;    // set local assignment bit (IEEE802)
+	snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X",
+		node_id [0], node_id [1], node_id [2],
+		node_id [3], node_id [4], node_id [5]);
+
+	net->change_mtu = eth_change_mtu;
+	net->get_stats = eth_get_stats;
+	net->hard_start_xmit = eth_start_xmit;
+	net->open = eth_open;
+	net->stop = eth_stop;
+	// watchdog_timeo, tx_timeout ...
+	// set_multicast_list
+	net->do_ioctl = eth_ioctl;
+
+	/* preallocate control response and buffer */
+	dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
+	if (!dev->req)
+		goto enomem;
+	dev->req->complete = eth_setup_complete;
+	dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ,
+				&dev->req->dma, GFP_KERNEL);
+	if (!dev->req->buf) {
+		usb_ep_free_request (gadget->ep0, dev->req);
+		goto enomem;
+	}
+
+	/* finish hookup to lower layer ... */
+	dev->gadget = gadget;
+	set_gadget_data (gadget, dev);
+	gadget->ep0->driver_data = dev;
+
+	/* two kinds of host-initiated state changes:
+	 *  - iff DATA transfer is active, carrier is "on"
+	 *  - tx queueing enabled if open *and* carrier is "on"
+	 */
+	INFO (dev, "%s, host enet %s, version: " DRIVER_VERSION "\n",
+			driver_desc, ethaddr);
+	register_netdev (&dev->net);
+	netif_stop_queue (&dev->net);
+	netif_carrier_off (&dev->net);
+
+	up (&dev->mutex);
+	return 0;
+
+enomem:
+	eth_unbind (gadget);
+	return -ENOMEM;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_gadget_driver eth_driver = {
+#ifdef HIGHSPEED
+	.speed		= USB_SPEED_HIGH,
+#else
+	.speed		= USB_SPEED_FULL,
+#endif
+	.function	= (char *) driver_desc,
+	.bind		= eth_bind,
+	.unbind		= eth_unbind,
+
+	.setup		= eth_setup,
+	.disconnect	= eth_disconnect,
+
+	.driver 	= {
+		.name		= (char *) shortname,
+		// .shutdown = ...
+		// .suspend = ...
+		// .resume = ...
+	},
+};
+
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_AUTHOR ("David Brownell");
+MODULE_LICENSE ("GPL");
+
+
+static int __init init (void)
+{
+	return usb_gadget_register_driver (&eth_driver);
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+	usb_gadget_unregister_driver (&eth_driver);
+}
+module_exit (cleanup);
+
diff -urN linux-2.5.69-bk5/drivers/usb/gadget/net2280.c linux-2.5.69-bk6/drivers/usb/gadget/net2280.c
--- linux-2.5.69-bk5/drivers/usb/gadget/net2280.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.69-bk6/drivers/usb/gadget/net2280.c	2003-05-11 04:43:33.000000000 -0700
@@ -0,0 +1,2660 @@
+/*
+ * Driver for the NetChip 2280 USB device controller.
+ * Specs and errata are available from <http://www.netchip.com>.
+ *
+ * NetChip Technology Inc. supported the development of this driver.
+ *
+ *
+ * CODE STATUS HIGHLIGHTS
+ *
+ * Used with a gadget driver like "zero.c" this enumerates fine to Windows
+ * or Linux hosts; handles disconnect, reconnect, and reset, for full or
+ * high speed operation; and passes USB-IF "chapter 9" tests.
+ *
+ * Handles standard stress loads from the Linux "usbtest" driver, with
+ * either DMA (default) or PIO (use_dma=n) used for ep-{a,b,c,d}.  Testing
+ * with "ttcp" (and the "ether.c" driver) behaves nicely too.
+ *
+ * DMA is enabled by default.  Drivers using transfer queues might use
+ * DMA chaining to remove IRQ latencies between transfers.  (Except when
+ * short OUT transfers happen.)  Drivers can use the req->no_interrupt
+ * hint to completely eliminate some IRQs, if a later IRQ is guaranteed
+ * and DMA chaining is enabled.
+ */
+
+// #define NET2280_DMA_OUT_WORKAROUND
+// #define USE_DMA_CHAINING
+
+
+/*
+ * Copyright (C) 2003 David Brownell
+ *
+ * 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
+ */
+
+#define DEBUG	1
+// #define	VERBOSE		/* extra debug messages (success too) */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+
+#define DRIVER_DESC		"NetChip 2280 USB Peripheral Controller"
+#define	DRIVER_VERSION		"May Day 2003"
+
+#define	DMA_ADDR_INVALID	(~(dma_addr_t)0)
+#define	EP_DONTUSE		13	/* nonzero */
+
+#define USE_RDK_LEDS		/* GPIO pins control three LEDs */
+#define USE_SYSFS_DEBUG_FILES
+
+
+static const char driver_name [] = "net2280";
+static const char driver_desc [] = DRIVER_DESC;
+
+static const char ep0name [] = "ep0";
+static const char *ep_name [] = {
+	ep0name,
+	"ep-a", "ep-b", "ep-c", "ep-d",
+	"ep-e", "ep-f",
+};
+
+static int use_dma = 1;
+
+/* "modprobe net2280 use_dma=n" etc */
+module_param (use_dma, bool, S_IRUGO|S_IWUSR);
+
+#define	DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
+
+#if defined(USE_SYSFS_DEBUG_FILES) || defined (DEBUG)
+static char *type_string (u8 bmAttributes)
+{
+	switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
+	case USB_ENDPOINT_XFER_BULK:	return "bulk";
+	case USB_ENDPOINT_XFER_ISOC:	return "iso";
+	case USB_ENDPOINT_XFER_INT:	return "intr";
+	};
+	return "control";
+}
+#endif
+
+#include "net2280.h"
+
+#define valid_bit	cpu_to_le32 (1 << VALID_BIT)
+#define dma_done_ie	cpu_to_le32 (1 << DMA_DONE_INTERRUPT_ENABLE)
+
+/*-------------------------------------------------------------------------*/
+
+static int
+net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+	struct net2280		*dev;
+	struct net2280_ep	*ep;
+	u32			max, tmp;
+	unsigned long		flags;
+
+	ep = container_of (_ep, struct net2280_ep, ep);
+	if (!_ep || !desc || ep->desc || _ep->name == ep0name
+			|| desc->bDescriptorType != USB_DT_ENDPOINT)
+		return -EINVAL;
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	/* erratum 0119 workaround ties up an endpoint number */
+	if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE)
+		return -EDOM;
+
+	/* sanity check ep-e/ep-f since their fifos are small */
+	max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff;
+	if (ep->num > 4 && max > 64)
+		return -ERANGE;
+
+	spin_lock_irqsave (&dev->lock, flags);
+	_ep->maxpacket = max & 0x7ff;
+	ep->desc = desc;
+
+	/* ep_reset() has already been called */
+	ep->stopped = 0;
+
+	/* set speed-dependent max packet; may kick in high bandwidth */
+	set_idx_reg (dev->regs, REG_EP_MAXPKT (dev, ep->num), max);
+
+	/* FIFO lines can't go to different packets.  PIO is ok, so
+	 * use it instead of troublesome (non-bulk) multi-packet DMA.
+	 */
+	if (ep->is_in && ep->dma && (max % 4) != 0) {
+		DEBUG (ep->dev, "%s, no IN dma for maxpacket %d\n",
+			ep->ep.name, ep->ep.maxpacket);
+		ep->dma = 0;
+	}
+
+	/* set type, direction, address; reset fifo counters */
+	writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat);
+	tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+	if (tmp == USB_ENDPOINT_XFER_INT) {
+		/* not just because of erratum 0105; avoid ever
+		 * kicking in the "toggle-irrelevant" mode.
+		 */
+		tmp = USB_ENDPOINT_XFER_BULK;
+	}
+	ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC) ? 1 : 0;
+	tmp <<= ENDPOINT_TYPE;
+	tmp |= desc->bEndpointAddress;
+	tmp |= (4 << ENDPOINT_BYTE_COUNT);	/* default full fifo lines */
+	tmp |= 1 << ENDPOINT_ENABLE;
+	wmb ();
+
+	/* for OUT transfers, block the rx fifo until a read is posted */
+	ep->is_in = (tmp & USB_DIR_IN) != 0;
+	if (!ep->is_in)
+		writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+
+	writel (tmp, &ep->regs->ep_cfg);
+
+#ifdef NET2280_DMA_OUT_WORKAROUND
+	if (!ep->is_in)
+		ep->dma = 0;
+#endif
+
+	/* enable irqs */
+	if (!ep->dma) {				/* pio, per-packet */
+		tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0);
+		writel (tmp, &dev->regs->pciirqenb0);
+
+		tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE)
+			| (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE)
+			| readl (&ep->regs->ep_irqenb);
+		writel (tmp, &ep->regs->ep_irqenb);
+	} else {				/* dma, per-request */
+		tmp = (1 << (8 + ep->num));	/* completion */
+		tmp |= readl (&dev->regs->pciirqenb1);
+		writel (tmp, &dev->regs->pciirqenb1);
+
+		/* for short OUT transfers, dma completions can't
+		 * advance the queue; do it pio-style, by hand.
+		 * NOTE erratum 0112 workaround #2
+		 */
+		if ((desc->bEndpointAddress & USB_DIR_IN) == 0) {
+			tmp = (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE);
+			writel (tmp, &ep->regs->ep_irqenb);
+
+			tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0);
+			writel (tmp, &dev->regs->pciirqenb0);
+		}
+	}
+
+	tmp = desc->bEndpointAddress;
+	DEBUG (dev, "enabled %s (ep%d%s-%s) %s max %04x\n",
+		_ep->name, tmp & 0x0f, DIR_STRING (tmp),
+		type_string (desc->bmAttributes),
+		ep->dma ? "dma" : "pio", max);
+
+	/* pci writes may still be posted */
+	spin_unlock_irqrestore (&dev->lock, flags);
+	return 0;
+}
+
+static int handshake (u32 *ptr, u32 mask, u32 done, int usec)
+{
+	u32	result;
+
+	do {
+		result = readl (ptr);
+		if (result == ~(u32)0)		/* "device unplugged" */
+			return -ENODEV;
+		result &= mask;
+		if (result == done)
+			return 0;
+		udelay (1);
+		usec--;
+	} while (usec > 0);
+#ifdef DEBUG
+	if (done == 0) dump_stack (); 		/* ignore out_flush timeout */
+#endif
+	return -ETIMEDOUT;
+}
+
+static struct usb_ep_ops net2280_ep_ops;
+
+static void ep_reset (struct net2280_regs *regs, struct net2280_ep *ep)
+{
+	u32		tmp;
+
+	ep->desc = 0;
+	INIT_LIST_HEAD (&ep->queue);
+
+	ep->ep.maxpacket = ~0;
+	ep->ep.ops = &net2280_ep_ops;
+
+	/* disable the dma, irqs, endpoint... */
+	if (ep->dma) {
+		writel (0, &ep->dma->dmactl);
+		writel (  (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT)
+			| (1 << DMA_TRANSACTION_DONE_INTERRUPT)
+			| (1 << DMA_ABORT)
+			, &ep->dma->dmastat);
+
+		tmp = readl (&regs->pciirqenb0);
+		tmp &= ~(1 << ep->num);
+		writel (tmp, &regs->pciirqenb0);
+	} else {
+		tmp = readl (&regs->pciirqenb1);
+		tmp &= ~(1 << (8 + ep->num));	/* completion */
+		writel (tmp, &regs->pciirqenb1);
+	}
+	writel (0, &ep->regs->ep_irqenb);
+
+	/* init to our chosen defaults, notably so that we NAK OUT
+	 * packets until the driver queues a read (+note erratum 0112)
+	 */
+	writel (  (1 << SET_NAK_OUT_PACKETS_MODE)
+		| (1 << SET_NAK_OUT_PACKETS)
+		| (1 << CLEAR_EP_HIDE_STATUS_PHASE)
+		| (1 << CLEAR_INTERRUPT_MODE)
+		| (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
+		| (1 << CLEAR_ENDPOINT_TOGGLE)
+		| (1 << CLEAR_ENDPOINT_HALT)
+		, &ep->regs->ep_rsp);
+
+	/* scrub most status bits, and flush any fifo state */
+	writel (  (1 << TIMEOUT)
+		| (1 << USB_STALL_SENT)
+		| (1 << USB_IN_NAK_SENT)
+		| (1 << USB_IN_ACK_RCVD)
+		| (1 << USB_OUT_PING_NAK_SENT)
+		| (1 << USB_OUT_ACK_SENT)
+		| (1 << FIFO_OVERFLOW)
+		| (1 << FIFO_UNDERFLOW)
+		| (1 << FIFO_FLUSH)
+		| (1 << SHORT_PACKET_OUT_DONE_INTERRUPT)
+		| (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)
+		| (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+		| (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
+		| (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+		| (1 << DATA_IN_TOKEN_INTERRUPT)
+		, &ep->regs->ep_stat);
+
+	/* fifo size is handled separately */
+}
+
+static void nuke (struct net2280_ep *);
+
+static int net2280_disable (struct usb_ep *_ep)
+{
+	struct net2280_ep	*ep;
+	unsigned long		flags;
+
+	ep = container_of (_ep, struct net2280_ep, ep);
+	if (!_ep || !ep->desc || _ep->name == ep0name)
+		return -EINVAL;
+
+	spin_lock_irqsave (&ep->dev->lock, flags);
+	nuke (ep);
+	ep_reset (ep->dev->regs, ep);
+
+	VDEBUG (ep->dev, "disabled %s %s\n",
+			ep->dma ? "dma" : "pio", _ep->name);
+
+	/* synch memory views with the device */
+	(void) readl (&ep->regs->ep_cfg);
+
+	if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4)
+		ep->dma = &ep->dev->dma [ep->num - 1];
+
+	spin_unlock_irqrestore (&ep->dev->lock, flags);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *
+net2280_alloc_request (struct usb_ep *_ep, int gfp_flags)
+{
+	struct net2280_ep	*ep;
+	struct net2280_request	*req;
+
+	if (!_ep)
+		return 0;
+	ep = container_of (_ep, struct net2280_ep, ep);
+
+	req = kmalloc (sizeof *req, gfp_flags);
+	if (!req)
+		return 0;
+
+	memset (req, 0, sizeof *req);
+	req->req.dma = DMA_ADDR_INVALID;
+	INIT_LIST_HEAD (&req->queue);
+
+	/* this dma descriptor may be swapped with the previous dummy */
+	if (ep->dma) {
+		struct net2280_dma	*td;
+
+		td = pci_pool_alloc (ep->dev->requests, gfp_flags,
+				&req->td_dma);
+		if (!td) {
+			kfree (req);
+			return 0;
+		}
+		td->dmacount = 0;	/* not VALID */
+		td->dmaaddr = cpu_to_le32 (DMA_ADDR_INVALID);
+		req->td = td;
+	}
+	return &req->req;
+}
+
+static void
+net2280_free_request (struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct net2280_ep	*ep;
+	struct net2280_request	*req;
+
+	ep = container_of (_ep, struct net2280_ep, ep);
+	if (!ep || !_req || (!ep->desc && ep->num != 0))
+		return;
+
+	req = container_of (_req, struct net2280_request, req);
+	WARN_ON (!list_empty (&req->queue));
+	if (req->td)
+		pci_pool_free (ep->dev->requests, req->td, req->td_dma);
+	kfree (req);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#undef USE_KMALLOC
+
+/* many common platforms have dma-coherent caches, which means that it's
+ * safe to use kmalloc() memory for all i/o buffers without using any
+ * cache flushing calls.  (unless you're trying to share cache lines
+ * between dma and non-dma activities, which is a slow idea in any case.)
+ *
+ * other platforms need more care, with 2.5 having a moderately general
+ * solution (which falls down for allocations smaller than one page)
+ * that improves significantly on the 2.4 PCI allocators by removing
+ * the restriction that memory never be freed in_interrupt().
+ */
+#if	defined(CONFIG_X86)
+#define USE_KMALLOC
+
+#elif	define(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE)
+#define USE_KMALLOC
+
+/* FIXME there are other cases, including an x86-64 one ...  */
+#endif
+
+/* allocating buffers this way eliminates dma mapping overhead, which
+ * on some platforms will mean eliminating a per-io buffer copy.  with
+ * some kinds of system caches, further tweaks may still be needed.
+ */
+static void *
+net2280_alloc_buffer (
+	struct usb_ep		*_ep,
+	unsigned		bytes,
+	dma_addr_t		*dma,
+	int			gfp_flags
+)
+{
+	void			*retval;
+	struct net2280_ep	*ep;
+
+	ep = container_of (_ep, struct net2280_ep, ep);
+	if (!ep || (!ep->desc && ep->num != 0))
+		return 0;
+
+	*dma = DMA_ADDR_INVALID;
+	if (ep->dma) {
+#if	defined(USE_KMALLOC)
+		retval = kmalloc (bytes, gfp_flags);
+		if (retval)
+			*dma = virt_to_phys (retval);
+
+#elif	LINUX_VERSION_CODE > KERNEL_VERSION(2,5,58)
+#warning Using dma_alloc_consistent even with sub-page allocations
+		/* the main problem with this call is that it wastes memory
+		 * on typical 1/N page allocations: it allocates 1-N pages.
+		 */
+		retval = dma_alloc_coherent (&ep->dev->pdev->dev,
+				bytes, dma, gfp_flags);
+#else
+#error No dma-coherent memory allocator is available
+		/* pci_alloc_consistent works, but pci_free_consistent()
+		 * isn't safe in_interrupt().  plus, in addition to the
+		 * 1/Nth page weakness, it doesn't understand gfp_flags.
+		 */
+#endif
+	} else
+		retval = kmalloc (bytes, gfp_flags);
+
+	return retval;
+}
+
+static void
+net2280_free_buffer (
+	struct usb_ep *_ep,
+	void *buf,
+	dma_addr_t dma,
+	unsigned bytes
+) {
+	/* free memory into the right allocator */
+#ifndef	USE_KMALLOC
+	if (dma != DMA_ADDR_INVALID)
+		dma_free_coherent (ep->dev->pdev, bytes, dma);
+	else
+#endif
+		kfree (buf);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* load a packet into the fifo we use for usb IN transfers.
+ * works for all endpoints.
+ *
+ * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo
+ * at a time, but this code is simpler because it knows it only writes
+ * one packet.  ep-a..ep-d should use dma instead.
+ */
+static void
+write_fifo (struct net2280_ep *ep, struct usb_request *req)
+{
+	struct net2280_ep_regs	*regs = ep->regs;
+	u8			*buf;
+	unsigned		count, total;
+
+	/* INVARIANT:  fifo is currently empty. (testable) */
+
+	if (req) {
+		total = req->length - req->actual;
+		buf = req->buf + req->actual;
+	} else {
+		total = 0;
+		buf = 0;
+	}
+
+	/* write just one packet at a time */
+	count = min (ep->ep.maxpacket, total);
+	VDEBUG (ep->dev, "write %s fifo (IN) %d bytes%s req %p\n",
+			ep->ep.name, count,
+			(count != ep->ep.maxpacket) ? " (short)" : "",
+			req);
+	while (count >= 4) {
+		/* NOTE be careful if you try to align these. fifo lines
+		 * should normally be full (4 bytes) and successive partial
+		 * lines are ok only in certain cases.
+		 */
+		writel (get_unaligned ((u32 *)buf), &regs->ep_data);
+		buf += 4;
+		count -= 4;
+	}
+
+	/* last fifo entry is "short" unless we wrote a full packet */
+	if (total < ep->ep.maxpacket) {
+		u32	tmp = count ? get_unaligned ((u32 *)buf) : count;
+		set_fifo_bytecount (ep, count & 0x03);
+		writel (tmp, &regs->ep_data);
+	}
+
+	/* pci writes may still be posted */
+}
+
+/* work around erratum 0106: PCI and USB race over the OUT fifo.
+ * caller guarantees chiprev 0100, out endpoint is NAKing, and
+ * there's no real data in the fifo.
+ */
+static void out_flush (struct net2280_ep *ep)
+{
+	u32	*statp, tmp;
+
+	ASSERT_OUT_NAKING (ep);
+
+	statp = &ep->regs->ep_stat;
+	writel (  (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+		| (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+		, statp);
+	writel ((1 << FIFO_FLUSH), statp);
+	mb ();
+	tmp = readl (statp);
+	if (tmp & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) {
+		unsigned	usec;
+
+		if (ep->dev->gadget.speed == USB_SPEED_HIGH) {
+			if (ep->ep.maxpacket <= 512)
+				usec = 10;	/* 512 byte bulk */
+			else
+				usec = 21;	/* 1024 byte interrupt */
+		} else
+			usec = 50;		/* 64 byte bulk/interrupt */
+		handshake (statp, (1 << USB_OUT_PING_NAK_SENT),
+				(1 << USB_OUT_PING_NAK_SENT), usec);
+		/* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */
+	}
+}
+
+/* unload packet(s) from the fifo we use for usb OUT transfers.
+ * returns true iff the request completed, because of short packet
+ * or the request buffer having filled with full packets.
+ *
+ * for ep-a..ep-d this will read multiple packets out when they
+ * have been accepted.
+ */
+static int
+read_fifo (struct net2280_ep *ep, struct net2280_request *req)
+{
+	struct net2280_ep_regs	*regs = ep->regs;
+	u8			*buf = req->req.buf + req->req.actual;
+	unsigned		count, tmp, is_short;
+	unsigned		cleanup = 0, prevent = 0;
+
+	/* erratum 0106 ... packets coming in during fifo reads might
+	 * be incompletely rejected.  not all cases have workarounds.
+	 */
+	if (ep->dev->chiprev == 0x0100) {
+		tmp = readl (&ep->regs->ep_stat);
+		if ((tmp & (1 << NAK_OUT_PACKETS)))
+			/* cleanup = 1 */;
+		else if ((tmp & (1 << FIFO_FULL))
+				/* don't break hs PING protocol ... */
+				|| ep->dev->gadget.speed == USB_SPEED_FULL) {
+			start_out_naking (ep);
+			prevent = 1;
+		}
+		/* else: hope we don't see the problem */
+	}
+
+	/* never overflow the rx buffer. the fifo reads packets until
+	 * it sees a short one; we might not be ready for them all.
+	 */
+	count = readl (&regs->ep_avail);
+	tmp = req->req.length - req->req.actual;
+	if (count > tmp) {
+		unsigned	over = tmp % ep->ep.maxpacket;
+
+		/* FIXME handle this consistently between PIO and DMA */
+		if (over) {
+			ERROR (ep->dev,
+				"%s out fifo %d bytes, over %d extra %d\n",
+				ep->ep.name, count, over, count - tmp);
+			req->req.status = -EOVERFLOW;
+			tmp -= over;
+		}
+		count = tmp;
+	}
+	req->req.actual += count;
+
+	is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0);
+
+	VDEBUG (ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n",
+			ep->ep.name, count, is_short ? " (short)" : "",
+			cleanup ? " flush" : "", prevent ? " nak" : "",
+			req, req->req.actual, req->req.length);
+
+	while (count >= 4) {
+		put_unaligned (readl (&regs->ep_data), (u32 *)buf);
+		buf += 4;
+		count -= 4;
+	}
+	if (count) {
+		tmp = readl (&regs->ep_data);
+		do {
+			*buf++ = (u8) tmp;
+			tmp >>= 8;
+		} while (--count);
+	}
+	if (cleanup)
+		out_flush (ep);
+	if (prevent) {
+		writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+		(void) readl (&ep->regs->ep_rsp);
+	}
+
+	return is_short || ((req->req.actual == req->req.length)
+				&& !req->req.zero);
+}
+
+/* fill out dma descriptor to match a given request */
+static inline void
+fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid)
+{
+	struct net2280_dma	*td = req->td;
+	u32			dmacount = req->req.length;
+
+	/* don't let DMA continue after a short OUT packet,
+	 * so overruns can't affect the next transfer.
+	 */
+	if (ep->is_in)
+		dmacount |= (1 << DMA_DIRECTION);
+	else if ((dmacount % ep->ep.maxpacket) != 0)
+		dmacount |= (1 << END_OF_CHAIN);
+
+	req->valid = valid;
+	if (valid)
+		dmacount |= (1 << VALID_BIT);
+#ifdef USE_DMA_CHAINING
+	if (!req->req.no_interrupt)
+#endif
+		dmacount |= (1 << DMA_DONE_INTERRUPT_ENABLE);
+
+	/* td->dmadesc = previously set by caller */
+	td->dmaaddr = cpu_to_le32p (&req->req.dma);
+
+	/* 2280 may be polling VALID_BIT through ep->dma->dmadesc */
+	wmb ();
+	td->dmacount = cpu_to_le32p (&dmacount);
+}
+
+static const u32 dmactl_default =
+		  (1 << DMA_CLEAR_COUNT_ENABLE)
+		/* erratum 0116 workaround part 1 (use POLLING) */
+		| (POLL_100_USEC << DESCRIPTOR_POLLING_RATE)
+		| (1 << DMA_VALID_BIT_POLLING_ENABLE)
+		| (1 << DMA_VALID_BIT_ENABLE)
+		| (1 << DMA_SCATTER_GATHER_ENABLE)
+		/* erratum 0116 workaround part 2 (no AUTOSTART) */
+		| (1 << DMA_ENABLE);
+
+static inline void spin_stop_dma (struct net2280_dma_regs *dma)
+{
+	handshake (&dma->dmactl, (1 << DMA_ENABLE), 0, 50);
+}
+
+static inline void stop_dma (struct net2280_dma_regs *dma)
+{
+	writel (dmactl_default & ~(1 << DMA_ENABLE), &dma->dmactl);
+	spin_stop_dma (dma);
+}
+
+static void start_dma (struct net2280_ep *ep, struct net2280_request *req)
+{
+	u32			tmp;
+	int			clear_nak = 0;
+	struct net2280_dma_regs	*dma = ep->dma;
+
+	/* FIXME can't use DMA for ZLPs */
+
+	/* previous OUT packet might have been short */
+	if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat))
+		 		& (1 << NAK_OUT_PACKETS)) != 0) {
+		writel ((1 << SHORT_PACKET_TRANSFERRED_INTERRUPT),
+			&ep->regs->ep_stat);
+
+		tmp = readl (&ep->regs->ep_avail);
+		if (tmp == 0)
+			clear_nak = 1;
+		else {
+			/* transfer all/some fifo data */
+			writel (req->req.dma, &dma->dmaaddr);
+			tmp = min (tmp, req->req.length);
+
+			/* dma irq, faking scatterlist status */
+			req->td->dmacount = cpu_to_le32 (req->req.length - tmp);
+			writel ((1 << DMA_DONE_INTERRUPT_ENABLE)
+				| tmp, &dma->dmacount);
+
+			writel ((1 << DMA_ENABLE), &dma->dmactl);
+			writel ((1 << DMA_START), &dma->dmastat);
+			return;
+		}
+	}
+
+	/* on this path we know there's no dma queue (yet) */
+	WARN_ON (readl (&dma->dmactl) & (1 << DMA_ENABLE));
+	tmp = dmactl_default;
+
+	/* force packet boundaries between dma requests, but prevent the
+	 * controller from automagically writing a last "short" packet
+	 * (zero length) unless the driver explicitly said to do that.
+	 */
+	if (ep->is_in) {
+		if (likely ((req->req.length % ep->ep.maxpacket) != 0
+				|| req->req.zero)) {
+			tmp |= (1 << DMA_FIFO_VALIDATE);
+			ep->in_fifo_validate = 1;
+		} else
+			ep->in_fifo_validate = 0;
+	}
+
+	/* init req->td, pointing to the current dummy */
+	req->td->dmadesc = cpu_to_le32 (ep->td_dma);
+	fill_dma_desc (ep, req, 1);
+
+#ifdef USE_DMA_CHAINING
+	writel (  (1 << VALID_BIT)
+		| (ep->is_in << DMA_DIRECTION)
+		| 0, &dma->dmacount);
+#else
+	req->td->dmacount |= cpu_to_le32 (1 << END_OF_CHAIN);
+#endif
+
+	writel (req->td_dma, &dma->dmadesc);
+	writel (tmp, &dma->dmactl);
+
+	/* erratum 0116 workaround part 3:  pci arbiter away from net2280 */
+	(void) readl (&ep->dev->pci->pcimstctl);
+
+	writel ((1 << DMA_START), &dma->dmastat);
+
+	/* recover from previous short read; erratum 0112 workaround #1 */
+	if (clear_nak)
+		writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+}
+
+static inline void
+queue_dma (struct net2280_ep *ep, struct net2280_request *req, int valid)
+{
+	struct net2280_dma	*end;
+	dma_addr_t		tmp;
+
+	/* swap new dummy for old, link; fill and maybe activate */
+	end = ep->dummy;
+	ep->dummy = req->td;
+	req->td = end;
+
+	tmp = ep->td_dma;
+	ep->td_dma = req->td_dma;
+	req->td_dma = tmp;
+
+	end->dmadesc = cpu_to_le32 (ep->td_dma);
+
+	fill_dma_desc (ep, req, valid);
+}
+
+static void
+done (struct net2280_ep *ep, struct net2280_request *req, int status)
+{
+	struct net2280		*dev;
+	unsigned		stopped = ep->stopped;
+
+	list_del_init (&req->queue);
+
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	dev = ep->dev;
+	if (req->mapped) {
+		pci_unmap_single (dev->pdev, req->req.dma, req->req.length,
+			ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+		req->req.dma = DMA_ADDR_INVALID;
+		req->mapped = 0;
+	}
+
+	if (status && status != -ESHUTDOWN)
+		VDEBUG (dev, "complete %s req %p stat %d len %u/%u\n",
+			ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length);
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+	spin_unlock (&dev->lock);
+	req->req.complete (&ep->ep, &req->req);
+	spin_lock (&dev->lock);
+	ep->stopped = stopped;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+net2280_queue (struct usb_ep *_ep, struct usb_request *_req, int gfp_flags)
+{
+	struct net2280_request	*req;
+	struct net2280_ep	*ep;
+	struct net2280		*dev;
+	unsigned long		flags;
+
+	/* we always require a cpu-view buffer, so that we can
+	 * always use pio (as fallback or whatever).
+	 */
+	req = container_of (_req, struct net2280_request, req);
+	if (!_req || !_req->complete || !_req->buf
+			|| !list_empty (&req->queue))
+		return -EINVAL;
+	if (_req->length > (~0 & DMA_BYTE_COUNT_MASK))
+		return -EDOM;
+	ep = container_of (_ep, struct net2280_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0))
+		return -EINVAL;
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	/* FIXME implement PIO fallback for ZLPs with DMA */
+	if (ep->dma && _req->length == 0)
+		return -EOPNOTSUPP;
+
+	/* set up dma mapping in case the caller didn't */
+	if (ep->dma && _req->dma == DMA_ADDR_INVALID) {
+		_req->dma = pci_map_single (dev->pdev, _req->buf, _req->length,
+			ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+		req->mapped = 1;
+	}
+
+#if 0
+	VDEBUG (dev, "%s queue req %p, len %d buf %p\n",
+			_ep->name, _req, _req->length, _req->buf);
+#endif
+
+	spin_lock_irqsave (&dev->lock, flags);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+	req->dma_done = 0;
+
+	/* kickstart this i/o queue? */
+	if (list_empty (&ep->queue) && !ep->stopped) {
+		/* use DMA if the endpoint supports it, else pio */
+		if (ep->dma)
+			start_dma (ep, req);
+		else {
+			/* maybe there's no control data, just status ack */
+			if (ep->num == 0 && _req->length == 0)
+				goto done;
+
+			/* PIO ... stuff the fifo, or unblock it.  */
+			if (ep->is_in)
+				write_fifo (ep, _req);
+			else if (list_empty (&ep->queue)) {
+				u32	s;
+
+				/* OUT FIFO might have packet(s) buffered */
+				s = readl (&ep->regs->ep_stat);
+				if ((s & (1 << FIFO_EMPTY)) == 0) {
+					/* note:  _req->short_not_ok is
+					 * ignored here since PIO _always_
+					 * stops queue advance here, and
+					 * _req->status doesn't change for
+					 * short reads (only _req->actual)
+					 */
+					if (read_fifo (ep, req)) {
+						done (ep, req, 0);
+						/* don't queue it */
+						req = 0;
+					} else
+						s = readl (&ep->regs->ep_stat);
+				}
+
+				/* don't NAK, let the fifo fill */
+				if (req && (s & (1 << NAK_OUT_PACKETS)))
+					writel ((1 << CLEAR_NAK_OUT_PACKETS),
+							&ep->regs->ep_rsp);
+			}
+		}
+
+	} else if (ep->dma) {
+		int	valid = 1;
+
+		if (ep->is_in) {
+			int	expect;
+
+			/* preventing magic zlps is per-engine state, not
+			 * per-transfer; irq logic must recover hiccups.
+			 */
+			expect = likely (req->req.zero
+				|| (req->req.length % ep->ep.maxpacket) != 0);
+			if (expect != ep->in_fifo_validate)
+				valid = 0;
+		}
+		queue_dma (ep, req, valid);
+
+	} /* else the irq handler advances the queue. */
+
+	if (req) {
+done:
+		list_add_tail (&req->queue, &ep->queue);
+	}
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	/* pci writes may still be posted */
+	return 0;
+}
+
+static inline void
+dma_done (
+	struct net2280_ep *ep,
+	struct net2280_request *req,
+	u32 dmacount,
+	int status
+)
+{
+	req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount);
+	rmb ();
+	done (ep, req, status);
+}
+
+static void scan_dma_completions (struct net2280_ep *ep)
+{
+	/* only look at descriptors that were "naturally" retired,
+	 * so fifo and list head state won't matter
+	 */
+	while (!list_empty (&ep->queue)) {
+		struct net2280_request	*req;
+		u32			tmp;
+
+		req = list_entry (ep->queue.next,
+				struct net2280_request, queue);
+		if (!req->valid)
+			break;
+		rmb ();
+		tmp = le32_to_cpup (&req->td->dmacount);
+		if ((tmp & (1 << VALID_BIT)) != 0)
+			break;
+
+		/* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short"
+		 * packets, including overruns, even when the transfer was
+		 * exactly the length requested (dmacount now zero).
+		 */
+		if (!ep->is_in && (req->req.length % ep->ep.maxpacket) != 0) {
+			req->dma_done = 1;
+			break;
+		}
+		dma_done (ep, req, tmp, 0);
+	}
+}
+
+static void restart_dma (struct net2280_ep *ep)
+{
+	struct net2280_request	*req;
+
+	if (ep->stopped)
+		return;
+	req = list_entry (ep->queue.next, struct net2280_request, queue);
+
+#ifdef USE_DMA_CHAINING
+	/* the 2280 will be processing the queue unless queue hiccups after
+	 * the previous transfer:
+	 *  IN:   wanted automagic zlp, head doesn't (or vice versa)
+	 *  OUT:  was "usb-short", we must restart.
+	 */
+	if (!req->valid) {
+		struct net2280_request	*entry, *prev = 0;
+		int			qmode, reqmode, done = 0;
+
+		DEBUG (ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td);
+		qmode = likely (req->req.zero
+			|| (req->req.length % ep->ep.maxpacket) != 0);
+		list_for_each_entry (entry, &ep->queue, queue) {
+			u32		dmacount;
+
+			if (entry != req)
+				continue;
+			dmacount = entry->td->dmacount;
+			if (!done) {
+				reqmode = likely (entry->req.zero
+					|| (entry->req.length
+						% ep->ep.maxpacket) != 0);
+				if (reqmode == qmode) {
+					entry->valid = 1;
+					dmacount |= valid_bit;
+					entry->td->dmacount = dmacount;
+					prev = entry;
+					continue;
+				} else {
+					prev->td->dmacount |= dma_done_ie;
+					done = 1;
+				}
+			}
+
+			/* walk the rest of the queue so unlinks behave */
+			entry->valid = 0;
+			dmacount &= ~valid_bit;
+			entry->td->dmacount = dmacount;
+			prev = entry;
+		}
+		start_dma (ep, req);
+	} else if (!ep->is_in
+			&& (readl (&ep->regs->ep_stat)
+				& (1 << NAK_OUT_PACKETS)) != 0)
+		start_dma (ep, req);
+#else
+	start_dma (ep, req);
+#endif
+}
+
+static inline void abort_dma (struct net2280_ep *ep)
+{
+	/* abort the current transfer */
+	writel ((1 << DMA_ABORT), &ep->dma->dmastat);
+
+	/* collect completed transfers (except the current one) */
+	scan_dma_completions (ep);
+}
+
+/* dequeue ALL requests */
+static void nuke (struct net2280_ep *ep)
+{
+	struct net2280_request	*req;
+
+	/* called with spinlock held */
+	ep->stopped = 1;
+	if (ep->dma)
+		abort_dma (ep);
+	while (!list_empty (&ep->queue)) {
+		req = list_entry (ep->queue.next,
+				struct net2280_request,
+				queue);
+		done (ep, req, -ESHUTDOWN);
+	}
+}
+
+/* dequeue JUST ONE request */
+static int net2280_dequeue (struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct net2280_ep	*ep;
+	struct net2280_request	*req;
+	unsigned long		flags;
+	u32			dmactl;
+	int			stopped;
+
+	ep = container_of (_ep, struct net2280_ep, ep);
+	req = container_of (_req, struct net2280_request, req);
+	if (!_ep || (!ep->desc && ep->num != 0) || !_req)
+		return -EINVAL;
+
+	spin_lock_irqsave (&ep->dev->lock, flags);
+	stopped = ep->stopped;
+
+	/* pause dma while we scan the queue */
+	dmactl = 0;
+	ep->stopped = 1;
+	if (ep->dma) {
+		dmactl = readl (&ep->dma->dmactl);
+		writel (dmactl & ~(1 << DMA_ENABLE), &ep->dma->dmactl);
+		/* force synch, clean any completed requests */
+		spin_stop_dma (ep->dma);
+		scan_dma_completions (ep);
+	}
+
+	/* queue head may be partially complete. */
+	if (ep->queue.next == &req->queue) {
+		if (ep->dma) {
+			DEBUG (ep->dev, "unlink (%s) dma\n", _ep->name);
+			_req->status = -ECONNRESET;
+			abort_dma (ep);
+			if (likely (ep->queue.next == &req->queue))
+				dma_done (ep, req,
+					le32_to_cpup (&req->td->dmacount),
+					-ECONNRESET);
+		} else {
+			DEBUG (ep->dev, "unlink (%s) pio\n", _ep->name);
+			done (ep, req, -ECONNRESET);
+		}
+		req = 0;
+
+#ifdef USE_DMA_CHAINING
+	/* patch up hardware chaining data */
+	} else if (ep->dma) {
+		if (req->queue.prev == ep->queue.next) {
+			writel (le32_to_cpu (req->td->dmadesc),
+				&ep->dma->dmadesc);
+			if (req->td->dmacount & dma_done_ie)
+				writel (readl (&ep->dma->dmacount)
+						| dma_done_ie,
+					&ep->dma->dmacount);
+		} else {
+			struct net2280_request	*prev;
+
+			prev = list_entry (req->queue.prev,
+				struct net2280_request, queue);
+			prev->td->dmadesc = req->td->dmadesc;
+			if (req->td->dmacount & dma_done_ie)
+				prev->td->dmacount |= dma_done_ie;
+		}
+#endif
+	}
+
+	if (req)
+		done (ep, req, -ECONNRESET);
+	ep->stopped = stopped;
+
+	if (ep->dma) {
+		/* turn off dma on inactive queues */
+		if (list_empty (&ep->queue))
+			stop_dma (ep->dma);
+		else if (!ep->stopped) {
+			/* resume current request, or start new one */
+			if (req)
+				writel (dmactl, &ep->dma->dmactl);
+			else
+				start_dma (ep, list_entry (ep->queue.next,
+					struct net2280_request, queue));
+		}
+	}
+
+	spin_unlock_irqrestore (&ep->dev->lock, flags);
+	return req ? 0 : -EOPNOTSUPP;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+net2280_set_halt (struct usb_ep *_ep, int value)
+{
+	struct net2280_ep	*ep;
+
+	ep = container_of (_ep, struct net2280_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0))
+		return -EINVAL;
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+	if ((ep->desc->bmAttributes & 0x03) == USB_ENDPOINT_XFER_ISOC)
+		return -EINVAL;
+
+	VDEBUG (ep->dev, "%s %s halt\n", _ep->name, value ? "set" : "clear");
+
+	/* set/clear, then synch memory views with the device */
+	if (value)
+		set_halt (ep);
+	else
+		clear_halt (ep);
+	(void) readl (&ep->regs->ep_rsp);
+
+	return 0;
+}
+
+static int
+net2280_fifo_status (struct usb_ep *_ep)
+{
+	struct net2280_ep	*ep;
+	u32			avail;
+
+	ep = container_of (_ep, struct net2280_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0))
+		return -EINVAL;
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	avail = readl (&ep->regs->ep_avail);
+	if (ep->is_in)
+		avail = ep->fifo_size - avail;
+	return avail;
+}
+
+static void
+net2280_fifo_flush (struct usb_ep *_ep)
+{
+	struct net2280_ep	*ep;
+
+	ep = container_of (_ep, struct net2280_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0))
+		return;
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return;
+
+	writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat);
+	(void) readl (&ep->regs->ep_rsp);
+}
+
+static struct usb_ep_ops net2280_ep_ops = {
+	.enable		= net2280_enable,
+	.disable	= net2280_disable,
+
+	.alloc_request	= net2280_alloc_request,
+	.free_request	= net2280_free_request,
+
+	.alloc_buffer	= net2280_alloc_buffer,
+	.free_buffer	= net2280_free_buffer,
+
+	.queue		= net2280_queue,
+	.dequeue	= net2280_dequeue,
+
+	.set_halt	= net2280_set_halt,
+	.fifo_status	= net2280_fifo_status,
+	.fifo_flush	= net2280_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int net2280_get_frame (struct usb_gadget *_gadget)
+{
+	struct net2280		*dev;
+	unsigned long		flags;
+	u16			retval;
+
+	if (!_gadget)
+		return -ENODEV;
+	dev = container_of (_gadget, struct net2280, gadget);
+	spin_lock_irqsave (&dev->lock, flags);
+	retval = get_idx_reg (dev->regs, REG_FRAME) & 0x03ff;
+	spin_unlock_irqrestore (&dev->lock, flags);
+	return retval;
+}
+
+static int net2280_wakeup (struct usb_gadget *_gadget)
+{
+	struct net2280		*dev;
+
+	if (!_gadget)
+		return 0;
+	dev = container_of (_gadget, struct net2280, gadget);
+	writel (1 << GENERATE_RESUME, dev->usb->usbstat);
+
+	/* pci writes may still be posted */
+	return 0;
+}
+
+static const struct usb_gadget_ops net2280_ops = {
+	.get_frame	= net2280_get_frame,
+	.wakeup		= net2280_wakeup,
+
+	// .set_selfpowered = net2280_set_selfpowered,
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	USE_SYSFS_DEBUG_FILES
+
+/* "function" sysfs attribute */
+static ssize_t
+show_function (struct device *_dev, char *buf)
+{
+	struct net2280	*dev = dev_get_drvdata (_dev);
+
+	if (!dev->driver
+			|| !dev->driver->function
+			|| strlen (dev->driver->function) > PAGE_SIZE)
+		return 0;
+	return snprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);
+}
+static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
+
+static ssize_t
+show_registers (struct device *_dev, char *buf)
+{
+	struct net2280		*dev;
+	char			*next;
+	unsigned		size, t;
+	unsigned long		flags;
+	int			i;
+	u32			t1, t2;
+	char			*s;
+
+	dev = dev_get_drvdata (_dev);
+	next = buf;
+	size = PAGE_SIZE;
+	spin_lock_irqsave (&dev->lock, flags);
+
+	if (dev->driver)
+		s = dev->driver->driver.name;
+	else
+		s = "(none)";
+
+	/* Main Control Registers */
+	t = snprintf (next, size, "%s " DRIVER_VERSION "\n"
+			"devinit %03x fifoctl %08x gadget '%s'\n"
+			"pci irqenb0 %02x irqenb1 %08x "
+			"irqstat0 %04x irqstat1 %08x\n",
+			driver_name,
+			readl (&dev->regs->devinit),
+			readl (&dev->regs->fifoctl),
+			s,
+			readl (&dev->regs->pciirqenb0),
+			readl (&dev->regs->pciirqenb1),
+			readl (&dev->regs->irqstat0),
+			readl (&dev->regs->irqstat1));
+	size -= t;
+	next += t;
+
+	/* USB Control Registers */
+	t1 = readl (&dev->usb->usbctl);
+	t2 = readl (&dev->usb->usbstat);
+	if (t1 & (1 << VBUS_PIN)) {
+		if (t2 & (1 << HIGH_SPEED))
+			s = "high speed";
+		else if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+			s = "powered";
+		else
+			s = "full speed";
+		/* full speed bit (6) not working?? */
+	} else
+			s = "not attached";
+	t = snprintf (next, size,
+			"stdrsp %08x usbctl %08x usbstat %08x "
+				"addr 0x%02x (%s)\n",
+			readl (&dev->usb->stdrsp), t1, t2,
+			readl (&dev->usb->ouraddr), s);
+	size -= t;
+	next += t;
+
+	/* PCI Master Control Registers */
+
+	/* DMA Control Registers */
+
+	/* Configurable EP Control Registers */
+	for (i = 0; i < 7; i++) {
+		struct net2280_ep	*ep;
+
+		ep = &dev->ep [i];
+		if (i && !ep->desc)
+			continue;
+
+		t1 = readl (&ep->regs->ep_cfg);
+		t = snprintf (next, size,
+				"%s\tcfg %05x rsp %02x enb %02x ",
+				ep->ep.name, t1,
+				readl (&ep->regs->ep_rsp) & 0xff,
+				readl (&ep->regs->ep_irqenb));
+		size -= t;
+		next += t;
+
+		t = snprintf (next, size,
+				"stat %08x avail %04x "
+				"(ep%d%s-%s)%s\n",
+				readl (&ep->regs->ep_stat),
+				readl (&ep->regs->ep_avail),
+				t1 & 0x0f, DIR_STRING (t1),
+				type_string (t1 >> 8),
+				ep->stopped ? "*" : "");
+		size -= t;
+		next += t;
+
+		if (!ep->dma)
+			continue;
+
+		t = snprintf (next, size,
+				"  dma\tctl %08x stat %08x count %08x\n"
+				"\taddr %08x desc %08x\n",
+				readl (&ep->dma->dmactl),
+				readl (&ep->dma->dmastat),
+				readl (&ep->dma->dmacount),
+				readl (&ep->dma->dmaaddr),
+				readl (&ep->dma->dmadesc));
+		size -= t;
+		next += t;
+
+	}
+
+	/* Indexed Registers */
+		// none yet 
+
+	/* Statistics */
+	t = snprintf (next, size, "irqs:  ");
+	size -= t;
+	next += t;
+	for (i = 0; i < 7; i++) {
+		struct net2280_ep	*ep;
+
+		ep = &dev->ep [i];
+		if (i && !ep->irqs)
+			continue;
+		t = snprintf (next, size, " %s/%ld", ep->ep.name, ep->irqs);
+		size -= t;
+		next += t;
+
+	}
+	t = snprintf (next, size, "\n");
+	size -= t;
+	next += t;
+
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	return PAGE_SIZE - size;
+}
+static DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
+
+static ssize_t
+show_queues (struct device *_dev, char *buf)
+{
+	struct net2280		*dev;
+	char			*next;
+	unsigned		size;
+	unsigned long		flags;
+	int			i;
+
+	dev = dev_get_drvdata (_dev);
+	next = buf;
+	size = PAGE_SIZE;
+	spin_lock_irqsave (&dev->lock, flags);
+
+	for (i = 0; i < 7; i++) {
+		struct net2280_ep		*ep = &dev->ep [i];
+		struct net2280_request		*req;
+		int				t;
+
+		if (i != 0) {
+			const struct usb_endpoint_descriptor	*d;
+
+			d = ep->desc;
+			if (!d)
+				continue;
+			t = d->bEndpointAddress;
+			t = snprintf (next, size,
+				"%s (ep%d%s-%s) max %04x %s\n",
+				ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK,
+				(t & USB_DIR_IN) ? "in" : "out",
+				({ char *val;
+				 switch (d->bmAttributes & 0x03) {
+				 case USB_ENDPOINT_XFER_BULK:
+				 	val = "bulk"; break;
+				 case USB_ENDPOINT_XFER_INT:
+				 	val = "intr"; break;
+				 default:
+				 	val = "iso"; break;
+				 }; val; }),
+				le16_to_cpu (d->wMaxPacketSize) & 0x1fff,
+				ep->dma ? "dma" : "pio"
+				);
+		} else /* ep0 should only have one transfer queued */
+			t = snprintf (next, size, "ep0 max 64 pio %s\n",
+					ep->is_in ? "in" : "out");
+		if (t <= 0 || t > size)
+			goto done;
+		size -= t;
+		next += t;
+
+		if (list_empty (&ep->queue)) {
+			t = snprintf (next, size, "\t(nothing queued)\n");
+			if (t <= 0 || t > size)
+				goto done;
+			size -= t;
+			next += t;
+			continue;
+		}
+		list_for_each_entry (req, &ep->queue, queue) {
+			if (ep->dma && req->td_dma == readl (&ep->dma->dmadesc))
+				t = snprintf (next, size,
+					"\treq %p len %d/%d "
+					"buf %p (dmacount %08x)\n",
+					&req->req, req->req.actual,
+					req->req.length, req->req.buf,
+					readl (&ep->dma->dmacount));
+			else
+				t = snprintf (next, size,
+					"\treq %p len %d/%d buf %p\n",
+					&req->req, req->req.actual,
+					req->req.length, req->req.buf);
+			if (t <= 0 || t > size)
+				goto done;
+			size -= t;
+			next += t;
+		}
+	}
+
+done:
+	spin_unlock_irqrestore (&dev->lock, flags);
+	return PAGE_SIZE - size;
+}
+static DEVICE_ATTR (queues, S_IRUGO, show_queues, NULL);
+
+
+#else
+
+#define device_create_file(a,b)	do {} while (0)
+#define device_remove_file	device_create_file
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* another driver-specific mode might be a request type doing dma
+ * to/from another device fifo instead of to/from memory.
+ */
+
+static void set_fifo_mode (struct net2280 *dev, int mode)
+{
+	/* keeping high bits preserves BAR2 */
+	writel ((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl);
+
+	/* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */
+	INIT_LIST_HEAD (&dev->gadget.ep_list);
+	list_add_tail (&dev->ep [1].ep.ep_list, &dev->gadget.ep_list);
+	list_add_tail (&dev->ep [2].ep.ep_list, &dev->gadget.ep_list);
+	switch (mode) {
+	case 0:
+		list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list);
+		list_add_tail (&dev->ep [4].ep.ep_list, &dev->gadget.ep_list);
+		dev->ep [1].fifo_size = dev->ep [2].fifo_size = 1024;
+		break;
+	case 1:
+		dev->ep [1].fifo_size = dev->ep [2].fifo_size = 2048;
+		break;
+	case 2:
+		list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list);
+		dev->ep [1].fifo_size = 2048;
+		dev->ep [2].fifo_size = 1024;
+		break;
+	}
+	/* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */
+	list_add_tail (&dev->ep [5].ep.ep_list, &dev->gadget.ep_list);
+	list_add_tail (&dev->ep [6].ep.ep_list, &dev->gadget.ep_list);
+}
+
+/**
+ * net2280_set_fifo_mode - change allocation of fifo buffers
+ * @gadget: access to the net2280 device that will be updated
+ * @mode: 0 for default, four 1kB buffers (ep-a through ep-d);
+ * 	1 for two 2kB buffers (ep-a and ep-b only);
+ * 	2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c).
+ *
+ * returns zero on success, else negative errno.  when this succeeds,
+ * the contents of gadget->ep_list may have changed.
+ *
+ * you may only call this function when endpoints a-d are all disabled.
+ * use it whenever extra hardware buffering can help performance, such
+ * as before enabling "high bandwidth" interrupt endpoints that use
+ * maxpacket bigger than 512 (when double buffering would otherwise
+ * be unavailable).
+ */
+int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode)
+{
+	int			i;
+	struct net2280		*dev;
+	int			status = 0;
+	unsigned long		flags;
+
+	if (!gadget)
+		return -ENODEV;
+	dev = container_of (gadget, struct net2280, gadget);
+
+	spin_lock_irqsave (&dev->lock, flags);
+
+	for (i = 1; i <= 4; i++)
+		if (dev->ep [i].desc) {
+			status = -EINVAL;
+			break;
+		}
+	if (mode < 0 || mode > 2)
+		status = -EINVAL;
+	if (status == 0)
+		set_fifo_mode (dev, mode);
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	if (status == 0) {
+		if (mode == 1)
+			DEBUG (dev, "fifo:  ep-a 2K, ep-b 2K\n");
+		else if (mode == 2)
+			DEBUG (dev, "fifo:  ep-a 2K, ep-b 1K, ep-c 1K\n");
+		/* else all are 1K */
+	}
+	return status;
+}
+EXPORT_SYMBOL (net2280_set_fifo_mode);
+
+/*-------------------------------------------------------------------------*/
+
+/* keeping it simple:
+ * - one bus driver, initted first;
+ * - one function driver, initted second
+ *
+ * most of the work to support multiple net2280 controllers would
+ * be to associate this gadget driver (yes?) with all of them, or
+ * perhaps to bind specific drivers to specific devices.
+ */
+
+static struct net2280	*the_controller;
+
+static void usb_reset (struct net2280 *dev)
+{
+	u32	tmp;
+
+	/* force immediate bus disconnect, and synch through pci */
+	writel (0, &dev->usb->usbctl);
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+	(void) readl (&dev->usb->usbctl);
+
+	net2280_led_init (dev);
+
+	/* disable automatic responses, and irqs */
+	writel (0, &dev->usb->stdrsp);
+	writel (0, &dev->regs->pciirqenb0);
+	writel (0, &dev->regs->pciirqenb1);
+
+	/* clear old dma and irq state */
+	for (tmp = 0; tmp < 4; tmp++) {
+		writel ((1 << DMA_ABORT), &dev->dma [tmp].dmastat);
+		stop_dma (&dev->dma [tmp]);
+	}
+	writel (~0, &dev->regs->irqstat0),
+	writel (~(1 << SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1),
+
+	/* reset, and enable pci */
+	tmp = readl (&dev->regs->devinit)
+		| (1 << PCI_ENABLE)
+		| (1 << FIFO_SOFT_RESET)
+		| (1 << USB_SOFT_RESET)
+		| (1 << M8051_RESET);
+	writel (tmp, &dev->regs->devinit);
+
+	/* standard fifo and endpoint allocations */
+	set_fifo_mode (dev, 0);
+}
+
+static void usb_reinit (struct net2280 *dev)
+{
+	u32	tmp;
+	int	init_dma;
+
+	/* use_dma changes are ignored till next device re-init */
+	init_dma = use_dma;
+
+	/* basic endpoint init */
+	for (tmp = 0; tmp < 7; tmp++) {
+		struct net2280_ep	*ep = &dev->ep [tmp];
+
+		ep->ep.name = ep_name [tmp];
+		ep->dev = dev;
+		ep->num = tmp;
+
+		if (tmp > 0 && tmp <= 4) {
+			ep->fifo_size = 1024;
+			if (init_dma)
+				ep->dma = &dev->dma [tmp - 1];
+		} else
+			ep->fifo_size = 64;
+		ep->regs = &dev->epregs [tmp];
+		ep_reset (dev->regs, ep);
+	}
+	dev->ep [0].ep.maxpacket = 64;
+	dev->ep [5].ep.maxpacket = 64;
+	dev->ep [6].ep.maxpacket = 64;
+
+	dev->gadget.ep0 = &dev->ep [0].ep;
+	dev->ep [0].stopped = 0;
+	INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
+
+	/* we want to prevent lowlevel/insecure access from the USB host,
+	 * but erratum 0119 means this enable bit is ignored
+	 */
+	for (tmp = 0; tmp < 5; tmp++)
+		writel (EP_DONTUSE, &dev->dep [tmp].dep_cfg);
+}
+
+static void ep0_start (struct net2280 *dev)
+{
+	writel (  (1 << SET_EP_HIDE_STATUS_PHASE)
+		| (1 << CLEAR_NAK_OUT_PACKETS)
+		| (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
+		, &dev->epregs [0].ep_rsp);
+
+	/*
+	 * hardware optionally handles a bunch of standard requests
+	 * that the API hides from drivers anyway.  have it do so.
+	 * endpoint status/features are handled in software, to
+	 * help pass tests for some dubious behavior.
+	 */
+	writel (  (1 << SET_TEST_MODE)
+		| (1 << SET_ADDRESS)
+		| (1 << DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP)
+		| (1 << GET_DEVICE_STATUS)
+		| (1 << GET_INTERFACE_STATUS)
+		, &dev->usb->stdrsp);
+	writel (  (1 << USB_ROOT_PORT_WAKEUP_ENABLE)
+		| (1 << SELF_POWERED_USB_DEVICE)
+		| (1 << REMOTE_WAKEUP_SUPPORT)
+		| (1 << USB_DETECT_ENABLE)
+		| (1 << DEVICE_REMOTE_WAKEUP_ENABLE)
+		, &dev->usb->usbctl);
+
+	/* enable irqs so we can see ep0 and general operation  */
+	writel (  (1 << SETUP_PACKET_INTERRUPT_ENABLE)
+		| (1 << ENDPOINT_0_INTERRUPT_ENABLE)
+		, &dev->regs->pciirqenb0);
+	writel (  (1 << PCI_INTERRUPT_ENABLE)
+		| (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE)
+		| (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE)
+		| (1 << PCI_RETRY_ABORT_INTERRUPT_ENABLE)
+		| (1 << VBUS_INTERRUPT_ENABLE)
+		| (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE)
+		, &dev->regs->pciirqenb1);
+
+	/* don't leave any writes posted */
+	(void) readl (&dev->usb->usbctl);
+}
+
+/* when a driver is successfully registered, it will receive
+ * control requests including set_configuration(), which enables
+ * non-control requests.  then usb traffic follows until a
+ * disconnect is reported.  then a host may connect again, or
+ * the driver might get unbound.
+ */
+int usb_gadget_register_driver (struct usb_gadget_driver *driver)
+{
+	struct net2280		*dev = the_controller;
+	int			retval;
+	unsigned		i;
+
+	/* insist on high speed support from the driver, since
+	 * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE)
+	 * "must not be used in normal operation"
+	 */
+	if (!driver
+			|| driver->speed != USB_SPEED_HIGH
+			|| !driver->bind
+			|| !driver->unbind
+			|| !driver->setup)
+		return -EINVAL;
+	if (!dev)
+		return -ENODEV;
+	if (dev->driver)
+		return -EBUSY;
+
+	for (i = 0; i < 7; i++)
+		dev->ep [i].irqs = 0;
+
+	/* hook up the driver ... */
+	dev->driver = driver;
+	dev->gadget.dev.driver = &driver->driver;
+	retval = driver->bind (&dev->gadget);
+	if (retval) {
+		DEBUG (dev, "bind to driver %s --> %d\n",
+				driver->driver.name, retval);
+		dev->driver = 0;
+		dev->gadget.dev.driver = 0;
+		return retval;
+	}
+
+	// FIXME
+	// driver_register (&driver->driver);
+	// device_register (&dev->gadget.dev);
+
+	device_create_file (&dev->pdev->dev, &dev_attr_function);
+	device_create_file (&dev->pdev->dev, &dev_attr_queues);
+
+	/* ... then enable host detection and ep0; and we're ready
+	 * for set_configuration as well as eventual disconnect.
+	 */
+	net2280_led_active (dev, 1);
+	ep0_start (dev);
+
+	DEBUG (dev, "%s ready, usbctl %08x stdrsp %08x\n",
+			driver->driver.name,
+			readl (&dev->usb->usbctl),
+			readl (&dev->usb->stdrsp));
+
+	/* pci writes may still be posted */
+	return 0;
+}
+EXPORT_SYMBOL (usb_gadget_register_driver);
+
+static void
+stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver)
+{
+	int			i;
+
+	/* don't disconnect if it's not connected */
+	if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = 0;
+
+	/* stop hardware; prevent new request submissions;
+	 * and kill any outstanding requests.
+	 */
+	usb_reset (dev);
+	for (i = 0; i < 7; i++)
+		nuke (&dev->ep [i]);
+
+	/* report disconnect; the driver is already quiesced */
+	if (driver) {
+		spin_unlock (&dev->lock);
+		driver->disconnect (&dev->gadget);
+		spin_lock (&dev->lock);
+	}
+
+	usb_reinit (dev);
+}
+
+int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
+{
+	struct net2280	*dev = the_controller;
+	unsigned long	flags;
+
+	if (!dev)
+		return -ENODEV;
+	if (!driver || driver != dev->driver)
+		return -EINVAL;
+
+	spin_lock_irqsave (&dev->lock, flags);
+	stop_activity (dev, driver);
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	driver->unbind (&dev->gadget);
+	dev->driver = 0;
+
+	net2280_led_active (dev, 0);
+	device_remove_file (&dev->pdev->dev, &dev_attr_function);
+	device_remove_file (&dev->pdev->dev, &dev_attr_queues);
+
+	// FIXME
+	// device_unregister()
+	// driver_unregister (&driver->driver);
+
+	DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL (usb_gadget_unregister_driver);
+
+
+/*-------------------------------------------------------------------------*/
+
+/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq.
+ * also works for dma-capable endpoints, in pio mode or just
+ * to manually advance the queue after short OUT transfers.
+ */
+static void handle_ep_small (struct net2280_ep *ep)
+{
+	struct net2280_request	*req;
+	u32			t;
+	/* 0 error, 1 mid-data, 2 done */
+	int			mode = 1;
+
+	if (!list_empty (&ep->queue))
+		req = list_entry (ep->queue.next,
+			struct net2280_request, queue);
+	else
+		req = 0;
+
+	/* ack all, and handle what we care about */
+	t = readl (&ep->regs->ep_stat);
+	ep->irqs++;
+#if 0
+	VDEBUG (ep->dev, "%s ack ep_stat %08x, req %p\n",
+			ep->ep.name, t, req ? &req->req : 0);
+#endif
+	writel (t & ~(1 << NAK_OUT_PACKETS), &ep->regs->ep_stat);
+
+	/* for ep0, monitor token irqs to catch data stage length errors
+	 * and to synchronize on status.
+	 *
+	 * also, to defer reporting of protocol stalls ... here's where
+	 * data or status first appears, handling stalls here should never
+	 * cause trouble on the host side..
+	 *
+	 * control requests could be slightly faster without token synch for
+	 * status, but status can jam up that way.
+	 */
+	if (unlikely (ep->num == 0)) {
+		if (ep->is_in) {
+			/* status; stop NAKing */
+			if (t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) {
+				if (ep->dev->protocol_stall)
+					set_halt (ep);
+				mode = 2;
+			/* reply to extra IN tokens with a zlp */
+			} else if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) {
+				if (ep->dev->protocol_stall) {
+					set_halt (ep);
+					mode = 2;
+				} else if (!req)
+					write_fifo (ep, 0);
+			}
+		} else {
+			/* status; stop NAKing */
+			if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) {
+				if (ep->dev->protocol_stall)
+					set_halt (ep);
+				mode = 2;
+			/* an extra OUT token is an error */
+			} else if (((t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT))
+					&& req
+					&& req->req.actual == req->req.length)
+					|| !req) {
+				ep->dev->protocol_stall = 1;
+				set_halt (ep);
+				ep->stopped = 1;
+				if (req)
+					done (ep, req, -EOVERFLOW);
+				req = 0;
+			}
+		}
+	}
+
+	if (unlikely (!req))
+		return;
+
+	/* manual DMA queue advance after short OUT */
+	if (likely (ep->dma != 0)) {
+		if (t & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) {
+			u32	count;
+
+			/* TRANSFERRED works around OUT_DONE erratum 0112.
+			 * we expect (N <= maxpacket) bytes; host wrote M.
+			 * iff (M < N) we won't ever see a DMA interrupt.
+			 */
+			count = readl (&ep->dma->dmacount);
+			count &= DMA_BYTE_COUNT_MASK;
+			if (!req->dma_done) {
+				/* dma can finish with the FIFO non-empty,
+				 * on (M > N) errors.
+				 */
+				while (count && (t & (1 << FIFO_EMPTY)) == 0) {
+					cpu_relax ();
+					t = readl (&ep->regs->ep_stat);
+					count = readl (&ep->dma->dmacount);
+					count &= DMA_BYTE_COUNT_MASK;
+				}
+			}
+
+			/* stop DMA, leave ep NAKing */
+			writel ((1 << DMA_ABORT), &ep->dma->dmastat);
+			spin_stop_dma (ep->dma);
+
+			/* buffer might have been too small */
+			t = readl (&ep->regs->ep_avail);
+			if (t != 0)
+				DEBUG (ep->dev, "%s dma, discard %d len %d\n",
+						ep->ep.name, t, count);
+			dma_done (ep, req, count, t ? -EOVERFLOW : 0);
+
+			/* also flush to prevent erratum 0106 trouble */
+			if (t || ep->dev->chiprev == 0x0100)
+				out_flush (ep);
+
+			/* restart dma (still NAKing OUT!) if needed */
+			if (!list_empty (&ep->queue))
+				restart_dma (ep);
+		} else
+			DEBUG (ep->dev, "%s dma ep_stat %08x ??\n",
+					ep->ep.name, t);
+		return;
+
+	/* data packet(s) received (in the fifo, OUT) */
+	} else if (t & (1 << DATA_PACKET_RECEIVED_INTERRUPT)) {
+		if (read_fifo (ep, req) && ep->num != 0)
+			mode = 2;
+
+	/* data packet(s) transmitted (IN) */
+	} else if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) {
+		unsigned	len;
+
+		len = req->req.length - req->req.actual;
+		len = min (ep->ep.maxpacket, len);
+		req->req.actual += len;
+
+		/* if we wrote it all, we're usually done */
+		if (req->req.actual == req->req.length) {
+			if (ep->num == 0) {
+				/* wait for control status */
+				if (mode != 2)
+					req = 0;
+			} else if (!req->req.zero || len != ep->ep.maxpacket)
+				mode = 2;
+		}
+
+	/* there was nothing to do ...  */
+	} else if (mode == 1)
+		return;
+
+	/* done */
+	if (mode == 2) {
+		/* stream endpoints often resubmit/unlink in completion */
+		done (ep, req, 0);
+
+		/* maybe advance queue to next request */
+		if (ep->num == 0) {
+			allow_status (ep);
+			req = 0;
+		} else {
+			if (!list_empty (&ep->queue) && !ep->stopped)
+				req = list_entry (ep->queue.next,
+					struct net2280_request, queue);
+			else
+				req = 0;
+			if (req && !ep->is_in)
+				stop_out_naking (ep);
+		}
+	}
+
+	/* is there a buffer for the next packet?
+	 * for best streaming performance, make sure there is one.
+	 */
+	if (req && !ep->stopped) {
+
+		/* load IN fifo with next packet (may be zlp) */
+		if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT))
+			write_fifo (ep, &req->req);
+	}
+}
+
+static struct net2280_ep *
+get_ep_by_addr (struct net2280 *dev, u16 wIndex)
+{
+	struct net2280_ep	*ep;
+
+	if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
+		return &dev->ep [0];
+	list_for_each_entry (ep, &dev->gadget.ep_list, ep.ep_list) {
+		u8	bEndpointAddress;
+
+		if (!ep->desc)
+			continue;
+		bEndpointAddress = ep->desc->bEndpointAddress;
+		if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
+			continue;
+		if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f))
+			return ep;
+	}
+	return 0;
+}
+
+static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
+{
+	struct net2280_ep	*ep;
+	u32			num, scratch;
+
+	/* most of these don't need individual acks */
+	stat &= ~(1 << INTA_ASSERTED);
+	if (!stat)
+		return;
+	// DEBUG (dev, "irqstat0 %04x\n", stat);
+
+	/* starting a control request? */
+	if (unlikely (stat & (1 << SETUP_PACKET_INTERRUPT))) {
+		union {
+			u32			raw [2];
+			struct usb_ctrlrequest	r;
+		} u;
+		int				tmp = 0;
+		struct net2280_request		*req;
+
+		if (dev->gadget.speed == USB_SPEED_UNKNOWN) {
+			if (readl (&dev->usb->usbstat) & (1 << HIGH_SPEED))
+				dev->gadget.speed = USB_SPEED_HIGH;
+			else
+				dev->gadget.speed = USB_SPEED_FULL;
+			net2280_led_speed (dev, dev->gadget.speed);
+			DEBUG (dev, "%s speed\n",
+				(dev->gadget.speed == USB_SPEED_HIGH)
+					? "high" : "full");
+		}
+
+		ep = &dev->ep [0];
+		ep->irqs++;
+
+		/* make sure any leftover request state is cleared */
+		stat &= ~(1 << ENDPOINT_0_INTERRUPT);
+		while (!list_empty (&ep->queue)) {
+			req = list_entry (ep->queue.next,
+					struct net2280_request, queue);
+			done (ep, req, (req->req.actual == req->req.length)
+						? 0 : -EPROTO);
+		}
+		ep->stopped = 0;
+		dev->protocol_stall = 0;
+		writel (  (1 << TIMEOUT)
+			| (1 << USB_STALL_SENT)
+			| (1 << USB_IN_NAK_SENT)
+			| (1 << USB_IN_ACK_RCVD)
+			| (1 << USB_OUT_PING_NAK_SENT)
+			| (1 << USB_OUT_ACK_SENT)
+			| (1 << FIFO_OVERFLOW)
+			| (1 << FIFO_UNDERFLOW)
+			| (1 << SHORT_PACKET_OUT_DONE_INTERRUPT)
+			| (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)
+			| (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+			| (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
+			| (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+			| (1 << DATA_IN_TOKEN_INTERRUPT)
+			, &ep->regs->ep_stat);
+		u.raw [0] = readl (&dev->usb->setup0123);
+		u.raw [1] = readl (&dev->usb->setup4567);
+		le16_to_cpus (&u.r.wValue);
+		le16_to_cpus (&u.r.wIndex);
+		le16_to_cpus (&u.r.wLength);
+
+		/* ack the irq */
+		writel (1 << SETUP_PACKET_INTERRUPT, &dev->regs->irqstat0);
+		stat ^= (1 << SETUP_PACKET_INTERRUPT);
+
+		/* watch control traffic at the token level, and force
+		 * synchronization before letting the status stage happen.
+		 */
+		ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0;
+		if (ep->is_in)
+			scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
+				| (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+				| (1 << DATA_IN_TOKEN_INTERRUPT);
+		else
+			scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+				| (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+				| (1 << DATA_IN_TOKEN_INTERRUPT);
+		writel (scratch, &dev->epregs [0].ep_irqenb);
+
+		/* we made the hardware handle most lowlevel requests;
+		 * everything else goes uplevel to the gadget code.
+		 */
+		switch (u.r.bRequest) {
+		case USB_REQ_GET_STATUS: {
+			struct net2280_ep	*e;
+			u16			status;
+
+			if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT))
+				goto delegate;
+			if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0
+					|| u.r.wLength > 2)
+				goto do_stall;
+
+			if (readl (&e->regs->ep_rsp)
+					& (1 << SET_ENDPOINT_HALT))
+				status = cpu_to_le16 (1);
+			else
+				status = cpu_to_le16 (0);
+
+			/* don't bother with a request object! */
+			writel (0, &dev->epregs [0].ep_irqenb);
+			set_fifo_bytecount (ep, u.r.wLength);
+			writel (status, &dev->epregs [0].ep_data);
+			allow_status (ep);
+			goto next_endpoints;
+			}
+			break;
+		case USB_REQ_CLEAR_FEATURE: {
+			struct net2280_ep	*e;
+
+			if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+				goto delegate;
+			if (u.r.wIndex != 0 /* HALT feature */
+					|| u.r.wLength != 0)
+				goto do_stall;
+			if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0)
+				goto do_stall;
+			clear_halt (e);
+			}
+			break;
+		case USB_REQ_SET_FEATURE: {
+			struct net2280_ep	*e;
+
+			if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+				goto delegate;
+			if (u.r.wIndex != 0 /* HALT feature */
+					|| u.r.wLength != 0)
+				goto do_stall;
+			if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0)
+				goto do_stall;
+			set_halt (e);
+			}
+			break;
+		default:
+delegate:
+			VDEBUG (dev, "setup %02x.%02x v%04x i%04x "
+				"ep_cfg %08x\n",
+				u.r.bRequestType, u.r.bRequest,
+				u.r.wValue, u.r.wIndex,
+				readl (&ep->regs->ep_cfg));
+			spin_unlock (&dev->lock);
+			tmp = dev->driver->setup (&dev->gadget, &u.r);
+			spin_lock (&dev->lock);
+		}
+
+		/* stall ep0 on error */
+		if (tmp < 0) {
+do_stall:
+			VDEBUG (dev, "req %02x.%02x protocol STALL; stat %d\n",
+					u.r.bRequestType, u.r.bRequest, tmp);
+			dev->protocol_stall = 1;
+
+		/* when there's no data, queueing a response is optional */
+		} else if (list_empty (&ep->queue)) {
+			if (u.r.wLength == 0) {
+				/* done() not possible/requested */
+				allow_status (ep);
+			} else {
+				DEBUG (dev, "req %02x.%02x v%04x "
+					"gadget error, len %d, stat %d\n",
+					u.r.bRequestType, u.r.bRequest,
+					le16_to_cpu (u.r.wValue),
+					u.r.wLength, tmp);
+				dev->protocol_stall = 1;
+			}
+		}
+
+		/* some in/out token irq should follow; maybe stall then. */
+	}
+
+next_endpoints:
+	/* endpoint data irq ? */
+	scratch = stat & 0x7f;
+	stat &= ~0x7f;
+	for (num = 0; scratch; num++) {
+		u32		t;
+
+		/* do this endpoint's FIFO and queue need tending? */
+		t = 1 << num;
+		if ((scratch & t) == 0)
+			continue;
+		scratch ^= t;
+
+		ep = &dev->ep [num];
+		handle_ep_small (ep);
+	}
+
+	if (stat)
+		DEBUG (dev, "unhandled irqstat0 %08x\n", stat);
+}
+
+#define DMA_INTERRUPTS ( \
+		  (1 << DMA_D_INTERRUPT) \
+		| (1 << DMA_C_INTERRUPT) \
+		| (1 << DMA_B_INTERRUPT) \
+		| (1 << DMA_A_INTERRUPT))
+#define	PCI_ERROR_INTERRUPTS ( \
+		  (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT) \
+		| (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT) \
+		| (1 << PCI_RETRY_ABORT_INTERRUPT))
+
+static void handle_stat1_irqs (struct net2280 *dev, u32 stat)
+{
+	struct net2280_ep	*ep;
+	u32			tmp, num, scratch;
+
+	/* after disconnect there's nothing else to do! */
+	tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT);
+	if (stat & tmp) {
+		writel (tmp, &dev->regs->irqstat1);
+		if (((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) != 0
+			|| (readl (&dev->usb->usbctl) & (1 << VBUS_PIN)) == 0
+			) && dev->gadget.speed != USB_SPEED_UNKNOWN) {
+			DEBUG (dev, "disconnect %s\n",
+					dev->driver->driver.name);
+			stop_activity (dev, dev->driver);
+			ep0_start (dev);
+			return;
+		}
+		stat &= ~tmp;
+
+		/* vBUS can bounce ... one of many reasons to ignore the
+		 * notion of hotplug events on bus connect/disconnect!
+		 */
+		if (!stat)
+			return;
+	}
+
+	/* NOTE: we don't actually suspend the hardware; that starts to
+	 * interact with PCI power management, and needs something like a
+	 * controller->suspend() call to clear SUSPEND_REQUEST_INTERRUPT.
+	 * we shouldn't see resume interrupts.
+	 * for rev 0100, this also avoids erratum 0102.
+	 */
+	tmp = (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT);
+	if (stat & tmp) {
+		if (dev->driver->suspend)
+			dev->driver->suspend (&dev->gadget);
+		stat &= ~tmp;
+	}
+	stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT);
+
+	/* clear any other status/irqs */
+	if (stat)
+		writel (stat, &dev->regs->irqstat1);
+
+	/* some status we can just ignore */
+	stat &= ~((1 << CONTROL_STATUS_INTERRUPT)
+			| (1 << RESUME_INTERRUPT)
+			| (1 << SOF_INTERRUPT));
+	if (!stat)
+		return;
+	// DEBUG (dev, "irqstat1 %08x\n", stat);
+
+	/* DMA status, for ep-{a,b,c,d} */
+	scratch = stat & DMA_INTERRUPTS;
+	stat &= ~DMA_INTERRUPTS;
+	scratch >>= 9;
+	for (num = 0; scratch; num++) {
+		struct net2280_dma_regs	*dma;
+
+		tmp = 1 << num;
+		if ((tmp & scratch) == 0)
+			continue;
+		scratch ^= tmp;
+
+		ep = &dev->ep [num + 1];
+		dma = ep->dma;
+
+		if (!dma)
+			continue;
+
+		/* clear ep's dma status */
+		tmp = readl (&dma->dmastat);
+		writel (tmp, &dma->dmastat);
+
+#ifdef USE_DMA_CHAINING
+		/* chaining should stop only on error (which?)
+		 * or (stat0 codepath) short OUT transfer.
+		 */
+#else
+		if ((tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT)) == 0) {
+			DEBUG (ep->dev, "%s no xact done? %08x\n",
+				ep->ep.name, tmp);
+			continue;
+		}
+		stop_dma (ep->dma);
+#endif
+
+		/* OUT transfers terminate when the data from the
+		 * host is in our memory.  Process whatever's done.
+		 * On this path, we know transfer's last packet wasn't
+		 * less than req->length. NAK_OUT_PACKETS may be set,
+		 * or the FIFO may already be holding new packets.
+		 *
+		 * IN transfers can linger in the FIFO for a very
+		 * long time ... we ignore that for now, accounting
+		 * precisely (like PIO does) needs per-packet irqs
+		 */
+		scan_dma_completions (ep);
+
+		/* disable dma on inactive queues; else maybe restart */
+		if (list_empty (&ep->queue)) {
+#ifdef USE_DMA_CHAINING
+			stop_dma (ep->dma);
+#endif
+		} else {
+			tmp = readl (&dma->dmactl);
+			if ((tmp & (1 << DMA_SCATTER_GATHER_ENABLE)) == 0
+					|| (tmp & (1 << DMA_ENABLE)) == 0)
+				restart_dma (ep);
+		}
+		ep->irqs++;
+	}
+
+	/* NOTE:  there are other PCI errors we might usefully notice.
+	 * if they appear very often, here's where to try recovering.
+	 */
+	if (stat & PCI_ERROR_INTERRUPTS) {
+		ERROR (dev, "pci dma error; stat %08x\n", stat);
+		stat &= ~PCI_ERROR_INTERRUPTS;
+		/* these are fatal errors, but "maybe" they won't
+		 * happen again ...
+		 */
+		stop_activity (dev, dev->driver);
+		ep0_start (dev);
+		stat = 0;
+	}
+
+	if (stat)
+		DEBUG (dev, "unhandled irqstat1 %08x\n", stat);
+}
+
+static irqreturn_t net2280_irq (int irq, void *_dev, struct pt_regs * r)
+{
+	struct net2280		*dev = _dev;
+
+	spin_lock (&dev->lock);
+
+	/* handle disconnect, dma, and more */
+	handle_stat1_irqs (dev, readl (&dev->regs->irqstat1));
+
+	/* control requests and PIO */
+	handle_stat0_irqs (dev, readl (&dev->regs->irqstat0));
+
+	spin_unlock (&dev->lock);
+
+	return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* tear down the binding between this driver and the pci device */
+
+static void net2280_remove (struct pci_dev *pdev)
+{
+	struct net2280		*dev = pci_get_drvdata (pdev);
+
+	/* start with the driver above us */
+	if (dev->driver) {
+		/* should have been done already by driver model core */
+		WARN (dev, "pci remove, driver '%s' is still registered\n",
+				dev->driver->driver.name);
+		usb_gadget_unregister_driver (dev->driver);
+	}
+
+	/* then clean up the resources we allocated during probe() */
+	net2280_led_shutdown (dev);
+	if (dev->requests) {
+		int		i;
+		for (i = 1; i < 5; i++) {
+			if (!dev->ep [i].dummy)
+				continue;
+			pci_pool_free (dev->requests, dev->ep [i].dummy,
+					dev->ep [i].td_dma);
+		}
+		pci_pool_destroy (dev->requests);
+	}
+	if (dev->got_irq)
+		free_irq (pdev->irq, dev);
+	if (dev->regs)
+		iounmap (dev->regs);
+	if (dev->region)
+		release_mem_region (pci_resource_start (pdev, 0),
+				pci_resource_len (pdev, 0));
+	if (dev->enabled)
+		pci_disable_device (pdev);
+	device_remove_file (&pdev->dev, &dev_attr_registers);
+	pci_set_drvdata (pdev, 0);
+
+	INFO (dev, "unbind from pci %s\n", pdev->slot_name);
+
+	kfree (dev);
+	the_controller = 0;
+}
+
+/* wrap this driver around the specified device, but
+ * don't respond over USB until a gadget driver binds to us.
+ */
+
+static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct net2280		*dev;
+	unsigned long		resource, len;
+	void			*base = 0;
+	int			retval, i;
+	char			buf [8], *bufp;
+
+	/* if you want to support more than one controller in a system,
+	 * usb_gadget_driver_{register,unregister}() must change.
+	 */
+	if (the_controller) {
+		WARN (the_controller, "ignoring %s\n", pdev->slot_name);
+		return -EBUSY;
+	}
+
+	/* alloc, and start init */
+	dev = kmalloc (sizeof *dev, SLAB_KERNEL);
+	if (dev == NULL){
+		retval = -ENOMEM;
+		goto done;
+	}
+
+	memset (dev, 0, sizeof *dev);
+	spin_lock_init (&dev->lock);
+	dev->pdev = pdev;
+	dev->gadget.ops = &net2280_ops;
+
+	strcpy (dev->gadget.dev.bus_id, pdev->slot_name);
+	strcpy (dev->gadget.dev.name, pdev->dev.name);
+	dev->gadget.dev.parent = &pdev->dev;
+	dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
+	dev->gadget.name = driver_name;
+
+	/* now all the pci goodies ... */
+	if (pci_enable_device (pdev) < 0) {
+   	        retval = -ENODEV;
+		goto done;
+	}
+	dev->enabled = 1;
+
+	/* BAR 0 holds all the registers
+	 * BAR 1 is 8051 memory; unused here (note erratum 0103)
+	 * BAR 2 is fifo memory; unused here
+	 */
+	resource = pci_resource_start (pdev, 0);
+	len = pci_resource_len (pdev, 0);
+	if (!request_mem_region (resource, len, driver_name)) {
+		DEBUG (dev, "controller already in use\n");
+		retval = -EBUSY;
+		goto done;
+	}
+	dev->region = 1;
+
+	base = ioremap_nocache (resource, len);
+	if (base == NULL) {
+		DEBUG (dev, "can't map memory\n");
+		retval = -EFAULT;
+		goto done;
+	}
+	dev->regs = (struct net2280_regs *) base;
+	dev->usb = (struct net2280_usb_regs *) (base + 0x0080);
+	dev->pci = (struct net2280_pci_regs *) (base + 0x0100);
+	dev->dma = (struct net2280_dma_regs *) (base + 0x0180);
+	dev->dep = (struct net2280_dep_regs *) (base + 0x0200);
+	dev->epregs = (struct net2280_ep_regs *) (base + 0x0300);
+
+	/* put into initial config, link up all endpoints */
+	usb_reset (dev);
+	usb_reinit (dev);
+
+	/* irq setup after old hardware is cleaned up */
+	if (!pdev->irq) {
+		ERROR (dev, "No IRQ.  Check PCI setup!\n");
+		retval = -ENODEV;
+		goto done;
+	}
+#ifndef __sparc__
+	snprintf (buf, sizeof buf, "%d", pdev->irq);
+	bufp = buf;
+#else
+	bufp = __irq_itoa(pdev->irq);
+#endif
+	if (request_irq (pdev->irq, net2280_irq, SA_SHIRQ, driver_name, dev)
+			!= 0) {
+		ERROR (dev, "request interrupt %s failed\n", bufp);
+		retval = -EBUSY;
+		goto done;
+	}
+	dev->got_irq = 1;
+
+	/* DMA setup */
+	dev->requests = pci_pool_create ("requests", pdev,
+		sizeof (struct net2280_dma),
+		0 /* no alignment requirements */,
+		0 /* or page-crossing issues */);
+	if (!dev->requests) {
+		DEBUG (dev, "can't get request pool\n");
+		retval = -ENOMEM;
+		goto done;
+	}
+	for (i = 1; i < 5; i++) {
+		struct net2280_dma	*td;
+
+		td = pci_pool_alloc (dev->requests, GFP_KERNEL,
+				&dev->ep [i].td_dma);
+		if (!td) {
+			DEBUG (dev, "can't get dummy %d\n", i);
+			retval = -ENOMEM;
+			goto done;
+		}
+		td->dmacount = 0;	/* not VALID */
+		td->dmaaddr = cpu_to_le32 (DMA_ADDR_INVALID);
+		dev->ep [i].dummy = td;
+	}
+
+	/* enable lower-overhead pci memory bursts during DMA */
+	writel ((1 << PCI_RETRY_ABORT_ENABLE)
+			| (1 << DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE)
+			| (1 << DMA_READ_MULTIPLE_ENABLE)
+			| (1 << DMA_READ_LINE_ENABLE)
+			, &dev->pci->pcimstctl);
+	/* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */
+	pci_set_master (pdev);
+	pci_set_mwi (pdev);
+
+	/* ... also flushes any posted pci writes */
+	dev->chiprev = get_idx_reg (dev->regs, REG_CHIPREV) & 0xffff;
+
+	/* done */
+	pci_set_drvdata (pdev, dev);
+	INFO (dev, "%s\n", driver_desc);
+	INFO (dev, "irq %s, pci mem %p, chip rev %04x\n",
+			bufp, base, dev->chiprev);
+	bufp = DRIVER_VERSION
+#ifndef USE_DMA_CHAINING
+		" (no dma chain)"
+#endif
+#ifdef NET2280_DMA_OUT_WORKAROUND
+		" (no dma out)"
+#endif
+		;
+	INFO (dev, "version: %s\n", bufp);
+	the_controller = dev;
+
+	device_create_file (&pdev->dev, &dev_attr_registers);
+
+	return 0;
+
+done:
+	if (dev)
+		net2280_remove (pdev);
+	return retval;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static const struct pci_device_id __devinitdata pci_ids [] = { {
+	.class = 	((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+	.class_mask = 	~0,
+	.vendor =	0x17cc,
+	.device =	0x2280,
+	.subvendor =	PCI_ANY_ID,
+	.subdevice =	PCI_ANY_ID,
+
+}, { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE (pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver net2280_pci_driver = {
+	.name =		(char *) driver_name,
+	.id_table =	pci_ids,
+
+	.probe =	net2280_probe,
+	.remove =	net2280_remove,
+
+	/* FIXME add power management support */
+};
+
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_AUTHOR ("David Brownell");
+MODULE_LICENSE ("GPL");
+
+static int __init init (void)
+{
+	return pci_module_init (&net2280_pci_driver);
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+	pci_unregister_driver (&net2280_pci_driver);
+}
+module_exit (cleanup);
diff -urN linux-2.5.69-bk5/drivers/usb/gadget/net2280.h linux-2.5.69-bk6/drivers/usb/gadget/net2280.h
--- linux-2.5.69-bk5/drivers/usb/gadget/net2280.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.69-bk6/drivers/usb/gadget/net2280.h	2003-05-11 04:43:33.000000000 -0700
@@ -0,0 +1,709 @@
+/*
+ * NetChip 2280 high/full speed USB device controller.
+ * Unlike many such controllers, this one talks PCI.
+ */
+
+/*
+ * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com)
+ * Copyright (C) 2003 David Brownell
+ *
+ * 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
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* NET2280 MEMORY MAPPED REGISTERS
+ *
+ * The register layout came from the chip documentation, and the bit
+ * number definitions were extracted from chip specification.
+ *
+ * Use the shift operator ('<<') to build bit masks, with readl/writel
+ * to access the registers through PCI.
+ */
+
+/* main registers, BAR0 + 0x0000 */
+struct net2280_regs {
+	// offset 0x0000
+	u32		devinit;
+#define     LOCAL_CLOCK_FREQUENCY                               8
+#define     FORCE_PCI_RESET                                     7
+#define     PCI_ID                                              6
+#define     PCI_ENABLE                                          5
+#define     FIFO_SOFT_RESET                                     4
+#define     CFG_SOFT_RESET                                      3
+#define     PCI_SOFT_RESET                                      2
+#define     USB_SOFT_RESET                                      1
+#define     M8051_RESET                                         0
+	u32		eectl;
+#define     EEPROM_ADDRESS_WIDTH                                23
+#define     EEPROM_CHIP_SELECT_ACTIVE                           22
+#define     EEPROM_PRESENT                                      21
+#define     EEPROM_VALID                                        20
+#define     EEPROM_BUSY                                         19
+#define     EEPROM_CHIP_SELECT_ENABLE                           18
+#define     EEPROM_BYTE_READ_START                              17
+#define     EEPROM_BYTE_WRITE_START                             16
+#define     EEPROM_READ_DATA                                    8
+#define     EEPROM_WRITE_DATA                                   0
+	u32		eeclkfreq;
+	u32		_unused0;
+	// offset 0x0010
+
+	u32		pciirqenb0;		/* interrupt PCI master ... */
+#define     SETUP_PACKET_INTERRUPT_ENABLE                       7
+#define     ENDPOINT_F_INTERRUPT_ENABLE                         6
+#define     ENDPOINT_E_INTERRUPT_ENABLE                         5
+#define     ENDPOINT_D_INTERRUPT_ENABLE                         4
+#define     ENDPOINT_C_INTERRUPT_ENABLE                         3
+#define     ENDPOINT_B_INTERRUPT_ENABLE                         2
+#define     ENDPOINT_A_INTERRUPT_ENABLE                         1
+#define     ENDPOINT_0_INTERRUPT_ENABLE                         0
+	u32		pciirqenb1;
+#define     PCI_INTERRUPT_ENABLE                                31
+#define     POWER_STATE_CHANGE_INTERRUPT_ENABLE                 27
+#define     PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE                26
+#define     PCI_PARITY_ERROR_INTERRUPT_ENABLE                   25
+#define     PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE          20
+#define     PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE          19
+#define     PCI_TARGET_ABORT_ASSERTED_INTERRUPT_ENABLE          18
+#define     PCI_RETRY_ABORT_INTERRUPT_ENABLE                    17
+#define     PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE              16
+#define     GPIO_INTERRUPT_ENABLE                               13
+#define     DMA_D_INTERRUPT_ENABLE                              12
+#define     DMA_C_INTERRUPT_ENABLE                              11
+#define     DMA_B_INTERRUPT_ENABLE                              10
+#define     DMA_A_INTERRUPT_ENABLE                              9
+#define     EEPROM_DONE_INTERRUPT_ENABLE                        8
+#define     VBUS_INTERRUPT_ENABLE                               7
+#define     CONTROL_STATUS_INTERRUPT_ENABLE                     6
+#define     ROOT_PORT_RESET_INTERRUPT_ENABLE                    4
+#define     SUSPEND_REQUEST_INTERRUPT_ENABLE                    3
+#define     SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE             2
+#define     RESUME_INTERRUPT_ENABLE                             1
+#define     SOF_INTERRUPT_ENABLE                                0
+	u32		cpu_irqenb0;		/* ... or onboard 8051 */
+#define     SETUP_PACKET_INTERRUPT_ENABLE                       7
+#define     ENDPOINT_F_INTERRUPT_ENABLE                         6
+#define     ENDPOINT_E_INTERRUPT_ENABLE                         5
+#define     ENDPOINT_D_INTERRUPT_ENABLE                         4
+#define     ENDPOINT_C_INTERRUPT_ENABLE                         3
+#define     ENDPOINT_B_INTERRUPT_ENABLE                         2
+#define     ENDPOINT_A_INTERRUPT_ENABLE                         1
+#define     ENDPOINT_0_INTERRUPT_ENABLE                         0
+	u32		cpu_irqenb1;
+#define     CPU_INTERRUPT_ENABLE                                31
+#define     POWER_STATE_CHANGE_INTERRUPT_ENABLE                 27
+#define     PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE                26
+#define     PCI_PARITY_ERROR_INTERRUPT_ENABLE                   25
+#define     PCI_INTA_INTERRUPT_ENABLE                           24
+#define     PCI_PME_INTERRUPT_ENABLE                            23
+#define     PCI_SERR_INTERRUPT_ENABLE                           22
+#define     PCI_PERR_INTERRUPT_ENABLE                           21
+#define     PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE          20
+#define     PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE          19
+#define     PCI_RETRY_ABORT_INTERRUPT_ENABLE                    17
+#define     PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE              16
+#define     GPIO_INTERRUPT_ENABLE                               13
+#define     DMA_D_INTERRUPT_ENABLE                              12
+#define     DMA_C_INTERRUPT_ENABLE                              11
+#define     DMA_B_INTERRUPT_ENABLE                              10
+#define     DMA_A_INTERRUPT_ENABLE                              9
+#define     EEPROM_DONE_INTERRUPT_ENABLE                        8
+#define     VBUS_INTERRUPT_ENABLE                               7
+#define     CONTROL_STATUS_INTERRUPT_ENABLE                     6
+#define     ROOT_PORT_RESET_INTERRUPT_ENABLE                    4
+#define     SUSPEND_REQUEST_INTERRUPT_ENABLE                    3
+#define     SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE             2
+#define     RESUME_INTERRUPT_ENABLE                             1
+#define     SOF_INTERRUPT_ENABLE                                0
+
+	// offset 0x0020
+	u32		_unused1;
+	u32		usbirqenb1;
+#define     USB_INTERRUPT_ENABLE                                31
+#define     POWER_STATE_CHANGE_INTERRUPT_ENABLE                 27
+#define     PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE                26
+#define     PCI_PARITY_ERROR_INTERRUPT_ENABLE                   25
+#define     PCI_INTA_INTERRUPT_ENABLE                           24
+#define     PCI_PME_INTERRUPT_ENABLE                            23
+#define     PCI_SERR_INTERRUPT_ENABLE                           22
+#define     PCI_PERR_INTERRUPT_ENABLE                           21
+#define     PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE          20
+#define     PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE          19
+#define     PCI_RETRY_ABORT_INTERRUPT_ENABLE                    17
+#define     PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE              16
+#define     GPIO_INTERRUPT_ENABLE                               13
+#define     DMA_D_INTERRUPT_ENABLE                              12
+#define     DMA_C_INTERRUPT_ENABLE                              11
+#define     DMA_B_INTERRUPT_ENABLE                              10
+#define     DMA_A_INTERRUPT_ENABLE                              9
+#define     EEPROM_DONE_INTERRUPT_ENABLE                        8
+#define     VBUS_INTERRUPT_ENABLE                               7
+#define     CONTROL_STATUS_INTERRUPT_ENABLE                     6
+#define     ROOT_PORT_RESET_INTERRUPT_ENABLE                    4
+#define     SUSPEND_REQUEST_INTERRUPT_ENABLE                    3
+#define     SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE             2
+#define     RESUME_INTERRUPT_ENABLE                             1
+#define     SOF_INTERRUPT_ENABLE                                0
+	u32		irqstat0;
+#define     INTA_ASSERTED                                       12
+#define     SETUP_PACKET_INTERRUPT                              7
+#define     ENDPOINT_F_INTERRUPT                                6
+#define     ENDPOINT_E_INTERRUPT                                5
+#define     ENDPOINT_D_INTERRUPT                                4
+#define     ENDPOINT_C_INTERRUPT                                3
+#define     ENDPOINT_B_INTERRUPT                                2
+#define     ENDPOINT_A_INTERRUPT                                1
+#define     ENDPOINT_0_INTERRUPT                                0
+	u32		irqstat1;
+#define     POWER_STATE_CHANGE_INTERRUPT                        27
+#define     PCI_ARBITER_TIMEOUT_INTERRUPT                       26
+#define     PCI_PARITY_ERROR_INTERRUPT                          25
+#define     PCI_INTA_INTERRUPT                                  24
+#define     PCI_PME_INTERRUPT                                   23
+#define     PCI_SERR_INTERRUPT                                  22
+#define     PCI_PERR_INTERRUPT                                  21
+#define     PCI_MASTER_ABORT_RECEIVED_INTERRUPT                 20
+#define     PCI_TARGET_ABORT_RECEIVED_INTERRUPT                 19
+#define     PCI_RETRY_ABORT_INTERRUPT                           17
+#define     PCI_MASTER_CYCLE_DONE_INTERRUPT                     16
+#define     GPIO_INTERRUPT                                      13
+#define     DMA_D_INTERRUPT                                     12
+#define     DMA_C_INTERRUPT                                     11
+#define     DMA_B_INTERRUPT                                     10
+#define     DMA_A_INTERRUPT                                     9
+#define     EEPROM_DONE_INTERRUPT                               8
+#define     VBUS_INTERRUPT                                      7
+#define     CONTROL_STATUS_INTERRUPT                            6
+#define     ROOT_PORT_RESET_INTERRUPT                           4
+#define     SUSPEND_REQUEST_INTERRUPT                           3
+#define     SUSPEND_REQUEST_CHANGE_INTERRUPT                    2
+#define     RESUME_INTERRUPT                                    1
+#define     SOF_INTERRUPT                                       0
+	// offset 0x0030
+	u32		idxaddr;
+	u32		idxdata;
+	u32		fifoctl;
+#define     PCI_BASE2_RANGE                                     16
+#define     IGNORE_FIFO_AVAILABILITY                            3
+#define     PCI_BASE2_SELECT                                    2
+#define     FIFO_CONFIGURATION_SELECT                           0
+	u32		_unused2;
+	// offset 0x0040
+	u32		memaddr;
+#define     START                                               28
+#define     DIRECTION                                           27
+#define     FIFO_DIAGNOSTIC_SELECT                              24
+#define     MEMORY_ADDRESS                                      0
+	u32		memdata0;
+	u32		memdata1;
+	u32		_unused3;
+	// offset 0x0050
+	u32		gpioctl;
+#define     GPIO3_LED_SELECT                                    12
+#define     GPIO3_INTERRUPT_ENABLE                              11
+#define     GPIO2_INTERRUPT_ENABLE                              10
+#define     GPIO1_INTERRUPT_ENABLE                              9
+#define     GPIO0_INTERRUPT_ENABLE                              8
+#define     GPIO3_OUTPUT_ENABLE                                 7
+#define     GPIO2_OUTPUT_ENABLE                                 6
+#define     GPIO1_OUTPUT_ENABLE                                 5
+#define     GPIO0_OUTPUT_ENABLE                                 4
+#define     GPIO3_DATA                                          3
+#define     GPIO2_DATA                                          2
+#define     GPIO1_DATA                                          1
+#define     GPIO0_DATA                                          0
+	u32		gpiostat;
+#define     GPIO3_INTERRUPT                                     3
+#define     GPIO2_INTERRUPT                                     2
+#define     GPIO1_INTERRUPT                                     1
+#define     GPIO0_INTERRUPT                                     0
+} __attribute__ ((packed));
+
+/* usb control, BAR0 + 0x0080 */
+struct net2280_usb_regs {
+	// offset 0x0080
+	u32		stdrsp;
+#define     STALL_UNSUPPORTED_REQUESTS                          31
+#define     SET_TEST_MODE                                       16
+#define     GET_OTHER_SPEED_CONFIGURATION                       15
+#define     GET_DEVICE_QUALIFIER                                14
+#define     SET_ADDRESS                                         13
+#define     ENDPOINT_SET_CLEAR_HALT                             12
+#define     DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP               11
+#define     GET_STRING_DESCRIPTOR_2                             10
+#define     GET_STRING_DESCRIPTOR_1                             9
+#define     GET_STRING_DESCRIPTOR_0                             8
+#define     GET_SET_INTERFACE                                   6
+#define     GET_SET_CONFIGURATION                               5
+#define     GET_CONFIGURATION_DESCRIPTOR                        4
+#define     GET_DEVICE_DESCRIPTOR                               3
+#define     GET_ENDPOINT_STATUS                                 2
+#define     GET_INTERFACE_STATUS                                1
+#define     GET_DEVICE_STATUS                                   0
+	u32		prodvendid;
+#define     PRODUCT_ID                                          16
+#define     VENDOR_ID                                           0
+	u32		relnum;
+	u32		usbctl;
+#define     SERIAL_NUMBER_INDEX                                 16
+#define     PRODUCT_ID_STRING_ENABLE                            13
+#define     VENDOR_ID_STRING_ENABLE                             12
+#define     USB_ROOT_PORT_WAKEUP_ENABLE                         11
+#define     VBUS_PIN                                            10
+#define     TIMED_DISCONNECT                                    9
+#define     SUSPEND_IMMEDIATELY                                 7
+#define     SELF_POWERED_USB_DEVICE                             6
+#define     REMOTE_WAKEUP_SUPPORT                               5
+#define     PME_POLARITY                                        4
+#define     USB_DETECT_ENABLE                                   3
+#define     PME_WAKEUP_ENABLE                                   2
+#define     DEVICE_REMOTE_WAKEUP_ENABLE                         1
+#define     SELF_POWERED_STATUS                                 0
+	// offset 0x0090
+	u32		usbstat;
+#define     HIGH_SPEED                                          7
+#define     FULL_SPEED                                          6
+#define     GENERATE_RESUME                                     5
+#define     GENERATE_DEVICE_REMOTE_WAKEUP                       4
+	u32		xcvrdiag;
+#define     FORCE_HIGH_SPEED_MODE                               31
+#define     FORCE_FULL_SPEED_MODE                               30
+#define     USB_TEST_MODE                                       24
+#define     LINE_STATE                                          16
+#define     TRANSCEIVER_OPERATION_MODE                          2
+#define     TRANSCEIVER_SELECT                                  1
+#define     TERMINATION_SELECT                                  0
+	u32		setup0123;
+	u32		setup4567;
+	// offset 0x0090
+	u32		_unused0;
+	u32		ouraddr;
+#define     FORCE_IMMEDIATE                                     7
+#define     OUR_USB_ADDRESS                                     0
+	u32		ourconfig;
+} __attribute__ ((packed));
+
+/* pci control, BAR0 + 0x0100 */
+struct net2280_pci_regs {
+	// offset 0x0100
+	u32		 pcimstctl;
+#define     PCI_ARBITER_PARK_SELECT                             13
+#define     PCI_MULTI LEVEL_ARBITER                             12
+#define     PCI_RETRY_ABORT_ENABLE                              11
+#define     DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE              10
+#define     DMA_READ_MULTIPLE_ENABLE                            9
+#define     DMA_READ_LINE_ENABLE                                8
+#define     PCI_MASTER_COMMAND_SELECT                           6
+#define         MEM_READ_OR_WRITE                                   0
+#define         IO_READ_OR_WRITE                                    1
+#define         CFG_READ_OR_WRITE                                   2
+#define     PCI_MASTER_START                                    5
+#define     PCI_MASTER_READ_WRITE                               4
+#define         PCI_MASTER_WRITE                                    0
+#define         PCI_MASTER_READ                                     1
+#define     PCI_MASTER_BYTE_WRITE_ENABLES                       0
+	u32		 pcimstaddr;
+	u32		 pcimstdata;
+	u32		 pcimststat;
+#define     PCI_ARBITER_CLEAR                                   2
+#define     PCI_EXTERNAL_ARBITER                                1
+#define     PCI_HOST_MODE                                       0
+} __attribute__ ((packed));
+
+/* dma control, BAR0 + 0x0180 ... array of four structs like this,
+ * for channels 0..3.  see also struct net2280_dma:  descriptor
+ * that can be loaded into some of these registers.
+ */
+struct net2280_dma_regs {	/* [11.7] */
+	// offset 0x0180, 0x01a0, 0x01c0, 0x01e0, 
+	u32		dmactl;
+#define     DMA_SCATTER_GATHER_DONE_INTERRUPT_ENABLE            25
+#define     DMA_CLEAR_COUNT_ENABLE                              21
+#define     DESCRIPTOR_POLLING_RATE                             19
+#define         POLL_CONTINUOUS                                     0
+#define         POLL_1_USEC                                         1
+#define         POLL_100_USEC                                       2
+#define         POLL_1_MSEC                                         3
+#define     DMA_VALID_BIT_POLLING_ENABLE                        18
+#define     DMA_VALID_BIT_ENABLE                                17
+#define     DMA_SCATTER_GATHER_ENABLE                           16
+#define     DMA_OUT_AUTO_START_ENABLE                           4
+#define     DMA_PREEMPT_ENABLE                                  3
+#define     DMA_FIFO_VALIDATE                                   2
+#define     DMA_ENABLE                                          1
+#define     DMA_ADDRESS_HOLD                                    0
+	u32		dmastat;
+#define     DMA_SCATTER_GATHER_DONE_INTERRUPT                   25
+#define     DMA_TRANSACTION_DONE_INTERRUPT                      24
+#define     DMA_ABORT                                           1
+#define     DMA_START                                           0
+	u32		_unused0 [2];
+	// offset 0x0190, 0x01b0, 0x01d0, 0x01f0, 
+	u32		dmacount;
+#define     VALID_BIT                                           31
+#define     DMA_DIRECTION                                       30
+#define     DMA_DONE_INTERRUPT_ENABLE                           29
+#define     END_OF_CHAIN                                        28
+#define         DMA_BYTE_COUNT_MASK                                 ((1<<24)-1)
+#define     DMA_BYTE_COUNT                                      0
+	u32		dmaaddr;
+	u32		dmadesc;
+	u32		_unused1;
+} __attribute__ ((packed));
+
+/* dedicated endpoint registers, BAR0 + 0x0200 */
+
+struct net2280_dep_regs {	/* [11.8] */
+	// offset 0x0200, 0x0210, 0x220, 0x230, 0x240
+	u32		dep_cfg;
+	// offset 0x0204, 0x0214, 0x224, 0x234, 0x244
+	u32		dep_rsp;
+	u32		_unused [2];
+} __attribute__ ((packed));
+
+/* configurable endpoint registers, BAR0 + 0x0300 ... array of seven structs
+ * like this, for ep0 then the configurable endpoints A..F
+ * ep0 reserved for control; E and F have only 64 bytes of fifo
+ */
+struct net2280_ep_regs {	/* [11.9] */
+	// offset 0x0300, 0x0320, 0x0340, 0x0360, 0x0380, 0x03a0, 0x03c0
+	u32		ep_cfg;
+#define     ENDPOINT_BYTE_COUNT                                 16
+#define     ENDPOINT_ENABLE                                     10
+#define     ENDPOINT_TYPE                                       8
+#define     ENDPOINT_DIRECTION                                  7
+#define     ENDPOINT_NUMBER                                     0
+	u32		ep_rsp;
+#define     SET_NAK_OUT_PACKETS                                 15
+#define     SET_EP_HIDE_STATUS_PHASE                            14
+#define     SET_INTERRUPT_MODE                                  12
+#define     SET_CONTROL_STATUS_PHASE_HANDSHAKE                  11
+#define     SET_NAK_OUT_PACKETS_MODE                            10
+#define     SET_ENDPOINT_TOGGLE                                 9
+#define     SET_ENDPOINT_HALT                                   8
+#define     CLEAR_NAK_OUT_PACKETS                               7
+#define     CLEAR_EP_HIDE_STATUS_PHASE                          6
+#define     CLEAR_INTERRUPT_MODE                                4
+#define     CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE                3
+#define     CLEAR_NAK_OUT_PACKETS_MODE                          2
+#define     CLEAR_ENDPOINT_TOGGLE                               1
+#define     CLEAR_ENDPOINT_HALT                                 0
+	u32		ep_irqenb;
+#define     SHORT_PACKET_OUT_DONE_INTERRUPT_ENABLE              6
+#define     SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE           5
+#define     DATA_PACKET_RECEIVED_INTERRUPT_ENABLE               3
+#define     DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE            2
+#define     DATA_OUT_PING_TOKEN_INTERRUPT_ENABLE                1
+#define     DATA_IN_TOKEN_INTERRUPT_ENABLE                      0
+	u32		ep_stat;
+#define     FIFO_VALID_COUNT                                    24
+#define     HIGH_BANDWIDTH_OUT_TRANSACTION_PID                  22
+#define     TIMEOUT                                             21
+#define     USB_STALL_SENT                                      20
+#define     USB_IN_NAK_SENT                                     19
+#define     USB_IN_ACK_RCVD                                     18
+#define     USB_OUT_PING_NAK_SENT                               17
+#define     USB_OUT_ACK_SENT                                    16
+#define     FIFO_OVERFLOW                                       13
+#define     FIFO_UNDERFLOW                                      12
+#define     FIFO_FULL                                           11
+#define     FIFO_EMPTY                                          10
+#define     FIFO_FLUSH                                          9
+#define     SHORT_PACKET_OUT_DONE_INTERRUPT                     6
+#define     SHORT_PACKET_TRANSFERRED_INTERRUPT                  5
+#define     NAK_OUT_PACKETS                                     4
+#define     DATA_PACKET_RECEIVED_INTERRUPT                      3
+#define     DATA_PACKET_TRANSMITTED_INTERRUPT                   2
+#define     DATA_OUT_PING_TOKEN_INTERRUPT                       1
+#define     DATA_IN_TOKEN_INTERRUPT                             0
+	// offset 0x0310, 0x0330, 0x0350, 0x0370, 0x0390, 0x03b0, 0x03d0
+	u32		ep_avail;
+	u32		ep_data;
+	u32		_unused0 [2];
+} __attribute__ ((packed));
+
+/*-------------------------------------------------------------------------*/
+
+/* indexed registers [11.10] are accessed indirectly
+ * caller must own the device lock.
+ */
+
+static inline u32
+get_idx_reg (struct net2280_regs *regs, u32 index)
+{
+	writel (index, &regs->idxaddr);
+	/* NOTE:  synchs device/cpu memory views */
+	return readl (&regs->idxdata);
+}
+
+static inline void
+set_idx_reg (struct net2280_regs *regs, u32 index, u32 value)
+{
+	writel (index, &regs->idxaddr);
+	writel (value, &regs->idxdata);
+	/* posted, may not be visible yet */
+}
+
+#define REG_DIAG		0x0
+#define     RETRY_COUNTER                                       16
+#define     FORCE_PCI_SERR                                      11
+#define     FORCE_PCI_INTERRUPT                                 10
+#define     FORCE_USB_INTERRUPT                                 9
+#define     FORCE_CPU_INTERRUPT                                 8
+#define     ILLEGAL_BYTE_ENABLES                                5
+#define     FAST_TIMES                                          4
+#define     FORCE_RECEIVE_ERROR                                 2
+#define     FORCE_TRANSMIT_CRC_ERROR                            0
+#define REG_FRAME		0x02	/* from last sof */
+#define REG_CHIPREV		0x03	/* in bcd */
+#define	REG_HS_NAK_RATE		0x0a	/* NAK per N uframes */
+
+/* ep a-f highspeed and fullspeed maxpacket, addresses
+ * computed from ep->num
+ */
+#define REG_EP_MAXPKT(dev,num) (((num) + 1) * 0x10 + \
+		(((dev)->gadget.speed == USB_SPEED_HIGH) ? 0 : 1))
+
+/*-------------------------------------------------------------------------*/
+
+/* [8.3] for scatter/gather i/o
+ * use struct net2280_dma_regs bitfields
+ */
+struct net2280_dma {
+	u32		dmacount;
+	u32		dmaaddr;		/* the buffer */
+	u32		dmadesc;		/* next dma descriptor */
+	u32		_reserved;
+} __attribute__ ((aligned (16)));
+
+/*-------------------------------------------------------------------------*/
+
+/* DRIVER DATA STRUCTURES and UTILITIES */
+
+struct net2280_ep {
+	struct usb_ep				ep;
+	struct net2280_ep_regs			*regs;
+	struct net2280_dma_regs			*dma;
+	struct net2280_dma			*dummy;
+	dma_addr_t				td_dma;	/* of dummy */
+	struct net2280				*dev;
+	unsigned long				irqs;
+
+	/* analogous to a host-side qh */
+	struct list_head			queue;
+	const struct usb_endpoint_descriptor	*desc;
+	unsigned				num : 8,
+						fifo_size : 12,
+						in_fifo_validate : 1,
+						stopped : 1,
+						is_in : 1,
+						is_iso : 1;
+};
+
+static inline void allow_status (struct net2280_ep *ep)
+{
+	/* ep0 only */
+	writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
+		| (1 << CLEAR_NAK_OUT_PACKETS_MODE)
+		, &ep->regs->ep_rsp);
+}
+
+static inline void set_halt (struct net2280_ep *ep)
+{
+	/* ep0 and bulk/intr endpoints */
+	writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
+		    /* set NAK_OUT for erratum 0114 */
+		| (1 << SET_NAK_OUT_PACKETS)
+		| (1 << SET_ENDPOINT_HALT)
+		, &ep->regs->ep_rsp);
+}
+
+static inline void clear_halt (struct net2280_ep *ep)
+{
+	/* bulk/intr endpoints */
+	writel (  (1 << CLEAR_ENDPOINT_HALT)
+		| (1 << CLEAR_ENDPOINT_TOGGLE)
+		, &ep->regs->ep_rsp);
+}
+
+/* count (<= 4) bytes in the next fifo write will be valid */
+static inline void set_fifo_bytecount (struct net2280_ep *ep, unsigned count)
+{
+	writeb (count, 2 + (u8 *) &ep->regs->ep_cfg);
+}
+
+struct net2280_request {
+	struct usb_request		req;
+	struct net2280_dma		*td;
+	dma_addr_t			td_dma;
+	struct list_head		queue;
+	unsigned			mapped : 1,
+					dma_done : 1,
+					valid : 1;
+};
+
+struct net2280 {
+	/* each pci device provides one gadget, several endpoints */
+	struct usb_gadget		gadget;
+	spinlock_t			lock;
+	struct net2280_ep		ep [7];
+	struct usb_gadget_driver 	*driver;
+	unsigned			enabled : 1,
+					protocol_stall : 1,
+					got_irq : 1,
+					region : 1,
+					selfpowered : 1;
+	u16				chiprev;
+
+	/* pci state used to access those endpoints */
+	struct pci_dev			*pdev;
+	struct net2280_regs		*regs;
+	struct net2280_usb_regs		*usb;
+	struct net2280_pci_regs		*pci;
+	struct net2280_dma_regs		*dma;
+	struct net2280_dep_regs		*dep;
+	struct net2280_ep_regs		*epregs;
+
+	struct pci_pool			*requests;
+	// statistics...
+};
+
+#ifdef USE_RDK_LEDS
+
+static inline void net2280_led_init (struct net2280 *dev)
+{
+	/* LED3 (green) is on during USB activity. note erratum 0113. */
+	writel ((1 << GPIO3_LED_SELECT)
+		| (1 << GPIO3_OUTPUT_ENABLE)
+		| (1 << GPIO2_OUTPUT_ENABLE)
+		| (1 << GPIO1_OUTPUT_ENABLE)
+		| (1 << GPIO0_OUTPUT_ENABLE)
+		, &dev->regs->gpioctl);
+}
+
+/* indicate speed with bi-color LED 0/1 */
+static inline
+void net2280_led_speed (struct net2280 *dev, enum usb_device_speed speed)
+{
+	u32	val = readl (&dev->regs->gpioctl);
+	switch (speed) {
+	case USB_SPEED_HIGH:		/* green */
+		val &= ~(1 << GPIO0_DATA);
+		val |= (1 << GPIO1_DATA);
+		break;
+	case USB_SPEED_FULL:		/* red */
+		val &= ~(1 << GPIO1_DATA);
+		val |= (1 << GPIO0_DATA);
+		break;
+	default:			/* (off/black) */
+		val &= ~((1 << GPIO1_DATA) | (1 << GPIO0_DATA));
+		break;
+	}
+	writel (val, &dev->regs->gpioctl);
+}
+
+/* indicate power with LED 2 */
+static inline void net2280_led_active (struct net2280 *dev, int is_active)
+{
+	u32	val = readl (&dev->regs->gpioctl);
+
+	// FIXME this LED never seems to turn on.
+	if (is_active)
+		val |= GPIO2_DATA;
+	else
+		val &= ~GPIO2_DATA;
+	writel (val, &dev->regs->gpioctl);
+}
+static inline void net2280_led_shutdown (struct net2280 *dev)
+{
+	/* turn off all four GPIO*_DATA bits */
+	writel (readl (&dev->regs->gpioctl) & ~0x0f,
+			&dev->regs->gpioctl);
+}
+
+#else
+
+#define net2280_led_init(dev)		do { } while (0)
+#define net2280_led_speed(dev, speed)	do { } while (0)
+#define net2280_led_shutdown(dev)	do { } while (0)
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define xprintk(dev,level,fmt,args...) \
+	printk(level "%s %s: " fmt , driver_name , \
+			dev->pdev->slot_name , ## args)
+
+#ifdef DEBUG
+#undef DEBUG
+#define DEBUG(dev,fmt,args...) \
+	xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DEBUG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDEBUG DEBUG
+#else
+#define VDEBUG(dev,fmt,args...) \
+	do { } while (0)
+#endif	/* VERBOSE */
+
+#define ERROR(dev,fmt,args...) \
+	xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARN(dev,fmt,args...) \
+	xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+	xprintk(dev , KERN_INFO , fmt , ## args)
+
+/*-------------------------------------------------------------------------*/
+
+static inline void start_out_naking (struct net2280_ep *ep)
+{
+	/* NOTE:  hardware races lurk here, and PING protocol issues */
+	writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+	/* synch with device */
+	readl (&ep->regs->ep_rsp);
+}
+
+#ifdef DEBUG
+static inline void assert_out_naking (struct net2280_ep *ep, const char *where)
+{
+	u32	tmp = readl (&ep->regs->ep_stat);
+
+	if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) {
+		DEBUG (ep->dev, "%s %s %08x !NAK\n",
+				ep->ep.name, where, tmp);
+		writel ((1 << SET_NAK_OUT_PACKETS),
+			&ep->regs->ep_rsp);
+	}
+}
+#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep,__FUNCTION__)
+#else
+#define ASSERT_OUT_NAKING(ep) do {} while (0)
+#endif
+
+static inline void stop_out_naking (struct net2280_ep *ep)
+{
+	u32	tmp;
+
+	tmp = readl (&ep->regs->ep_stat);
+	if ((tmp & (1 << NAK_OUT_PACKETS)) != 0)
+		writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+}
+
diff -urN linux-2.5.69-bk5/drivers/usb/gadget/usbstring.c linux-2.5.69-bk6/drivers/usb/gadget/usbstring.c
--- linux-2.5.69-bk5/drivers/usb/gadget/usbstring.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.69-bk6/drivers/usb/gadget/usbstring.c	2003-05-11 04:43:33.000000000 -0700
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2003 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/device.h>
+
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+
+
+/**
+ * usb_gadget_get_string - fill out a string descriptor 
+ * @table: of c strings using iso latin/1 characters
+ * @id: string id, from low byte of wValue in get string descriptor
+ * @buf: at least 256 bytes
+ *
+ * Finds the iso latin/1 string matching the ID, and converts it into a
+ * string descriptor in utf16-le.
+ * Returns length of descriptor (always even) or negative errno
+ *
+ * If your driver needs stings in multiple languages, you'll need to
+ * to use some alternate solution for languages where the ISO 8859/1
+ * (latin/1) character set can't be used.  For example, they can't be
+ * used with Chinese (Big5, GB2312, etc), Japanese, Korean, or many other
+ * languages.  You'd likely "switch (wIndex) { ... }"  in your ep0
+ * string descriptor logic, using this routine in cases where "western
+ * european" characters suffice for the strings being returned.
+ */
+int
+usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf)
+{
+	struct usb_string	*s;
+	int			len;
+
+	/* descriptor 0 has the language id */
+	if (id == 0) {
+		buf [0] = 4;
+		buf [1] = USB_DT_STRING;
+		buf [2] = (u8) table->language;
+		buf [3] = (u8) (table->language >> 8);
+		return 4;
+	}
+	for (s = table->strings; s && s->s; s++)
+		if (s->id == id)
+			break;
+
+	/* unrecognized: stall. */
+	if (!s || !s->s)
+		return -EINVAL;
+
+	/* string descriptors have length, tag, then UTF16-LE text */
+	len = min ((size_t) 126, strlen (s->s));
+	buf [0] = (len + 1) * 2;
+	buf [1] = USB_DT_STRING;
+	memset (buf + 2, 0, 2 * len);	/* zero all the high bytes */
+	while (len) {
+		buf [2 * len] = s->s [len - 1];
+		len--;
+	}
+	return buf [0];
+}
+
diff -urN linux-2.5.69-bk5/drivers/usb/gadget/zero.c linux-2.5.69-bk6/drivers/usb/gadget/zero.c
--- linux-2.5.69-bk5/drivers/usb/gadget/zero.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.69-bk6/drivers/usb/gadget/zero.c	2003-05-11 04:43:33.000000000 -0700
@@ -0,0 +1,1223 @@
+/*
+ * zero.c -- Gadget Zero, for USB development
+ *
+ * Copyright (C) 2003 David Brownell
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Gadget Zero only needs two bulk endpoints, and is an example of how you
+ * can write a hardware-agnostic gadget driver running inside a USB device.
+ *
+ * Hardware details are visible (see CONFIG_USB_ZERO_* below) but don't
+ * affect most of the driver.
+ *
+ * Use it with the Linux host/master side "usbtest" driver to get a basic
+ * functional test of your device-side usb stack, or with "usb-skeleton".
+ *
+ * It supports two similar configurations.  One sinks whatever the usb host
+ * writes, and in return sources zeroes.  The other loops whatever the host
+ * writes back, so the host can read it.  Module options include:
+ *
+ *   buflen=N		default N=4096, buffer size used
+ *   qlen=N		default N=32, how many buffers in the loopback queue
+ *   loopdefault	default false, list loopback config first
+ *
+ * Many drivers will only have one configuration, letting them be much
+ * simpler if they also don't support high speed operation (like this
+ * driver does).
+ */
+
+#define DEBUG 1
+// #define VERBOSE
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/uts.h>
+#include <linux/version.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+
+
+/*-------------------------------------------------------------------------*/
+
+#define DRIVER_VERSION		"19 Feb 2003"
+
+static const char shortname [] = "zero";
+static const char longname [] = "Gadget Zero";
+
+static const char source_sink [] = "source and sink data";
+static const char loopback [] = "loop input to output";
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * hardware-specific configuration, controlled by which device
+ * controller driver was configured.
+ *
+ * CHIP ... hardware identifier
+ * DRIVER_VERSION_NUM ... alerts the host side driver to differences
+ * EP0_MAXPACKET ... controls packetization of control requests
+ * EP_*_NAME ... which endpoints do we use for which purpose?
+ * EP_*_NUM ... numbers for them (often limited by hardware)
+ * HIGHSPEED ... define if ep0 and descriptors need high speed support
+ * MAX_USB_POWER ... define if we use other than 100 mA bus current
+ * SELFPOWER ... unless we can run on bus power, USB_CONFIG_ATT_SELFPOWER
+ * WAKEUP ... if hardware supports remote wakeup AND we will issue the
+ * 	usb_gadget_wakeup() call to initiate it, USB_CONFIG_ATT_WAKEUP
+ *
+ * hw_optimize(gadget) ... for any hardware tweaks we want to kick in
+ * 	before we enable our endpoints
+ *
+ * add other defines for other portability issues, like hardware that
+ * for some reason doesn't handle full speed bulk maxpacket of 64.
+ */
+
+/*
+ * DRIVER_VERSION_NUM 0x0000 (?):  Martin Diehl's ezusb an21/fx code
+ */
+
+/*
+ * NetChip 2280, PCI based.
+ *
+ * This has half a dozen configurable endpoints, four with dedicated
+ * DMA channels to manage their FIFOs.  It supports high speed.
+ * Those endpoints can be arranged in any desired configuration.
+ */
+#ifdef	CONFIG_USB_ZERO_NET2280
+#define CHIP			"net2280"
+#define DRIVER_VERSION_NUM	cpu_to_le16(0x0101)
+#define EP0_MAXPACKET		64
+static const char EP_OUT_NAME [] = "ep-a";
+#define EP_OUT_NUM	2
+static const char EP_IN_NAME [] = "ep-b";
+#define EP_IN_NUM	2
+#define HIGHSPEED
+/* specific hardware configs could be bus-powered */
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+/* supports remote wakeup, but this driver doesn't */
+
+extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode);
+
+static inline void hw_optimize (struct usb_gadget *gadget)
+{
+	/* we can have bigger ep-a/ep-b fifos (2KB each, 4 packets
+	 * for highspeed bulk) because we're not using ep-c/ep-d.
+	 */
+	net2280_set_fifo_mode (gadget, 1);
+}
+#endif
+
+/*
+ * PXA-250 UDC:  widely used in second gen Linux-capable PDAs.
+ *
+ * This has fifteen fixed-function full speed endpoints, and it
+ * can support all USB transfer types.
+ *
+ * It only supports three configurations (numbered 1, 2, or 3)
+ * with two interfaces each ... there's partial hardware support
+ * for set_configuration and set_interface, preventing some more
+ * interesting config/interface/endpoint arrangements.
+ */
+#ifdef	CONFIG_USB_ZERO_PXA250
+#define CHIP			"pxa250"
+#define DRIVER_VERSION_NUM	cpu_to_le16(0x0103)
+#define EP0_MAXPACKET		16
+static const char EP_OUT_NAME [] = "ep12out-bulk";
+#define EP_OUT_NUM	12
+static const char EP_IN_NAME [] = "ep11in-bulk";
+#define EP_IN_NUM	11
+/* doesn't support bus-powered operation */
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+/* supports remote wakeup, but this driver doesn't */
+
+/* no hw optimizations to apply */
+#define hw_optimize(g) do {} while (0);
+#endif
+
+/*
+ * SA-1100 UDC:  widely used in first gen Linux-capable PDAs.
+ *
+ * This has only two fixed function endpoints, which can only
+ * be used for bulk (or interrupt) transfers.  (Plus control.)
+ *
+ * Since it can't flush its TX fifos without disabling the UDC,
+ * the current configuration or altsettings can't change except
+ * in special situations.  So this is a case of "choose it right
+ * during enumeration" ...
+ */
+#ifdef	CONFIG_USB_ZERO_SA1100
+#define CHIP			"sa1100"
+#define DRIVER_VERSION_NUM	cpu_to_le16(0x0105)
+#define EP0_MAXPACKET		8
+static const char EP_OUT_NAME [] = "ep1out-bulk";
+#define EP_OUT_NUM	1
+static const char EP_IN_NAME [] = "ep2in-bulk";
+#define EP_IN_NUM	2
+/* doesn't support bus-powered operation */
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+/* doesn't support remote wakeup? */
+
+/* no hw optimizations to apply */
+#define hw_optimize(g) do {} while (0);
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef EP0_MAXPACKET
+#	error Configure some USB peripheral controller driver!
+#endif
+
+/* power usage is config specific.
+ * hardware that supports remote wakeup defaults to disabling it.
+ */
+
+#ifndef	SELFPOWER
+/* default: say we rely on bus power */
+#define SELFPOWER	0
+/* else:
+ * - SELFPOWER value must be USB_CONFIG_ATT_SELFPOWER
+ * - MAX_USB_POWER may be nonzero.
+ */
+#endif
+
+#ifndef	MAX_USB_POWER
+/* any hub supports this steady state bus power consumption */
+#define MAX_USB_POWER	100	/* mA */
+#endif
+
+#ifndef	WAKEUP
+/* default: this driver won't do remote wakeup */
+#define WAKEUP		0
+/* else value must be USB_CONFIG_ATT_WAKEUP */
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* big enough to hold our biggest descriptor */
+#define USB_BUFSIZ	256
+
+struct zero_dev {
+	spinlock_t		lock;
+	struct usb_gadget	*gadget;
+	struct usb_request	*req;		/* for control responses */
+
+	/* when configured, we have one of two configs:
+	 * - source data (in to host) and sink it (out from host)
+	 * - or loop it back (out from host back in to host)
+	 */
+	u8			config;
+	struct usb_ep		*in_ep, *out_ep;
+};
+
+#define xprintk(d,level,fmt,args...) \
+	dev_printk(level , &(d)->gadget->dev , fmt , ## args)
+
+#ifdef DEBUG
+#undef DEBUG
+#define DEBUG(dev,fmt,args...) \
+	xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DEBUG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDEBUG	DEBUG
+#else
+#define VDEBUG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#define ERROR(dev,fmt,args...) \
+	xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARN(dev,fmt,args...) \
+	xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+	xprintk(dev , KERN_INFO , fmt , ## args)
+
+/*-------------------------------------------------------------------------*/
+
+static unsigned buflen = 4096;
+static unsigned qlen = 32;
+
+module_param (buflen, uint, S_IRUGO|S_IWUSR);
+module_param (qlen, uint, S_IRUGO|S_IWUSR);
+
+/*
+ * Normally the "loopback" configuration is second (index 1) so
+ * it's not the default.  Here's where to change that order, to
+ * work better with hosts (like Linux ... for now!) where config
+ * changes are problematic.
+ */
+static int loopdefault = 0;
+
+module_param (loopdefault, bool, S_IRUGO|S_IWUSR);
+
+/*-------------------------------------------------------------------------*/
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with any other driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+#define DRIVER_VENDOR_NUM	0x0525		/* NetChip */
+#define DRIVER_PRODUCT_NUM	0xa4a0		/* Linux-USB "Gadget Zero" */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full)
+ * configuration descriptors are built on demand.
+ */
+
+#define STRING_MANUFACTURER		25
+#define STRING_PRODUCT			42
+#define STRING_SERIAL			101
+#define STRING_SOURCE_SINK		250
+#define STRING_LOOPBACK			251
+
+/*
+ * This device advertises two configurations; these numbers work
+ * on a pxa250 as well as more flexible hardware.
+ */
+#define	CONFIG_SOURCE_SINK	3
+#define	CONFIG_LOOPBACK		2
+
+static const struct usb_device_descriptor
+device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	.bcdUSB =		cpu_to_le16 (0x0200),
+	.bDeviceClass =		USB_CLASS_VENDOR_SPEC,
+	.bMaxPacketSize0 =	EP0_MAXPACKET,
+
+	.idVendor =		cpu_to_le16 (DRIVER_VENDOR_NUM),
+	.idProduct =		cpu_to_le16 (DRIVER_PRODUCT_NUM),
+	.bcdDevice =		cpu_to_le16 (DRIVER_VERSION_NUM),
+	.iManufacturer =	STRING_MANUFACTURER,
+	.iProduct =		STRING_PRODUCT,
+	.iSerialNumber =	STRING_SERIAL,
+	.bNumConfigurations =	2,
+};
+
+static const struct usb_config_descriptor
+source_sink_config = {
+	.bLength =		sizeof source_sink_config,
+	.bDescriptorType =	USB_DT_CONFIG,
+
+	/* compute wTotalLength on the fly */
+	.bNumInterfaces =	1,
+	.bConfigurationValue =	CONFIG_SOURCE_SINK,
+	.iConfiguration =	STRING_SOURCE_SINK,
+	.bmAttributes =		USB_CONFIG_ATT_ONE | SELFPOWER | WAKEUP,
+	.bMaxPower =		(MAX_USB_POWER + 1) / 2,
+};
+
+static const struct usb_config_descriptor
+loopback_config = {
+	.bLength =		sizeof loopback_config,
+	.bDescriptorType =	USB_DT_CONFIG,
+
+	/* compute wTotalLength on the fly */
+	.bNumInterfaces =	1,
+	.bConfigurationValue =	CONFIG_LOOPBACK,
+	.iConfiguration =	STRING_LOOPBACK,
+	.bmAttributes =		USB_CONFIG_ATT_ONE | SELFPOWER | WAKEUP,
+	.bMaxPower =		(MAX_USB_POWER + 1) / 2,
+};
+
+/* one interface in each configuration */
+
+static const struct usb_interface_descriptor
+source_sink_intf = {
+	.bLength =		sizeof source_sink_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	.iInterface =		STRING_SOURCE_SINK,
+};
+
+static const struct usb_interface_descriptor
+loopback_intf = {
+	.bLength =		sizeof loopback_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	.iInterface =		STRING_LOOPBACK,
+};
+
+/* two full speed bulk endpoints; their use is config-dependent */
+
+static const struct usb_endpoint_descriptor
+fs_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_IN_NUM | USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16 (64),
+};
+
+static const struct usb_endpoint_descriptor
+fs_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_OUT_NUM,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16 (64),
+};
+
+#ifdef	HIGHSPEED
+
+/*
+ * usb 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ *
+ * that means alternate endpoint descriptors (bigger packets)
+ * and a "device qualifier" ... plus more construction options
+ * for the config descriptor.
+ */
+
+static const struct usb_endpoint_descriptor
+hs_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_IN_NUM | USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16 (512),
+};
+
+static const struct usb_endpoint_descriptor
+hs_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_OUT_NUM,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16 (512),
+};
+
+static const struct usb_qualifier_descriptor
+dev_qualifier = {
+	.bLength =		sizeof dev_qualifier,
+	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER,
+
+	.bcdUSB =		cpu_to_le16 (0x0200),
+	.bDeviceClass =		USB_CLASS_VENDOR_SPEC,
+
+	/* assumes ep0 uses the same value for both speeds ... */
+	.bMaxPacketSize0 =	EP0_MAXPACKET,
+
+	.bNumConfigurations =	2,
+};
+
+/* maxpacket and other transfer characteristics vary by speed. */
+#define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs))
+
+#else
+
+/* if there's no high speed support, maxpacket doesn't change. */
+#define ep_desc(g,hs,fs) fs
+
+#endif	/* !HIGHSPEED */
+
+static char				serial [40];
+
+/* static strings, in iso 8859/1 */
+static struct usb_string		strings [] = {
+	{ STRING_MANUFACTURER, UTS_SYSNAME " " UTS_RELEASE " with " CHIP, },
+	{ STRING_PRODUCT, longname, },
+	{ STRING_SERIAL, serial, },
+	{ STRING_LOOPBACK, loopback, },
+	{ STRING_SOURCE_SINK, source_sink, },
+	{  }			/* end of list */
+};
+
+static struct usb_gadget_strings	stringtab = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings,
+};
+
+/*
+ * config descriptors are also handcrafted.  these must agree with code
+ * that sets configurations, and with code managing interface altsettings.
+ * other complexity may come from:
+ *
+ *  - high speed support, including "other speed config" rules
+ *  - multiple configurations
+ *  - interfaces with alternate settings
+ *  - embedded class or vendor-specific descriptors
+ *
+ * this handles high speed, and has a second config that could as easily
+ * have been an alternate interface setting.
+ *
+ * NOTE:  to demonstrate (and test) more USB capabilities, this driver
+ * should include an altsetting to test interrupt transfers, including
+ * high bandwidth modes at high speed.  (Maybe work like Intel's test
+ * device?)
+ */
+static int
+config_buf (enum usb_device_speed speed,
+		u8 *buf, u8 type, unsigned index)
+{
+	int		is_source_sink;
+	const unsigned	config_len = USB_DT_CONFIG_SIZE
+				+ USB_DT_INTERFACE_SIZE
+				+ 2 * USB_DT_ENDPOINT_SIZE;
+#ifdef HIGHSPEED
+	int		hs;
+#endif
+	/* two configurations will always be index 0 and index 1 */
+	if (index > 1)
+		return -EINVAL;
+	if (config_len > USB_BUFSIZ)
+		return -EDOM;
+	is_source_sink = loopdefault ? (index == 1) : (index == 0);
+
+	/* config (or other speed config) */
+	if (is_source_sink)
+		memcpy (buf, &source_sink_config, USB_DT_CONFIG_SIZE);
+	else
+		memcpy (buf, &loopback_config, USB_DT_CONFIG_SIZE);
+	buf [1] = type;
+	((struct usb_config_descriptor *) buf)->wTotalLength
+		= cpu_to_le16 (config_len);
+	buf += USB_DT_CONFIG_SIZE;
+
+	/* one interface */
+	if (is_source_sink)
+		memcpy (buf, &source_sink_intf, USB_DT_INTERFACE_SIZE);
+	else
+		memcpy (buf, &loopback_intf, USB_DT_INTERFACE_SIZE);
+	buf += USB_DT_INTERFACE_SIZE;
+
+	/* the endpoints in that interface (at that speed) */
+#ifdef HIGHSPEED
+	hs = (speed == USB_SPEED_HIGH);
+	if (type == USB_DT_OTHER_SPEED_CONFIG)
+		hs = !hs;
+	if (hs) {
+		memcpy (buf, &hs_source_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+		memcpy (buf, &hs_sink_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+	} else
+#endif
+	{
+		memcpy (buf, &fs_source_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+		memcpy (buf, &fs_sink_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+	}
+
+	return config_len;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *
+alloc_ep_req (struct usb_ep *ep, unsigned length)
+{
+	struct usb_request	*req;
+
+	req = usb_ep_alloc_request (ep, GFP_ATOMIC);
+	if (req) {
+		req->length = length;
+		req->buf = usb_ep_alloc_buffer (ep, length,
+				&req->dma, GFP_ATOMIC);
+		if (!req->buf) {
+			usb_ep_free_request (ep, req);
+			req = 0;
+		}
+	}
+	return req;
+}
+
+static void free_ep_req (struct usb_ep *ep, struct usb_request *req)
+{
+	if (req->buf)
+		usb_ep_free_buffer (ep, req->buf, req->dma, req->length);
+	usb_ep_free_request (ep, req);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* optionally require specific source/sink data patterns  */
+
+static inline int
+check_read_data (
+	struct zero_dev		*dev,
+	struct usb_ep		*ep,
+	struct usb_request	*req
+)
+{
+	int i;
+
+	for (i = 0; i < req->actual; i++) {
+		if (((u8 *)req->buf) [i] != 0) {
+			ERROR (dev, "nonzero OUT byte from host, "
+					"buf [%d] = %d\n",
+					i, ((u8 *)req->buf) [i]);
+			usb_ep_set_halt (ep);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static inline void
+reinit_write_data (
+	struct zero_dev		*dev,
+	struct usb_ep		*ep,
+	struct usb_request	*req
+)
+{
+	memset (req->buf, 0, req->length);
+}
+
+/* if there is only one request in the queue, there'll always be an
+ * irq delay between end of one request and start of the next.
+ * that prevents using hardware dma queues.
+ */
+static void source_sink_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	struct zero_dev	*dev = ep->driver_data;
+	int		status = req->status;
+
+	switch (status) {
+
+	case 0: 			/* normal completion? */
+		if (ep == dev->out_ep)
+			check_read_data (dev, ep, req);
+		else
+			reinit_write_data (dev, ep, req);
+		break;
+
+	/* this endpoint is normally active while we're configured */
+	case -ECONNRESET:		/* request dequeued */
+	case -ESHUTDOWN:		/* disconnect from host */
+		DEBUG (dev, "%s gone (%d), %d/%d\n", ep->name, status,
+				req->actual, req->length);
+		free_ep_req (ep, req);
+		return;
+
+	case -EOVERFLOW:		/* buffer overrun on read means that
+					 * we didn't provide a big enough
+					 * buffer.
+					 */
+	default:
+#if 1
+		DEBUG (dev, "%s complete --> %d, %d/%d\n", ep->name,
+				status, req->actual, req->length);
+#endif
+	case -EREMOTEIO:		/* short read */
+		break;
+	}
+
+	status = usb_ep_queue (ep, req, GFP_ATOMIC);
+	if (status) {
+		ERROR (dev, "kill %s:  resubmit %d bytes --> %d\n",
+				ep->name, req->length, status);
+		usb_ep_set_halt (ep);
+		/* FIXME recover later ... somehow */
+	}
+}
+
+static struct usb_request *
+source_sink_start_ep (struct usb_ep *ep, int gfp_flags)
+{
+	struct usb_request	*req;
+	int			status;
+
+	req = alloc_ep_req (ep, buflen);
+	if (!req)
+		return 0;
+
+	memset (req->buf, 0, req->length);
+	req->complete = source_sink_complete;
+
+	status = usb_ep_queue (ep, req, gfp_flags);
+	if (status) {
+		struct zero_dev	*dev = ep->driver_data;
+
+		ERROR (dev, "start %s --> %d\n", ep->name, status);
+		free_ep_req (ep, req);
+		req = 0;
+	}
+
+	return req;
+}
+
+static int
+set_source_sink_config (struct zero_dev *dev, int gfp_flags)
+{
+	int			result = 0;
+	struct usb_ep		*ep;
+	struct usb_gadget	*gadget = dev->gadget;
+
+	gadget_for_each_ep (ep, gadget) {
+		const struct usb_endpoint_descriptor	*d;
+
+		/* one endpoint writes (sources) zeroes in (to the host) */
+		if (strcmp (ep->name, EP_IN_NAME) == 0) {
+			d = ep_desc (gadget, &hs_source_desc, &fs_source_desc);
+			result = usb_ep_enable (ep, d);
+			if (result == 0) {
+				ep->driver_data = dev;
+				if (source_sink_start_ep (ep, gfp_flags) != 0) {
+					dev->in_ep = ep;
+					continue;
+				}
+				usb_ep_disable (ep);
+				result = -EIO;
+			}
+
+		/* one endpoint reads (sinks) anything out (from the host) */
+		} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
+			d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc);
+			result = usb_ep_enable (ep, d);
+			if (result == 0) {
+				ep->driver_data = dev;
+				if (source_sink_start_ep (ep, gfp_flags) != 0) {
+					dev->out_ep = ep;
+					continue;
+				}
+				usb_ep_disable (ep);
+				result = -EIO;
+			}
+
+		/* ignore any other endpoints */
+		} else
+			continue;
+
+		/* stop on error */
+		ERROR (dev, "can't start %s, result %d\n", ep->name, result);
+		break;
+	}
+	if (result == 0)
+		DEBUG (dev, "buflen %d\n", buflen);
+
+	/* caller is responsible for cleanup on error */
+	return result;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void loopback_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	struct zero_dev	*dev = ep->driver_data;
+	int		status = req->status;
+
+	switch (status) {
+
+	case 0: 			/* normal completion? */
+		if (ep == dev->out_ep) {
+			/* loop this OUT packet back IN to the host */
+			req->zero = (req->actual < req->length);
+			req->length = req->actual;
+			status = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC);
+			if (status == 0)
+				return;
+
+			/* "should never get here" */
+			ERROR (dev, "can't loop %s to %s: %d\n",
+				ep->name, dev->in_ep->name,
+				status);
+		}
+
+		/* queue the buffer for some later OUT packet */
+		req->length = buflen;
+		status = usb_ep_queue (dev->out_ep, req, GFP_ATOMIC);
+		if (status == 0)
+			return;
+
+		/* "should never get here" */
+		/* FALLTHROUGH */
+
+	default:
+		ERROR (dev, "%s loop complete --> %d, %d/%d\n", ep->name,
+				status, req->actual, req->length);
+		/* FALLTHROUGH */
+
+	/* NOTE:  since this driver doesn't maintain an explicit record
+	 * of requests it submitted (just maintains qlen count), we
+	 * rely on the hardware driver to clean up on disconnect or
+	 * endpoint disable.
+	 */
+	case -ESHUTDOWN:		/* disconnect from host */
+		free_ep_req (ep, req);
+		return;
+	}
+}
+
+static int
+set_loopback_config (struct zero_dev *dev, int gfp_flags)
+{
+	int			result = 0;
+	struct usb_ep		*ep;
+	struct usb_gadget	*gadget = dev->gadget;
+
+	gadget_for_each_ep (ep, gadget) {
+		const struct usb_endpoint_descriptor	*d;
+
+		/* one endpoint writes data back IN to the host */
+		if (strcmp (ep->name, EP_IN_NAME) == 0) {
+			d = ep_desc (gadget, &hs_source_desc, &fs_source_desc);
+			result = usb_ep_enable (ep, d);
+			if (result == 0) {
+				ep->driver_data = dev;
+				dev->in_ep = ep;
+				continue;
+			}
+
+		/* one endpoint just reads OUT packets */
+		} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
+			d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc);
+			result = usb_ep_enable (ep, d);
+			if (result == 0) {
+				ep->driver_data = dev;
+				dev->out_ep = ep;
+				continue;
+			}
+
+		/* ignore any other endpoints */
+		} else
+			continue;
+
+		/* stop on error */
+		ERROR (dev, "can't enable %s, result %d\n", ep->name, result);
+		break;
+	}
+
+	/* allocate a bunch of read buffers and queue them all at once.
+	 * we buffer at most 'qlen' transfers; fewer if any need more
+	 * than 'buflen' bytes each.
+	 */
+	if (result == 0) {
+		struct usb_request	*req;
+		unsigned		i;
+
+		ep = dev->out_ep;
+		for (i = 0; i < qlen && result == 0; i++) {
+			req = alloc_ep_req (ep, buflen);
+			if (req) {
+				req->complete = loopback_complete;
+				result = usb_ep_queue (ep, req, GFP_ATOMIC);
+				if (result)
+					DEBUG (dev, "%s queue req --> %d\n",
+							ep->name, result);
+			} else
+				result = -ENOMEM;
+		}
+	}
+	if (result == 0)
+		DEBUG (dev, "qlen %d, buflen %d\n", qlen, buflen);
+
+	/* caller is responsible for cleanup on error */
+	return result;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void zero_reset_config (struct zero_dev *dev)
+{
+	if (dev->config == 0)
+		return;
+
+	DEBUG (dev, "reset config\n");
+
+	/* just disable endpoints, forcing completion of pending i/o.
+	 * all our completion handlers free their requests in this case.
+	 */
+	if (dev->in_ep) {
+		usb_ep_disable (dev->in_ep);
+		dev->in_ep = 0;
+	}
+	if (dev->out_ep) {
+		usb_ep_disable (dev->out_ep);
+		dev->out_ep = 0;
+	}
+	dev->config = 0;
+}
+
+/* change our operational config.  this code must agree with the code
+ * that returns config descriptors, and altsetting code.
+ *
+ * it's also responsible for power management interactions. some
+ * configurations might not work with our current power sources.
+ *
+ * note that some device controller hardware will constrain what this
+ * code can do, perhaps by disallowing more than one configuration or
+ * by limiting configuration choices (like the pxa250).
+ */
+static int
+zero_set_config (struct zero_dev *dev, unsigned number, int gfp_flags)
+{
+	int			result = 0;
+	struct usb_gadget	*gadget = dev->gadget;
+
+	if (number == dev->config)
+		return 0;
+
+#ifdef CONFIG_USB_ZERO_SA1100
+	if (dev->config) {
+		/* tx fifo is full, but we can't clear it...*/
+		INFO (dev, "can't change configurations\n");
+		return -ESPIPE;
+	}
+#endif
+	zero_reset_config (dev);
+	hw_optimize (gadget);
+
+	switch (number) {
+	case CONFIG_SOURCE_SINK:
+		result = set_source_sink_config (dev, gfp_flags);
+		break;
+	case CONFIG_LOOPBACK:
+		result = set_loopback_config (dev, gfp_flags);
+		break;
+	default:
+		result = -EINVAL;
+		/* FALL THROUGH */
+	case 0:
+		return result;
+	}
+
+	if (!result && (!dev->in_ep || !dev->out_ep))
+		result = -ENODEV;
+	if (result)
+		zero_reset_config (dev);
+	else {
+		char *speed;
+
+		switch (gadget->speed) {
+		case USB_SPEED_LOW:	speed = "low"; break;
+		case USB_SPEED_FULL:	speed = "full"; break;
+		case USB_SPEED_HIGH:	speed = "high"; break;
+		default: 		speed = "?"; break;
+		}
+
+		dev->config = number;
+		INFO (dev, "%s speed config #%d: %s\n", speed, number,
+				(number == CONFIG_SOURCE_SINK)
+					? source_sink : loopback);
+	}
+	return result;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void zero_setup_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	if (req->status || req->actual != req->length)
+		DEBUG ((struct zero_dev *) ep->driver_data,
+				"setup complete --> %d, %d/%d\n",
+				req->status, req->actual, req->length);
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's
+ * not handled lower down, in hardware or the hardware driver (like
+ * device and endpoint feature flags, and their status).  It's all
+ * housekeeping for the gadget function we're implementing.  Most of
+ * the work is in config-specific setup.
+ */
+static int
+zero_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+	struct zero_dev		*dev = get_gadget_data (gadget);
+	struct usb_request	*req = dev->req;
+	int			value = -EOPNOTSUPP;
+
+	/* usually this stores reply data in the pre-allocated ep0 buffer,
+	 * but config change events will reconfigure hardware.
+	 */
+	switch (ctrl->bRequest) {
+
+	case USB_REQ_GET_DESCRIPTOR:
+		if (ctrl->bRequestType != USB_DIR_IN)
+			break;
+		switch (ctrl->wValue >> 8) {
+
+		case USB_DT_DEVICE:
+			value = min (ctrl->wLength, (u16) sizeof device_desc);
+			memcpy (req->buf, &device_desc, value);
+			break;
+#ifdef HIGHSPEED
+		case USB_DT_DEVICE_QUALIFIER:
+			value = min (ctrl->wLength, (u16) sizeof dev_qualifier);
+			memcpy (req->buf, &dev_qualifier, value);
+			break;
+
+		case USB_DT_OTHER_SPEED_CONFIG:
+			// FALLTHROUGH
+#endif /* HIGHSPEED */
+		case USB_DT_CONFIG:
+			value = config_buf (gadget->speed, req->buf,
+					ctrl->wValue >> 8,
+					ctrl->wValue & 0xff);
+			if (value >= 0)
+				value = min (ctrl->wLength, (u16) value);
+			break;
+
+		case USB_DT_STRING:
+			/* wIndex == language code.
+			 * this driver only handles one language, you can
+			 * add others even if they don't use iso8859/1
+			 */
+			value = usb_gadget_get_string (&stringtab,
+					ctrl->wValue & 0xff, req->buf);
+			if (value >= 0)
+				value = min (ctrl->wLength, (u16) value);
+			break;
+		}
+		break;
+
+	/* currently two configs, two speeds */
+	case USB_REQ_SET_CONFIGURATION:
+		if (ctrl->bRequestType != 0)
+			break;
+		spin_lock (&dev->lock);
+		/* change hardware configuration!
+		 * no response queued, just zero status == success
+		 */
+		value = zero_set_config (dev, ctrl->wValue, GFP_ATOMIC);
+		spin_unlock (&dev->lock);
+		break;
+	case USB_REQ_GET_CONFIGURATION:
+		if (ctrl->bRequestType != USB_DIR_IN)
+			break;
+		*(u8 *)req->buf = dev->config;
+		value = min (ctrl->wLength, (u16) 1);
+		break;
+
+	/* until we add altsetting support, or other interfaces,
+	 * only 0/0 are possible.
+	 */
+	case USB_REQ_SET_INTERFACE:
+		if (ctrl->bRequestType != USB_RECIP_INTERFACE)
+			break;
+		spin_lock (&dev->lock);
+		if (dev->config && ctrl->wIndex == 0 && ctrl->wValue == 0) {
+			u8		config = dev->config;
+
+			/* resets interface configuration, forgets about
+			 * previous transaction state (queued bufs, etc)
+			 * and re-inits endpoint state (toggle etc)
+			 * no response queued, just zero status == success.
+			 * if we had more than one interface we couldn't
+			 * use this "reset the config" shortcut.
+			 */
+			zero_reset_config (dev);
+			zero_set_config (dev, config, GFP_ATOMIC);
+			value = 0;
+		}
+		spin_unlock (&dev->lock);
+		break;
+	case USB_REQ_GET_INTERFACE:
+		if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
+			break;
+		if (!dev->config)
+			break;
+		if (ctrl->wIndex != 0) {
+			value = -EDOM;
+			break;
+		}
+		*(u8 *)req->buf = 0;
+		value = min (ctrl->wLength, (u16) 1);
+		break;
+
+	default:
+		VDEBUG (dev,
+			"unknown control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			ctrl->wValue, ctrl->wIndex, ctrl->wLength);
+	}
+
+	/* respond with data transfer before status phase? */
+	if (value > 0) {
+		req->length = value;
+		value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0) {
+			DEBUG (dev, "ep_queue --> %d\n", value);
+			req->status = 0;
+			zero_setup_complete (gadget->ep0, req);
+		}
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+static void
+zero_disconnect (struct usb_gadget *gadget)
+{
+	struct zero_dev		*dev = get_gadget_data (gadget);
+	unsigned long		flags;
+
+	spin_lock_irqsave (&dev->lock, flags);
+	zero_reset_config (dev);
+
+	/* a more significant application might have some non-usb
+	 * activities to quiesce here, saving resources like power
+	 * or pushing the notification up a network stack.
+	 */
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	/* next we may get setup() calls to enumerate new connections;
+	 * or an unbind() during shutdown (including removing module).
+	 */
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+zero_unbind (struct usb_gadget *gadget)
+{
+	struct zero_dev		*dev = get_gadget_data (gadget);
+
+	DEBUG (dev, "unbind\n");
+
+	/* we've already been disconnected ... no i/o is active */
+	if (dev->req)
+		free_ep_req (gadget->ep0, dev->req);
+	kfree (dev);
+	set_gadget_data (gadget, 0);
+}
+
+static int
+zero_bind (struct usb_gadget *gadget)
+{
+	struct zero_dev		*dev;
+
+	dev = kmalloc (sizeof *dev, SLAB_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	memset (dev, 0, sizeof *dev);
+	spin_lock_init (&dev->lock);
+	dev->gadget = gadget;
+	set_gadget_data (gadget, dev);
+
+	/* preallocate control response and buffer */
+	dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
+	if (!dev->req)
+		goto enomem;
+	dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ,
+				&dev->req->dma, GFP_KERNEL);
+	if (!dev->req->buf)
+		goto enomem;
+
+	dev->req->complete = zero_setup_complete;
+
+	gadget->ep0->driver_data = dev;
+
+	INFO (dev, "%s, version: " DRIVER_VERSION "\n", longname);
+	return 0;
+
+enomem:
+	zero_unbind (gadget);
+	return -ENOMEM;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_gadget_driver zero_driver = {
+#ifdef HIGHSPEED
+	.speed		= USB_SPEED_HIGH,
+#else
+	.speed		= USB_SPEED_FULL,
+#endif
+	.function	= (char *) longname,
+	.bind		= zero_bind,
+	.unbind		= zero_unbind,
+
+	.setup		= zero_setup,
+	.disconnect	= zero_disconnect,
+
+	.driver 	= {
+		.name		= (char *) shortname,
+		// .shutdown = ...
+		// .suspend = ...
+		// .resume = ...
+	},
+};
+
+MODULE_AUTHOR ("David Brownell");
+MODULE_LICENSE ("Dual BSD/GPL");
+
+
+static int __init init (void)
+{
+	/* a real value would likely come through some id prom
+	 * or module option.  this one takes at least two packets.
+	 */
+	strncpy (serial, "0123456789.0123456789.0123456789", sizeof serial);
+	serial [sizeof serial - 1] = 0;
+
+	return usb_gadget_register_driver (&zero_driver);
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+	usb_gadget_unregister_driver (&zero_driver);
+}
+module_exit (cleanup);
+
diff -urN linux-2.5.69-bk5/fs/bio.c linux-2.5.69-bk6/fs/bio.c
--- linux-2.5.69-bk5/fs/bio.c	2003-05-04 16:53:32.000000000 -0700
+++ linux-2.5.69-bk6/fs/bio.c	2003-05-11 04:43:34.000000000 -0700
@@ -728,6 +728,7 @@
 	}
 
 	bio->bi_size -= bytes_done;
+	bio->bi_sector += (bytes_done >> 9);
 
 	if (bio->bi_end_io)
 		bio->bi_end_io(bio, bytes_done, error);
diff -urN linux-2.5.69-bk5/include/linux/blkdev.h linux-2.5.69-bk6/include/linux/blkdev.h
--- linux-2.5.69-bk5/include/linux/blkdev.h	2003-05-04 16:53:08.000000000 -0700
+++ linux-2.5.69-bk6/include/linux/blkdev.h	2003-05-11 04:43:34.000000000 -0700
@@ -10,6 +10,7 @@
 #include <linux/pagemap.h>
 #include <linux/backing-dev.h>
 #include <linux/wait.h>
+#include <linux/mempool.h>
 
 #include <asm/scatterlist.h>
 
@@ -18,10 +19,12 @@
 struct elevator_s;
 typedef struct elevator_s elevator_t;
 
+#define BLKDEV_MIN_RQ	4
+#define BLKDEV_MAX_RQ	128
+
 struct request_list {
-	unsigned int count;
-	struct list_head free;
-	wait_queue_head_t wait;
+	int count[2];
+	mempool_t *rq_pool;
 };
 
 /*
@@ -180,7 +183,7 @@
 	/*
 	 * the queue request freelist, one for reads and one for writes
 	 */
-	struct request_list	rq[2];
+	struct request_list	rq;
 
 	request_fn_proc		*request_fn;
 	merge_request_fn	*back_merge_fn;
@@ -329,7 +332,6 @@
 extern void blk_attempt_remerge(request_queue_t *, struct request *);
 extern void __blk_attempt_remerge(request_queue_t *, struct request *);
 extern struct request *blk_get_request(request_queue_t *, int, int);
-extern struct request *__blk_get_request(request_queue_t *, int);
 extern void blk_put_request(struct request *);
 extern void blk_insert_request(request_queue_t *, struct request *, int, void *);
 extern void blk_plug_device(request_queue_t *);
diff -urN linux-2.5.69-bk5/include/linux/cpu.h linux-2.5.69-bk6/include/linux/cpu.h
--- linux-2.5.69-bk5/include/linux/cpu.h	2003-05-04 16:53:07.000000000 -0700
+++ linux-2.5.69-bk6/include/linux/cpu.h	2003-05-11 04:43:34.000000000 -0700
@@ -29,6 +29,7 @@
 };
 
 extern int register_cpu(struct cpu *, int, struct node *);
+extern struct class cpu_class;
 
 /* Stop CPUs going up and down. */
 extern struct semaphore cpucontrol;
diff -urN linux-2.5.69-bk5/include/linux/elevator.h linux-2.5.69-bk6/include/linux/elevator.h
--- linux-2.5.69-bk5/include/linux/elevator.h	2003-05-04 16:52:49.000000000 -0700
+++ linux-2.5.69-bk6/include/linux/elevator.h	2003-05-11 04:43:34.000000000 -0700
@@ -15,6 +15,8 @@
 typedef void (elevator_remove_req_fn) (request_queue_t *, struct request *);
 typedef struct request *(elevator_request_list_fn) (request_queue_t *, struct request *);
 typedef struct list_head *(elevator_get_sort_head_fn) (request_queue_t *, struct request *);
+typedef int (elevator_set_req_fn) (request_queue_t *, struct request *, int);
+typedef void (elevator_put_req_fn) (request_queue_t *, struct request *);
 
 typedef int (elevator_init_fn) (request_queue_t *, elevator_t *);
 typedef void (elevator_exit_fn) (request_queue_t *, elevator_t *);
@@ -34,6 +36,9 @@
 	elevator_request_list_fn *elevator_former_req_fn;
 	elevator_request_list_fn *elevator_latter_req_fn;
 
+	elevator_set_req_fn *elevator_set_req_fn;
+	elevator_put_req_fn *elevator_put_req_fn;
+
 	elevator_init_fn *elevator_init_fn;
 	elevator_exit_fn *elevator_exit_fn;
 
@@ -59,6 +64,8 @@
 extern struct request *elv_latter_request(request_queue_t *, struct request *);
 extern int elv_register_queue(struct gendisk *);
 extern void elv_unregister_queue(struct gendisk *);
+extern int elv_set_request(request_queue_t *, struct request *, int);
+extern void elv_put_request(request_queue_t *, struct request *);
 
 #define __elv_add_request_pos(q, rq, pos)	\
 	(q)->elevator.elevator_add_req_fn((q), (rq), (pos))
diff -urN linux-2.5.69-bk5/include/linux/i2c.h linux-2.5.69-bk6/include/linux/i2c.h
--- linux-2.5.69-bk5/include/linux/i2c.h	2003-05-11 04:43:30.000000000 -0700
+++ linux-2.5.69-bk6/include/linux/i2c.h	2003-05-11 04:43:34.000000000 -0700
@@ -240,7 +240,8 @@
 
 	int timeout;
 	int retries;
-	struct device dev;	/* the adapter device */
+	struct device dev;		/* the adapter device */
+	struct class_device class_dev;	/* the class device */
 
 #ifdef CONFIG_PROC_FS 
 	/* No need to set this when you initialize the adapter          */
@@ -280,10 +281,12 @@
 						/* Must equal I2C_M_TEN below */
 
 /* i2c adapter classes (bitmask) */
-#define I2C_ADAP_CLASS_SMBUS      (1<<0)        /* lm_sensors, ... */
-#define I2C_ADAP_CLASS_TV_ANALOG  (1<<1)        /* bttv + friends */
-#define I2C_ADAP_CLASS_TV_DIGINAL (1<<2)        /* dbv cards */
-#define I2C_ADAP_CLASS_DDC        (1<<3)        /* i2c-matroxfb ? */
+#define I2C_ADAP_CLASS_SMBUS		(1<<0)	/* lm_sensors, ... */
+#define I2C_ADAP_CLASS_TV_ANALOG	(1<<1)	/* bttv + friends */
+#define I2C_ADAP_CLASS_TV_DIGITAL	(1<<2)	/* dbv cards */
+#define I2C_ADAP_CLASS_DDC		(1<<3)	/* i2c-matroxfb ? */
+#define I2C_ADAP_CLASS_CAM_ANALOG	(1<<4)	/* camera with analog CCD */
+#define I2C_ADAP_CLASS_CAM_DIGITAL	(1<<5)	/* most webcams */
 
 /* i2c_client_address_data is the struct for holding default client
  * addresses for a driver and for the parameters supplied on the
diff -urN linux-2.5.69-bk5/include/linux/usb_gadget.h linux-2.5.69-bk6/include/linux/usb_gadget.h
--- linux-2.5.69-bk5/include/linux/usb_gadget.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.69-bk6/include/linux/usb_gadget.h	2003-05-11 04:43:34.000000000 -0700
@@ -0,0 +1,696 @@
+/*
+ * <linux/usb_gadget.h>
+ *
+ * We call the USB code inside a Linux-based peripheral device a "gadget"
+ * driver, except for the hardware-specific bus glue.  One USB host can
+ * master many USB gadgets, but the gadgets are only slaved to one host.
+ *
+ *
+ * (c) Copyright 2002-2003 by David Brownell
+ * All Rights Reserved.
+ *
+ * This software is licensed under the GNU GPL version 2.
+ *
+ * ALTERNATIVELY, the kernel API documentation which is included in this
+ * software may also be licenced under the "GNU Free Documentation
+ * License" (version 1.2 or, at your choice, any later version), when
+ * used as part of the "USB Gadget API for Linux" documentation.
+ */
+
+#ifndef __LINUX_USB_GADGET_H
+#define __LINUX_USB_GADGET_H
+
+#ifdef __KERNEL__
+
+struct usb_ep;
+
+/**
+ * struct usb_request - describes one i/o request
+ * @buf: Buffer used for data.  Always provide this; some controllers
+ * 	only use PIO, or don't use DMA for some endpoints.
+ * @dma: DMA address corresponding to 'buf'.  If you don't set this
+ * 	field, and the usb controller needs one, it is responsible
+ * 	for mapping and unmapping the buffer.
+ * @length: Length of that data
+ * @no_interrupt: If true, hints that no completion irq is needed.
+ *	Helpful sometimes with deep request queues.
+ * @zero: If true, when writing data, makes the last packet be "short"
+ *     by adding a zero length packet as needed;
+ * @short_not_ok: When reading data, makes short packets be
+ *     treated as errors (queue stops advancing till cleanup).
+ * @complete: Function called when request completes
+ * @context: For use by the completion callback
+ * @list: For use by the gadget driver.
+ * @status: Reports completion code, zero or a negative errno.
+ * 	Normally, faults block the transfer queue from advancing until
+ * 	the completion callback returns.
+ * 	Code "-ESHUTDOWN" indicates completion caused by device disconnect,
+ * 	or when the driver disabled the endpoint.
+ * @actual: Reports actual bytes transferred.  For reads (OUT
+ * 	transfers) this may be less than the requested length.  If the
+ * 	short_not_ok flag is set, short reads are treated as errors
+ * 	even when status otherwise indicates successful completion.
+ * 	Note that for writes (IN transfers) the data bytes may still
+ * 	reside in a device-side FIFO.
+ *
+ * These are allocated/freed through the endpoint they're used with.  The
+ * hardware's driver can add extra per-request data to the memory it returns,
+ * which often avoids separate memory allocations (potential failures),
+ * later when the request is queued.
+ *
+ * Request flags affect request handling, such as whether a zero length
+ * packet is written (the "zero" flag), whether a short read should be
+ * treated as an error (blocking request queue advance, the "short_not_ok"
+ * flag), or hinting that an interrupt is not required (the "no_interrupt"
+ * flag, for use with deep request queues).
+ *
+ * Bulk endpoints can use any size buffers, and can also be used for interrupt
+ * transfers. interrupt-only endpoints can be much less functional.
+ */
+	// NOTE this is analagous to 'struct urb' on the host side,
+	// except that it's thinner and promotes more pre-allocation.
+	//
+	// ISSUE should this be allocated through the device?
+
+struct usb_request {
+	void			*buf;
+	unsigned		length;
+	dma_addr_t		dma;
+
+	unsigned		no_interrupt : 1,
+				zero : 1,
+				short_not_ok : 1;
+
+	void			(*complete)(struct usb_ep *ep,
+					struct usb_request *req);
+	void			*context;
+	struct list_head	list;
+
+	int			status;
+	unsigned		actual;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* endpoint-specific parts of the api to the usb controller hardware.
+ * unlike the urb model, (de)multiplexing layers are not required.
+ * (so this api could slash overhead if used on the host side...)
+ *
+ * note that device side usb controllers commonly differ in how many
+ * endpoints they support, as well as their capabilities.
+ */
+struct usb_ep_ops {
+	int (*enable) (struct usb_ep *ep,
+		const struct usb_endpoint_descriptor *desc);
+	int (*disable) (struct usb_ep *ep);
+
+	struct usb_request *(*alloc_request) (struct usb_ep *ep,
+		int gfp_flags);
+	void (*free_request) (struct usb_ep *ep, struct usb_request *req);
+
+	void *(*alloc_buffer) (struct usb_ep *ep, unsigned bytes,
+		dma_addr_t *dma, int gfp_flags);
+	void (*free_buffer) (struct usb_ep *ep, void *buf, dma_addr_t dma,
+		unsigned bytes);
+	// NOTE:  on 2.5, drivers may also use dma_map() and
+	// dma_sync_single() to manage dma overhead. 
+
+	int (*queue) (struct usb_ep *ep, struct usb_request *req,
+		int gfp_flags);
+	int (*dequeue) (struct usb_ep *ep, struct usb_request *req);
+
+	int (*set_halt) (struct usb_ep *ep, int value);
+	int (*fifo_status) (struct usb_ep *ep);
+	void (*fifo_flush) (struct usb_ep *ep);
+};
+
+/**
+ * struct usb_ep - device side representation of USB endpoint
+ * @name:identifier for the endpoint, such as "ep-a" or "ep9in-bulk"
+ * @ep_list:the gadget's ep_list holds all of its endpoints
+ * @maxpacket:the maximum packet size used on this endpoint, as
+ * 	configured when the endpoint was enabled.
+ * @driver_data:for use by the gadget driver.  all other fields are
+ * 	read-only to gadget drivers.
+ *
+ * the bus controller driver lists all the general purpose endpoints in
+ * gadget->ep_list.  the control endpoint (gadget->ep0) is not in that list,
+ * and is accessed only in response to a driver setup() callback.
+ */
+struct usb_ep {
+	void			*driver_data;
+
+	const char		*name;
+	const struct usb_ep_ops	*ops;
+	struct list_head	ep_list;
+	unsigned		maxpacket : 16;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_ep_enable - configure endpoint, making it usable
+ * @ep:the endpoint being configured.  may not be the endpoint named "ep0".
+ * 	drivers discover endpoints through the ep_list of a usb_gadget.
+ * @desc:descriptor for desired behavior.  caller guarantees this pointer
+ * 	remains valid until the endpoint is disabled; the data byte order
+ * 	is little-endian (usb-standard).
+ *
+ * when configurations are set, or when interface settings change, the driver
+ * will enable or disable the relevant endpoints.  while it is enabled, an
+ * endpoint may be used for i/o until the driver receives a disconnect() from
+ * the host or until the endpoint is disabled.
+ *
+ * the ep0 implementation (which calls this routine) must ensure that the
+ * hardware capabilities of each endpoint match the descriptor provided
+ * for it.  for example, an endpoint named "ep2in-bulk" would be usable
+ * for interrupt transfers as well as bulk, but it likely couldn't be used
+ * for iso transfers or for endpoint 14.  some endpoints are fully
+ * configurable, with more generic names like "ep-a".  (remember that for
+ * USB, "in" means "towards the USB master".)
+ *
+ * returns zero, or a negative error code.
+ */
+static inline int
+usb_ep_enable (struct usb_ep *ep, const struct usb_endpoint_descriptor *desc)
+{
+	return ep->ops->enable (ep, desc);
+}
+
+/**
+ * usb_ep_disable - endpoint is no longer usable
+ * @ep:the endpoint being unconfigured.  may not be the endpoint named "ep0".
+ *
+ * no other task may be using this endpoint when this is called.
+ * any pending and uncompleted requests will complete with status
+ * indicating disconnect (-ESHUTDOWN) before this call returns.
+ * gadget drivers must call usb_ep_enable() again before queueing
+ * requests to the endpoint.
+ *
+ * returns zero, or a negative error code.
+ */
+static inline int
+usb_ep_disable (struct usb_ep *ep)
+{
+	return ep->ops->disable (ep);
+}
+
+/**
+ * usb_ep_alloc_request - allocate a request object to use with this endpoint
+ * @ep:the endpoint to be used with with the request
+ * @gfp_flags:GFP_* flags to use
+ *
+ * Request objects must be allocated with this call, since they normally
+ * need controller-specific setup and may even need endpoint-specific
+ * resources such as allocation of DMA descriptors.
+ * Requests may be submitted with usb_ep_queue(), and receive a single
+ * completion callback.  Free requests with usb_ep_free_request(), when
+ * they are no longer needed.
+ *
+ * Returns the request, or null if one could not be allocated.
+ */
+static inline struct usb_request *
+usb_ep_alloc_request (struct usb_ep *ep, int gfp_flags)
+{
+	return ep->ops->alloc_request (ep, gfp_flags);
+}
+
+/**
+ * usb_ep_free_request - frees a request object
+ * @ep:the endpoint associated with the request
+ * @req:the request being freed
+ *
+ * Reverses the effect of usb_ep_alloc_request().
+ * Caller guarantees the request is not queued, and that it will
+ * no longer be requeued (or otherwise used).
+ */
+static inline void
+usb_ep_free_request (struct usb_ep *ep, struct usb_request *req)
+{
+	ep->ops->free_request (ep, req);
+}
+
+/**
+ * usb_ep_alloc_buffer - allocate an I/O buffer
+ * @ep:the endpoint associated with the buffer
+ * @len:length of the desired buffer
+ * @dma:pointer to the buffer's DMA address; must be valid
+ * @gfp_flags:GFP_* flags to use
+ *
+ * Returns a new buffer, or null if one could not be allocated.
+ * The buffer is suitably aligned for dma, if that endpoint uses DMA,
+ * and the caller won't have to care about dma-inconsistency
+ * or any hidden "bounce buffer" mechanism.  No additional per-request
+ * DMA mapping will be required for such buffers.
+ * Free it later with usb_ep_free_buffer().
+ *
+ * You don't need to use this call to allocate I/O buffers unless you
+ * want to make sure drivers don't incur costs for such "bounce buffer"
+ * copies or per-request DMA mappings.
+ */
+static inline void *
+usb_ep_alloc_buffer (struct usb_ep *ep, unsigned len, dma_addr_t *dma,
+	int gfp_flags)
+{
+	return ep->ops->alloc_buffer (ep, len, dma, gfp_flags);
+}
+
+/**
+ * usb_ep_free_buffer - frees an i/o buffer
+ * @ep:the endpoint associated with the buffer
+ * @buf:CPU view address of the buffer
+ * @dma:the buffer's DMA address
+ * @len:length of the buffer
+ *
+ * reverses the effect of usb_ep_alloc_buffer().
+ * caller guarantees the buffer will no longer be accessed
+ */
+static inline void
+usb_ep_free_buffer (struct usb_ep *ep, void *buf, dma_addr_t dma, unsigned len)
+{
+	ep->ops->free_buffer (ep, buf, dma, len);
+}
+
+/**
+ * usb_ep_queue - queues (submits) an I/O request to an endpoint.
+ * @ep:the endpoint associated with the request
+ * @req:the request being submitted
+ * @gfp_flags: GFP_* flags to use in case the lower level driver couldn't
+ * 	pre-allocate all necessary memory with the request.
+ *
+ * This tells the device controller to perform the specified request through
+ * that endpoint (reading or writing a buffer).  When the request completes,
+ * including being canceled by usb_ep_dequeue(), the request's completion
+ * routine is called to return the request to the driver.  Any endpoint
+ * (except control endpoints like ep0) may have more than one transfer
+ * request queued; they complete in FIFO order.  Once a gadget driver
+ * submits a request, that request may not be examined or modified until it
+ * is given back to that driver through the completion callback.
+ *
+ * Each request is turned into one or more packets.  The controller driver
+ * never merges adjacent requests into the same packet.  OUT transfers
+ * will sometimes use data that's already buffered in the hardware.
+ *
+ * Bulk endpoints can queue any amount of data; the transfer is packetized
+ * automatically.  The last packet will be short if the request doesn't fill it
+ * out completely.  Zero length packets (ZLPs) should be avoided in portable
+ * protocols since not all usb hardware can successfully handle zero length
+ * packets.  (ZLPs may be explicitly written, and may be implicitly written if
+ * the request 'zero' flag is set.)  Bulk endpoints may also be used
+ * for interrupt transfers; but the reverse is not true, and some endpoints
+ * won't support every interrupt transfer.  (Such as 768 byte packets.)
+ *
+ * Interrupt-only endpoints are less functional than bulk endpoints, for
+ * example by not supporting queueing or not handling buffers that are
+ * larger than the endpoint's maxpacket size.  They may also treat data
+ * toggle differently.
+ *
+ * Control endpoints ... after getting a setup() callback, the driver queues
+ * one response (optional if it would be zero length).  That enables the
+ * status ack, after transfering data as specified in the response.  Setup
+ * functions may return negative error codes to generate protocol stalls.
+ *
+ * For periodic endpoints, like interrupt or isochronous ones, the usb host
+ * arranges to poll once per interval, and the gadget driver usually will
+ * have queued some data to transfer at that time.
+ *
+ * Returns zero, or a negative error code.  Endpoints that are not enabled,
+ * or which are enabled but halted, report errors; errors will also be
+ * reported when the usb peripheral is disconnected.
+ */
+static inline int
+usb_ep_queue (struct usb_ep *ep, struct usb_request *req, int gfp_flags)
+{
+	return ep->ops->queue (ep, req, gfp_flags);
+}
+
+/**
+ * usb_ep_dequeue - dequeues (cancels, unlinks) an I/O request from an endpoint
+ * @ep:the endpoint associated with the request
+ * @req:the request being canceled
+ *
+ * if the request is still active on the endpoint, it is dequeued and its
+ * completion routine is called (with status -ECONNRESET); else a negative
+ * error code is returned.
+ *
+ * note that some hardware can't clear out write fifos (to unlink the request
+ * at the head of the queue) except as part of disconnecting from usb.  such
+ * restrictions prevent drivers from supporting configuration changes,
+ * even to configuration zero (a "chapter 9" requirement).
+ */
+static inline int usb_ep_dequeue (struct usb_ep *ep, struct usb_request *req)
+{
+	return ep->ops->dequeue (ep, req);
+}
+
+/**
+ * usb_ep_set_halt - sets the endpoint halt feature.
+ * @ep: the non-isochronous endpoint being stalled
+ *
+ * Use this to stall an endpoint, perhaps as an error report.
+ * Except for control endpoints,
+ * the endpoint stays halted (will not stream any data) until the host
+ * clears this feature; drivers may need to empty the endpoint's request
+ * queue first, to make sure no inappropriate transfers happen.
+ *
+ * Returns zero, or a negative error code.  On success, this call sets
+ * underlying hardware state that blocks data transfers.
+ */
+static inline int
+usb_ep_set_halt (struct usb_ep *ep)
+{
+	return ep->ops->set_halt (ep, 1);
+}
+
+/**
+ * usb_ep_clear_halt - clears endpoint halt, and resets toggle
+ * @ep:the bulk or interrupt endpoint being reset
+ *
+ * use this when responding to the standard usb "set interface" request,
+ * for endpoints that aren't reconfigured, after clearing any other state
+ * in the endpoint's i/o queue.
+ *
+ * returns zero, or a negative error code.  on success, this call clears
+ * the underlying hardware state reflecting endpoint halt and data toggle.
+ */
+static inline int
+usb_ep_clear_halt (struct usb_ep *ep)
+{
+	return ep->ops->set_halt (ep, 0);
+}
+
+/**
+ * usb_ep_fifo_status - returns number of bytes in fifo, or error
+ * @ep: the endpoint whose fifo status is being checked.
+ *
+ * FIFO endpoints may have "unclaimed data" in them in certain cases,
+ * such as after aborted transfers.  Hosts may not have collected all
+ * the IN data written by the gadget driver, as reported by a request
+ * completion.  The gadget driver may not have collected all the data
+ * written OUT to it by the host.  Drivers that need precise handling for
+ * fault reporting or recovery may need to use this call.
+ *
+ * This returns the number of such bytes in the fifo, or a negative
+ * errno if the endpoint doesn't use a FIFO or doesn't support such
+ * precise handling.
+ */
+static inline int
+usb_ep_fifo_status (struct usb_ep *ep)
+{
+	if (ep->ops->fifo_status)
+		return ep->ops->fifo_status (ep);
+	else
+		return -EOPNOTSUPP;
+}
+
+/**
+ * usb_ep_fifo_flush - flushes contents of a fifo
+ * @ep: the endpoint whose fifo is being flushed.
+ *
+ * This call may be used to flush the "unclaimed data" that may exist in
+ * an endpoint fifo after abnormal transaction terminations.  The call
+ * must never be used except when endpoint is not being used for any
+ * protocol translation.
+ */
+static inline void
+usb_ep_fifo_flush (struct usb_ep *ep)
+{
+	if (ep->ops->fifo_flush)
+		ep->ops->fifo_flush (ep);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+struct usb_gadget;
+
+/* the rest of the api to the controller hardware: device operations,
+ * which don't involve endpoints (or i/o).
+ */
+struct usb_gadget_ops {
+	int	(*get_frame)(struct usb_gadget *);
+	int	(*wakeup)(struct usb_gadget *);
+	int	(*set_selfpowered) (struct usb_gadget *, int value);
+	int	(*ioctl)(struct usb_gadget *,
+				unsigned code, unsigned long param);
+};
+
+/**
+ * struct usb_gadget - represents a usb slave device
+ * @ep0: Endpoint zero, used when reading or writing responses to
+ * 	driver setup() requests
+ * @ep_list: List of other endpoints supported by the device.
+ * @speed: Speed of current connection to USB host.
+ * @name: Identifies the controller hardware type.  Used in diagnostics
+ * 	and sometimes configuration.
+ *
+ * Gadgets have a mostly-portable "gadget driver" implementing device
+ * functions, handling all usb configurations and interfaces.  They
+ * also have a hardware-specific driver (accessed through ops vectors),
+ * which insulates the gadget driver from hardware details and packages
+ * the hardware endpoints through generic i/o queues.
+ *
+ * Except for the driver data, all fields in this structure are
+ * read-only to the gadget driver.  That driver data is part of the
+ * "driver model" infrastructure in 2.5 (and later) kernels, and for
+ * earlier systems is grouped in a similar structure that's not known
+ * to the rest of the kernel.
+ */
+struct usb_gadget {
+	/* readonly to gadget driver */
+	const struct usb_gadget_ops	*ops;
+	struct usb_ep			*ep0;
+	struct list_head		ep_list;	/* of usb_ep */
+	enum usb_device_speed		speed;
+	const char			*name;
+
+	/* use this to allocate dma-coherent buffers or set up
+	 * dma mappings.  or print diagnostics, etc.
+	 */
+	struct device			dev;
+};
+
+static inline void set_gadget_data (struct usb_gadget *gadget, void *data)
+	{ dev_set_drvdata (&gadget->dev, data); }
+static inline void *get_gadget_data (struct usb_gadget *gadget)
+	{ return dev_get_drvdata (&gadget->dev); }
+
+/* iterates the non-control endpoints; 'tmp' is a struct usb_ep pointer */
+#define gadget_for_each_ep(tmp,gadget) \
+	list_for_each_entry(tmp, &(gadget)->ep_list, ep_list)
+
+
+/**
+ * usb_gadget_frame_number - returns the current frame number
+ * @gadget: controller that reports the frame number
+ *
+ * Returns the usb frame number, normally eleven bits from a SOF packet,
+ * or negative errno if this device doesn't support this capability.
+ */
+static inline int usb_gadget_frame_number (struct usb_gadget *gadget)
+{
+	return gadget->ops->get_frame (gadget);
+}
+
+/**
+ * usb_gadget_wakeup - tries to wake up the host connected to this gadget
+ * @gadget: controller used to wake up the host
+ *
+ * Returns zero on success, else negative error code if the hardware
+ * doesn't support such attempts, or its support has not been enabled
+ * by the usb host.  Drivers must return device descriptors that report
+ * their ability to support this, or hosts won't enable it.
+ */
+static inline int usb_gadget_wakeup (struct usb_gadget *gadget)
+{
+	if (!gadget->ops->wakeup)
+		return -EOPNOTSUPP;
+	return gadget->ops->wakeup (gadget);
+}
+
+/**
+ * usb_gadget_set_selfpowered - sets the device selfpowered feature.
+ * @gadget:the device being declared as self-powered
+ *
+ * this affects the device status reported by the hardware driver
+ * to reflect that it now has a local power supply.
+ *
+ * returns zero on success, else negative errno.
+ */
+static inline int
+usb_gadget_set_selfpowered (struct usb_gadget *gadget)
+{
+	if (!gadget->ops->set_selfpowered)
+		return -EOPNOTSUPP;
+	return gadget->ops->set_selfpowered (gadget, 1);
+}
+
+/**
+ * usb_gadget_clear_selfpowered - clear the device selfpowered feature.
+ * @gadget:the device being declared as bus-powered
+ *
+ * this affects the device status reported by the hardware driver.
+ * some hardware may not support bus-powered operation, in which
+ * case this feature's value can never change.
+ *
+ * returns zero on success, else negative errno.
+ */
+static inline int
+usb_gadget_clear_selfpowered (struct usb_gadget *gadget)
+{
+	if (!gadget->ops->set_selfpowered)
+		return -EOPNOTSUPP;
+	return gadget->ops->set_selfpowered (gadget, 0);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * struct usb_gadget_driver - driver for usb 'slave' devices
+ * @function: String describing the gadget's function
+ * @speed: Highest speed the driver handles.
+ * @bind: Invoked when the driver is bound to a gadget, usually
+ * 	after registering the driver.
+ * 	At that point, ep0 is fully initialized, and ep_list holds
+ * 	the currently-available endpoints.
+ * 	Called in a context that permits sleeping.
+ * @setup: Invoked for ep0 control requests that aren't handled by
+ * 	the hardware level driver. Most calls must be handled by
+ * 	the gadget driver, including descriptor and configuration
+ * 	management.  The 16 bit members of the setup data are in
+ * 	cpu order. Called in_interrupt; this may not sleep.  Driver
+ *	queues a response to ep0, or returns negative to stall.
+ * @disconnect: Invoked after all transfers have been stopped,
+ * 	when the host is disconnected.  May be called in_interrupt; this
+ * 	may not sleep.
+ * @unbind: Invoked when the driver is unbound from a gadget,
+ * 	usually from rmmod (after a disconnect is reported).
+ * 	Called in a context that permits sleeping.
+ * @suspend: Invoked on USB suspend.  May be called in_interrupt.
+ * @resume: Invoked on USB resume.  May be called in_interrupt.
+ *
+ * Devices are disabled till a gadget driver successfully bind()s, which
+ * means the driver will handle setup() requests needed to enumerate (and
+ * meet "chapter 9" requirements) then do some useful work.
+ *
+ * Drivers use hardware-specific knowledge to configure the usb hardware.
+ * endpoint addressing is only one of several hardware characteristics that
+ * are in descriptors the ep0 implementation returns from setup() calls.
+ *
+ * Except for ep0 implementation, most driver code shouldn't need change to
+ * run on top of different usb controllers.  It'll use endpoints set up by
+ * that ep0 implementation.
+ *
+ * The usb controller driver handles a few standard usb requests.  Those
+ * include set_address, and feature flags for devices, interfaces, and
+ * endpoints (the get_status, set_feature, and clear_feature requests).
+ *
+ * Accordingly, the driver's setup() callback must always implement all
+ * get_descriptor requests, returning at least a device descriptor and
+ * a configuration descriptor.  Drivers must make sure the endpoint
+ * descriptors match any hardware constraints. Some hardware also constrains
+ * other descriptors. (The pxa250 allows only configurations 1, 2, or 3).
+ *
+ * The driver's setup() callback must also implement set_configuration,
+ * and should also implement set_interface, get_configuration, and
+ * get_interface.  Setting a configuration (or interface) is where
+ * endpoints should be activated or (config 0) shut down.
+ *
+ * (Note that only the default control endpoint is supported.  Neither
+ * hosts nor devices generally support control traffic except to ep0.)
+ *
+ * Most devices will ignore USB suspend/resume operations, and so will
+ * not provide those callbacks.  However, some may need to change modes
+ * when the host is not longer directing those activities.  For example,
+ * local controls (buttons, dials, etc) may need to be re-enabled since
+ * the (remote) host can't do that any longer. 
+ */
+struct usb_gadget_driver {
+	char			*function;
+	enum usb_device_speed	speed;
+	int			(*bind)(struct usb_gadget *);
+	void			(*unbind)(struct usb_gadget *);
+	int			(*setup)(struct usb_gadget *,
+					const struct usb_ctrlrequest *);
+	void			(*disconnect)(struct usb_gadget *);
+	void			(*suspend)(struct usb_gadget *);
+	void			(*resume)(struct usb_gadget *);
+
+	// FIXME support safe rmmod
+	struct device_driver	driver;
+};
+
+
+
+/*-------------------------------------------------------------------------*/
+
+/* driver modules register and unregister, as usual.
+ * these calls must be made in a context that can sleep.
+ *
+ * these will usually be implemented directly by the hardware-dependent
+ * usb bus interface driver, which will only support a single driver.
+ */
+
+/**
+ * usb_gadget_register_driver - register a gadget driver
+ * @driver:the driver being registered
+ *
+ * Call this in your gadget driver's module initialization function,
+ * to tell the underlying usb controller driver about your driver.
+ * The driver's bind() function will be called to bind it to a
+ * gadget.  This function must be called in a context that can sleep.
+ */
+int usb_gadget_register_driver (struct usb_gadget_driver *driver);
+
+/**
+ * usb_gadget_unregister_driver - unregister a gadget driver
+ * @driver:the driver being unregistered
+ *
+ * Call this in your gadget driver's module cleanup function,
+ * to tell the underlying usb controller that your driver is
+ * going away.  If the controller is connected to a USB host,
+ * it will first disconnect().  The driver is also requested
+ * to unbind() and clean up any device state, before this procedure
+ * finally returns.
+ * This function must be called in a context that can sleep.
+ */
+int usb_gadget_unregister_driver (struct usb_gadget_driver *driver);
+
+/*-------------------------------------------------------------------------*/
+
+/* utility to simplify dealing with string descriptors */
+
+/**
+ * struct usb_string - wraps a C string and its USB id
+ * @id:the (nonzero) ID for this string
+ * @s:the string, in ISO-8859/1 characters
+ *
+ * If you're using usb_gadget_get_string(), use this to wrap a string
+ * together with its ID.
+ */
+struct usb_string {
+	u8			id;
+	const char		*s;
+};
+
+/**
+ * struct usb_gadget_strings - a set of USB strings in a given language
+ * @language:identifies the strings' language (0x0409 for en-us)
+ * @strings:array of strings with their ids
+ *
+ * If you're using usb_gadget_get_string(), use this to wrap all the
+ * strings for a given language.
+ */
+struct usb_gadget_strings {
+	u16			language;	/* 0x0409 for en-us */
+	struct usb_string	*strings;
+};
+
+/* put descriptor for string with that id into buf (buflen >= 256) */
+int usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf);
+
+
+#endif  /* __KERNEL__ */
+
+#endif	/* __LINUX_USB_GADGET_H */
diff -urN linux-2.5.69-bk5/include/net/protocol.h linux-2.5.69-bk6/include/net/protocol.h
--- linux-2.5.69-bk5/include/net/protocol.h	2003-05-04 16:53:13.000000000 -0700
+++ linux-2.5.69-bk6/include/net/protocol.h	2003-05-11 04:43:34.000000000 -0700
@@ -80,11 +80,9 @@
 
 extern struct inet_protocol *inet_protocol_base;
 extern struct inet_protocol *inet_protos[MAX_INET_PROTOS];
-extern struct list_head inetsw[SOCK_MAX];
 
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
 extern struct inet6_protocol *inet6_protos[MAX_INET_PROTOS];
-extern struct list_head inetsw6[SOCK_MAX];
 #endif
 
 extern int	inet_add_protocol(struct inet_protocol *prot, unsigned char num);
diff -urN linux-2.5.69-bk5/kernel/cpufreq.c linux-2.5.69-bk6/kernel/cpufreq.c
--- linux-2.5.69-bk5/kernel/cpufreq.c	2003-05-04 16:53:07.000000000 -0700
+++ linux-2.5.69-bk6/kernel/cpufreq.c	2003-05-11 04:43:34.000000000 -0700
@@ -22,6 +22,7 @@
 #include <linux/spinlock.h>
 #include <linux/device.h>
 #include <linux/slab.h>
+#include <linux/cpu.h>
 
 /**
  * The "cpufreq driver" - the arch- or hardware-dependend low
@@ -115,6 +116,7 @@
 extern struct device_class cpu_devclass;
 
 static struct class_interface cpufreq_interface = {
+        .class =	&cpu_class,
         .add =		&cpufreq_add_dev,
         .remove =	&cpufreq_remove_dev,
 };
diff -urN linux-2.5.69-bk5/net/8021q/vlanproc.c linux-2.5.69-bk6/net/8021q/vlanproc.c
--- linux-2.5.69-bk5/net/8021q/vlanproc.c	2003-05-04 16:53:09.000000000 -0700
+++ linux-2.5.69-bk6/net/8021q/vlanproc.c	2003-05-11 04:43:34.000000000 -0700
@@ -18,6 +18,7 @@
  *****************************************************************************/
 
 #include <linux/config.h>
+#include <linux/module.h>
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
 #include <linux/kernel.h>
@@ -75,6 +76,7 @@
  */
 
 static struct file_operations vlan_fops = {
+	.owner = THIS_MODULE,
 	.read =	vlan_proc_read,
 	.ioctl = NULL, /* vlan_proc_ioctl */
 };
@@ -84,6 +86,7 @@
  */
 
 static struct file_operations vlandev_fops = {
+	.owner = THIS_MODULE,
 	.read =	vlan_proc_read,
 	.ioctl =NULL, /* vlan_proc_ioctl */
 };
diff -urN linux-2.5.69-bk5/net/appletalk/atalk_proc.c linux-2.5.69-bk6/net/appletalk/atalk_proc.c
--- linux-2.5.69-bk5/net/appletalk/atalk_proc.c	2003-05-04 16:53:41.000000000 -0700
+++ linux-2.5.69-bk6/net/appletalk/atalk_proc.c	2003-05-11 04:43:34.000000000 -0700
@@ -241,6 +241,7 @@
 }
 
 static struct file_operations atalk_seq_interface_fops = {
+	.owner		= THIS_MODULE,
 	.open		= atalk_seq_interface_open,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
@@ -248,6 +249,7 @@
 };
 
 static struct file_operations atalk_seq_route_fops = {
+	.owner		= THIS_MODULE,
 	.open		= atalk_seq_route_open,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
@@ -255,6 +257,7 @@
 };
 
 static struct file_operations atalk_seq_socket_fops = {
+	.owner		= THIS_MODULE,
 	.open		= atalk_seq_socket_open,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
diff -urN linux-2.5.69-bk5/net/atm/mpoa_proc.c linux-2.5.69-bk6/net/atm/mpoa_proc.c
--- linux-2.5.69-bk5/net/atm/mpoa_proc.c	2003-05-04 16:53:37.000000000 -0700
+++ linux-2.5.69-bk6/net/atm/mpoa_proc.c	2003-05-11 04:43:34.000000000 -0700
@@ -42,6 +42,7 @@
  *   Define allowed FILE OPERATIONS
  */
 static struct file_operations mpc_file_operations = {
+	.owner =	THIS_MODULE,
 	.read =		proc_mpc_read,
 	.write =	proc_mpc_write,
 };
diff -urN linux-2.5.69-bk5/net/atm/proc.c linux-2.5.69-bk6/net/atm/proc.c
--- linux-2.5.69-bk5/net/atm/proc.c	2003-05-04 16:53:08.000000000 -0700
+++ linux-2.5.69-bk6/net/atm/proc.c	2003-05-11 04:43:34.000000000 -0700
@@ -57,10 +57,12 @@
     loff_t *pos);
 
 static struct file_operations proc_dev_atm_operations = {
+	.owner =	THIS_MODULE,
 	.read =		proc_dev_atm_read,
 };
 
 static struct file_operations proc_spec_atm_operations = {
+	.owner =	THIS_MODULE,
 	.read =		proc_spec_atm_read,
 };
 
diff -urN linux-2.5.69-bk5/net/bluetooth/hci_proc.c linux-2.5.69-bk6/net/bluetooth/hci_proc.c
--- linux-2.5.69-bk5/net/bluetooth/hci_proc.c	2003-05-04 16:53:08.000000000 -0700
+++ linux-2.5.69-bk6/net/bluetooth/hci_proc.c	2003-05-11 04:43:34.000000000 -0700
@@ -115,6 +115,7 @@
 }
 
 static struct file_operations inq_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open    = inq_seq_open,
 	.read    = seq_read,
 	.llseek  = seq_lseek,
diff -urN linux-2.5.69-bk5/net/bluetooth/l2cap.c linux-2.5.69-bk6/net/bluetooth/l2cap.c
--- linux-2.5.69-bk5/net/bluetooth/l2cap.c	2003-05-04 16:53:35.000000000 -0700
+++ linux-2.5.69-bk6/net/bluetooth/l2cap.c	2003-05-11 04:43:34.000000000 -0700
@@ -2048,6 +2048,7 @@
 }
 
 static struct file_operations l2cap_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open    = l2cap_seq_open,
 	.read    = seq_read,
 	.llseek  = seq_lseek,
diff -urN linux-2.5.69-bk5/net/bluetooth/rfcomm/core.c linux-2.5.69-bk6/net/bluetooth/rfcomm/core.c
--- linux-2.5.69-bk5/net/bluetooth/rfcomm/core.c	2003-05-04 16:52:48.000000000 -0700
+++ linux-2.5.69-bk6/net/bluetooth/rfcomm/core.c	2003-05-11 04:43:34.000000000 -0700
@@ -1824,6 +1824,7 @@
 }
 
 static struct file_operations rfcomm_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open    = rfcomm_seq_open,
 	.read    = seq_read,
 	.llseek  = seq_lseek,
diff -urN linux-2.5.69-bk5/net/bluetooth/rfcomm/sock.c linux-2.5.69-bk6/net/bluetooth/rfcomm/sock.c
--- linux-2.5.69-bk5/net/bluetooth/rfcomm/sock.c	2003-05-04 16:53:33.000000000 -0700
+++ linux-2.5.69-bk6/net/bluetooth/rfcomm/sock.c	2003-05-11 04:43:34.000000000 -0700
@@ -821,6 +821,7 @@
 }
 
 static struct file_operations rfcomm_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open    = rfcomm_seq_open,
 	.read    = seq_read,
 	.llseek  = seq_lseek,
diff -urN linux-2.5.69-bk5/net/bluetooth/sco.c linux-2.5.69-bk6/net/bluetooth/sco.c
--- linux-2.5.69-bk5/net/bluetooth/sco.c	2003-05-04 16:53:08.000000000 -0700
+++ linux-2.5.69-bk6/net/bluetooth/sco.c	2003-05-11 04:43:34.000000000 -0700
@@ -933,6 +933,7 @@
 }
 
 static struct file_operations sco_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open    = sco_seq_open,
 	.read    = seq_read,
 	.llseek  = seq_lseek,
diff -urN linux-2.5.69-bk5/net/core/dev.c linux-2.5.69-bk6/net/core/dev.c
--- linux-2.5.69-bk5/net/core/dev.c	2003-05-04 16:53:29.000000000 -0700
+++ linux-2.5.69-bk6/net/core/dev.c	2003-05-11 04:43:34.000000000 -0700
@@ -1912,6 +1912,7 @@
 }
 
 static struct file_operations dev_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open    = dev_seq_open,
 	.read    = seq_read,
 	.llseek  = seq_lseek,
@@ -1931,6 +1932,7 @@
 }
 
 static struct file_operations softnet_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open    = softnet_seq_open,
 	.read    = seq_read,
 	.llseek  = seq_lseek,
diff -urN linux-2.5.69-bk5/net/core/wireless.c linux-2.5.69-bk6/net/core/wireless.c
--- linux-2.5.69-bk5/net/core/wireless.c	2003-05-04 16:53:36.000000000 -0700
+++ linux-2.5.69-bk6/net/core/wireless.c	2003-05-11 04:43:34.000000000 -0700
@@ -465,6 +465,7 @@
 }
 
 static struct file_operations wireless_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open    = wireless_seq_open,
 	.read    = seq_read,
 	.llseek  = seq_lseek,
diff -urN linux-2.5.69-bk5/net/decnet/dn_neigh.c linux-2.5.69-bk6/net/decnet/dn_neigh.c
--- linux-2.5.69-bk5/net/decnet/dn_neigh.c	2003-05-11 04:43:30.000000000 -0700
+++ linux-2.5.69-bk6/net/decnet/dn_neigh.c	2003-05-11 04:43:34.000000000 -0700
@@ -693,6 +693,7 @@
 }
 
 static struct file_operations dn_neigh_seq_fops = {
+	.owner		= THIS_MODULE,
 	.open		= dn_neigh_seq_open,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
diff -urN linux-2.5.69-bk5/net/ipv4/af_inet.c linux-2.5.69-bk6/net/ipv4/af_inet.c
--- linux-2.5.69-bk5/net/ipv4/af_inet.c	2003-05-11 04:43:30.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv4/af_inet.c	2003-05-11 04:43:34.000000000 -0700
@@ -94,7 +94,6 @@
 #include <linux/inet.h>
 #include <linux/igmp.h>
 #include <linux/netdevice.h>
-#include <linux/brlock.h>
 #include <net/ip.h>
 #include <net/protocol.h>
 #include <net/arp.h>
@@ -129,7 +128,8 @@
 /* The inetsw table contains everything that inet_create needs to
  * build a new socket.
  */
-struct list_head inetsw[SOCK_MAX];
+static struct list_head inetsw[SOCK_MAX];
+static spinlock_t inetsw_lock = SPIN_LOCK_UNLOCKED;
 
 /* New destruction routine */
 
@@ -337,8 +337,8 @@
 
 	/* Look for the requested type/protocol pair. */
 	answer = NULL;
-	br_read_lock_bh(BR_NETPROTO_LOCK);
-	list_for_each(p, &inetsw[sock->type]) {
+	rcu_read_lock();
+	list_for_each_rcu(p, &inetsw[sock->type]) {
 		answer = list_entry(p, struct inet_protosw, list);
 
 		/* Check the non-wild match. */
@@ -356,7 +356,6 @@
 		}
 		answer = NULL;
 	}
-	br_read_unlock_bh(BR_NETPROTO_LOCK);
 
 	err = -ESOCKTNOSUPPORT;
 	if (!answer)
@@ -373,6 +372,7 @@
 	sk->no_check = answer->no_check;
 	if (INET_PROTOSW_REUSE & answer->flags)
 		sk->reuse = 1;
+	rcu_read_unlock();
 
 	inet = inet_sk(sk);
 
@@ -427,6 +427,7 @@
 out:
 	return err;
 out_sk_free:
+	rcu_read_unlock();
 	sk_free(sk);
 	goto out;
 }
@@ -979,7 +980,7 @@
 	int protocol = p->protocol;
 	struct list_head *last_perm;
 
-	br_write_lock_bh(BR_NETPROTO_LOCK);
+	spin_lock_bh(&inetsw_lock);
 
 	if (p->type > SOCK_MAX)
 		goto out_illegal;
@@ -1008,9 +1009,12 @@
 	 * non-permanent entry.  This means that when we remove this entry, the 
 	 * system automatically returns to the old behavior.
 	 */
-	list_add(&p->list, last_perm);
+	list_add_rcu(&p->list, last_perm);
 out:
-	br_write_unlock_bh(BR_NETPROTO_LOCK);
+	spin_unlock_bh(&inetsw_lock);
+
+	synchronize_kernel();
+
 	return;
 
 out_permanent:
@@ -1032,9 +1036,11 @@
 		       "Attempt to unregister permanent protocol %d.\n",
 		       p->protocol);
 	} else {
-		br_write_lock_bh(BR_NETPROTO_LOCK);
-		list_del(&p->list);
-		br_write_unlock_bh(BR_NETPROTO_LOCK);
+		spin_lock_bh(&inetsw_lock);
+		list_del_rcu(&p->list);
+		spin_unlock_bh(&inetsw_lock);
+
+		synchronize_kernel();
 	}
 }
 
diff -urN linux-2.5.69-bk5/net/ipv4/arp.c linux-2.5.69-bk6/net/ipv4/arp.c
--- linux-2.5.69-bk5/net/ipv4/arp.c	2003-05-04 16:53:40.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv4/arp.c	2003-05-11 04:43:34.000000000 -0700
@@ -1384,6 +1384,7 @@
 }
 
 static struct file_operations arp_seq_fops = {
+	.owner		= THIS_MODULE,
 	.open           = arp_seq_open,
 	.read           = seq_read,
 	.llseek         = seq_lseek,
diff -urN linux-2.5.69-bk5/net/ipv4/fib_hash.c linux-2.5.69-bk6/net/ipv4/fib_hash.c
--- linux-2.5.69-bk5/net/ipv4/fib_hash.c	2003-05-04 16:53:14.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv4/fib_hash.c	2003-05-11 04:43:34.000000000 -0700
@@ -1065,6 +1065,7 @@
 }
 
 static struct file_operations fib_seq_fops = {
+	.owner		= THIS_MODULE,
 	.open           = fib_seq_open,
 	.read           = seq_read,
 	.llseek         = seq_lseek,
diff -urN linux-2.5.69-bk5/net/ipv4/icmp.c linux-2.5.69-bk6/net/ipv4/icmp.c
--- linux-2.5.69-bk5/net/ipv4/icmp.c	2003-05-04 16:53:41.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv4/icmp.c	2003-05-11 04:43:34.000000000 -0700
@@ -695,15 +695,12 @@
 	}
 	read_unlock(&raw_v4_lock);
 
-	/*
-	 *	This can't change while we are doing it.
-	 *	Callers have obtained BR_NETPROTO_LOCK so
-	 *	we are OK.
-	 */
-
+	rcu_read_lock();
 	ipprot = inet_protos[hash];
+	smp_read_barrier_depends();
 	if (ipprot && ipprot->err_handler)
 		ipprot->err_handler(skb, info);
+	rcu_read_unlock();
 
 out:
 	return;
diff -urN linux-2.5.69-bk5/net/ipv4/ip_input.c linux-2.5.69-bk6/net/ipv4/ip_input.c
--- linux-2.5.69-bk5/net/ipv4/ip_input.c	2003-05-04 16:53:01.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv4/ip_input.c	2003-05-11 04:43:34.000000000 -0700
@@ -215,6 +215,7 @@
         /* Point into the IP datagram, just past the header. */
         skb->h.raw = skb->data;
 
+	rcu_read_lock();
 	{
 		/* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
 		int protocol = skb->nh.iph->protocol;
@@ -235,10 +236,11 @@
 		if ((ipprot = inet_protos[hash]) != NULL) {
 			int ret;
 
+			smp_read_barrier_depends();
 			if (!ipprot->no_policy &&
 			    !xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
 				kfree_skb(skb);
-				return 0;
+				goto out;
 			}
 			ret = ipprot->handler(skb);
 			if (ret < 0) {
@@ -258,6 +260,8 @@
 			kfree_skb(skb);
 		}
 	}
+ out:
+	rcu_read_unlock();
 
 	return 0;
 }
diff -urN linux-2.5.69-bk5/net/ipv4/proc.c linux-2.5.69-bk6/net/ipv4/proc.c
--- linux-2.5.69-bk5/net/ipv4/proc.c	2003-05-04 16:52:49.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv4/proc.c	2003-05-11 04:43:34.000000000 -0700
@@ -80,6 +80,7 @@
 }
 
 static struct file_operations sockstat_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open	 = sockstat_seq_open,
 	.read	 = seq_read,
 	.llseek	 = seq_lseek,
@@ -171,6 +172,7 @@
 }
 
 static struct file_operations snmp_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open	 = snmp_seq_open,
 	.read	 = seq_read,
 	.llseek	 = seq_lseek,
@@ -227,6 +229,7 @@
 }
 
 static struct file_operations netstat_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open	 = netstat_seq_open,
 	.read	 = seq_read,
 	.llseek	 = seq_lseek,
diff -urN linux-2.5.69-bk5/net/ipv4/protocol.c linux-2.5.69-bk6/net/ipv4/protocol.c
--- linux-2.5.69-bk5/net/ipv4/protocol.c	2003-05-04 16:53:02.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv4/protocol.c	2003-05-11 04:43:34.000000000 -0700
@@ -37,7 +37,6 @@
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/timer.h>
-#include <linux/brlock.h>
 #include <net/ip.h>
 #include <net/protocol.h>
 #include <net/tcp.h>
@@ -49,6 +48,7 @@
 #include <linux/igmp.h>
 
 struct inet_protocol *inet_protos[MAX_INET_PROTOS];
+static spinlock_t inet_proto_lock = SPIN_LOCK_UNLOCKED;
 
 /*
  *	Add a protocol handler to the hash tables
@@ -60,16 +60,14 @@
 
 	hash = protocol & (MAX_INET_PROTOS - 1);
 
-	br_write_lock_bh(BR_NETPROTO_LOCK);
-
+	spin_lock_bh(&inet_proto_lock);
 	if (inet_protos[hash]) {
 		ret = -1;
 	} else {
 		inet_protos[hash] = prot;
 		ret = 0;
 	}
-
-	br_write_unlock_bh(BR_NETPROTO_LOCK);
+	spin_unlock_bh(&inet_proto_lock);
 
 	return ret;
 }
@@ -84,16 +82,15 @@
 
 	hash = protocol & (MAX_INET_PROTOS - 1);
 
-	br_write_lock_bh(BR_NETPROTO_LOCK);
-
+	spin_lock_bh(&inet_proto_lock);
 	if (inet_protos[hash] == prot) {
 		inet_protos[hash] = NULL;
 		ret = 0;
 	} else {
 		ret = -1;
 	}
+	spin_unlock_bh(&inet_proto_lock);
 
-	br_write_unlock_bh(BR_NETPROTO_LOCK);
 
 	return ret;
 }
diff -urN linux-2.5.69-bk5/net/ipv4/raw.c linux-2.5.69-bk6/net/ipv4/raw.c
--- linux-2.5.69-bk5/net/ipv4/raw.c	2003-05-04 16:53:31.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv4/raw.c	2003-05-11 04:43:34.000000000 -0700
@@ -807,6 +807,7 @@
 }
 
 static struct file_operations raw_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open	 = raw_seq_open,
 	.read	 = seq_read,
 	.llseek	 = seq_lseek,
diff -urN linux-2.5.69-bk5/net/ipv4/route.c linux-2.5.69-bk6/net/ipv4/route.c
--- linux-2.5.69-bk5/net/ipv4/route.c	2003-05-11 04:43:30.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv4/route.c	2003-05-11 04:43:34.000000000 -0700
@@ -382,6 +382,7 @@
 }
 
 static struct file_operations rt_cache_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open	 = rt_cache_seq_open,
 	.read	 = seq_read,
 	.llseek	 = seq_lseek,
diff -urN linux-2.5.69-bk5/net/ipv4/tcp_ipv4.c linux-2.5.69-bk6/net/ipv4/tcp_ipv4.c
--- linux-2.5.69-bk5/net/ipv4/tcp_ipv4.c	2003-05-04 16:53:09.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv4/tcp_ipv4.c	2003-05-11 04:43:34.000000000 -0700
@@ -2569,6 +2569,7 @@
 }
 
 static struct file_operations tcp_seq_fops = {
+	.owner		=	THIS_MODULE,
 	.open		=	tcp_seq_open,
 	.read		=	seq_read,
 	.llseek		=	seq_lseek,
diff -urN linux-2.5.69-bk5/net/ipv4/tcp_minisocks.c linux-2.5.69-bk6/net/ipv4/tcp_minisocks.c
--- linux-2.5.69-bk5/net/ipv4/tcp_minisocks.c	2003-05-11 04:43:30.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv4/tcp_minisocks.c	2003-05-11 04:43:34.000000000 -0700
@@ -760,6 +760,7 @@
 			tcp_reset_keepalive_timer(newsk, keepalive_time_when(newtp));
 		newsk->socket = NULL;
 		newsk->sleep = NULL;
+		newsk->owner = NULL;
 
 		newtp->tstamp_ok = req->tstamp_ok;
 		if((newtp->sack_ok = req->sack_ok) != 0) {
diff -urN linux-2.5.69-bk5/net/ipv4/udp.c linux-2.5.69-bk6/net/ipv4/udp.c
--- linux-2.5.69-bk5/net/ipv4/udp.c	2003-05-04 16:53:03.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv4/udp.c	2003-05-11 04:43:34.000000000 -0700
@@ -1483,6 +1483,7 @@
 }
 
 static struct file_operations udp_seq_fops = {
+	.owner		= THIS_MODULE,
 	.open           = udp_seq_open,
 	.read           = seq_read,
 	.llseek         = seq_lseek,
diff -urN linux-2.5.69-bk5/net/ipv6/af_inet6.c linux-2.5.69-bk6/net/ipv6/af_inet6.c
--- linux-2.5.69-bk5/net/ipv6/af_inet6.c	2003-05-11 04:43:30.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv6/af_inet6.c	2003-05-11 04:43:34.000000000 -0700
@@ -45,7 +45,6 @@
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/icmpv6.h>
-#include <linux/brlock.h>
 #include <linux/smp_lock.h>
 
 #include <net/ip.h>
@@ -75,8 +74,10 @@
 /* IPv6 procfs goodies... */
 
 #ifdef CONFIG_PROC_FS
+extern int raw6_proc_init(void);
+extern int raw6_proc_exit(void);
+
 extern int anycast6_get_info(char *, char **, off_t, int);
-extern int raw6_get_info(char *, char **, off_t, int);
 extern int tcp6_get_info(char *, char **, off_t, int);
 extern int udp6_get_info(char *, char **, off_t, int);
 extern int afinet6_get_info(char *, char **, off_t, int);
@@ -102,7 +103,8 @@
 /* The inetsw table contains everything that inet_create needs to
  * build a new socket.
  */
-struct list_head inetsw6[SOCK_MAX];
+static struct list_head inetsw6[SOCK_MAX];
+static spinlock_t inetsw6_lock = SPIN_LOCK_UNLOCKED;
 
 static void inet6_sock_destruct(struct sock *sk)
 {
@@ -162,8 +164,8 @@
 
 	/* Look for the requested type/protocol pair. */
 	answer = NULL;
-	br_read_lock_bh(BR_NETPROTO_LOCK);
-	list_for_each(p, &inetsw6[sock->type]) {
+	rcu_read_lock();
+	list_for_each_rcu(p, &inetsw6[sock->type]) {
 		answer = list_entry(p, struct inet_protosw, list);
 
 		/* Check the non-wild match. */
@@ -181,7 +183,6 @@
 		}
 		answer = NULL;
 	}
-	br_read_unlock_bh(BR_NETPROTO_LOCK);
 
 	if (!answer)
 		goto free_and_badtype;
@@ -198,6 +199,7 @@
 	sk->no_check = answer->no_check;
 	if (INET_PROTOSW_REUSE & answer->flags)
 		sk->reuse = 1;
+	rcu_read_unlock();
 
 	inet = inet_sk(sk);
 
@@ -260,12 +262,15 @@
 	return 0;
 
 free_and_badtype:
+	rcu_read_unlock();
 	sk_free(sk);
 	return -ESOCKTNOSUPPORT;
 free_and_badperm:
+	rcu_read_unlock();
 	sk_free(sk);
 	return -EPERM;
 free_and_noproto:
+	rcu_read_unlock();
 	sk_free(sk);
 	return -EPROTONOSUPPORT;
 do_oom:
@@ -574,7 +579,7 @@
 	int protocol = p->protocol;
 	struct list_head *last_perm;
 
-	br_write_lock_bh(BR_NETPROTO_LOCK);
+	spin_lock_bh(&inetsw6_lock);
 
 	if (p->type > SOCK_MAX)
 		goto out_illegal;
@@ -603,9 +608,9 @@
 	 * non-permanent entry.  This means that when we remove this entry, the 
 	 * system automatically returns to the old behavior.
 	 */
-	list_add(&p->list, last_perm);
+	list_add_rcu(&p->list, last_perm);
 out:
-	br_write_unlock_bh(BR_NETPROTO_LOCK);
+	spin_unlock_bh(&inetsw6_lock);
 	return;
 
 out_permanent:
@@ -623,7 +628,17 @@
 void
 inet6_unregister_protosw(struct inet_protosw *p)
 {
-	inet_unregister_protosw(p);
+	if (INET_PROTOSW_PERMANENT & p->flags) {
+		printk(KERN_ERR
+		       "Attempt to unregister permanent protocol %d.\n",
+		       p->protocol);
+	} else {
+		spin_lock_bh(&inetsw6_lock);
+		list_del_rcu(&p->list);
+		spin_unlock_bh(&inetsw6_lock);
+
+		synchronize_kernel();
+	}
 }
 
 int
@@ -773,7 +788,7 @@
 	/* Create /proc/foo6 entries. */
 #ifdef CONFIG_PROC_FS
 	err = -ENOMEM;
-	if (!proc_net_create("raw6", 0, raw6_get_info))
+	if (raw6_proc_init())
 		goto proc_raw6_fail;
 	if (!proc_net_create("tcp6", 0, tcp6_get_info))
 		goto proc_tcp6_fail;
@@ -814,7 +829,7 @@
 proc_udp6_fail:
 	proc_net_remove("tcp6");
 proc_tcp6_fail:
-        proc_net_remove("raw6");
+	raw6_proc_exit();
 proc_raw6_fail:
 	igmp6_cleanup();
 #endif
@@ -839,7 +854,7 @@
 	/* First of all disallow new sockets creation. */
 	sock_unregister(PF_INET6);
 #ifdef CONFIG_PROC_FS
-	proc_net_remove("raw6");
+	raw6_proc_exit();
 	proc_net_remove("tcp6");
 	proc_net_remove("udp6");
 	proc_net_remove("sockstat6");
diff -urN linux-2.5.69-bk5/net/ipv6/icmp.c linux-2.5.69-bk6/net/ipv6/icmp.c
--- linux-2.5.69-bk5/net/ipv6/icmp.c	2003-05-04 16:53:08.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv6/icmp.c	2003-05-11 04:43:34.000000000 -0700
@@ -456,9 +456,12 @@
 
 	hash = nexthdr & (MAX_INET_PROTOS - 1);
 
+	rcu_read_lock();
 	ipprot = inet6_protos[hash];
+	smp_read_barrier_depends();
 	if (ipprot && ipprot->err_handler)
 		ipprot->err_handler(skb, NULL, type, code, inner_offset, info);
+	rcu_read_unlock();
 
 	read_lock(&raw_v6_lock);
 	if ((sk = raw_v6_htable[hash]) != NULL) {
diff -urN linux-2.5.69-bk5/net/ipv6/ip6_input.c linux-2.5.69-bk6/net/ipv6/ip6_input.c
--- linux-2.5.69-bk5/net/ipv6/ip6_input.c	2003-05-04 16:53:36.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv6/ip6_input.c	2003-05-11 04:43:34.000000000 -0700
@@ -152,6 +152,7 @@
 		skb->h.raw += (skb->h.raw[1]+1)<<3;
 	}
 
+	rcu_read_lock();
 resubmit:
 	if (!pskb_pull(skb, skb->h.raw - skb->data))
 		goto discard;
@@ -165,6 +166,7 @@
 	if ((ipprot = inet6_protos[hash]) != NULL) {
 		int ret;
 		
+		smp_read_barrier_depends();
 		if (ipprot->flags & INET6_PROTO_FINAL) {
 			if (!cksum_sub && skb->ip_summed == CHECKSUM_HW) {
 				skb->csum = csum_sub(skb->csum,
@@ -173,10 +175,8 @@
 			}
 		}
 		if (!(ipprot->flags & INET6_PROTO_NOPOLICY) &&
-		    !xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
-			kfree_skb(skb);
-			return 0;
-		}
+		    !xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) 
+			goto discard;
 		
 		ret = ipprot->handler(&skb, &nhoff);
 		if (ret > 0)
@@ -194,10 +194,11 @@
 			kfree_skb(skb);
 		}
 	}
-
+	rcu_read_unlock();
 	return 0;
 
 discard:
+	rcu_read_unlock();
 	kfree_skb(skb);
 	return 0;
 }
diff -urN linux-2.5.69-bk5/net/ipv6/proc.c linux-2.5.69-bk6/net/ipv6/proc.c
--- linux-2.5.69-bk5/net/ipv6/proc.c	2003-05-04 16:53:13.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv6/proc.c	2003-05-11 04:43:34.000000000 -0700
@@ -198,6 +198,7 @@
 }
 
 static struct file_operations sockstat6_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open	 = sockstat6_seq_open,
 	.read	 = seq_read,
 	.llseek	 = seq_lseek,
@@ -210,6 +211,7 @@
 }
 
 static struct file_operations snmp6_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open	 = snmp6_seq_open,
 	.read	 = seq_read,
 	.llseek	 = seq_lseek,
diff -urN linux-2.5.69-bk5/net/ipv6/protocol.c linux-2.5.69-bk6/net/ipv6/protocol.c
--- linux-2.5.69-bk5/net/ipv6/protocol.c	2003-05-04 16:53:03.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv6/protocol.c	2003-05-11 04:43:34.000000000 -0700
@@ -32,7 +32,6 @@
 #include <linux/in6.h>
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
-#include <linux/brlock.h>
 
 #include <net/sock.h>
 #include <net/snmp.h>
@@ -41,12 +40,14 @@
 #include <net/protocol.h>
 
 struct inet6_protocol *inet6_protos[MAX_INET_PROTOS];
+static spinlock_t inet6_proto_lock = SPIN_LOCK_UNLOCKED;
+
 
 int inet6_add_protocol(struct inet6_protocol *prot, unsigned char protocol)
 {
 	int ret, hash = protocol & (MAX_INET_PROTOS - 1);
 
-	br_write_lock_bh(BR_NETPROTO_LOCK);
+	spin_lock_bh(&inet6_proto_lock);
 
 	if (inet6_protos[hash]) {
 		ret = -1;
@@ -55,7 +56,7 @@
 		ret = 0;
 	}
 
-	br_write_unlock_bh(BR_NETPROTO_LOCK);
+	spin_unlock_bh(&inet6_proto_lock);
 
 	return ret;
 }
@@ -68,7 +69,7 @@
 {
 	int ret, hash = protocol & (MAX_INET_PROTOS - 1);
 
-	br_write_lock_bh(BR_NETPROTO_LOCK);
+	spin_lock_bh(&inet6_proto_lock);
 
 	if (inet6_protos[hash] != prot) {
 		ret = -1;
@@ -77,7 +78,7 @@
 		ret = 0;
 	}
 
-	br_write_unlock_bh(BR_NETPROTO_LOCK);
+	spin_unlock_bh(&inet6_proto_lock);
 
 	return ret;
 }
diff -urN linux-2.5.69-bk5/net/ipv6/raw.c linux-2.5.69-bk6/net/ipv6/raw.c
--- linux-2.5.69-bk5/net/ipv6/raw.c	2003-05-04 16:53:13.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv6/raw.c	2003-05-11 04:43:34.000000000 -0700
@@ -47,6 +47,9 @@
 #include <net/rawv6.h>
 #include <net/xfrm.h>
 
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
 struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE];
 rwlock_t raw_v6_lock = RW_LOCK_UNLOCKED;
 
@@ -831,80 +834,6 @@
 	return(0);
 }
 
-#define LINE_LEN 190
-#define LINE_FMT "%-190s\n"
-
-static void get_raw6_sock(struct sock *sp, char *tmpbuf, int i)
-{
-	struct ipv6_pinfo *np = inet6_sk(sp);
-	struct in6_addr *dest, *src;
-	__u16 destp, srcp;
-
-	dest  = &np->daddr;
-	src   = &np->rcv_saddr;
-	destp = 0;
-	srcp  = inet_sk(sp)->num;
-	sprintf(tmpbuf,
-		"%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
-		"%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p",
-		i,
-		src->s6_addr32[0], src->s6_addr32[1],
-		src->s6_addr32[2], src->s6_addr32[3], srcp,
-		dest->s6_addr32[0], dest->s6_addr32[1],
-		dest->s6_addr32[2], dest->s6_addr32[3], destp,
-		sp->state, 
-		atomic_read(&sp->wmem_alloc), atomic_read(&sp->rmem_alloc),
-		0, 0L, 0,
-		sock_i_uid(sp), 0,
-		sock_i_ino(sp),
-		atomic_read(&sp->refcnt), sp);
-}
-
-int raw6_get_info(char *buffer, char **start, off_t offset, int length)
-{
-	int len = 0, num = 0, i;
-	off_t pos = 0;
-	off_t begin;
-	char tmpbuf[LINE_LEN+2];
-
-	if (offset < LINE_LEN+1)
-		len += sprintf(buffer, LINE_FMT,
-			       "  sl  "						/* 6 */
-			       "local_address                         "		/* 38 */
-			       "remote_address                        "		/* 38 */
-			       "st tx_queue rx_queue tr tm->when retrnsmt"	/* 41 */
-			       "   uid  timeout inode");			/* 21 */
-										/*----*/
-										/*144 */
-	pos = LINE_LEN+1;
-	read_lock(&raw_v6_lock);
-	for (i = 0; i < RAWV6_HTABLE_SIZE; i++) {
-		struct sock *sk;
-
-		for (sk = raw_v6_htable[i]; sk; sk = sk->next, num++) {
-			if (sk->family != PF_INET6)
-				continue;
-			pos += LINE_LEN+1;
-			if (pos <= offset)
-				continue;
-			get_raw6_sock(sk, tmpbuf, i);
-			len += sprintf(buffer+len, LINE_FMT, tmpbuf);
-			if(len >= length)
-				goto out;
-		}
-	}
-out:
-	read_unlock(&raw_v6_lock);
-	begin = len - (pos - offset);
-	*start = buffer + begin;
-	len -= begin;
-	if(len > length)
-		len = length;
-	if (len < 0)
-		len = 0; 
-	return len;
-}
-
 struct proto rawv6_prot = {
 	.name =		"RAW",
 	.close =	rawv6_close,
@@ -922,3 +851,151 @@
 	.hash =		raw_v6_hash,
 	.unhash =	raw_v6_unhash,
 };
+
+#ifdef CONFIG_PROC_FS
+struct raw6_iter_state {
+	int bucket;
+};
+
+#define raw6_seq_private(seq) ((struct raw6_iter_state *)&seq->private)
+
+static struct sock *raw6_get_first(struct seq_file *seq)
+{
+	struct sock *sk = NULL;
+	struct raw6_iter_state* state = raw6_seq_private(seq);
+
+	for (state->bucket = 0; state->bucket < RAWV6_HTABLE_SIZE; ++state->bucket) {
+		sk = raw_v6_htable[state->bucket];
+		while (sk && sk->family != PF_INET6)
+			sk = sk->next;
+		if (sk)
+			break;
+	}
+	return sk;
+}
+
+static struct sock *raw6_get_next(struct seq_file *seq, struct sock *sk)
+{
+	struct raw6_iter_state* state = raw6_seq_private(seq);
+
+	do {
+		sk = sk->next;
+try_again:
+		;
+	} while (sk && sk->family != PF_INET6);
+
+	if (!sk && ++state->bucket < RAWV6_HTABLE_SIZE) {
+		sk = raw_v6_htable[state->bucket];
+		goto try_again;
+	}
+	return sk;
+}
+
+static struct sock *raw6_get_idx(struct seq_file *seq, loff_t pos)
+{
+	struct sock *sk = raw6_get_first(seq);
+	if (sk)
+		while (pos && (sk = raw6_get_next(seq, sk)) != NULL)
+			--pos;
+	return pos ? NULL : sk;
+}
+
+static void *raw6_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	read_lock(&raw_v6_lock);
+	return *pos ? raw6_get_idx(seq, *pos) : (void *)1;
+}
+
+static void *raw6_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct sock *sk;
+
+	if (v == (void *)1)
+		sk = raw6_get_first(seq);
+	else
+		sk = raw6_get_next(seq, v);
+	++*pos;
+	return sk;
+}
+
+static void raw6_seq_stop(struct seq_file *seq, void *v)
+{
+	read_unlock(&raw_v6_lock);
+}
+
+static void raw6_sock_seq_show(struct seq_file *seq, struct sock *sp, int i)
+{
+	struct ipv6_pinfo *np = inet6_sk(sp);
+	struct in6_addr *dest, *src;
+	__u16 destp, srcp;
+
+	dest  = &np->daddr;
+	src   = &np->rcv_saddr;
+	destp = 0;
+	srcp  = inet_sk(sp)->num;
+	seq_printf(seq,
+		   "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
+		   "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p\n",
+		   i,
+		   src->s6_addr32[0], src->s6_addr32[1],
+		   src->s6_addr32[2], src->s6_addr32[3], srcp,
+		   dest->s6_addr32[0], dest->s6_addr32[1],
+		   dest->s6_addr32[2], dest->s6_addr32[3], destp,
+		   sp->state, 
+		   atomic_read(&sp->wmem_alloc), atomic_read(&sp->rmem_alloc),
+		   0, 0L, 0,
+		   sock_i_uid(sp), 0,
+		   sock_i_ino(sp),
+		   atomic_read(&sp->refcnt), sp);
+}
+
+static int raw6_seq_show(struct seq_file *seq, void *v)
+{
+	if (v == (void *)1)
+		seq_printf(seq,
+			   "  sl  "
+			   "local_address                         "
+			   "remote_address                        "
+			   "st tx_queue rx_queue tr tm->when retrnsmt"
+			   "   uid  timeout inode\n");
+	else
+		raw6_sock_seq_show(seq, v, raw6_seq_private(seq)->bucket);
+	return 0;
+}
+
+static struct seq_operations raw6_seq_ops = {
+	.start =	raw6_seq_start,
+	.next =		raw6_seq_next,
+	.stop =		raw6_seq_stop,
+	.show =		raw6_seq_show,
+};
+
+static int raw6_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &raw6_seq_ops);
+}
+
+static struct file_operations raw6_seq_fops = {
+	.owner =	THIS_MODULE,
+	.open =		raw6_seq_open,
+	.read =		seq_read,
+	.llseek =	seq_lseek,
+	.release =	seq_release,
+};
+
+int __init raw6_proc_init(void)
+{
+	struct proc_dir_entry *p = create_proc_entry("raw6", S_IRUGO, proc_net);
+
+	if (!p)
+		return -ENOMEM;
+	p->proc_fops = &raw6_seq_fops;
+
+	return 0;
+}
+
+void raw6_proc_exit(void)
+{
+	remove_proc_entry("raw6", proc_net);
+}
+#endif	/* CONFIG_PROC_FS */
diff -urN linux-2.5.69-bk5/net/ipv6/route.c linux-2.5.69-bk6/net/ipv6/route.c
--- linux-2.5.69-bk5/net/ipv6/route.c	2003-05-11 04:43:30.000000000 -0700
+++ linux-2.5.69-bk6/net/ipv6/route.c	2003-05-11 04:43:34.000000000 -0700
@@ -1797,6 +1797,7 @@
 }
 
 static struct file_operations rt6_stats_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open	 = rt6_stats_seq_open,
 	.read	 = seq_read,
 	.llseek	 = seq_lseek,
diff -urN linux-2.5.69-bk5/net/ipx/ipx_proc.c linux-2.5.69-bk6/net/ipx/ipx_proc.c
--- linux-2.5.69-bk5/net/ipx/ipx_proc.c	2003-05-11 04:43:30.000000000 -0700
+++ linux-2.5.69-bk6/net/ipx/ipx_proc.c	2003-05-11 04:43:34.000000000 -0700
@@ -319,6 +319,7 @@
 }
 
 static struct file_operations ipx_seq_interface_fops = {
+	.owner		= THIS_MODULE,
 	.open           = ipx_seq_interface_open,
 	.read           = seq_read,
 	.llseek         = seq_lseek,
@@ -326,6 +327,7 @@
 };
 
 static struct file_operations ipx_seq_route_fops = {
+	.owner		= THIS_MODULE,
 	.open           = ipx_seq_route_open,
 	.read           = seq_read,
 	.llseek         = seq_lseek,
@@ -333,6 +335,7 @@
 };
 
 static struct file_operations ipx_seq_socket_fops = {
+	.owner		= THIS_MODULE,
 	.open           = ipx_seq_socket_open,
 	.read           = seq_read,
 	.llseek         = seq_lseek,
diff -urN linux-2.5.69-bk5/net/llc/llc_proc.c linux-2.5.69-bk6/net/llc/llc_proc.c
--- linux-2.5.69-bk5/net/llc/llc_proc.c	2003-05-04 16:53:01.000000000 -0700
+++ linux-2.5.69-bk6/net/llc/llc_proc.c	2003-05-11 04:43:34.000000000 -0700
@@ -211,6 +211,7 @@
 }
 
 static struct file_operations llc_seq_socket_fops = {
+	.owner		= THIS_MODULE,
 	.open		= llc_seq_socket_open,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
@@ -218,6 +219,7 @@
 };
 
 static struct file_operations llc_seq_core_fops = {
+	.owner		= THIS_MODULE,
 	.open		= llc_seq_core_open,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
diff -urN linux-2.5.69-bk5/net/nonet.c linux-2.5.69-bk6/net/nonet.c
--- linux-2.5.69-bk5/net/nonet.c	2003-05-04 16:53:56.000000000 -0700
+++ linux-2.5.69-bk6/net/nonet.c	2003-05-11 04:43:35.000000000 -0700
@@ -24,5 +24,6 @@
 }
 
 struct file_operations bad_sock_fops = {
+	.owner = THIS_MODULE,
 	.open = sock_no_open,
 };
diff -urN linux-2.5.69-bk5/net/rxrpc/proc.c linux-2.5.69-bk6/net/rxrpc/proc.c
--- linux-2.5.69-bk5/net/rxrpc/proc.c	2003-05-04 16:53:32.000000000 -0700
+++ linux-2.5.69-bk6/net/rxrpc/proc.c	2003-05-11 04:43:35.000000000 -0700
@@ -38,6 +38,7 @@
 };
 
 static struct file_operations rxrpc_proc_transports_fops = {
+	.owner		= THIS_MODULE,
 	.open		= rxrpc_proc_transports_open,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
@@ -51,6 +52,7 @@
 static int rxrpc_proc_peers_show(struct seq_file *m, void *v);
 
 static struct seq_operations rxrpc_proc_peers_ops = {
+	.owner	= THIS_MODULE,
 	.start	= rxrpc_proc_peers_start,
 	.next	= rxrpc_proc_peers_next,
 	.stop	= rxrpc_proc_peers_stop,
@@ -58,6 +60,7 @@
 };
 
 static struct file_operations rxrpc_proc_peers_fops = {
+	.owner		= THIS_MODULE,
 	.open		= rxrpc_proc_peers_open,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
@@ -78,6 +81,7 @@
 };
 
 static struct file_operations rxrpc_proc_conns_fops = {
+	.owner		= THIS_MODULE,
 	.open		= rxrpc_proc_conns_open,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
diff -urN linux-2.5.69-bk5/net/sctp/Kconfig linux-2.5.69-bk6/net/sctp/Kconfig
--- linux-2.5.69-bk5/net/sctp/Kconfig	2003-05-11 04:43:30.000000000 -0700
+++ linux-2.5.69-bk6/net/sctp/Kconfig	2003-05-11 04:43:35.000000000 -0700
@@ -75,6 +75,7 @@
 
 choice
 	prompt "SCTP: Cookie HMAC Algorithm"
+	depends on IP_SCTP
 	help
 	  HMAC algorithm to be used during association initialization.  It
 	  is strongly recommended to use HMAC-SHA1 or HMAC-MD5.  See 
diff -urN linux-2.5.69-bk5/net/sctp/proc.c linux-2.5.69-bk6/net/sctp/proc.c
--- linux-2.5.69-bk5/net/sctp/proc.c	2003-05-04 16:53:12.000000000 -0700
+++ linux-2.5.69-bk6/net/sctp/proc.c	2003-05-11 04:43:35.000000000 -0700
@@ -102,6 +102,7 @@
 }
 
 static struct file_operations sctp_snmp_seq_fops = {
+	.owner	 = THIS_MODULE,
 	.open	 = sctp_snmp_seq_open,
 	.read	 = seq_read,
 	.llseek	 = seq_lseek,
diff -urN linux-2.5.69-bk5/net/socket.c linux-2.5.69-bk6/net/socket.c
--- linux-2.5.69-bk5/net/socket.c	2003-05-11 04:43:30.000000000 -0700
+++ linux-2.5.69-bk6/net/socket.c	2003-05-11 04:43:35.000000000 -0700
@@ -121,6 +121,7 @@
  */
 
 static struct file_operations socket_file_ops = {
+	.owner =	THIS_MODULE,
 	.llseek =	no_llseek,
 	.aio_read =	sock_aio_read,
 	.aio_write =	sock_aio_write,
@@ -490,6 +491,7 @@
 }
 
 struct file_operations bad_sock_fops = {
+	.owner = THIS_MODULE,
 	.open = sock_no_open,
 };
 
diff -urN linux-2.5.69-bk5/net/sunrpc/cache.c linux-2.5.69-bk6/net/sunrpc/cache.c
--- linux-2.5.69-bk5/net/sunrpc/cache.c	2003-05-04 16:53:32.000000000 -0700
+++ linux-2.5.69-bk6/net/sunrpc/cache.c	2003-05-11 04:43:35.000000000 -0700
@@ -733,6 +733,7 @@
 
 
 static struct file_operations cache_file_operations = {
+	.owner		= THIS_MODULE,
 	.llseek		= no_llseek,
 	.read		= cache_read,
 	.write		= cache_write,
diff -urN linux-2.5.69-bk5/net/sunrpc/rpc_pipe.c linux-2.5.69-bk6/net/sunrpc/rpc_pipe.c
--- linux-2.5.69-bk5/net/sunrpc/rpc_pipe.c	2003-05-04 16:53:09.000000000 -0700
+++ linux-2.5.69-bk6/net/sunrpc/rpc_pipe.c	2003-05-11 04:43:35.000000000 -0700
@@ -310,6 +310,7 @@
 }
 
 static struct file_operations rpc_info_operations = {
+	.owner		= THIS_MODULE,
 	.open		= rpc_info_open,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
diff -urN linux-2.5.69-bk5/net/wanrouter/wanproc.c linux-2.5.69-bk6/net/wanrouter/wanproc.c
--- linux-2.5.69-bk5/net/wanrouter/wanproc.c	2003-05-04 16:53:14.000000000 -0700
+++ linux-2.5.69-bk6/net/wanrouter/wanproc.c	2003-05-11 04:43:35.000000000 -0700
@@ -202,6 +202,7 @@
 
 static struct file_operations config_fops =
 {
+	.owner =	THIS_MODULE,
 	.open =		config_open,
 	.read =		seq_read,
 	.llseek =	seq_lseek,
@@ -210,6 +211,7 @@
 
 static struct file_operations status_fops =
 {
+	.owner =	THIS_MODULE,
 	.open =		status_open,
 	.read =		seq_read,
 	.llseek =	seq_lseek,
@@ -285,6 +287,7 @@
 
 static struct file_operations wandev_fops =
 {
+	.owner =	THIS_MODULE,
 	.open =		wandev_open,
 	.read =		seq_read,
 	.llseek =	seq_lseek,
diff -urN linux-2.5.69-bk5/net/x25/x25_proc.c linux-2.5.69-bk6/net/x25/x25_proc.c
--- linux-2.5.69-bk5/net/x25/x25_proc.c	2003-05-04 16:53:02.000000000 -0700
+++ linux-2.5.69-bk6/net/x25/x25_proc.c	2003-05-11 04:43:35.000000000 -0700
@@ -189,6 +189,7 @@
 }
 
 static struct file_operations x25_seq_socket_fops = {
+	.owner		= THIS_MODULE,
 	.open		= x25_seq_socket_open,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
@@ -196,6 +197,7 @@
 };
 
 static struct file_operations x25_seq_route_fops = {
+	.owner		= THIS_MODULE,
 	.open		= x25_seq_route_open,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
