diff options
109 files changed, 4813 insertions, 2651 deletions
diff --git a/Documentation/block/ioprio.txt b/Documentation/block/ioprio.txt new file mode 100644 index 00000000000..96ccf681075 --- /dev/null +++ b/Documentation/block/ioprio.txt @@ -0,0 +1,176 @@ +Block io priorities +=================== + + +Intro +----- + +With the introduction of cfq v3 (aka cfq-ts or time sliced cfq), basic io +priorities is supported for reads on files. This enables users to io nice +processes or process groups, similar to what has been possible to cpu +scheduling for ages. This document mainly details the current possibilites +with cfq, other io schedulers do not support io priorities so far. + +Scheduling classes +------------------ + +CFQ implements three generic scheduling classes that determine how io is +served for a process. + +IOPRIO_CLASS_RT: This is the realtime io class. This scheduling class is given +higher priority than any other in the system, processes from this class are +given first access to the disk every time. Thus it needs to be used with some +care, one io RT process can starve the entire system. Within the RT class, +there are 8 levels of class data that determine exactly how much time this +process needs the disk for on each service. In the future this might change +to be more directly mappable to performance, by passing in a wanted data +rate instead. + +IOPRIO_CLASS_BE: This is the best-effort scheduling class, which is the default +for any process that hasn't set a specific io priority. The class data +determines how much io bandwidth the process will get, it's directly mappable +to the cpu nice levels just more coarsely implemented. 0 is the highest +BE prio level, 7 is the lowest. The mapping between cpu nice level and io +nice level is determined as: io_nice = (cpu_nice + 20) / 5. + +IOPRIO_CLASS_IDLE: This is the idle scheduling class, processes running at this +level only get io time when no one else needs the disk. The idle class has no +class data, since it doesn't really apply here. + +Tools +----- + +See below for a sample ionice tool. Usage: + +# ionice -c<class> -n<level> -p<pid> + +If pid isn't given, the current process is assumed. IO priority settings +are inherited on fork, so you can use ionice to start the process at a given +level: + +# ionice -c2 -n0 /bin/ls + +will run ls at the best-effort scheduling class at the highest priority. +For a running process, you can give the pid instead: + +# ionice -c1 -n2 -p100 + +will change pid 100 to run at the realtime scheduling class, at priority 2. + +---> snip ionice.c tool <--- + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <getopt.h> +#include <unistd.h> +#include <sys/ptrace.h> +#include <asm/unistd.h> + +extern int sys_ioprio_set(int, int, int); +extern int sys_ioprio_get(int, int); + +#if defined(__i386__) +#define __NR_ioprio_set 289 +#define __NR_ioprio_get 290 +#elif defined(__ppc__) +#define __NR_ioprio_set 273 +#define __NR_ioprio_get 274 +#elif defined(__x86_64__) +#define __NR_ioprio_set 251 +#define __NR_ioprio_get 252 +#elif defined(__ia64__) +#define __NR_ioprio_set 1274 +#define __NR_ioprio_get 1275 +#else +#error "Unsupported arch" +#endif + +_syscall3(int, ioprio_set, int, which, int, who, int, ioprio); +_syscall2(int, ioprio_get, int, which, int, who); + +enum { + IOPRIO_CLASS_NONE, + IOPRIO_CLASS_RT, + IOPRIO_CLASS_BE, + IOPRIO_CLASS_IDLE, +}; + +enum { + IOPRIO_WHO_PROCESS = 1, + IOPRIO_WHO_PGRP, + IOPRIO_WHO_USER, +}; + +#define IOPRIO_CLASS_SHIFT 13 + +const char *to_prio[] = { "none", "realtime", "best-effort", "idle", }; + +int main(int argc, char *argv[]) +{ + int ioprio = 4, set = 0, ioprio_class = IOPRIO_CLASS_BE; + int c, pid = 0; + + while ((c = getopt(argc, argv, "+n:c:p:")) != EOF) { + switch (c) { + case 'n': + ioprio = strtol(optarg, NULL, 10); + set = 1; + break; + case 'c': + ioprio_class = strtol(optarg, NULL, 10); + set = 1; + break; + case 'p': + pid = strtol(optarg, NULL, 10); + break; + } + } + + switch (ioprio_class) { + case IOPRIO_CLASS_NONE: + ioprio_class = IOPRIO_CLASS_BE; + break; + case IOPRIO_CLASS_RT: + case IOPRIO_CLASS_BE: + break; + case IOPRIO_CLASS_IDLE: + ioprio = 7; + break; + default: + printf("bad prio class %d\n", ioprio_class); + return 1; + } + + if (!set) { + if (!pid && argv[optind]) + pid = strtol(argv[optind], NULL, 10); + + ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid); + + printf("pid=%d, %d\n", pid, ioprio); + + if (ioprio == -1) + perror("ioprio_get"); + else { + ioprio_class = ioprio >> IOPRIO_CLASS_SHIFT; + ioprio = ioprio & 0xff; + printf("%s: prio %d\n", to_prio[ioprio_class], ioprio); + } + } else { + if (ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio | ioprio_class << IOPRIO_CLASS_SHIFT) == -1) { + perror("ioprio_set"); + return 1; + } + + if (argv[optind]) + execvp(argv[optind], &argv[optind]); + } + + return 0; +} + +---> snip ionice.c tool <--- + + +March 11 2005, Jens Axboe <axboe@suse.de> diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index f44bb5567c5..89cd417651e 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1115,7 +1115,7 @@ running once the system is up. See Documentation/ramdisk.txt. psmouse.proto= [HW,MOUSE] Highest PS2 mouse protocol extension to - probe for (bare|imps|exps). + probe for (bare|imps|exps|lifebook|any). psmouse.rate= [HW,MOUSE] Set desired mouse report rate, in reports per second. psmouse.resetafter= diff --git a/arch/i386/kernel/syscall_table.S b/arch/i386/kernel/syscall_table.S index 442a6e937b1..3db9a04aec6 100644 --- a/arch/i386/kernel/syscall_table.S +++ b/arch/i386/kernel/syscall_table.S @@ -289,3 +289,5 @@ ENTRY(sys_call_table) .long sys_add_key .long sys_request_key .long sys_keyctl + .long sys_ioprio_set + .long sys_ioprio_get /* 290 */ diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index b1d5d3d5276..785a51b0ad8 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -1577,8 +1577,8 @@ sys_call_table: data8 sys_add_key data8 sys_request_key data8 sys_keyctl - data8 sys_ni_syscall - data8 sys_ni_syscall // 1275 + data8 sys_ioprio_set + data8 sys_ioprio_get // 1275 data8 sys_set_zone_reclaim data8 sys_ni_syscall data8 sys_ni_syscall diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index b6a63a49a23..191a8def3bd 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -1449,3 +1449,5 @@ _GLOBAL(sys_call_table) .long sys_request_key /* 270 */ .long sys_keyctl .long sys_waitid + .long sys_ioprio_set + .long sys_ioprio_get diff --git a/drivers/block/as-iosched.c b/drivers/block/as-iosched.c index 3410b4d294b..91aeb678135 100644 --- a/drivers/block/as-iosched.c +++ b/drivers/block/as-iosched.c @@ -1806,7 +1806,8 @@ static void as_put_request(request_queue_t *q, struct request *rq) rq->elevator_private = NULL; } -static int as_set_request(request_queue_t *q, struct request *rq, int gfp_mask) +static int as_set_request(request_queue_t *q, struct request *rq, + struct bio *bio, int gfp_mask) { struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = mempool_alloc(ad->arq_pool, gfp_mask); @@ -1827,7 +1828,7 @@ static int as_set_request(request_queue_t *q, struct request *rq, int gfp_mask) return 1; } -static int as_may_queue(request_queue_t *q, int rw) +static int as_may_queue(request_queue_t *q, int rw, struct bio *bio) { int ret = ELV_MQUEUE_MAY; struct as_data *ad = q->elevator->elevator_data; diff --git a/drivers/block/cfq-iosched.c b/drivers/block/cfq-iosched.c index 3ac47dde64d..ff1cc968f96 100644 --- a/drivers/block/cfq-iosched.c +++ b/drivers/block/cfq-iosched.c @@ -21,22 +21,34 @@ #include <linux/hash.h> #include <linux/rbtree.h> #include <linux/mempool.h> - -static unsigned long max_elapsed_crq; -static unsigned long max_elapsed_dispatch; +#include <linux/ioprio.h> +#include <linux/writeback.h> /* * tunables */ static int cfq_quantum = 4; /* max queue in one round of service */ static int cfq_queued = 8; /* minimum rq allocate limit per-queue*/ -static int cfq_service = HZ; /* period over which service is avg */ -static int cfq_fifo_expire_r = HZ / 2; /* fifo timeout for sync requests */ -static int cfq_fifo_expire_w = 5 * HZ; /* fifo timeout for async requests */ -static int cfq_fifo_rate = HZ / 8; /* fifo expiry rate */ +static int cfq_fifo_expire[2] = { HZ / 4, HZ / 8 }; static int cfq_back_max = 16 * 1024; /* maximum backwards seek, in KiB */ static int cfq_back_penalty = 2; /* penalty of a backwards seek */ +static int cfq_slice_sync = HZ / 10; +static int cfq_slice_async = HZ / 25; +static int cfq_slice_async_rq = 2; +static int cfq_slice_idle = HZ / 100; + +#define CFQ_IDLE_GRACE (HZ / 10) +#define CFQ_SLICE_SCALE (5) + +#define CFQ_KEY_ASYNC (0) +#define CFQ_KEY_ANY (0xffff) + +/* + * disable queueing at the driver/hardware level + */ +static int cfq_max_depth = 1; + /* * for the hash of cfqq inside the cfqd */ @@ -55,6 +67,7 @@ static int cfq_back_penalty = 2; /* penalty of a backwards seek */ #define list_entry_hash(ptr) hlist_entry((ptr), struct cfq_rq, hash) #define list_entry_cfqq(ptr) list_entry((ptr), struct cfq_queue, cfq_list) +#define list_entry_fifo(ptr) list_entry((ptr), struct request, queuelist) #define RQ_DATA(rq) (rq)->elevator_private @@ -75,78 +88,110 @@ static int cfq_back_penalty = 2; /* penalty of a backwards seek */ #define rb_entry_crq(node) rb_entry((node), struct cfq_rq, rb_node) #define rq_rb_key(rq) (rq)->sector -/* - * threshold for switching off non-tag accounting - */ -#define CFQ_MAX_TAG (4) - -/* - * sort key types and names - */ -enum { - CFQ_KEY_PGID, - CFQ_KEY_TGID, - CFQ_KEY_UID, - CFQ_KEY_GID, - CFQ_KEY_LAST, -}; - -static char *cfq_key_types[] = { "pgid", "tgid", "uid", "gid", NULL }; - static kmem_cache_t *crq_pool; static kmem_cache_t *cfq_pool; static kmem_cache_t *cfq_ioc_pool; +#define CFQ_PRIO_LISTS IOPRIO_BE_NR +#define cfq_class_idle(cfqq) ((cfqq)->ioprio_class == IOPRIO_CLASS_IDLE) +#define cfq_class_be(cfqq) ((cfqq)->ioprio_class == IOPRIO_CLASS_BE) +#define cfq_class_rt(cfqq) ((cfqq)->ioprio_class == IOPRIO_CLASS_RT) + +#define ASYNC (0) +#define SYNC (1) + +#define cfq_cfqq_dispatched(cfqq) \ + ((cfqq)->on_dispatch[ASYNC] + (cfqq)->on_dispatch[SYNC]) + +#define cfq_cfqq_class_sync(cfqq) ((cfqq)->key != CFQ_KEY_ASYNC) + +#define cfq_cfqq_sync(cfqq) \ + (cfq_cfqq_class_sync(cfqq) || (cfqq)->on_dispatch[SYNC]) + +/* + * Per block device queue structure + */ struct cfq_data { - struct list_head rr_list; + atomic_t ref; + request_queue_t *queue; + + /* + * rr list of queues with requests and the count of them + */ + struct list_head rr_list[CFQ_PRIO_LISTS]; + struct list_head busy_rr; + struct list_head cur_rr; + struct list_head idle_rr; + unsigned int busy_queues; + + /* + * non-ordered list of empty cfqq's + */ struct list_head empty_list; + /* + * cfqq lookup hash + */ struct hlist_head *cfq_hash; - struct hlist_head *crq_hash; - /* queues on rr_list (ie they have pending requests */ - unsigned int busy_queues; + /* + * global crq hash for all queues + */ + struct hlist_head *crq_hash; unsigned int max_queued; - atomic_t ref; + mempool_t *crq_pool; - int key_type; + int rq_in_driver; - mempool_t *crq_pool; + /* + * schedule slice state info + */ + /* + * idle window management + */ + struct timer_list idle_slice_timer; + struct work_struct unplug_work; - request_queue_t *queue; + struct cfq_queue *active_queue; + struct cfq_io_context *active_cic; + int cur_prio, cur_end_prio; + unsigned int dispatch_slice; + + struct timer_list idle_class_timer; sector_t last_sector; + unsigned long last_end_request; - int rq_in_driver; + unsigned int rq_starved; /* * tunables, see top of file */ unsigned int cfq_quantum; unsigned int cfq_queued; - unsigned int cfq_fifo_expire_r; - unsigned int cfq_fifo_expire_w; - unsigned int cfq_fifo_batch_expire; + unsigned int cfq_fifo_expire[2]; unsigned int cfq_back_penalty; unsigned int cfq_back_max; - unsigned int find_best_crq; - - unsigned int cfq_tagged; + unsigned int cfq_slice[2]; + unsigned int cfq_slice_async_rq; + unsigned int cfq_slice_idle; + unsigned int cfq_max_depth; }; +/* + * Per process-grouping structure + */ struct cfq_queue { /* reference count */ atomic_t ref; /* parent cfq_data */ struct cfq_data *cfqd; - /* hash of mergeable requests */ + /* cfqq lookup hash */ struct hlist_node cfq_hash; /* hash key */ - unsigned long key; - /* whether queue is on rr (or empty) list */ - int on_rr; + unsigned int key; /* on either rr or empty list of cfqd */ struct list_head cfq_list; /* sorted list of pending requests */ @@ -158,21 +203,22 @@ struct cfq_queue { /* currently allocated requests */ int allocated[2]; /* fifo list of requests in sort_list */ - struct list_head fifo[2]; - /* last time fifo expired */ - unsigned long last_fifo_expire; + struct list_head fifo; - int key_type; + unsigned long slice_start; + unsigned long slice_end; + unsigned long slice_left; + unsigned long service_last; - unsigned long service_start; - unsigned long service_used; + /* number of requests that are on the dispatch list */ + int on_dispatch[2]; - unsigned int max_rate; + /* io prio of this group */ + unsigned short ioprio, org_ioprio; + unsigned short ioprio_class, org_ioprio_class; - /* number of requests that have been handed to the driver */ - int in_flight; - /* number of currently allocated requests */ - int alloc_limit[2]; + /* various state flags, see below */ + unsigned int flags; }; struct cfq_rq { @@ -184,42 +230,79 @@ struct cfq_rq { struct cfq_queue *cfq_queue; struct cfq_io_context *io_context; - unsigned long service_start; - unsigned long queue_start; + unsigned int crq_flags; +}; + +enum cfqq_state_flags { + CFQ_CFQQ_FLAG_on_rr = 0, + CFQ_CFQQ_FLAG_wait_request, + CFQ_CFQQ_FLAG_must_alloc, + CFQ_CFQQ_FLAG_must_alloc_slice, + CFQ_CFQQ_FLAG_must_dispatch, + CFQ_CFQQ_FLAG_fifo_expire, + CFQ_CFQQ_FLAG_idle_window, + CFQ_CFQQ_FLAG_prio_changed, + CFQ_CFQQ_FLAG_expired, +}; - unsigned int in_flight : 1; - unsigned int accounted : 1; - unsigned int is_sync : 1; - unsigned int is_write : 1; +#define CFQ_CFQQ_FNS(name) \ +static inline void cfq_mark_cfqq_##name(struct cfq_queue *cfqq) \ +{ \ + cfqq->flags |= (1 << CFQ_CFQQ_FLAG_##name); \ +} \ +static inline void cfq_clear_cfqq_##name(struct cfq_queue *cfqq) \ +{ \ + cfqq->flags &= ~(1 << CFQ_CFQQ_FLAG_##name); \ +} \ +static inline int cfq_cfqq_##name(const struct cfq_queue *cfqq) \ +{ \ + return (cfqq->flags & (1 << CFQ_CFQQ_FLAG_##name)) != 0; \ +} + +CFQ_CFQQ_FNS(on_rr); +CFQ_CFQQ_FNS(wait_request); +CFQ_CFQQ_FNS(must_alloc); +CFQ_CFQQ_FNS(must_alloc_slice); +CFQ_CFQQ_FNS(must_dispatch); +CFQ_CFQQ_FNS(fifo_expire); +CFQ_CFQQ_FNS(idle_window); +CFQ_CFQQ_FNS(prio_changed); +CFQ_CFQQ_FNS(expired); +#undef CFQ_CFQQ_FNS + +enum cfq_rq_state_flags { + CFQ_CRQ_FLAG_in_flight = 0, + CFQ_CRQ_FLAG_in_driver, + CFQ_CRQ_FLAG_is_sync, + CFQ_CRQ_FLAG_requeued, }; -static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *, unsigned long); +#define CFQ_CRQ_FNS(name) \ +static inline void cfq_mark_crq_##name(struct cfq_rq *crq) \ +{ \ + crq->crq_flags |= (1 << CFQ_CRQ_FLAG_##name); \ +} \ +static inline void cfq_clear_crq_##name(struct cfq_rq *crq) \ +{ \ + crq->crq_flags &= ~(1 << CFQ_CRQ_FLAG_##name); \ +} \ +static inline int cfq_crq_##name(const struct cfq_rq *crq) \ +{ \ + return (crq->crq_flags & (1 << CFQ_CRQ_FLAG_##name)) != 0; \ +} + +CFQ_CRQ_FNS(in_flight); +CFQ_CRQ_FNS(in_driver); +CFQ_CRQ_FNS(is_sync); +CFQ_CRQ_FNS(requeued); +#undef CFQ_CRQ_FNS + +static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *, unsigned int, unsigned short); static void cfq_dispatch_sort(request_queue_t *, struct cfq_rq *); -static void cfq_update_next_crq(struct cfq_rq *); static void cfq_put_cfqd(struct cfq_data *cfqd); +static inline int cfq_pending_requests(struct cfq_data *cfqd); -/* - * what the fairness is based on (ie how processes are grouped and - * differentiated) - */ -static inline unsigned long -cfq_hash_key(struct cfq_data *cfqd, struct task_struct *tsk) -{ - /* - * optimize this so that ->key_type is the offset into the struct - */ - switch (cfqd->key_type) { - case CFQ_KEY_PGID: - return process_group(tsk); - default: - case CFQ_KEY_TGID: - return tsk->tgid; - case CFQ_KEY_UID: - return tsk->uid; - case CFQ_KEY_GID: - return tsk->gid; - } -} +#define process_sync(tsk) ((tsk)->flags & PF_SYNCWRITE) /* * lots of deadline iosched dupes, can be abstracted later... @@ -235,16 +318,12 @@ static void cfq_remove_merge_hints(request_queue_t *q, struct cfq_rq *crq) if (q->last_merge == crq->request) q->last_merge = NULL; - - cfq_update_next_crq(crq); } static inline void cfq_add_crq_hash(struct cfq_data *cfqd, struct cfq_rq *crq) { const int hash_idx = CFQ_MHASH_FN(rq_hash_key(crq->request)); - BUG_ON(!hlist_unhashed(&crq->hash)); - hlist_add_head(&crq->hash, &cfqd->crq_hash[hash_idx]); } @@ -257,8 +336,6 @@ static struct request *cfq_find_rq_hash(struct cfq_data *cfqd, sector_t offset) struct cfq_rq *crq = list_entry_hash(entry); struct request *__rq = crq->request; - BUG_ON(hlist_unhashed(&crq->hash)); - if (!rq_mergeable(__rq)) { cfq_del_crq_hash(crq); continue; @@ -287,36 +364,16 @@ cfq_choose_req(struct cfq_data *cfqd, struct cfq_rq *crq1, struct cfq_rq *crq2) return crq2; if (crq2 == NULL) return crq1; + if (cfq_crq_requeued(crq1)) + return crq1; + if (cfq_crq_requeued(crq2)) + return crq2; s1 = crq1->request->sector; s2 = crq2->request->sector; last = cfqd->last_sector; -#if 0 - if (!list_empty(&cfqd->queue->queue_head)) { - struct list_head *entry = &cfqd->queue->queue_head; - unsigned long distance = ~0UL; - struct request *rq; - - while ((entry = entry->prev) != &cfqd->queue->queue_head) { - rq = list_entry_rq(entry); - - if (blk_barrier_rq(rq)) - break; - - if (distance < abs(s1 - rq->sector + rq->nr_sectors)) { - distance = abs(s1 - rq->sector +rq->nr_sectors); - last = rq->sector + rq->nr_sectors; - } - if (distance < abs(s2 - rq->sector + rq->nr_sectors)) { - distance = abs(s2 - rq->sector +rq->nr_sectors); - last = rq->sector + rq->nr_sectors; - } - } - } -#endif - /* * by definition, 1KiB is 2 sectors */ @@ -377,11 +434,14 @@ cfq_find_next_crq(struct cfq_data *cfqd, struct cfq_queue *cfqq, struct cfq_rq *crq_next = NULL, *crq_prev = NULL; struct rb_node *rbnext, *rbprev; - if (!ON_RB(&last->rb_node)) - return NULL; - - if ((rbnext = rb_next(&last->rb_node)) == NULL) + rbnext = NULL; + if (ON_RB(&last->rb_node)) + rbnext = rb_next(&last->rb_node); + if (!rbnext) { rbnext = rb_first(&cfqq->sort_list); + if (rbnext == &last->rb_node) + rbnext = NULL; + } rbprev = rb_prev(&last->rb_node); @@ -401,67 +461,53 @@ static void cfq_update_next_crq(struct cfq_rq *crq) cfqq->next_crq = cfq_find_next_crq(cfqq->cfqd, cfqq, crq); } -static int cfq_check_sort_rr_list(struct cfq_queue *cfqq) +static void cfq_resort_rr_list(struct cfq_queue *cfqq, int preempted) { - struct list_head *head = &cfqq->cfqd->rr_list; - struct list_head *next, *prev; - - /* - * list might still be ordered - */ - next = cfqq->cfq_list.next; - if (next != head) { - struct cfq_queue *cnext = list_entry_cfqq(next); + struct cfq_data *cfqd = cfqq->cfqd; + struct list_head *list, *entry; - if (cfqq->service_used > cnext->service_used) - return 1; - } + BUG_ON(!cfq_cfqq_on_rr(cfqq)); - prev = cfqq->cfq_list.prev; - if (prev != head) { - struct cfq_queue *cprev = list_entry_cfqq(prev); + list_del(&cfqq->cfq_list); - if (cfqq->service_used < cprev->service_used) - return 1; + if (cfq_class_rt(cfqq)) + list = &cfqd->cur_rr; + else if (cfq_class_idle(cfqq)) + list = &cfqd->idle_rr; + else { + /* + * if cfqq has requests in flight, don't allow it to be + * found in cfq_set_active_queue before it has finished them. + * this is done to increase fairness between a process that + * has lots of io pending vs one that only generates one + * sporadically or synchronously + */ + if (cfq_cfqq_dispatched(cfqq)) + list = &cfqd->busy_rr; + else + list = &cfqd->rr_list[cfqq->ioprio]; } - return 0; -} - -static void cfq_sort_rr_list(struct cfq_queue *cfqq, int new_queue) -{ - struct list_head *entry = &cfqq->cfqd->rr_list; - - if (!cfqq->on_rr) - return; - if (!new_queue && !cfq_check_sort_rr_list(cfqq)) + /* + * if queue was preempted, just add to front to be fair. busy_rr + * isn't sorted. + */ + if (preempted || list == &cfqd->busy_rr) { + list_add(&cfqq->cfq_list, list); return; - - list_del(&cfqq->cfq_list); + } /* - * sort by our mean service_used, sub-sort by in-flight requests + * sort by when queue was last serviced */ - while ((entry = entry->prev) != &cfqq->cfqd->rr_list) { + entry = list; + while ((entry = entry->prev) != list) { struct cfq_queue *__cfqq = list_entry_cfqq(entry); - if (cfqq->service_used > __cfqq->service_used) + if (!__cfqq->service_last) + break; + if (time_before(__cfqq->service_last, cfqq->service_last)) break; - else if (cfqq->service_used == __cfqq->service_used) { - struct list_head *prv; - - while ((prv = entry->prev) != &cfqq->cfqd->rr_list) { - __cfqq = list_entry_cfqq(prv); - - WARN_ON(__cfqq->service_used > cfqq->service_used); - if (cfqq->service_used != __cfqq->service_used) - break; - if (cfqq->in_flight > __cfqq->in_flight) - break; - - entry = prv; - } - } } list_add(&cfqq->cfq_list, entry); @@ -469,28 +515,24 @@ static void cfq_sort_rr_list(struct cfq_queue *cfqq, int new_queue) /* * add to busy list of queues for service, trying to be fair in ordering - * the pending list according to requests serviced + * the pending list according to last request service */ static inline void -cfq_add_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq) +cfq_add_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq, int requeue) { - /* - * it's currently on the empty list - */ - cfqq->on_rr = 1; + BUG_ON(cfq_cfqq_on_rr(cfqq)); + cfq_mark_cfqq_on_rr(cfqq); cfqd->busy_queues++; - if (time_after(jiffies, cfqq->service_start + cfq_service)) - cfqq->service_used >>= 3; - - cfq_sort_rr_list(cfqq, 1); + cfq_resort_rr_list(cfqq, requeue); } static inline void cfq_del_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq) { + BUG_ON(!cfq_cfqq_on_rr(cfqq)); + cfq_clear_cfqq_on_rr(cfqq); list_move(&cfqq->cfq_list, &cfqd->empty_list); - cfqq->on_rr = 0; BUG_ON(!cfqd->busy_queues); cfqd->busy_queues--; @@ -505,16 +547,17 @@ static inline void cfq_del_crq_rb(struct cfq_rq *crq) if (ON_RB(&crq->rb_node)) { struct cfq_data *cfqd = cfqq->cfqd; + const int sync = cfq_crq_is_sync(crq); - BUG_ON(!cfqq->queued[crq->is_sync]); + BUG_ON(!cfqq->queued[sync]); + cfqq->queued[sync]--; cfq_update_next_crq(crq); - cfqq->queued[crq->is_sync]--; rb_erase(&crq->rb_node, &cfqq->sort_list); RB_CLEAR_COLOR(&crq->rb_node); - if (RB_EMPTY(&cfqq->sort_list) && cfqq->on_rr) + if (cfq_cfqq_on_rr(cfqq) && RB_EMPTY(&cfqq->sort_list)) cfq_del_cfqq_rr(cfqd, cfqq); } } @@ -550,7 +593,7 @@ static void cfq_add_crq_rb(struct cfq_rq *crq) struct cfq_rq *__alias; crq->rb_key = rq_rb_key(rq); - cfqq->queued[crq->is_sync]++; + cfqq->queued[cfq_crq_is_sync(crq)]++; /* * looks a little odd, but the first insert might return an alias. @@ -561,8 +604,8 @@ static void cfq_add_crq_rb(struct cfq_rq *crq) rb_insert_color(&crq->rb_node, &cfqq->sort_list); - if (!cfqq->on_rr) - cfq_add_cfqq_rr(cfqd, cfqq); + if (!cfq_cfqq_on_rr(cfqq)) + cfq_add_cfqq_rr(cfqd, cfqq, cfq_crq_requeued(crq)); /* * check if this request is a better next-serve candidate @@ -575,17 +618,16 @@ cfq_reposition_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq) { if (ON_RB(&crq->rb_node)) { rb_erase(&crq->rb_node, &cfqq->sort_list); - cfqq->queued[crq->is_sync]--; + cfqq->queued[cfq_crq_is_sync(crq)]--; } cfq_add_crq_rb(crq); } -static struct request * -cfq_find_rq_rb(struct cfq_data *cfqd, sector_t sector) +static struct request *cfq_find_rq_rb(struct cfq_data *cfqd, sector_t sector) + { - const unsigned long key = cfq_hash_key(cfqd, current); - struct cfq_queue *cfqq = cfq_find_cfq_hash(cfqd, key); + struct cfq_queue *cfqq = cfq_find_cfq_hash(cfqd, current->pid, CFQ_KEY_ANY); struct rb_node *n; if (!cfqq) @@ -609,20 +651,25 @@ out: static void cfq_deactivate_request(request_queue_t *q, struct request *rq) { + struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_rq *crq = RQ_DATA(rq); if (crq) { struct cfq_queue *cfqq = crq->cfq_queue; - if (cfqq->cfqd->cfq_tagged) { - cfqq->service_used--; - cfq_sort_rr_list(cfqq, 0); + if (cfq_crq_in_driver(crq)) { + cfq_clear_crq_in_driver(crq); + WARN_ON(!cfqd->rq_in_driver); + cfqd->rq_in_driver--; } + if (cfq_crq_in_flight(crq)) { + const int sync = cfq_crq_is_sync(crq); - if (crq->accounted) { - crq->accounted = 0; - cfqq->cfqd->rq_in_driver--; + cfq_clear_crq_in_flight(crq); + WARN_ON(!cfqq->on_dispatch[sync]); + cfqq->on_dispatch[sync]--; } + cfq_mark_crq_requeued(crq); } } @@ -640,11 +687,10 @@ static void cfq_remove_request(request_queue_t *q, struct request *rq) struct cfq_rq *crq = RQ_DATA(rq); if (crq) { - cfq_remove_merge_hints(q, crq); list_del_init(&rq->queuelist); + cfq_del_crq_rb(crq); + cfq_remove_merge_hints(q, crq); - if (crq->cfq_queue) - cfq_del_crq_rb(crq); } } @@ -662,21 +708,15 @@ cfq_merge(request_queue_t *q, struct request **req, struct bio *bio) } __rq = cfq_find_rq_hash(cfqd, bio->bi_sector); - if (__rq) { - BUG_ON(__rq->sector + __rq->nr_sectors != bio->bi_sector); - - if (elv_rq_merge_ok(__rq, bio)) { - ret = ELEVATOR_BACK_MERGE; - goto out; - } + if (__rq && elv_rq_merge_ok(__rq, bio)) { + ret = ELEVATOR_BACK_MERGE; + goto out; } __rq = cfq_find_rq_rb(cfqd, bio->bi_sector + bio_sectors(bio)); - if (__rq) { - if (elv_rq_merge_ok(__rq, bio)) { - ret = ELEVATOR_FRONT_MERGE; - goto out; - } + if (__rq && elv_rq_merge_ok(__rq, bio)) { + ret = ELEVATOR_FRONT_MERGE; + goto out; } return ELEVATOR_NO_MERGE; @@ -709,20 +749,220 @@ static void cfq_merged_requests(request_queue_t *q, struct request *rq, struct request *next) { - struct cfq_rq *crq = RQ_DATA(rq); - struct cfq_rq *cnext = RQ_DATA(next); - cfq_merged_request(q, rq); - if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist)) { - if (time_before(cnext->queue_start, crq->queue_start)) { - list_move(&rq->queuelist, &next->queuelist); - crq->queue_start = cnext->queue_start; + /* + * reposition in fifo if next is older than rq + */ + if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist) && + time_before(next->start_time, rq->start_time)) + list_move(&rq->queuelist, &next->queuelist); + + cfq_remove_request(q, next); +} + +static inline void +__cfq_set_active_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + if (cfqq) { + /* + * stop potential idle class queues waiting service + */ + del_timer(&cfqd->idle_class_timer); + + cfqq->slice_start = jiffies; + cfqq->slice_end = 0; + cfqq->slice_left = 0; + cfq_clear_cfqq_must_alloc_slice(cfqq); + cfq_clear_cfqq_fifo_expire(cfqq); + cfq_clear_cfqq_expired(cfqq); + } + + cfqd->active_queue = cfqq; +} + +/* + * 0 + * 0,1 + * 0,1,2 + * 0,1,2,3 + * 0,1,2,3,4 + * 0,1,2,3,4,5 + * 0,1,2,3,4,5,6 + * 0,1,2,3,4,5,6,7 + */ +static int cfq_get_next_prio_level(struct cfq_data *cfqd) +{ + int prio, wrap; + + prio = -1; + wrap = 0; + do { + int p; + + for (p = cfqd->cur_prio; p <= cfqd->cur_end_prio; p++) { + if (!list_empty(&cfqd->rr_list[p])) { + prio = p; + break; + } + } + + if (prio != -1) + break; + cfqd->cur_prio = 0; + if (++cfqd->cur_end_prio == CFQ_PRIO_LISTS) { + cfqd->cur_end_prio = 0; + if (wrap) + break; + wrap = 1; } + } while (1); + + if (unlikely(prio == -1)) + return -1; + + BUG_ON(prio >= CFQ_PRIO_LISTS); + + list_splice_init(&cfqd->rr_list[prio], &cfqd->cur_rr); + + cfqd->cur_prio = prio + 1; + if (cfqd->cur_prio > cfqd->cur_end_prio) { + cfqd->cur_end_prio = cfqd->cur_prio; + cfqd->cur_prio = 0; + } + if (cfqd->cur_end_prio == CFQ_PRIO_LISTS) { + cfqd->cur_prio = 0; + cfqd->cur_end_prio = 0; } - cfq_update_next_crq(cnext); - cfq_remove_request(q, next); + return prio; +} + +static struct cfq_queue *cfq_set_active_queue(struct cfq_data *cfqd) +{ + struct cfq_queue *cfqq; + + /* + * if current queue is expired but not done with its requests yet, + * wait for that to happen + */ + if ((cfqq = cfqd->active_queue) != NULL) { + if (cfq_cfqq_expired(cfqq) && cfq_cfqq_dispatched(cfqq)) + return NULL; + } + + /* + * if current list is non-empty, grab first entry. if it is empty, + * get next prio level and grab first entry then if any are spliced + */ + if (!list_empty(&cfqd->cur_rr) || cfq_get_next_prio_level(cfqd) != -1) + cfqq = list_entry_cfqq(cfqd->cur_rr.next); + + /* + * if we have idle queues and no rt or be queues had pending + * requests, either allow immediate service if the grace period + * has passed or arm the idle grace timer + */ + if (!cfqq && !list_empty(&cfqd->idle_rr)) { + unsigned long end = cfqd->last_end_request + CFQ_IDLE_GRACE; + + if (time_after_eq(jiffies, end)) + cfqq = list_entry_cfqq(cfqd->idle_rr.next); + else + mod_timer(&cfqd->idle_class_timer, end); + } + + __cfq_set_active_queue(cfqd, cfqq); + return cfqq; +} + +/* + * current cfqq expired its slice (or was too idle), select new one + */ +static void +__cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq, + int preempted) +{ + unsigned long now = jiffies; + + if (cfq_cfqq_wait_request(cfqq)) + del_timer(&cfqd->idle_slice_timer); + + if (!preempted && !cfq_cfqq_dispatched(cfqq)) + cfqq->service_last = now; + + cfq_clear_cfqq_must_dispatch(cfqq); + cfq_clear_cfqq_wait_request(cfqq); + + /* + * store what was left of this slice, if the queue idled out + * or was preempted + */ + if (time_after(now, cfqq->slice_end)) + cfqq->slice_left = now - cfqq->slice_end; + else + cfqq->slice_left = 0; + + if (cfq_cfqq_on_rr(cfqq)) + cfq_resort_rr_list(cfqq, preempted); + + if (cfqq == cfqd->active_queue) + cfqd->active_queue = NULL; + + if (cfqd->active_cic) { + put_io_context(cfqd->active_cic->ioc); + cfqd->active_cic = NULL; + } + + cfqd->dispatch_slice = 0; +} + +static inline void cfq_slice_expired(struct cfq_data *cfqd, int preempted) +{ + struct cfq_queue *cfqq = cfqd->active_queue; + + if (cfqq) { + /* + * use deferred expiry, if there are requests in progress as + * not to disturb the slice of the next queue + */ + if (cfq_cfqq_dispatched(cfqq)) + cfq_mark_cfqq_expired(cfqq); + else + __cfq_slice_expired(cfqd, cfqq, preempted); + } +} + +static int cfq_arm_slice_timer(struct cfq_data *cfqd, struct cfq_queue *cfqq) + +{ + WARN_ON(!RB_EMPTY(&cfqq->sort_list)); + WARN_ON(cfqq != cfqd->active_queue); + + /* + * idle is disabled, either manually or by past process history + */ + if (!cfqd->cfq_slice_idle) + return 0; + if (!cfq_cfqq_idle_window(cfqq)) + return 0; + /* + * task has exited, don't wait + */ + if (cfqd->active_cic && !cfqd->active_cic->ioc->task) + return 0; + + cfq_mark_cfqq_must_dispatch(cfqq); + cfq_mark_cfqq_wait_request(cfqq); + + if (!timer_pending(&cfqd->idle_slice_timer)) { + unsigned long slice_left = min(cfqq->slice_end - 1, (unsigned long) cfqd->cfq_slice_idle); + + cfqd->idle_slice_timer.expires = jiffies + slice_left; + add_timer(&cfqd->idle_slice_timer); + } + + return 1; } /* @@ -738,31 +978,40 @@ static void cfq_dispatch_sort(request_queue_t *q, struct cfq_rq *crq) struct request *__rq; sector_t last; - cfq_del_crq_rb(crq); - cfq_remove_merge_hints(q, crq); list_del(&crq->request->queuelist); last = cfqd->last_sector; - while ((entry = entry->prev) != head) { - __rq = list_entry_rq(entry); + list_for_each_entry_reverse(__rq, head, queuelist) { + struct cfq_rq *__crq = RQ_DATA(__rq); - if (blk_barrier_rq(crq->request)) + if (blk_barrier_rq(__rq)) + break; + if (!blk_fs_request(__rq)) break; - if (!blk_fs_request(crq->request)) + if (cfq_crq_requeued(__crq)) break; - if (crq->request->sector > __rq->sector) + if (__rq->sector <= crq->request->sector) break; if (__rq->sector > last && crq->request->sector < last) { - last = crq->request->sector; + last = crq->request->sector + crq->request->nr_sectors; break; } + entry = &__rq->queuelist; } cfqd->last_sector = last; - crq->in_flight = 1; - cfqq->in_flight++; - list_add(&crq->request->queuelist, entry); + + cfqq->next_crq = cfq_find_next_crq(cfqd, cfqq, crq); + + cfq_del_crq_rb(crq); + cfq_remove_merge_hints(q, crq); + + cfq_mark_crq_in_flight(crq); + cfq_clear_crq_requeued(crq); + + cfqq->on_dispatch[cfq_crq_is_sync(crq)]++; + list_add_tail(&crq->request->queuelist, entry); } /* @@ -771,173 +1020,235 @@ static void cfq_dispatch_sort(request_queue_t *q, struct cfq_rq *crq) static inline struct cfq_rq *cfq_check_fifo(struct cfq_queue *cfqq) { struct cfq_data *cfqd = cfqq->cfqd; - const int reads = !list_empty(&cfqq->fifo[0]); - const int writes = !list_empty(&cfqq->fifo[1]); - unsigned long now = jiffies; + struct request *rq; struct cfq_rq *crq; - if (time_before(now, cfqq->last_fifo_expire + cfqd->cfq_fifo_batch_expire)) + if (cfq_cfqq_fifo_expire(cfqq)) return NULL; - crq = RQ_DATA(list_entry(cfqq->fifo[0].next, struct request, queuelist)); - if (reads && time_after(now, crq->queue_start + cfqd->cfq_fifo_expire_r)) { - cfqq->last_fifo_expire = now; - return crq; - } + if (!list_empty(&cfqq->fifo)) { + int fifo = cfq_cfqq_class_sync(cfqq); - crq = RQ_DATA(list_entry(cfqq->fifo[1].next, struct request, queuelist)); - if (writes && time_after(now, crq->queue_start + cfqd->cfq_fifo_expire_w)) { - cfqq->last_fifo_expire = now; - return crq; + crq = RQ_DATA(list_entry_fifo(cfqq->fifo.next)); + rq = crq->request; + if (time_after(jiffies, rq->start_time + cfqd->cfq_fifo_expire[fifo])) { + cfq_mark_cfqq_fifo_expire(cfqq); + return crq; + } } return NULL; } /* - * dispatch a single request from given queue + * Scale schedule slice based on io priority. Use the sync time slice only + * if a queue is marked sync and has sync io queued. A sync queue with async + * io only, should not get full sync slice length. */ +static inline int +cfq_prio_to_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + const int base_slice = cfqd->cfq_slice[cfq_cfqq_sync(cfqq)]; + + WARN_ON(cfqq->ioprio >= IOPRIO_BE_NR); + + return base_slice + (base_slice/CFQ_SLICE_SCALE * (4 - cfqq->ioprio)); +} + static inline void -cfq_dispatch_request(request_queue_t *q, struct cfq_data *cfqd, - struct cfq_queue *cfqq) +cfq_set_prio_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq) { - struct cfq_rq *crq; + cfqq->slice_end = cfq_prio_to_slice(cfqd, cfqq) + jiffies; +} + +static inline int +cfq_prio_to_maxrq(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + const int base_rq = cfqd->cfq_slice_async_rq; + + WARN_ON(cfqq->ioprio >= IOPRIO_BE_NR); + + return 2 * (base_rq + base_rq * (CFQ_PRIO_LISTS - 1 - cfqq->ioprio)); +} + +/* + * scheduler run of queue, if there are requests pending and no one in the + * driver that will restart queueing + */ +static inline void cfq_schedule_dispatch(struct cfq_data *cfqd) +{ + if (!cfqd->rq_in_driver && cfq_pending_requests(cfqd)) + kblockd_schedule_work(&cfqd->unplug_work); +} + +/* + * get next queue for service + */ +static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd, int force) +{ + unsigned long now = jiffies; + struct cfq_queue *cfqq; + + cfqq = cfqd->active_queue; + if (!cfqq) + goto new_queue; + + if (cfq_cfqq_expired(cfqq)) + goto new_queue; /* - * follow expired path, else get first next available + * slice has expired */ - if ((crq = cfq_check_fifo(cfqq)) == NULL) { - if (cfqd->find_best_crq) - crq = cfqq->next_crq; - else - crq = rb_entry_crq(rb_first(&cfqq->sort_list)); - } - - cfqd->last_sector = crq->request->sector + crq->request->nr_sectors; + if (!cfq_cfqq_must_dispatch(cfqq) && time_after(now, cfqq->slice_end)) + goto expire; /* - * finally, insert request into driver list + * if queue has requests, dispatch one. if not, check if + * enough slice is left to wait for one */ - cfq_dispatch_sort(q, crq); + if (!RB_EMPTY(&cfqq->sort_list)) + goto keep_queue; + else if (!force && cfq_cfqq_class_sync(cfqq) && + time_before(now, cfqq->slice_end)) { + if (cfq_arm_slice_timer(cfqd, cfqq)) + return NULL; + } + +expire: + cfq_slice_expired(cfqd, 0); +new_queue: + cfqq = cfq_set_active_queue(cfqd); +keep_queue: + return cfqq; } -static int cfq_dispatch_requests(request_queue_t *q, int max_dispatch) +static int +__cfq_dispatch_requests(struct cfq_data *cfqd, struct cfq_queue *cfqq, + int max_dispatch) { - struct cfq_data *cfqd = q->elevator->elevator_data; - struct cfq_queue *cfqq; - struct list_head *entry, *tmp; - int queued, busy_queues, first_round; + int dispatched = 0; - if (list_empty(&cfqd->rr_list)) - return 0; + BUG_ON(RB_EMPTY(&cfqq->sort_list)); - queued = 0; - first_round = 1; -restart: - busy_queues = 0; - list_for_each_safe(entry, tmp, &cfqd->rr_list) { - cfqq = list_entry_cfqq(entry); + do { + struct cfq_rq *crq; - BUG_ON(RB_EMPTY(&cfqq->sort_list)); + /* + * follow expired path, else get first next available + */ + if ((crq = cfq_check_fifo(cfqq)) == NULL) + crq = cfqq->next_crq; /* - * first round of queueing, only select from queues that - * don't already have io in-flight + * finally, insert request into driver dispatch list */ - if (first_round && cfqq->in_flight) - continue; + cfq_dispatch_sort(cfqd->queue, crq); + + cfqd->dispatch_slice++; + dispatched++; - cfq_dispatch_request(q, cfqd, cfqq); + if (!cfqd->active_cic) { + atomic_inc(&crq->io_context->ioc->refcount); + cfqd->active_cic = crq->io_context; + } - if (!RB_EMPTY(&cfqq->sort_list)) - busy_queues++; + if (RB_EMPTY(&cfqq->sort_list)) + break; - queued++; - } + } while (dispatched < max_dispatch); + + /* + * if slice end isn't set yet, set it. if at least one request was + * sync, use the sync time slice value + */ + if (!cfqq->slice_end) + cfq_set_prio_slice(cfqd, cfqq); + + /* + * expire an async queue immediately if it has used up its slice. idle + * queue always expire after 1 dispatch round. + */ + if ((!cfq_cfqq_sync(cfqq) && + cfqd->dispatch_slice >= cfq_prio_to_maxrq(cfqd, cfqq)) || + cfq_class_idle(cfqq)) + cfq_slice_expired(cfqd, 0); + + return dispatched; +} + +static int +cfq_dispatch_requests(request_queue_t *q, int max_dispatch, int force) +{ + struct cfq_data *cfqd = q->elevator->elevator_data; + struct cfq_queue *cfqq; + + if (!cfqd->busy_queues) + return 0; - if ((queued < max_dispatch) && (busy_queues || first_round)) { - first_round = 0; - goto restart; + cfqq = cfq_select_queue(cfqd, force); + if (cfqq) { + cfq_clear_cfqq_must_dispatch(cfqq); + cfq_clear_cfqq_wait_request(cfqq); + del_timer(&cfqd->idle_slice_timer); + + if (cfq_class_idle(cfqq)) + max_dispatch = 1; + + return __cfq_dispatch_requests(cfqd, cfqq, max_dispatch); } - return queued; + return 0; } static inline void cfq_account_dispatch(struct cfq_rq *crq) { struct cfq_queue *cfqq = crq->cfq_queue; struct cfq_data *cfqd = cfqq->cfqd; - unsigned long now, elapsed; - if (!blk_fs_request(crq->request)) + if (unlikely(!blk_fs_request(crq->request))) return; /* * accounted bit is necessary since some drivers will call * elv_next_request() many times for the same request (eg ide) */ - if (crq->accounted) + if (cfq_crq_in_driver(crq)) return; - now = jiffies; - if (cfqq->service_start == ~0UL) - cfqq->service_start = now; - - /* - * on drives with tagged command queueing, command turn-around time - * doesn't necessarily reflect the time spent processing this very - * command inside the drive. so do the accounting differently there, - * by just sorting on the number of requests - */ - if (cfqd->cfq_tagged) { - if (time_after(now, cfqq->service_start + cfq_service)) { - cfqq->service_start = now; - cfqq->service_used /= 10; - } - - cfqq->service_used++; - cfq_sort_rr_list(cfqq, 0); - } - - elapsed = now - crq->queue_start; - if (elapsed > max_elapsed_dispatch) - max_elapsed_dispatch = elapsed; - - crq->accounted = 1; - crq->service_start = now; - - if (++cfqd->rq_in_driver >= CFQ_MAX_TAG && !cfqd->cfq_tagged) { - cfqq->cfqd->cfq_tagged = 1; - printk("cfq: depth %d reached, tagging now on\n", CFQ_MAX_TAG); - } + cfq_mark_crq_in_driver(crq); + cfqd->rq_in_driver++; } static inline void cfq_account_completion(struct cfq_queue *cfqq, struct cfq_rq *crq) { struct cfq_data *cfqd = cfqq->cfqd; + unsigned long now; - if (!crq->accounted) + if (!cfq_crq_in_driver(crq)) return; + now = jiffies; + WARN_ON(!cfqd->rq_in_driver); cfqd->rq_in_driver--; - if (!cfqd->cfq_tagged) { - unsigned long now = jiffies; - unsigned long duration = now - crq->service_start; + if (!cfq_class_idle(cfqq)) + cfqd->last_end_request = now; - if (time_after(now, cfqq->service_start + cfq_service)) { - cfqq->service_start = now; - cfqq->service_used >>= 3; + if (!cfq_cfqq_dispatched(cfqq)) { + if (cfq_cfqq_on_rr(cfqq)) { + cfqq->service_last = now; + cfq_resort_rr_list(cfqq, 0); + } + if (cfq_cfqq_expired(cfqq)) { + __cfq_slice_expired(cfqd, cfqq, 0); + cfq_schedule_dispatch(cfqd); } - - cfqq->service_used += duration; - cfq_sort_rr_list(cfqq, 0); - - if (duration > max_elapsed_crq) - max_elapsed_crq = duration; } + + if (cfq_crq_is_sync(crq)) + crq->io_context->last_end_request = now; } static struct request *cfq_next_request(request_queue_t *q) @@ -950,7 +1261,18 @@ static struct request *cfq_next_request(request_queue_t *q) dispatch: rq = list_entry_rq(q->queue_head.next); - if ((crq = RQ_DATA(rq)) != NULL) { + crq = RQ_DATA(rq); + if (crq) { + struct cfq_queue *cfqq = crq->cfq_queue; + + /* + * if idle window is disabled, allow queue buildup + */ + if (!cfq_crq_in_driver(crq) && + !cfq_cfqq_idle_window(cfqq) && + cfqd->rq_in_driver >= cfqd->cfq_max_depth) + return NULL; + cfq_remove_merge_hints(q, crq); cfq_account_dispatch(crq); } @@ -958,7 +1280,7 @@ dispatch: return rq; } - if (cfq_dispatch_requests(q, cfqd->cfq_quantum)) + if (cfq_dispatch_requests(q, cfqd->cfq_quantum, 0)) goto dispatch; return NULL; @@ -972,13 +1294,21 @@ dispatch: */ static void cfq_put_queue(struct cfq_queue *cfqq) { - BUG_ON(!atomic_read(&cfqq->ref)); + struct cfq_data *cfqd = cfqq->cfqd; + + BUG_ON(atomic_read(&cfqq->ref) <= 0); if (!atomic_dec_and_test(&cfqq->ref)) return; BUG_ON(rb_first(&cfqq->sort_list)); - BUG_ON(cfqq->on_rr); + BUG_ON(cfqq->allocated[READ] + cfqq->allocated[WRITE]); + BUG_ON(cfq_cfqq_on_rr(cfqq)); + + if (unlikely(cfqd->active_queue == cfqq)) { + __cfq_slice_expired(cfqd, cfqq, 0); + cfq_schedule_dispatch(cfqd); + } cfq_put_cfqd(cfqq->cfqd); @@ -991,15 +1321,17 @@ static void cfq_put_queue(struct cfq_queue *cfqq) } static inline struct cfq_queue * -__cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned long key, const int hashval) +__cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned int key, unsigned int prio, + const int hashval) { struct hlist_head *hash_list = &cfqd->cfq_hash[hashval]; struct hlist_node *entry, *next; hlist_for_each_safe(entry, next, hash_list) { struct cfq_queue *__cfqq = list_entry_qhash(entry); + const unsigned short __p = IOPRIO_PRIO_VALUE(__cfqq->ioprio_class, __cfqq->ioprio); - if (__cfqq->key == key) + if (__cfqq->key == key && (__p == prio || prio == CFQ_KEY_ANY)) return __cfqq; } @@ -1007,94 +1339,220 @@ __cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned long key, const int hashval) } static struct cfq_queue * -cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned long key) +cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned int key, unsigned short prio) { - return __cfq_find_cfq_hash(cfqd, key, hash_long(key, CFQ_QHASH_SHIFT)); + return __cfq_find_cfq_hash(cfqd, key, prio, hash_long(key, CFQ_QHASH_SHIFT)); } -static inline void -cfq_rehash_cfqq(struct cfq_data *cfqd, struct cfq_queue **cfqq, - struct cfq_io_context *cic) +static void cfq_free_io_context(struct cfq_io_context *cic) { - unsigned long hashkey = cfq_hash_key(cfqd, current); - unsigned long hashval = hash_long(hashkey, CFQ_QHASH_SHIFT); - struct cfq_queue *__cfqq; - unsigned long flags; - - spin_lock_irqsave(cfqd->queue->queue_lock, flags); + struct cfq_io_context *__cic; + struct list_head *entry, *next; - hlist_del(&(*cfqq)->cfq_hash); - - __cfqq = __cfq_find_cfq_hash(cfqd, hashkey, hashval); - if (!__cfqq || __cfqq == *cfqq) { - __cfqq = *cfqq; - hlist_add_head(&__cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); - __cfqq->key_type = cfqd->key_type; - } else { - atomic_inc(&__cfqq->ref); - cic->cfqq = __cfqq; - cfq_put_queue(*cfqq); - *cfqq = __cfqq; + list_for_each_safe(entry, next, &cic->list) { + __cic = list_entry(entry, struct cfq_io_context, list); + kmem_cache_free(cfq_ioc_pool, __cic); } - cic->cfqq = __cfqq; - spin_unlock_irqrestore(cfqd->queue->queue_lock, flags); + kmem_cache_free(cfq_ioc_pool, cic); } -static void cfq_free_io_context(struct cfq_io_context *cic) +/* + * Called with interrupts disabled + */ +static void cfq_exit_single_io_context(struct cfq_io_context *cic) { - kmem_cache_free(cfq_ioc_pool, cic); + struct cfq_data *cfqd = cic->cfqq->cfqd; + request_queue_t *q = cfqd->queue; + + WARN_ON(!irqs_disabled()); + + spin_lock(q->queue_lock); + + if (unlikely(cic->cfqq == cfqd->active_queue)) { + __cfq_slice_expired(cfqd, cic->cfqq, 0); + cfq_schedule_dispatch(cfqd); + } + + cfq_put_queue(cic->cfqq); + cic->cfqq = NULL; + spin_unlock(q->queue_lock); } /* - * locking hierarchy is: io_context lock -> queue locks + * Another task may update the task cic list, if it is doing a queue lookup + * on its behalf. cfq_cic_lock excludes such concurrent updates */ static void cfq_exit_io_context(struct cfq_io_context *cic) { - struct cfq_queue *cfqq = cic->cfqq; - struct list_head *entry = &cic->list; - request_queue_t *q; + struct cfq_io_context *__cic; + struct list_head *entry; unsigned long flags; + local_irq_save(flags); + /* * put the reference this task is holding to the various queues */ - spin_lock_irqsave(&cic->ioc->lock, flags); - while ((entry = cic->list.next) != &cic->list) { - struct cfq_io_context *__cic; - + list_for_each(entry, &cic->list) { __cic = list_entry(entry, struct cfq_io_context, list); - list_del(entry); - - q = __cic->cfqq->cfqd->queue; - spin_lock(q->queue_lock); - cfq_put_queue(__cic->cfqq); - spin_unlock(q->queue_lock); + cfq_exit_single_io_context(__cic); } - q = cfqq->cfqd->queue; - spin_lock(q->queue_lock); - cfq_put_queue(cfqq); - spin_unlock(q->queue_lock); - - cic->cfqq = NULL; - spin_unlock_irqrestore(&cic->ioc->lock, flags); + cfq_exit_single_io_context(cic); + local_irq_restore(flags); } -static struct cfq_io_context *cfq_alloc_io_context(int gfp_flags) +static struct cfq_io_context * +cfq_alloc_io_context(struct cfq_data *cfqd, int gfp_mask) { - struct cfq_io_context *cic = kmem_cache_alloc(cfq_ioc_pool, gfp_flags); + struct cfq_io_context *cic = kmem_cache_alloc(cfq_ioc_pool, gfp_mask); if (cic) { - cic->dtor = cfq_free_io_context; - cic->exit = cfq_exit_io_context; INIT_LIST_HEAD(&cic->list); cic->cfqq = NULL; + cic->key = NULL; + cic->last_end_request = jiffies; + cic->ttime_total = 0; + cic->ttime_samples = 0; + cic->ttime_mean = 0; + cic->dtor = cfq_free_io_context; + cic->exit = cfq_exit_io_context; } return cic; } +static void cfq_init_prio_data(struct cfq_queue *cfqq) +{ + struct task_struct *tsk = current; + int ioprio_class; + + if (!cfq_cfqq_prio_changed(cfqq)) + return; + + ioprio_class = IOPRIO_PRIO_CLASS(tsk->ioprio); + switch (ioprio_class) { + default: + printk(KERN_ERR "cfq: bad prio %x\n", ioprio_class); + case IOPRIO_CLASS_NONE: + /* + * no prio set, place us in the middle of the BE classes + */ + cfqq->ioprio = task_nice_ioprio(tsk); + cfqq->ioprio_class = IOPRIO_CLASS_BE; + break; + case IOPRIO_CLASS_RT: + cfqq->ioprio = task_ioprio(tsk); + cfqq->ioprio_class = IOPRIO_CLASS_RT; + break; + case IOPRIO_CLASS_BE: + cfqq->ioprio = task_ioprio(tsk); + cfqq->ioprio_class = IOPRIO_CLASS_BE; + break; + case IOPRIO_CLASS_IDLE: + cfqq->ioprio_class = IOPRIO_CLASS_IDLE; + cfqq->ioprio = 7; + cfq_clear_cfqq_idle_window(cfqq); + break; + } + + /* + * keep track of original prio settings in case we have to temporarily + * elevate the priority of this queue + */ + cfqq->org_ioprio = cfqq->ioprio; + cfqq->org_ioprio_class = cfqq->ioprio_class; + + if (cfq_cfqq_on_rr(cfqq)) + cfq_resort_rr_list(cfqq, 0); + + cfq_clear_cfqq_prio_changed(cfqq); +} + +static inline void changed_ioprio(struct cfq_queue *cfqq) +{ + if (cfqq) { + struct cfq_data *cfqd = cfqq->cfqd; + + spin_lock(cfqd->queue->queue_lock); + cfq_mark_cfqq_prio_changed(cfqq); + cfq_init_prio_data(cfqq); + spin_unlock(cfqd->queue->queue_lock); + } +} + +/* + * callback from sys_ioprio_set, irqs are disabled + */ +static int cfq_ioc_set_ioprio(struct io_context *ioc, unsigned int ioprio) +{ + struct cfq_io_context *cic = ioc->cic; + + changed_ioprio(cic->cfqq); + + list_for_each_entry(cic, &cic->list, list) + changed_ioprio(cic->cfqq); + + return 0; +} + +static struct cfq_queue * +cfq_get_queue(struct cfq_data *cfqd, unsigned int key, unsigned short ioprio, + int gfp_mask) +{ + const int hashval = hash_long(key, CFQ_QHASH_SHIFT); + struct cfq_queue *cfqq, *new_cfqq = NULL; + +retry: + cfqq = __cfq_find_cfq_hash(cfqd, key, ioprio, hashval); + + if (!cfqq) { + if (new_cfqq) { + cfqq = new_cfqq; + new_cfqq = NULL; + } else if (gfp_mask & __GFP_WAIT) { + spin_unlock_irq(cfqd->queue->queue_lock); + new_cfqq = kmem_cache_alloc(cfq_pool, gfp_mask); + spin_lock_irq(cfqd->queue->queue_lock); + goto retry; + } else { + cfqq = kmem_cache_alloc(cfq_pool, gfp_mask); + if (!cfqq) + goto out; + } + + memset(cfqq, 0, sizeof(*cfqq)); + + INIT_HLIST_NODE(&cfqq->cfq_hash); + INIT_LIST_HEAD(&cfqq->cfq_list); + RB_CLEAR_ROOT(&cfqq->sort_list); + INIT_LIST_HEAD(&cfqq->fifo); + + cfqq->key = key; + hlist_add_head(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); + atomic_set(&cfqq->ref, 0); + cfqq->cfqd = cfqd; + atomic_inc(&cfqd->ref); + cfqq->service_last = 0; + /* + * set ->slice_left to allow preemption for a new process + */ + cfqq->slice_left = 2 * cfqd->cfq_slice_idle; + cfq_mark_cfqq_idle_window(cfqq); + cfq_mark_cfqq_prio_changed(cfqq); + cfq_init_prio_data(cfqq); + } + + if (new_cfqq) + kmem_cache_free(cfq_pool, new_cfqq); + + atomic_inc(&cfqq->ref); +out: + WARN_ON((gfp_mask & __GFP_WAIT) && !cfqq); + return cfqq; +} + /* * Setup general io context and cfq io context. There can be several cfq * io contexts per general io context, if this process is doing io to more @@ -1102,39 +1560,39 @@ static struct cfq_io_context *cfq_alloc_io_context(int gfp_flags) * cfqq, so we don't need to worry about it disappearing */ static struct cfq_io_context * -cfq_get_io_context(struct cfq_queue **cfqq, int gfp_flags) +cfq_get_io_context(struct cfq_data *cfqd, pid_t pid, int gfp_mask) { - struct cfq_data *cfqd = (*cfqq)->cfqd; - struct cfq_queue *__cfqq = *cfqq; + struct io_context *ioc = NULL; struct cfq_io_context *cic; - struct io_context *ioc; - might_sleep_if(gfp_flags & __GFP_WAIT); + might_sleep_if(gfp_mask & __GFP_WAIT); - ioc = get_io_context(gfp_flags); + ioc = get_io_context(gfp_mask); if (!ioc) return NULL; if ((cic = ioc->cic) == NULL) { - cic = cfq_alloc_io_context(gfp_flags); + cic = cfq_alloc_io_context(cfqd, gfp_mask); if (cic == NULL) goto err; + /* + * manually increment generic io_context usage count, it + * cannot go away since we are already holding one ref to it + */ ioc->cic = cic; + ioc->set_ioprio = cfq_ioc_set_ioprio; cic->ioc = ioc; - cic->cfqq = __cfqq; - atomic_inc(&__cfqq->ref); + cic->key = cfqd; + atomic_inc(&cfqd->ref); } else { struct cfq_io_context *__cic; - unsigned long flags; /* - * since the first cic on the list is actually the head - * itself, need to check this here or we'll duplicate an - * cic per ioc for no reason + * the first cic on the list is actually the head itself */ - if (cic->cfqq == __cfqq) + if (cic->key == cfqd) goto out; /* @@ -1142,152 +1600,262 @@ cfq_get_io_context(struct cfq_queue **cfqq, int gfp_flags) * should be ok here, the list will usually not be more than * 1 or a few entries long */ - spin_lock_irqsave(&ioc->lock, flags); list_for_each_entry(__cic, &cic->list, list) { /* * this process is already holding a reference to * this queue, so no need to get one more */ - if (__cic->cfqq == __cfqq) { + if (__cic->key == cfqd) { cic = __cic; - spin_unlock_irqrestore(&ioc->lock, flags); goto out; } } - spin_unlock_irqrestore(&ioc->lock, flags); /* * nope, process doesn't have a cic assoicated with this * cfqq yet. get a new one and add to list */ - __cic = cfq_alloc_io_context(gfp_flags); + __cic = cfq_alloc_io_context(cfqd, gfp_mask); if (__cic == NULL) goto err; __cic->ioc = ioc; - __cic->cfqq = __cfqq; - atomic_inc(&__cfqq->ref); - spin_lock_irqsave(&ioc->lock, flags); + __cic->key = cfqd; + atomic_inc(&cfqd->ref); list_add(&__cic->list, &cic->list); - spin_unlock_irqrestore(&ioc->lock, flags); - cic = __cic; - *cfqq = __cfqq; } out: - /* - * if key_type has been changed on the fly, we lazily rehash - * each queue at lookup time - */ - if ((*cfqq)->key_type != cfqd->key_type) - cfq_rehash_cfqq(cfqd, cfqq, cic); - return cic; err: put_io_context(ioc); return NULL; } -static struct cfq_queue * -__cfq_get_queue(struct cfq_data *cfqd, unsigned long key, int gfp_mask) +static void +cfq_update_io_thinktime(struct cfq_data *cfqd, struct cfq_io_context *cic) { - const int hashval = hash_long(key, CFQ_QHASH_SHIFT); - struct cfq_queue *cfqq, *new_cfqq = NULL; + unsigned long elapsed, ttime; -retry: - cfqq = __cfq_find_cfq_hash(cfqd, key, hashval); - - if (!cfqq) { - if (new_cfqq) { - cfqq = new_cfqq; - new_cfqq = NULL; - } else { - spin_unlock_irq(cfqd->queue->queue_lock); - new_cfqq = kmem_cache_alloc(cfq_pool, gfp_mask); - spin_lock_irq(cfqd->queue->queue_lock); + /* + * if this context already has stuff queued, thinktime is from + * last queue not last end + */ +#if 0 + if (time_after(cic->last_end_request, cic->last_queue)) + elapsed = jiffies - cic->last_end_request; + else + elapsed = jiffies - cic->last_queue; +#else + elapsed = jiffies - cic->last_end_request; +#endif - if (!new_cfqq && !(gfp_mask & __GFP_WAIT)) - goto out; + ttime = min(elapsed, 2UL * cfqd->cfq_slice_idle); - goto retry; - } + cic->ttime_samples = (7*cic->ttime_samples + 256) / 8; + cic->ttime_total = (7*cic->ttime_total + 256*ttime) / 8; + cic->ttime_mean = (cic->ttime_total + 128) / cic->ttime_samples; +} - memset(cfqq, 0, sizeof(*cfqq)); +#define sample_valid(samples) ((samples) > 80) - INIT_HLIST_NODE(&cfqq->cfq_hash); - INIT_LIST_HEAD(&cfqq->cfq_list); - RB_CLEAR_ROOT(&cfqq->sort_list); - INIT_LIST_HEAD(&cfqq->fifo[0]); - INIT_LIST_HEAD(&cfqq->fifo[1]); +/* + * Disable idle window if the process thinks too long or seeks so much that + * it doesn't matter + */ +static void +cfq_update_idle_window(struct cfq_data *cfqd, struct cfq_queue *cfqq, + struct cfq_io_context *cic) +{ + int enable_idle = cfq_cfqq_idle_window(cfqq); - cfqq->key = key; - hlist_add_head(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); - atomic_set(&cfqq->ref, 0); - cfqq->cfqd = cfqd; - atomic_inc(&cfqd->ref); - cfqq->key_type = cfqd->key_type; - cfqq->service_start = ~0UL; + if (!cic->ioc->task || !cfqd->cfq_slice_idle) + enable_idle = 0; + else if (sample_valid(cic->ttime_samples)) { + if (cic->ttime_mean > cfqd->cfq_slice_idle) + enable_idle = 0; + else + enable_idle = 1; } - if (new_cfqq) - kmem_cache_free(cfq_pool, new_cfqq); + if (enable_idle) + cfq_mark_cfqq_idle_window(cfqq); + else + cfq_clear_cfqq_idle_window(cfqq); +} - atomic_inc(&cfqq->ref); -out: - WARN_ON((gfp_mask & __GFP_WAIT) && !cfqq); - return cfqq; + +/* + * Check if new_cfqq should preempt the currently active queue. Return 0 for + * no or if we aren't sure, a 1 will cause a preempt. + */ +static int +cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq, + struct cfq_rq *crq) +{ + struct cfq_queue *cfqq = cfqd->active_queue; + + if (cfq_class_idle(new_cfqq)) + return 0; + + if (!cfqq) + return 1; + + if (cfq_class_idle(cfqq)) + return 1; + if (!cfq_cfqq_wait_request(new_cfqq)) + return 0; + /* + * if it doesn't have slice left, forget it + */ + if (new_cfqq->slice_left < cfqd->cfq_slice_idle) + return 0; + if (cfq_crq_is_sync(crq) && !cfq_cfqq_sync(cfqq)) + return 1; + + return 0; +} + +/* + * cfqq preempts the active queue. if we allowed preempt with no slice left, + * let it have half of its nominal slice. + */ +static void cfq_preempt_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + struct cfq_queue *__cfqq, *next; + + list_for_each_entry_safe(__cfqq, next, &cfqd->cur_rr, cfq_list) + cfq_resort_rr_list(__cfqq, 1); + + if (!cfqq->slice_left) + cfqq->slice_left = cfq_prio_to_slice(cfqd, cfqq) / 2; + + cfqq->slice_end = cfqq->slice_left + jiffies; + __cfq_slice_expired(cfqd, cfqq, 1); + __cfq_set_active_queue(cfqd, cfqq); +} + +/* + * should really be a ll_rw_blk.c helper + */ +static void cfq_start_queueing(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + request_queue_t *q = cfqd->queue; + + if (!blk_queue_plugged(q)) + q->request_fn(q); + else + __generic_unplug_device(q); } -static void cfq_enqueue(struct cfq_data *cfqd, struct cfq_rq *crq) +/* + * Called when a new fs request (crq) is added (to cfqq). Check if there's + * something we should do about it + */ +static void +cfq_crq_enqueued(struct cfq_data *cfqd, struct cfq_queue *cfqq, + struct cfq_rq *crq) { - crq->is_sync = 0; - if (rq_data_dir(crq->request) == READ || current->flags & PF_SYNCWRITE) - crq->is_sync = 1; + const int sync = cfq_crq_is_sync(crq); + + cfqq->next_crq = cfq_choose_req(cfqd, cfqq->next_crq, crq); + + if (sync) { + struct cfq_io_context *cic = crq->io_context; + + cfq_update_io_thinktime(cfqd, cic); + cfq_update_idle_window(cfqd, cfqq, cic); + + cic->last_queue = jiffies; + } + + if (cfqq == cfqd->active_queue) { + /* + * if we are waiting for a request for this queue, let it rip + * immediately and flag that we must not expire this queue + * just now + */ + if (cfq_cfqq_wait_request(cfqq)) { + cfq_mark_cfqq_must_dispatch(cfqq); + del_timer(&cfqd->idle_slice_timer); + cfq_start_queueing(cfqd, cfqq); + } + } else if (cfq_should_preempt(cfqd, cfqq, crq)) { + /* + * not the active queue - expire current slice if it is + * idle and has expired it's mean thinktime or this new queue + * has some old slice time left and is of higher priority + */ + cfq_preempt_queue(cfqd, cfqq); + cfq_mark_cfqq_must_dispatch(cfqq); + cfq_start_queueing(cfqd, cfqq); + } +} + +static void cfq_enqueue(struct cfq_data *cfqd, struct request *rq) +{ + struct cfq_rq *crq = RQ_DATA(rq); + struct cfq_queue *cfqq = crq->cfq_queue; + + cfq_init_prio_data(cfqq); cfq_add_crq_rb(crq); - crq->queue_start = jiffies; - list_add_tail(&crq->request->queuelist, &crq->cfq_queue->fifo[crq->is_sync]); + list_add_tail(&rq->queuelist, &cfqq->fifo); + + if (rq_mergeable(rq)) { + cfq_add_crq_hash(cfqd, crq); + + if (!cfqd->queue->last_merge) + cfqd->queue->last_merge = rq; + } + + cfq_crq_enqueued(cfqd, cfqq, crq); } static void cfq_insert_request(request_queue_t *q, struct request *rq, int where) { struct cfq_data *cfqd = q->elevator->elevator_data; - struct cfq_rq *crq = RQ_DATA(rq); switch (where) { case ELEVATOR_INSERT_BACK: - while (cfq_dispatch_requests(q, cfqd->cfq_quantum)) + while (cfq_dispatch_requests(q, INT_MAX, 1)) ; list_add_tail(&rq->queuelist, &q->queue_head); + /* + * If we were idling with pending requests on + * inactive cfqqs, force dispatching will + * remove the idle timer and the queue won't + * be kicked by __make_request() afterward. + * Kick it here. + */ + cfq_schedule_dispatch(cfqd); break; case ELEVATOR_INSERT_FRONT: list_add(&rq->queuelist, &q->queue_head); break; case ELEVATOR_INSERT_SORT: BUG_ON(!blk_fs_request(rq)); - cfq_enqueue(cfqd, crq); + cfq_enqueue(cfqd, rq); break; default: printk("%s: bad insert point %d\n", __FUNCTION__,where); return; } +} - if (rq_mergeable(rq)) { - cfq_add_crq_hash(cfqd, crq); - - if (!q->last_merge) - q->last_merge = rq; - } +static inline int cfq_pending_requests(struct cfq_data *cfqd) +{ + return !list_empty(&cfqd->queue->queue_head) || cfqd->busy_queues; } static int cfq_queue_empty(request_queue_t *q) { struct cfq_data *cfqd = q->elevator->elevator_data; - return list_empty(&q->queue_head) && list_empty(&cfqd->rr_list); + return !cfq_pending_requests(cfqd); } static void cfq_completed_request(request_queue_t *q, struct request *rq) @@ -1300,9 +1868,11 @@ static void cfq_completed_request(request_queue_t *q, struct request *rq) cfqq = crq->cfq_queue; - if (crq->in_flight) { - WARN_ON(!cfqq->in_flight); - cfqq->in_flight--; + if (cfq_crq_in_flight(crq)) { + const int sync = cfq_crq_is_sync(crq); + + WARN_ON(!cfqq->on_dispatch[sync]); + cfqq->on_dispatch[sync]--; } cfq_account_completion(cfqq, crq); @@ -1332,51 +1902,136 @@ cfq_latter_request(request_queue_t *q, struct request *rq) return NULL; } -static int cfq_may_queue(request_queue_t *q, int rw) +/* + * we temporarily boost lower priority queues if they are holding fs exclusive + * resources. they are boosted to normal prio (CLASS_BE/4) + */ +static void cfq_prio_boost(struct cfq_queue *cfqq) { - struct cfq_data *cfqd = q->elevator->elevator_data; - struct cfq_queue *cfqq; - int ret = ELV_MQUEUE_MAY; + const int ioprio_class = cfqq->ioprio_class; + const int ioprio = cfqq->ioprio; - if (current->flags & PF_MEMALLOC) - return ELV_MQUEUE_MAY; + if (has_fs_excl()) { + /* + * boost idle prio on transactions that would lock out other + * users of the filesystem + */ + if (cfq_class_idle(cfqq)) + cfqq->ioprio_class = IOPRIO_CLASS_BE; + if (cfqq->ioprio > IOPRIO_NORM) + cfqq->ioprio = IOPRIO_NORM; + } else { + /* + * check if we need to unboost the queue + */ + if (cfqq->ioprio_class != cfqq->org_ioprio_class) + cfqq->ioprio_class = cfqq->org_ioprio_class; + if (cfqq->ioprio != cfqq->org_ioprio) + cfqq->ioprio = cfqq->org_ioprio; + } - cfqq = cfq_find_cfq_hash(cfqd, cfq_hash_key(cfqd, current)); - if (cfqq) { - int limit = cfqd->max_queued; + /* + * refile between round-robin lists if we moved the priority class + */ + if ((ioprio_class != cfqq->ioprio_class || ioprio != cfqq->ioprio) && + cfq_cfqq_on_rr(cfqq)) + cfq_resort_rr_list(cfqq, 0); +} - if (cfqq->allocated[rw] < cfqd->cfq_queued) - return ELV_MQUEUE_MUST; +static inline pid_t cfq_queue_pid(struct task_struct *task, int rw) +{ + if (rw == READ || process_sync(task)) + return task->pid; - if (cfqd->busy_queues) - limit = q->nr_requests / cfqd->busy_queues; + return CFQ_KEY_ASYNC; +} - if (limit < cfqd->cfq_queued) - limit = cfqd->cfq_queued; - else if (limit > cfqd->max_queued) - limit = cfqd->max_queued; +static inline int +__cfq_may_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq, + struct task_struct *task, int rw) +{ +#if 1 + if ((cfq_cfqq_wait_request(cfqq) || cfq_cfqq_must_alloc(cfqq)) && + !cfq_cfqq_must_alloc_slice) { + cfq_mark_cfqq_must_alloc_slice(cfqq); + return ELV_MQUEUE_MUST; + } - if (cfqq->allocated[rw] >= limit) { - if (limit > cfqq->alloc_limit[rw]) - cfqq->alloc_limit[rw] = limit; + return ELV_MQUEUE_MAY; +#else + if (!cfqq || task->flags & PF_MEMALLOC) + return ELV_MQUEUE_MAY; + if (!cfqq->allocated[rw] || cfq_cfqq_must_alloc(cfqq)) { + if (cfq_cfqq_wait_request(cfqq)) + return ELV_MQUEUE_MUST; - ret = ELV_MQUEUE_NO; + /* + * only allow 1 ELV_MQUEUE_MUST per slice, otherwise we + * can quickly flood the queue with writes from a single task + */ + if (rw == READ || !cfq_cfqq_must_alloc_slice) { + cfq_mark_cfqq_must_alloc_slice(cfqq); + return ELV_MQUEUE_MUST; } + + return ELV_MQUEUE_MAY; } + if (cfq_class_idle(cfqq)) + return ELV_MQUEUE_NO; + if (cfqq->allocated[rw] >= cfqd->max_queued) { + struct io_context *ioc = get_io_context(GFP_ATOMIC); + int ret = ELV_MQUEUE_NO; - return ret; + if (ioc && ioc->nr_batch_requests) + ret = ELV_MQUEUE_MAY; + + put_io_context(ioc); + return ret; + } + + return ELV_MQUEUE_MAY; +#endif +} + +static int cfq_may_queue(request_queue_t *q, int rw, struct bio *bio) +{ + struct cfq_data *cfqd = q->elevator->elevator_data; + struct task_struct *tsk = current; + struct cfq_queue *cfqq; + + /* + * don't force setup of a queue from here, as a call to may_queue + * does not necessarily imply that a request actually will be queued. + * so just lookup a possibly existing queue, or return 'may queue' + * if that fails + */ + cfqq = cfq_find_cfq_hash(cfqd, cfq_queue_pid(tsk, rw), tsk->ioprio); + if (cfqq) { + cfq_init_prio_data(cfqq); + cfq_prio_boost(cfqq); + + return __cfq_may_queue(cfqd, cfqq, tsk, rw); + } + + return ELV_MQUEUE_MAY; } static void cfq_check_waiters(request_queue_t *q, struct cfq_queue *cfqq) { + struct cfq_data *cfqd = q->elevator->elevator_data; struct request_list *rl = &q->rq; - const int write = waitqueue_active(&rl->wait[WRITE]); - const int read = waitqueue_active(&rl->wait[READ]); - if (read && cfqq->allocated[READ] < cfqq->alloc_limit[READ]) - wake_up(&rl->wait[READ]); - if (write && cfqq->allocated[WRITE] < cfqq->alloc_limit[WRITE]) - wake_up(&rl->wait[WRITE]); + if (cfqq->allocated[READ] <= cfqd->max_queued || cfqd->rq_starved) { + smp_mb(); + if (waitqueue_active(&rl->wait[READ])) + wake_up(&rl->wait[READ]); + } + + if (cfqq->allocated[WRITE] <= cfqd->max_queued || cfqd->rq_starved) { + smp_mb(); + if (waitqueue_active(&rl->wait[WRITE])) + wake_up(&rl->wait[WRITE]); + } } /* @@ -1389,69 +2044,61 @@ static void cfq_put_request(request_queue_t *q, struct request *rq) if (crq) { struct cfq_queue *cfqq = crq->cfq_queue; + const int rw = rq_data_dir(rq); - BUG_ON(q->last_merge == rq); - BUG_ON(!hlist_unhashed(&crq->hash)); + BUG_ON(!cfqq->allocated[rw]); + cfqq->allocated[rw]--; - if (crq->io_context) - put_io_context(crq->io_context->ioc); - - BUG_ON(!cfqq->allocated[crq->is_write]); - cfqq->allocated[crq->is_write]--; + put_io_context(crq->io_context->ioc); mempool_free(crq, cfqd->crq_pool); rq->elevator_private = NULL; - smp_mb(); cfq_check_waiters(q, cfqq); cfq_put_queue(cfqq); } } /* - * Allocate cfq data structures associated with this request. A queue and + * Allocate cfq data structures associated with this request. */ -static int cfq_set_request(request_queue_t *q, struct request *rq, int gfp_mask) +static int +cfq_set_request(request_queue_t *q, struct request *rq, struct bio *bio, + int gfp_mask) { struct cfq_data *cfqd = q->elevator->elevator_data; + struct task_struct *tsk = current; struct cfq_io_context *cic; const int rw = rq_data_dir(rq); - struct cfq_queue *cfqq, *saved_cfqq; + pid_t key = cfq_queue_pid(tsk, rw); + struct cfq_queue *cfqq; struct cfq_rq *crq; unsigned long flags; might_sleep_if(gfp_mask & __GFP_WAIT); + cic = cfq_get_io_context(cfqd, key, gfp_mask); + spin_lock_irqsave(q->queue_lock, flags); - cfqq = __cfq_get_queue(cfqd, cfq_hash_key(cfqd, current), gfp_mask); - if (!cfqq) - goto out_lock; + if (!cic) + goto queue_fail; + + if (!cic->cfqq) { + cfqq = cfq_get_queue(cfqd, key, tsk->ioprio, gfp_mask); + if (!cfqq) + goto queue_fail; -repeat: - if (cfqq->allocated[rw] >= cfqd->max_queued) - goto out_lock; + cic->cfqq = cfqq; + } else + cfqq = cic->cfqq; cfqq->allocated[rw]++; + cfq_clear_cfqq_must_alloc(cfqq); + cfqd->rq_starved = 0; + atomic_inc(&cfqq->ref); spin_unlock_irqrestore(q->queue_lock, flags); - /* - * if hashing type has changed, the cfq_queue might change here. - */ - saved_cfqq = cfqq; - cic = cfq_get_io_context(&cfqq, gfp_mask); - if (!cic) - goto err; - - /* - * repeat allocation checks on queue change - */ - if (unlikely(saved_cfqq != cfqq)) { - spin_lock_irqsave(q->queue_lock, flags); - saved_cfqq->allocated[rw]--; - goto repeat; - } - crq = mempool_alloc(cfqd->crq_pool, gfp_mask); if (crq) { RB_CLEAR(&crq->rb_node); @@ -1460,24 +2107,141 @@ repeat: INIT_HLIST_NODE(&crq->hash); crq->cfq_queue = cfqq; crq->io_context = cic; - crq->service_start = crq->queue_start = 0; - crq->in_flight = crq->accounted = crq->is_sync = 0; - crq->is_write = rw; + cfq_clear_crq_in_flight(crq); + cfq_clear_crq_in_driver(crq); + cfq_clear_crq_requeued(crq); + + if (rw == READ || process_sync(tsk)) + cfq_mark_crq_is_sync(crq); + else + cfq_clear_crq_is_sync(crq); + rq->elevator_private = crq; - cfqq->alloc_limit[rw] = 0; return 0; } - put_io_context(cic->ioc); -err: spin_lock_irqsave(q->queue_lock, flags); cfqq->allocated[rw]--; + if (!(cfqq->allocated[0] + cfqq->allocated[1])) + cfq_mark_cfqq_must_alloc(cfqq); cfq_put_queue(cfqq); -out_lock: +queue_fail: + if (cic) + put_io_context(cic->ioc); + /* + * mark us rq allocation starved. we need to kickstart the process + * ourselves if there are no pending requests that can do it for us. + * that would be an extremely rare OOM situation + */ + cfqd->rq_starved = 1; + cfq_schedule_dispatch(cfqd); spin_unlock_irqrestore(q->queue_lock, flags); return 1; } +static void cfq_kick_queue(void *data) +{ + request_queue_t *q = data; + struct cfq_data *cfqd = q->elevator->elevator_data; + unsigned long flags; + + spin_lock_irqsave(q->queue_lock, flags); + + if (cfqd->rq_starved) { + struct request_list *rl = &q->rq; + + /* + * we aren't guaranteed to get a request after this, but we + * have to be opportunistic + */ + smp_mb(); + if (waitqueue_active(&rl->wait[READ])) + wake_up(&rl->wait[READ]); + if (waitqueue_active(&rl->wait[WRITE])) + wake_up(&rl->wait[WRITE]); + } + + blk_remove_plug(q); + q->request_fn(q); + spin_unlock_irqrestore(q->queue_lock, flags); +} + +/* + * Timer running if the active_queue is currently idling inside its time slice + */ +static void cfq_idle_slice_timer(unsigned long data) +{ + struct cfq_data *cfqd = (struct cfq_data *) data; + struct cfq_queue *cfqq; + unsigned long flags; + + spin_lock_irqsave(cfqd->queue->queue_lock, flags); + + if ((cfqq = cfqd->active_queue) != NULL) { + unsigned long now = jiffies; + + /* + * expired + */ + if (time_after(now, cfqq->slice_end)) + goto expire; + + /* + * only expire and reinvoke request handler, if there are + * other queues with pending requests + */ + if (!cfq_pending_requests(cfqd)) { + cfqd->idle_slice_timer.expires = min(now + cfqd->cfq_slice_idle, cfqq->slice_end); + add_timer(&cfqd->idle_slice_timer); + goto out_cont; + } + + /* + * not expired and it has a request pending, let it dispatch + */ + if (!RB_EMPTY(&cfqq->sort_list)) { + cfq_mark_cfqq_must_dispatch(cfqq); + goto out_kick; + } + } +expire: + cfq_slice_expired(cfqd, 0); +out_kick: + cfq_schedule_dispatch(cfqd); +out_cont: + spin_unlock_irqrestore(cfqd->queue->queue_lock, flags); +} + +/* + * Timer running if an idle class queue is waiting for service + */ +static void cfq_idle_class_timer(unsigned long data) +{ + struct cfq_data *cfqd = (struct cfq_data *) data; + unsigned long flags, end; + + spin_lock_irqsave(cfqd->queue->queue_lock, flags); + + /* + * race with a non-idle queue, reset timer + */ + end = cfqd->last_end_request + CFQ_IDLE_GRACE; + if (!time_after_eq(jiffies, end)) { + cfqd->idle_class_timer.expires = end; + add_timer(&cfqd->idle_class_timer); + } else + cfq_schedule_dispatch(cfqd); + + spin_unlock_irqrestore(cfqd->queue->queue_lock, flags); +} + +static void cfq_shutdown_timer_wq(struct cfq_data *cfqd) +{ + del_timer_sync(&cfqd->idle_slice_timer); + del_timer_sync(&cfqd->idle_class_timer); + blk_sync_queue(cfqd->queue); +} + static void cfq_put_cfqd(struct cfq_data *cfqd) { request_queue_t *q = cfqd->queue; @@ -1487,6 +2251,9 @@ static void cfq_put_cfqd(struct cfq_data *cfqd) blk_put_queue(q); + cfq_shutdown_timer_wq(cfqd); + q->elevator->elevator_data = NULL; + mempool_destroy(cfqd->crq_pool); kfree(cfqd->crq_hash); kfree(cfqd->cfq_hash); @@ -1495,7 +2262,10 @@ static void cfq_put_cfqd(struct cfq_data *cfqd) static void cfq_exit_queue(elevator_t *e) { - cfq_put_cfqd(e->elevator_data); + struct cfq_data *cfqd = e->elevator_data; + + cfq_shutdown_timer_wq(cfqd); + cfq_put_cfqd(cfqd); } static int cfq_init_queue(request_queue_t *q, elevator_t *e) @@ -1508,7 +2278,13 @@ static int cfq_init_queue(request_queue_t *q, elevator_t *e) return -ENOMEM; memset(cfqd, 0, sizeof(*cfqd)); - INIT_LIST_HEAD(&cfqd->rr_list); + + for (i = 0; i < CFQ_PRIO_LISTS; i++) + INIT_LIST_HEAD(&cfqd->rr_list[i]); + + INIT_LIST_HEAD(&cfqd->busy_rr); + INIT_LIST_HEAD(&cfqd->cur_rr); + INIT_LIST_HEAD(&cfqd->idle_rr); INIT_LIST_HEAD(&cfqd->empty_list); cfqd->crq_hash = kmalloc(sizeof(struct hlist_head) * CFQ_MHASH_ENTRIES, GFP_KERNEL); @@ -1533,24 +2309,32 @@ static int cfq_init_queue(request_queue_t *q, elevator_t *e) cfqd->queue = q; atomic_inc(&q->refcnt); - /* - * just set it to some high value, we want anyone to be able to queue - * some requests. fairness is handled differently - */ - q->nr_requests = 1024; - cfqd->max_queued = q->nr_requests / 16; + cfqd->max_queued = q->nr_requests / 4; q->nr_batching = cfq_queued; - cfqd->key_type = CFQ_KEY_TGID; - cfqd->find_best_crq = 1; + + init_timer(&cfqd->idle_slice_timer); + cfqd->idle_slice_timer.function = cfq_idle_slice_timer; + cfqd->idle_slice_timer.data = (unsigned long) cfqd; + + init_timer(&cfqd->idle_class_timer); + cfqd->idle_class_timer.function = cfq_idle_class_timer; + cfqd->idle_class_timer.data = (unsigned long) cfqd; + + INIT_WORK(&cfqd->unplug_work, cfq_kick_queue, q); + atomic_set(&cfqd->ref, 1); cfqd->cfq_queued = cfq_queued; cfqd->cfq_quantum = cfq_quantum; - cfqd->cfq_fifo_expire_r = cfq_fifo_expire_r; - cfqd->cfq_fifo_expire_w = cfq_fifo_expire_w; - cfqd->cfq_fifo_batch_expire = cfq_fifo_rate; + cfqd->cfq_fifo_expire[0] = cfq_fifo_expire[0]; + cfqd->cfq_fifo_expire[1] = cfq_fifo_expire[1]; cfqd->cfq_back_max = cfq_back_max; cfqd->cfq_back_penalty = cfq_back_penalty; + cfqd->cfq_slice[0] = cfq_slice_async; + cfqd->cfq_slice[1] = cfq_slice_sync; + cfqd->cfq_slice_async_rq = cfq_slice_async_rq; + cfqd->cfq_slice_idle = cfq_slice_idle; + cfqd->cfq_max_depth = cfq_max_depth; return 0; out_crqpool: @@ -1595,7 +2379,6 @@ fail: return -ENOMEM; } - /* * sysfs parts below --> */ @@ -1620,45 +2403,6 @@ cfq_var_store(unsigned int *var, const char *page, size_t count) return count; } -static ssize_t -cfq_clear_elapsed(struct cfq_data *cfqd, const char *page, size_t count) -{ - max_elapsed_dispatch = max_elapsed_crq = 0; - return count; -} - -static ssize_t -cfq_set_key_type(struct cfq_data *cfqd, const char *page, size_t count) -{ - spin_lock_irq(cfqd->queue->queue_lock); - if (!strncmp(page, "pgid", 4)) - cfqd->key_type = CFQ_KEY_PGID; - else if (!strncmp(page, "tgid", 4)) - cfqd->key_type = CFQ_KEY_TGID; - else if (!strncmp(page, "uid", 3)) - cfqd->key_type = CFQ_KEY_UID; - else if (!strncmp(page, "gid", 3)) - cfqd->key_type = CFQ_KEY_GID; - spin_unlock_irq(cfqd->queue->queue_lock); - return count; -} - -static ssize_t -cfq_read_key_type(struct cfq_data *cfqd, char *page) -{ - ssize_t len = 0; - int i; - - for (i = CFQ_KEY_PGID; i < CFQ_KEY_LAST; i++) { - if (cfqd->key_type == i) - len += sprintf(page+len, "[%s] ", cfq_key_types[i]); - else - len += sprintf(page+len, "%s ", cfq_key_types[i]); - } - len += sprintf(page+len, "\n"); - return len; -} - #define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ static ssize_t __FUNC(struct cfq_data *cfqd, char *page) \ { \ @@ -1669,12 +2413,15 @@ static ssize_t __FUNC(struct cfq_data *cfqd, char *page) \ } SHOW_FUNCTION(cfq_quantum_show, cfqd->cfq_quantum, 0); SHOW_FUNCTION(cfq_queued_show, cfqd->cfq_queued, 0); -SHOW_FUNCTION(cfq_fifo_expire_r_show, cfqd->cfq_fifo_expire_r, 1); -SHOW_FUNCTION(cfq_fifo_expire_w_show, cfqd->cfq_fifo_expire_w, 1); -SHOW_FUNCTION(cfq_fifo_batch_expire_show, cfqd->cfq_fifo_batch_expire, 1); -SHOW_FUNCTION(cfq_find_best_show, cfqd->find_best_crq, 0); +SHOW_FUNCTION(cfq_fifo_expire_sync_show, cfqd->cfq_fifo_expire[1], 1); +SHOW_FUNCTION(cfq_fifo_expire_async_show, cfqd->cfq_fifo_expire[0], 1); SHOW_FUNCTION(cfq_back_max_show, cfqd->cfq_back_max, 0); SHOW_FUNCTION(cfq_back_penalty_show, cfqd->cfq_back_penalty, 0); +SHOW_FUNCTION(cfq_slice_idle_show, cfqd->cfq_slice_idle, 1); +SHOW_FUNCTION(cfq_slice_sync_show, cfqd->cfq_slice[1], 1); +SHOW_FUNCTION(cfq_slice_async_show, cfqd->cfq_slice[0], 1); +SHOW_FUNCTION(cfq_slice_async_rq_show, cfqd->cfq_slice_async_rq, 0); +SHOW_FUNCTION(cfq_max_depth_show, cfqd->cfq_max_depth, 0); #undef SHOW_FUNCTION #define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ @@ -1694,12 +2441,15 @@ static ssize_t __FUNC(struct cfq_data *cfqd, const char *page, size_t count) \ } STORE_FUNCTION(cfq_quantum_store, &cfqd->cfq_quantum, 1, UINT_MAX, 0); STORE_FUNCTION(cfq_queued_store, &cfqd->cfq_queued, 1, UINT_MAX, 0); -STORE_FUNCTION(cfq_fifo_expire_r_store, &cfqd->cfq_fifo_expire_r, 1, UINT_MAX, 1); -STORE_FUNCTION(cfq_fifo_expire_w_store, &cfqd->cfq_fifo_expire_w, 1, UINT_MAX, 1); -STORE_FUNCTION(cfq_fifo_batch_expire_store, &cfqd->cfq_fifo_batch_expire, 0, UINT_MAX, 1); -STORE_FUNCTION(cfq_find_best_store, &cfqd->find_best_crq, 0, 1, 0); +STORE_FUNCTION(cfq_fifo_expire_sync_store, &cfqd->cfq_fifo_expire[1], 1, UINT_MAX, 1); +STORE_FUNCTION(cfq_fifo_expire_async_store, &cfqd->cfq_fifo_expire[0], 1, UINT_MAX, 1); STORE_FUNCTION(cfq_back_max_store, &cfqd->cfq_back_max, 0, UINT_MAX, 0); STORE_FUNCTION(cfq_back_penalty_store, &cfqd->cfq_back_penalty, 1, UINT_MAX, 0); +STORE_FUNCTION(cfq_slice_idle_store, &cfqd->cfq_slice_idle, 0, UINT_MAX, 1); +STORE_FUNCTION(cfq_slice_sync_store, &cfqd->cfq_slice[1], 1, UINT_MAX, 1); +STORE_FUNCTION(cfq_slice_async_store, &cfqd->cfq_slice[0], 1, UINT_MAX, 1); +STORE_FUNCTION(cfq_slice_async_rq_store, &cfqd->cfq_slice_async_rq, 1, UINT_MAX, 0); +STORE_FUNCTION(cfq_max_depth_store, &cfqd->cfq_max_depth, 1, UINT_MAX, 0); #undef STORE_FUNCTION static struct cfq_fs_entry cfq_quantum_entry = { @@ -1712,25 +2462,15 @@ static struct cfq_fs_entry cfq_queued_entry = { .show = cfq_queued_show, .store = cfq_queued_store, }; -static struct cfq_fs_entry cfq_fifo_expire_r_entry = { +static struct cfq_fs_entry cfq_fifo_expire_sync_entry = { .attr = {.name = "fifo_expire_sync", .mode = S_IRUGO | S_IWUSR }, - .show = cfq_fifo_expire_r_show, - .store = cfq_fifo_expire_r_store, + .show = cfq_fifo_expire_sync_show, + .store = cfq_fifo_expire_sync_store, }; -static struct cfq_fs_entry cfq_fifo_expire_w_entry = { +static struct cfq_fs_entry cfq_fifo_expire_async_entry = { .attr = {.name = "fifo_expire_async", .mode = S_IRUGO | S_IWUSR }, - .show = cfq_fifo_expire_w_show, - .store = cfq_fifo_expire_w_store, -}; -static struct cfq_fs_entry cfq_fifo_batch_expire_entry = { - .attr = {.name = "fifo_batch_expire", .mode = S_IRUGO | S_IWUSR }, - .show = cfq_fifo_batch_expire_show, - .store = cfq_fifo_batch_expire_store, -}; -static struct cfq_fs_entry cfq_find_best_entry = { - .attr = {.name = "find_best_crq", .mode = S_IRUGO | S_IWUSR }, - .show = cfq_find_best_show, - .store = cfq_find_best_store, + .show = cfq_fifo_expire_async_show, + .store = cfq_fifo_expire_async_store, }; static struct cfq_fs_entry cfq_back_max_entry = { .attr = {.name = "back_seek_max", .mode = S_IRUGO | S_IWUSR }, @@ -1742,27 +2482,44 @@ static struct cfq_fs_entry cfq_back_penalty_entry = { .show = cfq_back_penalty_show, .store = cfq_back_penalty_store, }; -static struct cfq_fs_entry cfq_clear_elapsed_entry = { - .attr = {.name = "clear_elapsed", .mode = S_IWUSR }, - .store = cfq_clear_elapsed, +static struct cfq_fs_entry cfq_slice_sync_entry = { + .attr = {.name = "slice_sync", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_slice_sync_show, + .store = cfq_slice_sync_store, +}; +static struct cfq_fs_entry cfq_slice_async_entry = { + .attr = {.name = "slice_async", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_slice_async_show, + .store = cfq_slice_async_store, }; -static struct cfq_fs_entry cfq_key_type_entry = { - .attr = {.name = "key_type", .mode = S_IRUGO | S_IWUSR }, - .show = cfq_read_key_type, - .store = cfq_set_key_type, +static struct cfq_fs_entry cfq_slice_async_rq_entry = { + .attr = {.name = "slice_async_rq", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_slice_async_rq_show, + .store = cfq_slice_async_rq_store, +}; +static struct cfq_fs_entry cfq_slice_idle_entry = { + .attr = {.name = "slice_idle", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_slice_idle_show, + .store = cfq_slice_idle_store, +}; +static struct cfq_fs_entry cfq_max_depth_entry = { + .attr = {.name = "max_depth", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_max_depth_show, + .store = cfq_max_depth_store, }; static struct attribute *default_attrs[] = { &cfq_quantum_entry.attr, &cfq_queued_entry.attr, - &cfq_fifo_expire_r_entry.attr, - &cfq_fifo_expire_w_entry.attr, - &cfq_fifo_batch_expire_entry.attr, - &cfq_key_type_entry.attr, - &cfq_find_best_entry.attr, + &cfq_fifo_expire_sync_entry.attr, + &cfq_fifo_expire_async_entry.attr, &cfq_back_max_entry.attr, &cfq_back_penalty_entry.attr, - &cfq_clear_elapsed_entry.attr, + &cfq_slice_sync_entry.attr, + &cfq_slice_async_entry.attr, + &cfq_slice_async_rq_entry.attr, + &cfq_slice_idle_entry.attr, + &cfq_max_depth_entry.attr, NULL, }; @@ -1832,21 +2589,46 @@ static int __init cfq_init(void) { int ret; + /* + * could be 0 on HZ < 1000 setups + */ + if (!cfq_slice_async) + cfq_slice_async = 1; + if (!cfq_slice_idle) + cfq_slice_idle = 1; + if (cfq_slab_setup()) return -ENOMEM; ret = elv_register(&iosched_cfq); - if (!ret) { - __module_get(THIS_MODULE); - return 0; - } + if (ret) + cfq_slab_kill(); - cfq_slab_kill(); return ret; } static void __exit cfq_exit(void) { + struct task_struct *g, *p; + unsigned long flags; + + read_lock_irqsave(&tasklist_lock, flags); + + /* + * iterate each process in the system, removing our io_context + */ + do_each_thread(g, p) { + struct io_context *ioc = p->io_context; + + if (ioc && ioc->cic) { + ioc->cic->exit(ioc->cic); + cfq_free_io_context(ioc->cic); + ioc->cic = NULL; + } + } while_each_thread(g, p); + + read_unlock_irqrestore(&tasklist_lock, flags); + cfq_slab_kill(); elv_unregister(&iosched_cfq); } diff --git a/drivers/block/deadline-iosched.c b/drivers/block/deadline-iosched.c index 4bc2fea7327..ff5201e0215 100644 --- a/drivers/block/deadline-iosched.c +++ b/drivers/block/deadline-iosched.c @@ -760,7 +760,8 @@ static void deadline_put_request(request_queue_t *q, struct request *rq) } static int -deadline_set_request(request_queue_t *q, struct request *rq, int gfp_mask) +deadline_set_request(request_queue_t *q, struct request *rq, struct bio *bio, + int gfp_mask) { struct deadline_data *dd = q->elevator->elevator_data; struct deadline_rq *drq; diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index f831f08f839..98f0126a2de 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -486,12 +486,13 @@ struct request *elv_former_request(request_queue_t *q, struct request *rq) return NULL; } -int elv_set_request(request_queue_t *q, struct request *rq, int gfp_mask) +int elv_set_request(request_queue_t *q, struct request *rq, struct bio *bio, + int gfp_mask) { elevator_t *e = q->elevator; if (e->ops->elevator_set_req_fn) - return e->ops->elevator_set_req_fn(q, rq, gfp_mask); + return e->ops->elevator_set_req_fn(q, rq, bio, gfp_mask); rq->elevator_private = NULL; return 0; @@ -505,12 +506,12 @@ void elv_put_request(request_queue_t *q, struct request *rq) e->ops->elevator_put_req_fn(q, rq); } -int elv_may_queue(request_queue_t *q, int rw) +int elv_may_queue(request_queue_t *q, int rw, struct bio *bio) { elevator_t *e = q->elevator; if (e->ops->elevator_may_queue_fn) - return e->ops->elevator_may_queue_fn(q, rw); + return e->ops->elevator_may_queue_fn(q, rw, bio); return ELV_MQUEUE_MAY; } diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 60e64091de1..234fdcfbdf0 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -276,6 +276,7 @@ static inline void rq_init(request_queue_t *q, struct request *rq) rq->errors = 0; rq->rq_status = RQ_ACTIVE; rq->bio = rq->biotail = NULL; + rq->ioprio = 0; rq->buffer = NULL; rq->ref_count = 1; rq->q = q; @@ -1442,11 +1443,7 @@ void __generic_unplug_device(request_queue_t *q) if (!blk_remove_plug(q)) return; - /* - * was plugged, fire request_fn if queue has stuff to do - */ - if (elv_next_request(q)) - q->request_fn(q); + q->request_fn(q); } EXPORT_SYMBOL(__generic_unplug_device); @@ -1776,8 +1773,8 @@ static inline void blk_free_request(request_queue_t *q, struct request *rq) mempool_free(rq, q->rq.rq_pool); } -static inline struct request *blk_alloc_request(request_queue_t *q, int rw, - int gfp_mask) +static inline struct request * +blk_alloc_request(request_queue_t *q, int rw, struct bio *bio, int gfp_mask) { struct request *rq = mempool_alloc(q->rq.rq_pool, gfp_mask); @@ -1790,7 +1787,7 @@ static inline struct request *blk_alloc_request(request_queue_t *q, int rw, */ rq->flags = rw; - if (!elv_set_request(q, rq, gfp_mask)) + if (!elv_set_request(q, rq, bio, gfp_mask)) return rq; mempool_free(rq, q->rq.rq_pool); @@ -1872,7 +1869,8 @@ static void freed_request(request_queue_t *q, int rw) /* * Get a free request, queue_lock must not be held */ -static struct request *get_request(request_queue_t *q, int rw, int gfp_mask) +static struct request *get_request(request_queue_t *q, int rw, struct bio *bio, + int gfp_mask) { struct request *rq = NULL; struct request_list *rl = &q->rq; @@ -1895,7 +1893,7 @@ static struct request *get_request(request_queue_t *q, int rw, int gfp_mask) } } - switch (elv_may_queue(q, rw)) { + switch (elv_may_queue(q, rw, bio)) { case ELV_MQUEUE_NO: goto rq_starved; case ELV_MQUEUE_MAY: @@ -1920,7 +1918,7 @@ get_rq: set_queue_congested(q, rw); spin_unlock_irq(q->queue_lock); - rq = blk_alloc_request(q, rw, gfp_mask); + rq = blk_alloc_request(q, rw, bio, gfp_mask); if (!rq) { /* * Allocation failed presumably due to memory. Undo anything @@ -1961,7 +1959,8 @@ out: * No available requests for this queue, unplug the device and wait for some * requests to become available. */ -static struct request *get_request_wait(request_queue_t *q, int rw) +static struct request *get_request_wait(request_queue_t *q, int rw, + struct bio *bio) { DEFINE_WAIT(wait); struct request *rq; @@ -1972,7 +1971,7 @@ static struct request *get_request_wait(request_queue_t *q, int rw) prepare_to_wait_exclusive(&rl->wait[rw], &wait, TASK_UNINTERRUPTIBLE); - rq = get_request(q, rw, GFP_NOIO); + rq = get_request(q, rw, bio, GFP_NOIO); if (!rq) { struct io_context *ioc; @@ -2003,9 +2002,9 @@ struct request *blk_get_request(request_queue_t *q, int rw, int gfp_mask) BUG_ON(rw != READ && rw != WRITE); if (gfp_mask & __GFP_WAIT) - rq = get_request_wait(q, rw); + rq = get_request_wait(q, rw, NULL); else - rq = get_request(q, rw, gfp_mask); + rq = get_request(q, rw, NULL, gfp_mask); return rq; } @@ -2333,7 +2332,6 @@ static void __blk_put_request(request_queue_t *q, struct request *req) return; req->rq_status = RQ_INACTIVE; - req->q = NULL; req->rl = NULL; /* @@ -2462,6 +2460,8 @@ static int attempt_merge(request_queue_t *q, struct request *req, req->rq_disk->in_flight--; } + req->ioprio = ioprio_best(req->ioprio, next->ioprio); + __blk_put_request(q, next); return 1; } @@ -2514,11 +2514,13 @@ static int __make_request(request_queue_t *q, struct bio *bio) { struct request *req, *freereq = NULL; int el_ret, rw, nr_sectors, cur_nr_sectors, barrier, err, sync; + unsigned short prio; sector_t sector; sector = bio->bi_sector; nr_sectors = bio_sectors(bio); cur_nr_sectors = bio_cur_sectors(bio); + prio = bio_prio(bio); rw = bio_data_dir(bio); sync = bio_sync(bio); @@ -2559,6 +2561,7 @@ again: req->biotail->bi_next = bio; req->biotail = bio; req->nr_sectors = req->hard_nr_sectors += nr_sectors; + req->ioprio = ioprio_best(req->ioprio, prio); drive_stat_acct(req, nr_sectors, 0); if (!attempt_back_merge(q, req)) elv_merged_request(q, req); @@ -2583,6 +2586,7 @@ again: req->hard_cur_sectors = cur_nr_sectors; req->sector = req->hard_sector = sector; req->nr_sectors = req->hard_nr_sectors += nr_sectors; + req->ioprio = ioprio_best(req->ioprio, prio); drive_stat_acct(req, nr_sectors, 0); if (!attempt_front_merge(q, req)) elv_merged_request(q, req); @@ -2610,7 +2614,7 @@ get_rq: freereq = NULL; } else { spin_unlock_irq(q->queue_lock); - if ((freereq = get_request(q, rw, GFP_ATOMIC)) == NULL) { + if ((freereq = get_request(q, rw, bio, GFP_ATOMIC)) == NULL) { /* * READA bit set */ @@ -2618,7 +2622,7 @@ get_rq: if (bio_rw_ahead(bio)) goto end_io; - freereq = get_request_wait(q, rw); + freereq = get_request_wait(q, rw, bio); } goto again; } @@ -2646,6 +2650,7 @@ get_rq: req->buffer = bio_data(bio); /* see ->buffer comment above */ req->waiting = NULL; req->bio = req->biotail = bio; + req->ioprio = prio; req->rq_disk = bio->bi_bdev->bd_disk; req->start_time = jiffies; @@ -2674,7 +2679,7 @@ static inline void blk_partition_remap(struct bio *bio) if (bdev != bdev->bd_contains) { struct hd_struct *p = bdev->bd_part; - switch (bio->bi_rw) { + switch (bio_data_dir(bio)) { case READ: p->read_sectors += bio_sectors(bio); p->reads++; @@ -2693,6 +2698,7 @@ void blk_finish_queue_drain(request_queue_t *q) { struct request_list *rl = &q->rq; struct request *rq; + int requeued = 0; spin_lock_irq(q->queue_lock); clear_bit(QUEUE_FLAG_DRAIN, &q->queue_flags); @@ -2701,9 +2707,13 @@ void blk_finish_queue_drain(request_queue_t *q) rq = list_entry_rq(q->drain_list.next); list_del_init(&rq->queuelist); - __elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1); + elv_requeue_request(q, rq); + requeued++; } + if (requeued) + q->request_fn(q); + spin_unlock_irq(q->queue_lock); wake_up(&rl->wait[0]); @@ -2900,7 +2910,7 @@ void submit_bio(int rw, struct bio *bio) BIO_BUG_ON(!bio->bi_size); BIO_BUG_ON(!bio->bi_io_vec); - bio->bi_rw = rw; + bio->bi_rw |= rw; if (rw & WRITE) mod_page_state(pgpgout, count); else @@ -3257,8 +3267,11 @@ void exit_io_context(void) struct io_context *ioc; local_irq_save(flags); + task_lock(current); ioc = current->io_context; current->io_context = NULL; + ioc->task = NULL; + task_unlock(current); local_irq_restore(flags); if (ioc->aic && ioc->aic->exit) @@ -3293,12 +3306,12 @@ struct io_context *get_io_context(int gfp_flags) ret = kmem_cache_alloc(iocontext_cachep, gfp_flags); if (ret) { atomic_set(&ret->refcount, 1); - ret->pid = tsk->pid; + ret->task = current; + ret->set_ioprio = NULL; ret->last_waited = jiffies; /* doesn't matter... */ ret->nr_batch_requests = 0; /* because this is 0 */ ret->aic = NULL; ret->cic = NULL; - spin_lock_init(&ret->lock); local_irq_save(flags); diff --git a/drivers/block/sx8.c b/drivers/block/sx8.c index 5ed3a637945..9db0a9e3e59 100644 --- a/drivers/block/sx8.c +++ b/drivers/block/sx8.c @@ -26,6 +26,7 @@ #include <linux/delay.h> #include <linux/time.h> #include <linux/hdreg.h> +#include <linux/dma-mapping.h> #include <asm/io.h> #include <asm/semaphore.h> #include <asm/uaccess.h> @@ -1582,9 +1583,9 @@ static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out; #if IF_64BIT_DMA_IS_POSSIBLE /* grrrr... */ - rc = pci_set_dma_mask(pdev, 0xffffffffffffffffULL); + rc = pci_set_dma_mask(pdev, DMA_64BIT_MASK); if (!rc) { - rc = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL); + rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); if (rc) { printk(KERN_ERR DRV_NAME "(%s): consistent DMA mask failure\n", pci_name(pdev)); @@ -1593,7 +1594,7 @@ static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) pci_dac = 1; } else { #endif - rc = pci_set_dma_mask(pdev, 0xffffffffULL); + rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { printk(KERN_ERR DRV_NAME "(%s): DMA mask failure\n", pci_name(pdev)); diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 556264b4342..374f404e81d 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -21,6 +21,7 @@ #include <linux/smp_lock.h> #include <linux/device.h> #include <linux/devfs_fs_kernel.h> +#include <linux/compat.h> struct evdev { int exist; @@ -145,6 +146,41 @@ static int evdev_open(struct inode * inode, struct file * file) return 0; } +#ifdef CONFIG_COMPAT +struct input_event_compat { + struct compat_timeval time; + __u16 type; + __u16 code; + __s32 value; +}; + +#ifdef CONFIG_X86_64 +# define COMPAT_TEST test_thread_flag(TIF_IA32) +#elif defined(CONFIG_IA64) +# define COMPAT_TEST IS_IA32_PROCESS(ia64_task_regs(current)) +#elif defined(CONFIG_ARCH_S390) +# define COMPAT_TEST test_thread_flag(TIF_31BIT) +#else +# define COMPAT_TEST test_thread_flag(TIF_32BIT) +#endif + +static ssize_t evdev_write_compat(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) +{ + struct evdev_list *list = file->private_data; + struct input_event_compat event; + int retval = 0; + + while (retval < count) { + if (copy_from_user(&event, buffer + retval, sizeof(struct input_event_compat))) + return -EFAULT; + input_event(list->evdev->handle.dev, event.type, event.code, event.value); + retval += sizeof(struct input_event_compat); + } + + return retval; +} +#endif + static ssize_t evdev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) { struct evdev_list *list = file->private_data; @@ -153,6 +189,11 @@ static ssize_t evdev_write(struct file * file, const char __user * buffer, size_ if (!list->evdev->exist) return -ENODEV; +#ifdef CONFIG_COMPAT + if (COMPAT_TEST) + return evdev_write_compat(file, buffer, count, ppos); +#endif + while (retval < count) { if (copy_from_user(&event, buffer + retval, sizeof(struct input_event))) @@ -164,11 +205,56 @@ static ssize_t evdev_write(struct file * file, const char __user * buffer, size_ return retval; } +#ifdef CONFIG_COMPAT +static ssize_t evdev_read_compat(struct file * file, char __user * buffer, size_t count, loff_t *ppos) +{ + struct evdev_list *list = file->private_data; + int retval; + + if (count < sizeof(struct input_event_compat)) + return -EINVAL; + + if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(list->evdev->wait, + list->head != list->tail || (!list->evdev->exist)); + + if (retval) + return retval; + + if (!list->evdev->exist) + return -ENODEV; + + while (list->head != list->tail && retval + sizeof(struct input_event_compat) <= count) { + struct input_event *event = (struct input_event *) list->buffer + list->tail; + struct input_event_compat event_compat; + event_compat.time.tv_sec = event->time.tv_sec; + event_compat.time.tv_usec = event->time.tv_usec; + event_compat.type = event->type; + event_compat.code = event->code; + event_compat.value = event->value; + + if (copy_to_user(buffer + retval, &event_compat, + sizeof(struct input_event_compat))) return -EFAULT; + list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1); + retval += sizeof(struct input_event_compat); + } + + return retval; +} +#endif + static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos) { struct evdev_list *list = file->private_data; int retval; +#ifdef CONFIG_COMPAT + if (COMPAT_TEST) + return evdev_read_compat(file, buffer, count, ppos); +#endif + if (count < sizeof(struct input_event)) return -EINVAL; @@ -186,7 +272,7 @@ static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count while (list->head != list->tail && retval + sizeof(struct input_event) <= count) { if (copy_to_user(buffer + retval, list->buffer + list->tail, - sizeof(struct input_event))) return -EFAULT; + sizeof(struct input_event))) return -EFAULT; list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1); retval += sizeof(struct input_event); } @@ -203,7 +289,7 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait) (list->evdev->exist ? 0 : (POLLHUP | POLLERR)); } -static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct evdev_list *list = file->private_data; struct evdev *evdev = list->evdev; @@ -285,109 +371,267 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, default: - if (_IOC_TYPE(cmd) != 'E' || _IOC_DIR(cmd) != _IOC_READ) + if (_IOC_TYPE(cmd) != 'E') return -EINVAL; - if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) { - - long *bits; - int len; - - switch (_IOC_NR(cmd) & EV_MAX) { - case 0: bits = dev->evbit; len = EV_MAX; break; - case EV_KEY: bits = dev->keybit; len = KEY_MAX; break; - case EV_REL: bits = dev->relbit; len = REL_MAX; break; - case EV_ABS: bits = dev->absbit; len = ABS_MAX; break; - case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break; - case EV_LED: bits = dev->ledbit; len = LED_MAX; break; - case EV_SND: bits = dev->sndbit; len = SND_MAX; break; - case EV_FF: bits = dev->ffbit; len = FF_MAX; break; - default: return -EINVAL; + if (_IOC_DIR(cmd) == _IOC_READ) { + + if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) { + + long *bits; + int len; + + switch (_IOC_NR(cmd) & EV_MAX) { + case 0: bits = dev->evbit; len = EV_MAX; break; + case EV_KEY: bits = dev->keybit; len = KEY_MAX; break; + case EV_REL: bits = dev->relbit; len = REL_MAX; break; + case EV_ABS: bits = dev->absbit; len = ABS_MAX; break; + case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break; + case EV_LED: bits = dev->ledbit; len = LED_MAX; break; + case EV_SND: bits = dev->sndbit; len = SND_MAX; break; + case EV_FF: bits = dev->ffbit; len = FF_MAX; break; + default: return -EINVAL; + } + len = NBITS(len) * sizeof(long); + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, bits, len) ? -EFAULT : len; } - len = NBITS(len) * sizeof(long); - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(p, bits, len) ? -EFAULT : len; - } - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) { - int len; - len = NBITS(KEY_MAX) * sizeof(long); - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(p, dev->key, len) ? -EFAULT : len; - } + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) { + int len; + len = NBITS(KEY_MAX) * sizeof(long); + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->key, len) ? -EFAULT : len; + } - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) { - int len; - len = NBITS(LED_MAX) * sizeof(long); - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(p, dev->led, len) ? -EFAULT : len; - } + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) { + int len; + len = NBITS(LED_MAX) * sizeof(long); + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->led, len) ? -EFAULT : len; + } - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0))) { - int len; - len = NBITS(SND_MAX) * sizeof(long); - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(p, dev->snd, len) ? -EFAULT : len; - } + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0))) { + int len; + len = NBITS(SND_MAX) * sizeof(long); + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->snd, len) ? -EFAULT : len; + } - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) { - int len; - if (!dev->name) return -ENOENT; - len = strlen(dev->name) + 1; - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(p, dev->name, len) ? -EFAULT : len; - } + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) { + int len; + if (!dev->name) return -ENOENT; + len = strlen(dev->name) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->name, len) ? -EFAULT : len; + } + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) { + int len; + if (!dev->phys) return -ENOENT; + len = strlen(dev->phys) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->phys, len) ? -EFAULT : len; + } + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) { + int len; + if (!dev->uniq) return -ENOENT; + len = strlen(dev->uniq) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->uniq, len) ? -EFAULT : len; + } + + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { + + int t = _IOC_NR(cmd) & ABS_MAX; + + abs.value = dev->abs[t]; + abs.minimum = dev->absmin[t]; + abs.maximum = dev->absmax[t]; + abs.fuzz = dev->absfuzz[t]; + abs.flat = dev->absflat[t]; + + if (copy_to_user(p, &abs, sizeof(struct input_absinfo))) + return -EFAULT; + + return 0; + } - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) { - int len; - if (!dev->phys) return -ENOENT; - len = strlen(dev->phys) + 1; - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(p, dev->phys, len) ? -EFAULT : len; } - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) { - int len; - if (!dev->uniq) return -ENOENT; - len = strlen(dev->uniq) + 1; - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user(p, dev->uniq, len) ? -EFAULT : len; + if (_IOC_DIR(cmd) == _IOC_WRITE) { + + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { + + int t = _IOC_NR(cmd) & ABS_MAX; + + if (copy_from_user(&abs, p, sizeof(struct input_absinfo))) + return -EFAULT; + + dev->abs[t] = abs.value; + dev->absmin[t] = abs.minimum; + dev->absmax[t] = abs.maximum; + dev->absfuzz[t] = abs.fuzz; + dev->absflat[t] = abs.flat; + + return 0; + } } + } + return -EINVAL; +} - if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { +#ifdef CONFIG_COMPAT + +#define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8) +#define NBITS_COMPAT(x) ((((x)-1)/BITS_PER_LONG_COMPAT)+1) +#define OFF_COMPAT(x) ((x)%BITS_PER_LONG_COMPAT) +#define BIT_COMPAT(x) (1UL<<OFF_COMPAT(x)) +#define LONG_COMPAT(x) ((x)/BITS_PER_LONG_COMPAT) +#define test_bit_compat(bit, array) ((array[LONG_COMPAT(bit)] >> OFF_COMPAT(bit)) & 1) + +#ifdef __BIG_ENDIAN +#define bit_to_user(bit, max) \ +do { \ + int i; \ + int len = NBITS_COMPAT((max)) * sizeof(compat_long_t); \ + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); \ + for (i = 0; i < len / sizeof(compat_long_t); i++) \ + if (copy_to_user((compat_long_t*) p + i, \ + (compat_long_t*) (bit) + i + 1 - ((i % 2) << 1), \ + sizeof(compat_long_t))) \ + return -EFAULT; \ + return len; \ +} while (0) +#else +#define bit_to_user(bit, max) \ +do { \ + int len = NBITS_COMPAT((max)) * sizeof(compat_long_t); \ + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); \ + return copy_to_user(p, (bit), len) ? -EFAULT : len; \ +} while (0) +#endif + +static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct evdev_list *list = file->private_data; + struct evdev *evdev = list->evdev; + struct input_dev *dev = evdev->handle.dev; + struct input_absinfo abs; + void __user *p = compat_ptr(arg); - int t = _IOC_NR(cmd) & ABS_MAX; + if (!evdev->exist) return -ENODEV; - abs.value = dev->abs[t]; - abs.minimum = dev->absmin[t]; - abs.maximum = dev->absmax[t]; - abs.fuzz = dev->absfuzz[t]; - abs.flat = dev->absflat[t]; + switch (cmd) { - if (copy_to_user(p, &abs, sizeof(struct input_absinfo))) - return -EFAULT; + case EVIOCGVERSION: + case EVIOCGID: + case EVIOCGKEYCODE: + case EVIOCSKEYCODE: + case EVIOCSFF: + case EVIOCRMFF: + case EVIOCGEFFECTS: + case EVIOCGRAB: + return evdev_ioctl(file, cmd, (unsigned long) p); - return 0; + default: + + if (_IOC_TYPE(cmd) != 'E') + return -EINVAL; + + if (_IOC_DIR(cmd) == _IOC_READ) { + + if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) { + long *bits; + int max; + + switch (_IOC_NR(cmd) & EV_MAX) { + case 0: bits = dev->evbit; max = EV_MAX; break; + case EV_KEY: bits = dev->keybit; max = KEY_MAX; break; + case EV_REL: bits = dev->relbit; max = REL_MAX; break; + case EV_ABS: bits = dev->absbit; max = ABS_MAX; break; + case EV_MSC: bits = dev->mscbit; max = MSC_MAX; break; + case EV_LED: bits = dev->ledbit; max = LED_MAX; break; + case EV_SND: bits = dev->sndbit; max = SND_MAX; break; + case EV_FF: bits = dev->ffbit; max = FF_MAX; break; + default: return -EINVAL; + } + bit_to_user(bits, max); + } + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) + bit_to_user(dev->key, KEY_MAX); + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) + bit_to_user(dev->led, LED_MAX); + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0))) + bit_to_user(dev->snd, SND_MAX); + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) { + int len; + if (!dev->name) return -ENOENT; + len = strlen(dev->name) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->name, len) ? -EFAULT : len; + } + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) { + int len; + if (!dev->phys) return -ENOENT; + len = strlen(dev->phys) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->phys, len) ? -EFAULT : len; + } + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) { + int len; + if (!dev->uniq) return -ENOENT; + len = strlen(dev->uniq) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user(p, dev->uniq, len) ? -EFAULT : len; + } + + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { + + int t = _IOC_NR(cmd) & ABS_MAX; + + abs.value = dev->abs[t]; + abs.minimum = dev->absmin[t]; + abs.maximum = dev->absmax[t]; + abs.fuzz = dev->absfuzz[t]; + abs.flat = dev->absflat[t]; + + if (copy_to_user(p, &abs, sizeof(struct input_absinfo))) + return -EFAULT; + + return 0; + } } - if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { + if (_IOC_DIR(cmd) == _IOC_WRITE) { - int t = _IOC_NR(cmd) & ABS_MAX; + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { - if (copy_from_user(&abs, p, sizeof(struct input_absinfo))) - return -EFAULT; + int t = _IOC_NR(cmd) & ABS_MAX; - dev->abs[t] = abs.value; - dev->absmin[t] = abs.minimum; - dev->absmax[t] = abs.maximum; - dev->absfuzz[t] = abs.fuzz; - dev->absflat[t] = abs.flat; + if (copy_from_user(&abs, p, sizeof(struct input_absinfo))) + return -EFAULT; - return 0; + dev->abs[t] = abs.value; + dev->absmin[t] = abs.minimum; + dev->absmax[t] = abs.maximum; + dev->absfuzz[t] = abs.fuzz; + dev->absflat[t] = abs.flat; + + return 0; + } } } return -EINVAL; } +#endif static struct file_operations evdev_fops = { .owner = THIS_MODULE, @@ -396,7 +640,10 @@ static struct file_operations evdev_fops = { .poll = evdev_poll, .open = evdev_open, .release = evdev_release, - .ioctl = evdev_ioctl, + .unlocked_ioctl = evdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = evdev_ioctl_compat, +#endif .fasync = evdev_fasync, .flush = evdev_flush }; diff --git a/drivers/input/gameport/Kconfig b/drivers/input/gameport/Kconfig index 1d93f509290..7524bd7d8b8 100644 --- a/drivers/input/gameport/Kconfig +++ b/drivers/input/gameport/Kconfig @@ -49,22 +49,8 @@ config GAMEPORT_EMU10K1 To compile this driver as a module, choose M here: the module will be called emu10k1-gp. -config GAMEPORT_VORTEX - tristate "Aureal Vortex, Vortex 2 gameport support" - depends on PCI - help - Say Y here if you have an Aureal Vortex 1 or 2 card and want - to use its gameport. - - To compile this driver as a module, choose M here: the - module will be called vortex. - config GAMEPORT_FM801 tristate "ForteMedia FM801 gameport support" depends on PCI -config GAMEPORT_CS461X - tristate "Crystal SoundFusion gameport support" - depends on PCI - endif diff --git a/drivers/input/gameport/Makefile b/drivers/input/gameport/Makefile index 5367b4267ad..b6f6097bd8c 100644 --- a/drivers/input/gameport/Makefile +++ b/drivers/input/gameport/Makefile @@ -5,9 +5,7 @@ # Each configuration option enables a list of files. obj-$(CONFIG_GAMEPORT) += gameport.o -obj-$(CONFIG_GAMEPORT_CS461X) += cs461x.o obj-$(CONFIG_GAMEPORT_EMU10K1) += emu10k1-gp.o obj-$(CONFIG_GAMEPORT_FM801) += fm801-gp.o obj-$(CONFIG_GAMEPORT_L4) += lightning.o obj-$(CONFIG_GAMEPORT_NS558) += ns558.o -obj-$(CONFIG_GAMEPORT_VORTEX) += vortex.o diff --git a/drivers/input/gameport/cs461x.c b/drivers/input/gameport/cs461x.c deleted file mode 100644 index d4013ff9862..00000000000 --- a/drivers/input/gameport/cs461x.c +++ /dev/null @@ -1,322 +0,0 @@ -/* - The all defines and part of code (such as cs461x_*) are - contributed from ALSA 0.5.8 sources. - See http://www.alsa-project.org/ for sources - - Tested on Linux 686 2.4.0-test9, ALSA 0.5.8a and CS4610 -*/ - -#include <asm/io.h> - -#include <linux/module.h> -#include <linux/ioport.h> -#include <linux/config.h> -#include <linux/init.h> -#include <linux/gameport.h> -#include <linux/slab.h> -#include <linux/pci.h> - -MODULE_AUTHOR("Victor Krapivin"); -MODULE_LICENSE("GPL"); - -/* - These options are experimental - -#define CS461X_FULL_MAP -*/ - - -#ifndef PCI_VENDOR_ID_CIRRUS -#define PCI_VENDOR_ID_CIRRUS 0x1013 -#endif -#ifndef PCI_DEVICE_ID_CIRRUS_4610 -#define PCI_DEVICE_ID_CIRRUS_4610 0x6001 -#endif -#ifndef PCI_DEVICE_ID_CIRRUS_4612 -#define PCI_DEVICE_ID_CIRRUS_4612 0x6003 -#endif -#ifndef PCI_DEVICE_ID_CIRRUS_4615 -#define PCI_DEVICE_ID_CIRRUS_4615 0x6004 -#endif - -/* Registers */ - -#define BA0_JSPT 0x00000480 -#define BA0_JSCTL 0x00000484 -#define BA0_JSC1 0x00000488 -#define BA0_JSC2 0x0000048C -#define BA0_JSIO 0x000004A0 - -/* Bits for JSPT */ - -#define JSPT_CAX 0x00000001 -#define JSPT_CAY 0x00000002 -#define JSPT_CBX 0x00000004 -#define JSPT_CBY 0x00000008 -#define JSPT_BA1 0x00000010 -#define JSPT_BA2 0x00000020 -#define JSPT_BB1 0x00000040 -#define JSPT_BB2 0x00000080 - -/* Bits for JSCTL */ - -#define JSCTL_SP_MASK 0x00000003 -#define JSCTL_SP_SLOW 0x00000000 -#define JSCTL_SP_MEDIUM_SLOW 0x00000001 -#define JSCTL_SP_MEDIUM_FAST 0x00000002 -#define JSCTL_SP_FAST 0x00000003 -#define JSCTL_ARE 0x00000004 - -/* Data register pairs masks */ - -#define JSC1_Y1V_MASK 0x0000FFFF -#define JSC1_X1V_MASK 0xFFFF0000 -#define JSC1_Y1V_SHIFT 0 -#define JSC1_X1V_SHIFT 16 -#define JSC2_Y2V_MASK 0x0000FFFF -#define JSC2_X2V_MASK 0xFFFF0000 -#define JSC2_Y2V_SHIFT 0 -#define JSC2_X2V_SHIFT 16 - -/* JS GPIO */ - -#define JSIO_DAX 0x00000001 -#define JSIO_DAY 0x00000002 -#define JSIO_DBX 0x00000004 -#define JSIO_DBY 0x00000008 -#define JSIO_AXOE 0x00000010 -#define JSIO_AYOE 0x00000020 -#define JSIO_BXOE 0x00000040 -#define JSIO_BYOE 0x00000080 - -/* - The card initialization code is obfuscated; the module cs461x - need to be loaded after ALSA modules initialized and something - played on the CS 4610 chip (see sources for details of CS4610 - initialization code from ALSA) -*/ - -/* Card specific definitions */ - -#define CS461X_BA0_SIZE 0x2000 -#define CS461X_BA1_DATA0_SIZE 0x3000 -#define CS461X_BA1_DATA1_SIZE 0x3800 -#define CS461X_BA1_PRG_SIZE 0x7000 -#define CS461X_BA1_REG_SIZE 0x0100 - -#define BA1_SP_DMEM0 0x00000000 -#define BA1_SP_DMEM1 0x00010000 -#define BA1_SP_PMEM 0x00020000 -#define BA1_SP_REG 0x00030000 - -#define BA1_DWORD_SIZE (13 * 1024 + 512) -#define BA1_MEMORY_COUNT 3 - -/* - Only one CS461x card is still suppoted; the code requires - redesign to avoid this limitatuion. -*/ - -static unsigned long ba0_addr; -static unsigned int __iomem *ba0; - -#ifdef CS461X_FULL_MAP -static unsigned long ba1_addr; -static union ba1_t { - struct { - unsigned int __iomem *data0; - unsigned int __iomem *data1; - unsigned int __iomem *pmem; - unsigned int __iomem *reg; - } name; - unsigned int __iomem *idx[4]; -} ba1; - -static void cs461x_poke(unsigned long reg, unsigned int val) -{ - writel(val, &ba1.idx[(reg >> 16) & 3][(reg >> 2) & 0x3fff]); -} - -static unsigned int cs461x_peek(unsigned long reg) -{ - return readl(&ba1.idx[(reg >> 16) & 3][(reg >> 2) & 0x3fff]); -} - -#endif - -static void cs461x_pokeBA0(unsigned long reg, unsigned int val) -{ - writel(val, &ba0[reg >> 2]); -} - -static unsigned int cs461x_peekBA0(unsigned long reg) -{ - return readl(&ba0[reg >> 2]); -} - -static int cs461x_free(struct pci_dev *pdev) -{ - struct gameport *port = pci_get_drvdata(pdev); - - if (port) - gameport_unregister_port(port); - - if (ba0) iounmap(ba0); -#ifdef CS461X_FULL_MAP - if (ba1.name.data0) iounmap(ba1.name.data0); - if (ba1.name.data1) iounmap(ba1.name.data1); - if (ba1.name.pmem) iounmap(ba1.name.pmem); - if (ba1.name.reg) iounmap(ba1.name.reg); -#endif - return 0; -} - -static void cs461x_gameport_trigger(struct gameport *gameport) -{ - cs461x_pokeBA0(BA0_JSPT, 0xFF); //outb(gameport->io, 0xFF); -} - -static unsigned char cs461x_gameport_read(struct gameport *gameport) -{ - return cs461x_peekBA0(BA0_JSPT); //inb(gameport->io); -} - -static int cs461x_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons) -{ - unsigned js1, js2, jst; - - js1 = cs461x_peekBA0(BA0_JSC1); - js2 = cs461x_peekBA0(BA0_JSC2); - jst = cs461x_peekBA0(BA0_JSPT); - - *buttons = (~jst >> 4) & 0x0F; - - axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF; - axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF; - axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF; - axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF; - - for(jst=0;jst<4;++jst) - if(axes[jst]==0xFFFF) axes[jst] = -1; - return 0; -} - -static int cs461x_gameport_open(struct gameport *gameport, int mode) -{ - switch (mode) { - case GAMEPORT_MODE_COOKED: - case GAMEPORT_MODE_RAW: - return 0; - default: - return -1; - } - return 0; -} - -static struct pci_device_id cs461x_pci_tbl[] = { - { PCI_VENDOR_ID_CIRRUS, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4610 */ - { PCI_VENDOR_ID_CIRRUS, 0x6003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4612 */ - { PCI_VENDOR_ID_CIRRUS, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4615 */ - { 0, } -}; -MODULE_DEVICE_TABLE(pci, cs461x_pci_tbl); - -static int __devinit cs461x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int rc; - struct gameport* port; - - rc = pci_enable_device(pdev); - if (rc) { - printk(KERN_ERR "cs461x: Cannot enable PCI gameport (bus %d, devfn %d) error=%d\n", - pdev->bus->number, pdev->devfn, rc); - return rc; - } - - ba0_addr = pci_resource_start(pdev, 0); -#ifdef CS461X_FULL_MAP - ba1_addr = pci_resource_start(pdev, 1); -#endif - if (ba0_addr == 0 || ba0_addr == ~0 -#ifdef CS461X_FULL_MAP - || ba1_addr == 0 || ba1_addr == ~0 -#endif - ) { - printk(KERN_ERR "cs461x: wrong address - ba0 = 0x%lx\n", ba0_addr); -#ifdef CS461X_FULL_MAP - printk(KERN_ERR "cs461x: wrong address - ba1 = 0x%lx\n", ba1_addr); -#endif - cs461x_free(pdev); - return -ENOMEM; - } - - ba0 = ioremap(ba0_addr, CS461X_BA0_SIZE); -#ifdef CS461X_FULL_MAP - ba1.name.data0 = ioremap(ba1_addr + BA1_SP_DMEM0, CS461X_BA1_DATA0_SIZE); - ba1.name.data1 = ioremap(ba1_addr + BA1_SP_DMEM1, CS461X_BA1_DATA1_SIZE); - ba1.name.pmem = ioremap(ba1_addr + BA1_SP_PMEM, CS461X_BA1_PRG_SIZE); - ba1.name.reg = ioremap(ba1_addr + BA1_SP_REG, CS461X_BA1_REG_SIZE); - - if (ba0 == NULL || ba1.name.data0 == NULL || - ba1.name.data1 == NULL || ba1.name.pmem == NULL || - ba1.name.reg == NULL) { - cs461x_free(pdev); - return -ENOMEM; - } -#else - if (ba0 == NULL) { - cs461x_free(pdev); - return -ENOMEM; - } -#endif - - if (!(port = gameport_allocate_port())) { - printk(KERN_ERR "cs461x: Memory allocation failed\n"); - cs461x_free(pdev); - return -ENOMEM; - } - - pci_set_drvdata(pdev, port); - - port->open = cs461x_gameport_open; - port->trigger = cs461x_gameport_trigger; - port->read = cs461x_gameport_read; - port->cooked_read = cs461x_gameport_cooked_read; - - gameport_set_name(port, "CS416x"); - gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev)); - port->dev.parent = &pdev->dev; - - cs461x_pokeBA0(BA0_JSIO, 0xFF); // ? - cs461x_pokeBA0(BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW); - - gameport_register_port(port); - - return 0; -} - -static void __devexit cs461x_pci_remove(struct pci_dev *pdev) -{ - cs461x_free(pdev); -} - -static struct pci_driver cs461x_pci_driver = { - .name = "CS461x_gameport", - .id_table = cs461x_pci_tbl, - .probe = cs461x_pci_probe, - .remove = __devexit_p(cs461x_pci_remove), -}; - -static int __init cs461x_init(void) -{ - return pci_register_driver(&cs461x_pci_driver); -} - -static void __exit cs461x_exit(void) -{ - pci_unregister_driver(&cs461x_pci_driver); -} - -module_init(cs461x_init); -module_exit(cs461x_exit); - diff --git a/drivers/input/gameport/ns558.c b/drivers/input/gameport/ns558.c index 7c5c6318eeb..1ab5f2dc8a2 100644 --- a/drivers/input/gameport/ns558.c +++ b/drivers/input/gameport/ns558.c @@ -258,18 +258,18 @@ static int __init ns558_init(void) { int i = 0; + if (pnp_register_driver(&ns558_pnp_driver) >= 0) + pnp_registered = 1; + /* - * Probe ISA ports first so that PnP gets to choose free port addresses - * not occupied by the ISA ports. + * Probe ISA ports after PnP, so that PnP ports that are already + * enabled get detected as PnP. This may be suboptimal in multi-device + * configurations, but saves hassle with simple setups. */ while (ns558_isa_portlist[i]) ns558_isa_probe(ns558_isa_portlist[i++]); - if (pnp_register_driver(&ns558_pnp_driver) >= 0) - pnp_registered = 1; - - return (list_empty(&ns558_list) && !pnp_registered) ? -ENODEV : 0; } diff --git a/drivers/input/gameport/vortex.c b/drivers/input/gameport/vortex.c deleted file mode 100644 index 36b0309c8bf..00000000000 --- a/drivers/input/gameport/vortex.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * $Id: vortex.c,v 1.5 2002/07/01 15:39:30 vojtech Exp $ - * - * Copyright (c) 2000-2001 Vojtech Pavlik - * - * Based on the work of: - * Raymond Ingles - */ - -/* - * Trident 4DWave and Aureal Vortex gameport driver for Linux - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/gameport.h> - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); -MODULE_DESCRIPTION("Aureal Vortex and Vortex2 gameport driver"); -MODULE_LICENSE("GPL"); - -#define VORTEX_GCR 0x0c /* Gameport control register */ -#define VORTEX_LEG 0x08 /* Legacy port location */ -#define VORTEX_AXD 0x10 /* Axes start */ -#define VORTEX_DATA_WAIT 20 /* 20 ms */ - -struct vortex { - struct gameport *gameport; - struct pci_dev *dev; - unsigned char __iomem *base; - unsigned char __iomem *io; -}; - -static unsigned char vortex_read(struct gameport *gameport) -{ - struct vortex *vortex = gameport->port_data; - return readb(vortex->io + VORTEX_LEG); -} - -static void vortex_trigger(struct gameport *gameport) -{ - struct vortex *vortex = gameport->port_data; - writeb(0xff, vortex->io + VORTEX_LEG); -} - -static int vortex_cooked_read(struct gameport *gameport, int *axes, int *buttons) -{ - struct vortex *vortex = gameport->port_data; - int i; - - *buttons = (~readb(vortex->base + VORTEX_LEG) >> 4) & 0xf; - - for (i = 0; i < 4; i++) { - axes[i] = readw(vortex->io + VORTEX_AXD + i * sizeof(u32)); - if (axes[i] == 0x1fff) axes[i] = -1; - } - - return 0; -} - -static int vortex_open(struct gameport *gameport, int mode) -{ - struct vortex *vortex = gameport->port_data; - - switch (mode) { - case GAMEPORT_MODE_COOKED: - writeb(0x40, vortex->io + VORTEX_GCR); - msleep(VORTEX_DATA_WAIT); - return 0; - case GAMEPORT_MODE_RAW: - writeb(0x00, vortex->io + VORTEX_GCR); - return 0; - default: - return -1; - } - - return 0; -} - -static int __devinit vortex_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - struct vortex *vortex; - struct gameport *port; - int i; - - vortex = kcalloc(1, sizeof(struct vortex), GFP_KERNEL); - port = gameport_allocate_port(); - if (!vortex || !port) { - printk(KERN_ERR "vortex: Memory allocation failed.\n"); - kfree(vortex); - gameport_free_port(port); - return -ENOMEM; - } - - for (i = 0; i < 6; i++) - if (~pci_resource_flags(dev, i) & IORESOURCE_IO) - break; - - pci_enable_device(dev); - - vortex->dev = dev; - vortex->gameport = port; - vortex->base = ioremap(pci_resource_start(vortex->dev, i), - pci_resource_len(vortex->dev, i)); - vortex->io = vortex->base + id->driver_data; - - pci_set_drvdata(dev, vortex); - - port->port_data = vortex; - port->fuzz = 64; - - gameport_set_name(port, "AU88x0"); - gameport_set_phys(port, "pci%s/gameport0", pci_name(dev)); - port->dev.parent = &dev->dev; - port->read = vortex_read; - port->trigger = vortex_trigger; - port->cooked_read = vortex_cooked_read; - port->open = vortex_open; - - gameport_register_port(port); - - return 0; -} - -static void __devexit vortex_remove(struct pci_dev *dev) -{ - struct vortex *vortex = pci_get_drvdata(dev); - - gameport_unregister_port(vortex->gameport); - iounmap(vortex->base); - kfree(vortex); -} - -static struct pci_device_id vortex_id_table[] = { - { 0x12eb, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x11000 }, - { 0x12eb, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x28800 }, - { 0 } -}; - -static struct pci_driver vortex_driver = { - .name = "vortex_gameport", - .id_table = vortex_id_table, - .probe = vortex_probe, - .remove = __devexit_p(vortex_remove), -}; - -static int __init vortex_init(void) -{ - return pci_register_driver(&vortex_driver); -} - -static void __exit vortex_exit(void) -{ - pci_unregister_driver(&vortex_driver); -} - -module_init(vortex_init); -module_exit(vortex_exit); diff --git a/drivers/input/input.c b/drivers/input/input.c index 83c77c990dd..7c4b4d37b3e 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -219,10 +219,24 @@ void input_release_device(struct input_handle *handle) int input_open_device(struct input_handle *handle) { + struct input_dev *dev = handle->dev; + int err; + + err = down_interruptible(&dev->sem); + if (err) + return err; + handle->open++; - if (handle->dev->open) - return handle->dev->open(handle->dev); - return 0; + + if (!dev->users++ && dev->open) + err = dev->open(dev); + + if (err) + handle->open--; + + up(&dev->sem); + + return err; } int input_flush_device(struct input_handle* handle, struct file* file) @@ -235,10 +249,17 @@ int input_flush_device(struct input_handle* handle, struct file* file) void input_close_device(struct input_handle *handle) { + struct input_dev *dev = handle->dev; + input_release_device(handle); - if (handle->dev->close) - handle->dev->close(handle->dev); + + down(&dev->sem); + + if (!--dev->users && dev->close) + dev->close(dev); handle->open--; + + up(&dev->sem); } static void input_link_handle(struct input_handle *handle) @@ -415,6 +436,8 @@ void input_register_device(struct input_dev *dev) set_bit(EV_SYN, dev->evbit); + init_MUTEX(&dev->sem); + /* * If delay and period are pre-set by the driver, then autorepeating * is handled by the driver itself and we don't do it in input.c. @@ -674,6 +697,8 @@ static int input_handlers_read(char *buf, char **start, off_t pos, int count, in return (count > cnt) ? cnt : count; } +static struct file_operations input_fileops; + static int __init input_proc_init(void) { struct proc_dir_entry *entry; @@ -688,6 +713,8 @@ static int __init input_proc_init(void) return -ENOMEM; } entry->owner = THIS_MODULE; + input_fileops = *entry->proc_fops; + entry->proc_fops = &input_fileops; entry->proc_fops->poll = input_devices_poll; entry = create_proc_read_entry("handlers", 0, proc_bus_input_dir, input_handlers_read, NULL); if (entry == NULL) { diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 39775fc380c..ff8e1bbd0e1 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -285,48 +285,33 @@ static unsigned int joydev_poll(struct file *file, poll_table *wait) (POLLIN | POLLRDNORM) : 0) | (list->joydev->exist ? 0 : (POLLHUP | POLLERR)); } -static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __user *argp) { - struct joydev_list *list = file->private_data; - struct joydev *joydev = list->joydev; struct input_dev *dev = joydev->handle.dev; - void __user *argp = (void __user *)arg; int i, j; - if (!joydev->exist) return -ENODEV; - switch (cmd) { case JS_SET_CAL: return copy_from_user(&joydev->glue.JS_CORR, argp, - sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; + sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; case JS_GET_CAL: return copy_to_user(argp, &joydev->glue.JS_CORR, - sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; + sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; case JS_SET_TIMEOUT: - return get_user(joydev->glue.JS_TIMEOUT, (int __user *) arg); + return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); case JS_GET_TIMEOUT: - return put_user(joydev->glue.JS_TIMEOUT, (int __user *) arg); - case JS_SET_TIMELIMIT: - return get_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg); - case JS_GET_TIMELIMIT: - return put_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg); - case JS_SET_ALL: - return copy_from_user(&joydev->glue, argp, - sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; - case JS_GET_ALL: - return copy_to_user(argp, &joydev->glue, - sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; + return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); case JSIOCGVERSION: - return put_user(JS_VERSION, (__u32 __user *) arg); + return put_user(JS_VERSION, (__u32 __user *) argp); case JSIOCGAXES: - return put_user(joydev->nabs, (__u8 __user *) arg); + return put_user(joydev->nabs, (__u8 __user *) argp); case JSIOCGBUTTONS: - return put_user(joydev->nkey, (__u8 __user *) arg); + return put_user(joydev->nkey, (__u8 __user *) argp); case JSIOCSCORR: if (copy_from_user(joydev->corr, argp, - sizeof(struct js_corr) * joydev->nabs)) + sizeof(joydev->corr[0]) * joydev->nabs)) return -EFAULT; for (i = 0; i < joydev->nabs; i++) { j = joydev->abspam[i]; @@ -335,7 +320,7 @@ static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd return 0; case JSIOCGCORR: return copy_to_user(argp, joydev->corr, - sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0; + sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0; case JSIOCSAXMAP: if (copy_from_user(joydev->abspam, argp, sizeof(__u8) * (ABS_MAX + 1))) return -EFAULT; @@ -371,6 +356,84 @@ static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd return -EINVAL; } +#ifdef CONFIG_COMPAT +static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct joydev_list *list = file->private_data; + struct joydev *joydev = list->joydev; + void __user *argp = (void __user *)arg; + s32 tmp32; + struct JS_DATA_SAVE_TYPE_32 ds32; + int err; + + if (!joydev->exist) return -ENODEV; + switch(cmd) { + case JS_SET_TIMELIMIT: + err = get_user(tmp32, (s32 __user *) arg); + if (err == 0) + joydev->glue.JS_TIMELIMIT = tmp32; + break; + case JS_GET_TIMELIMIT: + tmp32 = joydev->glue.JS_TIMELIMIT; + err = put_user(tmp32, (s32 __user *) arg); + break; + + case JS_SET_ALL: + err = copy_from_user(&ds32, argp, + sizeof(ds32)) ? -EFAULT : 0; + if (err == 0) { + joydev->glue.JS_TIMEOUT = ds32.JS_TIMEOUT; + joydev->glue.BUSY = ds32.BUSY; + joydev->glue.JS_EXPIRETIME = ds32.JS_EXPIRETIME; + joydev->glue.JS_TIMELIMIT = ds32.JS_TIMELIMIT; + joydev->glue.JS_SAVE = ds32.JS_SAVE; + joydev->glue.JS_CORR = ds32.JS_CORR; + } + break; + + case JS_GET_ALL: + ds32.JS_TIMEOUT = joydev->glue.JS_TIMEOUT; + ds32.BUSY = joydev->glue.BUSY; + ds32.JS_EXPIRETIME = joydev->glue.JS_EXPIRETIME; + ds32.JS_TIMELIMIT = joydev->glue.JS_TIMELIMIT; + ds32.JS_SAVE = joydev->glue.JS_SAVE; + ds32.JS_CORR = joydev->glue.JS_CORR; + + err = copy_to_user(argp, &ds32, + sizeof(ds32)) ? -EFAULT : 0; + break; + + default: + err = joydev_ioctl_common(joydev, cmd, argp); + } + return err; +} +#endif /* CONFIG_COMPAT */ + +static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct joydev_list *list = file->private_data; + struct joydev *joydev = list->joydev; + void __user *argp = (void __user *)arg; + + if (!joydev->exist) return -ENODEV; + + switch(cmd) { + case JS_SET_TIMELIMIT: + return get_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg); + case JS_GET_TIMELIMIT: + return put_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg); + case JS_SET_ALL: + return copy_from_user(&joydev->glue, argp, + sizeof(joydev->glue)) ? -EFAULT : 0; + case JS_GET_ALL: + return copy_to_user(argp, &joydev->glue, + sizeof(joydev->glue)) ? -EFAULT : 0; + default: + return joydev_ioctl_common(joydev, cmd, argp); + } +} + static struct file_operations joydev_fops = { .owner = THIS_MODULE, .read = joydev_read, @@ -379,6 +442,9 @@ static struct file_operations joydev_fops = { .open = joydev_open, .release = joydev_release, .ioctl = joydev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = joydev_compat_ioctl, +#endif .fasync = joydev_fasync, }; diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c index ad39fe4bf35..bf34f75b946 100644 --- a/drivers/input/joystick/a3d.c +++ b/drivers/input/joystick/a3d.c @@ -185,7 +185,7 @@ static void a3d_poll(struct gameport *gameport) a3d->reads++; if (a3d_read_packet(a3d->gameport, a3d->length, data) != a3d->length || data[0] != a3d->mode || a3d_csum(data, a3d->length)) - a3d->bads++; + a3d->bads++; else a3d_read(a3d, data); } diff --git a/drivers/input/joystick/adi.c b/drivers/input/joystick/adi.c index 83f6dafc171..265962956c6 100644 --- a/drivers/input/joystick/adi.c +++ b/drivers/input/joystick/adi.c @@ -82,7 +82,7 @@ static char adi_cm2_abs[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ }; static char adi_wmf_abs[] = { ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y }; static short adi_wmgpe_key[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT }; -static short adi_wmi_key[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_EXTRA }; +static short adi_wmi_key[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_EXTRA }; static short adi_wmed3d_key[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2 }; static short adi_cm2_key[] = { BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 }; @@ -183,7 +183,7 @@ static void adi_move_bits(struct adi_port *port, int length) int i; struct adi *adi = port->adi; - adi[0].idx = adi[1].idx = 0; + adi[0].idx = adi[1].idx = 0; if (adi[0].ret <= 0 || adi[1].ret <= 0) return; if (adi[0].data[0] & 0x20 || ~adi[1].data[0] & 0x20) return; diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c index cf36ca9b92f..033456bb9fe 100644 --- a/drivers/input/joystick/amijoy.c +++ b/drivers/input/joystick/amijoy.c @@ -51,7 +51,8 @@ MODULE_PARM_DESC(map, "Map of attached joysticks in form of <a>,<b> (default is __obsolete_setup("amijoy="); -static int amijoy_used[2] = { 0, 0 }; +static int amijoy_used; +static DECLARE_MUTEX(amijoy_sem); static struct input_dev amijoy_dev[2]; static char *amijoy_phys[2] = { "amijoy/input0", "amijoy/input1" }; @@ -84,26 +85,30 @@ static irqreturn_t amijoy_interrupt(int irq, void *dummy, struct pt_regs *fp) static int amijoy_open(struct input_dev *dev) { - int *used = dev->private; + int err; - if ((*used)++) - return 0; + err = down_interruptible(&amijoy_sem); + if (err) + return err; - if (request_irq(IRQ_AMIGA_VERTB, amijoy_interrupt, 0, "amijoy", amijoy_interrupt)) { - (*used)--; + if (!amijoy_used && request_irq(IRQ_AMIGA_VERTB, amijoy_interrupt, 0, "amijoy", amijoy_interrupt)) { printk(KERN_ERR "amijoy.c: Can't allocate irq %d\n", IRQ_AMIGA_VERTB); - return -EBUSY; + err = -EBUSY; + goto out; } - return 0; + amijoy_used++; +out: + up(&amijoy_sem); + return err; } static void amijoy_close(struct input_dev *dev) { - int *used = dev->private; - - if (!--(*used)) + down(&amijoysem); + if (!--amijoy_used) free_irq(IRQ_AMIGA_VERTB, amijoy_interrupt); + up(&amijoy_sem); } static int __init amijoy_init(void) @@ -138,8 +143,6 @@ static int __init amijoy_init(void) amijoy_dev[i].id.product = 0x0003; amijoy_dev[i].id.version = 0x0100; - amijoy_dev[i].private = amijoy_used + i; - input_register_device(amijoy_dev + i); printk(KERN_INFO "input: %s at joy%ddat\n", amijoy_name, i); } diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c index cfdd3acf06a..fbd3eed07f9 100644 --- a/drivers/input/joystick/db9.c +++ b/drivers/input/joystick/db9.c @@ -87,7 +87,7 @@ __obsolete_setup("db9_3="); #define DB9_NORMAL 0x0a #define DB9_NOSELECT 0x08 -#define DB9_MAX_DEVICES 2 +#define DB9_MAX_DEVICES 2 #define DB9_GENESIS6_DELAY 14 #define DB9_REFRESH_TIME HZ/100 @@ -98,6 +98,7 @@ struct db9 { struct pardevice *pd; int mode; int used; + struct semaphore sem; char phys[2][32]; }; @@ -503,6 +504,11 @@ static int db9_open(struct input_dev *dev) { struct db9 *db9 = dev->private; struct parport *port = db9->pd->port; + int err; + + err = down_interruptible(&db9->sem); + if (err) + return err; if (!db9->used++) { parport_claim(db9->pd); @@ -514,6 +520,7 @@ static int db9_open(struct input_dev *dev) mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME); } + up(&db9->sem); return 0; } @@ -522,12 +529,14 @@ static void db9_close(struct input_dev *dev) struct db9 *db9 = dev->private; struct parport *port = db9->pd->port; + down(&db9->sem); if (!--db9->used) { - del_timer(&db9->timer); + del_timer_sync(&db9->timer); parport_write_control(port, 0x00); parport_data_forward(port); parport_release(db9->pd); } + up(&db9->sem); } static struct db9 __init *db9_probe(int *config, int nargs) @@ -563,12 +572,12 @@ static struct db9 __init *db9_probe(int *config, int nargs) } } - if (!(db9 = kmalloc(sizeof(struct db9), GFP_KERNEL))) { + if (!(db9 = kcalloc(1, sizeof(struct db9), GFP_KERNEL))) { parport_put_port(pp); return NULL; } - memset(db9, 0, sizeof(struct db9)); + init_MUTEX(&db9->sem); db9->mode = config[1]; init_timer(&db9->timer); db9->timer.data = (long) db9; diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c index 8732f52bdd0..95bbdd302aa 100644 --- a/drivers/input/joystick/gamecon.c +++ b/drivers/input/joystick/gamecon.c @@ -1,12 +1,12 @@ /* * NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux * - * Copyright (c) 1999-2004 Vojtech Pavlik <vojtech@suse.cz> - * Copyright (c) 2004 Peter Nelson <rufus-kernel@hackish.org> + * Copyright (c) 1999-2004 Vojtech Pavlik <vojtech@suse.cz> + * Copyright (c) 2004 Peter Nelson <rufus-kernel@hackish.org> * * Based on the work of: - * Andree Borrmann John Dahlstrom - * David Kuder Nathan Hand + * Andree Borrmann John Dahlstrom + * David Kuder Nathan Hand */ /* @@ -81,6 +81,7 @@ struct gc { struct timer_list timer; unsigned char pads[GC_MAX + 1]; int used; + struct semaphore sem; char phys[5][32]; }; @@ -433,7 +434,7 @@ static void gc_timer(unsigned long private) gc_psx_read_packet(gc, data_psx, data); for (i = 0; i < 5; i++) { - switch (data[i]) { + switch (data[i]) { case GC_PSX_RUMBLE: @@ -503,22 +504,33 @@ static void gc_timer(unsigned long private) static int gc_open(struct input_dev *dev) { struct gc *gc = dev->private; + int err; + + err = down_interruptible(&gc->sem); + if (err) + return err; + if (!gc->used++) { parport_claim(gc->pd); parport_write_control(gc->pd->port, 0x04); mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME); } + + up(&gc->sem); return 0; } static void gc_close(struct input_dev *dev) { struct gc *gc = dev->private; + + down(&gc->sem); if (!--gc->used) { - del_timer(&gc->timer); + del_timer_sync(&gc->timer); parport_write_control(gc->pd->port, 0x00); parport_release(gc->pd); } + up(&gc->sem); } static struct gc __init *gc_probe(int *config, int nargs) @@ -542,11 +554,12 @@ static struct gc __init *gc_probe(int *config, int nargs) return NULL; } - if (!(gc = kmalloc(sizeof(struct gc), GFP_KERNEL))) { + if (!(gc = kcalloc(1, sizeof(struct gc), GFP_KERNEL))) { parport_put_port(pp); return NULL; } - memset(gc, 0, sizeof(struct gc)); + + init_MUTEX(&gc->sem); gc->pd = parport_register_device(pp, "gamecon", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c index ad13f09a4e7..7d969420066 100644 --- a/drivers/input/joystick/gf2k.c +++ b/drivers/input/joystick/gf2k.c @@ -329,7 +329,7 @@ static int gf2k_connect(struct gameport *gameport, struct gameport_driver *drv) for (i = 0; i < gf2k_axes[gf2k->id]; i++) { gf2k->dev.absmax[gf2k_abs[i]] = (i < 2) ? gf2k->dev.abs[gf2k_abs[i]] * 2 - 32 : - gf2k->dev.abs[gf2k_abs[0]] + gf2k->dev.abs[gf2k_abs[1]] - 32; + gf2k->dev.abs[gf2k_abs[0]] + gf2k->dev.abs[gf2k_abs[1]] - 32; gf2k->dev.absmin[gf2k_abs[i]] = 32; gf2k->dev.absfuzz[gf2k_abs[i]] = 8; gf2k->dev.absflat[gf2k_abs[i]] = (i < 2) ? 24 : 0; diff --git a/drivers/input/joystick/grip_mp.c b/drivers/input/joystick/grip_mp.c index 42e5005d621..0da7bd133cc 100644 --- a/drivers/input/joystick/grip_mp.c +++ b/drivers/input/joystick/grip_mp.c @@ -171,7 +171,7 @@ static int mp_io(struct gameport* gameport, int sendflags, int sendcode, u32 *pa *packet = 0; raw_data = gameport_read(gameport); if (raw_data & 1) - return IO_RETRY; + return IO_RETRY; for (i = 0; i < 64; i++) { raw_data = gameport_read(gameport); diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c index 028f3513629..e31b7b93fde 100644 --- a/drivers/input/joystick/iforce/iforce-main.c +++ b/drivers/input/joystick/iforce/iforce-main.c @@ -78,6 +78,7 @@ static struct iforce_device iforce_device[] = { { 0x061c, 0xc0a4, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, //? { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce }, //? { 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //? + { 0x06f8, 0x0004, "Gullemot Jet Leader 3D", btn_joystick, abs_joystick, ff_iforce }, //? { 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]", btn_joystick, abs_joystick, ff_iforce } }; diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index 617c0b0e5a3..6369a24684f 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -229,6 +229,7 @@ static struct usb_device_id iforce_usb_ids [] = { { USB_DEVICE(0x061c, 0xc0a4) }, /* ACT LABS Force RS */ { USB_DEVICE(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */ { USB_DEVICE(0x06f8, 0x0004) }, /* Guillemot Force Feedback Racing Wheel */ + { USB_DEVICE(0x06f8, 0xa302) }, /* Guillemot Jet Leader 3D */ { } /* Terminating entry */ }; diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c index ec0a2a64d49..a436f222085 100644 --- a/drivers/input/joystick/spaceball.c +++ b/drivers/input/joystick/spaceball.c @@ -4,8 +4,8 @@ * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: - * David Thompson - * Joseph Krahn + * David Thompson + * Joseph Krahn */ /* diff --git a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c index 874367bfab0..01fd2e4791a 100644 --- a/drivers/input/joystick/spaceorb.c +++ b/drivers/input/joystick/spaceorb.c @@ -4,7 +4,7 @@ * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: - * David Thompson + * David Thompson */ /* diff --git a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c index aaee52ceb92..9eb9954cac6 100644 --- a/drivers/input/joystick/tmdc.c +++ b/drivers/input/joystick/tmdc.c @@ -79,7 +79,7 @@ static short tmdc_btn_pad[TMDC_BTN] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_START, BTN_SELECT, BTN_TL, BTN_TR }; static short tmdc_btn_joy[TMDC_BTN] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_THUMB2, BTN_PINKIE, - BTN_BASE3, BTN_BASE4, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z }; + BTN_BASE3, BTN_BASE4, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z }; static short tmdc_btn_fm[TMDC_BTN] = { BTN_TRIGGER, BTN_C, BTN_B, BTN_A, BTN_THUMB, BTN_X, BTN_Y, BTN_Z, BTN_TOP, BTN_TOP2 }; static short tmdc_btn_at[TMDC_BTN] = diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c index dd88b9cb49f..28100d461cb 100644 --- a/drivers/input/joystick/turbografx.c +++ b/drivers/input/joystick/turbografx.c @@ -84,6 +84,7 @@ static struct tgfx { char phys[7][32]; int sticks; int used; + struct semaphore sem; } *tgfx_base[3]; /* @@ -99,7 +100,7 @@ static void tgfx_timer(unsigned long private) for (i = 0; i < 7; i++) if (tgfx->sticks & (1 << i)) { - dev = tgfx->dev + i; + dev = tgfx->dev + i; parport_write_data(tgfx->pd->port, ~(1 << i)); data1 = parport_read_status(tgfx->pd->port) ^ 0x7f; @@ -122,23 +123,34 @@ static void tgfx_timer(unsigned long private) static int tgfx_open(struct input_dev *dev) { - struct tgfx *tgfx = dev->private; - if (!tgfx->used++) { + struct tgfx *tgfx = dev->private; + int err; + + err = down_interruptible(&tgfx->sem); + if (err) + return err; + + if (!tgfx->used++) { parport_claim(tgfx->pd); parport_write_control(tgfx->pd->port, 0x04); - mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME); + mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME); } - return 0; + + up(&tgfx->sem); + return 0; } static void tgfx_close(struct input_dev *dev) { - struct tgfx *tgfx = dev->private; - if (!--tgfx->used) { - del_timer(&tgfx->timer); + struct tgfx *tgfx = dev->private; + + down(&tgfx->sem); + if (!--tgfx->used) { + del_timer_sync(&tgfx->timer); parport_write_control(tgfx->pd->port, 0x00); - parport_release(tgfx->pd); + parport_release(tgfx->pd); } + up(&tgfx->sem); } /* @@ -166,11 +178,12 @@ static struct tgfx __init *tgfx_probe(int *config, int nargs) return NULL; } - if (!(tgfx = kmalloc(sizeof(struct tgfx), GFP_KERNEL))) { + if (!(tgfx = kcalloc(1, sizeof(struct tgfx), GFP_KERNEL))) { parport_put_port(pp); return NULL; } - memset(tgfx, 0, sizeof(struct tgfx)); + + init_MUTEX(&tgfx->sem); tgfx->pd = parport_register_device(pp, "turbografx", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 82fad9a23ac..4d4985b59ab 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -227,7 +227,7 @@ static ssize_t atkbd_do_set_##_name(struct device *d, struct device_attribute *a { \ return atkbd_attr_set_helper(d, b, s, atkbd_set_##_name); \ } \ -static struct device_attribute atkbd_attr_##_name = \ +static struct device_attribute atkbd_attr_##_name = \ __ATTR(_name, S_IWUSR | S_IRUGO, atkbd_do_show_##_name, atkbd_do_set_##_name); ATKBD_DEFINE_ATTR(extra); @@ -388,7 +388,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, value = atkbd->release ? 0 : (1 + (!atkbd->softrepeat && test_bit(atkbd->keycode[code], atkbd->dev.key))); - switch (value) { /* Workaround Toshiba laptop multiple keypress */ + switch (value) { /* Workaround Toshiba laptop multiple keypress */ case 0: atkbd->last = 0; break; @@ -894,7 +894,7 @@ static int atkbd_reconnect(struct serio *serio) if (atkbd->write) { param[0] = (test_bit(LED_SCROLLL, atkbd->dev.led) ? 1 : 0) | (test_bit(LED_NUML, atkbd->dev.led) ? 2 : 0) - | (test_bit(LED_CAPSL, atkbd->dev.led) ? 4 : 0); + | (test_bit(LED_CAPSL, atkbd->dev.led) ? 4 : 0); if (atkbd_probe(atkbd)) return -1; diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c index 0f1220a0ceb..a8551711e8d 100644 --- a/drivers/input/keyboard/corgikbd.c +++ b/drivers/input/keyboard/corgikbd.c @@ -39,6 +39,7 @@ #define CORGI_KEY_CALENDER KEY_F1 #define CORGI_KEY_ADDRESS KEY_F2 #define CORGI_KEY_FN KEY_F3 +#define CORGI_KEY_CANCEL KEY_F4 #define CORGI_KEY_OFF KEY_SUSPEND #define CORGI_KEY_EXOK KEY_F5 #define CORGI_KEY_EXCANCEL KEY_F6 @@ -46,6 +47,7 @@ #define CORGI_KEY_EXJOGUP KEY_F8 #define CORGI_KEY_JAP1 KEY_LEFTCTRL #define CORGI_KEY_JAP2 KEY_LEFTALT +#define CORGI_KEY_MAIL KEY_F10 #define CORGI_KEY_OK KEY_F11 #define CORGI_KEY_MENU KEY_F12 #define CORGI_HINGE_0 KEY_KP0 @@ -59,8 +61,8 @@ static unsigned char corgikbd_keycode[NR_SCANCODES] = { KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0, /* 33-48 */ CORGI_KEY_CALENDER, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /* 49-64 */ CORGI_KEY_ADDRESS, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, 0, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, /* 65-80 */ - KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, 0, CORGI_KEY_FN, 0, 0, 0, 0, /* 81-96 */ - KEY_SYSRQ, CORGI_KEY_JAP1, CORGI_KEY_JAP2, KEY_CANCEL, CORGI_KEY_OK, CORGI_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0, /* 97-112 */ + CORGI_KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, 0, CORGI_KEY_FN, 0, 0, 0, 0, /* 81-96 */ + KEY_SYSRQ, CORGI_KEY_JAP1, CORGI_KEY_JAP2, CORGI_KEY_CANCEL, CORGI_KEY_OK, CORGI_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0, /* 97-112 */ CORGI_KEY_OFF, CORGI_KEY_EXOK, CORGI_KEY_EXCANCEL, CORGI_KEY_EXJOGDOWN, CORGI_KEY_EXJOGUP, 0, 0, 0, 0, 0, 0, 0, /* 113-124 */ CORGI_HINGE_0, CORGI_HINGE_1, CORGI_HINGE_2 /* 125-127 */ }; diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c index 2694ff2b5be..098963c7cdd 100644 --- a/drivers/input/keyboard/lkkbd.c +++ b/drivers/input/keyboard/lkkbd.c @@ -15,10 +15,10 @@ * information given below, I will _not_ be liable! * * RJ10 pinout: To DE9: Or DB25: - * 1 - RxD <----> Pin 3 (TxD) <-> Pin 2 (TxD) - * 2 - GND <----> Pin 5 (GND) <-> Pin 7 (GND) - * 4 - TxD <----> Pin 2 (RxD) <-> Pin 3 (RxD) - * 3 - +12V (from HDD drive connector), DON'T connect to DE9 or DB25!!! + * 1 - RxD <----> Pin 3 (TxD) <-> Pin 2 (TxD) + * 2 - GND <----> Pin 5 (GND) <-> Pin 7 (GND) + * 4 - TxD <----> Pin 2 (RxD) <-> Pin 3 (RxD) + * 3 - +12V (from HDD drive connector), DON'T connect to DE9 or DB25!!! * * Pin numbers for DE9 and DB25 are noted on the plug (quite small:). For * RJ10, it's like this: diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c index d3e9dd6a13c..8935290256b 100644 --- a/drivers/input/keyboard/locomokbd.c +++ b/drivers/input/keyboard/locomokbd.c @@ -42,7 +42,7 @@ MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>"); MODULE_DESCRIPTION("LoCoMo keyboard driver"); MODULE_LICENSE("GPL"); -#define LOCOMOKBD_NUMKEYS 128 +#define LOCOMOKBD_NUMKEYS 128 #define KEY_ACTIVITY KEY_F16 #define KEY_CONTACT KEY_F18 @@ -61,7 +61,7 @@ static unsigned char locomokbd_keycode[LOCOMOKBD_NUMKEYS] = { KEY_G, KEY_F, KEY_X, KEY_S, 0, 0, 0, 0, 0, 0, /* 90 - 99 */ 0, 0, KEY_DOT, 0, KEY_COMMA, KEY_N, KEY_B, KEY_C, KEY_Z, KEY_A, /* 100 - 109 */ KEY_LEFTSHIFT, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0, 0, 0, /* 110 - 119 */ - KEY_M, KEY_SPACE, KEY_V, KEY_APOSTROPHE, KEY_SLASH, 0, 0, 0 /* 120 - 128 */ + KEY_M, KEY_SPACE, KEY_V, KEY_APOSTROPHE, KEY_SLASH, 0, 0, 0 /* 120 - 128 */ }; #define KB_ROWS 16 @@ -82,7 +82,7 @@ struct locomokbd { struct locomo_dev *ldev; unsigned long base; spinlock_t lock; - + struct timer_list timer; }; @@ -95,7 +95,7 @@ static inline void locomokbd_charge_all(unsigned long membase) static inline void locomokbd_activate_all(unsigned long membase) { unsigned long r; - + locomo_writel(0, membase + LOCOMO_KSC); r = locomo_readl(membase + LOCOMO_KIC); r &= 0xFEFF; @@ -127,7 +127,7 @@ static inline void locomokbd_reset_col(unsigned long membase, int col) */ /* Scan the hardware keyboard and push any changes up through the input layer */ -static void locomokbd_scankeyboard(struct locomokbd *locomokbd, struct pt_regs *regs) +static void locomokbd_scankeyboard(struct locomokbd *locomokbd, struct pt_regs *regs) { unsigned int row, col, rowd, scancode; unsigned long flags; @@ -138,7 +138,7 @@ static void locomokbd_scankeyboard(struct locomokbd *locomokbd, struct pt_regs * if (regs) input_regs(&locomokbd->input, regs); - + locomokbd_charge_all(membase); num_pressed = 0; @@ -146,9 +146,9 @@ static void locomokbd_scankeyboard(struct locomokbd *locomokbd, struct pt_regs * locomokbd_activate_col(membase, col); udelay(KB_DELAY); - + rowd = ~locomo_readl(membase + LOCOMO_KIB); - for (row = 0; row < KB_ROWS; row++ ) { + for (row = 0; row < KB_ROWS; row++) { scancode = SCANCODE(col, row); if (rowd & KB_ROWMASK(row)) { num_pressed += 1; @@ -170,7 +170,7 @@ static void locomokbd_scankeyboard(struct locomokbd *locomokbd, struct pt_regs * spin_unlock_irqrestore(&locomokbd->lock, flags); } -/* +/* * LoCoMo keyboard interrupt handler. */ static irqreturn_t locomokbd_interrupt(int irq, void *dev_id, struct pt_regs *regs) @@ -205,8 +205,8 @@ static int locomokbd_probe(struct locomo_dev *dev) memset(locomokbd, 0, sizeof(struct locomokbd)); /* try and claim memory region */ - if (!request_mem_region((unsigned long) dev->mapbase, - dev->length, + if (!request_mem_region((unsigned long) dev->mapbase, + dev->length, LOCOMO_DRIVER_NAME(dev))) { ret = -EBUSY; printk(KERN_ERR "locomokbd: Can't acquire access to io memory for keyboard\n"); @@ -225,7 +225,7 @@ static int locomokbd_probe(struct locomo_dev *dev) locomokbd->timer.data = (unsigned long) locomokbd; locomokbd->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REP); - + init_input_dev(&locomokbd->input); locomokbd->input.keycode = locomokbd->keycode; locomokbd->input.keycodesize = sizeof(unsigned char); @@ -271,11 +271,11 @@ free: static int locomokbd_remove(struct locomo_dev *dev) { struct locomokbd *locomokbd = locomo_get_drvdata(dev); - + free_irq(dev->irq[0], locomokbd); del_timer_sync(&locomokbd->timer); - + input_unregister_device(&locomokbd->input); locomo_set_drvdata(dev, NULL); diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c index 859ed771ee0..eecbde294f1 100644 --- a/drivers/input/keyboard/maple_keyb.c +++ b/drivers/input/keyboard/maple_keyb.c @@ -1,6 +1,6 @@ /* * $Id: maple_keyb.c,v 1.4 2004/03/22 01:18:15 lethal Exp $ - * SEGA Dreamcast keyboard driver + * SEGA Dreamcast keyboard driver * Based on drivers/usb/usbkbd.c */ @@ -40,7 +40,6 @@ struct dc_kbd { struct input_dev dev; unsigned char new[8]; unsigned char old[8]; - int open; }; @@ -95,22 +94,6 @@ static void dc_kbd_callback(struct mapleq *mq) } } - -static int dc_kbd_open(struct input_dev *dev) -{ - struct dc_kbd *kbd = dev->private; - kbd->open++; - return 0; -} - - -static void dc_kbd_close(struct input_dev *dev) -{ - struct dc_kbd *kbd = dev->private; - kbd->open--; -} - - static int dc_kbd_connect(struct maple_device *dev) { int i; @@ -133,9 +116,6 @@ static int dc_kbd_connect(struct maple_device *dev) clear_bit(0, kbd->dev.keybit); kbd->dev.private = kbd; - kbd->dev.open = dc_kbd_open; - kbd->dev.close = dc_kbd_close; - kbd->dev.event = NULL; kbd->dev.name = dev->product_name; kbd->dev.id.bustype = BUS_MAPLE; diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 158c8e845ff..98710997aaa 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -298,9 +298,11 @@ static int uinput_alloc_device(struct file *file, const char __user *buffer, siz /* check if absmin/absmax/absfuzz/absflat are filled as * told in Documentation/input/input-programming.txt */ if (test_bit(EV_ABS, dev->evbit)) { - retval = uinput_validate_absbits(dev); - if (retval < 0) + int err = uinput_validate_absbits(dev); + if (err < 0) { + retval = err; kfree(dev->name); + } } exit: diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index a7864195806..c4909b49337 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -15,4 +15,4 @@ obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o -psmouse-objs := psmouse-base.o alps.o logips2pp.o synaptics.o +psmouse-objs := psmouse-base.o alps.o logips2pp.o synaptics.o lifebook.o diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 7bf4be733e9..a12e98158a7 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -30,10 +30,11 @@ #define ALPS_DUALPOINT 0x01 #define ALPS_WHEEL 0x02 -#define ALPS_FW_BK 0x04 +#define ALPS_FW_BK_1 0x04 #define ALPS_4BTN 0x08 #define ALPS_OLDPROTO 0x10 #define ALPS_PASS 0x20 +#define ALPS_FW_BK_2 0x40 static struct alps_model_info alps_model_data[] = { { { 0x33, 0x02, 0x0a }, 0x88, 0xf8, ALPS_OLDPROTO }, /* UMAX-530T */ @@ -43,11 +44,11 @@ static struct alps_model_info alps_model_data[] = { { { 0x63, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, { { 0x63, 0x02, 0x28 }, 0xf8, 0xf8, 0 }, { { 0x63, 0x02, 0x3c }, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */ - { { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK }, /* NEC Versa L320 */ + { { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ { { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 }, { { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS }, /* Dell Latitude D800 */ { { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, - { { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, + { { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ { { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ @@ -61,11 +62,11 @@ static struct alps_model_info alps_model_data[] = { /* * ALPS abolute Mode - new format - * - * byte 0: 1 ? ? ? 1 ? ? ? + * + * byte 0: 1 ? ? ? 1 ? ? ? * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 * byte 2: 0 x10 x9 x8 x7 ? fin ges - * byte 3: 0 y9 y8 y7 1 M R L + * byte 3: 0 y9 y8 y7 1 M R L * byte 4: 0 y6 y5 y4 y3 y2 y1 y0 * byte 5: 0 z6 z5 z4 z3 z2 z1 z0 * @@ -81,11 +82,12 @@ static void alps_process_packet(struct psmouse *psmouse, struct pt_regs *regs) struct input_dev *dev = &psmouse->dev; struct input_dev *dev2 = &priv->dev2; int x, y, z, ges, fin, left, right, middle; + int back = 0, forward = 0; input_regs(dev, regs); if ((packet[0] & 0xc8) == 0x08) { /* 3-byte PS/2 packet */ - input_report_key(dev2, BTN_LEFT, packet[0] & 1); + input_report_key(dev2, BTN_LEFT, packet[0] & 1); input_report_key(dev2, BTN_RIGHT, packet[0] & 2); input_report_key(dev2, BTN_MIDDLE, packet[0] & 4); input_report_rel(dev2, REL_X, @@ -112,6 +114,18 @@ static void alps_process_packet(struct psmouse *psmouse, struct pt_regs *regs) z = packet[5]; } + if (priv->i->flags & ALPS_FW_BK_1) { + back = packet[2] & 4; + forward = packet[0] & 0x10; + } + + if (priv->i->flags & ALPS_FW_BK_2) { + back = packet[3] & 4; + forward = packet[2] & 4; + if ((middle = forward && back)) + forward = back = 0; + } + ges = packet[2] & 1; fin = packet[2] & 2; @@ -155,13 +169,12 @@ static void alps_process_packet(struct psmouse *psmouse, struct pt_regs *regs) input_report_abs(dev, ABS_PRESSURE, z); input_report_key(dev, BTN_TOOL_FINGER, z > 0); - if (priv->i->flags & ALPS_WHEEL) input_report_rel(dev, REL_WHEEL, ((packet[0] >> 4) & 0x07) | ((packet[2] >> 2) & 0x08)); - if (priv->i->flags & ALPS_FW_BK) { - input_report_key(dev, BTN_FORWARD, packet[0] & 0x10); - input_report_key(dev, BTN_BACK, packet[2] & 0x04); + if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { + input_report_key(dev, BTN_FORWARD, forward); + input_report_key(dev, BTN_BACK, back); } input_sync(dev); @@ -257,7 +270,6 @@ static struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *vers static int alps_passthrough_mode(struct psmouse *psmouse, int enable) { struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param[3]; int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11; if (ps2_command(ps2dev, NULL, cmd) || @@ -267,7 +279,7 @@ static int alps_passthrough_mode(struct psmouse *psmouse, int enable) return -1; /* we may get 3 more bytes, just ignore them */ - ps2_command(ps2dev, param, 0x0300); + ps2_drain(ps2dev, 3, 100); return 0; } @@ -425,7 +437,7 @@ int alps_init(struct psmouse *psmouse) psmouse->dev.relbit[LONG(REL_WHEEL)] |= BIT(REL_WHEEL); } - if (priv->i->flags & ALPS_FW_BK) { + if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { psmouse->dev.keybit[LONG(BTN_FORWARD)] |= BIT(BTN_FORWARD); psmouse->dev.keybit[LONG(BTN_BACK)] |= BIT(BTN_BACK); } @@ -436,8 +448,8 @@ int alps_init(struct psmouse *psmouse) priv->dev2.id.bustype = BUS_I8042; priv->dev2.id.vendor = 0x0002; priv->dev2.id.product = PSMOUSE_ALPS; - priv->dev2.id.version = 0x0000; - + priv->dev2.id.version = 0x0000; + priv->dev2.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); priv->dev2.relbit[LONG(REL_X)] |= BIT(REL_X) | BIT(REL_Y); priv->dev2.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); @@ -461,17 +473,15 @@ init_fail: int alps_detect(struct psmouse *psmouse, int set_properties) { int version; - struct alps_model_info *model; + struct alps_model_info *model; if (!(model = alps_get_model(psmouse, &version))) return -1; if (set_properties) { psmouse->vendor = "ALPS"; - if (model->flags & ALPS_DUALPOINT) - psmouse->name = "DualPoint TouchPad"; - else - psmouse->name = "GlidePoint"; + psmouse->name = model->flags & ALPS_DUALPOINT ? + "DualPoint TouchPad" : "GlidePoint"; psmouse->model = version; } return 0; diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c index 7baa09cca7c..e994849efb8 100644 --- a/drivers/input/mouse/amimouse.c +++ b/drivers/input/mouse/amimouse.c @@ -33,7 +33,6 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Amiga mouse driver"); MODULE_LICENSE("GPL"); -static int amimouse_used = 0; static int amimouse_lastx, amimouse_lasty; static struct input_dev amimouse_dev; @@ -81,16 +80,12 @@ static int amimouse_open(struct input_dev *dev) { unsigned short joy0dat; - if (amimouse_used++) - return 0; - joy0dat = custom.joy0dat; amimouse_lastx = joy0dat & 0xff; amimouse_lasty = joy0dat >> 8; if (request_irq(IRQ_AMIGA_VERTB, amimouse_interrupt, 0, "amimouse", amimouse_interrupt)) { - amimouse_used--; printk(KERN_ERR "amimouse.c: Can't allocate irq %d\n", IRQ_AMIGA_VERTB); return -EBUSY; } @@ -100,8 +95,7 @@ static int amimouse_open(struct input_dev *dev) static void amimouse_close(struct input_dev *dev) { - if (!--amimouse_used) - free_irq(IRQ_AMIGA_VERTB, amimouse_interrupt); + free_irq(IRQ_AMIGA_VERTB, amimouse_interrupt); } static int __init amimouse_init(void) diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c index ca4e9688662..1f62c013401 100644 --- a/drivers/input/mouse/inport.c +++ b/drivers/input/mouse/inport.c @@ -17,18 +17,18 @@ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic @@ -87,29 +87,23 @@ MODULE_PARM_DESC(irq, "IRQ number (5=default)"); __obsolete_setup("inport_irq="); -static int inport_used; - static irqreturn_t inport_interrupt(int irq, void *dev_id, struct pt_regs *regs); static int inport_open(struct input_dev *dev) { - if (!inport_used++) { - if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL)) - return -EBUSY; - outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); - outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); - } + if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL)) + return -EBUSY; + outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); + outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); return 0; } static void inport_close(struct input_dev *dev) { - if (!--inport_used) { - outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); - outb(INPORT_MODE_BASE, INPORT_DATA_PORT); - free_irq(inport_irq, NULL); - } + outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); + outb(INPORT_MODE_BASE, INPORT_DATA_PORT); + free_irq(inport_irq, NULL); } static struct input_dev inport_dev = { @@ -120,11 +114,11 @@ static struct input_dev inport_dev = { .close = inport_close, .name = INPORT_NAME, .phys = "isa023c/input0", - .id = { - .bustype = BUS_ISA, - .vendor = INPORT_VENDOR, - .product = 0x0001, - .version = 0x0100, + .id = { + .bustype = BUS_ISA, + .vendor = INPORT_VENDOR, + .product = 0x0001, + .version = 0x0100, }, }; diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c new file mode 100644 index 00000000000..bd9df9b2832 --- /dev/null +++ b/drivers/input/mouse/lifebook.c @@ -0,0 +1,134 @@ +/* + * Fujitsu B-series Lifebook PS/2 TouchScreen driver + * + * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz> + * Copyright (c) 2005 Kenan Esau <kenan.esau@conan.de> + * + * TouchScreen detection, absolute mode setting and packet layout is taken from + * Harald Hoyer's description of the device. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/libps2.h> +#include <linux/dmi.h> + +#include "psmouse.h" +#include "lifebook.h" + +static struct dmi_system_id lifebook_dmi_table[] = { + { + .ident = "Lifebook B", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK B Series"), + }, + }, + { } +}; + + +static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse, struct pt_regs *regs) +{ + unsigned char *packet = psmouse->packet; + struct input_dev *dev = &psmouse->dev; + + if (psmouse->pktcnt != 3) + return PSMOUSE_GOOD_DATA; + + input_regs(dev, regs); + + /* calculate X and Y */ + if ((packet[0] & 0x08) == 0x00) { + input_report_abs(dev, ABS_X, + (packet[1] | ((packet[0] & 0x30) << 4))); + input_report_abs(dev, ABS_Y, + 1024 - (packet[2] | ((packet[0] & 0xC0) << 2))); + } else { + input_report_rel(dev, REL_X, + ((packet[0] & 0x10) ? packet[1] - 256 : packet[1])); + input_report_rel(dev, REL_Y, + -(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2])); + } + + input_report_key(dev, BTN_LEFT, packet[0] & 0x01); + input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); + input_report_key(dev, BTN_TOUCH, packet[0] & 0x04); + + input_sync(dev); + + return PSMOUSE_FULL_PACKET; +} + +static int lifebook_absolute_mode(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param; + + if (psmouse_reset(psmouse)) + return -1; + + /* + Enable absolute output -- ps2_command fails always but if + you leave this call out the touchsreen will never send + absolute coordinates + */ + param = 0x07; + ps2_command(ps2dev, ¶m, PSMOUSE_CMD_SETRES); + + return 0; +} + +static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution) +{ + unsigned char params[] = { 0, 1, 2, 2, 3 }; + + if (resolution == 0 || resolution > 400) + resolution = 400; + + ps2_command(&psmouse->ps2dev, ¶ms[resolution / 100], PSMOUSE_CMD_SETRES); + psmouse->resolution = 50 << params[resolution / 100]; +} + +static void lifebook_disconnect(struct psmouse *psmouse) +{ + psmouse_reset(psmouse); +} + +int lifebook_detect(struct psmouse *psmouse, int set_properties) +{ + if (!dmi_check_system(lifebook_dmi_table)) + return -1; + + if (set_properties) { + psmouse->vendor = "Fujitsu"; + psmouse->name = "Lifebook TouchScreen"; + } + + return 0; +} + +int lifebook_init(struct psmouse *psmouse) +{ + if (lifebook_absolute_mode(psmouse)) + return -1; + + psmouse->dev.evbit[0] = BIT(EV_ABS) | BIT(EV_KEY) | BIT(EV_REL); + psmouse->dev.keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); + psmouse->dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); + input_set_abs_params(&psmouse->dev, ABS_X, 0, 1024, 0, 0); + input_set_abs_params(&psmouse->dev, ABS_Y, 0, 1024, 0, 0); + + psmouse->protocol_handler = lifebook_process_byte; + psmouse->set_resolution = lifebook_set_resolution; + psmouse->disconnect = lifebook_disconnect; + psmouse->reconnect = lifebook_absolute_mode; + psmouse->pktsize = 3; + + return 0; +} + diff --git a/drivers/input/mouse/lifebook.h b/drivers/input/mouse/lifebook.h new file mode 100644 index 00000000000..be1c0943825 --- /dev/null +++ b/drivers/input/mouse/lifebook.h @@ -0,0 +1,17 @@ +/* + * Fujitsu B-series Lifebook PS/2 TouchScreen driver + * + * Copyright (c) 2005 Vojtech Pavlik + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#ifndef _LIFEBOOK_H +#define _LIFEBOOK_H + +int lifebook_detect(struct psmouse *psmouse, int set_properties); +int lifebook_init(struct psmouse *psmouse); + +#endif diff --git a/drivers/input/mouse/logibm.c b/drivers/input/mouse/logibm.c index 77eb83e87f6..8b524316722 100644 --- a/drivers/input/mouse/logibm.c +++ b/drivers/input/mouse/logibm.c @@ -18,18 +18,18 @@ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic @@ -77,16 +77,11 @@ MODULE_PARM_DESC(irq, "IRQ number (5=default)"); __obsolete_setup("logibm_irq="); -static int logibm_used = 0; - static irqreturn_t logibm_interrupt(int irq, void *dev_id, struct pt_regs *regs); static int logibm_open(struct input_dev *dev) { - if (logibm_used++) - return 0; if (request_irq(logibm_irq, logibm_interrupt, 0, "logibm", NULL)) { - logibm_used--; printk(KERN_ERR "logibm.c: Can't allocate irq %d\n", logibm_irq); return -EBUSY; } @@ -96,8 +91,6 @@ static int logibm_open(struct input_dev *dev) static void logibm_close(struct input_dev *dev) { - if (--logibm_used) - return; outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); free_irq(logibm_irq, NULL); } @@ -167,7 +160,7 @@ static int __init logibm_init(void) outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); input_register_device(&logibm_dev); - + printk(KERN_INFO "input: Logitech bus mouse at %#x irq %d\n", LOGIBM_BASE, logibm_irq); return 0; diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c index 12dc0ef5020..e90c60cbbf0 100644 --- a/drivers/input/mouse/maplemouse.c +++ b/drivers/input/mouse/maplemouse.c @@ -1,6 +1,6 @@ /* * $Id: maplemouse.c,v 1.2 2004/03/22 01:18:15 lethal Exp $ - * SEGA Dreamcast mouse driver + * SEGA Dreamcast mouse driver * Based on drivers/usb/usbmouse.c */ @@ -15,80 +15,51 @@ MODULE_AUTHOR("YAEGASHI Takeshi <t@keshi.org>"); MODULE_DESCRIPTION("SEGA Dreamcast mouse driver"); -struct dc_mouse { - struct input_dev dev; - int open; -}; - - static void dc_mouse_callback(struct mapleq *mq) { int buttons, relx, rely, relz; struct maple_device *mapledev = mq->dev; - struct dc_mouse *mouse = mapledev->private_data; - struct input_dev *dev = &mouse->dev; + struct input_dev *dev = mapledev->private_data; unsigned char *res = mq->recvbuf; buttons = ~res[8]; - relx=*(unsigned short *)(res+12)-512; - rely=*(unsigned short *)(res+14)-512; - relz=*(unsigned short *)(res+16)-512; + relx = *(unsigned short *)(res + 12) - 512; + rely = *(unsigned short *)(res + 14) - 512; + relz = *(unsigned short *)(res + 16) - 512; - input_report_key(dev, BTN_LEFT, buttons&4); - input_report_key(dev, BTN_MIDDLE, buttons&9); - input_report_key(dev, BTN_RIGHT, buttons&2); + input_report_key(dev, BTN_LEFT, buttons & 4); + input_report_key(dev, BTN_MIDDLE, buttons & 9); + input_report_key(dev, BTN_RIGHT, buttons & 2); input_report_rel(dev, REL_X, relx); input_report_rel(dev, REL_Y, rely); input_report_rel(dev, REL_WHEEL, relz); input_sync(dev); } - -static int dc_mouse_open(struct input_dev *dev) -{ - struct dc_mouse *mouse = dev->private; - mouse->open++; - return 0; -} - - -static void dc_mouse_close(struct input_dev *dev) -{ - struct dc_mouse *mouse = dev->private; - mouse->open--; -} - - static int dc_mouse_connect(struct maple_device *dev) { unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]); - struct dc_mouse *mouse; + struct input_dev *input_dev; - if (!(mouse = kmalloc(sizeof(struct dc_mouse), GFP_KERNEL))) + if (!(input_dev = kmalloc(sizeof(struct input_dev), GFP_KERNEL))) return -1; - memset(mouse, 0, sizeof(struct dc_mouse)); - - dev->private_data = mouse; - mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); - mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); - mouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL); + dev->private_data = input_dev; - init_input_dev(&mouse->dev); + memset(input_dev, 0, sizeof(struct dc_mouse)); + init_input_dev(input_dev); + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); + input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL); - mouse->dev.private = mouse; - mouse->dev.open = dc_mouse_open; - mouse->dev.close = dc_mouse_close; - mouse->dev.event = NULL; + input_dev->name = dev->product_name; + input_dev->id.bustype = BUS_MAPLE; - mouse->dev.name = dev->product_name; - mouse->dev.id.bustype = BUS_MAPLE; - - input_register_device(&mouse->dev); + input_register_device(input_dev); maple_getcond_callback(dev, dc_mouse_callback, 1, MAPLE_FUNC_MOUSE); - printk(KERN_INFO "input: mouse(0x%lx): %s\n", data, mouse->dev.name); + printk(KERN_INFO "input: mouse(0x%lx): %s\n", data, input_dev->name); return 0; } @@ -96,10 +67,10 @@ static int dc_mouse_connect(struct maple_device *dev) static void dc_mouse_disconnect(struct maple_device *dev) { - struct dc_mouse *mouse = dev->private_data; + struct input_dev *input_dev = dev->private_data; - input_unregister_device(&mouse->dev); - kfree(mouse); + input_unregister_device(input_dev); + kfree(input_dev); } diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c index 0c74918fe25..93393d5c007 100644 --- a/drivers/input/mouse/pc110pad.c +++ b/drivers/input/mouse/pc110pad.c @@ -4,7 +4,7 @@ * Copyright (c) 2000-2001 Vojtech Pavlik * * Based on the work of: - * Alan Cox Robin O'Leary + * Alan Cox Robin O'Leary */ /* @@ -56,7 +56,6 @@ static int pc110pad_io = 0x15e0; static struct input_dev pc110pad_dev; static int pc110pad_data[3]; static int pc110pad_count; -static int pc110pad_used; static char *pc110pad_name = "IBM PC110 TouchPad"; static char *pc110pad_phys = "isa15e0/input0"; @@ -74,7 +73,7 @@ static irqreturn_t pc110pad_interrupt(int irq, void *ptr, struct pt_regs *regs) if (pc110pad_count < 3) return IRQ_HANDLED; - + input_regs(&pc110pad_dev, regs); input_report_key(&pc110pad_dev, BTN_TOUCH, pc110pad_data[0] & 0x01); @@ -90,15 +89,11 @@ static irqreturn_t pc110pad_interrupt(int irq, void *ptr, struct pt_regs *regs) static void pc110pad_close(struct input_dev *dev) { - if (!--pc110pad_used) - outb(PC110PAD_OFF, pc110pad_io + 2); + outb(PC110PAD_OFF, pc110pad_io + 2); } static int pc110pad_open(struct input_dev *dev) { - if (pc110pad_used++) - return 0; - pc110pad_interrupt(0,NULL,NULL); pc110pad_interrupt(0,NULL,NULL); pc110pad_interrupt(0,NULL,NULL); @@ -145,7 +140,7 @@ static int __init pc110pad_init(void) pc110pad_dev.absmax[ABS_X] = 0x1ff; pc110pad_dev.absmax[ABS_Y] = 0x0ff; - + pc110pad_dev.open = pc110pad_open; pc110pad_dev.close = pc110pad_close; @@ -156,17 +151,17 @@ static int __init pc110pad_init(void) pc110pad_dev.id.product = 0x0001; pc110pad_dev.id.version = 0x0100; - input_register_device(&pc110pad_dev); + input_register_device(&pc110pad_dev); printk(KERN_INFO "input: %s at %#x irq %d\n", pc110pad_name, pc110pad_io, pc110pad_irq); - + return 0; } - + static void __exit pc110pad_exit(void) { - input_unregister_device(&pc110pad_dev); + input_unregister_device(&pc110pad_dev); outb(PC110PAD_OFF, pc110pad_io + 2); diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 019034b21a0..19785a6c5ab 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -24,6 +24,7 @@ #include "synaptics.h" #include "logips2pp.h" #include "alps.h" +#include "lifebook.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -31,10 +32,9 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -static unsigned int psmouse_max_proto = -1U; +static unsigned int psmouse_max_proto = PSMOUSE_AUTO; static int psmouse_set_maxproto(const char *val, struct kernel_param *kp); static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp); -static char *psmouse_proto_abbrev[] = { NULL, "bare", NULL, NULL, NULL, "imps", "exps", NULL, NULL, NULL }; #define param_check_proto_abbrev(name, p) __param_check(name, p, unsigned int) #define param_set_proto_abbrev psmouse_set_maxproto #define param_get_proto_abbrev psmouse_get_maxproto @@ -57,6 +57,7 @@ static unsigned int psmouse_resetafter; module_param_named(resetafter, psmouse_resetafter, uint, 0644); MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never)."); +PSMOUSE_DEFINE_ATTR(protocol); PSMOUSE_DEFINE_ATTR(rate); PSMOUSE_DEFINE_ATTR(resolution); PSMOUSE_DEFINE_ATTR(resetafter); @@ -67,7 +68,23 @@ __obsolete_setup("psmouse_smartscroll="); __obsolete_setup("psmouse_resetafter="); __obsolete_setup("psmouse_rate="); -static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "ThinkPS/2", "GenPS/2", "ImPS/2", "ImExPS/2", "SynPS/2", "AlpsPS/2" }; +/* + * psmouse_sem protects all operations changing state of mouse + * (connecting, disconnecting, changing rate or resolution via + * sysfs). We could use a per-device semaphore but since there + * rarely more than one PS/2 mouse connected and since semaphore + * is taken in "slow" paths it is not worth it. + */ +static DECLARE_MUTEX(psmouse_sem); + +struct psmouse_protocol { + enum psmouse_type type; + char *name; + char *alias; + int maxproto; + int (*detect)(struct psmouse *, int); + int (*init)(struct psmouse *); +}; /* * psmouse_process_byte() analyzes the PS/2 data stream and reports @@ -407,12 +424,15 @@ static int thinking_detect(struct psmouse *psmouse, int set_properties) */ static int ps2bare_detect(struct psmouse *psmouse, int set_properties) { - if (!psmouse->vendor) psmouse->vendor = "Generic"; - if (!psmouse->name) psmouse->name = "Mouse"; + if (set_properties) { + if (!psmouse->vendor) psmouse->vendor = "Generic"; + if (!psmouse->name) psmouse->name = "Mouse"; + } return 0; } + /* * psmouse_extensions() probes for any extensions to the basic PS/2 protocol * the mouse may have. @@ -424,6 +444,17 @@ static int psmouse_extensions(struct psmouse *psmouse, int synaptics_hardware = 0; /* + * We always check for lifebook because it does not disturb mouse + * (it only checks DMI information). + */ + if (lifebook_detect(psmouse, set_properties) == 0) { + if (max_proto > PSMOUSE_IMEX) { + if (!set_properties || lifebook_init(psmouse) == 0) + return PSMOUSE_LIFEBOOK; + } + } + +/* * Try Kensington ThinkingMouse (we try first, because synaptics probe * upsets the thinkingmouse). */ @@ -506,6 +537,103 @@ static int psmouse_extensions(struct psmouse *psmouse, return PSMOUSE_PS2; } +static struct psmouse_protocol psmouse_protocols[] = { + { + .type = PSMOUSE_PS2, + .name = "PS/2", + .alias = "bare", + .maxproto = 1, + .detect = ps2bare_detect, + }, + { + .type = PSMOUSE_PS2PP, + .name = "PS2++", + .alias = "logitech", + .detect = ps2pp_init, + }, + { + .type = PSMOUSE_THINKPS, + .name = "ThinkPS/2", + .alias = "thinkps", + .detect = thinking_detect, + }, + { + .type = PSMOUSE_GENPS, + .name = "GenPS/2", + .alias = "genius", + .detect = genius_detect, + }, + { + .type = PSMOUSE_IMPS, + .name = "ImPS/2", + .alias = "imps", + .maxproto = 1, + .detect = intellimouse_detect, + }, + { + .type = PSMOUSE_IMEX, + .name = "ImExPS/2", + .alias = "exps", + .maxproto = 1, + .detect = im_explorer_detect, + }, + { + .type = PSMOUSE_SYNAPTICS, + .name = "SynPS/2", + .alias = "synaptics", + .detect = synaptics_detect, + .init = synaptics_init, + }, + { + .type = PSMOUSE_ALPS, + .name = "AlpsPS/2", + .alias = "alps", + .detect = alps_detect, + .init = alps_init, + }, + { + .type = PSMOUSE_LIFEBOOK, + .name = "LBPS/2", + .alias = "lifebook", + .init = lifebook_init, + }, + { + .type = PSMOUSE_AUTO, + .name = "auto", + .alias = "any", + .maxproto = 1, + }, +}; + +static struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) + if (psmouse_protocols[i].type == type) + return &psmouse_protocols[i]; + + WARN_ON(1); + return &psmouse_protocols[0]; +} + +static struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len) +{ + struct psmouse_protocol *p; + int i; + + for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) { + p = &psmouse_protocols[i]; + + if ((strlen(p->name) == len && !strncmp(p->name, name, len)) || + (strlen(p->alias) == len && !strncmp(p->alias, name, len))) + return &psmouse_protocols[i]; + } + + return NULL; +} + + /* * psmouse_probe() probes for a PS/2 mouse. */ @@ -653,30 +781,84 @@ static void psmouse_cleanup(struct serio *serio) static void psmouse_disconnect(struct serio *serio) { - struct psmouse *psmouse, *parent; + struct psmouse *psmouse, *parent = NULL; + psmouse = serio_get_drvdata(serio); + + device_remove_file(&serio->dev, &psmouse_attr_protocol); device_remove_file(&serio->dev, &psmouse_attr_rate); device_remove_file(&serio->dev, &psmouse_attr_resolution); device_remove_file(&serio->dev, &psmouse_attr_resetafter); - psmouse = serio_get_drvdata(serio); + down(&psmouse_sem); + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); - if (parent->pt_deactivate) - parent->pt_deactivate(parent); + psmouse_deactivate(parent); } if (psmouse->disconnect) psmouse->disconnect(psmouse); + if (parent && parent->pt_deactivate) + parent->pt_deactivate(parent); + psmouse_set_state(psmouse, PSMOUSE_IGNORE); input_unregister_device(&psmouse->dev); serio_close(serio); serio_set_drvdata(serio, NULL); kfree(psmouse); + + if (parent) + psmouse_activate(parent); + + up(&psmouse_sem); +} + +static int psmouse_switch_protocol(struct psmouse *psmouse, struct psmouse_protocol *proto) +{ + memset(&psmouse->dev, 0, sizeof(struct input_dev)); + + init_input_dev(&psmouse->dev); + + psmouse->dev.private = psmouse; + psmouse->dev.dev = &psmouse->ps2dev.serio->dev; + + psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); + psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); + + psmouse->set_rate = psmouse_set_rate; + psmouse->set_resolution = psmouse_set_resolution; + psmouse->protocol_handler = psmouse_process_byte; + psmouse->pktsize = 3; + + if (proto && (proto->detect || proto->init)) { + if (proto->detect && proto->detect(psmouse, 1) < 0) + return -1; + + if (proto->init && proto->init(psmouse) < 0) + return -1; + + psmouse->type = proto->type; + } + else + psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1); + + sprintf(psmouse->devname, "%s %s %s", + psmouse_protocol_by_type(psmouse->type)->name, psmouse->vendor, psmouse->name); + + psmouse->dev.name = psmouse->devname; + psmouse->dev.phys = psmouse->phys; + psmouse->dev.id.bustype = BUS_I8042; + psmouse->dev.id.vendor = 0x0002; + psmouse->dev.id.product = psmouse->type; + psmouse->dev.id.version = psmouse->model; + + return 0; } /* @@ -688,6 +870,8 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) struct psmouse *psmouse, *parent = NULL; int retval; + down(&psmouse_sem); + /* * If this is a pass-through port deactivate parent so the device * connected to this port can be successfully identified @@ -697,20 +881,14 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) psmouse_deactivate(parent); } - if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL))) { + if (!(psmouse = kcalloc(1, sizeof(struct psmouse), GFP_KERNEL))) { retval = -ENOMEM; goto out; } - memset(psmouse, 0, sizeof(struct psmouse)); - ps2_init(&psmouse->ps2dev, serio); sprintf(psmouse->phys, "%s/input0", serio->phys); - psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); - psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); - psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); - psmouse->dev.private = psmouse; - psmouse->dev.dev = &serio->dev; + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); serio_set_drvdata(serio, psmouse); @@ -734,25 +912,10 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) psmouse->resolution = psmouse_resolution; psmouse->resetafter = psmouse_resetafter; psmouse->smartscroll = psmouse_smartscroll; - psmouse->set_rate = psmouse_set_rate; - psmouse->set_resolution = psmouse_set_resolution; - psmouse->protocol_handler = psmouse_process_byte; - psmouse->pktsize = 3; - psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1); - - sprintf(psmouse->devname, "%s %s %s", - psmouse_protocols[psmouse->type], psmouse->vendor, psmouse->name); - - psmouse->dev.name = psmouse->devname; - psmouse->dev.phys = psmouse->phys; - psmouse->dev.id.bustype = BUS_I8042; - psmouse->dev.id.vendor = 0x0002; - psmouse->dev.id.product = psmouse->type; - psmouse->dev.id.version = psmouse->model; + psmouse_switch_protocol(psmouse, NULL); input_register_device(&psmouse->dev); - printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys); psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); @@ -762,6 +925,7 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) if (parent && parent->pt_activate) parent->pt_activate(parent); + device_create_file(&serio->dev, &psmouse_attr_protocol); device_create_file(&serio->dev, &psmouse_attr_rate); device_create_file(&serio->dev, &psmouse_attr_resolution); device_create_file(&serio->dev, &psmouse_attr_resetafter); @@ -771,10 +935,11 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) retval = 0; out: - /* If this is a pass-through port the parent awaits to be activated */ + /* If this is a pass-through port the parent needs to be re-activated */ if (parent) psmouse_activate(parent); + up(&psmouse_sem); return retval; } @@ -791,6 +956,8 @@ static int psmouse_reconnect(struct serio *serio) return -1; } + down(&psmouse_sem); + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); psmouse_deactivate(parent); @@ -823,6 +990,7 @@ out: if (parent) psmouse_activate(parent); + up(&psmouse_sem); return rc; } @@ -893,26 +1061,109 @@ ssize_t psmouse_attr_set_helper(struct device *dev, const char *buf, size_t coun if (serio->drv != &psmouse_drv) { retval = -ENODEV; - goto out; + goto out_unpin; + } + + retval = down_interruptible(&psmouse_sem); + if (retval) + goto out_unpin; + + if (psmouse->state == PSMOUSE_IGNORE) { + retval = -ENODEV; + goto out_up; } if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); psmouse_deactivate(parent); } + psmouse_deactivate(psmouse); retval = handler(psmouse, buf, count); - psmouse_activate(psmouse); + if (retval != -ENODEV) + psmouse_activate(psmouse); + if (parent) psmouse_activate(parent); -out: + out_up: + up(&psmouse_sem); + out_unpin: serio_unpin_driver(serio); return retval; } +static ssize_t psmouse_attr_show_protocol(struct psmouse *psmouse, char *buf) +{ + return sprintf(buf, "%s\n", psmouse_protocol_by_type(psmouse->type)->name); +} + +static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, const char *buf, size_t count) +{ + struct serio *serio = psmouse->ps2dev.serio; + struct psmouse *parent = NULL; + struct psmouse_protocol *proto; + int retry = 0; + + if (!(proto = psmouse_protocol_by_name(buf, count))) + return -EINVAL; + + if (psmouse->type == proto->type) + return count; + + while (serio->child) { + if (++retry > 3) { + printk(KERN_WARNING "psmouse: failed to destroy child port, protocol change aborted.\n"); + return -EIO; + } + + up(&psmouse_sem); + serio_unpin_driver(serio); + serio_unregister_child_port(serio); + serio_pin_driver_uninterruptible(serio); + down(&psmouse_sem); + + if (serio->drv != &psmouse_drv) + return -ENODEV; + + if (psmouse->type == proto->type) + return count; /* switched by other thread */ + } + + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = serio_get_drvdata(serio->parent); + if (parent->pt_deactivate) + parent->pt_deactivate(parent); + } + + if (psmouse->disconnect) + psmouse->disconnect(psmouse); + + psmouse_set_state(psmouse, PSMOUSE_IGNORE); + input_unregister_device(&psmouse->dev); + + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + + if (psmouse_switch_protocol(psmouse, proto) < 0) { + psmouse_reset(psmouse); + /* default to PSMOUSE_PS2 */ + psmouse_switch_protocol(psmouse, &psmouse_protocols[0]); + } + + psmouse_initialize(psmouse); + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + + input_register_device(&psmouse->dev); + printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys); + + if (parent && parent->pt_activate) + parent->pt_activate(parent); + + return count; +} + static ssize_t psmouse_attr_show_rate(struct psmouse *psmouse, char *buf) { return sprintf(buf, "%d\n", psmouse->rate); @@ -969,34 +1220,26 @@ static ssize_t psmouse_attr_set_resetafter(struct psmouse *psmouse, const char * static int psmouse_set_maxproto(const char *val, struct kernel_param *kp) { - int i; + struct psmouse_protocol *proto; if (!val) return -EINVAL; - if (!strncmp(val, "any", 3)) { - *((unsigned int *)kp->arg) = -1U; - return 0; - } + proto = psmouse_protocol_by_name(val, strlen(val)); - for (i = 0; i < ARRAY_SIZE(psmouse_proto_abbrev); i++) { - if (!psmouse_proto_abbrev[i]) - continue; + if (!proto || !proto->maxproto) + return -EINVAL; - if (!strncmp(val, psmouse_proto_abbrev[i], strlen(psmouse_proto_abbrev[i]))) { - *((unsigned int *)kp->arg) = i; - return 0; - } - } + *((unsigned int *)kp->arg) = proto->type; - return -EINVAL; \ + return 0; \ } static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp) { - return sprintf(buffer, "%s\n", - psmouse_max_proto < ARRAY_SIZE(psmouse_proto_abbrev) ? - psmouse_proto_abbrev[psmouse_max_proto] : "any"); + int type = *((unsigned int *)kp->arg); + + return sprintf(buffer, "%s\n", psmouse_protocol_by_type(type)->name); } static int __init psmouse_init(void) diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 79e17a0c466..86691cf4343 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -77,6 +77,8 @@ enum psmouse_type { PSMOUSE_IMEX, PSMOUSE_SYNAPTICS, PSMOUSE_ALPS, + PSMOUSE_LIFEBOOK, + PSMOUSE_AUTO /* This one should always be last */ }; int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command); @@ -99,7 +101,7 @@ static ssize_t psmouse_do_set_##_name(struct device *d, struct device_attribute { \ return psmouse_attr_set_helper(d, b, s, psmouse_attr_set_##_name); \ } \ -static struct device_attribute psmouse_attr_##_name = \ +static struct device_attribute psmouse_attr_##_name = \ __ATTR(_name, S_IWUSR | S_IRUGO, \ psmouse_do_show_##_name, psmouse_do_set_##_name); diff --git a/drivers/input/mouse/rpcmouse.c b/drivers/input/mouse/rpcmouse.c index 7280f68afce..8fe1212b8fd 100644 --- a/drivers/input/mouse/rpcmouse.c +++ b/drivers/input/mouse/rpcmouse.c @@ -59,7 +59,7 @@ static irqreturn_t rpcmouse_irq(int irq, void *dev_id, struct pt_regs *regs) b = (short) (__raw_readl(0xe0310000) ^ 0x70); dx = x - rpcmouse_lastx; - dy = y - rpcmouse_lasty; + dy = y - rpcmouse_lasty; rpcmouse_lastx = x; rpcmouse_lasty = y; diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c index b2cb101c811..f024be9b44d 100644 --- a/drivers/input/mouse/vsxxxaa.c +++ b/drivers/input/mouse/vsxxxaa.c @@ -1,7 +1,7 @@ /* * Driver for DEC VSXXX-AA mouse (hockey-puck mouse, ball or two rollers) - * DEC VSXXX-GA mouse (rectangular mouse, with ball) - * DEC VSXXX-AB tablet (digitizer with hair cross or stylus) + * DEC VSXXX-GA mouse (rectangular mouse, with ball) + * DEC VSXXX-AB tablet (digitizer with hair cross or stylus) * * Copyright (C) 2003-2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de> * diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 062848ac7e6..c6194a9dd17 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -220,6 +220,7 @@ static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_h struct mousedev_list *list; struct mousedev_motion *p; unsigned long flags; + int wake_readers = 0; list_for_each_entry(list, &mousedev->list, node) { spin_lock_irqsave(&list->packet_lock, flags); @@ -255,11 +256,14 @@ static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_h spin_unlock_irqrestore(&list->packet_lock, flags); - if (list->ready) + if (list->ready) { kill_fasync(&list->fasync, SIGIO, POLL_IN); + wake_readers = 1; + } } - wake_up_interruptible(&mousedev->wait); + if (wake_readers) + wake_up_interruptible(&mousedev->wait); } static void mousedev_touchpad_touch(struct mousedev *mousedev, int value) diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index c978657068c..d4c990f7c85 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -29,6 +29,7 @@ MODULE_LICENSE("GPL"); EXPORT_SYMBOL(ps2_init); EXPORT_SYMBOL(ps2_sendbyte); +EXPORT_SYMBOL(ps2_drain); EXPORT_SYMBOL(ps2_command); EXPORT_SYMBOL(ps2_schedule_command); EXPORT_SYMBOL(ps2_handle_ack); @@ -45,11 +46,11 @@ struct ps2work { /* - * ps2_sendbyte() sends a byte to the mouse, and waits for acknowledge. - * It doesn't handle retransmission, though it could - because when there would - * be need for retransmissions, the mouse has to be replaced anyway. + * ps2_sendbyte() sends a byte to the device and waits for acknowledge. + * It doesn't handle retransmission, though it could - because if there + * is a need for retransmissions device has to be replaced anyway. * - * ps2_sendbyte() can only be called from a process context + * ps2_sendbyte() can only be called from a process context. */ int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) @@ -72,6 +73,91 @@ int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) } /* + * ps2_drain() waits for device to transmit requested number of bytes + * and discards them. + */ + +void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) +{ + if (maxbytes > sizeof(ps2dev->cmdbuf)) { + WARN_ON(1); + maxbytes = sizeof(ps2dev->cmdbuf); + } + + down(&ps2dev->cmd_sem); + + serio_pause_rx(ps2dev->serio); + ps2dev->flags = PS2_FLAG_CMD; + ps2dev->cmdcnt = maxbytes; + serio_continue_rx(ps2dev->serio); + + wait_event_timeout(ps2dev->wait, + !(ps2dev->flags & PS2_FLAG_CMD), + msecs_to_jiffies(timeout)); + up(&ps2dev->cmd_sem); +} + +/* + * ps2_is_keyboard_id() checks received ID byte against the list of + * known keyboard IDs. + */ + +static inline int ps2_is_keyboard_id(char id_byte) +{ + static char keyboard_ids[] = { + 0xab, /* Regular keyboards */ + 0xac, /* NCD Sun keyboard */ + 0x2b, /* Trust keyboard, translated */ + 0x5d, /* Trust keyboard */ + 0x60, /* NMB SGI keyboard, translated */ + 0x47, /* NMB SGI keyboard */ + }; + + return memchr(keyboard_ids, id_byte, sizeof(keyboard_ids)) != NULL; +} + +/* + * ps2_adjust_timeout() is called after receiving 1st byte of command + * response and tries to reduce remaining timeout to speed up command + * completion. + */ + +static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout) +{ + switch (command) { + case PS2_CMD_RESET_BAT: + /* + * Device has sent the first response byte after + * reset command, reset is thus done, so we can + * shorten the timeout. + * The next byte will come soon (keyboard) or not + * at all (mouse). + */ + if (timeout > msecs_to_jiffies(100)) + timeout = msecs_to_jiffies(100); + break; + + case PS2_CMD_GETID: + /* + * If device behind the port is not a keyboard there + * won't be 2nd byte of ID response. + */ + if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) { + serio_pause_rx(ps2dev->serio); + ps2dev->flags = ps2dev->cmdcnt = 0; + serio_continue_rx(ps2dev->serio); + timeout = 0; + } + break; + + default: + break; + } + + return timeout; +} + +/* * ps2_command() sends a command and its parameters to the mouse, * then waits for the response and puts it in the param array. * @@ -86,6 +172,11 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) int rc = -1; int i; + if (receive > sizeof(ps2dev->cmdbuf)) { + WARN_ON(1); + return -1; + } + down(&ps2dev->cmd_sem); serio_pause_rx(ps2dev->serio); @@ -101,10 +192,9 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) * ACKing the reset command, and so it can take a long * time before the ACK arrrives. */ - if (command & 0xff) - if (ps2_sendbyte(ps2dev, command & 0xff, - command == PS2_CMD_RESET_BAT ? 1000 : 200)) - goto out; + if (ps2_sendbyte(ps2dev, command & 0xff, + command == PS2_CMD_RESET_BAT ? 1000 : 200)) + goto out; for (i = 0; i < send; i++) if (ps2_sendbyte(ps2dev, param[i], 200)) @@ -120,33 +210,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) if (ps2dev->cmdcnt && timeout > 0) { - if (command == PS2_CMD_RESET_BAT && timeout > msecs_to_jiffies(100)) { - /* - * Device has sent the first response byte - * after a reset command, reset is thus done, - * shorten the timeout. The next byte will come - * soon (keyboard) or not at all (mouse). - */ - timeout = msecs_to_jiffies(100); - } - - if (command == PS2_CMD_GETID && - ps2dev->cmdbuf[receive - 1] != 0xab && /* Regular keyboards */ - ps2dev->cmdbuf[receive - 1] != 0xac && /* NCD Sun keyboard */ - ps2dev->cmdbuf[receive - 1] != 0x2b && /* Trust keyboard, translated */ - ps2dev->cmdbuf[receive - 1] != 0x5d && /* Trust keyboard */ - ps2dev->cmdbuf[receive - 1] != 0x60 && /* NMB SGI keyboard, translated */ - ps2dev->cmdbuf[receive - 1] != 0x47) { /* NMB SGI keyboard */ - /* - * Device behind the port is not a keyboard - * so we don't need to wait for the 2nd byte - * of ID response. - */ - serio_pause_rx(ps2dev->serio); - ps2dev->flags = ps2dev->cmdcnt = 0; - serio_continue_rx(ps2dev->serio); - } - + timeout = ps2_adjust_timeout(ps2dev, command, timeout); wait_event_timeout(ps2dev->wait, !(ps2dev->flags & PS2_FLAG_CMD), timeout); } @@ -160,7 +224,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) rc = 0; -out: + out: serio_pause_rx(ps2dev->serio); ps2dev->flags = 0; serio_continue_rx(ps2dev->serio); diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index 546ce599334..3cdc9cab688 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -226,7 +226,7 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv) input_set_abs_params(&elo->dev, ABS_Y, 96, 4000, 0, 0); input_set_abs_params(&elo->dev, ABS_PRESSURE, 0, 255, 0, 0); break; - + case 1: /* 6-byte protocol */ input_set_abs_params(&elo->dev, ABS_PRESSURE, 0, 15, 0, 0); diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c index acb9137a022..bcfa1e36f95 100644 --- a/drivers/input/touchscreen/h3600_ts_input.c +++ b/drivers/input/touchscreen/h3600_ts_input.c @@ -89,9 +89,9 @@ MODULE_LICENSE("GPL"); #define H3600_SCANCODE_Q 4 /* 4 -> Q button */ #define H3600_SCANCODE_START 5 /* 5 -> start menu */ #define H3600_SCANCODE_UP 6 /* 6 -> up */ -#define H3600_SCANCODE_RIGHT 7 /* 7 -> right */ -#define H3600_SCANCODE_LEFT 8 /* 8 -> left */ -#define H3600_SCANCODE_DOWN 9 /* 9 -> down */ +#define H3600_SCANCODE_RIGHT 7 /* 7 -> right */ +#define H3600_SCANCODE_LEFT 8 /* 8 -> left */ +#define H3600_SCANCODE_DOWN 9 /* 9 -> down */ static char *h3600_name = "H3600 TouchScreen"; @@ -113,7 +113,7 @@ struct h3600_dev { static irqreturn_t action_button_handler(int irq, void *dev_id, struct pt_regs *regs) { - int down = (GPLR & GPIO_BITSY_ACTION_BUTTON) ? 0 : 1; + int down = (GPLR & GPIO_BITSY_ACTION_BUTTON) ? 0 : 1; struct input_dev *dev = (struct input_dev *) dev_id; input_regs(dev, regs); @@ -125,7 +125,7 @@ static irqreturn_t action_button_handler(int irq, void *dev_id, struct pt_regs * static irqreturn_t npower_button_handler(int irq, void *dev_id, struct pt_regs *regs) { - int down = (GPLR & GPIO_BITSY_NPOWER_BUTTON) ? 0 : 1; + int down = (GPLR & GPIO_BITSY_NPOWER_BUTTON) ? 0 : 1; struct input_dev *dev = (struct input_dev *) dev_id; /* @@ -145,8 +145,8 @@ static irqreturn_t npower_button_handler(int irq, void *dev_id, struct pt_regs * static int flite_brightness = 25; enum flite_pwr { - FLITE_PWR_OFF = 0, - FLITE_PWR_ON = 1 + FLITE_PWR_OFF = 0, + FLITE_PWR_ON = 1 }; /* @@ -157,9 +157,9 @@ unsigned int h3600_flite_power(struct input_dev *dev, enum flite_pwr pwr) struct h3600_dev *ts = dev->private; /* Must be in this order */ - ts->serio->write(ts->serio, 1); + ts->serio->write(ts->serio, 1); ts->serio->write(ts->serio, pwr); - ts->serio->write(ts->serio, brightness); + ts->serio->write(ts->serio, brightness); return 0; } @@ -169,26 +169,26 @@ static int h3600ts_pm_callback(struct pm_dev *pm_dev, pm_request_t req, { struct input_dev *dev = (struct input_dev *) data; - switch (req) { - case PM_SUSPEND: /* enter D1-D3 */ - suspended = 1; - h3600_flite_power(dev, FLITE_PWR_OFF); - break; - case PM_BLANK: - if (!suspended) - h3600_flite_power(dev, FLITE_PWR_OFF); - break; - case PM_RESUME: /* enter D0 */ - /* same as unblank */ - case PM_UNBLANK: - if (suspended) { - //initSerial(); - suspended = 0; - } - h3600_flite_power(dev, FLITE_PWR_ON); - break; - } - return 0; + switch (req) { + case PM_SUSPEND: /* enter D1-D3 */ + suspended = 1; + h3600_flite_power(dev, FLITE_PWR_OFF); + break; + case PM_BLANK: + if (!suspended) + h3600_flite_power(dev, FLITE_PWR_OFF); + break; + case PM_RESUME: /* enter D0 */ + /* same as unblank */ + case PM_UNBLANK: + if (suspended) { + //initSerial(); + suspended = 0; + } + h3600_flite_power(dev, FLITE_PWR_ON); + break; + } + return 0; } #endif @@ -199,25 +199,25 @@ static int h3600ts_pm_callback(struct pm_dev *pm_dev, pm_request_t req, */ static void h3600ts_process_packet(struct h3600_dev *ts, struct pt_regs *regs) { - struct input_dev *dev = &ts->dev; + struct input_dev *dev = &ts->dev; static int touched = 0; int key, down = 0; input_regs(dev, regs); - switch (ts->event) { - /* - Buttons - returned as a single byte - 7 6 5 4 3 2 1 0 - S x x x N N N N + switch (ts->event) { + /* + Buttons - returned as a single byte + 7 6 5 4 3 2 1 0 + S x x x N N N N - S switch state ( 0=pressed 1=released) - x Unused. - NNNN switch number 0-15 + S switch state ( 0=pressed 1=released) + x Unused. + NNNN switch number 0-15 - Note: This is true for non interrupt generated key events. - */ - case KEYBD_ID: + Note: This is true for non interrupt generated key events. + */ + case KEYBD_ID: down = (ts->buf[0] & 0x80) ? 0 : 1; switch (ts->buf[0] & 0x7f) { @@ -229,40 +229,40 @@ static void h3600ts_process_packet(struct h3600_dev *ts, struct pt_regs *regs) break; case H3600_SCANCODE_CONTACTS: key = KEY_PROG2; - break; + break; case H3600_SCANCODE_Q: key = KEY_Q; - break; + break; case H3600_SCANCODE_START: key = KEY_PROG3; - break; + break; case H3600_SCANCODE_UP: key = KEY_UP; - break; + break; case H3600_SCANCODE_RIGHT: key = KEY_RIGHT; - break; + break; case H3600_SCANCODE_LEFT: key = KEY_LEFT; - break; + break; case H3600_SCANCODE_DOWN: key = KEY_DOWN; - break; + break; default: key = 0; } - if (key) - input_report_key(dev, key, down); - break; - /* - * Native touchscreen event data is formatted as shown below:- - * - * +-------+-------+-------+-------+ - * | Xmsb | Xlsb | Ymsb | Ylsb | - * +-------+-------+-------+-------+ - * byte 0 1 2 3 - */ - case TOUCHS_ID: + if (key) + input_report_key(dev, key, down); + break; + /* + * Native touchscreen event data is formatted as shown below:- + * + * +-------+-------+-------+-------+ + * | Xmsb | Xlsb | Ymsb | Ylsb | + * +-------+-------+-------+-------+ + * byte 0 1 2 3 + */ + case TOUCHS_ID: if (!touched) { input_report_key(dev, BTN_TOUCH, 1); touched = 1; @@ -272,19 +272,19 @@ static void h3600ts_process_packet(struct h3600_dev *ts, struct pt_regs *regs) unsigned short x, y; x = ts->buf[0]; x <<= 8; x += ts->buf[1]; - y = ts->buf[2]; y <<= 8; y += ts->buf[3]; + y = ts->buf[2]; y <<= 8; y += ts->buf[3]; - input_report_abs(dev, ABS_X, x); - input_report_abs(dev, ABS_Y, y); + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); } else { - input_report_key(dev, BTN_TOUCH, 0); + input_report_key(dev, BTN_TOUCH, 0); touched = 0; } - break; + break; default: /* Send a non input event elsewhere */ break; - } + } input_sync(dev); } @@ -293,7 +293,7 @@ static void h3600ts_process_packet(struct h3600_dev *ts, struct pt_regs *regs) * h3600ts_event() handles events from the input module. */ static int h3600ts_event(struct input_dev *dev, unsigned int type, - unsigned int code, int value) + unsigned int code, int value) { struct h3600_dev *ts = dev->private; @@ -332,41 +332,41 @@ static int state; static irqreturn_t h3600ts_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs) { - struct h3600_dev *ts = serio_get_drvdata(serio); + struct h3600_dev *ts = serio_get_drvdata(serio); /* - * We have a new frame coming in. - */ + * We have a new frame coming in. + */ switch (state) { case STATE_SOF: - if (data == CHAR_SOF) - state = STATE_ID; + if (data == CHAR_SOF) + state = STATE_ID; break; - case STATE_ID: + case STATE_ID: ts->event = (data & 0xf0) >> 4; ts->len = (data & 0xf); ts->idx = 0; if (ts->event >= MAX_ID) { state = STATE_SOF; - break; + break; } ts->chksum = data; - state = (ts->len > 0) ? STATE_DATA : STATE_EOF; + state = (ts->len > 0) ? STATE_DATA : STATE_EOF; break; case STATE_DATA: ts->chksum += data; ts->buf[ts->idx]= data; - if(++ts->idx == ts->len) - state = STATE_EOF; + if (++ts->idx == ts->len) + state = STATE_EOF; break; case STATE_EOF: - state = STATE_SOF; - if (data == CHAR_EOF || data == ts->chksum) + state = STATE_SOF; + if (data == CHAR_EOF || data == ts->chksum) h3600ts_process_packet(ts, regs); - break; - default: - printk("Error3\n"); - break; + break; + default: + printk("Error3\n"); + break; } return IRQ_HANDLED; @@ -390,10 +390,10 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv) init_input_dev(&ts->dev); /* Device specific stuff */ - set_GPIO_IRQ_edge(GPIO_BITSY_ACTION_BUTTON, GPIO_BOTH_EDGES); - set_GPIO_IRQ_edge(GPIO_BITSY_NPOWER_BUTTON, GPIO_RISING_EDGE); + set_GPIO_IRQ_edge(GPIO_BITSY_ACTION_BUTTON, GPIO_BOTH_EDGES); + set_GPIO_IRQ_edge(GPIO_BITSY_NPOWER_BUTTON, GPIO_RISING_EDGE); - if (request_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, action_button_handler, + if (request_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, action_button_handler, SA_SHIRQ | SA_INTERRUPT | SA_SAMPLE_RANDOM, "h3600_action", &ts->dev)) { printk(KERN_ERR "h3600ts.c: Could not allocate Action Button IRQ!\n"); @@ -401,7 +401,7 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv) return -EBUSY; } - if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler, + if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler, SA_SHIRQ | SA_INTERRUPT | SA_SAMPLE_RANDOM, "h3600_suspend", &ts->dev)) { free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, &ts->dev); @@ -433,7 +433,7 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv) sprintf(ts->phys, "%s/input0", serio->phys); - ts->dev.event = h3600ts_event; + ts->dev.event = h3600ts_event; ts->dev.private = ts; ts->dev.name = h3600_name; ts->dev.phys = ts->phys; @@ -446,8 +446,8 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv) err = serio_open(serio, drv); if (err) { - free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts); - free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts); + free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts); + free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts); serio_set_drvdata(serio, NULL); kfree(ts); return err; diff --git a/drivers/input/touchscreen/mk712.c b/drivers/input/touchscreen/mk712.c index 2d14a57a05e..afaaebe5225 100644 --- a/drivers/input/touchscreen/mk712.c +++ b/drivers/input/touchscreen/mk712.c @@ -17,7 +17,7 @@ * found in Gateway AOL Connected Touchpad computers. * * Documentation for ICS MK712 can be found at: - * http://www.icst.com/pdf/mk712.pdf + * http://www.icst.com/pdf/mk712.pdf */ /* @@ -77,7 +77,6 @@ MODULE_PARM_DESC(irq, "IRQ of MK712 touchscreen controller"); #define MK712_READ_ONE_POINT 0x20 #define MK712_POWERUP 0x40 -static int mk712_used = 0; static struct input_dev mk712_dev; static DEFINE_SPINLOCK(mk712_lock); @@ -130,17 +129,14 @@ static int mk712_open(struct input_dev *dev) spin_lock_irqsave(&mk712_lock, flags); - if (!mk712_used++) { + outb(0, mk712_io + MK712_CONTROL); /* Reset */ - outb(0, mk712_io + MK712_CONTROL); /* Reset */ + outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE | + MK712_INT_ON_CHANGE_IN_TOUCH_STATUS | + MK712_ENABLE_PERIODIC_CONVERSIONS | + MK712_POWERUP, mk712_io + MK712_CONTROL); - outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE | - MK712_INT_ON_CHANGE_IN_TOUCH_STATUS | - MK712_ENABLE_PERIODIC_CONVERSIONS | - MK712_POWERUP, mk712_io + MK712_CONTROL); - - outb(10, mk712_io + MK712_RATE); /* 187 points per second */ - } + outb(10, mk712_io + MK712_RATE); /* 187 points per second */ spin_unlock_irqrestore(&mk712_lock, flags); @@ -153,8 +149,7 @@ static void mk712_close(struct input_dev *dev) spin_lock_irqsave(&mk712_lock, flags); - if (!--mk712_used) - outb(0, mk712_io + MK712_CONTROL); + outb(0, mk712_io + MK712_CONTROL); spin_unlock_irqrestore(&mk712_lock, flags); } diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index f9383e7f34f..1b70f8b0feb 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -7,7 +7,7 @@ menu "Video For Linux" comment "Video Adapters" -config CONFIG_TUNER_MULTI_I2C +config TUNER_MULTI_I2C bool "Enable support for multiple I2C devices on Video Adapters (EXPERIMENTAL)" depends on VIDEO_DEV && EXPERIMENTAL ---help--- diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index a61d4433a98..a708a1dbb53 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_USB_MOUSE) += input/ obj-$(CONFIG_USB_MTOUCH) += input/ obj-$(CONFIG_USB_POWERMATE) += input/ obj-$(CONFIG_USB_WACOM) += input/ +obj-$(CONFIG_USB_ACECAD) += input/ obj-$(CONFIG_USB_XPAD) += input/ obj-$(CONFIG_USB_DABUSB) += media/ diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig index d28e7eab6f9..fd59f6bdd67 100644 --- a/drivers/usb/input/Kconfig +++ b/drivers/usb/input/Kconfig @@ -151,6 +151,18 @@ config USB_WACOM To compile this driver as a module, choose M here: the module will be called wacom. +config USB_ACECAD + tristate "Acecad Flair tablet support" + depends on USB && INPUT + help + Say Y here if you want to use the USB version of the Acecad Flair + tablet. Make sure to say Y to "Mouse support" + (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" + (CONFIG_INPUT_EVDEV) as well. + + To compile this driver as a module, choose M here: the + module will be called acecad. + config USB_KBTAB tristate "KB Gear JamStudio tablet support" depends on USB && INPUT @@ -190,6 +202,18 @@ config USB_MTOUCH To compile this driver as a module, choose M here: the module will be called mtouchusb. +config USB_ITMTOUCH + tristate "ITM Touch USB Touchscreen Driver" + depends on USB && INPUT + ---help--- + Say Y here if you want to use a ITM Touch USB + Touchscreen controller. + + This touchscreen is used in LG 1510SF monitors. + + To compile this driver as a module, choose M here: the + module will be called itmtouch. + config USB_EGALAX tristate "eGalax TouchKit USB Touchscreen Driver" depends on USB && INPUT diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile index 6bcedd16b0a..831b2b0f1f0 100644 --- a/drivers/usb/input/Makefile +++ b/drivers/usb/input/Makefile @@ -33,7 +33,9 @@ obj-$(CONFIG_USB_KBD) += usbkbd.o obj-$(CONFIG_USB_KBTAB) += kbtab.o obj-$(CONFIG_USB_MOUSE) += usbmouse.o obj-$(CONFIG_USB_MTOUCH) += mtouchusb.o +obj-$(CONFIG_USB_ITMTOUCH) += itmtouch.o obj-$(CONFIG_USB_EGALAX) += touchkitusb.o obj-$(CONFIG_USB_POWERMATE) += powermate.o obj-$(CONFIG_USB_WACOM) += wacom.o +obj-$(CONFIG_USB_ACECAD) += acecad.o obj-$(CONFIG_USB_XPAD) += xpad.o diff --git a/drivers/usb/input/acecad.c b/drivers/usb/input/acecad.c new file mode 100644 index 00000000000..ebcf7c95580 --- /dev/null +++ b/drivers/usb/input/acecad.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2001-2005 Edouard TISSERANT <edouard.tisserant@wanadoo.fr> + * Copyright (c) 2004-2005 Stephane VOLTZ <svoltz@numericable.fr> + * + * USB Acecad "Acecad Flair" tablet support + * + * Changelog: + * v3.2 - Added sysfs support + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> + +/* + * Version Information + */ +#define DRIVER_VERSION "v3.2" +#define DRIVER_DESC "USB Acecad Flair tablet driver" +#define DRIVER_LICENSE "GPL" +#define DRIVER_AUTHOR "Edouard TISSERANT <edouard.tisserant@wanadoo.fr>" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +#define USB_VENDOR_ID_ACECAD 0x0460 +#define USB_DEVICE_ID_FLAIR 0x0004 +#define USB_DEVICE_ID_302 0x0008 + +struct usb_acecad { + char name[128]; + char phys[64]; + struct usb_device *usbdev; + struct input_dev dev; + struct urb *irq; + + signed char *data; + dma_addr_t data_dma; +}; + +static void usb_acecad_irq(struct urb *urb, struct pt_regs *regs) +{ + struct usb_acecad *acecad = urb->context; + unsigned char *data = acecad->data; + struct input_dev *dev = &acecad->dev; + int prox, status; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + goto resubmit; + } + + prox = (data[0] & 0x04) >> 2; + input_report_key(dev, BTN_TOOL_PEN, prox); + + if (prox) { + int x = data[1] | (data[2] << 8); + int y = data[3] | (data[4] << 8); + /*Pressure should compute the same way for flair and 302*/ + int pressure = data[5] | ((int)data[6] << 8); + int touch = data[0] & 0x01; + int stylus = (data[0] & 0x10) >> 4; + int stylus2 = (data[0] & 0x20) >> 5; + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_report_abs(dev, ABS_PRESSURE, pressure); + input_report_key(dev, BTN_TOUCH, touch); + input_report_key(dev, BTN_STYLUS, stylus); + input_report_key(dev, BTN_STYLUS2, stylus2); + } + + /* event termination */ + input_sync(dev); + +resubmit: + status = usb_submit_urb (urb, GFP_ATOMIC); + if (status) + err ("can't resubmit intr, %s-%s/input0, status %d", + acecad->usbdev->bus->bus_name, acecad->usbdev->devpath, status); +} + +static int usb_acecad_open(struct input_dev *dev) +{ + struct usb_acecad *acecad = dev->private; + + acecad->irq->dev = acecad->usbdev; + if (usb_submit_urb(acecad->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void usb_acecad_close(struct input_dev *dev) +{ + struct usb_acecad *acecad = dev->private; + + usb_kill_urb(acecad->irq); +} + +static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *interface = intf->cur_altsetting; + struct usb_endpoint_descriptor *endpoint; + struct usb_acecad *acecad; + int pipe, maxp; + char path[64]; + + if (interface->desc.bNumEndpoints != 1) + return -ENODEV; + + endpoint = &interface->endpoint[0].desc; + + if (!(endpoint->bEndpointAddress & 0x80)) + return -ENODEV; + + if ((endpoint->bmAttributes & 3) != 3) + return -ENODEV; + + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + acecad = kcalloc(1, sizeof(struct usb_acecad), GFP_KERNEL); + if (!acecad) + return -ENOMEM; + + acecad->data = usb_buffer_alloc(dev, 8, SLAB_KERNEL, &acecad->data_dma); + if (!acecad->data) + goto fail1; + + acecad->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!acecad->irq) + goto fail2; + + if (dev->manufacturer) + strlcpy(acecad->name, dev->manufacturer, sizeof(acecad->name)); + + if (dev->product) { + if (dev->manufacturer) + strlcat(acecad->name, " ", sizeof(acecad->name)); + strlcat(acecad->name, dev->product, sizeof(acecad->name)); + } + + usb_make_path(dev, path, sizeof(path)); + snprintf(acecad->phys, sizeof(acecad->phys), "%s/input0", path); + + acecad->usbdev = dev; + + acecad->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + acecad->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE); + acecad->dev.keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); + acecad->dev.keybit[LONG(BTN_DIGI)] = BIT(BTN_TOOL_PEN) |BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2); + + switch (id->driver_info) { + case 0: + acecad->dev.absmax[ABS_X] = 5000; + acecad->dev.absmax[ABS_Y] = 3750; + acecad->dev.absmax[ABS_PRESSURE] = 512; + if (!strlen(acecad->name)) + snprintf(acecad->name, sizeof(acecad->name), + "USB Acecad Flair Tablet %04x:%04x", + dev->descriptor.idVendor, dev->descriptor.idProduct); + break; + case 1: + acecad->dev.absmax[ABS_X] = 3000; + acecad->dev.absmax[ABS_Y] = 2250; + acecad->dev.absmax[ABS_PRESSURE] = 1024; + if (!strlen(acecad->name)) + snprintf(acecad->name, sizeof(acecad->name), + "USB Acecad 302 Tablet %04x:%04x", + dev->descriptor.idVendor, dev->descriptor.idProduct); + break; + } + + acecad->dev.absfuzz[ABS_X] = 4; + acecad->dev.absfuzz[ABS_Y] = 4; + + acecad->dev.private = acecad; + acecad->dev.open = usb_acecad_open; + acecad->dev.close = usb_acecad_close; + + acecad->dev.name = acecad->name; + acecad->dev.phys = acecad->phys; + acecad->dev.id.bustype = BUS_USB; + acecad->dev.id.vendor = le16_to_cpu(dev->descriptor.idVendor); + acecad->dev.id.product = le16_to_cpu(dev->descriptor.idProduct); + acecad->dev.id.version = le16_to_cpu(dev->descriptor.bcdDevice); + acecad->dev.dev = &intf->dev; + + usb_fill_int_urb(acecad->irq, dev, pipe, + acecad->data, maxp > 8 ? 8 : maxp, + usb_acecad_irq, acecad, endpoint->bInterval); + acecad->irq->transfer_dma = acecad->data_dma; + acecad->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + input_register_device(&acecad->dev); + + printk(KERN_INFO "input: %s with packet size %d on %s\n", + acecad->name, maxp, path); + + usb_set_intfdata(intf, acecad); + + return 0; + + fail2: usb_buffer_free(dev, 8, acecad->data, acecad->data_dma); + fail1: kfree(acecad); + return -ENOMEM; +} + +static void usb_acecad_disconnect(struct usb_interface *intf) +{ + struct usb_acecad *acecad = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + if (acecad) { + usb_kill_urb(acecad->irq); + input_unregister_device(&acecad->dev); + usb_free_urb(acecad->irq); + usb_buffer_free(interface_to_usbdev(intf), 10, acecad->data, acecad->data_dma); + kfree(acecad); + } +} + +static struct usb_device_id usb_acecad_id_table [] = { + { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_FLAIR), .driver_info = 0 }, + { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_302), .driver_info = 1 }, + { } +}; + +MODULE_DEVICE_TABLE(usb, usb_acecad_id_table); + +static struct usb_driver usb_acecad_driver = { + .owner = THIS_MODULE, + .name = "usb_acecad", + .probe = usb_acecad_probe, + .disconnect = usb_acecad_disconnect, + .id_table = usb_acecad_id_table, +}; + +static int __init usb_acecad_init(void) +{ + int result = usb_register(&usb_acecad_driver); + if (result == 0) + info(DRIVER_VERSION ":" DRIVER_DESC); + return result; +} + +static void __exit usb_acecad_exit(void) +{ + usb_deregister(&usb_acecad_driver); +} + +module_init(usb_acecad_init); +module_exit(usb_acecad_exit); diff --git a/drivers/usb/input/aiptek.c b/drivers/usb/input/aiptek.c index e991f7ed733..6bb0f25e8e9 100644 --- a/drivers/usb/input/aiptek.c +++ b/drivers/usb/input/aiptek.c @@ -1,7 +1,7 @@ /* * Native support for the Aiptek HyperPen USB Tablets * (4000U/5000U/6000U/8000U/12000U) - * + * * Copyright (c) 2001 Chris Atenasio <chris@crud.net> * Copyright (c) 2002-2004 Bryan W. Headley <bwheadley@earthlink.net> * @@ -31,7 +31,7 @@ * - Added support for the sysfs interface, deprecating the * procfs interface for 2.5.x kernel. Also added support for * Wheel command. Bryan W. Headley July-15-2003. - * v1.2 - Reworked jitter timer as a kernel thread. + * v1.2 - Reworked jitter timer as a kernel thread. * Bryan W. Headley November-28-2003/Jan-10-2004. * v1.3 - Repaired issue of kernel thread going nuts on single-processor * machines, introduced programmableDelay as a command line @@ -49,10 +49,10 @@ * NOTE: * This kernel driver is augmented by the "Aiptek" XFree86 input * driver for your X server, as well as the Gaiptek GUI Front-end - * "Tablet Manager". - * These three products are highly interactive with one another, + * "Tablet Manager". + * These three products are highly interactive with one another, * so therefore it's easier to document them all as one subsystem. - * Please visit the project's "home page", located at, + * Please visit the project's "home page", located at, * http://aiptektablet.sourceforge.net. * * This program is free software; you can redistribute it and/or modify @@ -156,7 +156,7 @@ * Command/Data Description Return Bytes Return Value * 0x10/0x00 SwitchToMouse 0 * 0x10/0x01 SwitchToTablet 0 - * 0x18/0x04 SetResolution 0 + * 0x18/0x04 SetResolution 0 * 0x12/0xFF AutoGainOn 0 * 0x17/0x00 FilterOn 0 * 0x01/0x00 GetXExtension 2 MaxX @@ -247,7 +247,7 @@ #define AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE 2 #define AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED 3 - /* Time to wait (in ms) to help mask hand jittering + /* Time to wait (in ms) to help mask hand jittering * when pressing the stylus buttons. */ #define AIPTEK_JITTER_DELAY_DEFAULT 50 @@ -324,7 +324,6 @@ struct aiptek { struct aiptek_settings curSetting; /* tablet's current programmable */ struct aiptek_settings newSetting; /* ... and new param settings */ unsigned int ifnum; /* interface number for IO */ - int openCount; /* module use counter */ int diagnostic; /* tablet diagnostic codes */ unsigned long eventCount; /* event count */ int inDelay; /* jitter: in jitter delay? */ @@ -791,7 +790,7 @@ exit: * specific Aiptek model numbers, because there has been overlaps, * use, and reuse of id's in existing models. Certain models have * been known to use more than one ID, indicative perhaps of - * manufacturing revisions. In any event, we consider these + * manufacturing revisions. In any event, we consider these * IDs to not be model-specific nor unique. */ static const struct usb_device_id aiptek_ids[] = { @@ -814,15 +813,9 @@ static int aiptek_open(struct input_dev *inputdev) { struct aiptek *aiptek = inputdev->private; - if (aiptek->openCount++ > 0) { - return 0; - } - aiptek->urb->dev = aiptek->usbdev; - if (usb_submit_urb(aiptek->urb, GFP_KERNEL) != 0) { - aiptek->openCount--; + if (usb_submit_urb(aiptek->urb, GFP_KERNEL) != 0) return -EIO; - } return 0; } @@ -834,13 +827,11 @@ static void aiptek_close(struct input_dev *inputdev) { struct aiptek *aiptek = inputdev->private; - if (--aiptek->openCount == 0) { - usb_kill_urb(aiptek->urb); - } + usb_kill_urb(aiptek->urb); } /*********************************************************************** - * aiptek_set_report and aiptek_get_report() are borrowed from Linux 2.4.x, + * aiptek_set_report and aiptek_get_report() are borrowed from Linux 2.4.x, * where they were known as usb_set_report and usb_get_report. */ static int @@ -2252,7 +2243,6 @@ static void aiptek_disconnect(struct usb_interface *intf) AIPTEK_PACKET_LENGTH, aiptek->data, aiptek->data_dma); kfree(aiptek); - aiptek = NULL; } } diff --git a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c index 860df26323b..db95c975952 100644 --- a/drivers/usb/input/ati_remote.c +++ b/drivers/usb/input/ati_remote.c @@ -1,15 +1,15 @@ -/* +/* * USB ATI Remote support * * Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net> * Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev * * This 2.2.0 version is a rewrite / cleanup of the 2.1.1 driver, including - * porting to the 2.6 kernel interfaces, along with other modification + * porting to the 2.6 kernel interfaces, along with other modification * to better match the style of the existing usb/input drivers. However, the * protocol and hardware handling is essentially unchanged from 2.1.1. - * - * The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by + * + * The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by * Vojtech Pavlik. * * Changes: @@ -23,64 +23,64 @@ * Added support for the "Lola" remote contributed by: * Seth Cohn <sethcohn@yahoo.com> * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Hardware & software notes * - * These remote controls are distributed by ATI as part of their - * "All-In-Wonder" video card packages. The receiver self-identifies as a + * These remote controls are distributed by ATI as part of their + * "All-In-Wonder" video card packages. The receiver self-identifies as a * "USB Receiver" with manufacturer "X10 Wireless Technology Inc". * - * The "Lola" remote is available from X10. See: + * The "Lola" remote is available from X10. See: * http://www.x10.com/products/lola_sg1.htm * The Lola is similar to the ATI remote but has no mouse support, and slightly * different keys. * - * It is possible to use multiple receivers and remotes on multiple computers + * It is possible to use multiple receivers and remotes on multiple computers * simultaneously by configuring them to use specific channels. - * - * The RF protocol used by the remote supports 16 distinct channels, 1 to 16. - * Actually, it may even support more, at least in some revisions of the + * + * The RF protocol used by the remote supports 16 distinct channels, 1 to 16. + * Actually, it may even support more, at least in some revisions of the * hardware. * * Each remote can be configured to transmit on one channel as follows: - * - Press and hold the "hand icon" button. - * - When the red LED starts to blink, let go of the "hand icon" button. - * - When it stops blinking, input the channel code as two digits, from 01 + * - Press and hold the "hand icon" button. + * - When the red LED starts to blink, let go of the "hand icon" button. + * - When it stops blinking, input the channel code as two digits, from 01 * to 16, and press the hand icon again. - * + * * The timing can be a little tricky. Try loading the module with debug=1 * to have the kernel print out messages about the remote control number * and mask. Note: debugging prints remote numbers as zero-based hexadecimal. * * The driver has a "channel_mask" parameter. This bitmask specifies which - * channels will be ignored by the module. To mask out channels, just add + * channels will be ignored by the module. To mask out channels, just add * all the 2^channel_number values together. * * For instance, set channel_mask = 2^4 = 16 (binary 10000) to make ati_remote - * ignore signals coming from remote controls transmitting on channel 4, but + * ignore signals coming from remote controls transmitting on channel 4, but * accept all other channels. * - * Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be + * Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be * ignored. * - * The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this + * The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this * parameter are unused. * */ @@ -99,13 +99,13 @@ /* * Module and Version Information, Module Parameters */ - -#define ATI_REMOTE_VENDOR_ID 0x0bc7 -#define ATI_REMOTE_PRODUCT_ID 0x004 -#define LOLA_REMOTE_PRODUCT_ID 0x002 + +#define ATI_REMOTE_VENDOR_ID 0x0bc7 +#define ATI_REMOTE_PRODUCT_ID 0x004 +#define LOLA_REMOTE_PRODUCT_ID 0x002 #define MEDION_REMOTE_PRODUCT_ID 0x006 -#define DRIVER_VERSION "2.2.1" +#define DRIVER_VERSION "2.2.1" #define DRIVER_AUTHOR "Torrey Hoffman <thoffman@arnor.net>" #define DRIVER_DESC "ATI/X10 RF USB Remote Control" @@ -113,18 +113,18 @@ #define DATA_BUFSIZE 63 /* size of URB data buffers */ #define ATI_INPUTNUM 1 /* Which input device to register as */ -static unsigned long channel_mask = 0; +static unsigned long channel_mask; module_param(channel_mask, ulong, 0444); MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore"); -static int debug = 0; +static int debug; module_param(debug, int, 0444); MODULE_PARM_DESC(debug, "Enable extra debug messages and information"); #define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0) #undef err #define err(format, arg...) printk(KERN_ERR format , ## arg) - + static struct usb_device_id ati_remote_table[] = { { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) }, { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID) }, @@ -148,7 +148,7 @@ static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 }; /* Acceleration curve for directional control pad */ static char accel[] = { 1, 2, 4, 6, 9, 13, 20 }; -/* Duplicate event filtering time. +/* Duplicate event filtering time. * Sequential, identical KIND_FILTERED inputs with less than * FILTER_TIME jiffies between them are considered as repeat * events. The hardware generates 5 events for the first keypress @@ -161,10 +161,10 @@ static char accel[] = { 1, 2, 4, 6, 9, 13, 20 }; static DECLARE_MUTEX(disconnect_sem); struct ati_remote { - struct input_dev idev; + struct input_dev idev; struct usb_device *udev; struct usb_interface *interface; - + struct urb *irq_urb; struct urb *out_urb; struct usb_endpoint_descriptor *endpoint_in; @@ -174,13 +174,11 @@ struct ati_remote { dma_addr_t inbuf_dma; dma_addr_t outbuf_dma; - int open; /* open counter */ - unsigned char old_data[2]; /* Detect duplicate events */ unsigned long old_jiffies; unsigned long acc_jiffies; /* handle acceleration */ unsigned int repeat_count; - + char name[NAME_BUFSIZE]; char phys[NAME_BUFSIZE]; @@ -206,14 +204,14 @@ static struct int type; unsigned int code; int value; -} ati_remote_tbl[] = +} ati_remote_tbl[] = { /* Directional control pad axes */ {KIND_ACCEL, 0x35, 0x70, EV_REL, REL_X, -1}, /* left */ {KIND_ACCEL, 0x36, 0x71, EV_REL, REL_X, 1}, /* right */ {KIND_ACCEL, 0x37, 0x72, EV_REL, REL_Y, -1}, /* up */ {KIND_ACCEL, 0x38, 0x73, EV_REL, REL_Y, 1}, /* down */ - /* Directional control pad diagonals */ + /* Directional control pad diagonals */ {KIND_LU, 0x39, 0x74, EV_REL, 0, 0}, /* left up */ {KIND_RU, 0x3a, 0x75, EV_REL, 0, 0}, /* right up */ {KIND_LD, 0x3c, 0x77, EV_REL, 0, 0}, /* left down */ @@ -225,7 +223,7 @@ static struct {KIND_LITERAL, 0x41, 0x7c, EV_KEY, BTN_RIGHT, 1},/* right btn down */ {KIND_LITERAL, 0x42, 0x7d, EV_KEY, BTN_RIGHT, 0},/* right btn up */ - /* Artificial "doubleclick" events are generated by the hardware. + /* Artificial "doubleclick" events are generated by the hardware. * They are mapped to the "side" and "extra" mouse buttons here. */ {KIND_FILTERED, 0x3f, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */ {KIND_FILTERED, 0x43, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */ @@ -273,15 +271,15 @@ static struct {KIND_FILTERED, 0xea, 0x25, EV_KEY, KEY_PLAY, 1}, /* ( >) */ {KIND_FILTERED, 0xe9, 0x24, EV_KEY, KEY_REWIND, 1}, /* (<<) */ {KIND_FILTERED, 0xeb, 0x26, EV_KEY, KEY_FORWARD, 1}, /* (>>) */ - {KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1}, /* ([]) */ + {KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1}, /* ([]) */ {KIND_FILTERED, 0xee, 0x29, EV_KEY, KEY_PAUSE, 1}, /* ('') */ {KIND_FILTERED, 0xf0, 0x2b, EV_KEY, KEY_PREVIOUS, 1}, /* (<-) */ {KIND_FILTERED, 0xef, 0x2a, EV_KEY, KEY_NEXT, 1}, /* (>+) */ {KIND_FILTERED, 0xf2, 0x2D, EV_KEY, KEY_INFO, 1}, /* PLAYING */ {KIND_FILTERED, 0xf3, 0x2E, EV_KEY, KEY_HOME, 1}, /* TOP */ {KIND_FILTERED, 0xf4, 0x2F, EV_KEY, KEY_END, 1}, /* END */ - {KIND_FILTERED, 0xf5, 0x30, EV_KEY, KEY_SELECT, 1}, /* SELECT */ - + {KIND_FILTERED, 0xf5, 0x30, EV_KEY, KEY_SELECT, 1}, /* SELECT */ + {KIND_END, 0x00, 0x00, EV_MAX + 1, 0, 0} }; @@ -315,7 +313,7 @@ static void ati_remote_dump(unsigned char *data, unsigned int len) if ((len == 1) && (data[0] != (unsigned char)0xff) && (data[0] != 0x00)) warn("Weird byte 0x%02x", data[0]); else if (len == 4) - warn("Weird key %02x %02x %02x %02x", + warn("Weird key %02x %02x %02x %02x", data[0], data[1], data[2], data[3]); else warn("Weird data, len=%d %02x %02x %02x %02x %02x %02x ...", @@ -328,25 +326,16 @@ static void ati_remote_dump(unsigned char *data, unsigned int len) static int ati_remote_open(struct input_dev *inputdev) { struct ati_remote *ati_remote = inputdev->private; - int retval = 0; - - down(&disconnect_sem); - - if (ati_remote->open++) - goto exit; /* On first open, submit the read urb which was set up previously. */ ati_remote->irq_urb->dev = ati_remote->udev; if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) { - dev_err(&ati_remote->interface->dev, + dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb failed!\n", __FUNCTION__); - ati_remote->open--; - retval = -EIO; + return -EIO; } -exit: - up(&disconnect_sem); - return retval; + return 0; } /* @@ -355,9 +344,8 @@ exit: static void ati_remote_close(struct input_dev *inputdev) { struct ati_remote *ati_remote = inputdev->private; - - if (!--ati_remote->open) - usb_kill_urb(ati_remote->irq_urb); + + usb_kill_urb(ati_remote->irq_urb); } /* @@ -366,13 +354,13 @@ static void ati_remote_close(struct input_dev *inputdev) static void ati_remote_irq_out(struct urb *urb, struct pt_regs *regs) { struct ati_remote *ati_remote = urb->context; - + if (urb->status) { dev_dbg(&ati_remote->interface->dev, "%s: status %d\n", __FUNCTION__, urb->status); return; } - + ati_remote->send_flags |= SEND_FLAG_COMPLETE; wmb(); wake_up(&ati_remote->wait); @@ -380,16 +368,16 @@ static void ati_remote_irq_out(struct urb *urb, struct pt_regs *regs) /* * ati_remote_sendpacket - * + * * Used to send device initialization strings */ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data) { int retval = 0; - + /* Set up out_urb */ memcpy(ati_remote->out_urb->transfer_buffer + 1, data, LO(cmd)); - ((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd); + ((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd); ati_remote->out_urb->transfer_buffer_length = LO(cmd) + 1; ati_remote->out_urb->dev = ati_remote->udev; @@ -397,17 +385,17 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigne retval = usb_submit_urb(ati_remote->out_urb, GFP_ATOMIC); if (retval) { - dev_dbg(&ati_remote->interface->dev, + dev_dbg(&ati_remote->interface->dev, "sendpacket: usb_submit_urb failed: %d\n", retval); return retval; } wait_event_timeout(ati_remote->wait, ((ati_remote->out_urb->status != -EINPROGRESS) || - (ati_remote->send_flags & SEND_FLAG_COMPLETE)), + (ati_remote->send_flags & SEND_FLAG_COMPLETE)), HZ); usb_kill_urb(ati_remote->out_urb); - + return retval; } @@ -419,15 +407,15 @@ static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2) int i; for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) { - /* - * Decide if the table entry matches the remote input. + /* + * Decide if the table entry matches the remote input. */ if ((((ati_remote_tbl[i].data1 & 0x0f) == (d1 & 0x0f))) && - ((((ati_remote_tbl[i].data1 >> 4) - - (d1 >> 4) + rem) & 0x0f) == 0x0f) && + ((((ati_remote_tbl[i].data1 >> 4) - + (d1 >> 4) + rem) & 0x0f) == 0x0f) && (ati_remote_tbl[i].data2 == d2)) return i; - + } return -1; } @@ -435,16 +423,16 @@ static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2) /* * ati_remote_report_input */ -static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) +static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) { struct ati_remote *ati_remote = urb->context; unsigned char *data= ati_remote->inbuf; - struct input_dev *dev = &ati_remote->idev; + struct input_dev *dev = &ati_remote->idev; int index, acc; int remote_num; - + /* Deal with strange looking inputs */ - if ( (urb->actual_length != 4) || (data[0] != 0x14) || + if ( (urb->actual_length != 4) || (data[0] != 0x14) || ((data[3] & 0x0f) != 0x00) ) { ati_remote_dump(data, urb->actual_length); return; @@ -453,7 +441,7 @@ static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) /* Mask unwanted remote channels. */ /* note: remote_num is 0-based, channel 1 on remote == 0 here */ remote_num = (data[3] >> 4) & 0x0f; - if (channel_mask & (1 << (remote_num + 1))) { + if (channel_mask & (1 << (remote_num + 1))) { dbginfo(&ati_remote->interface->dev, "Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n", remote_num, data[1], data[2], channel_mask); @@ -463,37 +451,36 @@ static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) /* Look up event code index in translation table */ index = ati_remote_event_lookup(remote_num, data[1], data[2]); if (index < 0) { - dev_warn(&ati_remote->interface->dev, - "Unknown input from channel 0x%02x: data %02x,%02x\n", + dev_warn(&ati_remote->interface->dev, + "Unknown input from channel 0x%02x: data %02x,%02x\n", remote_num, data[1], data[2]); return; - } - dbginfo(&ati_remote->interface->dev, + } + dbginfo(&ati_remote->interface->dev, "channel 0x%02x; data %02x,%02x; index %d; keycode %d\n", remote_num, data[1], data[2], index, ati_remote_tbl[index].code); - + if (ati_remote_tbl[index].kind == KIND_LITERAL) { input_regs(dev, regs); input_event(dev, ati_remote_tbl[index].type, ati_remote_tbl[index].code, ati_remote_tbl[index].value); input_sync(dev); - + ati_remote->old_jiffies = jiffies; return; } - + if (ati_remote_tbl[index].kind == KIND_FILTERED) { /* Filter duplicate events which happen "too close" together. */ - if ((ati_remote->old_data[0] == data[1]) && - (ati_remote->old_data[1] == data[2]) && - ((ati_remote->old_jiffies + FILTER_TIME) > jiffies)) { + if ((ati_remote->old_data[0] == data[1]) && + (ati_remote->old_data[1] == data[2]) && + ((ati_remote->old_jiffies + FILTER_TIME) > jiffies)) { ati_remote->repeat_count++; - } - else { + } else { ati_remote->repeat_count = 0; } - + ati_remote->old_data[0] = data[1]; ati_remote->old_data[1] = data[2]; ati_remote->old_jiffies = jiffies; @@ -501,7 +488,7 @@ static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) if ((ati_remote->repeat_count > 0) && (ati_remote->repeat_count < 5)) return; - + input_regs(dev, regs); input_event(dev, ati_remote_tbl[index].type, @@ -511,13 +498,13 @@ static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) input_sync(dev); return; - } - - /* + } + + /* * Other event kinds are from the directional control pad, and have an * acceleration factor applied to them. Without this acceleration, the * control pad is mostly unusable. - * + * * If elapsed time since last event is > 1/4 second, user "stopped", * so reset acceleration. Otherwise, user is probably holding the control * pad down, so we increase acceleration, ramping up over two seconds to @@ -559,7 +546,7 @@ static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) input_report_rel(dev, REL_Y, acc); break; default: - dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n", + dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n", ati_remote_tbl[index].kind); } input_sync(dev); @@ -586,12 +573,12 @@ static void ati_remote_irq_in(struct urb *urb, struct pt_regs *regs) case -ESHUTDOWN: dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n", __FUNCTION__); - return; + return; default: /* error */ - dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n", + dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n", __FUNCTION__, urb->status); } - + retval = usb_submit_urb(urb, SLAB_ATOMIC); if (retval) dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n", @@ -603,8 +590,6 @@ static void ati_remote_irq_in(struct urb *urb, struct pt_regs *regs) */ static void ati_remote_delete(struct ati_remote *ati_remote) { - if (!ati_remote) return; - if (ati_remote->irq_urb) usb_kill_urb(ati_remote->irq_urb); @@ -614,16 +599,16 @@ static void ati_remote_delete(struct ati_remote *ati_remote) input_unregister_device(&ati_remote->idev); if (ati_remote->inbuf) - usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, + usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, ati_remote->inbuf, ati_remote->inbuf_dma); - + if (ati_remote->outbuf) - usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, + usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, ati_remote->outbuf, ati_remote->outbuf_dma); - + if (ati_remote->irq_urb) usb_free_urb(ati_remote->irq_urb); - + if (ati_remote->out_urb) usb_free_urb(ati_remote->out_urb); @@ -636,21 +621,21 @@ static void ati_remote_input_init(struct ati_remote *ati_remote) int i; idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); - idev->keybit[LONG(BTN_MOUSE)] = ( BIT(BTN_LEFT) | BIT(BTN_RIGHT) | + idev->keybit[LONG(BTN_MOUSE)] = ( BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_SIDE) | BIT(BTN_EXTRA) ); idev->relbit[0] = BIT(REL_X) | BIT(REL_Y); for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) if (ati_remote_tbl[i].type == EV_KEY) set_bit(ati_remote_tbl[i].code, idev->keybit); - + idev->private = ati_remote; idev->open = ati_remote_open; idev->close = ati_remote_close; - + idev->name = ati_remote->name; idev->phys = ati_remote->phys; - - idev->id.bustype = BUS_USB; + + idev->id.bustype = BUS_USB; idev->id.vendor = le16_to_cpu(ati_remote->udev->descriptor.idVendor); idev->id.product = le16_to_cpu(ati_remote->udev->descriptor.idProduct); idev->id.version = le16_to_cpu(ati_remote->udev->descriptor.bcdDevice); @@ -660,27 +645,27 @@ static int ati_remote_initialize(struct ati_remote *ati_remote) { struct usb_device *udev = ati_remote->udev; int pipe, maxp; - + init_waitqueue_head(&ati_remote->wait); /* Set up irq_urb */ pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress); maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp; - - usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf, - maxp, ati_remote_irq_in, ati_remote, + + usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf, + maxp, ati_remote_irq_in, ati_remote, ati_remote->endpoint_in->bInterval); ati_remote->irq_urb->transfer_dma = ati_remote->inbuf_dma; ati_remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - + /* Set up out_urb */ pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress); maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp; - usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf, - maxp, ati_remote_irq_out, ati_remote, + usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf, + maxp, ati_remote_irq_out, ati_remote, ati_remote->endpoint_out->bInterval); ati_remote->out_urb->transfer_dma = ati_remote->outbuf_dma; ati_remote->out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; @@ -688,11 +673,11 @@ static int ati_remote_initialize(struct ati_remote *ati_remote) /* send initialization strings */ if ((ati_remote_sendpacket(ati_remote, 0x8004, init1)) || (ati_remote_sendpacket(ati_remote, 0x8007, init2))) { - dev_err(&ati_remote->interface->dev, + dev_err(&ati_remote->interface->dev, "Initializing ati_remote hardware failed.\n"); return 1; } - + return 0; } @@ -769,7 +754,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de if (!strlen(ati_remote->name)) sprintf(ati_remote->name, DRIVER_DESC "(%04x,%04x)", - le16_to_cpu(ati_remote->udev->descriptor.idVendor), + le16_to_cpu(ati_remote->udev->descriptor.idVendor), le16_to_cpu(ati_remote->udev->descriptor.idProduct)); /* Device Hardware Initialization - fills in ati_remote->idev from udev. */ @@ -781,11 +766,11 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de ati_remote_input_init(ati_remote); input_register_device(&ati_remote->idev); - dev_info(&ati_remote->interface->dev, "Input registered: %s on %s\n", + dev_info(&ati_remote->interface->dev, "Input registered: %s on %s\n", ati_remote->name, path); usb_set_intfdata(interface, ati_remote); - + error: if (retval) ati_remote_delete(ati_remote); @@ -800,18 +785,14 @@ static void ati_remote_disconnect(struct usb_interface *interface) { struct ati_remote *ati_remote; - down(&disconnect_sem); - ati_remote = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); if (!ati_remote) { warn("%s - null device?\n", __FUNCTION__); return; } - - ati_remote_delete(ati_remote); - up(&disconnect_sem); + ati_remote_delete(ati_remote); } /* @@ -820,7 +801,7 @@ static void ati_remote_disconnect(struct usb_interface *interface) static int __init ati_remote_init(void) { int result; - + result = usb_register(&ati_remote_driver); if (result) err("usb_register error #%d\n", result); @@ -838,8 +819,8 @@ static void __exit ati_remote_exit(void) usb_deregister(&ati_remote_driver); } -/* - * module specification +/* + * module specification */ module_init(ati_remote_init); diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 740dec1f521..100b49bd1d3 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -232,7 +232,7 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign report->size += parser->global.report_size * parser->global.report_count; if (!parser->local.usage_index) /* Ignore padding fields */ - return 0; + return 0; usages = max_t(int, parser->local.usage_index, parser->global.report_count); @@ -765,7 +765,7 @@ static __inline__ __u32 s32ton(__s32 value, unsigned n) static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n) { report += (offset >> 5) << 2; offset &= 31; - return (le64_to_cpu(get_unaligned((__le64*)report)) >> offset) & ((1 << n) - 1); + return (le64_to_cpu(get_unaligned((__le64*)report)) >> offset) & ((1ULL << n) - 1); } static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value) @@ -1233,6 +1233,13 @@ int hid_wait_io(struct hid_device *hid) return 0; } +static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (idle << 8) | report, + ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT); +} + static int hid_get_class_descriptor(struct usb_device *dev, int ifnum, unsigned char type, void *buf, int size) { @@ -1301,10 +1308,6 @@ void hid_init_reports(struct hid_device *hid) if (err) warn("timeout initializing reports\n"); - - usb_control_msg(hid->dev, usb_sndctrlpipe(hid->dev, 0), - HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, - hid->ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT); } #define USB_VENDOR_ID_WACOM 0x056a @@ -1318,6 +1321,10 @@ void hid_init_reports(struct hid_device *hid) #define USB_DEVICE_ID_WACOM_INTUOS3 0x00B0 #define USB_DEVICE_ID_WACOM_CINTIQ 0x003F +#define USB_VENDOR_ID_ACECAD 0x0460 +#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004 +#define USB_DEVICE_ID_ACECAD_302 0x0008 + #define USB_VENDOR_ID_KBGEAR 0x084e #define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 @@ -1502,6 +1509,9 @@ static struct hid_blacklist { { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, @@ -1590,6 +1600,8 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) return NULL; } + hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0); + if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) { dbg("reading report descriptor failed"); kfree(rdesc); @@ -1635,7 +1647,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) /* Change the polling interval of mice. */ if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0) interval = hid_mousepoll_interval; - + if (endpoint->bEndpointAddress & USB_DIR_IN) { if (hid->urbin) continue; diff --git a/drivers/usb/input/hid-debug.h b/drivers/usb/input/hid-debug.h index 2b91705740a..52437e5e2e7 100644 --- a/drivers/usb/input/hid-debug.h +++ b/drivers/usb/input/hid-debug.h @@ -67,7 +67,7 @@ static const struct hid_usage_entry hid_usage_table[] = { {0, 0x44, "Vbry"}, {0, 0x45, "Vbrz"}, {0, 0x46, "Vno"}, - {0, 0x80, "SystemControl"}, + {0, 0x80, "SystemControl"}, {0, 0x81, "SystemPowerDown"}, {0, 0x82, "SystemSleep"}, {0, 0x83, "SystemWakeUp"}, @@ -347,7 +347,7 @@ __inline__ static void tab(int n) { static void hid_dump_field(struct hid_field *field, int n) { int j; - + if (field->physical) { tab(n); printk("Physical("); @@ -408,7 +408,7 @@ static void hid_dump_field(struct hid_field *field, int n) { printk("%s", units[sys][i]); if(nibble != 1) { /* This is a _signed_ nibble(!) */ - + int val = nibble & 0x7; if(nibble & 0x08) val = -((0x7 & ~val) +1); @@ -443,7 +443,7 @@ static void __attribute__((unused)) hid_dump_device(struct hid_device *device) { struct list_head *list; unsigned i,k; static char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; - + for (i = 0; i < HID_REPORT_TYPES; i++) { report_enum = device->report_enum + i; list = report_enum->report_list.next; @@ -664,8 +664,8 @@ static char *keys[KEY_MAX + 1] = { static char *relatives[REL_MAX + 1] = { [REL_X] = "X", [REL_Y] = "Y", [REL_Z] = "Z", [REL_HWHEEL] = "HWheel", - [REL_DIAL] = "Dial", [REL_WHEEL] = "Wheel", - [REL_MISC] = "Misc", + [REL_DIAL] = "Dial", [REL_WHEEL] = "Wheel", + [REL_MISC] = "Misc", }; static char *absolutes[ABS_MAX + 1] = { @@ -690,9 +690,9 @@ static char *misc[MSC_MAX + 1] = { }; static char *leds[LED_MAX + 1] = { - [LED_NUML] = "NumLock", [LED_CAPSL] = "CapsLock", + [LED_NUML] = "NumLock", [LED_CAPSL] = "CapsLock", [LED_SCROLLL] = "ScrollLock", [LED_COMPOSE] = "Compose", - [LED_KANA] = "Kana", [LED_SLEEP] = "Sleep", + [LED_KANA] = "Kana", [LED_SLEEP] = "Sleep", [LED_SUSPEND] = "Suspend", [LED_MUTE] = "Mute", [LED_MISC] = "Misc", }; diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c index 5553c3553e9..9ac1e909533 100644 --- a/drivers/usb/input/hid-input.c +++ b/drivers/usb/input/hid-input.c @@ -164,7 +164,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_GD_X: case HID_GD_Y: case HID_GD_Z: case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ: case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL: - if (field->flags & HID_MAIN_ITEM_RELATIVE) + if (field->flags & HID_MAIN_ITEM_RELATIVE) map_rel(usage->hid & 0xf); else map_abs(usage->hid & 0xf); @@ -297,7 +297,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_UP_MSVENDOR: goto ignore; - + case HID_UP_PID: set_bit(EV_FF, input->evbit); @@ -349,7 +349,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel goto ignore; if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | HID_QUIRK_2WHEEL_MOUSE_HACK_5)) && - (usage->type == EV_REL) && (usage->code == REL_WHEEL)) + (usage->type == EV_REL) && (usage->code == REL_WHEEL)) set_bit(REL_HWHEEL, bit); if (((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005)) @@ -365,11 +365,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel a = field->logical_minimum = 0; b = field->logical_maximum = 255; } - + if (field->application == HID_GD_GAMEPAD || field->application == HID_GD_JOYSTICK) input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4); else input_set_abs_params(input, usage->code, a, b, 0, 0); - + } if (usage->hat_min < usage->hat_max || usage->hat_dir) { @@ -420,7 +420,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; } - if (usage->hat_min < usage->hat_max || usage->hat_dir) { + if (usage->hat_min < usage->hat_max || usage->hat_dir) { int hat_dir = usage->hat_dir; if (!hat_dir) hat_dir = (value - usage->hat_min) * 8 / (usage->hat_max - usage->hat_min + 1) + 1; @@ -551,7 +551,7 @@ int hidinput_connect(struct hid_device *hid) for (i = 0; i < hid->maxcollection; i++) if (hid->collection[i].type == HID_COLLECTION_APPLICATION || hid->collection[i].type == HID_COLLECTION_PHYSICAL) - if (IS_INPUT_APPLICATION(hid->collection[i].usage)) + if (IS_INPUT_APPLICATION(hid->collection[i].usage)) break; if (i == hid->maxcollection) @@ -592,7 +592,7 @@ int hidinput_connect(struct hid_device *hid) for (j = 0; j < report->field[i]->maxusage; j++) hidinput_configure_usage(hidinput, report->field[i], report->field[i]->usage + j); - + if (hid->quirks & HID_QUIRK_MULTI_INPUT) { /* This will leave hidinput NULL, so that it * allocates another one if we have more inputs on diff --git a/drivers/usb/input/hid-lgff.c b/drivers/usb/input/hid-lgff.c index 0d7404bab92..0c4c77aa31e 100644 --- a/drivers/usb/input/hid-lgff.c +++ b/drivers/usb/input/hid-lgff.c @@ -94,7 +94,7 @@ struct lgff_device { isn't really necessary */ unsigned long flags[1]; /* Contains various information about the - state of the driver for this device */ + state of the driver for this device */ struct timer_list timer; }; @@ -234,7 +234,7 @@ static struct hid_report* hid_lgff_duplicate_report(struct hid_report* report) kfree(ret); return NULL; } - memset(ret->field[0]->value, 0, sizeof(s32[8])); + memset(ret->field[0]->value, 0, sizeof(s32[8])); return ret; } @@ -295,11 +295,11 @@ static int hid_lgff_event(struct hid_device *hid, struct input_dev* input, unsigned long flags; if (type != EV_FF) return -EINVAL; - if (!LGFF_CHECK_OWNERSHIP(code, lgff)) return -EACCES; + if (!LGFF_CHECK_OWNERSHIP(code, lgff)) return -EACCES; if (value < 0) return -EINVAL; spin_lock_irqsave(&lgff->lock, flags); - + if (value > 0) { if (test_bit(EFFECT_STARTED, effect->flags)) { spin_unlock_irqrestore(&lgff->lock, flags); @@ -345,7 +345,7 @@ static int hid_lgff_flush(struct input_dev *dev, struct file *file) and perform ioctls on the same fd all at the same time */ if ( current->pid == lgff->effects[i].owner && test_bit(EFFECT_USED, lgff->effects[i].flags)) { - + if (hid_lgff_erase(dev, i)) warn("erase effect %d failed", i); } @@ -378,7 +378,7 @@ static int hid_lgff_upload_effect(struct input_dev* input, struct lgff_effect new; int id; unsigned long flags; - + dbg("ioctl rumble"); if (!test_bit(effect->type, input->ffbit)) return -EINVAL; @@ -441,7 +441,7 @@ static void hid_lgff_timer(unsigned long timer_data) spin_lock_irqsave(&lgff->lock, flags); - for (i=0; i<LGFF_EFFECTS; ++i) { + for (i=0; i<LGFF_EFFECTS; ++i) { struct lgff_effect* effect = lgff->effects +i; if (test_bit(EFFECT_PLAYING, effect->flags)) { @@ -491,7 +491,7 @@ static void hid_lgff_timer(unsigned long timer_data) set_bit(EFFECT_PLAYING, lgff->effects[i].flags); } } - } + } #define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff @@ -524,5 +524,5 @@ static void hid_lgff_timer(unsigned long timer_data) add_timer(&lgff->timer); } - spin_unlock_irqrestore(&lgff->lock, flags); + spin_unlock_irqrestore(&lgff->lock, flags); } diff --git a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h index 6d9329c698d..c1b6b69bc4a 100644 --- a/drivers/usb/input/hid.h +++ b/drivers/usb/input/hid.h @@ -118,7 +118,7 @@ struct hid_item { #define HID_MAIN_ITEM_CONSTANT 0x001 #define HID_MAIN_ITEM_VARIABLE 0x002 #define HID_MAIN_ITEM_RELATIVE 0x004 -#define HID_MAIN_ITEM_WRAP 0x008 +#define HID_MAIN_ITEM_WRAP 0x008 #define HID_MAIN_ITEM_NONLINEAR 0x010 #define HID_MAIN_ITEM_NO_PREFERRED 0x020 #define HID_MAIN_ITEM_NULL_STATE 0x040 @@ -172,14 +172,14 @@ struct hid_item { #define HID_USAGE_PAGE 0xffff0000 #define HID_UP_UNDEFINED 0x00000000 -#define HID_UP_GENDESK 0x00010000 -#define HID_UP_KEYBOARD 0x00070000 -#define HID_UP_LED 0x00080000 -#define HID_UP_BUTTON 0x00090000 -#define HID_UP_ORDINAL 0x000a0000 +#define HID_UP_GENDESK 0x00010000 +#define HID_UP_KEYBOARD 0x00070000 +#define HID_UP_LED 0x00080000 +#define HID_UP_BUTTON 0x00090000 +#define HID_UP_ORDINAL 0x000a0000 #define HID_UP_CONSUMER 0x000c0000 -#define HID_UP_DIGITIZER 0x000d0000 -#define HID_UP_PID 0x000f0000 +#define HID_UP_DIGITIZER 0x000d0000 +#define HID_UP_PID 0x000f0000 #define HID_UP_HPVENDOR 0xff7f0000 #define HID_UP_MSVENDOR 0xff000000 @@ -406,7 +406,7 @@ struct hid_device { /* device report descriptor */ dma_addr_t outbuf_dma; /* Output buffer dma */ spinlock_t outlock; /* Output fifo spinlock */ - unsigned claimed; /* Claimed by hidinput, hiddev? */ + unsigned claimed; /* Claimed by hidinput, hiddev? */ unsigned quirks; /* Various quirks the device can pull on us */ struct list_head inputs; /* The list of inputs */ diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c index 96b7c906795..4c13331b5f4 100644 --- a/drivers/usb/input/hiddev.c +++ b/drivers/usb/input/hiddev.c @@ -95,7 +95,7 @@ hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo) return NULL; rinfo->report_id = ((struct hid_report *) list)->id; break; - + case HID_REPORT_ID_NEXT: list = (struct list_head *) report_enum->report_id_hash[rinfo->report_id & HID_REPORT_ID_MASK]; @@ -106,7 +106,7 @@ hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo) return NULL; rinfo->report_id = ((struct hid_report *) list)->id; break; - + default: return NULL; } @@ -158,7 +158,7 @@ static void hiddev_send_event(struct hid_device *hid, if (uref->field_index != HID_FIELD_INDEX_NONE || (list->flags & HIDDEV_FLAG_REPORT) != 0) { list->buffer[list->head] = *uref; - list->head = (list->head + 1) & + list->head = (list->head + 1) & (HIDDEV_BUFFER_SIZE - 1); kill_fasync(&list->fasync, SIGIO, POLL_IN); } @@ -179,9 +179,9 @@ void hiddev_hid_event(struct hid_device *hid, struct hid_field *field, unsigned type = field->report_type; struct hiddev_usage_ref uref; - uref.report_type = + uref.report_type = (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : - ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : + ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0)); uref.report_id = field->report->id; uref.field_index = field->index; @@ -199,9 +199,9 @@ void hiddev_report_event(struct hid_device *hid, struct hid_report *report) struct hiddev_usage_ref uref; memset(&uref, 0, sizeof(uref)); - uref.report_type = + uref.report_type = (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : - ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : + ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0)); uref.report_id = report->id; uref.field_index = HID_FIELD_INDEX_NONE; @@ -236,7 +236,7 @@ static int hiddev_release(struct inode * inode, struct file * file) *listptr = (*listptr)->next; if (!--list->hiddev->open) { - if (list->hiddev->exist) + if (list->hiddev->exist) hid_close(list->hiddev->hid); else kfree(list->hiddev); @@ -303,7 +303,7 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun if (list->head == list->tail) { add_wait_queue(&list->hiddev->wait, &wait); set_current_state(TASK_INTERRUPTIBLE); - + while (list->head == list->tail) { if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; @@ -317,7 +317,7 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun retval = -EIO; break; } - + schedule(); } @@ -329,7 +329,7 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun return retval; - while (list->head != list->tail && + while (list->head != list->tail && retval + event_size <= count) { if ((list->flags & HIDDEV_FLAG_UREF) == 0) { if (list->buffer[list->tail].field_index != @@ -405,10 +405,10 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd return -EINVAL; for (i = 0; i < hid->maxcollection; i++) - if (hid->collection[i].type == + if (hid->collection[i].type == HID_COLLECTION_APPLICATION && arg-- == 0) break; - + if (i == hid->maxcollection) return -EINVAL; @@ -562,7 +562,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd if (!uref_multi) return -ENOMEM; uref = &uref_multi->uref; - if (copy_from_user(uref, user_arg, sizeof(*uref))) + if (copy_from_user(uref, user_arg, sizeof(*uref))) goto fault; rinfo.report_type = uref->report_type; @@ -595,7 +595,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd return -ENOMEM; uref = &uref_multi->uref; if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { - if (copy_from_user(uref_multi, user_arg, + if (copy_from_user(uref_multi, user_arg, sizeof(*uref_multi))) goto fault; } else { @@ -603,7 +603,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd goto fault; } - if (cmd != HIDIOCGUSAGE && + if (cmd != HIDIOCGUSAGE && cmd != HIDIOCGUSAGES && uref->report_type == HID_REPORT_TYPE_INPUT) goto inval; @@ -651,16 +651,16 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd return field->usage[uref->usage_index].collection_index; case HIDIOCGUSAGES: for (i = 0; i < uref_multi->num_values; i++) - uref_multi->values[i] = + uref_multi->values[i] = field->value[uref->usage_index + i]; - if (copy_to_user(user_arg, uref_multi, + if (copy_to_user(user_arg, uref_multi, sizeof(*uref_multi))) goto fault; goto goodreturn; case HIDIOCSUSAGES: for (i = 0; i < uref_multi->num_values; i++) - field->value[uref->usage_index + i] = - uref_multi->values[i]; + field->value[uref->usage_index + i] = + uref_multi->values[i]; goto goodreturn; } @@ -670,7 +670,7 @@ goodreturn: fault: kfree(uref_multi); return -EFAULT; -inval: +inval: kfree(uref_multi); return -EINVAL; @@ -734,7 +734,7 @@ static struct usb_class_driver hiddev_class = { .name = "usb/hid/hiddev%d", .fops = &hiddev_fops, .mode = S_IFCHR | S_IRUGO | S_IWUSR, - .minor_base = HIDDEV_MINOR_BASE, + .minor_base = HIDDEV_MINOR_BASE, }; /* @@ -747,7 +747,7 @@ int hiddev_connect(struct hid_device *hid) int retval; for (i = 0; i < hid->maxcollection; i++) - if (hid->collection[i].type == + if (hid->collection[i].type == HID_COLLECTION_APPLICATION && !IS_INPUT_APPLICATION(hid->collection[i].usage)) break; @@ -755,11 +755,11 @@ int hiddev_connect(struct hid_device *hid) if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0) return -1; - if (!(hiddev = kmalloc(sizeof(struct hiddev), GFP_KERNEL))) + if (!(hiddev = kmalloc(sizeof(struct hiddev), GFP_KERNEL))) return -1; memset(hiddev, 0, sizeof(struct hiddev)); - retval = usb_register_dev(hid->intf, &hiddev_class); + retval = usb_register_dev(hid->intf, &hiddev_class); if (retval) { err("Not able to get a minor for this device."); kfree(hiddev); @@ -768,12 +768,12 @@ int hiddev_connect(struct hid_device *hid) init_waitqueue_head(&hiddev->wait); - hiddev_table[hid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; + hiddev_table[hid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; hiddev->hid = hid; hiddev->exist = 1; - hid->minor = hid->intf->minor; + hid->minor = hid->intf->minor; hid->hiddev = hiddev; return 0; @@ -818,7 +818,7 @@ void hiddev_disconnect(struct hid_device *hid) /* We never attach in this manner, and rely on HID to connect us. This * is why there is no disconnect routine defined in the usb_driver either. */ -static int hiddev_usbd_probe(struct usb_interface *intf, +static int hiddev_usbd_probe(struct usb_interface *intf, const struct usb_device_id *hiddev_info) { return -ENODEV; diff --git a/drivers/usb/input/itmtouch.c b/drivers/usb/input/itmtouch.c new file mode 100644 index 00000000000..47dec6a1b34 --- /dev/null +++ b/drivers/usb/input/itmtouch.c @@ -0,0 +1,268 @@ +/****************************************************************************** + * itmtouch.c -- Driver for ITM touchscreen panel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Based upon original work by Chris Collins <xfire-itmtouch@xware.cx>. + * + * Kudos to ITM for providing me with the datasheet for the panel, + * even though it was a day later than I had finished writing this + * driver. + * + * It has meant that I've been able to correct my interpretation of the + * protocol packets however. + * + * CC -- 2003/9/29 + * + * History + * 1.0 & 1.1 2003 (CC) vojtech@suse.cz + * Original version for 2.4.x kernels + * + * 1.2 02/03/2005 (HCE) hc@mivu.no + * Complete rewrite to support Linux 2.6.10, thanks to mtouchusb.c for hints. + * Unfortunately no calibration support at this time. + * + * 1.2.1 09/03/2005 (HCE) hc@mivu.no + * Code cleanup and adjusting syntax to start matching kernel standards + * + *****************************************************************************/ + +#include <linux/config.h> + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> + +/* only an 8 byte buffer necessary for a single packet */ +#define ITM_BUFSIZE 8 +#define PATH_SIZE 64 + +#define USB_VENDOR_ID_ITMINC 0x0403 +#define USB_PRODUCT_ID_TOUCHPANEL 0xf9e9 + +#define DRIVER_AUTHOR "Hans-Christian Egtvedt <hc@mivu.no>" +#define DRIVER_VERSION "v1.2.1" +#define DRIVER_DESC "USB ITM Inc Touch Panel Driver" +#define DRIVER_LICENSE "GPL" + +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_LICENSE( DRIVER_LICENSE ); + +struct itmtouch_dev { + struct usb_device *usbdev; /* usb device */ + struct input_dev inputdev; /* input device */ + struct urb *readurb; /* urb */ + char rbuf[ITM_BUFSIZE]; /* data */ + int users; + char name[128]; + char phys[64]; +}; + +static struct usb_device_id itmtouch_ids [] = { + { USB_DEVICE(USB_VENDOR_ID_ITMINC, USB_PRODUCT_ID_TOUCHPANEL) }, + { } +}; + +static void itmtouch_irq(struct urb *urb, struct pt_regs *regs) +{ + struct itmtouch_dev * itmtouch = urb->context; + unsigned char *data = urb->transfer_buffer; + struct input_dev *dev = &itmtouch->inputdev; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ETIMEDOUT: + /* this urb is timing out */ + dbg("%s - urb timed out - was the device unplugged?", + __FUNCTION__); + return; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); + goto exit; + } + + input_regs(dev, regs); + + /* if pressure has been released, then don't report X/Y */ + if (data[7] & 0x20) { + input_report_abs(dev, ABS_X, (data[0] & 0x1F) << 7 | (data[3] & 0x7F)); + input_report_abs(dev, ABS_Y, (data[1] & 0x1F) << 7 | (data[4] & 0x7F)); + } + + input_report_abs(dev, ABS_PRESSURE, (data[2] & 1) << 7 | (data[5] & 0x7F)); + input_report_key(dev, BTN_TOUCH, ~data[7] & 0x20); + input_sync(dev); + +exit: + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) + printk(KERN_ERR "%s - usb_submit_urb failed with result: %d", + __FUNCTION__, retval); +} + +static int itmtouch_open(struct input_dev *input) +{ + struct itmtouch_dev *itmtouch = input->private; + + itmtouch->readurb->dev = itmtouch->usbdev; + + if (usb_submit_urb(itmtouch->readurb, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void itmtouch_close(struct input_dev *input) +{ + struct itmtouch_dev *itmtouch = input->private; + + usb_kill_urb(itmtouch->readurb); +} + +static int itmtouch_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct itmtouch_dev *itmtouch; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_device *udev = interface_to_usbdev(intf); + unsigned int pipe; + unsigned int maxp; + char path[PATH_SIZE]; + + interface = intf->cur_altsetting; + endpoint = &interface->endpoint[0].desc; + + if (!(itmtouch = kcalloc(1, sizeof(struct itmtouch_dev), GFP_KERNEL))) { + err("%s - Out of memory.", __FUNCTION__); + return -ENOMEM; + } + + itmtouch->usbdev = udev; + + itmtouch->inputdev.private = itmtouch; + itmtouch->inputdev.open = itmtouch_open; + itmtouch->inputdev.close = itmtouch_close; + + usb_make_path(udev, path, PATH_SIZE); + + itmtouch->inputdev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + itmtouch->inputdev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE); + itmtouch->inputdev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + + itmtouch->inputdev.name = itmtouch->name; + itmtouch->inputdev.phys = itmtouch->phys; + itmtouch->inputdev.id.bustype = BUS_USB; + itmtouch->inputdev.id.vendor = udev->descriptor.idVendor; + itmtouch->inputdev.id.product = udev->descriptor.idProduct; + itmtouch->inputdev.id.version = udev->descriptor.bcdDevice; + itmtouch->inputdev.dev = &intf->dev; + + if (!strlen(itmtouch->name)) + sprintf(itmtouch->name, "USB ITM touchscreen"); + + /* device limits */ + /* as specified by the ITM datasheet, X and Y are 12bit, + * Z (pressure) is 8 bit. However, the fields are defined up + * to 14 bits for future possible expansion. + */ + input_set_abs_params(&itmtouch->inputdev, ABS_X, 0, 0x0FFF, 2, 0); + input_set_abs_params(&itmtouch->inputdev, ABS_Y, 0, 0x0FFF, 2, 0); + input_set_abs_params(&itmtouch->inputdev, ABS_PRESSURE, 0, 0xFF, 2, 0); + + /* initialise the URB so we can read from the transport stream */ + pipe = usb_rcvintpipe(itmtouch->usbdev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + + if (maxp > ITM_BUFSIZE) + maxp = ITM_BUFSIZE; + + itmtouch->readurb = usb_alloc_urb(0, GFP_KERNEL); + + if (!itmtouch->readurb) { + dbg("%s - usb_alloc_urb failed: itmtouch->readurb", __FUNCTION__); + kfree(itmtouch); + return -ENOMEM; + } + + usb_fill_int_urb(itmtouch->readurb, itmtouch->usbdev, pipe, itmtouch->rbuf, + maxp, itmtouch_irq, itmtouch, endpoint->bInterval); + + input_register_device(&itmtouch->inputdev); + + printk(KERN_INFO "itmtouch: %s registered on %s\n", itmtouch->name, path); + usb_set_intfdata(intf, itmtouch); + + return 0; +} + +static void itmtouch_disconnect(struct usb_interface *intf) +{ + struct itmtouch_dev *itmtouch = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (itmtouch) { + input_unregister_device(&itmtouch->inputdev); + usb_kill_urb(itmtouch->readurb); + usb_free_urb(itmtouch->readurb); + kfree(itmtouch); + } +} + +MODULE_DEVICE_TABLE(usb, itmtouch_ids); + +static struct usb_driver itmtouch_driver = { + .owner = THIS_MODULE, + .name = "itmtouch", + .probe = itmtouch_probe, + .disconnect = itmtouch_disconnect, + .id_table = itmtouch_ids, +}; + +static int __init itmtouch_init(void) +{ + info(DRIVER_DESC " " DRIVER_VERSION); + info(DRIVER_AUTHOR); + return usb_register(&itmtouch_driver); +} + +static void __exit itmtouch_exit(void) +{ + usb_deregister(&itmtouch_driver); +} + +module_init(itmtouch_init); +module_exit(itmtouch_exit); diff --git a/drivers/usb/input/kbtab.c b/drivers/usb/input/kbtab.c index a68c5b4e7b3..d2f0f90a9bc 100644 --- a/drivers/usb/input/kbtab.c +++ b/drivers/usb/input/kbtab.c @@ -36,7 +36,6 @@ struct kbtab { struct input_dev dev; struct usb_device *usbdev; struct urb *irq; - int open; int x, y; int button; int pressure; @@ -79,12 +78,12 @@ static void kbtab_irq(struct urb *urb, struct pt_regs *regs) /*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/ input_report_key(dev, BTN_RIGHT, data[0] & 0x02); - if( -1 == kb_pressure_click){ + if (-1 == kb_pressure_click) { input_report_abs(dev, ABS_PRESSURE, kbtab->pressure); } else { input_report_key(dev, BTN_LEFT, (kbtab->pressure > kb_pressure_click) ? 1 : 0); }; - + input_sync(dev); exit: @@ -105,14 +104,9 @@ static int kbtab_open(struct input_dev *dev) { struct kbtab *kbtab = dev->private; - if (kbtab->open++) - return 0; - kbtab->irq->dev = kbtab->usbdev; - if (usb_submit_urb(kbtab->irq, GFP_KERNEL)) { - kbtab->open--; + if (usb_submit_urb(kbtab->irq, GFP_KERNEL)) return -EIO; - } return 0; } @@ -121,8 +115,7 @@ static void kbtab_close(struct input_dev *dev) { struct kbtab *kbtab = dev->private; - if (!--kbtab->open) - usb_kill_urb(kbtab->irq); + usb_kill_urb(kbtab->irq); } static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -161,7 +154,7 @@ static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *i kbtab->dev.absmax[ABS_X] = 0x2000; kbtab->dev.absmax[ABS_Y] = 0x1750; kbtab->dev.absmax[ABS_PRESSURE] = 0xff; - + kbtab->dev.absfuzz[ABS_X] = 4; kbtab->dev.absfuzz[ABS_Y] = 4; diff --git a/drivers/usb/input/mtouchusb.c b/drivers/usb/input/mtouchusb.c index ab1a2a30ce7..09b5cc7c66d 100644 --- a/drivers/usb/input/mtouchusb.c +++ b/drivers/usb/input/mtouchusb.c @@ -42,9 +42,9 @@ #include <linux/config.h> #ifdef CONFIG_USB_DEBUG - #define DEBUG + #define DEBUG #else - #undef DEBUG + #undef DEBUG #endif #include <linux/kernel.h> @@ -93,275 +93,255 @@ module_param(raw_coordinates, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(raw_coordinates, "report raw coordinate values (y, default) or hardware-calibrated coordinate values (n)"); struct mtouch_usb { - unsigned char *data; - dma_addr_t data_dma; - struct urb *irq; - struct usb_device *udev; - struct input_dev input; - int open; - char name[128]; - char phys[64]; + unsigned char *data; + dma_addr_t data_dma; + struct urb *irq; + struct usb_device *udev; + struct input_dev input; + char name[128]; + char phys[64]; }; -static struct usb_device_id mtouchusb_devices [] = { - { USB_DEVICE(0x0596, 0x0001) }, - { } +static struct usb_device_id mtouchusb_devices[] = { + { USB_DEVICE(0x0596, 0x0001) }, + { } }; static void mtouchusb_irq(struct urb *urb, struct pt_regs *regs) { - struct mtouch_usb *mtouch = urb->context; - int retval; - - switch (urb->status) { - case 0: - /* success */ - break; - case -ETIMEDOUT: - /* this urb is timing out */ - dbg("%s - urb timed out - was the device unplugged?", - __FUNCTION__); - return; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", - __FUNCTION__, urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", - __FUNCTION__, urb->status); - goto exit; - } - - input_regs(&mtouch->input, regs); - input_report_key(&mtouch->input, BTN_TOUCH, - MTOUCHUSB_GET_TOUCHED(mtouch->data)); - input_report_abs(&mtouch->input, ABS_X, - MTOUCHUSB_GET_XC(mtouch->data)); - input_report_abs(&mtouch->input, ABS_Y, + struct mtouch_usb *mtouch = urb->context; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ETIMEDOUT: + /* this urb is timing out */ + dbg("%s - urb timed out - was the device unplugged?", + __FUNCTION__); + return; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); + goto exit; + } + + input_regs(&mtouch->input, regs); + input_report_key(&mtouch->input, BTN_TOUCH, + MTOUCHUSB_GET_TOUCHED(mtouch->data)); + input_report_abs(&mtouch->input, ABS_X, MTOUCHUSB_GET_XC(mtouch->data)); + input_report_abs(&mtouch->input, ABS_Y, (raw_coordinates ? MTOUCHUSB_MAX_RAW_YC : MTOUCHUSB_MAX_CALIB_YC) - - MTOUCHUSB_GET_YC(mtouch->data)); - input_sync(&mtouch->input); + - MTOUCHUSB_GET_YC(mtouch->data)); + input_sync(&mtouch->input); exit: - retval = usb_submit_urb (urb, GFP_ATOMIC); - if (retval) - err ("%s - usb_submit_urb failed with result: %d", - __FUNCTION__, retval); + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err("%s - usb_submit_urb failed with result: %d", + __FUNCTION__, retval); } -static int mtouchusb_open (struct input_dev *input) +static int mtouchusb_open(struct input_dev *input) { - struct mtouch_usb *mtouch = input->private; + struct mtouch_usb *mtouch = input->private; - if (mtouch->open++) - return 0; + mtouch->irq->dev = mtouch->udev; - mtouch->irq->dev = mtouch->udev; + if (usb_submit_urb(mtouch->irq, GFP_ATOMIC)) + return -EIO; - if (usb_submit_urb (mtouch->irq, GFP_ATOMIC)) { - mtouch->open--; - return -EIO; - } - - return 0; + return 0; } -static void mtouchusb_close (struct input_dev *input) +static void mtouchusb_close(struct input_dev *input) { - struct mtouch_usb *mtouch = input->private; + struct mtouch_usb *mtouch = input->private; - if (!--mtouch->open) - usb_kill_urb (mtouch->irq); + usb_kill_urb(mtouch->irq); } static int mtouchusb_alloc_buffers(struct usb_device *udev, struct mtouch_usb *mtouch) { - dbg("%s - called", __FUNCTION__); + dbg("%s - called", __FUNCTION__); - mtouch->data = usb_buffer_alloc(udev, MTOUCHUSB_REPORT_DATA_SIZE, - SLAB_ATOMIC, &mtouch->data_dma); + mtouch->data = usb_buffer_alloc(udev, MTOUCHUSB_REPORT_DATA_SIZE, + SLAB_ATOMIC, &mtouch->data_dma); - if (!mtouch->data) - return -1; + if (!mtouch->data) + return -1; - return 0; + return 0; } static void mtouchusb_free_buffers(struct usb_device *udev, struct mtouch_usb *mtouch) { - dbg("%s - called", __FUNCTION__); + dbg("%s - called", __FUNCTION__); - if (mtouch->data) - usb_buffer_free(udev, MTOUCHUSB_REPORT_DATA_SIZE, - mtouch->data, mtouch->data_dma); + if (mtouch->data) + usb_buffer_free(udev, MTOUCHUSB_REPORT_DATA_SIZE, + mtouch->data, mtouch->data_dma); } static int mtouchusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct mtouch_usb *mtouch; - struct usb_host_interface *interface; - struct usb_endpoint_descriptor *endpoint; - struct usb_device *udev = interface_to_usbdev (intf); - char path[64]; - int nRet; - - dbg("%s - called", __FUNCTION__); - - dbg("%s - setting interface", __FUNCTION__); - interface = intf->cur_altsetting; - - dbg("%s - setting endpoint", __FUNCTION__); - endpoint = &interface->endpoint[0].desc; - - if (!(mtouch = kmalloc (sizeof (struct mtouch_usb), GFP_KERNEL))) { - err("%s - Out of memory.", __FUNCTION__); - return -ENOMEM; - } - - memset(mtouch, 0, sizeof(struct mtouch_usb)); - mtouch->udev = udev; - - dbg("%s - allocating buffers", __FUNCTION__); - if (mtouchusb_alloc_buffers(udev, mtouch)) { - mtouchusb_free_buffers(udev, mtouch); - kfree(mtouch); - return -ENOMEM; - } - - mtouch->input.private = mtouch; - mtouch->input.open = mtouchusb_open; - mtouch->input.close = mtouchusb_close; - - usb_make_path(udev, path, 64); - sprintf(mtouch->phys, "%s/input0", path); - - mtouch->input.name = mtouch->name; - mtouch->input.phys = mtouch->phys; - mtouch->input.id.bustype = BUS_USB; - mtouch->input.id.vendor = le16_to_cpu(udev->descriptor.idVendor); - mtouch->input.id.product = le16_to_cpu(udev->descriptor.idProduct); - mtouch->input.id.version = le16_to_cpu(udev->descriptor.bcdDevice); - mtouch->input.dev = &intf->dev; - - mtouch->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - mtouch->input.absbit[0] = BIT(ABS_X) | BIT(ABS_Y); - mtouch->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); - - /* Used to Scale Compensated Data and Flip Y */ - mtouch->input.absmin[ABS_X] = MTOUCHUSB_MIN_XC; - mtouch->input.absmax[ABS_X] = raw_coordinates ? \ - MTOUCHUSB_MAX_RAW_XC : MTOUCHUSB_MAX_CALIB_XC; - mtouch->input.absfuzz[ABS_X] = MTOUCHUSB_XC_FUZZ; - mtouch->input.absflat[ABS_X] = MTOUCHUSB_XC_FLAT; - mtouch->input.absmin[ABS_Y] = MTOUCHUSB_MIN_YC; - mtouch->input.absmax[ABS_Y] = raw_coordinates ? \ - MTOUCHUSB_MAX_RAW_YC : MTOUCHUSB_MAX_CALIB_YC; - mtouch->input.absfuzz[ABS_Y] = MTOUCHUSB_YC_FUZZ; - mtouch->input.absflat[ABS_Y] = MTOUCHUSB_YC_FLAT; + struct mtouch_usb *mtouch; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_device *udev = interface_to_usbdev(intf); + char path[64]; + int nRet; + + dbg("%s - called", __FUNCTION__); + + dbg("%s - setting interface", __FUNCTION__); + interface = intf->cur_altsetting; + + dbg("%s - setting endpoint", __FUNCTION__); + endpoint = &interface->endpoint[0].desc; + + if (!(mtouch = kmalloc(sizeof(struct mtouch_usb), GFP_KERNEL))) { + err("%s - Out of memory.", __FUNCTION__); + return -ENOMEM; + } + + memset(mtouch, 0, sizeof(struct mtouch_usb)); + mtouch->udev = udev; + + dbg("%s - allocating buffers", __FUNCTION__); + if (mtouchusb_alloc_buffers(udev, mtouch)) { + mtouchusb_free_buffers(udev, mtouch); + kfree(mtouch); + return -ENOMEM; + } + + mtouch->input.private = mtouch; + mtouch->input.open = mtouchusb_open; + mtouch->input.close = mtouchusb_close; + + usb_make_path(udev, path, 64); + sprintf(mtouch->phys, "%s/input0", path); + + mtouch->input.name = mtouch->name; + mtouch->input.phys = mtouch->phys; + mtouch->input.id.bustype = BUS_USB; + mtouch->input.id.vendor = le16_to_cpu(udev->descriptor.idVendor); + mtouch->input.id.product = le16_to_cpu(udev->descriptor.idProduct); + mtouch->input.id.version = le16_to_cpu(udev->descriptor.bcdDevice); + mtouch->input.dev = &intf->dev; + + mtouch->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + mtouch->input.absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + mtouch->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + + /* Used to Scale Compensated Data and Flip Y */ + mtouch->input.absmin[ABS_X] = MTOUCHUSB_MIN_XC; + mtouch->input.absmax[ABS_X] = raw_coordinates ? + MTOUCHUSB_MAX_RAW_XC : MTOUCHUSB_MAX_CALIB_XC; + mtouch->input.absfuzz[ABS_X] = MTOUCHUSB_XC_FUZZ; + mtouch->input.absflat[ABS_X] = MTOUCHUSB_XC_FLAT; + mtouch->input.absmin[ABS_Y] = MTOUCHUSB_MIN_YC; + mtouch->input.absmax[ABS_Y] = raw_coordinates ? + MTOUCHUSB_MAX_RAW_YC : MTOUCHUSB_MAX_CALIB_YC; + mtouch->input.absfuzz[ABS_Y] = MTOUCHUSB_YC_FUZZ; + mtouch->input.absflat[ABS_Y] = MTOUCHUSB_YC_FLAT; if (udev->manufacturer) strcat(mtouch->name, udev->manufacturer); if (udev->product) sprintf(mtouch->name, "%s %s", mtouch->name, udev->product); - if (!strlen(mtouch->name)) - sprintf(mtouch->name, "USB Touchscreen %04x:%04x", - mtouch->input.id.vendor, mtouch->input.id.product); - - nRet = usb_control_msg(mtouch->udev, - usb_rcvctrlpipe(udev, 0), - MTOUCHUSB_RESET, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 1, - 0, - NULL, - 0, - USB_CTRL_SET_TIMEOUT); - dbg("%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d", - __FUNCTION__, nRet); - - dbg("%s - usb_alloc_urb: mtouch->irq", __FUNCTION__); - mtouch->irq = usb_alloc_urb(0, GFP_KERNEL); - if (!mtouch->irq) { - dbg("%s - usb_alloc_urb failed: mtouch->irq", __FUNCTION__); - mtouchusb_free_buffers(udev, mtouch); - kfree(mtouch); - return -ENOMEM; - } - - dbg("%s - usb_fill_int_urb", __FUNCTION__); - usb_fill_int_urb(mtouch->irq, - mtouch->udev, - usb_rcvintpipe(mtouch->udev, 0x81), - mtouch->data, - MTOUCHUSB_REPORT_DATA_SIZE, - mtouchusb_irq, - mtouch, - endpoint->bInterval); - - dbg("%s - input_register_device", __FUNCTION__); - input_register_device(&mtouch->input); - - nRet = usb_control_msg(mtouch->udev, - usb_rcvctrlpipe(udev, 0), - MTOUCHUSB_ASYNC_REPORT, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 1, - 1, - NULL, - 0, - USB_CTRL_SET_TIMEOUT); - dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d", - __FUNCTION__, nRet); - - printk(KERN_INFO "input: %s on %s\n", mtouch->name, path); - usb_set_intfdata(intf, mtouch); - - return 0; + if (!strlen(mtouch->name)) + sprintf(mtouch->name, "USB Touchscreen %04x:%04x", + mtouch->input.id.vendor, mtouch->input.id.product); + + nRet = usb_control_msg(mtouch->udev, usb_rcvctrlpipe(udev, 0), + MTOUCHUSB_RESET, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); + dbg("%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d", + __FUNCTION__, nRet); + + dbg("%s - usb_alloc_urb: mtouch->irq", __FUNCTION__); + mtouch->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!mtouch->irq) { + dbg("%s - usb_alloc_urb failed: mtouch->irq", __FUNCTION__); + mtouchusb_free_buffers(udev, mtouch); + kfree(mtouch); + return -ENOMEM; + } + + dbg("%s - usb_fill_int_urb", __FUNCTION__); + usb_fill_int_urb(mtouch->irq, mtouch->udev, + usb_rcvintpipe(mtouch->udev, 0x81), + mtouch->data, MTOUCHUSB_REPORT_DATA_SIZE, + mtouchusb_irq, mtouch, endpoint->bInterval); + + dbg("%s - input_register_device", __FUNCTION__); + input_register_device(&mtouch->input); + + nRet = usb_control_msg(mtouch->udev, usb_rcvctrlpipe(udev, 0), + MTOUCHUSB_ASYNC_REPORT, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT); + dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d", + __FUNCTION__, nRet); + + printk(KERN_INFO "input: %s on %s\n", mtouch->name, path); + usb_set_intfdata(intf, mtouch); + + return 0; } static void mtouchusb_disconnect(struct usb_interface *intf) { - struct mtouch_usb *mtouch = usb_get_intfdata (intf); - - dbg("%s - called", __FUNCTION__); - usb_set_intfdata(intf, NULL); - if (mtouch) { - dbg("%s - mtouch is initialized, cleaning up", __FUNCTION__); - usb_kill_urb(mtouch->irq); - input_unregister_device(&mtouch->input); - usb_free_urb(mtouch->irq); - mtouchusb_free_buffers(interface_to_usbdev(intf), mtouch); - kfree(mtouch); - } + struct mtouch_usb *mtouch = usb_get_intfdata(intf); + + dbg("%s - called", __FUNCTION__); + usb_set_intfdata(intf, NULL); + if (mtouch) { + dbg("%s - mtouch is initialized, cleaning up", __FUNCTION__); + usb_kill_urb(mtouch->irq); + input_unregister_device(&mtouch->input); + usb_free_urb(mtouch->irq); + mtouchusb_free_buffers(interface_to_usbdev(intf), mtouch); + kfree(mtouch); + } } -MODULE_DEVICE_TABLE (usb, mtouchusb_devices); +MODULE_DEVICE_TABLE(usb, mtouchusb_devices); static struct usb_driver mtouchusb_driver = { - .owner = THIS_MODULE, - .name = "mtouchusb", - .probe = mtouchusb_probe, - .disconnect = mtouchusb_disconnect, - .id_table = mtouchusb_devices, + .owner = THIS_MODULE, + .name = "mtouchusb", + .probe = mtouchusb_probe, + .disconnect = mtouchusb_disconnect, + .id_table = mtouchusb_devices, }; -static int __init mtouchusb_init(void) { - dbg("%s - called", __FUNCTION__); - return usb_register(&mtouchusb_driver); +static int __init mtouchusb_init(void) +{ + dbg("%s - called", __FUNCTION__); + return usb_register(&mtouchusb_driver); } -static void __exit mtouchusb_cleanup(void) { - dbg("%s - called", __FUNCTION__); - usb_deregister(&mtouchusb_driver); +static void __exit mtouchusb_cleanup(void) +{ + dbg("%s - called", __FUNCTION__); + usb_deregister(&mtouchusb_driver); } module_init(mtouchusb_init); module_exit(mtouchusb_cleanup); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/input/powermate.c b/drivers/usb/input/powermate.c index 7fa2f9b9fb6..3975b309d55 100644 --- a/drivers/usb/input/powermate.c +++ b/drivers/usb/input/powermate.c @@ -10,7 +10,7 @@ * back to the host when polled by the USB controller. * * Testing with the knob I have has shown that it measures approximately 94 "clicks" - * for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was + * for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was * a variable speed cordless electric drill) has shown that the device can measure * speeds of up to 7 clicks either clockwise or anticlockwise between pollings from * the host. If it counts more than 7 clicks before it is polled, it will wrap back @@ -120,9 +120,9 @@ exit: /* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */ static void powermate_sync_state(struct powermate_device *pm) { - if (pm->requires_update == 0) + if (pm->requires_update == 0) return; /* no updates are required */ - if (pm->config->status == -EINPROGRESS) + if (pm->config->status == -EINPROGRESS) return; /* an update is already in progress; it'll issue this update when it completes */ if (pm->requires_update & UPDATE_PULSE_ASLEEP){ @@ -142,7 +142,7 @@ static void powermate_sync_state(struct powermate_device *pm) 2: multiply the speed the argument only has an effect for operations 0 and 2, and ranges between 1 (least effect) to 255 (maximum effect). - + thus, several states are equivalent and are coalesced into one state. we map this onto a range from 0 to 510, with: @@ -151,7 +151,7 @@ static void powermate_sync_state(struct powermate_device *pm) 256 -- 510 -- use multiple (510 = fastest). Only values of 'arg' quite close to 255 are particularly useful/spectacular. - */ + */ if (pm->pulse_speed < 255){ op = 0; // divide arg = 255 - pm->pulse_speed; @@ -199,14 +199,14 @@ static void powermate_config_complete(struct urb *urb, struct pt_regs *regs) if (urb->status) printk(KERN_ERR "powermate: config urb returned %d\n", urb->status); - + spin_lock_irqsave(&pm->lock, flags); powermate_sync_state(pm); spin_unlock_irqrestore(&pm->lock, flags); } /* Set the LED up as described and begin the sync with the hardware if required */ -static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed, +static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed, int pulse_table, int pulse_asleep, int pulse_awake) { unsigned long flags; @@ -229,7 +229,7 @@ static void powermate_pulse_led(struct powermate_device *pm, int static_brightne /* mark state updates which are required */ if (static_brightness != pm->static_brightness){ pm->static_brightness = static_brightness; - pm->requires_update |= UPDATE_STATIC_BRIGHTNESS; + pm->requires_update |= UPDATE_STATIC_BRIGHTNESS; } if (pulse_asleep != pm->pulse_asleep){ pm->pulse_asleep = pulse_asleep; @@ -246,7 +246,7 @@ static void powermate_pulse_led(struct powermate_device *pm, int static_brightne } powermate_sync_state(pm); - + spin_unlock_irqrestore(&pm->lock, flags); } @@ -257,19 +257,19 @@ static int powermate_input_event(struct input_dev *dev, unsigned int type, unsig struct powermate_device *pm = dev->private; if (type == EV_MSC && code == MSC_PULSELED){ - /* + /* bits 0- 7: 8 bits: LED brightness bits 8-16: 9 bits: pulsing speed modifier (0 ... 510); 0-254 = slower, 255 = standard, 256-510 = faster. bits 17-18: 2 bits: pulse table (0, 1, 2 valid) bit 19: 1 bit : pulse whilst asleep? bit 20: 1 bit : pulse constantly? - */ + */ int static_brightness = command & 0xFF; // bits 0-7 int pulse_speed = (command >> 8) & 0x1FF; // bits 8-16 int pulse_table = (command >> 17) & 0x3; // bits 17-18 int pulse_asleep = (command >> 19) & 0x1; // bit 19 int pulse_awake = (command >> 20) & 0x1; // bit 20 - + powermate_pulse_led(pm, static_brightness, pulse_speed, pulse_table, pulse_asleep, pulse_awake); } @@ -378,7 +378,7 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i switch (le16_to_cpu(udev->descriptor.idProduct)) { case POWERMATE_PRODUCT_NEW: pm->input.name = pm_name_powermate; break; case POWERMATE_PRODUCT_OLD: pm->input.name = pm_name_soundknob; break; - default: + default: pm->input.name = pm_name_soundknob; printk(KERN_WARNING "powermate: unknown product id %04x\n", le16_to_cpu(udev->descriptor.idProduct)); @@ -402,11 +402,11 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i usb_make_path(udev, path, 64); snprintf(pm->phys, 64, "%s/input0", path); printk(KERN_INFO "input: %s on %s\n", pm->input.name, pm->input.phys); - + /* force an update of everything */ pm->requires_update = UPDATE_PULSE_ASLEEP | UPDATE_PULSE_AWAKE | UPDATE_PULSE_MODE | UPDATE_STATIC_BRIGHTNESS; powermate_pulse_led(pm, 0x80, 255, 0, 1, 0); // set default pulse parameters - + usb_set_intfdata(intf, pm); return 0; } diff --git a/drivers/usb/input/touchkitusb.c b/drivers/usb/input/touchkitusb.c index a71f1bbd0a1..386595ee21c 100644 --- a/drivers/usb/input/touchkitusb.c +++ b/drivers/usb/input/touchkitusb.c @@ -69,7 +69,6 @@ struct touchkit_usb { struct urb *irq; struct usb_device *udev; struct input_dev input; - int open; char name[128]; char phys[64]; }; @@ -134,15 +133,10 @@ static int touchkit_open(struct input_dev *input) { struct touchkit_usb *touchkit = input->private; - if (touchkit->open++) - return 0; - touchkit->irq->dev = touchkit->udev; - if (usb_submit_urb(touchkit->irq, GFP_ATOMIC)) { - touchkit->open--; + if (usb_submit_urb(touchkit->irq, GFP_ATOMIC)) return -EIO; - } return 0; } @@ -151,8 +145,7 @@ static void touchkit_close(struct input_dev *input) { struct touchkit_usb *touchkit = input->private; - if (!--touchkit->open) - usb_kill_urb(touchkit->irq); + usb_kill_urb(touchkit->irq); } static int touchkit_alloc_buffers(struct usb_device *udev, diff --git a/drivers/usb/input/usbkbd.c b/drivers/usb/input/usbkbd.c index 7038fb9d1ce..f35db1974c4 100644 --- a/drivers/usb/input/usbkbd.c +++ b/drivers/usb/input/usbkbd.c @@ -9,18 +9,18 @@ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic @@ -72,7 +72,6 @@ struct usb_kbd { unsigned char newleds; char name[128]; char phys[64]; - int open; unsigned char *new; struct usb_ctrlrequest *cr; @@ -166,7 +165,7 @@ static void usb_kbd_led(struct urb *urb, struct pt_regs *regs) if (urb->status) warn("led urb status %d received", urb->status); - + if (*(kbd->leds) == kbd->newleds) return; @@ -180,14 +179,9 @@ static int usb_kbd_open(struct input_dev *dev) { struct usb_kbd *kbd = dev->private; - if (kbd->open++) - return 0; - kbd->irq->dev = kbd->usbdev; - if (usb_submit_urb(kbd->irq, GFP_KERNEL)) { - kbd->open--; + if (usb_submit_urb(kbd->irq, GFP_KERNEL)) return -EIO; - } return 0; } @@ -196,8 +190,7 @@ static void usb_kbd_close(struct input_dev *dev) { struct usb_kbd *kbd = dev->private; - if (!--kbd->open) - usb_kill_urb(kbd->irq); + usb_kill_urb(kbd->irq); } static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd) @@ -230,7 +223,7 @@ static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd) usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma); } -static int usb_kbd_probe(struct usb_interface *iface, +static int usb_kbd_probe(struct usb_interface *iface, const struct usb_device_id *id) { struct usb_device * dev = interface_to_usbdev(iface); @@ -272,7 +265,7 @@ static int usb_kbd_probe(struct usb_interface *iface, for (i = 0; i < 255; i++) set_bit(usb_kbd_keycode[i], kbd->dev.keybit); clear_bit(0, kbd->dev.keybit); - + kbd->dev.private = kbd; kbd->dev.event = usb_kbd_event; kbd->dev.open = usb_kbd_open; @@ -294,7 +287,7 @@ static int usb_kbd_probe(struct usb_interface *iface, sprintf(kbd->phys, "%s/input0", path); kbd->dev.name = kbd->name; - kbd->dev.phys = kbd->phys; + kbd->dev.phys = kbd->phys; kbd->dev.id.bustype = BUS_USB; kbd->dev.id.vendor = le16_to_cpu(dev->descriptor.idVendor); kbd->dev.id.product = le16_to_cpu(dev->descriptor.idProduct); @@ -329,7 +322,7 @@ static int usb_kbd_probe(struct usb_interface *iface, static void usb_kbd_disconnect(struct usb_interface *intf) { struct usb_kbd *kbd = usb_get_intfdata (intf); - + usb_set_intfdata(intf, NULL); if (kbd) { usb_kill_urb(kbd->irq); diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c index 01155bbddd4..1ec41b5effe 100644 --- a/drivers/usb/input/usbmouse.c +++ b/drivers/usb/input/usbmouse.c @@ -9,18 +9,18 @@ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic @@ -51,7 +51,6 @@ struct usb_mouse { struct usb_device *usbdev; struct input_dev dev; struct urb *irq; - int open; signed char *data; dma_addr_t data_dma; @@ -101,14 +100,9 @@ static int usb_mouse_open(struct input_dev *dev) { struct usb_mouse *mouse = dev->private; - if (mouse->open++) - return 0; - mouse->irq->dev = mouse->usbdev; - if (usb_submit_urb(mouse->irq, GFP_KERNEL)) { - mouse->open--; + if (usb_submit_urb(mouse->irq, GFP_KERNEL)) return -EIO; - } return 0; } @@ -117,8 +111,7 @@ static void usb_mouse_close(struct input_dev *dev) { struct usb_mouse *mouse = dev->private; - if (!--mouse->open) - usb_kill_urb(mouse->irq); + usb_kill_urb(mouse->irq); } static int usb_mouse_probe(struct usb_interface * intf, const struct usb_device_id * id) @@ -132,19 +125,19 @@ static int usb_mouse_probe(struct usb_interface * intf, const struct usb_device_ interface = intf->cur_altsetting; - if (interface->desc.bNumEndpoints != 1) + if (interface->desc.bNumEndpoints != 1) return -ENODEV; endpoint = &interface->endpoint[0].desc; - if (!(endpoint->bEndpointAddress & 0x80)) + if (!(endpoint->bEndpointAddress & 0x80)) return -ENODEV; - if ((endpoint->bmAttributes & 3) != 3) + if ((endpoint->bmAttributes & 3) != 3) return -ENODEV; pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - if (!(mouse = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL))) + if (!(mouse = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL))) return -ENOMEM; memset(mouse, 0, sizeof(struct usb_mouse)); @@ -209,7 +202,7 @@ static int usb_mouse_probe(struct usb_interface * intf, const struct usb_device_ static void usb_mouse_disconnect(struct usb_interface *intf) { struct usb_mouse *mouse = usb_get_intfdata (intf); - + usb_set_intfdata(intf, NULL); if (mouse) { usb_kill_urb(mouse->irq); @@ -238,7 +231,7 @@ static struct usb_driver usb_mouse_driver = { static int __init usb_mouse_init(void) { int retval = usb_register(&usb_mouse_driver); - if (retval == 0) + if (retval == 0) info(DRIVER_VERSION ":" DRIVER_DESC); return retval; } diff --git a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c index fec04dda088..f6b34af66b3 100644 --- a/drivers/usb/input/wacom.c +++ b/drivers/usb/input/wacom.c @@ -9,7 +9,7 @@ * Copyright (c) 2000 Daniel Egger <egger@suse.de> * Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com> * Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be> - * Copyright (c) 2002-2004 Ping Cheng <pingc@wacom.com> + * Copyright (c) 2002-2005 Ping Cheng <pingc@wacom.com> * * ChangeLog: * v0.1 (vp) - Initial release @@ -18,7 +18,7 @@ * v0.4 (sm) - Support for more Intuos models, menustrip * relative mode, proximity. * v0.5 (vp) - Big cleanup, nifty features removed, - * they belong in userspace + * they belong in userspace * v1.8 (vp) - Submit URB only when operating, moved to CVS, * use input_report_key instead of report_btn and * other cleanups @@ -51,6 +51,9 @@ * - Cleanups here and there * v1.30.1 (pi) - Added Graphire3 support * v1.40 (pc) - Add support for several new devices, fix eraser reporting, ... + * v1.43 (pc) - Added support for Cintiq 21UX + - Fixed a Graphire bug + - Merged wacom_intuos3_irq into wacom_intuos_irq */ /* @@ -72,7 +75,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.40" +#define DRIVER_VERSION "v1.43" #define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>" #define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver" #define DRIVER_LICENSE "GPL" @@ -83,6 +86,16 @@ MODULE_LICENSE(DRIVER_LICENSE); #define USB_VENDOR_ID_WACOM 0x056a +enum { + PENPARTNER = 0, + GRAPHIRE, + PL, + INTUOS, + INTUOS3, + CINTIQ, + MAX_TYPE +}; + struct wacom_features { char *name; int pktlen; @@ -102,7 +115,6 @@ struct wacom { struct urb *irq; struct wacom_features *features; int tool[2]; - int open; __u32 serial[2]; char phys[32]; }; @@ -149,7 +161,7 @@ static void wacom_pl_irq(struct urb *urb, struct pt_regs *regs) prox = data[1] & 0x40; input_regs(dev, regs); - + if (prox) { pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1)); @@ -166,8 +178,7 @@ static void wacom_pl_irq(struct urb *urb, struct pt_regs *regs) if (!wacom->tool[0]) { /* Going into proximity select tool */ wacom->tool[1] = (data[4] & 0x20)? BTN_TOOL_RUBBER : BTN_TOOL_PEN; - } - else { + } else { /* was entered with stylus2 pressed */ if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20) ) { /* report out proximity for previous tool */ @@ -182,16 +193,15 @@ static void wacom_pl_irq(struct urb *urb, struct pt_regs *regs) wacom->tool[1] = BTN_TOOL_PEN; } input_report_key(dev, wacom->tool[1], prox); /* report in proximity for tool */ - input_report_abs(dev, ABS_X, data[3] | ((__u32)data[2] << 7) | ((__u32)(data[1] & 0x03) << 14)); - input_report_abs(dev, ABS_Y, data[6] | ((__u32)data[5] << 7) | ((__u32)(data[4] & 0x03) << 14)); + input_report_abs(dev, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14)); + input_report_abs(dev, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14)); input_report_abs(dev, ABS_PRESSURE, pressure); input_report_key(dev, BTN_TOUCH, data[4] & 0x08); input_report_key(dev, BTN_STYLUS, data[4] & 0x10); /* Only allow the stylus2 button to be reported for the pen tool. */ input_report_key(dev, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20)); - } - else { + } else { /* report proximity-out of a (valid) tool */ if (wacom->tool[1] != BTN_TOOL_RUBBER) { /* Unknown tool selected default to pen tool */ @@ -203,7 +213,7 @@ static void wacom_pl_irq(struct urb *urb, struct pt_regs *regs) wacom->tool[0] = prox; /* Save proximity state */ input_sync(dev); -exit: + exit: retval = usb_submit_urb (urb, GFP_ATOMIC); if (retval) err ("%s - usb_submit_urb failed with result %d", @@ -232,20 +242,16 @@ static void wacom_ptu_irq(struct urb *urb, struct pt_regs *regs) goto exit; } - if (data[0] != 2) - { + if (data[0] != 2) { printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]); goto exit; } input_regs(dev, regs); - if (data[1] & 0x04) - { + if (data[1] & 0x04) { input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x20); input_report_key(dev, BTN_TOUCH, data[1] & 0x08); - } - else - { + } else { input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x20); input_report_key(dev, BTN_TOUCH, data[1] & 0x01); } @@ -257,7 +263,7 @@ static void wacom_ptu_irq(struct urb *urb, struct pt_regs *regs) input_sync(dev); -exit: + exit: retval = usb_submit_urb (urb, GFP_ATOMIC); if (retval) err ("%s - usb_submit_urb failed with result %d", @@ -300,7 +306,7 @@ static void wacom_penpartner_irq(struct urb *urb, struct pt_regs *regs) input_report_key(dev, BTN_STYLUS, (data[5] & 0x40)); input_sync(dev); -exit: + exit: retval = usb_submit_urb (urb, GFP_ATOMIC); if (retval) err ("%s - usb_submit_urb failed with result %d", @@ -340,47 +346,47 @@ static void wacom_graphire_irq(struct urb *urb, struct pt_regs *regs) input_regs(dev, regs); - switch ((data[1] >> 5) & 3) { + if (data[1] & 0x10) { /* in prox */ - case 0: /* Pen */ - input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x80); - break; + switch ((data[1] >> 5) & 3) { - case 1: /* Rubber */ - input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x80); - break; - - case 2: /* Mouse with wheel */ - input_report_key(dev, BTN_MIDDLE, data[1] & 0x04); - input_report_rel(dev, REL_WHEEL, (signed char) data[6]); - /* fall through */ + case 0: /* Pen */ + wacom->tool[0] = BTN_TOOL_PEN; + break; - case 3: /* Mouse without wheel */ - input_report_key(dev, BTN_TOOL_MOUSE, data[7] > 24); - input_report_key(dev, BTN_LEFT, data[1] & 0x01); - input_report_key(dev, BTN_RIGHT, data[1] & 0x02); - input_report_abs(dev, ABS_DISTANCE, data[7]); + case 1: /* Rubber */ + wacom->tool[0] = BTN_TOOL_RUBBER; + break; - input_report_abs(dev, ABS_X, x); - input_report_abs(dev, ABS_Y, y); + case 2: /* Mouse with wheel */ + input_report_key(dev, BTN_MIDDLE, data[1] & 0x04); + input_report_rel(dev, REL_WHEEL, (signed char) data[6]); + /* fall through */ - input_sync(dev); - goto exit; + case 3: /* Mouse without wheel */ + wacom->tool[0] = BTN_TOOL_MOUSE; + input_report_key(dev, BTN_LEFT, data[1] & 0x01); + input_report_key(dev, BTN_RIGHT, data[1] & 0x02); + input_report_abs(dev, ABS_DISTANCE, data[7]); + break; + } } if (data[1] & 0x80) { input_report_abs(dev, ABS_X, x); input_report_abs(dev, ABS_Y, y); } + if (wacom->tool[0] != BTN_TOOL_MOUSE) { + input_report_abs(dev, ABS_PRESSURE, le16_to_cpu(*(__le16 *) &data[6])); + input_report_key(dev, BTN_TOUCH, data[1] & 0x01); + input_report_key(dev, BTN_STYLUS, data[1] & 0x02); + input_report_key(dev, BTN_STYLUS2, data[1] & 0x04); + } - input_report_abs(dev, ABS_PRESSURE, le16_to_cpu(*(__le16 *) &data[6])); - input_report_key(dev, BTN_TOUCH, data[1] & 0x01); - input_report_key(dev, BTN_STYLUS, data[1] & 0x02); - input_report_key(dev, BTN_STYLUS2, data[1] & 0x04); - + input_report_key(dev, wacom->tool[0], data[1] & 0x10); input_sync(dev); -exit: + exit: retval = usb_submit_urb (urb, GFP_ATOMIC); if (retval) err ("%s - usb_submit_urb failed with result %d", @@ -398,14 +404,13 @@ static int wacom_intuos_inout(struct urb *urb) idx = data[1] & 0x01; /* Enter report */ - if ((data[1] & 0xfc) == 0xc0) - { + if ((data[1] & 0xfc) == 0xc0) { /* serial number of the tool */ - wacom->serial[idx] = ((__u32)(data[3] & 0x0f) << 28) + - ((__u32)data[4] << 20) + ((__u32)data[5] << 12) + - ((__u32)data[6] << 4) + (data[7] >> 4); + wacom->serial[idx] = ((data[3] & 0x0f) << 28) + + (data[4] << 20) + (data[5] << 12) + + (data[6] << 4) + (data[7] >> 4); - switch (((__u32)data[2] << 4) | (data[3] >> 4)) { + switch ((data[2] << 4) | (data[3] >> 4)) { case 0x812: /* Inking pen */ case 0x801: /* Intuos3 Inking pen */ case 0x012: @@ -449,7 +454,7 @@ static int wacom_intuos_inout(struct urb *urb) case 0x112: case 0x913: /* Intuos3 Airbrush */ wacom->tool[idx] = BTN_TOOL_AIRBRUSH; - break; /* Airbrush */ + break; default: /* Unknown tool */ wacom->tool[idx] = BTN_TOOL_PEN; } @@ -478,9 +483,8 @@ static void wacom_intuos_general(struct urb *urb) unsigned int t; /* general pen packet */ - if ((data[1] & 0xb8) == 0xa0) - { - t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3); + if ((data[1] & 0xb8) == 0xa0) { + t = (data[6] << 2) | ((data[7] >> 6) & 3); input_report_abs(dev, ABS_PRESSURE, t); input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7)); @@ -491,10 +495,9 @@ static void wacom_intuos_general(struct urb *urb) } /* airbrush second packet */ - if ((data[1] & 0xbc) == 0xb4) - { + if ((data[1] & 0xbc) == 0xb4) { input_report_abs(dev, ABS_WHEEL, - ((__u32)data[6] << 2) | ((data[7] >> 6) & 3)); + (data[6] << 2) | ((data[7] >> 6) & 3)); input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7)); input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f); @@ -526,7 +529,7 @@ static void wacom_intuos_irq(struct urb *urb, struct pt_regs *regs) goto exit; } - if (data[0] != 2 && data[0] != 5 && data[0] != 6) { + if (data[0] != 2 && data[0] != 5 && data[0] != 6 && data[0] != 12) { dbg("wacom_intuos_irq: received unknown report #%d", data[0]); goto exit; } @@ -536,107 +539,10 @@ static void wacom_intuos_irq(struct urb *urb, struct pt_regs *regs) /* tool number */ idx = data[1] & 0x01; - /* process in/out prox events */ - if (wacom_intuos_inout(urb)) goto exit; - - input_report_abs(dev, ABS_X, be16_to_cpu(*(__be16 *) &data[2])); - input_report_abs(dev, ABS_Y, be16_to_cpu(*(__be16 *) &data[4])); - input_report_abs(dev, ABS_DISTANCE, data[9]); - - /* process general packets */ - wacom_intuos_general(urb); - - if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) { /* 4D mouse or Lens cursor packets */ - - if (data[1] & 0x02) { /* Rotation packet */ - - t = ((__u32)data[6] << 3) | ((data[7] >> 5) & 7); - input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ? ((t - 1) / 2) : -t / 2); - - } else { - - if ((data[1] & 0x10) == 0) { /* 4D mouse packets */ - - input_report_key(dev, BTN_LEFT, data[8] & 0x01); - input_report_key(dev, BTN_MIDDLE, data[8] & 0x02); - input_report_key(dev, BTN_RIGHT, data[8] & 0x04); - - input_report_key(dev, BTN_SIDE, data[8] & 0x20); - input_report_key(dev, BTN_EXTRA, data[8] & 0x10); - t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3); - input_report_abs(dev, ABS_THROTTLE, (data[8] & 0x08) ? -t : t); - - } else { - if (wacom->tool[idx] == BTN_TOOL_MOUSE) { /* 2D mouse packets */ - input_report_key(dev, BTN_LEFT, data[8] & 0x04); - input_report_key(dev, BTN_MIDDLE, data[8] & 0x08); - input_report_key(dev, BTN_RIGHT, data[8] & 0x10); - input_report_rel(dev, REL_WHEEL, - (-(__u32)(data[8] & 0x01) + (__u32)((data[8] & 0x02) >> 1))); - } - else { /* Lens cursor packets */ - input_report_key(dev, BTN_LEFT, data[8] & 0x01); - input_report_key(dev, BTN_MIDDLE, data[8] & 0x02); - input_report_key(dev, BTN_RIGHT, data[8] & 0x04); - input_report_key(dev, BTN_SIDE, data[8] & 0x10); - input_report_key(dev, BTN_EXTRA, data[8] & 0x08); - } - } - } - } - - input_report_key(dev, wacom->tool[idx], 1); - input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]); - input_sync(dev); - -exit: - retval = usb_submit_urb (urb, GFP_ATOMIC); - if (retval) - err ("%s - usb_submit_urb failed with result %d", - __FUNCTION__, retval); -} - -static void wacom_intuos3_irq(struct urb *urb, struct pt_regs *regs) -{ - struct wacom *wacom = urb->context; - unsigned char *data = wacom->data; - struct input_dev *dev = &wacom->dev; - unsigned int t; - int idx, retval; - - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); - goto exit; - } - - /* check for valid report */ - if (data[0] != 2 && data[0] != 5 && data[0] != 12) - { - printk(KERN_INFO "wacom_intuos3_irq: received unknown report #%d\n", data[0]); - goto exit; - } - - input_regs(dev, regs); - - /* tool index is always 0 here since there is no dual input tool */ - idx = data[1] & 0x01; - /* pad packets. Works as a second tool and is always in prox */ - if (data[0] == 12) - { + if (data[0] == 12) { /* initiate the pad as a device */ - if (wacom->tool[1] != BTN_TOOL_FINGER) - { + if (wacom->tool[1] != BTN_TOOL_FINGER) { wacom->tool[1] = BTN_TOOL_FINGER; input_report_key(dev, wacom->tool[1], 1); } @@ -656,37 +562,78 @@ static void wacom_intuos3_irq(struct urb *urb, struct pt_regs *regs) } /* process in/out prox events */ - if (wacom_intuos_inout(urb)) goto exit; + if (wacom_intuos_inout(urb)) + goto exit; - input_report_abs(dev, ABS_X, ((__u32)data[2] << 9) | ((__u32)data[3] << 1) | ((data[9] >> 1) & 1)); - input_report_abs(dev, ABS_Y, ((__u32)data[4] << 9) | ((__u32)data[5] << 1) | (data[9] & 1)); - input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 2) & 0x3f)); + /* Cintiq doesn't send data when RDY bit isn't set */ + if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40)) + return; + + if (wacom->features->type >= INTUOS3) { + input_report_abs(dev, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1)); + input_report_abs(dev, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1)); + input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 2) & 0x3f)); + } else { + input_report_abs(dev, ABS_X, be16_to_cpu(*(__be16 *) &data[2])); + input_report_abs(dev, ABS_Y, be16_to_cpu(*(__be16 *) &data[4])); + input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 3) & 0x1f)); + } /* process general packets */ wacom_intuos_general(urb); - if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) - { - /* Marker pen rotation packet. Reported as wheel due to valuator limitation */ - if (data[1] & 0x02) - { - t = ((__u32)data[6] << 3) | ((data[7] >> 5) & 7); - t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) : - ((t-1) / 2 + 450)) : (450 - t / 2) ; - input_report_abs(dev, ABS_WHEEL, t); - } + /* 4D mouse, 2D mouse, marker pen rotation, or Lens cursor packets */ + if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) { + + if (data[1] & 0x02) { + /* Rotation packet */ + if (wacom->features->type >= INTUOS3) { + /* I3 marker pen rotation reported as wheel + * due to valuator limitation + */ + t = (data[6] << 3) | ((data[7] >> 5) & 7); + t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) : + ((t-1) / 2 + 450)) : (450 - t / 2) ; + input_report_abs(dev, ABS_WHEEL, t); + } else { + /* 4D mouse rotation packet */ + t = (data[6] << 3) | ((data[7] >> 5) & 7); + input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ? + ((t - 1) / 2) : -t / 2); + } - /* 2D mouse packets */ - if (wacom->tool[idx] == BTN_TOOL_MOUSE) - { + } else if (!(data[1] & 0x10) && wacom->features->type < INTUOS3) { + /* 4D mouse packet */ + input_report_key(dev, BTN_LEFT, data[8] & 0x01); + input_report_key(dev, BTN_MIDDLE, data[8] & 0x02); + input_report_key(dev, BTN_RIGHT, data[8] & 0x04); + + input_report_key(dev, BTN_SIDE, data[8] & 0x20); + input_report_key(dev, BTN_EXTRA, data[8] & 0x10); + t = (data[6] << 2) | ((data[7] >> 6) & 3); + input_report_abs(dev, ABS_THROTTLE, (data[8] & 0x08) ? -t : t); + + } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) { + /* 2D mouse packet */ input_report_key(dev, BTN_LEFT, data[8] & 0x04); input_report_key(dev, BTN_MIDDLE, data[8] & 0x08); input_report_key(dev, BTN_RIGHT, data[8] & 0x10); - input_report_key(dev, BTN_SIDE, data[8] & 0x40); - input_report_key(dev, BTN_EXTRA, data[8] & 0x20); - /* mouse wheel is positive when rolled backwards */ - input_report_rel(dev, REL_WHEEL, ((__u32)((data[8] & 0x02) >> 1) - - (__u32)(data[8] & 0x01))); + input_report_rel(dev, REL_WHEEL, ((data[8] & 0x02) >> 1) + - (data[8] & 0x01)); + + /* I3 2D mouse side buttons */ + if (wacom->features->type == INTUOS3) { + input_report_key(dev, BTN_SIDE, data[8] & 0x40); + input_report_key(dev, BTN_EXTRA, data[8] & 0x20); + } + + } else if (wacom->features->type < INTUOS3) { + /* Lens cursor packets */ + input_report_key(dev, BTN_LEFT, data[8] & 0x01); + input_report_key(dev, BTN_MIDDLE, data[8] & 0x02); + input_report_key(dev, BTN_RIGHT, data[8] & 0x04); + input_report_key(dev, BTN_SIDE, data[8] & 0x10); + input_report_key(dev, BTN_EXTRA, data[8] & 0x08); } } @@ -702,35 +649,36 @@ exit: } static struct wacom_features wacom_features[] = { - { "Wacom Penpartner", 7, 5040, 3780, 255, 32, 0, wacom_penpartner_irq }, - { "Wacom Graphire", 8, 10206, 7422, 511, 32, 1, wacom_graphire_irq }, - { "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 32, 1, wacom_graphire_irq }, - { "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 32, 1, wacom_graphire_irq }, - { "Wacom Graphire3", 8, 10208, 7424, 511, 32, 1, wacom_graphire_irq }, - { "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 32, 1, wacom_graphire_irq }, - { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom PL400", 8, 5408, 4056, 255, 32, 3, wacom_pl_irq }, - { "Wacom PL500", 8, 6144, 4608, 255, 32, 3, wacom_pl_irq }, - { "Wacom PL600", 8, 6126, 4604, 255, 32, 3, wacom_pl_irq }, - { "Wacom PL600SX", 8, 6260, 5016, 255, 32, 3, wacom_pl_irq }, - { "Wacom PL550", 8, 6144, 4608, 511, 32, 3, wacom_pl_irq }, - { "Wacom PL800", 8, 7220, 5780, 511, 32, 3, wacom_pl_irq }, - { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, 2, wacom_intuos_irq }, - { "Wacom Volito", 8, 5104, 3712, 511, 32, 1, wacom_graphire_irq }, - { "Wacom Cintiq Partner",8, 20480, 15360, 511, 32, 3, wacom_ptu_irq }, - { "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 15, 4, wacom_intuos3_irq }, - { "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 15, 4, wacom_intuos3_irq }, - { "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 15, 4, wacom_intuos3_irq }, - { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, 2, wacom_intuos_irq }, - { } + { "Wacom Penpartner", 7, 5040, 3780, 255, 32, PENPARTNER, wacom_penpartner_irq }, + { "Wacom Graphire", 8, 10206, 7422, 511, 32, GRAPHIRE, wacom_graphire_irq }, + { "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 32, GRAPHIRE, wacom_graphire_irq }, + { "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 32, GRAPHIRE, wacom_graphire_irq }, + { "Wacom Graphire3", 8, 10208, 7424, 511, 32, GRAPHIRE, wacom_graphire_irq }, + { "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 32, GRAPHIRE, wacom_graphire_irq }, + { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom PL400", 8, 5408, 4056, 255, 32, PL, wacom_pl_irq }, + { "Wacom PL500", 8, 6144, 4608, 255, 32, PL, wacom_pl_irq }, + { "Wacom PL600", 8, 6126, 4604, 255, 32, PL, wacom_pl_irq }, + { "Wacom PL600SX", 8, 6260, 5016, 255, 32, PL, wacom_pl_irq }, + { "Wacom PL550", 8, 6144, 4608, 511, 32, PL, wacom_pl_irq }, + { "Wacom PL800", 8, 7220, 5780, 511, 32, PL, wacom_pl_irq }, + { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, INTUOS, wacom_intuos_irq }, + { "Wacom Volito", 8, 5104, 3712, 511, 32, GRAPHIRE, wacom_graphire_irq }, + { "Wacom Cintiq Partner",8, 20480, 15360, 511, 32, PL, wacom_ptu_irq }, + { "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 15, INTUOS3, wacom_intuos_irq }, + { "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 15, INTUOS3, wacom_intuos_irq }, + { "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 15, INTUOS3, wacom_intuos_irq }, + { "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 15, CINTIQ, wacom_intuos_irq }, + { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_intuos_irq }, + { } }; static struct usb_device_id wacom_ids[] = { @@ -761,6 +709,7 @@ static struct usb_device_id wacom_ids[] = { { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) }, { } }; @@ -771,14 +720,9 @@ static int wacom_open(struct input_dev *dev) { struct wacom *wacom = dev->private; - if (wacom->open++) - return 0; - wacom->irq->dev = wacom->usbdev; - if (usb_submit_urb(wacom->irq, GFP_KERNEL)) { - wacom->open--; + if (usb_submit_urb(wacom->irq, GFP_KERNEL)) return -EIO; - } return 0; } @@ -787,8 +731,7 @@ static void wacom_close(struct input_dev *dev) { struct wacom *wacom = dev->private; - if (!--wacom->open) - usb_kill_urb(wacom->irq); + usb_kill_urb(wacom->irq); } static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -823,32 +766,33 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS); switch (wacom->features->type) { - case 1: + case GRAPHIRE: wacom->dev.evbit[0] |= BIT(EV_REL); wacom->dev.relbit[0] |= BIT(REL_WHEEL); wacom->dev.absbit[0] |= BIT(ABS_DISTANCE); wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); - wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2); + wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2); break; - case 4: /* new functions for Intuos3 */ + case INTUOS3: + case CINTIQ: wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER); wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7); wacom->dev.absbit[0] |= BIT(ABS_RX) | BIT(ABS_RY); /* fall through */ - case 2: + case INTUOS: wacom->dev.evbit[0] |= BIT(EV_MSC) | BIT(EV_REL); wacom->dev.mscbit[0] |= BIT(MSC_SERIAL); wacom->dev.relbit[0] |= BIT(REL_WHEEL); wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE) | BIT(BTN_SIDE) | BIT(BTN_EXTRA); - wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOOL_BRUSH) + wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOOL_BRUSH) | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS) | BIT(BTN_STYLUS2); wacom->dev.absbit[0] |= BIT(ABS_DISTANCE) | BIT(ABS_WHEEL) | BIT(ABS_TILT_X) | BIT(ABS_TILT_Y) | BIT(ABS_RZ) | BIT(ABS_THROTTLE); break; - case 3: - wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER); + case PL: + wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER); break; } diff --git a/drivers/usb/input/xpad.c b/drivers/usb/input/xpad.c index d65edb22e54..a7fa1b17dcf 100644 --- a/drivers/usb/input/xpad.c +++ b/drivers/usb/input/xpad.c @@ -104,13 +104,12 @@ MODULE_DEVICE_TABLE (usb, xpad_table); struct usb_xpad { struct input_dev dev; /* input device interface */ struct usb_device *udev; /* usb device */ - + struct urb *irq_in; /* urb for interrupt in report */ unsigned char *idata; /* input data */ dma_addr_t idata_dma; - + char phys[65]; /* physical device path */ - int open_count; /* reference count */ }; /* @@ -128,35 +127,35 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d struct input_dev *dev = &xpad->dev; input_regs(dev, regs); - + /* left stick */ input_report_abs(dev, ABS_X, (__s16) (((__s16)data[13] << 8) | data[12])); input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[15] << 8) | data[14])); - + /* right stick */ input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[17] << 8) | data[16])); input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[19] << 8) | data[18])); - + /* triggers left/right */ input_report_abs(dev, ABS_Z, data[10]); input_report_abs(dev, ABS_RZ, data[11]); - + /* digital pad */ input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04)); input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01)); - + /* start/back buttons and stick press left/right */ input_report_key(dev, BTN_START, (data[2] & 0x10) >> 4); input_report_key(dev, BTN_BACK, (data[2] & 0x20) >> 5); input_report_key(dev, BTN_THUMBL, (data[2] & 0x40) >> 6); input_report_key(dev, BTN_THUMBR, data[2] >> 7); - + /* "analog" buttons A, B, X, Y */ input_report_key(dev, BTN_A, data[4]); input_report_key(dev, BTN_B, data[5]); input_report_key(dev, BTN_X, data[6]); input_report_key(dev, BTN_Y, data[7]); - + /* "analog" buttons black, white */ input_report_key(dev, BTN_C, data[8]); input_report_key(dev, BTN_Z, data[9]); @@ -168,7 +167,7 @@ static void xpad_irq_in(struct urb *urb, struct pt_regs *regs) { struct usb_xpad *xpad = urb->context; int retval; - + switch (urb->status) { case 0: /* success */ @@ -183,7 +182,7 @@ static void xpad_irq_in(struct urb *urb, struct pt_regs *regs) dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); goto exit; } - + xpad_process_packet(xpad, 0, xpad->idata, regs); exit: @@ -196,25 +195,19 @@ exit: static int xpad_open (struct input_dev *dev) { struct usb_xpad *xpad = dev->private; - - if (xpad->open_count++) - return 0; - + xpad->irq_in->dev = xpad->udev; - if (usb_submit_urb(xpad->irq_in, GFP_KERNEL)) { - xpad->open_count--; + if (usb_submit_urb(xpad->irq_in, GFP_KERNEL)) return -EIO; - } - + return 0; } static void xpad_close (struct input_dev *dev) { struct usb_xpad *xpad = dev->private; - - if (!--xpad->open_count) - usb_kill_urb(xpad->irq_in); + + usb_kill_urb(xpad->irq_in); } static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -224,19 +217,19 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id struct usb_endpoint_descriptor *ep_irq_in; char path[64]; int i; - + for (i = 0; xpad_device[i].idVendor; i++) { if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) && (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct)) break; } - + if ((xpad = kmalloc (sizeof(struct usb_xpad), GFP_KERNEL)) == NULL) { err("cannot allocate memory for new pad"); return -ENOMEM; } memset(xpad, 0, sizeof(struct usb_xpad)); - + xpad->idata = usb_buffer_alloc(udev, XPAD_PKT_LEN, SLAB_ATOMIC, &xpad->idata_dma); if (!xpad->idata) { @@ -251,18 +244,18 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id kfree(xpad); return -ENOMEM; } - + ep_irq_in = &intf->cur_altsetting->endpoint[0].desc; - + usb_fill_int_urb(xpad->irq_in, udev, usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress), xpad->idata, XPAD_PKT_LEN, xpad_irq_in, xpad, ep_irq_in->bInterval); xpad->irq_in->transfer_dma = xpad->idata_dma; xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - + xpad->udev = udev; - + xpad->dev.id.bustype = BUS_USB; xpad->dev.id.vendor = le16_to_cpu(udev->descriptor.idVendor); xpad->dev.id.product = le16_to_cpu(udev->descriptor.idProduct); @@ -273,21 +266,21 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id xpad->dev.phys = xpad->phys; xpad->dev.open = xpad_open; xpad->dev.close = xpad_close; - + usb_make_path(udev, path, 64); snprintf(xpad->phys, 64, "%s/input0", path); - + xpad->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - + for (i = 0; xpad_btn[i] >= 0; i++) set_bit(xpad_btn[i], xpad->dev.keybit); - + for (i = 0; xpad_abs[i] >= 0; i++) { - + signed short t = xpad_abs[i]; - + set_bit(t, xpad->dev.absbit); - + switch (t) { case ABS_X: case ABS_Y: @@ -310,11 +303,11 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id break; } } - + input_register_device(&xpad->dev); - + printk(KERN_INFO "input: %s on %s", xpad->dev.name, path); - + usb_set_intfdata(intf, xpad); return 0; } @@ -322,7 +315,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id static void xpad_disconnect(struct usb_interface *intf) { struct usb_xpad *xpad = usb_get_intfdata (intf); - + usb_set_intfdata(intf, NULL); if (xpad) { usb_kill_urb(xpad->irq_in); diff --git a/fs/Makefile b/fs/Makefile index fc92e59e9fa..20edcf28bfd 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -10,6 +10,7 @@ obj-y := open.o read_write.o file_table.o buffer.o bio.o super.o \ ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \ attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \ seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \ + ioprio.o obj-$(CONFIG_EPOLL) += eventpoll.o obj-$(CONFIG_COMPAT) += compat.o diff --git a/fs/ioprio.c b/fs/ioprio.c new file mode 100644 index 00000000000..663e420636d --- /dev/null +++ b/fs/ioprio.c @@ -0,0 +1,172 @@ +/* + * fs/ioprio.c + * + * Copyright (C) 2004 Jens Axboe <axboe@suse.de> + * + * Helper functions for setting/querying io priorities of processes. The + * system calls closely mimmick getpriority/setpriority, see the man page for + * those. The prio argument is a composite of prio class and prio data, where + * the data argument has meaning within that class. The standard scheduling + * classes have 8 distinct prio levels, with 0 being the highest prio and 7 + * being the lowest. + * + * IOW, setting BE scheduling class with prio 2 is done ala: + * + * unsigned int prio = (IOPRIO_CLASS_BE << IOPRIO_CLASS_SHIFT) | 2; + * + * ioprio_set(PRIO_PROCESS, pid, prio); + * + * See also Documentation/block/ioprio.txt + * + */ +#include <linux/kernel.h> +#include <linux/ioprio.h> +#include <linux/blkdev.h> + +static int set_task_ioprio(struct task_struct *task, int ioprio) +{ + struct io_context *ioc; + + if (task->uid != current->euid && + task->uid != current->uid && !capable(CAP_SYS_NICE)) + return -EPERM; + + task_lock(task); + + task->ioprio = ioprio; + + ioc = task->io_context; + if (ioc && ioc->set_ioprio) + ioc->set_ioprio(ioc, ioprio); + + task_unlock(task); + return 0; +} + +asmlinkage int sys_ioprio_set(int which, int who, int ioprio) +{ + int class = IOPRIO_PRIO_CLASS(ioprio); + int data = IOPRIO_PRIO_DATA(ioprio); + struct task_struct *p, *g; + struct user_struct *user; + int ret; + + switch (class) { + case IOPRIO_CLASS_RT: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + /* fall through, rt has prio field too */ + case IOPRIO_CLASS_BE: + if (data >= IOPRIO_BE_NR || data < 0) + return -EINVAL; + + break; + case IOPRIO_CLASS_IDLE: + break; + default: + return -EINVAL; + } + + ret = -ESRCH; + read_lock_irq(&tasklist_lock); + switch (which) { + case IOPRIO_WHO_PROCESS: + if (!who) + p = current; + else + p = find_task_by_pid(who); + if (p) + ret = set_task_ioprio(p, ioprio); + break; + case IOPRIO_WHO_PGRP: + if (!who) + who = process_group(current); + do_each_task_pid(who, PIDTYPE_PGID, p) { + ret = set_task_ioprio(p, ioprio); + if (ret) + break; + } while_each_task_pid(who, PIDTYPE_PGID, p); + break; + case IOPRIO_WHO_USER: + if (!who) + user = current->user; + else + user = find_user(who); + + if (!user) + break; + + do_each_thread(g, p) { + if (p->uid != who) + continue; + ret = set_task_ioprio(p, ioprio); + if (ret) + break; + } while_each_thread(g, p); + + if (who) + free_uid(user); + break; + default: + ret = -EINVAL; + } + + read_unlock_irq(&tasklist_lock); + return ret; +} + +asmlinkage int sys_ioprio_get(int which, int who) +{ + struct task_struct *g, *p; + struct user_struct *user; + int ret = -ESRCH; + + read_lock_irq(&tasklist_lock); + switch (which) { + case IOPRIO_WHO_PROCESS: + if (!who) + p = current; + else + p = find_task_by_pid(who); + if (p) + ret = p->ioprio; + break; + case IOPRIO_WHO_PGRP: + if (!who) + who = process_group(current); + do_each_task_pid(who, PIDTYPE_PGID, p) { + if (ret == -ESRCH) + ret = p->ioprio; + else + ret = ioprio_best(ret, p->ioprio); + } while_each_task_pid(who, PIDTYPE_PGID, p); + break; + case IOPRIO_WHO_USER: + if (!who) + user = current->user; + else + user = find_user(who); + + if (!user) + break; + + do_each_thread(g, p) { + if (p->uid != user->uid) + continue; + if (ret == -ESRCH) + ret = p->ioprio; + else + ret = ioprio_best(ret, p->ioprio); + } while_each_thread(g, p); + + if (who) + free_uid(user); + break; + default: + ret = -EINVAL; + } + + read_unlock_irq(&tasklist_lock); + return ret; +} + diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index 7b87707acc3..d1bcf0da672 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -645,18 +645,22 @@ struct buffer_chunk { static void write_chunk(struct buffer_chunk *chunk) { int i; + get_fs_excl(); for (i = 0; i < chunk->nr ; i++) { submit_logged_buffer(chunk->bh[i]) ; } chunk->nr = 0; + put_fs_excl(); } static void write_ordered_chunk(struct buffer_chunk *chunk) { int i; + get_fs_excl(); for (i = 0; i < chunk->nr ; i++) { submit_ordered_buffer(chunk->bh[i]) ; } chunk->nr = 0; + put_fs_excl(); } static int add_to_chunk(struct buffer_chunk *chunk, struct buffer_head *bh, @@ -918,6 +922,8 @@ static int flush_commit_list(struct super_block *s, struct reiserfs_journal_list return 0 ; } + get_fs_excl(); + /* before we can put our commit blocks on disk, we have to make sure everyone older than ** us is on disk too */ @@ -1055,6 +1061,7 @@ put_jl: if (retval) reiserfs_abort (s, retval, "Journal write error in %s", __FUNCTION__); + put_fs_excl(); return retval; } @@ -1251,6 +1258,8 @@ static int flush_journal_list(struct super_block *s, return 0 ; } + get_fs_excl(); + /* if all the work is already done, get out of here */ if (atomic_read(&(jl->j_nonzerolen)) <= 0 && atomic_read(&(jl->j_commit_left)) <= 0) { @@ -1450,6 +1459,7 @@ flush_older_and_return: put_journal_list(s, jl); if (flushall) up(&journal->j_flush_sem); + put_fs_excl(); return err ; } @@ -2719,6 +2729,7 @@ relock: th->t_trans_id = journal->j_trans_id ; unlock_journal(p_s_sb) ; INIT_LIST_HEAD (&th->t_list); + get_fs_excl(); return 0 ; out_fail: @@ -3526,6 +3537,7 @@ static int do_journal_end(struct reiserfs_transaction_handle *th, struct super_b BUG_ON (th->t_refcount > 1); BUG_ON (!th->t_trans_id); + put_fs_excl(); current->journal_info = th->t_handle_save; reiserfs_check_lock_depth(p_s_sb, "journal end"); if (journal->j_len == 0) { diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h index 176413fb9ae..e25e4c71a87 100644 --- a/include/asm-i386/unistd.h +++ b/include/asm-i386/unistd.h @@ -294,8 +294,10 @@ #define __NR_add_key 286 #define __NR_request_key 287 #define __NR_keyctl 288 +#define __NR_ioprio_set 289 +#define __NR_ioprio_get 290 -#define NR_syscalls 289 +#define NR_syscalls 291 /* * user-visible error numbers are in the range -1 - -128: see diff --git a/include/asm-ia64/unistd.h b/include/asm-ia64/unistd.h index f7f43ec2483..517f1649ee6 100644 --- a/include/asm-ia64/unistd.h +++ b/include/asm-ia64/unistd.h @@ -263,6 +263,8 @@ #define __NR_add_key 1271 #define __NR_request_key 1272 #define __NR_keyctl 1273 +#define __NR_ioprio_set 1274 +#define __NR_ioprio_get 1275 #define __NR_set_zone_reclaim 1276 #ifdef __KERNEL__ diff --git a/include/asm-ppc/unistd.h b/include/asm-ppc/unistd.h index cc51e5c9acc..e8b79220b29 100644 --- a/include/asm-ppc/unistd.h +++ b/include/asm-ppc/unistd.h @@ -277,8 +277,10 @@ #define __NR_request_key 270 #define __NR_keyctl 271 #define __NR_waitid 272 +#define __NR_ioprio_set 273 +#define __NR_ioprio_get 274 -#define __NR_syscalls 273 +#define __NR_syscalls 275 #define __NR(n) #n diff --git a/include/asm-x86_64/unistd.h b/include/asm-x86_64/unistd.h index d767adcbf0f..6560439a83e 100644 --- a/include/asm-x86_64/unistd.h +++ b/include/asm-x86_64/unistd.h @@ -561,8 +561,12 @@ __SYSCALL(__NR_add_key, sys_add_key) __SYSCALL(__NR_request_key, sys_request_key) #define __NR_keyctl 250 __SYSCALL(__NR_keyctl, sys_keyctl) +#define __NR_ioprio_set 251 +__SYSCALL(__NR_ioprio_set, sys_ioprio_set) +#define __NR_ioprio_get 252 +__SYSCALL(__NR_ioprio_get, sys_ioprio_get) -#define __NR_syscall_max __NR_keyctl +#define __NR_syscall_max __NR_ioprio_get #ifndef __NO_STUBS /* user-visible error numbers are in the range -1 - -4095 */ diff --git a/include/linux/bio.h b/include/linux/bio.h index 038022763f0..36ef29fa0d8 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -22,6 +22,7 @@ #include <linux/highmem.h> #include <linux/mempool.h> +#include <linux/ioprio.h> /* Platforms may set this to teach the BIO layer about IOMMU hardware. */ #include <asm/io.h> @@ -150,6 +151,19 @@ struct bio { #define BIO_RW_SYNC 4 /* + * upper 16 bits of bi_rw define the io priority of this bio + */ +#define BIO_PRIO_SHIFT (8 * sizeof(unsigned long) - IOPRIO_BITS) +#define bio_prio(bio) ((bio)->bi_rw >> BIO_PRIO_SHIFT) +#define bio_prio_valid(bio) ioprio_valid(bio_prio(bio)) + +#define bio_set_prio(bio, prio) do { \ + WARN_ON(prio >= (1 << IOPRIO_BITS)); \ + (bio)->bi_rw &= ((1UL << BIO_PRIO_SHIFT) - 1); \ + (bio)->bi_rw |= ((unsigned long) (prio) << BIO_PRIO_SHIFT); \ +} while (0) + +/* * various member access, note that bio_data should of course not be used * on highmem page vectors */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index b54a0348a89..21a8674cd14 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -54,16 +54,23 @@ struct as_io_context { struct cfq_queue; struct cfq_io_context { - void (*dtor)(struct cfq_io_context *); - void (*exit)(struct cfq_io_context *); - - struct io_context *ioc; - /* * circular list of cfq_io_contexts belonging to a process io context */ struct list_head list; struct cfq_queue *cfqq; + void *key; + + struct io_context *ioc; + + unsigned long last_end_request; + unsigned long last_queue; + unsigned long ttime_total; + unsigned long ttime_samples; + unsigned long ttime_mean; + + void (*dtor)(struct cfq_io_context *); + void (*exit)(struct cfq_io_context *); }; /* @@ -73,7 +80,9 @@ struct cfq_io_context { */ struct io_context { atomic_t refcount; - pid_t pid; + struct task_struct *task; + + int (*set_ioprio)(struct io_context *, unsigned int); /* * For request batching @@ -81,8 +90,6 @@ struct io_context { unsigned long last_waited; /* Time last woken after wait for request */ int nr_batch_requests; /* Number of requests left in the batch */ - spinlock_t lock; - struct as_io_context *aic; struct cfq_io_context *cic; }; @@ -134,6 +141,8 @@ struct request { void *elevator_private; + unsigned short ioprio; + int rq_status; /* should split this into a few status bits */ struct gendisk *rq_disk; int errors; diff --git a/include/linux/elevator.h b/include/linux/elevator.h index ee54f81faad..ea6bbc2d740 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -16,9 +16,9 @@ typedef void (elevator_remove_req_fn) (request_queue_t *, struct request *); typedef void (elevator_requeue_req_fn) (request_queue_t *, struct request *); typedef struct request *(elevator_request_list_fn) (request_queue_t *, struct request *); typedef void (elevator_completed_req_fn) (request_queue_t *, struct request *); -typedef int (elevator_may_queue_fn) (request_queue_t *, int); +typedef int (elevator_may_queue_fn) (request_queue_t *, int, struct bio *); -typedef int (elevator_set_req_fn) (request_queue_t *, struct request *, int); +typedef int (elevator_set_req_fn) (request_queue_t *, struct request *, struct bio *, int); typedef void (elevator_put_req_fn) (request_queue_t *, struct request *); typedef void (elevator_deactivate_req_fn) (request_queue_t *, struct request *); @@ -96,9 +96,9 @@ extern struct request *elv_former_request(request_queue_t *, struct request *); extern struct request *elv_latter_request(request_queue_t *, struct request *); extern int elv_register_queue(request_queue_t *q); extern void elv_unregister_queue(request_queue_t *q); -extern int elv_may_queue(request_queue_t *, int); +extern int elv_may_queue(request_queue_t *, int, struct bio *); extern void elv_completed_request(request_queue_t *, struct request *); -extern int elv_set_request(request_queue_t *, struct request *, int); +extern int elv_set_request(request_queue_t *, struct request *, struct bio *, int); extern void elv_put_request(request_queue_t *, struct request *); /* diff --git a/include/linux/fs.h b/include/linux/fs.h index 3ae8e37bdfc..047bde30836 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -213,6 +213,7 @@ extern int dir_notify_enable; #include <linux/radix-tree.h> #include <linux/prio_tree.h> #include <linux/init.h> +#include <linux/sched.h> #include <asm/atomic.h> #include <asm/semaphore.h> @@ -822,16 +823,34 @@ enum { #define vfs_check_frozen(sb, level) \ wait_event((sb)->s_wait_unfrozen, ((sb)->s_frozen < (level))) +static inline void get_fs_excl(void) +{ + atomic_inc(¤t->fs_excl); +} + +static inline void put_fs_excl(void) +{ + atomic_dec(¤t->fs_excl); +} + +static inline int has_fs_excl(void) +{ + return atomic_read(¤t->fs_excl); +} + + /* * Superblock locking. */ static inline void lock_super(struct super_block * sb) { + get_fs_excl(); down(&sb->s_lock); } static inline void unlock_super(struct super_block * sb) { + put_fs_excl(); up(&sb->s_lock); } diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 03206a425d7..c727c195a91 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -81,6 +81,7 @@ extern struct group_info init_groups; .mm = NULL, \ .active_mm = &init_mm, \ .run_list = LIST_HEAD_INIT(tsk.run_list), \ + .ioprio = 0, \ .time_slice = HZ, \ .tasks = LIST_HEAD_INIT(tsk.tasks), \ .ptrace_children= LIST_HEAD_INIT(tsk.ptrace_children), \ @@ -110,6 +111,7 @@ extern struct group_info init_groups; .proc_lock = SPIN_LOCK_UNLOCKED, \ .journal_info = NULL, \ .cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), \ + .fs_excl = ATOMIC_INIT(0), \ } diff --git a/include/linux/input.h b/include/linux/input.h index 9d9598ed760..b9cc0ac71f4 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -859,6 +859,10 @@ struct input_dev { int (*erase_effect)(struct input_dev *dev, int effect_id); struct input_handle *grab; + + struct semaphore sem; /* serializes open and close operations */ + unsigned int users; + struct device *dev; struct list_head h_list; diff --git a/include/linux/ioprio.h b/include/linux/ioprio.h new file mode 100644 index 00000000000..8a453a0b5e4 --- /dev/null +++ b/include/linux/ioprio.h @@ -0,0 +1,88 @@ +#ifndef IOPRIO_H +#define IOPRIO_H + +#include <linux/sched.h> + +/* + * Gives us 8 prio classes with 13-bits of data for each class + */ +#define IOPRIO_BITS (16) +#define IOPRIO_CLASS_SHIFT (13) +#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1) + +#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT) +#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK) +#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data) + +#define ioprio_valid(mask) (IOPRIO_PRIO_CLASS((mask)) != IOPRIO_CLASS_NONE) + +/* + * These are the io priority groups as implemented by CFQ. RT is the realtime + * class, it always gets premium service. BE is the best-effort scheduling + * class, the default for any process. IDLE is the idle scheduling class, it + * is only served when no one else is using the disk. + */ +enum { + IOPRIO_CLASS_NONE, + IOPRIO_CLASS_RT, + IOPRIO_CLASS_BE, + IOPRIO_CLASS_IDLE, +}; + +/* + * 8 best effort priority levels are supported + */ +#define IOPRIO_BE_NR (8) + +asmlinkage int sys_ioprio_set(int, int, int); +asmlinkage int sys_ioprio_get(int, int); + +enum { + IOPRIO_WHO_PROCESS = 1, + IOPRIO_WHO_PGRP, + IOPRIO_WHO_USER, +}; + +/* + * if process has set io priority explicitly, use that. if not, convert + * the cpu scheduler nice value to an io priority + */ +#define IOPRIO_NORM (4) +static inline int task_ioprio(struct task_struct *task) +{ + WARN_ON(!ioprio_valid(task->ioprio)); + return IOPRIO_PRIO_DATA(task->ioprio); +} + +static inline int task_nice_ioprio(struct task_struct *task) +{ + return (task_nice(task) + 20) / 5; +} + +/* + * For inheritance, return the highest of the two given priorities + */ +static inline int ioprio_best(unsigned short aprio, unsigned short bprio) +{ + unsigned short aclass = IOPRIO_PRIO_CLASS(aprio); + unsigned short bclass = IOPRIO_PRIO_CLASS(bprio); + + if (!ioprio_valid(aprio)) + return bprio; + if (!ioprio_valid(bprio)) + return aprio; + + if (aclass == IOPRIO_CLASS_NONE) + aclass = IOPRIO_CLASS_BE; + if (bclass == IOPRIO_CLASS_NONE) + bclass = IOPRIO_CLASS_BE; + + if (aclass == bclass) + return min(aprio, bprio); + if (aclass > bclass) + return bprio; + else + return aprio; +} + +#endif diff --git a/include/linux/joystick.h b/include/linux/joystick.h index b7e0ab622cd..06b9af77eb7 100644 --- a/include/linux/joystick.h +++ b/include/linux/joystick.h @@ -111,18 +111,35 @@ struct js_corr { #define JS_SET_ALL 8 struct JS_DATA_TYPE { - int buttons; - int x; - int y; + __s32 buttons; + __s32 x; + __s32 y; }; -struct JS_DATA_SAVE_TYPE { - int JS_TIMEOUT; - int BUSY; - long JS_EXPIRETIME; - long JS_TIMELIMIT; +struct JS_DATA_SAVE_TYPE_32 { + __s32 JS_TIMEOUT; + __s32 BUSY; + __s32 JS_EXPIRETIME; + __s32 JS_TIMELIMIT; struct JS_DATA_TYPE JS_SAVE; struct JS_DATA_TYPE JS_CORR; }; +struct JS_DATA_SAVE_TYPE_64 { + __s32 JS_TIMEOUT; + __s32 BUSY; + __s64 JS_EXPIRETIME; + __s64 JS_TIMELIMIT; + struct JS_DATA_TYPE JS_SAVE; + struct JS_DATA_TYPE JS_CORR; +}; + +#if BITS_PER_LONG == 64 +#define JS_DATA_SAVE_TYPE JS_DATA_SAVE_TYPE_64 +#elif BITS_PER_LONG == 32 +#define JS_DATA_SAVE_TYPE JS_DATA_SAVE_TYPE_32 +#else +#error Unexpected BITS_PER_LONG +#endif + #endif /* _LINUX_JOYSTICK_H */ diff --git a/include/linux/libps2.h b/include/linux/libps2.h index 923bdbc6d9e..a710bddda4e 100644 --- a/include/linux/libps2.h +++ b/include/linux/libps2.h @@ -41,6 +41,7 @@ struct ps2dev { void ps2_init(struct ps2dev *ps2dev, struct serio *serio); int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout); +void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout); int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int command); int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data); diff --git a/include/linux/sched.h b/include/linux/sched.h index 9530b190316..ff48815bd3a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -608,6 +608,8 @@ struct task_struct { struct list_head run_list; prio_array_t *array; + unsigned short ioprio; + unsigned long sleep_avg; unsigned long long timestamp, last_ran; unsigned long long sched_time; /* sched_clock time spent running */ @@ -763,6 +765,7 @@ struct task_struct { nodemask_t mems_allowed; int cpuset_mems_generation; #endif + atomic_t fs_excl; /* holding fs exclusive resources */ }; static inline pid_t process_group(struct task_struct *tsk) @@ -1112,7 +1115,8 @@ extern void unhash_process(struct task_struct *p); /* * Protects ->fs, ->files, ->mm, ->ptrace, ->group_info, ->comm, keyring - * subscriptions and synchronises with wait4(). Also used in procfs. + * subscriptions and synchronises with wait4(). Also used in procfs. Also + * pins the final release of task.io_context. * * Nests both inside and outside of read_lock(&tasklist_lock). * It must not be nested with write_lock_irq(&tasklist_lock), diff --git a/include/linux/serio.h b/include/linux/serio.h index a2d3b9ae06f..aa4d6493a03 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -83,6 +83,7 @@ static inline void serio_register_port(struct serio *serio) } void serio_unregister_port(struct serio *serio); +void serio_unregister_child_port(struct serio *serio); void __serio_unregister_port_delayed(struct serio *serio, struct module *owner); static inline void serio_unregister_port_delayed(struct serio *serio) { @@ -153,6 +154,11 @@ static inline int serio_pin_driver(struct serio *serio) return down_interruptible(&serio->drv_sem); } +static inline void serio_pin_driver_uninterruptible(struct serio *serio) +{ + down(&serio->drv_sem); +} + static inline void serio_unpin_driver(struct serio *serio) { up(&serio->drv_sem); diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 1262cb43c3a..d5c3fe1bf33 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -14,11 +14,13 @@ extern struct list_head inode_unused; * Yes, writeback.h requires sched.h * No, sched.h is not included from here. */ -static inline int current_is_pdflush(void) +static inline int task_is_pdflush(struct task_struct *task) { - return current->flags & PF_FLUSHER; + return task->flags & PF_FLUSHER; } +#define current_is_pdflush() task_is_pdflush(current) + /* * fs/fs-writeback.c */ diff --git a/kernel/exit.c b/kernel/exit.c index 3ebcd60a19c..9d1b10ed013 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -784,6 +784,8 @@ fastcall NORET_TYPE void do_exit(long code) profile_task_exit(tsk); + WARN_ON(atomic_read(&tsk->fs_excl)); + if (unlikely(in_interrupt())) panic("Aiee, killing interrupt handler!"); if (unlikely(!tsk->pid)) diff --git a/kernel/fork.c b/kernel/fork.c index 2c7806873bf..cdef6cea890 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1090,6 +1090,11 @@ static task_t *copy_process(unsigned long clone_flags, spin_unlock(¤t->sighand->siglock); } + /* + * inherit ioprio + */ + p->ioprio = current->ioprio; + SET_LINKS(p); if (unlikely(p->ptrace & PT_PTRACED)) __ptrace_link(p, current->parent); diff --git a/kernel/sched.c b/kernel/sched.c index a07cff90d84..e2b0d3e4dd0 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -3448,15 +3448,7 @@ int task_nice(const task_t *p) { return TASK_NICE(p); } - -/* - * The only users of task_nice are binfmt_elf and binfmt_elf32. - * binfmt_elf is no longer modular, but binfmt_elf32 still is. - * Therefore, task_nice is needed if there is a compat_mode. - */ -#ifdef CONFIG_COMPAT EXPORT_SYMBOL_GPL(task_nice); -#endif /** * idle_cpu - is a given cpu idle currently? diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig index 3b1fafc8f4f..7bd95ceab7c 100644 --- a/sound/oss/Kconfig +++ b/sound/oss/Kconfig @@ -52,7 +52,7 @@ config SOUND_CMPCI_MIDI config SOUND_CMPCI_JOYSTICK bool "Enable joystick" - depends on SOUND_CMPCI && X86 + depends on SOUND_CMPCI && X86 && (GAMEPORT=y || SOUND_CMPCI=GAMEPORT) help Say Y here in order to enable the joystick port on a sound card using the CMI8338 or the CMI8738 chipset. You need to config the diff --git a/sound/oss/es1370.c b/sound/oss/es1370.c index 886f61c1c34..8538085086e 100644 --- a/sound/oss/es1370.c +++ b/sound/oss/es1370.c @@ -162,6 +162,10 @@ #include <asm/page.h> #include <asm/uaccess.h> +#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#define SUPPORT_JOYSTICK +#endif + /* --------------------------------------------------------------------- */ #undef OSS_DOCUMENTED_MIXER_SEMANTICS @@ -385,7 +389,10 @@ struct es1370_state { unsigned char obuf[MIDIOUTBUF]; } midi; +#ifdef SUPPORT_JOYSTICK struct gameport *gameport; +#endif + struct semaphore sem; }; @@ -2554,10 +2561,55 @@ static struct initvol { { SOUND_MIXER_WRITE_OGAIN, 0x4040 } }; +#ifdef SUPPORT_JOYSTICK + +static int __devinit es1370_register_gameport(struct es1370_state *s) +{ + struct gameport *gp; + + if (!request_region(0x200, JOY_EXTENT, "es1370")) { + printk(KERN_ERR "es1370: joystick io port 0x200 in use\n"); + return -EBUSY; + } + + s->gameport = gp = gameport_allocate_port(); + if (!gp) { + printk(KERN_ERR "es1370: can not allocate memory for gameport\n"); + release_region(0x200, JOY_EXTENT); + return -ENOMEM; + } + + gameport_set_name(gp, "ESS1370"); + gameport_set_phys(gp, "pci%s/gameport0", pci_name(s->dev)); + gp->dev.parent = &s->dev->dev; + gp->io = 0x200; + + s->ctrl |= CTRL_JYSTK_EN; + outl(s->ctrl, s->io + ES1370_REG_CONTROL); + + gameport_register_port(gp); + + return 0; +} + +static inline void es1370_unregister_gameport(struct es1370_state *s) +{ + if (s->gameport) { + int gpio = s->gameport->io; + gameport_unregister_port(s->gameport); + release_region(gpio, JOY_EXTENT); + + } +} + +#else +static inline int es1370_register_gameport(struct es1370_state *s) { return -ENOSYS; } +static inline void es1370_unregister_gameport(struct es1370_state *s) { } +#endif /* SUPPORT_JOYSTICK */ + static int __devinit es1370_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) { struct es1370_state *s; - struct gameport *gp = NULL; mm_segment_t fs; int i, val, ret; @@ -2606,28 +2658,14 @@ static int __devinit es1370_probe(struct pci_dev *pcidev, const struct pci_devic /* note: setting CTRL_SERR_DIS is reported to break * mic bias setting (by Kim.Berts@fisub.mail.abb.com) */ s->ctrl = CTRL_CDC_EN | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV) | (1 << CTRL_SH_WTSRSEL); - if (!request_region(0x200, JOY_EXTENT, "es1370")) { - printk(KERN_ERR "es1370: joystick io port 0x200 in use\n"); - } else if (!(s->gameport = gp = gameport_allocate_port())) { - printk(KERN_ERR "es1370: can not allocate memory for gameport\n"); - release_region(0x200, JOY_EXTENT); - } else { - gameport_set_name(gp, "ESS1370"); - gameport_set_phys(gp, "pci%s/gameport0", pci_name(s->dev)); - gp->dev.parent = &s->dev->dev; - gp->io = 0x200; - s->ctrl |= CTRL_JYSTK_EN; - } if (lineout[devindex]) s->ctrl |= CTRL_XCTL0; if (micbias[devindex]) s->ctrl |= CTRL_XCTL1; s->sctrl = 0; - printk(KERN_INFO "es1370: found adapter at io %#lx irq %u\n" - KERN_INFO "es1370: features: joystick %s, line %s, mic impedance %s\n", - s->io, s->irq, (s->ctrl & CTRL_JYSTK_EN) ? "on" : "off", - (s->ctrl & CTRL_XCTL0) ? "out" : "in", - (s->ctrl & CTRL_XCTL1) ? "1" : "0"); + printk(KERN_INFO "es1370: adapter at io %#lx irq %u, line %s, mic impedance %s\n", + s->io, s->irq, (s->ctrl & CTRL_XCTL0) ? "out" : "in", + (s->ctrl & CTRL_XCTL1) ? "1" : "0"); /* register devices */ if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops, -1)) < 0) { ret = s->dev_audio; @@ -2673,9 +2711,7 @@ static int __devinit es1370_probe(struct pci_dev *pcidev, const struct pci_devic } set_fs(fs); - /* register gameport */ - if (gp) - gameport_register_port(gp); + es1370_register_gameport(s); /* store it in the driver field */ pci_set_drvdata(pcidev, s); @@ -2697,10 +2733,6 @@ static int __devinit es1370_probe(struct pci_dev *pcidev, const struct pci_devic err_dev1: printk(KERN_ERR "es1370: cannot register misc device\n"); free_irq(s->irq, s); - if (s->gameport) { - release_region(s->gameport->io, JOY_EXTENT); - gameport_free_port(s->gameport); - } err_irq: release_region(s->io, ES1370_EXTENT); err_region: @@ -2719,11 +2751,7 @@ static void __devexit es1370_remove(struct pci_dev *dev) outl(0, s->io+ES1370_REG_SERIAL_CONTROL); /* clear serial interrupts */ synchronize_irq(s->irq); free_irq(s->irq, s); - if (s->gameport) { - int gpio = s->gameport->io; - gameport_unregister_port(s->gameport); - release_region(gpio, JOY_EXTENT); - } + es1370_unregister_gameport(s); release_region(s->io, ES1370_EXTENT); unregister_sound_dsp(s->dev_audio); unregister_sound_mixer(s->dev_mixer); diff --git a/sound/oss/es1371.c b/sound/oss/es1371.c index 9266b777387..12a56d5ab49 100644 --- a/sound/oss/es1371.c +++ b/sound/oss/es1371.c @@ -134,6 +134,10 @@ #include <asm/page.h> #include <asm/uaccess.h> +#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#define SUPPORT_JOYSTICK +#endif + /* --------------------------------------------------------------------- */ #undef OSS_DOCUMENTED_MIXER_SEMANTICS @@ -454,7 +458,10 @@ struct es1371_state { unsigned char obuf[MIDIOUTBUF]; } midi; +#ifdef SUPPORT_JOYSTICK struct gameport *gameport; +#endif + struct semaphore sem; }; @@ -2787,12 +2794,63 @@ static struct { PCI_ANY_ID, PCI_ANY_ID } }; +#ifdef SUPPORT_JOYSTICK + +static int __devinit es1371_register_gameport(struct es1371_state *s) +{ + struct gameport *gp; + int gpio; + + for (gpio = 0x218; gpio >= 0x200; gpio -= 0x08) + if (request_region(gpio, JOY_EXTENT, "es1371")) + break; + + if (gpio < 0x200) { + printk(KERN_ERR PFX "no free joystick address found\n"); + return -EBUSY; + } + + s->gameport = gp = gameport_allocate_port(); + if (!gp) { + printk(KERN_ERR PFX "can not allocate memory for gameport\n"); + release_region(gpio, JOY_EXTENT); + return -ENOMEM; + } + + gameport_set_name(gp, "ESS1371 Gameport"); + gameport_set_phys(gp, "isa%04x/gameport0", gpio); + gp->dev.parent = &s->dev->dev; + gp->io = gpio; + + s->ctrl |= CTRL_JYSTK_EN | (((gpio >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT); + outl(s->ctrl, s->io + ES1371_REG_CONTROL); + + gameport_register_port(gp); + + return 0; +} + +static inline void es1371_unregister_gameport(struct es1371_state *s) +{ + if (s->gameport) { + int gpio = s->gameport->io; + gameport_unregister_port(s->gameport); + release_region(gpio, JOY_EXTENT); + + } +} + +#else +static inline int es1371_register_gameport(struct es1371_state *s) { return -ENOSYS; } +static inline void es1371_unregister_gameport(struct es1371_state *s) { } +#endif /* SUPPORT_JOYSTICK */ + + static int __devinit es1371_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) { struct es1371_state *s; - struct gameport *gp; mm_segment_t fs; - int i, gpio, val, res = -1; + int i, val, res = -1; int idx; unsigned long tmo; signed long tmo2; @@ -2883,23 +2941,6 @@ static int __devinit es1371_probe(struct pci_dev *pcidev, const struct pci_devic } } - for (gpio = 0x218; gpio >= 0x200; gpio -= 0x08) - if (request_region(gpio, JOY_EXTENT, "es1371")) - break; - - if (gpio < 0x200) { - printk(KERN_ERR PFX "no free joystick address found\n"); - } else if (!(s->gameport = gp = gameport_allocate_port())) { - printk(KERN_ERR PFX "can not allocate memory for gameport\n"); - release_region(gpio, JOY_EXTENT); - } else { - gameport_set_name(gp, "ESS1371 Gameport"); - gameport_set_phys(gp, "isa%04x/gameport0", gpio); - gp->dev.parent = &s->dev->dev; - gp->io = gpio; - s->ctrl |= CTRL_JYSTK_EN | (((gpio >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT); - } - s->sctrl = 0; cssr = 0; s->spdif_volume = -1; @@ -2969,9 +3010,7 @@ static int __devinit es1371_probe(struct pci_dev *pcidev, const struct pci_devic /* turn on S/PDIF output driver if requested */ outl(cssr, s->io+ES1371_REG_STATUS); - /* register gameport */ - if (s->gameport) - gameport_register_port(s->gameport); + es1371_register_gameport(s); /* store it in the driver field */ pci_set_drvdata(pcidev, s); @@ -2980,13 +3019,9 @@ static int __devinit es1371_probe(struct pci_dev *pcidev, const struct pci_devic /* increment devindex */ if (devindex < NR_DEVICE-1) devindex++; - return 0; + return 0; err_gp: - if (s->gameport) { - release_region(s->gameport->io, JOY_EXTENT); - gameport_free_port(s->gameport); - } #ifdef ES1371_DEBUG if (s->ps) remove_proc_entry("es1371", NULL); @@ -3025,11 +3060,7 @@ static void __devexit es1371_remove(struct pci_dev *dev) outl(0, s->io+ES1371_REG_SERIAL_CONTROL); /* clear serial interrupts */ synchronize_irq(s->irq); free_irq(s->irq, s); - if (s->gameport) { - int gpio = s->gameport->io; - gameport_unregister_port(s->gameport); - release_region(gpio, JOY_EXTENT); - } + es1371_unregister_gameport(s); release_region(s->io, ES1371_EXTENT); unregister_sound_dsp(s->dev_audio); unregister_sound_mixer(s->codec->dev_mixer); diff --git a/sound/oss/esssolo1.c b/sound/oss/esssolo1.c index fb09065d07c..a4ecab2f052 100644 --- a/sound/oss/esssolo1.c +++ b/sound/oss/esssolo1.c @@ -150,6 +150,10 @@ #define FMODE_DMFM 0x10 +#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#define SUPPORT_JOYSTICK 1 +#endif + static struct pci_driver solo1_driver; /* --------------------------------------------------------------------- */ @@ -227,7 +231,9 @@ struct solo1_state { unsigned char obuf[MIDIOUTBUF]; } midi; +#if SUPPORT_JOYSTICK struct gameport *gameport; +#endif }; /* --------------------------------------------------------------------- */ @@ -2281,6 +2287,7 @@ solo1_resume(struct pci_dev *pci_dev) { return 0; } +#ifdef SUPPORT_JOYSTICK static int __devinit solo1_register_gameport(struct solo1_state *s, int io_port) { struct gameport *gp; @@ -2307,6 +2314,19 @@ static int __devinit solo1_register_gameport(struct solo1_state *s, int io_port) return 0; } +static inline void solo1_unregister_gameport(struct solo1_state *s) +{ + if (s->gameport) { + int gpio = s->gameport->io; + gameport_unregister_port(s->gameport); + release_region(gpio, GAMEPORT_EXTENT); + } +} +#else +static inline int solo1_register_gameport(struct solo1_state *s, int io_port) { return -ENOSYS; } +static inline void solo1_unregister_gameport(struct solo1_state *s) { } +#endif /* SUPPORT_JOYSTICK */ + static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) { struct solo1_state *s; @@ -2438,11 +2458,7 @@ static void __devexit solo1_remove(struct pci_dev *dev) synchronize_irq(s->irq); pci_write_config_word(s->dev, 0x60, 0); /* turn off DDMA controller address space */ free_irq(s->irq, s); - if (s->gameport) { - int gpio = s->gameport->io; - gameport_unregister_port(s->gameport); - release_region(gpio, GAMEPORT_EXTENT); - } + solo1_unregister_gameport(s); release_region(s->iobase, IOBASE_EXTENT); release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); release_region(s->ddmabase, DDMABASE_EXTENT); diff --git a/sound/oss/mad16.c b/sound/oss/mad16.c index a7067f16991..aa3c50db66c 100644 --- a/sound/oss/mad16.c +++ b/sound/oss/mad16.c @@ -50,9 +50,12 @@ #include "sb.h" #include "mpu401.h" +#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#define SUPPORT_JOYSTICK 1 +#endif + static int mad16_conf; static int mad16_cdsel; -static struct gameport *gameport; static DEFINE_SPINLOCK(lock); #define C928 1 @@ -902,6 +905,10 @@ static int __initdata irq_map[16] = -1, -1, -1, -1 }; +#ifdef SUPPORT_JOYSTICK + +static struct gameport *gameport; + static int __devinit mad16_register_gameport(int io_port) { if (!request_region(io_port, 1, "mad16 gameport")) { @@ -925,6 +932,20 @@ static int __devinit mad16_register_gameport(int io_port) return 0; } +static inline void mad16_unregister_gameport(void) +{ + if (gameport) { + /* the gameport was initialized so we must free it up */ + gameport_unregister_port(gameport); + gameport = NULL; + release_region(0x201, 1); + } +} +#else +static inline int mad16_register_gameport(int io_port) { return -ENOSYS; } +static inline void mad16_unregister_gameport(void) { } +#endif + static int __devinit init_mad16(void) { int dmatype = 0; @@ -1060,12 +1081,7 @@ static void __exit cleanup_mad16(void) { if (found_mpu) unload_mad16_mpu(&cfg_mpu); - if (gameport) { - /* the gameport was initialized so we must free it up */ - gameport_unregister_port(gameport); - gameport = NULL; - release_region(0x201, 1); - } + mad16_unregister_gameport(); unload_mad16(&cfg); release_region(MC0_PORT, 12); } diff --git a/sound/oss/sonicvibes.c b/sound/oss/sonicvibes.c index 06047e7979a..17d0e461f8d 100644 --- a/sound/oss/sonicvibes.c +++ b/sound/oss/sonicvibes.c @@ -122,6 +122,9 @@ #include "dm.h" +#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#define SUPPORT_JOYSTICK 1 +#endif /* --------------------------------------------------------------------- */ @@ -365,7 +368,9 @@ struct sv_state { unsigned char obuf[MIDIOUTBUF]; } midi; +#if SUPPORT_JOYSTICK struct gameport *gameport; +#endif }; /* --------------------------------------------------------------------- */ @@ -2485,6 +2490,7 @@ static struct initvol { #define RSRCISIOREGION(dev,num) (pci_resource_start((dev), (num)) != 0 && \ (pci_resource_flags((dev), (num)) & IORESOURCE_IO)) +#ifdef SUPPORT_JOYSTICK static int __devinit sv_register_gameport(struct sv_state *s, int io_port) { struct gameport *gp; @@ -2511,6 +2517,19 @@ static int __devinit sv_register_gameport(struct sv_state *s, int io_port) return 0; } +static inline void sv_unregister_gameport(struct sv_state *s) +{ + if (s->gameport) { + int gpio = s->gameport->io; + gameport_unregister_port(s->gameport); + release_region(gpio, SV_EXTENT_GAME); + } +} +#else +static inline int sv_register_gameport(struct sv_state *s, int io_port) { return -ENOSYS; } +static inline void sv_unregister_gameport(struct sv_state *s) { } +#endif /* SUPPORT_JOYSTICK */ + static int __devinit sv_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) { static char __devinitdata sv_ddma_name[] = "S3 Inc. SonicVibes DDMA Controller"; @@ -2711,11 +2730,7 @@ static void __devexit sv_remove(struct pci_dev *dev) /*outb(0, s->iodmaa + SV_DMA_RESET);*/ /*outb(0, s->iodmac + SV_DMA_RESET);*/ free_irq(s->irq, s); - if (s->gameport) { - int gpio = s->gameport->io; - gameport_unregister_port(s->gameport); - release_region(gpio, SV_EXTENT_GAME); - } + sv_unregister_gameport(s); release_region(s->iodmac, SV_EXTENT_DMA); release_region(s->iodmaa, SV_EXTENT_DMA); release_region(s->ioenh, SV_EXTENT_ENH); diff --git a/sound/oss/trident.c b/sound/oss/trident.c index 47537f0a5b0..5f0ad6bb43b 100644 --- a/sound/oss/trident.c +++ b/sound/oss/trident.c @@ -228,6 +228,10 @@ #define DRIVER_VERSION "0.14.10j-2.6" +#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#define SUPPORT_JOYSTICK 1 +#endif + /* magic numbers to protect our data structures */ #define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */ #define TRIDENT_STATE_MAGIC 0x63657373 /* "cess" */ @@ -4252,24 +4256,25 @@ trident_ac97_init(struct trident_card *card) return num_ac97 + 1; } +#ifdef SUPPORT_JOYSTICK /* Gameport functions for the cards ADC gameport */ -static unsigned char -trident_game_read(struct gameport *gameport) +static unsigned char trident_game_read(struct gameport *gameport) { struct trident_card *card = gameport->port_data; + return inb(TRID_REG(card, T4D_GAME_LEG)); } -static void -trident_game_trigger(struct gameport *gameport) +static void trident_game_trigger(struct gameport *gameport) { struct trident_card *card = gameport->port_data; + outb(0xff, TRID_REG(card, T4D_GAME_LEG)); } -static int -trident_game_cooked_read(struct gameport *gameport, int *axes, int *buttons) +static int trident_game_cooked_read(struct gameport *gameport, + int *axes, int *buttons) { struct trident_card *card = gameport->port_data; int i; @@ -4285,8 +4290,7 @@ trident_game_cooked_read(struct gameport *gameport, int *axes, int *buttons) return 0; } -static int -trident_game_open(struct gameport *gameport, int mode) +static int trident_game_open(struct gameport *gameport, int mode) { struct trident_card *card = gameport->port_data; @@ -4305,8 +4309,7 @@ trident_game_open(struct gameport *gameport, int mode) return 0; } -static int __devinit -trident_register_gameport(struct trident_card *card) +static int __devinit trident_register_gameport(struct trident_card *card) { struct gameport *gp; @@ -4330,6 +4333,17 @@ trident_register_gameport(struct trident_card *card) return 0; } +static inline void trident_unregister_gameport(struct trident_card *card) +{ + if (card->gameport) + gameport_unregister_port(card->gameport); +} + +#else +static inline int trident_register_gameport(struct trident_card *card) { return -ENOSYS; } +static inline void trident_unregister_gameport(struct trident_card *card) { } +#endif /* SUPPORT_JOYSTICK */ + /* install the driver, we do not allocate hardware channel nor DMA buffer */ /* now, they are defered until "ACCESS" time (in prog_dmabuf called by */ /* open/read/write/ioctl/mmap) */ @@ -4569,8 +4583,7 @@ trident_remove(struct pci_dev *pci_dev) } /* Unregister gameport */ - if (card->gameport) - gameport_unregister_port(card->gameport); + trident_unregister_gameport(card); /* Kill interrupts, and SP/DIF */ trident_disable_loop_interrupts(card); diff --git a/sound/oss/via82cxxx_audio.c b/sound/oss/via82cxxx_audio.c index b387e1e5248..83edda93f0b 100644 --- a/sound/oss/via82cxxx_audio.c +++ b/sound/oss/via82cxxx_audio.c @@ -35,6 +35,7 @@ #include <linux/smp_lock.h> #include <linux/ioport.h> #include <linux/delay.h> +#include <linux/dma-mapping.h> #include <asm/io.h> #include <asm/uaccess.h> #include <asm/semaphore.h> @@ -3391,10 +3392,10 @@ static int __devinit via_init_one (struct pci_dev *pdev, const struct pci_device if (rc) goto err_out_disable; - rc = pci_set_dma_mask(pdev, 0xffffffffULL); + rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (rc) goto err_out_res; - rc = pci_set_consistent_dma_mask(pdev, 0xffffffffULL); + rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); if (rc) goto err_out_res; diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index b6e1854e938..eb3c52b03af 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1338,11 +1338,6 @@ static inline int snd_cs4281_create_gameport(cs4281_t *chip) { return -ENOSYS; } static inline void snd_cs4281_free_gameport(cs4281_t *chip) { } #endif /* CONFIG_GAMEPORT || (MODULE && CONFIG_GAMEPORT_MODULE) */ - -/* - - */ - static int snd_cs4281_free(cs4281_t *chip) { snd_cs4281_free_gameport(chip); |