patch-2.1.45 linux/drivers/char/joystick.c
Next file: linux/drivers/char/lp.c
Previous file: linux/drivers/char/cyclades.c
Back to the patch index
Back to the overall index
- Lines: 377
- Date:
Wed Jul 16 19:22:50 1997
- Orig file:
v2.1.44/linux/drivers/char/joystick.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.44/linux/drivers/char/joystick.c linux/drivers/char/joystick.c
@@ -0,0 +1,376 @@
+/*
+
+ linux/drivers/char/joystick.c
+ Copyright (C) 1992, 1993 Arthur C. Smith
+ Joystick driver for Linux running on an IBM compatible computer.
+
+VERSION INFO:
+01/08/93 ACS 0.1: Works but needs multi-joystick support
+01/13/93 ACS 0.2: Added multi-joystick support (minor 0 and 1)
+ Added delay between measuring joystick axis
+ Added scaling ioctl
+02/16/93 ACS 0.3: Modified scaling to use ints to prevent kernel
+ panics 8-)
+02/28/93 ACS 0.4: Linux99.6 and fixed race condition in js_read.
+ After looking at a schematic of a joystick card
+ it became apparent that any write to the joystick
+ port started ALL the joystick one shots. If the
+ one that we are reading is short enough and the
+ first one to be read, the second one will return
+ bad data if it's one shot has not expired when
+ the joystick port is written for the second time.
+ Thus solves the mystery delay problem in 0.2!
+05/05/93 ACS/Eyal 0.5: Upgraded the driver to the 99.9 kernel, added
+ joystick support to the make config options,
+ updated the driver to return the buttons as
+ positive logic, and read both axis at once
+ (thanks Eyal!), and added some new ioctls.
+02/12/94 Jeff Tranter 0.6: Made necessary changes to work with 0.99pl15
+ kernel (and hopefully 1.0). Also did some
+ cleanup: indented code, fixed some typos, wrote
+ man page, etc...
+05/17/95 Dan Fandrich 0.7.3: Added I/O port registration, cleaned up code
+04/03/96 Matt Rhoten 0.8: many minor changes:
+ new read loop from Hal Maney <maney@norden.com>
+ cleaned up #includes to allow #include of
+ joystick.h with gcc -Wall and from g++
+ made js_init fail if it finds zero joysticks
+ general source/comment cleanup
+ use of MOD_(INC|DEC)_USE_COUNT
+ changes from Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>
+ to compile correctly under 1.3 in kernel or as module
+06/30/97 Alan Cox 0.9: Ported to 2.1.x
+ Reformatted to resemble Linux coding standard
+ Removed semaphore bug (we can dump the lot I think)
+ Fixed xntp timer adjust during joystick timer0 bug
+ Changed variable names to lower case. Kept binary
+ compatibility.
+ Better ioctl names. Kept binary compatibility.
+ Removed 'save_busy'. Just set busy to 1.
+*/
+
+#include <linux/module.h>
+#include <linux/joystick.h>
+#include <linux/mm.h>
+#include <linux/major.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+static struct js_config js_data[JS_MAX]; /* misc data */
+static int js_exist; /* which joysticks' axis exist? */
+static int js_read_semaphore; /* to prevent two processes from trying
+ to read different joysticks at the
+ same time */
+
+/*
+ * get_timer0():
+ * returns the current value of timer 0. This is a 16 bit counter that starts
+ * at LATCH and counts down to 0
+ */
+
+extern inline int get_timer0(void)
+{
+ unsigned long flags;
+ int t0, t1;
+ save_flags(flags);
+ cli();
+ outb (0, PIT_MODE);
+ t0 = (int) inb (PIT_COUNTER_0);
+ t1 = ((int) inb (PIT_COUNTER_0) << 8) + t0;
+ restore_flags(flags);
+ return (t1);
+}
+
+/*
+ * find_axes():
+ *
+ * returns which axes are hooked up, in a bitfield. 2^n is set if
+ * axis n is hooked up, for 0 <= n < 4.
+ *
+ * REVIEW: should update this to handle eight-axis (four-stick) game port
+ * cards. anyone have one of these to test on? mattrh 3/23/96
+ */
+
+extern inline int find_axes(void)
+{
+ int j;
+ outb (0xff, JS_PORT); /* trigger oneshots */
+ /* and see what happens */
+ for (j = JS_DEF_TIMEOUT; (0x0f & inb (JS_PORT)) && j; j--);
+ /* do nothing; wait for the timeout */
+ js_exist = inb (JS_PORT) & 0x0f; /* get joystick status byte */
+ js_exist = (~js_exist) & 0x0f;
+/* printk("find_axes: js_exist is %d (0x%04X)\n", js_exist, js_exist);*/
+ return js_exist;
+}
+
+static int js_ioctl (struct inode *inode,
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned int minor = MINOR (inode->i_rdev);
+ if (minor >= JS_MAX)
+ return -ENODEV;
+
+ if ((((inb (JS_PORT) & 0x0f) >> (minor << 1)) & 0x03) == 0x03) /*js minor exists?*/
+ return -ENODEV;
+ switch (cmd)
+ {
+
+ case JSIOCSCAL: /*from struct *arg to js_data[minor]*/
+ if(copy_from_user(&js_data[minor].js_corr,
+ (void *)arg, sizeof(struct js_status)))
+ return -EFAULT;
+ break;
+ case JSIOCGCAL: /*to struct *arg from js_data[minor]*/
+ if(copy_to_user((void *) arg, &js_data[minor].js_corr,
+ sizeof(struct js_status)))
+ return -EFAULT;
+ break;
+ case JSIOCSTIMEOUT:
+ if(copy_from_user(&js_data[minor].js_timeout,
+ (void *)arg, sizeof(js_data[0].js_timeout)))
+ return -EFAULT;
+ break;
+ case JSIOCGTIMEOUT:
+ if(copy_to_user((void *)arg, &js_data[minor].js_timeout,
+ sizeof(js_data[0].js_timeout)))
+ return -EFAULT;
+ break;
+ case JSIOCSTIMELIMIT:
+ if(copy_from_user(&js_data[minor].js_timelimit,
+ (void *)arg, sizeof(js_data[0].js_timelimit)))
+ return -EFAULT;
+ break;
+ case JSIOCGTIMELIMIT:
+ if(copy_to_user((void *)arg, &js_data[minor].js_timelimit,
+ sizeof(js_data[0].js_timelimit)))
+ return -EFAULT;
+ break;
+ case JSIOCGCONFIG:
+ if(copy_to_user((void *)arg, &js_data[minor],
+ sizeof(struct js_config)))
+ return -EFAULT;
+ break;
+ case JSIOCSCONFIG:
+ if(copy_from_user(&js_data[minor], (void *)arg,
+ sizeof(struct js_config)))
+ return -EFAULT;
+ /* Must be busy to do this ioctl! */
+ js_data[minor].busy = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * js_open():
+ * device open routine. increments module usage count, initializes
+ * data for that joystick.
+ *
+ * returns: 0 or
+ * -ENODEV: asked for joystick other than #0 or #1
+ * -ENODEV: asked for joystick on axis where there is none
+ * -EBUSY: attempt to open joystick already open
+ */
+
+static int js_open (struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR (inode->i_rdev);
+ int j;
+
+ if (minor >= JS_MAX)
+ return -ENODEV; /*check for joysticks*/
+
+ for (j = JS_DEF_TIMEOUT; (js_exist & inb (JS_PORT)) && j; j--);
+ cli(); /*block js_read while js_exist is being modified*/
+ /*js minor exists?*/
+ if ((((js_exist = inb (JS_PORT)) >> (minor << 1)) & 0x03) == 0x03) {
+ js_exist = (~js_exist) & 0x0f;
+ sti();
+ return -ENODEV;
+ }
+ js_exist = (~js_exist) & 0x0f;
+ sti();
+
+ if (js_data[minor].busy)
+ return -EBUSY;
+ js_data[minor].busy = JS_TRUE;
+ js_data[minor].js_corr.x = JS_DEF_CORR; /*default scale*/
+ js_data[minor].js_corr.y = JS_DEF_CORR;
+ js_data[minor].js_timeout = JS_DEF_TIMEOUT;
+ js_data[minor].js_timelimit = JS_DEF_TIMELIMIT;
+ js_data[minor].js_expiretime = jiffies;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int js_release (struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR (inode->i_rdev);
+ inode->i_atime = CURRENT_TIME;
+ js_data[minor].busy = JS_FALSE;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_read() reads the buttons x, and y axis from both joysticks if a
+ * given interval has expired since the last read or is equal to
+ * -1l. The buttons are in port 0x201 in the high nibble. The axis are
+ * read by writing to 0x201 and then measuring the time it takes the
+ * one shots to clear.
+ */
+
+static long js_read (struct inode *inode, struct file *file, char *buf, unsigned long count)
+{
+ int j, chk, jsmask;
+ int t0, t_x0, t_y0, t_x1, t_y1;
+ unsigned int minor, minor2;
+ int buttons;
+
+ if (count != JS_RETURN)
+ return -EINVAL;
+ minor = MINOR (inode->i_rdev);
+ inode->i_atime = CURRENT_TIME;
+ if (jiffies >= js_data[minor].js_expiretime)
+ {
+ minor2 = minor << 1;
+ j = js_data[minor].js_timeout;
+ for (; (js_exist & inb (JS_PORT)) && j; j--);
+ if (j == 0)
+ return -ENODEV; /*no joystick here*/
+ /*Make sure no other proc is using port*/
+
+ cli();
+ js_read_semaphore++;
+ sti();
+
+ buttons = ~(inb (JS_PORT) >> 4);
+ js_data[0].js_save.buttons = buttons & 0x03;
+ js_data[1].js_save.buttons = (buttons >> 2) & 0x03;
+ j = js_data[minor].js_timeout;
+ jsmask = 0;
+
+ cli(); /*no interrupts!*/
+ outb (0xff, JS_PORT); /*trigger one-shots*/
+ /*get init timestamp*/
+ t_x0 = t_y0 = t_x1 = t_y1 = t0 = get_timer0 ();
+ /*wait for an axis' bit to clear or timeout*/
+ while (j-- && (chk = (inb (JS_PORT) & js_exist ) | jsmask))
+ {
+ if (!(chk & JS_X_0)) {
+ t_x0 = get_timer0();
+ jsmask |= JS_X_0;
+ }
+ if (!(chk & JS_Y_0)) {
+ t_y0 = get_timer0();
+ jsmask |= JS_Y_0;
+ }
+ if (!(chk & JS_X_1)) {
+ t_x1 = get_timer0();
+ jsmask |= JS_X_1;
+ }
+ if (!(chk & JS_Y_1)) {
+ t_y1 = get_timer0();
+ jsmask |= JS_Y_1;
+ }
+ }
+ sti(); /* allow interrupts */
+
+ js_read_semaphore = 0; /* allow other reads to progress */
+ if (j == 0)
+ return -ENODEV; /*read timed out*/
+ js_data[0].js_expiretime = jiffies +
+ js_data[0].js_timelimit; /*update data*/
+ js_data[1].js_expiretime = jiffies +
+ js_data[1].js_timelimit;
+ js_data[0].js_save.x = DELTA_TIME (t0, t_x0) >>
+ js_data[0].js_corr.x;
+ js_data[0].js_save.y = DELTA_TIME (t0, t_y0) >>
+ js_data[0].js_corr.y;
+ js_data[1].js_save.x = DELTA_TIME (t0, t_x1) >>
+ js_data[1].js_corr.x;
+ js_data[1].js_save.y = DELTA_TIME (t0, t_y1) >>
+ js_data[1].js_corr.y;
+ }
+
+ if(copy_to_user(buf, &js_data[minor].js_save, JS_RETURN))
+ return -EFAULT;
+ return JS_RETURN;
+}
+
+
+static struct file_operations js_fops =
+{
+ NULL, /* js_lseek*/
+ js_read, /* js_read */
+ NULL, /* js_write*/
+ NULL, /* js_readaddr*/
+ NULL, /* js_select */
+ js_ioctl, /* js_ioctl*/
+ NULL, /* js_mmap */
+ js_open, /* js_open*/
+ js_release, /* js_release*/
+ NULL /* js_fsync */
+};
+
+#ifdef MODULE
+
+#define joystick_init init_module
+
+void cleanup_module (void)
+{
+ if (unregister_chrdev (JOYSTICK_MAJOR, "joystick"))
+ printk ("joystick: cleanup_module failed\n");
+ release_region(JS_PORT, 1);
+}
+
+#endif /* MODULE */
+
+int joystick_init(void)
+{
+ int js_num;
+ int js_count;
+
+ if (check_region(JS_PORT, 1)) {
+ printk("js_init: port already in use\n");
+ return -EBUSY;
+ }
+
+ js_num = find_axes();
+ js_count = !!(js_num & 0x3) + !!(js_num & 0xC);
+
+
+ if (js_count == 0)
+ {
+ printk("No joysticks found.\n");
+ return -ENODEV;
+ /* if the user boots the machine, which runs insmod, and THEN
+ decides to hook up the joystick, well, then we do the wrong
+ thing. But it's a good idea to avoid giving out a false sense
+ of security by letting the module load otherwise. */
+ }
+
+ if (register_chrdev (JOYSTICK_MAJOR, "joystick", &js_fops)) {
+ printk ("Unable to get major=%d for joystick\n",
+ JOYSTICK_MAJOR);
+ return -EBUSY;
+ }
+ request_region(JS_PORT, 1, "joystick");
+
+ for (js_num = 0; js_num < JS_MAX; js_num++)
+ js_data[js_num].busy = JS_FALSE;
+ js_read_semaphore = 0;
+
+ printk (KERN_INFO "Found %d joystick%c.\n",
+ js_count,
+ (js_num == 1) ? ' ' : 's');
+ return 0;
+}
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov