diff options
Diffstat (limited to 'drivers')
271 files changed, 12617 insertions, 5568 deletions
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index a1d50242b8cd..6a43c807497d 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -356,6 +356,39 @@ config LBD your machine, or if you want to have a raid or loopback device bigger than 2TB. Otherwise say N. +config CDROM_PKTCDVD + tristate "Packet writing on CD/DVD media" + help + If you have a CDROM drive that supports packet writing, say Y to + include preliminary support. It should work with any MMC/Mt Fuji + compliant ATAPI or SCSI drive, which is just about any newer CD + writer. + + Currently only writing to CD-RW, DVD-RW and DVD+RW discs is possible. + DVD-RW disks must be in restricted overwrite mode. + + To compile this driver as a module, choose M here: the + module will be called pktcdvd. + +config CDROM_PKTCDVD_BUFFERS + int "Free buffers for data gathering" + depends on CDROM_PKTCDVD + default "8" + help + This controls the maximum number of active concurrent packets. More + concurrent packets can increase write performance, but also require + more memory. Each concurrent packet will require approximately 64Kb + of non-swappable kernel memory, memory which will be allocated at + pktsetup time. + +config CDROM_PKTCDVD_WCACHE + bool "Enable write caching" + depends on CDROM_PKTCDVD + help + If enabled, write caching will be set for the CD-R/W device. For now + this option is dangerous unless the CD-RW media is known good, as we + don't do deferred write error handling yet. + source "drivers/s390/block/Kconfig" endmenu diff --git a/drivers/block/Kconfig.iosched b/drivers/block/Kconfig.iosched index d938c5fd130b..e0ba6c93717e 100644 --- a/drivers/block/Kconfig.iosched +++ b/drivers/block/Kconfig.iosched @@ -1,5 +1,5 @@ config IOSCHED_NOOP - bool "No-op I/O scheduler" if EMBEDDED + bool default y ---help--- The no-op I/O scheduler is a minimal scheduler that does basic merging @@ -9,7 +9,7 @@ config IOSCHED_NOOP the kernel. config IOSCHED_AS - bool "Anticipatory I/O scheduler" if EMBEDDED + tristate "Anticipatory I/O scheduler" default y ---help--- The anticipatory I/O scheduler is the default disk scheduler. It is @@ -18,7 +18,7 @@ config IOSCHED_AS slower in some cases especially some database loads. config IOSCHED_DEADLINE - bool "Deadline I/O scheduler" if EMBEDDED + tristate "Deadline I/O scheduler" default y ---help--- The deadline I/O scheduler is simple and compact, and is often as @@ -28,7 +28,7 @@ config IOSCHED_DEADLINE anticipatory I/O scheduler and so is a good choice. config IOSCHED_CFQ - bool "CFQ I/O scheduler" if EMBEDDED + tristate "CFQ I/O scheduler" default y ---help--- The CFQ I/O scheduler tries to distribute bandwidth equally diff --git a/drivers/block/Makefile b/drivers/block/Makefile index c8fbbf14ce94..1cf09a1c065b 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_BLK_DEV_XD) += xd.o obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o +obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o obj-$(CONFIG_BLK_DEV_UMEM) += umem.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o diff --git a/drivers/block/as-iosched.c b/drivers/block/as-iosched.c index 0ef6a665d93e..0aa3ee8c309b 100644 --- a/drivers/block/as-iosched.c +++ b/drivers/block/as-iosched.c @@ -614,7 +614,7 @@ static void as_antic_stop(struct as_data *ad) static void as_antic_timeout(unsigned long data) { struct request_queue *q = (struct request_queue *)data; - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; unsigned long flags; spin_lock_irqsave(q->queue_lock, flags); @@ -945,7 +945,7 @@ static void update_write_batch(struct as_data *ad) */ static void as_completed_request(request_queue_t *q, struct request *rq) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = RQ_DATA(rq); WARN_ON(!list_empty(&rq->queuelist)); @@ -1030,7 +1030,7 @@ static void as_remove_queued_request(request_queue_t *q, struct request *rq) { struct as_rq *arq = RQ_DATA(rq); const int data_dir = arq->is_sync; - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; WARN_ON(arq->state != AS_RQ_QUEUED); @@ -1361,7 +1361,7 @@ fifo_expired: static struct request *as_next_request(request_queue_t *q) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct request *rq = NULL; /* @@ -1469,7 +1469,7 @@ static void as_add_request(struct as_data *ad, struct as_rq *arq) */ static void as_requeue_request(request_queue_t *q, struct request *rq) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = RQ_DATA(rq); if (arq) { @@ -1509,7 +1509,7 @@ static void as_account_queued_request(struct as_data *ad, struct request *rq) static void as_insert_request(request_queue_t *q, struct request *rq, int where) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = RQ_DATA(rq); if (arq) { @@ -1562,7 +1562,7 @@ as_insert_request(request_queue_t *q, struct request *rq, int where) */ static int as_queue_empty(request_queue_t *q) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; if (!list_empty(&ad->fifo_list[REQ_ASYNC]) || !list_empty(&ad->fifo_list[REQ_SYNC]) @@ -1601,7 +1601,7 @@ as_latter_request(request_queue_t *q, struct request *rq) static int as_merge(request_queue_t *q, struct request **req, struct bio *bio) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; sector_t rb_key = bio->bi_sector + bio_sectors(bio); struct request *__rq; int ret; @@ -1656,7 +1656,7 @@ out_insert: static void as_merged_request(request_queue_t *q, struct request *req) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = RQ_DATA(req); /* @@ -1701,7 +1701,7 @@ static void as_merged_requests(request_queue_t *q, struct request *req, struct request *next) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = RQ_DATA(req); struct as_rq *anext = RQ_DATA(next); @@ -1788,7 +1788,7 @@ static void as_work_handler(void *data) static void as_put_request(request_queue_t *q, struct request *rq) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = RQ_DATA(rq); if (!arq) { @@ -1807,7 +1807,7 @@ static void as_put_request(request_queue_t *q, struct request *rq) static int as_set_request(request_queue_t *q, struct request *rq, int gfp_mask) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = mempool_alloc(ad->arq_pool, gfp_mask); if (arq) { @@ -1828,21 +1828,21 @@ static int as_set_request(request_queue_t *q, struct request *rq, int gfp_mask) static int as_may_queue(request_queue_t *q, int rw) { - int ret = 0; - struct as_data *ad = q->elevator.elevator_data; + int ret = ELV_MQUEUE_MAY; + struct as_data *ad = q->elevator->elevator_data; struct io_context *ioc; if (ad->antic_status == ANTIC_WAIT_REQ || ad->antic_status == ANTIC_WAIT_NEXT) { ioc = as_get_io_context(); if (ad->io_context == ioc) - ret = 1; + ret = ELV_MQUEUE_MUST; put_io_context(ioc); } return ret; } -static void as_exit(request_queue_t *q, elevator_t *e) +static void as_exit_queue(elevator_t *e) { struct as_data *ad = e->elevator_data; @@ -1862,7 +1862,7 @@ static void as_exit(request_queue_t *q, elevator_t *e) * initialize elevator private data (as_data), and alloc a arq for * each request on the free lists */ -static int as_init(request_queue_t *q, elevator_t *e) +static int as_init_queue(request_queue_t *q, elevator_t *e) { struct as_data *ad; int i; @@ -1962,10 +1962,10 @@ static ssize_t as_est_show(struct as_data *ad, char *page) return pos; } -#define SHOW_FUNCTION(__FUNC, __VAR) \ +#define SHOW_FUNCTION(__FUNC, __VAR) \ static ssize_t __FUNC(struct as_data *ad, char *page) \ -{ \ - return as_var_show(__VAR, (page)); \ +{ \ + return as_var_show(jiffies_to_msecs((__VAR)), (page)); \ } SHOW_FUNCTION(as_readexpire_show, ad->fifo_expire[REQ_SYNC]); SHOW_FUNCTION(as_writeexpire_show, ad->fifo_expire[REQ_ASYNC]); @@ -1982,6 +1982,7 @@ static ssize_t __FUNC(struct as_data *ad, const char *page, size_t count) \ *(__PTR) = (MIN); \ else if (*(__PTR) > (MAX)) \ *(__PTR) = (MAX); \ + *(__PTR) = msecs_to_jiffies(*(__PTR)); \ return ret; \ } STORE_FUNCTION(as_readexpire_store, &ad->fifo_expire[REQ_SYNC], 0, INT_MAX); @@ -2070,39 +2071,64 @@ static struct kobj_type as_ktype = { .default_attrs = default_attrs, }; -static int __init as_slab_setup(void) +static struct elevator_type iosched_as = { + .ops = { + .elevator_merge_fn = as_merge, + .elevator_merged_fn = as_merged_request, + .elevator_merge_req_fn = as_merged_requests, + .elevator_next_req_fn = as_next_request, + .elevator_add_req_fn = as_insert_request, + .elevator_remove_req_fn = as_remove_request, + .elevator_requeue_req_fn = as_requeue_request, + .elevator_queue_empty_fn = as_queue_empty, + .elevator_completed_req_fn = as_completed_request, + .elevator_former_req_fn = as_former_request, + .elevator_latter_req_fn = as_latter_request, + .elevator_set_req_fn = as_set_request, + .elevator_put_req_fn = as_put_request, + .elevator_may_queue_fn = as_may_queue, + .elevator_init_fn = as_init_queue, + .elevator_exit_fn = as_exit_queue, + }, + + .elevator_ktype = &as_ktype, + .elevator_name = "anticipatory", + .elevator_owner = THIS_MODULE, +}; + +int as_init(void) { + int ret; + arq_pool = kmem_cache_create("as_arq", sizeof(struct as_rq), 0, 0, NULL, NULL); - if (!arq_pool) - panic("as: can't init slab pool\n"); + return -ENOMEM; - return 0; + ret = elv_register(&iosched_as); + if (!ret) { + /* + * don't allow AS to get unregistered, since we would have + * to browse all tasks in the system and release their + * as_io_context first + */ + __module_get(THIS_MODULE); + return 0; + } + + kmem_cache_destroy(arq_pool); + return ret; } -subsys_initcall(as_slab_setup); - -elevator_t iosched_as = { - .elevator_merge_fn = as_merge, - .elevator_merged_fn = as_merged_request, - .elevator_merge_req_fn = as_merged_requests, - .elevator_next_req_fn = as_next_request, - .elevator_add_req_fn = as_insert_request, - .elevator_remove_req_fn = as_remove_request, - .elevator_requeue_req_fn = as_requeue_request, - .elevator_queue_empty_fn = as_queue_empty, - .elevator_completed_req_fn = as_completed_request, - .elevator_former_req_fn = as_former_request, - .elevator_latter_req_fn = as_latter_request, - .elevator_set_req_fn = as_set_request, - .elevator_put_req_fn = as_put_request, - .elevator_may_queue_fn = as_may_queue, - .elevator_init_fn = as_init, - .elevator_exit_fn = as_exit, - - .elevator_ktype = &as_ktype, - .elevator_name = "anticipatory", -}; +void as_exit(void) +{ + kmem_cache_destroy(arq_pool); + elv_unregister(&iosched_as); +} + +module_init(as_init); +module_exit(as_exit); -EXPORT_SYMBOL(iosched_as); +MODULE_AUTHOR("Nick Piggin"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("anticipatory IO scheduler"); diff --git a/drivers/block/cfq-iosched.c b/drivers/block/cfq-iosched.c index 068f4eae0b5c..cf7fc7609e67 100644 --- a/drivers/block/cfq-iosched.c +++ b/drivers/block/cfq-iosched.c @@ -22,96 +22,216 @@ #include <linux/rbtree.h> #include <linux/mempool.h> +static unsigned long max_elapsed_crq; +static unsigned long max_elapsed_dispatch; + /* * tunables */ -static int cfq_quantum = 4; -static int cfq_queued = 8; +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_back_max = 16 * 1024; /* maximum backwards seek, in KiB */ +static int cfq_back_penalty = 2; /* penalty of a backwards seek */ +/* + * for the hash of cfqq inside the cfqd + */ #define CFQ_QHASH_SHIFT 6 #define CFQ_QHASH_ENTRIES (1 << CFQ_QHASH_SHIFT) -#define list_entry_qhash(entry) list_entry((entry), struct cfq_queue, cfq_hash) +#define list_entry_qhash(entry) hlist_entry((entry), struct cfq_queue, cfq_hash) -#define CFQ_MHASH_SHIFT 8 +/* + * for the hash of crq inside the cfqq + */ +#define CFQ_MHASH_SHIFT 6 #define CFQ_MHASH_BLOCK(sec) ((sec) >> 3) #define CFQ_MHASH_ENTRIES (1 << CFQ_MHASH_SHIFT) -#define CFQ_MHASH_FN(sec) (hash_long(CFQ_MHASH_BLOCK((sec)),CFQ_MHASH_SHIFT)) -#define ON_MHASH(crq) !list_empty(&(crq)->hash) +#define CFQ_MHASH_FN(sec) hash_long(CFQ_MHASH_BLOCK(sec), CFQ_MHASH_SHIFT) #define rq_hash_key(rq) ((rq)->sector + (rq)->nr_sectors) -#define list_entry_hash(ptr) list_entry((ptr), struct cfq_rq, hash) +#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 RQ_DATA(rq) ((struct cfq_rq *) (rq)->elevator_private) +#define RQ_DATA(rq) (rq)->elevator_private + +/* + * rb-tree defines + */ +#define RB_NONE (2) +#define RB_EMPTY(node) ((node)->rb_node == NULL) +#define RB_CLEAR_COLOR(node) (node)->rb_color = RB_NONE +#define RB_CLEAR(node) do { \ + (node)->rb_parent = NULL; \ + RB_CLEAR_COLOR((node)); \ + (node)->rb_right = NULL; \ + (node)->rb_left = NULL; \ +} while (0) +#define RB_CLEAR_ROOT(root) ((root)->rb_node = NULL) +#define ON_RB(node) ((node)->rb_color != RB_NONE) +#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 }; + +/* + * spare queue + */ +#define CFQ_KEY_SPARE (~0UL) static kmem_cache_t *crq_pool; static kmem_cache_t *cfq_pool; -static mempool_t *cfq_mpool; +static kmem_cache_t *cfq_ioc_pool; struct cfq_data { struct list_head rr_list; - struct list_head *dispatch; - struct list_head *cfq_hash; + struct list_head empty_list; - struct list_head *crq_hash; + struct hlist_head *cfq_hash; + struct hlist_head *crq_hash; + /* queues on rr_list (ie they have pending requests */ unsigned int busy_queues; + unsigned int max_queued; + atomic_t ref; + + int key_type; + mempool_t *crq_pool; request_queue_t *queue; + sector_t last_sector; + + int rq_in_driver; + /* - * tunables + * 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_back_penalty; + unsigned int cfq_back_max; + unsigned int find_best_crq; + + unsigned int cfq_tagged; }; struct cfq_queue { - struct list_head cfq_hash; + /* reference count */ + atomic_t ref; + /* parent cfq_data */ + struct cfq_data *cfqd; + /* hash of mergeable requests */ + struct hlist_node cfq_hash; + /* hash key */ + unsigned long key; + /* whether queue is on rr (or empty) list */ + int on_rr; + /* on either rr or empty list of cfqd */ struct list_head cfq_list; + /* sorted list of pending requests */ struct rb_root sort_list; - int pid; + /* if fifo isn't expired, next request to serve */ + struct cfq_rq *next_crq; + /* requests queued in sort_list */ int queued[2]; -#if 0 - /* - * with a simple addition like this, we can do io priorities. almost. - * does need a split request free list, too. - */ - int io_prio -#endif + /* 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; + + int key_type; + + unsigned long service_start; + unsigned long service_used; + + unsigned int max_rate; + + /* number of requests that have been handed to the driver */ + int in_flight; + /* number of currently allocated requests */ + int alloc_limit[2]; }; struct cfq_rq { struct rb_node rb_node; sector_t rb_key; - struct request *request; + struct hlist_node hash; struct cfq_queue *cfq_queue; + struct cfq_io_context *io_context; + + unsigned long service_start; + unsigned long queue_start; - struct list_head hash; + unsigned int in_flight : 1; + unsigned int accounted : 1; + unsigned int is_sync : 1; + unsigned int is_write : 1; }; -static void cfq_put_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq); -static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *cfqd, int pid); -static void cfq_dispatch_sort(struct cfq_data *cfqd, struct cfq_queue *cfqq, - struct cfq_rq *crq); +static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *, unsigned long); +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); /* - * lots of deadline iosched dupes, can be abstracted later... + * what the fairness is based on (ie how processes are grouped and + * differentiated) */ -static inline void __cfq_del_crq_hash(struct cfq_rq *crq) +static inline unsigned long +cfq_hash_key(struct cfq_data *cfqd, struct task_struct *tsk) { - list_del_init(&crq->hash); + /* + * 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; + } } +/* + * lots of deadline iosched dupes, can be abstracted later... + */ static inline void cfq_del_crq_hash(struct cfq_rq *crq) { - if (ON_MHASH(crq)) - __cfq_del_crq_hash(crq); + hlist_del_init(&crq->hash); } static void cfq_remove_merge_hints(request_queue_t *q, struct cfq_rq *crq) @@ -120,32 +240,32 @@ 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) { - struct request *rq = crq->request; + const int hash_idx = CFQ_MHASH_FN(rq_hash_key(crq->request)); - BUG_ON(ON_MHASH(crq)); + BUG_ON(!hlist_unhashed(&crq->hash)); - list_add(&crq->hash, &cfqd->crq_hash[CFQ_MHASH_FN(rq_hash_key(rq))]); + hlist_add_head(&crq->hash, &cfqd->crq_hash[hash_idx]); } static struct request *cfq_find_rq_hash(struct cfq_data *cfqd, sector_t offset) { - struct list_head *hash_list = &cfqd->crq_hash[CFQ_MHASH_FN(offset)]; - struct list_head *entry, *next = hash_list->next; + struct hlist_head *hash_list = &cfqd->crq_hash[CFQ_MHASH_FN(offset)]; + struct hlist_node *entry, *next; - while ((entry = next) != hash_list) { + hlist_for_each_safe(entry, next, hash_list) { struct cfq_rq *crq = list_entry_hash(entry); struct request *__rq = crq->request; - next = entry->next; - - BUG_ON(!ON_MHASH(crq)); + BUG_ON(hlist_unhashed(&crq->hash)); if (!rq_mergeable(__rq)) { - __cfq_del_crq_hash(crq); + cfq_del_crq_hash(crq); continue; } @@ -157,29 +277,257 @@ static struct request *cfq_find_rq_hash(struct cfq_data *cfqd, sector_t offset) } /* - * rb tree support functions + * Lifted from AS - choose which of crq1 and crq2 that is best served now. + * We choose the request that is closest to the head right now. Distance + * behind the head are penalized and only allowed to a certain extent. */ -#define RB_NONE (2) -#define RB_EMPTY(node) ((node)->rb_node == NULL) -#define RB_CLEAR(node) ((node)->rb_color = RB_NONE) -#define RB_CLEAR_ROOT(root) ((root)->rb_node = NULL) -#define ON_RB(node) ((node)->rb_color != RB_NONE) -#define rb_entry_crq(node) rb_entry((node), struct cfq_rq, rb_node) -#define rq_rb_key(rq) (rq)->sector +static struct cfq_rq * +cfq_choose_req(struct cfq_data *cfqd, struct cfq_rq *crq1, struct cfq_rq *crq2) +{ + sector_t last, s1, s2, d1 = 0, d2 = 0; + int r1_wrap = 0, r2_wrap = 0; /* requests are behind the disk head */ + unsigned long back_max; + + if (crq1 == NULL || crq1 == crq2) + return crq2; + if (crq2 == NULL) + return crq1; -static inline void cfq_del_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq) + 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 + */ + back_max = cfqd->cfq_back_max * 2; + + /* + * Strict one way elevator _except_ in the case where we allow + * short backward seeks which are biased as twice the cost of a + * similar forward seek. + */ + if (s1 >= last) + d1 = s1 - last; + else if (s1 + back_max >= last) + d1 = (last - s1) * cfqd->cfq_back_penalty; + else + r1_wrap = 1; + + if (s2 >= last) + d2 = s2 - last; + else if (s2 + back_max >= last) + d2 = (last - s2) * cfqd->cfq_back_penalty; + else + r2_wrap = 1; + + /* Found required data */ + if (!r1_wrap && r2_wrap) + return crq1; + else if (!r2_wrap && r1_wrap) + return crq2; + else if (r1_wrap && r2_wrap) { + /* both behind the head */ + if (s1 <= s2) + return crq1; + else + return crq2; + } + + /* Both requests in front of the head */ + if (d1 < d2) + return crq1; + else if (d2 < d1) + return crq2; + else { + if (s1 >= s2) + return crq1; + else + return crq2; + } +} + +/* + * would be nice to take fifo expire time into account as well + */ +static struct cfq_rq * +cfq_find_next_crq(struct cfq_data *cfqd, struct cfq_queue *cfqq, + struct cfq_rq *last) +{ + 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 = rb_first(&cfqq->sort_list); + + rbprev = rb_prev(&last->rb_node); + + if (rbprev) + crq_prev = rb_entry_crq(rbprev); + if (rbnext) + crq_next = rb_entry_crq(rbnext); + + return cfq_choose_req(cfqd, crq_next, crq_prev); +} + +static void cfq_update_next_crq(struct cfq_rq *crq) { + struct cfq_queue *cfqq = crq->cfq_queue; + + if (cfqq->next_crq == crq) + cfqq->next_crq = cfq_find_next_crq(cfqq->cfqd, cfqq, crq); +} + +static int cfq_check_sort_rr_list(struct cfq_queue *cfqq) +{ + 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); + + if (cfqq->service_used > cnext->service_used) + return 1; + } + + prev = cfqq->cfq_list.prev; + if (prev != head) { + struct cfq_queue *cprev = list_entry_cfqq(prev); + + if (cfqq->service_used < cprev->service_used) + return 1; + } + + 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)) + return; + + list_del(&cfqq->cfq_list); + + /* + * sort by our mean service_used, sub-sort by in-flight requests + */ + while ((entry = entry->prev) != &cfqq->cfqd->rr_list) { + struct cfq_queue *__cfqq = list_entry_cfqq(entry); + + if (cfqq->service_used > __cfqq->service_used) + 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); +} + +/* + * add to busy list of queues for service, trying to be fair in ordering + * the pending list according to requests serviced + */ +static inline void +cfq_add_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + /* + * it's currently on the empty list + */ + cfqq->on_rr = 1; + cfqd->busy_queues++; + + if (time_after(jiffies, cfqq->service_start + cfq_service)) + cfqq->service_used >>= 3; + + cfq_sort_rr_list(cfqq, 1); +} + +static inline void +cfq_del_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + list_move(&cfqq->cfq_list, &cfqd->empty_list); + cfqq->on_rr = 0; + + BUG_ON(!cfqd->busy_queues); + cfqd->busy_queues--; +} + +/* + * rb tree support functions + */ +static inline void cfq_del_crq_rb(struct cfq_rq *crq) +{ + struct cfq_queue *cfqq = crq->cfq_queue; + if (ON_RB(&crq->rb_node)) { - cfqq->queued[rq_data_dir(crq->request)]--; + struct cfq_data *cfqd = cfqq->cfqd; + + BUG_ON(!cfqq->queued[crq->is_sync]); + + cfq_update_next_crq(crq); + + cfqq->queued[crq->is_sync]--; rb_erase(&crq->rb_node, &cfqq->sort_list); - crq->cfq_queue = NULL; + RB_CLEAR_COLOR(&crq->rb_node); + + if (RB_EMPTY(&cfqq->sort_list) && cfqq->on_rr) + cfq_del_cfqq_rr(cfqd, cfqq); } } static struct cfq_rq * -__cfq_add_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq) +__cfq_add_crq_rb(struct cfq_rq *crq) { - struct rb_node **p = &cfqq->sort_list.rb_node; + struct rb_node **p = &crq->cfq_queue->sort_list.rb_node; struct rb_node *parent = NULL; struct cfq_rq *__crq; @@ -199,30 +547,50 @@ __cfq_add_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq) return NULL; } -static void -cfq_add_crq_rb(struct cfq_data *cfqd, struct cfq_queue *cfqq,struct cfq_rq *crq) +static void cfq_add_crq_rb(struct cfq_rq *crq) { + struct cfq_queue *cfqq = crq->cfq_queue; + struct cfq_data *cfqd = cfqq->cfqd; struct request *rq = crq->request; struct cfq_rq *__alias; crq->rb_key = rq_rb_key(rq); - cfqq->queued[rq_data_dir(rq)]++; -retry: - __alias = __cfq_add_crq_rb(cfqq, crq); - if (!__alias) { - rb_insert_color(&crq->rb_node, &cfqq->sort_list); - crq->cfq_queue = cfqq; - return; + cfqq->queued[crq->is_sync]++; + + /* + * looks a little odd, but the first insert might return an alias. + * if that happens, put the alias on the dispatch list + */ + while ((__alias = __cfq_add_crq_rb(crq)) != NULL) + cfq_dispatch_sort(cfqd->queue, __alias); + + rb_insert_color(&crq->rb_node, &cfqq->sort_list); + + if (!cfqq->on_rr) + cfq_add_cfqq_rr(cfqd, cfqq); + + /* + * check if this request is a better next-serve candidate + */ + cfqq->next_crq = cfq_choose_req(cfqd, cfqq->next_crq, crq); +} + +static inline void +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]--; } - cfq_dispatch_sort(cfqd, cfqq, __alias); - goto retry; + cfq_add_crq_rb(crq); } static struct request * cfq_find_rq_rb(struct cfq_data *cfqd, sector_t sector) { - struct cfq_queue *cfqq = cfq_find_cfq_hash(cfqd, current->tgid); + const unsigned long key = cfq_hash_key(cfqd, current); + struct cfq_queue *cfqq = cfq_find_cfq_hash(cfqd, key); struct rb_node *n; if (!cfqq) @@ -244,30 +612,44 @@ out: return NULL; } -static void cfq_remove_request(request_queue_t *q, struct request *rq) +/* + * make sure the service time gets corrected on reissue of this request + */ +static void cfq_requeue_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); + } + + crq->accounted = 0; + cfqq->cfqd->rq_in_driver--; + } + list_add(&rq->queuelist, &q->queue_head); +} + +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); - if (cfqq) { - cfq_del_crq_rb(cfqq, crq); - - if (RB_EMPTY(&cfqq->sort_list)) - cfq_put_queue(cfqd, cfqq); - } + if (crq->cfq_queue) + cfq_del_crq_rb(crq); } } static int cfq_merge(request_queue_t *q, struct request **req, struct bio *bio) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; struct request *__rq; int ret; @@ -305,7 +687,7 @@ out_insert: static void cfq_merged_request(request_queue_t *q, struct request *req) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_rq *crq = RQ_DATA(req); cfq_del_crq_hash(crq); @@ -314,193 +696,546 @@ static void cfq_merged_request(request_queue_t *q, struct request *req) if (ON_RB(&crq->rb_node) && (rq_rb_key(req) != crq->rb_key)) { struct cfq_queue *cfqq = crq->cfq_queue; - cfq_del_crq_rb(cfqq, crq); - cfq_add_crq_rb(cfqd, cfqq, crq); + cfq_update_next_crq(crq); + cfq_reposition_crq_rb(cfqq, crq); } q->last_merge = req; } static void -cfq_merged_requests(request_queue_t *q, struct request *req, +cfq_merged_requests(request_queue_t *q, struct request *rq, struct request *next) { - cfq_merged_request(q, req); + 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; + } + } + + cfq_update_next_crq(cnext); cfq_remove_request(q, next); } -static void -cfq_dispatch_sort(struct cfq_data *cfqd, struct cfq_queue *cfqq, - struct cfq_rq *crq) +/* + * we dispatch cfqd->cfq_quantum requests in total from the rr_list queues, + * this function sector sorts the selected request to minimize seeks. we start + * at cfqd->last_sector, not 0. + */ +static void cfq_dispatch_sort(request_queue_t *q, struct cfq_rq *crq) { - struct list_head *head = cfqd->dispatch, *entry = head; + struct cfq_data *cfqd = q->elevator->elevator_data; + struct cfq_queue *cfqq = crq->cfq_queue; + struct list_head *head = &q->queue_head, *entry = head; struct request *__rq; + sector_t last; - cfq_del_crq_rb(cfqq, crq); - cfq_remove_merge_hints(cfqd->queue, crq); + cfq_del_crq_rb(crq); + cfq_remove_merge_hints(q, crq); + list_del(&crq->request->queuelist); - if (!list_empty(head)) { - __rq = list_entry_rq(head->next); + last = cfqd->last_sector; + while ((entry = entry->prev) != head) { + __rq = list_entry_rq(entry); - if (crq->request->sector < __rq->sector) { - entry = head->prev; - goto link; + if (blk_barrier_rq(crq->request)) + break; + if (!blk_fs_request(crq->request)) + break; + + if (crq->request->sector > __rq->sector) + break; + if (__rq->sector > last && crq->request->sector < last) { + last = crq->request->sector; + break; } } - while ((entry = entry->prev) != head) { - __rq = list_entry_rq(entry); + cfqd->last_sector = last; + crq->in_flight = 1; + cfqq->in_flight++; + list_add(&crq->request->queuelist, entry); +} - if (crq->request->sector <= __rq->sector) - break; +/* + * return expired entry, or NULL to just start from scratch in rbtree + */ +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 cfq_rq *crq; + + if (time_before(now, cfqq->last_fifo_expire + cfqd->cfq_fifo_batch_expire)) + 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; + } + + 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; } -link: - list_add_tail(&crq->request->queuelist, entry); + return NULL; } +/* + * dispatch a single request from given queue + */ static inline void -__cfq_dispatch_requests(request_queue_t *q, struct cfq_data *cfqd, - struct cfq_queue *cfqq) +cfq_dispatch_request(request_queue_t *q, struct cfq_data *cfqd, + struct cfq_queue *cfqq) { - struct cfq_rq *crq = rb_entry_crq(rb_first(&cfqq->sort_list)); + struct cfq_rq *crq; + + /* + * follow expired path, else get first next available + */ + 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; - cfq_dispatch_sort(cfqd, cfqq, crq); + /* + * finally, insert request into driver list + */ + cfq_dispatch_sort(q, crq); } -static int cfq_dispatch_requests(request_queue_t *q, struct cfq_data *cfqd) +static int cfq_dispatch_requests(request_queue_t *q, int max_dispatch) { + struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_queue *cfqq; struct list_head *entry, *tmp; - int ret, queued, good_queues; + int queued, busy_queues, first_round; if (list_empty(&cfqd->rr_list)) return 0; - queued = ret = 0; + queued = 0; + first_round = 1; restart: - good_queues = 0; + busy_queues = 0; list_for_each_safe(entry, tmp, &cfqd->rr_list) { - cfqq = list_entry_cfqq(cfqd->rr_list.next); + cfqq = list_entry_cfqq(entry); BUG_ON(RB_EMPTY(&cfqq->sort_list)); - __cfq_dispatch_requests(q, cfqd, cfqq); + /* + * first round of queueing, only select from queues that + * don't already have io in-flight + */ + if (first_round && cfqq->in_flight) + continue; - if (RB_EMPTY(&cfqq->sort_list)) - cfq_put_queue(cfqd, cfqq); - else - good_queues++; + cfq_dispatch_request(q, cfqd, cfqq); + + if (!RB_EMPTY(&cfqq->sort_list)) + busy_queues++; queued++; - ret = 1; } - if ((queued < cfqd->cfq_quantum) && good_queues) + if ((queued < max_dispatch) && (busy_queues || first_round)) { + first_round = 0; goto restart; + } - return ret; + return queued; +} + +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; + + /* + * accounted bit is necessary since some drivers will call + * elv_next_request() many times for the same request (eg ide) + */ + if (crq->accounted) + 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); + } +} + +static inline void +cfq_account_completion(struct cfq_queue *cfqq, struct cfq_rq *crq) +{ + struct cfq_data *cfqd = cfqq->cfqd; + + 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 (time_after(now, cfqq->service_start + cfq_service)) { + cfqq->service_start = now; + cfqq->service_used >>= 3; + } + + cfqq->service_used += duration; + cfq_sort_rr_list(cfqq, 0); + + if (duration > max_elapsed_crq) + max_elapsed_crq = duration; + } } static struct request *cfq_next_request(request_queue_t *q) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; struct request *rq; - if (!list_empty(cfqd->dispatch)) { + if (!list_empty(&q->queue_head)) { struct cfq_rq *crq; dispatch: - rq = list_entry_rq(cfqd->dispatch->next); + rq = list_entry_rq(q->queue_head.next); - crq = RQ_DATA(rq); - if (crq) + if ((crq = RQ_DATA(rq)) != NULL) { cfq_remove_merge_hints(q, crq); + cfq_account_dispatch(crq); + } return rq; } - if (cfq_dispatch_requests(q, cfqd)) + if (cfq_dispatch_requests(q, cfqd->cfq_quantum)) goto dispatch; return NULL; } +/* + * task holds one reference to the queue, dropped when task exits. each crq + * in-flight on this queue also holds a reference, dropped when crq is freed. + * + * queue lock must be held here. + */ +static void cfq_put_queue(struct cfq_queue *cfqq) +{ + BUG_ON(!atomic_read(&cfqq->ref)); + + if (!atomic_dec_and_test(&cfqq->ref)) + return; + + BUG_ON(rb_first(&cfqq->sort_list)); + BUG_ON(cfqq->on_rr); + + cfq_put_cfqd(cfqq->cfqd); + + /* + * it's on the empty list and still hashed + */ + list_del(&cfqq->cfq_list); + hlist_del(&cfqq->cfq_hash); + kmem_cache_free(cfq_pool, cfqq); +} + static inline struct cfq_queue * -__cfq_find_cfq_hash(struct cfq_data *cfqd, int pid, const int hashval) +__cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned long key, const int hashval) { - struct list_head *hash_list = &cfqd->cfq_hash[hashval]; - struct list_head *entry; + struct hlist_head *hash_list = &cfqd->cfq_hash[hashval]; + struct hlist_node *entry, *next; - list_for_each(entry, hash_list) { + hlist_for_each_safe(entry, next, hash_list) { struct cfq_queue *__cfqq = list_entry_qhash(entry); - if (__cfqq->pid == pid) + if (__cfqq->key == key) return __cfqq; } return NULL; } -static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *cfqd, int pid) +static struct cfq_queue * +cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned long key) { - const int hashval = hash_long(current->tgid, CFQ_QHASH_SHIFT); + return __cfq_find_cfq_hash(cfqd, key, 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) +{ + 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); - return __cfq_find_cfq_hash(cfqd, pid, hashval); + 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; + } + + cic->cfqq = __cfqq; + spin_unlock_irqrestore(cfqd->queue->queue_lock, flags); } -static void cfq_put_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) +static void cfq_free_io_context(struct cfq_io_context *cic) { - cfqd->busy_queues--; - list_del(&cfqq->cfq_list); - list_del(&cfqq->cfq_hash); - mempool_free(cfqq, cfq_mpool); + kmem_cache_free(cfq_ioc_pool, cic); } -static struct cfq_queue *__cfq_get_queue(struct cfq_data *cfqd, int pid, - int gfp_mask) +/* + * locking hierarchy is: io_context lock -> queue locks + */ +static void cfq_exit_io_context(struct cfq_io_context *cic) { - const int hashval = hash_long(current->tgid, CFQ_QHASH_SHIFT); + struct cfq_queue *cfqq = cic->cfqq; + struct list_head *entry = &cic->list; + request_queue_t *q; + unsigned long 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; + + __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); + } + + 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); +} + +static struct cfq_io_context *cfq_alloc_io_context(int gfp_flags) +{ + struct cfq_io_context *cic = kmem_cache_alloc(cfq_ioc_pool, gfp_flags); + + if (cic) { + cic->dtor = cfq_free_io_context; + cic->exit = cfq_exit_io_context; + INIT_LIST_HEAD(&cic->list); + cic->cfqq = NULL; + } + + return cic; +} + +/* + * 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 + * than one device managed by cfq. Note that caller is holding a reference to + * 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) +{ + struct cfq_data *cfqd = (*cfqq)->cfqd; + struct cfq_queue *__cfqq = *cfqq; + struct cfq_io_context *cic; + struct io_context *ioc; + + might_sleep_if(gfp_flags & __GFP_WAIT); + + ioc = get_io_context(gfp_flags); + if (!ioc) + return NULL; + + if ((cic = ioc->cic) == NULL) { + cic = cfq_alloc_io_context(gfp_flags); + + if (cic == NULL) + goto err; + + ioc->cic = cic; + cic->ioc = ioc; + cic->cfqq = __cfqq; + atomic_inc(&__cfqq->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 + */ + if (cic->cfqq == __cfqq) + goto out; + + /* + * cic exists, check if we already are there. linear search + * 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) { + 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); + if (__cic == NULL) + goto err; + + __cic->ioc = ioc; + __cic->cfqq = __cfqq; + atomic_inc(&__cfqq->ref); + spin_lock_irqsave(&ioc->lock, flags); + 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) +{ + const int hashval = hash_long(key, CFQ_QHASH_SHIFT); struct cfq_queue *cfqq, *new_cfqq = NULL; - request_queue_t *q = cfqd->queue; retry: - cfqq = __cfq_find_cfq_hash(cfqd, pid, hashval); + cfqq = __cfq_find_cfq_hash(cfqd, key, hashval); if (!cfqq) { if (new_cfqq) { cfqq = new_cfqq; new_cfqq = NULL; } else if (gfp_mask & __GFP_WAIT) { - spin_unlock_irq(q->queue_lock); - new_cfqq = mempool_alloc(cfq_mpool, gfp_mask); - spin_lock_irq(q->queue_lock); + 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 - return NULL; + goto out; + + memset(cfqq, 0, sizeof(*cfqq)); - INIT_LIST_HEAD(&cfqq->cfq_hash); + INIT_HLIST_NODE(&cfqq->cfq_hash); INIT_LIST_HEAD(&cfqq->cfq_list); RB_CLEAR_ROOT(&cfqq->sort_list); - - cfqq->pid = pid; - cfqq->queued[0] = cfqq->queued[1] = 0; - list_add(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); + INIT_LIST_HEAD(&cfqq->fifo[0]); + INIT_LIST_HEAD(&cfqq->fifo[1]); + + 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 (new_cfqq) - mempool_free(new_cfqq, cfq_mpool); + kmem_cache_free(cfq_pool, new_cfqq); + atomic_inc(&cfqq->ref); +out: + WARN_ON((gfp_mask & __GFP_WAIT) && !cfqq); return cfqq; } -static struct cfq_queue *cfq_get_queue(struct cfq_data *cfqd, int pid, - int gfp_mask) +static struct cfq_queue * +cfq_get_queue(struct cfq_data *cfqd, unsigned long key, int gfp_mask) { request_queue_t *q = cfqd->queue; struct cfq_queue *cfqq; spin_lock_irq(q->queue_lock); - cfqq = __cfq_get_queue(cfqd, pid, gfp_mask); + cfqq = __cfq_get_queue(cfqd, key, gfp_mask); spin_unlock_irq(q->queue_lock); return cfqq; @@ -508,40 +1243,30 @@ static struct cfq_queue *cfq_get_queue(struct cfq_data *cfqd, int pid, static void cfq_enqueue(struct cfq_data *cfqd, struct cfq_rq *crq) { - struct cfq_queue *cfqq; + crq->is_sync = 0; + if (rq_data_dir(crq->request) == READ || current->flags & PF_SYNCWRITE) + crq->is_sync = 1; - cfqq = __cfq_get_queue(cfqd, current->tgid, GFP_ATOMIC); - if (cfqq) { - cfq_add_crq_rb(cfqd, cfqq, crq); + cfq_add_crq_rb(crq); + crq->queue_start = jiffies; - if (list_empty(&cfqq->cfq_list)) { - list_add(&cfqq->cfq_list, &cfqd->rr_list); - cfqd->busy_queues++; - } - } else { - /* - * should can only happen if the request wasn't allocated - * through blk_alloc_request(), eg stack requests from ide-cd - * (those should be removed) _and_ we are in OOM. - */ - list_add_tail(&crq->request->queuelist, cfqd->dispatch); - } + list_add_tail(&crq->request->queuelist, &crq->cfq_queue->fifo[crq->is_sync]); } static void cfq_insert_request(request_queue_t *q, struct request *rq, int where) { - struct cfq_data *cfqd = q->elevator.elevator_data; + 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)) + while (cfq_dispatch_requests(q, cfqd->cfq_quantum)) ; - list_add_tail(&rq->queuelist, cfqd->dispatch); + list_add_tail(&rq->queuelist, &q->queue_head); break; case ELEVATOR_INSERT_FRONT: - list_add(&rq->queuelist, cfqd->dispatch); + list_add(&rq->queuelist, &q->queue_head); break; case ELEVATOR_INSERT_SORT: BUG_ON(!blk_fs_request(rq)); @@ -562,12 +1287,27 @@ cfq_insert_request(request_queue_t *q, struct request *rq, int where) static int cfq_queue_empty(request_queue_t *q) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; - if (list_empty(cfqd->dispatch) && list_empty(&cfqd->rr_list)) - return 1; + return list_empty(&q->queue_head) && list_empty(&cfqd->rr_list); +} + +static void cfq_completed_request(request_queue_t *q, struct request *rq) +{ + struct cfq_rq *crq = RQ_DATA(rq); + + if (unlikely(!blk_fs_request(rq))) + return; + + if (crq->in_flight) { + struct cfq_queue *cfqq = crq->cfq_queue; + + WARN_ON(!cfqq->in_flight); + cfqq->in_flight--; + + cfq_account_completion(cfqq, crq); + } - return 0; } static struct request * @@ -596,92 +1336,169 @@ cfq_latter_request(request_queue_t *q, struct request *rq) static int cfq_may_queue(request_queue_t *q, int rw) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_queue *cfqq; - int ret = 1; + int ret = ELV_MQUEUE_MAY; - if (!cfqd->busy_queues) - goto out; + if (current->flags & PF_MEMALLOC) + return ELV_MQUEUE_MAY; - cfqq = cfq_find_cfq_hash(cfqd, current->tgid); + cfqq = cfq_find_cfq_hash(cfqd, cfq_hash_key(cfqd, current)); if (cfqq) { - int limit = (q->nr_requests - cfqd->cfq_queued) / cfqd->busy_queues; + int limit = cfqd->max_queued; + + if (cfqq->allocated[rw] < cfqd->cfq_queued) + return ELV_MQUEUE_MUST; - if (limit < 3) - limit = 3; + if (cfqd->busy_queues) + limit = q->nr_requests / cfqd->busy_queues; + + if (limit < cfqd->cfq_queued) + limit = cfqd->cfq_queued; else if (limit > cfqd->max_queued) limit = cfqd->max_queued; - if (cfqq->queued[rw] > limit) - ret = 0; + if (cfqq->allocated[rw] >= limit) { + if (limit > cfqq->alloc_limit[rw]) + cfqq->alloc_limit[rw] = limit; + + ret = ELV_MQUEUE_NO; + } } -out: + return ret; } +static void cfq_check_waiters(request_queue_t *q, struct cfq_queue *cfqq) +{ + 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]); +} + +/* + * queue lock held here + */ static void cfq_put_request(request_queue_t *q, struct request *rq) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_rq *crq = RQ_DATA(rq); - struct request_list *rl; - int other_rw; if (crq) { + struct cfq_queue *cfqq = crq->cfq_queue; + BUG_ON(q->last_merge == rq); - BUG_ON(ON_MHASH(crq)); + BUG_ON(!hlist_unhashed(&crq->hash)); + + if (crq->io_context) + put_io_context(crq->io_context->ioc); + + if (!cfqq->allocated[crq->is_write]) { + WARN_ON(1); + cfqq->allocated[crq->is_write] = 1; + } + cfqq->allocated[crq->is_write]--; mempool_free(crq, cfqd->crq_pool); rq->elevator_private = NULL; - } - /* - * work-around for may_queue "bug": if a read gets issued and refused - * to queue because writes ate all the allowed slots and no other - * reads are pending for this queue, it could get stuck infinitely - * since freed_request() only checks the waitqueue for writes when - * freeing them. or vice versa for a single write vs many reads. - * so check here whether "the other" data direction might be able - * to queue and wake them - */ - rl = &q->rq; - other_rw = rq_data_dir(rq) ^ 1; - if (rl->count[other_rw] <= q->nr_requests) { smp_mb(); - if (waitqueue_active(&rl->wait[other_rw])) - wake_up(&rl->wait[other_rw]); + cfq_check_waiters(q, cfqq); + cfq_put_queue(cfqq); } } +/* + * Allocate cfq data structures associated with this request. A queue and + */ static int cfq_set_request(request_queue_t *q, struct request *rq, int gfp_mask) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; + struct cfq_io_context *cic; + const int rw = rq_data_dir(rq); struct cfq_queue *cfqq; struct cfq_rq *crq; + unsigned long flags; + + might_sleep_if(gfp_mask & __GFP_WAIT); + + spin_lock_irqsave(q->queue_lock, flags); + + cfqq = __cfq_get_queue(cfqd, cfq_hash_key(cfqd, current), gfp_mask); + if (!cfqq) { +#if 0 + cfqq = cfq_get_queue(cfqd, CFQ_KEY_SPARE, gfp_mask); + printk("%s: got spare queue\n", current->comm); +#else + goto out_lock; +#endif + } + + if (cfqq->allocated[rw] >= cfqd->max_queued) + goto out_lock; + + spin_unlock_irqrestore(q->queue_lock, flags); /* - * prepare a queue up front, so cfq_enqueue() doesn't have to + * if hashing type has changed, the cfq_queue might change here. we + * don't bother rechecking ->allocated since it should be a rare + * event */ - cfqq = cfq_get_queue(cfqd, current->tgid, gfp_mask); - if (!cfqq) - return 1; + cic = cfq_get_io_context(&cfqq, gfp_mask); + if (!cic) + goto err; crq = mempool_alloc(cfqd->crq_pool, gfp_mask); if (crq) { - memset(crq, 0, sizeof(*crq)); RB_CLEAR(&crq->rb_node); + crq->rb_key = 0; crq->request = rq; - crq->cfq_queue = NULL; - INIT_LIST_HEAD(&crq->hash); + 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; rq->elevator_private = crq; + cfqq->allocated[rw]++; + cfqq->alloc_limit[rw] = 0; return 0; } + put_io_context(cic->ioc); +err: + spin_lock_irqsave(q->queue_lock, flags); + cfq_put_queue(cfqq); +out_lock: + spin_unlock_irqrestore(q->queue_lock, flags); return 1; } -static void cfq_exit(request_queue_t *q, elevator_t *e) +static void cfq_put_cfqd(struct cfq_data *cfqd) { - struct cfq_data *cfqd = e->elevator_data; + request_queue_t *q = cfqd->queue; + elevator_t *e = q->elevator; + struct cfq_queue *cfqq; + + if (!atomic_dec_and_test(&cfqd->ref)) + return; + + /* + * kill spare queue, getting it means we have two refences to it. + * drop both + */ + spin_lock_irq(q->queue_lock); + cfqq = __cfq_get_queue(cfqd, CFQ_KEY_SPARE, GFP_ATOMIC); + cfq_put_queue(cfqq); + cfq_put_queue(cfqq); + spin_unlock_irq(q->queue_lock); + + blk_put_queue(q); e->elevator_data = NULL; mempool_destroy(cfqd->crq_pool); @@ -690,9 +1507,15 @@ static void cfq_exit(request_queue_t *q, elevator_t *e) kfree(cfqd); } -static int cfq_init(request_queue_t *q, elevator_t *e) +static void cfq_exit_queue(elevator_t *e) +{ + cfq_put_cfqd(e->elevator_data); +} + +static int cfq_init_queue(request_queue_t *q, elevator_t *e) { struct cfq_data *cfqd; + struct cfq_queue *cfqq; int i; cfqd = kmalloc(sizeof(*cfqd), GFP_KERNEL); @@ -701,12 +1524,13 @@ static int cfq_init(request_queue_t *q, elevator_t *e) memset(cfqd, 0, sizeof(*cfqd)); INIT_LIST_HEAD(&cfqd->rr_list); + INIT_LIST_HEAD(&cfqd->empty_list); - cfqd->crq_hash = kmalloc(sizeof(struct list_head) * CFQ_MHASH_ENTRIES, GFP_KERNEL); + cfqd->crq_hash = kmalloc(sizeof(struct hlist_head) * CFQ_MHASH_ENTRIES, GFP_KERNEL); if (!cfqd->crq_hash) goto out_crqhash; - cfqd->cfq_hash = kmalloc(sizeof(struct list_head) * CFQ_QHASH_ENTRIES, GFP_KERNEL); + cfqd->cfq_hash = kmalloc(sizeof(struct hlist_head) * CFQ_QHASH_ENTRIES, GFP_KERNEL); if (!cfqd->cfq_hash) goto out_cfqhash; @@ -715,25 +1539,44 @@ static int cfq_init(request_queue_t *q, elevator_t *e) goto out_crqpool; for (i = 0; i < CFQ_MHASH_ENTRIES; i++) - INIT_LIST_HEAD(&cfqd->crq_hash[i]); + INIT_HLIST_HEAD(&cfqd->crq_hash[i]); for (i = 0; i < CFQ_QHASH_ENTRIES; i++) - INIT_LIST_HEAD(&cfqd->cfq_hash[i]); + INIT_HLIST_HEAD(&cfqd->cfq_hash[i]); - cfqd->dispatch = &q->queue_head; e->elevator_data = cfqd; + cfqd->queue = q; + atomic_inc(&q->refcnt); + + /* + * setup spare failure queue + */ + cfqq = cfq_get_queue(cfqd, CFQ_KEY_SPARE, GFP_KERNEL); + if (!cfqq) + goto out_spare; /* * just set it to some high value, we want anyone to be able to queue * some requests. fairness is handled differently */ - cfqd->max_queued = q->nr_requests; - q->nr_requests = 8192; + q->nr_requests = 1024; + cfqd->max_queued = q->nr_requests / 16; + q->nr_batching = cfq_queued; + cfqd->key_type = CFQ_KEY_TGID; + cfqd->find_best_crq = 1; + 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_back_max = cfq_back_max; + cfqd->cfq_back_penalty = cfq_back_penalty; return 0; +out_spare: + mempool_destroy(cfqd->crq_pool); out_crqpool: kfree(cfqd->cfq_hash); out_cfqhash: @@ -743,29 +1586,39 @@ out_crqhash: return -ENOMEM; } +static void cfq_slab_kill(void) +{ + if (crq_pool) + kmem_cache_destroy(crq_pool); + if (cfq_pool) + kmem_cache_destroy(cfq_pool); + if (cfq_ioc_pool) + kmem_cache_destroy(cfq_ioc_pool); +} + static int __init cfq_slab_setup(void) { crq_pool = kmem_cache_create("crq_pool", sizeof(struct cfq_rq), 0, 0, NULL, NULL); - if (!crq_pool) - panic("cfq_iosched: can't init crq pool\n"); + goto fail; cfq_pool = kmem_cache_create("cfq_pool", sizeof(struct cfq_queue), 0, 0, NULL, NULL); - if (!cfq_pool) - panic("cfq_iosched: can't init cfq pool\n"); + goto fail; - cfq_mpool = mempool_create(64, mempool_alloc_slab, mempool_free_slab, cfq_pool); - - if (!cfq_mpool) - panic("cfq_iosched: can't init cfq mpool\n"); + cfq_ioc_pool = kmem_cache_create("cfq_ioc_pool", + sizeof(struct cfq_io_context), 0, 0, NULL, NULL); + if (!cfq_ioc_pool) + goto fail; return 0; +fail: + cfq_slab_kill(); + return -ENOMEM; } -subsys_initcall(cfq_slab_setup); /* * sysfs parts below --> @@ -791,27 +1644,135 @@ cfq_var_store(unsigned int *var, const char *page, size_t count) return count; } -#define SHOW_FUNCTION(__FUNC, __VAR) \ +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; +} + +static ssize_t +cfq_status_show(struct cfq_data *cfqd, char *page) +{ + struct list_head *entry; + struct cfq_queue *cfqq; + ssize_t len; + int i = 0, queues; + + len = sprintf(page, "Busy queues: %u\n", cfqd->busy_queues); + len += sprintf(page+len, "key type: %s\n", + cfq_key_types[cfqd->key_type]); + len += sprintf(page+len, "last sector: %Lu\n", + (unsigned long long)cfqd->last_sector); + len += sprintf(page+len, "max time in iosched: %lu\n", + max_elapsed_dispatch); + len += sprintf(page+len, "max completion time: %lu\n", max_elapsed_crq); + + len += sprintf(page+len, "Busy queue list:\n"); + spin_lock_irq(cfqd->queue->queue_lock); + list_for_each(entry, &cfqd->rr_list) { + i++; + cfqq = list_entry_cfqq(entry); + len += sprintf(page+len, " cfqq: key=%lu alloc=%d/%d, " + "queued=%d/%d, last_fifo=%lu, service_used=%lu\n", + cfqq->key, cfqq->allocated[0], cfqq->allocated[1], + cfqq->queued[0], cfqq->queued[1], + cfqq->last_fifo_expire, cfqq->service_used); + } + len += sprintf(page+len, " busy queues total: %d\n", i); + queues = i; + + len += sprintf(page+len, "Empty queue list:\n"); + i = 0; + list_for_each(entry, &cfqd->empty_list) { + i++; + cfqq = list_entry_cfqq(entry); + len += sprintf(page+len, " cfqq: key=%lu alloc=%d/%d, " + "queued=%d/%d, last_fifo=%lu, service_used=%lu\n", + cfqq->key, cfqq->allocated[0], cfqq->allocated[1], + cfqq->queued[0], cfqq->queued[1], + cfqq->last_fifo_expire, cfqq->service_used); + } + len += sprintf(page+len, " empty queues total: %d\n", i); + queues += i; + len += sprintf(page+len, "Total queues: %d\n", queues); + spin_unlock_irq(cfqd->queue->queue_lock); + return len; +} + +#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ static ssize_t __FUNC(struct cfq_data *cfqd, char *page) \ { \ - return cfq_var_show(__VAR, (page)); \ + unsigned int __data = __VAR; \ + if (__CONV) \ + __data = jiffies_to_msecs(__data); \ + return cfq_var_show(__data, (page)); \ } -SHOW_FUNCTION(cfq_quantum_show, cfqd->cfq_quantum); -SHOW_FUNCTION(cfq_queued_show, cfqd->cfq_queued); +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_back_max_show, cfqd->cfq_back_max, 0); +SHOW_FUNCTION(cfq_back_penalty_show, cfqd->cfq_back_penalty, 0); #undef SHOW_FUNCTION -#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX) \ +#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ static ssize_t __FUNC(struct cfq_data *cfqd, const char *page, size_t count) \ { \ - int ret = cfq_var_store(__PTR, (page), count); \ - if (*(__PTR) < (MIN)) \ - *(__PTR) = (MIN); \ - else if (*(__PTR) > (MAX)) \ - *(__PTR) = (MAX); \ + unsigned int __data; \ + int ret = cfq_var_store(&__data, (page), count); \ + if (__data < (MIN)) \ + __data = (MIN); \ + else if (__data > (MAX)) \ + __data = (MAX); \ + if (__CONV) \ + *(__PTR) = msecs_to_jiffies(__data); \ + else \ + *(__PTR) = __data; \ return ret; \ } -STORE_FUNCTION(cfq_quantum_store, &cfqd->cfq_quantum, 1, INT_MAX); -STORE_FUNCTION(cfq_queued_store, &cfqd->cfq_queued, 1, INT_MAX); +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_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); #undef STORE_FUNCTION static struct cfq_fs_entry cfq_quantum_entry = { @@ -824,10 +1785,62 @@ 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 = { + .attr = {.name = "fifo_expire_sync", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_fifo_expire_r_show, + .store = cfq_fifo_expire_r_store, +}; +static struct cfq_fs_entry cfq_fifo_expire_w_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, +}; +static struct cfq_fs_entry cfq_back_max_entry = { + .attr = {.name = "back_seek_max", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_back_max_show, + .store = cfq_back_max_store, +}; +static struct cfq_fs_entry cfq_back_penalty_entry = { + .attr = {.name = "back_seek_penalty", .mode = S_IRUGO | S_IWUSR }, + .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_misc_entry = { + .attr = {.name = "show_status", .mode = S_IRUGO }, + .show = cfq_status_show, +}; +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 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_back_max_entry.attr, + &cfq_back_penalty_entry.attr, + &cfq_clear_elapsed_entry.attr, + &cfq_misc_entry.attr, NULL, }; @@ -868,23 +1881,56 @@ struct kobj_type cfq_ktype = { .default_attrs = default_attrs, }; -elevator_t iosched_cfq = { - .elevator_name = "cfq", - .elevator_ktype = &cfq_ktype, - .elevator_merge_fn = cfq_merge, - .elevator_merged_fn = cfq_merged_request, - .elevator_merge_req_fn = cfq_merged_requests, - .elevator_next_req_fn = cfq_next_request, - .elevator_add_req_fn = cfq_insert_request, - .elevator_remove_req_fn = cfq_remove_request, - .elevator_queue_empty_fn = cfq_queue_empty, - .elevator_former_req_fn = cfq_former_request, - .elevator_latter_req_fn = cfq_latter_request, - .elevator_set_req_fn = cfq_set_request, - .elevator_put_req_fn = cfq_put_request, - .elevator_may_queue_fn = cfq_may_queue, - .elevator_init_fn = cfq_init, - .elevator_exit_fn = cfq_exit, +static struct elevator_type iosched_cfq = { + .ops = { + .elevator_merge_fn = cfq_merge, + .elevator_merged_fn = cfq_merged_request, + .elevator_merge_req_fn = cfq_merged_requests, + .elevator_next_req_fn = cfq_next_request, + .elevator_add_req_fn = cfq_insert_request, + .elevator_remove_req_fn = cfq_remove_request, + .elevator_requeue_req_fn = cfq_requeue_request, + .elevator_queue_empty_fn = cfq_queue_empty, + .elevator_completed_req_fn = cfq_completed_request, + .elevator_former_req_fn = cfq_former_request, + .elevator_latter_req_fn = cfq_latter_request, + .elevator_set_req_fn = cfq_set_request, + .elevator_put_req_fn = cfq_put_request, + .elevator_may_queue_fn = cfq_may_queue, + .elevator_init_fn = cfq_init_queue, + .elevator_exit_fn = cfq_exit_queue, + }, + .elevator_ktype = &cfq_ktype, + .elevator_name = "cfq", + .elevator_owner = THIS_MODULE, }; -EXPORT_SYMBOL(iosched_cfq); +int cfq_init(void) +{ + int ret; + + if (cfq_slab_setup()) + return -ENOMEM; + + ret = elv_register(&iosched_cfq); + if (!ret) { + __module_get(THIS_MODULE); + return 0; + } + + cfq_slab_kill(); + return ret; +} + +void cfq_exit(void) +{ + cfq_slab_kill(); + elv_unregister(&iosched_cfq); +} + +module_init(cfq_init); +module_exit(cfq_exit); + +MODULE_AUTHOR("Jens Axboe"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Completely Fair Queueing IO scheduler"); diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 204b3182900d..dc896a12283b 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -21,7 +21,6 @@ */ #include <linux/config.h> /* CONFIG_PROC_FS */ #include <linux/module.h> -#include <linux/version.h> #include <linux/types.h> #include <linux/pci.h> #include <linux/bio.h> @@ -732,7 +731,6 @@ static void __iomem *remap_pci_mem(ulong base, ulong size) } #ifndef MODULE -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13) /* * Config string is a comma separated set of i/o addresses of EISA cards. */ @@ -749,18 +747,6 @@ static int cpqarray_setup(char *str) __setup("smart2=", cpqarray_setup); -#else - -/* - * Copy the contents of the ints[] array passed to us by init. - */ -void cpqarray_setup(char *str, int *ints) -{ - int i; - for(i=0; i<ints[0] && i<8; i++) - eisa[i] = ints[i+1]; -} -#endif #endif /* diff --git a/drivers/block/deadline-iosched.c b/drivers/block/deadline-iosched.c index fb7ab733c709..f482e8bdb4d6 100644 --- a/drivers/block/deadline-iosched.c +++ b/drivers/block/deadline-iosched.c @@ -289,7 +289,7 @@ deadline_find_first_drq(struct deadline_data *dd, int data_dir) static inline void deadline_add_request(struct request_queue *q, struct request *rq) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; struct deadline_rq *drq = RQ_DATA(rq); const int data_dir = rq_data_dir(drq->request); @@ -317,7 +317,7 @@ static void deadline_remove_request(request_queue_t *q, struct request *rq) struct deadline_rq *drq = RQ_DATA(rq); if (drq) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; list_del_init(&drq->fifo); deadline_remove_merge_hints(q, drq); @@ -328,7 +328,7 @@ static void deadline_remove_request(request_queue_t *q, struct request *rq) static int deadline_merge(request_queue_t *q, struct request **req, struct bio *bio) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; struct request *__rq; int ret; @@ -383,7 +383,7 @@ out_insert: static void deadline_merged_request(request_queue_t *q, struct request *req) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; struct deadline_rq *drq = RQ_DATA(req); /* @@ -407,7 +407,7 @@ static void deadline_merged_requests(request_queue_t *q, struct request *req, struct request *next) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; struct deadline_rq *drq = RQ_DATA(req); struct deadline_rq *dnext = RQ_DATA(next); @@ -604,7 +604,7 @@ dispatch_request: static struct request *deadline_next_request(request_queue_t *q) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; struct request *rq; /* @@ -625,7 +625,7 @@ dispatch: static void deadline_insert_request(request_queue_t *q, struct request *rq, int where) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; /* barriers must flush the reorder queue */ if (unlikely(rq->flags & (REQ_SOFTBARRIER | REQ_HARDBARRIER) @@ -653,7 +653,7 @@ deadline_insert_request(request_queue_t *q, struct request *rq, int where) static int deadline_queue_empty(request_queue_t *q) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; if (!list_empty(&dd->fifo_list[WRITE]) || !list_empty(&dd->fifo_list[READ]) @@ -687,7 +687,7 @@ deadline_latter_request(request_queue_t *q, struct request *rq) return NULL; } -static void deadline_exit(request_queue_t *q, elevator_t *e) +static void deadline_exit_queue(elevator_t *e) { struct deadline_data *dd = e->elevator_data; @@ -703,7 +703,7 @@ static void deadline_exit(request_queue_t *q, elevator_t *e) * initialize elevator private data (deadline_data), and alloc a drq for * each request on the free lists */ -static int deadline_init(request_queue_t *q, elevator_t *e) +static int deadline_init_queue(request_queue_t *q, elevator_t *e) { struct deadline_data *dd; int i; @@ -748,7 +748,7 @@ static int deadline_init(request_queue_t *q, elevator_t *e) static void deadline_put_request(request_queue_t *q, struct request *rq) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; struct deadline_rq *drq = RQ_DATA(rq); if (drq) { @@ -760,7 +760,7 @@ 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) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; struct deadline_rq *drq; drq = mempool_alloc(dd->drq_pool, gfp_mask); @@ -805,33 +805,41 @@ deadline_var_store(unsigned int *var, const char *page, size_t count) return count; } -#define SHOW_FUNCTION(__FUNC, __VAR) \ +#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ static ssize_t __FUNC(struct deadline_data *dd, char *page) \ { \ - return deadline_var_show(__VAR, (page)); \ -} -SHOW_FUNCTION(deadline_readexpire_show, dd->fifo_expire[READ]); -SHOW_FUNCTION(deadline_writeexpire_show, dd->fifo_expire[WRITE]); -SHOW_FUNCTION(deadline_writesstarved_show, dd->writes_starved); -SHOW_FUNCTION(deadline_frontmerges_show, dd->front_merges); -SHOW_FUNCTION(deadline_fifobatch_show, dd->fifo_batch); + unsigned int __data = __VAR; \ + if (__CONV) \ + __data = jiffies_to_msecs(__data); \ + return deadline_var_show(__data, (page)); \ +} +SHOW_FUNCTION(deadline_readexpire_show, dd->fifo_expire[READ], 1); +SHOW_FUNCTION(deadline_writeexpire_show, dd->fifo_expire[WRITE], 1); +SHOW_FUNCTION(deadline_writesstarved_show, dd->writes_starved, 0); +SHOW_FUNCTION(deadline_frontmerges_show, dd->front_merges, 0); +SHOW_FUNCTION(deadline_fifobatch_show, dd->fifo_batch, 0); #undef SHOW_FUNCTION -#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX) \ +#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ static ssize_t __FUNC(struct deadline_data *dd, const char *page, size_t count) \ { \ - int ret = deadline_var_store(__PTR, (page), count); \ - if (*(__PTR) < (MIN)) \ - *(__PTR) = (MIN); \ - else if (*(__PTR) > (MAX)) \ - *(__PTR) = (MAX); \ + unsigned int __data; \ + int ret = deadline_var_store(&__data, (page), count); \ + if (__data < (MIN)) \ + __data = (MIN); \ + else if (__data > (MAX)) \ + __data = (MAX); \ + if (__CONV) \ + *(__PTR) = msecs_to_jiffies(__data); \ + else \ + *(__PTR) = __data; \ return ret; \ } -STORE_FUNCTION(deadline_readexpire_store, &dd->fifo_expire[READ], 0, INT_MAX); -STORE_FUNCTION(deadline_writeexpire_store, &dd->fifo_expire[WRITE], 0, INT_MAX); -STORE_FUNCTION(deadline_writesstarved_store, &dd->writes_starved, INT_MIN, INT_MAX); -STORE_FUNCTION(deadline_frontmerges_store, &dd->front_merges, 0, 1); -STORE_FUNCTION(deadline_fifobatch_store, &dd->fifo_batch, 0, INT_MAX); +STORE_FUNCTION(deadline_readexpire_store, &dd->fifo_expire[READ], 0, INT_MAX, 1); +STORE_FUNCTION(deadline_writeexpire_store, &dd->fifo_expire[WRITE], 0, INT_MAX, 1); +STORE_FUNCTION(deadline_writesstarved_store, &dd->writes_starved, INT_MIN, INT_MAX, 0); +STORE_FUNCTION(deadline_frontmerges_store, &dd->front_merges, 0, 1, 0); +STORE_FUNCTION(deadline_fifobatch_store, &dd->fifo_batch, 0, INT_MAX, 0); #undef STORE_FUNCTION static struct deadline_fs_entry deadline_readexpire_entry = { @@ -906,36 +914,54 @@ struct kobj_type deadline_ktype = { .default_attrs = default_attrs, }; -static int __init deadline_slab_setup(void) +static struct elevator_type iosched_deadline = { + .ops = { + .elevator_merge_fn = deadline_merge, + .elevator_merged_fn = deadline_merged_request, + .elevator_merge_req_fn = deadline_merged_requests, + .elevator_next_req_fn = deadline_next_request, + .elevator_add_req_fn = deadline_insert_request, + .elevator_remove_req_fn = deadline_remove_request, + .elevator_queue_empty_fn = deadline_queue_empty, + .elevator_former_req_fn = deadline_former_request, + .elevator_latter_req_fn = deadline_latter_request, + .elevator_set_req_fn = deadline_set_request, + .elevator_put_req_fn = deadline_put_request, + .elevator_init_fn = deadline_init_queue, + .elevator_exit_fn = deadline_exit_queue, + }, + + .elevator_ktype = &deadline_ktype, + .elevator_name = "deadline", + .elevator_owner = THIS_MODULE, +}; + +int deadline_init(void) { + int ret; + drq_pool = kmem_cache_create("deadline_drq", sizeof(struct deadline_rq), 0, 0, NULL, NULL); if (!drq_pool) - panic("deadline: can't init slab pool\n"); + return -ENOMEM; - return 0; + ret = elv_register(&iosched_deadline); + if (ret) + kmem_cache_destroy(drq_pool); + + return ret; } -subsys_initcall(deadline_slab_setup); - -elevator_t iosched_deadline = { - .elevator_merge_fn = deadline_merge, - .elevator_merged_fn = deadline_merged_request, - .elevator_merge_req_fn = deadline_merged_requests, - .elevator_next_req_fn = deadline_next_request, - .elevator_add_req_fn = deadline_insert_request, - .elevator_remove_req_fn = deadline_remove_request, - .elevator_queue_empty_fn = deadline_queue_empty, - .elevator_former_req_fn = deadline_former_request, - .elevator_latter_req_fn = deadline_latter_request, - .elevator_set_req_fn = deadline_set_request, - .elevator_put_req_fn = deadline_put_request, - .elevator_init_fn = deadline_init, - .elevator_exit_fn = deadline_exit, - - .elevator_ktype = &deadline_ktype, - .elevator_name = "deadline", -}; +void deadline_exit(void) +{ + kmem_cache_destroy(drq_pool); + elv_unregister(&iosched_deadline); +} + +module_init(deadline_init); +module_exit(deadline_exit); -EXPORT_SYMBOL(iosched_deadline); +MODULE_AUTHOR("Jens Axboe"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("deadline IO scheduler"); diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 35c9385ac133..1b4f6a70c0ca 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -37,6 +37,9 @@ #include <asm/uaccess.h> +static spinlock_t elv_list_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(elv_list); + /* * can we safely merge with this request? */ @@ -60,6 +63,7 @@ inline int elv_rq_merge_ok(struct request *rq, struct bio *bio) return 0; } +EXPORT_SYMBOL(elv_rq_merge_ok); inline int elv_try_merge(struct request *__rq, struct bio *bio) { @@ -77,6 +81,7 @@ inline int elv_try_merge(struct request *__rq, struct bio *bio) return ret; } +EXPORT_SYMBOL(elv_try_merge); inline int elv_try_last_merge(request_queue_t *q, struct bio *bio) { @@ -85,31 +90,117 @@ inline int elv_try_last_merge(request_queue_t *q, struct bio *bio) return ELEVATOR_NO_MERGE; } +EXPORT_SYMBOL(elv_try_last_merge); -/* - * general block -> elevator interface starts here - */ -int elevator_init(request_queue_t *q, elevator_t *type) +struct elevator_type *elevator_find(const char *name) +{ + struct elevator_type *e = NULL; + struct list_head *entry; + + spin_lock_irq(&elv_list_lock); + list_for_each(entry, &elv_list) { + struct elevator_type *__e; + + __e = list_entry(entry, struct elevator_type, list); + + if (!strcmp(__e->elevator_name, name)) { + e = __e; + break; + } + } + spin_unlock_irq(&elv_list_lock); + + return e; +} + +static int elevator_attach(request_queue_t *q, struct elevator_type *e, + struct elevator_queue *eq) { - elevator_t *e = &q->elevator; + int ret = 0; - memcpy(e, type, sizeof(*e)); + if (!try_module_get(e->elevator_owner)) + return -EINVAL; + + memset(eq, 0, sizeof(*eq)); + eq->ops = &e->ops; + eq->elevator_type = e; INIT_LIST_HEAD(&q->queue_head); q->last_merge = NULL; + q->elevator = eq; + + if (eq->ops->elevator_init_fn) + ret = eq->ops->elevator_init_fn(q, eq); - if (e->elevator_init_fn) - return e->elevator_init_fn(q, e); + return ret; +} + +static char chosen_elevator[16]; + +static void elevator_setup_default(void) +{ + /* + * check if default is set and exists + */ + if (chosen_elevator[0] && elevator_find(chosen_elevator)) + return; + +#if defined(CONFIG_IOSCHED_AS) + strcpy(chosen_elevator, "anticipatory"); +#elif defined(CONFIG_IOSCHED_DEADLINE) + strcpy(chosen_elevator, "deadline"); +#elif defined(CONFIG_IOSCHED_CFQ) + strcpy(chosen_elevator, "cfq"); +#elif defined(CONFIG_IOSCHED_NOOP) + strcpy(chosen_elevator, "noop"); +#else +#error "You must build at least 1 IO scheduler into the kernel" +#endif + printk("elevator: using %s as default io scheduler\n", chosen_elevator); +} +static int __init elevator_setup(char *str) +{ + strncpy(chosen_elevator, str, sizeof(chosen_elevator) - 1); return 0; } -void elevator_exit(request_queue_t *q) +__setup("elevator=", elevator_setup); + +int elevator_init(request_queue_t *q, char *name) +{ + struct elevator_type *e = NULL; + struct elevator_queue *eq; + int ret = 0; + + elevator_setup_default(); + + if (!name) + name = chosen_elevator; + + e = elevator_find(name); + if (!e) + return -EINVAL; + + eq = kmalloc(sizeof(struct elevator_queue), GFP_KERNEL); + if (!eq) + return -ENOMEM; + + ret = elevator_attach(q, e, eq); + if (ret) + kfree(eq); + + return ret; +} + +void elevator_exit(elevator_t *e) { - elevator_t *e = &q->elevator; + if (e->ops->elevator_exit_fn) + e->ops->elevator_exit_fn(e); - if (e->elevator_exit_fn) - e->elevator_exit_fn(q, e); + module_put(e->elevator_type->elevator_owner); + e->elevator_type = NULL; + kfree(e); } int elevator_global_init(void) @@ -119,32 +210,32 @@ int elevator_global_init(void) int elv_merge(request_queue_t *q, struct request **req, struct bio *bio) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_merge_fn) - return e->elevator_merge_fn(q, req, bio); + if (e->ops->elevator_merge_fn) + return e->ops->elevator_merge_fn(q, req, bio); return ELEVATOR_NO_MERGE; } void elv_merged_request(request_queue_t *q, struct request *rq) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_merged_fn) - e->elevator_merged_fn(q, rq); + if (e->ops->elevator_merged_fn) + e->ops->elevator_merged_fn(q, rq); } void elv_merge_requests(request_queue_t *q, struct request *rq, struct request *next) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; if (q->last_merge == next) q->last_merge = NULL; - if (e->elevator_merge_req_fn) - e->elevator_merge_req_fn(q, rq, next); + if (e->ops->elevator_merge_req_fn) + e->ops->elevator_merge_req_fn(q, rq, next); } void elv_requeue_request(request_queue_t *q, struct request *rq) @@ -160,8 +251,8 @@ void elv_requeue_request(request_queue_t *q, struct request *rq) * if iosched has an explicit requeue hook, then use that. otherwise * just put the request at the front of the queue */ - if (q->elevator.elevator_requeue_req_fn) - q->elevator.elevator_requeue_req_fn(q, rq); + if (q->elevator->ops->elevator_requeue_req_fn) + q->elevator->ops->elevator_requeue_req_fn(q, rq); else __elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 0); } @@ -180,7 +271,7 @@ void __elv_add_request(request_queue_t *q, struct request *rq, int where, blk_plug_device(q); rq->q = q; - q->elevator.elevator_add_req_fn(q, rq, where); + q->elevator->ops->elevator_add_req_fn(q, rq, where); if (blk_queue_plugged(q)) { int nrq = q->rq.count[READ] + q->rq.count[WRITE] - q->in_flight; @@ -203,7 +294,7 @@ void elv_add_request(request_queue_t *q, struct request *rq, int where, static inline struct request *__elv_next_request(request_queue_t *q) { - return q->elevator.elevator_next_req_fn(q); + return q->elevator->ops->elevator_next_req_fn(q); } struct request *elv_next_request(request_queue_t *q) @@ -252,7 +343,7 @@ struct request *elv_next_request(request_queue_t *q) void elv_remove_request(request_queue_t *q, struct request *rq) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; /* * the time frame between a request being removed from the lists @@ -274,16 +365,16 @@ void elv_remove_request(request_queue_t *q, struct request *rq) if (rq == q->last_merge) q->last_merge = NULL; - if (e->elevator_remove_req_fn) - e->elevator_remove_req_fn(q, rq); + if (e->ops->elevator_remove_req_fn) + e->ops->elevator_remove_req_fn(q, rq); } int elv_queue_empty(request_queue_t *q) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_queue_empty_fn) - return e->elevator_queue_empty_fn(q); + if (e->ops->elevator_queue_empty_fn) + return e->ops->elevator_queue_empty_fn(q); return list_empty(&q->queue_head); } @@ -292,10 +383,10 @@ struct request *elv_latter_request(request_queue_t *q, struct request *rq) { struct list_head *next; - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_latter_req_fn) - return e->elevator_latter_req_fn(q, rq); + if (e->ops->elevator_latter_req_fn) + return e->ops->elevator_latter_req_fn(q, rq); next = rq->queuelist.next; if (next != &q->queue_head && next != &rq->queuelist) @@ -308,10 +399,10 @@ struct request *elv_former_request(request_queue_t *q, struct request *rq) { struct list_head *prev; - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_former_req_fn) - return e->elevator_former_req_fn(q, rq); + if (e->ops->elevator_former_req_fn) + return e->ops->elevator_former_req_fn(q, rq); prev = rq->queuelist.prev; if (prev != &q->queue_head && prev != &rq->queuelist) @@ -322,10 +413,10 @@ struct request *elv_former_request(request_queue_t *q, struct request *rq) int elv_set_request(request_queue_t *q, struct request *rq, int gfp_mask) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_set_req_fn) - return e->elevator_set_req_fn(q, rq, gfp_mask); + if (e->ops->elevator_set_req_fn) + return e->ops->elevator_set_req_fn(q, rq, gfp_mask); rq->elevator_private = NULL; return 0; @@ -333,25 +424,25 @@ int elv_set_request(request_queue_t *q, struct request *rq, int gfp_mask) void elv_put_request(request_queue_t *q, struct request *rq) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_put_req_fn) - e->elevator_put_req_fn(q, rq); + if (e->ops->elevator_put_req_fn) + e->ops->elevator_put_req_fn(q, rq); } int elv_may_queue(request_queue_t *q, int rw) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_may_queue_fn) - return e->elevator_may_queue_fn(q, rw); + if (e->ops->elevator_may_queue_fn) + return e->ops->elevator_may_queue_fn(q, rw); - return 0; + return ELV_MQUEUE_MAY; } void elv_completed_request(request_queue_t *q, struct request *rq) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; /* * request is released from the driver, io must be done @@ -359,22 +450,20 @@ void elv_completed_request(request_queue_t *q, struct request *rq) if (blk_account_rq(rq)) q->in_flight--; - if (e->elevator_completed_req_fn) - e->elevator_completed_req_fn(q, rq); + if (e->ops->elevator_completed_req_fn) + e->ops->elevator_completed_req_fn(q, rq); } int elv_register_queue(struct request_queue *q) { - elevator_t *e; - - e = &q->elevator; + elevator_t *e = q->elevator; e->kobj.parent = kobject_get(&q->kobj); if (!e->kobj.parent) return -EBUSY; snprintf(e->kobj.name, KOBJ_NAME_LEN, "%s", "iosched"); - e->kobj.ktype = e->elevator_ktype; + e->kobj.ktype = e->elevator_type->elevator_ktype; return kobject_register(&e->kobj); } @@ -382,12 +471,131 @@ int elv_register_queue(struct request_queue *q) void elv_unregister_queue(struct request_queue *q) { if (q) { - elevator_t * e = &q->elevator; + elevator_t *e = q->elevator; kobject_unregister(&e->kobj); kobject_put(&q->kobj); } } +int elv_register(struct elevator_type *e) +{ + if (elevator_find(e->elevator_name)) + BUG(); + + spin_lock_irq(&elv_list_lock); + list_add_tail(&e->list, &elv_list); + spin_unlock_irq(&elv_list_lock); + + printk("io scheduler %s registered\n", e->elevator_name); + return 0; +} +EXPORT_SYMBOL_GPL(elv_register); + +void elv_unregister(struct elevator_type *e) +{ + spin_lock_irq(&elv_list_lock); + list_del_init(&e->list); + spin_unlock_irq(&elv_list_lock); +} +EXPORT_SYMBOL_GPL(elv_unregister); + +/* + * switch to new_e io scheduler. be careful not to introduce deadlocks - + * we don't free the old io scheduler, before we have allocated what we + * need for the new one. this way we have a chance of going back to the old + * one, if the new one fails init for some reason + */ +static void elevator_switch(request_queue_t *q, struct elevator_type *new_e) +{ + elevator_t *e = kmalloc(sizeof(elevator_t), GFP_KERNEL); + elevator_t *old_elevator; + + if (!e) { + printk("elevator: out of memory\n"); + return; + } + + blk_wait_queue_drained(q); + + /* + * unregister old elevator data + */ + elv_unregister_queue(q); + old_elevator = q->elevator; + + /* + * attach and start new elevator + */ + if (elevator_attach(q, new_e, e)) + goto fail; + + if (elv_register_queue(q)) + goto fail_register; + + /* + * finally exit old elevator and start queue again + */ + elevator_exit(old_elevator); + blk_finish_queue_drain(q); + return; + +fail_register: + /* + * switch failed, exit the new io scheduler and reattach the old + * one again (along with re-adding the sysfs dir) + */ + elevator_exit(e); +fail: + q->elevator = old_elevator; + elv_register_queue(q); + blk_finish_queue_drain(q); + printk("elevator: switch to %s failed\n", new_e->elevator_name); +} + +ssize_t elv_iosched_store(request_queue_t *q, const char *name, size_t count) +{ + char elevator_name[ELV_NAME_MAX]; + struct elevator_type *e; + + memset(elevator_name, 0, sizeof(elevator_name)); + strncpy(elevator_name, name, sizeof(elevator_name)); + + if (elevator_name[strlen(elevator_name) - 1] == '\n') + elevator_name[strlen(elevator_name) - 1] = '\0'; + + e = elevator_find(elevator_name); + if (!e) { + printk("elevator: type %s not found\n", elevator_name); + return -EINVAL; + } + + elevator_switch(q, e); + return count; +} + +ssize_t elv_iosched_show(request_queue_t *q, char *name) +{ + elevator_t *e = q->elevator; + struct elevator_type *elv = e->elevator_type; + struct list_head *entry; + int len = 0; + + spin_lock_irq(q->queue_lock); + list_for_each(entry, &elv_list) { + struct elevator_type *__e; + + __e = list_entry(entry, struct elevator_type, list); + if (!strcmp(elv->elevator_name, __e->elevator_name)) + len += sprintf(name+len, "[%s] ", elv->elevator_name); + else + len += sprintf(name+len, "%s ", __e->elevator_name); + } + spin_unlock_irq(q->queue_lock); + + len += sprintf(len+name, "\n"); + return len; +} + module_init(elevator_global_init); EXPORT_SYMBOL(elv_add_request); diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 26fdf6be6bd0..3ba6430899df 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -243,6 +243,7 @@ void blk_queue_make_request(request_queue_t * q, make_request_fn * mfn) blk_queue_hardsect_size(q, 512); blk_queue_dma_alignment(q, 511); blk_queue_congestion_threshold(q); + q->nr_batching = BLK_BATCH_REQ; q->unplug_thresh = 4; /* hmm */ q->unplug_delay = (3 * HZ) / 1000; /* 3 milliseconds */ @@ -1395,7 +1396,8 @@ void blk_cleanup_queue(request_queue_t * q) if (!atomic_dec_and_test(&q->refcnt)) return; - elevator_exit(q); + if (q->elevator) + elevator_exit(q->elevator); del_timer_sync(&q->unplug_timer); kblockd_flush(); @@ -1418,6 +1420,7 @@ static int blk_init_free_list(request_queue_t *q) rl->count[READ] = rl->count[WRITE] = 0; init_waitqueue_head(&rl->wait[READ]); init_waitqueue_head(&rl->wait[WRITE]); + init_waitqueue_head(&rl->drain); rl->rq_pool = mempool_create(BLKDEV_MIN_RQ, mempool_alloc_slab, mempool_free_slab, request_cachep); @@ -1429,45 +1432,6 @@ static int blk_init_free_list(request_queue_t *q) static int __make_request(request_queue_t *, struct bio *); -static elevator_t *chosen_elevator = -#if defined(CONFIG_IOSCHED_AS) - &iosched_as; -#elif defined(CONFIG_IOSCHED_DEADLINE) - &iosched_deadline; -#elif defined(CONFIG_IOSCHED_CFQ) - &iosched_cfq; -#elif defined(CONFIG_IOSCHED_NOOP) - &elevator_noop; -#else - NULL; -#error "You must have at least 1 I/O scheduler selected" -#endif - -#if defined(CONFIG_IOSCHED_AS) || defined(CONFIG_IOSCHED_DEADLINE) || defined (CONFIG_IOSCHED_NOOP) -static int __init elevator_setup(char *str) -{ -#ifdef CONFIG_IOSCHED_DEADLINE - if (!strcmp(str, "deadline")) - chosen_elevator = &iosched_deadline; -#endif -#ifdef CONFIG_IOSCHED_AS - if (!strcmp(str, "as")) - chosen_elevator = &iosched_as; -#endif -#ifdef CONFIG_IOSCHED_CFQ - if (!strcmp(str, "cfq")) - chosen_elevator = &iosched_cfq; -#endif -#ifdef CONFIG_IOSCHED_NOOP - if (!strcmp(str, "noop")) - chosen_elevator = &elevator_noop; -#endif - return 1; -} - -__setup("elevator=", elevator_setup); -#endif /* CONFIG_IOSCHED_AS || CONFIG_IOSCHED_DEADLINE || CONFIG_IOSCHED_NOOP */ - request_queue_t *blk_alloc_queue(int gfp_mask) { request_queue_t *q = kmem_cache_alloc(requestq_cachep, gfp_mask); @@ -1520,21 +1484,14 @@ EXPORT_SYMBOL(blk_alloc_queue); **/ request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock) { - request_queue_t *q; - static int printed; + request_queue_t *q = blk_alloc_queue(GFP_KERNEL); - q = blk_alloc_queue(GFP_KERNEL); if (!q) return NULL; if (blk_init_free_list(q)) goto out_init; - if (!printed) { - printed = 1; - printk("Using %s io scheduler\n", chosen_elevator->elevator_name); - } - q->request_fn = rfn; q->back_merge_fn = ll_back_merge_fn; q->front_merge_fn = ll_front_merge_fn; @@ -1555,8 +1512,10 @@ request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock) /* * all done */ - if (!elevator_init(q, chosen_elevator)) + if (!elevator_init(q, NULL)) { + blk_queue_congestion_threshold(q); return q; + } blk_cleanup_queue(q); out_init: @@ -1584,13 +1543,20 @@ 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 gfp_mask) +static inline struct request *blk_alloc_request(request_queue_t *q, int rw, + int gfp_mask) { struct request *rq = mempool_alloc(q->rq.rq_pool, gfp_mask); if (!rq) return NULL; + /* + * first three bits are identical in rq->flags and bio->bi_rw, + * see bio.h and blkdev.h + */ + rq->flags = rw; + if (!elv_set_request(q, rq, gfp_mask)) return rq; @@ -1602,7 +1568,7 @@ static inline struct request *blk_alloc_request(request_queue_t *q,int gfp_mask) * ioc_batching returns true if the ioc is a valid batching request and * should be given priority access to a request. */ -static inline int ioc_batching(struct io_context *ioc) +static inline int ioc_batching(request_queue_t *q, struct io_context *ioc) { if (!ioc) return 0; @@ -1612,7 +1578,7 @@ static inline int ioc_batching(struct io_context *ioc) * even if the batch times out, otherwise we could theoretically * lose wakeups. */ - return ioc->nr_batch_requests == BLK_BATCH_REQ || + return ioc->nr_batch_requests == q->nr_batching || (ioc->nr_batch_requests > 0 && time_before(jiffies, ioc->last_waited + BLK_BATCH_TIME)); } @@ -1623,12 +1589,12 @@ static inline int ioc_batching(struct io_context *ioc) * is the behaviour we want though - once it gets a wakeup it should be given * a nice run. */ -void ioc_set_batching(struct io_context *ioc) +void ioc_set_batching(request_queue_t *q, struct io_context *ioc) { - if (!ioc || ioc_batching(ioc)) + if (!ioc || ioc_batching(q, ioc)) return; - ioc->nr_batch_requests = BLK_BATCH_REQ; + ioc->nr_batch_requests = q->nr_batching; ioc->last_waited = jiffies; } @@ -1644,11 +1610,14 @@ static void freed_request(request_queue_t *q, int rw) if (rl->count[rw] < queue_congestion_off_threshold(q)) clear_queue_congested(q, rw); if (rl->count[rw]+1 <= q->nr_requests) { + smp_mb(); if (waitqueue_active(&rl->wait[rw])) wake_up(&rl->wait[rw]); - if (!waitqueue_active(&rl->wait[rw])) - blk_clear_queue_full(q, rw); + blk_clear_queue_full(q, rw); } + if (unlikely(waitqueue_active(&rl->drain)) && + !rl->count[READ] && !rl->count[WRITE]) + wake_up(&rl->drain); } #define blkdev_free_rq(list) list_entry((list)->next, struct request, queuelist) @@ -1661,6 +1630,9 @@ static struct request *get_request(request_queue_t *q, int rw, int gfp_mask) struct request_list *rl = &q->rq; struct io_context *ioc = get_io_context(gfp_mask); + if (unlikely(test_bit(QUEUE_FLAG_DRAIN, &q->queue_flags))) + return NULL; + spin_lock_irq(q->queue_lock); if (rl->count[rw]+1 >= q->nr_requests) { /* @@ -1670,13 +1642,22 @@ static struct request *get_request(request_queue_t *q, int rw, int gfp_mask) * will be blocked. */ if (!blk_queue_full(q, rw)) { - ioc_set_batching(ioc); + ioc_set_batching(q, ioc); blk_set_queue_full(q, rw); } } - if (blk_queue_full(q, rw) - && !ioc_batching(ioc) && !elv_may_queue(q, rw)) { + switch (elv_may_queue(q, rw)) { + case ELV_MQUEUE_NO: + spin_unlock_irq(q->queue_lock); + goto out; + case ELV_MQUEUE_MAY: + break; + case ELV_MQUEUE_MUST: + goto get_rq; + } + + if (blk_queue_full(q, rw) && !ioc_batching(q, ioc)) { /* * The queue is full and the allocating process is not a * "batcher", and not exempted by the IO scheduler @@ -1685,12 +1666,13 @@ static struct request *get_request(request_queue_t *q, int rw, int gfp_mask) goto out; } +get_rq: rl->count[rw]++; if (rl->count[rw] >= queue_congestion_on_threshold(q)) set_queue_congested(q, rw); spin_unlock_irq(q->queue_lock); - rq = blk_alloc_request(q, gfp_mask); + rq = blk_alloc_request(q, rw, gfp_mask); if (!rq) { /* * Allocation failed presumably due to memory. Undo anything @@ -1705,17 +1687,11 @@ static struct request *get_request(request_queue_t *q, int rw, int gfp_mask) goto out; } - if (ioc_batching(ioc)) + if (ioc_batching(q, ioc)) ioc->nr_batch_requests--; INIT_LIST_HEAD(&rq->queuelist); - /* - * first three bits are identical in rq->flags and bio->bi_rw, - * see bio.h and blkdev.h - */ - rq->flags = rw; - rq->errors = 0; rq->rq_status = RQ_ACTIVE; rq->bio = rq->biotail = NULL; @@ -1764,7 +1740,7 @@ static struct request *get_request_wait(request_queue_t *q, int rw) * See ioc_batching, ioc_set_batching */ ioc = get_io_context(GFP_NOIO); - ioc_set_batching(ioc); + ioc_set_batching(q, ioc); put_io_context(ioc); } finish_wait(&rl->wait[rw], &wait); @@ -2506,6 +2482,70 @@ static inline void blk_partition_remap(struct bio *bio) } } +void blk_finish_queue_drain(request_queue_t *q) +{ + struct request_list *rl = &q->rq; + + clear_bit(QUEUE_FLAG_DRAIN, &q->queue_flags); + wake_up(&rl->wait[0]); + wake_up(&rl->wait[1]); + wake_up(&rl->drain); +} + +/* + * We rely on the fact that only requests allocated through blk_alloc_request() + * have io scheduler private data structures associated with them. Any other + * type of request (allocated on stack or through kmalloc()) should not go + * to the io scheduler core, but be attached to the queue head instead. + */ +void blk_wait_queue_drained(request_queue_t *q) +{ + struct request_list *rl = &q->rq; + DEFINE_WAIT(wait); + + spin_lock_irq(q->queue_lock); + set_bit(QUEUE_FLAG_DRAIN, &q->queue_flags); + + while (rl->count[READ] || rl->count[WRITE]) { + prepare_to_wait(&rl->drain, &wait, TASK_UNINTERRUPTIBLE); + + if (rl->count[READ] || rl->count[WRITE]) { + __generic_unplug_device(q); + spin_unlock_irq(q->queue_lock); + io_schedule(); + spin_lock_irq(q->queue_lock); + } + + finish_wait(&rl->drain, &wait); + } + + spin_unlock_irq(q->queue_lock); +} + +/* + * block waiting for the io scheduler being started again. + */ +static inline void block_wait_queue_running(request_queue_t *q) +{ + DEFINE_WAIT(wait); + + while (test_bit(QUEUE_FLAG_DRAIN, &q->queue_flags)) { + struct request_list *rl = &q->rq; + + prepare_to_wait_exclusive(&rl->drain, &wait, + TASK_UNINTERRUPTIBLE); + + /* + * re-check the condition. avoids using prepare_to_wait() + * in the fast path (queue is running) + */ + if (test_bit(QUEUE_FLAG_DRAIN, &q->queue_flags)) + io_schedule(); + + finish_wait(&rl->drain, &wait); + } +} + /** * generic_make_request: hand a buffer to its device driver for I/O * @bio: The bio describing the location in memory and on the device. @@ -2595,6 +2635,8 @@ end_io: if (test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)) goto end_io; + block_wait_queue_running(q); + /* * If this device has partitions, remap block n * of partition p to block n+start(p) of the disk. @@ -3018,6 +3060,7 @@ void kblockd_flush(void) { flush_workqueue(kblockd_workqueue); } +EXPORT_SYMBOL(kblockd_flush); int __init blk_dev_init(void) { @@ -3036,6 +3079,7 @@ int __init blk_dev_init(void) blk_max_low_pfn = max_low_pfn; blk_max_pfn = max_pfn; + return 0; } @@ -3052,9 +3096,13 @@ void put_io_context(struct io_context *ioc) if (atomic_dec_and_test(&ioc->refcount)) { if (ioc->aic && ioc->aic->dtor) ioc->aic->dtor(ioc->aic); + if (ioc->cic && ioc->cic->dtor) + ioc->cic->dtor(ioc->cic); + kmem_cache_free(iocontext_cachep, ioc); } } +EXPORT_SYMBOL(put_io_context); /* Called by the exitting task */ void exit_io_context(void) @@ -3064,14 +3112,15 @@ void exit_io_context(void) local_irq_save(flags); ioc = current->io_context; - if (ioc) { - if (ioc->aic && ioc->aic->exit) - ioc->aic->exit(ioc->aic); - put_io_context(ioc); - current->io_context = NULL; - } else - WARN_ON(1); + current->io_context = NULL; local_irq_restore(flags); + + if (ioc->aic && ioc->aic->exit) + ioc->aic->exit(ioc->aic); + if (ioc->cic && ioc->cic->exit) + ioc->cic->exit(ioc->cic); + + put_io_context(ioc); } /* @@ -3090,22 +3139,42 @@ struct io_context *get_io_context(int gfp_flags) local_irq_save(flags); ret = tsk->io_context; - if (ret == NULL) { - ret = kmem_cache_alloc(iocontext_cachep, GFP_ATOMIC); - if (ret) { - atomic_set(&ret->refcount, 1); - ret->pid = tsk->pid; - ret->last_waited = jiffies; /* doesn't matter... */ - ret->nr_batch_requests = 0; /* because this is 0 */ - ret->aic = NULL; + if (ret) + goto out; + + local_irq_restore(flags); + + ret = kmem_cache_alloc(iocontext_cachep, gfp_flags); + if (ret) { + atomic_set(&ret->refcount, 1); + ret->pid = tsk->pid; + 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); + + /* + * very unlikely, someone raced with us in setting up the task + * io context. free new context and just grab a reference. + */ + if (!tsk->io_context) tsk->io_context = ret; + else { + kmem_cache_free(iocontext_cachep, ret); + ret = tsk->io_context; } - } - if (ret) + +out: atomic_inc(&ret->refcount); - local_irq_restore(flags); + local_irq_restore(flags); + } + return ret; } +EXPORT_SYMBOL(get_io_context); void copy_io_context(struct io_context **pdst, struct io_context **psrc) { @@ -3119,6 +3188,7 @@ void copy_io_context(struct io_context **pdst, struct io_context **psrc) *pdst = src; } } +EXPORT_SYMBOL(copy_io_context); void swap_io_context(struct io_context **ioc1, struct io_context **ioc2) { @@ -3127,7 +3197,7 @@ void swap_io_context(struct io_context **ioc1, struct io_context **ioc2) *ioc1 = *ioc2; *ioc2 = temp; } - +EXPORT_SYMBOL(swap_io_context); /* * sysfs parts below @@ -3285,11 +3355,18 @@ static struct queue_sysfs_entry queue_max_hw_sectors_entry = { .show = queue_max_hw_sectors_show, }; +static struct queue_sysfs_entry queue_iosched_entry = { + .attr = {.name = "scheduler", .mode = S_IRUGO | S_IWUSR }, + .show = elv_iosched_show, + .store = elv_iosched_store, +}; + static struct attribute *default_attrs[] = { &queue_requests_entry.attr, &queue_ra_entry.attr, &queue_max_hw_sectors_entry.attr, &queue_max_sectors_entry.attr, + &queue_iosched_entry.attr, NULL, }; diff --git a/drivers/block/noop-iosched.c b/drivers/block/noop-iosched.c index ffef40be1f92..707dddd7d881 100644 --- a/drivers/block/noop-iosched.c +++ b/drivers/block/noop-iosched.c @@ -83,12 +83,31 @@ struct request *elevator_noop_next_request(request_queue_t *q) return NULL; } -elevator_t elevator_noop = { - .elevator_merge_fn = elevator_noop_merge, - .elevator_merge_req_fn = elevator_noop_merge_requests, - .elevator_next_req_fn = elevator_noop_next_request, - .elevator_add_req_fn = elevator_noop_add_request, - .elevator_name = "noop", +static struct elevator_type elevator_noop = { + .ops = { + .elevator_merge_fn = elevator_noop_merge, + .elevator_merge_req_fn = elevator_noop_merge_requests, + .elevator_next_req_fn = elevator_noop_next_request, + .elevator_add_req_fn = elevator_noop_add_request, + }, + .elevator_name = "noop", + .elevator_owner = THIS_MODULE, }; -EXPORT_SYMBOL(elevator_noop); +int noop_init(void) +{ + return elv_register(&elevator_noop); +} + +void noop_exit(void) +{ + elv_unregister(&elevator_noop); +} + +module_init(noop_init); +module_exit(noop_exit); + + +MODULE_AUTHOR("Jens Axboe"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("No-op IO scheduler"); diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c new file mode 100644 index 000000000000..fb80b6a91f84 --- /dev/null +++ b/drivers/block/pktcdvd.c @@ -0,0 +1,2679 @@ +/* + * Copyright (C) 2000 Jens Axboe <axboe@suse.de> + * Copyright (C) 2001-2004 Peter Osterlund <petero2@telia.com> + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * Packet writing layer for ATAPI and SCSI CD-R, CD-RW, DVD-R, and + * DVD-RW devices (aka an exercise in block layer masturbation) + * + * + * TODO: (circa order of when I will fix it) + * - Only able to write on CD-RW media right now. + * - check host application code on media and set it in write page + * - interface for UDF <-> packet to negotiate a new location when a write + * fails. + * - handle OPC, especially for -RW media + * + * Theory of operation: + * + * We use a custom make_request_fn function that forwards reads directly to + * the underlying CD device. Write requests are either attached directly to + * a live packet_data object, or simply stored sequentially in a list for + * later processing by the kcdrwd kernel thread. This driver doesn't use + * any elevator functionally as defined by the elevator_s struct, but the + * underlying CD device uses a standard elevator. + * + * This strategy makes it possible to do very late merging of IO requests. + * A new bio sent to pkt_make_request can be merged with a live packet_data + * object even if the object is in the data gathering state. + * + *************************************************************************/ + +#define VERSION_CODE "v0.2.0a 2004-07-14 Jens Axboe (axboe@suse.de) and petero2@telia.com" + +#include <linux/pktcdvd.h> +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/errno.h> +#include <linux/spinlock.h> +#include <linux/file.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/miscdevice.h> +#include <linux/suspend.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_ioctl.h> + +#include <asm/uaccess.h> + +#if PACKET_DEBUG +#define DPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args) +#else +#define DPRINTK(fmt, args...) +#endif + +#if PACKET_DEBUG > 1 +#define VPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args) +#else +#define VPRINTK(fmt, args...) +#endif + +#define MAX_SPEED 0xffff + +#define ZONE(sector, pd) (((sector) + (pd)->offset) & ~((pd)->settings.size - 1)) + +static struct pktcdvd_device *pkt_devs[MAX_WRITERS]; +static struct proc_dir_entry *pkt_proc; +static int pkt_major; +static struct semaphore ctl_mutex; /* Serialize open/close/setup/teardown */ +static mempool_t *psd_pool; + + +static void pkt_bio_finished(struct pktcdvd_device *pd) +{ + BUG_ON(atomic_read(&pd->cdrw.pending_bios) <= 0); + if (atomic_dec_and_test(&pd->cdrw.pending_bios)) { + VPRINTK("pktcdvd: queue empty\n"); + atomic_set(&pd->iosched.attention, 1); + wake_up(&pd->wqueue); + } +} + +static void pkt_bio_destructor(struct bio *bio) +{ + kfree(bio->bi_io_vec); + kfree(bio); +} + +static struct bio *pkt_bio_alloc(int nr_iovecs) +{ + struct bio_vec *bvl = NULL; + struct bio *bio; + + bio = kmalloc(sizeof(struct bio), GFP_KERNEL); + if (!bio) + goto no_bio; + bio_init(bio); + + bvl = kmalloc(nr_iovecs * sizeof(struct bio_vec), GFP_KERNEL); + if (!bvl) + goto no_bvl; + memset(bvl, 0, nr_iovecs * sizeof(struct bio_vec)); + + bio->bi_max_vecs = nr_iovecs; + bio->bi_io_vec = bvl; + bio->bi_destructor = pkt_bio_destructor; + + return bio; + + no_bvl: + kfree(bio); + no_bio: + return NULL; +} + +/* + * Allocate a packet_data struct + */ +static struct packet_data *pkt_alloc_packet_data(void) +{ + int i; + struct packet_data *pkt; + + pkt = kmalloc(sizeof(struct packet_data), GFP_KERNEL); + if (!pkt) + goto no_pkt; + memset(pkt, 0, sizeof(struct packet_data)); + + pkt->w_bio = pkt_bio_alloc(PACKET_MAX_SIZE); + if (!pkt->w_bio) + goto no_bio; + + for (i = 0; i < PAGES_PER_PACKET; i++) { + pkt->pages[i] = alloc_page(GFP_KERNEL); + if (!pkt->pages[i]) + goto no_page; + } + for (i = 0; i < PAGES_PER_PACKET; i++) + clear_page(page_address(pkt->pages[i])); + + spin_lock_init(&pkt->lock); + + for (i = 0; i < PACKET_MAX_SIZE; i++) { + struct bio *bio = pkt_bio_alloc(1); + if (!bio) + goto no_rd_bio; + pkt->r_bios[i] = bio; + } + + return pkt; + +no_rd_bio: + for (i = 0; i < PACKET_MAX_SIZE; i++) { + struct bio *bio = pkt->r_bios[i]; + if (bio) + bio_put(bio); + } + +no_page: + for (i = 0; i < PAGES_PER_PACKET; i++) + if (pkt->pages[i]) + __free_page(pkt->pages[i]); + bio_put(pkt->w_bio); +no_bio: + kfree(pkt); +no_pkt: + return NULL; +} + +/* + * Free a packet_data struct + */ +static void pkt_free_packet_data(struct packet_data *pkt) +{ + int i; + + for (i = 0; i < PACKET_MAX_SIZE; i++) { + struct bio *bio = pkt->r_bios[i]; + if (bio) + bio_put(bio); + } + for (i = 0; i < PAGES_PER_PACKET; i++) + __free_page(pkt->pages[i]); + bio_put(pkt->w_bio); + kfree(pkt); +} + +static void pkt_shrink_pktlist(struct pktcdvd_device *pd) +{ + struct packet_data *pkt, *next; + + BUG_ON(!list_empty(&pd->cdrw.pkt_active_list)); + + list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_free_list, list) { + pkt_free_packet_data(pkt); + } +} + +static int pkt_grow_pktlist(struct pktcdvd_device *pd, int nr_packets) +{ + struct packet_data *pkt; + + INIT_LIST_HEAD(&pd->cdrw.pkt_free_list); + INIT_LIST_HEAD(&pd->cdrw.pkt_active_list); + spin_lock_init(&pd->cdrw.active_list_lock); + while (nr_packets > 0) { + pkt = pkt_alloc_packet_data(); + if (!pkt) { + pkt_shrink_pktlist(pd); + return 0; + } + pkt->id = nr_packets; + pkt->pd = pd; + list_add(&pkt->list, &pd->cdrw.pkt_free_list); + nr_packets--; + } + return 1; +} + +static void *pkt_rb_alloc(int gfp_mask, void *data) +{ + return kmalloc(sizeof(struct pkt_rb_node), gfp_mask); +} + +static void pkt_rb_free(void *ptr, void *data) +{ + kfree(ptr); +} + +static inline struct pkt_rb_node *pkt_rbtree_next(struct pkt_rb_node *node) +{ + struct rb_node *n = rb_next(&node->rb_node); + if (!n) + return NULL; + return rb_entry(n, struct pkt_rb_node, rb_node); +} + +static inline void pkt_rbtree_erase(struct pktcdvd_device *pd, struct pkt_rb_node *node) +{ + rb_erase(&node->rb_node, &pd->bio_queue); + mempool_free(node, pd->rb_pool); + pd->bio_queue_size--; + BUG_ON(pd->bio_queue_size < 0); +} + +/* + * Find the first node in the pd->bio_queue rb tree with a starting sector >= s. + */ +static struct pkt_rb_node *pkt_rbtree_find(struct pktcdvd_device *pd, sector_t s) +{ + struct rb_node *n = pd->bio_queue.rb_node; + struct rb_node *next; + struct pkt_rb_node *tmp; + + if (!n) { + BUG_ON(pd->bio_queue_size > 0); + return NULL; + } + + for (;;) { + tmp = rb_entry(n, struct pkt_rb_node, rb_node); + if (s <= tmp->bio->bi_sector) + next = n->rb_left; + else + next = n->rb_right; + if (!next) + break; + n = next; + } + + if (s > tmp->bio->bi_sector) { + tmp = pkt_rbtree_next(tmp); + if (!tmp) + return NULL; + } + BUG_ON(s > tmp->bio->bi_sector); + return tmp; +} + +/* + * Insert a node into the pd->bio_queue rb tree. + */ +static void pkt_rbtree_insert(struct pktcdvd_device *pd, struct pkt_rb_node *node) +{ + struct rb_node **p = &pd->bio_queue.rb_node; + struct rb_node *parent = NULL; + sector_t s = node->bio->bi_sector; + struct pkt_rb_node *tmp; + + while (*p) { + parent = *p; + tmp = rb_entry(parent, struct pkt_rb_node, rb_node); + if (s < tmp->bio->bi_sector) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&node->rb_node, parent, p); + rb_insert_color(&node->rb_node, &pd->bio_queue); + pd->bio_queue_size++; +} + +/* + * Add a bio to a single linked list defined by its head and tail pointers. + */ +static inline void pkt_add_list_last(struct bio *bio, struct bio **list_head, struct bio **list_tail) +{ + bio->bi_next = NULL; + if (*list_tail) { + BUG_ON((*list_head) == NULL); + (*list_tail)->bi_next = bio; + (*list_tail) = bio; + } else { + BUG_ON((*list_head) != NULL); + (*list_head) = bio; + (*list_tail) = bio; + } +} + +/* + * Remove and return the first bio from a single linked list defined by its + * head and tail pointers. + */ +static inline struct bio *pkt_get_list_first(struct bio **list_head, struct bio **list_tail) +{ + struct bio *bio; + + if (*list_head == NULL) + return NULL; + + bio = *list_head; + *list_head = bio->bi_next; + if (*list_head == NULL) + *list_tail = NULL; + + bio->bi_next = NULL; + return bio; +} + +/* + * Send a packet_command to the underlying block device and + * wait for completion. + */ +static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command *cgc) +{ + char sense[SCSI_SENSE_BUFFERSIZE]; + request_queue_t *q; + struct request *rq; + DECLARE_COMPLETION(wait); + int err = 0; + + q = bdev_get_queue(pd->bdev); + + rq = blk_get_request(q, (cgc->data_direction == CGC_DATA_WRITE) ? WRITE : READ, + __GFP_WAIT); + rq->errors = 0; + rq->rq_disk = pd->bdev->bd_disk; + rq->bio = NULL; + rq->buffer = NULL; + rq->timeout = 60*HZ; + rq->data = cgc->buffer; + rq->data_len = cgc->buflen; + rq->sense = sense; + memset(sense, 0, sizeof(sense)); + rq->sense_len = 0; + rq->flags |= REQ_BLOCK_PC | REQ_HARDBARRIER; + if (cgc->quiet) + rq->flags |= REQ_QUIET; + memcpy(rq->cmd, cgc->cmd, CDROM_PACKET_SIZE); + if (sizeof(rq->cmd) > CDROM_PACKET_SIZE) + memset(rq->cmd + CDROM_PACKET_SIZE, 0, sizeof(rq->cmd) - CDROM_PACKET_SIZE); + + rq->ref_count++; + rq->flags |= REQ_NOMERGE; + rq->waiting = &wait; + elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1); + generic_unplug_device(q); + wait_for_completion(&wait); + + if (rq->errors) + err = -EIO; + + blk_put_request(rq); + return err; +} + +/* + * A generic sense dump / resolve mechanism should be implemented across + * all ATAPI + SCSI devices. + */ +static void pkt_dump_sense(struct packet_command *cgc) +{ + static char *info[9] = { "No sense", "Recovered error", "Not ready", + "Medium error", "Hardware error", "Illegal request", + "Unit attention", "Data protect", "Blank check" }; + int i; + struct request_sense *sense = cgc->sense; + + printk("pktcdvd:"); + for (i = 0; i < CDROM_PACKET_SIZE; i++) + printk(" %02x", cgc->cmd[i]); + printk(" - "); + + if (sense == NULL) { + printk("no sense\n"); + return; + } + + printk("sense %02x.%02x.%02x", sense->sense_key, sense->asc, sense->ascq); + + if (sense->sense_key > 8) { + printk(" (INVALID)\n"); + return; + } + + printk(" (%s)\n", info[sense->sense_key]); +} + +/* + * flush the drive cache to media + */ +static int pkt_flush_cache(struct pktcdvd_device *pd) +{ + struct packet_command cgc; + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.cmd[0] = GPCMD_FLUSH_CACHE; + cgc.quiet = 1; + + /* + * the IMMED bit -- we default to not setting it, although that + * would allow a much faster close, this is safer + */ +#if 0 + cgc.cmd[1] = 1 << 1; +#endif + return pkt_generic_packet(pd, &cgc); +} + +/* + * speed is given as the normal factor, e.g. 4 for 4x + */ +static int pkt_set_speed(struct pktcdvd_device *pd, unsigned write_speed, unsigned read_speed) +{ + struct packet_command cgc; + struct request_sense sense; + int ret; + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.sense = &sense; + cgc.cmd[0] = GPCMD_SET_SPEED; + cgc.cmd[2] = (read_speed >> 8) & 0xff; + cgc.cmd[3] = read_speed & 0xff; + cgc.cmd[4] = (write_speed >> 8) & 0xff; + cgc.cmd[5] = write_speed & 0xff; + + if ((ret = pkt_generic_packet(pd, &cgc))) + pkt_dump_sense(&cgc); + + return ret; +} + +/* + * Queue a bio for processing by the low-level CD device. Must be called + * from process context. + */ +static void pkt_queue_bio(struct pktcdvd_device *pd, struct bio *bio, int high_prio_read) +{ + spin_lock(&pd->iosched.lock); + if (bio_data_dir(bio) == READ) { + pkt_add_list_last(bio, &pd->iosched.read_queue, + &pd->iosched.read_queue_tail); + if (high_prio_read) + pd->iosched.high_prio_read = 1; + } else { + pkt_add_list_last(bio, &pd->iosched.write_queue, + &pd->iosched.write_queue_tail); + } + spin_unlock(&pd->iosched.lock); + + atomic_set(&pd->iosched.attention, 1); + wake_up(&pd->wqueue); +} + +/* + * Process the queued read/write requests. This function handles special + * requirements for CDRW drives: + * - A cache flush command must be inserted before a read request if the + * previous request was a write. + * - Switching between reading and writing is slow, so don't it more often + * than necessary. + * - Set the read speed according to current usage pattern. When only reading + * from the device, it's best to use the highest possible read speed, but + * when switching often between reading and writing, it's better to have the + * same read and write speeds. + * - Reads originating from user space should have higher priority than reads + * originating from pkt_gather_data, because some process is usually waiting + * on reads of the first kind. + */ +static void pkt_iosched_process_queue(struct pktcdvd_device *pd) +{ + request_queue_t *q; + + if (atomic_read(&pd->iosched.attention) == 0) + return; + atomic_set(&pd->iosched.attention, 0); + + q = bdev_get_queue(pd->bdev); + + for (;;) { + struct bio *bio; + int reads_queued, writes_queued, high_prio_read; + + spin_lock(&pd->iosched.lock); + reads_queued = (pd->iosched.read_queue != NULL); + writes_queued = (pd->iosched.write_queue != NULL); + if (!reads_queued) + pd->iosched.high_prio_read = 0; + high_prio_read = pd->iosched.high_prio_read; + spin_unlock(&pd->iosched.lock); + + if (!reads_queued && !writes_queued) + break; + + if (pd->iosched.writing) { + if (high_prio_read || (!writes_queued && reads_queued)) { + if (atomic_read(&pd->cdrw.pending_bios) > 0) { + VPRINTK("pktcdvd: write, waiting\n"); + break; + } + pkt_flush_cache(pd); + pd->iosched.writing = 0; + } + } else { + if (!reads_queued && writes_queued) { + if (atomic_read(&pd->cdrw.pending_bios) > 0) { + VPRINTK("pktcdvd: read, waiting\n"); + break; + } + pd->iosched.writing = 1; + } + } + + spin_lock(&pd->iosched.lock); + if (pd->iosched.writing) { + bio = pkt_get_list_first(&pd->iosched.write_queue, + &pd->iosched.write_queue_tail); + } else { + bio = pkt_get_list_first(&pd->iosched.read_queue, + &pd->iosched.read_queue_tail); + } + spin_unlock(&pd->iosched.lock); + + if (!bio) + continue; + + if (bio_data_dir(bio) == READ) + pd->iosched.successive_reads += bio->bi_size >> 10; + else + pd->iosched.successive_reads = 0; + if (pd->iosched.successive_reads >= HI_SPEED_SWITCH) { + if (pd->read_speed == pd->write_speed) { + pd->read_speed = MAX_SPEED; + pkt_set_speed(pd, pd->write_speed, pd->read_speed); + } + } else { + if (pd->read_speed != pd->write_speed) { + pd->read_speed = pd->write_speed; + pkt_set_speed(pd, pd->write_speed, pd->read_speed); + } + } + + atomic_inc(&pd->cdrw.pending_bios); + generic_make_request(bio); + } +} + +/* + * Special care is needed if the underlying block device has a small + * max_phys_segments value. + */ +static int pkt_set_segment_merging(struct pktcdvd_device *pd, request_queue_t *q) +{ + if ((pd->settings.size << 9) / CD_FRAMESIZE <= q->max_phys_segments) { + /* + * The cdrom device can handle one segment/frame + */ + clear_bit(PACKET_MERGE_SEGS, &pd->flags); + return 0; + } else if ((pd->settings.size << 9) / PAGE_SIZE <= q->max_phys_segments) { + /* + * We can handle this case at the expense of some extra memory + * copies during write operations + */ + set_bit(PACKET_MERGE_SEGS, &pd->flags); + return 0; + } else { + printk("pktcdvd: cdrom max_phys_segments too small\n"); + return -EIO; + } +} + +/* + * Copy CD_FRAMESIZE bytes from src_bio into a destination page + */ +static void pkt_copy_bio_data(struct bio *src_bio, int seg, int offs, + struct page *dst_page, int dst_offs) +{ + unsigned int copy_size = CD_FRAMESIZE; + + while (copy_size > 0) { + struct bio_vec *src_bvl = bio_iovec_idx(src_bio, seg); + void *vfrom = kmap_atomic(src_bvl->bv_page, KM_USER0) + + src_bvl->bv_offset + offs; + void *vto = page_address(dst_page) + dst_offs; + int len = min_t(int, copy_size, src_bvl->bv_len - offs); + + BUG_ON(len < 0); + memcpy(vto, vfrom, len); + kunmap_atomic(src_bvl->bv_page, KM_USER0); + + seg++; + offs = 0; + dst_offs += len; + copy_size -= len; + } +} + +/* + * Copy all data for this packet to pkt->pages[], so that + * a) The number of required segments for the write bio is minimized, which + * is necessary for some scsi controllers. + * b) The data can be used as cache to avoid read requests if we receive a + * new write request for the same zone. + */ +static void pkt_make_local_copy(struct packet_data *pkt, struct page **pages, int *offsets) +{ + int f, p, offs; + + /* Copy all data to pkt->pages[] */ + p = 0; + offs = 0; + for (f = 0; f < pkt->frames; f++) { + if (pages[f] != pkt->pages[p]) { + void *vfrom = kmap_atomic(pages[f], KM_USER0) + offsets[f]; + void *vto = page_address(pkt->pages[p]) + offs; + memcpy(vto, vfrom, CD_FRAMESIZE); + kunmap_atomic(pages[f], KM_USER0); + pages[f] = pkt->pages[p]; + offsets[f] = offs; + } else { + BUG_ON(offsets[f] != offs); + } + offs += CD_FRAMESIZE; + if (offs >= PAGE_SIZE) { + BUG_ON(offs > PAGE_SIZE); + offs = 0; + p++; + } + } +} + +static int pkt_end_io_read(struct bio *bio, unsigned int bytes_done, int err) +{ + struct packet_data *pkt = bio->bi_private; + struct pktcdvd_device *pd = pkt->pd; + BUG_ON(!pd); + + if (bio->bi_size) + return 1; + + VPRINTK("pkt_end_io_read: bio=%p sec0=%llx sec=%llx err=%d\n", bio, + (unsigned long long)pkt->sector, (unsigned long long)bio->bi_sector, err); + + if (err) + atomic_inc(&pkt->io_errors); + if (atomic_dec_and_test(&pkt->io_wait)) { + atomic_inc(&pkt->run_sm); + wake_up(&pd->wqueue); + } + pkt_bio_finished(pd); + + return 0; +} + +static int pkt_end_io_packet_write(struct bio *bio, unsigned int bytes_done, int err) +{ + struct packet_data *pkt = bio->bi_private; + struct pktcdvd_device *pd = pkt->pd; + BUG_ON(!pd); + + if (bio->bi_size) + return 1; + + VPRINTK("pkt_end_io_packet_write: id=%d, err=%d\n", pkt->id, err); + + pd->stats.pkt_ended++; + + pkt_bio_finished(pd); + atomic_dec(&pkt->io_wait); + atomic_inc(&pkt->run_sm); + wake_up(&pd->wqueue); + return 0; +} + +/* + * Schedule reads for the holes in a packet + */ +static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt) +{ + int frames_read = 0; + struct bio *bio; + int f; + char written[PACKET_MAX_SIZE]; + + BUG_ON(!pkt->orig_bios); + + atomic_set(&pkt->io_wait, 0); + atomic_set(&pkt->io_errors, 0); + + if (pkt->cache_valid) { + VPRINTK("pkt_gather_data: zone %llx cached\n", + (unsigned long long)pkt->sector); + goto out_account; + } + + /* + * Figure out which frames we need to read before we can write. + */ + memset(written, 0, sizeof(written)); + spin_lock(&pkt->lock); + for (bio = pkt->orig_bios; bio; bio = bio->bi_next) { + int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9); + int num_frames = bio->bi_size / CD_FRAMESIZE; + BUG_ON(first_frame < 0); + BUG_ON(first_frame + num_frames > pkt->frames); + for (f = first_frame; f < first_frame + num_frames; f++) + written[f] = 1; + } + spin_unlock(&pkt->lock); + + /* + * Schedule reads for missing parts of the packet. + */ + for (f = 0; f < pkt->frames; f++) { + int p, offset; + if (written[f]) + continue; + bio = pkt->r_bios[f]; + bio_init(bio); + bio->bi_max_vecs = 1; + bio->bi_sector = pkt->sector + f * (CD_FRAMESIZE >> 9); + bio->bi_bdev = pd->bdev; + bio->bi_end_io = pkt_end_io_read; + bio->bi_private = pkt; + + p = (f * CD_FRAMESIZE) / PAGE_SIZE; + offset = (f * CD_FRAMESIZE) % PAGE_SIZE; + VPRINTK("pkt_gather_data: Adding frame %d, page:%p offs:%d\n", + f, pkt->pages[p], offset); + if (!bio_add_page(bio, pkt->pages[p], CD_FRAMESIZE, offset)) + BUG(); + + atomic_inc(&pkt->io_wait); + bio->bi_rw = READ; + pkt_queue_bio(pd, bio, 0); + frames_read++; + } + +out_account: + VPRINTK("pkt_gather_data: need %d frames for zone %llx\n", + frames_read, (unsigned long long)pkt->sector); + pd->stats.pkt_started++; + pd->stats.secs_rg += frames_read * (CD_FRAMESIZE >> 9); + pd->stats.secs_w += pd->settings.size; +} + +/* + * Find a packet matching zone, or the least recently used packet if + * there is no match. + */ +static struct packet_data *pkt_get_packet_data(struct pktcdvd_device *pd, int zone) +{ + struct packet_data *pkt; + + list_for_each_entry(pkt, &pd->cdrw.pkt_free_list, list) { + if (pkt->sector == zone || pkt->list.next == &pd->cdrw.pkt_free_list) { + list_del_init(&pkt->list); + if (pkt->sector != zone) + pkt->cache_valid = 0; + break; + } + } + return pkt; +} + +static void pkt_put_packet_data(struct pktcdvd_device *pd, struct packet_data *pkt) +{ + if (pkt->cache_valid) { + list_add(&pkt->list, &pd->cdrw.pkt_free_list); + } else { + list_add_tail(&pkt->list, &pd->cdrw.pkt_free_list); + } +} + +/* + * recover a failed write, query for relocation if possible + * + * returns 1 if recovery is possible, or 0 if not + * + */ +static int pkt_start_recovery(struct packet_data *pkt) +{ + /* + * FIXME. We need help from the file system to implement + * recovery handling. + */ + return 0; +#if 0 + struct request *rq = pkt->rq; + struct pktcdvd_device *pd = rq->rq_disk->private_data; + struct block_device *pkt_bdev; + struct super_block *sb = NULL; + unsigned long old_block, new_block; + sector_t new_sector; + + pkt_bdev = bdget(kdev_t_to_nr(pd->pkt_dev)); + if (pkt_bdev) { + sb = get_super(pkt_bdev); + bdput(pkt_bdev); + } + + if (!sb) + return 0; + + if (!sb->s_op || !sb->s_op->relocate_blocks) + goto out; + + old_block = pkt->sector / (CD_FRAMESIZE >> 9); + if (sb->s_op->relocate_blocks(sb, old_block, &new_block)) + goto out; + + new_sector = new_block * (CD_FRAMESIZE >> 9); + pkt->sector = new_sector; + + pkt->bio->bi_sector = new_sector; + pkt->bio->bi_next = NULL; + pkt->bio->bi_flags = 1 << BIO_UPTODATE; + pkt->bio->bi_idx = 0; + + BUG_ON(pkt->bio->bi_rw != (1 << BIO_RW)); + BUG_ON(pkt->bio->bi_vcnt != pkt->frames); + BUG_ON(pkt->bio->bi_size != pkt->frames * CD_FRAMESIZE); + BUG_ON(pkt->bio->bi_end_io != pkt_end_io_packet_write); + BUG_ON(pkt->bio->bi_private != pkt); + + drop_super(sb); + return 1; + +out: + drop_super(sb); + return 0; +#endif +} + +static inline void pkt_set_state(struct packet_data *pkt, enum packet_data_state state) +{ +#if PACKET_DEBUG > 1 + static const char *state_name[] = { + "IDLE", "WAITING", "READ_WAIT", "WRITE_WAIT", "RECOVERY", "FINISHED" + }; + enum packet_data_state old_state = pkt->state; + VPRINTK("pkt %2d : s=%6llx %s -> %s\n", pkt->id, (unsigned long long)pkt->sector, + state_name[old_state], state_name[state]); +#endif + pkt->state = state; +} + +/* + * Scan the work queue to see if we can start a new packet. + * returns non-zero if any work was done. + */ +static int pkt_handle_queue(struct pktcdvd_device *pd) +{ + struct packet_data *pkt, *p; + struct bio *bio = NULL; + sector_t zone = 0; /* Suppress gcc warning */ + struct pkt_rb_node *node, *first_node; + struct rb_node *n; + + VPRINTK("handle_queue\n"); + + atomic_set(&pd->scan_queue, 0); + + if (list_empty(&pd->cdrw.pkt_free_list)) { + VPRINTK("handle_queue: no pkt\n"); + return 0; + } + + /* + * Try to find a zone we are not already working on. + */ + spin_lock(&pd->lock); + first_node = pkt_rbtree_find(pd, pd->current_sector); + if (!first_node) { + n = rb_first(&pd->bio_queue); + if (n) + first_node = rb_entry(n, struct pkt_rb_node, rb_node); + } + node = first_node; + while (node) { + bio = node->bio; + zone = ZONE(bio->bi_sector, pd); + list_for_each_entry(p, &pd->cdrw.pkt_active_list, list) { + if (p->sector == zone) + goto try_next_bio; + } + break; +try_next_bio: + node = pkt_rbtree_next(node); + if (!node) { + n = rb_first(&pd->bio_queue); + if (n) + node = rb_entry(n, struct pkt_rb_node, rb_node); + } + if (node == first_node) + node = NULL; + } + spin_unlock(&pd->lock); + if (!bio) { + VPRINTK("handle_queue: no bio\n"); + return 0; + } + + pkt = pkt_get_packet_data(pd, zone); + BUG_ON(!pkt); + + pd->current_sector = zone + pd->settings.size; + pkt->sector = zone; + pkt->frames = pd->settings.size >> 2; + BUG_ON(pkt->frames > PACKET_MAX_SIZE); + pkt->write_size = 0; + + /* + * Scan work queue for bios in the same zone and link them + * to this packet. + */ + spin_lock(&pd->lock); + VPRINTK("pkt_handle_queue: looking for zone %llx\n", (unsigned long long)zone); + while ((node = pkt_rbtree_find(pd, zone)) != NULL) { + bio = node->bio; + VPRINTK("pkt_handle_queue: found zone=%llx\n", + (unsigned long long)ZONE(bio->bi_sector, pd)); + if (ZONE(bio->bi_sector, pd) != zone) + break; + pkt_rbtree_erase(pd, node); + spin_lock(&pkt->lock); + pkt_add_list_last(bio, &pkt->orig_bios, &pkt->orig_bios_tail); + pkt->write_size += bio->bi_size / CD_FRAMESIZE; + spin_unlock(&pkt->lock); + } + spin_unlock(&pd->lock); + + pkt->sleep_time = max(PACKET_WAIT_TIME, 1); + pkt_set_state(pkt, PACKET_WAITING_STATE); + atomic_set(&pkt->run_sm, 1); + + spin_lock(&pd->cdrw.active_list_lock); + list_add(&pkt->list, &pd->cdrw.pkt_active_list); + spin_unlock(&pd->cdrw.active_list_lock); + + return 1; +} + +/* + * Assemble a bio to write one packet and queue the bio for processing + * by the underlying block device. + */ +static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt) +{ + struct bio *bio; + struct page *pages[PACKET_MAX_SIZE]; + int offsets[PACKET_MAX_SIZE]; + int f; + int frames_write; + + for (f = 0; f < pkt->frames; f++) { + pages[f] = pkt->pages[(f * CD_FRAMESIZE) / PAGE_SIZE]; + offsets[f] = (f * CD_FRAMESIZE) % PAGE_SIZE; + } + + /* + * Fill-in pages[] and offsets[] with data from orig_bios. + */ + frames_write = 0; + spin_lock(&pkt->lock); + for (bio = pkt->orig_bios; bio; bio = bio->bi_next) { + int segment = bio->bi_idx; + int src_offs = 0; + int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9); + int num_frames = bio->bi_size / CD_FRAMESIZE; + BUG_ON(first_frame < 0); + BUG_ON(first_frame + num_frames > pkt->frames); + for (f = first_frame; f < first_frame + num_frames; f++) { + struct bio_vec *src_bvl = bio_iovec_idx(bio, segment); + + while (src_offs >= src_bvl->bv_len) { + src_offs -= src_bvl->bv_len; + segment++; + BUG_ON(segment >= bio->bi_vcnt); + src_bvl = bio_iovec_idx(bio, segment); + } + + if (src_bvl->bv_len - src_offs >= CD_FRAMESIZE) { + pages[f] = src_bvl->bv_page; + offsets[f] = src_bvl->bv_offset + src_offs; + } else { + pkt_copy_bio_data(bio, segment, src_offs, + pages[f], offsets[f]); + } + src_offs += CD_FRAMESIZE; + frames_write++; + } + } + pkt_set_state(pkt, PACKET_WRITE_WAIT_STATE); + spin_unlock(&pkt->lock); + + VPRINTK("pkt_start_write: Writing %d frames for zone %llx\n", + frames_write, (unsigned long long)pkt->sector); + BUG_ON(frames_write != pkt->write_size); + + if (test_bit(PACKET_MERGE_SEGS, &pd->flags) || (pkt->write_size < pkt->frames)) { + pkt_make_local_copy(pkt, pages, offsets); + pkt->cache_valid = 1; + } else { + pkt->cache_valid = 0; + } + + /* Start the write request */ + bio_init(pkt->w_bio); + pkt->w_bio->bi_max_vecs = PACKET_MAX_SIZE; + pkt->w_bio->bi_sector = pkt->sector; + pkt->w_bio->bi_bdev = pd->bdev; + pkt->w_bio->bi_end_io = pkt_end_io_packet_write; + pkt->w_bio->bi_private = pkt; + for (f = 0; f < pkt->frames; f++) { + if ((f + 1 < pkt->frames) && (pages[f + 1] == pages[f]) && + (offsets[f + 1] = offsets[f] + CD_FRAMESIZE)) { + if (!bio_add_page(pkt->w_bio, pages[f], CD_FRAMESIZE * 2, offsets[f])) + BUG(); + f++; + } else { + if (!bio_add_page(pkt->w_bio, pages[f], CD_FRAMESIZE, offsets[f])) + BUG(); + } + } + VPRINTK("pktcdvd: vcnt=%d\n", pkt->w_bio->bi_vcnt); + + atomic_set(&pkt->io_wait, 1); + pkt->w_bio->bi_rw = WRITE; + pkt_queue_bio(pd, pkt->w_bio, 0); +} + +static void pkt_finish_packet(struct packet_data *pkt, int uptodate) +{ + struct bio *bio, *next; + + if (!uptodate) + pkt->cache_valid = 0; + + /* Finish all bios corresponding to this packet */ + bio = pkt->orig_bios; + while (bio) { + next = bio->bi_next; + bio->bi_next = NULL; + bio_endio(bio, bio->bi_size, uptodate ? 0 : -EIO); + bio = next; + } + pkt->orig_bios = pkt->orig_bios_tail = NULL; +} + +static void pkt_run_state_machine(struct pktcdvd_device *pd, struct packet_data *pkt) +{ + int uptodate; + + VPRINTK("run_state_machine: pkt %d\n", pkt->id); + + for (;;) { + switch (pkt->state) { + case PACKET_WAITING_STATE: + if ((pkt->write_size < pkt->frames) && (pkt->sleep_time > 0)) + return; + + pkt->sleep_time = 0; + pkt_gather_data(pd, pkt); + pkt_set_state(pkt, PACKET_READ_WAIT_STATE); + break; + + case PACKET_READ_WAIT_STATE: + if (atomic_read(&pkt->io_wait) > 0) + return; + + if (atomic_read(&pkt->io_errors) > 0) { + pkt_set_state(pkt, PACKET_RECOVERY_STATE); + } else { + pkt_start_write(pd, pkt); + } + break; + + case PACKET_WRITE_WAIT_STATE: + if (atomic_read(&pkt->io_wait) > 0) + return; + + if (test_bit(BIO_UPTODATE, &pkt->w_bio->bi_flags)) { + pkt_set_state(pkt, PACKET_FINISHED_STATE); + } else { + pkt_set_state(pkt, PACKET_RECOVERY_STATE); + } + break; + + case PACKET_RECOVERY_STATE: + if (pkt_start_recovery(pkt)) { + pkt_start_write(pd, pkt); + } else { + VPRINTK("No recovery possible\n"); + pkt_set_state(pkt, PACKET_FINISHED_STATE); + } + break; + + case PACKET_FINISHED_STATE: + uptodate = test_bit(BIO_UPTODATE, &pkt->w_bio->bi_flags); + pkt_finish_packet(pkt, uptodate); + return; + + default: + BUG(); + break; + } + } +} + +static void pkt_handle_packets(struct pktcdvd_device *pd) +{ + struct packet_data *pkt, *next; + + VPRINTK("pkt_handle_packets\n"); + + /* + * Run state machine for active packets + */ + list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { + if (atomic_read(&pkt->run_sm) > 0) { + atomic_set(&pkt->run_sm, 0); + pkt_run_state_machine(pd, pkt); + } + } + + /* + * Move no longer active packets to the free list + */ + spin_lock(&pd->cdrw.active_list_lock); + list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_active_list, list) { + if (pkt->state == PACKET_FINISHED_STATE) { + list_del(&pkt->list); + pkt_put_packet_data(pd, pkt); + pkt_set_state(pkt, PACKET_IDLE_STATE); + atomic_set(&pd->scan_queue, 1); + } + } + spin_unlock(&pd->cdrw.active_list_lock); +} + +static void pkt_count_states(struct pktcdvd_device *pd, int *states) +{ + struct packet_data *pkt; + int i; + + for (i = 0; i <= PACKET_NUM_STATES; i++) + states[i] = 0; + + spin_lock(&pd->cdrw.active_list_lock); + list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { + states[pkt->state]++; + } + spin_unlock(&pd->cdrw.active_list_lock); +} + +/* + * kcdrwd is woken up when writes have been queued for one of our + * registered devices + */ +static int kcdrwd(void *foobar) +{ + struct pktcdvd_device *pd = foobar; + struct packet_data *pkt; + long min_sleep_time, residue; + + set_user_nice(current, -20); + + for (;;) { + DECLARE_WAITQUEUE(wait, current); + + /* + * Wait until there is something to do + */ + add_wait_queue(&pd->wqueue, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + + /* Check if we need to run pkt_handle_queue */ + if (atomic_read(&pd->scan_queue) > 0) + goto work_to_do; + + /* Check if we need to run the state machine for some packet */ + list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { + if (atomic_read(&pkt->run_sm) > 0) + goto work_to_do; + } + + /* Check if we need to process the iosched queues */ + if (atomic_read(&pd->iosched.attention) != 0) + goto work_to_do; + + /* Otherwise, go to sleep */ + if (PACKET_DEBUG > 1) { + int states[PACKET_NUM_STATES]; + pkt_count_states(pd, states); + VPRINTK("kcdrwd: i:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n", + states[0], states[1], states[2], states[3], + states[4], states[5]); + } + + min_sleep_time = MAX_SCHEDULE_TIMEOUT; + list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { + if (pkt->sleep_time && pkt->sleep_time < min_sleep_time) + min_sleep_time = pkt->sleep_time; + } + + generic_unplug_device(bdev_get_queue(pd->bdev)); + + VPRINTK("kcdrwd: sleeping\n"); + residue = schedule_timeout(min_sleep_time); + VPRINTK("kcdrwd: wake up\n"); + + /* make swsusp happy with our thread */ + if (current->flags & PF_FREEZE) + refrigerator(PF_FREEZE); + + list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { + if (!pkt->sleep_time) + continue; + pkt->sleep_time -= min_sleep_time - residue; + if (pkt->sleep_time <= 0) { + pkt->sleep_time = 0; + atomic_inc(&pkt->run_sm); + } + } + + if (signal_pending(current)) { + flush_signals(current); + } + if (kthread_should_stop()) + break; + } +work_to_do: + set_current_state(TASK_RUNNING); + remove_wait_queue(&pd->wqueue, &wait); + + if (kthread_should_stop()) + break; + + /* + * if pkt_handle_queue returns true, we can queue + * another request. + */ + while (pkt_handle_queue(pd)) + ; + + /* + * Handle packet state machine + */ + pkt_handle_packets(pd); + + /* + * Handle iosched queues + */ + pkt_iosched_process_queue(pd); + } + + return 0; +} + +static void pkt_print_settings(struct pktcdvd_device *pd) +{ + printk("pktcdvd: %s packets, ", pd->settings.fp ? "Fixed" : "Variable"); + printk("%u blocks, ", pd->settings.size >> 2); + printk("Mode-%c disc\n", pd->settings.block_mode == 8 ? '1' : '2'); +} + +static int pkt_mode_sense(struct pktcdvd_device *pd, struct packet_command *cgc, + int page_code, int page_control) +{ + memset(cgc->cmd, 0, sizeof(cgc->cmd)); + + cgc->cmd[0] = GPCMD_MODE_SENSE_10; + cgc->cmd[2] = page_code | (page_control << 6); + cgc->cmd[7] = cgc->buflen >> 8; + cgc->cmd[8] = cgc->buflen & 0xff; + cgc->data_direction = CGC_DATA_READ; + return pkt_generic_packet(pd, cgc); +} + +static int pkt_mode_select(struct pktcdvd_device *pd, struct packet_command *cgc) +{ + memset(cgc->cmd, 0, sizeof(cgc->cmd)); + memset(cgc->buffer, 0, 2); + cgc->cmd[0] = GPCMD_MODE_SELECT_10; + cgc->cmd[1] = 0x10; /* PF */ + cgc->cmd[7] = cgc->buflen >> 8; + cgc->cmd[8] = cgc->buflen & 0xff; + cgc->data_direction = CGC_DATA_WRITE; + return pkt_generic_packet(pd, cgc); +} + +static int pkt_get_disc_info(struct pktcdvd_device *pd, disc_information *di) +{ + struct packet_command cgc; + int ret; + + /* set up command and get the disc info */ + init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ); + cgc.cmd[0] = GPCMD_READ_DISC_INFO; + cgc.cmd[8] = cgc.buflen = 2; + cgc.quiet = 1; + + if ((ret = pkt_generic_packet(pd, &cgc))) + return ret; + + /* not all drives have the same disc_info length, so requeue + * packet with the length the drive tells us it can supply + */ + cgc.buflen = be16_to_cpu(di->disc_information_length) + + sizeof(di->disc_information_length); + + if (cgc.buflen > sizeof(disc_information)) + cgc.buflen = sizeof(disc_information); + + cgc.cmd[8] = cgc.buflen; + return pkt_generic_packet(pd, &cgc); +} + +static int pkt_get_track_info(struct pktcdvd_device *pd, __u16 track, __u8 type, track_information *ti) +{ + struct packet_command cgc; + int ret; + + init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ); + cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO; + cgc.cmd[1] = type & 3; + cgc.cmd[4] = (track & 0xff00) >> 8; + cgc.cmd[5] = track & 0xff; + cgc.cmd[8] = 8; + cgc.quiet = 1; + + if ((ret = pkt_generic_packet(pd, &cgc))) + return ret; + + cgc.buflen = be16_to_cpu(ti->track_information_length) + + sizeof(ti->track_information_length); + + if (cgc.buflen > sizeof(track_information)) + cgc.buflen = sizeof(track_information); + + cgc.cmd[8] = cgc.buflen; + return pkt_generic_packet(pd, &cgc); +} + +static int pkt_get_last_written(struct pktcdvd_device *pd, long *last_written) +{ + disc_information di; + track_information ti; + __u32 last_track; + int ret = -1; + + if ((ret = pkt_get_disc_info(pd, &di))) + return ret; + + last_track = (di.last_track_msb << 8) | di.last_track_lsb; + if ((ret = pkt_get_track_info(pd, last_track, 1, &ti))) + return ret; + + /* if this track is blank, try the previous. */ + if (ti.blank) { + last_track--; + if ((ret = pkt_get_track_info(pd, last_track, 1, &ti))) + return ret; + } + + /* if last recorded field is valid, return it. */ + if (ti.lra_v) { + *last_written = be32_to_cpu(ti.last_rec_address); + } else { + /* make it up instead */ + *last_written = be32_to_cpu(ti.track_start) + + be32_to_cpu(ti.track_size); + if (ti.free_blocks) + *last_written -= (be32_to_cpu(ti.free_blocks) + 7); + } + return 0; +} + +/* + * write mode select package based on pd->settings + */ +static int pkt_set_write_settings(struct pktcdvd_device *pd) +{ + struct packet_command cgc; + struct request_sense sense; + write_param_page *wp; + char buffer[128]; + int ret, size; + + /* doesn't apply to DVD+RW */ + if (pd->mmc3_profile == 0x1a) + return 0; + + memset(buffer, 0, sizeof(buffer)); + init_cdrom_command(&cgc, buffer, sizeof(*wp), CGC_DATA_READ); + cgc.sense = &sense; + if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) { + pkt_dump_sense(&cgc); + return ret; + } + + size = 2 + ((buffer[0] << 8) | (buffer[1] & 0xff)); + pd->mode_offset = (buffer[6] << 8) | (buffer[7] & 0xff); + if (size > sizeof(buffer)) + size = sizeof(buffer); + + /* + * now get it all + */ + init_cdrom_command(&cgc, buffer, size, CGC_DATA_READ); + cgc.sense = &sense; + if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) { + pkt_dump_sense(&cgc); + return ret; + } + + /* + * write page is offset header + block descriptor length + */ + wp = (write_param_page *) &buffer[sizeof(struct mode_page_header) + pd->mode_offset]; + + wp->fp = pd->settings.fp; + wp->track_mode = pd->settings.track_mode; + wp->write_type = pd->settings.write_type; + wp->data_block_type = pd->settings.block_mode; + + wp->multi_session = 0; + +#ifdef PACKET_USE_LS + wp->link_size = 7; + wp->ls_v = 1; +#endif + + if (wp->data_block_type == PACKET_BLOCK_MODE1) { + wp->session_format = 0; + wp->subhdr2 = 0x20; + } else if (wp->data_block_type == PACKET_BLOCK_MODE2) { + wp->session_format = 0x20; + wp->subhdr2 = 8; +#if 0 + wp->mcn[0] = 0x80; + memcpy(&wp->mcn[1], PACKET_MCN, sizeof(wp->mcn) - 1); +#endif + } else { + /* + * paranoia + */ + printk("pktcdvd: write mode wrong %d\n", wp->data_block_type); + return 1; + } + wp->packet_size = cpu_to_be32(pd->settings.size >> 2); + + cgc.buflen = cgc.cmd[8] = size; + if ((ret = pkt_mode_select(pd, &cgc))) { + pkt_dump_sense(&cgc); + return ret; + } + + pkt_print_settings(pd); + return 0; +} + +/* + * 0 -- we can write to this track, 1 -- we can't + */ +static int pkt_good_track(track_information *ti) +{ + /* + * only good for CD-RW at the moment, not DVD-RW + */ + + /* + * FIXME: only for FP + */ + if (ti->fp == 0) + return 0; + + /* + * "good" settings as per Mt Fuji. + */ + if (ti->rt == 0 && ti->blank == 0 && ti->packet == 1) + return 0; + + if (ti->rt == 0 && ti->blank == 1 && ti->packet == 1) + return 0; + + if (ti->rt == 1 && ti->blank == 0 && ti->packet == 1) + return 0; + + printk("pktcdvd: bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet); + return 1; +} + +/* + * 0 -- we can write to this disc, 1 -- we can't + */ +static int pkt_good_disc(struct pktcdvd_device *pd, disc_information *di) +{ + switch (pd->mmc3_profile) { + case 0x0a: /* CD-RW */ + case 0xffff: /* MMC3 not supported */ + break; + case 0x1a: /* DVD+RW */ + case 0x13: /* DVD-RW */ + return 0; + default: + printk("pktcdvd: Wrong disc profile (%x)\n", pd->mmc3_profile); + return 1; + } + + /* + * for disc type 0xff we should probably reserve a new track. + * but i'm not sure, should we leave this to user apps? probably. + */ + if (di->disc_type == 0xff) { + printk("pktcdvd: Unknown disc. No track?\n"); + return 1; + } + + if (di->disc_type != 0x20 && di->disc_type != 0) { + printk("pktcdvd: Wrong disc type (%x)\n", di->disc_type); + return 1; + } + + if (di->erasable == 0) { + printk("pktcdvd: Disc not erasable\n"); + return 1; + } + + if (di->border_status == PACKET_SESSION_RESERVED) { + printk("pktcdvd: Can't write to last track (reserved)\n"); + return 1; + } + + return 0; +} + +static int pkt_probe_settings(struct pktcdvd_device *pd) +{ + struct packet_command cgc; + unsigned char buf[12]; + disc_information di; + track_information ti; + int ret, track; + + init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); + cgc.cmd[0] = GPCMD_GET_CONFIGURATION; + cgc.cmd[8] = 8; + ret = pkt_generic_packet(pd, &cgc); + pd->mmc3_profile = ret ? 0xffff : buf[6] << 8 | buf[7]; + + memset(&di, 0, sizeof(disc_information)); + memset(&ti, 0, sizeof(track_information)); + + if ((ret = pkt_get_disc_info(pd, &di))) { + printk("failed get_disc\n"); + return ret; + } + + if (pkt_good_disc(pd, &di)) + return -ENXIO; + + switch (pd->mmc3_profile) { + case 0x1a: /* DVD+RW */ + printk("pktcdvd: inserted media is DVD+RW\n"); + break; + case 0x13: /* DVD-RW */ + printk("pktcdvd: inserted media is DVD-RW\n"); + break; + default: + printk("pktcdvd: inserted media is CD-R%s\n", di.erasable ? "W" : ""); + break; + } + pd->type = di.erasable ? PACKET_CDRW : PACKET_CDR; + + track = 1; /* (di.last_track_msb << 8) | di.last_track_lsb; */ + if ((ret = pkt_get_track_info(pd, track, 1, &ti))) { + printk("pktcdvd: failed get_track\n"); + return ret; + } + + if (pkt_good_track(&ti)) { + printk("pktcdvd: can't write to this track\n"); + return -ENXIO; + } + + /* + * we keep packet size in 512 byte units, makes it easier to + * deal with request calculations. + */ + pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2; + if (pd->settings.size == 0) { + printk("pktcdvd: detected zero packet size!\n"); + pd->settings.size = 128; + } + pd->settings.fp = ti.fp; + pd->offset = (be32_to_cpu(ti.track_start) << 2) & (pd->settings.size - 1); + + if (ti.nwa_v) { + pd->nwa = be32_to_cpu(ti.next_writable); + set_bit(PACKET_NWA_VALID, &pd->flags); + } + + /* + * in theory we could use lra on -RW media as well and just zero + * blocks that haven't been written yet, but in practice that + * is just a no-go. we'll use that for -R, naturally. + */ + if (ti.lra_v) { + pd->lra = be32_to_cpu(ti.last_rec_address); + set_bit(PACKET_LRA_VALID, &pd->flags); + } else { + pd->lra = 0xffffffff; + set_bit(PACKET_LRA_VALID, &pd->flags); + } + + /* + * fine for now + */ + pd->settings.link_loss = 7; + pd->settings.write_type = 0; /* packet */ + pd->settings.track_mode = ti.track_mode; + + /* + * mode1 or mode2 disc + */ + switch (ti.data_mode) { + case PACKET_MODE1: + pd->settings.block_mode = PACKET_BLOCK_MODE1; + break; + case PACKET_MODE2: + pd->settings.block_mode = PACKET_BLOCK_MODE2; + break; + default: + printk("pktcdvd: unknown data mode\n"); + return 1; + } + return 0; +} + +/* + * enable/disable write caching on drive + */ +static int pkt_write_caching(struct pktcdvd_device *pd, int set) +{ + struct packet_command cgc; + struct request_sense sense; + unsigned char buf[64]; + int ret; + + memset(buf, 0, sizeof(buf)); + init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); + cgc.sense = &sense; + cgc.buflen = pd->mode_offset + 12; + + /* + * caching mode page might not be there, so quiet this command + */ + cgc.quiet = 1; + + if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WCACHING_PAGE, 0))) + return ret; + + buf[pd->mode_offset + 10] |= (!!set << 2); + + cgc.buflen = cgc.cmd[8] = 2 + ((buf[0] << 8) | (buf[1] & 0xff)); + ret = pkt_mode_select(pd, &cgc); + if (ret) { + printk("pktcdvd: write caching control failed\n"); + pkt_dump_sense(&cgc); + } else if (!ret && set) + printk("pktcdvd: enabled write caching on %s\n", pd->name); + return ret; +} + +static int pkt_lock_door(struct pktcdvd_device *pd, int lockflag) +{ + struct packet_command cgc; + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL; + cgc.cmd[4] = lockflag ? 1 : 0; + return pkt_generic_packet(pd, &cgc); +} + +/* + * Returns drive maximum write speed + */ +static int pkt_get_max_speed(struct pktcdvd_device *pd, unsigned *write_speed) +{ + struct packet_command cgc; + struct request_sense sense; + unsigned char buf[256+18]; + unsigned char *cap_buf; + int ret, offset; + + memset(buf, 0, sizeof(buf)); + cap_buf = &buf[sizeof(struct mode_page_header) + pd->mode_offset]; + init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_UNKNOWN); + cgc.sense = &sense; + + ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0); + if (ret) { + cgc.buflen = pd->mode_offset + cap_buf[1] + 2 + + sizeof(struct mode_page_header); + ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0); + if (ret) { + pkt_dump_sense(&cgc); + return ret; + } + } + + offset = 20; /* Obsoleted field, used by older drives */ + if (cap_buf[1] >= 28) + offset = 28; /* Current write speed selected */ + if (cap_buf[1] >= 30) { + /* If the drive reports at least one "Logical Unit Write + * Speed Performance Descriptor Block", use the information + * in the first block. (contains the highest speed) + */ + int num_spdb = (cap_buf[30] << 8) + cap_buf[31]; + if (num_spdb > 0) + offset = 34; + } + + *write_speed = (cap_buf[offset] << 8) | cap_buf[offset + 1]; + return 0; +} + +/* These tables from cdrecord - I don't have orange book */ +/* standard speed CD-RW (1-4x) */ +static char clv_to_speed[16] = { + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ + 0, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +/* high speed CD-RW (-10x) */ +static char hs_clv_to_speed[16] = { + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ + 0, 2, 4, 6, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +/* ultra high speed CD-RW */ +static char us_clv_to_speed[16] = { + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ + 0, 2, 4, 8, 0, 0,16, 0,24,32,40,48, 0, 0, 0, 0 +}; + +/* + * reads the maximum media speed from ATIP + */ +static int pkt_media_speed(struct pktcdvd_device *pd, unsigned *speed) +{ + struct packet_command cgc; + struct request_sense sense; + unsigned char buf[64]; + unsigned int size, st, sp; + int ret; + + init_cdrom_command(&cgc, buf, 2, CGC_DATA_READ); + cgc.sense = &sense; + cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP; + cgc.cmd[1] = 2; + cgc.cmd[2] = 4; /* READ ATIP */ + cgc.cmd[8] = 2; + ret = pkt_generic_packet(pd, &cgc); + if (ret) { + pkt_dump_sense(&cgc); + return ret; + } + size = ((unsigned int) buf[0]<<8) + buf[1] + 2; + if (size > sizeof(buf)) + size = sizeof(buf); + + init_cdrom_command(&cgc, buf, size, CGC_DATA_READ); + cgc.sense = &sense; + cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP; + cgc.cmd[1] = 2; + cgc.cmd[2] = 4; + cgc.cmd[8] = size; + ret = pkt_generic_packet(pd, &cgc); + if (ret) { + pkt_dump_sense(&cgc); + return ret; + } + + if (!buf[6] & 0x40) { + printk("pktcdvd: Disc type is not CD-RW\n"); + return 1; + } + if (!buf[6] & 0x4) { + printk("pktcdvd: A1 values on media are not valid, maybe not CDRW?\n"); + return 1; + } + + st = (buf[6] >> 3) & 0x7; /* disc sub-type */ + + sp = buf[16] & 0xf; /* max speed from ATIP A1 field */ + + /* Info from cdrecord */ + switch (st) { + case 0: /* standard speed */ + *speed = clv_to_speed[sp]; + break; + case 1: /* high speed */ + *speed = hs_clv_to_speed[sp]; + break; + case 2: /* ultra high speed */ + *speed = us_clv_to_speed[sp]; + break; + default: + printk("pktcdvd: Unknown disc sub-type %d\n",st); + return 1; + } + if (*speed) { + printk("pktcdvd: Max. media speed: %d\n",*speed); + return 0; + } else { + printk("pktcdvd: Unknown speed %d for sub-type %d\n",sp,st); + return 1; + } +} + +static int pkt_perform_opc(struct pktcdvd_device *pd) +{ + struct packet_command cgc; + struct request_sense sense; + int ret; + + VPRINTK("pktcdvd: Performing OPC\n"); + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.sense = &sense; + cgc.timeout = 60*HZ; + cgc.cmd[0] = GPCMD_SEND_OPC; + cgc.cmd[1] = 1; + if ((ret = pkt_generic_packet(pd, &cgc))) + pkt_dump_sense(&cgc); + return ret; +} + +static int pkt_open_write(struct pktcdvd_device *pd) +{ + int ret; + unsigned int write_speed, media_write_speed, read_speed; + + if ((ret = pkt_probe_settings(pd))) { + DPRINTK("pktcdvd: %s failed probe\n", pd->name); + return -EIO; + } + + if ((ret = pkt_set_write_settings(pd))) { + DPRINTK("pktcdvd: %s failed saving write settings\n", pd->name); + return -EIO; + } + + pkt_write_caching(pd, USE_WCACHING); + + if ((ret = pkt_get_max_speed(pd, &write_speed))) + write_speed = 16 * 177; + switch (pd->mmc3_profile) { + case 0x13: /* DVD-RW */ + case 0x1a: /* DVD+RW */ + DPRINTK("pktcdvd: write speed %ukB/s\n", write_speed); + break; + default: + if ((ret = pkt_media_speed(pd, &media_write_speed))) + media_write_speed = 16; + write_speed = min(write_speed, media_write_speed * 177); + DPRINTK("pktcdvd: write speed %ux\n", write_speed / 176); + break; + } + read_speed = write_speed; + + if ((ret = pkt_set_speed(pd, write_speed, read_speed))) { + DPRINTK("pktcdvd: %s couldn't set write speed\n", pd->name); + return -EIO; + } + pd->write_speed = write_speed; + pd->read_speed = read_speed; + + if ((ret = pkt_perform_opc(pd))) { + DPRINTK("pktcdvd: %s Optimum Power Calibration failed\n", pd->name); + } + + return 0; +} + +/* + * called at open time. + */ +static int pkt_open_dev(struct pktcdvd_device *pd, int write) +{ + int ret; + long lba; + request_queue_t *q; + + /* + * We need to re-open the cdrom device without O_NONBLOCK to be able + * to read/write from/to it. It is already opened in O_NONBLOCK mode + * so bdget() can't fail. + */ + bdget(pd->bdev->bd_dev); + if ((ret = blkdev_get(pd->bdev, FMODE_READ, O_RDONLY))) + goto out; + + if ((ret = pkt_get_last_written(pd, &lba))) { + printk("pktcdvd: pkt_get_last_written failed\n"); + goto out_putdev; + } + + set_capacity(pd->disk, lba << 2); + set_capacity(pd->bdev->bd_disk, lba << 2); + bd_set_size(pd->bdev, (loff_t)lba << 11); + + q = bdev_get_queue(pd->bdev); + if (write) { + if ((ret = pkt_open_write(pd))) + goto out_putdev; + /* + * Some CDRW drives can not handle writes larger than one packet, + * even if the size is a multiple of the packet size. + */ + spin_lock_irq(q->queue_lock); + blk_queue_max_sectors(q, pd->settings.size); + spin_unlock_irq(q->queue_lock); + set_bit(PACKET_WRITABLE, &pd->flags); + } else { + pkt_set_speed(pd, MAX_SPEED, MAX_SPEED); + clear_bit(PACKET_WRITABLE, &pd->flags); + } + + if ((ret = pkt_set_segment_merging(pd, q))) + goto out_putdev; + + if (write) + printk("pktcdvd: %lukB available on disc\n", lba << 1); + + return 0; + +out_putdev: + blkdev_put(pd->bdev); +out: + return ret; +} + +/* + * called when the device is closed. makes sure that the device flushes + * the internal cache before we close. + */ +static void pkt_release_dev(struct pktcdvd_device *pd, int flush) +{ + if (flush && pkt_flush_cache(pd)) + DPRINTK("pktcdvd: %s not flushing cache\n", pd->name); + + pkt_lock_door(pd, 0); + + pkt_set_speed(pd, MAX_SPEED, MAX_SPEED); + blkdev_put(pd->bdev); +} + +static struct pktcdvd_device *pkt_find_dev_from_minor(int dev_minor) +{ + if (dev_minor >= MAX_WRITERS) + return NULL; + return pkt_devs[dev_minor]; +} + +static int pkt_open(struct inode *inode, struct file *file) +{ + struct pktcdvd_device *pd = NULL; + int ret; + + VPRINTK("pktcdvd: entering open\n"); + + down(&ctl_mutex); + pd = pkt_find_dev_from_minor(iminor(inode)); + if (!pd) { + ret = -ENODEV; + goto out; + } + BUG_ON(pd->refcnt < 0); + + pd->refcnt++; + if (pd->refcnt == 1) { + if (pkt_open_dev(pd, file->f_mode & FMODE_WRITE)) { + ret = -EIO; + goto out_dec; + } + /* + * needed here as well, since ext2 (among others) may change + * the blocksize at mount time + */ + set_blocksize(inode->i_bdev, CD_FRAMESIZE); + } + + up(&ctl_mutex); + return 0; + +out_dec: + pd->refcnt--; +out: + VPRINTK("pktcdvd: failed open (%d)\n", ret); + up(&ctl_mutex); + return ret; +} + +static int pkt_close(struct inode *inode, struct file *file) +{ + struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data; + int ret = 0; + + down(&ctl_mutex); + pd->refcnt--; + BUG_ON(pd->refcnt < 0); + if (pd->refcnt == 0) { + int flush = test_bit(PACKET_WRITABLE, &pd->flags); + pkt_release_dev(pd, flush); + } + up(&ctl_mutex); + return ret; +} + + +static void *psd_pool_alloc(int gfp_mask, void *data) +{ + return kmalloc(sizeof(struct packet_stacked_data), gfp_mask); +} + +static void psd_pool_free(void *ptr, void *data) +{ + kfree(ptr); +} + +static int pkt_end_io_read_cloned(struct bio *bio, unsigned int bytes_done, int err) +{ + struct packet_stacked_data *psd = bio->bi_private; + struct pktcdvd_device *pd = psd->pd; + + if (bio->bi_size) + return 1; + + bio_put(bio); + bio_endio(psd->bio, psd->bio->bi_size, err); + mempool_free(psd, psd_pool); + pkt_bio_finished(pd); + return 0; +} + +static int pkt_make_request(request_queue_t *q, struct bio *bio) +{ + struct pktcdvd_device *pd; + char b[BDEVNAME_SIZE]; + sector_t zone; + struct packet_data *pkt; + int was_empty, blocked_bio; + struct pkt_rb_node *node; + + pd = q->queuedata; + if (!pd) { + printk("pktcdvd: %s incorrect request queue\n", bdevname(bio->bi_bdev, b)); + goto end_io; + } + + /* + * Clone READ bios so we can have our own bi_end_io callback. + */ + if (bio_data_dir(bio) == READ) { + struct bio *cloned_bio = bio_clone(bio, GFP_NOIO); + struct packet_stacked_data *psd = mempool_alloc(psd_pool, GFP_NOIO); + + psd->pd = pd; + psd->bio = bio; + cloned_bio->bi_bdev = pd->bdev; + cloned_bio->bi_private = psd; + cloned_bio->bi_end_io = pkt_end_io_read_cloned; + pd->stats.secs_r += bio->bi_size >> 9; + pkt_queue_bio(pd, cloned_bio, 1); + return 0; + } + + if (!test_bit(PACKET_WRITABLE, &pd->flags)) { + printk("pktcdvd: WRITE for ro device %s (%llu)\n", + pd->name, (unsigned long long)bio->bi_sector); + goto end_io; + } + + if (!bio->bi_size || (bio->bi_size % CD_FRAMESIZE)) { + printk("pktcdvd: wrong bio size\n"); + goto end_io; + } + + blk_queue_bounce(q, &bio); + + zone = ZONE(bio->bi_sector, pd); + VPRINTK("pkt_make_request: start = %6llx stop = %6llx\n", + (unsigned long long)bio->bi_sector, + (unsigned long long)(bio->bi_sector + bio_sectors(bio))); + + /* Check if we have to split the bio */ + { + struct bio_pair *bp; + sector_t last_zone; + int first_sectors; + + last_zone = ZONE(bio->bi_sector + bio_sectors(bio) - 1, pd); + if (last_zone != zone) { + BUG_ON(last_zone != zone + pd->settings.size); + first_sectors = last_zone - bio->bi_sector; + bp = bio_split(bio, bio_split_pool, first_sectors); + BUG_ON(!bp); + pkt_make_request(q, &bp->bio1); + pkt_make_request(q, &bp->bio2); + bio_pair_release(bp); + return 0; + } + } + + /* + * If we find a matching packet in state WAITING or READ_WAIT, we can + * just append this bio to that packet. + */ + spin_lock(&pd->cdrw.active_list_lock); + blocked_bio = 0; + list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { + if (pkt->sector == zone) { + spin_lock(&pkt->lock); + if ((pkt->state == PACKET_WAITING_STATE) || + (pkt->state == PACKET_READ_WAIT_STATE)) { + pkt_add_list_last(bio, &pkt->orig_bios, + &pkt->orig_bios_tail); + pkt->write_size += bio->bi_size / CD_FRAMESIZE; + if ((pkt->write_size >= pkt->frames) && + (pkt->state == PACKET_WAITING_STATE)) { + atomic_inc(&pkt->run_sm); + wake_up(&pd->wqueue); + } + spin_unlock(&pkt->lock); + spin_unlock(&pd->cdrw.active_list_lock); + return 0; + } else { + blocked_bio = 1; + } + spin_unlock(&pkt->lock); + } + } + spin_unlock(&pd->cdrw.active_list_lock); + + /* + * No matching packet found. Store the bio in the work queue. + */ + node = mempool_alloc(pd->rb_pool, GFP_NOIO); + BUG_ON(!node); + node->bio = bio; + spin_lock(&pd->lock); + BUG_ON(pd->bio_queue_size < 0); + was_empty = (pd->bio_queue_size == 0); + pkt_rbtree_insert(pd, node); + spin_unlock(&pd->lock); + + /* + * Wake up the worker thread. + */ + atomic_set(&pd->scan_queue, 1); + if (was_empty) { + /* This wake_up is required for correct operation */ + wake_up(&pd->wqueue); + } else if (!list_empty(&pd->cdrw.pkt_free_list) && !blocked_bio) { + /* + * This wake up is not required for correct operation, + * but improves performance in some cases. + */ + wake_up(&pd->wqueue); + } + return 0; +end_io: + bio_io_error(bio, bio->bi_size); + return 0; +} + + + +static int pkt_merge_bvec(request_queue_t *q, struct bio *bio, struct bio_vec *bvec) +{ + struct pktcdvd_device *pd = q->queuedata; + sector_t zone = ZONE(bio->bi_sector, pd); + int used = ((bio->bi_sector - zone) << 9) + bio->bi_size; + int remaining = (pd->settings.size << 9) - used; + int remaining2; + + /* + * A bio <= PAGE_SIZE must be allowed. If it crosses a packet + * boundary, pkt_make_request() will split the bio. + */ + remaining2 = PAGE_SIZE - bio->bi_size; + remaining = max(remaining, remaining2); + + BUG_ON(remaining < 0); + return remaining; +} + +static void pkt_init_queue(struct pktcdvd_device *pd) +{ + request_queue_t *q = pd->disk->queue; + + blk_queue_make_request(q, pkt_make_request); + blk_queue_hardsect_size(q, CD_FRAMESIZE); + blk_queue_max_sectors(q, PACKET_MAX_SECTORS); + blk_queue_merge_bvec(q, pkt_merge_bvec); + q->queuedata = pd; +} + +static int pkt_seq_show(struct seq_file *m, void *p) +{ + struct pktcdvd_device *pd = m->private; + char *msg; + char bdev_buf[BDEVNAME_SIZE]; + int states[PACKET_NUM_STATES]; + + seq_printf(m, "Writer %s mapped to %s:\n", pd->name, + bdevname(pd->bdev, bdev_buf)); + + seq_printf(m, "\nSettings:\n"); + seq_printf(m, "\tpacket size:\t\t%dkB\n", pd->settings.size / 2); + + if (pd->settings.write_type == 0) + msg = "Packet"; + else + msg = "Unknown"; + seq_printf(m, "\twrite type:\t\t%s\n", msg); + + seq_printf(m, "\tpacket type:\t\t%s\n", pd->settings.fp ? "Fixed" : "Variable"); + seq_printf(m, "\tlink loss:\t\t%d\n", pd->settings.link_loss); + + seq_printf(m, "\ttrack mode:\t\t%d\n", pd->settings.track_mode); + + if (pd->settings.block_mode == PACKET_BLOCK_MODE1) + msg = "Mode 1"; + else if (pd->settings.block_mode == PACKET_BLOCK_MODE2) + msg = "Mode 2"; + else + msg = "Unknown"; + seq_printf(m, "\tblock mode:\t\t%s\n", msg); + + seq_printf(m, "\nStatistics:\n"); + seq_printf(m, "\tpackets started:\t%lu\n", pd->stats.pkt_started); + seq_printf(m, "\tpackets ended:\t\t%lu\n", pd->stats.pkt_ended); + seq_printf(m, "\twritten:\t\t%lukB\n", pd->stats.secs_w >> 1); + seq_printf(m, "\tread gather:\t\t%lukB\n", pd->stats.secs_rg >> 1); + seq_printf(m, "\tread:\t\t\t%lukB\n", pd->stats.secs_r >> 1); + + seq_printf(m, "\nMisc:\n"); + seq_printf(m, "\treference count:\t%d\n", pd->refcnt); + seq_printf(m, "\tflags:\t\t\t0x%lx\n", pd->flags); + seq_printf(m, "\tread speed:\t\t%ukB/s\n", pd->read_speed); + seq_printf(m, "\twrite speed:\t\t%ukB/s\n", pd->write_speed); + seq_printf(m, "\tstart offset:\t\t%lu\n", pd->offset); + seq_printf(m, "\tmode page offset:\t%u\n", pd->mode_offset); + + seq_printf(m, "\nQueue state:\n"); + seq_printf(m, "\tbios queued:\t\t%d\n", pd->bio_queue_size); + seq_printf(m, "\tbios pending:\t\t%d\n", atomic_read(&pd->cdrw.pending_bios)); + seq_printf(m, "\tcurrent sector:\t\t0x%llx\n", (unsigned long long)pd->current_sector); + + pkt_count_states(pd, states); + seq_printf(m, "\tstate:\t\t\ti:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n", + states[0], states[1], states[2], states[3], states[4], states[5]); + + return 0; +} + +static int pkt_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, pkt_seq_show, PDE(inode)->data); +} + +static struct file_operations pkt_proc_fops = { + .open = pkt_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + +static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev) +{ + int i; + int ret = 0; + char b[BDEVNAME_SIZE]; + struct proc_dir_entry *proc; + struct block_device *bdev; + + if (pd->pkt_dev == dev) { + printk("pktcdvd: Recursive setup not allowed\n"); + return -EBUSY; + } + for (i = 0; i < MAX_WRITERS; i++) { + struct pktcdvd_device *pd2 = pkt_devs[i]; + if (!pd2) + continue; + if (pd2->bdev->bd_dev == dev) { + printk("pktcdvd: %s already setup\n", bdevname(pd2->bdev, b)); + return -EBUSY; + } + if (pd2->pkt_dev == dev) { + printk("pktcdvd: Can't chain pktcdvd devices\n"); + return -EBUSY; + } + } + + bdev = bdget(dev); + if (!bdev) + return -ENOMEM; + ret = blkdev_get(bdev, FMODE_READ, O_RDONLY | O_NONBLOCK); + if (ret) + return ret; + + /* This is safe, since we have a reference from open(). */ + __module_get(THIS_MODULE); + + if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) { + printk("pktcdvd: not enough memory for buffers\n"); + ret = -ENOMEM; + goto out_mem; + } + + pd->bdev = bdev; + set_blocksize(bdev, CD_FRAMESIZE); + + pkt_init_queue(pd); + + atomic_set(&pd->cdrw.pending_bios, 0); + pd->cdrw.thread = kthread_run(kcdrwd, pd, "%s", pd->name); + if (IS_ERR(pd->cdrw.thread)) { + printk("pktcdvd: can't start kernel thread\n"); + ret = -ENOMEM; + goto out_thread; + } + + proc = create_proc_entry(pd->name, 0, pkt_proc); + if (proc) { + proc->data = pd; + proc->proc_fops = &pkt_proc_fops; + } + DPRINTK("pktcdvd: writer %s mapped to %s\n", pd->name, bdevname(bdev, b)); + return 0; + +out_thread: + pkt_shrink_pktlist(pd); +out_mem: + blkdev_put(bdev); + /* This is safe: open() is still holding a reference. */ + module_put(THIS_MODULE); + return ret; +} + +static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data; + + VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd, imajor(inode), iminor(inode)); + BUG_ON(!pd); + + switch (cmd) { + /* + * forward selected CDROM ioctls to CD-ROM, for UDF + */ + case CDROMMULTISESSION: + case CDROMREADTOCENTRY: + case CDROM_LAST_WRITTEN: + case CDROM_SEND_PACKET: + case SCSI_IOCTL_SEND_COMMAND: + return ioctl_by_bdev(pd->bdev, cmd, arg); + + case CDROMEJECT: + /* + * The door gets locked when the device is opened, so we + * have to unlock it or else the eject command fails. + */ + pkt_lock_door(pd, 0); + return ioctl_by_bdev(pd->bdev, cmd, arg); + + default: + printk("pktcdvd: Unknown ioctl for %s (%x)\n", pd->name, cmd); + return -ENOTTY; + } + + return 0; +} + +static int pkt_media_changed(struct gendisk *disk) +{ + struct pktcdvd_device *pd = disk->private_data; + struct gendisk *attached_disk; + + if (!pd) + return 0; + if (!pd->bdev) + return 0; + attached_disk = pd->bdev->bd_disk; + if (!attached_disk) + return 0; + return attached_disk->fops->media_changed(attached_disk); +} + +static struct block_device_operations pktcdvd_ops = { + .owner = THIS_MODULE, + .open = pkt_open, + .release = pkt_close, + .ioctl = pkt_ioctl, + .media_changed = pkt_media_changed, +}; + +/* + * Set up mapping from pktcdvd device to CD-ROM device. + */ +static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd) +{ + int idx; + int ret = -ENOMEM; + struct pktcdvd_device *pd; + struct gendisk *disk; + dev_t dev = new_decode_dev(ctrl_cmd->dev); + + for (idx = 0; idx < MAX_WRITERS; idx++) + if (!pkt_devs[idx]) + break; + if (idx == MAX_WRITERS) { + printk("pktcdvd: max %d writers supported\n", MAX_WRITERS); + return -EBUSY; + } + + pd = kmalloc(sizeof(struct pktcdvd_device), GFP_KERNEL); + if (!pd) + return ret; + memset(pd, 0, sizeof(struct pktcdvd_device)); + + pd->rb_pool = mempool_create(PKT_RB_POOL_SIZE, pkt_rb_alloc, pkt_rb_free, NULL); + if (!pd->rb_pool) + goto out_mem; + + disk = alloc_disk(1); + if (!disk) + goto out_mem; + pd->disk = disk; + + spin_lock_init(&pd->lock); + spin_lock_init(&pd->iosched.lock); + sprintf(pd->name, "pktcdvd%d", idx); + init_waitqueue_head(&pd->wqueue); + pd->bio_queue = RB_ROOT; + + disk->major = pkt_major; + disk->first_minor = idx; + disk->fops = &pktcdvd_ops; + disk->flags = GENHD_FL_REMOVABLE; + sprintf(disk->disk_name, "pktcdvd%d", idx); + disk->private_data = pd; + disk->queue = blk_alloc_queue(GFP_KERNEL); + if (!disk->queue) + goto out_mem2; + + pd->pkt_dev = MKDEV(disk->major, disk->first_minor); + ret = pkt_new_dev(pd, dev); + if (ret) + goto out_new_dev; + + add_disk(disk); + pkt_devs[idx] = pd; + ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev); + return 0; + +out_new_dev: + blk_put_queue(disk->queue); +out_mem2: + put_disk(disk); +out_mem: + if (pd->rb_pool) + mempool_destroy(pd->rb_pool); + kfree(pd); + return ret; +} + +/* + * Tear down mapping from pktcdvd device to CD-ROM device. + */ +static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd) +{ + struct pktcdvd_device *pd; + int idx; + dev_t pkt_dev = new_decode_dev(ctrl_cmd->pkt_dev); + + for (idx = 0; idx < MAX_WRITERS; idx++) { + pd = pkt_devs[idx]; + if (pd && (pd->pkt_dev == pkt_dev)) + break; + } + if (idx == MAX_WRITERS) { + DPRINTK("pktcdvd: dev not setup\n"); + return -ENXIO; + } + + if (pd->refcnt > 0) + return -EBUSY; + + if (!IS_ERR(pd->cdrw.thread)) + kthread_stop(pd->cdrw.thread); + + blkdev_put(pd->bdev); + + pkt_shrink_pktlist(pd); + + remove_proc_entry(pd->name, pkt_proc); + DPRINTK("pktcdvd: writer %s unmapped\n", pd->name); + + del_gendisk(pd->disk); + blk_put_queue(pd->disk->queue); + put_disk(pd->disk); + + pkt_devs[idx] = NULL; + mempool_destroy(pd->rb_pool); + kfree(pd); + + /* This is safe: open() is still holding a reference. */ + module_put(THIS_MODULE); + return 0; +} + +static void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd) +{ + struct pktcdvd_device *pd = pkt_find_dev_from_minor(ctrl_cmd->dev_index); + if (pd) { + ctrl_cmd->dev = new_encode_dev(pd->bdev->bd_dev); + ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev); + } else { + ctrl_cmd->dev = 0; + ctrl_cmd->pkt_dev = 0; + } + ctrl_cmd->num_devices = MAX_WRITERS; +} + +static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct pkt_ctrl_command ctrl_cmd; + int ret = 0; + + if (cmd != PACKET_CTRL_CMD) + return -ENOTTY; + + if (copy_from_user(&ctrl_cmd, argp, sizeof(struct pkt_ctrl_command))) + return -EFAULT; + + switch (ctrl_cmd.command) { + case PKT_CTRL_CMD_SETUP: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + down(&ctl_mutex); + ret = pkt_setup_dev(&ctrl_cmd); + up(&ctl_mutex); + break; + case PKT_CTRL_CMD_TEARDOWN: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + down(&ctl_mutex); + ret = pkt_remove_dev(&ctrl_cmd); + up(&ctl_mutex); + break; + case PKT_CTRL_CMD_STATUS: + down(&ctl_mutex); + pkt_get_status(&ctrl_cmd); + up(&ctl_mutex); + break; + default: + return -ENOTTY; + } + + if (copy_to_user(argp, &ctrl_cmd, sizeof(struct pkt_ctrl_command))) + return -EFAULT; + return ret; +} + + +static struct file_operations pkt_ctl_fops = { + .ioctl = pkt_ctl_ioctl, + .owner = THIS_MODULE, +}; + +static struct miscdevice pkt_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "pktcdvd", + .devfs_name = "pktcdvd/control", + .fops = &pkt_ctl_fops +}; + +int pkt_init(void) +{ + int ret; + + psd_pool = mempool_create(PSD_POOL_SIZE, psd_pool_alloc, psd_pool_free, NULL); + if (!psd_pool) + return -ENOMEM; + + ret = register_blkdev(pkt_major, "pktcdvd"); + if (ret < 0) { + printk("pktcdvd: Unable to register block device\n"); + goto out2; + } + if (!pkt_major) + pkt_major = ret; + + ret = misc_register(&pkt_misc); + if (ret) { + printk("pktcdvd: Unable to register misc device\n"); + goto out; + } + + init_MUTEX(&ctl_mutex); + + pkt_proc = proc_mkdir("pktcdvd", proc_root_driver); + + DPRINTK("pktcdvd: %s\n", VERSION_CODE); + return 0; + +out: + unregister_blkdev(pkt_major, "pktcdvd"); +out2: + mempool_destroy(psd_pool); + return ret; +} + +void pkt_exit(void) +{ + remove_proc_entry("pktcdvd", proc_root_driver); + misc_deregister(&pkt_misc); + unregister_blkdev(pkt_major, "pktcdvd"); + mempool_destroy(psd_pool); +} + +MODULE_DESCRIPTION("Packet writing layer for CD/DVD drives"); +MODULE_AUTHOR("Jens Axboe <axboe@suse.de>"); +MODULE_LICENSE("GPL"); + +module_init(pkt_init); +module_exit(pkt_exit); diff --git a/drivers/block/ub.c b/drivers/block/ub.c index f605535d3f56..dd3be0a06219 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -25,6 +25,7 @@ * -- prune comments, they are too volumnous * -- Exterminate P3 printks * -- Resove XXX's + * -- Redo "benh's retries", perhaps have spin-up code to handle them. V:D=? */ #include <linux/kernel.h> #include <linux/module.h> @@ -62,9 +63,9 @@ /* command block wrapper */ struct bulk_cb_wrap { - u32 Signature; /* contains 'USBC' */ + __le32 Signature; /* contains 'USBC' */ u32 Tag; /* unique per command id */ - u32 DataTransferLength; /* size of data */ + __le32 DataTransferLength; /* size of data */ u8 Flags; /* direction in bit 0 */ u8 Lun; /* LUN normally 0 */ u8 Length; /* of of the CDB */ @@ -78,9 +79,9 @@ struct bulk_cb_wrap { /* command status wrapper */ struct bulk_cs_wrap { - u32 Signature; /* should = 'USBS' */ + __le32 Signature; /* should = 'USBS' */ u32 Tag; /* same as original command */ - u32 Residue; /* amount not transferred */ + __le32 Residue; /* amount not transferred */ u8 Status; /* see below */ }; @@ -157,7 +158,8 @@ struct ub_scsi_cmd { struct ub_scsi_cmd *next; int error; /* Return code - valid upon done */ - int act_len; /* Return size */ + unsigned int act_len; /* Return size */ + unsigned char key, asc, ascq; /* May be valid if error==-EIO */ int stat_count; /* Retries getting status. */ @@ -490,6 +492,18 @@ static void ub_id_put(int id) */ static void ub_cleanup(struct ub_dev *sc) { + + /* + * If we zero disk->private_data BEFORE put_disk, we have to check + * for NULL all over the place in open, release, check_media and + * revalidate, because the block level semaphore is well inside the + * put_disk. But we cannot zero after the call, because *disk is gone. + * The sd.c is blatantly racy in this area. + */ + /* disk->private_data = NULL; */ + put_disk(sc->disk); + sc->disk = NULL; + ub_id_put(sc->id); kfree(sc); } @@ -661,9 +675,12 @@ static inline int ub_bd_rq_fn_1(request_queue_t *q) /* * build the command + * + * The call to blk_queue_hardsect_size() guarantees that request + * is aligned, but it is given in terms of 512 byte units, always. */ - block = rq->sector; - nblks = rq->nr_sectors; + block = rq->sector >> sc->capacity.bshift; + nblks = rq->nr_sectors >> sc->capacity.bshift; memset(cmd, 0, sizeof(struct ub_scsi_cmd)); cmd->cdb[0] = (ub_dir == UB_DIR_READ)? READ_10: WRITE_10; @@ -678,7 +695,7 @@ static inline int ub_bd_rq_fn_1(request_queue_t *q) cmd->dir = ub_dir; cmd->state = UB_CMDST_INIT; cmd->data = rq->buffer; - cmd->len = nblks * 512; + cmd->len = rq->nr_sectors * 512; cmd->done = ub_rw_cmd_done; cmd->back = rq; @@ -786,17 +803,16 @@ static int ub_scsi_cmd_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd) sc->work_urb.error_count = 0; sc->work_urb.status = 0; - sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; - add_timer(&sc->work_timer); - if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { /* XXX Clear stalls */ printk("ub: cmd #%d start failed (%d)\n", cmd->tag, rc); /* P3 */ - del_timer(&sc->work_timer); ub_complete(&sc->work_done); return rc; } + sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; + add_timer(&sc->work_timer); + cmd->state = UB_CMDST_CMD; ub_cmdtr_state(sc, cmd); return 0; @@ -836,6 +852,7 @@ static void ub_scsi_action(unsigned long _dev) unsigned long flags; spin_lock_irqsave(&sc->lock, flags); + del_timer(&sc->work_timer); ub_scsi_dispatch(sc); spin_unlock_irqrestore(&sc->lock, flags); } @@ -968,18 +985,17 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) sc->work_urb.error_count = 0; sc->work_urb.status = 0; - sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; - add_timer(&sc->work_timer); - if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { /* XXX Clear stalls */ printk("ub: data #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */ - del_timer(&sc->work_timer); ub_complete(&sc->work_done); ub_state_done(sc, cmd, rc); return; } + sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; + add_timer(&sc->work_timer); + cmd->state = UB_CMDST_DATA; ub_cmdtr_state(sc, cmd); @@ -1063,19 +1079,18 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) sc->work_urb.error_count = 0; sc->work_urb.status = 0; - sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; - add_timer(&sc->work_timer); - rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC); if (rc != 0) { /* XXX Clear stalls */ printk("%s: CSW #%d submit failed (%d)\n", sc->name, cmd->tag, rc); /* P3 */ - del_timer(&sc->work_timer); ub_complete(&sc->work_done); ub_state_done(sc, cmd, rc); return; } + + sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; + add_timer(&sc->work_timer); return; } @@ -1132,16 +1147,8 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) (*cmd->done)(sc, cmd); } else if (cmd->state == UB_CMDST_SENSE) { - /* - * We do not look at sense, because even if there was no sense, - * we get into UB_CMDST_SENSE from a STALL or CSW FAIL only. - * We request sense because we want to clear CHECK CONDITION - * on devices with delusions of SCSI, and not because we - * are curious in any way about the sense itself. - */ - /* if ((cmd->top_sense[2] & 0x0F) == NO_SENSE) { foo } */ - ub_state_done(sc, cmd, -EIO); + } else { printk(KERN_WARNING "%s: " "wrong command state %d on device %u\n", @@ -1186,18 +1193,17 @@ static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd) sc->work_urb.error_count = 0; sc->work_urb.status = 0; - sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; - add_timer(&sc->work_timer); - if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { /* XXX Clear stalls */ printk("ub: CSW #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */ - del_timer(&sc->work_timer); ub_complete(&sc->work_done); ub_state_done(sc, cmd, rc); return; } + sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; + add_timer(&sc->work_timer); + cmd->stat_count = 0; cmd->state = UB_CMDST_STAT; ub_cmdtr_state(sc, cmd); @@ -1217,9 +1223,17 @@ static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd) goto error; } + /* + * ``If the allocation length is eighteen or greater, and a device + * server returns less than eithteen bytes of data, the application + * client should assume that the bytes not transferred would have been + * zeroes had the device server returned those bytes.'' + */ memset(&sc->top_sense, 0, UB_SENSE_SIZE); + scmd = &sc->top_rqs_cmd; scmd->cdb[0] = REQUEST_SENSE; + scmd->cdb[4] = UB_SENSE_SIZE; scmd->cdb_len = 6; scmd->dir = UB_DIR_READ; scmd->state = UB_CMDST_INIT; @@ -1271,14 +1285,13 @@ static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd, sc->work_urb.error_count = 0; sc->work_urb.status = 0; - sc->work_timer.expires = jiffies + UB_CTRL_TIMEOUT; - add_timer(&sc->work_timer); - if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { - del_timer(&sc->work_timer); ub_complete(&sc->work_done); return rc; } + + sc->work_timer.expires = jiffies + UB_CTRL_TIMEOUT; + add_timer(&sc->work_timer); return 0; } @@ -1289,8 +1302,15 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd) unsigned char *sense = scmd->data; struct ub_scsi_cmd *cmd; + /* + * Ignoring scmd->act_len, because the buffer was pre-zeroed. + */ ub_cmdtr_sense(sc, scmd, sense); + /* + * Find the command which triggered the unit attention or a check, + * save the sense into it, and advance its state machine. + */ if ((cmd = ub_cmdq_peek(sc)) == NULL) { printk(KERN_WARNING "%s: sense done while idle\n", sc->name); return; @@ -1308,6 +1328,10 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd) return; } + cmd->key = sense[2] & 0x0F; + cmd->asc = sense[12]; + cmd->ascq = sense[13]; + ub_scsi_urb_compl(sc, cmd); } @@ -1407,7 +1431,15 @@ static int ub_bd_open(struct inode *inode, struct file *filp) if (sc->removable || sc->readonly) check_disk_change(inode->i_bdev); - /* XXX sd.c and floppy.c bail on open if media is not present. */ + /* + * The sd.c considers ->media_present and ->changed not equivalent, + * under some pretty murky conditions (a failure of READ CAPACITY). + * We may need it one day. + */ + if (sc->removable && sc->changed && !(filp->f_flags & O_NDELAY)) { + rc = -ENOMEDIUM; + goto err_open; + } if (sc->readonly && (filp->f_mode & FMODE_WRITE)) { rc = -EROFS; @@ -1492,8 +1524,11 @@ static int ub_bd_revalidate(struct gendisk *disk) printk(KERN_INFO "%s: device %u capacity nsec %ld bsize %u\n", sc->name, sc->dev->devnum, sc->capacity.nsec, sc->capacity.bsize); + /* XXX Support sector size switching like in sr.c */ + blk_queue_hardsect_size(disk->queue, sc->capacity.bsize); set_capacity(disk, sc->capacity.nsec); // set_disk_ro(sdkp->disk, sc->readonly); + return 0; } @@ -1592,6 +1627,9 @@ static int ub_sync_tur(struct ub_dev *sc) rc = cmd->error; + if (rc == -EIO && cmd->key != 0) /* Retries for benh's key */ + rc = cmd->key; + err_submit: kfree(cmd); err_alloc: @@ -1654,8 +1692,8 @@ static int ub_sync_read_cap(struct ub_dev *sc, struct ub_capacity *ret) } /* sd.c special-cases sector size of 0 to mean 512. Needed? Safe? */ - nsec = be32_to_cpu(*(u32 *)p) + 1; - bsize = be32_to_cpu(*(u32 *)(p + 4)); + nsec = be32_to_cpu(*(__be32 *)p) + 1; + bsize = be32_to_cpu(*(__be32 *)(p + 4)); switch (bsize) { case 512: shift = 0; break; case 1024: shift = 1; break; @@ -1725,28 +1763,22 @@ static int ub_probe_clear_stall(struct ub_dev *sc, int stalled_pipe) sc->work_urb.error_count = 0; sc->work_urb.status = 0; - init_timer(&timer); - timer.function = ub_probe_timeout; - timer.data = (unsigned long) &compl; - timer.expires = jiffies + UB_CTRL_TIMEOUT; - add_timer(&timer); - if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0) { printk(KERN_WARNING "%s: Unable to submit a probe clear (%d)\n", sc->name, rc); - del_timer_sync(&timer); return rc; } + init_timer(&timer); + timer.function = ub_probe_timeout; + timer.data = (unsigned long) &compl; + timer.expires = jiffies + UB_CTRL_TIMEOUT; + add_timer(&timer); + wait_for_completion(&compl); del_timer_sync(&timer); - /* - * Most of the time, URB was done and dev set to NULL, and so - * the unlink bounces out with ENODEV. We do not call usb_kill_urb - * because we still think about a backport to 2.4. - */ - usb_unlink_urb(&sc->work_urb); + usb_kill_urb(&sc->work_urb); /* reset the endpoint toggle */ usb_settoggle(sc->dev, endp, usb_pipeout(sc->last_pipe), 0); @@ -1813,6 +1845,7 @@ static int ub_probe(struct usb_interface *intf, request_queue_t *q; struct gendisk *disk; int rc; + int i; rc = -ENOMEM; if ((sc = kmalloc(sizeof(struct ub_dev), GFP_KERNEL)) == NULL) @@ -1879,7 +1912,11 @@ static int ub_probe(struct usb_interface *intf, * has to succeed, so we clear checks with an additional one here. * In any case it's not our business how revaliadation is implemented. */ - ub_sync_tur(sc); + for (i = 0; i < 3; i++) { /* Retries for benh's key */ + if ((rc = ub_sync_tur(sc)) <= 0) break; + if (rc != 0x6) break; + msleep(10); + } sc->removable = 1; /* XXX Query this from the device */ @@ -1915,7 +1952,7 @@ static int ub_probe(struct usb_interface *intf, blk_queue_max_phys_segments(q, UB_MAX_REQ_SG); // blk_queue_segment_boundary(q, CARM_SG_BOUNDARY); blk_queue_max_sectors(q, UB_MAX_SECTORS); - // blk_queue_hardsect_size(q, xxxxx); + blk_queue_hardsect_size(q, sc->capacity.bsize); /* * This is a serious infraction, caused by a deficiency in the @@ -2006,17 +2043,6 @@ static void ub_disconnect(struct usb_interface *intf) blk_cleanup_queue(q); /* - * If we zero disk->private_data BEFORE put_disk, we have to check - * for NULL all over the place in open, release, check_media and - * revalidate, because the block level semaphore is well inside the - * put_disk. But we cannot zero after the call, because *disk is gone. - * The sd.c is blatantly racy in this area. - */ - /* disk->private_data = NULL; */ - put_disk(disk); - sc->disk = NULL; - - /* * We really expect blk_cleanup_queue() to wait, so no amount * of paranoya is too much. * @@ -2035,6 +2061,13 @@ static void ub_disconnect(struct usb_interface *intf) spin_unlock_irqrestore(&sc->lock, flags); /* + * There is virtually no chance that other CPU runs times so long + * after ub_urb_complete should have called del_timer, but only if HCD + * didn't forget to deliver a callback on unlink. + */ + del_timer_sync(&sc->work_timer); + + /* * At this point there must be no commands coming from anyone * and no URBs left in transit. */ diff --git a/drivers/cdrom/Makefile b/drivers/cdrom/Makefile index 5c484f3b3e58..4a8351753e07 100644 --- a/drivers/cdrom/Makefile +++ b/drivers/cdrom/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_BLK_DEV_IDECD) += cdrom.o obj-$(CONFIG_BLK_DEV_SR) += cdrom.o obj-$(CONFIG_PARIDE_PCD) += cdrom.o +obj-$(CONFIG_CDROM_PKTCDVD) += cdrom.o obj-$(CONFIG_AZTCD) += aztcd.o obj-$(CONFIG_CDU31A) += cdu31a.o cdrom.o diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index e57d19031f8e..4153153aeaf5 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -234,6 +234,12 @@ -- Mt Rainier support -- DVD-RAM write open fixes + Nov 5 2001, Aug 8 2002. Modified by Andy Polyakov + <appro@fy.chalmers.se> to support MMC-3 compliant DVD+RW units. + + Modified by Nigel Kukard <nkukard@lbsd.net> - support DVD+RW + 2.4.x patch by Andy Polyakov <appro@fy.chalmers.se> + -------------------------------------------------------------------------*/ #define REVISION "Revision: 3.20" @@ -848,6 +854,41 @@ static int cdrom_ram_open_write(struct cdrom_device_info *cdi) return ret; } +static void cdrom_mmc3_profile(struct cdrom_device_info *cdi) +{ + struct packet_command cgc; + char buffer[32]; + int ret, mmc3_profile; + + init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); + + cgc.cmd[0] = GPCMD_GET_CONFIGURATION; + cgc.cmd[1] = 0; + cgc.cmd[2] = cgc.cmd[3] = 0; /* Starting Feature Number */ + cgc.cmd[8] = sizeof(buffer); /* Allocation Length */ + cgc.quiet = 1; + + if ((ret = cdi->ops->generic_packet(cdi, &cgc))) { + mmc3_profile = 0xffff; + } else { + mmc3_profile = (buffer[6] << 8) | buffer[7]; + printk(KERN_INFO "cdrom: %s: mmc-3 profile capable, current profile: %Xh\n", + cdi->name, mmc3_profile); + } + cdi->mmc3_profile = mmc3_profile; +} + +static int cdrom_is_dvd_rw(struct cdrom_device_info *cdi) +{ + switch (cdi->mmc3_profile) { + case 0x12: /* DVD-RAM */ + case 0x1A: /* DVD+RW */ + return 0; + default: + return 1; + } +} + /* * returns 0 for ok to open write, non-0 to disallow */ @@ -889,10 +930,50 @@ static int cdrom_open_write(struct cdrom_device_info *cdi) ret = cdrom_ram_open_write(cdi); else if (CDROM_CAN(CDC_MO_DRIVE)) ret = mo_open_write(cdi); + else if (!cdrom_is_dvd_rw(cdi)) + ret = 0; return ret; } +static void cdrom_dvd_rw_close_write(struct cdrom_device_info *cdi) +{ + struct packet_command cgc; + + if (cdi->mmc3_profile != 0x1a) { + cdinfo(CD_CLOSE, "%s: No DVD+RW\n", cdi->name); + return; + } + + if (!cdi->media_written) { + cdinfo(CD_CLOSE, "%s: DVD+RW media clean\n", cdi->name); + return; + } + + printk(KERN_INFO "cdrom: %s: dirty DVD+RW media, \"finalizing\"\n", + cdi->name); + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.cmd[0] = GPCMD_FLUSH_CACHE; + cgc.timeout = 30*HZ; + cdi->ops->generic_packet(cdi, &cgc); + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.cmd[0] = GPCMD_CLOSE_TRACK; + cgc.timeout = 3000*HZ; + cgc.quiet = 1; + cdi->ops->generic_packet(cdi, &cgc); + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.cmd[0] = GPCMD_CLOSE_TRACK; + cgc.cmd[2] = 2; /* Close session */ + cgc.quiet = 1; + cgc.timeout = 3000*HZ; + cdi->ops->generic_packet(cdi, &cgc); + + cdi->media_written = 0; +} + static int cdrom_close_write(struct cdrom_device_info *cdi) { #if 0 @@ -925,6 +1006,7 @@ int cdrom_open(struct cdrom_device_info *cdi, struct inode *ip, struct file *fp) ret = open_for_data(cdi); if (ret) goto err; + cdrom_mmc3_profile(cdi); if (fp->f_mode & FMODE_WRITE) { ret = -EROFS; if (cdrom_open_write(cdi)) @@ -932,6 +1014,7 @@ int cdrom_open(struct cdrom_device_info *cdi, struct inode *ip, struct file *fp) if (!CDROM_CAN(CDC_RAM)) goto err; ret = 0; + cdi->media_written = 0; } } @@ -1123,6 +1206,8 @@ int cdrom_release(struct cdrom_device_info *cdi, struct file *fp) cdi->use_count--; if (cdi->use_count == 0) cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name); + if (cdi->use_count == 0) + cdrom_dvd_rw_close_write(cdi); if (cdi->use_count == 0 && (cdo->capability & CDC_LOCK) && !keeplocked) { cdinfo(CD_CLOSE, "Unlocking door!\n"); @@ -1329,6 +1414,7 @@ int media_changed(struct cdrom_device_info *cdi, int queue) if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) { cdi->mc_flags = 0x3; /* set bit on both queues */ ret |= 1; + cdi->media_written = 0; } cdi->mc_flags &= ~mask; /* clear bit */ return ret; diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 7bec505ad95e..b7b3c5617756 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -1782,16 +1782,8 @@ static struct pci_driver agp_intel_pci_driver = { .resume = agp_intel_resume, }; -/* intel_agp_init() must not be declared static for explicit - early initialization to work (ie i810fb) */ -int __init agp_intel_init(void) +static int __init agp_intel_init(void) { - static int agp_initialised=0; - - if (agp_initialised == 1) - return 0; - agp_initialised=1; - return pci_module_init(&agp_intel_pci_driver); } diff --git a/drivers/char/agp/sis-agp.c b/drivers/char/agp/sis-agp.c index 019a31c36121..4042fd3b56bd 100644 --- a/drivers/char/agp/sis-agp.c +++ b/drivers/char/agp/sis-agp.c @@ -6,6 +6,7 @@ #include <linux/pci.h> #include <linux/init.h> #include <linux/agp_backend.h> +#include <linux/delay.h> #include "agp.h" #define SIS_ATTBASE 0x90 @@ -102,8 +103,7 @@ static void sis_delayed_enable(u32 mode) */ if (device->device == agp_bridge->dev->device) { printk(KERN_INFO PFX "SiS delay workaround: giving bridge time to recover.\n"); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout (1+(HZ*10)/1000); + msleep(10); } } } diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index f11cf4ea0398..2355455786f4 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -32,6 +32,7 @@ */ #include <linux/config.h> +#include <linux/delay.h> #undef SERIAL_PARANOIA_CHECK #define SERIAL_DO_RESTART @@ -1563,8 +1564,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) info->tty = 0; if (info->blocked_open) { if (info->close_delay) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(info->close_delay); + msleep_interruptible(jiffies_to_msecs(info->close_delay)); } wake_up_interruptible(&info->open_wait); } @@ -1622,8 +1622,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("serdatr = %d (jiff=%lu)...", lsr, jiffies); #endif - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(char_time); + msleep_interruptible(jiffies_to_msecs(char_time)); if (signal_pending(current)) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 6ceaee64b269..46002d5bada1 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -2690,20 +2690,16 @@ cy_wait_until_sent(struct tty_struct *tty, int timeout) #ifdef CY_DEBUG_WAIT_UNTIL_SENT printk("Not clean (jiff=%lu)...", jiffies); #endif - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(char_time); - if (signal_pending(current)) + if (msleep_interruptible(jiffies_to_msecs(char_time))) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) break; } - current->state = TASK_RUNNING; } else { // Nothing to do! } /* Run one more char cycle */ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(char_time * 5); + msleep_interruptible(jiffies_to_msecs(char_time * 5)); #ifdef CY_DEBUG_WAIT_UNTIL_SENT printk("Clean (jiff=%lu)...done\n", jiffies); #endif @@ -2828,8 +2824,7 @@ cy_close(struct tty_struct *tty, struct file *filp) if (info->blocked_open) { CY_UNLOCK(info, flags); if (info->close_delay) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(info->close_delay); + msleep_interruptible(jiffies_to_msecs(info->close_delay)); } wake_up_interruptible(&info->open_wait); CY_LOCK(info, flags); diff --git a/drivers/char/drm/radeon_mem.c b/drivers/char/drm/radeon_mem.c index 289957406c9c..f82d0c42472f 100644 --- a/drivers/char/drm/radeon_mem.c +++ b/drivers/char/drm/radeon_mem.c @@ -85,7 +85,7 @@ static struct mem_block *alloc_block( struct mem_block *heap, int size, struct mem_block *p; int mask = (1 << align2)-1; - for (p = heap->next ; p != heap ; p = p->next) { + list_for_each(p, heap) { int start = (p->start + mask) & ~mask; if (p->filp == 0 && start + size <= p->start + p->size) return split_block( p, start, size, filp ); @@ -98,7 +98,7 @@ static struct mem_block *find_block( struct mem_block *heap, int start ) { struct mem_block *p; - for (p = heap->next ; p != heap ; p = p->next) + list_for_each(p, heap) if (p->start == start) return p; @@ -166,7 +166,7 @@ void radeon_mem_release( DRMFILE filp, struct mem_block *heap ) if (!heap || !heap->next) return; - for (p = heap->next ; p != heap ; p = p->next) { + list_for_each(p, heap) { if (p->filp == filp) p->filp = NULL; } @@ -174,7 +174,7 @@ void radeon_mem_release( DRMFILE filp, struct mem_block *heap ) /* Assumes a single contiguous range. Needs a special filp in * 'heap' to stop it being subsumed. */ - for (p = heap->next ; p != heap ; p = p->next) { + list_for_each(p, heap) { while (p->filp == 0 && p->next->filp == 0) { struct mem_block *q = p->next; p->size += q->size; diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c index e8f15f46eca4..903e4c3cc209 100644 --- a/drivers/char/dtlk.c +++ b/drivers/char/dtlk.c @@ -107,7 +107,6 @@ static struct file_operations dtlk_fops = }; /* local prototypes */ -static void dtlk_delay(int ms); static int dtlk_dev_probe(void); static struct dtlk_settings *dtlk_interrogate(void); static int dtlk_readable(void); @@ -146,7 +145,7 @@ static ssize_t dtlk_read(struct file *file, char __user *buf, return i; if (file->f_flags & O_NONBLOCK) break; - dtlk_delay(100); + msleep_interruptible(100); } if (retries == loops_per_jiffy) printk(KERN_ERR "dtlk_read times out\n"); @@ -191,7 +190,7 @@ static ssize_t dtlk_write(struct file *file, const char __user *buf, rate to 500 bytes/sec, but that's still enough to keep up with the speech synthesizer. */ - dtlk_delay(1); + msleep_interruptible(1); else { /* the RDY bit goes zero 2-3 usec after writing, and goes 1 again @@ -212,7 +211,7 @@ static ssize_t dtlk_write(struct file *file, const char __user *buf, if (file->f_flags & O_NONBLOCK) break; - dtlk_delay(1); + msleep_interruptible(1); if (++retries > 10 * HZ) { /* wait no more than 10 sec from last write */ @@ -351,8 +350,7 @@ static int __init dtlk_init(void) static void __exit dtlk_cleanup (void) { dtlk_write_bytes("goodbye", 8); - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(5 * HZ / 10); /* nap 0.50 sec but + msleep_interruptible(500); /* nap 0.50 sec but could be awakened earlier by signals... */ @@ -368,13 +366,6 @@ module_exit(dtlk_cleanup); /* ------------------------------------------------------------------------ */ -/* sleep for ms milliseconds */ -static void dtlk_delay(int ms) -{ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout((ms * HZ + 1000 - HZ) / 1000); -} - static int dtlk_readable(void) { #ifdef TRACING @@ -431,7 +422,7 @@ static int __init dtlk_dev_probe(void) /* posting an index takes 18 msec. Here, we wait up to 100 msec to see whether it appears. */ - dtlk_delay(100); + msleep_interruptible(100); dtlk_has_indexing = dtlk_readable(); #ifdef TRACING printk(", indexing %d\n", dtlk_has_indexing); diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 0f13bef975f5..4b1e0b38cd8c 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -561,8 +561,7 @@ static void pc_close(struct tty_struct * tty, struct file * filp) if (ch->close_delay) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(ch->close_delay); + msleep_interruptible(jiffies_to_msecs(ch->close_delay)); } wake_up_interruptible(&ch->open_wait); diff --git a/drivers/char/esp.c b/drivers/char/esp.c index d67098c45ffd..7353c2678916 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -57,6 +57,7 @@ #include <linux/ioport.h> #include <linux/mm.h> #include <linux/init.h> +#include <linux/delay.h> #include <asm/system.h> #include <asm/io.h> @@ -70,6 +71,7 @@ #define NR_PORTS 64 /* maximum number of ports */ #define NR_PRIMARY 8 /* maximum number of primary ports */ +#define REGION_SIZE 8 /* size of io region to request */ /* The following variables can be set by giving module options */ static int irq[NR_PRIMARY]; /* IRQ for each base port */ @@ -2066,8 +2068,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) if (info->blocked_open) { if (info->close_delay) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(info->close_delay); + msleep_interruptible(jiffies_to_msecs(info->close_delay)); } wake_up_interruptible(&info->open_wait); } @@ -2098,8 +2099,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) while ((serial_in(info, UART_ESI_STAT1) != 0x03) || (serial_in(info, UART_ESI_STAT2) != 0xff)) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(char_time); + msleep_interruptible(jiffies_to_msecs(char_time)); if (signal_pending(current)) break; @@ -2344,19 +2344,21 @@ static _INLINE_ void show_serial_version(void) * This routine is called by espserial_init() to initialize a specific serial * port. */ -static _INLINE_ int autoconfig(struct esp_struct * info, int *region_start) +static _INLINE_ int autoconfig(struct esp_struct * info) { int port_detected = 0; unsigned long flags; + if (!request_region(info->port, REGION_SIZE, "esp serial")) + return -EIO; + save_flags(flags); cli(); /* * Check for ESP card */ - if (!check_region(info->port, 8) && - serial_in(info, UART_ESI_BASE) == 0xf3) { + if (serial_in(info, UART_ESI_BASE) == 0xf3) { serial_out(info, UART_ESI_CMD1, 0x00); serial_out(info, UART_ESI_CMD1, 0x01); @@ -2372,19 +2374,6 @@ static _INLINE_ int autoconfig(struct esp_struct * info, int *region_start) info->irq = 4; } - if (ports && (ports->port == (info->port - 8))) { - release_region(*region_start, - info->port - *region_start); - } else - *region_start = info->port; - - if (!request_region(*region_start, - info->port - *region_start + 8, - "esp serial")) - { - restore_flags(flags); - return -EIO; - } /* put card in enhanced mode */ /* this prevents access through */ @@ -2397,6 +2386,8 @@ static _INLINE_ int autoconfig(struct esp_struct * info, int *region_start) serial_out(info, UART_ESI_CMD2, 0x00); } } + if (!port_detected) + release_region(info->port, REGION_SIZE); restore_flags(flags); return (port_detected); @@ -2430,7 +2421,6 @@ static struct tty_operations esp_ops = { int __init espserial_init(void) { int i, offset; - int region_start; struct esp_struct * info; struct esp_struct *last_primary = NULL; int esp[] = {0x100,0x140,0x180,0x200,0x240,0x280,0x300,0x380}; @@ -2516,7 +2506,7 @@ int __init espserial_init(void) info->irq = irq[i]; info->line = (i * 8) + (offset / 8); - if (!autoconfig(info, ®ion_start)) { + if (!autoconfig(info)) { i++; offset = 0; continue; @@ -2592,7 +2582,6 @@ static void __exit espserial_exit(void) { unsigned long flags; int e1; - unsigned int region_start, region_end; struct esp_struct *temp_async; struct esp_pio_buffer *pio_buf; @@ -2607,27 +2596,8 @@ static void __exit espserial_exit(void) while (ports) { if (ports->port) { - region_start = region_end = ports->port; - temp_async = ports; - - while (temp_async) { - if ((region_start - temp_async->port) == 8) { - region_start = temp_async->port; - temp_async->port = 0; - temp_async = ports; - } else if ((temp_async->port - region_end) - == 8) { - region_end = temp_async->port; - temp_async->port = 0; - temp_async = ports; - } else - temp_async = temp_async->next_port; - } - - release_region(region_start, - region_end - region_start + 8); + release_region(ports->port, REGION_SIZE); } - temp_async = ports->next_port; kfree(ports); ports = temp_async; diff --git a/drivers/char/ftape/lowlevel/fdc-io.c b/drivers/char/ftape/lowlevel/fdc-io.c index 3b78eda30b19..a3b0f510b1e8 100644 --- a/drivers/char/ftape/lowlevel/fdc-io.c +++ b/drivers/char/ftape/lowlevel/fdc-io.c @@ -389,7 +389,7 @@ int fdc_interrupt_wait(unsigned int time) recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&ftape_wait_intr, &wait); while (!ft_interrupt_seen && (current->state == TASK_INTERRUPTIBLE)) { timeout = schedule_timeout(timeout); diff --git a/drivers/char/ftape/lowlevel/ftape-io.c b/drivers/char/ftape/lowlevel/ftape-io.c index bd3b2f117ce0..b7910d429f34 100644 --- a/drivers/char/ftape/lowlevel/ftape-io.c +++ b/drivers/char/ftape/lowlevel/ftape-io.c @@ -32,6 +32,7 @@ #include <asm/system.h> #include <linux/ioctl.h> #include <linux/mtio.h> +#include <linux/delay.h> #include <linux/ftape.h> #include <linux/qic117.h> @@ -96,19 +97,12 @@ void ftape_sleep(unsigned int time) timeout = ticks; save_flags(flags); sti(); - set_current_state(TASK_INTERRUPTIBLE); - do { - /* Mmm. Isn't current->blocked == 0xffffffff ? - */ - if (signal_pending(current)) { - TRACE(ft_t_err, - "awoken by non-blocked signal :-("); - break; /* exit on signal */ - } - while (current->state != TASK_RUNNING) { - timeout = schedule_timeout(timeout); - } - } while (timeout); + msleep_interruptible(jiffies_to_msecs(timeout)); + /* Mmm. Isn't current->blocked == 0xffffffff ? + */ + if (signal_pending(current)) { + TRACE(ft_t_err, "awoken by non-blocked signal :-("); + } restore_flags(flags); } TRACE_EXIT; diff --git a/drivers/char/ftape/zftape/zftape-buffers.c b/drivers/char/ftape/zftape/zftape-buffers.c index 3d6483983423..ec4fdaabe39b 100644 --- a/drivers/char/ftape/zftape/zftape-buffers.c +++ b/drivers/char/ftape/zftape/zftape-buffers.c @@ -27,6 +27,7 @@ #include <linux/errno.h> #include <linux/mm.h> #include <linux/slab.h> +#include <linux/delay.h> #include <linux/zftape.h> @@ -119,8 +120,7 @@ void *zft_kmalloc(size_t size) void *new; while ((new = kmalloc(size, GFP_KERNEL)) == NULL) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ/10); + msleep_interruptible(100); } memset(new, 0, size); used_memory += size; diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 45bc1be63669..fd9ed4c3434a 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -26,6 +26,7 @@ #include <linux/mm.h> #include <linux/generic_serial.h> #include <linux/interrupt.h> +#include <linux/delay.h> #include <asm/semaphore.h> #include <asm/uaccess.h> @@ -399,8 +400,7 @@ static int gs_wait_tx_flushed (void * ptr, int timeout) gs_dprintk (GS_DEBUG_FLUSH, "Expect to finish in %d jiffies " "(%d chars).\n", jiffies_to_transmit, charsleft); - set_current_state (TASK_INTERRUPTIBLE); - schedule_timeout(jiffies_to_transmit); + msleep_interruptible(jiffies_to_msecs(jiffies_to_transmit)); if (signal_pending (current)) { gs_dprintk (GS_DEBUG_FLUSH, "Signal pending. Bombing out: "); rv = -EINTR; @@ -767,8 +767,7 @@ void gs_close(struct tty_struct * tty, struct file * filp) if (port->blocked_open) { if (port->close_delay) { - set_current_state (TASK_INTERRUPTIBLE); - schedule_timeout(port->close_delay); + msleep_interruptible(jiffies_to_msecs(port->close_delay)); } wake_up_interruptible(&port->open_wait); } diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 3c0af2cad6ee..8d569ed74bc8 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -37,6 +37,7 @@ #include <linux/tty_flip.h> #include <linux/sched.h> #include <linux/spinlock.h> +#include <linux/delay.h> #include <asm/uaccess.h> #include <asm/hvconsole.h> #include <asm/vio.h> @@ -44,7 +45,7 @@ #define HVC_MAJOR 229 #define HVC_MINOR 0 -#define TIMEOUT ((HZ + 99) / 100) +#define TIMEOUT (10) /* * Wait this long per iteration while trying to push buffered data to the @@ -607,7 +608,7 @@ int khvcd(void *unused) if (poll_mask == 0) schedule(); else - schedule_timeout(TIMEOUT); + msleep_interruptible(TIMEOUT); } __set_current_state(TASK_RUNNING); } while (!kthread_should_stop()); diff --git a/drivers/char/ip2main.c b/drivers/char/ip2main.c index fa3ca6eb5d53..e0cbd7552eb8 100644 --- a/drivers/char/ip2main.c +++ b/drivers/char/ip2main.c @@ -1632,8 +1632,7 @@ ip2_close( PTTY tty, struct file *pFile ) if (pCh->wopen) { if (pCh->ClosingDelay) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(pCh->ClosingDelay); + msleep_interruptible(jiffies_to_msecs(pCh->ClosingDelay)); } wake_up_interruptible(&pCh->open_wait); } diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 835d6a431a00..6a943d41093c 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2283,6 +2283,7 @@ void __exit cleanup_one_si(struct smi_info *to_clean) interface is in a clean state. */ while ((to_clean->curr_msg) || (to_clean->si_state != SI_NORMAL)) { poll(to_clean); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); } diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index b454bf2084bf..eb8c18883a9d 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -1124,11 +1124,10 @@ static void isicom_close(struct tty_struct * tty, struct file * filp) port->tty = NULL; if (port->blocked_open) { if (port->close_delay) { - set_current_state(TASK_INTERRUPTIBLE); #ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: scheduling until time out.\n"); #endif - schedule_timeout(port->close_delay); + msleep_interruptible(jiffies_to_msecs(port->close_delay)); } wake_up_interruptible(&port->open_wait); } diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 9460f6d0bf97..832806725c1e 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -691,7 +691,6 @@ static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, i static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait); static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *filp); static void stli_dohangup(void *arg); -static void stli_delay(int len); static int stli_setport(stliport_t *portp); static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback); static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback); @@ -1180,7 +1179,7 @@ static void stli_close(struct tty_struct *tty, struct file *filp) if (portp->openwaitcnt) { if (portp->close_delay) - stli_delay(portp->close_delay); + msleep_interruptible(jiffies_to_msecs(portp->close_delay)); wake_up_interruptible(&portp->open_wait); } @@ -1478,25 +1477,6 @@ static int stli_setport(stliport_t *portp) /*****************************************************************************/ /* - * Wait for a specified delay period, this is not a busy-loop. It will - * give up the processor while waiting. Unfortunately this has some - * rather intimate knowledge of the process management stuff. - */ - -static void stli_delay(int len) -{ -#ifdef DEBUG - printk("stli_delay(len=%d)\n", len); -#endif - if (len > 0) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(len); - } -} - -/*****************************************************************************/ - -/* * Possibly need to wait for carrier (DCD signal) to come high. Say * maybe because if we are clocal then we don't need to wait... */ @@ -2504,7 +2484,7 @@ static void stli_waituntilsent(struct tty_struct *tty, int timeout) while (test_bit(ST_TXBUSY, &portp->state)) { if (signal_pending(current)) break; - stli_delay(2); + msleep_interruptible(20); if (time_after_eq(jiffies, tend)) break; } diff --git a/drivers/char/lcd.c b/drivers/char/lcd.c index 64837783d180..717d812c2526 100644 --- a/drivers/char/lcd.c +++ b/drivers/char/lcd.c @@ -24,6 +24,7 @@ #include <linux/mc146818rtc.h> #include <linux/netdevice.h> #include <linux/sched.h> +#include <linux/delay.h> #include <asm/io.h> #include <asm/uaccess.h> @@ -583,8 +584,7 @@ static long lcd_read(struct inode *inode, struct file *file, char *buf, lcd_waiters++; while (((buttons_now = (long) button_pressed()) == 0) && !(signal_pending(current))) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(2 * HZ); + msleep_interruptible(2000); } lcd_waiters--; diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index d45d381d444f..11373907f29f 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -625,8 +625,7 @@ static void moxa_close(struct tty_struct *tty, struct file *filp) ch->tty = NULL; if (ch->blocked_open) { if (ch->close_delay) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(ch->close_delay); + msleep_interruptible(jiffies_to_msecs(ch->close_delay)); } wake_up_interruptible(&ch->open_wait); } diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 147c1ba2efe8..9ed493ed2220 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -59,6 +59,7 @@ #include <linux/smp_lock.h> #include <linux/pci.h> #include <linux/init.h> +#include <linux/delay.h> #include <asm/system.h> #include <asm/io.h> @@ -818,8 +819,7 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) info->tty = NULL; if (info->blocked_open) { if (info->close_delay) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(info->close_delay); + msleep_interruptible(jiffies_to_msecs(info->close_delay)); } wake_up_interruptible(&info->open_wait); } diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 5d7f2154247d..f334dbfcf21f 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2609,8 +2609,7 @@ static void mgslpc_close(struct tty_struct *tty, struct file * filp) if (info->blocked_open) { if (info->close_delay) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(info->close_delay); + msleep_interruptible(jiffies_to_msecs(info->close_delay)); } wake_up_interruptible(&info->open_wait); } @@ -2665,8 +2664,7 @@ static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout) if (info->params.mode == MGSL_MODE_HDLC) { while (info->tx_active) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(char_time); + msleep_interruptible(jiffies_to_msecs(char_time)); if (signal_pending(current)) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) @@ -2675,8 +2673,7 @@ static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout) } else { while ((info->tx_count || info->tx_active) && info->tx_enabled) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(char_time); + msleep_interruptible(jiffies_to_msecs(char_time)); if (signal_pending(current)) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) @@ -4129,8 +4126,7 @@ BOOLEAN irq_test(MGSLPC_INFO *info) end_time=100; while(end_time-- && !info->irq_occurred) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(10)); + msleep_interruptible(10); } info->testing_irq = FALSE; diff --git a/drivers/char/pcxx.c b/drivers/char/pcxx.c index 061237bb0c33..97fd9da9c5ef 100644 --- a/drivers/char/pcxx.c +++ b/drivers/char/pcxx.c @@ -538,8 +538,7 @@ static void pcxe_close(struct tty_struct * tty, struct file * filp) info->tty = NULL; if(info->blocked_open) { if(info->close_delay) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(info->close_delay); + msleep_interruptible(jiffies_to_msecs(info->close_delay)); } wake_up_interruptible(&info->open_wait); } diff --git a/drivers/char/rio/rio_linux.c b/drivers/char/rio/rio_linux.c index 0cc02371a845..a8f443e1461c 100644 --- a/drivers/char/rio/rio_linux.c +++ b/drivers/char/rio/rio_linux.c @@ -330,8 +330,7 @@ int RIODelay (struct Port *PortP, int njiffies) func_enter (); rio_dprintk (RIO_DEBUG_DELAY, "delaying %d jiffies\n", njiffies); - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(njiffies); + msleep_interruptible(jiffies_to_msecs(njiffies)); func_exit(); if (signal_pending(current)) @@ -347,8 +346,7 @@ int RIODelay_ni (struct Port *PortP, int njiffies) func_enter (); rio_dprintk (RIO_DEBUG_DELAY, "delaying %d jiffies (ni)\n", njiffies); - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(njiffies); + msleep(jiffies_to_msecs(njiffies)); func_exit(); return !RIO_FAIL; } diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index a35ea03a3227..616ecc65a06b 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -45,6 +45,7 @@ #include <linux/fcntl.h> #include <linux/major.h> #include <linux/init.h> +#include <linux/delay.h> #include <asm/uaccess.h> @@ -1114,8 +1115,7 @@ static void rc_close(struct tty_struct * tty, struct file * filp) */ timeout = jiffies+HZ; while(port->IER & IER_TXEMPTY) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(port->timeout); + msleep_interruptible(jiffies_to_msecs(port->timeout)); if (time_after(jiffies, timeout)) break; } @@ -1130,8 +1130,7 @@ static void rc_close(struct tty_struct * tty, struct file * filp) port->tty = NULL; if (port->blocked_open) { if (port->close_delay) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(port->close_delay); + msleep_interruptible(jiffies_to_msecs(port->close_delay)); } wake_up_interruptible(&port->open_wait); } diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 72af1bec91e5..9f13fad3fc11 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -1112,8 +1112,7 @@ static void rp_close(struct tty_struct *tty, struct file *filp) if (info->blocked_open) { if (info->close_delay) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(info->close_delay); + msleep_interruptible(jiffies_to_msecs(info->close_delay)); } wake_up_interruptible(&info->open_wait); } else { @@ -1538,8 +1537,7 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout) #ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT printk(KERN_INFO "txcnt = %d (jiff=%lu,check=%d)...", txcnt, jiffies, check_time); #endif - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(check_time); + msleep_interruptible(jiffies_to_msecs(check_time)); if (signal_pending(current)) break; } diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 885621edb4a6..b2538a6b85b0 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -1840,8 +1840,7 @@ cy_close(struct tty_struct * tty, struct file * filp) info->tty = 0; if (info->blocked_open) { if (info->close_delay) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(info->close_delay); + msleep_interruptible(jiffies_to_msecs(info->close_delay)); } wake_up_interruptible(&info->open_wait); } diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index bf82f06d4011..568edc38313c 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -1452,8 +1452,7 @@ static void sx_close(struct tty_struct * tty, struct file * filp) */ timeout = jiffies+HZ; while(port->IER & IER_TXEMPTY) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(port->timeout); + msleep_interruptible(jiffies_to_msecs(port->timeout)); if (time_after(jiffies, timeout)) { printk (KERN_INFO "Timeout waiting for close\n"); break; @@ -1470,8 +1469,7 @@ static void sx_close(struct tty_struct * tty, struct file * filp) port->tty = NULL; if (port->blocked_open) { if (port->close_delay) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(port->close_delay); + msleep_interruptible(jiffies_to_msecs(port->close_delay)); } wake_up_interruptible(&port->open_wait); } diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 90018ce17c57..38a7e846b813 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -42,6 +42,7 @@ #include <linux/smp_lock.h> #include <linux/devfs_fs_kernel.h> #include <linux/device.h> +#include <linux/delay.h> #include <asm/io.h> #include <asm/uaccess.h> @@ -512,7 +513,6 @@ static int stl_clrportstats(stlport_t *portp, comstats_t __user *cp); static int stl_getportstruct(stlport_t __user *arg); static int stl_getbrdstruct(stlbrd_t __user *arg); static int stl_waitcarrier(stlport_t *portp, struct file *filp); -static void stl_delay(int len); static void stl_eiointr(stlbrd_t *brdp); static void stl_echatintr(stlbrd_t *brdp); static void stl_echmcaintr(stlbrd_t *brdp); @@ -1204,7 +1204,7 @@ static void stl_close(struct tty_struct *tty, struct file *filp) if (portp->openwaitcnt) { if (portp->close_delay) - stl_delay(portp->close_delay); + msleep_interruptible(jiffies_to_msecs(portp->close_delay)); wake_up_interruptible(&portp->open_wait); } @@ -1216,25 +1216,6 @@ static void stl_close(struct tty_struct *tty, struct file *filp) /*****************************************************************************/ /* - * Wait for a specified delay period, this is not a busy-loop. It will - * give up the processor while waiting. Unfortunately this has some - * rather intimate knowledge of the process management stuff. - */ - -static void stl_delay(int len) -{ -#ifdef DEBUG - printk("stl_delay(len=%d)\n", len); -#endif - if (len > 0) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(len); - } -} - -/*****************************************************************************/ - -/* * Write routine. Take data and stuff it in to the TX ring queue. * If transmit interrupts are not running then start them. */ @@ -1854,7 +1835,7 @@ static void stl_waituntilsent(struct tty_struct *tty, int timeout) while (stl_datastate(portp)) { if (signal_pending(current)) break; - stl_delay(2); + msleep_interruptible(20); if (time_after_eq(jiffies, tend)) break; } diff --git a/drivers/char/sx.c b/drivers/char/sx.c index a990eb7aa6b5..3f95c4c6dff6 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -1499,7 +1499,7 @@ static void sx_close (void *ptr) sx_send_command (port, HS_CLOSE, 0, 0); while (to-- && (sx_read_channel_byte (port, hi_hstat) != HS_IDLE_CLOSED)) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout (1); if (signal_pending (current)) break; diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 0a1cc1d3630f..edf89fffe4e1 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -82,6 +82,7 @@ #include <linux/ioport.h> #include <linux/mm.h> #include <linux/slab.h> +#include <linux/delay.h> #include <linux/netdevice.h> @@ -3259,8 +3260,7 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp) if (info->blocked_open) { if (info->close_delay) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(info->close_delay); + msleep_interruptible(jiffies_to_msecs(info->close_delay)); } wake_up_interruptible(&info->open_wait); } @@ -3326,8 +3326,7 @@ static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout) if ( info->params.mode == MGSL_MODE_HDLC || info->params.mode == MGSL_MODE_RAW ) { while (info->tx_active) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(char_time); + msleep_interruptible(jiffies_to_msecs(char_time)); if (signal_pending(current)) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) @@ -3336,8 +3335,7 @@ static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout) } else { while (!(usc_InReg(info,TCSR) & TXSTATUS_ALL_SENT) && info->tx_enabled) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(char_time); + msleep_interruptible(jiffies_to_msecs(char_time)); if (signal_pending(current)) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) @@ -7200,8 +7198,7 @@ BOOLEAN mgsl_irq_test( struct mgsl_struct *info ) EndTime=100; while( EndTime-- && !info->irq_occurred ) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(10)); + msleep_interruptible(10); } spin_lock_irqsave(&info->irq_spinlock,flags); diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 5648ed35562f..9ccce79fdf4e 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -878,8 +878,7 @@ static void close(struct tty_struct *tty, struct file *filp) if (info->blocked_open) { if (info->close_delay) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(info->close_delay); + msleep_interruptible(jiffies_to_msecs(info->close_delay)); } wake_up_interruptible(&info->open_wait); } @@ -1164,8 +1163,7 @@ static void wait_until_sent(struct tty_struct *tty, int timeout) if ( info->params.mode == MGSL_MODE_HDLC ) { while (info->tx_active) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(char_time); + msleep_interruptible(jiffies_to_msecs(char_time)); if (signal_pending(current)) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) @@ -1175,8 +1173,7 @@ static void wait_until_sent(struct tty_struct *tty, int timeout) //TODO: determine if there is something similar to USC16C32 // TXSTATUS_ALL_SENT status while ( info->tx_active && info->tx_enabled) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(char_time); + msleep_interruptible(jiffies_to_msecs(char_time)); if (signal_pending(current)) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) @@ -5209,8 +5206,7 @@ int irq_test(SLMP_INFO *info) timeout=100; while( timeout-- && !info->irq_occurred ) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(10)); + msleep_interruptible(10); } spin_lock_irqsave(&info->lock,flags); @@ -5360,8 +5356,7 @@ int loopback_test(SLMP_INFO *info) /* wait for receive complete */ /* Set a timeout for waiting for interrupt. */ for ( timeout = 100; timeout; --timeout ) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(10)); + msleep_interruptible(10); if (rx_get_frame(info)) { rc = TRUE; diff --git a/drivers/char/tpqic02.c b/drivers/char/tpqic02.c index 66881e953399..d812253b4473 100644 --- a/drivers/char/tpqic02.c +++ b/drivers/char/tpqic02.c @@ -554,10 +554,9 @@ static int wait_for_ready(time_t timeout) /* not ready and no exception && timeout not expired yet */ while (((stat = inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && time_before(jiffies, spin_t)) { /* be `nice` to other processes on long operations... */ - current->state = TASK_INTERRUPTIBLE; /* nap 0.30 sec between checks, */ /* but could be woken up earlier by signals... */ - schedule_timeout(3 * HZ / 10); + msleep_interruptible(300); } /* don't use jiffies for this test because it may have changed by now */ diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 89457cc2ed72..9d5e4ba9dc25 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1981,10 +1981,10 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, static int tioccons(struct file *file) { + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; if (file->f_op->write == redirected_tty_write) { struct file *f; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; spin_lock(&redirect_lock); f = redirect; redirect = NULL; diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 31da37da5f40..cffa77b92629 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -3077,6 +3077,10 @@ int con_font_get(int currcons, struct console_font_op *op) if (rc) goto out; + op->height = font.height; + op->width = font.width; + op->charcount = font.charcount; + if (op->data && copy_to_user(op->data, font.data, c)) rc = -EFAULT; diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index cc6b66e34e37..895f245fcced 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1932,6 +1932,8 @@ static ide_startstop_t cdrom_start_write(ide_drive_t *drive, struct request *rq) info->dma = drive->using_dma ? 1 : 0; info->cmd = WRITE; + info->devinfo.media_written = 1; + /* Start sending the write request to the drive. */ return cdrom_start_packet_command(drive, 32768, cdrom_start_write_cont); } @@ -1999,7 +2001,7 @@ ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, sector_t block) } CDROM_CONFIG_FLAGS(drive)->seeking = 0; } - if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) { + if ((rq_data_dir(rq) == READ) && IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) { action = cdrom_start_seek(drive, block); } else { if (rq_data_dir(rq) == READ) @@ -2960,8 +2962,10 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive) CDROM_CONFIG_FLAGS(drive)->no_eject = 0; if (cap.cd_r_write) CDROM_CONFIG_FLAGS(drive)->cd_r = 1; - if (cap.cd_rw_write) + if (cap.cd_rw_write) { CDROM_CONFIG_FLAGS(drive)->cd_rw = 1; + CDROM_CONFIG_FLAGS(drive)->ram = 1; + } if (cap.test_write) CDROM_CONFIG_FLAGS(drive)->test_write = 1; if (cap.dvd_ram_read || cap.dvd_r_read || cap.dvd_rom) diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index cc9c999f9a8b..2a9ff6f0229b 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -70,8 +70,7 @@ static int nodemgr_bus_read(struct csr1212_csr *csr, u64 addr, u16 length, if (!ret) break; - set_current_state(TASK_INTERRUPTIBLE); - if (schedule_timeout (HZ/3)) + if (msleep_interruptible(334)) return -EINTR; } @@ -1496,7 +1495,7 @@ static int nodemgr_host_thread(void *__hi) * to make sure things settle down. */ for (i = 0; i < 4 ; i++) { set_current_state(TASK_INTERRUPTIBLE); - if (schedule_timeout(HZ/16)) { + if (msleep_interruptible(63)) { up(&nodemgr_serialize); goto caught_signal; } diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index 47de79f21797..aa9ab3378e4c 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -357,8 +357,7 @@ static int sbp2util_down_timeout(atomic_t *done, int timeout) int i; for (i = timeout; (i > 0 && atomic_read(done) == 0); i-= HZ/10) { - set_current_state(TASK_INTERRUPTIBLE); - if (schedule_timeout(HZ/10)) /* 100ms */ + if (msleep_interruptible(100)) /* 100ms */ return(1); } return ((i > 0) ? 0:1); @@ -1088,7 +1087,7 @@ static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id) scsi_id->query_logins_orb->query_response_hi = ORB_SET_NODE_ID(hi->host->node_id); SBP2_DEBUG("sbp2_query_logins: query_response_hi/lo initialized"); - scsi_id->query_logins_orb->lun_misc = ORB_SET_FUNCTION(QUERY_LOGINS_REQUEST); + scsi_id->query_logins_orb->lun_misc = ORB_SET_FUNCTION(SBP2_QUERY_LOGINS_REQUEST); scsi_id->query_logins_orb->lun_misc |= ORB_SET_NOTIFY(1); if (scsi_id->sbp2_device_type_and_lun != SBP2_DEVICE_TYPE_LUN_UNINITIALIZED) { scsi_id->query_logins_orb->lun_misc |= ORB_SET_LUN(scsi_id->sbp2_device_type_and_lun); @@ -1199,7 +1198,7 @@ static int sbp2_login_device(struct scsi_id_instance_data *scsi_id) scsi_id->login_orb->login_response_hi = ORB_SET_NODE_ID(hi->host->node_id); SBP2_DEBUG("sbp2_login_device: login_response_hi/lo initialized"); - scsi_id->login_orb->lun_misc = ORB_SET_FUNCTION(LOGIN_REQUEST); + scsi_id->login_orb->lun_misc = ORB_SET_FUNCTION(SBP2_LOGIN_REQUEST); scsi_id->login_orb->lun_misc |= ORB_SET_RECONNECT(0); /* One second reconnect time */ scsi_id->login_orb->lun_misc |= ORB_SET_EXCLUSIVE(exclusive_login); /* Exclusive access to device */ scsi_id->login_orb->lun_misc |= ORB_SET_NOTIFY(1); /* Notify us of login complete */ @@ -1325,7 +1324,7 @@ static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id) scsi_id->logout_orb->reserved3 = 0x0; scsi_id->logout_orb->reserved4 = 0x0; - scsi_id->logout_orb->login_ID_misc = ORB_SET_FUNCTION(LOGOUT_REQUEST); + scsi_id->logout_orb->login_ID_misc = ORB_SET_FUNCTION(SBP2_LOGOUT_REQUEST); scsi_id->logout_orb->login_ID_misc |= ORB_SET_LOGIN_ID(scsi_id->login_response->length_login_ID); /* Notify us when complete */ @@ -1390,7 +1389,7 @@ static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id) scsi_id->reconnect_orb->reserved3 = 0x0; scsi_id->reconnect_orb->reserved4 = 0x0; - scsi_id->reconnect_orb->login_ID_misc = ORB_SET_FUNCTION(RECONNECT_REQUEST); + scsi_id->reconnect_orb->login_ID_misc = ORB_SET_FUNCTION(SBP2_RECONNECT_REQUEST); scsi_id->reconnect_orb->login_ID_misc |= ORB_SET_LOGIN_ID(scsi_id->login_response->length_login_ID); diff --git a/drivers/ieee1394/sbp2.h b/drivers/ieee1394/sbp2.h index d12f8e160a80..94b59f80bf90 100644 --- a/drivers/ieee1394/sbp2.h +++ b/drivers/ieee1394/sbp2.h @@ -52,15 +52,15 @@ struct sbp2_command_orb { u8 cdb[12]; }; -#define LOGIN_REQUEST 0x0 -#define QUERY_LOGINS_REQUEST 0x1 -#define RECONNECT_REQUEST 0x3 -#define SET_PASSWORD_REQUEST 0x4 -#define LOGOUT_REQUEST 0x7 -#define ABORT_TASK_REQUEST 0xb -#define ABORT_TASK_SET 0xc -#define LOGICAL_UNIT_RESET 0xe -#define TARGET_RESET_REQUEST 0xf +#define SBP2_LOGIN_REQUEST 0x0 +#define SBP2_QUERY_LOGINS_REQUEST 0x1 +#define SBP2_RECONNECT_REQUEST 0x3 +#define SBP2_SET_PASSWORD_REQUEST 0x4 +#define SBP2_LOGOUT_REQUEST 0x7 +#define SBP2_ABORT_TASK_REQUEST 0xb +#define SBP2_ABORT_TASK_SET 0xc +#define SBP2_LOGICAL_UNIT_RESET 0xe +#define SBP2_TARGET_RESET_REQUEST 0xf #define ORB_SET_LUN(value) (value & 0xffff) #define ORB_SET_FUNCTION(value) ((value & 0xf) << 16) diff --git a/drivers/isdn/act2000/act2000_isa.c b/drivers/isdn/act2000/act2000_isa.c index b363d0976465..df7923c5b843 100644 --- a/drivers/isdn/act2000/act2000_isa.c +++ b/drivers/isdn/act2000/act2000_isa.c @@ -18,13 +18,6 @@ static act2000_card *irq2card_map[16]; -static void -act2000_isa_delay(long t) -{ - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(t); -} - /* * Reset Controller, then try to read the Card's signature. + Return: @@ -419,7 +412,7 @@ act2000_isa_download(act2000_card * card, act2000_ddef __user * cb) if (!act2000_isa_reset(card->port)) return -ENXIO; - act2000_isa_delay(HZ / 2); + msleep_interruptible(500); if(copy_from_user(&cblock, cb, sizeof(cblock))) return -EFAULT; length = cblock.length; @@ -452,6 +445,6 @@ act2000_isa_download(act2000_card * card, act2000_ddef __user * cb) p += l; } kfree(buf); - act2000_isa_delay(HZ / 2); + msleep_interruptible(500); return (act2000_isa_getid(card)); } diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c index 8d58cfd45b0e..8cd4dde06a81 100644 --- a/drivers/isdn/capi/kcapi.c +++ b/drivers/isdn/capi/kcapi.c @@ -24,6 +24,7 @@ #include <linux/capi.h> #include <linux/kernelcapi.h> #include <linux/init.h> +#include <linux/delay.h> #include <asm/uaccess.h> #include <linux/isdn/capicmd.h> #include <linux/isdn/capiutil.h> @@ -831,8 +832,7 @@ static int old_capi_manufacturer(unsigned int cmd, void __user *data) while (card->cardstate != CARD_RUNNING) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/10); /* 0.1 sec */ + msleep_interruptible(100); /* 0.1 sec */ if (signal_pending(current)) { capi_ctr_put(card); @@ -856,8 +856,7 @@ static int old_capi_manufacturer(unsigned int cmd, void __user *data) while (card->cardstate > CARD_DETECTED) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/10); /* 0.1 sec */ + msleep_interruptible(100); /* 0.1 sec */ if (signal_pending(current)) return -EINTR; diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index 26bb71d96b38..15b80c844205 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -843,9 +843,8 @@ static int init_card(struct IsdnCardState *cs) } while (cnt) { cs->cardmsg(cs, CARD_INIT, NULL); - set_current_state(TASK_UNINTERRUPTIBLE); /* Timeout 10ms */ - schedule_timeout((10 * HZ) / 1000); + msleep(10); printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], cs->irq, kstat_irqs(cs->irq)); if (kstat_irqs(cs->irq) == irq_cnt) { diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c index 3b526d704fa6..21271465bd2f 100644 --- a/drivers/isdn/hisax/elsa.c +++ b/drivers/isdn/hisax/elsa.c @@ -691,8 +691,7 @@ Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); byteout(cs->hw.elsa.timer, 0); spin_unlock_irqrestore(&cs->lock, flags); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((110*HZ)/1000); + msleep(110); spin_lock_irqsave(&cs->lock, flags); cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT; byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c index 3946536d112b..c2db52696a86 100644 --- a/drivers/isdn/hisax/hfc_pci.c +++ b/drivers/isdn/hisax/hfc_pci.c @@ -1619,8 +1619,7 @@ hfcpci_card_msg(struct IsdnCardState *cs, int mt, void *arg) inithfcpci(cs); reset_hfcpci(cs); spin_unlock_irqrestore(&cs->lock, flags); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((80 * HZ) / 1000); /* Timeout 80ms */ + msleep(80); /* Timeout 80ms */ /* now switch timer interrupt off */ spin_lock_irqsave(&cs->lock, flags); cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER; diff --git a/drivers/isdn/hisax/hfc_sx.c b/drivers/isdn/hisax/hfc_sx.c index 07a07aab792b..685fcc2d7256 100644 --- a/drivers/isdn/hisax/hfc_sx.c +++ b/drivers/isdn/hisax/hfc_sx.c @@ -314,8 +314,7 @@ release_io_hfcsx(struct IsdnCardState *cs) cs->hw.hfcsx.int_m2 = 0; /* interrupt output off ! */ Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2); Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET); /* Reset On */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((30 * HZ) / 1000); /* Timeout 30ms */ + msleep(30); /* Timeout 30ms */ Write_hfc(cs, HFCSX_CIRM, 0); /* Reset Off */ del_timer(&cs->hw.hfcsx.timer); release_region(cs->hw.hfcsx.base, 2); /* release IO-Block */ @@ -1367,8 +1366,7 @@ hfcsx_card_msg(struct IsdnCardState *cs, int mt, void *arg) spin_lock_irqsave(&cs->lock, flags); inithfcsx(cs); spin_unlock_irqrestore(&cs->lock, flags); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((80 * HZ) / 1000); /* Timeout 80ms */ + msleep(80); /* Timeout 80ms */ /* now switch timer interrupt off */ spin_lock_irqsave(&cs->lock, flags); cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER; diff --git a/drivers/isdn/hisax/hfcscard.c b/drivers/isdn/hisax/hfcscard.c index ea8da997039f..6fc55fea1702 100644 --- a/drivers/isdn/hisax/hfcscard.c +++ b/drivers/isdn/hisax/hfcscard.c @@ -125,8 +125,7 @@ hfcs_card_msg(struct IsdnCardState *cs, int mt, void *arg) init2bds0(cs); spin_unlock_irqrestore(&cs->lock, flags); delay = (80*HZ)/1000 +1; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((80*HZ)/1000); + msleep(80); spin_lock_irqsave(&cs->lock, flags); cs->hw.hfcD.ctmt |= HFCD_TIM800; cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); diff --git a/drivers/isdn/hysdn/boardergo.c b/drivers/isdn/hysdn/boardergo.c index 2f2731520d40..e19a01a305a9 100644 --- a/drivers/isdn/hysdn/boardergo.c +++ b/drivers/isdn/hysdn/boardergo.c @@ -21,6 +21,7 @@ #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/vmalloc.h> +#include <linux/delay.h> #include <asm/io.h> #include "hysdn_defs.h" @@ -246,8 +247,7 @@ ergo_writebootimg(struct HYSDN_CARD *card, uchar * buf, ulong offs) /* the interrupts are still masked */ sti(); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */ + msleep_interruptible(20); /* Timeout 20ms */ if (((tDpramBootSpooler *) card->dpram)->Len != DPRAM_SPOOLER_DATA_SIZE) { if (card->debug_flags & LOG_POF_CARD) @@ -386,8 +386,7 @@ ergo_waitpofready(struct HYSDN_CARD *card) return (0); /* success */ } /* data has arrived */ sti(); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout((50 * HZ) / 1000); /* Timeout 50ms */ + msleep_interruptible(50); /* Timeout 50ms */ } /* wait until timeout */ if (card->debug_flags & LOG_POF_CARD) diff --git a/drivers/isdn/hysdn/hysdn_sched.c b/drivers/isdn/hysdn/hysdn_sched.c index 1a2015ecfda5..4fa3b01707cd 100644 --- a/drivers/isdn/hysdn/hysdn_sched.c +++ b/drivers/isdn/hysdn/hysdn_sched.c @@ -17,6 +17,7 @@ #include <linux/kernel.h> #include <linux/ioport.h> #include <linux/interrupt.h> +#include <linux/delay.h> #include <asm/io.h> #include "hysdn_defs.h" @@ -160,8 +161,7 @@ hysdn_tx_cfgline(hysdn_card * card, uchar * line, word chan) if (card->debug_flags & LOG_SCHED_ASYN) hysdn_addlog(card, "async tx-cfg delayed"); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */ + msleep_interruptible(20); /* Timeout 20ms */ if (!--cnt) { restore_flags(flags); return (-ERR_ASYNC_TIME); /* timed out */ @@ -190,8 +190,7 @@ hysdn_tx_cfgline(hysdn_card * card, uchar * line, word chan) if (card->debug_flags & LOG_SCHED_ASYN) hysdn_addlog(card, "async tx-cfg waiting for tx-ready"); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */ + msleep_interruptible(20); /* Timeout 20ms */ if (!--cnt) { restore_flags(flags); return (-ERR_ASYNC_TIME); /* timed out */ diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 921c3c2ab97a..6b6d839930f1 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -13,6 +13,7 @@ #include <linux/config.h> #include <linux/isdn.h> +#include <linux/delay.h> #include "isdn_common.h" #include "isdn_tty.h" #ifdef CONFIG_ISDN_AUDIO @@ -1748,8 +1749,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) tty->closing = 0; module_put(info->owner); if (info->blocked_open) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/2); + msleep_interruptible(500); wake_up_interruptible(&info->open_wait); } info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CLOSING); diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c index 0c256d698686..70989aa4c157 100644 --- a/drivers/isdn/icn/icn.c +++ b/drivers/isdn/icn/icn.c @@ -762,8 +762,7 @@ icn_check_loader(int cardnumber) #ifdef BOOT_DEBUG printk(KERN_DEBUG "Loader %d TO?\n", cardnumber); #endif - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(ICN_BOOT_TIMEOUT1); + msleep_interruptible(ICN_BOOT_TIMEOUT1); } else { #ifdef BOOT_DEBUG printk(KERN_DEBUG "Loader %d OK\n", cardnumber); @@ -788,8 +787,7 @@ icn_check_loader(int cardnumber) int slsec = sec; \ printk(KERN_DEBUG "SLEEP(%d)\n",slsec); \ while (slsec) { \ - current->state = TASK_INTERRUPTIBLE; \ - schedule_timeout(HZ); \ + msleep_interruptible(1000); \ slsec--; \ } \ } @@ -950,7 +948,7 @@ icn_loadproto(u_char __user * buffer, icn_card * card) icn_maprelease_channel(card, 0); return -EIO; } - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(10); } } @@ -974,8 +972,7 @@ icn_loadproto(u_char __user * buffer, icn_card * card) #ifdef BOOT_DEBUG printk(KERN_DEBUG "Proto TO?\n"); #endif - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(ICN_BOOT_TIMEOUT1); + msleep_interruptible(ICN_BOOT_TIMEOUT1); } else { if ((card->secondhalf) || (!card->doubleS0)) { #ifdef BOOT_DEBUG @@ -1271,9 +1268,9 @@ icn_command(isdn_ctrl * c, icn_card * card) if (!card->leased) { card->leased = 1; while (card->ptype == ISDN_PTYPE_UNKNOWN) { - schedule_timeout(ICN_BOOT_TIMEOUT1); + msleep_interruptible(ICN_BOOT_TIMEOUT1); } - schedule_timeout(ICN_BOOT_TIMEOUT1); + msleep_interruptible(ICN_BOOT_TIMEOUT1); sprintf(cbuf, "00;FV2ON\n01;EAZ%c\n02;EAZ%c\n", (a & 1)?'1':'C', (a & 2)?'2':'C'); i = icn_writecmd(cbuf, strlen(cbuf), 0, card); diff --git a/drivers/isdn/icn/icn.h b/drivers/isdn/icn/icn.h index 4ab15c917ed4..28548318a389 100644 --- a/drivers/isdn/icn/icn.h +++ b/drivers/isdn/icn/icn.h @@ -70,8 +70,7 @@ typedef struct icn_cdef { #define ICN_FLAGS_RUNNING 4 /* Cards driver activated */ #define ICN_FLAGS_RBTIMER 8 /* cyclic scheduling of B-Channel-poll */ -#define ICN_BOOT_TIMEOUT1 (HZ) /* Delay for Boot-download (jiffies) */ -#define ICN_CHANLOCK_DELAY (HZ/10) /* Delay for Channel-mapping (jiffies) */ +#define ICN_BOOT_TIMEOUT1 1000 /* Delay for Boot-download (msecs) */ #define ICN_TIMER_BCREAD (HZ/100) /* B-Channel poll-cycle */ #define ICN_TIMER_DCREAD (HZ/2) /* D-Channel poll-cycle */ diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c index 544f41b413ed..7f17ab1ac7ee 100644 --- a/drivers/isdn/isdnloop/isdnloop.c +++ b/drivers/isdn/isdnloop/isdnloop.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/init.h> +#include <linux/sched.h> #include "isdnloop.h" static char *revision = "$Revision: 1.11.6.7 $"; @@ -1161,8 +1162,10 @@ isdnloop_command(isdn_ctrl * c, isdnloop_card * card) if (!card->leased) { card->leased = 1; while (card->ptype == ISDN_PTYPE_UNKNOWN) { + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(10); } + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(10); sprintf(cbuf, "00;FV2ON\n01;EAZ1\n02;EAZ2\n"); i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); diff --git a/drivers/isdn/sc/card.h b/drivers/isdn/sc/card.h index bdb27fae6f20..8e44928cdf1c 100644 --- a/drivers/isdn/sc/card.h +++ b/drivers/isdn/sc/card.h @@ -24,24 +24,25 @@ * We need these if they're not already included */ #include <linux/timer.h> +#include <linux/time.h> #include <linux/isdnif.h> #include "message.h" /* * Amount of time to wait for a reset to complete */ -#define CHECKRESET_TIME milliseconds(4000) +#define CHECKRESET_TIME msecs_to_jiffies(4000) /* * Amount of time between line status checks */ -#define CHECKSTAT_TIME milliseconds(8000) +#define CHECKSTAT_TIME msecs_to_jiffies(8000) /* * The maximum amount of time to wait for a message response * to arrive. Use exclusively by send_and_receive */ -#define SAR_TIMEOUT milliseconds(10000) +#define SAR_TIMEOUT msecs_to_jiffies(10000) /* * Macro to determine is a card id is valid diff --git a/drivers/isdn/sc/hardware.h b/drivers/isdn/sc/hardware.h index adde8fb14665..9e6d5302bf8e 100644 --- a/drivers/isdn/sc/hardware.h +++ b/drivers/isdn/sc/hardware.h @@ -104,9 +104,6 @@ * Some handy macros */ -/* Return the number of jiffies in a given number of msecs */ -#define milliseconds(x) (((x)*HZ)/1000) - /* Determine if a channel number is valid for the adapter */ #define IS_VALID_CHANNEL(y,x) ((x>0) && (x <= sc_adapter[y]->channels)) diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c index 930845cff800..a10c6af42a97 100644 --- a/drivers/isdn/sc/init.c +++ b/drivers/isdn/sc/init.c @@ -7,6 +7,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/delay.h> #include "includes.h" #include "hardware.h" #include "card.h" @@ -167,8 +168,7 @@ static int __init sc_init(void) if(do_reset) { pr_debug("Doing a SAFE probe reset\n"); outb(0xFF, io[b] + RESET_OFFSET); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(milliseconds(10000)); + msleep_interruptible(10000); } pr_debug("RAM Base for board %d is 0x%x, %s probe\n", b, ram[b], ram[b] == 0 ? "will" : "won't"); @@ -500,8 +500,7 @@ int identify_board(unsigned long rambase, unsigned int iobase) * Try to identify a PRI card */ outb(PRI_BASEPG_VAL, pgport); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); + msleep_interruptible(1000); sig = readl(rambase + SIG_OFFSET); pr_debug("Looking for a signature, got 0x%x\n", sig); if(sig == SIGNATURE) @@ -511,8 +510,7 @@ int identify_board(unsigned long rambase, unsigned int iobase) * Try to identify a PRI card */ outb(BRI_BASEPG_VAL, pgport); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); + msleep_interruptible(1000); sig = readl(rambase + SIG_OFFSET); pr_debug("Looking for a signature, got 0x%x\n", sig); if(sig == SIGNATURE) diff --git a/drivers/isdn/tpam/tpam.h b/drivers/isdn/tpam/tpam.h index c1456d4b6170..da3319453cd0 100644 --- a/drivers/isdn/tpam/tpam.h +++ b/drivers/isdn/tpam/tpam.h @@ -14,6 +14,8 @@ #ifndef _TPAM_PRIV_H_ #define _TPAM_PRIV_H_ +//#define DEBUG /* uncomment if you want debugging output */ +#include <linux/kernel.h> #include <linux/isdnif.h> #include <linux/init.h> #include <linux/workqueue.h> @@ -224,13 +226,4 @@ extern void hdlc_encode_modem(u8 *, u32, u8 *, u32 *); extern void hdlc_no_accm_encode(u8 *, u32, u8 *, u32 *); extern u32 hdlc_no_accm_decode(u8 *, u32); -/* Define this to enable debug tracing prints */ -#undef DEBUG - -#ifdef DEBUG -#define dprintk printk -#else -#define dprintk while(0) printk -#endif - #endif /* _TPAM_H_ */ diff --git a/drivers/isdn/tpam/tpam_commands.c b/drivers/isdn/tpam/tpam_commands.c index 9cf6ef14fb1d..60f75184a320 100644 --- a/drivers/isdn/tpam/tpam_commands.c +++ b/drivers/isdn/tpam/tpam_commands.c @@ -45,7 +45,7 @@ int tpam_command(isdn_ctrl *c) { tpam_card *card; unsigned long argp; - dprintk("TurboPAM(tpam_command) card=%d, command=%d\n", + pr_debug("TurboPAM(tpam_command) card=%d, command=%d\n", c->driver, c->command); /* search for the board */ @@ -75,7 +75,7 @@ int tpam_command(isdn_ctrl *c) { return tpam_command_ioctl_loopmode(card, 0); default: - dprintk("TurboPAM(tpam_command): " + pr_debug("TurboPAM(tpam_command): " "invalid tpam ioctl %ld\n", c->arg); return -EINVAL; @@ -95,7 +95,7 @@ int tpam_command(isdn_ctrl *c) { case ISDN_CMD_PROCEED: return tpam_command_proceed(card, c->arg); default: - dprintk("TurboPAM(tpam_command): " + pr_debug("TurboPAM(tpam_command): " "unknown or unused isdn ioctl %d\n", c->command); return -EINVAL; @@ -117,7 +117,7 @@ int tpam_command(isdn_ctrl *c) { static int tpam_command_ioctl_dspload(tpam_card *card, u32 arg) { tpam_dsp_ioctl tdl; - dprintk("TurboPAM(tpam_command_ioctl_dspload): card=%d\n", card->id); + pr_debug("TurboPAM(tpam_command_ioctl_dspload): card=%d\n", card->id); /* get the IOCTL parameter from userspace */ if (copy_from_user(&tdl, (void __user *)arg, sizeof(tpam_dsp_ioctl))) @@ -147,7 +147,7 @@ static int tpam_command_ioctl_dspload(tpam_card *card, u32 arg) { static int tpam_command_ioctl_dspsave(tpam_card *card, u32 arg) { tpam_dsp_ioctl tdl; - dprintk("TurboPAM(tpam_command_ioctl_dspsave): card=%d\n", card->id); + pr_debug("TurboPAM(tpam_command_ioctl_dspsave): card=%d\n", card->id); /* get the IOCTL parameter from userspace */ if (copy_from_user(&tdl, (void __user *)arg, sizeof(tpam_dsp_ioctl))) @@ -178,7 +178,7 @@ static int tpam_command_ioctl_dsprun(tpam_card *card) { isdn_ctrl ctrl; struct sk_buff *skb; - dprintk("TurboPAM(tpam_command_ioctl_dsprun): card=%d\n", card->id); + pr_debug("TurboPAM(tpam_command_ioctl_dsprun): card=%d\n", card->id); /* board must _not_ be running */ if (card->running) @@ -297,7 +297,7 @@ static int tpam_command_dial(tpam_card *card, u32 channel, u8 *phone) { struct sk_buff *skb; isdn_ctrl ctrl; - dprintk("TurboPAM(tpam_command_dial): card=%d, channel=%lu, phone=%s\n", + pr_debug("TurboPAM(tpam_command_dial): card=%d, channel=%lu, phone=%s\n", card->id, (unsigned long)channel, phone); /* board must be running */ @@ -341,7 +341,7 @@ static int tpam_command_dial(tpam_card *card, u32 channel, u8 *phone) { */ static int tpam_command_setl2(tpam_card *card, u32 channel, u8 proto) { - dprintk("TurboPAM(tpam_command_setl2): card=%d, channel=%lu, proto=%d\n", + pr_debug("TurboPAM(tpam_command_setl2): card=%d, channel=%lu, proto=%d\n", card->id, (unsigned long)channel, proto); /* board must be running */ @@ -376,7 +376,7 @@ static int tpam_command_acceptd(tpam_card *card, u32 channel) { isdn_ctrl ctrl; struct sk_buff *skb; - dprintk("TurboPAM(tpam_command_acceptd): card=%d, channel=%lu\n", + pr_debug("TurboPAM(tpam_command_acceptd): card=%d, channel=%lu\n", card->id, (unsigned long)channel); /* board must be running */ @@ -410,7 +410,7 @@ static int tpam_command_acceptd(tpam_card *card, u32 channel) { static int tpam_command_acceptb(tpam_card *card, u32 channel) { isdn_ctrl ctrl; - dprintk("TurboPAM(tpam_command_acceptb): card=%d, channel=%lu\n", + pr_debug("TurboPAM(tpam_command_acceptb): card=%d, channel=%lu\n", card->id, (unsigned long)channel); /* board must be running */ @@ -437,7 +437,7 @@ static int tpam_command_acceptb(tpam_card *card, u32 channel) { static int tpam_command_hangup(tpam_card *card, u32 channel) { struct sk_buff *skb; - dprintk("TurboPAM(tpam_command_hangup): card=%d, channel=%lu\n", + pr_debug("TurboPAM(tpam_command_hangup): card=%d, channel=%lu\n", card->id, (unsigned long)channel); /* board must be running */ @@ -464,7 +464,7 @@ static int tpam_command_hangup(tpam_card *card, u32 channel) { static int tpam_command_proceed(tpam_card *card, u32 channel) { struct sk_buff *skb; - dprintk("TurboPAM(tpam_command_proceed): card=%d, channel=%lu\n", + pr_debug("TurboPAM(tpam_command_proceed): card=%d, channel=%lu\n", card->id, (unsigned long)channel); /* board must be running */ @@ -496,7 +496,7 @@ int tpam_writebuf_skb(int driverId, int channel, int ack, struct sk_buff *skb) { void *finaldata; u32 finallen; - dprintk("TurboPAM(tpam_writebuf_skb): " + pr_debug("TurboPAM(tpam_writebuf_skb): " "card=%d, channel=%ld, ack=%d, data size=%d\n", driverId, (unsigned long)channel, ack, skb->len); @@ -569,7 +569,7 @@ void tpam_recv_ACreateNCOCnf(tpam_card *card, struct sk_buff *skb) { u8 status; u32 channel; - dprintk("TurboPAM(tpam_recv_ACreateNCOCnf): card=%d\n", card->id); + pr_debug("TurboPAM(tpam_recv_ACreateNCOCnf): card=%d\n", card->id); /* parse the message contents */ if (parse_ACreateNCOCnf(skb, &status, &ncoid)) @@ -614,7 +614,7 @@ void tpam_recv_ADestroyNCOCnf(tpam_card *card, struct sk_buff *skb) { u8 status; u32 channel; - dprintk("TurboPAM(tpam_recv_ADestroyNCOCnf): card=%d\n", card->id); + pr_debug("TurboPAM(tpam_recv_ADestroyNCOCnf): card=%d\n", card->id); /* parse the message contents */ if (parse_ADestroyNCOCnf(skb, &status, &ncoid)) @@ -647,7 +647,7 @@ void tpam_recv_CConnectCnf(tpam_card *card, struct sk_buff *skb) { u32 channel; isdn_ctrl ctrl; - dprintk("TurboPAM(tpam_recv_CConnectCnf): card=%d\n", card->id); + pr_debug("TurboPAM(tpam_recv_CConnectCnf): card=%d\n", card->id); /* parse the message contents */ if (parse_CConnectCnf(skb, &ncoid)) @@ -685,7 +685,7 @@ void tpam_recv_CConnectInd(tpam_card *card, struct sk_buff *skb) { isdn_ctrl ctrl; int status; - dprintk("TurboPAM(tpam_recv_CConnectInd): card=%d\n", card->id); + pr_debug("TurboPAM(tpam_recv_CConnectInd): card=%d\n", card->id); /* parse the message contents */ if (parse_CConnectInd(skb, &ncoid, &hdlc, calling, called, &plan, &screen)) @@ -720,13 +720,13 @@ void tpam_recv_CConnectInd(tpam_card *card, struct sk_buff *skb) { case 4: /* call accepted, link layer will send us a ACCEPTD * command later */ - dprintk("TurboPAM(tpam_recv_CConnectInd): " + pr_debug("TurboPAM(tpam_recv_CConnectInd): " "card=%d, channel=%d, icall waiting, status=%d\n", card->id, channel, status); break; default: /* call denied, we build and send a CDisconnectReq */ - dprintk("TurboPAM(tpam_recv_CConnectInd): " + pr_debug("TurboPAM(tpam_recv_CConnectInd): " "card=%d, channel=%d, icall denied, status=%d\n", card->id, channel, status); skb = build_CDisconnectReq(ncoid); @@ -749,7 +749,7 @@ void tpam_recv_CDisconnectInd(tpam_card *card, struct sk_buff *skb) { u32 cause; isdn_ctrl ctrl; - dprintk("TurboPAM(tpam_recv_CDisconnectInd): card=%d\n", card->id); + pr_debug("TurboPAM(tpam_recv_CDisconnectInd): card=%d\n", card->id); /* parse the message contents */ if (parse_CDisconnectInd(skb, &ncoid, &cause)) @@ -794,7 +794,7 @@ void tpam_recv_CDisconnectCnf(tpam_card *card, struct sk_buff *skb) { u32 cause; isdn_ctrl ctrl; - dprintk("TurboPAM(tpam_recv_CDisconnectCnf): card=%d\n", card->id); + pr_debug("TurboPAM(tpam_recv_CDisconnectCnf): card=%d\n", card->id); /* parse the message contents */ if (parse_CDisconnectCnf(skb, &ncoid, &cause)) @@ -835,7 +835,7 @@ void tpam_recv_U3DataInd(tpam_card *card, struct sk_buff *skb) { u16 len; struct sk_buff *result; - dprintk("TurboPAM(tpam_recv_U3DataInd): card=%d, datalen=%d\n", + pr_debug("TurboPAM(tpam_recv_U3DataInd): card=%d, datalen=%d\n", card->id, skb->len); /* parse the message contents */ @@ -914,7 +914,7 @@ void tpam_recv_U3ReadyToReceiveInd(tpam_card *card, struct sk_buff *skb) { u32 channel; u8 ready; - dprintk("TurboPAM(tpam_recv_U3ReadyToReceiveInd): card=%d\n", card->id); + pr_debug("TurboPAM(tpam_recv_U3ReadyToReceiveInd): card=%d\n", card->id); /* parse the message contents */ if (parse_U3ReadyToReceiveInd(skb, &ncoid, &ready)) @@ -943,7 +943,7 @@ void tpam_recv_U3ReadyToReceiveInd(tpam_card *card, struct sk_buff *skb) { static void tpam_statcallb_run(unsigned long parm) { tpam_statcallb_data *ds = (tpam_statcallb_data *)parm; - dprintk("TurboPAM(tpam_statcallb_run)\n"); + pr_debug("TurboPAM(tpam_statcallb_run)\n"); (* ds->card->interface.statcallb)(&ds->ctrl); @@ -961,7 +961,7 @@ static void tpam_statcallb(tpam_card *card, isdn_ctrl ctrl) { struct timer_list *timer; tpam_statcallb_data *ds; - dprintk("TurboPAM(tpam_statcallb): card=%d\n", card->id); + pr_debug("TurboPAM(tpam_statcallb): card=%d\n", card->id); if (!(timer = (struct timer_list *) kmalloc(sizeof(struct timer_list), GFP_ATOMIC))) { diff --git a/drivers/isdn/tpam/tpam_nco.c b/drivers/isdn/tpam/tpam_nco.c index 69a6bd4ba60e..de4904f35296 100644 --- a/drivers/isdn/tpam/tpam_nco.c +++ b/drivers/isdn/tpam/tpam_nco.c @@ -84,7 +84,7 @@ struct sk_buff *build_ACreateNCOReq(const u8 *phone) { struct sk_buff *skb; u8 *tlv; - dprintk("TurboPAM(build_ACreateNCOReq): phone=%s\n", phone); + pr_debug("TurboPAM(build_ACreateNCOReq): phone=%s\n", phone); /* build the NCO packet */ if (!(skb = build_NCOpacket(ID_ACreateNCOReq, 23 + strlen(phone), 0, 0, 0))) @@ -141,7 +141,7 @@ struct sk_buff *build_ADestroyNCOReq(u32 ncoid) { struct sk_buff *skb; u8 *tlv; - dprintk("TurboPAM(build_ADestroyNCOReq): ncoid=%lu\n", + pr_debug("TurboPAM(build_ADestroyNCOReq): ncoid=%lu\n", (unsigned long)ncoid); /* build the NCO packet */ @@ -170,7 +170,7 @@ struct sk_buff *build_CConnectReq(u32 ncoid, const u8 *called, u8 hdlc) { struct sk_buff *skb; u8 *tlv; - dprintk("TurboPAM(build_CConnectReq): ncoid=%lu, called=%s, hdlc=%d\n", + pr_debug("TurboPAM(build_CConnectReq): ncoid=%lu, called=%s, hdlc=%d\n", (unsigned long)ncoid, called, hdlc); /* build the NCO packet */ @@ -220,7 +220,7 @@ struct sk_buff *build_CConnectRsp(u32 ncoid) { struct sk_buff *skb; u8 *tlv; - dprintk("TurboPAM(build_CConnectRsp): ncoid=%lu\n", + pr_debug("TurboPAM(build_CConnectRsp): ncoid=%lu\n", (unsigned long)ncoid); /* build the NCO packet */ @@ -247,7 +247,7 @@ struct sk_buff *build_CDisconnectReq(u32 ncoid) { struct sk_buff *skb; u8 *tlv; - dprintk("TurboPAM(build_CDisconnectReq): ncoid=%lu\n", + pr_debug("TurboPAM(build_CDisconnectReq): ncoid=%lu\n", (unsigned long)ncoid); /* build the NCO packet */ @@ -274,7 +274,7 @@ struct sk_buff *build_CDisconnectRsp(u32 ncoid) { struct sk_buff *skb; u8 *tlv; - dprintk("TurboPAM(build_CDisconnectRsp): ncoid=%lu\n", + pr_debug("TurboPAM(build_CDisconnectRsp): ncoid=%lu\n", (unsigned long)ncoid); /* build the NCO packet */ @@ -307,7 +307,7 @@ struct sk_buff *build_U3DataReq(u32 ncoid, void *data, u16 len, u8 *tlv; void *p; - dprintk("TurboPAM(build_U3DataReq): " + pr_debug("TurboPAM(build_U3DataReq): " "ncoid=%lu, len=%d, ack=%d, ack_size=%d\n", (unsigned long)ncoid, len, ack, ack_size); @@ -397,7 +397,7 @@ int parse_ACreateNCOCnf(struct sk_buff *skb, u8 *status, u32 *ncoid) { } if (*status) { - dprintk("TurboPAM(parse_ACreateNCOCnf): status=%d\n", *status); + pr_debug("TurboPAM(parse_ACreateNCOCnf): status=%d\n", *status); return 0; } @@ -408,7 +408,7 @@ int parse_ACreateNCOCnf(struct sk_buff *skb, u8 *status, u32 *ncoid) { return -1; } - dprintk("TurboPAM(parse_ACreateNCOCnf): ncoid=%lu, status=%d\n", + pr_debug("TurboPAM(parse_ACreateNCOCnf): ncoid=%lu, status=%d\n", (unsigned long)*ncoid, *status); return 0; } @@ -432,7 +432,7 @@ int parse_ADestroyNCOCnf(struct sk_buff *skb, u8 *status, u32 *ncoid) { } if (*status) { - dprintk("TurboPAM(parse_ADestroyNCOCnf): status=%d\n", *status); + pr_debug("TurboPAM(parse_ADestroyNCOCnf): status=%d\n", *status); return 0; } @@ -443,7 +443,7 @@ int parse_ADestroyNCOCnf(struct sk_buff *skb, u8 *status, u32 *ncoid) { return -1; } - dprintk("TurboPAM(parse_ADestroyNCOCnf): ncoid=%lu, status=%d\n", + pr_debug("TurboPAM(parse_ADestroyNCOCnf): ncoid=%lu, status=%d\n", (unsigned long)*ncoid, *status); return 0; } @@ -464,7 +464,7 @@ int parse_CConnectCnf(struct sk_buff *skb, u32 *ncoid) { "NCOID not found\n"); return -1; } - dprintk("TurboPAM(parse_CConnectCnf): ncoid=%lu\n", + pr_debug("TurboPAM(parse_CConnectCnf): ncoid=%lu\n", (unsigned long)*ncoid); return 0; } @@ -522,7 +522,7 @@ int parse_CConnectInd(struct sk_buff *skb, u32 *ncoid, u8 *hdlc, } memcpy(called, phone + 2, PHONE_MAXIMUMSIZE); - dprintk("TurboPAM(parse_CConnectInd): " + pr_debug("TurboPAM(parse_CConnectInd): " "ncoid=%lu, hdlc=%d, plan=%d, scr=%d, calling=%s, called=%s\n", (unsigned long)*ncoid, *hdlc, *plan, *screen, calling, called); return 0; @@ -553,7 +553,7 @@ int parse_CDisconnectCnf(struct sk_buff *skb, u32 *ncoid, u32 *causetopuf) { return -1; } - dprintk("TurboPAM(parse_CDisconnectCnf): ncoid=%lu, causetopuf=%lu\n", + pr_debug("TurboPAM(parse_CDisconnectCnf): ncoid=%lu, causetopuf=%lu\n", (unsigned long)*ncoid, (unsigned long)*causetopuf); return 0; } @@ -583,7 +583,7 @@ int parse_CDisconnectInd(struct sk_buff *skb, u32 *ncoid, u32 *causetopuf) { return -1; } - dprintk("TurboPAM(parse_CDisconnectInd): ncoid=%lu, causetopuf=%lu\n", + pr_debug("TurboPAM(parse_CDisconnectInd): ncoid=%lu, causetopuf=%lu\n", (unsigned long)*ncoid, (unsigned long)*causetopuf); return 0; } @@ -613,7 +613,7 @@ int parse_U3ReadyToReceiveInd(struct sk_buff *skb, u32 *ncoid, u8 *ready) { return -1; } - dprintk("TurboPAM(parse_U3ReadyToReceiveInd): ncoid=%lu, ready=%d\n", + pr_debug("TurboPAM(parse_U3ReadyToReceiveInd): ncoid=%lu, ready=%d\n", (unsigned long)*ncoid, *ready); return 0; } @@ -644,7 +644,7 @@ int parse_U3DataInd(struct sk_buff *skb, u32 *ncoid, u8 **data, u16 *len) { sizeof(skb_header) + sizeof(pci_mpb) + p->actualBlockTLVSize); *data = skb->data; - dprintk("TurboPAM(parse_U3DataInd): ncoid=%lu, datalen=%d\n", + pr_debug("TurboPAM(parse_U3DataInd): ncoid=%lu, datalen=%d\n", (unsigned long)*ncoid, *len); return 0; } diff --git a/drivers/isdn/tpam/tpam_queues.c b/drivers/isdn/tpam/tpam_queues.c index 9b55684dde0c..a9d8bb08007e 100644 --- a/drivers/isdn/tpam/tpam_queues.c +++ b/drivers/isdn/tpam/tpam_queues.c @@ -30,7 +30,7 @@ static int tpam_sendpacket(tpam_card *card, tpam_channel *channel); */ void tpam_enqueue(tpam_card *card, struct sk_buff *skb) { - dprintk("TurboPAM(tpam_enqueue): card=%d\n", card->id); + pr_debug("TurboPAM(tpam_enqueue): card=%d\n", card->id); /* queue the sk_buff on the board's send queue */ skb_queue_tail(&card->sendq, skb); @@ -49,7 +49,7 @@ void tpam_enqueue(tpam_card *card, struct sk_buff *skb) { */ void tpam_enqueue_data(tpam_channel *channel, struct sk_buff *skb) { - dprintk("TurboPAM(tpam_enqueue_data): card=%d, channel=%d\n", + pr_debug("TurboPAM(tpam_enqueue_data): card=%d, channel=%d\n", channel->card->id, channel->num); /* if existant, queue the sk_buff on the channel's send queue */ @@ -84,7 +84,7 @@ irqreturn_t tpam_irq(int irq, void *dev_id, struct pt_regs *regs) pci_mpb mpb; skb_header *skbh; - dprintk("TurboPAM(tpam_irq): IRQ received, card=%d\n", card->id); + pr_debug("TurboPAM(tpam_irq): IRQ received, card=%d\n", card->id); /* grab the board lock */ spin_lock(&card->lock); @@ -99,7 +99,7 @@ irqreturn_t tpam_irq(int irq, void *dev_id, struct pt_regs *regs) if (!ackupload) { /* it is a new message from the board */ - dprintk("TurboPAM(tpam_irq): message received, card=%d\n", + pr_debug("TurboPAM(tpam_irq): message received, card=%d\n", card->id); /* get the upload pointer */ @@ -176,7 +176,7 @@ irqreturn_t tpam_irq(int irq, void *dev_id, struct pt_regs *regs) else { /* it is a ack from the board */ - dprintk("TurboPAM(tpam_irq): message acknowledged, card=%d\n", + pr_debug("TurboPAM(tpam_irq): message acknowledged, card=%d\n", card->id); /* board is not busy anymore */ @@ -231,7 +231,7 @@ void tpam_recv_tq(tpam_card *card) { tpam_recv_U3DataInd(card, skb); break; default: - dprintk("TurboPAM(tpam_recv_tq): " + pr_debug("TurboPAM(tpam_recv_tq): " "unknown messageID %d, card=%d\n", p->messageID, card->id); break; @@ -286,13 +286,13 @@ static int tpam_sendpacket(tpam_card *card, tpam_channel *channel) { skb_header *skbh; u32 waiting_too_long; - dprintk("TurboPAM(tpam_sendpacket), card=%d, channel=%d\n", + pr_debug("TurboPAM(tpam_sendpacket), card=%d, channel=%d\n", card->id, channel ? channel->num : -1); if (channel) { /* dequeue a packet from the channel's send queue */ if (!(skb = skb_dequeue(&channel->sendq))) { - dprintk("TurboPAM(tpam_sendpacket): " + pr_debug("TurboPAM(tpam_sendpacket): " "card=%d, channel=%d, no packet\n", card->id, channel->num); return 0; @@ -301,7 +301,7 @@ static int tpam_sendpacket(tpam_card *card, tpam_channel *channel) { /* if the channel is not ready to receive, requeue the packet * and return 0 to give a chance to another channel */ if (!channel->readytoreceive) { - dprintk("TurboPAM(tpam_sendpacket): " + pr_debug("TurboPAM(tpam_sendpacket): " "card=%d, channel=%d, channel not ready\n", card->id, channel->num); skb_queue_head(&channel->sendq, skb); @@ -314,7 +314,7 @@ static int tpam_sendpacket(tpam_card *card, tpam_channel *channel) { /* if the board is busy, requeue the packet and return 1 since * there is no need to try another channel */ if (card->busy) { - dprintk("TurboPAM(tpam_sendpacket): " + pr_debug("TurboPAM(tpam_sendpacket): " "card=%d, channel=%d, card busy\n", card->id, channel->num); skb_queue_head(&channel->sendq, skb); @@ -325,7 +325,7 @@ static int tpam_sendpacket(tpam_card *card, tpam_channel *channel) { else { /* dequeue a packet from the board's send queue */ if (!(skb = skb_dequeue(&card->sendq))) { - dprintk("TurboPAM(tpam_sendpacket): " + pr_debug("TurboPAM(tpam_sendpacket): " "card=%d, no packet\n", card->id); return 0; } @@ -336,7 +336,7 @@ static int tpam_sendpacket(tpam_card *card, tpam_channel *channel) { /* if the board is busy, requeue the packet and return 1 since * there is no need to try another channel */ if (card->busy) { - dprintk("TurboPAM(tpam_sendpacket): " + pr_debug("TurboPAM(tpam_sendpacket): " "card=%d, card busy\n", card->id); skb_queue_head(&card->sendq, skb); spin_unlock_irq(&card->lock); @@ -357,7 +357,7 @@ static int tpam_sendpacket(tpam_card *card, tpam_channel *channel) { } while (hpic & 0x00000002); skbh = (skb_header *)skb->data; - dprintk("TurboPAM(tpam_sendpacket): " + pr_debug("TurboPAM(tpam_sendpacket): " "card=%d, card ready, sending %d/%d bytes\n", card->id, skbh->size, skbh->data_size); diff --git a/drivers/md/md.c b/drivers/md/md.c index 745f7e7bd3ee..10efe7942775 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -3468,8 +3468,7 @@ static void md_do_sync(mddev_t *mddev) if (currspeed > sysctl_speed_limit_min) { if ((currspeed > sysctl_speed_limit_max) || !is_mddev_idle(mddev)) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ/4); + msleep_interruptible(250); goto repeat; } } diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 1ed82ea4be8e..553e16da088c 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1013,7 +1013,7 @@ static int sync_request(mddev_t *mddev, sector_t sector_nr, int go_faster) * put in a delay to throttle resync. */ if (!go_faster && waitqueue_active(&conf->wait_resume)) - schedule_timeout(HZ); + msleep_interruptible(1000); device_barrier(conf, sector_nr + RESYNC_SECTORS); /* diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 6c3fde9ba192..0ba3a4e1831c 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1358,7 +1358,7 @@ static int sync_request(mddev_t *mddev, sector_t sector_nr, int go_faster) * put in a delay to throttle resync. */ if (!go_faster && waitqueue_active(&conf->wait_resume)) - schedule_timeout(HZ); + msleep_interruptible(1000); device_barrier(conf, sector_nr + RESYNC_SECTORS); /* Again, very different code for resync and recovery. diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c index 31412e8d32db..293173885bd9 100644 --- a/drivers/media/video/bw-qcam.c +++ b/drivers/media/video/bw-qcam.c @@ -249,8 +249,7 @@ static int qc_waithand(struct qcam_device *q, int val) if(runs++>maxpoll) { - current->state=TASK_INTERRUPTIBLE; - schedule_timeout(HZ/200); + msleep_interruptible(5); } if(runs>(maxpoll+1000)) /* 5 seconds */ return -1; @@ -269,8 +268,7 @@ static int qc_waithand(struct qcam_device *q, int val) if(runs++>maxpoll) { - current->state=TASK_INTERRUPTIBLE; - schedule_timeout(HZ/200); + msleep_interruptible(5); } if(runs++>(maxpoll+1000)) /* 5 seconds */ return -1; @@ -302,8 +300,7 @@ static unsigned int qc_waithand2(struct qcam_device *q, int val) if(runs++>maxpoll) { - current->state=TASK_INTERRUPTIBLE; - schedule_timeout(HZ/200); + msleep_interruptible(5); } if(runs++>(maxpoll+1000)) /* 5 seconds */ return 0; @@ -669,8 +666,7 @@ long qc_capture(struct qcam_device * q, char __user *buf, unsigned long len) time will be 240 / 200 = 1.2 seconds. The compile-time default is to yield every 4 lines. */ if (i >= yield) { - current->state=TASK_INTERRUPTIBLE; - schedule_timeout(HZ/200); + msleep_interruptible(5); yield = i + yieldlines; } } diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c index bd9fef419a88..703c4cba97b6 100644 --- a/drivers/media/video/c-qcam.c +++ b/drivers/media/video/c-qcam.c @@ -103,8 +103,7 @@ static unsigned int qcam_await_ready1(struct qcam_device *qcam, { if (qcam_ready1(qcam) == value) return 0; - current->state=TASK_INTERRUPTIBLE; - schedule_timeout(HZ/10); + msleep_interruptible(100); } /* Probably somebody pulled the plug out. Not much we can do. */ @@ -129,8 +128,7 @@ static unsigned int qcam_await_ready2(struct qcam_device *qcam, int value) { if (qcam_ready2(qcam) == value) return 0; - current->state=TASK_INTERRUPTIBLE; - schedule_timeout(HZ/10); + msleep_interruptible(100); } /* Probably somebody pulled the plug out. Not much we can do. */ diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c index 21db4b390277..388bc09366bd 100644 --- a/drivers/media/video/cpia.c +++ b/drivers/media/video/cpia.c @@ -37,6 +37,7 @@ #include <linux/proc_fs.h> #include <linux/ctype.h> #include <linux/pagemap.h> +#include <linux/delay.h> #include <asm/io.h> #include <asm/semaphore.h> @@ -2886,9 +2887,7 @@ static int fetch_frame(void *data) cond_resched(); /* sleep for 10 ms, hopefully ;) */ - current->state = TASK_INTERRUPTIBLE; - - schedule_timeout(10*HZ/1000); + msleep_interruptible(10); if (signal_pending(current)) return -EINTR; @@ -2951,8 +2950,7 @@ static int fetch_frame(void *data) CPIA_GRAB_SINGLE, 0, 0, 0); /* FIXME: Trial & error - need up to 70ms for the grab mode change to complete ? */ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(70*HZ / 1000); + msleep_interruptible(70); if (signal_pending(current)) return -EINTR; } @@ -3003,8 +3001,7 @@ static int goto_high_power(struct cam_data *cam) { if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) return -EIO; - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(40*HZ/1000); /* windows driver does it too */ + msleep_interruptible(40); /* windows driver does it too */ if(signal_pending(current)) return -EINTR; if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) @@ -3074,10 +3071,8 @@ static int set_camera_state(struct cam_data *cam) /* Wait 6 frames for the sensor to get all settings and AEC/ACB to settle */ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout((6*(cam->params.sensorFps.baserate ? 33 : 40) * - (1 << cam->params.sensorFps.divisor) + 10) * - HZ / 1000); + msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) * + (1 << cam->params.sensorFps.divisor) + 10); if(signal_pending(current)) return -EINTR; diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c index d88956a26aee..67f2fc4a4f9f 100644 --- a/drivers/media/video/ovcamchip/ovcamchip_core.c +++ b/drivers/media/video/ovcamchip/ovcamchip_core.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/slab.h> +#include <linux/delay.h> #include "ovcamchip_priv.h" #define DRIVER_VERSION "v2.27 for Linux 2.6" @@ -128,8 +129,7 @@ static int init_camchip(struct i2c_client *c) ov_write(c, 0x12, 0x80); /* Wait for it to initialize */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1 + 150 * HZ / 1000); + msleep(150); for (i = 0, success = 0; i < I2C_DETECT_RETRIES && !success; i++) { if (ov_read(c, GENERIC_REG_ID_HIGH, &high) >= 0) { @@ -145,8 +145,7 @@ static int init_camchip(struct i2c_client *c) ov_write(c, 0x12, 0x80); /* Wait for it to initialize */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1 + 150 * HZ / 1000); + msleep(150); /* Dummy read to sync I2C */ ov_read(c, 0x00, &low); diff --git a/drivers/media/video/planb.c b/drivers/media/video/planb.c index ebf130f167e3..3a828546952a 100644 --- a/drivers/media/video/planb.c +++ b/drivers/media/video/planb.c @@ -178,8 +178,7 @@ static unsigned char saa_status(int byte, struct planb *pb) saa_write_reg (SAA7196_STDC, saa_regs[pb->win.norm][SAA7196_STDC]); /* Let's wait 30msec for this one */ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(30 * HZ / 1000); + msleep_interruptible(30); return (unsigned char)in_8 (&planb_regs->saa_status); } diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c index d486fb7faf67..7d5ca87c1619 100644 --- a/drivers/media/video/saa5249.c +++ b/drivers/media/video/saa5249.c @@ -273,8 +273,7 @@ static void jdelay(unsigned long delay) sigfillset(¤t->blocked); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(delay); + msleep_interruptible(jiffies_to_msecs(delay)); spin_lock_irq(¤t->sighand->siglock); current->blocked = oldblocked; diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c index e46fbdbc46e6..31977e770b23 100644 --- a/drivers/media/video/tda9887.c +++ b/drivers/media/video/tda9887.c @@ -6,6 +6,7 @@ #include <linux/init.h> #include <linux/errno.h> #include <linux/slab.h> +#include <linux/delay.h> #include <media/audiochip.h> #include <media/tuner.h> @@ -543,8 +544,7 @@ static int tda9887_configure(struct tda9887 *t) printk(PREFIX "i2c i/o error: rc == %d (should be 4)\n",rc); if (debug > 2) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); + msleep_interruptible(1000); tda9887_status(t); } return 0; diff --git a/drivers/media/video/videocodec.c b/drivers/media/video/videocodec.c index 081eb06a4c7a..ae1063e09a01 100644 --- a/drivers/media/video/videocodec.c +++ b/drivers/media/video/videocodec.c @@ -43,11 +43,6 @@ #include <asm/uaccess.h> #endif -#include <linux/version.h> -#ifndef KERNEL_VERSION -#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c)) -#endif - #include "videocodec.h" static int debug = 0; diff --git a/drivers/media/video/zoran_driver.c b/drivers/media/video/zoran_driver.c index d6294bd7909f..bb2b4201a24d 100644 --- a/drivers/media/video/zoran_driver.c +++ b/drivers/media/video/zoran_driver.c @@ -1917,8 +1917,7 @@ zoran_set_norm (struct zoran *zr, decoder_command(zr, DECODER_SET_NORM, &norm); /* let changes come into effect */ - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(2 * HZ); + ssleep(2); decoder_command(zr, DECODER_GET_STATUS, &status); if (!(status & DECODER_STATUS_GOOD)) { @@ -2639,8 +2638,7 @@ zoran_do_ioctl (struct inode *inode, decoder_command(zr, DECODER_SET_NORM, &norm); /* sleep 1 second */ - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(1 * HZ); + ssleep(1); /* Get status of video decoder */ decoder_command(zr, DECODER_GET_STATUS, &status); diff --git a/drivers/media/video/zr36120.c b/drivers/media/video/zr36120.c index 9b55341baf40..e99226434e73 100644 --- a/drivers/media/video/zr36120.c +++ b/drivers/media/video/zr36120.c @@ -819,8 +819,7 @@ void zoran_close(struct video_device* dev) * be sure its safe to free the buffer. We wait 5-6 fields * which is more than sufficient to be sure. */ - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(HZ/10); /* Wait 1/10th of a second */ + msleep(100); /* Wait 1/10th of a second */ /* free the allocated framebuffer */ if (ztv->fbuffer) @@ -1568,8 +1567,7 @@ void vbi_close(struct video_device *dev) * be sure its safe to free the buffer. We wait 5-6 fields * which is more than sufficient to be sure. */ - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(HZ/10); /* Wait 1/10th of a second */ + msleep(100); /* Wait 1/10th of a second */ for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++) { diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index c467480b958c..d252c2bda137 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -2195,8 +2195,7 @@ MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag) } if (sleepFlag == CAN_SLEEP) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1 * HZ / 1000); + msleep_interruptible(1); } else { mdelay (1); /* 1 msec delay */ } @@ -2565,8 +2564,7 @@ SendIocInit(MPT_ADAPTER *ioc, int sleepFlag) state = mpt_GetIocState(ioc, 1); while (state != MPI_IOC_STATE_OPERATIONAL && --cntdn) { if (sleepFlag == CAN_SLEEP) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1 * HZ / 1000); + msleep_interruptible(1); } else { mdelay(1); } @@ -2833,8 +2831,7 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag) /* wait 1 msec */ if (sleepFlag == CAN_SLEEP) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1 * HZ / 1000); + msleep_interruptible(1); } else { mdelay (1); } @@ -2851,8 +2848,7 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag) } /* wait 1 sec */ if (sleepFlag == CAN_SLEEP) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1000 * HZ / 1000); + msleep_interruptible (1000); } else { mdelay (1000); } @@ -2952,8 +2948,7 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag) return 0; } if (sleepFlag == CAN_SLEEP) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(10 * HZ / 1000); + msleep_interruptible (10); } else { mdelay (10); } @@ -3004,8 +2999,7 @@ KickStart(MPT_ADAPTER *ioc, int force, int sleepFlag) SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, sleepFlag); if (sleepFlag == CAN_SLEEP) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1000 * HZ / 1000); + msleep_interruptible (1000); } else { mdelay (1000); } @@ -3027,8 +3021,7 @@ KickStart(MPT_ADAPTER *ioc, int force, int sleepFlag) return hard_reset_done; } if (sleepFlag == CAN_SLEEP) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(10 * HZ / 1000); + msleep_interruptible (10); } else { mdelay (10); } @@ -3099,8 +3092,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag) /* wait 100 msec */ if (sleepFlag == CAN_SLEEP) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(100 * HZ / 1000); + msleep_interruptible (100); } else { mdelay (100); } @@ -3207,8 +3199,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag) /* wait 1 sec */ if (sleepFlag == CAN_SLEEP) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1000 * HZ / 1000); + msleep_interruptible (1000); } else { mdelay (1000); } @@ -3242,8 +3233,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag) /* wait 100 msec */ if (sleepFlag == CAN_SLEEP) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(100 * HZ / 1000); + msleep_interruptible (100); } else { mdelay (100); } @@ -3337,8 +3327,7 @@ SendIocReset(MPT_ADAPTER *ioc, u8 reset_type, int sleepFlag) } if (sleepFlag == CAN_SLEEP) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1 * HZ / 1000); + msleep_interruptible(1); } else { mdelay (1); /* 1 msec delay */ } @@ -3775,8 +3764,7 @@ WaitForDoorbellAck(MPT_ADAPTER *ioc, int howlong, int sleepFlag) intstat = CHIPREG_READ32(&ioc->chip->IntStatus); if (! (intstat & MPI_HIS_IOP_DOORBELL_STATUS)) break; - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1 * HZ / 1000); + msleep_interruptible (1); count++; } } else { @@ -3825,8 +3813,7 @@ WaitForDoorbellInt(MPT_ADAPTER *ioc, int howlong, int sleepFlag) intstat = CHIPREG_READ32(&ioc->chip->IntStatus); if (intstat & MPI_HIS_DOORBELL_INTERRUPT) break; - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1 * HZ / 1000); + msleep_interruptible(1); count++; } } else { diff --git a/drivers/message/i2o/debug.c b/drivers/message/i2o/debug.c index 7227a8a1e0bd..85db8c4c58e0 100644 --- a/drivers/message/i2o/debug.c +++ b/drivers/message/i2o/debug.c @@ -54,7 +54,7 @@ void i2o_report_status(const char *severity, const char *str, if (cmd == I2O_CMD_UTIL_EVT_REGISTER) return; // No status in this reply - printk("%s%s: ", severity, str); + printk(KERN_DEBUG "%s%s: ", severity, str); if (cmd < 0x1F) // Utility cmd i2o_report_util_cmd(cmd); @@ -62,7 +62,7 @@ void i2o_report_status(const char *severity, const char *str, else if (cmd >= 0xA0 && cmd <= 0xEF) // Executive cmd i2o_report_exec_cmd(cmd); else - printk("Cmd = %0#2x, ", cmd); // Other cmds + printk(KERN_DEBUG "Cmd = %0#2x, ", cmd); // Other cmds if (msg[0] & MSG_FAIL) { i2o_report_fail_status(req_status, msg); @@ -74,7 +74,8 @@ void i2o_report_status(const char *severity, const char *str, if (cmd < 0x1F || (cmd >= 0xA0 && cmd <= 0xEF)) i2o_report_common_dsc(detailed_status); else - printk(" / DetailedStatus = %0#4x.\n", detailed_status); + printk(KERN_DEBUG " / DetailedStatus = %0#4x.\n", + detailed_status); } /* Used to dump a message to syslog during debugging */ @@ -129,20 +130,20 @@ void i2o_report_controller_unit(struct i2o_controller *c, struct i2o_device *d) printk(KERN_INFO " Class: "); //sprintf(str, "%-21s", i2o_get_class_name(d->lct_data.class_id)); - printk("%s\n", str); + printk(KERN_DEBUG "%s\n", str); printk(KERN_INFO " Subclass: 0x%04X\n", d->lct_data.sub_class); printk(KERN_INFO " Flags: "); if (d->lct_data.device_flags & (1 << 0)) - printk("C"); // ConfigDialog requested + printk(KERN_DEBUG "C"); // ConfigDialog requested if (d->lct_data.device_flags & (1 << 1)) - printk("U"); // Multi-user capable + printk(KERN_DEBUG "U"); // Multi-user capable if (!(d->lct_data.device_flags & (1 << 4))) - printk("P"); // Peer service enabled! + printk(KERN_DEBUG "P"); // Peer service enabled! if (!(d->lct_data.device_flags & (1 << 5))) - printk("M"); // Mgmt service enabled! - printk("\n"); + printk(KERN_DEBUG "M"); // Mgmt service enabled! + printk(KERN_DEBUG "\n"); } /* @@ -177,9 +178,11 @@ void i2o_report_fail_status(u8 req_status, u32 * msg) }; if (req_status == I2O_FSC_TRANSPORT_UNKNOWN_FAILURE) - printk("TRANSPORT_UNKNOWN_FAILURE (%0#2x)\n.", req_status); + printk(KERN_DEBUG "TRANSPORT_UNKNOWN_FAILURE (%0#2x)\n.", + req_status); else - printk("TRANSPORT_%s.\n", FAIL_STATUS[req_status & 0x0F]); + printk(KERN_DEBUG "TRANSPORT_%s.\n", + FAIL_STATUS[req_status & 0x0F]); /* Dump some details */ @@ -192,16 +195,17 @@ void i2o_report_fail_status(u8 req_status, u32 * msg) printk(KERN_ERR " Severity: 0x%02X ", (msg[4] >> 16) & 0xFF); if (msg[4] & (1 << 16)) - printk("(FormatError), " + printk(KERN_DEBUG "(FormatError), " "this msg can never be delivered/processed.\n"); if (msg[4] & (1 << 17)) - printk("(PathError), " + printk(KERN_DEBUG "(PathError), " "this msg can no longer be delivered/processed.\n"); if (msg[4] & (1 << 18)) - printk("(PathState), " + printk(KERN_DEBUG "(PathState), " "the system state does not allow delivery.\n"); if (msg[4] & (1 << 19)) - printk("(Congestion), resources temporarily not available;" + printk(KERN_DEBUG + "(Congestion), resources temporarily not available;" "do not retry immediately.\n"); } @@ -227,9 +231,9 @@ void i2o_report_common_status(u8 req_status) }; if (req_status >= ARRAY_SIZE(REPLY_STATUS)) - printk("RequestStatus = %0#2x", req_status); + printk(KERN_DEBUG "RequestStatus = %0#2x", req_status); else - printk("%s", REPLY_STATUS[req_status]); + printk(KERN_DEBUG "%s", REPLY_STATUS[req_status]); } /* @@ -272,9 +276,10 @@ static void i2o_report_common_dsc(u16 detailed_status) }; if (detailed_status > I2O_DSC_DEVICE_NOT_AVAILABLE) - printk(" / DetailedStatus = %0#4x.\n", detailed_status); + printk(KERN_DEBUG " / DetailedStatus = %0#4x.\n", + detailed_status); else - printk(" / %s.\n", COMMON_DSC[detailed_status]); + printk(KERN_DEBUG " / %s.\n", COMMON_DSC[detailed_status]); } /* @@ -284,49 +289,49 @@ static void i2o_report_util_cmd(u8 cmd) { switch (cmd) { case I2O_CMD_UTIL_NOP: - printk("UTIL_NOP, "); + printk(KERN_DEBUG "UTIL_NOP, "); break; case I2O_CMD_UTIL_ABORT: - printk("UTIL_ABORT, "); + printk(KERN_DEBUG "UTIL_ABORT, "); break; case I2O_CMD_UTIL_CLAIM: - printk("UTIL_CLAIM, "); + printk(KERN_DEBUG "UTIL_CLAIM, "); break; case I2O_CMD_UTIL_RELEASE: - printk("UTIL_CLAIM_RELEASE, "); + printk(KERN_DEBUG "UTIL_CLAIM_RELEASE, "); break; case I2O_CMD_UTIL_CONFIG_DIALOG: - printk("UTIL_CONFIG_DIALOG, "); + printk(KERN_DEBUG "UTIL_CONFIG_DIALOG, "); break; case I2O_CMD_UTIL_DEVICE_RESERVE: - printk("UTIL_DEVICE_RESERVE, "); + printk(KERN_DEBUG "UTIL_DEVICE_RESERVE, "); break; case I2O_CMD_UTIL_DEVICE_RELEASE: - printk("UTIL_DEVICE_RELEASE, "); + printk(KERN_DEBUG "UTIL_DEVICE_RELEASE, "); break; case I2O_CMD_UTIL_EVT_ACK: - printk("UTIL_EVENT_ACKNOWLEDGE, "); + printk(KERN_DEBUG "UTIL_EVENT_ACKNOWLEDGE, "); break; case I2O_CMD_UTIL_EVT_REGISTER: - printk("UTIL_EVENT_REGISTER, "); + printk(KERN_DEBUG "UTIL_EVENT_REGISTER, "); break; case I2O_CMD_UTIL_LOCK: - printk("UTIL_LOCK, "); + printk(KERN_DEBUG "UTIL_LOCK, "); break; case I2O_CMD_UTIL_LOCK_RELEASE: - printk("UTIL_LOCK_RELEASE, "); + printk(KERN_DEBUG "UTIL_LOCK_RELEASE, "); break; case I2O_CMD_UTIL_PARAMS_GET: - printk("UTIL_PARAMS_GET, "); + printk(KERN_DEBUG "UTIL_PARAMS_GET, "); break; case I2O_CMD_UTIL_PARAMS_SET: - printk("UTIL_PARAMS_SET, "); + printk(KERN_DEBUG "UTIL_PARAMS_SET, "); break; case I2O_CMD_UTIL_REPLY_FAULT_NOTIFY: - printk("UTIL_REPLY_FAULT_NOTIFY, "); + printk(KERN_DEBUG "UTIL_REPLY_FAULT_NOTIFY, "); break; default: - printk("Cmd = %0#2x, ", cmd); + printk(KERN_DEBUG "Cmd = %0#2x, ", cmd); } } @@ -337,106 +342,106 @@ static void i2o_report_exec_cmd(u8 cmd) { switch (cmd) { case I2O_CMD_ADAPTER_ASSIGN: - printk("EXEC_ADAPTER_ASSIGN, "); + printk(KERN_DEBUG "EXEC_ADAPTER_ASSIGN, "); break; case I2O_CMD_ADAPTER_READ: - printk("EXEC_ADAPTER_READ, "); + printk(KERN_DEBUG "EXEC_ADAPTER_READ, "); break; case I2O_CMD_ADAPTER_RELEASE: - printk("EXEC_ADAPTER_RELEASE, "); + printk(KERN_DEBUG "EXEC_ADAPTER_RELEASE, "); break; case I2O_CMD_BIOS_INFO_SET: - printk("EXEC_BIOS_INFO_SET, "); + printk(KERN_DEBUG "EXEC_BIOS_INFO_SET, "); break; case I2O_CMD_BOOT_DEVICE_SET: - printk("EXEC_BOOT_DEVICE_SET, "); + printk(KERN_DEBUG "EXEC_BOOT_DEVICE_SET, "); break; case I2O_CMD_CONFIG_VALIDATE: - printk("EXEC_CONFIG_VALIDATE, "); + printk(KERN_DEBUG "EXEC_CONFIG_VALIDATE, "); break; case I2O_CMD_CONN_SETUP: - printk("EXEC_CONN_SETUP, "); + printk(KERN_DEBUG "EXEC_CONN_SETUP, "); break; case I2O_CMD_DDM_DESTROY: - printk("EXEC_DDM_DESTROY, "); + printk(KERN_DEBUG "EXEC_DDM_DESTROY, "); break; case I2O_CMD_DDM_ENABLE: - printk("EXEC_DDM_ENABLE, "); + printk(KERN_DEBUG "EXEC_DDM_ENABLE, "); break; case I2O_CMD_DDM_QUIESCE: - printk("EXEC_DDM_QUIESCE, "); + printk(KERN_DEBUG "EXEC_DDM_QUIESCE, "); break; case I2O_CMD_DDM_RESET: - printk("EXEC_DDM_RESET, "); + printk(KERN_DEBUG "EXEC_DDM_RESET, "); break; case I2O_CMD_DDM_SUSPEND: - printk("EXEC_DDM_SUSPEND, "); + printk(KERN_DEBUG "EXEC_DDM_SUSPEND, "); break; case I2O_CMD_DEVICE_ASSIGN: - printk("EXEC_DEVICE_ASSIGN, "); + printk(KERN_DEBUG "EXEC_DEVICE_ASSIGN, "); break; case I2O_CMD_DEVICE_RELEASE: - printk("EXEC_DEVICE_RELEASE, "); + printk(KERN_DEBUG "EXEC_DEVICE_RELEASE, "); break; case I2O_CMD_HRT_GET: - printk("EXEC_HRT_GET, "); + printk(KERN_DEBUG "EXEC_HRT_GET, "); break; case I2O_CMD_ADAPTER_CLEAR: - printk("EXEC_IOP_CLEAR, "); + printk(KERN_DEBUG "EXEC_IOP_CLEAR, "); break; case I2O_CMD_ADAPTER_CONNECT: - printk("EXEC_IOP_CONNECT, "); + printk(KERN_DEBUG "EXEC_IOP_CONNECT, "); break; case I2O_CMD_ADAPTER_RESET: - printk("EXEC_IOP_RESET, "); + printk(KERN_DEBUG "EXEC_IOP_RESET, "); break; case I2O_CMD_LCT_NOTIFY: - printk("EXEC_LCT_NOTIFY, "); + printk(KERN_DEBUG "EXEC_LCT_NOTIFY, "); break; case I2O_CMD_OUTBOUND_INIT: - printk("EXEC_OUTBOUND_INIT, "); + printk(KERN_DEBUG "EXEC_OUTBOUND_INIT, "); break; case I2O_CMD_PATH_ENABLE: - printk("EXEC_PATH_ENABLE, "); + printk(KERN_DEBUG "EXEC_PATH_ENABLE, "); break; case I2O_CMD_PATH_QUIESCE: - printk("EXEC_PATH_QUIESCE, "); + printk(KERN_DEBUG "EXEC_PATH_QUIESCE, "); break; case I2O_CMD_PATH_RESET: - printk("EXEC_PATH_RESET, "); + printk(KERN_DEBUG "EXEC_PATH_RESET, "); break; case I2O_CMD_STATIC_MF_CREATE: - printk("EXEC_STATIC_MF_CREATE, "); + printk(KERN_DEBUG "EXEC_STATIC_MF_CREATE, "); break; case I2O_CMD_STATIC_MF_RELEASE: - printk("EXEC_STATIC_MF_RELEASE, "); + printk(KERN_DEBUG "EXEC_STATIC_MF_RELEASE, "); break; case I2O_CMD_STATUS_GET: - printk("EXEC_STATUS_GET, "); + printk(KERN_DEBUG "EXEC_STATUS_GET, "); break; case I2O_CMD_SW_DOWNLOAD: - printk("EXEC_SW_DOWNLOAD, "); + printk(KERN_DEBUG "EXEC_SW_DOWNLOAD, "); break; case I2O_CMD_SW_UPLOAD: - printk("EXEC_SW_UPLOAD, "); + printk(KERN_DEBUG "EXEC_SW_UPLOAD, "); break; case I2O_CMD_SW_REMOVE: - printk("EXEC_SW_REMOVE, "); + printk(KERN_DEBUG "EXEC_SW_REMOVE, "); break; case I2O_CMD_SYS_ENABLE: - printk("EXEC_SYS_ENABLE, "); + printk(KERN_DEBUG "EXEC_SYS_ENABLE, "); break; case I2O_CMD_SYS_MODIFY: - printk("EXEC_SYS_MODIFY, "); + printk(KERN_DEBUG "EXEC_SYS_MODIFY, "); break; case I2O_CMD_SYS_QUIESCE: - printk("EXEC_SYS_QUIESCE, "); + printk(KERN_DEBUG "EXEC_SYS_QUIESCE, "); break; case I2O_CMD_SYS_TAB_SET: - printk("EXEC_SYS_TAB_SET, "); + printk(KERN_DEBUG "EXEC_SYS_TAB_SET, "); break; default: - printk("Cmd = %#02x, ", cmd); + printk(KERN_DEBUG "Cmd = %#02x, ", cmd); } } @@ -445,28 +450,28 @@ void i2o_debug_state(struct i2o_controller *c) printk(KERN_INFO "%s: State = ", c->name); switch (((i2o_status_block *) c->status_block.virt)->iop_state) { case 0x01: - printk("INIT\n"); + printk(KERN_DEBUG "INIT\n"); break; case 0x02: - printk("RESET\n"); + printk(KERN_DEBUG "RESET\n"); break; case 0x04: - printk("HOLD\n"); + printk(KERN_DEBUG "HOLD\n"); break; case 0x05: - printk("READY\n"); + printk(KERN_DEBUG "READY\n"); break; case 0x08: - printk("OPERATIONAL\n"); + printk(KERN_DEBUG "OPERATIONAL\n"); break; case 0x10: - printk("FAILED\n"); + printk(KERN_DEBUG "FAILED\n"); break; case 0x11: - printk("FAULTED\n"); + printk(KERN_DEBUG "FAULTED\n"); break; default: - printk("%x (unknown !!)\n", + printk(KERN_DEBUG "%x (unknown !!)\n", ((i2o_status_block *) c->status_block.virt)->iop_state); } }; @@ -516,53 +521,58 @@ void i2o_dump_hrt(struct i2o_controller *c) d = (u8 *) (rows + 2); state = p[1] << 8 | p[0]; - printk("TID %04X:[", state & 0xFFF); + printk(KERN_DEBUG "TID %04X:[", state & 0xFFF); state >>= 12; if (state & (1 << 0)) - printk("H"); /* Hidden */ + printk(KERN_DEBUG "H"); /* Hidden */ if (state & (1 << 2)) { - printk("P"); /* Present */ + printk(KERN_DEBUG "P"); /* Present */ if (state & (1 << 1)) - printk("C"); /* Controlled */ + printk(KERN_DEBUG "C"); /* Controlled */ } if (state > 9) - printk("*"); /* Hard */ + printk(KERN_DEBUG "*"); /* Hard */ - printk("]:"); + printk(KERN_DEBUG "]:"); switch (p[3] & 0xFFFF) { case 0: /* Adapter private bus - easy */ - printk("Local bus %d: I/O at 0x%04X Mem 0x%08X", - p[2], d[1] << 8 | d[0], *(u32 *) (d + 4)); + printk(KERN_DEBUG + "Local bus %d: I/O at 0x%04X Mem 0x%08X", p[2], + d[1] << 8 | d[0], *(u32 *) (d + 4)); break; case 1: /* ISA bus */ - printk("ISA %d: CSN %d I/O at 0x%04X Mem 0x%08X", - p[2], d[2], d[1] << 8 | d[0], *(u32 *) (d + 4)); + printk(KERN_DEBUG + "ISA %d: CSN %d I/O at 0x%04X Mem 0x%08X", p[2], + d[2], d[1] << 8 | d[0], *(u32 *) (d + 4)); break; case 2: /* EISA bus */ - printk("EISA %d: Slot %d I/O at 0x%04X Mem 0x%08X", + printk(KERN_DEBUG + "EISA %d: Slot %d I/O at 0x%04X Mem 0x%08X", p[2], d[3], d[1] << 8 | d[0], *(u32 *) (d + 4)); break; case 3: /* MCA bus */ - printk("MCA %d: Slot %d I/O at 0x%04X Mem 0x%08X", - p[2], d[3], d[1] << 8 | d[0], *(u32 *) (d + 4)); + printk(KERN_DEBUG + "MCA %d: Slot %d I/O at 0x%04X Mem 0x%08X", p[2], + d[3], d[1] << 8 | d[0], *(u32 *) (d + 4)); break; case 4: /* PCI bus */ - printk("PCI %d: Bus %d Device %d Function %d", - p[2], d[2], d[1], d[0]); + printk(KERN_DEBUG + "PCI %d: Bus %d Device %d Function %d", p[2], + d[2], d[1], d[0]); break; case 0x80: /* Other */ default: - printk("Unsupported bus type."); + printk(KERN_DEBUG "Unsupported bus type."); break; } - printk("\n"); + printk(KERN_DEBUG "\n"); rows += length; } } diff --git a/drivers/message/i2o/device.c b/drivers/message/i2o/device.c index ff4822ed4df8..def6ea54416e 100644 --- a/drivers/message/i2o/device.c +++ b/drivers/message/i2o/device.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/i2o.h> +#include <linux/delay.h> /* Exec OSM functions */ extern struct bus_type i2o_bus_type; @@ -106,8 +107,7 @@ int i2o_device_claim_release(struct i2o_device *dev) if (!rc) break; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ); + ssleep(1); } if (!rc) diff --git a/drivers/message/i2o/driver.c b/drivers/message/i2o/driver.c index bc69d66c2623..1102013ab768 100644 --- a/drivers/message/i2o/driver.c +++ b/drivers/message/i2o/driver.c @@ -18,7 +18,6 @@ #include <linux/rwsem.h> #include <linux/i2o.h> - /* max_drivers - Maximum I2O drivers (OSMs) which could be registered */ unsigned int i2o_max_drivers = I2O_MAX_DRIVERS; module_param_named(max_drivers, i2o_max_drivers, uint, 0); @@ -146,7 +145,7 @@ void i2o_driver_unregister(struct i2o_driver *drv) struct i2o_device *i2o_dev; list_for_each_entry(i2o_dev, &c->devices, list) - i2o_driver_notify_device_remove(drv, i2o_dev); + i2o_driver_notify_device_remove(drv, i2o_dev); i2o_driver_notify_controller_remove(drv, c); } @@ -246,14 +245,15 @@ int i2o_driver_dispatch(struct i2o_controller *c, u32 m, * Send notifications to all registered drivers that a new controller was * added. */ -void i2o_driver_notify_controller_add_all(struct i2o_controller *c) { +void i2o_driver_notify_controller_add_all(struct i2o_controller *c) +{ int i; struct i2o_driver *drv; - for(i = 0; i < I2O_MAX_DRIVERS; i ++) { + for (i = 0; i < I2O_MAX_DRIVERS; i++) { drv = i2o_drivers[i]; - if(drv) + if (drv) i2o_driver_notify_controller_add(drv, c); } } @@ -265,14 +265,15 @@ void i2o_driver_notify_controller_add_all(struct i2o_controller *c) { * Send notifications to all registered drivers that a controller was * removed. */ -void i2o_driver_notify_controller_remove_all(struct i2o_controller *c) { +void i2o_driver_notify_controller_remove_all(struct i2o_controller *c) +{ int i; struct i2o_driver *drv; - for(i = 0; i < I2O_MAX_DRIVERS; i ++) { + for (i = 0; i < I2O_MAX_DRIVERS; i++) { drv = i2o_drivers[i]; - if(drv) + if (drv) i2o_driver_notify_controller_remove(drv, c); } } @@ -283,14 +284,15 @@ void i2o_driver_notify_controller_remove_all(struct i2o_controller *c) { * * Send notifications to all registered drivers that a device was added. */ -void i2o_driver_notify_device_add_all(struct i2o_device *i2o_dev) { +void i2o_driver_notify_device_add_all(struct i2o_device *i2o_dev) +{ int i; struct i2o_driver *drv; - for(i = 0; i < I2O_MAX_DRIVERS; i ++) { + for (i = 0; i < I2O_MAX_DRIVERS; i++) { drv = i2o_drivers[i]; - if(drv) + if (drv) i2o_driver_notify_device_add(drv, i2o_dev); } } @@ -301,14 +303,15 @@ void i2o_driver_notify_device_add_all(struct i2o_device *i2o_dev) { * * Send notifications to all registered drivers that a device was removed. */ -void i2o_driver_notify_device_remove_all(struct i2o_device *i2o_dev) { +void i2o_driver_notify_device_remove_all(struct i2o_device *i2o_dev) +{ int i; struct i2o_driver *drv; - for(i = 0; i < I2O_MAX_DRIVERS; i ++) { + for (i = 0; i < I2O_MAX_DRIVERS; i++) { drv = i2o_drivers[i]; - if(drv) + if (drv) i2o_driver_notify_device_remove(drv, i2o_dev); } } diff --git a/drivers/message/i2o/exec-osm.c b/drivers/message/i2o/exec-osm.c index 117f26106491..6d865f89642f 100644 --- a/drivers/message/i2o/exec-osm.c +++ b/drivers/message/i2o/exec-osm.c @@ -29,6 +29,7 @@ #include <linux/module.h> #include <linux/i2o.h> +#include <linux/delay.h> struct i2o_driver i2o_exec_driver; @@ -151,7 +152,7 @@ int i2o_msg_post_wait_mem(struct i2o_controller *c, u32 m, unsigned long prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE); if (!iwait->complete) - schedule_timeout(timeout * HZ); + msleep_interruptible(timeout * 1000); finish_wait(&wq, &wait); @@ -322,13 +323,13 @@ static void i2o_exec_lct_modified(struct i2o_controller *c) static int i2o_exec_reply(struct i2o_controller *c, u32 m, struct i2o_message *msg) { - if (readl(&msg->u.head[0]) & MSG_FAIL) { // Fail bit is set + if (le32_to_cpu(msg->u.head[0]) & MSG_FAIL) { // Fail bit is set struct i2o_message *pmsg; /* preserved message */ u32 pm; - pm = readl(&msg->body[3]); + pm = le32_to_cpu(msg->body[3]); - pmsg = c->in_queue.virt + pm; + pmsg = i2o_msg_in_to_virt(c, pm); i2o_report_status(KERN_INFO, "i2o_core", msg); @@ -339,10 +340,10 @@ static int i2o_exec_reply(struct i2o_controller *c, u32 m, return -1; } - if (readl(&msg->u.s.tcntxt) & 0x80000000) + if (le32_to_cpu(msg->u.s.tcntxt) & 0x80000000) return i2o_msg_post_wait_complete(c, m, msg); - if ((readl(&msg->u.head[1]) >> 24) == I2O_CMD_LCT_NOTIFY) { + if ((le32_to_cpu(msg->u.head[1]) >> 24) == I2O_CMD_LCT_NOTIFY) { struct work_struct *work; pr_debug("%s: LCT notify received\n", c->name); diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c index 102fb83164c0..04080c80ba37 100644 --- a/drivers/message/i2o/i2o_block.c +++ b/drivers/message/i2o/i2o_block.c @@ -416,11 +416,10 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m, unsigned long flags; /* FAILed message */ - if (unlikely(readl(&msg->u.head[0]) & (1 << 13))) { + if (unlikely(le32_to_cpu(msg->u.head[0]) & (1 << 13))) { struct i2o_message *pmsg; u32 pm; - printk(KERN_WARNING "FAIL"); /* * FAILed message from controller * We increment the error count and abort it @@ -431,10 +430,10 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m, * better be on the safe side since no one really follows * the spec to the book :) */ - pm = readl(&msg->body[3]); - pmsg = c->in_queue.virt + pm; + pm = le32_to_cpu(msg->body[3]); + pmsg = i2o_msg_in_to_virt(c, pm); - req = i2o_cntxt_list_get(c, readl(&pmsg->u.s.tcntxt)); + req = i2o_cntxt_list_get(c, le32_to_cpu(pmsg->u.s.tcntxt)); if (unlikely(!req)) { printk(KERN_ERR "block-osm: NULL reply received!\n"); return -1; @@ -449,7 +448,7 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m, spin_lock_irqsave(q->queue_lock, flags); while (end_that_request_chunk(req, !req->errors, - readl(&pmsg->body[1]))) ; + le32_to_cpu(pmsg->body[1]))) ; end_that_request_last(req); dev->open_queue_depth--; @@ -464,7 +463,7 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m, return -1; } - req = i2o_cntxt_list_get(c, readl(&msg->u.s.tcntxt)); + req = i2o_cntxt_list_get(c, le32_to_cpu(msg->u.s.tcntxt)); if (unlikely(!req)) { printk(KERN_ERR "block-osm: NULL reply received!\n"); return -1; @@ -487,7 +486,7 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m, "I2O Block: Data transfer to deleted device!\n"); spin_lock_irqsave(q->queue_lock, flags); while (end_that_request_chunk - (req, !req->errors, readl(&msg->body[1]))) ; + (req, !req->errors, le32_to_cpu(msg->body[1]))) ; end_that_request_last(req); dev->open_queue_depth--; @@ -503,7 +502,7 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m, * request in the context. */ - st = readl(&msg->body[0]) >> 24; + st = le32_to_cpu(msg->body[0]) >> 24; if (st != 0) { int err; @@ -524,7 +523,7 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m, "Volume has changed, waiting for acknowledgement" }; - err = readl(&msg->body[0]) & 0xffff; + err = le32_to_cpu(msg->body[0]) & 0xffff; /* * Device not ready means two things. One is that the @@ -538,17 +537,18 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m, * Don't stick a supertrak100 into cache aggressive modes */ - printk(KERN_ERR "\n/dev/%s error: %s", dev->gd->disk_name, - bsa_errors[readl(&msg->body[0]) & 0xffff]); - if (readl(&msg->body[0]) & 0x00ff0000) - printk(" - DDM attempted %d retries", - (readl(&msg->body[0]) >> 16) & 0x00ff); - printk(".\n"); + printk(KERN_ERR "/dev/%s error: %s", dev->gd->disk_name, + bsa_errors[le32_to_cpu(msg->body[0]) & 0xffff]); + if (le32_to_cpu(msg->body[0]) & 0x00ff0000) + printk(KERN_ERR " - DDM attempted %d retries", + (le32_to_cpu(msg->body[0]) >> 16) & 0x00ff); + printk(KERN_ERR ".\n"); req->errors++; } else req->errors = 0; - if (!end_that_request_chunk(req, !req->errors, readl(&msg->body[1]))) { + if (!end_that_request_chunk + (req, !req->errors, le32_to_cpu(msg->body[1]))) { add_disk_randomness(req->rq_disk); spin_lock_irqsave(q->queue_lock, flags); @@ -563,7 +563,7 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m, i2o_block_sglist_free(ireq); i2o_block_request_free(ireq); } else - printk(KERN_ERR "still remaining chunks\n"); + printk(KERN_ERR "i2o_block: still remaining chunks\n"); return 1; }; @@ -573,174 +573,6 @@ static void i2o_block_event(struct i2o_event *evt) printk(KERN_INFO "block-osm: event received\n"); }; -#if 0 -static int i2o_block_event(void *dummy) -{ - unsigned int evt; - unsigned long flags; - struct i2o_block_device *dev; - int unit; - //The only event that has data is the SCSI_SMART event. - struct i2o_reply { - u32 header[4]; - u32 evt_indicator; - u8 ASC; - u8 ASCQ; - u16 pad; - u8 data[16]; - } *evt_local; - - daemonize("i2oblock"); - allow_signal(SIGKILL); - - evt_running = 1; - - while (1) { - if (down_interruptible(&i2ob_evt_sem)) { - evt_running = 0; - printk("exiting..."); - break; - } - - /* - * Keep another CPU/interrupt from overwriting the - * message while we're reading it - * - * We stuffed the unit in the TxContext and grab the event mask - * None of the BSA we care about events have EventData - */ - spin_lock_irqsave(&i2ob_evt_lock, flags); - evt_local = (struct i2o_reply *)evt_msg; - spin_unlock_irqrestore(&i2ob_evt_lock, flags); - - unit = le32_to_cpu(evt_local->header[3]); - evt = le32_to_cpu(evt_local->evt_indicator); - - dev = &i2o_blk_dev[unit]; - switch (evt) { - /* - * New volume loaded on same TID, so we just re-install. - * The TID/controller don't change as it is the same - * I2O device. It's just new media that we have to - * rescan. - */ - case I2O_EVT_IND_BSA_VOLUME_LOAD: - { - i2ob_install_device(dev->i2o_device->iop, - dev->i2o_device, unit); - add_disk(dev->gendisk); - break; - } - - /* - * No media, so set all parameters to 0 and set the media - * change flag. The I2O device is still valid, just doesn't - * have media, so we don't want to clear the controller or - * device pointer. - */ - case I2O_EVT_IND_BSA_VOLUME_UNLOAD: - { - struct gendisk *p = dev->gendisk; - blk_queue_max_sectors(dev->gendisk->queue, 0); - del_gendisk(p); - put_disk(p); - dev->gendisk = NULL; - dev->media_change_flag = 1; - break; - } - - case I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ: - printk(KERN_WARNING - "%s: Attempt to eject locked media\n", - dev->i2o_device->dev_name); - break; - - /* - * The capacity has changed and we are going to be - * updating the max_sectors and other information - * about this disk. We try a revalidate first. If - * the block device is in use, we don't want to - * do that as there may be I/Os bound for the disk - * at the moment. In that case we read the size - * from the device and update the information ourselves - * and the user can later force a partition table - * update through an ioctl. - */ - case I2O_EVT_IND_BSA_CAPACITY_CHANGE: - { - u64 size; - - if (i2ob_query_device(dev, 0x0004, 0, &size, 8) - != 0) - i2ob_query_device(dev, 0x0000, 4, &size, - 8); - - spin_lock_irqsave(dev->req_queue->queue_lock, - flags); - set_capacity(dev->gendisk, size >> 9); - spin_unlock_irqrestore(dev->req_queue-> - queue_lock, flags); - break; - } - - /* - * We got a SCSI SMART event, we just log the relevant - * information and let the user decide what they want - * to do with the information. - */ - case I2O_EVT_IND_BSA_SCSI_SMART: - { - char buf[16]; - printk(KERN_INFO - "I2O Block: %s received a SCSI SMART Event\n", - dev->i2o_device->dev_name); - evt_local->data[16] = '\0'; - sprintf(buf, "%s", &evt_local->data[0]); - printk(KERN_INFO " Disk Serial#:%s\n", - buf); - printk(KERN_INFO " ASC 0x%02x \n", - evt_local->ASC); - printk(KERN_INFO " ASCQ 0x%02x \n", - evt_local->ASCQ); - break; - } - - /* - * Non event - */ - - case 0: - break; - - /* - * An event we didn't ask for. Call the card manufacturer - * and tell them to fix their firmware :) - */ - - case 0x20: - /* - * If a promise card reports 0x20 event then the brown stuff - * hit the fan big time. The card seems to recover but loses - * the pending writes. Deeply ungood except for testing fsck - */ - if (dev->i2o_device->iop->promise) - panic - ("I2O controller firmware failed. Reboot and force a filesystem check.\n"); - default: - printk(KERN_INFO - "%s: Received event 0x%X we didn't register for\n" - KERN_INFO - " Blame the I2O card manufacturer 8)\n", - dev->i2o_device->dev_name, evt); - break; - } - }; - - complete_and_exit(&i2ob_thread_dead, 0); - return 0; -} -#endif - /* * SCSI-CAM for ioctl geometry mapping * Duplicated with SCSI - this should be moved into somewhere common diff --git a/drivers/message/i2o/i2o_config.c b/drivers/message/i2o/i2o_config.c index 4c240102a50e..353762860251 100644 --- a/drivers/message/i2o/i2o_config.c +++ b/drivers/message/i2o/i2o_config.c @@ -74,96 +74,6 @@ struct i2o_cfg_info { static struct i2o_cfg_info *open_files = NULL; static ulong i2o_cfg_info_id = 0; -#if 0 -/* - * This is the callback for any message we have posted. The message itself - * will be returned to the message pool when we return from the IRQ - * - * This runs in irq context so be short and sweet. - */ -static void i2o_cfg_reply(struct i2o_handler *h, struct i2o_controller *c, - struct i2o_message *m) -{ - u32 *msg = (u32 *) m; - - if (msg[0] & MSG_FAIL) { - u32 *preserved_msg = (u32 *) (c->msg_virt + msg[7]); - - printk(KERN_ERR "i2o_config: IOP failed to process the msg.\n"); - - /* Release the preserved msg frame by resubmitting it as a NOP */ - - preserved_msg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; - preserved_msg[1] = I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0; - preserved_msg[2] = 0; - i2o_post_message(c, msg[7]); - } - - if (msg[4] >> 24) // ReqStatus != SUCCESS - i2o_report_status(KERN_INFO, "i2o_config", msg); - - if (m->function == I2O_CMD_UTIL_EVT_REGISTER) { - struct i2o_cfg_info *inf; - - for (inf = open_files; inf; inf = inf->next) - if (inf->q_id == i2o_cntxt_list_get(c, msg[3])) - break; - - // - // If this is the case, it means that we're getting - // events for a file descriptor that's been close()'d - // w/o the user unregistering for events first. - // The code currently assumes that the user will - // take care of unregistering for events before closing - // a file. - // - // TODO: - // Should we track event registartion and deregister - // for events when a file is close()'d so this doesn't - // happen? That would get rid of the search through - // the linked list since file->private_data could point - // directly to the i2o_config_info data structure...but - // it would mean having all sorts of tables to track - // what each file is registered for...I think the - // current method is simpler. - DS - // - if (!inf) - return; - - inf->event_q[inf->q_in].id.iop = c->unit; - inf->event_q[inf->q_in].id.tid = m->target_tid; - inf->event_q[inf->q_in].id.evt_mask = msg[4]; - - // - // Data size = msg size - reply header - // - inf->event_q[inf->q_in].data_size = (m->size - 5) * 4; - if (inf->event_q[inf->q_in].data_size) - memcpy(inf->event_q[inf->q_in].evt_data, - (unsigned char *)(msg + 5), - inf->event_q[inf->q_in].data_size); - - spin_lock(&i2o_config_lock); - MODINC(inf->q_in, I2O_EVT_Q_LEN); - if (inf->q_len == I2O_EVT_Q_LEN) { - MODINC(inf->q_out, I2O_EVT_Q_LEN); - inf->q_lost++; - } else { - // Keep I2OEVTGET on another CPU from touching this - inf->q_len++; - } - spin_unlock(&i2o_config_lock); - -// printk(KERN_INFO "File %p w/id %d has %d events\n", -// inf->fp, inf->q_id, inf->q_len); - - kill_fasync(&inf->fasync, SIGIO, POLL_IN); - } - - return; -} -#endif - /* * Each of these describes an i2o message handler. They are * multiplexed by the i2o_core code @@ -388,7 +298,7 @@ static int i2o_cfg_swdl(unsigned long arg) writel(0xD0000000 | fragsize, &msg->body[3]); writel(buffer.phys, &msg->body[4]); -// printk("i2o_config: swdl frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); +// printk(KERN_INFO "i2o_config: swdl frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); status = i2o_msg_post_wait_mem(c, m, 60, &buffer); if (status != -ETIMEDOUT) @@ -461,7 +371,7 @@ static int i2o_cfg_swul(unsigned long arg) writel(0xD0000000 | fragsize, &msg->body[3]); writel(buffer.phys, &msg->body[4]); -// printk("i2o_config: swul frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); +// printk(KERN_INFO "i2o_config: swul frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); status = i2o_msg_post_wait_mem(c, m, 60, &buffer); if (status != I2O_POST_WAIT_OK) { diff --git a/drivers/message/i2o/i2o_proc.c b/drivers/message/i2o/i2o_proc.c index a535c7a1f66f..25e9e3df3075 100644 --- a/drivers/message/i2o/i2o_proc.c +++ b/drivers/message/i2o/i2o_proc.c @@ -938,11 +938,6 @@ int i2o_seq_show_drivers_stored(struct seq_file *seq, void *v) seq_printf(seq, " "); } -#if 0 - if (c->i2oversion == 0x02) - seq_printf(seq, "%-d", dst->module_state); -#endif - seq_printf(seq, "%-#7x", dst->i2o_vendor_id); seq_printf(seq, "%-#8x", dst->module_id); seq_printf(seq, "%-29s", chtostr(dst->module_name_version, 28)); @@ -950,10 +945,6 @@ int i2o_seq_show_drivers_stored(struct seq_file *seq, void *v) seq_printf(seq, "%8d ", dst->module_size); seq_printf(seq, "%8d ", dst->mpb_size); seq_printf(seq, "0x%04x", dst->module_flags); -#if 0 - if (c->i2oversion == 0x02) - seq_printf(seq, "%d", dst->notification_level); -#endif seq_printf(seq, "\n"); } diff --git a/drivers/message/i2o/i2o_scsi.c b/drivers/message/i2o/i2o_scsi.c index 9be68f380b1b..7186d004ec1d 100644 --- a/drivers/message/i2o/i2o_scsi.c +++ b/drivers/message/i2o/i2o_scsi.c @@ -274,53 +274,6 @@ static const char *i2o_scsi_info(struct Scsi_Host *SChost) return hostdata->iop->name; } -#if 0 -/** - * i2o_retry_run - retry on timeout - * @f: unused - * - * Retry congested frames. This actually needs pushing down into - * i2o core. We should only bother the OSM with this when we can't - * queue and retry the frame. Or perhaps we should call the OSM - * and its default handler should be this in the core, and this - * call a 2nd "I give up" handler in the OSM ? - */ - -static void i2o_retry_run(unsigned long f) -{ - int i; - unsigned long flags; - - spin_lock_irqsave(&retry_lock, flags); - for (i = 0; i < retry_ct; i++) - i2o_post_message(retry_ctrl[i], virt_to_bus(retry[i])); - retry_ct = 0; - spin_unlock_irqrestore(&retry_lock, flags); -} - -/** - * flush_pending - empty the retry queue - * - * Turn each of the pending commands into a NOP and post it back - * to the controller to clear it. - */ - -static void flush_pending(void) -{ - int i; - unsigned long flags; - - spin_lock_irqsave(&retry_lock, flags); - for (i = 0; i < retry_ct; i++) { - retry[i][0] &= ~0xFFFFFF; - retry[i][0] |= I2O_CMD_UTIL_NOP << 24; - i2o_post_message(retry_ctrl[i], virt_to_bus(retry[i])); - } - retry_ct = 0; - spin_unlock_irqrestore(&retry_lock, flags); -} -#endif - /** * i2o_scsi_reply - SCSI OSM message reply handler * @c: controller issuing the reply @@ -343,38 +296,41 @@ static int i2o_scsi_reply(struct i2o_controller *c, u32 m, struct device *dev; u8 as, ds, st; - cmd = i2o_cntxt_list_get(c, readl(&msg->u.s.tcntxt)); + cmd = i2o_cntxt_list_get(c, le32_to_cpu(msg->u.s.tcntxt)); if (msg->u.head[0] & (1 << 13)) { struct i2o_message *pmsg; /* preserved message */ u32 pm; + int err = DID_ERROR; - pm = readl(&msg->body[3]); + pm = le32_to_cpu(msg->body[3]); - pmsg = c->in_queue.virt + pm; + pmsg = i2o_msg_in_to_virt(c, pm); - printk("IOP fail.\n"); - printk("From %d To %d Cmd %d.\n", + printk(KERN_ERR "IOP fail.\n"); + printk(KERN_ERR "From %d To %d Cmd %d.\n", (msg->u.head[1] >> 12) & 0xFFF, msg->u.head[1] & 0xFFF, msg->u.head[1] >> 24); - printk("Failure Code %d.\n", msg->body[0] >> 24); + printk(KERN_ERR "Failure Code %d.\n", msg->body[0] >> 24); if (msg->body[0] & (1 << 16)) - printk("Format error.\n"); + printk(KERN_ERR "Format error.\n"); if (msg->body[0] & (1 << 17)) - printk("Path error.\n"); + printk(KERN_ERR "Path error.\n"); if (msg->body[0] & (1 << 18)) - printk("Path State.\n"); + printk(KERN_ERR "Path State.\n"); if (msg->body[0] & (1 << 18)) - printk("Congestion.\n"); + { + printk(KERN_ERR "Congestion.\n"); + err = DID_BUS_BUSY; + } - printk("Failing message is %p.\n", pmsg); + printk(KERN_DEBUG "Failing message is %p.\n", pmsg); cmd = i2o_cntxt_list_get(c, readl(&pmsg->u.s.tcntxt)); if (!cmd) return 1; - printk("Aborted %ld\n", cmd->serial_number); - cmd->result = DID_ERROR << 16; + cmd->result = err << 16; cmd->scsi_done(cmd); /* Now flush the message by making it a NOP */ @@ -387,9 +343,9 @@ static int i2o_scsi_reply(struct i2o_controller *c, u32 m, * Low byte is device status, next is adapter status, * (then one byte reserved), then request status. */ - ds = (u8) readl(&msg->body[0]); - as = (u8) (readl(&msg->body[0]) >> 8); - st = (u8) (readl(&msg->body[0]) >> 24); + ds = (u8) le32_to_cpu(msg->body[0]); + as = (u8) (le32_to_cpu(msg->body[0]) >> 8); + st = (u8) (le32_to_cpu(msg->body[0]) >> 24); /* * Is this a control request coming back - eg an abort ? @@ -398,7 +354,7 @@ static int i2o_scsi_reply(struct i2o_controller *c, u32 m, if (!cmd) { if (st) printk(KERN_WARNING "SCSI abort: %08X", - readl(&msg->body[0])); + le32_to_cpu(msg->body[0])); printk(KERN_INFO "SCSI abort completed.\n"); return -EFAULT; } @@ -411,21 +367,22 @@ static int i2o_scsi_reply(struct i2o_controller *c, u32 m, switch (st) { case 0x06: - count = readl(&msg->body[1]); + count = le32_to_cpu(msg->body[1]); if (count < cmd->underflow) { int i; printk(KERN_ERR "SCSI: underflow 0x%08X 0x%08X" "\n", count, cmd->underflow); - printk("Cmd: "); + printk(KERN_DEBUG "Cmd: "); for (i = 0; i < 15; i++) - printk("%02X ", cmd->cmnd[i]); - printk(".\n"); + printk(KERN_DEBUG "%02X ", + cmd->cmnd[i]); + printk(KERN_DEBUG ".\n"); cmd->result = (DID_ERROR << 16); } break; default: - error = readl(&msg->body[0]); + error = le32_to_cpu(msg->body[0]); printk(KERN_ERR "scsi-osm: SCSI error %08x\n", error); @@ -517,8 +474,7 @@ void i2o_scsi_notify_controller_add(struct i2o_controller *c) rc = scsi_add_host(i2o_shost->scsi_host, &c->device); if (rc) { - printk(KERN_ERR "scsi-osm: Could not add SCSI " - "host\n"); + printk(KERN_ERR "scsi-osm: Could not add SCSI " "host\n"); scsi_host_put(i2o_shost->scsi_host); return; } diff --git a/drivers/message/i2o/iop.c b/drivers/message/i2o/iop.c index 699723e3a3c3..46fa7dfd6eb1 100644 --- a/drivers/message/i2o/iop.c +++ b/drivers/message/i2o/iop.c @@ -27,6 +27,7 @@ #include <linux/module.h> #include <linux/i2o.h> +#include <linux/delay.h> /* global I2O controller list */ LIST_HEAD(i2o_controllers); @@ -117,7 +118,7 @@ u32 i2o_msg_get_wait(struct i2o_controller *c, struct i2o_message **msg, * * Returns context id > 0 on success or 0 on failure. */ -u32 i2o_cntxt_list_add(struct i2o_controller *c, void *ptr) +u32 i2o_cntxt_list_add(struct i2o_controller * c, void *ptr) { struct i2o_context_list_element *entry; unsigned long flags; @@ -162,7 +163,7 @@ u32 i2o_cntxt_list_add(struct i2o_controller *c, void *ptr) * * Returns context id on succes or 0 on failure. */ -u32 i2o_cntxt_list_remove(struct i2o_controller *c, void *ptr) +u32 i2o_cntxt_list_remove(struct i2o_controller * c, void *ptr) { struct i2o_context_list_element *entry; u32 context = 0; @@ -470,7 +471,7 @@ static int i2o_iop_reset(struct i2o_controller *c) if (m == I2O_QUEUE_EMPTY) return -ETIMEDOUT; - memset(status, 0, 4); + memset(status, 0, 8); /* Quiesce all IOPs first */ i2o_iop_quiesce_all(); @@ -495,6 +496,13 @@ static int i2o_iop_reset(struct i2o_controller *c) rc = -ETIMEDOUT; goto exit; } + + /* Promise bug */ + if (status[1] || status[4]) { + *status = 0; + break; + } + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); @@ -605,6 +613,7 @@ int i2o_iop_init_outbound_queue(struct i2o_controller *c) /* Post frames */ for (i = 0; i < NMBR_MSG_FRAMES; i++) { i2o_flush_reply(c, m); + udelay(1); /* Promise */ m += MSG_FRAME_SIZE * 4; } @@ -612,6 +621,23 @@ int i2o_iop_init_outbound_queue(struct i2o_controller *c) } /** + * i2o_iop_send_nop - send a core NOP message + * @c: controller + * + * Send a no-operation message with a reply set to cause no + * action either. Needed for bringing up promise controllers. + */ +static int i2o_iop_send_nop(struct i2o_controller *c) +{ + struct i2o_message *msg; + u32 m = i2o_msg_get_wait(c, &msg, HZ); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + i2o_msg_nop(c, m); + return 0; +} + +/** * i2o_iop_activate - Bring controller up to HOLD * @c: controller * @@ -622,8 +648,27 @@ int i2o_iop_init_outbound_queue(struct i2o_controller *c) */ static int i2o_iop_activate(struct i2o_controller *c) { + struct pci_dev *i960 = NULL; i2o_status_block *sb = c->status_block.virt; int rc; + + if (c->promise) { + /* Beat up the hardware first of all */ + i960 = + pci_find_slot(c->pdev->bus->number, + PCI_DEVFN(PCI_SLOT(c->pdev->devfn), 0)); + if (i960) + pci_write_config_word(i960, 0x42, 0); + + /* Follow this sequence precisely or the controller + ceases to perform useful functions until reboot */ + if ((rc = i2o_iop_send_nop(c))) + return rc; + + if ((rc = i2o_iop_reset(c))) + return rc; + } + /* In INIT state, Wait Inbound Q to initialize (in i2o_status_get) */ /* In READY state, Get status */ @@ -659,13 +704,22 @@ static int i2o_iop_activate(struct i2o_controller *c) if (rc) return rc; + if (c->promise) { + if ((rc = i2o_iop_send_nop(c))) + return rc; + + if ((rc = i2o_status_get(c))) + return rc; + + if (i960) + pci_write_config_word(i960, 0x42, 0x3FF); + } + /* In HOLD state */ rc = i2o_hrt_get(c); - if (rc) - return rc; - return 0; + return rc; }; /** @@ -691,10 +745,11 @@ static int i2o_iop_systab_set(struct i2o_controller *c) res->flags = IORESOURCE_MEM; res->start = 0; res->end = 0; - printk("%s: requires private memory resources.\n", c->name); + printk(KERN_INFO "%s: requires private memory resources.\n", + c->name); root = pci_find_parent_resource(c->pdev, res); if (root == NULL) - printk("Can't find parent resource!\n"); + printk(KERN_WARNING "Can't find parent resource!\n"); if (root && allocate_resource(root, res, sb->desired_mem_size, sb->desired_mem_size, sb->desired_mem_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */ NULL, NULL) >= 0) { c->mem_alloc = 1; @@ -712,10 +767,11 @@ static int i2o_iop_systab_set(struct i2o_controller *c) res->flags = IORESOURCE_IO; res->start = 0; res->end = 0; - printk("%s: requires private memory resources.\n", c->name); + printk(KERN_INFO "%s: requires private memory resources.\n", + c->name); root = pci_find_parent_resource(c->pdev, res); if (root == NULL) - printk("Can't find parent resource!\n"); + printk(KERN_WARNING "Can't find parent resource!\n"); if (root && allocate_resource(root, res, sb->desired_io_size, sb->desired_io_size, sb->desired_io_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */ NULL, NULL) >= 0) { c->io_alloc = 1; diff --git a/drivers/message/i2o/pci.c b/drivers/message/i2o/pci.c index 9ee58b6cf55b..f98849abbe2a 100644 --- a/drivers/message/i2o/pci.c +++ b/drivers/message/i2o/pci.c @@ -138,13 +138,13 @@ static int __devinit i2o_pci_alloc(struct i2o_controller *c) * If we know what card it is, set the size * correctly. Code is taken from dpt_i2o.c */ - if(pdev->device == 0xa501) { - if(pdev->subsystem_device >= 0xc032 && - pdev->subsystem_device <= 0xc03b) { - if(c->base.len > 0x400000) + if (pdev->device == 0xa501) { + if (pdev->subsystem_device >= 0xc032 && + pdev->subsystem_device <= 0xc03b) { + if (c->base.len > 0x400000) c->base.len = 0x400000; } else { - if(c->base.len > 0x100000) + if (c->base.len > 0x100000) c->base.len = 0x100000; } } @@ -231,7 +231,7 @@ static int __devinit i2o_pci_alloc(struct i2o_controller *c) } #endif - if (i2o_dma_alloc(dev, &c->status, 4, GFP_KERNEL)) { + if (i2o_dma_alloc(dev, &c->status, 8, GFP_KERNEL)) { i2o_pci_free(c); return -ENOMEM; } @@ -277,7 +277,6 @@ static irqreturn_t i2o_pci_interrupt(int irq, void *dev_id, struct pt_regs *r) struct device *dev = &c->pdev->dev; struct i2o_message *m; u32 mv; - u32 *msg; /* * Old 960 steppings had a bug in the I2O unit that caused @@ -298,11 +297,7 @@ static irqreturn_t i2o_pci_interrupt(int irq, void *dev_id, struct pt_regs *r) * Because bus_to_virt is deprecated, we have calculate the * location by ourself! */ - m = (struct i2o_message *)(mv - - (unsigned long)c->out_queue.phys + - (unsigned long)c->out_queue.virt); - - msg = (u32 *) m; + m = i2o_msg_out_to_virt(c, mv); /* * Ensure this message is seen coherently but cachably by diff --git a/drivers/net/bsd_comp.c b/drivers/net/bsd_comp.c index 409b75442bbf..3d88ad622bdb 100644 --- a/drivers/net/bsd_comp.c +++ b/drivers/net/bsd_comp.c @@ -1160,7 +1160,7 @@ static struct compressor ppp_bsd_compress = { * Module support routines *************************************************************/ -int __init bsdcomp_init(void) +static int __init bsdcomp_init(void) { int answer = ppp_register_compressor(&ppp_bsd_compress); if (answer == 0) @@ -1168,7 +1168,7 @@ int __init bsdcomp_init(void) return answer; } -void __exit bsdcomp_cleanup(void) +static void __exit bsdcomp_cleanup(void) { ppp_unregister_compressor(&ppp_bsd_compress); } diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c index 4beb7ac2a861..94191fdb8e13 100644 --- a/drivers/net/hamradio/dmascc.c +++ b/drivers/net/hamradio/dmascc.c @@ -37,7 +37,6 @@ #include <linux/rtnetlink.h> #include <linux/sockios.h> #include <linux/workqueue.h> -#include <linux/version.h> #include <asm/atomic.h> #include <asm/bitops.h> #include <asm/dma.h> diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c index 81282d94f91d..5f26d9ff30e9 100644 --- a/drivers/net/irda/stir4200.c +++ b/drivers/net/irda/stir4200.c @@ -168,6 +168,7 @@ enum StirTestMask { struct stir_cb { struct usb_device *usbdev; /* init: probe_irda */ + struct usb_interface *usbintf; struct net_device *netdev; /* network layer */ struct irlap_cb *irlap; /* The link layer we are binded to */ struct net_device_stats stats; /* network statistics */ @@ -508,6 +509,7 @@ static int change_speed(struct stir_cb *stir, unsigned speed) { int i, err; __u8 mode; + int rc; for (i = 0; i < ARRAY_SIZE(stir_modes); ++i) { if (speed == stir_modes[i].speed) @@ -521,7 +523,14 @@ static int change_speed(struct stir_cb *stir, unsigned speed) pr_debug("speed change from %d to %d\n", stir->speed, speed); /* sometimes needed to get chip out of stuck state */ + rc = usb_lock_device_for_reset(stir->usbdev, stir->usbintf); + if (rc < 0) { + err = rc; + goto out; + } err = usb_reset_device(stir->usbdev); + if (rc) + usb_unlock_device(stir->usbdev); if (err) goto out; @@ -1066,6 +1075,7 @@ static int stir_probe(struct usb_interface *intf, stir = net->priv; stir->netdev = net; stir->usbdev = dev; + stir->usbintf = intf; ret = usb_reset_configuration(dev); if (ret != 0) { diff --git a/drivers/net/mac89x0.c b/drivers/net/mac89x0.c index 8c08255899ea..f65b0db111b8 100644 --- a/drivers/net/mac89x0.c +++ b/drivers/net/mac89x0.c @@ -98,6 +98,7 @@ static char *version = #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> +#include <linux/delay.h> #include <asm/system.h> #include <asm/bitops.h> @@ -308,8 +309,7 @@ void __init reset_chip(struct net_device *dev) writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET); /* wait 30 ms */ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(30*HZ/1000); + msleep_interruptible(30); /* Wait until the chip is reset */ reset_start_time = jiffies; diff --git a/drivers/net/ppp_deflate.c b/drivers/net/ppp_deflate.c index c51291a3c40a..df75c94e1d0c 100644 --- a/drivers/net/ppp_deflate.c +++ b/drivers/net/ppp_deflate.c @@ -636,7 +636,7 @@ struct compressor ppp_deflate_draft = { .owner = THIS_MODULE }; -int __init deflate_init(void) +static int __init deflate_init(void) { int answer = ppp_register_compressor(&ppp_deflate); if (answer == 0) @@ -646,7 +646,7 @@ int __init deflate_init(void) return answer; } -void __exit deflate_cleanup(void) +static void __exit deflate_cleanup(void) { ppp_unregister_compressor(&ppp_deflate); ppp_unregister_compressor(&ppp_deflate_draft); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index faaf5d04c05b..cf2b3ee2ce3f 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -827,6 +827,236 @@ static void __init quirk_sis_96x_smbus(struct pci_dev *dev) pci_read_config_byte(dev, 0x77, &val); } + +#define UHCI_USBLEGSUP 0xc0 /* legacy support */ +#define UHCI_USBCMD 0 /* command register */ +#define UHCI_USBSTS 2 /* status register */ +#define UHCI_USBINTR 4 /* interrupt register */ +#define UHCI_USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ +#define UHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */ +#define UHCI_USBCMD_GRESET (1 << 2) /* Global reset */ +#define UHCI_USBCMD_CONFIGURE (1 << 6) /* config semaphore */ +#define UHCI_USBSTS_HALTED (1 << 5) /* HCHalted bit */ + +#define OHCI_CONTROL 0x04 +#define OHCI_CMDSTATUS 0x08 +#define OHCI_INTRSTATUS 0x0c +#define OHCI_INTRENABLE 0x10 +#define OHCI_INTRDISABLE 0x14 +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ + +#define EHCI_HCC_PARAMS 0x08 /* extended capabilities */ +#define EHCI_USBCMD 0 /* command register */ +#define EHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */ +#define EHCI_USBSTS 4 /* status register */ +#define EHCI_USBSTS_HALTED (1 << 12) /* HCHalted bit */ +#define EHCI_USBINTR 8 /* interrupt register */ +#define EHCI_USBLEGSUP 0 /* legacy support register */ +#define EHCI_USBLEGSUP_BIOS (1 << 16) /* BIOS semaphore */ +#define EHCI_USBLEGSUP_OS (1 << 24) /* OS semaphore */ +#define EHCI_USBLEGCTLSTS 4 /* legacy control/status */ +#define EHCI_USBLEGCTLSTS_SOOE (1 << 13) /* SMI on ownership change */ + +int usb_early_handoff __initdata = 0; +static int __init usb_handoff_early(char *str) +{ + usb_early_handoff = 1; + return 0; +} +__setup("usb-handoff", usb_handoff_early); + +static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev) +{ + unsigned long base = 0; + int wait_time, delta; + u16 val, sts; + int i; + + for (i = 0; i < PCI_ROM_RESOURCE; i++) + if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) { + base = pci_resource_start(pdev, i); + break; + } + + if (!base) + return; + + /* + * stop controller + */ + sts = inw(base + UHCI_USBSTS); + val = inw(base + UHCI_USBCMD); + val &= ~(u16)(UHCI_USBCMD_RUN | UHCI_USBCMD_CONFIGURE); + outw(val, base + UHCI_USBCMD); + + /* + * wait while it stops if it was running + */ + if ((sts & UHCI_USBSTS_HALTED) == 0) + { + wait_time = 1000; + delta = 100; + + do { + outw(0x1f, base + UHCI_USBSTS); + udelay(delta); + wait_time -= delta; + val = inw(base + UHCI_USBSTS); + if (val & UHCI_USBSTS_HALTED) + break; + } while (wait_time > 0); + } + + /* + * disable interrupts & legacy support + */ + outw(0, base + UHCI_USBINTR); + outw(0x1f, base + UHCI_USBSTS); + pci_read_config_word(pdev, UHCI_USBLEGSUP, &val); + if (val & 0xbf) + pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_DEFAULT); + +} + +static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) +{ + void __iomem *base; + int wait_time; + + base = ioremap_nocache(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (base == NULL) return; + + if (readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) { + wait_time = 500; /* 0.5 seconds */ + writel(OHCI_INTR_OC, base + OHCI_INTRENABLE); + writel(OHCI_OCR, base + OHCI_CMDSTATUS); + while (wait_time > 0 && + readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) { + wait_time -= 10; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ*10 + 999) / 1000); + } + } + + /* + * disable interrupts + */ + writel(~(u32)0, base + OHCI_INTRDISABLE); + writel(~(u32)0, base + OHCI_INTRSTATUS); + + iounmap(base); +} + +static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) +{ + int wait_time, delta; + void __iomem *base, *op_reg_base; + u32 hcc_params, val, temp; + u8 cap_length; + + base = ioremap_nocache(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (base == NULL) return; + + cap_length = readb(base); + op_reg_base = base + cap_length; + hcc_params = readl(base + EHCI_HCC_PARAMS); + hcc_params = (hcc_params >> 8) & 0xff; + if (hcc_params) { + pci_read_config_dword(pdev, + hcc_params + EHCI_USBLEGSUP, + &val); + if (((val & 0xff) == 1) && (val & EHCI_USBLEGSUP_BIOS)) { + /* + * Ok, BIOS is in smm mode, try to hand off... + */ + pci_read_config_dword(pdev, + hcc_params + EHCI_USBLEGCTLSTS, + &temp); + pci_write_config_dword(pdev, + hcc_params + EHCI_USBLEGCTLSTS, + temp | EHCI_USBLEGCTLSTS_SOOE); + val |= EHCI_USBLEGSUP_OS; + pci_write_config_dword(pdev, + hcc_params + EHCI_USBLEGSUP, + val); + + wait_time = 500; + do { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ*10+999)/1000); + wait_time -= 10; + pci_read_config_dword(pdev, + hcc_params + EHCI_USBLEGSUP, + &val); + } while (wait_time && (val & EHCI_USBLEGSUP_BIOS)); + if (!wait_time) { + /* + * well, possibly buggy BIOS... + */ + printk(KERN_WARNING "EHCI early BIOS handoff " + "failed (BIOS bug ?)\n"); + pci_write_config_dword(pdev, + hcc_params + EHCI_USBLEGSUP, + EHCI_USBLEGSUP_OS); + pci_write_config_dword(pdev, + hcc_params + EHCI_USBLEGCTLSTS, + 0); + } + } + } + + /* + * halt EHCI & disable its interrupts in any case + */ + val = readl(op_reg_base + EHCI_USBSTS); + if ((val & EHCI_USBSTS_HALTED) == 0) { + val = readl(op_reg_base + EHCI_USBCMD); + val &= ~EHCI_USBCMD_RUN; + writel(val, op_reg_base + EHCI_USBCMD); + + wait_time = 2000; + delta = 100; + do { + writel(0x3f, op_reg_base + EHCI_USBSTS); + udelay(delta); + wait_time -= delta; + val = readl(op_reg_base + EHCI_USBSTS); + if ((val == ~(u32)0) || (val & EHCI_USBSTS_HALTED)) { + break; + } + } while (wait_time > 0); + } + writel(0, op_reg_base + EHCI_USBINTR); + writel(0x3f, op_reg_base + EHCI_USBSTS); + + iounmap(base); + + return; +} + + + +static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) +{ + if (!usb_early_handoff) + return; + + if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x00)) { /* UHCI */ + quirk_usb_handoff_uhci(pdev); + } else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x10)) { /* OHCI */ + quirk_usb_handoff_ohci(pdev); + } else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x20)) { /* EHCI */ + quirk_usb_disable_ehci(pdev); + } + + return; +} +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff); + /* * ... This is further complicated by the fact that some SiS96x south * bridges pretend to be 85C503/5513 instead. In that case see if we diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 2249b78487bd..b3714fbd0083 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1595,8 +1595,8 @@ dasd_alloc_queue(struct dasd_device * device) device->request_queue->queuedata = device; #if 0 - elevator_exit(device->request_queue); - rc = elevator_init(device->request_queue, &elevator_noop); + elevator_exit(device->request_queue->elevator); + rc = elevator_init(device->request_queue, "noop"); if (rc) { blk_cleanup_queue(device->request_queue); return rc; diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c index b7f4e7b8be74..1efc9f21229e 100644 --- a/drivers/s390/char/tape_block.c +++ b/drivers/s390/char/tape_block.c @@ -225,8 +225,8 @@ tapeblock_setup_device(struct tape_device * device) if (!blkdat->request_queue) return -ENOMEM; - elevator_exit(blkdat->request_queue); - rc = elevator_init(blkdat->request_queue, &elevator_noop); + elevator_exit(blkdat->request_queue->elevator); + rc = elevator_init(blkdat->request_queue, "noop"); if (rc) goto cleanup_queue; diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index b6c76b5680a2..da06adf48834 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -377,6 +377,7 @@ static int sr_init_command(struct scsi_cmnd * SCpnt) return 0; SCpnt->cmnd[0] = WRITE_10; SCpnt->sc_data_direction = DMA_TO_DEVICE; + cd->cdi.media_written = 1; } else if (rq_data_dir(SCpnt->request) == READ) { SCpnt->cmnd[0] = READ_10; SCpnt->sc_data_direction = DMA_FROM_DEVICE; @@ -875,10 +876,10 @@ static void get_capabilities(struct scsi_cd *cd) cd->cdi.mask |= CDC_CLOSE_TRAY; */ /* - * if DVD-RAM of MRW-W, we are randomly writeable + * if DVD-RAM, MRW-W or CD-RW, we are randomly writable */ - if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM)) != - (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM)) { + if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) != + (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) { cd->device->writeable = 1; } diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 0e805e4f3aa1..7bb3fa4c38b6 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -7,7 +7,7 @@ menu "USB support" # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. config USB tristate "Support for Host-side USB" - depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_LH7A404 + depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_LH7A404 || PXA27x ---help--- Universal Serial Bus (USB) is a specification for a serial bus subsystem which offers higher speeds and more features than the @@ -91,6 +91,8 @@ source "drivers/usb/serial/Kconfig" source "drivers/usb/misc/Kconfig" +source "drivers/usb/atm/Kconfig" + source "drivers/usb/gadget/Kconfig" endmenu diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index e3787f35cd21..63a7f2d80f83 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -62,8 +62,10 @@ obj-$(CONFIG_USB_LCD) += misc/ obj-$(CONFIG_USB_LED) += misc/ obj-$(CONFIG_USB_LEGOTOWER) += misc/ obj-$(CONFIG_USB_RIO500) += misc/ -obj-$(CONFIG_USB_SPEEDTOUCH) += misc/ obj-$(CONFIG_USB_TEST) += misc/ obj-$(CONFIG_USB_TIGL) += misc/ obj-$(CONFIG_USB_USS720) += misc/ obj-$(CONFIG_USB_PHIDGETSERVO) += misc/ + +obj-$(CONFIG_USB_ATM) += atm/ +obj-$(CONFIG_USB_SPEEDTOUCH) += atm/ diff --git a/drivers/usb/atm/Kconfig b/drivers/usb/atm/Kconfig new file mode 100644 index 000000000000..0d9f5379b8cf --- /dev/null +++ b/drivers/usb/atm/Kconfig @@ -0,0 +1,30 @@ +# +# USB ATM driver configuration +# +comment "USB ATM/DSL drivers" + depends on USB + +config USB_ATM + tristate "Generic USB ATM/DSL core I/O 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. + + To compile this driver as a module, choose M here: the + module will be called usb_atm. + +config USB_SPEEDTOUCH + tristate "Alcatel Speedtouch USB support" + depends on USB && ATM + select USB_ATM + help + Say Y here if you have an Alcatel 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. diff --git a/drivers/usb/atm/Makefile b/drivers/usb/atm/Makefile new file mode 100644 index 000000000000..9213b8b97587 --- /dev/null +++ b/drivers/usb/atm/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the rest of the USB drivers +# (the ones that don't fit into any other categories) +# + +obj-$(CONFIG_USB_ATM) += usb_atm.o +obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c new file mode 100644 index 000000000000..f17e576d12ec --- /dev/null +++ b/drivers/usb/atm/speedtch.c @@ -0,0 +1,866 @@ +/****************************************************************************** + * speedtch.c - Alcatel SpeedTouch USB xDSL modem driver + * + * Copyright (C) 2001, Alcatel + * Copyright (C) 2003, Duncan Sands + * 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/module.h> +#include <linux/moduleparam.h> +#include <linux/gfp.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/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" + +/* +#define DEBUG +#define VERBOSE_DEBUG +*/ + +#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) +# define DEBUG +#endif + +#include <linux/usb.h> + +#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) +# define USE_FW_LOADER +#endif + +#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 "Alcatel SpeedTouch USB driver version " DRIVER_VERSION + +static const char speedtch_driver_name[] = "speedtch"; + +#define SPEEDTOUCH_VENDORID 0x06b9 +#define SPEEDTOUCH_PRODUCTID 0x4061 + +/* Timeout in jiffies */ +#define CTRL_TIMEOUT (2*HZ) +#define DATA_TIMEOUT (2*HZ) + +#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 + +static int dl_512_first = 0; +static int sw_buffering = 0; + +module_param(dl_512_first, bool, 0444); +MODULE_PARM_DESC(dl_512_first, "Read 512 bytes before sending firmware"); + +module_param(sw_buffering, uint, 0444); +MODULE_PARM_DESC(sw_buffering, "Enable software buffering"); + +#define UDSL_IOCTL_LINE_UP 1 +#define UDSL_IOCTL_LINE_DOWN 2 + +#define SPEEDTCH_ENDPOINT_INT 0x81 +#define SPEEDTCH_ENDPOINT_DATA 0x07 +#define SPEEDTCH_ENDPOINT_FIRMWARE 0x05 + +#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) ) + +static struct usb_device_id speedtch_usb_ids[] = { + {USB_DEVICE(SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, speedtch_usb_ids); + +struct speedtch_instance_data { + struct udsl_instance_data u; + + /* Status */ + 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, +}; + +/*************** +** firmware ** +***************/ + +static void speedtch_got_firmware(struct speedtch_instance_data *instance, + int got_it) +{ + 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; + 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; +} + +static void speedtch_test_sequence(struct speedtch_instance_data *instance) +{ + struct usb_device *dev = instance->u.usb_dev; + unsigned char buf[10]; + 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); + if (ret < 0) + printk(KERN_WARNING "%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); + if (ret < 0) + printk(KERN_WARNING "%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); + if (ret < 0) + printk(KERN_WARNING "%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); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB150: %d\n", __func__, ret); +} + +static int speedtch_start_synchro(struct speedtch_instance_data *instance) +{ + struct usb_device *dev = instance->u.usb_dev; + unsigned char buf[2]; + int ret; + + 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; + } + + dbg("speedtch_start_synchro: modem prodded. %d Bytes returned: %02x %02x", ret, buf[0], buf[1]); + return 0; +} + +static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs) +{ + struct speedtch_instance_data *instance = urb->context; + unsigned int count = urb->actual_length; + int ret; + + /* 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 }; + + 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 (count < 6) { + dbg("%s - int packet too short", __func__); + goto exit; + } + + 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; + + printk(KERN_DEBUG "Unknown interrupt packet of %d bytes:", count); + for (i = 0; i < count; i++) + printk(" %02x", instance->int_data[i]); + printk("\n"); + } + schedule_work(&instance->poll_work); + + exit: + rmb(); + if (!instance->int_urb) + return; + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + err("%s - usb_submit_urb failed with result %d", __func__, ret); +} + +static int speedtch_get_status(struct speedtch_instance_data *instance, + unsigned char *buf) +{ + struct usb_device *dev = instance->u.usb_dev; + int ret; + + memset(buf, 0, TOTAL); + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7, + CTRL_TIMEOUT); + if (ret < 0) { + dbg("MSG 7 failed"); + return ret; + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b, + CTRL_TIMEOUT); + if (ret < 0) { + dbg("MSG B failed"); + return ret; + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d, + CTRL_TIMEOUT); + if (ret < 0) { + dbg("MSG D failed"); + return ret; + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e, + CTRL_TIMEOUT); + if (ret < 0) { + dbg("MSG E failed"); + return ret; + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f, + CTRL_TIMEOUT); + if (ret < 0) { + dbg("MSG F failed"); + return ret; + } + + return 0; +} + +static void speedtch_poll_status(struct speedtch_instance_data *instance) +{ + unsigned char buf[TOTAL]; + int ret; + + ret = speedtch_get_status(instance, buf); + if (ret) { + printk(KERN_WARNING + "SpeedTouch: Error %d fetching device status\n", ret); + return; + } + + dbg("Line state %02x", 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"); + } + 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"); + } + 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"); + } + break; + + case 0x20: + if (instance->u.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)) { + 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); + } + 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]); + } + break; + } +} + +static void speedtch_timer_poll(unsigned long data) +{ + struct speedtch_instance_data *instance = (void *)data; + + schedule_work(&instance->poll_work); + mod_timer(&instance->poll_timer, jiffies + (5 * HZ)); +} + +#ifdef USE_FW_LOADER +static void speedtch_upload_firmware(struct speedtch_instance_data *instance, + const struct firmware *fw1, + const struct firmware *fw2) +{ + 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, 2 * HZ); + + 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); + + ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, thislen, &actual_length, DATA_TIMEOUT); + + if (ret < 0) { + dbg("speedtch_upload_firmware: write BLOCK1 to modem failed (%d)!", ret); + goto fail_release; + } + dbg("speedtch_upload_firmware: BLOCK1 uploaded (%d bytes)", fw1->size); + } + + /* USB led blinking green, ADSL led off */ + + /* URB 11 */ + 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 BLOCK2 from modem failed (%d)!", ret); + goto fail_release; + } + 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); + + ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, thislen, &actual_length, DATA_TIMEOUT); + + if (ret < 0) { + dbg("speedtch_upload_firmware: write BLOCK3 to modem failed (%d)!", ret); + goto fail_release; + } + } + dbg("speedtch_upload_firmware: BLOCK3 uploaded (%d 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); +} + +static int speedtch_find_firmware(struct speedtch_instance_data + *instance, int phase, + const struct firmware **fw_p) +{ + char buf[24]; + const u16 bcdDevice = 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); + + 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); + + 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; + } + } + } + + 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); + + daemonize("firmware/speedtch"); + + if (!speedtch_find_firmware(instance, 1, &fw1)) { + if (!speedtch_find_firmware(instance, 2, &fw2)) { + speedtch_upload_firmware(instance, fw1, fw2); + release_firmware(fw2); + } + 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); + + 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) +{ +#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; + } + + instance->u.status = UDSL_LOADING_FIRMWARE; + up(&instance->u.serialize); + +#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); + + if (ret >= 0) + return; /* OK */ + + dbg("speedtch_firmware_start: kernel_thread failed (%d)!", ret); + + module_put(THIS_MODULE); + udsl_put_instance(&instance->u); + /* Just pretend it never happened... hope modem_run happens */ +#endif /* USE_FW_LOADER */ + + speedtch_got_firmware(instance, 0); +} + +static int speedtch_firmware_wait(struct udsl_instance_data *instance) +{ + speedtch_firmware_start((void *)instance); + + if (wait_event_interruptible(instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0) + return -ERESTARTSYS; + + return (instance->status == UDSL_LOADED_FIRMWARE) ? 0 : -EAGAIN; +} + +/********** +** USB ** +**********/ + +static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code, + void *user_data) +{ + struct speedtch_instance_data *instance = usb_get_intfdata(intf); + + dbg("speedtch_usb_ioctl entered"); + + if (!instance) { + dbg("speedtch_usb_ioctl: NULL instance!"); + 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; + } +} + +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]; + + dbg("speedtch_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d", dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); + + if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) || + (dev->descriptor.idVendor != SPEEDTOUCH_VENDORID) || + (dev->descriptor.idProduct != SPEEDTOUCH_PRODUCTID) || (ifnum != 1)) + return -ENODEV; + + dbg("speedtch_usb_probe: device accepted"); + + /* instance init */ + instance = kmalloc(sizeof(*instance), GFP_KERNEL); + if (!instance) { + dbg("speedtch_usb_probe: no memory for instance data!"); + return -ENOMEM; + } + + memset(instance, 0, sizeof(struct speedtch_instance_data)); + + if ((ret = usb_set_interface(dev, 0, 0)) < 0) + goto fail; + + if ((ret = usb_set_interface(dev, 2, 0)) < 0) + goto fail; + + instance->u.data_endpoint = SPEEDTCH_ENDPOINT_DATA; + instance->u.firmware_wait = speedtch_firmware_wait; + instance->u.driver_name = speedtch_driver_name; + + ret = udsl_instance_setup(dev, &instance->u); + if (ret) + goto fail; + + init_timer(&instance->poll_timer); + instance->poll_timer.function = speedtch_timer_poll; + instance->poll_timer.data = (unsigned long)instance; + + INIT_WORK(&instance->poll_work, (void *)speedtch_poll_status, instance); + + /* 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])); + } + + /* 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, HZ / 2); + + 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_set_intfdata(intf, instance); + + return 0; + + fail: + kfree(instance); + + return -ENOMEM; +} + +static void speedtch_usb_disconnect(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; + } + +/*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); + } + + 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); +} + +/*********** +** init ** +***********/ + +static int __init speedtch_usb_init(void) +{ + dbg("speedtch_usb_init: driver version " DRIVER_VERSION); + + return usb_register(&speedtch_usb_driver); +} + +static void __exit speedtch_usb_cleanup(void) +{ + dbg("speedtch_usb_cleanup entered"); + + usb_deregister(&speedtch_usb_driver); +} + +module_init(speedtch_usb_init); +module_exit(speedtch_usb_cleanup); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/usb/atm/usb_atm.c b/drivers/usb/atm/usb_atm.c new file mode 100644 index 000000000000..8d1d365846a1 --- /dev/null +++ b/drivers/usb/atm/usb_atm.c @@ -0,0 +1,1205 @@ +/****************************************************************************** + * 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" + +/* +#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 + +#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 "Alcatel SpeedTouch USB driver 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; + + 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; + 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; + } + + 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 new file mode 100644 index 000000000000..219763cc3242 --- /dev/null +++ b/drivers/usb/atm/usb_atm.h @@ -0,0 +1,159 @@ +/****************************************************************************** + * 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/list.h> +#include <linux/usb.h> +#include <linux/kref.h> +#include <linux/atm.h> +#include <linux/atmdev.h> +#include <asm/semaphore.h> + +#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/class/Kconfig b/drivers/usb/class/Kconfig index 3d49e9d8cb67..0561d0234f23 100644 --- a/drivers/usb/class/Kconfig +++ b/drivers/usb/class/Kconfig @@ -9,7 +9,8 @@ config USB_AUDIO depends on USB && SOUND help Say Y here if you want to connect USB audio equipment such as - speakers to your computer's USB port. + speakers to your computer's USB port. You only need this if you use + the OSS sound driver; ALSA has its own option for usb audio support. To compile this driver as a module, choose M here: the module will be called audio. diff --git a/drivers/usb/class/audio.c b/drivers/usb/class/audio.c index 17a6dd74713f..88628d85dff2 100644 --- a/drivers/usb/class/audio.c +++ b/drivers/usb/class/audio.c @@ -635,13 +635,13 @@ static void usbin_stop(struct usb_audiodev *as) spin_unlock_irqrestore(&as->lock, flags); if (notkilled && signal_pending(current)) { if (i & FLG_URB0RUNNING) - usb_unlink_urb(u->durb[0].urb); + usb_kill_urb(u->durb[0].urb); if (i & FLG_URB1RUNNING) - usb_unlink_urb(u->durb[1].urb); + usb_kill_urb(u->durb[1].urb); if (i & FLG_SYNC0RUNNING) - usb_unlink_urb(u->surb[0].urb); + usb_kill_urb(u->surb[0].urb); if (i & FLG_SYNC1RUNNING) - usb_unlink_urb(u->surb[1].urb); + usb_kill_urb(u->surb[1].urb); notkilled = 0; } } @@ -1114,13 +1114,13 @@ static void usbout_stop(struct usb_audiodev *as) spin_unlock_irqrestore(&as->lock, flags); if (notkilled && signal_pending(current)) { if (i & FLG_URB0RUNNING) - usb_unlink_urb(u->durb[0].urb); + usb_kill_urb(u->durb[0].urb); if (i & FLG_URB1RUNNING) - usb_unlink_urb(u->durb[1].urb); + usb_kill_urb(u->durb[1].urb); if (i & FLG_SYNC0RUNNING) - usb_unlink_urb(u->surb[0].urb); + usb_kill_urb(u->surb[0].urb); if (i & FLG_SYNC1RUNNING) - usb_unlink_urb(u->surb[1].urb); + usb_kill_urb(u->surb[1].urb); notkilled = 0; } } @@ -1949,15 +1949,12 @@ static inline int prog_dmabuf_out(struct usb_audiodev *as) static int usb_audio_open_mixdev(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); - struct list_head *devs, *mdevs; struct usb_mixerdev *ms; struct usb_audio_state *s; down(&open_sem); - list_for_each(devs, &audiodevs) { - s = list_entry(devs, struct usb_audio_state, audiodev); - list_for_each(mdevs, &s->mixerlist) { - ms = list_entry(mdevs, struct usb_mixerdev, list); + list_for_each_entry(s, &audiodevs, audiodev) { + list_for_each_entry(ms, &s->mixerlist, list) { if (ms->dev_mixer == minor) goto mixer_found; } @@ -2634,16 +2631,13 @@ static int usb_audio_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); DECLARE_WAITQUEUE(wait, current); - struct list_head *devs, *adevs; struct usb_audiodev *as; struct usb_audio_state *s; for (;;) { down(&open_sem); - list_for_each(devs, &audiodevs) { - s = list_entry(devs, struct usb_audio_state, audiodev); - list_for_each(adevs, &s->audiolist) { - as = list_entry(adevs, struct usb_audiodev, list); + list_for_each_entry(s, &audiodevs, audiodev) { + list_for_each_entry(as, &s->audiolist, list) { if (!((as->dev_audio ^ minor) & ~0xf)) goto device_found; } @@ -3809,7 +3803,6 @@ static int usb_audio_probe(struct usb_interface *intf, static void usb_audio_disconnect(struct usb_interface *intf) { struct usb_audio_state *s = usb_get_intfdata (intf); - struct list_head *list; struct usb_audiodev *as; struct usb_mixerdev *ms; @@ -3831,8 +3824,7 @@ static void usb_audio_disconnect(struct usb_interface *intf) usb_set_intfdata (intf, NULL); /* deregister all audio and mixer devices, so no new processes can open this device */ - list_for_each(list, &s->audiolist) { - as = list_entry(list, struct usb_audiodev, list); + list_for_each_entry(as, &s->audiolist, list) { usbin_disc(as); usbout_disc(as); wake_up(&as->usbin.dma.wait); @@ -3843,8 +3835,7 @@ static void usb_audio_disconnect(struct usb_interface *intf) } as->dev_audio = -1; } - list_for_each(list, &s->mixerlist) { - ms = list_entry(list, struct usb_mixerdev, list); + list_for_each_entry(ms, &s->mixerlist, list) { if (ms->dev_mixer >= 0) { unregister_sound_mixer(ms->dev_mixer); printk(KERN_INFO "usbaudio: unregister mixer 14,%d\n", ms->dev_mixer); diff --git a/drivers/usb/class/bluetty.c b/drivers/usb/class/bluetty.c index 4d8e01895ce9..6baf68badc06 100644 --- a/drivers/usb/class/bluetty.c +++ b/drivers/usb/class/bluetty.c @@ -426,8 +426,8 @@ static void bluetooth_close (struct tty_struct *tty, struct file * filp) bluetooth->open_count = 0; /* shutdown any in-flight urbs that we know about */ - usb_unlink_urb (bluetooth->read_urb); - usb_unlink_urb (bluetooth->interrupt_in_urb); + usb_kill_urb (bluetooth->read_urb); + usb_kill_urb (bluetooth->interrupt_in_urb); } up(&bluetooth->lock); } @@ -705,7 +705,7 @@ void btusb_disable_bulk_read(struct tty_struct *tty){ } if ((bluetooth->read_urb) && (bluetooth->read_urb->actual_length)) - usb_unlink_urb(bluetooth->read_urb); + usb_kill_urb(bluetooth->read_urb); } #endif @@ -1179,14 +1179,14 @@ static void usb_bluetooth_disconnect(struct usb_interface *intf) bluetooth->open_count = 0; if (bluetooth->read_urb) { - usb_unlink_urb (bluetooth->read_urb); + usb_kill_urb (bluetooth->read_urb); usb_free_urb (bluetooth->read_urb); } if (bluetooth->bulk_in_buffer) kfree (bluetooth->bulk_in_buffer); if (bluetooth->interrupt_in_urb) { - usb_unlink_urb (bluetooth->interrupt_in_urb); + usb_kill_urb (bluetooth->interrupt_in_urb); usb_free_urb (bluetooth->interrupt_in_urb); } if (bluetooth->interrupt_in_buffer) @@ -1196,7 +1196,7 @@ static void usb_bluetooth_disconnect(struct usb_interface *intf) for (i = 0; i < NUM_CONTROL_URBS; ++i) { if (bluetooth->control_urb_pool[i]) { - usb_unlink_urb (bluetooth->control_urb_pool[i]); + usb_kill_urb (bluetooth->control_urb_pool[i]); if (bluetooth->control_urb_pool[i]->transfer_buffer) kfree (bluetooth->control_urb_pool[i]->transfer_buffer); usb_free_urb (bluetooth->control_urb_pool[i]); diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index a929a521242b..0d0d49923bee 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -301,9 +301,9 @@ done: return 0; full_bailout: - usb_unlink_urb(acm->readurb); + usb_kill_urb(acm->readurb); bail_out_and_unlink: - usb_unlink_urb(acm->ctrlurb); + usb_kill_urb(acm->ctrlurb); bail_out: up(&open_sem); return -EIO; @@ -320,9 +320,9 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp) if (!--acm->used) { if (acm->dev) { acm_set_control(acm, acm->ctrlout = 0); - usb_unlink_urb(acm->ctrlurb); - usb_unlink_urb(acm->writeurb); - usb_unlink_urb(acm->readurb); + usb_kill_urb(acm->ctrlurb); + usb_kill_urb(acm->writeurb); + usb_kill_urb(acm->readurb); } else { tty_unregister_device(acm_tty_driver, acm->minor); acm_table[acm->minor] = NULL; @@ -542,6 +542,17 @@ static int acm_probe (struct usb_interface *intf, return -EINVAL; } + if (!buflen) { + if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) { + dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint"); + buflen = intf->cur_altsetting->endpoint->extralen; + buffer = intf->cur_altsetting->endpoint->extra; + } else { + err("Zero length descriptor references"); + return -EINVAL; + } + } + while (buflen > 0) { if (buffer [1] != USB_DT_CS_INTERFACE) { err("skipping garbage"); @@ -558,6 +569,8 @@ static int acm_probe (struct usb_interface *intf, break; case CDC_COUNTRY_TYPE: /* maybe somehow export */ break; /* for now we ignore it */ + case CDC_HEADER_TYPE: /* maybe check version */ + break; /* for now we ignore it */ case CDC_AC_MANAGEMENT_TYPE: ac_management_function = buffer[3]; break; @@ -569,7 +582,7 @@ static int acm_probe (struct usb_interface *intf, break; default: - err("Ignoring extra header"); + err("Ignoring extra header, type %d, length %d", buffer[2], buffer[0]); break; } next_desc: @@ -637,7 +650,7 @@ next_desc: dbg("interfaces are valid"); for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); - if (acm_table[minor]) { + if (minor == ACM_TTY_MINORS) { err("no more free acm devices"); return -ENODEV; } @@ -762,9 +775,9 @@ static void acm_disconnect(struct usb_interface *intf) acm->dev = NULL; usb_set_intfdata (intf, NULL); - usb_unlink_urb(acm->ctrlurb); - usb_unlink_urb(acm->readurb); - usb_unlink_urb(acm->writeurb); + usb_kill_urb(acm->ctrlurb); + usb_kill_urb(acm->readurb); + usb_kill_urb(acm->writeurb); flush_scheduled_work(); /* wait for acm_softint */ diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index cc26d9517398..da945b367a85 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -117,6 +117,7 @@ struct union_desc { } __attribute__ ((packed)); /* class specific descriptor types */ +#define CDC_HEADER_TYPE 0x00 #define CDC_CALL_MANAGEMENT_TYPE 0x01 #define CDC_AC_MANAGEMENT_TYPE 0x02 #define CDC_UNION_TYPE 0x06 diff --git a/drivers/usb/class/usb-midi.c b/drivers/usb/class/usb-midi.c index a9dc047a04a7..1dc952e9bc81 100644 --- a/drivers/usb/class/usb-midi.c +++ b/drivers/usb/class/usb-midi.c @@ -805,7 +805,6 @@ static int usb_midi_open(struct inode *inode, struct file *file) { int minor = iminor(inode); DECLARE_WAITQUEUE(wait, current); - struct list_head *devs, *mdevs; struct usb_midi_state *s; struct usb_mididev *m; unsigned long flags; @@ -817,10 +816,8 @@ static int usb_midi_open(struct inode *inode, struct file *file) for(;;) { down(&open_sem); - list_for_each(devs, &mididevs) { - s = list_entry(devs, struct usb_midi_state, mididev); - list_for_each(mdevs, &s->midiDevList) { - m = list_entry(mdevs, struct usb_mididev, list); + list_for_each_entry(s, &mididevs, mididev) { + list_for_each_entry(m, &s->midiDevList, list) { if ( !((m->dev_midi ^ minor) & ~0xf) ) goto device_found; } @@ -939,7 +936,7 @@ static int usb_midi_release(struct inode *inode, struct file *file) if ( m->open_mode & FMODE_WRITE ) { m->open_mode &= ~FMODE_WRITE; - usb_unlink_urb( m->mout.ep->urb ); + usb_kill_urb( m->mout.ep->urb ); } if ( m->open_mode & FMODE_READ ) { @@ -951,7 +948,7 @@ static int usb_midi_release(struct inode *inode, struct file *file) if ( m->min.ep->readers == 0 && m->min.ep->urbSubmitted ) { m->min.ep->urbSubmitted = 0; - usb_unlink_urb(m->min.ep->urb); + usb_kill_urb(m->min.ep->urb); } spin_unlock_irqrestore( &m->min.ep->lock, flagsep ); } @@ -1042,7 +1039,7 @@ static struct midi_in_endpoint *alloc_midi_in_endpoint( struct usb_device *d, in static int remove_midi_in_endpoint( struct midi_in_endpoint *min ) { - usb_unlink_urb( min->urb ); + usb_kill_urb( min->urb ); usb_free_urb( min->urb ); kfree( min->recvBuf ); kfree( min ); @@ -1102,7 +1099,7 @@ static struct midi_out_endpoint *alloc_midi_out_endpoint( struct usb_device *d, static int remove_midi_out_endpoint( struct midi_out_endpoint *mout ) { - usb_unlink_urb( mout->urb ); + usb_kill_urb( mout->urb ); usb_free_urb( mout->urb ); kfree( mout->buf ); kfree( mout ); @@ -1994,7 +1991,6 @@ static int usb_midi_probe(struct usb_interface *intf, static void usb_midi_disconnect(struct usb_interface *intf) { struct usb_midi_state *s = usb_get_intfdata (intf); - struct list_head *list; struct usb_mididev *m; if ( !s ) @@ -2012,8 +2008,7 @@ static void usb_midi_disconnect(struct usb_interface *intf) s->usbdev = NULL; usb_set_intfdata (intf, NULL); - list_for_each(list, &s->midiDevList) { - m = list_entry(list, struct usb_mididev, list); + list_for_each_entry(m, &s->midiDevList, list) { wake_up(&(m->min.ep->wait)); wake_up(&(m->mout.ep->wait)); if ( m->dev_midi >= 0 ) { diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index da91bbd6735c..da145c91c1fa 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -406,9 +406,9 @@ static void usblp_cleanup (struct usblp *usblp) static void usblp_unlink_urbs(struct usblp *usblp) { - usb_unlink_urb(usblp->writeurb); + usb_kill_urb(usblp->writeurb); if (usblp->bidir) - usb_unlink_urb(usblp->readurb); + usb_kill_urb(usblp->readurb); } static int usblp_release(struct inode *inode, struct file *file) diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 8e535789fce7..50009ed51e8d 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -149,7 +149,7 @@ static const struct class_info clas_info[] = /*****************************************************************/ -void usbdevfs_conn_disc_event(void) +void usbfs_conn_disc_event(void) { conndiscevcnt++; wake_up(&deviceconndiscwq); @@ -451,7 +451,7 @@ static char *usb_dump_string(char *start, char *end, const struct usb_device *de * nbytes - the maximum number of bytes to write * skip_bytes - the number of bytes to skip before writing anything * file_offset - the offset into the devices file on completion - * The caller must own the usbdev->serialize semaphore. + * The caller must own the device lock. */ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *skip_bytes, loff_t *file_offset, struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count) @@ -569,7 +569,6 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *ski static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { - struct list_head *buslist; struct usb_bus *bus; ssize_t ret, total_written = 0; loff_t skip_bytes = *ppos; @@ -581,18 +580,15 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte if (!access_ok(VERIFY_WRITE, buf, nbytes)) return -EFAULT; - /* enumerate busses */ down (&usb_bus_list_lock); - list_for_each(buslist, &usb_bus_list) { - /* print devices for this bus */ - bus = list_entry(buslist, struct usb_bus, bus_list); - + /* print devices for all busses */ + list_for_each_entry(bus, &usb_bus_list, bus_list) { /* recurse through all children of the root hub */ if (!bus->root_hub) continue; - down(&bus->root_hub->serialize); + usb_lock_device(bus->root_hub); ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0); - up(&bus->root_hub->serialize); + usb_unlock_device(bus->root_hub); if (ret < 0) { up(&usb_bus_list_lock); return ret; @@ -682,7 +678,7 @@ static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig) return ret; } -struct file_operations usbdevfs_devices_fops = { +struct file_operations usbfs_devices_fops = { .llseek = usb_device_lseek, .read = usb_device_read, .poll = usb_device_poll, diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 776c1bf0df9b..0e0ea0806606 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -21,7 +21,7 @@ * * $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $ * - * This file implements the usbdevfs/x/y files, where + * This file implements the usbfs/x/y files, where * x is the bus number and y the device number. * * It allows user space programs/"drivers" to communicate directly @@ -113,7 +113,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l int i; pos = *ppos; - down(&dev->serialize); + usb_lock_device(dev); if (!connected(dev)) { ret = -ENODEV; goto err; @@ -175,7 +175,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l } err: - up(&dev->serialize); + usb_unlock_device(dev); return ret; } @@ -286,9 +286,10 @@ static void destroy_async (struct dev_state *ps, struct list_head *list) while (!list_empty(list)) { as = list_entry(list->next, struct async, asynclist); list_del_init(&as->asynclist); + + /* drop the spinlock so the completion handler can run */ spin_unlock_irqrestore(&ps->lock, flags); - /* usb_unlink_urb calls the completion handler with status == -ENOENT */ - usb_unlink_urb(as->urb); + usb_kill_urb(as->urb); spin_lock_irqsave(&ps->lock, flags); } spin_unlock_irqrestore(&ps->lock, flags); @@ -353,7 +354,7 @@ static void driver_disconnect(struct usb_interface *intf) destroy_async_on_interface(ps, ifnum); } -struct usb_driver usbdevfs_driver = { +struct usb_driver usbfs_driver = { .owner = THIS_MODULE, .name = "usbfs", .probe = driver_probe, @@ -378,7 +379,7 @@ static int claimintf(struct dev_state *ps, unsigned int ifnum) if (!intf) err = -ENOENT; else - err = usb_driver_claim_interface(&usbdevfs_driver, intf, ps); + err = usb_driver_claim_interface(&usbfs_driver, intf, ps); up_write(&usb_bus_type.subsys.rwsem); if (err == 0) set_bit(ifnum, &ps->ifclaimed); @@ -401,7 +402,7 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum) if (!intf) err = -ENOENT; else if (test_and_clear_bit(ifnum, &ps->ifclaimed)) { - usb_driver_release_interface(&usbdevfs_driver, intf); + usb_driver_release_interface(&usbfs_driver, intf); err = 0; } up_write(&usb_bus_type.subsys.rwsem); @@ -516,7 +517,7 @@ static int usbdev_release(struct inode *inode, struct file *file) struct usb_device *dev = ps->dev; unsigned int ifnum; - down(&dev->serialize); + usb_lock_device(dev); list_del_init(&ps->list); if (connected(dev)) { @@ -525,7 +526,7 @@ static int usbdev_release(struct inode *inode, struct file *file) releaseintf(ps, ifnum); destroy_all_async(ps); } - up(&dev->serialize); + usb_unlock_device(dev); usb_put_dev(dev); ps->dev = NULL; kfree(ps); @@ -557,10 +558,10 @@ static int proc_control(struct dev_state *ps, void __user *arg) snoop(&dev->dev, "control read: bRequest=%02x bRrequestType=%02x wValue=%04x wIndex=%04x\n", ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex); - up(&dev->serialize); + usb_unlock_device(dev); i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); - down(&dev->serialize); + usb_lock_device(dev); if ((i > 0) && ctrl.wLength) { if (usbfs_snoop) { dev_info(&dev->dev, "control read: data "); @@ -588,10 +589,10 @@ static int proc_control(struct dev_state *ps, void __user *arg) printk ("%02x ", (unsigned char)(tbuf)[j]); printk("\n"); } - up(&dev->serialize); + usb_unlock_device(dev); i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); - down(&dev->serialize); + usb_lock_device(dev); } free_page((unsigned long)tbuf); if (i<0) { @@ -635,9 +636,9 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) kfree(tbuf); return -EINVAL; } - up(&dev->serialize); + usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); - down(&dev->serialize); + usb_lock_device(dev); if (!i && len2) { if (copy_to_user(bulk.data, tbuf, len2)) { kfree(tbuf); @@ -651,9 +652,9 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) return -EFAULT; } } - up(&dev->serialize); + usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); - down(&dev->serialize); + usb_lock_device(dev); } kfree(tbuf); if (i < 0) { @@ -734,7 +735,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg) static int proc_resetdevice(struct dev_state *ps) { - return __usb_reset_device(ps->dev); + return usb_reset_device(ps->dev); } @@ -976,7 +977,7 @@ static int proc_unlinkurb(struct dev_state *ps, void __user *arg) as = async_getpending(ps, arg); if (!as) return -EINVAL; - usb_unlink_urb(as->urb); + usb_kill_urb(as->urb); return 0; } @@ -1024,9 +1025,9 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg) break; if (signal_pending(current)) break; - up(&dev->serialize); + usb_unlock_device(dev); schedule(); - down(&dev->serialize); + usb_lock_device(dev); } remove_wait_queue(&ps->wait, &wait); set_current_state(TASK_RUNNING); @@ -1149,7 +1150,11 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg) /* let kernel drivers try to (re)bind to the interface */ case USBDEVFS_CONNECT: + usb_unlock_device(ps->dev); + usb_lock_all_devices(); bus_rescan_devices(intf->dev.bus); + usb_unlock_all_devices(); + usb_lock_device(ps->dev); break; /* talk directly to the interface's driver */ @@ -1192,9 +1197,9 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd if (!(file->f_mode & FMODE_WRITE)) return -EPERM; - down(&dev->serialize); + usb_lock_device(dev); if (!connected(dev)) { - up(&dev->serialize); + usb_unlock_device(dev); return -ENODEV; } @@ -1294,7 +1299,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd ret = proc_ioctl(ps, p); break; } - up(&dev->serialize); + usb_unlock_device(dev); if (ret >= 0) inode->i_atime = CURRENT_TIME; return ret; @@ -1314,7 +1319,7 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai return mask; } -struct file_operations usbdevfs_device_file_operations = { +struct file_operations usbfs_device_file_operations = { .llseek = usbdev_lseek, .read = usbdev_read, .poll = usbdev_poll, diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 5fdaae079cf6..837bb267f194 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -188,9 +188,9 @@ clean_3: } hcd->irq = dev->irq; - dev_info (hcd->self.controller, "irq %s, %s %p\n", bufp, + dev_info (hcd->self.controller, "irq %s, %s 0x%lx\n", bufp, (driver->flags & HCD_MEMORY) ? "pci mem" : "io base", - base); + resource); usb_bus_init (&hcd->self); hcd->self.op = &usb_hcd_operations; @@ -260,6 +260,8 @@ void usb_hcd_pci_remove (struct pci_dev *dev) } usb_deregister_bus (&hcd->self); + + pci_disable_device(dev); } EXPORT_SYMBOL (usb_hcd_pci_remove); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index d970e2c0c066..8ab1163e59b9 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -798,9 +798,9 @@ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev return (retval < 0) ? retval : -EMSGSIZE; } - down (&usb_dev->serialize); + usb_lock_device (usb_dev); retval = usb_new_device (usb_dev); - up (&usb_dev->serialize); + usb_unlock_device (usb_dev); if (retval) { usb_dev->bus->root_hub = NULL; dev_err (parent_dev, "can't register root hub for %s, %d\n", @@ -1264,7 +1264,7 @@ static int hcd_unlink_urb (struct urb *urb, int status) * never get completion IRQs ... maybe even the ones we need to * finish unlinking the initial failed usb_set_address(). */ - if (!hcd->saw_irq) { + if (!hcd->saw_irq && hcd->rh_timer.data != (unsigned long) urb) { dev_warn (hcd->self.controller, "Unlink after no-IRQ? " "Different ACPI or APIC settings may help." "\n"); @@ -1573,13 +1573,12 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r) struct usb_hcd *hcd = __hcd; int start = hcd->state; - if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */ + if (start == USB_STATE_HALT) return IRQ_NONE; - - hcd->saw_irq = 1; if (hcd->driver->irq (hcd, r) == IRQ_NONE) return IRQ_NONE; + hcd->saw_irq = 1; if (hcd->state != start && hcd->state == USB_STATE_HALT) usb_hc_died (hcd); return IRQ_HANDLED; @@ -1588,22 +1587,6 @@ EXPORT_SYMBOL (usb_hcd_irq); /*-------------------------------------------------------------------------*/ -static void hcd_panic (void *_hcd) -{ - struct usb_hcd *hcd = _hcd; - struct usb_device *hub = hcd->self.root_hub; - unsigned i; - - /* hc's root hub is removed later removed in hcd->stop() */ - down (&hub->serialize); - usb_set_device_state(hub, USB_STATE_NOTATTACHED); - for (i = 0; i < hub->maxchild; i++) { - if (hub->children [i]) - usb_disconnect (&hub->children [i]); - } - up (&hub->serialize); -} - /** * usb_hc_died - report abnormal shutdown of a host controller (bus glue) * @hcd: pointer to the HCD representing the controller @@ -1616,9 +1599,9 @@ void usb_hc_died (struct usb_hcd *hcd) { dev_err (hcd->self.controller, "HC died; cleaning up\n"); - /* clean up old urbs and devices; needs a task context */ - INIT_WORK (&hcd->work, hcd_panic, hcd); - (void) schedule_work (&hcd->work); + /* make khubd clean up old urbs and devices */ + usb_set_device_state(hcd->self.root_hub, USB_STATE_NOTATTACHED); + mod_timer(&hcd->rh_timer, jiffies); } EXPORT_SYMBOL (usb_hc_died); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 340977b72925..eb8ae109096e 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -67,7 +67,6 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ struct timer_list rh_timer; /* drives root hub */ struct list_head dev_list; /* devices on this bus */ - struct work_struct work; /* * hardware info/state @@ -363,6 +362,9 @@ static inline int hcd_register_root (struct usb_device *usb_dev, return usb_register_root_hub (usb_dev, hcd->self.controller); } +extern void usb_set_device_state(struct usb_device *udev, + enum usb_device_state new_state); + /*-------------------------------------------------------------------------*/ /* exported only within usbcore */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 7f72ee96c7fe..10baa8f52566 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -36,15 +36,18 @@ #include "hcd.h" #include "hub.h" -/* Protect struct usb_device state and children members */ +/* Protect struct usb_device->state and ->children members + * Note: Both are also protected by ->serialize, except that ->state can + * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ static spinlock_t device_state_lock = SPIN_LOCK_UNLOCKED; -/* Wakes up khubd */ +/* khubd's worklist and its lock */ static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED; - static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */ +/* Wakes up khubd */ static DECLARE_WAIT_QUEUE_HEAD(khubd_wait); + static pid_t khubd_pid = 0; /* PID of khubd */ static DECLARE_COMPLETION(khubd_exited); @@ -226,6 +229,19 @@ static int get_port_status(struct usb_device *hdev, int port, data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT); } +static void kick_khubd(struct usb_hub *hub) +{ + unsigned long flags; + + spin_lock_irqsave(&hub_event_lock, flags); + if (list_empty(&hub->event_list)) { + list_add_tail(&hub->event_list, &hub_event_list); + wake_up(&khubd_wait); + } + spin_unlock_irqrestore(&hub_event_lock, flags); +} + + /* completion function, fires on port status changes and various faults */ static void hub_irq(struct urb *urb, struct pt_regs *regs) { @@ -261,12 +277,7 @@ static void hub_irq(struct urb *urb, struct pt_regs *regs) hub->nerrors = 0; /* Something happened, let khubd figure it out */ - spin_lock(&hub_event_lock); - if (list_empty(&hub->event_list)) { - list_add_tail(&hub->event_list, &hub_event_list); - wake_up(&khubd_wait); - } - spin_unlock(&hub_event_lock); + kick_khubd(hub); resubmit: if (hub->quiescing) @@ -384,6 +395,33 @@ static void hub_power_on(struct usb_hub *hub) msleep(hub->descriptor->bPwrOn2PwrGood * 2); } +static void hub_quiesce(struct usb_hub *hub) +{ + /* stop khubd and related activity */ + hub->quiescing = 1; + usb_kill_urb(hub->urb); + if (hub->has_indicators) + cancel_delayed_work(&hub->leds); + if (hub->has_indicators || hub->tt.hub) + flush_scheduled_work(); +} + +static void hub_activate(struct usb_hub *hub) +{ + int status; + + hub->quiescing = 0; + status = usb_submit_urb(hub->urb, GFP_NOIO); + if (status < 0) + dev_err(&hub->intf->dev, "activate --> %d\n", status); + if (hub->has_indicators && blinkenlights) + schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); + + /* scan all ports ASAP */ + hub->event_bits[0] = ~0; + kick_khubd(hub); +} + static int hub_hub_status(struct usb_hub *hub, u16 *status, u16 *change) { @@ -579,7 +617,7 @@ static int hub_configure(struct usb_hub *hub, dev_dbg(hub_dev, "%sover-current condition exists\n", (hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no "); - /* Start the interrupt endpoint */ + /* set up the interrupt endpoint */ pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); @@ -597,24 +635,13 @@ static int hub_configure(struct usb_hub *hub, hub, endpoint->bInterval); hub->urb->transfer_dma = hub->buffer_dma; hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - ret = usb_submit_urb(hub->urb, GFP_KERNEL); - if (ret) { - message = "couldn't submit status urb"; - goto fail; - } - - /* Wake up khubd */ - wake_up(&khubd_wait); - /* maybe start cycling the hub leds */ - if (hub->has_indicators && blinkenlights) { - set_port_led(hdev, 1, HUB_LED_GREEN); + /* maybe cycle the hub leds */ + if (hub->has_indicators && blinkenlights) hub->indicator [0] = INDICATOR_CYCLE; - schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); - } hub_power_on(hub); - + hub_activate(hub); return 0; fail: @@ -626,33 +653,6 @@ fail: static unsigned highspeed_hubs; -static void hub_quiesce(struct usb_hub *hub) -{ - /* stop khubd and related activity */ - hub->quiescing = 1; - usb_kill_urb(hub->urb); - if (hub->has_indicators) - cancel_delayed_work(&hub->leds); - if (hub->has_indicators || hub->tt.hub) - flush_scheduled_work(); -} - -#ifdef CONFIG_USB_SUSPEND - -static void hub_reactivate(struct usb_hub *hub) -{ - int status; - - hub->quiescing = 0; - status = usb_submit_urb(hub->urb, GFP_NOIO); - if (status < 0) - dev_err(&hub->intf->dev, "reactivate --> %d\n", status); - if (hub->has_indicators && blinkenlights) - schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); -} - -#endif - static void hub_disconnect(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata (intf); @@ -794,68 +794,29 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) } } -/* caller has locked the hub and must own the device lock */ -static int hub_reset(struct usb_hub *hub) +/* caller has locked the hub device */ +static void hub_pre_reset(struct usb_device *hdev) { - struct usb_device *hdev = hub->hdev; + struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]); int i; - /* Disconnect any attached devices */ - for (i = 0; i < hub->descriptor->bNbrPorts; i++) { + for (i = 0; i < hdev->maxchild; ++i) { if (hdev->children[i]) usb_disconnect(&hdev->children[i]); } - - /* Attempt to reset the hub */ - if (hub->urb) - usb_kill_urb(hub->urb); - else - return -1; - - if (__usb_reset_device(hdev)) - return -1; - - hub->urb->dev = hdev; - if (usb_submit_urb(hub->urb, GFP_KERNEL)) - return -1; - - hub_power_on(hub); - - return 0; + hub_quiesce(hub); } -/* caller has locked the hub */ -/* FIXME! This routine should be subsumed into hub_reset */ -static void hub_start_disconnect(struct usb_device *hdev) +/* caller has locked the hub device */ +static void hub_post_reset(struct usb_device *hdev) { - struct usb_device *parent = hdev->parent; - int i; - - /* Find the device pointer to disconnect */ - if (parent) { - for (i = 0; i < parent->maxchild; i++) { - if (parent->children[i] == hdev) { - usb_disconnect(&parent->children[i]); - return; - } - } - } + struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]); - dev_err(&hdev->dev, "cannot disconnect hub!\n"); + hub_activate(hub); + hub_power_on(hub); } -static void recursively_mark_NOTATTACHED(struct usb_device *udev) -{ - int i; - - for (i = 0; i < udev->maxchild; ++i) { - if (udev->children[i]) - recursively_mark_NOTATTACHED(udev->children[i]); - } - udev->state = USB_STATE_NOTATTACHED; -} - /* grab device/port lock, returning index of that port (zero based). * protects the upstream link used by this device from concurrent * tree operations like suspend, resume, reset, and disconnect, which @@ -872,21 +833,16 @@ static int locktree(struct usb_device *udev) /* root hub is always the first lock in the series */ hdev = udev->parent; if (!hdev) { - down(&udev->serialize); + usb_lock_device(udev); return 0; } /* on the path from root to us, lock everything from * top down, dropping parent locks when not needed - * - * NOTE: if disconnect were to ignore the locking, we'd need - * to get extra refcounts to everything since hdev->children - * and udev->parent could be invalidated while we work... */ t = locktree(hdev); if (t < 0) return t; - spin_lock_irq(&device_state_lock); for (t = 0; t < hdev->maxchild; t++) { if (hdev->children[t] == udev) { /* everything is fail-fast once disconnect @@ -898,33 +854,45 @@ static int locktree(struct usb_device *udev) /* when everyone grabs locks top->bottom, * non-overlapping work may be concurrent */ - spin_unlock_irq(&device_state_lock); down(&udev->serialize); up(&hdev->serialize); return t; } } - spin_unlock_irq(&device_state_lock); - up(&hdev->serialize); + usb_unlock_device(hdev); return -ENODEV; } +static void recursively_mark_NOTATTACHED(struct usb_device *udev) +{ + int i; + + for (i = 0; i < udev->maxchild; ++i) { + if (udev->children[i]) + recursively_mark_NOTATTACHED(udev->children[i]); + } + udev->state = USB_STATE_NOTATTACHED; +} + /** - * usb_set_device_state - change a device's current state (usbcore-internal) + * usb_set_device_state - change a device's current state (usbcore, hcds) * @udev: pointer to device whose state should be changed * @new_state: new state value to be stored * - * udev->state is _not_ protected by the device lock. This + * udev->state is _not_ fully protected by the device lock. Although + * most transitions are made only while holding the lock, the state can + * can change to USB_STATE_NOTATTACHED at almost any time. This * is so that devices can be marked as disconnected as soon as possible, - * without having to wait for the semaphore to be released. Instead, - * changes to the state must be protected by the device_state_lock spinlock. + * without having to wait for any semaphores to be released. As a result, + * all changes to any device's state must be protected by the + * device_state_lock spinlock. * * Once a device has been added to the device tree, all changes to its state * should be made using this routine. The state should _not_ be set directly. * * If udev->state is already USB_STATE_NOTATTACHED then no change is made. * Otherwise udev->state is set to new_state, and if new_state is - * USB_STATE_NOTATTACHED then all of udev's descendant's states are also set + * USB_STATE_NOTATTACHED then all of udev's descendants' states are also set * to USB_STATE_NOTATTACHED. */ void usb_set_device_state(struct usb_device *udev, @@ -941,6 +909,7 @@ void usb_set_device_state(struct usb_device *udev, recursively_mark_NOTATTACHED(udev); spin_unlock_irqrestore(&device_state_lock, flags); } +EXPORT_SYMBOL(usb_set_device_state); static void choose_address(struct usb_device *udev) @@ -974,11 +943,12 @@ static void release_address(struct usb_device *udev) /** * usb_disconnect - disconnect a device (usbcore-internal) - * @pdev: pointer to device being disconnected, into a locked hub + * @pdev: pointer to device being disconnected * Context: !in_interrupt () * - * Something got disconnected. Get rid of it, and all of its children. - * If *pdev is a normal device then the parent hub should be locked. + * Something got disconnected. Get rid of it and all of its children. + * + * If *pdev is a normal device then the parent hub must already be locked. * If *pdev is a root hub then this routine will acquire the * usb_bus_list_lock on behalf of the caller. * @@ -1004,9 +974,11 @@ void usb_disconnect(struct usb_device **pdev) usb_set_device_state(udev, USB_STATE_NOTATTACHED); /* lock the bus list on behalf of HCDs unregistering their root hubs */ - if (!udev->parent) + if (!udev->parent) { down(&usb_bus_list_lock); - down(&udev->serialize); + usb_lock_device(udev); + } else + down(&udev->serialize); dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum); @@ -1031,14 +1003,16 @@ void usb_disconnect(struct usb_device **pdev) usbfs_remove_device(udev); usb_remove_sysfs_dev_files(udev); - /* Avoid races with recursively_mark_NOTATTACHED() and locktree() */ + /* Avoid races with recursively_mark_NOTATTACHED() */ spin_lock_irq(&device_state_lock); *pdev = NULL; spin_unlock_irq(&device_state_lock); - up(&udev->serialize); - if (!udev->parent) + if (!udev->parent) { + usb_unlock_device(udev); up(&usb_bus_list_lock); + } else + up(&udev->serialize); device_unregister(&udev->dev); } @@ -1061,11 +1035,19 @@ static int choose_configuration(struct usb_device *udev) ->altsetting->desc; if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC) continue; - /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS */ + /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS. + * MSFT needs this to be the first config; never use + * it as the default unless Linux has host-side RNDIS. + * A second config would ideally be CDC-Ethernet, but + * may instead be the "vendor specific" CDC subset + * long used by ARM Linux for sa1100 or pxa255. + */ if (desc->bInterfaceClass == USB_CLASS_COMM && desc->bInterfaceSubClass == 2 - && desc->bInterfaceProtocol == 0xff) + && desc->bInterfaceProtocol == 0xff) { + c = udev->config[1].desc.bConfigurationValue; continue; + } c = udev->config[i].desc.bConfigurationValue; break; } @@ -1384,7 +1366,6 @@ static int hub_port_disable(struct usb_device *hdev, int port) int ret; if (hdev->children[port]) { - /* FIXME need disconnect() for NOTATTACHED device */ usb_set_device_state(hdev->children[port], USB_STATE_NOTATTACHED); } @@ -1396,6 +1377,33 @@ static int hub_port_disable(struct usb_device *hdev, int port) return ret; } +/* + * Disable a port and mark a logical connnect-change event, so that some + * time later khubd will disconnect() any existing usb_device on the port + * and will re-enumerate if there actually is a device attached. + */ +static void hub_port_logical_disconnect(struct usb_device *hdev, int port) +{ + struct usb_hub *hub; + + dev_dbg(hubdev(hdev), "logical disconnect on port %d\n", port + 1); + hub_port_disable(hdev, port); + + /* FIXME let caller ask to power down the port: + * - some devices won't enumerate without a VBUS power cycle + * - SRP saves power that way + * - usb_suspend_device(dev,PM_SUSPEND_DISK) + * That's easy if this hub can switch power per-port, and + * khubd reactivates the port later (timer, SRP, etc). + * Powerdown must be optional, because of reset/DFU. + */ + + hub = usb_get_intfdata(hdev->actconfig->interface[0]); + set_bit(port, hub->change_bits); + kick_khubd(hub); +} + + #ifdef CONFIG_USB_SUSPEND /* @@ -1413,8 +1421,8 @@ static int hub_port_suspend(struct usb_device *hdev, int port) int status; struct usb_device *udev; - udev = hdev->children[port - 1]; - // dev_dbg(hubdev(hdev), "suspend port %d\n", port); + udev = hdev->children[port]; + // dev_dbg(hubdev(hdev), "suspend port %d\n", port + 1); /* enable remote wakeup when appropriate; this lets the device * wake up the upstream hub (including maybe the root hub). @@ -1439,11 +1447,11 @@ static int hub_port_suspend(struct usb_device *hdev, int port) } /* see 7.1.7.6 */ - status = set_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND); + status = set_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND); if (status) { dev_dbg(hubdev(hdev), "can't suspend port %d, status %d\n", - port, status); + port + 1, status); /* paranoia: "should not happen" */ (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, @@ -1477,16 +1485,14 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state) { int status; + /* caller owns the udev device lock */ if (port < 0) return port; - /* NOTE: udev->serialize released on all real returns! */ - if (state <= udev->dev.power.power_state || state < PM_SUSPEND_MEM || udev->state == USB_STATE_SUSPENDED || udev->state == USB_STATE_NOTATTACHED) { - up(&udev->serialize); return 0; } @@ -1562,11 +1568,10 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state) else status = -EOPNOTSUPP; } else - status = hub_port_suspend(udev->parent, port + 1); + status = hub_port_suspend(udev->parent, port); if (status == 0) udev->dev.power.power_state = state; - up(&udev->serialize); return status; } @@ -1590,7 +1595,15 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state) */ int usb_suspend_device(struct usb_device *udev, u32 state) { - return __usb_suspend_device(udev, locktree(udev), state); + int port, status; + + port = locktree(udev); + if (port < 0) + return port; + + status = __usb_suspend_device(udev, port, state); + usb_unlock_device(udev); + return status; } /* @@ -1603,7 +1616,7 @@ static int finish_port_resume(struct usb_device *udev) int status; u16 devstatus; - /* caller owns udev->serialize */ + /* caller owns the udev device lock */ dev_dbg(&udev->dev, "usb resume\n"); udev->dev.power.power_state = PM_SUSPEND_ON; @@ -1687,15 +1700,15 @@ hub_port_resume(struct usb_device *hdev, int port) int status; struct usb_device *udev; - udev = hdev->children[port - 1]; - // dev_dbg(hubdev(hdev), "resume port %d\n", port); + udev = hdev->children[port]; + // dev_dbg(hubdev(hdev), "resume port %d\n", port + 1); /* see 7.1.7.7; affects power usage, but not budgeting */ - status = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND); + status = clear_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND); if (status) { dev_dbg(&hdev->actconfig->interface[0]->dev, "can't resume port %d, status %d\n", - port, status); + port + 1, status); } else { u16 devstatus; u16 portchange; @@ -1713,7 +1726,7 @@ hub_port_resume(struct usb_device *hdev, int port) * sequence. */ devstatus = portchange = 0; - status = hub_port_status(hdev, port - 1, + status = hub_port_status(hdev, port, &devstatus, &portchange); if (status < 0 || (devstatus & LIVE_FLAGS) != LIVE_FLAGS @@ -1721,7 +1734,7 @@ hub_port_resume(struct usb_device *hdev, int port) ) { dev_dbg(&hdev->actconfig->interface[0]->dev, "port %d status %04x.%04x after resume, %d\n", - port, portchange, devstatus, status); + port + 1, portchange, devstatus, status); } else { /* TRSMRCY = 10 msec */ msleep(10); @@ -1729,7 +1742,7 @@ hub_port_resume(struct usb_device *hdev, int port) } } if (status < 0) - status = hub_port_disable(hdev, port); + hub_port_logical_disconnect(hdev, port); return status; } @@ -1773,7 +1786,7 @@ int usb_resume_device(struct usb_device *udev) ->actconfig->interface[0]); } } else if (udev->state == USB_STATE_SUSPENDED) { - status = hub_port_resume(udev->parent, port + 1); + status = hub_port_resume(udev->parent, port); } else { status = 0; udev->dev.power.power_state = PM_SUSPEND_ON; @@ -1783,10 +1796,12 @@ int usb_resume_device(struct usb_device *udev) status); } - up(&udev->serialize); + usb_unlock_device(udev); /* rebind drivers that had no suspend() */ + usb_lock_all_devices(); bus_rescan_devices(&usb_bus_type); + usb_unlock_all_devices(); return status; } @@ -1828,6 +1843,7 @@ static int hub_suspend(struct usb_interface *intf, u32 state) continue; down(&udev->serialize); status = __usb_suspend_device(udev, port, state); + up(&udev->serialize); if (status < 0) dev_dbg(&intf->dev, "suspend port %d --> %d\n", port, status); @@ -1866,20 +1882,20 @@ static int hub_resume(struct usb_interface *intf) continue; down (&udev->serialize); if (portstat & USB_PORT_STAT_SUSPEND) - status = hub_port_resume(hdev, port + 1); + status = hub_port_resume(hdev, port); else { status = finish_port_resume(udev); - if (status < 0) - status = hub_port_disable(hdev, port); - if (status < 0) + if (status < 0) { dev_dbg(&intf->dev, "resume port %d --> %d\n", - port, status); + port + 1, status); + hub_port_logical_disconnect(hdev, port); + } } up(&udev->serialize); } intf->dev.power.power_state = PM_SUSPEND_ON; - hub_reactivate(hub); + hub_activate(hub); return 0; } @@ -2011,7 +2027,7 @@ hub_port_init (struct usb_device *hdev, struct usb_device *udev, int port) hdev->bus->b_hnp_enable = 0; } - retval = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND); + retval = clear_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND); if (retval < 0 && retval != -EPIPE) dev_dbg(&udev->dev, "can't clear suspend; %d\n", retval); @@ -2443,10 +2459,10 @@ static void hub_events(void) dev_dbg (hub_dev, "resetting for error %d\n", hub->error); - if (hub_reset(hub)) { + ret = usb_reset_device(hdev); + if (ret) { dev_dbg (hub_dev, - "can't reset; disconnecting\n"); - hub_start_disconnect(hdev); + "error resetting hub: %d\n", ret); goto loop; } @@ -2502,15 +2518,17 @@ static void hub_events(void) if (portchange & USB_PORT_STAT_C_SUSPEND) { clear_port_feature(hdev, i + 1, USB_PORT_FEAT_C_SUSPEND); - if (hdev->children[i]) + if (hdev->children[i]) { ret = remote_wakeup(hdev->children[i]); - else + if (ret < 0) + connect_change = 1; + } else { ret = -ENODEV; + hub_port_disable(hdev, i); + } dev_dbg (hub_dev, "resume on port %d, status %d\n", i + 1, ret); - if (ret < 0) - ret = hub_port_disable(hdev, i); } if (portchange & USB_PORT_STAT_C_OVERCURRENT) { @@ -2554,7 +2572,7 @@ static void hub_events(void) } loop: - up(&hdev->serialize); + usb_unlock_device(hdev); usb_put_dev(hdev); } /* end while (1) */ @@ -2707,13 +2725,15 @@ static int config_descriptors_changed(struct usb_device *udev) * * The caller must own the device lock. For example, it's safe to use * this from a driver probe() routine after downloading new firmware. + * For calls that might not occur during probe(), drivers should lock + * the device using usb_lock_device_for_reset(). */ -int __usb_reset_device(struct usb_device *udev) +int usb_reset_device(struct usb_device *udev) { struct usb_device *parent = udev->parent; struct usb_device_descriptor descriptor = udev->descriptor; int i, ret, port = -1; - struct usb_hub *hub; + int udev_is_a_hub = 0; if (udev->state == USB_STATE_NOTATTACHED || udev->state == USB_STATE_SUSPENDED) { @@ -2722,13 +2742,9 @@ int __usb_reset_device(struct usb_device *udev) return -EINVAL; } - /* FIXME: This should be legal for regular hubs. Root hubs may - * have special requirements. */ - if (udev->maxchild) { - /* this requires hub- or hcd-specific logic; - * see hub_reset() and OHCI hc_restart() - */ - dev_dbg(&udev->dev, "%s for hub!\n", __FUNCTION__); + if (!parent) { + /* this requires hcd-specific logic; see OHCI hc_restart() */ + dev_dbg(&udev->dev, "%s for root hub!\n", __FUNCTION__); return -EISDIR; } @@ -2744,6 +2760,19 @@ int __usb_reset_device(struct usb_device *udev) return -ENOENT; } + /* If we're resetting an active hub, take some special actions */ + if (udev->actconfig && + udev->actconfig->interface[0]->dev.driver == + &hub_driver.driver) { + udev_is_a_hub = 1; + hub_pre_reset(udev); + } + + /* ep0 maxpacket size may change; let the HCD know about it. + * Other endpoints will be handled by re-enumeration. */ + usb_disable_endpoint(udev, 0); + usb_disable_endpoint(udev, 0 + USB_DIR_IN); + ret = hub_port_init(parent, udev, port); if (ret < 0) goto re_enumerate; @@ -2757,7 +2786,7 @@ int __usb_reset_device(struct usb_device *udev) } if (!udev->actconfig) - return 0; + goto done; ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_CONFIGURATION, 0, @@ -2791,32 +2820,12 @@ int __usb_reset_device(struct usb_device *udev) } } +done: + if (udev_is_a_hub) + hub_post_reset(udev); return 0; re_enumerate: - hub_port_disable(parent, port); - - hub = usb_get_intfdata(parent->actconfig->interface[0]); - set_bit(port, hub->change_bits); - - spin_lock_irq(&hub_event_lock); - if (list_empty(&hub->event_list)) { - list_add_tail(&hub->event_list, &hub_event_list); - wake_up(&khubd_wait); - } - spin_unlock_irq(&hub_event_lock); - + hub_port_logical_disconnect(parent, port); return -ENODEV; } -EXPORT_SYMBOL(__usb_reset_device); - -int usb_reset_device(struct usb_device *udev) -{ - int r; - - down(&udev->serialize); - r = __usb_reset_device(udev); - up(&udev->serialize); - - return r; -} diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index c9f57e334a4f..24452d66d576 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -4,7 +4,7 @@ * inode.c -- Inode/Dentry functions for the USB device file system. * * Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * Copyright (C) 2001,2002 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001,2002,2004 Greg Kroah-Hartman (greg@kroah.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 @@ -40,17 +40,15 @@ #include <linux/smp_lock.h> #include <linux/parser.h> #include <asm/byteorder.h> +#include "usb.h" static struct super_operations usbfs_ops; static struct file_operations default_file_operations; static struct inode_operations usbfs_dir_inode_operations; -static struct vfsmount *usbdevfs_mount; static struct vfsmount *usbfs_mount; -static int usbdevfs_mount_count; /* = 0 */ static int usbfs_mount_count; /* = 0 */ static int ignore_mount = 0; -static struct dentry *devices_usbdevfs_dentry; static struct dentry *devices_usbfs_dentry; static int num_buses; /* = 0 */ @@ -240,9 +238,6 @@ static int remount(struct super_block *sb, int *flags, char *data) if (usbfs_mount && usbfs_mount->mnt_sb) update_sb(usbfs_mount->mnt_sb); - if (usbdevfs_mount && usbdevfs_mount->mnt_sb) - update_sb(usbdevfs_mount->mnt_sb); - return 0; } @@ -561,28 +556,12 @@ static void fs_remove_file (struct dentry *dentry) /* --------------------------------------------------------------------- */ - - -/* - * The usbdevfs name is now deprecated (as of 2.5.1). - * It will be removed when the 2.7.x development cycle is started. - * You have been warned :) - */ -static struct file_system_type usbdevice_fs_type; - static struct super_block *usb_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_single(fs_type, flags, data, usbfs_fill_super); } -static struct file_system_type usbdevice_fs_type = { - .owner = THIS_MODULE, - .name = "usbdevfs", - .get_sb = usb_get_sb, - .kill_sb = kill_litter_super, -}; - static struct file_system_type usb_fs_type = { .owner = THIS_MODULE, .name = "usbfs", @@ -603,16 +582,10 @@ static int create_special_files (void) ignore_mount = 1; /* create the devices special file */ - retval = simple_pin_fs("usbdevfs", &usbdevfs_mount, &usbdevfs_mount_count); - if (retval) { - err ("Unable to get usbdevfs mount"); - goto exit; - } - retval = simple_pin_fs("usbfs", &usbfs_mount, &usbfs_mount_count); if (retval) { err ("Unable to get usbfs mount"); - goto error_clean_usbdevfs_mount; + goto exit; } ignore_mount = 0; @@ -620,7 +593,7 @@ static int create_special_files (void) parent = usbfs_mount->mnt_sb->s_root; devices_usbfs_dentry = fs_create_file ("devices", listmode | S_IFREG, parent, - NULL, &usbdevfs_devices_fops, + NULL, &usbfs_devices_fops, listuid, listgid); if (devices_usbfs_dentry == NULL) { err ("Unable to create devices usbfs file"); @@ -628,42 +601,19 @@ static int create_special_files (void) goto error_clean_mounts; } - parent = usbdevfs_mount->mnt_sb->s_root; - devices_usbdevfs_dentry = fs_create_file ("devices", - listmode | S_IFREG, parent, - NULL, &usbdevfs_devices_fops, - listuid, listgid); - if (devices_usbdevfs_dentry == NULL) { - err ("Unable to create devices usbfs file"); - retval = -ENODEV; - goto error_remove_file; - } - goto exit; -error_remove_file: - fs_remove_file (devices_usbfs_dentry); - devices_usbfs_dentry = NULL; - error_clean_mounts: simple_release_fs(&usbfs_mount, &usbfs_mount_count); - -error_clean_usbdevfs_mount: - simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count); - exit: return retval; } static void remove_special_files (void) { - if (devices_usbdevfs_dentry) - fs_remove_file (devices_usbdevfs_dentry); if (devices_usbfs_dentry) fs_remove_file (devices_usbfs_dentry); - devices_usbdevfs_dentry = NULL; devices_usbfs_dentry = NULL; - simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count); simple_release_fs(&usbfs_mount, &usbfs_mount_count); } @@ -671,11 +621,6 @@ void usbfs_update_special (void) { struct inode *inode; - if (devices_usbdevfs_dentry) { - inode = devices_usbdevfs_dentry->d_inode; - if (inode) - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - } if (devices_usbfs_dentry) { inode = devices_usbfs_dentry->d_inode; if (inode) @@ -707,29 +652,16 @@ void usbfs_add_bus(struct usb_bus *bus) return; } - parent = usbdevfs_mount->mnt_sb->s_root; - bus->usbdevfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent, - bus, NULL, busuid, busgid); - if (bus->usbdevfs_dentry == NULL) { - err ("error creating usbdevfs bus entry"); - return; - } - usbfs_update_special(); - usbdevfs_conn_disc_event(); + usbfs_conn_disc_event(); } - void usbfs_remove_bus(struct usb_bus *bus) { if (bus->usbfs_dentry) { fs_remove_file (bus->usbfs_dentry); bus->usbfs_dentry = NULL; } - if (bus->usbdevfs_dentry) { - fs_remove_file (bus->usbdevfs_dentry); - bus->usbdevfs_dentry = NULL; - } --num_buses; if (num_buses <= 0) { @@ -738,7 +670,7 @@ void usbfs_remove_bus(struct usb_bus *bus) } usbfs_update_special(); - usbdevfs_conn_disc_event(); + usbfs_conn_disc_event(); } void usbfs_add_device(struct usb_device *dev) @@ -750,20 +682,12 @@ void usbfs_add_device(struct usb_device *dev) sprintf (name, "%03d", dev->devnum); dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG, dev->bus->usbfs_dentry, dev, - &usbdevfs_device_file_operations, + &usbfs_device_file_operations, devuid, devgid); if (dev->usbfs_dentry == NULL) { err ("error creating usbfs device entry"); return; } - dev->usbdevfs_dentry = fs_create_file (name, devmode | S_IFREG, - dev->bus->usbdevfs_dentry, dev, - &usbdevfs_device_file_operations, - devuid, devgid); - if (dev->usbdevfs_dentry == NULL) { - err ("error creating usbdevfs device entry"); - return; - } /* Set the size of the device's file to be * equal to the size of the device descriptors. */ @@ -775,11 +699,9 @@ void usbfs_add_device(struct usb_device *dev) } if (dev->usbfs_dentry->d_inode) dev->usbfs_dentry->d_inode->i_size = i_size; - if (dev->usbdevfs_dentry->d_inode) - dev->usbdevfs_dentry->d_inode->i_size = i_size; usbfs_update_special(); - usbdevfs_conn_disc_event(); + usbfs_conn_disc_event(); } void usbfs_remove_device(struct usb_device *dev) @@ -791,10 +713,6 @@ void usbfs_remove_device(struct usb_device *dev) fs_remove_file (dev->usbfs_dentry); dev->usbfs_dentry = NULL; } - if (dev->usbdevfs_dentry) { - fs_remove_file (dev->usbdevfs_dentry); - dev->usbdevfs_dentry = NULL; - } while (!list_empty(&dev->filelist)) { ds = list_entry(dev->filelist.next, struct dev_state, list); list_del_init(&ds->list); @@ -807,51 +725,38 @@ void usbfs_remove_device(struct usb_device *dev) } } usbfs_update_special(); - usbdevfs_conn_disc_event(); + usbfs_conn_disc_event(); } /* --------------------------------------------------------------------- */ -#ifdef CONFIG_PROC_FS static struct proc_dir_entry *usbdir = NULL; -#endif int __init usbfs_init(void) { int retval; - retval = usb_register(&usbdevfs_driver); + retval = usb_register(&usbfs_driver); if (retval) return retval; retval = register_filesystem(&usb_fs_type); if (retval) { - usb_deregister(&usbdevfs_driver); - return retval; - } - retval = register_filesystem(&usbdevice_fs_type); - if (retval) { - unregister_filesystem(&usb_fs_type); - usb_deregister(&usbdevfs_driver); + usb_deregister(&usbfs_driver); return retval; } -#ifdef CONFIG_PROC_FS - /* create mount point for usbdevfs */ + /* create mount point for usbfs */ usbdir = proc_mkdir("usb", proc_bus); -#endif return 0; } void usbfs_cleanup(void) { - usb_deregister(&usbdevfs_driver); + usb_deregister(&usbfs_driver); unregister_filesystem(&usb_fs_type); - unregister_filesystem(&usbdevice_fs_type); -#ifdef CONFIG_PROC_FS if (usbdir) remove_proc_entry("usb", proc_bus); -#endif } diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 98695c68d687..f2cd4770eb4f 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -17,6 +17,8 @@ #include <linux/init.h> #include <linux/mm.h> #include <linux/timer.h> +#include <linux/ctype.h> +#include <linux/device.h> #include <asm/byteorder.h> #include "hcd.h" /* for usbcore internals */ @@ -623,6 +625,20 @@ int usb_get_string(struct usb_device *dev, unsigned short langid, return result; } +static void usb_try_string_workarounds(unsigned char *buf, int *length) +{ + int newlength, oldlength = *length; + + for (newlength = 2; newlength + 1 < oldlength; newlength += 2) + if (!isprint(buf[newlength]) || buf[newlength + 1]) + break; + + if (newlength > 2) { + buf[0] = newlength; + *length = newlength; + } +} + static int usb_string_sub(struct usb_device *dev, unsigned int langid, unsigned int index, unsigned char *buf) { @@ -634,19 +650,26 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid, /* If that failed try to read the descriptor length, then * ask for just that many bytes */ - if (rc < 0) { + if (rc < 2) { rc = usb_get_string(dev, langid, index, buf, 2); if (rc == 2) rc = usb_get_string(dev, langid, index, buf, buf[0]); } - if (rc >= 0) { + if (rc >= 2) { + if (!buf[0] && !buf[1]) + usb_try_string_workarounds(buf, &rc); + /* There might be extra junk at the end of the descriptor */ if (buf[0] < rc) rc = buf[0]; - if (rc < 2) - rc = -EINVAL; + + rc = rc - (rc & 1); /* force a multiple of two */ } + + if (rc < 2) + rc = (rc < 0 ? rc : -EINVAL); + return rc; } @@ -724,6 +747,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) buf[idx] = 0; err = idx; + if (tbuf[1] != USB_DT_STRING) + dev_dbg(&dev->dev, "wrong descriptor type %02x for string %d (\"%s\")\n", tbuf[1], index, buf); + errout: kfree(tbuf); return err; @@ -1132,6 +1158,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) * use usb_set_interface() on the interfaces it claims. Resetting the whole * configuration would affect other drivers' interfaces. * + * The caller must own the device lock. + * * Returns zero on success, else a negative error code. */ int usb_reset_configuration(struct usb_device *dev) @@ -1142,9 +1170,9 @@ int usb_reset_configuration(struct usb_device *dev) if (dev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; - /* caller must own dev->serialize (config won't change) - * and the usb bus readlock (so driver bindings are stable); - * so calls during probe() are fine + /* caller must have locked the device and must own + * the usb bus readlock (so driver bindings are stable); + * calls during probe() are fine */ for (i = 1; i < 16; ++i) { @@ -1199,7 +1227,7 @@ static void release_interface(struct device *dev) * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated * @configuration: the configuration being chosen. - * Context: !in_interrupt(), caller holds dev->serialize + * Context: !in_interrupt(), caller owns the device lock * * This is used to enable non-default device modes. Not all devices * use this kind of configurability; many devices only have one @@ -1220,8 +1248,8 @@ static void release_interface(struct device *dev) * usb_set_interface(). * * This call is synchronous. The calling context must be able to sleep, - * and must not hold the driver model lock for USB; usb device driver - * probe() methods may not use this routine. + * must own the device lock, and must not hold the driver model's USB + * bus rwsem; usb device driver probe() methods cannot use this routine. * * Returns zero on success, or else the status code returned by the * underlying call that failed. On succesful completion, each interface @@ -1236,8 +1264,6 @@ int usb_set_configuration(struct usb_device *dev, int configuration) struct usb_interface **new_interfaces = NULL; int n, nintf; - /* dev->serialize guards all config changes */ - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { if (dev->config[i].desc.bConfigurationValue == configuration) { cp = &dev->config[i]; diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 78c5ca2f1051..bae974d587ae 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -27,11 +27,13 @@ static ssize_t show_##field (struct device *dev, char *buf) \ { \ struct usb_device *udev; \ + struct usb_host_config *actconfig; \ \ udev = to_usb_device (dev); \ - if (udev->actconfig) \ + actconfig = udev->actconfig; \ + if (actconfig) \ return sprintf (buf, format_string, \ - udev->actconfig->desc.field * multiplier); \ + actconfig->desc.field * multiplier); \ else \ return 0; \ } \ @@ -44,6 +46,28 @@ usb_actconfig_attr (bNumInterfaces, 1, "%2d\n") usb_actconfig_attr (bmAttributes, 1, "%2x\n") usb_actconfig_attr (bMaxPower, 2, "%3dmA\n") +#define usb_actconfig_str(name, field) \ +static ssize_t show_##name(struct device *dev, char *buf) \ +{ \ + struct usb_device *udev; \ + struct usb_host_config *actconfig; \ + int len; \ + \ + udev = to_usb_device (dev); \ + actconfig = udev->actconfig; \ + if (!actconfig) \ + return 0; \ + len = usb_string(udev, actconfig->desc.field, buf, PAGE_SIZE); \ + if (len < 0) \ + return 0; \ + buf[len] = '\n'; \ + buf[len+1] = 0; \ + return len+1; \ +} \ +static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); + +usb_actconfig_str (configuration, iConfiguration) + /* configuration value is always present, and r/w */ usb_actconfig_show(bConfigurationValue, 1, "%u\n"); @@ -55,9 +79,9 @@ set_bConfigurationValue (struct device *dev, const char *buf, size_t count) if (sscanf (buf, "%u", &config) != 1 || config > 255) return -EINVAL; - down(&udev->serialize); + usb_lock_device(udev); value = usb_set_configuration (udev, config); - up(&udev->serialize); + usb_unlock_device(udev); return (value < 0) ? value : count; } @@ -198,6 +222,7 @@ void usb_create_sysfs_dev_files (struct usb_device *udev) device_create_file (dev, &dev_attr_product); if (udev->descriptor.iSerialNumber) device_create_file (dev, &dev_attr_serial); + device_create_file (dev, &dev_attr_configuration); } void usb_remove_sysfs_dev_files (struct usb_device *udev) @@ -212,6 +237,7 @@ void usb_remove_sysfs_dev_files (struct usb_device *udev) device_remove_file(dev, &dev_attr_product); if (udev->descriptor.iSerialNumber) device_remove_file(dev, &dev_attr_serial); + device_remove_file (dev, &dev_attr_configuration); } /* Interface fields */ @@ -231,7 +257,26 @@ usb_intf_attr (bNumEndpoints, "%02x\n") usb_intf_attr (bInterfaceClass, "%02x\n") usb_intf_attr (bInterfaceSubClass, "%02x\n") usb_intf_attr (bInterfaceProtocol, "%02x\n") -usb_intf_attr (iInterface, "%02x\n") + +#define usb_intf_str(name, field) \ +static ssize_t show_##name(struct device *dev, char *buf) \ +{ \ + struct usb_interface *intf; \ + struct usb_device *udev; \ + int len; \ + \ + intf = to_usb_interface (dev); \ + udev = interface_to_usbdev (intf); \ + len = usb_string(udev, intf->cur_altsetting->desc.field, buf, PAGE_SIZE);\ + if (len < 0) \ + return 0; \ + buf[len] = '\n'; \ + buf[len+1] = 0; \ + return len+1; \ +} \ +static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); + +usb_intf_str (interface, iInterface); static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceNumber.attr, @@ -240,7 +285,6 @@ static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceClass.attr, &dev_attr_bInterfaceSubClass.attr, &dev_attr_bInterfaceProtocol.attr, - &dev_attr_iInterface.attr, NULL, }; static struct attribute_group intf_attr_grp = { @@ -250,9 +294,17 @@ static struct attribute_group intf_attr_grp = { void usb_create_sysfs_intf_files (struct usb_interface *intf) { sysfs_create_group(&intf->dev.kobj, &intf_attr_grp); + + if (intf->cur_altsetting->desc.iInterface) + device_create_file(&intf->dev, &dev_attr_interface); + } void usb_remove_sysfs_intf_files (struct usb_interface *intf) { sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp); + + if (intf->cur_altsetting->desc.iInterface) + device_remove_file(&intf->dev, &dev_attr_interface); + } diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 3c14361bbeb3..f57de4dca2bf 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -451,6 +451,11 @@ int usb_unlink_urb(struct urb *urb) if (!urb) return -EINVAL; if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) { +#ifdef CONFIG_DEBUG_KERNEL + printk(KERN_NOTICE "usb_unlink_urb() is deprecated for " + "synchronous unlinks. Use usb_kill_urb() instead.\n"); + WARN_ON(1); +#endif usb_kill_urb(urb); return 0; } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 138541225604..ca0b29d0ac1f 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -39,6 +39,7 @@ #include <linux/spinlock.h> #include <linux/errno.h> #include <linux/smp_lock.h> +#include <linux/rwsem.h> #include <linux/usb.h> #include <asm/io.h> @@ -62,6 +63,8 @@ const char *usbcore_name = "usbcore"; int nousb; /* Disable USB when built into kernel image */ /* Not honored on modular build */ +static DECLARE_RWSEM(usb_all_devices_rwsem); + static int generic_probe (struct device *dev) { @@ -100,7 +103,10 @@ int usb_probe_interface(struct device *dev) id = usb_match_id (intf, driver->id_table); if (id) { dev_dbg (dev, "%s - got id\n", __FUNCTION__); + intf->condition = USB_INTERFACE_BINDING; error = driver->probe (intf, id); + intf->condition = error ? USB_INTERFACE_UNBOUND : + USB_INTERFACE_BOUND; } return error; @@ -112,6 +118,8 @@ int usb_unbind_interface(struct device *dev) struct usb_interface *intf = to_usb_interface(dev); struct usb_driver *driver = to_usb_driver(intf->dev.driver); + intf->condition = USB_INTERFACE_UNBINDING; + /* release all urbs for this interface */ usb_disable_interface(interface_to_usbdev(intf), intf); @@ -123,6 +131,7 @@ int usb_unbind_interface(struct device *dev) intf->altsetting[0].desc.bInterfaceNumber, 0); usb_set_intfdata(intf, NULL); + intf->condition = USB_INTERFACE_UNBOUND; return 0; } @@ -153,7 +162,9 @@ int usb_register(struct usb_driver *new_driver) new_driver->driver.remove = usb_unbind_interface; new_driver->driver.owner = new_driver->owner; + usb_lock_all_devices(); retval = driver_register(&new_driver->driver); + usb_unlock_all_devices(); if (!retval) { pr_info("%s: registered new driver %s\n", @@ -182,7 +193,9 @@ void usb_deregister(struct usb_driver *driver) { pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name); + usb_lock_all_devices(); driver_unregister (&driver->driver); + usb_unlock_all_devices(); usbfs_update_special(); } @@ -204,7 +217,7 @@ void usb_deregister(struct usb_driver *driver) * alternate settings available for this interfaces. * * Don't call this function unless you are bound to one of the interfaces - * on this device or you own the dev->serialize semaphore! + * on this device or you have locked the device! */ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) { @@ -237,7 +250,7 @@ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) * drivers avoid such mistakes. * * Don't call this function unless you are bound to the intf interface - * or you own the device's ->serialize semaphore! + * or you have locked the device! */ struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf, unsigned int altnum) @@ -305,11 +318,12 @@ usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum) * way to bind to an interface is to return the private data from * the driver's probe() method. * - * Callers must own the driver model's usb bus writelock. So driver - * probe() entries don't need extra locking, but other call contexts - * may need to explicitly claim that lock. + * Callers must own the device lock and the driver model's usb_bus_type.subsys + * writelock. So driver probe() entries don't need extra locking, + * but other call contexts may need to explicitly claim those locks. */ -int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv) +int usb_driver_claim_interface(struct usb_driver *driver, + struct usb_interface *iface, void* priv) { struct device *dev = &iface->dev; @@ -318,6 +332,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface * dev->driver = &driver->driver; usb_set_intfdata(iface, priv); + iface->condition = USB_INTERFACE_BOUND; /* if interface was already added, bind now; else let * the future device_add() bind it, bypassing probe() @@ -338,8 +353,8 @@ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface * * also causes the driver disconnect() method to be called. * * This call is synchronous, and may not be used in an interrupt context. - * Callers must own the usb_device serialize semaphore and the driver model's - * usb bus writelock. So driver disconnect() entries don't need extra locking, + * Callers must own the device lock and the driver model's usb_bus_type.subsys + * writelock. So driver disconnect() entries don't need extra locking, * but other call contexts may need to explicitly claim those locks. */ void usb_driver_release_interface(struct usb_driver *driver, @@ -357,6 +372,7 @@ void usb_driver_release_interface(struct usb_driver *driver, dev->driver = NULL; usb_set_intfdata(iface, NULL); + iface->condition = USB_INTERFACE_UNBOUND; } /** @@ -748,7 +764,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port) init_MUTEX(&dev->serialize); if (dev->bus->op->allocate) - dev->bus->op->allocate(dev); + if (dev->bus->op->allocate(dev)) { + kfree(dev); + return NULL; + } return dev; } @@ -819,6 +838,160 @@ void usb_put_intf(struct usb_interface *intf) put_device(&intf->dev); } + +/* USB device locking + * + * Although locking USB devices should be straightforward, it is + * complicated by the way the driver-model core works. When a new USB + * driver is registered or unregistered, the core will automatically + * probe or disconnect all matching interfaces on all USB devices while + * holding the USB subsystem writelock. There's no good way for us to + * tell which devices will be used or to lock them beforehand; our only + * option is to effectively lock all the USB devices. + * + * We do that by using a private rw-semaphore, usb_all_devices_rwsem. + * When locking an individual device you must first acquire the rwsem's + * readlock. When a driver is registered or unregistered the writelock + * must be held. These actions are encapsulated in the subroutines + * below, so all a driver needs to do is call usb_lock_device() and + * usb_unlock_device(). + * + * Complications arise when several devices are to be locked at the same + * time. Only hub-aware drivers that are part of usbcore ever have to + * do this; nobody else needs to worry about it. The problem is that + * usb_lock_device() must not be called to lock a second device since it + * would acquire the rwsem's readlock reentrantly, leading to deadlock if + * another thread was waiting for the writelock. The solution is simple: + * + * When locking more than one device, call usb_lock_device() + * to lock the first one. Lock the others by calling + * down(&udev->serialize) directly. + * + * When unlocking multiple devices, use up(&udev->serialize) + * to unlock all but the last one. Unlock the last one by + * calling usb_unlock_device(). + * + * When locking both a device and its parent, always lock the + * the parent first. + */ + +/** + * usb_lock_device - acquire the lock for a usb device structure + * @udev: device that's being locked + * + * Use this routine when you don't hold any other device locks; + * to acquire nested inner locks call down(&udev->serialize) directly. + * This is necessary for proper interaction with usb_lock_all_devices(). + */ +void usb_lock_device(struct usb_device *udev) +{ + down_read(&usb_all_devices_rwsem); + down(&udev->serialize); +} + +/** + * usb_trylock_device - attempt to acquire the lock for a usb device structure + * @udev: device that's being locked + * + * Don't use this routine if you already hold a device lock; + * use down_trylock(&udev->serialize) instead. + * This is necessary for proper interaction with usb_lock_all_devices(). + * + * Returns 1 if successful, 0 if contention. + */ +int usb_trylock_device(struct usb_device *udev) +{ + if (!down_read_trylock(&usb_all_devices_rwsem)) + return 0; + if (down_trylock(&udev->serialize)) { + up_read(&usb_all_devices_rwsem); + return 0; + } + return 1; +} + +/** + * usb_lock_device_for_reset - cautiously acquire the lock for a + * usb device structure + * @udev: device that's being locked + * @iface: interface bound to the driver making the request (optional) + * + * Attempts to acquire the device lock, but fails if the device is + * NOTATTACHED or SUSPENDED, or if iface is specified and the interface + * is neither BINDING nor BOUND. Rather than sleeping to wait for the + * lock, the routine polls repeatedly. This is to prevent deadlock with + * disconnect; in some drivers (such as usb-storage) the disconnect() + * callback will block waiting for a device reset to complete. + * + * Returns a negative error code for failure, otherwise 1 or 0 to indicate + * that the device will or will not have to be unlocked. (0 can be + * returned when an interface is given and is BINDING, because in that + * case the driver already owns the device lock.) + */ +int usb_lock_device_for_reset(struct usb_device *udev, + struct usb_interface *iface) +{ + if (udev->state == USB_STATE_NOTATTACHED) + return -ENODEV; + if (udev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + if (iface) { + switch (iface->condition) { + case USB_INTERFACE_BINDING: + return 0; + case USB_INTERFACE_BOUND: + break; + default: + return -EINTR; + } + } + + while (!usb_trylock_device(udev)) { + msleep(15); + if (udev->state == USB_STATE_NOTATTACHED) + return -ENODEV; + if (udev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + if (iface && iface->condition != USB_INTERFACE_BOUND) + return -EINTR; + } + return 1; +} + +/** + * usb_unlock_device - release the lock for a usb device structure + * @udev: device that's being unlocked + * + * Use this routine when releasing the only device lock you hold; + * to release inner nested locks call up(&udev->serialize) directly. + * This is necessary for proper interaction with usb_lock_all_devices(). + */ +void usb_unlock_device(struct usb_device *udev) +{ + up(&udev->serialize); + up_read(&usb_all_devices_rwsem); +} + +/** + * usb_lock_all_devices - acquire the lock for all usb device structures + * + * This is necessary when registering a new driver or probing a bus, + * since the driver-model core may try to use any usb_device. + */ +void usb_lock_all_devices(void) +{ + down_write(&usb_all_devices_rwsem); +} + +/** + * usb_unlock_all_devices - release the lock for all usb device structures + */ +void usb_unlock_all_devices(void) +{ + up_write(&usb_all_devices_rwsem); +} + + static struct usb_device *match_device(struct usb_device *dev, u16 vendor_id, u16 product_id) { @@ -840,8 +1013,10 @@ static struct usb_device *match_device(struct usb_device *dev, /* look through all of the children of this device */ for (child = 0; child < dev->maxchild; ++child) { if (dev->children[child]) { + down(&dev->children[child]->serialize); ret_dev = match_device(dev->children[child], vendor_id, product_id); + up(&dev->children[child]->serialize); if (ret_dev) goto exit; } @@ -876,7 +1051,9 @@ struct usb_device *usb_find_device(u16 vendor_id, u16 product_id) bus = container_of(buslist, struct usb_bus, bus_list); if (!bus->root_hub) continue; + usb_lock_device(bus->root_hub); dev = match_device(bus->root_hub, vendor_id, product_id); + usb_unlock_device(bus->root_hub); if (dev) goto exit; } @@ -1362,6 +1539,11 @@ EXPORT_SYMBOL(usb_put_dev); EXPORT_SYMBOL(usb_get_dev); EXPORT_SYMBOL(usb_hub_tt_clear_buffer); +EXPORT_SYMBOL(usb_lock_device); +EXPORT_SYMBOL(usb_trylock_device); +EXPORT_SYMBOL(usb_lock_device_for_reset); +EXPORT_SYMBOL(usb_unlock_device); + EXPORT_SYMBOL(usb_driver_claim_interface); EXPORT_SYMBOL(usb_driver_release_interface); EXPORT_SYMBOL(usb_match_id); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index f1dff4f4d5d6..30d2cf7dd762 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -22,8 +22,14 @@ extern int usb_get_device_descriptor(struct usb_device *dev, unsigned int size); extern int usb_set_configuration(struct usb_device *dev, int configuration); -extern void usb_set_device_state(struct usb_device *udev, - enum usb_device_state new_state); +extern void usb_lock_all_devices(void); +extern void usb_unlock_all_devices(void); /* for labeling diagnostics */ extern const char *usbcore_name; + +/* usbfs stuff */ +extern struct usb_driver usbfs_driver; +extern struct file_operations usbfs_devices_fops; +extern struct file_operations usbfs_device_file_operations; +extern void usbfs_conn_disc_event(void); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index decbca335389..c6e0693ed1d6 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -39,6 +39,17 @@ config USB_GADGET If in doubt, say "N" and don't enable these drivers; most people don't have this kind of hardware (except maybe inside Linux PDAs). +config USB_GADGET_DEBUG_FILES + boolean "Debugging information files" + depends on USB_GADGET && PROC_FS + help + Some of the drivers in the "gadget" framework can expose + debugging information in files such as /proc/driver/udc + (for a peripheral controller). The information in these + files may help when you're troubleshooting or bringing up a + driver on a new board. Enable these files by choosing "Y" + here. If in doubt, or to conserve kernel memory, say "N". + # # USB Peripheral Controller Support # @@ -206,10 +217,6 @@ config USB_OTG Select this only if your OMAP board has a Mini-AB connector. -config USB_OMAP_PROC - boolean "/proc/driver/udc file" - depends on USB_GADGET_OMAP - endchoice config USB_GADGET_DUALSPEED diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 91e878642b9f..ed532f7176f9 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -770,7 +770,8 @@ usb_gadget_unregister_driver (struct usb_gadget_driver *driver) spin_lock_irqsave (&dum->lock, flags); stop_activity (dum, driver); - dum->port_status &= ~USB_PORT_STAT_CONNECTION; + 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); spin_unlock_irqrestore (&dum->lock, flags); @@ -815,8 +816,8 @@ static int dummy_urb_enqueue ( struct dummy *dum; unsigned long flags; - /* patch to usb_sg_init() is in 2.5.60 */ - BUG_ON (!urb->transfer_buffer && urb->transfer_buffer_length); + if (!urb->transfer_buffer && urb->transfer_buffer_length) + return -EINVAL; dum = container_of (hcd, struct dummy, hcd); spin_lock_irqsave (&dum->lock, flags); @@ -1102,10 +1103,10 @@ restart: ep = find_endpoint(dum, address); if (!ep) { /* set_configuration() disagreement */ - dev_err (hardware, + dev_dbg (hardware, "no ep configured for urb %p\n", urb); - maybe_set_status (urb, -ETIMEDOUT); + maybe_set_status (urb, -EPROTO); goto return_urb; } @@ -1409,9 +1410,12 @@ static int dummy_hub_control ( case ClearPortFeature: switch (wValue) { case USB_PORT_FEAT_SUSPEND: - /* 20msec resume signaling */ - dum->resuming = 1; - dum->re_timeout = jiffies + ((HZ * 20)/1000); + if (dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)) { + /* 20msec resume signaling */ + dum->resuming = 1; + dum->re_timeout = jiffies + + msecs_to_jiffies(20); + } break; case USB_PORT_FEAT_POWER: dum->port_status = 0; @@ -1440,7 +1444,7 @@ static int dummy_hub_control ( dum->port_status &= ~(1 << USB_PORT_FEAT_SUSPEND); dum->resuming = 0; dum->re_timeout = 0; - if (dum->driver->resume) { + if (dum->driver && dum->driver->resume) { spin_unlock (&dum->lock); dum->driver->resume (&dum->gadget); spin_lock (&dum->lock); @@ -1481,11 +1485,15 @@ static int dummy_hub_control ( case SetPortFeature: switch (wValue) { case USB_PORT_FEAT_SUSPEND: - dum->port_status |= (1 << USB_PORT_FEAT_SUSPEND); - if (dum->driver->suspend) { - spin_unlock (&dum->lock); - dum->driver->suspend (&dum->gadget); - spin_lock (&dum->lock); + 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); + } } break; case USB_PORT_FEAT_RESET: @@ -1502,7 +1510,7 @@ static int dummy_hub_control ( /* FIXME test that code path! */ } /* 50msec reset signaling */ - dum->re_timeout = jiffies + ((HZ * 50)/1000); + dum->re_timeout = jiffies + msecs_to_jiffies(50); /* FALLTHROUGH */ default: dum->port_status |= (1 << wValue); @@ -1790,4 +1798,3 @@ static void __exit cleanup (void) the_controller = 0; } module_exit (cleanup); - diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 763d0552146a..e3fec4a5a7f8 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -84,7 +84,7 @@ */ #define DRIVER_DESC "Ethernet Gadget" -#define DRIVER_VERSION "St Patrick's Day 2004" +#define DRIVER_VERSION "Equinox 2004" static const char shortname [] = "ether"; static const char driver_desc [] = DRIVER_DESC; @@ -231,6 +231,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); #define DEV_CONFIG_CDC #endif +#ifdef CONFIG_USB_GADGET_N9604 +#define DEV_CONFIG_CDC +#endif + /* For CDC-incapable hardware, choose the simple cdc subset. * Anything that talks bulk (without notable bugs) can do this. @@ -387,7 +391,7 @@ eth_config = { .bConfigurationValue = DEV_CONFIG_VALUE, .iConfiguration = STRING_CDC, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 1, + .bMaxPower = 50, }; #ifdef CONFIG_USB_ETH_RNDIS @@ -401,7 +405,7 @@ rndis_config = { .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE, .iConfiguration = STRING_RNDIS, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 1, + .bMaxPower = 50, }; #endif @@ -1198,13 +1202,20 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags) result = -EINVAL; /* FALL THROUGH */ case 0: - return result; + break; } - if (result) - eth_reset_config (dev); - else { + if (result) { + if (number) + eth_reset_config (dev); + usb_gadget_vbus_draw(dev->gadget, + dev->gadget->is_otg ? 8 : 100); + } else { char *speed; + unsigned power; + + power = 2 * eth_config.bMaxPower; + usb_gadget_vbus_draw(dev->gadget, power); switch (gadget->speed) { case USB_SPEED_FULL: speed = "full"; break; @@ -1215,8 +1226,8 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags) } dev->config = number; - INFO (dev, "%s speed config #%d: %s, using %s\n", - speed, number, driver_desc, + INFO (dev, "%s speed config #%d: %d mA, %s, using %s\n", + speed, number, power, driver_desc, dev->rndis ? "RNDIS" : (dev->cdc @@ -1375,8 +1386,9 @@ static void eth_setup_complete (struct usb_ep *ep, struct usb_request *req) static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req) { if (req->status || req->actual != req->length) - DEBUG (dev, "rndis response complete --> %d, %d/%d\n", - req->status, req->actual, req->length); + DEBUG ((struct eth_dev *) ep->driver_data, + "rndis response complete --> %d, %d/%d\n", + req->status, req->actual, req->length); /* done sending after CDC_GET_ENCAPSULATED_RESPONSE */ } @@ -2098,11 +2110,13 @@ static void rndis_send_media_state (struct eth_dev *dev, int connect) } } -static void rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req) +static void +rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req) { if (req->status || req->actual != req->length) - DEBUG (dev, "rndis control ack complete --> %d, %d/%d\n", - req->status, req->actual, req->length); + DEBUG ((struct eth_dev *) ep->driver_data, + "rndis control ack complete --> %d, %d/%d\n", + req->status, req->actual, req->length); usb_ep_free_buffer(ep, req->buf, req->dma, 8); usb_ep_free_request(ep, req); @@ -2334,6 +2348,8 @@ eth_bind (struct usb_gadget *gadget) device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208); } else if (gadget_is_lh7a40x(gadget)) { device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209); + } else if (gadget_is_n9604(gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x020a); } else { /* can't assume CDC works. don't want to default to * anything less functional on CDC-capable hardware, @@ -2466,8 +2482,10 @@ autoconf_fail: if (gadget->is_otg) { otg_descriptor.bmAttributes |= USB_OTG_HNP, eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + eth_config.bMaxPower = 4; #ifdef CONFIG_USB_ETH_RNDIS rndis_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + rndis_config.bMaxPower = 4; #endif } diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 6e8008fa43cb..d8c8ab7750ec 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -217,6 +217,7 @@ #include <linux/compiler.h> #include <linux/completion.h> #include <linux/dcache.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/fcntl.h> #include <linux/file.h> @@ -234,6 +235,7 @@ #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/string.h> +#include <linux/suspend.h> #include <linux/uts.h> #include <linux/version.h> #include <linux/wait.h> @@ -248,7 +250,7 @@ #define DRIVER_DESC "File-backed Storage Gadget" #define DRIVER_NAME "g_file_storage" -#define DRIVER_VERSION "28 July 2004" +#define DRIVER_VERSION "31 August 2004" static const char longname[] = DRIVER_DESC; static const char shortname[] = DRIVER_NAME; @@ -866,6 +868,14 @@ config_desc = { .bMaxPower = 1, // self-powered }; +static struct usb_otg_descriptor +otg_desc = { + .bLength = sizeof(otg_desc), + .bDescriptorType = USB_DT_OTG, + + .bmAttributes = USB_OTG_SRP, +}; + /* There is only one interface. */ static struct usb_interface_descriptor @@ -914,12 +924,14 @@ fs_intr_in_desc = { }; static const struct usb_descriptor_header *fs_function[] = { + (struct usb_descriptor_header *) &otg_desc, (struct usb_descriptor_header *) &intf_desc, (struct usb_descriptor_header *) &fs_bulk_in_desc, (struct usb_descriptor_header *) &fs_bulk_out_desc, (struct usb_descriptor_header *) &fs_intr_in_desc, NULL, }; +#define FS_FUNCTION_PRE_EP_ENTRIES 2 #ifdef CONFIG_USB_GADGET_DUALSPEED @@ -976,12 +988,14 @@ hs_intr_in_desc = { }; static const struct usb_descriptor_header *hs_function[] = { + (struct usb_descriptor_header *) &otg_desc, (struct usb_descriptor_header *) &intf_desc, (struct usb_descriptor_header *) &hs_bulk_in_desc, (struct usb_descriptor_header *) &hs_bulk_out_desc, (struct usb_descriptor_header *) &hs_intr_in_desc, NULL, }; +#define HS_FUNCTION_PRE_EP_ENTRIES 2 /* Maxpacket and other transfer characteristics vary by speed. */ #define ep_desc(g,fs,hs) (((g)->speed==USB_SPEED_HIGH) ? (hs) : (fs)) @@ -1018,9 +1032,10 @@ static struct usb_gadget_strings stringtab = { * and with code managing interfaces and their altsettings. They must * also handle different speeds and other-speed requests. */ -static int populate_config_buf(enum usb_device_speed speed, +static int populate_config_buf(struct usb_gadget *gadget, u8 *buf, u8 type, unsigned index) { + enum usb_device_speed speed = gadget->speed; int len; const struct usb_descriptor_header **function; @@ -1036,6 +1051,10 @@ static int populate_config_buf(enum usb_device_speed speed, #endif function = fs_function; + /* for now, don't advertise srp-only devices */ + if (!gadget->is_otg) + function++; + len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function); ((struct usb_config_descriptor *) buf)->bDescriptorType = type; return len; @@ -1366,7 +1385,7 @@ static int standard_setup_req(struct fsg_dev *fsg, #ifdef CONFIG_USB_GADGET_DUALSPEED get_config: #endif - value = populate_config_buf(fsg->gadget->speed, + value = populate_config_buf(fsg->gadget, req->buf, ctrl->wValue >> 8, ctrl->wValue & 0xff); @@ -1523,6 +1542,8 @@ static int sleep_thread(struct fsg_dev *fsg) rc = wait_event_interruptible(fsg->thread_wqh, fsg->thread_wakeup_needed); fsg->thread_wakeup_needed = 0; + if (current->flags & PF_FREEZE) + refrigerator(PF_FREEZE); return (rc ? -EINTR : 0); } @@ -2280,8 +2301,7 @@ static int halt_bulk_in_endpoint(struct fsg_dev *fsg) } /* Wait for a short time and then try again */ - set_current_state(TASK_INTERRUPTIBLE); - if (schedule_timeout(HZ / 10) != 0) + if (msleep_interruptible(100) != 0) return -EINTR; rc = usb_ep_set_halt(fsg->bulk_in); } @@ -3713,8 +3733,10 @@ static int __init check_parameters(struct fsg_dev *fsg) mod_data.release = __constant_cpu_to_le16(0x0307); else if (gadget_is_omap(fsg->gadget)) mod_data.release = __constant_cpu_to_le16(0x0308); - else if (gadget_is_lh7a40x(gadget)) + else if (gadget_is_lh7a40x(fsg->gadget)) mod_data.release = __constant_cpu_to_le16 (0x0309); + else if (gadget_is_n9604(fsg->gadget)) + mod_data.release = __constant_cpu_to_le16 (0x030a); else { WARN(fsg, "controller '%s' not recognized\n", fsg->gadget->name); @@ -3882,10 +3904,10 @@ static int __init fsg_bind(struct usb_gadget *gadget) intf_desc.bNumEndpoints = i; intf_desc.bInterfaceSubClass = mod_data.protocol_type; intf_desc.bInterfaceProtocol = mod_data.transport_type; - fs_function[i+1] = NULL; + fs_function[i + FS_FUNCTION_PRE_EP_ENTRIES] = NULL; #ifdef CONFIG_USB_GADGET_DUALSPEED - hs_function[i+1] = NULL; + hs_function[i + HS_FUNCTION_PRE_EP_ENTRIES] = NULL; /* Assume ep0 uses the same maxpacket value for both speeds */ dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket; @@ -3896,6 +3918,11 @@ static int __init fsg_bind(struct usb_gadget *gadget) hs_intr_in_desc.bEndpointAddress = fs_intr_in_desc.bEndpointAddress; #endif + if (gadget->is_otg) { + otg_desc.bmAttributes |= USB_OTG_HNP, + config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + rc = -ENOMEM; /* Allocate the request and buffer for endpoint 0 */ diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index e24e2f9ad28e..f6273701fec6 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -62,6 +62,12 @@ #define gadget_is_omap(g) 0 #endif +#ifdef CONFIG_USB_GADGET_N9604 +#define gadget_is_n9604(g) !strcmp("n9604_udc", (g)->name) +#else +#define gadget_is_n9604(g) 0 +#endif + // CONFIG_USB_GADGET_AT91RM9200 // CONFIG_USB_GADGET_SX2 // CONFIG_USB_GADGET_AU1X00 diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 61ce5b2eea41..f9cdb23da15b 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1092,13 +1092,7 @@ static inline char *dmastr(void) return "(dma IN)"; } -/* if we're trying to save space, don't bother with this proc file */ - -#if defined(CONFIG_PROC_FS) && !defined(CONFIG_EMBEDDED) -# define UDC_PROC_FILE -#endif - -#ifdef UDC_PROC_FILE +#ifdef CONFIG_USB_GADGET_DEBUG_FILES static const char proc_node_name [] = "driver/udc"; @@ -1312,7 +1306,7 @@ done: return count - size; } -#endif /* UDC_PROC_FILE */ +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ /*-------------------------------------------------------------------------*/ @@ -1815,7 +1809,7 @@ static void goku_remove(struct pci_dev *pdev) usb_gadget_unregister_driver(dev->driver); } -#ifdef UDC_PROC_FILE +#ifdef CONFIG_USB_GADGET_DEBUG_FILES remove_proc_entry(proc_node_name, NULL); #endif if (dev->regs) @@ -1933,7 +1927,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_master(pdev); -#ifdef UDC_PROC_FILE +#ifdef CONFIG_USB_GADGET_DEBUG_FILES create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev); #endif diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c index 772627e97a00..0def9f70e889 100644 --- a/drivers/usb/gadget/lh7a40x_udc.c +++ b/drivers/usb/gadget/lh7a40x_udc.c @@ -54,7 +54,6 @@ static const char ep0name[] = "ep0-control"; /* Local definintions. */ -#define UDC_PROC_FILE #ifndef NO_STATES static char *state_names[] = { @@ -192,7 +191,7 @@ static __inline__ void usb_clear(u32 val, u32 port) */ #define is_usb_connected() get_portc_pdr(2) -#ifdef UDC_PROC_FILE +#ifdef CONFIG_USB_GADGET_DEBUG_FILES static const char proc_node_name[] = "driver/udc"; @@ -248,12 +247,12 @@ udc_proc_read(char *page, char **start, off_t off, int count, #define create_proc_files() create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev) #define remove_proc_files() remove_proc_entry(proc_node_name, NULL) -#else /* !UDC_PROC_FILE */ +#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ #define create_proc_files() do {} while (0) #define remove_proc_files() do {} while (0) -#endif /* UDC_PROC_FILE */ +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ /* * udc_disable - disable USB device controller diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index d28de0b8ceba..ed6711d54c32 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -76,7 +76,6 @@ #define EP_DONTUSE 13 /* nonzero */ #define USE_RDK_LEDS /* GPIO pins control three LEDs */ -#define USE_SYSFS_DEBUG_FILES static const char driver_name [] = "net2280"; @@ -117,7 +116,7 @@ module_param (fifo_mode, ushort, 0644); #define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") -#if defined(USE_SYSFS_DEBUG_FILES) || defined (DEBUG) +#if defined(CONFIG_USB_GADGET_DEBUG_FILES) || defined (DEBUG) static char *type_string (u8 bmAttributes) { switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { @@ -1450,7 +1449,12 @@ static const struct usb_gadget_ops net2280_ops = { /*-------------------------------------------------------------------------*/ -#ifdef USE_SYSFS_DEBUG_FILES +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +/* FIXME move these into procfs, and use seq_file. + * Sysfs _still_ doesn't behave for arbitrarily sized files, + * and also doesn't help products using this with 2.4 kernels. + */ /* "function" sysfs attribute */ static ssize_t diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index e40089d79365..c321c542f0df 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -1200,7 +1200,8 @@ static void pullup_enable(struct omap_udc *udc) { UDC_SYSCON1_REG |= UDC_PULLUP_EN; #ifndef CONFIG_USB_OTG - OTG_CTRL_REG |= OTG_BSESSVLD; + if (!cpu_is_omap15xx()) + OTG_CTRL_REG |= OTG_BSESSVLD; #endif UDC_IRQ_EN_REG = UDC_DS_CHG_IE; } @@ -1208,7 +1209,8 @@ static void pullup_enable(struct omap_udc *udc) static void pullup_disable(struct omap_udc *udc) { #ifndef CONFIG_USB_OTG - OTG_CTRL_REG &= ~OTG_BSESSVLD; + if (!cpu_is_omap15xx()) + OTG_CTRL_REG &= ~OTG_BSESSVLD; #endif UDC_IRQ_EN_REG = UDC_DS_CHG_IE; UDC_SYSCON1_REG &= ~UDC_PULLUP_EN; @@ -1688,7 +1690,7 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) } change &= ~UDC_SUS; } - if (change & OTG_FLAGS) { + if (!cpu_is_omap15xx() && (change & OTG_FLAGS)) { update_otg(udc); change &= ~OTG_FLAGS; } @@ -1974,7 +1976,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver); /*-------------------------------------------------------------------------*/ -#ifdef CONFIG_USB_OMAP_PROC +#ifdef CONFIG_USB_GADGET_DEBUG_FILES #include <linux/seq_file.h> @@ -2036,34 +2038,14 @@ static char *trx_mode(unsigned m) } } -static int proc_udc_show(struct seq_file *s, void *_) +static int proc_otg_show(struct seq_file *s) { u32 tmp; - struct omap_ep *ep; - unsigned long flags; - - spin_lock_irqsave(&udc->lock, flags); - seq_printf(s, "%s, version: " DRIVER_VERSION -#ifdef USE_ISO - " (iso)" -#endif - "%s\n", - driver_desc, - use_dma ? " (dma)" : ""); - - tmp = UDC_REV_REG & 0xff; - seq_printf(s, - "UDC rev %d.%d, OTG rev %d.%d, fifo mode %d, gadget %s\n" - "hmc %d, transceiver %08x %s\n", + tmp = OTG_REV_REG; + seq_printf(s, "OTG rev %d.%d, transceiver_ctrl %08x\n", tmp >> 4, tmp & 0xf, - OTG_REV_REG >> 4, OTG_REV_REG & 0xf, - fifo_mode, - udc->driver ? udc->driver->driver.name : "(none)", - HMC, USB_TRANSCEIVER_CTRL_REG, - udc->transceiver ? udc->transceiver->label : ""); - - /* OTG controller registers */ + USB_TRANSCEIVER_CTRL_REG); tmp = OTG_SYSCON_1_REG; seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s," FOURBITS "\n", tmp, @@ -2117,6 +2099,37 @@ static int proc_udc_show(struct seq_file *s, void *_) seq_printf(s, "otg_outctrl %04x" "\n", tmp); tmp = OTG_TEST_REG; seq_printf(s, "otg_test %04x" "\n", tmp); +} + +static int proc_udc_show(struct seq_file *s, void *_) +{ + u32 tmp; + struct omap_ep *ep; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + seq_printf(s, "%s, version: " DRIVER_VERSION +#ifdef USE_ISO + " (iso)" +#endif + "%s\n", + driver_desc, + use_dma ? " (dma)" : ""); + + tmp = UDC_REV_REG & 0xff; + seq_printf(s, + "UDC rev %d.%d, fifo mode %d, gadget %s\n" + "hmc %d, transceiver %s\n", + tmp >> 4, tmp & 0xf, + fifo_mode, + udc->driver ? udc->driver->driver.name : "(none)", + HMC, + udc->transceiver ? udc->transceiver->label : ""); + + /* OTG controller registers */ + if (!cpu_is_omap15xx()) + proc_otg_show(s); tmp = UDC_SYSCON1_REG; seq_printf(s, "\nsyscon1 %04x" EIGHTBITS "\n", tmp, @@ -2496,41 +2509,51 @@ static int __init omap_udc_probe(struct device *dev) return -EBUSY; } - INFO("OMAP UDC rev %d.%d, OTG rev %d.%d, %s receptacle\n", + INFO("OMAP UDC rev %d.%d, %s receptacle\n", UDC_REV_REG >> 4, UDC_REV_REG & 0xf, - OTG_REV_REG >> 4, OTG_REV_REG & 0xf, config->otg ? "Mini-AB" : "B/Mini-B"); /* use the mode given to us by board init code */ - hmc = HMC; - switch (hmc) { - case 3: - case 11: - case 19: - case 25: - xceiv = otg_get_transceiver(); - if (!xceiv) { - DBG("external transceiver not registered!\n"); - goto cleanup0; - } - type = xceiv->label; - break; - case 0: /* POWERUP DEFAULT == 0 */ - case 4: - case 12: - case 20: - type = "INTEGRATED"; - break; - case 21: /* internal loopback */ - type = "(loopback)"; - break; - case 14: /* transceiverless */ - type = "(none)"; - break; + if (cpu_is_omap15xx()) { + hmc = HMC_1510; + type = "(unknown)"; - default: - ERR("unrecognized UDC HMC mode %d\n", hmc); - return -ENODEV; + /* FIXME may need a GPIO-0 handler to call + * usb_gadget_vbus_{dis,}connect() on us... + */ + } else { + hmc = HMC_1610; + switch (hmc) { + case 3: + case 11: + 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"; + break; + case 21: /* internal loopback */ + type = "(loopback)"; + break; + case 14: /* transceiverless */ + type = "(none)"; + break; + + default: + ERR("unrecognized UDC HMC mode %d\n", hmc); + return -ENODEV; + } } INFO("hmc mode %d, transceiver %s\n", hmc, type); @@ -2671,13 +2694,6 @@ static struct device_driver udc_driver = { static int __init udc_init(void) { - /* should work on many OMAP systems with at most minor changes, - * but the 1510 doesn't have an OTG controller. - */ - if (cpu_is_omap1510()) { - DBG("no OMAP1510 support yet\n"); - return -ENODEV; - } INFO("%s, version: " DRIVER_VERSION "%s\n", driver_desc, use_dma ? " (dma)" : ""); return driver_register(&udc_driver); diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h index bd5420cd0b05..ca8572314f95 100644 --- a/drivers/usb/gadget/omap_udc.h +++ b/drivers/usb/gadget/omap_udc.h @@ -193,7 +193,14 @@ struct omap_udc { /*-------------------------------------------------------------------------*/ -// #define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f) +#define MOD_CONF_CTRL_0_REG __REG32(MOD_CONF_CTRL_0) +#define VBUS_W2FC_1510 (1 << 17) /* 0 gpio0, 1 dvdd2 pin */ + +#define FUNC_MUX_CTRL_0_REG __REG32(FUNC_MUX_CTRL_0) +#define VBUS_CTRL_1510 (1 << 19) /* 1 connected (software) */ +#define VBUS_MODE_1510 (1 << 18) /* 0 hardware, 1 software */ + +#define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f) #define HMC_1610 (OTG_SYSCON_2_REG & 0x3f) -#define HMC HMC_1610 +#define HMC (cpu_is_omap15xx() ? HMC_1510 : HMC_1610) diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c index c1139b51db46..710f7a435f9e 100644 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c @@ -92,10 +92,6 @@ static const char ep0name [] = "ep0"; // #define USE_OUT_DMA // #define DISABLE_TEST_MODE -#ifdef CONFIG_PROC_FS -#define UDC_PROC_FILE -#endif - #ifdef CONFIG_ARCH_IXP4XX #undef USE_DMA @@ -109,12 +105,6 @@ static const char ep0name [] = "ep0"; #include "pxa2xx_udc.h" -#ifdef CONFIG_EMBEDDED -/* few strings, and little code to use them */ -#undef DEBUG -#undef UDC_PROC_FILE -#endif - #ifdef USE_DMA static int use_dma = 1; module_param(use_dma, bool, 0); @@ -1212,7 +1202,7 @@ static const struct usb_gadget_ops pxa2xx_udc_ops = { /*-------------------------------------------------------------------------*/ -#ifdef UDC_PROC_FILE +#ifdef CONFIG_USB_GADGET_DEBUG_FILES static const char proc_node_name [] = "driver/udc"; @@ -1368,11 +1358,12 @@ done: #define remove_proc_files() \ remove_proc_entry(proc_node_name, NULL) -#else /* !UDC_PROC_FILE */ +#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ + #define create_proc_files() do {} while (0) #define remove_proc_files() do {} while (0) -#endif /* UDC_PROC_FILE */ +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ /* "function" sysfs attribute */ static ssize_t diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 1cd445a01d13..561ca545e4e5 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -70,8 +70,6 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging"); #define RNDIS_MAX_CONFIGS 1 -static struct proc_dir_entry *rndis_connect_dir; -static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS]; @@ -1275,6 +1273,8 @@ int rndis_rm_hdr (u8 *buf, u32 *length) return 0; } +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -1365,43 +1365,40 @@ int rndis_proc_write (struct file *file, const char __user *buffer, return count; } +#define NAME_TEMPLATE "driver/rndis-%03d" + +static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; + +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + + int __init rndis_init (void) { u8 i; - char name [4]; - /* FIXME this should probably be /proc/driver/rndis, - * and only if debugging is enabled - */ - - if (!(rndis_connect_dir = proc_mkdir ("rndis", NULL))) { - printk (KERN_ERR "%s: couldn't create /proc/rndis entry", - __FUNCTION__); - return -EIO; - } - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { - sprintf (name, "%03d", i); +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + char name [20]; + + sprintf (name, NAME_TEMPLATE, i); if (!(rndis_connect_state [i] - = create_proc_entry (name, 0660, - rndis_connect_dir))) + = create_proc_entry (name, 0660, NULL))) { DEBUG ("%s :remove entries", __FUNCTION__); - for (i--; i > 0; i--) { - sprintf (name, "%03d", i); - remove_proc_entry (name, rndis_connect_dir); + while (i) { + sprintf (name, NAME_TEMPLATE, --i); + remove_proc_entry (name, NULL); } DEBUG ("\n"); - - remove_proc_entry ("000", rndis_connect_dir); - remove_proc_entry ("rndis", NULL); return -EIO; } + rndis_connect_state [i]->nlink = 1; rndis_connect_state [i]->write_proc = rndis_proc_write; rndis_connect_state [i]->read_proc = rndis_proc_read; rndis_connect_state [i]->data = (void *) (rndis_per_dev_params + i); +#endif rndis_per_dev_params [i].confignr = i; rndis_per_dev_params [i].used = 0; rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED; @@ -1415,14 +1412,14 @@ int __init rndis_init (void) void rndis_exit (void) { +#ifdef CONFIG_USB_GADGET_DEBUG_FILES u8 i; - char name [4]; + char name [20]; for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { - sprintf (name, "%03d", i); - remove_proc_entry (name, rndis_connect_dir); + sprintf (name, NAME_TEMPLATE, i); + remove_proc_entry (name, NULL); } - remove_proc_entry ("rndis", NULL); - return; +#endif } diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index a415a33ed117..69962c30a3a9 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -1188,6 +1188,8 @@ autoconf_fail: device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208); } else if (gadget_is_lh7a40x(gadget)) { device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209); + } else if (gadget_is_n9604(gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x020a); } else { /* gadget zero is so simple (for now, no altsettings) that * it SHOULD NOT have problems with bulk-capable hardware. diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index e25adf501967..03d427a672a4 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -155,7 +155,7 @@ MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); * before driver shutdown. But it also seems to be caused by bugs in cardbus * bridge shutdown: shutting down the bridge before the devices using it. */ -static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec) +static int handshake (void __iomem *ptr, u32 mask, u32 done, int usec) { u32 result; @@ -341,8 +341,7 @@ static int ehci_hc_reset (struct usb_hcd *hcd) spin_lock_init (&ehci->lock); ehci->caps = hcd->regs; - ehci->regs = (hcd->regs + - HC_LENGTH (readl (&ehci->caps->hc_capbase))); + ehci->regs = hcd->regs + HC_LENGTH (readl (&ehci->caps->hc_capbase)); dbg_hcs_params (ehci, "reset"); dbg_hcc_params (ehci, "reset"); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 4ff7f868b157..86872c8877cb 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -81,7 +81,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd) } -/* caller owns root->serialize, and should reset/reinit on error */ +/* caller has locked the root hub, and should reset/reinit on error */ static int ehci_hub_resume (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); diff --git a/drivers/usb/host/hc_sl811.c b/drivers/usb/host/hc_sl811.c index b57f1fe8258d..baf0e8086352 100644 --- a/drivers/usb/host/hc_sl811.c +++ b/drivers/usb/host/hc_sl811.c @@ -1343,15 +1343,11 @@ static int __init hci_hcd_init (void) *****************************************************************/ static void __exit hci_hcd_cleanup (void) { - struct list_head *hci_l; - hci_t *hci; + hci_t *hci, *tmp; DBGFUNC ("Enter hci_hcd_cleanup\n"); - for (hci_l = hci_hcd_list.next; hci_l != &hci_hcd_list;) { - hci = list_entry (hci_l, hci_t, hci_hcd_list); - hci_l = hci_l->next; + list_for_each_entry_safe(hci, tmp, &hci_hcd_list, hci_hcd_list) hc_release_hci (hci); - } } module_init (hci_hcd_init); diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 947bf4a5ea03..ff1f80fd59c8 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -640,14 +640,14 @@ show_registers (struct class_device *class_dev, char *buf) rdata = ohci_readl (®s->fminterval); temp = scnprintf (next, size, "fmintvl 0x%08x %sFSMPS=0x%04x FI=0x%04x\n", - rdata, (rdata >> 31) ? " FIT" : "", + rdata, (rdata >> 31) ? "FIT " : "", (rdata >> 16) & 0xefff, rdata & 0xffff); size -= temp; next += temp; rdata = ohci_readl (®s->fmremaining); temp = scnprintf (next, size, "fmremaining 0x%08x %sFR=0x%04x\n", - rdata, (rdata >> 31) ? " FRT" : "", + rdata, (rdata >> 31) ? "FRT " : "", rdata & 0x3fff); size -= temp; next += temp; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 0609e12efeb2..09f37cce2c75 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -2,7 +2,7 @@ * OHCI HCD (Host Controller Driver) for USB. * * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> - * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> + * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net> * * [ Initialisation is based on Linus' ] * [ uhci code and gregs ohci fragments ] @@ -122,12 +122,27 @@ #define OHCI_INTR_INIT \ (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH) +#ifdef __hppa__ +/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */ +#define IR_DISABLE +#endif + +#ifdef CONFIG_ARCH_OMAP +/* OMAP doesn't support IR (no SMM; not needed) */ +#define IR_DISABLE +#endif + /*-------------------------------------------------------------------------*/ static const char hcd_name [] = "ohci_hcd"; #include "ohci.h" +static void ohci_dump (struct ohci_hcd *ohci, int verbose); +static int ohci_init (struct ohci_hcd *ohci); +static int ohci_restart (struct ohci_hcd *ohci); +static void ohci_stop (struct usb_hcd *hcd); + #include "ohci-hub.c" #include "ohci-dbg.c" #include "ohci-mem.c" @@ -387,30 +402,30 @@ static int ohci_get_frame (struct usb_hcd *hcd) return OHCI_FRAME_NO(ohci->hcca); } +static void ohci_usb_reset (struct ohci_hcd *ohci) +{ + ohci->hc_control = ohci_readl (&ohci->regs->control); + ohci->hc_control &= OHCI_CTRL_RWC; + writel (ohci->hc_control, &ohci->regs->control); +} + /*-------------------------------------------------------------------------* * HC functions *-------------------------------------------------------------------------*/ -/* reset the HC and BUS */ +/* init memory, and kick BIOS/SMM off */ -static int hc_reset (struct ohci_hcd *ohci) +static int ohci_init (struct ohci_hcd *ohci) { u32 temp; + int ret; - /* boot firmware should have set this up (5.1.1.3.1) */ - if (!ohci->fminterval) { - temp = ohci_readl (&ohci->regs->fminterval); - if (temp & 0x3fff0000) - ohci->fminterval = temp; - else - ohci->fminterval = DEFAULT_FMINTERVAL; - /* also: power/overcurrent flags in roothub.a */ - } + disable (ohci); + ohci->regs = ohci->hcd.regs; + ohci->next_statechange = jiffies; - /* SMM owns the HC? not for long! - * On PA-RISC, PDC can leave IR set incorrectly; ignore it there. - */ -#ifndef __hppa__ +#ifndef IR_DISABLE + /* SMM owns the HC? not for long! */ if (ohci_readl (&ohci->regs->control) & OHCI_CTRL_IR) { ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n"); @@ -426,27 +441,95 @@ static int hc_reset (struct ohci_hcd *ohci) msleep (10); if (--temp == 0) { ohci_err (ohci, "USB HC TakeOver failed!\n"); - return -1; + return -EBUSY; } } + ohci_usb_reset (ohci); } #endif /* Disable HC interrupts */ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); + // flush the writes + (void) ohci_readl (&ohci->regs->control); + + if (ohci->hcca) + return 0; + + ohci->hcca = dma_alloc_coherent (ohci->hcd.self.controller, + sizeof *ohci->hcca, &ohci->hcca_dma, 0); + if (!ohci->hcca) + return -ENOMEM; + + if ((ret = ohci_mem_init (ohci)) < 0) + ohci_stop (&ohci->hcd); - ohci_dbg (ohci, "reset, control = 0x%x\n", - ohci_readl (&ohci->regs->control)); + return ret; - /* Reset USB (needed by some controllers); RemoteWakeupConnected +} + +/*-------------------------------------------------------------------------*/ + +/* 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); + + /* boot firmware should have set this up (5.1.1.3.1) */ + if (first) { + + temp = ohci_readl (&ohci->regs->fminterval); + ohci->fminterval = temp & 0x3fff; + if (ohci->fminterval != FI) + ohci_dbg (ohci, "fminterval delta %d\n", + ohci->fminterval - FI); + ohci->fminterval |= FSMP (ohci->fminterval) << 16; + /* also: power/overcurrent flags in roothub.a */ + } + + /* Reset USB nearly "by the book". RemoteWakeupConnected * saved if boot firmware (BIOS/SMM/...) told us it's connected * (for OHCI integrated on mainboard, it normally is) */ ohci->hc_control = ohci_readl (&ohci->regs->control); - ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */ - if (ohci->hc_control) + ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n", + hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), + ohci->hc_control); + + if (ohci->hc_control & OHCI_CTRL_RWC + && !(ohci->flags & OHCI_QUIRK_AMD756)) ohci->hcd.can_wakeup = 1; + + switch (ohci->hc_control & OHCI_CTRL_HCFS) { + case OHCI_USB_OPER: + temp = 0; + break; + case OHCI_USB_SUSPEND: + case OHCI_USB_RESUME: + ohci->hc_control &= OHCI_CTRL_RWC; + ohci->hc_control |= OHCI_USB_RESUME; + temp = 10 /* msec wait */; + break; + // case OHCI_USB_RESET: + default: + ohci->hc_control &= OHCI_CTRL_RWC; + ohci->hc_control |= OHCI_USB_RESET; + temp = 50 /* msec wait */; + break; + } writel (ohci->hc_control, &ohci->regs->control); + // flush the writes + (void) ohci_readl (&ohci->regs->control); + msleep(temp); if (power_switching) { unsigned ports = roothub_a (ohci) & RH_A_NDP; @@ -455,15 +538,20 @@ static int hc_reset (struct ohci_hcd *ohci) writel (RH_PS_LSDA, &ohci->regs->roothub.portstatus [temp]); } - // flush those pci writes + // flush those writes (void) ohci_readl (&ohci->regs->control); - msleep (50); + memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); + + /* 2msec timelimit here means no irqs/preempt */ + spin_lock_irq (&ohci->lock); +retry: /* HC Reset requires max 10 us delay */ writel (OHCI_HCR, &ohci->regs->cmdstatus); temp = 30; /* ... allow extra time */ while ((ohci_readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { if (--temp == 0) { + spin_unlock_irq (&ohci->lock); ohci_err (ohci, "USB HC reset timed out!\n"); return -1; } @@ -476,27 +564,15 @@ static int hc_reset (struct ohci_hcd *ohci) * ... but some hardware won't init fmInterval "by the book" * (SiS, OPTi ...), so reset again instead. SiS doesn't need * this if we write fmInterval after we're OPERATIONAL. + * Unclear about ALi, ServerWorks, and others ... this could + * easily be a longstanding bug in chip init on Linux. */ - writel (ohci->hc_control, &ohci->regs->control); - // flush those pci writes - (void) ohci_readl (&ohci->regs->control); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* Start an OHCI controller, set the BUS operational - * enable interrupts - * connect the virtual root hub - */ -static int hc_start (struct ohci_hcd *ohci) -{ - u32 mask, tmp; - struct usb_device *udev; - struct usb_bus *bus; - - disable (ohci); + if (ohci->flags & OHCI_QUIRK_INITRESET) { + writel (ohci->hc_control, &ohci->regs->control); + // flush those writes + (void) ohci_readl (&ohci->regs->control); + } + writel (ohci->fminterval, &ohci->regs->fminterval); /* Tell the controller where the control and bulk lists are * The lists are empty now. */ @@ -513,7 +589,15 @@ static int hc_start (struct ohci_hcd *ohci) */ if ((ohci_readl (&ohci->regs->fminterval) & 0x3fff0000) == 0 || !ohci_readl (&ohci->regs->periodicstart)) { - ohci_err (ohci, "init err\n"); + if (!(ohci->flags & OHCI_QUIRK_INITRESET)) { + ohci->flags |= OHCI_QUIRK_INITRESET; + ohci_dbg (ohci, "enabling initreset quirk\n"); + goto retry; + } + spin_unlock_irq (&ohci->lock); + ohci_err (ohci, "init err (%08x %04x)\n", + ohci_readl (&ohci->regs->fminterval), + ohci_readl (&ohci->regs->periodicstart)); return -EOVERFLOW; } @@ -532,42 +616,48 @@ static int hc_start (struct ohci_hcd *ohci) writel (mask, &ohci->regs->intrenable); /* handle root hub init quirks ... */ - tmp = roothub_a (ohci); - tmp &= ~(RH_A_PSM | RH_A_OCPM); + temp = roothub_a (ohci); + temp &= ~(RH_A_PSM | RH_A_OCPM); if (ohci->flags & OHCI_QUIRK_SUPERIO) { /* NSC 87560 and maybe others */ - tmp |= RH_A_NOCP; - tmp &= ~(RH_A_POTPGT | RH_A_NPS); + temp |= RH_A_NOCP; + temp &= ~(RH_A_POTPGT | RH_A_NPS); } else if (power_switching) { /* act like most external hubs: use per-port power * switching and overcurrent reporting. */ - tmp &= ~(RH_A_NPS | RH_A_NOCP); - tmp |= RH_A_PSM | RH_A_OCPM; + temp &= ~(RH_A_NPS | RH_A_NOCP); + temp |= RH_A_PSM | RH_A_OCPM; } else { /* hub power always on; required for AMD-756 and some * Mac platforms. ganged overcurrent reporting, if any. */ - tmp |= RH_A_NPS; + temp |= RH_A_NPS; } - writel (tmp, &ohci->regs->roothub.a); + writel (temp, &ohci->regs->roothub.a); writel (RH_HS_LPSC, &ohci->regs->roothub.status); writel (power_switching ? RH_B_PPCM : 0, &ohci->regs->roothub.b); - // flush those pci writes + // flush those writes (void) ohci_readl (&ohci->regs->control); + spin_unlock_irq (&ohci->lock); + // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((roothub_a (ohci) >> 23) & 0x1fe); bus = hcd_to_bus (&ohci->hcd); + ohci->hcd.state = USB_STATE_RUNNING; + + ohci_dump (ohci, 1); - if (bus->root_hub) { - ohci->hcd.state = USB_STATE_RUNNING; + udev = hcd_to_bus (&ohci->hcd)->root_hub; + if (udev) { + udev->dev.power.power_state = 0; + usb_set_device_state (udev, USB_STATE_CONFIGURED); return 0; } /* connect the virtual root hub */ udev = usb_alloc_dev (NULL, bus, 0); - ohci->hcd.state = USB_STATE_RUNNING; if (!udev) { disable (ohci); ohci->hc_control &= ~OHCI_CTRL_HCFS; @@ -583,7 +673,10 @@ static int hc_start (struct ohci_hcd *ohci) writel (ohci->hc_control, &ohci->regs->control); return -ENODEV; } + if (ohci->power_budget) + hub_set_power_budget(udev, ohci->power_budget); + create_debug_files (ohci); return 0; } @@ -620,7 +713,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) // e.g. due to PCI Master/Target Abort ohci_dump (ohci, 1); - hc_reset (ohci); + ohci_usb_reset (ohci); } if (ints & OHCI_INTR_RD) { @@ -655,7 +748,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) if (HCD_IS_RUNNING(ohci->hcd.state)) { writel (ints, ®s->intrstatus); writel (OHCI_INTR_MIE, ®s->intrenable); - // flush those pci writes + // flush those writes (void) ohci_readl (&ohci->regs->control); } @@ -674,10 +767,9 @@ static void ohci_stop (struct usb_hcd *hcd) ohci_dump (ohci, 1); flush_scheduled_work(); - if (HCD_IS_RUNNING(ohci->hcd.state)) - hc_reset (ohci); - else - writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); + + ohci_usb_reset (ohci); + writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); remove_debug_files (ohci); ohci_mem_cleanup (ohci); @@ -696,19 +788,7 @@ static void ohci_stop (struct usb_hcd *hcd) #if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM) -static void mark_children_gone (struct usb_device *dev) -{ - unsigned i; - - for (i = 0; i < dev->maxchild; i++) { - if (dev->children [i] == 0) - continue; - dev->children [i]->state = USB_STATE_NOTATTACHED; - mark_children_gone (dev->children [i]); - } -} - -static int hc_restart (struct ohci_hcd *ohci) +static int ohci_restart (struct ohci_hcd *ohci) { int temp; int i; @@ -721,7 +801,7 @@ static int hc_restart (struct ohci_hcd *ohci) */ spin_lock_irq(&ohci->lock); disable (ohci); - mark_children_gone (ohci->hcd.self.root_hub); + usb_set_device_state (ohci->hcd.self.root_hub, USB_STATE_NOTATTACHED); if (!list_empty (&ohci->pending)) ohci_dbg(ohci, "abort schedule...\n"); list_for_each_entry (priv, &ohci->pending, pending) { @@ -765,7 +845,7 @@ static int hc_restart (struct ohci_hcd *ohci) ohci->ed_controltail = NULL; ohci->ed_bulktail = NULL; - if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { + if ((temp = ohci_run (ohci)) < 0) { ohci_err (ohci, "can't restart, %d\n", temp); return temp; } else { @@ -777,10 +857,7 @@ static int hc_restart (struct ohci_hcd *ohci) while (i--) writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [temp]); - ohci->hcd.self.root_hub->dev.power.power_state = 0; - ohci->hcd.state = USB_STATE_RUNNING; ohci_dbg (ohci, "restart complete\n"); - ohci_dump (ohci, 1); } return 0; } @@ -810,10 +887,25 @@ MODULE_LICENSE ("GPL"); #include "ohci-lh7a404.c" #endif +#ifdef CONFIG_PXA27x +#include "ohci-pxa27x.c" +#endif + #if !(defined(CONFIG_PCI) \ || defined(CONFIG_SA1111) \ || defined(CONFIG_ARCH_OMAP) \ || defined (CONFIG_ARCH_LH7A404) \ + || defined (CONFIG_PXA27x) \ ) #error "missing bus glue for ohci-hcd" #endif + +#if !defined(HAVE_HNP) && defined(CONFIG_USB_OTG) + +#warning non-OTG configuration, too many HCDs + +static void start_hnp(struct ohci_hcd *ohci) +{ + /* "can't happen" */ +} +#endif diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 424971c4bc27..84b133304edf 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -2,7 +2,7 @@ * OHCI HCD (Host Controller Driver) for USB. * * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> - * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> + * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net> * * This file is licenced under GPL */ @@ -11,34 +11,8 @@ /* * OHCI Root Hub ... the nonsharable stuff - * - * Registers don't need cpu_to_le32, that happens transparently */ -/* AMD-756 (D2 rev) reports corrupt register contents in some cases. - * The erratum (#4) description is incorrect. AMD's workaround waits - * till some bits (mostly reserved) are clear; ok for all revs. - */ -#define read_roothub(hc, register, mask) ({ \ - u32 temp = ohci_readl (&hc->regs->roothub.register); \ - if (temp == -1) \ - disable (hc); \ - else if (hc->flags & OHCI_QUIRK_AMD756) \ - while (temp & mask) \ - temp = ohci_readl (&hc->regs->roothub.register); \ - temp; }) - -static u32 roothub_a (struct ohci_hcd *hc) - { return read_roothub (hc, a, 0xfc0fe000); } -static inline u32 roothub_b (struct ohci_hcd *hc) - { return ohci_readl (&hc->regs->roothub.b); } -static inline u32 roothub_status (struct ohci_hcd *hc) - { return ohci_readl (&hc->regs->roothub.status); } -static u32 roothub_portstatus (struct ohci_hcd *hc, int i) - { return read_roothub (hc, portstatus [i], 0xffe0fce0); } - -/*-------------------------------------------------------------------------*/ - #define dbg_port(hc,label,num,value) \ ohci_dbg (hc, \ "%s roothub.portstatus [%d] " \ @@ -146,10 +120,11 @@ static int ohci_hub_suspend (struct usb_hcd *hcd) ohci->next_statechange = jiffies + msecs_to_jiffies (5); succeed: - /* it's not USB_STATE_SUSPENDED unless access to this + /* it's not HCD_STATE_SUSPENDED unless access to this * hub from the non-usb side (PCI, SOC, etc) stopped */ root->dev.power.power_state = 3; + usb_set_device_state (root, USB_STATE_SUSPENDED); done: spin_unlock_irq (&ohci->lock); return status; @@ -163,9 +138,7 @@ static inline struct ed *find_head (struct ed *ed) return ed; } -static int hc_restart (struct ohci_hcd *ohci); - -/* caller owns root->serialize */ +/* caller has locked the root hub */ static int ohci_hub_resume (struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); @@ -180,7 +153,12 @@ static int ohci_hub_resume (struct usb_hcd *hcd) spin_lock_irq (&ohci->lock); ohci->hc_control = ohci_readl (&ohci->regs->control); - switch (ohci->hc_control & OHCI_CTRL_HCFS) { + if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) { + /* this can happen after suspend-to-disk */ + ohci_dbg (ohci, "BIOS/SMM active, control %03x\n", + ohci->hc_control); + status = -EBUSY; + } else switch (ohci->hc_control & OHCI_CTRL_HCFS) { case OHCI_USB_SUSPEND: ohci->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES); ohci->hc_control |= OHCI_USB_RESUME; @@ -202,8 +180,10 @@ static int ohci_hub_resume (struct usb_hcd *hcd) status = -EBUSY; } spin_unlock_irq (&ohci->lock); - if (status == -EBUSY) - return hc_restart (ohci); + if (status == -EBUSY) { + (void) ohci_init (ohci); + return ohci_restart (ohci); + } if (status != -EINPROGRESS) return status; @@ -260,6 +240,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd) /* TRSMRCY */ msleep (10); root->dev.power.power_state = 0; + usb_set_device_state (root, USB_STATE_CONFIGURED); /* keep it alive for ~5x suspend + resume costs */ ohci->next_statechange = jiffies + msecs_to_jiffies (250); @@ -289,7 +270,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd) ohci->hc_control |= enables; writel (ohci->hc_control, &ohci->regs->control); if (temp) - writel (status, &ohci->regs->cmdstatus); + writel (temp, &ohci->regs->cmdstatus); (void) ohci_readl (&ohci->regs->control); } @@ -301,9 +282,9 @@ static void ohci_rh_resume (void *_hcd) { struct usb_hcd *hcd = _hcd; - down (&hcd->self.root_hub->serialize); + usb_lock_device (hcd->self.root_hub); (void) ohci_hub_resume (hcd); - up (&hcd->self.root_hub->serialize); + usb_unlock_device (hcd->self.root_hub); } #else @@ -381,12 +362,12 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES) & ohci->hc_control) == OHCI_USB_OPER - && down_trylock (&hcd->self.root_hub->serialize) == 0 + && usb_trylock_device (hcd->self.root_hub) ) { ohci_vdbg (ohci, "autosuspend\n"); (void) ohci_hub_suspend (&ohci->hcd); ohci->hcd.state = USB_STATE_RUNNING; - up (&hcd->self.root_hub->serialize); + usb_unlock_device (hcd->self.root_hub); } #endif @@ -481,8 +462,8 @@ static void start_hnp(struct ohci_hcd *ohci); /* this timer value might be vendor-specific ... */ #define PORT_RESET_HW_MSEC 10 -/* wrap-aware logic stolen from <linux/jiffies.h> */ -#define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0) +/* wrap-aware logic morphed from <linux/jiffies.h> */ +#define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0) /* called from some task, normally khubd */ static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port) diff --git a/drivers/usb/host/ohci-lh7a404.c b/drivers/usb/host/ohci-lh7a404.c index 4e11a8eae481..1594d1e635da 100644 --- a/drivers/usb/host/ohci-lh7a404.c +++ b/drivers/usb/host/ohci-lh7a404.c @@ -229,38 +229,14 @@ ohci_lh7a404_start (struct usb_hcd *hcd) int ret; ohci_dbg (ohci, "ohci_lh7a404_start, ohci:%p", ohci); - - ohci->hcca = dma_alloc_coherent (hcd->self.controller, - sizeof *ohci->hcca, &ohci->hcca_dma, 0); - if (!ohci->hcca) - return -ENOMEM; - - ohci_dbg (ohci, "ohci_lh7a404_start, ohci->hcca:%p", - ohci->hcca); - - memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); - - if ((ret = ohci_mem_init (ohci)) < 0) { - ohci_stop (hcd); + if ((ret = ohci_init(ohci)) < 0) return ret; - } - ohci->regs = hcd->regs; - - if (hc_reset (ohci) < 0) { - ohci_stop (hcd); - return -ENODEV; - } - if (hc_start (ohci) < 0) { + if ((ret = ohci_run (ohci)) < 0) { err ("can't start %s", ohci->hcd.self.bus_name); ohci_stop (hcd); - return -EBUSY; + return ret; } - create_debug_files (ohci); - -#ifdef DEBUG - ohci_dump (ohci, 1); -#endif /*DEBUG*/ return 0; } diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index d133ff22a4a7..a8e641f595bb 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -428,40 +428,18 @@ ohci_omap_start (struct usb_hcd *hcd) struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ret; - config = hcd->self.controller->platform_data; - ohci->hcca = dma_alloc_coherent (hcd->self.controller, - sizeof *ohci->hcca, &ohci->hcca_dma, 0); - if (!ohci->hcca) - return -ENOMEM; - - memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); - if ((ret = ohci_mem_init (ohci)) < 0) { - ohci_stop (hcd); + if ((ret = ohci_init(ohci)) < 0) return ret; - } - ohci->regs = hcd->regs; + config = hcd->self.controller->platform_data; if (config->otg || config->rwc) writel(OHCI_CTRL_RWC, &ohci->regs->control); - if (hc_reset (ohci) < 0) { - ohci_stop (hcd); - return -ENODEV; - } - - if (hc_start (ohci) < 0) { + if ((ret = ohci_run (ohci)) < 0) { err ("can't start %s", ohci->hcd.self.bus_name); ohci_stop (hcd); - return -EBUSY; + return ret; } - if (ohci->power_budget) - hub_set_power_budget(ohci->hcd.self.root_hub, - ohci->power_budget); - create_debug_files (ohci); - -#ifdef DEBUG - ohci_dump (ohci, 1); -#endif return 0; } diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 73d9aee657d5..2211a69e0b9d 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -35,9 +35,7 @@ ohci_pci_reset (struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - ohci->regs = hcd->regs; - ohci->next_statechange = jiffies; - return hc_reset (ohci); + return ohci_init (ohci); } static int __devinit @@ -46,11 +44,6 @@ ohci_pci_start (struct usb_hcd *hcd) struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ret; - ohci->hcca = dma_alloc_coherent (hcd->self.controller, - sizeof *ohci->hcca, &ohci->hcca_dma, 0); - if (!ohci->hcca) - return -ENOMEM; - if(hcd->self.controller && hcd->self.controller->bus == &pci_bus_type) { struct pci_dev *pdev = to_pci_dev(hcd->self.controller); @@ -61,6 +54,7 @@ ohci_pci_start (struct usb_hcd *hcd) && pdev->device == 0x740c) { ohci->flags = OHCI_QUIRK_AMD756; ohci_info (ohci, "AMD756 erratum 4 workaround\n"); + // also somewhat erratum 10 (suspend/resume issues) } /* FIXME for some of the early AMD 760 southbridges, OHCI @@ -92,25 +86,16 @@ ohci_pci_start (struct usb_hcd *hcd) ohci_info (ohci, "Using NSC SuperIO setup\n"); } } - - } - - memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); - if ((ret = ohci_mem_init (ohci)) < 0) { - ohci_stop (hcd); - return ret; } - if (hc_start (ohci) < 0) { + /* NOTE: there may have already been a first reset, to + * keep bios/smm irqs from making trouble + */ + if ((ret = ohci_run (ohci)) < 0) { ohci_err (ohci, "can't start\n"); ohci_stop (hcd); - return -EBUSY; + return ret; } - create_debug_files (ohci); - -#ifdef DEBUG - ohci_dump (ohci, 1); -#endif return 0; } @@ -127,9 +112,9 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state) #ifdef CONFIG_USB_SUSPEND (void) usb_suspend_device (hcd->self.root_hub, state); #else - down (&hcd->self.root_hub->serialize); + usb_lock_device (hcd->self.root_hub); (void) ohci_hub_suspend (hcd); - up (&hcd->self.root_hub->serialize); + usb_unlock_device (hcd->self.root_hub); #endif /* let things settle down a bit */ @@ -175,9 +160,9 @@ static int ohci_pci_resume (struct usb_hcd *hcd) /* get extra cleanup even if remote wakeup isn't in use */ retval = usb_resume_device (hcd->self.root_hub); #else - down (&hcd->self.root_hub->serialize); + usb_lock_device (hcd->self.root_hub); retval = ohci_hub_resume (hcd); - up (&hcd->self.root_hub->serialize); + usb_unlock_device (hcd->self.root_hub); #endif if (retval == 0) { diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c new file mode 100644 index 000000000000..4bfe74123373 --- /dev/null +++ b/drivers/usb/host/ohci-pxa27x.c @@ -0,0 +1,460 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> + * (C) Copyright 2002 Hewlett-Packard Company + * + * Bus Glue for pxa27x + * + * Written by Christopher Hoover <ch@hpl.hp.com> + * Based on fragments of previous driver by Russell King et al. + * + * Modified for LH7A404 from ohci-sa1111.c + * by Durgesh Pattamatta <pattamattad@sharpsec.com> + * + * Modified for pxa27x from ohci-lh7a404.c + * by Nick Bane <nick@cecomputing.co.uk> 26-8-2004 + * + * This file is licenced under the GPL. + */ + +#include <linux/device.h> +#include <asm/mach-types.h> +#include <asm/hardware.h> + + +#define PMM_NPS_MODE 1 +#define PMM_GLOBAL_MODE 2 +#define PMM_PERPORT_MODE 3 + +#define PXA_UHC_MAX_PORTNUM 3 + +#define UHCRHPS(x) __REG2( 0x4C000050, (x)<<2 ) + +static int pxa27x_ohci_pmm_state; + +/* + PMM_NPS_MODE -- PMM Non-power switching mode + Ports are powered continuously. + + PMM_GLOBAL_MODE -- PMM global switching mode + All ports are powered at the same time. + + PMM_PERPORT_MODE -- PMM per port switching mode + Ports are powered individually. + */ +static int pxa27x_ohci_select_pmm( int mode ) +{ + pxa27x_ohci_pmm_state = mode; + + switch ( mode ) { + case PMM_NPS_MODE: + UHCRHDA |= RH_A_NPS; + break; + case PMM_GLOBAL_MODE: + UHCRHDA &= ~(RH_A_NPS & RH_A_PSM); + break; + case PMM_PERPORT_MODE: + UHCRHDA &= ~(RH_A_NPS); + UHCRHDA |= RH_A_PSM; + + /* Set port power control mask bits, only 3 ports. */ + UHCRHDB |= (0x7<<17); + break; + default: + printk( KERN_ERR + "Invalid mode %d, set to non-power switch mode.\n", + mode ); + + pxa27x_ohci_pmm_state = PMM_NPS_MODE; + UHCRHDA |= RH_A_NPS; + } + + return 0; +} + +/* + If you select PMM_PERPORT_MODE, you should set the port power + */ +static int pxa27x_ohci_set_port_power( int port ) +{ + if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE) + && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) { + UHCRHPS(port) |= 0x100; + return 0; + } + return -1; +} + +/* + If you select PMM_PERPORT_MODE, you should set the port power + */ +static int pxa27x_ohci_clear_port_power( int port ) +{ + if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE) + && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) { + UHCRHPS(port) |= 0x200; + return 0; + } + + return -1; +} + +extern int usb_disabled(void); + +/*-------------------------------------------------------------------------*/ + +static void pxa27x_start_hc(struct platform_device *dev) +{ + pxa_set_cken(CKEN10_USBHOST, 1); + + UHCHR |= UHCHR_FHR; + udelay(11); + UHCHR &= ~UHCHR_FHR; + + UHCHR |= UHCHR_FSBIR; + while (UHCHR & UHCHR_FSBIR) + cpu_relax(); + + /* This could be properly abstracted away through the + device data the day more machines are supported and + their differences can be figured out correctly. */ + if (machine_is_mainstone()) { + /* setup Port1 GPIO pin. */ + pxa_gpio_mode( 88 | GPIO_ALT_FN_1_IN); /* USBHPWR1 */ + pxa_gpio_mode( 89 | GPIO_ALT_FN_2_OUT); /* USBHPEN1 */ + + /* Set the Power Control Polarity Low and Power Sense + Polarity Low to active low. Supply power to USB ports. */ + UHCHR = (UHCHR | UHCHR_PCPL | UHCHR_PSPL) & + ~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSEP3 | UHCHR_SSE); + } + + UHCHR &= ~UHCHR_SSE; + + UHCHIE = (UHCHIE_UPRIE | UHCHIE_RWIE); +} + +static void pxa27x_stop_hc(struct platform_device *dev) +{ + UHCHR |= UHCHR_FHR; + udelay(11); + UHCHR &= ~UHCHR_FHR; + + UHCCOMS |= 1; + udelay(10); + + pxa_set_cken(CKEN10_USBHOST, 0); +} + + +/*-------------------------------------------------------------------------*/ + +void usb_hcd_pxa27x_remove (struct usb_hcd *, struct platform_device *); + +/* configure so an HC device and id are always provided */ +/* always called with process context; sleeping is OK */ + + +/** + * usb_hcd_pxa27x_probe - initialize pxa27x-based HCDs + * Context: !in_interrupt() + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + * + */ +int usb_hcd_pxa27x_probe (const struct hc_driver *driver, + struct usb_hcd **hcd_out, + struct platform_device *dev) +{ + int retval; + struct usb_hcd *hcd = 0; + + unsigned int *addr = NULL; + + if (!request_mem_region(dev->resource[0].start, + dev->resource[0].end + - dev->resource[0].start + 1, hcd_name)) { + pr_debug("request_mem_region failed"); + return -EBUSY; + } + + pxa27x_start_hc(dev); + + /* Select Power Management Mode */ + pxa27x_ohci_select_pmm( PMM_PERPORT_MODE ); + + /* If choosing PMM_PERPORT_MODE, we should set the port power before we use it. */ + if (pxa27x_ohci_set_port_power(1) < 0) + printk(KERN_ERR "Setting port 1 power failed.\n"); + + if (pxa27x_ohci_clear_port_power(2) < 0) + printk(KERN_ERR "Setting port 2 power failed.\n"); + + if (pxa27x_ohci_clear_port_power(3) < 0) + printk(KERN_ERR "Setting port 3 power failed.\n"); + + addr = ioremap(dev->resource[0].start, + dev->resource[0].end - dev->resource[0].start + 1); + if (!addr) { + pr_debug("ioremap failed"); + retval = -ENOMEM; + goto err1; + } + + hcd = driver->hcd_alloc (); + if (hcd == NULL){ + pr_debug ("hcd_alloc failed"); + retval = -ENOMEM; + goto err1; + } + + if(dev->resource[1].flags != IORESOURCE_IRQ){ + pr_debug ("resource[1] is not IORESOURCE_IRQ"); + retval = -ENOMEM; + goto err1; + } + + hcd->driver = (struct hc_driver *) driver; + hcd->description = driver->description; + hcd->irq = dev->resource[1].start; + hcd->regs = addr; + hcd->self.controller = &dev->dev; + + retval = hcd_buffer_create (hcd); + if (retval != 0) { + pr_debug ("pool alloc fail"); + goto err1; + } + + retval = request_irq (hcd->irq, usb_hcd_irq, SA_INTERRUPT, + hcd->description, hcd); + if (retval != 0) { + pr_debug("request_irq(%d) failed with retval %d\n",hcd->irq,retval); + retval = -EBUSY; + goto err2; + } + + pr_debug ("%s (pxa27x) at 0x%p, irq %d", + hcd->description, hcd->regs, hcd->irq); + + usb_bus_init (&hcd->self); + hcd->self.op = &usb_hcd_operations; + hcd->self.hcpriv = (void *) hcd; + hcd->self.bus_name = "pxa27x"; + hcd->product_desc = "PXA27x OHCI"; + + INIT_LIST_HEAD (&hcd->dev_list); + + usb_register_bus (&hcd->self); + + if ((retval = driver->start (hcd)) < 0) { + usb_hcd_pxa27x_remove(hcd, dev); + return retval; + } + + *hcd_out = hcd; + return 0; + + err2: + hcd_buffer_destroy (hcd); + if (hcd) + driver->hcd_free(hcd); + err1: + pxa27x_stop_hc(dev); + release_mem_region(dev->resource[0].start, + dev->resource[0].end + - dev->resource[0].start + 1); + return retval; +} + + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * usb_hcd_pxa27x_remove - shutdown processing for pxa27x-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_hcd_pxa27x_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + * + */ +void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *dev) +{ + void *base; + + pr_debug ("remove: %s, state %x", hcd->self.bus_name, hcd->state); + + if (in_interrupt ()) + BUG (); + + hcd->state = USB_STATE_QUIESCING; + + pr_debug ("%s: roothub graceful disconnect", hcd->self.bus_name); + usb_disconnect (&hcd->self.root_hub); + + hcd->driver->stop (hcd); + hcd->state = USB_STATE_HALT; + + free_irq (hcd->irq, hcd); + hcd_buffer_destroy (hcd); + + usb_deregister_bus (&hcd->self); + + base = hcd->regs; + hcd->driver->hcd_free (hcd); + + pxa27x_stop_hc(dev); + release_mem_region(dev->resource[0].start, + dev->resource[0].end - dev->resource[0].start + 1); +} + +/*-------------------------------------------------------------------------*/ + +static int __devinit +ohci_pxa27x_start (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int ret; + + ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci); + + if ((ret = ohci_init(ohci)) < 0) + return ret; + + if ((ret = ohci_run (ohci)) < 0) { + err ("can't start %s", ohci->hcd.self.bus_name); + ohci_stop (hcd); + return ret; + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ohci_pxa27x_hc_driver = { + .description = hcd_name, + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11, + + /* + * basic lifecycle operations + */ + .start = ohci_pxa27x_start, + .stop = ohci_stop, + + /* + * memory lifecycle (except per-request) + */ + .hcd_alloc = ohci_hcd_alloc, + .hcd_free = ohci_hcd_free, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ohci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_USB_SUSPEND + .hub_suspend = ohci_hub_suspend, + .hub_resume = ohci_hub_resume, +#endif +}; + +/*-------------------------------------------------------------------------*/ + +static int ohci_hcd_pxa27x_drv_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = NULL; + int ret; + + pr_debug ("In ohci_hcd_pxa27x_drv_probe"); + + if (usb_disabled()) + return -ENODEV; + + ret = usb_hcd_pxa27x_probe(&ohci_pxa27x_hc_driver, &hcd, pdev); + + if (ret == 0) + dev_set_drvdata(dev, hcd); + + return ret; +} + +static int ohci_hcd_pxa27x_drv_remove(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + + usb_hcd_pxa27x_remove(hcd, pdev); + dev_set_drvdata(dev, NULL); + return 0; +} + +static int ohci_hcd_pxa27x_drv_suspend(struct device *dev, u32 state, u32 level) +{ +// struct platform_device *pdev = to_platform_device(dev); +// struct usb_hcd *hcd = dev_get_drvdata(dev); + printk("%s: not implemented yet\n", __FUNCTION__); + + return 0; +} + +static int ohci_hcd_pxa27x_drv_resume(struct device *dev, u32 state) +{ +// struct platform_device *pdev = to_platform_device(dev); +// struct usb_hcd *hcd = dev_get_drvdata(dev); + printk("%s: not implemented yet\n", __FUNCTION__); + + return 0; +} + + +static struct device_driver ohci_hcd_pxa27x_driver = { + .name = "pxa27x-ohci", + .bus = &platform_bus_type, + .probe = ohci_hcd_pxa27x_drv_probe, + .remove = ohci_hcd_pxa27x_drv_remove, + .suspend = ohci_hcd_pxa27x_drv_suspend, + .resume = ohci_hcd_pxa27x_drv_resume, +}; + +static int __init ohci_hcd_pxa27x_init (void) +{ + pr_debug (DRIVER_INFO " (pxa27x)"); + pr_debug ("block sizes: ed %d td %d\n", + sizeof (struct ed), sizeof (struct td)); + + return driver_register(&ohci_hcd_pxa27x_driver); +} + +static void __exit ohci_hcd_pxa27x_cleanup (void) +{ + driver_unregister(&ohci_hcd_pxa27x_driver); +} + +module_init (ohci_hcd_pxa27x_init); +module_exit (ohci_hcd_pxa27x_cleanup); diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index c3cd76aba163..35d71aceff63 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -272,33 +272,14 @@ ohci_sa1111_start (struct usb_hcd *hcd) struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ret; - ohci->hcca = dma_alloc_coherent (hcd->self.controller, - sizeof *ohci->hcca, &ohci->hcca_dma, 0); - if (!ohci->hcca) - return -ENOMEM; - - memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); - if ((ret = ohci_mem_init (ohci)) < 0) { - ohci_stop (hcd); + if ((ret = ohci_init(ohci)) < 0) return ret; - } - ohci->regs = hcd->regs; - - if (hc_reset (ohci) < 0) { - ohci_stop (hcd); - return -ENODEV; - } - if (hc_start (ohci) < 0) { + if ((ret = ohci_run (ohci)) < 0) { err ("can't start %s", ohci->hcd.self.bus_name); ohci_stop (hcd); - return -EBUSY; + return ret; } - create_debug_files (ohci); - -#ifdef DEBUG - ohci_dump (ohci, 1); -#endif return 0; } diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 19436cdd7a11..b2756dee9507 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -42,7 +42,6 @@ struct ed { /* create --> IDLE --> OPER --> ... --> IDLE --> destroy * usually: OPER --> UNLINK --> (IDLE | OPER) --> ... - * some special cases : OPER --> IDLE ... */ u8 state; /* ED_{IDLE,UNLINK,OPER} */ #define ED_IDLE 0x00 /* NOT linked to HC */ @@ -387,6 +386,7 @@ struct ohci_hcd { unsigned long flags; /* for HC bugs */ #define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ #define OHCI_QUIRK_SUPERIO 0x02 /* natsemi */ +#define OHCI_QUIRK_INITRESET 0x04 /* SiS, OPTi, ... */ // there are also chip quirks/bugs in init logic /* @@ -405,14 +405,14 @@ static inline void disable (struct ohci_hcd *ohci) } #define FI 0x2edf /* 12000 bits per frame (-1) */ -#define DEFAULT_FMINTERVAL ((((6 * (FI - 210)) / 7) << 16) | FI) +#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7)) #define LSTHRESH 0x628 /* lowspeed bit threshold */ static inline void periodic_reinit (struct ohci_hcd *ohci) { - writel (ohci->fminterval, &ohci->regs->fminterval); - writel (((9 * FI) / 10) & 0x3fff, &ohci->regs->periodicstart); - writel (LSTHRESH, &ohci->regs->lsthresh); + u32 fi = ohci->fminterval & 0x0ffff; + + writel (((9 * fi) / 10) & 0x3fff, &ohci->regs->periodicstart); } /*-------------------------------------------------------------------------*/ @@ -436,6 +436,8 @@ static inline void periodic_reinit (struct ohci_hcd *ohci) # define ohci_vdbg(ohci, fmt, args...) do { } while (0) #endif +/*-------------------------------------------------------------------------*/ + #ifdef CONFIG_ARCH_LH7A404 /* Marc Singer: at the time this code was written, the LH7A404 * had a problem reading the USB host registers. This @@ -455,3 +457,25 @@ static inline unsigned int ohci_readl (void __iomem * regs) return readl (regs); } #endif + +/* AMD-756 (D2 rev) reports corrupt register contents in some cases. + * The erratum (#4) description is incorrect. AMD's workaround waits + * till some bits (mostly reserved) are clear; ok for all revs. + */ +#define read_roothub(hc, register, mask) ({ \ + u32 temp = ohci_readl (&hc->regs->roothub.register); \ + if (temp == -1) \ + disable (hc); \ + else if (hc->flags & OHCI_QUIRK_AMD756) \ + while (temp & mask) \ + temp = ohci_readl (&hc->regs->roothub.register); \ + temp; }) + +static u32 roothub_a (struct ohci_hcd *hc) + { return read_roothub (hc, a, 0xfc0fe000); } +static inline u32 roothub_b (struct ohci_hcd *hc) + { return ohci_readl (&hc->regs->roothub.b); } +static inline u32 roothub_status (struct ohci_hcd *hc) + { return ohci_readl (&hc->regs->roothub.status); } +static u32 roothub_portstatus (struct ohci_hcd *hc, int i) + { return read_roothub (hc, portstatus [i], 0xffe0fce0); } diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 20a42f914684..99b706b64052 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -230,42 +230,22 @@ static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td) } /* - * Inserts a td into qh list at the top. + * Inserts a td list into qh. */ static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, __le32 breadth) { - struct list_head *tmp, *head; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct uhci_td *td, *ptd; - - if (list_empty(&urbp->td_list)) - return; - - head = &urbp->td_list; - tmp = head->next; + struct uhci_td *td; + u32 *plink; /* Ordering isn't important here yet since the QH hasn't been */ - /* inserted into the schedule yet */ - td = list_entry(tmp, struct uhci_td, list); - - /* Add the first TD to the QH element pointer */ - qh->element = cpu_to_le32(td->dma_handle) | breadth; - - ptd = td; - - /* Then link the rest of the TD's */ - tmp = tmp->next; - while (tmp != head) { - td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - - ptd->link = cpu_to_le32(td->dma_handle) | breadth; - - ptd = td; + /* inserted into the schedule yet */ + plink = &qh->element; + list_for_each_entry(td, &urbp->td_list, list) { + *plink = cpu_to_le32(td->dma_handle) | breadth; + plink = &td->link; } - - ptd->link = UHCI_PTR_TERM; + *plink = UHCI_PTR_TERM; } static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td) @@ -330,7 +310,7 @@ static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb) { struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct list_head *tmp; + struct urb_priv *turbp; struct uhci_qh *lqh; /* Grab the last QH */ @@ -358,12 +338,8 @@ static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct */ lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; if (lqh->urbp) { - list_for_each (tmp, &lqh->urbp->queue_list) { - struct urb_priv *turbp = - list_entry(tmp, struct urb_priv, queue_list); - + list_for_each_entry(turbp, &lqh->urbp->queue_list, queue_list) turbp->qh->link = lqh->link; - } } list_add_tail(&urbp->qh->list, &skelqh->list); @@ -405,18 +381,11 @@ static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) pqh = list_entry(qh->list.prev, struct uhci_qh, list); pqh->link = newlink; if (pqh->urbp) { - struct list_head *head, *tmp; - - head = &pqh->urbp->queue_list; - tmp = head->next; - while (head != tmp) { - struct urb_priv *turbp = - list_entry(tmp, struct urb_priv, queue_list); - - tmp = tmp->next; + struct urb_priv *turbp; + list_for_each_entry(turbp, &pqh->urbp->queue_list, + queue_list) turbp->qh->link = newlink; - } } wmb(); @@ -447,21 +416,14 @@ static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle) { struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct list_head *head, *tmp; - - head = &urbp->td_list; - tmp = head->next; - while (head != tmp) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; + struct uhci_td *td; + list_for_each_entry(td, &urbp->td_list, list) { if (toggle) td->token |= cpu_to_le32(TD_TOKEN_TOGGLE); else td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE); - toggle ^= 1; } @@ -473,30 +435,19 @@ static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle) static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb) { struct urb_priv *eurbp, *urbp, *furbp, *lurbp; - struct list_head *tmp; struct uhci_td *lltd; eurbp = eurb->hcpriv; urbp = urb->hcpriv; /* Find the first URB in the queue */ + furbp = eurbp; if (eurbp->queued) { - struct list_head *head = &eurbp->queue_list; - - tmp = head->next; - while (tmp != head) { - struct urb_priv *turbp = - list_entry(tmp, struct urb_priv, queue_list); - - if (!turbp->queued) + list_for_each_entry(furbp, &eurbp->queue_list, queue_list) + if (!furbp->queued) break; + } - tmp = tmp->next; - } - } else - tmp = &eurbp->queue_list; - - furbp = list_entry(tmp, struct urb_priv, queue_list); lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list); lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list); @@ -522,9 +473,7 @@ static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, stru static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb) { - struct urb_priv *urbp, *nurbp; - struct list_head *head, *tmp; - struct urb_priv *purbp; + struct urb_priv *urbp, *nurbp, *purbp, *turbp; struct uhci_td *pltd; unsigned int toggle; @@ -556,14 +505,7 @@ static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb) toggle = uhci_toggle(td_token(pltd)) ^ 1; } - head = &urbp->queue_list; - tmp = head->next; - while (head != tmp) { - struct urb_priv *turbp; - - turbp = list_entry(tmp, struct urb_priv, queue_list); - tmp = tmp->next; - + list_for_each_entry(turbp, &urbp->queue_list, queue_list) { if (!turbp->queued) break; toggle = uhci_fixup_toggle(turbp->urb, toggle); @@ -637,7 +579,7 @@ static void uhci_remove_td_from_urb(struct uhci_td *td) static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb) { - struct list_head *head, *tmp; + struct uhci_td *td, *tmp; struct urb_priv *urbp; unsigned int age; @@ -660,13 +602,7 @@ static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb) if (list_empty(&uhci->td_remove_list)) uhci_set_next_interrupt(uhci); - head = &urbp->td_list; - tmp = head->next; - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - + list_for_each_entry_safe(td, tmp, &urbp->td_list, list) { uhci_remove_td_from_urb(td); uhci_remove_td(uhci, td); list_add(&td->remove_list, &uhci->td_remove_list); @@ -1083,7 +1019,6 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb */ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) { - struct list_head *tmp, *head; struct urb_priv *urbp = urb->hcpriv; struct uhci_td *td; unsigned int status = 0; @@ -1091,13 +1026,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) urb->actual_length = 0; - head = &urbp->td_list; - tmp = head->next; - while (tmp != head) { - td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - + list_for_each_entry(td, &urbp->td_list, list) { status = uhci_status_bits(td_status(td)); if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; @@ -1176,17 +1105,12 @@ static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end) { struct urb *last_urb = NULL; - struct list_head *tmp, *head; + struct urb_priv *up; int ret = 0; - head = &uhci->urb_list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list); + list_for_each_entry(up, &uhci->urb_list, urb_list) { struct urb *u = up->urb; - tmp = tmp->next; - /* look for pending URB's with identical pipe handle */ if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS) && (u != urb)) { @@ -1272,7 +1196,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb) static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) { - struct list_head *tmp, *head; + struct uhci_td *td; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; int status; int i, ret = 0; @@ -1280,14 +1204,9 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) urb->actual_length = 0; i = 0; - head = &urbp->td_list; - tmp = head->next; - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); + list_for_each_entry(td, &urbp->td_list, list) { int actlength; - tmp = tmp->next; - if (td_status(td) & TD_CTRL_ACTIVE) return -EINPROGRESS; @@ -1311,20 +1230,15 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb) { - struct list_head *tmp, *head; + struct urb_priv *up; /* We don't match Isoc transfers since they are special */ if (usb_pipeisoc(urb->pipe)) return NULL; - head = &uhci->urb_list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list); + list_for_each_entry(up, &uhci->urb_list, urb_list) { struct urb *u = up->urb; - tmp = tmp->next; - if (u->dev == urb->dev && u->status == -EINPROGRESS) { /* For control, ignore the direction */ if (usb_pipecontrol(urb->pipe) && @@ -1475,9 +1389,10 @@ out: static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb) { - struct list_head *head, *tmp; + struct list_head *head; + struct uhci_td *td; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - int prevactive = 1; + int prevactive = 0; uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ @@ -1485,25 +1400,28 @@ static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb) * Now we need to find out what the last successful toggle was * so we can update the local data toggle for the next transfer * - * There's 3 way's the last successful completed TD is found: + * There are 2 ways the last successful completed TD is found: * * 1) The TD is NOT active and the actual length < expected length * 2) The TD is NOT active and it's the last TD in the chain + * + * and a third way the first uncompleted TD is found: + * * 3) The TD is active and the previous TD is NOT active * * Control and Isochronous ignore the toggle, so this is safe * for all types + * + * FIXME: The toggle fixups won't be 100% reliable until we + * change over to using a single queue for each endpoint and + * stop the queue before unlinking. */ head = &urbp->td_list; - tmp = head->next; - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - + list_for_each_entry(td, head, list) { if (!(td_status(td) & TD_CTRL_ACTIVE) && - (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td)) || - tmp == head)) + (uhci_actual_length(td_status(td)) < + uhci_expected_length(td_token(td)) || + td->list.next == head)) usb_settoggle(urb->dev, uhci_endpoint(td_token(td)), uhci_packetout(td_token(td)), uhci_toggle(td_token(td)) ^ 1); @@ -1556,7 +1474,8 @@ done: static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb) { struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct list_head *head, *tmp; + struct list_head *head; + struct uhci_td *td; int count = 0; uhci_dec_fsbr(uhci, urb); @@ -1570,18 +1489,14 @@ static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb) */ head = &urbp->td_list; - tmp = head->next; - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - + list_for_each_entry(td, head, list) { /* * Make sure we don't do the last one (since it'll have the * TERM bit set) as well as we skip every so many TD's to * make sure it doesn't hog the bandwidth */ - if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1)) + if (td->list.next != head && (count % DEPTH_INTERVAL) == + (DEPTH_INTERVAL - 1)) td->link |= UHCI_PTR_DEPTH; count++; @@ -1606,12 +1521,10 @@ static void stall_callback(unsigned long ptr) { struct usb_hcd *hcd = (struct usb_hcd *)ptr; struct uhci_hcd *uhci = hcd_to_uhci(hcd); - struct list_head list, *tmp, *head; + struct urb_priv *up; unsigned long flags; int called_uhci_finish_completion = 0; - INIT_LIST_HEAD(&list); - spin_lock_irqsave(&uhci->schedule_lock, flags); if (!list_empty(&uhci->urb_remove_list) && uhci_get_current_frame_number(uhci) != uhci->urb_remove_age) { @@ -1620,14 +1533,9 @@ static void stall_callback(unsigned long ptr) called_uhci_finish_completion = 1; } - head = &uhci->urb_list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list); + list_for_each_entry(up, &uhci->urb_list, urb_list) { struct urb *u = up->urb; - tmp = tmp->next; - spin_lock(&u->lock); /* Check if the FSBR timed out */ @@ -1642,17 +1550,6 @@ static void stall_callback(unsigned long ptr) if (called_uhci_finish_completion) wake_up_all(&uhci->waitqh); - head = &list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list); - struct urb *u = up->urb; - - tmp = tmp->next; - - uhci_urb_dequeue(hcd, u); - } - /* Really disable FSBR */ if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) { uhci->fsbrtimeout = 0; @@ -1661,6 +1558,8 @@ static void stall_callback(unsigned long ptr) /* Poll for and perform state transitions */ hc_state_transitions(uhci); + if (unlikely(uhci->suspended_ports && uhci->state != UHCI_SUSPENDED)) + uhci_check_resume(uhci); init_stall_timer(hcd); } @@ -1680,15 +1579,9 @@ static int init_stall_timer(struct usb_hcd *hcd) static void uhci_free_pending_qhs(struct uhci_hcd *uhci) { - struct list_head *tmp, *head; - - head = &uhci->qh_remove_list; - tmp = head->next; - while (tmp != head) { - struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list); - - tmp = tmp->next; + struct uhci_qh *qh, *tmp; + list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) { list_del_init(&qh->remove_list); uhci_free_qh(uhci, qh); @@ -1697,15 +1590,9 @@ static void uhci_free_pending_qhs(struct uhci_hcd *uhci) static void uhci_free_pending_tds(struct uhci_hcd *uhci) { - struct list_head *tmp, *head; - - head = &uhci->td_remove_list; - tmp = head->next; - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, remove_list); - - tmp = tmp->next; + struct uhci_td *td, *tmp; + list_for_each_entry_safe(td, tmp, &uhci->td_remove_list, remove_list) { list_del_init(&td->remove_list); uhci_free_td(uhci, td); @@ -1726,19 +1613,13 @@ static void uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs *regs) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); - struct list_head *tmp, *head; + struct urb_priv *urbp, *tmp; - head = &uhci->complete_list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list); + list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) { struct urb *urb = urbp->urb; list_del_init(&urbp->urb_list); uhci_finish_urb(hcd, urb, regs); - - head = &uhci->complete_list; - tmp = head->next; } } @@ -1754,7 +1635,7 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) struct uhci_hcd *uhci = hcd_to_uhci(hcd); unsigned long io_addr = uhci->io_addr; unsigned short status; - struct list_head *tmp, *head; + struct urb_priv *urbp, *tmp; unsigned int age; /* @@ -1801,15 +1682,11 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) else uhci_set_next_interrupt(uhci); - /* Walk the list of pending URB's to see which ones completed */ - head = &uhci->urb_list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list); + /* Walk the list of pending URBs to see which ones completed + * (must be _safe because uhci_transfer_result() dequeues URBs) */ + list_for_each_entry_safe(urbp, tmp, &uhci->urb_list, urb_list) { struct urb *urb = urbp->urb; - tmp = tmp->next; - /* Checks the status and does all of the magic necessary */ uhci_transfer_result(uhci, urb); } diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 785c0c2cca12..ea9bd98b5edc 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -352,6 +352,12 @@ struct uhci_hcd { int resume_detect; /* Need a Global Resume */ unsigned int saved_framenumber; /* Save during PM suspend */ + /* Support for port suspend/resume */ + unsigned long port_c_suspend; /* Bit-arrays of ports */ + unsigned long suspended_ports; + unsigned long resuming_ports; + unsigned long resume_timeout; /* Time to stop signalling */ + /* Main list of URB's currently controlled by this HC */ struct list_head urb_list; /* P: uhci->schedule_lock */ @@ -385,12 +391,12 @@ struct urb_priv { struct uhci_qh *qh; /* QH for this URB */ struct list_head td_list; /* P: urb->lock */ - int fsbr : 1; /* URB turned on FSBR */ - int fsbr_timeout : 1; /* URB timed out on FSBR */ - int queued : 1; /* QH was queued (not linked in) */ - int short_control_packet : 1; /* If we get a short packet during */ - /* a control transfer, retrigger */ - /* the status phase */ + unsigned fsbr : 1; /* URB turned on FSBR */ + unsigned fsbr_timeout : 1; /* URB timed out on FSBR */ + unsigned queued : 1; /* QH was queued (not linked in) */ + unsigned short_control_packet : 1; /* If we get a short packet during */ + /* a control transfer, retrigger */ + /* the status phase */ unsigned long inserttime; /* In jiffies */ unsigned long fsbrtime; /* In jiffies */ diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 22bbd70063ce..f44d90e23df2 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -36,13 +36,13 @@ static __u8 root_hub_hub_des[] = static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); - unsigned long io_addr = uhci->io_addr; - int i; + int port; *buf = 0; - for (i = 0; i < uhci->rh_numports; i++) { - if (inw(io_addr + USBPORTSC1 + i * 2) & RWC_BITS) - *buf |= (1 << (i + 1)); + for (port = 0; port < uhci->rh_numports; ++port) { + if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & RWC_BITS) || + test_bit(port, &uhci->port_c_suspend)) + *buf |= (1 << (port + 1)); } return !!*buf; } @@ -62,31 +62,67 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) status &= ~(RWC_BITS|WZ_BITS); \ outw(status, port_addr) +/* UHCI controllers don't automatically stop resume signalling after 20 msec, + * so we have to poll and check timeouts in order to take care of it. + * FIXME: Synchronize access to these fields by a spinlock. + */ +static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, + unsigned int port_addr) +{ + int status; + + if (test_bit(port, &uhci->suspended_ports)) { + CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD); + clear_bit(port, &uhci->suspended_ports); + clear_bit(port, &uhci->resuming_ports); + set_bit(port, &uhci->port_c_suspend); + } +} + +static void uhci_check_resume(struct uhci_hcd *uhci) +{ + unsigned int port; + unsigned int port_addr; + + for (port = 0; port < uhci->rh_numports; ++port) { + port_addr = uhci->io_addr + USBPORTSC1 + 2 * port; + if (unlikely(inw(port_addr) & USBPORTSC_RD)) { + if (!test_bit(port, &uhci->resuming_ports)) { + + /* Port received a wakeup request */ + set_bit(port, &uhci->resuming_ports); + uhci->resume_timeout = jiffies + + msecs_to_jiffies(20); + } else if (time_after_eq(jiffies, + uhci->resume_timeout)) { + uhci_finish_suspend(uhci, port, port_addr); + } + } + } +} /* 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) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); - int status, retval = 0, len = 0; - unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * (wIndex-1); - __u16 wPortChange, wPortStatus; + int status, lstatus, retval = 0, len = 0; + unsigned int port = wIndex - 1; + unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port; + u16 wPortChange, wPortStatus; switch (typeReq) { - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ case GetHubStatus: *(__le32 *)buf = cpu_to_le32(0); OK(4); /* hub power */ case GetPortStatus: - if (!wIndex || wIndex > uhci->rh_numports) + if (port >= uhci->rh_numports) goto err; + + if (uhci->resuming_ports) + uhci_check_resume(uhci); + status = inw(port_addr); /* Intel controllers report the OverCurrent bit active on. @@ -97,34 +133,43 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, PCI_VENDOR_ID_VIA) status ^= USBPORTSC_OC; - /* UHCI doesn't support C_SUSPEND and C_RESET (always false) */ - wPortChange = 0; + /* UHCI doesn't support C_RESET (always false) */ + wPortChange = lstatus = 0; if (status & USBPORTSC_CSC) - wPortChange |= 1 << (USB_PORT_FEAT_C_CONNECTION - 16); + wPortChange |= USB_PORT_STAT_C_CONNECTION; if (status & USBPORTSC_PEC) - wPortChange |= 1 << (USB_PORT_FEAT_C_ENABLE - 16); + wPortChange |= USB_PORT_STAT_C_ENABLE; if (status & USBPORTSC_OCC) - wPortChange |= 1 << (USB_PORT_FEAT_C_OVER_CURRENT - 16); + wPortChange |= USB_PORT_STAT_C_OVERCURRENT; + + if (test_bit(port, &uhci->port_c_suspend)) { + wPortChange |= USB_PORT_STAT_C_SUSPEND; + lstatus |= 1; + } + if (test_bit(port, &uhci->suspended_ports)) + lstatus |= 2; + if (test_bit(port, &uhci->resuming_ports)) + lstatus |= 4; /* UHCI has no power switching (always on) */ - wPortStatus = 1 << USB_PORT_FEAT_POWER; + wPortStatus = USB_PORT_STAT_POWER; if (status & USBPORTSC_CCS) - wPortStatus |= 1 << USB_PORT_FEAT_CONNECTION; + wPortStatus |= USB_PORT_STAT_CONNECTION; if (status & USBPORTSC_PE) { - wPortStatus |= 1 << USB_PORT_FEAT_ENABLE; + wPortStatus |= USB_PORT_STAT_ENABLE; if (status & (USBPORTSC_SUSP | USBPORTSC_RD)) - wPortStatus |= 1 << USB_PORT_FEAT_SUSPEND; + wPortStatus |= USB_PORT_STAT_SUSPEND; } if (status & USBPORTSC_OC) - wPortStatus |= 1 << USB_PORT_FEAT_OVER_CURRENT; + wPortStatus |= USB_PORT_STAT_OVERCURRENT; if (status & USBPORTSC_PR) - wPortStatus |= 1 << USB_PORT_FEAT_RESET; + wPortStatus |= USB_PORT_STAT_RESET; if (status & USBPORTSC_LSDA) - wPortStatus |= 1 << USB_PORT_FEAT_LOWSPEED; + wPortStatus |= USB_PORT_STAT_LOW_SPEED; if (wPortChange) - dev_dbg(uhci_dev(uhci), "port %d portsc %04x\n", - wIndex, status); + dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n", + wIndex, status, lstatus); *(__le16 *)buf = cpu_to_le16(wPortStatus); *(__le16 *)(buf + 2) = cpu_to_le16(wPortChange); @@ -140,11 +185,12 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } break; case SetPortFeature: - if (!wIndex || wIndex > uhci->rh_numports) + if (port >= uhci->rh_numports) goto err; switch (wValue) { case USB_PORT_FEAT_SUSPEND: + set_bit(port, &uhci->suspended_ports); SET_RH_PORTSTAT(USBPORTSC_SUSP); OK(0); case USB_PORT_FEAT_RESET: @@ -152,6 +198,9 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, mdelay(50); /* USB v1.1 7.1.7.3 */ CLR_RH_PORTSTAT(USBPORTSC_PR); udelay(10); + + /* Reset terminates Resume signalling */ + uhci_finish_suspend(uhci, port, port_addr); SET_RH_PORTSTAT(USBPORTSC_PE); mdelay(10); CLR_RH_PORTSTAT(USBPORTSC_PEC|USBPORTSC_CSC); @@ -164,21 +213,38 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } break; case ClearPortFeature: - if (!wIndex || wIndex > uhci->rh_numports) + if (port >= uhci->rh_numports) goto err; switch (wValue) { case USB_PORT_FEAT_ENABLE: CLR_RH_PORTSTAT(USBPORTSC_PE); + + /* Disable terminates Resume signalling */ + uhci_finish_suspend(uhci, port, port_addr); OK(0); case USB_PORT_FEAT_C_ENABLE: CLR_RH_PORTSTAT(USBPORTSC_PEC); OK(0); case USB_PORT_FEAT_SUSPEND: - CLR_RH_PORTSTAT(USBPORTSC_SUSP); + if (test_bit(port, &uhci->suspended_ports) && + !test_and_set_bit(port, + &uhci->resuming_ports)) { + uhci->resume_timeout = jiffies + + msecs_to_jiffies(20); + SET_RH_PORTSTAT(USBPORTSC_RD); + + /* The controller won't allow RD to be set + * if the port is disabled. When this happens + * just skip the Resume signalling. + */ + if (!(inw(port_addr) & USBPORTSC_RD)) + uhci_finish_suspend(uhci, port, + port_addr); + } OK(0); case USB_PORT_FEAT_C_SUSPEND: - /* this driver won't report these */ + clear_bit(port, &uhci->port_c_suspend); OK(0); case USB_PORT_FEAT_POWER: /* UHCI has no power switching */ diff --git a/drivers/usb/image/Kconfig b/drivers/usb/image/Kconfig index 0b0f80e4f9a7..b541b67d2eb6 100644 --- a/drivers/usb/image/Kconfig +++ b/drivers/usb/image/Kconfig @@ -30,11 +30,12 @@ config USB_MICROTEK This driver can be compiled as a module, called microtek. config USB_HPUSBSCSI - tristate "HP53xx USB scanner support (EXPERIMENTAL)" - depends on USB && SCSI && EXPERIMENTAL + tristate "HP53xx USB scanner support" + depends on USB && SCSI help Say Y here if you want support for the HP 53xx series of scanners - and the Minolta Scan Dual. This driver is experimental. + and the Minolta Scan Dual. The scanner will be accessible as a SCSI device. + Please note that recent versions of SANE use usbfs, not this driver. This can be compiled as a module, called hpusbscsi. diff --git a/drivers/usb/image/hpusbscsi.c b/drivers/usb/image/hpusbscsi.c index dabc3666aee7..47a864b29f3d 100644 --- a/drivers/usb/image/hpusbscsi.c +++ b/drivers/usb/image/hpusbscsi.c @@ -106,7 +106,7 @@ hpusbscsi_usb_probe(struct usb_interface *intf, /* In host->hostdata we store a pointer to desc */ new->host = scsi_host_alloc(&hpusbscsi_scsi_host_template, sizeof(new)); if (!new->host) - goto out_unlink_controlurb; + goto out_kill_controlurb; new->host->hostdata[0] = (unsigned long)new; scsi_add_host(new->host, &intf->dev); /* XXX handle failure */ @@ -118,8 +118,8 @@ hpusbscsi_usb_probe(struct usb_interface *intf, usb_set_intfdata(intf, new); return 0; - out_unlink_controlurb: - usb_unlink_urb(new->controlurb); + out_kill_controlurb: + usb_kill_urb(new->controlurb); out_free_controlurb: usb_free_urb(new->controlurb); out_free_dataurb: @@ -137,7 +137,7 @@ hpusbscsi_usb_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); scsi_remove_host(desc->host); - usb_unlink_urb(desc->controlurb); + usb_kill_urb(desc->controlurb); scsi_host_put(desc->host); usb_free_urb(desc->controlurb); @@ -280,8 +280,8 @@ static int hpusbscsi_scsi_abort (Scsi_Cmnd *srb) struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->device->host->hostdata[0]); printk(KERN_DEBUG"Requested is canceled.\n"); - usb_unlink_urb(hpusbscsi->dataurb); - usb_unlink_urb(hpusbscsi->controlurb); + usb_kill_urb(hpusbscsi->dataurb); + usb_kill_urb(hpusbscsi->controlurb); hpusbscsi->state = HP_STATE_FREE; return SCSI_ABORT_PENDING; diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index f401557f0356..66bb13307df5 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -317,7 +317,6 @@ static int mdc800_usb_waitForIRQ (int mode, int msec) mdc800->camera_request_ready=1+mode; add_wait_queue(&mdc800->irq_wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); timeout = msec*HZ/1000; while (!mdc800->irq_woken && timeout) { @@ -325,7 +324,6 @@ static int mdc800_usb_waitForIRQ (int mode, int msec) timeout = schedule_timeout (timeout); } remove_wait_queue(&mdc800->irq_wait, &wait); - set_current_state(TASK_RUNNING); mdc800->irq_woken = 0; if (mdc800->camera_request_ready>0) @@ -543,9 +541,9 @@ static void mdc800_usb_disconnect (struct usb_interface *intf) mdc800->state=NOT_CONNECTED; - usb_unlink_urb (mdc800->irq_urb); - usb_unlink_urb (mdc800->write_urb); - usb_unlink_urb (mdc800->download_urb); + usb_kill_urb(mdc800->irq_urb); + usb_kill_urb(mdc800->write_urb); + usb_kill_urb(mdc800->download_urb); mdc800->dev = NULL; usb_set_intfdata(intf, NULL); @@ -649,9 +647,9 @@ static int mdc800_device_release (struct inode* inode, struct file *file) down (&mdc800->io_lock); if (mdc800->open && (mdc800->state != NOT_CONNECTED)) { - usb_unlink_urb (mdc800->irq_urb); - usb_unlink_urb (mdc800->write_urb); - usb_unlink_urb (mdc800->download_urb); + usb_kill_urb(mdc800->irq_urb); + usb_kill_urb(mdc800->write_urb); + usb_kill_urb(mdc800->download_urb); mdc800->open=0; } else @@ -725,7 +723,6 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l set_current_state(TASK_UNINTERRUPTIBLE); timeout = schedule_timeout (timeout); } - set_current_state(TASK_RUNNING); remove_wait_queue(&mdc800->download_wait, &wait); mdc800->downloaded = 0; if (mdc800->download_urb->status != 0) @@ -851,12 +848,11 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s set_current_state(TASK_UNINTERRUPTIBLE); timeout = schedule_timeout (timeout); } - set_current_state(TASK_RUNNING); remove_wait_queue(&mdc800->write_wait, &wait); mdc800->written = 0; if (mdc800->state == WORKING) { - usb_unlink_urb (mdc800->write_urb); + usb_kill_urb(mdc800->write_urb); up (&mdc800->io_lock); return -EIO; } diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 9d3aa3075086..2a18c35629eb 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -324,7 +324,7 @@ static inline void mts_urb_abort(struct mts_desc* desc) { MTS_DEBUG_GOT_HERE(); mts_debug_dump(desc); - usb_unlink_urb( desc->urb ); + usb_kill_urb( desc->urb ); } static int mts_scsi_abort (Scsi_Cmnd *srb) @@ -341,12 +341,18 @@ static int mts_scsi_abort (Scsi_Cmnd *srb) static int mts_scsi_host_reset (Scsi_Cmnd *srb) { struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); + int result, rc; MTS_DEBUG_GOT_HERE(); mts_debug_dump(desc); - usb_reset_device(desc->usb_dev); /*FIXME: untested on new reset code */ - return 0; /* RANT why here 0 and not SUCCESS */ + rc = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf); + if (rc < 0) + return FAILED; + result = usb_reset_device(desc->usb_dev);; + if (rc) + usb_unlock_device(desc->usb_dev); + return result ? FAILED : SUCCESS; } static @@ -777,6 +783,7 @@ static int mts_usb_probe(struct usb_interface *intf, goto out_kfree; new_desc->usb_dev = dev; + new_desc->usb_intf = intf; init_MUTEX(&new_desc->lock); /* endpoints */ @@ -822,10 +829,10 @@ static void mts_usb_disconnect (struct usb_interface *intf) usb_set_intfdata(intf, NULL); + usb_kill_urb(desc->urb); scsi_remove_host(desc->host); - usb_unlink_urb(desc->urb); - scsi_host_put(desc->host); + scsi_host_put(desc->host); usb_free_urb(desc->urb); kfree(desc); } diff --git a/drivers/usb/image/microtek.h b/drivers/usb/image/microtek.h index 206994ddcedf..3271deb8c001 100644 --- a/drivers/usb/image/microtek.h +++ b/drivers/usb/image/microtek.h @@ -31,6 +31,7 @@ struct mts_desc { struct mts_desc *prev; struct usb_device *usb_dev; + struct usb_interface *usb_intf; /* Endpoint addresses */ u8 ep_out; diff --git a/drivers/usb/input/aiptek.c b/drivers/usb/input/aiptek.c index 44b8faee6536..67a5b70a6daf 100644 --- a/drivers/usb/input/aiptek.c +++ b/drivers/usb/input/aiptek.c @@ -837,7 +837,7 @@ static void aiptek_close(struct input_dev *inputdev) struct aiptek *aiptek = inputdev->private; if (--aiptek->openCount == 0) { - usb_unlink_urb(aiptek->urb); + usb_kill_urb(aiptek->urb); } } @@ -2258,7 +2258,7 @@ static void aiptek_disconnect(struct usb_interface *intf) if (aiptek != NULL) { /* Free & unhook everything from the system. */ - usb_unlink_urb(aiptek->urb); + usb_kill_urb(aiptek->urb); input_unregister_device(&aiptek->inputdev); aiptek_delete_files(&intf->dev); usb_free_urb(aiptek->urb); diff --git a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c index 61a42bdae1d8..91f7f434dd48 100644 --- a/drivers/usb/input/ati_remote.c +++ b/drivers/usb/input/ati_remote.c @@ -418,13 +418,14 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigne while (timeout && (ati_remote->out_urb->status == -EINPROGRESS) && !(ati_remote->send_flags & SEND_FLAG_COMPLETE)) { + set_current_state(TASK_INTERRUPTIBLE); timeout = schedule_timeout(timeout); rmb(); } set_current_state(TASK_RUNNING); remove_wait_queue(&ati_remote->wait, &wait); - usb_unlink_urb(ati_remote->out_urb); + usb_kill_urb(ati_remote->out_urb); return retval; } @@ -624,10 +625,10 @@ static void ati_remote_delete(struct ati_remote *ati_remote) if (!ati_remote) return; if (ati_remote->irq_urb) - usb_unlink_urb(ati_remote->irq_urb); + usb_kill_urb(ati_remote->irq_urb); if (ati_remote->out_urb) - usb_unlink_urb(ati_remote->out_urb); + usb_kill_urb(ati_remote->out_urb); input_unregister_device(&ati_remote->idev); diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 35baacd7706d..c38e7fccf564 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -1260,8 +1260,10 @@ int hid_wait_io(struct hid_device *hid) add_wait_queue(&hid->wait, &wait); while (timeout && (test_bit(HID_CTRL_RUNNING, &hid->iofl) || - test_bit(HID_OUT_RUNNING, &hid->iofl))) + test_bit(HID_OUT_RUNNING, &hid->iofl))) { + set_current_state(TASK_UNINTERRUPTIBLE); timeout = schedule_timeout(timeout); + } set_current_state(TASK_RUNNING); remove_wait_queue(&hid->wait, &wait); @@ -1350,9 +1352,9 @@ void hid_init_reports(struct hid_device *hid) while (ret) { err |= ret; if (test_bit(HID_CTRL_RUNNING, &hid->iofl)) - usb_unlink_urb(hid->urbctrl); + usb_kill_urb(hid->urbctrl); if (test_bit(HID_OUT_RUNNING, &hid->iofl)) - usb_unlink_urb(hid->urbout); + usb_kill_urb(hid->urbout); ret = hid_wait_io(hid); } @@ -1455,10 +1457,11 @@ void hid_init_reports(struct hid_device *hid) #define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101 #define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104 -#define USB_VENDOR_ID_CODEMERCS 0x07c0 -#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500 -#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501 - +#define USB_VENDOR_ID_CODEMERCS 0x07c0 +#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500 +#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501 +#define USB_DEVICE_ID_CODEMERCS_IOW48 0x1502 +#define USB_DEVICE_ID_CODEMERCS_IOW28 0x1503 static struct hid_blacklist { __u16 idVendor; @@ -1542,6 +1545,11 @@ static struct hid_blacklist { { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW48, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW28, HID_QUIRK_IGNORE }, + { 0, 0 } }; diff --git a/drivers/usb/input/kbtab.c b/drivers/usb/input/kbtab.c index 39c0d38aefa4..2e4c0d246030 100644 --- a/drivers/usb/input/kbtab.c +++ b/drivers/usb/input/kbtab.c @@ -122,7 +122,7 @@ static void kbtab_close(struct input_dev *dev) struct kbtab *kbtab = dev->private; if (!--kbtab->open) - usb_unlink_urb(kbtab->irq); + usb_kill_urb(kbtab->irq); } static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -205,7 +205,7 @@ static void kbtab_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (kbtab) { - usb_unlink_urb(kbtab->irq); + usb_kill_urb(kbtab->irq); input_unregister_device(&kbtab->dev); usb_free_urb(kbtab->irq); usb_buffer_free(interface_to_usbdev(intf), 10, kbtab->data, kbtab->data_dma); diff --git a/drivers/usb/input/mtouchusb.c b/drivers/usb/input/mtouchusb.c index ae5713876d4c..9dcfd7e2ffbf 100644 --- a/drivers/usb/input/mtouchusb.c +++ b/drivers/usb/input/mtouchusb.c @@ -155,7 +155,7 @@ static void mtouchusb_close (struct input_dev *input) struct mtouch_usb *mtouch = input->private; if (!--mtouch->open) - usb_unlink_urb (mtouch->irq); + usb_kill_urb (mtouch->irq); } static int mtouchusb_alloc_buffers(struct usb_device *udev, struct mtouch_usb *mtouch) @@ -320,7 +320,7 @@ static void mtouchusb_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (mtouch) { dbg("%s - mtouch is initialized, cleaning up", __FUNCTION__); - usb_unlink_urb(mtouch->irq); + usb_kill_urb(mtouch->irq); input_unregister_device(&mtouch->input); usb_free_urb(mtouch->irq); mtouchusb_free_buffers(interface_to_usbdev(intf), mtouch); diff --git a/drivers/usb/input/pid.c b/drivers/usb/input/pid.c index a38b0db8dcea..d1ea1f056599 100644 --- a/drivers/usb/input/pid.c +++ b/drivers/usb/input/pid.c @@ -56,7 +56,7 @@ static void hid_pid_exit(struct hid_device* hid) struct hid_ff_pid *private = hid->ff_private; if (private->urbffout) { - usb_unlink_urb(private->urbffout); + usb_kill_urb(private->urbffout); usb_free_urb(private->urbffout); } } diff --git a/drivers/usb/input/powermate.c b/drivers/usb/input/powermate.c index 0ba1bcbfb431..852ebf8574a8 100644 --- a/drivers/usb/input/powermate.c +++ b/drivers/usb/input/powermate.c @@ -417,7 +417,7 @@ static void powermate_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (pm) { pm->requires_update = 0; - usb_unlink_urb(pm->irq); + usb_kill_urb(pm->irq); input_unregister_device(&pm->input); usb_free_urb(pm->irq); usb_free_urb(pm->config); diff --git a/drivers/usb/input/touchkitusb.c b/drivers/usb/input/touchkitusb.c index 4917b042ef9a..14443f926ab0 100644 --- a/drivers/usb/input/touchkitusb.c +++ b/drivers/usb/input/touchkitusb.c @@ -141,7 +141,7 @@ static void touchkit_close(struct input_dev *input) struct touchkit_usb *touchkit = input->private; if (!--touchkit->open) - usb_unlink_urb(touchkit->irq); + usb_kill_urb(touchkit->irq); } static int touchkit_alloc_buffers(struct usb_device *udev, @@ -276,7 +276,7 @@ static void touchkit_disconnect(struct usb_interface *intf) dbg("%s - touchkit is initialized, cleaning up", __FUNCTION__); usb_set_intfdata(intf, NULL); input_unregister_device(&touchkit->input); - usb_unlink_urb(touchkit->irq); + usb_kill_urb(touchkit->irq); usb_free_urb(touchkit->irq); touchkit_free_buffers(interface_to_usbdev(intf), touchkit); kfree(touchkit); diff --git a/drivers/usb/input/usbkbd.c b/drivers/usb/input/usbkbd.c index 0c51b1eb84cf..1700f405b00b 100644 --- a/drivers/usb/input/usbkbd.c +++ b/drivers/usb/input/usbkbd.c @@ -196,7 +196,7 @@ static void usb_kbd_close(struct input_dev *dev) struct usb_kbd *kbd = dev->private; if (!--kbd->open) - usb_unlink_urb(kbd->irq); + usb_kill_urb(kbd->irq); } static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd) @@ -343,7 +343,7 @@ static void usb_kbd_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (kbd) { - usb_unlink_urb(kbd->irq); + usb_kill_urb(kbd->irq); input_unregister_device(&kbd->dev); usb_kbd_free_mem(interface_to_usbdev(intf), kbd); kfree(kbd); diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c index 5bf27306ea3c..8c7381b74499 100644 --- a/drivers/usb/input/usbmouse.c +++ b/drivers/usb/input/usbmouse.c @@ -118,7 +118,7 @@ static void usb_mouse_close(struct input_dev *dev) struct usb_mouse *mouse = dev->private; if (!--mouse->open) - usb_unlink_urb(mouse->irq); + usb_kill_urb(mouse->irq); } static int usb_mouse_probe(struct usb_interface * intf, const struct usb_device_id * id) @@ -223,7 +223,7 @@ static void usb_mouse_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (mouse) { - usb_unlink_urb(mouse->irq); + usb_kill_urb(mouse->irq); input_unregister_device(&mouse->dev); usb_free_urb(mouse->irq); usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma); diff --git a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c index 492a5cb12af4..471d1bf68bf0 100644 --- a/drivers/usb/input/wacom.c +++ b/drivers/usb/input/wacom.c @@ -608,7 +608,7 @@ static void wacom_close(struct input_dev *dev) struct wacom *wacom = dev->private; if (!--wacom->open) - usb_unlink_urb(wacom->irq); + usb_kill_urb(wacom->irq); } static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -729,7 +729,7 @@ static void wacom_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (wacom) { - usb_unlink_urb(wacom->irq); + usb_kill_urb(wacom->irq); input_unregister_device(&wacom->dev); usb_free_urb(wacom->irq); usb_buffer_free(interface_to_usbdev(intf), 10, wacom->data, wacom->data_dma); diff --git a/drivers/usb/input/xpad.c b/drivers/usb/input/xpad.c index 1956eb06b75c..c5fa87edb667 100644 --- a/drivers/usb/input/xpad.c +++ b/drivers/usb/input/xpad.c @@ -214,7 +214,7 @@ static void xpad_close (struct input_dev *dev) struct usb_xpad *xpad = dev->private; if (!--xpad->open_count) - usb_unlink_urb(xpad->irq_in); + usb_kill_urb(xpad->irq_in); } static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -325,7 +325,7 @@ static void xpad_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (xpad) { - usb_unlink_urb(xpad->irq_in); + usb_kill_urb(xpad->irq_in); input_unregister_device(&xpad->dev); usb_free_urb(xpad->irq_in); usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); diff --git a/drivers/usb/media/Kconfig b/drivers/usb/media/Kconfig index 4ac7ac4f3b1d..678e5fcbe29a 100644 --- a/drivers/usb/media/Kconfig +++ b/drivers/usb/media/Kconfig @@ -123,11 +123,11 @@ config USB_SE401 module will be called se401. config USB_SN9C102 - tristate "USB SN9C10[12] PC Camera Controller support" + tristate "USB SN9C10x PC Camera Controller support" depends on USB && VIDEO_DEV ---help--- - Say Y here if you want support for cameras based on SONiX SN9C101 - or SN9C102 PC Camera Controllers. + Say Y here if you want support for cameras based on SONiX SN9C101, + SN9C102 or SN9C103 PC Camera Controllers. See <file:Documentation/usb/sn9c102.txt> for more informations. diff --git a/drivers/usb/media/dabusb.c b/drivers/usb/media/dabusb.c index 0e5425f328d0..0aae6ecc7ad6 100644 --- a/drivers/usb/media/dabusb.c +++ b/drivers/usb/media/dabusb.c @@ -109,16 +109,13 @@ static void dump_urb (struct urb *urb) static int dabusb_cancel_queue (pdabusb_t s, struct list_head *q) { unsigned long flags; - struct list_head *p; pbuff_t b; dbg("dabusb_cancel_queue"); spin_lock_irqsave (&s->lock, flags); - for (p = q->next; p != q; p = p->next) { - b = list_entry (p, buff_t, buff_list); - + list_for_each_entry(b, q, buff_list) { #ifdef DEBUG dump_urb(b->purb); #endif @@ -598,6 +595,7 @@ static int dabusb_open (struct inode *inode, struct file *file) if (file->f_flags & O_NONBLOCK) { return -EBUSY; } + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout (HZ / 2); if (signal_pending (current)) { diff --git a/drivers/usb/media/konicawc.c b/drivers/usb/media/konicawc.c index 3376654ca051..aca3093c2b87 100644 --- a/drivers/usb/media/konicawc.c +++ b/drivers/usb/media/konicawc.c @@ -474,13 +474,8 @@ static void konicawc_stop_data(struct uvd *uvd) /* Unschedule all of the iso td's */ for (i=0; i < USBVIDEO_NUMSBUF; i++) { - j = usb_unlink_urb(uvd->sbuf[i].urb); - if (j < 0) - err("usb_unlink_urb() error %d.", j); - - j = usb_unlink_urb(cam->sts_urb[i]); - if (j < 0) - err("usb_unlink_urb() error %d.", j); + usb_kill_urb(uvd->sbuf[i].urb); + usb_kill_urb(cam->sts_urb[i]); } if (!uvd->remove_pending) { diff --git a/drivers/usb/media/ov511.c b/drivers/usb/media/ov511.c index d040c8e30b6b..7de03331289e 100644 --- a/drivers/usb/media/ov511.c +++ b/drivers/usb/media/ov511.c @@ -3830,7 +3830,7 @@ ov51x_unlink_isoc(struct usb_ov511 *ov) /* Unschedule all of the iso td's */ for (n = OV511_NUMSBUF - 1; n >= 0; n--) { if (ov->sbuf[n].urb) { - usb_unlink_urb(ov->sbuf[n].urb); + usb_kill_urb(ov->sbuf[n].urb); usb_free_urb(ov->sbuf[n].urb); ov->sbuf[n].urb = NULL; } diff --git a/drivers/usb/media/se401.c b/drivers/usb/media/se401.c index c694caaac447..37d28640c790 100644 --- a/drivers/usb/media/se401.c +++ b/drivers/usb/media/se401.c @@ -514,7 +514,7 @@ static int se401_stop_stream(struct usb_se401 *se401) se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); for (i=0; i<SE401_NUMSBUF; i++) if (se401->urb[i]) { - usb_unlink_urb(se401->urb[i]); + usb_kill_urb(se401->urb[i]); usb_free_urb(se401->urb[i]); se401->urb[i]=NULL; kfree(se401->sbuf[i].data); @@ -883,7 +883,7 @@ static void usb_se401_remove_disconnected (struct usb_se401 *se401) se401->dev = NULL; for (i=0; i<SE401_NUMSBUF; i++) if (se401->urb[i]) { - usb_unlink_urb(se401->urb[i]); + usb_kill_urb(se401->urb[i]); usb_free_urb(se401->urb[i]); se401->urb[i] = NULL; kfree(se401->sbuf[i].data); @@ -892,7 +892,7 @@ static void usb_se401_remove_disconnected (struct usb_se401 *se401) kfree(se401->scratch[i].data); } if (se401->inturb) { - usb_unlink_urb(se401->inturb); + usb_kill_urb(se401->inturb); usb_free_urb(se401->inturb); } info("%s disconnected", se401->camera_name); diff --git a/drivers/usb/media/sn9c102.h b/drivers/usb/media/sn9c102.h index 62ff2144392c..a682dfc2f747 100644 --- a/drivers/usb/media/sn9c102.h +++ b/drivers/usb/media/sn9c102.h @@ -1,5 +1,5 @@ /*************************************************************************** - * V4L2 driver for SN9C10[12] PC Camera Controllers * + * V4L2 driver for SN9C10x PC Camera Controllers * * * * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * * @@ -49,15 +49,21 @@ /*****************************************************************************/ -#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10[12] PC Camera Controllers" +#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10x PC Camera Controllers" #define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia" #define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" #define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.08" -#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 8) +#define SN9C102_MODULE_VERSION "1:1.12" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 12) -SN9C102_ID_TABLE; -SN9C102_SENSOR_TABLE; +enum sn9c102_bridge { + BRIDGE_SN9C101 = 0x01, + BRIDGE_SN9C102 = 0x02, + BRIDGE_SN9C103 = 0x04, +}; + +SN9C102_ID_TABLE +SN9C102_SENSOR_TABLE enum sn9c102_frame_state { F_UNUSED, @@ -105,6 +111,7 @@ struct sn9c102_device { struct video_device* v4ldev; + enum sn9c102_bridge bridge; struct sn9c102_sensor* sensor; struct usb_device* usbdev; diff --git a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c index 9f775f74002c..6d428a5b8d03 100644 --- a/drivers/usb/media/sn9c102_core.c +++ b/drivers/usb/media/sn9c102_core.c @@ -1,5 +1,5 @@ /*************************************************************************** - * V4L2 driver for SN9C10[12] PC Camera Controllers * + * V4L2 driver for SN9C10x PC Camera Controllers * * * * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * * @@ -169,15 +169,15 @@ static u32 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count) cam->nbuffers = count; while (cam->nbuffers > 0) { - if ((buff = rvmalloc(cam->nbuffers * imagesize))) + if ((buff = rvmalloc(cam->nbuffers * PAGE_ALIGN(imagesize)))) break; cam->nbuffers--; } for (i = 0; i < cam->nbuffers; i++) { - cam->frame[i].bufmem = buff + i*imagesize; + cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); cam->frame[i].buf.index = i; - cam->frame[i].buf.m.offset = i*imagesize; + cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); cam->frame[i].buf.length = imagesize; cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cam->frame[i].buf.sequence = 0; @@ -388,7 +388,7 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, data[4] = data3; data[5] = data4; data[6] = data5; - data[7] = 0x10; + data[7] = 0x14; res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); if (res < 0) @@ -400,7 +400,7 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, if (err) DBG(3, "I2C write failed for %s image sensor", sensor->name) - PDBGG("I2C write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, " + PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, " "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X", n, data0, data1, data2, data3, data4, data5) @@ -634,7 +634,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam) struct usb_device *udev = cam->usbdev; struct urb* urb; const unsigned int wMaxPacketSize[] = {0, 128, 256, 384, 512, - 680, 800, 900, 1023}; + 680, 800, 900, 1023}; const unsigned int psz = wMaxPacketSize[SN9C102_ALTERNATE_SETTING]; s8 i, j; int err = 0; @@ -965,6 +965,11 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf) return -ENODEV; } + if (cam->sensor->slave_read_id == SN9C102_I2C_SLAVEID_UNAVAILABLE) { + up(&sn9c102_sysfs_lock); + return -ENOSYS; + } + if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { up(&sn9c102_sysfs_lock); return -EIO; @@ -1022,15 +1027,79 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) static ssize_t sn9c102_store_green(struct class_device* cd, const char* buf, size_t len) { + struct sn9c102_device* cam; + enum sn9c102_bridge bridge; ssize_t res = 0; u8 value; ssize_t count; + if (down_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + up(&sn9c102_sysfs_lock); + return -ENODEV; + } + + bridge = cam->bridge; + + up(&sn9c102_sysfs_lock); + value = sn9c102_strtou8(buf, len, &count); - if (!count || value > 0x0f) + if (!count) return -EINVAL; - if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0) + switch (bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + if (value > 0x0f) + return -EINVAL; + if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + break; + case BRIDGE_SN9C103: + if (value > 0x7f) + return -EINVAL; + if ((res = sn9c102_store_reg(cd, "0x04", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + break; + } + + return res; +} + + +static ssize_t +sn9c102_store_blue(struct class_device* cd, const char* buf, size_t len) +{ + ssize_t res = 0; + u8 value; + ssize_t count; + + value = sn9c102_strtou8(buf, len, &count); + if (!count || value > 0x7f) + return -EINVAL; + + if ((res = sn9c102_store_reg(cd, "0x06", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + + return res; +} + + +static ssize_t +sn9c102_store_red(struct class_device* cd, const char* buf, size_t len) +{ + ssize_t res = 0; + u8 value; + ssize_t count; + + value = sn9c102_strtou8(buf, len, &count); + if (!count || value > 0x7f) + return -EINVAL; + + if ((res = sn9c102_store_reg(cd, "0x05", 4)) >= 0) res = sn9c102_store_val(cd, buf, len); return res; @@ -1046,6 +1115,8 @@ static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, sn9c102_show_i2c_val, sn9c102_store_i2c_val); static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green); +static CLASS_DEVICE_ATTR(blue, S_IWUGO, NULL, sn9c102_store_blue); +static CLASS_DEVICE_ATTR(red, S_IWUGO, NULL, sn9c102_store_red); static void sn9c102_create_sysfs(struct sn9c102_device* cam) @@ -1054,8 +1125,14 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam) video_device_create_file(v4ldev, &class_device_attr_reg); video_device_create_file(v4ldev, &class_device_attr_val); - video_device_create_file(v4ldev, &class_device_attr_green); - if (cam->sensor->slave_write_id && cam->sensor->slave_read_id) { + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + video_device_create_file(v4ldev, &class_device_attr_green); + else if (cam->bridge == BRIDGE_SN9C103) { + video_device_create_file(v4ldev, &class_device_attr_blue); + video_device_create_file(v4ldev, &class_device_attr_red); + } + if (cam->sensor->slave_write_id != SN9C102_I2C_SLAVEID_UNAVAILABLE || + cam->sensor->slave_read_id != SN9C102_I2C_SLAVEID_UNAVAILABLE) { video_device_create_file(v4ldev, &class_device_attr_i2c_reg); video_device_create_file(v4ldev, &class_device_attr_i2c_val); } @@ -1092,21 +1169,13 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) u8 h_start = (u8)(rect->left - s->cropcap.bounds.left), v_start = (u8)(rect->top - s->cropcap.bounds.top), h_size = (u8)(rect->width / 16), - v_size = (u8)(rect->height / 16), - ae_strx = 0x00, - ae_stry = 0x00, - ae_endx = h_size / 2, - ae_endy = v_size / 2; + v_size = (u8)(rect->height / 16); int err = 0; err += sn9c102_write_reg(cam, h_start, 0x12); err += sn9c102_write_reg(cam, v_start, 0x13); err += sn9c102_write_reg(cam, h_size, 0x15); err += sn9c102_write_reg(cam, v_size, 0x16); - err += sn9c102_write_reg(cam, ae_strx, 0x1c); - err += sn9c102_write_reg(cam, ae_stry, 0x1d); - err += sn9c102_write_reg(cam, ae_endx, 0x1e); - err += sn9c102_write_reg(cam, ae_endy, 0x1f); if (err) return -EIO; @@ -1636,16 +1705,21 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; - if ((err = s->set_ctrl(cam, &ctrl))) - return err; - n = sizeof(s->qctrl) / sizeof(s->qctrl[0]); for (i = 0; i < n; i++) if (ctrl.id == s->qctrl[i].id) { - s->_qctrl[i].default_value = ctrl.value; + if (ctrl.value < s->qctrl[i].minimum || + ctrl.value > s->qctrl[i].maximum) + return -ERANGE; + ctrl.value -= ctrl.value % s->qctrl[i].step; break; } + if ((err = s->set_ctrl(cam, &ctrl))) + return err; + + s->_qctrl[i].default_value = ctrl.value; + return 0; } @@ -1776,7 +1850,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, DBG(1, "VIDIOC_S_CROP failed because of hardware " "problems. To use the camera, close and open " "/dev/video%d again.", cam->v4ldev->minor) - return err; + return -EIO; } s->pix_format.width = rect->width/scale; @@ -1951,7 +2025,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, DBG(1, "VIDIOC_S_FMT failed because of hardware " "problems. To use the camera, close and open " "/dev/video%d again.", cam->v4ldev->minor) - return err; + return -EIO; } memcpy(pfmt, pix, sizeof(*pix)); @@ -2286,16 +2360,28 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) r = sn9c102_read_reg(cam, 0x00); if (r < 0 || r != 0x10) { - DBG(1, "Sorry, this is not a SN9C10[12] based camera " + DBG(1, "Sorry, this is not a SN9C10x based camera " "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor,sn9c102_id_table[i].idProduct) err = -ENODEV; goto fail; } - DBG(2, "SN9C10[12] PC Camera Controller detected " - "(vid/pid 0x%04X/0x%04X)", - sn9c102_id_table[i].idVendor, sn9c102_id_table[i].idProduct) + cam->bridge = (sn9c102_id_table[i].idProduct & 0xffc0) == 0x6080 ? + BRIDGE_SN9C103 : BRIDGE_SN9C102; + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + DBG(2, "SN9C10[12] PC Camera Controller detected " + "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor, + sn9c102_id_table[i].idProduct) + break; + case BRIDGE_SN9C103: + DBG(2, "SN9C103 PC Camera Controller detected " + "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor, + sn9c102_id_table[i].idProduct) + break; + } for (i = 0; sn9c102_sensor_table[i]; i++) { err = sn9c102_sensor_table[i](cam); @@ -2318,7 +2404,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->state |= DEV_MISCONFIGURED; } - strcpy(cam->v4ldev->name, "SN9C10[12] PC Camera"); + strcpy(cam->v4ldev->name, "SN9C10x PC Camera"); cam->v4ldev->owner = THIS_MODULE; cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; cam->v4ldev->hardware = VID_HARDWARE_SN9C102; diff --git a/drivers/usb/media/sn9c102_pas106b.c b/drivers/usb/media/sn9c102_pas106b.c index a302a4845893..8453409ea125 100644 --- a/drivers/usb/media/sn9c102_pas106b.c +++ b/drivers/usb/media/sn9c102_pas106b.c @@ -1,5 +1,5 @@ /*************************************************************************** - * Driver for PAS106B image sensor connected to the SN9C10[12] PC Camera * + * Driver for PAS106B image sensor connected to the SN9C10x PC Camera * * Controllers * * * * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * @@ -100,26 +100,26 @@ static int pas106b_set_ctrl(struct sn9c102_device* cam, switch (ctrl->id) { case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value & 0x1f); + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); break; case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x1f); + err += sn9c102_i2c_write(cam, 0x09, ctrl->value); break; case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x0e, ctrl->value & 0x1f); + err += sn9c102_i2c_write(cam, 0x0e, ctrl->value); break; case V4L2_CID_BRIGHTNESS: - err += sn9c102_i2c_write(cam, 0x0d, 0x1f-(ctrl->value & 0x1f)); + err += sn9c102_i2c_write(cam, 0x0d, 0x1f - ctrl->value); break; case V4L2_CID_CONTRAST: - err += sn9c102_i2c_write(cam, 0x0f, ctrl->value & 0x03); + err += sn9c102_i2c_write(cam, 0x0f, ctrl->value); break; default: return -EINVAL; } err += sn9c102_i2c_write(cam, 0x13, 0x01); - return err; + return err ? -EIO : 0; } diff --git a/drivers/usb/media/sn9c102_pas202bcb.c b/drivers/usb/media/sn9c102_pas202bcb.c index 26944eaf85d0..72063e885871 100644 --- a/drivers/usb/media/sn9c102_pas202bcb.c +++ b/drivers/usb/media/sn9c102_pas202bcb.c @@ -1,5 +1,5 @@ /*************************************************************************** - * Driver for PAS202BCB image sensor connected to the SN9C10[12] PC Camera * + * Driver for PAS202BCB image sensor connected to the SN9C10x PC Camera * * Controllers * * * * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio * @@ -36,18 +36,19 @@ static int pas202bcb_init(struct sn9c102_device* cam) err += sn9c102_write_reg(cam, 0x00, 0x11); err += sn9c102_write_reg(cam, 0x00, 0x14); err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x20, 0x19); + err += sn9c102_write_reg(cam, 0x30, 0x19); err += sn9c102_write_reg(cam, 0x09, 0x18); - err += sn9c102_i2c_write(cam, 0x02, 0x0c); + err += sn9c102_i2c_write(cam, 0x02, 0x14); err += sn9c102_i2c_write(cam, 0x03, 0x40); err += sn9c102_i2c_write(cam, 0x04, 0x07); err += sn9c102_i2c_write(cam, 0x05, 0x25); err += sn9c102_i2c_write(cam, 0x0d, 0x2c); err += sn9c102_i2c_write(cam, 0x0e, 0x01); err += sn9c102_i2c_write(cam, 0x0f, 0xa9); - err += sn9c102_i2c_write(cam, 0x08, 0x01); + err += sn9c102_i2c_write(cam, 0x10, 0x08); err += sn9c102_i2c_write(cam, 0x0b, 0x01); + err += sn9c102_i2c_write(cam, 0x0c, 0x04); err += sn9c102_i2c_write(cam, 0x13, 0x63); err += sn9c102_i2c_write(cam, 0x15, 0x70); err += sn9c102_i2c_write(cam, 0x11, 0x01); @@ -95,23 +96,23 @@ static int pas202bcb_set_ctrl(struct sn9c102_device* cam, switch (ctrl->id) { case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x0f); + err += sn9c102_i2c_write(cam, 0x09, ctrl->value); break; case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x07, ctrl->value & 0x0f); + err += sn9c102_i2c_write(cam, 0x07, ctrl->value); break; case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value & 0x1f); + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); break; case V4L2_CID_BRIGHTNESS: - err += sn9c102_i2c_write(cam, 0x06, 0x0f-(ctrl->value & 0x0f)); + err += sn9c102_i2c_write(cam, 0x06, 0x0f - ctrl->value); break; default: return -EINVAL; } err += sn9c102_i2c_write(cam, 0x11, 0x01); - return err; + return err ? -EIO : 0; } @@ -217,7 +218,7 @@ int sn9c102_probe_pas202bcb(struct sn9c102_device* cam) * NOTE: do NOT change the values! */ err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */ - err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */ + err += sn9c102_write_reg(cam, 0x40, 0x01); /* sensor power on */ err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */ if (err) return -EIO; diff --git a/drivers/usb/media/sn9c102_sensor.h b/drivers/usb/media/sn9c102_sensor.h index 3e7e4a2578bc..01bcad14ca6d 100644 --- a/drivers/usb/media/sn9c102_sensor.h +++ b/drivers/usb/media/sn9c102_sensor.h @@ -1,5 +1,5 @@ /*************************************************************************** - * API for image sensors connected to the SN9C10[12] PC Camera Controllers * + * API for image sensors connected to the SN9C10x PC Camera Controllers * * * * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * * @@ -89,17 +89,44 @@ sn9c102_attach_sensor(struct sn9c102_device* cam, /* Each SN9C10X camera has proper PID/VID identifiers. Add them here in case.*/ #define SN9C102_ID_TABLE \ static const struct usb_device_id sn9c102_id_table[] = { \ - { USB_DEVICE(0xc45, 0x6001), }, /* TAS5110C1B */ \ - { USB_DEVICE(0xc45, 0x6005), }, /* TAS5110C1B */ \ - { USB_DEVICE(0xc45, 0x6009), }, /* PAS106B */ \ - { USB_DEVICE(0xc45, 0x600d), }, /* PAS106B */ \ - { USB_DEVICE(0xc45, 0x6024), }, \ - { USB_DEVICE(0xc45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \ - { USB_DEVICE(0xc45, 0x6028), }, /* PAS202BCB */ \ - { USB_DEVICE(0xc45, 0x6029), }, /* PAS106B */ \ - { USB_DEVICE(0xc45, 0x602a), }, /* HV7131[D|E1] */ \ - { USB_DEVICE(0xc45, 0x602c), }, /* OV7620 */ \ - { USB_DEVICE(0xc45, 0x6030), }, /* MI03 */ \ + { USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */ \ + { USB_DEVICE(0x0c45, 0x6005), }, /* TAS5110C1B */ \ + { USB_DEVICE(0x0c45, 0x6009), }, /* PAS106B */ \ + { USB_DEVICE(0x0c45, 0x600d), }, /* PAS106B */ \ + { USB_DEVICE(0x0c45, 0x6024), }, \ + { USB_DEVICE(0x0c45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \ + { USB_DEVICE(0x0c45, 0x6028), }, /* PAS202BCB */ \ + { USB_DEVICE(0x0c45, 0x6029), }, /* PAS106B */ \ + { USB_DEVICE(0x0c45, 0x602a), }, /* HV7131[D|E1] */ \ + { USB_DEVICE(0x0c45, 0x602b), }, \ + { USB_DEVICE(0x0c45, 0x602c), }, /* OV7620 */ \ + { USB_DEVICE(0x0c45, 0x6030), }, /* MI03x */ \ + { USB_DEVICE(0x0c45, 0x6080), }, \ + { USB_DEVICE(0x0c45, 0x6082), }, /* MI0343 and MI0360 */ \ + { USB_DEVICE(0x0c45, 0x6083), }, /* HV7131[D|E1] */ \ + { USB_DEVICE(0x0c45, 0x6088), }, \ + { USB_DEVICE(0x0c45, 0x608a), }, \ + { USB_DEVICE(0x0c45, 0x608b), }, \ + { USB_DEVICE(0x0c45, 0x608c), }, /* HV7131x */ \ + { USB_DEVICE(0x0c45, 0x608e), }, /* CIS-VF10 */ \ + { USB_DEVICE(0x0c45, 0x608f), }, /* OV7630 */ \ + { USB_DEVICE(0x0c45, 0x60a0), }, \ + { USB_DEVICE(0x0c45, 0x60a2), }, \ + { USB_DEVICE(0x0c45, 0x60a3), }, \ + { USB_DEVICE(0x0c45, 0x60a8), }, /* PAS106B */ \ + { USB_DEVICE(0x0c45, 0x60aa), }, /* TAS5130D1B */ \ + { USB_DEVICE(0x0c45, 0x60ab), }, /* TAS5110C1B */ \ + { USB_DEVICE(0x0c45, 0x60ac), }, \ + { USB_DEVICE(0x0c45, 0x60ae), }, \ + { USB_DEVICE(0x0c45, 0x60af), }, /* PAS202BCB */ \ + { USB_DEVICE(0x0c45, 0x60b0), }, \ + { USB_DEVICE(0x0c45, 0x60b2), }, \ + { USB_DEVICE(0x0c45, 0x60b3), }, \ + { USB_DEVICE(0x0c45, 0x60b8), }, \ + { USB_DEVICE(0x0c45, 0x60ba), }, \ + { USB_DEVICE(0x0c45, 0x60bb), }, \ + { USB_DEVICE(0x0c45, 0x60bc), }, \ + { USB_DEVICE(0x0c45, 0x60be), }, \ { } \ }; @@ -159,6 +186,9 @@ enum sn9c102_i2c_interface { SN9C102_I2C_3WIRES, }; +#define SN9C102_I2C_SLAVEID_FICTITIOUS 0xff +#define SN9C102_I2C_SLAVEID_UNAVAILABLE 0x00 + struct sn9c102_sensor { char name[32], /* sensor name */ maintainer[64]; /* name of the mantainer <email> */ @@ -173,9 +203,7 @@ struct sn9c102_sensor { /* These identifiers must be provided if the image sensor implements - the standard I2C protocol. TASC sensors don't, although they have a - serial interface: so this is a case where the "raw" I2C version - could be helpful. + the standard I2C protocol. */ u8 slave_read_id, slave_write_id; /* reg. 0x09 */ @@ -214,7 +242,8 @@ struct sn9c102_sensor { the list above. The returned value must follow the V4L2 specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER are not supported by this driver, so do not implement them. Also, - passed values are NOT checked to see if they are out of bounds. + you don't have to check whether the passed values are out of bounds, + given that this is done by the core module. */ struct v4l2_cropcap cropcap; diff --git a/drivers/usb/media/sn9c102_tas5110c1b.c b/drivers/usb/media/sn9c102_tas5110c1b.c index 68e1b2e0ce18..ce8b47b59a75 100644 --- a/drivers/usb/media/sn9c102_tas5110c1b.c +++ b/drivers/usb/media/sn9c102_tas5110c1b.c @@ -1,6 +1,6 @@ /*************************************************************************** - * Driver for TAS5110C1B image sensor connected to the SN9C10[12] PC * - * Camera Controllers * + * Driver for TAS5110C1B image sensor connected to the SN9C10x PC Camera * + * Controllers * * * * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * * @@ -24,6 +24,8 @@ static struct sn9c102_sensor tas5110c1b; +static struct v4l2_control tas5110c1b_gain; + static int tas5110c1b_init(struct sn9c102_device* cam) { @@ -38,25 +40,42 @@ static int tas5110c1b_init(struct sn9c102_device* cam) err += sn9c102_write_reg(cam, 0x06, 0x18); err += sn9c102_write_reg(cam, 0xfb, 0x19); - err += sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11, 0x00, 0xc0, - 0x80, 0, 0); + err += sn9c102_i2c_write(cam, 0xc0, 0x80); return err; } +static int tas5110c1b_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl->value = tas5110c1b_gain.value; + break; + default: + return -EINVAL; + } + + return 0; +} + + static int tas5110c1b_set_ctrl(struct sn9c102_device* cam, const struct v4l2_control* ctrl) { + int err = 0; + switch (ctrl->id) { case V4L2_CID_GAIN: - return sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11, - 0x02, 0x20, - 0xff - (ctrl->value & 0xff), - 0, 0); + if (!(err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value))) + tas5110c1b_gain.value = ctrl->value; + break; default: return -EINVAL; } + + return err ? -EIO : 0; } @@ -85,6 +104,8 @@ static struct sn9c102_sensor tas5110c1b = { .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, + .slave_read_id = SN9C102_I2C_SLAVEID_UNAVAILABLE, + .slave_write_id = SN9C102_I2C_SLAVEID_FICTITIOUS, .init = &tas5110c1b_init, .qctrl = { { @@ -92,9 +113,9 @@ static struct sn9c102_sensor tas5110c1b = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "global gain", .minimum = 0x00, - .maximum = 0xff, + .maximum = 0xf6, .step = 0x01, - .default_value = 0x48, + .default_value = 0x40, .flags = 0, }, }, @@ -113,6 +134,7 @@ static struct sn9c102_sensor tas5110c1b = { .height = 288, }, }, + .get_ctrl = &tas5110c1b_get_ctrl, .set_crop = &tas5110c1b_set_crop, .pix_format = { .width = 352, @@ -130,7 +152,8 @@ int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) /* At the moment, sensor detection is based on USB pid/vid */ if (tas5110c1b.usbdev->descriptor.idProduct != 0x6001 && - tas5110c1b.usbdev->descriptor.idProduct != 0x6005) + tas5110c1b.usbdev->descriptor.idProduct != 0x6005 && + tas5110c1b.usbdev->descriptor.idProduct != 0x60ab) return -ENODEV; return 0; diff --git a/drivers/usb/media/sn9c102_tas5130d1b.c b/drivers/usb/media/sn9c102_tas5130d1b.c index 0bab19435399..0048e9c20d80 100644 --- a/drivers/usb/media/sn9c102_tas5130d1b.c +++ b/drivers/usb/media/sn9c102_tas5130d1b.c @@ -1,6 +1,6 @@ /*************************************************************************** - * Driver for TAS5130D1B image sensor connected to the SN9C10[12] PC * - * Camera Controllers * + * Driver for TAS5130D1B image sensor connected to the SN9C10x PC Camera * + * Controllers * * * * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * * @@ -24,6 +24,8 @@ static struct sn9c102_sensor tas5130d1b; +static struct v4l2_control tas5130d1b_gain, tas5130d1b_exposure; + static int tas5130d1b_init(struct sn9c102_device* cam) { @@ -38,25 +40,47 @@ static int tas5130d1b_init(struct sn9c102_device* cam) err += sn9c102_write_reg(cam, 0x60, 0x17); err += sn9c102_write_reg(cam, 0x07, 0x18); - err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x40, - 0x47, 0, 0); - return err; } +static int tas5130d1b_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl->value = tas5130d1b_gain.value; + break; + case V4L2_CID_EXPOSURE: + ctrl->value = tas5130d1b_exposure.value; + break; + default: + return -EINVAL; + } + + return 0; +} + + static int tas5130d1b_set_ctrl(struct sn9c102_device* cam, const struct v4l2_control* ctrl) { + int err = 0; + switch (ctrl->id) { case V4L2_CID_GAIN: - return sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, - 0x02, 0x20, - 0xff - (ctrl->value & 0xff), - 0, 0); + if (!(err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value))) + tas5130d1b_gain.value = ctrl->value; + break; + case V4L2_CID_EXPOSURE: + if (!(err += sn9c102_i2c_write(cam, 0x40, 0x47 - ctrl->value))) + tas5130d1b_exposure.value = ctrl->value; + break; default: return -EINVAL; } + + return err ? -EIO : 0; } @@ -85,6 +109,8 @@ static struct sn9c102_sensor tas5130d1b = { .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, + .slave_read_id = SN9C102_I2C_SLAVEID_UNAVAILABLE, + .slave_write_id = SN9C102_I2C_SLAVEID_FICTITIOUS, .init = &tas5130d1b_init, .qctrl = { { @@ -92,12 +118,23 @@ static struct sn9c102_sensor tas5130d1b = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "global gain", .minimum = 0x00, - .maximum = 0xff, + .maximum = 0xf6, + .step = 0x02, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x47, .step = 0x01, .default_value = 0x00, .flags = 0, }, }, + .get_ctrl = &tas5130d1b_get_ctrl, .set_ctrl = &tas5130d1b_set_ctrl, .cropcap = { .bounds = { @@ -129,7 +166,8 @@ int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) sn9c102_attach_sensor(cam, &tas5130d1b); /* At the moment, sensor detection is based on USB pid/vid */ - if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025) + if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025 && + tas5130d1b.usbdev->descriptor.idProduct != 0x60aa) return -ENODEV; return 0; diff --git a/drivers/usb/media/stv680.c b/drivers/usb/media/stv680.c index cd73a45d0dd0..a91870f0b412 100644 --- a/drivers/usb/media/stv680.c +++ b/drivers/usb/media/stv680.c @@ -725,7 +725,7 @@ static int stv680_stop_stream (struct usb_stv *stv680) for (i = 0; i < STV680_NUMSBUF; i++) if (stv680->urb[i]) { - usb_unlink_urb (stv680->urb[i]); + usb_kill_urb (stv680->urb[i]); usb_free_urb (stv680->urb[i]); stv680->urb[i] = NULL; kfree (stv680->sbuf[i].data); @@ -1457,7 +1457,7 @@ static inline void usb_stv680_remove_disconnected (struct usb_stv *stv680) for (i = 0; i < STV680_NUMSBUF; i++) if (stv680->urb[i]) { - usb_unlink_urb (stv680->urb[i]); + usb_kill_urb (stv680->urb[i]); usb_free_urb (stv680->urb[i]); stv680->urb[i] = NULL; kfree (stv680->sbuf[i].data); diff --git a/drivers/usb/media/usbvideo.c b/drivers/usb/media/usbvideo.c index 1107e398d6f3..122b7accf1e1 100644 --- a/drivers/usb/media/usbvideo.c +++ b/drivers/usb/media/usbvideo.c @@ -1910,9 +1910,7 @@ static void usbvideo_StopDataPump(struct uvd *uvd) /* Unschedule all of the iso td's */ for (i=0; i < USBVIDEO_NUMSBUF; i++) { - j = usb_unlink_urb(uvd->sbuf[i].urb); - if (j < 0) - err("%s: usb_unlink_urb() error %d.", __FUNCTION__, j); + usb_kill_urb(uvd->sbuf[i].urb); } if (uvd->debug > 1) info("%s: streaming=0", __FUNCTION__); diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 8c958af4a1d3..d6401c0a80d5 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -121,18 +121,6 @@ config USB_CYTHERM To compile this driver as a module, choose M here: the module will be called cytherm. -config USB_SPEEDTOUCH - tristate "Alcatel Speedtouch USB support" - depends on USB && ATM - select CRC32 - help - Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330 - modem. In order to use your modem you will need to install some 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_PHIDGETSERVO tristate "USB PhidgetServo support" depends on USB diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index d641a470e8d7..074a1d66250b 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_USB_LCD) += usblcd.o obj-$(CONFIG_USB_LED) += usbled.o obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o obj-$(CONFIG_USB_RIO500) += rio500.o -obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o obj-$(CONFIG_USB_TEST) += usbtest.o obj-$(CONFIG_USB_TIGL) += tiglusb.o obj-$(CONFIG_USB_USS720) += uss720.o diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c index c4bebdacda5c..94842c34a4c4 100644 --- a/drivers/usb/misc/auerswald.c +++ b/drivers/usb/misc/auerswald.c @@ -516,7 +516,7 @@ static void auerchain_unlink_all (pauerchain_t acp) urbp = acep->urbp; urbp->transfer_flags &= ~URB_ASYNC_UNLINK; dbg ("unlink active urb"); - usb_unlink_urb (urbp); + usb_kill_urb (urbp); } } @@ -1171,22 +1171,16 @@ intoend: endpoint. This function returns 0 if successful or an error code. NOTE: no mutex please! */ -static int auerswald_int_release (pauerswald_t cp) +static void auerswald_int_release (pauerswald_t cp) { - int ret = 0; dbg ("auerswald_int_release"); /* stop the int endpoint */ - if (cp->inturbp) { - ret = usb_unlink_urb (cp->inturbp); - if (ret) - dbg ("nonzero int unlink result received: %d", ret); - } + if (cp->inturbp) + usb_kill_urb (cp->inturbp); /* deallocate memory */ auerswald_int_free (cp); - - return ret; } /* --------------------------------------------------------------------- */ diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 06c4bfa2f4b9..e83e91148970 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -505,12 +505,12 @@ static void tower_abort_transfers (struct lego_usb_tower *dev) dev->interrupt_in_running = 0; mb(); if (dev->interrupt_in_urb != NULL && dev->udev) { - usb_unlink_urb (dev->interrupt_in_urb); + usb_kill_urb (dev->interrupt_in_urb); } } if (dev->interrupt_out_busy) { if (dev->interrupt_out_urb != NULL && dev->udev) { - usb_unlink_urb (dev->interrupt_out_urb); + usb_kill_urb (dev->interrupt_out_urb); } } diff --git a/drivers/usb/misc/speedtch.c b/drivers/usb/misc/speedtch.c deleted file mode 100644 index 667e2d1b227c..000000000000 --- a/drivers/usb/misc/speedtch.c +++ /dev/null @@ -1,1373 +0,0 @@ -/****************************************************************************** - * speedtouch.c - Alcatel SpeedTouch USB xDSL modem driver - * - * Copyright (C) 2001, Alcatel - * Copyright (C) 2003, Duncan Sands - * - * 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 <asm/semaphore.h> -#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/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> - -/* -#define DEBUG -#define VERBOSE_DEBUG -*/ - -#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) -# define DEBUG -#endif - -#include <linux/usb.h> - -#ifdef DEBUG -#define DEBUG_ON(x) BUG_ON(x) -#else -#define DEBUG_ON(x) do { if (x); } while (0) -#endif - -#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 "Alcatel SpeedTouch USB driver version " DRIVER_VERSION - -static const char udsl_driver_name [] = "speedtch"; - -#define SPEEDTOUCH_VENDORID 0x06b9 -#define SPEEDTOUCH_PRODUCTID 0x4061 - -#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 */ - -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) ")"); - -#define UDSL_IOCTL_LINE_UP 1 -#define UDSL_IOCTL_LINE_DOWN 2 - -#define UDSL_ENDPOINT_DATA_OUT 0x07 -#define UDSL_ENDPOINT_DATA_IN 0x87 - -#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) - -#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) ) - -static struct usb_device_id udsl_usb_ids [] = { - { USB_DEVICE (SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID) }, - { } -}; - -MODULE_DEVICE_TABLE (usb, udsl_usb_ids); - -/* 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 cell_header [ATM_CELL_HEADER]; - unsigned char aal5_trailer [ATM_AAL5_TRAILER]; -}; - -#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb) - -/* main driver data */ - -struct udsl_instance_data { - struct semaphore serialize; - - /* USB device part */ - struct usb_device *usb_dev; - char description [64]; - int firmware_loaded; - - /* ATM device part */ - struct atm_dev *atm_dev; - struct list_head vcc_list; - - /* 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; -}; - -/* 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, -}; - -/* USB */ - -static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_id *id); -static void udsl_usb_disconnect (struct usb_interface *intf); -static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data); - -static struct usb_driver udsl_usb_driver = { - .owner = THIS_MODULE, - .name = udsl_driver_name, - .probe = udsl_usb_probe, - .disconnect = udsl_usb_disconnect, - .ioctl = udsl_usb_ioctl, - .id_table = udsl_usb_ids, -}; - - -/*********** -** 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) { - 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 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->cell_header [0] = vcc->vpi >> 4; - ctrl->cell_header [1] = (vcc->vpi << 4) | (vcc->vci >> 12); - ctrl->cell_header [2] = vcc->vci >> 4; - ctrl->cell_header [3] = vcc->vci << 4; - ctrl->cell_header [4] = 0xec; - - 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 (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++) { - memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); - target += ATM_CELL_HEADER; - memcpy (target, skb->data, ATM_CELL_PAYLOAD); - target += ATM_CELL_PAYLOAD; - __skb_pull (skb, ATM_CELL_PAYLOAD); - } - - ctrl->num_entire -= ne; - - if (!(ctrl->num_cells -= ne) || !(howmany -= ne)) - goto out; - - memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); - 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; - } - - memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); - target += ATM_CELL_HEADER; - memset (target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); - target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; - - DEBUG_ON (--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; - -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; - - 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); - - DEBUG_ON (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, UDSL_ENDPOINT_DATA_IN), - buf->base, - rcv_buf_size * ATM_CELL_SIZE, - 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, UDSL_ENDPOINT_DATA_OUT), - buf->base, - (snd_buf_size - buf->free_cells) * ATM_CELL_SIZE, - 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))) - 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 (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 || !instance->usb_dev) { - 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; -} - - -/********** -** ATM ** -**********/ - -static void udsl_atm_dev_close (struct atm_dev *dev) -{ - struct udsl_instance_data *instance = dev->dev_data; - - if (!instance) { - dbg ("udsl_atm_dev_close: NULL instance!"); - return; - } - - dbg ("udsl_atm_dev_close: queue has %u elements", instance->sndqueue.qlen); - - tasklet_kill (&instance->receive_tasklet); - tasklet_kill (&instance->send_tasklet); - kfree (instance); - dev->dev_data = NULL; -} - -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) { - if (!instance->firmware_loaded) - strcat (page, ", no firmware\n"); - else - strcat (page, ", firmware loaded\n"); - } else - strcat (page, ", disconnected\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; - - dbg ("udsl_atm_open: vpi %hd, vci %d", vpi, vci); - - if (!instance || !instance->usb_dev) { - 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_loaded) { - dbg ("udsl_atm_open: firmware not loaded!"); - return -EAGAIN; - } - - 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 ** -**********/ - -static int udsl_set_alternate (struct udsl_instance_data *instance) -{ - down (&instance->serialize); /* vs self */ - if (!instance->firmware_loaded) { - int ret; - - if ((ret = usb_set_interface (instance->usb_dev, 1, 1)) < 0) { - dbg ("udsl_set_alternate: usb_set_interface returned %d!", ret); - up (&instance->serialize); - return ret; - } - instance->firmware_loaded = 1; - } - up (&instance->serialize); - - tasklet_schedule (&instance->receive_tasklet); - - return 0; -} - -static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data) -{ - struct udsl_instance_data *instance = usb_get_intfdata (intf); - - dbg ("udsl_usb_ioctl entered"); - - if (!instance) { - dbg ("udsl_usb_ioctl: NULL instance!"); - return -ENODEV; - } - - switch (code) { - case UDSL_IOCTL_LINE_UP: - instance->atm_dev->signal = ATM_PHY_SIG_FOUND; - return udsl_set_alternate (instance); - case UDSL_IOCTL_LINE_DOWN: - instance->atm_dev->signal = ATM_PHY_SIG_LOST; - return 0; - default: - return -ENOTTY; - } -} - -static int udsl_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 udsl_instance_data *instance; - unsigned char mac_str [13]; - int i, length; - char *buf; - - dbg ("udsl_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d", - dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); - - if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) || - (dev->descriptor.idVendor != SPEEDTOUCH_VENDORID) || - (dev->descriptor.idProduct != SPEEDTOUCH_PRODUCTID) || (ifnum != 1)) - return -ENODEV; - - dbg ("udsl_usb_probe: device accepted"); - - /* instance init */ - if (!(instance = kmalloc (sizeof (struct udsl_instance_data), GFP_KERNEL))) { - dbg ("udsl_usb_probe: no memory for instance data!"); - return -ENOMEM; - } - - memset (instance, 0, sizeof (struct udsl_instance_data)); - - init_MUTEX (&instance->serialize); - - instance->usb_dev = dev; - - INIT_LIST_HEAD (&instance->vcc_list); - - 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]); - - if (!(buf->base = kmalloc (rcv_buf_size * ATM_CELL_SIZE, GFP_KERNEL))) { - 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]); - - if (!(buf->base = kmalloc (snd_buf_size * ATM_CELL_SIZE, GFP_KERNEL))) { - dbg ("udsl_usb_probe: no memory for send buffer %d!", i); - goto fail; - } - - list_add (&buf->list, &instance->spare_send_buffers); - } - - /* ATM init */ - if (!(instance->atm_dev = atm_dev_register (udsl_driver_name, &udsl_atm_devops, -1, NULL))) { - 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; - - /* set MAC address, it is stored in the serial number */ - memset (instance->atm_dev->esi, 0, sizeof (instance->atm_dev->esi)); - if (usb_string (dev, dev->descriptor.iSerialNumber, mac_str, sizeof (mac_str)) == 12) - for (i = 0; i < 6; i++) - instance->atm_dev->esi [i] = (hex2int (mac_str [i * 2]) * 16) + (hex2int (mac_str [i * 2 + 1])); - - /* 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_set_intfdata (intf, instance); - - 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); - - kfree (instance); - - return -ENOMEM; -} - -static void udsl_usb_disconnect (struct usb_interface *intf) -{ - struct udsl_instance_data *instance = usb_get_intfdata (intf); - struct list_head *pos; - unsigned int count; - int result, i; - - dbg ("udsl_usb_disconnect entered"); - - usb_set_intfdata (intf, NULL); - - if (!instance) { - dbg ("udsl_usb_disconnect: NULL instance!"); - return; - } - - /* receive finalize */ - tasklet_disable (&instance->receive_tasklet); - - for (i = 0; i < num_rcv_urbs; i++) - if ((result = usb_unlink_urb (instance->receivers [i].urb)) < 0) - dbg ("udsl_usb_disconnect: usb_unlink_urb on receive urb %d returned %d!", i, result); - - /* wait for completion handlers to finish */ - do { - count = 0; - spin_lock_irq (&instance->receive_lock); - list_for_each (pos, &instance->spare_receivers) - DEBUG_ON (++count > num_rcv_urbs); - spin_unlock_irq (&instance->receive_lock); - - dbg ("udsl_usb_disconnect: found %u spare receivers", count); - - if (count == num_rcv_urbs) - break; - - set_current_state (TASK_RUNNING); - schedule (); - } while (1); - - /* 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++) - if ((result = usb_unlink_urb (instance->senders [i].urb)) < 0) - dbg ("udsl_usb_disconnect: usb_unlink_urb on send urb %d returned %d!", i, result); - - /* wait for completion handlers to finish */ - do { - count = 0; - spin_lock_irq (&instance->send_lock); - list_for_each (pos, &instance->spare_senders) - DEBUG_ON (++count > num_snd_urbs); - spin_unlock_irq (&instance->send_lock); - - dbg ("udsl_usb_disconnect: found %u spare senders", count); - - if (count == num_snd_urbs) - break; - - set_current_state (TASK_RUNNING); - schedule (); - } while (1); - - /* 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); - - wmb (); - instance->usb_dev = NULL; - - /* ATM finalize */ - shutdown_atm_dev (instance->atm_dev); /* frees instance, kills tasklets */ -} - - -/*********** -** 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 usb_register (&udsl_usb_driver); -} - -static void __exit udsl_usb_cleanup (void) -{ - dbg ("udsl_usb_cleanup entered"); - - usb_deregister (&udsl_usb_driver); -} - -module_init (udsl_usb_init); -module_exit (udsl_usb_cleanup); - -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/misc/tiglusb.c b/drivers/usb/misc/tiglusb.c index 0f9c5753772d..f902884a7bbb 100644 --- a/drivers/usb/misc/tiglusb.c +++ b/drivers/usb/misc/tiglusb.c @@ -115,6 +115,7 @@ tiglusb_open (struct inode *inode, struct file *filp) return -EBUSY; } + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout (HZ / 2); if (signal_pending (current)) { diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index 0bc5ccc244ba..8b22320d5ea6 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -44,6 +44,7 @@ #include <linux/parport.h> #include <linux/init.h> #include <linux/usb.h> +#include <linux/delay.h> /* * Version Information @@ -159,8 +160,7 @@ static int change_mode(struct parport *pp, int m) if (time_after_eq (jiffies, expire)) /* The FIFO is stuck. */ return -EBUSY; - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout((HZ + 99) / 100); + msleep_interruptible(10); if (signal_pending (current)) break; } diff --git a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c index 455fe6e3be06..66d0a70a8210 100644 --- a/drivers/usb/net/catc.c +++ b/drivers/usb/net/catc.c @@ -765,10 +765,10 @@ static int catc_stop(struct net_device *netdev) if (!catc->is_f5u011) del_timer_sync(&catc->timer); - usb_unlink_urb(catc->rx_urb); - usb_unlink_urb(catc->tx_urb); - usb_unlink_urb(catc->irq_urb); - usb_unlink_urb(catc->ctrl_urb); + usb_kill_urb(catc->rx_urb); + usb_kill_urb(catc->tx_urb); + usb_kill_urb(catc->irq_urb); + usb_kill_urb(catc->ctrl_urb); return 0; } diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c index 40e921ade8c8..2092cb9cb33f 100644 --- a/drivers/usb/net/kaweth.c +++ b/drivers/usb/net/kaweth.c @@ -668,13 +668,13 @@ static int kaweth_open(struct net_device *net) INTBUFFERSIZE, int_callback, kaweth, - 8); + 250); /* overriding the descriptor */ kaweth->irq_urb->transfer_dma = kaweth->intbufferhandle; kaweth->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; res = usb_submit_urb(kaweth->irq_urb, GFP_KERNEL); if (res) { - usb_unlink_urb(kaweth->rx_urb); + usb_kill_urb(kaweth->rx_urb); return -EIO; } @@ -695,15 +695,15 @@ static int kaweth_close(struct net_device *net) kaweth->status |= KAWETH_STATUS_CLOSING; - usb_unlink_urb(kaweth->irq_urb); - usb_unlink_urb(kaweth->rx_urb); + usb_kill_urb(kaweth->irq_urb); + usb_kill_urb(kaweth->rx_urb); flush_scheduled_work(); /* a scheduled work may have resubmitted, we hit them again */ - usb_unlink_urb(kaweth->irq_urb); - usb_unlink_urb(kaweth->rx_urb); + usb_kill_urb(kaweth->irq_urb); + usb_kill_urb(kaweth->rx_urb); kaweth->status &= ~KAWETH_STATUS_CLOSING; @@ -1173,8 +1173,8 @@ static void kaweth_disconnect(struct usb_interface *intf) } kaweth->removed = 1; - usb_unlink_urb(kaweth->irq_urb); - usb_unlink_urb(kaweth->rx_urb); + usb_kill_urb(kaweth->irq_urb); + usb_kill_urb(kaweth->rx_urb); /* we need to wait for the urb to be cancelled, if it is active */ spin_lock(&kaweth->device_lock); @@ -1250,19 +1250,17 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) return status; } - set_current_state(TASK_UNINTERRUPTIBLE); while (timeout && !awd.done) { - timeout = schedule_timeout(timeout); set_current_state(TASK_UNINTERRUPTIBLE); + timeout = schedule_timeout(timeout); } - set_current_state(TASK_RUNNING); remove_wait_queue(&awd.wqh, &wait); if (!timeout) { // timeout kaweth_warn("usb_control/bulk_msg: timeout"); - usb_unlink_urb(urb); // remove urb safely + usb_kill_urb(urb); // remove urb safely status = -ETIMEDOUT; } else { diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c index c515916e37a7..98cb5ae3f6f1 100644 --- a/drivers/usb/net/pegasus.c +++ b/drivers/usb/net/pegasus.c @@ -854,10 +854,10 @@ static void free_all_urbs(pegasus_t * pegasus) static void unlink_all_urbs(pegasus_t * pegasus) { - usb_unlink_urb(pegasus->intr_urb); - usb_unlink_urb(pegasus->tx_urb); - usb_unlink_urb(pegasus->rx_urb); - usb_unlink_urb(pegasus->ctrl_urb); + usb_kill_urb(pegasus->intr_urb); + usb_kill_urb(pegasus->tx_urb); + usb_kill_urb(pegasus->rx_urb); + usb_kill_urb(pegasus->ctrl_urb); } static int alloc_urbs(pegasus_t * pegasus) @@ -920,8 +920,8 @@ static int pegasus_open(struct net_device *net) if ((res = enable_net_traffic(net, pegasus->usb))) { err("can't enable_net_traffic() - %d", res); res = -EIO; - usb_unlink_urb(pegasus->rx_urb); - usb_unlink_urb(pegasus->intr_urb); + usb_kill_urb(pegasus->rx_urb); + usb_kill_urb(pegasus->intr_urb); free_skb_pool(pegasus); goto exit; } diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c index 640aa5b68095..5cd2c9c5987c 100644 --- a/drivers/usb/net/rtl8150.c +++ b/drivers/usb/net/rtl8150.c @@ -20,7 +20,7 @@ #include <asm/uaccess.h> /* Version Information */ -#define DRIVER_VERSION "v0.6.1 (2004/03/13)" +#define DRIVER_VERSION "v0.6.2 (2004/08/27)" #define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>" #define DRIVER_DESC "rtl8150 based usb-ethernet driver" @@ -392,10 +392,10 @@ static void free_all_urbs(rtl8150_t * dev) static void unlink_all_urbs(rtl8150_t * dev) { - usb_unlink_urb(dev->rx_urb); - usb_unlink_urb(dev->tx_urb); - usb_unlink_urb(dev->intr_urb); - usb_unlink_urb(dev->ctrl_urb); + usb_kill_urb(dev->rx_urb); + usb_kill_urb(dev->tx_urb); + usb_kill_urb(dev->intr_urb); + usb_kill_urb(dev->ctrl_urb); } static inline struct sk_buff *pull_skb(rtl8150_t *dev) diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c index cca6e72cf2fa..ad65e741fa44 100644 --- a/drivers/usb/net/usbnet.c +++ b/drivers/usb/net/usbnet.c @@ -825,7 +825,7 @@ static void ax8817x_unbind(struct usbnet *dev, struct usb_interface *intf) { struct ax8817x_data *data = (struct ax8817x_data *)dev->data; - usb_unlink_urb(data->int_urb); + usb_kill_urb(data->int_urb); usb_free_urb(data->int_urb); kfree(data->int_buf); } @@ -1437,7 +1437,7 @@ static int genelink_free (struct usbnet *dev) // handling needs to be generic) // cancel irq urb first - usb_unlink_urb (priv->irq_urb); + usb_kill_urb (priv->irq_urb); // free irq urb usb_free_urb (priv->irq_urb); @@ -3252,6 +3252,10 @@ static const struct usb_device_id products [] = { // Sitecom LN-029 "USB 2.0 10/100 Ethernet adapter" USB_DEVICE (0x6189, 0x182d), .driver_info = (unsigned long) &ax8817x_info, +}, { + // Surecom EP-1427X-2 + USB_DEVICE (0x1189, 0x0893), + .driver_info = (unsigned long) &ax8817x_info, }, #endif @@ -3308,11 +3312,18 @@ static const struct usb_device_id products [] = { * * PXA25x or PXA210 ... these use a "usb-eth" driver much like * the sa1100 one, but hardware uses different endpoint numbers. + * + * Or the Linux "Ethernet" gadget on hardware that can't talk + * CDC Ethernet (e.g., no altsettings), in either of two modes: + * - acting just like the old "usb-eth" firmware, though + * the implementation is different + * - supporting RNDIS as the first/default configuration for + * MS-Windows interop; Linux needs to use the other config */ { // 1183 = 0x049F, both used as hex values? // Compaq "Itsy" vendor/product id - USB_DEVICE (0x049F, 0x505A), + USB_DEVICE (0x049F, 0x505A), // usb-eth, or compatible .driver_info = (unsigned long) &linuxdev_info, }, { USB_DEVICE (0x0E7E, 0x1001), // G.Mate "Yopy" @@ -3320,6 +3331,10 @@ static const struct usb_device_id products [] = { }, { USB_DEVICE (0x8086, 0x07d3), // "blob" bootloader .driver_info = (unsigned long) &blob_info, +}, { + // Linux Ethernet/RNDIS gadget on pxa210/25x/26x + USB_DEVICE_VER (0x0525, 0xa4a2, 0x0203, 0x0203), + .driver_info = (unsigned long) &linuxdev_info, }, #endif diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 88fede7b447e..1499bf2fcd13 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -187,6 +187,16 @@ config USB_SERIAL_EDGEPORT_TI To compile this driver as a module, choose M here: the module will be called io_ti. +config USB_SERIAL_IPW + tristate "USB IPWireless (3G UMTS TDD) Driver (EXPERIMENTAL)" + depends on USB_SERIAL && EXPERIMENTAL + help + Say Y here if you want to use a IPWireless USB modem such as + the ones supplied by Axity3G/Sentech South Africa. + + To compile this driver as a module, choose M here: the + module will be called ipw. + config USB_SERIAL_KEYSPAN_PDA tristate "USB Keyspan PDA Single Port Serial Driver" depends on USB_SERIAL diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 6cfbe3108ccb..dfd43ec80ff3 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_USB_SERIAL_EDGEPORT_TI) += io_ti.o obj-$(CONFIG_USB_SERIAL_EMPEG) += empeg.o obj-$(CONFIG_USB_SERIAL_FTDI_SIO) += ftdi_sio.o obj-$(CONFIG_USB_SERIAL_IPAQ) += ipaq.o +obj-$(CONFIG_USB_SERIAL_IPW) += ipw.o obj-$(CONFIG_USB_SERIAL_IR) += ir-usb.o obj-$(CONFIG_USB_SERIAL_KEYSPAN) += keyspan.o obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index bcf56011ec22..a44cb9d96113 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -228,7 +228,7 @@ static int belkin_sa_open (struct usb_serial_port *port, struct file *filp) port->interrupt_in_urb->dev = port->serial->dev; retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (retval) { - usb_unlink_urb(port->read_urb); + usb_kill_urb(port->read_urb); err(" usb_submit_urb(read int) failed"); } @@ -242,9 +242,9 @@ static void belkin_sa_close (struct usb_serial_port *port, struct file *filp) dbg("%s port %d", __FUNCTION__, port->number); /* shutdown our bulk reads and writes */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - usb_unlink_urb (port->interrupt_in_urb); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); + usb_kill_urb(port->interrupt_in_urb); } /* belkin_sa_close */ diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 33613b008557..6b5ec2af32ad 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -149,7 +149,7 @@ static void cyberjack_shutdown (struct usb_serial *serial) dbg("%s", __FUNCTION__); for (i=0; i < serial->num_ports; ++i) { - usb_unlink_urb (serial->port[i]->interrupt_in_urb); + usb_kill_urb(serial->port[i]->interrupt_in_urb); /* My special items, the standard routines free my urbs */ kfree(usb_get_serial_port_data(serial->port[i])); usb_set_serial_port_data(serial->port[i], NULL); @@ -189,8 +189,8 @@ static void cyberjack_close (struct usb_serial_port *port, struct file *filp) if (port->serial->dev) { /* shutdown any bulk reads that might be going on */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); } } diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 3ac69f2c2510..b904bfcbffcf 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1615,7 +1615,7 @@ dbg( "digi_close: TOP: port=%d, open_count=%d", priv->dp_port_num, port->open_co DIGI_CLOSE_TIMEOUT ); /* shutdown any outstanding bulk writes */ - usb_unlink_urb (port->write_urb); + usb_kill_urb(port->write_urb); } tty->closing = 0; @@ -1754,8 +1754,8 @@ dbg( "digi_shutdown: TOP, in_interrupt()=%ld", in_interrupt() ); /* stop reads and writes on all ports */ for( i=0; i<serial->type->num_ports+1; i++ ) { - usb_unlink_urb( serial->port[i]->read_urb ); - usb_unlink_urb( serial->port[i]->write_urb ); + usb_kill_urb(serial->port[i]->read_urb); + usb_kill_urb(serial->port[i]->write_urb); } /* free the private data structures for all ports */ diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 251a50fdab86..e94113e0dd43 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -185,7 +185,7 @@ static void empeg_close (struct usb_serial_port *port, struct file * filp) dbg("%s - port %d", __FUNCTION__, port->number); /* shutdown our bulk read */ - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); /* Uncomment the following line if you want to see some statistics in your syslog */ /* dev_info (&port->dev, "Bytes In = %d Bytes Out = %d\n", bytes_in, bytes_out); */ } @@ -406,7 +406,7 @@ static void empeg_read_bulk_callback (struct urb *urb, struct pt_regs *regs) static void empeg_throttle (struct usb_serial_port *port) { dbg("%s - port %d", __FUNCTION__, port->number); - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); } @@ -579,10 +579,10 @@ static void __exit empeg_exit (void) for (i = 0; i < NUM_URBS; ++i) { if (write_urb_pool[i]) { - /* FIXME - uncomment the following usb_unlink_urb call when + /* FIXME - uncomment the following usb_kill_urb call when * the host controllers get fixed to set urb->dev = NULL after * the urb is finished. Otherwise this call oopses. */ - /* usb_unlink_urb(write_urb_pool[i]); */ + /* usb_kill_urb(write_urb_pool[i]); */ if (write_urb_pool[i]->transfer_buffer) kfree(write_urb_pool[i]->transfer_buffer); usb_free_urb (write_urb_pool[i]); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 9073ea84d8d3..b1a985470e42 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -368,6 +368,10 @@ static struct usb_device_id id_table_8U232AM [] = { { USB_DEVICE_VER(INTREPID_VID, INTREPID_NEOVI_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FALCOM_VID, FALCOM_TWIST_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_SUUNTO_SPORTS_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_RM_VID, FTDI_RMCANVIEW_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(BANDB_VID, BANDB_USOTL4_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(BANDB_VID, BANDB_USTL4_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(BANDB_VID, BANDB_USO9ML2_PID, 0, 0x3ff) }, { } /* Terminating entry */ }; @@ -478,6 +482,10 @@ static struct usb_device_id id_table_FT232BM [] = { { USB_DEVICE_VER(INTREPID_VID, INTREPID_NEOVI_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FALCOM_VID, FALCOM_TWIST_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_SUUNTO_SPORTS_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_RM_VID, FTDI_RMCANVIEW_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(BANDB_VID, BANDB_USOTL4_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(BANDB_VID, BANDB_USTL4_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(BANDB_VID, BANDB_USO9ML2_PID, 0x400, 0xffff) }, { } /* Terminating entry */ }; @@ -595,6 +603,10 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) }, { USB_DEVICE(FALCOM_VID, FALCOM_TWIST_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SUUNTO_SPORTS_PID) }, + { USB_DEVICE(FTDI_RM_VID, FTDI_RMCANVIEW_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) }, { } /* Terminating entry */ }; @@ -1479,16 +1491,8 @@ static void ftdi_close (struct usb_serial_port *port, struct file *filp) } /* Note change no line if hupcl is off */ /* shutdown our bulk read */ - if (port->read_urb) { - if (usb_unlink_urb (port->read_urb) < 0) { - /* Generally, this isn't an error. If the previous - read bulk callback occurred (or is about to occur) - while the port was being closed or was throtted - (and is still throttled), the read urb will not - have been submitted. */ - dbg("%s - failed to unlink read urb (generally not an error)", __FUNCTION__); - } - } + if (port->read_urb) + usb_kill_urb(port->read_urb); } /* ftdi_close */ diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 232213b02860..e7a7e0aba4f2 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -225,6 +225,21 @@ */ #define FTDI_SUUNTO_SPORTS_PID 0xF680 /* Suunto Sports instrument */ +/* + * Definitions for B&B Electronics products. + */ +#define BANDB_VID 0x0856 /* B&B Electronics Vendor ID */ +#define BANDB_USOTL4_PID 0xAC01 /* USOTL4 Isolated RS-485 Converter */ +#define BANDB_USTL4_PID 0xAC02 /* USTL4 RS-485 Converter */ +#define BANDB_USO9ML2_PID 0xAC03 /* USO9ML2 Isolated RS-232 Converter */ + +/* + * RM Michaelides CANview USB (http://www.rmcan.com) + * CAN filedbus interface adapter, addad by port GmbH www.port.de) + */ +#define FTDI_RM_VID 0x0403 /* Vendor Id */ +#define FTDI_RMCANVIEW_PID 0xfd60 /* Product Id */ + /* Commands */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 196fea084f21..c88885f8ca05 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -147,9 +147,9 @@ static void generic_cleanup (struct usb_serial_port *port) if (serial->dev) { /* shutdown any bulk reads that might be going on */ if (serial->num_bulk_out) - usb_unlink_urb (port->write_urb); + usb_kill_urb(port->write_urb); if (serial->num_bulk_in) - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); } } diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 1ef47613b958..b5d18c699a0e 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -1238,7 +1238,7 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) edge_port->openPending = FALSE; if (edge_port->write_urb) { - usb_unlink_urb (edge_port->write_urb); + usb_kill_urb(edge_port->write_urb); } if (edge_port->write_urb) { @@ -2443,8 +2443,8 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer if (status) { /* something went wrong */ dbg("%s - usb_submit_urb(write bulk) failed", __FUNCTION__); - usb_unlink_urb (urb); - usb_free_urb (urb); + usb_kill_urb(urb); + usb_free_urb(urb); return status; } diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index ace42c870727..359d3c0b8a15 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1972,7 +1972,7 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) /* chase the port close */ TIChasePort (edge_port); - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); /* assuming we can still talk to the device, * send a close port command to it */ @@ -1987,7 +1987,7 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) --edge_port->edge_serial->num_ports_open; if (edge_port->edge_serial->num_ports_open <= 0) { /* last port is now closed, let's shut down our interrupt urb */ - usb_unlink_urb (port->serial->port[0]->interrupt_in_urb); + usb_kill_urb(port->serial->port[0]->interrupt_in_urb); edge_port->edge_serial->num_ports_open = 0; } edge_port->close_pending = 0; @@ -2121,7 +2121,7 @@ static void edge_throttle (struct usb_serial_port *port) status = TIClearRts (edge_port); } - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); } static void edge_unthrottle (struct usb_serial_port *port) diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 8fbce2732c51..1a748b515112 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -288,8 +288,8 @@ static void ipaq_close(struct usb_serial_port *port, struct file *filp) /* * shut down bulk read and write */ - usb_unlink_urb(port->write_urb); - usb_unlink_urb(port->read_urb); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); ipaq_destroy_lists(port); kfree(priv); usb_set_serial_port_data(port, NULL); @@ -419,9 +419,8 @@ static void ipaq_write_gather(struct usb_serial_port *port) struct ipaq_private *priv = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; int count, room; - struct ipaq_packet *pkt; + struct ipaq_packet *pkt, *tmp; struct urb *urb = port->write_urb; - struct list_head *tmp; if (urb->status == -EINPROGRESS) { /* Should never happen */ @@ -429,9 +428,7 @@ static void ipaq_write_gather(struct usb_serial_port *port) return; } room = URBDATA_SIZE; - for (tmp = priv->queue.next; tmp != &priv->queue;) { - pkt = list_entry(tmp, struct ipaq_packet, list); - tmp = tmp->next; + list_for_each_entry_safe(pkt, tmp, &priv->queue, list) { count = min(room, (int)(pkt->len - pkt->written)); memcpy(urb->transfer_buffer + (URBDATA_SIZE - room), pkt->data + pkt->written, count); @@ -503,22 +500,16 @@ static int ipaq_chars_in_buffer(struct usb_serial_port *port) static void ipaq_destroy_lists(struct usb_serial_port *port) { struct ipaq_private *priv = usb_get_serial_port_data(port); - struct list_head *tmp; - struct ipaq_packet *pkt; + struct ipaq_packet *pkt, *tmp; - for (tmp = priv->queue.next; tmp != &priv->queue;) { - pkt = list_entry(tmp, struct ipaq_packet, list); - tmp = tmp->next; + list_for_each_entry_safe(pkt, tmp, &priv->queue, list) { kfree(pkt->data); kfree(pkt); } - for (tmp = priv->freelist.next; tmp != &priv->freelist;) { - pkt = list_entry(tmp, struct ipaq_packet, list); - tmp = tmp->next; + list_for_each_entry_safe(pkt, tmp, &priv->freelist, list) { kfree(pkt->data); kfree(pkt); } - return; } diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c new file mode 100644 index 000000000000..2fc04f905a5c --- /dev/null +++ b/drivers/usb/serial/ipw.c @@ -0,0 +1,496 @@ +/* + * IPWireless 3G UMTS TDD Modem driver (USB connected) + * + * Copyright (C) 2004 Roelf Diedericks <roelfd@inet.co.za> + * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.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 + * (at your option) any later version. + * + * All information about the device was acquired using SnoopyPro + * on MSFT's O/S, and examing the MSFT drivers' debug output + * (insanely left _on_ in the enduser version) + * + * It was written out of frustration with the IPWireless USB modem + * supplied by Axity3G/Sentech South Africa not supporting + * Linux whatsoever. + * + * Nobody provided any proprietary information that was not already + * available for this device. + * + * The modem adheres to the "3GPP TS 27.007 AT command set for 3G + * User Equipment (UE)" standard, available from + * http://www.3gpp.org/ftp/Specs/html-info/27007.htm + * + * The code was only tested the IPWireless handheld modem distributed + * in South Africa by Sentech. + * + * It may work for Woosh Inc in .nz too, as it appears they use the + * same kit. + * + * There is still some work to be done in terms of handling + * DCD, DTR, RTS, CTS which are currently faked. + * It's good enough for PPP at this point. It's based off all kinds of + * code found in usb/serial and usb/class + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/usb.h> +#include <linux/usb.h> +#include <asm/uaccess.h> +#include "usb-serial.h" + +/* + * Version Information + */ +#define DRIVER_VERSION "v0.3" +#define DRIVER_AUTHOR "Roelf Diedericks" +#define DRIVER_DESC "IPWireless tty driver" + +#define IPW_TTY_MAJOR 240 /* real device node major id, experimental range */ +#define IPW_TTY_MINORS 256 /* we support 256 devices, dunno why, it'd be insane :) */ + +#define USB_IPW_MAGIC 0x6d02 /* magic number for ipw struct */ + + +/* Message sizes */ +#define EVENT_BUFFER_SIZE 0xFF +#define CHAR2INT16(c1,c0) (((u32)((c1) & 0xff) << 8) + (u32)((c0) & 0xff)) +#define NUM_BULK_URBS 24 +#define NUM_CONTROL_URBS 16 + +/* vendor/product pairs that are known work with this driver*/ +#define IPW_VID 0x0bc3 +#define IPW_PID 0x0001 + + +/* Vendor commands: */ + +/* baud rates */ +enum { + ipw_sio_b256000 = 0x000e, + ipw_sio_b128000 = 0x001d, + ipw_sio_b115200 = 0x0020, + ipw_sio_b57600 = 0x0040, + ipw_sio_b56000 = 0x0042, + ipw_sio_b38400 = 0x0060, + ipw_sio_b19200 = 0x00c0, + ipw_sio_b14400 = 0x0100, + ipw_sio_b9600 = 0x0180, + ipw_sio_b4800 = 0x0300, + ipw_sio_b2400 = 0x0600, + ipw_sio_b1200 = 0x0c00, + ipw_sio_b600 = 0x1800 +}; + +/* data bits */ +#define ipw_dtb_7 0x700 +#define ipw_dtb_8 0x810 // ok so the define is misleading, I know, but forces 8,n,1 + // I mean, is there a point to any other setting these days? :) + +/* usb control request types : */ +#define IPW_SIO_RXCTL 0x00 // control bulk rx channel transmissions, value=1/0 (on/off) +#define IPW_SIO_SET_BAUD 0x01 // set baud, value=requested ipw_sio_bxxxx +#define IPW_SIO_SET_LINE 0x03 // set databits, parity. value=ipw_dtb_x +#define IPW_SIO_SET_PIN 0x03 // set/clear dtr/rts value=ipw_pin_xxx +#define IPW_SIO_POLL 0x08 // get serial port status byte, call with value=0 +#define IPW_SIO_INIT 0x11 // initializes ? value=0 (appears as first thing todo on open) +#define IPW_SIO_PURGE 0x12 // purge all transmissions?, call with value=numchar_to_purge +#define IPW_SIO_HANDFLOW 0x13 // set xon/xoff limits value=0, and a buffer of 0x10 bytes +#define IPW_SIO_SETCHARS 0x13 // set the flowcontrol special chars, value=0, buf=6 bytes, + // last 2 bytes contain flowcontrol chars e.g. 00 00 00 00 11 13 + +/* values used for request IPW_SIO_SET_PIN */ +#define IPW_PIN_SETDTR 0x101 +#define IPW_PIN_SETRTS 0x202 +#define IPW_PIN_CLRDTR 0x100 +#define IPW_PIN_CLRRTS 0x200 // unconfirmed + +/* values used for request IPW_SIO_RXCTL */ +#define IPW_RXBULK_ON 1 +#define IPW_RXBULK_OFF 0 + +/* various 16 byte hardcoded transferbuffers used by flow control */ +#define IPW_BYTES_FLOWINIT { 0x01, 0, 0, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + +/* Interpretation of modem status lines */ +/* These need sorting out by individually connecting pins and checking + * results. FIXME! + * When data is being sent we see 0x30 in the lower byte; this must + * contain DSR and CTS ... + */ +#define IPW_DSR ((1<<4) | (1<<5)) +#define IPW_CTS ((1<<5) | (1<<4)) + +#define IPW_WANTS_TO_SEND 0x30 +//#define IPW_DTR /* Data Terminal Ready */ +//#define IPW_CTS /* Clear To Send */ +//#define IPW_CD /* Carrier Detect */ +//#define IPW_DSR /* Data Set Ready */ +//#define IPW_RxD /* Receive pin */ + +//#define IPW_LE +//#define IPW_RTS +//#define IPW_ST +//#define IPW_SR +//#define IPW_RI /* Ring Indicator */ + +static struct usb_device_id usb_ipw_ids[] = { + { USB_DEVICE(IPW_VID, IPW_PID) }, + { }, +}; + +MODULE_DEVICE_TABLE(usb, usb_ipw_ids); + +static struct usb_driver usb_ipw_driver = { + .owner = THIS_MODULE, + .name = "ipwtty", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = usb_ipw_ids, +}; + +static int debug; + +static void ipw_read_bulk_callback(struct urb *urb, struct pt_regs *regs) +{ + struct usb_serial_port *port = urb->context; + unsigned char *data = urb->transfer_buffer; + struct tty_struct *tty; + int i; + int result; + + dbg("%s - port %d", __FUNCTION__, port->number); + + if (urb->status) { + dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); + return; + } + + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); + + tty = port->tty; + if (tty && urb->actual_length) { + for (i = 0; i < urb->actual_length ; ++i) { + /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */ + if(tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty_flip_buffer_push(tty); + } + /* this doesn't actually push the data through unless tty->low_latency is set */ + tty_insert_flip_char(tty, data[i], 0); + } + tty_flip_buffer_push(tty); + } + + /* Continue trying to always read */ + usb_fill_bulk_urb (port->read_urb, port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + ipw_read_bulk_callback, port); + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); + return; +} + +static int ipw_open(struct usb_serial_port *port, struct file *filp) +{ + struct usb_device *dev = port->serial->dev; + u8 buf_flow_static[16] = IPW_BYTES_FLOWINIT; + u8 *buf_flow_init; + int result; + + dbg("%s", __FUNCTION__); + + buf_flow_init = kmalloc(16, GFP_KERNEL); + if (!buf_flow_init) + return -ENOMEM; + memcpy(buf_flow_init, buf_flow_static, 16); + + if (port->tty) + port->tty->low_latency = 1; + + /* --1: Tell the modem to initialize (we think) From sniffs this is always the + * first thing that gets sent to the modem during opening of the device */ + dbg("%s: Sending SIO_INIT (we guess)",__FUNCTION__); + result = usb_control_msg(dev, usb_sndctrlpipe(dev,0), + IPW_SIO_INIT, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + 0, + 0, /* index */ + NULL, + 0, + 100*HZ); + if (result < 0) + dev_err(&port->dev, "Init of modem failed (error = %d)", result); + + /* reset the bulk pipes */ + usb_clear_halt(dev, usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress)); + usb_clear_halt(dev, usb_sndbulkpipe(dev, port->bulk_out_endpointAddress)); + + /*--2: Start reading from the device */ + dbg("%s: setting up bulk read callback",__FUNCTION__); + usb_fill_bulk_urb(port->read_urb, dev, + usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress), + port->bulk_in_buffer, + port->bulk_in_size, + ipw_read_bulk_callback, port); + result = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (result < 0) + dbg("%s - usb_submit_urb(read bulk) failed with status %d", __FUNCTION__, result); + + /*--3: Tell the modem to open the floodgates on the rx bulk channel */ + dbg("%s:asking modem for RxRead (RXBULK_ON)",__FUNCTION__); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_RXCTL, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_RXBULK_ON, + 0, /* index */ + NULL, + 0, + 100*HZ); + if (result < 0) + dev_err(&port->dev, "Enabling bulk RxRead failed (error = %d)", result); + + /*--4: setup the initial flowcontrol */ + dbg("%s:setting init flowcontrol (%s)",__FUNCTION__,buf_flow_init); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_HANDFLOW, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + 0, + 0, + buf_flow_init, + 0x10, + 200*HZ); + if (result < 0) + dev_err(&port->dev, "initial flowcontrol failed (error = %d)", result); + + + /*--5: raise the dtr */ + dbg("%s:raising dtr",__FUNCTION__); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_SET_PIN, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_PIN_SETDTR, + 0, + NULL, + 0, + 200*HZ); + if (result < 0) + dev_err(&port->dev, "setting dtr failed (error = %d)", result); + + /*--6: raise the rts */ + dbg("%s:raising rts",__FUNCTION__); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_SET_PIN, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_PIN_SETRTS, + 0, + NULL, + 0, + 200*HZ); + if (result < 0) + dev_err(&port->dev, "setting dtr failed (error = %d)", result); + + kfree(buf_flow_init); + return 0; +} + +static void ipw_close(struct usb_serial_port *port, struct file * filp) +{ + struct usb_device *dev = port->serial->dev; + int result; + + if (tty_hung_up_p(filp)) { + dbg("%s: tty_hung_up_p ...", __FUNCTION__); + return; + } + + /*--1: drop the dtr */ + dbg("%s:dropping dtr",__FUNCTION__); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_SET_PIN, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_PIN_CLRDTR, + 0, + NULL, + 0, + 200*HZ); + if (result < 0) + dev_err(&port->dev, "dropping dtr failed (error = %d)", result); + + /*--2: drop the rts */ + dbg("%s:dropping rts",__FUNCTION__); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_SET_PIN, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_PIN_CLRRTS, + 0, + NULL, + 0, + 200*HZ); + if (result < 0) + dev_err(&port->dev, "dropping rts failed (error = %d)", result); + + + /*--3: purge */ + dbg("%s:sending purge",__FUNCTION__); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_PURGE, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + 0x03, + 0, + NULL, + 0, + 200*HZ); + if (result < 0) + dev_err(&port->dev, "purge failed (error = %d)", result); + + + /* send RXBULK_off (tell modem to stop transmitting bulk data on rx chan) */ + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_RXCTL, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_RXBULK_OFF, + 0, /* index */ + NULL, + 0, + 100*HZ); + + if (result < 0) + dev_err(&port->dev, "Disabling bulk RxRead failed (error = %d)", result); + + /* shutdown any in-flight urbs that we know about */ + usb_kill_urb(port->read_urb); + usb_kill_urb(port->write_urb); +} + +static void ipw_write_bulk_callback(struct urb *urb, struct pt_regs *regs) +{ + struct usb_serial_port *port = urb->context; + + dbg("%s", __FUNCTION__); + + if (urb->status) + dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + + schedule_work(&port->work); +} + +static int ipw_write(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) +{ + struct usb_device *dev = port->serial->dev; + int ret; + + dbg("%s: TOP: count=%d, from_user=%d, in_interrupt=%ld", __FUNCTION__, + count, from_user, in_interrupt() ); + + if (count == 0) { + dbg("%s - write request of 0 bytes", __FUNCTION__); + return 0; + } + + /* Racy and broken, FIXME properly! */ + if (port->write_urb->status == -EINPROGRESS) + return 0; + + count = min(count, port->bulk_out_size); + if (from_user) { + if (copy_from_user(port->bulk_out_buffer, buf, count)) + return -EFAULT; + } else { + 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, + count, + ipw_write_bulk_callback, + port); + + ret = usb_submit_urb(port->write_urb, GFP_ATOMIC); + if (ret != 0) { + dbg("%s - usb_submit_urb(write bulk) failed with error = %d", __FUNCTION__, ret); + return ret; + } + + dbg("%s returning %d", __FUNCTION__, count); + return count; +} + +static int ipw_probe(struct usb_serial_port *port) +{ + return 0; +} + +static int ipw_disconnect(struct usb_serial_port *port) +{ + usb_set_serial_port_data(port, NULL); + return 0; +} + +static struct usb_serial_device_type ipw_device = { + .owner = THIS_MODULE, + .name = "IPWireless converter", + .short_name = "ipw", + .id_table = usb_ipw_ids, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = ipw_open, + .close = ipw_close, + .port_probe = ipw_probe, + .port_remove = ipw_disconnect, + .write = ipw_write, + .write_bulk_callback = ipw_write_bulk_callback, + .read_bulk_callback = ipw_read_bulk_callback, +}; + + + +int usb_ipw_init(void) +{ + int retval; + + retval = usb_serial_register(&ipw_device); + if (retval) + return retval; + retval = usb_register(&usb_ipw_driver); + if (retval) { + usb_serial_deregister(&ipw_device); + return retval; + } + info(DRIVER_DESC " " DRIVER_VERSION); + return 0; +} + +void usb_ipw_exit(void) +{ + usb_deregister(&usb_ipw_driver); + usb_serial_deregister(&ipw_device); +} + +module_init(usb_ipw_init); +module_exit(usb_ipw_exit); + +/* Module information */ +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 3d1571695bde..c2c536f194e0 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -322,7 +322,7 @@ static void ir_close (struct usb_serial_port *port, struct file * filp) dbg("%s - port %d", __FUNCTION__, port->number); /* shutdown our bulk read */ - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); } static int ir_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index cd441d783cbe..0a979e208167 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -285,7 +285,7 @@ static void keyspan_pda_rx_throttle (struct usb_serial_port *port) upon the device too. */ dbg("keyspan_pda_rx_throttle port %d", port->number); - usb_unlink_urb(port->interrupt_in_urb); + usb_kill_urb(port->interrupt_in_urb); } @@ -706,8 +706,8 @@ static void keyspan_pda_close(struct usb_serial_port *port, struct file *filp) keyspan_pda_set_modem_info(serial, 0); /* shutdown our bulk reads and writes */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->interrupt_in_urb); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->interrupt_in_urb); } } diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index c5207d19401a..9e60b3476775 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -336,12 +336,12 @@ static void klsi_105_shutdown (struct usb_serial *serial) for (j = 0; j < NUM_URBS; j++) { if (write_urbs[j]) { /* FIXME - uncomment the following - * usb_unlink_urb call when the host + * usb_kill_urb call when the host * controllers get fixed to set * urb->dev = NULL after the urb is * finished. Otherwise this call * oopses. */ - /* usb_unlink_urb(write_urbs[j]); */ + /* usb_kill_urb(write_urbs[j]); */ if (write_urbs[j]->transfer_buffer) kfree(write_urbs[j]->transfer_buffer); usb_free_urb (write_urbs[j]); @@ -467,12 +467,12 @@ static void klsi_105_close (struct usb_serial_port *port, struct file *filp) err("Disabling read failed (error = %d)", rc); /* shutdown our bulk reads and writes */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); /* unlink our write pool */ /* FIXME */ /* wgg - do I need this? I think so. */ - usb_unlink_urb (port->interrupt_in_urb); + usb_kill_urb(port->interrupt_in_urb); info("kl5kusb105 port stats: %ld bytes in, %ld bytes out", priv->bytes_in, priv->bytes_out); } /* klsi_105_close */ @@ -994,7 +994,7 @@ static int klsi_105_ioctl (struct usb_serial_port *port, struct file * file, static void klsi_105_throttle (struct usb_serial_port *port) { dbg("%s - port %d", __FUNCTION__, port->number); - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); } static void klsi_105_unthrottle (struct usb_serial_port *port) diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index b479916ce269..33539874bd0c 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -350,14 +350,13 @@ static void kobil_close (struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __FUNCTION__, port->number); - if (port->write_urb){ - usb_unlink_urb( port->write_urb ); + if (port->write_urb) { + usb_kill_urb(port->write_urb); usb_free_urb( port->write_urb ); port->write_urb = NULL; } - if (port->interrupt_in_urb){ - usb_unlink_urb (port->interrupt_in_urb); - } + if (port->interrupt_in_urb) + usb_kill_urb(port->interrupt_in_urb); } @@ -458,9 +457,8 @@ static int kobil_write (struct usb_serial_port *port, int from_user, ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4))) ) { // stop reading (except TWIN and KAAN SIM) - if ( (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) ) { - usb_unlink_urb( port->interrupt_in_urb ); - } + if ( (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) ) + usb_kill_urb(port->interrupt_in_urb); todo = priv->filled - priv->cur_pos; diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 212906c74f25..1d7c682aed7a 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -480,9 +480,9 @@ static void mct_u232_close (struct usb_serial_port *port, struct file *filp) if (port->serial->dev) { /* shutdown our urbs */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - usb_unlink_urb (port->interrupt_in_urb); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); + usb_kill_urb(port->interrupt_in_urb); } } /* mct_u232_close */ diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 371aa2e8e3f1..7032d5f7c6e1 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -183,8 +183,8 @@ static void omninet_close (struct usb_serial_port *port, struct file * filp) dbg("%s - port %d", __FUNCTION__, port->number); wport = serial->port[1]; - usb_unlink_urb(wport->write_urb); - usb_unlink_urb(port->read_urb); + usb_kill_urb(wport->write_urb); + usb_kill_urb(port->read_urb); od = usb_get_serial_port_data(port); if (od) diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index c61e56829a96..5be9c370313b 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -55,11 +55,26 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.11" +#define DRIVER_VERSION "v0.12" #define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver" static int debug; +#define PL2303_CLOSING_WAIT (30*HZ) + +#define PL2303_BUF_SIZE 1024 +#define PL2303_TMP_BUF_SIZE 1024 + +static char pl2303_tmp_buf[PL2303_TMP_BUF_SIZE]; +static DECLARE_MUTEX(pl2303_tmp_buf_sem); + +struct pl2303_buf { + unsigned int buf_size; + char *buf_buf; + char *buf_get; + char *buf_put; +}; + static struct usb_device_id id_table [] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) }, @@ -134,12 +149,24 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs); static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs); static int pl2303_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); +static void pl2303_send (struct usb_serial_port *port); +static int pl2303_write_room(struct usb_serial_port *port); +static int pl2303_chars_in_buffer(struct usb_serial_port *port); static void pl2303_break_ctl(struct usb_serial_port *port,int break_state); static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file); static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); static int pl2303_startup (struct usb_serial *serial); static void pl2303_shutdown (struct usb_serial *serial); +static struct pl2303_buf *pl2303_buf_alloc(unsigned int size); +static void pl2303_buf_free(struct pl2303_buf *pb); +static void pl2303_buf_clear(struct pl2303_buf *pb); +static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb); +static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb); +static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, + unsigned int count); +static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, + unsigned int count); /* All of the device info needed for the PL2303 SIO serial converter */ @@ -162,6 +189,8 @@ static struct usb_serial_device_type pl2303_device = { .read_bulk_callback = pl2303_read_bulk_callback, .read_int_callback = pl2303_read_int_callback, .write_bulk_callback = pl2303_write_bulk_callback, + .write_room = pl2303_write_room, + .chars_in_buffer = pl2303_chars_in_buffer, .attach = pl2303_startup, .shutdown = pl2303_shutdown, }; @@ -174,6 +203,8 @@ enum pl2303_type { struct pl2303_private { spinlock_t lock; + struct pl2303_buf *buf; + int write_urb_in_use; wait_queue_head_t delta_msr_wait; u8 line_control; u8 line_status; @@ -201,14 +232,28 @@ static int pl2303_startup (struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL); if (!priv) - return -ENOMEM; + goto cleanup; memset (priv, 0x00, sizeof (struct pl2303_private)); spin_lock_init(&priv->lock); + priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE); + if (priv->buf == NULL) { + kfree(priv); + goto cleanup; + } init_waitqueue_head(&priv->delta_msr_wait); priv->type = type; usb_set_serial_port_data(serial->port[i], priv); } return 0; + +cleanup: + for (--i; i>=0; --i) { + priv = usb_get_serial_port_data(serial->port[i]); + pl2303_buf_free(priv->buf); + kfree(priv); + usb_set_serial_port_data(serial->port[i], NULL); + } + return -ENOMEM; } static int set_control_lines (struct usb_device *dev, u8 value) @@ -224,40 +269,109 @@ static int set_control_lines (struct usb_device *dev, u8 value) static int pl2303_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) { - int result; + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count); if (!count) return count; - if (port->write_urb->status == -EINPROGRESS) { - dbg("%s - already writing", __FUNCTION__); - return 0; - } - - count = (count > port->bulk_out_size) ? port->bulk_out_size : count; if (from_user) { - if (copy_from_user (port->write_urb->transfer_buffer, buf, count)) + if (count > PL2303_TMP_BUF_SIZE) + count = PL2303_TMP_BUF_SIZE; + down(&pl2303_tmp_buf_sem); + if (copy_from_user(pl2303_tmp_buf, buf, count)) { + up(&pl2303_tmp_buf_sem); return -EFAULT; - } else { - memcpy (port->write_urb->transfer_buffer, buf, count); + } + buf = pl2303_tmp_buf; } - + + spin_lock_irqsave(&priv->lock, flags); + count = pl2303_buf_put(priv->buf, buf, count); + spin_unlock_irqrestore(&priv->lock, flags); + + if (from_user) + up(&pl2303_tmp_buf_sem); + + pl2303_send(port); + + return count; +} + +static void pl2303_send(struct usb_serial_port *port) +{ + int count, result; + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + + if (priv->write_urb_in_use) { + spin_unlock_irqrestore(&priv->lock, flags); + return; + } + + count = pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer, + port->bulk_out_size); + + if (count == 0) { + spin_unlock_irqrestore(&priv->lock, flags); + return; + } + + priv->write_urb_in_use = 1; + + spin_unlock_irqrestore(&priv->lock, flags); + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_buffer); port->write_urb->transfer_buffer_length = count; port->write_urb->dev = port->serial->dev; 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 - result = count; + priv->write_urb_in_use = 0; + // TODO: reschedule pl2303_send + } - return result; + schedule_work(&port->work); } +static int pl2303_write_room(struct usb_serial_port *port) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + int room = 0; + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + room = pl2303_buf_space_avail(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); + + dbg("%s - returns %d", __FUNCTION__, room); + return room; +} + +static int pl2303_chars_in_buffer(struct usb_serial_port *port) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + int chars = 0; + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + spin_lock_irqsave(&priv->lock, flags); + chars = pl2303_buf_data_avail(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); + + dbg("%s - returns %d", __FUNCTION__, chars); + return chars; +} static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios) { @@ -422,7 +536,7 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol } kfree (buf); -} +} static int pl2303_open (struct usb_serial_port *port, struct file *filp) { @@ -461,7 +575,7 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp) FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0); SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0, 1); SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 1, 0); - + if (priv->type == HX) { /* HX chip */ SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 0x44); @@ -504,45 +618,67 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp) static void pl2303_close (struct usb_serial_port *port, struct file *filp) { - struct pl2303_private *priv; + struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int c_cflag; - int result; + int bps; + long timeout; + wait_queue_t wait; \ dbg("%s - port %d", __FUNCTION__, port->number); - /* shutdown our urbs */ - dbg("%s - shutting down urbs", __FUNCTION__); - result = usb_unlink_urb (port->write_urb); - if (result) - dbg("%s - usb_unlink_urb (write_urb)" - " failed with reason: %d", __FUNCTION__, - result); + /* wait for data to drain from the buffer */ + spin_lock_irqsave(&priv->lock, flags); + timeout = PL2303_CLOSING_WAIT; + init_waitqueue_entry(&wait, current); + add_wait_queue(&port->tty->write_wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (pl2303_buf_data_avail(priv->buf) == 0 + || timeout == 0 || signal_pending(current) + || !usb_get_intfdata(port->serial->interface)) /* disconnect */ + break; + spin_unlock_irqrestore(&priv->lock, flags); + timeout = schedule_timeout(timeout); + spin_lock_irqsave(&priv->lock, flags); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->tty->write_wait, &wait); + /* clear out any remaining data in the buffer */ + pl2303_buf_clear(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); - result = usb_unlink_urb (port->read_urb); - if (result) - dbg("%s - usb_unlink_urb (read_urb) " - "failed with reason: %d", __FUNCTION__, - result); + /* wait for characters to drain from the device */ + /* (this is long enough for the entire 256 byte */ + /* pl2303 hardware buffer to drain with no flow */ + /* control for data rates of 1200 bps or more, */ + /* for lower rates we should really know how much */ + /* data is in the buffer to compute a delay */ + /* that is not unnecessarily long) */ + bps = tty_get_baud_rate(port->tty); + if (bps > 1200) + timeout = max((HZ*2560)/bps,HZ/10); + else + timeout = 2*HZ; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(timeout); - result = usb_unlink_urb (port->interrupt_in_urb); - if (result) - dbg("%s - usb_unlink_urb (interrupt_in_urb)" - " failed with reason: %d", __FUNCTION__, - result); + /* shutdown our urbs */ + dbg("%s - shutting down urbs", __FUNCTION__); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); + usb_kill_urb(port->interrupt_in_urb); if (port->tty) { c_cflag = port->tty->termios->c_cflag; if (c_cflag & HUPCL) { /* drop DTR and RTS */ - priv = usb_get_serial_port_data(port); spin_lock_irqsave(&priv->lock, flags); priv->line_control = 0; spin_unlock_irqrestore (&priv->lock, flags); set_control_lines (port->serial->dev, 0); } } - } static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file, @@ -672,12 +808,17 @@ static void pl2303_break_ctl (struct usb_serial_port *port, int break_state) static void pl2303_shutdown (struct usb_serial *serial) { int i; + struct pl2303_private *priv; dbg("%s", __FUNCTION__); for (i = 0; i < serial->num_ports; ++i) { - kfree (usb_get_serial_port_data(serial->port[i])); - usb_set_serial_port_data(serial->port[i], NULL); + priv = usb_get_serial_port_data(serial->port[i]); + if (priv) { + pl2303_buf_free(priv->buf); + kfree(priv); + usb_set_serial_port_data(serial->port[i], NULL); + } } } @@ -815,11 +956,23 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs) static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs) { struct usb_serial_port *port = (struct usb_serial_port *) urb->context; + struct pl2303_private *priv = usb_get_serial_port_data(port); int result; dbg("%s - port %d", __FUNCTION__, port->number); - - if (urb->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); + priv->write_urb_in_use = 0; + return; + default: /* error in the urb, so we have to resubmit it */ dbg("%s - Overflow in write", __FUNCTION__); dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); @@ -828,14 +981,199 @@ static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs) result = usb_submit_urb (port->write_urb, GFP_ATOMIC); if (result) dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", __FUNCTION__, result); + else + return; + } - return; + priv->write_urb_in_use = 0; + + /* send any buffered data */ + pl2303_send(port); +} + + +/* + * pl2303_buf_alloc + * + * Allocate a circular buffer and all associated memory. + */ + +static struct pl2303_buf *pl2303_buf_alloc(unsigned int size) +{ + + struct pl2303_buf *pb; + + + if (size == 0) + return NULL; + + pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL); + if (pb == NULL) + return NULL; + + pb->buf_buf = kmalloc(size, GFP_KERNEL); + if (pb->buf_buf == NULL) { + kfree(pb); + return NULL; } - schedule_work(&port->work); + pb->buf_size = size; + pb->buf_get = pb->buf_put = pb->buf_buf; + + return pb; + +} + + +/* + * pl2303_buf_free + * + * Free the buffer and all associated memory. + */ + +static void pl2303_buf_free(struct pl2303_buf *pb) +{ + if (pb != NULL) { + if (pb->buf_buf != NULL) + kfree(pb->buf_buf); + kfree(pb); + } +} + + +/* + * pl2303_buf_clear + * + * Clear out all data in the circular buffer. + */ + +static void pl2303_buf_clear(struct pl2303_buf *pb) +{ + if (pb != NULL) + pb->buf_get = pb->buf_put; + /* equivalent to a get of all data available */ +} + + +/* + * pl2303_buf_data_avail + * + * Return the number of bytes of data available in the circular + * buffer. + */ + +static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb) +{ + if (pb != NULL) + return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size); + else + return 0; +} + + +/* + * pl2303_buf_space_avail + * + * Return the number of bytes of space available in the circular + * buffer. + */ + +static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb) +{ + if (pb != NULL) + return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size); + else + return 0; +} + + +/* + * pl2303_buf_put + * + * Copy data data from a user buffer and put it into the circular buffer. + * Restrict to the amount of space available. + * + * Return the number of bytes copied. + */ + +static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, + unsigned int count) +{ + + unsigned int len; + + + if (pb == NULL) + return 0; + + len = pl2303_buf_space_avail(pb); + if (count > len) + count = len; + + if (count == 0) + return 0; + + len = pb->buf_buf + pb->buf_size - pb->buf_put; + if (count > len) { + memcpy(pb->buf_put, buf, len); + memcpy(pb->buf_buf, buf+len, count - len); + pb->buf_put = pb->buf_buf + count - len; + } else { + memcpy(pb->buf_put, buf, count); + if (count < len) + pb->buf_put += count; + else /* count == len */ + pb->buf_put = pb->buf_buf; + } + + return count; + } +/* + * pl2303_buf_get + * + * Get data from the circular buffer and copy to the given buffer. + * Restrict to the amount of data available. + * + * Return the number of bytes copied. + */ + +static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, + unsigned int count) +{ + + unsigned int len; + + + if (pb == NULL) + return 0; + + len = pl2303_buf_data_avail(pb); + if (count > len) + count = len; + + if (count == 0) + return 0; + + len = pb->buf_buf + pb->buf_size - pb->buf_get; + if (count > len) { + memcpy(buf, pb->buf_get, len); + memcpy(buf+len, pb->buf_buf, count - len); + pb->buf_get = pb->buf_buf + count - len; + } else { + memcpy(buf, pb->buf_get, count); + if (count < len) + pb->buf_get += count; + else /* count == len */ + pb->buf_get = pb->buf_buf; + } + + return count; + +} + static int __init pl2303_init (void) { int retval; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 03e8a7e1c8ee..e3b7c1dd2f4c 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -388,7 +388,7 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po good_spot = 1; for (j = 1; j <= num_ports-1; ++j) - if ((serial_table[i+j]) || (i+j >= SERIAL_TTY_MINORS)) { + if ((i+j >= SERIAL_TTY_MINORS) || (serial_table[i+j])) { good_spot = 0; i += j; break; @@ -405,7 +405,7 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po return NULL; } -static void return_serial (struct usb_serial *serial) +static void return_serial(struct usb_serial *serial) { int i; @@ -417,8 +417,6 @@ static void return_serial (struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { serial_table[serial->minor + i] = NULL; } - - return; } static void destroy_serial(struct kref *kref) @@ -455,15 +453,15 @@ static void destroy_serial(struct kref *kref) if (!port) continue; if (port->read_urb) { - usb_unlink_urb(port->read_urb); + usb_kill_urb(port->read_urb); usb_free_urb(port->read_urb); } if (port->write_urb) { - usb_unlink_urb(port->write_urb); + usb_kill_urb(port->write_urb); usb_free_urb(port->write_urb); } if (port->interrupt_in_urb) { - usb_unlink_urb(port->interrupt_in_urb); + usb_kill_urb(port->interrupt_in_urb); usb_free_urb(port->interrupt_in_urb); } kfree(port->bulk_in_buffer); @@ -621,15 +619,12 @@ static void serial_throttle (struct tty_struct * tty) if (!port->open_count) { dbg ("%s - port not open", __FUNCTION__); - goto exit; + return; } /* pass on to the driver specific version of this function */ if (port->serial->type->throttle) port->serial->type->throttle(port); - -exit: - ; } static void serial_unthrottle (struct tty_struct * tty) @@ -640,15 +635,12 @@ static void serial_unthrottle (struct tty_struct * tty) if (!port->open_count) { dbg("%s - port not open", __FUNCTION__); - goto exit; + return; } /* pass on to the driver specific version of this function */ if (port->serial->type->unthrottle) port->serial->type->unthrottle(port); - -exit: - ; } static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) @@ -681,15 +673,12 @@ static void serial_set_termios (struct tty_struct *tty, struct termios * old) if (!port->open_count) { dbg("%s - port not open", __FUNCTION__); - goto exit; + return; } /* pass on to the driver specific version of this function if it is available */ if (port->serial->type->set_termios) port->serial->type->set_termios(port, old); - -exit: - ; } static void serial_break (struct tty_struct *tty, int break_state) @@ -700,15 +689,12 @@ static void serial_break (struct tty_struct *tty, int break_state) if (!port->open_count) { dbg("%s - port not open", __FUNCTION__); - goto exit; + return; } /* pass on to the driver specific version of this function if it is available */ if (port->serial->type->break_ctl) port->serial->type->break_ctl(port, break_state); - -exit: - ; } static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) @@ -814,15 +800,15 @@ static void port_release(struct device *dev) dbg ("%s - %s", __FUNCTION__, dev->bus_id); if (port->read_urb) { - usb_unlink_urb(port->read_urb); + usb_kill_urb(port->read_urb); usb_free_urb(port->read_urb); } if (port->write_urb) { - usb_unlink_urb(port->write_urb); + usb_kill_urb(port->write_urb); usb_free_urb(port->write_urb); } if (port->interrupt_in_urb) { - usb_unlink_urb(port->interrupt_in_urb); + usb_kill_urb(port->interrupt_in_urb); usb_free_urb(port->interrupt_in_urb); } kfree(port->bulk_in_buffer); @@ -853,6 +839,25 @@ static struct usb_serial * create_serial (struct usb_device *dev, return serial; } +static struct usb_serial_device_type *search_serial_device(struct usb_interface *iface) +{ + struct list_head *p; + const struct usb_device_id *id; + struct usb_serial_device_type *t; + + /* List trough know devices and see if the usb id matches */ + list_for_each(p, &usb_serial_driver_list) { + t = list_entry(p, struct usb_serial_device_type, driver_list); + id = usb_match_id(iface, t->id_table); + if (id != NULL) { + dbg("descriptor matches"); + return t; + } + } + + return NULL; +} + int usb_serial_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -865,9 +870,7 @@ int usb_serial_probe(struct usb_interface *interface, struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS]; struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS]; struct usb_serial_device_type *type = NULL; - struct list_head *tmp; int retval; - int found; int minor; int buffer_size; int i; @@ -876,22 +879,9 @@ int usb_serial_probe(struct usb_interface *interface, int num_bulk_out = 0; int num_ports = 0; int max_endpoints; - const struct usb_device_id *id_pattern = NULL; - - /* loop through our list of known serial converters, and see if this - device matches. */ - found = 0; - list_for_each (tmp, &usb_serial_driver_list) { - type = list_entry(tmp, struct usb_serial_device_type, driver_list); - id_pattern = usb_match_id(interface, type->id_table); - if (id_pattern != NULL) { - dbg("descriptor matches"); - found = 1; - break; - } - } - if (!found) { - /* no match */ + + type = search_serial_device(interface); + if (!type) { dbg("none matched"); return -ENODEV; } @@ -899,17 +889,21 @@ int usb_serial_probe(struct usb_interface *interface, serial = create_serial (dev, interface, type); if (!serial) { dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__); - return -ENODEV; + return -ENOMEM; } /* if this device type has a probe function, call it */ if (type->probe) { + const struct usb_device_id *id; + if (!try_module_get(type->owner)) { dev_err(&interface->dev, "module get failed, exiting\n"); kfree (serial); return -EIO; } - retval = type->probe (serial, id_pattern); + + id = usb_match_id(interface, type->id_table); + retval = type->probe(serial, id); module_put(type->owner); if (retval) { @@ -1053,6 +1047,7 @@ int usb_serial_probe(struct usb_interface *interface, goto probe_error; } buffer_size = endpoint->wMaxPacketSize; + port->bulk_in_size = buffer_size; port->bulk_in_endpointAddress = endpoint->bEndpointAddress; port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL); if (!port->bulk_in_buffer) { @@ -1224,7 +1219,7 @@ struct tty_driver *usb_serial_tty_driver; static int __init usb_serial_init(void) { int i; - int result = 0; + int result; usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS); if (!usb_serial_tty_driver) @@ -1235,13 +1230,17 @@ static int __init usb_serial_init(void) serial_table[i] = NULL; } - bus_register(&usb_serial_bus_type); + result = bus_register(&usb_serial_bus_type); + if (result) { + err("%s - registering bus driver failed", __FUNCTION__); + goto exit_bus; + } /* register the generic driver, if we should */ result = usb_serial_generic_register(debug); if (result < 0) { err("%s - registering generic driver failed", __FUNCTION__); - goto exit; + goto exit_generic; } usb_serial_tty_driver->owner = THIS_MODULE; @@ -1259,7 +1258,7 @@ static int __init usb_serial_init(void) result = tty_register_driver(usb_serial_tty_driver); if (result) { err("%s - tty_register_driver failed", __FUNCTION__); - goto exit_generic; + goto exit_reg_driver; } /* register the USB driver */ @@ -1276,10 +1275,13 @@ static int __init usb_serial_init(void) exit_tty: tty_unregister_driver(usb_serial_tty_driver); -exit_generic: +exit_reg_driver: usb_serial_generic_deregister(); -exit: +exit_generic: + bus_unregister(&usb_serial_bus_type); + +exit_bus: err ("%s - returning with error %d", __FUNCTION__, result); put_tty_driver(usb_serial_tty_driver); return result; @@ -1332,17 +1334,13 @@ int usb_serial_register(struct usb_serial_device_type *new_device) /* Add this device to our list of devices */ list_add(&new_device->driver_list, &usb_serial_driver_list); - retval = usb_serial_bus_register (new_device); - - if (retval) - goto error; - - info("USB Serial support registered for %s", new_device->name); - - return retval; -error: - err("problem %d when registering driver %s", retval, new_device->name); - list_del(&new_device->driver_list); + retval = usb_serial_bus_register(new_device); + if (retval) { + err("problem %d when registering driver %s", retval, new_device->name); + list_del(&new_device->driver_list); + } + else + info("USB Serial support registered for %s", new_device->name); return retval; } @@ -1369,6 +1367,7 @@ EXPORT_SYMBOL(usb_serial_port_softint); /* Module information */ MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_VERSION( DRIVER_VERSION ); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h index e2e59560d7fb..d3702957b609 100644 --- a/drivers/usb/serial/usb-serial.h +++ b/drivers/usb/serial/usb-serial.h @@ -100,6 +100,7 @@ struct usb_serial_port { __u8 interrupt_in_endpointAddress; unsigned char * bulk_in_buffer; + int bulk_in_size; struct urb * read_urb; __u8 bulk_in_endpointAddress; diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 8a0a75533e70..0e5d90552789 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -446,9 +446,9 @@ static void visor_close (struct usb_serial_port *port, struct file * filp) dbg("%s - port %d", __FUNCTION__, port->number); /* shutdown our urbs */ - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); if (port->interrupt_in_urb) - usb_unlink_urb (port->interrupt_in_urb); + usb_kill_urb(port->interrupt_in_urb); /* Try to send shutdown message, if the device is gone, this will just fail. */ transfer_buffer = kmalloc (0x12, GFP_KERNEL); @@ -655,7 +655,7 @@ exit: static void visor_throttle (struct usb_serial_port *port) { dbg("%s - port %d", __FUNCTION__, port->number); - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); } diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 1b759018e8b8..179a671a2770 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -679,7 +679,7 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) { wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); urb = wrap->urb; - usb_unlink_urb(urb); + usb_kill_urb(urb); list_del(tmp); list_add(tmp, &info->rx_urbs_free); } @@ -690,7 +690,7 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) { wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); urb = wrap->urb; - usb_unlink_urb(urb); + usb_kill_urb(urb); list_del(tmp); list_add(tmp, &info->tx_urbs_free); } @@ -1343,7 +1343,7 @@ static void stop_command_port(struct usb_serial *serial) spin_lock_irqsave(&command_info->lock, flags); command_info->port_running--; if (!command_info->port_running) - usb_unlink_urb(command_port->read_urb); + usb_kill_urb(command_port->read_urb); spin_unlock_irqrestore(&command_info->lock, flags); } @@ -1371,7 +1371,7 @@ static int start_port_read(struct usb_serial_port *port) list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) { wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); urb = wrap->urb; - usb_unlink_urb(urb); + usb_kill_urb(urb); list_del(tmp); list_add(tmp, &info->rx_urbs_free); } diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 8aa136b4b380..d599362bebcc 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1053,12 +1053,6 @@ static int isd200_get_inquiry_data( struct us_data *us ) /* Standard IDE interface only supports disks */ info->InquiryData.DeviceType = DIRECT_ACCESS_DEVICE; - /* Fix-up the return data from an INQUIRY command to show - * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us - * in Linux. - */ - info->InquiryData.Versions = 0x2; - /* The length must be at least 36 (5 + 31) */ info->InquiryData.AdditionalLength = 0x1F; diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index 99ed4d9bc490..9d3d77452525 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -58,38 +58,6 @@ ***********************************************************************/ /* - * Fix-up the return data from an INQUIRY command to show - * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us - */ -static void fix_inquiry_data(struct scsi_cmnd *srb) -{ - unsigned char databuf[3]; - unsigned int index, offset; - - /* verify that it's an INQUIRY command */ - if (srb->cmnd[0] != INQUIRY) - return; - - index = offset = 0; - if (usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb, - &index, &offset, FROM_XFER_BUF) != sizeof(databuf)) - return; - - if ((databuf[2] & 7) == 2) - return; - - US_DEBUGP("Fixing INQUIRY data to show SCSI rev 2 - was %d\n", - databuf[2] & 7); - - /* Change the SCSI revision number */ - databuf[2] = (databuf[2] & ~7) | 2; - - index = offset = 0; - usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb, - &index, &offset, TO_XFER_BUF); -} - -/* * Fix-up the return data from a READ CAPACITY command. My Feiya reader * returns a value that is 1 too large. */ @@ -137,10 +105,6 @@ void usb_stor_qic157_command(struct scsi_cmnd *srb, struct us_data *us) /* send the command to the transport layer */ usb_stor_invoke_transport(srb, us); - if (srb->result == SAM_STAT_GOOD) { - /* fix the INQUIRY data if necessary */ - fix_inquiry_data(srb); - } } void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us) @@ -160,11 +124,6 @@ void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us) /* send the command to the transport layer */ usb_stor_invoke_transport(srb, us); - - if (srb->result == SAM_STAT_GOOD) { - /* fix the INQUIRY data if necessary */ - fix_inquiry_data(srb); - } } @@ -208,11 +167,6 @@ void usb_stor_ufi_command(struct scsi_cmnd *srb, struct us_data *us) /* send the command to the transport layer */ usb_stor_invoke_transport(srb, us); - - if (srb->result == SAM_STAT_GOOD) { - /* Fix the data for an INQUIRY, if necessary */ - fix_inquiry_data(srb); - } } void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb, @@ -222,9 +176,6 @@ void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb, usb_stor_invoke_transport(srb, us); if (srb->result == SAM_STAT_GOOD) { - /* Fix the INQUIRY data if necessary */ - fix_inquiry_data(srb); - /* Fix the READ CAPACITY result if necessary */ if (us->flags & US_FL_FIX_CAPACITY) fix_read_capacity(srb); diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index c18cd06ea31c..83d7003861ff 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -98,6 +98,23 @@ static int slave_configure(struct scsi_device *sdev) * the end, scatter-gather buffers follow page boundaries. */ blk_queue_dma_alignment(sdev->request_queue, (512 - 1)); + /* Set the SCSI level to at least 2. We'll leave it at 3 if that's + * what is originally reported. We need this to avoid confusing + * the SCSI layer with devices that report 0 or 1, but need 10-byte + * commands (ala ATAPI devices behind certain bridges, or devices + * which simply have broken INQUIRY data). + * + * NOTE: This means /dev/sg programs (ala cdrecord) will get the + * actual information. This seems to be the preference for + * programs like that. + * + * NOTE: This also means that /proc/scsi/scsi and sysfs may report + * the actual value or the modified one, depending on where the + * data comes from. + */ + if (sdev->scsi_level < SCSI_2) + sdev->scsi_level = SCSI_2; + /* According to the technical support people at Genesys Logic, * devices using their chips have problems transferring more than * 32 KB at a time. In practice people have found that 64 KB @@ -266,7 +283,7 @@ static int device_reset(struct scsi_cmnd *srb) static int bus_reset(struct scsi_cmnd *srb) { struct us_data *us = (struct us_data *)srb->device->host->hostdata[0]; - int result; + int result, rc; US_DEBUGP("%s called\n", __FUNCTION__); if (us->sm_state != US_STATE_IDLE) { @@ -291,8 +308,16 @@ static int bus_reset(struct scsi_cmnd *srb) result = -EBUSY; US_DEBUGP("Refusing to reset a multi-interface device\n"); } else { - result = usb_reset_device(us->pusb_dev); - US_DEBUGP("usb_reset_device returns %d\n", result); + 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); + } } up(&(us->dev_semaphore)); diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 42f784321607..07b919d800a1 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -911,7 +911,6 @@ int usb_stor_Bulk_max_lun(struct us_data *us) int result; /* issue the command */ - us->iobuf[0] = 0; result = usb_stor_control_msg(us, us->recv_ctrl_pipe, US_BULK_GET_MAX_LUN, USB_DIR_IN | USB_TYPE_CLASS | @@ -922,7 +921,7 @@ int usb_stor_Bulk_max_lun(struct us_data *us) result, us->iobuf[0]); /* if we have a successful request, return the result */ - if (result >= 0) + if (result > 0) return us->iobuf[0]; /* @@ -934,13 +933,16 @@ int usb_stor_Bulk_max_lun(struct us_data *us) if (result == -EPIPE) { usb_stor_clear_halt(us, us->recv_bulk_pipe); usb_stor_clear_halt(us, us->send_bulk_pipe); - /* return the default -- no LUNs */ - return 0; } - /* An answer or a STALL are the only valid responses. If we get - * something else, return an indication of error */ - return -1; + /* + * Some devices don't like GetMaxLUN. They may STALL the control + * pipe, they may return a zero-length result, they may do nothing at + * all and timeout, or they may fail in even more bizarrely creative + * ways. In these cases the best approach is to use the default + * value: only one LUN. + */ + return 0; } int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) @@ -1055,8 +1057,13 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) /* try to compute the actual residue, based on how much data * was really transferred and what the device tells us */ - residue = min(residue, transfer_length); - srb->resid = max(srb->resid, (int) residue); + if (residue) { + if (!(us->flags & US_FL_IGNORE_RESIDUE) || + srb->sc_data_direction == DMA_TO_DEVICE) { + residue = min(residue, transfer_length); + srb->resid = max(srb->resid, (int) residue); + } + } /* based on the status code, we report good or bad */ switch (bcs->Status) { diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 4fe25f6b0432..1e0eb58f0c09 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -36,13 +36,16 @@ /* If you edit this file, please try to keep it sorted first by VendorID, * then by ProductID. * - * If you want to add an entry for this file, please send the following - * to greg@kroah.com: - * - patch that adds the entry for your device which includes your - * email address right above the entry. + * If you want to add an entry for this file, be sure to include the + * following information: + * - a patch that adds the entry for your device, including your + * email address right above the entry (plus maybe a brief + * explanation of the reason for the entry), * - a copy of /proc/bus/usb/devices with your device plugged in * running with this patch. - * + * Send your submission to either Phil Dibowitz <phil@ipom.com> or + * Alan Stern <stern@rowland.harvard.edu>, and don't forget to CC: the + * USB development list <linux-usb-devel@lists.sourceforge.net>. */ UNUSUAL_DEV( 0x03ee, 0x6901, 0x0000, 0x0100, @@ -68,16 +71,6 @@ UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001, US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), #endif -/* <torsten.scherer@uni-bielefeld.de>: I don't know the name of the bridge - * manufacturer, but I've got an external USB drive by the Revoltec company - * that needs this. otherwise the drive is recognized as /dev/sda, but any - * access to it blocks indefinitely. - */ -UNUSUAL_DEV( 0x0402, 0x5621, 0x0103, 0x0103, - "Revoltec", - "USB/IDE Bridge (ATA/ATAPI)", - US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), - /* Deduced by Jonathan Woithe <jwoithe@physics.adelaide.edu.au> * Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message * always fails and confuses drive. @@ -95,12 +88,6 @@ UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100, US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ), #endif -/* Patch submitted by Alessandro Fracchetti <al.fracchetti@tin.it> */ -UNUSUAL_DEV( 0x0482, 0x0105, 0x0100, 0x0100, - "Kyocera", - "Finecam L3", - US_SC_SCSI, US_PR_BULK, NULL, US_FL_FIX_INQUIRY), - /* Patch submitted by Philipp Friedrich <philipp@void.at> */ UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100, "Kyocera", @@ -121,6 +108,7 @@ UNUSUAL_DEV( 0x0482, 0x0103, 0x0100, 0x0100, /* Patch for Kyocera Finecam L3 * Submitted by Michael Krauth <michael.krauth@web.de> + * and Alessandro Fracchetti <al.fracchetti@tin.it> */ UNUSUAL_DEV( 0x0482, 0x0105, 0x0100, 0x0100, "Kyocera", @@ -149,10 +137,13 @@ UNUSUAL_DEV( 0x04b8, 0x0602, 0x0110, 0x0110, "785EPX Storage", US_SC_SCSI, US_PR_BULK, NULL, US_FL_SINGLE_LUN), +/* Not sure who reported this originally but + * Pavel Machek <pavel@ucw.cz> reported that the extra US_FL_SINGLE_LUN + * flag be added */ UNUSUAL_DEV( 0x04cb, 0x0100, 0x0000, 0x2210, "Fujifilm", "FinePix 1400Zoom", - US_SC_UFI, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), + US_SC_UFI, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY | US_FL_SINGLE_LUN), /* Reported by Peter Wächtler <pwaechtler@loewe-komp.de> * The device needs the flags only. @@ -180,6 +171,16 @@ UNUSUAL_DEV( 0x04da, 0x0d05, 0x0000, 0x0000, "CD-R/RW Drive", US_SC_8070, US_PR_CB, NULL, 0), +/* Reported by Adriaan Penning <a.penning@luon.net> + * Note that these cameras report "Medium not present" after + * ALLOW_MEDIUM_REMOVAL, so they also need to be marked + * NOT_LOCKABLE in the SCSI blacklist (and the vendor is MATSHITA). */ +UNUSUAL_DEV( 0x04da, 0x2372, 0x0000, 0x9999, + "Panasonic", + "DMC-LCx Camera", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + /* Most of the following entries were developed with the help of * Shuttle/SCM directly. */ @@ -265,6 +266,21 @@ UNUSUAL_DEV( 0x0525, 0xa140, 0x0100, 0x0100, US_SC_8070, US_PR_BULK, NULL, US_FL_FIX_INQUIRY ), +/* Reported by Iacopo Spalletti <avvisi@spalletti.it> */ +UNUSUAL_DEV( 0x052b, 0x1807, 0x0100, 0x0100, + "Tekom Technologies, Inc", + "300_CAMERA", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* Yakumo Mega Image 37 + * Submitted by Stephan Fuhrmann <atomenergie@t-online.de> */ +UNUSUAL_DEV( 0x052b, 0x1801, 0x0100, 0x0100, + "Tekom Technologies, Inc", + "300_CAMERA", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + /* This entry is needed because the device reports Sub=ff */ UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0450, "Sony", @@ -383,10 +399,17 @@ UNUSUAL_DEV( 0x0595, 0x4343, 0x0000, 0x2210, "Digital Camera EX-20 DSC", US_SC_8070, US_PR_DEVICE, NULL, 0 ), +/* The entry was here before I took over, and had US_SC_RBC. It turns + * out that isn't needed. Additionally, Torsten Eriksson + * <Torsten.Eriksson@bergianska.se> is able to use his device fine + * without this entry at all - but I don't suspect that will be true + * for all users (the protocol is likely needed), so is staying at + * this time. - Phil Dibowitz <phil@ipom.com> + */ UNUSUAL_DEV( 0x059f, 0xa601, 0x0200, 0x0200, "LaCie", "USB Hard Disk", - US_SC_RBC, US_PR_CB, NULL, 0 ), + US_SC_DEVICE, US_PR_CB, NULL, 0 ), /* Submitted by Joel Bourquard <numlock@freesurf.ch> * Some versions of this device need the SubClass and Protocol overrides @@ -439,36 +462,6 @@ UNUSUAL_DEV( 0x05dc, 0xb002, 0x0000, 0x0113, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), -/* Reported by Carlos Villegas <cav@uniscope.co.jp> - * This device needs an INQUIRY of exactly 36-bytes to function. - * That is the only reason this entry is needed. - */ -UNUSUAL_DEV( 0x05e3, 0x0700, 0x0000, 0xffff, - "Genesys Logic", - "USB to IDE Card Reader", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_INQUIRY ), - -/* Submitted Alexander Oltu <alexander@all-2.com> */ -UNUSUAL_DEV( 0x05e3, 0x0701, 0x0000, 0xffff, - "Genesys Logic", - "USB to IDE Optical", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_MODE_XLATE ), - -/* Reported by Peter Marks <peter.marks@turner.com> - * Like the SIIG unit above, this unit needs an INQUIRY to ask for exactly - * 36 bytes of data. No more, no less. That is the only reason this entry - * is needed. - * - * ST818 slim drives (rev 0.02) don't need special care. -*/ -UNUSUAL_DEV( 0x05e3, 0x0702, 0x0000, 0xffff, - "Genesys Logic", - "USB to IDE Disk", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_INQUIRY ), - /* Reported by Hanno Boeck <hanno@gmx.de> * Taken from the Lycoris Kernel */ UNUSUAL_DEV( 0x0636, 0x0003, 0x0000, 0x9999, @@ -554,6 +547,13 @@ UNUSUAL_DEV( 0x07ab, 0xfc01, 0x0000, 0x9999, US_SC_QIC, US_PR_FREECOM, freecom_init, 0), #endif +/* Reported by Eero Volotinen <eero@ping-viini.org> */ +UNUSUAL_DEV( 0x07ab, 0xfccd, 0x0406, 0x0406, + "Freecom Technologies", + "FHD-Classic", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + UNUSUAL_DEV( 0x07af, 0x0004, 0x0100, 0x0133, "Microtech", "USB-SCSI-DB25", @@ -724,12 +724,6 @@ UNUSUAL_DEV( 0x097a, 0x0001, 0x0000, 0x0001, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_MODE_XLATE ), -UNUSUAL_DEV( 0x0a16, 0x8888, 0x0100, 0x0100, - "IBM", - "IBM USB Memory Key", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_INQUIRY ), - /* This Pentax still camera is not conformant * to the USB storage specification: - * - It does not like the INQUIRY command. So we must handle this command @@ -802,13 +796,28 @@ UNUSUAL_DEV( 0x0dd8, 0x1060, 0x0000, 0xffff, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), +/* Patch by Stephan Walter <stephan.walter@epfl.ch> + * I don't know why, but it works... */ +UNUSUAL_DEV( 0x0dda, 0x0001, 0x0012, 0x0012, + "WINWARD", + "Music Disk", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + /* Submitted by Antoine Mairesse <antoine.mairesse@free.fr> */ UNUSUAL_DEV( 0x0ed1, 0x6660, 0x0100, 0x0300, "USB", "Solid state disk", US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), - + +/* Reported by Rastislav Stanik <rs_kernel@yahoo.com> */ +UNUSUAL_DEV( 0x0ea0, 0x6828, 0x0110, 0x0110, + "USB", + "Flash Disk", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + /* Reported by Kevin Cernekee <kpc-usbdev@gelato.uiuc.edu> * Tested on hardware version 1.10. * Entry is needed only for the initializer function override. @@ -830,6 +839,13 @@ UNUSUAL_DEV( 0x1065, 0x2136, 0x0000, 0x9999, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_MODE_XLATE ), +/* Reported by Kotrla Vitezslav <kotrla@ceb.cz> */ +UNUSUAL_DEV( 0x1370, 0x6828, 0x0110, 0x0110, + "SWISSBIT", + "Black Silver", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + #ifdef CONFIG_USB_STORAGE_SDDR55 UNUSUAL_DEV( 0x55aa, 0xa103, 0x0000, 0x9999, "Sandisk", diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 9579bdf4969a..32e2f1482c57 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -97,6 +97,11 @@ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>"); MODULE_DESCRIPTION("USB Mass Storage driver for Linux"); MODULE_LICENSE("GPL"); +static unsigned int delay_use = 5; +module_param(delay_use, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device"); + + static int storage_probe(struct usb_interface *iface, const struct usb_device_id *id); @@ -882,6 +887,42 @@ static void dissociate_dev(struct us_data *us) kfree(us); } +/* Thread to carry out delayed SCSI-device scanning */ +static int usb_stor_scan_thread(void * __us) +{ + struct us_data *us = (struct us_data *)__us; + + /* + * This thread doesn't need any user-level access, + * so get rid of all our resources. + */ + lock_kernel(); + daemonize("usb-stor"); + current->flags |= PF_NOFREEZE; + unlock_kernel(); + + printk(KERN_DEBUG + "usb-storage: device found at %d\n", us->pusb_dev->devnum); + + /* Wait for the timeout to expire or for a disconnect */ + if (delay_use > 0) { + printk(KERN_DEBUG "usb-storage: waiting for device " + "to settle before scanning\n"); + wait_event_interruptible_timeout(us->scsi_scan_wait, + test_bit(US_FLIDX_DISCONNECTING, &us->flags), + delay_use * HZ); + } + + /* If the device is still connected, perform the scanning */ + if (!test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { + scsi_scan_host(us->host); + printk(KERN_DEBUG "usb-storage: device scan complete\n"); + } + + complete_and_exit(&us->scsi_scan_done, 0); +} + + /* Probe to see if we can drive a newly-connected USB device */ static int storage_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -903,6 +944,8 @@ static int storage_probe(struct usb_interface *intf, init_MUTEX_LOCKED(&(us->sema)); init_completion(&(us->notify)); init_waitqueue_head(&us->dev_reset_wait); + init_waitqueue_head(&us->scsi_scan_wait); + init_completion(&us->scsi_scan_done); /* Associate the us_data structure with the USB device */ result = associate_dev(us, intf); @@ -951,12 +994,10 @@ static int storage_probe(struct usb_interface *intf, if (result) goto BadDevice; - /* Acquire all the other resources */ + /* Acquire all the other resources and add the host */ result = usb_stor_acquire_resources(us); if (result) goto BadDevice; - - /* Finally, add the host (this does SCSI device scanning) */ result = scsi_add_host(us->host, &intf->dev); if (result) { printk(KERN_WARNING USB_STORAGE @@ -964,10 +1005,15 @@ static int storage_probe(struct usb_interface *intf, goto BadDevice; } - scsi_scan_host(us->host); + /* Start up the thread for delayed SCSI-device scanning */ + result = kernel_thread(usb_stor_scan_thread, us, CLONE_VM); + if (result < 0) { + printk(KERN_WARNING USB_STORAGE + "Unable to start the device-scanning thread\n"); + scsi_remove_host(us->host); + goto BadDevice; + } - printk(KERN_DEBUG - "USB Mass Storage device found at %d\n", us->pusb_dev->devnum); return 0; /* We come here if there are any problems */ @@ -991,6 +1037,11 @@ static void storage_disconnect(struct usb_interface *intf) usb_stor_stop_transport(us); wake_up(&us->dev_reset_wait); + /* Interrupt the SCSI-device-scanning thread's time delay, and + * wait for the thread to finish */ + wake_up(&us->scsi_scan_wait); + wait_for_completion(&us->scsi_scan_done); + /* Wait for the current command to finish, then remove the host */ down(&us->dev_semaphore); up(&us->dev_semaphore); @@ -1012,12 +1063,9 @@ static int __init usb_stor_init(void) /* register the driver, return usb_register return code if error */ retval = usb_register(&usb_storage_driver); - if (retval) - goto out; + if (retval == 0) + printk(KERN_INFO "USB Mass Storage support registered.\n"); - /* we're all set */ - printk(KERN_INFO "USB Mass Storage support registered.\n"); -out: return retval; } diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 785f30be68c5..199594967870 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -73,6 +73,7 @@ struct us_unusual_dev { #define US_FL_SCM_MULT_TARG 0x00000020 /* supports multiple targets */ #define US_FL_FIX_INQUIRY 0x00000040 /* INQUIRY response needs faking */ #define US_FL_FIX_CAPACITY 0x00000080 /* READ CAPACITY response too big */ +#define US_FL_IGNORE_RESIDUE 0x00000100 /* reported residue is wrong */ /* Dynamic flag definitions: used in set_bit() etc. */ #define US_FLIDX_URB_ACTIVE 18 /* 0x00040000 current_urb is in use */ @@ -161,6 +162,8 @@ struct us_data { struct semaphore sema; /* to sleep thread on */ struct completion notify; /* thread begin/end */ wait_queue_head_t dev_reset_wait; /* wait during reset */ + wait_queue_head_t scsi_scan_wait; /* wait before scanning */ + struct completion scsi_scan_done; /* scan thread end */ /* subdriver information */ void *extra; /* Any extra data */ diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index ab23abe01649..8fff0829a2b6 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -49,6 +49,24 @@ config FB_MODE_HELPERS your driver does not take advantage of this feature, choosing Y will just increase the kernel size by about 5K. +config FB_TILEBLITTING + bool "Enable Tile Blitting Support" + depends on FB + default n + ---help--- + This enables tile blitting. Tile blitting is a drawing technique + where the screen is divided into rectangular sections (tiles), whereas + the standard blitting divides the screen into pixels. Because the + default drawing element is a tile, drawing functions will be passed + parameters in terms of number of tiles instead of number of pixels. + For example, to draw a single character, instead of using bitmaps, + an index to an array of bitmaps will be used. To clear or move a + rectangular section of a screen, the rectangle willbe described in + terms of number of tiles in the x- and y-axis. + + This is particularly important to one driver, the matroxfb. If + unsure, say N. + config FB_CIRRUS tristate "Cirrus Logic support" depends on FB && (ZORRO || PCI) diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index 2d753a150405..264b53179321 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -1974,7 +1974,7 @@ int __init atyfb_do_init(void) info->fix = atyfb_fix; info->par = default_par; - + info->device = &pdev->dev; #ifdef __sparc__ /* * Map memory-mapped registers. diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 325b83b3017c..abecde5c4ed5 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c @@ -61,6 +61,7 @@ #include <linux/tty.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/time.h> #include <linux/fb.h> #include <linux/ioport.h> #include <linux/init.h> @@ -527,8 +528,7 @@ static int __devinit radeon_probe_pll_params(struct radeonfb_info *rinfo) break; } - OUTREG8(CLOCK_CNTL_INDEX, 1); - ppll_div_sel = INREG8(CLOCK_CNTL_DATA + 1) & 0x3; + ppll_div_sel = INREG(CLOCK_CNTL_INDEX + 1) & 0x3; n = (INPLL(PPLL_DIV_0 + ppll_div_sel) & 0x7ff); m = (INPLL(PPLL_REF_DIV) & 0x3ff); @@ -595,53 +595,10 @@ static int __devinit radeon_probe_pll_params(struct radeonfb_info *rinfo) */ static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo) { -#ifdef CONFIG_PPC_OF - /* - * Retreive PLL infos from Open Firmware first - */ - if (!force_measure_pll && radeon_read_xtal_OF(rinfo) == 0) { - printk(KERN_INFO "radeonfb: Retreived PLL infos from Open Firmware\n"); - rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & 0x3ff; - /* FIXME: Max clock may be higher on newer chips */ - rinfo->pll.ppll_min = 12000; - rinfo->pll.ppll_max = 35000; - goto found; - } -#endif /* CONFIG_PPC_OF */ - - /* - * Check out if we have an X86 which gave us some PLL informations - * and if yes, retreive them - */ - if (!force_measure_pll && rinfo->bios_seg) { - u16 pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30); - - rinfo->pll.sclk = BIOS_IN16(pll_info_block + 0x08); - rinfo->pll.mclk = BIOS_IN16(pll_info_block + 0x0a); - rinfo->pll.ref_clk = BIOS_IN16(pll_info_block + 0x0e); - rinfo->pll.ref_div = BIOS_IN16(pll_info_block + 0x10); - rinfo->pll.ppll_min = BIOS_IN32(pll_info_block + 0x12); - rinfo->pll.ppll_max = BIOS_IN32(pll_info_block + 0x16); - - printk(KERN_INFO "radeonfb: Retreived PLL infos from BIOS\n"); - goto found; - } - - /* - * We didn't get PLL parameters from either OF or BIOS, we try to - * probe them - */ - if (radeon_probe_pll_params(rinfo) == 0) { - printk(KERN_INFO "radeonfb: Retreived PLL infos from registers\n"); - /* FIXME: Max clock may be higher on newer chips */ - rinfo->pll.ppll_min = 12000; - rinfo->pll.ppll_max = 35000; - goto found; - } - /* - * Neither of the above worked, we have a few default values, though - * that's mostly incomplete + * In the case nothing works, these are defaults; they are mostly + * incomplete, however. It does provide ppll_max and _min values + * even for most other methods, however. */ switch (rinfo->chipset) { case PCI_DEVICE_ID_ATI_RADEON_QW: @@ -697,6 +654,47 @@ static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo) } rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & 0x3ff; + +#ifdef CONFIG_PPC_OF + /* + * Retreive PLL infos from Open Firmware first + */ + if (!force_measure_pll && radeon_read_xtal_OF(rinfo) == 0) { + printk(KERN_INFO "radeonfb: Retreived PLL infos from Open Firmware\n"); + goto found; + } +#endif /* CONFIG_PPC_OF */ + + /* + * Check out if we have an X86 which gave us some PLL informations + * and if yes, retreive them + */ + if (!force_measure_pll && rinfo->bios_seg) { + u16 pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30); + + rinfo->pll.sclk = BIOS_IN16(pll_info_block + 0x08); + rinfo->pll.mclk = BIOS_IN16(pll_info_block + 0x0a); + rinfo->pll.ref_clk = BIOS_IN16(pll_info_block + 0x0e); + rinfo->pll.ref_div = BIOS_IN16(pll_info_block + 0x10); + rinfo->pll.ppll_min = BIOS_IN32(pll_info_block + 0x12); + rinfo->pll.ppll_max = BIOS_IN32(pll_info_block + 0x16); + + printk(KERN_INFO "radeonfb: Retreived PLL infos from BIOS\n"); + goto found; + } + + /* + * We didn't get PLL parameters from either OF or BIOS, we try to + * probe them + */ + if (radeon_probe_pll_params(rinfo) == 0) { + printk(KERN_INFO "radeonfb: Retreived PLL infos from registers\n"); + goto found; + } + + /* + * Fall back to already-set defaults... + */ printk(KERN_INFO "radeonfb: Used default PLL infos\n"); found: @@ -715,6 +713,7 @@ found: rinfo->pll.ref_div, rinfo->pll.mclk / 100, rinfo->pll.mclk % 100, rinfo->pll.sclk / 100, rinfo->pll.sclk % 100); + printk("radeonfb: PLL min %d max %d\n", rinfo->pll.ppll_min, rinfo->pll.ppll_max); } static int radeonfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info) @@ -934,43 +933,94 @@ static int radeonfb_ioctl (struct inode *inode, struct file *file, unsigned int } -static int radeon_screen_blank (struct radeonfb_info *rinfo, int blank) +static int radeon_screen_blank (struct radeonfb_info *rinfo, int blank, int mode_switch) { - u32 val = INREG(CRTC_EXT_CNTL); - u32 val2 = 0; + u32 val; + u32 tmp_pix_clks; - if (rinfo->mon1_type == MT_LCD) - val2 = INREG(LVDS_GEN_CNTL) & ~LVDS_DISPLAY_DIS; - - /* reset it */ + if (rinfo->lock_blank) + return 0; + + radeon_engine_idle(); + + val = INREG(CRTC_EXT_CNTL); val &= ~(CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS | CRTC_VSYNC_DIS); - switch (blank) { - case VESA_NO_BLANKING: - break; - case VESA_VSYNC_SUSPEND: - val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS); - break; - case VESA_HSYNC_SUSPEND: - val |= (CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS); - break; - case VESA_POWERDOWN: - val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS | - CRTC_HSYNC_DIS); - val2 |= (LVDS_DISPLAY_DIS); - break; + case VESA_NO_BLANKING: + break; + case VESA_VSYNC_SUSPEND: + val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS); + break; + case VESA_HSYNC_SUSPEND: + val |= (CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS); + break; + case VESA_POWERDOWN: + val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS | + CRTC_HSYNC_DIS); + break; } + OUTREG(CRTC_EXT_CNTL, val); + - radeon_fifo_wait(1); switch (rinfo->mon1_type) { - case MT_LCD: - OUTREG(LVDS_GEN_CNTL, val2); - break; - case MT_CRT: - default: - OUTREG(CRTC_EXT_CNTL, val); + case MT_DFP: + if (mode_switch) break; + if (blank == VESA_NO_BLANKING) + OUTREGP(FP_GEN_CNTL, (FP_FPON | FP_TMDS_EN), + ~(FP_FPON | FP_TMDS_EN)); + else + OUTREGP(FP_GEN_CNTL, 0, ~(FP_FPON | FP_TMDS_EN)); + break; + case MT_LCD: + val = INREG(LVDS_GEN_CNTL); + if (blank == VESA_NO_BLANKING) { + u32 target_val = (val & ~LVDS_DISPLAY_DIS) | LVDS_BLON | LVDS_ON + | LVDS_ON | (rinfo->init_state.lvds_gen_cntl & LVDS_DIGON); + if ((val ^ target_val) == LVDS_DISPLAY_DIS) + OUTREG(LVDS_GEN_CNTL, target_val); + else if ((val ^ target_val) != 0) { + del_timer_sync(&rinfo->lvds_timer); + OUTREG(LVDS_GEN_CNTL, target_val & ~LVDS_ON); + rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK; + rinfo->init_state.lvds_gen_cntl |= target_val & LVDS_STATE_MASK; + if (mode_switch) { + msleep(rinfo->panel_info.pwr_delay); + OUTREG(LVDS_GEN_CNTL, target_val); + } + else { + rinfo->pending_lvds_gen_cntl = target_val; + mod_timer(&rinfo->lvds_timer, + jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay)); + } + } + } else { + val |= LVDS_DISPLAY_DIS; + OUTREG(LVDS_GEN_CNTL, val); + + /* We don't do a full switch-off on a simple mode switch */ + if (mode_switch) + break; + + /* Asic bug, when turning off LVDS_ON, we have to make sure + * RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off + */ + tmp_pix_clks = INPLL(PIXCLKS_CNTL); + if (rinfo->is_mobility || rinfo->is_IGP) + OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb); + val &= ~(LVDS_BLON | LVDS_ON); + OUTREG(LVDS_GEN_CNTL, val); + rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK; + rinfo->init_state.lvds_gen_cntl |= val & LVDS_STATE_MASK; + if (rinfo->is_mobility || rinfo->is_IGP) + OUTPLL(PIXCLKS_CNTL, tmp_pix_clks); + } + break; + case MT_CRT: + // todo: powerdown DAC + default: + break; } return 0; @@ -983,17 +1033,7 @@ int radeonfb_blank (int blank, struct fb_info *info) if (rinfo->asleep) return 0; -#ifdef CONFIG_PMAC_BACKLIGHT - if (rinfo->mon1_type == MT_LCD && _machine == _MACH_Pmac && blank) - set_backlight_enable(0); -#endif - - radeon_screen_blank(rinfo, blank); - -#ifdef CONFIG_PMAC_BACKLIGHT - if (rinfo->mon1_type == MT_LCD && _machine == _MACH_Pmac && !blank) - set_backlight_enable(1); -#endif + radeon_screen_blank(rinfo, blank, 0); return 0; } @@ -1225,7 +1265,8 @@ static void radeon_write_mode (struct radeonfb_info *rinfo, del_timer_sync(&rinfo->lvds_timer); - radeon_screen_blank(rinfo, VESA_POWERDOWN); + radeon_screen_blank(rinfo, VESA_POWERDOWN, 1); + msleep(100); radeon_fifo_wait(31); for (i=0; i<10; i++) @@ -1265,35 +1306,9 @@ static void radeon_write_mode (struct radeonfb_info *rinfo, OUTREG(FP_GEN_CNTL, mode->fp_gen_cntl); OUTREG(TMDS_CRC, mode->tmds_crc); OUTREG(TMDS_TRANSMITTER_CNTL, mode->tmds_transmitter_cntl); - - if (primary_mon == MT_LCD) { - unsigned int tmp = INREG(LVDS_GEN_CNTL); - - /* HACK: The backlight control code may have modified init_state.lvds_gen_cntl, - * so we update ourselves - */ - mode->lvds_gen_cntl &= ~LVDS_STATE_MASK; - mode->lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_STATE_MASK); - - if ((tmp & (LVDS_ON | LVDS_BLON)) == - (mode->lvds_gen_cntl & (LVDS_ON | LVDS_BLON))) { - OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl); - } else { - rinfo->pending_pixclks_cntl = INPLL(PIXCLKS_CNTL); - if (rinfo->is_mobility || rinfo->is_IGP) - OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb); - if (!(tmp & (LVDS_ON | LVDS_BLON))) - OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl | LVDS_BLON); - rinfo->pending_lvds_gen_cntl = mode->lvds_gen_cntl; - mod_timer(&rinfo->lvds_timer, - jiffies + MS_TO_HZ(rinfo->panel_info.pwr_delay)); - } - } } - RTRACE("lvds_gen_cntl: %08x\n", INREG(LVDS_GEN_CNTL)); - - radeon_screen_blank(rinfo, VESA_NO_BLANKING); + radeon_screen_blank(rinfo, VESA_NO_BLANKING, 1); radeon_fifo_wait(2); OUTPLL(VCLK_ECP_CNTL, mode->vclk_ecp_cntl); @@ -1329,7 +1344,7 @@ static void radeon_calc_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs * not sure which model starts having FP2_GEN_CNTL, I assume anything more * recent than an r(v)100... */ -#if 0 +#if 1 /* XXX I had reports of flicker happening with the cinema display * on TMDS1 that seem to be fixed if I also forbit odd dividers in * this case. This could just be a bandwidth calculation issue, I @@ -1379,6 +1394,8 @@ static void radeon_calc_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs freq = rinfo->pll.ppll_max; if (freq*12 < rinfo->pll.ppll_min) freq = rinfo->pll.ppll_min / 12; + RTRACE("freq = %lu, PLL min = %u, PLL max = %u\n", + freq, rinfo->pll.ppll_min, rinfo->pll.ppll_max); for (post_div = &post_divs[0]; post_div->divider; ++post_div) { pll_output_freq = post_div->divider * freq; @@ -1392,6 +1409,16 @@ static void radeon_calc_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs break; } + /* If we fall through the bottom, try the "default value" + given by the terminal post_div->bitvalue */ + if ( !post_div->divider ) { + post_div = &post_divs[post_div->bitvalue]; + pll_output_freq = post_div->divider * freq; + } + RTRACE("ref_div = %d, ref_clk = %d, output_freq = %d\n", + rinfo->pll.ref_div, rinfo->pll.ref_clk, + pll_output_freq); + fb_div = round_div(rinfo->pll.ref_div*pll_output_freq, rinfo->pll.ref_clk); regs->ppll_ref_div = rinfo->pll.ref_div; @@ -1780,8 +1807,7 @@ static int backlight_conv_m7[] = { static int radeon_set_backlight_enable(int on, int level, void *data) { struct radeonfb_info *rinfo = (struct radeonfb_info *)data; - unsigned int lvds_gen_cntl = INREG(LVDS_GEN_CNTL); - unsigned long tmpPixclksCntl = INPLL(PIXCLKS_CNTL); + u32 lvds_gen_cntl, tmpPixclksCntl; int* conv_table; if (rinfo->mon1_type != MT_LCD) @@ -1803,42 +1829,47 @@ static int radeon_set_backlight_enable(int on, int level, void *data) conv_table = backlight_conv_m6; del_timer_sync(&rinfo->lvds_timer); + radeon_engine_idle(); - lvds_gen_cntl |= (LVDS_BL_MOD_EN | LVDS_BLON); - radeon_fifo_wait(3); + lvds_gen_cntl = INREG(LVDS_GEN_CNTL); if (on && (level > BACKLIGHT_OFF)) { - lvds_gen_cntl |= LVDS_DIGON; - if (!(lvds_gen_cntl & LVDS_ON)) { - lvds_gen_cntl &= ~LVDS_BLON; + lvds_gen_cntl &= ~LVDS_DISPLAY_DIS; + if (!(lvds_gen_cntl & LVDS_BLON) || !(lvds_gen_cntl & LVDS_ON)) { + lvds_gen_cntl |= LVDS_BLON /* | LVDS_EN | LVDS_DIGON */; OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); - (void)INREG(LVDS_GEN_CNTL); - mdelay(rinfo->panel_info.pwr_delay);/* OUCH !!! FIXME */ - lvds_gen_cntl |= LVDS_BLON; + lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK; + lvds_gen_cntl |= (conv_table[level] << + LVDS_BL_MOD_LEVEL_SHIFT); + lvds_gen_cntl |= LVDS_ON; + rinfo->pending_lvds_gen_cntl = lvds_gen_cntl; + mod_timer(&rinfo->lvds_timer, + jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay)); + } else { + lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK; + lvds_gen_cntl |= (conv_table[level] << + LVDS_BL_MOD_LEVEL_SHIFT); OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); } - lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK; - lvds_gen_cntl |= (conv_table[level] << - LVDS_BL_MOD_LEVEL_SHIFT); - lvds_gen_cntl |= (LVDS_ON | LVDS_EN); - lvds_gen_cntl &= ~LVDS_DISPLAY_DIS; + rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK; + rinfo->init_state.lvds_gen_cntl |= rinfo->pending_lvds_gen_cntl + & LVDS_STATE_MASK; } else { /* Asic bug, when turning off LVDS_ON, we have to make sure RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off */ + tmpPixclksCntl = INPLL(PIXCLKS_CNTL); if (rinfo->is_mobility || rinfo->is_IGP) OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb); lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK; lvds_gen_cntl |= (conv_table[0] << LVDS_BL_MOD_LEVEL_SHIFT); - lvds_gen_cntl |= LVDS_DISPLAY_DIS | LVDS_BLON; + lvds_gen_cntl |= LVDS_DISPLAY_DIS; + OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); + lvds_gen_cntl &= ~(LVDS_ON | LVDS_BLON /* | LVDS_EN | LVDS_DIGON */); OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); - mdelay(rinfo->panel_info.pwr_delay);/* OUCH !!! FIXME */ - lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGON); + if (rinfo->is_mobility || rinfo->is_IGP) + OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl); } - - OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); - if (rinfo->is_mobility || rinfo->is_IGP) - OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl); rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK; rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK); diff --git a/drivers/video/aty/radeon_monitor.c b/drivers/video/aty/radeon_monitor.c index e6c01642cfdc..22cf8b0c9c57 100644 --- a/drivers/video/aty/radeon_monitor.c +++ b/drivers/video/aty/radeon_monitor.c @@ -69,11 +69,12 @@ static int __devinit radeon_parse_montype_prop(struct device_node *dp, u8 **out_ mt = MT_DFP; else if (!strcmp(pmt, "CRT")) mt = MT_CRT; - else if (strcmp(pmt, "NONE")) { - printk(KERN_WARNING "radeonfb: Unknown OF display-type: %s\n", pmt); - return MT_NONE; - } else + else { + if (strcmp(pmt, "NONE") != 0) + printk(KERN_WARNING "radeonfb: Unknown OF display-type: %s\n", + pmt); return MT_NONE; + } for (i = 0; propnames[i] != NULL; ++i) { pedid = (u8 *)get_property(dp, propnames[i], NULL); @@ -632,43 +633,25 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo, */ static void radeon_fixup_panel_info(struct radeonfb_info *rinfo) { + #ifdef CONFIG_PPC_OF /* - * A few iBook laptop panels seem to need a fixed PLL setting - * - * We should probably do this differently based on the panel - * type/model or eventually some other device-tree informations, - * but these tweaks below work enough for now. --BenH + * LCD Flat panels should use fixed dividers, we enfore that on + * PowerMac only for now... */ -#ifdef CONFIG_PPC_OF - /* iBook2's */ - if (machine_is_compatible("PowerBook4,3")) { - rinfo->panel_info.ref_divider = rinfo->pll.ref_div; - rinfo->panel_info.post_divider = 0x6; - rinfo->panel_info.fbk_divider = 0xad; - rinfo->panel_info.use_bios_dividers = 1; - } - /* Aluminium PowerBook 15" */ - if (machine_is_compatible("PowerBook5,4")) { + if (!rinfo->panel_info.use_bios_dividers && rinfo->mon1_type == MT_LCD + && rinfo->is_mobility) { + int ppll_div_sel = INREG8(CLOCK_CNTL_INDEX + 1) & 0x3; + u32 ppll_divn = INPLL(PPLL_DIV_0 + ppll_div_sel); rinfo->panel_info.ref_divider = rinfo->pll.ref_div; - rinfo->panel_info.post_divider = 0x2; - rinfo->panel_info.fbk_divider = 0x8e; - rinfo->panel_info.use_bios_dividers = 1; - } - /* Aluminium PowerBook 17" */ - if (machine_is_compatible("PowerBook5,3") || - machine_is_compatible("PowerBook5,5")) { - rinfo->panel_info.ref_divider = rinfo->pll.ref_div; - rinfo->panel_info.post_divider = 0x4; - rinfo->panel_info.fbk_divider = 0x80; - rinfo->panel_info.use_bios_dividers = 1; - } - /* iBook G4 */ - if (machine_is_compatible("PowerBook6,3") || - machine_is_compatible("PowerBook6,5")) { - rinfo->panel_info.ref_divider = rinfo->pll.ref_div; - rinfo->panel_info.post_divider = 0x6; - rinfo->panel_info.fbk_divider = 0xad; + rinfo->panel_info.fbk_divider = ppll_divn & 0x7ff; + rinfo->panel_info.post_divider = (ppll_divn >> 16) & 0x7; rinfo->panel_info.use_bios_dividers = 1; + + printk(KERN_DEBUG "radeonfb: Using Firmware dividers 0x%08x " + "from PPLL %d\n", + rinfo->panel_info.fbk_divider | + (rinfo->panel_info.post_divider << 16), + ppll_div_sel); } #endif /* CONFIG_PPC_OF */ } diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c index c26f43c04155..cad299f160c1 100644 --- a/drivers/video/aty/radeon_pm.c +++ b/drivers/video/aty/radeon_pm.c @@ -465,7 +465,9 @@ static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo) OUTPLL( pllPIXCLKS_CNTL, pixclks_cntl); - + /* Switch off LVDS interface */ + OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & + ~(LVDS_BLON | LVDS_EN | LVDS_ON | LVDS_DIGON)); /* Enable System power management */ pll_pwrmgt_cntl = INPLL( pllPLL_PWRMGT_CNTL); diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h index 447eb58411d7..7377cb7b14b9 100644 --- a/drivers/video/aty/radeonfb.h +++ b/drivers/video/aty/radeonfb.h @@ -425,8 +425,6 @@ static inline u32 _INPLL(struct radeonfb_info *rinfo, u32 addr) spin_unlock_irqrestore(&rinfo->reg_lock, flags); \ } while (0) -#define MS_TO_HZ(ms) ((ms * HZ + 999) / 1000) - #define BIOS_IN8(v) (readb(rinfo->bios_seg + (v))) #define BIOS_IN16(v) (readb(rinfo->bios_seg + (v)) | \ (readb(rinfo->bios_seg + (v) + 1) << 8)) diff --git a/drivers/video/bw2.c b/drivers/video/bw2.c index 65b65addff6b..3b2c5a5e6c12 100644 --- a/drivers/video/bw2.c +++ b/drivers/video/bw2.c @@ -386,7 +386,7 @@ int __init bw2_init(void) struct sbus_bus *sbus; struct sbus_dev *sdev; - if (fb_get_options("bw2fb", &option)) + if (fb_get_options("bw2fb", NULL)) return -ENODEV; #ifdef CONFIG_SUN4 diff --git a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c index 4ee5a25db298..a51f4d2b69d8 100644 --- a/drivers/video/chipsfb.c +++ b/drivers/video/chipsfb.c @@ -416,7 +416,7 @@ chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) release_mem_region(addr, size); return -ENOMEM; } - + p->device = &dp->dev; init_chips(p, addr); #ifdef CONFIG_PMAC_PBOOK diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile index bfdd40665b6c..e5fa93b68043 100644 --- a/drivers/video/console/Makefile +++ b/drivers/video/console/Makefile @@ -24,7 +24,8 @@ obj-$(CONFIG_PROM_CONSOLE) += promcon.o promcon_tbl.o obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o obj-$(CONFIG_VGA_CONSOLE) += vgacon.o obj-$(CONFIG_MDA_CONSOLE) += mdacon.o -obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += fbcon.o font.o +obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += fbcon.o bitblit.o font.o +obj-$(CONFIG_FB_TILEBLITTING) += tileblit.o obj-$(CONFIG_FB_STI) += sticore.o diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c new file mode 100644 index 000000000000..43044f99c37f --- /dev/null +++ b/drivers/video/console/bitblit.c @@ -0,0 +1,370 @@ +/* + * linux/drivers/video/console/bitblit.c -- BitBlitting Operation + * + * Originally from the 'accel_*' routines in drivers/video/console/fbcon.c + * + * Copyright (C) 2004 Antonino Daplas <adaplas @pol.net> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/fb.h> +#include <linux/vt_kern.h> +#include <linux/console.h> +#include <asm/types.h> +#include "fbcon.h" + +/* + * Accelerated handlers. + */ +#define FBCON_ATTRIBUTE_UNDERLINE 1 +#define FBCON_ATTRIBUTE_REVERSE 2 +#define FBCON_ATTRIBUTE_BOLD 4 + +static inline int real_y(struct display *p, int ypos) +{ + int rows = p->vrows; + + ypos += p->yscroll; + return ypos < rows ? ypos : ypos - rows; +} + + +static inline int get_attribute(struct fb_info *info, u16 c) +{ + int attribute = 0; + + if (fb_get_color_depth(info) == 1) { + if (attr_underline(c)) + attribute |= FBCON_ATTRIBUTE_UNDERLINE; + if (attr_reverse(c)) + attribute |= FBCON_ATTRIBUTE_REVERSE; + if (attr_bold(c)) + attribute |= FBCON_ATTRIBUTE_BOLD; + } + + return attribute; +} + +static inline void update_attr(u8 *dst, u8 *src, int attribute, + struct vc_data *vc) +{ + int i, offset = (vc->vc_font.height < 10) ? 1 : 2; + int width = (vc->vc_font.width + 7) >> 3; + unsigned int cellsize = vc->vc_font.height * width; + u8 c; + + offset = cellsize - (offset * width); + for (i = 0; i < cellsize; i++) { + c = src[i]; + if (attribute & FBCON_ATTRIBUTE_UNDERLINE && i >= offset) + c = 0xff; + if (attribute & FBCON_ATTRIBUTE_BOLD) + c |= c >> 1; + if (attribute & FBCON_ATTRIBUTE_REVERSE) + c = ~c; + dst[i] = c; + } +} + +static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int dy, int dx, int height, int width) +{ + struct fb_copyarea area; + + area.sx = sx * vc->vc_font.width; + area.sy = sy * vc->vc_font.height; + area.dx = dx * vc->vc_font.width; + area.dy = dy * vc->vc_font.height; + area.height = height * vc->vc_font.height; + area.width = width * vc->vc_font.width; + + info->fbops->fb_copyarea(info, &area); +} + +static void bit_clear(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int height, int width) +{ + int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; + struct fb_fillrect region; + + region.color = attr_bgcol_ec(bgshift, vc); + region.dx = sx * vc->vc_font.width; + region.dy = sy * vc->vc_font.height; + region.width = width * vc->vc_font.width; + region.height = height * vc->vc_font.height; + region.rop = ROP_COPY; + + info->fbops->fb_fillrect(info, ®ion); +} + +static void bit_putcs(struct vc_data *vc, struct fb_info *info, + const unsigned short *s, int count, int yy, int xx, + int fg, int bg) +{ + void (*move_unaligned)(struct fb_info *info, struct fb_pixmap *buf, + u8 *dst, u32 d_pitch, u8 *src, u32 idx, + u32 height, u32 shift_high, u32 shift_low, + u32 mod); + void (*move_aligned)(struct fb_info *info, struct fb_pixmap *buf, + u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, + u32 height); + unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; + unsigned int width = (vc->vc_font.width + 7) >> 3; + unsigned int cellsize = vc->vc_font.height * width; + unsigned int maxcnt = info->pixmap.size/cellsize; + unsigned int scan_align = info->pixmap.scan_align - 1; + unsigned int buf_align = info->pixmap.buf_align - 1; + unsigned int shift_low = 0, mod = vc->vc_font.width % 8; + unsigned int shift_high = 8, pitch, cnt, size, k; + unsigned int idx = vc->vc_font.width >> 3; + unsigned int attribute = get_attribute(info, scr_readw(s)); + struct fb_image image; + u8 *src, *dst, *buf = NULL; + + if (attribute) { + buf = kmalloc(cellsize, GFP_KERNEL); + if (!buf) + return; + } + + image.fg_color = fg; + image.bg_color = bg; + + image.dx = xx * vc->vc_font.width; + image.dy = yy * vc->vc_font.height; + image.height = vc->vc_font.height; + image.depth = 1; + + if (info->pixmap.outbuf && info->pixmap.inbuf) { + move_aligned = fb_iomove_buf_aligned; + move_unaligned = fb_iomove_buf_unaligned; + } else { + move_aligned = fb_sysmove_buf_aligned; + move_unaligned = fb_sysmove_buf_unaligned; + } + while (count) { + if (count > maxcnt) + cnt = k = maxcnt; + else + cnt = k = count; + + image.width = vc->vc_font.width * cnt; + pitch = ((image.width + 7) >> 3) + scan_align; + pitch &= ~scan_align; + size = pitch * image.height + buf_align; + size &= ~buf_align; + dst = fb_get_buffer_offset(info, &info->pixmap, size); + image.data = dst; + if (mod) { + while (k--) { + src = vc->vc_font.data + (scr_readw(s++)& + charmask)*cellsize; + + if (attribute) { + update_attr(buf, src, attribute, vc); + src = buf; + } + + move_unaligned(info, &info->pixmap, dst, pitch, + src, idx, image.height, + shift_high, shift_low, mod); + shift_low += mod; + dst += (shift_low >= 8) ? width : width - 1; + shift_low &= 7; + shift_high = 8 - shift_low; + } + } else { + while (k--) { + src = vc->vc_font.data + (scr_readw(s++)& + charmask)*cellsize; + + if (attribute) { + update_attr(buf, src, attribute, vc); + src = buf; + } + + move_aligned(info, &info->pixmap, dst, pitch, + src, idx, image.height); + dst += width; + } + } + info->fbops->fb_imageblit(info, &image); + image.dx += cnt * vc->vc_font.width; + count -= cnt; + } + + if (buf) + kfree(buf); +} + +static void bit_clear_margins(struct vc_data *vc, struct fb_info *info, + int bottom_only) +{ + int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; + unsigned int cw = vc->vc_font.width; + unsigned int ch = vc->vc_font.height; + unsigned int rw = info->var.xres - (vc->vc_cols*cw); + unsigned int bh = info->var.yres - (vc->vc_rows*ch); + unsigned int rs = info->var.xres - rw; + unsigned int bs = info->var.yres - bh; + struct fb_fillrect region; + + region.color = attr_bgcol_ec(bgshift, vc); + region.rop = ROP_COPY; + + if (rw && !bottom_only) { + region.dx = info->var.xoffset + rs; + region.dy = 0; + region.width = rw; + region.height = info->var.yres_virtual; + info->fbops->fb_fillrect(info, ®ion); + } + + if (bh) { + region.dx = info->var.xoffset; + region.dy = info->var.yoffset + bs; + region.width = rs; + region.height = bh; + info->fbops->fb_fillrect(info, ®ion); + } +} + +static void bit_cursor(struct vc_data *vc, struct fb_info *info, + struct display *p, int mode, int fg, int bg) +{ + struct fb_cursor cursor; + unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; + int w = (vc->vc_font.width + 7) >> 3, c; + int y = real_y(p, vc->vc_y); + int attribute; + char *src; + + c = scr_readw((u16 *) vc->vc_pos); + attribute = get_attribute(info, c); + src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height)); + if (attribute) { + u8 *dst; + + dst = kmalloc(w * vc->vc_font.height, GFP_ATOMIC); + if (!dst) + return; + if (info->cursor.data) + kfree(info->cursor.data); + info->cursor.data = dst; + update_attr(dst, src, attribute, vc); + src = dst; + } + + cursor.image.data = src; + cursor.set = FB_CUR_SETCUR; + cursor.image.depth = 1; + + switch (mode) { + case CM_ERASE: + if (info->cursor.rop == ROP_XOR) { + info->cursor.enable = 0; + info->cursor.rop = ROP_COPY; + info->fbops->fb_cursor(info, &cursor); + } + break; + case CM_MOVE: + case CM_DRAW: + info->cursor.enable = 1; + info->cursor.rop = ROP_XOR; + + if (info->cursor.image.fg_color != fg || + info->cursor.image.bg_color != bg) { + cursor.image.fg_color = fg; + cursor.image.bg_color = bg; + cursor.set |= FB_CUR_SETCMAP; + } + + if ((info->cursor.image.dx != (vc->vc_font.width * vc->vc_x)) || + (info->cursor.image.dy != (vc->vc_font.height * y))) { + cursor.image.dx = vc->vc_font.width * vc->vc_x; + cursor.image.dy = vc->vc_font.height * y; + cursor.set |= FB_CUR_SETPOS; + } + + if (info->cursor.image.height != vc->vc_font.height || + info->cursor.image.width != vc->vc_font.width) { + cursor.image.height = vc->vc_font.height; + cursor.image.width = vc->vc_font.width; + cursor.set |= FB_CUR_SETSIZE; + } + + if (info->cursor.hot.x || info->cursor.hot.y) { + cursor.hot.x = cursor.hot.y = 0; + cursor.set |= FB_CUR_SETHOT; + } + + if ((cursor.set & FB_CUR_SETSIZE) || + ((vc->vc_cursor_type & 0x0f) != p->cursor_shape) + || info->cursor.mask == NULL) { + char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC); + int cur_height, size, i = 0; + u8 msk = 0xff; + + if (!mask) + return; + + if (info->cursor.mask) + kfree(info->cursor.mask); + info->cursor.mask = mask; + p->cursor_shape = vc->vc_cursor_type & 0x0f; + cursor.set |= FB_CUR_SETSHAPE; + + switch (vc->vc_cursor_type & 0x0f) { + case CUR_NONE: + cur_height = 0; + break; + case CUR_UNDERLINE: + cur_height = (vc->vc_font.height < 10) ? 1 : 2; + break; + case CUR_LOWER_THIRD: + cur_height = vc->vc_font.height/3; + break; + case CUR_LOWER_HALF: + cur_height = vc->vc_font.height >> 1; + break; + case CUR_TWO_THIRDS: + cur_height = (vc->vc_font.height << 1)/3; + break; + case CUR_BLOCK: + default: + cur_height = vc->vc_font.height; + break; + } + size = (vc->vc_font.height - cur_height) * w; + while (size--) + mask[i++] = ~msk; + size = cur_height * w; + while (size--) + mask[i++] = msk; + } + info->fbops->fb_cursor(info, &cursor); + break; + } +} + +void fbcon_set_bitops(struct fbcon_ops *ops) +{ + ops->bmove = bit_bmove; + ops->clear = bit_clear; + ops->putcs = bit_putcs; + ops->clear_margins = bit_clear_margins; + ops->cursor = bit_cursor; +} + +EXPORT_SYMBOL(fbcon_set_bitops); + +MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>"); +MODULE_DESCRIPTION("Bit Blitting Operation"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 2fba5cfcbcee..aa4019d5a4e7 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -102,6 +102,7 @@ struct display fb_display[MAX_NR_CONSOLES]; signed char con2fb_map[MAX_NR_CONSOLES]; +signed char con2fb_map_boot[MAX_NR_CONSOLES]; static int logo_height; static int logo_lines; static int logo_shown = -1; @@ -119,13 +120,7 @@ static int fbcon_is_default = 1; static char fontname[40]; /* current fb_info */ -static int info_idx = -1; - -#define REFCOUNT(fd) (((int *)(fd))[-1]) -#define FNTSIZE(fd) (((int *)(fd))[-2]) -#define FNTCHARCNT(fd) (((int *)(fd))[-3]) -#define FNTSUM(fd) (((int *)(fd))[-4]) -#define FONT_EXTRA_WORDS 4 +static int info_idx = -1; #define CM_SOFTBACK (8) @@ -158,6 +153,7 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos); static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, int count, int ypos, int xpos); +static void fbcon_clear_margins(struct vc_data *vc, int bottom_only); static void fbcon_cursor(struct vc_data *vc, int mode); static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, int count); @@ -167,9 +163,6 @@ static int fbcon_switch(struct vc_data *vc); static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch); static int fbcon_set_palette(struct vc_data *vc, unsigned char *table); static int fbcon_scrolldelta(struct vc_data *vc, int lines); -void accel_clear_margins(struct vc_data *vc, struct fb_info *info, - int bottom_only); - /* * Internal routines @@ -182,6 +175,7 @@ static __inline__ void ypan_down(struct vc_data *vc, int count); static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx, int dy, int dx, int height, int width, u_int y_break); static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc); +static void fbcon_preset_disp(struct fb_info *info, int unit); static void fbcon_redraw_move(struct vc_data *vc, struct display *p, int line, int count, int dy); @@ -198,21 +192,69 @@ static irqreturn_t fb_vbl_detect(int irq, void *dummy, struct pt_regs *fp) } #endif +static inline int get_color(struct vc_data *vc, struct fb_info *info, + u16 c, int is_fg) +{ + int depth = fb_get_color_depth(info); + int color = 0; + + if (depth != 1) + color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c) + : attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c); + + switch (depth) { + case 1: + { + /* 0 or 1 */ + int fg = (info->fix.visual != FB_VISUAL_MONO01) ? 1 : 0; + int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : 1; + + color = (is_fg) ? fg : bg; + break; + } + case 2: + /* + * Scale down 16-colors to 4 colors. Default 4-color palette + * is grayscale. + */ + color /= 4; + break; + case 3: + /* + * Last 8 entries of default 16-color palette is a more intense + * version of the first 8 (i.e., same chrominance, different + * luminance). + */ + color &= 7; + break; + } + + return color; +} + static void fb_flashcursor(void *private) { struct fb_info *info = (struct fb_info *) private; + struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par; + struct display *p; struct vc_data *vc = NULL; + int c; + int mode; if (info->currcon != -1) vc = vc_cons[info->currcon].d; if (info->state != FBINFO_STATE_RUNNING || - info->cursor.rop == ROP_COPY || !vc || !CON_IS_VISIBLE(vc) - || registered_fb[(int) con2fb_map[vc->vc_num]] != info) + !vc || !CON_IS_VISIBLE(vc) || !info->cursor.flash || + vt_cons[vc->vc_num]->vc_mode != KD_TEXT || + registered_fb[(int) con2fb_map[vc->vc_num]] != info) return; + p = &fb_display[vc->vc_num]; + c = scr_readw((u16 *) vc->vc_pos); acquire_console_sem(); - info->cursor.enable ^= 1; - info->fbops->fb_cursor(info, &info->cursor); + mode = (info->cursor.enable) ? CM_ERASE : CM_DRAW; + ops->cursor(vc, info, p, mode, get_color(vc, info, c, 1), + get_color(vc, info, c, 0)); release_console_sem(); } @@ -271,7 +313,8 @@ int __init fb_console_setup(char *this_opt) for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) { if (!options[j]) j = 0; - con2fb_map[i] = (options[j++]-'0') % FB_MAX; + con2fb_map_boot[i] = + (options[j++]-'0') % FB_MAX; } return 0; } @@ -314,13 +357,16 @@ static int search_for_mapped_con(void) return 0; } -static int fbcon_takeover(void) +static int fbcon_takeover(int show_logo) { int err, i; if (!num_registered_fb) return -ENODEV; + if (!show_logo) + logo_shown = -3; + for (i = first_fb_vc; i <= last_fb_vc; i++) con2fb_map[i] = info_idx; @@ -336,15 +382,112 @@ static int fbcon_takeover(void) return err; } +static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, + int cols, int rows, int new_cols, int new_rows) +{ + /* Need to make room for the logo */ + int cnt, erase = vc->vc_video_erase_char, step; + unsigned short *save = NULL, *r, *q; + + /* + * remove underline attribute from erase character + * if black and white framebuffer. + */ + if (fb_get_color_depth(info) == 1) + erase &= ~0x400; + logo_height = fb_prepare_logo(info); + logo_lines = (logo_height + vc->vc_font.height - 1) / + vc->vc_font.height; + q = (unsigned short *) (vc->vc_origin + + vc->vc_size_row * rows); + step = logo_lines * cols; + for (r = q - logo_lines * cols; r < q; r++) + if (scr_readw(r) != vc->vc_video_erase_char) + break; + if (r != q && new_rows >= rows + logo_lines) { + save = kmalloc(logo_lines * new_cols * 2, GFP_KERNEL); + if (save) { + int i = cols < new_cols ? cols : new_cols; + scr_memsetw(save, erase, logo_lines * new_cols * 2); + r = q - step; + for (cnt = 0; cnt < logo_lines; cnt++, r += i) + scr_memcpyw(save + cnt * new_cols, r, 2 * i); + r = q; + } + } + if (r == q) { + /* We can scroll screen down */ + r = q - step - cols; + for (cnt = rows - logo_lines; cnt > 0; cnt--) { + scr_memcpyw(r + step, r, vc->vc_size_row); + r -= cols; + } + if (!save) { + vc->vc_y += logo_lines; + vc->vc_pos += logo_lines * vc->vc_size_row; + } + } + scr_memsetw((unsigned short *) vc->vc_origin, + erase, + vc->vc_size_row * logo_lines); + + if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) { + fbcon_clear_margins(vc, 0); + update_screen(vc->vc_num); + } + + if (save) { + q = (unsigned short *) (vc->vc_origin + + vc->vc_size_row * + rows); + scr_memcpyw(q, save, logo_lines * new_cols * 2); + vc->vc_y += logo_lines; + vc->vc_pos += logo_lines * vc->vc_size_row; + kfree(save); + } + + if (logo_lines > vc->vc_bottom) { + logo_shown = -1; + printk(KERN_INFO + "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n"); + } else if (logo_shown != -3) { + logo_shown = -2; + vc->vc_top = logo_lines; + } +} + +#ifdef CONFIG_FB_TILEBLITTING +static void set_blitting_type(struct vc_data *vc, struct fb_info *info, + struct display *p) +{ + struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par; + + if ((info->flags & FBINFO_MISC_TILEBLITTING)) + fbcon_set_tileops(vc, info, p, ops); + else + fbcon_set_bitops(ops); +} +#else +static void set_blitting_type(struct vc_data *vc, struct fb_info *info, + struct display *p) +{ + struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par; + + info->flags &= ~FBINFO_MISC_TILEBLITTING; + fbcon_set_bitops(ops); +} +#endif /* CONFIG_MISC_TILEBLITTING */ + /** * set_con2fb_map - map console to frame buffer device * @unit: virtual console number to map * @newidx: frame buffer index to map virtual console to + * @user: user request * * Maps a virtual console @unit to a frame buffer device * @newidx. */ -static int set_con2fb_map(int unit, int newidx) +static int set_con2fb_map(int unit, int newidx, int user) { struct vc_data *vc = vc_cons[unit].d; int oldidx = con2fb_map[unit]; @@ -355,12 +498,12 @@ static int set_con2fb_map(int unit, int newidx) if (oldidx == newidx) return 0; - if (!vc) - return -ENODEV; + if (!info) + return -EINVAL; if (!search_for_mapped_con()) { info_idx = newidx; - return fbcon_takeover(); + return fbcon_takeover(0); } if (oldidx != -1) @@ -370,17 +513,35 @@ static int set_con2fb_map(int unit, int newidx) acquire_console_sem(); con2fb_map[unit] = newidx; + if (!found) { + struct fbcon_ops *ops = NULL; + int err = 0; if (!try_module_get(info->fbops->owner)) { - con2fb_map[unit] = oldidx; - release_console_sem(); - return -ENODEV; + err = -ENODEV; } - if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) { - module_put(info->fbops->owner); + + if (!err && info->fbops->fb_open && + info->fbops->fb_open(info, 0)) { + err = -ENODEV; + } + + if (!err) { + ops = kmalloc(sizeof(struct fbcon_ops), GFP_KERNEL); + if (!ops) + err = -ENOMEM; + } + + if (!err) { + info->fbcon_par = ops; + set_blitting_type(vc, info, NULL); + } + + if (err) { con2fb_map[unit] = oldidx; + module_put(info->fbops->owner); release_console_sem(); - return -ENODEV; + return err; } } @@ -399,11 +560,14 @@ static int set_con2fb_map(int unit, int newidx) release_console_sem(); return -ENODEV; } + if (oldinfo->queue.func == fb_flashcursor) del_timer_sync(&oldinfo->cursor_timer); + + kfree(oldinfo->fbcon_par); module_put(oldinfo->fbops->owner); } - info->currcon = -1; + if (!found) { if (!info->queue.func || info->queue.func == fb_flashcursor) { if (!info->queue.func) @@ -416,259 +580,31 @@ static int set_con2fb_map(int unit, int newidx) add_timer(&info->cursor_timer); } } - if (info->fbops->fb_set_par) - info->fbops->fb_set_par(info); - fbcon_set_disp(info, vc); - release_console_sem(); - return 0; -} - -/* - * Accelerated handlers. - */ -static inline int get_color(struct vc_data *vc, struct fb_info *info, - u16 c, int is_fg) -{ - int depth = fb_get_color_depth(info); - int color = 0; - - if (depth != 1) - color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c) - : attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c); - - switch (depth) { - case 1: - { - /* 0 or 1 */ - int fg = (info->fix.visual != FB_VISUAL_MONO01) ? 1 : 0; - int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : 1; - - color = (is_fg) ? fg : bg; - break; - } - case 2: - /* - * Scale down 16-colors to 4 colors. Default 4-color palette - * is grayscale. - */ - color /= 4; - break; - case 3: - /* - * Last 8 entries of default 16-color palette is a more intense - * version of the first 8 (i.e., same chrominance, different - * luminance). - */ - color &= 7; - break; - } - return color; -} + info->currcon = fg_console; + con2fb_map_boot[unit] = newidx; -#define FBCON_ATTRIBUTE_UNDERLINE 1 -#define FBCON_ATTRIBUTE_REVERSE 2 -#define FBCON_ATTRIBUTE_BOLD 4 - -static inline int get_attribute(struct fb_info *info, u16 c) -{ - int attribute = 0; - - if (fb_get_color_depth(info) == 1) { - if (attr_underline(c)) - attribute |= FBCON_ATTRIBUTE_UNDERLINE; - if (attr_reverse(c)) - attribute |= FBCON_ATTRIBUTE_REVERSE; - if (attr_bold(c)) - attribute |= FBCON_ATTRIBUTE_BOLD; - } - - return attribute; -} - -static inline void update_attr(u8 *dst, u8 *src, int attribute, - struct vc_data *vc) -{ - int i, offset = (vc->vc_font.height < 10) ? 1 : 2; - int width = (vc->vc_font.width + 7) >> 3; - unsigned int cellsize = vc->vc_font.height * width; - u8 c; - - offset = cellsize - (offset * width); - for (i = 0; i < cellsize; i++) { - c = src[i]; - if (attribute & FBCON_ATTRIBUTE_UNDERLINE && i >= offset) - c = 0xff; - if (attribute & FBCON_ATTRIBUTE_BOLD) - c |= c >> 1; - if (attribute & FBCON_ATTRIBUTE_REVERSE) - c = ~c; - dst[i] = c; - } -} - -void accel_bmove(struct vc_data *vc, struct fb_info *info, int sy, - int sx, int dy, int dx, int height, int width) -{ - struct fb_copyarea area; - - area.sx = sx * vc->vc_font.width; - area.sy = sy * vc->vc_font.height; - area.dx = dx * vc->vc_font.width; - area.dy = dy * vc->vc_font.height; - area.height = height * vc->vc_font.height; - area.width = width * vc->vc_font.width; - - info->fbops->fb_copyarea(info, &area); -} - -void accel_clear(struct vc_data *vc, struct fb_info *info, int sy, - int sx, int height, int width) -{ - int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; - struct fb_fillrect region; - - region.color = attr_bgcol_ec(bgshift, vc); - region.dx = sx * vc->vc_font.width; - region.dy = sy * vc->vc_font.height; - region.width = width * vc->vc_font.width; - region.height = height * vc->vc_font.height; - region.rop = ROP_COPY; - - info->fbops->fb_fillrect(info, ®ion); -} - -void accel_putcs(struct vc_data *vc, struct fb_info *info, - const unsigned short *s, int count, int yy, int xx) -{ - void (*move_unaligned)(struct fb_info *info, struct fb_pixmap *buf, - u8 *dst, u32 d_pitch, u8 *src, u32 idx, - u32 height, u32 shift_high, u32 shift_low, - u32 mod); - void (*move_aligned)(struct fb_info *info, struct fb_pixmap *buf, - u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, - u32 height); - unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; - unsigned int width = (vc->vc_font.width + 7) >> 3; - unsigned int cellsize = vc->vc_font.height * width; - unsigned int maxcnt = info->pixmap.size/cellsize; - unsigned int scan_align = info->pixmap.scan_align - 1; - unsigned int buf_align = info->pixmap.buf_align - 1; - unsigned int shift_low = 0, mod = vc->vc_font.width % 8; - unsigned int shift_high = 8, pitch, cnt, size, k; - unsigned int idx = vc->vc_font.width >> 3; - unsigned int attribute = get_attribute(info, scr_readw(s)); - struct fb_image image; - u8 *src, *dst, *buf = NULL; - - if (attribute) { - buf = kmalloc(cellsize, GFP_KERNEL); - if (!buf) - return; - } - - image.fg_color = get_color(vc, info, scr_readw(s), 1); - image.bg_color = get_color(vc, info, scr_readw(s), 0); - - image.dx = xx * vc->vc_font.width; - image.dy = yy * vc->vc_font.height; - image.height = vc->vc_font.height; - image.depth = 1; - - if (info->pixmap.outbuf && info->pixmap.inbuf) { - move_aligned = fb_iomove_buf_aligned; - move_unaligned = fb_iomove_buf_unaligned; - } else { - move_aligned = fb_sysmove_buf_aligned; - move_unaligned = fb_sysmove_buf_unaligned; - } - while (count) { - if (count > maxcnt) - cnt = k = maxcnt; - else - cnt = k = count; - - image.width = vc->vc_font.width * cnt; - pitch = ((image.width + 7) >> 3) + scan_align; - pitch &= ~scan_align; - size = pitch * image.height + buf_align; - size &= ~buf_align; - dst = fb_get_buffer_offset(info, &info->pixmap, size); - image.data = dst; - if (mod) { - while (k--) { - src = vc->vc_font.data + (scr_readw(s++)& - charmask)*cellsize; - - if (attribute) { - update_attr(buf, src, attribute, vc); - src = buf; - } + if (info->fbops->fb_set_par) + info->fbops->fb_set_par(info); - move_unaligned(info, &info->pixmap, dst, pitch, - src, idx, image.height, - shift_high, shift_low, mod); - shift_low += mod; - dst += (shift_low >= 8) ? width : width - 1; - shift_low &= 7; - shift_high = 8 - shift_low; - } - } else { - while (k--) { - src = vc->vc_font.data + (scr_readw(s++)& - charmask)*cellsize; + if (vc) + fbcon_set_disp(info, vc); + else + fbcon_preset_disp(info, unit); - if (attribute) { - update_attr(buf, src, attribute, vc); - src = buf; - } + if (fg_console == 0 && !user && logo_shown != -3) { + struct vc_data *vc = vc_cons[fg_console].d; + struct fb_info *fg_info = registered_fb[(int) con2fb_map[fg_console]]; - move_aligned(info, &info->pixmap, dst, pitch, - src, idx, image.height); - dst += width; - } - } - info->fbops->fb_imageblit(info, &image); - image.dx += cnt * vc->vc_font.width; - count -= cnt; + fbcon_prepare_logo(vc, fg_info, vc->vc_cols, vc->vc_rows, + vc->vc_cols, vc->vc_rows); } - if (buf) - kfree(buf); + switch_screen(fg_console); + release_console_sem(); + return 0; } -void accel_clear_margins(struct vc_data *vc, struct fb_info *info, - int bottom_only) -{ - int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; - unsigned int cw = vc->vc_font.width; - unsigned int ch = vc->vc_font.height; - unsigned int rw = info->var.xres - (vc->vc_cols*cw); - unsigned int bh = info->var.yres - (vc->vc_rows*ch); - unsigned int rs = info->var.xres - rw; - unsigned int bs = info->var.yres - bh; - struct fb_fillrect region; - - region.color = attr_bgcol_ec(bgshift, vc); - region.rop = ROP_COPY; - - if (rw && !bottom_only) { - region.dx = info->var.xoffset + rs; - region.dy = 0; - region.width = rw; - region.height = info->var.yres_virtual; - info->fbops->fb_fillrect(info, ®ion); - } - - if (bh) { - region.dx = info->var.xoffset; - region.dy = info->var.yoffset + bs; - region.width = rs; - region.height = bh; - info->fbops->fb_fillrect(info, ®ion); - } -} - /* * Low Level Operations */ @@ -722,6 +658,7 @@ static const char *fbcon_startup(void) struct font_desc *font = NULL; struct module *owner; struct fb_info *info = NULL; + struct fbcon_ops *ops; int rows, cols; int irqres; @@ -748,6 +685,16 @@ static const char *fbcon_startup(void) module_put(owner); return NULL; } + + ops = kmalloc(sizeof(struct fbcon_ops), GFP_KERNEL); + if (!ops) { + module_put(owner); + return NULL; + } + + info->fbcon_par = ops; + set_blitting_type(vc, info, NULL); + if (info->fix.type != FB_TYPE_TEXT) { if (fbcon_softback_size) { if (!softback_buf) { @@ -777,7 +724,7 @@ static const char *fbcon_startup(void) if (!p->fontdata) { if (!fontname[0] || !(font = find_font(fontname))) font = get_default_font(info->var.xres, - info->var.yres); + info->var.yres); vc->vc_font.width = font->width; vc->vc_font.height = font->height; vc->vc_font.data = p->fontdata = font->data; @@ -793,7 +740,6 @@ static const char *fbcon_startup(void) DPRINTK("res: %dx%d-%d\n", info->var.xres, info->var.yres, info->var.bits_per_pixel); - con_set_default_unimap(vc->vc_num); #ifdef CONFIG_ATARI if (MACH_IS_ATARI) { @@ -873,15 +819,15 @@ static void fbcon_init(struct vc_data *vc, int init) { struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; struct vc_data **default_mode = vc->vc_display_fg; + struct vc_data *svc = *default_mode; struct display *t, *p = &fb_display[vc->vc_num]; int display_fg = (*default_mode)->vc_num; int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256; - unsigned short *save = NULL, *r, *q; int cap = info->flags; if (info_idx == -1 || info == NULL) return; - if (vc->vc_num != display_fg || (info->flags & FBINFO_MODULE) || + if (vc->vc_num != display_fg || logo_shown == -3 || (info->fix.type == FB_TYPE_TEXT)) logo = 0; @@ -900,7 +846,6 @@ static void fbcon_init(struct vc_data *vc, int init) p->userfont = t->userfont; if (p->userfont) REFCOUNT(p->fontdata)++; - con_copy_unimap(vc->vc_num, display_fg); } if (p->userfont) charcnt = FNTCHARCNT(p->fontdata); @@ -913,6 +858,12 @@ static void fbcon_init(struct vc_data *vc, int init) if (vc->vc_can_do_color) vc->vc_complement_mask <<= 1; } + + if (!*svc->vc_uni_pagedir_loc) + con_set_default_unimap(display_fg); + if (!*vc->vc_uni_pagedir_loc) + con_copy_unimap(vc->vc_num, display_fg); + cols = vc->vc_cols; rows = vc->vc_rows; new_cols = info->var.xres / vc->vc_font.width; @@ -945,75 +896,8 @@ static void fbcon_init(struct vc_data *vc, int init) vc->vc_rows = new_rows; } - if (logo) { - /* Need to make room for the logo */ - int cnt, erase = vc->vc_video_erase_char; - int step; - - /* - * remove underline attribute from erase character - * if black and white framebuffer. - */ - if (fb_get_color_depth(info) == 1) - erase &= ~0x400; - logo_height = fb_prepare_logo(info); - logo_lines = (logo_height + vc->vc_font.height - 1) / - vc->vc_font.height; - q = (unsigned short *) (vc->vc_origin + - vc->vc_size_row * rows); - step = logo_lines * cols; - for (r = q - logo_lines * cols; r < q; r++) - if (scr_readw(r) != vc->vc_video_erase_char) - break; - if (r != q && new_rows >= rows + logo_lines) { - save = kmalloc(logo_lines * new_cols * 2, GFP_KERNEL); - if (save) { - int i = cols < new_cols ? cols : new_cols; - scr_memsetw(save, erase, logo_lines * new_cols * 2); - r = q - step; - for (cnt = 0; cnt < logo_lines; cnt++, r += i) - scr_memcpyw(save + cnt * new_cols, r, 2 * i); - r = q; - } - } - if (r == q) { - /* We can scroll screen down */ - r = q - step - cols; - for (cnt = rows - logo_lines; cnt > 0; cnt--) { - scr_memcpyw(r + step, r, vc->vc_size_row); - r -= cols; - } - if (!save) { - vc->vc_y += logo_lines; - vc->vc_pos += logo_lines * vc->vc_size_row; - } - } - scr_memsetw((unsigned short *) vc->vc_origin, - erase, - vc->vc_size_row * logo_lines); - - if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) { - accel_clear_margins(vc, info, 0); - update_screen(vc->vc_num); - } - if (save) { - q = (unsigned short *) (vc->vc_origin + - vc->vc_size_row * - rows); - scr_memcpyw(q, save, logo_lines * new_cols * 2); - vc->vc_y += logo_lines; - vc->vc_pos += logo_lines * vc->vc_size_row; - kfree(save); - } - if (logo_lines > vc->vc_bottom) { - logo_shown = -1; - printk(KERN_INFO - "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n"); - } else { - logo_shown = -2; - vc->vc_top = logo_lines; - } - } + if (logo) + fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows); if (vc->vc_num == display_fg && softback_buf) { int l = fbcon_softback_size / vc->vc_size_row; @@ -1043,7 +927,7 @@ static void fbcon_deinit(struct vc_data *vc) * This system is now divided into two levels because of complications * caused by hardware scrolling. Top level functions: * - * fbcon_bmove(), fbcon_clear(), fbcon_putc() + * fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins() * * handles y values in range [0, scr_height-1] that correspond to real * screen positions. y_wrap shift means that first line of bitmap may be @@ -1074,7 +958,8 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, int width) { struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; - + struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par; + struct display *p = &fb_display[vc->vc_num]; u_int y_break; @@ -1091,11 +976,11 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, y_break = p->vrows - p->yscroll; if (sy < y_break && sy + height - 1 >= y_break) { u_int b = y_break - sy; - accel_clear(vc, info, real_y(p, sy), sx, b, width); - accel_clear(vc, info, real_y(p, sy + b), sx, height - b, + ops->clear(vc, info, real_y(p, sy), sx, b, width); + ops->clear(vc, info, real_y(p, sy + b), sx, height - b, width); } else - accel_clear(vc, info, real_y(p, sy), sx, height, width); + ops->clear(vc, info, real_y(p, sy), sx, height, width); } static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, @@ -1103,6 +988,7 @@ static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, { struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; + struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par; if (!info->fbops->fb_blank && console_blanked) return; @@ -1112,7 +998,9 @@ static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT) return; - accel_putcs(vc, info, s, count, real_y(p, ypos), xpos); + ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, + get_color(vc, info, scr_readw(s), 1), + get_color(vc, info, scr_readw(s), 0)); } static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos) @@ -1120,137 +1008,39 @@ static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos) fbcon_putcs(vc, (const unsigned short *) &c, 1, ypos, xpos); } +static void fbcon_clear_margins(struct vc_data *vc, int bottom_only) +{ + struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par; + + ops->clear_margins(vc, info, bottom_only); +} + static void fbcon_cursor(struct vc_data *vc, int mode) { - struct fb_cursor cursor; struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; - unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; + struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par; struct display *p = &fb_display[vc->vc_num]; - int w = (vc->vc_font.width + 7) >> 3, c; - int y = real_y(p, vc->vc_y), fg, bg; - int attribute; - u8 *src; + int y = real_y(p, vc->vc_y); + int c = scr_readw((u16 *) vc->vc_pos); + info->cursor.flash = 1; if (mode & CM_SOFTBACK) { mode &= ~CM_SOFTBACK; if (softback_lines) { - if (y + softback_lines >= vc->vc_rows) + if (y + softback_lines >= vc->vc_rows) { mode = CM_ERASE; + info->cursor.flash = 0; + } else y += softback_lines; } } else if (softback_lines) fbcon_set_origin(vc); - c = scr_readw((u16 *) vc->vc_pos); - attribute = get_attribute(info, c); - src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height)); - if (attribute) { - u8 *dst; - - dst = kmalloc(w * vc->vc_font.height, GFP_ATOMIC); - if (!dst) - return; - if (info->cursor.data) - kfree(info->cursor.data); - info->cursor.data = dst; - update_attr(dst, src, attribute, vc); - src = dst; - } - - cursor.image.data = src; - cursor.set = FB_CUR_SETCUR; - cursor.image.depth = 1; - - switch (mode) { - case CM_ERASE: - if (info->cursor.rop == ROP_XOR) { - info->cursor.enable = 0; - info->cursor.rop = ROP_COPY; - info->fbops->fb_cursor(info, &cursor); - } - break; - case CM_MOVE: - case CM_DRAW: - info->cursor.enable = 1; - fg = get_color(vc, info, c, 1); - bg = get_color(vc, info, c, 0); - - if (info->cursor.image.fg_color != fg || - info->cursor.image.bg_color != bg) { - cursor.image.fg_color = fg; - cursor.image.bg_color = bg; - cursor.set |= FB_CUR_SETCMAP; - } - - if ((info->cursor.image.dx != (vc->vc_font.width * vc->vc_x)) || - (info->cursor.image.dy != (vc->vc_font.height * y))) { - cursor.image.dx = vc->vc_font.width * vc->vc_x; - cursor.image.dy = vc->vc_font.height * y; - cursor.set |= FB_CUR_SETPOS; - } - - if (info->cursor.image.height != vc->vc_font.height || - info->cursor.image.width != vc->vc_font.width) { - cursor.image.height = vc->vc_font.height; - cursor.image.width = vc->vc_font.width; - cursor.set |= FB_CUR_SETSIZE; - } - - if (info->cursor.hot.x || info->cursor.hot.y) { - cursor.hot.x = cursor.hot.y = 0; - cursor.set |= FB_CUR_SETHOT; - } - - if ((cursor.set & FB_CUR_SETSIZE) || - ((vc->vc_cursor_type & 0x0f) != p->cursor_shape) - || info->cursor.mask == NULL) { - char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC); - int cur_height, size, i = 0; - u8 msk = 0xff; - - if (!mask) - return; - - if (info->cursor.mask) - kfree(info->cursor.mask); - info->cursor.mask = mask; - p->cursor_shape = vc->vc_cursor_type & 0x0f; - cursor.set |= FB_CUR_SETSHAPE; - - switch (vc->vc_cursor_type & 0x0f) { - case CUR_NONE: - cur_height = 0; - break; - case CUR_UNDERLINE: - cur_height = (vc->vc_font.height < 10) ? 1 : 2; - break; - case CUR_LOWER_THIRD: - cur_height = vc->vc_font.height/3; - break; - case CUR_LOWER_HALF: - cur_height = vc->vc_font.height >> 1; - break; - case CUR_TWO_THIRDS: - cur_height = (vc->vc_font.height << 1)/3; - break; - case CUR_BLOCK: - default: - cur_height = vc->vc_font.height; - break; - } - size = (vc->vc_font.height - cur_height) * w; - while (size--) - mask[i++] = ~msk; - size = cur_height * w; - while (size--) - mask[i++] = msk; - } - info->cursor.rop = ROP_XOR; - info->fbops->fb_cursor(info, &cursor); - vbl_cursor_cnt = CURSOR_DRAW_DELAY; - break; - } + ops->cursor(vc, info, p, mode, get_color(vc, info, c, 1), + get_color(vc, info, c, 0)); + vbl_cursor_cnt = CURSOR_DRAW_DELAY; } static int scrollback_phys_max = 0; @@ -1264,10 +1054,29 @@ int update_var(int con, struct fb_info *info) return 0; } +/* + * If no vc is existent yet, just set struct display + */ +static void fbcon_preset_disp(struct fb_info *info, int unit) +{ + struct display *p = &fb_display[unit]; + struct display *t = &fb_display[fg_console]; + + info->var.xoffset = info->var.yoffset = p->yscroll = 0; + if (var_to_display(p, &info->var, info)) + return; + + p->fontdata = t->fontdata; + p->userfont = t->userfont; + if (p->userfont) + REFCOUNT(p->fontdata)++; +} + static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc) { struct display *p = &fb_display[vc->vc_num], *t; struct vc_data **default_mode = vc->vc_display_fg; + struct vc_data *svc = *default_mode; int display_fg = (*default_mode)->vc_num; int rows, cols, charcnt = 256; @@ -1282,7 +1091,6 @@ static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc) p->userfont = t->userfont; if (p->userfont) REFCOUNT(p->fontdata)++; - con_copy_unimap(vc->vc_num, display_fg); } if (p->userfont) charcnt = FNTCHARCNT(p->fontdata); @@ -1296,6 +1104,12 @@ static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc) if (vc->vc_can_do_color) vc->vc_complement_mask <<= 1; } + + if (!*svc->vc_uni_pagedir_loc) + con_set_default_unimap(display_fg); + if (!*vc->vc_uni_pagedir_loc) + con_copy_unimap(vc->vc_num, display_fg); + cols = info->var.xres / vc->vc_font.width; rows = info->var.yres / vc->vc_font.height; vc_resize(vc->vc_num, cols, rows); @@ -1314,7 +1128,6 @@ static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc) } } } - switch_screen(fg_console); } static __inline__ void ywrap_up(struct vc_data *vc, int count) @@ -1357,18 +1170,19 @@ static __inline__ void ypan_up(struct vc_data *vc, int count) { struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; - + struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par; + p->yscroll += count; if (p->yscroll > p->vrows - vc->vc_rows) { - accel_bmove(vc, info, p->vrows - vc->vc_rows, - 0, 0, 0, vc->vc_rows, vc->vc_cols); + ops->bmove(vc, info, p->vrows - vc->vc_rows, + 0, 0, 0, vc->vc_rows, vc->vc_cols); p->yscroll -= p->vrows - vc->vc_rows; } info->var.xoffset = 0; info->var.yoffset = p->yscroll * vc->vc_font.height; info->var.vmode &= ~FB_VMODE_YWRAP; update_var(vc->vc_num, info); - accel_clear_margins(vc, info, 1); + fbcon_clear_margins(vc, 1); scrollback_max += count; if (scrollback_max > scrollback_phys_max) scrollback_max = scrollback_phys_max; @@ -1393,7 +1207,7 @@ static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count) if (redraw) fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t); update_var(vc->vc_num, info); - accel_clear_margins(vc, info, 1); + fbcon_clear_margins(vc, 1); scrollback_max += count; if (scrollback_max > scrollback_phys_max) scrollback_max = scrollback_phys_max; @@ -1404,18 +1218,19 @@ static __inline__ void ypan_down(struct vc_data *vc, int count) { struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; + struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par; p->yscroll -= count; if (p->yscroll < 0) { - accel_bmove(vc, info, 0, 0, p->vrows - vc->vc_rows, - 0, vc->vc_rows, vc->vc_cols); + ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows, + 0, vc->vc_rows, vc->vc_cols); p->yscroll += p->vrows - vc->vc_rows; } info->var.xoffset = 0; info->var.yoffset = p->yscroll * vc->vc_font.height; info->var.vmode &= ~FB_VMODE_YWRAP; update_var(vc->vc_num, info); - accel_clear_margins(vc, info, 1); + fbcon_clear_margins(vc, 1); scrollback_max -= count; if (scrollback_max < 0) scrollback_max = 0; @@ -1439,7 +1254,7 @@ static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) if (redraw) fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count); update_var(vc->vc_num, info); - accel_clear_margins(vc, info, 1); + fbcon_clear_margins(vc, 1); scrollback_max -= count; if (scrollback_max < 0) scrollback_max = 0; @@ -1449,7 +1264,6 @@ static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) static void fbcon_redraw_softback(struct vc_data *vc, struct display *p, long delta) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; int count = vc->vc_rows; unsigned short *d, *s; unsigned long n; @@ -1506,16 +1320,16 @@ static void fbcon_redraw_softback(struct vc_data *vc, struct display *p, if (attr != (c & 0xff00)) { attr = c & 0xff00; if (s > start) { - accel_putcs(vc, info, start, s - start, - real_y(p, line), x); + fbcon_putcs(vc, start, s - start, + line, x); x += s - start; start = s; } } if (c == scr_readw(d)) { if (s > start) { - accel_putcs(vc, info, start, s - start, - real_y(p, line), x); + fbcon_putcs(vc, start, s - start, + line, x); x += s - start + 1; start = s + 1; } else { @@ -1527,8 +1341,7 @@ static void fbcon_redraw_softback(struct vc_data *vc, struct display *p, d++; } while (s < le); if (s > start) - accel_putcs(vc, info, start, s - start, - real_y(p, line), x); + fbcon_putcs(vc, start, s - start, line, x); line++; if (d == (u16 *) softback_end) d = (u16 *) softback_buf; @@ -1544,7 +1357,6 @@ static void fbcon_redraw_softback(struct vc_data *vc, struct display *p, static void fbcon_redraw_move(struct vc_data *vc, struct display *p, int line, int count, int dy) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; unsigned short *s = (unsigned short *) (vc->vc_origin + vc->vc_size_row * line); @@ -1560,8 +1372,8 @@ static void fbcon_redraw_move(struct vc_data *vc, struct display *p, if (attr != (c & 0xff00)) { attr = c & 0xff00; if (s > start) { - accel_putcs(vc, info, start, s - start, - real_y(p, dy), x); + fbcon_putcs(vc, start, s - start, + dy, x); x += s - start; start = s; } @@ -1570,8 +1382,7 @@ static void fbcon_redraw_move(struct vc_data *vc, struct display *p, s++; } while (s < le); if (s > start) - accel_putcs(vc, info, start, s - start, - real_y(p, dy), x); + fbcon_putcs(vc, start, s - start, dy, x); console_conditional_schedule(); dy++; } @@ -1582,7 +1393,6 @@ static void fbcon_redraw(struct vc_data *vc, struct display *p, { unsigned short *d = (unsigned short *) (vc->vc_origin + vc->vc_size_row * line); - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; unsigned short *s = d + offset; while (count--) { @@ -1597,16 +1407,16 @@ static void fbcon_redraw(struct vc_data *vc, struct display *p, if (attr != (c & 0xff00)) { attr = c & 0xff00; if (s > start) { - accel_putcs(vc, info, start, s - start, - real_y(p, line), x); + fbcon_putcs(vc, start, s - start, + line, x); x += s - start; start = s; } } if (c == scr_readw(d)) { if (s > start) { - accel_putcs(vc, info, start, s - start, - real_y(p, line), x); + fbcon_putcs(vc, start, s - start, + line, x); x += s - start + 1; start = s + 1; } else { @@ -1620,8 +1430,7 @@ static void fbcon_redraw(struct vc_data *vc, struct display *p, d++; } while (s < le); if (s > start) - accel_putcs(vc, info, start, s - start, - real_y(p, line), x); + fbcon_putcs(vc, start, s - start, line, x); console_conditional_schedule(); if (offset > 0) line++; @@ -1664,6 +1473,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, { struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; + struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par; int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK; if (!info->fbops->fb_blank && console_blanked) @@ -1690,10 +1500,10 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, goto redraw_up; switch (p->scrollmode) { case SCROLL_MOVE: - accel_bmove(vc, info, t + count, 0, t, 0, - b - t - count, vc->vc_cols); - accel_clear(vc, info, b - count, 0, count, - vc->vc_cols); + ops->bmove(vc, info, t + count, 0, t, 0, + b - t - count, vc->vc_cols); + ops->clear(vc, info, b - count, 0, count, + vc->vc_cols); break; case SCROLL_WRAP_MOVE: @@ -1759,8 +1569,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, redraw_up: fbcon_redraw(vc, p, t, b - t - count, count * vc->vc_cols); - accel_clear(vc, info, real_y(p, b - count), 0, - count, vc->vc_cols); + fbcon_clear(vc, b - count, 0, count, vc->vc_cols); scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * (b - count)), @@ -1775,9 +1584,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, count = vc->vc_rows; switch (p->scrollmode) { case SCROLL_MOVE: - accel_bmove(vc, info, t, 0, t + count, 0, - b - t - count, vc->vc_cols); - accel_clear(vc, info, t, 0, count, vc->vc_cols); + ops->bmove(vc, info, t, 0, t + count, 0, + b - t - count, vc->vc_cols); + ops->clear(vc, info, t, 0, count, vc->vc_cols); break; case SCROLL_WRAP_MOVE: @@ -1841,8 +1650,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, redraw_down: fbcon_redraw(vc, p, b - 1, b - t - count, -count * vc->vc_cols); - accel_clear(vc, info, real_y(p, t), 0, count, - vc->vc_cols); + fbcon_clear(vc, t, 0, count, vc->vc_cols); scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * t), @@ -1882,6 +1690,7 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int s int dy, int dx, int height, int width, u_int y_break) { struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par; u_int b; if (sy < y_break && sy + height > y_break) { @@ -1915,8 +1724,8 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int s } return; } - accel_bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, - height, width); + ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, + height, width); } static __inline__ void updatescrollmode(struct display *p, struct fb_info *info, @@ -2010,11 +1819,12 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width, static int fbcon_switch(struct vc_data *vc) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct fb_info *info; struct display *p = &fb_display[vc->vc_num]; struct fb_var_screeninfo var; - int i; + int i, prev_console, do_set_par = 0; + info = registered_fb[(int) con2fb_map[vc->vc_num]]; if (softback_top) { int l = fbcon_softback_size / vc->vc_size_row; if (softback_lines) @@ -2039,6 +1849,8 @@ static int fbcon_switch(struct vc_data *vc) logo_shown = -1; } + prev_console = info->currcon; + /* * FIXME: If we have multiple fbdev's loaded, we need to * update all info->currcon. Perhaps, we can place this @@ -2053,7 +1865,6 @@ static int fbcon_switch(struct vc_data *vc) } memset(&var, 0, sizeof(struct fb_var_screeninfo)); - fb_videomode_to_var(&var, p->mode); display_to_var(&var, p); var.activate = FB_ACTIVATE_NOW; @@ -2065,12 +1876,18 @@ static int fbcon_switch(struct vc_data *vc) info->var.yoffset = info->var.xoffset = p->yscroll = 0; fb_set_var(info, &var); - if (info->flags & FBINFO_MISC_MODESWITCH) { + if (prev_console != -1 && + registered_fb[(int) con2fb_map[prev_console]] != info) + do_set_par = 1; + + if (do_set_par || info->flags & FBINFO_MISC_MODESWITCH) { if (info->fbops->fb_set_par) info->fbops->fb_set_par(info); info->flags &= ~FBINFO_MISC_MODESWITCH; } + set_blitting_type(vc, info, p); + vc->vc_can_do_color = (fb_get_color_depth(info) != 1); vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; updatescrollmode(p, info, vc); @@ -2096,8 +1913,9 @@ static int fbcon_switch(struct vc_data *vc) fbcon_set_palette(vc, color_table); if (vt_cons[vc->vc_num]->vc_mode == KD_TEXT) - accel_clear_margins(vc, info, 0); + fbcon_clear_margins(vc, 0); if (logo_shown == -2) { + logo_shown = fg_console; /* This is protected above by initmem_freed */ fb_show_logo(info); @@ -2143,6 +1961,7 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch) } fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW); + info->cursor.flash = (!blank); if (!info->fbops->fb_blank) { if (blank) { @@ -2155,14 +1974,11 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch) height = vc->vc_rows; y_break = p->vrows - p->yscroll; if (height > y_break) { - accel_clear(vc, info, real_y(p, 0), - 0, y_break, vc->vc_cols); - accel_clear(vc, info, real_y(p, y_break), - 0, height - y_break, + fbcon_clear(vc, 0, 0, y_break, vc->vc_cols); + fbcon_clear(vc, y_break, 0, height - y_break, vc->vc_cols); } else - accel_clear(vc, info, real_y(p, 0), - 0, height, vc->vc_cols); + fbcon_clear(vc, 0, 0, height, vc->vc_cols); vc->vc_video_erase_char = oldc; } else update_screen(vc->vc_num); @@ -2329,7 +2145,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, } } else if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) { - accel_clear_margins(vc, info, 0); + fbcon_clear_margins(vc, 0); update_screen(vc->vc_num); } @@ -2672,9 +2488,14 @@ static int fbcon_set_origin(struct vc_data *vc) static void fbcon_suspended(struct fb_info *info) { + struct vc_data *vc = NULL; + + if (info->currcon < 0) + return; + vc = vc_cons[info->currcon].d; + /* Clear cursor, restore saved data */ - info->cursor.enable = 0; - info->fbops->fb_cursor(info, &info->cursor); + fbcon_cursor(vc, CM_ERASE); } static void fbcon_resumed(struct fb_info *info) @@ -2753,11 +2574,22 @@ static int fbcon_mode_deleted(struct fb_info *info, static int fbcon_fb_registered(int idx) { - int ret = 0; + int ret = 0, i; if (info_idx == -1) { - info_idx = idx; - ret = fbcon_takeover(); + for (i = 0; i < MAX_NR_CONSOLES; i++) { + if (con2fb_map_boot[i] == idx) { + info_idx = idx; + break; + } + } + if (info_idx != -1) + ret = fbcon_takeover(1); + } else { + for (i = 0; i < MAX_NR_CONSOLES; i++) { + if (con2fb_map_boot[i] == idx) + set_con2fb_map(i, idx, 0); + } } return ret; @@ -2791,7 +2623,8 @@ static int fbcon_event_notify(struct notifier_block *self, break; case FB_EVENT_SET_CONSOLE_MAP: con2fb = (struct fb_con2fbmap *) event->data; - ret = set_con2fb_map(con2fb->console - 1, con2fb->framebuffer); + ret = set_con2fb_map(con2fb->console - 1, + con2fb->framebuffer, 1); break; case FB_EVENT_GET_CONSOLE_MAP: con2fb = (struct fb_con2fbmap *) event->data; @@ -2854,7 +2687,7 @@ int __init fb_console_init(void) break; } } - fbcon_takeover(); + fbcon_takeover(0); } return 0; diff --git a/drivers/video/console/fbcon.h b/drivers/video/console/fbcon.h index a89001a40856..2b8d413772d9 100644 --- a/drivers/video/console/fbcon.h +++ b/drivers/video/console/fbcon.h @@ -48,6 +48,19 @@ struct display { struct fb_videomode *mode; }; +struct fbcon_ops { + void (*bmove)(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int dy, int dx, int height, int width); + void (*clear)(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int height, int width); + void (*putcs)(struct vc_data *vc, struct fb_info *info, + const unsigned short *s, int count, int yy, int xx, + int fg, int bg); + void (*clear_margins)(struct vc_data *vc, struct fb_info *info, + int bottom_only); + void (*cursor)(struct vc_data *vc, struct fb_info *info, + struct display *p, int mode, int fg, int bg); +}; /* * Attribute Decoding */ @@ -72,6 +85,13 @@ struct display { #define attr_blink(s) \ ((s) & 0x8000) +/* Font */ +#define REFCOUNT(fd) (((int *)(fd))[-1]) +#define FNTSIZE(fd) (((int *)(fd))[-2]) +#define FNTCHARCNT(fd) (((int *)(fd))[-3]) +#define FNTSUM(fd) (((int *)(fd))[-4]) +#define FONT_EXTRA_WORDS 4 + /* * Scroll Method */ @@ -129,5 +149,9 @@ struct display { #define SCROLL_PAN_REDRAW 0x005 extern int fb_console_init(void); - +#ifdef CONFIG_FB_TILEBLITTING +extern void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info, + struct display *p, struct fbcon_ops *ops); +#endif +extern void fbcon_set_bitops(struct fbcon_ops *ops); #endif /* _VIDEO_FBCON_H */ diff --git a/drivers/video/console/tileblit.c b/drivers/video/console/tileblit.c new file mode 100644 index 000000000000..93927180b298 --- /dev/null +++ b/drivers/video/console/tileblit.c @@ -0,0 +1,146 @@ +/* + * linux/drivers/video/console/tileblit.c -- Tile Blitting Operation + * + * Copyright (C) 2004 Antonino Daplas <adaplas @pol.net> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/fb.h> +#include <linux/vt_kern.h> +#include <linux/console.h> +#include <asm/types.h> +#include "fbcon.h" + +static void tile_bmove(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int dy, int dx, int height, int width) +{ + struct fb_tilearea area; + + area.sx = sx; + area.sy = sy; + area.dx = dx; + area.dy = dy; + area.height = height; + area.width = width; + + info->tileops->fb_tilecopy(info, &area); +} + +static void tile_clear(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int height, int width) +{ + struct fb_tilerect rect; + int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; + int fgshift = (vc->vc_hi_font_mask) ? 9 : 8; + + rect.index = vc->vc_video_erase_char & + ((vc->vc_hi_font_mask) ? 0x1ff : 0xff); + rect.fg = attr_fgcol_ec(fgshift, vc); + rect.bg = attr_bgcol_ec(bgshift, vc); + rect.sx = sx; + rect.sy = sy; + rect.width = width; + rect.height = height; + rect.rop = ROP_COPY; + + info->tileops->fb_tilefill(info, &rect); +} + +static void tile_putcs(struct vc_data *vc, struct fb_info *info, + const unsigned short *s, int count, int yy, int xx, + int fg, int bg) +{ + struct fb_tileblit blit; + unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; + int size = sizeof(u32) * count, i; + + blit.sx = xx; + blit.sy = yy; + blit.width = count; + blit.height = 1; + blit.fg = fg; + blit.bg = bg; + blit.length = count; + blit.indices = (u32 *) fb_get_buffer_offset(info, &info->pixmap, size); + for (i = 0; i < count; i++) + blit.indices[i] = (u32)(scr_readw(s++) & charmask); + + info->tileops->fb_tileblit(info, &blit); +} + +static void tile_clear_margins(struct vc_data *vc, struct fb_info *info, + int bottom_only) +{ + return; +} + +static void tile_cursor(struct vc_data *vc, struct fb_info *info, + struct display *p, int mode, int fg, int bg) +{ + struct fb_tilecursor cursor; + + cursor.sx = vc->vc_x; + cursor.sy = vc->vc_y; + cursor.mode = (mode == CM_ERASE) ? 0 : 1; + cursor.fg = fg; + cursor.bg = bg; + + switch (vc->vc_cursor_type & 0x0f) { + case CUR_NONE: + cursor.shape = FB_TILE_CURSOR_NONE; + break; + case CUR_UNDERLINE: + cursor.shape = FB_TILE_CURSOR_UNDERLINE; + break; + case CUR_LOWER_THIRD: + cursor.shape = FB_TILE_CURSOR_LOWER_THIRD; + break; + case CUR_LOWER_HALF: + cursor.shape = FB_TILE_CURSOR_LOWER_HALF; + break; + case CUR_TWO_THIRDS: + cursor.shape = FB_TILE_CURSOR_TWO_THIRDS; + break; + case CUR_BLOCK: + default: + cursor.shape = FB_TILE_CURSOR_BLOCK; + break; + } + + info->tileops->fb_tilecursor(info, &cursor); +} + +void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info, + struct display *p, struct fbcon_ops *ops) +{ + struct fb_tilemap map; + + ops->bmove = tile_bmove; + ops->clear = tile_clear; + ops->putcs = tile_putcs; + ops->clear_margins = tile_clear_margins; + ops->cursor = tile_cursor; + + if (p) { + map.width = vc->vc_font.width; + map.height = vc->vc_font.height; + map.depth = 1; + map.length = (p->userfont) ? + FNTCHARCNT(p->fontdata) : 256; + map.data = p->fontdata; + info->tileops->fb_settile(info, &map); + } +} + +EXPORT_SYMBOL(fbcon_set_tileops); + +MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>"); +MODULE_DESCRIPTION("Tile Blitting Operation"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 0e91d6b577e4..61181b2d7a19 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -901,8 +901,10 @@ static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight) for (i = 0; i < MAX_NR_CONSOLES; i++) { struct vc_data *c = vc_cons[i].d; - if (c && c->vc_sw == &vga_con) + if (c && c->vc_sw == &vga_con) { + c->vc_font.height = fontheight; vc_resize(c->vc_num, 0, rows); /* Adjust console size */ + } } return 0; } diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c index 4dca34fdf767..0e3ab898b1e8 100644 --- a/drivers/video/cyber2000fb.c +++ b/drivers/video/cyber2000fb.c @@ -1399,6 +1399,8 @@ static int __devinit cyberpro_common_probe(struct cfb_info *cfb) cfb->fb.var.xres, cfb->fb.var.yres, h_sync / 1000, h_sync % 1000, v_sync); + if (cfb->dev) + cfb->fb.device = &cfb->dev->dev; err = register_framebuffer(&cfb->fb); failed: @@ -1722,7 +1724,7 @@ int __init cyber2000fb_init(void) #ifndef MODULE char *option = NULL; - if (fb_get_options("cyber2000fb", NULL)) + if (fb_get_options("cyber2000fb", &option)) return -ENODEV; cyber2000fb_setup(option); #endif diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index ef5cea5a0fb4..c21b42e4b09a 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -368,6 +368,9 @@ int fb_prepare_logo(struct fb_info *info) memset(&fb_logo, 0, sizeof(struct logo_data)); + if (info->flags & FBINFO_MISC_TILEBLITTING) + return 0; + if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) { depth = info->var.blue.length; if (info->var.red.length < depth) @@ -504,7 +507,8 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) struct inode *inode = file->f_dentry->d_inode; int fbidx = iminor(inode); struct fb_info *info = registered_fb[fbidx]; - u32 *buffer, *dst, *src; + u32 *buffer, *dst; + u32 __iomem *src; int c, i, cnt = 0, err = 0; unsigned long total_size; @@ -534,7 +538,7 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) if (!buffer) return -ENOMEM; - src = (u32 *) (info->screen_base + p); + src = (u32 __iomem *) (info->screen_base + p); if (info->fbops->fb_sync) info->fbops->fb_sync(info); @@ -546,12 +550,12 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) *dst++ = fb_readl(src++); if (c & 3) { u8 *dst8 = (u8 *) dst; - u8 *src8 = (u8 *) src; + u8 __iomem *src8 = (u8 __iomem *) src; for (i = c & 3; i--;) *dst8++ = fb_readb(src8++); - src = (u32 *) src8; + src = (u32 __iomem *) src8; } if (copy_to_user(buf, buffer, c)) { @@ -575,7 +579,8 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) struct inode *inode = file->f_dentry->d_inode; int fbidx = iminor(inode); struct fb_info *info = registered_fb[fbidx]; - u32 *buffer, *dst, *src; + u32 *buffer, *src; + u32 __iomem *dst; int c, i, cnt = 0, err; unsigned long total_size; @@ -607,7 +612,7 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) if (!buffer) return -ENOMEM; - dst = (u32 *) (info->screen_base + p); + dst = (u32 __iomem *) (info->screen_base + p); if (info->fbops->fb_sync) info->fbops->fb_sync(info); @@ -623,12 +628,12 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) fb_writel(*src++, dst++); if (c & 3) { u8 *src8 = (u8 *) src; - u8 *dst8 = (u8 *) dst; + u8 __iomem *dst8 = (u8 __iomem *) dst; for (i = c & 3; i--; ) fb_writeb(*src8++, dst8++); - dst = (u32 *) dst8; + dst = (u32 __iomem *) dst8; } *ppos += c; buf += c; @@ -654,7 +659,8 @@ fb_load_cursor_image(struct fb_info *info) u8 *data = (u8 *) info->cursor.image.data; if (info->sprite.outbuf) - info->sprite.outbuf(info, info->sprite.addr, data, width); + info->sprite.outbuf(info, info->sprite.addr, data, + width); else memcpy(info->sprite.addr, data, width); } @@ -679,6 +685,7 @@ fb_cursor(struct fb_info *info, struct fb_cursor_user __user *sprite) cursor.image.cmap.blue = info->cursor.image.cmap.blue; cursor.image.cmap.transp = info->cursor.image.cmap.transp; cursor.data = NULL; + cursor.flash = 0; if (cursor.set & FB_CUR_SETCUR) info->cursor.enable = 1; @@ -847,20 +854,28 @@ int fb_blank(struct fb_info *info, int blank) { /* ??? Variable sized stack allocation. */ - u16 black[info->cmap.len]; struct fb_cmap cmap; + u16 *black = NULL; + int err = 0; if (info->fbops->fb_blank && !info->fbops->fb_blank(blank, info)) return 0; if (blank) { - memset(black, 0, info->cmap.len * sizeof(u16)); - cmap.red = cmap.green = cmap.blue = black; - cmap.transp = info->cmap.transp ? black : NULL; - cmap.start = info->cmap.start; - cmap.len = info->cmap.len; + black = kmalloc(sizeof(u16) * info->cmap.len, GFP_KERNEL); + if (!black) { + memset(black, 0, info->cmap.len * sizeof(u16)); + cmap.red = cmap.green = cmap.blue = black; + cmap.transp = info->cmap.transp ? black : NULL; + cmap.start = info->cmap.start; + cmap.len = info->cmap.len; + } } else cmap = info->cmap; - return fb_set_cmap(&cmap, info); + + err = fb_set_cmap(&cmap, info); + kfree(black); + + return err; } static int @@ -948,14 +963,11 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, #endif /* CONFIG_KMOD */ if (!registered_fb[con2fb.framebuffer]) return -EINVAL; - if (con2fb.console > 0 && con2fb.console < MAX_NR_CONSOLES) { - event.info = info; - event.data = &con2fb; - return notifier_call_chain(&fb_notifier_list, - FB_EVENT_SET_CONSOLE_MAP, - &event); - } - return -EINVAL; + event.info = info; + event.data = &con2fb; + return notifier_call_chain(&fb_notifier_list, + FB_EVENT_SET_CONSOLE_MAP, + &event); case FBIOBLANK: acquire_console_sem(); i = fb_blank(info, arg); @@ -1144,7 +1156,8 @@ register_framebuffer(struct fb_info *fb_info) break; fb_info->node = i; - c = class_simple_device_add(fb_class, MKDEV(FB_MAJOR, i), NULL, "fb%d", i); + c = class_simple_device_add(fb_class, MKDEV(FB_MAJOR, i), + fb_info->device, "fb%d", i); if (IS_ERR(c)) { /* Not fatal */ printk(KERN_WARNING "Unable to create class_device for framebuffer %d; errno = %ld\n", i, PTR_ERR(c)); diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index 8b3bbe0c641b..4fec33dc8e31 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -51,6 +51,8 @@ struct fb_info *framebuffer_alloc(size_t size, struct device *dev) if (size) info->par = p + fb_info_size; + info->device = dev; + return info; #undef PADDING #undef BYTES_PER_LONG diff --git a/drivers/video/fm2fb.c b/drivers/video/fm2fb.c index 3adb65df1fac..cda492a55f61 100644 --- a/drivers/video/fm2fb.c +++ b/drivers/video/fm2fb.c @@ -292,18 +292,7 @@ static int __devinit fm2fb_probe(struct zorro_dev *z, return 0; } -int __init fm2fb_setup(char *options); - -int __init fm2fb_init(void) -{ - char *option = NULL; - - if (fb_get_options("fm2fb", &option)) - return -ENODEV; - fm2fb_setup(option); - return zorro_register_driver(&fm2fb_driver); -} - +int __init fm2fb_setup(char *options) { char *this_opt; @@ -319,5 +308,15 @@ int __init fm2fb_init(void) return 0; } +int __init fm2fb_init(void) +{ + char *option = NULL; + + if (fb_get_options("fm2fb", &option)) + return -ENODEV; + fm2fb_setup(option); + return zorro_register_driver(&fm2fb_driver); +} + module_init(fm2fb_init); MODULE_LICENSE("GPL"); diff --git a/drivers/video/i810/i810.h b/drivers/video/i810/i810.h index 68b64233a1aa..4df3b7766ff5 100644 --- a/drivers/video/i810/i810.h +++ b/drivers/video/i810/i810.h @@ -222,7 +222,7 @@ struct mode_registers { struct heap_data { unsigned long physical; - __u8 *virtual; + __u8 __iomem *virtual; u32 offset; u32 size; }; @@ -256,7 +256,7 @@ struct i810fb_par { u32 pseudo_palette[17]; u32 pci_state[16]; unsigned long mmio_start_phys; - u8 *mmio_start_virtual; + u8 __iomem *mmio_start_virtual; u32 pitch; u32 pixconf; u32 watermark; diff --git a/drivers/video/i810/i810_accel.c b/drivers/video/i810/i810_accel.c index 71f649f55592..9921db8a48c7 100644 --- a/drivers/video/i810/i810_accel.c +++ b/drivers/video/i810/i810_accel.c @@ -32,7 +32,7 @@ extern void flush_cache(void); /************************************************************/ /* BLT Engine Routines */ -static inline void i810_report_error(u8 *mmio) +static inline void i810_report_error(u8 __iomem *mmio) { printk("IIR : 0x%04x\n" "EIR : 0x%04x\n" @@ -59,7 +59,7 @@ static inline int wait_for_space(struct fb_info *info, u32 space) { struct i810fb_par *par = (struct i810fb_par *) info->par; u32 head, count = WAIT_COUNT, tail; - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; tail = par->cur_tail; while (count--) { @@ -89,7 +89,7 @@ static inline int wait_for_space(struct fb_info *info, u32 space) static inline int wait_for_engine_idle(struct fb_info *info) { struct i810fb_par *par = (struct i810fb_par *) info->par; - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; int count = WAIT_COUNT; if (wait_for_space(info, par->iring.size)) /* flush */ @@ -133,7 +133,7 @@ static inline u32 begin_iring(struct fb_info *info, u32 space) */ static inline void end_iring(struct i810fb_par *par) { - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; i810_writel(IRING, mmio, par->cur_tail); } @@ -326,7 +326,7 @@ static inline void load_front(int offset, struct fb_info *info) static inline void i810fb_iring_enable(struct i810fb_par *par, u32 mode) { u32 tmp; - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; tmp = i810_readl(IRING + 12, mmio); if (mode == OFF) @@ -451,7 +451,7 @@ int i810fb_sync(struct fb_info *info) void i810fb_load_front(u32 offset, struct fb_info *info) { struct i810fb_par *par = (struct i810fb_par *) info->par; - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; if (!info->var.accel_flags || par->dev_flags & LOCKUP) i810_writel(DPLYBASE, mmio, par->fb.physical + offset); @@ -472,7 +472,7 @@ void i810fb_init_ringbuffer(struct fb_info *info) { struct i810fb_par *par = (struct i810fb_par *) info->par; u32 tmp1, tmp2; - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; wait_for_engine_idle(info); i810fb_iring_enable(par, OFF); diff --git a/drivers/video/i810/i810_gtf.c b/drivers/video/i810/i810_gtf.c index 7d0c02ca0a42..814698b90fb6 100644 --- a/drivers/video/i810/i810_gtf.c +++ b/drivers/video/i810/i810_gtf.c @@ -124,7 +124,8 @@ void i810fb_encode_registers(const struct fb_var_screeninfo *var, struct i810fb_par *par, u32 xres, u32 yres) { int n, blank_s, blank_e; - u8 *mmio = par->mmio_start_virtual, msr = 0; + u8 __iomem *mmio = par->mmio_start_virtual; + u8 msr = 0; /* Horizontal */ /* htotal */ diff --git a/drivers/video/i810/i810_main.c b/drivers/video/i810/i810_main.c index 6acde2569975..144c82785f59 100644 --- a/drivers/video/i810/i810_main.c +++ b/drivers/video/i810/i810_main.c @@ -121,7 +121,7 @@ static int dcolor __initdata = 0; * DESCRIPTION: * Blanks/unblanks the display */ -static void i810_screen_off(u8 *mmio, u8 mode) +static void i810_screen_off(u8 __iomem *mmio, u8 mode) { u32 count = WAIT_COUNT; u8 val; @@ -145,7 +145,7 @@ static void i810_screen_off(u8 *mmio, u8 mode) * Turns off DRAM refresh. Must be off for only 2 vsyncs * before data becomes corrupt */ -static void i810_dram_off(u8 *mmio, u8 mode) +static void i810_dram_off(u8 __iomem *mmio, u8 mode) { u8 val; @@ -164,7 +164,7 @@ static void i810_dram_off(u8 *mmio, u8 mode) * The IBM VGA standard allows protection of certain VGA registers. * This will protect or unprotect them. */ -static void i810_protect_regs(u8 *mmio, int mode) +static void i810_protect_regs(u8 __iomem *mmio, int mode) { u8 reg; @@ -187,7 +187,7 @@ static void i810_protect_regs(u8 *mmio, int mode) static void i810_load_pll(struct i810fb_par *par) { u32 tmp1, tmp2; - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; tmp1 = par->regs.M | par->regs.N << 16; tmp2 = i810_readl(DCLK_2D, mmio); @@ -212,7 +212,7 @@ static void i810_load_pll(struct i810fb_par *par) */ static void i810_load_vga(struct i810fb_par *par) { - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; /* interlace */ i810_writeb(CR_INDEX_CGA, mmio, CR70); @@ -255,7 +255,7 @@ static void i810_load_vga(struct i810fb_par *par) */ static void i810_load_vgax(struct i810fb_par *par) { - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; i810_writeb(CR_INDEX_CGA, mmio, CR30); i810_writeb(CR_DATA_CGA, mmio, par->regs.cr30); @@ -281,7 +281,8 @@ static void i810_load_vgax(struct i810fb_par *par) static void i810_load_2d(struct i810fb_par *par) { u32 tmp; - u8 tmp8, *mmio = par->mmio_start_virtual; + u8 tmp8; + u8 __iomem *mmio = par->mmio_start_virtual; i810_writel(FW_BLC, mmio, par->watermark); tmp = i810_readl(PIXCONF, mmio); @@ -301,7 +302,7 @@ static void i810_load_2d(struct i810fb_par *par) * i810_hires - enables high resolution mode * @mmio: address of register space */ -static void i810_hires(u8 *mmio) +static void i810_hires(u8 __iomem *mmio) { u8 val; @@ -321,7 +322,8 @@ static void i810_hires(u8 *mmio) static void i810_load_pitch(struct i810fb_par *par) { u32 tmp, pitch; - u8 val, *mmio = par->mmio_start_virtual; + u8 val; + u8 __iomem *mmio = par->mmio_start_virtual; pitch = par->pitch >> 3; i810_writeb(SR_INDEX, mmio, SR01); @@ -351,9 +353,10 @@ static void i810_load_pitch(struct i810fb_par *par) */ static void i810_load_color(struct i810fb_par *par) { - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; u32 reg1; u16 reg2; + reg1 = i810_readl(PIXCONF, mmio) & ~(0xF0000 | 1 << 27); reg2 = i810_readw(BLTCNTL, mmio) & ~0x30; @@ -372,7 +375,7 @@ static void i810_load_color(struct i810fb_par *par) */ static void i810_load_regs(struct i810fb_par *par) { - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; i810_screen_off(mmio, OFF); i810_protect_regs(mmio, OFF); @@ -390,7 +393,7 @@ static void i810_load_regs(struct i810fb_par *par) } static void i810_write_dac(u8 regno, u8 red, u8 green, u8 blue, - u8 *mmio) + u8 __iomem *mmio) { i810_writeb(CLUT_INDEX_WRITE, mmio, regno); i810_writeb(CLUT_DATA, mmio, red); @@ -399,7 +402,7 @@ static void i810_write_dac(u8 regno, u8 red, u8 green, u8 blue, } static void i810_read_dac(u8 regno, u8 *red, u8 *green, u8 *blue, - u8 *mmio) + u8 __iomem *mmio) { i810_writeb(CLUT_INDEX_READ, mmio, regno); *red = i810_readb(CLUT_DATA, mmio); @@ -413,7 +416,7 @@ static void i810_read_dac(u8 regno, u8 *red, u8 *green, u8 *blue, static void i810_restore_pll(struct i810fb_par *par) { u32 tmp1, tmp2; - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; tmp1 = par->hw_state.dclk_2d; tmp2 = i810_readl(DCLK_2D, mmio); @@ -433,7 +436,7 @@ static void i810_restore_pll(struct i810fb_par *par) static void i810_restore_dac(struct i810fb_par *par) { u32 tmp1, tmp2; - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; tmp1 = par->hw_state.pixconf; tmp2 = i810_readl(PIXCONF, mmio); @@ -444,7 +447,8 @@ static void i810_restore_dac(struct i810fb_par *par) static void i810_restore_vgax(struct i810fb_par *par) { - u8 i, j, *mmio = par->mmio_start_virtual; + u8 i, j; + u8 __iomem *mmio = par->mmio_start_virtual; for (i = 0; i < 4; i++) { i810_writeb(CR_INDEX_CGA, mmio, CR30+i); @@ -477,7 +481,8 @@ static void i810_restore_vgax(struct i810fb_par *par) static void i810_restore_vga(struct i810fb_par *par) { - u8 i, *mmio = par->mmio_start_virtual; + u8 i; + u8 __iomem *mmio = par->mmio_start_virtual; for (i = 0; i < 10; i++) { i810_writeb(CR_INDEX_CGA, mmio, CR00 + i); @@ -491,7 +496,8 @@ static void i810_restore_vga(struct i810fb_par *par) static void i810_restore_addr_map(struct i810fb_par *par) { - u8 tmp, *mmio = par->mmio_start_virtual; + u8 tmp; + u8 __iomem *mmio = par->mmio_start_virtual; i810_writeb(GR_INDEX, mmio, GR10); tmp = i810_readb(GR_DATA, mmio); @@ -505,7 +511,7 @@ static void i810_restore_2d(struct i810fb_par *par) { u32 tmp_long; u16 tmp_word; - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; tmp_word = i810_readw(BLTCNTL, mmio); tmp_word &= ~(3 << 4); @@ -534,7 +540,7 @@ static void i810_restore_2d(struct i810fb_par *par) static void i810_restore_vga_state(struct i810fb_par *par) { - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; i810_screen_off(mmio, OFF); i810_protect_regs(mmio, OFF); @@ -556,7 +562,8 @@ static void i810_restore_vga_state(struct i810fb_par *par) static void i810_save_vgax(struct i810fb_par *par) { - u8 i, *mmio = par->mmio_start_virtual; + u8 i; + u8 __iomem *mmio = par->mmio_start_virtual; for (i = 0; i < 4; i++) { i810_writeb(CR_INDEX_CGA, mmio, CR30 + i); @@ -579,7 +586,8 @@ static void i810_save_vgax(struct i810fb_par *par) static void i810_save_vga(struct i810fb_par *par) { - u8 i, *mmio = par->mmio_start_virtual; + u8 i; + u8 __iomem *mmio = par->mmio_start_virtual; for (i = 0; i < 10; i++) { i810_writeb(CR_INDEX_CGA, mmio, CR00 + i); @@ -593,7 +601,7 @@ static void i810_save_vga(struct i810fb_par *par) static void i810_save_2d(struct i810fb_par *par) { - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; par->hw_state.dclk_2d = i810_readl(DCLK_2D, mmio); par->hw_state.dclk_1d = i810_readl(DCLK_1D, mmio); @@ -716,7 +724,7 @@ static void i810_calc_dclk(u32 freq, u32 *m, u32 *n, u32 *p) * Description: * Shows or hides the hardware cursor */ -void i810_enable_cursor(u8 *mmio, int mode) +void i810_enable_cursor(u8 __iomem *mmio, int mode) { u32 temp; @@ -729,7 +737,7 @@ void i810_enable_cursor(u8 *mmio, int mode) static void i810_reset_cursor_image(struct i810fb_par *par) { - u8 *addr = par->cursor_heap.virtual; + u8 __iomem *addr = par->cursor_heap.virtual; int i, j; for (i = 64; i--; ) { @@ -744,7 +752,7 @@ static void i810_reset_cursor_image(struct i810fb_par *par) static void i810_load_cursor_image(int width, int height, u8 *data, struct i810fb_par *par) { - u8 *addr = par->cursor_heap.virtual; + u8 __iomem *addr = par->cursor_heap.virtual; int i, j, w = width/8; int mod = width % 8, t_mask, d_mask; @@ -766,8 +774,8 @@ static void i810_load_cursor_image(int width, int height, u8 *data, static void i810_load_cursor_colors(int fg, int bg, struct fb_info *info) { struct i810fb_par *par = (struct i810fb_par *) info->par; - u8 *mmio = par->mmio_start_virtual, temp; - u8 red, green, blue, trans; + u8 __iomem *mmio = par->mmio_start_virtual; + u8 red, green, blue, trans, temp; i810fb_getcolreg(bg, &red, &green, &blue, &trans, info); @@ -796,7 +804,7 @@ static void i810_load_cursor_colors(int fg, int bg, struct fb_info *info) */ static void i810_init_cursor(struct i810fb_par *par) { - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; i810_enable_cursor(mmio, OFF); i810_writel(CURBASE, mmio, par->cursor_heap.physical); @@ -1124,7 +1132,8 @@ static int i810fb_getcolreg(u8 regno, u8 *red, u8 *green, u8 *blue, u8 *transp, struct fb_info *info) { struct i810fb_par *par = (struct i810fb_par *) info->par; - u8 *mmio = par->mmio_start_virtual, temp; + u8 __iomem *mmio = par->mmio_start_virtual; + u8 temp; if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) { if ((info->var.green.length == 5 && regno > 31) || @@ -1167,7 +1176,7 @@ static int i810fb_open(struct fb_info *info, int user) if (count == 0) { memset(&par->state, 0, sizeof(struct vgastate)); par->state.flags = VGA_SAVE_CMAP; - par->state.vgabase = (caddr_t) par->mmio_start_virtual; + par->state.vgabase = par->mmio_start_virtual; save_vga(&par->state); i810_save_vga_state(par); @@ -1203,7 +1212,8 @@ static int i810fb_setcolreg(unsigned regno, unsigned red, unsigned green, struct fb_info *info) { struct i810fb_par *par = (struct i810fb_par *) info->par; - u8 *mmio = par->mmio_start_virtual, temp; + u8 __iomem *mmio = par->mmio_start_virtual; + u8 temp; int i; if (regno > 255) return 1; @@ -1308,7 +1318,7 @@ static int i810fb_pan_display(struct fb_var_screeninfo *var, static int i810fb_blank (int blank_mode, struct fb_info *info) { struct i810fb_par *par = (struct i810fb_par *) info->par; - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; int mode = 0, pwr, scr_off = 0; pwr = i810_readl(PWR_CLKC, mmio); @@ -1391,7 +1401,7 @@ static int i810fb_check_var(struct fb_var_screeninfo *var, static int i810fb_cursor(struct fb_info *info, struct fb_cursor *cursor) { struct i810fb_par *par = (struct i810fb_par *)info->par; - u8 *mmio = par->mmio_start_virtual; + u8 __iomem *mmio = par->mmio_start_virtual; if (!info->var.accel_flags || par->dev_flags & LOCKUP) return soft_cursor(info, cursor); @@ -1724,7 +1734,8 @@ static void __devinit i810_init_defaults(struct i810fb_par *par, */ static void __devinit i810_init_device(struct i810fb_par *par) { - u8 reg, *mmio = par->mmio_start_virtual; + u8 reg; + u8 __iomem *mmio = par->mmio_start_virtual; if (mtrr) set_mtrr(par); @@ -1855,20 +1866,13 @@ static int __devinit i810fb_init_pci (struct pci_dev *dev, int i, err = -1, vfreq, hfreq, pixclock; i = 0; - if (!(info = kmalloc(sizeof(struct fb_info), GFP_KERNEL))) { - i810fb_release_resource(info, par); - return -ENOMEM; - } - memset(info, 0, sizeof(struct fb_info)); - if(!(par = kmalloc(sizeof(struct i810fb_par), GFP_KERNEL))) { - i810fb_release_resource(info, par); + info = framebuffer_alloc(sizeof(struct i810fb_par), &dev->dev); + if (!info) return -ENOMEM; - } - memset(par, 0, sizeof(struct i810fb_par)); + par = (struct i810fb_par *) info->par; par->dev = dev; - info->par = par; if (!(info->pixmap.addr = kmalloc(64*1024, GFP_KERNEL))) { i810fb_release_resource(info, par); @@ -1941,38 +1945,36 @@ static int __devinit i810fb_init_pci (struct pci_dev *dev, static void i810fb_release_resource(struct fb_info *info, struct i810fb_par *par) { - if (par) { - unset_mtrr(par); - if (par->drm_agp) { - drm_agp_t *agp = par->drm_agp; - struct gtt_data *gtt = &par->i810_gtt; - - if (par->i810_gtt.i810_cursor_memory) - agp->free_memory(gtt->i810_cursor_memory); - if (par->i810_gtt.i810_fb_memory) - agp->free_memory(gtt->i810_fb_memory); - - inter_module_put("drm_agp"); - par->drm_agp = NULL; - } + unset_mtrr(par); + if (par->drm_agp) { + drm_agp_t *agp = par->drm_agp; + struct gtt_data *gtt = &par->i810_gtt; + + if (par->i810_gtt.i810_cursor_memory) + agp->free_memory(gtt->i810_cursor_memory); + if (par->i810_gtt.i810_fb_memory) + agp->free_memory(gtt->i810_fb_memory); + + inter_module_put("drm_agp"); + par->drm_agp = NULL; + } - if (par->mmio_start_virtual) - iounmap(par->mmio_start_virtual); - if (par->aperture.virtual) - iounmap(par->aperture.virtual); + if (par->mmio_start_virtual) + iounmap(par->mmio_start_virtual); + if (par->aperture.virtual) + iounmap(par->aperture.virtual); - if (par->res_flags & FRAMEBUFFER_REQ) - release_mem_region(par->aperture.physical, - par->aperture.size); - if (par->res_flags & MMIO_REQ) - release_mem_region(par->mmio_start_phys, MMIO_SIZE); + if (par->res_flags & FRAMEBUFFER_REQ) + release_mem_region(par->aperture.physical, + par->aperture.size); + if (par->res_flags & MMIO_REQ) + release_mem_region(par->mmio_start_phys, MMIO_SIZE); - if (par->res_flags & PCI_DEVICE_ENABLED) - pci_disable_device(par->dev); + if (par->res_flags & PCI_DEVICE_ENABLED) + pci_disable_device(par->dev); + + framebuffer_release(info); - kfree(par); - } - kfree(info); } static void __exit i810fb_remove_pci(struct pci_dev *dev) diff --git a/drivers/video/igafb.c b/drivers/video/igafb.c index dd6b16c845c2..f9d77b0f51ab 100644 --- a/drivers/video/igafb.c +++ b/drivers/video/igafb.c @@ -531,6 +531,7 @@ int __init igafb_init(void) info->var = default_var; info->fix = igafb_fix; info->pseudo_palette = (void *)(par + 1); + info->device = &pdev->dev; if (!iga_init(info, par)) { iounmap((void *)par->io_base); diff --git a/drivers/video/imsttfb.c b/drivers/video/imsttfb.c index c4a07f27c18c..d51e8f080fe1 100644 --- a/drivers/video/imsttfb.c +++ b/drivers/video/imsttfb.c @@ -1524,6 +1524,7 @@ imsttfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) par->cmap_regs = (__u8 *)ioremap(addr + 0x840000, 0x1000); info->par = par; info->pseudo_palette = (void *) (par + 1); + info->device = &pdev->dev; init_imstt(info); pci_set_drvdata(pdev, info); diff --git a/drivers/video/kyro/fbdev.c b/drivers/video/kyro/fbdev.c index 2352a9142a77..51a6de200f73 100644 --- a/drivers/video/kyro/fbdev.c +++ b/drivers/video/kyro/fbdev.c @@ -735,6 +735,7 @@ static int __devinit kyrofb_probe(struct pci_dev *pdev, fb_memset(info->screen_base, 0, size); + info->device = &pdev->dev; if (register_framebuffer(info) < 0) goto out_unmap; diff --git a/drivers/video/matrox/matroxfb_accel.c b/drivers/video/matrox/matroxfb_accel.c index fd38e0536e94..8f14c9b300c2 100644 --- a/drivers/video/matrox/matroxfb_accel.c +++ b/drivers/video/matrox/matroxfb_accel.c @@ -411,12 +411,7 @@ static void matroxfb_1bpp_imageblit(WPMINFO u_int32_t fgx, u_int32_t bgx, CRITBEGIN -#ifdef __BIG_ENDIAN - WaitTillIdle(); - mga_outl(M_OPMODE, M_OPMODE_8BPP); -#else mga_fifo(3); -#endif if (easy) mga_outl(M_DWGCTL, M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE); else @@ -432,32 +427,24 @@ static void matroxfb_1bpp_imageblit(WPMINFO u_int32_t fgx, u_int32_t bgx, mga_writel(mmio, M_AR3, 0); if (easy) { mga_writel(mmio, M_YDSTLEN | M_EXEC, ydstlen); - mga_memcpy_toio(mmio, 0, chardata, xlen); + mga_memcpy_toio(mmio, chardata, xlen); } else { mga_writel(mmio, M_AR5, 0); mga_writel(mmio, M_YDSTLEN | M_EXEC, ydstlen); if ((step & 3) == 0) { /* Great. Source has 32bit aligned lines, so we can feed them directly to the accelerator. */ - mga_memcpy_toio(mmio, 0, chardata, charcell); + mga_memcpy_toio(mmio, chardata, charcell); } else if (step == 1) { /* Special case for 1..8bit widths */ while (height--) { -#ifdef __LITTLE_ENDIAN mga_writel(mmio, 0, *chardata); -#else - mga_writel(mmio, 0, (*chardata) << 24); -#endif chardata++; } } else if (step == 2) { /* Special case for 9..15bit widths */ while (height--) { -#ifdef __LITTLE_ENDIAN mga_writel(mmio, 0, *(u_int16_t*)chardata); -#else - mga_writel(mmio, 0, (*(u_int16_t*)chardata) << 16); -#endif chardata += 2; } } else { @@ -474,9 +461,6 @@ static void matroxfb_1bpp_imageblit(WPMINFO u_int32_t fgx, u_int32_t bgx, } } WaitTillIdle(); -#ifdef __BIG_ENDIAN - mga_outl(M_OPMODE, ACCESS_FBINFO(accel.m_opmode)); -#endif CRITEND } @@ -486,7 +470,7 @@ static void matroxfb_imageblit(struct fb_info* info, const struct fb_image* imag DBG_HEAVY(__FUNCTION__); - if (image->depth == 0) { + if (image->depth == 1) { u_int32_t fgx, bgx; fgx = ((u_int32_t*)info->pseudo_palette)[image->fg_color]; diff --git a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c index e2a2290b628a..47439c4d276a 100644 --- a/drivers/video/matrox/matroxfb_base.c +++ b/drivers/video/matrox/matroxfb_base.c @@ -1143,6 +1143,7 @@ static int matroxfb_ioctl(struct inode *inode, struct file *file, return -EFAULT; return err; } + case VIDIOC_S_CTRL_OLD: case VIDIOC_S_CTRL: { struct v4l2_control ctrl; @@ -1750,6 +1751,12 @@ static int initMatrox2(WPMINFO struct board* b){ ACCESS_FBINFO(fbcon.pseudo_palette) = ACCESS_FBINFO(cmap); /* after __init time we are like module... no logo */ ACCESS_FBINFO(fbcon.flags) = hotplug ? FBINFO_FLAG_MODULE : FBINFO_FLAG_DEFAULT; + ACCESS_FBINFO(fbcon.flags) |= FBINFO_PARTIAL_PAN_OK | /* Prefer panning for scroll under MC viewer/edit */ + FBINFO_HWACCEL_COPYAREA | /* We have hw-assisted bmove */ + FBINFO_HWACCEL_FILLRECT | /* And fillrect */ + FBINFO_HWACCEL_IMAGEBLIT | /* And imageblit */ + FBINFO_HWACCEL_XPAN | /* And we support both horizontal */ + FBINFO_HWACCEL_YPAN; /* And vertical panning */ ACCESS_FBINFO(video.len_usable) &= PAGE_MASK; fb_alloc_cmap(&ACCESS_FBINFO(fbcon.cmap), 256, 1); @@ -1864,6 +1871,7 @@ static int initMatrox2(WPMINFO struct board* b){ /* We do not have to set currcon to 0... register_framebuffer do it for us on first console * and we do not want currcon == 0 for subsequent framebuffers */ + ACCESS_FBINFO(fbcon).device = &ACCESS_FBINFO(pcidev)->dev; if (register_framebuffer(&ACCESS_FBINFO(fbcon)) < 0) { goto failVideoIO; } diff --git a/drivers/video/matrox/matroxfb_base.h b/drivers/video/matrox/matroxfb_base.h index 79a6873f7a59..e62fcc84054b 100644 --- a/drivers/video/matrox/matroxfb_base.h +++ b/drivers/video/matrox/matroxfb_base.h @@ -93,29 +93,6 @@ #endif /* MATROXFB_DEBUG */ -#if !defined(__i386__) && !defined(__x86_64__) -#ifndef ioremap_nocache -#define ioremap_nocache(X,Y) ioremap(X,Y) -#endif -#endif - -#if defined(__alpha__) || defined(__mc68000__) || defined(__i386__) || defined(__x86_64__) -#define READx_WORKS -#define MEMCPYTOIO_WORKS -#else -/* ppc/ppc64 must use __raw_{read,write}[bwl] as we drive adapter - in big-endian mode for compatibility with XFree mga driver, and - so we do not want little-endian {read,write}[bwl] */ -#define READx_FAILS -#define MEMCPYTOIO_WRITEL -#endif - -#if defined(__mc68000__) -#define MAP_BUSTOVIRT -#else -#define MAP_IOREMAP -#endif - #ifdef DEBUG #define dprintk(X...) printk(X) #else @@ -155,22 +132,13 @@ #endif typedef struct { - u_int8_t __iomem* vaddr; + void __iomem* vaddr; } vaddr_t; -#ifdef READx_WORKS static inline unsigned int mga_readb(vaddr_t va, unsigned int offs) { return readb(va.vaddr + offs); } -static inline unsigned int mga_readw(vaddr_t va, unsigned int offs) { - return readw(va.vaddr + offs); -} - -static inline u_int32_t mga_readl(vaddr_t va, unsigned int offs) { - return readl(va.vaddr + offs); -} - static inline void mga_writeb(vaddr_t va, unsigned int offs, u_int8_t value) { writeb(value, va.vaddr + offs); } @@ -179,62 +147,42 @@ static inline void mga_writew(vaddr_t va, unsigned int offs, u_int16_t value) { writew(value, va.vaddr + offs); } -static inline void mga_writel(vaddr_t va, unsigned int offs, u_int32_t value) { - writel(value, va.vaddr + offs); -} -#else -static inline unsigned int mga_readb(vaddr_t va, unsigned int offs) { - return __raw_readb(va.vaddr + offs); -} - -static inline unsigned int mga_readw(vaddr_t va, unsigned int offs) { - return __raw_readw(va.vaddr + offs); -} - static inline u_int32_t mga_readl(vaddr_t va, unsigned int offs) { - return __raw_readl(va.vaddr + offs); -} - -static inline void mga_writeb(vaddr_t va, unsigned int offs, u_int8_t value) { - __raw_writeb(value, va.vaddr + offs); -} - -static inline void mga_writew(vaddr_t va, unsigned int offs, u_int16_t value) { - __raw_writew(value, va.vaddr + offs); + return readl(va.vaddr + offs); } static inline void mga_writel(vaddr_t va, unsigned int offs, u_int32_t value) { - __raw_writel(value, va.vaddr + offs); + writel(value, va.vaddr + offs); } -#endif -static inline void mga_memcpy_toio(vaddr_t va, unsigned int offs, const void* src, int len) { -#ifdef MEMCPYTOIO_WORKS - memcpy_toio(va.vaddr + offs, src, len); -#elif defined(MEMCPYTOIO_WRITEL) - if (offs & 3) { +static inline void mga_memcpy_toio(vaddr_t va, const void* src, int len) { +#if defined(__alpha__) || defined(__i386__) || defined(__x86_64__) + /* + * memcpy_toio works for us if: + * (1) Copies data as 32bit quantities, not byte after byte, + * (2) Performs LE ordered stores, and + * (3) It copes with unaligned source (destination is guaranteed to be page + * aligned and length is guaranteed to be multiple of 4). + */ + memcpy_toio(va.vaddr, src, len); +#else + u_int32_t __iomem* addr = va.vaddr; + + if ((unsigned long)src & 3) { while (len >= 4) { - mga_writel(va, offs, get_unaligned((u32 *)src)); - offs += 4; + writel(get_unaligned((u32 *)src), addr); + addr++; len -= 4; src += 4; } } else { while (len >= 4) { - mga_writel(va, offs, *(u32 *)src); - offs += 4; + writel(*(u32 *)src, addr); + addr++; len -= 4; src += 4; } } - if (len) { - u_int32_t tmp; - - memcpy(&tmp, src, len); - mga_writel(va, offs, tmp); - } -#else -#error "Sorry, do not know how to write block of data to device" #endif } @@ -252,25 +200,15 @@ static inline void __iomem* vaddr_va(vaddr_t va) { #define MGA_IOREMAP_FB MGA_IOREMAP_NOCACHE #define MGA_IOREMAP_MMIO MGA_IOREMAP_NOCACHE static inline int mga_ioremap(unsigned long phys, unsigned long size, int flags, vaddr_t* virt) { -#ifdef MAP_IOREMAP if (flags & MGA_IOREMAP_NOCACHE) virt->vaddr = ioremap_nocache(phys, size); else virt->vaddr = ioremap(phys, size); -#else -#ifdef MAP_BUSTOVIRT - virt->vaddr = bus_to_virt(phys); -#else -#error "Your architecture does not have neither ioremap nor bus_to_virt... Giving up" -#endif -#endif return (virt->vaddr == 0); /* 0, !0... 0, error_code in future */ } static inline void mga_iounmap(vaddr_t va) { -#ifdef MAP_IOREMAP iounmap(va.vaddr); -#endif } struct my_timming { @@ -774,11 +712,11 @@ void matroxfb_unregister_driver(struct matroxfb_driver* drv); #define DAC_XGENIOCTRL 0x2A #define DAC_XGENIODATA 0x2B -#define M_C2CTL 0x3E10 +#define M_C2CTL 0x3C10 -#ifdef __LITTLE_ENDIAN -#define MX_OPTION_BSWAP 0x00000000 +#define MX_OPTION_BSWAP 0x00000000 +#ifdef __LITTLE_ENDIAN #define M_OPMODE_4BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT) #define M_OPMODE_8BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT) #define M_OPMODE_16BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT) @@ -786,29 +724,23 @@ void matroxfb_unregister_driver(struct matroxfb_driver* drv); #define M_OPMODE_32BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT) #else #ifdef __BIG_ENDIAN -#define MX_OPTION_BSWAP 0x80000000 - -#define M_OPMODE_4BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT) /* TODO */ -#define M_OPMODE_8BPP (M_OPMODE_DMA_BE_8BPP | M_OPMODE_DIR_BE_8BPP | M_OPMODE_DMA_BLIT) -#define M_OPMODE_16BPP (M_OPMODE_DMA_BE_16BPP | M_OPMODE_DIR_BE_16BPP | M_OPMODE_DMA_BLIT) -#define M_OPMODE_24BPP (M_OPMODE_DMA_BE_8BPP | M_OPMODE_DIR_BE_8BPP | M_OPMODE_DMA_BLIT) /* TODO, ?32 */ -#define M_OPMODE_32BPP (M_OPMODE_DMA_BE_32BPP | M_OPMODE_DIR_BE_32BPP | M_OPMODE_DMA_BLIT) +#define M_OPMODE_4BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT) /* TODO */ +#define M_OPMODE_8BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_BE_8BPP | M_OPMODE_DMA_BLIT) +#define M_OPMODE_16BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_BE_16BPP | M_OPMODE_DMA_BLIT) +#define M_OPMODE_24BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_BE_8BPP | M_OPMODE_DMA_BLIT) /* TODO, ?32 */ +#define M_OPMODE_32BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_BE_32BPP | M_OPMODE_DMA_BLIT) #else #error "Byte ordering have to be defined. Cannot continue." #endif #endif -#define mga_inb(addr) mga_readb(ACCESS_FBINFO(mmio.vbase), (addr)) -#define mga_inl(addr) mga_readl(ACCESS_FBINFO(mmio.vbase), (addr)) -#define mga_outb(addr,val) mga_writeb(ACCESS_FBINFO(mmio.vbase), (addr), (val)) -#define mga_outw(addr,val) mga_writew(ACCESS_FBINFO(mmio.vbase), (addr), (val)) -#define mga_outl(addr,val) mga_writel(ACCESS_FBINFO(mmio.vbase), (addr), (val)) -#define mga_readr(port,idx) (mga_outb((port),(idx)), mga_inb((port)+1)) -#ifdef __LITTLE_ENDIAN -#define mga_setr(addr,port,val) mga_outw(addr, ((val)<<8) | (port)) -#else -#define mga_setr(addr,port,val) do { mga_outb(addr, port); mga_outb((addr)+1, val); } while (0) -#endif +#define mga_inb(addr) mga_readb(ACCESS_FBINFO(mmio.vbase), (addr)) +#define mga_inl(addr) mga_readl(ACCESS_FBINFO(mmio.vbase), (addr)) +#define mga_outb(addr,val) mga_writeb(ACCESS_FBINFO(mmio.vbase), (addr), (val)) +#define mga_outw(addr,val) mga_writew(ACCESS_FBINFO(mmio.vbase), (addr), (val)) +#define mga_outl(addr,val) mga_writel(ACCESS_FBINFO(mmio.vbase), (addr), (val)) +#define mga_readr(port,idx) (mga_outb((port),(idx)), mga_inb((port)+1)) +#define mga_setr(addr,port,val) mga_outw(addr, ((val)<<8) | (port)) #define mga_fifo(n) do {} while ((mga_inl(M_FIFOSTATUS) & 0xFF) < (n)) diff --git a/drivers/video/matrox/matroxfb_crtc2.c b/drivers/video/matrox/matroxfb_crtc2.c index 75d19696ffe4..c5230bbe1da4 100644 --- a/drivers/video/matrox/matroxfb_crtc2.c +++ b/drivers/video/matrox/matroxfb_crtc2.c @@ -603,6 +603,8 @@ static int matroxfb_dh_regit(CPMINFO struct matroxfb_dh_fb_info* m2info) { m2info->fbcon.fbops = &matroxfb_dh_ops; m2info->fbcon.flags = FBINFO_FLAG_DEFAULT; + m2info->fbcon.flags |= FBINFO_HWACCEL_XPAN | + FBINFO_HWACCEL_YPAN; m2info->fbcon.currcon = -1; m2info->fbcon.pseudo_palette = m2info->cmap; fb_alloc_cmap(&m2info->fbcon.cmap, 256, 1); diff --git a/drivers/video/pvr2fb.c b/drivers/video/pvr2fb.c index a6ecf9674f40..f4b20704db6c 100644 --- a/drivers/video/pvr2fb.c +++ b/drivers/video/pvr2fb.c @@ -939,6 +939,7 @@ static int __devinit pvr2fb_pci_probe(struct pci_dev *pdev, pvr2_fix.mmio_start = pci_resource_start(pdev, 1); pvr2_fix.mmio_len = pci_resource_len(pdev, 1); + fbinfo->device = &pdev->dev; return pvr2fb_common_init(); } diff --git a/drivers/video/radeonfb.c b/drivers/video/radeonfb.c index eedb2b8e93a5..82f8e1529498 100644 --- a/drivers/video/radeonfb.c +++ b/drivers/video/radeonfb.c @@ -3040,7 +3040,7 @@ static int radeonfb_pci_register (struct pci_dev *pdev, pci_set_drvdata(pdev, rinfo); rinfo->next = board_list; board_list = rinfo; - + ((struct fb_info *) rinfo)->device = &pdev->dev; if (register_framebuffer ((struct fb_info *) rinfo) < 0) { printk ("radeonfb: could not register framebuffer\n"); iounmap(rinfo->fb_base); diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index 825b39dbe91a..f8772da6a59a 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -1858,21 +1858,17 @@ static int __devinit rivafb_probe(struct pci_dev *pd, NVTRACE_ENTER(); assert(pd != NULL); - info = kmalloc(sizeof(struct fb_info), GFP_KERNEL); + info = framebuffer_alloc(sizeof(struct riva_par), &pd->dev); + if (!info) goto err_out; - default_par = kmalloc(sizeof(struct riva_par), GFP_KERNEL); - if (!default_par) - goto err_out_kfree; - - memset(info, 0, sizeof(struct fb_info)); - memset(default_par, 0, sizeof(struct riva_par)); + default_par = (struct riva_par *) info->par; default_par->pdev = pd; info->pixmap.addr = kmalloc(64 * 1024, GFP_KERNEL); if (info->pixmap.addr == NULL) - goto err_out_kfree1; + goto err_out_kfree; memset(info->pixmap.addr, 0, 64 * 1024); if (pci_enable_device(pd)) { @@ -1896,7 +1892,7 @@ static int __devinit rivafb_probe(struct pci_dev *pd, if(default_par->riva.Architecture == 0) { printk(KERN_ERR PFX "unknown NV_ARCH\n"); - goto err_out_kfree1; + goto err_out_free_base0; } if(default_par->riva.Architecture == NV_ARCH_10 || default_par->riva.Architecture == NV_ARCH_20 || @@ -2001,7 +1997,6 @@ static int __devinit rivafb_probe(struct pci_dev *pd, fb_destroy_modedb(info->monspecs.modedb); info->monspecs.modedb_len = 0; info->monspecs.modedb = NULL; - if (register_framebuffer(info) < 0) { printk(KERN_ERR PFX "error registering riva framebuffer\n"); @@ -2040,10 +2035,8 @@ err_out_request: pci_disable_device(pd); err_out_enable: kfree(info->pixmap.addr); -err_out_kfree1: - kfree(default_par); err_out_kfree: - kfree(info); + framebuffer_release(info); err_out: return -ENODEV; } @@ -2077,8 +2070,7 @@ static void __exit rivafb_remove(struct pci_dev *pd) pci_release_regions(pd); pci_disable_device(pd); kfree(info->pixmap.addr); - kfree(par); - kfree(info); + framebuffer_release(info); pci_set_drvdata(pd, NULL); NVTRACE_LEAVE(); } diff --git a/drivers/video/sstfb.c b/drivers/video/sstfb.c index e3ea32b6cafb..46dfcce9fcaf 100644 --- a/drivers/video/sstfb.c +++ b/drivers/video/sstfb.c @@ -1507,6 +1507,7 @@ static int __devinit sstfb_probe(struct pci_dev *pdev, fb_alloc_cmap(&info->cmap, 256, 0); /* register fb */ + info->device = &pdev->dev; if (register_framebuffer(info) < 0) { eprintk("can't register framebuffer.\n"); goto fail; diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index 503ecc3569e8..263a40288118 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c @@ -202,7 +202,6 @@ static unsigned long do_lfb_size(struct tdfx_par *par, unsigned short); */ static int nopan = 0; static int nowrap = 1; // not implemented (yet) -static int inverse = 0; static char *mode_option __initdata = NULL; /* ------------------------------------------------------------------------- @@ -921,7 +920,6 @@ static void tdfxfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect tdfx_outl(par, COMMAND_2D, COMMAND_2D_FILLRECT | (tdfx_rop << 24)); tdfx_outl(par, DSTSIZE, rect->width | (rect->height << 16)); tdfx_outl(par, LAUNCH_2D, rect->dx | (rect->dy << 16)); - banshee_wait_idle(info); } /* @@ -957,7 +955,6 @@ static void tdfxfb_copyarea(struct fb_info *info, const struct fb_copyarea *area tdfx_outl(par, DSTSIZE, area->width | (area->height << 16)); tdfx_outl(par, DSTXY, dx | (dy << 16)); tdfx_outl(par, LAUNCH_2D, sx | (sy << 16)); - banshee_wait_idle(info); } static void tdfxfb_imageblit(struct fb_info *info, const struct fb_image *image) @@ -1025,7 +1022,6 @@ static void tdfxfb_imageblit(struct fb_info *info, const struct fb_image *image) case 2: tdfx_outl(par, LAUNCH_2D,*(u16*)chardata); break; case 3: tdfx_outl(par, LAUNCH_2D,*(u16*)chardata | ((chardata[3]) << 24)); break; } - banshee_wait_idle(info); } #endif /* CONFIG_FB_3DFX_ACCEL */ @@ -1397,10 +1393,7 @@ void tdfxfb_setup(char *options) while ((this_opt = strsep(&options, ",")) != NULL) { if (!*this_opt) continue; - if (!strcmp(this_opt, "inverse")) { - inverse = 1; - fb_invert_cmaps(); - } else if(!strcmp(this_opt, "nopan")) { + if(!strcmp(this_opt, "nopan")) { nopan = 1; } else if(!strcmp(this_opt, "nowrap")) { nowrap = 1; diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c index 07ee3202e92c..ffe811038a9a 100644 --- a/drivers/video/tgafb.c +++ b/drivers/video/tgafb.c @@ -1454,6 +1454,7 @@ tgafb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent) tgafb_set_par(&all->info); tgafb_init_fix(&all->info); + all->info.device = &pdev->dev; if (register_framebuffer(&all->info) < 0) { printk(KERN_ERR "tgafb: Could not register framebuffer\n"); ret = -EINVAL; diff --git a/drivers/video/tridentfb.c b/drivers/video/tridentfb.c index aea0e05c882f..83184c78606a 100644 --- a/drivers/video/tridentfb.c +++ b/drivers/video/tridentfb.c @@ -1164,6 +1164,7 @@ static int __devinit trident_pci_probe(struct pci_dev * dev, const struct pci_de default_var.accel_flags &= ~FB_ACCELF_TEXT; default_var.activate |= FB_ACTIVATE_NOW; fb_info.var = default_var; + fb_info.device = &dev->dev; if (register_framebuffer(&fb_info) < 0) { output("Could not register Trident framebuffer\n"); return -EINVAL; diff --git a/drivers/video/vesafb.c b/drivers/video/vesafb.c index c5c649d9b1ae..9429e6c63708 100644 --- a/drivers/video/vesafb.c +++ b/drivers/video/vesafb.c @@ -49,7 +49,8 @@ static struct fb_fix_screeninfo vesafb_fix __initdata = { static int inverse = 0; static int mtrr = 1; -static int vram __initdata = 0; /* Set amount of memory to be used */ +static int vram_remap __initdata = 0; /* Set amount of memory to be used */ +static int vram_total __initdata = 0; /* Set total amount of memory */ static int pmi_setpal = 0; /* pmi for palette changes ??? */ static int ypan = 0; /* 0..nothing, 1..ypan, 2..ywrap */ static unsigned short *pmi_base = NULL; @@ -209,8 +210,10 @@ int __init vesafb_setup(char *options) mtrr=1; else if (! strcmp(this_opt, "nomtrr")) mtrr=0; - else if (! strncmp(this_opt, "vram:", 5)) - vram = simple_strtoul(this_opt+5, NULL, 0); + else if (! strncmp(this_opt, "vtotal:", 7)) + vram_total = simple_strtoul(this_opt+7, NULL, 0); + else if (! strncmp(this_opt, "vremap:", 7)) + vram_remap = simple_strtoul(this_opt+7, NULL, 0); } return 0; } @@ -220,6 +223,9 @@ static int __init vesafb_probe(struct device *device) struct platform_device *dev = to_platform_device(device); struct fb_info *info; int i, err; + unsigned int size_vmode; + unsigned int size_remap; + unsigned int size_total; if (screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB) return -ENXIO; @@ -231,32 +237,41 @@ static int __init vesafb_probe(struct device *device) vesafb_defined.xres = screen_info.lfb_width; vesafb_defined.yres = screen_info.lfb_height; vesafb_fix.line_length = screen_info.lfb_linelength; - - /* Allocate enough memory for double buffering */ - vesafb_fix.smem_len = screen_info.lfb_width * screen_info.lfb_height * vesafb_defined.bits_per_pixel >> 2; - - /* check that we don't remap more memory than old cards have */ - if (vesafb_fix.smem_len > (screen_info.lfb_size * 65536)) - vesafb_fix.smem_len = screen_info.lfb_size * 65536; - - /* Set video size according to vram boot option */ - if (vram) - vesafb_fix.smem_len = vram * 1024 * 1024; - vesafb_fix.visual = (vesafb_defined.bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; - /* limit framebuffer size to 16 MB. Otherwise we'll eat tons of - * kernel address space for nothing if the gfx card has alot of - * memory (>= 128 MB isn't uncommon these days ...) */ - if (vesafb_fix.smem_len > 16 * 1024 * 1024) - vesafb_fix.smem_len = 16 * 1024 * 1024; + /* size_vmode -- that is the amount of memory needed for the + * used video mode, i.e. the minimum amount of + * memory we need. */ + size_vmode = vesafb_defined.yres * vesafb_fix.line_length; + + /* size_total -- all video memory we have. Used for mtrr + * entries, ressource allocation and bounds + * checking. */ + size_total = screen_info.lfb_size * 65536; + if (vram_total) + size_total = vram_total * 1024 * 1024; + if (size_total < size_vmode) + size_total = size_vmode; + + /* size_remap -- the amount of video memory we are going to + * use for vesafb. With modern cards it is no + * option to simply use size_total as that + * wastes plenty of kernel address space. */ + size_remap = size_vmode * 2; + if (vram_remap) + size_remap = vram_remap * 1024 * 1024; + if (size_remap < size_vmode) + size_remap = size_vmode; + if (size_remap > size_total) + size_remap = size_total; + vesafb_fix.smem_len = size_remap; #ifndef __i386__ screen_info.vesapm_seg = 0; #endif - if (!request_mem_region(vesafb_fix.smem_start, vesafb_fix.smem_len, "vesafb")) { + if (!request_mem_region(vesafb_fix.smem_start, size_total, "vesafb")) { printk(KERN_WARNING "vesafb: abort, cannot reserve video memory at 0x%lx\n", vesafb_fix.smem_start); @@ -281,8 +296,10 @@ static int __init vesafb_probe(struct device *device) goto err; } - printk(KERN_INFO "vesafb: framebuffer at 0x%lx, mapped to 0x%p, size %dk\n", - vesafb_fix.smem_start, info->screen_base, vesafb_fix.smem_len/1024); + printk(KERN_INFO "vesafb: framebuffer at 0x%lx, mapped to 0x%p, " + "using %dk, total %dk\n", + vesafb_fix.smem_start, info->screen_base, + size_remap/1024, size_total/1024); printk(KERN_INFO "vesafb: mode is %dx%dx%d, linelength=%d, pages=%d\n", vesafb_defined.xres, vesafb_defined.yres, vesafb_defined.bits_per_pixel, vesafb_fix.line_length, screen_info.pages); @@ -362,7 +379,7 @@ static int __init vesafb_probe(struct device *device) request_region(0x3c0, 32, "vesafb"); if (mtrr) { - int temp_size = vesafb_fix.smem_len; + int temp_size = size_total; /* Find the largest power-of-two */ while (temp_size & (temp_size - 1)) temp_size &= (temp_size - 1); @@ -393,7 +410,7 @@ static int __init vesafb_probe(struct device *device) return 0; err: framebuffer_release(info); - release_mem_region(vesafb_fix.smem_start, vesafb_fix.smem_len); + release_mem_region(vesafb_fix.smem_start, size_total); return err; } diff --git a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c index 2e132fb61562..f2b053c0f214 100644 --- a/drivers/video/vga16fb.c +++ b/drivers/video/vga16fb.c @@ -123,7 +123,7 @@ static struct fb_fix_screeninfo vga16fb_fix __initdata = { suitable instruction is the x86 bitwise OR. The following read-modify-write routine should optimize to one such bitwise OR. */ -static inline void rmw(volatile char *p) +static inline void rmw(volatile char __iomem *p) { readb(p); writeb(1, p); @@ -883,7 +883,7 @@ void vga_8planes_fillrect(struct fb_info *info, const struct fb_fillrect *rect) char oldmask = selectmask(); int line_ofs, height; char oldop, oldsr; - char *where; + char __iomem *where; dx /= 4; where = info->screen_base + dx + rect->dy * info->fix.line_length; @@ -932,7 +932,7 @@ void vga_8planes_fillrect(struct fb_info *info, const struct fb_fillrect *rect) void vga16fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { int x, x2, y2, vxres, vyres, width, height, line_ofs; - char *dst; + char __iomem *dst; vxres = info->var.xres_virtual; vyres = info->var.yres_virtual; @@ -1012,7 +1012,8 @@ void vga_8planes_copyarea(struct fb_info *info, const struct fb_copyarea *area) char oldsr = setsr(0xf); int height, line_ofs, x; u32 sx, dx, width; - char *dest, *src; + char __iomem *dest; + char __iomem *src; height = area->height; @@ -1063,7 +1064,8 @@ void vga16fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy; int x, x2, y2, old_dx, old_dy, vxres, vyres; int height, width, line_ofs; - char *dst = NULL, *src = NULL; + char __iomem *dst = NULL; + char __iomem *src = NULL; vxres = info->var.xres_virtual; vyres = info->var.yres_virtual; @@ -1174,7 +1176,7 @@ void vga_8planes_imageblit(struct fb_info *info, const struct fb_image *image) char oldmask = selectmask(); const char *cdat = image->data; u32 dx = image->dx; - char *where; + char __iomem *where; int y; dx /= 4; @@ -1198,10 +1200,11 @@ void vga_8planes_imageblit(struct fb_info *info, const struct fb_image *image) void vga_imageblit_expand(struct fb_info *info, const struct fb_image *image) { - char *where = info->screen_base + (image->dx/8) + + char __iomem *where = info->screen_base + (image->dx/8) + image->dy * info->fix.line_length; struct vga16fb_par *par = (struct vga16fb_par *) info->par; - char *cdat = (char *) image->data, *dst; + char *cdat = (char *) image->data; + char __iomem *dst; int x, y; switch (info->fix.type) { @@ -1265,9 +1268,11 @@ void vga_imageblit_color(struct fb_info *info, const struct fb_image *image) * Draw logo */ struct vga16fb_par *par = (struct vga16fb_par *) info->par; - char *where = info->screen_base + image->dy * info->fix.line_length + + char __iomem *where = + info->screen_base + image->dy * info->fix.line_length + image->dx/8; - const char *cdat = image->data, *dst; + const char *cdat = image->data; + char __iomem *dst; int x, y; switch (info->fix.type) { @@ -1306,7 +1311,7 @@ void vga16fb_imageblit(struct fb_info *info, const struct fb_image *image) { if (image->depth == 1) vga_imageblit_expand(info, image); - else if (image->depth <= info->var.bits_per_pixel) + else vga_imageblit_color(info, image); } @@ -1354,7 +1359,7 @@ int __init vga16fb_init(void) /* XXX share VGA_FB_PHYS and I/O region with vgacon and others */ - vga16fb.screen_base = (void *)VGA_MAP_MEM(VGA_FB_PHYS); + vga16fb.screen_base = (void __iomem *)VGA_MAP_MEM(VGA_FB_PHYS); if (!vga16fb.screen_base) { printk(KERN_ERR "vga16fb: unable to map device\n"); ret = -ENOMEM; |
