summaryrefslogtreecommitdiffstats
path: root/drivers/char/joystick.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/joystick.c')
-rw-r--r--drivers/char/joystick.c298
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;
}