diff options
author | Jeff Garzik <jgarzik@pretzel.yyz.us> | 2005-06-28 02:05:03 (GMT) |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-06-28 02:05:03 (GMT) |
commit | 1bad3f4050b2a641bbfeaddb2717b28247311e9c (patch) | |
tree | bdc2efda40f48e0fbb64e6b9aca793e95ac72fc4 /drivers | |
parent | 747802ab478399f13ff57751c2ebd22577be4eeb (diff) | |
parent | c7b645f934e52a54af58142d91fb51f881f8ce26 (diff) | |
download | linux-1bad3f4050b2a641bbfeaddb2717b28247311e9c.tar.xz |
Merge /spare/repo/linux-2.6/
Diffstat (limited to 'drivers')
228 files changed, 16354 insertions, 13223 deletions
diff --git a/drivers/block/as-iosched.c b/drivers/block/as-iosched.c index 3410b4d..91aeb67 100644 --- a/drivers/block/as-iosched.c +++ b/drivers/block/as-iosched.c @@ -1806,7 +1806,8 @@ static void as_put_request(request_queue_t *q, struct request *rq) rq->elevator_private = NULL; } -static int as_set_request(request_queue_t *q, struct request *rq, int gfp_mask) +static int as_set_request(request_queue_t *q, struct request *rq, + struct bio *bio, int gfp_mask) { struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = mempool_alloc(ad->arq_pool, gfp_mask); @@ -1827,7 +1828,7 @@ static int as_set_request(request_queue_t *q, struct request *rq, int gfp_mask) return 1; } -static int as_may_queue(request_queue_t *q, int rw) +static int as_may_queue(request_queue_t *q, int rw, struct bio *bio) { int ret = ELV_MQUEUE_MAY; struct as_data *ad = q->elevator->elevator_data; diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index abde270..653512b 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -1,6 +1,6 @@ /* * Disk Array driver for HP SA 5xxx and 6xxx Controllers - * Copyright 2000, 2002 Hewlett-Packard Development Company, L.P. + * Copyright 2000, 2005 Hewlett-Packard Development Company, L.P. * * 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 @@ -54,7 +54,7 @@ MODULE_AUTHOR("Hewlett-Packard Company"); MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.6"); MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400" - " SA6i P600 P800 E400"); + " SA6i P600 P800 E400 E300"); MODULE_LICENSE("GPL"); #include "cciss_cmd.h" @@ -85,8 +85,10 @@ static const struct pci_device_id cciss_pci_device_id[] = { 0x103C, 0x3225, 0, 0, 0}, { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSB, 0x103c, 0x3223, 0, 0, 0}, - { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSB, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, 0x103c, 0x3231, 0, 0, 0}, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, + 0x103c, 0x3233, 0, 0, 0}, {0,} }; MODULE_DEVICE_TABLE(pci, cciss_pci_device_id); @@ -110,6 +112,7 @@ static struct board_type products[] = { { 0x3225103C, "Smart Array P600", &SA5_access}, { 0x3223103C, "Smart Array P800", &SA5_access}, { 0x3231103C, "Smart Array E400", &SA5_access}, + { 0x3233103C, "Smart Array E300", &SA5_access}, }; /* How long to wait (in millesconds) for board to go into simple mode */ @@ -635,6 +638,7 @@ static int cciss_ioctl(struct inode *inode, struct file *filep, cciss_pci_info_struct pciinfo; if (!arg) return -EINVAL; + pciinfo.domain = pci_domain_nr(host->pdev->bus); pciinfo.bus = host->pdev->bus->number; pciinfo.dev_fn = host->pdev->devfn; pciinfo.board_id = host->board_id; @@ -787,13 +791,6 @@ static int cciss_ioctl(struct inode *inode, struct file *filep, luninfo.LunID = drv->LunID; luninfo.num_opens = drv->usage_count; luninfo.num_parts = 0; - /* count partitions 1 to 15 with sizes > 0 */ - for (i = 0; i < MAX_PART - 1; i++) { - if (!disk->part[i]) - continue; - if (disk->part[i]->nr_sects != 0) - luninfo.num_parts++; - } if (copy_to_user(argp, &luninfo, sizeof(LogvolInfo_struct))) return -EFAULT; diff --git a/drivers/block/cfq-iosched.c b/drivers/block/cfq-iosched.c index 3ac47dd..ff1cc96 100644 --- a/drivers/block/cfq-iosched.c +++ b/drivers/block/cfq-iosched.c @@ -21,22 +21,34 @@ #include <linux/hash.h> #include <linux/rbtree.h> #include <linux/mempool.h> - -static unsigned long max_elapsed_crq; -static unsigned long max_elapsed_dispatch; +#include <linux/ioprio.h> +#include <linux/writeback.h> /* * tunables */ static int cfq_quantum = 4; /* max queue in one round of service */ static int cfq_queued = 8; /* minimum rq allocate limit per-queue*/ -static int cfq_service = HZ; /* period over which service is avg */ -static int cfq_fifo_expire_r = HZ / 2; /* fifo timeout for sync requests */ -static int cfq_fifo_expire_w = 5 * HZ; /* fifo timeout for async requests */ -static int cfq_fifo_rate = HZ / 8; /* fifo expiry rate */ +static int cfq_fifo_expire[2] = { HZ / 4, HZ / 8 }; static int cfq_back_max = 16 * 1024; /* maximum backwards seek, in KiB */ static int cfq_back_penalty = 2; /* penalty of a backwards seek */ +static int cfq_slice_sync = HZ / 10; +static int cfq_slice_async = HZ / 25; +static int cfq_slice_async_rq = 2; +static int cfq_slice_idle = HZ / 100; + +#define CFQ_IDLE_GRACE (HZ / 10) +#define CFQ_SLICE_SCALE (5) + +#define CFQ_KEY_ASYNC (0) +#define CFQ_KEY_ANY (0xffff) + +/* + * disable queueing at the driver/hardware level + */ +static int cfq_max_depth = 1; + /* * for the hash of cfqq inside the cfqd */ @@ -55,6 +67,7 @@ static int cfq_back_penalty = 2; /* penalty of a backwards seek */ #define list_entry_hash(ptr) hlist_entry((ptr), struct cfq_rq, hash) #define list_entry_cfqq(ptr) list_entry((ptr), struct cfq_queue, cfq_list) +#define list_entry_fifo(ptr) list_entry((ptr), struct request, queuelist) #define RQ_DATA(rq) (rq)->elevator_private @@ -75,78 +88,110 @@ static int cfq_back_penalty = 2; /* penalty of a backwards seek */ #define rb_entry_crq(node) rb_entry((node), struct cfq_rq, rb_node) #define rq_rb_key(rq) (rq)->sector -/* - * threshold for switching off non-tag accounting - */ -#define CFQ_MAX_TAG (4) - -/* - * sort key types and names - */ -enum { - CFQ_KEY_PGID, - CFQ_KEY_TGID, - CFQ_KEY_UID, - CFQ_KEY_GID, - CFQ_KEY_LAST, -}; - -static char *cfq_key_types[] = { "pgid", "tgid", "uid", "gid", NULL }; - static kmem_cache_t *crq_pool; static kmem_cache_t *cfq_pool; static kmem_cache_t *cfq_ioc_pool; +#define CFQ_PRIO_LISTS IOPRIO_BE_NR +#define cfq_class_idle(cfqq) ((cfqq)->ioprio_class == IOPRIO_CLASS_IDLE) +#define cfq_class_be(cfqq) ((cfqq)->ioprio_class == IOPRIO_CLASS_BE) +#define cfq_class_rt(cfqq) ((cfqq)->ioprio_class == IOPRIO_CLASS_RT) + +#define ASYNC (0) +#define SYNC (1) + +#define cfq_cfqq_dispatched(cfqq) \ + ((cfqq)->on_dispatch[ASYNC] + (cfqq)->on_dispatch[SYNC]) + +#define cfq_cfqq_class_sync(cfqq) ((cfqq)->key != CFQ_KEY_ASYNC) + +#define cfq_cfqq_sync(cfqq) \ + (cfq_cfqq_class_sync(cfqq) || (cfqq)->on_dispatch[SYNC]) + +/* + * Per block device queue structure + */ struct cfq_data { - struct list_head rr_list; + atomic_t ref; + request_queue_t *queue; + + /* + * rr list of queues with requests and the count of them + */ + struct list_head rr_list[CFQ_PRIO_LISTS]; + struct list_head busy_rr; + struct list_head cur_rr; + struct list_head idle_rr; + unsigned int busy_queues; + + /* + * non-ordered list of empty cfqq's + */ struct list_head empty_list; + /* + * cfqq lookup hash + */ struct hlist_head *cfq_hash; - struct hlist_head *crq_hash; - /* queues on rr_list (ie they have pending requests */ - unsigned int busy_queues; + /* + * global crq hash for all queues + */ + struct hlist_head *crq_hash; unsigned int max_queued; - atomic_t ref; + mempool_t *crq_pool; - int key_type; + int rq_in_driver; - mempool_t *crq_pool; + /* + * schedule slice state info + */ + /* + * idle window management + */ + struct timer_list idle_slice_timer; + struct work_struct unplug_work; - request_queue_t *queue; + struct cfq_queue *active_queue; + struct cfq_io_context *active_cic; + int cur_prio, cur_end_prio; + unsigned int dispatch_slice; + + struct timer_list idle_class_timer; sector_t last_sector; + unsigned long last_end_request; - int rq_in_driver; + unsigned int rq_starved; /* * tunables, see top of file */ unsigned int cfq_quantum; unsigned int cfq_queued; - unsigned int cfq_fifo_expire_r; - unsigned int cfq_fifo_expire_w; - unsigned int cfq_fifo_batch_expire; + unsigned int cfq_fifo_expire[2]; unsigned int cfq_back_penalty; unsigned int cfq_back_max; - unsigned int find_best_crq; - - unsigned int cfq_tagged; + unsigned int cfq_slice[2]; + unsigned int cfq_slice_async_rq; + unsigned int cfq_slice_idle; + unsigned int cfq_max_depth; }; +/* + * Per process-grouping structure + */ struct cfq_queue { /* reference count */ atomic_t ref; /* parent cfq_data */ struct cfq_data *cfqd; - /* hash of mergeable requests */ + /* cfqq lookup hash */ struct hlist_node cfq_hash; /* hash key */ - unsigned long key; - /* whether queue is on rr (or empty) list */ - int on_rr; + unsigned int key; /* on either rr or empty list of cfqd */ struct list_head cfq_list; /* sorted list of pending requests */ @@ -158,21 +203,22 @@ struct cfq_queue { /* currently allocated requests */ int allocated[2]; /* fifo list of requests in sort_list */ - struct list_head fifo[2]; - /* last time fifo expired */ - unsigned long last_fifo_expire; + struct list_head fifo; - int key_type; + unsigned long slice_start; + unsigned long slice_end; + unsigned long slice_left; + unsigned long service_last; - unsigned long service_start; - unsigned long service_used; + /* number of requests that are on the dispatch list */ + int on_dispatch[2]; - unsigned int max_rate; + /* io prio of this group */ + unsigned short ioprio, org_ioprio; + unsigned short ioprio_class, org_ioprio_class; - /* number of requests that have been handed to the driver */ - int in_flight; - /* number of currently allocated requests */ - int alloc_limit[2]; + /* various state flags, see below */ + unsigned int flags; }; struct cfq_rq { @@ -184,42 +230,79 @@ struct cfq_rq { struct cfq_queue *cfq_queue; struct cfq_io_context *io_context; - unsigned long service_start; - unsigned long queue_start; + unsigned int crq_flags; +}; + +enum cfqq_state_flags { + CFQ_CFQQ_FLAG_on_rr = 0, + CFQ_CFQQ_FLAG_wait_request, + CFQ_CFQQ_FLAG_must_alloc, + CFQ_CFQQ_FLAG_must_alloc_slice, + CFQ_CFQQ_FLAG_must_dispatch, + CFQ_CFQQ_FLAG_fifo_expire, + CFQ_CFQQ_FLAG_idle_window, + CFQ_CFQQ_FLAG_prio_changed, + CFQ_CFQQ_FLAG_expired, +}; - unsigned int in_flight : 1; - unsigned int accounted : 1; - unsigned int is_sync : 1; - unsigned int is_write : 1; +#define CFQ_CFQQ_FNS(name) \ +static inline void cfq_mark_cfqq_##name(struct cfq_queue *cfqq) \ +{ \ + cfqq->flags |= (1 << CFQ_CFQQ_FLAG_##name); \ +} \ +static inline void cfq_clear_cfqq_##name(struct cfq_queue *cfqq) \ +{ \ + cfqq->flags &= ~(1 << CFQ_CFQQ_FLAG_##name); \ +} \ +static inline int cfq_cfqq_##name(const struct cfq_queue *cfqq) \ +{ \ + return (cfqq->flags & (1 << CFQ_CFQQ_FLAG_##name)) != 0; \ +} + +CFQ_CFQQ_FNS(on_rr); +CFQ_CFQQ_FNS(wait_request); +CFQ_CFQQ_FNS(must_alloc); +CFQ_CFQQ_FNS(must_alloc_slice); +CFQ_CFQQ_FNS(must_dispatch); +CFQ_CFQQ_FNS(fifo_expire); +CFQ_CFQQ_FNS(idle_window); +CFQ_CFQQ_FNS(prio_changed); +CFQ_CFQQ_FNS(expired); +#undef CFQ_CFQQ_FNS + +enum cfq_rq_state_flags { + CFQ_CRQ_FLAG_in_flight = 0, + CFQ_CRQ_FLAG_in_driver, + CFQ_CRQ_FLAG_is_sync, + CFQ_CRQ_FLAG_requeued, }; -static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *, unsigned long); +#define CFQ_CRQ_FNS(name) \ +static inline void cfq_mark_crq_##name(struct cfq_rq *crq) \ +{ \ + crq->crq_flags |= (1 << CFQ_CRQ_FLAG_##name); \ +} \ +static inline void cfq_clear_crq_##name(struct cfq_rq *crq) \ +{ \ + crq->crq_flags &= ~(1 << CFQ_CRQ_FLAG_##name); \ +} \ +static inline int cfq_crq_##name(const struct cfq_rq *crq) \ +{ \ + return (crq->crq_flags & (1 << CFQ_CRQ_FLAG_##name)) != 0; \ +} + +CFQ_CRQ_FNS(in_flight); +CFQ_CRQ_FNS(in_driver); +CFQ_CRQ_FNS(is_sync); +CFQ_CRQ_FNS(requeued); +#undef CFQ_CRQ_FNS + +static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *, unsigned int, unsigned short); static void cfq_dispatch_sort(request_queue_t *, struct cfq_rq *); -static void cfq_update_next_crq(struct cfq_rq *); static void cfq_put_cfqd(struct cfq_data *cfqd); +static inline int cfq_pending_requests(struct cfq_data *cfqd); -/* - * what the fairness is based on (ie how processes are grouped and - * differentiated) - */ -static inline unsigned long -cfq_hash_key(struct cfq_data *cfqd, struct task_struct *tsk) -{ - /* - * optimize this so that ->key_type is the offset into the struct - */ - switch (cfqd->key_type) { - case CFQ_KEY_PGID: - return process_group(tsk); - default: - case CFQ_KEY_TGID: - return tsk->tgid; - case CFQ_KEY_UID: - return tsk->uid; - case CFQ_KEY_GID: - return tsk->gid; - } -} +#define process_sync(tsk) ((tsk)->flags & PF_SYNCWRITE) /* * lots of deadline iosched dupes, can be abstracted later... @@ -235,16 +318,12 @@ static void cfq_remove_merge_hints(request_queue_t *q, struct cfq_rq *crq) if (q->last_merge == crq->request) q->last_merge = NULL; - - cfq_update_next_crq(crq); } static inline void cfq_add_crq_hash(struct cfq_data *cfqd, struct cfq_rq *crq) { const int hash_idx = CFQ_MHASH_FN(rq_hash_key(crq->request)); - BUG_ON(!hlist_unhashed(&crq->hash)); - hlist_add_head(&crq->hash, &cfqd->crq_hash[hash_idx]); } @@ -257,8 +336,6 @@ static struct request *cfq_find_rq_hash(struct cfq_data *cfqd, sector_t offset) struct cfq_rq *crq = list_entry_hash(entry); struct request *__rq = crq->request; - BUG_ON(hlist_unhashed(&crq->hash)); - if (!rq_mergeable(__rq)) { cfq_del_crq_hash(crq); continue; @@ -287,36 +364,16 @@ cfq_choose_req(struct cfq_data *cfqd, struct cfq_rq *crq1, struct cfq_rq *crq2) return crq2; if (crq2 == NULL) return crq1; + if (cfq_crq_requeued(crq1)) + return crq1; + if (cfq_crq_requeued(crq2)) + return crq2; s1 = crq1->request->sector; s2 = crq2->request->sector; last = cfqd->last_sector; -#if 0 - if (!list_empty(&cfqd->queue->queue_head)) { - struct list_head *entry = &cfqd->queue->queue_head; - unsigned long distance = ~0UL; - struct request *rq; - - while ((entry = entry->prev) != &cfqd->queue->queue_head) { - rq = list_entry_rq(entry); - - if (blk_barrier_rq(rq)) - break; - - if (distance < abs(s1 - rq->sector + rq->nr_sectors)) { - distance = abs(s1 - rq->sector +rq->nr_sectors); - last = rq->sector + rq->nr_sectors; - } - if (distance < abs(s2 - rq->sector + rq->nr_sectors)) { - distance = abs(s2 - rq->sector +rq->nr_sectors); - last = rq->sector + rq->nr_sectors; - } - } - } -#endif - /* * by definition, 1KiB is 2 sectors */ @@ -377,11 +434,14 @@ cfq_find_next_crq(struct cfq_data *cfqd, struct cfq_queue *cfqq, struct cfq_rq *crq_next = NULL, *crq_prev = NULL; struct rb_node *rbnext, *rbprev; - if (!ON_RB(&last->rb_node)) - return NULL; - - if ((rbnext = rb_next(&last->rb_node)) == NULL) + rbnext = NULL; + if (ON_RB(&last->rb_node)) + rbnext = rb_next(&last->rb_node); + if (!rbnext) { rbnext = rb_first(&cfqq->sort_list); + if (rbnext == &last->rb_node) + rbnext = NULL; + } rbprev = rb_prev(&last->rb_node); @@ -401,67 +461,53 @@ static void cfq_update_next_crq(struct cfq_rq *crq) cfqq->next_crq = cfq_find_next_crq(cfqq->cfqd, cfqq, crq); } -static int cfq_check_sort_rr_list(struct cfq_queue *cfqq) +static void cfq_resort_rr_list(struct cfq_queue *cfqq, int preempted) { - struct list_head *head = &cfqq->cfqd->rr_list; - struct list_head *next, *prev; - - /* - * list might still be ordered - */ - next = cfqq->cfq_list.next; - if (next != head) { - struct cfq_queue *cnext = list_entry_cfqq(next); + struct cfq_data *cfqd = cfqq->cfqd; + struct list_head *list, *entry; - if (cfqq->service_used > cnext->service_used) - return 1; - } + BUG_ON(!cfq_cfqq_on_rr(cfqq)); - prev = cfqq->cfq_list.prev; - if (prev != head) { - struct cfq_queue *cprev = list_entry_cfqq(prev); + list_del(&cfqq->cfq_list); - if (cfqq->service_used < cprev->service_used) - return 1; + if (cfq_class_rt(cfqq)) + list = &cfqd->cur_rr; + else if (cfq_class_idle(cfqq)) + list = &cfqd->idle_rr; + else { + /* + * if cfqq has requests in flight, don't allow it to be + * found in cfq_set_active_queue before it has finished them. + * this is done to increase fairness between a process that + * has lots of io pending vs one that only generates one + * sporadically or synchronously + */ + if (cfq_cfqq_dispatched(cfqq)) + list = &cfqd->busy_rr; + else + list = &cfqd->rr_list[cfqq->ioprio]; } - return 0; -} - -static void cfq_sort_rr_list(struct cfq_queue *cfqq, int new_queue) -{ - struct list_head *entry = &cfqq->cfqd->rr_list; - - if (!cfqq->on_rr) - return; - if (!new_queue && !cfq_check_sort_rr_list(cfqq)) + /* + * if queue was preempted, just add to front to be fair. busy_rr + * isn't sorted. + */ + if (preempted || list == &cfqd->busy_rr) { + list_add(&cfqq->cfq_list, list); return; - - list_del(&cfqq->cfq_list); + } /* - * sort by our mean service_used, sub-sort by in-flight requests + * sort by when queue was last serviced */ - while ((entry = entry->prev) != &cfqq->cfqd->rr_list) { + entry = list; + while ((entry = entry->prev) != list) { struct cfq_queue *__cfqq = list_entry_cfqq(entry); - if (cfqq->service_used > __cfqq->service_used) + if (!__cfqq->service_last) + break; + if (time_before(__cfqq->service_last, cfqq->service_last)) break; - else if (cfqq->service_used == __cfqq->service_used) { - struct list_head *prv; - - while ((prv = entry->prev) != &cfqq->cfqd->rr_list) { - __cfqq = list_entry_cfqq(prv); - - WARN_ON(__cfqq->service_used > cfqq->service_used); - if (cfqq->service_used != __cfqq->service_used) - break; - if (cfqq->in_flight > __cfqq->in_flight) - break; - - entry = prv; - } - } } list_add(&cfqq->cfq_list, entry); @@ -469,28 +515,24 @@ static void cfq_sort_rr_list(struct cfq_queue *cfqq, int new_queue) /* * add to busy list of queues for service, trying to be fair in ordering - * the pending list according to requests serviced + * the pending list according to last request service */ static inline void -cfq_add_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq) +cfq_add_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq, int requeue) { - /* - * it's currently on the empty list - */ - cfqq->on_rr = 1; + BUG_ON(cfq_cfqq_on_rr(cfqq)); + cfq_mark_cfqq_on_rr(cfqq); cfqd->busy_queues++; - if (time_after(jiffies, cfqq->service_start + cfq_service)) - cfqq->service_used >>= 3; - - cfq_sort_rr_list(cfqq, 1); + cfq_resort_rr_list(cfqq, requeue); } static inline void cfq_del_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq) { + BUG_ON(!cfq_cfqq_on_rr(cfqq)); + cfq_clear_cfqq_on_rr(cfqq); list_move(&cfqq->cfq_list, &cfqd->empty_list); - cfqq->on_rr = 0; BUG_ON(!cfqd->busy_queues); cfqd->busy_queues--; @@ -505,16 +547,17 @@ static inline void cfq_del_crq_rb(struct cfq_rq *crq) if (ON_RB(&crq->rb_node)) { struct cfq_data *cfqd = cfqq->cfqd; + const int sync = cfq_crq_is_sync(crq); - BUG_ON(!cfqq->queued[crq->is_sync]); + BUG_ON(!cfqq->queued[sync]); + cfqq->queued[sync]--; cfq_update_next_crq(crq); - cfqq->queued[crq->is_sync]--; rb_erase(&crq->rb_node, &cfqq->sort_list); RB_CLEAR_COLOR(&crq->rb_node); - if (RB_EMPTY(&cfqq->sort_list) && cfqq->on_rr) + if (cfq_cfqq_on_rr(cfqq) && RB_EMPTY(&cfqq->sort_list)) cfq_del_cfqq_rr(cfqd, cfqq); } } @@ -550,7 +593,7 @@ static void cfq_add_crq_rb(struct cfq_rq *crq) struct cfq_rq *__alias; crq->rb_key = rq_rb_key(rq); - cfqq->queued[crq->is_sync]++; + cfqq->queued[cfq_crq_is_sync(crq)]++; /* * looks a little odd, but the first insert might return an alias. @@ -561,8 +604,8 @@ static void cfq_add_crq_rb(struct cfq_rq *crq) rb_insert_color(&crq->rb_node, &cfqq->sort_list); - if (!cfqq->on_rr) - cfq_add_cfqq_rr(cfqd, cfqq); + if (!cfq_cfqq_on_rr(cfqq)) + cfq_add_cfqq_rr(cfqd, cfqq, cfq_crq_requeued(crq)); /* * check if this request is a better next-serve candidate @@ -575,17 +618,16 @@ cfq_reposition_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq) { if (ON_RB(&crq->rb_node)) { rb_erase(&crq->rb_node, &cfqq->sort_list); - cfqq->queued[crq->is_sync]--; + cfqq->queued[cfq_crq_is_sync(crq)]--; } cfq_add_crq_rb(crq); } -static struct request * -cfq_find_rq_rb(struct cfq_data *cfqd, sector_t sector) +static struct request *cfq_find_rq_rb(struct cfq_data *cfqd, sector_t sector) + { - const unsigned long key = cfq_hash_key(cfqd, current); - struct cfq_queue *cfqq = cfq_find_cfq_hash(cfqd, key); + struct cfq_queue *cfqq = cfq_find_cfq_hash(cfqd, current->pid, CFQ_KEY_ANY); struct rb_node *n; if (!cfqq) @@ -609,20 +651,25 @@ out: static void cfq_deactivate_request(request_queue_t *q, struct request *rq) { + struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_rq *crq = RQ_DATA(rq); if (crq) { struct cfq_queue *cfqq = crq->cfq_queue; - if (cfqq->cfqd->cfq_tagged) { - cfqq->service_used--; - cfq_sort_rr_list(cfqq, 0); + if (cfq_crq_in_driver(crq)) { + cfq_clear_crq_in_driver(crq); + WARN_ON(!cfqd->rq_in_driver); + cfqd->rq_in_driver--; } + if (cfq_crq_in_flight(crq)) { + const int sync = cfq_crq_is_sync(crq); - if (crq->accounted) { - crq->accounted = 0; - cfqq->cfqd->rq_in_driver--; + cfq_clear_crq_in_flight(crq); + WARN_ON(!cfqq->on_dispatch[sync]); + cfqq->on_dispatch[sync]--; } + cfq_mark_crq_requeued(crq); } } @@ -640,11 +687,10 @@ static void cfq_remove_request(request_queue_t *q, struct request *rq) struct cfq_rq *crq = RQ_DATA(rq); if (crq) { - cfq_remove_merge_hints(q, crq); list_del_init(&rq->queuelist); + cfq_del_crq_rb(crq); + cfq_remove_merge_hints(q, crq); - if (crq->cfq_queue) - cfq_del_crq_rb(crq); } } @@ -662,21 +708,15 @@ cfq_merge(request_queue_t *q, struct request **req, struct bio *bio) } __rq = cfq_find_rq_hash(cfqd, bio->bi_sector); - if (__rq) { - BUG_ON(__rq->sector + __rq->nr_sectors != bio->bi_sector); - - if (elv_rq_merge_ok(__rq, bio)) { - ret = ELEVATOR_BACK_MERGE; - goto out; - } + if (__rq && elv_rq_merge_ok(__rq, bio)) { + ret = ELEVATOR_BACK_MERGE; + goto out; } __rq = cfq_find_rq_rb(cfqd, bio->bi_sector + bio_sectors(bio)); - if (__rq) { - if (elv_rq_merge_ok(__rq, bio)) { - ret = ELEVATOR_FRONT_MERGE; - goto out; - } + if (__rq && elv_rq_merge_ok(__rq, bio)) { + ret = ELEVATOR_FRONT_MERGE; + goto out; } return ELEVATOR_NO_MERGE; @@ -709,20 +749,220 @@ static void cfq_merged_requests(request_queue_t *q, struct request *rq, struct request *next) { - struct cfq_rq *crq = RQ_DATA(rq); - struct cfq_rq *cnext = RQ_DATA(next); - cfq_merged_request(q, rq); - if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist)) { - if (time_before(cnext->queue_start, crq->queue_start)) { - list_move(&rq->queuelist, &next->queuelist); - crq->queue_start = cnext->queue_start; + /* + * reposition in fifo if next is older than rq + */ + if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist) && + time_before(next->start_time, rq->start_time)) + list_move(&rq->queuelist, &next->queuelist); + + cfq_remove_request(q, next); +} + +static inline void +__cfq_set_active_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + if (cfqq) { + /* + * stop potential idle class queues waiting service + */ + del_timer(&cfqd->idle_class_timer); + + cfqq->slice_start = jiffies; + cfqq->slice_end = 0; + cfqq->slice_left = 0; + cfq_clear_cfqq_must_alloc_slice(cfqq); + cfq_clear_cfqq_fifo_expire(cfqq); + cfq_clear_cfqq_expired(cfqq); + } + + cfqd->active_queue = cfqq; +} + +/* + * 0 + * 0,1 + * 0,1,2 + * 0,1,2,3 + * 0,1,2,3,4 + * 0,1,2,3,4,5 + * 0,1,2,3,4,5,6 + * 0,1,2,3,4,5,6,7 + */ +static int cfq_get_next_prio_level(struct cfq_data *cfqd) +{ + int prio, wrap; + + prio = -1; + wrap = 0; + do { + int p; + + for (p = cfqd->cur_prio; p <= cfqd->cur_end_prio; p++) { + if (!list_empty(&cfqd->rr_list[p])) { + prio = p; + break; + } + } + + if (prio != -1) + break; + cfqd->cur_prio = 0; + if (++cfqd->cur_end_prio == CFQ_PRIO_LISTS) { + cfqd->cur_end_prio = 0; + if (wrap) + break; + wrap = 1; } + } while (1); + + if (unlikely(prio == -1)) + return -1; + + BUG_ON(prio >= CFQ_PRIO_LISTS); + + list_splice_init(&cfqd->rr_list[prio], &cfqd->cur_rr); + + cfqd->cur_prio = prio + 1; + if (cfqd->cur_prio > cfqd->cur_end_prio) { + cfqd->cur_end_prio = cfqd->cur_prio; + cfqd->cur_prio = 0; + } + if (cfqd->cur_end_prio == CFQ_PRIO_LISTS) { + cfqd->cur_prio = 0; + cfqd->cur_end_prio = 0; } - cfq_update_next_crq(cnext); - cfq_remove_request(q, next); + return prio; +} + +static struct cfq_queue *cfq_set_active_queue(struct cfq_data *cfqd) +{ + struct cfq_queue *cfqq; + + /* + * if current queue is expired but not done with its requests yet, + * wait for that to happen + */ + if ((cfqq = cfqd->active_queue) != NULL) { + if (cfq_cfqq_expired(cfqq) && cfq_cfqq_dispatched(cfqq)) + return NULL; + } + + /* + * if current list is non-empty, grab first entry. if it is empty, + * get next prio level and grab first entry then if any are spliced + */ + if (!list_empty(&cfqd->cur_rr) || cfq_get_next_prio_level(cfqd) != -1) + cfqq = list_entry_cfqq(cfqd->cur_rr.next); + + /* + * if we have idle queues and no rt or be queues had pending + * requests, either allow immediate service if the grace period + * has passed or arm the idle grace timer + */ + if (!cfqq && !list_empty(&cfqd->idle_rr)) { + unsigned long end = cfqd->last_end_request + CFQ_IDLE_GRACE; + + if (time_after_eq(jiffies, end)) + cfqq = list_entry_cfqq(cfqd->idle_rr.next); + else + mod_timer(&cfqd->idle_class_timer, end); + } + + __cfq_set_active_queue(cfqd, cfqq); + return cfqq; +} + +/* + * current cfqq expired its slice (or was too idle), select new one + */ +static void +__cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq, + int preempted) +{ + unsigned long now = jiffies; + + if (cfq_cfqq_wait_request(cfqq)) + del_timer(&cfqd->idle_slice_timer); + + if (!preempted && !cfq_cfqq_dispatched(cfqq)) + cfqq->service_last = now; + + cfq_clear_cfqq_must_dispatch(cfqq); + cfq_clear_cfqq_wait_request(cfqq); + + /* + * store what was left of this slice, if the queue idled out + * or was preempted + */ + if (time_after(now, cfqq->slice_end)) + cfqq->slice_left = now - cfqq->slice_end; + else + cfqq->slice_left = 0; + + if (cfq_cfqq_on_rr(cfqq)) + cfq_resort_rr_list(cfqq, preempted); + + if (cfqq == cfqd->active_queue) + cfqd->active_queue = NULL; + + if (cfqd->active_cic) { + put_io_context(cfqd->active_cic->ioc); + cfqd->active_cic = NULL; + } + + cfqd->dispatch_slice = 0; +} + +static inline void cfq_slice_expired(struct cfq_data *cfqd, int preempted) +{ + struct cfq_queue *cfqq = cfqd->active_queue; + + if (cfqq) { + /* + * use deferred expiry, if there are requests in progress as + * not to disturb the slice of the next queue + */ + if (cfq_cfqq_dispatched(cfqq)) + cfq_mark_cfqq_expired(cfqq); + else + __cfq_slice_expired(cfqd, cfqq, preempted); + } +} + +static int cfq_arm_slice_timer(struct cfq_data *cfqd, struct cfq_queue *cfqq) + +{ + WARN_ON(!RB_EMPTY(&cfqq->sort_list)); + WARN_ON(cfqq != cfqd->active_queue); + + /* + * idle is disabled, either manually or by past process history + */ + if (!cfqd->cfq_slice_idle) + return 0; + if (!cfq_cfqq_idle_window(cfqq)) + return 0; + /* + * task has exited, don't wait + */ + if (cfqd->active_cic && !cfqd->active_cic->ioc->task) + return 0; + + cfq_mark_cfqq_must_dispatch(cfqq); + cfq_mark_cfqq_wait_request(cfqq); + + if (!timer_pending(&cfqd->idle_slice_timer)) { + unsigned long slice_left = min(cfqq->slice_end - 1, (unsigned long) cfqd->cfq_slice_idle); + + cfqd->idle_slice_timer.expires = jiffies + slice_left; + add_timer(&cfqd->idle_slice_timer); + } + + return 1; } /* @@ -738,31 +978,40 @@ static void cfq_dispatch_sort(request_queue_t *q, struct cfq_rq *crq) struct request *__rq; sector_t last; - cfq_del_crq_rb(crq); - cfq_remove_merge_hints(q, crq); list_del(&crq->request->queuelist); last = cfqd->last_sector; - while ((entry = entry->prev) != head) { - __rq = list_entry_rq(entry); + list_for_each_entry_reverse(__rq, head, queuelist) { + struct cfq_rq *__crq = RQ_DATA(__rq); - if (blk_barrier_rq(crq->request)) + if (blk_barrier_rq(__rq)) + break; + if (!blk_fs_request(__rq)) break; - if (!blk_fs_request(crq->request)) + if (cfq_crq_requeued(__crq)) break; - if (crq->request->sector > __rq->sector) + if (__rq->sector <= crq->request->sector) break; if (__rq->sector > last && crq->request->sector < last) { - last = crq->request->sector; + last = crq->request->sector + crq->request->nr_sectors; break; } + entry = &__rq->queuelist; } cfqd->last_sector = last; - crq->in_flight = 1; - cfqq->in_flight++; - list_add(&crq->request->queuelist, entry); + + cfqq->next_crq = cfq_find_next_crq(cfqd, cfqq, crq); + + cfq_del_crq_rb(crq); + cfq_remove_merge_hints(q, crq); + + cfq_mark_crq_in_flight(crq); + cfq_clear_crq_requeued(crq); + + cfqq->on_dispatch[cfq_crq_is_sync(crq)]++; + list_add_tail(&crq->request->queuelist, entry); } /* @@ -771,173 +1020,235 @@ static void cfq_dispatch_sort(request_queue_t *q, struct cfq_rq *crq) static inline struct cfq_rq *cfq_check_fifo(struct cfq_queue *cfqq) { struct cfq_data *cfqd = cfqq->cfqd; - const int reads = !list_empty(&cfqq->fifo[0]); - const int writes = !list_empty(&cfqq->fifo[1]); - unsigned long now = jiffies; + struct request *rq; struct cfq_rq *crq; - if (time_before(now, cfqq->last_fifo_expire + cfqd->cfq_fifo_batch_expire)) + if (cfq_cfqq_fifo_expire(cfqq)) return NULL; - crq = RQ_DATA(list_entry(cfqq->fifo[0].next, struct request, queuelist)); - if (reads && time_after(now, crq->queue_start + cfqd->cfq_fifo_expire_r)) { - cfqq->last_fifo_expire = now; - return crq; - } + if (!list_empty(&cfqq->fifo)) { + int fifo = cfq_cfqq_class_sync(cfqq); - crq = RQ_DATA(list_entry(cfqq->fifo[1].next, struct request, queuelist)); - if (writes && time_after(now, crq->queue_start + cfqd->cfq_fifo_expire_w)) { - cfqq->last_fifo_expire = now; - return crq; + crq = RQ_DATA(list_entry_fifo(cfqq->fifo.next)); + rq = crq->request; + if (time_after(jiffies, rq->start_time + cfqd->cfq_fifo_expire[fifo])) { + cfq_mark_cfqq_fifo_expire(cfqq); + return crq; + } } return NULL; } /* - * dispatch a single request from given queue + * Scale schedule slice based on io priority. Use the sync time slice only + * if a queue is marked sync and has sync io queued. A sync queue with async + * io only, should not get full sync slice length. */ +static inline int +cfq_prio_to_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + const int base_slice = cfqd->cfq_slice[cfq_cfqq_sync(cfqq)]; + + WARN_ON(cfqq->ioprio >= IOPRIO_BE_NR); + + return base_slice + (base_slice/CFQ_SLICE_SCALE * (4 - cfqq->ioprio)); +} + static inline void -cfq_dispatch_request(request_queue_t *q, struct cfq_data *cfqd, - struct cfq_queue *cfqq) +cfq_set_prio_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq) { - struct cfq_rq *crq; + cfqq->slice_end = cfq_prio_to_slice(cfqd, cfqq) + jiffies; +} + +static inline int +cfq_prio_to_maxrq(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + const int base_rq = cfqd->cfq_slice_async_rq; + + WARN_ON(cfqq->ioprio >= IOPRIO_BE_NR); + + return 2 * (base_rq + base_rq * (CFQ_PRIO_LISTS - 1 - cfqq->ioprio)); +} + +/* + * scheduler run of queue, if there are requests pending and no one in the + * driver that will restart queueing + */ +static inline void cfq_schedule_dispatch(struct cfq_data *cfqd) +{ + if (!cfqd->rq_in_driver && cfq_pending_requests(cfqd)) + kblockd_schedule_work(&cfqd->unplug_work); +} + +/* + * get next queue for service + */ +static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd, int force) +{ + unsigned long now = jiffies; + struct cfq_queue *cfqq; + + cfqq = cfqd->active_queue; + if (!cfqq) + goto new_queue; + + if (cfq_cfqq_expired(cfqq)) + goto new_queue; /* - * follow expired path, else get first next available + * slice has expired */ - if ((crq = cfq_check_fifo(cfqq)) == NULL) { - if (cfqd->find_best_crq) - crq = cfqq->next_crq; - else - crq = rb_entry_crq(rb_first(&cfqq->sort_list)); - } - - cfqd->last_sector = crq->request->sector + crq->request->nr_sectors; + if (!cfq_cfqq_must_dispatch(cfqq) && time_after(now, cfqq->slice_end)) + goto expire; /* - * finally, insert request into driver list + * if queue has requests, dispatch one. if not, check if + * enough slice is left to wait for one */ - cfq_dispatch_sort(q, crq); + if (!RB_EMPTY(&cfqq->sort_list)) + goto keep_queue; + else if (!force && cfq_cfqq_class_sync(cfqq) && + time_before(now, cfqq->slice_end)) { + if (cfq_arm_slice_timer(cfqd, cfqq)) + return NULL; + } + +expire: + cfq_slice_expired(cfqd, 0); +new_queue: + cfqq = cfq_set_active_queue(cfqd); +keep_queue: + return cfqq; } -static int cfq_dispatch_requests(request_queue_t *q, int max_dispatch) +static int +__cfq_dispatch_requests(struct cfq_data *cfqd, struct cfq_queue *cfqq, + int max_dispatch) { - struct cfq_data *cfqd = q->elevator->elevator_data; - struct cfq_queue *cfqq; - struct list_head *entry, *tmp; - int queued, busy_queues, first_round; + int dispatched = 0; - if (list_empty(&cfqd->rr_list)) - return 0; + BUG_ON(RB_EMPTY(&cfqq->sort_list)); - queued = 0; - first_round = 1; -restart: - busy_queues = 0; - list_for_each_safe(entry, tmp, &cfqd->rr_list) { - cfqq = list_entry_cfqq(entry); + do { + struct cfq_rq *crq; - BUG_ON(RB_EMPTY(&cfqq->sort_list)); + /* + * follow expired path, else get first next available + */ + if ((crq = cfq_check_fifo(cfqq)) == NULL) + crq = cfqq->next_crq; /* - * first round of queueing, only select from queues that - * don't already have io in-flight + * finally, insert request into driver dispatch list */ - if (first_round && cfqq->in_flight) - continue; + cfq_dispatch_sort(cfqd->queue, crq); + + cfqd->dispatch_slice++; + dispatched++; - cfq_dispatch_request(q, cfqd, cfqq); + if (!cfqd->active_cic) { + atomic_inc(&crq->io_context->ioc->refcount); + cfqd->active_cic = crq->io_context; + } - if (!RB_EMPTY(&cfqq->sort_list)) - busy_queues++; + if (RB_EMPTY(&cfqq->sort_list)) + break; - queued++; - } + } while (dispatched < max_dispatch); + + /* + * if slice end isn't set yet, set it. if at least one request was + * sync, use the sync time slice value + */ + if (!cfqq->slice_end) + cfq_set_prio_slice(cfqd, cfqq); + + /* + * expire an async queue immediately if it has used up its slice. idle + * queue always expire after 1 dispatch round. + */ + if ((!cfq_cfqq_sync(cfqq) && + cfqd->dispatch_slice >= cfq_prio_to_maxrq(cfqd, cfqq)) || + cfq_class_idle(cfqq)) + cfq_slice_expired(cfqd, 0); + + return dispatched; +} + +static int +cfq_dispatch_requests(request_queue_t *q, int max_dispatch, int force) +{ + struct cfq_data *cfqd = q->elevator->elevator_data; + struct cfq_queue *cfqq; + + if (!cfqd->busy_queues) + return 0; - if ((queued < max_dispatch) && (busy_queues || first_round)) { - first_round = 0; - goto restart; + cfqq = cfq_select_queue(cfqd, force); + if (cfqq) { + cfq_clear_cfqq_must_dispatch(cfqq); + cfq_clear_cfqq_wait_request(cfqq); + del_timer(&cfqd->idle_slice_timer); + + if (cfq_class_idle(cfqq)) + max_dispatch = 1; + + return __cfq_dispatch_requests(cfqd, cfqq, max_dispatch); } - return queued; + return 0; } static inline void cfq_account_dispatch(struct cfq_rq *crq) { struct cfq_queue *cfqq = crq->cfq_queue; struct cfq_data *cfqd = cfqq->cfqd; - unsigned long now, elapsed; - if (!blk_fs_request(crq->request)) + if (unlikely(!blk_fs_request(crq->request))) return; /* * accounted bit is necessary since some drivers will call * elv_next_request() many times for the same request (eg ide) */ - if (crq->accounted) + if (cfq_crq_in_driver(crq)) return; - now = jiffies; - if (cfqq->service_start == ~0UL) - cfqq->service_start = now; - - /* - * on drives with tagged command queueing, command turn-around time - * doesn't necessarily reflect the time spent processing this very - * command inside the drive. so do the accounting differently there, - * by just sorting on the number of requests - */ - if (cfqd->cfq_tagged) { - if (time_after(now, cfqq->service_start + cfq_service)) { - cfqq->service_start = now; - cfqq->service_used /= 10; - } - - cfqq->service_used++; - cfq_sort_rr_list(cfqq, 0); - } - - elapsed = now - crq->queue_start; - if (elapsed > max_elapsed_dispatch) - max_elapsed_dispatch = elapsed; - - crq->accounted = 1; - crq->service_start = now; - - if (++cfqd->rq_in_driver >= CFQ_MAX_TAG && !cfqd->cfq_tagged) { - cfqq->cfqd->cfq_tagged = 1; - printk("cfq: depth %d reached, tagging now on\n", CFQ_MAX_TAG); - } + cfq_mark_crq_in_driver(crq); + cfqd->rq_in_driver++; } static inline void cfq_account_completion(struct cfq_queue *cfqq, struct cfq_rq *crq) { struct cfq_data *cfqd = cfqq->cfqd; + unsigned long now; - if (!crq->accounted) + if (!cfq_crq_in_driver(crq)) return; + now = jiffies; + WARN_ON(!cfqd->rq_in_driver); cfqd->rq_in_driver--; - if (!cfqd->cfq_tagged) { - unsigned long now = jiffies; - unsigned long duration = now - crq->service_start; + if (!cfq_class_idle(cfqq)) + cfqd->last_end_request = now; - if (time_after(now, cfqq->service_start + cfq_service)) { - cfqq->service_start = now; - cfqq->service_used >>= 3; + if (!cfq_cfqq_dispatched(cfqq)) { + if (cfq_cfqq_on_rr(cfqq)) { + cfqq->service_last = now; + cfq_resort_rr_list(cfqq, 0); + } + if (cfq_cfqq_expired(cfqq)) { + __cfq_slice_expired(cfqd, cfqq, 0); + cfq_schedule_dispatch(cfqd); } - - cfqq->service_used += duration; - cfq_sort_rr_list(cfqq, 0); - - if (duration > max_elapsed_crq) - max_elapsed_crq = duration; } + + if (cfq_crq_is_sync(crq)) + crq->io_context->last_end_request = now; } static struct request *cfq_next_request(request_queue_t *q) @@ -950,7 +1261,18 @@ static struct request *cfq_next_request(request_queue_t *q) dispatch: rq = list_entry_rq(q->queue_head.next); - if ((crq = RQ_DATA(rq)) != NULL) { + crq = RQ_DATA(rq); + if (crq) { + struct cfq_queue *cfqq = crq->cfq_queue; + + /* + * if idle window is disabled, allow queue buildup + */ + if (!cfq_crq_in_driver(crq) && + !cfq_cfqq_idle_window(cfqq) && + cfqd->rq_in_driver >= cfqd->cfq_max_depth) + return NULL; + cfq_remove_merge_hints(q, crq); cfq_account_dispatch(crq); } @@ -958,7 +1280,7 @@ dispatch: return rq; } - if (cfq_dispatch_requests(q, cfqd->cfq_quantum)) + if (cfq_dispatch_requests(q, cfqd->cfq_quantum, 0)) goto dispatch; return NULL; @@ -972,13 +1294,21 @@ dispatch: */ static void cfq_put_queue(struct cfq_queue *cfqq) { - BUG_ON(!atomic_read(&cfqq->ref)); + struct cfq_data *cfqd = cfqq->cfqd; + + BUG_ON(atomic_read(&cfqq->ref) <= 0); if (!atomic_dec_and_test(&cfqq->ref)) return; BUG_ON(rb_first(&cfqq->sort_list)); - BUG_ON(cfqq->on_rr); + BUG_ON(cfqq->allocated[READ] + cfqq->allocated[WRITE]); + BUG_ON(cfq_cfqq_on_rr(cfqq)); + + if (unlikely(cfqd->active_queue == cfqq)) { + __cfq_slice_expired(cfqd, cfqq, 0); + cfq_schedule_dispatch(cfqd); + } cfq_put_cfqd(cfqq->cfqd); @@ -991,15 +1321,17 @@ static void cfq_put_queue(struct cfq_queue *cfqq) } static inline struct cfq_queue * -__cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned long key, const int hashval) +__cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned int key, unsigned int prio, + const int hashval) { struct hlist_head *hash_list = &cfqd->cfq_hash[hashval]; struct hlist_node *entry, *next; hlist_for_each_safe(entry, next, hash_list) { struct cfq_queue *__cfqq = list_entry_qhash(entry); + const unsigned short __p = IOPRIO_PRIO_VALUE(__cfqq->ioprio_class, __cfqq->ioprio); - if (__cfqq->key == key) + if (__cfqq->key == key && (__p == prio || prio == CFQ_KEY_ANY)) return __cfqq; } @@ -1007,94 +1339,220 @@ __cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned long key, const int hashval) } static struct cfq_queue * -cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned long key) +cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned int key, unsigned short prio) { - return __cfq_find_cfq_hash(cfqd, key, hash_long(key, CFQ_QHASH_SHIFT)); + return __cfq_find_cfq_hash(cfqd, key, prio, hash_long(key, CFQ_QHASH_SHIFT)); } -static inline void -cfq_rehash_cfqq(struct cfq_data *cfqd, struct cfq_queue **cfqq, - struct cfq_io_context *cic) +static void cfq_free_io_context(struct cfq_io_context *cic) { - unsigned long hashkey = cfq_hash_key(cfqd, current); - unsigned long hashval = hash_long(hashkey, CFQ_QHASH_SHIFT); - struct cfq_queue *__cfqq; - unsigned long flags; - - spin_lock_irqsave(cfqd->queue->queue_lock, flags); + struct cfq_io_context *__cic; + struct list_head *entry, *next; - hlist_del(&(*cfqq)->cfq_hash); - - __cfqq = __cfq_find_cfq_hash(cfqd, hashkey, hashval); - if (!__cfqq || __cfqq == *cfqq) { - __cfqq = *cfqq; - hlist_add_head(&__cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); - __cfqq->key_type = cfqd->key_type; - } else { - atomic_inc(&__cfqq->ref); - cic->cfqq = __cfqq; - cfq_put_queue(*cfqq); - *cfqq = __cfqq; + list_for_each_safe(entry, next, &cic->list) { + __cic = list_entry(entry, struct cfq_io_context, list); + kmem_cache_free(cfq_ioc_pool, __cic); } - cic->cfqq = __cfqq; - spin_unlock_irqrestore(cfqd->queue->queue_lock, flags); + kmem_cache_free(cfq_ioc_pool, cic); } -static void cfq_free_io_context(struct cfq_io_context *cic) +/* + * Called with interrupts disabled + */ +static void cfq_exit_single_io_context(struct cfq_io_context *cic) { - kmem_cache_free(cfq_ioc_pool, cic); + struct cfq_data *cfqd = cic->cfqq->cfqd; + request_queue_t *q = cfqd->queue; + + WARN_ON(!irqs_disabled()); + + spin_lock(q->queue_lock); + + if (unlikely(cic->cfqq == cfqd->active_queue)) { + __cfq_slice_expired(cfqd, cic->cfqq, 0); + cfq_schedule_dispatch(cfqd); + } + + cfq_put_queue(cic->cfqq); + cic->cfqq = NULL; + spin_unlock(q->queue_lock); } /* - * locking hierarchy is: io_context lock -> queue locks + * Another task may update the task cic list, if it is doing a queue lookup + * on its behalf. cfq_cic_lock excludes such concurrent updates */ static void cfq_exit_io_context(struct cfq_io_context *cic) { - struct cfq_queue *cfqq = cic->cfqq; - struct list_head *entry = &cic->list; - request_queue_t *q; + struct cfq_io_context *__cic; + struct list_head *entry; unsigned long flags; + local_irq_save(flags); + /* * put the reference this task is holding to the various queues */ - spin_lock_irqsave(&cic->ioc->lock, flags); - while ((entry = cic->list.next) != &cic->list) { - struct cfq_io_context *__cic; - + list_for_each(entry, &cic->list) { __cic = list_entry(entry, struct cfq_io_context, list); - list_del(entry); - - q = __cic->cfqq->cfqd->queue; - spin_lock(q->queue_lock); - cfq_put_queue(__cic->cfqq); - spin_unlock(q->queue_lock); + cfq_exit_single_io_context(__cic); } - q = cfqq->cfqd->queue; - spin_lock(q->queue_lock); - cfq_put_queue(cfqq); - spin_unlock(q->queue_lock); - - cic->cfqq = NULL; - spin_unlock_irqrestore(&cic->ioc->lock, flags); + cfq_exit_single_io_context(cic); + local_irq_restore(flags); } -static struct cfq_io_context *cfq_alloc_io_context(int gfp_flags) +static struct cfq_io_context * +cfq_alloc_io_context(struct cfq_data *cfqd, int gfp_mask) { - struct cfq_io_context *cic = kmem_cache_alloc(cfq_ioc_pool, gfp_flags); + struct cfq_io_context *cic = kmem_cache_alloc(cfq_ioc_pool, gfp_mask); if (cic) { - cic->dtor = cfq_free_io_context; - cic->exit = cfq_exit_io_context; INIT_LIST_HEAD(&cic->list); cic->cfqq = NULL; + cic->key = NULL; + cic->last_end_request = jiffies; + cic->ttime_total = 0; + cic->ttime_samples = 0; + cic->ttime_mean = 0; + cic->dtor = cfq_free_io_context; + cic->exit = cfq_exit_io_context; } return cic; } +static void cfq_init_prio_data(struct cfq_queue *cfqq) +{ + struct task_struct *tsk = current; + int ioprio_class; + + if (!cfq_cfqq_prio_changed(cfqq)) + return; + + ioprio_class = IOPRIO_PRIO_CLASS(tsk->ioprio); + switch (ioprio_class) { + default: + printk(KERN_ERR "cfq: bad prio %x\n", ioprio_class); + case IOPRIO_CLASS_NONE: + /* + * no prio set, place us in the middle of the BE classes + */ + cfqq->ioprio = task_nice_ioprio(tsk); + cfqq->ioprio_class = IOPRIO_CLASS_BE; + break; + case IOPRIO_CLASS_RT: + cfqq->ioprio = task_ioprio(tsk); + cfqq->ioprio_class = IOPRIO_CLASS_RT; + break; + case IOPRIO_CLASS_BE: + cfqq->ioprio = task_ioprio(tsk); + cfqq->ioprio_class = IOPRIO_CLASS_BE; + break; + case IOPRIO_CLASS_IDLE: + cfqq->ioprio_class = IOPRIO_CLASS_IDLE; + cfqq->ioprio = 7; + cfq_clear_cfqq_idle_window(cfqq); + break; + } + + /* + * keep track of original prio settings in case we have to temporarily + * elevate the priority of this queue + */ + cfqq->org_ioprio = cfqq->ioprio; + cfqq->org_ioprio_class = cfqq->ioprio_class; + + if (cfq_cfqq_on_rr(cfqq)) + cfq_resort_rr_list(cfqq, 0); + + cfq_clear_cfqq_prio_changed(cfqq); +} + +static inline void changed_ioprio(struct cfq_queue *cfqq) +{ + if (cfqq) { + struct cfq_data *cfqd = cfqq->cfqd; + + spin_lock(cfqd->queue->queue_lock); + cfq_mark_cfqq_prio_changed(cfqq); + cfq_init_prio_data(cfqq); + spin_unlock(cfqd->queue->queue_lock); + } +} + +/* + * callback from sys_ioprio_set, irqs are disabled + */ +static int cfq_ioc_set_ioprio(struct io_context *ioc, unsigned int ioprio) +{ + struct cfq_io_context *cic = ioc->cic; + + changed_ioprio(cic->cfqq); + + list_for_each_entry(cic, &cic->list, list) + changed_ioprio(cic->cfqq); + + return 0; +} + +static struct cfq_queue * +cfq_get_queue(struct cfq_data *cfqd, unsigned int key, unsigned short ioprio, + int gfp_mask) +{ + const int hashval = hash_long(key, CFQ_QHASH_SHIFT); + struct cfq_queue *cfqq, *new_cfqq = NULL; + +retry: + cfqq = __cfq_find_cfq_hash(cfqd, key, ioprio, hashval); + + if (!cfqq) { + if (new_cfqq) { + cfqq = new_cfqq; + new_cfqq = NULL; + } else if (gfp_mask & __GFP_WAIT) { + spin_unlock_irq(cfqd->queue->queue_lock); + new_cfqq = kmem_cache_alloc(cfq_pool, gfp_mask); + spin_lock_irq(cfqd->queue->queue_lock); + goto retry; + } else { + cfqq = kmem_cache_alloc(cfq_pool, gfp_mask); + if (!cfqq) + goto out; + } + + memset(cfqq, 0, sizeof(*cfqq)); + + INIT_HLIST_NODE(&cfqq->cfq_hash); + INIT_LIST_HEAD(&cfqq->cfq_list); + RB_CLEAR_ROOT(&cfqq->sort_list); + INIT_LIST_HEAD(&cfqq->fifo); + + cfqq->key = key; + hlist_add_head(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); + atomic_set(&cfqq->ref, 0); + cfqq->cfqd = cfqd; + atomic_inc(&cfqd->ref); + cfqq->service_last = 0; + /* + * set ->slice_left to allow preemption for a new process + */ + cfqq->slice_left = 2 * cfqd->cfq_slice_idle; + cfq_mark_cfqq_idle_window(cfqq); + cfq_mark_cfqq_prio_changed(cfqq); + cfq_init_prio_data(cfqq); + } + + if (new_cfqq) + kmem_cache_free(cfq_pool, new_cfqq); + + atomic_inc(&cfqq->ref); +out: + WARN_ON((gfp_mask & __GFP_WAIT) && !cfqq); + return cfqq; +} + /* * Setup general io context and cfq io context. There can be several cfq * io contexts per general io context, if this process is doing io to more @@ -1102,39 +1560,39 @@ static struct cfq_io_context *cfq_alloc_io_context(int gfp_flags) * cfqq, so we don't need to worry about it disappearing */ static struct cfq_io_context * -cfq_get_io_context(struct cfq_queue **cfqq, int gfp_flags) +cfq_get_io_context(struct cfq_data *cfqd, pid_t pid, int gfp_mask) { - struct cfq_data *cfqd = (*cfqq)->cfqd; - struct cfq_queue *__cfqq = *cfqq; + struct io_context *ioc = NULL; struct cfq_io_context *cic; - struct io_context *ioc; - might_sleep_if(gfp_flags & __GFP_WAIT); + might_sleep_if(gfp_mask & __GFP_WAIT); - ioc = get_io_context(gfp_flags); + ioc = get_io_context(gfp_mask); if (!ioc) return NULL; if ((cic = ioc->cic) == NULL) { - cic = cfq_alloc_io_context(gfp_flags); + cic = cfq_alloc_io_context(cfqd, gfp_mask); if (cic == NULL) goto err; + /* + * manually increment generic io_context usage count, it + * cannot go away since we are already holding one ref to it + */ ioc->cic = cic; + ioc->set_ioprio = cfq_ioc_set_ioprio; cic->ioc = ioc; - cic->cfqq = __cfqq; - atomic_inc(&__cfqq->ref); + cic->key = cfqd; + atomic_inc(&cfqd->ref); } else { struct cfq_io_context *__cic; - unsigned long flags; /* - * since the first cic on the list is actually the head - * itself, need to check this here or we'll duplicate an - * cic per ioc for no reason + * the first cic on the list is actually the head itself */ - if (cic->cfqq == __cfqq) + if (cic->key == cfqd) goto out; /* @@ -1142,152 +1600,262 @@ cfq_get_io_context(struct cfq_queue **cfqq, int gfp_flags) * should be ok here, the list will usually not be more than * 1 or a few entries long */ - spin_lock_irqsave(&ioc->lock, flags); list_for_each_entry(__cic, &cic->list, list) { /* * this process is already holding a reference to * this queue, so no need to get one more */ - if (__cic->cfqq == __cfqq) { + if (__cic->key == cfqd) { cic = __cic; - spin_unlock_irqrestore(&ioc->lock, flags); goto out; } } - spin_unlock_irqrestore(&ioc->lock, flags); /* * nope, process doesn't have a cic assoicated with this * cfqq yet. get a new one and add to list */ - __cic = cfq_alloc_io_context(gfp_flags); + __cic = cfq_alloc_io_context(cfqd, gfp_mask); if (__cic == NULL) goto err; __cic->ioc = ioc; - __cic->cfqq = __cfqq; - atomic_inc(&__cfqq->ref); - spin_lock_irqsave(&ioc->lock, flags); + __cic->key = cfqd; + atomic_inc(&cfqd->ref); list_add(&__cic->list, &cic->list); - spin_unlock_irqrestore(&ioc->lock, flags); - cic = __cic; - *cfqq = __cfqq; } out: - /* - * if key_type has been changed on the fly, we lazily rehash - * each queue at lookup time - */ - if ((*cfqq)->key_type != cfqd->key_type) - cfq_rehash_cfqq(cfqd, cfqq, cic); - return cic; err: put_io_context(ioc); return NULL; } -static struct cfq_queue * -__cfq_get_queue(struct cfq_data *cfqd, unsigned long key, int gfp_mask) +static void +cfq_update_io_thinktime(struct cfq_data *cfqd, struct cfq_io_context *cic) { - const int hashval = hash_long(key, CFQ_QHASH_SHIFT); - struct cfq_queue *cfqq, *new_cfqq = NULL; + unsigned long elapsed, ttime; -retry: - cfqq = __cfq_find_cfq_hash(cfqd, key, hashval); - - if (!cfqq) { - if (new_cfqq) { - cfqq = new_cfqq; - new_cfqq = NULL; - } else { - spin_unlock_irq(cfqd->queue->queue_lock); - new_cfqq = kmem_cache_alloc(cfq_pool, gfp_mask); - spin_lock_irq(cfqd->queue->queue_lock); + /* + * if this context already has stuff queued, thinktime is from + * last queue not last end + */ +#if 0 + if (time_after(cic->last_end_request, cic->last_queue)) + elapsed = jiffies - cic->last_end_request; + else + elapsed = jiffies - cic->last_queue; +#else + elapsed = jiffies - cic->last_end_request; +#endif - if (!new_cfqq && !(gfp_mask & __GFP_WAIT)) - goto out; + ttime = min(elapsed, 2UL * cfqd->cfq_slice_idle); - goto retry; - } + cic->ttime_samples = (7*cic->ttime_samples + 256) / 8; + cic->ttime_total = (7*cic->ttime_total + 256*ttime) / 8; + cic->ttime_mean = (cic->ttime_total + 128) / cic->ttime_samples; +} - memset(cfqq, 0, sizeof(*cfqq)); +#define sample_valid(samples) ((samples) > 80) - INIT_HLIST_NODE(&cfqq->cfq_hash); - INIT_LIST_HEAD(&cfqq->cfq_list); - RB_CLEAR_ROOT(&cfqq->sort_list); - INIT_LIST_HEAD(&cfqq->fifo[0]); - INIT_LIST_HEAD(&cfqq->fifo[1]); +/* + * Disable idle window if the process thinks too long or seeks so much that + * it doesn't matter + */ +static void +cfq_update_idle_window(struct cfq_data *cfqd, struct cfq_queue *cfqq, + struct cfq_io_context *cic) +{ + int enable_idle = cfq_cfqq_idle_window(cfqq); - cfqq->key = key; - hlist_add_head(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); - atomic_set(&cfqq->ref, 0); - cfqq->cfqd = cfqd; - atomic_inc(&cfqd->ref); - cfqq->key_type = cfqd->key_type; - cfqq->service_start = ~0UL; + if (!cic->ioc->task || !cfqd->cfq_slice_idle) + enable_idle = 0; + else if (sample_valid(cic->ttime_samples)) { + if (cic->ttime_mean > cfqd->cfq_slice_idle) + enable_idle = 0; + else + enable_idle = 1; } - if (new_cfqq) - kmem_cache_free(cfq_pool, new_cfqq); + if (enable_idle) + cfq_mark_cfqq_idle_window(cfqq); + else + cfq_clear_cfqq_idle_window(cfqq); +} - atomic_inc(&cfqq->ref); -out: - WARN_ON((gfp_mask & __GFP_WAIT) && !cfqq); - return cfqq; + +/* + * Check if new_cfqq should preempt the currently active queue. Return 0 for + * no or if we aren't sure, a 1 will cause a preempt. + */ +static int +cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq, + struct cfq_rq *crq) +{ + struct cfq_queue *cfqq = cfqd->active_queue; + + if (cfq_class_idle(new_cfqq)) + return 0; + + if (!cfqq) + return 1; + + if (cfq_class_idle(cfqq)) + return 1; + if (!cfq_cfqq_wait_request(new_cfqq)) + return 0; + /* + * if it doesn't have slice left, forget it + */ + if (new_cfqq->slice_left < cfqd->cfq_slice_idle) + return 0; + if (cfq_crq_is_sync(crq) && !cfq_cfqq_sync(cfqq)) + return 1; + + return 0; +} + +/* + * cfqq preempts the active queue. if we allowed preempt with no slice left, + * let it have half of its nominal slice. + */ +static void cfq_preempt_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + struct cfq_queue *__cfqq, *next; + + list_for_each_entry_safe(__cfqq, next, &cfqd->cur_rr, cfq_list) + cfq_resort_rr_list(__cfqq, 1); + + if (!cfqq->slice_left) + cfqq->slice_left = cfq_prio_to_slice(cfqd, cfqq) / 2; + + cfqq->slice_end = cfqq->slice_left + jiffies; + __cfq_slice_expired(cfqd, cfqq, 1); + __cfq_set_active_queue(cfqd, cfqq); +} + +/* + * should really be a ll_rw_blk.c helper + */ +static void cfq_start_queueing(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + request_queue_t *q = cfqd->queue; + + if (!blk_queue_plugged(q)) + q->request_fn(q); + else + __generic_unplug_device(q); } -static void cfq_enqueue(struct cfq_data *cfqd, struct cfq_rq *crq) +/* + * Called when a new fs request (crq) is added (to cfqq). Check if there's + * something we should do about it + */ +static void +cfq_crq_enqueued(struct cfq_data *cfqd, struct cfq_queue *cfqq, + struct cfq_rq *crq) { - crq->is_sync = 0; - if (rq_data_dir(crq->request) == READ || current->flags & PF_SYNCWRITE) - crq->is_sync = 1; + const int sync = cfq_crq_is_sync(crq); + + cfqq->next_crq = cfq_choose_req(cfqd, cfqq->next_crq, crq); + + if (sync) { + struct cfq_io_context *cic = crq->io_context; + + cfq_update_io_thinktime(cfqd, cic); + cfq_update_idle_window(cfqd, cfqq, cic); + + cic->last_queue = jiffies; + } + + if (cfqq == cfqd->active_queue) { + /* + * if we are waiting for a request for this queue, let it rip + * immediately and flag that we must not expire this queue + * just now + */ + if (cfq_cfqq_wait_request(cfqq)) { + cfq_mark_cfqq_must_dispatch(cfqq); + del_timer(&cfqd->idle_slice_timer); + cfq_start_queueing(cfqd, cfqq); + } + } else if (cfq_should_preempt(cfqd, cfqq, crq)) { + /* + * not the active queue - expire current slice if it is + * idle and has expired it's mean thinktime or this new queue + * has some old slice time left and is of higher priority + */ + cfq_preempt_queue(cfqd, cfqq); + cfq_mark_cfqq_must_dispatch(cfqq); + cfq_start_queueing(cfqd, cfqq); + } +} + +static void cfq_enqueue(struct cfq_data *cfqd, struct request *rq) +{ + struct cfq_rq *crq = RQ_DATA(rq); + struct cfq_queue *cfqq = crq->cfq_queue; + + cfq_init_prio_data(cfqq); cfq_add_crq_rb(crq); - crq->queue_start = jiffies; - list_add_tail(&crq->request->queuelist, &crq->cfq_queue->fifo[crq->is_sync]); + list_add_tail(&rq->queuelist, &cfqq->fifo); + + if (rq_mergeable(rq)) { + cfq_add_crq_hash(cfqd, crq); + + if (!cfqd->queue->last_merge) + cfqd->queue->last_merge = rq; + } + + cfq_crq_enqueued(cfqd, cfqq, crq); } static void cfq_insert_request(request_queue_t *q, struct request *rq, int where) { struct cfq_data *cfqd = q->elevator->elevator_data; - struct cfq_rq *crq = RQ_DATA(rq); switch (where) { case ELEVATOR_INSERT_BACK: - while (cfq_dispatch_requests(q, cfqd->cfq_quantum)) + while (cfq_dispatch_requests(q, INT_MAX, 1)) ; list_add_tail(&rq->queuelist, &q->queue_head); + /* + * If we were idling with pending requests on + * inactive cfqqs, force dispatching will + * remove the idle timer and the queue won't + * be kicked by __make_request() afterward. + * Kick it here. + */ + cfq_schedule_dispatch(cfqd); break; case ELEVATOR_INSERT_FRONT: list_add(&rq->queuelist, &q->queue_head); break; case ELEVATOR_INSERT_SORT: BUG_ON(!blk_fs_request(rq)); - cfq_enqueue(cfqd, crq); + cfq_enqueue(cfqd, rq); break; default: printk("%s: bad insert point %d\n", __FUNCTION__,where); return; } +} - if (rq_mergeable(rq)) { - cfq_add_crq_hash(cfqd, crq); - - if (!q->last_merge) - q->last_merge = rq; - } +static inline int cfq_pending_requests(struct cfq_data *cfqd) +{ + return !list_empty(&cfqd->queue->queue_head) || cfqd->busy_queues; } static int cfq_queue_empty(request_queue_t *q) { struct cfq_data *cfqd = q->elevator->elevator_data; - return list_empty(&q->queue_head) && list_empty(&cfqd->rr_list); + return !cfq_pending_requests(cfqd); } static void cfq_completed_request(request_queue_t *q, struct request *rq) @@ -1300,9 +1868,11 @@ static void cfq_completed_request(request_queue_t *q, struct request *rq) cfqq = crq->cfq_queue; - if (crq->in_flight) { - WARN_ON(!cfqq->in_flight); - cfqq->in_flight--; + if (cfq_crq_in_flight(crq)) { + const int sync = cfq_crq_is_sync(crq); + + WARN_ON(!cfqq->on_dispatch[sync]); + cfqq->on_dispatch[sync]--; } cfq_account_completion(cfqq, crq); @@ -1332,51 +1902,136 @@ cfq_latter_request(request_queue_t *q, struct request *rq) return NULL; } -static int cfq_may_queue(request_queue_t *q, int rw) +/* + * we temporarily boost lower priority queues if they are holding fs exclusive + * resources. they are boosted to normal prio (CLASS_BE/4) + */ +static void cfq_prio_boost(struct cfq_queue *cfqq) { - struct cfq_data *cfqd = q->elevator->elevator_data; - struct cfq_queue *cfqq; - int ret = ELV_MQUEUE_MAY; + const int ioprio_class = cfqq->ioprio_class; + const int ioprio = cfqq->ioprio; - if (current->flags & PF_MEMALLOC) - return ELV_MQUEUE_MAY; + if (has_fs_excl()) { + /* + * boost idle prio on transactions that would lock out other + * users of the filesystem + */ + if (cfq_class_idle(cfqq)) + cfqq->ioprio_class = IOPRIO_CLASS_BE; + if (cfqq->ioprio > IOPRIO_NORM) + cfqq->ioprio = IOPRIO_NORM; + } else { + /* + * check if we need to unboost the queue + */ + if (cfqq->ioprio_class != cfqq->org_ioprio_class) + cfqq->ioprio_class = cfqq->org_ioprio_class; + if (cfqq->ioprio != cfqq->org_ioprio) + cfqq->ioprio = cfqq->org_ioprio; + } - cfqq = cfq_find_cfq_hash(cfqd, cfq_hash_key(cfqd, current)); - if (cfqq) { - int limit = cfqd->max_queued; + /* + * refile between round-robin lists if we moved the priority class + */ + if ((ioprio_class != cfqq->ioprio_class || ioprio != cfqq->ioprio) && + cfq_cfqq_on_rr(cfqq)) + cfq_resort_rr_list(cfqq, 0); +} - if (cfqq->allocated[rw] < cfqd->cfq_queued) - return ELV_MQUEUE_MUST; +static inline pid_t cfq_queue_pid(struct task_struct *task, int rw) +{ + if (rw == READ || process_sync(task)) + return task->pid; - if (cfqd->busy_queues) - limit = q->nr_requests / cfqd->busy_queues; + return CFQ_KEY_ASYNC; +} - if (limit < cfqd->cfq_queued) - limit = cfqd->cfq_queued; - else if (limit > cfqd->max_queued) - limit = cfqd->max_queued; +static inline int +__cfq_may_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq, + struct task_struct *task, int rw) +{ +#if 1 + if ((cfq_cfqq_wait_request(cfqq) || cfq_cfqq_must_alloc(cfqq)) && + !cfq_cfqq_must_alloc_slice) { + cfq_mark_cfqq_must_alloc_slice(cfqq); + return ELV_MQUEUE_MUST; + } - if (cfqq->allocated[rw] >= limit) { - if (limit > cfqq->alloc_limit[rw]) - cfqq->alloc_limit[rw] = limit; + return ELV_MQUEUE_MAY; +#else + if (!cfqq || task->flags & PF_MEMALLOC) + return ELV_MQUEUE_MAY; + if (!cfqq->allocated[rw] || cfq_cfqq_must_alloc(cfqq)) { + if (cfq_cfqq_wait_request(cfqq)) + return ELV_MQUEUE_MUST; - ret = ELV_MQUEUE_NO; + /* + * only allow 1 ELV_MQUEUE_MUST per slice, otherwise we + * can quickly flood the queue with writes from a single task + */ + if (rw == READ || !cfq_cfqq_must_alloc_slice) { + cfq_mark_cfqq_must_alloc_slice(cfqq); + return ELV_MQUEUE_MUST; } + + return ELV_MQUEUE_MAY; } + if (cfq_class_idle(cfqq)) + return ELV_MQUEUE_NO; + if (cfqq->allocated[rw] >= cfqd->max_queued) { + struct io_context *ioc = get_io_context(GFP_ATOMIC); + int ret = ELV_MQUEUE_NO; - return ret; + if (ioc && ioc->nr_batch_requests) + ret = ELV_MQUEUE_MAY; + + put_io_context(ioc); + return ret; + } + + return ELV_MQUEUE_MAY; +#endif +} + +static int cfq_may_queue(request_queue_t *q, int rw, struct bio *bio) +{ + struct cfq_data *cfqd = q->elevator->elevator_data; + struct task_struct *tsk = current; + struct cfq_queue *cfqq; + + /* + * don't force setup of a queue from here, as a call to may_queue + * does not necessarily imply that a request actually will be queued. + * so just lookup a possibly existing queue, or return 'may queue' + * if that fails + */ + cfqq = cfq_find_cfq_hash(cfqd, cfq_queue_pid(tsk, rw), tsk->ioprio); + if (cfqq) { + cfq_init_prio_data(cfqq); + cfq_prio_boost(cfqq); + + return __cfq_may_queue(cfqd, cfqq, tsk, rw); + } + + return ELV_MQUEUE_MAY; } static void cfq_check_waiters(request_queue_t *q, struct cfq_queue *cfqq) { + struct cfq_data *cfqd = q->elevator->elevator_data; struct request_list *rl = &q->rq; - const int write = waitqueue_active(&rl->wait[WRITE]); - const int read = waitqueue_active(&rl->wait[READ]); - if (read && cfqq->allocated[READ] < cfqq->alloc_limit[READ]) - wake_up(&rl->wait[READ]); - if (write && cfqq->allocated[WRITE] < cfqq->alloc_limit[WRITE]) - wake_up(&rl->wait[WRITE]); + if (cfqq->allocated[READ] <= cfqd->max_queued || cfqd->rq_starved) { + smp_mb(); + if (waitqueue_active(&rl->wait[READ])) + wake_up(&rl->wait[READ]); + } + + if (cfqq->allocated[WRITE] <= cfqd->max_queued || cfqd->rq_starved) { + smp_mb(); + if (waitqueue_active(&rl->wait[WRITE])) + wake_up(&rl->wait[WRITE]); + } } /* @@ -1389,69 +2044,61 @@ static void cfq_put_request(request_queue_t *q, struct request *rq) if (crq) { struct cfq_queue *cfqq = crq->cfq_queue; + const int rw = rq_data_dir(rq); - BUG_ON(q->last_merge == rq); - BUG_ON(!hlist_unhashed(&crq->hash)); + BUG_ON(!cfqq->allocated[rw]); + cfqq->allocated[rw]--; - if (crq->io_context) - put_io_context(crq->io_context->ioc); - - BUG_ON(!cfqq->allocated[crq->is_write]); - cfqq->allocated[crq->is_write]--; + put_io_context(crq->io_context->ioc); mempool_free(crq, cfqd->crq_pool); rq->elevator_private = NULL; - smp_mb(); cfq_check_waiters(q, cfqq); cfq_put_queue(cfqq); } } /* - * Allocate cfq data structures associated with this request. A queue and + * Allocate cfq data structures associated with this request. */ -static int cfq_set_request(request_queue_t *q, struct request *rq, int gfp_mask) +static int +cfq_set_request(request_queue_t *q, struct request *rq, struct bio *bio, + int gfp_mask) { struct cfq_data *cfqd = q->elevator->elevator_data; + struct task_struct *tsk = current; struct cfq_io_context *cic; const int rw = rq_data_dir(rq); - struct cfq_queue *cfqq, *saved_cfqq; + pid_t key = cfq_queue_pid(tsk, rw); + struct cfq_queue *cfqq; struct cfq_rq *crq; unsigned long flags; might_sleep_if(gfp_mask & __GFP_WAIT); + cic = cfq_get_io_context(cfqd, key, gfp_mask); + spin_lock_irqsave(q->queue_lock, flags); - cfqq = __cfq_get_queue(cfqd, cfq_hash_key(cfqd, current), gfp_mask); - if (!cfqq) - goto out_lock; + if (!cic) + goto queue_fail; + + if (!cic->cfqq) { + cfqq = cfq_get_queue(cfqd, key, tsk->ioprio, gfp_mask); + if (!cfqq) + goto queue_fail; -repeat: - if (cfqq->allocated[rw] >= cfqd->max_queued) - goto out_lock; + cic->cfqq = cfqq; + } else + cfqq = cic->cfqq; cfqq->allocated[rw]++; + cfq_clear_cfqq_must_alloc(cfqq); + cfqd->rq_starved = 0; + atomic_inc(&cfqq->ref); spin_unlock_irqrestore(q->queue_lock, flags); - /* - * if hashing type has changed, the cfq_queue might change here. - */ - saved_cfqq = cfqq; - cic = cfq_get_io_context(&cfqq, gfp_mask); - if (!cic) - goto err; - - /* - * repeat allocation checks on queue change - */ - if (unlikely(saved_cfqq != cfqq)) { - spin_lock_irqsave(q->queue_lock, flags); - saved_cfqq->allocated[rw]--; - goto repeat; - } - crq = mempool_alloc(cfqd->crq_pool, gfp_mask); if (crq) { RB_CLEAR(&crq->rb_node); @@ -1460,24 +2107,141 @@ repeat: INIT_HLIST_NODE(&crq->hash); crq->cfq_queue = cfqq; crq->io_context = cic; - crq->service_start = crq->queue_start = 0; - crq->in_flight = crq->accounted = crq->is_sync = 0; - crq->is_write = rw; + cfq_clear_crq_in_flight(crq); + cfq_clear_crq_in_driver(crq); + cfq_clear_crq_requeued(crq); + + if (rw == READ || process_sync(tsk)) + cfq_mark_crq_is_sync(crq); + else + cfq_clear_crq_is_sync(crq); + rq->elevator_private = crq; - cfqq->alloc_limit[rw] = 0; return 0; } - put_io_context(cic->ioc); -err: spin_lock_irqsave(q->queue_lock, flags); cfqq->allocated[rw]--; + if (!(cfqq->allocated[0] + cfqq->allocated[1])) + cfq_mark_cfqq_must_alloc(cfqq); cfq_put_queue(cfqq); -out_lock: +queue_fail: + if (cic) + put_io_context(cic->ioc); + /* + * mark us rq allocation starved. we need to kickstart the process + * ourselves if there are no pending requests that can do it for us. + * that would be an extremely rare OOM situation + */ + cfqd->rq_starved = 1; + cfq_schedule_dispatch(cfqd); spin_unlock_irqrestore(q->queue_lock, flags); return 1; } +static void cfq_kick_queue(void *data) +{ + request_queue_t *q = data; + struct cfq_data *cfqd = q->elevator->elevator_data; + unsigned long flags; + + spin_lock_irqsave(q->queue_lock, flags); + + if (cfqd->rq_starved) { + struct request_list *rl = &q->rq; + + /* + * we aren't guaranteed to get a request after this, but we + * have to be opportunistic + */ + smp_mb(); + if (waitqueue_active(&rl->wait[READ])) + wake_up(&rl->wait[READ]); + if (waitqueue_active(&rl->wait[WRITE])) + wake_up(&rl->wait[WRITE]); + } + + blk_remove_plug(q); + q->request_fn(q); + spin_unlock_irqrestore(q->queue_lock, flags); +} + +/* + * Timer running if the active_queue is currently idling inside its time slice + */ +static void cfq_idle_slice_timer(unsigned long data) +{ + struct cfq_data *cfqd = (struct cfq_data *) data; + struct cfq_queue *cfqq; + unsigned long flags; + + spin_lock_irqsave(cfqd->queue->queue_lock, flags); + + if ((cfqq = cfqd->active_queue) != NULL) { + unsigned long now = jiffies; + + /* + * expired + */ + if (time_after(now, cfqq->slice_end)) + goto expire; + + /* + * only expire and reinvoke request handler, if there are + * other queues with pending requests + */ + if (!cfq_pending_requests(cfqd)) { + cfqd->idle_slice_timer.expires = min(now + cfqd->cfq_slice_idle, cfqq->slice_end); + add_timer(&cfqd->idle_slice_timer); + goto out_cont; + } + + /* + * not expired and it has a request pending, let it dispatch + */ + if (!RB_EMPTY(&cfqq->sort_list)) { + cfq_mark_cfqq_must_dispatch(cfqq); + goto out_kick; + } + } +expire: + cfq_slice_expired(cfqd, 0); +out_kick: + cfq_schedule_dispatch(cfqd); +out_cont: + spin_unlock_irqrestore(cfqd->queue->queue_lock, flags); +} + +/* + * Timer running if an idle class queue is waiting for service + */ +static void cfq_idle_class_timer(unsigned long data) +{ + struct cfq_data *cfqd = (struct cfq_data *) data; + unsigned long flags, end; + + spin_lock_irqsave(cfqd->queue->queue_lock, flags); + + /* + * race with a non-idle queue, reset timer + */ + end = cfqd->last_end_request + CFQ_IDLE_GRACE; + if (!time_after_eq(jiffies, end)) { + cfqd->idle_class_timer.expires = end; + add_timer(&cfqd->idle_class_timer); + } else + cfq_schedule_dispatch(cfqd); + + spin_unlock_irqrestore(cfqd->queue->queue_lock, flags); +} + +static void cfq_shutdown_timer_wq(struct cfq_data *cfqd) +{ + del_timer_sync(&cfqd->idle_slice_timer); + del_timer_sync(&cfqd->idle_class_timer); + blk_sync_queue(cfqd->queue); +} + static void cfq_put_cfqd(struct cfq_data *cfqd) { request_queue_t *q = cfqd->queue; @@ -1487,6 +2251,9 @@ static void cfq_put_cfqd(struct cfq_data *cfqd) blk_put_queue(q); + cfq_shutdown_timer_wq(cfqd); + q->elevator->elevator_data = NULL; + mempool_destroy(cfqd->crq_pool); kfree(cfqd->crq_hash); kfree(cfqd->cfq_hash); @@ -1495,7 +2262,10 @@ static void cfq_put_cfqd(struct cfq_data *cfqd) static void cfq_exit_queue(elevator_t *e) { - cfq_put_cfqd(e->elevator_data); + struct cfq_data *cfqd = e->elevator_data; + + cfq_shutdown_timer_wq(cfqd); + cfq_put_cfqd(cfqd); } static int cfq_init_queue(request_queue_t *q, elevator_t *e) @@ -1508,7 +2278,13 @@ static int cfq_init_queue(request_queue_t *q, elevator_t *e) return -ENOMEM; memset(cfqd, 0, sizeof(*cfqd)); - INIT_LIST_HEAD(&cfqd->rr_list); + + for (i = 0; i < CFQ_PRIO_LISTS; i++) + INIT_LIST_HEAD(&cfqd->rr_list[i]); + + INIT_LIST_HEAD(&cfqd->busy_rr); + INIT_LIST_HEAD(&cfqd->cur_rr); + INIT_LIST_HEAD(&cfqd->idle_rr); INIT_LIST_HEAD(&cfqd->empty_list); cfqd->crq_hash = kmalloc(sizeof(struct hlist_head) * CFQ_MHASH_ENTRIES, GFP_KERNEL); @@ -1533,24 +2309,32 @@ static int cfq_init_queue(request_queue_t *q, elevator_t *e) cfqd->queue = q; atomic_inc(&q->refcnt); - /* - * just set it to some high value, we want anyone to be able to queue - * some requests. fairness is handled differently - */ - q->nr_requests = 1024; - cfqd->max_queued = q->nr_requests / 16; + cfqd->max_queued = q->nr_requests / 4; q->nr_batching = cfq_queued; - cfqd->key_type = CFQ_KEY_TGID; - cfqd->find_best_crq = 1; + + init_timer(&cfqd->idle_slice_timer); + cfqd->idle_slice_timer.function = cfq_idle_slice_timer; + cfqd->idle_slice_timer.data = (unsigned long) cfqd; + + init_timer(&cfqd->idle_class_timer); + cfqd->idle_class_timer.function = cfq_idle_class_timer; + cfqd->idle_class_timer.data = (unsigned long) cfqd; + + INIT_WORK(&cfqd->unplug_work, cfq_kick_queue, q); + atomic_set(&cfqd->ref, 1); cfqd->cfq_queued = cfq_queued; cfqd->cfq_quantum = cfq_quantum; - cfqd->cfq_fifo_expire_r = cfq_fifo_expire_r; - cfqd->cfq_fifo_expire_w = cfq_fifo_expire_w; - cfqd->cfq_fifo_batch_expire = cfq_fifo_rate; + cfqd->cfq_fifo_expire[0] = cfq_fifo_expire[0]; + cfqd->cfq_fifo_expire[1] = cfq_fifo_expire[1]; cfqd->cfq_back_max = cfq_back_max; cfqd->cfq_back_penalty = cfq_back_penalty; + cfqd->cfq_slice[0] = cfq_slice_async; + cfqd->cfq_slice[1] = cfq_slice_sync; + cfqd->cfq_slice_async_rq = cfq_slice_async_rq; + cfqd->cfq_slice_idle = cfq_slice_idle; + cfqd->cfq_max_depth = cfq_max_depth; return 0; out_crqpool: @@ -1595,7 +2379,6 @@ fail: return -ENOMEM; } - /* * sysfs parts below --> */ @@ -1620,45 +2403,6 @@ cfq_var_store(unsigned int *var, const char *page, size_t count) return count; } -static ssize_t -cfq_clear_elapsed(struct cfq_data *cfqd, const char *page, size_t count) -{ - max_elapsed_dispatch = max_elapsed_crq = 0; - return count; -} - -static ssize_t -cfq_set_key_type(struct cfq_data *cfqd, const char *page, size_t count) -{ - spin_lock_irq(cfqd->queue->queue_lock); - if (!strncmp(page, "pgid", 4)) - cfqd->key_type = CFQ_KEY_PGID; - else if (!strncmp(page, "tgid", 4)) - cfqd->key_type = CFQ_KEY_TGID; - else if (!strncmp(page, "uid", 3)) - cfqd->key_type = CFQ_KEY_UID; - else if (!strncmp(page, "gid", 3)) - cfqd->key_type = CFQ_KEY_GID; - spin_unlock_irq(cfqd->queue->queue_lock); - return count; -} - -static ssize_t -cfq_read_key_type(struct cfq_data *cfqd, char *page) -{ - ssize_t len = 0; - int i; - - for (i = CFQ_KEY_PGID; i < CFQ_KEY_LAST; i++) { - if (cfqd->key_type == i) - len += sprintf(page+len, "[%s] ", cfq_key_types[i]); - else - len += sprintf(page+len, "%s ", cfq_key_types[i]); - } - len += sprintf(page+len, "\n"); - return len; -} - #define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ static ssize_t __FUNC(struct cfq_data *cfqd, char *page) \ { \ @@ -1669,12 +2413,15 @@ static ssize_t __FUNC(struct cfq_data *cfqd, char *page) \ } SHOW_FUNCTION(cfq_quantum_show, cfqd->cfq_quantum, 0); SHOW_FUNCTION(cfq_queued_show, cfqd->cfq_queued, 0); -SHOW_FUNCTION(cfq_fifo_expire_r_show, cfqd->cfq_fifo_expire_r, 1); -SHOW_FUNCTION(cfq_fifo_expire_w_show, cfqd->cfq_fifo_expire_w, 1); -SHOW_FUNCTION(cfq_fifo_batch_expire_show, cfqd->cfq_fifo_batch_expire, 1); -SHOW_FUNCTION(cfq_find_best_show, cfqd->find_best_crq, 0); +SHOW_FUNCTION(cfq_fifo_expire_sync_show, cfqd->cfq_fifo_expire[1], 1); +SHOW_FUNCTION(cfq_fifo_expire_async_show, cfqd->cfq_fifo_expire[0], 1); SHOW_FUNCTION(cfq_back_max_show, cfqd->cfq_back_max, 0); SHOW_FUNCTION(cfq_back_penalty_show, cfqd->cfq_back_penalty, 0); +SHOW_FUNCTION(cfq_slice_idle_show, cfqd->cfq_slice_idle, 1); +SHOW_FUNCTION(cfq_slice_sync_show, cfqd->cfq_slice[1], 1); +SHOW_FUNCTION(cfq_slice_async_show, cfqd->cfq_slice[0], 1); +SHOW_FUNCTION(cfq_slice_async_rq_show, cfqd->cfq_slice_async_rq, 0); +SHOW_FUNCTION(cfq_max_depth_show, cfqd->cfq_max_depth, 0); #undef SHOW_FUNCTION #define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ @@ -1694,12 +2441,15 @@ static ssize_t __FUNC(struct cfq_data *cfqd, const char *page, size_t count) \ } STORE_FUNCTION(cfq_quantum_store, &cfqd->cfq_quantum, 1, UINT_MAX, 0); STORE_FUNCTION(cfq_queued_store, &cfqd->cfq_queued, 1, UINT_MAX, 0); -STORE_FUNCTION(cfq_fifo_expire_r_store, &cfqd->cfq_fifo_expire_r, 1, UINT_MAX, 1); -STORE_FUNCTION(cfq_fifo_expire_w_store, &cfqd->cfq_fifo_expire_w, 1, UINT_MAX, 1); -STORE_FUNCTION(cfq_fifo_batch_expire_store, &cfqd->cfq_fifo_batch_expire, 0, UINT_MAX, 1); -STORE_FUNCTION(cfq_find_best_store, &cfqd->find_best_crq, 0, 1, 0); +STORE_FUNCTION(cfq_fifo_expire_sync_store, &cfqd->cfq_fifo_expire[1], 1, UINT_MAX, 1); +STORE_FUNCTION(cfq_fifo_expire_async_store, &cfqd->cfq_fifo_expire[0], 1, UINT_MAX, 1); STORE_FUNCTION(cfq_back_max_store, &cfqd->cfq_back_max, 0, UINT_MAX, 0); STORE_FUNCTION(cfq_back_penalty_store, &cfqd->cfq_back_penalty, 1, UINT_MAX, 0); +STORE_FUNCTION(cfq_slice_idle_store, &cfqd->cfq_slice_idle, 0, UINT_MAX, 1); +STORE_FUNCTION(cfq_slice_sync_store, &cfqd->cfq_slice[1], 1, UINT_MAX, 1); +STORE_FUNCTION(cfq_slice_async_store, &cfqd->cfq_slice[0], 1, UINT_MAX, 1); +STORE_FUNCTION(cfq_slice_async_rq_store, &cfqd->cfq_slice_async_rq, 1, UINT_MAX, 0); +STORE_FUNCTION(cfq_max_depth_store, &cfqd->cfq_max_depth, 1, UINT_MAX, 0); #undef STORE_FUNCTION static struct cfq_fs_entry cfq_quantum_entry = { @@ -1712,25 +2462,15 @@ static struct cfq_fs_entry cfq_queued_entry = { .show = cfq_queued_show, .store = cfq_queued_store, }; -static struct cfq_fs_entry cfq_fifo_expire_r_entry = { +static struct cfq_fs_entry cfq_fifo_expire_sync_entry = { .attr = {.name = "fifo_expire_sync", .mode = S_IRUGO | S_IWUSR }, - .show = cfq_fifo_expire_r_show, - .store = cfq_fifo_expire_r_store, + .show = cfq_fifo_expire_sync_show, + .store = cfq_fifo_expire_sync_store, }; -static struct cfq_fs_entry cfq_fifo_expire_w_entry = { +static struct cfq_fs_entry cfq_fifo_expire_async_entry = { .attr = {.name = "fifo_expire_async", .mode = S_IRUGO | S_IWUSR }, - .show = cfq_fifo_expire_w_show, - .store = cfq_fifo_expire_w_store, -}; -static struct cfq_fs_entry cfq_fifo_batch_expire_entry = { - .attr = {.name = "fifo_batch_expire", .mode = S_IRUGO | S_IWUSR }, - .show = cfq_fifo_batch_expire_show, - .store = cfq_fifo_batch_expire_store, -}; -static struct cfq_fs_entry cfq_find_best_entry = { - .attr = {.name = "find_best_crq", .mode = S_IRUGO | S_IWUSR }, - .show = cfq_find_best_show, - .store = cfq_find_best_store, + .show = cfq_fifo_expire_async_show, + .store = cfq_fifo_expire_async_store, }; static struct cfq_fs_entry cfq_back_max_entry = { .attr = {.name = "back_seek_max", .mode = S_IRUGO | S_IWUSR }, @@ -1742,27 +2482,44 @@ static struct cfq_fs_entry cfq_back_penalty_entry = { .show = cfq_back_penalty_show, .store = cfq_back_penalty_store, }; -static struct cfq_fs_entry cfq_clear_elapsed_entry = { - .attr = {.name = "clear_elapsed", .mode = S_IWUSR }, - .store = cfq_clear_elapsed, +static struct cfq_fs_entry cfq_slice_sync_entry = { + .attr = {.name = "slice_sync", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_slice_sync_show, + .store = cfq_slice_sync_store, +}; +static struct cfq_fs_entry cfq_slice_async_entry = { + .attr = {.name = "slice_async", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_slice_async_show, + .store = cfq_slice_async_store, }; -static struct cfq_fs_entry cfq_key_type_entry = { - .attr = {.name = "key_type", .mode = S_IRUGO | S_IWUSR }, - .show = cfq_read_key_type, - .store = cfq_set_key_type, +static struct cfq_fs_entry cfq_slice_async_rq_entry = { + .attr = {.name = "slice_async_rq", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_slice_async_rq_show, + .store = cfq_slice_async_rq_store, +}; +static struct cfq_fs_entry cfq_slice_idle_entry = { + .attr = {.name = "slice_idle", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_slice_idle_show, + .store = cfq_slice_idle_store, +}; +static struct cfq_fs_entry cfq_max_depth_entry = { + .attr = {.name = "max_depth", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_max_depth_show, + .store = cfq_max_depth_store, }; static struct attribute *default_attrs[] = { &cfq_quantum_entry.attr, &cfq_queued_entry.attr, - &cfq_fifo_expire_r_entry.attr, - &cfq_fifo_expire_w_entry.attr, - &cfq_fifo_batch_expire_entry.attr, - &cfq_key_type_entry.attr, - &cfq_find_best_entry.attr, + &cfq_fifo_expire_sync_entry.attr, + &cfq_fifo_expire_async_entry.attr, &cfq_back_max_entry.attr, &cfq_back_penalty_entry.attr, - &cfq_clear_elapsed_entry.attr, + &cfq_slice_sync_entry.attr, + &cfq_slice_async_entry.attr, + &cfq_slice_async_rq_entry.attr, + &cfq_slice_idle_entry.attr, + &cfq_max_depth_entry.attr, NULL, }; @@ -1832,21 +2589,46 @@ static int __init cfq_init(void) { int ret; + /* + * could be 0 on HZ < 1000 setups + */ + if (!cfq_slice_async) + cfq_slice_async = 1; + if (!cfq_slice_idle) + cfq_slice_idle = 1; + if (cfq_slab_setup()) return -ENOMEM; ret = elv_register(&iosched_cfq); - if (!ret) { - __module_get(THIS_MODULE); - return 0; - } + if (ret) + cfq_slab_kill(); - cfq_slab_kill(); return ret; } static void __exit cfq_exit(void) { + struct task_struct *g, *p; + unsigned long flags; + + read_lock_irqsave(&tasklist_lock, flags); + + /* + * iterate each process in the system, removing our io_context + */ + do_each_thread(g, p) { + struct io_context *ioc = p->io_context; + + if (ioc && ioc->cic) { + ioc->cic->exit(ioc->cic); + cfq_free_io_context(ioc->cic); + ioc->cic = NULL; + } + } while_each_thread(g, p); + + read_unlock_irqrestore(&tasklist_lock, flags); + cfq_slab_kill(); elv_unregister(&iosched_cfq); } diff --git a/drivers/block/deadline-iosched.c b/drivers/block/deadline-iosched.c index 4bc2fea..ff5201e 100644 --- a/drivers/block/deadline-iosched.c +++ b/drivers/block/deadline-iosched.c @@ -760,7 +760,8 @@ static void deadline_put_request(request_queue_t *q, struct request *rq) } static int -deadline_set_request(request_queue_t *q, struct request *rq, int gfp_mask) +deadline_set_request(request_queue_t *q, struct request *rq, struct bio *bio, + int gfp_mask) { struct deadline_data *dd = q->elevator->elevator_data; struct deadline_rq *drq; diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index f831f08..98f0126 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -486,12 +486,13 @@ struct request *elv_former_request(request_queue_t *q, struct request *rq) return NULL; } -int elv_set_request(request_queue_t *q, struct request *rq, int gfp_mask) +int elv_set_request(request_queue_t *q, struct request *rq, struct bio *bio, + int gfp_mask) { elevator_t *e = q->elevator; if (e->ops->elevator_set_req_fn) - return e->ops->elevator_set_req_fn(q, rq, gfp_mask); + return e->ops->elevator_set_req_fn(q, rq, bio, gfp_mask); rq->elevator_private = NULL; return 0; @@ -505,12 +506,12 @@ void elv_put_request(request_queue_t *q, struct request *rq) e->ops->elevator_put_req_fn(q, rq); } -int elv_may_queue(request_queue_t *q, int rw) +int elv_may_queue(request_queue_t *q, int rw, struct bio *bio) { elevator_t *e = q->elevator; if (e->ops->elevator_may_queue_fn) - return e->ops->elevator_may_queue_fn(q, rw); + return e->ops->elevator_may_queue_fn(q, rw, bio); return ELV_MQUEUE_MAY; } diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 60e6409..234fdcf 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -276,6 +276,7 @@ static inline void rq_init(request_queue_t *q, struct request *rq) rq->errors = 0; rq->rq_status = RQ_ACTIVE; rq->bio = rq->biotail = NULL; + rq->ioprio = 0; rq->buffer = NULL; rq->ref_count = 1; rq->q = q; @@ -1442,11 +1443,7 @@ void __generic_unplug_device(request_queue_t *q) if (!blk_remove_plug(q)) return; - /* - * was plugged, fire request_fn if queue has stuff to do - */ - if (elv_next_request(q)) - q->request_fn(q); + q->request_fn(q); } EXPORT_SYMBOL(__generic_unplug_device); @@ -1776,8 +1773,8 @@ static inline void blk_free_request(request_queue_t *q, struct request *rq) mempool_free(rq, q->rq.rq_pool); } -static inline struct request *blk_alloc_request(request_queue_t *q, int rw, - int gfp_mask) +static inline struct request * +blk_alloc_request(request_queue_t *q, int rw, struct bio *bio, int gfp_mask) { struct request *rq = mempool_alloc(q->rq.rq_pool, gfp_mask); @@ -1790,7 +1787,7 @@ static inline struct request *blk_alloc_request(request_queue_t *q, int rw, */ rq->flags = rw; - if (!elv_set_request(q, rq, gfp_mask)) + if (!elv_set_request(q, rq, bio, gfp_mask)) return rq; mempool_free(rq, q->rq.rq_pool); @@ -1872,7 +1869,8 @@ static void freed_request(request_queue_t *q, int rw) /* * Get a free request, queue_lock must not be held */ -static struct request *get_request(request_queue_t *q, int rw, int gfp_mask) +static struct request *get_request(request_queue_t *q, int rw, struct bio *bio, + int gfp_mask) { struct request *rq = NULL; struct request_list *rl = &q->rq; @@ -1895,7 +1893,7 @@ static struct request *get_request(request_queue_t *q, int rw, int gfp_mask) } } - switch (elv_may_queue(q, rw)) { + switch (elv_may_queue(q, rw, bio)) { case ELV_MQUEUE_NO: goto rq_starved; case ELV_MQUEUE_MAY: @@ -1920,7 +1918,7 @@ get_rq: set_queue_congested(q, rw); spin_unlock_irq(q->queue_lock); - rq = blk_alloc_request(q, rw, gfp_mask); + rq = blk_alloc_request(q, rw, bio, gfp_mask); if (!rq) { /* * Allocation failed presumably due to memory. Undo anything @@ -1961,7 +1959,8 @@ out: * No available requests for this queue, unplug the device and wait for some * requests to become available. */ -static struct request *get_request_wait(request_queue_t *q, int rw) +static struct request *get_request_wait(request_queue_t *q, int rw, + struct bio *bio) { DEFINE_WAIT(wait); struct request *rq; @@ -1972,7 +1971,7 @@ static struct request *get_request_wait(request_queue_t *q, int rw) prepare_to_wait_exclusive(&rl->wait[rw], &wait, TASK_UNINTERRUPTIBLE); - rq = get_request(q, rw, GFP_NOIO); + rq = get_request(q, rw, bio, GFP_NOIO); if (!rq) { struct io_context *ioc; @@ -2003,9 +2002,9 @@ struct request *blk_get_request(request_queue_t *q, int rw, int gfp_mask) BUG_ON(rw != READ && rw != WRITE); if (gfp_mask & __GFP_WAIT) - rq = get_request_wait(q, rw); + rq = get_request_wait(q, rw, NULL); else - rq = get_request(q, rw, gfp_mask); + rq = get_request(q, rw, NULL, gfp_mask); return rq; } @@ -2333,7 +2332,6 @@ static void __blk_put_request(request_queue_t *q, struct request *req) return; req->rq_status = RQ_INACTIVE; - req->q = NULL; req->rl = NULL; /* @@ -2462,6 +2460,8 @@ static int attempt_merge(request_queue_t *q, struct request *req, req->rq_disk->in_flight--; } + req->ioprio = ioprio_best(req->ioprio, next->ioprio); + __blk_put_request(q, next); return 1; } @@ -2514,11 +2514,13 @@ static int __make_request(request_queue_t *q, struct bio *bio) { struct request *req, *freereq = NULL; int el_ret, rw, nr_sectors, cur_nr_sectors, barrier, err, sync; + unsigned short prio; sector_t sector; sector = bio->bi_sector; nr_sectors = bio_sectors(bio); cur_nr_sectors = bio_cur_sectors(bio); + prio = bio_prio(bio); rw = bio_data_dir(bio); sync = bio_sync(bio); @@ -2559,6 +2561,7 @@ again: req->biotail->bi_next = bio; req->biotail = bio; req->nr_sectors = req->hard_nr_sectors += nr_sectors; + req->ioprio = ioprio_best(req->ioprio, prio); drive_stat_acct(req, nr_sectors, 0); if (!attempt_back_merge(q, req)) elv_merged_request(q, req); @@ -2583,6 +2586,7 @@ again: req->hard_cur_sectors = cur_nr_sectors; req->sector = req->hard_sector = sector; req->nr_sectors = req->hard_nr_sectors += nr_sectors; + req->ioprio = ioprio_best(req->ioprio, prio); drive_stat_acct(req, nr_sectors, 0); if (!attempt_front_merge(q, req)) elv_merged_request(q, req); @@ -2610,7 +2614,7 @@ get_rq: freereq = NULL; } else { spin_unlock_irq(q->queue_lock); - if ((freereq = get_request(q, rw, GFP_ATOMIC)) == NULL) { + if ((freereq = get_request(q, rw, bio, GFP_ATOMIC)) == NULL) { /* * READA bit set */ @@ -2618,7 +2622,7 @@ get_rq: if (bio_rw_ahead(bio)) goto end_io; - freereq = get_request_wait(q, rw); + freereq = get_request_wait(q, rw, bio); } goto again; } @@ -2646,6 +2650,7 @@ get_rq: req->buffer = bio_data(bio); /* see ->buffer comment above */ req->waiting = NULL; req->bio = req->biotail = bio; + req->ioprio = prio; req->rq_disk = bio->bi_bdev->bd_disk; req->start_time = jiffies; @@ -2674,7 +2679,7 @@ static inline void blk_partition_remap(struct bio *bio) if (bdev != bdev->bd_contains) { struct hd_struct *p = bdev->bd_part; - switch (bio->bi_rw) { + switch (bio_data_dir(bio)) { case READ: p->read_sectors += bio_sectors(bio); p->reads++; @@ -2693,6 +2698,7 @@ void blk_finish_queue_drain(request_queue_t *q) { struct request_list *rl = &q->rq; struct request *rq; + int requeued = 0; spin_lock_irq(q->queue_lock); clear_bit(QUEUE_FLAG_DRAIN, &q->queue_flags); @@ -2701,9 +2707,13 @@ void blk_finish_queue_drain(request_queue_t *q) rq = list_entry_rq(q->drain_list.next); list_del_init(&rq->queuelist); - __elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1); + elv_requeue_request(q, rq); + requeued++; } + if (requeued) + q->request_fn(q); + spin_unlock_irq(q->queue_lock); wake_up(&rl->wait[0]); @@ -2900,7 +2910,7 @@ void submit_bio(int rw, struct bio *bio) BIO_BUG_ON(!bio->bi_size); BIO_BUG_ON(!bio->bi_io_vec); - bio->bi_rw = rw; + bio->bi_rw |= rw; if (rw & WRITE) mod_page_state(pgpgout, count); else @@ -3257,8 +3267,11 @@ void exit_io_context(void) struct io_context *ioc; local_irq_save(flags); + task_lock(current); ioc = current->io_context; current->io_context = NULL; + ioc->task = NULL; + task_unlock(current); local_irq_restore(flags); if (ioc->aic && ioc->aic->exit) @@ -3293,12 +3306,12 @@ struct io_context *get_io_context(int gfp_flags) ret = kmem_cache_alloc(iocontext_cachep, gfp_flags); if (ret) { atomic_set(&ret->refcount, 1); - ret->pid = tsk->pid; + ret->task = current; + ret->set_ioprio = NULL; ret->last_waited = jiffies; /* doesn't matter... */ ret->nr_batch_requests = 0; /* because this is 0 */ ret->aic = NULL; ret->cic = NULL; - spin_lock_init(&ret->lock); local_irq_save(flags); diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c index 5b09cf1..e5f7494 100644 --- a/drivers/block/swim3.c +++ b/drivers/block/swim3.c @@ -253,7 +253,7 @@ static int floppy_revalidate(struct gendisk *disk); static int swim3_add_device(struct device_node *swims); int swim3_init(void); -#ifndef CONFIG_PMAC_PBOOK +#ifndef CONFIG_PMAC_MEDIABAY #define check_media_bay(which, what) 1 #endif @@ -297,9 +297,11 @@ static void do_fd_request(request_queue_t * q) int i; for(i=0;i<floppy_count;i++) { +#ifdef CONFIG_PMAC_MEDIABAY if (floppy_states[i].media_bay && check_media_bay(floppy_states[i].media_bay, MB_FD)) continue; +#endif /* CONFIG_PMAC_MEDIABAY */ start_request(&floppy_states[i]); } sti(); @@ -856,8 +858,10 @@ static int floppy_ioctl(struct inode *inode, struct file *filp, if ((cmd & 0x80) && !capable(CAP_SYS_ADMIN)) return -EPERM; +#ifdef CONFIG_PMAC_MEDIABAY if (fs->media_bay && check_media_bay(fs->media_bay, MB_FD)) return -ENXIO; +#endif switch (cmd) { case FDEJECT: @@ -881,8 +885,10 @@ static int floppy_open(struct inode *inode, struct file *filp) int n, err = 0; if (fs->ref_count == 0) { +#ifdef CONFIG_PMAC_MEDIABAY if (fs->media_bay && check_media_bay(fs->media_bay, MB_FD)) return -ENXIO; +#endif out_8(&sw->setup, S_IBM_DRIVE | S_FCLK_DIV2); out_8(&sw->control_bic, 0xff); out_8(&sw->mode, 0x95); @@ -967,8 +973,10 @@ static int floppy_revalidate(struct gendisk *disk) struct swim3 __iomem *sw; int ret, n; +#ifdef CONFIG_PMAC_MEDIABAY if (fs->media_bay && check_media_bay(fs->media_bay, MB_FD)) return -ENXIO; +#endif sw = fs->swim3; grab_drive(fs, revalidating, 0); diff --git a/drivers/block/sx8.c b/drivers/block/sx8.c index 5ed3a63..9db0a9e 100644 --- a/drivers/block/sx8.c +++ b/drivers/block/sx8.c @@ -26,6 +26,7 @@ #include <linux/delay.h> #include <linux/time.h> #include <linux/hdreg.h> +#include <linux/dma-mapping.h> #include <asm/io.h> #include <asm/semaphore.h> #include <asm/uaccess.h> @@ -1582,9 +1583,9 @@ static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out; #if IF_64BIT_DMA_IS_POSSIBLE /* grrrr... */ - rc = pci_set_dma_mask(pdev, 0xffffffffffffffffULL); + rc = pci_set_dma_mask(pdev, DMA_64BIT_MASK); if (!rc) { - rc = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL); + rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); if (rc) { printk(KERN_ERR DRV_NAME "(%s): consistent DMA mask failure\n", pci_name(pdev)); @@ -1593,7 +1594,7 @@ static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) pci_dac = 1; } else { #endif - rc = pci_set_dma_mask(pdev, 0xffffffffULL); + rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { printk(KERN_ERR DRV_NAME "(%s): DMA mask failure\n", pci_name(pdev)); diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 31cf84d..931efd5 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -309,9 +309,6 @@ static int __init misc_init(void) #ifdef CONFIG_BVME6000 rtc_DP8570A_init(); #endif -#ifdef CONFIG_PMAC_PBOOK - pmu_device_init(); -#endif if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) { printk("unable to get major %d for misc devices\n", MISC_MAJOR); diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index 569f167..818380b 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -1324,9 +1324,9 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) /* XXX FIXME: Media bay stuff need re-organizing */ if (np->parent && np->parent->name && strcasecmp(np->parent->name, "media-bay") == 0) { -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PMAC_MEDIABAY media_bay_set_ide_infos(np->parent, pmif->regbase, pmif->irq, hwif->index); -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PMAC_MEDIABAY */ pmif->mediabay = 1; if (!bidp) pmif->aapl_bus_id = 1; @@ -1382,10 +1382,10 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) hwif->index, model_name[pmif->kind], pmif->aapl_bus_id, pmif->mediabay ? " (mediabay)" : "", hwif->irq); -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PMAC_MEDIABAY if (pmif->mediabay && check_media_bay_by_base(pmif->regbase, MB_CD) == 0) hwif->noprobe = 0; -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PMAC_MEDIABAY */ hwif->sg_max_nents = MAX_DCMDS; diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c index 36e25ac..b3d3d22 100644 --- a/drivers/ieee1394/ohci1394.c +++ b/drivers/ieee1394/ohci1394.c @@ -3538,8 +3538,8 @@ static void ohci1394_pci_remove(struct pci_dev *pdev) static int ohci1394_pci_resume (struct pci_dev *pdev) { -#ifdef CONFIG_PMAC_PBOOK - { +#ifdef CONFIG_PPC_PMAC + if (_machine == _MACH_Pmac) { struct device_node *of_node; /* Re-enable 1394 */ @@ -3547,7 +3547,7 @@ static int ohci1394_pci_resume (struct pci_dev *pdev) if (of_node) pmac_call_feature (PMAC_FTR_1394_ENABLE, of_node, 0, 1); } -#endif +#endif /* CONFIG_PPC_PMAC */ pci_enable_device(pdev); @@ -3557,8 +3557,8 @@ static int ohci1394_pci_resume (struct pci_dev *pdev) static int ohci1394_pci_suspend (struct pci_dev *pdev, pm_message_t state) { -#ifdef CONFIG_PMAC_PBOOK - { +#ifdef CONFIG_PPC_PMAC + if (_machine == _MACH_Pmac) { struct device_node *of_node; /* Disable 1394 */ diff --git a/drivers/infiniband/core/packer.c b/drivers/infiniband/core/packer.c index 5f15fef..eb5ff54 100644 --- a/drivers/infiniband/core/packer.c +++ b/drivers/infiniband/core/packer.c @@ -96,7 +96,7 @@ void ib_pack(const struct ib_field *desc, else val = 0; - mask = cpu_to_be64(((1ull << desc[i].size_bits) - 1) << shift); + mask = cpu_to_be64((~0ull >> (64 - desc[i].size_bits)) << shift); addr = (__be64 *) ((__be32 *) buf + desc[i].offset_words); *addr = (*addr & ~mask) | (cpu_to_be64(val) & mask); } else { @@ -176,7 +176,7 @@ void ib_unpack(const struct ib_field *desc, __be64 *addr; shift = 64 - desc[i].offset_bits - desc[i].size_bits; - mask = ((1ull << desc[i].size_bits) - 1) << shift; + mask = (~0ull >> (64 - desc[i].size_bits)) << shift; addr = (__be64 *) buf + desc[i].offset_words; val = (be64_to_cpup(addr) & mask) >> shift; value_write(desc[i].struct_offset_bytes, diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index 276e1a5..5a08e81 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -507,7 +507,13 @@ retry: spin_unlock_irqrestore(&idr_lock, flags); } - return ret; + /* + * It's not safe to dereference query any more, because the + * send may already have completed and freed the query in + * another context. So use wr.wr_id, which has a copy of the + * query's id. + */ + return ret ? ret : wr.wr_id; } static void ib_sa_path_rec_callback(struct ib_sa_query *sa_query, @@ -598,14 +604,15 @@ int ib_sa_path_rec_get(struct ib_device *device, u8 port_num, rec, query->sa_query.mad->data); *sa_query = &query->sa_query; + ret = send_mad(&query->sa_query, timeout_ms); - if (ret) { + if (ret < 0) { *sa_query = NULL; kfree(query->sa_query.mad); kfree(query); } - return ret ? ret : query->sa_query.id; + return ret; } EXPORT_SYMBOL(ib_sa_path_rec_get); @@ -674,14 +681,15 @@ int ib_sa_mcmember_rec_query(struct ib_device *device, u8 port_num, rec, query->sa_query.mad->data); *sa_query = &query->sa_query; + ret = send_mad(&query->sa_query, timeout_ms); - if (ret) { + if (ret < 0) { *sa_query = NULL; kfree(query->sa_query.mad); kfree(query); } - return ret ? ret : query->sa_query.id; + return ret; } EXPORT_SYMBOL(ib_sa_mcmember_rec_query); diff --git a/drivers/infiniband/hw/mthca/mthca_av.c b/drivers/infiniband/hw/mthca/mthca_av.c index 085baf3..d58dcbe 100644 --- a/drivers/infiniband/hw/mthca/mthca_av.c +++ b/drivers/infiniband/hw/mthca/mthca_av.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2004 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/mthca/mthca_cmd.c b/drivers/infiniband/hw/mthca/mthca_cmd.c index cd9ed95..1557a52 100644 --- a/drivers/infiniband/hw/mthca/mthca_cmd.c +++ b/drivers/infiniband/hw/mthca/mthca_cmd.c @@ -431,6 +431,36 @@ static int mthca_cmd_imm(struct mthca_dev *dev, timeout, status); } +int mthca_cmd_init(struct mthca_dev *dev) +{ + sema_init(&dev->cmd.hcr_sem, 1); + sema_init(&dev->cmd.poll_sem, 1); + dev->cmd.use_events = 0; + + dev->hcr = ioremap(pci_resource_start(dev->pdev, 0) + MTHCA_HCR_BASE, + MTHCA_HCR_SIZE); + if (!dev->hcr) { + mthca_err(dev, "Couldn't map command register."); + return -ENOMEM; + } + + dev->cmd.pool = pci_pool_create("mthca_cmd", dev->pdev, + MTHCA_MAILBOX_SIZE, + MTHCA_MAILBOX_SIZE, 0); + if (!dev->cmd.pool) { + iounmap(dev->hcr); + return -ENOMEM; + } + + return 0; +} + +void mthca_cmd_cleanup(struct mthca_dev *dev) +{ + pci_pool_destroy(dev->cmd.pool); + iounmap(dev->hcr); +} + /* * Switch to using events to issue FW commands (should be called after * event queue to command events has been initialized). @@ -489,6 +519,33 @@ void mthca_cmd_use_polling(struct mthca_dev *dev) up(&dev->cmd.poll_sem); } +struct mthca_mailbox *mthca_alloc_mailbox(struct mthca_dev *dev, + unsigned int gfp_mask) +{ + struct mthca_mailbox *mailbox; + + mailbox = kmalloc(sizeof *mailbox, gfp_mask); + if (!mailbox) + return ERR_PTR(-ENOMEM); + + mailbox->buf = pci_pool_alloc(dev->cmd.pool, gfp_mask, &mailbox->dma); + if (!mailbox->buf) { + kfree(mailbox); + return ERR_PTR(-ENOMEM); + } + + return mailbox; +} + +void mthca_free_mailbox(struct mthca_dev *dev, struct mthca_mailbox *mailbox) +{ + if (!mailbox) + return; + + pci_pool_free(dev->cmd.pool, mailbox->buf, mailbox->dma); + kfree(mailbox); +} + int mthca_SYS_EN(struct mthca_dev *dev, u8 *status) { u64 out; @@ -513,20 +570,20 @@ int mthca_SYS_DIS(struct mthca_dev *dev, u8 *status) static int mthca_map_cmd(struct mthca_dev *dev, u16 op, struct mthca_icm *icm, u64 virt, u8 *status) { - u32 *inbox; - dma_addr_t indma; + struct mthca_mailbox *mailbox; struct mthca_icm_iter iter; + __be64 *pages; int lg; int nent = 0; int i; int err = 0; int ts = 0, tc = 0; - inbox = pci_alloc_consistent(dev->pdev, PAGE_SIZE, &indma); - if (!inbox) - return -ENOMEM; - - memset(inbox, 0, PAGE_SIZE); + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + memset(mailbox->buf, 0, MTHCA_MAILBOX_SIZE); + pages = mailbox->buf; for (mthca_icm_first(icm, &iter); !mthca_icm_last(&iter); @@ -546,19 +603,17 @@ static int mthca_map_cmd(struct mthca_dev *dev, u16 op, struct mthca_icm *icm, } for (i = 0; i < mthca_icm_size(&iter) / (1 << lg); ++i, ++nent) { if (virt != -1) { - *((__be64 *) (inbox + nent * 4)) = - cpu_to_be64(virt); + pages[nent * 2] = cpu_to_be64(virt); virt += 1 << lg; } - *((__be64 *) (inbox + nent * 4 + 2)) = - cpu_to_be64((mthca_icm_addr(&iter) + - (i << lg)) | (lg - 12)); + pages[nent * 2 + 1] = cpu_to_be64((mthca_icm_addr(&iter) + + (i << lg)) | (lg - 12)); ts += 1 << (lg - 10); ++tc; - if (nent == PAGE_SIZE / 16) { - err = mthca_cmd(dev, indma, nent, 0, op, + if (nent == MTHCA_MAILBOX_SIZE / 16) { + err = mthca_cmd(dev, mailbox->dma, nent, 0, op, CMD_TIME_CLASS_B, status); if (err || *status) goto out; @@ -568,7 +623,7 @@ static int mthca_map_cmd(struct mthca_dev *dev, u16 op, struct mthca_icm *icm, } if (nent) - err = mthca_cmd(dev, indma, nent, 0, op, + err = mthca_cmd(dev, mailbox->dma, nent, 0, op, CMD_TIME_CLASS_B, status); switch (op) { @@ -585,7 +640,7 @@ static int mthca_map_cmd(struct mthca_dev *dev, u16 op, struct mthca_icm *icm, } out: - pci_free_consistent(dev->pdev, PAGE_SIZE, inbox, indma); + mthca_free_mailbox(dev, mailbox); return err; } @@ -606,8 +661,8 @@ int mthca_RUN_FW(struct mthca_dev *dev, u8 *status) int mthca_QUERY_FW(struct mthca_dev *dev, u8 *status) { + struct mthca_mailbox *mailbox; u32 *outbox; - dma_addr_t outdma; int err = 0; u8 lg; @@ -625,12 +680,12 @@ int mthca_QUERY_FW(struct mthca_dev *dev, u8 *status) #define QUERY_FW_EQ_ARM_BASE_OFFSET 0x40 #define QUERY_FW_EQ_SET_CI_BASE_OFFSET 0x48 - outbox = pci_alloc_consistent(dev->pdev, QUERY_FW_OUT_SIZE, &outdma); - if (!outbox) { - return -ENOMEM; - } + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + outbox = mailbox->buf; - err = mthca_cmd_box(dev, 0, outdma, 0, 0, CMD_QUERY_FW, + err = mthca_cmd_box(dev, 0, mailbox->dma, 0, 0, CMD_QUERY_FW, CMD_TIME_CLASS_A, status); if (err) @@ -681,15 +736,15 @@ int mthca_QUERY_FW(struct mthca_dev *dev, u8 *status) } out: - pci_free_consistent(dev->pdev, QUERY_FW_OUT_SIZE, outbox, outdma); + mthca_free_mailbox(dev, mailbox); return err; } int mthca_ENABLE_LAM(struct mthca_dev *dev, u8 *status) { + struct mthca_mailbox *mailbox; u8 info; u32 *outbox; - dma_addr_t outdma; int err = 0; #define ENABLE_LAM_OUT_SIZE 0x100 @@ -700,11 +755,12 @@ int mthca_ENABLE_LAM(struct mthca_dev *dev, u8 *status) #define ENABLE_LAM_INFO_HIDDEN_FLAG (1 << 4) #define ENABLE_LAM_INFO_ECC_MASK 0x3 - outbox = pci_alloc_consistent(dev->pdev, ENABLE_LAM_OUT_SIZE, &outdma); - if (!outbox) - return -ENOMEM; + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + outbox = mailbox->buf; - err = mthca_cmd_box(dev, 0, outdma, 0, 0, CMD_ENABLE_LAM, + err = mthca_cmd_box(dev, 0, mailbox->dma, 0, 0, CMD_ENABLE_LAM, CMD_TIME_CLASS_C, status); if (err) @@ -733,7 +789,7 @@ int mthca_ENABLE_LAM(struct mthca_dev *dev, u8 *status) (unsigned long long) dev->ddr_end); out: - pci_free_consistent(dev->pdev, ENABLE_LAM_OUT_SIZE, outbox, outdma); + mthca_free_mailbox(dev, mailbox); return err; } @@ -744,9 +800,9 @@ int mthca_DISABLE_LAM(struct mthca_dev *dev, u8 *status) int mthca_QUERY_DDR(struct mthca_dev *dev, u8 *status) { + struct mthca_mailbox *mailbox; u8 info; u32 *outbox; - dma_addr_t outdma; int err = 0; #define QUERY_DDR_OUT_SIZE 0x100 @@ -757,11 +813,12 @@ int mthca_QUERY_DDR(struct mthca_dev *dev, u8 *status) #define QUERY_DDR_INFO_HIDDEN_FLAG (1 << 4) #define QUERY_DDR_INFO_ECC_MASK 0x3 - outbox = pci_alloc_consistent(dev->pdev, QUERY_DDR_OUT_SIZE, &outdma); - if (!outbox) - return -ENOMEM; + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + outbox = mailbox->buf; - err = mthca_cmd_box(dev, 0, outdma, 0, 0, CMD_QUERY_DDR, + err = mthca_cmd_box(dev, 0, mailbox->dma, 0, 0, CMD_QUERY_DDR, CMD_TIME_CLASS_A, status); if (err) @@ -787,15 +844,15 @@ int mthca_QUERY_DDR(struct mthca_dev *dev, u8 *status) (unsigned long long) dev->ddr_end); out: - pci_free_consistent(dev->pdev, QUERY_DDR_OUT_SIZE, outbox, outdma); + mthca_free_mailbox(dev, mailbox); return err; } int mthca_QUERY_DEV_LIM(struct mthca_dev *dev, struct mthca_dev_lim *dev_lim, u8 *status) { + struct mthca_mailbox *mailbox; u32 *outbox; - dma_addr_t outdma; u8 field; u16 size; int err; @@ -860,11 +917,12 @@ int mthca_QUERY_DEV_LIM(struct mthca_dev *dev, #define QUERY_DEV_LIM_LAMR_OFFSET 0x9f #define QUERY_DEV_LIM_MAX_ICM_SZ_OFFSET 0xa0 - outbox = pci_alloc_consistent(dev->pdev, QUERY_DEV_LIM_OUT_SIZE, &outdma); - if (!outbox) - return -ENOMEM; + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + outbox = mailbox->buf; - err = mthca_cmd_box(dev, 0, outdma, 0, 0, CMD_QUERY_DEV_LIM, + err = mthca_cmd_box(dev, 0, mailbox->dma, 0, 0, CMD_QUERY_DEV_LIM, CMD_TIME_CLASS_A, status); if (err) @@ -1020,15 +1078,15 @@ int mthca_QUERY_DEV_LIM(struct mthca_dev *dev, } out: - pci_free_consistent(dev->pdev, QUERY_DEV_LIM_OUT_SIZE, outbox, outdma); + mthca_free_mailbox(dev, mailbox); return err; } int mthca_QUERY_ADAPTER(struct mthca_dev *dev, struct mthca_adapter *adapter, u8 *status) { + struct mthca_mailbox *mailbox; u32 *outbox; - dma_addr_t outdma; int err; #define QUERY_ADAPTER_OUT_SIZE 0x100 @@ -1037,23 +1095,24 @@ int mthca_QUERY_ADAPTER(struct mthca_dev *dev, #define QUERY_ADAPTER_REVISION_ID_OFFSET 0x08 #define QUERY_ADAPTER_INTA_PIN_OFFSET 0x10 - outbox = pci_alloc_consistent(dev->pdev, QUERY_ADAPTER_OUT_SIZE, &outdma); - if (!outbox) - return -ENOMEM; + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + outbox = mailbox->buf; - err = mthca_cmd_box(dev, 0, outdma, 0, 0, CMD_QUERY_ADAPTER, + err = mthca_cmd_box(dev, 0, mailbox->dma, 0, 0, CMD_QUERY_ADAPTER, CMD_TIME_CLASS_A, status); if (err) goto out; - MTHCA_GET(adapter->vendor_id, outbox, QUERY_ADAPTER_VENDOR_ID_OFFSET); - MTHCA_GET(adapter->device_id, outbox, QUERY_ADAPTER_DEVICE_ID_OFFSET); + MTHCA_GET(adapter->vendor_id, outbox, QUERY_ADAPTER_VENDOR_ID_OFFSET); + MTHCA_GET(adapter->device_id, outbox, QUERY_ADAPTER_DEVICE_ID_OFFSET); MTHCA_GET(adapter->revision_id, outbox, QUERY_ADAPTER_REVISION_ID_OFFSET); - MTHCA_GET(adapter->inta_pin, outbox, QUERY_ADAPTER_INTA_PIN_OFFSET); + MTHCA_GET(adapter->inta_pin, outbox, QUERY_ADAPTER_INTA_PIN_OFFSET); out: - pci_free_consistent(dev->pdev, QUERY_DEV_LIM_OUT_SIZE, outbox, outdma); + mthca_free_mailbox(dev, mailbox); return err; } @@ -1061,8 +1120,8 @@ int mthca_INIT_HCA(struct mthca_dev *dev, struct mthca_init_hca_param *param, u8 *status) { + struct mthca_mailbox *mailbox; u32 *inbox; - dma_addr_t indma; int err; #define INIT_HCA_IN_SIZE 0x200 @@ -1102,9 +1161,10 @@ int mthca_INIT_HCA(struct mthca_dev *dev, #define INIT_HCA_UAR_SCATCH_BASE_OFFSET (INIT_HCA_UAR_OFFSET + 0x10) #define INIT_HCA_UAR_CTX_BASE_OFFSET (INIT_HCA_UAR_OFFSET + 0x18) - inbox = pci_alloc_consistent(dev->pdev, INIT_HCA_IN_SIZE, &indma); - if (!inbox) - return -ENOMEM; + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + inbox = mailbox->buf; memset(inbox, 0, INIT_HCA_IN_SIZE); @@ -1167,10 +1227,9 @@ int mthca_INIT_HCA(struct mthca_dev *dev, MTHCA_PUT(inbox, param->uarc_base, INIT_HCA_UAR_CTX_BASE_OFFSET); } - err = mthca_cmd(dev, indma, 0, 0, CMD_INIT_HCA, - HZ, status); + err = mthca_cmd(dev, mailbox->dma, 0, 0, CMD_INIT_HCA, HZ, status); - pci_free_consistent(dev->pdev, INIT_HCA_IN_SIZE, inbox, indma); + mthca_free_mailbox(dev, mailbox); return err; } @@ -1178,8 +1237,8 @@ int mthca_INIT_IB(struct mthca_dev *dev, struct mthca_init_ib_param *param, int port, u8 *status) { + struct mthca_mailbox *mailbox; u32 *inbox; - dma_addr_t indma; int err; u32 flags; @@ -1199,9 +1258,10 @@ int mthca_INIT_IB(struct mthca_dev *dev, #define INIT_IB_NODE_GUID_OFFSET 0x18 #define INIT_IB_SI_GUID_OFFSET 0x20 - inbox = pci_alloc_consistent(dev->pdev, INIT_IB_IN_SIZE, &indma); - if (!inbox) - return -ENOMEM; + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + inbox = mailbox->buf; memset(inbox, 0, INIT_IB_IN_SIZE); @@ -1221,10 +1281,10 @@ int mthca_INIT_IB(struct mthca_dev *dev, MTHCA_PUT(inbox, param->node_guid, INIT_IB_NODE_GUID_OFFSET); MTHCA_PUT(inbox, param->si_guid, INIT_IB_SI_GUID_OFFSET); - err = mthca_cmd(dev, indma, port, 0, CMD_INIT_IB, + err = mthca_cmd(dev, mailbox->dma, port, 0, CMD_INIT_IB, CMD_TIME_CLASS_A, status); - pci_free_consistent(dev->pdev, INIT_HCA_IN_SIZE, inbox, indma); + mthca_free_mailbox(dev, mailbox); return err; } @@ -1241,8 +1301,8 @@ int mthca_CLOSE_HCA(struct mthca_dev *dev, int panic, u8 *status) int mthca_SET_IB(struct mthca_dev *dev, struct mthca_set_ib_param *param, int port, u8 *status) { + struct mthca_mailbox *mailbox; u32 *inbox; - dma_addr_t indma; int err; u32 flags = 0; @@ -1253,9 +1313,10 @@ int mthca_SET_IB(struct mthca_dev *dev, struct mthca_set_ib_param *param, #define SET_IB_CAP_MASK_OFFSET 0x04 #define SET_IB_SI_GUID_OFFSET 0x08 - inbox = pci_alloc_consistent(dev->pdev, SET_IB_IN_SIZE, &indma); - if (!inbox) - return -ENOMEM; + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + inbox = mailbox->buf; memset(inbox, 0, SET_IB_IN_SIZE); @@ -1266,10 +1327,10 @@ int mthca_SET_IB(struct mthca_dev *dev, struct mthca_set_ib_param *param, MTHCA_PUT(inbox, param->cap_mask, SET_IB_CAP_MASK_OFFSET); MTHCA_PUT(inbox, param->si_guid, SET_IB_SI_GUID_OFFSET); - err = mthca_cmd(dev, indma, port, 0, CMD_SET_IB, + err = mthca_cmd(dev, mailbox->dma, port, 0, CMD_SET_IB, CMD_TIME_CLASS_B, status); - pci_free_consistent(dev->pdev, INIT_HCA_IN_SIZE, inbox, indma); + mthca_free_mailbox(dev, mailbox); return err; } @@ -1280,20 +1341,22 @@ int mthca_MAP_ICM(struct mthca_dev *dev, struct mthca_icm *icm, u64 virt, u8 *st int mthca_MAP_ICM_page(struct mthca_dev *dev, u64 dma_addr, u64 virt, u8 *status) { + struct mthca_mailbox *mailbox; u64 *inbox; - dma_addr_t indma; int err; - inbox = pci_alloc_consistent(dev->pdev, 16, &indma); - if (!inbox) - return -ENOMEM; + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + inbox = mailbox->buf; inbox[0] = cpu_to_be64(virt); inbox[1] = cpu_to_be64(dma_addr); - err = mthca_cmd(dev, indma, 1, 0, CMD_MAP_ICM, CMD_TIME_CLASS_B, status); + err = mthca_cmd(dev, mailbox->dma, 1, 0, CMD_MAP_ICM, + CMD_TIME_CLASS_B, status); - pci_free_consistent(dev->pdev, 16, inbox, indma); + mthca_free_mailbox(dev, mailbox); if (!err) mthca_dbg(dev, "Mapped page at %llx to %llx for ICM.\n", @@ -1338,69 +1401,26 @@ int mthca_SET_ICM_SIZE(struct mthca_dev *dev, u64 icm_size, u64 *aux_pages, return 0; } -int mthca_SW2HW_MPT(struct mthca_dev *dev, void *mpt_entry, +int mthca_SW2HW_MPT(struct mthca_dev *dev, struct mthca_mailbox *mailbox, int mpt_index, u8 *status) { - dma_addr_t indma; - int err; - - indma = pci_map_single(dev->pdev, mpt_entry, - MTHCA_MPT_ENTRY_SIZE, - PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(indma)) - return -ENOMEM; - - err = mthca_cmd(dev, indma, mpt_index, 0, CMD_SW2HW_MPT, - CMD_TIME_CLASS_B, status); - - pci_unmap_single(dev->pdev, indma, - MTHCA_MPT_ENTRY_SIZE, PCI_DMA_TODEVICE); - return err; + return mthca_cmd(dev, mailbox->dma, mpt_index, 0, CMD_SW2HW_MPT, + CMD_TIME_CLASS_B, status); } -int mthca_HW2SW_MPT(struct mthca_dev *dev, void *mpt_entry, +int mthca_HW2SW_MPT(struct mthca_dev *dev, struct mthca_mailbox *mailbox, int mpt_index, u8 *status) { - dma_addr_t outdma = 0; - int err; - - if (mpt_entry) { - outdma = pci_map_single(dev->pdev, mpt_entry, - MTHCA_MPT_ENTRY_SIZE, - PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(outdma)) - return -ENOMEM; - } - - err = mthca_cmd_box(dev, 0, outdma, mpt_index, !mpt_entry, - CMD_HW2SW_MPT, - CMD_TIME_CLASS_B, status); - - if (mpt_entry) - pci_unmap_single(dev->pdev, outdma, - MTHCA_MPT_ENTRY_SIZE, - PCI_DMA_FROMDEVICE); - return err; + return mthca_cmd_box(dev, 0, mailbox ? mailbox->dma : 0, mpt_index, + !mailbox, CMD_HW2SW_MPT, + CMD_TIME_CLASS_B, status); } -int mthca_WRITE_MTT(struct mthca_dev *dev, u64 *mtt_entry, +int mthca_WRITE_MTT(struct mthca_dev *dev, struct mthca_mailbox *mailbox, int num_mtt, u8 *status) { - dma_addr_t indma; - int err; - - indma = pci_map_single(dev->pdev, mtt_entry, - (num_mtt + 2) * 8, - PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(indma)) - return -ENOMEM; - - err = mthca_cmd(dev, indma, num_mtt, 0, CMD_WRITE_MTT, - CMD_TIME_CLASS_B, status); - - pci_unmap_single(dev->pdev, indma, - (num_mtt + 2) * 8, PCI_DMA_TODEVICE); - return err; + return mthca_cmd(dev, mailbox->dma, num_mtt, 0, CMD_WRITE_MTT, + CMD_TIME_CLASS_B, status); } int mthca_SYNC_TPT(struct mthca_dev *dev, u8 *status) @@ -1418,92 +1438,38 @@ int mthca_MAP_EQ(struct mthca_dev *dev, u64 event_mask, int unmap, 0, CMD_MAP_EQ, CMD_TIME_CLASS_B, status); } -int mthca_SW2HW_EQ(struct mthca_dev *dev, void *eq_context, +int mthca_SW2HW_EQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox, int eq_num, u8 *status) { - dma_addr_t indma; - int err; - - indma = pci_map_single(dev->pdev, eq_context, - MTHCA_EQ_CONTEXT_SIZE, - PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(indma)) - return -ENOMEM; - - err = mthca_cmd(dev, indma, eq_num, 0, CMD_SW2HW_EQ, - CMD_TIME_CLASS_A, status); - - pci_unmap_single(dev->pdev, indma, - MTHCA_EQ_CONTEXT_SIZE, PCI_DMA_TODEVICE); - return err; + return mthca_cmd(dev, mailbox->dma, eq_num, 0, CMD_SW2HW_EQ, + CMD_TIME_CLASS_A, status); } -int mthca_HW2SW_EQ(struct mthca_dev *dev, void *eq_context, +int mthca_HW2SW_EQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox, int eq_num, u8 *status) { - dma_addr_t outdma = 0; - int err; - - outdma = pci_map_single(dev->pdev, eq_context, - MTHCA_EQ_CONTEXT_SIZE, - PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(outdma)) - return -ENOMEM; - - err = mthca_cmd_box(dev, 0, outdma, eq_num, 0, - CMD_HW2SW_EQ, - CMD_TIME_CLASS_A, status); - - pci_unmap_single(dev->pdev, outdma, - MTHCA_EQ_CONTEXT_SIZE, - PCI_DMA_FROMDEVICE); - return err; + return mthca_cmd_box(dev, 0, mailbox->dma, eq_num, 0, + CMD_HW2SW_EQ, + CMD_TIME_CLASS_A, status); } -int mthca_SW2HW_CQ(struct mthca_dev *dev, void *cq_context, +int mthca_SW2HW_CQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox, int cq_num, u8 *status) { - dma_addr_t indma; - int err; - - indma = pci_map_single(dev->pdev, cq_context, - MTHCA_CQ_CONTEXT_SIZE, - PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(indma)) - return -ENOMEM; - - err = mthca_cmd(dev, indma, cq_num, 0, CMD_SW2HW_CQ, + return mthca_cmd(dev, mailbox->dma, cq_num, 0, CMD_SW2HW_CQ, CMD_TIME_CLASS_A, status); - - pci_unmap_single(dev->pdev, indma, - MTHCA_CQ_CONTEXT_SIZE, PCI_DMA_TODEVICE); - return err; } -int mthca_HW2SW_CQ(struct mthca_dev *dev, void *cq_context, +int mthca_HW2SW_CQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox, int cq_num, u8 *status) { - dma_addr_t outdma = 0; - int err; - - outdma = pci_map_single(dev->pdev, cq_context, - MTHCA_CQ_CONTEXT_SIZE, - PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(outdma)) - return -ENOMEM; - - err = mthca_cmd_box(dev, 0, outdma, cq_num, 0, - CMD_HW2SW_CQ, - CMD_TIME_CLASS_A, status); - - pci_unmap_single(dev->pdev, outdma, - MTHCA_CQ_CONTEXT_SIZE, - PCI_DMA_FROMDEVICE); - return err; + return mthca_cmd_box(dev, 0, mailbox->dma, cq_num, 0, + CMD_HW2SW_CQ, + CMD_TIME_CLASS_A, status); } int mthca_MODIFY_QP(struct mthca_dev *dev, int trans, u32 num, - int is_ee, void *qp_context, u32 optmask, + int is_ee, struct mthca_mailbox *mailbox, u32 optmask, u8 *status) { static const u16 op[] = { @@ -1520,36 +1486,34 @@ int mthca_MODIFY_QP(struct mthca_dev *dev, int trans, u32 num, [MTHCA_TRANS_ANY2RST] = CMD_ERR2RST_QPEE }; u8 op_mod = 0; - - dma_addr_t indma; + int my_mailbox = 0; int err; if (trans < 0 || trans >= ARRAY_SIZE(op)) return -EINVAL; if (trans == MTHCA_TRANS_ANY2RST) { - indma = 0; op_mod = 3; /* don't write outbox, any->reset */ /* For debugging */ - qp_context = pci_alloc_consistent(dev->pdev, MTHCA_QP_CONTEXT_SIZE, - &indma); - op_mod = 2; /* write outbox, any->reset */ + if (!mailbox) { + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (!IS_ERR(mailbox)) { + my_mailbox = 1; + op_mod = 2; /* write outbox, any->reset */ + } else + mailbox = NULL; + } } else { - indma = pci_map_single(dev->pdev, qp_context, - MTHCA_QP_CONTEXT_SIZE, - PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(indma)) - return -ENOMEM; - if (0) { int i; mthca_dbg(dev, "Dumping QP context:\n"); - printk(" opt param mask: %08x\n", be32_to_cpup(qp_context)); + printk(" opt param mask: %08x\n", be32_to_cpup(mailbox->buf)); for (i = 0; i < 0x100 / 4; ++i) { if (i % 8 == 0) printk(" [%02x] ", i * 4); - printk(" %08x", be32_to_cpu(((u32 *) qp_context)[i + 2])); + printk(" %08x", + be32_to_cpu(((u32 *) mailbox->buf)[i + 2])); if ((i + 1) % 8 == 0) printk("\n"); } @@ -1557,55 +1521,39 @@ int mthca_MODIFY_QP(struct mthca_dev *dev, int trans, u32 num, } if (trans == MTHCA_TRANS_ANY2RST) { - err = mthca_cmd_box(dev, 0, indma, (!!is_ee << 24) | num, - op_mod, op[trans], CMD_TIME_CLASS_C, status); + err = mthca_cmd_box(dev, 0, mailbox ? mailbox->dma : 0, + (!!is_ee << 24) | num, op_mod, + op[trans], CMD_TIME_CLASS_C, status); - if (0) { + if (0 && mailbox) { int i; mthca_dbg(dev, "Dumping QP context:\n"); - printk(" %08x\n", be32_to_cpup(qp_context)); + printk(" %08x\n", be32_to_cpup(mailbox->buf)); for (i = 0; i < 0x100 / 4; ++i) { if (i % 8 == 0) printk("[%02x] ", i * 4); - printk(" %08x", be32_to_cpu(((u32 *) qp_context)[i + 2])); + printk(" %08x", + be32_to_cpu(((u32 *) mailbox->buf)[i + 2])); if ((i + 1) % 8 == 0) printk("\n"); } } } else - err = mthca_cmd(dev, indma, (!!is_ee << 24) | num, + err = mthca_cmd(dev, mailbox->dma, (!!is_ee << 24) | num, op_mod, op[trans], CMD_TIME_CLASS_C, status); - if (trans != MTHCA_TRANS_ANY2RST) - pci_unmap_single(dev->pdev, indma, - MTHCA_QP_CONTEXT_SIZE, PCI_DMA_TODEVICE); - else - pci_free_consistent(dev->pdev, MTHCA_QP_CONTEXT_SIZE, - qp_context, indma); + if (my_mailbox) + mthca_free_mailbox(dev, mailbox); + return err; } int mthca_QUERY_QP(struct mthca_dev *dev, u32 num, int is_ee, - void *qp_context, u8 *status) + struct mthca_mailbox *mailbox, u8 *status) { - dma_addr_t outdma = 0; - int err; - - outdma = pci_map_single(dev->pdev, qp_context, - MTHCA_QP_CONTEXT_SIZE, - PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(outdma)) - return -ENOMEM; - - err = mthca_cmd_box(dev, 0, outdma, (!!is_ee << 24) | num, 0, - CMD_QUERY_QPEE, - CMD_TIME_CLASS_A, status); - - pci_unmap_single(dev->pdev, outdma, - MTHCA_QP_CONTEXT_SIZE, - PCI_DMA_FROMDEVICE); - return err; + return mthca_cmd_box(dev, 0, mailbox->dma, (!!is_ee << 24) | num, 0, + CMD_QUERY_QPEE, CMD_TIME_CLASS_A, status); } int mthca_CONF_SPECIAL_QP(struct mthca_dev *dev, int type, u32 qpn, @@ -1635,11 +1583,11 @@ int mthca_CONF_SPECIAL_QP(struct mthca_dev *dev, int type, u32 qpn, } int mthca_MAD_IFC(struct mthca_dev *dev, int ignore_mkey, int ignore_bkey, - int port, struct ib_wc* in_wc, struct ib_grh* in_grh, + int port, struct ib_wc *in_wc, struct ib_grh *in_grh, void *in_mad, void *response_mad, u8 *status) { - void *box; - dma_addr_t dma; + struct mthca_mailbox *inmailbox, *outmailbox; + void *inbox; int err; u32 in_modifier = port; u8 op_modifier = 0; @@ -1653,11 +1601,18 @@ int mthca_MAD_IFC(struct mthca_dev *dev, int ignore_mkey, int ignore_bkey, #define MAD_IFC_PKEY_OFFSET 0x10e #define MAD_IFC_GRH_OFFSET 0x140 - box = pci_alloc_consistent(dev->pdev, MAD_IFC_BOX_SIZE, &dma); - if (!box) - return -ENOMEM; + inmailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(inmailbox)) + return PTR_ERR(inmailbox); + inbox = inmailbox->buf; - memcpy(box, in_mad, 256); + outmailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(outmailbox)) { + mthca_free_mailbox(dev, inmailbox); + return PTR_ERR(outmailbox); + } + + memcpy(inbox, in_mad, 256); /* * Key check traps can't be generated unless we have in_wc to @@ -1671,97 +1626,65 @@ int mthca_MAD_IFC(struct mthca_dev *dev, int ignore_mkey, int ignore_bkey, if (in_wc) { u8 val; - memset(box + 256, 0, 256); + memset(inbox + 256, 0, 256); - MTHCA_PUT(box, in_wc->qp_num, MAD_IFC_MY_QPN_OFFSET); - MTHCA_PUT(box, in_wc->src_qp, MAD_IFC_RQPN_OFFSET); + MTHCA_PUT(inbox, in_wc->qp_num, MAD_IFC_MY_QPN_OFFSET); + MTHCA_PUT(inbox, in_wc->src_qp, MAD_IFC_RQPN_OFFSET); val = in_wc->sl << 4; - MTHCA_PUT(box, val, MAD_IFC_SL_OFFSET); + MTHCA_PUT(inbox, val, MAD_IFC_SL_OFFSET); val = in_wc->dlid_path_bits | (in_wc->wc_flags & IB_WC_GRH ? 0x80 : 0); - MTHCA_PUT(box, val, MAD_IFC_GRH_OFFSET); + MTHCA_PUT(inbox, val, MAD_IFC_GRH_OFFSET); - MTHCA_PUT(box, in_wc->slid, MAD_IFC_RLID_OFFSET); - MTHCA_PUT(box, in_wc->pkey_index, MAD_IFC_PKEY_OFFSET); + MTHCA_PUT(inbox, in_wc->slid, MAD_IFC_RLID_OFFSET); + MTHCA_PUT(inbox, in_wc->pkey_index, MAD_IFC_PKEY_OFFSET); if (in_grh) - memcpy((u8 *) box + MAD_IFC_GRH_OFFSET, in_grh, 40); + memcpy(inbox + MAD_IFC_GRH_OFFSET, in_grh, 40); op_modifier |= 0x10; in_modifier |= in_wc->slid << 16; } - err = mthca_cmd_box(dev, dma, dma + 512, in_modifier, op_modifier, + err = mthca_cmd_box(dev, inmailbox->dma, outmailbox->dma, + in_modifier, op_modifier, CMD_MAD_IFC, CMD_TIME_CLASS_C, status); if (!err && !*status) - memcpy(response_mad, box + 512, 256); + memcpy(response_mad, outmailbox->buf, 256); - pci_free_consistent(dev->pdev, MAD_IFC_BOX_SIZE, box, dma); + mthca_free_mailbox(dev, inmailbox); + mthca_free_mailbox(dev, outmailbox); return err; } -int mthca_READ_MGM(struct mthca_dev *dev, int index, void *mgm, - u8 *status) +int mthca_READ_MGM(struct mthca_dev *dev, int index, + struct mthca_mailbox *mailbox, u8 *status) { - dma_addr_t outdma = 0; - int err; - - outdma = pci_map_single(dev->pdev, mgm, - MTHCA_MGM_ENTRY_SIZE, - PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(outdma)) - return -ENOMEM; - - err = mthca_cmd_box(dev, 0, outdma, index, 0, - CMD_READ_MGM, - CMD_TIME_CLASS_A, status); - - pci_unmap_single(dev->pdev, outdma, - MTHCA_MGM_ENTRY_SIZE, - PCI_DMA_FROMDEVICE); - return err; + return mthca_cmd_box(dev, 0, mailbox->dma, index, 0, + CMD_READ_MGM, CMD_TIME_CLASS_A, status); } -int mthca_WRITE_MGM(struct mthca_dev *dev, int index, void *mgm, - u8 *status) +int mthca_WRITE_MGM(struct mthca_dev *dev, int index, + struct mthca_mailbox *mailbox, u8 *status) { - dma_addr_t indma; - int err; - - indma = pci_map_single(dev->pdev, mgm, - MTHCA_MGM_ENTRY_SIZE, - PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(indma)) - return -ENOMEM; - - err = mthca_cmd(dev, indma, index, 0, CMD_WRITE_MGM, - CMD_TIME_CLASS_A, status); - - pci_unmap_single(dev->pdev, indma, - MTHCA_MGM_ENTRY_SIZE, PCI_DMA_TODEVICE); - return err; + return mthca_cmd(dev, mailbox->dma, index, 0, CMD_WRITE_MGM, + CMD_TIME_CLASS_A, status); } -int mthca_MGID_HASH(struct mthca_dev *dev, void *gid, u16 *hash, - u8 *status) +int mthca_MGID_HASH(struct mthca_dev *dev, struct mthca_mailbox *mailbox, + u16 *hash, u8 *status) { - dma_addr_t indma; u64 imm; int err; - indma = pci_map_single(dev->pdev, gid, 16, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(indma)) - return -ENOMEM; - - err = mthca_cmd_imm(dev, indma, &imm, 0, 0, CMD_MGID_HASH, + err = mthca_cmd_imm(dev, mailbox->dma, &imm, 0, 0, CMD_MGID_HASH, CMD_TIME_CLASS_A, status); - *hash = imm; - pci_unmap_single(dev->pdev, indma, 16, PCI_DMA_TODEVICE); + *hash = imm; return err; } diff --git a/drivers/infiniband/hw/mthca/mthca_cmd.h b/drivers/infiniband/hw/mthca/mthca_cmd.h index adf039b..ed517f1 100644 --- a/drivers/infiniband/hw/mthca/mthca_cmd.h +++ b/drivers/infiniband/hw/mthca/mthca_cmd.h @@ -37,8 +37,7 @@ #include <ib_verbs.h> -#define MTHCA_CMD_MAILBOX_ALIGN 16UL -#define MTHCA_CMD_MAILBOX_EXTRA (MTHCA_CMD_MAILBOX_ALIGN - 1) +#define MTHCA_MAILBOX_SIZE 4096 enum { /* command completed successfully: */ @@ -112,6 +111,11 @@ enum { DEV_LIM_FLAG_UD_MULTI = 1 << 21, }; +struct mthca_mailbox { + dma_addr_t dma; + void *buf; +}; + struct mthca_dev_lim { int max_srq_sz; int max_qp_sz; @@ -235,11 +239,17 @@ struct mthca_set_ib_param { u32 cap_mask; }; +int mthca_cmd_init(struct mthca_dev *dev); +void mthca_cmd_cleanup(struct mthca_dev *dev); int mthca_cmd_use_events(struct mthca_dev *dev); void mthca_cmd_use_polling(struct mthca_dev *dev); void mthca_cmd_event(struct mthca_dev *dev, u16 token, u8 status, u64 out_param); +struct mthca_mailbox *mthca_alloc_mailbox(struct mthca_dev *dev, + unsigned int gfp_mask); +void mthca_free_mailbox(struct mthca_dev *dev, struct mthca_mailbox *mailbox); + int mthca_SYS_EN(struct mthca_dev *dev, u8 *status); int mthca_SYS_DIS(struct mthca_dev *dev, u8 *status); int mthca_MAP_FA(struct mthca_dev *dev, struct mthca_icm *icm, u8 *status); @@ -270,41 +280,39 @@ int mthca_MAP_ICM_AUX(struct mthca_dev *dev, struct mthca_icm *icm, u8 *status); int mthca_UNMAP_ICM_AUX(struct mthca_dev *dev, u8 *status); int mthca_SET_ICM_SIZE(struct mthca_dev *dev, u64 icm_size, u64 *aux_pages, u8 *status); -int mthca_SW2HW_MPT(struct mthca_dev *dev, void *mpt_entry, +int mthca_SW2HW_MPT(struct mthca_dev *dev, struct mthca_mailbox *mailbox, int mpt_index, u8 *status); -int mthca_HW2SW_MPT(struct mthca_dev *dev, void *mpt_entry, +int mthca_HW2SW_MPT(struct mthca_dev *dev, struct mthca_mailbox *mailbox, int mpt_index, u8 *status); -int mthca_WRITE_MTT(struct mthca_dev *dev, u64 *mtt_entry, +int mthca_WRITE_MTT(struct mthca_dev *dev, struct mthca_mailbox *mailbox, int num_mtt, u8 *status); int mthca_SYNC_TPT(struct mthca_dev *dev, u8 *status); int mthca_MAP_EQ(struct mthca_dev *dev, u64 event_mask, int unmap, int eq_num, u8 *status); -int mthca_SW2HW_EQ(struct mthca_dev *dev, void *eq_context, +int mthca_SW2HW_EQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox, int eq_num, u8 *status); -int mthca_HW2SW_EQ(struct mthca_dev *dev, void *eq_context, +int mthca_HW2SW_EQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox, int eq_num, u8 *status); -int mthca_SW2HW_CQ(struct mthca_dev *dev, void *cq_context, +int mthca_SW2HW_CQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox, int cq_num, u8 *status); -int mthca_HW2SW_CQ(struct mthca_dev *dev, void *cq_context, +int mthca_HW2SW_CQ(struct mthca_dev *dev, struct mthca_mailbox *mailbox, int cq_num, u8 *status); int mthca_MODIFY_QP(struct mthca_dev *dev, int trans, u32 num, - int is_ee, void *qp_context, u32 optmask, + int is_ee, struct mthca_mailbox *mailbox, u32 optmask, u8 *status); int mthca_QUERY_QP(struct mthca_dev *dev, u32 num, int is_ee, - void *qp_context, u8 *status); + struct mthca_mailbox *mailbox, u8 *status); int mthca_CONF_SPECIAL_QP(struct mthca_dev *dev, int type, u32 qpn, u8 *status); int mthca_MAD_IFC(struct mthca_dev *dev, int ignore_mkey, int ignore_bkey, - int port, struct ib_wc* in_wc, struct ib_grh* in_grh, + int port, struct ib_wc *in_wc, struct ib_grh *in_grh, void *in_mad, void *response_mad, u8 *status); -int mthca_READ_MGM(struct mthca_dev *dev, int index, void *mgm, - u8 *status); -int mthca_WRITE_MGM(struct mthca_dev *dev, int index, void *mgm, - u8 *status); -int mthca_MGID_HASH(struct mthca_dev *dev, void *gid, u16 *hash, - u8 *status); +int mthca_READ_MGM(struct mthca_dev *dev, int index, + struct mthca_mailbox *mailbox, u8 *status); +int mthca_WRITE_MGM(struct mthca_dev *dev, int index, + struct mthca_mailbox *mailbox, u8 *status); +int mthca_MGID_HASH(struct mthca_dev *dev, struct mthca_mailbox *mailbox, + u16 *hash, u8 *status); int mthca_NOP(struct mthca_dev *dev, u8 *status); -#define MAILBOX_ALIGN(x) ((void *) ALIGN((unsigned long) (x), MTHCA_CMD_MAILBOX_ALIGN)) - #endif /* MTHCA_CMD_H */ diff --git a/drivers/infiniband/hw/mthca/mthca_cq.c b/drivers/infiniband/hw/mthca/mthca_cq.c index 2bf347b..766e9031 100644 --- a/drivers/infiniband/hw/mthca/mthca_cq.c +++ b/drivers/infiniband/hw/mthca/mthca_cq.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -171,6 +172,17 @@ static inline void set_cqe_hw(struct mthca_cqe *cqe) cqe->owner = MTHCA_CQ_ENTRY_OWNER_HW; } +static void dump_cqe(struct mthca_dev *dev, void *cqe_ptr) +{ + __be32 *cqe = cqe_ptr; + + (void) cqe; /* avoid warning if mthca_dbg compiled away... */ + mthca_dbg(dev, "CQE contents %08x %08x %08x %08x %08x %08x %08x %08x\n", + be32_to_cpu(cqe[0]), be32_to_cpu(cqe[1]), be32_to_cpu(cqe[2]), + be32_to_cpu(cqe[3]), be32_to_cpu(cqe[4]), be32_to_cpu(cqe[5]), + be32_to_cpu(cqe[6]), be32_to_cpu(cqe[7])); +} + /* * incr is ignored in native Arbel (mem-free) mode, so cq->cons_index * should be correct before calling update_cons_index(). @@ -280,16 +292,12 @@ static int handle_error_cqe(struct mthca_dev *dev, struct mthca_cq *cq, int dbd; u32 new_wqe; - if (1 && cqe->syndrome != SYNDROME_WR_FLUSH_ERR) { - int j; - - mthca_dbg(dev, "%x/%d: error CQE -> QPN %06x, WQE @ %08x\n", - cq->cqn, cq->cons_index, be32_to_cpu(cqe->my_qpn), - be32_to_cpu(cqe->wqe)); - - for (j = 0; j < 8; ++j) - printk(KERN_DEBUG " [%2x] %08x\n", - j * 4, be32_to_cpu(((u32 *) cqe)[j])); + if (cqe->syndrome == SYNDROME_LOCAL_QP_OP_ERR) { + mthca_dbg(dev, "local QP operation err " + "(QPN %06x, WQE @ %08x, CQN %06x, index %d)\n", + be32_to_cpu(cqe->my_qpn), be32_to_cpu(cqe->wqe), + cq->cqn, cq->cons_index); + dump_cqe(dev, cqe); } /* @@ -377,15 +385,6 @@ static int handle_error_cqe(struct mthca_dev *dev, struct mthca_cq *cq, return 0; } -static void dump_cqe(struct mthca_cqe *cqe) -{ - int j; - - for (j = 0; j < 8; ++j) - printk(KERN_DEBUG " [%2x] %08x\n", - j * 4, be32_to_cpu(((u32 *) cqe)[j])); -} - static inline int mthca_poll_one(struct mthca_dev *dev, struct mthca_cq *cq, struct mthca_qp **cur_qp, @@ -414,8 +413,7 @@ static inline int mthca_poll_one(struct mthca_dev *dev, mthca_dbg(dev, "%x/%d: CQE -> QPN %06x, WQE @ %08x\n", cq->cqn, cq->cons_index, be32_to_cpu(cqe->my_qpn), be32_to_cpu(cqe->wqe)); - - dump_cqe(cqe); + dump_cqe(dev, cqe); } is_error = (cqe->opcode & MTHCA_ERROR_CQE_OPCODE_MASK) == @@ -638,19 +636,19 @@ static void mthca_free_cq_buf(struct mthca_dev *dev, struct mthca_cq *cq) int size; if (cq->is_direct) - pci_free_consistent(dev->pdev, - (cq->ibcq.cqe + 1) * MTHCA_CQ_ENTRY_SIZE, - cq->queue.direct.buf, - pci_unmap_addr(&cq->queue.direct, - mapping)); + dma_free_coherent(&dev->pdev->dev, + (cq->ibcq.cqe + 1) * MTHCA_CQ_ENTRY_SIZE, + cq->queue.direct.buf, + pci_unmap_addr(&cq->queue.direct, + mapping)); else { size = (cq->ibcq.cqe + 1) * MTHCA_CQ_ENTRY_SIZE; for (i = 0; i < (size + PAGE_SIZE - 1) / PAGE_SIZE; ++i) if (cq->queue.page_list[i].buf) - pci_free_consistent(dev->pdev, PAGE_SIZE, - cq->queue.page_list[i].buf, - pci_unmap_addr(&cq->queue.page_list[i], - mapping)); + dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, + cq->queue.page_list[i].buf, + pci_unmap_addr(&cq->queue.page_list[i], + mapping)); kfree(cq->queue.page_list); } @@ -670,8 +668,8 @@ static int mthca_alloc_cq_buf(struct mthca_dev *dev, int size, npages = 1; shift = get_order(size) + PAGE_SHIFT; - cq->queue.direct.buf = pci_alloc_consistent(dev->pdev, - size, &t); + cq->queue.direct.buf = dma_alloc_coherent(&dev->pdev->dev, + size, &t, GFP_KERNEL); if (!cq->queue.direct.buf) return -ENOMEM; @@ -709,7 +707,8 @@ static int mthca_alloc_cq_buf(struct mthca_dev *dev, int size, for (i = 0; i < npages; ++i) { cq->queue.page_list[i].buf = - pci_alloc_consistent(dev->pdev, PAGE_SIZE, &t); + dma_alloc_coherent(&dev->pdev->dev, PAGE_SIZE, + &t, GFP_KERNEL); if (!cq->queue.page_list[i].buf) goto err_free; @@ -746,7 +745,7 @@ int mthca_init_cq(struct mthca_dev *dev, int nent, struct mthca_cq *cq) { int size = nent * MTHCA_CQ_ENTRY_SIZE; - void *mailbox = NULL; + struct mthca_mailbox *mailbox; struct mthca_cq_context *cq_context; int err = -ENOMEM; u8 status; @@ -780,12 +779,11 @@ int mthca_init_cq(struct mthca_dev *dev, int nent, goto err_out_ci; } - mailbox = kmalloc(sizeof (struct mthca_cq_context) + MTHCA_CMD_MAILBOX_EXTRA, - GFP_KERNEL); - if (!mailbox) - goto err_out_mailbox; + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) + goto err_out_arm; - cq_context = MAILBOX_ALIGN(mailbox); + cq_context = mailbox->buf; err = mthca_alloc_cq_buf(dev, size, cq); if (err) @@ -816,7 +814,7 @@ int mthca_init_cq(struct mthca_dev *dev, int nent, cq_context->state_db = cpu_to_be32(cq->arm_db_index); } - err = mthca_SW2HW_CQ(dev, cq_context, cq->cqn, &status); + err = mthca_SW2HW_CQ(dev, mailbox, cq->cqn, &status); if (err) { mthca_warn(dev, "SW2HW_CQ failed (%d)\n", err); goto err_out_free_mr; @@ -840,7 +838,7 @@ int mthca_init_cq(struct mthca_dev *dev, int nent, cq->cons_index = 0; - kfree(mailbox); + mthca_free_mailbox(dev, mailbox); return 0; @@ -849,8 +847,9 @@ err_out_free_mr: mthca_free_cq_buf(dev, cq); err_out_mailbox: - kfree(mailbox); + mthca_free_mailbox(dev, mailbox); +err_out_arm: if (mthca_is_memfree(dev)) mthca_free_db(dev, MTHCA_DB_TYPE_CQ_ARM, cq->arm_db_index); @@ -870,28 +869,26 @@ err_out: void mthca_free_cq(struct mthca_dev *dev, struct mthca_cq *cq) { - void *mailbox; + struct mthca_mailbox *mailbox; int err; u8 status; might_sleep(); - mailbox = kmalloc(sizeof (struct mthca_cq_context) + MTHCA_CMD_MAILBOX_EXTRA, - GFP_KERNEL); - if (!mailbox) { + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) { mthca_warn(dev, "No memory for mailbox to free CQ.\n"); return; } - err = mthca_HW2SW_CQ(dev, MAILBOX_ALIGN(mailbox), cq->cqn, &status); + err = mthca_HW2SW_CQ(dev, mailbox, cq->cqn, &status); if (err) mthca_warn(dev, "HW2SW_CQ failed (%d)\n", err); else if (status) - mthca_warn(dev, "HW2SW_CQ returned status 0x%02x\n", - status); + mthca_warn(dev, "HW2SW_CQ returned status 0x%02x\n", status); if (0) { - u32 *ctx = MAILBOX_ALIGN(mailbox); + u32 *ctx = mailbox->buf; int j; printk(KERN_ERR "context for CQN %x (cons index %x, next sw %d)\n", @@ -919,11 +916,11 @@ void mthca_free_cq(struct mthca_dev *dev, if (mthca_is_memfree(dev)) { mthca_free_db(dev, MTHCA_DB_TYPE_CQ_ARM, cq->arm_db_index); mthca_free_db(dev, MTHCA_DB_TYPE_CQ_SET_CI, cq->set_ci_db_index); - mthca_table_put(dev, dev->cq_table.table, cq->cqn); } + mthca_table_put(dev, dev->cq_table.table, cq->cqn); mthca_free(&dev->cq_table.alloc, cq->cqn); - kfree(mailbox); + mthca_free_mailbox(dev, mailbox); } int __devinit mthca_init_cq_table(struct mthca_dev *dev) diff --git a/drivers/infiniband/hw/mthca/mthca_dev.h b/drivers/infiniband/hw/mthca/mthca_dev.h index e3d79e2..4127f09 100644 --- a/drivers/infiniband/hw/mthca/mthca_dev.h +++ b/drivers/infiniband/hw/mthca/mthca_dev.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -46,8 +47,8 @@ #define DRV_NAME "ib_mthca" #define PFX DRV_NAME ": " -#define DRV_VERSION "0.06-pre" -#define DRV_RELDATE "November 8, 2004" +#define DRV_VERSION "0.06" +#define DRV_RELDATE "June 23, 2005" enum { MTHCA_FLAG_DDR_HIDDEN = 1 << 1, @@ -98,6 +99,7 @@ enum { }; struct mthca_cmd { + struct pci_pool *pool; int use_events; struct semaphore hcr_sem; struct semaphore poll_sem; @@ -379,6 +381,12 @@ void mthca_uar_free(struct mthca_dev *dev, struct mthca_uar *uar); int mthca_pd_alloc(struct mthca_dev *dev, struct mthca_pd *pd); void mthca_pd_free(struct mthca_dev *dev, struct mthca_pd *pd); +struct mthca_mtt *mthca_alloc_mtt(struct mthca_dev *dev, int size); +void mthca_free_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt); +int mthca_write_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt, + int start_index, u64 *buffer_list, int list_len); +int mthca_mr_alloc(struct mthca_dev *dev, u32 pd, int buffer_size_shift, + u64 iova, u64 total_size, u32 access, struct mthca_mr *mr); int mthca_mr_alloc_notrans(struct mthca_dev *dev, u32 pd, u32 access, struct mthca_mr *mr); int mthca_mr_alloc_phys(struct mthca_dev *dev, u32 pd, diff --git a/drivers/infiniband/hw/mthca/mthca_doorbell.h b/drivers/infiniband/hw/mthca/mthca_doorbell.h index 821039a..535fad7 100644 --- a/drivers/infiniband/hw/mthca/mthca_doorbell.h +++ b/drivers/infiniband/hw/mthca/mthca_doorbell.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2004 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/mthca/mthca_eq.c b/drivers/infiniband/hw/mthca/mthca_eq.c index f46d615..cbcf2b4 100644 --- a/drivers/infiniband/hw/mthca/mthca_eq.c +++ b/drivers/infiniband/hw/mthca/mthca_eq.c @@ -469,7 +469,7 @@ static int __devinit mthca_create_eq(struct mthca_dev *dev, PAGE_SIZE; u64 *dma_list = NULL; dma_addr_t t; - void *mailbox = NULL; + struct mthca_mailbox *mailbox; struct mthca_eq_context *eq_context; int err = -ENOMEM; int i; @@ -494,17 +494,16 @@ static int __devinit mthca_create_eq(struct mthca_dev *dev, if (!dma_list) goto err_out_free; - mailbox = kmalloc(sizeof *eq_context + MTHCA_CMD_MAILBOX_EXTRA, - GFP_KERNEL); - if (!mailbox) + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) goto err_out_free; - eq_context = MAILBOX_ALIGN(mailbox); + eq_context = mailbox->buf; for (i = 0; i < npages; ++i) { - eq->page_list[i].buf = pci_alloc_consistent(dev->pdev, - PAGE_SIZE, &t); + eq->page_list[i].buf = dma_alloc_coherent(&dev->pdev->dev, + PAGE_SIZE, &t, GFP_KERNEL); if (!eq->page_list[i].buf) - goto err_out_free; + goto err_out_free_pages; dma_list[i] = t; pci_unmap_addr_set(&eq->page_list[i], mapping, t); @@ -517,7 +516,7 @@ static int __devinit mthca_create_eq(struct mthca_dev *dev, eq->eqn = mthca_alloc(&dev->eq_table.alloc); if (eq->eqn == -1) - goto err_out_free; + goto err_out_free_pages; err = mthca_mr_alloc_phys(dev, dev->driver_pd.pd_num, dma_list, PAGE_SHIFT, npages, @@ -548,7 +547,7 @@ static int __devinit mthca_create_eq(struct mthca_dev *dev, eq_context->intr = intr; eq_context->lkey = cpu_to_be32(eq->mr.ibmr.lkey); - err = mthca_SW2HW_EQ(dev, eq_context, eq->eqn, &status); + err = mthca_SW2HW_EQ(dev, mailbox, eq->eqn, &status); if (err) { mthca_warn(dev, "SW2HW_EQ failed (%d)\n", err); goto err_out_free_mr; @@ -561,7 +560,7 @@ static int __devinit mthca_create_eq(struct mthca_dev *dev, } kfree(dma_list); - kfree(mailbox); + mthca_free_mailbox(dev, mailbox); eq->eqn_mask = swab32(1 << eq->eqn); eq->cons_index = 0; @@ -579,17 +578,19 @@ static int __devinit mthca_create_eq(struct mthca_dev *dev, err_out_free_eq: mthca_free(&dev->eq_table.alloc, eq->eqn); - err_out_free: + err_out_free_pages: for (i = 0; i < npages; ++i) if (eq->page_list[i].buf) - pci_free_consistent(dev->pdev, PAGE_SIZE, - eq->page_list[i].buf, - pci_unmap_addr(&eq->page_list[i], - mapping)); + dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, + eq->page_list[i].buf, + pci_unmap_addr(&eq->page_list[i], + mapping)); + + mthca_free_mailbox(dev, mailbox); + err_out_free: kfree(eq->page_list); kfree(dma_list); - kfree(mailbox); err_out: return err; @@ -598,25 +599,22 @@ static int __devinit mthca_create_eq(struct mthca_dev *dev, static void mthca_free_eq(struct mthca_dev *dev, struct mthca_eq *eq) { - void *mailbox = NULL; + struct mthca_mailbox *mailbox; int err; u8 status; int npages = (eq->nent * MTHCA_EQ_ENTRY_SIZE + PAGE_SIZE - 1) / PAGE_SIZE; int i; - mailbox = kmalloc(sizeof (struct mthca_eq_context) + MTHCA_CMD_MAILBOX_EXTRA, - GFP_KERNEL); - if (!mailbox) + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) return; - err = mthca_HW2SW_EQ(dev, MAILBOX_ALIGN(mailbox), - eq->eqn, &status); + err = mthca_HW2SW_EQ(dev, mailbox, eq->eqn, &status); if (err) mthca_warn(dev, "HW2SW_EQ failed (%d)\n", err); if (status) - mthca_warn(dev, "HW2SW_EQ returned status 0x%02x\n", - status); + mthca_warn(dev, "HW2SW_EQ returned status 0x%02x\n", status); dev->eq_table.arm_mask &= ~eq->eqn_mask; @@ -625,7 +623,7 @@ static void mthca_free_eq(struct mthca_dev *dev, for (i = 0; i < sizeof (struct mthca_eq_context) / 4; ++i) { if (i % 4 == 0) printk("[%02x] ", i * 4); - printk(" %08x", be32_to_cpup(MAILBOX_ALIGN(mailbox) + i * 4)); + printk(" %08x", be32_to_cpup(mailbox->buf + i * 4)); if ((i + 1) % 4 == 0) printk("\n"); } @@ -638,7 +636,7 @@ static void mthca_free_eq(struct mthca_dev *dev, pci_unmap_addr(&eq->page_list[i], mapping)); kfree(eq->page_list); - kfree(mailbox); + mthca_free_mailbox(dev, mailbox); } static void mthca_free_irqs(struct mthca_dev *dev) @@ -709,8 +707,7 @@ static int __devinit mthca_map_eq_regs(struct mthca_dev *dev) if (mthca_map_reg(dev, ((pci_resource_len(dev->pdev, 0) - 1) & dev->fw.arbel.eq_arm_base) + 4, 4, &dev->eq_regs.arbel.eq_arm)) { - mthca_err(dev, "Couldn't map interrupt clear register, " - "aborting.\n"); + mthca_err(dev, "Couldn't map EQ arm register, aborting.\n"); mthca_unmap_reg(dev, (pci_resource_len(dev->pdev, 0) - 1) & dev->fw.arbel.clr_int_base, MTHCA_CLR_INT_SIZE, dev->clr_base); @@ -721,8 +718,7 @@ static int __devinit mthca_map_eq_regs(struct mthca_dev *dev) dev->fw.arbel.eq_set_ci_base, MTHCA_EQ_SET_CI_SIZE, &dev->eq_regs.arbel.eq_set_ci_base)) { - mthca_err(dev, "Couldn't map interrupt clear register, " - "aborting.\n"); + mthca_err(dev, "Couldn't map EQ CI register, aborting.\n"); mthca_unmap_reg(dev, ((pci_resource_len(dev->pdev, 0) - 1) & dev->fw.arbel.eq_arm_base) + 4, 4, dev->eq_regs.arbel.eq_arm); diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c index d405903..09519b6 100644 --- a/drivers/infiniband/hw/mthca/mthca_main.c +++ b/drivers/infiniband/hw/mthca/mthca_main.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -69,7 +70,7 @@ MODULE_PARM_DESC(msi, "attempt to use MSI if nonzero"); #endif /* CONFIG_PCI_MSI */ static const char mthca_version[] __devinitdata = - "ib_mthca: Mellanox InfiniBand HCA driver v" + DRV_NAME ": Mellanox InfiniBand HCA driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; static struct mthca_profile default_profile = { @@ -927,13 +928,13 @@ static int __devinit mthca_init_one(struct pci_dev *pdev, */ if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) || pci_resource_len(pdev, 0) != 1 << 20) { - dev_err(&pdev->dev, "Missing DCS, aborting."); + dev_err(&pdev->dev, "Missing DCS, aborting.\n"); err = -ENODEV; goto err_disable_pdev; } if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM) || pci_resource_len(pdev, 2) != 1 << 23) { - dev_err(&pdev->dev, "Missing UAR, aborting."); + dev_err(&pdev->dev, "Missing UAR, aborting.\n"); err = -ENODEV; goto err_disable_pdev; } @@ -1004,25 +1005,18 @@ static int __devinit mthca_init_one(struct pci_dev *pdev, !pci_enable_msi(pdev)) mdev->mthca_flags |= MTHCA_FLAG_MSI; - sema_init(&mdev->cmd.hcr_sem, 1); - sema_init(&mdev->cmd.poll_sem, 1); - mdev->cmd.use_events = 0; - - mdev->hcr = ioremap(pci_resource_start(pdev, 0) + MTHCA_HCR_BASE, MTHCA_HCR_SIZE); - if (!mdev->hcr) { - mthca_err(mdev, "Couldn't map command register, " - "aborting.\n"); - err = -ENOMEM; + if (mthca_cmd_init(mdev)) { + mthca_err(mdev, "Failed to init command interface, aborting.\n"); goto err_free_dev; } err = mthca_tune_pci(mdev); if (err) - goto err_iounmap; + goto err_cmd; err = mthca_init_hca(mdev); if (err) - goto err_iounmap; + goto err_cmd; if (mdev->fw_ver < mthca_hca_table[id->driver_data].latest_fw) { mthca_warn(mdev, "HCA FW version %x.%x.%x is old (%x.%x.%x is current).\n", @@ -1070,8 +1064,8 @@ err_cleanup: err_close: mthca_close_hca(mdev); -err_iounmap: - iounmap(mdev->hcr); +err_cmd: + mthca_cmd_cleanup(mdev); err_free_dev: if (mdev->mthca_flags & MTHCA_FLAG_MSI_X) @@ -1118,10 +1112,8 @@ static void __devexit mthca_remove_one(struct pci_dev *pdev) iounmap(mdev->kar); mthca_uar_free(mdev, &mdev->driver_uar); mthca_cleanup_uar_table(mdev); - mthca_close_hca(mdev); - - iounmap(mdev->hcr); + mthca_cmd_cleanup(mdev); if (mdev->mthca_flags & MTHCA_FLAG_MSI_X) pci_disable_msix(pdev); @@ -1163,7 +1155,7 @@ static struct pci_device_id mthca_pci_table[] = { MODULE_DEVICE_TABLE(pci, mthca_pci_table); static struct pci_driver mthca_driver = { - .name = "ib_mthca", + .name = DRV_NAME, .id_table = mthca_pci_table, .probe = mthca_init_one, .remove = __devexit_p(mthca_remove_one) diff --git a/drivers/infiniband/hw/mthca/mthca_mcg.c b/drivers/infiniband/hw/mthca/mthca_mcg.c index 70a6553..5be7d94 100644 --- a/drivers/infiniband/hw/mthca/mthca_mcg.c +++ b/drivers/infiniband/hw/mthca/mthca_mcg.c @@ -66,22 +66,23 @@ static const u8 zero_gid[16]; /* automatically initialized to 0 */ * entry in hash chain and *mgm holds end of hash chain. */ static int find_mgm(struct mthca_dev *dev, - u8 *gid, struct mthca_mgm *mgm, + u8 *gid, struct mthca_mailbox *mgm_mailbox, u16 *hash, int *prev, int *index) { - void *mailbox; + struct mthca_mailbox *mailbox; + struct mthca_mgm *mgm = mgm_mailbox->buf; u8 *mgid; int err; u8 status; - mailbox = kmalloc(16 + MTHCA_CMD_MAILBOX_EXTRA, GFP_KERNEL); - if (!mailbox) + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) return -ENOMEM; - mgid = MAILBOX_ALIGN(mailbox); + mgid = mailbox->buf; memcpy(mgid, gid, 16); - err = mthca_MGID_HASH(dev, mgid, hash, &status); + err = mthca_MGID_HASH(dev, mailbox, hash, &status); if (err) goto out; if (status) { @@ -103,7 +104,7 @@ static int find_mgm(struct mthca_dev *dev, *prev = -1; do { - err = mthca_READ_MGM(dev, *index, mgm, &status); + err = mthca_READ_MGM(dev, *index, mgm_mailbox, &status); if (err) goto out; if (status) { @@ -129,14 +130,14 @@ static int find_mgm(struct mthca_dev *dev, *index = -1; out: - kfree(mailbox); + mthca_free_mailbox(dev, mailbox); return err; } int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) { struct mthca_dev *dev = to_mdev(ibqp->device); - void *mailbox; + struct mthca_mailbox *mailbox; struct mthca_mgm *mgm; u16 hash; int index, prev; @@ -145,15 +146,15 @@ int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) int err; u8 status; - mailbox = kmalloc(sizeof *mgm + MTHCA_CMD_MAILBOX_EXTRA, GFP_KERNEL); - if (!mailbox) - return -ENOMEM; - mgm = MAILBOX_ALIGN(mailbox); + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + mgm = mailbox->buf; if (down_interruptible(&dev->mcg_table.sem)) return -EINTR; - err = find_mgm(dev, gid->raw, mgm, &hash, &prev, &index); + err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index); if (err) goto out; @@ -170,7 +171,7 @@ int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) goto out; } - err = mthca_READ_MGM(dev, index, mgm, &status); + err = mthca_READ_MGM(dev, index, mailbox, &status); if (err) goto out; if (status) { @@ -195,7 +196,7 @@ int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) goto out; } - err = mthca_WRITE_MGM(dev, index, mgm, &status); + err = mthca_WRITE_MGM(dev, index, mailbox, &status); if (err) goto out; if (status) { @@ -206,7 +207,7 @@ int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) if (!link) goto out; - err = mthca_READ_MGM(dev, prev, mgm, &status); + err = mthca_READ_MGM(dev, prev, mailbox, &status); if (err) goto out; if (status) { @@ -217,7 +218,7 @@ int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) mgm->next_gid_index = cpu_to_be32(index << 5); - err = mthca_WRITE_MGM(dev, prev, mgm, &status); + err = mthca_WRITE_MGM(dev, prev, mailbox, &status); if (err) goto out; if (status) { @@ -227,14 +228,14 @@ int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) out: up(&dev->mcg_table.sem); - kfree(mailbox); + mthca_free_mailbox(dev, mailbox); return err; } int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) { struct mthca_dev *dev = to_mdev(ibqp->device); - void *mailbox; + struct mthca_mailbox *mailbox; struct mthca_mgm *mgm; u16 hash; int prev, index; @@ -242,15 +243,15 @@ int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) int err; u8 status; - mailbox = kmalloc(sizeof *mgm + MTHCA_CMD_MAILBOX_EXTRA, GFP_KERNEL); - if (!mailbox) - return -ENOMEM; - mgm = MAILBOX_ALIGN(mailbox); + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + mgm = mailbox->buf; if (down_interruptible(&dev->mcg_table.sem)) return -EINTR; - err = find_mgm(dev, gid->raw, mgm, &hash, &prev, &index); + err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index); if (err) goto out; @@ -285,7 +286,7 @@ int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) mgm->qp[loc] = mgm->qp[i - 1]; mgm->qp[i - 1] = 0; - err = mthca_WRITE_MGM(dev, index, mgm, &status); + err = mthca_WRITE_MGM(dev, index, mailbox, &status); if (err) goto out; if (status) { @@ -304,7 +305,7 @@ int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) if (be32_to_cpu(mgm->next_gid_index) >> 5) { err = mthca_READ_MGM(dev, be32_to_cpu(mgm->next_gid_index) >> 5, - mgm, &status); + mailbox, &status); if (err) goto out; if (status) { @@ -316,7 +317,7 @@ int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) } else memset(mgm->gid, 0, 16); - err = mthca_WRITE_MGM(dev, index, mgm, &status); + err = mthca_WRITE_MGM(dev, index, mailbox, &status); if (err) goto out; if (status) { @@ -327,7 +328,7 @@ int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) } else { /* Remove entry from AMGM */ index = be32_to_cpu(mgm->next_gid_index) >> 5; - err = mthca_READ_MGM(dev, prev, mgm, &status); + err = mthca_READ_MGM(dev, prev, mailbox, &status); if (err) goto out; if (status) { @@ -338,7 +339,7 @@ int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) mgm->next_gid_index = cpu_to_be32(index << 5); - err = mthca_WRITE_MGM(dev, prev, mgm, &status); + err = mthca_WRITE_MGM(dev, prev, mailbox, &status); if (err) goto out; if (status) { @@ -350,7 +351,7 @@ int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) out: up(&dev->mcg_table.sem); - kfree(mailbox); + mthca_free_mailbox(dev, mailbox); return err; } diff --git a/drivers/infiniband/hw/mthca/mthca_memfree.c b/drivers/infiniband/hw/mthca/mthca_memfree.c index 637b30e..6d3b05d 100644 --- a/drivers/infiniband/hw/mthca/mthca_memfree.c +++ b/drivers/infiniband/hw/mthca/mthca_memfree.c @@ -179,9 +179,14 @@ out: void mthca_table_put(struct mthca_dev *dev, struct mthca_icm_table *table, int obj) { - int i = (obj & (table->num_obj - 1)) * table->obj_size / MTHCA_TABLE_CHUNK_SIZE; + int i; u8 status; + if (!mthca_is_memfree(dev)) + return; + + i = (obj & (table->num_obj - 1)) * table->obj_size / MTHCA_TABLE_CHUNK_SIZE; + down(&table->mutex); if (--table->icm[i]->refcount == 0) { @@ -256,6 +261,9 @@ void mthca_table_put_range(struct mthca_dev *dev, struct mthca_icm_table *table, { int i; + if (!mthca_is_memfree(dev)) + return; + for (i = start; i <= end; i += MTHCA_TABLE_CHUNK_SIZE / table->obj_size) mthca_table_put(dev, table, i); } diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c index 8960fc2..cbe50fe 100644 --- a/drivers/infiniband/hw/mthca/mthca_mr.c +++ b/drivers/infiniband/hw/mthca/mthca_mr.c @@ -40,6 +40,12 @@ #include "mthca_cmd.h" #include "mthca_memfree.h" +struct mthca_mtt { + struct mthca_buddy *buddy; + int order; + u32 first_seg; +}; + /* * Must be packed because mtt_seg is 64 bits but only aligned to 32 bits. */ @@ -173,8 +179,8 @@ static void __devexit mthca_buddy_cleanup(struct mthca_buddy *buddy) kfree(buddy->bits); } -static u32 mthca_alloc_mtt(struct mthca_dev *dev, int order, - struct mthca_buddy *buddy) +static u32 mthca_alloc_mtt_range(struct mthca_dev *dev, int order, + struct mthca_buddy *buddy) { u32 seg = mthca_buddy_alloc(buddy, order); @@ -191,14 +197,102 @@ static u32 mthca_alloc_mtt(struct mthca_dev *dev, int order, return seg; } -static void mthca_free_mtt(struct mthca_dev *dev, u32 seg, int order, - struct mthca_buddy* buddy) +static struct mthca_mtt *__mthca_alloc_mtt(struct mthca_dev *dev, int size, + struct mthca_buddy *buddy) { - mthca_buddy_free(buddy, seg, order); + struct mthca_mtt *mtt; + int i; - if (mthca_is_memfree(dev)) - mthca_table_put_range(dev, dev->mr_table.mtt_table, seg, - seg + (1 << order) - 1); + if (size <= 0) + return ERR_PTR(-EINVAL); + + mtt = kmalloc(sizeof *mtt, GFP_KERNEL); + if (!mtt) + return ERR_PTR(-ENOMEM); + + mtt->buddy = buddy; + mtt->order = 0; + for (i = MTHCA_MTT_SEG_SIZE / 8; i < size; i <<= 1) + ++mtt->order; + + mtt->first_seg = mthca_alloc_mtt_range(dev, mtt->order, buddy); + if (mtt->first_seg == -1) { + kfree(mtt); + return ERR_PTR(-ENOMEM); + } + + return mtt; +} + +struct mthca_mtt *mthca_alloc_mtt(struct mthca_dev *dev, int size) +{ + return __mthca_alloc_mtt(dev, size, &dev->mr_table.mtt_buddy); +} + +void mthca_free_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt) +{ + if (!mtt) + return; + + mthca_buddy_free(mtt->buddy, mtt->first_seg, mtt->order); + + mthca_table_put_range(dev, dev->mr_table.mtt_table, + mtt->first_seg, + mtt->first_seg + (1 << mtt->order) - 1); + + kfree(mtt); +} + +int mthca_write_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt, + int start_index, u64 *buffer_list, int list_len) +{ + struct mthca_mailbox *mailbox; + u64 *mtt_entry; + int err = 0; + u8 status; + int i; + + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + mtt_entry = mailbox->buf; + + while (list_len > 0) { + mtt_entry[0] = cpu_to_be64(dev->mr_table.mtt_base + + mtt->first_seg * MTHCA_MTT_SEG_SIZE + + start_index * 8); + mtt_entry[1] = 0; + for (i = 0; i < list_len && i < MTHCA_MAILBOX_SIZE / 8 - 2; ++i) + mtt_entry[i + 2] = cpu_to_be64(buffer_list[i] | + MTHCA_MTT_FLAG_PRESENT); + + /* + * If we have an odd number of entries to write, add + * one more dummy entry for firmware efficiency. + */ + if (i & 1) + mtt_entry[i + 2] = 0; + + err = mthca_WRITE_MTT(dev, mailbox, (i + 1) & ~1, &status); + if (err) { + mthca_warn(dev, "WRITE_MTT failed (%d)\n", err); + goto out; + } + if (status) { + mthca_warn(dev, "WRITE_MTT returned status 0x%02x\n", + status); + err = -EINVAL; + goto out; + } + + list_len -= i; + start_index += i; + buffer_list += i; + } + +out: + mthca_free_mailbox(dev, mailbox); + return err; } static inline u32 tavor_hw_index_to_key(u32 ind) @@ -237,91 +331,18 @@ static inline u32 key_to_hw_index(struct mthca_dev *dev, u32 key) return tavor_key_to_hw_index(key); } -int mthca_mr_alloc_notrans(struct mthca_dev *dev, u32 pd, - u32 access, struct mthca_mr *mr) +int mthca_mr_alloc(struct mthca_dev *dev, u32 pd, int buffer_size_shift, + u64 iova, u64 total_size, u32 access, struct mthca_mr *mr) { - void *mailbox = NULL; + struct mthca_mailbox *mailbox; struct mthca_mpt_entry *mpt_entry; u32 key; + int i; int err; u8 status; might_sleep(); - mr->order = -1; - key = mthca_alloc(&dev->mr_table.mpt_alloc); - if (key == -1) - return -ENOMEM; - mr->ibmr.rkey = mr->ibmr.lkey = hw_index_to_key(dev, key); - - if (mthca_is_memfree(dev)) { - err = mthca_table_get(dev, dev->mr_table.mpt_table, key); - if (err) - goto err_out_mpt_free; - } - - mailbox = kmalloc(sizeof *mpt_entry + MTHCA_CMD_MAILBOX_EXTRA, - GFP_KERNEL); - if (!mailbox) { - err = -ENOMEM; - goto err_out_table; - } - mpt_entry = MAILBOX_ALIGN(mailbox); - - mpt_entry->flags = cpu_to_be32(MTHCA_MPT_FLAG_SW_OWNS | - MTHCA_MPT_FLAG_MIO | - MTHCA_MPT_FLAG_PHYSICAL | - MTHCA_MPT_FLAG_REGION | - access); - mpt_entry->page_size = 0; - mpt_entry->key = cpu_to_be32(key); - mpt_entry->pd = cpu_to_be32(pd); - mpt_entry->start = 0; - mpt_entry->length = ~0ULL; - - memset(&mpt_entry->lkey, 0, - sizeof *mpt_entry - offsetof(struct mthca_mpt_entry, lkey)); - - err = mthca_SW2HW_MPT(dev, mpt_entry, - key & (dev->limits.num_mpts - 1), - &status); - if (err) { - mthca_warn(dev, "SW2HW_MPT failed (%d)\n", err); - goto err_out_table; - } else if (status) { - mthca_warn(dev, "SW2HW_MPT returned status 0x%02x\n", - status); - err = -EINVAL; - goto err_out_table; - } - - kfree(mailbox); - return err; - -err_out_table: - if (mthca_is_memfree(dev)) - mthca_table_put(dev, dev->mr_table.mpt_table, key); - -err_out_mpt_free: - mthca_free(&dev->mr_table.mpt_alloc, key); - kfree(mailbox); - return err; -} - -int mthca_mr_alloc_phys(struct mthca_dev *dev, u32 pd, - u64 *buffer_list, int buffer_size_shift, - int list_len, u64 iova, u64 total_size, - u32 access, struct mthca_mr *mr) -{ - void *mailbox; - u64 *mtt_entry; - struct mthca_mpt_entry *mpt_entry; - u32 key; - int err = -ENOMEM; - u8 status; - int i; - - might_sleep(); WARN_ON(buffer_size_shift >= 32); key = mthca_alloc(&dev->mr_table.mpt_alloc); @@ -335,75 +356,33 @@ int mthca_mr_alloc_phys(struct mthca_dev *dev, u32 pd, goto err_out_mpt_free; } - for (i = MTHCA_MTT_SEG_SIZE / 8, mr->order = 0; - i < list_len; - i <<= 1, ++mr->order) - ; /* nothing */ - - mr->first_seg = mthca_alloc_mtt(dev, mr->order, - &dev->mr_table.mtt_buddy); - if (mr->first_seg == -1) + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) { + err = PTR_ERR(mailbox); goto err_out_table; - - /* - * If list_len is odd, we add one more dummy entry for - * firmware efficiency. - */ - mailbox = kmalloc(max(sizeof *mpt_entry, - (size_t) 8 * (list_len + (list_len & 1) + 2)) + - MTHCA_CMD_MAILBOX_EXTRA, - GFP_KERNEL); - if (!mailbox) - goto err_out_free_mtt; - - mtt_entry = MAILBOX_ALIGN(mailbox); - - mtt_entry[0] = cpu_to_be64(dev->mr_table.mtt_base + - mr->first_seg * MTHCA_MTT_SEG_SIZE); - mtt_entry[1] = 0; - for (i = 0; i < list_len; ++i) - mtt_entry[i + 2] = cpu_to_be64(buffer_list[i] | - MTHCA_MTT_FLAG_PRESENT); - if (list_len & 1) { - mtt_entry[i + 2] = 0; - ++list_len; - } - - if (0) { - mthca_dbg(dev, "Dumping MPT entry\n"); - for (i = 0; i < list_len + 2; ++i) - printk(KERN_ERR "[%2d] %016llx\n", - i, (unsigned long long) be64_to_cpu(mtt_entry[i])); - } - - err = mthca_WRITE_MTT(dev, mtt_entry, list_len, &status); - if (err) { - mthca_warn(dev, "WRITE_MTT failed (%d)\n", err); - goto err_out_mailbox_free; - } - if (status) { - mthca_warn(dev, "WRITE_MTT returned status 0x%02x\n", - status); - err = -EINVAL; - goto err_out_mailbox_free; } - - mpt_entry = MAILBOX_ALIGN(mailbox); + mpt_entry = mailbox->buf; mpt_entry->flags = cpu_to_be32(MTHCA_MPT_FLAG_SW_OWNS | MTHCA_MPT_FLAG_MIO | MTHCA_MPT_FLAG_REGION | access); + if (!mr->mtt) + mpt_entry->flags |= cpu_to_be32(MTHCA_MPT_FLAG_PHYSICAL); mpt_entry->page_size = cpu_to_be32(buffer_size_shift - 12); mpt_entry->key = cpu_to_be32(key); mpt_entry->pd = cpu_to_be32(pd); mpt_entry->start = cpu_to_be64(iova); mpt_entry->length = cpu_to_be64(total_size); + memset(&mpt_entry->lkey, 0, sizeof *mpt_entry - offsetof(struct mthca_mpt_entry, lkey)); - mpt_entry->mtt_seg = cpu_to_be64(dev->mr_table.mtt_base + - mr->first_seg * MTHCA_MTT_SEG_SIZE); + + if (mr->mtt) + mpt_entry->mtt_seg = + cpu_to_be64(dev->mr_table.mtt_base + + mr->mtt->first_seg * MTHCA_MTT_SEG_SIZE); if (0) { mthca_dbg(dev, "Dumping MPT entry %08x:\n", mr->ibmr.lkey); @@ -416,45 +395,70 @@ int mthca_mr_alloc_phys(struct mthca_dev *dev, u32 pd, } } - err = mthca_SW2HW_MPT(dev, mpt_entry, + err = mthca_SW2HW_MPT(dev, mailbox, key & (dev->limits.num_mpts - 1), &status); - if (err) + if (err) { mthca_warn(dev, "SW2HW_MPT failed (%d)\n", err); - else if (status) { + goto err_out_mailbox; + } else if (status) { mthca_warn(dev, "SW2HW_MPT returned status 0x%02x\n", status); err = -EINVAL; + goto err_out_mailbox; } - kfree(mailbox); + mthca_free_mailbox(dev, mailbox); return err; -err_out_mailbox_free: - kfree(mailbox); - -err_out_free_mtt: - mthca_free_mtt(dev, mr->first_seg, mr->order, &dev->mr_table.mtt_buddy); +err_out_mailbox: + mthca_free_mailbox(dev, mailbox); err_out_table: - if (mthca_is_memfree(dev)) - mthca_table_put(dev, dev->mr_table.mpt_table, key); + mthca_table_put(dev, dev->mr_table.mpt_table, key); err_out_mpt_free: mthca_free(&dev->mr_table.mpt_alloc, key); return err; } -/* Free mr or fmr */ -static void mthca_free_region(struct mthca_dev *dev, u32 lkey, int order, - u32 first_seg, struct mthca_buddy *buddy) +int mthca_mr_alloc_notrans(struct mthca_dev *dev, u32 pd, + u32 access, struct mthca_mr *mr) { - if (order >= 0) - mthca_free_mtt(dev, first_seg, order, buddy); + mr->mtt = NULL; + return mthca_mr_alloc(dev, pd, 12, 0, ~0ULL, access, mr); +} - if (mthca_is_memfree(dev)) - mthca_table_put(dev, dev->mr_table.mpt_table, - arbel_key_to_hw_index(lkey)); +int mthca_mr_alloc_phys(struct mthca_dev *dev, u32 pd, + u64 *buffer_list, int buffer_size_shift, + int list_len, u64 iova, u64 total_size, + u32 access, struct mthca_mr *mr) +{ + int err; + + mr->mtt = mthca_alloc_mtt(dev, list_len); + if (IS_ERR(mr->mtt)) + return PTR_ERR(mr->mtt); + + err = mthca_write_mtt(dev, mr->mtt, 0, buffer_list, list_len); + if (err) { + mthca_free_mtt(dev, mr->mtt); + return err; + } + + err = mthca_mr_alloc(dev, pd, buffer_size_shift, iova, + total_size, access, mr); + if (err) + mthca_free_mtt(dev, mr->mtt); + + return err; +} + +/* Free mr or fmr */ +static void mthca_free_region(struct mthca_dev *dev, u32 lkey) +{ + mthca_table_put(dev, dev->mr_table.mpt_table, + arbel_key_to_hw_index(lkey)); mthca_free(&dev->mr_table.mpt_alloc, key_to_hw_index(dev, lkey)); } @@ -476,15 +480,15 @@ void mthca_free_mr(struct mthca_dev *dev, struct mthca_mr *mr) mthca_warn(dev, "HW2SW_MPT returned status 0x%02x\n", status); - mthca_free_region(dev, mr->ibmr.lkey, mr->order, mr->first_seg, - &dev->mr_table.mtt_buddy); + mthca_free_region(dev, mr->ibmr.lkey); + mthca_free_mtt(dev, mr->mtt); } int mthca_fmr_alloc(struct mthca_dev *dev, u32 pd, u32 access, struct mthca_fmr *mr) { struct mthca_mpt_entry *mpt_entry; - void *mailbox; + struct mthca_mailbox *mailbox; u64 mtt_seg; u32 key, idx; u8 status; @@ -522,31 +526,24 @@ int mthca_fmr_alloc(struct mthca_dev *dev, u32 pd, mr->mem.tavor.mpt = dev->mr_table.tavor_fmr.mpt_base + sizeof *(mr->mem.tavor.mpt) * idx; - for (i = MTHCA_MTT_SEG_SIZE / 8, mr->order = 0; - i < list_len; - i <<= 1, ++mr->order) - ; /* nothing */ - - mr->first_seg = mthca_alloc_mtt(dev, mr->order, - dev->mr_table.fmr_mtt_buddy); - if (mr->first_seg == -1) + mr->mtt = __mthca_alloc_mtt(dev, list_len, dev->mr_table.fmr_mtt_buddy); + if (IS_ERR(mr->mtt)) goto err_out_table; - mtt_seg = mr->first_seg * MTHCA_MTT_SEG_SIZE; + mtt_seg = mr->mtt->first_seg * MTHCA_MTT_SEG_SIZE; if (mthca_is_memfree(dev)) { mr->mem.arbel.mtts = mthca_table_find(dev->mr_table.mtt_table, - mr->first_seg); + mr->mtt->first_seg); BUG_ON(!mr->mem.arbel.mtts); } else mr->mem.tavor.mtts = dev->mr_table.tavor_fmr.mtt_base + mtt_seg; - mailbox = kmalloc(sizeof *mpt_entry + MTHCA_CMD_MAILBOX_EXTRA, - GFP_KERNEL); - if (!mailbox) + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) goto err_out_free_mtt; - mpt_entry = MAILBOX_ALIGN(mailbox); + mpt_entry = mailbox->buf; mpt_entry->flags = cpu_to_be32(MTHCA_MPT_FLAG_SW_OWNS | MTHCA_MPT_FLAG_MIO | @@ -571,7 +568,7 @@ int mthca_fmr_alloc(struct mthca_dev *dev, u32 pd, } } - err = mthca_SW2HW_MPT(dev, mpt_entry, + err = mthca_SW2HW_MPT(dev, mailbox, key & (dev->limits.num_mpts - 1), &status); if (err) { @@ -585,19 +582,17 @@ int mthca_fmr_alloc(struct mthca_dev *dev, u32 pd, goto err_out_mailbox_free; } - kfree(mailbox); + mthca_free_mailbox(dev, mailbox); return 0; err_out_mailbox_free: - kfree(mailbox); + mthca_free_mailbox(dev, mailbox); err_out_free_mtt: - mthca_free_mtt(dev, mr->first_seg, mr->order, - dev->mr_table.fmr_mtt_buddy); + mthca_free_mtt(dev, mr->mtt); err_out_table: - if (mthca_is_memfree(dev)) - mthca_table_put(dev, dev->mr_table.mpt_table, key); + mthca_table_put(dev, dev->mr_table.mpt_table, key); err_out_mpt_free: mthca_free(&dev->mr_table.mpt_alloc, mr->ibmr.lkey); @@ -609,8 +604,9 @@ int mthca_free_fmr(struct mthca_dev *dev, struct mthca_fmr *fmr) if (fmr->maps) return -EBUSY; - mthca_free_region(dev, fmr->ibmr.lkey, fmr->order, fmr->first_seg, - dev->mr_table.fmr_mtt_buddy); + mthca_free_region(dev, fmr->ibmr.lkey); + mthca_free_mtt(dev, fmr->mtt); + return 0; } @@ -826,7 +822,8 @@ int __devinit mthca_init_mr_table(struct mthca_dev *dev) if (dev->limits.reserved_mtts) { i = fls(dev->limits.reserved_mtts - 1); - if (mthca_alloc_mtt(dev, i, dev->mr_table.fmr_mtt_buddy) == -1) { + if (mthca_alloc_mtt_range(dev, i, + dev->mr_table.fmr_mtt_buddy) == -1) { mthca_warn(dev, "MTT table of order %d is too small.\n", dev->mr_table.fmr_mtt_buddy->max_order); err = -ENOMEM; diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 159f4e6..0b5adfd 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -52,7 +53,7 @@ static int mthca_query_device(struct ib_device *ibdev, if (!in_mad || !out_mad) goto out; - memset(props, 0, sizeof props); + memset(props, 0, sizeof *props); props->fw_ver = mdev->fw_ver; @@ -558,6 +559,7 @@ static struct ib_mr *mthca_reg_phys_mr(struct ib_pd *pd, convert_access(acc), mr); if (err) { + kfree(page_list); kfree(mr); return ERR_PTR(err); } diff --git a/drivers/infiniband/hw/mthca/mthca_provider.h b/drivers/infiniband/hw/mthca/mthca_provider.h index 619710f..4d976cc 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.h +++ b/drivers/infiniband/hw/mthca/mthca_provider.h @@ -54,18 +54,18 @@ struct mthca_uar { int index; }; +struct mthca_mtt; + struct mthca_mr { - struct ib_mr ibmr; - int order; - u32 first_seg; + struct ib_mr ibmr; + struct mthca_mtt *mtt; }; struct mthca_fmr { - struct ib_fmr ibmr; + struct ib_fmr ibmr; struct ib_fmr_attr attr; - int order; - u32 first_seg; - int maps; + struct mthca_mtt *mtt; + int maps; union { struct { struct mthca_mpt_entry __iomem *mpt; diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c index ca73bab..163a8ef 100644 --- a/drivers/infiniband/hw/mthca/mthca_qp.c +++ b/drivers/infiniband/hw/mthca/mthca_qp.c @@ -357,6 +357,9 @@ static const struct { [UD] = (IB_QP_PKEY_INDEX | IB_QP_PORT | IB_QP_QKEY), + [UC] = (IB_QP_PKEY_INDEX | + IB_QP_PORT | + IB_QP_ACCESS_FLAGS), [RC] = (IB_QP_PKEY_INDEX | IB_QP_PORT | IB_QP_ACCESS_FLAGS), @@ -378,6 +381,9 @@ static const struct { [UD] = (IB_QP_PKEY_INDEX | IB_QP_PORT | IB_QP_QKEY), + [UC] = (IB_QP_PKEY_INDEX | + IB_QP_PORT | + IB_QP_ACCESS_FLAGS), [RC] = (IB_QP_PKEY_INDEX | IB_QP_PORT | IB_QP_ACCESS_FLAGS), @@ -388,6 +394,11 @@ static const struct { [IB_QPS_RTR] = { .trans = MTHCA_TRANS_INIT2RTR, .req_param = { + [UC] = (IB_QP_AV | + IB_QP_PATH_MTU | + IB_QP_DEST_QPN | + IB_QP_RQ_PSN | + IB_QP_MAX_DEST_RD_ATOMIC), [RC] = (IB_QP_AV | IB_QP_PATH_MTU | IB_QP_DEST_QPN | @@ -398,6 +409,9 @@ static const struct { .opt_param = { [UD] = (IB_QP_PKEY_INDEX | IB_QP_QKEY), + [UC] = (IB_QP_ALT_PATH | + IB_QP_ACCESS_FLAGS | + IB_QP_PKEY_INDEX), [RC] = (IB_QP_ALT_PATH | IB_QP_ACCESS_FLAGS | IB_QP_PKEY_INDEX), @@ -413,6 +427,8 @@ static const struct { .trans = MTHCA_TRANS_RTR2RTS, .req_param = { [UD] = IB_QP_SQ_PSN, + [UC] = (IB_QP_SQ_PSN | + IB_QP_MAX_QP_RD_ATOMIC), [RC] = (IB_QP_TIMEOUT | IB_QP_RETRY_CNT | IB_QP_RNR_RETRY | @@ -423,6 +439,11 @@ static const struct { .opt_param = { [UD] = (IB_QP_CUR_STATE | IB_QP_QKEY), + [UC] = (IB_QP_CUR_STATE | + IB_QP_ALT_PATH | + IB_QP_ACCESS_FLAGS | + IB_QP_PKEY_INDEX | + IB_QP_PATH_MIG_STATE), [RC] = (IB_QP_CUR_STATE | IB_QP_ALT_PATH | IB_QP_ACCESS_FLAGS | @@ -442,6 +463,9 @@ static const struct { .opt_param = { [UD] = (IB_QP_CUR_STATE | IB_QP_QKEY), + [UC] = (IB_QP_ACCESS_FLAGS | + IB_QP_ALT_PATH | + IB_QP_PATH_MIG_STATE), [RC] = (IB_QP_ACCESS_FLAGS | IB_QP_ALT_PATH | IB_QP_PATH_MIG_STATE | @@ -462,6 +486,10 @@ static const struct { .opt_param = { [UD] = (IB_QP_CUR_STATE | IB_QP_QKEY), + [UC] = (IB_QP_CUR_STATE | + IB_QP_ALT_PATH | + IB_QP_ACCESS_FLAGS | + IB_QP_PATH_MIG_STATE), [RC] = (IB_QP_CUR_STATE | IB_QP_ALT_PATH | IB_QP_ACCESS_FLAGS | @@ -476,6 +504,14 @@ static const struct { .opt_param = { [UD] = (IB_QP_PKEY_INDEX | IB_QP_QKEY), + [UC] = (IB_QP_AV | + IB_QP_MAX_QP_RD_ATOMIC | + IB_QP_MAX_DEST_RD_ATOMIC | + IB_QP_CUR_STATE | + IB_QP_ALT_PATH | + IB_QP_ACCESS_FLAGS | + IB_QP_PKEY_INDEX | + IB_QP_PATH_MIG_STATE), [RC] = (IB_QP_AV | IB_QP_TIMEOUT | IB_QP_RETRY_CNT | @@ -501,6 +537,7 @@ static const struct { .opt_param = { [UD] = (IB_QP_CUR_STATE | IB_QP_QKEY), + [UC] = (IB_QP_CUR_STATE), [RC] = (IB_QP_CUR_STATE | IB_QP_MIN_RNR_TIMER), [MLX] = (IB_QP_CUR_STATE | @@ -552,7 +589,7 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask) struct mthca_dev *dev = to_mdev(ibqp->device); struct mthca_qp *qp = to_mqp(ibqp); enum ib_qp_state cur_state, new_state; - void *mailbox = NULL; + struct mthca_mailbox *mailbox; struct mthca_qp_param *qp_param; struct mthca_qp_context *qp_context; u32 req_param, opt_param; @@ -609,10 +646,10 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask) return -EINVAL; } - mailbox = kmalloc(sizeof (*qp_param) + MTHCA_CMD_MAILBOX_EXTRA, GFP_KERNEL); - if (!mailbox) - return -ENOMEM; - qp_param = MAILBOX_ALIGN(mailbox); + mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + qp_param = mailbox->buf; qp_context = &qp_param->context; memset(qp_param, 0, sizeof *qp_param); @@ -683,7 +720,7 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask) if (attr_mask & IB_QP_AV) { qp_context->pri_path.g_mylmc = attr->ah_attr.src_path_bits & 0x7f; qp_context->pri_path.rlid = cpu_to_be16(attr->ah_attr.dlid); - qp_context->pri_path.static_rate = (!!attr->ah_attr.static_rate) << 3; + qp_context->pri_path.static_rate = !!attr->ah_attr.static_rate; if (attr->ah_attr.ah_flags & IB_AH_GRH) { qp_context->pri_path.g_mylmc |= 1 << 7; qp_context->pri_path.mgid_index = attr->ah_attr.grh.sgid_index; @@ -724,9 +761,9 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask) qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_RETRY_COUNT); } - if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) { - qp_context->params1 |= cpu_to_be32(min(attr->max_dest_rd_atomic ? - ffs(attr->max_dest_rd_atomic) - 1 : 0, + if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC) { + qp_context->params1 |= cpu_to_be32(min(attr->max_rd_atomic ? + ffs(attr->max_rd_atomic) - 1 : 0, 7) << 21); qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_SRA_MAX); } @@ -764,10 +801,10 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask) qp->atomic_rd_en = attr->qp_access_flags; } - if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC) { + if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) { u8 rra_max; - if (qp->resp_depth && !attr->max_rd_atomic) { + if (qp->resp_depth && !attr->max_dest_rd_atomic) { /* * Lowering our responder resources to zero. * Turn off RDMA/atomics as responder. @@ -778,7 +815,7 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask) MTHCA_QP_OPTPAR_RAE); } - if (!qp->resp_depth && attr->max_rd_atomic) { + if (!qp->resp_depth && attr->max_dest_rd_atomic) { /* * Increasing our responder resources from * zero. Turn on RDMA/atomics as appropriate. @@ -799,7 +836,7 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask) } for (rra_max = 0; - 1 << rra_max < attr->max_rd_atomic && + 1 << rra_max < attr->max_dest_rd_atomic && rra_max < dev->qp_table.rdb_shift; ++rra_max) ; /* nothing */ @@ -807,7 +844,7 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask) qp_context->params2 |= cpu_to_be32(rra_max << 21); qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_RRA_MAX); - qp->resp_depth = attr->max_rd_atomic; + qp->resp_depth = attr->max_dest_rd_atomic; } qp_context->params2 |= cpu_to_be32(MTHCA_QP_BIT_RSC); @@ -835,7 +872,7 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask) } err = mthca_MODIFY_QP(dev, state_table[cur_state][new_state].trans, - qp->qpn, 0, qp_param, 0, &status); + qp->qpn, 0, mailbox, 0, &status); if (status) { mthca_warn(dev, "modify QP %d returned status %02x.\n", state_table[cur_state][new_state].trans, status); @@ -845,7 +882,7 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask) if (!err) qp->state = new_state; - kfree(mailbox); + mthca_free_mailbox(dev, mailbox); if (is_sqp(dev, qp)) store_attrs(to_msqp(qp), attr, attr_mask); @@ -934,7 +971,8 @@ static int mthca_alloc_wqe_buf(struct mthca_dev *dev, mthca_dbg(dev, "Creating direct QP of size %d (shift %d)\n", size, shift); - qp->queue.direct.buf = pci_alloc_consistent(dev->pdev, size, &t); + qp->queue.direct.buf = dma_alloc_coherent(&dev->pdev->dev, size, + &t, GFP_KERNEL); if (!qp->queue.direct.buf) goto err_out; @@ -973,7 +1011,8 @@ static int mthca_alloc_wqe_buf(struct mthca_dev *dev, for (i = 0; i < npages; ++i) { qp->queue.page_list[i].buf = - pci_alloc_consistent(dev->pdev, PAGE_SIZE, &t); + dma_alloc_coherent(&dev->pdev->dev, PAGE_SIZE, + &t, GFP_KERNEL); if (!qp->queue.page_list[i].buf) goto err_out_free; @@ -996,16 +1035,15 @@ static int mthca_alloc_wqe_buf(struct mthca_dev *dev, err_out_free: if (qp->is_direct) { - pci_free_consistent(dev->pdev, size, - qp->queue.direct.buf, - pci_unmap_addr(&qp->queue.direct, mapping)); + dma_free_coherent(&dev->pdev->dev, size, qp->queue.direct.buf, + pci_unmap_addr(&qp->queue.direct, mapping)); } else for (i = 0; i < npages; ++i) { if (qp->queue.page_list[i].buf) - pci_free_consistent(dev->pdev, PAGE_SIZE, - qp->queue.page_list[i].buf, - pci_unmap_addr(&qp->queue.page_list[i], - mapping)); + dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, + qp->queue.page_list[i].buf, + pci_unmap_addr(&qp->queue.page_list[i], + mapping)); } @@ -1073,11 +1111,12 @@ static void mthca_free_memfree(struct mthca_dev *dev, if (mthca_is_memfree(dev)) { mthca_free_db(dev, MTHCA_DB_TYPE_SQ, qp->sq.db_index); mthca_free_db(dev, MTHCA_DB_TYPE_RQ, qp->rq.db_index); - mthca_table_put(dev, dev->qp_table.rdb_table, - qp->qpn << dev->qp_table.rdb_shift); - mthca_table_put(dev, dev->qp_table.eqp_table, qp->qpn); - mthca_table_put(dev, dev->qp_table.qp_table, qp->qpn); } + + mthca_table_put(dev, dev->qp_table.rdb_table, + qp->qpn << dev->qp_table.rdb_shift); + mthca_table_put(dev, dev->qp_table.eqp_table, qp->qpn); + mthca_table_put(dev, dev->qp_table.qp_table, qp->qpn); } static void mthca_wq_init(struct mthca_wq* wq) @@ -1529,6 +1568,26 @@ int mthca_tavor_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, break; + case UC: + switch (wr->opcode) { + case IB_WR_RDMA_WRITE: + case IB_WR_RDMA_WRITE_WITH_IMM: + ((struct mthca_raddr_seg *) wqe)->raddr = + cpu_to_be64(wr->wr.rdma.remote_addr); + ((struct mthca_raddr_seg *) wqe)->rkey = + cpu_to_be32(wr->wr.rdma.rkey); + ((struct mthca_raddr_seg *) wqe)->reserved = 0; + wqe += sizeof (struct mthca_raddr_seg); + size += sizeof (struct mthca_raddr_seg) / 16; + break; + + default: + /* No extra segments required for sends */ + break; + } + + break; + case UD: ((struct mthca_tavor_ud_seg *) wqe)->lkey = cpu_to_be32(to_mah(wr->wr.ud.ah)->key); @@ -1814,9 +1873,29 @@ int mthca_arbel_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, sizeof (struct mthca_atomic_seg); break; + case IB_WR_RDMA_READ: + case IB_WR_RDMA_WRITE: + case IB_WR_RDMA_WRITE_WITH_IMM: + ((struct mthca_raddr_seg *) wqe)->raddr = + cpu_to_be64(wr->wr.rdma.remote_addr); + ((struct mthca_raddr_seg *) wqe)->rkey = + cpu_to_be32(wr->wr.rdma.rkey); + ((struct mthca_raddr_seg *) wqe)->reserved = 0; + wqe += sizeof (struct mthca_raddr_seg); + size += sizeof (struct mthca_raddr_seg) / 16; + break; + + default: + /* No extra segments required for sends */ + break; + } + + break; + + case UC: + switch (wr->opcode) { case IB_WR_RDMA_WRITE: case IB_WR_RDMA_WRITE_WITH_IMM: - case IB_WR_RDMA_READ: ((struct mthca_raddr_seg *) wqe)->raddr = cpu_to_be64(wr->wr.rdma.remote_addr); ((struct mthca_raddr_seg *) wqe)->rkey = diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 556264b..374f404 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -21,6 +21,7 @@ #include <linux/smp_lock.h> #include <linux/device.h> #include <linux/devfs_fs_kernel.h> +#include <linux/compat.h> struct evdev { int exist; @@ -145,6 +146,41 @@ static int evdev_open(struct inode * inode, struct file * file) return 0; } +#ifdef CONFIG_COMPAT +struct input_event_compat { + struct compat_timeval time; + __u16 type; + __u16 code; + __s32 value; +}; + +#ifdef CONFIG_X86_64 +# define COMPAT_TEST test_thread_flag(TIF_IA32) +#elif defined(CONFIG_IA64) +# define COMPAT_TEST IS_IA32_PROCESS(ia64_task_regs(current)) +#elif defined(CONFIG_ARCH_S390) +# define COMPAT_TEST test_thread_flag(TIF_31BIT) +#else +# define COMPAT_TEST test_thread_flag(TIF_32BIT) +#endif + +static ssize_t evdev_write_compat(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) +{ + struct evdev_list *list = file->private_data; + struct input_event_compat event; + int retval = 0; + + while (retval < count) { + if (copy_from_user(&event, buffer + retval, sizeof(struct input_event_compat))) + return -EFAULT; + input_event(list->evdev->handle.dev, event.type, event.code, event.value); + retval += sizeof(struct input_event_compat); + } + + return retval; +} +#endif + static ssize_t evdev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) { struct evdev_list *list = file->private_data; @@ -153,6 +189,11 @@ static ssize_t evdev_write(struct file * file, const char __user * buffer, size_ if (!list->evdev->exist) return -ENODEV; +#ifdef CONFIG_COMPAT + if (COMPAT_TEST) + return evdev_write_compat(file, buffer, count, ppos); +#endif + while (retval < count) { if (copy_from_user(&event, buffer + retval, sizeof(struct input_event))) @@ -164,11 +205,56 @@ static ssize_t evdev_write(struct file * file, const char __user * buffer, size_ return retval; } +#ifdef CONFIG_COMPAT +static ssize_t evdev_read_compat(struct file * file, char __user * buffer, size_t count, loff_t *ppos) +{ + struct evdev_list *list = file->private_data; + int retval; + + if (count < sizeof(struct input_event_compat)) + return -EINVAL; + + if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(list->evdev->wait, + list->head != list->tail || (!list->evdev->exist)); + + if (retval) + return retval; + + if (!list->evdev->exist) + return -ENODEV; + + while (list->head != list->tail && retval + sizeof(struct input_event_compat) <= count) { + struct input_event *event = (struct input_event *) list->buffer + list->tail; + struct input_event_compat event_compat; + event_compat.time.tv_sec = event->time.tv_sec; + event_compat.time.tv_usec = event->time.tv_usec; + event_compat.type = event->type; + event_compat.code = event->code; + event_compat.value = event->value; + + if (copy_to_user(buffer + retval, &event_compat, + sizeof(struct input_event_compat))) return -EFAULT; + list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1); + retval += sizeof(struct input_event_compat); + } + + return retval; +} +#endif + static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos) { struct evdev_list *list = file->private_data; int retval; +#ifdef CONFIG_COMPAT + if (COMPAT_TEST) + return evdev_read_compat(file, buffer, count, ppos); +#endif + if (count < sizeof(struct input_event)) return -EINVAL; @@ -186,7 +272,7 @@ static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count while (list->head != list->tail && retval + sizeof(struct input_event) <= count) { if (copy_to_user(buffer + retval, list->buffer + list->tail, - sizeof(struct input_event))) return -EFAULT; + sizeof(struct input_event))) return -EFAULT; list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1); retval += sizeof(struct input_event); } @@ -203,7 +289,7 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait) (list->evdev->exist ? 0 : (POLLHUP | POLLERR)); } -static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct evdev_list *list = file->private_data; struct evdev *evdev = list->evdev; @@ -285,109 +371,267 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, default: - if (_IOC_TYPE(cmd) != 'E' || _IOC_DIR(cmd) != _IOC_READ) + if (_IOC_TYPE(cmd) != 'E') return -EINVAL; - if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) { - - long *bits; - int len; - - switch (_IOC_NR(cmd) & EV_MAX) { - case 0: bits = dev->evbit; len = EV_MAX; break; - case EV_KEY: bits = dev->keybit; len = KEY_MAX; break; - case EV_REL: bits = dev->relbit; len = REL_MAX; break; - case EV_ABS: bits = dev->absbit; len = ABS_MAX; break; - case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break; - case EV_LED: bits = dev->ledbit; len = LED_MAX; break; - case EV_SND: bits = dev->sndbit; len = SND_MAX; break; - case EV_FF: bits = dev->ffbit; len = FF_MAX; break; - default: return -EINVAL; + if (_IOC_DIR(cmd) == _IOC_READ) { + + if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) { + + long *bits; + int len; + + switch (_IOC_NR(cmd) & EV_MAX) { + case 0: bits = dev->evbit; len = EV_MAX; break; + case EV_KEY: bits = dev->keybit; len = KEY_MAX; break; + case EV_REL: bits = dev->relbit; len = REL_MAX; break; + case EV_ABS: bits = dev->absbit; len = ABS_MAX; break; + case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break; + case EV_LED: bits = dev->ledbit; len = LED_MAX; break; + case EV_SND: bits = dev->sndbit; len = SND_MAX; break; + case EV_FF: bits = dev->ffbit; len = FF_MAX; break; + default: return -EINVAL; + } + len = NBITS(len) * sizeof(long); + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, bits, len) ? -EFAULT : len; } - len = NBITS(len) * sizeof(long); - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(p, bits, len) ? -EFAULT : len; - } - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) { - int len; - len = NBITS(KEY_MAX) * sizeof(long); - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(p, dev->key, len) ? -EFAULT : len; - } + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) { + int len; + len = NBITS(KEY_MAX) * sizeof(long); + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->key, len) ? -EFAULT : len; + } - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) { - int len; - len = NBITS(LED_MAX) * sizeof(long); - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(p, dev->led, len) ? -EFAULT : len; - } + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) { + int len; + len = NBITS(LED_MAX) * sizeof(long); + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->led, len) ? -EFAULT : len; + } - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0))) { - int len; - len = NBITS(SND_MAX) * sizeof(long); - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(p, dev->snd, len) ? -EFAULT : len; - } + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0))) { + int len; + len = NBITS(SND_MAX) * sizeof(long); + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->snd, len) ? -EFAULT : len; + } - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) { - int len; - if (!dev->name) return -ENOENT; - len = strlen(dev->name) + 1; - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(p, dev->name, len) ? -EFAULT : len; - } + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) { + int len; + if (!dev->name) return -ENOENT; + len = strlen(dev->name) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->name, len) ? -EFAULT : len; + } + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) { + int len; + if (!dev->phys) return -ENOENT; + len = strlen(dev->phys) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->phys, len) ? -EFAULT : len; + } + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) { + int len; + if (!dev->uniq) return -ENOENT; + len = strlen(dev->uniq) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->uniq, len) ? -EFAULT : len; + } + + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { + + int t = _IOC_NR(cmd) & ABS_MAX; + + abs.value = dev->abs[t]; + abs.minimum = dev->absmin[t]; + abs.maximum = dev->absmax[t]; + abs.fuzz = dev->absfuzz[t]; + abs.flat = dev->absflat[t]; + + if (copy_to_user(p, &abs, sizeof(struct input_absinfo))) + return -EFAULT; + + return 0; + } - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) { - int len; - if (!dev->phys) return -ENOENT; - len = strlen(dev->phys) + 1; - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(p, dev->phys, len) ? -EFAULT : len; } - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) { - int len; - if (!dev->uniq) return -ENOENT; - len = strlen(dev->uniq) + 1; - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(p, dev->uniq, len) ? -EFAULT : len; + if (_IOC_DIR(cmd) == _IOC_WRITE) { + + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { + + int t = _IOC_NR(cmd) & ABS_MAX; + + if (copy_from_user(&abs, p, sizeof(struct input_absinfo))) + return -EFAULT; + + dev->abs[t] = abs.value; + dev->absmin[t] = abs.minimum; + dev->absmax[t] = abs.maximum; + dev->absfuzz[t] = abs.fuzz; + dev->absflat[t] = abs.flat; + + return 0; + } } + } + return -EINVAL; +} - if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { +#ifdef CONFIG_COMPAT + +#define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8) +#define NBITS_COMPAT(x) ((((x)-1)/BITS_PER_LONG_COMPAT)+1) +#define OFF_COMPAT(x) ((x)%BITS_PER_LONG_COMPAT) +#define BIT_COMPAT(x) (1UL<<OFF_COMPAT(x)) +#define LONG_COMPAT(x) ((x)/BITS_PER_LONG_COMPAT) +#define test_bit_compat(bit, array) ((array[LONG_COMPAT(bit)] >> OFF_COMPAT(bit)) & 1) + +#ifdef __BIG_ENDIAN +#define bit_to_user(bit, max) \ +do { \ + int i; \ + int len = NBITS_COMPAT((max)) * sizeof(compat_long_t); \ + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); \ + for (i = 0; i < len / sizeof(compat_long_t); i++) \ + if (copy_to_user((compat_long_t*) p + i, \ + (compat_long_t*) (bit) + i + 1 - ((i % 2) << 1), \ + sizeof(compat_long_t))) \ + return -EFAULT; \ + return len; \ +} while (0) +#else +#define bit_to_user(bit, max) \ +do { \ + int len = NBITS_COMPAT((max)) * sizeof(compat_long_t); \ + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); \ + return copy_to_user(p, (bit), len) ? -EFAULT : len; \ +} while (0) +#endif + +static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct evdev_list *list = file->private_data; + struct evdev *evdev = list->evdev; + struct input_dev *dev = evdev->handle.dev; + struct input_absinfo abs; + void __user *p = compat_ptr(arg); - int t = _IOC_NR(cmd) & ABS_MAX; + if (!evdev->exist) return -ENODEV; - abs.value = dev->abs[t]; - abs.minimum = dev->absmin[t]; - abs.maximum = dev->absmax[t]; - abs.fuzz = dev->absfuzz[t]; - abs.flat = dev->absflat[t]; + switch (cmd) { - if (copy_to_user(p, &abs, sizeof(struct input_absinfo))) - return -EFAULT; + case EVIOCGVERSION: + case EVIOCGID: + case EVIOCGKEYCODE: + case EVIOCSKEYCODE: + case EVIOCSFF: + case EVIOCRMFF: + case EVIOCGEFFECTS: + case EVIOCGRAB: + return evdev_ioctl(file, cmd, (unsigned long) p); - return 0; + default: + + if (_IOC_TYPE(cmd) != 'E') + return -EINVAL; + + if (_IOC_DIR(cmd) == _IOC_READ) { + + if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) { + long *bits; + int max; + + switch (_IOC_NR(cmd) & EV_MAX) { + case 0: bits = dev->evbit; max = EV_MAX; break; + case EV_KEY: bits = dev->keybit; max = KEY_MAX; break; + case EV_REL: bits = dev->relbit; max = REL_MAX; break; + case EV_ABS: bits = dev->absbit; max = ABS_MAX; break; + case EV_MSC: bits = dev->mscbit; max = MSC_MAX; break; + case EV_LED: bits = dev->ledbit; max = LED_MAX; break; + case EV_SND: bits = dev->sndbit; max = SND_MAX; break; + case EV_FF: bits = dev->ffbit; max = FF_MAX; break; + default: return -EINVAL; + } + bit_to_user(bits, max); + } + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) + bit_to_user(dev->key, KEY_MAX); + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) + bit_to_user(dev->led, LED_MAX); + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0))) + bit_to_user(dev->snd, SND_MAX); + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) { + int len; + if (!dev->name) return -ENOENT; + len = strlen(dev->name) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->name, len) ? -EFAULT : len; + } + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) { + int len; + if (!dev->phys) return -ENOENT; + len = strlen(dev->phys) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->phys, len) ? -EFAULT : len; + } + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) { + int len; + if (!dev->uniq) return -ENOENT; + len = strlen(dev->uniq) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->uniq, len) ? -EFAULT : len; + } + + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { + + int t = _IOC_NR(cmd) & ABS_MAX; + + abs.value = dev->abs[t]; + abs.minimum = dev->absmin[t]; + abs.maximum = dev->absmax[t]; + abs.fuzz = dev->absfuzz[t]; + abs.flat = dev->absflat[t]; + + if (copy_to_user(p, &abs, sizeof(struct input_absinfo))) + return -EFAULT; + + return 0; + } } - if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { + if (_IOC_DIR(cmd) == _IOC_WRITE) { - int t = _IOC_NR(cmd) & ABS_MAX; + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { - if (copy_from_user(&abs, p, sizeof(struct input_absinfo))) - return -EFAULT; + int t = _IOC_NR(cmd) & ABS_MAX; - dev->abs[t] = abs.value; - dev->absmin[t] = abs.minimum; - dev->absmax[t] = abs.maximum; - dev->absfuzz[t] = abs.fuzz; - dev->absflat[t] = abs.flat; + if (copy_from_user(&abs, p, sizeof(struct input_absinfo))) + return -EFAULT; - return 0; + dev->abs[t] = abs.value; + dev->absmin[t] = abs.minimum; + dev->absmax[t] = abs.maximum; + dev->absfuzz[t] = abs.fuzz; + dev->absflat[t] = abs.flat; + + return 0; + } } } return -EINVAL; } +#endif static struct file_operations evdev_fops = { .owner = THIS_MODULE, @@ -396,7 +640,10 @@ static struct file_operations evdev_fops = { .poll = evdev_poll, .open = evdev_open, .release = evdev_release, - .ioctl = evdev_ioctl, + .unlocked_ioctl = evdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = evdev_ioctl_compat, +#endif .fasync = evdev_fasync, .flush = evdev_flush }; diff --git a/drivers/input/gameport/Kconfig b/drivers/input/gameport/Kconfig index 1d93f50..7524bd7 100644 --- a/drivers/input/gameport/Kconfig +++ b/drivers/input/gameport/Kconfig @@ -49,22 +49,8 @@ config GAMEPORT_EMU10K1 To compile this driver as a module, choose M here: the module will be called emu10k1-gp. -config GAMEPORT_VORTEX - tristate "Aureal Vortex, Vortex 2 gameport support" - depends on PCI - help - Say Y here if you have an Aureal Vortex 1 or 2 card and want - to use its gameport. - - To compile this driver as a module, choose M here: the - module will be called vortex. - config GAMEPORT_FM801 tristate "ForteMedia FM801 gameport support" depends on PCI -config GAMEPORT_CS461X - tristate "Crystal SoundFusion gameport support" - depends on PCI - endif diff --git a/drivers/input/gameport/Makefile b/drivers/input/gameport/Makefile index 5367b42..b6f6097 100644 --- a/drivers/input/gameport/Makefile +++ b/drivers/input/gameport/Makefile @@ -5,9 +5,7 @@ # Each configuration option enables a list of files. obj-$(CONFIG_GAMEPORT) += gameport.o -obj-$(CONFIG_GAMEPORT_CS461X) += cs461x.o obj-$(CONFIG_GAMEPORT_EMU10K1) += emu10k1-gp.o obj-$(CONFIG_GAMEPORT_FM801) += fm801-gp.o obj-$(CONFIG_GAMEPORT_L4) += lightning.o obj-$(CONFIG_GAMEPORT_NS558) += ns558.o -obj-$(CONFIG_GAMEPORT_VORTEX) += vortex.o diff --git a/drivers/input/gameport/cs461x.c b/drivers/input/gameport/cs461x.c deleted file mode 100644 index d4013ff..0000000 --- a/drivers/input/gameport/cs461x.c +++ /dev/null @@ -1,322 +0,0 @@ -/* - The all defines and part of code (such as cs461x_*) are - contributed from ALSA 0.5.8 sources. - See http://www.alsa-project.org/ for sources - - Tested on Linux 686 2.4.0-test9, ALSA 0.5.8a and CS4610 -*/ - -#include <asm/io.h> - -#include <linux/module.h> -#include <linux/ioport.h> -#include <linux/config.h> -#include <linux/init.h> -#include <linux/gameport.h> -#include <linux/slab.h> -#include <linux/pci.h> - -MODULE_AUTHOR("Victor Krapivin"); -MODULE_LICENSE("GPL"); - -/* - These options are experimental - -#define CS461X_FULL_MAP -*/ - - -#ifndef PCI_VENDOR_ID_CIRRUS -#define PCI_VENDOR_ID_CIRRUS 0x1013 -#endif -#ifndef PCI_DEVICE_ID_CIRRUS_4610 -#define PCI_DEVICE_ID_CIRRUS_4610 0x6001 -#endif -#ifndef PCI_DEVICE_ID_CIRRUS_4612 -#define PCI_DEVICE_ID_CIRRUS_4612 0x6003 -#endif -#ifndef PCI_DEVICE_ID_CIRRUS_4615 -#define PCI_DEVICE_ID_CIRRUS_4615 0x6004 -#endif - -/* Registers */ - -#define BA0_JSPT 0x00000480 -#define BA0_JSCTL 0x00000484 -#define BA0_JSC1 0x00000488 -#define BA0_JSC2 0x0000048C -#define BA0_JSIO 0x000004A0 - -/* Bits for JSPT */ - -#define JSPT_CAX 0x00000001 -#define JSPT_CAY 0x00000002 -#define JSPT_CBX 0x00000004 -#define JSPT_CBY 0x00000008 -#define JSPT_BA1 0x00000010 -#define JSPT_BA2 0x00000020 -#define JSPT_BB1 0x00000040 -#define JSPT_BB2 0x00000080 - -/* Bits for JSCTL */ - -#define JSCTL_SP_MASK 0x00000003 -#define JSCTL_SP_SLOW 0x00000000 -#define JSCTL_SP_MEDIUM_SLOW 0x00000001 -#define JSCTL_SP_MEDIUM_FAST 0x00000002 -#define JSCTL_SP_FAST 0x00000003 -#define JSCTL_ARE 0x00000004 - -/* Data register pairs masks */ - -#define JSC1_Y1V_MASK 0x0000FFFF -#define JSC1_X1V_MASK 0xFFFF0000 -#define JSC1_Y1V_SHIFT 0 -#define JSC1_X1V_SHIFT 16 -#define JSC2_Y2V_MASK 0x0000FFFF -#define JSC2_X2V_MASK 0xFFFF0000 -#define JSC2_Y2V_SHIFT 0 -#define JSC2_X2V_SHIFT 16 - -/* JS GPIO */ - -#define JSIO_DAX 0x00000001 -#define JSIO_DAY 0x00000002 -#define JSIO_DBX 0x00000004 -#define JSIO_DBY 0x00000008 -#define JSIO_AXOE 0x00000010 -#define JSIO_AYOE 0x00000020 -#define JSIO_BXOE 0x00000040 -#define JSIO_BYOE 0x00000080 - -/* - The card initialization code is obfuscated; the module cs461x - need to be loaded after ALSA modules initialized and something - played on the CS 4610 chip (see sources for details of CS4610 - initialization code from ALSA) -*/ - -/* Card specific definitions */ - -#define CS461X_BA0_SIZE 0x2000 -#define CS461X_BA1_DATA0_SIZE 0x3000 -#define CS461X_BA1_DATA1_SIZE 0x3800 -#define CS461X_BA1_PRG_SIZE 0x7000 -#define CS461X_BA1_REG_SIZE 0x0100 - -#define BA1_SP_DMEM0 0x00000000 -#define BA1_SP_DMEM1 0x00010000 -#define BA1_SP_PMEM 0x00020000 -#define BA1_SP_REG 0x00030000 - -#define BA1_DWORD_SIZE (13 * 1024 + 512) -#define BA1_MEMORY_COUNT 3 - -/* - Only one CS461x card is still suppoted; the code requires - redesign to avoid this limitatuion. -*/ - -static unsigned long ba0_addr; -static unsigned int __iomem *ba0; - -#ifdef CS461X_FULL_MAP -static unsigned long ba1_addr; -static union ba1_t { - struct { - unsigned int __iomem *data0; - unsigned int __iomem *data1; - unsigned int __iomem *pmem; - unsigned int __iomem *reg; - } name; - unsigned int __iomem *idx[4]; -} ba1; - -static void cs461x_poke(unsigned long reg, unsigned int val) -{ - writel(val, &ba1.idx[(reg >> 16) & 3][(reg >> 2) & 0x3fff]); -} - -static unsigned int cs461x_peek(unsigned long reg) -{ - return readl(&ba1.idx[(reg >> 16) & 3][(reg >> 2) & 0x3fff]); -} - -#endif - -static void cs461x_pokeBA0(unsigned long reg, unsigned int val) -{ - writel(val, &ba0[reg >> 2]); -} - -static unsigned int cs461x_peekBA0(unsigned long reg) -{ - return readl(&ba0[reg >> 2]); -} - -static int cs461x_free(struct pci_dev *pdev) -{ - struct gameport *port = pci_get_drvdata(pdev); - - if (port) - gameport_unregister_port(port); - - if (ba0) iounmap(ba0); -#ifdef CS461X_FULL_MAP - if (ba1.name.data0) iounmap(ba1.name.data0); - if (ba1.name.data1) iounmap(ba1.name.data1); - if (ba1.name.pmem) iounmap(ba1.name.pmem); - if (ba1.name.reg) iounmap(ba1.name.reg); -#endif - return 0; -} - -static void cs461x_gameport_trigger(struct gameport *gameport) -{ - cs461x_pokeBA0(BA0_JSPT, 0xFF); //outb(gameport->io, 0xFF); -} - -static unsigned char cs461x_gameport_read(struct gameport *gameport) -{ - return cs461x_peekBA0(BA0_JSPT); //inb(gameport->io); -} - -static int cs461x_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons) -{ - unsigned js1, js2, jst; - - js1 = cs461x_peekBA0(BA0_JSC1); - js2 = cs461x_peekBA0(BA0_JSC2); - jst = cs461x_peekBA0(BA0_JSPT); - - *buttons = (~jst >> 4) & 0x0F; - - axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF; - axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF; - axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF; - axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF; - - for(jst=0;jst<4;++jst) - if(axes[jst]==0xFFFF) axes[jst] = -1; - return 0; -} - -static int cs461x_gameport_open(struct gameport *gameport, int mode) -{ - switch (mode) { - case GAMEPORT_MODE_COOKED: - case GAMEPORT_MODE_RAW: - return 0; - default: - return -1; - } - return 0; -} - -static struct pci_device_id cs461x_pci_tbl[] = { - { PCI_VENDOR_ID_CIRRUS, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4610 */ - { PCI_VENDOR_ID_CIRRUS, 0x6003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4612 */ - { PCI_VENDOR_ID_CIRRUS, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4615 */ - { 0, } -}; -MODULE_DEVICE_TABLE(pci, cs461x_pci_tbl); - -static int __devinit cs461x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int rc; - struct gameport* port; - - rc = pci_enable_device(pdev); - if (rc) { - printk(KERN_ERR "cs461x: Cannot enable PCI gameport (bus %d, devfn %d) error=%d\n", - pdev->bus->number, pdev->devfn, rc); - return rc; - } - - ba0_addr = pci_resource_start(pdev, 0); -#ifdef CS461X_FULL_MAP - ba1_addr = pci_resource_start(pdev, 1); -#endif - if (ba0_addr == 0 || ba0_addr == ~0 -#ifdef CS461X_FULL_MAP - || ba1_addr == 0 || ba1_addr == ~0 -#endif - ) { - printk(KERN_ERR "cs461x: wrong address - ba0 = 0x%lx\n", ba0_addr); -#ifdef CS461X_FULL_MAP - printk(KERN_ERR "cs461x: wrong address - ba1 = 0x%lx\n", ba1_addr); -#endif - cs461x_free(pdev); - return -ENOMEM; - } - - ba0 = ioremap(ba0_addr, CS461X_BA0_SIZE); -#ifdef CS461X_FULL_MAP - ba1.name.data0 = ioremap(ba1_addr + BA1_SP_DMEM0, CS461X_BA1_DATA0_SIZE); - ba1.name.data1 = ioremap(ba1_addr + BA1_SP_DMEM1, CS461X_BA1_DATA1_SIZE); - ba1.name.pmem = ioremap(ba1_addr + BA1_SP_PMEM, CS461X_BA1_PRG_SIZE); - ba1.name.reg = ioremap(ba1_addr + BA1_SP_REG, CS461X_BA1_REG_SIZE); - - if (ba0 == NULL || ba1.name.data0 == NULL || - ba1.name.data1 == NULL || ba1.name.pmem == NULL || - ba1.name.reg == NULL) { - cs461x_free(pdev); - return -ENOMEM; - } -#else - if (ba0 == NULL) { - cs461x_free(pdev); - return -ENOMEM; - } -#endif - - if (!(port = gameport_allocate_port())) { - printk(KERN_ERR "cs461x: Memory allocation failed\n"); - cs461x_free(pdev); - return -ENOMEM; - } - - pci_set_drvdata(pdev, port); - - port->open = cs461x_gameport_open; - port->trigger = cs461x_gameport_trigger; - port->read = cs461x_gameport_read; - port->cooked_read = cs461x_gameport_cooked_read; - - gameport_set_name(port, "CS416x"); - gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev)); - port->dev.parent = &pdev->dev; - - cs461x_pokeBA0(BA0_JSIO, 0xFF); // ? - cs461x_pokeBA0(BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW); - - gameport_register_port(port); - - return 0; -} - -static void __devexit cs461x_pci_remove(struct pci_dev *pdev) -{ - cs461x_free(pdev); -} - -static struct pci_driver cs461x_pci_driver = { - .name = "CS461x_gameport", - .id_table = cs461x_pci_tbl, - .probe = cs461x_pci_probe, - .remove = __devexit_p(cs461x_pci_remove), -}; - -static int __init cs461x_init(void) -{ - return pci_register_driver(&cs461x_pci_driver); -} - -static void __exit cs461x_exit(void) -{ - pci_unregister_driver(&cs461x_pci_driver); -} - -module_init(cs461x_init); -module_exit(cs461x_exit); - diff --git a/drivers/input/gameport/ns558.c b/drivers/input/gameport/ns558.c index 7c5c631..1ab5f2d 100644 --- a/drivers/input/gameport/ns558.c +++ b/drivers/input/gameport/ns558.c @@ -258,18 +258,18 @@ static int __init ns558_init(void) { int i = 0; + if (pnp_register_driver(&ns558_pnp_driver) >= 0) + pnp_registered = 1; + /* - * Probe ISA ports first so that PnP gets to choose free port addresses - * not occupied by the ISA ports. + * Probe ISA ports after PnP, so that PnP ports that are already + * enabled get detected as PnP. This may be suboptimal in multi-device + * configurations, but saves hassle with simple setups. */ while (ns558_isa_portlist[i]) ns558_isa_probe(ns558_isa_portlist[i++]); - if (pnp_register_driver(&ns558_pnp_driver) >= 0) - pnp_registered = 1; - - return (list_empty(&ns558_list) && !pnp_registered) ? -ENODEV : 0; } diff --git a/drivers/input/gameport/vortex.c b/drivers/input/gameport/vortex.c deleted file mode 100644 index 36b0309..0000000 --- a/drivers/input/gameport/vortex.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * $Id: vortex.c,v 1.5 2002/07/01 15:39:30 vojtech Exp $ - * - * Copyright (c) 2000-2001 Vojtech Pavlik - * - * Based on the work of: - * Raymond Ingles - */ - -/* - * Trident 4DWave and Aureal Vortex gameport driver for Linux - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/gameport.h> - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); -MODULE_DESCRIPTION("Aureal Vortex and Vortex2 gameport driver"); -MODULE_LICENSE("GPL"); - -#define VORTEX_GCR 0x0c /* Gameport control register */ -#define VORTEX_LEG 0x08 /* Legacy port location */ -#define VORTEX_AXD 0x10 /* Axes start */ -#define VORTEX_DATA_WAIT 20 /* 20 ms */ - -struct vortex { - struct gameport *gameport; - struct pci_dev *dev; - unsigned char __iomem *base; - unsigned char __iomem *io; -}; - -static unsigned char vortex_read(struct gameport *gameport) -{ - struct vortex *vortex = gameport->port_data; - return readb(vortex->io + VORTEX_LEG); -} - -static void vortex_trigger(struct gameport *gameport) -{ - struct vortex *vortex = gameport->port_data; - writeb(0xff, vortex->io + VORTEX_LEG); -} - -static int vortex_cooked_read(struct gameport *gameport, int *axes, int *buttons) -{ - struct vortex *vortex = gameport->port_data; - int i; - - *buttons = (~readb(vortex->base + VORTEX_LEG) >> 4) & 0xf; - - for (i = 0; i < 4; i++) { - axes[i] = readw(vortex->io + VORTEX_AXD + i * sizeof(u32)); - if (axes[i] == 0x1fff) axes[i] = -1; - } - - return 0; -} - -static int vortex_open(struct gameport *gameport, int mode) -{ - struct vortex *vortex = gameport->port_data; - - switch (mode) { - case GAMEPORT_MODE_COOKED: - writeb(0x40, vortex->io + VORTEX_GCR); - msleep(VORTEX_DATA_WAIT); - return 0; - case GAMEPORT_MODE_RAW: - writeb(0x00, vortex->io + VORTEX_GCR); - return 0; - default: - return -1; - } - - return 0; -} - -static int __devinit vortex_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - struct vortex *vortex; - struct gameport *port; - int i; - - vortex = kcalloc(1, sizeof(struct vortex), GFP_KERNEL); - port = gameport_allocate_port(); - if (!vortex || !port) { - printk(KERN_ERR "vortex: Memory allocation failed.\n"); - kfree(vortex); - gameport_free_port(port); - return -ENOMEM; - } - - for (i = 0; i < 6; i++) - if (~pci_resource_flags(dev, i) & IORESOURCE_IO) - break; - - pci_enable_device(dev); - - vortex->dev = dev; - vortex->gameport = port; - vortex->base = ioremap(pci_resource_start(vortex->dev, i), - pci_resource_len(vortex->dev, i)); - vortex->io = vortex->base + id->driver_data; - - pci_set_drvdata(dev, vortex); - - port->port_data = vortex; - port->fuzz = 64; - - gameport_set_name(port, "AU88x0"); - gameport_set_phys(port, "pci%s/gameport0", pci_name(dev)); - port->dev.parent = &dev->dev; - port->read = vortex_read; - port->trigger = vortex_trigger; - port->cooked_read = vortex_cooked_read; - port->open = vortex_open; - - gameport_register_port(port); - - return 0; -} - -static void __devexit vortex_remove(struct pci_dev *dev) -{ - struct vortex *vortex = pci_get_drvdata(dev); - - gameport_unregister_port(vortex->gameport); - iounmap(vortex->base); - kfree(vortex); -} - -static struct pci_device_id vortex_id_table[] = { - { 0x12eb, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x11000 }, - { 0x12eb, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x28800 }, - { 0 } -}; - -static struct pci_driver vortex_driver = { - .name = "vortex_gameport", - .id_table = vortex_id_table, - .probe = vortex_probe, - .remove = __devexit_p(vortex_remove), -}; - -static int __init vortex_init(void) -{ - return pci_register_driver(&vortex_driver); -} - -static void __exit vortex_exit(void) -{ - pci_unregister_driver(&vortex_driver); -} - -module_init(vortex_init); -module_exit(vortex_exit); diff --git a/drivers/input/input.c b/drivers/input/input.c index 83c77c9..7c4b4d3 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -219,10 +219,24 @@ void input_release_device(struct input_handle *handle) int input_open_device(struct input_handle *handle) { + struct input_dev *dev = handle->dev; + int err; + + err = down_interruptible(&dev->sem); + if (err) + return err; + handle->open++; - if (handle->dev->open) - return handle->dev->open(handle->dev); - return 0; + + if (!dev->users++ && dev->open) + err = dev->open(dev); + + if (err) + handle->open--; + + up(&dev->sem); + + return err; } int input_flush_device(struct input_handle* handle, struct file* file) @@ -235,10 +249,17 @@ int input_flush_device(struct input_handle* handle, struct file* file) void input_close_device(struct input_handle *handle) { + struct input_dev *dev = handle->dev; + input_release_device(handle); - if (handle->dev->close) - handle->dev->close(handle->dev); + + down(&dev->sem); + + if (!--dev->users && dev->close) + dev->close(dev); handle->open--; + + up(&dev->sem); } static void input_link_handle(struct input_handle *handle) @@ -415,6 +436,8 @@ void input_register_device(struct input_dev *dev) set_bit(EV_SYN, dev->evbit); + init_MUTEX(&dev->sem); + /* * If delay and period are pre-set by the driver, then autorepeating * is handled by the driver itself and we don't do it in input.c. @@ -674,6 +697,8 @@ static int input_handlers_read(char *buf, char **start, off_t pos, int count, in return (count > cnt) ? cnt : count; } +static struct file_operations input_fileops; + static int __init input_proc_init(void) { struct proc_dir_entry *entry; @@ -688,6 +713,8 @@ static int __init input_proc_init(void) return -ENOMEM; } entry->owner = THIS_MODULE; + input_fileops = *entry->proc_fops; + entry->proc_fops = &input_fileops; entry->proc_fops->poll = input_devices_poll; entry = create_proc_read_entry("handlers", 0, proc_bus_input_dir, input_handlers_read, NULL); if (entry == NULL) { diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 39775fc..ff8e1bb 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -285,48 +285,33 @@ static unsigned int joydev_poll(struct file *file, poll_table *wait) (POLLIN | POLLRDNORM) : 0) | (list->joydev->exist ? 0 : (POLLHUP | POLLERR)); } -static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __user *argp) { - struct joydev_list *list = file->private_data; - struct joydev *joydev = list->joydev; struct input_dev *dev = joydev->handle.dev; - void __user *argp = (void __user *)arg; int i, j; - if (!joydev->exist) return -ENODEV; - switch (cmd) { case JS_SET_CAL: return copy_from_user(&joydev->glue.JS_CORR, argp, - sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; + sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; case JS_GET_CAL: return copy_to_user(argp, &joydev->glue.JS_CORR, - sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; + sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; case JS_SET_TIMEOUT: - return get_user(joydev->glue.JS_TIMEOUT, (int __user *) arg); + return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); case JS_GET_TIMEOUT: - return put_user(joydev->glue.JS_TIMEOUT, (int __user *) arg); - case JS_SET_TIMELIMIT: - return get_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg); - case JS_GET_TIMELIMIT: - return put_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg); - case JS_SET_ALL: - return copy_from_user(&joydev->glue, argp, - sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; - case JS_GET_ALL: - return copy_to_user(argp, &joydev->glue, - sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; + return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); case JSIOCGVERSION: - return put_user(JS_VERSION, (__u32 __user *) arg); + return put_user(JS_VERSION, (__u32 __user *) argp); case JSIOCGAXES: - return put_user(joydev->nabs, (__u8 __user *) arg); + return put_user(joydev->nabs, (__u8 __user *) argp); case JSIOCGBUTTONS: - return put_user(joydev->nkey, (__u8 __user *) arg); + return put_user(joydev->nkey, (__u8 __user *) argp); case JSIOCSCORR: if (copy_from_user(joydev->corr, argp, - sizeof(struct js_corr) * joydev->nabs)) + sizeof(joydev->corr[0]) * joydev->nabs)) return -EFAULT; for (i = 0; i < joydev->nabs; i++) { j = joydev->abspam[i]; @@ -335,7 +320,7 @@ static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd return 0; case JSIOCGCORR: return copy_to_user(argp, joydev->corr, - sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0; + sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0; case JSIOCSAXMAP: if (copy_from_user(joydev->abspam, argp, sizeof(__u8) * (ABS_MAX + 1))) return -EFAULT; @@ -371,6 +356,84 @@ static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd return -EINVAL; } +#ifdef CONFIG_COMPAT +static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct joydev_list *list = file->private_data; + struct joydev *joydev = list->joydev; + void __user *argp = (void __user *)arg; + s32 tmp32; + struct JS_DATA_SAVE_TYPE_32 ds32; + int err; + + if (!joydev->exist) return -ENODEV; + switch(cmd) { + case JS_SET_TIMELIMIT: + err = get_user(tmp32, (s32 __user *) arg); + if (err == 0) + joydev->glue.JS_TIMELIMIT = tmp32; + break; + case JS_GET_TIMELIMIT: + tmp32 = joydev->glue.JS_TIMELIMIT; + err = put_user(tmp32, (s32 __user *) arg); + break; + + case JS_SET_ALL: + err = copy_from_user(&ds32, argp, + sizeof(ds32)) ? -EFAULT : 0; + if (err == 0) { + joydev->glue.JS_TIMEOUT = ds32.JS_TIMEOUT; + joydev->glue.BUSY = ds32.BUSY; + joydev->glue.JS_EXPIRETIME = ds32.JS_EXPIRETIME; + joydev->glue.JS_TIMELIMIT = ds32.JS_TIMELIMIT; + joydev->glue.JS_SAVE = ds32.JS_SAVE; + joydev->glue.JS_CORR = ds32.JS_CORR; + } + break; + + case JS_GET_ALL: + ds32.JS_TIMEOUT = joydev->glue.JS_TIMEOUT; + ds32.BUSY = joydev->glue.BUSY; + ds32.JS_EXPIRETIME = joydev->glue.JS_EXPIRETIME; + ds32.JS_TIMELIMIT = joydev->glue.JS_TIMELIMIT; + ds32.JS_SAVE = joydev->glue.JS_SAVE; + ds32.JS_CORR = joydev->glue.JS_CORR; + + err = copy_to_user(argp, &ds32, + sizeof(ds32)) ? -EFAULT : 0; + break; + + default: + err = joydev_ioctl_common(joydev, cmd, argp); + } + return err; +} +#endif /* CONFIG_COMPAT */ + +static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct joydev_list *list = file->private_data; + struct joydev *joydev = list->joydev; + void __user *argp = (void __user *)arg; + + if (!joydev->exist) return -ENODEV; + + switch(cmd) { + case JS_SET_TIMELIMIT: + return get_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg); + case JS_GET_TIMELIMIT: + return put_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg); + case JS_SET_ALL: + return copy_from_user(&joydev->glue, argp, + sizeof(joydev->glue)) ? -EFAULT : 0; + case JS_GET_ALL: + return copy_to_user(argp, &joydev->glue, + sizeof(joydev->glue)) ? -EFAULT : 0; + default: + return joydev_ioctl_common(joydev, cmd, argp); + } +} + static struct file_operations joydev_fops = { .owner = THIS_MODULE, .read = joydev_read, @@ -379,6 +442,9 @@ static struct file_operations joydev_fops = { .open = joydev_open, .release = joydev_release, .ioctl = joydev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = joydev_compat_ioctl, +#endif .fasync = joydev_fasync, }; diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c index ad39fe4..bf34f75 100644 --- a/drivers/input/joystick/a3d.c +++ b/drivers/input/joystick/a3d.c @@ -185,7 +185,7 @@ static void a3d_poll(struct gameport *gameport) a3d->reads++; if (a3d_read_packet(a3d->gameport, a3d->length, data) != a3d->length || data[0] != a3d->mode || a3d_csum(data, a3d->length)) - a3d->bads++; + a3d->bads++; else a3d_read(a3d, data); } diff --git a/drivers/input/joystick/adi.c b/drivers/input/joystick/adi.c index 83f6daf..2659629 100644 --- a/drivers/input/joystick/adi.c +++ b/drivers/input/joystick/adi.c @@ -82,7 +82,7 @@ static char adi_cm2_abs[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ }; static char adi_wmf_abs[] = { ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y }; static short adi_wmgpe_key[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT }; -static short adi_wmi_key[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_EXTRA }; +static short adi_wmi_key[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_EXTRA }; static short adi_wmed3d_key[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2 }; static short adi_cm2_key[] = { BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 }; @@ -183,7 +183,7 @@ static void adi_move_bits(struct adi_port *port, int length) int i; struct adi *adi = port->adi; - adi[0].idx = adi[1].idx = 0; + adi[0].idx = adi[1].idx = 0; if (adi[0].ret <= 0 || adi[1].ret <= 0) return; if (adi[0].data[0] & 0x20 || ~adi[1].data[0] & 0x20) return; diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c index cf36ca9..033456b 100644 --- a/drivers/input/joystick/amijoy.c +++ b/drivers/input/joystick/amijoy.c @@ -51,7 +51,8 @@ MODULE_PARM_DESC(map, "Map of attached joysticks in form of <a>,<b> (default is __obsolete_setup("amijoy="); -static int amijoy_used[2] = { 0, 0 }; +static int amijoy_used; +static DECLARE_MUTEX(amijoy_sem); static struct input_dev amijoy_dev[2]; static char *amijoy_phys[2] = { "amijoy/input0", "amijoy/input1" }; @@ -84,26 +85,30 @@ static irqreturn_t amijoy_interrupt(int irq, void *dummy, struct pt_regs *fp) static int amijoy_open(struct input_dev *dev) { - int *used = dev->private; + int err; - if ((*used)++) - return 0; + err = down_interruptible(&amijoy_sem); + if (err) + return err; - if (request_irq(IRQ_AMIGA_VERTB, amijoy_interrupt, 0, "amijoy", amijoy_interrupt)) { - (*used)--; + if (!amijoy_used && request_irq(IRQ_AMIGA_VERTB, amijoy_interrupt, 0, "amijoy", amijoy_interrupt)) { printk(KERN_ERR "amijoy.c: Can't allocate irq %d\n", IRQ_AMIGA_VERTB); - return -EBUSY; + err = -EBUSY; + goto out; } - return 0; + amijoy_used++; +out: + up(&amijoy_sem); + return err; } static void amijoy_close(struct input_dev *dev) { - int *used = dev->private; - - if (!--(*used)) + down(&amijoysem); + if (!--amijoy_used) free_irq(IRQ_AMIGA_VERTB, amijoy_interrupt); + up(&amijoy_sem); } static int __init amijoy_init(void) @@ -138,8 +143,6 @@ static int __init amijoy_init(void) amijoy_dev[i].id.product = 0x0003; amijoy_dev[i].id.version = 0x0100; - amijoy_dev[i].private = amijoy_used + i; - input_register_device(amijoy_dev + i); printk(KERN_INFO "input: %s at joy%ddat\n", amijoy_name, i); } diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c index cfdd3ac..fbd3eed 100644 --- a/drivers/input/joystick/db9.c +++ b/drivers/input/joystick/db9.c @@ -87,7 +87,7 @@ __obsolete_setup("db9_3="); #define DB9_NORMAL 0x0a #define DB9_NOSELECT 0x08 -#define DB9_MAX_DEVICES 2 +#define DB9_MAX_DEVICES 2 #define DB9_GENESIS6_DELAY 14 #define DB9_REFRESH_TIME HZ/100 @@ -98,6 +98,7 @@ struct db9 { struct pardevice *pd; int mode; int used; + struct semaphore sem; char phys[2][32]; }; @@ -503,6 +504,11 @@ static int db9_open(struct input_dev *dev) { struct db9 *db9 = dev->private; struct parport *port = db9->pd->port; + int err; + + err = down_interruptible(&db9->sem); + if (err) + return err; if (!db9->used++) { parport_claim(db9->pd); @@ -514,6 +520,7 @@ static int db9_open(struct input_dev *dev) mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME); } + up(&db9->sem); return 0; } @@ -522,12 +529,14 @@ static void db9_close(struct input_dev *dev) struct db9 *db9 = dev->private; struct parport *port = db9->pd->port; + down(&db9->sem); if (!--db9->used) { - del_timer(&db9->timer); + del_timer_sync(&db9->timer); parport_write_control(port, 0x00); parport_data_forward(port); parport_release(db9->pd); } + up(&db9->sem); } static struct db9 __init *db9_probe(int *config, int nargs) @@ -563,12 +572,12 @@ static struct db9 __init *db9_probe(int *config, int nargs) } } - if (!(db9 = kmalloc(sizeof(struct db9), GFP_KERNEL))) { + if (!(db9 = kcalloc(1, sizeof(struct db9), GFP_KERNEL))) { parport_put_port(pp); return NULL; } - memset(db9, 0, sizeof(struct db9)); + init_MUTEX(&db9->sem); db9->mode = config[1]; init_timer(&db9->timer); db9->timer.data = (long) db9; diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c index 8732f52..95bbdd3 100644 --- a/drivers/input/joystick/gamecon.c +++ b/drivers/input/joystick/gamecon.c @@ -1,12 +1,12 @@ /* * NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux * - * Copyright (c) 1999-2004 Vojtech Pavlik <vojtech@suse.cz> - * Copyright (c) 2004 Peter Nelson <rufus-kernel@hackish.org> + * Copyright (c) 1999-2004 Vojtech Pavlik <vojtech@suse.cz> + * Copyright (c) 2004 Peter Nelson <rufus-kernel@hackish.org> * * Based on the work of: - * Andree Borrmann John Dahlstrom - * David Kuder Nathan Hand + * Andree Borrmann John Dahlstrom + * David Kuder Nathan Hand */ /* @@ -81,6 +81,7 @@ struct gc { struct timer_list timer; unsigned char pads[GC_MAX + 1]; int used; + struct semaphore sem; char phys[5][32]; }; @@ -433,7 +434,7 @@ static void gc_timer(unsigned long private) gc_psx_read_packet(gc, data_psx, data); for (i = 0; i < 5; i++) { - switch (data[i]) { + switch (data[i]) { case GC_PSX_RUMBLE: @@ -503,22 +504,33 @@ static void gc_timer(unsigned long private) static int gc_open(struct input_dev *dev) { struct gc *gc = dev->private; + int err; + + err = down_interruptible(&gc->sem); + if (err) + return err; + if (!gc->used++) { parport_claim(gc->pd); parport_write_control(gc->pd->port, 0x04); mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME); } + + up(&gc->sem); return 0; } static void gc_close(struct input_dev *dev) { struct gc *gc = dev->private; + + down(&gc->sem); if (!--gc->used) { - del_timer(&gc->timer); + del_timer_sync(&gc->timer); parport_write_control(gc->pd->port, 0x00); parport_release(gc->pd); } + up(&gc->sem); } static struct gc __init *gc_probe(int *config, int nargs) @@ -542,11 +554,12 @@ static struct gc __init *gc_probe(int *config, int nargs) return NULL; } - if (!(gc = kmalloc(sizeof(struct gc), GFP_KERNEL))) { + if (!(gc = kcalloc(1, sizeof(struct gc), GFP_KERNEL))) { parport_put_port(pp); return NULL; } - memset(gc, 0, sizeof(struct gc)); + + init_MUTEX(&gc->sem); gc->pd = parport_register_device(pp, "gamecon", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c index ad13f09..7d96942 100644 --- a/drivers/input/joystick/gf2k.c +++ b/drivers/input/joystick/gf2k.c @@ -329,7 +329,7 @@ static int gf2k_connect(struct gameport *gameport, struct gameport_driver *drv) for (i = 0; i < gf2k_axes[gf2k->id]; i++) { gf2k->dev.absmax[gf2k_abs[i]] = (i < 2) ? gf2k->dev.abs[gf2k_abs[i]] * 2 - 32 : - gf2k->dev.abs[gf2k_abs[0]] + gf2k->dev.abs[gf2k_abs[1]] - 32; + gf2k->dev.abs[gf2k_abs[0]] + gf2k->dev.abs[gf2k_abs[1]] - 32; gf2k->dev.absmin[gf2k_abs[i]] = 32; gf2k->dev.absfuzz[gf2k_abs[i]] = 8; gf2k->dev.absflat[gf2k_abs[i]] = (i < 2) ? 24 : 0; diff --git a/drivers/input/joystick/grip_mp.c b/drivers/input/joystick/grip_mp.c index 42e5005..0da7bd1 100644 --- a/drivers/input/joystick/grip_mp.c +++ b/drivers/input/joystick/grip_mp.c @@ -171,7 +171,7 @@ static int mp_io(struct gameport* gameport, int sendflags, int sendcode, u32 *pa *packet = 0; raw_data = gameport_read(gameport); if (raw_data & 1) - return IO_RETRY; + return IO_RETRY; for (i = 0; i < 64; i++) { raw_data = gameport_read(gameport); diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c index 028f351..e31b7b9 100644 --- a/drivers/input/joystick/iforce/iforce-main.c +++ b/drivers/input/joystick/iforce/iforce-main.c @@ -78,6 +78,7 @@ static struct iforce_device iforce_device[] = { { 0x061c, 0xc0a4, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, //? { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce }, //? { 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //? + { 0x06f8, 0x0004, "Gullemot Jet Leader 3D", btn_joystick, abs_joystick, ff_iforce }, //? { 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]", btn_joystick, abs_joystick, ff_iforce } }; diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index 617c0b0..6369a24 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -229,6 +229,7 @@ static struct usb_device_id iforce_usb_ids [] = { { USB_DEVICE(0x061c, 0xc0a4) }, /* ACT LABS Force RS */ { USB_DEVICE(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */ { USB_DEVICE(0x06f8, 0x0004) }, /* Guillemot Force Feedback Racing Wheel */ + { USB_DEVICE(0x06f8, 0xa302) }, /* Guillemot Jet Leader 3D */ { } /* Terminating entry */ }; diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c index ec0a2a6..a436f22 100644 --- a/drivers/input/joystick/spaceball.c +++ b/drivers/input/joystick/spaceball.c @@ -4,8 +4,8 @@ * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: - * David Thompson - * Joseph Krahn + * David Thompson + * Joseph Krahn */ /* diff --git a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c index 874367b..01fd2e4 100644 --- a/drivers/input/joystick/spaceorb.c +++ b/drivers/input/joystick/spaceorb.c @@ -4,7 +4,7 @@ * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: - * David Thompson + * David Thompson */ /* diff --git a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c index aaee52c..9eb9954 100644 --- a/drivers/input/joystick/tmdc.c +++ b/drivers/input/joystick/tmdc.c @@ -79,7 +79,7 @@ static short tmdc_btn_pad[TMDC_BTN] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_START, BTN_SELECT, BTN_TL, BTN_TR }; static short tmdc_btn_joy[TMDC_BTN] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_THUMB2, BTN_PINKIE, - BTN_BASE3, BTN_BASE4, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z }; + BTN_BASE3, BTN_BASE4, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z }; static short tmdc_btn_fm[TMDC_BTN] = { BTN_TRIGGER, BTN_C, BTN_B, BTN_A, BTN_THUMB, BTN_X, BTN_Y, BTN_Z, BTN_TOP, BTN_TOP2 }; static short tmdc_btn_at[TMDC_BTN] = diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c index dd88b9c..28100d4 100644 --- a/drivers/input/joystick/turbografx.c +++ b/drivers/input/joystick/turbografx.c @@ -84,6 +84,7 @@ static struct tgfx { char phys[7][32]; int sticks; int used; + struct semaphore sem; } *tgfx_base[3]; /* @@ -99,7 +100,7 @@ static void tgfx_timer(unsigned long private) for (i = 0; i < 7; i++) if (tgfx->sticks & (1 << i)) { - dev = tgfx->dev + i; + dev = tgfx->dev + i; parport_write_data(tgfx->pd->port, ~(1 << i)); data1 = parport_read_status(tgfx->pd->port) ^ 0x7f; @@ -122,23 +123,34 @@ static void tgfx_timer(unsigned long private) static int tgfx_open(struct input_dev *dev) { - struct tgfx *tgfx = dev->private; - if (!tgfx->used++) { + struct tgfx *tgfx = dev->private; + int err; + + err = down_interruptible(&tgfx->sem); + if (err) + return err; + + if (!tgfx->used++) { parport_claim(tgfx->pd); parport_write_control(tgfx->pd->port, 0x04); - mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME); + mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME); } - return 0; + + up(&tgfx->sem); + return 0; } static void tgfx_close(struct input_dev *dev) { - struct tgfx *tgfx = dev->private; - if (!--tgfx->used) { - del_timer(&tgfx->timer); + struct tgfx *tgfx = dev->private; + + down(&tgfx->sem); + if (!--tgfx->used) { + del_timer_sync(&tgfx->timer); parport_write_control(tgfx->pd->port, 0x00); - parport_release(tgfx->pd); + parport_release(tgfx->pd); } + up(&tgfx->sem); } /* @@ -166,11 +178,12 @@ static struct tgfx __init *tgfx_probe(int *config, int nargs) return NULL; } - if (!(tgfx = kmalloc(sizeof(struct tgfx), GFP_KERNEL))) { + if (!(tgfx = kcalloc(1, sizeof(struct tgfx), GFP_KERNEL))) { parport_put_port(pp); return NULL; } - memset(tgfx, 0, sizeof(struct tgfx)); + + init_MUTEX(&tgfx->sem); tgfx->pd = parport_register_device(pp, "turbografx", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 82fad9a..4d4985b 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -227,7 +227,7 @@ static ssize_t atkbd_do_set_##_name(struct device *d, struct device_attribute *a { \ return atkbd_attr_set_helper(d, b, s, atkbd_set_##_name); \ } \ -static struct device_attribute atkbd_attr_##_name = \ +static struct device_attribute atkbd_attr_##_name = \ __ATTR(_name, S_IWUSR | S_IRUGO, atkbd_do_show_##_name, atkbd_do_set_##_name); ATKBD_DEFINE_ATTR(extra); @@ -388,7 +388,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, value = atkbd->release ? 0 : (1 + (!atkbd->softrepeat && test_bit(atkbd->keycode[code], atkbd->dev.key))); - switch (value) { /* Workaround Toshiba laptop multiple keypress */ + switch (value) { /* Workaround Toshiba laptop multiple keypress */ case 0: atkbd->last = 0; break; @@ -894,7 +894,7 @@ static int atkbd_reconnect(struct serio *serio) if (atkbd->write) { param[0] = (test_bit(LED_SCROLLL, atkbd->dev.led) ? 1 : 0) | (test_bit(LED_NUML, atkbd->dev.led) ? 2 : 0) - | (test_bit(LED_CAPSL, atkbd->dev.led) ? 4 : 0); + | (test_bit(LED_CAPSL, atkbd->dev.led) ? 4 : 0); if (atkbd_probe(atkbd)) return -1; diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c index 0f1220a..a855171 100644 --- a/drivers/input/keyboard/corgikbd.c +++ b/drivers/input/keyboard/corgikbd.c @@ -39,6 +39,7 @@ #define CORGI_KEY_CALENDER KEY_F1 #define CORGI_KEY_ADDRESS KEY_F2 #define CORGI_KEY_FN KEY_F3 +#define CORGI_KEY_CANCEL KEY_F4 #define CORGI_KEY_OFF KEY_SUSPEND #define CORGI_KEY_EXOK KEY_F5 #define CORGI_KEY_EXCANCEL KEY_F6 @@ -46,6 +47,7 @@ #define CORGI_KEY_EXJOGUP KEY_F8 #define CORGI_KEY_JAP1 KEY_LEFTCTRL #define CORGI_KEY_JAP2 KEY_LEFTALT +#define CORGI_KEY_MAIL KEY_F10 #define CORGI_KEY_OK KEY_F11 #define CORGI_KEY_MENU KEY_F12 #define CORGI_HINGE_0 KEY_KP0 @@ -59,8 +61,8 @@ static unsigned char corgikbd_keycode[NR_SCANCODES] = { KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0, /* 33-48 */ CORGI_KEY_CALENDER, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /* 49-64 */ CORGI_KEY_ADDRESS, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, 0, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, /* 65-80 */ - KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, 0, CORGI_KEY_FN, 0, 0, 0, 0, /* 81-96 */ - KEY_SYSRQ, CORGI_KEY_JAP1, CORGI_KEY_JAP2, KEY_CANCEL, CORGI_KEY_OK, CORGI_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0, /* 97-112 */ + CORGI_KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, 0, CORGI_KEY_FN, 0, 0, 0, 0, /* 81-96 */ + KEY_SYSRQ, CORGI_KEY_JAP1, CORGI_KEY_JAP2, CORGI_KEY_CANCEL, CORGI_KEY_OK, CORGI_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0, /* 97-112 */ CORGI_KEY_OFF, CORGI_KEY_EXOK, CORGI_KEY_EXCANCEL, CORGI_KEY_EXJOGDOWN, CORGI_KEY_EXJOGUP, 0, 0, 0, 0, 0, 0, 0, /* 113-124 */ CORGI_HINGE_0, CORGI_HINGE_1, CORGI_HINGE_2 /* 125-127 */ }; diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c index 2694ff2..098963c 100644 --- a/drivers/input/keyboard/lkkbd.c +++ b/drivers/input/keyboard/lkkbd.c @@ -15,10 +15,10 @@ * information given below, I will _not_ be liable! * * RJ10 pinout: To DE9: Or DB25: - * 1 - RxD <----> Pin 3 (TxD) <-> Pin 2 (TxD) - * 2 - GND <----> Pin 5 (GND) <-> Pin 7 (GND) - * 4 - TxD <----> Pin 2 (RxD) <-> Pin 3 (RxD) - * 3 - +12V (from HDD drive connector), DON'T connect to DE9 or DB25!!! + * 1 - RxD <----> Pin 3 (TxD) <-> Pin 2 (TxD) + * 2 - GND <----> Pin 5 (GND) <-> Pin 7 (GND) + * 4 - TxD <----> Pin 2 (RxD) <-> Pin 3 (RxD) + * 3 - +12V (from HDD drive connector), DON'T connect to DE9 or DB25!!! * * Pin numbers for DE9 and DB25 are noted on the plug (quite small:). For * RJ10, it's like this: diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c index d3e9dd6..8935290 100644 --- a/drivers/input/keyboard/locomokbd.c +++ b/drivers/input/keyboard/locomokbd.c @@ -42,7 +42,7 @@ MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>"); MODULE_DESCRIPTION("LoCoMo keyboard driver"); MODULE_LICENSE("GPL"); -#define LOCOMOKBD_NUMKEYS 128 +#define LOCOMOKBD_NUMKEYS 128 #define KEY_ACTIVITY KEY_F16 #define KEY_CONTACT KEY_F18 @@ -61,7 +61,7 @@ static unsigned char locomokbd_keycode[LOCOMOKBD_NUMKEYS] = { KEY_G, KEY_F, KEY_X, KEY_S, 0, 0, 0, 0, 0, 0, /* 90 - 99 */ 0, 0, KEY_DOT, 0, KEY_COMMA, KEY_N, KEY_B, KEY_C, KEY_Z, KEY_A, /* 100 - 109 */ KEY_LEFTSHIFT, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0, 0, 0, /* 110 - 119 */ - KEY_M, KEY_SPACE, KEY_V, KEY_APOSTROPHE, KEY_SLASH, 0, 0, 0 /* 120 - 128 */ + KEY_M, KEY_SPACE, KEY_V, KEY_APOSTROPHE, KEY_SLASH, 0, 0, 0 /* 120 - 128 */ }; #define KB_ROWS 16 @@ -82,7 +82,7 @@ struct locomokbd { struct locomo_dev *ldev; unsigned long base; spinlock_t lock; - + struct timer_list timer; }; @@ -95,7 +95,7 @@ static inline void locomokbd_charge_all(unsigned long membase) static inline void locomokbd_activate_all(unsigned long membase) { unsigned long r; - + locomo_writel(0, membase + LOCOMO_KSC); r = locomo_readl(membase + LOCOMO_KIC); r &= 0xFEFF; @@ -127,7 +127,7 @@ static inline void locomokbd_reset_col(unsigned long membase, int col) */ /* Scan the hardware keyboard and push any changes up through the input layer */ -static void locomokbd_scankeyboard(struct locomokbd *locomokbd, struct pt_regs *regs) +static void locomokbd_scankeyboard(struct locomokbd *locomokbd, struct pt_regs *regs) { unsigned int row, col, rowd, scancode; unsigned long flags; @@ -138,7 +138,7 @@ static void locomokbd_scankeyboard(struct locomokbd *locomokbd, struct pt_regs * if (regs) input_regs(&locomokbd->input, regs); - + locomokbd_charge_all(membase); num_pressed = 0; @@ -146,9 +146,9 @@ static void locomokbd_scankeyboard(struct locomokbd *locomokbd, struct pt_regs * locomokbd_activate_col(membase, col); udelay(KB_DELAY); - + rowd = ~locomo_readl(membase + LOCOMO_KIB); - for (row = 0; row < KB_ROWS; row++ ) { + for (row = 0; row < KB_ROWS; row++) { scancode = SCANCODE(col, row); if (rowd & KB_ROWMASK(row)) { num_pressed += 1; @@ -170,7 +170,7 @@ static void locomokbd_scankeyboard(struct locomokbd *locomokbd, struct pt_regs * spin_unlock_irqrestore(&locomokbd->lock, flags); } -/* +/* * LoCoMo keyboard interrupt handler. */ static irqreturn_t locomokbd_interrupt(int irq, void *dev_id, struct pt_regs *regs) @@ -205,8 +205,8 @@ static int locomokbd_probe(struct locomo_dev *dev) memset(locomokbd, 0, sizeof(struct locomokbd)); /* try and claim memory region */ - if (!request_mem_region((unsigned long) dev->mapbase, - dev->length, + if (!request_mem_region((unsigned long) dev->mapbase, + dev->length, LOCOMO_DRIVER_NAME(dev))) { ret = -EBUSY; printk(KERN_ERR "locomokbd: Can't acquire access to io memory for keyboard\n"); @@ -225,7 +225,7 @@ static int locomokbd_probe(struct locomo_dev *dev) locomokbd->timer.data = (unsigned long) locomokbd; locomokbd->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REP); - + init_input_dev(&locomokbd->input); locomokbd->input.keycode = locomokbd->keycode; locomokbd->input.keycodesize = sizeof(unsigned char); @@ -271,11 +271,11 @@ free: static int locomokbd_remove(struct locomo_dev *dev) { struct locomokbd *locomokbd = locomo_get_drvdata(dev); - + free_irq(dev->irq[0], locomokbd); del_timer_sync(&locomokbd->timer); - + input_unregister_device(&locomokbd->input); locomo_set_drvdata(dev, NULL); diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c index 859ed77..eecbde2 100644 --- a/drivers/input/keyboard/maple_keyb.c +++ b/drivers/input/keyboard/maple_keyb.c @@ -1,6 +1,6 @@ /* * $Id: maple_keyb.c,v 1.4 2004/03/22 01:18:15 lethal Exp $ - * SEGA Dreamcast keyboard driver + * SEGA Dreamcast keyboard driver * Based on drivers/usb/usbkbd.c */ @@ -40,7 +40,6 @@ struct dc_kbd { struct input_dev dev; unsigned char new[8]; unsigned char old[8]; - int open; }; @@ -95,22 +94,6 @@ static void dc_kbd_callback(struct mapleq *mq) } } - -static int dc_kbd_open(struct input_dev *dev) -{ - struct dc_kbd *kbd = dev->private; - kbd->open++; - return 0; -} - - -static void dc_kbd_close(struct input_dev *dev) -{ - struct dc_kbd *kbd = dev->private; - kbd->open--; -} - - static int dc_kbd_connect(struct maple_device *dev) { int i; @@ -133,9 +116,6 @@ static int dc_kbd_connect(struct maple_device *dev) clear_bit(0, kbd->dev.keybit); kbd->dev.private = kbd; - kbd->dev.open = dc_kbd_open; - kbd->dev.close = dc_kbd_close; - kbd->dev.event = NULL; kbd->dev.name = dev->product_name; kbd->dev.id.bustype = BUS_MAPLE; diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 158c8e8..9871099 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -298,9 +298,11 @@ static int uinput_alloc_device(struct file *file, const char __user *buffer, siz /* check if absmin/absmax/absfuzz/absflat are filled as * told in Documentation/input/input-programming.txt */ if (test_bit(EV_ABS, dev->evbit)) { - retval = uinput_validate_absbits(dev); - if (retval < 0) + int err = uinput_validate_absbits(dev); + if (err < 0) { + retval = err; kfree(dev->name); + } } exit: diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index a786419..c4909b4 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -15,4 +15,4 @@ obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o -psmouse-objs := psmouse-base.o alps.o logips2pp.o synaptics.o +psmouse-objs := psmouse-base.o alps.o logips2pp.o synaptics.o lifebook.o diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 7bf4be7..a12e981 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -30,10 +30,11 @@ #define ALPS_DUALPOINT 0x01 #define ALPS_WHEEL 0x02 -#define ALPS_FW_BK 0x04 +#define ALPS_FW_BK_1 0x04 #define ALPS_4BTN 0x08 #define ALPS_OLDPROTO 0x10 #define ALPS_PASS 0x20 +#define ALPS_FW_BK_2 0x40 static struct alps_model_info alps_model_data[] = { { { 0x33, 0x02, 0x0a }, 0x88, 0xf8, ALPS_OLDPROTO }, /* UMAX-530T */ @@ -43,11 +44,11 @@ static struct alps_model_info alps_model_data[] = { { { 0x63, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, { { 0x63, 0x02, 0x28 }, 0xf8, 0xf8, 0 }, { { 0x63, 0x02, 0x3c }, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */ - { { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK }, /* NEC Versa L320 */ + { { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ { { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 }, { { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS }, /* Dell Latitude D800 */ { { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, - { { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, + { { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ { { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ @@ -61,11 +62,11 @@ static struct alps_model_info alps_model_data[] = { /* * ALPS abolute Mode - new format - * - * byte 0: 1 ? ? ? 1 ? ? ? + * + * byte 0: 1 ? ? ? 1 ? ? ? * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 * byte 2: 0 x10 x9 x8 x7 ? fin ges - * byte 3: 0 y9 y8 y7 1 M R L + * byte 3: 0 y9 y8 y7 1 M R L * byte 4: 0 y6 y5 y4 y3 y2 y1 y0 * byte 5: 0 z6 z5 z4 z3 z2 z1 z0 * @@ -81,11 +82,12 @@ static void alps_process_packet(struct psmouse *psmouse, struct pt_regs *regs) struct input_dev *dev = &psmouse->dev; struct input_dev *dev2 = &priv->dev2; int x, y, z, ges, fin, left, right, middle; + int back = 0, forward = 0; input_regs(dev, regs); if ((packet[0] & 0xc8) == 0x08) { /* 3-byte PS/2 packet */ - input_report_key(dev2, BTN_LEFT, packet[0] & 1); + input_report_key(dev2, BTN_LEFT, packet[0] & 1); input_report_key(dev2, BTN_RIGHT, packet[0] & 2); input_report_key(dev2, BTN_MIDDLE, packet[0] & 4); input_report_rel(dev2, REL_X, @@ -112,6 +114,18 @@ static void alps_process_packet(struct psmouse *psmouse, struct pt_regs *regs) z = packet[5]; } + if (priv->i->flags & ALPS_FW_BK_1) { + back = packet[2] & 4; + forward = packet[0] & 0x10; + } + + if (priv->i->flags & ALPS_FW_BK_2) { + back = packet[3] & 4; + forward = packet[2] & 4; + if ((middle = forward && back)) + forward = back = 0; + } + ges = packet[2] & 1; fin = packet[2] & 2; @@ -155,13 +169,12 @@ static void alps_process_packet(struct psmouse *psmouse, struct pt_regs *regs) input_report_abs(dev, ABS_PRESSURE, z); input_report_key(dev, BTN_TOOL_FINGER, z > 0); - if (priv->i->flags & ALPS_WHEEL) input_report_rel(dev, REL_WHEEL, ((packet[0] >> 4) & 0x07) | ((packet[2] >> 2) & 0x08)); - if (priv->i->flags & ALPS_FW_BK) { - input_report_key(dev, BTN_FORWARD, packet[0] & 0x10); - input_report_key(dev, BTN_BACK, packet[2] & 0x04); + if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { + input_report_key(dev, BTN_FORWARD, forward); + input_report_key(dev, BTN_BACK, back); } input_sync(dev); @@ -257,7 +270,6 @@ static struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *vers static int alps_passthrough_mode(struct psmouse *psmouse, int enable) { struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param[3]; int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11; if (ps2_command(ps2dev, NULL, cmd) || @@ -267,7 +279,7 @@ static int alps_passthrough_mode(struct psmouse *psmouse, int enable) return -1; /* we may get 3 more bytes, just ignore them */ - ps2_command(ps2dev, param, 0x0300); + ps2_drain(ps2dev, 3, 100); return 0; } @@ -425,7 +437,7 @@ int alps_init(struct psmouse *psmouse) psmouse->dev.relbit[LONG(REL_WHEEL)] |= BIT(REL_WHEEL); } - if (priv->i->flags & ALPS_FW_BK) { + if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { psmouse->dev.keybit[LONG(BTN_FORWARD)] |= BIT(BTN_FORWARD); psmouse->dev.keybit[LONG(BTN_BACK)] |= BIT(BTN_BACK); } @@ -436,8 +448,8 @@ int alps_init(struct psmouse *psmouse) priv->dev2.id.bustype = BUS_I8042; priv->dev2.id.vendor = 0x0002; priv->dev2.id.product = PSMOUSE_ALPS; - priv->dev2.id.version = 0x0000; - + priv->dev2.id.version = 0x0000; + priv->dev2.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); priv->dev2.relbit[LONG(REL_X)] |= BIT(REL_X) | BIT(REL_Y); priv->dev2.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); @@ -461,17 +473,15 @@ init_fail: int alps_detect(struct psmouse *psmouse, int set_properties) { int version; - struct alps_model_info *model; + struct alps_model_info *model; if (!(model = alps_get_model(psmouse, &version))) return -1; if (set_properties) { psmouse->vendor = "ALPS"; - if (model->flags & ALPS_DUALPOINT) - psmouse->name = "DualPoint TouchPad"; - else - psmouse->name = "GlidePoint"; + psmouse->name = model->flags & ALPS_DUALPOINT ? + "DualPoint TouchPad" : "GlidePoint"; psmouse->model = version; } return 0; diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c index 7baa09c..e994849 100644 --- a/drivers/input/mouse/amimouse.c +++ b/drivers/input/mouse/amimouse.c @@ -33,7 +33,6 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Amiga mouse driver"); MODULE_LICENSE("GPL"); -static int amimouse_used = 0; static int amimouse_lastx, amimouse_lasty; static struct input_dev amimouse_dev; @@ -81,16 +80,12 @@ static int amimouse_open(struct input_dev *dev) { unsigned short joy0dat; - if (amimouse_used++) - return 0; - joy0dat = custom.joy0dat; amimouse_lastx = joy0dat & 0xff; amimouse_lasty = joy0dat >> 8; if (request_irq(IRQ_AMIGA_VERTB, amimouse_interrupt, 0, "amimouse", amimouse_interrupt)) { - amimouse_used--; printk(KERN_ERR "amimouse.c: Can't allocate irq %d\n", IRQ_AMIGA_VERTB); return -EBUSY; } @@ -100,8 +95,7 @@ static int amimouse_open(struct input_dev *dev) static void amimouse_close(struct input_dev *dev) { - if (!--amimouse_used) - free_irq(IRQ_AMIGA_VERTB, amimouse_interrupt); + free_irq(IRQ_AMIGA_VERTB, amimouse_interrupt); } static int __init amimouse_init(void) diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c index ca4e968..1f62c01 100644 --- a/drivers/input/mouse/inport.c +++ b/drivers/input/mouse/inport.c @@ -17,18 +17,18 @@ /* * 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 + * 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 - * + * * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic @@ -87,29 +87,23 @@ MODULE_PARM_DESC(irq, "IRQ number (5=default)"); __obsolete_setup("inport_irq="); -static int inport_used; - static irqreturn_t inport_interrupt(int irq, void *dev_id, struct pt_regs *regs); static int inport_open(struct input_dev *dev) { - if (!inport_used++) { - if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL)) - return -EBUSY; - outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); - outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); - } + if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL)) + return -EBUSY; + outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); + outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); return 0; } static void inport_close(struct input_dev *dev) { - if (!--inport_used) { - outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); - outb(INPORT_MODE_BASE, INPORT_DATA_PORT); - free_irq(inport_irq, NULL); - } + outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); + outb(INPORT_MODE_BASE, INPORT_DATA_PORT); + free_irq(inport_irq, NULL); } static struct input_dev inport_dev = { @@ -120,11 +114,11 @@ static struct input_dev inport_dev = { .close = inport_close, .name = INPORT_NAME, .phys = "isa023c/input0", - .id = { - .bustype = BUS_ISA, - .vendor = INPORT_VENDOR, - .product = 0x0001, - .version = 0x0100, + .id = { + .bustype = BUS_ISA, + .vendor = INPORT_VENDOR, + .product = 0x0001, + .version = 0x0100, }, }; diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c new file mode 100644 index 0000000..bd9df9b --- /dev/null +++ b/drivers/input/mouse/lifebook.c @@ -0,0 +1,134 @@ +/* + * Fujitsu B-series Lifebook PS/2 TouchScreen driver + * + * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz> + * Copyright (c) 2005 Kenan Esau <kenan.esau@conan.de> + * + * TouchScreen detection, absolute mode setting and packet layout is taken from + * Harald Hoyer's description of the device. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/libps2.h> +#include <linux/dmi.h> + +#include "psmouse.h" +#include "lifebook.h" + +static struct dmi_system_id lifebook_dmi_table[] = { + { + .ident = "Lifebook B", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK B Series"), + }, + }, + { } +}; + + +static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse, struct pt_regs *regs) +{ + unsigned char *packet = psmouse->packet; + struct input_dev *dev = &psmouse->dev; + + if (psmouse->pktcnt != 3) + return PSMOUSE_GOOD_DATA; + + input_regs(dev, regs); + + /* calculate X and Y */ + if ((packet[0] & 0x08) == 0x00) { + input_report_abs(dev, ABS_X, + (packet[1] | ((packet[0] & 0x30) << 4))); + input_report_abs(dev, ABS_Y, + 1024 - (packet[2] | ((packet[0] & 0xC0) << 2))); + } else { + input_report_rel(dev, REL_X, + ((packet[0] & 0x10) ? packet[1] - 256 : packet[1])); + input_report_rel(dev, REL_Y, + -(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2])); + } + + input_report_key(dev, BTN_LEFT, packet[0] & 0x01); + input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); + input_report_key(dev, BTN_TOUCH, packet[0] & 0x04); + + input_sync(dev); + + return PSMOUSE_FULL_PACKET; +} + +static int lifebook_absolute_mode(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param; + + if (psmouse_reset(psmouse)) + return -1; + + /* + Enable absolute output -- ps2_command fails always but if + you leave this call out the touchsreen will never send + absolute coordinates + */ + param = 0x07; + ps2_command(ps2dev, ¶m, PSMOUSE_CMD_SETRES); + + return 0; +} + +static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution) +{ + unsigned char params[] = { 0, 1, 2, 2, 3 }; + + if (resolution == 0 || resolution > 400) + resolution = 400; + + ps2_command(&psmouse->ps2dev, ¶ms[resolution / 100], PSMOUSE_CMD_SETRES); + psmouse->resolution = 50 << params[resolution / 100]; +} + +static void lifebook_disconnect(struct psmouse *psmouse) +{ + psmouse_reset(psmouse); +} + +int lifebook_detect(struct psmouse *psmouse, int set_properties) +{ + if (!dmi_check_system(lifebook_dmi_table)) + return -1; + + if (set_properties) { + psmouse->vendor = "Fujitsu"; + psmouse->name = "Lifebook TouchScreen"; + } + + return 0; +} + +int lifebook_init(struct psmouse *psmouse) +{ + if (lifebook_absolute_mode(psmouse)) + return -1; + + psmouse->dev.evbit[0] = BIT(EV_ABS) | BIT(EV_KEY) | BIT(EV_REL); + psmouse->dev.keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); + psmouse->dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); + input_set_abs_params(&psmouse->dev, ABS_X, 0, 1024, 0, 0); + input_set_abs_params(&psmouse->dev, ABS_Y, 0, 1024, 0, 0); + + psmouse->protocol_handler = lifebook_process_byte; + psmouse->set_resolution = lifebook_set_resolution; + psmouse->disconnect = lifebook_disconnect; + psmouse->reconnect = lifebook_absolute_mode; + psmouse->pktsize = 3; + + return 0; +} + diff --git a/drivers/input/mouse/lifebook.h b/drivers/input/mouse/lifebook.h new file mode 100644 index 0000000..be1c094 --- /dev/null +++ b/drivers/input/mouse/lifebook.h @@ -0,0 +1,17 @@ +/* + * Fujitsu B-series Lifebook PS/2 TouchScreen driver + * + * Copyright (c) 2005 Vojtech Pavlik + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#ifndef _LIFEBOOK_H +#define _LIFEBOOK_H + +int lifebook_detect(struct psmouse *psmouse, int set_properties); +int lifebook_init(struct psmouse *psmouse); + +#endif diff --git a/drivers/input/mouse/logibm.c b/drivers/input/mouse/logibm.c index 77eb83e..8b52431 100644 --- a/drivers/input/mouse/logibm.c +++ b/drivers/input/mouse/logibm.c @@ -18,18 +18,18 @@ /* * 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 + * 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 - * + * * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic @@ -77,16 +77,11 @@ MODULE_PARM_DESC(irq, "IRQ number (5=default)"); __obsolete_setup("logibm_irq="); -static int logibm_used = 0; - static irqreturn_t logibm_interrupt(int irq, void *dev_id, struct pt_regs *regs); static int logibm_open(struct input_dev *dev) { - if (logibm_used++) - return 0; if (request_irq(logibm_irq, logibm_interrupt, 0, "logibm", NULL)) { - logibm_used--; printk(KERN_ERR "logibm.c: Can't allocate irq %d\n", logibm_irq); return -EBUSY; } @@ -96,8 +91,6 @@ static int logibm_open(struct input_dev *dev) static void logibm_close(struct input_dev *dev) { - if (--logibm_used) - return; outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); free_irq(logibm_irq, NULL); } @@ -167,7 +160,7 @@ static int __init logibm_init(void) outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); input_register_device(&logibm_dev); - + printk(KERN_INFO "input: Logitech bus mouse at %#x irq %d\n", LOGIBM_BASE, logibm_irq); return 0; diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c index 12dc0ef..e90c60c 100644 --- a/drivers/input/mouse/maplemouse.c +++ b/drivers/input/mouse/maplemouse.c @@ -1,6 +1,6 @@ /* * $Id: maplemouse.c,v 1.2 2004/03/22 01:18:15 lethal Exp $ - * SEGA Dreamcast mouse driver + * SEGA Dreamcast mouse driver * Based on drivers/usb/usbmouse.c */ @@ -15,80 +15,51 @@ MODULE_AUTHOR("YAEGASHI Takeshi <t@keshi.org>"); MODULE_DESCRIPTION("SEGA Dreamcast mouse driver"); -struct dc_mouse { - struct input_dev dev; - int open; -}; - - static void dc_mouse_callback(struct mapleq *mq) { int buttons, relx, rely, relz; struct maple_device *mapledev = mq->dev; - struct dc_mouse *mouse = mapledev->private_data; - struct input_dev *dev = &mouse->dev; + struct input_dev *dev = mapledev->private_data; unsigned char *res = mq->recvbuf; buttons = ~res[8]; - relx=*(unsigned short *)(res+12)-512; - rely=*(unsigned short *)(res+14)-512; - relz=*(unsigned short *)(res+16)-512; + relx = *(unsigned short *)(res + 12) - 512; + rely = *(unsigned short *)(res + 14) - 512; + relz = *(unsigned short *)(res + 16) - 512; - input_report_key(dev, BTN_LEFT, buttons&4); - input_report_key(dev, BTN_MIDDLE, buttons&9); - input_report_key(dev, BTN_RIGHT, buttons&2); + input_report_key(dev, BTN_LEFT, buttons & 4); + input_report_key(dev, BTN_MIDDLE, buttons & 9); + input_report_key(dev, BTN_RIGHT, buttons & 2); input_report_rel(dev, REL_X, relx); input_report_rel(dev, REL_Y, rely); input_report_rel(dev, REL_WHEEL, relz); input_sync(dev); } - -static int dc_mouse_open(struct input_dev *dev) -{ - struct dc_mouse *mouse = dev->private; - mouse->open++; - return 0; -} - - -static void dc_mouse_close(struct input_dev *dev) -{ - struct dc_mouse *mouse = dev->private; - mouse->open--; -} - - static int dc_mouse_connect(struct maple_device *dev) { unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]); - struct dc_mouse *mouse; + struct input_dev *input_dev; - if (!(mouse = kmalloc(sizeof(struct dc_mouse), GFP_KERNEL))) + if (!(input_dev = kmalloc(sizeof(struct input_dev), GFP_KERNEL))) return -1; - memset(mouse, 0, sizeof(struct dc_mouse)); - - dev->private_data = mouse; - mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); - mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); - mouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL); + dev->private_data = input_dev; - init_input_dev(&mouse->dev); + memset(input_dev, 0, sizeof(struct dc_mouse)); + init_input_dev(input_dev); + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); + input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL); - mouse->dev.private = mouse; - mouse->dev.open = dc_mouse_open; - mouse->dev.close = dc_mouse_close; - mouse->dev.event = NULL; + input_dev->name = dev->product_name; + input_dev->id.bustype = BUS_MAPLE; - mouse->dev.name = dev->product_name; - mouse->dev.id.bustype = BUS_MAPLE; - - input_register_device(&mouse->dev); + input_register_device(input_dev); maple_getcond_callback(dev, dc_mouse_callback, 1, MAPLE_FUNC_MOUSE); - printk(KERN_INFO "input: mouse(0x%lx): %s\n", data, mouse->dev.name); + printk(KERN_INFO "input: mouse(0x%lx): %s\n", data, input_dev->name); return 0; } @@ -96,10 +67,10 @@ static int dc_mouse_connect(struct maple_device *dev) static void dc_mouse_disconnect(struct maple_device *dev) { - struct dc_mouse *mouse = dev->private_data; + struct input_dev *input_dev = dev->private_data; - input_unregister_device(&mouse->dev); - kfree(mouse); + input_unregister_device(input_dev); + kfree(input_dev); } diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c index 0c74918..93393d5 100644 --- a/drivers/input/mouse/pc110pad.c +++ b/drivers/input/mouse/pc110pad.c @@ -4,7 +4,7 @@ * Copyright (c) 2000-2001 Vojtech Pavlik * * Based on the work of: - * Alan Cox Robin O'Leary + * Alan Cox Robin O'Leary */ /* @@ -56,7 +56,6 @@ static int pc110pad_io = 0x15e0; static struct input_dev pc110pad_dev; static int pc110pad_data[3]; static int pc110pad_count; -static int pc110pad_used; static char *pc110pad_name = "IBM PC110 TouchPad"; static char *pc110pad_phys = "isa15e0/input0"; @@ -74,7 +73,7 @@ static irqreturn_t pc110pad_interrupt(int irq, void *ptr, struct pt_regs *regs) if (pc110pad_count < 3) return IRQ_HANDLED; - + input_regs(&pc110pad_dev, regs); input_report_key(&pc110pad_dev, BTN_TOUCH, pc110pad_data[0] & 0x01); @@ -90,15 +89,11 @@ static irqreturn_t pc110pad_interrupt(int irq, void *ptr, struct pt_regs *regs) static void pc110pad_close(struct input_dev *dev) { - if (!--pc110pad_used) - outb(PC110PAD_OFF, pc110pad_io + 2); + outb(PC110PAD_OFF, pc110pad_io + 2); } static int pc110pad_open(struct input_dev *dev) { - if (pc110pad_used++) - return 0; - pc110pad_interrupt(0,NULL,NULL); pc110pad_interrupt(0,NULL,NULL); pc110pad_interrupt(0,NULL,NULL); @@ -145,7 +140,7 @@ static int __init pc110pad_init(void) pc110pad_dev.absmax[ABS_X] = 0x1ff; pc110pad_dev.absmax[ABS_Y] = 0x0ff; - + pc110pad_dev.open = pc110pad_open; pc110pad_dev.close = pc110pad_close; @@ -156,17 +151,17 @@ static int __init pc110pad_init(void) pc110pad_dev.id.product = 0x0001; pc110pad_dev.id.version = 0x0100; - input_register_device(&pc110pad_dev); + input_register_device(&pc110pad_dev); printk(KERN_INFO "input: %s at %#x irq %d\n", pc110pad_name, pc110pad_io, pc110pad_irq); - + return 0; } - + static void __exit pc110pad_exit(void) { - input_unregister_device(&pc110pad_dev); + input_unregister_device(&pc110pad_dev); outb(PC110PAD_OFF, pc110pad_io + 2); diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 019034b..19785a6c 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -24,6 +24,7 @@ #include "synaptics.h" #include "logips2pp.h" #include "alps.h" +#include "lifebook.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -31,10 +32,9 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -static unsigned int psmouse_max_proto = -1U; +static unsigned int psmouse_max_proto = PSMOUSE_AUTO; static int psmouse_set_maxproto(const char *val, struct kernel_param *kp); static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp); -static char *psmouse_proto_abbrev[] = { NULL, "bare", NULL, NULL, NULL, "imps", "exps", NULL, NULL, NULL }; #define param_check_proto_abbrev(name, p) __param_check(name, p, unsigned int) #define param_set_proto_abbrev psmouse_set_maxproto #define param_get_proto_abbrev psmouse_get_maxproto @@ -57,6 +57,7 @@ static unsigned int psmouse_resetafter; module_param_named(resetafter, psmouse_resetafter, uint, 0644); MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never)."); +PSMOUSE_DEFINE_ATTR(protocol); PSMOUSE_DEFINE_ATTR(rate); PSMOUSE_DEFINE_ATTR(resolution); PSMOUSE_DEFINE_ATTR(resetafter); @@ -67,7 +68,23 @@ __obsolete_setup("psmouse_smartscroll="); __obsolete_setup("psmouse_resetafter="); __obsolete_setup("psmouse_rate="); -static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "ThinkPS/2", "GenPS/2", "ImPS/2", "ImExPS/2", "SynPS/2", "AlpsPS/2" }; +/* + * psmouse_sem protects all operations changing state of mouse + * (connecting, disconnecting, changing rate or resolution via + * sysfs). We could use a per-device semaphore but since there + * rarely more than one PS/2 mouse connected and since semaphore + * is taken in "slow" paths it is not worth it. + */ +static DECLARE_MUTEX(psmouse_sem); + +struct psmouse_protocol { + enum psmouse_type type; + char *name; + char *alias; + int maxproto; + int (*detect)(struct psmouse *, int); + int (*init)(struct psmouse *); +}; /* * psmouse_process_byte() analyzes the PS/2 data stream and reports @@ -407,12 +424,15 @@ static int thinking_detect(struct psmouse *psmouse, int set_properties) */ static int ps2bare_detect(struct psmouse *psmouse, int set_properties) { - if (!psmouse->vendor) psmouse->vendor = "Generic"; - if (!psmouse->name) psmouse->name = "Mouse"; + if (set_properties) { + if (!psmouse->vendor) psmouse->vendor = "Generic"; + if (!psmouse->name) psmouse->name = "Mouse"; + } return 0; } + /* * psmouse_extensions() probes for any extensions to the basic PS/2 protocol * the mouse may have. @@ -424,6 +444,17 @@ static int psmouse_extensions(struct psmouse *psmouse, int synaptics_hardware = 0; /* + * We always check for lifebook because it does not disturb mouse + * (it only checks DMI information). + */ + if (lifebook_detect(psmouse, set_properties) == 0) { + if (max_proto > PSMOUSE_IMEX) { + if (!set_properties || lifebook_init(psmouse) == 0) + return PSMOUSE_LIFEBOOK; + } + } + +/* * Try Kensington ThinkingMouse (we try first, because synaptics probe * upsets the thinkingmouse). */ @@ -506,6 +537,103 @@ static int psmouse_extensions(struct psmouse *psmouse, return PSMOUSE_PS2; } +static struct psmouse_protocol psmouse_protocols[] = { + { + .type = PSMOUSE_PS2, + .name = "PS/2", + .alias = "bare", + .maxproto = 1, + .detect = ps2bare_detect, + }, + { + .type = PSMOUSE_PS2PP, + .name = "PS2++", + .alias = "logitech", + .detect = ps2pp_init, + }, + { + .type = PSMOUSE_THINKPS, + .name = "ThinkPS/2", + .alias = "thinkps", + .detect = thinking_detect, + }, + { + .type = PSMOUSE_GENPS, + .name = "GenPS/2", + .alias = "genius", + .detect = genius_detect, + }, + { + .type = PSMOUSE_IMPS, + .name = "ImPS/2", + .alias = "imps", + .maxproto = 1, + .detect = intellimouse_detect, + }, + { + .type = PSMOUSE_IMEX, + .name = "ImExPS/2", + .alias = "exps", + .maxproto = 1, + .detect = im_explorer_detect, + }, + { + .type = PSMOUSE_SYNAPTICS, + .name = "SynPS/2", + .alias = "synaptics", + .detect = synaptics_detect, + .init = synaptics_init, + }, + { + .type = PSMOUSE_ALPS, + .name = "AlpsPS/2", + .alias = "alps", + .detect = alps_detect, + .init = alps_init, + }, + { + .type = PSMOUSE_LIFEBOOK, + .name = "LBPS/2", + .alias = "lifebook", + .init = lifebook_init, + }, + { + .type = PSMOUSE_AUTO, + .name = "auto", + .alias = "any", + .maxproto = 1, + }, +}; + +static struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) + if (psmouse_protocols[i].type == type) + return &psmouse_protocols[i]; + + WARN_ON(1); + return &psmouse_protocols[0]; +} + +static struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len) +{ + struct psmouse_protocol *p; + int i; + + for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) { + p = &psmouse_protocols[i]; + + if ((strlen(p->name) == len && !strncmp(p->name, name, len)) || + (strlen(p->alias) == len && !strncmp(p->alias, name, len))) + return &psmouse_protocols[i]; + } + + return NULL; +} + + /* * psmouse_probe() probes for a PS/2 mouse. */ @@ -653,30 +781,84 @@ static void psmouse_cleanup(struct serio *serio) static void psmouse_disconnect(struct serio *serio) { - struct psmouse *psmouse, *parent; + struct psmouse *psmouse, *parent = NULL; + psmouse = serio_get_drvdata(serio); + + device_remove_file(&serio->dev, &psmouse_attr_protocol); device_remove_file(&serio->dev, &psmouse_attr_rate); device_remove_file(&serio->dev, &psmouse_attr_resolution); device_remove_file(&serio->dev, &psmouse_attr_resetafter); - psmouse = serio_get_drvdata(serio); + down(&psmouse_sem); + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); - if (parent->pt_deactivate) - parent->pt_deactivate(parent); + psmouse_deactivate(parent); } if (psmouse->disconnect) psmouse->disconnect(psmouse); + if (parent && parent->pt_deactivate) + parent->pt_deactivate(parent); + psmouse_set_state(psmouse, PSMOUSE_IGNORE); input_unregister_device(&psmouse->dev); serio_close(serio); serio_set_drvdata(serio, NULL); kfree(psmouse); + + if (parent) + psmouse_activate(parent); + + up(&psmouse_sem); +} + +static int psmouse_switch_protocol(struct psmouse *psmouse, struct psmouse_protocol *proto) +{ + memset(&psmouse->dev, 0, sizeof(struct input_dev)); + + init_input_dev(&psmouse->dev); + + psmouse->dev.private = psmouse; + psmouse->dev.dev = &psmouse->ps2dev.serio->dev; + + psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); + psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); + + psmouse->set_rate = psmouse_set_rate; + psmouse->set_resolution = psmouse_set_resolution; + psmouse->protocol_handler = psmouse_process_byte; + psmouse->pktsize = 3; + + if (proto && (proto->detect || proto->init)) { + if (proto->detect && proto->detect(psmouse, 1) < 0) + return -1; + + if (proto->init && proto->init(psmouse) < 0) + return -1; + + psmouse->type = proto->type; + } + else + psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1); + + sprintf(psmouse->devname, "%s %s %s", + psmouse_protocol_by_type(psmouse->type)->name, psmouse->vendor, psmouse->name); + + psmouse->dev.name = psmouse->devname; + psmouse->dev.phys = psmouse->phys; + psmouse->dev.id.bustype = BUS_I8042; + psmouse->dev.id.vendor = 0x0002; + psmouse->dev.id.product = psmouse->type; + psmouse->dev.id.version = psmouse->model; + + return 0; } /* @@ -688,6 +870,8 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) struct psmouse *psmouse, *parent = NULL; int retval; + down(&psmouse_sem); + /* * If this is a pass-through port deactivate parent so the device * connected to this port can be successfully identified @@ -697,20 +881,14 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) psmouse_deactivate(parent); } - if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL))) { + if (!(psmouse = kcalloc(1, sizeof(struct psmouse), GFP_KERNEL))) { retval = -ENOMEM; goto out; } - memset(psmouse, 0, sizeof(struct psmouse)); - ps2_init(&psmouse->ps2dev, serio); sprintf(psmouse->phys, "%s/input0", serio->phys); - psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); - psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); - psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); - psmouse->dev.private = psmouse; - psmouse->dev.dev = &serio->dev; + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); serio_set_drvdata(serio, psmouse); @@ -734,25 +912,10 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) psmouse->resolution = psmouse_resolution; psmouse->resetafter = psmouse_resetafter; psmouse->smartscroll = psmouse_smartscroll; - psmouse->set_rate = psmouse_set_rate; - psmouse->set_resolution = psmouse_set_resolution; - psmouse->protocol_handler = psmouse_process_byte; - psmouse->pktsize = 3; - psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1); - - sprintf(psmouse->devname, "%s %s %s", - psmouse_protocols[psmouse->type], psmouse->vendor, psmouse->name); - - psmouse->dev.name = psmouse->devname; - psmouse->dev.phys = psmouse->phys; - psmouse->dev.id.bustype = BUS_I8042; - psmouse->dev.id.vendor = 0x0002; - psmouse->dev.id.product = psmouse->type; - psmouse->dev.id.version = psmouse->model; + psmouse_switch_protocol(psmouse, NULL); input_register_device(&psmouse->dev); - printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys); psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); @@ -762,6 +925,7 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) if (parent && parent->pt_activate) parent->pt_activate(parent); + device_create_file(&serio->dev, &psmouse_attr_protocol); device_create_file(&serio->dev, &psmouse_attr_rate); device_create_file(&serio->dev, &psmouse_attr_resolution); device_create_file(&serio->dev, &psmouse_attr_resetafter); @@ -771,10 +935,11 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) retval = 0; out: - /* If this is a pass-through port the parent awaits to be activated */ + /* If this is a pass-through port the parent needs to be re-activated */ if (parent) psmouse_activate(parent); + up(&psmouse_sem); return retval; } @@ -791,6 +956,8 @@ static int psmouse_reconnect(struct serio *serio) return -1; } + down(&psmouse_sem); + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); psmouse_deactivate(parent); @@ -823,6 +990,7 @@ out: if (parent) psmouse_activate(parent); + up(&psmouse_sem); return rc; } @@ -893,26 +1061,109 @@ ssize_t psmouse_attr_set_helper(struct device *dev, const char *buf, size_t coun if (serio->drv != &psmouse_drv) { retval = -ENODEV; - goto out; + goto out_unpin; + } + + retval = down_interruptible(&psmouse_sem); + if (retval) + goto out_unpin; + + if (psmouse->state == PSMOUSE_IGNORE) { + retval = -ENODEV; + goto out_up; } if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); psmouse_deactivate(parent); } + psmouse_deactivate(psmouse); retval = handler(psmouse, buf, count); - psmouse_activate(psmouse); + if (retval != -ENODEV) + psmouse_activate(psmouse); + if (parent) psmouse_activate(parent); -out: + out_up: + up(&psmouse_sem); + out_unpin: serio_unpin_driver(serio); return retval; } +static ssize_t psmouse_attr_show_protocol(struct psmouse *psmouse, char *buf) +{ + return sprintf(buf, "%s\n", psmouse_protocol_by_type(psmouse->type)->name); +} + +static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, const char *buf, size_t count) +{ + struct serio *serio = psmouse->ps2dev.serio; + struct psmouse *parent = NULL; + struct psmouse_protocol *proto; + int retry = 0; + + if (!(proto = psmouse_protocol_by_name(buf, count))) + return -EINVAL; + + if (psmouse->type == proto->type) + return count; + + while (serio->child) { + if (++retry > 3) { + printk(KERN_WARNING "psmouse: failed to destroy child port, protocol change aborted.\n"); + return -EIO; + } + + up(&psmouse_sem); + serio_unpin_driver(serio); + serio_unregister_child_port(serio); + serio_pin_driver_uninterruptible(serio); + down(&psmouse_sem); + + if (serio->drv != &psmouse_drv) + return -ENODEV; + + if (psmouse->type == proto->type) + return count; /* switched by other thread */ + } + + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = serio_get_drvdata(serio->parent); + if (parent->pt_deactivate) + parent->pt_deactivate(parent); + } + + if (psmouse->disconnect) + psmouse->disconnect(psmouse); + + psmouse_set_state(psmouse, PSMOUSE_IGNORE); + input_unregister_device(&psmouse->dev); + + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + + if (psmouse_switch_protocol(psmouse, proto) < 0) { + psmouse_reset(psmouse); + /* default to PSMOUSE_PS2 */ + psmouse_switch_protocol(psmouse, &psmouse_protocols[0]); + } + + psmouse_initialize(psmouse); + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + + input_register_device(&psmouse->dev); + printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys); + + if (parent && parent->pt_activate) + parent->pt_activate(parent); + + return count; +} + static ssize_t psmouse_attr_show_rate(struct psmouse *psmouse, char *buf) { return sprintf(buf, "%d\n", psmouse->rate); @@ -969,34 +1220,26 @@ static ssize_t psmouse_attr_set_resetafter(struct psmouse *psmouse, const char * static int psmouse_set_maxproto(const char *val, struct kernel_param *kp) { - int i; + struct psmouse_protocol *proto; if (!val) return -EINVAL; - if (!strncmp(val, "any", 3)) { - *((unsigned int *)kp->arg) = -1U; - return 0; - } + proto = psmouse_protocol_by_name(val, strlen(val)); - for (i = 0; i < ARRAY_SIZE(psmouse_proto_abbrev); i++) { - if (!psmouse_proto_abbrev[i]) - continue; + if (!proto || !proto->maxproto) + return -EINVAL; - if (!strncmp(val, psmouse_proto_abbrev[i], strlen(psmouse_proto_abbrev[i]))) { - *((unsigned int *)kp->arg) = i; - return 0; - } - } + *((unsigned int *)kp->arg) = proto->type; - return -EINVAL; \ + return 0; \ } static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp) { - return sprintf(buffer, "%s\n", - psmouse_max_proto < ARRAY_SIZE(psmouse_proto_abbrev) ? - psmouse_proto_abbrev[psmouse_max_proto] : "any"); + int type = *((unsigned int *)kp->arg); + + return sprintf(buffer, "%s\n", psmouse_protocol_by_type(type)->name); } static int __init psmouse_init(void) diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 79e17a0..86691cf 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -77,6 +77,8 @@ enum psmouse_type { PSMOUSE_IMEX, PSMOUSE_SYNAPTICS, PSMOUSE_ALPS, + PSMOUSE_LIFEBOOK, + PSMOUSE_AUTO /* This one should always be last */ }; int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command); @@ -99,7 +101,7 @@ static ssize_t psmouse_do_set_##_name(struct device *d, struct device_attribute { \ return psmouse_attr_set_helper(d, b, s, psmouse_attr_set_##_name); \ } \ -static struct device_attribute psmouse_attr_##_name = \ +static struct device_attribute psmouse_attr_##_name = \ __ATTR(_name, S_IWUSR | S_IRUGO, \ psmouse_do_show_##_name, psmouse_do_set_##_name); diff --git a/drivers/input/mouse/rpcmouse.c b/drivers/input/mouse/rpcmouse.c index 7280f68..8fe1212 100644 --- a/drivers/input/mouse/rpcmouse.c +++ b/drivers/input/mouse/rpcmouse.c @@ -59,7 +59,7 @@ static irqreturn_t rpcmouse_irq(int irq, void *dev_id, struct pt_regs *regs) b = (short) (__raw_readl(0xe0310000) ^ 0x70); dx = x - rpcmouse_lastx; - dy = y - rpcmouse_lasty; + dy = y - rpcmouse_lasty; rpcmouse_lastx = x; rpcmouse_lasty = y; diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c index b2cb101..f024be9 100644 --- a/drivers/input/mouse/vsxxxaa.c +++ b/drivers/input/mouse/vsxxxaa.c @@ -1,7 +1,7 @@ /* * Driver for DEC VSXXX-AA mouse (hockey-puck mouse, ball or two rollers) - * DEC VSXXX-GA mouse (rectangular mouse, with ball) - * DEC VSXXX-AB tablet (digitizer with hair cross or stylus) + * DEC VSXXX-GA mouse (rectangular mouse, with ball) + * DEC VSXXX-AB tablet (digitizer with hair cross or stylus) * * Copyright (C) 2003-2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de> * diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 062848a..c6194a9 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -220,6 +220,7 @@ static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_h struct mousedev_list *list; struct mousedev_motion *p; unsigned long flags; + int wake_readers = 0; list_for_each_entry(list, &mousedev->list, node) { spin_lock_irqsave(&list->packet_lock, flags); @@ -255,11 +256,14 @@ static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_h spin_unlock_irqrestore(&list->packet_lock, flags); - if (list->ready) + if (list->ready) { kill_fasync(&list->fasync, SIGIO, POLL_IN); + wake_readers = 1; + } } - wake_up_interruptible(&mousedev->wait); + if (wake_readers) + wake_up_interruptible(&mousedev->wait); } static void mousedev_touchpad_touch(struct mousedev *mousedev, int value) diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index c978657..d4c990f 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -29,6 +29,7 @@ MODULE_LICENSE("GPL"); EXPORT_SYMBOL(ps2_init); EXPORT_SYMBOL(ps2_sendbyte); +EXPORT_SYMBOL(ps2_drain); EXPORT_SYMBOL(ps2_command); EXPORT_SYMBOL(ps2_schedule_command); EXPORT_SYMBOL(ps2_handle_ack); @@ -45,11 +46,11 @@ struct ps2work { /* - * ps2_sendbyte() sends a byte to the mouse, and waits for acknowledge. - * It doesn't handle retransmission, though it could - because when there would - * be need for retransmissions, the mouse has to be replaced anyway. + * ps2_sendbyte() sends a byte to the device and waits for acknowledge. + * It doesn't handle retransmission, though it could - because if there + * is a need for retransmissions device has to be replaced anyway. * - * ps2_sendbyte() can only be called from a process context + * ps2_sendbyte() can only be called from a process context. */ int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) @@ -72,6 +73,91 @@ int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) } /* + * ps2_drain() waits for device to transmit requested number of bytes + * and discards them. + */ + +void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) +{ + if (maxbytes > sizeof(ps2dev->cmdbuf)) { + WARN_ON(1); + maxbytes = sizeof(ps2dev->cmdbuf); + } + + down(&ps2dev->cmd_sem); + + serio_pause_rx(ps2dev->serio); + ps2dev->flags = PS2_FLAG_CMD; + ps2dev->cmdcnt = maxbytes; + serio_continue_rx(ps2dev->serio); + + wait_event_timeout(ps2dev->wait, + !(ps2dev->flags & PS2_FLAG_CMD), + msecs_to_jiffies(timeout)); + up(&ps2dev->cmd_sem); +} + +/* + * ps2_is_keyboard_id() checks received ID byte against the list of + * known keyboard IDs. + */ + +static inline int ps2_is_keyboard_id(char id_byte) +{ + static char keyboard_ids[] = { + 0xab, /* Regular keyboards */ + 0xac, /* NCD Sun keyboard */ + 0x2b, /* Trust keyboard, translated */ + 0x5d, /* Trust keyboard */ + 0x60, /* NMB SGI keyboard, translated */ + 0x47, /* NMB SGI keyboard */ + }; + + return memchr(keyboard_ids, id_byte, sizeof(keyboard_ids)) != NULL; +} + +/* + * ps2_adjust_timeout() is called after receiving 1st byte of command + * response and tries to reduce remaining timeout to speed up command + * completion. + */ + +static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout) +{ + switch (command) { + case PS2_CMD_RESET_BAT: + /* + * Device has sent the first response byte after + * reset command, reset is thus done, so we can + * shorten the timeout. + * The next byte will come soon (keyboard) or not + * at all (mouse). + */ + if (timeout > msecs_to_jiffies(100)) + timeout = msecs_to_jiffies(100); + break; + + case PS2_CMD_GETID: + /* + * If device behind the port is not a keyboard there + * won't be 2nd byte of ID response. + */ + if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) { + serio_pause_rx(ps2dev->serio); + ps2dev->flags = ps2dev->cmdcnt = 0; + serio_continue_rx(ps2dev->serio); + timeout = 0; + } + break; + + default: + break; + } + + return timeout; +} + +/* * ps2_command() sends a command and its parameters to the mouse, * then waits for the response and puts it in the param array. * @@ -86,6 +172,11 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) int rc = -1; int i; + if (receive > sizeof(ps2dev->cmdbuf)) { + WARN_ON(1); + return -1; + } + down(&ps2dev->cmd_sem); serio_pause_rx(ps2dev->serio); @@ -101,10 +192,9 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) * ACKing the reset command, and so it can take a long * time before the ACK arrrives. */ - if (command & 0xff) - if (ps2_sendbyte(ps2dev, command & 0xff, - command == PS2_CMD_RESET_BAT ? 1000 : 200)) - goto out; + if (ps2_sendbyte(ps2dev, command & 0xff, + command == PS2_CMD_RESET_BAT ? 1000 : 200)) + goto out; for (i = 0; i < send; i++) if (ps2_sendbyte(ps2dev, param[i], 200)) @@ -120,33 +210,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) if (ps2dev->cmdcnt && timeout > 0) { - if (command == PS2_CMD_RESET_BAT && timeout > msecs_to_jiffies(100)) { - /* - * Device has sent the first response byte - * after a reset command, reset is thus done, - * shorten the timeout. The next byte will come - * soon (keyboard) or not at all (mouse). - */ - timeout = msecs_to_jiffies(100); - } - - if (command == PS2_CMD_GETID && - ps2dev->cmdbuf[receive - 1] != 0xab && /* Regular keyboards */ - ps2dev->cmdbuf[receive - 1] != 0xac && /* NCD Sun keyboard */ - ps2dev->cmdbuf[receive - 1] != 0x2b && /* Trust keyboard, translated */ - ps2dev->cmdbuf[receive - 1] != 0x5d && /* Trust keyboard */ - ps2dev->cmdbuf[receive - 1] != 0x60 && /* NMB SGI keyboard, translated */ - ps2dev->cmdbuf[receive - 1] != 0x47) { /* NMB SGI keyboard */ - /* - * Device behind the port is not a keyboard - * so we don't need to wait for the 2nd byte - * of ID response. - */ - serio_pause_rx(ps2dev->serio); - ps2dev->flags = ps2dev->cmdcnt = 0; - serio_continue_rx(ps2dev->serio); - } - + timeout = ps2_adjust_timeout(ps2dev, command, timeout); wait_event_timeout(ps2dev->wait, !(ps2dev->flags & PS2_FLAG_CMD), timeout); } @@ -160,7 +224,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) rc = 0; -out: + out: serio_pause_rx(ps2dev->serio); ps2dev->flags = 0; serio_continue_rx(ps2dev->serio); diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index 546ce59..3cdc9ca 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -226,7 +226,7 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv) input_set_abs_params(&elo->dev, ABS_Y, 96, 4000, 0, 0); input_set_abs_params(&elo->dev, ABS_PRESSURE, 0, 255, 0, 0); break; - + case 1: /* 6-byte protocol */ input_set_abs_params(&elo->dev, ABS_PRESSURE, 0, 15, 0, 0); diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c index acb9137..bcfa1e3 100644 --- a/drivers/input/touchscreen/h3600_ts_input.c +++ b/drivers/input/touchscreen/h3600_ts_input.c @@ -89,9 +89,9 @@ MODULE_LICENSE("GPL"); #define H3600_SCANCODE_Q 4 /* 4 -> Q button */ #define H3600_SCANCODE_START 5 /* 5 -> start menu */ #define H3600_SCANCODE_UP 6 /* 6 -> up */ -#define H3600_SCANCODE_RIGHT 7 /* 7 -> right */ -#define H3600_SCANCODE_LEFT 8 /* 8 -> left */ -#define H3600_SCANCODE_DOWN 9 /* 9 -> down */ +#define H3600_SCANCODE_RIGHT 7 /* 7 -> right */ +#define H3600_SCANCODE_LEFT 8 /* 8 -> left */ +#define H3600_SCANCODE_DOWN 9 /* 9 -> down */ static char *h3600_name = "H3600 TouchScreen"; @@ -113,7 +113,7 @@ struct h3600_dev { static irqreturn_t action_button_handler(int irq, void *dev_id, struct pt_regs *regs) { - int down = (GPLR & GPIO_BITSY_ACTION_BUTTON) ? 0 : 1; + int down = (GPLR & GPIO_BITSY_ACTION_BUTTON) ? 0 : 1; struct input_dev *dev = (struct input_dev *) dev_id; input_regs(dev, regs); @@ -125,7 +125,7 @@ static irqreturn_t action_button_handler(int irq, void *dev_id, struct pt_regs * static irqreturn_t npower_button_handler(int irq, void *dev_id, struct pt_regs *regs) { - int down = (GPLR & GPIO_BITSY_NPOWER_BUTTON) ? 0 : 1; + int down = (GPLR & GPIO_BITSY_NPOWER_BUTTON) ? 0 : 1; struct input_dev *dev = (struct input_dev *) dev_id; /* @@ -145,8 +145,8 @@ static irqreturn_t npower_button_handler(int irq, void *dev_id, struct pt_regs * static int flite_brightness = 25; enum flite_pwr { - FLITE_PWR_OFF = 0, - FLITE_PWR_ON = 1 + FLITE_PWR_OFF = 0, + FLITE_PWR_ON = 1 }; /* @@ -157,9 +157,9 @@ unsigned int h3600_flite_power(struct input_dev *dev, enum flite_pwr pwr) struct h3600_dev *ts = dev->private; /* Must be in this order */ - ts->serio->write(ts->serio, 1); + ts->serio->write(ts->serio, 1); ts->serio->write(ts->serio, pwr); - ts->serio->write(ts->serio, brightness); + ts->serio->write(ts->serio, brightness); return 0; } @@ -169,26 +169,26 @@ static int h3600ts_pm_callback(struct pm_dev *pm_dev, pm_request_t req, { struct input_dev *dev = (struct input_dev *) data; - switch (req) { - case PM_SUSPEND: /* enter D1-D3 */ - suspended = 1; - h3600_flite_power(dev, FLITE_PWR_OFF); - break; - case PM_BLANK: - if (!suspended) - h3600_flite_power(dev, FLITE_PWR_OFF); - break; - case PM_RESUME: /* enter D0 */ - /* same as unblank */ - case PM_UNBLANK: - if (suspended) { - //initSerial(); - suspended = 0; - } - h3600_flite_power(dev, FLITE_PWR_ON); - break; - } - return 0; + switch (req) { + case PM_SUSPEND: /* enter D1-D3 */ + suspended = 1; + h3600_flite_power(dev, FLITE_PWR_OFF); + break; + case PM_BLANK: + if (!suspended) + h3600_flite_power(dev, FLITE_PWR_OFF); + break; + case PM_RESUME: /* enter D0 */ + /* same as unblank */ + case PM_UNBLANK: + if (suspended) { + //initSerial(); + suspended = 0; + } + h3600_flite_power(dev, FLITE_PWR_ON); + break; + } + return 0; } #endif @@ -199,25 +199,25 @@ static int h3600ts_pm_callback(struct pm_dev *pm_dev, pm_request_t req, */ static void h3600ts_process_packet(struct h3600_dev *ts, struct pt_regs *regs) { - struct input_dev *dev = &ts->dev; + struct input_dev *dev = &ts->dev; static int touched = 0; int key, down = 0; input_regs(dev, regs); - switch (ts->event) { - /* - Buttons - returned as a single byte - 7 6 5 4 3 2 1 0 - S x x x N N N N + switch (ts->event) { + /* + Buttons - returned as a single byte + 7 6 5 4 3 2 1 0 + S x x x N N N N - S switch state ( 0=pressed 1=released) - x Unused. - NNNN switch number 0-15 + S switch state ( 0=pressed 1=released) + x Unused. + NNNN switch number 0-15 - Note: This is true for non interrupt generated key events. - */ - case KEYBD_ID: + Note: This is true for non interrupt generated key events. + */ + case KEYBD_ID: down = (ts->buf[0] & 0x80) ? 0 : 1; switch (ts->buf[0] & 0x7f) { @@ -229,40 +229,40 @@ static void h3600ts_process_packet(struct h3600_dev *ts, struct pt_regs *regs) break; case H3600_SCANCODE_CONTACTS: key = KEY_PROG2; - break; + break; case H3600_SCANCODE_Q: key = KEY_Q; - break; + break; case H3600_SCANCODE_START: key = KEY_PROG3; - break; + break; case H3600_SCANCODE_UP: key = KEY_UP; - break; + break; case H3600_SCANCODE_RIGHT: key = KEY_RIGHT; - break; + break; case H3600_SCANCODE_LEFT: key = KEY_LEFT; - break; + break; case H3600_SCANCODE_DOWN: key = KEY_DOWN; - break; + break; default: key = 0; } - if (key) - input_report_key(dev, key, down); - break; - /* - * Native touchscreen event data is formatted as shown below:- - * - * +-------+-------+-------+-------+ - * | Xmsb | Xlsb | Ymsb | Ylsb | - * +-------+-------+-------+-------+ - * byte 0 1 2 3 - */ - case TOUCHS_ID: + if (key) + input_report_key(dev, key, down); + break; + /* + * Native touchscreen event data is formatted as shown below:- + * + * +-------+-------+-------+-------+ + * | Xmsb | Xlsb | Ymsb | Ylsb | + * +-------+-------+-------+-------+ + * byte 0 1 2 3 + */ + case TOUCHS_ID: if (!touched) { input_report_key(dev, BTN_TOUCH, 1); touched = 1; @@ -272,19 +272,19 @@ static void h3600ts_process_packet(struct h3600_dev *ts, struct pt_regs *regs) unsigned short x, y; x = ts->buf[0]; x <<= 8; x += ts->buf[1]; - y = ts->buf[2]; y <<= 8; y += ts->buf[3]; + y = ts->buf[2]; y <<= 8; y += ts->buf[3]; - input_report_abs(dev, ABS_X, x); - input_report_abs(dev, ABS_Y, y); + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); } else { - input_report_key(dev, BTN_TOUCH, 0); + input_report_key(dev, BTN_TOUCH, 0); touched = 0; } - break; + break; default: /* Send a non input event elsewhere */ break; - } + } input_sync(dev); } @@ -293,7 +293,7 @@ static void h3600ts_process_packet(struct h3600_dev *ts, struct pt_regs *regs) * h3600ts_event() handles events from the input module. */ static int h3600ts_event(struct input_dev *dev, unsigned int type, - unsigned int code, int value) + unsigned int code, int value) { struct h3600_dev *ts = dev->private; @@ -332,41 +332,41 @@ static int state; static irqreturn_t h3600ts_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs) { - struct h3600_dev *ts = serio_get_drvdata(serio); + struct h3600_dev *ts = serio_get_drvdata(serio); /* - * We have a new frame coming in. - */ + * We have a new frame coming in. + */ switch (state) { case STATE_SOF: - if (data == CHAR_SOF) - state = STATE_ID; + if (data == CHAR_SOF) + state = STATE_ID; break; - case STATE_ID: + case STATE_ID: ts->event = (data & 0xf0) >> 4; ts->len = (data & 0xf); ts->idx = 0; if (ts->event >= MAX_ID) { state = STATE_SOF; - break; + break; } ts->chksum = data; - state = (ts->len > 0) ? STATE_DATA : STATE_EOF; + state = (ts->len > 0) ? STATE_DATA : STATE_EOF; break; case STATE_DATA: ts->chksum += data; ts->buf[ts->idx]= data; - if(++ts->idx == ts->len) - state = STATE_EOF; + if (++ts->idx == ts->len) + state = STATE_EOF; break; case STATE_EOF: - state = STATE_SOF; - if (data == CHAR_EOF || data == ts->chksum) + state = STATE_SOF; + if (data == CHAR_EOF || data == ts->chksum) h3600ts_process_packet(ts, regs); - break; - default: - printk("Error3\n"); - break; + break; + default: + printk("Error3\n"); + break; } return IRQ_HANDLED; @@ -390,10 +390,10 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv) init_input_dev(&ts->dev); /* Device specific stuff */ - set_GPIO_IRQ_edge(GPIO_BITSY_ACTION_BUTTON, GPIO_BOTH_EDGES); - set_GPIO_IRQ_edge(GPIO_BITSY_NPOWER_BUTTON, GPIO_RISING_EDGE); + set_GPIO_IRQ_edge(GPIO_BITSY_ACTION_BUTTON, GPIO_BOTH_EDGES); + set_GPIO_IRQ_edge(GPIO_BITSY_NPOWER_BUTTON, GPIO_RISING_EDGE); - if (request_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, action_button_handler, + if (request_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, action_button_handler, SA_SHIRQ | SA_INTERRUPT | SA_SAMPLE_RANDOM, "h3600_action", &ts->dev)) { printk(KERN_ERR "h3600ts.c: Could not allocate Action Button IRQ!\n"); @@ -401,7 +401,7 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv) return -EBUSY; } - if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler, + if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler, SA_SHIRQ | SA_INTERRUPT | SA_SAMPLE_RANDOM, "h3600_suspend", &ts->dev)) { free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, &ts->dev); @@ -433,7 +433,7 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv) sprintf(ts->phys, "%s/input0", serio->phys); - ts->dev.event = h3600ts_event; + ts->dev.event = h3600ts_event; ts->dev.private = ts; ts->dev.name = h3600_name; ts->dev.phys = ts->phys; @@ -446,8 +446,8 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv) err = serio_open(serio, drv); if (err) { - free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts); - free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts); + free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts); + free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts); serio_set_drvdata(serio, NULL); kfree(ts); return err; diff --git a/drivers/input/touchscreen/mk712.c b/drivers/input/touchscreen/mk712.c index 2d14a57..afaaebe 100644 --- a/drivers/input/touchscreen/mk712.c +++ b/drivers/input/touchscreen/mk712.c @@ -17,7 +17,7 @@ * found in Gateway AOL Connected Touchpad computers. * * Documentation for ICS MK712 can be found at: - * http://www.icst.com/pdf/mk712.pdf + * http://www.icst.com/pdf/mk712.pdf */ /* @@ -77,7 +77,6 @@ MODULE_PARM_DESC(irq, "IRQ of MK712 touchscreen controller"); #define MK712_READ_ONE_POINT 0x20 #define MK712_POWERUP 0x40 -static int mk712_used = 0; static struct input_dev mk712_dev; static DEFINE_SPINLOCK(mk712_lock); @@ -130,17 +129,14 @@ static int mk712_open(struct input_dev *dev) spin_lock_irqsave(&mk712_lock, flags); - if (!mk712_used++) { + outb(0, mk712_io + MK712_CONTROL); /* Reset */ - outb(0, mk712_io + MK712_CONTROL); /* Reset */ + outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE | + MK712_INT_ON_CHANGE_IN_TOUCH_STATUS | + MK712_ENABLE_PERIODIC_CONVERSIONS | + MK712_POWERUP, mk712_io + MK712_CONTROL); - outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE | - MK712_INT_ON_CHANGE_IN_TOUCH_STATUS | - MK712_ENABLE_PERIODIC_CONVERSIONS | - MK712_POWERUP, mk712_io + MK712_CONTROL); - - outb(10, mk712_io + MK712_RATE); /* 187 points per second */ - } + outb(10, mk712_io + MK712_RATE); /* 187 points per second */ spin_unlock_irqrestore(&mk712_lock, flags); @@ -153,8 +149,7 @@ static void mk712_close(struct input_dev *dev) spin_lock_irqsave(&mk712_lock, flags); - if (!--mk712_used) - outb(0, mk712_io + MK712_CONTROL); + outb(0, mk712_io + MK712_CONTROL); spin_unlock_irqrestore(&mk712_lock, flags); } diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index 8a7117a..91691a6 100644 --- a/drivers/macintosh/Kconfig +++ b/drivers/macintosh/Kconfig @@ -86,33 +86,18 @@ config PMAC_SMU on the "SMU" system control chip which replaces the old PMU. If you don't know, say Y. -config PMAC_PBOOK - bool "Power management support for PowerBooks" - depends on ADB_PMU - ---help--- - This provides support for putting a PowerBook to sleep; it also - enables media bay support. Power management works on the - PB2400/3400/3500, Wallstreet, Lombard, and Bronze PowerBook G3 and - the Titanium Powerbook G4, as well as the iBooks. You should get - the power management daemon, pmud, to make it work and you must have - the /dev/pmu device (see the pmud README). - - Get pmud from <ftp://ftp.samba.org/pub/ppclinux/pmud/>. - - If you have a PowerBook, you should say Y here. - - You may also want to compile the dma sound driver as a module and - have it autoloaded. The act of removing the module shuts down the - sound hardware for more power savings. - -config PM - bool - depends on PPC_PMAC && ADB_PMU && PMAC_PBOOK - default y - config PMAC_APM_EMU tristate "APM emulation" - depends on PMAC_PBOOK + depends on PPC_PMAC && PPC32 && PM + +config PMAC_MEDIABAY + bool "Support PowerBook hotswap media bay" + depends on PPC_PMAC && PPC32 + help + This option adds support for older PowerBook's hotswap media bay + that can contains batteries, floppy drives, or IDE devices. PCI + devices are not fully supported in the bay as I never had one to + try with # made a separate option since backlight may end up beeing used # on non-powerbook machines (but only on PMU based ones AFAIK) @@ -126,13 +111,6 @@ config PMAC_BACKLIGHT events; also, the PowerBook button device will be enabled so you can change the screen brightness. -config MAC_SERIAL - tristate "Support for PowerMac serial ports (OBSOLETE DRIVER)" - depends on PPC_PMAC && BROKEN - help - This driver is obsolete. Use CONFIG_SERIAL_PMACZILOG in - "Character devices --> Serial drivers --> PowerMac z85c30" option. - config ADB_MACIO bool "Include MacIO (CHRP) ADB driver" depends on ADB && PPC_CHRP && !PPC_PMAC64 diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile index c3a4705..f5ae171 100644 --- a/drivers/macintosh/Makefile +++ b/drivers/macintosh/Makefile @@ -6,8 +6,7 @@ obj-$(CONFIG_PPC_PMAC) += macio_asic.o -obj-$(CONFIG_PMAC_PBOOK) += mediabay.o -obj-$(CONFIG_MAC_SERIAL) += macserial.o +obj-$(CONFIG_PMAC_MEDIABAY) += mediabay.o obj-$(CONFIG_MAC_EMUMOUSEBTN) += mac_hid.o obj-$(CONFIG_INPUT_ADBHID) += adbhid.o obj-$(CONFIG_ANSLCD) += ans-lcd.o diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index 493e2af..c0dc1e3 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -90,7 +90,7 @@ static int sleepy_trackpad; static int autopoll_devs; int __adb_probe_sync; -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM static int adb_notify_sleep(struct pmu_sleep_notifier *self, int when); static struct pmu_sleep_notifier adb_sleep_notifier = { adb_notify_sleep, @@ -320,9 +320,9 @@ int __init adb_init(void) printk(KERN_WARNING "Warning: no ADB interface detected\n"); adb_controller = NULL; } else { -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM pmu_register_sleep_notifier(&adb_sleep_notifier); -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PM */ #ifdef CONFIG_PPC if (machine_is_compatible("AAPL,PowerBook1998") || machine_is_compatible("PowerBook1,1")) @@ -337,7 +337,7 @@ int __init adb_init(void) __initcall(adb_init); -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM /* * notify clients before sleep and reset bus afterwards */ @@ -378,7 +378,7 @@ adb_notify_sleep(struct pmu_sleep_notifier *self, int when) } return PBOOK_SLEEP_OK; } -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PM */ static int do_adb_reset_bus(void) diff --git a/drivers/macintosh/macserial.c b/drivers/macintosh/macserial.c deleted file mode 100644 index 0be3ac6..0000000 --- a/drivers/macintosh/macserial.c +++ /dev/null @@ -1,3036 +0,0 @@ -/* - * macserial.c: Serial port driver for Power Macintoshes. - * - * Derived from drivers/sbus/char/sunserial.c by Paul Mackerras. - * - * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au) - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - * - * Receive DMA code by Takashi Oe <toe@unlserve.unl.edu>. - * - * $Id: macserial.c,v 1.24.2.4 1999/10/19 04:36:42 paulus Exp $ - */ - -#include <linux/config.h> -#include <linux/errno.h> -#include <linux/module.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/workqueue.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/major.h> -#include <linux/string.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/init.h> -#ifdef CONFIG_SERIAL_CONSOLE -#include <linux/console.h> -#endif -#include <linux/slab.h> -#include <linux/bitops.h> - -#include <asm/sections.h> -#include <asm/io.h> -#include <asm/pgtable.h> -#include <asm/irq.h> -#include <asm/prom.h> -#include <asm/system.h> -#include <asm/segment.h> -#include <asm/machdep.h> -#include <asm/pmac_feature.h> -#include <linux/adb.h> -#include <linux/pmu.h> -#ifdef CONFIG_KGDB -#include <asm/kgdb.h> -#endif -#include <asm/dbdma.h> - -#include "macserial.h" - -#ifdef CONFIG_PMAC_PBOOK -static int serial_notify_sleep(struct pmu_sleep_notifier *self, int when); -static struct pmu_sleep_notifier serial_sleep_notifier = { - serial_notify_sleep, - SLEEP_LEVEL_MISC, -}; -#endif - -#define SUPPORT_SERIAL_DMA -#define MACSERIAL_VERSION "2.0" - -/* - * It would be nice to dynamically allocate everything that - * depends on NUM_SERIAL, so we could support any number of - * Z8530s, but for now... - */ -#define NUM_SERIAL 2 /* Max number of ZS chips supported */ -#define NUM_CHANNELS (NUM_SERIAL * 2) /* 2 channels per chip */ - -/* On PowerMacs, the hardware takes care of the SCC recovery time, - but we need the eieio to make sure that the accesses occur - in the order we want. */ -#define RECOVERY_DELAY eieio() - -static struct tty_driver *serial_driver; - -struct mac_zschannel zs_channels[NUM_CHANNELS]; - -struct mac_serial zs_soft[NUM_CHANNELS]; -int zs_channels_found; -struct mac_serial *zs_chain; /* list of all channels */ - -struct tty_struct zs_ttys[NUM_CHANNELS]; - -static int is_powerbook; - -#ifdef CONFIG_SERIAL_CONSOLE -static struct console sercons; -#endif - -#ifdef CONFIG_KGDB -struct mac_zschannel *zs_kgdbchan; -static unsigned char scc_inittab[] = { - 9, 0x80, /* reset A side (CHRA) */ - 13, 0, /* set baud rate divisor */ - 12, 1, - 14, 1, /* baud rate gen enable, src=rtxc (BRENABL) */ - 11, 0x50, /* clocks = br gen (RCBR | TCBR) */ - 5, 0x6a, /* tx 8 bits, assert RTS (Tx8 | TxENAB | RTS) */ - 4, 0x44, /* x16 clock, 1 stop (SB1 | X16CLK)*/ - 3, 0xc1, /* rx enable, 8 bits (RxENABLE | Rx8)*/ -}; -#endif -#define ZS_CLOCK 3686400 /* Z8530 RTxC input clock rate */ - -/* serial subtype definitions */ -#define SERIAL_TYPE_NORMAL 1 - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 - -/* - * Debugging. - */ -#undef SERIAL_DEBUG_INTR -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_FLOW -#undef SERIAL_DEBUG_POWER -#undef SERIAL_DEBUG_THROTTLE -#undef SERIAL_DEBUG_STOP -#undef SERIAL_DEBUG_BAUDS - -#define RS_STROBE_TIME 10 -#define RS_ISR_PASS_LIMIT 256 - -#define _INLINE_ inline - -#ifdef SERIAL_DEBUG_OPEN -#define OPNDBG(fmt, arg...) printk(KERN_DEBUG fmt , ## arg) -#else -#define OPNDBG(fmt, arg...) do { } while (0) -#endif -#ifdef SERIAL_DEBUG_POWER -#define PWRDBG(fmt, arg...) printk(KERN_DEBUG fmt , ## arg) -#else -#define PWRDBG(fmt, arg...) do { } while (0) -#endif -#ifdef SERIAL_DEBUG_BAUDS -#define BAUDBG(fmt, arg...) printk(fmt , ## arg) -#else -#define BAUDBG(fmt, arg...) do { } while (0) -#endif - -static void probe_sccs(void); -static void change_speed(struct mac_serial *info, struct termios *old); -static void rs_wait_until_sent(struct tty_struct *tty, int timeout); -static int set_scc_power(struct mac_serial * info, int state); -static int setup_scc(struct mac_serial * info); -static void dbdma_reset(volatile struct dbdma_regs *dma); -static void dbdma_flush(volatile struct dbdma_regs *dma); -static irqreturn_t rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs); -static irqreturn_t rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs); -static void dma_init(struct mac_serial * info); -static void rxdma_start(struct mac_serial * info, int curr); -static void rxdma_to_tty(struct mac_serial * info); - -/* - * tmp_buf is used as a temporary buffer by serial_write. We need to - * lock it in case the copy_from_user blocks while swapping in a page, - * and some other program tries to do a serial write at the same time. - * Since the lock will only come under contention when the system is - * swapping and available memory is low, it makes sense to share one - * buffer across all the serial ports, since it significantly saves - * memory if large numbers of serial ports are open. - */ -static unsigned char *tmp_buf; -static DECLARE_MUTEX(tmp_buf_sem); - - -static inline int __pmac -serial_paranoia_check(struct mac_serial *info, - char *name, const char *routine) -{ -#ifdef SERIAL_PARANOIA_CHECK - static const char badmagic[] = KERN_WARNING - "Warning: bad magic number for serial struct %s in %s\n"; - static const char badinfo[] = KERN_WARNING - "Warning: null mac_serial for %s in %s\n"; - - if (!info) { - printk(badinfo, name, routine); - return 1; - } - if (info->magic != SERIAL_MAGIC) { - printk(badmagic, name, routine); - return 1; - } -#endif - return 0; -} - -/* - * Reading and writing Z8530 registers. - */ -static inline unsigned char __pmac read_zsreg(struct mac_zschannel *channel, - unsigned char reg) -{ - unsigned char retval; - unsigned long flags; - - /* - * We have to make this atomic. - */ - spin_lock_irqsave(&channel->lock, flags); - if (reg != 0) { - *channel->control = reg; - RECOVERY_DELAY; - } - retval = *channel->control; - RECOVERY_DELAY; - spin_unlock_irqrestore(&channel->lock, flags); - return retval; -} - -static inline void __pmac write_zsreg(struct mac_zschannel *channel, - unsigned char reg, unsigned char value) -{ - unsigned long flags; - - spin_lock_irqsave(&channel->lock, flags); - if (reg != 0) { - *channel->control = reg; - RECOVERY_DELAY; - } - *channel->control = value; - RECOVERY_DELAY; - spin_unlock_irqrestore(&channel->lock, flags); - return; -} - -static inline unsigned char __pmac read_zsdata(struct mac_zschannel *channel) -{ - unsigned char retval; - - retval = *channel->data; - RECOVERY_DELAY; - return retval; -} - -static inline void write_zsdata(struct mac_zschannel *channel, - unsigned char value) -{ - *channel->data = value; - RECOVERY_DELAY; - return; -} - -static inline void load_zsregs(struct mac_zschannel *channel, - unsigned char *regs) -{ - ZS_CLEARERR(channel); - ZS_CLEARFIFO(channel); - /* Load 'em up */ - write_zsreg(channel, R4, regs[R4]); - write_zsreg(channel, R10, regs[R10]); - write_zsreg(channel, R3, regs[R3] & ~RxENABLE); - write_zsreg(channel, R5, regs[R5] & ~TxENAB); - write_zsreg(channel, R1, regs[R1]); - write_zsreg(channel, R9, regs[R9]); - write_zsreg(channel, R11, regs[R11]); - write_zsreg(channel, R12, regs[R12]); - write_zsreg(channel, R13, regs[R13]); - write_zsreg(channel, R14, regs[R14]); - write_zsreg(channel, R15, regs[R15]); - write_zsreg(channel, R3, regs[R3]); - write_zsreg(channel, R5, regs[R5]); - return; -} - -/* Sets or clears DTR/RTS on the requested line */ -static inline void zs_rtsdtr(struct mac_serial *ss, int set) -{ - if (set) - ss->curregs[5] |= (RTS | DTR); - else - ss->curregs[5] &= ~(RTS | DTR); - write_zsreg(ss->zs_channel, 5, ss->curregs[5]); - return; -} - -/* Utility routines for the Zilog */ -static inline int get_zsbaud(struct mac_serial *ss) -{ - struct mac_zschannel *channel = ss->zs_channel; - int brg; - - if ((ss->curregs[R11] & TCBR) == 0) { - /* higher rates don't use the baud rate generator */ - return (ss->curregs[R4] & X32CLK)? ZS_CLOCK/32: ZS_CLOCK/16; - } - /* The baud rate is split up between two 8-bit registers in - * what is termed 'BRG time constant' format in my docs for - * the chip, it is a function of the clk rate the chip is - * receiving which happens to be constant. - */ - brg = (read_zsreg(channel, 13) << 8); - brg |= read_zsreg(channel, 12); - return BRG_TO_BPS(brg, (ZS_CLOCK/(ss->clk_divisor))); -} - -/* On receive, this clears errors and the receiver interrupts */ -static inline void rs_recv_clear(struct mac_zschannel *zsc) -{ - write_zsreg(zsc, 0, ERR_RES); - write_zsreg(zsc, 0, RES_H_IUS); /* XXX this is unnecessary */ -} - -/* - * Reset a Descriptor-Based DMA channel. - */ -static void dbdma_reset(volatile struct dbdma_regs *dma) -{ - int i; - - out_le32(&dma->control, (WAKE|FLUSH|PAUSE|RUN) << 16); - - /* - * Yes this looks peculiar, but apparently it needs to be this - * way on some machines. (We need to make sure the DBDMA - * engine has actually got the write above and responded - * to it. - paulus) - */ - for (i = 200; i > 0; --i) - if (ld_le32(&dma->status) & RUN) - udelay(1); -} - -/* - * Tells a DBDMA channel to stop and write any buffered data - * it might have to memory. - */ -static _INLINE_ void dbdma_flush(volatile struct dbdma_regs *dma) -{ - int i = 0; - - out_le32(&dma->control, (FLUSH << 16) | FLUSH); - while (((in_le32(&dma->status) & FLUSH) != 0) && (i++ < 100)) - udelay(1); -} - -/* - * ---------------------------------------------------------------------- - * - * Here starts the interrupt handling routines. All of the following - * subroutines are declared as inline and are folded into - * rs_interrupt(). They were separated out for readability's sake. - * - * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 - * ----------------------------------------------------------------------- - */ - -/* - * This routine is used by the interrupt handler to schedule - * processing in the software interrupt portion of the driver. - */ -static _INLINE_ void rs_sched_event(struct mac_serial *info, - int event) -{ - info->event |= 1 << event; - schedule_work(&info->tqueue); -} - -/* Work out the flag value for a z8530 status value. */ -static _INLINE_ int stat_to_flag(int stat) -{ - int flag; - - if (stat & Rx_OVR) { - flag = TTY_OVERRUN; - } else if (stat & FRM_ERR) { - flag = TTY_FRAME; - } else if (stat & PAR_ERR) { - flag = TTY_PARITY; - } else - flag = 0; - return flag; -} - -static _INLINE_ void receive_chars(struct mac_serial *info, - struct pt_regs *regs) -{ - struct tty_struct *tty = info->tty; - unsigned char ch, stat, flag; - - while ((read_zsreg(info->zs_channel, 0) & Rx_CH_AV) != 0) { - - stat = read_zsreg(info->zs_channel, R1); - ch = read_zsdata(info->zs_channel); - -#ifdef CONFIG_KGDB - if (info->kgdb_channel) { - if (ch == 0x03 || ch == '$') - breakpoint(); - if (stat & (Rx_OVR|FRM_ERR|PAR_ERR)) - write_zsreg(info->zs_channel, 0, ERR_RES); - return; - } -#endif - if (!tty) - continue; - if (tty->flip.count >= TTY_FLIPBUF_SIZE) - tty_flip_buffer_push(tty); - - if (tty->flip.count >= TTY_FLIPBUF_SIZE) { - static int flip_buf_ovf; - if (++flip_buf_ovf <= 1) - printk(KERN_WARNING "FB. overflow: %d\n", - flip_buf_ovf); - break; - } - tty->flip.count++; - { - static int flip_max_cnt; - if (flip_max_cnt < tty->flip.count) - flip_max_cnt = tty->flip.count; - } - flag = stat_to_flag(stat); - if (flag) - /* reset the error indication */ - write_zsreg(info->zs_channel, 0, ERR_RES); - *tty->flip.flag_buf_ptr++ = flag; - *tty->flip.char_buf_ptr++ = ch; - } - if (tty) - tty_flip_buffer_push(tty); -} - -static void transmit_chars(struct mac_serial *info) -{ - if ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) == 0) - return; - info->tx_active = 0; - - if (info->x_char && !info->power_wait) { - /* Send next char */ - write_zsdata(info->zs_channel, info->x_char); - info->x_char = 0; - info->tx_active = 1; - return; - } - - if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tx_stopped - || info->power_wait) { - write_zsreg(info->zs_channel, 0, RES_Tx_P); - return; - } - - /* Send char */ - write_zsdata(info->zs_channel, info->xmit_buf[info->xmit_tail++]); - info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt--; - info->tx_active = 1; - - if (info->xmit_cnt < WAKEUP_CHARS) - rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); -} - -static void powerup_done(unsigned long data) -{ - struct mac_serial *info = (struct mac_serial *) data; - unsigned long flags; - - spin_lock_irqsave(&info->lock, flags); - info->power_wait = 0; - transmit_chars(info); - spin_unlock_irqrestore(&info->lock, flags); -} - -static _INLINE_ void status_handle(struct mac_serial *info) -{ - unsigned char status; - - /* Get status from Read Register 0 */ - status = read_zsreg(info->zs_channel, 0); - - /* Check for DCD transitions */ - if (((status ^ info->read_reg_zero) & DCD) != 0 - && info->tty && !C_CLOCAL(info->tty)) { - if (status & DCD) { - wake_up_interruptible(&info->open_wait); - } else { - if (info->tty) - tty_hangup(info->tty); - } - } - - /* Check for CTS transitions */ - if (info->tty && C_CRTSCTS(info->tty)) { - /* - * For some reason, on the Power Macintosh, - * it seems that the CTS bit is 1 when CTS is - * *negated* and 0 when it is asserted. - * The DCD bit doesn't seem to be inverted - * like this. - */ - if ((status & CTS) == 0) { - if (info->tx_stopped) { -#ifdef SERIAL_DEBUG_FLOW - printk(KERN_DEBUG "CTS up\n"); -#endif - info->tx_stopped = 0; - if (!info->tx_active) - transmit_chars(info); - } - } else { -#ifdef SERIAL_DEBUG_FLOW - printk(KERN_DEBUG "CTS down\n"); -#endif - info->tx_stopped = 1; - } - } - - /* Clear status condition... */ - write_zsreg(info->zs_channel, 0, RES_EXT_INT); - info->read_reg_zero = status; -} - -static _INLINE_ void receive_special_dma(struct mac_serial *info) -{ - unsigned char stat, flag; - volatile struct dbdma_regs *rd = &info->rx->dma; - int where = RX_BUF_SIZE; - - spin_lock(&info->rx_dma_lock); - if ((ld_le32(&rd->status) & ACTIVE) != 0) - dbdma_flush(rd); - if (in_le32(&rd->cmdptr) - == virt_to_bus(info->rx_cmds[info->rx_cbuf] + 1)) - where -= in_le16(&info->rx->res_count); - where--; - - stat = read_zsreg(info->zs_channel, R1); - - flag = stat_to_flag(stat); - if (flag) { - info->rx_flag_buf[info->rx_cbuf][where] = flag; - /* reset the error indication */ - write_zsreg(info->zs_channel, 0, ERR_RES); - } - - spin_unlock(&info->rx_dma_lock); -} - -/* - * This is the serial driver's generic interrupt routine - */ -static irqreturn_t rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) -{ - struct mac_serial *info = (struct mac_serial *) dev_id; - unsigned char zs_intreg; - int shift; - unsigned long flags; - int handled = 0; - - if (!(info->flags & ZILOG_INITIALIZED)) { - printk(KERN_WARNING "rs_interrupt: irq %d, port not " - "initialized\n", irq); - disable_irq(irq); - return IRQ_NONE; - } - - /* NOTE: The read register 3, which holds the irq status, - * does so for both channels on each chip. Although - * the status value itself must be read from the A - * channel and is only valid when read from channel A. - * Yes... broken hardware... - */ -#define CHAN_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT) - - if (info->zs_chan_a == info->zs_channel) - shift = 3; /* Channel A */ - else - shift = 0; /* Channel B */ - - spin_lock_irqsave(&info->lock, flags); - for (;;) { - zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift; -#ifdef SERIAL_DEBUG_INTR - printk(KERN_DEBUG "rs_interrupt: irq %d, zs_intreg 0x%x\n", - irq, (int)zs_intreg); -#endif - - if ((zs_intreg & CHAN_IRQMASK) == 0) - break; - handled = 1; - - if (zs_intreg & CHBRxIP) { - /* If we are doing DMA, we only ask for interrupts - on characters with errors or special conditions. */ - if (info->dma_initted) - receive_special_dma(info); - else - receive_chars(info, regs); - } - if (zs_intreg & CHBTxIP) - transmit_chars(info); - if (zs_intreg & CHBEXT) - status_handle(info); - } - spin_unlock_irqrestore(&info->lock, flags); - return IRQ_RETVAL(handled); -} - -/* Transmit DMA interrupt - not used at present */ -static irqreturn_t rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - return IRQ_HANDLED; -} - -/* - * Receive DMA interrupt. - */ -static irqreturn_t rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - struct mac_serial *info = (struct mac_serial *) dev_id; - volatile struct dbdma_cmd *cd; - - if (!info->dma_initted) - return IRQ_NONE; - spin_lock(&info->rx_dma_lock); - /* First, confirm that this interrupt is, indeed, coming */ - /* from Rx DMA */ - cd = info->rx_cmds[info->rx_cbuf] + 2; - if ((in_le16(&cd->xfer_status) & (RUN | ACTIVE)) != (RUN | ACTIVE)) { - spin_unlock(&info->rx_dma_lock); - return IRQ_NONE; - } - if (info->rx_fbuf != RX_NO_FBUF) { - info->rx_cbuf = info->rx_fbuf; - if (++info->rx_fbuf == info->rx_nbuf) - info->rx_fbuf = 0; - if (info->rx_fbuf == info->rx_ubuf) - info->rx_fbuf = RX_NO_FBUF; - } - spin_unlock(&info->rx_dma_lock); - return IRQ_HANDLED; -} - -/* - * ------------------------------------------------------------------- - * Here ends the serial interrupt routines. - * ------------------------------------------------------------------- - */ - -/* - * ------------------------------------------------------------ - * rs_stop() and rs_start() - * - * This routines are called before setting or resetting tty->stopped. - * ------------------------------------------------------------ - */ -static void rs_stop(struct tty_struct *tty) -{ - struct mac_serial *info = (struct mac_serial *)tty->driver_data; - -#ifdef SERIAL_DEBUG_STOP - printk(KERN_DEBUG "rs_stop %ld....\n", - tty->ldisc.chars_in_buffer(tty)); -#endif - - if (serial_paranoia_check(info, tty->name, "rs_stop")) - return; - -#if 0 - spin_lock_irqsave(&info->lock, flags); - if (info->curregs[5] & TxENAB) { - info->curregs[5] &= ~TxENAB; - info->pendregs[5] &= ~TxENAB; - write_zsreg(info->zs_channel, 5, info->curregs[5]); - } - spin_unlock_irqrestore(&info->lock, flags); -#endif -} - -static void rs_start(struct tty_struct *tty) -{ - struct mac_serial *info = (struct mac_serial *)tty->driver_data; - unsigned long flags; - -#ifdef SERIAL_DEBUG_STOP - printk(KERN_DEBUG "rs_start %ld....\n", - tty->ldisc.chars_in_buffer(tty)); -#endif - - if (serial_paranoia_check(info, tty->name, "rs_start")) - return; - - spin_lock_irqsave(&info->lock, flags); -#if 0 - if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) { - info->curregs[5] |= TxENAB; - info->pendregs[5] = info->curregs[5]; - write_zsreg(info->zs_channel, 5, info->curregs[5]); - } -#else - if (info->xmit_cnt && info->xmit_buf && !info->tx_active) { - transmit_chars(info); - } -#endif - spin_unlock_irqrestore(&info->lock, flags); -} - -static void do_softint(void *private_) -{ - struct mac_serial *info = (struct mac_serial *) private_; - struct tty_struct *tty; - - tty = info->tty; - if (!tty) - return; - - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) - tty_wakeup(tty); -} - -static int startup(struct mac_serial * info) -{ - int delay; - - OPNDBG("startup() (ttyS%d, irq %d)\n", info->line, info->irq); - - if (info->flags & ZILOG_INITIALIZED) { - OPNDBG(" -> already inited\n"); - return 0; - } - - if (!info->xmit_buf) { - info->xmit_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL); - if (!info->xmit_buf) - return -ENOMEM; - } - - OPNDBG("starting up ttyS%d (irq %d)...\n", info->line, info->irq); - - delay = set_scc_power(info, 1); - - setup_scc(info); - - if (delay) { - unsigned long flags; - - /* delay is in ms */ - spin_lock_irqsave(&info->lock, flags); - info->power_wait = 1; - mod_timer(&info->powerup_timer, - jiffies + (delay * HZ + 999) / 1000); - spin_unlock_irqrestore(&info->lock, flags); - } - - OPNDBG("enabling IRQ on ttyS%d (irq %d)...\n", info->line, info->irq); - - info->flags |= ZILOG_INITIALIZED; - enable_irq(info->irq); - if (info->dma_initted) { - enable_irq(info->rx_dma_irq); - } - - return 0; -} - -static _INLINE_ void rxdma_start(struct mac_serial * info, int curr) -{ - volatile struct dbdma_regs *rd = &info->rx->dma; - volatile struct dbdma_cmd *cd = info->rx_cmds[curr]; - -//printk(KERN_DEBUG "SCC: rxdma_start\n"); - - st_le32(&rd->cmdptr, virt_to_bus(cd)); - out_le32(&rd->control, (RUN << 16) | RUN); -} - -static void rxdma_to_tty(struct mac_serial *info) -{ - struct tty_struct *tty = info->tty; - volatile struct dbdma_regs *rd = &info->rx->dma; - unsigned long flags; - int residue, available, space, do_queue; - - if (!tty) - return; - - do_queue = 0; - spin_lock_irqsave(&info->rx_dma_lock, flags); -more: - space = TTY_FLIPBUF_SIZE - tty->flip.count; - if (!space) { - do_queue++; - goto out; - } - residue = 0; - if (info->rx_ubuf == info->rx_cbuf) { - if ((ld_le32(&rd->status) & ACTIVE) != 0) { - dbdma_flush(rd); - if (in_le32(&rd->cmdptr) - == virt_to_bus(info->rx_cmds[info->rx_cbuf]+1)) - residue = in_le16(&info->rx->res_count); - } - } - available = RX_BUF_SIZE - residue - info->rx_done_bytes; - if (available > space) - available = space; - if (available) { - memcpy(tty->flip.char_buf_ptr, - info->rx_char_buf[info->rx_ubuf] + info->rx_done_bytes, - available); - memcpy(tty->flip.flag_buf_ptr, - info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes, - available); - tty->flip.char_buf_ptr += available; - tty->flip.count += available; - tty->flip.flag_buf_ptr += available; - memset(info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes, - 0, available); - info->rx_done_bytes += available; - do_queue++; - } - if (info->rx_done_bytes == RX_BUF_SIZE) { - volatile struct dbdma_cmd *cd = info->rx_cmds[info->rx_ubuf]; - - if (info->rx_ubuf == info->rx_cbuf) - goto out; - /* mark rx_char_buf[rx_ubuf] free */ - st_le16(&cd->command, DBDMA_NOP); - cd++; - st_le32(&cd->cmd_dep, 0); - st_le32((unsigned int *)&cd->res_count, 0); - cd++; - st_le16(&cd->xfer_status, 0); - - if (info->rx_fbuf == RX_NO_FBUF) { - info->rx_fbuf = info->rx_ubuf; - if (!(ld_le32(&rd->status) & ACTIVE)) { - dbdma_reset(&info->rx->dma); - rxdma_start(info, info->rx_ubuf); - info->rx_cbuf = info->rx_ubuf; - } - } - info->rx_done_bytes = 0; - if (++info->rx_ubuf == info->rx_nbuf) - info->rx_ubuf = 0; - if (info->rx_fbuf == info->rx_ubuf) - info->rx_fbuf = RX_NO_FBUF; - goto more; - } -out: - spin_unlock_irqrestore(&info->rx_dma_lock, flags); - if (do_queue) - tty_flip_buffer_push(tty); -} - -static void poll_rxdma(unsigned long private_) -{ - struct mac_serial *info = (struct mac_serial *) private_; - unsigned long flags; - - rxdma_to_tty(info); - spin_lock_irqsave(&info->rx_dma_lock, flags); - mod_timer(&info->poll_dma_timer, RX_DMA_TIMER); - spin_unlock_irqrestore(&info->rx_dma_lock, flags); -} - -static void dma_init(struct mac_serial * info) -{ - int i, size; - volatile struct dbdma_cmd *cd; - unsigned char *p; - - info->rx_nbuf = 8; - - /* various mem set up */ - size = sizeof(struct dbdma_cmd) * (3 * info->rx_nbuf + 2) - + (RX_BUF_SIZE * 2 + sizeof(*info->rx_cmds) - + sizeof(*info->rx_char_buf) + sizeof(*info->rx_flag_buf)) - * info->rx_nbuf; - info->dma_priv = kmalloc(size, GFP_KERNEL | GFP_DMA); - if (info->dma_priv == NULL) - return; - memset(info->dma_priv, 0, size); - - info->rx_cmds = (volatile struct dbdma_cmd **)info->dma_priv; - info->rx_char_buf = (unsigned char **) (info->rx_cmds + info->rx_nbuf); - info->rx_flag_buf = info->rx_char_buf + info->rx_nbuf; - p = (unsigned char *) (info->rx_flag_buf + info->rx_nbuf); - for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE) - info->rx_char_buf[i] = p; - for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE) - info->rx_flag_buf[i] = p; - - /* a bit of DMA programming */ - cd = info->rx_cmds[0] = (volatile struct dbdma_cmd *) DBDMA_ALIGN(p); - st_le16(&cd->command, DBDMA_NOP); - cd++; - st_le16(&cd->req_count, RX_BUF_SIZE); - st_le16(&cd->command, INPUT_MORE); - st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[0])); - cd++; - st_le16(&cd->req_count, 4); - st_le16(&cd->command, STORE_WORD | INTR_ALWAYS); - st_le32(&cd->phy_addr, virt_to_bus(cd-2)); - st_le32(&cd->cmd_dep, DBDMA_STOP); - for (i = 1; i < info->rx_nbuf; i++) { - info->rx_cmds[i] = ++cd; - st_le16(&cd->command, DBDMA_NOP); - cd++; - st_le16(&cd->req_count, RX_BUF_SIZE); - st_le16(&cd->command, INPUT_MORE); - st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[i])); - cd++; - st_le16(&cd->req_count, 4); - st_le16(&cd->command, STORE_WORD | INTR_ALWAYS); - st_le32(&cd->phy_addr, virt_to_bus(cd-2)); - st_le32(&cd->cmd_dep, DBDMA_STOP); - } - cd++; - st_le16(&cd->command, DBDMA_NOP | BR_ALWAYS); - st_le32(&cd->cmd_dep, virt_to_bus(info->rx_cmds[0])); - - /* setup DMA to our liking */ - dbdma_reset(&info->rx->dma); - st_le32(&info->rx->dma.intr_sel, 0x10001); - st_le32(&info->rx->dma.br_sel, 0x10001); - out_le32(&info->rx->dma.wait_sel, 0x10001); - - /* set various flags */ - info->rx_ubuf = 0; - info->rx_cbuf = 0; - info->rx_fbuf = info->rx_ubuf + 1; - if (info->rx_fbuf == info->rx_nbuf) - info->rx_fbuf = RX_NO_FBUF; - info->rx_done_bytes = 0; - - /* setup polling */ - init_timer(&info->poll_dma_timer); - info->poll_dma_timer.function = (void *)&poll_rxdma; - info->poll_dma_timer.data = (unsigned long)info; - - info->dma_initted = 1; -} - -/* - * FixZeroBug....Works around a bug in the SCC receving channel. - * Taken from Darwin code, 15 Sept. 2000 -DanM - * - * The following sequence prevents a problem that is seen with O'Hare ASICs - * (most versions -- also with some Heathrow and Hydra ASICs) where a zero - * at the input to the receiver becomes 'stuck' and locks up the receiver. - * This problem can occur as a result of a zero bit at the receiver input - * coincident with any of the following events: - * - * The SCC is initialized (hardware or software). - * A framing error is detected. - * The clocking option changes from synchronous or X1 asynchronous - * clocking to X16, X32, or X64 asynchronous clocking. - * The decoding mode is changed among NRZ, NRZI, FM0, or FM1. - * - * This workaround attempts to recover from the lockup condition by placing - * the SCC in synchronous loopback mode with a fast clock before programming - * any of the asynchronous modes. - */ -static void fix_zero_bug_scc(struct mac_serial * info) -{ - write_zsreg(info->zs_channel, 9, - (info->zs_channel == info->zs_chan_a? CHRA: CHRB)); - udelay(10); - write_zsreg(info->zs_channel, 9, - ((info->zs_channel == info->zs_chan_a? CHRA: CHRB) | NV)); - - write_zsreg(info->zs_channel, 4, (X1CLK | EXTSYNC)); - - /* I think this is wrong....but, I just copying code.... - */ - write_zsreg(info->zs_channel, 3, (8 & ~RxENABLE)); - - write_zsreg(info->zs_channel, 5, (8 & ~TxENAB)); - write_zsreg(info->zs_channel, 9, NV); /* Didn't we already do this? */ - write_zsreg(info->zs_channel, 11, (RCBR | TCBR)); - write_zsreg(info->zs_channel, 12, 0); - write_zsreg(info->zs_channel, 13, 0); - write_zsreg(info->zs_channel, 14, (LOOPBAK | SSBR)); - write_zsreg(info->zs_channel, 14, (LOOPBAK | SSBR | BRENABL)); - write_zsreg(info->zs_channel, 3, (8 | RxENABLE)); - write_zsreg(info->zs_channel, 0, RES_EXT_INT); - write_zsreg(info->zs_channel, 0, RES_EXT_INT); /* to kill some time */ - - /* The channel should be OK now, but it is probably receiving - * loopback garbage. - * Switch to asynchronous mode, disable the receiver, - * and discard everything in the receive buffer. - */ - write_zsreg(info->zs_channel, 9, NV); - write_zsreg(info->zs_channel, 4, PAR_ENA); - write_zsreg(info->zs_channel, 3, (8 & ~RxENABLE)); - - while (read_zsreg(info->zs_channel, 0) & Rx_CH_AV) { - (void)read_zsreg(info->zs_channel, 8); - write_zsreg(info->zs_channel, 0, RES_EXT_INT); - write_zsreg(info->zs_channel, 0, ERR_RES); - } -} - -static int setup_scc(struct mac_serial * info) -{ - unsigned long flags; - - OPNDBG("setting up ttyS%d SCC...\n", info->line); - - spin_lock_irqsave(&info->lock, flags); - - /* Nice buggy HW ... */ - fix_zero_bug_scc(info); - - /* - * Reset the chip. - */ - write_zsreg(info->zs_channel, 9, - (info->zs_channel == info->zs_chan_a? CHRA: CHRB)); - udelay(10); - write_zsreg(info->zs_channel, 9, 0); - - /* - * Clear the receive FIFO. - */ - ZS_CLEARFIFO(info->zs_channel); - info->xmit_fifo_size = 1; - - /* - * Reset DMAs - */ - if (info->has_dma) - dma_init(info); - - /* - * Clear the interrupt registers. - */ - write_zsreg(info->zs_channel, 0, ERR_RES); - write_zsreg(info->zs_channel, 0, RES_H_IUS); - - /* - * Turn on RTS and DTR. - */ - if (!info->is_irda) - zs_rtsdtr(info, 1); - - /* - * Finally, enable sequencing and interrupts - */ - if (!info->dma_initted) { - /* interrupt on ext/status changes, all received chars, - transmit ready */ - info->curregs[1] = (info->curregs[1] & ~0x18) - | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB); - } else { - /* interrupt on ext/status changes, W/Req pin is - receive DMA request */ - info->curregs[1] = (info->curregs[1] & ~(0x18 | TxINT_ENAB)) - | (EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN); - write_zsreg(info->zs_channel, 1, info->curregs[1]); - /* enable W/Req pin */ - info->curregs[1] |= WT_RDY_ENAB; - write_zsreg(info->zs_channel, 1, info->curregs[1]); - /* enable interrupts on transmit ready and receive errors */ - info->curregs[1] |= INT_ERR_Rx | TxINT_ENAB; - } - info->pendregs[1] = info->curregs[1]; - info->curregs[3] |= (RxENABLE | Rx8); - info->pendregs[3] = info->curregs[3]; - info->curregs[5] |= (TxENAB | Tx8); - info->pendregs[5] = info->curregs[5]; - info->curregs[9] |= (NV | MIE); - info->pendregs[9] = info->curregs[9]; - write_zsreg(info->zs_channel, 3, info->curregs[3]); - write_zsreg(info->zs_channel, 5, info->curregs[5]); - write_zsreg(info->zs_channel, 9, info->curregs[9]); - - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - spin_unlock_irqrestore(&info->lock, flags); - - /* - * Set the speed of the serial port - */ - change_speed(info, 0); - - /* Save the current value of RR0 */ - info->read_reg_zero = read_zsreg(info->zs_channel, 0); - - if (info->dma_initted) { - spin_lock_irqsave(&info->rx_dma_lock, flags); - rxdma_start(info, 0); - info->poll_dma_timer.expires = RX_DMA_TIMER; - add_timer(&info->poll_dma_timer); - spin_unlock_irqrestore(&info->rx_dma_lock, flags); - } - - return 0; -} - -/* - * This routine will shutdown a serial port; interrupts are disabled, and - * DTR is dropped if the hangup on close termio flag is on. - */ -static void shutdown(struct mac_serial * info) -{ - OPNDBG("Shutting down serial port %d (irq %d)....\n", info->line, - info->irq); - - if (!(info->flags & ZILOG_INITIALIZED)) { - OPNDBG("(already shutdown)\n"); - return; - } - - if (info->has_dma) { - del_timer(&info->poll_dma_timer); - dbdma_reset(info->tx_dma); - dbdma_reset(&info->rx->dma); - disable_irq(info->tx_dma_irq); - disable_irq(info->rx_dma_irq); - } - disable_irq(info->irq); - - info->pendregs[1] = info->curregs[1] = 0; - write_zsreg(info->zs_channel, 1, 0); /* no interrupts */ - - info->curregs[3] &= ~RxENABLE; - info->pendregs[3] = info->curregs[3]; - write_zsreg(info->zs_channel, 3, info->curregs[3]); - - info->curregs[5] &= ~TxENAB; - if (!info->tty || C_HUPCL(info->tty)) - info->curregs[5] &= ~DTR; - info->pendregs[5] = info->curregs[5]; - write_zsreg(info->zs_channel, 5, info->curregs[5]); - - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - - set_scc_power(info, 0); - - if (info->xmit_buf) { - free_page((unsigned long) info->xmit_buf); - info->xmit_buf = 0; - } - - if (info->has_dma && info->dma_priv) { - kfree(info->dma_priv); - info->dma_priv = NULL; - info->dma_initted = 0; - } - - memset(info->curregs, 0, sizeof(info->curregs)); - memset(info->pendregs, 0, sizeof(info->pendregs)); - - info->flags &= ~ZILOG_INITIALIZED; -} - -/* - * Turn power on or off to the SCC and associated stuff - * (port drivers, modem, IR port, etc.) - * Returns the number of milliseconds we should wait before - * trying to use the port. - */ -static int set_scc_power(struct mac_serial * info, int state) -{ - int delay = 0; - - if (state) { - PWRDBG("ttyS%d: powering up hardware\n", info->line); - pmac_call_feature( - PMAC_FTR_SCC_ENABLE, - info->dev_node, info->port_type, 1); - if (info->is_internal_modem) { - pmac_call_feature( - PMAC_FTR_MODEM_ENABLE, - info->dev_node, 0, 1); - delay = 2500; /* wait for 2.5s before using */ - } else if (info->is_irda) - mdelay(50); /* Do better here once the problems - * with blocking have been ironed out - */ - } else { - /* TODO: Make that depend on a timer, don't power down - * immediately - */ - PWRDBG("ttyS%d: shutting down hardware\n", info->line); - if (info->is_internal_modem) { - PWRDBG("ttyS%d: shutting down modem\n", info->line); - pmac_call_feature( - PMAC_FTR_MODEM_ENABLE, - info->dev_node, 0, 0); - } - pmac_call_feature( - PMAC_FTR_SCC_ENABLE, - info->dev_node, info->port_type, 0); - } - return delay; -} - -static void irda_rts_pulses(struct mac_serial *info, int w) -{ - udelay(w); - write_zsreg(info->zs_channel, 5, Tx8 | TxENAB); - udelay(2); - write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS); - udelay(8); - write_zsreg(info->zs_channel, 5, Tx8 | TxENAB); - udelay(4); - write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS); -} - -/* - * Set the irda codec on the imac to the specified baud rate. - */ -static void irda_setup(struct mac_serial *info) -{ - int code, speed, t; - - speed = info->tty->termios->c_cflag & CBAUD; - if (speed < B2400 || speed > B115200) - return; - code = 0x4d + B115200 - speed; - - /* disable serial interrupts and receive DMA */ - write_zsreg(info->zs_channel, 1, info->curregs[1] & ~0x9f); - - /* wait for transmitter to drain */ - t = 10000; - while ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) == 0 - || (read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) { - if (--t <= 0) { - printk(KERN_ERR "transmitter didn't drain\n"); - return; - } - udelay(10); - } - udelay(100); - - /* set to 8 bits, no parity, 19200 baud, RTS on, DTR off */ - write_zsreg(info->zs_channel, 4, X16CLK | SB1); - write_zsreg(info->zs_channel, 11, TCBR | RCBR); - t = BPS_TO_BRG(19200, ZS_CLOCK/16); - write_zsreg(info->zs_channel, 12, t); - write_zsreg(info->zs_channel, 13, t >> 8); - write_zsreg(info->zs_channel, 14, BRENABL); - write_zsreg(info->zs_channel, 3, Rx8 | RxENABLE); - write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS); - - /* set TxD low for ~104us and pulse RTS */ - udelay(1000); - write_zsdata(info->zs_channel, 0xfe); - irda_rts_pulses(info, 150); - irda_rts_pulses(info, 180); - irda_rts_pulses(info, 50); - udelay(100); - - /* assert DTR, wait 30ms, talk to the chip */ - write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS | DTR); - mdelay(30); - while (read_zsreg(info->zs_channel, 0) & Rx_CH_AV) - read_zsdata(info->zs_channel); - - write_zsdata(info->zs_channel, 1); - t = 1000; - while ((read_zsreg(info->zs_channel, 0) & Rx_CH_AV) == 0) { - if (--t <= 0) { - printk(KERN_ERR "irda_setup timed out on 1st byte\n"); - goto out; - } - udelay(10); - } - t = read_zsdata(info->zs_channel); - if (t != 4) - printk(KERN_ERR "irda_setup 1st byte = %x\n", t); - - write_zsdata(info->zs_channel, code); - t = 1000; - while ((read_zsreg(info->zs_channel, 0) & Rx_CH_AV) == 0) { - if (--t <= 0) { - printk(KERN_ERR "irda_setup timed out on 2nd byte\n"); - goto out; - } - udelay(10); - } - t = read_zsdata(info->zs_channel); - if (t != code) - printk(KERN_ERR "irda_setup 2nd byte = %x (%x)\n", t, code); - - /* Drop DTR again and do some more RTS pulses */ - out: - udelay(100); - write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS); - irda_rts_pulses(info, 80); - - /* We should be right to go now. We assume that load_zsregs - will get called soon to load up the correct baud rate etc. */ - info->curregs[5] = (info->curregs[5] | RTS) & ~DTR; - info->pendregs[5] = info->curregs[5]; -} - -/* - * This routine is called to set the UART divisor registers to match - * the specified baud rate for a serial port. - */ -static void change_speed(struct mac_serial *info, struct termios *old_termios) -{ - unsigned cflag; - int bits; - int brg, baud; - unsigned long flags; - - if (!info->tty || !info->tty->termios) - return; - - cflag = info->tty->termios->c_cflag; - baud = tty_get_baud_rate(info->tty); - if (baud == 0) { - if (old_termios) { - info->tty->termios->c_cflag &= ~CBAUD; - info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); - cflag = info->tty->termios->c_cflag; - baud = tty_get_baud_rate(info->tty); - } - else - baud = info->zs_baud; - } - if (baud > 230400) - baud = 230400; - else if (baud == 0) - baud = 38400; - - spin_lock_irqsave(&info->lock, flags); - info->zs_baud = baud; - info->clk_divisor = 16; - - BAUDBG(KERN_DEBUG "set speed to %d bds, ", baud); - - switch (baud) { - case ZS_CLOCK/16: /* 230400 */ - info->curregs[4] = X16CLK; - info->curregs[11] = 0; - break; - case ZS_CLOCK/32: /* 115200 */ - info->curregs[4] = X32CLK; - info->curregs[11] = 0; - break; - default: - info->curregs[4] = X16CLK; - info->curregs[11] = TCBR | RCBR; - brg = BPS_TO_BRG(baud, ZS_CLOCK/info->clk_divisor); - info->curregs[12] = (brg & 255); - info->curregs[13] = ((brg >> 8) & 255); - info->curregs[14] = BRENABL; - } - - /* byte size and parity */ - info->curregs[3] &= ~RxNBITS_MASK; - info->curregs[5] &= ~TxNBITS_MASK; - switch (cflag & CSIZE) { - case CS5: - info->curregs[3] |= Rx5; - info->curregs[5] |= Tx5; - BAUDBG("5 bits, "); - bits = 7; - break; - case CS6: - info->curregs[3] |= Rx6; - info->curregs[5] |= Tx6; - BAUDBG("6 bits, "); - bits = 8; - break; - case CS7: - info->curregs[3] |= Rx7; - info->curregs[5] |= Tx7; - BAUDBG("7 bits, "); - bits = 9; - break; - case CS8: - default: /* defaults to 8 bits */ - info->curregs[3] |= Rx8; - info->curregs[5] |= Tx8; - BAUDBG("8 bits, "); - bits = 10; - break; - } - info->pendregs[3] = info->curregs[3]; - info->pendregs[5] = info->curregs[5]; - - info->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN); - if (cflag & CSTOPB) { - info->curregs[4] |= SB2; - bits++; - BAUDBG("2 stop, "); - } else { - info->curregs[4] |= SB1; - BAUDBG("1 stop, "); - } - if (cflag & PARENB) { - bits++; - info->curregs[4] |= PAR_ENA; - BAUDBG("parity, "); - } - if (!(cflag & PARODD)) { - info->curregs[4] |= PAR_EVEN; - } - info->pendregs[4] = info->curregs[4]; - - if (!(cflag & CLOCAL)) { - if (!(info->curregs[15] & DCDIE)) - info->read_reg_zero = read_zsreg(info->zs_channel, 0); - info->curregs[15] |= DCDIE; - } else - info->curregs[15] &= ~DCDIE; - if (cflag & CRTSCTS) { - info->curregs[15] |= CTSIE; - if ((read_zsreg(info->zs_channel, 0) & CTS) != 0) - info->tx_stopped = 1; - } else { - info->curregs[15] &= ~CTSIE; - info->tx_stopped = 0; - } - info->pendregs[15] = info->curregs[15]; - - /* Calc timeout value. This is pretty broken with high baud rates with HZ=100. - This code would love a larger HZ and a >1 fifo size, but this is not - a priority. The resulting value must be >HZ/2 - */ - info->timeout = ((info->xmit_fifo_size*HZ*bits) / baud); - info->timeout += HZ/50+1; /* Add .02 seconds of slop */ - - BAUDBG("timeout=%d/%ds, base:%d\n", (int)info->timeout, (int)HZ, - (int)info->baud_base); - - /* set the irda codec to the right rate */ - if (info->is_irda) - irda_setup(info); - - /* Load up the new values */ - load_zsregs(info->zs_channel, info->curregs); - - spin_unlock_irqrestore(&info->lock, flags); -} - -static void rs_flush_chars(struct tty_struct *tty) -{ - struct mac_serial *info = (struct mac_serial *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_flush_chars")) - return; - - spin_lock_irqsave(&info->lock, flags); - if (!(info->xmit_cnt <= 0 || tty->stopped || info->tx_stopped || - !info->xmit_buf)) - /* Enable transmitter */ - transmit_chars(info); - spin_unlock_irqrestore(&info->lock, flags); -} - -static int rs_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - int c, ret = 0; - struct mac_serial *info = (struct mac_serial *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_write")) - return 0; - - if (!tty || !info->xmit_buf || !tmp_buf) - return 0; - - while (1) { - spin_lock_irqsave(&info->lock, flags); - c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - if (c <= 0) { - spin_unlock_irqrestore(&info->lock, flags); - break; - } - memcpy(info->xmit_buf + info->xmit_head, buf, c); - info->xmit_head = ((info->xmit_head + c) & - (SERIAL_XMIT_SIZE-1)); - info->xmit_cnt += c; - spin_unlock_irqrestore(&info->lock, flags); - buf += c; - count -= c; - ret += c; - } - spin_lock_irqsave(&info->lock, flags); - if (info->xmit_cnt && !tty->stopped && !info->tx_stopped - && !info->tx_active) - transmit_chars(info); - spin_unlock_irqrestore(&info->lock, flags); - return ret; -} - -static int rs_write_room(struct tty_struct *tty) -{ - struct mac_serial *info = (struct mac_serial *)tty->driver_data; - int ret; - - if (serial_paranoia_check(info, tty->name, "rs_write_room")) - return 0; - ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; - return ret; -} - -static int rs_chars_in_buffer(struct tty_struct *tty) -{ - struct mac_serial *info = (struct mac_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer")) - return 0; - return info->xmit_cnt; -} - -static void rs_flush_buffer(struct tty_struct *tty) -{ - struct mac_serial *info = (struct mac_serial *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_flush_buffer")) - return; - spin_lock_irqsave(&info->lock, flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - spin_unlock_irqrestore(&info->lock, flags); - tty_wakeup(tty); -} - -/* - * ------------------------------------------------------------ - * rs_throttle() - * - * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled. - * ------------------------------------------------------------ - */ -static void rs_throttle(struct tty_struct * tty) -{ - struct mac_serial *info = (struct mac_serial *)tty->driver_data; - unsigned long flags; -#ifdef SERIAL_DEBUG_THROTTLE - printk(KERN_DEBUG "throttle %ld....\n",tty->ldisc.chars_in_buffer(tty)); -#endif - - if (serial_paranoia_check(info, tty->name, "rs_throttle")) - return; - - if (I_IXOFF(tty)) { - spin_lock_irqsave(&info->lock, flags); - info->x_char = STOP_CHAR(tty); - if (!info->tx_active) - transmit_chars(info); - spin_unlock_irqrestore(&info->lock, flags); - } - - if (C_CRTSCTS(tty)) { - /* - * Here we want to turn off the RTS line. On Macintoshes, - * the external serial ports using a DIN-8 or DIN-9 - * connector only have the DTR line (which is usually - * wired to both RTS and DTR on an external modem in - * the cable). RTS doesn't go out to the serial port - * socket, it acts as an output enable for the transmit - * data line. So in this case we don't drop RTS. - * - * Macs with internal modems generally do have both RTS - * and DTR wired to the modem, so in that case we do - * drop RTS. - */ - if (info->is_internal_modem) { - spin_lock_irqsave(&info->lock, flags); - info->curregs[5] &= ~RTS; - info->pendregs[5] &= ~RTS; - write_zsreg(info->zs_channel, 5, info->curregs[5]); - spin_unlock_irqrestore(&info->lock, flags); - } - } - -#ifdef CDTRCTS - if (tty->termios->c_cflag & CDTRCTS) { - spin_lock_irqsave(&info->lock, flags); - info->curregs[5] &= ~DTR; - info->pendregs[5] &= ~DTR; - write_zsreg(info->zs_channel, 5, info->curregs[5]); - spin_unlock_irqrestore(&info->lock, flags); - } -#endif /* CDTRCTS */ -} - -static void rs_unthrottle(struct tty_struct * tty) -{ - struct mac_serial *info = (struct mac_serial *)tty->driver_data; - unsigned long flags; -#ifdef SERIAL_DEBUG_THROTTLE - printk(KERN_DEBUG "unthrottle %s: %d....\n", - tty->ldisc.chars_in_buffer(tty)); -#endif - - if (serial_paranoia_check(info, tty->name, "rs_unthrottle")) - return; - - if (I_IXOFF(tty)) { - spin_lock_irqsave(&info->lock, flags); - if (info->x_char) - info->x_char = 0; - else { - info->x_char = START_CHAR(tty); - if (!info->tx_active) - transmit_chars(info); - } - spin_unlock_irqrestore(&info->lock, flags); - } - - if (C_CRTSCTS(tty) && info->is_internal_modem) { - /* Assert RTS line */ - spin_lock_irqsave(&info->lock, flags); - info->curregs[5] |= RTS; - info->pendregs[5] |= RTS; - write_zsreg(info->zs_channel, 5, info->curregs[5]); - spin_unlock_irqrestore(&info->lock, flags); - } - -#ifdef CDTRCTS - if (tty->termios->c_cflag & CDTRCTS) { - /* Assert DTR line */ - spin_lock_irqsave(&info->lock, flags); - info->curregs[5] |= DTR; - info->pendregs[5] |= DTR; - write_zsreg(info->zs_channel, 5, info->curregs[5]); - spin_unlock_irqrestore(&info->lock, flags); - } -#endif -} - -/* - * ------------------------------------------------------------ - * rs_ioctl() and friends - * ------------------------------------------------------------ - */ - -static int get_serial_info(struct mac_serial * info, - struct serial_struct __user * retinfo) -{ - struct serial_struct tmp; - - if (!retinfo) - return -EFAULT; - memset(&tmp, 0, sizeof(tmp)); - tmp.type = info->type; - tmp.line = info->line; - tmp.port = info->port; - tmp.irq = info->irq; - tmp.flags = info->flags; - tmp.baud_base = info->baud_base; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; - tmp.custom_divisor = info->custom_divisor; - if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) - return -EFAULT; - return 0; -} - -static int set_serial_info(struct mac_serial * info, - struct serial_struct __user * new_info) -{ - struct serial_struct new_serial; - struct mac_serial old_info; - int retval = 0; - - if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) - return -EFAULT; - old_info = *info; - - if (!capable(CAP_SYS_ADMIN)) { - if ((new_serial.baud_base != info->baud_base) || - (new_serial.type != info->type) || - (new_serial.close_delay != info->close_delay) || - ((new_serial.flags & ~ZILOG_USR_MASK) != - (info->flags & ~ZILOG_USR_MASK))) - return -EPERM; - info->flags = ((info->flags & ~ZILOG_USR_MASK) | - (new_serial.flags & ZILOG_USR_MASK)); - info->custom_divisor = new_serial.custom_divisor; - goto check_and_exit; - } - - if (info->count > 1) - return -EBUSY; - - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - - info->baud_base = new_serial.baud_base; - info->flags = ((info->flags & ~ZILOG_FLAGS) | - (new_serial.flags & ZILOG_FLAGS)); - info->type = new_serial.type; - info->close_delay = new_serial.close_delay; - info->closing_wait = new_serial.closing_wait; - -check_and_exit: - if (info->flags & ZILOG_INITIALIZED) - retval = setup_scc(info); - return retval; -} - -/* - * get_lsr_info - get line status register info - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - */ -static int get_lsr_info(struct mac_serial * info, unsigned int *value) -{ - unsigned char status; - unsigned long flags; - - spin_lock_irqsave(&info->lock, flags); - status = read_zsreg(info->zs_channel, 0); - spin_unlock_irqrestore(&info->lock, flags); - status = (status & Tx_BUF_EMP)? TIOCSER_TEMT: 0; - return put_user(status,value); -} - -static int rs_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct mac_serial * info = (struct mac_serial *)tty->driver_data; - unsigned char control, status; - unsigned long flags; - -#ifdef CONFIG_KGDB - if (info->kgdb_channel) - return -ENODEV; -#endif - if (serial_paranoia_check(info, tty->name, __FUNCTION__)) - return -ENODEV; - - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - - spin_lock_irqsave(&info->lock, flags); - control = info->curregs[5]; - status = read_zsreg(info->zs_channel, 0); - spin_unlock_irqrestore(&info->lock, flags); - return ((control & RTS) ? TIOCM_RTS: 0) - | ((control & DTR) ? TIOCM_DTR: 0) - | ((status & DCD) ? TIOCM_CAR: 0) - | ((status & CTS) ? 0: TIOCM_CTS); -} - -static int rs_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ - struct mac_serial * info = (struct mac_serial *)tty->driver_data; - unsigned int arg, bits; - unsigned long flags; - -#ifdef CONFIG_KGDB - if (info->kgdb_channel) - return -ENODEV; -#endif - if (serial_paranoia_check(info, tty->name, __FUNCTION__)) - return -ENODEV; - - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - - spin_lock_irqsave(&info->lock, flags); - if (set & TIOCM_RTS) - info->curregs[5] |= RTS; - if (set & TIOCM_DTR) - info->curregs[5] |= DTR; - if (clear & TIOCM_RTS) - info->curregs[5] &= ~RTS; - if (clear & TIOCM_DTR) - info->curregs[5] &= ~DTR; - - info->pendregs[5] = info->curregs[5]; - write_zsreg(info->zs_channel, 5, info->curregs[5]); - spin_unlock_irqrestore(&info->lock, flags); - return 0; -} - -/* - * rs_break - turn transmit break condition on/off - */ -static void rs_break(struct tty_struct *tty, int break_state) -{ - struct mac_serial *info = (struct mac_serial *) tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_break")) - return; - - spin_lock_irqsave(&info->lock, flags); - if (break_state == -1) - info->curregs[5] |= SND_BRK; - else - info->curregs[5] &= ~SND_BRK; - write_zsreg(info->zs_channel, 5, info->curregs[5]); - spin_unlock_irqrestore(&info->lock, flags); -} - -static int rs_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg) -{ - struct mac_serial * info = (struct mac_serial *)tty->driver_data; - -#ifdef CONFIG_KGDB - if (info->kgdb_channel) - return -ENODEV; -#endif - if (serial_paranoia_check(info, tty->name, "rs_ioctl")) - return -ENODEV; - - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { - case TIOCGSERIAL: - return get_serial_info(info, - (struct serial_struct __user *) arg); - case TIOCSSERIAL: - return set_serial_info(info, - (struct serial_struct __user *) arg); - case TIOCSERGETLSR: /* Get line status register */ - return get_lsr_info(info, (unsigned int *) arg); - - case TIOCSERGSTRUCT: - if (copy_to_user((struct mac_serial __user *) arg, - info, sizeof(struct mac_serial))) - return -EFAULT; - return 0; - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) -{ - struct mac_serial *info = (struct mac_serial *)tty->driver_data; - int was_stopped; - - if (tty->termios->c_cflag == old_termios->c_cflag) - return; - was_stopped = info->tx_stopped; - - change_speed(info, old_termios); - - if (was_stopped && !info->tx_stopped) { - tty->hw_stopped = 0; - rs_start(tty); - } -} - -/* - * ------------------------------------------------------------ - * rs_close() - * - * This routine is called when the serial port gets closed. - * Wait for the last remaining data to be sent. - * ------------------------------------------------------------ - */ -static void rs_close(struct tty_struct *tty, struct file * filp) -{ - struct mac_serial * info = (struct mac_serial *)tty->driver_data; - unsigned long flags; - - if (!info || serial_paranoia_check(info, tty->name, "rs_close")) - return; - - spin_lock_irqsave(&info->lock, flags); - - if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&info->lock, flags); - return; - } - - OPNDBG("rs_close ttyS%d, count = %d\n", info->line, info->count); - if ((tty->count == 1) && (info->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk(KERN_ERR "rs_close: bad serial port count; tty->count " - "is 1, info->count is %d\n", info->count); - info->count = 1; - } - if (--info->count < 0) { - printk(KERN_ERR "rs_close: bad serial port count for " - "ttyS%d: %d\n", info->line, info->count); - info->count = 0; - } - if (info->count) { - spin_unlock_irqrestore(&info->lock, flags); - return; - } - info->flags |= ZILOG_CLOSING; - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - OPNDBG("waiting end of Tx... (timeout:%d)\n", info->closing_wait); - tty->closing = 1; - if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) { - spin_unlock_irqrestore(&info->lock, flags); - tty_wait_until_sent(tty, info->closing_wait); - spin_lock_irqsave(&info->lock, flags); - } - - /* - * At this point we stop accepting input. To do this, we - * disable the receiver and receive interrupts. - */ - info->curregs[3] &= ~RxENABLE; - info->pendregs[3] = info->curregs[3]; - write_zsreg(info->zs_channel, 3, info->curregs[3]); - info->curregs[1] &= ~(0x18); /* disable any rx ints */ - info->pendregs[1] = info->curregs[1]; - write_zsreg(info->zs_channel, 1, info->curregs[1]); - ZS_CLEARFIFO(info->zs_channel); - if (info->flags & ZILOG_INITIALIZED) { - /* - * Before we drop DTR, make sure the SCC transmitter - * has completely drained. - */ - OPNDBG("waiting end of Rx...\n"); - spin_unlock_irqrestore(&info->lock, flags); - rs_wait_until_sent(tty, info->timeout); - spin_lock_irqsave(&info->lock, flags); - } - - shutdown(info); - /* restore flags now since shutdown() will have disabled this port's - specific irqs */ - spin_unlock_irqrestore(&info->lock, flags); - - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); - tty_ldisc_flush(tty); - tty->closing = 0; - info->event = 0; - info->tty = 0; - - if (info->blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); - } - wake_up_interruptible(&info->open_wait); - } - info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CLOSING); - wake_up_interruptible(&info->close_wait); -} - -/* - * rs_wait_until_sent() --- wait until the transmitter is empty - */ -static void rs_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct mac_serial *info = (struct mac_serial *) tty->driver_data; - unsigned long orig_jiffies, char_time; - - if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent")) - return; - -/* printk("rs_wait_until_sent, timeout:%d, tty_stopped:%d, tx_stopped:%d\n", - timeout, tty->stopped, info->tx_stopped); -*/ - orig_jiffies = jiffies; - /* - * Set the check interval to be 1/5 of the estimated time to - * send a single character, and make it at least 1. The check - * interval should also be less than the timeout. - */ - if (info->timeout <= HZ/50) { - printk(KERN_INFO "macserial: invalid info->timeout=%d\n", - info->timeout); - info->timeout = HZ/50+1; - } - - char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; - char_time = char_time / 5; - if (char_time > HZ) { - printk(KERN_WARNING "macserial: char_time %ld >HZ !!!\n", - char_time); - char_time = 1; - } else if (char_time == 0) - char_time = 1; - if (timeout) - char_time = min_t(unsigned long, char_time, timeout); - while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) { - msleep_interruptible(jiffies_to_msecs(char_time)); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } - current->state = TASK_RUNNING; -} - -/* - * rs_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -static void rs_hangup(struct tty_struct *tty) -{ - struct mac_serial * info = (struct mac_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_hangup")) - return; - - rs_flush_buffer(tty); - shutdown(info); - info->event = 0; - info->count = 0; - info->flags &= ~ZILOG_NORMAL_ACTIVE; - info->tty = 0; - wake_up_interruptible(&info->open_wait); -} - -/* - * ------------------------------------------------------------ - * rs_open() and friends - * ------------------------------------------------------------ - */ -static int block_til_ready(struct tty_struct *tty, struct file * filp, - struct mac_serial *info) -{ - DECLARE_WAITQUEUE(wait,current); - int retval; - int do_clocal = 0; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (info->flags & ZILOG_CLOSING) { - interruptible_sleep_on(&info->close_wait); - return -EAGAIN; - } - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= ZILOG_NORMAL_ACTIVE; - return 0; - } - - if (tty->termios->c_cflag & CLOCAL) - do_clocal = 1; - - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * rs_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->open_wait, &wait); - OPNDBG("block_til_ready before block: ttyS%d, count = %d\n", - info->line, info->count); - spin_lock_irq(&info->lock); - if (!tty_hung_up_p(filp)) - info->count--; - spin_unlock_irq(&info->lock); - info->blocked_open++; - while (1) { - spin_lock_irq(&info->lock); - if ((tty->termios->c_cflag & CBAUD) && - !info->is_irda) - zs_rtsdtr(info, 1); - spin_unlock_irq(&info->lock); - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(info->flags & ZILOG_INITIALIZED)) { - retval = -EAGAIN; - break; - } - if (!(info->flags & ZILOG_CLOSING) && - (do_clocal || (read_zsreg(info->zs_channel, 0) & DCD))) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - OPNDBG("block_til_ready blocking: ttyS%d, count = %d\n", - info->line, info->count); - schedule(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&info->open_wait, &wait); - if (!tty_hung_up_p(filp)) - info->count++; - info->blocked_open--; - OPNDBG("block_til_ready after blocking: ttyS%d, count = %d\n", - info->line, info->count); - if (retval) - return retval; - info->flags |= ZILOG_NORMAL_ACTIVE; - return 0; -} - -/* - * This routine is called whenever a serial port is opened. It - * enables interrupts for a serial port, linking in its ZILOG structure into - * the IRQ chain. It also performs the serial-specific - * initialization for the tty structure. - */ -static int rs_open(struct tty_struct *tty, struct file * filp) -{ - struct mac_serial *info; - int retval, line; - unsigned long page; - - line = tty->index; - if ((line < 0) || (line >= zs_channels_found)) { - return -ENODEV; - } - info = zs_soft + line; - -#ifdef CONFIG_KGDB - if (info->kgdb_channel) { - return -ENODEV; - } -#endif - if (serial_paranoia_check(info, tty->name, "rs_open")) - return -ENODEV; - OPNDBG("rs_open %s, count = %d, tty=%p\n", tty->name, - info->count, tty); - - info->count++; - tty->driver_data = info; - info->tty = tty; - - if (!tmp_buf) { - page = get_zeroed_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - if (tmp_buf) - free_page(page); - else - tmp_buf = (unsigned char *) page; - } - - /* - * If the port is the middle of closing, bail out now - */ - if (tty_hung_up_p(filp) || - (info->flags & ZILOG_CLOSING)) { - if (info->flags & ZILOG_CLOSING) - interruptible_sleep_on(&info->close_wait); - return -EAGAIN; - } - - /* - * Start up serial port - */ - - retval = startup(info); - if (retval) - return retval; - - retval = block_til_ready(tty, filp, info); - if (retval) { - OPNDBG("rs_open returning after block_til_ready with %d\n", - retval); - return retval; - } - -#ifdef CONFIG_SERIAL_CONSOLE - if (sercons.cflag && sercons.index == line) { - tty->termios->c_cflag = sercons.cflag; - sercons.cflag = 0; - change_speed(info, 0); - } -#endif - - OPNDBG("rs_open %s successful...\n", tty->name); - return 0; -} - -/* Finally, routines used to initialize the serial driver. */ - -static void show_serial_version(void) -{ - printk(KERN_INFO "PowerMac Z8530 serial driver version " MACSERIAL_VERSION "\n"); -} - -/* - * Initialize one channel, both the mac_serial and mac_zschannel - * structs. We use the dev_node field of the mac_serial struct. - */ -static int -chan_init(struct mac_serial *zss, struct mac_zschannel *zs_chan, - struct mac_zschannel *zs_chan_a) -{ - struct device_node *ch = zss->dev_node; - char *conn; - int len; - struct slot_names_prop { - int count; - char name[1]; - } *slots; - - zss->irq = ch->intrs[0].line; - zss->has_dma = 0; -#if !defined(CONFIG_KGDB) && defined(SUPPORT_SERIAL_DMA) - if (ch->n_addrs >= 3 && ch->n_intrs == 3) - zss->has_dma = 1; -#endif - zss->dma_initted = 0; - - zs_chan->control = (volatile unsigned char *) - ioremap(ch->addrs[0].address, 0x1000); - zs_chan->data = zs_chan->control + 0x10; - spin_lock_init(&zs_chan->lock); - zs_chan->parent = zss; - zss->zs_channel = zs_chan; - zss->zs_chan_a = zs_chan_a; - - /* setup misc varariables */ - zss->kgdb_channel = 0; - - /* For now, we assume you either have a slot-names property - * with "Modem" in it, or your channel is compatible with - * "cobalt". Might need additional fixups - */ - zss->is_internal_modem = device_is_compatible(ch, "cobalt"); - conn = get_property(ch, "AAPL,connector", &len); - zss->is_irda = conn && (strcmp(conn, "infrared") == 0); - zss->port_type = PMAC_SCC_ASYNC; - /* 1999 Powerbook G3 has slot-names property instead */ - slots = (struct slot_names_prop *)get_property(ch, "slot-names", &len); - if (slots && slots->count > 0) { - if (strcmp(slots->name, "IrDA") == 0) - zss->is_irda = 1; - else if (strcmp(slots->name, "Modem") == 0) - zss->is_internal_modem = 1; - } - if (zss->is_irda) - zss->port_type = PMAC_SCC_IRDA; - if (zss->is_internal_modem) { - struct device_node* i2c_modem = find_devices("i2c-modem"); - if (i2c_modem) { - char* mid = get_property(i2c_modem, "modem-id", NULL); - if (mid) switch(*mid) { - case 0x04 : - case 0x05 : - case 0x07 : - case 0x08 : - case 0x0b : - case 0x0c : - zss->port_type = PMAC_SCC_I2S1; - } - printk(KERN_INFO "macserial: i2c-modem detected, id: %d\n", - mid ? (*mid) : 0); - } else { - printk(KERN_INFO "macserial: serial modem detected\n"); - } - } - - while (zss->has_dma) { - zss->dma_priv = NULL; - /* it seems that the last two addresses are the - DMA controllers */ - zss->tx_dma = (volatile struct dbdma_regs *) - ioremap(ch->addrs[ch->n_addrs - 2].address, 0x100); - zss->rx = (volatile struct mac_dma *) - ioremap(ch->addrs[ch->n_addrs - 1].address, 0x100); - zss->tx_dma_irq = ch->intrs[1].line; - zss->rx_dma_irq = ch->intrs[2].line; - spin_lock_init(&zss->rx_dma_lock); - break; - } - - init_timer(&zss->powerup_timer); - zss->powerup_timer.function = powerup_done; - zss->powerup_timer.data = (unsigned long) zss; - return 0; -} - -/* - * /proc fs routines. TODO: Add status lines & error stats - */ -static inline int -line_info(char *buf, struct mac_serial *info) -{ - int ret=0; - unsigned char* connector; - int lenp; - - ret += sprintf(buf, "%d: port:0x%X irq:%d", info->line, info->port, info->irq); - - connector = get_property(info->dev_node, "AAPL,connector", &lenp); - if (connector) - ret+=sprintf(buf+ret," con:%s ", connector); - if (info->is_internal_modem) { - if (!connector) - ret+=sprintf(buf+ret," con:"); - ret+=sprintf(buf+ret,"%s", " (internal modem)"); - } - if (info->is_irda) { - if (!connector) - ret+=sprintf(buf+ret," con:"); - ret+=sprintf(buf+ret,"%s", " (IrDA)"); - } - ret+=sprintf(buf+ret,"\n"); - - return ret; -} - -int macserial_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - int l, len = 0; - off_t begin = 0; - struct mac_serial *info; - - len += sprintf(page, "serinfo:1.0 driver:" MACSERIAL_VERSION "\n"); - for (info = zs_chain; info && len < 4000; info = info->zs_next) { - l = line_info(page + len, info); - len += l; - if (len+begin > off+count) - goto done; - if (len+begin < off) { - begin += len; - len = 0; - } - } - *eof = 1; -done: - if (off >= len+begin) - return 0; - *start = page + (off-begin); - return ((count < begin+len-off) ? count : begin+len-off); -} - -/* Ask the PROM how many Z8530s we have and initialize their zs_channels */ -static void -probe_sccs(void) -{ - struct device_node *dev, *ch; - struct mac_serial **pp; - int n, chip, nchan; - struct mac_zschannel *zs_chan; - int chan_a_index; - - n = 0; - pp = &zs_chain; - zs_chan = zs_channels; - for (dev = find_devices("escc"); dev != 0; dev = dev->next) { - nchan = 0; - chip = n; - if (n >= NUM_CHANNELS) { - printk(KERN_WARNING "Sorry, can't use %s: no more " - "channels\n", dev->full_name); - continue; - } - chan_a_index = 0; - for (ch = dev->child; ch != 0; ch = ch->sibling) { - if (nchan >= 2) { - printk(KERN_WARNING "SCC: Only 2 channels per " - "chip are supported\n"); - break; - } - if (ch->n_addrs < 1 || (ch ->n_intrs < 1)) { - printk("Can't use %s: %d addrs %d intrs\n", - ch->full_name, ch->n_addrs, ch->n_intrs); - continue; - } - - /* The channel with the higher address - will be the A side. */ - if (nchan > 0 && - ch->addrs[0].address - > zs_soft[n-1].dev_node->addrs[0].address) - chan_a_index = 1; - - /* minimal initialization for now */ - zs_soft[n].dev_node = ch; - *pp = &zs_soft[n]; - pp = &zs_soft[n].zs_next; - ++nchan; - ++n; - } - if (nchan == 0) - continue; - - /* set up A side */ - if (chan_init(&zs_soft[chip + chan_a_index], zs_chan, zs_chan)) - continue; - ++zs_chan; - - /* set up B side, if it exists */ - if (nchan > 1) - if (chan_init(&zs_soft[chip + 1 - chan_a_index], - zs_chan, zs_chan - 1)) - continue; - ++zs_chan; - } - *pp = 0; - - zs_channels_found = n; -#ifdef CONFIG_PMAC_PBOOK - if (n) - pmu_register_sleep_notifier(&serial_sleep_notifier); -#endif /* CONFIG_PMAC_PBOOK */ -} - -static struct tty_operations serial_ops = { - .open = rs_open, - .close = rs_close, - .write = rs_write, - .flush_chars = rs_flush_chars, - .write_room = rs_write_room, - .chars_in_buffer = rs_chars_in_buffer, - .flush_buffer = rs_flush_buffer, - .ioctl = rs_ioctl, - .throttle = rs_throttle, - .unthrottle = rs_unthrottle, - .set_termios = rs_set_termios, - .stop = rs_stop, - .start = rs_start, - .hangup = rs_hangup, - .break_ctl = rs_break, - .wait_until_sent = rs_wait_until_sent, - .read_proc = macserial_read_proc, - .tiocmget = rs_tiocmget, - .tiocmset = rs_tiocmset, -}; - -static int macserial_init(void) -{ - int channel, i; - struct mac_serial *info; - - /* Find out how many Z8530 SCCs we have */ - if (zs_chain == 0) - probe_sccs(); - - serial_driver = alloc_tty_driver(zs_channels_found); - if (!serial_driver) - return -ENOMEM; - - /* XXX assume it's a powerbook if we have a via-pmu - * - * This is OK for core99 machines as well. - */ - is_powerbook = find_devices("via-pmu") != 0; - - /* Register the interrupt handler for each one - * We also request the OF resources here as probe_sccs() - * might be called too early for that - */ - for (i = 0; i < zs_channels_found; ++i) { - struct device_node* ch = zs_soft[i].dev_node; - if (!request_OF_resource(ch, 0, NULL)) { - printk(KERN_ERR "macserial: can't request IO resource !\n"); - put_tty_driver(serial_driver); - return -ENODEV; - } - if (zs_soft[i].has_dma) { - if (!request_OF_resource(ch, ch->n_addrs - 2, " (tx dma)")) { - printk(KERN_ERR "macserial: can't request TX DMA resource !\n"); - zs_soft[i].has_dma = 0; - goto no_dma; - } - if (!request_OF_resource(ch, ch->n_addrs - 1, " (rx dma)")) { - release_OF_resource(ch, ch->n_addrs - 2); - printk(KERN_ERR "macserial: can't request RX DMA resource !\n"); - zs_soft[i].has_dma = 0; - goto no_dma; - } - if (request_irq(zs_soft[i].tx_dma_irq, rs_txdma_irq, 0, - "SCC-txdma", &zs_soft[i])) - printk(KERN_ERR "macserial: can't get irq %d\n", - zs_soft[i].tx_dma_irq); - disable_irq(zs_soft[i].tx_dma_irq); - if (request_irq(zs_soft[i].rx_dma_irq, rs_rxdma_irq, 0, - "SCC-rxdma", &zs_soft[i])) - printk(KERN_ERR "macserial: can't get irq %d\n", - zs_soft[i].rx_dma_irq); - disable_irq(zs_soft[i].rx_dma_irq); - } -no_dma: - if (request_irq(zs_soft[i].irq, rs_interrupt, 0, - "SCC", &zs_soft[i])) - printk(KERN_ERR "macserial: can't get irq %d\n", - zs_soft[i].irq); - disable_irq(zs_soft[i].irq); - } - - show_serial_version(); - - /* Initialize the tty_driver structure */ - /* Not all of this is exactly right for us. */ - - serial_driver->owner = THIS_MODULE; - serial_driver->driver_name = "macserial"; - serial_driver->devfs_name = "tts/"; - serial_driver->name = "ttyS"; - serial_driver->major = TTY_MAJOR; - serial_driver->minor_start = 64; - serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - serial_driver->subtype = SERIAL_TYPE_NORMAL; - serial_driver->init_termios = tty_std_termios; - serial_driver->init_termios.c_cflag = - B38400 | CS8 | CREAD | HUPCL | CLOCAL; - serial_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(serial_driver, &serial_ops); - - if (tty_register_driver(serial_driver)) - printk(KERN_ERR "Error: couldn't register serial driver\n"); - - for (channel = 0; channel < zs_channels_found; ++channel) { -#ifdef CONFIG_KGDB - if (zs_soft[channel].kgdb_channel) { - kgdb_interruptible(1); - continue; - } -#endif - zs_soft[channel].clk_divisor = 16; -/* -- we are not sure the SCC is powered ON at this point - zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]); -*/ - zs_soft[channel].zs_baud = 38400; - - /* If console serial line, then enable interrupts. */ - if (zs_soft[channel].is_cons) { - printk(KERN_INFO "macserial: console line, enabling " - "interrupt %d\n", zs_soft[channel].irq); - panic("macserial: console not supported yet !"); - write_zsreg(zs_soft[channel].zs_channel, R1, - (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB)); - write_zsreg(zs_soft[channel].zs_channel, R9, - (NV | MIE)); - } - } - - for (info = zs_chain, i = 0; info; info = info->zs_next, i++) - { - unsigned char* connector; - int lenp; - -#ifdef CONFIG_KGDB - if (info->kgdb_channel) { - continue; - } -#endif - info->magic = SERIAL_MAGIC; - info->port = (int) info->zs_channel->control; - info->line = i; - info->tty = 0; - info->custom_divisor = 16; - info->timeout = 0; - info->close_delay = 50; - info->closing_wait = 3000; - info->x_char = 0; - info->event = 0; - info->count = 0; - info->blocked_open = 0; - INIT_WORK(&info->tqueue, do_softint, info); - spin_lock_init(&info->lock); - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - info->timeout = HZ; - printk(KERN_INFO "tty%02d at 0x%08x (irq = %d)", info->line, - info->port, info->irq); - printk(" is a Z8530 ESCC"); - connector = get_property(info->dev_node, "AAPL,connector", &lenp); - if (connector) - printk(", port = %s", connector); - if (info->is_internal_modem) - printk(" (internal modem)"); - if (info->is_irda) - printk(" (IrDA)"); - printk("\n"); - } - tmp_buf = 0; - - return 0; -} - -void macserial_cleanup(void) -{ - int i; - unsigned long flags; - struct mac_serial *info; - - for (info = zs_chain, i = 0; info; info = info->zs_next, i++) - set_scc_power(info, 0); - spin_lock_irqsave(&info->lock, flags); - for (i = 0; i < zs_channels_found; ++i) { - free_irq(zs_soft[i].irq, &zs_soft[i]); - if (zs_soft[i].has_dma) { - free_irq(zs_soft[i].tx_dma_irq, &zs_soft[i]); - free_irq(zs_soft[i].rx_dma_irq, &zs_soft[i]); - } - release_OF_resource(zs_soft[i].dev_node, 0); - if (zs_soft[i].has_dma) { - struct device_node* ch = zs_soft[i].dev_node; - release_OF_resource(ch, ch->n_addrs - 2); - release_OF_resource(ch, ch->n_addrs - 1); - } - } - spin_unlock_irqrestore(&info->lock, flags); - tty_unregister_driver(serial_driver); - put_tty_driver(serial_driver); - - if (tmp_buf) { - free_page((unsigned long) tmp_buf); - tmp_buf = 0; - } - -#ifdef CONFIG_PMAC_PBOOK - if (zs_channels_found) - pmu_unregister_sleep_notifier(&serial_sleep_notifier); -#endif /* CONFIG_PMAC_PBOOK */ -} - -module_init(macserial_init); -module_exit(macserial_cleanup); -MODULE_LICENSE("GPL"); - -#if 0 -/* - * register_serial and unregister_serial allows for serial ports to be - * configured at run-time, to support PCMCIA modems. - */ -/* PowerMac: Unused at this time, just here to make things link. */ -int register_serial(struct serial_struct *req) -{ - return -1; -} - -void unregister_serial(int line) -{ - return; -} -#endif - -/* - * ------------------------------------------------------------ - * Serial console driver - * ------------------------------------------------------------ - */ -#ifdef CONFIG_SERIAL_CONSOLE - -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - */ -static void serial_console_write(struct console *co, const char *s, - unsigned count) -{ - struct mac_serial *info = zs_soft + co->index; - int i; - - /* Turn of interrupts and enable the transmitter. */ - write_zsreg(info->zs_channel, R1, info->curregs[1] & ~TxINT_ENAB); - write_zsreg(info->zs_channel, R5, info->curregs[5] | TxENAB | RTS | DTR); - - for (i=0; i<count; i++) { - /* Wait for the transmit buffer to empty. */ - while ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) == 0) { - eieio(); - } - - write_zsdata(info->zs_channel, s[i]); - if (s[i] == 10) { - while ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) - == 0) - eieio(); - - write_zsdata(info->zs_channel, 13); - } - } - - /* Restore the values in the registers. */ - write_zsreg(info->zs_channel, R1, info->curregs[1]); - /* Don't disable the transmitter. */ -} - -static struct tty_driver *serial_driver; - -static struct tty_driver *serial_console_device(struct console *c, int *index) -{ - *index = c->index; - return serial_driver; -} - -/* - * Setup initial baud/bits/parity. We do two things here: - * - construct a cflag setting for the first rs_open() - * - initialize the serial port - * Return non-zero if we didn't find a serial port. - */ -static int __init serial_console_setup(struct console *co, char *options) -{ - struct mac_serial *info; - int baud = 38400; - int bits = 8; - int parity = 'n'; - int cflag = CREAD | HUPCL | CLOCAL; - int brg; - char *s; - long flags; - - /* Find out how many Z8530 SCCs we have */ - if (zs_chain == 0) - probe_sccs(); - - if (zs_chain == 0) - return -1; - - /* Do we have the device asked for? */ - if (co->index >= zs_channels_found) - return -1; - info = zs_soft + co->index; - - set_scc_power(info, 1); - - /* Reset the channel */ - write_zsreg(info->zs_channel, R9, CHRA); - - if (options) { - baud = simple_strtoul(options, NULL, 10); - s = options; - while(*s >= '0' && *s <= '9') - s++; - if (*s) - parity = *s++; - if (*s) - bits = *s - '0'; - } - - /* - * Now construct a cflag setting. - */ - switch(baud) { - case 1200: - cflag |= B1200; - break; - case 2400: - cflag |= B2400; - break; - case 4800: - cflag |= B4800; - break; - case 9600: - cflag |= B9600; - break; - case 19200: - cflag |= B19200; - break; - case 57600: - cflag |= B57600; - break; - case 115200: - cflag |= B115200; - break; - case 38400: - default: - cflag |= B38400; - break; - } - switch(bits) { - case 7: - cflag |= CS7; - break; - default: - case 8: - cflag |= CS8; - break; - } - switch(parity) { - case 'o': case 'O': - cflag |= PARENB | PARODD; - break; - case 'e': case 'E': - cflag |= PARENB; - break; - } - co->cflag = cflag; - - spin_lock_irqsave(&info->lock, flags); - memset(info->curregs, 0, sizeof(info->curregs)); - - info->zs_baud = baud; - info->clk_divisor = 16; - switch (info->zs_baud) { - case ZS_CLOCK/16: /* 230400 */ - info->curregs[4] = X16CLK; - info->curregs[11] = 0; - break; - case ZS_CLOCK/32: /* 115200 */ - info->curregs[4] = X32CLK; - info->curregs[11] = 0; - break; - default: - info->curregs[4] = X16CLK; - info->curregs[11] = TCBR | RCBR; - brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor); - info->curregs[12] = (brg & 255); - info->curregs[13] = ((brg >> 8) & 255); - info->curregs[14] = BRENABL; - } - - /* byte size and parity */ - info->curregs[3] &= ~RxNBITS_MASK; - info->curregs[5] &= ~TxNBITS_MASK; - switch (cflag & CSIZE) { - case CS5: - info->curregs[3] |= Rx5; - info->curregs[5] |= Tx5; - break; - case CS6: - info->curregs[3] |= Rx6; - info->curregs[5] |= Tx6; - break; - case CS7: - info->curregs[3] |= Rx7; - info->curregs[5] |= Tx7; - break; - case CS8: - default: /* defaults to 8 bits */ - info->curregs[3] |= Rx8; - info->curregs[5] |= Tx8; - break; - } - info->curregs[5] |= TxENAB | RTS | DTR; - info->pendregs[3] = info->curregs[3]; - info->pendregs[5] = info->curregs[5]; - - info->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN); - if (cflag & CSTOPB) { - info->curregs[4] |= SB2; - } else { - info->curregs[4] |= SB1; - } - if (cflag & PARENB) { - info->curregs[4] |= PAR_ENA; - if (!(cflag & PARODD)) { - info->curregs[4] |= PAR_EVEN; - } - } - info->pendregs[4] = info->curregs[4]; - - if (!(cflag & CLOCAL)) { - if (!(info->curregs[15] & DCDIE)) - info->read_reg_zero = read_zsreg(info->zs_channel, 0); - info->curregs[15] |= DCDIE; - } else - info->curregs[15] &= ~DCDIE; - if (cflag & CRTSCTS) { - info->curregs[15] |= CTSIE; - if ((read_zsreg(info->zs_channel, 0) & CTS) != 0) - info->tx_stopped = 1; - } else { - info->curregs[15] &= ~CTSIE; - info->tx_stopped = 0; - } - info->pendregs[15] = info->curregs[15]; - - /* Load up the new values */ - load_zsregs(info->zs_channel, info->curregs); - - spin_unlock_irqrestore(&info->lock, flags); - - return 0; -} - -static struct console sercons = { - .name = "ttyS", - .write = serial_console_write, - .device = serial_console_device, - .setup = serial_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -/* - * Register console. - */ -static void __init mac_scc_console_init(void) -{ - register_console(&sercons); -} -console_initcall(mac_scc_console_init); - -#endif /* ifdef CONFIG_SERIAL_CONSOLE */ - -#ifdef CONFIG_KGDB -/* These are for receiving and sending characters under the kgdb - * source level kernel debugger. - */ -void putDebugChar(char kgdb_char) -{ - struct mac_zschannel *chan = zs_kgdbchan; - while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0) - udelay(5); - write_zsdata(chan, kgdb_char); -} - -char getDebugChar(void) -{ - struct mac_zschannel *chan = zs_kgdbchan; - while((read_zsreg(chan, 0) & Rx_CH_AV) == 0) - eieio(); /*barrier();*/ - return read_zsdata(chan); -} - -void kgdb_interruptible(int yes) -{ - struct mac_zschannel *chan = zs_kgdbchan; - int one, nine; - nine = read_zsreg(chan, 9); - if (yes == 1) { - one = EXT_INT_ENAB|INT_ALL_Rx; - nine |= MIE; - printk("turning serial ints on\n"); - } else { - one = RxINT_DISAB; - nine &= ~MIE; - printk("turning serial ints off\n"); - } - write_zsreg(chan, 1, one); - write_zsreg(chan, 9, nine); -} - -/* This sets up the serial port we're using, and turns on - * interrupts for that channel, so kgdb is usable once we're done. - */ -static inline void kgdb_chaninit(struct mac_zschannel *ms, int intson, int bps) -{ - int brg; - int i, x; - volatile char *sccc = ms->control; - brg = BPS_TO_BRG(bps, ZS_CLOCK/16); - printk("setting bps on kgdb line to %d [brg=%x]\n", bps, brg); - for (i = 20000; i != 0; --i) { - x = *sccc; eieio(); - } - for (i = 0; i < sizeof(scc_inittab); ++i) { - write_zsreg(ms, scc_inittab[i], scc_inittab[i+1]); - i++; - } -} - -/* This is called at boot time to prime the kgdb serial debugging - * serial line. The 'tty_num' argument is 0 for /dev/ttya and 1 - * for /dev/ttyb which is determined in setup_arch() from the - * boot command line flags. - * XXX at the moment probably only channel A will work - */ -void __init zs_kgdb_hook(int tty_num) -{ - /* Find out how many Z8530 SCCs we have */ - if (zs_chain == 0) - probe_sccs(); - - set_scc_power(&zs_soft[tty_num], 1); - - zs_kgdbchan = zs_soft[tty_num].zs_channel; - zs_soft[tty_num].change_needed = 0; - zs_soft[tty_num].clk_divisor = 16; - zs_soft[tty_num].zs_baud = 38400; - zs_soft[tty_num].kgdb_channel = 1; /* This runs kgdb */ - - /* Turn on transmitter/receiver at 8-bits/char */ - kgdb_chaninit(zs_soft[tty_num].zs_channel, 1, 38400); - printk("KGDB: on channel %d initialized\n", tty_num); - set_debug_traps(); /* init stub */ -} -#endif /* ifdef CONFIG_KGDB */ - -#ifdef CONFIG_PMAC_PBOOK -/* - * notify clients before sleep and reset bus afterwards - */ -int -serial_notify_sleep(struct pmu_sleep_notifier *self, int when) -{ - int i; - - switch (when) { - case PBOOK_SLEEP_REQUEST: - case PBOOK_SLEEP_REJECT: - break; - - case PBOOK_SLEEP_NOW: - for (i=0; i<zs_channels_found; i++) { - struct mac_serial *info = &zs_soft[i]; - if (info->flags & ZILOG_INITIALIZED) { - shutdown(info); - info->flags |= ZILOG_SLEEPING; - } - } - break; - case PBOOK_WAKE: - for (i=0; i<zs_channels_found; i++) { - struct mac_serial *info = &zs_soft[i]; - if (info->flags & ZILOG_SLEEPING) { - info->flags &= ~ZILOG_SLEEPING; - startup(info); - } - } - break; - } - return PBOOK_SLEEP_OK; -} -#endif /* CONFIG_PMAC_PBOOK */ diff --git a/drivers/macintosh/macserial.h b/drivers/macintosh/macserial.h deleted file mode 100644 index bade11a..0000000 --- a/drivers/macintosh/macserial.h +++ /dev/null @@ -1,461 +0,0 @@ -/* - * macserial.h: Definitions for the Macintosh Z8530 serial driver. - * - * Adapted from drivers/sbus/char/sunserial.h by Paul Mackerras. - * - * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au) - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - */ -#ifndef _MACSERIAL_H -#define _MACSERIAL_H - -#include <linux/spinlock.h> - -#define NUM_ZSREGS 16 - -struct serial_struct { - int type; - int line; - int port; - int irq; - int flags; - int xmit_fifo_size; - int custom_divisor; - int baud_base; - unsigned short close_delay; - char reserved_char[2]; - int hub6; - unsigned short closing_wait; /* time to wait before closing */ - unsigned short closing_wait2; /* no longer used... */ - int reserved[4]; -}; - -/* - * For the close wait times, 0 means wait forever for serial port to - * flush its output. 65535 means don't wait at all. - */ -#define ZILOG_CLOSING_WAIT_INF 0 -#define ZILOG_CLOSING_WAIT_NONE 65535 - -/* - * Definitions for ZILOG_struct (and serial_struct) flags field - */ -#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes - * on the callout port */ -#define ZILOG_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */ -#define ZILOG_SAK 0x0004 /* Secure Attention Key (Orange book) */ -#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */ - -#define ZILOG_SPD_MASK 0x0030 -#define ZILOG_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */ - -#define ZILOG_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */ -#define ZILOG_SPD_CUST 0x0030 /* Use user-specified divisor */ - -#define ZILOG_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */ -#define ZILOG_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */ -#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */ -#define ZILOG_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */ -#define ZILOG_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */ - -#define ZILOG_FLAGS 0x0FFF /* Possible legal ZILOG flags */ -#define ZILOG_USR_MASK 0x0430 /* Legal flags that non-privileged - * users can set or reset */ - -/* Internal flags used only by kernel/chr_drv/serial.c */ -#define ZILOG_INITIALIZED 0x80000000 /* Serial port was initialized */ -#define ZILOG_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */ -#define ZILOG_NORMAL_ACTIVE 0x20000000 /* Normal device is active */ -#define ZILOG_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */ -#define ZILOG_CLOSING 0x08000000 /* Serial port is closing */ -#define ZILOG_CTS_FLOW 0x04000000 /* Do CTS flow control */ -#define ZILOG_CHECK_CD 0x02000000 /* i.e., CLOCAL */ -#define ZILOG_SLEEPING 0x01000000 /* have shut it down for sleep */ - -/* Software state per channel */ - -#ifdef __KERNEL__ -/* - * This is our internal structure for each serial port's state. - * - * Many fields are paralleled by the structure used by the serial_struct - * structure. - * - * For definitions of the flags field, see tty.h - */ - -struct mac_serial; - -struct mac_zschannel { - volatile unsigned char* control; - volatile unsigned char* data; - spinlock_t lock; - /* Used for debugging */ - struct mac_serial* parent; -}; - -struct mac_dma { - volatile struct dbdma_regs dma; - volatile unsigned short res_count; - volatile unsigned short command; - volatile unsigned int buf_addr; -}; - -struct mac_serial { - struct mac_serial *zs_next; /* For IRQ servicing chain */ - struct mac_zschannel *zs_channel; /* Channel registers */ - struct mac_zschannel *zs_chan_a; /* A side registers */ - unsigned char read_reg_zero; - struct device_node* dev_node; - spinlock_t lock; - - char soft_carrier; /* Use soft carrier on this channel */ - char break_abort; /* Is serial console in, so process brk/abrt */ - char kgdb_channel; /* Kgdb is running on this channel */ - char is_cons; /* Is this our console. */ - char is_internal_modem; /* is connected to an internal modem */ - char is_irda; /* is connected to an IrDA codec */ - int port_type; /* Port type for pmac_feature */ - unsigned char tx_active; /* character is being xmitted */ - unsigned char tx_stopped; /* output is suspended */ - unsigned char power_wait; /* waiting for power-up delay to expire */ - - /* We need to know the current clock divisor - * to read the bps rate the chip has currently - * loaded. - */ - unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */ - int zs_baud; - - /* Current write register values */ - unsigned char curregs[NUM_ZSREGS]; - - /* Values we need to set next opportunity */ - unsigned char pendregs[NUM_ZSREGS]; - - char change_needed; - - int magic; - int baud_base; - int port; - int irq; - int flags; /* defined in tty.h */ - int type; /* UART type */ - struct tty_struct *tty; - int read_status_mask; - int ignore_status_mask; - int timeout; - int xmit_fifo_size; - int custom_divisor; - int x_char; /* xon/xoff character */ - int close_delay; - unsigned short closing_wait; - unsigned short closing_wait2; - unsigned long event; - unsigned long last_active; - int line; - int count; /* # of fd on device */ - int blocked_open; /* # of blocked opens */ - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; - struct work_struct tqueue; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - - volatile struct dbdma_regs *tx_dma; - int tx_dma_irq; - volatile struct dbdma_cmd *tx_cmds; - volatile struct mac_dma *rx; - int rx_dma_irq; - volatile struct dbdma_cmd **rx_cmds; - unsigned char **rx_char_buf; - unsigned char **rx_flag_buf; -#define RX_BUF_SIZE 256 - int rx_nbuf; - int rx_done_bytes; - int rx_ubuf; - int rx_fbuf; -#define RX_NO_FBUF (-1) - int rx_cbuf; - spinlock_t rx_dma_lock; - int has_dma; - int dma_initted; - void *dma_priv; - struct timer_list poll_dma_timer; -#define RX_DMA_TIMER (jiffies + 10*HZ/1000) - - struct timer_list powerup_timer; -}; - - -#define SERIAL_MAGIC 0x5301 - -/* - * The size of the serial xmit buffer is 1 page, or 4096 bytes - */ -#define SERIAL_XMIT_SIZE 4096 - -/* - * Events are used to schedule things to happen at timer-interrupt - * time, instead of at rs interrupt time. - */ -#define RS_EVENT_WRITE_WAKEUP 0 - -#endif /* __KERNEL__ */ - -/* Conversion routines to/from brg time constants from/to bits - * per second. - */ -#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2)) -#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2) - -/* The Zilog register set */ - -#define FLAG 0x7e - -/* Write Register 0 */ -#define R0 0 /* Register selects */ -#define R1 1 -#define R2 2 -#define R3 3 -#define R4 4 -#define R5 5 -#define R6 6 -#define R7 7 -#define R8 8 -#define R9 9 -#define R10 10 -#define R11 11 -#define R12 12 -#define R13 13 -#define R14 14 -#define R15 15 - -#define NULLCODE 0 /* Null Code */ -#define POINT_HIGH 0x8 /* Select upper half of registers */ -#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ -#define SEND_ABORT 0x18 /* HDLC Abort */ -#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ -#define RES_Tx_P 0x28 /* Reset TxINT Pending */ -#define ERR_RES 0x30 /* Error Reset */ -#define RES_H_IUS 0x38 /* Reset highest IUS */ - -#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ -#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ -#define RES_EOM_L 0xC0 /* Reset EOM latch */ - -/* Write Register 1 */ - -#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ -#define TxINT_ENAB 0x2 /* Tx Int Enable */ -#define PAR_SPEC 0x4 /* Parity is special condition */ - -#define RxINT_DISAB 0 /* Rx Int Disable */ -#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ -#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ -#define INT_ERR_Rx 0x18 /* Int on error only */ - -#define WT_RDY_RT 0x20 /* W/Req reflects recv if 1, xmit if 0 */ -#define WT_FN_RDYFN 0x40 /* W/Req pin is DMA request if 1, wait if 0 */ -#define WT_RDY_ENAB 0x80 /* Enable W/Req pin */ - -/* Write Register #2 (Interrupt Vector) */ - -/* Write Register 3 */ - -#define RxENABLE 0x1 /* Rx Enable */ -#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ -#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ -#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ -#define ENT_HM 0x10 /* Enter Hunt Mode */ -#define AUTO_ENAB 0x20 /* Auto Enables */ -#define Rx5 0x0 /* Rx 5 Bits/Character */ -#define Rx7 0x40 /* Rx 7 Bits/Character */ -#define Rx6 0x80 /* Rx 6 Bits/Character */ -#define Rx8 0xc0 /* Rx 8 Bits/Character */ -#define RxNBITS_MASK 0xc0 - -/* Write Register 4 */ - -#define PAR_ENA 0x1 /* Parity Enable */ -#define PAR_EVEN 0x2 /* Parity Even/Odd* */ - -#define SYNC_ENAB 0 /* Sync Modes Enable */ -#define SB1 0x4 /* 1 stop bit/char */ -#define SB15 0x8 /* 1.5 stop bits/char */ -#define SB2 0xc /* 2 stop bits/char */ -#define SB_MASK 0xc - -#define MONSYNC 0 /* 8 Bit Sync character */ -#define BISYNC 0x10 /* 16 bit sync character */ -#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ -#define EXTSYNC 0x30 /* External Sync Mode */ - -#define X1CLK 0x0 /* x1 clock mode */ -#define X16CLK 0x40 /* x16 clock mode */ -#define X32CLK 0x80 /* x32 clock mode */ -#define X64CLK 0xC0 /* x64 clock mode */ -#define XCLK_MASK 0xC0 - -/* Write Register 5 */ - -#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ -#define RTS 0x2 /* RTS */ -#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ -#define TxENAB 0x8 /* Tx Enable */ -#define SND_BRK 0x10 /* Send Break */ -#define Tx5 0x0 /* Tx 5 bits (or less)/character */ -#define Tx7 0x20 /* Tx 7 bits/character */ -#define Tx6 0x40 /* Tx 6 bits/character */ -#define Tx8 0x60 /* Tx 8 bits/character */ -#define TxNBITS_MASK 0x60 -#define DTR 0x80 /* DTR */ - -/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ - -/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ - -/* Write Register 7' (Some enhanced feature control) */ -#define ENEXREAD 0x40 /* Enable read of some write registers */ - -/* Write Register 8 (transmit buffer) */ - -/* Write Register 9 (Master interrupt control) */ -#define VIS 1 /* Vector Includes Status */ -#define NV 2 /* No Vector */ -#define DLC 4 /* Disable Lower Chain */ -#define MIE 8 /* Master Interrupt Enable */ -#define STATHI 0x10 /* Status high */ -#define NORESET 0 /* No reset on write to R9 */ -#define CHRB 0x40 /* Reset channel B */ -#define CHRA 0x80 /* Reset channel A */ -#define FHWRES 0xc0 /* Force hardware reset */ - -/* Write Register 10 (misc control bits) */ -#define BIT6 1 /* 6 bit/8bit sync */ -#define LOOPMODE 2 /* SDLC Loop mode */ -#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ -#define MARKIDLE 8 /* Mark/flag on idle */ -#define GAOP 0x10 /* Go active on poll */ -#define NRZ 0 /* NRZ mode */ -#define NRZI 0x20 /* NRZI mode */ -#define FM1 0x40 /* FM1 (transition = 1) */ -#define FM0 0x60 /* FM0 (transition = 0) */ -#define CRCPS 0x80 /* CRC Preset I/O */ - -/* Write Register 11 (Clock Mode control) */ -#define TRxCXT 0 /* TRxC = Xtal output */ -#define TRxCTC 1 /* TRxC = Transmit clock */ -#define TRxCBR 2 /* TRxC = BR Generator Output */ -#define TRxCDP 3 /* TRxC = DPLL output */ -#define TRxCOI 4 /* TRxC O/I */ -#define TCRTxCP 0 /* Transmit clock = RTxC pin */ -#define TCTRxCP 8 /* Transmit clock = TRxC pin */ -#define TCBR 0x10 /* Transmit clock = BR Generator output */ -#define TCDPLL 0x18 /* Transmit clock = DPLL output */ -#define RCRTxCP 0 /* Receive clock = RTxC pin */ -#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ -#define RCBR 0x40 /* Receive clock = BR Generator output */ -#define RCDPLL 0x60 /* Receive clock = DPLL output */ -#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ - -/* Write Register 12 (lower byte of baud rate generator time constant) */ - -/* Write Register 13 (upper byte of baud rate generator time constant) */ - -/* Write Register 14 (Misc control bits) */ -#define BRENABL 1 /* Baud rate generator enable */ -#define BRSRC 2 /* Baud rate generator source */ -#define DTRREQ 4 /* DTR/Request function */ -#define AUTOECHO 8 /* Auto Echo */ -#define LOOPBAK 0x10 /* Local loopback */ -#define SEARCH 0x20 /* Enter search mode */ -#define RMC 0x40 /* Reset missing clock */ -#define DISDPLL 0x60 /* Disable DPLL */ -#define SSBR 0x80 /* Set DPLL source = BR generator */ -#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ -#define SFMM 0xc0 /* Set FM mode */ -#define SNRZI 0xe0 /* Set NRZI mode */ - -/* Write Register 15 (external/status interrupt control) */ -#define EN85C30 1 /* Enable some 85c30-enhanced registers */ -#define ZCIE 2 /* Zero count IE */ -#define ENSTFIFO 4 /* Enable status FIFO (SDLC) */ -#define DCDIE 8 /* DCD IE */ -#define SYNCIE 0x10 /* Sync/hunt IE */ -#define CTSIE 0x20 /* CTS IE */ -#define TxUIE 0x40 /* Tx Underrun/EOM IE */ -#define BRKIE 0x80 /* Break/Abort IE */ - - -/* Read Register 0 */ -#define Rx_CH_AV 0x1 /* Rx Character Available */ -#define ZCOUNT 0x2 /* Zero count */ -#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ -#define DCD 0x8 /* DCD */ -#define SYNC_HUNT 0x10 /* Sync/hunt */ -#define CTS 0x20 /* CTS */ -#define TxEOM 0x40 /* Tx underrun */ -#define BRK_ABRT 0x80 /* Break/Abort */ - -/* Read Register 1 */ -#define ALL_SNT 0x1 /* All sent */ -/* Residue Data for 8 Rx bits/char programmed */ -#define RES3 0x8 /* 0/3 */ -#define RES4 0x4 /* 0/4 */ -#define RES5 0xc /* 0/5 */ -#define RES6 0x2 /* 0/6 */ -#define RES7 0xa /* 0/7 */ -#define RES8 0x6 /* 0/8 */ -#define RES18 0xe /* 1/8 */ -#define RES28 0x0 /* 2/8 */ -/* Special Rx Condition Interrupts */ -#define PAR_ERR 0x10 /* Parity error */ -#define Rx_OVR 0x20 /* Rx Overrun Error */ -#define FRM_ERR 0x40 /* CRC/Framing Error */ -#define END_FR 0x80 /* End of Frame (SDLC) */ - -/* Read Register 2 (channel b only) - Interrupt vector */ -#define CHB_Tx_EMPTY 0x00 -#define CHB_EXT_STAT 0x02 -#define CHB_Rx_AVAIL 0x04 -#define CHB_SPECIAL 0x06 -#define CHA_Tx_EMPTY 0x08 -#define CHA_EXT_STAT 0x0a -#define CHA_Rx_AVAIL 0x0c -#define CHA_SPECIAL 0x0e -#define STATUS_MASK 0x06 - -/* Read Register 3 (interrupt pending register) ch a only */ -#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ -#define CHBTxIP 0x2 /* Channel B Tx IP */ -#define CHBRxIP 0x4 /* Channel B Rx IP */ -#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ -#define CHATxIP 0x10 /* Channel A Tx IP */ -#define CHARxIP 0x20 /* Channel A Rx IP */ - -/* Read Register 8 (receive data register) */ - -/* Read Register 10 (misc status bits) */ -#define ONLOOP 2 /* On loop */ -#define LOOPSEND 0x10 /* Loop sending */ -#define CLK2MIS 0x40 /* Two clocks missing */ -#define CLK1MIS 0x80 /* One clock missing */ - -/* Read Register 12 (lower byte of baud rate generator constant) */ - -/* Read Register 13 (upper byte of baud rate generator constant) */ - -/* Read Register 15 (value of WR 15) */ - -/* Misc macros */ -#define ZS_CLEARERR(channel) (write_zsreg(channel, 0, ERR_RES)) -#define ZS_CLEARFIFO(channel) do { volatile unsigned char garbage; \ - garbage = read_zsdata(channel); \ - garbage = read_zsdata(channel); \ - garbage = read_zsdata(channel); \ - } while(0) - -#endif /* !(_MACSERIAL_H) */ diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index b941ee2..4a0a0ad 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -63,6 +63,10 @@ #include <asm/backlight.h> #endif +#ifdef CONFIG_PPC32 +#include <asm/open_pic.h> +#endif + /* Some compile options */ #undef SUSPEND_USES_PMU #define DEBUG_SLEEP @@ -151,10 +155,10 @@ static spinlock_t pmu_lock; static u8 pmu_intr_mask; static int pmu_version; static int drop_interrupts; -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM static int option_lid_wakeup = 1; static int sleep_in_progress; -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PM */ static unsigned long async_req_locks; static unsigned int pmu_irq_stats[11]; @@ -164,7 +168,6 @@ static struct proc_dir_entry *proc_pmu_irqstats; static struct proc_dir_entry *proc_pmu_options; static int option_server_mode; -#ifdef CONFIG_PMAC_PBOOK int pmu_battery_count; int pmu_cur_battery; unsigned int pmu_power_flags; @@ -172,7 +175,6 @@ struct pmu_battery_info pmu_batteries[PMU_MAX_BATTERIES]; static int query_batt_timer = BATTERY_POLLING_COUNT; static struct adb_request batt_req; static struct proc_dir_entry *proc_pmu_batt[PMU_MAX_BATTERIES]; -#endif /* CONFIG_PMAC_PBOOK */ #if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) extern int disable_kernel_backlight; @@ -206,11 +208,9 @@ static int proc_get_irqstats(char *page, char **start, off_t off, static int pmu_set_backlight_level(int level, void* data); static int pmu_set_backlight_enable(int on, int level, void* data); #endif /* CONFIG_PMAC_BACKLIGHT */ -#ifdef CONFIG_PMAC_PBOOK static void pmu_pass_intr(unsigned char *data, int len); static int proc_get_batt(char *page, char **start, off_t off, int count, int *eof, void *data); -#endif /* CONFIG_PMAC_PBOOK */ static int proc_read_options(char *page, char **start, off_t off, int count, int *eof, void *data); static int proc_write_options(struct file *file, const char __user *buffer, @@ -403,8 +403,12 @@ static int __init via_pmu_start(void) bright_req_1.complete = 1; bright_req_2.complete = 1; -#ifdef CONFIG_PMAC_PBOOK batt_req.complete = 1; + +#ifdef CONFIG_PPC32 + if (pmu_kind == PMU_KEYLARGO_BASED) + openpic_set_irq_priority(vias->intrs[0].line, + OPENPIC_PRIORITY_DEFAULT + 1); #endif if (request_irq(vias->intrs[0].line, via_pmu_interrupt, 0, "VIA-PMU", @@ -458,7 +462,7 @@ static int __init via_pmu_dev_init(void) register_backlight_controller(&pmu_backlight_controller, NULL, "pmu"); #endif /* CONFIG_PMAC_BACKLIGHT */ -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PPC32 if (machine_is_compatible("AAPL,3400/2400") || machine_is_compatible("AAPL,3500")) { int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO, @@ -486,20 +490,19 @@ static int __init via_pmu_dev_init(void) pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART; } } -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PPC32 */ + /* Create /proc/pmu */ proc_pmu_root = proc_mkdir("pmu", NULL); if (proc_pmu_root) { -#ifdef CONFIG_PMAC_PBOOK - int i; + long i; for (i=0; i<pmu_battery_count; i++) { char title[16]; - sprintf(title, "battery_%d", i); + sprintf(title, "battery_%ld", i); proc_pmu_batt[i] = create_proc_read_entry(title, 0, proc_pmu_root, proc_get_batt, (void *)i); } -#endif /* CONFIG_PMAC_PBOOK */ proc_pmu_info = create_proc_read_entry("info", 0, proc_pmu_root, proc_get_info, NULL); @@ -619,8 +622,6 @@ static void pmu_set_server_mode(int server_mode) pmu_wait_complete(&req); } -#ifdef CONFIG_PMAC_PBOOK - /* This new version of the code for 2400/3400/3500 powerbooks * is inspired from the implementation in gkrellm-pmu */ @@ -803,8 +804,6 @@ query_battery_state(void) 2, PMU_SMART_BATTERY_STATE, pmu_cur_battery+1); } -#endif /* CONFIG_PMAC_PBOOK */ - static int __pmac proc_get_info(char *page, char **start, off_t off, int count, int *eof, void *data) @@ -813,11 +812,9 @@ proc_get_info(char *page, char **start, off_t off, p += sprintf(p, "PMU driver version : %d\n", PMU_DRIVER_VERSION); p += sprintf(p, "PMU firmware version : %02x\n", pmu_version); -#ifdef CONFIG_PMAC_PBOOK p += sprintf(p, "AC Power : %d\n", ((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0)); p += sprintf(p, "Battery count : %d\n", pmu_battery_count); -#endif /* CONFIG_PMAC_PBOOK */ return p - page; } @@ -849,12 +846,11 @@ proc_get_irqstats(char *page, char **start, off_t off, return p - page; } -#ifdef CONFIG_PMAC_PBOOK static int __pmac proc_get_batt(char *page, char **start, off_t off, int count, int *eof, void *data) { - int batnum = (int)data; + long batnum = (long)data; char *p = page; p += sprintf(p, "\n"); @@ -873,7 +869,6 @@ proc_get_batt(char *page, char **start, off_t off, return p - page; } -#endif /* CONFIG_PMAC_PBOOK */ static int __pmac proc_read_options(char *page, char **start, off_t off, @@ -881,11 +876,11 @@ proc_read_options(char *page, char **start, off_t off, { char *p = page; -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM if (pmu_kind == PMU_KEYLARGO_BASED && pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0) p += sprintf(p, "lid_wakeup=%d\n", option_lid_wakeup); -#endif /* CONFIG_PMAC_PBOOK */ +#endif if (pmu_kind == PMU_KEYLARGO_BASED) p += sprintf(p, "server_mode=%d\n", option_server_mode); @@ -922,12 +917,12 @@ proc_write_options(struct file *file, const char __user *buffer, *(val++) = 0; while(*val == ' ') val++; -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM if (pmu_kind == PMU_KEYLARGO_BASED && pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0) if (!strcmp(label, "lid_wakeup")) option_lid_wakeup = ((*val) == '1'); -#endif /* CONFIG_PMAC_PBOOK */ +#endif if (pmu_kind == PMU_KEYLARGO_BASED && !strcmp(label, "server_mode")) { int new_value; new_value = ((*val) == '1'); @@ -1422,7 +1417,6 @@ next: } /* Tick interrupt */ else if ((1 << pirq) & PMU_INT_TICK) { -#ifdef CONFIG_PMAC_PBOOK /* Environement or tick interrupt, query batteries */ if (pmu_battery_count) { if ((--query_batt_timer) == 0) { @@ -1437,7 +1431,6 @@ next: pmu_pass_intr(data, len); } else { pmu_pass_intr(data, len); -#endif /* CONFIG_PMAC_PBOOK */ } goto next; } @@ -2052,7 +2045,7 @@ pmu_i2c_simple_write(int bus, int addr, u8* data, int len) return -1; } -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM static LIST_HEAD(sleep_notifiers); @@ -2705,6 +2698,8 @@ powerbook_sleep_3400(void) return 0; } +#endif /* CONFIG_PM */ + /* * Support for /dev/pmu device */ @@ -2884,11 +2879,11 @@ static int __pmac pmu_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) { - struct pmu_private *pp = filp->private_data; __u32 __user *argp = (__u32 __user *)arg; - int error; + int error = -EINVAL; switch (cmd) { +#ifdef CONFIG_PM case PMU_IOC_SLEEP: if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -2910,12 +2905,13 @@ pmu_ioctl(struct inode * inode, struct file *filp, error = -ENOSYS; } sleep_in_progress = 0; - return error; + break; case PMU_IOC_CAN_SLEEP: if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) < 0) return put_user(0, argp); else return put_user(1, argp); +#endif /* CONFIG_PM */ #ifdef CONFIG_PMAC_BACKLIGHT /* Backlight should have its own device or go via @@ -2936,11 +2932,13 @@ pmu_ioctl(struct inode * inode, struct file *filp, error = get_user(value, argp); if (!error) error = set_backlight_level(value); - return error; + break; } #ifdef CONFIG_INPUT_ADBHID case PMU_IOC_GRAB_BACKLIGHT: { + struct pmu_private *pp = filp->private_data; unsigned long flags; + if (pp->backlight_locker) return 0; pp->backlight_locker = 1; @@ -2956,7 +2954,7 @@ pmu_ioctl(struct inode * inode, struct file *filp, case PMU_IOC_HAS_ADB: return put_user(pmu_has_adb, argp); } - return -EINVAL; + return error; } static struct file_operations pmu_device_fops __pmacdata = { @@ -2972,14 +2970,16 @@ static struct miscdevice pmu_device __pmacdata = { PMU_MINOR, "pmu", &pmu_device_fops }; -void pmu_device_init(void) +static int pmu_device_init(void) { if (!via) - return; + return 0; if (misc_register(&pmu_device) < 0) printk(KERN_ERR "via-pmu: cannot register misc device.\n"); + return 0; } -#endif /* CONFIG_PMAC_PBOOK */ +device_initcall(pmu_device_init); + #ifdef DEBUG_SLEEP static inline void __pmac @@ -3147,12 +3147,12 @@ EXPORT_SYMBOL(pmu_i2c_combined_read); EXPORT_SYMBOL(pmu_i2c_stdsub_write); EXPORT_SYMBOL(pmu_i2c_simple_read); EXPORT_SYMBOL(pmu_i2c_simple_write); -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM EXPORT_SYMBOL(pmu_register_sleep_notifier); EXPORT_SYMBOL(pmu_unregister_sleep_notifier); EXPORT_SYMBOL(pmu_enable_irled); EXPORT_SYMBOL(pmu_battery_count); EXPORT_SYMBOL(pmu_batteries); EXPORT_SYMBOL(pmu_power_flags); -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PM */ diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index f9383e7..1b70f8b 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -7,7 +7,7 @@ menu "Video For Linux" comment "Video Adapters" -config CONFIG_TUNER_MULTI_I2C +config TUNER_MULTI_I2C bool "Enable support for multiple I2C devices on Video Adapters (EXPERIMENTAL)" depends on VIDEO_DEV && EXPERIMENTAL ---help--- diff --git a/drivers/net/3c523.c b/drivers/net/3c523.c index 1247a25..9e1fe2e 100644 --- a/drivers/net/3c523.c +++ b/drivers/net/3c523.c @@ -1274,6 +1274,7 @@ module_param_array(irq, int, NULL, 0); module_param_array(io, int, NULL, 0); MODULE_PARM_DESC(io, "EtherLink/MC I/O base address(es)"); MODULE_PARM_DESC(irq, "EtherLink/MC IRQ number(s)"); +MODULE_LICENSE("GPL"); int init_module(void) { diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 47e158f..2b55687 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1320,7 +1320,7 @@ config FORCEDETH config CS89x0 tristate "CS89x0 support" - depends on NET_PCI && (ISA || ARCH_IXDP2X01) + depends on (NET_PCI && (ISA || ARCH_IXDP2X01)) || ARCH_PNX0105 ---help--- Support for CS89x0 chipset based Ethernet cards. If you have a network (Ethernet) card of this type, say Y and read the diff --git a/drivers/net/b44.c b/drivers/net/b44.c index 3fe8ba9..f1bd45e 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -1285,6 +1285,9 @@ static int b44_open(struct net_device *dev) b44_init_hw(bp); bp->flags |= B44_FLAG_INIT_COMPLETE; + netif_carrier_off(dev); + b44_check_phy(bp); + spin_unlock_irq(&bp->lock); init_timer(&bp->timer); diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c index 25e4495..b96d6fb 100644 --- a/drivers/net/cs89x0.c +++ b/drivers/net/cs89x0.c @@ -174,6 +174,13 @@ static unsigned int cs8900_irq_map[] = {1,0,0,0}; #include <asm/irq.h> static unsigned int netcard_portlist[] __initdata = {IXDP2X01_CS8900_VIRT_BASE, 0}; static unsigned int cs8900_irq_map[] = {IRQ_IXDP2X01_CS8900, 0, 0, 0}; +#elif defined(CONFIG_ARCH_PNX0105) +#include <asm/irq.h> +#include <asm/arch/gpio.h> +#define CIRRUS_DEFAULT_BASE IO_ADDRESS(EXT_STATIC2_s0_BASE + 0x200000) /* = Physical address 0x48200000 */ +#define CIRRUS_DEFAULT_IRQ VH_INTC_INT_NUM_CASCADED_INTERRUPT_1 /* Event inputs bank 1 - ID 35/bit 3 */ +static unsigned int netcard_portlist[] __initdata = {CIRRUS_DEFAULT_BASE, 0}; +static unsigned int cs8900_irq_map[] = {CIRRUS_DEFAULT_IRQ, 0, 0, 0}; #else static unsigned int netcard_portlist[] __initdata = { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0}; @@ -431,6 +438,30 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular) #endif } +#ifdef CONFIG_ARCH_PNX0105 + initialize_ebi(); + + /* Map GPIO registers for the pins connected to the CS8900a. */ + if (map_cirrus_gpio() < 0) + return -ENODEV; + + reset_cirrus(); + + /* Map event-router registers. */ + if (map_event_router() < 0) + return -ENODEV; + + enable_cirrus_irq(); + + unmap_cirrus_gpio(); + unmap_event_router(); + + dev->base_addr = ioaddr; + + for (i = 0 ; i < 3 ; i++) + readreg(dev, 0); +#endif + /* Grab the region so we can find another board if autoIRQ fails. */ /* WTF is going on here? */ if (!request_region(ioaddr & ~3, NETCARD_IO_EXTENT, DRV_NAME)) { @@ -672,7 +703,7 @@ printk("PP_addr=0x%x\n", inw(ioaddr + ADD_PORT)); } else { i = lp->isa_config & INT_NO_MASK; if (lp->chip_type == CS8900) { -#ifdef CONFIG_ARCH_IXDP2X01 +#if defined(CONFIG_ARCH_IXDP2X01) || defined(CONFIG_ARCH_PNX0105) i = cs8900_irq_map[0]; #else /* Translate the IRQ using the IRQ mapping table. */ @@ -1145,7 +1176,7 @@ net_open(struct net_device *dev) int i; int ret; -#ifndef CONFIG_SH_HICOSH4 /* uses irq#1, so this won't work */ +#if !defined(CONFIG_SH_HICOSH4) && !defined(CONFIG_ARCH_PNX0105) /* uses irq#1, so this won't work */ if (dev->irq < 2) { /* Allow interrupts to be generated by the chip */ /* Cirrus' release had this: */ @@ -1176,7 +1207,7 @@ net_open(struct net_device *dev) else #endif { -#ifndef CONFIG_ARCH_IXDP2X01 +#if !defined(CONFIG_ARCH_IXDP2X01) && !defined(CONFIG_ARCH_PNX0105) if (((1 << dev->irq) & lp->irq_map) == 0) { printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n", dev->name, dev->irq, lp->irq_map); @@ -1261,6 +1292,9 @@ net_open(struct net_device *dev) case A_CNF_MEDIA_10B_2: result = lp->adapter_cnf & A_CNF_10B_2; break; default: result = lp->adapter_cnf & (A_CNF_10B_T | A_CNF_AUI | A_CNF_10B_2); } +#ifdef CONFIG_ARCH_PNX0105 + result = A_CNF_10B_T; +#endif if (!result) { printk(KERN_ERR "%s: EEPROM is configured for unavailable media\n", dev->name); release_irq: diff --git a/drivers/net/cs89x0.h b/drivers/net/cs89x0.h index b0ef7ad..bd3ad8e 100644 --- a/drivers/net/cs89x0.h +++ b/drivers/net/cs89x0.h @@ -16,7 +16,7 @@ #include <linux/config.h> -#ifdef CONFIG_ARCH_IXDP2X01 +#if defined(CONFIG_ARCH_IXDP2X01) || defined(CONFIG_ARCH_PNX0105) /* IXDP2401/IXDP2801 uses dword-aligned register addressing */ #define CS89x0_PORT(reg) ((reg) * 2) #else diff --git a/drivers/net/e100.c b/drivers/net/e100.c index cfaa6b2..1e56c8e 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -1093,11 +1093,16 @@ static int e100_phy_init(struct nic *nic) } if((nic->mac >= mac_82550_D102) || ((nic->flags & ich) && - (mdio_read(netdev, nic->mii.phy_id, MII_TPISTATUS) & 0x8000) && - (nic->eeprom[eeprom_cnfg_mdix] & eeprom_mdix_enabled))) - /* enable/disable MDI/MDI-X auto-switching */ - mdio_write(netdev, nic->mii.phy_id, MII_NCONFIG, - nic->mii.force_media ? 0 : NCONFIG_AUTO_SWITCH); + (mdio_read(netdev, nic->mii.phy_id, MII_TPISTATUS) & 0x8000))) { + /* enable/disable MDI/MDI-X auto-switching. + MDI/MDI-X auto-switching is disabled for 82551ER/QM chips */ + if((nic->mac == mac_82551_E) || (nic->mac == mac_82551_F) || + (nic->mac == mac_82551_10) || (nic->mii.force_media) || + !(nic->eeprom[eeprom_cnfg_mdix] & eeprom_mdix_enabled)) + mdio_write(netdev, nic->mii.phy_id, MII_NCONFIG, 0); + else + mdio_write(netdev, nic->mii.phy_id, MII_NCONFIG, NCONFIG_AUTO_SWITCH); + } return 0; } @@ -1666,8 +1671,10 @@ static irqreturn_t e100_intr(int irq, void *dev_id, struct pt_regs *regs) if(stat_ack & stat_ack_rnr) nic->ru_running = RU_SUSPENDED; - e100_disable_irq(nic); - netif_rx_schedule(netdev); + if(likely(netif_rx_schedule_prep(netdev))) { + e100_disable_irq(nic); + __netif_rx_schedule(netdev); + } return IRQ_HANDLED; } @@ -2335,11 +2342,11 @@ static int __devinit e100_probe(struct pci_dev *pdev, goto err_out_iounmap; } - e100_phy_init(nic); - if((err = e100_eeprom_load(nic))) goto err_out_free; + e100_phy_init(nic); + memcpy(netdev->dev_addr, nic->eeprom, ETH_ALEN); if(!is_valid_ether_addr(netdev->dev_addr)) { DPRINTK(PROBE, ERR, "Invalid MAC address from " diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index af1e82c..092757b 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h @@ -140,7 +140,7 @@ struct e1000_adapter; #define E1000_RX_BUFFER_WRITE 16 /* Must be power of 2 */ #define AUTO_ALL_MODES 0 -#define E1000_EEPROM_82544_APM 0x0400 +#define E1000_EEPROM_82544_APM 0x0004 #define E1000_EEPROM_APME 0x0400 #ifndef E1000_MASTER_SLAVE @@ -159,7 +159,7 @@ struct e1000_adapter; * so a DMA handle can be stored along with the buffer */ struct e1000_buffer { struct sk_buff *skb; - uint64_t dma; + dma_addr_t dma; unsigned long time_stamp; uint16_t length; uint16_t next_to_watch; diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index 237247f..f133ff0 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c @@ -105,7 +105,7 @@ static const char e1000_gstrings_test[][ETH_GSTRING_LEN] = { static int e1000_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; if(hw->media_type == e1000_media_type_copper) { @@ -141,9 +141,9 @@ e1000_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) SUPPORTED_FIBRE | SUPPORTED_Autoneg); - ecmd->advertising = (SUPPORTED_1000baseT_Full | - SUPPORTED_FIBRE | - SUPPORTED_Autoneg); + ecmd->advertising = (ADVERTISED_1000baseT_Full | + ADVERTISED_FIBRE | + ADVERTISED_Autoneg); ecmd->port = PORT_FIBRE; @@ -179,13 +179,24 @@ e1000_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) static int e1000_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; if(ecmd->autoneg == AUTONEG_ENABLE) { hw->autoneg = 1; - hw->autoneg_advertised = 0x002F; - ecmd->advertising = 0x002F; + if(hw->media_type == e1000_media_type_fiber) + hw->autoneg_advertised = ADVERTISED_1000baseT_Full | + ADVERTISED_FIBRE | + ADVERTISED_Autoneg; + else + hw->autoneg_advertised = ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Full| + ADVERTISED_Autoneg | + ADVERTISED_TP; + ecmd->advertising = hw->autoneg_advertised; } else if(e1000_set_spd_dplx(adapter, ecmd->speed + ecmd->duplex)) return -EINVAL; @@ -206,7 +217,7 @@ static void e1000_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; pause->autoneg = @@ -226,7 +237,7 @@ static int e1000_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; adapter->fc_autoneg = pause->autoneg; @@ -259,14 +270,14 @@ e1000_set_pauseparam(struct net_device *netdev, static uint32_t e1000_get_rx_csum(struct net_device *netdev) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); return adapter->rx_csum; } static int e1000_set_rx_csum(struct net_device *netdev, uint32_t data) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); adapter->rx_csum = data; if(netif_running(netdev)) { @@ -286,7 +297,7 @@ e1000_get_tx_csum(struct net_device *netdev) static int e1000_set_tx_csum(struct net_device *netdev, uint32_t data) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); if(adapter->hw.mac_type < e1000_82543) { if (!data) @@ -306,8 +317,8 @@ e1000_set_tx_csum(struct net_device *netdev, uint32_t data) static int e1000_set_tso(struct net_device *netdev, uint32_t data) { - struct e1000_adapter *adapter = netdev->priv; - if ((adapter->hw.mac_type < e1000_82544) || + struct e1000_adapter *adapter = netdev_priv(netdev); + if((adapter->hw.mac_type < e1000_82544) || (adapter->hw.mac_type == e1000_82547)) return data ? -EINVAL : 0; @@ -322,14 +333,14 @@ e1000_set_tso(struct net_device *netdev, uint32_t data) static uint32_t e1000_get_msglevel(struct net_device *netdev) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); return adapter->msg_enable; } static void e1000_set_msglevel(struct net_device *netdev, uint32_t data) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); adapter->msg_enable = data; } @@ -344,7 +355,7 @@ static void e1000_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; uint32_t *regs_buff = p; uint16_t phy_data; @@ -432,7 +443,7 @@ e1000_get_regs(struct net_device *netdev, static int e1000_get_eeprom_len(struct net_device *netdev) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); return adapter->hw.eeprom.word_size * 2; } @@ -440,7 +451,7 @@ static int e1000_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, uint8_t *bytes) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; uint16_t *eeprom_buff; int first_word, last_word; @@ -486,7 +497,7 @@ static int e1000_set_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, uint8_t *bytes) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; uint16_t *eeprom_buff; void *ptr; @@ -547,7 +558,7 @@ static void e1000_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); strncpy(drvinfo->driver, e1000_driver_name, 32); strncpy(drvinfo->version, e1000_driver_version, 32); @@ -563,7 +574,7 @@ static void e1000_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); e1000_mac_type mac_type = adapter->hw.mac_type; struct e1000_desc_ring *txdr = &adapter->tx_ring; struct e1000_desc_ring *rxdr = &adapter->rx_ring; @@ -584,7 +595,7 @@ static int e1000_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); e1000_mac_type mac_type = adapter->hw.mac_type; struct e1000_desc_ring *txdr = &adapter->tx_ring; struct e1000_desc_ring *rxdr = &adapter->rx_ring; @@ -651,6 +662,9 @@ err_setup_rx: E1000_WRITE_REG(&adapter->hw, R, (test[pat] & W)); \ value = E1000_READ_REG(&adapter->hw, R); \ if(value != (test[pat] & W & M)) { \ + DPRINTK(DRV, ERR, "pattern test reg %04X failed: got " \ + "0x%08X expected 0x%08X\n", \ + E1000_##R, value, (test[pat] & W & M)); \ *data = (adapter->hw.mac_type < e1000_82543) ? \ E1000_82542_##R : E1000_##R; \ return 1; \ @@ -663,7 +677,9 @@ err_setup_rx: uint32_t value; \ E1000_WRITE_REG(&adapter->hw, R, W & M); \ value = E1000_READ_REG(&adapter->hw, R); \ - if ((W & M) != (value & M)) { \ + if((W & M) != (value & M)) { \ + DPRINTK(DRV, ERR, "set/check reg %04X test failed: got 0x%08X "\ + "expected 0x%08X\n", E1000_##R, (value & M), (W & M)); \ *data = (adapter->hw.mac_type < e1000_82543) ? \ E1000_82542_##R : E1000_##R; \ return 1; \ @@ -673,18 +689,33 @@ err_setup_rx: static int e1000_reg_test(struct e1000_adapter *adapter, uint64_t *data) { - uint32_t value; - uint32_t i; + uint32_t value, before, after; + uint32_t i, toggle; /* The status register is Read Only, so a write should fail. * Some bits that get toggled are ignored. */ - value = (E1000_READ_REG(&adapter->hw, STATUS) & (0xFFFFF833)); - E1000_WRITE_REG(&adapter->hw, STATUS, (0xFFFFFFFF)); - if(value != (E1000_READ_REG(&adapter->hw, STATUS) & (0xFFFFF833))) { + switch (adapter->hw.mac_type) { + case e1000_82573: + toggle = 0x7FFFF033; + break; + default: + toggle = 0xFFFFF833; + break; + } + + before = E1000_READ_REG(&adapter->hw, STATUS); + value = (E1000_READ_REG(&adapter->hw, STATUS) & toggle); + E1000_WRITE_REG(&adapter->hw, STATUS, toggle); + after = E1000_READ_REG(&adapter->hw, STATUS) & toggle; + if(value != after) { + DPRINTK(DRV, ERR, "failed STATUS register test got: " + "0x%08X expected: 0x%08X\n", after, value); *data = 1; return 1; } + /* restore previous status */ + E1000_WRITE_REG(&adapter->hw, STATUS, before); REG_PATTERN_TEST(FCAL, 0xFFFFFFFF, 0xFFFFFFFF); REG_PATTERN_TEST(FCAH, 0x0000FFFF, 0xFFFFFFFF); @@ -766,7 +797,7 @@ e1000_test_intr(int irq, struct pt_regs *regs) { struct net_device *netdev = (struct net_device *) data; - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); adapter->test_icr |= E1000_READ_REG(&adapter->hw, ICR); @@ -1214,6 +1245,7 @@ e1000_set_phy_loopback(struct e1000_adapter *adapter) case e1000_82541_rev_2: case e1000_82547: case e1000_82547_rev_2: + case e1000_82573: return e1000_integrated_phy_loopback(adapter); break; @@ -1422,7 +1454,7 @@ static void e1000_diag_test(struct net_device *netdev, struct ethtool_test *eth_test, uint64_t *data) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); boolean_t if_running = netif_running(netdev); if(eth_test->flags == ETH_TEST_FL_OFFLINE) { @@ -1482,7 +1514,7 @@ e1000_diag_test(struct net_device *netdev, static void e1000_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; switch(adapter->hw.device_id) { @@ -1527,7 +1559,7 @@ e1000_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) static int e1000_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; switch(adapter->hw.device_id) { @@ -1588,22 +1620,31 @@ e1000_led_blink_callback(unsigned long data) static int e1000_phys_id(struct net_device *netdev, uint32_t data) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); if(!data || data > (uint32_t)(MAX_SCHEDULE_TIMEOUT / HZ)) data = (uint32_t)(MAX_SCHEDULE_TIMEOUT / HZ); - if(!adapter->blink_timer.function) { - init_timer(&adapter->blink_timer); - adapter->blink_timer.function = e1000_led_blink_callback; - adapter->blink_timer.data = (unsigned long) adapter; + if(adapter->hw.mac_type < e1000_82573) { + if(!adapter->blink_timer.function) { + init_timer(&adapter->blink_timer); + adapter->blink_timer.function = e1000_led_blink_callback; + adapter->blink_timer.data = (unsigned long) adapter; + } + e1000_setup_led(&adapter->hw); + mod_timer(&adapter->blink_timer, jiffies); + msleep_interruptible(data * 1000); + del_timer_sync(&adapter->blink_timer); + } + else { + E1000_WRITE_REG(&adapter->hw, LEDCTL, (E1000_LEDCTL_LED2_BLINK_RATE | + E1000_LEDCTL_LED1_BLINK | E1000_LEDCTL_LED2_BLINK | + (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED2_MODE_SHIFT) | + (E1000_LEDCTL_MODE_LINK_ACTIVITY << E1000_LEDCTL_LED1_MODE_SHIFT) | + (E1000_LEDCTL_MODE_LED_OFF << E1000_LEDCTL_LED0_MODE_SHIFT))); + msleep_interruptible(data * 1000); } - e1000_setup_led(&adapter->hw); - mod_timer(&adapter->blink_timer, jiffies); - - msleep_interruptible(data * 1000); - del_timer_sync(&adapter->blink_timer); e1000_led_off(&adapter->hw); clear_bit(E1000_LED_ON, &adapter->led_status); e1000_cleanup_led(&adapter->hw); @@ -1614,7 +1655,7 @@ e1000_phys_id(struct net_device *netdev, uint32_t data) static int e1000_nway_reset(struct net_device *netdev) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); if(netif_running(netdev)) { e1000_down(adapter); e1000_up(adapter); @@ -1632,7 +1673,7 @@ static void e1000_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats, uint64_t *data) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); int i; e1000_update_stats(adapter); diff --git a/drivers/net/e1000/e1000_hw.c b/drivers/net/e1000/e1000_hw.c index 723589b..045f542 100644 --- a/drivers/net/e1000/e1000_hw.c +++ b/drivers/net/e1000/e1000_hw.c @@ -354,18 +354,27 @@ e1000_set_media_type(struct e1000_hw *hw) hw->media_type = e1000_media_type_internal_serdes; break; default: - if(hw->mac_type >= e1000_82543) { + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + hw->media_type = e1000_media_type_fiber; + break; + case e1000_82573: + /* The STATUS_TBIMODE bit is reserved or reused for the this + * device. + */ + hw->media_type = e1000_media_type_copper; + break; + default: status = E1000_READ_REG(hw, STATUS); - if(status & E1000_STATUS_TBIMODE) { + if (status & E1000_STATUS_TBIMODE) { hw->media_type = e1000_media_type_fiber; /* tbi_compatibility not valid on fiber */ hw->tbi_compatibility_en = FALSE; } else { hw->media_type = e1000_media_type_copper; } - } else { - /* This is an 82542 (fiber only) */ - hw->media_type = e1000_media_type_fiber; + break; } } } @@ -1189,9 +1198,9 @@ e1000_copper_link_igp_setup(struct e1000_hw *hw) ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_data); if(ret_val) return ret_val; - } + } - return E1000_SUCCESS; + return E1000_SUCCESS; } diff --git a/drivers/net/e1000/e1000_hw.h b/drivers/net/e1000/e1000_hw.h index a0263ee..93e9f87 100644 --- a/drivers/net/e1000/e1000_hw.h +++ b/drivers/net/e1000/e1000_hw.h @@ -66,6 +66,7 @@ typedef enum { e1000_eeprom_spi, e1000_eeprom_microwire, e1000_eeprom_flash, + e1000_eeprom_none, /* No NVM support */ e1000_num_eeprom_types } e1000_eeprom_type; diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 137226d..cb7f051 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -29,6 +29,8 @@ #include "e1000.h" /* Change Log + * 6.0.58 4/20/05 + * o Accepted ethtool cleanup patch from Stephen Hemminger * 6.0.44+ 2/15/05 * o applied Anton's patch to resolve tx hang in hardware * o Applied Andrew Mortons patch - e1000 stops working after resume @@ -41,9 +43,9 @@ char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver"; #else #define DRIVERNAPI "-NAPI" #endif -#define DRV_VERSION "6.0.54-k2"DRIVERNAPI +#define DRV_VERSION "6.0.60-k2"DRIVERNAPI char e1000_driver_version[] = DRV_VERSION; -char e1000_copyright[] = "Copyright (c) 1999-2004 Intel Corporation."; +char e1000_copyright[] = "Copyright (c) 1999-2005 Intel Corporation."; /* e1000_pci_tbl - PCI Device ID Table * @@ -517,7 +519,7 @@ e1000_probe(struct pci_dev *pdev, SET_NETDEV_DEV(netdev, &pdev->dev); pci_set_drvdata(pdev, netdev); - adapter = netdev->priv; + adapter = netdev_priv(netdev); adapter->netdev = netdev; adapter->pdev = pdev; adapter->hw.back = adapter; @@ -738,7 +740,7 @@ static void __devexit e1000_remove(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); uint32_t manc, swsm; flush_scheduled_work(); @@ -871,7 +873,7 @@ e1000_sw_init(struct e1000_adapter *adapter) static int e1000_open(struct net_device *netdev) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); int err; /* allocate transmit descriptors */ @@ -919,7 +921,7 @@ err_setup_tx: static int e1000_close(struct net_device *netdev) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); e1000_down(adapter); @@ -1599,7 +1601,7 @@ e1000_leave_82542_rst(struct e1000_adapter *adapter) static int e1000_set_mac(struct net_device *netdev, void *p) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); struct sockaddr *addr = p; if(!is_valid_ether_addr(addr->sa_data)) @@ -1634,7 +1636,7 @@ e1000_set_mac(struct net_device *netdev, void *p) static void e1000_set_multi(struct net_device *netdev) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; struct dev_mc_list *mc_ptr; unsigned long flags; @@ -2213,7 +2215,7 @@ e1000_transfer_dhcp_info(struct e1000_adapter *adapter, struct sk_buff *skb) static int e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); unsigned int first, max_per_txd = E1000_MAX_DATA_PER_TXD; unsigned int max_txd_pwr = E1000_MAX_TXD_PWR; unsigned int tx_flags = 0; @@ -2344,7 +2346,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) static void e1000_tx_timeout(struct net_device *netdev) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); /* Do the reset outside of interrupt context */ schedule_work(&adapter->tx_timeout_task); @@ -2353,7 +2355,7 @@ e1000_tx_timeout(struct net_device *netdev) static void e1000_tx_timeout_task(struct net_device *netdev) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); e1000_down(adapter); e1000_up(adapter); @@ -2370,7 +2372,7 @@ e1000_tx_timeout_task(struct net_device *netdev) static struct net_device_stats * e1000_get_stats(struct net_device *netdev) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); e1000_update_stats(adapter); return &adapter->net_stats; @@ -2387,7 +2389,7 @@ e1000_get_stats(struct net_device *netdev) static int e1000_change_mtu(struct net_device *netdev, int new_mtu) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); int max_frame = new_mtu + ENET_HEADER_SIZE + ETHERNET_FCS_SIZE; if((max_frame < MINIMUM_ETHERNET_FRAME_SIZE) || @@ -2598,7 +2600,7 @@ static irqreturn_t e1000_intr(int irq, void *data, struct pt_regs *regs) { struct net_device *netdev = data; - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; uint32_t icr = E1000_READ_REG(hw, ICR); #ifndef CONFIG_E1000_NAPI @@ -2661,7 +2663,7 @@ e1000_intr(int irq, void *data, struct pt_regs *regs) static int e1000_clean(struct net_device *netdev, int *budget) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); int work_to_do = min(*budget, netdev->quota); int tx_cleaned; int work_done = 0; @@ -2672,8 +2674,8 @@ e1000_clean(struct net_device *netdev, int *budget) *budget -= work_done; netdev->quota -= work_done; - /* If no Tx and no Rx work done, exit the polling mode */ if ((!tx_cleaned && (work_done == 0)) || !netif_running(netdev)) { + /* If no Tx and not enough Rx work done, exit the polling mode */ netif_rx_complete(netdev); e1000_irq_enable(adapter); return 0; @@ -2769,13 +2771,13 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter) i = tx_ring->next_to_clean; eop = tx_ring->buffer_info[i].next_to_watch; eop_desc = E1000_TX_DESC(*tx_ring, eop); - DPRINTK(TX_ERR, ERR, "Detected Tx Unit Hang\n" + DPRINTK(DRV, ERR, "Detected Tx Unit Hang\n" " TDH <%x>\n" " TDT <%x>\n" " next_to_use <%x>\n" " next_to_clean <%x>\n" "buffer_info[next_to_clean]\n" - " dma <%llx>\n" + " dma <%zx>\n" " time_stamp <%lx>\n" " next_to_watch <%x>\n" " jiffies <%lx>\n" @@ -2994,7 +2996,7 @@ e1000_clean_rx_irq_ps(struct e1000_adapter *adapter) i = rx_ring->next_to_clean; rx_desc = E1000_RX_DESC_PS(*rx_ring, i); - staterr = rx_desc->wb.middle.status_error; + staterr = le32_to_cpu(rx_desc->wb.middle.status_error); while(staterr & E1000_RXD_STAT_DD) { buffer_info = &rx_ring->buffer_info[i]; @@ -3065,16 +3067,16 @@ e1000_clean_rx_irq_ps(struct e1000_adapter *adapter) #ifdef CONFIG_E1000_NAPI if(unlikely(adapter->vlgrp && (staterr & E1000_RXD_STAT_VP))) { vlan_hwaccel_receive_skb(skb, adapter->vlgrp, - le16_to_cpu(rx_desc->wb.middle.vlan & - E1000_RXD_SPC_VLAN_MASK)); + le16_to_cpu(rx_desc->wb.middle.vlan) & + E1000_RXD_SPC_VLAN_MASK); } else { netif_receive_skb(skb); } #else /* CONFIG_E1000_NAPI */ if(unlikely(adapter->vlgrp && (staterr & E1000_RXD_STAT_VP))) { vlan_hwaccel_rx(skb, adapter->vlgrp, - le16_to_cpu(rx_desc->wb.middle.vlan & - E1000_RXD_SPC_VLAN_MASK)); + le16_to_cpu(rx_desc->wb.middle.vlan) & + E1000_RXD_SPC_VLAN_MASK); } else { netif_rx(skb); } @@ -3087,7 +3089,7 @@ next_desc: if(unlikely(++i == rx_ring->count)) i = 0; rx_desc = E1000_RX_DESC_PS(*rx_ring, i); - staterr = rx_desc->wb.middle.status_error; + staterr = le32_to_cpu(rx_desc->wb.middle.status_error); } rx_ring->next_to_clean = i; adapter->alloc_rx_buf(adapter); @@ -3371,11 +3373,12 @@ e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); struct mii_ioctl_data *data = if_mii(ifr); int retval; uint16_t mii_reg; uint16_t spddplx; + unsigned long flags; if(adapter->hw.media_type != e1000_media_type_copper) return -EOPNOTSUPP; @@ -3385,22 +3388,29 @@ e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) data->phy_id = adapter->hw.phy_addr; break; case SIOCGMIIREG: - if (!capable(CAP_NET_ADMIN)) + if(!capable(CAP_NET_ADMIN)) return -EPERM; - if (e1000_read_phy_reg(&adapter->hw, data->reg_num & 0x1F, - &data->val_out)) + spin_lock_irqsave(&adapter->stats_lock, flags); + if(e1000_read_phy_reg(&adapter->hw, data->reg_num & 0x1F, + &data->val_out)) { + spin_unlock_irqrestore(&adapter->stats_lock, flags); return -EIO; + } + spin_unlock_irqrestore(&adapter->stats_lock, flags); break; case SIOCSMIIREG: - if (!capable(CAP_NET_ADMIN)) + if(!capable(CAP_NET_ADMIN)) return -EPERM; - if (data->reg_num & ~(0x1F)) + if(data->reg_num & ~(0x1F)) return -EFAULT; mii_reg = data->val_in; - if (e1000_write_phy_reg(&adapter->hw, data->reg_num, - mii_reg)) + spin_lock_irqsave(&adapter->stats_lock, flags); + if(e1000_write_phy_reg(&adapter->hw, data->reg_num, + mii_reg)) { + spin_unlock_irqrestore(&adapter->stats_lock, flags); return -EIO; - if (adapter->hw.phy_type == e1000_phy_m88) { + } + if(adapter->hw.phy_type == e1000_phy_m88) { switch (data->reg_num) { case PHY_CTRL: if(mii_reg & MII_CR_POWER_DOWN) @@ -3420,8 +3430,12 @@ e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) HALF_DUPLEX; retval = e1000_set_spd_dplx(adapter, spddplx); - if(retval) + if(retval) { + spin_unlock_irqrestore( + &adapter->stats_lock, + flags); return retval; + } } if(netif_running(adapter->netdev)) { e1000_down(adapter); @@ -3431,8 +3445,11 @@ e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) break; case M88E1000_PHY_SPEC_CTRL: case M88E1000_EXT_PHY_SPEC_CTRL: - if (e1000_phy_reset(&adapter->hw)) + if(e1000_phy_reset(&adapter->hw)) { + spin_unlock_irqrestore( + &adapter->stats_lock, flags); return -EIO; + } break; } } else { @@ -3448,6 +3465,7 @@ e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) break; } } + spin_unlock_irqrestore(&adapter->stats_lock, flags); break; default: return -EOPNOTSUPP; @@ -3504,7 +3522,7 @@ e1000_io_write(struct e1000_hw *hw, unsigned long port, uint32_t value) static void e1000_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); uint32_t ctrl, rctl; e1000_irq_disable(adapter); @@ -3544,7 +3562,7 @@ e1000_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp) static void e1000_vlan_rx_add_vid(struct net_device *netdev, uint16_t vid) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); uint32_t vfta, index; if((adapter->hw.mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT) && @@ -3560,7 +3578,7 @@ e1000_vlan_rx_add_vid(struct net_device *netdev, uint16_t vid) static void e1000_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); uint32_t vfta, index; e1000_irq_disable(adapter); @@ -3601,6 +3619,13 @@ e1000_set_spd_dplx(struct e1000_adapter *adapter, uint16_t spddplx) { adapter->hw.autoneg = 0; + /* Fiber NICs only allow 1000 gbps Full duplex */ + if((adapter->hw.media_type == e1000_media_type_fiber) && + spddplx != (SPEED_1000 + DUPLEX_FULL)) { + DPRINTK(PROBE, ERR, "Unsupported Speed/Duplex configuration\n"); + return -EINVAL; + } + switch(spddplx) { case SPEED_10 + DUPLEX_HALF: adapter->hw.forced_speed_duplex = e1000_10_half; @@ -3647,7 +3672,7 @@ static int e1000_suspend(struct pci_dev *pdev, uint32_t state) { struct net_device *netdev = pci_get_drvdata(pdev); - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); uint32_t ctrl, ctrl_ext, rctl, manc, status, swsm; uint32_t wufc = adapter->wol; @@ -3740,12 +3765,12 @@ static int e1000_resume(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); - struct e1000_adapter *adapter = netdev->priv; - uint32_t manc, ret, swsm; + struct e1000_adapter *adapter = netdev_priv(netdev); + uint32_t manc, ret_val, swsm; pci_set_power_state(pdev, 0); pci_restore_state(pdev); - ret = pci_enable_device(pdev); + ret_val = pci_enable_device(pdev); pci_set_master(pdev); pci_enable_wake(pdev, 3, 0); @@ -3788,7 +3813,7 @@ e1000_resume(struct pci_dev *pdev) static void e1000_netpoll(struct net_device *netdev) { - struct e1000_adapter *adapter = netdev->priv; + struct e1000_adapter *adapter = netdev_priv(netdev); disable_irq(adapter->pdev->irq); e1000_intr(adapter->pdev->irq, netdev, NULL); enable_irq(adapter->pdev->irq); diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 4ebcd05..64f0f69 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -82,6 +82,9 @@ * 0.31: 14 Nov 2004: ethtool support for getting/setting link * capabilities. * 0.32: 16 Apr 2005: RX_ERROR4 handling added. + * 0.33: 16 May 2005: Support for MCP51 added. + * 0.34: 18 Jun 2005: Add DEV_NEED_LINKTIMER to all nForce nics. + * 0.35: 26 Jun 2005: Support for MCP55 added. * * Known bugs: * We suspect that on some hardware no TX done interrupts are generated. @@ -93,7 +96,7 @@ * DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few * superfluous timer interrupts from the nic. */ -#define FORCEDETH_VERSION "0.32" +#define FORCEDETH_VERSION "0.35" #define DRV_NAME "forcedeth" #include <linux/module.h> @@ -2005,7 +2008,9 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i /* handle different descriptor versions */ if (pci_dev->device == PCI_DEVICE_ID_NVIDIA_NVENET_1 || pci_dev->device == PCI_DEVICE_ID_NVIDIA_NVENET_2 || - pci_dev->device == PCI_DEVICE_ID_NVIDIA_NVENET_3) + pci_dev->device == PCI_DEVICE_ID_NVIDIA_NVENET_3 || + pci_dev->device == PCI_DEVICE_ID_NVIDIA_NVENET_12 || + pci_dev->device == PCI_DEVICE_ID_NVIDIA_NVENET_13) np->desc_ver = DESC_VER_1; else np->desc_ver = DESC_VER_2; @@ -2215,56 +2220,84 @@ static struct pci_device_id pci_tbl[] = { .device = PCI_DEVICE_ID_NVIDIA_NVENET_4, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ, + .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER, }, { /* nForce3 Ethernet Controller */ .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_DEVICE_ID_NVIDIA_NVENET_5, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ, + .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER, }, { /* nForce3 Ethernet Controller */ .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_DEVICE_ID_NVIDIA_NVENET_6, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ, + .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER, }, { /* nForce3 Ethernet Controller */ .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_DEVICE_ID_NVIDIA_NVENET_7, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ, + .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER, }, { /* CK804 Ethernet Controller */ .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_DEVICE_ID_NVIDIA_NVENET_8, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ, + .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER, }, { /* CK804 Ethernet Controller */ .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_DEVICE_ID_NVIDIA_NVENET_9, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ, + .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER, }, { /* MCP04 Ethernet Controller */ .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_DEVICE_ID_NVIDIA_NVENET_10, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ, + .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER, }, { /* MCP04 Ethernet Controller */ .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_DEVICE_ID_NVIDIA_NVENET_11, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ, + .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER, + }, + { /* MCP51 Ethernet Controller */ + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NVENET_12, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER, + }, + { /* MCP51 Ethernet Controller */ + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NVENET_13, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER, + }, + { /* MCP55 Ethernet Controller */ + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NVENET_14, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER, + }, + { /* MCP55 Ethernet Controller */ + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NVENET_15, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER, }, {0,}, }; diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index b43b2b1..6518334 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -1,4 +1,4 @@ -/* +/* * drivers/net/gianfar.c * * Gianfar Ethernet Driver @@ -22,10 +22,9 @@ * B-V +1.62 * * Theory of operation - * This driver is designed for the Triple-speed Ethernet - * controllers on the Freescale 8540/8560 integrated processors, - * as well as the Fast Ethernet Controller on the 8540. - * + * This driver is designed for the non-CPM ethernet controllers + * on the 85xx and 83xx family of integrated processors + * * The driver is initialized through platform_device. Structures which * define the configuration needed by the board are defined in a * board structure in arch/ppc/platforms (though I do not @@ -39,12 +38,12 @@ * * The Gianfar Ethernet Controller uses a ring of buffer * descriptors. The beginning is indicated by a register - * pointing to the physical address of the start of the ring. - * The end is determined by a "wrap" bit being set in the + * pointing to the physical address of the start of the ring. + * The end is determined by a "wrap" bit being set in the * last descriptor of the ring. * * When a packet is received, the RXF bit in the - * IEVENT register is set, triggering an interrupt when the + * IEVENT register is set, triggering an interrupt when the * corresponding bit in the IMASK register is also set (if * interrupt coalescing is active, then the interrupt may not * happen immediately, but will wait until either a set number @@ -52,7 +51,7 @@ * interrupt handler will signal there is work to be done, and * exit. Without NAPI, the packet(s) will be handled * immediately. Both methods will start at the last known empty - * descriptor, and process every subsequent descriptor until there + * descriptor, and process every subsequent descriptor until there * are none left with data (NAPI will stop after a set number of * packets to give time to other tasks, but will eventually * process all the packets). The data arrives inside a @@ -83,9 +82,13 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> +#include <linux/if_vlan.h> #include <linux/spinlock.h> #include <linux/mm.h> #include <linux/device.h> +#include <linux/ip.h> +#include <linux/tcp.h> +#include <linux/udp.h> #include <asm/io.h> #include <asm/irq.h> @@ -123,7 +126,7 @@ static int gfar_set_mac_address(struct net_device *dev); static int gfar_change_mtu(struct net_device *dev, int new_mtu); static irqreturn_t gfar_error(int irq, void *dev_id, struct pt_regs *regs); static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs); -irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs); +static irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs); static irqreturn_t gfar_interrupt(int irq, void *dev_id, struct pt_regs *regs); static irqreturn_t phy_interrupt(int irq, void *dev_id, struct pt_regs *regs); static void gfar_phy_change(void *data); @@ -139,9 +142,12 @@ static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr); #ifdef CONFIG_GFAR_NAPI static int gfar_poll(struct net_device *dev, int *budget); #endif -static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit); +int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit); static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int length); static void gfar_phy_startup_timer(unsigned long data); +static void gfar_vlan_rx_register(struct net_device *netdev, + struct vlan_group *grp); +static void gfar_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid); extern struct ethtool_ops gfar_ethtool_ops; @@ -149,6 +155,13 @@ MODULE_AUTHOR("Freescale Semiconductor, Inc"); MODULE_DESCRIPTION("Gianfar Ethernet Driver"); MODULE_LICENSE("GPL"); +int gfar_uses_fcb(struct gfar_private *priv) +{ + if (priv->vlan_enable || priv->rx_csum_enable) + return 1; + else + return 0; +} static int gfar_probe(struct device *device) { u32 tempval; @@ -159,7 +172,6 @@ static int gfar_probe(struct device *device) struct resource *r; int idx; int err = 0; - int dev_ethtool_ops = 0; einfo = (struct gianfar_platform_data *) pdev->dev.platform_data; @@ -265,15 +277,69 @@ static int gfar_probe(struct device *device) dev->mtu = 1500; dev->set_multicast_list = gfar_set_multi; - /* Index into the array of possible ethtool - * ops to catch all 4 possibilities */ - if((priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) == 0) - dev_ethtool_ops += 1; + dev->ethtool_ops = &gfar_ethtool_ops; + + if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) { + priv->rx_csum_enable = 1; + dev->features |= NETIF_F_IP_CSUM; + } else + priv->rx_csum_enable = 0; + + priv->vlgrp = NULL; - if((priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE) == 0) - dev_ethtool_ops += 2; + if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) { + dev->vlan_rx_register = gfar_vlan_rx_register; + dev->vlan_rx_kill_vid = gfar_vlan_rx_kill_vid; - dev->ethtool_ops = gfar_op_array[dev_ethtool_ops]; + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + + priv->vlan_enable = 1; + } + + if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_EXTENDED_HASH) { + priv->extended_hash = 1; + priv->hash_width = 9; + + priv->hash_regs[0] = &priv->regs->igaddr0; + priv->hash_regs[1] = &priv->regs->igaddr1; + priv->hash_regs[2] = &priv->regs->igaddr2; + priv->hash_regs[3] = &priv->regs->igaddr3; + priv->hash_regs[4] = &priv->regs->igaddr4; + priv->hash_regs[5] = &priv->regs->igaddr5; + priv->hash_regs[6] = &priv->regs->igaddr6; + priv->hash_regs[7] = &priv->regs->igaddr7; + priv->hash_regs[8] = &priv->regs->gaddr0; + priv->hash_regs[9] = &priv->regs->gaddr1; + priv->hash_regs[10] = &priv->regs->gaddr2; + priv->hash_regs[11] = &priv->regs->gaddr3; + priv->hash_regs[12] = &priv->regs->gaddr4; + priv->hash_regs[13] = &priv->regs->gaddr5; + priv->hash_regs[14] = &priv->regs->gaddr6; + priv->hash_regs[15] = &priv->regs->gaddr7; + + } else { + priv->extended_hash = 0; + priv->hash_width = 8; + + priv->hash_regs[0] = &priv->regs->gaddr0; + priv->hash_regs[1] = &priv->regs->gaddr1; + priv->hash_regs[2] = &priv->regs->gaddr2; + priv->hash_regs[3] = &priv->regs->gaddr3; + priv->hash_regs[4] = &priv->regs->gaddr4; + priv->hash_regs[5] = &priv->regs->gaddr5; + priv->hash_regs[6] = &priv->regs->gaddr6; + priv->hash_regs[7] = &priv->regs->gaddr7; + } + + if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_PADDING) + priv->padding = DEFAULT_PADDING; + else + priv->padding = 0; + + dev->hard_header_len += priv->padding; + + if (dev->features & NETIF_F_IP_CSUM) + dev->hard_header_len += GMAC_FCB_LEN; priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE; #ifdef CONFIG_GFAR_BUFSTASH @@ -289,6 +355,9 @@ static int gfar_probe(struct device *device) priv->rxcount = DEFAULT_RXCOUNT; priv->rxtime = DEFAULT_RXTIME; + /* Enable most messages by default */ + priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1; + err = register_netdev(dev); if (err) { @@ -360,8 +429,9 @@ static int init_phy(struct net_device *dev) GFP_KERNEL); if(NULL == mii_info) { - printk(KERN_ERR "%s: Could not allocate mii_info\n", - dev->name); + if (netif_msg_ifup(priv)) + printk(KERN_ERR "%s: Could not allocate mii_info\n", + dev->name); return -ENOMEM; } @@ -410,7 +480,8 @@ static int init_phy(struct net_device *dev) curphy = get_phy_info(priv->mii_info); if (curphy == NULL) { - printk(KERN_ERR "%s: No PHY found\n", dev->name); + if (netif_msg_ifup(priv)) + printk(KERN_ERR "%s: No PHY found\n", dev->name); err = -1; goto no_phy; } @@ -421,7 +492,7 @@ static int init_phy(struct net_device *dev) if(curphy->init) { err = curphy->init(priv->mii_info); - if (err) + if (err) goto phy_init_fail; } @@ -446,14 +517,14 @@ static void init_registers(struct net_device *dev) gfar_write(&priv->regs->imask, IMASK_INIT_CLEAR); /* Init hash registers to zero */ - gfar_write(&priv->regs->iaddr0, 0); - gfar_write(&priv->regs->iaddr1, 0); - gfar_write(&priv->regs->iaddr2, 0); - gfar_write(&priv->regs->iaddr3, 0); - gfar_write(&priv->regs->iaddr4, 0); - gfar_write(&priv->regs->iaddr5, 0); - gfar_write(&priv->regs->iaddr6, 0); - gfar_write(&priv->regs->iaddr7, 0); + gfar_write(&priv->regs->igaddr0, 0); + gfar_write(&priv->regs->igaddr1, 0); + gfar_write(&priv->regs->igaddr2, 0); + gfar_write(&priv->regs->igaddr3, 0); + gfar_write(&priv->regs->igaddr4, 0); + gfar_write(&priv->regs->igaddr5, 0); + gfar_write(&priv->regs->igaddr6, 0); + gfar_write(&priv->regs->igaddr7, 0); gfar_write(&priv->regs->gaddr0, 0); gfar_write(&priv->regs->gaddr1, 0); @@ -464,9 +535,6 @@ static void init_registers(struct net_device *dev) gfar_write(&priv->regs->gaddr6, 0); gfar_write(&priv->regs->gaddr7, 0); - /* Zero out rctrl */ - gfar_write(&priv->regs->rctrl, 0x00000000); - /* Zero out the rmon mib registers if it has them */ if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) { memset((void *) &(priv->regs->rmon), 0, @@ -497,20 +565,14 @@ static void init_registers(struct net_device *dev) gfar_write(&priv->regs->tbipa, TBIPA_VALUE); } -void stop_gfar(struct net_device *dev) + +/* Halt the receive and transmit queues */ +void gfar_halt(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); struct gfar *regs = priv->regs; - unsigned long flags; u32 tempval; - /* Lock it down */ - spin_lock_irqsave(&priv->lock, flags); - - /* Tell the kernel the link is down */ - priv->mii_info->link = 0; - adjust_link(dev); - /* Mask all interrupts */ gfar_write(®s->imask, IMASK_INIT_CLEAR); @@ -533,13 +595,29 @@ void stop_gfar(struct net_device *dev) tempval = gfar_read(®s->maccfg1); tempval &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN); gfar_write(®s->maccfg1, tempval); +} + +void stop_gfar(struct net_device *dev) +{ + struct gfar_private *priv = netdev_priv(dev); + struct gfar *regs = priv->regs; + unsigned long flags; + + /* Lock it down */ + spin_lock_irqsave(&priv->lock, flags); + + /* Tell the kernel the link is down */ + priv->mii_info->link = 0; + adjust_link(dev); + + gfar_halt(dev); if (priv->einfo->board_flags & FSL_GIANFAR_BRD_HAS_PHY_INTR) { /* Clear any pending interrupts */ mii_clear_phy_interrupt(priv->mii_info); /* Disable PHY Interrupts */ - mii_configure_phy_interrupt(priv->mii_info, + mii_configure_phy_interrupt(priv->mii_info, MII_INTERRUPT_DISABLED); } @@ -566,7 +644,7 @@ void stop_gfar(struct net_device *dev) sizeof(struct txbd8)*priv->tx_ring_size + sizeof(struct rxbd8)*priv->rx_ring_size, priv->tx_bd_base, - gfar_read(®s->tbase)); + gfar_read(®s->tbase0)); } /* If there are any tx skbs or rx skbs still around, free them. @@ -620,6 +698,34 @@ void free_skb_resources(struct gfar_private *priv) } } +void gfar_start(struct net_device *dev) +{ + struct gfar_private *priv = netdev_priv(dev); + struct gfar *regs = priv->regs; + u32 tempval; + + /* Enable Rx and Tx in MACCFG1 */ + tempval = gfar_read(®s->maccfg1); + tempval |= (MACCFG1_RX_EN | MACCFG1_TX_EN); + gfar_write(®s->maccfg1, tempval); + + /* Initialize DMACTRL to have WWR and WOP */ + tempval = gfar_read(&priv->regs->dmactrl); + tempval |= DMACTRL_INIT_SETTINGS; + gfar_write(&priv->regs->dmactrl, tempval); + + /* Clear THLT, so that the DMA starts polling now */ + gfar_write(®s->tstat, TSTAT_CLEAR_THALT); + + /* Make sure we aren't stopped */ + tempval = gfar_read(&priv->regs->dmactrl); + tempval &= ~(DMACTRL_GRS | DMACTRL_GTS); + gfar_write(&priv->regs->dmactrl, tempval); + + /* Unmask the interrupts we look for */ + gfar_write(®s->imask, IMASK_DEFAULT); +} + /* Bring the controller up and running */ int startup_gfar(struct net_device *dev) { @@ -630,33 +736,34 @@ int startup_gfar(struct net_device *dev) int i; struct gfar_private *priv = netdev_priv(dev); struct gfar *regs = priv->regs; - u32 tempval; int err = 0; + u32 rctrl = 0; gfar_write(®s->imask, IMASK_INIT_CLEAR); /* Allocate memory for the buffer descriptors */ - vaddr = (unsigned long) dma_alloc_coherent(NULL, + vaddr = (unsigned long) dma_alloc_coherent(NULL, sizeof (struct txbd8) * priv->tx_ring_size + sizeof (struct rxbd8) * priv->rx_ring_size, &addr, GFP_KERNEL); if (vaddr == 0) { - printk(KERN_ERR "%s: Could not allocate buffer descriptors!\n", - dev->name); + if (netif_msg_ifup(priv)) + printk(KERN_ERR "%s: Could not allocate buffer descriptors!\n", + dev->name); return -ENOMEM; } priv->tx_bd_base = (struct txbd8 *) vaddr; /* enet DMA only understands physical addresses */ - gfar_write(®s->tbase, addr); + gfar_write(®s->tbase0, addr); /* Start the rx descriptor ring where the tx ring leaves off */ addr = addr + sizeof (struct txbd8) * priv->tx_ring_size; vaddr = vaddr + sizeof (struct txbd8) * priv->tx_ring_size; priv->rx_bd_base = (struct rxbd8 *) vaddr; - gfar_write(®s->rbase, addr); + gfar_write(®s->rbase0, addr); /* Setup the skbuff rings */ priv->tx_skbuff = @@ -664,8 +771,9 @@ int startup_gfar(struct net_device *dev) priv->tx_ring_size, GFP_KERNEL); if (priv->tx_skbuff == NULL) { - printk(KERN_ERR "%s: Could not allocate tx_skbuff\n", - dev->name); + if (netif_msg_ifup(priv)) + printk(KERN_ERR "%s: Could not allocate tx_skbuff\n", + dev->name); err = -ENOMEM; goto tx_skb_fail; } @@ -678,8 +786,9 @@ int startup_gfar(struct net_device *dev) priv->rx_ring_size, GFP_KERNEL); if (priv->rx_skbuff == NULL) { - printk(KERN_ERR "%s: Could not allocate rx_skbuff\n", - dev->name); + if (netif_msg_ifup(priv)) + printk(KERN_ERR "%s: Could not allocate rx_skbuff\n", + dev->name); err = -ENOMEM; goto rx_skb_fail; } @@ -726,12 +835,13 @@ int startup_gfar(struct net_device *dev) /* If the device has multiple interrupts, register for * them. Otherwise, only register for the one */ if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) { - /* Install our interrupt handlers for Error, + /* Install our interrupt handlers for Error, * Transmit, and Receive */ if (request_irq(priv->interruptError, gfar_error, 0, "enet_error", dev) < 0) { - printk(KERN_ERR "%s: Can't get IRQ %d\n", - dev->name, priv->interruptError); + if (netif_msg_intr(priv)) + printk(KERN_ERR "%s: Can't get IRQ %d\n", + dev->name, priv->interruptError); err = -1; goto err_irq_fail; @@ -739,8 +849,9 @@ int startup_gfar(struct net_device *dev) if (request_irq(priv->interruptTransmit, gfar_transmit, 0, "enet_tx", dev) < 0) { - printk(KERN_ERR "%s: Can't get IRQ %d\n", - dev->name, priv->interruptTransmit); + if (netif_msg_intr(priv)) + printk(KERN_ERR "%s: Can't get IRQ %d\n", + dev->name, priv->interruptTransmit); err = -1; @@ -749,8 +860,9 @@ int startup_gfar(struct net_device *dev) if (request_irq(priv->interruptReceive, gfar_receive, 0, "enet_rx", dev) < 0) { - printk(KERN_ERR "%s: Can't get IRQ %d (receive0)\n", - dev->name, priv->interruptReceive); + if (netif_msg_intr(priv)) + printk(KERN_ERR "%s: Can't get IRQ %d (receive0)\n", + dev->name, priv->interruptReceive); err = -1; goto rx_irq_fail; @@ -758,8 +870,9 @@ int startup_gfar(struct net_device *dev) } else { if (request_irq(priv->interruptTransmit, gfar_interrupt, 0, "gfar_interrupt", dev) < 0) { - printk(KERN_ERR "%s: Can't get IRQ %d\n", - dev->name, priv->interruptError); + if (netif_msg_intr(priv)) + printk(KERN_ERR "%s: Can't get IRQ %d\n", + dev->name, priv->interruptError); err = -1; goto err_irq_fail; @@ -787,28 +900,22 @@ int startup_gfar(struct net_device *dev) else gfar_write(®s->rxic, 0); - init_waitqueue_head(&priv->rxcleanupq); + if (priv->rx_csum_enable) + rctrl |= RCTRL_CHECKSUMMING; - /* Enable Rx and Tx in MACCFG1 */ - tempval = gfar_read(®s->maccfg1); - tempval |= (MACCFG1_RX_EN | MACCFG1_TX_EN); - gfar_write(®s->maccfg1, tempval); + if (priv->extended_hash) + rctrl |= RCTRL_EXTHASH; - /* Initialize DMACTRL to have WWR and WOP */ - tempval = gfar_read(&priv->regs->dmactrl); - tempval |= DMACTRL_INIT_SETTINGS; - gfar_write(&priv->regs->dmactrl, tempval); + if (priv->vlan_enable) + rctrl |= RCTRL_VLAN; - /* Clear THLT, so that the DMA starts polling now */ - gfar_write(®s->tstat, TSTAT_CLEAR_THALT); + /* Init rctrl based on our settings */ + gfar_write(&priv->regs->rctrl, rctrl); - /* Make sure we aren't stopped */ - tempval = gfar_read(&priv->regs->dmactrl); - tempval &= ~(DMACTRL_GRS | DMACTRL_GTS); - gfar_write(&priv->regs->dmactrl, tempval); + if (dev->features & NETIF_F_IP_CSUM) + gfar_write(&priv->regs->tctrl, TCTRL_INIT_CSUM); - /* Unmask the interrupts we look for */ - gfar_write(®s->imask, IMASK_DEFAULT); + gfar_start(dev); return 0; @@ -824,7 +931,7 @@ tx_skb_fail: sizeof(struct txbd8)*priv->tx_ring_size + sizeof(struct rxbd8)*priv->rx_ring_size, priv->tx_bd_base, - gfar_read(®s->tbase)); + gfar_read(®s->tbase0)); if (priv->mii_info->phyinfo->close) priv->mii_info->phyinfo->close(priv->mii_info); @@ -857,11 +964,62 @@ static int gfar_enet_open(struct net_device *dev) return err; } +static struct txfcb *gfar_add_fcb(struct sk_buff *skb, struct txbd8 *bdp) +{ + struct txfcb *fcb = (struct txfcb *)skb_push (skb, GMAC_FCB_LEN); + + memset(fcb, 0, GMAC_FCB_LEN); + + /* Flag the bd so the controller looks for the FCB */ + bdp->status |= TXBD_TOE; + + return fcb; +} + +static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb) +{ + int len; + + /* If we're here, it's a IP packet with a TCP or UDP + * payload. We set it to checksum, using a pseudo-header + * we provide + */ + fcb->ip = 1; + fcb->tup = 1; + fcb->ctu = 1; + fcb->nph = 1; + + /* Notify the controller what the protocol is */ + if (skb->nh.iph->protocol == IPPROTO_UDP) + fcb->udp = 1; + + /* l3os is the distance between the start of the + * frame (skb->data) and the start of the IP hdr. + * l4os is the distance between the start of the + * l3 hdr and the l4 hdr */ + fcb->l3os = (u16)(skb->nh.raw - skb->data - GMAC_FCB_LEN); + fcb->l4os = (u16)(skb->h.raw - skb->nh.raw); + + len = skb->nh.iph->tot_len - fcb->l4os; + + /* Provide the pseudoheader csum */ + fcb->phcs = ~csum_tcpudp_magic(skb->nh.iph->saddr, + skb->nh.iph->daddr, len, + skb->nh.iph->protocol, 0); +} + +void gfar_tx_vlan(struct sk_buff *skb, struct txfcb *fcb) +{ + fcb->vln = 1; + fcb->vlctl = vlan_tx_tag_get(skb); +} + /* This is called by the kernel when a frame is ready for transmission. */ /* It is pointed to by the dev->hard_start_xmit function pointer */ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); + struct txfcb *fcb = NULL; struct txbd8 *txbdp; /* Update transmit stats */ @@ -876,9 +1034,24 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Clear all but the WRAP status flags */ txbdp->status &= TXBD_WRAP; + /* Set up checksumming */ + if ((dev->features & NETIF_F_IP_CSUM) + && (CHECKSUM_HW == skb->ip_summed)) { + fcb = gfar_add_fcb(skb, txbdp); + gfar_tx_checksum(skb, fcb); + } + + if (priv->vlan_enable && + unlikely(priv->vlgrp && vlan_tx_tag_present(skb))) { + if (NULL == fcb) + fcb = gfar_add_fcb(skb, txbdp); + + gfar_tx_vlan(skb, fcb); + } + /* Set buffer length and pointer */ txbdp->length = skb->len; - txbdp->bufPtr = dma_map_single(NULL, skb->data, + txbdp->bufPtr = dma_map_single(NULL, skb->data, skb->len, DMA_TO_DEVICE); /* Save the skb pointer so we can free it later */ @@ -972,15 +1145,78 @@ int gfar_set_mac_address(struct net_device *dev) } +/* Enables and disables VLAN insertion/extraction */ +static void gfar_vlan_rx_register(struct net_device *dev, + struct vlan_group *grp) +{ + struct gfar_private *priv = netdev_priv(dev); + unsigned long flags; + u32 tempval; + + spin_lock_irqsave(&priv->lock, flags); + + priv->vlgrp = grp; + + if (grp) { + /* Enable VLAN tag insertion */ + tempval = gfar_read(&priv->regs->tctrl); + tempval |= TCTRL_VLINS; + + gfar_write(&priv->regs->tctrl, tempval); + + /* Enable VLAN tag extraction */ + tempval = gfar_read(&priv->regs->rctrl); + tempval |= RCTRL_VLEX; + gfar_write(&priv->regs->rctrl, tempval); + } else { + /* Disable VLAN tag insertion */ + tempval = gfar_read(&priv->regs->tctrl); + tempval &= ~TCTRL_VLINS; + gfar_write(&priv->regs->tctrl, tempval); + + /* Disable VLAN tag extraction */ + tempval = gfar_read(&priv->regs->rctrl); + tempval &= ~RCTRL_VLEX; + gfar_write(&priv->regs->rctrl, tempval); + } + + spin_unlock_irqrestore(&priv->lock, flags); +} + + +static void gfar_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid) +{ + struct gfar_private *priv = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + if (priv->vlgrp) + priv->vlgrp->vlan_devices[vid] = NULL; + + spin_unlock_irqrestore(&priv->lock, flags); +} + + static int gfar_change_mtu(struct net_device *dev, int new_mtu) { int tempsize, tempval; struct gfar_private *priv = netdev_priv(dev); int oldsize = priv->rx_buffer_size; - int frame_size = new_mtu + 18; + int frame_size = new_mtu + ETH_HLEN; + + if (priv->vlan_enable) + frame_size += VLAN_ETH_HLEN; + + if (gfar_uses_fcb(priv)) + frame_size += GMAC_FCB_LEN; + + frame_size += priv->padding; if ((frame_size < 64) || (frame_size > JUMBO_FRAME_SIZE)) { - printk(KERN_ERR "%s: Invalid MTU setting\n", dev->name); + if (netif_msg_drv(priv)) + printk(KERN_ERR "%s: Invalid MTU setting\n", + dev->name); return -EINVAL; } @@ -1120,7 +1356,7 @@ struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp) skb->dev = dev; bdp->bufPtr = dma_map_single(NULL, skb->data, - priv->rx_buffer_size + RXBUF_ALIGNMENT, + priv->rx_buffer_size + RXBUF_ALIGNMENT, DMA_FROM_DEVICE); bdp->length = 0; @@ -1190,11 +1426,10 @@ irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs) __netif_rx_schedule(dev); } else { -#ifdef VERBOSE_GFAR_ERRORS - printk(KERN_DEBUG "%s: receive called twice (%x)[%x]\n", - dev->name, gfar_read(&priv->regs->ievent), - gfar_read(&priv->regs->imask)); -#endif + if (netif_msg_rx_err(priv)) + printk(KERN_DEBUG "%s: receive called twice (%x)[%x]\n", + dev->name, gfar_read(&priv->regs->ievent), + gfar_read(&priv->regs->imask)); } #else @@ -1209,15 +1444,43 @@ irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs) else gfar_write(&priv->regs->rxic, 0); - /* Just in case we need to wake the ring param changer */ - priv->rxclean = 1; - spin_unlock(&priv->lock); #endif return IRQ_HANDLED; } +static inline int gfar_rx_vlan(struct sk_buff *skb, + struct vlan_group *vlgrp, unsigned short vlctl) +{ +#ifdef CONFIG_GFAR_NAPI + return vlan_hwaccel_receive_skb(skb, vlgrp, vlctl); +#else + return vlan_hwaccel_rx(skb, vlgrp, vlctl); +#endif +} + +static inline void gfar_rx_checksum(struct sk_buff *skb, struct rxfcb *fcb) +{ + /* If valid headers were found, and valid sums + * were verified, then we tell the kernel that no + * checksumming is necessary. Otherwise, it is */ + if (fcb->cip && !fcb->eip && fcb->ctu && !fcb->etu) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; +} + + +static inline struct rxfcb *gfar_get_fcb(struct sk_buff *skb) +{ + struct rxfcb *fcb = (struct rxfcb *)skb->data; + + /* Remove the FCB from the skb */ + skb_pull(skb, GMAC_FCB_LEN); + + return fcb; +} /* gfar_process_frame() -- handle one incoming packet if skb * isn't NULL. */ @@ -1225,35 +1488,51 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int length) { struct gfar_private *priv = netdev_priv(dev); + struct rxfcb *fcb = NULL; if (skb == NULL) { -#ifdef BRIEF_GFAR_ERRORS - printk(KERN_WARNING "%s: Missing skb!!.\n", - dev->name); -#endif + if (netif_msg_rx_err(priv)) + printk(KERN_WARNING "%s: Missing skb!!.\n", dev->name); priv->stats.rx_dropped++; priv->extra_stats.rx_skbmissing++; } else { + int ret; + /* Prep the skb for the packet */ skb_put(skb, length); + /* Grab the FCB if there is one */ + if (gfar_uses_fcb(priv)) + fcb = gfar_get_fcb(skb); + + /* Remove the padded bytes, if there are any */ + if (priv->padding) + skb_pull(skb, priv->padding); + + if (priv->rx_csum_enable) + gfar_rx_checksum(skb, fcb); + /* Tell the skb what kind of packet this is */ skb->protocol = eth_type_trans(skb, dev); /* Send the packet up the stack */ - if (RECEIVE(skb) == NET_RX_DROP) { + if (unlikely(priv->vlgrp && fcb->vln)) + ret = gfar_rx_vlan(skb, priv->vlgrp, fcb->vlctl); + else + ret = RECEIVE(skb); + + if (NET_RX_DROP == ret) priv->extra_stats.kernel_dropped++; - } } return 0; } /* gfar_clean_rx_ring() -- Processes each frame in the rx ring - * until the budget/quota has been reached. Returns the number + * until the budget/quota has been reached. Returns the number * of frames handled */ -static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) +int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) { struct rxbd8 *bdp; struct sk_buff *skb; @@ -1355,9 +1634,6 @@ static int gfar_poll(struct net_device *dev, int *budget) mk_ic_value(priv->rxcount, priv->rxtime)); else gfar_write(&priv->regs->rxic, 0); - - /* Signal to the ring size changer that it's safe to go */ - priv->rxclean = 1; } return (rx_work_limit < 0) ? 1 : 0; @@ -1393,10 +1669,8 @@ static irqreturn_t gfar_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (events & IEVENT_CRL) priv->stats.tx_aborted_errors++; if (events & IEVENT_XFUN) { -#ifdef VERBOSE_GFAR_ERRORS - printk(KERN_WARNING "%s: tx underrun. dropped packet\n", - dev->name); -#endif + if (netif_msg_tx_err(priv)) + printk(KERN_WARNING "%s: tx underrun. dropped packet\n", dev->name); priv->stats.tx_dropped++; priv->extra_stats.tx_underrun++; @@ -1415,36 +1689,30 @@ static irqreturn_t gfar_interrupt(int irq, void *dev_id, struct pt_regs *regs) gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT); #endif -#ifdef VERBOSE_GFAR_ERRORS - printk(KERN_DEBUG "%s: busy error (rhalt: %x)\n", dev->name, - gfar_read(&priv->regs->rstat)); -#endif + if (netif_msg_rx_err(priv)) + printk(KERN_DEBUG "%s: busy error (rhalt: %x)\n", + dev->name, + gfar_read(&priv->regs->rstat)); } if (events & IEVENT_BABR) { priv->stats.rx_errors++; priv->extra_stats.rx_babr++; -#ifdef VERBOSE_GFAR_ERRORS - printk(KERN_DEBUG "%s: babbling error\n", dev->name); -#endif + if (netif_msg_rx_err(priv)) + printk(KERN_DEBUG "%s: babbling error\n", dev->name); } if (events & IEVENT_EBERR) { priv->extra_stats.eberr++; -#ifdef VERBOSE_GFAR_ERRORS - printk(KERN_DEBUG "%s: EBERR\n", dev->name); -#endif - } - if (events & IEVENT_RXC) { -#ifdef VERBOSE_GFAR_ERRORS - printk(KERN_DEBUG "%s: control frame\n", dev->name); -#endif + if (netif_msg_rx_err(priv)) + printk(KERN_DEBUG "%s: EBERR\n", dev->name); } + if ((events & IEVENT_RXC) && (netif_msg_rx_err(priv))) + printk(KERN_DEBUG "%s: control frame\n", dev->name); if (events & IEVENT_BABT) { priv->extra_stats.tx_babt++; -#ifdef VERBOSE_GFAR_ERRORS - printk(KERN_DEBUG "%s: babt error\n", dev->name); -#endif + if (netif_msg_rx_err(priv)) + printk(KERN_DEBUG "%s: babt error\n", dev->name); } return IRQ_HANDLED; @@ -1510,7 +1778,7 @@ static void gfar_phy_timer(unsigned long data) * If, after GFAR_AN_TIMEOUT seconds, it has not * finished, we switch to forced. * Either way, once the process has completed, we either - * request the interrupt, or switch the timer over to + * request the interrupt, or switch the timer over to * using gfar_phy_timer to check status */ static void gfar_phy_startup_timer(unsigned long data) { @@ -1535,8 +1803,9 @@ static void gfar_phy_startup_timer(unsigned long data) /* Forcing failed! Give up */ if(result) { - printk(KERN_ERR "%s: Forcing failed!\n", - mii_info->dev->name); + if (netif_msg_link(priv)) + printk(KERN_ERR "%s: Forcing failed!\n", + mii_info->dev->name); return; } } @@ -1546,16 +1815,17 @@ static void gfar_phy_startup_timer(unsigned long data) /* Grab the PHY interrupt, if necessary/possible */ if (priv->einfo->board_flags & FSL_GIANFAR_BRD_HAS_PHY_INTR) { - if (request_irq(priv->einfo->interruptPHY, + if (request_irq(priv->einfo->interruptPHY, phy_interrupt, - SA_SHIRQ, - "phy_interrupt", + SA_SHIRQ, + "phy_interrupt", mii_info->dev) < 0) { - printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n", - mii_info->dev->name, + if (netif_msg_intr(priv)) + printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n", + mii_info->dev->name, priv->einfo->interruptPHY); } else { - mii_configure_phy_interrupt(priv->mii_info, + mii_configure_phy_interrupt(priv->mii_info, MII_INTERRUPT_ENABLED); return; } @@ -1592,15 +1862,17 @@ static void adjust_link(struct net_device *dev) tempval &= ~(MACCFG2_FULL_DUPLEX); gfar_write(®s->maccfg2, tempval); - printk(KERN_INFO "%s: Half Duplex\n", - dev->name); + if (netif_msg_link(priv)) + printk(KERN_INFO "%s: Half Duplex\n", + dev->name); } else { tempval = gfar_read(®s->maccfg2); tempval |= MACCFG2_FULL_DUPLEX; gfar_write(®s->maccfg2, tempval); - printk(KERN_INFO "%s: Full Duplex\n", - dev->name); + if (netif_msg_link(priv)) + printk(KERN_INFO "%s: Full Duplex\n", + dev->name); } priv->oldduplex = mii_info->duplex; @@ -1622,27 +1894,32 @@ static void adjust_link(struct net_device *dev) gfar_write(®s->maccfg2, tempval); break; default: - printk(KERN_WARNING - "%s: Ack! Speed (%d) is not 10/100/1000!\n", - dev->name, mii_info->speed); + if (netif_msg_link(priv)) + printk(KERN_WARNING + "%s: Ack! Speed (%d) is not 10/100/1000!\n", + dev->name, mii_info->speed); break; } - printk(KERN_INFO "%s: Speed %dBT\n", dev->name, - mii_info->speed); + if (netif_msg_link(priv)) + printk(KERN_INFO "%s: Speed %dBT\n", dev->name, + mii_info->speed); priv->oldspeed = mii_info->speed; } if (!priv->oldlink) { - printk(KERN_INFO "%s: Link is up\n", dev->name); + if (netif_msg_link(priv)) + printk(KERN_INFO "%s: Link is up\n", dev->name); priv->oldlink = 1; netif_carrier_on(dev); netif_schedule(dev); } } else { if (priv->oldlink) { - printk(KERN_INFO "%s: Link is down\n", dev->name); + if (netif_msg_link(priv)) + printk(KERN_INFO "%s: Link is down\n", + dev->name); priv->oldlink = 0; priv->oldspeed = 0; priv->oldduplex = -1; @@ -1664,8 +1941,9 @@ static void gfar_set_multi(struct net_device *dev) u32 tempval; if(dev->flags & IFF_PROMISC) { - printk(KERN_INFO "%s: Entering promiscuous mode.\n", - dev->name); + if (netif_msg_drv(priv)) + printk(KERN_INFO "%s: Entering promiscuous mode.\n", + dev->name); /* Set RCTRL to PROM */ tempval = gfar_read(®s->rctrl); tempval |= RCTRL_PROM; @@ -1679,6 +1957,14 @@ static void gfar_set_multi(struct net_device *dev) if(dev->flags & IFF_ALLMULTI) { /* Set the hash to rx all multicast frames */ + gfar_write(®s->igaddr0, 0xffffffff); + gfar_write(®s->igaddr1, 0xffffffff); + gfar_write(®s->igaddr2, 0xffffffff); + gfar_write(®s->igaddr3, 0xffffffff); + gfar_write(®s->igaddr4, 0xffffffff); + gfar_write(®s->igaddr5, 0xffffffff); + gfar_write(®s->igaddr6, 0xffffffff); + gfar_write(®s->igaddr7, 0xffffffff); gfar_write(®s->gaddr0, 0xffffffff); gfar_write(®s->gaddr1, 0xffffffff); gfar_write(®s->gaddr2, 0xffffffff); @@ -1689,6 +1975,14 @@ static void gfar_set_multi(struct net_device *dev) gfar_write(®s->gaddr7, 0xffffffff); } else { /* zero out the hash */ + gfar_write(®s->igaddr0, 0x0); + gfar_write(®s->igaddr1, 0x0); + gfar_write(®s->igaddr2, 0x0); + gfar_write(®s->igaddr3, 0x0); + gfar_write(®s->igaddr4, 0x0); + gfar_write(®s->igaddr5, 0x0); + gfar_write(®s->igaddr6, 0x0); + gfar_write(®s->igaddr7, 0x0); gfar_write(®s->gaddr0, 0x0); gfar_write(®s->gaddr1, 0x0); gfar_write(®s->gaddr2, 0x0); @@ -1727,16 +2021,15 @@ static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr) { u32 tempval; struct gfar_private *priv = netdev_priv(dev); - struct gfar *regs = priv->regs; - u32 *hash = ®s->gaddr0; u32 result = ether_crc(MAC_ADDR_LEN, addr); - u8 whichreg = ((result >> 29) & 0x7); - u8 whichbit = ((result >> 24) & 0x1f); + int width = priv->hash_width; + u8 whichbit = (result >> (32 - width)) & 0x1f; + u8 whichreg = result >> (32 - width + 5); u32 value = (1 << (31-whichbit)); - tempval = gfar_read(&hash[whichreg]); + tempval = gfar_read(priv->hash_regs[whichreg]); tempval |= value; - gfar_write(&hash[whichreg], tempval); + gfar_write(priv->hash_regs[whichreg], tempval); return; } @@ -1754,10 +2047,9 @@ static irqreturn_t gfar_error(int irq, void *dev_id, struct pt_regs *regs) gfar_write(&priv->regs->ievent, IEVENT_ERR_MASK); /* Hmm... */ -#if defined (BRIEF_GFAR_ERRORS) || defined (VERBOSE_GFAR_ERRORS) - printk(KERN_DEBUG "%s: error interrupt (ievent=0x%08x imask=0x%08x)\n", - dev->name, events, gfar_read(&priv->regs->imask)); -#endif + if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv)) + printk(KERN_DEBUG "%s: error interrupt (ievent=0x%08x imask=0x%08x)\n", + dev->name, events, gfar_read(&priv->regs->imask)); /* Update the error counters */ if (events & IEVENT_TXE) { @@ -1768,19 +2060,17 @@ static irqreturn_t gfar_error(int irq, void *dev_id, struct pt_regs *regs) if (events & IEVENT_CRL) priv->stats.tx_aborted_errors++; if (events & IEVENT_XFUN) { -#ifdef VERBOSE_GFAR_ERRORS - printk(KERN_DEBUG "%s: underrun. packet dropped.\n", - dev->name); -#endif + if (netif_msg_tx_err(priv)) + printk(KERN_DEBUG "%s: underrun. packet dropped.\n", + dev->name); priv->stats.tx_dropped++; priv->extra_stats.tx_underrun++; /* Reactivate the Tx Queues */ gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT); } -#ifdef VERBOSE_GFAR_ERRORS - printk(KERN_DEBUG "%s: Transmit Error\n", dev->name); -#endif + if (netif_msg_tx_err(priv)) + printk(KERN_DEBUG "%s: Transmit Error\n", dev->name); } if (events & IEVENT_BSY) { priv->stats.rx_errors++; @@ -1793,35 +2083,31 @@ static irqreturn_t gfar_error(int irq, void *dev_id, struct pt_regs *regs) gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT); #endif -#ifdef VERBOSE_GFAR_ERRORS - printk(KERN_DEBUG "%s: busy error (rhalt: %x)\n", dev->name, - gfar_read(&priv->regs->rstat)); -#endif + if (netif_msg_rx_err(priv)) + printk(KERN_DEBUG "%s: busy error (rhalt: %x)\n", + dev->name, + gfar_read(&priv->regs->rstat)); } if (events & IEVENT_BABR) { priv->stats.rx_errors++; priv->extra_stats.rx_babr++; -#ifdef VERBOSE_GFAR_ERRORS - printk(KERN_DEBUG "%s: babbling error\n", dev->name); -#endif + if (netif_msg_rx_err(priv)) + printk(KERN_DEBUG "%s: babbling error\n", dev->name); } if (events & IEVENT_EBERR) { priv->extra_stats.eberr++; -#ifdef VERBOSE_GFAR_ERRORS - printk(KERN_DEBUG "%s: EBERR\n", dev->name); -#endif + if (netif_msg_rx_err(priv)) + printk(KERN_DEBUG "%s: EBERR\n", dev->name); } - if (events & IEVENT_RXC) -#ifdef VERBOSE_GFAR_ERRORS - printk(KERN_DEBUG "%s: control frame\n", dev->name); -#endif + if ((events & IEVENT_RXC) && netif_msg_rx_status(priv)) + if (netif_msg_rx_status(priv)) + printk(KERN_DEBUG "%s: control frame\n", dev->name); if (events & IEVENT_BABT) { priv->extra_stats.tx_babt++; -#ifdef VERBOSE_GFAR_ERRORS - printk(KERN_DEBUG "%s: babt error\n", dev->name); -#endif + if (netif_msg_tx_err(priv)) + printk(KERN_DEBUG "%s: babt error\n", dev->name); } return IRQ_HANDLED; } diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index c2f783a..28af087 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -1,4 +1,4 @@ -/* +/* * drivers/net/gianfar.h * * Gianfar Ethernet Driver @@ -53,6 +53,12 @@ /* The maximum number of packets to be handled in one call of gfar_poll */ #define GFAR_DEV_WEIGHT 64 +/* Length for FCB */ +#define GMAC_FCB_LEN 8 + +/* Default padding amount */ +#define DEFAULT_PADDING 2 + /* Number of bytes to align the rx bufs to */ #define RXBUF_ALIGNMENT 64 @@ -91,7 +97,7 @@ extern const char gfar_driver_version[]; #define JUMBO_FRAME_SIZE 9600 /* Latency of interface clock in nanoseconds */ -/* Interface clock latency , in this case, means the +/* Interface clock latency , in this case, means the * time described by a value of 1 in the interrupt * coalescing registers' time fields. Since those fields * refer to the time it takes for 64 clocks to pass, the @@ -166,9 +172,28 @@ extern const char gfar_driver_version[]; mk_ic_icft(count) | \ mk_ic_ictt(time)) +#define RCTRL_PAL_MASK 0x001f0000 +#define RCTRL_VLEX 0x00002000 +#define RCTRL_FILREN 0x00001000 +#define RCTRL_GHTX 0x00000400 +#define RCTRL_IPCSEN 0x00000200 +#define RCTRL_TUCSEN 0x00000100 +#define RCTRL_PRSDEP_MASK 0x000000c0 +#define RCTRL_PRSDEP_INIT 0x000000c0 #define RCTRL_PROM 0x00000008 +#define RCTRL_CHECKSUMMING (RCTRL_IPCSEN \ + | RCTRL_TUCSEN | RCTRL_PRSDEP_INIT) +#define RCTRL_EXTHASH (RCTRL_GHTX) +#define RCTRL_VLAN (RCTRL_PRSDEP_INIT) + + #define RSTAT_CLEAR_RHALT 0x00800000 +#define TCTRL_IPCSEN 0x00004000 +#define TCTRL_TUCSEN 0x00002000 +#define TCTRL_VLINS 0x00001000 +#define TCTRL_INIT_CSUM (TCTRL_TUCSEN | TCTRL_IPCSEN) + #define IEVENT_INIT_CLEAR 0xffffffff #define IEVENT_BABR 0x80000000 #define IEVENT_RXC 0x40000000 @@ -187,12 +212,16 @@ extern const char gfar_driver_version[]; #define IEVENT_RXB0 0x00008000 #define IEVENT_GRSC 0x00000100 #define IEVENT_RXF0 0x00000080 +#define IEVENT_FIR 0x00000008 +#define IEVENT_FIQ 0x00000004 +#define IEVENT_DPE 0x00000002 +#define IEVENT_PERR 0x00000001 #define IEVENT_RX_MASK (IEVENT_RXB0 | IEVENT_RXF0) #define IEVENT_TX_MASK (IEVENT_TXB | IEVENT_TXF) #define IEVENT_ERR_MASK \ (IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \ IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \ - | IEVENT_CRL | IEVENT_XFUN) + | IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR) #define IMASK_INIT_CLEAR 0x00000000 #define IMASK_BABR 0x80000000 @@ -212,10 +241,15 @@ extern const char gfar_driver_version[]; #define IMASK_RXB0 0x00008000 #define IMASK_GTSC 0x00000100 #define IMASK_RXFEN0 0x00000080 +#define IMASK_FIR 0x00000008 +#define IMASK_FIQ 0x00000004 +#define IMASK_DPE 0x00000002 +#define IMASK_PERR 0x00000001 #define IMASK_RX_DISABLED ~(IMASK_RXFEN0 | IMASK_BSY) #define IMASK_DEFAULT (IMASK_TXEEN | IMASK_TXFEN | IMASK_TXBEN | \ IMASK_RXFEN0 | IMASK_BSY | IMASK_EBERR | IMASK_BABR | \ - IMASK_XFUN | IMASK_RXC | IMASK_BABT) + IMASK_XFUN | IMASK_RXC | IMASK_BABT | IMASK_DPE \ + | IMASK_PERR) /* Attribute fields */ @@ -254,6 +288,18 @@ extern const char gfar_driver_version[]; #define TXBD_RETRYLIMIT 0x0040 #define TXBD_RETRYCOUNTMASK 0x003c #define TXBD_UNDERRUN 0x0002 +#define TXBD_TOE 0x0002 + +/* Tx FCB param bits */ +#define TXFCB_VLN 0x80 +#define TXFCB_IP 0x40 +#define TXFCB_IP6 0x20 +#define TXFCB_TUP 0x10 +#define TXFCB_UDP 0x08 +#define TXFCB_CIP 0x04 +#define TXFCB_CTU 0x02 +#define TXFCB_NPH 0x01 +#define TXFCB_DEFAULT (TXFCB_IP|TXFCB_TUP|TXFCB_CTU|TXFCB_NPH) /* RxBD status field bits */ #define RXBD_EMPTY 0x8000 @@ -273,6 +319,18 @@ extern const char gfar_driver_version[]; #define RXBD_TRUNCATED 0x0001 #define RXBD_STATS 0x01ff +/* Rx FCB status field bits */ +#define RXFCB_VLN 0x8000 +#define RXFCB_IP 0x4000 +#define RXFCB_IP6 0x2000 +#define RXFCB_TUP 0x1000 +#define RXFCB_CIP 0x0800 +#define RXFCB_CTU 0x0400 +#define RXFCB_EIP 0x0200 +#define RXFCB_ETU 0x0100 +#define RXFCB_PERR_MASK 0x000c +#define RXFCB_PERR_BADL3 0x0008 + struct txbd8 { u16 status; /* Status Fields */ @@ -280,6 +338,22 @@ struct txbd8 u32 bufPtr; /* Buffer Pointer */ }; +struct txfcb { + u8 vln:1, + ip:1, + ip6:1, + tup:1, + udp:1, + cip:1, + ctu:1, + nph:1; + u8 reserved; + u8 l4os; /* Level 4 Header Offset */ + u8 l3os; /* Level 3 Header Offset */ + u16 phcs; /* Pseudo-header Checksum */ + u16 vlctl; /* VLAN control word */ +}; + struct rxbd8 { u16 status; /* Status Fields */ @@ -287,6 +361,21 @@ struct rxbd8 u32 bufPtr; /* Buffer Pointer */ }; +struct rxfcb { + u16 vln:1, + ip:1, + ip6:1, + tup:1, + cip:1, + ctu:1, + eip:1, + etu:1; + u8 rq; /* Receive Queue index */ + u8 pro; /* Layer 4 Protocol */ + u16 reserved; + u16 vlctl; /* VLAN control word */ +}; + struct rmon_mib { u32 tr64; /* 0x.680 - Transmit and Receive 64-byte Frame Counter */ @@ -371,90 +460,191 @@ struct gfar_stats { struct gfar { - u8 res1[16]; - u32 ievent; /* 0x.010 - Interrupt Event Register */ - u32 imask; /* 0x.014 - Interrupt Mask Register */ - u32 edis; /* 0x.018 - Error Disabled Register */ + u32 tsec_id; /* 0x.000 - Controller ID register */ + u8 res1[12]; + u32 ievent; /* 0x.010 - Interrupt Event Register */ + u32 imask; /* 0x.014 - Interrupt Mask Register */ + u32 edis; /* 0x.018 - Error Disabled Register */ u8 res2[4]; - u32 ecntrl; /* 0x.020 - Ethernet Control Register */ - u32 minflr; /* 0x.024 - Minimum Frame Length Register */ - u32 ptv; /* 0x.028 - Pause Time Value Register */ - u32 dmactrl; /* 0x.02c - DMA Control Register */ - u32 tbipa; /* 0x.030 - TBI PHY Address Register */ + u32 ecntrl; /* 0x.020 - Ethernet Control Register */ + u32 minflr; /* 0x.024 - Minimum Frame Length Register */ + u32 ptv; /* 0x.028 - Pause Time Value Register */ + u32 dmactrl; /* 0x.02c - DMA Control Register */ + u32 tbipa; /* 0x.030 - TBI PHY Address Register */ u8 res3[88]; - u32 fifo_tx_thr; /* 0x.08c - FIFO transmit threshold register */ + u32 fifo_tx_thr; /* 0x.08c - FIFO transmit threshold register */ u8 res4[8]; - u32 fifo_tx_starve; /* 0x.098 - FIFO transmit starve register */ + u32 fifo_tx_starve; /* 0x.098 - FIFO transmit starve register */ u32 fifo_tx_starve_shutoff; /* 0x.09c - FIFO transmit starve shutoff register */ - u8 res5[96]; - u32 tctrl; /* 0x.100 - Transmit Control Register */ - u32 tstat; /* 0x.104 - Transmit Status Register */ - u8 res6[4]; - u32 tbdlen; /* 0x.10c - Transmit Buffer Descriptor Data Length Register */ - u32 txic; /* 0x.110 - Transmit Interrupt Coalescing Configuration Register */ - u8 res7[16]; - u32 ctbptr; /* 0x.124 - Current Transmit Buffer Descriptor Pointer Register */ - u8 res8[92]; - u32 tbptr; /* 0x.184 - Transmit Buffer Descriptor Pointer Low Register */ - u8 res9[124]; - u32 tbase; /* 0x.204 - Transmit Descriptor Base Address Register */ - u8 res10[168]; - u32 ostbd; /* 0x.2b0 - Out-of-Sequence Transmit Buffer Descriptor Register */ - u32 ostbdp; /* 0x.2b4 - Out-of-Sequence Transmit Data Buffer Pointer Register */ - u8 res11[72]; - u32 rctrl; /* 0x.300 - Receive Control Register */ - u32 rstat; /* 0x.304 - Receive Status Register */ - u8 res12[4]; - u32 rbdlen; /* 0x.30c - RxBD Data Length Register */ - u32 rxic; /* 0x.310 - Receive Interrupt Coalescing Configuration Register */ - u8 res13[16]; - u32 crbptr; /* 0x.324 - Current Receive Buffer Descriptor Pointer */ - u8 res14[24]; - u32 mrblr; /* 0x.340 - Maximum Receive Buffer Length Register */ - u8 res15[64]; - u32 rbptr; /* 0x.384 - Receive Buffer Descriptor Pointer */ - u8 res16[124]; - u32 rbase; /* 0x.404 - Receive Descriptor Base Address */ - u8 res17[248]; - u32 maccfg1; /* 0x.500 - MAC Configuration 1 Register */ - u32 maccfg2; /* 0x.504 - MAC Configuration 2 Register */ - u32 ipgifg; /* 0x.508 - Inter Packet Gap/Inter Frame Gap Register */ - u32 hafdup; /* 0x.50c - Half Duplex Register */ - u32 maxfrm; /* 0x.510 - Maximum Frame Length Register */ + u8 res5[4]; + u32 fifo_rx_pause; /* 0x.0a4 - FIFO receive pause threshold register */ + u32 fifo_rx_alarm; /* 0x.0a8 - FIFO receive alarm threshold register */ + u8 res6[84]; + u32 tctrl; /* 0x.100 - Transmit Control Register */ + u32 tstat; /* 0x.104 - Transmit Status Register */ + u32 dfvlan; /* 0x.108 - Default VLAN Control word */ + u32 tbdlen; /* 0x.10c - Transmit Buffer Descriptor Data Length Register */ + u32 txic; /* 0x.110 - Transmit Interrupt Coalescing Configuration Register */ + u32 tqueue; /* 0x.114 - Transmit queue control register */ + u8 res7[40]; + u32 tr03wt; /* 0x.140 - TxBD Rings 0-3 round-robin weightings */ + u32 tr47wt; /* 0x.144 - TxBD Rings 4-7 round-robin weightings */ + u8 res8[52]; + u32 tbdbph; /* 0x.17c - Tx data buffer pointer high */ + u8 res9a[4]; + u32 tbptr0; /* 0x.184 - TxBD Pointer for ring 0 */ + u8 res9b[4]; + u32 tbptr1; /* 0x.18c - TxBD Pointer for ring 1 */ + u8 res9c[4]; + u32 tbptr2; /* 0x.194 - TxBD Pointer for ring 2 */ + u8 res9d[4]; + u32 tbptr3; /* 0x.19c - TxBD Pointer for ring 3 */ + u8 res9e[4]; + u32 tbptr4; /* 0x.1a4 - TxBD Pointer for ring 4 */ + u8 res9f[4]; + u32 tbptr5; /* 0x.1ac - TxBD Pointer for ring 5 */ + u8 res9g[4]; + u32 tbptr6; /* 0x.1b4 - TxBD Pointer for ring 6 */ + u8 res9h[4]; + u32 tbptr7; /* 0x.1bc - TxBD Pointer for ring 7 */ + u8 res9[64]; + u32 tbaseh; /* 0x.200 - TxBD base address high */ + u32 tbase0; /* 0x.204 - TxBD Base Address of ring 0 */ + u8 res10a[4]; + u32 tbase1; /* 0x.20c - TxBD Base Address of ring 1 */ + u8 res10b[4]; + u32 tbase2; /* 0x.214 - TxBD Base Address of ring 2 */ + u8 res10c[4]; + u32 tbase3; /* 0x.21c - TxBD Base Address of ring 3 */ + u8 res10d[4]; + u32 tbase4; /* 0x.224 - TxBD Base Address of ring 4 */ + u8 res10e[4]; + u32 tbase5; /* 0x.22c - TxBD Base Address of ring 5 */ + u8 res10f[4]; + u32 tbase6; /* 0x.234 - TxBD Base Address of ring 6 */ + u8 res10g[4]; + u32 tbase7; /* 0x.23c - TxBD Base Address of ring 7 */ + u8 res10[192]; + u32 rctrl; /* 0x.300 - Receive Control Register */ + u32 rstat; /* 0x.304 - Receive Status Register */ + u8 res12[8]; + u32 rxic; /* 0x.310 - Receive Interrupt Coalescing Configuration Register */ + u32 rqueue; /* 0x.314 - Receive queue control register */ + u8 res13[24]; + u32 rbifx; /* 0x.330 - Receive bit field extract control register */ + u32 rqfar; /* 0x.334 - Receive queue filing table address register */ + u32 rqfcr; /* 0x.338 - Receive queue filing table control register */ + u32 rqfpr; /* 0x.33c - Receive queue filing table property register */ + u32 mrblr; /* 0x.340 - Maximum Receive Buffer Length Register */ + u8 res14[56]; + u32 rbdbph; /* 0x.37c - Rx data buffer pointer high */ + u8 res15a[4]; + u32 rbptr0; /* 0x.384 - RxBD pointer for ring 0 */ + u8 res15b[4]; + u32 rbptr1; /* 0x.38c - RxBD pointer for ring 1 */ + u8 res15c[4]; + u32 rbptr2; /* 0x.394 - RxBD pointer for ring 2 */ + u8 res15d[4]; + u32 rbptr3; /* 0x.39c - RxBD pointer for ring 3 */ + u8 res15e[4]; + u32 rbptr4; /* 0x.3a4 - RxBD pointer for ring 4 */ + u8 res15f[4]; + u32 rbptr5; /* 0x.3ac - RxBD pointer for ring 5 */ + u8 res15g[4]; + u32 rbptr6; /* 0x.3b4 - RxBD pointer for ring 6 */ + u8 res15h[4]; + u32 rbptr7; /* 0x.3bc - RxBD pointer for ring 7 */ + u8 res16[64]; + u32 rbaseh; /* 0x.400 - RxBD base address high */ + u32 rbase0; /* 0x.404 - RxBD base address of ring 0 */ + u8 res17a[4]; + u32 rbase1; /* 0x.40c - RxBD base address of ring 1 */ + u8 res17b[4]; + u32 rbase2; /* 0x.414 - RxBD base address of ring 2 */ + u8 res17c[4]; + u32 rbase3; /* 0x.41c - RxBD base address of ring 3 */ + u8 res17d[4]; + u32 rbase4; /* 0x.424 - RxBD base address of ring 4 */ + u8 res17e[4]; + u32 rbase5; /* 0x.42c - RxBD base address of ring 5 */ + u8 res17f[4]; + u32 rbase6; /* 0x.434 - RxBD base address of ring 6 */ + u8 res17g[4]; + u32 rbase7; /* 0x.43c - RxBD base address of ring 7 */ + u8 res17[192]; + u32 maccfg1; /* 0x.500 - MAC Configuration 1 Register */ + u32 maccfg2; /* 0x.504 - MAC Configuration 2 Register */ + u32 ipgifg; /* 0x.508 - Inter Packet Gap/Inter Frame Gap Register */ + u32 hafdup; /* 0x.50c - Half Duplex Register */ + u32 maxfrm; /* 0x.510 - Maximum Frame Length Register */ u8 res18[12]; - u32 miimcfg; /* 0x.520 - MII Management Configuration Register */ - u32 miimcom; /* 0x.524 - MII Management Command Register */ - u32 miimadd; /* 0x.528 - MII Management Address Register */ - u32 miimcon; /* 0x.52c - MII Management Control Register */ - u32 miimstat; /* 0x.530 - MII Management Status Register */ - u32 miimind; /* 0x.534 - MII Management Indicator Register */ + u32 miimcfg; /* 0x.520 - MII Management Configuration Register */ + u32 miimcom; /* 0x.524 - MII Management Command Register */ + u32 miimadd; /* 0x.528 - MII Management Address Register */ + u32 miimcon; /* 0x.52c - MII Management Control Register */ + u32 miimstat; /* 0x.530 - MII Management Status Register */ + u32 miimind; /* 0x.534 - MII Management Indicator Register */ u8 res19[4]; - u32 ifstat; /* 0x.53c - Interface Status Register */ - u32 macstnaddr1; /* 0x.540 - Station Address Part 1 Register */ - u32 macstnaddr2; /* 0x.544 - Station Address Part 2 Register */ - u8 res20[312]; - struct rmon_mib rmon; - u8 res21[192]; - u32 iaddr0; /* 0x.800 - Indivdual address register 0 */ - u32 iaddr1; /* 0x.804 - Indivdual address register 1 */ - u32 iaddr2; /* 0x.808 - Indivdual address register 2 */ - u32 iaddr3; /* 0x.80c - Indivdual address register 3 */ - u32 iaddr4; /* 0x.810 - Indivdual address register 4 */ - u32 iaddr5; /* 0x.814 - Indivdual address register 5 */ - u32 iaddr6; /* 0x.818 - Indivdual address register 6 */ - u32 iaddr7; /* 0x.81c - Indivdual address register 7 */ + u32 ifstat; /* 0x.53c - Interface Status Register */ + u32 macstnaddr1; /* 0x.540 - Station Address Part 1 Register */ + u32 macstnaddr2; /* 0x.544 - Station Address Part 2 Register */ + u32 mac01addr1; /* 0x.548 - MAC exact match address 1, part 1 */ + u32 mac01addr2; /* 0x.54c - MAC exact match address 1, part 2 */ + u32 mac02addr1; /* 0x.550 - MAC exact match address 2, part 1 */ + u32 mac02addr2; /* 0x.554 - MAC exact match address 2, part 2 */ + u32 mac03addr1; /* 0x.558 - MAC exact match address 3, part 1 */ + u32 mac03addr2; /* 0x.55c - MAC exact match address 3, part 2 */ + u32 mac04addr1; /* 0x.560 - MAC exact match address 4, part 1 */ + u32 mac04addr2; /* 0x.564 - MAC exact match address 4, part 2 */ + u32 mac05addr1; /* 0x.568 - MAC exact match address 5, part 1 */ + u32 mac05addr2; /* 0x.56c - MAC exact match address 5, part 2 */ + u32 mac06addr1; /* 0x.570 - MAC exact match address 6, part 1 */ + u32 mac06addr2; /* 0x.574 - MAC exact match address 6, part 2 */ + u32 mac07addr1; /* 0x.578 - MAC exact match address 7, part 1 */ + u32 mac07addr2; /* 0x.57c - MAC exact match address 7, part 2 */ + u32 mac08addr1; /* 0x.580 - MAC exact match address 8, part 1 */ + u32 mac08addr2; /* 0x.584 - MAC exact match address 8, part 2 */ + u32 mac09addr1; /* 0x.588 - MAC exact match address 9, part 1 */ + u32 mac09addr2; /* 0x.58c - MAC exact match address 9, part 2 */ + u32 mac10addr1; /* 0x.590 - MAC exact match address 10, part 1*/ + u32 mac10addr2; /* 0x.594 - MAC exact match address 10, part 2*/ + u32 mac11addr1; /* 0x.598 - MAC exact match address 11, part 1*/ + u32 mac11addr2; /* 0x.59c - MAC exact match address 11, part 2*/ + u32 mac12addr1; /* 0x.5a0 - MAC exact match address 12, part 1*/ + u32 mac12addr2; /* 0x.5a4 - MAC exact match address 12, part 2*/ + u32 mac13addr1; /* 0x.5a8 - MAC exact match address 13, part 1*/ + u32 mac13addr2; /* 0x.5ac - MAC exact match address 13, part 2*/ + u32 mac14addr1; /* 0x.5b0 - MAC exact match address 14, part 1*/ + u32 mac14addr2; /* 0x.5b4 - MAC exact match address 14, part 2*/ + u32 mac15addr1; /* 0x.5b8 - MAC exact match address 15, part 1*/ + u32 mac15addr2; /* 0x.5bc - MAC exact match address 15, part 2*/ + u8 res20[192]; + struct rmon_mib rmon; /* 0x.680-0x.73c */ + u32 rrej; /* 0x.740 - Receive filer rejected packet counter */ + u8 res21[188]; + u32 igaddr0; /* 0x.800 - Indivdual/Group address register 0*/ + u32 igaddr1; /* 0x.804 - Indivdual/Group address register 1*/ + u32 igaddr2; /* 0x.808 - Indivdual/Group address register 2*/ + u32 igaddr3; /* 0x.80c - Indivdual/Group address register 3*/ + u32 igaddr4; /* 0x.810 - Indivdual/Group address register 4*/ + u32 igaddr5; /* 0x.814 - Indivdual/Group address register 5*/ + u32 igaddr6; /* 0x.818 - Indivdual/Group address register 6*/ + u32 igaddr7; /* 0x.81c - Indivdual/Group address register 7*/ u8 res22[96]; - u32 gaddr0; /* 0x.880 - Global address register 0 */ - u32 gaddr1; /* 0x.884 - Global address register 1 */ - u32 gaddr2; /* 0x.888 - Global address register 2 */ - u32 gaddr3; /* 0x.88c - Global address register 3 */ - u32 gaddr4; /* 0x.890 - Global address register 4 */ - u32 gaddr5; /* 0x.894 - Global address register 5 */ - u32 gaddr6; /* 0x.898 - Global address register 6 */ - u32 gaddr7; /* 0x.89c - Global address register 7 */ - u8 res23[856]; - u32 attr; /* 0x.bf8 - Attributes Register */ - u32 attreli; /* 0x.bfc - Attributes Extract Length and Extract Index Register */ + u32 gaddr0; /* 0x.880 - Group address register 0 */ + u32 gaddr1; /* 0x.884 - Group address register 1 */ + u32 gaddr2; /* 0x.888 - Group address register 2 */ + u32 gaddr3; /* 0x.88c - Group address register 3 */ + u32 gaddr4; /* 0x.890 - Group address register 4 */ + u32 gaddr5; /* 0x.894 - Group address register 5 */ + u32 gaddr6; /* 0x.898 - Group address register 6 */ + u32 gaddr7; /* 0x.89c - Group address register 7 */ + u8 res23a[352]; + u32 fifocfg; /* 0x.a00 - FIFO interface config register */ + u8 res23b[252]; + u8 res23c[248]; + u32 attr; /* 0x.bf8 - Attributes Register */ + u32 attreli; /* 0x.bfc - Attributes Extract Length and Extract Index Register */ u8 res24[1024]; }; @@ -496,6 +686,8 @@ struct gfar_private { struct txbd8 *cur_tx; /* Next free ring entry */ struct txbd8 *dirty_tx; /* The Ring entry to be freed. */ struct gfar *regs; /* Pointer to the GFAR memory mapped Registers */ + u32 *hash_regs[16]; + int hash_width; struct gfar *phyregs; struct work_struct tq; struct timer_list phy_info_timer; @@ -506,9 +698,12 @@ struct gfar_private { unsigned int rx_stash_size; unsigned int tx_ring_size; unsigned int rx_ring_size; - wait_queue_head_t rxcleanupq; - unsigned int rxclean; + unsigned char vlan_enable:1, + rx_csum_enable:1, + extended_hash:1; + unsigned short padding; + struct vlan_group *vlgrp; /* Info structure initialized by board setup code */ unsigned int interruptTransmit; unsigned int interruptReceive; @@ -519,6 +714,8 @@ struct gfar_private { int oldspeed; int oldduplex; int oldlink; + + uint32_t msg_enable; }; extern inline u32 gfar_read(volatile unsigned *addr) diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c index 28046e9..a451de6 100644 --- a/drivers/net/gianfar_ethtool.c +++ b/drivers/net/gianfar_ethtool.c @@ -46,16 +46,18 @@ extern int startup_gfar(struct net_device *dev); extern void stop_gfar(struct net_device *dev); -extern void gfar_receive(int irq, void *dev_id, struct pt_regs *regs); +extern void gfar_halt(struct net_device *dev); +extern void gfar_start(struct net_device *dev); +extern int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit); -void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, +static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf); -void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf); -int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals); -int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals); -void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals); -int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals); -void gfar_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo); +static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf); +static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals); +static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals); +static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals); +static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals); +static void gfar_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo); static char stat_gstrings[][ETH_GSTRING_LEN] = { "rx-dropped-by-kernel", @@ -118,57 +120,56 @@ static char stat_gstrings[][ETH_GSTRING_LEN] = { "tx-fragmented-frames", }; +/* Fill in a buffer with the strings which correspond to the + * stats */ +static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf) +{ + struct gfar_private *priv = netdev_priv(dev); + + if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) + memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN); + else + memcpy(buf, stat_gstrings, + GFAR_EXTRA_STATS_LEN * ETH_GSTRING_LEN); +} + /* Fill in an array of 64-bit statistics from various sources. * This array will be appended to the end of the ethtool_stats * structure, and returned to user space */ -void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf) +static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf) { int i; struct gfar_private *priv = netdev_priv(dev); - u32 *rmon = (u32 *) & priv->regs->rmon; u64 *extra = (u64 *) & priv->extra_stats; - struct gfar_stats *stats = (struct gfar_stats *) buf; - for (i = 0; i < GFAR_RMON_LEN; i++) { - stats->rmon[i] = (u64) (rmon[i]); - } - - for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) { - stats->extra[i] = extra[i]; - } -} + if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) { + u32 *rmon = (u32 *) & priv->regs->rmon; + struct gfar_stats *stats = (struct gfar_stats *) buf; -/* Returns the number of stats (and their corresponding strings) */ -int gfar_stats_count(struct net_device *dev) -{ - return GFAR_STATS_LEN; -} + for (i = 0; i < GFAR_RMON_LEN; i++) + stats->rmon[i] = (u64) (rmon[i]); -void gfar_gstrings_normon(struct net_device *dev, u32 stringset, u8 * buf) -{ - memcpy(buf, stat_gstrings, GFAR_EXTRA_STATS_LEN * ETH_GSTRING_LEN); + for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) + stats->extra[i] = extra[i]; + } else + for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) + buf[i] = extra[i]; } -void gfar_fill_stats_normon(struct net_device *dev, - struct ethtool_stats *dummy, u64 * buf) +/* Returns the number of stats (and their corresponding strings) */ +static int gfar_stats_count(struct net_device *dev) { - int i; struct gfar_private *priv = netdev_priv(dev); - u64 *extra = (u64 *) & priv->extra_stats; - for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) { - buf[i] = extra[i]; - } + if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) + return GFAR_STATS_LEN; + else + return GFAR_EXTRA_STATS_LEN; } - -int gfar_stats_count_normon(struct net_device *dev) -{ - return GFAR_EXTRA_STATS_LEN; -} /* Fills in the drvinfo structure with some basic info */ -void gfar_gdrvinfo(struct net_device *dev, struct +static void gfar_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { strncpy(drvinfo->driver, DRV_NAME, GFAR_INFOSTR_LEN); @@ -182,7 +183,7 @@ void gfar_gdrvinfo(struct net_device *dev, struct } /* Return the current settings in the ethtool_cmd structure */ -int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd) +static int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd) { struct gfar_private *priv = netdev_priv(dev); uint gigabit_support = @@ -216,13 +217,13 @@ int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd) } /* Return the length of the register structure */ -int gfar_reglen(struct net_device *dev) +static int gfar_reglen(struct net_device *dev) { return sizeof (struct gfar); } /* Return a dump of the GFAR register space */ -void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) +static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) { int i; struct gfar_private *priv = netdev_priv(dev); @@ -233,13 +234,6 @@ void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regb buf[i] = theregs[i]; } -/* Fill in a buffer with the strings which correspond to the - * stats */ -void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf) -{ - memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN); -} - /* Convert microseconds to ethernet clock ticks, which changes * depending on what speed the controller is running at */ static unsigned int gfar_usecs2ticks(struct gfar_private *priv, unsigned int usecs) @@ -291,9 +285,12 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic /* Get the coalescing parameters, and put them in the cvals * structure. */ -int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) +static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) { struct gfar_private *priv = netdev_priv(dev); + + if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) + return -EOPNOTSUPP; cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, priv->rxtime); cvals->rx_max_coalesced_frames = priv->rxcount; @@ -337,10 +334,13 @@ int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) * Both cvals->*_usecs and cvals->*_frames have to be > 0 * in order for coalescing to be active */ -int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) +static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) { struct gfar_private *priv = netdev_priv(dev); + if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) + return -EOPNOTSUPP; + /* Set up rx coalescing */ if ((cvals->rx_coalesce_usecs == 0) || (cvals->rx_max_coalesced_frames == 0)) @@ -379,7 +379,7 @@ int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) /* Fills in rvals with the current ring parameters. Currently, * rx, rx_mini, and rx_jumbo rings are the same size, as mini and * jumbo are ignored by the driver */ -void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals) +static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals) { struct gfar_private *priv = netdev_priv(dev); @@ -401,9 +401,8 @@ void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals) * necessary so that we don't mess things up while we're in * motion. We wait for the ring to be clean before reallocating * the rings. */ -int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals) +static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals) { - u32 tempval; struct gfar_private *priv = netdev_priv(dev); int err = 0; @@ -425,37 +424,54 @@ int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals) return -EINVAL; } - /* Stop the controller so we don't rx any more frames */ - /* But first, make sure we clear the bits */ - tempval = gfar_read(&priv->regs->dmactrl); - tempval &= ~(DMACTRL_GRS | DMACTRL_GTS); - gfar_write(&priv->regs->dmactrl, tempval); + if (dev->flags & IFF_UP) { + unsigned long flags; - tempval = gfar_read(&priv->regs->dmactrl); - tempval |= (DMACTRL_GRS | DMACTRL_GTS); - gfar_write(&priv->regs->dmactrl, tempval); + /* Halt TX and RX, and process the frames which + * have already been received */ + spin_lock_irqsave(&priv->lock, flags); + gfar_halt(dev); + gfar_clean_rx_ring(dev, priv->rx_ring_size); + spin_unlock_irqrestore(&priv->lock, flags); - while (!(gfar_read(&priv->regs->ievent) & (IEVENT_GRSC | IEVENT_GTSC))) - cpu_relax(); + /* Now we take down the rings to rebuild them */ + stop_gfar(dev); + } - /* Note that rx is not clean right now */ - priv->rxclean = 0; + /* Change the size */ + priv->rx_ring_size = rvals->rx_pending; + priv->tx_ring_size = rvals->tx_pending; - if (dev->flags & IFF_UP) { - /* Tell the driver to process the rest of the frames */ - gfar_receive(0, (void *) dev, NULL); + /* Rebuild the rings with the new size */ + if (dev->flags & IFF_UP) + err = startup_gfar(dev); - /* Now wait for it to be done */ - wait_event_interruptible(priv->rxcleanupq, priv->rxclean); + return err; +} - /* Ok, all packets have been handled. Now we bring it down, - * change the ring size, and bring it up */ +static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) +{ + struct gfar_private *priv = netdev_priv(dev); + int err = 0; + if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) + return -EOPNOTSUPP; + + if (dev->flags & IFF_UP) { + unsigned long flags; + + /* Halt TX and RX, and process the frames which + * have already been received */ + spin_lock_irqsave(&priv->lock, flags); + gfar_halt(dev); + gfar_clean_rx_ring(dev, priv->rx_ring_size); + spin_unlock_irqrestore(&priv->lock, flags); + + /* Now we take down the rings to rebuild them */ stop_gfar(dev); } - priv->rx_ring_size = rvals->rx_pending; - priv->tx_ring_size = rvals->tx_pending; + priv->rx_csum_enable = data; if (dev->flags & IFF_UP) err = startup_gfar(dev); @@ -463,6 +479,61 @@ int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals) return err; } +static uint32_t gfar_get_rx_csum(struct net_device *dev) +{ + struct gfar_private *priv = netdev_priv(dev); + + if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) + return 0; + + return priv->rx_csum_enable; +} + +static int gfar_set_tx_csum(struct net_device *dev, uint32_t data) +{ + unsigned long flags; + struct gfar_private *priv = netdev_priv(dev); + + if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) + return -EOPNOTSUPP; + + spin_lock_irqsave(&priv->lock, flags); + gfar_halt(dev); + + if (data) + dev->features |= NETIF_F_IP_CSUM; + else + dev->features &= ~NETIF_F_IP_CSUM; + + gfar_start(dev); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static uint32_t gfar_get_tx_csum(struct net_device *dev) +{ + struct gfar_private *priv = netdev_priv(dev); + + if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) + return 0; + + return (dev->features & NETIF_F_IP_CSUM) != 0; +} + +static uint32_t gfar_get_msglevel(struct net_device *dev) +{ + struct gfar_private *priv = netdev_priv(dev); + return priv->msg_enable; +} + +static void gfar_set_msglevel(struct net_device *dev, uint32_t data) +{ + struct gfar_private *priv = netdev_priv(dev); + priv->msg_enable = data; +} + + struct ethtool_ops gfar_ethtool_ops = { .get_settings = gfar_gsettings, .get_drvinfo = gfar_gdrvinfo, @@ -476,52 +547,10 @@ struct ethtool_ops gfar_ethtool_ops = { .get_strings = gfar_gstrings, .get_stats_count = gfar_stats_count, .get_ethtool_stats = gfar_fill_stats, -}; - -struct ethtool_ops gfar_normon_nocoalesce_ethtool_ops = { - .get_settings = gfar_gsettings, - .get_drvinfo = gfar_gdrvinfo, - .get_regs_len = gfar_reglen, - .get_regs = gfar_get_regs, - .get_link = ethtool_op_get_link, - .get_ringparam = gfar_gringparam, - .set_ringparam = gfar_sringparam, - .get_strings = gfar_gstrings_normon, - .get_stats_count = gfar_stats_count_normon, - .get_ethtool_stats = gfar_fill_stats_normon, -}; - -struct ethtool_ops gfar_nocoalesce_ethtool_ops = { - .get_settings = gfar_gsettings, - .get_drvinfo = gfar_gdrvinfo, - .get_regs_len = gfar_reglen, - .get_regs = gfar_get_regs, - .get_link = ethtool_op_get_link, - .get_ringparam = gfar_gringparam, - .set_ringparam = gfar_sringparam, - .get_strings = gfar_gstrings, - .get_stats_count = gfar_stats_count, - .get_ethtool_stats = gfar_fill_stats, -}; - -struct ethtool_ops gfar_normon_ethtool_ops = { - .get_settings = gfar_gsettings, - .get_drvinfo = gfar_gdrvinfo, - .get_regs_len = gfar_reglen, - .get_regs = gfar_get_regs, - .get_link = ethtool_op_get_link, - .get_coalesce = gfar_gcoalesce, - .set_coalesce = gfar_scoalesce, - .get_ringparam = gfar_gringparam, - .set_ringparam = gfar_sringparam, - .get_strings = gfar_gstrings_normon, - .get_stats_count = gfar_stats_count_normon, - .get_ethtool_stats = gfar_fill_stats_normon, -}; - -struct ethtool_ops *gfar_op_array[] = { - &gfar_ethtool_ops, - &gfar_normon_ethtool_ops, - &gfar_nocoalesce_ethtool_ops, - &gfar_normon_nocoalesce_ethtool_ops + .get_rx_csum = gfar_get_rx_csum, + .get_tx_csum = gfar_get_tx_csum, + .set_rx_csum = gfar_set_rx_csum, + .set_tx_csum = gfar_set_tx_csum, + .get_msglevel = gfar_get_msglevel, + .set_msglevel = gfar_set_msglevel, }; diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 13f1148..3213f3e 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -850,7 +850,7 @@ static int pcnet32_phys_id(struct net_device *dev, u32 data) if ((!data) || (data > (u32)(MAX_SCHEDULE_TIMEOUT / HZ))) data = (u32)(MAX_SCHEDULE_TIMEOUT / HZ); - schedule_timeout(data * HZ); + msleep_interruptible(data * 1000); del_timer_sync(&lp->blink_timer); /* Restore the original value of the bcrs */ diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c index e15369c..d6388e1 100644 --- a/drivers/net/sb1000.c +++ b/drivers/net/sb1000.c @@ -90,7 +90,6 @@ static int sb1000_close(struct net_device *dev); /* SB1000 hardware routines to be used during open/configuration phases */ -static inline void nicedelay(unsigned long usecs); static inline int card_wait_for_busy_clear(const int ioaddr[], const char* name); static inline int card_wait_for_ready(const int ioaddr[], const char* name, @@ -254,13 +253,6 @@ static struct pnp_driver sb1000_driver = { static const int TimeOutJiffies = (875 * HZ) / 100; -static inline void nicedelay(unsigned long usecs) -{ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ); - return; -} - /* Card Wait For Busy Clear (cannot be used during an interrupt) */ static inline int card_wait_for_busy_clear(const int ioaddr[], const char* name) @@ -475,7 +467,7 @@ sb1000_reset(const int ioaddr[], const char* name) udelay(1000); outb(0x0, port); inb(port); - nicedelay(60000); + ssleep(1); outb(0x4, port); inb(port); udelay(1000); @@ -537,7 +529,7 @@ sb1000_activate(const int ioaddr[], const char* name) const unsigned char Command0[6] = {0x80, 0x11, 0x00, 0x00, 0x00, 0x00}; const unsigned char Command1[6] = {0x80, 0x16, 0x00, 0x00, 0x00, 0x00}; - nicedelay(50000); + ssleep(1); if ((status = card_send_command(ioaddr, name, Command0, st))) return status; if ((status = card_send_command(ioaddr, name, Command1, st))) @@ -944,7 +936,7 @@ sb1000_open(struct net_device *dev) /* initialize sb1000 */ if ((status = sb1000_reset(ioaddr, name))) return status; - nicedelay(200000); + ssleep(1); if ((status = sb1000_check_CRC(ioaddr, name))) return status; diff --git a/drivers/net/skfp/Makefile b/drivers/net/skfp/Makefile index 5f4bb1a..cb23580 100644 --- a/drivers/net/skfp/Makefile +++ b/drivers/net/skfp/Makefile @@ -6,8 +6,8 @@ obj-$(CONFIG_SKFP) += skfp.o skfp-objs := skfddi.o hwmtm.o fplustm.o smt.o cfm.o \ ecm.o pcmplc.o pmf.o queue.o rmt.o \ - smtdef.o smtinit.o smttimer.o srf.o smtparse.o\ - hwt.o drvfbi.o ess.o + smtdef.o smtinit.o smttimer.o srf.o hwt.o \ + drvfbi.o ess.o # NOTE: # Compiling this driver produces some warnings (and some more are diff --git a/drivers/net/skfp/drvfbi.c b/drivers/net/skfp/drvfbi.c index 052e841..5b47583 100644 --- a/drivers/net/skfp/drvfbi.c +++ b/drivers/net/skfp/drvfbi.c @@ -105,8 +105,8 @@ extern int AIX_vpdReadByte() ; #endif -/* Prototypes of local functions. */ -void smt_stop_watchdog(struct s_smc *smc); +/* Prototype of a local function. */ +static void smt_stop_watchdog(struct s_smc *smc); #ifdef MCA static int read_card_id() ; @@ -631,7 +631,7 @@ void plc_clear_irq(struct s_smc *smc, int p) * LED_Y_OFF just switch yellow LED off * LED_Y_ON just switch yello LED on */ -void led_indication(struct s_smc *smc, int led_event) +static void led_indication(struct s_smc *smc, int led_event) { /* use smc->hw.mac_ring_is_up == TRUE * as indication for Ring Operational @@ -764,122 +764,6 @@ void llc_recover_tx(struct s_smc *smc) #endif } -/*--------------------------- DMA init ----------------------------*/ -#ifdef ISA - -/* - * init DMA - */ -void init_dma(struct s_smc *smc, int dma) -{ - SK_UNUSED(smc) ; - - /* - * set cascade mode, - * clear mask bit (enable DMA cannal) - */ - if (dma > 3) { - outp(0xd6,(dma & 0x03) | 0xc0) ; - outp(0xd4, dma & 0x03) ; - } - else { - outp(0x0b,(dma & 0x03) | 0xc0) ; - outp(0x0a,dma & 0x03) ; - } -} - -/* - * disable DMA - */ -void dis_dma(struct s_smc *smc, int dma) -{ - SK_UNUSED(smc) ; - - /* - * set mask bit (disable DMA cannal) - */ - if (dma > 3) { - outp(0xd4,(dma & 0x03) | 0x04) ; - } - else { - outp(0x0a,(dma & 0x03) | 0x04) ; - } -} - -#endif /* ISA */ - -#ifdef EISA - -/*arrays with io addresses of dma controller length and address registers*/ -static const int cntr[8] = { 0x001,0x003,0x005,0x007,0,0x0c6,0x0ca,0x0ce } ; -static const int base[8] = { 0x000,0x002,0x004,0x006,0,0x0c4,0x0c8,0x0cc } ; -static const int page[8] = { 0x087,0x083,0x081,0x082,0,0x08b,0x089,0x08a } ; - -void init_dma(struct s_smc *smc, int dma) -{ - /* - * extended mode register - * 32 bit IO - * type c - * TC output - * disable stop - */ - - /* mode read (write) demand */ - smc->hw.dma_rmode = (dma & 3) | 0x08 | 0x0 ; - smc->hw.dma_wmode = (dma & 3) | 0x04 | 0x0 ; - - /* 32 bit IO's, burst DMA mode (type "C") */ - smc->hw.dma_emode = (dma & 3) | 0x08 | 0x30 ; - - outp((dma < 4) ? 0x40b : 0x4d6,smc->hw.dma_emode) ; - - /* disable chaining */ - outp((dma < 4) ? 0x40a : 0x4d4,(dma&3)) ; - - /*load dma controller addresses for fast access during set dma*/ - smc->hw.dma_base_word_count = cntr[smc->hw.dma]; - smc->hw.dma_base_address = base[smc->hw.dma]; - smc->hw.dma_base_address_page = page[smc->hw.dma]; - -} - -void dis_dma(struct s_smc *smc, int dma) -{ - SK_UNUSED(smc) ; - - outp((dma < 4) ? 0x0a : 0xd4,(dma&3)|4) ;/* mask bit */ -} -#endif /* EISA */ - -#ifdef MCA -void init_dma(struct s_smc *smc, int dma) -{ - SK_UNUSED(smc) ; - SK_UNUSED(dma) ; -} - -void dis_dma(struct s_smc *smc, int dma) -{ - SK_UNUSED(smc) ; - SK_UNUSED(dma) ; -} -#endif - -#ifdef PCI -void init_dma(struct s_smc *smc, int dma) -{ - SK_UNUSED(smc) ; - SK_UNUSED(dma) ; -} - -void dis_dma(struct s_smc *smc, int dma) -{ - SK_UNUSED(smc) ; - SK_UNUSED(dma) ; -} -#endif - #ifdef MULT_OEM static int is_equal_num(char comp1[], char comp2[], int num) { @@ -1407,7 +1291,7 @@ void smt_start_watchdog(struct s_smc *smc) #endif /* DEBUG */ } -void smt_stop_watchdog(struct s_smc *smc) +static void smt_stop_watchdog(struct s_smc *smc) { SK_UNUSED(smc) ; /* Make LINT happy. */ #ifndef DEBUG @@ -1422,104 +1306,6 @@ void smt_stop_watchdog(struct s_smc *smc) } #ifdef PCI -static char get_rom_byte(struct s_smc *smc, u_short addr) -{ - GET_PAGE(addr) ; - return (READ_PROM(ADDR(B2_FDP))) ; -} - -/* - * ROM image defines - */ -#define ROM_SIG_1 0 -#define ROM_SIG_2 1 -#define PCI_DATA_1 0x18 -#define PCI_DATA_2 0x19 - -/* - * PCI data structure defines - */ -#define VPD_DATA_1 0x08 -#define VPD_DATA_2 0x09 -#define IMAGE_LEN_1 0x10 -#define IMAGE_LEN_2 0x11 -#define CODE_TYPE 0x14 -#define INDICATOR 0x15 - -/* - * BEGIN_MANUAL_ENTRY(mac_drv_vpd_read) - * mac_drv_vpd_read(smc,buf,size,image) - * - * function DOWNCALL (FDDIWARE) - * reads the VPD data of the FPROM and writes it into the - * buffer - * - * para buf points to the buffer for the VPD data - * size size of the VPD data buffer - * image boot image; code type of the boot image - * image = 0 Intel x86, PC-AT compatible - * 1 OPENBOOT standard for PCI - * 2-FF reserved - * - * returns len number of VPD data bytes read form the FPROM - * <0 number of read bytes - * >0 error: data invalid - * - * END_MANUAL_ENTRY - */ -int mac_drv_vpd_read(struct s_smc *smc, char *buf, int size, char image) -{ - u_short ibase ; - u_short pci_base ; - u_short vpd ; - int len ; - - len = 0 ; - ibase = 0 ; - /* - * as long images defined - */ - while (get_rom_byte(smc,ibase+ROM_SIG_1) == 0x55 && - (u_char) get_rom_byte(smc,ibase+ROM_SIG_2) == 0xaa) { - /* - * get the pointer to the PCI data structure - */ - pci_base = ibase + get_rom_byte(smc,ibase+PCI_DATA_1) + - (get_rom_byte(smc,ibase+PCI_DATA_2) << 8) ; - - if (image == get_rom_byte(smc,pci_base+CODE_TYPE)) { - /* - * we have the right image, read the VPD data - */ - vpd = ibase + get_rom_byte(smc,pci_base+VPD_DATA_1) + - (get_rom_byte(smc,pci_base+VPD_DATA_2) << 8) ; - if (vpd == ibase) { - break ; /* no VPD data */ - } - for (len = 0; len < size; len++,buf++,vpd++) { - *buf = get_rom_byte(smc,vpd) ; - } - break ; - } - else { - /* - * try the next image - */ - if (get_rom_byte(smc,pci_base+INDICATOR) & 0x80) { - break ; /* this was the last image */ - } - ibase = ibase + get_rom_byte(smc,ibase+IMAGE_LEN_1) + - (get_rom_byte(smc,ibase+IMAGE_LEN_2) << 8) ; - } - } - - return(len) ; -} - -void mac_drv_pci_fix(struct s_smc *smc, u_long fix_value) -{ - smc->hw.pci_fix_value = fix_value ; -} void mac_do_pci_fix(struct s_smc *smc) { diff --git a/drivers/net/skfp/ess.c b/drivers/net/skfp/ess.c index fd39b4b..62b0132 100644 --- a/drivers/net/skfp/ess.c +++ b/drivers/net/skfp/ess.c @@ -102,7 +102,7 @@ void ess_timer_poll(struct s_smc *smc); void ess_para_change(struct s_smc *smc); int ess_raf_received_pack(struct s_smc *smc, SMbuf *mb, struct smt_header *sm, int fs); -int process_bw_alloc(struct s_smc *smc, long int payload, long int overhead); +static int process_bw_alloc(struct s_smc *smc, long int payload, long int overhead); /* @@ -375,7 +375,7 @@ int ess_raf_received_pack(struct s_smc *smc, SMbuf *mb, struct smt_header *sm, * determines the synchronous bandwidth, set the TSYNC register and the * mib variables SBAPayload, SBAOverhead and fddiMACT-NEG. */ -int process_bw_alloc(struct s_smc *smc, long int payload, long int overhead) +static int process_bw_alloc(struct s_smc *smc, long int payload, long int overhead) { /* * determine the synchronous bandwidth (sync_bw) in bytes per T-NEG, diff --git a/drivers/net/skfp/fplustm.c b/drivers/net/skfp/fplustm.c index 76e7844..a2ed47f 100644 --- a/drivers/net/skfp/fplustm.c +++ b/drivers/net/skfp/fplustm.c @@ -1117,30 +1117,6 @@ void mac_clear_multicast(struct s_smc *smc) /* BEGIN_MANUAL_ENTRY(if,func;others;2) - int mac_set_func_addr(smc,f_addr) - struct s_smc *smc ; - u_long f_addr ; - -Function DOWNCALL (SMT, fplustm.c) - Set a Token-Ring functional address, the address will - be activated after calling mac_update_multicast() - -Para f_addr functional bits in non-canonical format - -Returns 0: always success - - END_MANUAL_ENTRY() - */ -int mac_set_func_addr(struct s_smc *smc, u_long f_addr) -{ - smc->hw.fp.func_addr = f_addr ; - return(0) ; -} - - -/* - BEGIN_MANUAL_ENTRY(if,func;others;2) - int mac_add_multicast(smc,addr,can) struct s_smc *smc ; struct fddi_addr *addr ; @@ -1203,52 +1179,6 @@ int mac_add_multicast(struct s_smc *smc, struct fddi_addr *addr, int can) } /* - BEGIN_MANUAL_ENTRY(if,func;others;2) - - void mac_del_multicast(smc,addr,can) - struct s_smc *smc ; - struct fddi_addr *addr ; - int can ; - -Function DOWNCALL (SMT, fplustm.c) - Delete an entry from the multicast table - -Para addr pointer to a multicast address - can = 0: the multicast address has the physical format - = 1: the multicast address has the canonical format - | 0x80 permanent - - END_MANUAL_ENTRY() - */ -void mac_del_multicast(struct s_smc *smc, struct fddi_addr *addr, int can) -{ - SK_LOC_DECL(struct fddi_addr,own) ; - struct s_fpmc *tb ; - - if (!(tb = mac_get_mc_table(smc,addr,&own,1,can & ~0x80))) - return ; - /* - * permanent addresses must be deleted with perm bit - * and vice versa - */ - if (( tb->perm && (can & 0x80)) || - (!tb->perm && !(can & 0x80))) { - /* - * delete it - */ - if (tb->n) { - tb->n-- ; - if (tb->perm) { - smc->hw.fp.smt_slots_used-- ; - } - else { - smc->hw.fp.os_slots_used-- ; - } - } - } -} - -/* * mode */ diff --git a/drivers/net/skfp/h/cmtdef.h b/drivers/net/skfp/h/cmtdef.h index 603982d..f2f771d 100644 --- a/drivers/net/skfp/h/cmtdef.h +++ b/drivers/net/skfp/h/cmtdef.h @@ -507,7 +507,6 @@ void pcm_status_state(struct s_smc *smc, int np, int *type, int *state, int *remote, int *mac); void plc_config_mux(struct s_smc *smc, int mux); void sm_lem_evaluate(struct s_smc *smc); -void smt_clear_una_dna(struct s_smc *smc); void mac_update_counter(struct s_smc *smc); void sm_pm_ls_latch(struct s_smc *smc, int phy, int on_off); void sm_ma_control(struct s_smc *smc, int mode); @@ -541,11 +540,9 @@ void smt_timer_poll(struct s_smc *smc); u_long smt_get_time(void); u_long smt_get_tid(struct s_smc *smc); void smt_timer_done(struct s_smc *smc); -void smt_set_defaults(struct s_smc *smc); void smt_fixup_mib(struct s_smc *smc); void smt_reset_defaults(struct s_smc *smc, int level); void smt_agent_task(struct s_smc *smc); -void smt_please_reconnect(struct s_smc *smc, int reconn_time); int smt_check_para(struct s_smc *smc, struct smt_header *sm, const u_short list[]); void driver_get_bia(struct s_smc *smc, struct fddi_addr *bia_addr); @@ -568,7 +565,6 @@ int pcm_get_s_port(struct s_smc *smc); int pcm_rooted_station(struct s_smc *smc); int cfm_get_mac_input(struct s_smc *smc); int cfm_get_mac_output(struct s_smc *smc); -int port_to_mib(struct s_smc *smc, int p); int cem_build_path(struct s_smc *smc, char *to, int path_index); int sm_mac_get_tx_state(struct s_smc *smc); char *get_pcmstate(struct s_smc *smc, int np); @@ -580,8 +576,6 @@ void smt_send_frame(struct s_smc *smc, SMbuf *mb, int fc, int local); void smt_set_timestamp(struct s_smc *smc, u_char *p); void mac_set_rx_mode(struct s_smc *smc, int mode); int mac_add_multicast(struct s_smc *smc, struct fddi_addr *addr, int can); -int mac_set_func_addr(struct s_smc *smc, u_long f_addr); -void mac_del_multicast(struct s_smc *smc, struct fddi_addr *addr, int can); void mac_update_multicast(struct s_smc *smc); void mac_clear_multicast(struct s_smc *smc); void set_formac_tsync(struct s_smc *smc, long sync_bw); @@ -599,7 +593,6 @@ void plc_irq(struct s_smc *smc, int np, unsigned int cmd); int smt_set_mac_opvalues(struct s_smc *smc); #ifdef TAG_MODE -void mac_drv_pci_fix(struct s_smc *smc, u_long fix_value); void mac_do_pci_fix(struct s_smc *smc); void mac_drv_clear_tx_queue(struct s_smc *smc); void mac_drv_repair_descr(struct s_smc *smc); diff --git a/drivers/net/skfp/h/hwmtm.h b/drivers/net/skfp/h/hwmtm.h index 4e360af..1a606d4 100644 --- a/drivers/net/skfp/h/hwmtm.h +++ b/drivers/net/skfp/h/hwmtm.h @@ -262,31 +262,6 @@ struct os_debug { (smc)->hw.fp.tx_q[queue].tx_curr_put /* - * BEGIN_MANUAL_ENTRY(HWM_TX_CHECK) - * void HWM_TX_CHECK(smc,frame_status,low_water) - * - * function MACRO (hardware module, hwmtm.h) - * This macro is invoked by the OS-specific before it left it's - * driver_send function. This macro calls mac_drv_clear_txd - * if the free TxDs of the current transmit queue is equal or - * lower than the given low water mark. - * - * para frame_status status of the frame, see design description - * low_water low water mark of free TxD's - * - * END_MANUAL_ENTRY - */ -#ifndef HWM_NO_FLOW_CTL -#define HWM_TX_CHECK(smc,frame_status,low_water) {\ - if ((low_water)>=(smc)->hw.fp.tx_q[(frame_status)&QUEUE_A0].tx_free) {\ - mac_drv_clear_txd(smc) ;\ - }\ -} -#else -#define HWM_TX_CHECK(smc,frame_status,low_water) mac_drv_clear_txd(smc) -#endif - -/* * BEGIN_MANUAL_ENTRY(HWM_GET_RX_FRAG_LEN) * int HWM_GET_RX_FRAG_LEN(rxd) * diff --git a/drivers/net/skfp/hwmtm.c b/drivers/net/skfp/hwmtm.c index 18d4290..438f424 100644 --- a/drivers/net/skfp/hwmtm.c +++ b/drivers/net/skfp/hwmtm.c @@ -86,6 +86,7 @@ static u_long repair_txd_ring(struct s_smc *smc, struct s_smt_tx_queue *queue); static u_long repair_rxd_ring(struct s_smc *smc, struct s_smt_rx_queue *queue); static SMbuf* get_llc_rx(struct s_smc *smc); static SMbuf* get_txd_mb(struct s_smc *smc); +static void mac_drv_clear_txd(struct s_smc *smc); /* ------------------------------------------------------------- @@ -146,7 +147,6 @@ extern int mac_drv_rx_init(struct s_smc *smc, int len, int fc, char *look_ahead, */ void process_receive(struct s_smc *smc); void fddi_isr(struct s_smc *smc); -void mac_drv_clear_txd(struct s_smc *smc); void smt_free_mbuf(struct s_smc *smc, SMbuf *mb); void init_driver_fplus(struct s_smc *smc); void mac_drv_rx_mode(struct s_smc *smc, int mode); @@ -158,7 +158,6 @@ void hwm_tx_frag(struct s_smc *smc, char far *virt, u_long phys, int len, void hwm_rx_frag(struct s_smc *smc, char far *virt, u_long phys, int len, int frame_status); -int mac_drv_rx_frag(struct s_smc *smc, void far *virt, int len); int mac_drv_init(struct s_smc *smc); int hwm_tx_init(struct s_smc *smc, u_char fc, int frag_count, int frame_len, int frame_status); @@ -1448,35 +1447,6 @@ void hwm_rx_frag(struct s_smc *smc, char far *virt, u_long phys, int len, NDD_TRACE("RHfE",r,AIX_REVERSE(r->rxd_rbadr),0) ; } -#ifndef NDIS_OS2 -/* - * BEGIN_MANUAL_ENTRY(mac_drv_rx_frag) - * int mac_drv_rx_frag(smc,virt,len) - * - * function DOWNCALL (hwmtm.c) - * mac_drv_rx_frag fills the fragment with a part of the frame. - * - * para virt the virtual address of the fragment - * len the length in bytes of the fragment - * - * return 0: success code, no errors possible - * - * END_MANUAL_ENTRY - */ -int mac_drv_rx_frag(struct s_smc *smc, void far *virt, int len) -{ - NDD_TRACE("RHSB",virt,len,smc->os.hwm.r.mb_pos) ; - - DB_RX("receive from queue: len/virt: = %d/%x",len,virt,4) ; - memcpy((char far *)virt,smc->os.hwm.r.mb_pos,len) ; - smc->os.hwm.r.mb_pos += len ; - - NDD_TRACE("RHSE",smc->os.hwm.r.mb_pos,0,0) ; - return(0) ; -} -#endif - - /* * BEGINN_MANUAL_ENTRY(mac_drv_clear_rx_queue) * @@ -1978,7 +1948,7 @@ void smt_send_mbuf(struct s_smc *smc, SMbuf *mb, int fc) * * END_MANUAL_ENTRY */ -void mac_drv_clear_txd(struct s_smc *smc) +static void mac_drv_clear_txd(struct s_smc *smc) { struct s_smt_tx_queue *queue ; struct s_smt_fp_txd volatile *t1 ; diff --git a/drivers/net/skfp/pcmplc.c b/drivers/net/skfp/pcmplc.c index 571f055..cd0aa4c 100644 --- a/drivers/net/skfp/pcmplc.c +++ b/drivers/net/skfp/pcmplc.c @@ -1861,13 +1861,6 @@ void plc_irq(struct s_smc *smc, int np, unsigned int cmd) #endif } -void pcm_set_lct_short(struct s_smc *smc, int n) -{ - if (n <= 0 || n > 1000) - return ; - smc->s.lct_short = n ; -} - #ifdef DEBUG /* * fill state struct diff --git a/drivers/net/skfp/pmf.c b/drivers/net/skfp/pmf.c index f2b446d..efc639c 100644 --- a/drivers/net/skfp/pmf.c +++ b/drivers/net/skfp/pmf.c @@ -36,12 +36,13 @@ static int smt_authorize(struct s_smc *smc, struct smt_header *sm); static int smt_check_set_count(struct s_smc *smc, struct smt_header *sm); static const struct s_p_tab* smt_get_ptab(u_short para); static int smt_mib_phys(struct s_smc *smc); -int smt_set_para(struct s_smc *smc, struct smt_para *pa, int index, int local, - int set); +static int smt_set_para(struct s_smc *smc, struct smt_para *pa, int index, + int local, int set); void smt_add_para(struct s_smc *smc, struct s_pcon *pcon, u_short para, int index, int local); static SMbuf *smt_build_pmf_response(struct s_smc *smc, struct smt_header *req, int set, int local); +static int port_to_mib(struct s_smc *smc, int p); #define MOFFSS(e) ((int)&(((struct fddi_mib *)0)->e)) #define MOFFSA(e) ((int) (((struct fddi_mib *)0)->e)) @@ -1078,8 +1079,8 @@ wrong_error: /* * set parameter */ -int smt_set_para(struct s_smc *smc, struct smt_para *pa, int index, int local, - int set) +static int smt_set_para(struct s_smc *smc, struct smt_para *pa, int index, + int local, int set) { #define IFSET(x) if (set) (x) @@ -1549,7 +1550,7 @@ static int smt_mib_phys(struct s_smc *smc) #endif } -int port_to_mib(struct s_smc *smc, int p) +static int port_to_mib(struct s_smc *smc, int p) { #ifdef CONCENTRATOR SK_UNUSED(smc) ; diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c index c88aad6..4b5ed2c 100644 --- a/drivers/net/skfp/skfddi.c +++ b/drivers/net/skfp/skfddi.c @@ -149,7 +149,6 @@ extern void hwm_rx_frag(struct s_smc *smc, char far * virt, u_long phys, extern void mac_drv_rx_mode(struct s_smc *smc, int mode); extern void mac_drv_clear_rx_queue(struct s_smc *smc); extern void enable_tx_irq(struct s_smc *smc, u_short queue); -extern void mac_drv_clear_txd(struct s_smc *smc); static struct pci_device_id skfddi_pci_tbl[] = { { PCI_VENDOR_ID_SK, PCI_DEVICE_ID_SK_FP, PCI_ANY_ID, PCI_ANY_ID, }, diff --git a/drivers/net/skfp/smt.c b/drivers/net/skfp/smt.c index c3a0d2f..f17c05c 100644 --- a/drivers/net/skfp/smt.c +++ b/drivers/net/skfp/smt.c @@ -110,7 +110,7 @@ static void smt_fill_setcount(struct s_smc *smc, struct smt_p_setcount *setcount static void smt_fill_echo(struct s_smc *smc, struct smt_p_echo *echo, u_long seed, int len); -void smt_clear_una_dna(struct s_smc *smc); +static void smt_clear_una_dna(struct s_smc *smc); static void smt_clear_old_una_dna(struct s_smc *smc); #ifdef CONCENTRATOR static int entity_to_index(void); @@ -118,7 +118,7 @@ static int entity_to_index(void); static void update_dac(struct s_smc *smc, int report); static int div_ratio(u_long upper, u_long lower); #ifdef USE_CAN_ADDR -void hwm_conv_can(struct s_smc *smc, char *data, int len); +static void hwm_conv_can(struct s_smc *smc, char *data, int len); #else #define hwm_conv_can(smc,data,len) #endif @@ -216,24 +216,6 @@ void smt_agent_task(struct s_smc *smc) DB_SMT("SMT agent task\n",0,0) ; } -void smt_please_reconnect(struct s_smc *smc, int reconn_time) -/* struct s_smc *smc; Pointer to SMT context */ -/* int reconn_time; Wait for reconnect time in seconds */ -{ - /* - * The please reconnect variable is used as a timer. - * It is decremented each time smt_event is called. - * This happens every second or when smt_force_irq is called. - * Note: smt_force_irq () is called on some packet receives and - * when a multicast address is changed. Since nothing - * is received during the disconnect and the multicast - * address changes can be viewed as not very often and - * the timer runs out close to its given value - * (reconn_time). - */ - smc->sm.please_reconnect = reconn_time ; -} - #ifndef SMT_REAL_TOKEN_CT void smt_emulate_token_ct(struct s_smc *smc, int mac_index) { @@ -1574,7 +1556,7 @@ static void smt_fill_echo(struct s_smc *smc, struct smt_p_echo *echo, u_long see * clear DNA and UNA * called from CFM if configuration changes */ -void smt_clear_una_dna(struct s_smc *smc) +static void smt_clear_una_dna(struct s_smc *smc) { smc->mib.m[MAC0].fddiMACUpstreamNbr = SMT_Unknown ; smc->mib.m[MAC0].fddiMACDownstreamNbr = SMT_Unknown ; @@ -2058,30 +2040,10 @@ int smt_action(struct s_smc *smc, int class, int code, int index) } /* - * change tneg - * set T_Req in MIB (Path Attribute) - * calculate new values for MAC - * if change required - * disconnect - * set reconnect - * end - */ -void smt_change_t_neg(struct s_smc *smc, u_long tneg) -{ - smc->mib.a[PATH0].fddiPATHMaxT_Req = tneg ; - - if (smt_set_mac_opvalues(smc)) { - RS_SET(smc,RS_EVENT) ; - smc->sm.please_reconnect = 1 ; - queue_event(smc,EVENT_ECM,EC_DISCONNECT) ; - } -} - -/* * canonical conversion of <len> bytes beginning form *data */ #ifdef USE_CAN_ADDR -void hwm_conv_can(struct s_smc *smc, char *data, int len) +static void hwm_conv_can(struct s_smc *smc, char *data, int len) { int i ; diff --git a/drivers/net/skfp/smtdef.c b/drivers/net/skfp/smtdef.c index 5a0c8db..4e07ff7 100644 --- a/drivers/net/skfp/smtdef.c +++ b/drivers/net/skfp/smtdef.c @@ -76,11 +76,6 @@ void smt_reset_defaults(struct s_smc *smc, int level); static void smt_init_mib(struct s_smc *smc, int level); static int set_min_max(int maxflag, u_long mib, u_long limit, u_long *oper); -void smt_set_defaults(struct s_smc *smc) -{ - smt_reset_defaults(smc,0) ; -} - #define MS2BCLK(x) ((x)*12500L) #define US2BCLK(x) ((x)*1250L) diff --git a/drivers/net/skfp/smtparse.c b/drivers/net/skfp/smtparse.c deleted file mode 100644 index d5779e4..0000000 --- a/drivers/net/skfp/smtparse.c +++ /dev/null @@ -1,467 +0,0 @@ -/****************************************************************************** - * - * (C)Copyright 1998,1999 SysKonnect, - * a business unit of Schneider & Koch & Co. Datensysteme GmbH. - * - * See the file "skfddi.c" for further information. - * - * 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. - * - * The information in this file is provided "AS IS" without warranty. - * - ******************************************************************************/ - - -/* - parser for SMT parameters -*/ - -#include "h/types.h" -#include "h/fddi.h" -#include "h/smc.h" -#include "h/smt_p.h" - -#define KERNEL -#include "h/smtstate.h" - -#ifndef lint -static const char ID_sccs[] = "@(#)smtparse.c 1.12 98/10/06 (C) SK " ; -#endif - -#ifdef sun -#define _far -#endif - -/* - * convert to BCLK units - */ -#define MS2BCLK(x) ((x)*12500L) -#define US2BCLK(x) ((x/10)*125L) - -/* - * parameter table - */ -static struct s_ptab { - char *pt_name ; - u_short pt_num ; - u_short pt_type ; - u_long pt_min ; - u_long pt_max ; -} ptab[] = { - { "PMFPASSWD",0, 0 } , - { "USERDATA",1, 0 } , - { "LERCUTOFFA",2, 1, 4, 15 } , - { "LERCUTOFFB",3, 1, 4, 15 } , - { "LERALARMA",4, 1, 4, 15 } , - { "LERALARMB",5, 1, 4, 15 } , - { "TMAX",6, 1, 5, 165 } , - { "TMIN",7, 1, 5, 165 } , - { "TREQ",8, 1, 5, 165 } , - { "TVX",9, 1, 2500, 10000 } , -#ifdef ESS - { "SBAPAYLOAD",10, 1, 0, 1562 } , - { "SBAOVERHEAD",11, 1, 50, 5000 } , - { "MAXTNEG",12, 1, 5, 165 } , - { "MINSEGMENTSIZE",13, 1, 0, 4478 } , - { "SBACATEGORY",14, 1, 0, 0xffff } , - { "SYNCHTXMODE",15, 0 } , -#endif -#ifdef SBA - { "SBACOMMAND",16, 0 } , - { "SBAAVAILABLE",17, 1, 0, 100 } , -#endif - { NULL } -} ; - -/* Define maximum string size for values and keybuffer */ -#define MAX_VAL 40 - -/* - * local function declarations - */ -static u_long parse_num(int type, char _far *value, char *v, u_long mn, - u_long mx, int scale); -static int parse_word(char *buf, char _far *text); - -#ifdef SIM -#define DB_MAIN(a,b,c) printf(a,b,c) -#else -#define DB_MAIN(a,b,c) -#endif - -/* - * BEGIN_MANUAL_ENTRY() - * - * int smt_parse_arg(struct s_smc *,char _far *keyword,int type, - char _far *value) - * - * parse SMT parameter - * *keyword - * pointer to keyword, must be \0, \n or \r terminated - * *value pointer to value, either char * or u_long * - * if char * - * pointer to value, must be \0, \n or \r terminated - * if u_long * - * contains binary value - * - * type 0: integer - * 1: string - * return - * 0 parameter parsed ok - * != 0 error - * NOTE: - * function can be called with DS != SS - * - * - * END_MANUAL_ENTRY() - */ -int smt_parse_arg(struct s_smc *smc, char _far *keyword, int type, - char _far *value) -{ - char keybuf[MAX_VAL+1]; - char valbuf[MAX_VAL+1]; - char c ; - char *p ; - char *v ; - char *d ; - u_long val = 0 ; - struct s_ptab *pt ; - int st ; - int i ; - - /* - * parse keyword - */ - if ((st = parse_word(keybuf,keyword))) - return(st) ; - /* - * parse value if given as string - */ - if (type == 1) { - if ((st = parse_word(valbuf,value))) - return(st) ; - } - /* - * search in table - */ - st = 0 ; - for (pt = ptab ; (v = pt->pt_name) ; pt++) { - for (p = keybuf ; (c = *p) ; p++,v++) { - if (c != *v) - break ; - } - if (!c && !*v) - break ; - } - if (!v) - return(-1) ; -#if 0 - printf("=>%s<==>%s<=\n",pt->pt_name,valbuf) ; -#endif - /* - * set value in MIB - */ - if (pt->pt_type) - val = parse_num(type,value,valbuf,pt->pt_min,pt->pt_max,1) ; - switch (pt->pt_num) { - case 0 : - v = valbuf ; - d = (char *) smc->mib.fddiPRPMFPasswd ; - for (i = 0 ; i < (signed)sizeof(smc->mib.fddiPRPMFPasswd) ; i++) - *d++ = *v++ ; - DB_MAIN("SET %s = %s\n",pt->pt_name,smc->mib.fddiPRPMFPasswd) ; - break ; - case 1 : - v = valbuf ; - d = (char *) smc->mib.fddiSMTUserData ; - for (i = 0 ; i < (signed)sizeof(smc->mib.fddiSMTUserData) ; i++) - *d++ = *v++ ; - DB_MAIN("SET %s = %s\n",pt->pt_name,smc->mib.fddiSMTUserData) ; - break ; - case 2 : - smc->mib.p[PA].fddiPORTLer_Cutoff = (u_char) val ; - DB_MAIN("SET %s = %d\n", - pt->pt_name,smc->mib.p[PA].fddiPORTLer_Cutoff) ; - break ; - case 3 : - smc->mib.p[PB].fddiPORTLer_Cutoff = (u_char) val ; - DB_MAIN("SET %s = %d\n", - pt->pt_name,smc->mib.p[PB].fddiPORTLer_Cutoff) ; - break ; - case 4 : - smc->mib.p[PA].fddiPORTLer_Alarm = (u_char) val ; - DB_MAIN("SET %s = %d\n", - pt->pt_name,smc->mib.p[PA].fddiPORTLer_Alarm) ; - break ; - case 5 : - smc->mib.p[PB].fddiPORTLer_Alarm = (u_char) val ; - DB_MAIN("SET %s = %d\n", - pt->pt_name,smc->mib.p[PB].fddiPORTLer_Alarm) ; - break ; - case 6 : /* TMAX */ - DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; - smc->mib.a[PATH0].fddiPATHT_MaxLowerBound = - (u_long) -MS2BCLK((long)val) ; - break ; - case 7 : /* TMIN */ - DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; - smc->mib.m[MAC0].fddiMACT_Min = - (u_long) -MS2BCLK((long)val) ; - break ; - case 8 : /* TREQ */ - DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; - smc->mib.a[PATH0].fddiPATHMaxT_Req = - (u_long) -MS2BCLK((long)val) ; - break ; - case 9 : /* TVX */ - DB_MAIN("SET %s = %d \n",pt->pt_name,val) ; - smc->mib.a[PATH0].fddiPATHTVXLowerBound = - (u_long) -US2BCLK((long)val) ; - break ; -#ifdef ESS - case 10 : /* SBAPAYLOAD */ - DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; - if (smc->mib.fddiESSPayload != val) { - smc->ess.raf_act_timer_poll = TRUE ; - smc->mib.fddiESSPayload = val ; - } - break ; - case 11 : /* SBAOVERHEAD */ - DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; - smc->mib.fddiESSOverhead = val ; - break ; - case 12 : /* MAXTNEG */ - DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; - smc->mib.fddiESSMaxTNeg = (u_long) -MS2BCLK((long)val) ; - break ; - case 13 : /* MINSEGMENTSIZE */ - DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; - smc->mib.fddiESSMinSegmentSize = val ; - break ; - case 14 : /* SBACATEGORY */ - DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; - smc->mib.fddiESSCategory = - (smc->mib.fddiESSCategory & 0xffff) | - ((u_long)(val << 16)) ; - break ; - case 15 : /* SYNCHTXMODE */ - /* do not use memcmp(valbuf,"ALL",3) because DS != SS */ - if (valbuf[0] == 'A' && valbuf[1] == 'L' && valbuf[2] == 'L') { - smc->mib.fddiESSSynchTxMode = TRUE ; - DB_MAIN("SET %s = %s\n",pt->pt_name,valbuf) ; - } - /* if (!memcmp(valbuf,"SPLIT",5)) { */ - if (valbuf[0] == 'S' && valbuf[1] == 'P' && valbuf[2] == 'L' && - valbuf[3] == 'I' && valbuf[4] == 'T') { - DB_MAIN("SET %s = %s\n",pt->pt_name,valbuf) ; - smc->mib.fddiESSSynchTxMode = FALSE ; - } - break ; -#endif -#ifdef SBA - case 16 : /* SBACOMMAND */ - /* if (!memcmp(valbuf,"START",5)) { */ - if (valbuf[0] == 'S' && valbuf[1] == 'T' && valbuf[2] == 'A' && - valbuf[3] == 'R' && valbuf[4] == 'T') { - DB_MAIN("SET %s = %s\n",pt->pt_name,valbuf) ; - smc->mib.fddiSBACommand = SB_START ; - } - /* if (!memcmp(valbuf,"STOP",4)) { */ - if (valbuf[0] == 'S' && valbuf[1] == 'T' && valbuf[2] == 'O' && - valbuf[3] == 'P') { - DB_MAIN("SET %s = %s\n",pt->pt_name,valbuf) ; - smc->mib.fddiSBACommand = SB_STOP ; - } - break ; - case 17 : /* SBAAVAILABLE */ - DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; - smc->mib.fddiSBAAvailable = (u_char) val ; - break ; -#endif - } - return(0) ; -} - -static int parse_word(char *buf, char _far *text) -{ - char c ; - char *p ; - int p_len ; - int quote ; - int i ; - int ok ; - - /* - * skip leading white space - */ - p = buf ; - for (i = 0 ; i < MAX_VAL ; i++) - *p++ = 0 ; - p = buf ; - p_len = 0 ; - ok = 0 ; - while ( (c = *text++) && (c != '\n') && (c != '\r')) { - if ((c != ' ') && (c != '\t')) { - ok = 1 ; - break ; - } - } - if (!ok) - return(-1) ; - if (c == '"') { - quote = 1 ; - } - else { - quote = 0 ; - text-- ; - } - /* - * parse valbuf - */ - ok = 0 ; - while (!ok && p_len < MAX_VAL-1 && (c = *text++) && (c != '\n') - && (c != '\r')) { - switch (quote) { - case 0 : - if ((c == ' ') || (c == '\t') || (c == '=')) { - ok = 1 ; - break ; - } - *p++ = c ; - p_len++ ; - break ; - case 2 : - *p++ = c ; - p_len++ ; - quote = 1 ; - break ; - case 1 : - switch (c) { - case '"' : - ok = 1 ; - break ; - case '\\' : - quote = 2 ; - break ; - default : - *p++ = c ; - p_len++ ; - } - } - } - *p++ = 0 ; - for (p = buf ; (c = *p) ; p++) { - if (c >= 'a' && c <= 'z') - *p = c + 'A' - 'a' ; - } - return(0) ; -} - -static u_long parse_num(int type, char _far *value, char *v, u_long mn, - u_long mx, int scale) -{ - u_long x = 0 ; - char c ; - - if (type == 0) { /* integer */ - u_long _far *l ; - u_long u1 ; - - l = (u_long _far *) value ; - u1 = *l ; - /* - * if the value is negative take the lower limit - */ - if ((long)u1 < 0) { - if (- ((long)u1) > (long) mx) { - u1 = 0 ; - } - else { - u1 = (u_long) - ((long)u1) ; - } - } - x = u1 ; - } - else { /* string */ - int sign = 0 ; - - if (*v == '-') { - sign = 1 ; - } - while ((c = *v++) && (c >= '0') && (c <= '9')) { - x = x * 10 + c - '0' ; - } - if (scale == 10) { - x *= 10 ; - if (c == '.') { - if ((c = *v++) && (c >= '0') && (c <= '9')) { - x += c - '0' ; - } - } - } - if (sign) - x = (u_long) - ((long)x) ; - } - /* - * if the value is negative - * and the absolute value is outside the limits - * take the lower limit - * else - * take the absoute value - */ - if ((long)x < 0) { - if (- ((long)x) > (long) mx) { - x = 0 ; - } - else { - x = (u_long) - ((long)x) ; - } - } - if (x < mn) - return(mn) ; - else if (x > mx) - return(mx) ; - return(x) ; -} - -#if 0 -struct s_smc SMC ; -main() -{ - char *p ; - char *v ; - char buf[100] ; - int toggle = 0 ; - - while (gets(buf)) { - p = buf ; - while (*p && ((*p == ' ') || (*p == '\t'))) - p++ ; - - while (*p && ((*p != ' ') && (*p != '\t'))) - p++ ; - - v = p ; - while (*v && ((*v == ' ') || (*v == '\t'))) - v++ ; - if ((*v >= '0') && (*v <= '9')) { - toggle = !toggle ; - if (toggle) { - u_long l ; - l = atol(v) ; - smt_parse_arg(&SMC,buf,0,(char _far *)&l) ; - } - else - smt_parse_arg(&SMC,buf,1,(char _far *)p) ; - } - else { - smt_parse_arg(&SMC,buf,1,(char _far *)p) ; - } - } - exit(0) ; -} -#endif - diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index fd80048..cfb9d3c 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -315,15 +315,25 @@ static void smc_reset(struct net_device *dev) struct smc_local *lp = netdev_priv(dev); void __iomem *ioaddr = lp->base; unsigned int ctl, cfg; + struct sk_buff *pending_skb; DBG(2, "%s: %s\n", dev->name, __FUNCTION__); - /* Disable all interrupts */ + /* Disable all interrupts, block TX tasklet */ spin_lock(&lp->lock); SMC_SELECT_BANK(2); SMC_SET_INT_MASK(0); + pending_skb = lp->pending_tx_skb; + lp->pending_tx_skb = NULL; spin_unlock(&lp->lock); + /* free any pending tx skb */ + if (pending_skb) { + dev_kfree_skb(pending_skb); + lp->stats.tx_errors++; + lp->stats.tx_aborted_errors++; + } + /* * This resets the registers mostly to defaults, but doesn't * affect EEPROM. That seems unnecessary @@ -389,14 +399,6 @@ static void smc_reset(struct net_device *dev) SMC_SELECT_BANK(2); SMC_SET_MMU_CMD(MC_RESET); SMC_WAIT_MMU_BUSY(); - - /* clear anything saved */ - if (lp->pending_tx_skb != NULL) { - dev_kfree_skb (lp->pending_tx_skb); - lp->pending_tx_skb = NULL; - lp->stats.tx_errors++; - lp->stats.tx_aborted_errors++; - } } /* @@ -440,6 +442,7 @@ static void smc_shutdown(struct net_device *dev) { struct smc_local *lp = netdev_priv(dev); void __iomem *ioaddr = lp->base; + struct sk_buff *pending_skb; DBG(2, "%s: %s\n", CARDNAME, __FUNCTION__); @@ -447,7 +450,11 @@ static void smc_shutdown(struct net_device *dev) spin_lock(&lp->lock); SMC_SELECT_BANK(2); SMC_SET_INT_MASK(0); + pending_skb = lp->pending_tx_skb; + lp->pending_tx_skb = NULL; spin_unlock(&lp->lock); + if (pending_skb) + dev_kfree_skb(pending_skb); /* and tell the card to stay away from that nasty outside world */ SMC_SELECT_BANK(0); @@ -627,7 +634,12 @@ static void smc_hardware_send_pkt(unsigned long data) } skb = lp->pending_tx_skb; + if (unlikely(!skb)) { + smc_special_unlock(&lp->lock); + return; + } lp->pending_tx_skb = NULL; + packet_no = SMC_GET_AR(); if (unlikely(packet_no & AR_FAILED)) { printk("%s: Memory allocation failed.\n", dev->name); @@ -702,7 +714,6 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) DBG(3, "%s: %s\n", dev->name, __FUNCTION__); BUG_ON(lp->pending_tx_skb != NULL); - lp->pending_tx_skb = skb; /* * The MMU wants the number of pages to be the number of 256 bytes @@ -718,7 +729,6 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) numPages = ((skb->len & ~1) + (6 - 1)) >> 8; if (unlikely(numPages > 7)) { printk("%s: Far too big packet error.\n", dev->name); - lp->pending_tx_skb = NULL; lp->stats.tx_errors++; lp->stats.tx_dropped++; dev_kfree_skb(skb); @@ -745,6 +755,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) smc_special_unlock(&lp->lock); + lp->pending_tx_skb = skb; if (!poll_count) { /* oh well, wait until the chip finds memory later */ netif_stop_queue(dev); @@ -1062,7 +1073,7 @@ static void smc_phy_powerdown(struct net_device *dev) above). linkwatch_event() also wants the netlink semaphore. */ while(lp->work_pending) - schedule(); + yield(); bmcr = smc_phy_read(dev, phy, MII_BMCR); smc_phy_write(dev, phy, MII_BMCR, bmcr | BMCR_PDOWN); @@ -1606,14 +1617,8 @@ static int smc_close(struct net_device *dev) /* clear everything */ smc_shutdown(dev); - + tasklet_kill(&lp->tx_task); smc_phy_powerdown(dev); - - if (lp->pending_tx_skb) { - dev_kfree_skb(lp->pending_tx_skb); - lp->pending_tx_skb = NULL; - } - return 0; } diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c index 6e5ade9..97712c3 100644 --- a/drivers/net/tokenring/lanstreamer.c +++ b/drivers/net/tokenring/lanstreamer.c @@ -455,8 +455,7 @@ static int streamer_reset(struct net_device *dev) writew(readw(streamer_mmio + BCTL) | BCTL_SOFTRESET, streamer_mmio + BCTL); t = jiffies; /* Hold soft reset bit for a while */ - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(HZ); + ssleep(1); writew(readw(streamer_mmio + BCTL) & ~BCTL_SOFTRESET, streamer_mmio + BCTL); @@ -512,8 +511,7 @@ static int streamer_reset(struct net_device *dev) writew(SISR_MI, streamer_mmio + SISR_MASK_SUM); while (!((readw(streamer_mmio + SISR)) & SISR_SRB_REPLY)) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ/10); + msleep_interruptible(100); if (jiffies - t > 40 * HZ) { printk(KERN_ERR "IBM PCI tokenring card not responding\n"); diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index cfc346e..08e0f80 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -242,6 +242,7 @@ static struct pci_device_id tulip_pci_tbl[] = { { 0x10b9, 0x5261, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ULI526X }, /* ALi 1563 integrated ethernet */ { 0x10b9, 0x5263, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ULI526X }, /* ALi 1563 integrated ethernet */ { 0x10b7, 0x9300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* 3Com 3CSOHO100B-TX */ + { 0x14ea, 0xab08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* Planex FNW-3602-TX */ { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, tulip_pci_tbl); @@ -1756,11 +1757,19 @@ static int tulip_suspend (struct pci_dev *pdev, pm_message_t state) { struct net_device *dev = pci_get_drvdata(pdev); - if (dev && netif_running (dev) && netif_device_present (dev)) { - netif_device_detach (dev); - tulip_down (dev); - /* pci_power_off(pdev, -1); */ - } + if (!dev) + return -EINVAL; + + if (netif_running(dev)) + tulip_down(dev); + + netif_device_detach(dev); + free_irq(dev->irq, dev); + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; } @@ -1768,15 +1777,26 @@ static int tulip_suspend (struct pci_dev *pdev, pm_message_t state) static int tulip_resume(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); + int retval; - if (dev && netif_running (dev) && !netif_device_present (dev)) { -#if 1 - pci_enable_device (pdev); -#endif - /* pci_power_on(pdev); */ - tulip_up (dev); - netif_device_attach (dev); + if (!dev) + return -EINVAL; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + pci_enable_device(pdev); + + if ((retval = request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev))) { + printk (KERN_ERR "tulip: request_irq failed in resume\n"); + return retval; } + + netif_device_attach(dev); + + if (netif_running(dev)) + tulip_up(dev); + return 0; } diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index 6200cfc..be1c104 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -1398,7 +1398,7 @@ static void rhine_tx(struct net_device *dev) while (rp->dirty_tx != rp->cur_tx) { txstatus = le32_to_cpu(rp->tx_ring[entry].tx_status); if (debug > 6) - printk(KERN_DEBUG " Tx scavenge %d status %8.8x.\n", + printk(KERN_DEBUG "Tx scavenge %d status %8.8x.\n", entry, txstatus); if (txstatus & DescOwn) break; @@ -1469,7 +1469,7 @@ static void rhine_rx(struct net_device *dev) int data_size = desc_status >> 16; if (debug > 4) - printk(KERN_DEBUG " rhine_rx() status is %8.8x.\n", + printk(KERN_DEBUG "rhine_rx() status is %8.8x.\n", desc_status); if (--boguscnt < 0) break; @@ -1487,7 +1487,7 @@ static void rhine_rx(struct net_device *dev) } else if (desc_status & RxErr) { /* There was a error. */ if (debug > 2) - printk(KERN_DEBUG " rhine_rx() Rx " + printk(KERN_DEBUG "rhine_rx() Rx " "error was %8.8x.\n", desc_status); rp->stats.rx_errors++; diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index 7575b79..7217d44 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -981,6 +981,7 @@ fst_issue_cmd(struct fst_port_info *port, unsigned short cmd) /* Wait for any previous command to complete */ while (mbval > NAK) { spin_unlock_irqrestore(&card->card_lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); spin_lock_irqsave(&card->card_lock, flags); diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 1809688..c12648d 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -900,7 +900,7 @@ typedef struct aironet_ioctl { unsigned char __user *data; // d-data } aironet_ioctl; -static char *swversion = "2.1"; +static char swversion[] = "2.1"; #endif /* CISCO_EXT */ #define NUM_MODULES 2 diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index b1078baa..aabcdc2 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -46,382 +46,9 @@ * under either the MPL or the GPL. */ /* - * v0.01 -> v0.02 - 21/3/2001 - Jean II - * o Allow to use regular ethX device name instead of dldwdX - * o Warning on IBSS with ESSID=any for firmware 6.06 - * o Put proper range.throughput values (optimistic) - * o IWSPY support (IOCTL and stat gather in Rx path) - * o Allow setting frequency in Ad-Hoc mode - * o Disable WEP setting if !has_wep to work on old firmware - * o Fix txpower range - * o Start adding support for Samsung/Compaq firmware - * - * v0.02 -> v0.03 - 23/3/2001 - Jean II - * o Start adding Symbol support - need to check all that - * o Fix Prism2/Symbol WEP to accept 128 bits keys - * o Add Symbol WEP (add authentication type) - * o Add Prism2/Symbol rate - * o Add PM timeout (holdover duration) - * o Enable "iwconfig eth0 key off" and friends (toggle flags) - * o Enable "iwconfig eth0 power unicast/all" (toggle flags) - * o Try with an Intel card. It report firmware 1.01, behave like - * an antiquated firmware, however on windows it says 2.00. Yuck ! - * o Workaround firmware bug in allocate buffer (Intel 1.01) - * o Finish external renaming to orinoco... - * o Testing with various Wavelan firmwares - * - * v0.03 -> v0.04 - 30/3/2001 - Jean II - * o Update to Wireless 11 -> add retry limit/lifetime support - * o Tested with a D-Link DWL 650 card, fill in firmware support - * o Warning on Vcc mismatch (D-Link 3.3v card in Lucent 5v only slot) - * o Fixed the Prism2 WEP bugs that I introduced in v0.03 :-( - * It works on D-Link *only* after a tcpdump. Weird... - * And still doesn't work on Intel card. Grrrr... - * o Update the mode after a setport3 - * o Add preamble setting for Symbol cards (not yet enabled) - * o Don't complain as much about Symbol cards... - * - * v0.04 -> v0.04b - 22/4/2001 - David Gibson - * o Removed the 'eth' parameter - always use ethXX as the - * interface name instead of dldwdXX. The other was racy - * anyway. - * o Clean up RID definitions in hermes.h, other cleanups - * - * v0.04b -> v0.04c - 24/4/2001 - Jean II - * o Tim Hurley <timster AT seiki.bliztech.com> reported a D-Link card - * with vendor 02 and firmware 0.08. Added in the capabilities... - * o Tested Lucent firmware 7.28, everything works... - * - * v0.04c -> v0.05 - 3/5/2001 - Benjamin Herrenschmidt - * o Spin-off Pcmcia code. This file is renamed orinoco.c, - * and orinoco_cs.c now contains only the Pcmcia specific stuff - * o Add Airport driver support on top of orinoco.c (see airport.c) - * - * v0.05 -> v0.05a - 4/5/2001 - Jean II - * o Revert to old Pcmcia code to fix breakage of Ben's changes... - * - * v0.05a -> v0.05b - 4/5/2001 - Jean II - * o add module parameter 'ignore_cis_vcc' for D-Link @ 5V - * o D-Link firmware doesn't support multicast. We just print a few - * error messages, but otherwise everything works... - * o For David : set/getport3 works fine, just upgrade iwpriv... - * - * v0.05b -> v0.05c - 5/5/2001 - Benjamin Herrenschmidt - * o Adapt airport.c to latest changes in orinoco.c - * o Remove deferred power enabling code - * - * v0.05c -> v0.05d - 5/5/2001 - Jean II - * o Workaround to SNAP decapsulate frame from Linksys AP - * original patch from : Dong Liu <dliu AT research.bell-labs.com> - * (note : the memcmp bug was mine - fixed) - * o Remove set_retry stuff, no firmware support it (bloat--). - * - * v0.05d -> v0.06 - 25/5/2001 - Jean II - * Original patch from "Hong Lin" <alin AT redhat.com>, - * "Ian Kinner" <ikinner AT redhat.com> - * and "David Smith" <dsmith AT redhat.com> - * o Init of priv->tx_rate_ctrl in firmware specific section. - * o Prism2/Symbol rate, upto should be 0xF and not 0x15. Doh ! - * o Spectrum card always need cor_reset (for every reset) - * o Fix cor_reset to not lose bit 7 in the register - * o flush_stale_links to remove zombie Pcmcia instances - * o Ack previous hermes event before reset - * Me (with my little hands) - * o Allow orinoco.c to call cor_reset via priv->card_reset_handler - * o Add priv->need_card_reset to toggle this feature - * o Fix various buglets when setting WEP in Symbol firmware - * Now, encryption is fully functional on Symbol cards. Youpi ! - * - * v0.06 -> v0.06b - 25/5/2001 - Jean II - * o IBSS on Symbol use port_mode = 4. Please don't ask... - * - * v0.06b -> v0.06c - 29/5/2001 - Jean II - * o Show first spy address in /proc/net/wireless for IBSS mode as well - * - * v0.06c -> v0.06d - 6/7/2001 - David Gibson - * o Change a bunch of KERN_INFO messages to KERN_DEBUG, as per Linus' - * wishes to reduce the number of unnecessary messages. - * o Removed bogus message on CRC error. - * o Merged fixes for v0.08 Prism 2 firmware from William Waghorn - * <willwaghorn AT yahoo.co.uk> - * o Slight cleanup/re-arrangement of firmware detection code. - * - * v0.06d -> v0.06e - 1/8/2001 - David Gibson - * o Removed some redundant global initializers (orinoco_cs.c). - * o Added some module metadata - * - * v0.06e -> v0.06f - 14/8/2001 - David Gibson - * o Wording fix to license - * o Added a 'use_alternate_encaps' module parameter for APs which need an - * oui of 00:00:00. We really need a better way of handling this, but - * the module flag is better than nothing for now. - * - * v0.06f -> v0.07 - 20/8/2001 - David Gibson - * o Removed BAP error retries from hermes_bap_seek(). For Tx we now - * let the upper layers handle the retry, we retry explicitly in the - * Rx path, but don't make as much noise about it. - * o Firmware detection cleanups. - * - * v0.07 -> v0.07a - 1/10/3001 - Jean II - * o Add code to read Symbol firmware revision, inspired by latest code - * in Spectrum24 by Lee John Keyser-Allen - Thanks Lee ! - * o Thanks to Jared Valentine <hidden AT xmission.com> for "providing" me - * a 3Com card with a recent firmware, fill out Symbol firmware - * capabilities of latest rev (2.20), as well as older Symbol cards. - * o Disable Power Management in newer Symbol firmware, the API - * has changed (documentation needed). - * - * v0.07a -> v0.08 - 3/10/2001 - David Gibson - * o Fixed a possible buffer overrun found by the Stanford checker (in - * dldwd_ioctl_setiwencode()). Can only be called by root anyway, so not - * a big problem. - * o Turned has_big_wep on for Intersil cards. That's not true for all of - * them but we should at least let the capable ones try. - * o Wait for BUSY to clear at the beginning of hermes_bap_seek(). I - * realized that my assumption that the driver's serialization - * would prevent the BAP being busy on entry was possibly false, because - * things other than seeks may make the BAP busy. - * o Use "alternate" (oui 00:00:00) encapsulation by default. - * Setting use_old_encaps will mimic the old behaviour, but I think we - * will be able to eliminate this. - * o Don't try to make __initdata const (the version string). This can't - * work because of the way the __initdata sectioning works. - * o Added MODULE_LICENSE tags. - * o Support for PLX (transparent PCMCIA->PCI bridge) cards. - * o Changed to using the new type-fascist min/max. - * - * v0.08 -> v0.08a - 9/10/2001 - David Gibson - * o Inserted some missing acknowledgements/info into the Changelog. - * o Fixed some bugs in the normalization of signal level reporting. - * o Fixed bad bug in WEP key handling on Intersil and Symbol firmware, - * which led to an instant crash on big-endian machines. - * - * v0.08a -> v0.08b - 20/11/2001 - David Gibson - * o Lots of cleanup and bugfixes in orinoco_plx.c - * o Cleanup to handling of Tx rate setting. - * o Removed support for old encapsulation method. - * o Removed old "dldwd" names. - * o Split RID constants into a new file hermes_rid.h - * o Renamed RID constants to match linux-wlan-ng and prism2.o - * o Bugfixes in hermes.c - * o Poke the PLX's INTCSR register, so it actually starts - * generating interrupts. These cards might actually work now. - * o Update to wireless extensions v12 (Jean II) - * o Support for tallies and inquire command (Jean II) - * o Airport updates for newer PPC kernels (BenH) - * - * v0.08b -> v0.09 - 21/12/2001 - David Gibson - * o Some new PCI IDs for PLX cards. - * o Removed broken attempt to do ALLMULTI reception. Just use - * promiscuous mode instead - * o Preliminary work for list-AP (Jean II) - * o Airport updates from (BenH) - * o Eliminated racy hw_ready stuff - * o Fixed generation of fake events in irq handler. This should - * finally kill the EIO problems (Jean II & dgibson) - * o Fixed breakage of bitrate set/get on Agere firmware (Jean II) - * - * v0.09 -> v0.09a - 2/1/2002 - David Gibson - * o Fixed stupid mistake in multicast list handling, triggering - * a BUG() - * - * v0.09a -> v0.09b - 16/1/2002 - David Gibson - * o Fixed even stupider mistake in new interrupt handling, which - * seriously broke things on big-endian machines. - * o Removed a bunch of redundant includes and exports. - * o Removed a redundant MOD_{INC,DEC}_USE_COUNT pair in airport.c - * o Don't attempt to do hardware level multicast reception on - * Intersil firmware, just go promisc instead. - * o Typo fixed in hermes_issue_cmd() - * o Eliminated WIRELESS_SPY #ifdefs - * o Status code reported on Tx exceptions - * o Moved netif_wake_queue() from ALLOC interrupts to TX and TXEXC - * interrupts, which should fix the timeouts we're seeing. - * - * v0.09b -> v0.10 - 25 Feb 2002 - David Gibson - * o Removed nested structures used for header parsing, so the - * driver should now work without hackery on ARM - * o Fix for WEP handling on Intersil (Hawk Newton) - * o Eliminated the /proc/hermes/ethXX/regs debugging file. It - * was never very useful. - * o Make Rx errors less noisy. - * - * v0.10 -> v0.11 - 5 Apr 2002 - David Gibson - * o Laid the groundwork in hermes.[ch] for devices which map - * into PCI memory space rather than IO space. - * o Fixed bug in multicast handling (cleared multicast list when - * leaving promiscuous mode). - * o Relegated Tx error messages to debug. - * o Cleaned up / corrected handling of allocation lengths. - * o Set OWNSSID in IBSS mode for WinXP interoperability (jimc). - * o Change to using alloc_etherdev() for structure allocations. - * o Check for and drop undersized packets. - * o Fixed a race in stopping/waking the queue. This should fix - * the timeout problems (Pavel Roskin) - * o Reverted to netif_wake_queue() on the ALLOC event. - * o Fixes for recent Symbol firmwares which lack AP density - * (Pavel Roskin). - * - * v0.11 -> v0.11a - 29 Apr 2002 - David Gibson - * o Handle different register spacing, necessary for Prism 2.5 - * PCI adaptors (Steve Hill). - * o Cleaned up initialization of card structures in orinoco_cs - * and airport. Removed card->priv field. - * o Make response structure optional for hermes_docmd_wait() - * Pavel Roskin) - * o Added PCI id for Nortel emobility to orinoco_plx.c. - * o Cleanup to handling of Symbol's allocation bug. (Pavel Roskin) - * o Cleanups to firmware capability detection. - * o Arrange for orinoco_pci.c to override firmware detection. - * We should be able to support the PCI Intersil cards now. - * o Cleanup handling of reset_cor and hard_reset (Pavel Roskin). - * o Remove erroneous use of USER_BAP in the TxExc handler (Jouni - * Malinen). - * o Makefile changes for better integration into David Hinds - * pcmcia-cs package. - * - * v0.11a -> v0.11b - 1 May 2002 - David Gibson - * o Better error reporting in orinoco_plx_init_one() - * o Fixed multiple bad kfree() bugs introduced by the - * alloc_orinocodev() changes. - * - * v0.11b -> v0.12 - 19 Jun 2002 - David Gibson - * o Support changing the MAC address. - * o Correct display of Intersil firmware revision numbers. - * o Entirely revised locking scheme. Should be both simpler and - * better. - * o Merged some common code in orinoco_plx, orinoco_pci and - * airport by creating orinoco_default_{open,stop,reset}() - * which are used as the dev->open, dev->stop, priv->reset - * callbacks if none are specified when alloc_orinocodev() is - * called. - * o Removed orinoco_plx_interrupt() and orinoco_pci_interrupt(). - * They didn't do anything. - * - * v0.12 -> v0.12a - 4 Jul 2002 - David Gibson - * o Some rearrangement of code. - * o Numerous fixups to locking and rest handling, particularly - * for PCMCIA. - * o This allows open and stop net_device methods to be in - * orinoco.c now, rather than in the init modules. - * o In orinoco_cs.c link->priv now points to the struct - * net_device not to the struct orinoco_private. - * o Added a check for undersized SNAP frames, which could cause - * crashes. - * - * v0.12a -> v0.12b - 11 Jul 2002 - David Gibson - * o Fix hw->num_init testing code, so num_init is actually - * incremented. - * o Fix very stupid bug in orinoco_cs which broke compile with - * CONFIG_SMP. - * o Squashed a warning. - * - * v0.12b -> v0.12c - 26 Jul 2002 - David Gibson - * o Change to C9X style designated initializers. - * o Add support for 3Com AirConnect PCI. - * o No longer ignore the hard_reset argument to - * alloc_orinocodev(). Oops. - * - * v0.12c -> v0.13beta1 - 13 Sep 2002 - David Gibson - * o Revert the broken 0.12* locking scheme and go to a new yet - * simpler scheme. - * o Do firmware resets only in orinoco_init() and when waking - * the card from hard sleep. - * - * v0.13beta1 -> v0.13 - 27 Sep 2002 - David Gibson - * o Re-introduced full resets (via schedule_task()) on Tx - * timeout. - * - * v0.13 -> v0.13a - 30 Sep 2002 - David Gibson - * o Minor cleanups to info frame handling. Add basic support - * for linkstatus info frames. - * o Include required kernel headers in orinoco.h, to avoid - * compile problems. - * - * v0.13a -> v0.13b - 10 Feb 2003 - David Gibson - * o Implemented hard reset for Airport cards - * o Experimental suspend/resume implementation for orinoco_pci - * o Abolished /proc debugging support, replaced with a debugging - * iwpriv. Now it's ugly and simple instead of ugly and complex. - * o Bugfix in hermes.c if the firmware returned a record length - * of 0, we could go clobbering memory. - * o Bugfix in orinoco_stop() - it used to fail if hw_unavailable - * was set, which was usually true on PCMCIA hot removes. - * o Track LINKSTATUS messages, silently drop Tx packets before - * we are connected (avoids confusing the firmware), and only - * give LINKSTATUS printk()s if the status has changed. - * - * v0.13b -> v0.13c - 11 Mar 2003 - David Gibson - * o Cleanup: use dev instead of priv in various places. - * o Bug fix: Don't ReleaseConfiguration on RESET_PHYSICAL event - * if we're in the middle of a (driver initiated) hard reset. - * o Bug fix: ETH_ZLEN is supposed to include the header - * (Dionysus Blazakis & Manish Karir) - * o Convert to using workqueues instead of taskqueues (and - * backwards compatibility macros for pre 2.5.41 kernels). - * o Drop redundant (I think...) MOD_{INC,DEC}_USE_COUNT in - * airport.c - * o New orinoco_tmd.c init module from Joerg Dorchain for - * TMD7160 based PCI to PCMCIA bridges (similar to - * orinoco_plx.c). - * - * v0.13c -> v0.13d - 22 Apr 2003 - David Gibson - * o Make hw_unavailable a counter, rather than just a flag, this - * is necessary to avoid some races (such as a card being - * removed in the middle of orinoco_reset(). - * o Restore Release/RequestConfiguration in the PCMCIA event handler - * when dealing with a driver initiated hard reset. This is - * necessary to prevent hangs due to a spurious interrupt while - * the reset is in progress. - * o Clear the 802.11 header when transmitting, even though we - * don't use it. This fixes a long standing bug on some - * firmwares, which seem to get confused if that isn't done. - * o Be less eager to de-encapsulate SNAP frames, only do so if - * the OUI is 00:00:00 or 00:00:f8, leave others alone. The old - * behaviour broke CDP (Cisco Discovery Protocol). - * o Use dev instead of priv for free_irq() as well as - * request_irq() (oops). - * o Attempt to reset rather than giving up if we get too many - * IRQs. - * o Changed semantics of __orinoco_down() so it can be called - * safely with hw_unavailable set. It also now clears the - * linkstatus (since we're going to have to reassociate). - * - * v0.13d -> v0.13e - 12 May 2003 - David Gibson - * o Support for post-2.5.68 return values from irq handler. - * o Fixed bug where underlength packets would be double counted - * in the rx_dropped statistics. - * o Provided a module parameter to suppress linkstatus messages. - * - * v0.13e -> v0.14alpha1 - 30 Sep 2003 - David Gibson - * o Replaced priv->connected logic with netif_carrier_on/off() - * calls. - * o Remove has_ibss_any and never set the CREATEIBSS RID when - * the ESSID is empty. Too many firmwares break if we do. - * o 2.6 merges: Replace pdev->slot_name with pci_name(), remove - * __devinitdata from PCI ID tables, use free_netdev(). - * o Enabled shared-key authentication for Agere firmware (from - * Robert J. Moore <Robert.J.Moore AT allanbank.com> - * o Move netif_wake_queue() (back) to the Tx completion from the - * ALLOC event. This seems to prevent/mitigate the rolling - * error -110 problems at least on some Intersil firmwares. - * Theoretically reduces performance, but I can't measure it. - * Patch from Andrew Tridgell <tridge AT samba.org> - * - * v0.14alpha1 -> v0.14alpha2 - 20 Oct 2003 - David Gibson - * o Correctly turn off shared-key authentication when requested - * (bugfix from Robert J. Moore). - * o Correct airport sleep interfaces for current 2.6 kernels. - * o Add code for key change without disabling/enabling the MAC - * port. This is supposed to allow 802.1x to work sanely, but - * doesn't seem to yet. - * * TODO - * o New wireless extensions API (patch from Moustafa - * Youssef, updated by Jim Carter and Pavel Roskin). * o Handle de-encapsulation within network layer, provide 802.11 * headers (patch from Thomas 'Dent' Mirlacher) - * o RF monitor mode support * o Fix possible races in SPY handling. * o Disconnect wireless extensions from fundamental configuration. * o (maybe) Software WEP support (patch from Stano Meduna). @@ -462,7 +89,10 @@ #include <linux/netdevice.h> #include <linux/if_arp.h> #include <linux/etherdevice.h> +#include <linux/ethtool.h> #include <linux/wireless.h> +#include <net/iw_handler.h> +#include <net/ieee80211.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -496,6 +126,10 @@ static int ignore_disconnect; /* = 0 */ module_param(ignore_disconnect, int, 0644); MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer"); +static int force_monitor; /* = 0 */ +module_param(force_monitor, int, 0644); +MODULE_PARM_DESC(force_monitor, "Allow monitor mode for all firmware versions"); + /********************************************************************/ /* Compile time configuration and compatibility stuff */ /********************************************************************/ @@ -511,6 +145,10 @@ MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer /* Internal constants */ /********************************************************************/ +/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */ +static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; +#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2) + #define ORINOCO_MIN_MTU 256 #define ORINOCO_MAX_MTU (IEEE802_11_DATA_LEN - ENCAPS_OVERHEAD) @@ -537,6 +175,11 @@ MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer | HERMES_EV_WTERR | HERMES_EV_INFO \ | HERMES_EV_INFDROP ) +#define MAX_RID_LEN 1024 + +static const struct iw_handler_def orinoco_handler_def; +static struct ethtool_ops orinoco_ethtool_ops; + /********************************************************************/ /* Data tables */ /********************************************************************/ @@ -571,26 +214,45 @@ static struct { /* Data types */ /********************************************************************/ -struct header_struct { - /* 802.3 */ - u8 dest[ETH_ALEN]; - u8 src[ETH_ALEN]; - u16 len; - /* 802.2 */ +/* Used in Event handling. + * We avoid nested structres as they break on ARM -- Moustafa */ +struct hermes_tx_descriptor_802_11 { + /* hermes_tx_descriptor */ + u16 status; + u16 reserved1; + u16 reserved2; + u32 sw_support; + u8 retry_count; + u8 tx_rate; + u16 tx_control; + + /* ieee802_11_hdr */ + u16 frame_ctl; + u16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + u16 seq_ctl; + u8 addr4[ETH_ALEN]; + u16 data_len; + + /* ethhdr */ + unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ + unsigned char h_source[ETH_ALEN]; /* source ether addr */ + unsigned short h_proto; /* packet type ID field */ + + /* p8022_hdr */ u8 dsap; u8 ssap; u8 ctrl; - /* SNAP */ u8 oui[3]; + u16 ethertype; } __attribute__ ((packed)); -/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */ -u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; - -#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2) - +/* Rx frame header except compatibility 802.3 header */ struct hermes_rx_descriptor { + /* Control */ u16 status; u32 time; u8 silence; @@ -598,13 +260,24 @@ struct hermes_rx_descriptor { u8 rate; u8 rxflow; u32 reserved; + + /* 802.11 header */ + u16 frame_ctl; + u16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + u16 seq_ctl; + u8 addr4[ETH_ALEN]; + + /* Data length */ + u16 data_len; } __attribute__ ((packed)); /********************************************************************/ /* Function prototypes */ /********************************************************************/ -static int orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static int __orinoco_program_rids(struct net_device *dev); static void __orinoco_set_multicast_list(struct net_device *dev); @@ -628,6 +301,10 @@ static inline void set_port_type(struct orinoco_private *priv) priv->createibss = 1; } break; + case IW_MODE_MONITOR: + priv->port_type = 3; + priv->createibss = 0; + break; default: printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n", priv->ndev->name); @@ -814,7 +491,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) return 1; } - if (! netif_carrier_ok(dev)) { + if (! netif_carrier_ok(dev) || (priv->iw_mode == IW_MODE_MONITOR)) { /* Oops, the firmware hasn't established a connection, silently drop the packet (this seems to be the safest approach). */ @@ -951,26 +628,55 @@ static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw) struct orinoco_private *priv = netdev_priv(dev); struct net_device_stats *stats = &priv->stats; u16 fid = hermes_read_regn(hw, TXCOMPLFID); - struct hermes_tx_descriptor desc; + struct hermes_tx_descriptor_802_11 hdr; int err = 0; if (fid == DUMMY_FID) return; /* Nothing's really happened */ - err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), fid, 0); + /* Read the frame header */ + err = hermes_bap_pread(hw, IRQ_BAP, &hdr, + sizeof(struct hermes_tx_descriptor) + + sizeof(struct ieee80211_hdr), + fid, 0); + + hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); + stats->tx_errors++; + if (err) { printk(KERN_WARNING "%s: Unable to read descriptor on Tx error " "(FID=%04X error %d)\n", dev->name, fid, err); - } else { - DEBUG(1, "%s: Tx error, status %d\n", - dev->name, le16_to_cpu(desc.status)); + return; } - stats->tx_errors++; + DEBUG(1, "%s: Tx error, err %d (FID=%04X)\n", dev->name, + err, fid); + + /* We produce a TXDROP event only for retry or lifetime + * exceeded, because that's the only status that really mean + * that this particular node went away. + * Other errors means that *we* screwed up. - Jean II */ + hdr.status = le16_to_cpu(hdr.status); + if (hdr.status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) { + union iwreq_data wrqu; + + /* Copy 802.11 dest address. + * We use the 802.11 header because the frame may + * not be 802.3 or may be mangled... + * In Ad-Hoc mode, it will be the node address. + * In managed mode, it will be most likely the AP addr + * User space will figure out how to convert it to + * whatever it needs (IP address or else). + * - Jean II */ + memcpy(wrqu.addr.sa_data, hdr.addr1, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + + /* Send event to user space */ + wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL); + } netif_wake_queue(dev); - hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); } static void orinoco_tx_timeout(struct net_device *dev) @@ -1047,18 +753,127 @@ static void orinoco_stat_gather(struct net_device *dev, } } +/* + * orinoco_rx_monitor - handle received monitor frames. + * + * Arguments: + * dev network device + * rxfid received FID + * desc rx descriptor of the frame + * + * Call context: interrupt + */ +static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid, + struct hermes_rx_descriptor *desc) +{ + u32 hdrlen = 30; /* return full header by default */ + u32 datalen = 0; + u16 fc; + int err; + int len; + struct sk_buff *skb; + struct orinoco_private *priv = netdev_priv(dev); + struct net_device_stats *stats = &priv->stats; + hermes_t *hw = &priv->hw; + + len = le16_to_cpu(desc->data_len); + + /* Determine the size of the header and the data */ + fc = le16_to_cpu(desc->frame_ctl); + switch (fc & IEEE80211_FCTL_FTYPE) { + case IEEE80211_FTYPE_DATA: + if ((fc & IEEE80211_FCTL_TODS) + && (fc & IEEE80211_FCTL_FROMDS)) + hdrlen = 30; + else + hdrlen = 24; + datalen = len; + break; + case IEEE80211_FTYPE_MGMT: + hdrlen = 24; + datalen = len; + break; + case IEEE80211_FTYPE_CTL: + switch (fc & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_PSPOLL: + case IEEE80211_STYPE_RTS: + case IEEE80211_STYPE_CFEND: + case IEEE80211_STYPE_CFENDACK: + hdrlen = 16; + break; + case IEEE80211_STYPE_CTS: + case IEEE80211_STYPE_ACK: + hdrlen = 10; + break; + } + break; + default: + /* Unknown frame type */ + break; + } + + /* sanity check the length */ + if (datalen > IEEE80211_DATA_LEN + 12) { + printk(KERN_DEBUG "%s: oversized monitor frame, " + "data length = %d\n", dev->name, datalen); + err = -EIO; + stats->rx_length_errors++; + goto update_stats; + } + + skb = dev_alloc_skb(hdrlen + datalen); + if (!skb) { + printk(KERN_WARNING "%s: Cannot allocate skb for monitor frame\n", + dev->name); + err = -ENOMEM; + goto drop; + } + + /* Copy the 802.11 header to the skb */ + memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen); + skb->mac.raw = skb->data; + + /* If any, copy the data from the card to the skb */ + if (datalen > 0) { + err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, datalen), + ALIGN(datalen, 2), rxfid, + HERMES_802_2_OFFSET); + if (err) { + printk(KERN_ERR "%s: error %d reading monitor frame\n", + dev->name, err); + goto drop; + } + } + + skb->dev = dev; + skb->ip_summed = CHECKSUM_NONE; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = __constant_htons(ETH_P_802_2); + + dev->last_rx = jiffies; + stats->rx_packets++; + stats->rx_bytes += skb->len; + + netif_rx(skb); + return; + + drop: + dev_kfree_skb_irq(skb); + update_stats: + stats->rx_errors++; + stats->rx_dropped++; +} + static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) { struct orinoco_private *priv = netdev_priv(dev); struct net_device_stats *stats = &priv->stats; struct iw_statistics *wstats = &priv->wstats; struct sk_buff *skb = NULL; - u16 rxfid, status; - int length, data_len, data_off; - char *p; + u16 rxfid, status, fc; + int length; struct hermes_rx_descriptor desc; - struct header_struct hdr; - struct ethhdr *eh; + struct ethhdr *hdr; int err; rxfid = hermes_read_regn(hw, RXFID); @@ -1068,53 +883,46 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) if (err) { printk(KERN_ERR "%s: error %d reading Rx descriptor. " "Frame dropped.\n", dev->name, err); - stats->rx_errors++; - goto drop; + goto update_stats; } status = le16_to_cpu(desc.status); - if (status & HERMES_RXSTAT_ERR) { - if (status & HERMES_RXSTAT_UNDECRYPTABLE) { - wstats->discard.code++; - DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n", - dev->name); - } else { - stats->rx_crc_errors++; - DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", dev->name); - } - stats->rx_errors++; - goto drop; + if (status & HERMES_RXSTAT_BADCRC) { + DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", + dev->name); + stats->rx_crc_errors++; + goto update_stats; } - /* For now we ignore the 802.11 header completely, assuming - that the card's firmware has handled anything vital */ + /* Handle frames in monitor mode */ + if (priv->iw_mode == IW_MODE_MONITOR) { + orinoco_rx_monitor(dev, rxfid, &desc); + return; + } - err = hermes_bap_pread(hw, IRQ_BAP, &hdr, sizeof(hdr), - rxfid, HERMES_802_3_OFFSET); - if (err) { - printk(KERN_ERR "%s: error %d reading frame header. " - "Frame dropped.\n", dev->name, err); - stats->rx_errors++; - goto drop; + if (status & HERMES_RXSTAT_UNDECRYPTABLE) { + DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n", + dev->name); + wstats->discard.code++; + goto update_stats; } - length = ntohs(hdr.len); - + length = le16_to_cpu(desc.data_len); + fc = le16_to_cpu(desc.frame_ctl); + /* Sanity checks */ if (length < 3) { /* No for even an 802.2 LLC header */ /* At least on Symbol firmware with PCF we get quite a lot of these legitimately - Poll frames with no data. */ - stats->rx_dropped++; - goto drop; + return; } if (length > IEEE802_11_DATA_LEN) { printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n", dev->name, length); stats->rx_length_errors++; - stats->rx_errors++; - goto drop; + goto update_stats; } /* We need space for the packet data itself, plus an ethernet @@ -1126,60 +934,53 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) if (!skb) { printk(KERN_WARNING "%s: Can't allocate skb for Rx\n", dev->name); - goto drop; + goto update_stats; } - skb_reserve(skb, 2); /* This way the IP header is aligned */ + /* We'll prepend the header, so reserve space for it. The worst + case is no decapsulation, when 802.3 header is prepended and + nothing is removed. 2 is for aligning the IP header. */ + skb_reserve(skb, ETH_HLEN + 2); + + err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, length), + ALIGN(length, 2), rxfid, + HERMES_802_2_OFFSET); + if (err) { + printk(KERN_ERR "%s: error %d reading frame. " + "Frame dropped.\n", dev->name, err); + goto drop; + } /* Handle decapsulation * In most cases, the firmware tell us about SNAP frames. * For some reason, the SNAP frames sent by LinkSys APs * are not properly recognised by most firmwares. * So, check ourselves */ - if (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) || - ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) || - is_ethersnap(&hdr)) { + if (length >= ENCAPS_OVERHEAD && + (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) || + ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) || + is_ethersnap(skb->data))) { /* These indicate a SNAP within 802.2 LLC within 802.11 frame which we'll need to de-encapsulate to the original EthernetII frame. */ - - if (length < ENCAPS_OVERHEAD) { /* No room for full LLC+SNAP */ - stats->rx_length_errors++; - goto drop; - } - - /* Remove SNAP header, reconstruct EthernetII frame */ - data_len = length - ENCAPS_OVERHEAD; - data_off = HERMES_802_3_OFFSET + sizeof(hdr); - - eh = (struct ethhdr *)skb_put(skb, ETH_HLEN); - - memcpy(eh, &hdr, 2 * ETH_ALEN); - eh->h_proto = hdr.ethertype; + hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN - ENCAPS_OVERHEAD); } else { - /* All other cases indicate a genuine 802.3 frame. No - decapsulation needed. We just throw the whole - thing in, and hope the protocol layer can deal with - it as 802.3 */ - data_len = length; - data_off = HERMES_802_3_OFFSET; - /* FIXME: we re-read from the card data we already read here */ - } - - p = skb_put(skb, data_len); - err = hermes_bap_pread(hw, IRQ_BAP, p, ALIGN(data_len, 2), - rxfid, data_off); - if (err) { - printk(KERN_ERR "%s: error %d reading frame. " - "Frame dropped.\n", dev->name, err); - stats->rx_errors++; - goto drop; + /* 802.3 frame - prepend 802.3 header as is */ + hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN); + hdr->h_proto = htons(length); } + memcpy(hdr->h_dest, desc.addr1, ETH_ALEN); + if (fc & IEEE80211_FCTL_FROMDS) + memcpy(hdr->h_source, desc.addr3, ETH_ALEN); + else + memcpy(hdr->h_source, desc.addr2, ETH_ALEN); dev->last_rx = jiffies; skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_NONE; + if (fc & IEEE80211_FCTL_TODS) + skb->pkt_type = PACKET_OTHERHOST; /* Process the wireless stats if needed */ orinoco_stat_gather(dev, skb, &desc); @@ -1192,11 +993,10 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) return; drop: + dev_kfree_skb_irq(skb); + update_stats: + stats->rx_errors++; stats->rx_dropped++; - - if (skb) - dev_kfree_skb_irq(skb); - return; } /********************************************************************/ @@ -1240,6 +1040,99 @@ static void print_linkstatus(struct net_device *dev, u16 status) dev->name, s, status); } +/* Search scan results for requested BSSID, join it if found */ +static void orinoco_join_ap(struct net_device *dev) +{ + struct orinoco_private *priv = netdev_priv(dev); + struct hermes *hw = &priv->hw; + int err; + unsigned long flags; + struct join_req { + u8 bssid[ETH_ALEN]; + u16 channel; + } __attribute__ ((packed)) req; + const int atom_len = offsetof(struct prism2_scan_apinfo, atim); + struct prism2_scan_apinfo *atom; + int offset = 4; + u8 *buf; + u16 len; + + /* Allocate buffer for scan results */ + buf = kmalloc(MAX_SCAN_LEN, GFP_KERNEL); + if (! buf) + return; + + if (orinoco_lock(priv, &flags) != 0) + goto out; + + /* Sanity checks in case user changed something in the meantime */ + if (! priv->bssid_fixed) + goto out; + + if (strlen(priv->desired_essid) == 0) + goto out; + + /* Read scan results from the firmware */ + err = hermes_read_ltv(hw, USER_BAP, + HERMES_RID_SCANRESULTSTABLE, + MAX_SCAN_LEN, &len, buf); + if (err) { + printk(KERN_ERR "%s: Cannot read scan results\n", + dev->name); + goto out; + } + + len = HERMES_RECLEN_TO_BYTES(len); + + /* Go through the scan results looking for the channel of the AP + * we were requested to join */ + for (; offset + atom_len <= len; offset += atom_len) { + atom = (struct prism2_scan_apinfo *) (buf + offset); + if (memcmp(&atom->bssid, priv->desired_bssid, ETH_ALEN) == 0) + goto found; + } + + DEBUG(1, "%s: Requested AP not found in scan results\n", + dev->name); + goto out; + + found: + memcpy(req.bssid, priv->desired_bssid, ETH_ALEN); + req.channel = atom->channel; /* both are little-endian */ + err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFJOINREQUEST, + &req); + if (err) + printk(KERN_ERR "%s: Error issuing join request\n", dev->name); + + out: + kfree(buf); + orinoco_unlock(priv, &flags); +} + +/* Send new BSSID to userspace */ +static void orinoco_send_wevents(struct net_device *dev) +{ + struct orinoco_private *priv = netdev_priv(dev); + struct hermes *hw = &priv->hw; + union iwreq_data wrqu; + int err; + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return; + + err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENTBSSID, + ETH_ALEN, NULL, wrqu.ap_addr.sa_data); + if (err != 0) + return; + + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + + /* Send event to user space */ + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); + orinoco_unlock(priv, &flags); +} + static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) { struct orinoco_private *priv = netdev_priv(dev); @@ -1307,6 +1200,9 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) u16 newstatus; int connected; + if (priv->iw_mode == IW_MODE_MONITOR) + break; + if (len != sizeof(linkstatus)) { printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n", dev->name, len); @@ -1319,6 +1215,15 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) break; newstatus = le16_to_cpu(linkstatus.linkstatus); + /* Symbol firmware uses "out of range" to signal that + * the hostscan frame can be requested. */ + if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE && + priv->firmware_type == FIRMWARE_TYPE_SYMBOL && + priv->has_hostscan && priv->scan_inprogress) { + hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL); + break; + } + connected = (newstatus == HERMES_LINKSTATUS_CONNECTED) || (newstatus == HERMES_LINKSTATUS_AP_CHANGE) || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE); @@ -1328,12 +1233,89 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) else if (!ignore_disconnect) netif_carrier_off(dev); - if (newstatus != priv->last_linkstatus) + if (newstatus != priv->last_linkstatus) { + priv->last_linkstatus = newstatus; print_linkstatus(dev, newstatus); + /* The info frame contains only one word which is the + * status (see hermes.h). The status is pretty boring + * in itself, that's why we export the new BSSID... + * Jean II */ + schedule_work(&priv->wevent_work); + } + } + break; + case HERMES_INQ_SCAN: + if (!priv->scan_inprogress && priv->bssid_fixed && + priv->firmware_type == FIRMWARE_TYPE_INTERSIL) { + schedule_work(&priv->join_work); + break; + } + /* fall through */ + case HERMES_INQ_HOSTSCAN: + case HERMES_INQ_HOSTSCAN_SYMBOL: { + /* Result of a scanning. Contains information about + * cells in the vicinity - Jean II */ + union iwreq_data wrqu; + unsigned char *buf; + + /* Sanity check */ + if (len > 4096) { + printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n", + dev->name, len); + break; + } - priv->last_linkstatus = newstatus; + /* We are a strict producer. If the previous scan results + * have not been consumed, we just have to drop this + * frame. We can't remove the previous results ourselves, + * that would be *very* racy... Jean II */ + if (priv->scan_result != NULL) { + printk(KERN_WARNING "%s: Previous scan results not consumed, dropping info frame.\n", dev->name); + break; + } + + /* Allocate buffer for results */ + buf = kmalloc(len, GFP_ATOMIC); + if (buf == NULL) + /* No memory, so can't printk()... */ + break; + + /* Read scan data */ + err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len, + infofid, sizeof(info)); + if (err) + break; + +#ifdef ORINOCO_DEBUG + { + int i; + printk(KERN_DEBUG "Scan result [%02X", buf[0]); + for(i = 1; i < (len * 2); i++) + printk(":%02X", buf[i]); + printk("]\n"); + } +#endif /* ORINOCO_DEBUG */ + + /* Allow the clients to access the results */ + priv->scan_len = len; + priv->scan_result = buf; + + /* Send an empty event to user space. + * We don't send the received data on the event because + * it would require us to do complex transcoding, and + * we want to minimise the work done in the irq handler + * Use a request to extract the data - Jean II */ + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); } break; + case HERMES_INQ_SEC_STAT_AGERE: + /* Security status (Agere specific) */ + /* Ignore this frame for now */ + if (priv->firmware_type == FIRMWARE_TYPE_AGERE) + break; + /* fall through */ default: printk(KERN_DEBUG "%s: Unknown information frame received: " "type 0x%04x, length %d\n", dev->name, type, len); @@ -1470,6 +1452,36 @@ static int __orinoco_hw_set_bitrate(struct orinoco_private *priv) return err; } +/* Set fixed AP address */ +static int __orinoco_hw_set_wap(struct orinoco_private *priv) +{ + int roaming_flag; + int err = 0; + hermes_t *hw = &priv->hw; + + switch (priv->firmware_type) { + case FIRMWARE_TYPE_AGERE: + /* not supported */ + break; + case FIRMWARE_TYPE_INTERSIL: + if (priv->bssid_fixed) + roaming_flag = 2; + else + roaming_flag = 1; + + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFROAMINGMODE, + roaming_flag); + break; + case FIRMWARE_TYPE_SYMBOL: + err = HERMES_WRITE_RECORD(hw, USER_BAP, + HERMES_RID_CNFMANDATORYBSSID_SYMBOL, + &priv->desired_bssid); + break; + } + return err; +} + /* Change the WEP keys and/or the current keys. Can be called * either from __orinoco_hw_setup_wep() or directly from * orinoco_ioctl_setiwencode(). In the later case the association @@ -1655,6 +1667,13 @@ static int __orinoco_program_rids(struct net_device *dev) } } + /* Set the desired BSSID */ + err = __orinoco_hw_set_wap(priv); + if (err) { + printk(KERN_ERR "%s: Error %d setting AP address\n", + dev->name, err); + return err; + } /* Set the desired ESSID */ idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); @@ -1793,6 +1812,20 @@ static int __orinoco_program_rids(struct net_device *dev) } } + if (priv->iw_mode == IW_MODE_MONITOR) { + /* Enable monitor mode */ + dev->type = ARPHRD_IEEE80211; + err = hermes_docmd_wait(hw, HERMES_CMD_TEST | + HERMES_TEST_MONITOR, 0, NULL); + } else { + /* Disable monitor mode */ + dev->type = ARPHRD_ETHER; + err = hermes_docmd_wait(hw, HERMES_CMD_TEST | + HERMES_TEST_STOP, 0, NULL); + } + if (err) + return err; + /* Set promiscuity / multicast*/ priv->promiscuous = 0; priv->mc_count = 0; @@ -1869,55 +1902,6 @@ __orinoco_set_multicast_list(struct net_device *dev) dev->flags &= ~IFF_PROMISC; } -static int orinoco_reconfigure(struct net_device *dev) -{ - struct orinoco_private *priv = netdev_priv(dev); - struct hermes *hw = &priv->hw; - unsigned long flags; - int err = 0; - - if (priv->broken_disableport) { - schedule_work(&priv->reset_work); - return 0; - } - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - err = hermes_disable_port(hw, 0); - if (err) { - printk(KERN_WARNING "%s: Unable to disable port while reconfiguring card\n", - dev->name); - priv->broken_disableport = 1; - goto out; - } - - err = __orinoco_program_rids(dev); - if (err) { - printk(KERN_WARNING "%s: Unable to reconfigure card\n", - dev->name); - goto out; - } - - err = hermes_enable_port(hw, 0); - if (err) { - printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", - dev->name); - goto out; - } - - out: - if (err) { - printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); - schedule_work(&priv->reset_work); - err = 0; - } - - orinoco_unlock(priv, &flags); - return err; - -} - /* This must be called from user context, without locks held - use * schedule_work() */ static void orinoco_reset(struct net_device *dev) @@ -1946,6 +1930,11 @@ static void orinoco_reset(struct net_device *dev) orinoco_unlock(priv, &flags); + /* Scanning support: Cleanup of driver struct */ + kfree(priv->scan_result); + priv->scan_result = NULL; + priv->scan_inprogress = 0; + if (priv->hard_reset) { err = (*priv->hard_reset)(priv); if (err) { @@ -2184,6 +2173,8 @@ static int determine_firmware(struct net_device *dev) priv->has_mwo = (firmver >= 0x60000); priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ priv->ibss_port = 1; + priv->has_hostscan = (firmver >= 0x8000a); + priv->broken_monitor = (firmver >= 0x80000); /* Tested with Agere firmware : * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II @@ -2229,6 +2220,8 @@ static int determine_firmware(struct net_device *dev) priv->ibss_port = 4; priv->broken_disableport = (firmver == 0x25013) || (firmver >= 0x30000 && firmver <= 0x31000); + priv->has_hostscan = (firmver >= 0x31001) || + (firmver >= 0x29057 && firmver < 0x30000); /* Tested with Intel firmware : 0x20015 => Jean II */ /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */ break; @@ -2248,6 +2241,7 @@ static int determine_firmware(struct net_device *dev) priv->has_ibss = (firmver >= 0x000700); /* FIXME */ priv->has_big_wep = priv->has_wep = (firmver >= 0x000800); priv->has_pm = (firmver >= 0x000700); + priv->has_hostscan = (firmver >= 0x010301); if (firmver >= 0x000800) priv->ibss_port = 0; @@ -2456,8 +2450,9 @@ struct net_device *alloc_orinocodev(int sizeof_card, dev->tx_timeout = orinoco_tx_timeout; dev->watchdog_timeo = HZ; /* 1 second timeout */ dev->get_stats = orinoco_get_stats; + dev->ethtool_ops = &orinoco_ethtool_ops; dev->get_wireless_stats = orinoco_get_wireless_stats; - dev->do_ioctl = orinoco_ioctl; + dev->wireless_handlers = (struct iw_handler_def *)&orinoco_handler_def; dev->change_mtu = orinoco_change_mtu; dev->set_multicast_list = orinoco_set_multicast_list; /* we use the default eth_mac_addr for setting the MAC addr */ @@ -2473,6 +2468,8 @@ struct net_device *alloc_orinocodev(int sizeof_card, * before anything else touches the * hardware */ INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev); + INIT_WORK(&priv->join_work, (void (*)(void *))orinoco_join_ap, dev); + INIT_WORK(&priv->wevent_work, (void (*)(void *))orinoco_send_wevents, dev); netif_carrier_off(dev); priv->last_linkstatus = 0xffff; @@ -2483,6 +2480,9 @@ struct net_device *alloc_orinocodev(int sizeof_card, void free_orinocodev(struct net_device *dev) { + struct orinoco_private *priv = netdev_priv(dev); + + kfree(priv->scan_result); free_netdev(dev); } @@ -2490,24 +2490,6 @@ void free_orinocodev(struct net_device *dev) /* Wireless extensions */ /********************************************************************/ -static int orinoco_hw_get_bssid(struct orinoco_private *priv, - char buf[ETH_ALEN]) -{ - hermes_t *hw = &priv->hw; - int err = 0; - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, - ETH_ALEN, NULL, buf); - - orinoco_unlock(priv, &flags); - - return err; -} - static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, char buf[IW_ESSID_MAX_SIZE+1]) { @@ -2633,140 +2615,271 @@ static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, return 0; } -static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq) +static int orinoco_ioctl_getname(struct net_device *dev, + struct iw_request_info *info, + char *name, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); - int err = 0; - int mode; - struct iw_range range; int numrates; - int i, k; + int err; + + err = orinoco_hw_get_bitratelist(priv, &numrates, NULL, 0); + + if (!err && (numrates > 2)) + strcpy(name, "IEEE 802.11b"); + else + strcpy(name, "IEEE 802.11-DS"); + + return 0; +} + +static int orinoco_ioctl_setwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, + char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; + static const u8 off_addr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const u8 any_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - TRACE_ENTER(dev->name); + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + /* Enable automatic roaming - no sanity checks are needed */ + if (memcmp(&ap_addr->sa_data, off_addr, ETH_ALEN) == 0 || + memcmp(&ap_addr->sa_data, any_addr, ETH_ALEN) == 0) { + priv->bssid_fixed = 0; + memset(priv->desired_bssid, 0, ETH_ALEN); + + /* "off" means keep existing connection */ + if (ap_addr->sa_data[0] == 0) { + __orinoco_hw_set_wap(priv); + err = 0; + } + goto out; + } + + if (priv->firmware_type == FIRMWARE_TYPE_AGERE) { + printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't " + "support manual roaming\n", + dev->name); + err = -EOPNOTSUPP; + goto out; + } + + if (priv->iw_mode != IW_MODE_INFRA) { + printk(KERN_WARNING "%s: Manual roaming supported only in " + "managed mode\n", dev->name); + err = -EOPNOTSUPP; + goto out; + } + + /* Intersil firmware hangs without Desired ESSID */ + if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL && + strlen(priv->desired_essid) == 0) { + printk(KERN_WARNING "%s: Desired ESSID must be set for " + "manual roaming\n", dev->name); + err = -EOPNOTSUPP; + goto out; + } + + /* Finally, enable manual roaming */ + priv->bssid_fixed = 1; + memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN); + + out: + orinoco_unlock(priv, &flags); + return err; +} + +static int orinoco_ioctl_getwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, + char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + + hermes_t *hw = &priv->hw; + int err = 0; + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + ap_addr->sa_family = ARPHRD_ETHER; + err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, + ETH_ALEN, NULL, ap_addr->sa_data); + + orinoco_unlock(priv, &flags); + + return err; +} - if (!access_ok(VERIFY_WRITE, rrq->pointer, sizeof(range))) - return -EFAULT; +static int orinoco_ioctl_setmode(struct net_device *dev, + struct iw_request_info *info, + u32 *mode, + char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + int err = -EINPROGRESS; /* Call commit handler */ + unsigned long flags; - rrq->length = sizeof(range); + if (priv->iw_mode == *mode) + return 0; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; - mode = priv->iw_mode; + switch (*mode) { + case IW_MODE_ADHOC: + if (!priv->has_ibss && !priv->has_port3) + err = -EOPNOTSUPP; + break; + + case IW_MODE_INFRA: + break; + + case IW_MODE_MONITOR: + if (priv->broken_monitor && !force_monitor) { + printk(KERN_WARNING "%s: Monitor mode support is " + "buggy in this firmware, not enabling\n", + dev->name); + err = -EOPNOTSUPP; + } + break; + + default: + err = -EOPNOTSUPP; + break; + } + + if (err == -EINPROGRESS) { + priv->iw_mode = *mode; + set_port_type(priv); + } + orinoco_unlock(priv, &flags); - memset(&range, 0, sizeof(range)); + return err; +} + +static int orinoco_ioctl_getmode(struct net_device *dev, + struct iw_request_info *info, + u32 *mode, + char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + + *mode = priv->iw_mode; + return 0; +} + +static int orinoco_ioctl_getiwrange(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *rrq, + char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + int err = 0; + struct iw_range *range = (struct iw_range *) extra; + int numrates; + int i, k; + + TRACE_ENTER(dev->name); - /* Much of this shamelessly taken from wvlan_cs.c. No idea - * what it all means -dgibson */ - range.we_version_compiled = WIRELESS_EXT; - range.we_version_source = 11; + rrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); - range.min_nwid = range.max_nwid = 0; /* We don't use nwids */ + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 14; /* Set available channels/frequencies */ - range.num_channels = NUM_CHANNELS; + range->num_channels = NUM_CHANNELS; k = 0; for (i = 0; i < NUM_CHANNELS; i++) { if (priv->channel_mask & (1 << i)) { - range.freq[k].i = i + 1; - range.freq[k].m = channel_frequency[i] * 100000; - range.freq[k].e = 1; + range->freq[k].i = i + 1; + range->freq[k].m = channel_frequency[i] * 100000; + range->freq[k].e = 1; k++; } if (k >= IW_MAX_FREQUENCIES) break; } - range.num_frequency = k; + range->num_frequency = k; + range->sensitivity = 3; - range.sensitivity = 3; + if (priv->has_wep) { + range->max_encoding_tokens = ORINOCO_MAX_KEYS; + range->encoding_size[0] = SMALL_KEY_SIZE; + range->num_encoding_sizes = 1; - if ((mode == IW_MODE_ADHOC) && (priv->spy_number == 0)){ + if (priv->has_big_wep) { + range->encoding_size[1] = LARGE_KEY_SIZE; + range->num_encoding_sizes = 2; + } + } + + if ((priv->iw_mode == IW_MODE_ADHOC) && (priv->spy_number == 0)){ /* Quality stats meaningless in ad-hoc mode */ - range.max_qual.qual = 0; - range.max_qual.level = 0; - range.max_qual.noise = 0; - range.avg_qual.qual = 0; - range.avg_qual.level = 0; - range.avg_qual.noise = 0; } else { - range.max_qual.qual = 0x8b - 0x2f; - range.max_qual.level = 0x2f - 0x95 - 1; - range.max_qual.noise = 0x2f - 0x95 - 1; + range->max_qual.qual = 0x8b - 0x2f; + range->max_qual.level = 0x2f - 0x95 - 1; + range->max_qual.noise = 0x2f - 0x95 - 1; /* Need to get better values */ - range.avg_qual.qual = 0x24; - range.avg_qual.level = 0xC2; - range.avg_qual.noise = 0x9E; + range->avg_qual.qual = 0x24; + range->avg_qual.level = 0xC2; + range->avg_qual.noise = 0x9E; } err = orinoco_hw_get_bitratelist(priv, &numrates, - range.bitrate, IW_MAX_BITRATES); + range->bitrate, IW_MAX_BITRATES); if (err) return err; - range.num_bitrates = numrates; - + range->num_bitrates = numrates; + /* Set an indication of the max TCP throughput in bit/s that we can * expect using this interface. May be use for QoS stuff... * Jean II */ - if(numrates > 2) - range.throughput = 5 * 1000 * 1000; /* ~5 Mb/s */ + if (numrates > 2) + range->throughput = 5 * 1000 * 1000; /* ~5 Mb/s */ else - range.throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */ - - range.min_rts = 0; - range.max_rts = 2347; - range.min_frag = 256; - range.max_frag = 2346; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - if (priv->has_wep) { - range.max_encoding_tokens = ORINOCO_MAX_KEYS; - - range.encoding_size[0] = SMALL_KEY_SIZE; - range.num_encoding_sizes = 1; - - if (priv->has_big_wep) { - range.encoding_size[1] = LARGE_KEY_SIZE; - range.num_encoding_sizes = 2; - } - } else { - range.num_encoding_sizes = 0; - range.max_encoding_tokens = 0; - } - orinoco_unlock(priv, &flags); - - range.min_pmp = 0; - range.max_pmp = 65535000; - range.min_pmt = 0; - range.max_pmt = 65535 * 1000; /* ??? */ - range.pmp_flags = IW_POWER_PERIOD; - range.pmt_flags = IW_POWER_TIMEOUT; - range.pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_UNICAST_R; - - range.num_txpower = 1; - range.txpower[0] = 15; /* 15dBm */ - range.txpower_capa = IW_TXPOW_DBM; - - range.retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; - range.retry_flags = IW_RETRY_LIMIT; - range.r_time_flags = IW_RETRY_LIFETIME; - range.min_retry = 0; - range.max_retry = 65535; /* ??? */ - range.min_r_time = 0; - range.max_r_time = 65535 * 1000; /* ??? */ - - if (copy_to_user(rrq->pointer, &range, sizeof(range))) - return -EFAULT; + range->throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */ + + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + + range->min_pmp = 0; + range->max_pmp = 65535000; + range->min_pmt = 0; + range->max_pmt = 65535 * 1000; /* ??? */ + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_UNICAST_R; + + range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; + range->retry_flags = IW_RETRY_LIMIT; + range->r_time_flags = IW_RETRY_LIFETIME; + range->min_retry = 0; + range->max_retry = 65535; /* ??? */ + range->min_r_time = 0; + range->max_r_time = 65535 * 1000; /* ??? */ TRACE_EXIT(dev->name); return 0; } -static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq) +static int orinoco_ioctl_setiwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, + char *keybuf) { struct orinoco_private *priv = netdev_priv(dev); int index = (erq->flags & IW_ENCODE_INDEX) - 1; @@ -2774,8 +2887,7 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er int enable = priv->wep_on; int restricted = priv->wep_restrict; u16 xlen = 0; - int err = 0; - char keybuf[ORINOCO_MAX_KEY_SIZE]; + int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; if (! priv->has_wep) @@ -2788,9 +2900,6 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er if ( (erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep ) return -E2BIG; - - if (copy_from_user(keybuf, erq->pointer, erq->length)) - return -EFAULT; } if (orinoco_lock(priv, &flags) != 0) @@ -2864,12 +2973,14 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er return err; } -static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq) +static int orinoco_ioctl_getiwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, + char *keybuf) { struct orinoco_private *priv = netdev_priv(dev); int index = (erq->flags & IW_ENCODE_INDEX) - 1; u16 xlen = 0; - char keybuf[ORINOCO_MAX_KEY_SIZE]; unsigned long flags; if (! priv->has_wep) @@ -2898,51 +3009,47 @@ static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *er memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE); orinoco_unlock(priv, &flags); - - if (erq->pointer) { - if (copy_to_user(erq->pointer, keybuf, xlen)) - return -EFAULT; - } - return 0; } -static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_point *erq) +static int orinoco_ioctl_setessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, + char *essidbuf) { struct orinoco_private *priv = netdev_priv(dev); - char essidbuf[IW_ESSID_MAX_SIZE+1]; unsigned long flags; /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it * anyway... - Jean II */ - memset(&essidbuf, 0, sizeof(essidbuf)); - - if (erq->flags) { - /* iwconfig includes the NUL in the specified length */ - if (erq->length > IW_ESSID_MAX_SIZE+1) - return -E2BIG; - - if (copy_from_user(&essidbuf, erq->pointer, erq->length)) - return -EFAULT; - - essidbuf[IW_ESSID_MAX_SIZE] = '\0'; - } + /* Hum... Should not use Wireless Extension constant (may change), + * should use our own... - Jean II */ + if (erq->length > IW_ESSID_MAX_SIZE) + return -E2BIG; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; - memcpy(priv->desired_essid, essidbuf, sizeof(priv->desired_essid)); + /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */ + memset(priv->desired_essid, 0, sizeof(priv->desired_essid)); + + /* If not ANY, get the new ESSID */ + if (erq->flags) { + memcpy(priv->desired_essid, essidbuf, erq->length); + } orinoco_unlock(priv, &flags); - return 0; + return -EINPROGRESS; /* Call commit handler */ } -static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq) +static int orinoco_ioctl_getessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, + char *essidbuf) { struct orinoco_private *priv = netdev_priv(dev); - char essidbuf[IW_ESSID_MAX_SIZE+1]; int active; int err = 0; unsigned long flags; @@ -2956,51 +3063,46 @@ static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq) } else { if (orinoco_lock(priv, &flags) != 0) return -EBUSY; - memcpy(essidbuf, priv->desired_essid, sizeof(essidbuf)); + memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE + 1); orinoco_unlock(priv, &flags); } erq->flags = 1; erq->length = strlen(essidbuf) + 1; - if (erq->pointer) - if (copy_to_user(erq->pointer, essidbuf, erq->length)) - return -EFAULT; TRACE_EXIT(dev->name); return 0; } -static int orinoco_ioctl_setnick(struct net_device *dev, struct iw_point *nrq) +static int orinoco_ioctl_setnick(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *nrq, + char *nickbuf) { struct orinoco_private *priv = netdev_priv(dev); - char nickbuf[IW_ESSID_MAX_SIZE+1]; unsigned long flags; if (nrq->length > IW_ESSID_MAX_SIZE) return -E2BIG; - memset(nickbuf, 0, sizeof(nickbuf)); - - if (copy_from_user(nickbuf, nrq->pointer, nrq->length)) - return -EFAULT; - - nickbuf[nrq->length] = '\0'; - if (orinoco_lock(priv, &flags) != 0) return -EBUSY; - memcpy(priv->nick, nickbuf, sizeof(priv->nick)); + memset(priv->nick, 0, sizeof(priv->nick)); + memcpy(priv->nick, nickbuf, nrq->length); orinoco_unlock(priv, &flags); - return 0; + return -EINPROGRESS; /* Call commit handler */ } -static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_point *nrq) +static int orinoco_ioctl_getnick(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *nrq, + char *nickbuf) { struct orinoco_private *priv = netdev_priv(dev); - char nickbuf[IW_ESSID_MAX_SIZE+1]; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) @@ -3011,23 +3113,22 @@ static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_point *nrq) nrq->length = strlen(nickbuf)+1; - if (copy_to_user(nrq->pointer, nickbuf, sizeof(nickbuf))) - return -EFAULT; - return 0; } -static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq) +static int orinoco_ioctl_setfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *frq, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); int chan = -1; unsigned long flags; + int err = -EINPROGRESS; /* Call commit handler */ - /* We can only use this in Ad-Hoc demo mode to set the operating - * frequency, or in IBSS mode to set the frequency where the IBSS - * will be created - Jean II */ - if (priv->iw_mode != IW_MODE_ADHOC) - return -EOPNOTSUPP; + /* In infrastructure mode the AP sets the channel */ + if (priv->iw_mode == IW_MODE_INFRA) + return -EBUSY; if ( (frq->e == 0) && (frq->m <= 1000) ) { /* Setting by channel number */ @@ -3051,13 +3152,44 @@ static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq) if (orinoco_lock(priv, &flags) != 0) return -EBUSY; + priv->channel = chan; + if (priv->iw_mode == IW_MODE_MONITOR) { + /* Fast channel change - no commit if successful */ + hermes_t *hw = &priv->hw; + err = hermes_docmd_wait(hw, HERMES_CMD_TEST | + HERMES_TEST_SET_CHANNEL, + chan, NULL); + } orinoco_unlock(priv, &flags); + return err; +} + +static int orinoco_ioctl_getfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *frq, + char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + int tmp; + + /* Locking done in there */ + tmp = orinoco_hw_get_freq(priv); + if (tmp < 0) { + return tmp; + } + + frq->m = tmp; + frq->e = 1; + return 0; } -static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq) +static int orinoco_ioctl_getsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *srq, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; @@ -3083,7 +3215,10 @@ static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq) return 0; } -static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq) +static int orinoco_ioctl_setsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *srq, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); int val = srq->value; @@ -3100,10 +3235,13 @@ static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq) priv->ap_density = val; orinoco_unlock(priv, &flags); - return 0; + return -EINPROGRESS; /* Call commit handler */ } -static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq) +static int orinoco_ioctl_setrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); int val = rrq->value; @@ -3121,13 +3259,30 @@ static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq) priv->rts_thresh = val; orinoco_unlock(priv, &flags); + return -EINPROGRESS; /* Call commit handler */ +} + +static int orinoco_ioctl_getrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, + char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + + rrq->value = priv->rts_thresh; + rrq->disabled = (rrq->value == 2347); + rrq->fixed = 1; + return 0; } -static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq) +static int orinoco_ioctl_setfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *frq, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); - int err = 0; + int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; if (orinoco_lock(priv, &flags) != 0) @@ -3159,11 +3314,14 @@ static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq) return err; } -static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq) +static int orinoco_ioctl_getfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *frq, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; - int err = 0; + int err; u16 val; unsigned long flags; @@ -3196,10 +3354,12 @@ static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq) return err; } -static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *rrq) +static int orinoco_ioctl_setrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); - int err = 0; int ratemode = -1; int bitrate; /* 100s of kilobits */ int i; @@ -3235,10 +3395,13 @@ static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *rrq) priv->bitratemode = ratemode; orinoco_unlock(priv, &flags); - return err; + return -EINPROGRESS; } -static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *rrq) +static int orinoco_ioctl_getrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; @@ -3303,10 +3466,13 @@ static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *rrq) return err; } -static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq) +static int orinoco_ioctl_setpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *prq, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); - int err = 0; + int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; if (orinoco_lock(priv, &flags) != 0) @@ -3355,7 +3521,10 @@ static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq) return err; } -static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq) +static int orinoco_ioctl_getpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *prq, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; @@ -3403,7 +3572,10 @@ static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq) return err; } -static int orinoco_ioctl_getretry(struct net_device *dev, struct iw_param *rrq) +static int orinoco_ioctl_getretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; @@ -3454,10 +3626,38 @@ static int orinoco_ioctl_getretry(struct net_device *dev, struct iw_param *rrq) return err; } -static int orinoco_ioctl_setibssport(struct net_device *dev, struct iwreq *wrq) +static int orinoco_ioctl_reset(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); - int val = *( (int *) wrq->u.name ); + + if (! capable(CAP_NET_ADMIN)) + return -EPERM; + + if (info->cmd == (SIOCIWFIRSTPRIV + 0x1)) { + printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name); + + /* Firmware reset */ + orinoco_reset(dev); + } else { + printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name); + + schedule_work(&priv->reset_work); + } + + return 0; +} + +static int orinoco_ioctl_setibssport(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) + +{ + struct orinoco_private *priv = netdev_priv(dev); + int val = *( (int *) extra ); unsigned long flags; if (orinoco_lock(priv, &flags) != 0) @@ -3469,28 +3669,28 @@ static int orinoco_ioctl_setibssport(struct net_device *dev, struct iwreq *wrq) set_port_type(priv); orinoco_unlock(priv, &flags); - return 0; + return -EINPROGRESS; /* Call commit handler */ } -static int orinoco_ioctl_getibssport(struct net_device *dev, struct iwreq *wrq) +static int orinoco_ioctl_getibssport(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); - int *val = (int *)wrq->u.name; - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; + int *val = (int *) extra; *val = priv->ibss_port; - orinoco_unlock(priv, &flags); - return 0; } -static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq) +static int orinoco_ioctl_setport3(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); - int val = *( (int *) wrq->u.name ); + int val = *( (int *) extra ); int err = 0; unsigned long flags; @@ -3519,51 +3719,131 @@ static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq) err = -EINVAL; } - if (! err) + if (! err) { /* Actually update the mode we are using */ set_port_type(priv); + err = -EINPROGRESS; + } orinoco_unlock(priv, &flags); return err; } -static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq) +static int orinoco_ioctl_getport3(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + int *val = (int *) extra; + + *val = priv->prefer_port3; + return 0; +} + +static int orinoco_ioctl_setpreamble(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); - int *val = (int *)wrq->u.name; unsigned long flags; + int val; + + if (! priv->has_preamble) + return -EOPNOTSUPP; + + /* 802.11b has recently defined some short preamble. + * Basically, the Phy header has been reduced in size. + * This increase performance, especially at high rates + * (the preamble is transmitted at 1Mb/s), unfortunately + * this give compatibility troubles... - Jean II */ + val = *( (int *) extra ); if (orinoco_lock(priv, &flags) != 0) return -EBUSY; - *val = priv->prefer_port3; + if (val) + priv->preamble = 1; + else + priv->preamble = 0; + orinoco_unlock(priv, &flags); + + return -EINPROGRESS; /* Call commit handler */ +} + +static int orinoco_ioctl_getpreamble(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + int *val = (int *) extra; + + if (! priv->has_preamble) + return -EOPNOTSUPP; + + *val = priv->preamble; return 0; } +/* ioctl interface to hermes_read_ltv() + * To use with iwpriv, pass the RID as the token argument, e.g. + * iwpriv get_rid [0xfc00] + * At least Wireless Tools 25 is required to use iwpriv. + * For Wireless Tools 25 and 26 append "dummy" are the end. */ +static int orinoco_ioctl_getrid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, + char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + hermes_t *hw = &priv->hw; + int rid = data->flags; + u16 length; + int err; + unsigned long flags; + + /* It's a "get" function, but we don't want users to access the + * WEP key and other raw firmware data */ + if (! capable(CAP_NET_ADMIN)) + return -EPERM; + + if (rid < 0xfc00 || rid > 0xffff) + return -EINVAL; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + err = hermes_read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length, + extra); + if (err) + goto out; + + data->length = min_t(u16, HERMES_RECLEN_TO_BYTES(length), + MAX_RID_LEN); + + out: + orinoco_unlock(priv, &flags); + return err; +} + /* Spy is used for link quality/strength measurements in Ad-Hoc mode * Jean II */ -static int orinoco_ioctl_setspy(struct net_device *dev, struct iw_point *srq) +static int orinoco_ioctl_setspy(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *srq, + char *extra) + { struct orinoco_private *priv = netdev_priv(dev); - struct sockaddr address[IW_MAX_SPY]; + struct sockaddr *address = (struct sockaddr *) extra; int number = srq->length; int i; - int err = 0; unsigned long flags; - /* Check the number of addresses */ - if (number > IW_MAX_SPY) - return -E2BIG; - - /* Get the data in the driver */ - if (srq->pointer) { - if (copy_from_user(address, srq->pointer, - sizeof(struct sockaddr) * number)) - return -EFAULT; - } - /* Make sure nobody mess with the structure while we do */ if (orinoco_lock(priv, &flags) != 0) return -EBUSY; @@ -3587,14 +3867,17 @@ static int orinoco_ioctl_setspy(struct net_device *dev, struct iw_point *srq) /* Now, let the others play */ orinoco_unlock(priv, &flags); - return err; + /* Do NOT call commit handler */ + return 0; } -static int orinoco_ioctl_getspy(struct net_device *dev, struct iw_point *srq) +static int orinoco_ioctl_getspy(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *srq, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); - struct sockaddr address[IW_MAX_SPY]; - struct iw_quality spy_stat[IW_MAX_SPY]; + struct sockaddr *address = (struct sockaddr *) extra; int number; int i; unsigned long flags; @@ -3603,7 +3886,12 @@ static int orinoco_ioctl_getspy(struct net_device *dev, struct iw_point *srq) return -EBUSY; number = priv->spy_number; - if ((number > 0) && (srq->pointer)) { + /* Create address struct */ + for (i = 0; i < number; i++) { + memcpy(address[i].sa_data, priv->spy_address[i], ETH_ALEN); + address[i].sa_family = AF_UNIX; + } + if (number > 0) { /* Create address struct */ for (i = 0; i < number; i++) { memcpy(address[i].sa_data, priv->spy_address[i], @@ -3614,344 +3902,503 @@ static int orinoco_ioctl_getspy(struct net_device *dev, struct iw_point *srq) /* In theory, we should disable irqs while copying the stats * because the rx path might update it in the middle... * Bah, who care ? - Jean II */ - memcpy(&spy_stat, priv->spy_stat, - sizeof(struct iw_quality) * IW_MAX_SPY); - for (i=0; i < number; i++) - priv->spy_stat[i].updated = 0; + memcpy(extra + (sizeof(struct sockaddr) * number), + priv->spy_stat, sizeof(struct iw_quality) * number); } + /* Reset updated flags. */ + for (i = 0; i < number; i++) + priv->spy_stat[i].updated = 0; orinoco_unlock(priv, &flags); - /* Push stuff to user space */ srq->length = number; - if(copy_to_user(srq->pointer, address, - sizeof(struct sockaddr) * number)) - return -EFAULT; - if(copy_to_user(srq->pointer + (sizeof(struct sockaddr)*number), - &spy_stat, sizeof(struct iw_quality) * number)) - return -EFAULT; return 0; } -static int -orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +/* Trigger a scan (look for other cells in the vicinity */ +static int orinoco_ioctl_setscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *srq, + char *extra) { struct orinoco_private *priv = netdev_priv(dev); - struct iwreq *wrq = (struct iwreq *)rq; + hermes_t *hw = &priv->hw; int err = 0; - int tmp; - int changed = 0; unsigned long flags; - TRACE_ENTER(dev->name); + /* Note : you may have realised that, as this is a SET operation, + * this is priviledged and therefore a normal user can't + * perform scanning. + * This is not an error, while the device perform scanning, + * traffic doesn't flow, so it's a perfect DoS... + * Jean II */ - /* In theory, we could allow most of the the SET stuff to be - * done. In practice, the lapse of time at startup when the - * card is not ready is very short, so why bother... Note - * that netif_device_present is different from up/down - * (ifconfig), when the device is not yet up, it is usually - * already ready... Jean II */ - if (! netif_device_present(dev)) - return -ENODEV; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; - switch (cmd) { - case SIOCGIWNAME: - strcpy(wrq->u.name, "IEEE 802.11-DS"); - break; - - case SIOCGIWAP: - wrq->u.ap_addr.sa_family = ARPHRD_ETHER; - err = orinoco_hw_get_bssid(priv, wrq->u.ap_addr.sa_data); - break; + /* Scanning with port 0 disabled would fail */ + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } - case SIOCGIWRANGE: - err = orinoco_ioctl_getiwrange(dev, &wrq->u.data); - break; + /* In monitor mode, the scan results are always empty. + * Probe responses are passed to the driver as received + * frames and could be processed in software. */ + if (priv->iw_mode == IW_MODE_MONITOR) { + err = -EOPNOTSUPP; + goto out; + } - case SIOCSIWMODE: - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - switch (wrq->u.mode) { - case IW_MODE_ADHOC: - if (! (priv->has_ibss || priv->has_port3) ) - err = -EINVAL; - else { - priv->iw_mode = IW_MODE_ADHOC; - changed = 1; - } - break; + /* Note : because we don't lock out the irq handler, the way + * we access scan variables in priv is critical. + * o scan_inprogress : not touched by irq handler + * o scan_mode : not touched by irq handler + * o scan_result : irq is strict producer, non-irq is strict + * consumer. + * o scan_len : synchronised with scan_result + * Before modifying anything on those variables, please think hard ! + * Jean II */ - case IW_MODE_INFRA: - priv->iw_mode = IW_MODE_INFRA; - changed = 1; - break; + /* If there is still some left-over scan results, get rid of it */ + if (priv->scan_result != NULL) { + /* What's likely is that a client did crash or was killed + * between triggering the scan request and reading the + * results, so we need to reset everything. + * Some clients that are too slow may suffer from that... + * Jean II */ + kfree(priv->scan_result); + priv->scan_result = NULL; + } - default: - err = -EINVAL; - break; - } - set_port_type(priv); - orinoco_unlock(priv, &flags); - break; + /* Save flags */ + priv->scan_mode = srq->flags; - case SIOCGIWMODE: - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - wrq->u.mode = priv->iw_mode; - orinoco_unlock(priv, &flags); - break; + /* Always trigger scanning, even if it's in progress. + * This way, if the info frame get lost, we will recover somewhat + * gracefully - Jean II */ - case SIOCSIWENCODE: - err = orinoco_ioctl_setiwencode(dev, &wrq->u.encoding); - if (! err) - changed = 1; + if (priv->has_hostscan) { + switch (priv->firmware_type) { + case FIRMWARE_TYPE_SYMBOL: + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFHOSTSCAN_SYMBOL, + HERMES_HOSTSCAN_SYMBOL_ONCE | + HERMES_HOSTSCAN_SYMBOL_BCAST); + break; + case FIRMWARE_TYPE_INTERSIL: { + u16 req[3]; + + req[0] = cpu_to_le16(0x3fff); /* All channels */ + req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */ + req[2] = 0; /* Any ESSID */ + err = HERMES_WRITE_RECORD(hw, USER_BAP, + HERMES_RID_CNFHOSTSCAN, &req); + } break; + case FIRMWARE_TYPE_AGERE: + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFSCANSSID_AGERE, + 0); /* Any ESSID */ + if (err) + break; - case SIOCGIWENCODE: - if (! capable(CAP_NET_ADMIN)) { - err = -EPERM; + err = hermes_inquire(hw, HERMES_INQ_SCAN); break; } + } else + err = hermes_inquire(hw, HERMES_INQ_SCAN); - err = orinoco_ioctl_getiwencode(dev, &wrq->u.encoding); - break; - - case SIOCSIWESSID: - err = orinoco_ioctl_setessid(dev, &wrq->u.essid); - if (! err) - changed = 1; - break; + /* One more client */ + if (! err) + priv->scan_inprogress = 1; - case SIOCGIWESSID: - err = orinoco_ioctl_getessid(dev, &wrq->u.essid); - break; + out: + orinoco_unlock(priv, &flags); + return err; +} - case SIOCSIWNICKN: - err = orinoco_ioctl_setnick(dev, &wrq->u.data); - if (! err) - changed = 1; - break; +/* Translate scan data returned from the card to a card independant + * format that the Wireless Tools will understand - Jean II */ +static inline int orinoco_translate_scan(struct net_device *dev, + char *buffer, + char *scan, + int scan_len) +{ + struct orinoco_private *priv = netdev_priv(dev); + int offset; /* In the scan data */ + union hermes_scan_info *atom; + int atom_len; + u16 capabilities; + u16 channel; + struct iw_event iwe; /* Temporary buffer */ + char * current_ev = buffer; + char * end_buf = buffer + IW_SCAN_MAX_DATA; - case SIOCGIWNICKN: - err = orinoco_ioctl_getnick(dev, &wrq->u.data); + switch (priv->firmware_type) { + case FIRMWARE_TYPE_AGERE: + atom_len = sizeof(struct agere_scan_apinfo); + offset = 0; break; - - case SIOCGIWFREQ: - tmp = orinoco_hw_get_freq(priv); - if (tmp < 0) { - err = tmp; - } else { - wrq->u.freq.m = tmp; - wrq->u.freq.e = 1; - } + case FIRMWARE_TYPE_SYMBOL: + /* Lack of documentation necessitates this hack. + * Different firmwares have 68 or 76 byte long atoms. + * We try modulo first. If the length divides by both, + * we check what would be the channel in the second + * frame for a 68-byte atom. 76-byte atoms have 0 there. + * Valid channel cannot be 0. */ + if (scan_len % 76) + atom_len = 68; + else if (scan_len % 68) + atom_len = 76; + else if (scan_len >= 1292 && scan[68] == 0) + atom_len = 76; + else + atom_len = 68; + offset = 0; break; - - case SIOCSIWFREQ: - err = orinoco_ioctl_setfreq(dev, &wrq->u.freq); - if (! err) - changed = 1; + case FIRMWARE_TYPE_INTERSIL: + offset = 4; + if (priv->has_hostscan) + atom_len = scan[0] + (scan[1] << 8); + else + atom_len = offsetof(struct prism2_scan_apinfo, atim); break; + default: + return 0; + } - case SIOCGIWSENS: - err = orinoco_ioctl_getsens(dev, &wrq->u.sens); - break; + /* Check that we got an whole number of atoms */ + if ((scan_len - offset) % atom_len) { + printk(KERN_ERR "%s: Unexpected scan data length %d, " + "atom_len %d, offset %d\n", dev->name, scan_len, + atom_len, offset); + return 0; + } - case SIOCSIWSENS: - err = orinoco_ioctl_setsens(dev, &wrq->u.sens); - if (! err) - changed = 1; - break; + /* Read the entries one by one */ + for (; offset + atom_len <= scan_len; offset += atom_len) { + /* Get next atom */ + atom = (union hermes_scan_info *) (scan + offset); + + /* First entry *MUST* be the AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, atom->a.bssid, ETH_ALEN); + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); + + /* Other entries will be displayed in the order we give them */ + + /* Add the ESSID */ + iwe.u.data.length = le16_to_cpu(atom->a.essid_len); + if (iwe.u.data.length > 32) + iwe.u.data.length = 32; + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, atom->a.essid); + + /* Add mode */ + iwe.cmd = SIOCGIWMODE; + capabilities = le16_to_cpu(atom->a.capabilities); + if (capabilities & 0x3) { + if (capabilities & 0x1) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN); + } - case SIOCGIWRTS: - wrq->u.rts.value = priv->rts_thresh; - wrq->u.rts.disabled = (wrq->u.rts.value == 2347); - wrq->u.rts.fixed = 1; - break; + channel = atom->s.channel; + if ( (channel >= 1) && (channel <= NUM_CHANNELS) ) { + /* Add frequency */ + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = channel_frequency[channel-1] * 100000; + iwe.u.freq.e = 1; + current_ev = iwe_stream_add_event(current_ev, end_buf, + &iwe, IW_EV_FREQ_LEN); + } - case SIOCSIWRTS: - err = orinoco_ioctl_setrts(dev, &wrq->u.rts); - if (! err) - changed = 1; - break; + /* Add quality statistics */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.updated = 0x10; /* no link quality */ + iwe.u.qual.level = (__u8) le16_to_cpu(atom->a.level) - 0x95; + iwe.u.qual.noise = (__u8) le16_to_cpu(atom->a.noise) - 0x95; + /* Wireless tools prior to 27.pre22 will show link quality + * anyway, so we provide a reasonable value. */ + if (iwe.u.qual.level > iwe.u.qual.noise) + iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; + else + iwe.u.qual.qual = 0; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); - case SIOCSIWFRAG: - err = orinoco_ioctl_setfrag(dev, &wrq->u.frag); - if (! err) - changed = 1; - break; + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if (capabilities & 0x10) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, atom->a.essid); + + /* Bit rate is not available in Lucent/Agere firmwares */ + if (priv->firmware_type != FIRMWARE_TYPE_AGERE) { + char * current_val = current_ev + IW_EV_LCP_LEN; + int i; + int step; + + if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL) + step = 2; + else + step = 1; + + iwe.cmd = SIOCGIWRATE; + /* Those two flags are ignored... */ + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + /* Max 10 values */ + for (i = 0; i < 10; i += step) { + /* NULL terminated */ + if (atom->p.rates[i] == 0x0) + break; + /* Bit rate given in 500 kb/s units (+ 0x80) */ + iwe.u.bitrate.value = ((atom->p.rates[i] & 0x7f) * 500000); + current_val = iwe_stream_add_value(current_ev, current_val, + end_buf, &iwe, + IW_EV_PARAM_LEN); + } + /* Check if we added any event */ + if ((current_val - current_ev) > IW_EV_LCP_LEN) + current_ev = current_val; + } - case SIOCGIWFRAG: - err = orinoco_ioctl_getfrag(dev, &wrq->u.frag); - break; + /* The other data in the scan result are not really + * interesting, so for now drop it - Jean II */ + } + return current_ev - buffer; +} - case SIOCSIWRATE: - err = orinoco_ioctl_setrate(dev, &wrq->u.bitrate); - if (! err) - changed = 1; - break; +/* Return results of a scan */ +static int orinoco_ioctl_getscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *srq, + char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + int err = 0; + unsigned long flags; - case SIOCGIWRATE: - err = orinoco_ioctl_getrate(dev, &wrq->u.bitrate); - break; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; - case SIOCSIWPOWER: - err = orinoco_ioctl_setpower(dev, &wrq->u.power); - if (! err) - changed = 1; - break; + /* If no results yet, ask to try again later */ + if (priv->scan_result == NULL) { + if (priv->scan_inprogress) + /* Important note : we don't want to block the caller + * until results are ready for various reasons. + * First, managing wait queues is complex and racy. + * Second, we grab some rtnetlink lock before comming + * here (in dev_ioctl()). + * Third, we generate an Wireless Event, so the + * caller can wait itself on that - Jean II */ + err = -EAGAIN; + else + /* Client error, no scan results... + * The caller need to restart the scan. */ + err = -ENODATA; + } else { + /* We have some results to push back to user space */ + + /* Translate to WE format */ + srq->length = orinoco_translate_scan(dev, extra, + priv->scan_result, + priv->scan_len); + + /* Return flags */ + srq->flags = (__u16) priv->scan_mode; + + /* Results are here, so scan no longer in progress */ + priv->scan_inprogress = 0; + + /* In any case, Scan results will be cleaned up in the + * reset function and when exiting the driver. + * The person triggering the scanning may never come to + * pick the results, so we need to do it in those places. + * Jean II */ + +#ifdef SCAN_SINGLE_READ + /* If you enable this option, only one client (the first + * one) will be able to read the result (and only one + * time). If there is multiple concurent clients that + * want to read scan results, this behavior is not + * advisable - Jean II */ + kfree(priv->scan_result); + priv->scan_result = NULL; +#endif /* SCAN_SINGLE_READ */ + /* Here, if too much time has elapsed since last scan, + * we may want to clean up scan results... - Jean II */ + } + + orinoco_unlock(priv, &flags); + return err; +} - case SIOCGIWPOWER: - err = orinoco_ioctl_getpower(dev, &wrq->u.power); - break; +/* Commit handler, called after set operations */ +static int orinoco_ioctl_commit(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + struct hermes *hw = &priv->hw; + unsigned long flags; + int err = 0; - case SIOCGIWTXPOW: - /* The card only supports one tx power, so this is easy */ - wrq->u.txpower.value = 15; /* dBm */ - wrq->u.txpower.fixed = 1; - wrq->u.txpower.disabled = 0; - wrq->u.txpower.flags = IW_TXPOW_DBM; - break; + if (!priv->open) + return 0; - case SIOCSIWRETRY: - err = -EOPNOTSUPP; - break; + if (priv->broken_disableport) { + orinoco_reset(dev); + return 0; + } - case SIOCGIWRETRY: - err = orinoco_ioctl_getretry(dev, &wrq->u.retry); - break; + if (orinoco_lock(priv, &flags) != 0) + return err; - case SIOCSIWSPY: - err = orinoco_ioctl_setspy(dev, &wrq->u.data); - break; + err = hermes_disable_port(hw, 0); + if (err) { + printk(KERN_WARNING "%s: Unable to disable port " + "while reconfiguring card\n", dev->name); + priv->broken_disableport = 1; + goto out; + } - case SIOCGIWSPY: - err = orinoco_ioctl_getspy(dev, &wrq->u.data); - break; + err = __orinoco_program_rids(dev); + if (err) { + printk(KERN_WARNING "%s: Unable to reconfigure card\n", + dev->name); + goto out; + } - case SIOCGIWPRIV: - if (wrq->u.data.pointer) { - struct iw_priv_args privtab[] = { - { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" }, - { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" }, - { SIOCIWFIRSTPRIV + 0x2, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - 0, "set_port3" }, - { SIOCIWFIRSTPRIV + 0x3, 0, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - "get_port3" }, - { SIOCIWFIRSTPRIV + 0x4, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - 0, "set_preamble" }, - { SIOCIWFIRSTPRIV + 0x5, 0, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - "get_preamble" }, - { SIOCIWFIRSTPRIV + 0x6, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - 0, "set_ibssport" }, - { SIOCIWFIRSTPRIV + 0x7, 0, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - "get_ibssport" }, - }; - - wrq->u.data.length = sizeof(privtab) / sizeof(privtab[0]); - if (copy_to_user(wrq->u.data.pointer, privtab, sizeof(privtab))) - err = -EFAULT; - } - break; - - case SIOCIWFIRSTPRIV + 0x0: /* force_reset */ - case SIOCIWFIRSTPRIV + 0x1: /* card_reset */ - if (! capable(CAP_NET_ADMIN)) { - err = -EPERM; - break; - } - - printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name); + err = hermes_enable_port(hw, 0); + if (err) { + printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", + dev->name); + goto out; + } + out: + if (err) { + printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); schedule_work(&priv->reset_work); - break; - - case SIOCIWFIRSTPRIV + 0x2: /* set_port3 */ - if (! capable(CAP_NET_ADMIN)) { - err = -EPERM; - break; - } - - err = orinoco_ioctl_setport3(dev, wrq); - if (! err) - changed = 1; - break; + err = 0; + } - case SIOCIWFIRSTPRIV + 0x3: /* get_port3 */ - err = orinoco_ioctl_getport3(dev, wrq); - break; + orinoco_unlock(priv, &flags); + return err; +} - case SIOCIWFIRSTPRIV + 0x4: /* set_preamble */ - if (! capable(CAP_NET_ADMIN)) { - err = -EPERM; - break; - } +static const struct iw_priv_args orinoco_privtab[] = { + { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" }, + { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" }, + { SIOCIWFIRSTPRIV + 0x2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_port3" }, + { SIOCIWFIRSTPRIV + 0x3, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_port3" }, + { SIOCIWFIRSTPRIV + 0x4, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_preamble" }, + { SIOCIWFIRSTPRIV + 0x5, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_preamble" }, + { SIOCIWFIRSTPRIV + 0x6, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_ibssport" }, + { SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_ibssport" }, + { SIOCIWFIRSTPRIV + 0x9, 0, IW_PRIV_TYPE_BYTE | MAX_RID_LEN, + "get_rid" }, +}; - /* 802.11b has recently defined some short preamble. - * Basically, the Phy header has been reduced in size. - * This increase performance, especially at high rates - * (the preamble is transmitted at 1Mb/s), unfortunately - * this give compatibility troubles... - Jean II */ - if(priv->has_preamble) { - int val = *( (int *) wrq->u.name ); - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - if (val) - priv->preamble = 1; - else - priv->preamble = 0; - orinoco_unlock(priv, &flags); - changed = 1; - } else - err = -EOPNOTSUPP; - break; - case SIOCIWFIRSTPRIV + 0x5: /* get_preamble */ - if(priv->has_preamble) { - int *val = (int *)wrq->u.name; +/* + * Structures to export the Wireless Handlers + */ - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - *val = priv->preamble; - orinoco_unlock(priv, &flags); - } else - err = -EOPNOTSUPP; - break; - case SIOCIWFIRSTPRIV + 0x6: /* set_ibssport */ - if (! capable(CAP_NET_ADMIN)) { - err = -EPERM; - break; - } +static const iw_handler orinoco_handler[] = { + [SIOCSIWCOMMIT-SIOCIWFIRST] (iw_handler) orinoco_ioctl_commit, + [SIOCGIWNAME -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getname, + [SIOCSIWFREQ -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setfreq, + [SIOCGIWFREQ -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getfreq, + [SIOCSIWMODE -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setmode, + [SIOCGIWMODE -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getmode, + [SIOCSIWSENS -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setsens, + [SIOCGIWSENS -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getsens, + [SIOCGIWRANGE -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getiwrange, + [SIOCSIWSPY -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setspy, + [SIOCGIWSPY -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getspy, + [SIOCSIWAP -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setwap, + [SIOCGIWAP -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getwap, + [SIOCSIWSCAN -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setscan, + [SIOCGIWSCAN -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getscan, + [SIOCSIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setessid, + [SIOCGIWESSID -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getessid, + [SIOCSIWNICKN -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setnick, + [SIOCGIWNICKN -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getnick, + [SIOCSIWRATE -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setrate, + [SIOCGIWRATE -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getrate, + [SIOCSIWRTS -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setrts, + [SIOCGIWRTS -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getrts, + [SIOCSIWFRAG -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setfrag, + [SIOCGIWFRAG -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getfrag, + [SIOCGIWRETRY -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getretry, + [SIOCSIWENCODE-SIOCIWFIRST] (iw_handler) orinoco_ioctl_setiwencode, + [SIOCGIWENCODE-SIOCIWFIRST] (iw_handler) orinoco_ioctl_getiwencode, + [SIOCSIWPOWER -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setpower, + [SIOCGIWPOWER -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getpower, +}; - err = orinoco_ioctl_setibssport(dev, wrq); - if (! err) - changed = 1; - break; - case SIOCIWFIRSTPRIV + 0x7: /* get_ibssport */ - err = orinoco_ioctl_getibssport(dev, wrq); - break; +/* + Added typecasting since we no longer use iwreq_data -- Moustafa + */ +static const iw_handler orinoco_private_handler[] = { + [0] (iw_handler) orinoco_ioctl_reset, + [1] (iw_handler) orinoco_ioctl_reset, + [2] (iw_handler) orinoco_ioctl_setport3, + [3] (iw_handler) orinoco_ioctl_getport3, + [4] (iw_handler) orinoco_ioctl_setpreamble, + [5] (iw_handler) orinoco_ioctl_getpreamble, + [6] (iw_handler) orinoco_ioctl_setibssport, + [7] (iw_handler) orinoco_ioctl_getibssport, + [9] (iw_handler) orinoco_ioctl_getrid, +}; - default: - err = -EOPNOTSUPP; - } - - if (! err && changed && netif_running(dev)) { - err = orinoco_reconfigure(dev); - } +static const struct iw_handler_def orinoco_handler_def = { + .num_standard = ARRAY_SIZE(orinoco_handler), + .num_private = ARRAY_SIZE(orinoco_private_handler), + .num_private_args = ARRAY_SIZE(orinoco_privtab), + .standard = orinoco_handler, + .private = orinoco_private_handler, + .private_args = orinoco_privtab, +}; - TRACE_EXIT(dev->name); +static void orinoco_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct orinoco_private *priv = netdev_priv(dev); - return err; + strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1); + strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 1); + strncpy(info->fw_version, priv->fw_name, sizeof(info->fw_version) - 1); + if (dev->class_dev.dev) + strncpy(info->bus_info, dev->class_dev.dev->bus_id, + sizeof(info->bus_info) - 1); + else + snprintf(info->bus_info, sizeof(info->bus_info) - 1, + "PCMCIA %p", priv->hw.iobase); } +static struct ethtool_ops orinoco_ethtool_ops = { + .get_drvinfo = orinoco_get_drvinfo, + .get_link = ethtool_op_get_link, +}; /********************************************************************/ /* Debugging */ diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h index f749b50..2f213a7 100644 --- a/drivers/net/wireless/orinoco.h +++ b/drivers/net/wireless/orinoco.h @@ -7,7 +7,7 @@ #ifndef _ORINOCO_H #define _ORINOCO_H -#define DRIVER_VERSION "0.14alpha2" +#define DRIVER_VERSION "0.15rc2" #include <linux/types.h> #include <linux/spinlock.h> @@ -22,6 +22,8 @@ #define WIRELESS_SPY // enable iwspy support +#define MAX_SCAN_LEN 4096 + #define ORINOCO_MAX_KEY_SIZE 14 #define ORINOCO_MAX_KEYS 4 @@ -30,6 +32,20 @@ struct orinoco_key { char data[ORINOCO_MAX_KEY_SIZE]; } __attribute__ ((packed)); +struct header_struct { + /* 802.3 */ + u8 dest[ETH_ALEN]; + u8 src[ETH_ALEN]; + u16 len; + /* 802.2 */ + u8 dsap; + u8 ssap; + u8 ctrl; + /* SNAP */ + u8 oui[3]; + u16 ethertype; +} __attribute__ ((packed)); + typedef enum { FIRMWARE_TYPE_AGERE, FIRMWARE_TYPE_INTERSIL, @@ -48,6 +64,8 @@ struct orinoco_private { /* driver state */ int open; u16 last_linkstatus; + struct work_struct join_work; + struct work_struct wevent_work; /* Net device stuff */ struct net_device *ndev; @@ -74,7 +92,9 @@ struct orinoco_private { unsigned int has_pm:1; unsigned int has_preamble:1; unsigned int has_sensitivity:1; + unsigned int has_hostscan:1; unsigned int broken_disableport:1; + unsigned int broken_monitor:1; /* Configuration paramaters */ u32 iw_mode; @@ -84,6 +104,8 @@ struct orinoco_private { int bitratemode; char nick[IW_ESSID_MAX_SIZE+1]; char desired_essid[IW_ESSID_MAX_SIZE+1]; + char desired_bssid[ETH_ALEN]; + int bssid_fixed; u16 frag_thresh, mwo_robust; u16 channel; u16 ap_density, rts_thresh; @@ -98,6 +120,12 @@ struct orinoco_private { /* Configuration dependent variables */ int port_type, createibss; int promiscuous, mc_count; + + /* Scanning support */ + int scan_inprogress; /* Scan pending... */ + u32 scan_mode; /* Type of scan done */ + char * scan_result; /* Result of previous scan */ + int scan_len; /* Lenght of result */ }; #ifdef ORINOCO_DEBUG diff --git a/drivers/net/wireless/prism54/isl_38xx.c b/drivers/net/wireless/prism54/isl_38xx.c index 4481ec1..adc7499 100644 --- a/drivers/net/wireless/prism54/isl_38xx.c +++ b/drivers/net/wireless/prism54/isl_38xx.c @@ -112,10 +112,10 @@ isl38xx_handle_wakeup(isl38xx_control_block *control_block, void isl38xx_trigger_device(int asleep, void __iomem *device_base) { - struct timeval current_time; u32 reg, counter = 0; #if VERBOSE > SHOW_ERROR_MESSAGES + struct timeval current_time; DEBUG(SHOW_FUNCTION_CALLS, "isl38xx trigger device\n"); #endif @@ -126,11 +126,11 @@ isl38xx_trigger_device(int asleep, void __iomem *device_base) do_gettimeofday(¤t_time); DEBUG(SHOW_TRACING, "%08li.%08li Device wakeup triggered\n", current_time.tv_sec, (long)current_time.tv_usec); -#endif DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n", current_time.tv_sec, (long)current_time.tv_usec, readl(device_base + ISL38XX_CTRL_STAT_REG)); +#endif udelay(ISL38XX_WRITEIO_DELAY); reg = readl(device_base + ISL38XX_INT_IDENT_REG); @@ -148,10 +148,12 @@ isl38xx_trigger_device(int asleep, void __iomem *device_base) counter++; } +#if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n", current_time.tv_sec, (long)current_time.tv_usec, readl(device_base + ISL38XX_CTRL_STAT_REG)); +#endif udelay(ISL38XX_WRITEIO_DELAY); #if VERBOSE > SHOW_ERROR_MESSAGES diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index 7a4adc4..794fb55 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c @@ -1176,8 +1176,12 @@ unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf, n_sectors = ata_id_u32(args->id, 60); n_sectors--; /* ATA TotalUserSectors - 1 */ - tmp = n_sectors; /* note: truncates, if lba48 */ if (args->cmd->cmnd[0] == READ_CAPACITY) { + if( n_sectors >= 0xffffffffULL ) + tmp = 0xffffffff ; /* Return max count on overflow */ + else + tmp = n_sectors ; + /* sector count, 32-bit */ rbuf[0] = tmp >> (8 * 3); rbuf[1] = tmp >> (8 * 2); @@ -1191,10 +1195,12 @@ unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf, } else { /* sector count, 64-bit */ - rbuf[2] = n_sectors >> (8 * 7); - rbuf[3] = n_sectors >> (8 * 6); - rbuf[4] = n_sectors >> (8 * 5); - rbuf[5] = n_sectors >> (8 * 4); + tmp = n_sectors >> (8 * 4); + rbuf[2] = tmp >> (8 * 3); + rbuf[3] = tmp >> (8 * 2); + rbuf[4] = tmp >> (8 * 1); + rbuf[5] = tmp; + tmp = n_sectors; rbuf[6] = tmp >> (8 * 3); rbuf[7] = tmp >> (8 * 2); rbuf[8] = tmp >> (8 * 1); diff --git a/drivers/serial/68328serial.c b/drivers/serial/68328serial.c index feb8e73..d27fb4c 100644 --- a/drivers/serial/68328serial.c +++ b/drivers/serial/68328serial.c @@ -1497,23 +1497,6 @@ rs68328_init(void) return 0; } - - -/* - * register_serial and unregister_serial allows for serial ports to be - * configured at run-time, to support PCMCIA modems. - */ -/* SPARC: Unused at this time, just here to make things link. */ -int register_serial(struct serial_struct *req) -{ - return -1; -} - -void unregister_serial(int line) -{ - return; -} - module_init(rs68328_init); diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index d8b9d2b..34e75bc 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -77,23 +77,9 @@ static unsigned int share_irqs = SERIAL8250_SHARE_IRQS; */ #define is_real_interrupt(irq) ((irq) != 0) -/* - * This converts from our new CONFIG_ symbols to the symbols - * that asm/serial.h expects. You _NEED_ to comment out the - * linux/config.h include contained inside asm/serial.h for - * this to work. - */ -#undef CONFIG_SERIAL_MANY_PORTS -#undef CONFIG_SERIAL_DETECT_IRQ -#undef CONFIG_SERIAL_MULTIPORT -#undef CONFIG_HUB6 - #ifdef CONFIG_SERIAL_8250_DETECT_IRQ #define CONFIG_SERIAL_DETECT_IRQ 1 #endif -#ifdef CONFIG_SERIAL_8250_MULTIPORT -#define CONFIG_SERIAL_MULTIPORT 1 -#endif #ifdef CONFIG_SERIAL_8250_MANY_PORTS #define CONFIG_SERIAL_MANY_PORTS 1 #endif @@ -2323,10 +2309,11 @@ static int __devinit serial8250_probe(struct device *dev) { struct plat_serial8250_port *p = dev->platform_data; struct uart_port port; + int ret, i; memset(&port, 0, sizeof(struct uart_port)); - for (; p && p->flags != 0; p++) { + for (i = 0; p && p->flags != 0; p++, i++) { port.iobase = p->iobase; port.membase = p->membase; port.irq = p->irq; @@ -2335,10 +2322,16 @@ static int __devinit serial8250_probe(struct device *dev) port.iotype = p->iotype; port.flags = p->flags; port.mapbase = p->mapbase; + port.hub6 = p->hub6; port.dev = dev; if (share_irqs) port.flags |= UPF_SHARE_IRQ; - serial8250_register_port(&port); + ret = serial8250_register_port(&port); + if (ret < 0) { + dev_err(dev, "unable to register port at index %d " + "(IO%lx MEM%lx IRQ%d): %d\n", i, + p->iobase, p->mapbase, p->irq, ret); + } } return 0; } diff --git a/drivers/serial/8250_accent.c b/drivers/serial/8250_accent.c new file mode 100644 index 0000000..1f2c276 --- /dev/null +++ b/drivers/serial/8250_accent.c @@ -0,0 +1,47 @@ +/* + * linux/drivers/serial/8250_accent.c + * + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/serial_8250.h> + +#define PORT(_base,_irq) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = UPF_BOOT_AUTOCONF, \ + } + +static struct plat_serial8250_port accent_data[] = { + PORT(0x330, 4), + PORT(0x338, 4), + { }, +}; + +static struct platform_device accent_device = { + .name = "serial8250", + .id = 2, + .dev = { + .platform_data = accent_data, + }, +}; + +static int __init accent_init(void) +{ + return platform_device_register(&accent_device); +} + +module_init(accent_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for Accent Async cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/8250_boca.c b/drivers/serial/8250_boca.c new file mode 100644 index 0000000..465c9ea --- /dev/null +++ b/drivers/serial/8250_boca.c @@ -0,0 +1,61 @@ +/* + * linux/drivers/serial/8250_boca.c + * + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/serial_8250.h> + +#define PORT(_base,_irq) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = UPF_BOOT_AUTOCONF, \ + } + +static struct plat_serial8250_port boca_data[] = { + PORT(0x100, 12), + PORT(0x108, 12), + PORT(0x110, 12), + PORT(0x118, 12), + PORT(0x120, 12), + PORT(0x128, 12), + PORT(0x130, 12), + PORT(0x138, 12), + PORT(0x140, 12), + PORT(0x148, 12), + PORT(0x150, 12), + PORT(0x158, 12), + PORT(0x160, 12), + PORT(0x168, 12), + PORT(0x170, 12), + PORT(0x178, 12), + { }, +}; + +static struct platform_device boca_device = { + .name = "serial8250", + .id = 3, + .dev = { + .platform_data = boca_data, + }, +}; + +static int __init boca_init(void) +{ + return platform_device_register(&boca_device); +} + +module_init(boca_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for Boca cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/8250_fourport.c b/drivers/serial/8250_fourport.c new file mode 100644 index 0000000..e9b4d90 --- /dev/null +++ b/drivers/serial/8250_fourport.c @@ -0,0 +1,53 @@ +/* + * linux/drivers/serial/8250_fourport.c + * + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/serial_8250.h> + +#define PORT(_base,_irq) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = UPF_BOOT_AUTOCONF | UPF_FOURPORT, \ + } + +static struct plat_serial8250_port fourport_data[] = { + PORT(0x1a0, 9), + PORT(0x1a8, 9), + PORT(0x1b0, 9), + PORT(0x1b8, 9), + PORT(0x2a0, 5), + PORT(0x2a8, 5), + PORT(0x2b0, 5), + PORT(0x2b8, 5), + { }, +}; + +static struct platform_device fourport_device = { + .name = "serial8250", + .id = 1, + .dev = { + .platform_data = fourport_data, + }, +}; + +static int __init fourport_init(void) +{ + return platform_device_register(&fourport_device); +} + +module_init(fourport_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for AST Fourport cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/8250_hub6.c b/drivers/serial/8250_hub6.c new file mode 100644 index 0000000..77f396f --- /dev/null +++ b/drivers/serial/8250_hub6.c @@ -0,0 +1,58 @@ +/* + * linux/drivers/serial/8250_hub6.c + * + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/serial_8250.h> + +#define HUB6(card,port) \ + { \ + .iobase = 0x302, \ + .irq = 3, \ + .uartclk = 1843200, \ + .iotype = UPIO_HUB6, \ + .flags = UPF_BOOT_AUTOCONF, \ + .hub6 = (card) << 6 | (port) << 3 | 1, \ + } + +static struct plat_serial8250_port hub6_data[] = { + HUB6(0,0), + HUB6(0,1), + HUB6(0,2), + HUB6(0,3), + HUB6(0,4), + HUB6(0,5), + HUB6(1,0), + HUB6(1,1), + HUB6(1,2), + HUB6(1,3), + HUB6(1,4), + HUB6(1,5), + { }, +}; + +static struct platform_device hub6_device = { + .name = "serial8250", + .id = 4, + .dev = { + .platform_data = hub6_data, + }, +}; + +static int __init hub6_init(void) +{ + return platform_device_register(&hub6_device); +} + +module_init(hub6_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for Hub6 cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/8250_mca.c b/drivers/serial/8250_mca.c new file mode 100644 index 0000000..f0c40d6 --- /dev/null +++ b/drivers/serial/8250_mca.c @@ -0,0 +1,64 @@ +/* + * linux/drivers/serial/8250_mca.c + * + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/mca.h> +#include <linux/serial_8250.h> + +/* + * FIXME: Should we be doing AUTO_IRQ here? + */ +#ifdef CONFIG_SERIAL_8250_DETECT_IRQ +#define MCA_FLAGS UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ +#else +#define MCA_FLAGS UPF_BOOT_AUTOCONF | UPF_SKIP_TEST +#endif + +#define PORT(_base,_irq) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = MCA_FLAGS, \ + } + +static struct plat_serial8250_port mca_data[] = { + PORT(0x3220, 3), + PORT(0x3228, 3), + PORT(0x4220, 3), + PORT(0x4228, 3), + PORT(0x5220, 3), + PORT(0x5228, 3), + { }, +}; + +static struct platform_device mca_device = { + .name = "serial8250", + .id = 5, + .dev = { + .platform_data = mca_data, + }, +}; + +static int __init mca_init(void) +{ + if (!MCA_bus) + return -ENODEV; + return platform_device_register(&mca_device); +} + +module_init(mca_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for MCA ports"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 25fcef2..e879bce 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -89,11 +89,11 @@ config SERIAL_8250_NR_UARTS int "Maximum number of non-legacy 8250/16550 serial ports" depends on SERIAL_8250 default "4" - ---help--- - Set this to the number of non-legacy serial ports you want - the driver to support. This includes any ports discovered - via ACPI or PCI enumeration and any ports that may be added - at run-time via hot-plug. + help + Set this to the number of serial ports you want the driver + to support. This includes any ports discovered via ACPI or + PCI enumeration and any ports that may be added at run-time + via hot-plug, or any ISA multi-port serial cards. config SERIAL_8250_EXTENDED bool "Extended 8250/16550 serial driver options" @@ -141,31 +141,74 @@ config SERIAL_8250_DETECT_IRQ If unsure, say N. -config SERIAL_8250_MULTIPORT - bool "Support special multiport boards" - depends on SERIAL_8250_EXTENDED - help - Some multiport serial ports have special ports which are used to - signal when there are any serial ports on the board which need - servicing. Say Y here to enable the serial driver to take advantage - of those special I/O ports. - config SERIAL_8250_RSA bool "Support RSA serial ports" depends on SERIAL_8250_EXTENDED help ::: To be written ::: -comment "Non-8250 serial port support" +# +# Multi-port serial cards +# + +config SERIAL_8250_FOURPORT + tristate "Support Fourport cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have an AST FourPort serial board. + + To compile this driver as a module, choose M here: the module + will be called 8250_fourport. + +config SERIAL_8250_ACCENT + tristate "Support Accent cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have an Accent Async serial board. + + To compile this driver as a module, choose M here: the module + will be called 8250_accent. + + +config SERIAL_8250_BOCA + tristate "Support Boca cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have a Boca serial board. Please read the Boca + mini-HOWTO, avaialble from <http://www.tldp.org/docs.html#howto> + + To compile this driver as a module, choose M here: the module + will be called 8250_boca. + + +config SERIAL_8250_HUB6 + tristate "Support Hub6 cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have a HUB6 serial board. + + To compile this driver as a module, choose M here: the module + will be called 8250_hub6. + +config SERIAL_8250_MCA + tristate "Support 8250-type ports on MCA buses" + depends on SERIAL_8250 != n && MCA + help + Say Y here if you have a MCA serial ports. + + To compile this driver as a module, choose M here: the module + will be called 8250_mca. config SERIAL_8250_ACORN tristate "Acorn expansion card serial port support" - depends on ARM && ARCH_ACORN && SERIAL_8250 + depends on ARCH_ACORN && SERIAL_8250 help If you have an Atomwide Serial card or Serial Port card for an Acorn system, say Y to this option. The driver can handle 1, 2, or 3 port cards. If unsure, say N. +comment "Non-8250 serial port support" + config SERIAL_AMBA_PL010 tristate "ARM AMBA PL010 serial port support" depends on ARM_AMBA diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 8f1cdde..65bd438 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -17,6 +17,11 @@ obj-$(CONFIG_SERIAL_8250) += 8250.o $(serial-8250-y) obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o +obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o +obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o +obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o +obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o +obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index a61d443..d79cd21 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB_MON) += mon/ obj-$(CONFIG_USB_EHCI_HCD) += host/ +obj-$(CONFIG_USB_ISP116X_HCD) += host/ obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_SL811_HCD) += host/ @@ -31,6 +32,7 @@ obj-$(CONFIG_USB_MOUSE) += input/ obj-$(CONFIG_USB_MTOUCH) += input/ obj-$(CONFIG_USB_POWERMATE) += input/ obj-$(CONFIG_USB_WACOM) += input/ +obj-$(CONFIG_USB_ACECAD) += input/ obj-$(CONFIG_USB_XPAD) += input/ obj-$(CONFIG_USB_DABUSB) += media/ diff --git a/drivers/usb/atm/Kconfig b/drivers/usb/atm/Kconfig index 0d9f537..f429862 100644 --- a/drivers/usb/atm/Kconfig +++ b/drivers/usb/atm/Kconfig @@ -1,30 +1,60 @@ # -# USB ATM driver configuration +# USB/ATM DSL configuration # -comment "USB ATM/DSL drivers" + +menu "USB DSL modem support" depends on USB config USB_ATM - tristate "Generic USB ATM/DSL core I/O support" + tristate "USB DSL modem support" depends on USB && ATM select CRC32 default n help - This provides a library which is used for packet I/O by USB DSL - modems, such as the SpeedTouch driver below. + Say Y here if you want to connect a USB Digital Subscriber Line (DSL) + modem to your computer's USB port. You will then need to choose your + modem from the list below. To compile this driver as a module, choose M here: the - module will be called usb_atm. + module will be called usbatm. config USB_SPEEDTOUCH - tristate "Alcatel Speedtouch USB support" - depends on USB && ATM - select USB_ATM + tristate "Speedtouch USB support" + depends on USB_ATM + select FW_LOADER help - Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330 + Say Y here if you have an SpeedTouch USB or SpeedTouch 330 modem. In order to use your modem you will need to install the two parts of the firmware, extracted by the user space tools; see <http://www.linux-usb.org/SpeedTouch/> for details. To compile this driver as a module, choose M here: the module will be called speedtch. + +config USB_CXACRU + tristate "Conexant AccessRunner USB support" + depends on USB_ATM + select FW_LOADER + help + Say Y here if you have an ADSL USB modem based on the Conexant + AccessRunner chipset. In order to use your modem you will need to + install the firmware, extracted by the user space tools; see + <http://accessrunner.sourceforge.net/> for details. + + To compile this driver as a module, choose M here: the + module will be called cxacru. + +config USB_XUSBATM + tristate "Other USB DSL modem support" + depends on USB_ATM + help + Say Y here if you have a DSL USB modem not explicitly supported by + another USB DSL drivers. In order to use your modem you will need to + pass the vendor ID, product ID, and endpoint numbers for transmission + and reception as module parameters. You may need to initialize the + the modem using a user space utility (a firmware loader for example). + + To compile this driver as a module, choose M here: the + module will be called xusbatm. + +endmenu diff --git a/drivers/usb/atm/Makefile b/drivers/usb/atm/Makefile index 9213b8b9..751f297 100644 --- a/drivers/usb/atm/Makefile +++ b/drivers/usb/atm/Makefile @@ -1,7 +1,8 @@ # -# Makefile for the rest of the USB drivers -# (the ones that don't fit into any other categories) +# Makefile for USB ATM/xDSL drivers # -obj-$(CONFIG_USB_ATM) += usb_atm.o +obj-$(CONFIG_USB_CXACRU) += cxacru.o obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o +obj-$(CONFIG_USB_ATM) += usbatm.o +obj-$(CONFIG_USB_XUSBATM) += xusbatm.o diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c new file mode 100644 index 0000000..cbd4a7d --- /dev/null +++ b/drivers/usb/atm/cxacru.c @@ -0,0 +1,878 @@ +/****************************************************************************** + * cxacru.c - driver for USB ADSL modems based on + * Conexant AccessRunner chipset + * + * Copyright (C) 2004 David Woodhouse, Duncan Sands, Roman Kagan + * Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru) + * + * 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. + * + ******************************************************************************/ + +/* + * Credit is due for Josep Comas, who created the original patch to speedtch.c + * to support the different padding used by the AccessRunner (now generalized + * into usbatm), and the userspace firmware loading utility. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/device.h> /* FIXME: linux/firmware.h should include it itself */ +#include <linux/firmware.h> + +#include "usbatm.h" + +#define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands" +#define DRIVER_VERSION "0.2" +#define DRIVER_DESC "Conexant AccessRunner ADSL USB modem driver" + +static const char cxacru_driver_name[] = "cxacru"; + +#define CXACRU_EP_CMD 0x01 /* Bulk/interrupt in/out */ +#define CXACRU_EP_DATA 0x02 /* Bulk in/out */ + +#define CMD_PACKET_SIZE 64 /* Should be maxpacket(ep)? */ + +/* Addresses */ +#define PLLFCLK_ADDR 0x00350068 +#define PLLBCLK_ADDR 0x0035006c +#define SDRAMEN_ADDR 0x00350010 +#define FW_ADDR 0x00801000 +#define BR_ADDR 0x00180600 +#define SIG_ADDR 0x00180500 +#define BR_STACK_ADDR 0x00187f10 + +/* Values */ +#define SDRAM_ENA 0x1 + +#define CMD_TIMEOUT 2000 /* msecs */ +#define POLL_INTERVAL 5000 /* msecs */ + +/* commands for interaction with the modem through the control channel before + * firmware is loaded */ +enum cxacru_fw_request { + FW_CMD_ERR, + FW_GET_VER, + FW_READ_MEM, + FW_WRITE_MEM, + FW_RMW_MEM, + FW_CHECKSUM_MEM, + FW_GOTO_MEM, +}; + +/* commands for interaction with the modem through the control channel once + * firmware is loaded */ +enum cxacru_cm_request { + CM_REQUEST_UNDEFINED = 0x80, + CM_REQUEST_TEST, + CM_REQUEST_CHIP_GET_MAC_ADDRESS, + CM_REQUEST_CHIP_GET_DP_VERSIONS, + CM_REQUEST_CHIP_ADSL_LINE_START, + CM_REQUEST_CHIP_ADSL_LINE_STOP, + CM_REQUEST_CHIP_ADSL_LINE_GET_STATUS, + CM_REQUEST_CHIP_ADSL_LINE_GET_SPEED, + CM_REQUEST_CARD_INFO_GET, + CM_REQUEST_CARD_DATA_GET, + CM_REQUEST_CARD_DATA_SET, + CM_REQUEST_COMMAND_HW_IO, + CM_REQUEST_INTERFACE_HW_IO, + CM_REQUEST_CARD_SERIAL_DATA_PATH_GET, + CM_REQUEST_CARD_SERIAL_DATA_PATH_SET, + CM_REQUEST_CARD_CONTROLLER_VERSION_GET, + CM_REQUEST_CARD_GET_STATUS, + CM_REQUEST_CARD_GET_MAC_ADDRESS, + CM_REQUEST_CARD_GET_DATA_LINK_STATUS, + CM_REQUEST_MAX, +}; + +/* reply codes to the commands above */ +enum cxacru_cm_status { + CM_STATUS_UNDEFINED, + CM_STATUS_SUCCESS, + CM_STATUS_ERROR, + CM_STATUS_UNSUPPORTED, + CM_STATUS_UNIMPLEMENTED, + CM_STATUS_PARAMETER_ERROR, + CM_STATUS_DBG_LOOPBACK, + CM_STATUS_MAX, +}; + +/* indices into CARD_INFO_GET return array */ +enum cxacru_info_idx { + CXINF_DOWNSTREAM_RATE, + CXINF_UPSTREAM_RATE, + CXINF_LINK_STATUS, + CXINF_LINE_STATUS, + CXINF_MAC_ADDRESS_HIGH, + CXINF_MAC_ADDRESS_LOW, + CXINF_UPSTREAM_SNR_MARGIN, + CXINF_DOWNSTREAM_SNR_MARGIN, + CXINF_UPSTREAM_ATTENUATION, + CXINF_DOWNSTREAM_ATTENUATION, + CXINF_TRANSMITTER_POWER, + CXINF_UPSTREAM_BITS_PER_FRAME, + CXINF_DOWNSTREAM_BITS_PER_FRAME, + CXINF_STARTUP_ATTEMPTS, + CXINF_UPSTREAM_CRC_ERRORS, + CXINF_DOWNSTREAM_CRC_ERRORS, + CXINF_UPSTREAM_FEC_ERRORS, + CXINF_DOWNSTREAM_FEC_ERRORS, + CXINF_UPSTREAM_HEC_ERRORS, + CXINF_DOWNSTREAM_HEC_ERRORS, + CXINF_LINE_STARTABLE, + CXINF_MODULATION, + CXINF_ADSL_HEADEND, + CXINF_ADSL_HEADEND_ENVIRONMENT, + CXINF_CONTROLLER_VERSION, + /* dunno what the missing two mean */ + CXINF_MAX = 0x1c, +}; + +struct cxacru_modem_type { + u32 pll_f_clk; + u32 pll_b_clk; + int boot_rom_patch; +}; + +struct cxacru_data { + struct usbatm_data *usbatm; + + const struct cxacru_modem_type *modem_type; + + int line_status; + struct work_struct poll_work; + + /* contol handles */ + struct semaphore cm_serialize; + u8 *rcv_buf; + u8 *snd_buf; + struct urb *rcv_urb; + struct urb *snd_urb; + struct completion rcv_done; + struct completion snd_done; +}; + +/* the following three functions are stolen from drivers/usb/core/message.c */ +static void cxacru_blocking_completion(struct urb *urb, struct pt_regs *regs) +{ + complete((struct completion *)urb->context); +} + +static void cxacru_timeout_kill(unsigned long data) +{ + usb_unlink_urb((struct urb *) data); +} + +static int cxacru_start_wait_urb(struct urb *urb, struct completion *done, + int* actual_length) +{ + struct timer_list timer; + int status; + + init_timer(&timer); + timer.expires = jiffies + msecs_to_jiffies(CMD_TIMEOUT); + timer.data = (unsigned long) urb; + timer.function = cxacru_timeout_kill; + add_timer(&timer); + wait_for_completion(done); + status = urb->status; + if (status == -ECONNRESET) + status = -ETIMEDOUT; + del_timer_sync(&timer); + + if (actual_length) + *actual_length = urb->actual_length; + return status; +} + +static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm, + u8 *wdata, int wsize, u8 *rdata, int rsize) +{ + int ret, actlen; + int offb, offd; + const int stride = CMD_PACKET_SIZE - 4; + u8 *wbuf = instance->snd_buf; + u8 *rbuf = instance->rcv_buf; + int wbuflen = ((wsize - 1) / stride + 1) * CMD_PACKET_SIZE; + int rbuflen = ((rsize - 1) / stride + 1) * CMD_PACKET_SIZE; + + if (wbuflen > PAGE_SIZE || rbuflen > PAGE_SIZE) { + dbg("too big transfer requested"); + ret = -ENOMEM; + goto fail; + } + + down(&instance->cm_serialize); + + /* submit reading urb before the writing one */ + init_completion(&instance->rcv_done); + ret = usb_submit_urb(instance->rcv_urb, GFP_KERNEL); + if (ret < 0) { + dbg("submitting read urb for cm %#x failed", cm); + ret = ret; + goto fail; + } + + memset(wbuf, 0, wbuflen); + /* handle wsize == 0 */ + wbuf[0] = cm; + for (offb = offd = 0; offd < wsize; offd += stride, offb += CMD_PACKET_SIZE) { + wbuf[offb] = cm; + memcpy(wbuf + offb + 4, wdata + offd, min_t(int, stride, wsize - offd)); + } + + instance->snd_urb->transfer_buffer_length = wbuflen; + init_completion(&instance->snd_done); + ret = usb_submit_urb(instance->snd_urb, GFP_KERNEL); + if (ret < 0) { + dbg("submitting write urb for cm %#x failed", cm); + ret = ret; + goto fail; + } + + ret = cxacru_start_wait_urb(instance->snd_urb, &instance->snd_done, NULL); + if (ret < 0) { + dbg("sending cm %#x failed", cm); + ret = ret; + goto fail; + } + + ret = cxacru_start_wait_urb(instance->rcv_urb, &instance->rcv_done, &actlen); + if (ret < 0) { + dbg("receiving cm %#x failed", cm); + ret = ret; + goto fail; + } + if (actlen % CMD_PACKET_SIZE || !actlen) { + dbg("response is not a positive multiple of %d: %#x", + CMD_PACKET_SIZE, actlen); + ret = -EIO; + goto fail; + } + + /* check the return status and copy the data to the output buffer, if needed */ + for (offb = offd = 0; offd < rsize && offb < actlen; offb += CMD_PACKET_SIZE) { + if (rbuf[offb] != cm) { + dbg("wrong cm %#x in response", rbuf[offb]); + ret = -EIO; + goto fail; + } + if (rbuf[offb + 1] != CM_STATUS_SUCCESS) { + dbg("response failed: %#x", rbuf[offb + 1]); + ret = -EIO; + goto fail; + } + if (offd >= rsize) + break; + memcpy(rdata + offd, rbuf + offb + 4, min_t(int, stride, rsize - offd)); + offd += stride; + } + + ret = offd; + dbg("cm %#x", cm); +fail: + up(&instance->cm_serialize); + return ret; +} + +static int cxacru_cm_get_array(struct cxacru_data *instance, enum cxacru_cm_request cm, + u32 *data, int size) +{ + int ret, len; + u32 *buf; + int offb, offd; + const int stride = CMD_PACKET_SIZE / (4 * 2) - 1; + int buflen = ((size - 1) / stride + 1 + size * 2) * 4; + + buf = kmalloc(buflen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = cxacru_cm(instance, cm, NULL, 0, (u8 *) buf, buflen); + if (ret < 0) + goto cleanup; + + /* len > 0 && len % 4 == 0 guaranteed by cxacru_cm() */ + len = ret / 4; + for (offb = 0; offb < len; ) { + int l = le32_to_cpu(buf[offb++]); + if (l > stride || l > (len - offb) / 2) { + dbg("wrong data length %#x in response", l); + ret = -EIO; + goto cleanup; + } + while (l--) { + offd = le32_to_cpu(buf[offb++]); + if (offd >= size) { + dbg("wrong index %#x in response", offd); + ret = -EIO; + goto cleanup; + } + data[offd] = le32_to_cpu(buf[offb++]); + } + } + + ret = 0; + +cleanup: + kfree(buf); + return ret; +} + +static int cxacru_card_status(struct cxacru_data *instance) +{ + int ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_STATUS, NULL, 0, NULL, 0); + if (ret < 0) { /* firmware not loaded */ + dbg("cxacru_adsl_start: CARD_GET_STATUS returned %d", ret); + return ret; + } + return 0; +} + +static void cxacru_poll_status(struct cxacru_data *instance); + +static int cxacru_atm_start(struct usbatm_data *usbatm_instance, + struct atm_dev *atm_dev) +{ + struct cxacru_data *instance = usbatm_instance->driver_data; + struct device *dev = &usbatm_instance->usb_intf->dev; + /* + struct atm_dev *atm_dev = usbatm_instance->atm_dev; + */ + int ret; + + dbg("cxacru_atm_start"); + + /* Read MAC address */ + ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_MAC_ADDRESS, NULL, 0, + atm_dev->esi, sizeof(atm_dev->esi)); + if (ret < 0) { + dev_err(dev, "cxacru_atm_start: CARD_GET_MAC_ADDRESS returned %d\n", ret); + return ret; + } + + /* start ADSL */ + ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0); + if (ret < 0) { + dev_err(dev, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret); + return ret; + } + + /* Start status polling */ + cxacru_poll_status(instance); + return 0; +} + +static void cxacru_poll_status(struct cxacru_data *instance) +{ + u32 buf[CXINF_MAX] = {}; + struct device *dev = &instance->usbatm->usb_intf->dev; + struct atm_dev *atm_dev = instance->usbatm->atm_dev; + int ret; + + ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX); + if (ret < 0) { + dev_warn(dev, "poll status: error %d\n", ret); + goto reschedule; + } + + if (instance->line_status == buf[CXINF_LINE_STATUS]) + goto reschedule; + + instance->line_status = buf[CXINF_LINE_STATUS]; + switch (instance->line_status) { + case 0: + atm_dev->signal = ATM_PHY_SIG_LOST; + dev_info(dev, "ADSL line: down\n"); + break; + + case 1: + atm_dev->signal = ATM_PHY_SIG_LOST; + dev_info(dev, "ADSL line: attemtping to activate\n"); + break; + + case 2: + atm_dev->signal = ATM_PHY_SIG_LOST; + dev_info(dev, "ADSL line: training\n"); + break; + + case 3: + atm_dev->signal = ATM_PHY_SIG_LOST; + dev_info(dev, "ADSL line: channel analysis\n"); + break; + + case 4: + atm_dev->signal = ATM_PHY_SIG_LOST; + dev_info(dev, "ADSL line: exchange\n"); + break; + + case 5: + atm_dev->link_rate = buf[CXINF_DOWNSTREAM_RATE] * 1000 / 424; + atm_dev->signal = ATM_PHY_SIG_FOUND; + + dev_info(dev, "ADSL line: up (%d Kib/s down | %d Kib/s up)\n", + buf[CXINF_DOWNSTREAM_RATE], buf[CXINF_UPSTREAM_RATE]); + break; + + case 6: + atm_dev->signal = ATM_PHY_SIG_LOST; + dev_info(dev, "ADSL line: waiting\n"); + break; + + case 7: + atm_dev->signal = ATM_PHY_SIG_LOST; + dev_info(dev, "ADSL line: initializing\n"); + break; + + default: + atm_dev->signal = ATM_PHY_SIG_UNKNOWN; + dev_info(dev, "Unknown line state %02x\n", instance->line_status); + break; + } +reschedule: + schedule_delayed_work(&instance->poll_work, msecs_to_jiffies(POLL_INTERVAL)); +} + +static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw, + u8 code1, u8 code2, u32 addr, u8 *data, int size) +{ + int ret; + u8 *buf; + int offd, offb; + const int stride = CMD_PACKET_SIZE - 8; + + buf = (u8 *) __get_free_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + + offb = offd = 0; + do { + int l = min_t(int, stride, size - offd); + buf[offb++] = fw; + buf[offb++] = l; + buf[offb++] = code1; + buf[offb++] = code2; + *((u32 *) (buf + offb)) = cpu_to_le32(addr); + offb += 4; + addr += l; + if(l) + memcpy(buf + offb, data + offd, l); + if (l < stride) + memset(buf + offb + l, 0, stride - l); + offb += stride; + offd += stride; + if ((offb >= PAGE_SIZE) || (offd >= size)) { + ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_CMD), + buf, offb, NULL, CMD_TIMEOUT); + if (ret < 0) { + dbg("sending fw %#x failed", fw); + goto cleanup; + } + offb = 0; + } + } while(offd < size); + dbg("sent fw %#x", fw); + + ret = 0; + +cleanup: + free_page((unsigned long) buf); + return ret; +} + +static void cxacru_upload_firmware(struct cxacru_data *instance, + const struct firmware *fw, + const struct firmware *bp, + const struct firmware *cf) +{ + int ret; + int off; + struct usb_device *usb_dev = instance->usbatm->usb_dev; + struct device *dev = &instance->usbatm->usb_intf->dev; + u16 signature[] = { usb_dev->descriptor.idVendor, usb_dev->descriptor.idProduct }; + u32 val; + + dbg("cxacru_upload_firmware"); + + /* FirmwarePllFClkValue */ + val = cpu_to_le32(instance->modem_type->pll_f_clk); + ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLFCLK_ADDR, (u8 *) &val, 4); + if (ret) { + dev_err(dev, "FirmwarePllFClkValue failed: %d\n", ret); + return; + } + + /* FirmwarePllBClkValue */ + val = cpu_to_le32(instance->modem_type->pll_b_clk); + ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLBCLK_ADDR, (u8 *) &val, 4); + if (ret) { + dev_err(dev, "FirmwarePllBClkValue failed: %d\n", ret); + return; + } + + /* Enable SDRAM */ + val = cpu_to_le32(SDRAM_ENA); + ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SDRAMEN_ADDR, (u8 *) &val, 4); + if (ret) { + dev_err(dev, "Enable SDRAM failed: %d\n", ret); + return; + } + + /* Firmware */ + ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, FW_ADDR, fw->data, fw->size); + if (ret) { + dev_err(dev, "Firmware upload failed: %d\n", ret); + return; + } + + /* Boot ROM patch */ + if (instance->modem_type->boot_rom_patch) { + ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_ADDR, bp->data, bp->size); + if (ret) { + dev_err(dev, "Boot ROM patching failed: %d\n", ret); + return; + } + } + + /* Signature */ + ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SIG_ADDR, (u8 *) signature, 4); + if (ret) { + dev_err(dev, "Signature storing failed: %d\n", ret); + return; + } + + if (instance->modem_type->boot_rom_patch) { + val = cpu_to_le32(BR_ADDR); + ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_STACK_ADDR, (u8 *) &val, 4); + } + else { + ret = cxacru_fw(usb_dev, FW_GOTO_MEM, 0x0, 0x0, FW_ADDR, NULL, 0); + } + if (ret) { + dev_err(dev, "Passing control to firmware failed: %d\n", ret); + return; + } + + /* Delay to allow firmware to start up. */ + msleep_interruptible(1000); + + usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_CMD)); + usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, CXACRU_EP_CMD)); + usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_DATA)); + usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, CXACRU_EP_DATA)); + + ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_STATUS, NULL, 0, NULL, 0); + if (ret < 0) { + dev_err(dev, "modem failed to initialize: %d\n", ret); + return; + } + + /* Load config data (le32), doing one packet at a time */ + if (cf) + for (off = 0; off < cf->size / 4; ) { + u32 buf[CMD_PACKET_SIZE / 4 - 1]; + int i, len = min_t(int, cf->size / 4 - off, CMD_PACKET_SIZE / 4 / 2 - 1); + buf[0] = cpu_to_le32(len); + for (i = 0; i < len; i++, off++) { + buf[i * 2 + 1] = cpu_to_le32(off); + memcpy(buf + i * 2 + 2, cf->data + off * 4, 4); + } + ret = cxacru_cm(instance, CM_REQUEST_CARD_DATA_SET, + (u8 *) buf, len, NULL, 0); + if (ret < 0) { + dev_err(dev, "load config data failed: %d\n", ret); + return; + } + } + + msleep_interruptible(4000); +} + +static int cxacru_find_firmware(struct cxacru_data *instance, + char* phase, const struct firmware **fw_p) +{ + struct device *dev = &instance->usbatm->usb_intf->dev; + char buf[16]; + + sprintf(buf, "cxacru-%s.bin", phase); + dbg("cxacru_find_firmware: looking for %s", buf); + + if (request_firmware(fw_p, buf, dev)) { + dev_dbg(dev, "no stage %s firmware found\n", phase); + return -ENOENT; + } + + dev_info(dev, "found firmware %s\n", buf); + + return 0; +} + +static int cxacru_heavy_init(struct usbatm_data *usbatm_instance, + struct usb_interface *usb_intf) +{ + struct device *dev = &usbatm_instance->usb_intf->dev; + const struct firmware *fw, *bp, *cf; + struct cxacru_data *instance = usbatm_instance->driver_data; + + int ret = cxacru_find_firmware(instance, "fw", &fw); + if (ret) { + dev_warn(dev, "firmware (cxacru-fw.bin) unavailable (hotplug misconfiguration?)\n"); + return ret; + } + + if (instance->modem_type->boot_rom_patch) { + ret = cxacru_find_firmware(instance, "bp", &bp); + if (ret) { + dev_warn(dev, "boot ROM patch (cxacru-bp.bin) unavailable (hotplug misconfiguration?)\n"); + release_firmware(fw); + return ret; + } + } + + if (cxacru_find_firmware(instance, "cf", &cf)) /* optional */ + cf = NULL; + + cxacru_upload_firmware(instance, fw, bp, cf); + + if (cf) + release_firmware(cf); + if (instance->modem_type->boot_rom_patch) + release_firmware(bp); + release_firmware(fw); + + ret = cxacru_card_status(instance); + if (ret) + dbg("modem initialisation failed"); + else + dbg("done setting up the modem"); + + return ret; +} + +static int cxacru_bind(struct usbatm_data *usbatm_instance, + struct usb_interface *intf, const struct usb_device_id *id, + int *need_heavy_init) +{ + struct cxacru_data *instance; + struct usb_device *usb_dev = interface_to_usbdev(intf); + int ret; + + /* instance init */ + instance = kmalloc(sizeof(*instance), GFP_KERNEL); + if (!instance) { + dbg("cxacru_bind: no memory for instance data"); + return -ENOMEM; + } + + memset(instance, 0, sizeof(*instance)); + + instance->usbatm = usbatm_instance; + instance->modem_type = (struct cxacru_modem_type *) id->driver_info; + + instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL); + if (!instance->rcv_buf) { + dbg("cxacru_bind: no memory for rcv_buf"); + ret = -ENOMEM; + goto fail; + } + instance->snd_buf = (u8 *) __get_free_page(GFP_KERNEL); + if (!instance->snd_buf) { + dbg("cxacru_bind: no memory for snd_buf"); + ret = -ENOMEM; + goto fail; + } + instance->rcv_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!instance->rcv_urb) { + dbg("cxacru_bind: no memory for rcv_urb"); + ret = -ENOMEM; + goto fail; + } + instance->snd_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!instance->snd_urb) { + dbg("cxacru_bind: no memory for snd_urb"); + ret = -ENOMEM; + goto fail; + } + + usb_fill_int_urb(instance->rcv_urb, + usb_dev, usb_rcvintpipe(usb_dev, CXACRU_EP_CMD), + instance->rcv_buf, PAGE_SIZE, + cxacru_blocking_completion, &instance->rcv_done, 1); + instance->rcv_urb->transfer_flags |= URB_ASYNC_UNLINK; + + usb_fill_int_urb(instance->snd_urb, + usb_dev, usb_sndintpipe(usb_dev, CXACRU_EP_CMD), + instance->snd_buf, PAGE_SIZE, + cxacru_blocking_completion, &instance->snd_done, 4); + instance->snd_urb->transfer_flags |= URB_ASYNC_UNLINK; + + init_MUTEX(&instance->cm_serialize); + + INIT_WORK(&instance->poll_work, (void *)cxacru_poll_status, instance); + + usbatm_instance->driver_data = instance; + + *need_heavy_init = cxacru_card_status(instance); + + return 0; + + fail: + free_page((unsigned long) instance->snd_buf); + free_page((unsigned long) instance->rcv_buf); + usb_free_urb(instance->snd_urb); + usb_free_urb(instance->rcv_urb); + kfree(instance); + + return ret; +} + +static void cxacru_unbind(struct usbatm_data *usbatm_instance, + struct usb_interface *intf) +{ + struct cxacru_data *instance = usbatm_instance->driver_data; + + dbg("cxacru_unbind entered"); + + if (!instance) { + dbg("cxacru_unbind: NULL instance!"); + return; + } + + while (!cancel_delayed_work(&instance->poll_work)) + flush_scheduled_work(); + + usb_kill_urb(instance->snd_urb); + usb_kill_urb(instance->rcv_urb); + usb_free_urb(instance->snd_urb); + usb_free_urb(instance->rcv_urb); + + free_page((unsigned long) instance->snd_buf); + free_page((unsigned long) instance->rcv_buf); + kfree(instance); + + usbatm_instance->driver_data = NULL; +} + +static const struct cxacru_modem_type cxacru_cafe = { + .pll_f_clk = 0x02d874df, + .pll_b_clk = 0x0196a51a, + .boot_rom_patch = 1, +}; + +static const struct cxacru_modem_type cxacru_cb00 = { + .pll_f_clk = 0x5, + .pll_b_clk = 0x3, + .boot_rom_patch = 0, +}; + +static const struct usb_device_id cxacru_usb_ids[] = { + { /* V = Conexant P = ADSL modem (Euphrates project) */ + USB_DEVICE(0x0572, 0xcafe), .driver_info = (unsigned long) &cxacru_cafe + }, + { /* V = Conexant P = ADSL modem (Hasbani project) */ + USB_DEVICE(0x0572, 0xcb00), .driver_info = (unsigned long) &cxacru_cb00 + }, + { /* V = Conexant P = ADSL modem */ + USB_DEVICE(0x0572, 0xcb01), .driver_info = (unsigned long) &cxacru_cb00 + }, + { /* V = Conexant P = ADSL modem */ + USB_DEVICE(0x0572, 0xcb06), .driver_info = (unsigned long) &cxacru_cb00 + }, + { /* V = Olitec P = ADSL modem version 2 */ + USB_DEVICE(0x08e3, 0x0100), .driver_info = (unsigned long) &cxacru_cafe + }, + { /* V = Olitec P = ADSL modem version 3 */ + USB_DEVICE(0x08e3, 0x0102), .driver_info = (unsigned long) &cxacru_cb00 + }, + { /* V = Trust/Amigo Technology Co. P = AMX-CA86U */ + USB_DEVICE(0x0eb0, 0x3457), .driver_info = (unsigned long) &cxacru_cafe + }, + { /* V = Zoom P = 5510 */ + USB_DEVICE(0x1803, 0x5510), .driver_info = (unsigned long) &cxacru_cb00 + }, + { /* V = Draytek P = Vigor 318 */ + USB_DEVICE(0x0675, 0x0200), .driver_info = (unsigned long) &cxacru_cb00 + }, + { /* V = Zyxel P = 630-C1 aka OMNI ADSL USB (Annex A) */ + USB_DEVICE(0x0586, 0x330a), .driver_info = (unsigned long) &cxacru_cb00 + }, + { /* V = Zyxel P = 630-C3 aka OMNI ADSL USB (Annex B) */ + USB_DEVICE(0x0586, 0x330b), .driver_info = (unsigned long) &cxacru_cb00 + }, + { /* V = Aethra P = Starmodem UM1020 */ + USB_DEVICE(0x0659, 0x0020), .driver_info = (unsigned long) &cxacru_cb00 + }, + { /* V = Aztech Systems P = ? AKA Pirelli AUA-010 */ + USB_DEVICE(0x0509, 0x0812), .driver_info = (unsigned long) &cxacru_cb00 + }, + { /* V = Netopia P = Cayman 3341(Annex A)/3351(Annex B) */ + USB_DEVICE(0x100d, 0xcb01), .driver_info = (unsigned long) &cxacru_cb00 + }, + { /* V = Netopia P = Cayman 3342(Annex A)/3352(Annex B) */ + USB_DEVICE(0x100d, 0x3342), .driver_info = (unsigned long) &cxacru_cb00 + }, + {} +}; + +MODULE_DEVICE_TABLE(usb, cxacru_usb_ids); + +static struct usbatm_driver cxacru_driver = { + .owner = THIS_MODULE, + .driver_name = cxacru_driver_name, + .bind = cxacru_bind, + .heavy_init = cxacru_heavy_init, + .unbind = cxacru_unbind, + .atm_start = cxacru_atm_start, + .in = CXACRU_EP_DATA, + .out = CXACRU_EP_DATA, + .rx_padding = 3, + .tx_padding = 11, +}; + +static int cxacru_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + return usbatm_usb_probe(intf, id, &cxacru_driver); +} + +static struct usb_driver cxacru_usb_driver = { + .owner = THIS_MODULE, + .name = cxacru_driver_name, + .probe = cxacru_usb_probe, + .disconnect = usbatm_usb_disconnect, + .id_table = cxacru_usb_ids +}; + +static int __init cxacru_init(void) +{ + return usb_register(&cxacru_usb_driver); +} + +static void __exit cxacru_cleanup(void) +{ + usb_deregister(&cxacru_usb_driver); +} + +module_init(cxacru_init); +module_exit(cxacru_cleanup); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 2a1697b..6a6eaa2 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -5,6 +5,8 @@ * Copyright (C) 2003, Duncan Sands * Copyright (C) 2004, David Woodhouse * + * Based on "modem_run.c", copyright (C) 2001, Benoit Papillault + * * 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) @@ -21,821 +23,798 @@ * ******************************************************************************/ -#include <linux/module.h> -#include <linux/moduleparam.h> +#include <asm/page.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/firmware.h> #include <linux/gfp.h> +#include <linux/init.h> #include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/errno.h> -#include <linux/proc_fs.h> +#include <linux/module.h> +#include <linux/moduleparam.h> #include <linux/slab.h> -#include <linux/wait.h> -#include <linux/list.h> -#include <asm/processor.h> -#include <asm/uaccess.h> -#include <linux/smp_lock.h> -#include <linux/interrupt.h> -#include <linux/atm.h> -#include <linux/atmdev.h> -#include <linux/crc32.h> -#include <linux/init.h> -#include <linux/firmware.h> - -#include "usb_atm.h" +#include <linux/stat.h> +#include <linux/timer.h> +#include <linux/workqueue.h> -#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) -# define USE_FW_LOADER -#endif +#include "usbatm.h" #define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <duncan.sands@free.fr>" -#define DRIVER_VERSION "1.8" +#define DRIVER_VERSION "1.9" #define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION static const char speedtch_driver_name[] = "speedtch"; -#define SPEEDTOUCH_VENDORID 0x06b9 -#define SPEEDTOUCH_PRODUCTID 0x4061 +#define CTRL_TIMEOUT 2000 /* milliseconds */ +#define DATA_TIMEOUT 2000 /* milliseconds */ -/* Timeout in jiffies */ -#define CTRL_TIMEOUT 2000 -#define DATA_TIMEOUT 2000 +#define OFFSET_7 0 /* size 1 */ +#define OFFSET_b 1 /* size 8 */ +#define OFFSET_d 9 /* size 4 */ +#define OFFSET_e 13 /* size 1 */ +#define OFFSET_f 14 /* size 1 */ +#define TOTAL 15 -#define OFFSET_7 0 /* size 1 */ -#define OFFSET_b 1 /* size 8 */ -#define OFFSET_d 9 /* size 4 */ -#define OFFSET_e 13 /* size 1 */ -#define OFFSET_f 14 /* size 1 */ -#define TOTAL 15 +#define SIZE_7 1 +#define SIZE_b 8 +#define SIZE_d 4 +#define SIZE_e 1 +#define SIZE_f 1 -#define SIZE_7 1 -#define SIZE_b 8 -#define SIZE_d 4 -#define SIZE_e 1 -#define SIZE_f 1 +#define MIN_POLL_DELAY 5000 /* milliseconds */ +#define MAX_POLL_DELAY 60000 /* milliseconds */ -static int dl_512_first = 0; -static int sw_buffering = 0; +#define RESUBMIT_DELAY 1000 /* milliseconds */ -module_param(dl_512_first, bool, 0444); -MODULE_PARM_DESC(dl_512_first, "Read 512 bytes before sending firmware"); +#define DEFAULT_ALTSETTING 1 +#define DEFAULT_DL_512_FIRST 0 +#define DEFAULT_SW_BUFFERING 0 -module_param(sw_buffering, uint, 0444); -MODULE_PARM_DESC(sw_buffering, "Enable software buffering"); +static int altsetting = DEFAULT_ALTSETTING; +static int dl_512_first = DEFAULT_DL_512_FIRST; +static int sw_buffering = DEFAULT_SW_BUFFERING; -#define UDSL_IOCTL_LINE_UP 1 -#define UDSL_IOCTL_LINE_DOWN 2 +module_param(altsetting, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(altsetting, + "Alternative setting for data interface (default: " + __MODULE_STRING(DEFAULT_ALTSETTING) ")"); -#define SPEEDTCH_ENDPOINT_INT 0x81 -#define SPEEDTCH_ENDPOINT_DATA 0x07 -#define SPEEDTCH_ENDPOINT_FIRMWARE 0x05 +module_param(dl_512_first, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(dl_512_first, + "Read 512 bytes before sending firmware (default: " + __MODULE_STRING(DEFAULT_DL_512_FIRST) ")"); -#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) ) +module_param(sw_buffering, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(sw_buffering, + "Enable software buffering (default: " + __MODULE_STRING(DEFAULT_SW_BUFFERING) ")"); -static struct usb_device_id speedtch_usb_ids[] = { - {USB_DEVICE(SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID)}, - {} -}; +#define ENDPOINT_INT 0x81 +#define ENDPOINT_DATA 0x07 +#define ENDPOINT_FIRMWARE 0x05 -MODULE_DEVICE_TABLE(usb, speedtch_usb_ids); +#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) ) struct speedtch_instance_data { - struct udsl_instance_data u; + struct usbatm_data *usbatm; + + struct work_struct status_checker; - /* Status */ + int poll_delay; /* milliseconds */ + + struct timer_list resubmit_timer; struct urb *int_urb; unsigned char int_data[16]; - struct work_struct poll_work; - struct timer_list poll_timer; -}; -/* USB */ - -static int speedtch_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id); -static void speedtch_usb_disconnect(struct usb_interface *intf); -static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code, - void *user_data); -static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs); -static void speedtch_poll_status(struct speedtch_instance_data *instance); -static struct usb_driver speedtch_usb_driver = { - .owner = THIS_MODULE, - .name = speedtch_driver_name, - .probe = speedtch_usb_probe, - .disconnect = speedtch_usb_disconnect, - .ioctl = speedtch_usb_ioctl, - .id_table = speedtch_usb_ids, + unsigned char scratch_buffer[TOTAL]; }; /*************** ** firmware ** ***************/ -static void speedtch_got_firmware(struct speedtch_instance_data *instance, - int got_it) +static void speedtch_set_swbuff(struct speedtch_instance_data *instance, int state) { - int err; - struct usb_interface *intf; - - down(&instance->u.serialize); /* vs self, speedtch_firmware_start */ - if (instance->u.status == UDSL_LOADED_FIRMWARE) - goto out; - if (!got_it) { - instance->u.status = UDSL_NO_FIRMWARE; - goto out; - } - if ((err = usb_set_interface(instance->u.usb_dev, 1, 1)) < 0) { - dbg("speedtch_got_firmware: usb_set_interface returned %d!", err); - instance->u.status = UDSL_NO_FIRMWARE; - goto out; - } - - /* Set up interrupt endpoint */ - intf = usb_ifnum_to_if(instance->u.usb_dev, 0); - if (intf && !usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) { - - instance->int_urb = usb_alloc_urb(0, GFP_KERNEL); - if (instance->int_urb) { - - usb_fill_int_urb(instance->int_urb, instance->u.usb_dev, - usb_rcvintpipe(instance->u.usb_dev, SPEEDTCH_ENDPOINT_INT), - instance->int_data, - sizeof(instance->int_data), - speedtch_handle_int, instance, 50); - err = usb_submit_urb(instance->int_urb, GFP_KERNEL); - if (err) { - /* Doesn't matter; we'll poll anyway */ - dbg("speedtch_got_firmware: Submission of interrupt URB failed %d", err); - usb_free_urb(instance->int_urb); - instance->int_urb = NULL; - usb_driver_release_interface(&speedtch_usb_driver, intf); - } - } - } - /* Start status polling */ - mod_timer(&instance->poll_timer, jiffies + (1 * HZ)); - - instance->u.status = UDSL_LOADED_FIRMWARE; - tasklet_schedule(&instance->u.receive_tasklet); - out: - up(&instance->u.serialize); - wake_up_interruptible(&instance->u.firmware_waiters); -} - -static int speedtch_set_swbuff(struct speedtch_instance_data *instance, - int state) -{ - struct usb_device *dev = instance->u.usb_dev; + struct usbatm_data *usbatm = instance->usbatm; + struct usb_device *usb_dev = usbatm->usb_dev; int ret; - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x32, 0x40, state ? 0x01 : 0x00, - 0x00, NULL, 0, 100); - if (ret < 0) { - printk("Warning: %sabling SW buffering: usb_control_msg returned %d\n", - state ? "En" : "Dis", ret); - return ret; - } - - dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis"); - return 0; + ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + 0x32, 0x40, state ? 0x01 : 0x00, 0x00, NULL, 0, CTRL_TIMEOUT); + if (ret < 0) + usb_warn(usbatm, + "%sabling SW buffering: usb_control_msg returned %d\n", + state ? "En" : "Dis", ret); + else + dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis"); } static void speedtch_test_sequence(struct speedtch_instance_data *instance) { - struct usb_device *dev = instance->u.usb_dev; - unsigned char buf[10]; + struct usbatm_data *usbatm = instance->usbatm; + struct usb_device *usb_dev = usbatm->usb_dev; + unsigned char *buf = instance->scratch_buffer; int ret; /* URB 147 */ buf[0] = 0x1c; buf[1] = 0x50; - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x01, 0x40, 0x0b, 0x00, buf, 2, 100); + ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + 0x01, 0x40, 0x0b, 0x00, buf, 2, CTRL_TIMEOUT); if (ret < 0) - printk(KERN_WARNING "%s failed on URB147: %d\n", __func__, ret); + usb_warn(usbatm, "%s failed on URB147: %d\n", __func__, ret); /* URB 148 */ buf[0] = 0x32; buf[1] = 0x00; - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x01, 0x40, 0x02, 0x00, buf, 2, 100); + ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + 0x01, 0x40, 0x02, 0x00, buf, 2, CTRL_TIMEOUT); if (ret < 0) - printk(KERN_WARNING "%s failed on URB148: %d\n", __func__, ret); + usb_warn(usbatm, "%s failed on URB148: %d\n", __func__, ret); /* URB 149 */ buf[0] = 0x01; buf[1] = 0x00; buf[2] = 0x01; - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x01, 0x40, 0x03, 0x00, buf, 3, 100); + ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + 0x01, 0x40, 0x03, 0x00, buf, 3, CTRL_TIMEOUT); if (ret < 0) - printk(KERN_WARNING "%s failed on URB149: %d\n", __func__, ret); + usb_warn(usbatm, "%s failed on URB149: %d\n", __func__, ret); /* URB 150 */ buf[0] = 0x01; buf[1] = 0x00; buf[2] = 0x01; - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x01, 0x40, 0x04, 0x00, buf, 3, 100); + ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + 0x01, 0x40, 0x04, 0x00, buf, 3, CTRL_TIMEOUT); if (ret < 0) - printk(KERN_WARNING "%s failed on URB150: %d\n", __func__, ret); + usb_warn(usbatm, "%s failed on URB150: %d\n", __func__, ret); } -static int speedtch_start_synchro(struct speedtch_instance_data *instance) +static int speedtch_upload_firmware(struct speedtch_instance_data *instance, + const struct firmware *fw1, + const struct firmware *fw2) { - struct usb_device *dev = instance->u.usb_dev; - unsigned char buf[2]; - int ret; + unsigned char *buffer; + struct usbatm_data *usbatm = instance->usbatm; + struct usb_interface *intf; + struct usb_device *usb_dev = usbatm->usb_dev; + int actual_length; + int ret = 0; + int offset; + + usb_dbg(usbatm, "%s entered\n", __func__); + + if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) { + ret = -ENOMEM; + usb_dbg(usbatm, "%s: no memory for buffer!\n", __func__); + goto out; + } + + if (!(intf = usb_ifnum_to_if(usb_dev, 2))) { + ret = -ENODEV; + usb_dbg(usbatm, "%s: interface not found!\n", __func__); + goto out_free; + } + + /* URB 7 */ + if (dl_512_first) { /* some modems need a read before writing the firmware */ + ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE), + buffer, 0x200, &actual_length, 2000); + + if (ret < 0 && ret != -ETIMEDOUT) + usb_dbg(usbatm, "%s: read BLOCK0 from modem failed (%d)!\n", __func__, ret); + else + usb_dbg(usbatm, "%s: BLOCK0 downloaded (%d bytes)\n", __func__, ret); + } + + /* URB 8 : both leds are static green */ + for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) { + int thislen = min_t(int, PAGE_SIZE, fw1->size - offset); + memcpy(buffer, fw1->data + offset, thislen); + + ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE), + buffer, thislen, &actual_length, DATA_TIMEOUT); + + if (ret < 0) { + usb_dbg(usbatm, "%s: write BLOCK1 to modem failed (%d)!\n", __func__, ret); + goto out_free; + } + usb_dbg(usbatm, "%s: BLOCK1 uploaded (%zu bytes)\n", __func__, fw1->size); + } + + /* USB led blinking green, ADSL led off */ + + /* URB 11 */ + ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE), + buffer, 0x200, &actual_length, DATA_TIMEOUT); - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - 0x12, 0xc0, 0x04, 0x00, - buf, sizeof(buf), CTRL_TIMEOUT); if (ret < 0) { - printk(KERN_WARNING "SpeedTouch: Failed to start ADSL synchronisation: %d\n", ret); - return ret; + usb_dbg(usbatm, "%s: read BLOCK2 from modem failed (%d)!\n", __func__, ret); + goto out_free; } + usb_dbg(usbatm, "%s: BLOCK2 downloaded (%d bytes)\n", __func__, actual_length); - dbg("speedtch_start_synchro: modem prodded. %d Bytes returned: %02x %02x", ret, buf[0], buf[1]); - return 0; + /* URBs 12 to 139 - USB led blinking green, ADSL led off */ + for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) { + int thislen = min_t(int, PAGE_SIZE, fw2->size - offset); + memcpy(buffer, fw2->data + offset, thislen); + + ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE), + buffer, thislen, &actual_length, DATA_TIMEOUT); + + if (ret < 0) { + usb_dbg(usbatm, "%s: write BLOCK3 to modem failed (%d)!\n", __func__, ret); + goto out_free; + } + } + usb_dbg(usbatm, "%s: BLOCK3 uploaded (%zu bytes)\n", __func__, fw2->size); + + /* USB led static green, ADSL led static red */ + + /* URB 142 */ + ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE), + buffer, 0x200, &actual_length, DATA_TIMEOUT); + + if (ret < 0) { + usb_dbg(usbatm, "%s: read BLOCK4 from modem failed (%d)!\n", __func__, ret); + goto out_free; + } + + /* success */ + usb_dbg(usbatm, "%s: BLOCK4 downloaded (%d bytes)\n", __func__, actual_length); + + /* Delay to allow firmware to start up. We can do this here + because we're in our own kernel thread anyway. */ + msleep_interruptible(1000); + + /* Enable software buffering, if requested */ + if (sw_buffering) + speedtch_set_swbuff(instance, 1); + + /* Magic spell; don't ask us what this does */ + speedtch_test_sequence(instance); + + ret = 0; + +out_free: + free_page((unsigned long)buffer); +out: + return ret; } -static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs) +static int speedtch_find_firmware(struct usb_interface *intf, int phase, + const struct firmware **fw_p) { - struct speedtch_instance_data *instance = urb->context; - unsigned int count = urb->actual_length; - int ret; + struct device *dev = &intf->dev; + const u16 bcdDevice = le16_to_cpu(interface_to_usbdev(intf)->descriptor.bcdDevice); + const u8 major_revision = bcdDevice >> 8; + const u8 minor_revision = bcdDevice & 0xff; + char buf[24]; - /* The magic interrupt for "up state" */ - const static unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 }; - /* The magic interrupt for "down state" */ - const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 }; + sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision); + dev_dbg(dev, "%s: looking for %s\n", __func__, buf); - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated; clean up */ - dbg("%s - urb shutting down with status: %d", __func__, urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", __func__, urb->status); - goto exit; - } + if (request_firmware(fw_p, buf, dev)) { + sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision); + dev_dbg(dev, "%s: looking for %s\n", __func__, buf); - if (count < 6) { - dbg("%s - int packet too short", __func__); - goto exit; + if (request_firmware(fw_p, buf, dev)) { + sprintf(buf, "speedtch-%d.bin", phase); + dev_dbg(dev, "%s: looking for %s\n", __func__, buf); + + if (request_firmware(fw_p, buf, dev)) { + dev_warn(dev, "no stage %d firmware found!\n", phase); + return -ENOENT; + } + } } - if (!memcmp(up_int, instance->int_data, 6)) { - del_timer(&instance->poll_timer); - printk(KERN_NOTICE "DSL line goes up\n"); - } else if (!memcmp(down_int, instance->int_data, 6)) { - printk(KERN_NOTICE "DSL line goes down\n"); - } else { - int i; + dev_info(dev, "found stage %d firmware %s\n", phase, buf); - printk(KERN_DEBUG "Unknown interrupt packet of %d bytes:", count); - for (i = 0; i < count; i++) - printk(" %02x", instance->int_data[i]); - printk("\n"); + return 0; +} + +static int speedtch_heavy_init(struct usbatm_data *usbatm, struct usb_interface *intf) +{ + const struct firmware *fw1, *fw2; + struct speedtch_instance_data *instance = usbatm->driver_data; + int ret; + + if ((ret = speedtch_find_firmware(intf, 1, &fw1)) < 0) + return ret; + + if ((ret = speedtch_find_firmware(intf, 2, &fw2)) < 0) { + release_firmware(fw1); + return ret; } - schedule_work(&instance->poll_work); - exit: - rmb(); - if (!instance->int_urb) - return; + ret = speedtch_upload_firmware(instance, fw1, fw2); + + release_firmware(fw2); + release_firmware(fw1); - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) - err("%s - usb_submit_urb failed with result %d", __func__, ret); + return ret; } -static int speedtch_get_status(struct speedtch_instance_data *instance, - unsigned char *buf) + +/********** +** ATM ** +**********/ + +static int speedtch_read_status(struct speedtch_instance_data *instance) { - struct usb_device *dev = instance->u.usb_dev; + struct usbatm_data *usbatm = instance->usbatm; + struct usb_device *usb_dev = usbatm->usb_dev; + unsigned char *buf = instance->scratch_buffer; int ret; memset(buf, 0, TOTAL); - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7, CTRL_TIMEOUT); if (ret < 0) { - dbg("MSG 7 failed"); + atm_dbg(usbatm, "%s: MSG 7 failed\n", __func__); return ret; } - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b, CTRL_TIMEOUT); if (ret < 0) { - dbg("MSG B failed"); + atm_dbg(usbatm, "%s: MSG B failed\n", __func__); return ret; } - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d, CTRL_TIMEOUT); if (ret < 0) { - dbg("MSG D failed"); + atm_dbg(usbatm, "%s: MSG D failed\n", __func__); return ret; } - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e, CTRL_TIMEOUT); if (ret < 0) { - dbg("MSG E failed"); + atm_dbg(usbatm, "%s: MSG E failed\n", __func__); return ret; } - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f, CTRL_TIMEOUT); if (ret < 0) { - dbg("MSG F failed"); + atm_dbg(usbatm, "%s: MSG F failed\n", __func__); return ret; } return 0; } -static void speedtch_poll_status(struct speedtch_instance_data *instance) +static int speedtch_start_synchro(struct speedtch_instance_data *instance) { - unsigned char buf[TOTAL]; + struct usbatm_data *usbatm = instance->usbatm; + struct usb_device *usb_dev = usbatm->usb_dev; + unsigned char *buf = instance->scratch_buffer; int ret; - ret = speedtch_get_status(instance, buf); - if (ret) { - printk(KERN_WARNING - "SpeedTouch: Error %d fetching device status\n", ret); + atm_dbg(usbatm, "%s entered\n", __func__); + + memset(buf, 0, 2); + + ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + 0x12, 0xc0, 0x04, 0x00, + buf, 2, CTRL_TIMEOUT); + + if (ret < 0) + atm_warn(usbatm, "failed to start ADSL synchronisation: %d\n", ret); + else + atm_dbg(usbatm, "%s: modem prodded. %d bytes returned: %02x %02x\n", + __func__, ret, buf[0], buf[1]); + + return ret; +} + +static void speedtch_check_status(struct speedtch_instance_data *instance) +{ + struct usbatm_data *usbatm = instance->usbatm; + struct atm_dev *atm_dev = usbatm->atm_dev; + unsigned char *buf = instance->scratch_buffer; + int ret; + + atm_dbg(usbatm, "%s entered\n", __func__); + + ret = speedtch_read_status(instance); + if (ret < 0) { + atm_warn(usbatm, "error %d fetching device status\n", ret); + if (instance->poll_delay < MAX_POLL_DELAY) + instance->poll_delay *= 2; return; } - dbg("Line state %02x", buf[OFFSET_7]); + if (instance->poll_delay > MIN_POLL_DELAY) + instance->poll_delay /= 2; + + atm_dbg(usbatm, "%s: line state %02x\n", __func__, buf[OFFSET_7]); switch (buf[OFFSET_7]) { case 0: - if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) { - instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; - printk(KERN_NOTICE "ADSL line is down\n"); + if (atm_dev->signal != ATM_PHY_SIG_LOST) { + atm_dev->signal = ATM_PHY_SIG_LOST; + atm_info(usbatm, "ADSL line is down\n"); /* It'll never resync again unless we ask it to... */ - speedtch_start_synchro(instance); + ret = speedtch_start_synchro(instance); } break; case 0x08: - if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) { - instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN; - printk(KERN_NOTICE "ADSL line is blocked?\n"); + if (atm_dev->signal != ATM_PHY_SIG_UNKNOWN) { + atm_dev->signal = ATM_PHY_SIG_UNKNOWN; + atm_info(usbatm, "ADSL line is blocked?\n"); } break; case 0x10: - if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) { - instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; - printk(KERN_NOTICE "ADSL line is synchronising\n"); + if (atm_dev->signal != ATM_PHY_SIG_LOST) { + atm_dev->signal = ATM_PHY_SIG_LOST; + atm_info(usbatm, "ADSL line is synchronising\n"); } break; case 0x20: - if (instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND) { + if (atm_dev->signal != ATM_PHY_SIG_FOUND) { int down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8) | (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24); int up_speed = buf[OFFSET_b + 4] | (buf[OFFSET_b + 5] << 8) | (buf[OFFSET_b + 6] << 16) | (buf[OFFSET_b + 7] << 24); - if (!(down_speed & 0x0000ffff) && - !(up_speed & 0x0000ffff)) { + if (!(down_speed & 0x0000ffff) && !(up_speed & 0x0000ffff)) { down_speed >>= 16; up_speed >>= 16; } - instance->u.atm_dev->link_rate = down_speed * 1000 / 424; - instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND; - printk(KERN_NOTICE - "ADSL line is up (%d Kib/s down | %d Kib/s up)\n", - down_speed, up_speed); + atm_dev->link_rate = down_speed * 1000 / 424; + atm_dev->signal = ATM_PHY_SIG_FOUND; + + atm_info(usbatm, + "ADSL line is up (%d Kib/s down | %d Kib/s up)\n", + down_speed, up_speed); } break; default: - if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) { - instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN; - printk(KERN_NOTICE "Unknown line state %02x\n", buf[OFFSET_7]); + if (atm_dev->signal != ATM_PHY_SIG_UNKNOWN) { + atm_dev->signal = ATM_PHY_SIG_UNKNOWN; + atm_info(usbatm, "Unknown line state %02x\n", buf[OFFSET_7]); } break; } } -static void speedtch_timer_poll(unsigned long data) +static void speedtch_status_poll(unsigned long data) { struct speedtch_instance_data *instance = (void *)data; - schedule_work(&instance->poll_work); - mod_timer(&instance->poll_timer, jiffies + (5 * HZ)); + schedule_work(&instance->status_checker); + + /* The following check is racy, but the race is harmless */ + if (instance->poll_delay < MAX_POLL_DELAY) + mod_timer(&instance->status_checker.timer, jiffies + msecs_to_jiffies(instance->poll_delay)); + else + atm_warn(instance->usbatm, "Too many failures - disabling line status polling\n"); } -#ifdef USE_FW_LOADER -static void speedtch_upload_firmware(struct speedtch_instance_data *instance, - const struct firmware *fw1, - const struct firmware *fw2) +static void speedtch_resubmit_int(unsigned long data) { - unsigned char *buffer; - struct usb_device *usb_dev = instance->u.usb_dev; - struct usb_interface *intf; - int actual_length, ret; - int offset; - - dbg("speedtch_upload_firmware"); - - if (!(intf = usb_ifnum_to_if(usb_dev, 2))) { - dbg("speedtch_upload_firmware: interface not found!"); - goto fail; - } - - if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) { - dbg("speedtch_upload_firmware: no memory for buffer!"); - goto fail; - } - - /* A user-space firmware loader may already have claimed interface #2 */ - if ((ret = - usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) < 0) { - dbg("speedtch_upload_firmware: interface in use (%d)!", ret); - goto fail_free; - } - - /* URB 7 */ - if (dl_512_first) { /* some modems need a read before writing the firmware */ - ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), - buffer, 0x200, &actual_length, 2000); - - if (ret < 0 && ret != -ETIMEDOUT) - dbg("speedtch_upload_firmware: read BLOCK0 from modem failed (%d)!", ret); - else - dbg("speedtch_upload_firmware: BLOCK0 downloaded (%d bytes)", ret); - } - - /* URB 8 : both leds are static green */ - for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) { - int thislen = min_t(int, PAGE_SIZE, fw1->size - offset); - memcpy(buffer, fw1->data + offset, thislen); + struct speedtch_instance_data *instance = (void *)data; + struct urb *int_urb = instance->int_urb; + int ret; - ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), - buffer, thislen, &actual_length, DATA_TIMEOUT); + atm_dbg(instance->usbatm, "%s entered\n", __func__); - if (ret < 0) { - dbg("speedtch_upload_firmware: write BLOCK1 to modem failed (%d)!", ret); - goto fail_release; + if (int_urb) { + ret = usb_submit_urb(int_urb, GFP_ATOMIC); + if (!ret) + schedule_work(&instance->status_checker); + else { + atm_dbg(instance->usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret); + mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY)); } - dbg("speedtch_upload_firmware: BLOCK1 uploaded (%zu bytes)", fw1->size); } +} - /* USB led blinking green, ADSL led off */ +static void speedtch_handle_int(struct urb *int_urb, struct pt_regs *regs) +{ + struct speedtch_instance_data *instance = int_urb->context; + struct usbatm_data *usbatm = instance->usbatm; + unsigned int count = int_urb->actual_length; + int ret = int_urb->status; - /* URB 11 */ - ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), - buffer, 0x200, &actual_length, DATA_TIMEOUT); + /* The magic interrupt for "up state" */ + const static unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 }; + /* The magic interrupt for "down state" */ + const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + atm_dbg(usbatm, "%s entered\n", __func__); if (ret < 0) { - dbg("speedtch_upload_firmware: read BLOCK2 from modem failed (%d)!", ret); - goto fail_release; + atm_dbg(usbatm, "%s: nonzero urb status %d!\n", __func__, ret); + goto fail; } - dbg("speedtch_upload_firmware: BLOCK2 downloaded (%d bytes)", actual_length); - /* URBs 12 to 139 - USB led blinking green, ADSL led off */ - for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) { - int thislen = min_t(int, PAGE_SIZE, fw2->size - offset); - memcpy(buffer, fw2->data + offset, thislen); + if ((count == 6) && !memcmp(up_int, instance->int_data, 6)) { + del_timer(&instance->status_checker.timer); + atm_info(usbatm, "DSL line goes up\n"); + } else if ((count == 6) && !memcmp(down_int, instance->int_data, 6)) { + atm_info(usbatm, "DSL line goes down\n"); + } else { + int i; - ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), - buffer, thislen, &actual_length, DATA_TIMEOUT); + atm_dbg(usbatm, "%s: unknown interrupt packet of length %d:", __func__, count); + for (i = 0; i < count; i++) + printk(" %02x", instance->int_data[i]); + printk("\n"); + goto fail; + } + if ((int_urb = instance->int_urb)) { + ret = usb_submit_urb(int_urb, GFP_ATOMIC); + schedule_work(&instance->status_checker); if (ret < 0) { - dbg("speedtch_upload_firmware: write BLOCK3 to modem failed (%d)!", ret); - goto fail_release; + atm_dbg(usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret); + goto fail; } } - dbg("speedtch_upload_firmware: BLOCK3 uploaded (%zu bytes)", fw2->size); - - /* USB led static green, ADSL led static red */ - - /* URB 142 */ - ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), - buffer, 0x200, &actual_length, DATA_TIMEOUT); - - if (ret < 0) { - dbg("speedtch_upload_firmware: read BLOCK4 from modem failed (%d)!", ret); - goto fail_release; - } - - /* success */ - dbg("speedtch_upload_firmware: BLOCK4 downloaded (%d bytes)", actual_length); - - /* Delay to allow firmware to start up. We can do this here - because we're in our own kernel thread anyway. */ - msleep(1000); - - /* Enable software buffering, if requested */ - if (sw_buffering) - speedtch_set_swbuff(instance, 1); - - /* Magic spell; don't ask us what this does */ - speedtch_test_sequence(instance); - - /* Start modem synchronisation */ - if (speedtch_start_synchro(instance)) - dbg("speedtch_start_synchro: failed"); - - speedtch_got_firmware(instance, 1); - free_page((unsigned long)buffer); return; - fail_release: - /* Only release interface #2 if uploading failed; we don't release it - we succeeded. This prevents the userspace tools from trying to load - the firmware themselves */ - usb_driver_release_interface(&speedtch_usb_driver, intf); - fail_free: - free_page((unsigned long)buffer); - fail: - speedtch_got_firmware(instance, 0); +fail: + if ((int_urb = instance->int_urb)) + mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY)); } -static int speedtch_find_firmware(struct speedtch_instance_data - *instance, int phase, - const struct firmware **fw_p) +static int speedtch_atm_start(struct usbatm_data *usbatm, struct atm_dev *atm_dev) { - char buf[24]; - const u16 bcdDevice = le16_to_cpu(instance->u.usb_dev->descriptor.bcdDevice); - const u8 major_revision = bcdDevice >> 8; - const u8 minor_revision = bcdDevice & 0xff; - - sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision); - dbg("speedtch_find_firmware: looking for %s", buf); - - if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { - sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision); - dbg("speedtch_find_firmware: looking for %s", buf); + struct usb_device *usb_dev = usbatm->usb_dev; + struct speedtch_instance_data *instance = usbatm->driver_data; + int i, ret; + unsigned char mac_str[13]; - if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { - sprintf(buf, "speedtch-%d.bin", phase); - dbg("speedtch_find_firmware: looking for %s", buf); + atm_dbg(usbatm, "%s entered\n", __func__); - if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { - dev_warn(&instance->u.usb_dev->dev, "no stage %d firmware found!", phase); - return -ENOENT; - } - } + if ((ret = usb_set_interface(usb_dev, 1, altsetting)) < 0) { + atm_dbg(usbatm, "%s: usb_set_interface returned %d!\n", __func__, ret); + return ret; } - dev_info(&instance->u.usb_dev->dev, "found stage %d firmware %s\n", phase, buf); - - return 0; -} - -static int speedtch_load_firmware(void *arg) -{ - const struct firmware *fw1, *fw2; - struct speedtch_instance_data *instance = arg; - - BUG_ON(!instance); + /* Set MAC address, it is stored in the serial number */ + memset(atm_dev->esi, 0, sizeof(atm_dev->esi)); + if (usb_string(usb_dev, usb_dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) { + for (i = 0; i < 6; i++) + atm_dev->esi[i] = (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1])); + } - daemonize("firmware/speedtch"); + /* Start modem synchronisation */ + ret = speedtch_start_synchro(instance); - if (!speedtch_find_firmware(instance, 1, &fw1)) { - if (!speedtch_find_firmware(instance, 2, &fw2)) { - speedtch_upload_firmware(instance, fw1, fw2); - release_firmware(fw2); + /* Set up interrupt endpoint */ + if (instance->int_urb) { + ret = usb_submit_urb(instance->int_urb, GFP_KERNEL); + if (ret < 0) { + /* Doesn't matter; we'll poll anyway */ + atm_dbg(usbatm, "%s: submission of interrupt URB failed (%d)!\n", __func__, ret); + usb_free_urb(instance->int_urb); + instance->int_urb = NULL; } - release_firmware(fw1); } - /* In case we failed, set state back to NO_FIRMWARE so that - another later attempt may work. Otherwise, we never actually - manage to recover if, for example, the firmware is on /usr and - we look for it too early. */ - speedtch_got_firmware(instance, 0); + /* Start status polling */ + mod_timer(&instance->status_checker.timer, jiffies + msecs_to_jiffies(1000)); - module_put(THIS_MODULE); - udsl_put_instance(&instance->u); return 0; } -#endif /* USE_FW_LOADER */ -static void speedtch_firmware_start(struct speedtch_instance_data *instance) +static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_dev) { -#ifdef USE_FW_LOADER - int ret; -#endif - - dbg("speedtch_firmware_start"); - - down(&instance->u.serialize); /* vs self, speedtch_got_firmware */ - - if (instance->u.status >= UDSL_LOADING_FIRMWARE) { - up(&instance->u.serialize); - return; - } + struct speedtch_instance_data *instance = usbatm->driver_data; + struct urb *int_urb = instance->int_urb; + + atm_dbg(usbatm, "%s entered\n", __func__); + + del_timer_sync(&instance->status_checker.timer); + + /* + * Since resubmit_timer and int_urb can schedule themselves and + * each other, shutting them down correctly takes some care + */ + instance->int_urb = NULL; /* signal shutdown */ + mb(); + usb_kill_urb(int_urb); + del_timer_sync(&instance->resubmit_timer); + /* + * At this point, speedtch_handle_int and speedtch_resubmit_int + * can run or be running, but instance->int_urb == NULL means that + * they will not reschedule + */ + usb_kill_urb(int_urb); + del_timer_sync(&instance->resubmit_timer); + usb_free_urb(int_urb); - instance->u.status = UDSL_LOADING_FIRMWARE; - up(&instance->u.serialize); + flush_scheduled_work(); +} -#ifdef USE_FW_LOADER - udsl_get_instance(&instance->u); - try_module_get(THIS_MODULE); - ret = kernel_thread(speedtch_load_firmware, instance, - CLONE_FS | CLONE_FILES); +/********** +** USB ** +**********/ - if (ret >= 0) - return; /* OK */ +static struct usb_device_id speedtch_usb_ids[] = { + {USB_DEVICE(0x06b9, 0x4061)}, + {} +}; - dbg("speedtch_firmware_start: kernel_thread failed (%d)!", ret); +MODULE_DEVICE_TABLE(usb, speedtch_usb_ids); - module_put(THIS_MODULE); - udsl_put_instance(&instance->u); - /* Just pretend it never happened... hope modem_run happens */ -#endif /* USE_FW_LOADER */ +static int speedtch_usb_probe(struct usb_interface *, const struct usb_device_id *); - speedtch_got_firmware(instance, 0); -} - -static int speedtch_firmware_wait(struct udsl_instance_data *instance) -{ - speedtch_firmware_start((void *)instance); +static struct usb_driver speedtch_usb_driver = { + .owner = THIS_MODULE, + .name = speedtch_driver_name, + .probe = speedtch_usb_probe, + .disconnect = usbatm_usb_disconnect, + .id_table = speedtch_usb_ids +}; - if (wait_event_interruptible(instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0) - return -ERESTARTSYS; +static void speedtch_release_interfaces(struct usb_device *usb_dev, int num_interfaces) { + struct usb_interface *cur_intf; + int i; - return (instance->status == UDSL_LOADED_FIRMWARE) ? 0 : -EAGAIN; + for(i = 0; i < num_interfaces; i++) + if ((cur_intf = usb_ifnum_to_if(usb_dev, i))) { + usb_set_intfdata(cur_intf, NULL); + usb_driver_release_interface(&speedtch_usb_driver, cur_intf); + } } -/********** -** USB ** -**********/ - -static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code, - void *user_data) +static int speedtch_bind(struct usbatm_data *usbatm, + struct usb_interface *intf, + const struct usb_device_id *id, + int *need_heavy_init) { - struct speedtch_instance_data *instance = usb_get_intfdata(intf); + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct usb_interface *cur_intf; + struct speedtch_instance_data *instance; + int ifnum = intf->altsetting->desc.bInterfaceNumber; + int num_interfaces = usb_dev->actconfig->desc.bNumInterfaces; + int i, ret; - dbg("speedtch_usb_ioctl entered"); + usb_dbg(usbatm, "%s entered\n", __func__); - if (!instance) { - dbg("speedtch_usb_ioctl: NULL instance!"); + if (usb_dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) { + usb_dbg(usbatm, "%s: wrong device class %d\n", __func__, usb_dev->descriptor.bDeviceClass); return -ENODEV; } - switch (code) { - case UDSL_IOCTL_LINE_UP: - instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND; - speedtch_got_firmware(instance, 1); - return (instance->u.status == UDSL_LOADED_FIRMWARE) ? 0 : -EIO; - case UDSL_IOCTL_LINE_DOWN: - instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; - return 0; - default: - return -ENOTTY; - } -} + /* claim all interfaces */ -static int speedtch_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - int ifnum = intf->altsetting->desc.bInterfaceNumber; - struct speedtch_instance_data *instance; - unsigned char mac_str[13]; - int ret, i; - char buf7[SIZE_7]; + for (i=0; i < num_interfaces; i++) { + cur_intf = usb_ifnum_to_if(usb_dev, i); - dbg("speedtch_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d", - le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct), ifnum); + if ((i != ifnum) && cur_intf) { + ret = usb_driver_claim_interface(&speedtch_usb_driver, cur_intf, usbatm); - if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) || - (ifnum != 1)) - return -ENODEV; - - dbg("speedtch_usb_probe: device accepted"); + if (ret < 0) { + usb_dbg(usbatm, "%s: failed to claim interface %d (%d)\n", __func__, i, ret); + speedtch_release_interfaces(usb_dev, i); + return ret; + } + } + } - /* instance init */ instance = kmalloc(sizeof(*instance), GFP_KERNEL); + if (!instance) { - dbg("speedtch_usb_probe: no memory for instance data!"); - return -ENOMEM; + usb_dbg(usbatm, "%s: no memory for instance data!\n", __func__); + ret = -ENOMEM; + goto fail_release; } memset(instance, 0, sizeof(struct speedtch_instance_data)); - if ((ret = usb_set_interface(dev, 0, 0)) < 0) - goto fail; + instance->usbatm = usbatm; - if ((ret = usb_set_interface(dev, 2, 0)) < 0) - goto fail; + INIT_WORK(&instance->status_checker, (void *)speedtch_check_status, instance); - instance->u.data_endpoint = SPEEDTCH_ENDPOINT_DATA; - instance->u.firmware_wait = speedtch_firmware_wait; - instance->u.driver_name = speedtch_driver_name; + instance->status_checker.timer.function = speedtch_status_poll; + instance->status_checker.timer.data = (unsigned long)instance; + instance->poll_delay = MIN_POLL_DELAY; - ret = udsl_instance_setup(dev, &instance->u); - if (ret) - goto fail; + init_timer(&instance->resubmit_timer); + instance->resubmit_timer.function = speedtch_resubmit_int; + instance->resubmit_timer.data = (unsigned long)instance; - init_timer(&instance->poll_timer); - instance->poll_timer.function = speedtch_timer_poll; - instance->poll_timer.data = (unsigned long)instance; + instance->int_urb = usb_alloc_urb(0, GFP_KERNEL); - INIT_WORK(&instance->poll_work, (void *)speedtch_poll_status, instance); + if (instance->int_urb) + usb_fill_int_urb(instance->int_urb, usb_dev, + usb_rcvintpipe(usb_dev, ENDPOINT_INT), + instance->int_data, sizeof(instance->int_data), + speedtch_handle_int, instance, 50); + else + usb_dbg(usbatm, "%s: no memory for interrupt urb!\n", __func__); - /* set MAC address, it is stored in the serial number */ - memset(instance->u.atm_dev->esi, 0, sizeof(instance->u.atm_dev->esi)); - if (usb_string(dev, dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) { - for (i = 0; i < 6; i++) - instance->u.atm_dev->esi[i] = - (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1])); - } + /* check whether the modem already seems to be alive */ + ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + 0x12, 0xc0, 0x07, 0x00, + instance->scratch_buffer + OFFSET_7, SIZE_7, 500); - /* First check whether the modem already seems to be alive */ - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - 0x12, 0xc0, 0x07, 0x00, buf7, SIZE_7, 500); + *need_heavy_init = (ret != SIZE_7); - if (ret == SIZE_7) { - dbg("firmware appears to be already loaded"); - speedtch_got_firmware(instance, 1); - speedtch_poll_status(instance); - } else { - speedtch_firmware_start(instance); - } + usb_dbg(usbatm, "%s: firmware %s loaded\n", __func__, need_heavy_init ? "not" : "already"); + + if (*need_heavy_init) + if ((ret = usb_reset_device(usb_dev)) < 0) + goto fail_free; - usb_set_intfdata(intf, instance); + usbatm->driver_data = instance; return 0; - fail: +fail_free: + usb_free_urb(instance->int_urb); kfree(instance); - - return -ENOMEM; +fail_release: + speedtch_release_interfaces(usb_dev, num_interfaces); + return ret; } -static void speedtch_usb_disconnect(struct usb_interface *intf) +static void speedtch_unbind(struct usbatm_data *usbatm, struct usb_interface *intf) { - struct speedtch_instance_data *instance = usb_get_intfdata(intf); - - dbg("speedtch_usb_disconnect entered"); - - if (!instance) { - dbg("speedtch_usb_disconnect: NULL instance!"); - return; - } + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct speedtch_instance_data *instance = usbatm->driver_data; -/*QQ need to handle disconnects on interface #2 while uploading firmware */ -/*QQ and what about interface #1? */ - - if (instance->int_urb) { - struct urb *int_urb = instance->int_urb; - instance->int_urb = NULL; - wmb(); - usb_unlink_urb(int_urb); - usb_free_urb(int_urb); - } + usb_dbg(usbatm, "%s entered\n", __func__); - instance->int_data[0] = 1; - del_timer_sync(&instance->poll_timer); - wmb(); - flush_scheduled_work(); - - udsl_instance_disconnect(&instance->u); - - /* clean up */ - usb_set_intfdata(intf, NULL); - udsl_put_instance(&instance->u); + speedtch_release_interfaces(usb_dev, usb_dev->actconfig->desc.bNumInterfaces); + usb_free_urb(instance->int_urb); + kfree(instance); } + /*********** ** init ** ***********/ +static struct usbatm_driver speedtch_usbatm_driver = { + .owner = THIS_MODULE, + .driver_name = speedtch_driver_name, + .bind = speedtch_bind, + .heavy_init = speedtch_heavy_init, + .unbind = speedtch_unbind, + .atm_start = speedtch_atm_start, + .atm_stop = speedtch_atm_stop, + .in = ENDPOINT_DATA, + .out = ENDPOINT_DATA +}; + +static int speedtch_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + return usbatm_usb_probe(intf, id, &speedtch_usbatm_driver); +} + static int __init speedtch_usb_init(void) { - dbg("speedtch_usb_init: driver version " DRIVER_VERSION); + dbg("%s: driver version %s", __func__, DRIVER_VERSION); return usb_register(&speedtch_usb_driver); } static void __exit speedtch_usb_cleanup(void) { - dbg("speedtch_usb_cleanup entered"); + dbg("%s", __func__); usb_deregister(&speedtch_usb_driver); } diff --git a/drivers/usb/atm/usb_atm.c b/drivers/usb/atm/usb_atm.c deleted file mode 100644 index a4cd447..0000000 --- a/drivers/usb/atm/usb_atm.c +++ /dev/null @@ -1,1188 +0,0 @@ -/****************************************************************************** - * usb_atm.c - Generic USB xDSL driver core - * - * Copyright (C) 2001, Alcatel - * Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas - * Copyright (C) 2004, David Woodhouse - * - * 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. - * - ******************************************************************************/ - -/* - * Written by Johan Verrept, maintained by Duncan Sands (duncan.sands@free.fr) - * - * 1.7+: - See the check-in logs - * - * 1.6: - No longer opens a connection if the firmware is not loaded - * - Added support for the speedtouch 330 - * - Removed the limit on the number of devices - * - Module now autoloads on device plugin - * - Merged relevant parts of sarlib - * - Replaced the kernel thread with a tasklet - * - New packet transmission code - * - Changed proc file contents - * - Fixed all known SMP races - * - Many fixes and cleanups - * - Various fixes by Oliver Neukum (oliver@neukum.name) - * - * 1.5A: - Version for inclusion in 2.5 series kernel - * - Modifications by Richard Purdie (rpurdie@rpsys.net) - * - made compatible with kernel 2.5.6 onwards by changing - * udsl_usb_send_data_context->urb to a pointer and adding code - * to alloc and free it - * - remove_wait_queue() added to udsl_atm_processqueue_thread() - * - * 1.5: - fixed memory leak when atmsar_decode_aal5 returned NULL. - * (reported by stephen.robinson@zen.co.uk) - * - * 1.4: - changed the spin_lock() under interrupt to spin_lock_irqsave() - * - unlink all active send urbs of a vcc that is being closed. - * - * 1.3.1: - added the version number - * - * 1.3: - Added multiple send urb support - * - fixed memory leak and vcc->tx_inuse starvation bug - * when not enough memory left in vcc. - * - * 1.2: - Fixed race condition in udsl_usb_send_data() - * 1.1: - Turned off packet debugging - * - */ - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/errno.h> -#include <linux/proc_fs.h> -#include <linux/slab.h> -#include <linux/wait.h> -#include <linux/list.h> -#include <asm/uaccess.h> -#include <linux/smp_lock.h> -#include <linux/interrupt.h> -#include <linux/atm.h> -#include <linux/atmdev.h> -#include <linux/crc32.h> -#include <linux/init.h> -#include <linux/firmware.h> - -#include "usb_atm.h" - -#ifdef VERBOSE_DEBUG -static int udsl_print_packet(const unsigned char *data, int len); -#define PACKETDEBUG(arg...) udsl_print_packet (arg) -#define vdbg(arg...) dbg (arg) -#else -#define PACKETDEBUG(arg...) -#define vdbg(arg...) -#endif - -#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <duncan.sands@free.fr>" -#define DRIVER_VERSION "1.8" -#define DRIVER_DESC "Generic USB ATM/DSL I/O, version " DRIVER_VERSION - -static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS; -static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS; -static unsigned int num_rcv_bufs = UDSL_DEFAULT_RCV_BUFS; -static unsigned int num_snd_bufs = UDSL_DEFAULT_SND_BUFS; -static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE; -static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE; - -module_param(num_rcv_urbs, uint, 0444); -MODULE_PARM_DESC(num_rcv_urbs, - "Number of urbs used for reception (range: 0-" - __MODULE_STRING(UDSL_MAX_RCV_URBS) ", default: " - __MODULE_STRING(UDSL_DEFAULT_RCV_URBS) ")"); - -module_param(num_snd_urbs, uint, 0444); -MODULE_PARM_DESC(num_snd_urbs, - "Number of urbs used for transmission (range: 0-" - __MODULE_STRING(UDSL_MAX_SND_URBS) ", default: " - __MODULE_STRING(UDSL_DEFAULT_SND_URBS) ")"); - -module_param(num_rcv_bufs, uint, 0444); -MODULE_PARM_DESC(num_rcv_bufs, - "Number of buffers used for reception (range: 0-" - __MODULE_STRING(UDSL_MAX_RCV_BUFS) ", default: " - __MODULE_STRING(UDSL_DEFAULT_RCV_BUFS) ")"); - -module_param(num_snd_bufs, uint, 0444); -MODULE_PARM_DESC(num_snd_bufs, - "Number of buffers used for transmission (range: 0-" - __MODULE_STRING(UDSL_MAX_SND_BUFS) ", default: " - __MODULE_STRING(UDSL_DEFAULT_SND_BUFS) ")"); - -module_param(rcv_buf_size, uint, 0444); -MODULE_PARM_DESC(rcv_buf_size, - "Size of the buffers used for reception (range: 0-" - __MODULE_STRING(UDSL_MAX_RCV_BUF_SIZE) ", default: " - __MODULE_STRING(UDSL_DEFAULT_RCV_BUF_SIZE) ")"); - -module_param(snd_buf_size, uint, 0444); -MODULE_PARM_DESC(snd_buf_size, - "Size of the buffers used for transmission (range: 0-" - __MODULE_STRING(UDSL_MAX_SND_BUF_SIZE) ", default: " - __MODULE_STRING(UDSL_DEFAULT_SND_BUF_SIZE) ")"); - -/* ATM */ - -static void udsl_atm_dev_close(struct atm_dev *dev); -static int udsl_atm_open(struct atm_vcc *vcc); -static void udsl_atm_close(struct atm_vcc *vcc); -static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg); -static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb); -static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page); - -static struct atmdev_ops udsl_atm_devops = { - .dev_close = udsl_atm_dev_close, - .open = udsl_atm_open, - .close = udsl_atm_close, - .ioctl = udsl_atm_ioctl, - .send = udsl_atm_send, - .proc_read = udsl_atm_proc_read, - .owner = THIS_MODULE, -}; - -/*********** -** misc ** -***********/ - -static inline void udsl_pop(struct atm_vcc *vcc, struct sk_buff *skb) -{ - if (vcc->pop) - vcc->pop(vcc, skb); - else - dev_kfree_skb(skb); -} - -/************* -** decode ** -*************/ - -static inline struct udsl_vcc_data *udsl_find_vcc(struct udsl_instance_data *instance, - short vpi, int vci) -{ - struct udsl_vcc_data *vcc; - - list_for_each_entry(vcc, &instance->vcc_list, list) - if ((vcc->vci == vci) && (vcc->vpi == vpi)) - return vcc; - return NULL; -} - -static void udsl_extract_cells(struct udsl_instance_data *instance, - unsigned char *source, unsigned int howmany) -{ - struct udsl_vcc_data *cached_vcc = NULL; - struct atm_vcc *vcc; - struct sk_buff *sarb; - struct udsl_vcc_data *vcc_data; - int cached_vci = 0; - unsigned int i; - int pti; - int vci; - short cached_vpi = 0; - short vpi; - - for (i = 0; i < howmany; - i++, source += ATM_CELL_SIZE + instance->rcv_padding) { - vpi = ((source[0] & 0x0f) << 4) | (source[1] >> 4); - vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4); - pti = (source[3] & 0x2) != 0; - - vdbg("udsl_extract_cells: vpi %hd, vci %d, pti %d", vpi, vci, pti); - - if (cached_vcc && (vci == cached_vci) && (vpi == cached_vpi)) - vcc_data = cached_vcc; - else if ((vcc_data = udsl_find_vcc(instance, vpi, vci))) { - cached_vcc = vcc_data; - cached_vpi = vpi; - cached_vci = vci; - } else { - dbg("udsl_extract_cells: unknown vpi/vci (%hd/%d)!", vpi, vci); - continue; - } - - vcc = vcc_data->vcc; - sarb = vcc_data->sarb; - - if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) { - dbg("udsl_extract_cells: buffer overrun (sarb->len %u, vcc: 0x%p)!", sarb->len, vcc); - /* discard cells already received */ - skb_trim(sarb, 0); - } - - memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD); - __skb_put(sarb, ATM_CELL_PAYLOAD); - - if (pti) { - struct sk_buff *skb; - unsigned int length; - unsigned int pdu_length; - - length = (source[ATM_CELL_SIZE - 6] << 8) + source[ATM_CELL_SIZE - 5]; - - /* guard against overflow */ - if (length > ATM_MAX_AAL5_PDU) { - dbg("udsl_extract_cells: bogus length %u (vcc: 0x%p)!", length, vcc); - atomic_inc(&vcc->stats->rx_err); - goto out; - } - - pdu_length = UDSL_NUM_CELLS(length) * ATM_CELL_PAYLOAD; - - if (sarb->len < pdu_length) { - dbg("udsl_extract_cells: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!", pdu_length, sarb->len, vcc); - atomic_inc(&vcc->stats->rx_err); - goto out; - } - - if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) { - dbg("udsl_extract_cells: packet failed crc check (vcc: 0x%p)!", vcc); - atomic_inc(&vcc->stats->rx_err); - goto out; - } - - vdbg("udsl_extract_cells: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", length, pdu_length, vcc); - - if (!(skb = dev_alloc_skb(length))) { - dbg("udsl_extract_cells: no memory for skb (length: %u)!", length); - atomic_inc(&vcc->stats->rx_drop); - goto out; - } - - vdbg("udsl_extract_cells: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", skb, skb->truesize); - - if (!atm_charge(vcc, skb->truesize)) { - dbg("udsl_extract_cells: failed atm_charge (skb->truesize: %u)!", skb->truesize); - dev_kfree_skb(skb); - goto out; /* atm_charge increments rx_drop */ - } - - memcpy(skb->data, sarb->tail - pdu_length, length); - __skb_put(skb, length); - - vdbg("udsl_extract_cells: sending skb 0x%p, skb->len %u, skb->truesize %u", skb, skb->len, skb->truesize); - - PACKETDEBUG(skb->data, skb->len); - - vcc->push(vcc, skb); - - atomic_inc(&vcc->stats->rx); - out: - skb_trim(sarb, 0); - } - } -} - -/************* -** encode ** -*************/ - -static inline void udsl_fill_cell_header(unsigned char *target, struct atm_vcc *vcc) -{ - target[0] = vcc->vpi >> 4; - target[1] = (vcc->vpi << 4) | (vcc->vci >> 12); - target[2] = vcc->vci >> 4; - target[3] = vcc->vci << 4; - target[4] = 0xec; -} - -static const unsigned char zeros[ATM_CELL_PAYLOAD]; - -static void udsl_groom_skb(struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct udsl_control *ctrl = UDSL_SKB(skb); - unsigned int zero_padding; - u32 crc; - - ctrl->atm_data.vcc = vcc; - - ctrl->num_cells = UDSL_NUM_CELLS(skb->len); - ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD; - - zero_padding = ctrl->num_cells * ATM_CELL_PAYLOAD - skb->len - ATM_AAL5_TRAILER; - - if (ctrl->num_entire + 1 < ctrl->num_cells) - ctrl->pdu_padding = zero_padding - (ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); - else - ctrl->pdu_padding = zero_padding; - - ctrl->aal5_trailer[0] = 0; /* UU = 0 */ - ctrl->aal5_trailer[1] = 0; /* CPI = 0 */ - ctrl->aal5_trailer[2] = skb->len >> 8; - ctrl->aal5_trailer[3] = skb->len; - - crc = crc32_be(~0, skb->data, skb->len); - crc = crc32_be(crc, zeros, zero_padding); - crc = crc32_be(crc, ctrl->aal5_trailer, 4); - crc = ~crc; - - ctrl->aal5_trailer[4] = crc >> 24; - ctrl->aal5_trailer[5] = crc >> 16; - ctrl->aal5_trailer[6] = crc >> 8; - ctrl->aal5_trailer[7] = crc; -} - -static unsigned int udsl_write_cells(struct udsl_instance_data *instance, - unsigned int howmany, struct sk_buff *skb, - unsigned char **target_p) -{ - struct udsl_control *ctrl = UDSL_SKB(skb); - unsigned char *target = *target_p; - unsigned int nc, ne, i; - - vdbg("udsl_write_cells: howmany=%u, skb->len=%d, num_cells=%u, num_entire=%u, pdu_padding=%u", howmany, skb->len, ctrl->num_cells, ctrl->num_entire, ctrl->pdu_padding); - - nc = ctrl->num_cells; - ne = min(howmany, ctrl->num_entire); - - for (i = 0; i < ne; i++) { - udsl_fill_cell_header(target, ctrl->atm_data.vcc); - target += ATM_CELL_HEADER; - memcpy(target, skb->data, ATM_CELL_PAYLOAD); - target += ATM_CELL_PAYLOAD; - if (instance->snd_padding) { - memset(target, 0, instance->snd_padding); - target += instance->snd_padding; - } - __skb_pull(skb, ATM_CELL_PAYLOAD); - } - - ctrl->num_entire -= ne; - - if (!(ctrl->num_cells -= ne) || !(howmany -= ne)) - goto out; - - udsl_fill_cell_header(target, ctrl->atm_data.vcc); - target += ATM_CELL_HEADER; - memcpy(target, skb->data, skb->len); - target += skb->len; - __skb_pull(skb, skb->len); - memset(target, 0, ctrl->pdu_padding); - target += ctrl->pdu_padding; - - if (--ctrl->num_cells) { - if (!--howmany) { - ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; - goto out; - } - - if (instance->snd_padding) { - memset(target, 0, instance->snd_padding); - target += instance->snd_padding; - } - udsl_fill_cell_header(target, ctrl->atm_data.vcc); - target += ATM_CELL_HEADER; - memset(target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); - target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; - - --ctrl->num_cells; - UDSL_ASSERT(!ctrl->num_cells); - } - - memcpy(target, ctrl->aal5_trailer, ATM_AAL5_TRAILER); - target += ATM_AAL5_TRAILER; - /* set pti bit in last cell */ - *(target + 3 - ATM_CELL_SIZE) |= 0x2; - if (instance->snd_padding) { - memset(target, 0, instance->snd_padding); - target += instance->snd_padding; - } - out: - *target_p = target; - return nc - ctrl->num_cells; -} - -/************** -** receive ** -**************/ - -static void udsl_complete_receive(struct urb *urb, struct pt_regs *regs) -{ - struct udsl_receive_buffer *buf; - struct udsl_instance_data *instance; - struct udsl_receiver *rcv; - unsigned long flags; - - if (!urb || !(rcv = urb->context)) { - dbg("udsl_complete_receive: bad urb!"); - return; - } - - instance = rcv->instance; - buf = rcv->buffer; - - buf->filled_cells = urb->actual_length / (ATM_CELL_SIZE + instance->rcv_padding); - - vdbg("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf); - - UDSL_ASSERT(buf->filled_cells <= rcv_buf_size); - - /* may not be in_interrupt() */ - spin_lock_irqsave(&instance->receive_lock, flags); - list_add(&rcv->list, &instance->spare_receivers); - list_add_tail(&buf->list, &instance->filled_receive_buffers); - if (likely(!urb->status)) - tasklet_schedule(&instance->receive_tasklet); - spin_unlock_irqrestore(&instance->receive_lock, flags); -} - -static void udsl_process_receive(unsigned long data) -{ - struct udsl_receive_buffer *buf; - struct udsl_instance_data *instance = (struct udsl_instance_data *)data; - struct udsl_receiver *rcv; - int err; - - made_progress: - while (!list_empty(&instance->spare_receive_buffers)) { - spin_lock_irq(&instance->receive_lock); - if (list_empty(&instance->spare_receivers)) { - spin_unlock_irq(&instance->receive_lock); - break; - } - rcv = list_entry(instance->spare_receivers.next, - struct udsl_receiver, list); - list_del(&rcv->list); - spin_unlock_irq(&instance->receive_lock); - - buf = list_entry(instance->spare_receive_buffers.next, - struct udsl_receive_buffer, list); - list_del(&buf->list); - - rcv->buffer = buf; - - usb_fill_bulk_urb(rcv->urb, instance->usb_dev, - usb_rcvbulkpipe(instance->usb_dev, instance->data_endpoint), - buf->base, - rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding), - udsl_complete_receive, rcv); - - vdbg("udsl_process_receive: sending urb 0x%p, rcv 0x%p, buf 0x%p", - rcv->urb, rcv, buf); - - if ((err = usb_submit_urb(rcv->urb, GFP_ATOMIC)) < 0) { - dbg("udsl_process_receive: urb submission failed (%d)!", err); - list_add(&buf->list, &instance->spare_receive_buffers); - spin_lock_irq(&instance->receive_lock); - list_add(&rcv->list, &instance->spare_receivers); - spin_unlock_irq(&instance->receive_lock); - break; - } - } - - spin_lock_irq(&instance->receive_lock); - if (list_empty(&instance->filled_receive_buffers)) { - spin_unlock_irq(&instance->receive_lock); - return; /* done - no more buffers */ - } - buf = list_entry(instance->filled_receive_buffers.next, - struct udsl_receive_buffer, list); - list_del(&buf->list); - spin_unlock_irq(&instance->receive_lock); - - vdbg("udsl_process_receive: processing buf 0x%p", buf); - udsl_extract_cells(instance, buf->base, buf->filled_cells); - list_add(&buf->list, &instance->spare_receive_buffers); - goto made_progress; -} - -/*********** -** send ** -***********/ - -static void udsl_complete_send(struct urb *urb, struct pt_regs *regs) -{ - struct udsl_instance_data *instance; - struct udsl_sender *snd; - unsigned long flags; - - if (!urb || !(snd = urb->context) || !(instance = snd->instance)) { - dbg("udsl_complete_send: bad urb!"); - return; - } - - vdbg("udsl_complete_send: urb 0x%p, status %d, snd 0x%p, buf 0x%p", urb, - urb->status, snd, snd->buffer); - - /* may not be in_interrupt() */ - spin_lock_irqsave(&instance->send_lock, flags); - list_add(&snd->list, &instance->spare_senders); - list_add(&snd->buffer->list, &instance->spare_send_buffers); - tasklet_schedule(&instance->send_tasklet); - spin_unlock_irqrestore(&instance->send_lock, flags); -} - -static void udsl_process_send(unsigned long data) -{ - struct udsl_send_buffer *buf; - struct udsl_instance_data *instance = (struct udsl_instance_data *)data; - struct sk_buff *skb; - struct udsl_sender *snd; - int err; - unsigned int num_written; - - made_progress: - spin_lock_irq(&instance->send_lock); - while (!list_empty(&instance->spare_senders)) { - if (!list_empty(&instance->filled_send_buffers)) { - buf = list_entry(instance->filled_send_buffers.next, - struct udsl_send_buffer, list); - list_del(&buf->list); - } else if ((buf = instance->current_buffer)) { - instance->current_buffer = NULL; - } else /* all buffers empty */ - break; - - snd = list_entry(instance->spare_senders.next, - struct udsl_sender, list); - list_del(&snd->list); - spin_unlock_irq(&instance->send_lock); - - snd->buffer = buf; - usb_fill_bulk_urb(snd->urb, instance->usb_dev, - usb_sndbulkpipe(instance->usb_dev, instance->data_endpoint), - buf->base, - (snd_buf_size - buf->free_cells) * (ATM_CELL_SIZE + instance->snd_padding), - udsl_complete_send, snd); - - vdbg("udsl_process_send: submitting urb 0x%p (%d cells), snd 0x%p, buf 0x%p", - snd->urb, snd_buf_size - buf->free_cells, snd, buf); - - if ((err = usb_submit_urb(snd->urb, GFP_ATOMIC)) < 0) { - dbg("udsl_process_send: urb submission failed (%d)!", err); - spin_lock_irq(&instance->send_lock); - list_add(&snd->list, &instance->spare_senders); - spin_unlock_irq(&instance->send_lock); - list_add(&buf->list, &instance->filled_send_buffers); - return; /* bail out */ - } - - spin_lock_irq(&instance->send_lock); - } /* while */ - spin_unlock_irq(&instance->send_lock); - - if (!instance->current_skb) - instance->current_skb = skb_dequeue(&instance->sndqueue); - if (!instance->current_skb) - return; /* done - no more skbs */ - - skb = instance->current_skb; - - if (!(buf = instance->current_buffer)) { - spin_lock_irq(&instance->send_lock); - if (list_empty(&instance->spare_send_buffers)) { - instance->current_buffer = NULL; - spin_unlock_irq(&instance->send_lock); - return; /* done - no more buffers */ - } - buf = list_entry(instance->spare_send_buffers.next, - struct udsl_send_buffer, list); - list_del(&buf->list); - spin_unlock_irq(&instance->send_lock); - - buf->free_start = buf->base; - buf->free_cells = snd_buf_size; - - instance->current_buffer = buf; - } - - num_written = udsl_write_cells(instance, buf->free_cells, skb, &buf->free_start); - - vdbg("udsl_process_send: wrote %u cells from skb 0x%p to buffer 0x%p", - num_written, skb, buf); - - if (!(buf->free_cells -= num_written)) { - list_add_tail(&buf->list, &instance->filled_send_buffers); - instance->current_buffer = NULL; - } - - vdbg("udsl_process_send: buffer contains %d cells, %d left", - snd_buf_size - buf->free_cells, buf->free_cells); - - if (!UDSL_SKB(skb)->num_cells) { - struct atm_vcc *vcc = UDSL_SKB(skb)->atm_data.vcc; - - udsl_pop(vcc, skb); - instance->current_skb = NULL; - - atomic_inc(&vcc->stats->tx); - } - - goto made_progress; -} - -static void udsl_cancel_send(struct udsl_instance_data *instance, - struct atm_vcc *vcc) -{ - struct sk_buff *skb, *n; - - dbg("udsl_cancel_send entered"); - spin_lock_irq(&instance->sndqueue.lock); - for (skb = instance->sndqueue.next, n = skb->next; - skb != (struct sk_buff *)&instance->sndqueue; - skb = n, n = skb->next) - if (UDSL_SKB(skb)->atm_data.vcc == vcc) { - dbg("udsl_cancel_send: popping skb 0x%p", skb); - __skb_unlink(skb, &instance->sndqueue); - udsl_pop(vcc, skb); - } - spin_unlock_irq(&instance->sndqueue.lock); - - tasklet_disable(&instance->send_tasklet); - if ((skb = instance->current_skb) && (UDSL_SKB(skb)->atm_data.vcc == vcc)) { - dbg("udsl_cancel_send: popping current skb (0x%p)", skb); - instance->current_skb = NULL; - udsl_pop(vcc, skb); - } - tasklet_enable(&instance->send_tasklet); - dbg("udsl_cancel_send done"); -} - -static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct udsl_instance_data *instance = vcc->dev->dev_data; - int err; - - vdbg("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len); - - if (!instance) { - dbg("udsl_atm_send: NULL data!"); - err = -ENODEV; - goto fail; - } - - if (vcc->qos.aal != ATM_AAL5) { - dbg("udsl_atm_send: unsupported ATM type %d!", vcc->qos.aal); - err = -EINVAL; - goto fail; - } - - if (skb->len > ATM_MAX_AAL5_PDU) { - dbg("udsl_atm_send: packet too long (%d vs %d)!", skb->len, - ATM_MAX_AAL5_PDU); - err = -EINVAL; - goto fail; - } - - PACKETDEBUG(skb->data, skb->len); - - udsl_groom_skb(vcc, skb); - skb_queue_tail(&instance->sndqueue, skb); - tasklet_schedule(&instance->send_tasklet); - - return 0; - - fail: - udsl_pop(vcc, skb); - return err; -} - -/******************** -** bean counting ** -********************/ - -static void udsl_destroy_instance(struct kref *kref) -{ - struct udsl_instance_data *instance = - container_of(kref, struct udsl_instance_data, refcount); - - tasklet_kill(&instance->receive_tasklet); - tasklet_kill(&instance->send_tasklet); - usb_put_dev(instance->usb_dev); - kfree(instance); -} - -void udsl_get_instance(struct udsl_instance_data *instance) -{ - kref_get(&instance->refcount); -} - -void udsl_put_instance(struct udsl_instance_data *instance) -{ - kref_put(&instance->refcount, udsl_destroy_instance); -} - -/********** -** ATM ** -**********/ - -static void udsl_atm_dev_close(struct atm_dev *dev) -{ - struct udsl_instance_data *instance = dev->dev_data; - - dev->dev_data = NULL; - udsl_put_instance(instance); -} - -static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page) -{ - struct udsl_instance_data *instance = atm_dev->dev_data; - int left = *pos; - - if (!instance) { - dbg("udsl_atm_proc_read: NULL instance!"); - return -ENODEV; - } - - if (!left--) - return sprintf(page, "%s\n", instance->description); - - if (!left--) - return sprintf(page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", - atm_dev->esi[0], atm_dev->esi[1], - atm_dev->esi[2], atm_dev->esi[3], - atm_dev->esi[4], atm_dev->esi[5]); - - if (!left--) - return sprintf(page, - "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n", - atomic_read(&atm_dev->stats.aal5.tx), - atomic_read(&atm_dev->stats.aal5.tx_err), - atomic_read(&atm_dev->stats.aal5.rx), - atomic_read(&atm_dev->stats.aal5.rx_err), - atomic_read(&atm_dev->stats.aal5.rx_drop)); - - if (!left--) { - switch (atm_dev->signal) { - case ATM_PHY_SIG_FOUND: - sprintf(page, "Line up"); - break; - case ATM_PHY_SIG_LOST: - sprintf(page, "Line down"); - break; - default: - sprintf(page, "Line state unknown"); - break; - } - - if (instance->usb_dev->state == USB_STATE_NOTATTACHED) - strcat(page, ", disconnected\n"); - else { - if (instance->status == UDSL_LOADED_FIRMWARE) - strcat(page, ", firmware loaded\n"); - else if (instance->status == UDSL_LOADING_FIRMWARE) - strcat(page, ", firmware loading\n"); - else - strcat(page, ", no firmware\n"); - } - - return strlen(page); - } - - return 0; -} - -static int udsl_atm_open(struct atm_vcc *vcc) -{ - struct udsl_instance_data *instance = vcc->dev->dev_data; - struct udsl_vcc_data *new; - unsigned int max_pdu; - int vci = vcc->vci; - short vpi = vcc->vpi; - int err; - - dbg("udsl_atm_open: vpi %hd, vci %d", vpi, vci); - - if (!instance) { - dbg("udsl_atm_open: NULL data!"); - return -ENODEV; - } - - /* only support AAL5 */ - if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0) - || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) { - dbg("udsl_atm_open: unsupported ATM type %d!", vcc->qos.aal); - return -EINVAL; - } - - if (instance->firmware_wait && - (err = instance->firmware_wait(instance)) < 0) { - dbg("udsl_atm_open: firmware not loaded (%d)!", err); - return err; - } - - down(&instance->serialize); /* vs self, udsl_atm_close */ - - if (udsl_find_vcc(instance, vpi, vci)) { - dbg("udsl_atm_open: %hd/%d already in use!", vpi, vci); - up(&instance->serialize); - return -EADDRINUSE; - } - - if (!(new = kmalloc(sizeof(struct udsl_vcc_data), GFP_KERNEL))) { - dbg("udsl_atm_open: no memory for vcc_data!"); - up(&instance->serialize); - return -ENOMEM; - } - - memset(new, 0, sizeof(struct udsl_vcc_data)); - new->vcc = vcc; - new->vpi = vpi; - new->vci = vci; - - /* udsl_extract_cells requires at least one cell */ - max_pdu = max(1, UDSL_NUM_CELLS(vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD; - if (!(new->sarb = alloc_skb(max_pdu, GFP_KERNEL))) { - dbg("udsl_atm_open: no memory for SAR buffer!"); - kfree(new); - up(&instance->serialize); - return -ENOMEM; - } - - vcc->dev_data = new; - - tasklet_disable(&instance->receive_tasklet); - list_add(&new->list, &instance->vcc_list); - tasklet_enable(&instance->receive_tasklet); - - set_bit(ATM_VF_ADDR, &vcc->flags); - set_bit(ATM_VF_PARTIAL, &vcc->flags); - set_bit(ATM_VF_READY, &vcc->flags); - - up(&instance->serialize); - - tasklet_schedule(&instance->receive_tasklet); - - dbg("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, max_pdu); - - return 0; -} - -static void udsl_atm_close(struct atm_vcc *vcc) -{ - struct udsl_instance_data *instance = vcc->dev->dev_data; - struct udsl_vcc_data *vcc_data = vcc->dev_data; - - dbg("udsl_atm_close called"); - - if (!instance || !vcc_data) { - dbg("udsl_atm_close: NULL data!"); - return; - } - - dbg("udsl_atm_close: deallocating vcc 0x%p with vpi %d vci %d", - vcc_data, vcc_data->vpi, vcc_data->vci); - - udsl_cancel_send(instance, vcc); - - down(&instance->serialize); /* vs self, udsl_atm_open */ - - tasklet_disable(&instance->receive_tasklet); - list_del(&vcc_data->list); - tasklet_enable(&instance->receive_tasklet); - - kfree_skb(vcc_data->sarb); - vcc_data->sarb = NULL; - - kfree(vcc_data); - vcc->dev_data = NULL; - - vcc->vpi = ATM_VPI_UNSPEC; - vcc->vci = ATM_VCI_UNSPEC; - clear_bit(ATM_VF_READY, &vcc->flags); - clear_bit(ATM_VF_PARTIAL, &vcc->flags); - clear_bit(ATM_VF_ADDR, &vcc->flags); - - up(&instance->serialize); - - dbg("udsl_atm_close successful"); -} - -static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd, - void __user * arg) -{ - switch (cmd) { - case ATM_QUERYLOOP: - return put_user(ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0; - default: - return -ENOIOCTLCMD; - } -} - -/********** -** USB ** -**********/ - -int udsl_instance_setup(struct usb_device *dev, - struct udsl_instance_data *instance) -{ - char *buf; - int i, length; - - kref_init(&instance->refcount); /* one for USB */ - udsl_get_instance(instance); /* one for ATM */ - - init_MUTEX(&instance->serialize); - - instance->usb_dev = dev; - - INIT_LIST_HEAD(&instance->vcc_list); - - instance->status = UDSL_NO_FIRMWARE; - init_waitqueue_head(&instance->firmware_waiters); - - spin_lock_init(&instance->receive_lock); - INIT_LIST_HEAD(&instance->spare_receivers); - INIT_LIST_HEAD(&instance->filled_receive_buffers); - - tasklet_init(&instance->receive_tasklet, udsl_process_receive, (unsigned long)instance); - INIT_LIST_HEAD(&instance->spare_receive_buffers); - - skb_queue_head_init(&instance->sndqueue); - - spin_lock_init(&instance->send_lock); - INIT_LIST_HEAD(&instance->spare_senders); - INIT_LIST_HEAD(&instance->spare_send_buffers); - - tasklet_init(&instance->send_tasklet, udsl_process_send, - (unsigned long)instance); - INIT_LIST_HEAD(&instance->filled_send_buffers); - - /* receive init */ - for (i = 0; i < num_rcv_urbs; i++) { - struct udsl_receiver *rcv = &(instance->receivers[i]); - - if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) { - dbg("udsl_usb_probe: no memory for receive urb %d!", i); - goto fail; - } - - rcv->instance = instance; - - list_add(&rcv->list, &instance->spare_receivers); - } - - for (i = 0; i < num_rcv_bufs; i++) { - struct udsl_receive_buffer *buf = - &(instance->receive_buffers[i]); - - buf->base = kmalloc(rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding), - GFP_KERNEL); - if (!buf->base) { - dbg("udsl_usb_probe: no memory for receive buffer %d!", i); - goto fail; - } - - list_add(&buf->list, &instance->spare_receive_buffers); - } - - /* send init */ - for (i = 0; i < num_snd_urbs; i++) { - struct udsl_sender *snd = &(instance->senders[i]); - - if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) { - dbg("udsl_usb_probe: no memory for send urb %d!", i); - goto fail; - } - - snd->instance = instance; - - list_add(&snd->list, &instance->spare_senders); - } - - for (i = 0; i < num_snd_bufs; i++) { - struct udsl_send_buffer *buf = &(instance->send_buffers[i]); - - buf->base = kmalloc(snd_buf_size * (ATM_CELL_SIZE + instance->snd_padding), - GFP_KERNEL); - if (!buf->base) { - dbg("udsl_usb_probe: no memory for send buffer %d!", i); - goto fail; - } - - list_add(&buf->list, &instance->spare_send_buffers); - } - - /* ATM init */ - instance->atm_dev = atm_dev_register(instance->driver_name, - &udsl_atm_devops, -1, NULL); - if (!instance->atm_dev) { - dbg("udsl_usb_probe: failed to register ATM device!"); - goto fail; - } - - instance->atm_dev->ci_range.vpi_bits = ATM_CI_MAX; - instance->atm_dev->ci_range.vci_bits = ATM_CI_MAX; - instance->atm_dev->signal = ATM_PHY_SIG_UNKNOWN; - - /* temp init ATM device, set to 128kbit */ - instance->atm_dev->link_rate = 128 * 1000 / 424; - - /* device description */ - buf = instance->description; - length = sizeof(instance->description); - - if ((i = usb_string(dev, dev->descriptor.iProduct, buf, length)) < 0) - goto finish; - - buf += i; - length -= i; - - i = scnprintf(buf, length, " ("); - buf += i; - length -= i; - - if (length <= 0 || (i = usb_make_path(dev, buf, length)) < 0) - goto finish; - - buf += i; - length -= i; - - snprintf(buf, length, ")"); - - finish: - /* ready for ATM callbacks */ - wmb(); - instance->atm_dev->dev_data = instance; - - usb_get_dev(dev); - - return 0; - - fail: - for (i = 0; i < num_snd_bufs; i++) - kfree(instance->send_buffers[i].base); - - for (i = 0; i < num_snd_urbs; i++) - usb_free_urb(instance->senders[i].urb); - - for (i = 0; i < num_rcv_bufs; i++) - kfree(instance->receive_buffers[i].base); - - for (i = 0; i < num_rcv_urbs; i++) - usb_free_urb(instance->receivers[i].urb); - - return -ENOMEM; -} - -void udsl_instance_disconnect(struct udsl_instance_data *instance) -{ - int i; - - dbg("udsl_instance_disconnect entered"); - - if (!instance) { - dbg("udsl_instance_disconnect: NULL instance!"); - return; - } - - /* receive finalize */ - tasklet_disable(&instance->receive_tasklet); - - for (i = 0; i < num_rcv_urbs; i++) - usb_kill_urb(instance->receivers[i].urb); - - /* no need to take the spinlock */ - INIT_LIST_HEAD(&instance->filled_receive_buffers); - INIT_LIST_HEAD(&instance->spare_receive_buffers); - - tasklet_enable(&instance->receive_tasklet); - - for (i = 0; i < num_rcv_urbs; i++) - usb_free_urb(instance->receivers[i].urb); - - for (i = 0; i < num_rcv_bufs; i++) - kfree(instance->receive_buffers[i].base); - - /* send finalize */ - tasklet_disable(&instance->send_tasklet); - - for (i = 0; i < num_snd_urbs; i++) - usb_kill_urb(instance->senders[i].urb); - - /* no need to take the spinlock */ - INIT_LIST_HEAD(&instance->spare_senders); - INIT_LIST_HEAD(&instance->spare_send_buffers); - instance->current_buffer = NULL; - - tasklet_enable(&instance->send_tasklet); - - for (i = 0; i < num_snd_urbs; i++) - usb_free_urb(instance->senders[i].urb); - - for (i = 0; i < num_snd_bufs; i++) - kfree(instance->send_buffers[i].base); - - /* ATM finalize */ - shutdown_atm_dev(instance->atm_dev); -} - -EXPORT_SYMBOL_GPL(udsl_get_instance); -EXPORT_SYMBOL_GPL(udsl_put_instance); -EXPORT_SYMBOL_GPL(udsl_instance_setup); -EXPORT_SYMBOL_GPL(udsl_instance_disconnect); - -/*********** -** init ** -***********/ - -static int __init udsl_usb_init(void) -{ - dbg("udsl_usb_init: driver version " DRIVER_VERSION); - - if (sizeof(struct udsl_control) > sizeof(((struct sk_buff *) 0)->cb)) { - printk(KERN_ERR __FILE__ ": unusable with this kernel!\n"); - return -EIO; - } - - if ((num_rcv_urbs > UDSL_MAX_RCV_URBS) - || (num_snd_urbs > UDSL_MAX_SND_URBS) - || (num_rcv_bufs > UDSL_MAX_RCV_BUFS) - || (num_snd_bufs > UDSL_MAX_SND_BUFS) - || (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE) - || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE)) - return -EINVAL; - - return 0; -} - -static void __exit udsl_usb_exit(void) -{ -} - -module_init(udsl_usb_init); -module_exit(udsl_usb_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRIVER_VERSION); - -/************ -** debug ** -************/ - -#ifdef VERBOSE_DEBUG -static int udsl_print_packet(const unsigned char *data, int len) -{ - unsigned char buffer[256]; - int i = 0, j = 0; - - for (i = 0; i < len;) { - buffer[0] = '\0'; - sprintf(buffer, "%.3d :", i); - for (j = 0; (j < 16) && (i < len); j++, i++) { - sprintf(buffer, "%s %2.2x", buffer, data[i]); - } - dbg("%s", buffer); - } - return i; -} -#endif diff --git a/drivers/usb/atm/usb_atm.h b/drivers/usb/atm/usb_atm.h deleted file mode 100644 index cf8c532..0000000 --- a/drivers/usb/atm/usb_atm.h +++ /dev/null @@ -1,176 +0,0 @@ -/****************************************************************************** - * usb_atm.h - Generic USB xDSL driver core - * - * Copyright (C) 2001, Alcatel - * Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas - * Copyright (C) 2004, David Woodhouse - * - * 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. - * - ******************************************************************************/ - -#include <linux/config.h> -#include <linux/list.h> -#include <linux/kref.h> -#include <linux/atm.h> -#include <linux/atmdev.h> -#include <asm/semaphore.h> - -/* -#define DEBUG -#define VERBOSE_DEBUG -*/ - -#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) -# define DEBUG -#endif - -#include <linux/usb.h> - -#ifdef DEBUG -#define UDSL_ASSERT(x) BUG_ON(!(x)) -#else -#define UDSL_ASSERT(x) do { if (!(x)) warn("failed assertion '" #x "' at line %d", __LINE__); } while(0) -#endif - -#define UDSL_MAX_RCV_URBS 4 -#define UDSL_MAX_SND_URBS 4 -#define UDSL_MAX_RCV_BUFS 8 -#define UDSL_MAX_SND_BUFS 8 -#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */ -#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */ -#define UDSL_DEFAULT_RCV_URBS 2 -#define UDSL_DEFAULT_SND_URBS 2 -#define UDSL_DEFAULT_RCV_BUFS 4 -#define UDSL_DEFAULT_SND_BUFS 4 -#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */ -#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */ - -#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD) -#define UDSL_NUM_CELLS(x) (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD) - -/* receive */ - -struct udsl_receive_buffer { - struct list_head list; - unsigned char *base; - unsigned int filled_cells; -}; - -struct udsl_receiver { - struct list_head list; - struct udsl_receive_buffer *buffer; - struct urb *urb; - struct udsl_instance_data *instance; -}; - -struct udsl_vcc_data { - /* vpi/vci lookup */ - struct list_head list; - short vpi; - int vci; - struct atm_vcc *vcc; - - /* raw cell reassembly */ - struct sk_buff *sarb; -}; - -/* send */ - -struct udsl_send_buffer { - struct list_head list; - unsigned char *base; - unsigned char *free_start; - unsigned int free_cells; -}; - -struct udsl_sender { - struct list_head list; - struct udsl_send_buffer *buffer; - struct urb *urb; - struct udsl_instance_data *instance; -}; - -struct udsl_control { - struct atm_skb_data atm_data; - unsigned int num_cells; - unsigned int num_entire; - unsigned int pdu_padding; - unsigned char aal5_trailer[ATM_AAL5_TRAILER]; -}; - -#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb) - -/* main driver data */ - -enum udsl_status { - UDSL_NO_FIRMWARE, - UDSL_LOADING_FIRMWARE, - UDSL_LOADED_FIRMWARE -}; - -struct udsl_instance_data { - struct kref refcount; - struct semaphore serialize; - - /* USB device part */ - struct usb_device *usb_dev; - char description[64]; - int data_endpoint; - int snd_padding; - int rcv_padding; - const char *driver_name; - - /* ATM device part */ - struct atm_dev *atm_dev; - struct list_head vcc_list; - - /* firmware */ - int (*firmware_wait) (struct udsl_instance_data *); - enum udsl_status status; - wait_queue_head_t firmware_waiters; - - /* receive */ - struct udsl_receiver receivers[UDSL_MAX_RCV_URBS]; - struct udsl_receive_buffer receive_buffers[UDSL_MAX_RCV_BUFS]; - - spinlock_t receive_lock; - struct list_head spare_receivers; - struct list_head filled_receive_buffers; - - struct tasklet_struct receive_tasklet; - struct list_head spare_receive_buffers; - - /* send */ - struct udsl_sender senders[UDSL_MAX_SND_URBS]; - struct udsl_send_buffer send_buffers[UDSL_MAX_SND_BUFS]; - - struct sk_buff_head sndqueue; - - spinlock_t send_lock; - struct list_head spare_senders; - struct list_head spare_send_buffers; - - struct tasklet_struct send_tasklet; - struct sk_buff *current_skb; /* being emptied */ - struct udsl_send_buffer *current_buffer; /* being filled */ - struct list_head filled_send_buffers; -}; - -extern int udsl_instance_setup(struct usb_device *dev, - struct udsl_instance_data *instance); -extern void udsl_instance_disconnect(struct udsl_instance_data *instance); -extern void udsl_get_instance(struct udsl_instance_data *instance); -extern void udsl_put_instance(struct udsl_instance_data *instance); diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c new file mode 100644 index 0000000..bb1db19 --- /dev/null +++ b/drivers/usb/atm/usbatm.c @@ -0,0 +1,1230 @@ +/****************************************************************************** + * usbatm.c - Generic USB xDSL driver core + * + * Copyright (C) 2001, Alcatel + * Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas + * Copyright (C) 2004, David Woodhouse, Roman Kagan + * + * 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. + * + ******************************************************************************/ + +/* + * Written by Johan Verrept, Duncan Sands (duncan.sands@free.fr) and David Woodhouse + * + * 1.7+: - See the check-in logs + * + * 1.6: - No longer opens a connection if the firmware is not loaded + * - Added support for the speedtouch 330 + * - Removed the limit on the number of devices + * - Module now autoloads on device plugin + * - Merged relevant parts of sarlib + * - Replaced the kernel thread with a tasklet + * - New packet transmission code + * - Changed proc file contents + * - Fixed all known SMP races + * - Many fixes and cleanups + * - Various fixes by Oliver Neukum (oliver@neukum.name) + * + * 1.5A: - Version for inclusion in 2.5 series kernel + * - Modifications by Richard Purdie (rpurdie@rpsys.net) + * - made compatible with kernel 2.5.6 onwards by changing + * usbatm_usb_send_data_context->urb to a pointer and adding code + * to alloc and free it + * - remove_wait_queue() added to usbatm_atm_processqueue_thread() + * + * 1.5: - fixed memory leak when atmsar_decode_aal5 returned NULL. + * (reported by stephen.robinson@zen.co.uk) + * + * 1.4: - changed the spin_lock() under interrupt to spin_lock_irqsave() + * - unlink all active send urbs of a vcc that is being closed. + * + * 1.3.1: - added the version number + * + * 1.3: - Added multiple send urb support + * - fixed memory leak and vcc->tx_inuse starvation bug + * when not enough memory left in vcc. + * + * 1.2: - Fixed race condition in usbatm_usb_send_data() + * 1.1: - Turned off packet debugging + * + */ + +#include "usbatm.h" + +#include <asm/uaccess.h> +#include <linux/crc32.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/proc_fs.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> +#include <linux/stat.h> +#include <linux/timer.h> +#include <linux/wait.h> + +#ifdef VERBOSE_DEBUG +static int usbatm_print_packet(const unsigned char *data, int len); +#define PACKETDEBUG(arg...) usbatm_print_packet (arg) +#define vdbg(arg...) dbg (arg) +#else +#define PACKETDEBUG(arg...) +#define vdbg(arg...) +#endif + +#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <duncan.sands@free.fr>" +#define DRIVER_VERSION "1.9" +#define DRIVER_DESC "Generic USB ATM/DSL I/O, version " DRIVER_VERSION + +static const char usbatm_driver_name[] = "usbatm"; + +#define UDSL_MAX_RCV_URBS 16 +#define UDSL_MAX_SND_URBS 16 +#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */ +#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */ +#define UDSL_DEFAULT_RCV_URBS 4 +#define UDSL_DEFAULT_SND_URBS 4 +#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */ +#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */ + +#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD) + +#define THROTTLE_MSECS 100 /* delay to recover processing after urb submission fails */ + +static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS; +static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS; +static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE; +static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE; + +module_param(num_rcv_urbs, uint, S_IRUGO); +MODULE_PARM_DESC(num_rcv_urbs, + "Number of urbs used for reception (range: 0-" + __MODULE_STRING(UDSL_MAX_RCV_URBS) ", default: " + __MODULE_STRING(UDSL_DEFAULT_RCV_URBS) ")"); + +module_param(num_snd_urbs, uint, S_IRUGO); +MODULE_PARM_DESC(num_snd_urbs, + "Number of urbs used for transmission (range: 0-" + __MODULE_STRING(UDSL_MAX_SND_URBS) ", default: " + __MODULE_STRING(UDSL_DEFAULT_SND_URBS) ")"); + +module_param(rcv_buf_size, uint, S_IRUGO); +MODULE_PARM_DESC(rcv_buf_size, + "Size of the buffers used for reception in ATM cells (range: 1-" + __MODULE_STRING(UDSL_MAX_RCV_BUF_SIZE) ", default: " + __MODULE_STRING(UDSL_DEFAULT_RCV_BUF_SIZE) ")"); + +module_param(snd_buf_size, uint, S_IRUGO); +MODULE_PARM_DESC(snd_buf_size, + "Size of the buffers used for transmission in ATM cells (range: 1-" + __MODULE_STRING(UDSL_MAX_SND_BUF_SIZE) ", default: " + __MODULE_STRING(UDSL_DEFAULT_SND_BUF_SIZE) ")"); + + +/* receive */ + +struct usbatm_vcc_data { + /* vpi/vci lookup */ + struct list_head list; + short vpi; + int vci; + struct atm_vcc *vcc; + + /* raw cell reassembly */ + struct sk_buff *sarb; +}; + + +/* send */ + +struct usbatm_control { + struct atm_skb_data atm; + u32 len; + u32 crc; +}; + +#define UDSL_SKB(x) ((struct usbatm_control *)(x)->cb) + + +/* ATM */ + +static void usbatm_atm_dev_close(struct atm_dev *dev); +static int usbatm_atm_open(struct atm_vcc *vcc); +static void usbatm_atm_close(struct atm_vcc *vcc); +static int usbatm_atm_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg); +static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb); +static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page); + +static struct atmdev_ops usbatm_atm_devops = { + .dev_close = usbatm_atm_dev_close, + .open = usbatm_atm_open, + .close = usbatm_atm_close, + .ioctl = usbatm_atm_ioctl, + .send = usbatm_atm_send, + .proc_read = usbatm_atm_proc_read, + .owner = THIS_MODULE, +}; + + +/*********** +** misc ** +***********/ + +static inline unsigned int usbatm_pdu_length(unsigned int length) +{ + length += ATM_CELL_PAYLOAD - 1 + ATM_AAL5_TRAILER; + return length - length % ATM_CELL_PAYLOAD; +} + +static inline void usbatm_pop(struct atm_vcc *vcc, struct sk_buff *skb) +{ + if (vcc->pop) + vcc->pop(vcc, skb); + else + dev_kfree_skb(skb); +} + + +/*********** +** urbs ** +************/ + +static inline struct urb *usbatm_pop_urb(struct usbatm_channel *channel) +{ + struct urb *urb; + + spin_lock_irq(&channel->lock); + if (list_empty(&channel->list)) { + spin_unlock_irq(&channel->lock); + return NULL; + } + + urb = list_entry(channel->list.next, struct urb, urb_list); + list_del(&urb->urb_list); + spin_unlock_irq(&channel->lock); + + return urb; +} + +static inline int usbatm_submit_urb(struct urb *urb) +{ + struct usbatm_channel *channel = urb->context; + int ret; + + vdbg("%s: submitting urb 0x%p, size %u", + __func__, urb, urb->transfer_buffer_length); + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) { + atm_dbg(channel->usbatm, "%s: urb 0x%p submission failed (%d)!\n", + __func__, urb, ret); + + /* consider all errors transient and return the buffer back to the queue */ + urb->status = -EAGAIN; + spin_lock_irq(&channel->lock); + + /* must add to the front when sending; doesn't matter when receiving */ + list_add(&urb->urb_list, &channel->list); + + spin_unlock_irq(&channel->lock); + + /* make sure the channel doesn't stall */ + mod_timer(&channel->delay, jiffies + msecs_to_jiffies(THROTTLE_MSECS)); + } + + return ret; +} + +static void usbatm_complete(struct urb *urb, struct pt_regs *regs) +{ + struct usbatm_channel *channel = urb->context; + unsigned long flags; + + vdbg("%s: urb 0x%p, status %d, actual_length %d", + __func__, urb, urb->status, urb->actual_length); + + /* usually in_interrupt(), but not always */ + spin_lock_irqsave(&channel->lock, flags); + + /* must add to the back when receiving; doesn't matter when sending */ + list_add_tail(&urb->urb_list, &channel->list); + + spin_unlock_irqrestore(&channel->lock, flags); + + if (unlikely(urb->status)) + /* throttle processing in case of an error */ + mod_timer(&channel->delay, jiffies + msecs_to_jiffies(THROTTLE_MSECS)); + else + tasklet_schedule(&channel->tasklet); +} + + +/************* +** decode ** +*************/ + +static inline struct usbatm_vcc_data *usbatm_find_vcc(struct usbatm_data *instance, + short vpi, int vci) +{ + struct usbatm_vcc_data *vcc; + + list_for_each_entry(vcc, &instance->vcc_list, list) + if ((vcc->vci == vci) && (vcc->vpi == vpi)) + return vcc; + return NULL; +} + +static void usbatm_extract_cells(struct usbatm_data *instance, + unsigned char *source, unsigned int avail_data) +{ + struct usbatm_vcc_data *cached_vcc = NULL; + struct atm_vcc *vcc; + struct sk_buff *sarb; + unsigned int stride = instance->rx_channel.stride; + int vci, cached_vci = 0; + short vpi, cached_vpi = 0; + u8 pti; + + for (; avail_data >= stride; avail_data -= stride, source += stride) { + vpi = ((source[0] & 0x0f) << 4) | (source[1] >> 4); + vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4); + pti = ((source[3] & 0xe) >> 1); + + vdbg("%s: vpi %hd, vci %d, pti %d", __func__, vpi, vci, pti); + + if ((vci != cached_vci) || (vpi != cached_vpi)) { + cached_vpi = vpi; + cached_vci = vci; + + cached_vcc = usbatm_find_vcc(instance, vpi, vci); + + if (!cached_vcc) + atm_dbg(instance, "%s: unknown vpi/vci (%hd/%d)!\n", __func__, vpi, vci); + } + + if (!cached_vcc) + continue; + + vcc = cached_vcc->vcc; + + /* OAM F5 end-to-end */ + if (pti == ATM_PTI_E2EF5) { + atm_warn(instance, "%s: OAM not supported (vpi %d, vci %d)!\n", __func__, vpi, vci); + atomic_inc(&vcc->stats->rx_err); + continue; + } + + sarb = cached_vcc->sarb; + + if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) { + atm_dbg(instance, "%s: buffer overrun (sarb->len %u, vcc: 0x%p)!\n", + __func__, sarb->len, vcc); + /* discard cells already received */ + skb_trim(sarb, 0); + UDSL_ASSERT(sarb->tail + ATM_CELL_PAYLOAD <= sarb->end); + } + + memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD); + __skb_put(sarb, ATM_CELL_PAYLOAD); + + if (pti & 1) { + struct sk_buff *skb; + unsigned int length; + unsigned int pdu_length; + + length = (source[ATM_CELL_SIZE - 6] << 8) + source[ATM_CELL_SIZE - 5]; + + /* guard against overflow */ + if (length > ATM_MAX_AAL5_PDU) { + atm_dbg(instance, "%s: bogus length %u (vcc: 0x%p)!\n", + __func__, length, vcc); + atomic_inc(&vcc->stats->rx_err); + goto out; + } + + pdu_length = usbatm_pdu_length(length); + + if (sarb->len < pdu_length) { + atm_dbg(instance, "%s: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!\n", + __func__, pdu_length, sarb->len, vcc); + atomic_inc(&vcc->stats->rx_err); + goto out; + } + + if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) { + atm_dbg(instance, "%s: packet failed crc check (vcc: 0x%p)!\n", + __func__, vcc); + atomic_inc(&vcc->stats->rx_err); + goto out; + } + + vdbg("%s: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", __func__, length, pdu_length, vcc); + + if (!(skb = dev_alloc_skb(length))) { + atm_dbg(instance, "%s: no memory for skb (length: %u)!\n", __func__, length); + atomic_inc(&vcc->stats->rx_drop); + goto out; + } + + vdbg("%s: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", __func__, skb, skb->truesize); + + if (!atm_charge(vcc, skb->truesize)) { + atm_dbg(instance, "%s: failed atm_charge (skb->truesize: %u)!\n", __func__, skb->truesize); + dev_kfree_skb(skb); + goto out; /* atm_charge increments rx_drop */ + } + + memcpy(skb->data, sarb->tail - pdu_length, length); + __skb_put(skb, length); + + vdbg("%s: sending skb 0x%p, skb->len %u, skb->truesize %u", + __func__, skb, skb->len, skb->truesize); + + PACKETDEBUG(skb->data, skb->len); + + vcc->push(vcc, skb); + + atomic_inc(&vcc->stats->rx); + out: + skb_trim(sarb, 0); + } + } +} + + +/************* +** encode ** +*************/ + +static unsigned int usbatm_write_cells(struct usbatm_data *instance, + struct sk_buff *skb, + u8 *target, unsigned int avail_space) +{ + struct usbatm_control *ctrl = UDSL_SKB(skb); + struct atm_vcc *vcc = ctrl->atm.vcc; + unsigned int num_written; + unsigned int stride = instance->tx_channel.stride; + + vdbg("%s: skb->len=%d, avail_space=%u", __func__, skb->len, avail_space); + UDSL_ASSERT(!(avail_space % stride)); + + for (num_written = 0; num_written < avail_space && ctrl->len; + num_written += stride, target += stride) { + unsigned int data_len = min_t(unsigned int, skb->len, ATM_CELL_PAYLOAD); + unsigned int left = ATM_CELL_PAYLOAD - data_len; + u8 *ptr = target; + + ptr[0] = vcc->vpi >> 4; + ptr[1] = (vcc->vpi << 4) | (vcc->vci >> 12); + ptr[2] = vcc->vci >> 4; + ptr[3] = vcc->vci << 4; + ptr[4] = 0xec; + ptr += ATM_CELL_HEADER; + + memcpy(ptr, skb->data, data_len); + ptr += data_len; + __skb_pull(skb, data_len); + + if(!left) + continue; + + memset(ptr, 0, left); + + if (left >= ATM_AAL5_TRAILER) { /* trailer will go in this cell */ + u8 *trailer = target + ATM_CELL_SIZE - ATM_AAL5_TRAILER; + /* trailer[0] = 0; UU = 0 */ + /* trailer[1] = 0; CPI = 0 */ + trailer[2] = ctrl->len >> 8; + trailer[3] = ctrl->len; + + ctrl->crc = ~ crc32_be(ctrl->crc, ptr, left - 4); + + trailer[4] = ctrl->crc >> 24; + trailer[5] = ctrl->crc >> 16; + trailer[6] = ctrl->crc >> 8; + trailer[7] = ctrl->crc; + + target[3] |= 0x2; /* adjust PTI */ + + ctrl->len = 0; /* tag this skb finished */ + } + else + ctrl->crc = crc32_be(ctrl->crc, ptr, left); + } + + return num_written; +} + + +/************** +** receive ** +**************/ + +static void usbatm_rx_process(unsigned long data) +{ + struct usbatm_data *instance = (struct usbatm_data *)data; + struct urb *urb; + + while ((urb = usbatm_pop_urb(&instance->rx_channel))) { + vdbg("%s: processing urb 0x%p", __func__, urb); + + if (usb_pipeisoc(urb->pipe)) { + int i; + for (i = 0; i < urb->number_of_packets; i++) + if (!urb->iso_frame_desc[i].status) + usbatm_extract_cells(instance, + (u8 *)urb->transfer_buffer + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + } + else + if (!urb->status) + usbatm_extract_cells(instance, urb->transfer_buffer, urb->actual_length); + + if (usbatm_submit_urb(urb)) + return; + } +} + + +/*********** +** send ** +***********/ + +static void usbatm_tx_process(unsigned long data) +{ + struct usbatm_data *instance = (struct usbatm_data *)data; + struct sk_buff *skb = instance->current_skb; + struct urb *urb = NULL; + const unsigned int buf_size = instance->tx_channel.buf_size; + unsigned int num_written = 0; + u8 *buffer = NULL; + + if (!skb) + skb = skb_dequeue(&instance->sndqueue); + + while (skb) { + if (!urb) { + urb = usbatm_pop_urb(&instance->tx_channel); + if (!urb) + break; /* no more senders */ + buffer = urb->transfer_buffer; + num_written = (urb->status == -EAGAIN) ? + urb->transfer_buffer_length : 0; + } + + num_written += usbatm_write_cells(instance, skb, + buffer + num_written, + buf_size - num_written); + + vdbg("%s: wrote %u bytes from skb 0x%p to urb 0x%p", + __func__, num_written, skb, urb); + + if (!UDSL_SKB(skb)->len) { + struct atm_vcc *vcc = UDSL_SKB(skb)->atm.vcc; + + usbatm_pop(vcc, skb); + atomic_inc(&vcc->stats->tx); + + skb = skb_dequeue(&instance->sndqueue); + } + + if (num_written == buf_size || (!skb && num_written)) { + urb->transfer_buffer_length = num_written; + + if (usbatm_submit_urb(urb)) + break; + urb = NULL; + } + } + + instance->current_skb = skb; +} + +static void usbatm_cancel_send(struct usbatm_data *instance, + struct atm_vcc *vcc) +{ + struct sk_buff *skb, *n; + + atm_dbg(instance, "%s entered\n", __func__); + spin_lock_irq(&instance->sndqueue.lock); + for (skb = instance->sndqueue.next, n = skb->next; + skb != (struct sk_buff *)&instance->sndqueue; + skb = n, n = skb->next) + if (UDSL_SKB(skb)->atm.vcc == vcc) { + atm_dbg(instance, "%s: popping skb 0x%p\n", __func__, skb); + __skb_unlink(skb, &instance->sndqueue); + usbatm_pop(vcc, skb); + } + spin_unlock_irq(&instance->sndqueue.lock); + + tasklet_disable(&instance->tx_channel.tasklet); + if ((skb = instance->current_skb) && (UDSL_SKB(skb)->atm.vcc == vcc)) { + atm_dbg(instance, "%s: popping current skb (0x%p)\n", __func__, skb); + instance->current_skb = NULL; + usbatm_pop(vcc, skb); + } + tasklet_enable(&instance->tx_channel.tasklet); + atm_dbg(instance, "%s done\n", __func__); +} + +static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb) +{ + struct usbatm_data *instance = vcc->dev->dev_data; + struct usbatm_control *ctrl = UDSL_SKB(skb); + int err; + + vdbg("%s called (skb 0x%p, len %u)", __func__, skb, skb->len); + + if (!instance) { + dbg("%s: NULL data!", __func__); + err = -ENODEV; + goto fail; + } + + if (vcc->qos.aal != ATM_AAL5) { + atm_dbg(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal); + err = -EINVAL; + goto fail; + } + + if (skb->len > ATM_MAX_AAL5_PDU) { + atm_dbg(instance, "%s: packet too long (%d vs %d)!\n", + __func__, skb->len, ATM_MAX_AAL5_PDU); + err = -EINVAL; + goto fail; + } + + PACKETDEBUG(skb->data, skb->len); + + /* initialize the control block */ + ctrl->atm.vcc = vcc; + ctrl->len = skb->len; + ctrl->crc = crc32_be(~0, skb->data, skb->len); + + skb_queue_tail(&instance->sndqueue, skb); + tasklet_schedule(&instance->tx_channel.tasklet); + + return 0; + + fail: + usbatm_pop(vcc, skb); + return err; +} + + +/******************** +** bean counting ** +********************/ + +static void usbatm_destroy_instance(struct kref *kref) +{ + struct usbatm_data *instance = container_of(kref, struct usbatm_data, refcount); + + dbg("%s", __func__); + + tasklet_kill(&instance->rx_channel.tasklet); + tasklet_kill(&instance->tx_channel.tasklet); + usb_put_dev(instance->usb_dev); + kfree(instance); +} + +void usbatm_get_instance(struct usbatm_data *instance) +{ + dbg("%s", __func__); + + kref_get(&instance->refcount); +} + +void usbatm_put_instance(struct usbatm_data *instance) +{ + dbg("%s", __func__); + + kref_put(&instance->refcount, usbatm_destroy_instance); +} + + +/********** +** ATM ** +**********/ + +static void usbatm_atm_dev_close(struct atm_dev *dev) +{ + struct usbatm_data *instance = dev->dev_data; + + dbg("%s", __func__); + + if (!instance) + return; + + dev->dev_data = NULL; + usbatm_put_instance(instance); /* taken in usbatm_atm_init */ +} + +static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page) +{ + struct usbatm_data *instance = atm_dev->dev_data; + int left = *pos; + + if (!instance) { + dbg("%s: NULL instance!", __func__); + return -ENODEV; + } + + if (!left--) + return sprintf(page, "%s\n", instance->description); + + if (!left--) + return sprintf(page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + atm_dev->esi[0], atm_dev->esi[1], + atm_dev->esi[2], atm_dev->esi[3], + atm_dev->esi[4], atm_dev->esi[5]); + + if (!left--) + return sprintf(page, + "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n", + atomic_read(&atm_dev->stats.aal5.tx), + atomic_read(&atm_dev->stats.aal5.tx_err), + atomic_read(&atm_dev->stats.aal5.rx), + atomic_read(&atm_dev->stats.aal5.rx_err), + atomic_read(&atm_dev->stats.aal5.rx_drop)); + + if (!left--) + switch (atm_dev->signal) { + case ATM_PHY_SIG_FOUND: + return sprintf(page, "Line up\n"); + case ATM_PHY_SIG_LOST: + return sprintf(page, "Line down\n"); + default: + return sprintf(page, "Line state unknown\n"); + } + + return 0; +} + +static int usbatm_atm_open(struct atm_vcc *vcc) +{ + struct usbatm_data *instance = vcc->dev->dev_data; + struct usbatm_vcc_data *new = NULL; + int ret; + int vci = vcc->vci; + short vpi = vcc->vpi; + + if (!instance) { + dbg("%s: NULL data!", __func__); + return -ENODEV; + } + + atm_dbg(instance, "%s: vpi %hd, vci %d\n", __func__, vpi, vci); + + /* only support AAL5 */ + if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0) + || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) { + atm_dbg(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal); + return -EINVAL; + } + + down(&instance->serialize); /* vs self, usbatm_atm_close */ + + if (usbatm_find_vcc(instance, vpi, vci)) { + atm_dbg(instance, "%s: %hd/%d already in use!\n", __func__, vpi, vci); + ret = -EADDRINUSE; + goto fail; + } + + if (!(new = kmalloc(sizeof(struct usbatm_vcc_data), GFP_KERNEL))) { + atm_dbg(instance, "%s: no memory for vcc_data!\n", __func__); + ret = -ENOMEM; + goto fail; + } + + memset(new, 0, sizeof(struct usbatm_vcc_data)); + new->vcc = vcc; + new->vpi = vpi; + new->vci = vci; + + new->sarb = alloc_skb(usbatm_pdu_length(vcc->qos.rxtp.max_sdu), GFP_KERNEL); + if (!new->sarb) { + atm_dbg(instance, "%s: no memory for SAR buffer!\n", __func__); + ret = -ENOMEM; + goto fail; + } + + vcc->dev_data = new; + + tasklet_disable(&instance->rx_channel.tasklet); + list_add(&new->list, &instance->vcc_list); + tasklet_enable(&instance->rx_channel.tasklet); + + set_bit(ATM_VF_ADDR, &vcc->flags); + set_bit(ATM_VF_PARTIAL, &vcc->flags); + set_bit(ATM_VF_READY, &vcc->flags); + + up(&instance->serialize); + + atm_dbg(instance, "%s: allocated vcc data 0x%p\n", __func__, new); + + return 0; + +fail: + kfree(new); + up(&instance->serialize); + return ret; +} + +static void usbatm_atm_close(struct atm_vcc *vcc) +{ + struct usbatm_data *instance = vcc->dev->dev_data; + struct usbatm_vcc_data *vcc_data = vcc->dev_data; + + if (!instance || !vcc_data) { + dbg("%s: NULL data!", __func__); + return; + } + + atm_dbg(instance, "%s entered\n", __func__); + + atm_dbg(instance, "%s: deallocating vcc 0x%p with vpi %d vci %d\n", + __func__, vcc_data, vcc_data->vpi, vcc_data->vci); + + usbatm_cancel_send(instance, vcc); + + down(&instance->serialize); /* vs self, usbatm_atm_open */ + + tasklet_disable(&instance->rx_channel.tasklet); + list_del(&vcc_data->list); + tasklet_enable(&instance->rx_channel.tasklet); + + kfree_skb(vcc_data->sarb); + vcc_data->sarb = NULL; + + kfree(vcc_data); + vcc->dev_data = NULL; + + vcc->vpi = ATM_VPI_UNSPEC; + vcc->vci = ATM_VCI_UNSPEC; + clear_bit(ATM_VF_READY, &vcc->flags); + clear_bit(ATM_VF_PARTIAL, &vcc->flags); + clear_bit(ATM_VF_ADDR, &vcc->flags); + + up(&instance->serialize); + + atm_dbg(instance, "%s successful\n", __func__); +} + +static int usbatm_atm_ioctl(struct atm_dev *dev, unsigned int cmd, + void __user * arg) +{ + switch (cmd) { + case ATM_QUERYLOOP: + return put_user(ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0; + default: + return -ENOIOCTLCMD; + } +} + +static int usbatm_atm_init(struct usbatm_data *instance) +{ + struct atm_dev *atm_dev; + int ret, i; + + /* ATM init */ + atm_dev = atm_dev_register(instance->driver_name, &usbatm_atm_devops, -1, NULL); + if (!atm_dev) { + usb_dbg(instance, "%s: failed to register ATM device!\n", __func__); + return -1; + } + + instance->atm_dev = atm_dev; + + atm_dev->ci_range.vpi_bits = ATM_CI_MAX; + atm_dev->ci_range.vci_bits = ATM_CI_MAX; + atm_dev->signal = ATM_PHY_SIG_UNKNOWN; + + /* temp init ATM device, set to 128kbit */ + atm_dev->link_rate = 128 * 1000 / 424; + + if (instance->driver->atm_start && ((ret = instance->driver->atm_start(instance, atm_dev)) < 0)) { + atm_dbg(instance, "%s: atm_start failed: %d!\n", __func__, ret); + goto fail; + } + + /* ready for ATM callbacks */ + usbatm_get_instance(instance); /* dropped in usbatm_atm_dev_close */ + mb(); + atm_dev->dev_data = instance; + + /* submit all rx URBs */ + for (i = 0; i < num_rcv_urbs; i++) + usbatm_submit_urb(instance->urbs[i]); + + return 0; + + fail: + instance->atm_dev = NULL; + shutdown_atm_dev(atm_dev); /* usbatm_atm_dev_close will eventually be called */ + return ret; +} + + +/********** +** USB ** +**********/ + +static int usbatm_do_heavy_init(void *arg) +{ + struct usbatm_data *instance = arg; + int ret; + + daemonize(instance->driver->driver_name); + allow_signal(SIGTERM); + + complete(&instance->thread_started); + + ret = instance->driver->heavy_init(instance, instance->usb_intf); + + if (!ret) + ret = usbatm_atm_init(instance); + + down(&instance->serialize); + instance->thread_pid = -1; + up(&instance->serialize); + + complete_and_exit(&instance->thread_exited, ret); +} + +static int usbatm_heavy_init(struct usbatm_data *instance) +{ + int ret = kernel_thread(usbatm_do_heavy_init, instance, CLONE_KERNEL); + + if (ret < 0) { + usb_dbg(instance, "%s: failed to create kernel_thread (%d)!\n", __func__, ret); + return ret; + } + + down(&instance->serialize); + instance->thread_pid = ret; + up(&instance->serialize); + + wait_for_completion(&instance->thread_started); + + return 0; +} + +static void usbatm_tasklet_schedule(unsigned long data) +{ + tasklet_schedule((struct tasklet_struct *) data); +} + +static inline void usbatm_init_channel(struct usbatm_channel *channel) +{ + spin_lock_init(&channel->lock); + INIT_LIST_HEAD(&channel->list); + channel->delay.function = usbatm_tasklet_schedule; + channel->delay.data = (unsigned long) &channel->tasklet; + init_timer(&channel->delay); +} + +int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id, + struct usbatm_driver *driver) +{ + struct device *dev = &intf->dev; + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct usbatm_data *instance; + char *buf; + int error = -ENOMEM; + int i, length; + int need_heavy; + + dev_dbg(dev, "%s: trying driver %s with vendor=0x%x, product=0x%x, ifnum %d\n", + __func__, driver->driver_name, + le16_to_cpu(usb_dev->descriptor.idVendor), + le16_to_cpu(usb_dev->descriptor.idProduct), + intf->altsetting->desc.bInterfaceNumber); + + /* instance init */ + instance = kcalloc(1, sizeof(*instance) + sizeof(struct urb *) * (num_rcv_urbs + num_snd_urbs), GFP_KERNEL); + if (!instance) { + dev_dbg(dev, "%s: no memory for instance data!\n", __func__); + return -ENOMEM; + } + + /* public fields */ + + instance->driver = driver; + snprintf(instance->driver_name, sizeof(instance->driver_name), driver->driver_name); + + instance->usb_dev = usb_dev; + instance->usb_intf = intf; + + buf = instance->description; + length = sizeof(instance->description); + + if ((i = usb_string(usb_dev, usb_dev->descriptor.iProduct, buf, length)) < 0) + goto bind; + + buf += i; + length -= i; + + i = scnprintf(buf, length, " ("); + buf += i; + length -= i; + + if (length <= 0 || (i = usb_make_path(usb_dev, buf, length)) < 0) + goto bind; + + buf += i; + length -= i; + + snprintf(buf, length, ")"); + + bind: + need_heavy = 1; + if (driver->bind && (error = driver->bind(instance, intf, id, &need_heavy)) < 0) { + dev_dbg(dev, "%s: bind failed: %d!\n", __func__, error); + goto fail_free; + } + + /* private fields */ + + kref_init(&instance->refcount); /* dropped in usbatm_usb_disconnect */ + init_MUTEX(&instance->serialize); + + instance->thread_pid = -1; + init_completion(&instance->thread_started); + init_completion(&instance->thread_exited); + + INIT_LIST_HEAD(&instance->vcc_list); + + usbatm_init_channel(&instance->rx_channel); + usbatm_init_channel(&instance->tx_channel); + tasklet_init(&instance->rx_channel.tasklet, usbatm_rx_process, (unsigned long)instance); + tasklet_init(&instance->tx_channel.tasklet, usbatm_tx_process, (unsigned long)instance); + instance->rx_channel.endpoint = usb_rcvbulkpipe(usb_dev, driver->in); + instance->tx_channel.endpoint = usb_sndbulkpipe(usb_dev, driver->out); + instance->rx_channel.stride = ATM_CELL_SIZE + driver->rx_padding; + instance->tx_channel.stride = ATM_CELL_SIZE + driver->tx_padding; + instance->rx_channel.buf_size = rcv_buf_size * instance->rx_channel.stride; + instance->tx_channel.buf_size = snd_buf_size * instance->tx_channel.stride; + instance->rx_channel.usbatm = instance->tx_channel.usbatm = instance; + + skb_queue_head_init(&instance->sndqueue); + + for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) { + struct urb *urb; + u8 *buffer; + unsigned int iso_packets = 0, iso_size = 0; + struct usbatm_channel *channel = i < num_rcv_urbs ? + &instance->rx_channel : &instance->tx_channel; + + if (usb_pipeisoc(channel->endpoint)) { + /* don't expect iso out endpoints */ + iso_size = usb_maxpacket(instance->usb_dev, channel->endpoint, 0); + iso_size -= iso_size % channel->stride; /* alignment */ + BUG_ON(!iso_size); + iso_packets = (channel->buf_size - 1) / iso_size + 1; + } + + urb = usb_alloc_urb(iso_packets, GFP_KERNEL); + if (!urb) { + dev_dbg(dev, "%s: no memory for urb %d!\n", __func__, i); + goto fail_unbind; + } + + instance->urbs[i] = urb; + + buffer = kmalloc(channel->buf_size, GFP_KERNEL); + if (!buffer) { + dev_dbg(dev, "%s: no memory for buffer %d!\n", __func__, i); + goto fail_unbind; + } + memset(buffer, 0, channel->buf_size); + + usb_fill_bulk_urb(urb, instance->usb_dev, channel->endpoint, + buffer, channel->buf_size, usbatm_complete, channel); + if (iso_packets) { + int j; + urb->interval = 1; + urb->transfer_flags = URB_ISO_ASAP; + urb->number_of_packets = iso_packets; + for (j = 0; j < iso_packets; j++) { + urb->iso_frame_desc[j].offset = iso_size * j; + urb->iso_frame_desc[j].length = min_t(int, iso_size, + channel->buf_size - urb->iso_frame_desc[j].offset); + } + } + + /* put all tx URBs on the list of spares */ + if (i >= num_rcv_urbs) + list_add_tail(&urb->urb_list, &channel->list); + + vdbg("%s: alloced buffer 0x%p buf size %u urb 0x%p", + __func__, urb->transfer_buffer, urb->transfer_buffer_length, urb); + } + + if (need_heavy && driver->heavy_init) { + error = usbatm_heavy_init(instance); + } else { + complete(&instance->thread_exited); /* pretend that heavy_init was run */ + error = usbatm_atm_init(instance); + } + + if (error < 0) + goto fail_unbind; + + usb_get_dev(usb_dev); + usb_set_intfdata(intf, instance); + + return 0; + + fail_unbind: + if (instance->driver->unbind) + instance->driver->unbind(instance, intf); + fail_free: + for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) { + if (instance->urbs[i]) + kfree(instance->urbs[i]->transfer_buffer); + usb_free_urb(instance->urbs[i]); + } + + kfree (instance); + + return error; +} +EXPORT_SYMBOL_GPL(usbatm_usb_probe); + +void usbatm_usb_disconnect(struct usb_interface *intf) +{ + struct device *dev = &intf->dev; + struct usbatm_data *instance = usb_get_intfdata(intf); + int i; + + dev_dbg(dev, "%s entered\n", __func__); + + if (!instance) { + dev_dbg(dev, "%s: NULL instance!\n", __func__); + return; + } + + usb_set_intfdata(intf, NULL); + + down(&instance->serialize); + if (instance->thread_pid >= 0) + kill_proc(instance->thread_pid, SIGTERM, 1); + up(&instance->serialize); + + wait_for_completion(&instance->thread_exited); + + tasklet_disable(&instance->rx_channel.tasklet); + tasklet_disable(&instance->tx_channel.tasklet); + + for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) + usb_kill_urb(instance->urbs[i]); + + del_timer_sync(&instance->rx_channel.delay); + del_timer_sync(&instance->tx_channel.delay); + + if (instance->atm_dev && instance->driver->atm_stop) + instance->driver->atm_stop(instance, instance->atm_dev); + + if (instance->driver->unbind) + instance->driver->unbind(instance, intf); + + instance->driver_data = NULL; + + /* turn usbatm_[rt]x_process into noop */ + /* no need to take the spinlock */ + INIT_LIST_HEAD(&instance->rx_channel.list); + INIT_LIST_HEAD(&instance->tx_channel.list); + + tasklet_enable(&instance->rx_channel.tasklet); + tasklet_enable(&instance->tx_channel.tasklet); + + for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) { + kfree(instance->urbs[i]->transfer_buffer); + usb_free_urb(instance->urbs[i]); + } + + /* ATM finalize */ + if (instance->atm_dev) + shutdown_atm_dev(instance->atm_dev); + + usbatm_put_instance(instance); /* taken in usbatm_usb_probe */ +} +EXPORT_SYMBOL_GPL(usbatm_usb_disconnect); + + +/*********** +** init ** +***********/ + +static int __init usbatm_usb_init(void) +{ + dbg("%s: driver version %s", __func__, DRIVER_VERSION); + + if (sizeof(struct usbatm_control) > sizeof(((struct sk_buff *) 0)->cb)) { + printk(KERN_ERR "%s unusable with this kernel!\n", usbatm_driver_name); + return -EIO; + } + + if ((num_rcv_urbs > UDSL_MAX_RCV_URBS) + || (num_snd_urbs > UDSL_MAX_SND_URBS) + || (rcv_buf_size < 1) + || (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE) + || (snd_buf_size < 1) + || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE)) + return -EINVAL; + + return 0; +} +module_init(usbatm_usb_init); + +static void __exit usbatm_usb_exit(void) +{ + dbg("%s", __func__); +} +module_exit(usbatm_usb_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); + +/************ +** debug ** +************/ + +#ifdef VERBOSE_DEBUG +static int usbatm_print_packet(const unsigned char *data, int len) +{ + unsigned char buffer[256]; + int i = 0, j = 0; + + for (i = 0; i < len;) { + buffer[0] = '\0'; + sprintf(buffer, "%.3d :", i); + for (j = 0; (j < 16) && (i < len); j++, i++) { + sprintf(buffer, "%s %2.2x", buffer, data[i]); + } + dbg("%s", buffer); + } + return i; +} +#endif diff --git a/drivers/usb/atm/usbatm.h b/drivers/usb/atm/usbatm.h new file mode 100644 index 0000000..9366464 --- /dev/null +++ b/drivers/usb/atm/usbatm.h @@ -0,0 +1,184 @@ +/****************************************************************************** + * usbatm.h - Generic USB xDSL driver core + * + * Copyright (C) 2001, Alcatel + * Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas + * Copyright (C) 2004, David Woodhouse + * + * 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. + * + ******************************************************************************/ + +#ifndef _USBATM_H_ +#define _USBATM_H_ + +#include <linux/config.h> + +/* +#define DEBUG +#define VERBOSE_DEBUG +*/ + +#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) +# define DEBUG +#endif + +#include <asm/semaphore.h> +#include <linux/atm.h> +#include <linux/atmdev.h> +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/kref.h> +#include <linux/list.h> +#include <linux/stringify.h> +#include <linux/usb.h> + +#ifdef DEBUG +#define UDSL_ASSERT(x) BUG_ON(!(x)) +#else +#define UDSL_ASSERT(x) do { if (!(x)) warn("failed assertion '%s' at line %d", __stringify(x), __LINE__); } while(0) +#endif + +#define usb_err(instance, format, arg...) \ + dev_err(&(instance)->usb_intf->dev , format , ## arg) +#define usb_info(instance, format, arg...) \ + dev_info(&(instance)->usb_intf->dev , format , ## arg) +#define usb_warn(instance, format, arg...) \ + dev_warn(&(instance)->usb_intf->dev , format , ## arg) +#define usb_dbg(instance, format, arg...) \ + dev_dbg(&(instance)->usb_intf->dev , format , ## arg) + +/* FIXME: move to dev_* once ATM is driver model aware */ +#define atm_printk(level, instance, format, arg...) \ + printk(level "ATM dev %d: " format , \ + (instance)->atm_dev->number , ## arg) + +#define atm_err(instance, format, arg...) \ + atm_printk(KERN_ERR, instance , format , ## arg) +#define atm_info(instance, format, arg...) \ + atm_printk(KERN_INFO, instance , format , ## arg) +#define atm_warn(instance, format, arg...) \ + atm_printk(KERN_WARNING, instance , format , ## arg) +#ifdef DEBUG +#define atm_dbg(instance, format, arg...) \ + atm_printk(KERN_DEBUG, instance , format , ## arg) +#else +#define atm_dbg(instance, format, arg...) \ + do {} while (0) +#endif + + +/* mini driver */ + +struct usbatm_data; + +/* +* Assuming all methods exist and succeed, they are called in this order: +* +* bind, heavy_init, atm_start, ..., atm_stop, unbind +*/ + +struct usbatm_driver { + struct module *owner; + + const char *driver_name; + + /* + * init device ... can sleep, or cause probe() failure. Drivers with a heavy_init + * method can avoid having it called by setting need_heavy_init to zero. + */ + int (*bind) (struct usbatm_data *, struct usb_interface *, + const struct usb_device_id *id, int *need_heavy_init); + + /* additional device initialization that is too slow to be done in probe() */ + int (*heavy_init) (struct usbatm_data *, struct usb_interface *); + + /* cleanup device ... can sleep, but can't fail */ + void (*unbind) (struct usbatm_data *, struct usb_interface *); + + /* init ATM device ... can sleep, or cause ATM initialization failure */ + int (*atm_start) (struct usbatm_data *, struct atm_dev *); + + /* cleanup ATM device ... can sleep, but can't fail */ + void (*atm_stop) (struct usbatm_data *, struct atm_dev *); + + int in; /* rx endpoint */ + int out; /* tx endpoint */ + + unsigned rx_padding; + unsigned tx_padding; +}; + +extern int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id, + struct usbatm_driver *driver); +extern void usbatm_usb_disconnect(struct usb_interface *intf); + + +struct usbatm_channel { + int endpoint; /* usb pipe */ + unsigned int stride; /* ATM cell size + padding */ + unsigned int buf_size; /* urb buffer size */ + spinlock_t lock; + struct list_head list; + struct tasklet_struct tasklet; + struct timer_list delay; + struct usbatm_data *usbatm; +}; + +/* main driver data */ + +struct usbatm_data { + /****************** + * public fields * + ******************/ + + /* mini driver */ + struct usbatm_driver *driver; + void *driver_data; + char driver_name[16]; + + /* USB device */ + struct usb_device *usb_dev; + struct usb_interface *usb_intf; + char description[64]; + + /* ATM device */ + struct atm_dev *atm_dev; + + /******************************** + * private fields - do not use * + ********************************/ + + struct kref refcount; + struct semaphore serialize; + + /* heavy init */ + int thread_pid; + struct completion thread_started; + struct completion thread_exited; + + /* ATM device */ + struct list_head vcc_list; + + struct usbatm_channel rx_channel; + struct usbatm_channel tx_channel; + + struct sk_buff_head sndqueue; + struct sk_buff *current_skb; /* being emptied */ + + struct urb *urbs[0]; +}; + +#endif /* _USBATM_H_ */ diff --git a/drivers/usb/atm/xusbatm.c b/drivers/usb/atm/xusbatm.c new file mode 100644 index 0000000..7fe7fb4 --- /dev/null +++ b/drivers/usb/atm/xusbatm.c @@ -0,0 +1,196 @@ +/****************************************************************************** + * xusbatm.c - dumb usbatm-based driver for modems initialized in userspace + * + * Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru) + * + * 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. + * + ******************************************************************************/ + +#include <linux/module.h> +#include <linux/netdevice.h> /* FIXME: required by linux/etherdevice.h */ +#include <linux/etherdevice.h> /* for random_ether_addr() */ + +#include "usbatm.h" + + +#define XUSBATM_DRIVERS_MAX 8 + +#define XUSBATM_PARM(name, type, parmtype, desc) \ + static type name[XUSBATM_DRIVERS_MAX]; \ + static int num_##name; \ + module_param_array(name, parmtype, &num_##name, 0444); \ + MODULE_PARM_DESC(name, desc) + +XUSBATM_PARM(vendor, unsigned short, ushort, "USB device vendor"); +XUSBATM_PARM(product, unsigned short, ushort, "USB device product"); + +XUSBATM_PARM(rx_endpoint, unsigned char, byte, "rx endpoint number"); +XUSBATM_PARM(tx_endpoint, unsigned char, byte, "tx endpoint number"); +XUSBATM_PARM(rx_padding, unsigned char, byte, "rx padding (default 0)"); +XUSBATM_PARM(tx_padding, unsigned char, byte, "tx padding (default 0)"); + +static const char xusbatm_driver_name[] = "xusbatm"; + +static struct usbatm_driver xusbatm_drivers[XUSBATM_DRIVERS_MAX]; +static struct usb_device_id xusbatm_usb_ids[XUSBATM_DRIVERS_MAX + 1]; +static struct usb_driver xusbatm_usb_driver; + +static int usb_intf_has_ep(const struct usb_interface *intf, u8 ep) +{ + int i, j; + + for (i = 0; i < intf->num_altsetting; i++) { + struct usb_host_interface *alt = intf->altsetting; + for (j = 0; j < alt->desc.bNumEndpoints; j++) + if ((alt->endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) == ep) + return 1; + } + return 0; +} + +static int xusbatm_bind(struct usbatm_data *usbatm_instance, + struct usb_interface *intf, const struct usb_device_id *id, + int *need_heavy_init) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + int drv_ix = id - xusbatm_usb_ids; + int rx_ep_present = usb_intf_has_ep(intf, rx_endpoint[drv_ix]); + int tx_ep_present = usb_intf_has_ep(intf, tx_endpoint[drv_ix]); + u8 searched_ep = rx_ep_present ? tx_endpoint[drv_ix] : rx_endpoint[drv_ix]; + int i, ret; + + usb_dbg(usbatm_instance, "%s: binding driver %d: vendor %#x product %#x" + " rx: ep %#x padd %d tx: ep %#x padd %d\n", + __func__, drv_ix, vendor[drv_ix], product[drv_ix], + rx_endpoint[drv_ix], rx_padding[drv_ix], + tx_endpoint[drv_ix], tx_padding[drv_ix]); + + if (!rx_ep_present && !tx_ep_present) { + usb_dbg(usbatm_instance, "%s: intf #%d has neither rx (%#x) nor tx (%#x) endpoint\n", + __func__, intf->altsetting->desc.bInterfaceNumber, + rx_endpoint[drv_ix], tx_endpoint[drv_ix]); + return -ENODEV; + } + + if (rx_ep_present && tx_ep_present) + return 0; + + for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) { + struct usb_interface *cur_if = usb_dev->actconfig->interface[i]; + + if (cur_if != intf && usb_intf_has_ep(cur_if, searched_ep)) { + ret = usb_driver_claim_interface(&xusbatm_usb_driver, + cur_if, usbatm_instance); + if (!ret) + usb_err(usbatm_instance, "%s: failed to claim interface #%d (%d)\n", + __func__, cur_if->altsetting->desc.bInterfaceNumber, ret); + return ret; + } + } + + usb_err(usbatm_instance, "%s: no interface has endpoint %#x\n", + __func__, searched_ep); + return -ENODEV; +} + +static void xusbatm_unbind(struct usbatm_data *usbatm_instance, + struct usb_interface *intf) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + int i; + usb_dbg(usbatm_instance, "%s entered\n", __func__); + + for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) { + struct usb_interface *cur_if = usb_dev->actconfig->interface[i]; + usb_set_intfdata(cur_if, NULL); + usb_driver_release_interface(&xusbatm_usb_driver, cur_if); + } +} + +static int xusbatm_atm_start(struct usbatm_data *usbatm_instance, + struct atm_dev *atm_dev) +{ + atm_dbg(usbatm_instance, "%s entered\n", __func__); + + /* use random MAC as we've no way to get it from the device */ + random_ether_addr(atm_dev->esi); + + return 0; +} + + +static int xusbatm_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return usbatm_usb_probe(intf, id, + xusbatm_drivers + (id - xusbatm_usb_ids)); +} + +static struct usb_driver xusbatm_usb_driver = { + .owner = THIS_MODULE, + .name = xusbatm_driver_name, + .probe = xusbatm_usb_probe, + .disconnect = usbatm_usb_disconnect, + .id_table = xusbatm_usb_ids +}; + +static int __init xusbatm_init(void) +{ + int i; + + dbg("xusbatm_init"); + + if (!num_vendor || + num_vendor != num_product || + num_vendor != num_rx_endpoint || + num_vendor != num_tx_endpoint) { + warn("malformed module parameters"); + return -EINVAL; + } + + for (i = 0; i < num_vendor; i++) { + xusbatm_usb_ids[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE; + xusbatm_usb_ids[i].idVendor = vendor[i]; + xusbatm_usb_ids[i].idProduct = product[i]; + + + xusbatm_drivers[i].owner = THIS_MODULE; + xusbatm_drivers[i].driver_name = xusbatm_driver_name; + xusbatm_drivers[i].bind = xusbatm_bind; + xusbatm_drivers[i].unbind = xusbatm_unbind; + xusbatm_drivers[i].atm_start = xusbatm_atm_start; + xusbatm_drivers[i].in = rx_endpoint[i]; + xusbatm_drivers[i].out = tx_endpoint[i]; + xusbatm_drivers[i].rx_padding = rx_padding[i]; + xusbatm_drivers[i].tx_padding = tx_padding[i]; + } + + return usb_register(&xusbatm_usb_driver); +} +module_init(xusbatm_init); + +static void __exit xusbatm_exit(void) +{ + dbg("xusbatm_exit entered"); + + usb_deregister(&xusbatm_usb_driver); +} +module_exit(xusbatm_exit); + +MODULE_AUTHOR("Roman Kagan, Duncan Sands"); +MODULE_DESCRIPTION("Driver for USB ADSL modems initialized in userspace"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 6d1f9b6..69e859e 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -106,6 +106,111 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) /* + * Write buffer management. + * All of these assume proper locks taken by the caller. + */ + +static int acm_wb_alloc(struct acm *acm) +{ + int i, wbn; + struct acm_wb *wb; + + wbn = acm->write_current; + i = 0; + for (;;) { + wb = &acm->wb[wbn]; + if (!wb->use) { + wb->use = 1; + return wbn; + } + wbn = (wbn + 1) % ACM_NWB; + if (++i >= ACM_NWB) + return -1; + } +} + +static void acm_wb_free(struct acm *acm, int wbn) +{ + acm->wb[wbn].use = 0; +} + +static int acm_wb_is_avail(struct acm *acm) +{ + int i, n; + + n = 0; + for (i = 0; i < ACM_NWB; i++) { + if (!acm->wb[i].use) + n++; + } + return n; +} + +static inline int acm_wb_is_used(struct acm *acm, int wbn) +{ + return acm->wb[wbn].use; +} + +/* + * Finish write. + */ +static void acm_write_done(struct acm *acm) +{ + unsigned long flags; + int wbn; + + spin_lock_irqsave(&acm->write_lock, flags); + acm->write_ready = 1; + wbn = acm->write_current; + acm_wb_free(acm, wbn); + acm->write_current = (wbn + 1) % ACM_NWB; + spin_unlock_irqrestore(&acm->write_lock, flags); +} + +/* + * Poke write. + */ +static int acm_write_start(struct acm *acm) +{ + unsigned long flags; + int wbn; + struct acm_wb *wb; + int rc; + + spin_lock_irqsave(&acm->write_lock, flags); + if (!acm->dev) { + spin_unlock_irqrestore(&acm->write_lock, flags); + return -ENODEV; + } + + if (!acm->write_ready) { + spin_unlock_irqrestore(&acm->write_lock, flags); + return 0; /* A white lie */ + } + + wbn = acm->write_current; + if (!acm_wb_is_used(acm, wbn)) { + spin_unlock_irqrestore(&acm->write_lock, flags); + return 0; + } + wb = &acm->wb[wbn]; + + acm->write_ready = 0; + spin_unlock_irqrestore(&acm->write_lock, flags); + + acm->writeurb->transfer_buffer = wb->buf; + acm->writeurb->transfer_dma = wb->dmah; + acm->writeurb->transfer_buffer_length = wb->len; + acm->writeurb->dev = acm->dev; + + if ((rc = usb_submit_urb(acm->writeurb, GFP_ATOMIC)) < 0) { + dbg("usb_submit_urb(write bulk) failed: %d", rc); + acm_write_done(acm); + } + return rc; +} + +/* * Interrupt handlers for various ACM device responses */ @@ -237,17 +342,13 @@ static void acm_rx_tasklet(unsigned long _acm) static void acm_write_bulk(struct urb *urb, struct pt_regs *regs) { struct acm *acm = (struct acm *)urb->context; - dbg("Entering acm_write_bulk with status %d\n", urb->status); - - if (!ACM_READY(acm)) - goto out; - if (urb->status) - dbg("nonzero write bulk status received: %d", urb->status); + dbg("Entering acm_write_bulk with status %d\n", urb->status); - schedule_work(&acm->work); -out: - acm->ready_for_write = 1; + acm_write_done(acm); + acm_write_start(acm); + if (ACM_READY(acm)) + schedule_work(&acm->work); } static void acm_softint(void *private) @@ -351,32 +452,33 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c { struct acm *acm = tty->driver_data; int stat; + unsigned long flags; + int wbn; + struct acm_wb *wb; + dbg("Entering acm_tty_write to write %d bytes,\n", count); if (!ACM_READY(acm)) return -EINVAL; - if (!acm->ready_for_write) - return 0; if (!count) return 0; - count = (count > acm->writesize) ? acm->writesize : count; + spin_lock_irqsave(&acm->write_lock, flags); + if ((wbn = acm_wb_alloc(acm)) < 0) { + spin_unlock_irqrestore(&acm->write_lock, flags); + acm_write_start(acm); + return 0; + } + wb = &acm->wb[wbn]; + count = (count > acm->writesize) ? acm->writesize : count; dbg("Get %d bytes...", count); - memcpy(acm->write_buffer, buf, count); - dbg(" Successfully copied.\n"); + memcpy(wb->buf, buf, count); + wb->len = count; + spin_unlock_irqrestore(&acm->write_lock, flags); - acm->writeurb->transfer_buffer_length = count; - acm->writeurb->dev = acm->dev; - - acm->ready_for_write = 0; - stat = usb_submit_urb(acm->writeurb, GFP_ATOMIC); - if (stat < 0) { - dbg("usb_submit_urb(write bulk) failed"); - acm->ready_for_write = 1; + if ((stat = acm_write_start(acm)) < 0) return stat; - } - return count; } @@ -385,7 +487,11 @@ static int acm_tty_write_room(struct tty_struct *tty) struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return -EINVAL; - return !acm->ready_for_write ? 0 : acm->writesize; + /* + * Do not let the line discipline to know that we have a reserve, + * or it might get too enthusiastic. + */ + return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0; } static int acm_tty_chars_in_buffer(struct tty_struct *tty) @@ -393,7 +499,10 @@ static int acm_tty_chars_in_buffer(struct tty_struct *tty) struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return -EINVAL; - return !acm->ready_for_write ? acm->writeurb->transfer_buffer_length : 0; + /* + * This is inaccurate (overcounts), but it works. + */ + return (ACM_NWB - acm_wb_is_avail(acm)) * acm->writesize; } static void acm_tty_throttle(struct tty_struct *tty) @@ -526,6 +635,39 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_ * USB probe and disconnect routines. */ +/* Little helper: write buffers free */ +static void acm_write_buffers_free(struct acm *acm) +{ + int i; + struct acm_wb *wb; + + for (wb = &acm->wb[0], i = 0; i < ACM_NWB; i++, wb++) { + usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah); + } +} + +/* Little helper: write buffers allocate */ +static int acm_write_buffers_alloc(struct acm *acm) +{ + int i; + struct acm_wb *wb; + + for (wb = &acm->wb[0], i = 0; i < ACM_NWB; i++, wb++) { + wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL, + &wb->dmah); + if (!wb->buf) { + while (i != 0) { + --i; + --wb; + usb_buffer_free(acm->dev, acm->writesize, + wb->buf, wb->dmah); + } + return -ENOMEM; + } + } + return 0; +} + static int acm_probe (struct usb_interface *intf, const struct usb_device_id *id) { @@ -700,7 +842,8 @@ skip_normal_probe: acm->bh.data = (unsigned long) acm; INIT_WORK(&acm->work, acm_softint, acm); spin_lock_init(&acm->throttle_lock); - acm->ready_for_write = 1; + spin_lock_init(&acm->write_lock); + acm->write_ready = 1; buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); if (!buf) { @@ -716,12 +859,10 @@ skip_normal_probe: } acm->read_buffer = buf; - buf = usb_buffer_alloc(usb_dev, acm->writesize, GFP_KERNEL, &acm->write_dma); - if (!buf) { + if (acm_write_buffers_alloc(acm) < 0) { dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n"); goto alloc_fail4; } - acm->write_buffer = buf; acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); if (!acm->ctrlurb) { @@ -750,9 +891,9 @@ skip_normal_probe: acm->readurb->transfer_dma = acm->read_dma; usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), - acm->write_buffer, acm->writesize, acm_write_bulk, acm); + NULL, acm->writesize, acm_write_bulk, acm); acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP; - acm->writeurb->transfer_dma = acm->write_dma; + /* acm->writeurb->transfer_dma = 0; */ dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); @@ -775,7 +916,7 @@ alloc_fail7: alloc_fail6: usb_free_urb(acm->ctrlurb); alloc_fail5: - usb_buffer_free(usb_dev, acm->writesize, acm->write_buffer, acm->write_dma); + acm_write_buffers_free(acm); alloc_fail4: usb_buffer_free(usb_dev, readsize, acm->read_buffer, acm->read_dma); alloc_fail3: @@ -806,7 +947,7 @@ static void acm_disconnect(struct usb_interface *intf) flush_scheduled_work(); /* wait for acm_softint */ - usb_buffer_free(usb_dev, acm->writesize, acm->write_buffer, acm->write_dma); + acm_write_buffers_free(acm); usb_buffer_free(usb_dev, acm->readsize, acm->read_buffer, acm->read_dma); usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 9009114..963a5df 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -51,14 +51,34 @@ * Internal driver structures. */ +/* + * The only reason to have several buffers is to accomodate assumptions + * in line disciplines. They ask for empty space amount, receive our URB size, + * and proceed to issue several 1-character writes, assuming they will fit. + * The very first write takes a complete URB. Fortunately, this only happens + * when processing onlcr, so we only need 2 buffers. + */ +#define ACM_NWB 2 +struct acm_wb { + unsigned char *buf; + dma_addr_t dmah; + int len; + int use; +}; + struct acm { struct usb_device *dev; /* the corresponding usb device */ struct usb_interface *control; /* control interface */ struct usb_interface *data; /* data interface */ struct tty_struct *tty; /* the corresponding tty */ struct urb *ctrlurb, *readurb, *writeurb; /* urbs */ - u8 *ctrl_buffer, *read_buffer, *write_buffer; /* buffers of urbs */ - dma_addr_t ctrl_dma, read_dma, write_dma; /* dma handles of buffers */ + u8 *ctrl_buffer, *read_buffer; /* buffers of urbs */ + dma_addr_t ctrl_dma, read_dma; /* dma handles of buffers */ + struct acm_wb wb[ACM_NWB]; + int write_current; /* current write buffer */ + int write_used; /* number of non-empty write buffers */ + int write_ready; /* write urb is not running */ + spinlock_t write_lock; struct usb_cdc_line_coding line; /* bits, stop, parity */ struct work_struct work; /* work queue entry for line discipline waking up */ struct tasklet_struct bh; /* rx processing */ @@ -71,7 +91,6 @@ struct acm { unsigned int minor; /* acm minor number */ unsigned char throttle; /* throttled by tty layer */ unsigned char clocal; /* termios CLOCAL */ - unsigned char ready_for_write; /* write urb can be used */ unsigned char resubmit_to_unthrottle; /* throtteling has disabled the read urb */ unsigned int ctrl_caps; /* control capabilities from the class specific header */ }; diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index bba22e9..7ce43fb 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -379,6 +379,8 @@ static int usblp_open(struct inode *inode, struct file *file) usblp->writeurb->transfer_buffer_length = 0; usblp->wcomplete = 1; /* we begin writeable */ usblp->rcomplete = 0; + usblp->writeurb->status = 0; + usblp->readurb->status = 0; if (usblp->bidir) { usblp->readcount = 0; @@ -751,6 +753,7 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count, schedule(); } else { set_current_state(TASK_RUNNING); + down(&usblp->sem); break; } down (&usblp->sem); diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 6bfab4b..787c27a6 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -784,16 +784,16 @@ static int proc_setconfig(struct dev_state *ps, void __user *arg) for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) { if (usb_interface_claimed(actconfig->interface[i])) { dev_warn (&ps->dev->dev, - "usbfs: interface %d claimed " + "usbfs: interface %d claimed by %s " "while '%s' sets config #%d\n", actconfig->interface[i] ->cur_altsetting ->desc.bInterfaceNumber, + actconfig->interface[i] + ->dev.driver->name, current->comm, u); -#if 0 /* FIXME: enable in 2.6.10 or so */ status = -EBUSY; break; -#endif } } } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 0da2373..83e732a 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -519,119 +519,120 @@ error: /*-------------------------------------------------------------------------*/ /* - * Root Hub interrupt transfers are synthesized with a timer. - * Completions are called in_interrupt() but not in_irq(). + * Root Hub interrupt transfers are polled using a timer if the + * driver requests it; otherwise the driver is responsible for + * calling usb_hcd_poll_rh_status() when an event occurs. * - * Note: some root hubs (including common UHCI based designs) can't - * correctly issue port change IRQs. They're the ones that _need_ a - * timer; most other root hubs don't. Some systems could save a - * lot of battery power by eliminating these root hub timer IRQs. + * Completions are called in_interrupt(), but they may or may not + * be in_irq(). */ +void usb_hcd_poll_rh_status(struct usb_hcd *hcd) +{ + struct urb *urb; + int length; + unsigned long flags; + char buffer[4]; /* Any root hubs with > 31 ports? */ -static void rh_report_status (unsigned long ptr); + if (!hcd->uses_new_polling && !hcd->status_urb) + return; -static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb) -{ - int len = 1 + (urb->dev->maxchild / 8); + length = hcd->driver->hub_status_data(hcd, buffer); + if (length > 0) { - /* rh_timer protected by hcd_data_lock */ - if (hcd->rh_timer.data || urb->transfer_buffer_length < len) { - dev_dbg (hcd->self.controller, - "not queuing rh status urb, stat %d\n", - urb->status); - return -EINVAL; + /* try to complete the status urb */ + local_irq_save (flags); + spin_lock(&hcd_root_hub_lock); + urb = hcd->status_urb; + if (urb) { + spin_lock(&urb->lock); + if (urb->status == -EINPROGRESS) { + hcd->poll_pending = 0; + hcd->status_urb = NULL; + urb->status = 0; + urb->hcpriv = NULL; + urb->actual_length = length; + memcpy(urb->transfer_buffer, buffer, length); + } else /* urb has been unlinked */ + length = 0; + spin_unlock(&urb->lock); + } else + length = 0; + spin_unlock(&hcd_root_hub_lock); + + /* local irqs are always blocked in completions */ + if (length > 0) + usb_hcd_giveback_urb (hcd, urb, NULL); + else + hcd->poll_pending = 1; + local_irq_restore (flags); } - init_timer (&hcd->rh_timer); - hcd->rh_timer.function = rh_report_status; - hcd->rh_timer.data = (unsigned long) urb; - /* USB 2.0 spec says 256msec; this is close enough */ - hcd->rh_timer.expires = jiffies + HZ/4; - add_timer (&hcd->rh_timer); - urb->hcpriv = hcd; /* nonzero to indicate it's queued */ - return 0; + /* The USB 2.0 spec says 256 ms. This is close enough and won't + * exceed that limit if HZ is 100. */ + if (hcd->uses_new_polling ? hcd->poll_rh : + (length == 0 && hcd->status_urb != NULL)) + mod_timer (&hcd->rh_timer, jiffies + msecs_to_jiffies(250)); } +EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status); /* timer callback */ +static void rh_timer_func (unsigned long _hcd) +{ + usb_hcd_poll_rh_status((struct usb_hcd *) _hcd); +} -static void rh_report_status (unsigned long ptr) +/*-------------------------------------------------------------------------*/ + +static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb) { - struct urb *urb; - struct usb_hcd *hcd; - int length = 0; + int retval; unsigned long flags; + int len = 1 + (urb->dev->maxchild / 8); - urb = (struct urb *) ptr; - local_irq_save (flags); - spin_lock (&urb->lock); + spin_lock_irqsave (&hcd_root_hub_lock, flags); + if (urb->status != -EINPROGRESS) /* already unlinked */ + retval = urb->status; + else if (hcd->status_urb || urb->transfer_buffer_length < len) { + dev_dbg (hcd->self.controller, "not queuing rh status urb\n"); + retval = -EINVAL; + } else { + hcd->status_urb = urb; + urb->hcpriv = hcd; /* indicate it's queued */ - /* do nothing if the urb's been unlinked */ - if (!urb->dev - || urb->status != -EINPROGRESS - || (hcd = urb->dev->bus->hcpriv) == NULL) { - spin_unlock (&urb->lock); - local_irq_restore (flags); - return; - } + if (!hcd->uses_new_polling) + mod_timer (&hcd->rh_timer, jiffies + + msecs_to_jiffies(250)); - /* complete the status urb, or retrigger the timer */ - spin_lock (&hcd_data_lock); - if (urb->dev->state == USB_STATE_CONFIGURED) { - length = hcd->driver->hub_status_data ( - hcd, urb->transfer_buffer); - if (length > 0) { - hcd->rh_timer.data = 0; - urb->actual_length = length; - urb->status = 0; - urb->hcpriv = NULL; - } else - mod_timer (&hcd->rh_timer, jiffies + HZ/4); + /* If a status change has already occurred, report it ASAP */ + else if (hcd->poll_pending) + mod_timer (&hcd->rh_timer, jiffies); + retval = 0; } - spin_unlock (&hcd_data_lock); - spin_unlock (&urb->lock); - - /* local irqs are always blocked in completions */ - if (length > 0) - usb_hcd_giveback_urb (hcd, urb, NULL); - local_irq_restore (flags); + spin_unlock_irqrestore (&hcd_root_hub_lock, flags); + return retval; } -/*-------------------------------------------------------------------------*/ - static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb) { - if (usb_pipeint (urb->pipe)) { - int retval; - unsigned long flags; - - spin_lock_irqsave (&hcd_data_lock, flags); - retval = rh_status_urb (hcd, urb); - spin_unlock_irqrestore (&hcd_data_lock, flags); - return retval; - } + if (usb_pipeint (urb->pipe)) + return rh_queue_status (hcd, urb); if (usb_pipecontrol (urb->pipe)) return rh_call_control (hcd, urb); - else - return -EINVAL; + return -EINVAL; } /*-------------------------------------------------------------------------*/ +/* Asynchronous unlinks of root-hub control URBs are legal, but they + * don't do anything. Status URB unlinks must be made in process context + * with interrupts enabled. + */ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) { - unsigned long flags; + if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */ + if (in_interrupt()) + return 0; /* nothing to do */ - /* note: always a synchronous unlink */ - if ((unsigned long) urb == hcd->rh_timer.data) { - del_timer_sync (&hcd->rh_timer); - hcd->rh_timer.data = 0; - - local_irq_save (flags); - urb->hcpriv = NULL; - usb_hcd_giveback_urb (hcd, urb, NULL); - local_irq_restore (flags); - - } else if (usb_pipeendpoint(urb->pipe) == 0) { spin_lock_irq(&urb->lock); /* from usb_kill_urb */ ++urb->reject; spin_unlock_irq(&urb->lock); @@ -642,8 +643,22 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) spin_lock_irq(&urb->lock); --urb->reject; spin_unlock_irq(&urb->lock); - } else - return -EINVAL; + + } else { /* Status URB */ + if (!hcd->uses_new_polling) + del_timer_sync (&hcd->rh_timer); + local_irq_disable (); + spin_lock (&hcd_root_hub_lock); + if (urb == hcd->status_urb) { + hcd->status_urb = NULL; + urb->hcpriv = NULL; + } else + urb = NULL; /* wasn't fully queued */ + spin_unlock (&hcd_root_hub_lock); + if (urb) + usb_hcd_giveback_urb (hcd, urb, NULL); + local_irq_enable (); + } return 0; } @@ -817,30 +832,22 @@ static void usb_deregister_bus (struct usb_bus *bus) } /** - * usb_hcd_register_root_hub - called by HCD to register its root hub + * register_root_hub - called by usb_add_hcd() to register a root hub * @usb_dev: the usb root hub device to be registered. * @hcd: host controller for this root hub * - * The USB host controller calls this function to register the root hub - * properly with the USB subsystem. It sets up the device properly in - * the device tree and stores the root_hub pointer in the bus structure, - * then calls usb_new_device() to register the usb device. It also - * assigns the root hub's USB address (always 1). + * This function registers the root hub with the USB subsystem. It sets up + * the device properly in the device tree and stores the root_hub pointer + * in the bus structure, then calls usb_new_device() to register the usb + * device. It also assigns the root hub's USB address (always 1). */ -int usb_hcd_register_root_hub (struct usb_device *usb_dev, struct usb_hcd *hcd) +static int register_root_hub (struct usb_device *usb_dev, + struct usb_hcd *hcd) { struct device *parent_dev = hcd->self.controller; const int devnum = 1; int retval; - /* hcd->driver->start() reported can_wakeup, probably with - * assistance from board's boot firmware. - * NOTE: normal devices won't enable wakeup by default. - */ - if (hcd->can_wakeup) - dev_dbg (parent_dev, "supports USB remote wakeup\n"); - hcd->remote_wakeup = hcd->can_wakeup; - usb_dev->devnum = devnum; usb_dev->bus->devnum_next = devnum + 1; memset (&usb_dev->bus->devmap.devicemap, 0, @@ -883,7 +890,16 @@ int usb_hcd_register_root_hub (struct usb_device *usb_dev, struct usb_hcd *hcd) return retval; } -EXPORT_SYMBOL_GPL(usb_hcd_register_root_hub); + +void usb_enable_root_hub_irq (struct usb_bus *bus) +{ + struct usb_hcd *hcd; + + hcd = container_of (bus, struct usb_hcd, self); + if (hcd->driver->hub_irq_enable && !hcd->poll_rh && + hcd->state != HC_STATE_HALT) + hcd->driver->hub_irq_enable (hcd); +} /*-------------------------------------------------------------------------*/ @@ -1348,7 +1364,8 @@ hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep) hcd = udev->bus->hcpriv; - WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT); + WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT && + udev->state != USB_STATE_NOTATTACHED); local_irq_disable (); @@ -1612,6 +1629,8 @@ void usb_hc_died (struct usb_hcd *hcd) spin_lock_irqsave (&hcd_root_hub_lock, flags); if (hcd->rh_registered) { + hcd->poll_rh = 0; + del_timer(&hcd->rh_timer); /* make khubd clean up old urbs and devices */ usb_set_device_state (hcd->self.root_hub, @@ -1665,6 +1684,8 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, hcd->self.bus_name = bus_name; init_timer(&hcd->rh_timer); + hcd->rh_timer.function = rh_timer_func; + hcd->rh_timer.data = (unsigned long) hcd; hcd->driver = driver; hcd->product_desc = (driver->product_desc) ? driver->product_desc : @@ -1694,7 +1715,8 @@ EXPORT_SYMBOL (usb_put_hcd); int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags) { - int retval; + int retval; + struct usb_device *rhdev; dev_info(hcd->self.controller, "%s\n", hcd->product_desc); @@ -1710,7 +1732,7 @@ int usb_add_hcd(struct usb_hcd *hcd, } if ((retval = usb_register_bus(&hcd->self)) < 0) - goto err1; + goto err_register_bus; if (hcd->driver->irq) { char buf[8], *bufp = buf; @@ -1727,7 +1749,7 @@ int usb_add_hcd(struct usb_hcd *hcd, hcd->irq_descr, hcd)) != 0) { dev_err(hcd->self.controller, "request interrupt %s failed\n", bufp); - goto err2; + goto err_request_irq; } hcd->irq = irqnum; dev_info(hcd->self.controller, "irq %s, %s 0x%08llx\n", bufp, @@ -1743,19 +1765,55 @@ int usb_add_hcd(struct usb_hcd *hcd, (unsigned long long)hcd->rsrc_start); } + /* Allocate the root hub before calling hcd->driver->start(), + * but don't register it until afterward so that the hardware + * is running. + */ + if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) { + dev_err(hcd->self.controller, "unable to allocate root hub\n"); + retval = -ENOMEM; + goto err_allocate_root_hub; + } + rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH : + USB_SPEED_FULL; + + /* Although in principle hcd->driver->start() might need to use rhdev, + * none of the current drivers do. + */ if ((retval = hcd->driver->start(hcd)) < 0) { dev_err(hcd->self.controller, "startup error %d\n", retval); - goto err3; + goto err_hcd_driver_start; } + /* hcd->driver->start() reported can_wakeup, probably with + * assistance from board's boot firmware. + * NOTE: normal devices won't enable wakeup by default. + */ + if (hcd->can_wakeup) + dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); + hcd->remote_wakeup = hcd->can_wakeup; + + if ((retval = register_root_hub(rhdev, hcd)) != 0) + goto err_register_root_hub; + + if (hcd->uses_new_polling && hcd->poll_rh) + usb_hcd_poll_rh_status(hcd); return retval; - err3: + err_register_root_hub: + hcd->driver->stop(hcd); + + err_hcd_driver_start: + usb_put_dev(rhdev); + + err_allocate_root_hub: if (hcd->irq >= 0) free_irq(irqnum, hcd); - err2: + + err_request_irq: usb_deregister_bus(&hcd->self); - err1: + + err_register_bus: hcd_buffer_destroy(hcd); return retval; } @@ -1782,6 +1840,9 @@ void usb_remove_hcd(struct usb_hcd *hcd) spin_unlock_irq (&hcd_root_hub_lock); usb_disconnect(&hcd->self.root_hub); + hcd->poll_rh = 0; + del_timer_sync(&hcd->rh_timer); + hcd->driver->stop(hcd); hcd->state = HC_STATE_HALT; diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 325a516..8dc13cd 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -65,7 +65,8 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ const char *product_desc; /* product/vendor string */ char irq_descr[24]; /* driver + bus # */ - struct timer_list rh_timer; /* drives root hub */ + struct timer_list rh_timer; /* drives root-hub polling */ + struct urb *status_urb; /* the current status urb */ /* * hardware info/state @@ -76,10 +77,17 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ unsigned remote_wakeup:1;/* sw should use wakeup? */ unsigned rh_registered:1;/* is root hub registered? */ + /* The next flag is a stopgap, to be removed when all the HCDs + * support the new root-hub polling mechanism. */ + unsigned uses_new_polling:1; + unsigned poll_rh:1; /* poll for rh status? */ + unsigned poll_pending:1; /* status has changed? */ + int irq; /* irq allocated */ void __iomem *regs; /* device memory/io */ u64 rsrc_start; /* memory/io resource start */ u64 rsrc_len; /* memory/io resource length */ + unsigned power_budget; /* in mA, 0 = no limit */ #define HCD_BUFFER_POOLS 4 struct dma_pool *pool [HCD_BUFFER_POOLS]; @@ -207,6 +215,8 @@ struct hc_driver { int (*hub_suspend)(struct usb_hcd *); int (*hub_resume)(struct usb_hcd *); int (*start_port_reset)(struct usb_hcd *, unsigned port_num); + void (*hub_irq_enable)(struct usb_hcd *); + /* Needed only if port-change IRQs are level-triggered */ }; extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs); @@ -243,7 +253,9 @@ void hcd_buffer_free (struct usb_bus *bus, size_t size, /* generic bus glue, needed for host controllers that don't use PCI */ extern irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r); + extern void usb_hc_died (struct usb_hcd *hcd); +extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd); /* -------------------------------------------------------------------------- */ @@ -341,9 +353,6 @@ extern long usb_calc_bus_time (int speed, int is_input, extern struct usb_bus *usb_alloc_bus (struct usb_operations *); -extern int usb_hcd_register_root_hub (struct usb_device *usb_dev, - struct usb_hcd *hcd); - extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd); extern void usb_set_device_state(struct usb_device *udev, @@ -360,6 +369,8 @@ extern wait_queue_head_t usb_kill_urb_queue; extern struct usb_bus *usb_bus_get (struct usb_bus *bus); extern void usb_bus_put (struct usb_bus *bus); +extern void usb_enable_root_hub_irq (struct usb_bus *bus); + extern int usb_find_interface_driver (struct usb_device *dev, struct usb_interface *interface); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a8d879a..32ff321 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -643,15 +643,21 @@ static int hub_configure(struct usb_hub *hub, message = "can't get hub status"; goto fail; } - cpu_to_le16s(&hubstatus); - if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) { + le16_to_cpus(&hubstatus); + if (hdev == hdev->bus->root_hub) { + struct usb_hcd *hcd = + container_of(hdev->bus, struct usb_hcd, self); + + hub->power_budget = min(500u, hcd->power_budget) / 2; + } else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) { dev_dbg(hub_dev, "hub controller current requirement: %dmA\n", hub->descriptor->bHubContrCurrent); hub->power_budget = (501 - hub->descriptor->bHubContrCurrent) / 2; + } + if (hub->power_budget) dev_dbg(hub_dev, "%dmA bus power budget for children\n", hub->power_budget * 2); - } ret = hub_hub_status(hub, &hubstatus, &hubchange); @@ -1727,7 +1733,7 @@ static int finish_port_resume(struct usb_device *udev) struct usb_driver *driver; intf = udev->actconfig->interface[i]; - if (intf->dev.power.power_state == PMSG_SUSPEND) + if (intf->dev.power.power_state == PMSG_ON) continue; if (!intf->dev.driver) { /* FIXME maybe force to alt 0 */ @@ -2787,6 +2793,11 @@ static void hub_events(void) hub->activating = 0; + /* If this is a root hub, tell the HCD it's okay to + * re-enable port-change interrupts now. */ + if (!hdev->parent) + usb_enable_root_hub_irq(hdev->bus); + loop: usb_unlock_device(hdev); usb_put_intf(intf); diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index d114b84..53bf564 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -224,15 +224,4 @@ struct usb_hub { struct work_struct leds; }; -/* use this for low-powered root hubs */ -static inline void -hub_set_power_budget (struct usb_device *hubdev, unsigned mA) -{ - struct usb_hub *hub; - - hub = (struct usb_hub *) - usb_get_intfdata (hubdev->actconfig->interface[0]); - hub->power_budget = min(mA,(unsigned)500)/2; -} - #endif /* __LINUX_HUB_H */ diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 3b24f9f..ff075a5 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -53,6 +53,9 @@ config USB_GADGET_DEBUG_FILES driver on a new board. Enable these files by choosing "Y" here. If in doubt, or to conserve kernel memory, say "N". +config USB_GADGET_SELECTED + boolean + # # USB Peripheral Controller Support # @@ -85,6 +88,7 @@ config USB_NET2280 tristate depends on USB_GADGET_NET2280 default USB_GADGET + select USB_GADGET_SELECTED config USB_GADGET_PXA2XX boolean "PXA 25x or IXP 4xx" @@ -105,6 +109,7 @@ config USB_PXA2XX tristate depends on USB_GADGET_PXA2XX default USB_GADGET + select USB_GADGET_SELECTED # if there's only one gadget driver, using only two bulk endpoints, # don't waste memory for the other endpoints @@ -134,6 +139,7 @@ config USB_GOKU tristate depends on USB_GADGET_GOKU default USB_GADGET + select USB_GADGET_SELECTED config USB_GADGET_LH7A40X @@ -146,6 +152,7 @@ config USB_LH7A40X tristate depends on USB_GADGET_LH7A40X default USB_GADGET + select USB_GADGET_SELECTED config USB_GADGET_OMAP @@ -167,6 +174,7 @@ config USB_OMAP tristate depends on USB_GADGET_OMAP default USB_GADGET + select USB_GADGET_SELECTED config USB_OTG boolean "OTG Support" @@ -207,6 +215,7 @@ config USB_DUMMY_HCD tristate depends on USB_GADGET_DUMMY_HCD default USB_GADGET + select USB_GADGET_SELECTED # NOTE: Please keep dummy_hcd LAST so that "real hardware" appears # first and will be selected by default. @@ -226,7 +235,7 @@ config USB_GADGET_DUALSPEED # choice tristate "USB Gadget Drivers" - depends on USB_GADGET + depends on USB_GADGET && USB_GADGET_SELECTED default USB_ETH help A Linux "Gadget Driver" talks to the USB Peripheral Controller diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index c039d2f..4d69267 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -65,7 +65,7 @@ #define DRIVER_DESC "USB Host+Gadget Emulator" -#define DRIVER_VERSION "17 Dec 2004" +#define DRIVER_VERSION "02 May 2005" static const char driver_name [] = "dummy_hcd"; static const char driver_desc [] = "USB Host+Gadget Emulator"; @@ -141,6 +141,8 @@ static const char *const ep_name [] = { }; #define DUMMY_ENDPOINTS (sizeof(ep_name)/sizeof(char *)) +/*-------------------------------------------------------------------------*/ + #define FIFO_SIZE 64 struct urbp { @@ -148,6 +150,13 @@ struct urbp { struct list_head urbp_list; }; + +enum dummy_rh_state { + DUMMY_RH_RESET, + DUMMY_RH_SUSPENDED, + DUMMY_RH_RUNNING +}; + struct dummy { spinlock_t lock; @@ -161,12 +170,18 @@ struct dummy { struct dummy_request fifo_req; u8 fifo_buf [FIFO_SIZE]; u16 devstatus; + unsigned udc_suspended:1; + unsigned pullup:1; + unsigned active:1; + unsigned old_active:1; /* * MASTER/HOST side support */ + enum dummy_rh_state rh_state; struct timer_list timer; u32 port_status; + u32 old_status; unsigned resuming:1; unsigned long re_timeout; @@ -189,6 +204,11 @@ static inline struct device *dummy_dev (struct dummy *dum) return dummy_to_hcd(dum)->self.controller; } +static inline struct device *udc_dev (struct dummy *dum) +{ + return dum->gadget.dev.parent; +} + static inline struct dummy *ep_to_dummy (struct dummy_ep *ep) { return container_of (ep->gadget, struct dummy, gadget); @@ -208,16 +228,98 @@ static struct dummy *the_controller; /*-------------------------------------------------------------------------*/ -/* - * This "hardware" may look a bit odd in diagnostics since it's got both - * host and device sides; and it binds different drivers to each side. - */ -static struct platform_device the_pdev; +/* SLAVE/GADGET SIDE UTILITY ROUTINES */ -static struct device_driver dummy_driver = { - .name = (char *) driver_name, - .bus = &platform_bus_type, -}; +/* called with spinlock held */ +static void nuke (struct dummy *dum, struct dummy_ep *ep) +{ + while (!list_empty (&ep->queue)) { + struct dummy_request *req; + + req = list_entry (ep->queue.next, struct dummy_request, queue); + list_del_init (&req->queue); + req->req.status = -ESHUTDOWN; + + spin_unlock (&dum->lock); + req->req.complete (&ep->ep, &req->req); + spin_lock (&dum->lock); + } +} + +/* caller must hold lock */ +static void +stop_activity (struct dummy *dum) +{ + struct dummy_ep *ep; + + /* prevent any more requests */ + dum->address = 0; + + /* The timer is left running so that outstanding URBs can fail */ + + /* nuke any pending requests first, so driver i/o is quiesced */ + list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list) + nuke (dum, ep); + + /* driver now does any non-usb quiescing necessary */ +} + +/* caller must hold lock */ +static void +set_link_state (struct dummy *dum) +{ + dum->active = 0; + if ((dum->port_status & USB_PORT_STAT_POWER) == 0) + dum->port_status = 0; + + /* UDC suspend must cause a disconnect */ + else if (!dum->pullup || dum->udc_suspended) { + dum->port_status &= ~(USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_ENABLE | + USB_PORT_STAT_LOW_SPEED | + USB_PORT_STAT_HIGH_SPEED | + USB_PORT_STAT_SUSPEND); + if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0) + dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16); + } else { + dum->port_status |= USB_PORT_STAT_CONNECTION; + if ((dum->old_status & USB_PORT_STAT_CONNECTION) == 0) + dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16); + if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0) + dum->port_status &= ~USB_PORT_STAT_SUSPEND; + else if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 && + dum->rh_state != DUMMY_RH_SUSPENDED) + dum->active = 1; + } + + if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0 || dum->active) + dum->resuming = 0; + + if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0 || + (dum->port_status & USB_PORT_STAT_RESET) != 0) { + if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0 && + (dum->old_status & USB_PORT_STAT_RESET) == 0 && + dum->driver) { + stop_activity (dum); + spin_unlock (&dum->lock); + dum->driver->disconnect (&dum->gadget); + spin_lock (&dum->lock); + } + } else if (dum->active != dum->old_active) { + if (dum->old_active && dum->driver->suspend) { + spin_unlock (&dum->lock); + dum->driver->suspend (&dum->gadget); + spin_lock (&dum->lock); + } else if (!dum->old_active && dum->driver->resume) { + spin_unlock (&dum->lock); + dum->driver->resume (&dum->gadget); + spin_lock (&dum->lock); + } + } + + dum->old_status = dum->port_status; + dum->old_active = dum->active; +} /*-------------------------------------------------------------------------*/ @@ -324,7 +426,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) _ep->maxpacket = max; ep->desc = desc; - dev_dbg (dummy_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d\n", + dev_dbg (udc_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d\n", _ep->name, desc->bEndpointAddress & 0x0f, (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", @@ -345,22 +447,6 @@ done: return retval; } -/* called with spinlock held */ -static void nuke (struct dummy *dum, struct dummy_ep *ep) -{ - while (!list_empty (&ep->queue)) { - struct dummy_request *req; - - req = list_entry (ep->queue.next, struct dummy_request, queue); - list_del_init (&req->queue); - req->req.status = -ESHUTDOWN; - - spin_unlock (&dum->lock); - req->req.complete (&ep->ep, &req->req); - spin_lock (&dum->lock); - } -} - static int dummy_disable (struct usb_ep *_ep) { struct dummy_ep *ep; @@ -379,7 +465,7 @@ static int dummy_disable (struct usb_ep *_ep) nuke (dum, ep); spin_unlock_irqrestore (&dum->lock, flags); - dev_dbg (dummy_dev(dum), "disabled %s\n", _ep->name); + dev_dbg (udc_dev(dum), "disabled %s\n", _ep->name); return retval; } @@ -474,7 +560,7 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req, int mem_flags) return -ESHUTDOWN; #if 0 - dev_dbg (dummy_dev(dum), "ep %p queue req %p to %s, len %d buf %p\n", + dev_dbg (udc_dev(dum), "ep %p queue req %p to %s, len %d buf %p\n", ep, _req, _ep->name, _req->length, _req->buf); #endif @@ -537,7 +623,7 @@ static int dummy_dequeue (struct usb_ep *_ep, struct usb_request *_req) spin_unlock_irqrestore (&dum->lock, flags); if (retval == 0) { - dev_dbg (dummy_dev(dum), + dev_dbg (udc_dev(dum), "dequeued req %p from %s, len %d buf %p\n", req, _ep->name, _req->length, _req->buf); _req->complete (_ep, _req); @@ -601,13 +687,21 @@ static int dummy_wakeup (struct usb_gadget *_gadget) struct dummy *dum; dum = gadget_to_dummy (_gadget); - if ((dum->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) == 0 - || !(dum->port_status & (1 << USB_PORT_FEAT_SUSPEND))) + if (!(dum->devstatus & ( (1 << USB_DEVICE_B_HNP_ENABLE) + | (1 << USB_DEVICE_REMOTE_WAKEUP)))) return -EINVAL; + if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0) + return -ENOLINK; + if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 && + dum->rh_state != DUMMY_RH_SUSPENDED) + return -EIO; + + /* FIXME: What if the root hub is suspended but the port isn't? */ /* hub notices our request, issues downstream resume, etc */ dum->resuming = 1; - dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND); + dum->re_timeout = jiffies + msecs_to_jiffies(20); + mod_timer (&dummy_to_hcd (dum)->rh_timer, dum->re_timeout); return 0; } @@ -623,10 +717,26 @@ static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value) return 0; } +static int dummy_pullup (struct usb_gadget *_gadget, int value) +{ + struct dummy *dum; + unsigned long flags; + + dum = gadget_to_dummy (_gadget); + spin_lock_irqsave (&dum->lock, flags); + dum->pullup = (value != 0); + set_link_state (dum); + spin_unlock_irqrestore (&dum->lock, flags); + + usb_hcd_poll_rh_status (dummy_to_hcd (dum)); + return 0; +} + static const struct usb_gadget_ops dummy_ops = { .get_frame = dummy_g_get_frame, .wakeup = dummy_wakeup, .set_selfpowered = dummy_set_selfpowered, + .pullup = dummy_pullup, }; /*-------------------------------------------------------------------------*/ @@ -641,7 +751,7 @@ show_function (struct device *dev, struct device_attribute *attr, char *buf) return 0; return scnprintf (buf, PAGE_SIZE, "%s\n", dum->driver->function); } -DEVICE_ATTR (function, S_IRUGO, show_function, NULL); +static DEVICE_ATTR (function, S_IRUGO, show_function, NULL); /*-------------------------------------------------------------------------*/ @@ -659,38 +769,6 @@ DEVICE_ATTR (function, S_IRUGO, show_function, NULL); * for each driver that registers: just add to a big root hub. */ -static void -dummy_udc_release (struct device *dev) -{ -} - -static void -dummy_pdev_release (struct device *dev) -{ -} - -static int -dummy_register_udc (struct dummy *dum) -{ - int rc; - - strcpy (dum->gadget.dev.bus_id, "udc"); - dum->gadget.dev.parent = dummy_dev(dum); - dum->gadget.dev.release = dummy_udc_release; - - rc = device_register (&dum->gadget.dev); - if (rc == 0) - device_create_file (&dum->gadget.dev, &dev_attr_function); - return rc; -} - -static void -dummy_unregister_udc (struct dummy *dum) -{ - device_remove_file (&dum->gadget.dev, &dev_attr_function); - device_unregister (&dum->gadget.dev); -} - int usb_gadget_register_driver (struct usb_gadget_driver *driver) { @@ -709,12 +787,8 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver) * SLAVE side init ... the layer above hardware, which * can't enumerate without help from the driver we're binding. */ - dum->gadget.name = gadget_name; - dum->gadget.ops = &dummy_ops; - dum->gadget.is_dualspeed = 1; dum->devstatus = 0; - dum->resuming = 0; INIT_LIST_HEAD (&dum->gadget.ep_list); for (i = 0; i < DUMMY_ENDPOINTS; i++) { @@ -740,7 +814,7 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver) dum->driver = driver; dum->gadget.dev.driver = &driver->driver; - dev_dbg (dummy_dev(dum), "binding gadget driver '%s'\n", + dev_dbg (udc_dev(dum), "binding gadget driver '%s'\n", driver->driver.name); if ((retval = driver->bind (&dum->gadget)) != 0) { dum->driver = NULL; @@ -748,42 +822,21 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver) return retval; } - // FIXME: Check these calls for errors and re-order driver->driver.bus = dum->gadget.dev.parent->bus; driver_register (&driver->driver); - device_bind_driver (&dum->gadget.dev); /* khubd will enumerate this in a while */ - dum->port_status |= USB_PORT_STAT_CONNECTION - | (1 << USB_PORT_FEAT_C_CONNECTION); + spin_lock_irq (&dum->lock); + dum->pullup = 1; + set_link_state (dum); + spin_unlock_irq (&dum->lock); + + usb_hcd_poll_rh_status (dummy_to_hcd (dum)); return 0; } EXPORT_SYMBOL (usb_gadget_register_driver); -/* caller must hold lock */ -static void -stop_activity (struct dummy *dum, struct usb_gadget_driver *driver) -{ - struct dummy_ep *ep; - - /* prevent any more requests */ - dum->address = 0; - - /* The timer is left running so that outstanding URBs can fail */ - - /* nuke any pending requests first, so driver i/o is quiesced */ - list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list) - nuke (dum, ep); - - /* driver now does any non-usb quiescing necessary */ - if (driver) { - spin_unlock (&dum->lock); - driver->disconnect (&dum->gadget); - spin_lock (&dum->lock); - } -} - int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) { @@ -795,35 +848,138 @@ usb_gadget_unregister_driver (struct usb_gadget_driver *driver) if (!driver || driver != dum->driver) return -EINVAL; - dev_dbg (dummy_dev(dum), "unregister gadget driver '%s'\n", + dev_dbg (udc_dev(dum), "unregister gadget driver '%s'\n", driver->driver.name); spin_lock_irqsave (&dum->lock, flags); - stop_activity (dum, driver); - dum->port_status &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE | - USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED); - dum->port_status |= (1 << USB_PORT_FEAT_C_CONNECTION); + dum->pullup = 0; + set_link_state (dum); spin_unlock_irqrestore (&dum->lock, flags); driver->unbind (&dum->gadget); dum->driver = NULL; device_release_driver (&dum->gadget.dev); - driver_unregister (&driver->driver); + spin_lock_irqsave (&dum->lock, flags); + dum->pullup = 0; + set_link_state (dum); + spin_unlock_irqrestore (&dum->lock, flags); + + usb_hcd_poll_rh_status (dummy_to_hcd (dum)); return 0; } EXPORT_SYMBOL (usb_gadget_unregister_driver); #undef is_enabled +/* just declare this in any driver that really need it */ +extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode); + int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode) { return -ENOSYS; } EXPORT_SYMBOL (net2280_set_fifo_mode); + +/* The gadget structure is stored inside the hcd structure and will be + * released along with it. */ +static void +dummy_gadget_release (struct device *dev) +{ +#if 0 /* usb_bus_put isn't EXPORTed! */ + struct dummy *dum = gadget_dev_to_dummy (dev); + + usb_bus_put (&dummy_to_hcd (dum)->self); +#endif +} + +static int dummy_udc_probe (struct device *dev) +{ + struct dummy *dum = the_controller; + int rc; + + dum->gadget.name = gadget_name; + dum->gadget.ops = &dummy_ops; + dum->gadget.is_dualspeed = 1; + + /* maybe claim OTG support, though we won't complete HNP */ + dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0); + + strcpy (dum->gadget.dev.bus_id, "gadget"); + dum->gadget.dev.parent = dev; + dum->gadget.dev.release = dummy_gadget_release; + rc = device_register (&dum->gadget.dev); + if (rc < 0) + return rc; + +#if 0 /* usb_bus_get isn't EXPORTed! */ + usb_bus_get (&dummy_to_hcd (dum)->self); +#endif + + dev_set_drvdata (dev, dum); + device_create_file (&dum->gadget.dev, &dev_attr_function); + return rc; +} + +static int dummy_udc_remove (struct device *dev) +{ + struct dummy *dum = dev_get_drvdata (dev); + + dev_set_drvdata (dev, NULL); + device_remove_file (&dum->gadget.dev, &dev_attr_function); + device_unregister (&dum->gadget.dev); + return 0; +} + +static int dummy_udc_suspend (struct device *dev, pm_message_t state, + u32 level) +{ + struct dummy *dum = dev_get_drvdata(dev); + + if (level != SUSPEND_DISABLE) + return 0; + + dev_dbg (dev, "%s\n", __FUNCTION__); + spin_lock_irq (&dum->lock); + dum->udc_suspended = 1; + set_link_state (dum); + spin_unlock_irq (&dum->lock); + + dev->power.power_state = state; + usb_hcd_poll_rh_status (dummy_to_hcd (dum)); + return 0; +} + +static int dummy_udc_resume (struct device *dev, u32 level) +{ + struct dummy *dum = dev_get_drvdata(dev); + + if (level != RESUME_ENABLE) + return 0; + + dev_dbg (dev, "%s\n", __FUNCTION__); + spin_lock_irq (&dum->lock); + dum->udc_suspended = 0; + set_link_state (dum); + spin_unlock_irq (&dum->lock); + + dev->power.power_state = PMSG_ON; + usb_hcd_poll_rh_status (dummy_to_hcd (dum)); + return 0; +} + +static struct device_driver dummy_udc_driver = { + .name = (char *) gadget_name, + .bus = &platform_bus_type, + .probe = dummy_udc_probe, + .remove = dummy_udc_remove, + .suspend = dummy_udc_suspend, + .resume = dummy_udc_resume, +}; + /*-------------------------------------------------------------------------*/ /* MASTER/HOST SIDE DRIVER @@ -880,7 +1036,16 @@ static int dummy_urb_enqueue ( static int dummy_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) { - /* giveback happens automatically in timer callback */ + struct dummy *dum; + unsigned long flags; + + /* giveback happens automatically in timer callback, + * so make sure the callback happens */ + dum = hcd_to_dummy (hcd); + spin_lock_irqsave (&dum->lock, flags); + if (dum->rh_state != DUMMY_RH_RUNNING && !list_empty(&dum->urbp_list)) + mod_timer (&dum->timer, jiffies); + spin_unlock_irqrestore (&dum->lock, flags); return 0; } @@ -1025,7 +1190,6 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep) /* high bandwidth mode */ tmp = le16_to_cpu(ep->desc->wMaxPacketSize); - tmp = le16_to_cpu (tmp); tmp = (tmp >> 11) & 0x03; tmp *= 8 /* applies to entire frame */; limit += limit * tmp; @@ -1123,7 +1287,8 @@ restart: if (urb->status != -EINPROGRESS) { /* likely it was just unlinked */ goto return_urb; - } + } else if (dum->rh_state != DUMMY_RH_RUNNING) + continue; type = usb_pipetype (urb->pipe); /* used up this frame's non-periodic bandwidth? @@ -1168,12 +1333,14 @@ restart: struct usb_ctrlrequest setup; int value = 1; struct dummy_ep *ep2; + unsigned w_index; + unsigned w_value; setup = *(struct usb_ctrlrequest*) urb->setup_packet; - le16_to_cpus (&setup.wIndex); - le16_to_cpus (&setup.wValue); - le16_to_cpus (&setup.wLength); - if (setup.wLength != urb->transfer_buffer_length) { + w_index = le16_to_cpu(setup.wIndex); + w_value = le16_to_cpu(setup.wValue); + if (le16_to_cpu(setup.wLength) != + urb->transfer_buffer_length) { maybe_set_status (urb, -EOVERFLOW); goto return_urb; } @@ -1182,7 +1349,7 @@ restart: list_for_each_entry (req, &ep->queue, queue) { list_del_init (&req->queue); req->req.status = -EOVERFLOW; - dev_dbg (dummy_dev(dum), "stale req = %p\n", + dev_dbg (udc_dev(dum), "stale req = %p\n", req); spin_unlock (&dum->lock); @@ -1203,31 +1370,40 @@ restart: case USB_REQ_SET_ADDRESS: if (setup.bRequestType != Dev_Request) break; - dum->address = setup.wValue; + dum->address = w_value; maybe_set_status (urb, 0); - dev_dbg (dummy_dev(dum), "set_address = %d\n", - setup.wValue); + dev_dbg (udc_dev(dum), "set_address = %d\n", + w_value); value = 0; break; case USB_REQ_SET_FEATURE: if (setup.bRequestType == Dev_Request) { value = 0; - switch (setup.wValue) { + switch (w_value) { case USB_DEVICE_REMOTE_WAKEUP: break; + case USB_DEVICE_B_HNP_ENABLE: + dum->gadget.b_hnp_enable = 1; + break; + case USB_DEVICE_A_HNP_SUPPORT: + dum->gadget.a_hnp_support = 1; + break; + case USB_DEVICE_A_ALT_HNP_SUPPORT: + dum->gadget.a_alt_hnp_support + = 1; + break; default: value = -EOPNOTSUPP; } if (value == 0) { dum->devstatus |= - (1 << setup.wValue); + (1 << w_value); maybe_set_status (urb, 0); } } else if (setup.bRequestType == Ep_Request) { // endpoint halt - ep2 = find_endpoint (dum, - setup.wIndex); + ep2 = find_endpoint (dum, w_index); if (!ep2) { value = -EOPNOTSUPP; break; @@ -1239,7 +1415,7 @@ restart: break; case USB_REQ_CLEAR_FEATURE: if (setup.bRequestType == Dev_Request) { - switch (setup.wValue) { + switch (w_value) { case USB_DEVICE_REMOTE_WAKEUP: dum->devstatus &= ~(1 << USB_DEVICE_REMOTE_WAKEUP); @@ -1252,8 +1428,7 @@ restart: } } else if (setup.bRequestType == Ep_Request) { // endpoint halt - ep2 = find_endpoint (dum, - setup.wIndex); + ep2 = find_endpoint (dum, w_index); if (!ep2) { value = -EOPNOTSUPP; break; @@ -1279,7 +1454,7 @@ restart: if (urb->transfer_buffer_length > 0) { if (setup.bRequestType == Ep_InRequest) { - ep2 = find_endpoint (dum, setup.wIndex); + ep2 = find_endpoint (dum, w_index); if (!ep2) { value = -EOPNOTSUPP; break; @@ -1321,7 +1496,7 @@ restart: if (value < 0) { if (value != -EOPNOTSUPP) - dev_dbg (dummy_dev(dum), + dev_dbg (udc_dev(dum), "setup --> %d\n", value); maybe_set_status (urb, -EPIPE); @@ -1377,12 +1552,12 @@ return_urb: goto restart; } - /* want a 1 msec delay here */ - if (!list_empty (&dum->urbp_list)) - mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1)); - else { + if (list_empty (&dum->urbp_list)) { usb_put_dev (dum->udev); dum->udev = NULL; + } else if (dum->rh_state == DUMMY_RH_RUNNING) { + /* want a 1 msec delay here */ + mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1)); } spin_unlock_irqrestore (&dum->lock, flags); @@ -1391,29 +1566,39 @@ return_urb: /*-------------------------------------------------------------------------*/ #define PORT_C_MASK \ - ((1 << USB_PORT_FEAT_C_CONNECTION) \ - | (1 << USB_PORT_FEAT_C_ENABLE) \ - | (1 << USB_PORT_FEAT_C_SUSPEND) \ - | (1 << USB_PORT_FEAT_C_OVER_CURRENT) \ - | (1 << USB_PORT_FEAT_C_RESET)) + ((USB_PORT_STAT_C_CONNECTION \ + | USB_PORT_STAT_C_ENABLE \ + | USB_PORT_STAT_C_SUSPEND \ + | USB_PORT_STAT_C_OVERCURRENT \ + | USB_PORT_STAT_C_RESET) << 16) static int dummy_hub_status (struct usb_hcd *hcd, char *buf) { struct dummy *dum; unsigned long flags; - int retval; + int retval = 0; dum = hcd_to_dummy (hcd); spin_lock_irqsave (&dum->lock, flags); - if (!(dum->port_status & PORT_C_MASK)) - retval = 0; - else { + if (hcd->state != HC_STATE_RUNNING) + goto done; + + if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) { + dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); + dum->port_status &= ~USB_PORT_STAT_SUSPEND; + set_link_state (dum); + } + + if ((dum->port_status & PORT_C_MASK) != 0) { *buf = (1 << 1); dev_dbg (dummy_dev(dum), "port status 0x%08x has changes\n", - dum->port_status); + dum->port_status); retval = 1; + if (dum->rh_state == DUMMY_RH_SUSPENDED) + usb_hcd_resume_root_hub (hcd); } +done: spin_unlock_irqrestore (&dum->lock, flags); return retval; } @@ -1424,7 +1609,8 @@ hub_descriptor (struct usb_hub_descriptor *desc) memset (desc, 0, sizeof *desc); desc->bDescriptorType = 0x29; desc->bDescLength = 9; - desc->wHubCharacteristics = __constant_cpu_to_le16 (0x0001); + desc->wHubCharacteristics = (__force __u16) + (__constant_cpu_to_le16 (0x0001)); desc->bNbrPorts = 1; desc->bitmap [0] = 0xff; desc->bitmap [1] = 0xff; @@ -1442,6 +1628,9 @@ static int dummy_hub_control ( int retval = 0; unsigned long flags; + if (hcd->state != HC_STATE_RUNNING) + return -ETIMEDOUT; + dum = hcd_to_dummy (hcd); spin_lock_irqsave (&dum->lock, flags); switch (typeReq) { @@ -1450,27 +1639,27 @@ static int dummy_hub_control ( case ClearPortFeature: switch (wValue) { case USB_PORT_FEAT_SUSPEND: - if (dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)) { + if (dum->port_status & USB_PORT_STAT_SUSPEND) { /* 20msec resume signaling */ dum->resuming = 1; dum->re_timeout = jiffies + - msecs_to_jiffies(20); + msecs_to_jiffies(20); } break; case USB_PORT_FEAT_POWER: - dum->port_status = 0; - dum->resuming = 0; - stop_activity(dum, dum->driver); - break; + if (dum->port_status & USB_PORT_STAT_POWER) + dev_dbg (dummy_dev(dum), "power-off\n"); + /* FALLS THROUGH */ default: dum->port_status &= ~(1 << wValue); + set_link_state (dum); } break; case GetHubDescriptor: hub_descriptor ((struct usb_hub_descriptor *) buf); break; case GetHubStatus: - *(u32 *) buf = __constant_cpu_to_le32 (0); + *(__le32 *) buf = __constant_cpu_to_le32 (0); break; case GetPortStatus: if (wIndex != 1) @@ -1479,23 +1668,16 @@ static int dummy_hub_control ( /* whoever resets or resumes must GetPortStatus to * complete it!! */ - if (dum->resuming && time_after (jiffies, dum->re_timeout)) { - dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND); - dum->port_status &= ~(1 << USB_PORT_FEAT_SUSPEND); - dum->resuming = 0; - dum->re_timeout = 0; - if (dum->driver && dum->driver->resume) { - spin_unlock (&dum->lock); - dum->driver->resume (&dum->gadget); - spin_lock (&dum->lock); - } + if (dum->resuming && + time_after_eq (jiffies, dum->re_timeout)) { + dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); + dum->port_status &= ~USB_PORT_STAT_SUSPEND; } - if ((dum->port_status & (1 << USB_PORT_FEAT_RESET)) != 0 - && time_after (jiffies, dum->re_timeout)) { - dum->port_status |= (1 << USB_PORT_FEAT_C_RESET); - dum->port_status &= ~(1 << USB_PORT_FEAT_RESET); - dum->re_timeout = 0; - if (dum->driver) { + if ((dum->port_status & USB_PORT_STAT_RESET) != 0 && + time_after_eq (jiffies, dum->re_timeout)) { + dum->port_status |= (USB_PORT_STAT_C_RESET << 16); + dum->port_status &= ~USB_PORT_STAT_RESET; + if (dum->pullup) { dum->port_status |= USB_PORT_STAT_ENABLE; /* give it the best speed we agree on */ dum->gadget.speed = dum->driver->speed; @@ -1516,8 +1698,9 @@ static int dummy_hub_control ( } } } - ((u16 *) buf)[0] = cpu_to_le16 (dum->port_status); - ((u16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16); + set_link_state (dum); + ((__le16 *) buf)[0] = cpu_to_le16 (dum->port_status); + ((__le16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16); break; case SetHubFeature: retval = -EPIPE; @@ -1525,36 +1708,37 @@ static int dummy_hub_control ( case SetPortFeature: switch (wValue) { case USB_PORT_FEAT_SUSPEND: - if ((dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)) - == 0) { - dum->port_status |= - (1 << USB_PORT_FEAT_SUSPEND); - if (dum->driver && dum->driver->suspend) { - spin_unlock (&dum->lock); - dum->driver->suspend (&dum->gadget); - spin_lock (&dum->lock); - } + if (dum->active) { + dum->port_status |= USB_PORT_STAT_SUSPEND; + + /* HNP would happen here; for now we + * assume b_bus_req is always true. + */ + set_link_state (dum); + if (((1 << USB_DEVICE_B_HNP_ENABLE) + & dum->devstatus) != 0) + dev_dbg (dummy_dev(dum), + "no HNP yet!\n"); } break; + case USB_PORT_FEAT_POWER: + dum->port_status |= USB_PORT_STAT_POWER; + set_link_state (dum); + break; case USB_PORT_FEAT_RESET: - /* if it's already running, disconnect first */ - if (dum->port_status & USB_PORT_STAT_ENABLE) { - dum->port_status &= ~(USB_PORT_STAT_ENABLE - | USB_PORT_STAT_LOW_SPEED - | USB_PORT_STAT_HIGH_SPEED); - if (dum->driver) { - dev_dbg (dummy_dev(dum), - "disconnect\n"); - stop_activity (dum, dum->driver); - } - - /* FIXME test that code path! */ - } + /* if it's already enabled, disable */ + dum->port_status &= ~(USB_PORT_STAT_ENABLE + | USB_PORT_STAT_LOW_SPEED + | USB_PORT_STAT_HIGH_SPEED); + dum->devstatus = 0; /* 50msec reset signaling */ dum->re_timeout = jiffies + msecs_to_jiffies(50); - /* FALLTHROUGH */ + /* FALLS THROUGH */ default: - dum->port_status |= (1 << wValue); + if ((dum->port_status & USB_PORT_STAT_POWER) != 0) { + dum->port_status |= (1 << wValue); + set_link_state (dum); + } } break; @@ -1567,9 +1751,35 @@ static int dummy_hub_control ( retval = -EPIPE; } spin_unlock_irqrestore (&dum->lock, flags); + + if ((dum->port_status & PORT_C_MASK) != 0) + usb_hcd_poll_rh_status (hcd); return retval; } +static int dummy_hub_suspend (struct usb_hcd *hcd) +{ + struct dummy *dum = hcd_to_dummy (hcd); + + spin_lock_irq (&dum->lock); + dum->rh_state = DUMMY_RH_SUSPENDED; + set_link_state (dum); + spin_unlock_irq (&dum->lock); + return 0; +} + +static int dummy_hub_resume (struct usb_hcd *hcd) +{ + struct dummy *dum = hcd_to_dummy (hcd); + + spin_lock_irq (&dum->lock); + dum->rh_state = DUMMY_RH_RUNNING; + set_link_state (dum); + if (!list_empty(&dum->urbp_list)) + mod_timer (&dum->timer, jiffies); + spin_unlock_irq (&dum->lock); + return 0; +} /*-------------------------------------------------------------------------*/ @@ -1625,8 +1835,6 @@ static DEVICE_ATTR (urbs, S_IRUGO, show_urbs, NULL); static int dummy_start (struct usb_hcd *hcd) { struct dummy *dum; - struct usb_device *root; - int retval; dum = hcd_to_dummy (hcd); @@ -1639,38 +1847,22 @@ static int dummy_start (struct usb_hcd *hcd) init_timer (&dum->timer); dum->timer.function = dummy_timer; dum->timer.data = (unsigned long) dum; + dum->rh_state = DUMMY_RH_RUNNING; INIT_LIST_HEAD (&dum->urbp_list); - root = usb_alloc_dev (NULL, &hcd->self, 0); - if (!root) - return -ENOMEM; - - /* root hub enters addressed state... */ - hcd->state = HC_STATE_RUNNING; - root->speed = USB_SPEED_HIGH; - - /* ...then configured, so khubd sees us. */ - if ((retval = usb_hcd_register_root_hub (root, hcd)) != 0) { - goto err1; - } - /* only show a low-power port: just 8mA */ - hub_set_power_budget (root, 8); + hcd->power_budget = 8; + hcd->state = HC_STATE_RUNNING; + hcd->uses_new_polling = 1; - if ((retval = dummy_register_udc (dum)) != 0) - goto err2; +#ifdef CONFIG_USB_OTG + hcd->self.otg_port = 1; +#endif /* FIXME 'urbs' should be a per-device thing, maybe in usbcore */ device_create_file (dummy_dev(dum), &dev_attr_urbs); return 0; - - err2: - usb_disconnect (&hcd->self.root_hub); - err1: - usb_put_dev (root); - hcd->state = HC_STATE_QUIESCING; - return retval; } static void dummy_stop (struct usb_hcd *hcd) @@ -1680,10 +1872,7 @@ static void dummy_stop (struct usb_hcd *hcd) dum = hcd_to_dummy (hcd); device_remove_file (dummy_dev(dum), &dev_attr_urbs); - usb_gadget_unregister_driver (dum->driver); - dummy_unregister_udc (dum); - dev_info (dummy_dev(dum), "stopped\n"); } @@ -1711,9 +1900,11 @@ static const struct hc_driver dummy_hcd = { .hub_status_data = dummy_hub_status, .hub_control = dummy_hub_control, + .hub_suspend = dummy_hub_suspend, + .hub_resume = dummy_hub_resume, }; -static int dummy_probe (struct device *dev) +static int dummy_hcd_probe (struct device *dev) { struct usb_hcd *hcd; int retval; @@ -1733,7 +1924,7 @@ static int dummy_probe (struct device *dev) return retval; } -static void dummy_remove (struct device *dev) +static int dummy_hcd_remove (struct device *dev) { struct usb_hcd *hcd; @@ -1741,53 +1932,127 @@ static void dummy_remove (struct device *dev) usb_remove_hcd (hcd); usb_put_hcd (hcd); the_controller = NULL; + return 0; } -/*-------------------------------------------------------------------------*/ - -static int dummy_pdev_detect (void) +static int dummy_hcd_suspend (struct device *dev, pm_message_t state, + u32 level) { - int retval; + struct usb_hcd *hcd; - retval = driver_register (&dummy_driver); - if (retval < 0) - return retval; + if (level != SUSPEND_DISABLE) + return 0; + + dev_dbg (dev, "%s\n", __FUNCTION__); + hcd = dev_get_drvdata (dev); - the_pdev.name = "hc"; - the_pdev.dev.driver = &dummy_driver; - the_pdev.dev.release = dummy_pdev_release; +#ifndef CONFIG_USB_SUSPEND + /* Otherwise this would never happen */ + usb_lock_device (hcd->self.root_hub); + dummy_hub_suspend (hcd); + usb_unlock_device (hcd->self.root_hub); +#endif - retval = platform_device_register (&the_pdev); - if (retval < 0) - driver_unregister (&dummy_driver); - return retval; + hcd->state = HC_STATE_SUSPENDED; + return 0; } -static void dummy_pdev_remove (void) +static int dummy_hcd_resume (struct device *dev, u32 level) { - platform_device_unregister (&the_pdev); - driver_unregister (&dummy_driver); + struct usb_hcd *hcd; + + if (level != RESUME_ENABLE) + return 0; + + dev_dbg (dev, "%s\n", __FUNCTION__); + hcd = dev_get_drvdata (dev); + hcd->state = HC_STATE_RUNNING; + +#ifndef CONFIG_USB_SUSPEND + /* Otherwise this would never happen */ + usb_lock_device (hcd->self.root_hub); + dummy_hub_resume (hcd); + usb_unlock_device (hcd->self.root_hub); +#endif + + usb_hcd_poll_rh_status (hcd); + return 0; } +static struct device_driver dummy_hcd_driver = { + .name = (char *) driver_name, + .bus = &platform_bus_type, + .probe = dummy_hcd_probe, + .remove = dummy_hcd_remove, + .suspend = dummy_hcd_suspend, + .resume = dummy_hcd_resume, +}; + /*-------------------------------------------------------------------------*/ +/* These don't need to do anything because the pdev structures are + * statically allocated. */ +static void +dummy_udc_release (struct device *dev) {} + +static void +dummy_hcd_release (struct device *dev) {} + +static struct platform_device the_udc_pdev = { + .name = (char *) gadget_name, + .id = -1, + .dev = { + .release = dummy_udc_release, + }, +}; + +static struct platform_device the_hcd_pdev = { + .name = (char *) driver_name, + .id = -1, + .dev = { + .release = dummy_hcd_release, + }, +}; + static int __init init (void) { int retval; if (usb_disabled ()) return -ENODEV; - if ((retval = dummy_pdev_detect ()) != 0) + + retval = driver_register (&dummy_hcd_driver); + if (retval < 0) return retval; - if ((retval = dummy_probe (&the_pdev.dev)) != 0) - dummy_pdev_remove (); + + retval = driver_register (&dummy_udc_driver); + if (retval < 0) + goto err_register_udc_driver; + + retval = platform_device_register (&the_hcd_pdev); + if (retval < 0) + goto err_register_hcd; + + retval = platform_device_register (&the_udc_pdev); + if (retval < 0) + goto err_register_udc; + return retval; + +err_register_udc: + platform_device_unregister (&the_hcd_pdev); +err_register_hcd: + driver_unregister (&dummy_udc_driver); +err_register_udc_driver: + driver_unregister (&dummy_hcd_driver); return retval; } module_init (init); static void __exit cleanup (void) { - dummy_remove (&the_pdev.dev); - dummy_pdev_remove (); + platform_device_unregister (&the_udc_pdev); + platform_device_unregister (&the_hcd_pdev); + driver_unregister (&dummy_udc_driver); + driver_unregister (&dummy_hcd_driver); } module_exit (cleanup); diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 3f783cb..5bb53ae 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -84,18 +84,19 @@ */ #define DRIVER_DESC "Ethernet Gadget" -#define DRIVER_VERSION "Equinox 2004" +#define DRIVER_VERSION "May Day 2005" static const char shortname [] = "ether"; static const char driver_desc [] = DRIVER_DESC; #define RX_EXTRA 20 /* guard against rx overflows */ -#ifdef CONFIG_USB_ETH_RNDIS #include "rndis.h" -#else -#define rndis_init() 0 -#define rndis_exit() do{}while(0) + +#ifndef CONFIG_USB_ETH_RNDIS +#define rndis_uninit(x) do{}while(0) +#define rndis_deregister(c) do{}while(0) +#define rndis_exit() do{}while(0) #endif /* CDC and RNDIS support the same host-chosen outgoing packet filters. */ @@ -140,9 +141,6 @@ struct eth_dev { * It also ASSUMES a self-powered device, without remote wakeup, * although remote wakeup support would make sense. */ -static const char *EP_IN_NAME; -static const char *EP_OUT_NAME; -static const char *EP_STATUS_NAME; /*-------------------------------------------------------------------------*/ @@ -312,6 +310,7 @@ static inline int rndis_active(struct eth_dev *dev) #define FS_BPS (19 * 64 * 1 * 1000 * 8) #ifdef CONFIG_USB_GADGET_DUALSPEED +#define DEVSPEED USB_SPEED_HIGH static unsigned qmult = 5; module_param (qmult, uint, S_IRUGO|S_IWUSR); @@ -330,6 +329,8 @@ static inline int BITRATE(struct usb_gadget *g) } #else /* full speed (low speed doesn't do bulk) */ +#define DEVSPEED USB_SPEED_FULL + #define qlen(gadget) DEFAULT_QLEN static inline int BITRATE(struct usb_gadget *g) @@ -395,7 +396,8 @@ static inline int BITRATE(struct usb_gadget *g) #define STRING_SUBSET 8 #define STRING_RNDIS 9 -#define USB_BUFSIZ 256 /* holds our biggest descriptor */ +/* holds our biggest descriptor (or RNDIS response) */ +#define USB_BUFSIZ 256 /* * This device advertises one configuration, eth_config, unless RNDIS @@ -538,7 +540,7 @@ static const struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = { .bDataInterface = 0x01, }; -static struct usb_cdc_acm_descriptor acm_descriptor = { +static const struct usb_cdc_acm_descriptor acm_descriptor = { .bLength = sizeof acm_descriptor, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_ACM_TYPE, @@ -846,7 +848,7 @@ static const struct usb_descriptor_header *hs_rndis_function [] = { #else /* if there's no high speed support, maxpacket doesn't change. */ -#define ep_desc(g,hs,fs) fs +#define ep_desc(g,hs,fs) (((void)(g)), (fs)) static inline void __init hs_subset_descriptors(void) { @@ -946,10 +948,31 @@ config_buf (enum usb_device_speed speed, static void eth_start (struct eth_dev *dev, int gfp_flags); static int alloc_requests (struct eth_dev *dev, unsigned n, int gfp_flags); -#ifdef DEV_CONFIG_CDC -static inline int ether_alt_ep_setup (struct eth_dev *dev, struct usb_ep *ep) +static int +set_ether_config (struct eth_dev *dev, int gfp_flags) { - const struct usb_endpoint_descriptor *d; + int result = 0; + struct usb_gadget *gadget = dev->gadget; + + /* status endpoint used for RNDIS and (optionally) CDC */ + if (!subset_active(dev) && dev->status_ep) { + dev->status = ep_desc (gadget, &hs_status_desc, + &fs_status_desc); + dev->status_ep->driver_data = dev; + + result = usb_ep_enable (dev->status_ep, dev->status); + if (result != 0) { + DEBUG (dev, "enable %s --> %d\n", + dev->status_ep->name, result); + goto done; + } + } + + dev->in = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc); + dev->in_ep->driver_data = dev; + + dev->out = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc); + dev->out_ep->driver_data = dev; /* With CDC, the host isn't allowed to use these two data * endpoints in the default altsetting for the interface. @@ -959,135 +982,33 @@ static inline int ether_alt_ep_setup (struct eth_dev *dev, struct usb_ep *ep) * a side effect of setting a packet filter. Deactivation is * from REMOTE_NDIS_HALT_MSG, reset from REMOTE_NDIS_RESET_MSG. */ - - /* one endpoint writes data back IN to the host */ - if (strcmp (ep->name, EP_IN_NAME) == 0) { - d = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc); - ep->driver_data = dev; - dev->in = d; - - /* one endpoint just reads OUT packets */ - } else if (strcmp (ep->name, EP_OUT_NAME) == 0) { - d = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc); - ep->driver_data = dev; - dev->out = d; - - /* optional status/notification endpoint */ - } else if (EP_STATUS_NAME && - strcmp (ep->name, EP_STATUS_NAME) == 0) { - int result; - - d = ep_desc (dev->gadget, &hs_status_desc, &fs_status_desc); - result = usb_ep_enable (ep, d); - if (result < 0) - return result; - - ep->driver_data = dev; - dev->status = d; - } - return 0; -} -#endif - -#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) -static inline int ether_ep_setup (struct eth_dev *dev, struct usb_ep *ep) -{ - int result; - const struct usb_endpoint_descriptor *d; - - /* CDC subset is simpler: if the device is there, - * it's live with rx and tx endpoints. - * - * Do this as a shortcut for RNDIS too. - */ - - /* one endpoint writes data back IN to the host */ - if (strcmp (ep->name, EP_IN_NAME) == 0) { - d = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc); - result = usb_ep_enable (ep, d); - if (result < 0) - return result; - - ep->driver_data = dev; - dev->in = d; - - /* one endpoint just reads OUT packets */ - } else if (strcmp (ep->name, EP_OUT_NAME) == 0) { - d = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc); - result = usb_ep_enable (ep, d); - if (result < 0) - return result; - - ep->driver_data = dev; - dev->out = d; - } - - return 0; -} -#endif - -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) { -#ifdef DEV_CONFIG_CDC - if (!dev->rndis && dev->cdc) { - result = ether_alt_ep_setup (dev, ep); - if (result == 0) - continue; + if (!cdc_active(dev)) { + result = usb_ep_enable (dev->in_ep, dev->in); + if (result != 0) { + DEBUG(dev, "enable %s --> %d\n", + dev->in_ep->name, result); + goto done; } -#endif - -#ifdef CONFIG_USB_ETH_RNDIS - if (dev->rndis && strcmp (ep->name, EP_STATUS_NAME) == 0) { - const struct usb_endpoint_descriptor *d; - 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 = d; - continue; - } - } else -#endif - { -#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) - result = ether_ep_setup (dev, ep); - if (result == 0) - continue; -#endif + result = usb_ep_enable (dev->out_ep, dev->out); + if (result != 0) { + DEBUG (dev, "enable %s --> %d\n", + dev->in_ep->name, result); + goto done; } - - /* stop on error */ - ERROR (dev, "can't enable %s, result %d\n", ep->name, result); - break; } - if (!result && (!dev->in_ep || !dev->out_ep)) - result = -ENODEV; +done: if (result == 0) result = alloc_requests (dev, qlen (gadget), gfp_flags); /* on error, disable any endpoints */ if (result < 0) { -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) - if (dev->status) + if (!subset_active(dev)) (void) usb_ep_disable (dev->status_ep); -#endif dev->status = NULL; -#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) - if (dev->rndis || !dev->cdc) { - if (dev->in) - (void) usb_ep_disable (dev->in_ep); - if (dev->out) - (void) usb_ep_disable (dev->out_ep); - } -#endif + (void) usb_ep_disable (dev->in_ep); + (void) usb_ep_disable (dev->out_ep); dev->in = NULL; dev->out = NULL; } else @@ -1095,8 +1016,7 @@ set_ether_config (struct eth_dev *dev, int gfp_flags) /* activate non-CDC configs right away * this isn't strictly according to the RNDIS spec */ -#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) - if (dev->rndis || !dev->cdc) { + if (!cdc_active (dev)) { netif_carrier_on (dev->net); if (netif_running (dev->net)) { spin_unlock (&dev->lock); @@ -1104,7 +1024,6 @@ set_ether_config (struct eth_dev *dev, int gfp_flags) spin_lock (&dev->lock); } } -#endif if (result == 0) DEBUG (dev, "qlen %d\n", qlen (gadget)); @@ -1124,6 +1043,7 @@ static void eth_reset_config (struct eth_dev *dev) netif_stop_queue (dev->net); netif_carrier_off (dev->net); + rndis_uninit(dev->rndis_config); /* disable endpoints, forcing (synchronous) completion of * pending i/o. then free the requests. @@ -1150,6 +1070,8 @@ static void eth_reset_config (struct eth_dev *dev) if (dev->status) { usb_ep_disable (dev->status_ep); } + dev->rndis = 0; + dev->cdc_filter = 0; dev->config = 0; } @@ -1162,9 +1084,6 @@ 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; - if (gadget_is_sa1100 (gadget) && dev->config && atomic_read (&dev->tx_qlen) != 0) { @@ -1174,12 +1093,8 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags) } eth_reset_config (dev); - /* default: pass all packets, no multicast filtering */ - dev->cdc_filter = DEFAULT_FILTER; - switch (number) { case DEV_CONFIG_VALUE: - dev->rndis = 0; result = set_ether_config (dev, gfp_flags); break; #ifdef CONFIG_USB_ETH_RNDIS @@ -1218,9 +1133,9 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags) dev->config = number; INFO (dev, "%s speed config #%d: %d mA, %s, using %s\n", speed, number, power, driver_desc, - dev->rndis + rndis_active(dev) ? "RNDIS" - : (dev->cdc + : (cdc_active(dev) ? "CDC Ethernet" : "CDC Ethernet Subset")); } @@ -1231,6 +1146,13 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags) #ifdef DEV_CONFIG_CDC +/* The interrupt endpoint is used in CDC networking models (Ethernet, ATM) + * only to notify the host about link status changes (which we support) or + * report completion of some encapsulated command (as used in RNDIS). Since + * we want this CDC Ethernet code to be vendor-neutral, we don't use that + * command mechanism; and only one status request is ever queued. + */ + static void eth_status_complete (struct usb_ep *ep, struct usb_request *req) { struct usb_cdc_notification *event = req->buf; @@ -1259,7 +1181,7 @@ static void eth_status_complete (struct usb_ep *ep, struct usb_request *req) } else if (value != -ECONNRESET) DEBUG (dev, "event %02x --> %d\n", event->bNotificationType, value); - event->bmRequestType = 0xff; + req->context = NULL; } static void issue_start_status (struct eth_dev *dev) @@ -1276,6 +1198,8 @@ static void issue_start_status (struct eth_dev *dev) * a "cancel the whole queue" primitive since any * unlink-one primitive has way too many error modes. * here, we "know" toggle is already clear... + * + * FIXME iff req->context != null just dequeue it */ usb_ep_disable (dev->status_ep); usb_ep_enable (dev->status_ep, dev->status); @@ -1292,6 +1216,8 @@ static void issue_start_status (struct eth_dev *dev) req->length = sizeof *event; req->complete = eth_status_complete; + req->context = dev; + value = usb_ep_queue (dev->status_ep, req, GFP_ATOMIC); if (value < 0) DEBUG (dev, "status buf queue --> %d\n", value); @@ -1351,9 +1277,9 @@ 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; - u16 wIndex = (__force u16) ctrl->wIndex; - u16 wValue = (__force u16) ctrl->wValue; - u16 wLength = (__force u16) ctrl->wLength; + u16 wIndex = le16_to_cpu(ctrl->wIndex); + u16 wValue = le16_to_cpu(ctrl->wValue); + u16 wLength = le16_to_cpu(ctrl->wLength); /* descriptors just go into the pre-allocated ep0 buffer, * while config change events may enable network traffic. @@ -1424,7 +1350,7 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) || !dev->config || wIndex > 1) break; - if (!dev->cdc && wIndex != 0) + if (!cdc_active(dev) && wIndex != 0) break; spin_lock (&dev->lock); @@ -1456,9 +1382,11 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) /* CDC requires the data transfers not be done from * the default interface setting ... also, setting - * the non-default interface clears filters etc. + * the non-default interface resets filters etc. */ if (wValue == 1) { + if (!cdc_active (dev)) + break; usb_ep_enable (dev->in_ep, dev->in); usb_ep_enable (dev->out_ep, dev->out); dev->cdc_filter = DEFAULT_FILTER; @@ -1492,11 +1420,11 @@ done_set_intf: || !dev->config || wIndex > 1) break; - if (!(dev->cdc || dev->rndis) && wIndex != 0) + if (!(cdc_active(dev) || rndis_active(dev)) && wIndex != 0) break; /* for CDC, iff carrier is on, data interface is active. */ - if (dev->rndis || wIndex != 1) + if (rndis_active(dev) || wIndex != 1) *(u8 *)req->buf = 0; else *(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0; @@ -1509,8 +1437,7 @@ done_set_intf: * wValue = packet filter bitmap */ if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) - || !dev->cdc - || dev->rndis + || !cdc_active(dev) || wLength != 0 || wIndex > 1) break; @@ -1534,7 +1461,7 @@ done_set_intf: */ case USB_CDC_SEND_ENCAPSULATED_COMMAND: if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) - || !dev->rndis + || !rndis_active(dev) || wLength > USB_BUFSIZ || wValue || rndis_control_intf.bInterfaceNumber @@ -1549,7 +1476,7 @@ done_set_intf: case USB_CDC_GET_ENCAPSULATED_RESPONSE: if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE) == ctrl->bRequestType - && dev->rndis + && rndis_active(dev) // && wLength >= 0x0400 && !wValue && rndis_control_intf.bInterfaceNumber @@ -1688,10 +1615,8 @@ rx_submit (struct eth_dev *dev, struct usb_request *req, int gfp_flags) */ size = (sizeof (struct ethhdr) + dev->net->mtu + RX_EXTRA); size += dev->out_ep->maxpacket - 1; -#ifdef CONFIG_USB_ETH_RNDIS - if (dev->rndis) + if (rndis_active(dev)) size += sizeof (struct rndis_packet_msg_type); -#endif size -= size % dev->out_ep->maxpacket; if ((skb = alloc_skb (size + NET_IP_ALIGN, gfp_flags)) == 0) { @@ -1735,11 +1660,9 @@ static void rx_complete (struct usb_ep *ep, struct usb_request *req) /* normal completion */ case 0: skb_put (skb, req->actual); -#ifdef CONFIG_USB_ETH_RNDIS /* we know MaxPacketsPerTransfer == 1 here */ - if (dev->rndis) + if (rndis_active(dev)) status = rndis_rm_hdr (skb); -#endif if (status < 0 || ETH_HLEN > skb->len || skb->len > ETH_FRAME_LEN) { @@ -1859,8 +1782,6 @@ static void rx_fill (struct eth_dev *dev, int gfp_flags) struct usb_request *req; unsigned long flags; - clear_bit (WORK_RX_MEMORY, &dev->todo); - /* fill unused rxq slots with some skb */ spin_lock_irqsave (&dev->lock, flags); while (!list_empty (&dev->rx_reqs)) { @@ -1883,11 +1804,9 @@ static void eth_work (void *_dev) { struct eth_dev *dev = _dev; - if (test_bit (WORK_RX_MEMORY, &dev->todo)) { + if (test_and_clear_bit (WORK_RX_MEMORY, &dev->todo)) { if (netif_running (dev->net)) rx_fill (dev, GFP_KERNEL); - else - clear_bit (WORK_RX_MEMORY, &dev->todo); } if (dev->todo) @@ -1971,8 +1890,7 @@ static int eth_start_xmit (struct sk_buff *skb, struct net_device *net) * or the hardware can't use skb buffers. * or there's not enough space for any RNDIS headers we need */ -#ifdef CONFIG_USB_ETH_RNDIS - if (dev->rndis) { + if (rndis_active(dev)) { struct sk_buff *skb_rndis; skb_rndis = skb_realloc_headroom (skb, @@ -1985,7 +1903,6 @@ static int eth_start_xmit (struct sk_buff *skb, struct net_device *net) rndis_add_hdr (skb); length = skb->len; } -#endif req->buf = skb->data; req->context = skb; req->complete = tx_complete; @@ -2018,9 +1935,7 @@ static int eth_start_xmit (struct sk_buff *skb, struct net_device *net) } if (retval) { -#ifdef CONFIG_USB_ETH_RNDIS drop: -#endif dev->stats.tx_dropped++; dev_kfree_skb_any (skb); spin_lock_irqsave (&dev->lock, flags); @@ -2036,27 +1951,31 @@ drop: #ifdef CONFIG_USB_ETH_RNDIS -static void rndis_send_media_state (struct eth_dev *dev, int connect) -{ - if (!dev) - return; - - if (connect) { - if (rndis_signal_connect (dev->rndis_config)) - return; - } else { - if (rndis_signal_disconnect (dev->rndis_config)) - return; - } -} +/* The interrupt endpoint is used in RNDIS to notify the host when messages + * other than data packets are available ... notably the REMOTE_NDIS_*_CMPLT + * messages, but also REMOTE_NDIS_INDICATE_STATUS_MSG and potentially even + * REMOTE_NDIS_KEEPALIVE_MSG. + * + * The RNDIS control queue is processed by GET_ENCAPSULATED_RESPONSE, and + * normally just one notification will be queued. + */ + +static struct usb_request *eth_req_alloc (struct usb_ep *, unsigned, unsigned); +static void eth_req_free (struct usb_ep *ep, struct usb_request *req); static void rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req) { + struct eth_dev *dev = ep->driver_data; + if (req->status || req->actual != req->length) - DEBUG ((struct eth_dev *) ep->driver_data, + DEBUG (dev, "rndis control ack complete --> %d, %d/%d\n", req->status, req->actual, req->length); + req->context = NULL; + + if (req != dev->stat_req) + eth_req_free(ep, req); } static int rndis_control_ack (struct net_device *net) @@ -2071,11 +1990,19 @@ static int rndis_control_ack (struct net_device *net) return -ENODEV; } + /* in case queue length > 1 */ + if (resp->context) { + resp = eth_req_alloc (dev->status_ep, 8, GFP_ATOMIC); + if (!resp) + return -ENOMEM; + } + /* Send RNDIS RESPONSE_AVAILABLE notification; * USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too */ resp->length = 8; resp->complete = rndis_control_ack_complete; + resp->context = dev; *((__le32 *) resp->buf) = __constant_cpu_to_le32 (1); *((__le32 *) resp->buf + 1) = __constant_cpu_to_le32 (0); @@ -2089,6 +2016,10 @@ static int rndis_control_ack (struct net_device *net) return 0; } +#else + +#define rndis_control_ack NULL + #endif /* RNDIS */ static void eth_start (struct eth_dev *dev, int gfp_flags) @@ -2101,14 +2032,12 @@ static void eth_start (struct eth_dev *dev, int gfp_flags) /* and open the tx floodgates */ atomic_set (&dev->tx_qlen, 0); netif_wake_queue (dev->net); -#ifdef CONFIG_USB_ETH_RNDIS - if (dev->rndis) { + if (rndis_active(dev)) { rndis_set_param_medium (dev->rndis_config, NDIS_MEDIUM_802_3, BITRATE(dev->gadget)/100); - rndis_send_media_state (dev, 1); + (void) rndis_signal_connect (dev->rndis_config); } -#endif } static int eth_open (struct net_device *net) @@ -2149,28 +2078,27 @@ static int eth_stop (struct net_device *net) } } -#ifdef CONFIG_USB_ETH_RNDIS - if (dev->rndis) { + if (rndis_active(dev)) { rndis_set_param_medium (dev->rndis_config, NDIS_MEDIUM_802_3, 0); - rndis_send_media_state (dev, 0); + (void) rndis_signal_disconnect (dev->rndis_config); } -#endif return 0; } /*-------------------------------------------------------------------------*/ -static struct usb_request *eth_req_alloc (struct usb_ep *ep, unsigned size) +static struct usb_request * +eth_req_alloc (struct usb_ep *ep, unsigned size, unsigned gfp_flags) { struct usb_request *req; - req = usb_ep_alloc_request (ep, GFP_KERNEL); + req = usb_ep_alloc_request (ep, gfp_flags); if (!req) return NULL; - req->buf = kmalloc (size, GFP_KERNEL); + req->buf = kmalloc (size, gfp_flags); if (!req->buf) { usb_ep_free_request (ep, req); req = NULL; @@ -2192,10 +2120,8 @@ eth_unbind (struct usb_gadget *gadget) struct eth_dev *dev = get_gadget_data (gadget); DEBUG (dev, "unbind\n"); -#ifdef CONFIG_USB_ETH_RNDIS rndis_deregister (dev->rndis_config); rndis_exit (); -#endif /* we've already been disconnected ... no i/o is active */ if (dev->req) { @@ -2368,13 +2294,11 @@ autoconf_fail: gadget->name); return -ENODEV; } - EP_IN_NAME = in_ep->name; in_ep->driver_data = in_ep; /* claim */ out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc); if (!out_ep) goto autoconf_fail; - EP_OUT_NAME = out_ep->name; out_ep->driver_data = out_ep; /* claim */ #if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) @@ -2384,7 +2308,6 @@ autoconf_fail: if (cdc || rndis) { status_ep = usb_ep_autoconfig (gadget, &fs_status_desc); if (status_ep) { - EP_STATUS_NAME = status_ep->name; status_ep->driver_data = status_ep; /* claim */ } else if (rndis) { dev_err (&gadget->dev, @@ -2426,7 +2349,7 @@ autoconf_fail: hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; #if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) - if (EP_STATUS_NAME) + if (status_ep) hs_status_desc.bEndpointAddress = fs_status_desc.bEndpointAddress; #endif @@ -2499,20 +2422,23 @@ autoconf_fail: SET_ETHTOOL_OPS(net, &ops); /* preallocate control message data and buffer */ - dev->req = eth_req_alloc (gadget->ep0, USB_BUFSIZ); + dev->req = eth_req_alloc (gadget->ep0, USB_BUFSIZ, GFP_KERNEL); if (!dev->req) goto fail; dev->req->complete = eth_setup_complete; /* ... and maybe likewise for status transfer */ +#ifdef DEV_CONFIG_CDC if (dev->status_ep) { dev->stat_req = eth_req_alloc (dev->status_ep, - STATUS_BYTECOUNT); + STATUS_BYTECOUNT, GFP_KERNEL); if (!dev->stat_req) { eth_req_free (gadget->ep0, dev->req); goto fail; } + dev->stat_req->context = NULL; } +#endif /* finish hookup to lower layer ... */ dev->gadget = gadget; @@ -2526,16 +2452,16 @@ autoconf_fail: netif_stop_queue (dev->net); netif_carrier_off (dev->net); - // SET_NETDEV_DEV (dev->net, &gadget->dev); + SET_NETDEV_DEV (dev->net, &gadget->dev); status = register_netdev (dev->net); if (status < 0) goto fail1; INFO (dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); INFO (dev, "using %s, OUT %s IN %s%s%s\n", gadget->name, - EP_OUT_NAME, EP_IN_NAME, - EP_STATUS_NAME ? " STATUS " : "", - EP_STATUS_NAME ? EP_STATUS_NAME : "" + out_ep->name, in_ep->name, + status_ep ? " STATUS " : "", + status_ep ? status_ep->name : "" ); INFO (dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n", net->dev_addr [0], net->dev_addr [1], @@ -2548,7 +2474,6 @@ autoconf_fail: dev->host_mac [2], dev->host_mac [3], dev->host_mac [4], dev->host_mac [5]); -#ifdef CONFIG_USB_ETH_RNDIS if (rndis) { u32 vendorID = 0; @@ -2565,7 +2490,7 @@ fail0: /* these set up a lot of the OIDs that RNDIS needs */ rndis_set_host_mac (dev->rndis_config, dev->host_mac); if (rndis_set_param_dev (dev->rndis_config, dev->net, - &dev->stats)) + &dev->stats, &dev->cdc_filter)) goto fail0; if (rndis_set_param_vendor (dev->rndis_config, vendorID, manufacturer)) @@ -2576,7 +2501,6 @@ fail0: goto fail0; INFO (dev, "RNDIS ready\n"); } -#endif return status; @@ -2610,11 +2534,8 @@ eth_resume (struct usb_gadget *gadget) /*-------------------------------------------------------------------------*/ static struct usb_gadget_driver eth_driver = { -#ifdef CONFIG_USB_GADGET_DUALSPEED - .speed = USB_SPEED_HIGH, -#else - .speed = USB_SPEED_FULL, -#endif + .speed = DEVSPEED, + .function = (char *) driver_desc, .bind = eth_bind, .unbind = eth_unbind, diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index a9be851..4f57085 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -81,6 +81,10 @@ * removable Default false, boolean for removable media * luns=N Default N = number of filenames, number of * LUNs to support + * stall Default determined according to the type of + * USB device controller (usually true), + * boolean to permit the driver to halt + * bulk endpoints * transport=XXX Default BBB, transport name (CB, CBI, or BBB) * protocol=YYY Default SCSI, protocol name (RBC, 8020 or * ATAPI, QIC, UFI, 8070, or SCSI; @@ -91,14 +95,10 @@ * buflen=N Default N=16384, buffer size used (will be * rounded down to a multiple of * PAGE_CACHE_SIZE) - * stall Default determined according to the type of - * USB device controller (usually true), - * boolean to permit the driver to halt - * bulk endpoints * * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro", - * "removable", and "luns" options are available; default values are used - * for everything else. + * "removable", "luns", and "stall" options are available; default values + * are used for everything else. * * The pathnames of the backing files and the ro settings are available in * the attribute files "file" and "ro" in the lun<n> subdirectory of the @@ -342,14 +342,15 @@ static struct { int num_ros; unsigned int nluns; + int removable; + int can_stall; + char *transport_parm; char *protocol_parm; - int removable; unsigned short vendor; unsigned short product; unsigned short release; unsigned int buflen; - int can_stall; int transport_type; char *transport_name; @@ -360,11 +361,11 @@ static struct { .transport_parm = "BBB", .protocol_parm = "SCSI", .removable = 0, + .can_stall = 1, .vendor = DRIVER_VENDOR_ID, .product = DRIVER_PRODUCT_ID, .release = 0xffff, // Use controller chip type .buflen = 16384, - .can_stall = 1, }; @@ -380,6 +381,9 @@ MODULE_PARM_DESC(luns, "number of LUNs"); module_param_named(removable, mod_data.removable, bool, S_IRUGO); MODULE_PARM_DESC(removable, "true to simulate removable media"); +module_param_named(stall, mod_data.can_stall, bool, S_IRUGO); +MODULE_PARM_DESC(stall, "false to prevent bulk stalls"); + /* In the non-TEST version, only the module parameters listed above * are available. */ @@ -404,9 +408,6 @@ MODULE_PARM_DESC(release, "USB release number"); module_param_named(buflen, mod_data.buflen, uint, S_IRUGO); MODULE_PARM_DESC(buflen, "I/O buffer size"); -module_param_named(stall, mod_data.can_stall, bool, S_IRUGO); -MODULE_PARM_DESC(stall, "false to prevent bulk stalls"); - #endif /* CONFIG_USB_FILE_STORAGE_TEST */ @@ -818,7 +819,7 @@ static void inline put_be32(u8 *buf, u32 val) buf[0] = val >> 24; buf[1] = val >> 16; buf[2] = val >> 8; - buf[3] = val; + buf[3] = val & 0xff; } @@ -1276,8 +1277,8 @@ static int class_setup_req(struct fsg_dev *fsg, { struct usb_request *req = fsg->ep0req; int value = -EOPNOTSUPP; - u16 w_index = ctrl->wIndex; - u16 w_length = ctrl->wLength; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_length = le16_to_cpu(ctrl->wLength); if (!fsg->config) return value; @@ -1312,7 +1313,7 @@ static int class_setup_req(struct fsg_dev *fsg, } VDBG(fsg, "get max LUN\n"); *(u8 *) req->buf = fsg->nluns - 1; - value = min(w_length, (u16) 1); + value = 1; break; } } @@ -1344,7 +1345,7 @@ static int class_setup_req(struct fsg_dev *fsg, "unknown class-specific control req " "%02x.%02x v%04x i%04x l%u\n", ctrl->bRequestType, ctrl->bRequest, - ctrl->wValue, w_index, w_length); + le16_to_cpu(ctrl->wValue), w_index, w_length); return value; } @@ -1358,9 +1359,8 @@ static int standard_setup_req(struct fsg_dev *fsg, { struct usb_request *req = fsg->ep0req; int value = -EOPNOTSUPP; - u16 w_index = ctrl->wIndex; - u16 w_value = ctrl->wValue; - u16 w_length = ctrl->wLength; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); /* Usually this just stores reply data in the pre-allocated ep0 buffer, * but config change events will also reconfigure hardware. */ @@ -1374,7 +1374,7 @@ static int standard_setup_req(struct fsg_dev *fsg, case USB_DT_DEVICE: VDBG(fsg, "get device descriptor\n"); - value = min(w_length, (u16) sizeof device_desc); + value = sizeof device_desc; memcpy(req->buf, &device_desc, value); break; #ifdef CONFIG_USB_GADGET_DUALSPEED @@ -1382,7 +1382,7 @@ static int standard_setup_req(struct fsg_dev *fsg, VDBG(fsg, "get device qualifier\n"); if (!fsg->gadget->is_dualspeed) break; - value = min(w_length, (u16) sizeof dev_qualifier); + value = sizeof dev_qualifier; memcpy(req->buf, &dev_qualifier, value); break; @@ -1401,8 +1401,6 @@ static int standard_setup_req(struct fsg_dev *fsg, req->buf, w_value >> 8, w_value & 0xff); - if (value >= 0) - value = min(w_length, (u16) value); break; case USB_DT_STRING: @@ -1411,8 +1409,6 @@ static int standard_setup_req(struct fsg_dev *fsg, /* wIndex == language code */ value = usb_gadget_get_string(&stringtab, w_value & 0xff, req->buf); - if (value >= 0) - value = min(w_length, (u16) value); break; } break; @@ -1438,7 +1434,7 @@ static int standard_setup_req(struct fsg_dev *fsg, break; VDBG(fsg, "get configuration\n"); *(u8 *) req->buf = fsg->config; - value = min(w_length, (u16) 1); + value = 1; break; case USB_REQ_SET_INTERFACE: @@ -1466,14 +1462,14 @@ static int standard_setup_req(struct fsg_dev *fsg, } VDBG(fsg, "get interface\n"); *(u8 *) req->buf = 0; - value = min(w_length, (u16) 1); + value = 1; break; default: VDBG(fsg, "unknown control req %02x.%02x v%04x i%04x l%u\n", ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); + w_value, w_index, le16_to_cpu(ctrl->wLength)); } return value; @@ -1485,6 +1481,7 @@ static int fsg_setup(struct usb_gadget *gadget, { struct fsg_dev *fsg = get_gadget_data(gadget); int rc; + int w_length = le16_to_cpu(ctrl->wLength); ++fsg->ep0_req_tag; // Record arrival of a new request fsg->ep0req->context = NULL; @@ -1498,9 +1495,9 @@ static int fsg_setup(struct usb_gadget *gadget, /* Respond with data/status or defer until later? */ if (rc >= 0 && rc != DELAYED_STATUS) { + rc = min(rc, w_length); fsg->ep0req->length = rc; - fsg->ep0req->zero = (rc < ctrl->wLength && - (rc % gadget->ep0->maxpacket) == 0); + fsg->ep0req->zero = rc < w_length; fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ? "ep0-in" : "ep0-out"); rc = ep0_queue(fsg); @@ -2660,7 +2657,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, } } - /* Check that the LUN values are oonsistent */ + /* Check that the LUN values are consistent */ if (transport_is_bbb()) { if (fsg->lun != lun) DBG(fsg, "using LUN %d from CBW, " diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 005db7c..ed773a9 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -70,7 +70,7 @@ MODULE_LICENSE("GPL"); * seem to behave quite as expected. Used by default. * * OUT dma documents design problems handling the common "short packet" - * transfer termination policy; it couldn't enabled by default, even + * transfer termination policy; it couldn't be enabled by default, even * if the OUT-dma abort problems had a resolution. */ static unsigned use_dma = 1; @@ -313,7 +313,7 @@ goku_free_request(struct usb_ep *_ep, struct usb_request *_req) #if defined(CONFIG_X86) #define USE_KMALLOC -#elif defined(CONFIG_MIPS) && !defined(CONFIG_NONCOHERENT_IO) +#elif defined(CONFIG_MIPS) && !defined(CONFIG_DMA_NONCOHERENT) #define USE_KMALLOC #elif defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE) @@ -1524,9 +1524,12 @@ static void ep0_setup(struct goku_udc *dev) /* read SETUP packet and enter DATA stage */ ctrl.bRequestType = readl(®s->bRequestType); ctrl.bRequest = readl(®s->bRequest); - ctrl.wValue = (readl(®s->wValueH) << 8) | readl(®s->wValueL); - ctrl.wIndex = (readl(®s->wIndexH) << 8) | readl(®s->wIndexL); - ctrl.wLength = (readl(®s->wLengthH) << 8) | readl(®s->wLengthL); + ctrl.wValue = cpu_to_le16((readl(®s->wValueH) << 8) + | readl(®s->wValueL)); + ctrl.wIndex = cpu_to_le16((readl(®s->wIndexH) << 8) + | readl(®s->wIndexL)); + ctrl.wLength = cpu_to_le16((readl(®s->wLengthH) << 8) + | readl(®s->wLengthL)); writel(0, ®s->SetupRecv); nuke(&dev->ep[0], 0); @@ -1548,18 +1551,20 @@ static void ep0_setup(struct goku_udc *dev) case USB_REQ_CLEAR_FEATURE: switch (ctrl.bRequestType) { case USB_RECIP_ENDPOINT: - tmp = ctrl.wIndex & 0x0f; + tmp = le16_to_cpu(ctrl.wIndex) & 0x0f; /* active endpoint */ if (tmp > 3 || (!dev->ep[tmp].desc && tmp != 0)) goto stall; - if (ctrl.wIndex & USB_DIR_IN) { + if (ctrl.wIndex & __constant_cpu_to_le16( + USB_DIR_IN)) { if (!dev->ep[tmp].is_in) goto stall; } else { if (dev->ep[tmp].is_in) goto stall; } - if (ctrl.wValue != USB_ENDPOINT_HALT) + if (ctrl.wValue != __constant_cpu_to_le16( + USB_ENDPOINT_HALT)) goto stall; if (tmp) goku_clear_halt(&dev->ep[tmp]); @@ -1571,7 +1576,7 @@ succeed: return; case USB_RECIP_DEVICE: /* device remote wakeup: always clear */ - if (ctrl.wValue != 1) + if (ctrl.wValue != __constant_cpu_to_le16(1)) goto stall; VDBG(dev, "clear dev remote wakeup\n"); goto succeed; @@ -1589,14 +1594,15 @@ succeed: #ifdef USB_TRACE VDBG(dev, "SETUP %02x.%02x v%04x i%04x l%04x\n", ctrl.bRequestType, ctrl.bRequest, - ctrl.wValue, ctrl.wIndex, ctrl.wLength); + le16_to_cpu(ctrl.wValue), le16_to_cpu(ctrl.wIndex), + le16_to_cpu(ctrl.wLength)); #endif /* hw wants to know when we're configured (or not) */ dev->req_config = (ctrl.bRequest == USB_REQ_SET_CONFIGURATION && ctrl.bRequestType == USB_RECIP_DEVICE); if (unlikely(dev->req_config)) - dev->configured = (ctrl.wValue != 0); + dev->configured = (ctrl.wValue != __constant_cpu_to_le16(0)); /* delegate everything to the gadget driver. * it may respond after this irq handler returns. diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 1e5e6dd..0208153 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -417,8 +417,8 @@ ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr) goto free1; value = ep_io (data, kbuf, len); - VDEBUG (data->dev, "%s read %d OUT, status %d\n", - data->name, len, value); + VDEBUG (data->dev, "%s read %zu OUT, status %d\n", + data->name, len, (int) value); if (value >= 0 && copy_to_user (buf, kbuf, value)) value = -EFAULT; @@ -465,8 +465,8 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) } value = ep_io (data, kbuf, len); - VDEBUG (data->dev, "%s write %d IN, status %d\n", - data->name, len, value); + VDEBUG (data->dev, "%s write %zu IN, status %d\n", + data->name, len, (int) value); free1: up (&data->lock); kfree (kbuf); @@ -1318,8 +1318,8 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) struct usb_request *req = dev->req; int value = -EOPNOTSUPP; struct usb_gadgetfs_event *event; - u16 w_value = ctrl->wValue; - u16 w_length = ctrl->wLength; + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); spin_lock (&dev->lock); dev->setup_abort = 0; diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/ndis.h index c553bbf..09e3ee4 100644 --- a/drivers/usb/gadget/ndis.h +++ b/drivers/usb/gadget/ndis.h @@ -47,17 +47,17 @@ struct NDIS_PM_WAKE_UP_CAPABILITIES { #define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004 struct NDIS_PNP_CAPABILITIES { - u32 Flags; + __le32 Flags; struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities; }; struct NDIS_PM_PACKET_PATTERN { - u32 Priority; - u32 Reserved; - u32 MaskSize; - u32 PatternOffset; - u32 PatternSize; - u32 PatternFlags; + __le32 Priority; + __le32 Reserved; + __le32 MaskSize; + __le32 PatternOffset; + __le32 PatternSize; + __le32 PatternFlags; }; diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index e47e398..13a3dbc 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -448,7 +448,7 @@ net2280_free_request (struct usb_ep *_ep, struct usb_request *_req) #elif defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE) #define USE_KMALLOC -#elif defined(CONFIG_MIPS) && !defined(CONFIG_NONCOHERENT_IO) +#elif defined(CONFIG_MIPS) && !defined(CONFIG_DMA_NONCOHERENT) #define USE_KMALLOC /* FIXME there are other cases, including an x86-64 one ... */ @@ -1113,7 +1113,7 @@ static void restart_dma (struct net2280_ep *ep) if (ep->in_fifo_validate) dmactl |= (1 << DMA_FIFO_VALIDATE); list_for_each_entry (entry, &ep->queue, queue) { - u32 dmacount; + __le32 dmacount; if (entry == req) continue; @@ -1238,7 +1238,7 @@ static int net2280_dequeue (struct usb_ep *_ep, struct usb_request *_req) &ep->dma->dmadesc); if (req->td->dmacount & dma_done_ie) writel (readl (&ep->dma->dmacount) - | dma_done_ie, + | le32_to_cpu(dma_done_ie), &ep->dma->dmacount); } else { struct net2280_request *prev; @@ -1779,6 +1779,9 @@ static void set_fifo_mode (struct net2280 *dev, int mode) list_add_tail (&dev->ep [6].ep.ep_list, &dev->gadget.ep_list); } +/* just declare this in any driver that really need it */ +extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode); + /** * net2280_set_fifo_mode - change allocation of fifo buffers * @gadget: access to the net2280 device that will be updated @@ -2382,9 +2385,9 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) cpu_to_le32s (&u.raw [0]); cpu_to_le32s (&u.raw [1]); - le16_to_cpus (&u.r.wValue); - le16_to_cpus (&u.r.wIndex); - le16_to_cpus (&u.r.wLength); +#define w_value le16_to_cpup (&u.r.wValue) +#define w_index le16_to_cpup (&u.r.wIndex) +#define w_length le16_to_cpup (&u.r.wLength) /* ack the irq */ writel (1 << SETUP_PACKET_INTERRUPT, &dev->regs->irqstat0); @@ -2413,25 +2416,25 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) switch (u.r.bRequest) { case USB_REQ_GET_STATUS: { struct net2280_ep *e; - u16 status; + __le32 status; /* hw handles device and interface 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) + if ((e = get_ep_by_addr (dev, w_index)) == 0 + || w_length > 2) goto do_stall; if (readl (&e->regs->ep_rsp) & (1 << SET_ENDPOINT_HALT)) - status = __constant_cpu_to_le16 (1); + status = __constant_cpu_to_le32 (1); else - status = __constant_cpu_to_le16 (0); + status = __constant_cpu_to_le32 (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); + set_fifo_bytecount (ep, w_length); + writel ((__force u32)status, &dev->epregs [0].ep_data); allow_status (ep); VDEBUG (dev, "%s stat %02x\n", ep->ep.name, status); goto next_endpoints; @@ -2443,10 +2446,10 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) /* hw handles device features */ if (u.r.bRequestType != USB_RECIP_ENDPOINT) goto delegate; - if (u.r.wValue != USB_ENDPOINT_HALT - || u.r.wLength != 0) + if (w_value != USB_ENDPOINT_HALT + || w_length != 0) goto do_stall; - if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0) + if ((e = get_ep_by_addr (dev, w_index)) == 0) goto do_stall; clear_halt (e); allow_status (ep); @@ -2460,10 +2463,10 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) /* hw handles device features */ if (u.r.bRequestType != USB_RECIP_ENDPOINT) goto delegate; - if (u.r.wValue != USB_ENDPOINT_HALT - || u.r.wLength != 0) + if (w_value != USB_ENDPOINT_HALT + || w_length != 0) goto do_stall; - if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0) + if ((e = get_ep_by_addr (dev, w_index)) == 0) goto do_stall; set_halt (e); allow_status (ep); @@ -2473,10 +2476,10 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) break; default: delegate: - VDEBUG (dev, "setup %02x.%02x v%04x i%04x " + VDEBUG (dev, "setup %02x.%02x v%04x i%04x l%04x" "ep_cfg %08x\n", u.r.bRequestType, u.r.bRequest, - u.r.wValue, u.r.wIndex, + w_value, w_index, w_length, readl (&ep->regs->ep_cfg)); spin_unlock (&dev->lock); tmp = dev->driver->setup (&dev->gadget, &u.r); @@ -2497,6 +2500,10 @@ do_stall: */ } +#undef w_value +#undef w_index +#undef w_length + next_endpoints: /* endpoint data irq ? */ scratch = stat & 0x7f; @@ -2653,7 +2660,7 @@ static void handle_stat1_irqs (struct net2280 *dev, u32 stat) restart_dma (ep); else if (ep->is_in && use_dma_chaining) { struct net2280_request *req; - u32 dmacount; + __le32 dmacount; /* the descriptor at the head of the chain * may still have VALID_BIT clear; that's diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 98cbcbc..a2b812a 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -52,7 +52,6 @@ #include <asm/mach-types.h> #include <asm/arch/dma.h> -#include <asm/arch/mux.h> #include <asm/arch/usb.h> #include "omap_udc.h" @@ -167,7 +166,7 @@ static int omap_ep_enable(struct usb_ep *_ep, maxp = le16_to_cpu (desc->wMaxPacketSize); if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK && maxp != ep->maxpacket) - || desc->wMaxPacketSize > ep->maxpacket + || le16_to_cpu(desc->wMaxPacketSize) > ep->maxpacket || !desc->wMaxPacketSize) { DBG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name); return -ERANGE; @@ -214,7 +213,7 @@ static int omap_ep_enable(struct usb_ep *_ep, ep->has_dma = 0; ep->lch = -1; use_ep(ep, UDC_EP_SEL); - UDC_CTRL_REG = UDC_RESET_EP; + UDC_CTRL_REG = udc->clr_halt; ep->ackwait = 0; deselect_ep(); @@ -253,7 +252,7 @@ static int omap_ep_disable(struct usb_ep *_ep) } spin_lock_irqsave(&ep->udc->lock, flags); - ep->desc = 0; + ep->desc = NULL; nuke (ep, -ESHUTDOWN); ep->ep.maxpacket = ep->maxpacket; ep->has_dma = 0; @@ -388,8 +387,8 @@ done(struct omap_ep *ep, struct omap_req *req, int status) /*-------------------------------------------------------------------------*/ -#define FIFO_FULL (UDC_NON_ISO_FIFO_FULL | UDC_ISO_FIFO_FULL) -#define FIFO_UNWRITABLE (UDC_EP_HALTED | FIFO_FULL) +#define UDC_FIFO_FULL (UDC_NON_ISO_FIFO_FULL | UDC_ISO_FIFO_FULL) +#define UDC_FIFO_UNWRITABLE (UDC_EP_HALTED | UDC_FIFO_FULL) #define FIFO_EMPTY (UDC_NON_ISO_FIFO_EMPTY | UDC_ISO_FIFO_EMPTY) #define FIFO_UNREADABLE (UDC_EP_HALTED | FIFO_EMPTY) @@ -433,7 +432,7 @@ static int write_fifo(struct omap_ep *ep, struct omap_req *req) /* PIO-IN isn't double buffered except for iso */ ep_stat = UDC_STAT_FLG_REG; - if (ep_stat & FIFO_UNWRITABLE) + if (ep_stat & UDC_FIFO_UNWRITABLE) return 0; count = ep->ep.maxpacket; @@ -504,7 +503,7 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req) if (ep_stat & UDC_EP_HALTED) break; - if (ep_stat & FIFO_FULL) + if (ep_stat & UDC_FIFO_FULL) avail = ep->ep.maxpacket; else { avail = UDC_RXFSTAT_REG; @@ -538,6 +537,32 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req) /*-------------------------------------------------------------------------*/ +static inline dma_addr_t dma_csac(unsigned lch) +{ + dma_addr_t csac; + + /* omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is + * read before the DMA controller finished disabling the channel. + */ + csac = omap_readw(OMAP_DMA_CSAC(lch)); + if (csac == 0) + csac = omap_readw(OMAP_DMA_CSAC(lch)); + return csac; +} + +static inline dma_addr_t dma_cdac(unsigned lch) +{ + dma_addr_t cdac; + + /* omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is + * read before the DMA controller finished disabling the channel. + */ + cdac = omap_readw(OMAP_DMA_CDAC(lch)); + if (cdac == 0) + cdac = omap_readw(OMAP_DMA_CDAC(lch)); + return cdac; +} + static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start) { dma_addr_t end; @@ -548,7 +573,7 @@ static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start) if (cpu_is_omap15xx()) return 0; - end = omap_readw(OMAP_DMA_CSAC(ep->lch)); + end = dma_csac(ep->lch); if (end == ep->dma_counter) return 0; @@ -559,14 +584,14 @@ static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start) } #define DMA_DEST_LAST(x) (cpu_is_omap15xx() \ - ? OMAP_DMA_CSAC(x) /* really: CPC */ \ - : OMAP_DMA_CDAC(x)) + ? omap_readw(OMAP_DMA_CSAC(x)) /* really: CPC */ \ + : dma_cdac(x)) static u16 dma_dest_len(struct omap_ep *ep, dma_addr_t start) { dma_addr_t end; - end = omap_readw(DMA_DEST_LAST(ep->lch)); + end = DMA_DEST_LAST(ep->lch); if (end == ep->dma_counter) return 0; @@ -593,7 +618,7 @@ static void next_in_dma(struct omap_ep *ep, struct omap_req *req) : OMAP_DMA_SYNC_ELEMENT; /* measure length in either bytes or packets */ - if ((cpu_is_omap16xx() && length <= (UDC_TXN_TSC + 1)) + if ((cpu_is_omap16xx() && length <= UDC_TXN_TSC) || (cpu_is_omap15xx() && length < ep->maxpacket)) { txdma_ctrl = UDC_TXN_EOT | length; omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, @@ -602,15 +627,15 @@ static void next_in_dma(struct omap_ep *ep, struct omap_req *req) length = min(length / ep->maxpacket, (unsigned) UDC_TXN_TSC + 1); txdma_ctrl = length; - omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, - ep->ep.maxpacket, length, sync_mode); + omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, + ep->ep.maxpacket >> 1, length, sync_mode); length *= ep->maxpacket; } omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual); omap_start_dma(ep->lch); - ep->dma_counter = omap_readw(OMAP_DMA_CSAC(ep->lch)); + ep->dma_counter = dma_csac(ep->lch); UDC_DMA_IRQ_EN_REG |= UDC_TX_DONE_IE(ep->dma_channel); UDC_TXDMA_REG(ep->dma_channel) = UDC_TXN_START | txdma_ctrl; req->dma_bytes = length; @@ -650,12 +675,12 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req) packets = (req->req.length - req->req.actual) / ep->ep.maxpacket; packets = min(packets, (unsigned)UDC_RXN_TC + 1); req->dma_bytes = packets * ep->ep.maxpacket; - omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, - ep->ep.maxpacket, packets, + omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, + ep->ep.maxpacket >> 1, packets, OMAP_DMA_SYNC_ELEMENT); omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual); - ep->dma_counter = omap_readw(DMA_DEST_LAST(ep->lch)); + ep->dma_counter = DMA_DEST_LAST(ep->lch); UDC_RXDMA_REG(ep->dma_channel) = UDC_RXN_STOP | (packets - 1); UDC_DMA_IRQ_EN_REG |= UDC_RX_EOT_IE(ep->dma_channel); @@ -763,7 +788,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) reg = UDC_TXDMA_CFG_REG; else reg = UDC_RXDMA_CFG_REG; - reg |= 1 << 12; /* "pulse" activated */ + reg |= UDC_DMA_REQ; /* "pulse" activated */ ep->dma_channel = 0; ep->lch = -1; @@ -787,6 +812,11 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) ep->ep.name, dma_error, ep, &ep->lch); if (status == 0) { UDC_TXDMA_CFG_REG = reg; + /* EMIFF */ + omap_set_dma_src_burst_mode(ep->lch, + OMAP_DMA_DATA_BURST_4); + omap_set_dma_src_data_pack(ep->lch, 1); + /* TIPB */ omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_TIPB, OMAP_DMA_AMODE_CONSTANT, @@ -797,10 +827,15 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) ep->ep.name, dma_error, ep, &ep->lch); if (status == 0) { UDC_RXDMA_CFG_REG = reg; + /* TIPB */ omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_TIPB, OMAP_DMA_AMODE_CONSTANT, (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG)); + /* EMIFF */ + omap_set_dma_dest_burst_mode(ep->lch, + OMAP_DMA_DATA_BURST_4); + omap_set_dma_dest_data_pack(ep->lch, 1); } } if (status) @@ -856,7 +891,7 @@ static void dma_channel_release(struct omap_ep *ep) if (!list_empty(&ep->queue)) req = container_of(ep->queue.next, struct omap_req, queue); else - req = 0; + req = NULL; active = ((1 << 7) & omap_readl(OMAP_DMA_CCR(ep->lch))) != 0; @@ -865,9 +900,13 @@ static void dma_channel_release(struct omap_ep *ep) (ep->bEndpointAddress & USB_DIR_IN) ? 't' : 'r', ep->dma_channel - 1, req); + /* NOTE: re-setting RX_REQ/TX_REQ because of a chip bug (before + * OMAP 1710 ES2.0) where reading the DMA_CFG can clear them. + */ + /* wait till current packet DMA finishes, and fifo empties */ if (ep->bEndpointAddress & USB_DIR_IN) { - UDC_TXDMA_CFG_REG &= ~mask; + UDC_TXDMA_CFG_REG = (UDC_TXDMA_CFG_REG & ~mask) | UDC_DMA_REQ; if (req) { finish_in_dma(ep, req, -ECONNRESET); @@ -880,7 +919,7 @@ static void dma_channel_release(struct omap_ep *ep) while (UDC_TXDMA_CFG_REG & mask) udelay(10); } else { - UDC_RXDMA_CFG_REG &= ~mask; + UDC_RXDMA_CFG_REG = (UDC_RXDMA_CFG_REG & ~mask) | UDC_DMA_REQ; /* dma empties the fifo */ while (UDC_RXDMA_CFG_REG & mask) @@ -997,18 +1036,19 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) UDC_IRQ_EN_REG = irq_en; } - /* STATUS is reverse direction */ - UDC_EP_NUM_REG = is_in - ? UDC_EP_SEL - : (UDC_EP_SEL|UDC_EP_DIR); + /* STATUS for zero length DATA stages is + * always an IN ... even for IN transfers, + * a wierd case which seem to stall OMAP. + */ + UDC_EP_NUM_REG = (UDC_EP_SEL|UDC_EP_DIR); UDC_CTRL_REG = UDC_CLR_EP; UDC_CTRL_REG = UDC_SET_FIFO_EN; - UDC_EP_NUM_REG = udc->ep0_in ? 0 : UDC_EP_DIR; + UDC_EP_NUM_REG = UDC_EP_DIR; /* cleanup */ udc->ep0_pending = 0; done(ep, req, 0); - req = 0; + req = NULL; /* non-empty DATA stage */ } else if (is_in) { @@ -1029,7 +1069,7 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) (is_in ? next_in_dma : next_out_dma)(ep, req); else if (req) { if ((is_in ? write_fifo : read_fifo)(ep, req) == 1) - req = 0; + req = NULL; deselect_ep(); if (!is_in) { UDC_CTRL_REG = UDC_SET_FIFO_EN; @@ -1041,7 +1081,7 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) irq_wait: /* irq handler advances the queue */ - if (req != 0) + if (req != NULL) list_add_tail(&req->queue, &ep->queue); spin_unlock_irqrestore(&udc->lock, flags); @@ -1140,7 +1180,7 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value) dma_channel_claim(ep, channel); } else { use_ep(ep, 0); - UDC_CTRL_REG = UDC_RESET_EP; + UDC_CTRL_REG = ep->udc->clr_halt; ep->ackwait = 0; if (!(ep->bEndpointAddress & USB_DIR_IN)) { UDC_CTRL_REG = UDC_SET_FIFO_EN; @@ -1238,6 +1278,8 @@ static int can_pullup(struct omap_udc *udc) static void pullup_enable(struct omap_udc *udc) { + udc->gadget.dev.parent->power.power_state = PMSG_ON; + udc->gadget.dev.power.power_state = PMSG_ON; UDC_SYSCON1_REG |= UDC_PULLUP_EN; #ifndef CONFIG_USB_OTG if (!cpu_is_omap15xx()) @@ -1382,7 +1424,7 @@ static void update_otg(struct omap_udc *udc) static void ep0_irq(struct omap_udc *udc, u16 irq_src) { struct omap_ep *ep0 = &udc->ep[0]; - struct omap_req *req = 0; + struct omap_req *req = NULL; ep0->irqs++; @@ -1438,7 +1480,7 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) if (req) done(ep0, req, 0); } - req = 0; + req = NULL; } else if (stat & UDC_STALL) { UDC_CTRL_REG = UDC_CLR_HALT; UDC_EP_NUM_REG = UDC_EP_DIR; @@ -1511,9 +1553,10 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) u.word[3] = UDC_DATA_REG; UDC_EP_NUM_REG = 0; } while (UDC_IRQ_SRC_REG & UDC_SETUP); - le16_to_cpus (&u.r.wValue); - le16_to_cpus (&u.r.wIndex); - le16_to_cpus (&u.r.wLength); + +#define w_value le16_to_cpup (&u.r.wValue) +#define w_index le16_to_cpup (&u.r.wIndex) +#define w_length le16_to_cpup (&u.r.wLength) /* Delegate almost all control requests to the gadget driver, * except for a handful of ch9 status/feature requests that @@ -1529,11 +1572,11 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) /* udc needs to know when ep != 0 is valid */ if (u.r.bRequestType != USB_RECIP_DEVICE) goto delegate; - if (u.r.wLength != 0) + if (w_length != 0) goto do_stall; udc->ep0_set_config = 1; - udc->ep0_reset_config = (u.r.wValue == 0); - VDBG("set config %d\n", u.r.wValue); + udc->ep0_reset_config = (w_value == 0); + VDBG("set config %d\n", w_value); /* update udc NOW since gadget driver may start * queueing requests immediately; clear config @@ -1549,23 +1592,28 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) /* clear endpoint halt */ if (u.r.bRequestType != USB_RECIP_ENDPOINT) goto delegate; - if (u.r.wValue != USB_ENDPOINT_HALT - || u.r.wLength != 0) + if (w_value != USB_ENDPOINT_HALT + || w_length != 0) goto do_stall; - ep = &udc->ep[u.r.wIndex & 0xf]; + ep = &udc->ep[w_index & 0xf]; if (ep != ep0) { - if (u.r.wIndex & USB_DIR_IN) + if (w_index & USB_DIR_IN) ep += 16; if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC || !ep->desc) goto do_stall; use_ep(ep, 0); - UDC_CTRL_REG = UDC_RESET_EP; + UDC_CTRL_REG = udc->clr_halt; ep->ackwait = 0; if (!(ep->bEndpointAddress & USB_DIR_IN)) { UDC_CTRL_REG = UDC_SET_FIFO_EN; ep->ackwait = 1 + ep->double_buf; } + /* NOTE: assumes the host behaves sanely, + * only clearing real halts. Else we may + * need to kill pending transfers and then + * restart the queue... very messy for DMA! + */ } VDBG("%s halt cleared by host\n", ep->name); goto ep0out_status_stage; @@ -1573,11 +1621,11 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) /* set endpoint halt */ if (u.r.bRequestType != USB_RECIP_ENDPOINT) goto delegate; - if (u.r.wValue != USB_ENDPOINT_HALT - || u.r.wLength != 0) + if (w_value != USB_ENDPOINT_HALT + || w_length != 0) goto do_stall; - ep = &udc->ep[u.r.wIndex & 0xf]; - if (u.r.wIndex & USB_DIR_IN) + ep = &udc->ep[w_index & 0xf]; + if (w_index & USB_DIR_IN) ep += 16; if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC || ep == ep0 || !ep->desc) @@ -1615,13 +1663,13 @@ ep0out_status_stage: UDC_CTRL_REG = UDC_SET_FIFO_EN; UDC_EP_NUM_REG = UDC_EP_DIR; status = 0; - VDBG("GET_STATUS, interface %d\n", u.r.wIndex); + VDBG("GET_STATUS, interface %d\n", w_index); /* next, status stage */ break; default: delegate: /* activate the ep0out fifo right away */ - if (!udc->ep0_in && u.r.wLength) { + if (!udc->ep0_in && w_length) { UDC_EP_NUM_REG = 0; UDC_CTRL_REG = UDC_SET_FIFO_EN; } @@ -1632,7 +1680,11 @@ delegate: */ VDBG("SETUP %02x.%02x v%04x i%04x l%04x\n", u.r.bRequestType, u.r.bRequest, - u.r.wValue, u.r.wIndex, u.r.wLength); + w_value, w_index, w_length); + +#undef w_value +#undef w_index +#undef w_length /* The gadget driver may return an error here, * causing an immediate protocol stall. @@ -2013,7 +2065,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) udc->softconnect = 1; /* hook up the driver */ - driver->driver.bus = 0; + driver->driver.bus = NULL; udc->driver = driver; udc->gadget.dev.driver = &driver->driver; spin_unlock_irqrestore(&udc->lock, flags); @@ -2021,8 +2073,8 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) status = driver->bind (&udc->gadget); if (status) { DBG("bind to %s --> %d\n", driver->driver.name, status); - udc->gadget.dev.driver = 0; - udc->driver = 0; + udc->gadget.dev.driver = NULL; + udc->driver = NULL; goto done; } DBG("bound to driver %s\n", driver->driver.name); @@ -2035,8 +2087,8 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) if (status < 0) { ERR("can't bind to transceiver\n"); driver->unbind (&udc->gadget); - udc->gadget.dev.driver = 0; - udc->driver = 0; + udc->gadget.dev.driver = NULL; + udc->driver = NULL; goto done; } } else { @@ -2071,7 +2123,7 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) omap_vbus_session(&udc->gadget, 0); if (udc->transceiver) - (void) otg_set_peripheral(udc->transceiver, 0); + (void) otg_set_peripheral(udc->transceiver, NULL); else pullup_disable(udc); @@ -2080,9 +2132,8 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) spin_unlock_irqrestore(&udc->lock, flags); driver->unbind(&udc->gadget); - udc->gadget.dev.driver = 0; - udc->driver = 0; - + udc->gadget.dev.driver = NULL; + udc->driver = NULL; DBG("unregistered driver '%s'\n", driver->driver.name); return status; @@ -2178,14 +2229,14 @@ static int proc_otg_show(struct seq_file *s) tmp = OTG_REV_REG; trans = USB_TRANSCEIVER_CTRL_REG; - seq_printf(s, "OTG rev %d.%d, transceiver_ctrl %03x\n", + seq_printf(s, "\nOTG rev %d.%d, transceiver_ctrl %05x\n", tmp >> 4, tmp & 0xf, trans); tmp = OTG_SYSCON_1_REG; seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s," FOURBITS "\n", tmp, trx_mode(USB2_TRX_MODE(tmp), trans & CONF_USB2_UNI_R), trx_mode(USB1_TRX_MODE(tmp), trans & CONF_USB1_UNI_R), - (USB0_TRX_MODE(tmp) == 0) + (USB0_TRX_MODE(tmp) == 0 && !cpu_is_omap1710()) ? "internal" : trx_mode(USB0_TRX_MODE(tmp), 1), (tmp & OTG_IDLE_EN) ? " !otg" : "", @@ -2235,6 +2286,7 @@ static int proc_otg_show(struct seq_file *s) seq_printf(s, "otg_outctrl %04x" "\n", tmp); tmp = OTG_TEST_REG; seq_printf(s, "otg_test %04x" "\n", tmp); + return 0; } static int proc_udc_show(struct seq_file *s, void *_) @@ -2378,7 +2430,7 @@ static int proc_udc_show(struct seq_file *s, void *_) static int proc_udc_open(struct inode *inode, struct file *file) { - return single_open(file, proc_udc_show, 0); + return single_open(file, proc_udc_show, NULL); } static struct file_operations proc_ops = { @@ -2399,7 +2451,7 @@ static void create_proc_file(void) static void remove_proc_file(void) { - remove_proc_entry(proc_filename, 0); + remove_proc_entry(proc_filename, NULL); } #else @@ -2414,6 +2466,10 @@ static inline void remove_proc_file(void) {} /* Before this controller can enumerate, we need to pick an endpoint * configuration, or "fifo_mode" That involves allocating 2KB of packet * buffer space among the endpoints we'll be operating. + * + * NOTE: as of OMAP 1710 ES2.0, writing a new endpoint config when + * UDC_SYSCON_1_REG.CFG_LOCK is set can now work. We won't use that + * capability yet though. */ static unsigned __init omap_ep_setup(char *name, u8 addr, u8 type, @@ -2505,7 +2561,7 @@ static void omap_udc_release(struct device *dev) { complete(udc->done); kfree (udc); - udc = 0; + udc = NULL; } static int __init @@ -2577,23 +2633,33 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) case 1: OMAP_BULK_EP("ep1in", USB_DIR_IN | 1); OMAP_BULK_EP("ep2out", USB_DIR_OUT | 2); + OMAP_INT_EP("ep9in", USB_DIR_IN | 9, 16); + OMAP_BULK_EP("ep3in", USB_DIR_IN | 3); OMAP_BULK_EP("ep4out", USB_DIR_OUT | 4); + OMAP_INT_EP("ep10in", USB_DIR_IN | 10, 16); OMAP_BULK_EP("ep5in", USB_DIR_IN | 5); OMAP_BULK_EP("ep5out", USB_DIR_OUT | 5); + OMAP_INT_EP("ep11in", USB_DIR_IN | 11, 16); + OMAP_BULK_EP("ep6in", USB_DIR_IN | 6); OMAP_BULK_EP("ep6out", USB_DIR_OUT | 6); + OMAP_INT_EP("ep12in", USB_DIR_IN | 12, 16); OMAP_BULK_EP("ep7in", USB_DIR_IN | 7); OMAP_BULK_EP("ep7out", USB_DIR_OUT | 7); + OMAP_INT_EP("ep13in", USB_DIR_IN | 13, 16); + OMAP_INT_EP("ep13out", USB_DIR_OUT | 13, 16); + OMAP_BULK_EP("ep8in", USB_DIR_IN | 8); OMAP_BULK_EP("ep8out", USB_DIR_OUT | 8); + OMAP_INT_EP("ep14in", USB_DIR_IN | 14, 16); + OMAP_INT_EP("ep14out", USB_DIR_OUT | 14, 16); + + OMAP_BULK_EP("ep15in", USB_DIR_IN | 15); + OMAP_BULK_EP("ep15out", USB_DIR_OUT | 15); - OMAP_INT_EP("ep9in", USB_DIR_IN | 9, 16); - OMAP_INT_EP("ep10out", USB_DIR_IN | 10, 16); - OMAP_INT_EP("ep11in", USB_DIR_IN | 9, 16); - OMAP_INT_EP("ep12out", USB_DIR_IN | 10, 16); break; #ifdef USE_ISO @@ -2640,8 +2706,8 @@ static int __init omap_udc_probe(struct device *dev) struct platform_device *odev = to_platform_device(dev); int status = -ENODEV; int hmc; - struct otg_transceiver *xceiv = 0; - const char *type = 0; + struct otg_transceiver *xceiv = NULL; + const char *type = NULL; struct omap_usb_config *config = dev->platform_data; /* NOTE: "knows" the order of the resources! */ @@ -2676,54 +2742,78 @@ static int __init omap_udc_probe(struct device *dev) FUNC_MUX_CTRL_0_REG = tmp; } } else { + /* The transceiver may package some GPIO logic or handle + * loopback and/or transceiverless setup; if we find one, + * use it. Except for OTG, we don't _need_ to talk to one; + * but not having one probably means no VBUS detection. + */ + xceiv = otg_get_transceiver(); + if (xceiv) + type = xceiv->label; + else if (config->otg) { + DBG("OTG requires external transceiver!\n"); + goto cleanup0; + } + hmc = HMC_1610; switch (hmc) { + case 0: /* POWERUP DEFAULT == 0 */ + case 4: + case 12: + case 20: + if (!cpu_is_omap1710()) { + type = "integrated"; + break; + } + /* FALL THROUGH */ case 3: case 11: case 16: case 19: case 25: - xceiv = otg_get_transceiver(); if (!xceiv) { DBG("external transceiver not registered!\n"); - if (config->otg) - goto cleanup0; - type = "(unknown external)"; - } else - type = xceiv->label; - break; - case 0: /* POWERUP DEFAULT == 0 */ - case 4: - case 12: - case 20: - type = "INTEGRATED"; + type = "unknown"; + } break; case 21: /* internal loopback */ - type = "(loopback)"; + type = "loopback"; break; case 14: /* transceiverless */ - type = "(none)"; + if (cpu_is_omap1710()) + goto bad_on_1710; + /* FALL THROUGH */ + case 13: + case 15: + type = "no"; break; default: +bad_on_1710: ERR("unrecognized UDC HMC mode %d\n", hmc); - return -ENODEV; + goto cleanup0; } } - INFO("hmc mode %d, transceiver %s\n", hmc, type); + INFO("hmc mode %d, %s transceiver\n", hmc, type); /* a "gadget" abstracts/virtualizes the controller */ status = omap_udc_setup(odev, xceiv); if (status) { goto cleanup0; } - xceiv = 0; + xceiv = NULL; // "udc" is now valid pullup_disable(udc); #if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) udc->gadget.is_otg = (config->otg != 0); #endif + /* starting with omap1710 es2.0, clear toggle is a separate bit */ + if (UDC_REV_REG >= 0x61) + udc->clr_halt = UDC_RESET_EP | UDC_CLRDATA_TOGGLE; + else + udc->clr_halt = UDC_RESET_EP; + /* USB general purpose IRQ: ep0, state changes, dma, etc */ status = request_irq(odev->resource[1].start, omap_udc_irq, SA_SAMPLE_RANDOM, driver_name, udc); @@ -2765,7 +2855,7 @@ cleanup2: cleanup1: kfree (udc); - udc = 0; + udc = NULL; cleanup0: if (xceiv) @@ -2788,7 +2878,7 @@ static int __exit omap_udc_remove(struct device *dev) pullup_disable(udc); if (udc->transceiver) { put_device(udc->transceiver->dev); - udc->transceiver = 0; + udc->transceiver = NULL; } UDC_SYSCON1_REG = 0; @@ -2809,13 +2899,32 @@ static int __exit omap_udc_remove(struct device *dev) return 0; } -static int omap_udc_suspend(struct device *dev, pm_message_t state, u32 level) +/* suspend/resume/wakeup from sysfs (echo > power/state) or when the + * system is forced into deep sleep + * + * REVISIT we should probably reject suspend requests when there's a host + * session active, rather than disconnecting, at least on boards that can + * report VBUS irqs (UDC_DEVSTAT_REG.UDC_ATT). And in any case, we need to + * make host resumes and VBUS detection trigger OMAP wakeup events; that + * may involve talking to an external transceiver (e.g. isp1301). + */ +static int omap_udc_suspend(struct device *dev, pm_message_t message, u32 level) { - if (level != 0) + u32 devstat; + + if (level != SUSPEND_POWER_DOWN) return 0; + devstat = UDC_DEVSTAT_REG; + + /* we're requesting 48 MHz clock if the pullup is enabled + * (== we're attached to the host) and we're not suspended, + * which would prevent entry to deep sleep... + */ + if ((devstat & UDC_ATT) != 0 && (devstat & UDC_SUS) == 0) { + WARN("session active; suspend requires disconnect\n"); + omap_pullup(&udc->gadget, 0); + } - DBG("suspend, state %d\n", state); - omap_pullup(&udc->gadget, 0); udc->gadget.dev.power.power_state = PMSG_SUSPEND; udc->gadget.dev.parent->power.power_state = PMSG_SUSPEND; return 0; @@ -2823,7 +2932,7 @@ static int omap_udc_suspend(struct device *dev, pm_message_t state, u32 level) static int omap_udc_resume(struct device *dev, u32 level) { - if (level != 0) + if (level != RESUME_POWER_ON) return 0; DBG("resume + wakeup/SRP\n"); diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h index c9e6854..652ee46 100644 --- a/drivers/usb/gadget/omap_udc.h +++ b/drivers/usb/gadget/omap_udc.h @@ -20,6 +20,7 @@ #define UDC_CTRL_REG UDC_REG(0x0C) /* Endpoint control */ # define UDC_CLR_HALT (1 << 7) # define UDC_SET_HALT (1 << 6) +# define UDC_CLRDATA_TOGGLE (1 << 3) # define UDC_SET_FIFO_EN (1 << 2) # define UDC_CLR_EP (1 << 1) # define UDC_RESET_EP (1 << 0) @@ -99,6 +100,7 @@ /* DMA configuration registers: up to three channels in each direction. */ #define UDC_RXDMA_CFG_REG UDC_REG(0x40) /* 3 eps for RX DMA */ +# define UDC_DMA_REQ (1 << 12) #define UDC_TXDMA_CFG_REG UDC_REG(0x44) /* 3 eps for TX DMA */ #define UDC_DATA_DMA_REG UDC_REG(0x48) /* rx/tx fifo addr */ @@ -162,6 +164,7 @@ struct omap_udc { spinlock_t lock; struct omap_ep ep[32]; u16 devstat; + u16 clr_halt; struct otg_transceiver *transceiver; struct list_head iso; unsigned softconnect:1; @@ -171,7 +174,6 @@ struct omap_udc { unsigned ep0_set_config:1; unsigned ep0_reset_config:1; unsigned ep0_setup:1; - struct completion *done; }; diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c index b8b4524..6a0b957 100644 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c @@ -1,6 +1,6 @@ /* * linux/drivers/usb/gadget/pxa2xx_udc.c - * Intel PXA2xx and IXP4xx on-chip full speed USB device controllers + * Intel PXA25x and IXP4xx on-chip full speed USB device controllers * * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) * Copyright (C) 2003 Robert Schwebel, Pengutronix @@ -63,7 +63,7 @@ /* - * This driver handles the USB Device Controller (UDC) in Intel's PXA 2xx + * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x * series processors. The UDC for the IXP 4xx series is very similar. * There are fifteen endpoints, in addition to ep0. * @@ -79,8 +79,8 @@ * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. */ -#define DRIVER_VERSION "14-Dec-2003" -#define DRIVER_DESC "PXA 2xx USB Device Controller driver" +#define DRIVER_VERSION "4-May-2005" +#define DRIVER_DESC "PXA 25x USB Device Controller driver" static const char driver_name [] = "pxa2xx_udc"; @@ -290,6 +290,7 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep, static int pxa2xx_ep_disable (struct usb_ep *_ep) { struct pxa2xx_ep *ep; + unsigned long flags; ep = container_of (_ep, struct pxa2xx_ep, ep); if (!_ep || !ep->desc) { @@ -297,6 +298,8 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep) _ep ? ep->ep.name : NULL); return -EINVAL; } + local_irq_save(flags); + nuke (ep, -ESHUTDOWN); #ifdef USE_DMA @@ -313,6 +316,7 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep) ep->desc = NULL; ep->stopped = 1; + local_irq_restore(flags); DBG(DBG_VERBOSE, "%s disabled\n", _ep->name); return 0; } @@ -971,10 +975,10 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) kick_dma(ep, req); #endif /* can the FIFO can satisfy the request immediately? */ - } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0 - && (*ep->reg_udccs & UDCCS_BI_TFS) != 0 - && write_fifo(ep, req)) { - req = NULL; + } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { + if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0 + && write_fifo(ep, req)) + req = NULL; } else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0 && read_fifo(ep, req)) { req = NULL; @@ -1290,7 +1294,7 @@ udc_proc_read(char *page, char **start, off_t off, int count, "%s version: %s\nGadget driver: %s\nHost %s\n\n", driver_name, DRIVER_VERSION SIZE_STR DMASTR, dev->driver ? dev->driver->driver.name : "(none)", - is_usb_connected() ? "full speed" : "disconnected"); + is_vbus_present() ? "full speed" : "disconnected"); size -= t; next += t; @@ -1339,7 +1343,7 @@ udc_proc_read(char *page, char **start, off_t off, int count, next += t; } - if (!is_usb_connected() || !dev->driver) + if (!is_vbus_present() || !dev->driver) goto done; t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", @@ -1454,7 +1458,7 @@ static void udc_disable(struct pxa2xx_udc *dev) UFNRH = UFNRH_SIM; /* if hardware supports it, disconnect from usb */ - make_usb_disappear(); + pullup_off(); udc_clear_mask_UDCCR(UDCCR_UDE); @@ -1567,7 +1571,7 @@ static void udc_enable (struct pxa2xx_udc *dev) UICR0 &= ~UICR0_IM0; /* if hardware supports it, pullup D+ and wait for reset */ - let_usb_appear(); + pullup_on(); } @@ -2052,10 +2056,10 @@ pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r) if (unlikely(udccr & UDCCR_SUSIR)) { udc_ack_int_UDCCR(UDCCR_SUSIR); handled = 1; - DBG(DBG_VERBOSE, "USB suspend%s\n", is_usb_connected() + DBG(DBG_VERBOSE, "USB suspend%s\n", is_vbus_present() ? "" : "+disconnect"); - if (!is_usb_connected()) + if (!is_vbus_present()) stop_activity(dev, dev->driver); else if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver @@ -2073,7 +2077,7 @@ pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r) if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->resume - && is_usb_connected()) + && is_vbus_present()) dev->driver->resume(&dev->gadget); } @@ -2509,7 +2513,7 @@ static int __init pxa2xx_udc_probe(struct device *_dev) udc_disable(dev); udc_reinit(dev); - dev->vbus = is_usb_connected(); + dev->vbus = is_vbus_present(); /* irq setup after old hardware state is cleaned up */ retval = request_irq(IRQ_USB, pxa2xx_udc_irq, @@ -2555,6 +2559,12 @@ lubbock_fail0: return 0; } + +static void pxa2xx_udc_shutdown(struct device *_dev) +{ + pullup_off(); +} + static int __exit pxa2xx_udc_remove(struct device *_dev) { struct pxa2xx_udc *dev = dev_get_drvdata(_dev); @@ -2624,6 +2634,7 @@ static struct device_driver udc_driver = { .name = "pxa2xx-udc", .bus = &platform_bus_type, .probe = pxa2xx_udc_probe, + .shutdown = pxa2xx_udc_shutdown, .remove = __exit_p(pxa2xx_udc_remove), .suspend = pxa2xx_udc_suspend, .resume = pxa2xx_udc_resume, diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h index 1f3a7d9..d0bc396 100644 --- a/drivers/usb/gadget/pxa2xx_udc.h +++ b/drivers/usb/gadget/pxa2xx_udc.h @@ -177,23 +177,23 @@ struct pxa2xx_udc { static struct pxa2xx_udc *the_controller; -/* one GPIO should be used to detect host disconnect */ -static inline int is_usb_connected(void) +/* one GPIO should be used to detect VBUS from the host */ +static inline int is_vbus_present(void) { if (!the_controller->mach->udc_is_connected) return 1; return the_controller->mach->udc_is_connected(); } -/* one GPIO should force the host to see this device (or not) */ -static inline void make_usb_disappear(void) +/* one GPIO should control a D+ pullup, so host sees this device (or not) */ +static inline void pullup_off(void) { if (!the_controller->mach->udc_command) return; the_controller->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); } -static inline void let_usb_appear(void) +static inline void pullup_on(void) { if (!the_controller->mach->udc_command) return; diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 7457268..06b6eba 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -41,6 +41,7 @@ #undef RNDIS_PM +#undef RNDIS_WAKEUP #undef VERBOSE #include "rndis.h" @@ -60,7 +61,7 @@ } while (0) static int rndis_debug = 0; -module_param (rndis_debug, bool, 0); +module_param (rndis_debug, int, 0); MODULE_PARM_DESC (rndis_debug, "enable debugging"); #else @@ -78,22 +79,103 @@ static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS]; static const __le32 rndis_driver_version = __constant_cpu_to_le32 (1); /* Function Prototypes */ -static int rndis_init_response (int configNr, rndis_init_msg_type *buf); -static int rndis_query_response (int configNr, rndis_query_msg_type *buf); -static int rndis_set_response (int configNr, rndis_set_msg_type *buf); -static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf); -static int rndis_keepalive_response (int configNr, - rndis_keepalive_msg_type *buf); - static rndis_resp_t *rndis_add_response (int configNr, u32 length); +/* supported OIDs */ +static const u32 oid_supported_list [] = +{ + /* the general stuff */ + OID_GEN_SUPPORTED_LIST, + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_GEN_PHYSICAL_MEDIUM, +#if 0 + OID_GEN_RNDIS_CONFIG_PARAMETER, +#endif + + /* the statistical stuff */ + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_GEN_RCV_NO_BUFFER, +#ifdef RNDIS_OPTIONAL_STATS + OID_GEN_DIRECTED_BYTES_XMIT, + OID_GEN_DIRECTED_FRAMES_XMIT, + OID_GEN_MULTICAST_BYTES_XMIT, + OID_GEN_MULTICAST_FRAMES_XMIT, + OID_GEN_BROADCAST_BYTES_XMIT, + OID_GEN_BROADCAST_FRAMES_XMIT, + OID_GEN_DIRECTED_BYTES_RCV, + OID_GEN_DIRECTED_FRAMES_RCV, + OID_GEN_MULTICAST_BYTES_RCV, + OID_GEN_MULTICAST_FRAMES_RCV, + OID_GEN_BROADCAST_BYTES_RCV, + OID_GEN_BROADCAST_FRAMES_RCV, + OID_GEN_RCV_CRC_ERROR, + OID_GEN_TRANSMIT_QUEUE_LENGTH, +#endif /* RNDIS_OPTIONAL_STATS */ + + /* mandatory 802.3 */ + /* the general stuff */ + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAC_OPTIONS, + OID_802_3_MAXIMUM_LIST_SIZE, + + /* the statistical stuff */ + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS, +#ifdef RNDIS_OPTIONAL_STATS + OID_802_3_XMIT_DEFERRED, + OID_802_3_XMIT_MAX_COLLISIONS, + OID_802_3_RCV_OVERRUN, + OID_802_3_XMIT_UNDERRUN, + OID_802_3_XMIT_HEARTBEAT_FAILURE, + OID_802_3_XMIT_TIMES_CRS_LOST, + OID_802_3_XMIT_LATE_COLLISIONS, +#endif /* RNDIS_OPTIONAL_STATS */ + +#ifdef RNDIS_PM + /* PM and wakeup are mandatory for USB: */ + + /* power management */ + OID_PNP_CAPABILITIES, + OID_PNP_QUERY_POWER, + OID_PNP_SET_POWER, + +#ifdef RNDIS_WAKEUP + /* wake up host */ + OID_PNP_ENABLE_WAKE_UP, + OID_PNP_ADD_WAKE_UP_PATTERN, + OID_PNP_REMOVE_WAKE_UP_PATTERN, +#endif /* RNDIS_WAKEUP */ +#endif /* RNDIS_PM */ +}; + + /* NDIS Functions */ -static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) +static int +gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, + rndis_resp_t *r) { int retval = -ENOTSUPP; - u32 length = 0; - __le32 *tmp; + u32 length = 4; /* usually */ + __le32 *outbuf; int i, count; rndis_query_cmplt_type *resp; @@ -101,7 +183,22 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) resp = (rndis_query_cmplt_type *) r->buf; if (!resp) return -ENOMEM; - + + if (buf_len && rndis_debug > 1) { + DEBUG("query OID %08x value, len %d:\n", OID, buf_len); + for (i = 0; i < buf_len; i += 16) { + DEBUG ("%03d: %08x %08x %08x %08x\n", i, + le32_to_cpup((__le32 *)&buf[i]), + le32_to_cpup((__le32 *)&buf[i + 4]), + le32_to_cpup((__le32 *)&buf[i + 8]), + le32_to_cpup((__le32 *)&buf[i + 12])); + } + } + + /* response goes here, right after the header */ + outbuf = (__le32 *) &resp[1]; + resp->InformationBufferOffset = __constant_cpu_to_le32 (16); + switch (OID) { /* general oids (table 4-1) */ @@ -111,42 +208,36 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) DEBUG ("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__); length = sizeof (oid_supported_list); count = length / sizeof (u32); - tmp = (__le32 *) ((u8 *)resp + 24); for (i = 0; i < count; i++) - tmp[i] = cpu_to_le32 (oid_supported_list[i]); + outbuf[i] = cpu_to_le32 (oid_supported_list[i]); retval = 0; break; /* mandatory */ case OID_GEN_HARDWARE_STATUS: DEBUG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__); - length = 4; /* Bogus question! * Hardware must be ready to receive high level protocols. * BTW: * reddite ergo quae sunt Caesaris Caesari * et quae sunt Dei Deo! */ - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; /* mandatory */ case OID_GEN_MEDIA_SUPPORTED: DEBUG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__); - length = 4; - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr].medium); + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium); retval = 0; break; /* mandatory */ case OID_GEN_MEDIA_IN_USE: DEBUG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__); - length = 4; /* one medium, one transport... (maybe you do it better) */ - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr].medium); + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium); retval = 0; break; @@ -154,25 +245,21 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) case OID_GEN_MAXIMUM_FRAME_SIZE: DEBUG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__); if (rndis_per_dev_params [configNr].dev) { - length = 4; - *((__le32 *) resp + 6) = cpu_to_le32 ( + *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].dev->mtu); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; /* mandatory */ case OID_GEN_LINK_SPEED: -// DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__); - length = 4; + if (rndis_debug > 1) + DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__); if (rndis_per_dev_params [configNr].media_state - == NDIS_MEDIA_STATE_DISCONNECTED) - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + == NDIS_MEDIA_STATE_DISCONNECTED) + *outbuf = __constant_cpu_to_le32 (0); else - *((__le32 *) resp + 6) = cpu_to_le32 ( + *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].speed); retval = 0; break; @@ -181,8 +268,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) case OID_GEN_TRANSMIT_BLOCK_SIZE: DEBUG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__); if (rndis_per_dev_params [configNr].dev) { - length = 4; - *((__le32 *) resp + 6) = cpu_to_le32 ( + *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].dev->mtu); retval = 0; } @@ -192,8 +278,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) case OID_GEN_RECEIVE_BLOCK_SIZE: DEBUG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__); if (rndis_per_dev_params [configNr].dev) { - length = 4; - *((__le32 *) resp + 6) = cpu_to_le32 ( + *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].dev->mtu); retval = 0; } @@ -202,8 +287,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) /* mandatory */ case OID_GEN_VENDOR_ID: DEBUG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__); - length = 4; - *((__le32 *) resp + 6) = cpu_to_le32 ( + *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].vendorID); retval = 0; break; @@ -212,51 +296,44 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) case OID_GEN_VENDOR_DESCRIPTION: DEBUG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__); length = strlen (rndis_per_dev_params [configNr].vendorDescr); - memcpy ((u8 *) resp + 24, + memcpy (outbuf, rndis_per_dev_params [configNr].vendorDescr, length); retval = 0; break; case OID_GEN_VENDOR_DRIVER_VERSION: DEBUG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__); - length = 4; /* Created as LE */ - *((__le32 *) resp + 6) = rndis_driver_version; + *outbuf = rndis_driver_version; retval = 0; break; /* mandatory */ case OID_GEN_CURRENT_PACKET_FILTER: DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__); - length = 4; - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params[configNr].filter); + *outbuf = cpu_to_le32 (*rndis_per_dev_params[configNr].filter); retval = 0; break; /* mandatory */ case OID_GEN_MAXIMUM_TOTAL_SIZE: DEBUG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__); - length = 4; - *((__le32 *) resp + 6) = __constant_cpu_to_le32( - RNDIS_MAX_TOTAL_SIZE); + *outbuf = __constant_cpu_to_le32(RNDIS_MAX_TOTAL_SIZE); retval = 0; break; /* mandatory */ case OID_GEN_MEDIA_CONNECT_STATUS: - DEBUG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__); - length = 4; - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr] + if (rndis_debug > 1) + DEBUG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__); + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .media_state); retval = 0; break; case OID_GEN_PHYSICAL_MEDIUM: DEBUG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__); - length = 4; - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; @@ -266,8 +343,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) */ case OID_GEN_MAC_OPTIONS: /* from WinME */ DEBUG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__); - length = 4; - *((__le32 *) resp + 6) = __constant_cpu_to_le32( + *outbuf = __constant_cpu_to_le32( NDIS_MAC_OPTION_RECEIVE_SERIALIZED | NDIS_MAC_OPTION_FULL_DUPLEX); retval = 0; @@ -277,62 +353,49 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) /* mandatory */ case OID_GEN_XMIT_OK: - DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__); + if (rndis_debug > 1) + DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { - length = 4; - *((__le32 *) resp + 6) = cpu_to_le32 ( + *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].stats->tx_packets - rndis_per_dev_params [configNr].stats->tx_errors - rndis_per_dev_params [configNr].stats->tx_dropped); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; /* mandatory */ case OID_GEN_RCV_OK: - DEBUG("%s: OID_GEN_RCV_OK\n", __FUNCTION__); + if (rndis_debug > 1) + DEBUG("%s: OID_GEN_RCV_OK\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { - length = 4; - *((__le32 *) resp + 6) = cpu_to_le32 ( + *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].stats->rx_packets - rndis_per_dev_params [configNr].stats->rx_errors - rndis_per_dev_params [configNr].stats->rx_dropped); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; /* mandatory */ case OID_GEN_XMIT_ERROR: - DEBUG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__); + if (rndis_debug > 1) + DEBUG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { - length = 4; - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr] + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->tx_errors); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; /* mandatory */ case OID_GEN_RCV_ERROR: - DEBUG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__); + if (rndis_debug > 1) + DEBUG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr] + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_errors); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; @@ -340,13 +403,9 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) case OID_GEN_RCV_NO_BUFFER: DEBUG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr] + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_dropped); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; @@ -359,8 +418,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) * divided by weight of Alpha Centauri */ if (rndis_per_dev_params [configNr].stats) { - length = 4; - *((__le32 *) resp + 6) = cpu_to_le32 ( + *outbuf = cpu_to_le32 ( (rndis_per_dev_params [configNr] .stats->tx_packets - rndis_per_dev_params [configNr] @@ -369,9 +427,6 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) .stats->tx_dropped) * 123); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; @@ -379,8 +434,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) DEBUG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__); /* dito */ if (rndis_per_dev_params [configNr].stats) { - length = 4; - *((__le32 *) resp + 6) = cpu_to_le32 ( + *outbuf = cpu_to_le32 ( (rndis_per_dev_params [configNr] .stats->tx_packets - rndis_per_dev_params [configNr] @@ -389,144 +443,105 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) .stats->tx_dropped) / 123); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; case OID_GEN_MULTICAST_BYTES_XMIT: DEBUG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr] + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->multicast*1234); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; case OID_GEN_MULTICAST_FRAMES_XMIT: DEBUG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr] + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->multicast); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; case OID_GEN_BROADCAST_BYTES_XMIT: DEBUG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr] + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->tx_packets/42*255); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; case OID_GEN_BROADCAST_FRAMES_XMIT: DEBUG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr] + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->tx_packets/42); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; case OID_GEN_DIRECTED_BYTES_RCV: DEBUG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__); - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; case OID_GEN_DIRECTED_FRAMES_RCV: DEBUG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__); - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; case OID_GEN_MULTICAST_BYTES_RCV: DEBUG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr] + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->multicast * 1111); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; case OID_GEN_MULTICAST_FRAMES_RCV: DEBUG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr] + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->multicast); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; case OID_GEN_BROADCAST_BYTES_RCV: DEBUG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr] + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_packets/42*255); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; case OID_GEN_BROADCAST_FRAMES_RCV: DEBUG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr] + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_packets/42); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; case OID_GEN_RCV_CRC_ERROR: DEBUG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr] + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_crc_errors); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; case OID_GEN_TRANSMIT_QUEUE_LENGTH: DEBUG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__); - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; #endif /* RNDIS_OPTIONAL_STATS */ @@ -538,13 +553,10 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) DEBUG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__); if (rndis_per_dev_params [configNr].dev) { length = ETH_ALEN; - memcpy ((u8 *) resp + 24, + memcpy (outbuf, rndis_per_dev_params [configNr].host_mac, length); retval = 0; - } else { - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); - retval = 0; } break; @@ -553,7 +565,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) DEBUG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__); if (rndis_per_dev_params [configNr].dev) { length = ETH_ALEN; - memcpy ((u8 *) resp + 24, + memcpy (outbuf, rndis_per_dev_params [configNr].host_mac, length); retval = 0; @@ -563,18 +575,16 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) /* mandatory */ case OID_802_3_MULTICAST_LIST: DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); - length = 4; /* Multicast base address only */ - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0xE0000000); + *outbuf = __constant_cpu_to_le32 (0xE0000000); retval = 0; break; /* mandatory */ case OID_802_3_MAXIMUM_LIST_SIZE: DEBUG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__); - length = 4; /* Multicast base address only */ - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (1); + *outbuf = __constant_cpu_to_le32 (1); retval = 0; break; @@ -587,11 +597,8 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) /* mandatory */ case OID_802_3_RCV_ERROR_ALIGNMENT: DEBUG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__); - if (rndis_per_dev_params [configNr].stats) - { - length = 4; - *((__le32 *) resp + 6) = cpu_to_le32 ( - rndis_per_dev_params [configNr] + if (rndis_per_dev_params [configNr].stats) { + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_frame_errors); retval = 0; } @@ -600,16 +607,14 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) /* mandatory */ case OID_802_3_XMIT_ONE_COLLISION: DEBUG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__); - length = 4; - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; /* mandatory */ case OID_802_3_XMIT_MORE_COLLISIONS: DEBUG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__); - length = 4; - *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0); + *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; @@ -655,27 +660,18 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) case OID_PNP_CAPABILITIES: DEBUG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__); - /* just PM, and remote wakeup on link status change - * (not magic packet or pattern match) - */ + /* for now, no wakeup capabilities */ length = sizeof (struct NDIS_PNP_CAPABILITIES); - memset (resp, 0, length); - { - struct NDIS_PNP_CAPABILITIES *caps = (void *) resp; - - caps->Flags = NDIS_DEVICE_WAKE_UP_ENABLE; - caps->WakeUpCapabilities.MinLinkChangeWakeUp - = NdisDeviceStateD3; - - /* FIXME then use usb_gadget_wakeup(), and - * set USB_CONFIG_ATT_WAKEUP in config desc - */ - } + memset(outbuf, 0, length); retval = 0; break; case OID_PNP_QUERY_POWER: - DEBUG("%s: OID_PNP_QUERY_POWER\n", __FUNCTION__); - /* sure, handle any power state that maps to USB suspend */ + DEBUG("%s: OID_PNP_QUERY_POWER D%d\n", __FUNCTION__, + le32_to_cpup((__le32 *) buf) - 1); + /* only suspend is a real power state, and + * it can't be entered by OID_PNP_SET_POWER... + */ + length = 0; retval = 0; break; #endif @@ -684,11 +680,12 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) printk (KERN_WARNING "%s: query unknown OID 0x%08X\n", __FUNCTION__, OID); } + if (retval < 0) + length = 0; - resp->InformationBufferOffset = __constant_cpu_to_le32 (16); resp->InformationBufferLength = cpu_to_le32 (length); - resp->MessageLength = cpu_to_le32 (24 + length); - r->length = 24 + length; + r->length = length + sizeof *resp; + resp->MessageLength = cpu_to_le32 (r->length); return retval; } @@ -705,45 +702,40 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, if (!resp) return -ENOMEM; - DEBUG("set OID %08x value, len %d:\n", OID, buf_len); - for (i = 0; i < buf_len; i += 16) { - DEBUG ("%03d: " - " %02x %02x %02x %02x" - " %02x %02x %02x %02x" - " %02x %02x %02x %02x" - " %02x %02x %02x %02x" - "\n", - i, - buf[i], buf [i+1], - buf[i+2], buf[i+3], - buf[i+4], buf [i+5], - buf[i+6], buf[i+7], - buf[i+8], buf [i+9], - buf[i+10], buf[i+11], - buf[i+12], buf [i+13], - buf[i+14], buf[i+15]); + if (buf_len && rndis_debug > 1) { + DEBUG("set OID %08x value, len %d:\n", OID, buf_len); + for (i = 0; i < buf_len; i += 16) { + DEBUG ("%03d: %08x %08x %08x %08x\n", i, + le32_to_cpup((__le32 *)&buf[i]), + le32_to_cpup((__le32 *)&buf[i + 4]), + le32_to_cpup((__le32 *)&buf[i + 8]), + le32_to_cpup((__le32 *)&buf[i + 12])); + } } + params = &rndis_per_dev_params [configNr]; switch (OID) { case OID_GEN_CURRENT_PACKET_FILTER: - params = &rndis_per_dev_params [configNr]; - retval = 0; - /* FIXME use these NDIS_PACKET_TYPE_* bitflags to - * set the cdc_filter; it's not RNDIS-specific + /* these NDIS_PACKET_TYPE_* bitflags are shared with + * cdc_filter; it's not RNDIS-specific * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in: * PROMISCUOUS, DIRECTED, * MULTICAST, ALL_MULTICAST, BROADCAST */ - params->filter = le32_to_cpup((__le32 *)buf); + *params->filter = (u16) le32_to_cpup((__le32 *)buf); DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n", - __FUNCTION__, params->filter); + __FUNCTION__, *params->filter); /* this call has a significant side effect: it's * what makes the packet flow start and stop, like * activating the CDC Ethernet altsetting. */ - if (params->filter) { +#ifdef RNDIS_PM +update_linkstate: +#endif + retval = 0; + if (*params->filter) { params->state = RNDIS_DATA_INITIALIZED; netif_carrier_on(params->dev); if (netif_running(params->dev)) @@ -776,21 +768,34 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, #ifdef RNDIS_PM case OID_PNP_SET_POWER: - DEBUG ("OID_PNP_SET_POWER\n"); - /* sure, handle any power state that maps to USB suspend */ - retval = 0; - break; - - case OID_PNP_ENABLE_WAKE_UP: - /* always-connected ... */ - DEBUG ("OID_PNP_ENABLE_WAKE_UP\n"); - retval = 0; + /* The only real power state is USB suspend, and RNDIS requests + * can't enter it; this one isn't really about power. After + * resuming, Windows forces a reset, and then SET_POWER D0. + * FIXME ... then things go batty; Windows wedges itself. + */ + i = le32_to_cpup((__force __le32 *)buf); + DEBUG("%s: OID_PNP_SET_POWER D%d\n", __FUNCTION__, i - 1); + switch (i) { + case NdisDeviceStateD0: + *params->filter = params->saved_filter; + goto update_linkstate; + case NdisDeviceStateD3: + case NdisDeviceStateD2: + case NdisDeviceStateD1: + params->saved_filter = *params->filter; + retval = 0; + break; + } break; - // no PM resume patterns supported (specified where?) - // so OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN always fails +#ifdef RNDIS_WAKEUP + // no wakeup support advertised, so wakeup OIDs always fail: + // - OID_PNP_ENABLE_WAKE_UP + // - OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN #endif +#endif /* RNDIS_PM */ + default: printk (KERN_WARNING "%s: set unknown OID 0x%08X, size %d\n", __FUNCTION__, OID, buf_len); @@ -811,13 +816,10 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf) if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type)); - - if (!r) return -ENOMEM; - + if (!r) + return -ENOMEM; resp = (rndis_init_cmplt_type *) r->buf; - if (!resp) return -ENOMEM; - resp->MessageType = __constant_cpu_to_le32 ( REMOTE_NDIS_INITIALIZE_CMPLT); resp->MessageLength = __constant_cpu_to_le32 (52); @@ -857,20 +859,22 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf) * oid_supported_list is the largest answer */ r = rndis_add_response (configNr, sizeof (oid_supported_list)); - - if (!r) return -ENOMEM; + if (!r) + return -ENOMEM; resp = (rndis_query_cmplt_type *) r->buf; - if (!resp) return -ENOMEM; - resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT); - resp->MessageLength = __constant_cpu_to_le32 (24); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - - if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID), r)) { + + if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID), + le32_to_cpu(buf->InformationBufferOffset) + + 8 + (u8 *) buf, + le32_to_cpu(buf->InformationBufferLength), + r)) { /* OID not supported */ resp->Status = __constant_cpu_to_le32 ( RNDIS_STATUS_NOT_SUPPORTED); + resp->MessageLength = __constant_cpu_to_le32 (sizeof *resp); resp->InformationBufferLength = __constant_cpu_to_le32 (0); resp->InformationBufferOffset = __constant_cpu_to_le32 (0); } else @@ -889,10 +893,9 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) rndis_resp_t *r; r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type)); - - if (!r) return -ENOMEM; + if (!r) + return -ENOMEM; resp = (rndis_set_cmplt_type *) r->buf; - if (!resp) return -ENOMEM; BufLength = le32_to_cpu (buf->InformationBufferLength); BufOffset = le32_to_cpu (buf->InformationBufferOffset); @@ -930,10 +933,9 @@ static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf) rndis_resp_t *r; r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type)); - - if (!r) return -ENOMEM; + if (!r) + return -ENOMEM; resp = (rndis_reset_cmplt_type *) r->buf; - if (!resp) return -ENOMEM; resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_RESET_CMPLT); resp->MessageLength = __constant_cpu_to_le32 (16); @@ -957,8 +959,9 @@ static int rndis_keepalive_response (int configNr, /* host "should" check only in RNDIS_DATA_INITIALIZED state */ r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type)); + if (!r) + return -ENOMEM; resp = (rndis_keepalive_cmplt_type *) r->buf; - if (!resp) return -ENOMEM; resp->MessageType = __constant_cpu_to_le32 ( REMOTE_NDIS_KEEPALIVE_CMPLT); @@ -987,10 +990,9 @@ static int rndis_indicate_status_msg (int configNr, u32 status) r = rndis_add_response (configNr, sizeof (rndis_indicate_status_msg_type)); - if (!r) return -ENOMEM; - + if (!r) + return -ENOMEM; resp = (rndis_indicate_status_msg_type *) r->buf; - if (!resp) return -ENOMEM; resp->MessageType = __constant_cpu_to_le32 ( REMOTE_NDIS_INDICATE_STATUS_MSG); @@ -1021,6 +1023,21 @@ int rndis_signal_disconnect (int configNr) RNDIS_STATUS_MEDIA_DISCONNECT); } +void rndis_uninit (int configNr) +{ + u8 *buf; + u32 length; + + if (configNr >= RNDIS_MAX_CONFIGS) + return; + rndis_per_dev_params [configNr].used = 0; + rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED; + + /* drain the response queue */ + while ((buf = rndis_get_next_response(configNr, &length))) + rndis_free_response(configNr, buf); +} + void rndis_set_host_mac (int configNr, const u8 *addr) { rndis_per_dev_params [configNr].host_mac = addr; @@ -1046,9 +1063,13 @@ int rndis_msg_parser (u8 configNr, u8 *buf) return -ENOTSUPP; params = &rndis_per_dev_params [configNr]; + /* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for + * rx/tx statistics and link status, in addition to KEEPALIVE traffic + * and normal HC level polling to see if there's any IN traffic. + */ + /* For USB: responses may take up to 10 seconds */ - switch (MsgType) - { + switch (MsgType) { case REMOTE_NDIS_INITIALIZE_MSG: DEBUG("%s: REMOTE_NDIS_INITIALIZE_MSG\n", __FUNCTION__ ); @@ -1082,10 +1103,9 @@ int rndis_msg_parser (u8 configNr, u8 *buf) case REMOTE_NDIS_KEEPALIVE_MSG: /* For USB: host does this every 5 seconds */ -#ifdef VERBOSE - DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", - __FUNCTION__ ); -#endif + if (rndis_debug > 1) + DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", + __FUNCTION__ ); return rndis_keepalive_response (configNr, (rndis_keepalive_msg_type *) buf); @@ -1152,7 +1172,8 @@ void rndis_deregister (int configNr) } int rndis_set_param_dev (u8 configNr, struct net_device *dev, - struct net_device_stats *stats) + struct net_device_stats *stats, + u16 *cdc_filter) { DEBUG("%s:\n", __FUNCTION__ ); if (!dev || !stats) return -1; @@ -1160,6 +1181,7 @@ int rndis_set_param_dev (u8 configNr, struct net_device *dev, rndis_per_dev_params [configNr].dev = dev; rndis_per_dev_params [configNr].stats = stats; + rndis_per_dev_params [configNr].filter = cdc_filter; return 0; } @@ -1178,7 +1200,7 @@ int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr) int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed) { - DEBUG("%s:\n", __FUNCTION__ ); + DEBUG("%s: %u %u\n", __FUNCTION__, medium, speed); if (configNr >= RNDIS_MAX_CONFIGS) return -1; rndis_per_dev_params [configNr].medium = medium; @@ -1242,6 +1264,7 @@ static rndis_resp_t *rndis_add_response (int configNr, u32 length) { rndis_resp_t *r; + /* NOTE: this gets copied into ether.c USB_BUFSIZ bytes ... */ r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC); if (!r) return NULL; diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index 2b5b55d..95b4c63 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/rndis.h @@ -69,90 +69,6 @@ #define OID_PNP_ENABLE_WAKE_UP 0xFD010106 -/* supported OIDs */ -static const u32 oid_supported_list [] = -{ - /* the general stuff */ - OID_GEN_SUPPORTED_LIST, - OID_GEN_HARDWARE_STATUS, - OID_GEN_MEDIA_SUPPORTED, - OID_GEN_MEDIA_IN_USE, - OID_GEN_MAXIMUM_FRAME_SIZE, - OID_GEN_LINK_SPEED, - OID_GEN_TRANSMIT_BLOCK_SIZE, - OID_GEN_RECEIVE_BLOCK_SIZE, - OID_GEN_VENDOR_ID, - OID_GEN_VENDOR_DESCRIPTION, - OID_GEN_VENDOR_DRIVER_VERSION, - OID_GEN_CURRENT_PACKET_FILTER, - OID_GEN_MAXIMUM_TOTAL_SIZE, - OID_GEN_MEDIA_CONNECT_STATUS, - OID_GEN_PHYSICAL_MEDIUM, -#if 0 - OID_GEN_RNDIS_CONFIG_PARAMETER, -#endif - - /* the statistical stuff */ - OID_GEN_XMIT_OK, - OID_GEN_RCV_OK, - OID_GEN_XMIT_ERROR, - OID_GEN_RCV_ERROR, - OID_GEN_RCV_NO_BUFFER, -#ifdef RNDIS_OPTIONAL_STATS - OID_GEN_DIRECTED_BYTES_XMIT, - OID_GEN_DIRECTED_FRAMES_XMIT, - OID_GEN_MULTICAST_BYTES_XMIT, - OID_GEN_MULTICAST_FRAMES_XMIT, - OID_GEN_BROADCAST_BYTES_XMIT, - OID_GEN_BROADCAST_FRAMES_XMIT, - OID_GEN_DIRECTED_BYTES_RCV, - OID_GEN_DIRECTED_FRAMES_RCV, - OID_GEN_MULTICAST_BYTES_RCV, - OID_GEN_MULTICAST_FRAMES_RCV, - OID_GEN_BROADCAST_BYTES_RCV, - OID_GEN_BROADCAST_FRAMES_RCV, - OID_GEN_RCV_CRC_ERROR, - OID_GEN_TRANSMIT_QUEUE_LENGTH, -#endif /* RNDIS_OPTIONAL_STATS */ - - /* mandatory 802.3 */ - /* the general stuff */ - OID_802_3_PERMANENT_ADDRESS, - OID_802_3_CURRENT_ADDRESS, - OID_802_3_MULTICAST_LIST, - OID_802_3_MAC_OPTIONS, - OID_802_3_MAXIMUM_LIST_SIZE, - - /* the statistical stuff */ - OID_802_3_RCV_ERROR_ALIGNMENT, - OID_802_3_XMIT_ONE_COLLISION, - OID_802_3_XMIT_MORE_COLLISIONS, -#ifdef RNDIS_OPTIONAL_STATS - OID_802_3_XMIT_DEFERRED, - OID_802_3_XMIT_MAX_COLLISIONS, - OID_802_3_RCV_OVERRUN, - OID_802_3_XMIT_UNDERRUN, - OID_802_3_XMIT_HEARTBEAT_FAILURE, - OID_802_3_XMIT_TIMES_CRS_LOST, - OID_802_3_XMIT_LATE_COLLISIONS, -#endif /* RNDIS_OPTIONAL_STATS */ - -#ifdef RNDIS_PM - /* PM and wakeup are mandatory for USB: */ - - /* power management */ - OID_PNP_CAPABILITIES, - OID_PNP_QUERY_POWER, - OID_PNP_SET_POWER, - - /* wake up host */ - OID_PNP_ENABLE_WAKE_UP, - OID_PNP_ADD_WAKE_UP_PATTERN, - OID_PNP_REMOVE_WAKE_UP_PATTERN, -#endif -}; - - typedef struct rndis_init_msg_type { __le32 MessageType; @@ -309,15 +225,18 @@ typedef struct rndis_resp_t typedef struct rndis_params { u8 confignr; - int used; + u8 used; + u16 saved_filter; enum rndis_state state; - u32 filter; u32 medium; u32 speed; u32 media_state; + const u8 *host_mac; + u16 *filter; struct net_device *dev; struct net_device_stats *stats; + u32 vendorID; const char *vendorDescr; int (*ack) (struct net_device *); @@ -329,7 +248,8 @@ int rndis_msg_parser (u8 configNr, u8 *buf); int rndis_register (int (*rndis_control_ack) (struct net_device *)); void rndis_deregister (int configNr); int rndis_set_param_dev (u8 configNr, struct net_device *dev, - struct net_device_stats *stats); + struct net_device_stats *stats, + u16 *cdc_filter); int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr); int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed); @@ -338,6 +258,7 @@ int rndis_rm_hdr (struct sk_buff *skb); u8 *rndis_get_next_response (int configNr, u32 *length); void rndis_free_response (int configNr, u8 *buf); +void rndis_uninit (int configNr); int rndis_signal_connect (int configNr); int rndis_signal_disconnect (int configNr); int rndis_state (int configNr); diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 4d591c7..9e4f1c6 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -300,18 +300,18 @@ static int gs_build_config_buf(u8 *buf, enum usb_device_speed speed, u8 type, unsigned int index, int is_otg); static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len, - int kmalloc_flags); + unsigned kmalloc_flags); static void gs_free_req(struct usb_ep *ep, struct usb_request *req); static struct gs_req_entry *gs_alloc_req_entry(struct usb_ep *ep, unsigned len, - int kmalloc_flags); + unsigned kmalloc_flags); static void gs_free_req_entry(struct usb_ep *ep, struct gs_req_entry *req); -static int gs_alloc_ports(struct gs_dev *dev, int kmalloc_flags); +static int gs_alloc_ports(struct gs_dev *dev, unsigned kmalloc_flags); static void gs_free_ports(struct gs_dev *dev); /* circular buffer */ -static struct gs_buf *gs_buf_alloc(unsigned int size, int kmalloc_flags); +static struct gs_buf *gs_buf_alloc(unsigned int size, unsigned kmalloc_flags); static void gs_buf_free(struct gs_buf *gb); static void gs_buf_clear(struct gs_buf *gb); static unsigned int gs_buf_data_avail(struct gs_buf *gb); @@ -1607,9 +1607,9 @@ static int gs_setup(struct usb_gadget *gadget, int ret = -EOPNOTSUPP; struct gs_dev *dev = get_gadget_data(gadget); struct usb_request *req = dev->dev_ctrl_req; - u16 wIndex = ctrl->wIndex; - u16 wValue = ctrl->wValue; - u16 wLength = ctrl->wLength; + u16 wIndex = le16_to_cpu(ctrl->wIndex); + u16 wValue = le16_to_cpu(ctrl->wValue); + u16 wLength = le16_to_cpu(ctrl->wLength); switch (ctrl->bRequestType & USB_TYPE_MASK) { case USB_TYPE_STANDARD: @@ -1651,9 +1651,9 @@ static int gs_setup_standard(struct usb_gadget *gadget, int ret = -EOPNOTSUPP; struct gs_dev *dev = get_gadget_data(gadget); struct usb_request *req = dev->dev_ctrl_req; - u16 wIndex = ctrl->wIndex; - u16 wValue = ctrl->wValue; - u16 wLength = ctrl->wLength; + u16 wIndex = le16_to_cpu(ctrl->wIndex); + u16 wValue = le16_to_cpu(ctrl->wValue); + u16 wLength = le16_to_cpu(ctrl->wLength); switch (ctrl->bRequest) { case USB_REQ_GET_DESCRIPTOR: @@ -1782,9 +1782,9 @@ static int gs_setup_class(struct usb_gadget *gadget, struct gs_dev *dev = get_gadget_data(gadget); struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */ struct usb_request *req = dev->dev_ctrl_req; - u16 wIndex = ctrl->wIndex; - u16 wValue = ctrl->wValue; - u16 wLength = ctrl->wLength; + u16 wIndex = le16_to_cpu(ctrl->wIndex); + u16 wValue = le16_to_cpu(ctrl->wValue); + u16 wLength = le16_to_cpu(ctrl->wLength); switch (ctrl->bRequest) { case USB_CDC_REQ_SET_LINE_CODING: @@ -2119,7 +2119,8 @@ static int gs_build_config_buf(u8 *buf, enum usb_device_speed speed, * Allocate a usb_request and its buffer. Returns a pointer to the * usb_request or NULL if there is an error. */ -static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len, int kmalloc_flags) +static struct usb_request * +gs_alloc_req(struct usb_ep *ep, unsigned int len, unsigned kmalloc_flags) { struct usb_request *req; @@ -2159,7 +2160,8 @@ static void gs_free_req(struct usb_ep *ep, struct usb_request *req) * Allocates a request and its buffer, using the given * endpoint, buffer len, and kmalloc flags. */ -static struct gs_req_entry *gs_alloc_req_entry(struct usb_ep *ep, unsigned len, int kmalloc_flags) +static struct gs_req_entry * +gs_alloc_req_entry(struct usb_ep *ep, unsigned len, unsigned kmalloc_flags) { struct gs_req_entry *req; @@ -2200,7 +2202,7 @@ static void gs_free_req_entry(struct usb_ep *ep, struct gs_req_entry *req) * * The device lock is normally held when calling this function. */ -static int gs_alloc_ports(struct gs_dev *dev, int kmalloc_flags) +static int gs_alloc_ports(struct gs_dev *dev, unsigned kmalloc_flags) { int i; struct gs_port *port; @@ -2282,7 +2284,7 @@ static void gs_free_ports(struct gs_dev *dev) * * Allocate a circular buffer and all associated memory. */ -static struct gs_buf *gs_buf_alloc(unsigned int size, int kmalloc_flags) +static struct gs_buf *gs_buf_alloc(unsigned int size, unsigned kmalloc_flags) { struct gs_buf *gb; diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 6e49432..a6e035e 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -919,9 +919,9 @@ 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; - u16 w_index = ctrl->wIndex; - u16 w_value = ctrl->wValue; - u16 w_length = ctrl->wLength; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); /* usually this stores reply data in the pre-allocated ep0 buffer, * but config change events will reconfigure hardware. diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 19e598c..ed1899d 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -49,6 +49,19 @@ config USB_EHCI_ROOT_HUB_TT This supports the EHCI implementation from TransDimension Inc. +config USB_ISP116X_HCD + tristate "ISP116X HCD support" + depends on USB + default N + ---help--- + The ISP1160 and ISP1161 chips are USB host controllers. Enable this + option if your board has this chip. If unsure, say N. + + This driver does not support isochronous transfers. + + To compile this driver as a module, choose M here: the + module will be called isp116x-hcd. + config USB_OHCI_HCD tristate "OHCI HCD support" depends on USB && USB_ARCH_HAS_OHCI diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 5dbd3e7..350d14f 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o +obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 2ff11d5..50cb018 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -254,7 +254,7 @@ dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status) } return scnprintf (buf, len, - "%s%sport %d status %06x%s%s sig=%s %s%s%s%s%s%s%s%s%s", + "%s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s", label, label [0] ? " " : "", port, status, (status & PORT_POWER) ? " POWER" : "", (status & PORT_OWNER) ? " OWNER" : "", @@ -644,9 +644,11 @@ show_registers (struct class_device *class_dev, char *buf) if (bus->controller->power.power_state) { size = scnprintf (next, size, "bus %s, device %s (driver " DRIVER_VERSION ")\n" + "%s\n" "SUSPENDED (no register access)\n", hcd->self.controller->bus->name, - hcd->self.controller->bus_id); + hcd->self.controller->bus_id, + hcd->product_desc); goto done; } @@ -654,13 +656,53 @@ show_registers (struct class_device *class_dev, char *buf) i = HC_VERSION(readl (&ehci->caps->hc_capbase)); temp = scnprintf (next, size, "bus %s, device %s (driver " DRIVER_VERSION ")\n" + "%s\n" "EHCI %x.%02x, hcd state %d\n", hcd->self.controller->bus->name, hcd->self.controller->bus_id, + hcd->product_desc, i >> 8, i & 0x0ff, hcd->state); size -= temp; next += temp; +#ifdef CONFIG_PCI + /* EHCI 0.96 and later may have "extended capabilities" */ + if (hcd->self.controller->bus == &pci_bus_type) { + struct pci_dev *pdev; + u32 offset, cap, cap2; + unsigned count = 256/4; + + pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller); + offset = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params)); + while (offset && count--) { + pci_read_config_dword (pdev, offset, &cap); + switch (cap & 0xff) { + case 1: + temp = scnprintf (next, size, + "ownership %08x%s%s\n", cap, + (cap & (1 << 24)) ? " linux" : "", + (cap & (1 << 16)) ? " firmware" : ""); + size -= temp; + next += temp; + + offset += 4; + pci_read_config_dword (pdev, offset, &cap2); + temp = scnprintf (next, size, + "SMI sts/enable 0x%08x\n", cap2); + size -= temp; + next += temp; + break; + case 0: /* illegal reserved capability */ + cap = 0; + /* FALLTHROUGH */ + default: /* unknown */ + break; + } + temp = (cap >> 8) & 0xff; + } + } +#endif + // FIXME interpret both types of params i = readl (&ehci->caps->hcs_params); temp = scnprintf (next, size, "structural params 0x%08x\n", i); @@ -696,12 +738,19 @@ show_registers (struct class_device *class_dev, char *buf) size -= temp; next += temp; - for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) { - temp = dbg_port_buf (scratch, sizeof scratch, label, i + 1, - readl (&ehci->regs->port_status [i])); + for (i = 1; i <= HCS_N_PORTS (ehci->hcs_params); i++) { + temp = dbg_port_buf (scratch, sizeof scratch, label, i, + readl (&ehci->regs->port_status [i - 1])); temp = scnprintf (next, size, fmt, temp, scratch); size -= temp; next += temp; + if (i == HCS_DEBUG_PORT(ehci->hcs_params) && ehci->debug) { + temp = scnprintf (next, size, + " debug control %08x\n", + readl (&ehci->debug->control)); + size -= temp; + next += temp; + } } if (ehci->reclaim) { diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index bc69bd7..35248a3 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -304,30 +304,31 @@ static void ehci_watchdog (unsigned long param) */ static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap) { + struct pci_dev *pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller); + + /* always say Linux will own the hardware */ + pci_write_config_byte(pdev, where + 3, 1); + + /* maybe wait a while for BIOS to respond */ if (cap & (1 << 16)) { int msec = 5000; - struct pci_dev *pdev = - to_pci_dev(ehci_to_hcd(ehci)->self.controller); - /* request handoff to OS */ - cap |= 1 << 24; - pci_write_config_dword(pdev, where, cap); - - /* and wait a while for it to happen */ do { msleep(10); msec -= 10; pci_read_config_dword(pdev, where, &cap); } while ((cap & (1 << 16)) && msec); if (cap & (1 << 16)) { - ehci_err (ehci, "BIOS handoff failed (%d, %04x)\n", + ehci_err(ehci, "BIOS handoff failed (%d, %08x)\n", where, cap); // some BIOS versions seem buggy... // return 1; ehci_warn (ehci, "continuing after BIOS bug...\n"); - return 0; - } - ehci_dbg (ehci, "BIOS handoff succeeded\n"); + /* disable all SMIs, and clear "BIOS owns" flag */ + pci_write_config_dword(pdev, where + 4, 0); + pci_write_config_byte(pdev, where + 2, 0); + } else + ehci_dbg(ehci, "BIOS handoff succeeded\n"); } return 0; } @@ -492,8 +493,6 @@ static int ehci_start (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp; - struct usb_device *udev; - struct usb_bus *bus; int retval; u32 hcc_params; u8 sbrn = 0; @@ -588,8 +587,8 @@ static int ehci_start (struct usb_hcd *hcd) writel (0, &ehci->regs->segment); #if 0 // this is deeply broken on almost all architectures - if (!pci_set_dma_mask (to_pci_dev(hcd->self.controller), 0xffffffffffffffffULL)) - ehci_info (ehci, "enabled 64bit PCI DMA\n"); + if (!dma_set_mask (hcd->self.controller, DMA_64BIT_MASK)) + ehci_info (ehci, "enabled 64bit DMA\n"); #endif } @@ -631,17 +630,6 @@ static int ehci_start (struct usb_hcd *hcd) /* set async sleep time = 10 us ... ? */ - /* wire up the root hub */ - bus = hcd_to_bus (hcd); - udev = first ? usb_alloc_dev (NULL, bus, 0) : bus->root_hub; - if (!udev) { -done2: - ehci_mem_cleanup (ehci); - return -ENOMEM; - } - udev->speed = USB_SPEED_HIGH; - udev->state = first ? USB_STATE_ATTACHED : USB_STATE_CONFIGURED; - /* * Start, enabling full USB 2.0 functionality ... usb 1.1 devices * are explicitly handed to companion controller(s), so no TT is @@ -664,24 +652,6 @@ done2: first ? "initialized" : "restarted", temp >> 8, temp & 0xff, DRIVER_VERSION); - /* - * From here on, khubd concurrently accesses the root - * hub; drivers will be talking to enumerated devices. - * (On restart paths, khubd already knows about the root - * hub and could find work as soon as we wrote FLAG_CF.) - * - * Before this point the HC was idle/ready. After, khubd - * and device drivers may start it running. - */ - if (first && usb_hcd_register_root_hub (udev, hcd) != 0) { - if (hcd->state == HC_STATE_RUNNING) - ehci_quiesce (ehci); - ehci_reset (ehci); - usb_put_dev (udev); - retval = -ENODEV; - goto done2; - } - writel (INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */ if (first) diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index d7b4f79..36cc1f2 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2002 by David Brownell + * Copyright (C) 2001-2004 by 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 diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 7df9b9a..45d89a7 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2002 by David Brownell + * Copyright (C) 2001-2004 by 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 diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 2fa1ffe..c2104ca 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -637,9 +637,8 @@ iso_stream_alloc (int mem_flags) { struct ehci_iso_stream *stream; - stream = kmalloc(sizeof *stream, mem_flags); + stream = kcalloc(1, sizeof *stream, mem_flags); if (likely (stream != NULL)) { - memset (stream, 0, sizeof(*stream)); INIT_LIST_HEAD(&stream->td_list); INIT_LIST_HEAD(&stream->free_list); stream->next_uframe = -1; @@ -894,7 +893,7 @@ itd_sched_init ( trans |= length << 16; uframe->transaction = cpu_to_le32 (trans); - /* might need to cross a buffer page within a td */ + /* might need to cross a buffer page within a uframe */ uframe->bufp = (buf & ~(u64)0x0fff); buf += length; if (unlikely ((uframe->bufp != (buf & ~(u64)0x0fff)))) @@ -1194,6 +1193,7 @@ itd_init (struct ehci_iso_stream *stream, struct ehci_itd *itd) { int i; + /* it's been recently zeroed */ itd->hw_next = EHCI_LIST_END; itd->hw_bufp [0] = stream->buf0; itd->hw_bufp [1] = stream->buf1; @@ -1210,8 +1210,7 @@ itd_patch ( struct ehci_itd *itd, struct ehci_iso_sched *iso_sched, unsigned index, - u16 uframe, - int first + u16 uframe ) { struct ehci_iso_packet *uf = &iso_sched->packet [index]; @@ -1228,7 +1227,7 @@ itd_patch ( itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(uf->bufp >> 32)); /* iso_frame_desc[].offset must be strictly increasing */ - if (unlikely (!first && uf->cross)) { + if (unlikely (uf->cross)) { u64 bufp = uf->bufp + 4096; itd->pg = ++pg; itd->hw_bufp [pg] |= cpu_to_le32 (bufp & ~(u32)0); @@ -1257,7 +1256,7 @@ itd_link_urb ( struct ehci_iso_stream *stream ) { - int packet, first = 1; + int packet; unsigned next_uframe, uframe, frame; struct ehci_iso_sched *iso_sched = urb->hcpriv; struct ehci_itd *itd; @@ -1290,7 +1289,6 @@ itd_link_urb ( list_move_tail (&itd->itd_list, &stream->td_list); itd->stream = iso_stream_get (stream); itd->urb = usb_get_urb (urb); - first = 1; itd_init (stream, itd); } @@ -1298,8 +1296,7 @@ itd_link_urb ( frame = next_uframe >> 3; itd->usecs [uframe] = stream->usecs; - itd_patch (itd, iso_sched, packet, uframe, first); - first = 0; + itd_patch (itd, iso_sched, packet, uframe); next_uframe += stream->interval; stream->depth += stream->interval; diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c new file mode 100644 index 0000000..ff0a168 --- /dev/null +++ b/drivers/usb/host/isp116x-hcd.c @@ -0,0 +1,1875 @@ +/* + * ISP116x HCD (Host Controller Driver) for USB. + * + * Derived from the SL811 HCD, rewritten for ISP116x. + * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee> + * + * Portions: + * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) + * Copyright (C) 2004 David Brownell + * + * Periodic scheduling is based on Roman's OHCI code + * Copyright (C) 1999 Roman Weissgaerber + * + */ + +/* + * The driver basically works. A number of people have used it with a range + * of devices. + * + *The driver passes all usbtests 1-14. + * + * Suspending/resuming of root hub via sysfs works. Remote wakeup works too. + * And suspending/resuming of platform device works too. Suspend/resume + * via HCD operations vector is not implemented. + * + * Iso transfer support is not implemented. Adding this would include + * implementing recovery from the failure to service the processed ITL + * fifo ram in time, which will involve chip reset. + * + * TODO: + + More testing of suspend/resume. +*/ + +/* + ISP116x chips require certain delays between accesses to its + registers. The following timing options exist. + + 1. Configure your memory controller (the best) + 2. Implement platform-specific delay function possibly + combined with configuring the memory controller; see + include/linux/usb-isp116x.h for more info. Some broken + memory controllers line LH7A400 SMC need this. Also, + uncomment for that to work the following + USE_PLATFORM_DELAY macro. + 3. Use ndelay (easiest, poorest). For that, uncomment + the following USE_NDELAY macro. +*/ +#define USE_PLATFORM_DELAY +//#define USE_NDELAY + +//#define DEBUG +//#define VERBOSE +/* Transfer descriptors. See dump_ptd() for printout format */ +//#define PTD_TRACE +/* enqueuing/finishing log of urbs */ +//#define URB_TRACE + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/usb.h> +#include <linux/usb_isp116x.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/byteorder.h> + +#ifndef DEBUG +# define STUB_DEBUG_FILE +#endif + +#include "../core/hcd.h" +#include "isp116x.h" + +#define DRIVER_VERSION "08 Apr 2005" +#define DRIVER_DESC "ISP116x USB Host Controller Driver" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +static const char hcd_name[] = "isp116x-hcd"; + +/*-----------------------------------------------------------------*/ + +/* + Write len bytes to fifo, pad till 32-bit boundary + */ +static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len) +{ + u8 *dp = (u8 *) buf; + u16 *dp2 = (u16 *) buf; + u16 w; + int quot = len % 4; + + if ((unsigned long)dp2 & 1) { + /* not aligned */ + for (; len > 1; len -= 2) { + w = *dp++; + w |= *dp++ << 8; + isp116x_raw_write_data16(isp116x, w); + } + if (len) + isp116x_write_data16(isp116x, (u16) * dp); + } else { + /* aligned */ + for (; len > 1; len -= 2) + isp116x_raw_write_data16(isp116x, *dp2++); + if (len) + isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2)); + } + if (quot == 1 || quot == 2) + isp116x_raw_write_data16(isp116x, 0); +} + +/* + Read len bytes from fifo and then read till 32-bit boundary. + */ +static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len) +{ + u8 *dp = (u8 *) buf; + u16 *dp2 = (u16 *) buf; + u16 w; + int quot = len % 4; + + if ((unsigned long)dp2 & 1) { + /* not aligned */ + for (; len > 1; len -= 2) { + w = isp116x_raw_read_data16(isp116x); + *dp++ = w & 0xff; + *dp++ = (w >> 8) & 0xff; + } + if (len) + *dp = 0xff & isp116x_read_data16(isp116x); + } else { + /* aligned */ + for (; len > 1; len -= 2) + *dp2++ = isp116x_raw_read_data16(isp116x); + if (len) + *(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x); + } + if (quot == 1 || quot == 2) + isp116x_raw_read_data16(isp116x); +} + +/* + Write ptd's and data for scheduled transfers into + the fifo ram. Fifo must be empty and ready. +*/ +static void pack_fifo(struct isp116x *isp116x) +{ + struct isp116x_ep *ep; + struct ptd *ptd; + int buflen = isp116x->atl_last_dir == PTD_DIR_IN + ? isp116x->atl_bufshrt : isp116x->atl_buflen; + int ptd_count = 0; + + isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); + isp116x_write_reg16(isp116x, HCXFERCTR, buflen); + isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET); + for (ep = isp116x->atl_active; ep; ep = ep->active) { + ++ptd_count; + ptd = &ep->ptd; + dump_ptd(ptd); + dump_ptd_out_data(ptd, ep->data); + isp116x_write_data16(isp116x, ptd->count); + isp116x_write_data16(isp116x, ptd->mps); + isp116x_write_data16(isp116x, ptd->len); + isp116x_write_data16(isp116x, ptd->faddr); + buflen -= sizeof(struct ptd); + /* Skip writing data for last IN PTD */ + if (ep->active || (isp116x->atl_last_dir != PTD_DIR_IN)) { + write_ptddata_to_fifo(isp116x, ep->data, ep->length); + buflen -= ALIGN(ep->length, 4); + } + } + BUG_ON(buflen); +} + +/* + Read the processed ptd's and data from fifo ram back to + URBs' buffers. Fifo must be full and done +*/ +static void unpack_fifo(struct isp116x *isp116x) +{ + struct isp116x_ep *ep; + struct ptd *ptd; + int buflen = isp116x->atl_last_dir == PTD_DIR_IN + ? isp116x->atl_buflen : isp116x->atl_bufshrt; + + isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); + isp116x_write_reg16(isp116x, HCXFERCTR, buflen); + isp116x_write_addr(isp116x, HCATLPORT); + for (ep = isp116x->atl_active; ep; ep = ep->active) { + ptd = &ep->ptd; + ptd->count = isp116x_read_data16(isp116x); + ptd->mps = isp116x_read_data16(isp116x); + ptd->len = isp116x_read_data16(isp116x); + ptd->faddr = isp116x_read_data16(isp116x); + buflen -= sizeof(struct ptd); + /* Skip reading data for last Setup or Out PTD */ + if (ep->active || (isp116x->atl_last_dir == PTD_DIR_IN)) { + read_ptddata_from_fifo(isp116x, ep->data, ep->length); + buflen -= ALIGN(ep->length, 4); + } + dump_ptd(ptd); + dump_ptd_in_data(ptd, ep->data); + } + BUG_ON(buflen); +} + +/*---------------------------------------------------------------*/ + +/* + Set up PTD's. +*/ +static void preproc_atl_queue(struct isp116x *isp116x) +{ + struct isp116x_ep *ep; + struct urb *urb; + struct ptd *ptd; + u16 toggle, dir, len; + + for (ep = isp116x->atl_active; ep; ep = ep->active) { + BUG_ON(list_empty(&ep->hep->urb_list)); + urb = container_of(ep->hep->urb_list.next, + struct urb, urb_list); + ptd = &ep->ptd; + len = ep->length; + spin_lock(&urb->lock); + ep->data = (unsigned char *)urb->transfer_buffer + + urb->actual_length; + + switch (ep->nextpid) { + case USB_PID_IN: + toggle = usb_gettoggle(urb->dev, ep->epnum, 0); + dir = PTD_DIR_IN; + break; + case USB_PID_OUT: + toggle = usb_gettoggle(urb->dev, ep->epnum, 1); + dir = PTD_DIR_OUT; + break; + case USB_PID_SETUP: + toggle = 0; + dir = PTD_DIR_SETUP; + len = sizeof(struct usb_ctrlrequest); + ep->data = urb->setup_packet; + break; + case USB_PID_ACK: + toggle = 1; + len = 0; + dir = (urb->transfer_buffer_length + && usb_pipein(urb->pipe)) + ? PTD_DIR_OUT : PTD_DIR_IN; + break; + default: + /* To please gcc */ + toggle = dir = 0; + ERR("%s %d: ep->nextpid %d\n", __func__, __LINE__, + ep->nextpid); + BUG_ON(1); + } + + ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(toggle); + ptd->mps = PTD_MPS(ep->maxpacket) + | PTD_SPD(urb->dev->speed == USB_SPEED_LOW) + | PTD_EP(ep->epnum); + ptd->len = PTD_LEN(len) | PTD_DIR(dir); + ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe)); + spin_unlock(&urb->lock); + if (!ep->active) { + ptd->mps |= PTD_LAST_MSK; + isp116x->atl_last_dir = dir; + } + isp116x->atl_bufshrt = sizeof(struct ptd) + isp116x->atl_buflen; + isp116x->atl_buflen = isp116x->atl_bufshrt + ALIGN(len, 4); + } +} + +/* + Analyze transfer results, handle partial transfers and errors +*/ +static void postproc_atl_queue(struct isp116x *isp116x) +{ + struct isp116x_ep *ep; + struct urb *urb; + struct usb_device *udev; + struct ptd *ptd; + int short_not_ok; + u8 cc; + + for (ep = isp116x->atl_active; ep; ep = ep->active) { + BUG_ON(list_empty(&ep->hep->urb_list)); + urb = + container_of(ep->hep->urb_list.next, struct urb, urb_list); + udev = urb->dev; + ptd = &ep->ptd; + cc = PTD_GET_CC(ptd); + + spin_lock(&urb->lock); + short_not_ok = 1; + + /* Data underrun is special. For allowed underrun + we clear the error and continue as normal. For + forbidden underrun we finish the DATA stage + immediately while for control transfer, + we do a STATUS stage. */ + if (cc == TD_DATAUNDERRUN) { + if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) { + DBG("Allowed data underrun\n"); + cc = TD_CC_NOERROR; + short_not_ok = 0; + } else { + ep->error_count = 1; + if (usb_pipecontrol(urb->pipe)) + ep->nextpid = USB_PID_ACK; + else + usb_settoggle(udev, ep->epnum, + ep->nextpid == + USB_PID_OUT, + PTD_GET_TOGGLE(ptd) ^ 1); + urb->status = cc_to_error[TD_DATAUNDERRUN]; + spin_unlock(&urb->lock); + continue; + } + } + /* Keep underrun error through the STATUS stage */ + if (urb->status == cc_to_error[TD_DATAUNDERRUN]) + cc = TD_DATAUNDERRUN; + + if (cc != TD_CC_NOERROR && cc != TD_NOTACCESSED + && (++ep->error_count >= 3 || cc == TD_CC_STALL + || cc == TD_DATAOVERRUN)) { + if (urb->status == -EINPROGRESS) + urb->status = cc_to_error[cc]; + if (ep->nextpid == USB_PID_ACK) + ep->nextpid = 0; + spin_unlock(&urb->lock); + continue; + } + /* According to usb spec, zero-length Int transfer signals + finishing of the urb. Hey, does this apply only + for IN endpoints? */ + if (usb_pipeint(urb->pipe) && !PTD_GET_LEN(ptd)) { + if (urb->status == -EINPROGRESS) + urb->status = 0; + spin_unlock(&urb->lock); + continue; + } + + /* Relax after previously failed, but later succeeded + or correctly NAK'ed retransmission attempt */ + if (ep->error_count + && (cc == TD_CC_NOERROR || cc == TD_NOTACCESSED)) + ep->error_count = 0; + + /* Take into account idiosyncracies of the isp116x chip + regarding toggle bit for failed transfers */ + if (ep->nextpid == USB_PID_OUT) + usb_settoggle(udev, ep->epnum, 1, PTD_GET_TOGGLE(ptd) + ^ (ep->error_count > 0)); + else if (ep->nextpid == USB_PID_IN) + usb_settoggle(udev, ep->epnum, 0, PTD_GET_TOGGLE(ptd) + ^ (ep->error_count > 0)); + + switch (ep->nextpid) { + case USB_PID_IN: + case USB_PID_OUT: + urb->actual_length += PTD_GET_COUNT(ptd); + if (PTD_GET_ACTIVE(ptd) + || (cc != TD_CC_NOERROR && cc < 0x0E)) + break; + if (urb->transfer_buffer_length != urb->actual_length) { + if (short_not_ok) + break; + } else { + if (urb->transfer_flags & URB_ZERO_PACKET + && ep->nextpid == USB_PID_OUT + && !(PTD_GET_COUNT(ptd) % ep->maxpacket)) { + DBG("Zero packet requested\n"); + break; + } + } + /* All data for this URB is transferred, let's finish */ + if (usb_pipecontrol(urb->pipe)) + ep->nextpid = USB_PID_ACK; + else if (urb->status == -EINPROGRESS) + urb->status = 0; + break; + case USB_PID_SETUP: + if (PTD_GET_ACTIVE(ptd) + || (cc != TD_CC_NOERROR && cc < 0x0E)) + break; + if (urb->transfer_buffer_length == urb->actual_length) + ep->nextpid = USB_PID_ACK; + else if (usb_pipeout(urb->pipe)) { + usb_settoggle(udev, 0, 1, 1); + ep->nextpid = USB_PID_OUT; + } else { + usb_settoggle(udev, 0, 0, 1); + ep->nextpid = USB_PID_IN; + } + break; + case USB_PID_ACK: + if (PTD_GET_ACTIVE(ptd) + || (cc != TD_CC_NOERROR && cc < 0x0E)) + break; + if (urb->status == -EINPROGRESS) + urb->status = 0; + ep->nextpid = 0; + break; + default: + BUG_ON(1); + } + spin_unlock(&urb->lock); + } +} + +/* + Take done or failed requests out of schedule. Give back + processed urbs. +*/ +static void finish_request(struct isp116x *isp116x, struct isp116x_ep *ep, + struct urb *urb, struct pt_regs *regs) +__releases(isp116x->lock) __acquires(isp116x->lock) +{ + unsigned i; + + urb->hcpriv = NULL; + ep->error_count = 0; + + if (usb_pipecontrol(urb->pipe)) + ep->nextpid = USB_PID_SETUP; + + urb_dbg(urb, "Finish"); + + spin_unlock(&isp116x->lock); + usb_hcd_giveback_urb(isp116x_to_hcd(isp116x), urb, regs); + spin_lock(&isp116x->lock); + + /* take idle endpoints out of the schedule */ + if (!list_empty(&ep->hep->urb_list)) + return; + + /* async deschedule */ + if (!list_empty(&ep->schedule)) { + list_del_init(&ep->schedule); + return; + } + + /* periodic deschedule */ + DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); + for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { + struct isp116x_ep *temp; + struct isp116x_ep **prev = &isp116x->periodic[i]; + + while (*prev && ((temp = *prev) != ep)) + prev = &temp->next; + if (*prev) + *prev = ep->next; + isp116x->load[i] -= ep->load; + } + ep->branch = PERIODIC_SIZE; + isp116x_to_hcd(isp116x)->self.bandwidth_allocated -= + ep->load / ep->period; + + /* switch irq type? */ + if (!--isp116x->periodic_count) { + isp116x->irqenb &= ~HCuPINT_SOF; + isp116x->irqenb |= HCuPINT_ATL; + } +} + +/* + Scan transfer lists, schedule transfers, send data off + to chip. + */ +static void start_atl_transfers(struct isp116x *isp116x) +{ + struct isp116x_ep *last_ep = NULL, *ep; + struct urb *urb; + u16 load = 0; + int len, index, speed, byte_time; + + if (atomic_read(&isp116x->atl_finishing)) + return; + + if (!HC_IS_RUNNING(isp116x_to_hcd(isp116x)->state)) + return; + + /* FIFO not empty? */ + if (isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_FULL) + return; + + isp116x->atl_active = NULL; + isp116x->atl_buflen = isp116x->atl_bufshrt = 0; + + /* Schedule int transfers */ + if (isp116x->periodic_count) { + isp116x->fmindex = index = + (isp116x->fmindex + 1) & (PERIODIC_SIZE - 1); + if ((load = isp116x->load[index])) { + /* Bring all int transfers for this frame + into the active queue */ + isp116x->atl_active = last_ep = + isp116x->periodic[index]; + while (last_ep->next) + last_ep = (last_ep->active = last_ep->next); + last_ep->active = NULL; + } + } + + /* Schedule control/bulk transfers */ + list_for_each_entry(ep, &isp116x->async, schedule) { + urb = container_of(ep->hep->urb_list.next, + struct urb, urb_list); + speed = urb->dev->speed; + byte_time = speed == USB_SPEED_LOW + ? BYTE_TIME_LOWSPEED : BYTE_TIME_FULLSPEED; + + if (ep->nextpid == USB_PID_SETUP) { + len = sizeof(struct usb_ctrlrequest); + } else if (ep->nextpid == USB_PID_ACK) { + len = 0; + } else { + /* Find current free length ... */ + len = (MAX_LOAD_LIMIT - load) / byte_time; + + /* ... then limit it to configured max size ... */ + len = min(len, speed == USB_SPEED_LOW ? + MAX_TRANSFER_SIZE_LOWSPEED : + MAX_TRANSFER_SIZE_FULLSPEED); + + /* ... and finally cut to the multiple of MaxPacketSize, + or to the real length if there's enough room. */ + if (len < + (urb->transfer_buffer_length - + urb->actual_length)) { + len -= len % ep->maxpacket; + if (!len) + continue; + } else + len = urb->transfer_buffer_length - + urb->actual_length; + BUG_ON(len < 0); + } + + load += len * byte_time; + if (load > MAX_LOAD_LIMIT) + break; + + ep->active = NULL; + ep->length = len; + if (last_ep) + last_ep->active = ep; + else + isp116x->atl_active = ep; + last_ep = ep; + } + + /* Avoid starving of endpoints */ + if ((&isp116x->async)->next != (&isp116x->async)->prev) + list_move(&isp116x->async, (&isp116x->async)->next); + + if (isp116x->atl_active) { + preproc_atl_queue(isp116x); + pack_fifo(isp116x); + } +} + +/* + Finish the processed transfers +*/ +static void finish_atl_transfers(struct isp116x *isp116x, struct pt_regs *regs) +{ + struct isp116x_ep *ep; + struct urb *urb; + + if (!isp116x->atl_active) + return; + /* Fifo not ready? */ + if (!(isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_DONE)) + return; + + atomic_inc(&isp116x->atl_finishing); + unpack_fifo(isp116x); + postproc_atl_queue(isp116x); + for (ep = isp116x->atl_active; ep; ep = ep->active) { + urb = + container_of(ep->hep->urb_list.next, struct urb, urb_list); + /* USB_PID_ACK check here avoids finishing of + control transfers, for which TD_DATAUNDERRUN + occured, while URB_SHORT_NOT_OK was set */ + if (urb && urb->status != -EINPROGRESS + && ep->nextpid != USB_PID_ACK) + finish_request(isp116x, ep, urb, regs); + } + atomic_dec(&isp116x->atl_finishing); +} + +static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs) +{ + struct isp116x *isp116x = hcd_to_isp116x(hcd); + u16 irqstat; + irqreturn_t ret = IRQ_NONE; + + spin_lock(&isp116x->lock); + isp116x_write_reg16(isp116x, HCuPINTENB, 0); + irqstat = isp116x_read_reg16(isp116x, HCuPINT); + isp116x_write_reg16(isp116x, HCuPINT, irqstat); + + if (irqstat & (HCuPINT_ATL | HCuPINT_SOF)) { + ret = IRQ_HANDLED; + finish_atl_transfers(isp116x, regs); + } + + if (irqstat & HCuPINT_OPR) { + u32 intstat = isp116x_read_reg32(isp116x, HCINTSTAT); + isp116x_write_reg32(isp116x, HCINTSTAT, intstat); + if (intstat & HCINT_UE) { + ERR("Unrecoverable error\n"); + /* What should we do here? Reset? */ + } + if (intstat & HCINT_RHSC) { + isp116x->rhstatus = + isp116x_read_reg32(isp116x, HCRHSTATUS); + isp116x->rhport[0] = + isp116x_read_reg32(isp116x, HCRHPORT1); + isp116x->rhport[1] = + isp116x_read_reg32(isp116x, HCRHPORT2); + } + if (intstat & HCINT_RD) { + DBG("---- remote wakeup\n"); + schedule_work(&isp116x->rh_resume); + ret = IRQ_HANDLED; + } + irqstat &= ~HCuPINT_OPR; + ret = IRQ_HANDLED; + } + + if (irqstat & (HCuPINT_ATL | HCuPINT_SOF)) { + start_atl_transfers(isp116x); + } + + isp116x_write_reg16(isp116x, HCuPINTENB, isp116x->irqenb); + spin_unlock(&isp116x->lock); + return ret; +} + +/*-----------------------------------------------------------------*/ + +/* usb 1.1 says max 90% of a frame is available for periodic transfers. + * this driver doesn't promise that much since it's got to handle an + * IRQ per packet; irq handling latencies also use up that time. + */ + +/* out of 1000 us */ +#define MAX_PERIODIC_LOAD 600 +static int balance(struct isp116x *isp116x, u16 period, u16 load) +{ + int i, branch = -ENOSPC; + + /* search for the least loaded schedule branch of that period + which has enough bandwidth left unreserved. */ + for (i = 0; i < period; i++) { + if (branch < 0 || isp116x->load[branch] > isp116x->load[i]) { + int j; + + for (j = i; j < PERIODIC_SIZE; j += period) { + if ((isp116x->load[j] + load) + > MAX_PERIODIC_LOAD) + break; + } + if (j < PERIODIC_SIZE) + continue; + branch = i; + } + } + return branch; +} + +/* NB! ALL the code above this point runs with isp116x->lock + held, irqs off +*/ + +/*-----------------------------------------------------------------*/ + +static int isp116x_urb_enqueue(struct usb_hcd *hcd, + struct usb_host_endpoint *hep, struct urb *urb, + int mem_flags) +{ + struct isp116x *isp116x = hcd_to_isp116x(hcd); + struct usb_device *udev = urb->dev; + unsigned int pipe = urb->pipe; + int is_out = !usb_pipein(pipe); + int type = usb_pipetype(pipe); + int epnum = usb_pipeendpoint(pipe); + struct isp116x_ep *ep = NULL; + unsigned long flags; + int i; + int ret = 0; + + urb_dbg(urb, "Enqueue"); + + if (type == PIPE_ISOCHRONOUS) { + ERR("Isochronous transfers not supported\n"); + urb_dbg(urb, "Refused to enqueue"); + return -ENXIO; + } + /* avoid all allocations within spinlocks: request or endpoint */ + if (!hep->hcpriv) { + ep = kcalloc(1, sizeof *ep, (__force unsigned)mem_flags); + if (!ep) + return -ENOMEM; + } + + spin_lock_irqsave(&isp116x->lock, flags); + if (!HC_IS_RUNNING(hcd->state)) { + ret = -ENODEV; + goto fail; + } + + if (hep->hcpriv) + ep = hep->hcpriv; + else { + INIT_LIST_HEAD(&ep->schedule); + ep->udev = usb_get_dev(udev); + ep->epnum = epnum; + ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out); + usb_settoggle(udev, epnum, is_out, 0); + + if (type == PIPE_CONTROL) { + ep->nextpid = USB_PID_SETUP; + } else if (is_out) { + ep->nextpid = USB_PID_OUT; + } else { + ep->nextpid = USB_PID_IN; + } + + if (urb->interval) { + /* + With INT URBs submitted, the driver works with SOF + interrupt enabled and ATL interrupt disabled. After + the PTDs are written to fifo ram, the chip starts + fifo processing and usb transfers after the next + SOF and continues until the transfers are finished + (succeeded or failed) or the frame ends. Therefore, + the transfers occur only in every second frame, + while fifo reading/writing and data processing + occur in every other second frame. */ + if (urb->interval < 2) + urb->interval = 2; + if (urb->interval > 2 * PERIODIC_SIZE) + urb->interval = 2 * PERIODIC_SIZE; + ep->period = urb->interval >> 1; + ep->branch = PERIODIC_SIZE; + ep->load = usb_calc_bus_time(udev->speed, + !is_out, + (type == PIPE_ISOCHRONOUS), + usb_maxpacket(udev, pipe, + is_out)) / + 1000; + } + hep->hcpriv = ep; + ep->hep = hep; + } + + /* maybe put endpoint into schedule */ + switch (type) { + case PIPE_CONTROL: + case PIPE_BULK: + if (list_empty(&ep->schedule)) + list_add_tail(&ep->schedule, &isp116x->async); + break; + case PIPE_INTERRUPT: + urb->interval = ep->period; + ep->length = min((int)ep->maxpacket, + urb->transfer_buffer_length); + + /* urb submitted for already existing endpoint */ + if (ep->branch < PERIODIC_SIZE) + break; + + ret = ep->branch = balance(isp116x, ep->period, ep->load); + if (ret < 0) + goto fail; + ret = 0; + + urb->start_frame = (isp116x->fmindex & (PERIODIC_SIZE - 1)) + + ep->branch; + + /* sort each schedule branch by period (slow before fast) + to share the faster parts of the tree without needing + dummy/placeholder nodes */ + DBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); + for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { + struct isp116x_ep **prev = &isp116x->periodic[i]; + struct isp116x_ep *here = *prev; + + while (here && ep != here) { + if (ep->period > here->period) + break; + prev = &here->next; + here = *prev; + } + if (ep != here) { + ep->next = here; + *prev = ep; + } + isp116x->load[i] += ep->load; + } + hcd->self.bandwidth_allocated += ep->load / ep->period; + + /* switch over to SOFint */ + if (!isp116x->periodic_count++) { + isp116x->irqenb &= ~HCuPINT_ATL; + isp116x->irqenb |= HCuPINT_SOF; + isp116x_write_reg16(isp116x, HCuPINTENB, + isp116x->irqenb); + } + } + + /* in case of unlink-during-submit */ + spin_lock(&urb->lock); + if (urb->status != -EINPROGRESS) { + spin_unlock(&urb->lock); + finish_request(isp116x, ep, urb, NULL); + ret = 0; + goto fail; + } + urb->hcpriv = hep; + spin_unlock(&urb->lock); + start_atl_transfers(isp116x); + + fail: + spin_unlock_irqrestore(&isp116x->lock, flags); + return ret; +} + +/* + Dequeue URBs. +*/ +static int isp116x_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +{ + struct isp116x *isp116x = hcd_to_isp116x(hcd); + struct usb_host_endpoint *hep; + struct isp116x_ep *ep, *ep_act; + unsigned long flags; + + spin_lock_irqsave(&isp116x->lock, flags); + hep = urb->hcpriv; + /* URB already unlinked (or never linked)? */ + if (!hep) { + spin_unlock_irqrestore(&isp116x->lock, flags); + return 0; + } + ep = hep->hcpriv; + WARN_ON(hep != ep->hep); + + /* In front of queue? */ + if (ep->hep->urb_list.next == &urb->urb_list) + /* active? */ + for (ep_act = isp116x->atl_active; ep_act; + ep_act = ep_act->active) + if (ep_act == ep) { + VDBG("dequeue, urb %p active; wait for irq\n", + urb); + urb = NULL; + break; + } + + if (urb) + finish_request(isp116x, ep, urb, NULL); + + spin_unlock_irqrestore(&isp116x->lock, flags); + return 0; +} + +static void isp116x_endpoint_disable(struct usb_hcd *hcd, + struct usb_host_endpoint *hep) +{ + int i; + struct isp116x_ep *ep = hep->hcpriv;; + + if (!ep) + return; + + /* assume we'd just wait for the irq */ + for (i = 0; i < 100 && !list_empty(&hep->urb_list); i++) + msleep(3); + if (!list_empty(&hep->urb_list)) + WARN("ep %p not empty?\n", ep); + + usb_put_dev(ep->udev); + kfree(ep); + hep->hcpriv = NULL; +} + +static int isp116x_get_frame(struct usb_hcd *hcd) +{ + struct isp116x *isp116x = hcd_to_isp116x(hcd); + u32 fmnum; + unsigned long flags; + + spin_lock_irqsave(&isp116x->lock, flags); + fmnum = isp116x_read_reg32(isp116x, HCFMNUM); + spin_unlock_irqrestore(&isp116x->lock, flags); + return (int)fmnum; +} + +/*----------------------------------------------------------------*/ + +/* + Adapted from ohci-hub.c. Currently we don't support autosuspend. +*/ +static int isp116x_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct isp116x *isp116x = hcd_to_isp116x(hcd); + int ports, i, changed = 0; + + if (!HC_IS_RUNNING(hcd->state)) + return -ESHUTDOWN; + + ports = isp116x->rhdesca & RH_A_NDP; + + /* init status */ + if (isp116x->rhstatus & (RH_HS_LPSC | RH_HS_OCIC)) + buf[0] = changed = 1; + else + buf[0] = 0; + + for (i = 0; i < ports; i++) { + u32 status = isp116x->rhport[i]; + + if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC + | RH_PS_OCIC | RH_PS_PRSC)) { + changed = 1; + buf[0] |= 1 << (i + 1); + continue; + } + } + return changed; +} + +static void isp116x_hub_descriptor(struct isp116x *isp116x, + struct usb_hub_descriptor *desc) +{ + u32 reg = isp116x->rhdesca; + + desc->bDescriptorType = 0x29; + desc->bDescLength = 9; + desc->bHubContrCurrent = 0; + desc->bNbrPorts = (u8) (reg & 0x3); + /* Power switching, device type, overcurrent. */ + desc->wHubCharacteristics = + (__force __u16) cpu_to_le16((u16) ((reg >> 8) & 0x1f)); + desc->bPwrOn2PwrGood = (u8) ((reg >> 24) & 0xff); + /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */ + desc->bitmap[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1; + desc->bitmap[1] = ~0; +} + +/* Perform reset of a given port. + It would be great to just start the reset and let the + USB core to clear the reset in due time. However, + root hub ports should be reset for at least 50 ms, while + our chip stays in reset for about 10 ms. I.e., we must + repeatedly reset it ourself here. +*/ +static inline void root_port_reset(struct isp116x *isp116x, unsigned port) +{ + u32 tmp; + unsigned long flags, t; + + /* Root hub reset should be 50 ms, but some devices + want it even longer. */ + t = jiffies + msecs_to_jiffies(100); + + while (time_before(jiffies, t)) { + spin_lock_irqsave(&isp116x->lock, flags); + /* spin until any current reset finishes */ + for (;;) { + tmp = isp116x_read_reg32(isp116x, port ? + HCRHPORT2 : HCRHPORT1); + if (!(tmp & RH_PS_PRS)) + break; + udelay(500); + } + /* Don't reset a disconnected port */ + if (!(tmp & RH_PS_CCS)) { + spin_unlock_irqrestore(&isp116x->lock, flags); + break; + } + /* Reset lasts 10ms (claims datasheet) */ + isp116x_write_reg32(isp116x, port ? HCRHPORT2 : + HCRHPORT1, (RH_PS_PRS)); + spin_unlock_irqrestore(&isp116x->lock, flags); + msleep(10); + } +} + +/* Adapted from ohci-hub.c */ +static int isp116x_hub_control(struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, u16 wIndex, char *buf, u16 wLength) +{ + struct isp116x *isp116x = hcd_to_isp116x(hcd); + int ret = 0; + unsigned long flags; + int ports = isp116x->rhdesca & RH_A_NDP; + u32 tmp = 0; + + switch (typeReq) { + case ClearHubFeature: + DBG("ClearHubFeature: "); + switch (wValue) { + case C_HUB_OVER_CURRENT: + DBG("C_HUB_OVER_CURRENT\n"); + spin_lock_irqsave(&isp116x->lock, flags); + isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_OCIC); + spin_unlock_irqrestore(&isp116x->lock, flags); + case C_HUB_LOCAL_POWER: + DBG("C_HUB_LOCAL_POWER\n"); + break; + default: + goto error; + } + break; + case SetHubFeature: + DBG("SetHubFeature: "); + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + DBG("C_HUB_OVER_CURRENT or C_HUB_LOCAL_POWER\n"); + break; + default: + goto error; + } + break; + case GetHubDescriptor: + DBG("GetHubDescriptor\n"); + isp116x_hub_descriptor(isp116x, + (struct usb_hub_descriptor *)buf); + break; + case GetHubStatus: + DBG("GetHubStatus\n"); + *(__le32 *) buf = cpu_to_le32(0); + break; + case GetPortStatus: + DBG("GetPortStatus\n"); + if (!wIndex || wIndex > ports) + goto error; + tmp = isp116x->rhport[--wIndex]; + *(__le32 *) buf = cpu_to_le32(tmp); + DBG("GetPortStatus: port[%d] %08x\n", wIndex + 1, tmp); + break; + case ClearPortFeature: + DBG("ClearPortFeature: "); + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + DBG("USB_PORT_FEAT_ENABLE\n"); + tmp = RH_PS_CCS; + break; + case USB_PORT_FEAT_C_ENABLE: + DBG("USB_PORT_FEAT_C_ENABLE\n"); + tmp = RH_PS_PESC; + break; + case USB_PORT_FEAT_SUSPEND: + DBG("USB_PORT_FEAT_SUSPEND\n"); + tmp = RH_PS_POCI; + break; + case USB_PORT_FEAT_C_SUSPEND: + DBG("USB_PORT_FEAT_C_SUSPEND\n"); + tmp = RH_PS_PSSC; + break; + case USB_PORT_FEAT_POWER: + DBG("USB_PORT_FEAT_POWER\n"); + tmp = RH_PS_LSDA; + break; + case USB_PORT_FEAT_C_CONNECTION: + DBG("USB_PORT_FEAT_C_CONNECTION\n"); + tmp = RH_PS_CSC; + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + DBG("USB_PORT_FEAT_C_OVER_CURRENT\n"); + tmp = RH_PS_OCIC; + break; + case USB_PORT_FEAT_C_RESET: + DBG("USB_PORT_FEAT_C_RESET\n"); + tmp = RH_PS_PRSC; + break; + default: + goto error; + } + spin_lock_irqsave(&isp116x->lock, flags); + isp116x_write_reg32(isp116x, wIndex + ? HCRHPORT2 : HCRHPORT1, tmp); + isp116x->rhport[wIndex] = + isp116x_read_reg32(isp116x, wIndex ? HCRHPORT2 : HCRHPORT1); + spin_unlock_irqrestore(&isp116x->lock, flags); + break; + case SetPortFeature: + DBG("SetPortFeature: "); + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + DBG("USB_PORT_FEAT_SUSPEND\n"); + spin_lock_irqsave(&isp116x->lock, flags); + isp116x_write_reg32(isp116x, wIndex + ? HCRHPORT2 : HCRHPORT1, RH_PS_PSS); + break; + case USB_PORT_FEAT_POWER: + DBG("USB_PORT_FEAT_POWER\n"); + spin_lock_irqsave(&isp116x->lock, flags); + isp116x_write_reg32(isp116x, wIndex + ? HCRHPORT2 : HCRHPORT1, RH_PS_PPS); + break; + case USB_PORT_FEAT_RESET: + DBG("USB_PORT_FEAT_RESET\n"); + root_port_reset(isp116x, wIndex); + spin_lock_irqsave(&isp116x->lock, flags); + break; + default: + goto error; + } + isp116x->rhport[wIndex] = + isp116x_read_reg32(isp116x, wIndex ? HCRHPORT2 : HCRHPORT1); + spin_unlock_irqrestore(&isp116x->lock, flags); + break; + + default: + error: + /* "protocol stall" on error */ + DBG("PROTOCOL STALL\n"); + ret = -EPIPE; + } + return ret; +} + +#ifdef CONFIG_PM + +static int isp116x_hub_suspend(struct usb_hcd *hcd) +{ + struct isp116x *isp116x = hcd_to_isp116x(hcd); + unsigned long flags; + u32 val; + int ret = 0; + + spin_lock_irqsave(&isp116x->lock, flags); + + val = isp116x_read_reg32(isp116x, HCCONTROL); + switch (val & HCCONTROL_HCFS) { + case HCCONTROL_USB_OPER: + hcd->state = HC_STATE_QUIESCING; + val &= (~HCCONTROL_HCFS & ~HCCONTROL_RWE); + val |= HCCONTROL_USB_SUSPEND; + if (hcd->remote_wakeup) + val |= HCCONTROL_RWE; + /* Wait for usb transfers to finish */ + mdelay(2); + isp116x_write_reg32(isp116x, HCCONTROL, val); + hcd->state = HC_STATE_SUSPENDED; + /* Wait for devices to suspend */ + mdelay(5); + case HCCONTROL_USB_SUSPEND: + break; + case HCCONTROL_USB_RESUME: + isp116x_write_reg32(isp116x, HCCONTROL, + (val & ~HCCONTROL_HCFS) | + HCCONTROL_USB_RESET); + case HCCONTROL_USB_RESET: + ret = -EBUSY; + break; + default: + ret = -EINVAL; + } + + spin_unlock_irqrestore(&isp116x->lock, flags); + return ret; +} + +static int isp116x_hub_resume(struct usb_hcd *hcd) +{ + struct isp116x *isp116x = hcd_to_isp116x(hcd); + u32 val; + int ret = -EINPROGRESS; + + msleep(5); + spin_lock_irq(&isp116x->lock); + + val = isp116x_read_reg32(isp116x, HCCONTROL); + switch (val & HCCONTROL_HCFS) { + case HCCONTROL_USB_SUSPEND: + val &= ~HCCONTROL_HCFS; + val |= HCCONTROL_USB_RESUME; + isp116x_write_reg32(isp116x, HCCONTROL, val); + case HCCONTROL_USB_RESUME: + break; + case HCCONTROL_USB_OPER: + /* Without setting power_state here the + SUSPENDED state won't be removed from + sysfs/usbN/power.state as a response to remote + wakeup. Maybe in the future. */ + hcd->self.root_hub->dev.power.power_state = PMSG_ON; + ret = 0; + break; + default: + ret = -EBUSY; + } + + if (ret != -EINPROGRESS) { + spin_unlock_irq(&isp116x->lock); + return ret; + } + + val = isp116x->rhdesca & RH_A_NDP; + while (val--) { + u32 stat = + isp116x_read_reg32(isp116x, val ? HCRHPORT2 : HCRHPORT1); + /* force global, not selective, resume */ + if (!(stat & RH_PS_PSS)) + continue; + DBG("%s: Resuming port %d\n", __func__, val); + isp116x_write_reg32(isp116x, RH_PS_POCI, val + ? HCRHPORT2 : HCRHPORT1); + } + spin_unlock_irq(&isp116x->lock); + + hcd->state = HC_STATE_RESUMING; + mdelay(20); + + /* Go operational */ + spin_lock_irq(&isp116x->lock); + val = isp116x_read_reg32(isp116x, HCCONTROL); + isp116x_write_reg32(isp116x, HCCONTROL, + (val & ~HCCONTROL_HCFS) | HCCONTROL_USB_OPER); + spin_unlock_irq(&isp116x->lock); + /* see analogous comment above */ + hcd->self.root_hub->dev.power.power_state = PMSG_ON; + hcd->state = HC_STATE_RUNNING; + + return 0; +} + +static void isp116x_rh_resume(void *_hcd) +{ + struct usb_hcd *hcd = _hcd; + + usb_resume_device(hcd->self.root_hub); +} + +#else + +#define isp116x_hub_suspend NULL +#define isp116x_hub_resume NULL + +static void isp116x_rh_resume(void *_hcd) +{ +} + +#endif + +/*-----------------------------------------------------------------*/ + +#ifdef STUB_DEBUG_FILE + +static inline void create_debug_file(struct isp116x *isp116x) +{ +} + +static inline void remove_debug_file(struct isp116x *isp116x) +{ +} + +#else + +#include <linux/proc_fs.h> +#include <linux/seq_file.h> + +static void dump_irq(struct seq_file *s, char *label, u16 mask) +{ + seq_printf(s, "%s %04x%s%s%s%s%s%s\n", label, mask, + mask & HCuPINT_CLKRDY ? " clkrdy" : "", + mask & HCuPINT_SUSP ? " susp" : "", + mask & HCuPINT_OPR ? " opr" : "", + mask & HCuPINT_AIIEOT ? " eot" : "", + mask & HCuPINT_ATL ? " atl" : "", + mask & HCuPINT_SOF ? " sof" : ""); +} + +static void dump_int(struct seq_file *s, char *label, u32 mask) +{ + seq_printf(s, "%s %08x%s%s%s%s%s%s%s\n", label, mask, + mask & HCINT_MIE ? " MIE" : "", + mask & HCINT_RHSC ? " rhsc" : "", + mask & HCINT_FNO ? " fno" : "", + mask & HCINT_UE ? " ue" : "", + mask & HCINT_RD ? " rd" : "", + mask & HCINT_SF ? " sof" : "", mask & HCINT_SO ? " so" : ""); +} + +static int proc_isp116x_show(struct seq_file *s, void *unused) +{ + struct isp116x *isp116x = s->private; + struct isp116x_ep *ep; + struct urb *urb; + unsigned i; + char *str; + + seq_printf(s, "%s\n%s version %s\n", + isp116x_to_hcd(isp116x)->product_desc, hcd_name, + DRIVER_VERSION); + + if (HC_IS_SUSPENDED(isp116x_to_hcd(isp116x)->state)) { + seq_printf(s, "HCD is suspended\n"); + return 0; + } + if (!HC_IS_RUNNING(isp116x_to_hcd(isp116x)->state)) { + seq_printf(s, "HCD not running\n"); + return 0; + } + + spin_lock_irq(&isp116x->lock); + + dump_irq(s, "hc_irq_enable", isp116x_read_reg16(isp116x, HCuPINTENB)); + dump_irq(s, "hc_irq_status", isp116x_read_reg16(isp116x, HCuPINT)); + dump_int(s, "hc_int_enable", isp116x_read_reg32(isp116x, HCINTENB)); + dump_int(s, "hc_int_status", isp116x_read_reg32(isp116x, HCINTSTAT)); + + list_for_each_entry(ep, &isp116x->async, schedule) { + + switch (ep->nextpid) { + case USB_PID_IN: + str = "in"; + break; + case USB_PID_OUT: + str = "out"; + break; + case USB_PID_SETUP: + str = "setup"; + break; + case USB_PID_ACK: + str = "status"; + break; + default: + str = "?"; + break; + }; + seq_printf(s, "%p, ep%d%s, maxpacket %d:\n", ep, + ep->epnum, str, ep->maxpacket); + list_for_each_entry(urb, &ep->hep->urb_list, urb_list) { + seq_printf(s, " urb%p, %d/%d\n", urb, + urb->actual_length, + urb->transfer_buffer_length); + } + } + if (!list_empty(&isp116x->async)) + seq_printf(s, "\n"); + + seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE); + + for (i = 0; i < PERIODIC_SIZE; i++) { + ep = isp116x->periodic[i]; + if (!ep) + continue; + seq_printf(s, "%2d [%3d]:\n", i, isp116x->load[i]); + + /* DUMB: prints shared entries multiple times */ + do { + seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n", + ep->period, ep, + (ep->udev->speed == + USB_SPEED_FULL) ? "" : "ls ", + ep->udev->devnum, ep->epnum, + (ep->epnum == + 0) ? "" : ((ep->nextpid == + USB_PID_IN) ? "in" : "out"), + ep->maxpacket); + ep = ep->next; + } while (ep); + } + spin_unlock_irq(&isp116x->lock); + seq_printf(s, "\n"); + + return 0; +} + +static int proc_isp116x_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_isp116x_show, PDE(inode)->data); +} + +static struct file_operations proc_ops = { + .open = proc_isp116x_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* expect just one isp116x per system */ +static const char proc_filename[] = "driver/isp116x"; + +static void create_debug_file(struct isp116x *isp116x) +{ + struct proc_dir_entry *pde; + + pde = create_proc_entry(proc_filename, 0, NULL); + if (pde == NULL) + return; + + pde->proc_fops = &proc_ops; + pde->data = isp116x; + isp116x->pde = pde; +} + +static void remove_debug_file(struct isp116x *isp116x) +{ + if (isp116x->pde) + remove_proc_entry(proc_filename, NULL); +} + +#endif + +/*-----------------------------------------------------------------*/ + +/* + Software reset - can be called from any contect. +*/ +static int isp116x_sw_reset(struct isp116x *isp116x) +{ + int retries = 15; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&isp116x->lock, flags); + isp116x_write_reg16(isp116x, HCSWRES, HCSWRES_MAGIC); + isp116x_write_reg32(isp116x, HCCMDSTAT, HCCMDSTAT_HCR); + while (--retries) { + /* It usually resets within 1 ms */ + mdelay(1); + if (!(isp116x_read_reg32(isp116x, HCCMDSTAT) & HCCMDSTAT_HCR)) + break; + } + if (!retries) { + ERR("Software reset timeout\n"); + ret = -ETIME; + } + spin_unlock_irqrestore(&isp116x->lock, flags); + return ret; +} + +/* + Reset. Tries to perform platform-specific hardware + reset first; falls back to software reset. +*/ +static int isp116x_reset(struct usb_hcd *hcd) +{ + struct isp116x *isp116x = hcd_to_isp116x(hcd); + unsigned long t; + u16 clkrdy = 0; + int ret = 0, timeout = 15 /* ms */ ; + + if (isp116x->board && isp116x->board->reset) { + /* Hardware reset */ + isp116x->board->reset(hcd->self.controller, 1); + msleep(10); + if (isp116x->board->clock) + isp116x->board->clock(hcd->self.controller, 1); + msleep(1); + isp116x->board->reset(hcd->self.controller, 0); + } else + ret = isp116x_sw_reset(isp116x); + + if (ret) + return ret; + + t = jiffies + msecs_to_jiffies(timeout); + while (time_before_eq(jiffies, t)) { + msleep(4); + spin_lock_irq(&isp116x->lock); + clkrdy = isp116x_read_reg16(isp116x, HCuPINT) & HCuPINT_CLKRDY; + spin_unlock_irq(&isp116x->lock); + if (clkrdy) + break; + } + if (!clkrdy) { + ERR("Clock not ready after 20ms\n"); + /* After sw_reset the clock won't report to be ready, if + H_WAKEUP pin is high. */ + if (!isp116x->board || !isp116x->board->reset) + ERR("The driver does not support hardware wakeup.\n"); + ERR("Please make sure that the H_WAKEUP pin " + "is pulled low!\n"); + ret = -ENODEV; + } + return ret; +} + +static void isp116x_stop(struct usb_hcd *hcd) +{ + struct isp116x *isp116x = hcd_to_isp116x(hcd); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&isp116x->lock, flags); + isp116x_write_reg16(isp116x, HCuPINTENB, 0); + + /* Switch off ports' power, some devices don't come up + after next 'insmod' without this */ + val = isp116x_read_reg32(isp116x, HCRHDESCA); + val &= ~(RH_A_NPS | RH_A_PSM); + isp116x_write_reg32(isp116x, HCRHDESCA, val); + isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS); + spin_unlock_irqrestore(&isp116x->lock, flags); + + /* Put the chip into reset state */ + if (isp116x->board && isp116x->board->reset) + isp116x->board->reset(hcd->self.controller, 0); + else + isp116x_sw_reset(isp116x); + + /* Stop the clock */ + if (isp116x->board && isp116x->board->clock) + isp116x->board->clock(hcd->self.controller, 0); +} + +/* + Configure the chip. The chip must be successfully reset by now. +*/ +static int isp116x_start(struct usb_hcd *hcd) +{ + struct isp116x *isp116x = hcd_to_isp116x(hcd); + struct isp116x_platform_data *board = isp116x->board; + u32 val; + unsigned long flags; + + spin_lock_irqsave(&isp116x->lock, flags); + + /* clear interrupt status and disable all interrupt sources */ + isp116x_write_reg16(isp116x, HCuPINT, 0xff); + isp116x_write_reg16(isp116x, HCuPINTENB, 0); + + val = isp116x_read_reg16(isp116x, HCCHIPID); + if ((val & HCCHIPID_MASK) != HCCHIPID_MAGIC) { + ERR("Invalid chip ID %04x\n", val); + spin_unlock_irqrestore(&isp116x->lock, flags); + return -ENODEV; + } + + isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE); + isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE); + + /* ----- HW conf */ + val = HCHWCFG_INT_ENABLE | HCHWCFG_DBWIDTH(1); + if (board->sel15Kres) + val |= HCHWCFG_15KRSEL; + /* Remote wakeup won't work without working clock */ + if (board->clknotstop || board->remote_wakeup_enable) + val |= HCHWCFG_CLKNOTSTOP; + if (board->oc_enable) + val |= HCHWCFG_ANALOG_OC; + if (board->int_act_high) + val |= HCHWCFG_INT_POL; + if (board->int_edge_triggered) + val |= HCHWCFG_INT_TRIGGER; + isp116x_write_reg16(isp116x, HCHWCFG, val); + + /* ----- Root hub conf */ + val = 0; + /* AN10003_1.pdf recommends NPS to be always 1 */ + if (board->no_power_switching) + val |= RH_A_NPS; + if (board->power_switching_mode) + val |= RH_A_PSM; + if (board->potpg) + val |= (board->potpg << 24) & RH_A_POTPGT; + else + val |= (25 << 24) & RH_A_POTPGT; + isp116x_write_reg32(isp116x, HCRHDESCA, val); + isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA); + + val = RH_B_PPCM; + isp116x_write_reg32(isp116x, HCRHDESCB, val); + isp116x->rhdescb = isp116x_read_reg32(isp116x, HCRHDESCB); + + val = 0; + if (board->remote_wakeup_enable) { + hcd->can_wakeup = 1; + val |= RH_HS_DRWE; + } + isp116x_write_reg32(isp116x, HCRHSTATUS, val); + isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS); + + isp116x_write_reg32(isp116x, HCFMINTVL, 0x27782edf); + + hcd->state = HC_STATE_RUNNING; + + /* Set up interrupts */ + isp116x->intenb = HCINT_MIE | HCINT_RHSC | HCINT_UE; + if (board->remote_wakeup_enable) + isp116x->intenb |= HCINT_RD; + isp116x->irqenb = HCuPINT_ATL | HCuPINT_OPR; /* | HCuPINT_SUSP; */ + isp116x_write_reg32(isp116x, HCINTENB, isp116x->intenb); + isp116x_write_reg16(isp116x, HCuPINTENB, isp116x->irqenb); + + /* Go operational */ + val = HCCONTROL_USB_OPER; + /* Remote wakeup connected - NOT SUPPORTED */ + /* if (board->remote_wakeup_connected) + val |= HCCONTROL_RWC; */ + if (board->remote_wakeup_enable) + val |= HCCONTROL_RWE; + isp116x_write_reg32(isp116x, HCCONTROL, val); + + /* Disable ports to avoid race in device enumeration */ + isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS); + isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS); + + isp116x_show_regs(isp116x); + spin_unlock_irqrestore(&isp116x->lock, flags); + return 0; +} + +/*-----------------------------------------------------------------*/ + +static struct hc_driver isp116x_hc_driver = { + .description = hcd_name, + .product_desc = "ISP116x Host Controller", + .hcd_priv_size = sizeof(struct isp116x), + + .irq = isp116x_irq, + .flags = HCD_USB11, + + .reset = isp116x_reset, + .start = isp116x_start, + .stop = isp116x_stop, + + .urb_enqueue = isp116x_urb_enqueue, + .urb_dequeue = isp116x_urb_dequeue, + .endpoint_disable = isp116x_endpoint_disable, + + .get_frame_number = isp116x_get_frame, + + .hub_status_data = isp116x_hub_status_data, + .hub_control = isp116x_hub_control, + .hub_suspend = isp116x_hub_suspend, + .hub_resume = isp116x_hub_resume, +}; + +/*----------------------------------------------------------------*/ + +static int __init_or_module isp116x_remove(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct isp116x *isp116x; + struct platform_device *pdev; + struct resource *res; + + if(!hcd) + return 0; + isp116x = hcd_to_isp116x(hcd); + pdev = container_of(dev, struct platform_device, dev); + remove_debug_file(isp116x); + usb_remove_hcd(hcd); + + iounmap(isp116x->data_reg); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + release_mem_region(res->start, 2); + iounmap(isp116x->addr_reg); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, 2); + + usb_put_hcd(hcd); + return 0; +} + +#define resource_len(r) (((r)->end - (r)->start) + 1) + +static int __init isp116x_probe(struct device *dev) +{ + struct usb_hcd *hcd; + struct isp116x *isp116x; + struct platform_device *pdev; + struct resource *addr, *data; + void __iomem *addr_reg; + void __iomem *data_reg; + int irq; + int ret = 0; + + pdev = container_of(dev, struct platform_device, dev); + if (pdev->num_resources < 3) { + ret = -ENODEV; + goto err1; + } + + data = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = platform_get_resource(pdev, IORESOURCE_MEM, 1); + irq = platform_get_irq(pdev, 0); + if (!addr || !data || irq < 0) { + ret = -ENODEV; + goto err1; + } + + if (dev->dma_mask) { + DBG("DMA not supported\n"); + ret = -EINVAL; + goto err1; + } + + if (!request_mem_region(addr->start, 2, hcd_name)) { + ret = -EBUSY; + goto err1; + } + addr_reg = ioremap(addr->start, resource_len(addr)); + if (addr_reg == NULL) { + ret = -ENOMEM; + goto err2; + } + if (!request_mem_region(data->start, 2, hcd_name)) { + ret = -EBUSY; + goto err3; + } + data_reg = ioremap(data->start, resource_len(data)); + if (data_reg == NULL) { + ret = -ENOMEM; + goto err4; + } + + /* allocate and initialize hcd */ + hcd = usb_create_hcd(&isp116x_hc_driver, dev, dev->bus_id); + if (!hcd) { + ret = -ENOMEM; + goto err5; + } + /* this rsrc_start is bogus */ + hcd->rsrc_start = addr->start; + isp116x = hcd_to_isp116x(hcd); + isp116x->data_reg = data_reg; + isp116x->addr_reg = addr_reg; + spin_lock_init(&isp116x->lock); + INIT_LIST_HEAD(&isp116x->async); + INIT_WORK(&isp116x->rh_resume, isp116x_rh_resume, hcd); + isp116x->board = dev->platform_data; + + if (!isp116x->board) { + ERR("Platform data structure not initialized\n"); + ret = -ENODEV; + goto err6; + } + if (isp116x_check_platform_delay(isp116x)) { + ERR("USE_PLATFORM_DELAY defined, but delay function not " + "implemented.\n"); + ERR("See comments in drivers/usb/host/isp116x-hcd.c\n"); + ret = -ENODEV; + goto err6; + } + + ret = usb_add_hcd(hcd, irq, SA_INTERRUPT); + if (ret != 0) + goto err6; + + create_debug_file(isp116x); + return 0; + + err6: + usb_put_hcd(hcd); + err5: + iounmap(data_reg); + err4: + release_mem_region(data->start, 2); + err3: + iounmap(addr_reg); + err2: + release_mem_region(addr->start, 2); + err1: + ERR("init error, %d\n", ret); + return ret; +} + +#ifdef CONFIG_PM +/* + Suspend of platform device +*/ +static int isp116x_suspend(struct device *dev, pm_message_t state, u32 phase) +{ + int ret = 0; + struct usb_hcd *hcd = dev_get_drvdata(dev); + + VDBG("%s: state %x, phase %x\n", __func__, state, phase); + + if (phase != SUSPEND_DISABLE && phase != SUSPEND_POWER_DOWN) + return 0; + + ret = usb_suspend_device(hcd->self.root_hub, state); + if (!ret) { + dev->power.power_state = state; + INFO("%s suspended\n", (char *)hcd_name); + } else + ERR("%s suspend failed\n", (char *)hcd_name); + + return ret; +} + +/* + Resume platform device +*/ +static int isp116x_resume(struct device *dev, u32 phase) +{ + int ret = 0; + struct usb_hcd *hcd = dev_get_drvdata(dev); + + VDBG("%s: state %x, phase %x\n", __func__, dev->power.power_state, + phase); + if (phase != RESUME_POWER_ON) + return 0; + + ret = usb_resume_device(hcd->self.root_hub); + if (!ret) { + dev->power.power_state = PMSG_ON; + VDBG("%s resumed\n", (char *)hcd_name); + } + return ret; +} + +#else + +#define isp116x_suspend NULL +#define isp116x_resume NULL + +#endif + +static struct device_driver isp116x_driver = { + .name = (char *)hcd_name, + .bus = &platform_bus_type, + .probe = isp116x_probe, + .remove = isp116x_remove, + .suspend = isp116x_suspend, + .resume = isp116x_resume, +}; + +/*-----------------------------------------------------------------*/ + +static int __init isp116x_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + INFO("driver %s, %s\n", hcd_name, DRIVER_VERSION); + return driver_register(&isp116x_driver); +} + +module_init(isp116x_init); + +static void __exit isp116x_cleanup(void) +{ + driver_unregister(&isp116x_driver); +} + +module_exit(isp116x_cleanup); diff --git a/drivers/usb/host/isp116x.h b/drivers/usb/host/isp116x.h new file mode 100644 index 0000000..5887347 --- /dev/null +++ b/drivers/usb/host/isp116x.h @@ -0,0 +1,583 @@ +/* + * ISP116x register declarations and HCD data structures + * + * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee> + * Portions: + * Copyright (C) 2004 Lothar Wassmann + * Copyright (C) 2004 Psion Teklogix + * Copyright (C) 2004 David Brownell + */ + +/* us of 1ms frame */ +#define MAX_LOAD_LIMIT 850 + +/* Full speed: max # of bytes to transfer for a single urb + at a time must be < 1024 && must be multiple of 64. + 832 allows transfering 4kiB within 5 frames. */ +#define MAX_TRANSFER_SIZE_FULLSPEED 832 + +/* Low speed: there is no reason to schedule in very big + chunks; often the requested long transfers are for + string descriptors containing short strings. */ +#define MAX_TRANSFER_SIZE_LOWSPEED 64 + +/* Bytetime (us), a rough indication of how much time it + would take to transfer a byte of useful data over USB */ +#define BYTE_TIME_FULLSPEED 1 +#define BYTE_TIME_LOWSPEED 20 + +/* Buffer sizes */ +#define ISP116x_BUF_SIZE 4096 +#define ISP116x_ITL_BUFSIZE 0 +#define ISP116x_ATL_BUFSIZE ((ISP116x_BUF_SIZE) - 2*(ISP116x_ITL_BUFSIZE)) + +#define ISP116x_WRITE_OFFSET 0x80 + +/*------------ ISP116x registers/bits ------------*/ +#define HCREVISION 0x00 +#define HCCONTROL 0x01 +#define HCCONTROL_HCFS (3 << 6) /* host controller + functional state */ +#define HCCONTROL_USB_RESET (0 << 6) +#define HCCONTROL_USB_RESUME (1 << 6) +#define HCCONTROL_USB_OPER (2 << 6) +#define HCCONTROL_USB_SUSPEND (3 << 6) +#define HCCONTROL_RWC (1 << 9) /* remote wakeup connected */ +#define HCCONTROL_RWE (1 << 10) /* remote wakeup enable */ +#define HCCMDSTAT 0x02 +#define HCCMDSTAT_HCR (1 << 0) /* host controller reset */ +#define HCCMDSTAT_SOC (3 << 16) /* scheduling overrun count */ +#define HCINTSTAT 0x03 +#define HCINT_SO (1 << 0) /* scheduling overrun */ +#define HCINT_WDH (1 << 1) /* writeback of done_head */ +#define HCINT_SF (1 << 2) /* start frame */ +#define HCINT_RD (1 << 3) /* resume detect */ +#define HCINT_UE (1 << 4) /* unrecoverable error */ +#define HCINT_FNO (1 << 5) /* frame number overflow */ +#define HCINT_RHSC (1 << 6) /* root hub status change */ +#define HCINT_OC (1 << 30) /* ownership change */ +#define HCINT_MIE (1 << 31) /* master interrupt enable */ +#define HCINTENB 0x04 +#define HCINTDIS 0x05 +#define HCFMINTVL 0x0d +#define HCFMREM 0x0e +#define HCFMNUM 0x0f +#define HCLSTHRESH 0x11 +#define HCRHDESCA 0x12 +#define RH_A_NDP (0x3 << 0) /* # downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* overcurrent protection + mode */ +#define RH_A_NOCP (1 << 12) /* no overcurrent protection */ +#define RH_A_POTPGT (0xff << 24) /* power on -> power good + time */ +#define HCRHDESCB 0x13 +#define RH_B_DR (0xffff << 0) /* device removable flags */ +#define RH_B_PPCM (0xffff << 16) /* port power control mask */ +#define HCRHSTATUS 0x14 +#define RH_HS_LPS (1 << 0) /* local power status */ +#define RH_HS_OCI (1 << 1) /* over current indicator */ +#define RH_HS_DRWE (1 << 15) /* device remote wakeup + enable */ +#define RH_HS_LPSC (1 << 16) /* local power status change */ +#define RH_HS_OCIC (1 << 17) /* over current indicator + change */ +#define RH_HS_CRWE (1 << 31) /* clear remote wakeup + enable */ +#define HCRHPORT1 0x15 +#define RH_PS_CCS (1 << 0) /* current connect status */ +#define RH_PS_PES (1 << 1) /* port enable status */ +#define RH_PS_PSS (1 << 2) /* port suspend status */ +#define RH_PS_POCI (1 << 3) /* port over current + indicator */ +#define RH_PS_PRS (1 << 4) /* port reset status */ +#define RH_PS_PPS (1 << 8) /* port power status */ +#define RH_PS_LSDA (1 << 9) /* low speed device attached */ +#define RH_PS_CSC (1 << 16) /* connect status change */ +#define RH_PS_PESC (1 << 17) /* port enable status change */ +#define RH_PS_PSSC (1 << 18) /* port suspend status + change */ +#define RH_PS_OCIC (1 << 19) /* over current indicator + change */ +#define RH_PS_PRSC (1 << 20) /* port reset status change */ +#define HCRHPORT_CLRMASK (0x1f << 16) +#define HCRHPORT2 0x16 +#define HCHWCFG 0x20 +#define HCHWCFG_15KRSEL (1 << 12) +#define HCHWCFG_CLKNOTSTOP (1 << 11) +#define HCHWCFG_ANALOG_OC (1 << 10) +#define HCHWCFG_DACK_MODE (1 << 8) +#define HCHWCFG_EOT_POL (1 << 7) +#define HCHWCFG_DACK_POL (1 << 6) +#define HCHWCFG_DREQ_POL (1 << 5) +#define HCHWCFG_DBWIDTH_MASK (0x03 << 3) +#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK) +#define HCHWCFG_INT_POL (1 << 2) +#define HCHWCFG_INT_TRIGGER (1 << 1) +#define HCHWCFG_INT_ENABLE (1 << 0) +#define HCDMACFG 0x21 +#define HCDMACFG_BURST_LEN_MASK (0x03 << 5) +#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK) +#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0) +#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1) +#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2) +#define HCDMACFG_DMA_ENABLE (1 << 4) +#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1) +#define HCDMACFG_CTR_SEL (1 << 2) +#define HCDMACFG_ITLATL_SEL (1 << 1) +#define HCDMACFG_DMA_RW_SELECT (1 << 0) +#define HCXFERCTR 0x22 +#define HCuPINT 0x24 +#define HCuPINT_SOF (1 << 0) +#define HCuPINT_ATL (1 << 1) +#define HCuPINT_AIIEOT (1 << 2) +#define HCuPINT_OPR (1 << 4) +#define HCuPINT_SUSP (1 << 5) +#define HCuPINT_CLKRDY (1 << 6) +#define HCuPINTENB 0x25 +#define HCCHIPID 0x27 +#define HCCHIPID_MASK 0xff00 +#define HCCHIPID_MAGIC 0x6100 +#define HCSCRATCH 0x28 +#define HCSWRES 0x29 +#define HCSWRES_MAGIC 0x00f6 +#define HCITLBUFLEN 0x2a +#define HCATLBUFLEN 0x2b +#define HCBUFSTAT 0x2c +#define HCBUFSTAT_ITL0_FULL (1 << 0) +#define HCBUFSTAT_ITL1_FULL (1 << 1) +#define HCBUFSTAT_ATL_FULL (1 << 2) +#define HCBUFSTAT_ITL0_DONE (1 << 3) +#define HCBUFSTAT_ITL1_DONE (1 << 4) +#define HCBUFSTAT_ATL_DONE (1 << 5) +#define HCRDITL0LEN 0x2d +#define HCRDITL1LEN 0x2e +#define HCITLPORT 0x40 +#define HCATLPORT 0x41 + +/* Philips transfer descriptor */ +struct ptd { + u16 count; +#define PTD_COUNT_MSK (0x3ff << 0) +#define PTD_TOGGLE_MSK (1 << 10) +#define PTD_ACTIVE_MSK (1 << 11) +#define PTD_CC_MSK (0xf << 12) + u16 mps; +#define PTD_MPS_MSK (0x3ff << 0) +#define PTD_SPD_MSK (1 << 10) +#define PTD_LAST_MSK (1 << 11) +#define PTD_EP_MSK (0xf << 12) + u16 len; +#define PTD_LEN_MSK (0x3ff << 0) +#define PTD_DIR_MSK (3 << 10) +#define PTD_DIR_SETUP (0) +#define PTD_DIR_OUT (1) +#define PTD_DIR_IN (2) +#define PTD_B5_5_MSK (1 << 13) + u16 faddr; +#define PTD_FA_MSK (0x7f << 0) +#define PTD_FMT_MSK (1 << 7) +} __attribute__ ((packed, aligned(2))); + +/* PTD accessor macros. */ +#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0) +#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK) +#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10) +#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK) +#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11) +#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK) +#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12) +#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK) +#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0) +#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK) +#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10) +#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK) +#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11) +#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK) +#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12) +#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK) +#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0) +#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK) +#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10) +#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK) +#define PTD_GET_B5_5(p) (((p)->len & PTD_B5_5_MSK) >> 13) +#define PTD_B5_5(v) (((v) << 13) & PTD_B5_5_MSK) +#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0) +#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK) +#define PTD_GET_FMT(p) (((p)->faddr & PTD_FMT_MSK) >> 7) +#define PTD_FMT(v) (((v) << 7) & PTD_FMT_MSK) + +/* Hardware transfer status codes -- CC from ptd->count */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 + /* 0x0A, 0x0B reserved for hardware */ +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D + /* 0x0E, 0x0F reserved for HCD */ +#define TD_NOTACCESSED 0x0F + +/* map PTD status codes (CC) to errno values */ +static const int cc_to_error[16] = { + /* No Error */ 0, + /* CRC Error */ -EILSEQ, + /* Bit Stuff */ -EPROTO, + /* Data Togg */ -EILSEQ, + /* Stall */ -EPIPE, + /* DevNotResp */ -ETIMEDOUT, + /* PIDCheck */ -EPROTO, + /* UnExpPID */ -EPROTO, + /* DataOver */ -EOVERFLOW, + /* DataUnder */ -EREMOTEIO, + /* (for hw) */ -EIO, + /* (for hw) */ -EIO, + /* BufferOver */ -ECOMM, + /* BuffUnder */ -ENOSR, + /* (for HCD) */ -EALREADY, + /* (for HCD) */ -EALREADY +}; + +/*--------------------------------------------------------------*/ + +#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */ +#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE) + +struct isp116x { + spinlock_t lock; + struct work_struct rh_resume; + + void __iomem *addr_reg; + void __iomem *data_reg; + + struct isp116x_platform_data *board; + + struct proc_dir_entry *pde; + unsigned long stat1, stat2, stat4, stat8, stat16; + + /* HC registers */ + u32 intenb; /* "OHCI" interrupts */ + u16 irqenb; /* uP interrupts */ + + /* Root hub registers */ + u32 rhdesca; + u32 rhdescb; + u32 rhstatus; + u32 rhport[2]; + + /* async schedule: control, bulk */ + struct list_head async; + + /* periodic schedule: int */ + u16 load[PERIODIC_SIZE]; + struct isp116x_ep *periodic[PERIODIC_SIZE]; + unsigned periodic_count; + u16 fmindex; + + /* Schedule for the current frame */ + struct isp116x_ep *atl_active; + int atl_buflen; + int atl_bufshrt; + int atl_last_dir; + atomic_t atl_finishing; +}; + +static inline struct isp116x *hcd_to_isp116x(struct usb_hcd *hcd) +{ + return (struct isp116x *)(hcd->hcd_priv); +} + +static inline struct usb_hcd *isp116x_to_hcd(struct isp116x *isp116x) +{ + return container_of((void *)isp116x, struct usb_hcd, hcd_priv); +} + +struct isp116x_ep { + struct usb_host_endpoint *hep; + struct usb_device *udev; + struct ptd ptd; + + u8 maxpacket; + u8 epnum; + u8 nextpid; + u16 error_count; + u16 length; /* of current packet */ + unsigned char *data; /* to databuf */ + /* queue of active EP's (the ones scheduled for the + current frame) */ + struct isp116x_ep *active; + + /* periodic schedule */ + u16 period; + u16 branch; + u16 load; + struct isp116x_ep *next; + + /* async schedule */ + struct list_head schedule; +}; + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +#define DBG(stuff...) printk(KERN_DEBUG "116x: " stuff) +#else +#define DBG(stuff...) do{}while(0) +#endif + +#ifdef VERBOSE +# define VDBG DBG +#else +# define VDBG(stuff...) do{}while(0) +#endif + +#define ERR(stuff...) printk(KERN_ERR "116x: " stuff) +#define WARN(stuff...) printk(KERN_WARNING "116x: " stuff) +#define INFO(stuff...) printk(KERN_INFO "116x: " stuff) + +/* ------------------------------------------------- */ + +#if defined(USE_PLATFORM_DELAY) +#if defined(USE_NDELAY) +#error USE_PLATFORM_DELAY and USE_NDELAY simultaneously defined. +#endif +#define isp116x_delay(h,d) (h)->board->delay( \ + isp116x_to_hcd(h)->self.controller,d) +#define isp116x_check_platform_delay(h) ((h)->board->delay == NULL) +#elif defined(USE_NDELAY) +#define isp116x_delay(h,d) ndelay(d) +#define isp116x_check_platform_delay(h) 0 +#else +#define isp116x_delay(h,d) do{}while(0) +#define isp116x_check_platform_delay(h) 0 +#endif + +#if defined(DEBUG) +#define IRQ_TEST() BUG_ON(!irqs_disabled()) +#else +#define IRQ_TEST() do{}while(0) +#endif + +static inline void isp116x_write_addr(struct isp116x *isp116x, unsigned reg) +{ + IRQ_TEST(); + writew(reg & 0xff, isp116x->addr_reg); + isp116x_delay(isp116x, 300); +} + +static inline void isp116x_write_data16(struct isp116x *isp116x, u16 val) +{ + writew(val, isp116x->data_reg); + isp116x_delay(isp116x, 150); +} + +static inline void isp116x_raw_write_data16(struct isp116x *isp116x, u16 val) +{ + __raw_writew(val, isp116x->data_reg); + isp116x_delay(isp116x, 150); +} + +static inline u16 isp116x_read_data16(struct isp116x *isp116x) +{ + u16 val; + + val = readw(isp116x->data_reg); + isp116x_delay(isp116x, 150); + return val; +} + +static inline u16 isp116x_raw_read_data16(struct isp116x *isp116x) +{ + u16 val; + + val = __raw_readw(isp116x->data_reg); + isp116x_delay(isp116x, 150); + return val; +} + +static inline void isp116x_write_data32(struct isp116x *isp116x, u32 val) +{ + writew(val & 0xffff, isp116x->data_reg); + isp116x_delay(isp116x, 150); + writew(val >> 16, isp116x->data_reg); + isp116x_delay(isp116x, 150); +} + +static inline u32 isp116x_read_data32(struct isp116x *isp116x) +{ + u32 val; + + val = (u32) readw(isp116x->data_reg); + isp116x_delay(isp116x, 150); + val |= ((u32) readw(isp116x->data_reg)) << 16; + isp116x_delay(isp116x, 150); + return val; +} + +/* Let's keep register access functions out of line. Hint: + we wait at least 150 ns at every access. +*/ +static u16 isp116x_read_reg16(struct isp116x *isp116x, unsigned reg) +{ + isp116x_write_addr(isp116x, reg); + return isp116x_read_data16(isp116x); +} + +static u32 isp116x_read_reg32(struct isp116x *isp116x, unsigned reg) +{ + isp116x_write_addr(isp116x, reg); + return isp116x_read_data32(isp116x); +} + +static void isp116x_write_reg16(struct isp116x *isp116x, unsigned reg, + unsigned val) +{ + isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET); + isp116x_write_data16(isp116x, (u16) (val & 0xffff)); +} + +static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg, + unsigned val) +{ + isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET); + isp116x_write_data32(isp116x, (u32) val); +} + +#define isp116x_show_reg(d,r) { \ + if ((r) < 0x20) { \ + DBG("%-12s[%02x]: %08x\n", #r, \ + r, isp116x_read_reg32(d, r)); \ + } else { \ + DBG("%-12s[%02x]: %04x\n", #r, \ + r, isp116x_read_reg16(d, r)); \ + } \ +} + +static inline void isp116x_show_regs(struct isp116x *isp116x) +{ + isp116x_show_reg(isp116x, HCREVISION); + isp116x_show_reg(isp116x, HCCONTROL); + isp116x_show_reg(isp116x, HCCMDSTAT); + isp116x_show_reg(isp116x, HCINTSTAT); + isp116x_show_reg(isp116x, HCINTENB); + isp116x_show_reg(isp116x, HCFMINTVL); + isp116x_show_reg(isp116x, HCFMREM); + isp116x_show_reg(isp116x, HCFMNUM); + isp116x_show_reg(isp116x, HCLSTHRESH); + isp116x_show_reg(isp116x, HCRHDESCA); + isp116x_show_reg(isp116x, HCRHDESCB); + isp116x_show_reg(isp116x, HCRHSTATUS); + isp116x_show_reg(isp116x, HCRHPORT1); + isp116x_show_reg(isp116x, HCRHPORT2); + isp116x_show_reg(isp116x, HCHWCFG); + isp116x_show_reg(isp116x, HCDMACFG); + isp116x_show_reg(isp116x, HCXFERCTR); + isp116x_show_reg(isp116x, HCuPINT); + isp116x_show_reg(isp116x, HCuPINTENB); + isp116x_show_reg(isp116x, HCCHIPID); + isp116x_show_reg(isp116x, HCSCRATCH); + isp116x_show_reg(isp116x, HCITLBUFLEN); + isp116x_show_reg(isp116x, HCATLBUFLEN); + isp116x_show_reg(isp116x, HCBUFSTAT); + isp116x_show_reg(isp116x, HCRDITL0LEN); + isp116x_show_reg(isp116x, HCRDITL1LEN); +} + +#if defined(URB_TRACE) + +#define PIPETYPE(pipe) ({ char *__s; \ + if (usb_pipecontrol(pipe)) __s = "ctrl"; \ + else if (usb_pipeint(pipe)) __s = "int"; \ + else if (usb_pipebulk(pipe)) __s = "bulk"; \ + else __s = "iso"; \ + __s;}) +#define PIPEDIR(pipe) ({ usb_pipein(pipe) ? "in" : "out"; }) +#define URB_NOTSHORT(urb) ({ (urb)->transfer_flags & URB_SHORT_NOT_OK ? \ + "short_not_ok" : ""; }) + +/* print debug info about the URB */ +static void urb_dbg(struct urb *urb, char *msg) +{ + unsigned int pipe; + + if (!urb) { + DBG("%s: zero urb\n", msg); + return; + } + pipe = urb->pipe; + DBG("%s: FA %d ep%d%s %s: len %d/%d %s\n", msg, + usb_pipedevice(pipe), usb_pipeendpoint(pipe), + PIPEDIR(pipe), PIPETYPE(pipe), + urb->transfer_buffer_length, urb->actual_length, URB_NOTSHORT(urb)); +} + +#else + +#define urb_dbg(urb,msg) do{}while(0) + +#endif /* ! defined(URB_TRACE) */ + +#if defined(PTD_TRACE) + +#define PTD_DIR_STR(ptd) ({char __c; \ + switch(PTD_GET_DIR(ptd)){ \ + case 0: __c = 's'; break; \ + case 1: __c = 'o'; break; \ + default: __c = 'i'; break; \ + }; __c;}) + +/* + Dump PTD info. The code documents the format + perfectly, right :) +*/ +static inline void dump_ptd(struct ptd *ptd) +{ + printk("td: %x %d%c%d %d,%d,%d %x %x%x%x\n", + PTD_GET_CC(ptd), PTD_GET_FA(ptd), + PTD_DIR_STR(ptd), PTD_GET_EP(ptd), + PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd), + PTD_GET_TOGGLE(ptd), PTD_GET_ACTIVE(ptd), + PTD_GET_SPD(ptd), PTD_GET_LAST(ptd)); +} + +static inline void dump_ptd_out_data(struct ptd *ptd, u8 * buf) +{ + int k; + + if (PTD_GET_DIR(ptd) != PTD_DIR_IN && PTD_GET_LEN(ptd)) { + printk("-> "); + for (k = 0; k < PTD_GET_LEN(ptd); ++k) + printk("%02x ", ((u8 *) buf)[k]); + printk("\n"); + } +} + +static inline void dump_ptd_in_data(struct ptd *ptd, u8 * buf) +{ + int k; + + if (PTD_GET_DIR(ptd) == PTD_DIR_IN && PTD_GET_COUNT(ptd)) { + printk("<- "); + for (k = 0; k < PTD_GET_COUNT(ptd); ++k) + printk("%02x ", ((u8 *) buf)[k]); + printk("\n"); + } + if (PTD_GET_LAST(ptd)) + printk("-\n"); +} + +#else + +#define dump_ptd(ptd) do{}while(0) +#define dump_ptd_in_data(ptd,buf) do{}while(0) +#define dump_ptd_out_data(ptd,buf) do{}while(0) + +#endif /* ! defined(PTD_TRACE) */ diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 1e27f10..13cd217 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -95,12 +95,11 @@ #include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> -#include <linux/interrupt.h> /* for in_interrupt () */ #include <linux/usb.h> #include <linux/usb_otg.h> -#include "../core/hcd.h" #include <linux/dma-mapping.h> -#include <linux/dmapool.h> /* needed by ohci-mem.c when no PCI */ +#include <linux/dmapool.h> +#include <linux/reboot.h> #include <asm/io.h> #include <asm/irq.h> @@ -108,8 +107,9 @@ #include <asm/unaligned.h> #include <asm/byteorder.h> +#include "../core/hcd.h" -#define DRIVER_VERSION "2004 Nov 08" +#define DRIVER_VERSION "2005 April 22" #define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell" #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver" @@ -141,6 +141,7 @@ static const char hcd_name [] = "ohci_hcd"; static void ohci_dump (struct ohci_hcd *ohci, int verbose); static int ohci_init (struct ohci_hcd *ohci); static void ohci_stop (struct usb_hcd *hcd); +static int ohci_reboot (struct notifier_block *, unsigned long , void *); #include "ohci-hub.c" #include "ohci-dbg.c" @@ -420,6 +421,23 @@ static void ohci_usb_reset (struct ohci_hcd *ohci) ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); } +/* reboot notifier forcibly disables IRQs and DMA, helping kexec and + * other cases where the next software may expect clean state from the + * "firmware". this is bus-neutral, unlike shutdown() methods. + */ +static int +ohci_reboot (struct notifier_block *block, unsigned long code, void *null) +{ + struct ohci_hcd *ohci; + + ohci = container_of (block, struct ohci_hcd, reboot_notifier); + ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); + ohci_usb_reset (ohci); + /* flush the writes */ + (void) ohci_readl (ohci, &ohci->regs->control); + return 0; +} + /*-------------------------------------------------------------------------* * HC functions *-------------------------------------------------------------------------*/ @@ -487,13 +505,10 @@ static int ohci_init (struct ohci_hcd *ohci) /* Start an OHCI controller, set the BUS operational * resets USB and controller * enable interrupts - * connect the virtual root hub */ static int ohci_run (struct ohci_hcd *ohci) { u32 mask, temp; - struct usb_device *udev; - struct usb_bus *bus; int first = ohci->fminterval == 0; disable (ohci); @@ -654,37 +669,13 @@ retry: // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((temp >> 23) & 0x1fe); - bus = &ohci_to_hcd(ohci)->self; ohci_to_hcd(ohci)->state = HC_STATE_RUNNING; ohci_dump (ohci, 1); - udev = bus->root_hub; - if (udev) { - return 0; - } - - /* connect the virtual root hub */ - udev = usb_alloc_dev (NULL, bus, 0); - if (!udev) { - disable (ohci); - ohci->hc_control &= ~OHCI_CTRL_HCFS; - ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); - return -ENOMEM; - } - - udev->speed = USB_SPEED_FULL; - if (usb_hcd_register_root_hub (udev, ohci_to_hcd(ohci)) != 0) { - usb_put_dev (udev); - disable (ohci); - ohci->hc_control &= ~OHCI_CTRL_HCFS; - ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); - return -ENODEV; - } - if (ohci->power_budget) - hub_set_power_budget(udev, ohci->power_budget); + if (ohci_to_hcd(ohci)->self.root_hub == NULL) + create_debug_files (ohci); - create_debug_files (ohci); return 0; } @@ -781,6 +772,7 @@ static void ohci_stop (struct usb_hcd *hcd) ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); remove_debug_files (ohci); + unregister_reboot_notifier (&ohci->reboot_notifier); ohci_mem_cleanup (ohci); if (ohci->hcca) { dma_free_coherent (hcd->self.controller, diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c index e55682b..23735a3 100644 --- a/drivers/usb/host/ohci-mem.c +++ b/drivers/usb/host/ohci-mem.c @@ -29,6 +29,7 @@ static void ohci_hcd_init (struct ohci_hcd *ohci) spin_lock_init (&ohci->lock); INIT_LIST_HEAD (&ohci->pending); INIT_WORK (&ohci->rh_resume, ohci_rh_resume, ohci_to_hcd(ohci)); + ohci->reboot_notifier.notifier_call = ohci_reboot; } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 8aab590..b62d699 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -181,7 +181,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev) if (config->otg) { ohci_to_hcd(ohci)->self.otg_port = config->otg; /* default/minimum OTG power budget: 8 mA */ - ohci->power_budget = 8; + ohci_to_hcd(ohci)->power_budget = 8; } /* boards can use OTG transceivers in non-OTG modes */ @@ -230,7 +230,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev) /* TPS2045 switch for internal transceiver (port 1) */ if (machine_is_omap_osk()) { - ohci->power_budget = 250; + ohci_to_hcd(ohci)->power_budget = 250; rh &= ~RH_A_NOCP; diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 57fd07d..eede6be 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -14,14 +14,11 @@ * This file is licenced under the GPL. */ -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PPC_PMAC #include <asm/machdep.h> #include <asm/pmac_feature.h> #include <asm/pci-bridge.h> #include <asm/prom.h> -#ifndef CONFIG_PM -# define CONFIG_PM -#endif #endif #ifndef CONFIG_PCI @@ -132,7 +129,7 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) /* let things settle down a bit */ msleep (100); -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PPC_PMAC if (_machine == _MACH_Pmac) { struct device_node *of_node; @@ -141,7 +138,7 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) if (of_node) pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); } -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PPC_PMAC */ return 0; } @@ -151,7 +148,7 @@ static int ohci_pci_resume (struct usb_hcd *hcd) struct ohci_hcd *ohci = hcd_to_ohci (hcd); int retval = 0; -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PPC_PMAC if (_machine == _MACH_Pmac) { struct device_node *of_node; @@ -160,7 +157,7 @@ static int ohci_pci_resume (struct usb_hcd *hcd) if (of_node) pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1); } -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PPC_PMAC */ /* resume root hub */ if (time_before (jiffies, ohci->next_statechange)) diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 22e1ac1..71cdd22 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -371,7 +371,6 @@ struct ohci_hcd { * other external transceivers should be software-transparent */ struct otg_transceiver *transceiver; - unsigned power_budget; /* * memory management for queue data structures @@ -390,6 +389,7 @@ struct ohci_hcd { u32 fminterval; /* saved register */ struct work_struct rh_resume; + struct notifier_block reboot_notifier; unsigned long flags; /* for HC bugs */ #define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 99d43f7..6c3f910b 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1563,29 +1563,15 @@ static int sl811h_start(struct usb_hcd *hcd) { struct sl811 *sl811 = hcd_to_sl811(hcd); - struct usb_device *udev; /* chip has been reset, VBUS power is off */ - - udev = usb_alloc_dev(NULL, &hcd->self, 0); - if (!udev) - return -ENOMEM; - - udev->speed = USB_SPEED_FULL; hcd->state = HC_STATE_RUNNING; - if (sl811->board) + if (sl811->board) { hcd->can_wakeup = sl811->board->can_wakeup; - - if (usb_hcd_register_root_hub(udev, hcd) != 0) { - usb_put_dev(udev); - sl811h_stop(hcd); - return -ENODEV; + hcd->power_budget = sl811->board->power * 2; } - if (sl811->board && sl811->board->power) - hub_set_power_budget(udev, sl811->board->power * 2); - /* enable power and interupts */ port_power(sl811, 1); diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index 24c73c5..4538a98 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -237,6 +237,37 @@ static int uhci_show_sc(int port, unsigned short status, char *buf, int len) return out - buf; } +static int uhci_show_root_hub_state(struct uhci_hcd *uhci, char *buf, int len) +{ + char *out = buf; + char *rh_state; + + /* Try to make sure there's enough memory */ + if (len < 60) + return 0; + + switch (uhci->rh_state) { + case UHCI_RH_RESET: + rh_state = "reset"; break; + case UHCI_RH_SUSPENDED: + rh_state = "suspended"; break; + case UHCI_RH_AUTO_STOPPED: + rh_state = "auto-stopped"; break; + case UHCI_RH_RESUMING: + rh_state = "resuming"; break; + case UHCI_RH_SUSPENDING: + rh_state = "suspending"; break; + case UHCI_RH_RUNNING: + rh_state = "running"; break; + case UHCI_RH_RUNNING_NODEVS: + rh_state = "running, no devs"; break; + default: + rh_state = "?"; break; + } + out += sprintf(out, "Root-hub state: %s\n", rh_state); + return out - buf; +} + static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len) { char *out = buf; @@ -408,6 +439,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) spin_lock_irqsave(&uhci->lock, flags); + out += uhci_show_root_hub_state(uhci, out, len - (out - buf)); out += sprintf(out, "HC status\n"); out += uhci_show_status(uhci, out, len - (out - buf)); diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 49bd83e..0d5d254 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -13,18 +13,13 @@ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com). * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) - * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu + * (C) Copyright 2004-2005 Alan Stern, stern@rowland.harvard.edu * * Intel documents this fairly well, and as far as I know there * are no royalties or anything like that, but even so there are * people who decided that they want to do the same thing in a * completely different way. * - * WARNING! The USB documentation is downright evil. Most of it - * is just crap, written by a committee. You're better off ignoring - * most of it, the important stuff is: - * - the low-level protocol (fairly simple but lots of small details) - * - working around the horridness of the rest */ #include <linux/config.h> @@ -64,7 +59,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v2.2" +#define DRIVER_VERSION "v2.3" #define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, \ Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, \ Alan Stern" @@ -89,8 +84,9 @@ static char *errbuf; static kmem_cache_t *uhci_up_cachep; /* urb_priv */ +static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state); +static void wakeup_rh(struct uhci_hcd *uhci); static void uhci_get_current_frame_number(struct uhci_hcd *uhci); -static void hc_state_transitions(struct uhci_hcd *uhci); /* If a transfer is still active after this much time, turn off FSBR */ #define IDLE_TIMEOUT msecs_to_jiffies(50) @@ -101,308 +97,352 @@ static void hc_state_transitions(struct uhci_hcd *uhci); /* to make sure it doesn't hog all of the bandwidth */ #define DEPTH_INTERVAL 5 +static inline void restart_timer(struct uhci_hcd *uhci) +{ + mod_timer(&uhci->stall_timer, jiffies + msecs_to_jiffies(100)); +} + #include "uhci-hub.c" #include "uhci-debug.c" #include "uhci-q.c" -static int init_stall_timer(struct usb_hcd *hcd); - -static void stall_callback(unsigned long ptr) +/* + * Make sure the controller is completely inactive, unable to + * generate interrupts or do DMA. + */ +static void reset_hc(struct uhci_hcd *uhci) { - struct usb_hcd *hcd = (struct usb_hcd *)ptr; - struct uhci_hcd *uhci = hcd_to_uhci(hcd); - struct urb_priv *up; - unsigned long flags; + int port; - spin_lock_irqsave(&uhci->lock, flags); - uhci_scan_schedule(uhci, NULL); - - list_for_each_entry(up, &uhci->urb_list, urb_list) { - struct urb *u = up->urb; - - spin_lock(&u->lock); - - /* Check if the FSBR timed out */ - if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT)) - uhci_fsbr_timeout(uhci, u); + /* Turn off PIRQ enable and SMI enable. (This also turns off the + * BIOS's USB Legacy Support.) Turn off all the R/WC bits too. + */ + pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, + USBLEGSUP_RWC); - spin_unlock(&u->lock); - } + /* Reset the HC - this will force us to get a + * new notification of any already connected + * ports due to the virtual disconnect that it + * implies. + */ + outw(USBCMD_HCRESET, uhci->io_addr + USBCMD); + mb(); + udelay(5); + if (inw(uhci->io_addr + USBCMD) & USBCMD_HCRESET) + dev_warn(uhci_dev(uhci), "HCRESET not completed yet!\n"); - /* Really disable FSBR */ - if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) { - uhci->fsbrtimeout = 0; - uhci->skel_term_qh->link = UHCI_PTR_TERM; - } + /* Just to be safe, disable interrupt requests and + * make sure the controller is stopped. + */ + outw(0, uhci->io_addr + USBINTR); + outw(0, uhci->io_addr + USBCMD); - /* Poll for and perform state transitions */ - hc_state_transitions(uhci); - if (unlikely(uhci->suspended_ports && uhci->state != UHCI_SUSPENDED)) - uhci_check_ports(uhci); + /* HCRESET doesn't affect the Suspend, Reset, and Resume Detect + * bits in the port status and control registers. + * We have to clear them by hand. + */ + for (port = 0; port < uhci->rh_numports; ++port) + outw(0, uhci->io_addr + USBPORTSC1 + (port * 2)); - init_stall_timer(hcd); - spin_unlock_irqrestore(&uhci->lock, flags); + uhci->port_c_suspend = uhci->suspended_ports = + uhci->resuming_ports = 0; + uhci->rh_state = UHCI_RH_RESET; + uhci->is_stopped = UHCI_IS_STOPPED; + uhci_to_hcd(uhci)->state = HC_STATE_HALT; + uhci_to_hcd(uhci)->poll_rh = 0; } -static int init_stall_timer(struct usb_hcd *hcd) +/* + * Last rites for a defunct/nonfunctional controller + * or one we don't want to use any more. + */ +static void hc_died(struct uhci_hcd *uhci) { - struct uhci_hcd *uhci = hcd_to_uhci(hcd); - - init_timer(&uhci->stall_timer); - uhci->stall_timer.function = stall_callback; - uhci->stall_timer.data = (unsigned long)hcd; - uhci->stall_timer.expires = jiffies + msecs_to_jiffies(100); - add_timer(&uhci->stall_timer); - - return 0; + reset_hc(uhci); + uhci->hc_inaccessible = 1; + del_timer(&uhci->stall_timer); } -static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) +/* + * Initialize a controller that was newly discovered or has just been + * resumed. In either case we can't be sure of its previous state. + */ +static void check_and_reset_hc(struct uhci_hcd *uhci) { - struct uhci_hcd *uhci = hcd_to_uhci(hcd); - unsigned long io_addr = uhci->io_addr; - unsigned short status; + u16 legsup; + unsigned int cmd, intr; /* - * Read the interrupt status, and write it back to clear the - * interrupt cause. Contrary to the UHCI specification, the - * "HC Halted" status bit is persistent: it is RO, not R/WC. + * When restarting a suspended controller, we expect all the + * settings to be the same as we left them: + * + * PIRQ and SMI disabled, no R/W bits set in USBLEGSUP; + * Controller is stopped and configured with EGSM set; + * No interrupts enabled except possibly Resume Detect. + * + * If any of these conditions are violated we do a complete reset. */ - status = inw(io_addr + USBSTS); - if (!(status & ~USBSTS_HCH)) /* shared interrupt, not mine */ - return IRQ_NONE; - outw(status, io_addr + USBSTS); /* Clear it */ - - if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) { - if (status & USBSTS_HSE) - dev_err(uhci_dev(uhci), "host system error, " - "PCI problems?\n"); - if (status & USBSTS_HCPE) - dev_err(uhci_dev(uhci), "host controller process " - "error, something bad happened!\n"); - if ((status & USBSTS_HCH) && uhci->state > 0) { - dev_err(uhci_dev(uhci), "host controller halted, " - "very bad!\n"); - /* FIXME: Reset the controller, fix the offending TD */ - } + pci_read_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, &legsup); + if (legsup & ~(USBLEGSUP_RO | USBLEGSUP_RWC)) { + dev_dbg(uhci_dev(uhci), "%s: legsup = 0x%04x\n", + __FUNCTION__, legsup); + goto reset_needed; } - if (status & USBSTS_RD) - uhci->resume_detect = 1; + cmd = inw(uhci->io_addr + USBCMD); + if ((cmd & USBCMD_RS) || !(cmd & USBCMD_CF) || !(cmd & USBCMD_EGSM)) { + dev_dbg(uhci_dev(uhci), "%s: cmd = 0x%04x\n", + __FUNCTION__, cmd); + goto reset_needed; + } - spin_lock(&uhci->lock); - uhci_scan_schedule(uhci, regs); - spin_unlock(&uhci->lock); + intr = inw(uhci->io_addr + USBINTR); + if (intr & (~USBINTR_RESUME)) { + dev_dbg(uhci_dev(uhci), "%s: intr = 0x%04x\n", + __FUNCTION__, intr); + goto reset_needed; + } + return; - return IRQ_HANDLED; +reset_needed: + dev_dbg(uhci_dev(uhci), "Performing full reset\n"); + reset_hc(uhci); } -static void reset_hc(struct uhci_hcd *uhci) +/* + * Store the basic register settings needed by the controller. + */ +static void configure_hc(struct uhci_hcd *uhci) { - unsigned long io_addr = uhci->io_addr; + /* Set the frame length to the default: 1 ms exactly */ + outb(USBSOF_DEFAULT, uhci->io_addr + USBSOF); - /* Turn off PIRQ, SMI, and all interrupts. This also turns off - * the BIOS's USB Legacy Support. - */ - pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0); - outw(0, uhci->io_addr + USBINTR); + /* Store the frame list base address */ + outl(uhci->fl->dma_handle, uhci->io_addr + USBFLBASEADD); - /* Global reset for 50ms */ - uhci->state = UHCI_RESET; - outw(USBCMD_GRESET, io_addr + USBCMD); - msleep(50); - outw(0, io_addr + USBCMD); + /* Set the current frame number */ + outw(uhci->frame_number, uhci->io_addr + USBFRNUM); - /* Another 10ms delay */ - msleep(10); - uhci->resume_detect = 0; - uhci->is_stopped = UHCI_IS_STOPPED; + /* Mark controller as running before we enable interrupts */ + uhci_to_hcd(uhci)->state = HC_STATE_RUNNING; + mb(); + + /* Enable PIRQ */ + pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, + USBLEGSUP_DEFAULT); } -static void suspend_hc(struct uhci_hcd *uhci) + +static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) { - unsigned long io_addr = uhci->io_addr; + int port; - dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__); - uhci->state = UHCI_SUSPENDED; - uhci->resume_detect = 0; - outw(USBCMD_EGSM, io_addr + USBCMD); + switch (to_pci_dev(uhci_dev(uhci))->vendor) { + default: + break; - /* FIXME: Wait for the controller to actually stop */ - uhci_get_current_frame_number(uhci); - uhci->is_stopped = UHCI_IS_STOPPED; + case PCI_VENDOR_ID_GENESYS: + /* Genesys Logic's GL880S controllers don't generate + * resume-detect interrupts. + */ + return 1; - uhci_scan_schedule(uhci, NULL); + case PCI_VENDOR_ID_INTEL: + /* Some of Intel's USB controllers have a bug that causes + * resume-detect interrupts if any port has an over-current + * condition. To make matters worse, some motherboards + * hardwire unused USB ports' over-current inputs active! + * To prevent problems, we will not enable resume-detect + * interrupts if any ports are OC. + */ + for (port = 0; port < uhci->rh_numports; ++port) { + if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & + USBPORTSC_OC) + return 1; + } + break; + } + return 0; } -static void wakeup_hc(struct uhci_hcd *uhci) +static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state) +__releases(uhci->lock) +__acquires(uhci->lock) { - unsigned long io_addr = uhci->io_addr; + int auto_stop; + int int_enable; - switch (uhci->state) { - case UHCI_SUSPENDED: /* Start the resume */ - dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__); - - /* Global resume for >= 20ms */ - outw(USBCMD_FGR | USBCMD_EGSM, io_addr + USBCMD); - uhci->state = UHCI_RESUMING_1; - uhci->state_end = jiffies + msecs_to_jiffies(20); - uhci->is_stopped = 0; - break; + auto_stop = (new_state == UHCI_RH_AUTO_STOPPED); + dev_dbg(uhci_dev(uhci), "%s%s\n", __FUNCTION__, + (auto_stop ? " (auto-stop)" : "")); - case UHCI_RESUMING_1: /* End global resume */ - uhci->state = UHCI_RESUMING_2; - outw(0, io_addr + USBCMD); - /* Falls through */ - - case UHCI_RESUMING_2: /* Wait for EOP to be sent */ - if (inw(io_addr + USBCMD) & USBCMD_FGR) - break; - - /* Run for at least 1 second, and - * mark it configured with a 64-byte max packet */ - uhci->state = UHCI_RUNNING_GRACE; - uhci->state_end = jiffies + HZ; - outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, - io_addr + USBCMD); - break; + /* If we get a suspend request when we're already auto-stopped + * then there's nothing to do. + */ + if (uhci->rh_state == UHCI_RH_AUTO_STOPPED) { + uhci->rh_state = new_state; + return; + } - case UHCI_RUNNING_GRACE: /* Now allowed to suspend */ - uhci->state = UHCI_RUNNING; - break; + /* Enable resume-detect interrupts if they work. + * Then enter Global Suspend mode, still configured. + */ + int_enable = (resume_detect_interrupts_are_broken(uhci) ? + 0 : USBINTR_RESUME); + outw(int_enable, uhci->io_addr + USBINTR); + outw(USBCMD_EGSM | USBCMD_CF, uhci->io_addr + USBCMD); + mb(); + udelay(5); - default: - break; + /* If we're auto-stopping then no devices have been attached + * for a while, so there shouldn't be any active URBs and the + * controller should stop after a few microseconds. Otherwise + * we will give the controller one frame to stop. + */ + if (!auto_stop && !(inw(uhci->io_addr + USBSTS) & USBSTS_HCH)) { + uhci->rh_state = UHCI_RH_SUSPENDING; + spin_unlock_irq(&uhci->lock); + msleep(1); + spin_lock_irq(&uhci->lock); + if (uhci->hc_inaccessible) /* Died */ + return; } -} + if (!(inw(uhci->io_addr + USBSTS) & USBSTS_HCH)) + dev_warn(uhci_dev(uhci), "Controller not stopped yet!\n"); -static int ports_active(struct uhci_hcd *uhci) -{ - unsigned long io_addr = uhci->io_addr; - int connection = 0; - int i; + uhci_get_current_frame_number(uhci); + smp_wmb(); - for (i = 0; i < uhci->rh_numports; i++) - connection |= (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_CCS); + uhci->rh_state = new_state; + uhci->is_stopped = UHCI_IS_STOPPED; + del_timer(&uhci->stall_timer); + uhci_to_hcd(uhci)->poll_rh = !int_enable; - return connection; + uhci_scan_schedule(uhci, NULL); } -static int suspend_allowed(struct uhci_hcd *uhci) +static void start_rh(struct uhci_hcd *uhci) { - unsigned long io_addr = uhci->io_addr; - int i; - - if (to_pci_dev(uhci_dev(uhci))->vendor != PCI_VENDOR_ID_INTEL) - return 1; + uhci->is_stopped = 0; + smp_wmb(); - /* Some of Intel's USB controllers have a bug that causes false - * resume indications if any port has an over current condition. - * To prevent problems, we will not allow a global suspend if - * any ports are OC. - * - * Some motherboards using Intel's chipsets (but not using all - * the USB ports) appear to hardwire the over current inputs active - * to disable the USB ports. + /* Mark it configured and running with a 64-byte max packet. + * All interrupts are enabled, even though RESUME won't do anything. */ - - /* check for over current condition on any port */ - for (i = 0; i < uhci->rh_numports; i++) { - if (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_OC) - return 0; - } - - return 1; + outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, uhci->io_addr + USBCMD); + outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, + uhci->io_addr + USBINTR); + mb(); + uhci->rh_state = UHCI_RH_RUNNING; + uhci_to_hcd(uhci)->poll_rh = 1; + restart_timer(uhci); } -static void hc_state_transitions(struct uhci_hcd *uhci) +static void wakeup_rh(struct uhci_hcd *uhci) +__releases(uhci->lock) +__acquires(uhci->lock) { - switch (uhci->state) { - case UHCI_RUNNING: + dev_dbg(uhci_dev(uhci), "%s%s\n", __FUNCTION__, + uhci->rh_state == UHCI_RH_AUTO_STOPPED ? + " (auto-start)" : ""); - /* global suspend if nothing connected for 1 second */ - if (!ports_active(uhci) && suspend_allowed(uhci)) { - uhci->state = UHCI_SUSPENDING_GRACE; - uhci->state_end = jiffies + HZ; - } - break; - - case UHCI_SUSPENDING_GRACE: - if (ports_active(uhci)) - uhci->state = UHCI_RUNNING; - else if (time_after_eq(jiffies, uhci->state_end)) - suspend_hc(uhci); - break; - - case UHCI_SUSPENDED: - - /* wakeup if requested by a device */ - if (uhci->resume_detect) - wakeup_hc(uhci); - break; + /* If we are auto-stopped then no devices are attached so there's + * no need for wakeup signals. Otherwise we send Global Resume + * for 20 ms. + */ + if (uhci->rh_state == UHCI_RH_SUSPENDED) { + uhci->rh_state = UHCI_RH_RESUMING; + outw(USBCMD_FGR | USBCMD_EGSM | USBCMD_CF, + uhci->io_addr + USBCMD); + spin_unlock_irq(&uhci->lock); + msleep(20); + spin_lock_irq(&uhci->lock); + if (uhci->hc_inaccessible) /* Died */ + return; + + /* End Global Resume and wait for EOP to be sent */ + outw(USBCMD_CF, uhci->io_addr + USBCMD); + mb(); + udelay(4); + if (inw(uhci->io_addr + USBCMD) & USBCMD_FGR) + dev_warn(uhci_dev(uhci), "FGR not stopped yet!\n"); + } - case UHCI_RESUMING_1: - case UHCI_RESUMING_2: - case UHCI_RUNNING_GRACE: - if (time_after_eq(jiffies, uhci->state_end)) - wakeup_hc(uhci); - break; + start_rh(uhci); - default: - break; - } + /* Restart root hub polling */ + mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies); } -/* - * Store the current frame number in uhci->frame_number if the controller - * is runnning - */ -static void uhci_get_current_frame_number(struct uhci_hcd *uhci) +static void stall_callback(unsigned long _uhci) { + struct uhci_hcd *uhci = (struct uhci_hcd *) _uhci; + unsigned long flags; + + spin_lock_irqsave(&uhci->lock, flags); + uhci_scan_schedule(uhci, NULL); + check_fsbr(uhci); + if (!uhci->is_stopped) - uhci->frame_number = inw(uhci->io_addr + USBFRNUM); + restart_timer(uhci); + spin_unlock_irqrestore(&uhci->lock, flags); } -static int start_hc(struct uhci_hcd *uhci) +static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) { - unsigned long io_addr = uhci->io_addr; - int timeout = 10; + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + unsigned short status; + unsigned long flags; /* - * Reset the HC - this will force us to get a - * new notification of any already connected - * ports due to the virtual disconnect that it - * implies. + * Read the interrupt status, and write it back to clear the + * interrupt cause. Contrary to the UHCI specification, the + * "HC Halted" status bit is persistent: it is RO, not R/WC. */ - outw(USBCMD_HCRESET, io_addr + USBCMD); - while (inw(io_addr + USBCMD) & USBCMD_HCRESET) { - if (--timeout < 0) { - dev_err(uhci_dev(uhci), "USBCMD_HCRESET timed out!\n"); - return -ETIMEDOUT; + status = inw(uhci->io_addr + USBSTS); + if (!(status & ~USBSTS_HCH)) /* shared interrupt, not mine */ + return IRQ_NONE; + outw(status, uhci->io_addr + USBSTS); /* Clear it */ + + if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) { + if (status & USBSTS_HSE) + dev_err(uhci_dev(uhci), "host system error, " + "PCI problems?\n"); + if (status & USBSTS_HCPE) + dev_err(uhci_dev(uhci), "host controller process " + "error, something bad happened!\n"); + if (status & USBSTS_HCH) { + spin_lock_irqsave(&uhci->lock, flags); + if (uhci->rh_state >= UHCI_RH_RUNNING) { + dev_err(uhci_dev(uhci), + "host controller halted, " + "very bad!\n"); + hc_died(uhci); + spin_unlock_irqrestore(&uhci->lock, flags); + return IRQ_HANDLED; + } + spin_unlock_irqrestore(&uhci->lock, flags); } - msleep(1); } - /* Mark controller as running before we enable interrupts */ - uhci_to_hcd(uhci)->state = HC_STATE_RUNNING; - - /* Turn on PIRQ and all interrupts */ - pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, - USBLEGSUP_DEFAULT); - outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, - io_addr + USBINTR); + if (status & USBSTS_RD) + usb_hcd_poll_rh_status(hcd); - /* Start at frame 0 */ - outw(0, io_addr + USBFRNUM); - outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD); + spin_lock_irqsave(&uhci->lock, flags); + uhci_scan_schedule(uhci, regs); + spin_unlock_irqrestore(&uhci->lock, flags); - /* Run and mark it configured with a 64-byte max packet */ - uhci->state = UHCI_RUNNING_GRACE; - uhci->state_end = jiffies + HZ; - outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); - uhci->is_stopped = 0; + return IRQ_HANDLED; +} - return 0; +/* + * Store the current frame number in uhci->frame_number if the controller + * is runnning + */ +static void uhci_get_current_frame_number(struct uhci_hcd *uhci) +{ + if (!uhci->is_stopped) + uhci->frame_number = inw(uhci->io_addr + USBFRNUM); } /* @@ -448,16 +488,58 @@ static void release_uhci(struct uhci_hcd *uhci) static int uhci_reset(struct usb_hcd *hcd) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); + unsigned io_size = (unsigned) hcd->rsrc_len; + int port; uhci->io_addr = (unsigned long) hcd->rsrc_start; - /* Kick BIOS off this hardware and reset, so we won't get - * interrupts from any previous setup. + /* The UHCI spec says devices must have 2 ports, and goes on to say + * they may have more but gives no way to determine how many there + * are. However according to the UHCI spec, Bit 7 of the port + * status and control register is always set to 1. So we try to + * use this to our advantage. Another common failure mode when + * a nonexistent register is addressed is to return all ones, so + * we test for that also. */ - reset_hc(uhci); + for (port = 0; port < (io_size - USBPORTSC1) / 2; port++) { + unsigned int portstatus; + + portstatus = inw(uhci->io_addr + USBPORTSC1 + (port * 2)); + if (!(portstatus & 0x0080) || portstatus == 0xffff) + break; + } + if (debug) + dev_info(uhci_dev(uhci), "detected %d ports\n", port); + + /* Anything greater than 7 is weird so we'll ignore it. */ + if (port > UHCI_RH_MAXCHILD) { + dev_info(uhci_dev(uhci), "port count misdetected? " + "forcing to 2 ports\n"); + port = 2; + } + uhci->rh_numports = port; + + /* Kick BIOS off this hardware and reset if the controller + * isn't already safely quiescent. + */ + check_and_reset_hc(uhci); return 0; } +/* Make sure the controller is quiescent and that we're not using it + * any more. This is mainly for the benefit of programs which, like kexec, + * expect the hardware to be idle: not doing DMA or generating IRQs. + * + * This routine may be called in a damaged or failing kernel. Hence we + * do not acquire the spinlock before shutting down the controller. + */ +static void uhci_shutdown(struct pci_dev *pdev) +{ + struct usb_hcd *hcd = (struct usb_hcd *) pci_get_drvdata(pdev); + + hc_died(hcd_to_uhci(hcd)); +} + /* * Allocate a frame list, and then setup the skeleton * @@ -478,17 +560,20 @@ static int uhci_start(struct usb_hcd *hcd) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); int retval = -EBUSY; - int i, port; - unsigned io_size; + int i; dma_addr_t dma_handle; - struct usb_device *udev; struct dentry *dentry; - io_size = (unsigned) hcd->rsrc_len; + hcd->uses_new_polling = 1; + if (pci_find_capability(to_pci_dev(uhci_dev(uhci)), PCI_CAP_ID_PM)) + hcd->can_wakeup = 1; /* Assume it supports PME# */ - dentry = debugfs_create_file(hcd->self.bus_name, S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root, uhci, &uhci_debug_operations); + dentry = debugfs_create_file(hcd->self.bus_name, + S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root, uhci, + &uhci_debug_operations); if (!dentry) { - dev_err(uhci_dev(uhci), "couldn't create uhci debugfs entry\n"); + dev_err(uhci_dev(uhci), + "couldn't create uhci debugfs entry\n"); retval = -ENOMEM; goto err_create_debug_entry; } @@ -510,6 +595,10 @@ static int uhci_start(struct usb_hcd *hcd) init_waitqueue_head(&uhci->waitqh); + init_timer(&uhci->stall_timer); + uhci->stall_timer.function = stall_callback; + uhci->stall_timer.data = (unsigned long) uhci; + uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl), &dma_handle, 0); if (!uhci->fl) { @@ -536,46 +625,14 @@ static int uhci_start(struct usb_hcd *hcd) goto err_create_qh_pool; } - /* Initialize the root hub */ - - /* UHCI specs says devices must have 2 ports, but goes on to say */ - /* they may have more but give no way to determine how many they */ - /* have. However, according to the UHCI spec, Bit 7 is always set */ - /* to 1. So we try to use this to our advantage */ - for (port = 0; port < (io_size - 0x10) / 2; port++) { - unsigned int portstatus; - - portstatus = inw(uhci->io_addr + 0x10 + (port * 2)); - if (!(portstatus & 0x0080)) - break; - } - if (debug) - dev_info(uhci_dev(uhci), "detected %d ports\n", port); - - /* This is experimental so anything less than 2 or greater than 8 is */ - /* something weird and we'll ignore it */ - if (port < 2 || port > UHCI_RH_MAXCHILD) { - dev_info(uhci_dev(uhci), "port count misdetected? " - "forcing to 2 ports\n"); - port = 2; - } - - uhci->rh_numports = port; - - udev = usb_alloc_dev(NULL, &hcd->self, 0); - if (!udev) { - dev_err(uhci_dev(uhci), "unable to allocate root hub\n"); - goto err_alloc_root_hub; - } - - uhci->term_td = uhci_alloc_td(uhci, udev); + uhci->term_td = uhci_alloc_td(uhci); if (!uhci->term_td) { dev_err(uhci_dev(uhci), "unable to allocate terminating TD\n"); goto err_alloc_term_td; } for (i = 0; i < UHCI_NUM_SKELQH; i++) { - uhci->skelqh[i] = uhci_alloc_qh(uhci, udev); + uhci->skelqh[i] = uhci_alloc_qh(uhci); if (!uhci->skelqh[i]) { dev_err(uhci_dev(uhci), "unable to allocate QH\n"); goto err_alloc_skelqh; @@ -641,32 +698,17 @@ static int uhci_start(struct usb_hcd *hcd) /* * Some architectures require a full mb() to enforce completion of - * the memory writes above before the I/O transfers in start_hc(). + * the memory writes above before the I/O transfers in configure_hc(). */ mb(); - if ((retval = start_hc(uhci)) != 0) - goto err_alloc_skelqh; - - init_stall_timer(hcd); - - udev->speed = USB_SPEED_FULL; - - if (usb_hcd_register_root_hub(udev, hcd) != 0) { - dev_err(uhci_dev(uhci), "unable to start root hub\n"); - retval = -ENOMEM; - goto err_start_root_hub; - } + configure_hc(uhci); + start_rh(uhci); return 0; /* * error exits: */ -err_start_root_hub: - reset_hc(uhci); - - del_timer_sync(&uhci->stall_timer); - err_alloc_skelqh: for (i = 0; i < UHCI_NUM_SKELQH; i++) if (uhci->skelqh[i]) { @@ -678,9 +720,6 @@ err_alloc_skelqh: uhci->term_td = NULL; err_alloc_term_td: - usb_put_dev(udev); - -err_alloc_root_hub: dma_pool_destroy(uhci->qh_pool); uhci->qh_pool = NULL; @@ -705,73 +744,114 @@ static void uhci_stop(struct usb_hcd *hcd) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); - del_timer_sync(&uhci->stall_timer); - reset_hc(uhci); - spin_lock_irq(&uhci->lock); + reset_hc(uhci); uhci_scan_schedule(uhci, NULL); spin_unlock_irq(&uhci->lock); - + + del_timer_sync(&uhci->stall_timer); release_uhci(uhci); } #ifdef CONFIG_PM +static int uhci_rh_suspend(struct usb_hcd *hcd) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + + spin_lock_irq(&uhci->lock); + if (!uhci->hc_inaccessible) /* Not dead */ + suspend_rh(uhci, UHCI_RH_SUSPENDED); + spin_unlock_irq(&uhci->lock); + return 0; +} + +static int uhci_rh_resume(struct usb_hcd *hcd) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + int rc = 0; + + spin_lock_irq(&uhci->lock); + if (uhci->hc_inaccessible) { + if (uhci->rh_state == UHCI_RH_SUSPENDED) { + dev_warn(uhci_dev(uhci), "HC isn't running!\n"); + rc = -ENODEV; + } + /* Otherwise the HC is dead */ + } else + wakeup_rh(uhci); + spin_unlock_irq(&uhci->lock); + return rc; +} + static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); + int rc = 0; + + dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__); spin_lock_irq(&uhci->lock); + if (uhci->hc_inaccessible) /* Dead or already suspended */ + goto done; - /* Don't try to suspend broken motherboards, reset instead */ - if (suspend_allowed(uhci)) - suspend_hc(uhci); - else { - spin_unlock_irq(&uhci->lock); - reset_hc(uhci); - spin_lock_irq(&uhci->lock); - uhci_scan_schedule(uhci, NULL); - } +#ifndef CONFIG_USB_SUSPEND + /* Otherwise this would never happen */ + suspend_rh(uhci, UHCI_RH_SUSPENDED); +#endif + + if (uhci->rh_state > UHCI_RH_SUSPENDED) { + dev_warn(uhci_dev(uhci), "Root hub isn't suspended!\n"); + hcd->state = HC_STATE_RUNNING; + rc = -EBUSY; + goto done; + }; + /* All PCI host controllers are required to disable IRQ generation + * at the source, so we must turn off PIRQ. + */ + pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0); + uhci->hc_inaccessible = 1; + + /* FIXME: Enable non-PME# remote wakeup? */ + +done: spin_unlock_irq(&uhci->lock); - return 0; + if (rc == 0) + del_timer_sync(&hcd->rh_timer); + return rc; } static int uhci_resume(struct usb_hcd *hcd) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); - int rc; - pci_set_master(to_pci_dev(uhci_dev(uhci))); + dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__); + if (uhci->rh_state == UHCI_RH_RESET) /* Dead */ + return 0; spin_lock_irq(&uhci->lock); - if (uhci->state == UHCI_SUSPENDED) { + /* FIXME: Disable non-PME# remote wakeup? */ - /* - * Some systems don't maintain the UHCI register values - * during a PM suspend/resume cycle, so reinitialize - * the Frame Number, Framelist Base Address, Interrupt - * Enable, and Legacy Support registers. - */ - pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, - 0); - outw(uhci->frame_number, uhci->io_addr + USBFRNUM); - outl(uhci->fl->dma_handle, uhci->io_addr + USBFLBASEADD); - outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | - USBINTR_SP, uhci->io_addr + USBINTR); - uhci->resume_detect = 1; - pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, - USBLEGSUP_DEFAULT); - } else { - spin_unlock_irq(&uhci->lock); - reset_hc(uhci); - if ((rc = start_hc(uhci)) != 0) - return rc; - spin_lock_irq(&uhci->lock); - } - hcd->state = HC_STATE_RUNNING; + uhci->hc_inaccessible = 0; + + /* The BIOS may have changed the controller settings during a + * system wakeup. Check it and reconfigure to avoid problems. + */ + check_and_reset_hc(uhci); + configure_hc(uhci); + +#ifndef CONFIG_USB_SUSPEND + /* Otherwise this would never happen */ + wakeup_rh(uhci); +#endif + if (uhci->rh_state == UHCI_RH_RESET) + suspend_rh(uhci, UHCI_RH_SUSPENDED); spin_unlock_irq(&uhci->lock); + + if (hcd->poll_rh) + usb_hcd_poll_rh_status(hcd); return 0; } #endif @@ -788,13 +868,15 @@ static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd, static int uhci_hcd_get_frame_number(struct usb_hcd *hcd) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); - int frame_number; unsigned long flags; + int is_stopped; + int frame_number; /* Minimize latency by avoiding the spinlock */ local_irq_save(flags); - rmb(); - frame_number = (uhci->is_stopped ? uhci->frame_number : + is_stopped = uhci->is_stopped; + smp_rmb(); + frame_number = (is_stopped ? uhci->frame_number : inw(uhci->io_addr + USBFRNUM)); local_irq_restore(flags); return frame_number; @@ -817,6 +899,8 @@ static const struct hc_driver uhci_driver = { #ifdef CONFIG_PM .suspend = uhci_suspend, .resume = uhci_resume, + .hub_suspend = uhci_rh_suspend, + .hub_resume = uhci_rh_resume, #endif .stop = uhci_stop, @@ -845,6 +929,7 @@ static struct pci_driver uhci_pci_driver = { .probe = usb_hcd_pci_probe, .remove = usb_hcd_pci_remove, + .shutdown = uhci_shutdown, #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 02255d6..bf9c5f9 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -41,6 +41,7 @@ #define USBFRNUM 6 #define USBFLBASEADD 8 #define USBSOF 12 +#define USBSOF_DEFAULT 64 /* Frame length is exactly 1 ms */ /* USB port status and control registers */ #define USBPORTSC1 16 @@ -66,6 +67,8 @@ /* Legacy support register */ #define USBLEGSUP 0xc0 #define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ +#define USBLEGSUP_RWC 0x8f00 /* the R/WC bits */ +#define USBLEGSUP_RO 0x5040 /* R/O and reserved bits */ #define UHCI_NULL_DATA_SIZE 0x7FF /* for UHCI controller TD */ @@ -111,7 +114,6 @@ struct uhci_qh { /* Software fields */ dma_addr_t dma_handle; - struct usb_device *dev; struct urb_priv *urbp; struct list_head list; /* P: uhci->frame_list_lock */ @@ -203,7 +205,6 @@ struct uhci_td { /* Software fields */ dma_addr_t dma_handle; - struct usb_device *dev; struct urb *urb; struct list_head list; /* P: urb->lock */ @@ -314,26 +315,32 @@ static inline int __interval_to_skel(int interval) } /* - * Device states for the host controller. + * States for the root hub. * * To prevent "bouncing" in the presence of electrical noise, - * we insist on a 1-second "grace" period, before switching to - * the RUNNING or SUSPENDED states, during which the state is - * not allowed to change. - * - * The resume process is divided into substates in order to avoid - * potentially length delays during the timer handler. - * - * States in which the host controller is halted must have values <= 0. + * when there are no devices attached we delay for 1 second in the + * RUNNING_NODEVS state before switching to the AUTO_STOPPED state. + * + * (Note that the AUTO_STOPPED state won't be necessary once the hub + * driver learns to autosuspend.) */ -enum uhci_state { - UHCI_RESET, - UHCI_RUNNING_GRACE, /* Before RUNNING */ - UHCI_RUNNING, /* The normal state */ - UHCI_SUSPENDING_GRACE, /* Before SUSPENDED */ - UHCI_SUSPENDED = -10, /* When no devices are attached */ - UHCI_RESUMING_1, - UHCI_RESUMING_2 +enum uhci_rh_state { + /* In the following states the HC must be halted. + * These two must come first */ + UHCI_RH_RESET, + UHCI_RH_SUSPENDED, + + UHCI_RH_AUTO_STOPPED, + UHCI_RH_RESUMING, + + /* In this state the HC changes from running to halted, + * so it can legally appear either way. */ + UHCI_RH_SUSPENDING, + + /* In the following states it's an error if the HC is halted. + * These two must come last */ + UHCI_RH_RUNNING, /* The normal state */ + UHCI_RH_RUNNING_NODEVS, /* Running with no devices attached */ }; /* @@ -363,15 +370,16 @@ struct uhci_hcd { int fsbr; /* Full-speed bandwidth reclamation */ unsigned long fsbrtimeout; /* FSBR delay */ - enum uhci_state state; /* FIXME: needs a spinlock */ - unsigned long state_end; /* Time of next transition */ + enum uhci_rh_state rh_state; + unsigned long auto_stop_time; /* When to AUTO_STOP */ + unsigned int frame_number; /* As of last check */ unsigned int is_stopped; #define UHCI_IS_STOPPED 9999 /* Larger than a frame # */ unsigned int scan_in_progress:1; /* Schedule scan is running */ unsigned int need_rescan:1; /* Redo the schedule scan */ - unsigned int resume_detect:1; /* Need a Global Resume */ + unsigned int hc_inaccessible:1; /* HC is suspended or dead */ /* Support for port suspend/resume/reset */ unsigned long port_c_suspend; /* Bit-arrays of ports */ @@ -451,4 +459,11 @@ struct urb_priv { * #2 urb->lock */ + +/* Some special IDs */ + +#define PCI_VENDOR_ID_GENESYS 0x17a0 +#define PCI_DEVICE_ID_GL880S_UHCI 0x8083 +#define PCI_DEVICE_ID_GL880S_EHCI 0x8084 + #endif diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 4c45ba8..4eace2b 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -33,9 +33,24 @@ static __u8 root_hub_hub_des[] = /* status change bits: nonzero writes will clear */ #define RWC_BITS (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC) -static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) +/* A port that either is connected or has a changed-bit set will prevent + * us from AUTO_STOPPING. + */ +static int any_ports_active(struct uhci_hcd *uhci) +{ + int port; + + for (port = 0; port < uhci->rh_numports; ++port) { + if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & + (USBPORTSC_CCS | RWC_BITS)) || + test_bit(port, &uhci->port_c_suspend)) + return 1; + } + return 0; +} + +static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf) { - struct uhci_hcd *uhci = hcd_to_uhci(hcd); int port; *buf = 0; @@ -44,8 +59,6 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) test_bit(port, &uhci->port_c_suspend)) *buf |= (1 << (port + 1)); } - if (*buf && uhci->state == UHCI_SUSPENDED) - uhci->resume_detect = 1; return !!*buf; } @@ -115,6 +128,11 @@ static void uhci_check_ports(struct uhci_hcd *uhci) set_bit(port, &uhci->resuming_ports); uhci->ports_timeout = jiffies + msecs_to_jiffies(20); + + /* Make sure we see the port again + * after the resuming period is over. */ + mod_timer(&uhci_to_hcd(uhci)->rh_timer, + uhci->ports_timeout); } else if (time_after_eq(jiffies, uhci->ports_timeout)) { uhci_finish_suspend(uhci, port, port_addr); @@ -123,6 +141,60 @@ static void uhci_check_ports(struct uhci_hcd *uhci) } } +static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + unsigned long flags; + int status; + + spin_lock_irqsave(&uhci->lock, flags); + if (uhci->hc_inaccessible) { + status = 0; + goto done; + } + + uhci_check_ports(uhci); + status = get_hub_status_data(uhci, buf); + + switch (uhci->rh_state) { + case UHCI_RH_SUSPENDING: + case UHCI_RH_SUSPENDED: + /* if port change, ask to be resumed */ + if (status) + usb_hcd_resume_root_hub(hcd); + break; + + case UHCI_RH_AUTO_STOPPED: + /* if port change, auto start */ + if (status) + wakeup_rh(uhci); + break; + + case UHCI_RH_RUNNING: + /* are any devices attached? */ + if (!any_ports_active(uhci)) { + uhci->rh_state = UHCI_RH_RUNNING_NODEVS; + uhci->auto_stop_time = jiffies + HZ; + } + break; + + case UHCI_RH_RUNNING_NODEVS: + /* auto-stop if nothing connected for 1 second */ + if (any_ports_active(uhci)) + uhci->rh_state = UHCI_RH_RUNNING; + else if (time_after_eq(jiffies, uhci->auto_stop_time)) + suspend_rh(uhci, UHCI_RH_AUTO_STOPPED); + break; + + default: + break; + } + +done: + spin_unlock_irqrestore(&uhci->lock, flags); + return status; +} + /* size of returned buffer is part of USB spec */ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) @@ -134,6 +206,9 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wPortChange, wPortStatus; unsigned long flags; + if (uhci->hc_inaccessible) + return -ETIMEDOUT; + spin_lock_irqsave(&uhci->lock, flags); switch (typeReq) { diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 2a7c195..5f18084 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -32,6 +32,8 @@ static void uhci_free_pending_tds(struct uhci_hcd *uhci); */ static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci) { + if (uhci->is_stopped) + mod_timer(&uhci->stall_timer, jiffies); uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); } @@ -46,7 +48,7 @@ static inline void uhci_moveto_complete(struct uhci_hcd *uhci, list_move_tail(&urbp->urb_list, &uhci->complete_list); } -static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci, struct usb_device *dev) +static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci) { dma_addr_t dma_handle; struct uhci_td *td; @@ -61,14 +63,11 @@ static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci, struct usb_device *d td->buffer = 0; td->frame = -1; - td->dev = dev; INIT_LIST_HEAD(&td->list); INIT_LIST_HEAD(&td->remove_list); INIT_LIST_HEAD(&td->fl_list); - usb_get_dev(dev); - return td; } @@ -168,13 +167,10 @@ static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td) if (!list_empty(&td->fl_list)) dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td); - if (td->dev) - usb_put_dev(td->dev); - dma_pool_free(uhci->td_pool, td, td->dma_handle); } -static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, struct usb_device *dev) +static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci) { dma_addr_t dma_handle; struct uhci_qh *qh; @@ -188,14 +184,11 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, struct usb_device *d qh->element = UHCI_PTR_TERM; qh->link = UHCI_PTR_TERM; - qh->dev = dev; qh->urbp = NULL; INIT_LIST_HEAD(&qh->list); INIT_LIST_HEAD(&qh->remove_list); - usb_get_dev(dev); - return qh; } @@ -206,9 +199,6 @@ static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) if (!list_empty(&qh->remove_list)) dev_warn(uhci_dev(uhci), "qh %p still in remove_list!\n", qh); - if (qh->dev) - usb_put_dev(qh->dev); - dma_pool_free(uhci->qh_pool, qh, qh->dma_handle); } @@ -597,7 +587,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur /* * Build the TD for the control request setup packet */ - td = uhci_alloc_td(uhci, urb->dev); + td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; @@ -626,7 +616,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur if (pktsze > maxsze) pktsze = maxsze; - td = uhci_alloc_td(uhci, urb->dev); + td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; @@ -644,7 +634,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur /* * Build the final TD for control status */ - td = uhci_alloc_td(uhci, urb->dev); + td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; @@ -666,7 +656,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur uhci_fill_td(td, status | TD_CTRL_IOC, destination | uhci_explen(UHCI_NULL_DATA_SIZE), 0); - qh = uhci_alloc_qh(uhci, urb->dev); + qh = uhci_alloc_qh(uhci); if (!qh) return -ENOMEM; @@ -865,7 +855,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb status &= ~TD_CTRL_SPD; } - td = uhci_alloc_td(uhci, urb->dev); + td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; @@ -891,7 +881,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb */ if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) && !len && urb->transfer_buffer_length) { - td = uhci_alloc_td(uhci, urb->dev); + td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; @@ -913,7 +903,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb * flag setting. */ td->status |= cpu_to_le32(TD_CTRL_IOC); - qh = uhci_alloc_qh(uhci, urb->dev); + qh = uhci_alloc_qh(uhci); if (!qh) return -ENOMEM; @@ -1096,7 +1086,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb) if (!urb->iso_frame_desc[i].length) continue; - td = uhci_alloc_td(uhci, urb->dev); + td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; @@ -1497,6 +1487,7 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs) rescan: uhci->need_rescan = 0; + uhci_clear_next_interrupt(uhci); uhci_get_current_frame_number(uhci); if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age) @@ -1537,3 +1528,26 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs) /* Wake up anyone waiting for an URB to complete */ wake_up_all(&uhci->waitqh); } + +static void check_fsbr(struct uhci_hcd *uhci) +{ + struct urb_priv *up; + + list_for_each_entry(up, &uhci->urb_list, urb_list) { + struct urb *u = up->urb; + + spin_lock(&u->lock); + + /* Check if the FSBR timed out */ + if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT)) + uhci_fsbr_timeout(uhci, u); + + spin_unlock(&u->lock); + } + + /* Really disable FSBR */ + if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) { + uhci->fsbrtimeout = 0; + uhci->skel_term_qh->link = UHCI_PTR_TERM; + } +} diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig index d28e7ea..fd59f6b 100644 --- a/drivers/usb/input/Kconfig +++ b/drivers/usb/input/Kconfig @@ -151,6 +151,18 @@ config USB_WACOM To compile this driver as a module, choose M here: the module will be called wacom. +config USB_ACECAD + tristate "Acecad Flair tablet support" + depends on USB && INPUT + help + Say Y here if you want to use the USB version of the Acecad Flair + tablet. Make sure to say Y to "Mouse support" + (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" + (CONFIG_INPUT_EVDEV) as well. + + To compile this driver as a module, choose M here: the + module will be called acecad. + config USB_KBTAB tristate "KB Gear JamStudio tablet support" depends on USB && INPUT @@ -190,6 +202,18 @@ config USB_MTOUCH To compile this driver as a module, choose M here: the module will be called mtouchusb. +config USB_ITMTOUCH + tristate "ITM Touch USB Touchscreen Driver" + depends on USB && INPUT + ---help--- + Say Y here if you want to use a ITM Touch USB + Touchscreen controller. + + This touchscreen is used in LG 1510SF monitors. + + To compile this driver as a module, choose M here: the + module will be called itmtouch. + config USB_EGALAX tristate "eGalax TouchKit USB Touchscreen Driver" depends on USB && INPUT diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile index 6bcedd1..831b2b0 100644 --- a/drivers/usb/input/Makefile +++ b/drivers/usb/input/Makefile @@ -33,7 +33,9 @@ obj-$(CONFIG_USB_KBD) += usbkbd.o obj-$(CONFIG_USB_KBTAB) += kbtab.o obj-$(CONFIG_USB_MOUSE) += usbmouse.o obj-$(CONFIG_USB_MTOUCH) += mtouchusb.o +obj-$(CONFIG_USB_ITMTOUCH) += itmtouch.o obj-$(CONFIG_USB_EGALAX) += touchkitusb.o obj-$(CONFIG_USB_POWERMATE) += powermate.o obj-$(CONFIG_USB_WACOM) += wacom.o +obj-$(CONFIG_USB_ACECAD) += acecad.o obj-$(CONFIG_USB_XPAD) += xpad.o diff --git a/drivers/usb/input/acecad.c b/drivers/usb/input/acecad.c new file mode 100644 index 0000000..ebcf7c9 --- /dev/null +++ b/drivers/usb/input/acecad.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2001-2005 Edouard TISSERANT <edouard.tisserant@wanadoo.fr> + * Copyright (c) 2004-2005 Stephane VOLTZ <svoltz@numericable.fr> + * + * USB Acecad "Acecad Flair" tablet support + * + * Changelog: + * v3.2 - Added sysfs support + */ + +/* + * 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 + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> + +/* + * Version Information + */ +#define DRIVER_VERSION "v3.2" +#define DRIVER_DESC "USB Acecad Flair tablet driver" +#define DRIVER_LICENSE "GPL" +#define DRIVER_AUTHOR "Edouard TISSERANT <edouard.tisserant@wanadoo.fr>" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +#define USB_VENDOR_ID_ACECAD 0x0460 +#define USB_DEVICE_ID_FLAIR 0x0004 +#define USB_DEVICE_ID_302 0x0008 + +struct usb_acecad { + char name[128]; + char phys[64]; + struct usb_device *usbdev; + struct input_dev dev; + struct urb *irq; + + signed char *data; + dma_addr_t data_dma; +}; + +static void usb_acecad_irq(struct urb *urb, struct pt_regs *regs) +{ + struct usb_acecad *acecad = urb->context; + unsigned char *data = acecad->data; + struct input_dev *dev = &acecad->dev; + int prox, status; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + goto resubmit; + } + + prox = (data[0] & 0x04) >> 2; + input_report_key(dev, BTN_TOOL_PEN, prox); + + if (prox) { + int x = data[1] | (data[2] << 8); + int y = data[3] | (data[4] << 8); + /*Pressure should compute the same way for flair and 302*/ + int pressure = data[5] | ((int)data[6] << 8); + int touch = data[0] & 0x01; + int stylus = (data[0] & 0x10) >> 4; + int stylus2 = (data[0] & 0x20) >> 5; + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_report_abs(dev, ABS_PRESSURE, pressure); + input_report_key(dev, BTN_TOUCH, touch); + input_report_key(dev, BTN_STYLUS, stylus); + input_report_key(dev, BTN_STYLUS2, stylus2); + } + + /* event termination */ + input_sync(dev); + +resubmit: + status = usb_submit_urb (urb, GFP_ATOMIC); + if (status) + err ("can't resubmit intr, %s-%s/input0, status %d", + acecad->usbdev->bus->bus_name, acecad->usbdev->devpath, status); +} + +static int usb_acecad_open(struct input_dev *dev) +{ + struct usb_acecad *acecad = dev->private; + + acecad->irq->dev = acecad->usbdev; + if (usb_submit_urb(acecad->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void usb_acecad_close(struct input_dev *dev) +{ + struct usb_acecad *acecad = dev->private; + + usb_kill_urb(acecad->irq); +} + +static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *interface = intf->cur_altsetting; + struct usb_endpoint_descriptor *endpoint; + struct usb_acecad *acecad; + int pipe, maxp; + char path[64]; + + if (interface->desc.bNumEndpoints != 1) + return -ENODEV; + + endpoint = &interface->endpoint[0].desc; + + if (!(endpoint->bEndpointAddress & 0x80)) + return -ENODEV; + + if ((endpoint->bmAttributes & 3) != 3) + return -ENODEV; + + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + acecad = kcalloc(1, sizeof(struct usb_acecad), GFP_KERNEL); + if (!acecad) + return -ENOMEM; + + acecad->data = usb_buffer_alloc(dev, 8, SLAB_KERNEL, &acecad->data_dma); + if (!acecad->data) + goto fail1; + + acecad->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!acecad->irq) + goto fail2; + + if (dev->manufacturer) + strlcpy(acecad->name, dev->manufacturer, sizeof(acecad->name)); + + if (dev->product) { + if (dev->manufacturer) + strlcat(acecad->name, " ", sizeof(acecad->name)); + strlcat(acecad->name, dev->product, sizeof(acecad->name)); + } + + usb_make_path(dev, path, sizeof(path)); + snprintf(acecad->phys, sizeof(acecad->phys), "%s/input0", path); + + acecad->usbdev = dev; + + acecad->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + acecad->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE); + acecad->dev.keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); + acecad->dev.keybit[LONG(BTN_DIGI)] = BIT(BTN_TOOL_PEN) |BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2); + + switch (id->driver_info) { + case 0: + acecad->dev.absmax[ABS_X] = 5000; + acecad->dev.absmax[ABS_Y] = 3750; + acecad->dev.absmax[ABS_PRESSURE] = 512; + if (!strlen(acecad->name)) + snprintf(acecad->name, sizeof(acecad->name), + "USB Acecad Flair Tablet %04x:%04x", + dev->descriptor.idVendor, dev->descriptor.idProduct); + break; + case 1: + acecad->dev.absmax[ABS_X] = 3000; + acecad->dev.absmax[ABS_Y] = 2250; + acecad->dev.absmax[ABS_PRESSURE] = 1024; + if (!strlen(acecad->name)) + snprintf(acecad->name, sizeof(acecad->name), + "USB Acecad 302 Tablet %04x:%04x", + dev->descriptor.idVendor, dev->descriptor.idProduct); + break; + } + + acecad->dev.absfuzz[ABS_X] = 4; + acecad->dev.absfuzz[ABS_Y] = 4; + + acecad->dev.private = acecad; + acecad->dev.open = usb_acecad_open; + acecad->dev.close = usb_acecad_close; + + acecad->dev.name = acecad->name; + acecad->dev.phys = acecad->phys; + acecad->dev.id.bustype = BUS_USB; + acecad->dev.id.vendor = le16_to_cpu(dev->descriptor.idVendor); + acecad->dev.id.product = le16_to_cpu(dev->descriptor.idProduct); + acecad->dev.id.version = le16_to_cpu(dev->descriptor.bcdDevice); + acecad->dev.dev = &intf->dev; + + usb_fill_int_urb(acecad->irq, dev, pipe, + acecad->data, maxp > 8 ? 8 : maxp, + usb_acecad_irq, acecad, endpoint->bInterval); + acecad->irq->transfer_dma = acecad->data_dma; + acecad->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + input_register_device(&acecad->dev); + + printk(KERN_INFO "input: %s with packet size %d on %s\n", + acecad->name, maxp, path); + + usb_set_intfdata(intf, acecad); + + return 0; + + fail2: usb_buffer_free(dev, 8, acecad->data, acecad->data_dma); + fail1: kfree(acecad); + return -ENOMEM; +} + +static void usb_acecad_disconnect(struct usb_interface *intf) +{ + struct usb_acecad *acecad = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + if (acecad) { + usb_kill_urb(acecad->irq); + input_unregister_device(&acecad->dev); + usb_free_urb(acecad->irq); + usb_buffer_free(interface_to_usbdev(intf), 10, acecad->data, acecad->data_dma); + kfree(acecad); + } +} + +static struct usb_device_id usb_acecad_id_table [] = { + { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_FLAIR), .driver_info = 0 }, + { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_302), .driver_info = 1 }, + { } +}; + +MODULE_DEVICE_TABLE(usb, usb_acecad_id_table); + +static struct usb_driver usb_acecad_driver = { + .owner = THIS_MODULE, + .name = "usb_acecad", + .probe = usb_acecad_probe, + .disconnect = usb_acecad_disconnect, + .id_table = usb_acecad_id_table, +}; + +static int __init usb_acecad_init(void) +{ + int result = usb_register(&usb_acecad_driver); + if (result == 0) + info(DRIVER_VERSION ":" DRIVER_DESC); + return result; +} + +static void __exit usb_acecad_exit(void) +{ + usb_deregister(&usb_acecad_driver); +} + +module_init(usb_acecad_init); +module_exit(usb_acecad_exit); diff --git a/drivers/usb/input/aiptek.c b/drivers/usb/input/aiptek.c index e991f7e..6bb0f25 100644 --- a/drivers/usb/input/aiptek.c +++ b/drivers/usb/input/aiptek.c @@ -1,7 +1,7 @@ /* * Native support for the Aiptek HyperPen USB Tablets * (4000U/5000U/6000U/8000U/12000U) - * + * * Copyright (c) 2001 Chris Atenasio <chris@crud.net> * Copyright (c) 2002-2004 Bryan W. Headley <bwheadley@earthlink.net> * @@ -31,7 +31,7 @@ * - Added support for the sysfs interface, deprecating the * procfs interface for 2.5.x kernel. Also added support for * Wheel command. Bryan W. Headley July-15-2003. - * v1.2 - Reworked jitter timer as a kernel thread. + * v1.2 - Reworked jitter timer as a kernel thread. * Bryan W. Headley November-28-2003/Jan-10-2004. * v1.3 - Repaired issue of kernel thread going nuts on single-processor * machines, introduced programmableDelay as a command line @@ -49,10 +49,10 @@ * NOTE: * This kernel driver is augmented by the "Aiptek" XFree86 input * driver for your X server, as well as the Gaiptek GUI Front-end - * "Tablet Manager". - * These three products are highly interactive with one another, + * "Tablet Manager". + * These three products are highly interactive with one another, * so therefore it's easier to document them all as one subsystem. - * Please visit the project's "home page", located at, + * Please visit the project's "home page", located at, * http://aiptektablet.sourceforge.net. * * This program is free software; you can redistribute it and/or modify @@ -156,7 +156,7 @@ * Command/Data Description Return Bytes Return Value * 0x10/0x00 SwitchToMouse 0 * 0x10/0x01 SwitchToTablet 0 - * 0x18/0x04 SetResolution 0 + * 0x18/0x04 SetResolution 0 * 0x12/0xFF AutoGainOn 0 * 0x17/0x00 FilterOn 0 * 0x01/0x00 GetXExtension 2 MaxX @@ -247,7 +247,7 @@ #define AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE 2 #define AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED 3 - /* Time to wait (in ms) to help mask hand jittering + /* Time to wait (in ms) to help mask hand jittering * when pressing the stylus buttons. */ #define AIPTEK_JITTER_DELAY_DEFAULT 50 @@ -324,7 +324,6 @@ struct aiptek { struct aiptek_settings curSetting; /* tablet's current programmable */ struct aiptek_settings newSetting; /* ... and new param settings */ unsigned int ifnum; /* interface number for IO */ - int openCount; /* module use counter */ int diagnostic; /* tablet diagnostic codes */ unsigned long eventCount; /* event count */ int inDelay; /* jitter: in jitter delay? */ @@ -791,7 +790,7 @@ exit: * specific Aiptek model numbers, because there has been overlaps, * use, and reuse of id's in existing models. Certain models have * been known to use more than one ID, indicative perhaps of - * manufacturing revisions. In any event, we consider these + * manufacturing revisions. In any event, we consider these * IDs to not be model-specific nor unique. */ static const struct usb_device_id aiptek_ids[] = { @@ -814,15 +813,9 @@ static int aiptek_open(struct input_dev *inputdev) { struct aiptek *aiptek = inputdev->private; - if (aiptek->openCount++ > 0) { - return 0; - } - aiptek->urb->dev = aiptek->usbdev; - if (usb_submit_urb(aiptek->urb, GFP_KERNEL) != 0) { - aiptek->openCount--; + if (usb_submit_urb(aiptek->urb, GFP_KERNEL) != 0) return -EIO; - } return 0; } @@ -834,13 +827,11 @@ static void aiptek_close(struct input_dev *inputdev) { struct aiptek *aiptek = inputdev->private; - if (--aiptek->openCount == 0) { - usb_kill_urb(aiptek->urb); - } + usb_kill_urb(aiptek->urb); } /*********************************************************************** - * aiptek_set_report and aiptek_get_report() are borrowed from Linux 2.4.x, + * aiptek_set_report and aiptek_get_report() are borrowed from Linux 2.4.x, * where they were known as usb_set_report and usb_get_report. */ static int @@ -2252,7 +2243,6 @@ static void aiptek_disconnect(struct usb_interface *intf) AIPTEK_PACKET_LENGTH, aiptek->data, aiptek->data_dma); kfree(aiptek); - aiptek = NULL; } } diff --git a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c index 860df26..654ac45 100644 --- a/drivers/usb/input/ati_remote.c +++ b/drivers/usb/input/ati_remote.c @@ -1,15 +1,15 @@ -/* +/* * USB ATI Remote support * * Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net> * Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev * * This 2.2.0 version is a rewrite / cleanup of the 2.1.1 driver, including - * porting to the 2.6 kernel interfaces, along with other modification + * porting to the 2.6 kernel interfaces, along with other modification * to better match the style of the existing usb/input drivers. However, the * protocol and hardware handling is essentially unchanged from 2.1.1. - * - * The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by + * + * The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by * Vojtech Pavlik. * * Changes: @@ -23,64 +23,64 @@ * Added support for the "Lola" remote contributed by: * Seth Cohn <sethcohn@yahoo.com> * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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 + * 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 - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Hardware & software notes * - * These remote controls are distributed by ATI as part of their - * "All-In-Wonder" video card packages. The receiver self-identifies as a + * These remote controls are distributed by ATI as part of their + * "All-In-Wonder" video card packages. The receiver self-identifies as a * "USB Receiver" with manufacturer "X10 Wireless Technology Inc". * - * The "Lola" remote is available from X10. See: + * The "Lola" remote is available from X10. See: * http://www.x10.com/products/lola_sg1.htm * The Lola is similar to the ATI remote but has no mouse support, and slightly * different keys. * - * It is possible to use multiple receivers and remotes on multiple computers + * It is possible to use multiple receivers and remotes on multiple computers * simultaneously by configuring them to use specific channels. - * - * The RF protocol used by the remote supports 16 distinct channels, 1 to 16. - * Actually, it may even support more, at least in some revisions of the + * + * The RF protocol used by the remote supports 16 distinct channels, 1 to 16. + * Actually, it may even support more, at least in some revisions of the * hardware. * * Each remote can be configured to transmit on one channel as follows: - * - Press and hold the "hand icon" button. - * - When the red LED starts to blink, let go of the "hand icon" button. - * - When it stops blinking, input the channel code as two digits, from 01 + * - Press and hold the "hand icon" button. + * - When the red LED starts to blink, let go of the "hand icon" button. + * - When it stops blinking, input the channel code as two digits, from 01 * to 16, and press the hand icon again. - * + * * The timing can be a little tricky. Try loading the module with debug=1 * to have the kernel print out messages about the remote control number * and mask. Note: debugging prints remote numbers as zero-based hexadecimal. * * The driver has a "channel_mask" parameter. This bitmask specifies which - * channels will be ignored by the module. To mask out channels, just add + * channels will be ignored by the module. To mask out channels, just add * all the 2^channel_number values together. * * For instance, set channel_mask = 2^4 = 16 (binary 10000) to make ati_remote - * ignore signals coming from remote controls transmitting on channel 4, but + * ignore signals coming from remote controls transmitting on channel 4, but * accept all other channels. * - * Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be + * Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be * ignored. * - * The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this + * The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this * parameter are unused. * */ @@ -99,13 +99,13 @@ /* * Module and Version Information, Module Parameters */ - -#define ATI_REMOTE_VENDOR_ID 0x0bc7 -#define ATI_REMOTE_PRODUCT_ID 0x004 -#define LOLA_REMOTE_PRODUCT_ID 0x002 + +#define ATI_REMOTE_VENDOR_ID 0x0bc7 +#define ATI_REMOTE_PRODUCT_ID 0x004 +#define LOLA_REMOTE_PRODUCT_ID 0x002 #define MEDION_REMOTE_PRODUCT_ID 0x006 -#define DRIVER_VERSION "2.2.1" +#define DRIVER_VERSION "2.2.1" #define DRIVER_AUTHOR "Torrey Hoffman <thoffman@arnor.net>" #define DRIVER_DESC "ATI/X10 RF USB Remote Control" @@ -113,18 +113,18 @@ #define DATA_BUFSIZE 63 /* size of URB data buffers */ #define ATI_INPUTNUM 1 /* Which input device to register as */ -static unsigned long channel_mask = 0; +static unsigned long channel_mask; module_param(channel_mask, ulong, 0444); MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore"); -static int debug = 0; +static int debug; module_param(debug, int, 0444); MODULE_PARM_DESC(debug, "Enable extra debug messages and information"); #define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0) #undef err #define err(format, arg...) printk(KERN_ERR format , ## arg) - + static struct usb_device_id ati_remote_table[] = { { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) }, { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID) }, @@ -148,7 +148,7 @@ static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 }; /* Acceleration curve for directional control pad */ static char accel[] = { 1, 2, 4, 6, 9, 13, 20 }; -/* Duplicate event filtering time. +/* Duplicate event filtering time. * Sequential, identical KIND_FILTERED inputs with less than * FILTER_TIME jiffies between them are considered as repeat * events. The hardware generates 5 events for the first keypress @@ -161,10 +161,10 @@ static char accel[] = { 1, 2, 4, 6, 9, 13, 20 }; static DECLARE_MUTEX(disconnect_sem); struct ati_remote { - struct input_dev idev; + struct input_dev idev; struct usb_device *udev; struct usb_interface *interface; - + struct urb *irq_urb; struct urb *out_urb; struct usb_endpoint_descriptor *endpoint_in; @@ -174,13 +174,11 @@ struct ati_remote { dma_addr_t inbuf_dma; dma_addr_t outbuf_dma; - int open; /* open counter */ - unsigned char old_data[2]; /* Detect duplicate events */ unsigned long old_jiffies; unsigned long acc_jiffies; /* handle acceleration */ unsigned int repeat_count; - + char name[NAME_BUFSIZE]; char phys[NAME_BUFSIZE]; @@ -206,14 +204,14 @@ static struct int type; unsigned int code; int value; -} ati_remote_tbl[] = +} ati_remote_tbl[] = { /* Directional control pad axes */ {KIND_ACCEL, 0x35, 0x70, EV_REL, REL_X, -1}, /* left */ {KIND_ACCEL, 0x36, 0x71, EV_REL, REL_X, 1}, /* right */ {KIND_ACCEL, 0x37, 0x72, EV_REL, REL_Y, -1}, /* up */ {KIND_ACCEL, 0x38, 0x73, EV_REL, REL_Y, 1}, /* down */ - /* Directional control pad diagonals */ + /* Directional control pad diagonals */ {KIND_LU, 0x39, 0x74, EV_REL, 0, 0}, /* left up */ {KIND_RU, 0x3a, 0x75, EV_REL, 0, 0}, /* right up */ {KIND_LD, 0x3c, 0x77, EV_REL, 0, 0}, /* left down */ @@ -225,7 +223,7 @@ static struct {KIND_LITERAL, 0x41, 0x7c, EV_KEY, BTN_RIGHT, 1},/* right btn down */ {KIND_LITERAL, 0x42, 0x7d, EV_KEY, BTN_RIGHT, 0},/* right btn up */ - /* Artificial "doubleclick" events are generated by the hardware. + /* Artificial "doubleclick" events are generated by the hardware. * They are mapped to the "side" and "extra" mouse buttons here. */ {KIND_FILTERED, 0x3f, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */ {KIND_FILTERED, 0x43, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */ @@ -273,15 +271,15 @@ static struct {KIND_FILTERED, 0xea, 0x25, EV_KEY, KEY_PLAY, 1}, /* ( >) */ {KIND_FILTERED, 0xe9, 0x24, EV_KEY, KEY_REWIND, 1}, /* (<<) */ {KIND_FILTERED, 0xeb, 0x26, EV_KEY, KEY_FORWARD, 1}, /* (>>) */ - {KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1}, /* ([]) */ + {KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1}, /* ([]) */ {KIND_FILTERED, 0xee, 0x29, EV_KEY, KEY_PAUSE, 1}, /* ('') */ {KIND_FILTERED, 0xf0, 0x2b, EV_KEY, KEY_PREVIOUS, 1}, /* (<-) */ {KIND_FILTERED, 0xef, 0x2a, EV_KEY, KEY_NEXT, 1}, /* (>+) */ {KIND_FILTERED, 0xf2, 0x2D, EV_KEY, KEY_INFO, 1}, /* PLAYING */ {KIND_FILTERED, 0xf3, 0x2E, EV_KEY, KEY_HOME, 1}, /* TOP */ {KIND_FILTERED, 0xf4, 0x2F, EV_KEY, KEY_END, 1}, /* END */ - {KIND_FILTERED, 0xf5, 0x30, EV_KEY, KEY_SELECT, 1}, /* SELECT */ - + {KIND_FILTERED, 0xf5, 0x30, EV_KEY, KEY_SELECT, 1}, /* SELECT */ + {KIND_END, 0x00, 0x00, EV_MAX + 1, 0, 0} }; @@ -315,7 +313,7 @@ static void ati_remote_dump(unsigned char *data, unsigned int len) if ((len == 1) && (data[0] != (unsigned char)0xff) && (data[0] != 0x00)) warn("Weird byte 0x%02x", data[0]); else if (len == 4) - warn("Weird key %02x %02x %02x %02x", + warn("Weird key %02x %02x %02x %02x", data[0], data[1], data[2], data[3]); else warn("Weird data, len=%d %02x %02x %02x %02x %02x %02x ...", @@ -328,25 +326,16 @@ static void ati_remote_dump(unsigned char *data, unsigned int len) static int ati_remote_open(struct input_dev *inputdev) { struct ati_remote *ati_remote = inputdev->private; - int retval = 0; - - down(&disconnect_sem); - - if (ati_remote->open++) - goto exit; /* On first open, submit the read urb which was set up previously. */ ati_remote->irq_urb->dev = ati_remote->udev; if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) { - dev_err(&ati_remote->interface->dev, + dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb failed!\n", __FUNCTION__); - ati_remote->open--; - retval = -EIO; + return -EIO; } -exit: - up(&disconnect_sem); - return retval; + return 0; } /* @@ -355,9 +344,8 @@ exit: static void ati_remote_close(struct input_dev *inputdev) { struct ati_remote *ati_remote = inputdev->private; - - if (!--ati_remote->open) - usb_kill_urb(ati_remote->irq_urb); + + usb_kill_urb(ati_remote->irq_urb); } /* @@ -366,13 +354,13 @@ static void ati_remote_close(struct input_dev *inputdev) static void ati_remote_irq_out(struct urb *urb, struct pt_regs *regs) { struct ati_remote *ati_remote = urb->context; - + if (urb->status) { dev_dbg(&ati_remote->interface->dev, "%s: status %d\n", __FUNCTION__, urb->status); return; } - + ati_remote->send_flags |= SEND_FLAG_COMPLETE; wmb(); wake_up(&ati_remote->wait); @@ -380,16 +368,16 @@ static void ati_remote_irq_out(struct urb *urb, struct pt_regs *regs) /* * ati_remote_sendpacket - * + * * Used to send device initialization strings */ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data) { int retval = 0; - + /* Set up out_urb */ memcpy(ati_remote->out_urb->transfer_buffer + 1, data, LO(cmd)); - ((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd); + ((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd); ati_remote->out_urb->transfer_buffer_length = LO(cmd) + 1; ati_remote->out_urb->dev = ati_remote->udev; @@ -397,17 +385,17 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigne retval = usb_submit_urb(ati_remote->out_urb, GFP_ATOMIC); if (retval) { - dev_dbg(&ati_remote->interface->dev, + dev_dbg(&ati_remote->interface->dev, "sendpacket: usb_submit_urb failed: %d\n", retval); return retval; } wait_event_timeout(ati_remote->wait, ((ati_remote->out_urb->status != -EINPROGRESS) || - (ati_remote->send_flags & SEND_FLAG_COMPLETE)), + (ati_remote->send_flags & SEND_FLAG_COMPLETE)), HZ); usb_kill_urb(ati_remote->out_urb); - + return retval; } @@ -419,15 +407,15 @@ static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2) int i; for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) { - /* - * Decide if the table entry matches the remote input. + /* + * Decide if the table entry matches the remote input. */ if ((((ati_remote_tbl[i].data1 & 0x0f) == (d1 & 0x0f))) && - ((((ati_remote_tbl[i].data1 >> 4) - - (d1 >> 4) + rem) & 0x0f) == 0x0f) && + ((((ati_remote_tbl[i].data1 >> 4) - + (d1 >> 4) + rem) & 0x0f) == 0x0f) && (ati_remote_tbl[i].data2 == d2)) return i; - + } return -1; } @@ -435,16 +423,16 @@ static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2) /* * ati_remote_report_input */ -static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) +static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) { struct ati_remote *ati_remote = urb->context; unsigned char *data= ati_remote->inbuf; - struct input_dev *dev = &ati_remote->idev; + struct input_dev *dev = &ati_remote->idev; int index, acc; int remote_num; - + /* Deal with strange looking inputs */ - if ( (urb->actual_length != 4) || (data[0] != 0x14) || + if ( (urb->actual_length != 4) || (data[0] != 0x14) || ((data[3] & 0x0f) != 0x00) ) { ati_remote_dump(data, urb->actual_length); return; @@ -453,7 +441,7 @@ static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) /* Mask unwanted remote channels. */ /* note: remote_num is 0-based, channel 1 on remote == 0 here */ remote_num = (data[3] >> 4) & 0x0f; - if (channel_mask & (1 << (remote_num + 1))) { + if (channel_mask & (1 << (remote_num + 1))) { dbginfo(&ati_remote->interface->dev, "Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n", remote_num, data[1], data[2], channel_mask); @@ -463,37 +451,36 @@ static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) /* Look up event code index in translation table */ index = ati_remote_event_lookup(remote_num, data[1], data[2]); if (index < 0) { - dev_warn(&ati_remote->interface->dev, - "Unknown input from channel 0x%02x: data %02x,%02x\n", + dev_warn(&ati_remote->interface->dev, + "Unknown input from channel 0x%02x: data %02x,%02x\n", remote_num, data[1], data[2]); return; - } - dbginfo(&ati_remote->interface->dev, + } + dbginfo(&ati_remote->interface->dev, "channel 0x%02x; data %02x,%02x; index %d; keycode %d\n", remote_num, data[1], data[2], index, ati_remote_tbl[index].code); - + if (ati_remote_tbl[index].kind == KIND_LITERAL) { input_regs(dev, regs); input_event(dev, ati_remote_tbl[index].type, ati_remote_tbl[index].code, ati_remote_tbl[index].value); input_sync(dev); - + ati_remote->old_jiffies = jiffies; return; } - + if (ati_remote_tbl[index].kind == KIND_FILTERED) { /* Filter duplicate events which happen "too close" together. */ - if ((ati_remote->old_data[0] == data[1]) && - (ati_remote->old_data[1] == data[2]) && - ((ati_remote->old_jiffies + FILTER_TIME) > jiffies)) { + if ((ati_remote->old_data[0] == data[1]) && + (ati_remote->old_data[1] == data[2]) && + ((ati_remote->old_jiffies + FILTER_TIME) > jiffies)) { ati_remote->repeat_count++; - } - else { + } else { ati_remote->repeat_count = 0; } - + ati_remote->old_data[0] = data[1]; ati_remote->old_data[1] = data[2]; ati_remote->old_jiffies = jiffies; @@ -501,7 +488,7 @@ static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) if ((ati_remote->repeat_count > 0) && (ati_remote->repeat_count < 5)) return; - + input_regs(dev, regs); input_event(dev, ati_remote_tbl[index].type, @@ -511,13 +498,13 @@ static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) input_sync(dev); return; - } - - /* + } + + /* * Other event kinds are from the directional control pad, and have an * acceleration factor applied to them. Without this acceleration, the * control pad is mostly unusable. - * + * * If elapsed time since last event is > 1/4 second, user "stopped", * so reset acceleration. Otherwise, user is probably holding the control * pad down, so we increase acceleration, ramping up over two seconds to @@ -559,7 +546,7 @@ static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) input_report_rel(dev, REL_Y, acc); break; default: - dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n", + dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n", ati_remote_tbl[index].kind); } input_sync(dev); @@ -586,12 +573,12 @@ static void ati_remote_irq_in(struct urb *urb, struct pt_regs *regs) case -ESHUTDOWN: dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n", __FUNCTION__); - return; + return; default: /* error */ - dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n", + dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n", __FUNCTION__, urb->status); } - + retval = usb_submit_urb(urb, SLAB_ATOMIC); if (retval) dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n", @@ -603,8 +590,6 @@ static void ati_remote_irq_in(struct urb *urb, struct pt_regs *regs) */ static void ati_remote_delete(struct ati_remote *ati_remote) { - if (!ati_remote) return; - if (ati_remote->irq_urb) usb_kill_urb(ati_remote->irq_urb); @@ -614,16 +599,16 @@ static void ati_remote_delete(struct ati_remote *ati_remote) input_unregister_device(&ati_remote->idev); if (ati_remote->inbuf) - usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, + usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, ati_remote->inbuf, ati_remote->inbuf_dma); - + if (ati_remote->outbuf) - usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, + usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, ati_remote->outbuf, ati_remote->outbuf_dma); - + if (ati_remote->irq_urb) usb_free_urb(ati_remote->irq_urb); - + if (ati_remote->out_urb) usb_free_urb(ati_remote->out_urb); @@ -636,51 +621,52 @@ static void ati_remote_input_init(struct ati_remote *ati_remote) int i; idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); - idev->keybit[LONG(BTN_MOUSE)] = ( BIT(BTN_LEFT) | BIT(BTN_RIGHT) | + idev->keybit[LONG(BTN_MOUSE)] = ( BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_SIDE) | BIT(BTN_EXTRA) ); idev->relbit[0] = BIT(REL_X) | BIT(REL_Y); for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) if (ati_remote_tbl[i].type == EV_KEY) set_bit(ati_remote_tbl[i].code, idev->keybit); - + idev->private = ati_remote; idev->open = ati_remote_open; idev->close = ati_remote_close; - + idev->name = ati_remote->name; idev->phys = ati_remote->phys; - - idev->id.bustype = BUS_USB; + + idev->id.bustype = BUS_USB; idev->id.vendor = le16_to_cpu(ati_remote->udev->descriptor.idVendor); idev->id.product = le16_to_cpu(ati_remote->udev->descriptor.idProduct); idev->id.version = le16_to_cpu(ati_remote->udev->descriptor.bcdDevice); + idev->dev = &(ati_remote->udev->dev); } static int ati_remote_initialize(struct ati_remote *ati_remote) { struct usb_device *udev = ati_remote->udev; int pipe, maxp; - + init_waitqueue_head(&ati_remote->wait); /* Set up irq_urb */ pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress); maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp; - - usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf, - maxp, ati_remote_irq_in, ati_remote, + + usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf, + maxp, ati_remote_irq_in, ati_remote, ati_remote->endpoint_in->bInterval); ati_remote->irq_urb->transfer_dma = ati_remote->inbuf_dma; ati_remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - + /* Set up out_urb */ pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress); maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp; - usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf, - maxp, ati_remote_irq_out, ati_remote, + usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf, + maxp, ati_remote_irq_out, ati_remote, ati_remote->endpoint_out->bInterval); ati_remote->out_urb->transfer_dma = ati_remote->outbuf_dma; ati_remote->out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; @@ -688,11 +674,11 @@ static int ati_remote_initialize(struct ati_remote *ati_remote) /* send initialization strings */ if ((ati_remote_sendpacket(ati_remote, 0x8004, init1)) || (ati_remote_sendpacket(ati_remote, 0x8007, init2))) { - dev_err(&ati_remote->interface->dev, + dev_err(&ati_remote->interface->dev, "Initializing ati_remote hardware failed.\n"); return 1; } - + return 0; } @@ -769,7 +755,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de if (!strlen(ati_remote->name)) sprintf(ati_remote->name, DRIVER_DESC "(%04x,%04x)", - le16_to_cpu(ati_remote->udev->descriptor.idVendor), + le16_to_cpu(ati_remote->udev->descriptor.idVendor), le16_to_cpu(ati_remote->udev->descriptor.idProduct)); /* Device Hardware Initialization - fills in ati_remote->idev from udev. */ @@ -781,11 +767,11 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de ati_remote_input_init(ati_remote); input_register_device(&ati_remote->idev); - dev_info(&ati_remote->interface->dev, "Input registered: %s on %s\n", + dev_info(&ati_remote->interface->dev, "Input registered: %s on %s\n", ati_remote->name, path); usb_set_intfdata(interface, ati_remote); - + error: if (retval) ati_remote_delete(ati_remote); @@ -800,18 +786,14 @@ static void ati_remote_disconnect(struct usb_interface *interface) { struct ati_remote *ati_remote; - down(&disconnect_sem); - ati_remote = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); if (!ati_remote) { warn("%s - null device?\n", __FUNCTION__); return; } - - ati_remote_delete(ati_remote); - up(&disconnect_sem); + ati_remote_delete(ati_remote); } /* @@ -820,7 +802,7 @@ static void ati_remote_disconnect(struct usb_interface *interface) static int __init ati_remote_init(void) { int result; - + result = usb_register(&ati_remote_driver); if (result) err("usb_register error #%d\n", result); @@ -838,8 +820,8 @@ static void __exit ati_remote_exit(void) usb_deregister(&ati_remote_driver); } -/* - * module specification +/* + * module specification */ module_init(ati_remote_init); diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 740dec1..100b49bd 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -232,7 +232,7 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign report->size += parser->global.report_size * parser->global.report_count; if (!parser->local.usage_index) /* Ignore padding fields */ - return 0; + return 0; usages = max_t(int, parser->local.usage_index, parser->global.report_count); @@ -765,7 +765,7 @@ static __inline__ __u32 s32ton(__s32 value, unsigned n) static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n) { report += (offset >> 5) << 2; offset &= 31; - return (le64_to_cpu(get_unaligned((__le64*)report)) >> offset) & ((1 << n) - 1); + return (le64_to_cpu(get_unaligned((__le64*)report)) >> offset) & ((1ULL << n) - 1); } static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value) @@ -1233,6 +1233,13 @@ int hid_wait_io(struct hid_device *hid) return 0; } +static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (idle << 8) | report, + ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT); +} + static int hid_get_class_descriptor(struct usb_device *dev, int ifnum, unsigned char type, void *buf, int size) { @@ -1301,10 +1308,6 @@ void hid_init_reports(struct hid_device *hid) if (err) warn("timeout initializing reports\n"); - - usb_control_msg(hid->dev, usb_sndctrlpipe(hid->dev, 0), - HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, - hid->ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT); } #define USB_VENDOR_ID_WACOM 0x056a @@ -1318,6 +1321,10 @@ void hid_init_reports(struct hid_device *hid) #define USB_DEVICE_ID_WACOM_INTUOS3 0x00B0 #define USB_DEVICE_ID_WACOM_CINTIQ 0x003F +#define USB_VENDOR_ID_ACECAD 0x0460 +#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004 +#define USB_DEVICE_ID_ACECAD_302 0x0008 + #define USB_VENDOR_ID_KBGEAR 0x084e #define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 @@ -1502,6 +1509,9 @@ static struct hid_blacklist { { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, @@ -1590,6 +1600,8 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) return NULL; } + hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0); + if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) { dbg("reading report descriptor failed"); kfree(rdesc); @@ -1635,7 +1647,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) /* Change the polling interval of mice. */ if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0) interval = hid_mousepoll_interval; - + if (endpoint->bEndpointAddress & USB_DIR_IN) { if (hid->urbin) continue; diff --git a/drivers/usb/input/hid-debug.h b/drivers/usb/input/hid-debug.h index 2b91705..52437e5 100644 --- a/drivers/usb/input/hid-debug.h +++ b/drivers/usb/input/hid-debug.h @@ -67,7 +67,7 @@ static const struct hid_usage_entry hid_usage_table[] = { {0, 0x44, "Vbry"}, {0, 0x45, "Vbrz"}, {0, 0x46, "Vno"}, - {0, 0x80, "SystemControl"}, + {0, 0x80, "SystemControl"}, {0, 0x81, "SystemPowerDown"}, {0, 0x82, "SystemSleep"}, {0, 0x83, "SystemWakeUp"}, @@ -347,7 +347,7 @@ __inline__ static void tab(int n) { static void hid_dump_field(struct hid_field *field, int n) { int j; - + if (field->physical) { tab(n); printk("Physical("); @@ -408,7 +408,7 @@ static void hid_dump_field(struct hid_field *field, int n) { printk("%s", units[sys][i]); if(nibble != 1) { /* This is a _signed_ nibble(!) */ - + int val = nibble & 0x7; if(nibble & 0x08) val = -((0x7 & ~val) +1); @@ -443,7 +443,7 @@ static void __attribute__((unused)) hid_dump_device(struct hid_device *device) { struct list_head *list; unsigned i,k; static char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; - + for (i = 0; i < HID_REPORT_TYPES; i++) { report_enum = device->report_enum + i; list = report_enum->report_list.next; @@ -664,8 +664,8 @@ static char *keys[KEY_MAX + 1] = { static char *relatives[REL_MAX + 1] = { [REL_X] = "X", [REL_Y] = "Y", [REL_Z] = "Z", [REL_HWHEEL] = "HWheel", - [REL_DIAL] = "Dial", [REL_WHEEL] = "Wheel", - [REL_MISC] = "Misc", + [REL_DIAL] = "Dial", [REL_WHEEL] = "Wheel", + [REL_MISC] = "Misc", }; static char *absolutes[ABS_MAX + 1] = { @@ -690,9 +690,9 @@ static char *misc[MSC_MAX + 1] = { }; static char *leds[LED_MAX + 1] = { - [LED_NUML] = "NumLock", [LED_CAPSL] = "CapsLock", + [LED_NUML] = "NumLock", [LED_CAPSL] = "CapsLock", [LED_SCROLLL] = "ScrollLock", [LED_COMPOSE] = "Compose", - [LED_KANA] = "Kana", [LED_SLEEP] = "Sleep", + [LED_KANA] = "Kana", [LED_SLEEP] = "Sleep", [LED_SUSPEND] = "Suspend", [LED_MUTE] = "Mute", [LED_MISC] = "Misc", }; diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c index 5553c35..9ac1e90 100644 --- a/drivers/usb/input/hid-input.c +++ b/drivers/usb/input/hid-input.c @@ -164,7 +164,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_GD_X: case HID_GD_Y: case HID_GD_Z: case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ: case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL: - if (field->flags & HID_MAIN_ITEM_RELATIVE) + if (field->flags & HID_MAIN_ITEM_RELATIVE) map_rel(usage->hid & 0xf); else map_abs(usage->hid & 0xf); @@ -297,7 +297,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_UP_MSVENDOR: goto ignore; - + case HID_UP_PID: set_bit(EV_FF, input->evbit); @@ -349,7 +349,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel goto ignore; if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | HID_QUIRK_2WHEEL_MOUSE_HACK_5)) && - (usage->type == EV_REL) && (usage->code == REL_WHEEL)) + (usage->type == EV_REL) && (usage->code == REL_WHEEL)) set_bit(REL_HWHEEL, bit); if (((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005)) @@ -365,11 +365,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel a = field->logical_minimum = 0; b = field->logical_maximum = 255; } - + if (field->application == HID_GD_GAMEPAD || field->application == HID_GD_JOYSTICK) input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4); else input_set_abs_params(input, usage->code, a, b, 0, 0); - + } if (usage->hat_min < usage->hat_max || usage->hat_dir) { @@ -420,7 +420,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; } - if (usage->hat_min < usage->hat_max || usage->hat_dir) { + if (usage->hat_min < usage->hat_max || usage->hat_dir) { int hat_dir = usage->hat_dir; if (!hat_dir) hat_dir = (value - usage->hat_min) * 8 / (usage->hat_max - usage->hat_min + 1) + 1; @@ -551,7 +551,7 @@ int hidinput_connect(struct hid_device *hid) for (i = 0; i < hid->maxcollection; i++) if (hid->collection[i].type == HID_COLLECTION_APPLICATION || hid->collection[i].type == HID_COLLECTION_PHYSICAL) - if (IS_INPUT_APPLICATION(hid->collection[i].usage)) + if (IS_INPUT_APPLICATION(hid->collection[i].usage)) break; if (i == hid->maxcollection) @@ -592,7 +592,7 @@ int hidinput_connect(struct hid_device *hid) for (j = 0; j < report->field[i]->maxusage; j++) hidinput_configure_usage(hidinput, report->field[i], report->field[i]->usage + j); - + if (hid->quirks & HID_QUIRK_MULTI_INPUT) { /* This will leave hidinput NULL, so that it * allocates another one if we have more inputs on diff --git a/drivers/usb/input/hid-lgff.c b/drivers/usb/input/hid-lgff.c index 0d7404b..0c4c77a 100644 --- a/drivers/usb/input/hid-lgff.c +++ b/drivers/usb/input/hid-lgff.c @@ -94,7 +94,7 @@ struct lgff_device { isn't really necessary */ unsigned long flags[1]; /* Contains various information about the - state of the driver for this device */ + state of the driver for this device */ struct timer_list timer; }; @@ -234,7 +234,7 @@ static struct hid_report* hid_lgff_duplicate_report(struct hid_report* report) kfree(ret); return NULL; } - memset(ret->field[0]->value, 0, sizeof(s32[8])); + memset(ret->field[0]->value, 0, sizeof(s32[8])); return ret; } @@ -295,11 +295,11 @@ static int hid_lgff_event(struct hid_device *hid, struct input_dev* input, unsigned long flags; if (type != EV_FF) return -EINVAL; - if (!LGFF_CHECK_OWNERSHIP(code, lgff)) return -EACCES; + if (!LGFF_CHECK_OWNERSHIP(code, lgff)) return -EACCES; if (value < 0) return -EINVAL; spin_lock_irqsave(&lgff->lock, flags); - + if (value > 0) { if (test_bit(EFFECT_STARTED, effect->flags)) { spin_unlock_irqrestore(&lgff->lock, flags); @@ -345,7 +345,7 @@ static int hid_lgff_flush(struct input_dev *dev, struct file *file) and perform ioctls on the same fd all at the same time */ if ( current->pid == lgff->effects[i].owner && test_bit(EFFECT_USED, lgff->effects[i].flags)) { - + if (hid_lgff_erase(dev, i)) warn("erase effect %d failed", i); } @@ -378,7 +378,7 @@ static int hid_lgff_upload_effect(struct input_dev* input, struct lgff_effect new; int id; unsigned long flags; - + dbg("ioctl rumble"); if (!test_bit(effect->type, input->ffbit)) return -EINVAL; @@ -441,7 +441,7 @@ static void hid_lgff_timer(unsigned long timer_data) spin_lock_irqsave(&lgff->lock, flags); - for (i=0; i<LGFF_EFFECTS; ++i) { + for (i=0; i<LGFF_EFFECTS; ++i) { struct lgff_effect* effect = lgff->effects +i; if (test_bit(EFFECT_PLAYING, effect->flags)) { @@ -491,7 +491,7 @@ static void hid_lgff_timer(unsigned long timer_data) set_bit(EFFECT_PLAYING, lgff->effects[i].flags); } } - } + } #define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff @@ -524,5 +524,5 @@ static void hid_lgff_timer(unsigned long timer_data) add_timer(&lgff->timer); } - spin_unlock_irqrestore(&lgff->lock, flags); + spin_unlock_irqrestore(&lgff->lock, flags); } diff --git a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h index 6d9329c..c1b6b69 100644 --- a/drivers/usb/input/hid.h +++ b/drivers/usb/input/hid.h @@ -118,7 +118,7 @@ struct hid_item { #define HID_MAIN_ITEM_CONSTANT 0x001 #define HID_MAIN_ITEM_VARIABLE 0x002 #define HID_MAIN_ITEM_RELATIVE 0x004 -#define HID_MAIN_ITEM_WRAP 0x008 +#define HID_MAIN_ITEM_WRAP 0x008 #define HID_MAIN_ITEM_NONLINEAR 0x010 #define HID_MAIN_ITEM_NO_PREFERRED 0x020 #define HID_MAIN_ITEM_NULL_STATE 0x040 @@ -172,14 +172,14 @@ struct hid_item { #define HID_USAGE_PAGE 0xffff0000 #define HID_UP_UNDEFINED 0x00000000 -#define HID_UP_GENDESK 0x00010000 -#define HID_UP_KEYBOARD 0x00070000 -#define HID_UP_LED 0x00080000 -#define HID_UP_BUTTON 0x00090000 -#define HID_UP_ORDINAL 0x000a0000 +#define HID_UP_GENDESK 0x00010000 +#define HID_UP_KEYBOARD 0x00070000 +#define HID_UP_LED 0x00080000 +#define HID_UP_BUTTON 0x00090000 +#define HID_UP_ORDINAL 0x000a0000 #define HID_UP_CONSUMER 0x000c0000 -#define HID_UP_DIGITIZER 0x000d0000 -#define HID_UP_PID 0x000f0000 +#define HID_UP_DIGITIZER 0x000d0000 +#define HID_UP_PID 0x000f0000 #define HID_UP_HPVENDOR 0xff7f0000 #define HID_UP_MSVENDOR 0xff000000 @@ -406,7 +406,7 @@ struct hid_device { /* device report descriptor */ dma_addr_t outbuf_dma; /* Output buffer dma */ spinlock_t outlock; /* Output fifo spinlock */ - unsigned claimed; /* Claimed by hidinput, hiddev? */ + unsigned claimed; /* Claimed by hidinput, hiddev? */ unsigned quirks; /* Various quirks the device can pull on us */ struct list_head inputs; /* The list of inputs */ diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c index 96b7c90..4c13331 100644 --- a/drivers/usb/input/hiddev.c +++ b/drivers/usb/input/hiddev.c @@ -95,7 +95,7 @@ hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo) return NULL; rinfo->report_id = ((struct hid_report *) list)->id; break; - + case HID_REPORT_ID_NEXT: list = (struct list_head *) report_enum->report_id_hash[rinfo->report_id & HID_REPORT_ID_MASK]; @@ -106,7 +106,7 @@ hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo) return NULL; rinfo->report_id = ((struct hid_report *) list)->id; break; - + default: return NULL; } @@ -158,7 +158,7 @@ static void hiddev_send_event(struct hid_device *hid, if (uref->field_index != HID_FIELD_INDEX_NONE || (list->flags & HIDDEV_FLAG_REPORT) != 0) { list->buffer[list->head] = *uref; - list->head = (list->head + 1) & + list->head = (list->head + 1) & (HIDDEV_BUFFER_SIZE - 1); kill_fasync(&list->fasync, SIGIO, POLL_IN); } @@ -179,9 +179,9 @@ void hiddev_hid_event(struct hid_device *hid, struct hid_field *field, unsigned type = field->report_type; struct hiddev_usage_ref uref; - uref.report_type = + uref.report_type = (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : - ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : + ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0)); uref.report_id = field->report->id; uref.field_index = field->index; @@ -199,9 +199,9 @@ void hiddev_report_event(struct hid_device *hid, struct hid_report *report) struct hiddev_usage_ref uref; memset(&uref, 0, sizeof(uref)); - uref.report_type = + uref.report_type = (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : - ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : + ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0)); uref.report_id = report->id; uref.field_index = HID_FIELD_INDEX_NONE; @@ -236,7 +236,7 @@ static int hiddev_release(struct inode * inode, struct file * file) *listptr = (*listptr)->next; if (!--list->hiddev->open) { - if (list->hiddev->exist) + if (list->hiddev->exist) hid_close(list->hiddev->hid); else kfree(list->hiddev); @@ -303,7 +303,7 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun if (list->head == list->tail) { add_wait_queue(&list->hiddev->wait, &wait); set_current_state(TASK_INTERRUPTIBLE); - + while (list->head == list->tail) { if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; @@ -317,7 +317,7 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun retval = -EIO; break; } - + schedule(); } @@ -329,7 +329,7 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun return retval; - while (list->head != list->tail && + while (list->head != list->tail && retval + event_size <= count) { if ((list->flags & HIDDEV_FLAG_UREF) == 0) { if (list->buffer[list->tail].field_index != @@ -405,10 +405,10 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd return -EINVAL; for (i = 0; i < hid->maxcollection; i++) - if (hid->collection[i].type == + if (hid->collection[i].type == HID_COLLECTION_APPLICATION && arg-- == 0) break; - + if (i == hid->maxcollection) return -EINVAL; @@ -562,7 +562,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd if (!uref_multi) return -ENOMEM; uref = &uref_multi->uref; - if (copy_from_user(uref, user_arg, sizeof(*uref))) + if (copy_from_user(uref, user_arg, sizeof(*uref))) goto fault; rinfo.report_type = uref->report_type; @@ -595,7 +595,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd return -ENOMEM; uref = &uref_multi->uref; if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { - if (copy_from_user(uref_multi, user_arg, + if (copy_from_user(uref_multi, user_arg, sizeof(*uref_multi))) goto fault; } else { @@ -603,7 +603,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd goto fault; } - if (cmd != HIDIOCGUSAGE && + if (cmd != HIDIOCGUSAGE && cmd != HIDIOCGUSAGES && uref->report_type == HID_REPORT_TYPE_INPUT) goto inval; @@ -651,16 +651,16 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd return field->usage[uref->usage_index].collection_index; case HIDIOCGUSAGES: for (i = 0; i < uref_multi->num_values; i++) - uref_multi->values[i] = + uref_multi->values[i] = field->value[uref->usage_index + i]; - if (copy_to_user(user_arg, uref_multi, + if (copy_to_user(user_arg, uref_multi, sizeof(*uref_multi))) goto fault; goto goodreturn; case HIDIOCSUSAGES: for (i = 0; i < uref_multi->num_values; i++) - field->value[uref->usage_index + i] = - uref_multi->values[i]; + field->value[uref->usage_index + i] = + uref_multi->values[i]; goto goodreturn; } @@ -670,7 +670,7 @@ goodreturn: fault: kfree(uref_multi); return -EFAULT; -inval: +inval: kfree(uref_multi); return -EINVAL; @@ -734,7 +734,7 @@ static struct usb_class_driver hiddev_class = { .name = "usb/hid/hiddev%d", .fops = &hiddev_fops, .mode = S_IFCHR | S_IRUGO | S_IWUSR, - .minor_base = HIDDEV_MINOR_BASE, + .minor_base = HIDDEV_MINOR_BASE, }; /* @@ -747,7 +747,7 @@ int hiddev_connect(struct hid_device *hid) int retval; for (i = 0; i < hid->maxcollection; i++) - if (hid->collection[i].type == + if (hid->collection[i].type == HID_COLLECTION_APPLICATION && !IS_INPUT_APPLICATION(hid->collection[i].usage)) break; @@ -755,11 +755,11 @@ int hiddev_connect(struct hid_device *hid) if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0) return -1; - if (!(hiddev = kmalloc(sizeof(struct hiddev), GFP_KERNEL))) + if (!(hiddev = kmalloc(sizeof(struct hiddev), GFP_KERNEL))) return -1; memset(hiddev, 0, sizeof(struct hiddev)); - retval = usb_register_dev(hid->intf, &hiddev_class); + retval = usb_register_dev(hid->intf, &hiddev_class); if (retval) { err("Not able to get a minor for this device."); kfree(hiddev); @@ -768,12 +768,12 @@ int hiddev_connect(struct hid_device *hid) init_waitqueue_head(&hiddev->wait); - hiddev_table[hid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; + hiddev_table[hid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; hiddev->hid = hid; hiddev->exist = 1; - hid->minor = hid->intf->minor; + hid->minor = hid->intf->minor; hid->hiddev = hiddev; return 0; @@ -818,7 +818,7 @@ void hiddev_disconnect(struct hid_device *hid) /* We never attach in this manner, and rely on HID to connect us. This * is why there is no disconnect routine defined in the usb_driver either. */ -static int hiddev_usbd_probe(struct usb_interface *intf, +static int hiddev_usbd_probe(struct usb_interface *intf, const struct usb_device_id *hiddev_info) { return -ENODEV; diff --git a/drivers/usb/input/itmtouch.c b/drivers/usb/input/itmtouch.c new file mode 100644 index 0000000..47dec6a --- /dev/null +++ b/drivers/usb/input/itmtouch.c @@ -0,0 +1,268 @@ +/****************************************************************************** + * itmtouch.c -- Driver for ITM touchscreen panel + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Based upon original work by Chris Collins <xfire-itmtouch@xware.cx>. + * + * Kudos to ITM for providing me with the datasheet for the panel, + * even though it was a day later than I had finished writing this + * driver. + * + * It has meant that I've been able to correct my interpretation of the + * protocol packets however. + * + * CC -- 2003/9/29 + * + * History + * 1.0 & 1.1 2003 (CC) vojtech@suse.cz + * Original version for 2.4.x kernels + * + * 1.2 02/03/2005 (HCE) hc@mivu.no + * Complete rewrite to support Linux 2.6.10, thanks to mtouchusb.c for hints. + * Unfortunately no calibration support at this time. + * + * 1.2.1 09/03/2005 (HCE) hc@mivu.no + * Code cleanup and adjusting syntax to start matching kernel standards + * + *****************************************************************************/ + +#include <linux/config.h> + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> + +/* only an 8 byte buffer necessary for a single packet */ +#define ITM_BUFSIZE 8 +#define PATH_SIZE 64 + +#define USB_VENDOR_ID_ITMINC 0x0403 +#define USB_PRODUCT_ID_TOUCHPANEL 0xf9e9 + +#define DRIVER_AUTHOR "Hans-Christian Egtvedt <hc@mivu.no>" +#define DRIVER_VERSION "v1.2.1" +#define DRIVER_DESC "USB ITM Inc Touch Panel Driver" +#define DRIVER_LICENSE "GPL" + +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_LICENSE( DRIVER_LICENSE ); + +struct itmtouch_dev { + struct usb_device *usbdev; /* usb device */ + struct input_dev inputdev; /* input device */ + struct urb *readurb; /* urb */ + char rbuf[ITM_BUFSIZE]; /* data */ + int users; + char name[128]; + char phys[64]; +}; + +static struct usb_device_id itmtouch_ids [] = { + { USB_DEVICE(USB_VENDOR_ID_ITMINC, USB_PRODUCT_ID_TOUCHPANEL) }, + { } +}; + +static void itmtouch_irq(struct urb *urb, struct pt_regs *regs) +{ + struct itmtouch_dev * itmtouch = urb->context; + unsigned char *data = urb->transfer_buffer; + struct input_dev *dev = &itmtouch->inputdev; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ETIMEDOUT: + /* this urb is timing out */ + dbg("%s - urb timed out - was the device unplugged?", + __FUNCTION__); + return; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); + goto exit; + } + + input_regs(dev, regs); + + /* if pressure has been released, then don't report X/Y */ + if (data[7] & 0x20) { + input_report_abs(dev, ABS_X, (data[0] & 0x1F) << 7 | (data[3] & 0x7F)); + input_report_abs(dev, ABS_Y, (data[1] & 0x1F) << 7 | (data[4] & 0x7F)); + } + + input_report_abs(dev, ABS_PRESSURE, (data[2] & 1) << 7 | (data[5] & 0x7F)); + input_report_key(dev, BTN_TOUCH, ~data[7] & 0x20); + input_sync(dev); + +exit: + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) + printk(KERN_ERR "%s - usb_submit_urb failed with result: %d", + __FUNCTION__, retval); +} + +static int itmtouch_open(struct input_dev *input) +{ + struct itmtouch_dev *itmtouch = input->private; + + itmtouch->readurb->dev = itmtouch->usbdev; + + if (usb_submit_urb(itmtouch->readurb, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void itmtouch_close(struct input_dev *input) +{ + struct itmtouch_dev *itmtouch = input->private; + + usb_kill_urb(itmtouch->readurb); +} + +static int itmtouch_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct itmtouch_dev *itmtouch; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_device *udev = interface_to_usbdev(intf); + unsigned int pipe; + unsigned int maxp; + char path[PATH_SIZE]; + + interface = intf->cur_altsetting; + endpoint = &interface->endpoint[0].desc; + + if (!(itmtouch = kcalloc(1, sizeof(struct itmtouch_dev), GFP_KERNEL))) { + err("%s - Out of memory.", __FUNCTION__); + return -ENOMEM; + } + + itmtouch->usbdev = udev; + + itmtouch->inputdev.private = itmtouch; + itmtouch->inputdev.open = itmtouch_open; + itmtouch->inputdev.close = itmtouch_close; + + usb_make_path(udev, path, PATH_SIZE); + + itmtouch->inputdev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + itmtouch->inputdev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE); + itmtouch->inputdev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + + itmtouch->inputdev.name = itmtouch->name; + itmtouch->inputdev.phys = itmtouch->phys; + itmtouch->inputdev.id.bustype = BUS_USB; + itmtouch->inputdev.id.vendor = udev->descriptor.idVendor; + itmtouch->inputdev.id.product = udev->descriptor.idProduct; + itmtouch->inputdev.id.version = udev->descriptor.bcdDevice; + itmtouch->inputdev.dev = &intf->dev; + + if (!strlen(itmtouch->name)) + sprintf(itmtouch->name, "USB ITM touchscreen"); + + /* device limits */ + /* as specified by the ITM datasheet, X and Y are 12bit, + * Z (pressure) is 8 bit. However, the fields are defined up + * to 14 bits for future possible expansion. + */ + input_set_abs_params(&itmtouch->inputdev, ABS_X, 0, 0x0FFF, 2, 0); + input_set_abs_params(&itmtouch->inputdev, ABS_Y, 0, 0x0FFF, 2, 0); + input_set_abs_params(&itmtouch->inputdev, ABS_PRESSURE, 0, 0xFF, 2, 0); + + /* initialise the URB so we can read from the transport stream */ + pipe = usb_rcvintpipe(itmtouch->usbdev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + + if (maxp > ITM_BUFSIZE) + maxp = ITM_BUFSIZE; + + itmtouch->readurb = usb_alloc_urb(0, GFP_KERNEL); + + if (!itmtouch->readurb) { + dbg("%s - usb_alloc_urb failed: itmtouch->readurb", __FUNCTION__); + kfree(itmtouch); + return -ENOMEM; + } + + usb_fill_int_urb(itmtouch->readurb, itmtouch->usbdev, pipe, itmtouch->rbuf, + maxp, itmtouch_irq, itmtouch, endpoint->bInterval); + + input_register_device(&itmtouch->inputdev); + + printk(KERN_INFO "itmtouch: %s registered on %s\n", itmtouch->name, path); + usb_set_intfdata(intf, itmtouch); + + return 0; +} + +static void itmtouch_disconnect(struct usb_interface *intf) +{ + struct itmtouch_dev *itmtouch = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (itmtouch) { + input_unregister_device(&itmtouch->inputdev); + usb_kill_urb(itmtouch->readurb); + usb_free_urb(itmtouch->readurb); + kfree(itmtouch); + } +} + +MODULE_DEVICE_TABLE(usb, itmtouch_ids); + +static struct usb_driver itmtouch_driver = { + .owner = THIS_MODULE, + .name = "itmtouch", + .probe = itmtouch_probe, + .disconnect = itmtouch_disconnect, + .id_table = itmtouch_ids, +}; + +static int __init itmtouch_init(void) +{ + info(DRIVER_DESC " " DRIVER_VERSION); + info(DRIVER_AUTHOR); + return usb_register(&itmtouch_driver); +} + +static void __exit itmtouch_exit(void) +{ + usb_deregister(&itmtouch_driver); +} + +module_init(itmtouch_init); +module_exit(itmtouch_exit); diff --git a/drivers/usb/input/kbtab.c b/drivers/usb/input/kbtab.c index a68c5b4..d2f0f90 100644 --- a/drivers/usb/input/kbtab.c +++ b/drivers/usb/input/kbtab.c @@ -36,7 +36,6 @@ struct kbtab { struct input_dev dev; struct usb_device *usbdev; struct urb *irq; - int open; int x, y; int button; int pressure; @@ -79,12 +78,12 @@ static void kbtab_irq(struct urb *urb, struct pt_regs *regs) /*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/ input_report_key(dev, BTN_RIGHT, data[0] & 0x02); - if( -1 == kb_pressure_click){ + if (-1 == kb_pressure_click) { input_report_abs(dev, ABS_PRESSURE, kbtab->pressure); } else { input_report_key(dev, BTN_LEFT, (kbtab->pressure > kb_pressure_click) ? 1 : 0); }; - + input_sync(dev); exit: @@ -105,14 +104,9 @@ static int kbtab_open(struct input_dev *dev) { struct kbtab *kbtab = dev->private; - if (kbtab->open++) - return 0; - kbtab->irq->dev = kbtab->usbdev; - if (usb_submit_urb(kbtab->irq, GFP_KERNEL)) { - kbtab->open--; + if (usb_submit_urb(kbtab->irq, GFP_KERNEL)) return -EIO; - } return 0; } @@ -121,8 +115,7 @@ static void kbtab_close(struct input_dev *dev) { struct kbtab *kbtab = dev->private; - if (!--kbtab->open) - usb_kill_urb(kbtab->irq); + usb_kill_urb(kbtab->irq); } static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -161,7 +154,7 @@ static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *i kbtab->dev.absmax[ABS_X] = 0x2000; kbtab->dev.absmax[ABS_Y] = 0x1750; kbtab->dev.absmax[ABS_PRESSURE] = 0xff; - + kbtab->dev.absfuzz[ABS_X] = 4; kbtab->dev.absfuzz[ABS_Y] = 4; diff --git a/drivers/usb/input/mtouchusb.c b/drivers/usb/input/mtouchusb.c index ab1a2a3..09b5cc7 100644 --- a/drivers/usb/input/mtouchusb.c +++ b/drivers/usb/input/mtouchusb.c @@ -42,9 +42,9 @@ #include <linux/config.h> #ifdef CONFIG_USB_DEBUG - #define DEBUG + #define DEBUG #else - #undef DEBUG + #undef DEBUG #endif #include <linux/kernel.h> @@ -93,275 +93,255 @@ module_param(raw_coordinates, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(raw_coordinates, "report raw coordinate values (y, default) or hardware-calibrated coordinate values (n)"); struct mtouch_usb { - unsigned char *data; - dma_addr_t data_dma; - struct urb *irq; - struct usb_device *udev; - struct input_dev input; - int open; - char name[128]; - char phys[64]; + unsigned char *data; + dma_addr_t data_dma; + struct urb *irq; + struct usb_device *udev; + struct input_dev input; + char name[128]; + char phys[64]; }; -static struct usb_device_id mtouchusb_devices [] = { - { USB_DEVICE(0x0596, 0x0001) }, - { } +static struct usb_device_id mtouchusb_devices[] = { + { USB_DEVICE(0x0596, 0x0001) }, + { } }; static void mtouchusb_irq(struct urb *urb, struct pt_regs *regs) { - struct mtouch_usb *mtouch = urb->context; - int retval; - - switch (urb->status) { - case 0: - /* success */ - break; - case -ETIMEDOUT: - /* this urb is timing out */ - dbg("%s - urb timed out - was the device unplugged?", - __FUNCTION__); - return; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", - __FUNCTION__, urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", - __FUNCTION__, urb->status); - goto exit; - } - - input_regs(&mtouch->input, regs); - input_report_key(&mtouch->input, BTN_TOUCH, - MTOUCHUSB_GET_TOUCHED(mtouch->data)); - input_report_abs(&mtouch->input, ABS_X, - MTOUCHUSB_GET_XC(mtouch->data)); - input_report_abs(&mtouch->input, ABS_Y, + struct mtouch_usb *mtouch = urb->context; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ETIMEDOUT: + /* this urb is timing out */ + dbg("%s - urb timed out - was the device unplugged?", + __FUNCTION__); + return; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); + goto exit; + } + + input_regs(&mtouch->input, regs); + input_report_key(&mtouch->input, BTN_TOUCH, + MTOUCHUSB_GET_TOUCHED(mtouch->data)); + input_report_abs(&mtouch->input, ABS_X, MTOUCHUSB_GET_XC(mtouch->data)); + input_report_abs(&mtouch->input, ABS_Y, (raw_coordinates ? MTOUCHUSB_MAX_RAW_YC : MTOUCHUSB_MAX_CALIB_YC) - - MTOUCHUSB_GET_YC(mtouch->data)); - input_sync(&mtouch->input); + - MTOUCHUSB_GET_YC(mtouch->data)); + input_sync(&mtouch->input); exit: - retval = usb_submit_urb (urb, GFP_ATOMIC); - if (retval) - err ("%s - usb_submit_urb failed with result: %d", - __FUNCTION__, retval); + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err("%s - usb_submit_urb failed with result: %d", + __FUNCTION__, retval); } -static int mtouchusb_open (struct input_dev *input) +static int mtouchusb_open(struct input_dev *input) { - struct mtouch_usb *mtouch = input->private; + struct mtouch_usb *mtouch = input->private; - if (mtouch->open++) - return 0; + mtouch->irq->dev = mtouch->udev; - mtouch->irq->dev = mtouch->udev; + if (usb_submit_urb(mtouch->irq, GFP_ATOMIC)) + return -EIO; - if (usb_submit_urb (mtouch->irq, GFP_ATOMIC)) { - mtouch->open--; - return -EIO; - } - - return 0; + return 0; } -static void mtouchusb_close (struct input_dev *input) +static void mtouchusb_close(struct input_dev *input) { - struct mtouch_usb *mtouch = input->private; + struct mtouch_usb *mtouch = input->private; - if (!--mtouch->open) - usb_kill_urb (mtouch->irq); + usb_kill_urb(mtouch->irq); } static int mtouchusb_alloc_buffers(struct usb_device *udev, struct mtouch_usb *mtouch) { - dbg("%s - called", __FUNCTION__); + dbg("%s - called", __FUNCTION__); - mtouch->data = usb_buffer_alloc(udev, MTOUCHUSB_REPORT_DATA_SIZE, - SLAB_ATOMIC, &mtouch->data_dma); + mtouch->data = usb_buffer_alloc(udev, MTOUCHUSB_REPORT_DATA_SIZE, + SLAB_ATOMIC, &mtouch->data_dma); - if (!mtouch->data) - return -1; + if (!mtouch->data) + return -1; - return 0; + return 0; } static void mtouchusb_free_buffers(struct usb_device *udev, struct mtouch_usb *mtouch) { - dbg("%s - called", __FUNCTION__); + dbg("%s - called", __FUNCTION__); - if (mtouch->data) - usb_buffer_free(udev, MTOUCHUSB_REPORT_DATA_SIZE, - mtouch->data, mtouch->data_dma); + if (mtouch->data) + usb_buffer_free(udev, MTOUCHUSB_REPORT_DATA_SIZE, + mtouch->data, mtouch->data_dma); } static int mtouchusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct mtouch_usb *mtouch; - struct usb_host_interface *interface; - struct usb_endpoint_descriptor *endpoint; - struct usb_device *udev = interface_to_usbdev (intf); - char path[64]; - int nRet; - - dbg("%s - called", __FUNCTION__); - - dbg("%s - setting interface", __FUNCTION__); - interface = intf->cur_altsetting; - - dbg("%s - setting endpoint", __FUNCTION__); - endpoint = &interface->endpoint[0].desc; - - if (!(mtouch = kmalloc (sizeof (struct mtouch_usb), GFP_KERNEL))) { - err("%s - Out of memory.", __FUNCTION__); - return -ENOMEM; - } - - memset(mtouch, 0, sizeof(struct mtouch_usb)); - mtouch->udev = udev; - - dbg("%s - allocating buffers", __FUNCTION__); - if (mtouchusb_alloc_buffers(udev, mtouch)) { - mtouchusb_free_buffers(udev, mtouch); - kfree(mtouch); - return -ENOMEM; - } - - mtouch->input.private = mtouch; - mtouch->input.open = mtouchusb_open; - mtouch->input.close = mtouchusb_close; - - usb_make_path(udev, path, 64); - sprintf(mtouch->phys, "%s/input0", path); - - mtouch->input.name = mtouch->name; - mtouch->input.phys = mtouch->phys; - mtouch->input.id.bustype = BUS_USB; - mtouch->input.id.vendor = le16_to_cpu(udev->descriptor.idVendor); - mtouch->input.id.product = le16_to_cpu(udev->descriptor.idProduct); - mtouch->input.id.version = le16_to_cpu(udev->descriptor.bcdDevice); - mtouch->input.dev = &intf->dev; - - mtouch->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - mtouch->input.absbit[0] = BIT(ABS_X) | BIT(ABS_Y); - mtouch->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); - - /* Used to Scale Compensated Data and Flip Y */ - mtouch->input.absmin[ABS_X] = MTOUCHUSB_MIN_XC; - mtouch->input.absmax[ABS_X] = raw_coordinates ? \ - MTOUCHUSB_MAX_RAW_XC : MTOUCHUSB_MAX_CALIB_XC; - mtouch->input.absfuzz[ABS_X] = MTOUCHUSB_XC_FUZZ; - mtouch->input.absflat[ABS_X] = MTOUCHUSB_XC_FLAT; - mtouch->input.absmin[ABS_Y] = MTOUCHUSB_MIN_YC; - mtouch->input.absmax[ABS_Y] = raw_coordinates ? \ - MTOUCHUSB_MAX_RAW_YC : MTOUCHUSB_MAX_CALIB_YC; - mtouch->input.absfuzz[ABS_Y] = MTOUCHUSB_YC_FUZZ; - mtouch->input.absflat[ABS_Y] = MTOUCHUSB_YC_FLAT; + struct mtouch_usb *mtouch; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_device *udev = interface_to_usbdev(intf); + char path[64]; + int nRet; + + dbg("%s - called", __FUNCTION__); + + dbg("%s - setting interface", __FUNCTION__); + interface = intf->cur_altsetting; + + dbg("%s - setting endpoint", __FUNCTION__); + endpoint = &interface->endpoint[0].desc; + + if (!(mtouch = kmalloc(sizeof(struct mtouch_usb), GFP_KERNEL))) { + err("%s - Out of memory.", __FUNCTION__); + return -ENOMEM; + } + + memset(mtouch, 0, sizeof(struct mtouch_usb)); + mtouch->udev = udev; + + dbg("%s - allocating buffers", __FUNCTION__); + if (mtouchusb_alloc_buffers(udev, mtouch)) { + mtouchusb_free_buffers(udev, mtouch); + kfree(mtouch); + return -ENOMEM; + } + + mtouch->input.private = mtouch; + mtouch->input.open = mtouchusb_open; + mtouch->input.close = mtouchusb_close; + + usb_make_path(udev, path, 64); + sprintf(mtouch->phys, "%s/input0", path); + + mtouch->input.name = mtouch->name; + mtouch->input.phys = mtouch->phys; + mtouch->input.id.bustype = BUS_USB; + mtouch->input.id.vendor = le16_to_cpu(udev->descriptor.idVendor); + mtouch->input.id.product = le16_to_cpu(udev->descriptor.idProduct); + mtouch->input.id.version = le16_to_cpu(udev->descriptor.bcdDevice); + mtouch->input.dev = &intf->dev; + + mtouch->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + mtouch->input.absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + mtouch->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + + /* Used to Scale Compensated Data and Flip Y */ + mtouch->input.absmin[ABS_X] = MTOUCHUSB_MIN_XC; + mtouch->input.absmax[ABS_X] = raw_coordinates ? + MTOUCHUSB_MAX_RAW_XC : MTOUCHUSB_MAX_CALIB_XC; + mtouch->input.absfuzz[ABS_X] = MTOUCHUSB_XC_FUZZ; + mtouch->input.absflat[ABS_X] = MTOUCHUSB_XC_FLAT; + mtouch->input.absmin[ABS_Y] = MTOUCHUSB_MIN_YC; + mtouch->input.absmax[ABS_Y] = raw_coordinates ? + MTOUCHUSB_MAX_RAW_YC : MTOUCHUSB_MAX_CALIB_YC; + mtouch->input.absfuzz[ABS_Y] = MTOUCHUSB_YC_FUZZ; + mtouch->input.absflat[ABS_Y] = MTOUCHUSB_YC_FLAT; if (udev->manufacturer) strcat(mtouch->name, udev->manufacturer); if (udev->product) sprintf(mtouch->name, "%s %s", mtouch->name, udev->product); - if (!strlen(mtouch->name)) - sprintf(mtouch->name, "USB Touchscreen %04x:%04x", - mtouch->input.id.vendor, mtouch->input.id.product); - - nRet = usb_control_msg(mtouch->udev, - usb_rcvctrlpipe(udev, 0), - MTOUCHUSB_RESET, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 1, - 0, - NULL, - 0, - USB_CTRL_SET_TIMEOUT); - dbg("%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d", - __FUNCTION__, nRet); - - dbg("%s - usb_alloc_urb: mtouch->irq", __FUNCTION__); - mtouch->irq = usb_alloc_urb(0, GFP_KERNEL); - if (!mtouch->irq) { - dbg("%s - usb_alloc_urb failed: mtouch->irq", __FUNCTION__); - mtouchusb_free_buffers(udev, mtouch); - kfree(mtouch); - return -ENOMEM; - } - - dbg("%s - usb_fill_int_urb", __FUNCTION__); - usb_fill_int_urb(mtouch->irq, - mtouch->udev, - usb_rcvintpipe(mtouch->udev, 0x81), - mtouch->data, - MTOUCHUSB_REPORT_DATA_SIZE, - mtouchusb_irq, - mtouch, - endpoint->bInterval); - - dbg("%s - input_register_device", __FUNCTION__); - input_register_device(&mtouch->input); - - nRet = usb_control_msg(mtouch->udev, - usb_rcvctrlpipe(udev, 0), - MTOUCHUSB_ASYNC_REPORT, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 1, - 1, - NULL, - 0, - USB_CTRL_SET_TIMEOUT); - dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d", - __FUNCTION__, nRet); - - printk(KERN_INFO "input: %s on %s\n", mtouch->name, path); - usb_set_intfdata(intf, mtouch); - - return 0; + if (!strlen(mtouch->name)) + sprintf(mtouch->name, "USB Touchscreen %04x:%04x", + mtouch->input.id.vendor, mtouch->input.id.product); + + nRet = usb_control_msg(mtouch->udev, usb_rcvctrlpipe(udev, 0), + MTOUCHUSB_RESET, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); + dbg("%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d", + __FUNCTION__, nRet); + + dbg("%s - usb_alloc_urb: mtouch->irq", __FUNCTION__); + mtouch->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!mtouch->irq) { + dbg("%s - usb_alloc_urb failed: mtouch->irq", __FUNCTION__); + mtouchusb_free_buffers(udev, mtouch); + kfree(mtouch); + return -ENOMEM; + } + + dbg("%s - usb_fill_int_urb", __FUNCTION__); + usb_fill_int_urb(mtouch->irq, mtouch->udev, + usb_rcvintpipe(mtouch->udev, 0x81), + mtouch->data, MTOUCHUSB_REPORT_DATA_SIZE, + mtouchusb_irq, mtouch, endpoint->bInterval); + + dbg("%s - input_register_device", __FUNCTION__); + input_register_device(&mtouch->input); + + nRet = usb_control_msg(mtouch->udev, usb_rcvctrlpipe(udev, 0), + MTOUCHUSB_ASYNC_REPORT, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT); + dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d", + __FUNCTION__, nRet); + + printk(KERN_INFO "input: %s on %s\n", mtouch->name, path); + usb_set_intfdata(intf, mtouch); + + return 0; } static void mtouchusb_disconnect(struct usb_interface *intf) { - struct mtouch_usb *mtouch = usb_get_intfdata (intf); - - dbg("%s - called", __FUNCTION__); - usb_set_intfdata(intf, NULL); - if (mtouch) { - dbg("%s - mtouch is initialized, cleaning up", __FUNCTION__); - usb_kill_urb(mtouch->irq); - input_unregister_device(&mtouch->input); - usb_free_urb(mtouch->irq); - mtouchusb_free_buffers(interface_to_usbdev(intf), mtouch); - kfree(mtouch); - } + struct mtouch_usb *mtouch = usb_get_intfdata(intf); + + dbg("%s - called", __FUNCTION__); + usb_set_intfdata(intf, NULL); + if (mtouch) { + dbg("%s - mtouch is initialized, cleaning up", __FUNCTION__); + usb_kill_urb(mtouch->irq); + input_unregister_device(&mtouch->input); + usb_free_urb(mtouch->irq); + mtouchusb_free_buffers(interface_to_usbdev(intf), mtouch); + kfree(mtouch); + } } -MODULE_DEVICE_TABLE (usb, mtouchusb_devices); +MODULE_DEVICE_TABLE(usb, mtouchusb_devices); static struct usb_driver mtouchusb_driver = { - .owner = THIS_MODULE, - .name = "mtouchusb", - .probe = mtouchusb_probe, - .disconnect = mtouchusb_disconnect, - .id_table = mtouchusb_devices, + .owner = THIS_MODULE, + .name = "mtouchusb", + .probe = mtouchusb_probe, + .disconnect = mtouchusb_disconnect, + .id_table = mtouchusb_devices, }; -static int __init mtouchusb_init(void) { - dbg("%s - called", __FUNCTION__); - return usb_register(&mtouchusb_driver); +static int __init mtouchusb_init(void) +{ + dbg("%s - called", __FUNCTION__); + return usb_register(&mtouchusb_driver); } -static void __exit mtouchusb_cleanup(void) { - dbg("%s - called", __FUNCTION__); - usb_deregister(&mtouchusb_driver); +static void __exit mtouchusb_cleanup(void) +{ + dbg("%s - called", __FUNCTION__); + usb_deregister(&mtouchusb_driver); } module_init(mtouchusb_init); module_exit(mtouchusb_cleanup); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/input/powermate.c b/drivers/usb/input/powermate.c index 7fa2f9b..3975b30 100644 --- a/drivers/usb/input/powermate.c +++ b/drivers/usb/input/powermate.c @@ -10,7 +10,7 @@ * back to the host when polled by the USB controller. * * Testing with the knob I have has shown that it measures approximately 94 "clicks" - * for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was + * for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was * a variable speed cordless electric drill) has shown that the device can measure * speeds of up to 7 clicks either clockwise or anticlockwise between pollings from * the host. If it counts more than 7 clicks before it is polled, it will wrap back @@ -120,9 +120,9 @@ exit: /* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */ static void powermate_sync_state(struct powermate_device *pm) { - if (pm->requires_update == 0) + if (pm->requires_update == 0) return; /* no updates are required */ - if (pm->config->status == -EINPROGRESS) + if (pm->config->status == -EINPROGRESS) return; /* an update is already in progress; it'll issue this update when it completes */ if (pm->requires_update & UPDATE_PULSE_ASLEEP){ @@ -142,7 +142,7 @@ static void powermate_sync_state(struct powermate_device *pm) 2: multiply the speed the argument only has an effect for operations 0 and 2, and ranges between 1 (least effect) to 255 (maximum effect). - + thus, several states are equivalent and are coalesced into one state. we map this onto a range from 0 to 510, with: @@ -151,7 +151,7 @@ static void powermate_sync_state(struct powermate_device *pm) 256 -- 510 -- use multiple (510 = fastest). Only values of 'arg' quite close to 255 are particularly useful/spectacular. - */ + */ if (pm->pulse_speed < 255){ op = 0; // divide arg = 255 - pm->pulse_speed; @@ -199,14 +199,14 @@ static void powermate_config_complete(struct urb *urb, struct pt_regs *regs) if (urb->status) printk(KERN_ERR "powermate: config urb returned %d\n", urb->status); - + spin_lock_irqsave(&pm->lock, flags); powermate_sync_state(pm); spin_unlock_irqrestore(&pm->lock, flags); } /* Set the LED up as described and begin the sync with the hardware if required */ -static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed, +static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed, int pulse_table, int pulse_asleep, int pulse_awake) { unsigned long flags; @@ -229,7 +229,7 @@ static void powermate_pulse_led(struct powermate_device *pm, int static_brightne /* mark state updates which are required */ if (static_brightness != pm->static_brightness){ pm->static_brightness = static_brightness; - pm->requires_update |= UPDATE_STATIC_BRIGHTNESS; + pm->requires_update |= UPDATE_STATIC_BRIGHTNESS; } if (pulse_asleep != pm->pulse_asleep){ pm->pulse_asleep = pulse_asleep; @@ -246,7 +246,7 @@ static void powermate_pulse_led(struct powermate_device *pm, int static_brightne } powermate_sync_state(pm); - + spin_unlock_irqrestore(&pm->lock, flags); } @@ -257,19 +257,19 @@ static int powermate_input_event(struct input_dev *dev, unsigned int type, unsig struct powermate_device *pm = dev->private; if (type == EV_MSC && code == MSC_PULSELED){ - /* + /* bits 0- 7: 8 bits: LED brightness bits 8-16: 9 bits: pulsing speed modifier (0 ... 510); 0-254 = slower, 255 = standard, 256-510 = faster. bits 17-18: 2 bits: pulse table (0, 1, 2 valid) bit 19: 1 bit : pulse whilst asleep? bit 20: 1 bit : pulse constantly? - */ + */ int static_brightness = command & 0xFF; // bits 0-7 int pulse_speed = (command >> 8) & 0x1FF; // bits 8-16 int pulse_table = (command >> 17) & 0x3; // bits 17-18 int pulse_asleep = (command >> 19) & 0x1; // bit 19 int pulse_awake = (command >> 20) & 0x1; // bit 20 - + powermate_pulse_led(pm, static_brightness, pulse_speed, pulse_table, pulse_asleep, pulse_awake); } @@ -378,7 +378,7 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i switch (le16_to_cpu(udev->descriptor.idProduct)) { case POWERMATE_PRODUCT_NEW: pm->input.name = pm_name_powermate; break; case POWERMATE_PRODUCT_OLD: pm->input.name = pm_name_soundknob; break; - default: + default: pm->input.name = pm_name_soundknob; printk(KERN_WARNING "powermate: unknown product id %04x\n", le16_to_cpu(udev->descriptor.idProduct)); @@ -402,11 +402,11 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i usb_make_path(udev, path, 64); snprintf(pm->phys, 64, "%s/input0", path); printk(KERN_INFO "input: %s on %s\n", pm->input.name, pm->input.phys); - + /* force an update of everything */ pm->requires_update = UPDATE_PULSE_ASLEEP | UPDATE_PULSE_AWAKE | UPDATE_PULSE_MODE | UPDATE_STATIC_BRIGHTNESS; powermate_pulse_led(pm, 0x80, 255, 0, 1, 0); // set default pulse parameters - + usb_set_intfdata(intf, pm); return 0; } diff --git a/drivers/usb/input/touchkitusb.c b/drivers/usb/input/touchkitusb.c index a71f1bb..386595e 100644 --- a/drivers/usb/input/touchkitusb.c +++ b/drivers/usb/input/touchkitusb.c @@ -69,7 +69,6 @@ struct touchkit_usb { struct urb *irq; struct usb_device *udev; struct input_dev input; - int open; char name[128]; char phys[64]; }; @@ -134,15 +133,10 @@ static int touchkit_open(struct input_dev *input) { struct touchkit_usb *touchkit = input->private; - if (touchkit->open++) - return 0; - touchkit->irq->dev = touchkit->udev; - if (usb_submit_urb(touchkit->irq, GFP_ATOMIC)) { - touchkit->open--; + if (usb_submit_urb(touchkit->irq, GFP_ATOMIC)) return -EIO; - } return 0; } @@ -151,8 +145,7 @@ static void touchkit_close(struct input_dev *input) { struct touchkit_usb *touchkit = input->private; - if (!--touchkit->open) - usb_kill_urb(touchkit->irq); + usb_kill_urb(touchkit->irq); } static int touchkit_alloc_buffers(struct usb_device *udev, diff --git a/drivers/usb/input/usbkbd.c b/drivers/usb/input/usbkbd.c index 7038fb9..f35db19 100644 --- a/drivers/usb/input/usbkbd.c +++ b/drivers/usb/input/usbkbd.c @@ -9,18 +9,18 @@ /* * 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 + * 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 - * + * * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic @@ -72,7 +72,6 @@ struct usb_kbd { unsigned char newleds; char name[128]; char phys[64]; - int open; unsigned char *new; struct usb_ctrlrequest *cr; @@ -166,7 +165,7 @@ static void usb_kbd_led(struct urb *urb, struct pt_regs *regs) if (urb->status) warn("led urb status %d received", urb->status); - + if (*(kbd->leds) == kbd->newleds) return; @@ -180,14 +179,9 @@ static int usb_kbd_open(struct input_dev *dev) { struct usb_kbd *kbd = dev->private; - if (kbd->open++) - return 0; - kbd->irq->dev = kbd->usbdev; - if (usb_submit_urb(kbd->irq, GFP_KERNEL)) { - kbd->open--; + if (usb_submit_urb(kbd->irq, GFP_KERNEL)) return -EIO; - } return 0; } @@ -196,8 +190,7 @@ static void usb_kbd_close(struct input_dev *dev) { struct usb_kbd *kbd = dev->private; - if (!--kbd->open) - usb_kill_urb(kbd->irq); + usb_kill_urb(kbd->irq); } static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd) @@ -230,7 +223,7 @@ static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd) usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma); } -static int usb_kbd_probe(struct usb_interface *iface, +static int usb_kbd_probe(struct usb_interface *iface, const struct usb_device_id *id) { struct usb_device * dev = interface_to_usbdev(iface); @@ -272,7 +265,7 @@ static int usb_kbd_probe(struct usb_interface *iface, for (i = 0; i < 255; i++) set_bit(usb_kbd_keycode[i], kbd->dev.keybit); clear_bit(0, kbd->dev.keybit); - + kbd->dev.private = kbd; kbd->dev.event = usb_kbd_event; kbd->dev.open = usb_kbd_open; @@ -294,7 +287,7 @@ static int usb_kbd_probe(struct usb_interface *iface, sprintf(kbd->phys, "%s/input0", path); kbd->dev.name = kbd->name; - kbd->dev.phys = kbd->phys; + kbd->dev.phys = kbd->phys; kbd->dev.id.bustype = BUS_USB; kbd->dev.id.vendor = le16_to_cpu(dev->descriptor.idVendor); kbd->dev.id.product = le16_to_cpu(dev->descriptor.idProduct); @@ -329,7 +322,7 @@ static int usb_kbd_probe(struct usb_interface *iface, static void usb_kbd_disconnect(struct usb_interface *intf) { struct usb_kbd *kbd = usb_get_intfdata (intf); - + usb_set_intfdata(intf, NULL); if (kbd) { usb_kill_urb(kbd->irq); diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c index 01155bb..1ec41b5 100644 --- a/drivers/usb/input/usbmouse.c +++ b/drivers/usb/input/usbmouse.c @@ -9,18 +9,18 @@ /* * 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 + * 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 - * + * * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic @@ -51,7 +51,6 @@ struct usb_mouse { struct usb_device *usbdev; struct input_dev dev; struct urb *irq; - int open; signed char *data; dma_addr_t data_dma; @@ -101,14 +100,9 @@ static int usb_mouse_open(struct input_dev *dev) { struct usb_mouse *mouse = dev->private; - if (mouse->open++) - return 0; - mouse->irq->dev = mouse->usbdev; - if (usb_submit_urb(mouse->irq, GFP_KERNEL)) { - mouse->open--; + if (usb_submit_urb(mouse->irq, GFP_KERNEL)) return -EIO; - } return 0; } @@ -117,8 +111,7 @@ static void usb_mouse_close(struct input_dev *dev) { struct usb_mouse *mouse = dev->private; - if (!--mouse->open) - usb_kill_urb(mouse->irq); + usb_kill_urb(mouse->irq); } static int usb_mouse_probe(struct usb_interface * intf, const struct usb_device_id * id) @@ -132,19 +125,19 @@ static int usb_mouse_probe(struct usb_interface * intf, const struct usb_device_ interface = intf->cur_altsetting; - if (interface->desc.bNumEndpoints != 1) + if (interface->desc.bNumEndpoints != 1) return -ENODEV; endpoint = &interface->endpoint[0].desc; - if (!(endpoint->bEndpointAddress & 0x80)) + if (!(endpoint->bEndpointAddress & 0x80)) return -ENODEV; - if ((endpoint->bmAttributes & 3) != 3) + if ((endpoint->bmAttributes & 3) != 3) return -ENODEV; pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - if (!(mouse = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL))) + if (!(mouse = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL))) return -ENOMEM; memset(mouse, 0, sizeof(struct usb_mouse)); @@ -209,7 +202,7 @@ static int usb_mouse_probe(struct usb_interface * intf, const struct usb_device_ static void usb_mouse_disconnect(struct usb_interface *intf) { struct usb_mouse *mouse = usb_get_intfdata (intf); - + usb_set_intfdata(intf, NULL); if (mouse) { usb_kill_urb(mouse->irq); @@ -238,7 +231,7 @@ static struct usb_driver usb_mouse_driver = { static int __init usb_mouse_init(void) { int retval = usb_register(&usb_mouse_driver); - if (retval == 0) + if (retval == 0) info(DRIVER_VERSION ":" DRIVER_DESC); return retval; } diff --git a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c index fec04dd..f6b34af 100644 --- a/drivers/usb/input/wacom.c +++ b/drivers/usb/input/wacom.c @@ -9,7 +9,7 @@ * Copyright (c) 2000 Daniel Egger <egger@suse.de> * Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com> * Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be> - * Copyright (c) 2002-2004 Ping Cheng <pingc@wacom.com> + * Copyright (c) 2002-2005 Ping Cheng <pingc@wacom.com> * * ChangeLog: * v0.1 (vp) - Initial release @@ -18,7 +18,7 @@ * v0.4 (sm) - Support for more Intuos models, menustrip * relative mode, proximity. * v0.5 (vp) - Big cleanup, nifty features removed, - * they belong in userspace + * they belong in userspace * v1.8 (vp) - Submit URB only when operating, moved to CVS, * use input_report_key instead of report_btn and * other cleanups @@ -51,6 +51,9 @@ * - Cleanups here and there * v1.30.1 (pi) - Added Graphire3 support * v1.40 (pc) - Add support for several new devices, fix eraser reporting, ... + * v1.43 (pc) - Added support for Cintiq 21UX + - Fixed a Graphire bug + - Merged wacom_intuos3_irq into wacom_intuos_irq */ /* @@ -72,7 +75,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.40" +#define DRIVER_VERSION "v1.43" #define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>" #define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver" #define DRIVER_LICENSE "GPL" @@ -83,6 +86,16 @@ MODULE_LICENSE(DRIVER_LICENSE); #define USB_VENDOR_ID_WACOM 0x056a +enum { + PENPARTNER = 0, + GRAPHIRE, + PL, + INTUOS, + INTUOS3, + CINTIQ, + MAX_TYPE +}; + struct wacom_features { char *name; int pktlen; @@ -102,7 +115,6 @@ struct wacom { struct urb *irq; struct wacom_features *features; int tool[2]; - int open; __u32 serial[2]; char phys[32]; }; @@ -149,7 +161,7 @@ static void wacom_pl_irq(struct urb *urb, struct pt_regs *regs) prox = data[1] & 0x40; input_regs(dev, regs); - + if (prox) { pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1)); @@ -166,8 +178,7 @@ static void wacom_pl_irq(struct urb *urb, struct pt_regs *regs) if (!wacom->tool[0]) { /* Going into proximity select tool */ wacom->tool[1] = (data[4] & 0x20)? BTN_TOOL_RUBBER : BTN_TOOL_PEN; - } - else { + } else { /* was entered with stylus2 pressed */ if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20) ) { /* report out proximity for previous tool */ @@ -182,16 +193,15 @@ static void wacom_pl_irq(struct urb *urb, struct pt_regs *regs) wacom->tool[1] = BTN_TOOL_PEN; } input_report_key(dev, wacom->tool[1], prox); /* report in proximity for tool */ - input_report_abs(dev, ABS_X, data[3] | ((__u32)data[2] << 7) | ((__u32)(data[1] & 0x03) << 14)); - input_report_abs(dev, ABS_Y, data[6] | ((__u32)data[5] << 7) | ((__u32)(data[4] & 0x03) << 14)); + input_report_abs(dev, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14)); + input_report_abs(dev, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14)); input_report_abs(dev, ABS_PRESSURE, pressure); input_report_key(dev, BTN_TOUCH, data[4] & 0x08); input_report_key(dev, BTN_STYLUS, data[4] & 0x10); /* Only allow the stylus2 button to be reported for the pen tool. */ input_report_key(dev, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20)); - } - else { + } else { /* report proximity-out of a (valid) tool */ if (wacom->tool[1] != BTN_TOOL_RUBBER) { /* Unknown tool selected default to pen tool */ @@ -203,7 +213,7 @@ static void wacom_pl_irq(struct urb *urb, struct pt_regs *regs) wacom->tool[0] = prox; /* Save proximity state */ input_sync(dev); -exit: + exit: retval = usb_submit_urb (urb, GFP_ATOMIC); if (retval) err ("%s - usb_submit_urb failed with result %d", @@ -232,20 +242,16 @@ static void wacom_ptu_irq(struct urb *urb, struct pt_regs *regs) goto exit; } - if (data[0] != 2) - { + if (data[0] != 2) { printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]); goto exit; } input_regs(dev, regs); - if (data[1] & 0x04) - { + if (data[1] & 0x04) { input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x20); input_report_key(dev, BTN_TOUCH, data[1] & 0x08); - } - else - { + } else { input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x20); input_report_key(dev, BTN_TOUCH, data[1] & 0x01); } @@ -257,7 +263,7 @@ static void wacom_ptu_irq(struct urb *urb, struct pt_regs *regs) input_sync(dev); -exit: + exit: retval = usb_submit_urb (urb, GFP_ATOMIC); if (retval) err ("%s - usb_submit_urb failed with result %d", @@ -300,7 +306,7 @@ static void wacom_penpartner_irq(struct urb *urb, struct pt_regs *regs) input_report_key(dev, BTN_STYLUS, (data[5] & 0x40)); input_sync(dev); -exit: + exit: retval = usb_submit_urb (urb, GFP_ATOMIC); if (retval) err ("%s - usb_submit_urb failed with result %d", @@ -340,47 +346,47 @@ static void wacom_graphire_irq(struct urb *urb, struct pt_regs *regs) input_regs(dev, regs); - switch ((data[1] >> 5) & 3) { + if (data[1] & 0x10) { /* in prox */ - case 0: /* Pen */ - input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x80); - break; + switch ((data[1] >> 5) & 3) { - case 1: /* Rubber */ - input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x80); - break; - - case 2: /* Mouse with wheel */ - input_report_key(dev, BTN_MIDDLE, data[1] & 0x04); - input_report_rel(dev, REL_WHEEL, (signed char) data[6]); - /* fall through */ + case 0: /* Pen */ + wacom->tool[0] = BTN_TOOL_PEN; + break; - case 3: /* Mouse without wheel */ - input_report_key(dev, BTN_TOOL_MOUSE, data[7] > 24); - input_report_key(dev, BTN_LEFT, data[1] & 0x01); - input_report_key(dev, BTN_RIGHT, data[1] & 0x02); - input_report_abs(dev, ABS_DISTANCE, data[7]); + case 1: /* Rubber */ + wacom->tool[0] = BTN_TOOL_RUBBER; + break; - input_report_abs(dev, ABS_X, x); - input_report_abs(dev, ABS_Y, y); + case 2: /* Mouse with wheel */ + input_report_key(dev, BTN_MIDDLE, data[1] & 0x04); + input_report_rel(dev, REL_WHEEL, (signed char) data[6]); + /* fall through */ - input_sync(dev); - goto exit; + case 3: /* Mouse without wheel */ + wacom->tool[0] = BTN_TOOL_MOUSE; + input_report_key(dev, BTN_LEFT, data[1] & 0x01); + input_report_key(dev, BTN_RIGHT, data[1] & 0x02); + input_report_abs(dev, ABS_DISTANCE, data[7]); + break; + } } if (data[1] & 0x80) { input_report_abs(dev, ABS_X, x); input_report_abs(dev, ABS_Y, y); } + if (wacom->tool[0] != BTN_TOOL_MOUSE) { + input_report_abs(dev, ABS_PRESSURE, le16_to_cpu(*(__le16 *) &data[6])); + input_report_key(dev, BTN_TOUCH, data[1] & 0x01); + input_report_key(dev, BTN_STYLUS, data[1] & 0x02); + input_report_key(dev, BTN_STYLUS2, data[1] & 0x04); + } - input_report_abs(dev, ABS_PRESSURE, le16_to_cpu(*(__le16 *) &data[6])); - input_report_key(dev, BTN_TOUCH, data[1] & 0x01); - input_report_key(dev, BTN_STYLUS, data[1] & 0x02); - input_report_key(dev, BTN_STYLUS2, data[1] & 0x04); - + input_report_key(dev, wacom->tool[0], data[1] & 0x10); input_sync(dev); -exit: + exit: retval = usb_submit_urb (urb, GFP_ATOMIC); if (retval) err ("%s - usb_submit_urb failed with result %d", @@ -398,14 +404,13 @@ static int wacom_intuos_inout(struct urb *urb) idx = data[1] & 0x01; /* Enter report */ - if ((data[1] & 0xfc) == 0xc0) - { + if ((data[1] & 0xfc) == 0xc0) { /* serial number of the tool */ - wacom->serial[idx] = ((__u32)(data[3] & 0x0f) << 28) + - ((__u32)data[4] << 20) + ((__u32)data[5] << 12) + - ((__u32)data[6] << 4) + (data[7] >> 4); + wacom->serial[idx] = ((data[3] & 0x0f) << 28) + + (data[4] << 20) + (data[5] << 12) + + (data[6] << 4) + (data[7] >> 4); - switch (((__u32)data[2] << 4) | (data[3] >> 4)) { + switch ((data[2] << 4) | (data[3] >> 4)) { case 0x812: /* Inking pen */ case 0x801: /* Intuos3 Inking pen */ case 0x012: @@ -449,7 +454,7 @@ static int wacom_intuos_inout(struct urb *urb) case 0x112: case 0x913: /* Intuos3 Airbrush */ wacom->tool[idx] = BTN_TOOL_AIRBRUSH; - break; /* Airbrush */ + break; default: /* Unknown tool */ wacom->tool[idx] = BTN_TOOL_PEN; } @@ -478,9 +483,8 @@ static void wacom_intuos_general(struct urb *urb) unsigned int t; /* general pen packet */ - if ((data[1] & 0xb8) == 0xa0) - { - t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3); + if ((data[1] & 0xb8) == 0xa0) { + t = (data[6] << 2) | ((data[7] >> 6) & 3); input_report_abs(dev, ABS_PRESSURE, t); input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7)); @@ -491,10 +495,9 @@ static void wacom_intuos_general(struct urb *urb) } /* airbrush second packet */ - if ((data[1] & 0xbc) == 0xb4) - { + if ((data[1] & 0xbc) == 0xb4) { input_report_abs(dev, ABS_WHEEL, - ((__u32)data[6] << 2) | ((data[7] >> 6) & 3)); + (data[6] << 2) | ((data[7] >> 6) & 3)); input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7)); input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f); @@ -526,7 +529,7 @@ static void wacom_intuos_irq(struct urb *urb, struct pt_regs *regs) goto exit; } - if (data[0] != 2 && data[0] != 5 && data[0] != 6) { + if (data[0] != 2 && data[0] != 5 && data[0] != 6 && data[0] != 12) { dbg("wacom_intuos_irq: received unknown report #%d", data[0]); goto exit; } @@ -536,107 +539,10 @@ static void wacom_intuos_irq(struct urb *urb, struct pt_regs *regs) /* tool number */ idx = data[1] & 0x01; - /* process in/out prox events */ - if (wacom_intuos_inout(urb)) goto exit; - - input_report_abs(dev, ABS_X, be16_to_cpu(*(__be16 *) &data[2])); - input_report_abs(dev, ABS_Y, be16_to_cpu(*(__be16 *) &data[4])); - input_report_abs(dev, ABS_DISTANCE, data[9]); - - /* process general packets */ - wacom_intuos_general(urb); - - if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) { /* 4D mouse or Lens cursor packets */ - - if (data[1] & 0x02) { /* Rotation packet */ - - t = ((__u32)data[6] << 3) | ((data[7] >> 5) & 7); - input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ? ((t - 1) / 2) : -t / 2); - - } else { - - if ((data[1] & 0x10) == 0) { /* 4D mouse packets */ - - input_report_key(dev, BTN_LEFT, data[8] & 0x01); - input_report_key(dev, BTN_MIDDLE, data[8] & 0x02); - input_report_key(dev, BTN_RIGHT, data[8] & 0x04); - - input_report_key(dev, BTN_SIDE, data[8] & 0x20); - input_report_key(dev, BTN_EXTRA, data[8] & 0x10); - t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3); - input_report_abs(dev, ABS_THROTTLE, (data[8] & 0x08) ? -t : t); - - } else { - if (wacom->tool[idx] == BTN_TOOL_MOUSE) { /* 2D mouse packets */ - input_report_key(dev, BTN_LEFT, data[8] & 0x04); - input_report_key(dev, BTN_MIDDLE, data[8] & 0x08); - input_report_key(dev, BTN_RIGHT, data[8] & 0x10); - input_report_rel(dev, REL_WHEEL, - (-(__u32)(data[8] & 0x01) + (__u32)((data[8] & 0x02) >> 1))); - } - else { /* Lens cursor packets */ - input_report_key(dev, BTN_LEFT, data[8] & 0x01); - input_report_key(dev, BTN_MIDDLE, data[8] & 0x02); - input_report_key(dev, BTN_RIGHT, data[8] & 0x04); - input_report_key(dev, BTN_SIDE, data[8] & 0x10); - input_report_key(dev, BTN_EXTRA, data[8] & 0x08); - } - } - } - } - - input_report_key(dev, wacom->tool[idx], 1); - input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]); - input_sync(dev); - -exit: - retval = usb_submit_urb (urb, GFP_ATOMIC); - if (retval) - err ("%s - usb_submit_urb failed with result %d", - __FUNCTION__, retval); -} - -static void wacom_intuos3_irq(struct urb *urb, struct pt_regs *regs) -{ - struct wacom *wacom = urb->context; - unsigned char *data = wacom->data; - struct input_dev *dev = &wacom->dev; - unsigned int t; - int idx, retval; - - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); - goto exit; - } - - /* check for valid report */ - if (data[0] != 2 && data[0] != 5 && data[0] != 12) - { - printk(KERN_INFO "wacom_intuos3_irq: received unknown report #%d\n", data[0]); - goto exit; - } - - input_regs(dev, regs); - - /* tool index is always 0 here since there is no dual input tool */ - idx = data[1] & 0x01; - /* pad packets. Works as a second tool and is always in prox */ - if (data[0] == 12) - { + if (data[0] == 12) { /* initiate the pad as a device */ - if (wacom->tool[1] != BTN_TOOL_FINGER) - { + if (wacom->tool[1] != BTN_TOOL_FINGER) { wacom->tool[1] = BTN_TOOL_FINGER; input_report_key(dev, wacom->tool[1], 1); } @@ -656,37 +562,78 @@ static void wacom_intuos3_irq(struct urb *urb, struct pt_regs *regs) } /* process in/out prox events */ - if (wacom_intuos_inout(urb)) goto exit; + if (wacom_intuos_inout(urb)) + goto exit; - input_report_abs(dev, ABS_X, ((__u32)data[2] << 9) | ((__u32)data[3] << 1) | ((data[9] >> 1) & 1)); - input_report_abs(dev, ABS_Y, ((__u32)data[4] << 9) | ((__u32)data[5] << 1) | (data[9] & 1)); - input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 2) & 0x3f)); + /* Cintiq doesn't send data when RDY bit isn't set */ + if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40)) + return; + + if (wacom->features->type >= INTUOS3) { + input_report_abs(dev, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1)); + input_report_abs(dev, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1)); + input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 2) & 0x3f)); + } else { + input_report_abs(dev, ABS_X, be16_to_cpu(*(__be16 *) &data[2])); + input_report_abs(dev, ABS_Y, be16_to_cpu(*(__be16 *) &data[4])); + input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 3) & 0x1f)); + } /* process general packets */ wacom_intuos_general(urb); - if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) - { - /* Marker pen rotation packet. Reported as wheel due to valuator limitation */ - if (data[1] & 0x02) - { - t = ((__u32)data[6] << 3) | ((data[7] >> 5) & 7); - t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) : - ((t-1) / 2 + 450)) : (450 - t / 2) ; - input_report_abs(dev, ABS_WHEEL, t); - } + /* 4D mouse, 2D mouse, marker pen rotation, or Lens cursor packets */ + if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) { + + if (data[1] & 0x02) { + /* Rotation packet */ + if (wacom->features->type >= INTUOS3) { + /* I3 marker pen rotation reported as wheel + * due to valuator limitation + */ + t = (data[6] << 3) | ((data[7] >> 5) & 7); + t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) : + ((t-1) / 2 + 450)) : (450 - t / 2) ; + input_report_abs(dev, ABS_WHEEL, t); + } else { + /* 4D mouse rotation packet */ + t = (data[6] << 3) | ((data[7] >> 5) & 7); + input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ? + ((t - 1) / 2) : -t / 2); + } - /* 2D mouse packets */ - if (wacom->tool[idx] == BTN_TOOL_MOUSE) - { + } else if (!(data[1] & 0x10) && wacom->features->type < INTUOS3) { + /* 4D mouse packet */ + input_report_key(dev, BTN_LEFT, data[8] & 0x01); + input_report_key(dev, BTN_MIDDLE, data[8] & 0x02); + input_report_key(dev, BTN_RIGHT, data[8] & 0x04); + + input_report_key(dev, BTN_SIDE, data[8] & 0x20); + input_report_key(dev, BTN_EXTRA, data[8] & 0x10); + t = (data[6] << 2) | ((data[7] >> 6) & 3); + input_report_abs(dev, ABS_THROTTLE, (data[8] & 0x08) ? -t : t); + + } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) { + /* 2D mouse packet */ input_report_key(dev, BTN_LEFT, data[8] & 0x04); input_report_key(dev, BTN_MIDDLE, data[8] & 0x08); input_report_key(dev, BTN_RIGHT, data[8] & 0x10); - input_report_key(dev, BTN_SIDE, data[8] & 0x40); - input_report_key(dev, BTN_EXTRA, data[8] & 0x20); - /* mouse wheel is positive when rolled backwards */ - input_report_rel(dev, REL_WHEEL, ((__u32)((data[8] & 0x02) >> 1) - - (__u32)(data[8] & 0x01))); + input_report_rel(dev, REL_WHEEL, ((data[8] & 0x02) >> 1) + - (data[8] & 0x01)); + + /* I3 2D mouse side buttons */ + if (wacom->features->type == INTUOS3) { + input_report_key(dev, BTN_SIDE, data[8] & 0x40); + input_report_key(dev, BTN_EXTRA, data[8] & 0x20); + } + + } else if (wacom->features->type < INTUOS3) { + /* Lens cursor packets */ + input_report_key(dev, BTN_LEFT, data[8] & 0x01); + input_report_key(dev, BTN_MIDDLE, data[8] & 0x02); + input_report_key(dev, BTN_RIGHT, data[8] & 0x04); + input_report_key(dev, BTN_SIDE, data[8] & 0x10); + input_report_key(dev, BTN_EXTRA, data[8] & 0x08); } } @@ -702,35 +649,36 @@ exit: } static struct wacom_features wacom_features[] = { - { "Wacom Penpartner", 7, 5040, 3780, 255, 32, 0, wacom_penpartner_irq }, - { "Wacom Graphire", 8, 10206, 7422, 511, 32, 1, wacom_graphire_irq }, - { "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 32, 1, wacom_graphire_irq }, - { "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 32, 1, wacom_graphire_irq }, - { "Wacom Graphire3", 8, 10208, 7424, 511, 32, 1, wacom_graphire_irq }, - { "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 32, 1, wacom_graphire_irq }, - { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom PL400", 8, 5408, 4056, 255, 32, 3, wacom_pl_irq }, - { "Wacom PL500", 8, 6144, 4608, 255, 32, 3, wacom_pl_irq }, - { "Wacom PL600", 8, 6126, 4604, 255, 32, 3, wacom_pl_irq }, - { "Wacom PL600SX", 8, 6260, 5016, 255, 32, 3, wacom_pl_irq }, - { "Wacom PL550", 8, 6144, 4608, 511, 32, 3, wacom_pl_irq }, - { "Wacom PL800", 8, 7220, 5780, 511, 32, 3, wacom_pl_irq }, - { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Volito", 8, 5104, 3712, 511, 32, 1, wacom_graphire_irq }, - { "Wacom Cintiq Partner",8, 20480, 15360, 511, 32, 3, wacom_ptu_irq }, - { "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 15, 4, wacom_intuos3_irq }, - { "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 15, 4, wacom_intuos3_irq }, - { "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 15, 4, wacom_intuos3_irq }, - { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, 2, wacom_intuos_irq }, - { } + { "Wacom Penpartner", 7, 5040, 3780, 255, 32, PENPARTNER, wacom_penpartner_irq }, + { "Wacom Graphire", 8, 10206, 7422, 511, 32, GRAPHIRE, wacom_graphire_irq }, + { "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 32, GRAPHIRE, wacom_graphire_irq }, + { "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 32, GRAPHIRE, wacom_graphire_irq }, + { "Wacom Graphire3", 8, 10208, 7424, 511, 32, GRAPHIRE, wacom_graphire_irq }, + { "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 32, GRAPHIRE, wacom_graphire_irq }, + { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom PL400", 8, 5408, 4056, 255, 32, PL, wacom_pl_irq }, + { "Wacom PL500", 8, 6144, 4608, 255, 32, PL, wacom_pl_irq }, + { "Wacom PL600", 8, 6126, 4604, 255, 32, PL, wacom_pl_irq }, + { "Wacom PL600SX", 8, 6260, 5016, 255, 32, PL, wacom_pl_irq }, + { "Wacom PL550", 8, 6144, 4608, 511, 32, PL, wacom_pl_irq }, + { "Wacom PL800", 8, 7220, 5780, 511, 32, PL, wacom_pl_irq }, + { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Volito", 8, 5104, 3712, 511, 32, GRAPHIRE, wacom_graphire_irq }, + { "Wacom Cintiq Partner",8, 20480, 15360, 511, 32, PL, wacom_ptu_irq }, + { "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 15, INTUOS3, wacom_intuos_irq }, + { "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 15, INTUOS3, wacom_intuos_irq }, + { "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 15, INTUOS3, wacom_intuos_irq }, + { "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 15, CINTIQ, wacom_intuos_irq }, + { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_intuos_irq }, + { } }; static struct usb_device_id wacom_ids[] = { @@ -761,6 +709,7 @@ static struct usb_device_id wacom_ids[] = { { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) }, { } }; @@ -771,14 +720,9 @@ static int wacom_open(struct input_dev *dev) { struct wacom *wacom = dev->private; - if (wacom->open++) - return 0; - wacom->irq->dev = wacom->usbdev; - if (usb_submit_urb(wacom->irq, GFP_KERNEL)) { - wacom->open--; + if (usb_submit_urb(wacom->irq, GFP_KERNEL)) return -EIO; - } return 0; } @@ -787,8 +731,7 @@ static void wacom_close(struct input_dev *dev) { struct wacom *wacom = dev->private; - if (!--wacom->open) - usb_kill_urb(wacom->irq); + usb_kill_urb(wacom->irq); } static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -823,32 +766,33 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS); switch (wacom->features->type) { - case 1: + case GRAPHIRE: wacom->dev.evbit[0] |= BIT(EV_REL); wacom->dev.relbit[0] |= BIT(REL_WHEEL); wacom->dev.absbit[0] |= BIT(ABS_DISTANCE); wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); - wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2); + wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2); break; - case 4: /* new functions for Intuos3 */ + case INTUOS3: + case CINTIQ: wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER); wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7); wacom->dev.absbit[0] |= BIT(ABS_RX) | BIT(ABS_RY); /* fall through */ - case 2: + case INTUOS: wacom->dev.evbit[0] |= BIT(EV_MSC) | BIT(EV_REL); wacom->dev.mscbit[0] |= BIT(MSC_SERIAL); wacom->dev.relbit[0] |= BIT(REL_WHEEL); wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE) | BIT(BTN_SIDE) | BIT(BTN_EXTRA); - wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOOL_BRUSH) + wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOOL_BRUSH) | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS) | BIT(BTN_STYLUS2); wacom->dev.absbit[0] |= BIT(ABS_DISTANCE) | BIT(ABS_WHEEL) | BIT(ABS_TILT_X) | BIT(ABS_TILT_Y) | BIT(ABS_RZ) | BIT(ABS_THROTTLE); break; - case 3: - wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER); + case PL: + wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER); break; } diff --git a/drivers/usb/input/xpad.c b/drivers/usb/input/xpad.c index d65edb2..a7fa1b1 100644 --- a/drivers/usb/input/xpad.c +++ b/drivers/usb/input/xpad.c @@ -104,13 +104,12 @@ MODULE_DEVICE_TABLE (usb, xpad_table); struct usb_xpad { struct input_dev dev; /* input device interface */ struct usb_device *udev; /* usb device */ - + struct urb *irq_in; /* urb for interrupt in report */ unsigned char *idata; /* input data */ dma_addr_t idata_dma; - + char phys[65]; /* physical device path */ - int open_count; /* reference count */ }; /* @@ -128,35 +127,35 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d struct input_dev *dev = &xpad->dev; input_regs(dev, regs); - + /* left stick */ input_report_abs(dev, ABS_X, (__s16) (((__s16)data[13] << 8) | data[12])); input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[15] << 8) | data[14])); - + /* right stick */ input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[17] << 8) | data[16])); input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[19] << 8) | data[18])); - + /* triggers left/right */ input_report_abs(dev, ABS_Z, data[10]); input_report_abs(dev, ABS_RZ, data[11]); - + /* digital pad */ input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04)); input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01)); - + /* start/back buttons and stick press left/right */ input_report_key(dev, BTN_START, (data[2] & 0x10) >> 4); input_report_key(dev, BTN_BACK, (data[2] & 0x20) >> 5); input_report_key(dev, BTN_THUMBL, (data[2] & 0x40) >> 6); input_report_key(dev, BTN_THUMBR, data[2] >> 7); - + /* "analog" buttons A, B, X, Y */ input_report_key(dev, BTN_A, data[4]); input_report_key(dev, BTN_B, data[5]); input_report_key(dev, BTN_X, data[6]); input_report_key(dev, BTN_Y, data[7]); - + /* "analog" buttons black, white */ input_report_key(dev, BTN_C, data[8]); input_report_key(dev, BTN_Z, data[9]); @@ -168,7 +167,7 @@ static void xpad_irq_in(struct urb *urb, struct pt_regs *regs) { struct usb_xpad *xpad = urb->context; int retval; - + switch (urb->status) { case 0: /* success */ @@ -183,7 +182,7 @@ static void xpad_irq_in(struct urb *urb, struct pt_regs *regs) dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); goto exit; } - + xpad_process_packet(xpad, 0, xpad->idata, regs); exit: @@ -196,25 +195,19 @@ exit: static int xpad_open (struct input_dev *dev) { struct usb_xpad *xpad = dev->private; - - if (xpad->open_count++) - return 0; - + xpad->irq_in->dev = xpad->udev; - if (usb_submit_urb(xpad->irq_in, GFP_KERNEL)) { - xpad->open_count--; + if (usb_submit_urb(xpad->irq_in, GFP_KERNEL)) return -EIO; - } - + return 0; } static void xpad_close (struct input_dev *dev) { struct usb_xpad *xpad = dev->private; - - if (!--xpad->open_count) - usb_kill_urb(xpad->irq_in); + + usb_kill_urb(xpad->irq_in); } static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -224,19 +217,19 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id struct usb_endpoint_descriptor *ep_irq_in; char path[64]; int i; - + for (i = 0; xpad_device[i].idVendor; i++) { if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) && (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct)) break; } - + if ((xpad = kmalloc (sizeof(struct usb_xpad), GFP_KERNEL)) == NULL) { err("cannot allocate memory for new pad"); return -ENOMEM; } memset(xpad, 0, sizeof(struct usb_xpad)); - + xpad->idata = usb_buffer_alloc(udev, XPAD_PKT_LEN, SLAB_ATOMIC, &xpad->idata_dma); if (!xpad->idata) { @@ -251,18 +244,18 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id kfree(xpad); return -ENOMEM; } - + ep_irq_in = &intf->cur_altsetting->endpoint[0].desc; - + usb_fill_int_urb(xpad->irq_in, udev, usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress), xpad->idata, XPAD_PKT_LEN, xpad_irq_in, xpad, ep_irq_in->bInterval); xpad->irq_in->transfer_dma = xpad->idata_dma; xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - + xpad->udev = udev; - + xpad->dev.id.bustype = BUS_USB; xpad->dev.id.vendor = le16_to_cpu(udev->descriptor.idVendor); xpad->dev.id.product = le16_to_cpu(udev->descriptor.idProduct); @@ -273,21 +266,21 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id xpad->dev.phys = xpad->phys; xpad->dev.open = xpad_open; xpad->dev.close = xpad_close; - + usb_make_path(udev, path, 64); snprintf(xpad->phys, 64, "%s/input0", path); - + xpad->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - + for (i = 0; xpad_btn[i] >= 0; i++) set_bit(xpad_btn[i], xpad->dev.keybit); - + for (i = 0; xpad_abs[i] >= 0; i++) { - + signed short t = xpad_abs[i]; - + set_bit(t, xpad->dev.absbit); - + switch (t) { case ABS_X: case ABS_Y: @@ -310,11 +303,11 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id break; } } - + input_register_device(&xpad->dev); - + printk(KERN_INFO "input: %s on %s", xpad->dev.name, path); - + usb_set_intfdata(intf, xpad); return 0; } @@ -322,7 +315,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id static void xpad_disconnect(struct usb_interface *intf) { struct usb_xpad *xpad = usb_get_intfdata (intf); - + usb_set_intfdata(intf, NULL); if (xpad) { usb_kill_urb(xpad->irq_in); diff --git a/drivers/usb/media/stv680.c b/drivers/usb/media/stv680.c index ae455c8..7398a7f 100644 --- a/drivers/usb/media/stv680.c +++ b/drivers/usb/media/stv680.c @@ -1375,9 +1375,13 @@ static int stv680_probe (struct usb_interface *intf, const struct usb_device_id (le16_to_cpu(dev->descriptor.idProduct) == USB_PENCAM_PRODUCT_ID)) { camera_name = "STV0680"; PDEBUG (0, "STV(i): STV0680 camera found."); + } else if ((le16_to_cpu(dev->descriptor.idVendor) == USB_CREATIVEGOMINI_VENDOR_ID) && + (le16_to_cpu(dev->descriptor.idProduct) == USB_CREATIVEGOMINI_PRODUCT_ID)) { + camera_name = "Creative WebCam Go Mini"; + PDEBUG (0, "STV(i): Creative WebCam Go Mini found."); } else { - PDEBUG (0, "STV(e): Vendor/Product ID do not match STV0680 values."); - PDEBUG (0, "STV(e): Check that the STV0680 camera is connected to the computer."); + PDEBUG (0, "STV(e): Vendor/Product ID do not match STV0680 or Creative WebCam Go Mini values."); + PDEBUG (0, "STV(e): Check that the STV0680 or Creative WebCam Go Mini camera is connected to the computer."); retval = -ENODEV; goto error; } diff --git a/drivers/usb/media/stv680.h b/drivers/usb/media/stv680.h index 7e0e314d..4459406 100644 --- a/drivers/usb/media/stv680.h +++ b/drivers/usb/media/stv680.h @@ -41,12 +41,17 @@ #define USB_PENCAM_VENDOR_ID 0x0553 #define USB_PENCAM_PRODUCT_ID 0x0202 + +#define USB_CREATIVEGOMINI_VENDOR_ID 0x041e +#define USB_CREATIVEGOMINI_PRODUCT_ID 0x4007 + #define PENCAM_TIMEOUT 1000 /* fmt 4 */ #define STV_VIDEO_PALETTE VIDEO_PALETTE_RGB24 static struct usb_device_id device_table[] = { {USB_DEVICE (USB_PENCAM_VENDOR_ID, USB_PENCAM_PRODUCT_ID)}, + {USB_DEVICE (USB_CREATIVEGOMINI_VENDOR_ID, USB_CREATIVEGOMINI_PRODUCT_ID)}, {} }; MODULE_DEVICE_TABLE (usb, device_table); diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index ce030d1..733acc2 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -1,4 +1,4 @@ -/* Siemens ID Mouse driver v0.5 +/* Siemens ID Mouse driver v0.6 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -11,6 +11,9 @@ Derived from the USB Skeleton driver 1.1, Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com) + Additional information provided by Martin Reising + <Martin.Reising@natural-computing.de> + */ #include <linux/config.h> @@ -25,29 +28,44 @@ #include <asm/uaccess.h> #include <linux/usb.h> +/* image constants */ #define WIDTH 225 -#define HEIGHT 288 -#define HEADER "P5 225 288 255 " +#define HEIGHT 289 +#define HEADER "P5 225 289 255 " #define IMGSIZE ((WIDTH * HEIGHT) + sizeof(HEADER)-1) -/* Version Information */ -#define DRIVER_VERSION "0.5" +/* version information */ +#define DRIVER_VERSION "0.6" #define DRIVER_SHORT "idmouse" #define DRIVER_AUTHOR "Florian 'Floe' Echtler <echtler@fs.tum.de>" #define DRIVER_DESC "Siemens ID Mouse FingerTIP Sensor Driver" -/* Siemens ID Mouse */ -#define USB_IDMOUSE_VENDOR_ID 0x0681 -#define USB_IDMOUSE_PRODUCT_ID 0x0005 - -/* we still need a minor number */ +/* minor number for misc USB devices */ #define USB_IDMOUSE_MINOR_BASE 132 +/* vendor and device IDs */ +#define ID_SIEMENS 0x0681 +#define ID_IDMOUSE 0x0005 +#define ID_CHERRY 0x0010 + +/* device ID table */ static struct usb_device_id idmouse_table[] = { - {USB_DEVICE(USB_IDMOUSE_VENDOR_ID, USB_IDMOUSE_PRODUCT_ID)}, - {} /* null entry at the end */ + {USB_DEVICE(ID_SIEMENS, ID_IDMOUSE)}, /* Siemens ID Mouse (Professional) */ + {USB_DEVICE(ID_SIEMENS, ID_CHERRY )}, /* Cherry FingerTIP ID Board */ + {} /* terminating null entry */ }; +/* sensor commands */ +#define FTIP_RESET 0x20 +#define FTIP_ACQUIRE 0x21 +#define FTIP_RELEASE 0x22 +#define FTIP_BLINK 0x23 /* LSB of value = blink pulse width */ +#define FTIP_SCROLL 0x24 + +#define ftip_command(dev, command, value, index) \ + usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), command, \ + USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, value, index, NULL, 0, 1000) + MODULE_DEVICE_TABLE(usb, idmouse_table); /* structure to hold all of our device specific stuff */ @@ -57,7 +75,8 @@ struct usb_idmouse { struct usb_interface *interface; /* the interface for this device */ unsigned char *bulk_in_buffer; /* the buffer to receive data */ - size_t bulk_in_size; /* the size of the receive buffer */ + size_t bulk_in_size; /* the maximum bulk packet size */ + size_t orig_bi_size; /* same as above, but reported by the device */ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ int open; /* if the port is open or not */ @@ -103,7 +122,7 @@ static struct usb_driver idmouse_driver = { .id_table = idmouse_table, }; -// prevent races between open() and disconnect() +/* prevent races between open() and disconnect() */ static DECLARE_MUTEX(disconnect_sem); static int idmouse_create_image(struct usb_idmouse *dev) @@ -112,42 +131,34 @@ static int idmouse_create_image(struct usb_idmouse *dev) int bulk_read = 0; int result = 0; - if (dev->bulk_in_size < sizeof(HEADER)) - return -ENOMEM; - - memcpy(dev->bulk_in_buffer,HEADER,sizeof(HEADER)-1); + memcpy(dev->bulk_in_buffer, HEADER, sizeof(HEADER)-1); bytes_read += sizeof(HEADER)-1; - /* Dump the setup packets. Yes, they are uncommented, simply - because they were sniffed under Windows using SnoopyPro. - I _guess_ that 0x22 is a kind of reset command and 0x21 - means init.. - */ - result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), - 0x21, 0x42, 0x0001, 0x0002, NULL, 0, 1000); - if (result < 0) - return result; - result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), - 0x20, 0x42, 0x0001, 0x0002, NULL, 0, 1000); + /* reset the device and set a fast blink rate */ + result = ftip_command(dev, FTIP_RELEASE, 0, 0); if (result < 0) - return result; - result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), - 0x22, 0x42, 0x0000, 0x0002, NULL, 0, 1000); + goto reset; + result = ftip_command(dev, FTIP_BLINK, 1, 0); if (result < 0) - return result; + goto reset; - result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), - 0x21, 0x42, 0x0001, 0x0002, NULL, 0, 1000); + /* initialize the sensor - sending this command twice */ + /* significantly reduces the rate of failed reads */ + result = ftip_command(dev, FTIP_ACQUIRE, 0, 0); if (result < 0) - return result; - result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), - 0x20, 0x42, 0x0001, 0x0002, NULL, 0, 1000); + goto reset; + result = ftip_command(dev, FTIP_ACQUIRE, 0, 0); if (result < 0) - return result; - result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), - 0x20, 0x42, 0x0000, 0x0002, NULL, 0, 1000); + goto reset; + + /* start the readout - sending this command twice */ + /* presumably enables the high dynamic range mode */ + result = ftip_command(dev, FTIP_RESET, 0, 0); if (result < 0) - return result; + goto reset; + result = ftip_command(dev, FTIP_RESET, 0, 0); + if (result < 0) + goto reset; /* loop over a blocking bulk read to get data from the device */ while (bytes_read < IMGSIZE) { @@ -155,22 +166,40 @@ static int idmouse_create_image(struct usb_idmouse *dev) usb_rcvbulkpipe (dev->udev, dev->bulk_in_endpointAddr), dev->bulk_in_buffer + bytes_read, dev->bulk_in_size, &bulk_read, 5000); - if (result < 0) - return result; - if (signal_pending(current)) - return -EINTR; + if (result < 0) { + /* Maybe this error was caused by the increased packet size? */ + /* Reset to the original value and tell userspace to retry. */ + if (dev->bulk_in_size != dev->orig_bi_size) { + dev->bulk_in_size = dev->orig_bi_size; + result = -EAGAIN; + } + break; + } + if (signal_pending(current)) { + result = -EINTR; + break; + } bytes_read += bulk_read; } /* reset the device */ - result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), - 0x22, 0x42, 0x0000, 0x0002, NULL, 0, 1000); - if (result < 0) - return result; +reset: + ftip_command(dev, FTIP_RELEASE, 0, 0); + + /* check for valid image */ + /* right border should be black (0x00) */ + for (bytes_read = sizeof(HEADER)-1 + WIDTH-1; bytes_read < IMGSIZE; bytes_read += WIDTH) + if (dev->bulk_in_buffer[bytes_read] != 0x00) + return -EAGAIN; - /* should be IMGSIZE == 64815 */ + /* lower border should be white (0xFF) */ + for (bytes_read = IMGSIZE-WIDTH; bytes_read < IMGSIZE-1; bytes_read++) + if (dev->bulk_in_buffer[bytes_read] != 0xFF) + return -EAGAIN; + + /* should be IMGSIZE == 65040 */ dbg("read %d bytes fingerprint data", bytes_read); - return 0; + return result; } static inline void idmouse_delete(struct usb_idmouse *dev) @@ -282,10 +311,10 @@ static ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count dev = (struct usb_idmouse *) file->private_data; - // lock this object + /* lock this object */ down (&dev->sem); - // verify that the device wasn't unplugged + /* verify that the device wasn't unplugged */ if (!dev->present) { up (&dev->sem); return -ENODEV; @@ -296,8 +325,7 @@ static ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count return 0; } - if (count > IMGSIZE - *ppos) - count = IMGSIZE - *ppos; + count = min ((loff_t)count, IMGSIZE - (*ppos)); if (copy_to_user (buffer, dev->bulk_in_buffer + *ppos, count)) { result = -EFAULT; @@ -306,7 +334,7 @@ static ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count *ppos += count; } - // unlock the device + /* unlock the device */ up(&dev->sem); return result; } @@ -318,7 +346,6 @@ static int idmouse_probe(struct usb_interface *interface, struct usb_idmouse *dev = NULL; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; - size_t buffer_size; int result; /* check if we have gotten the data or the hid interface */ @@ -344,11 +371,11 @@ static int idmouse_probe(struct usb_interface *interface, USB_ENDPOINT_XFER_BULK)) { /* we found a bulk in endpoint */ - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); - dev->bulk_in_size = buffer_size; + dev->orig_bi_size = le16_to_cpu(endpoint->wMaxPacketSize); + dev->bulk_in_size = 0x200; /* works _much_ faster */ dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = - kmalloc(IMGSIZE + buffer_size, GFP_KERNEL); + kmalloc(IMGSIZE + dev->bulk_in_size, GFP_KERNEL); if (!dev->bulk_in_buffer) { err("Unable to allocate input buffer."); diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 3104f28..cda7249 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -461,7 +461,7 @@ static int perform_sglist ( static unsigned realworld = 1; module_param (realworld, uint, 0); -MODULE_PARM_DESC (realworld, "clear to demand stricter ch9 compliance"); +MODULE_PARM_DESC (realworld, "clear to demand stricter spec compliance"); static int get_altsetting (struct usbtest_dev *dev) { @@ -604,9 +604,8 @@ static int ch9_postconfig (struct usbtest_dev *dev) USB_DIR_IN | USB_RECIP_DEVICE, 0, 0, dev->buf, 1, USB_CTRL_GET_TIMEOUT); if (retval != 1 || dev->buf [0] != expected) { - dev_dbg (&iface->dev, - "get config --> %d (%d)\n", retval, - expected); + dev_dbg (&iface->dev, "get config --> %d %d (1 %d)\n", + retval, dev->buf[0], expected); return (retval < 0) ? retval : -EDOM; } } @@ -1243,7 +1242,7 @@ static int ctrl_out (struct usbtest_dev *dev, char *what = "?"; struct usb_device *udev; - if (length > 0xffff || vary >= length) + if (length < 1 || length > 0xffff || vary >= length) return -EINVAL; buf = kmalloc(length, SLAB_KERNEL); @@ -1266,6 +1265,11 @@ static int ctrl_out (struct usbtest_dev *dev, 0, 0, buf, len, USB_CTRL_SET_TIMEOUT); if (retval != len) { what = "write"; + if (retval >= 0) { + INFO(dev, "ctrl_out, wlen %d (expected %d)\n", + retval, len); + retval = -EBADMSG; + } break; } @@ -1275,6 +1279,11 @@ static int ctrl_out (struct usbtest_dev *dev, 0, 0, buf, len, USB_CTRL_GET_TIMEOUT); if (retval != len) { what = "read"; + if (retval >= 0) { + INFO(dev, "ctrl_out, rlen %d (expected %d)\n", + retval, len); + retval = -EBADMSG; + } break; } @@ -1293,8 +1302,13 @@ static int ctrl_out (struct usbtest_dev *dev, } len += vary; + + /* [real world] the "zero bytes IN" case isn't really used. + * hardware can easily trip up in this wierd case, since its + * status stage is IN, not OUT like other ep0in transfers. + */ if (len > length) - len = 0; + len = realworld ? 1 : 0; } if (retval < 0) @@ -1519,6 +1533,11 @@ usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf) if (down_interruptible (&dev->sem)) return -ERESTARTSYS; + if (intf->dev.power.power_state != PMSG_ON) { + up (&dev->sem); + return -EHOSTUNREACH; + } + /* some devices, like ez-usb default devices, need a non-default * altsetting to have any active endpoints. some tests change * altsettings; force a default so most tests don't need to check. @@ -1762,8 +1781,10 @@ usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf) case 14: if (!dev->info->ctrl_out) break; - dev_dbg (&intf->dev, "TEST 14: %d ep0out, 0..%d vary %d\n", - param->iterations, param->length, param->vary); + dev_dbg (&intf->dev, "TEST 14: %d ep0out, %d..%d vary %d\n", + param->iterations, + realworld ? 1 : 0, param->length, + param->vary); retval = ctrl_out (dev, param->iterations, param->length, param->vary); break; @@ -1927,6 +1948,27 @@ usbtest_probe (struct usb_interface *intf, const struct usb_device_id *id) return 0; } +static int usbtest_suspend (struct usb_interface *intf, pm_message_t message) +{ + struct usbtest_dev *dev = usb_get_intfdata (intf); + + down (&dev->sem); + intf->dev.power.power_state = PMSG_SUSPEND; + up (&dev->sem); + return 0; +} + +static int usbtest_resume (struct usb_interface *intf) +{ + struct usbtest_dev *dev = usb_get_intfdata (intf); + + down (&dev->sem); + intf->dev.power.power_state = PMSG_ON; + up (&dev->sem); + return 0; +} + + static void usbtest_disconnect (struct usb_interface *intf) { struct usbtest_dev *dev = usb_get_intfdata (intf); @@ -2115,6 +2157,8 @@ static struct usb_driver usbtest_driver = { .probe = usbtest_probe, .ioctl = usbtest_ioctl, .disconnect = usbtest_disconnect, + .suspend = usbtest_suspend, + .resume = usbtest_resume, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c index d976790..5f4496d 100644 --- a/drivers/usb/net/pegasus.c +++ b/drivers/usb/net/pegasus.c @@ -1166,7 +1166,7 @@ static void pegasus_set_multicast(struct net_device *net) pegasus->eth_regs[EthCtrl2] |= RX_PROMISCUOUS; if (netif_msg_link(pegasus)) pr_info("%s: Promiscuous mode enabled.\n", net->name); - } else if ((net->mc_count > multicast_filter_limit) || + } else if (net->mc_count || (net->flags & IFF_ALLMULTI)) { pegasus->eth_regs[EthCtrl0] |= RX_MULTICAST; pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS; diff --git a/drivers/usb/net/pegasus.h b/drivers/usb/net/pegasus.h index 13ccede..b98f2a8 100644 --- a/drivers/usb/net/pegasus.h +++ b/drivers/usb/net/pegasus.h @@ -249,6 +249,8 @@ PEGASUS_DEV( "Kingston KNU101TX Ethernet", VENDOR_KINGSTON, 0x000a, DEFAULT_GPIO_RESET) PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x4002, DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "LANEED USB Ethernet LD-USBL/TX", VENDOR_LANEED, 0x4005, + DEFAULT_GPIO_RESET | PEGASUS_II) PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x400b, DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "LANEED USB Ethernet LD-USB/T", VENDOR_LANEED, 0xabc1, diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c index 8fb2233..626b016 100644 --- a/drivers/usb/net/rtl8150.c +++ b/drivers/usb/net/rtl8150.c @@ -667,7 +667,7 @@ static void rtl8150_set_multicast(struct net_device *netdev) if (netdev->flags & IFF_PROMISC) { dev->rx_creg |= cpu_to_le16(0x0001); info("%s: promiscuous mode", netdev->name); - } else if ((netdev->mc_count > multicast_filter_limit) || + } else if (netdev->mc_count || (netdev->flags & IFF_ALLMULTI)) { dev->rx_creg &= cpu_to_le16(0xfffe); dev->rx_creg |= cpu_to_le16(0x0002); diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c index 4cbb408..8a945f4 100644 --- a/drivers/usb/net/usbnet.c +++ b/drivers/usb/net/usbnet.c @@ -1429,7 +1429,7 @@ static int generic_cdc_bind (struct usbnet *dev, struct usb_interface *intf) info->ether = (void *) buf; if (info->ether->bLength != sizeof *info->ether) { dev_dbg (&intf->dev, "CDC ether len %u\n", - info->u->bLength); + info->ether->bLength); goto bad_desc; } dev->net->mtu = le16_to_cpup ( diff --git a/drivers/usb/net/zd1201.c b/drivers/usb/net/zd1201.c index 341ae5f..3b387b0 100644 --- a/drivers/usb/net/zd1201.c +++ b/drivers/usb/net/zd1201.c @@ -1884,12 +1884,53 @@ static void zd1201_disconnect(struct usb_interface *interface) kfree(zd); } +#ifdef CONFIG_PM + +static int zd1201_suspend(struct usb_interface *interface, + pm_message_t message) +{ + struct zd1201 *zd = usb_get_intfdata(interface); + + netif_device_detach(zd->dev); + + zd->was_enabled = zd->mac_enabled; + + if (zd->was_enabled) + return zd1201_disable(zd); + else + return 0; +} + +static int zd1201_resume(struct usb_interface *interface) +{ + struct zd1201 *zd = usb_get_intfdata(interface); + + if (!zd || !zd->dev) + return -ENODEV; + + netif_device_attach(zd->dev); + + if (zd->was_enabled) + return zd1201_enable(zd); + else + return 0; +} + +#else + +#define zd1201_suspend NULL +#define zd1201_resume NULL + +#endif + static struct usb_driver zd1201_usb = { .owner = THIS_MODULE, .name = "zd1201", .probe = zd1201_probe, .disconnect = zd1201_disconnect, .id_table = zd1201_table, + .suspend = zd1201_suspend, + .resume = zd1201_resume, }; static int __init zd1201_init(void) diff --git a/drivers/usb/net/zd1201.h b/drivers/usb/net/zd1201.h index 1627c71..235f0ee 100644 --- a/drivers/usb/net/zd1201.h +++ b/drivers/usb/net/zd1201.h @@ -46,6 +46,7 @@ struct zd1201 { char essid[IW_ESSID_MAX_SIZE+1]; int essidlen; int mac_enabled; + int was_enabled; int monitor; int encode_enabled; int encode_restricted; diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 46a204c..b5b4310 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -213,10 +213,14 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b return (0); } - if (port->write_urb->status == -EINPROGRESS) { + spin_lock(&port->lock); + if (port->write_urb_busy) { + spin_unlock(&port->lock); dbg("%s - already writing", __FUNCTION__); - return (0); + return 0; } + port->write_urb_busy = 1; + spin_unlock(&port->lock); spin_lock_irqsave(&priv->lock, flags); @@ -224,6 +228,7 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b /* To much data for buffer. Reset buffer. */ priv->wrfilled=0; spin_unlock_irqrestore(&priv->lock, flags); + port->write_urb_busy = 0; return (0); } @@ -268,6 +273,7 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b priv->wrfilled=0; priv->wrsent=0; spin_unlock_irqrestore(&priv->lock, flags); + port->write_urb_busy = 0; return 0; } @@ -412,7 +418,8 @@ static void cyberjack_write_bulk_callback (struct urb *urb, struct pt_regs *regs struct cyberjack_private *priv = usb_get_serial_port_data(port); dbg("%s - port %d", __FUNCTION__, port->number); - + + port->write_urb_busy = 0; if (urb->status) { dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); return; @@ -424,12 +431,6 @@ static void cyberjack_write_bulk_callback (struct urb *urb, struct pt_regs *regs if( priv->wrfilled ) { int length, blksize, result; - if (port->write_urb->status == -EINPROGRESS) { - dbg("%s - already writing", __FUNCTION__); - spin_unlock(&priv->lock); - return; - } - dbg("%s - transmitting data (frame n)", __FUNCTION__); length = ((priv->wrfilled - priv->wrsent) > port->bulk_out_size) ? diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 99214aa..ddde5fb 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -174,10 +174,14 @@ int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char * /* only do something if we have a bulk out endpoint */ if (serial->num_bulk_out) { - if (port->write_urb->status == -EINPROGRESS) { + spin_lock(&port->lock); + if (port->write_urb_busy) { + spin_unlock(&port->lock); dbg("%s - already writing", __FUNCTION__); - return (0); + return 0; } + port->write_urb_busy = 1; + spin_unlock(&port->lock); count = (count > port->bulk_out_size) ? port->bulk_out_size : count; @@ -195,17 +199,20 @@ int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char * usb_serial_generic_write_bulk_callback), port); /* send the data out the bulk port */ + port->write_urb_busy = 1; result = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (result) + if (result) { dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result); - else + /* don't have to grab the lock here, as we will retry if != 0 */ + port->write_urb_busy = 0; + } else result = count; return result; } /* no bulk out, so return 0 bytes written */ - return (0); + return 0; } int usb_serial_generic_write_room (struct usb_serial_port *port) @@ -214,9 +221,9 @@ int usb_serial_generic_write_room (struct usb_serial_port *port) int room = 0; dbg("%s - port %d", __FUNCTION__, port->number); - + if (serial->num_bulk_out) { - if (port->write_urb->status != -EINPROGRESS) + if (port->write_urb_busy) room = port->bulk_out_size; } @@ -232,7 +239,7 @@ int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port) dbg("%s - port %d", __FUNCTION__, port->number); if (serial->num_bulk_out) { - if (port->write_urb->status == -EINPROGRESS) + if (port->write_urb_busy) chars = port->write_urb->transfer_buffer_length; } @@ -291,6 +298,7 @@ void usb_serial_generic_write_bulk_callback (struct urb *urb, struct pt_regs *re dbg("%s - port %d", __FUNCTION__, port->number); + port->write_urb_busy = 0; if (urb->status) { dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); return; diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 3bd69c4..c05c2a2 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -818,11 +818,6 @@ static void ipaq_write_gather(struct usb_serial_port *port) struct ipaq_packet *pkt, *tmp; struct urb *urb = port->write_urb; - if (urb->status == -EINPROGRESS) { - /* Should never happen */ - err("%s - flushing while urb is active !", __FUNCTION__); - return; - } room = URBDATA_SIZE; list_for_each_entry_safe(pkt, tmp, &priv->queue, list) { count = min(room, (int)(pkt->len - pkt->written)); diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index 11105d7..85e2424 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -399,16 +399,21 @@ static int ipw_write(struct usb_serial_port *port, const unsigned char *buf, int dbg("%s - write request of 0 bytes", __FUNCTION__); return 0; } - - /* Racy and broken, FIXME properly! */ - if (port->write_urb->status == -EINPROGRESS) + + spin_lock(&port->lock); + if (port->write_urb_busy) { + spin_unlock(&port->lock); + dbg("%s - already writing", __FUNCTION__); return 0; + } + port->write_urb_busy = 1; + spin_unlock(&port->lock); count = min(count, port->bulk_out_size); memcpy(port->bulk_out_buffer, buf, count); dbg("%s count now:%d", __FUNCTION__, count); - + usb_fill_bulk_urb(port->write_urb, dev, usb_sndbulkpipe(dev, port->bulk_out_endpointAddress), port->write_urb->transfer_buffer, @@ -418,6 +423,7 @@ static int ipw_write(struct usb_serial_port *port, const unsigned char *buf, int ret = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (ret != 0) { + port->write_urb_busy = 0; dbg("%s - usb_submit_urb(write bulk) failed with error = %d", __FUNCTION__, ret); return ret; } diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 59f234d..937b2fd 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -341,10 +341,14 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int if (count == 0) return 0; - if (port->write_urb->status == -EINPROGRESS) { - dbg ("%s - already writing", __FUNCTION__); + spin_lock(&port->lock); + if (port->write_urb_busy) { + spin_unlock(&port->lock); + dbg("%s - already writing", __FUNCTION__); return 0; } + port->write_urb_busy = 1; + spin_unlock(&port->lock); transfer_buffer = port->write_urb->transfer_buffer; transfer_size = min(count, port->bulk_out_size - 1); @@ -374,9 +378,10 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int port->write_urb->transfer_flags = URB_ZERO_PACKET; result = usb_submit_urb (port->write_urb, GFP_ATOMIC); - if (result) + if (result) { + port->write_urb_busy = 0; dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result); - else + } else result = transfer_size; return result; @@ -387,7 +392,8 @@ static void ir_write_bulk_callback (struct urb *urb, struct pt_regs *regs) struct usb_serial_port *port = (struct usb_serial_port *)urb->context; dbg("%s - port %d", __FUNCTION__, port->number); - + + port->write_urb_busy = 0; if (urb->status) { dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); return; diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 7fd0aa9..635c384 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -520,9 +520,13 @@ static int keyspan_pda_write(struct usb_serial_port *port, the TX urb is in-flight (wait until it completes) the device is full (wait until it says there is room) */ - if (port->write_urb->status == -EINPROGRESS || priv->tx_throttled ) { - return( 0 ); + spin_lock(&port->lock); + if (port->write_urb_busy || priv->tx_throttled) { + spin_unlock(&port->lock); + return 0; } + port->write_urb_busy = 1; + spin_unlock(&port->lock); /* At this point the URB is in our control, nobody else can submit it again (the only sudden transition was the one from EINPROGRESS to @@ -570,7 +574,7 @@ static int keyspan_pda_write(struct usb_serial_port *port, memcpy (port->write_urb->transfer_buffer, buf, count); /* send the data out the bulk port */ port->write_urb->transfer_buffer_length = count; - + priv->tx_room -= count; port->write_urb->dev = port->serial->dev; @@ -593,6 +597,8 @@ static int keyspan_pda_write(struct usb_serial_port *port, rc = count; exit: + if (rc < 0) + port->write_urb_busy = 0; return rc; } @@ -602,6 +608,7 @@ static void keyspan_pda_write_bulk_callback (struct urb *urb, struct pt_regs *re struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct keyspan_pda_private *priv; + port->write_urb_busy = 0; priv = usb_get_serial_port_data(port); /* queue up a wakeup at scheduler time */ @@ -626,12 +633,12 @@ static int keyspan_pda_write_room (struct usb_serial_port *port) static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port) { struct keyspan_pda_private *priv; - + priv = usb_get_serial_port_data(port); - + /* when throttled, return at least WAKEUP_CHARS to tell select() (via n_tty.c:normal_poll() ) that we're not writeable. */ - if( port->write_urb->status == -EINPROGRESS || priv->tx_throttled ) + if (port->write_urb_busy || priv->tx_throttled) return 256; return 0; } diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index b5f2c06..6a99ae1 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -254,10 +254,15 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf dbg("%s - write request of 0 bytes", __FUNCTION__); return (0); } - if (wport->write_urb->status == -EINPROGRESS) { + + spin_lock(&port->lock); + if (port->write_urb_busy) { + spin_unlock(&port->lock); dbg("%s - already writing", __FUNCTION__); - return (0); + return 0; } + port->write_urb_busy = 1; + spin_unlock(&port->lock); count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count; @@ -275,9 +280,10 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf wport->write_urb->dev = serial->dev; result = usb_submit_urb(wport->write_urb, GFP_ATOMIC); - if (result) + if (result) { + port->write_urb_busy = 0; err("%s - failed submitting write urb, error %d", __FUNCTION__, result); - else + } else result = count; return result; @@ -291,7 +297,7 @@ static int omninet_write_room (struct usb_serial_port *port) int room = 0; // Default: no room - if (wport->write_urb->status != -EINPROGRESS) + if (wport->write_urb_busy) room = wport->bulk_out_size - OMNINET_HEADERLEN; // dbg("omninet_write_room returns %d", room); @@ -306,6 +312,7 @@ static void omninet_write_bulk_callback (struct urb *urb, struct pt_regs *regs) // dbg("omninet_write_bulk_callback, port %0x\n", port); + port->write_urb_busy = 0; if (urb->status) { dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); return; diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 0e85ed6..96a1756 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -299,10 +299,14 @@ static int safe_write (struct usb_serial_port *port, const unsigned char *buf, i dbg ("%s - write request of 0 bytes", __FUNCTION__); return (0); } - if (port->write_urb->status == -EINPROGRESS) { - dbg ("%s - already writing", __FUNCTION__); - return (0); + spin_lock(&port->lock); + if (port->write_urb_busy) { + spin_unlock(&port->lock); + dbg("%s - already writing", __FUNCTION__); + return 0; } + port->write_urb_busy = 1; + spin_unlock(&port->lock); packet_length = port->bulk_out_size; // get max packetsize @@ -354,6 +358,7 @@ static int safe_write (struct usb_serial_port *port, const unsigned char *buf, i #endif port->write_urb->dev = port->serial->dev; if ((result = usb_submit_urb (port->write_urb, GFP_KERNEL))) { + port->write_urb_busy = 0; err ("%s - failed submitting write urb, error %d", __FUNCTION__, result); return 0; } @@ -368,7 +373,7 @@ static int safe_write_room (struct usb_serial_port *port) dbg ("%s", __FUNCTION__); - if (port->write_urb->status != -EINPROGRESS) + if (port->write_urb_busy) room = port->bulk_out_size - (safe ? 2 : 0); if (room) { diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 5da76dd..0267b26 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1047,6 +1047,7 @@ int usb_serial_probe(struct usb_interface *interface, memset(port, 0x00, sizeof(struct usb_serial_port)); port->number = i + serial->minor; port->serial = serial; + spin_lock_init(&port->lock); INIT_WORK(&port->work, usb_serial_port_softint, port); serial->port[i] = port; } diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h index d1f0c40..57f92f0 100644 --- a/drivers/usb/serial/usb-serial.h +++ b/drivers/usb/serial/usb-serial.h @@ -69,6 +69,7 @@ * usb_serial_port: structure for the specific ports of a device. * @serial: pointer back to the struct usb_serial owner of this port. * @tty: pointer to the corresponding tty for this port. + * @lock: spinlock to grab when updating portions of this structure. * @number: the number of the port (the minor number). * @interrupt_in_buffer: pointer to the interrupt in buffer for this port. * @interrupt_in_urb: pointer to the interrupt in struct urb for this port. @@ -98,6 +99,7 @@ struct usb_serial_port { struct usb_serial * serial; struct tty_struct * tty; + spinlock_t lock; unsigned char number; unsigned char * interrupt_in_buffer; @@ -117,6 +119,7 @@ struct usb_serial_port { unsigned char * bulk_out_buffer; int bulk_out_size; struct urb * write_urb; + int write_urb_busy; __u8 bulk_out_endpointAddress; wait_queue_head_t write_wait; diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index e43eddc..af294bb 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -155,6 +155,15 @@ static int slave_configure(struct scsi_device *sdev) * If this device makes that mistake, tell the sd driver. */ if (us->flags & US_FL_FIX_CAPACITY) sdev->fix_capacity = 1; + + /* USB-IDE bridges tend to report SK = 0x04 (Non-recoverable + * Hardware Error) when any low-level error occurs, + * recoverable or not. Setting this flag tells the SCSI + * midlayer to retry such commands, which frequently will + * succeed and fix the error. The worst this can lead to + * is an occasional series of retries that will all fail. */ + sdev->retry_hwerror = 1; + } else { /* Non-disk-type devices don't need to blacklist any pages @@ -255,50 +264,23 @@ static int device_reset(struct scsi_cmnd *srb) /* lock the device pointers and do the reset */ down(&(us->dev_semaphore)); - if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { - result = FAILED; - US_DEBUGP("No reset during disconnect\n"); - } else - result = us->transport_reset(us); + result = us->transport_reset(us); up(&(us->dev_semaphore)); - return result; + return result < 0 ? FAILED : SUCCESS; } -/* This resets the device's USB port. */ -/* It refuses to work if there's more than one interface in - * the device, so that other users are not affected. */ +/* Simulate a SCSI bus reset by resetting the device's USB port. */ /* This is always called with scsi_lock(host) held */ static int bus_reset(struct scsi_cmnd *srb) { struct us_data *us = host_to_us(srb->device->host); - int result, rc; + int result; US_DEBUGP("%s called\n", __FUNCTION__); - /* The USB subsystem doesn't handle synchronisation between - * a device's several drivers. Therefore we reset only devices - * with just one interface, which we of course own. */ - down(&(us->dev_semaphore)); - if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { - result = -EIO; - US_DEBUGP("No reset during disconnect\n"); - } else if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1) { - result = -EBUSY; - US_DEBUGP("Refusing to reset a multi-interface device\n"); - } else { - rc = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf); - if (rc < 0) { - US_DEBUGP("unable to lock device for reset: %d\n", rc); - result = rc; - } else { - result = usb_reset_device(us->pusb_dev); - if (rc) - usb_unlock_device(us->pusb_dev); - US_DEBUGP("usb_reset_device returns %d\n", result); - } - } + result = usb_stor_port_reset(us); up(&(us->dev_semaphore)); /* lock the host for the return */ @@ -320,6 +302,14 @@ void usb_stor_report_device_reset(struct us_data *us) } } +/* Report a driver-initiated bus reset to the SCSI layer. + * Calling this for a SCSI-initiated reset is unnecessary but harmless. + * The caller must own the SCSI host lock. */ +void usb_stor_report_bus_reset(struct us_data *us) +{ + scsi_report_bus_reset(us_to_host(us), 0); +} + /*********************************************************************** * /proc/scsi/ functions ***********************************************************************/ diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h index d0a49af..737e4fa 100644 --- a/drivers/usb/storage/scsiglue.h +++ b/drivers/usb/storage/scsiglue.h @@ -42,6 +42,7 @@ #define _SCSIGLUE_H_ extern void usb_stor_report_device_reset(struct us_data *us); +extern void usb_stor_report_bus_reset(struct us_data *us); extern unsigned char usb_stor_sense_invalidCDB[18]; extern struct scsi_host_template usb_stor_host_template; diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 9743e28..e6b1c6c 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -266,8 +266,9 @@ int usb_stor_clear_halt(struct us_data *us, unsigned int pipe) NULL, 0, 3*HZ); /* reset the endpoint toggle */ - usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe), - usb_pipeout(pipe), 0); + if (result >= 0) + usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe), + usb_pipeout(pipe), 0); US_DEBUGP("%s: result = %d\n", __FUNCTION__, result); return result; @@ -540,15 +541,15 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) */ if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { US_DEBUGP("-- command was aborted\n"); - goto Handle_Abort; + srb->result = DID_ABORT << 16; + goto Handle_Errors; } /* if there is a transport error, reset and don't auto-sense */ if (result == USB_STOR_TRANSPORT_ERROR) { US_DEBUGP("-- transport indicates error, resetting\n"); - us->transport_reset(us); srb->result = DID_ERROR << 16; - return; + goto Handle_Errors; } /* if the transport provided its own sense data, don't auto-sense */ @@ -668,7 +669,8 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { US_DEBUGP("-- auto-sense aborted\n"); - goto Handle_Abort; + srb->result = DID_ABORT << 16; + goto Handle_Errors; } if (temp_result != USB_STOR_TRANSPORT_GOOD) { US_DEBUGP("-- auto-sense failure\n"); @@ -677,9 +679,9 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) * multi-target device, since failure of an * auto-sense is perfectly valid */ - if (!(us->flags & US_FL_SCM_MULT_TARG)) - us->transport_reset(us); srb->result = DID_ERROR << 16; + if (!(us->flags & US_FL_SCM_MULT_TARG)) + goto Handle_Errors; return; } @@ -720,12 +722,28 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) return; - /* abort processing: the bulk-only transport requires a reset - * following an abort */ - Handle_Abort: - srb->result = DID_ABORT << 16; - if (us->protocol == US_PR_BULK) + /* Error and abort processing: try to resynchronize with the device + * by issuing a port reset. If that fails, try a class-specific + * device reset. */ + Handle_Errors: + + /* Let the SCSI layer know we are doing a reset, set the + * RESETTING bit, and clear the ABORTING bit so that the reset + * may proceed. */ + scsi_lock(us_to_host(us)); + usb_stor_report_bus_reset(us); + set_bit(US_FLIDX_RESETTING, &us->flags); + clear_bit(US_FLIDX_ABORTING, &us->flags); + scsi_unlock(us_to_host(us)); + + result = usb_stor_port_reset(us); + if (result < 0) { + scsi_lock(us_to_host(us)); + usb_stor_report_device_reset(us); + scsi_unlock(us_to_host(us)); us->transport_reset(us); + } + clear_bit(US_FLIDX_RESETTING, &us->flags); } /* Stop the current URB transfer */ @@ -1124,7 +1142,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) * It's handy that every transport mechanism uses the control endpoint for * resets. * - * Basically, we send a reset with a 20-second timeout, so we don't get + * Basically, we send a reset with a 5-second timeout, so we don't get * jammed attempting to do the reset. */ static int usb_stor_reset_common(struct us_data *us, @@ -1133,28 +1151,18 @@ static int usb_stor_reset_common(struct us_data *us, { int result; int result2; - int rc = FAILED; - /* Let the SCSI layer know we are doing a reset, set the - * RESETTING bit, and clear the ABORTING bit so that the reset - * may proceed. - */ - scsi_lock(us_to_host(us)); - usb_stor_report_device_reset(us); - set_bit(US_FLIDX_RESETTING, &us->flags); - clear_bit(US_FLIDX_ABORTING, &us->flags); - scsi_unlock(us_to_host(us)); + if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { + US_DEBUGP("No reset during disconnect\n"); + return -EIO; + } - /* A 20-second timeout may seem rather long, but a LaCie - * StudioDrive USB2 device takes 16+ seconds to get going - * following a powerup or USB attach event. - */ result = usb_stor_control_msg(us, us->send_ctrl_pipe, request, requesttype, value, index, data, size, - 20*HZ); + 5*HZ); if (result < 0) { US_DEBUGP("Soft reset failed: %d\n", result); - goto Done; + return result; } /* Give the device some time to recover from the reset, @@ -1164,7 +1172,7 @@ static int usb_stor_reset_common(struct us_data *us, HZ*6); if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { US_DEBUGP("Reset interrupted by disconnect\n"); - goto Done; + return -EIO; } US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n"); @@ -1173,17 +1181,14 @@ static int usb_stor_reset_common(struct us_data *us, US_DEBUGP("Soft reset: clearing bulk-out endpoint halt\n"); result2 = usb_stor_clear_halt(us, us->send_bulk_pipe); - /* return a result code based on the result of the control message */ - if (result < 0 || result2 < 0) { + /* return a result code based on the result of the clear-halts */ + if (result >= 0) + result = result2; + if (result < 0) US_DEBUGP("Soft reset failed\n"); - goto Done; - } - US_DEBUGP("Soft reset done\n"); - rc = SUCCESS; - - Done: - clear_bit(US_FLIDX_RESETTING, &us->flags); - return rc; + else + US_DEBUGP("Soft reset done\n"); + return result; } /* This issues a CB[I] Reset to the device in question @@ -1213,3 +1218,32 @@ int usb_stor_Bulk_reset(struct us_data *us) USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, us->ifnum, NULL, 0); } + +/* Issue a USB port reset to the device. But don't do anything if + * there's more than one interface in the device, so that other users + * are not affected. */ +int usb_stor_port_reset(struct us_data *us) +{ + int result, rc; + + if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { + result = -EIO; + US_DEBUGP("No reset during disconnect\n"); + } else if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1) { + result = -EBUSY; + US_DEBUGP("Refusing to reset a multi-interface device\n"); + } else { + result = rc = + usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf); + if (result < 0) { + US_DEBUGP("unable to lock device for reset: %d\n", + result); + } else { + result = usb_reset_device(us->pusb_dev); + if (rc) + usb_unlock_device(us->pusb_dev); + US_DEBUGP("usb_reset_device returns %d\n", result); + } + } + return result; +} diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index e25f8d8..8d9e066 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -171,4 +171,5 @@ extern int usb_stor_bulk_transfer_buf(struct us_data *us, unsigned int pipe, extern int usb_stor_bulk_transfer_sg(struct us_data *us, unsigned int pipe, void *buf, unsigned int length, int use_sg, int *residual); +extern int usb_stor_port_reset(struct us_data *us); #endif diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index 9789115..7bc1d44 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -350,10 +350,8 @@ static int default_vmode __initdata = VMODE_1024_768_60; static int default_cmode __initdata = CMODE_8; #endif -#ifdef CONFIG_PMAC_PBOOK static int default_crt_on __initdata = 0; static int default_lcd_on __initdata = 1; -#endif #ifdef CONFIG_MTRR static int mtrr = 1; @@ -1249,7 +1247,6 @@ static int aty128_crtc_to_var(const struct aty128_crtc *crtc, return 0; } -#ifdef CONFIG_PMAC_PBOOK static void aty128_set_crt_enable(struct aty128fb_par *par, int on) { if (on) { @@ -1284,7 +1281,6 @@ static void aty128_set_lcd_enable(struct aty128fb_par *par, int on) aty_st_le32(LVDS_GEN_CNTL, reg); } } -#endif /* CONFIG_PMAC_PBOOK */ static void aty128_set_pll(struct aty128_pll *pll, const struct aty128fb_par *par) { @@ -1491,12 +1487,10 @@ static int aty128fb_set_par(struct fb_info *info) info->fix.visual = par->crtc.bpp == 8 ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR; -#ifdef CONFIG_PMAC_PBOOK if (par->chip_gen == rage_M3) { aty128_set_crt_enable(par, par->crt_on); aty128_set_lcd_enable(par, par->lcd_on); } -#endif if (par->accel_flags & FB_ACCELF_TEXT) aty128_init_engine(par); @@ -1652,7 +1646,6 @@ static int __init aty128fb_setup(char *options) return 0; while ((this_opt = strsep(&options, ",")) != NULL) { -#ifdef CONFIG_PMAC_PBOOK if (!strncmp(this_opt, "lcd:", 4)) { default_lcd_on = simple_strtoul(this_opt+4, NULL, 0); continue; @@ -1660,7 +1653,6 @@ static int __init aty128fb_setup(char *options) default_crt_on = simple_strtoul(this_opt+4, NULL, 0); continue; } -#endif #ifdef CONFIG_MTRR if(!strncmp(this_opt, "nomtrr", 6)) { mtrr = 0; @@ -1752,10 +1744,8 @@ static int __init aty128_init(struct pci_dev *pdev, const struct pci_device_id * info->fbops = &aty128fb_ops; info->flags = FBINFO_FLAG_DEFAULT; -#ifdef CONFIG_PMAC_PBOOK par->lcd_on = default_lcd_on; par->crt_on = default_crt_on; -#endif var = default_var; #ifdef CONFIG_PPC_PMAC @@ -2035,12 +2025,10 @@ static int aty128fb_blank(int blank, struct fb_info *fb) aty_st_8(CRTC_EXT_CNTL+1, state); -#ifdef CONFIG_PMAC_PBOOK if (par->chip_gen == rage_M3) { aty128_set_crt_enable(par, par->crt_on && !blank); aty128_set_lcd_enable(par, par->lcd_on && !blank); } -#endif #ifdef CONFIG_PMAC_BACKLIGHT if ((_machine == _MACH_Pmac) && !blank) set_backlight_enable(1); @@ -2124,7 +2112,6 @@ static int aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, static int aty128fb_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg, struct fb_info *info) { -#ifdef CONFIG_PMAC_PBOOK struct aty128fb_par *par = info->par; u32 value; int rc; @@ -2149,7 +2136,6 @@ static int aty128fb_ioctl(struct inode *inode, struct file *file, u_int cmd, value = (par->crt_on << 1) | par->lcd_on; return put_user(value, (__u32 __user *)arg); } -#endif return -EINVAL; } diff --git a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c index 95e7255..e75a965 100644 --- a/drivers/video/chipsfb.c +++ b/drivers/video/chipsfb.c @@ -28,22 +28,17 @@ #include <linux/fb.h> #include <linux/init.h> #include <linux/pci.h> +#include <linux/console.h> #include <asm/io.h> #ifdef CONFIG_PMAC_BACKLIGHT #include <asm/backlight.h> #endif -#ifdef CONFIG_PMAC_PBOOK -#include <linux/adb.h> -#include <linux/pmu.h> -#endif /* * Since we access the display with inb/outb to fixed port numbers, * we can only handle one 6555x chip. -- paulus */ -static struct fb_info chipsfb_info; - #define write_ind(num, val, ap, dp) do { \ outb((num), (ap)); outb((val), (dp)); \ } while (0) @@ -74,14 +69,6 @@ static struct fb_info chipsfb_info; inb(0x3da); read_ind(num, var, 0x3c0, 0x3c1); \ } while (0) -#ifdef CONFIG_PMAC_PBOOK -static unsigned char *save_framebuffer; -int chips_sleep_notify(struct pmu_sleep_notifier *self, int when); -static struct pmu_sleep_notifier chips_sleep_notifier = { - chips_sleep_notify, SLEEP_LEVEL_VIDEO, -}; -#endif - /* * Exported functions */ @@ -356,6 +343,8 @@ static struct fb_var_screeninfo chipsfb_var __initdata = { static void __init init_chips(struct fb_info *p, unsigned long addr) { + memset(p->screen_base, 0, 0x100000); + p->fix = chipsfb_fix; p->fix.smem_start = addr; @@ -366,34 +355,41 @@ static void __init init_chips(struct fb_info *p, unsigned long addr) fb_alloc_cmap(&p->cmap, 256, 0); - if (register_framebuffer(p) < 0) { - printk(KERN_ERR "C&T 65550 framebuffer failed to register\n"); - return; - } - - printk(KERN_INFO "fb%d: Chips 65550 frame buffer (%dK RAM detected)\n", - p->node, p->fix.smem_len / 1024); - chips_hw_init(); } static int __devinit chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) { - struct fb_info *p = &chipsfb_info; + struct fb_info *p; unsigned long addr, size; unsigned short cmd; + int rc = -ENODEV; + + if (pci_enable_device(dp) < 0) { + dev_err(&dp->dev, "Cannot enable PCI device\n"); + goto err_out; + } if ((dp->resource[0].flags & IORESOURCE_MEM) == 0) - return -ENODEV; + goto err_disable; addr = pci_resource_start(dp, 0); size = pci_resource_len(dp, 0); if (addr == 0) - return -ENODEV; - if (p->screen_base != 0) - return -EBUSY; - if (!request_mem_region(addr, size, "chipsfb")) - return -EBUSY; + goto err_disable; + + p = framebuffer_alloc(0, &dp->dev); + if (p == NULL) { + dev_err(&dp->dev, "Cannot allocate framebuffer structure\n"); + rc = -ENOMEM; + goto err_disable; + } + + if (pci_request_region(dp, 0, "chipsfb") != 0) { + dev_err(&dp->dev, "Cannot request framebuffer\n"); + rc = -EBUSY; + goto err_release_fb; + } #ifdef __BIG_ENDIAN addr += 0x800000; // Use big-endian aperture @@ -411,37 +407,89 @@ chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) set_backlight_enable(1); #endif /* CONFIG_PMAC_BACKLIGHT */ +#ifdef CONFIG_PPC p->screen_base = __ioremap(addr, 0x200000, _PAGE_NO_CACHE); +#else + p->screen_base = ioremap(addr, 0x200000); +#endif if (p->screen_base == NULL) { - release_mem_region(addr, size); - return -ENOMEM; + dev_err(&dp->dev, "Cannot map framebuffer\n"); + rc = -ENOMEM; + goto err_release_pci; } + + pci_set_drvdata(dp, p); p->device = &dp->dev; + init_chips(p, addr); -#ifdef CONFIG_PMAC_PBOOK - pmu_register_sleep_notifier(&chips_sleep_notifier); -#endif /* CONFIG_PMAC_PBOOK */ + if (register_framebuffer(p) < 0) { + dev_err(&dp->dev,"C&T 65550 framebuffer failed to register\n"); + goto err_unmap; + } + + dev_info(&dp->dev,"fb%d: Chips 65550 frame buffer" + " (%dK RAM detected)\n", + p->node, p->fix.smem_len / 1024); - pci_set_drvdata(dp, p); return 0; + + err_unmap: + iounmap(p->screen_base); + err_release_pci: + pci_release_region(dp, 0); + err_release_fb: + framebuffer_release(p); + err_disable: + err_out: + return rc; } static void __devexit chipsfb_remove(struct pci_dev *dp) { struct fb_info *p = pci_get_drvdata(dp); - if (p != &chipsfb_info || p->screen_base == NULL) + if (p->screen_base == NULL) return; unregister_framebuffer(p); iounmap(p->screen_base); p->screen_base = NULL; - release_mem_region(pci_resource_start(dp, 0), pci_resource_len(dp, 0)); + pci_release_region(dp, 0); +} + +#ifdef CONFIG_PM +static int chipsfb_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct fb_info *p = pci_get_drvdata(pdev); + + if (state == pdev->dev.power.power_state) + return 0; + if (state != PM_SUSPEND_MEM) + goto done; + + acquire_console_sem(); + chipsfb_blank(1, p); + fb_set_suspend(p, 1); + release_console_sem(); + done: + pdev->dev.power.power_state = state; + return 0; +} + +static int chipsfb_pci_resume(struct pci_dev *pdev) +{ + struct fb_info *p = pci_get_drvdata(pdev); -#ifdef CONFIG_PMAC_PBOOK - pmu_unregister_sleep_notifier(&chips_sleep_notifier); -#endif /* CONFIG_PMAC_PBOOK */ + acquire_console_sem(); + fb_set_suspend(p, 0); + chipsfb_blank(0, p); + release_console_sem(); + + pdev->dev.power.power_state = PMSG_ON; + return 0; } +#endif /* CONFIG_PM */ + static struct pci_device_id chipsfb_pci_tbl[] = { { PCI_VENDOR_ID_CT, PCI_DEVICE_ID_CT_65550, PCI_ANY_ID, PCI_ANY_ID }, @@ -455,6 +503,10 @@ static struct pci_driver chipsfb_driver = { .id_table = chipsfb_pci_tbl, .probe = chipsfb_pci_init, .remove = __devexit_p(chipsfb_remove), +#ifdef CONFIG_PM + .suspend = chipsfb_pci_suspend, + .resume = chipsfb_pci_resume, +#endif }; int __init chips_init(void) @@ -472,48 +524,4 @@ static void __exit chipsfb_exit(void) pci_unregister_driver(&chipsfb_driver); } -#ifdef CONFIG_PMAC_PBOOK -/* - * Save the contents of the frame buffer when we go to sleep, - * and restore it when we wake up again. - */ -int -chips_sleep_notify(struct pmu_sleep_notifier *self, int when) -{ - struct fb_info *p = &chipsfb_info; - int nb = p->var.yres * p->fix.line_length; - - if (p->screen_base == NULL) - return PBOOK_SLEEP_OK; - - switch (when) { - case PBOOK_SLEEP_REQUEST: - save_framebuffer = vmalloc(nb); - if (save_framebuffer == NULL) - return PBOOK_SLEEP_REFUSE; - break; - case PBOOK_SLEEP_REJECT: - if (save_framebuffer) { - vfree(save_framebuffer); - save_framebuffer = NULL; - } - break; - case PBOOK_SLEEP_NOW: - chipsfb_blank(1, p); - if (save_framebuffer) - memcpy(save_framebuffer, p->screen_base, nb); - break; - case PBOOK_WAKE: - if (save_framebuffer) { - memcpy(p->screen_base, save_framebuffer, nb); - vfree(save_framebuffer); - save_framebuffer = NULL; - } - chipsfb_blank(0, p); - break; - } - return PBOOK_SLEEP_OK; -} -#endif /* CONFIG_PMAC_PBOOK */ - MODULE_LICENSE("GPL"); |