## 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 = ®s_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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +/*-------------------------------------------------------------------------*/ + +/* + * "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, ð_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, ðer_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 (ð_driver); +} +module_init (init); + +static void __exit cleanup (void) +{ + usb_gadget_unregister_driver (ð_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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +#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 (®s->pciirqenb0); + tmp &= ~(1 << ep->num); + writel (tmp, ®s->pciirqenb0); + } else { + tmp = readl (®s->pciirqenb1); + tmp &= ~(1 << (8 + ep->num)); /* completion */ + writel (tmp, ®s->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), ®s->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, ®s->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 (®s->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 (®s->ep_data), (u32 *)buf); + buf += 4; + count -= 4; + } + if (count) { + tmp = readl (®s->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, ®s->idxaddr); + /* NOTE: synchs device/cpu memory views */ + return readl (®s->idxdata); +} + +static inline void +set_idx_reg (struct net2280_regs *regs, u32 index, u32 value) +{ + writel (index, ®s->idxaddr); + writel (value, ®s->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 +#include +#include +#include +#include + +#include +#include + + +/** + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + + +/*-------------------------------------------------------------------------*/ + +#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 #include #include +#include #include @@ -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 @@ +/* + * + * + * 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 #include #include +#include /** * 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 +#include #include /* offsetof(), etc. */ #include /* return codes */ #include @@ -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 #include #include -#include #include #include #include @@ -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 #include #include -#include #include #include #include @@ -49,6 +48,7 @@ #include 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 #include #include -#include #include #include @@ -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 #include #include -#include #include #include @@ -41,12 +40,14 @@ #include 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 #include +#include +#include + 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,