diff options
Diffstat (limited to 'drivers/char/joystick.c')
-rw-r--r-- | drivers/char/joystick.c | 298 |
1 files changed, 161 insertions, 137 deletions
diff --git a/drivers/char/joystick.c b/drivers/char/joystick.c index 4994dabb4..25a7c2b47 100644 --- a/drivers/char/joystick.c +++ b/drivers/char/joystick.c @@ -1,13 +1,12 @@ /* - * $Id: joystick.c,v 1.3 1997/12/16 05:35:12 ralf Exp $ - * - * Copyright (C) 1997 Vojtech Pavlik + * linux/drivers/char/joystick.c Version 1.0.9 + * Copyright (C) 1996-1998 Vojtech Pavlik */ /* - * This is joystick driver for Linux. It supports up to two analog joysticks - * on a PC compatible machine. See Documentation/joystick.txt for changelog - * and credits. + * This is joystick driver for Linux. It supports up to two analog joysticks + * on a PC compatible machine. See Documentation/joystick.txt for changelog + * and credits. */ #include <linux/init.h> @@ -15,30 +14,25 @@ #include <linux/ioport.h> #include <linux/errno.h> #include <linux/mm.h> -#include <linux/ptrace.h> #include <linux/interrupt.h> #include <linux/malloc.h> #include <linux/poll.h> #include <linux/major.h> #include <linux/joystick.h> - #include <asm/io.h> -#include <asm/ptrace.h> -#include <asm/uaccess.h> -#include <asm/param.h> #define PIT_HZ 1193180L /* PIT clock is 1.19318 MHz */ #define JS_MAXTIME PIT_HZ/250 /* timeout for read (4 ms) */ -#define JS_BUTTON_PERIOD HZ/50 /* button valid time (20 ms) */ -#define JS_AXIS_MIN_PERIOD HZ/25 /* axis min valid time (40 ms) */ -#define JS_AXIS_MAX_PERIOD HZ/25*2 /* axis max valid time (80 ms) */ +#define JS_TIMER_PERIOD HZ/50 /* button valid time (20 ms) */ +#define JS_BH_MIN_PERIOD HZ/25 /* axis min valid time (40 ms) */ +#define JS_BH_MAX_PERIOD HZ/25*2 /* axis max valid time (80 ms) */ -#define JS_FIFO_SIZE 16 /* number of FIFO entries */ +#define JS_FIFO_SIZE 16 /* number of FIFO entries */ #define JS_BUFF_SIZE 32 /* output buffer size */ -#define JS_RETRIES 4 /* number of retries */ -#define JS_DEF_PREC 8 /* initial precision for all axes */ +#define JS_RETRIES 4 /* number of retries */ +#define JS_DEF_PREC 16 /* initial precision for all axes */ #define JS_NUM 2 /* number of joysticks */ @@ -49,7 +43,7 @@ #define PIT_DATA 0x40 /* timer 0 data port */ #define JS_PORT 0x201 /* joystick port */ -#define JS_TRIGGER 0xff /* triggers one-shots */ +#define JS_TRIGGER 0xff /* triggers one-shots */ #define PIT_READ_TIMER 0x00 /* to read timer 0 */ #define DELTA(X,Y,Z) ((X)-(Y)+(((X)>=(Y))?0:Z)) /* cyclic delta */ @@ -95,7 +89,7 @@ static unsigned char js_fifo_tail = JS_FIFO_SIZE - 1; /* tail of the fifo */ static struct js_fifo js_fifo[JS_FIFO_SIZE]; /* the fifo */ static unsigned char js_last_buttons = 0; /* last read button state */ -static unsigned long js_axis_time = 0; /* last read axis time */ +static unsigned long js_bh_time = 0; /* last read axis time */ static unsigned long js_mark_time = 0; static unsigned char js_axes_exist; /* all axes that exist */ @@ -106,7 +100,7 @@ static unsigned int js_buttons = 0; MODULE_AUTHOR("Vojtech Pavlik <vojtech@atrey.karlin.mff.cuni.cz>"); MODULE_SUPPORTED_DEVICE("js"); -MODULE_PARM(js, "0-1b"); +MODULE_PARM(js, "1-2b"); static char js[] = {0, 0}; @@ -118,7 +112,7 @@ static char js[] = {0, 0}; static inline int get_pit(void) { int t, flags; - + save_flags(flags); cli(); outb(PIT_READ_TIMER, PIT_MODE); @@ -132,12 +126,14 @@ static inline int get_pit(void) * count_bits() counts set bits in a byte. */ -static int count_bits(unsigned char c) +static int count_bits(unsigned int c) { - int i, t = 0; - for (i = 0; i < 8; i++) - if (c & (1 << i)) t++; - return t; + int i = 0; + while (c) { + i += c & 1; + c >>= 1; + } + return i; } /* @@ -149,36 +145,34 @@ static int js_correct(int value, struct js_corr *corr) int t; if (corr->type == JS_CORR_NONE) return value; - t = value > corr->coef[0] ? (value < corr->coef[1] ? corr->coef[0] : value - corr->coef[1] + corr->coef[0]) : value; - if (t == corr->coef[0]) return 32768; + t = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : value - corr->coef[1]) : value - corr->coef[0]; switch (corr->type) { case JS_CORR_BROKEN: - t = t < corr->coef[0] ? ((corr->coef[2] * t) >> 14) + corr->coef[3] : - ((corr->coef[4] * t) >> 14) + corr->coef[5]; + t = t < 0 ? ((corr->coef[2] * t) >> 14) : ((corr->coef[3] * t) >> 14); break; - default: + default: return 0; } - if (t < 0) return 0; - if (t > 65535) return 65535; + if (t < -32767) return -32767; + if (t > 32767) return 32767; - return t; + return t; } /* - * js_compare() compares two close axis values and decides + * js_compare() compares two close axis values and decides * whether they are "same". */ static int js_compare(int x, int y, int prec) { - return (x < y + prec) && (y < x + prec); + return (x < y + prec) && (y < x + prec); } /* - * js_probe() probes for joysticks + * js_probe() probes for joysticks */ inline int js_probe(void) @@ -196,10 +190,10 @@ inline int js_probe(void) } else switch (t & JS_AXES) { case 0x0c: jsd[0].exist = 0x33; jsd[1].exist = 0x00; break; /* joystick 0 connected */ - case 0x03: jsd[0].exist = 0x00; jsd[1].exist = 0xcc; break; /* joystick 1 connected */ + case 0x03: jsd[0].exist = 0xcc; jsd[1].exist = 0x00; break; /* joystick 1 connected */ case 0x04: jsd[0].exist = 0xfb; jsd[1].exist = 0x00; break; /* 3-axis joystick connected */ case 0x00: jsd[0].exist = 0x33; jsd[1].exist = 0xcc; break; /* joysticks 0 and 1 connected */ - default: jsd[0].exist = 0x00; jsd[1].exist = 0x00; return -1; /* no joysticks */ + default: jsd[0].exist = 0x00; jsd[1].exist = 0x00; return -1; /* no joysticks */ } js_axes_exist = (jsd[0].exist | jsd[1].exist) & JS_AXES; @@ -208,7 +202,7 @@ inline int js_probe(void) return 0; } -/* +/* * js_do_timer() controls the action by adding entries to the event * fifo each time a button changes its state or axis valid time * expires. @@ -226,16 +220,61 @@ static void js_do_timer(unsigned long data) js_mark_time = jiffies; mark_bh(JS_BH); } - } + } else - if ((jiffies > js_axis_time + JS_AXIS_MAX_PERIOD) && !js_mark_time) { + if ((jiffies > js_bh_time + JS_BH_MAX_PERIOD) && !js_mark_time) { js_mark_time = jiffies; mark_bh(JS_BH); } - js_timer.expires = jiffies + JS_BUTTON_PERIOD; + js_timer.expires = jiffies + JS_TIMER_PERIOD; add_timer(&js_timer); } + +/* + * Put an event in the buffer. This requires additional queue processing + * done by js_sync_buff, otherwise the buffer will be corrupted. + */ + +static void js_add_event(int i, __u32 time, __u8 type, __u8 number, __u16 value) +{ + int ahead = jsd[i].ahead++; + jsd[i].buff[ahead].time = time; + jsd[i].buff[ahead].type = type; + jsd[i].buff[ahead].number = number; + jsd[i].buff[ahead].value = value; + if (jsd[i].ahead == JS_BUFF_SIZE) jsd[i].ahead=0; +} + +/* + * This checks for all owerflows caused by recent additions to the buffer. + * It does anything only if some processes are reading the data too slowly. + */ + +static void js_sync_buff(void) +{ + int i; + + for (i = 0; i < JS_NUM; i++) + if (jsd[i].list) + if (jsd[i].bhead != jsd[i].ahead) { + if (ROT(jsd[i].bhead, jsd[i].tail, jsd[i].ahead) || (jsd[i].tail == jsd[i].bhead)) { + struct js_list *curl; + curl = jsd[i].list; + while (curl) { + if (ROT(jsd[i].bhead, curl->tail, jsd[i].ahead) || (curl->tail == jsd[i].bhead)) { + curl->tail = jsd[i].ahead; + curl->startup = jsd[i].exist; + } + curl = curl->next; + } + jsd[i].tail = jsd[i].ahead; + } + jsd[i].bhead = jsd[i].ahead; + wake_up_interruptible(&jsd[i].wait); + } +} + /* * js_do_bh() does the main processing and adds events to output buffers. */ @@ -246,7 +285,7 @@ static void js_do_bh(void) int i, j, k; unsigned int t; - if (jiffies > js_axis_time + JS_AXIS_MIN_PERIOD) { + if (jiffies > js_bh_time + JS_BH_MIN_PERIOD) { unsigned int old_axis[4]; unsigned int t_low, t_high; @@ -274,8 +313,8 @@ static void js_do_bh(void) cli(); /* no interrupts */ outb(JS_TRIGGER, JS_PORT); /* trigger one-shots */ outb(PIT_READ_TIMER, PIT_MODE); /* read timer */ - t = (t1l = inb(PIT_DATA)) | - (t1h = inb(PIT_DATA)) << 8; + t = (t1l = inb(PIT_DATA)) | + (t1h = inb(PIT_DATA)) << 8; restore_flags(flags); do { @@ -286,7 +325,7 @@ static void js_do_bh(void) joy_state = (joy_state << 8) | jss; i++; } - + cli(); outb(PIT_READ_TIMER, PIT_MODE); t1l = inb(PIT_DATA); @@ -299,15 +338,15 @@ static void js_do_bh(void) * Process the gathered axis data in joy_state. */ - joy_state ^= ((joy_state >> 8) | 0xff000000L); /* More magic */ + joy_state ^= ((joy_state >> 8) | 0xff000000L); /* More magic */ for (; i > 0; i--) { for (j = 0; j < 4; j++) if (joy_state & js_axes_exist & (1 << j)) { - jsm = js_correct(DELTA_TX(t, t_low, t_high), &js_axis[j].corr); + jsm = DELTA_TX(t, t_low, t_high); if (!js_compare(jsm, js_axis[j].value, js_axis[j].corr.prec)) { if (jsm < js_axis[j].value || !retries) - js_axis[j].value = jsm; + js_axis[j].value = jsm; again = 1; } } @@ -316,7 +355,7 @@ static void js_do_bh(void) t_high = t_high >> 8; } - } while (retries++ < JS_RETRIES && again); + } while (retries++ < JS_RETRIES && again); /* * Check if joystick lost. @@ -348,18 +387,13 @@ static void js_do_bh(void) k = 0; for (j = 0; j < 4; j++) if ((1 << j) & jsd[i].exist) { - if (!js_compare(js_axis[j].value, old_axis[j], js_axis[j].corr.prec)) { - jsd[i].buff[jsd[i].ahead].time = js_mark_time; - jsd[i].buff[jsd[i].ahead].type = JS_EVENT_AXIS; - jsd[i].buff[jsd[i].ahead].number = k; - jsd[i].buff[jsd[i].ahead].value = js_axis[j].value; - jsd[i].ahead++; - if (jsd[i].ahead == JS_BUFF_SIZE) jsd[i].ahead = 0; - } + if ((t = js_correct(js_axis[j].value, &js_axis[j].corr)) != + js_correct(old_axis[j], &js_axis[j].corr)) + js_add_event(i, js_mark_time, JS_EVENT_AXIS, k, t); k++; } } - js_axis_time = jiffies; + js_bh_time = jiffies; } js_mark_time = 0; @@ -373,14 +407,8 @@ static void js_do_bh(void) k = 0; for (j = 4; j < 8; j++) if ((1 << j) & jsd[i].exist) { - if ((1 << j) & (js_buttons ^ js_fifo[t].event)) { - jsd[i].buff[jsd[i].ahead].time = js_fifo[t].time; - jsd[i].buff[jsd[i].ahead].type = JS_EVENT_BUTTON; - jsd[i].buff[jsd[i].ahead].number = k; - jsd[i].buff[jsd[i].ahead].value = (js_fifo[t].event >> j) & 1; - jsd[i].ahead++; - if (jsd[i].ahead == JS_BUFF_SIZE) jsd[i].ahead = 0; - } + if ((1 << j) & (js_buttons ^ js_fifo[t].event)) + js_add_event(i, js_fifo[t].time, JS_EVENT_BUTTON, k, (js_fifo[t].event >> j) & 1); k++; } } @@ -388,27 +416,10 @@ static void js_do_bh(void) } /* - * Sync ahead with bhead and cut too long tails. + * Synchronize the buffer. */ - - for (i = 0; i < JS_NUM; i++) - if (jsd[i].list) - if (jsd[i].bhead != jsd[i].ahead) { - if (ROT(jsd[i].bhead, jsd[i].tail, jsd[i].ahead) || (jsd[i].tail == jsd[i].bhead)) { - struct js_list *curl; - curl = jsd[i].list; - while (curl) { - if (ROT(jsd[i].bhead, curl->tail, jsd[i].ahead) || (curl->tail == jsd[i].bhead)) { - curl->tail = jsd[i].ahead; - curl->startup = jsd[i].exist; - } - curl = curl->next; - } - jsd[i].tail = jsd[i].ahead; - } - jsd[i].bhead = jsd[i].ahead; - wake_up_interruptible(&jsd[i].wait); - } + + js_sync_buff(); } @@ -435,7 +446,7 @@ static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) unsigned long blocks = count / sizeof(struct js_event); unsigned long i = 0, j; int t, u = curl->tail; - int retval = 0; + int retval = 0; /* * Check user data. @@ -459,38 +470,46 @@ static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) * Handle (non)blocking i/o. */ - if (count != sizeof(struct JS_DATA_TYPE)) { - if ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup) || (curl->startup && !js_axis_time)) { - add_wait_queue(&jsd[minor].wait, &wait); - current->state = TASK_INTERRUPTIBLE; - while ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup) || (curl->startup && !js_axis_time)) { - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - if (!jsd[minor].exist) { - retval = -ENODEV; - break; - } + if ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup && count != sizeof(struct JS_DATA_TYPE)) + || (curl->startup && !js_bh_time)) { + + add_wait_queue(&jsd[minor].wait, &wait); + current->state = TASK_INTERRUPTIBLE; + + while ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup && count != sizeof(struct JS_DATA_TYPE)) + || (curl->startup && !js_bh_time)) { + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + if (!jsd[minor].exist) { + retval = -ENODEV; + break; } - current->state = TASK_RUNNING; - remove_wait_queue(&jsd[minor].wait, &wait); } + current->state = TASK_RUNNING; + remove_wait_queue(&jsd[minor].wait, &wait); + } + + if (retval) return retval; - if (retval) return retval; - /* * Do the i/o. */ + if (count != sizeof(struct JS_DATA_TYPE)) { if (curl->startup) { struct js_event tmpevent; +/* + * Initial button state. + */ t = 0; for (j = 0; j < 4 && (i < blocks) && !retval; j++) @@ -498,17 +517,21 @@ static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) if (curl->startup & (1 << j)) { tmpevent.type = JS_EVENT_AXIS | JS_EVENT_INIT; tmpevent.number = t; - tmpevent.value = js_axis[j].value; - if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event))) + tmpevent.value = js_correct(js_axis[j].value, &js_axis[j].corr); + if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event))) retval = -EFAULT; if (put_user((__u32)((jiffies - curl->time) * (1000/HZ)), &buff[i].time)) retval = -EFAULT; curl->startup &= ~(1 << j); i++; } - t++; + t++; } +/* + * Initial axis state. + */ + t = 0; for (j = 4; j < 8 && (i < blocks) && !retval; j++) if (jsd[minor].exist & (1 << j)) { @@ -516,17 +539,20 @@ static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) tmpevent.type = JS_EVENT_BUTTON | JS_EVENT_INIT; tmpevent.number = t; tmpevent.value = (js_buttons >> j) & 1; - if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event))) + if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event))) retval = -EFAULT; if (put_user((__u32)((jiffies - curl->time) * (1000/HZ)), &buff[i].time)) retval = -EFAULT; curl->startup &= ~(1 << j); i++; } - t++; + t++; } } +/* + * Buffer data. + */ while ((jsd[minor].ahead != (t = GOF(curl->tail))) && (i < blocks) && !retval) { if (copy_to_user(&buff[i], &jsd[minor].buff[t], sizeof(struct js_event))) @@ -536,7 +562,7 @@ static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) curl->tail = t; i++; } - + } else @@ -562,6 +588,7 @@ static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) buttons |= (!!(js_last_buttons & (1 << j))) << (i++); copy_to_user(&bufo->buttons, &buttons, sizeof(int)); + curl->startup = 0; curl->tail = GOB(jsd[minor].ahead); retval = sizeof(struct JS_DATA_TYPE); } @@ -580,7 +607,7 @@ static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) } if (!curl) jsd[minor].tail = t; } - + return retval ? retval : i*sizeof(struct js_event); } @@ -595,19 +622,16 @@ static unsigned int js_poll(struct file *file, poll_table *wait) curl = file->private_data; poll_wait(file, &jsd[minor].wait, wait); - if (GOF(curl->tail) != jsd[minor].ahead) + if (GOF(curl->tail) != jsd[minor].ahead) return POLLIN | POLLRDNORM; - return 0; + return 0; } /* * js_ioctl handles misc ioctl calls. */ -static int js_ioctl(struct inode *inode, - struct file *file, - unsigned int cmd, - unsigned long arg) +static int js_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { unsigned int minor = MINOR(inode->i_rdev); int i, j; @@ -637,13 +661,13 @@ static int js_ioctl(struct inode *inode, sizeof(struct js_corr))) return -EFAULT; j++; } - js_axis_time = 0; + js_bh_time = 0; break; case JSIOCGCORR: j = 0; for (i = 0; i < 4; i++) if ((1 << i) & jsd[minor].exist) { - if (copy_to_user((void *) arg + j * sizeof(struct js_corr), &js_axis[i].corr, + if (copy_to_user((void *) arg + j * sizeof(struct js_corr), &js_axis[i].corr, sizeof(struct js_corr))) return -EFAULT; j++; } @@ -651,7 +675,7 @@ static int js_ioctl(struct inode *inode, default: return -EINVAL; } - + return 0; } @@ -672,15 +696,15 @@ static int js_open(struct inode *inode, struct file *file) return -ENODEV; if (!jsd[minor].exist) { js_probe(); - if (jsd[minor].exist) printk(KERN_INFO "js%d: %d-axis joystick at %#x\n", - minor, count_bits(jsd[minor].exist & JS_AXES), JS_PORT); + if (jsd[minor].exist) printk(KERN_INFO "js%d: %d-axis %d-button joystick at %#x\n", + minor, count_bits(jsd[minor].exist & JS_AXES), count_bits(jsd[minor].exist & JS_BUTTONS), JS_PORT); else return -ENODEV; } MOD_INC_USE_COUNT; - if (!jsd[0].list && !jsd[1].list) { - js_timer.expires = jiffies + JS_BUTTON_PERIOD; + if (!jsd[0].list && !jsd[1].list) { + js_timer.expires = jiffies + JS_TIMER_PERIOD; add_timer(&js_timer); } @@ -760,7 +784,7 @@ __initfunc(void js_setup(char *str, int *ints)) { js[0] = ((ints[0] > 0) ? ints[1] : 0 ); - js[1] = ((ints[0] > 1) ? ints[2] : 0 ); + js[1] = ((ints[0] > 1) ? ints[2] : 0 ); } #endif @@ -770,7 +794,7 @@ __initfunc(void js_setup(char *str, int *ints)) */ #ifdef MODULE -int init_module(void) +int init_module(void) #else __initfunc(int js_init(void)) #endif @@ -793,8 +817,8 @@ __initfunc(int js_init(void)) } for (i = 0; i < JS_NUM; i++) { - if (jsd[i].exist) printk(KERN_INFO "js%d: %d-axis joystick at %#x\n", - i, count_bits(jsd[i].exist & JS_AXES), JS_PORT); + if (jsd[i].exist) printk(KERN_INFO "js%d: %d-axis %d-button joystick at %#x\n", + i, count_bits(jsd[i].exist & JS_AXES), count_bits(jsd[i].exist & JS_BUTTONS), JS_PORT); jsd[i].ahead = jsd[i].bhead = 0; jsd[i].tail = JS_BUFF_SIZE - 1; jsd[i].list = NULL; @@ -803,7 +827,7 @@ __initfunc(int js_init(void)) } for (i = 0; i < 4; i++) { - js_axis[i].corr.type = JS_CORR_NONE; + js_axis[i].corr.type = JS_CORR_NONE; js_axis[i].corr.prec = JS_DEF_PREC; } @@ -812,7 +836,7 @@ __initfunc(int js_init(void)) enable_bh(JS_BH); init_timer(&js_timer); js_timer.function = js_do_timer; - + return 0; } |