diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-04 07:40:19 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-04 07:40:19 +0000 |
commit | 33263fc5f9ac8e8cb2b22d06af3ce5ac1dd815e4 (patch) | |
tree | 2d1b86a40bef0958a68cf1a2eafbeb0667a70543 /drivers/char/joystick | |
parent | 216f5f51aa02f8b113aa620ebc14a9631a217a00 (diff) |
Merge with Linux 2.3.32.
Diffstat (limited to 'drivers/char/joystick')
21 files changed, 3862 insertions, 1384 deletions
diff --git a/drivers/char/joystick/Config.in b/drivers/char/joystick/Config.in index 810decada..3e371744e 100644 --- a/drivers/char/joystick/Config.in +++ b/drivers/char/joystick/Config.in @@ -1,19 +1,34 @@ # -# Joystick lowlevel driver configuration +# Joystick driver # -dep_tristate ' Classic PC analog joysticks and gamepads' CONFIG_JOY_ANALOG $CONFIG_JOYSTICK -dep_tristate ' FPGaming and MadCatz A3D controllers' CONFIG_JOY_ASSASIN $CONFIG_JOYSTICK -dep_tristate ' Gravis GrIP joysticks and gamepads' CONFIG_JOY_GRAVIS $CONFIG_JOYSTICK -dep_tristate ' Logitech Digital joysticks and gamepads' CONFIG_JOY_LOGITECH $CONFIG_JOYSTICK -dep_tristate ' Microsoft SideWinder, Genius Digital joysticks and gamepads' CONFIG_JOY_SIDEWINDER $CONFIG_JOYSTICK -dep_tristate ' ThrustMaster DirectConnect joysticks and gamepads' CONFIG_JOY_THRUSTMASTER $CONFIG_JOYSTICK -dep_tristate ' PDPI Lightning 4 gamecards' CONFIG_JOY_LIGHTNING $CONFIG_JOYSTICK -if [ "$CONFIG_PARPORT" != "n" ]; then - dep_tristate ' NES, SNES, PSX, Multisystem joysticks and gamepads' CONFIG_JOY_CONSOLE $CONFIG_JOYSTICK $CONFIG_PARPORT - dep_tristate ' Sega, Multisystem joysticks and gamepads' CONFIG_JOY_DB9 $CONFIG_JOYSTICK $CONFIG_PARPORT - dep_tristate ' TurboGraFX Multisystem joystick interface' CONFIG_JOY_TURBOGRAFX $CONFIG_JOYSTICK $CONFIG_PARPORT -fi -if [ "$CONFIG_AMIGA" = "y" ]; then - dep_tristate ' Amiga joysticks' CONFIG_JOY_AMIGA $CONFIG_JOYSTICK +mainmenu_option next_comment +comment 'Joysticks' + +tristate 'Joystick support' CONFIG_JOYSTICK + +if [ "$CONFIG_JOYSTICK" != "n" ]; then + dep_tristate ' Classic PC analog' CONFIG_JOY_ANALOG $CONFIG_JOYSTICK + dep_tristate ' FPGaming and MadCatz A3D' CONFIG_JOY_ASSASSIN $CONFIG_JOYSTICK + dep_tristate ' Gravis GrIP' CONFIG_JOY_GRAVIS $CONFIG_JOYSTICK + dep_tristate ' Logitech ADI' CONFIG_JOY_LOGITECH $CONFIG_JOYSTICK + dep_tristate ' Microsoft SideWinder' CONFIG_JOY_SIDEWINDER $CONFIG_JOYSTICK + dep_tristate ' ThrustMaster DirectConnect' CONFIG_JOY_THRUSTMASTER $CONFIG_JOYSTICK + dep_tristate ' Creative Labs Blaster' CONFIG_JOY_CREATIVE $CONFIG_JOYSTICK + dep_tristate ' PDPI Lightning 4 card' CONFIG_JOY_LIGHTNING $CONFIG_JOYSTICK + dep_tristate ' Trident 4DWave and Aureal Vortex gameport' CONFIG_JOY_PCI $CONFIG_JOYSTICK + dep_tristate ' Magellan and Space Mouse' CONFIG_JOY_MAGELLAN $CONFIG_JOYSTICK + dep_tristate ' SpaceTec SpaceOrb 360 and SpaceBall Avenger' CONFIG_JOY_SPACEORB $CONFIG_JOYSTICK + dep_tristate ' SpaceTec SpaceBall 4000 FLX' CONFIG_JOY_SPACEBALL $CONFIG_JOYSTICK + dep_tristate ' Logitech WingMan Warrior' CONFIG_JOY_WARRIOR $CONFIG_JOYSTICK + if [ "$CONFIG_PARPORT" != "n" ]; then + dep_tristate ' NES, SNES, PSX, N64, Multi' CONFIG_JOY_CONSOLE $CONFIG_JOYSTICK $CONFIG_PARPORT + dep_tristate ' Sega, Multi' CONFIG_JOY_DB9 $CONFIG_JOYSTICK $CONFIG_PARPORT + dep_tristate ' TurboGraFX interface' CONFIG_JOY_TURBOGRAFX $CONFIG_JOYSTICK $CONFIG_PARPORT + fi + if [ "$CONFIG_AMIGA" = "y" ]; then + dep_tristate ' Amiga joysticks' CONFIG_JOY_AMIGA $CONFIG_JOYSTICK + fi fi + +endmenu diff --git a/drivers/char/joystick/Makefile b/drivers/char/joystick/Makefile index 3339fc83f..2bb5870b0 100644 --- a/drivers/char/joystick/Makefile +++ b/drivers/char/joystick/Makefile @@ -1,23 +1,18 @@ # # Makefile for the joystick drivers. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes.. -# O_TARGET := js.o +OX_OBJS := O_OBJS := +MX_OBJS := M_OBJS := ifeq ($(CONFIG_JOYSTICK),y) -O_OBJS += joystick.o +OX_OBJS += joystick.o else ifeq ($(CONFIG_JOYSTICK),m) - M_OBJS += joystick.o + MX_OBJS += joystick.o endif endif @@ -37,11 +32,11 @@ else endif endif -ifeq ($(CONFIG_JOY_ASSASIN),y) -O_OBJS += joy-assasin.o +ifeq ($(CONFIG_JOY_ASSASSIN),y) +O_OBJS += joy-assassin.o else - ifeq ($(CONFIG_JOY_ASSASIN),m) - M_OBJS += joy-assasin.o + ifeq ($(CONFIG_JOY_ASSASSIN),m) + M_OBJS += joy-assassin.o endif endif @@ -53,6 +48,14 @@ else endif endif +ifeq ($(CONFIG_JOY_CREATIVE),y) +O_OBJS += joy-creative.o +else + ifeq ($(CONFIG_JOY_CREATIVE),m) + M_OBJS += joy-creative.o + endif +endif + ifeq ($(CONFIG_JOY_DB9),y) O_OBJS += joy-db9.o else @@ -85,6 +88,22 @@ else endif endif +ifeq ($(CONFIG_JOY_MAGELLAN),y) +O_OBJS += joy-magellan.o +else + ifeq ($(CONFIG_JOY_MAGELLAN),m) + M_OBJS += joy-magellan.o + endif +endif + +ifeq ($(CONFIG_JOY_PCI),y) +O_OBJS += joy-pci.o +else + ifeq ($(CONFIG_JOY_PCI),m) + M_OBJS += joy-pci.o + endif +endif + ifeq ($(CONFIG_JOY_SIDEWINDER),y) O_OBJS += joy-sidewinder.o else @@ -93,6 +112,22 @@ else endif endif +ifeq ($(CONFIG_JOY_SPACEORB),y) +O_OBJS += joy-spaceorb.o +else + ifeq ($(CONFIG_JOY_SPACEORB),m) + M_OBJS += joy-spaceorb.o + endif +endif + +ifeq ($(CONFIG_JOY_SPACEBALL),y) +O_OBJS += joy-spaceball.o +else + ifeq ($(CONFIG_JOY_SPACEBALL),m) + M_OBJS += joy-spaceball.o + endif +endif + ifeq ($(CONFIG_JOY_THRUSTMASTER),y) O_OBJS += joy-thrustmaster.o else @@ -109,4 +144,12 @@ else endif endif +ifeq ($(CONFIG_JOY_WARRIOR),y) +O_OBJS += joy-warrior.o +else + ifeq ($(CONFIG_JOY_WARRIOR),m) + M_OBJS += joy-warrior.o + endif +endif + include $(TOPDIR)/Rules.make diff --git a/drivers/char/joystick/joy-amiga.c b/drivers/char/joystick/joy-amiga.c index 918025040..5e3198f70 100644 --- a/drivers/char/joystick/joy-amiga.c +++ b/drivers/char/joystick/joy-amiga.c @@ -1,7 +1,9 @@ /* * joy-amiga.c Version 1.2 * - * Copyright (c) 1998 Vojtech Pavlik + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -36,13 +38,14 @@ #include <linux/kernel.h> #include <linux/module.h> #include <asm/amigahw.h> +#include <linux/init.h> static struct js_port* js_am_port __initdata = NULL; MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_PARM(js_am, "1-2i"); -static int js_am[]={0,0}; +static int __initdata js_am[] = { 0, 0 }; /* * js_am_read() reads and Amiga joystick data. @@ -69,7 +72,7 @@ static int js_am_read(void *info, int **axes, int **buttons) axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1); data = ~(data ^ (data << 1)); - axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1); + axes[0][1] = ((data >> 1) & 1) - ((data >> 9) & 1); return 0; } @@ -114,11 +117,14 @@ static void __init js_am_init_corr(struct js_corr **corr) } #ifndef MODULE -void __init js_am_setup(char *str, int *ints) +int __init js_am_setup(SETUP_PARAM) { int i; + SETUP_PARSE(2); for (i = 0; i <= ints[0] && i < 2; i++) js_am[i] = ints[i+1]; + return 1; } +__setup("js_am=", js_am_setup); #endif #ifdef MODULE @@ -148,8 +154,8 @@ int __init js_am_init(void) #ifdef MODULE void cleanup_module(void) { - while (js_am_port != NULL) { - if (js_am_port->devs[0] != NULL) + while (js_am_port) { + if (js_am_port->devs[0]) js_unregister_device(js_am_port->devs[0]); js_am_port = js_unregister_port(js_am_port); } diff --git a/drivers/char/joystick/joy-analog.c b/drivers/char/joystick/joy-analog.c index 7c1c9719d..a4a7b9849 100644 --- a/drivers/char/joystick/joy-analog.c +++ b/drivers/char/joystick/joy-analog.c @@ -1,7 +1,9 @@ /* * joy-analog.c Version 1.2 * - * Copyright (c) 1996-1998 Vojtech Pavlik + * Copyright (c) 1996-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -30,15 +32,21 @@ */ #include <asm/io.h> +#include <asm/param.h> +#include <asm/system.h> +#include <linux/config.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/ioport.h> #include <linux/joystick.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/sched.h> #include <linux/string.h> +#include <linux/init.h> -#define JS_AN_MAX_TIME 3000 +#define JS_AN_MAX_TIME 3000 /* 3 ms */ +#define JS_AN_LOOP_TIME 2000 /* 2 t */ static int js_an_port_list[] __initdata = {0x201, 0}; static struct js_port* js_an_port __initdata = NULL; @@ -46,53 +54,102 @@ static struct js_port* js_an_port __initdata = NULL; MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_PARM(js_an, "2-24i"); -static int js_an[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0}; +static int __initdata js_an[] = { -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0 }; #include "joy-analog.h" +struct js_ax_info { + int io; + int speed; + int loop; + int timeout; + struct js_an_info an; +}; + +/* + * Time macros. + */ + +#ifdef __i386__ +#ifdef CONFIG_X86_TSC +#define GET_TIME(x) __asm__ __volatile__ ( "rdtsc" : "=a" (x) : : "dx" ) +#define DELTA(x,y) ((x)-(y)) +#define TIME_NAME "TSC" +#else +#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0) +#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180L/HZ:0)) +#define TIME_NAME "PIT" +#endif +#elif __alpha__ +#define GET_TIME(x) __asm__ __volatile__ ( "rpcc %0" : "=r" (x) ) +#define DELTA(x,y) ((x)-(y)) +#define TIME_NAME "PCC" +#endif + +#ifndef GET_TIME +#define FAKE_TIME +static unsigned long js_an_faketime = 0; +#define GET_TIME(x) do { x = js_an_faketime++; } while(0) +#define DELTA(x,y) ((x)-(y)) +#define TIME_NAME "Unreliable" +#endif + /* * js_an_read() reads analog joystick data. */ static int js_an_read(void *xinfo, int **axes, int **buttons) { - struct js_an_info *info = xinfo; + struct js_ax_info *info = xinfo; + struct js_an_info *an = &info->an; + int io = info->io; + unsigned long flags; unsigned char buf[4]; - int time[4]; - unsigned char u, v, a; - unsigned int t, t1; + unsigned int time[4]; + unsigned char u, v, w; + unsigned int p, q, r, s, t; int i, j; - int timeout; - int io = info->io; - timeout = (JS_AN_MAX_TIME * js_time_speed_a) >> 10; - - info->buttons = (~inb(io) & JS_AN_BUTTONS_STD) >> 4; + an->buttons = ~inb(io) >> 4; i = 0; - u = a = ((info->mask[0] | info->mask[1]) & JS_AN_AXES_STD) | (info->extensions & JS_AN_HAT_FCS) - | ((info->extensions & JS_AN_BUTTONS_PXY_XY) >> 2) | ((info->extensions & JS_AN_BUTTONS_PXY_UV) >> 4); - + w = ((an->mask[0] | an->mask[1]) & JS_AN_AXES_STD) | (an->extensions & JS_AN_HAT_FCS) + | ((an->extensions & JS_AN_BUTTONS_PXY_XY) >> 2) | ((an->extensions & JS_AN_BUTTONS_PXY_UV) >> 4); + p = info->loop; + q = info->timeout; + + __save_flags(flags); + __cli(); outb(0xff,io); - t = js_get_time_a(); + GET_TIME(r); + __restore_flags(flags); + t = r; + v = w; do { - v = inb(io) & a; - t1 = js_get_time_a(); - if (u ^ v) { - time[i] = js_delta_a(t1,t); + s = t; + u = v; + __cli(); + v = inb(io) & w; + GET_TIME(t); + __restore_flags(flags); + if ((u ^ v) && (DELTA(t,s) < p)) { + time[i] = t; buf[i] = u ^ v; - u = v; i++; } - } while (v && js_delta_a(t1,t) < timeout); + } while (v && (i < 4) && (DELTA(t,r) < q)); - for (--i; i >= 0; i--) + v <<= 4; + + for (--i; i >= 0; i--) { + v |= buf[i]; for (j = 0; j < 4; j++) - if (buf[i] & (1 << j)) info->axes[j] = (time[i] << 10) / js_time_speed_a; + if (buf[i] & (1 << j)) an->axes[j] = (DELTA(time[i],r) << 10) / info->speed; + } - js_an_decode(info, axes, buttons); + js_an_decode(an, axes, buttons); - return 0; + return -(v != w); } /* @@ -116,12 +173,53 @@ static int js_an_close(struct js_dev *jd) } /* + * js_an_calibrate_timer() calibrates the timer and computes loop + * and timeout values for a joystick port. + */ + +static void __init js_an_calibrate_timer(struct js_ax_info *info) +{ + unsigned int i, t, tx, t1, t2, t3; + unsigned long flags; + int io = info->io; + + save_flags(flags); + cli(); + GET_TIME(t1); +#ifdef FAKE_TIME + js_an_faketime += 830; +#endif + udelay(1000); + GET_TIME(t2); + GET_TIME(t3); + restore_flags(flags); + + info->speed = DELTA(t2, t1) - DELTA(t3, t2); + + tx = 1 << 30; + + for(i = 0; i < 50; i++) { + save_flags(flags); + cli(); + GET_TIME(t1); + for(t = 0; t < 50; t++) { inb(io); GET_TIME(t2); } + GET_TIME(t3); + restore_flags(flags); + udelay(i); + if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; + } + + info->loop = (JS_AN_LOOP_TIME * t) / 50000; + info->timeout = (JS_AN_MAX_TIME * info->speed) / 1000; +} + +/* * js_an_probe() probes for analog joysticks. */ static struct js_port __init *js_an_probe(int io, int mask0, int mask1, struct js_port *port) { - struct js_an_info info; + struct js_ax_info info, *ax; int i, numdev; unsigned char u; @@ -129,7 +227,6 @@ static struct js_port __init *js_an_probe(int io, int mask0, int mask1, struct j if (check_region(io, 1)) return port; - if (((u = inb(io)) & 3) == 3) return port; outb(0xff,io); u = inb(io); udelay(JS_AN_MAX_TIME); @@ -138,31 +235,41 @@ static struct js_port __init *js_an_probe(int io, int mask0, int mask1, struct j if (!u) return port; if (u & 0xf0) return port; - if ((numdev = js_an_probe_devs(&info, u, mask0, mask1, port)) <= 0) + if ((numdev = js_an_probe_devs(&info.an, u, mask0, mask1, port)) <= 0) return port; info.io = io; + js_an_calibrate_timer(&info); + request_region(info.io, 1, "joystick (analog)"); - port = js_register_port(port, &info, numdev, sizeof(struct js_an_info), js_an_read); + port = js_register_port(port, &info, numdev, sizeof(struct js_ax_info), js_an_read); + ax = port->info; for (i = 0; i < numdev; i++) - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, i, js_an_axes(i, &info), js_an_buttons(i, &info), - js_an_name(i, &info), js_an_open, js_an_close), - js_an_name(i, &info), info.io); - - js_an_read(port->info, port->axes, port->buttons); - js_an_init_corr(port->info, port->axes, port->corr, 8); + printk(KERN_INFO "js%d: %s at %#x ["TIME_NAME" timer, %d %sHz clock, %d ns res]\n", + js_register_device(port, i, js_an_axes(i, &ax->an), js_an_buttons(i, &ax->an), + js_an_name(i, &ax->an), js_an_open, js_an_close), + js_an_name(i, &ax->an), + ax->io, + ax->speed > 10000 ? (ax->speed + 800) / 1000 : ax->speed, + ax->speed > 10000 ? "M" : "k", + ax->loop * 1000000000 / JS_AN_LOOP_TIME / ax->speed); + + js_an_read(ax, port->axes, port->buttons); + js_an_init_corr(&ax->an, port->axes, port->corr, 8); return port; } #ifndef MODULE -void __init js_an_setup(char *str, int *ints) +int __init js_an_setup(SETUP_PARAM) { int i; + SETUP_PARSE(24); for (i = 0; i <= ints[0] && i < 24; i++) js_an[i] = ints[i+1]; + return 1; } +__setup("js_an=", js_an_setup); #endif #ifdef MODULE @@ -193,11 +300,11 @@ int __init js_an_init(void) void cleanup_module(void) { int i; - struct js_an_info *info; + struct js_ax_info *info; - while (js_an_port != NULL) { + while (js_an_port) { for (i = 0; i < js_an_port->ndevs; i++) - if (js_an_port->devs[i] != NULL) + if (js_an_port->devs[i]) js_unregister_device(js_an_port->devs[i]); info = js_an_port->info; release_region(info->io, 1); diff --git a/drivers/char/joystick/joy-analog.h b/drivers/char/joystick/joy-analog.h index 84479fcd1..a1644350c 100644 --- a/drivers/char/joystick/joy-analog.h +++ b/drivers/char/joystick/joy-analog.h @@ -1,13 +1,15 @@ /* * joy-analog.h Version 1.2 * - * Copyright (c) 1996-1998 Vojtech Pavlik + * Copyright (c) 1996-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* * This file is designed to be included in any joystick driver * that communicates with standard analog joysticks. This currently - * is: joy-analog.c, joy-assasin.c, and joy-lightning.c + * is: joy-analog.c, joy-assassin.c, and joy-lightning.c */ /* @@ -30,6 +32,8 @@ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ +#include <linux/bitops.h> + #define JS_AN_AXES_STD 0x0f #define JS_AN_BUTTONS_STD 0xf0 @@ -53,7 +57,6 @@ static struct { } js_an_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1, 0}, { 0, 1}, {-1, 0}}; struct js_an_info { - int io; unsigned char mask[2]; unsigned int extensions; int axes[4]; @@ -75,7 +78,7 @@ static void js_an_decode(struct js_an_info *info, int **axes, int **buttons) if (info->mask[1] & JS_AN_BUTTONS_STD) buttons[1][0] = 0; if (info->extensions & JS_AN_ANY_CHF) { - switch (info->buttons) { + switch (info->buttons & 0xf) { case 0x1: buttons[0][0] = 0x01; break; case 0x2: buttons[0][0] = 0x02; break; case 0x4: buttons[0][0] = 0x04; break; @@ -134,19 +137,6 @@ static void js_an_decode(struct js_an_info *info, int **axes, int **buttons) } } -/* - * js_an_count_bits() counts set bits in a byte. - */ - -static inline int js_an_count_bits(unsigned long c) -{ - int i = 0; - while (c) { - i += c & 1; - c >>= 1; - } - return i; -} /* * js_an_init_corr() initializes the correction values for @@ -158,7 +148,7 @@ static void __init js_an_init_corr(struct js_an_info *info, int **axes, struct j int i, j, t; for (i = 0; i < 2; i++) - for (j = 0; j < js_an_count_bits(info->mask[i] & 0xf); j++) { + for (j = 0; j < hweight8(info->mask[i] & 0xf); j++) { if ((j == 2 && (info->mask[i] & 0xb) == 0xb) || (j == 3 && (info->mask[i] & 0xf) == 0xf)) { @@ -175,9 +165,9 @@ static void __init js_an_init_corr(struct js_an_info *info, int **axes, struct j corr[i][j].coef[3] = (1 << 29) / (t - (t >> 2) + 1); } - i = js_an_count_bits(info->mask[0] & 0xf); + i = hweight8(info->mask[0] & 0xf); - for (j = i; j < i + (js_an_count_bits(info->extensions & JS_AN_HATS_ALL) << 1); j++) { + for (j = i; j < i + (hweight8(info->extensions & JS_AN_HATS_ALL) << 1); j++) { corr[0][j].type = JS_CORR_BROKEN; corr[0][j].prec = 0; corr[0][j].coef[0] = 0; @@ -204,6 +194,7 @@ static int __init js_an_probe_devs(struct js_an_info *info, int exist, int mask0 info->mask[1] = mask1 & (exist | 0xf0) & ~info->mask[0]; info->extensions = (mask0 >> 8) & ((exist & JS_AN_HAT_FCS) | ((exist << 2) & JS_AN_BUTTONS_PXY_XY) | ((exist << 4) & JS_AN_BUTTONS_PXY_UV) | JS_AN_ANY_CHF); + if (info->extensions & JS_AN_BUTTONS_PXY) { info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_XY) >> 2); info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_UV) >> 4); @@ -212,7 +203,7 @@ static int __init js_an_probe_devs(struct js_an_info *info, int exist, int mask0 if (info->extensions & JS_AN_HAT_FCS) { info->mask[0] &= ~JS_AN_HAT_FCS; info->mask[1] = 0; - info->extensions &= ~(JS_AN_BUTTON_PXY_Y | JS_AN_BUTTON_PXY_U); + info->extensions &= ~(JS_AN_BUTTON_PXY_Y | JS_AN_BUTTON_PXY_V); } if (info->extensions & JS_AN_ANY_CHF) { info->mask[0] |= 0xf0; @@ -233,8 +224,7 @@ static int __init js_an_probe_devs(struct js_an_info *info, int exist, int mask0 info->mask[0] = 0xcc; /* joystick 1 */ break; case 0xf: - info->mask[0] = 0x33; /* joysticks 0 and 1 */ - info->mask[1] = 0xcc; + info->mask[0] = 0xff; /* 4-axis 4-button joystick */ break; default: printk(KERN_WARNING "joy-analog: Unknown joystick device detected " @@ -252,7 +242,7 @@ static int __init js_an_probe_devs(struct js_an_info *info, int exist, int mask0 static inline int js_an_axes(int i, struct js_an_info *info) { - return js_an_count_bits(info->mask[i] & 0x0f) + js_an_count_bits(info->extensions & JS_AN_HATS_ALL) * 2; + return hweight8(info->mask[i] & 0x0f) + hweight8(info->extensions & JS_AN_HATS_ALL) * 2; } /* @@ -261,9 +251,9 @@ static inline int js_an_axes(int i, struct js_an_info *info) static inline int js_an_buttons(int i, struct js_an_info *info) { - return js_an_count_bits(info->mask[i] & 0xf0) + + return hweight8(info->mask[i] & 0xf0) + (info->extensions & JS_AN_BUTTONS_CHF) * 2 + - js_an_count_bits(info->extensions & JS_AN_BUTTONS_PXY); + hweight8(info->extensions & JS_AN_BUTTONS_PXY); } /* @@ -276,13 +266,13 @@ static char __init *js_an_name(int i, struct js_an_info *info) { sprintf(js_an_name_buf, "Analog %d-axis %d-button", - js_an_count_bits(info->mask[i] & 0x0f), + hweight8(info->mask[i] & 0x0f), js_an_buttons(i, info)); if (info->extensions & JS_AN_HATS_ALL) sprintf(js_an_name_buf, "%s %d-hat", js_an_name_buf, - js_an_count_bits(info->extensions & JS_AN_HATS_ALL)); + hweight8(info->extensions & JS_AN_HATS_ALL)); strcat(js_an_name_buf, " joystick"); @@ -291,7 +281,7 @@ static char __init *js_an_name(int i, struct js_an_info *info) js_an_name_buf, info->extensions & JS_AN_ANY_CHF ? " CHF" : "", info->extensions & JS_AN_HAT_FCS ? " FCS" : "", - info->extensions & JS_AN_BUTTONS_PXY ? " XY-button" : ""); + info->extensions & JS_AN_BUTTONS_PXY ? " XY/UV" : ""); return js_an_name_buf; } diff --git a/drivers/char/joystick/joy-assasin.c b/drivers/char/joystick/joy-assassin.c index 731c6e56a..b76ba8e36 100644 --- a/drivers/char/joystick/joy-assasin.c +++ b/drivers/char/joystick/joy-assassin.c @@ -1,12 +1,14 @@ /* - * joy-assasin.c Version 1.2 + * joy-assassin.c Version 1.2 * - * Copyright (c) 1998 Vojtech Pavlik + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* * This is a module for the Linux joystick driver, supporting - * joysticks using FP-Gaming's Assasin 3D protocol. + * joysticks using FP-Gaming's Assassin 3D protocol. */ /* @@ -38,13 +40,13 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/string.h> +#include <linux/init.h> -#define JS_AS_MAX_START 250 -#define JS_AS_MAX_STROBE 50 -#define JS_AS_MAX_TIME 2400 +#define JS_AS_MAX_START 1000 +#define JS_AS_DELAY_READ 3000 #define JS_AS_MAX_LENGTH 40 -#define JS_AS_MODE_A3D 1 /* Assasin 3D */ +#define JS_AS_MODE_A3D 1 /* Assassin 3D */ #define JS_AS_MODE_PAN 2 /* Panther */ #define JS_AS_MODE_OEM 3 /* Panther OEM version */ #define JS_AS_MODE_PXL 4 /* Panther XL */ @@ -52,7 +54,7 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_PARM(js_as, "2-24i"); -static int js_as[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0}; +static int __initdata js_as[] = { -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0 }; static int js_as_port_list[] __initdata = {0x201, 0}; static struct js_port* js_as_port __initdata = NULL; @@ -67,44 +69,33 @@ struct js_as_info { }; /* - * js_as_read_packet() reads an Assasin 3D packet. + * js_as_read_packet() reads an Assassin 3D packet. */ static int js_as_read_packet(int io, int length, char *data) { unsigned char u, v; int i; - unsigned int t, t1; + unsigned int t, p; unsigned long flags; - int start = (js_time_speed * JS_AS_MAX_START) >> 10; - int strobe = (js_time_speed * JS_AS_MAX_STROBE) >> 10; - i = 0; __save_flags(flags); __cli(); - outb(0xff,io); - u = inb(io); - t = js_get_time(); - - do { - v = inb(io); - t1 = js_get_time(); - } while (u == v && js_delta(t1, t) < start); - - t = t1; + outb(0xff,io); + v = inb(io); + t = p = JS_AS_MAX_START; - do { - v = inb(io); - t1 = js_get_time(); - if ((u ^ v) & u & 0x10) { + while (t > 0 && i < length) { + t--; + u = v; v = inb(io); + if (~v & u & 0x10) { data[i++] = v >> 5; - t = t1; + p = t = (p - t) << 3; } - u = v; - } while (i < length && js_delta(t1,t) < strobe); + } __restore_flags(flags); @@ -181,11 +172,8 @@ static int js_as_read(void *xinfo, int **axes, int **buttons) if (info->rudder) axes[1][0] = info->an.axes[0]; return 0; - - default: - printk("Error.\n"); - return -1; } + return -1; } /* @@ -254,7 +242,7 @@ static void __init js_as_pxl_init_corr(struct js_corr **corr, int **axes) /* * js_as_as_init_corr() initializes the correction values for - * the Panther and Assasin. + * the Panther and Assassin. */ static void __init js_as_as_init_corr(struct js_corr **corr) @@ -305,25 +293,27 @@ static struct js_port __init *js_as_probe(int io, int mask0, int mask1, struct j if (io < 0) return port; if (check_region(io, 1)) return port; - if (((u = inb(io)) & 3) == 3) return port; - outb(0xff,io); - if (!((inb(io) ^ u) & ~u & 0xf)) return port; - if (js_as_read_packet(io, 1, data) != 1) return port; + i = js_as_read_packet(io, JS_AS_MAX_LENGTH, data); + + printk("%d\n", i); + + if (!i) return port; + if (js_as_csum(data, i)) return port; if (data[0] && data[0] <= 4) { info->mode = data[0]; info->io = io; - request_region(io, 1, "joystick (assasin)"); + request_region(io, 1, "joystick (assassin)"); port = js_register_port(port, info, 3, sizeof(struct js_as_info), js_as_read); info = port->info; } else { - printk(KERN_WARNING "joy-assasin: unknown joystick device detected " + printk(KERN_WARNING "joy-assassin: unknown joystick device detected " "(io=%#x, id=%d), contact <vojtech@suse.cz>\n", io, data[0]); return port; } - udelay(JS_AS_MAX_TIME); + udelay(JS_AS_DELAY_READ); if (info->mode == JS_AS_MODE_PXL) { printk(KERN_INFO "js%d: MadCatz Panther XL at %#x\n", @@ -342,9 +332,9 @@ static struct js_port __init *js_as_probe(int io, int mask0, int mask1, struct j } switch (info->mode) { - case JS_AS_MODE_A3D: name = "FP-Gaming Assasin 3D"; break; + case JS_AS_MODE_A3D: name = "FP-Gaming Assassin 3D"; break; case JS_AS_MODE_PAN: name = "MadCatz Panther"; break; - case JS_AS_MODE_OEM: name = "OEM Assasin 3D"; break; + case JS_AS_MODE_OEM: name = "OEM Assassin 3D"; break; default: name = "This cannot happen"; break; } @@ -374,11 +364,14 @@ static struct js_port __init *js_as_probe(int io, int mask0, int mask1, struct j } #ifndef MODULE -void __init js_as_setup(char *str, int *ints) +int __init js_as_setup(SETUP_PARAM) { int i; + SETUP_PARSE(24); for (i = 0; i <= ints[0] && i < 24; i++) js_as[i] = ints[i+1]; + return 1; } +__setup("js_as=", js_as_setup); #endif #ifdef MODULE @@ -398,7 +391,7 @@ int __init js_as_init(void) if (js_as_port) return 0; #ifdef MODULE - printk(KERN_WARNING "joy-assasin: no joysticks found\n"); + printk(KERN_WARNING "joy-assassin: no joysticks found\n"); #endif return -ENODEV; @@ -410,9 +403,9 @@ void cleanup_module(void) int i; struct js_as_info *info; - while (js_as_port != NULL) { + while (js_as_port) { for (i = 0; i < js_as_port->ndevs; i++) - if (js_as_port->devs[i] != NULL) + if (js_as_port->devs[i]) js_unregister_device(js_as_port->devs[i]); info = js_as_port->info; release_region(info->io, 1); diff --git a/drivers/char/joystick/joy-console.c b/drivers/char/joystick/joy-console.c index ad4be3076..58392f839 100644 --- a/drivers/char/joystick/joy-console.c +++ b/drivers/char/joystick/joy-console.c @@ -1,14 +1,19 @@ /* - * joy-console.c Version 0.11V + * joy-console.c Version 0.14V * - * Copyright (c) 1998 Andree Borrmann + * Copyright (c) 1998 Andree Borrmann + * Copyright (c) 1999 John Dahlstrom + * Copyright (c) 1999 David Kuder + * Copyright (c) 1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* * This is a module for the Linux joystick driver, supporting - * console (NES, SNES, Multi1, Multi2, PSX) gamepads connected - * via parallel port. Up to five such controllers can be - * connected to one parallel port. + * console (NES, SNES, N64, Multi1, Multi2, PSX) gamepads + * connected via parallel port. Up to five such controllers + * can be connected to one parallel port. */ /* @@ -25,6 +30,10 @@ * 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@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ #include <asm/io.h> @@ -36,12 +45,13 @@ #include <linux/module.h> #include <linux/string.h> #include <linux/delay.h> +#include <linux/init.h> -MODULE_AUTHOR("Andree Borrmann <A.Borrmann@tu-bs.de>"); +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_PARM(js_console, "2-6i"); -MODULE_PARM(js_console2,"2-6i"); -MODULE_PARM(js_console3,"2-6i"); +MODULE_PARM(js_console_2,"2-6i"); +MODULE_PARM(js_console_3,"2-6i"); #define JS_NO_PAD 0 @@ -51,29 +61,29 @@ MODULE_PARM(js_console3,"2-6i"); #define JS_MULTI_STICK 4 #define JS_MULTI2_STICK 5 #define JS_PSX_PAD 6 - -#define JS_MAX_PAD JS_PSX_PAD +#define JS_N64_PAD 7 +#define JS_N64_PAD_DPP 8 /* DirectPad Pro compatible layout */ + +#define JS_MAX_PAD JS_N64_PAD_DPP struct js_console_info { -#ifdef USE_PARPORT struct pardevice *port; /* parport device */ -#else - int port; /* hw port */ -#endif int pads; /* total number of pads */ + int pad_to_device[5]; /* pad to js device mapping (js0, js1, etc.) */ int snes; /* SNES pads */ int nes; /* NES pads */ + int n64; /* N64 pads */ + int n64_dpp; /* bits indicate N64 pads treated 14 button, 2 axis */ int multi; /* Multi joysticks */ int multi2; /* Multi joysticks with 2 buttons */ - int psx; /* Normal PSX controllers */ - int negcon; /* PSX NEGCON controllers */ + int psx; /* PSX controllers */ }; static struct js_port* js_console_port = NULL; static int js_console[] __initdata = { -1, 0, 0, 0, 0, 0 }; -static int js_console2[] __initdata = { -1, 0, 0, 0, 0, 0 }; -static int js_console3[] __initdata = { -1, 0, 0, 0, 0, 0 }; +static int js_console_2[] __initdata = { -1, 0, 0, 0, 0, 0 }; +static int js_console_3[] __initdata = { -1, 0, 0, 0, 0, 0 }; static int status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 }; @@ -109,7 +119,7 @@ static int status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 }; #define JS_SNES_L 10 #define JS_SNES_R 11 -#define JS_NES_POWER 0xf8 +#define JS_NES_POWER 0xfc #define JS_NES_CLOCK 0x01 #define JS_NES_LATCH 0x02 @@ -123,17 +133,99 @@ static void js_nes_read_packet(struct js_console_info *info, int length, unsigne { int i; - JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK + JS_NES_LATCH, info->port); + JS_PAR_DATA_OUT(JS_NES_POWER | JS_NES_CLOCK | JS_NES_LATCH, info->port); udelay(JS_NES_DELAY * 2); - JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK, info->port); + JS_PAR_DATA_OUT(JS_NES_POWER | JS_NES_CLOCK, info->port); for (i = 0; i < length; i++) { udelay(JS_NES_DELAY); JS_PAR_DATA_OUT(JS_NES_POWER, info->port); data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; udelay(JS_NES_DELAY); - JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK, info->port); + JS_PAR_DATA_OUT(JS_NES_POWER | JS_NES_CLOCK, info->port); + } +} + +/* + * N64 support. + */ + +#define JS_N64_A 0 +#define JS_N64_B 1 +#define JS_N64_Z 2 +#define JS_N64_START 3 +#define JS_N64_UP 4 +#define JS_N64_DOWN 5 +#define JS_N64_LEFT 6 +#define JS_N64_RIGHT 7 +#define JS_N64_UNUSED1 8 +#define JS_N64_UNUSED2 9 +#define JS_N64_L 10 +#define JS_N64_R 11 +#define JS_N64_CU 12 +#define JS_N64_CD 13 +#define JS_N64_CL 14 +#define JS_N64_CR 15 +#define JS_N64_X 23 /* 16 - 23, signed 8-bit int */ +#define JS_N64_Y 31 /* 24 - 31, signed 8-bit int */ + +#define JS_N64_LENGTH 32 /* N64 bit length, not including stop bit */ +#define JS_N64_REQUEST_LENGTH 37 /* transmit request sequence is 9 bits long */ +#define JS_N64_DELAY 133 /* delay between transmit request, and response ready (us) */ +#define JS_N64_REQUEST 0x1dd1111111ULL /* the request data command (encoded for 000000011) */ +#define JS_N64_DWS 3 /* delay between write segments (required for sound playback because of ISA DMA) */ + /* JS_N64_DWS > 24 is known to fail */ +#define JS_N64_POWER_W 0xe2 /* power during write (transmit request) */ +#define JS_N64_POWER_R 0xfd /* power during read */ +#define JS_N64_OUT 0x1d /* output bits to the 4 pads */ + /* Reading the main axes of any N64 pad is known to fail if the corresponding bit */ + /* in JS_N64_OUT is pulled low on the output port (by any routine) for more */ + /* than 0.123 consecutive ms */ +#define JS_N64_CLOCK 0x02 /* clock bits for read */ + +/* + * js_n64_read_packet() reads an N64 packet. + * Each pad uses one bit per byte. So all pads connected to this port are read in parallel. + */ + +static void js_n64_read_packet(struct js_console_info *info, unsigned char *data) +{ + int i; + unsigned long flags; + +/* + * Request the pad to transmit data + */ + + save_flags(flags); + cli(); + for (i = 0; i < JS_N64_REQUEST_LENGTH; i++) { + JS_PAR_DATA_OUT(JS_N64_POWER_W | ((JS_N64_REQUEST >> i) & 1 ? JS_N64_OUT : 0), info->port); + udelay(JS_N64_DWS); } + restore_flags(flags); + +/* + * Wait for the pad response to be loaded into the 33-bit register of the adapter + */ + + udelay(JS_N64_DELAY); + +/* + * Grab data (ignoring the last bit, which is a stop bit) + */ + + for (i = 0; i < JS_N64_LENGTH; i++) { + JS_PAR_DATA_OUT(JS_N64_POWER_R, info->port); + data[i] = JS_PAR_STATUS(info->port); + JS_PAR_DATA_OUT(JS_N64_POWER_R | JS_N64_CLOCK, info->port); + } + +/* + * We must wait ~0.2 ms here for the controller to reinitialize before the next read request. + * No worries as long as js_console_read is polled less frequently than this. + */ + } /* @@ -161,7 +253,9 @@ static void js_multi_read_packet(struct js_console_info *info, int length, unsig for (i = 0; i < length; i++) { JS_PAR_DATA_OUT(~(1 << i), info->port); data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; + printk(" %d", data[i]); } + printk("\n"); } /* @@ -169,27 +263,28 @@ static void js_multi_read_packet(struct js_console_info *info, int length, unsig */ #define JS_PSX_DELAY 10 +#define JS_PSX_LENGTH 8 /* talk to the controller in bytes */ -#define JS_PSX_LENGTH 8 +#define JS_PSX_NORMAL 0x41 /* Standard Digital controller */ +#define JS_PSX_NEGCON 0x23 /* NegCon pad */ +#define JS_PSX_MOUSE 0x12 /* PSX Mouse */ +#define JS_PSX_ANALOGR 0x73 /* Analog controller in Red mode */ +#define JS_PSX_ANALOGG 0x53 /* Analog controller in Green mode */ -#define JS_PSX_NORMAL 0x41 -#define JS_PSX_NEGCON 0x23 -#define JS_PSX_MOUSE 0x12 - -#define JS_PSX_SELBUT 0x01 +#define JS_PSX_JOYR 0x02 /* These are for the Analog/Dual Shock controller in RED mode */ +#define JS_PSX_JOYL 0x04 /* I'm not sure the exact purpose of these but its in the docs */ +#define JS_PSX_SELBUT 0x01 /* Standard buttons on almost all PSX controllers. */ #define JS_PSX_START 0x08 -#define JS_PSX_UP 0x10 +#define JS_PSX_UP 0x10 /* Digital direction pad */ #define JS_PSX_RIGHT 0x20 #define JS_PSX_DOWN 0x40 #define JS_PSX_LEFT 0x80 -#define JS_PSX_CLOCK 0x01 -#define JS_PSX_COMMAND 0x02 -#define JS_PSX_POWER 0xf8 -#define JS_PSX_NOPOWER 0x04 -#define JS_PSX_SELECT 0x08 - -#define JS_PSX_CTRL_OUT(X,Y) JS_PAR_CTRL_OUT((X)^0x0f, Y) +#define JS_PSX_CLOCK 0x04 /* Pin 3 */ +#define JS_PSX_COMMAND 0x01 /* Pin 1 */ +#define JS_PSX_POWER 0xf8 /* Pins 5-9 */ +#define JS_PSX_SELECT 0x02 /* Pin 2 */ +#define JS_PSX_NOPOWER 0x04 /* * js_psx_command() writes 8bit command and reads 8bit data from @@ -202,11 +297,11 @@ static int js_psx_command(struct js_console_info *info, int b) cmd = (b&1)?JS_PSX_COMMAND:0; for (i=0; i<8; i++) { - JS_PSX_CTRL_OUT(cmd, info->port); + JS_PAR_DATA_OUT(cmd | JS_PSX_POWER, info->port); udelay(JS_PSX_DELAY); ret |= ((JS_PAR_STATUS(info->port) ^ JS_PAR_STATUS_INVERT ) & info->psx) ? (1<<i) : 0; cmd = (b&1)?JS_PSX_COMMAND:0; - JS_PSX_CTRL_OUT(JS_PSX_CLOCK | cmd, info->port); + JS_PAR_DATA_OUT(cmd | JS_PSX_CLOCK | JS_PSX_POWER, info->port); udelay(JS_PSX_DELAY); b >>= 1; } @@ -228,7 +323,7 @@ static int js_psx_read_packet(struct js_console_info *info, int length, unsigned JS_PAR_DATA_OUT(JS_PSX_POWER, info->port); - JS_PSX_CTRL_OUT(JS_PSX_CLOCK | JS_PSX_SELECT, info->port); /* Select pad */ + JS_PAR_DATA_OUT(JS_PSX_CLOCK | JS_PSX_SELECT | JS_PSX_POWER, info->port); /* Select pad */ udelay(JS_PSX_DELAY*2); js_psx_command(info, 0x01); /* Access pad */ ret = js_psx_command(info, 0x42); /* Get device id */ @@ -237,7 +332,7 @@ static int js_psx_read_packet(struct js_console_info *info, int length, unsigned data[i]=js_psx_command(info, 0); else ret = -1; - JS_PSX_CTRL_OUT(JS_PSX_SELECT | JS_PSX_CLOCK, info->port); + JS_PAR_DATA_OUT(JS_PSX_SELECT | JS_PSX_CLOCK | JS_PSX_POWER, info->port); __restore_flags(flags); return ret; @@ -248,14 +343,14 @@ static int js_psx_read_packet(struct js_console_info *info, int length, unsigned * js_console_read() reads and analyzes console pads data. */ -#define JS_MAX_LENGTH JS_SNES_LENGTH +#define JS_MAX_LENGTH JS_N64_LENGTH static int js_console_read(void *xinfo, int **axes, int **buttons) { struct js_console_info *info = xinfo; unsigned char data[JS_MAX_LENGTH]; - int i, s; + int i, j, s; int n = 0; /* @@ -268,24 +363,68 @@ static int js_console_read(void *xinfo, int **axes, int **buttons) for (i = 0; i < 5; i++) { s = status_bit[i]; + n = info->pad_to_device[i]; if (info->nes & s) { axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0); axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP] &s?1:0); - buttons[n][0] = ((data[JS_NES_A] &s)?1:0) | ((data[JS_NES_B] &s)?2:0) - | ((data[JS_NES_START]&s)?4:0) | ((data[JS_NES_SELECT]&s)?8:0); - - n++; + buttons[n][0] = (data[JS_NES_A] &s?1:0) | (data[JS_NES_B] &s?2:0) + | (data[JS_NES_START]&s?4:0) | (data[JS_NES_SELECT]&s?8:0); } else if (info->snes & s) { axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0); axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP] &s?1:0); - buttons[n][0] = ((data[JS_SNES_A] &s)?0x01:0) | ((data[JS_SNES_B] &s)?0x02:0) - | ((data[JS_SNES_X] &s)?0x04:0) | ((data[JS_SNES_Y] &s)?0x08:0) - | ((data[JS_SNES_L] &s)?0x10:0) | ((data[JS_SNES_R] &s)?0x20:0) - | ((data[JS_SNES_START]&s)?0x40:0) | ((data[JS_SNES_SELECT]&s)?0x80:0); - n++; + buttons[n][0] = (data[JS_SNES_A] &s?0x01:0) | (data[JS_SNES_B] &s?0x02:0) + | (data[JS_SNES_X] &s?0x04:0) | (data[JS_SNES_Y] &s?0x08:0) + | (data[JS_SNES_L] &s?0x10:0) | (data[JS_SNES_R] &s?0x20:0) + | (data[JS_SNES_START]&s?0x40:0) | (data[JS_SNES_SELECT]&s?0x80:0); + } + } + } + +/* + * N64 pads + */ + + if (info->n64) { + if ( (info->nes || info->snes) && (info->n64 & status_bit[0]) ) { + /* SNES/NES compatibility */ + udelay(240); /* 200 us delay + 20% tolerance */ + } + + js_n64_read_packet(info, data); + + for (i = 0; i < 5; i++) { + s = status_bit[i]; + n = info->pad_to_device[i]; + if (info->n64 & s & ~(data[JS_N64_UNUSED1] | data[JS_N64_UNUSED2])) { + + buttons[n][0] = ( ((data[JS_N64_A]&s) ? 0x01:0) | ((data[JS_N64_B] & s ) ? 0x02:0) + | ((data[JS_N64_Z]&s) ? 0x04:0) | ((data[JS_N64_L] & s ) ? 0x08:0) + | ((data[JS_N64_R]&s) ? 0x10:0) | ((data[JS_N64_START]&s)? 0x20:0) + | ((data[JS_N64_CU]&s)? 0x40:0) | ((data[JS_N64_CR]&s) ? 0x80:0) + | ((data[JS_N64_CD]&s)?0x100:0) | ((data[JS_N64_CL]&s) ?0x200:0) ); + + if (info->n64_dpp & s) { + buttons[n][0] |= ((data[JS_N64_LEFT]&s) ? 0x400:0) | ((data[JS_N64_UP] & s)? 0x800:0) + |((data[JS_N64_RIGHT]&s)?0x1000:0) | ((data[JS_N64_DOWN]&s)?0x2000:0); + } else { + axes[n][2] = (data[JS_N64_RIGHT]&s?1:0) - (data[JS_N64_LEFT]&s?1:0); + axes[n][3] = (data[JS_N64_DOWN] &s?1:0) - (data[JS_N64_UP] &s?1:0); + } + + /* build int from bits of signed 8-bit int's */ + j = 7; + axes[n][0] = (data[JS_N64_X - j] & s) ? ~0x7f : 0; + axes[n][1] = (data[JS_N64_Y - j] & s) ? ~0x7f : 0; + while ( j-- > 0 ) { + axes[n][0] |= (data[JS_N64_X - j] & s) ? (1 << j) : 0; + axes[n][1] |= (data[JS_N64_Y - j] & s) ? (1 << j) : 0; + } + /* flip Y-axis for conformity */ + axes[n][1] = -axes[n][1]; + } } } @@ -300,21 +439,18 @@ static int js_console_read(void *xinfo, int **axes, int **buttons) for (i = 0; i < 5; i++) { s = status_bit[i]; + n = info->pad_to_device[i]; if (info->multi & s) { axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0); axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP] &s?1:0); buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0; - - n++; } else if (info->multi2 & s) { axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0); axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP] &s?1:0); buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0 | (data[JS_MULTI_BUTTON2]&s)?2:0; - - n++; } } } @@ -323,18 +459,44 @@ static int js_console_read(void *xinfo, int **axes, int **buttons) * PSX controllers */ - if (info->psx && (js_psx_read_packet(info, 2, data) == JS_PSX_NORMAL)) { /* FIXME? >1 PSX pads? */ + if (info->psx) { + + for ( i = 0; i < 5; i++ ) + if ( info->psx & status_bit[i] ) { + n = info->pad_to_device[i]; + break; + } + + buttons[n][0] = 0; + + switch (js_psx_read_packet(info, 6, data)) { + + case JS_PSX_ANALOGR: + + buttons[n][0] |= (data[0]&JS_PSX_JOYL?0:0x800) | (data[0]&JS_PSX_JOYR?0:0x400); - axes[n][0] = (data[0]&JS_PSX_RIGHT?0:1) - (data[0]&JS_PSX_LEFT?0:1); - axes[n][1] = (data[0]&JS_PSX_DOWN ?0:1) - (data[0]&JS_PSX_UP ?0:1); + case JS_PSX_ANALOGG: + + axes[n][2] = data[2]; + axes[n][3] = data[3]; + axes[n][4] = data[4]; + axes[n][5] = data[5]; - buttons[n][0] = ((~data[1]&0xf)<<4) | ((~data[1]&0xf0)>>4) | - (data[0]&JS_PSX_START?0:0x200) | (data[0]&JS_PSX_SELBUT?0:0x100); + case JS_PSX_NORMAL: + case JS_PSX_NEGCON: - n++; + axes[n][0] = (data[0]&JS_PSX_RIGHT?0:1) - (data[0]&JS_PSX_LEFT?0:1); + axes[n][1] = (data[0]&JS_PSX_DOWN ?0:1) - (data[0]&JS_PSX_UP ?0:1); + + buttons[n][0] |= ((~data[1]&0xf)<<4) | ((~data[1]&0xf0)>>4) | + (data[0]&JS_PSX_START?0:0x200) | (data[0]&JS_PSX_SELBUT?0:0x100); + + break; + + } } - return -(n != info->pads); + return 0; } /* @@ -343,10 +505,8 @@ static int js_console_read(void *xinfo, int **axes, int **buttons) int js_console_open(struct js_dev *dev) { -#ifdef USE_PARPORT struct js_console_info *info = dev->port->info; if (!MOD_IN_USE && parport_claim(info->port)) return -EBUSY; -#endif MOD_INC_USE_COUNT; return 0; } @@ -357,13 +517,9 @@ int js_console_open(struct js_dev *dev) int js_console_close(struct js_dev *dev) { -#ifdef USE_PARPORT struct js_console_info *info = dev->port->info; -#endif MOD_DEC_USE_COUNT; -#ifdef USE_PARPORT if (!MOD_IN_USE) parport_release(info->port); -#endif return 0; } @@ -373,16 +529,12 @@ void cleanup_module(void) struct js_console_info *info; int i; - while (js_console_port != NULL) { + while (js_console_port) { for (i = 0; i < js_console_port->ndevs; i++) - if (js_console_port->devs[i] != NULL) + if (js_console_port->devs[i]) js_unregister_device(js_console_port->devs[i]); info = js_console_port->info; -#ifdef USE_PARPORT parport_unregister_device(info->port); -#else - release_region(info->port, 3); -#endif js_console_port = js_unregister_port(js_console_port); } } @@ -393,7 +545,7 @@ void cleanup_module(void) * console gamepads. */ -static void __init js_console_init_corr(int num_axes, struct js_corr *corr) +static void __init js_console_init_corr(int num_axes, int type, struct js_corr *corr) { int i; @@ -405,6 +557,28 @@ static void __init js_console_init_corr(int num_axes, struct js_corr *corr) corr[i].coef[2] = (1 << 29); corr[i].coef[3] = (1 << 29); } + + if (type == JS_N64_PAD || type == JS_N64_PAD_DPP) { + for (i = 0; i < 2; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 0; + corr[i].coef[1] = 0; + corr[i].coef[2] = (1 << 22); + corr[i].coef[3] = (1 << 22); + } + } + + if (type == JS_PSX_ANALOGG || type == JS_PSX_ANALOGR) { + for (i = 2; i < 6; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 127 - 2; + corr[i].coef[1] = 128 + 2; + corr[i].coef[2] = (1 << 29) / (127 - 4); + corr[i].coef[3] = (1 << 29) / (127 - 4); + } + } } /* @@ -415,45 +589,40 @@ static void __init js_console_init_corr(int num_axes, struct js_corr *corr) static struct js_port __init *js_console_probe(int *config, struct js_port *port) { char *name[5]; - int i, psx, axes[5], buttons[5]; + int i, psx, axes[5], buttons[5], type[5]; unsigned char data[2]; /* used for PSX probe */ struct js_console_info info; + struct parport *pp; memset(&info, 0, sizeof(struct js_console_info)); if (config[0] < 0) return port; -#ifdef USE_PARPORT - { - struct parport *pp; - - if (config[0] > 0x10) - for (pp=parport_enumerate(); pp != NULL && (pp->base!=config[0]); pp=pp->next); - else - for (pp=parport_enumerate(); pp != NULL && (config[0]>0); pp=pp->next) config[0]--; - - if (pp == NULL) { - printk(KERN_ERR "joy-console: no such parport\n"); - return port; - } + if (config[0] > 0x10) + for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next); + else + for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--; - info.port = parport_register_device(pp, "joystick (console)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); - if (!info.port) - return port; + if (!pp) { + printk(KERN_ERR "joy-console: no such parport\n"); + return port; } + info.port = parport_register_device(pp, "joystick (console)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + if (!info.port) + return port; + if (parport_claim(info.port)) { parport_unregister_device(info.port); /* port currently not available ... */ return port; } -#else - info.port = config[0]; - if (check_region(info.port, 3)) return port; - request_region(info.port, 3, "joystick (console pad)"); -#endif - for (i = 0; i < 5; i++) + for (i = 0; i < 5; i++) { + + type[info.pads] = config[i+1]; + info.pad_to_device[i] = info.pads; + switch(config[i+1]) { case JS_NO_PAD: @@ -478,6 +647,23 @@ static struct js_port __init *js_console_probe(int *config, struct js_port *port info.pads++; break; + case JS_N64_PAD: + axes[info.pads] = 4; + buttons[info.pads] = 10; + name[info.pads] = "N64 pad"; + info.n64 |= status_bit[i]; + info.pads++; + break; + + case JS_N64_PAD_DPP: + axes[info.pads] = 2; + buttons[info.pads] = 14; + name[info.pads] = "N64 pad (DPP mode)"; + info.n64 |= status_bit[i]; + info.n64_dpp |= status_bit[i]; + info.pads++; + break; + case JS_MULTI_STICK: axes[info.pads] = 2; @@ -497,31 +683,58 @@ static struct js_port __init *js_console_probe(int *config, struct js_port *port break; case JS_PSX_PAD: - + info.psx |= status_bit[i]; psx = js_psx_read_packet(&info, 2, data); psx = js_psx_read_packet(&info, 2, data); info.psx &= ~status_bit[i]; + type[i] = psx; + switch(psx) { case JS_PSX_NORMAL: axes[info.pads] = 2; buttons[info.pads] = 10; - name[info.pads] = "PSX controller"; + name[info.pads] = "PSX pad"; info.psx |= status_bit[i]; info.pads++; break; + + case JS_PSX_ANALOGR: + axes[info.pads] = 6; + buttons[info.pads] = 12; + name[info.pads] = "Analog Red PSX pad"; + info.psx |= status_bit[i]; + info.pads++; + break; + + case JS_PSX_ANALOGG: + axes[info.pads] = 6; + buttons[info.pads] = 10; + name[info.pads] = "Analog Green PSX pad"; + info.psx |= status_bit[i]; + info.pads++; + break; + case JS_PSX_NEGCON: - printk(KERN_WARNING "joy-console: NegCon not yet supported...\n"); + axes[info.pads] = 2; + buttons[info.pads] = 10; + name[info.pads] = "NegCon PSX pad"; + info.psx |= status_bit[i]; + info.pads++; break; + case JS_PSX_MOUSE: - printk(KERN_WARNING "joy-console: PSX mouse not supported...\n"); + printk(KERN_WARNING "joy-psx: PSX mouse not supported...\n"); break; + case -1: - printk(KERN_ERR "joy-console: no PSX controller found...\n"); + printk(KERN_ERR "joy-psx: no PSX controller found...\n"); break; + default: - printk(KERN_WARNING "joy-console: unknown PSX controller 0x%x\n", psx); + printk(KERN_WARNING "joy-psx: PSX controller unknown: 0x%x," + " please report to <vojtech@suse.cz>.\n", psx); } break; @@ -529,52 +742,53 @@ static struct js_port __init *js_console_probe(int *config, struct js_port *port printk(KERN_WARNING "joy-console: pad type %d unknown\n", config[i+1]); } + } if (!info.pads) { -#ifdef USE_PARPORT parport_release(info.port); parport_unregister_device(info.port); -#else - release_region(info.port, 3); -#endif return port; } port = js_register_port(port, &info, info.pads, sizeof(struct js_console_info), js_console_read); for (i = 0; i < info.pads; i++) { -#ifdef USE_PARPORT printk(KERN_INFO "js%d: %s on %s\n", js_register_device(port, i, axes[i], buttons[i], name[i], js_console_open, js_console_close), name[i], info.port->port->name); -#else - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, i, axes[i], buttons[i], name[i], js_console_open, js_console_close), - name[i], info.port); -#endif - js_console_init_corr(axes[i], port->corr[i]); + js_console_init_corr(axes[i], type[i], port->corr[i]); } -#ifdef USE_PARPORT parport_release(info.port); -#endif return port; } #ifndef MODULE -void __init js_console_setup(char *str, int *ints) +int __init js_console_setup(SETUP_PARAM) { int i; - - if (!strcmp(str,"js_console")) - for (i = 0; i <= ints[0] && i < 6; i++) js_console[i] = ints[i+1]; - if (!strcmp(str,"js_console2")) - for (i = 0; i <= ints[0] && i < 6; i++) js_console2[i] = ints[i+1]; - if (!strcmp(str,"js_console3")) - for (i = 0; i <= ints[0] && i < 6; i++) js_console3[i] = ints[i+1]; - + SETUP_PARSE(6); + for (i = 0; i <= ints[0] && i < 6; i++) js_console[i] = ints[i+1]; + return 1; +} +int __init js_console_setup_2(SETUP_PARAM) +{ + int i; + SETUP_PARSE(6); + for (i = 0; i <= ints[0] && i < 6; i++) js_console_2[i] = ints[i+1]; + return 1; +} +int __init js_console_setup_3(SETUP_PARAM) +{ + int i; + SETUP_PARSE(6); + for (i = 0; i <= ints[0] && i < 6; i++) js_console_3[i] = ints[i+1]; + return 1; } +__setup("js_console=", js_console_setup); +__setup("js_console_2=", js_console_setup_2); +__setup("js_console_3=", js_console_setup_3); #endif #ifdef MODULE @@ -584,8 +798,8 @@ int __init js_console_init(void) #endif { js_console_port = js_console_probe(js_console, js_console_port); - js_console_port = js_console_probe(js_console2, js_console_port); - js_console_port = js_console_probe(js_console3, js_console_port); + js_console_port = js_console_probe(js_console_2, js_console_port); + js_console_port = js_console_probe(js_console_3, js_console_port); if (js_console_port) return 0; diff --git a/drivers/char/joystick/joy-creative.c b/drivers/char/joystick/joy-creative.c new file mode 100644 index 000000000..7e97a677d --- /dev/null +++ b/drivers/char/joystick/joy-creative.c @@ -0,0 +1,287 @@ +/* + * joy-creative.c Version 1.2 + * + * Copyright (c) 1999 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This is a module for the Linux joystick driver, supporting + * Creative Labs Blaster gamepad family. + */ + +/* + * 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@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <asm/system.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/init.h> + +#define JS_CR_MAX_STROBE 100 /* 100 us max wait for first strobe */ +#define JS_CR_LENGTH 36 + +#define JS_CR_MODE_BGPC 8 + +static int js_cr_port_list[] __initdata = {0x201, 0}; +static struct js_port* js_cr_port __initdata = NULL; + +struct js_cr_info { + int io; + unsigned char mode[2]; +}; + +/* + * js_cr_read_packet() reads a Blaster gamepad packet. + */ + +static int js_cr_read_packet(int io, unsigned int *data) +{ + unsigned long flags; + unsigned char u, v, w; + __u64 buf[2]; + int r[2], t[2], p[2]; + int i, j, ret; + + for (i = 0; i < 2; i++); { + r[i] = buf[i] = 0; + p[i] = t[i] = JS_CR_MAX_STROBE; + p[i] += JS_CR_MAX_STROBE; + } + + __save_flags(flags); + __cli(); + + u = inb(io); + + do { + t[0]--; t[1]--; + v = inb(io); + for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2) + if (w & 0x30) { + if ((w & 0x30) < 0x30 && r[i] < JS_CR_LENGTH && t[i] > 0) { + buf[i] |= (__u64)((w >> 5) & 1) << r[i]++; + p[i] = t[i] = (p[i] - t[i]) << 1; + u = v; + } else t[i] = 0; + } + } while (t[0] > 0 || t[1] > 0); + + __restore_flags(flags); + + + ret = 0; + + for (i = 0; i < 2; i++) { + + if (r[i] != JS_CR_LENGTH) continue; + + for (j = 0; j < JS_CR_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++) + buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (JS_CR_LENGTH - 1)); + + if (j < JS_CR_LENGTH) ret |= (1 << i); + + data[i] = ((buf[i] >> 7) & 0x000001f) | ((buf[i] >> 8) & 0x00003e0) + | ((buf[i] >> 9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000) + | ((buf[i] >> 11) & 0x1f00000); + + } + + return ret; +} + +/* + * js_cr_read() reads and analyzes Blaster gamepad data. + */ + +static int js_cr_read(void *xinfo, int **axes, int **buttons) +{ + struct js_cr_info *info = xinfo; + unsigned int data[2]; + int i, r; + + if (!(r = js_cr_read_packet(info->io, data))) + return -1; + + for (i = 0; i < 2; i++) + if (r & (1 << i)) { + switch (info->mode[i]) { + + case JS_CR_MODE_BGPC: + + axes[i][0] = ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1); + axes[i][1] = ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1); + + buttons[i][0] = ((data[i] >> 12) & 0x007) | ((data[i] >> 6) & 0x038) + | ((data[i] >> 1) & 0x0c0) | ((data[i] >> 7) & 0x300) + | ((data[i] << 5) & 0xc00); + + break; + + default: + break; + + } + } + + return 0; +} + +/* + * js_cr_open() is a callback from the file open routine. + */ + +static int js_cr_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_cr_close() is a callback from the file release routine. + */ + +static int js_cr_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_cr_init_corr() initializes correction values of + * Blaster gamepads. + */ + +static void __init js_cr_init_corr(int mode, struct js_corr *corr) +{ + int i; + + switch (mode) { + + case JS_CR_MODE_BGPC: + + for (i = 0; i < 2; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 0; + corr[i].coef[1] = 0; + corr[i].coef[2] = (1 << 29); + corr[i].coef[3] = (1 << 29); + } + + break; + + } +} + +/* + * js_cr_probe() probes for Blaster gamepads. + */ + +static struct js_port __init *js_cr_probe(int io, struct js_port *port) +{ + struct js_cr_info info; + char *names[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Blaster GamePad Cobra" }; + char axes[] = { 0, 0, 0, 0, 0, 0, 0, 0, 2 }; + char buttons[] = { 0, 0, 0, 0, 0, 0, 0, 0, 12 }; + unsigned int data[2]; + int i, r; + + if (check_region(io, 1)) return port; + + info.mode[0] = info.mode[1] = 0; + + if (!(r = js_cr_read_packet(io, data))) + return port; + + for (i = 0; i < 2; i++) { + if (r & (1 << i)) { + if (~data[i] & 1) { + info.mode[i] = JS_CR_MODE_BGPC; + } else { + info.mode[i] = (data[i] >> 2) & 7; + } + if (!names[info.mode[i]]) { + printk(KERN_WARNING "joy-creative: Unknown Creative device %d at %#x\n", + info.mode[i], io); + info.mode[i] = 0; + } + } + } + + if (!info.mode[0] && !info.mode[1]) return port; + + info.io = io; + + request_region(io, 1, "joystick (creative)"); + port = js_register_port(port, &info, 2, sizeof(struct js_cr_info), js_cr_read); + + for (i = 0; i < 2; i++) + if (info.mode[i]) { + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, i, axes[info.mode[i]], buttons[info.mode[i]], + names[info.mode[i]], js_cr_open, js_cr_close), + names[info.mode[i]], io); + js_cr_init_corr(info.mode[i], port->corr[i]); + } + + return port; +} + +#ifdef MODULE +int init_module(void) +#else +int __init js_cr_init(void) +#endif +{ + int *p; + + for (p = js_cr_port_list; *p; p++) js_cr_port = js_cr_probe(*p, js_cr_port); + if (js_cr_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-creative: no joysticks found\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i; + struct js_cr_info *info; + + while (js_cr_port) { + for (i = 0; i < js_cr_port->ndevs; i++) + if (js_cr_port->devs[i]) + js_unregister_device(js_cr_port->devs[i]); + info = js_cr_port->info; + release_region(info->io, 1); + js_cr_port = js_unregister_port(js_cr_port); + } +} +#endif diff --git a/drivers/char/joystick/joy-db9.c b/drivers/char/joystick/joy-db9.c index b459b91f5..d5b6565ee 100644 --- a/drivers/char/joystick/joy-db9.c +++ b/drivers/char/joystick/joy-db9.c @@ -1,7 +1,10 @@ /* - * joy-db9.c Version 0.5V + * joy-db9.c Version 0.6V * - * Copyright (c) 1998 Andree Borrmann + * Copyright (c) 1998 Andree Borrmann + * Copyright (c) 1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -24,6 +27,10 @@ * 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@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ #include <asm/io.h> @@ -35,8 +42,9 @@ #include <linux/module.h> #include <linux/string.h> #include <linux/delay.h> +#include <linux/init.h> -MODULE_AUTHOR("Andree Borrmann <A.Borrmann@tu-bs.de>"); +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_PARM(js_db9, "2i"); MODULE_PARM(js_db9_2, "2i"); MODULE_PARM(js_db9_3, "2i"); @@ -48,7 +56,8 @@ MODULE_PARM(js_db9_3, "2i"); #define JS_GENESIS6_PAD 0x06 #define JS_SATURN_PAD 0x07 #define JS_MULTI_0802 0x08 -#define JS_MAX_PAD 0x09 +#define JS_MULTI_0802_2 0x09 +#define JS_MAX_PAD 0x0A #define JS_DB9_UP 0x01 #define JS_DB9_DOWN 0x02 @@ -59,8 +68,8 @@ MODULE_PARM(js_db9_3, "2i"); #define JS_DB9_FIRE3 0x40 #define JS_DB9_FIRE4 0x80 -#define JS_DB9_NORMAL 0x22 -#define JS_DB9_NOSELECT 0x20 +#define JS_DB9_NORMAL 0x2a +#define JS_DB9_NOSELECT 0x28 #define JS_DB9_SATURN0 0x20 #define JS_DB9_SATURN1 0x22 @@ -76,11 +85,7 @@ static int js_db9_2[] __initdata = { -1, 0 }; static int js_db9_3[] __initdata = { -1, 0 }; struct js_db9_info { -#ifdef USE_PARPORT struct pardevice *port; /* parport device */ -#else - int port; /* hw port */ -#endif int mode; /* pad mode */ }; @@ -95,6 +100,15 @@ static int js_db9_read(void *xinfo, int **axes, int **buttons) switch(info->mode) { + case JS_MULTI_0802_2: + + data = JS_PAR_DATA_IN(info->port) >> 3; + + axes[1][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); + axes[1][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); + + buttons[1][0] = (data&JS_DB9_FIRE1?0:1); + case JS_MULTI_0802: data = JS_PAR_STATUS(info->port) >> 3; @@ -185,11 +199,12 @@ static int js_db9_read(void *xinfo, int **axes, int **buttons) udelay(JS_GENESIS6_DELAY); JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 3 */ udelay(JS_GENESIS6_DELAY); - JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); data=JS_PAR_DATA_IN(info->port); - buttons[0][0] |= (data&JS_DB9_LEFT?0:0x10) | (data&JS_DB9_DOWN?0:0x20) | (data&JS_DB9_UP?0:0x40); + buttons[0][0] |= (data&JS_DB9_LEFT?0:0x10) | (data&JS_DB9_DOWN ?0:0x20) | + (data&JS_DB9_UP ?0:0x40) | (data&JS_DB9_RIGHT?0:0x80); + JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); udelay(JS_GENESIS6_DELAY); JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 4 */ udelay(JS_GENESIS6_DELAY); @@ -236,11 +251,11 @@ int js_db9_open(struct js_dev *dev) struct js_db9_info *info = dev->port->info; if (!MOD_IN_USE) { -#ifdef USE_PARPORT if (parport_claim(info->port)) return -EBUSY; -#endif - JS_PAR_ECTRL_OUT(0x35,info->port); /* enable PS/2 mode: */ + JS_PAR_DATA_OUT(0xff, info->port); + if (info->mode != JS_MULTI_0802) + JS_PAR_ECTRL_OUT(0x35,info->port); /* enable PS/2 mode: */ JS_PAR_CTRL_OUT(JS_DB9_NORMAL,info->port); /* reverse direction, enable Select signal */ } @@ -260,12 +275,11 @@ int js_db9_close(struct js_dev *dev) if (!MOD_IN_USE) { - JS_PAR_CTRL_OUT(0x00,info->port); /* normal direction */ - JS_PAR_ECTRL_OUT(0x15,info->port); /* enable normal mode */ + JS_PAR_CTRL_OUT(0x00,info->port); /* normal direction */ + if (info->mode != JS_MULTI_0802) + JS_PAR_ECTRL_OUT(0x15,info->port); /* enable normal mode */ -#ifdef USE_PARPORT parport_release(info->port); -#endif } return 0; } @@ -274,16 +288,15 @@ int js_db9_close(struct js_dev *dev) void cleanup_module(void) { struct js_db9_info *info; + int i; - while (js_db9_port != NULL) { - js_unregister_device(js_db9_port->devs[0]); + while (js_db9_port) { info = js_db9_port->info; -#ifdef USE_PARPORT + + for (i = 0; i < js_db9_port->ndevs; i++) + if (js_db9_port->devs[i]) + js_unregister_device(js_db9_port->devs[i]); parport_unregister_device(info->port); -#else - release_region(info->port, 3); - release_region(info->port+0x402, 1); -#endif js_db9_port = js_unregister_port(js_db9_port); } @@ -295,17 +308,17 @@ void cleanup_module(void) * db9 gamepads. */ -static void __init js_db9_init_corr(struct js_corr **corr) +static void __init js_db9_init_corr(struct js_corr *corr) { int i; for (i = 0; i < 2; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = 0; - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (1 << 29); - corr[0][i].coef[3] = (1 << 29); + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 0; + corr[i].coef[1] = 0; + corr[i].coef[2] = (1 << 29); + corr[i].coef[3] = (1 << 29); } } @@ -316,75 +329,76 @@ static void __init js_db9_init_corr(struct js_corr **corr) static struct js_port __init *js_db9_probe(int *config, struct js_port *port) { struct js_db9_info info; - char buttons[JS_MAX_PAD] = {0,1,2,4,0,6,7,8,1}; + struct parport *pp; + int i; + char buttons[JS_MAX_PAD] = {0,1,2,4,0,6,8,8,1,1}; char *name[JS_MAX_PAD] = {NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad", - NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick"}; + NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick", + "Multisystem (0.8.0.2-dual) joystick"}; if (config[0] < 0) return port; if (config[1] < 0 || config[1] >= JS_MAX_PAD || !name[config[1]]) return port; -#ifdef USE_PARPORT - { - struct parport *pp; - - if (config[0] > 0x10) - for (pp=parport_enumerate(); pp != NULL && (pp->base!=config[0]); pp=pp->next); - else - for (pp=parport_enumerate(); pp != NULL && (config[0]>0); pp=pp->next) config[0]--; - - if (pp == NULL) { - printk(KERN_ERR "joy-db9: no such parport\n"); - return port; - } - - if (!(pp->modes & PARPORT_MODE_TRISTATE)) { - printk(KERN_ERR "js-db9: specified parport is not bidirectional\n"); - return port; - } - - info.port = parport_register_device(pp, "joystick (db9)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); - if (!info.port) - return port; + info.mode = config[1]; + + if (config[0] > 0x10) + for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next); + else + for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--; + + if (!pp) { + printk(KERN_ERR "joy-db9: no such parport\n"); + return port; } -#else - info.port = config[0]; - if (check_region(info.port, 3) || check_region(info.port+0x402,1)) return port; - request_region(info.port, 3, "joystick (db9)"); - request_region(info.port+0x402, 1, "joystick (db9)"); -#endif - info.mode = config[1]; + if (!(pp->modes & (PARPORT_MODE_PCPS2 | PARPORT_MODE_PCECPPS2)) && info.mode != JS_MULTI_0802) { + printk(KERN_ERR "js-db9: specified parport is not bidirectional\n"); + return port; + } - port = js_register_port(port, &info, 1, sizeof(struct js_db9_info), js_db9_read); + info.port = parport_register_device(pp, "joystick (db9)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + if (!info.port) + return port; -#ifdef USE_PARPORT - printk(KERN_INFO "js%d: %s on %s\n", - js_register_device(port, 0, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close), - name[info.mode], info.port->port->name); -#else - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, 0, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close), - name[info.mode], info.port); -#endif + port = js_register_port(port, &info, 1 + (info.mode == JS_MULTI_0802_2), sizeof(struct js_db9_info), js_db9_read); + + for (i = 0; i < 1 + (info.mode == JS_MULTI_0802_2); i++) { + printk(KERN_INFO "js%d: %s on %s\n", + js_register_device(port, i, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close), + name[info.mode], info.port->port->name); + + js_db9_init_corr(port->corr[i]); + } - js_db9_init_corr(port->corr); return port; } #ifndef MODULE -void __init js_db9_setup(char *str, int *ints) +int __init js_db9_setup(SETUP_PARAM) { int i; - - if (!strcmp(str,"js_db9")) - for (i = 0; i <= ints[0] && i < 2; i++) js_db9[i] = ints[i+1]; - if (!strcmp(str,"js_db9_2")) - for (i = 0; i <= ints[0] && i < 2; i++) js_db9_2[i] = ints[i+1]; - if (!strcmp(str,"js_db9_3")) - for (i = 0; i <= ints[0] && i < 2; i++) js_db9_3[i] = ints[i+1]; - + SETUP_PARSE(2); + for (i = 0; i <= ints[0] && i < 2; i++) js_db9[i] = ints[i+1]; + return 1; +} +int __init js_db9_setup_2(SETUP_PARAM) +{ + int i; + SETUP_PARSE(2); + for (i = 0; i <= ints[0] && i < 2; i++) js_db9_2[i] = ints[i+1]; + return 1; +} +int __init js_db9_setup_3(SETUP_PARAM) +{ + int i; + SETUP_PARSE(2); + for (i = 0; i <= ints[0] && i < 2; i++) js_db9_3[i] = ints[i+1]; + return 1; } +__setup("js_db9=", js_db9_setup); +__setup("js_db9_2=", js_db9_setup_2); +__setup("js_db9_3=", js_db9_setup_3); #endif #ifdef MODULE diff --git a/drivers/char/joystick/joy-gravis.c b/drivers/char/joystick/joy-gravis.c index 7416527e4..3863d161f 100644 --- a/drivers/char/joystick/joy-gravis.c +++ b/drivers/char/joystick/joy-gravis.c @@ -1,7 +1,9 @@ /* * joy-gravis.c Version 1.2 * - * Copyright (c) 1998 Vojtech Pavlik + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -37,15 +39,16 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/string.h> +#include <linux/init.h> #define JS_GR_MODE_GPP 1 #define JS_GR_LENGTH_GPP 24 -#define JS_GR_STROBE_GPP 75 +#define JS_GR_STROBE_GPP 400 #define JS_GR_MODE_XT 2 #define JS_GR_MODE_BD 3 #define JS_GR_LENGTH_XT 4 -#define JS_GR_STROBE_XT 30 +#define JS_GR_STROBE_XT 200 #define JS_GR_MAX_CHUNKS_XT 10 #define JS_GR_MAX_BITS_XT 30 @@ -63,37 +66,36 @@ struct js_gr_info { static int js_gr_gpp_read_packet(int io, int shift, unsigned int *data) { - unsigned int t, t1; + unsigned long flags; unsigned char u, v; + unsigned int t, p; int i; - unsigned long flags; - - int strobe = (js_time_speed * JS_GR_STROBE_GPP) >> 10; i = 0; data[0] = 0; + p = t = JS_GR_STROBE_GPP; + p += JS_GR_STROBE_GPP; __save_flags(flags); __cli(); - u = inb(io) >> shift; - t = js_get_time(); + + v = inb(io) >> shift; do { - v = (inb(io) >> shift) & 3; - t1 = js_get_time(); - if ((u ^ v) & u & 1) { + t--; + u = v; v = (inb(io) >> shift) & 3; + if (~v & u & 1) { data[0] |= (v >> 1) << i++; - t = t1; + p = t = (p - t) << 1; } - u = v; - } while (i < JS_GR_LENGTH_GPP && js_delta(t1,t) < strobe); + } while (i < JS_GR_LENGTH_GPP && t > 0); __restore_flags(flags); if (i < JS_GR_LENGTH_GPP) return -1; for (i = 0; i < JS_GR_LENGTH_GPP && (data[0] & 0xfe4210) ^ 0x7c0000; i++) - data[0] = data[0] >> 1 | (data[0] & 1) << 23; + data[0] = data[0] >> 1 | (data[0] & 1) << (JS_GR_LENGTH_GPP - 1); return -(i == JS_GR_LENGTH_GPP); } @@ -104,35 +106,36 @@ static int js_gr_gpp_read_packet(int io, int shift, unsigned int *data) static int js_gr_xt_read_packet(int io, int shift, unsigned int *data) { - unsigned int t, t1; - unsigned char u, v, w; unsigned int i, j, buf, crc; + unsigned char u, v, w; unsigned long flags; + unsigned int t, p; char status; - int strobe = (js_time_speed * JS_GR_STROBE_XT) >> 10; - data[0] = data[1] = data[2] = data[3] = 0; status = buf = i = j = 0; + p = t = JS_GR_STROBE_XT; + p += JS_GR_STROBE_XT; __save_flags(flags); __cli(); v = w = (inb(io) >> shift) & 3; - t = js_get_time(); do { + t--; u = (inb(io) >> shift) & 3; - t1 = js_get_time(); if (u ^ v) { if ((u ^ v) & 1) { + p = t = (p - t) << 2; buf = (buf << 1) | (u >> 1); i++; } else if ((((u ^ v) & (v ^ w)) >> 1) & ~(u | v | w) & 1) { + p = t = (p - t) << 2; if (i == 20) { crc = buf ^ (buf >> 7) ^ (buf >> 14); if (!((crc ^ (0x25cb9e70 >> ((crc >> 2) & 0x1c))) & 0xf)) { @@ -145,12 +148,11 @@ static int js_gr_xt_read_packet(int io, int shift, unsigned int *data) i = 0; } - t = t1; w = v; v = u; } - } while (status != 0xf && i < JS_GR_MAX_BITS_XT && j < JS_GR_MAX_CHUNKS_XT && js_delta(t1,t) < strobe); + } while (status != 0xf && i < JS_GR_MAX_BITS_XT && j < JS_GR_MAX_CHUNKS_XT && t > 0); __restore_flags(flags); @@ -320,7 +322,7 @@ static void __init js_gr_init_corr(int mode, struct js_corr *corr) } /* - * js_gr_probe() probes fro GrIP joysticks. + * js_gr_probe() probes for GrIP joysticks. */ static struct js_port __init *js_gr_probe(int io, struct js_port *port) @@ -389,9 +391,9 @@ void cleanup_module(void) int i; struct js_gr_info *info; - while (js_gr_port != NULL) { + while (js_gr_port) { for (i = 0; i < js_gr_port->ndevs; i++) - if (js_gr_port->devs[i] != NULL) + if (js_gr_port->devs[i]) js_unregister_device(js_gr_port->devs[i]); info = js_gr_port->info; release_region(info->io, 1); diff --git a/drivers/char/joystick/joy-lightning.c b/drivers/char/joystick/joy-lightning.c index 3b337fd9f..038d33a3c 100644 --- a/drivers/char/joystick/joy-lightning.c +++ b/drivers/char/joystick/joy-lightning.c @@ -1,7 +1,9 @@ /* * joy-lightning.c Version 1.2 * - * Copyright (c) 1998 Vojtech Pavlik + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -38,6 +40,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/string.h> +#include <linux/init.h> #define JS_L4_PORT 0x201 #define JS_L4_SELECT_ANALOG 0xa4 @@ -55,7 +58,7 @@ static struct js_port* __initdata js_l4_port = NULL; MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_PARM(js_l4, "2-24i"); -static int js_l4[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0}; +static int __initdata js_l4[] = { -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0 }; #include "joy-analog.h" @@ -70,11 +73,10 @@ struct js_l4_info { static int js_l4_wait_ready(void) { - unsigned int t, t1, timeout; - timeout = (JS_L4_TIMEOUT * js_time_speed) >> 10; - t = t1 = js_get_time(); - while ((inb(JS_L4_PORT) & JS_L4_BUSY) && (js_delta(t1 = js_get_time(), t) < timeout)); - return -(js_delta(t1, t) >= timeout); + unsigned int t; + t = JS_L4_TIMEOUT; + while ((inb(JS_L4_PORT) & JS_L4_BUSY) && t > 0) t--; + return -(t<=0); } /* @@ -255,14 +257,14 @@ static struct js_port __init *js_l4_probe(unsigned char *cards, int l4port, int port = js_register_port(port, info, numdev, sizeof(struct js_l4_info), js_l4_read); + info = port->info; + for (i = 0; i < numdev; i++) printk(KERN_INFO "js%d: %s on L4 port %d\n", js_register_device(port, i, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), js_an_name(i, &info->an), js_l4_open, js_l4_close), js_an_name(i, &info->an), info->port); - info = port->info; - js_l4_calibrate(info); js_l4_read(info, port->axes, port->buttons); js_an_init_corr(&info->an, port->axes, port->corr, 0); @@ -300,18 +302,21 @@ static void __init js_l4_card_probe(unsigned char *cards) cards[i] = rev; - printk(KERN_INFO "js: PDPI Lightning 4 %s card (ports %d-%d) firmware v%d.%d found at %#x\n", + printk(KERN_INFO "js: PDPI Lightning 4 %s card (ports %d-%d) firmware v%d.%d at %#x\n", i ? "secondary" : "primary", (i << 2), (i << 2) + 3, rev >> 4, rev & 0xf, JS_L4_PORT); } } #ifndef MODULE -void __init js_l4_setup(char *str, int *ints) +int __init js_l4_setup(SETUP_PARAM) { int i; + SETUP_PARSE(24); for (i = 0; i <= ints[0] && i < 24; i++) js_l4[i] = ints[i+1]; + return 1; } +__setup("js_l4=", js_l4_setup); #endif #ifdef MODULE @@ -333,7 +338,7 @@ int __init js_l4_init(void) js_l4_port = js_l4_probe(cards, i, 0, 0, js_l4_port); } - if (js_l4_port == NULL) { + if (!js_l4_port) { #ifdef MODULE printk(KERN_WARNING "joy-lightning: no joysticks found\n"); #endif @@ -352,9 +357,9 @@ void cleanup_module(void) int cal[4] = {59, 59, 59, 59}; struct js_l4_info *info; - while (js_l4_port != NULL) { + while (js_l4_port) { for (i = 0; i < js_l4_port->ndevs; i++) - if (js_l4_port->devs[i] != NULL) + if (js_l4_port->devs[i]) js_unregister_device(js_l4_port->devs[i]); info = js_l4_port->info; js_l4_setcal(info->port, cal); diff --git a/drivers/char/joystick/joy-logitech.c b/drivers/char/joystick/joy-logitech.c index 064ecade4..daea89c8b 100644 --- a/drivers/char/joystick/joy-logitech.c +++ b/drivers/char/joystick/joy-logitech.c @@ -1,12 +1,14 @@ /* * joy-logitech.c Version 1.2 * - * Copyright (c) 1998 Vojtech Pavlik + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* * This is a module for the Linux joystick driver, supporting - * Logitech Digital joystick family. + * Logitech ADI joystick family. */ /* @@ -38,113 +40,175 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/string.h> +#include <linux/malloc.h> +#include <linux/init.h> + +/* + * Times array sizes, flags, ids. + */ + +#undef JS_LT_DEBUG + +#define JS_LT_MAX_START 400 /* Trigger to packet timeout [400us] */ -#define JS_LT_MAX_START 250 -#define JS_LT_MAX_STROBE 25 -#define JS_LT_MAX_LENGTH 72 +#define JS_LT_MAX_LENGTH 256 +#define JS_LT_MIN_LENGTH 8 +#define JS_LT_MIN_LEN_LENGTH 10 +#define JS_LT_MIN_ID_LENGTH 66 +#define JS_LT_MAX_NAME_LENGTH 16 -#define JS_LT_MAX_DELAY 12000 +#define JS_LT_INIT_DELAY 10 /* Delay after init packet [10ms] */ +#define JS_LT_DATA_DELAY 4 /* Delay after data packet [4ms] */ -#define JS_LT_MODE_WMED 1 -#define JS_LT_MODE_CM2 2 -#define JS_LT_MODE_TPD 3 +#define JS_LT_FLAG_HAT 0x04 +#define JS_LT_FLAG_10BIT 0x08 -static int js_lt_seq_init[] __initdata = { 6000, 11000, 7000, 9000, 6000, 11000, 7000, 9000, 0 }; -static int js_lt_seq_reset[] __initdata = { 2000, 3000, 2000, 3000, 0 }; +#define JS_LT_ID_WMED 0x00 +#define JS_LT_ID_TPD 0x01 +#define JS_LT_ID_WMI 0x04 +#define JS_LT_ID_WGP 0x06 +#define JS_LT_ID_WM3D 0x07 +#define JS_LT_ID_WGPE 0x08 -static int js_lt_port_list[] __initdata = {0x201, 0}; +#define JS_LT_BUG_BUTTONS 0x01 +#define JS_LT_BUG_LONGID 0x02 +#define JS_LT_BUG_LONGDATA 0x04 +#define JS_LT_BUG_IGNTRIG 0x08 + +/* + * Port probing variables. + */ + +static int js_lt_port_list[] __initdata = { 0x201, 0 }; static struct js_port* js_lt_port __initdata = NULL; +/* + * Device names. + */ + +#define JS_LT_MAX_ID 10 + +static char *js_lt_names[] = { "WingMan Extreme Digital", "ThunderPad Digital", "SideCar", "CyberMan 2", + "WingMan Interceptor", "WingMan Formula", "WingMan GamePad", + "WingMan Extreme Digital 3D", "WingMan GamePad Extreme", + "WingMan GamePad USB", "Unknown Device %#x"}; + +/* + * Hat to axis conversion arrays. + */ + static struct { int x; int y; } js_lt_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; +/* + * Per-port information. + */ + struct js_lt_info { - int io; - unsigned char mode; + int io; + int length[2]; + int ret[2]; + int idx[2]; + unsigned char id[2]; + char buttons[2]; + char axes10[2]; + char axes8[2]; + char pad[2]; + char hats[2]; + char name[2][JS_LT_MAX_NAME_LENGTH]; + unsigned char data[2][JS_LT_MAX_LENGTH]; + char bugs[2]; }; /* - * js_lt_read_packet() reads a Logitech packet. + * js_lt_read_packet() reads a Logitech ADI packet. */ -static int js_lt_read_packet(int io, __u64 *data) +static void js_lt_read_packet(struct js_lt_info *info) { - - static unsigned char buf[JS_LT_MAX_LENGTH]; - unsigned char u, v, w, mask = 0; - int i; + unsigned char u, v, w, x, z; + int t[2], s[2], p[2], i; unsigned long flags; - unsigned int t, t1; - - int start = (js_time_speed * JS_LT_MAX_START) >> 10; - int strobe = (js_time_speed * JS_LT_MAX_STROBE) >> 10; - u = inb(io) >> 4; - - if (u == 0xc) mask = 0x10; - if (u == 0x0) mask = 0x50; - if (!mask) return 0; - - i = 0; + for (i = 0; i < 2; i++) { + info->ret[i] = -1; + p[i] = t[i] = JS_LT_MAX_START; + s[i] = 0; + } __save_flags(flags); __cli(); - outb(0xff,io); + outb(0xff, info->io); + v = z = inb(info->io); - u = inb(io); - t = js_get_time(); + do { + u = v; + w = u ^ (v = x = inb(info->io)); + for (i = 0; i < 2; i++, w >>= 2, x >>= 2) { + t[i]--; + if ((w & 0x30) && s[i]) { + if ((w & 0x30) < 0x30 && info->ret[i] < JS_LT_MAX_LENGTH && t[i] > 0) { + info->data[i][++info->ret[i]] = w; + p[i] = t[i] = (p[i] - t[i]) << 1; + } else t[i] = 0; + } else if (!(x & 0x30)) s[i] = 1; + } + } while (t[0] > 0 || t[1] > 0); - if ((u & 0xc) != 0xc) mask = 0x10; + __restore_flags(flags); - do { - u = inb(io); - t1 = js_get_time(); - } while ((((u >> 1) ^ u) & mask) != mask && js_delta(t1,t) < start); + for (i = 0; i < 2; i++, z >>= 2) + if ((z & 0x30) && info->ret[i] > 0) + info->bugs[i] |= JS_LT_BUG_BUTTONS; + +#ifdef JS_LT_DEBUG + printk(KERN_DEBUG "joy-logitech: read %d %d bits\n", info->ret[0], info->ret[1]); + printk(KERN_DEBUG "joy-logitech: stream0:"); + for (i = 0; i <= info->ret[0]; i++) printk("%d", (info->data[0][i] >> 5) & 1); + printk("\n"); + printk(KERN_DEBUG "joy-logitech: stream1:"); + for (i = 0; i <= info->ret[1]; i++) printk("%d", (info->data[1][i] >> 5) & 1); + printk("\n"); +#endif - t = t1; + return; +} - do { - v = inb(io); - t1 = js_get_time(); - w = u ^ v; - if ((((w >> 1) ^ w) & mask) == mask) { - buf[i++] = w; - t = t1; - u = v; - } - } while (i < JS_LT_MAX_LENGTH && js_delta(t1,t) < strobe); +/* + * js_lt_move_bits() detects a possible 2-stream mode, and moves + * the bits accordingly. + */ - __restore_flags(flags); +static void js_lt_move_bits(struct js_lt_info *info, int length) +{ + int i; - t = i; - *data = 0; + info->idx[0] = info->idx[1] = 0; - if (mask == 0x10) { - for (i = 0; i < t; i++) - *data = ((buf[i] >> 5) & 1) | (*data << 1); - return t; - } - if (mask == 0x50) { - for (i = 0; i < t; i++) - *data = ((__u64)(buf[i] & 0x20) << (t - 5)) | (buf[i] >> 7) | (*data << 1); - return t << 1; - } - return 0; + if (info->ret[0] <= 0 || info->ret[1] <= 0) return; + if (info->data[0][0] & 0x20 || ~info->data[1][0] & 0x20) return; + + for (i = 1; i <= info->ret[1]; i++) + info->data[0][((length - 1) >> 1) + i + 1] = info->data[1][i]; + + info->ret[0] += info->ret[1]; + info->ret[1] = -1; } /* - * js_lt_reverse() reverses the order of bits in a byte. + * js_lt_get_bits() gathers bits from the data packet. */ -static unsigned char js_lt_reverse(unsigned char u) +static inline int js_lt_get_bits(struct js_lt_info *info, int device, int count) { - u = ((u & 0x0f) << 4) | ((u >> 4) & 0x0f); - u = ((u & 0x33) << 2) | ((u >> 2) & 0x33); - u = ((u & 0x55) << 1) | ((u >> 1) & 0x55); - return u; + int bits = 0; + int i; + if ((info->idx[device] += count) > info->ret[device]) return 0; + for (i = 0; i < count; i++) bits |= ((info->data[device][info->idx[device] - i] >> 5) & 1) << i; + return bits; } /* @@ -154,54 +218,61 @@ static unsigned char js_lt_reverse(unsigned char u) static int js_lt_read(void *xinfo, int **axes, int **buttons) { struct js_lt_info *info = xinfo; - __u64 data; - int hat; - - switch (info->mode) { - - case JS_LT_MODE_TPD: + int i, j, k, l, t; + int ret = 0; - if (js_lt_read_packet(info->io, &data) != 20) return -1; + js_lt_read_packet(info); + js_lt_move_bits(info, info->length[0]); - axes[0][0] = ((data >> 6) & 1) - ((data >> 4) & 1); - axes[0][1] = ((data >> 5) & 1) - ((data >> 7) & 1); + for (i = 0; i < 2; i++) { - buttons[0][0] = js_lt_reverse((data & 0x0f) | ((data >> 4) & 0xf0)); - - return 0; - - case JS_LT_MODE_WMED: - - if (js_lt_read_packet(info->io, &data) != 42) return -1; - if ((hat = data & 0xf) > 8) return -1; - - axes[0][0] = (data >> 26) & 0xff; - axes[0][1] = (data >> 18) & 0xff; - axes[0][2] = (data >> 10) & 0xff; - axes[0][3] = js_lt_hat_to_axis[hat].x; - axes[0][4] = js_lt_hat_to_axis[hat].y; + if (!info->length[i]) continue; + + if (info->length[i] > info->ret[i] || + info->id[i] != (js_lt_get_bits(info, i, 4) | (js_lt_get_bits(info, i, 4) << 4))) { + ret = -1; + continue; + } - buttons[0][0] = js_lt_reverse((data >> 2) & 0xfc); + if (info->length[i] < info->ret[i]) + info->bugs[i] |= JS_LT_BUG_LONGDATA; + + k = l = 0; - return 0; + for (j = 0; j < info->axes10[i]; j++) + axes[i][k++] = js_lt_get_bits(info, i, 10); - case JS_LT_MODE_CM2: + for (j = 0; j < info->axes8[i]; j++) + axes[i][k++] = js_lt_get_bits(info, i, 8); - if (js_lt_read_packet(info->io, &data) != 64) return -1; + for (j = 0; j <= (info->buttons[i] - 1) >> 5; j++) buttons[i][j] = 0; - axes[0][0] = (data >> 48) & 0xff; - axes[0][1] = (data >> 40) & 0xff; - axes[0][2] = (data >> 32) & 0xff; - axes[0][3] = (data >> 24) & 0xff; - axes[0][4] = (data >> 16) & 0xff; - axes[0][5] = (data >> 8) & 0xff; + for (j = 0; j < info->buttons[i] && j < 63; j++) { + if (j == info->pad[i]) { + t = js_lt_get_bits(info, i, 4); + axes[i][k++] = ((t >> 2) & 1) - ( t & 1); + axes[i][k++] = ((t >> 1) & 1) - ((t >> 3) & 1); + } + buttons[i][l >> 5] |= js_lt_get_bits(info, i, 1) << (l & 0x1f); + l++; + } - buttons[0][0] = js_lt_reverse(data & 0xff); + for (j = 0; j < info->hats[i]; j++) { + if((t = js_lt_get_bits(info, i, 4)) > 8) { + if (t != 15) ret = -1; /* Hat press */ + t = 0; + } + axes[i][k++] = js_lt_hat_to_axis[t].x; + axes[i][k++] = js_lt_hat_to_axis[t].y; + } - return 0; + for (j = 63; j < info->buttons[i]; j++) { + buttons[i][l >> 5] |= js_lt_get_bits(info, i, 1) << (l & 0x1f); + l++; + } } - return -1; + return ret; } /* @@ -225,15 +296,18 @@ static int js_lt_close(struct js_dev *jd) } /* - * js_lt_trigger_sequence() sends a trigger & delay sequence - * to reset/initialize a Logitech joystick. + * js_lt_init_digital() sends a trigger & delay sequence + * to reset and initialize a Logitech joystick into digital mode. */ -static void __init js_lt_trigger_sequence(int io, int *seq) +static void __init js_lt_init_digital(int io) { - while (*seq) { + int seq[] = { 3, 2, 3, 10, 6, 11, 7, 9, 11, 0 }; + int i; + + for (i = 0; seq[i]; i++) { outb(0xff,io); - udelay(*seq++); + mdelay(seq[i]); } } @@ -242,35 +316,45 @@ static void __init js_lt_trigger_sequence(int io, int *seq) * Logitech joysticks. */ -static void __init js_lt_init_corr(int num_axes, int mode, int **axes, struct js_corr **corr) +static void __init js_lt_init_corr(int id, int naxes10, int naxes8, int naxes1, int *axes, struct js_corr *corr) { int j; - - for (j = 0; j < num_axes; j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 2; - corr[0][j].coef[0] = axes[0][j] - 8; - corr[0][j].coef[1] = axes[0][j] + 8; - corr[0][j].coef[2] = (1 << 29) / (127 - 32); - corr[0][j].coef[3] = (1 << 29) / (127 - 32); + + if (id == JS_LT_ID_WMED) axes[2] = 128; /* Throttle fixup */ + if (id == JS_LT_ID_WMI) axes[2] = 512; + if (id == JS_LT_ID_WM3D) axes[3] = 128; + + if (id == JS_LT_ID_WGPE) { /* Tilt fixup */ + axes[0] = 512; + axes[1] = 512; } - switch (mode) { - case JS_LT_MODE_TPD: j = 0; break; - case JS_LT_MODE_WMED: j = 3; break; - case JS_LT_MODE_CM2: j = 6; break; - default: j = 0; break; + for (j = 0; j < naxes10; j++) { + corr[j].type = JS_CORR_BROKEN; + corr[j].prec = 2; + corr[j].coef[0] = axes[j] - 16; + corr[j].coef[1] = axes[j] + 16; + corr[j].coef[2] = (1 << 29) / (256 - 32); + corr[j].coef[3] = (1 << 29) / (256 - 32); } - for (; j < num_axes; j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 0; - corr[0][j].coef[0] = 0; - corr[0][j].coef[1] = 0; - corr[0][j].coef[2] = (1 << 29); - corr[0][j].coef[3] = (1 << 29); + for (; j < naxes8 + naxes10; j++) { + corr[j].type = JS_CORR_BROKEN; + corr[j].prec = 1; + corr[j].coef[0] = axes[j] - 2; + corr[j].coef[1] = axes[j] + 2; + corr[j].coef[2] = (1 << 29) / (64 - 16); + corr[j].coef[3] = (1 << 29) / (64 - 16); } + for (; j < naxes1 + naxes8 + naxes10; j++) { + corr[j].type = JS_CORR_BROKEN; + corr[j].prec = 0; + corr[j].coef[0] = 0; + corr[j].coef[1] = 0; + corr[j].coef[2] = (1 << 29); + corr[j].coef[3] = (1 << 29); + } } /* @@ -279,61 +363,157 @@ static void __init js_lt_init_corr(int num_axes, int mode, int **axes, struct js static struct js_port __init *js_lt_probe(int io, struct js_port *port) { - struct js_lt_info info; - char *name; - int axes, buttons, i; - __u64 data; - unsigned char u; + struct js_lt_info iniinfo; + struct js_lt_info *info = &iniinfo; + char name[32]; + int i, j, t; if (check_region(io, 1)) return port; - if (((u = inb(io)) & 3) == 3) return port; - outb(0xff,io); - if (!((inb(io) ^ u) & ~u & 0xf)) return port; + js_lt_init_digital(io); - if (!(i = js_lt_read_packet(io, &data))) { - udelay(JS_LT_MAX_DELAY); - js_lt_trigger_sequence(io, js_lt_seq_reset); - js_lt_trigger_sequence(io, js_lt_seq_init); - i = js_lt_read_packet(io, &data); - } + memset(info, 0, sizeof(struct js_lt_info)); + + info->length[0] = info->length[1] = JS_LT_MAX_LENGTH; + + info->io = io; + js_lt_read_packet(info); + + if (info->ret[0] >= JS_LT_MIN_LEN_LENGTH) + js_lt_move_bits(info, js_lt_get_bits(info, 0, 10)); + + info->length[0] = info->length[1] = 0; + + for (i = 0; i < 2; i++) { + + if (info->ret[i] < JS_LT_MIN_ID_LENGTH) continue; /* Minimum ID packet length */ + + if (info->ret[i] < (t = js_lt_get_bits(info, i, 10))) { + printk(KERN_WARNING "joy-logitech: Short ID packet: reported: %d != read: %d\n", + t, info->ret[i]); + continue; + } + + if (info->ret[i] > t) + info->bugs[i] |= JS_LT_BUG_LONGID; + +#ifdef JS_LT_DEBUG + printk(KERN_DEBUG "joy-logitech: id %d length %d", i, t); +#endif + + info->id[i] = js_lt_get_bits(info, i, 4) | (js_lt_get_bits(info, i, 4) << 4); + + if ((t = js_lt_get_bits(info, i, 4)) & JS_LT_FLAG_HAT) info->hats[i]++; + +#ifdef JS_LT_DEBUG + printk(KERN_DEBUG "joy-logitech: id %d flags %d", i, t); +#endif + + if ((info->length[i] = js_lt_get_bits(info, i, 10)) >= JS_LT_MAX_LENGTH) { + printk(KERN_WARNING "joy-logitech: Expected packet length too long (%d).\n", + info->length[i]); + continue; + } + + if (info->length[i] < JS_LT_MIN_LENGTH) { + printk(KERN_WARNING "joy-logitech: Expected packet length too short (%d).\n", + info->length[i]); + continue; + } + + info->axes8[i] = js_lt_get_bits(info, i, 4); + info->buttons[i] = js_lt_get_bits(info, i, 6); + + if (js_lt_get_bits(info, i, 6) != 8 && info->hats[i]) { + printk(KERN_WARNING "joy-logitech: Other than 8-dir POVs not supported yet.\n"); + continue; + } + + info->buttons[i] += js_lt_get_bits(info, i, 6); + info->hats[i] += js_lt_get_bits(info, i, 4); + + j = js_lt_get_bits(info, i, 4); + + if (t & JS_LT_FLAG_10BIT) { + info->axes10[i] = info->axes8[i] - j; + info->axes8[i] = j; + } + + t = js_lt_get_bits(info, i, 4); + + for (j = 0; j < t; j++) + info->name[i][j] = js_lt_get_bits(info, i, 8); + info->name[i][j] = 0; + + switch (info->id[i]) { + case JS_LT_ID_TPD: + info->pad[i] = 4; + info->buttons[i] -= 4; + break; + case JS_LT_ID_WGP: + info->pad[i] = 0; + info->buttons[i] -= 4; + break; + default: + info->pad[i] = -1; + break; + } + + if (info->length[i] != + (t = 8 + info->buttons[i] + info->axes10[i] * 10 + info->axes8[i] * 8 + + info->hats[i] * 4 + (info->pad[i] != -1) * 4)) { + printk(KERN_WARNING "js%d: Expected lenght %d != data length %d\n", i, t, info->length[i]); + } - switch (i) { - case 0: - return port; - case 20: - info.mode = JS_LT_MODE_TPD; - axes = 2; buttons = 8; name = "Logitech ThunderPad Digital"; - break; - case 42: - info.mode = JS_LT_MODE_WMED; - axes = 5; buttons = 6; name = "Logitech WingMan Extreme Digital"; - break; - case 64: - info.mode = JS_LT_MODE_CM2; - axes = 6; buttons = 8; name = "Logitech CyberMan 2"; - break; - case 72: - case 144: - return port; - default: - printk(KERN_WARNING "joy-logitech: unknown joystick device detected " - "(io=%#x, count=%d, data=0x%08x%08x), contact <vojtech@suse.cz>\n", - io, i, (int)(data >> 32), (int)(data & 0xffffffff)); - return port; } - info.io = io; + if (!info->length[0] && !info->length[1]) + return port; request_region(io, 1, "joystick (logitech)"); - port = js_register_port(port, &info, 1, sizeof(struct js_lt_info), js_lt_read); - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, 0, axes, buttons, name, js_lt_open, js_lt_close), name, io); - udelay(JS_LT_MAX_DELAY); + port = js_register_port(port, info, 2, sizeof(struct js_lt_info), js_lt_read); + info = port->info; + + for (i = 0; i < 2; i++) + if (info->length[i] > 0) { + sprintf(name, info->id[i] < JS_LT_MAX_ID ? + js_lt_names[info->id[i]] : js_lt_names[JS_LT_MAX_ID], info->id[i]); + printk(KERN_INFO "js%d: %s [%s] at %#x\n", + js_register_device(port, i, + info->axes10[i] + info->axes8[i] + ((info->hats[i] + (info->pad[i] >= 0)) << 1), + info->buttons[i], name, js_lt_open, js_lt_close), name, info->name[i], io); + } + + mdelay(JS_LT_INIT_DELAY); + if (js_lt_read(info, port->axes, port->buttons)) { + if (info->ret[0] < 1) info->bugs[0] |= JS_LT_BUG_IGNTRIG; + if (info->ret[1] < 1) info->bugs[1] |= JS_LT_BUG_IGNTRIG; + mdelay(JS_LT_DATA_DELAY); + js_lt_read(info, port->axes, port->buttons); + } - js_lt_read(port->info, port->axes, port->buttons); - js_lt_init_corr(axes, info.mode, port->axes, port->corr); + for (i = 0; i < 2; i++) + if (info->length[i] > 0) { +#ifdef JS_LT_DEBUG + printk(KERN_DEBUG "js%d: length %d ret %d id %d buttons %d axes10 %d axes8 %d " + "pad %d hats %d name %s explen %d\n", + i, info->length[i], info->ret[i], info->id[i], + info->buttons[i], info->axes10[i], info->axes8[i], + info->pad[i], info->hats[i], info->name[i], + 8 + info->buttons[i] + info->axes10[i] * 10 + info->axes8[i] * 8 + + info->hats[i] * 4 + (info->pad[i] != -1) * 4); +#endif + if (info->bugs[i]) { + printk(KERN_WARNING "js%d: Firmware bugs detected:%s%s%s%s\n", i, + info->bugs[i] & JS_LT_BUG_BUTTONS ? " init_buttons" : "", + info->bugs[i] & JS_LT_BUG_LONGID ? " long_id" : "", + info->bugs[i] & JS_LT_BUG_LONGDATA ? " long_data" : "", + info->bugs[i] & JS_LT_BUG_IGNTRIG ? " ignore_trigger" : ""); + } + js_lt_init_corr(info->id[i], info->axes10[i], info->axes8[i], + ((info->pad[i] >= 0) + info->hats[i]) << 1, port->axes[i], port->corr[i]); + } return port; } @@ -359,10 +539,13 @@ int __init js_lt_init(void) #ifdef MODULE void cleanup_module(void) { + int i; struct js_lt_info *info; - while (js_lt_port != NULL) { - js_unregister_device(js_lt_port->devs[0]); + while (js_lt_port) { + for (i = 0; i < js_lt_port->ndevs; i++) + if (js_lt_port->devs[i]) + js_unregister_device(js_lt_port->devs[i]); info = js_lt_port->info; release_region(info->io, 1); js_lt_port = js_unregister_port(js_lt_port); diff --git a/drivers/char/joystick/joy-magellan.c b/drivers/char/joystick/joy-magellan.c new file mode 100644 index 000000000..3a54d157e --- /dev/null +++ b/drivers/char/joystick/joy-magellan.c @@ -0,0 +1,397 @@ +/* + * joy-magellan.c Version 0.1 + * + * Copyright (c) 1999 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This is a module for the Linux joystick driver, supporting + * the Magellan and Space Mouse 6dof controllers. + */ + +/* + * 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@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <asm/system.h> +#include <linux/errno.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/init.h> + +/* + * Constants. + */ + +#define N_JOYSTICK_MAG 14 +#define JS_MAG_MAX_LENGTH 64 + +/* + * List of Magellans. + */ + +static struct js_port* js_mag_port = NULL; + +/* + * Per-Magellan data. + */ + +struct js_mag_info { + struct tty_struct* tty; + struct js_port* port; + int idx; + unsigned char data[JS_MAG_MAX_LENGTH]; + unsigned char name[JS_MAG_MAX_LENGTH]; + char ack; + char used; +}; + +/* + * js_mag_crunch_nibbles() verifies that the bytes sent from the Magellan + * have correct upper nibbles for the lower ones, if not, the packet will + * be thrown away. It also strips these upper halves to simplify further + * processing. + */ + +static int js_mag_crunch_nibbles(unsigned char *data, int count) +{ + static unsigned char nibbles[16] = "0AB3D56GH9:K<MN?"; + + do { + if (data[count] == nibbles[data[count] & 0xf]) + data[count] = data[count] & 0xf; + else + return -1; + } while (--count); + + return 0; +} + +/* + * js_mag_process_packet() decodes packets the driver receives from the + * Magellan. It updates the data accordingly, and sets an ACK flag + * to the type of last packet received, if received OK. + */ + +static void js_mag_process_packet(struct js_mag_info* info) +{ + int i; + + if (!info->idx) return; + + switch (info->data[0]) { + + case 'd': /* Axis data */ + if (info->idx != 25) return; + if (js_mag_crunch_nibbles(info->data, 24)) return; + if (!info->port->devs[0]) return; + for (i = 0; i < 6; i++) { + info->port->axes[0][i] = + ( info->data[(i << 2) + 1] << 12 | info->data[(i << 2) + 2] << 8 | + info->data[(i << 2) + 3] << 4 | info->data[(i << 2) + 4] ) + - 32768; + } + break; + + case 'e': /* Error packet */ + if (info->idx != 4) return; + if (js_mag_crunch_nibbles(info->data, 3)) return; + switch (info->data[1]) { + case 1: + printk(KERN_ERR "joy-magellan: Received command error packet. Failing command byte: %c\n", + info->data[2] | (info->data[3] << 4)); + break; + case 2: + printk(KERN_ERR "joy-magellan: Received framing error packet.\n"); + break; + default: + printk(KERN_ERR "joy-magellan: Received unknown error packet.\n"); + } + break; + + case 'k': /* Button data */ + if (info->idx != 4) return; + if (js_mag_crunch_nibbles(info->data, 3)) return; + if (!info->port->devs[0]) return; + info->port->buttons[0][0] = (info->data[1] << 1) | (info->data[2] << 5) | info->data[3]; + break; + + case 'm': /* Mode */ + if (info->idx != 2) return; + if (js_mag_crunch_nibbles(info->data, 1)) return; + break; + + case 'n': /* Null radius */ + if (info->idx != 2) return; + if (js_mag_crunch_nibbles(info->data, 1)) return; + break; + + case 'p': /* Data rate */ + if (info->idx != 3) return; + if (js_mag_crunch_nibbles(info->data, 2)) return; + break; + + case 'q': /* Sensitivity */ + if (info->idx != 3) return; + if (js_mag_crunch_nibbles(info->data, 2)) return; + break; + + case 'v': /* Version string */ + info->data[info->idx] = 0; + for (i = 1; i < info->idx && info->data[i] == ' '; i++); + memcpy(info->name, info->data + i, info->idx - i); + break; + + case 'z': /* Zero position */ + break; + + default: + printk("joy-magellan: Unknown packet %d length %d:", info->data[0], info->idx); + for (i = 0; i < info->idx; i++) printk(" %02x", info->data[i]); + printk("\n"); + return; + } + + info->ack = info->data[0]; +} + +/* + * js_mag_command() sends a command to the Magellan, and waits for + * acknowledge. + */ + +static int js_mag_command(struct js_mag_info *info, char *command, int timeout) +{ + info->ack = 0; + if (info->tty->driver.write(info->tty, 0, command, strlen(command)) != strlen(command)) return -1; + while (!info->ack && timeout--) mdelay(1); + return -(info->ack != command[0]); +} + +/* + * js_mag_setup() initializes the Magellan to sane state. Also works as + * a probe for Magellan existence. + */ + +static int js_mag_setup(struct js_mag_info *info) +{ + + if (js_mag_command(info, "vQ\r", 800)) /* Read version */ + return -1; + if (js_mag_command(info, "m3\r", 50)) /* Set full 3d mode */ + return -1; + if (js_mag_command(info, "pBB\r", 50)) /* Set 16 reports/second (max) */ + return -1; + if (js_mag_command(info, "z\r", 50)) /* Set zero position */ + return -1; + + return 0; +} + +/* + * js_mag_read() updates the axis and button data upon startup. + */ + +static int js_mag_read(struct js_mag_info *info) +{ + memset(info->port->axes[0],0, sizeof(int) * 6); /* Axes are 0 after zero postition cmd */ + + if (js_mag_command(info, "kQ\r", 50)) /* Read buttons */ + return -1; + + return 0; +} + +/* + * js_mag_open() is a callback from the joystick device open routine. + */ + +static int js_mag_open(struct js_dev *jd) +{ + struct js_mag_info *info = jd->port->info; + info->used++; + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_mag_close() is a callback from the joystick device release routine. + */ + +static int js_mag_close(struct js_dev *jd) +{ + struct js_mag_info *info = jd->port->info; + if (!--info->used) { + js_unregister_device(jd->port->devs[0]); + js_mag_port = js_unregister_port(jd->port); + } + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_mag_init_corr() initializes the correction values for the Magellan. + * It asumes gain setting of 0, question is, what we should do for higher + * gain settings ... + */ + +static void js_mag_init_corr(struct js_corr **corr) +{ + int i; + + for (i = 0; i < 6; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = 0; + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (1 << 29) / 256; + corr[0][i].coef[3] = (1 << 29) / 256; + } +} + +/* + * js_mag_ldisc_open() is the routine that is called upon setting our line + * discipline on a tty. It looks for the Magellan, and if found, registers + * it as a joystick device. + */ + +static int js_mag_ldisc_open(struct tty_struct *tty) +{ + struct js_mag_info iniinfo; + struct js_mag_info *info = &iniinfo; + + info->tty = tty; + info->idx = 0; + info->used = 1; + + js_mag_port = js_register_port(js_mag_port, info, 1, sizeof(struct js_mag_info), NULL); + + info = js_mag_port->info; + info->port = js_mag_port; + tty->disc_data = info; + + if (js_mag_setup(info)) { + js_mag_port = js_unregister_port(info->port); + return -ENODEV; + } + + printk(KERN_INFO "js%d: Magellan [%s] on %s%d\n", + js_register_device(js_mag_port, 0, 6, 9, "Magellan", js_mag_open, js_mag_close), + info->name, tty->driver.name, MINOR(tty->device) - tty->driver.minor_start); + + + js_mag_read(info); + js_mag_init_corr(js_mag_port->corr); + + MOD_INC_USE_COUNT; + + return 0; +} + +/* + * js_mag_ldisc_close() is the opposite of js_mag_ldisc_open() + */ + +static void js_mag_ldisc_close(struct tty_struct *tty) +{ + struct js_mag_info* info = (struct js_mag_info*) tty->disc_data; + if (!--info->used) { + js_unregister_device(info->port->devs[0]); + js_mag_port = js_unregister_port(info->port); + } + MOD_DEC_USE_COUNT; +} + +/* + * js_mag_ldisc_receive() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static void js_mag_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct js_mag_info* info = (struct js_mag_info*) tty->disc_data; + int i; + + for (i = 0; i < count; i++) + if (cp[i] == '\r') { + js_mag_process_packet(info); + info->idx = 0; + } else { + if (info->idx < JS_MAG_MAX_LENGTH) + info->data[info->idx++] = cp[i]; + } +} + +/* + * js_mag_ldisc_room() reports how much room we do have for receiving data. + * Although we in fact have infinite room, we need to specify some value + * here, so why not the size of our packet buffer. It's big anyway. + */ + +static int js_mag_ldisc_room(struct tty_struct *tty) +{ + return JS_MAG_MAX_LENGTH; +} + +/* + * The line discipline structure. + */ + +static struct tty_ldisc js_mag_ldisc = { + magic: TTY_LDISC_MAGIC, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + name: "magellan", +#endif + open: js_mag_ldisc_open, + close: js_mag_ldisc_close, + receive_buf: js_mag_ldisc_receive, + receive_room: js_mag_ldisc_room, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +#ifdef MODULE +int init_module(void) +#else +int __init js_mag_init(void) +#endif +{ + if (tty_register_ldisc(N_JOYSTICK_MAG, &js_mag_ldisc)) { + printk(KERN_ERR "joy-magellan: Error registering line discipline.\n"); + return -ENODEV; + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + tty_register_ldisc(N_JOYSTICK_MAG, NULL); +} +#endif diff --git a/drivers/char/joystick/joy-pci.c b/drivers/char/joystick/joy-pci.c new file mode 100644 index 000000000..ae49fd8e1 --- /dev/null +++ b/drivers/char/joystick/joy-pci.c @@ -0,0 +1,273 @@ +/* + * joy-pci.c Version 0.4.0 + * + * Copyright (c) 1999 Raymond Ingles + * Copyright (c) 1999 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This is a module for the Linux joystick driver, supporting the + * gameports on Trident 4DWave and Aureal Vortex soundcards, and + * analog joysticks connected to them. + */ + +/* + * 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, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/init.h> + +MODULE_AUTHOR("Raymond Ingles <sorceror@tir.com>"); +MODULE_PARM(js_pci, "3-32i"); + +#define NUM_CARDS 8 +static int js_pci[NUM_CARDS * 4] __initdata = { -1,0,0,0,-1,0,0,0,-1,0,0,0,-1,0,0,0, + -1,0,0,0,-1,0,0,0,-1,0,0,0,-1,0,0,0 }; + +static struct js_port * js_pci_port __initdata = NULL; + +#include "joy-analog.h" + +struct js_pci_info; +typedef void (*js_pci_func)(struct js_pci_info *); + +struct js_pci_data { + int vendor; /* PCI Vendor ID */ + int model; /* PCI Model ID */ + int size; /* Memory / IO region size */ + int lcr; /* Aureal Legacy Control Register */ + int gcr; /* Gameport control register */ + int buttons; /* Buttons location */ + int axes; /* Axes start */ + int axsize; /* Axis field size */ + int axmax; /* Axis field max value */ + js_pci_func init; + js_pci_func cleanup; + char *name; +}; + +struct js_pci_info { + unsigned char *base; + struct pci_dev *pci_p; + __u32 lcr; + struct js_pci_data *data; + struct js_an_info an; +}; + +/* + * js_pci_*_init() sets the info->base field, disables legacy gameports, + * and enables the enhanced ones. + */ + +static void js_pci_4dwave_init(struct js_pci_info *info) +{ + info->base = ioremap(BASE_ADDRESS(info->pci_p, 1), info->data->size); + pci_read_config_word(info->pci_p, info->data->lcr, (unsigned short *)&info->lcr); + pci_write_config_word(info->pci_p, info->data->lcr, info->lcr & ~0x20); + writeb(0x80, info->base + info->data->gcr); +} + +static void js_pci_vortex_init(struct js_pci_info *info) +{ + info->base = ioremap(BASE_ADDRESS(info->pci_p, 0), info->data->size); + info->lcr = readl(info->base + info->data->lcr); + writel(info->lcr & ~0x8, info->base + info->data->lcr); + writel(0x40, info->base + info->data->gcr); +} + +/* + * js_pci_*_cleanup does the opposite of the above functions. + */ + +static void js_pci_4dwave_cleanup(struct js_pci_info *info) +{ + pci_write_config_word(info->pci_p, info->data->lcr, info->lcr); + writeb(0x00, info->base + info->data->gcr); + iounmap(info->base); +} + +static void js_pci_vortex_cleanup(struct js_pci_info *info) +{ + writel(info->lcr, info->base + info->data->lcr); + writel(0x00, info->base + info->data->gcr); + iounmap(info->base); +} + +static struct js_pci_data js_pci_data[] = +{{ PCI_VENDOR_ID_TRIDENT, 0x2000, 0x10000, 0x00044 ,0x00030, 0x00031, 0x00034, 2, 0xffff, + js_pci_4dwave_init, js_pci_4dwave_cleanup, "Trident 4DWave DX" }, + { PCI_VENDOR_ID_TRIDENT, 0x2001, 0x10000, 0x00044, 0x00030, 0x00031, 0x00034, 2, 0xffff, + js_pci_4dwave_init, js_pci_4dwave_cleanup, "Trident 4DWave NX" }, + { PCI_VENDOR_ID_AUREAL, 0x0001, 0x40000, 0x1280c, 0x1100c, 0x11008, 0x11010, 4, 0x1fff, + js_pci_vortex_init, js_pci_vortex_cleanup, "Aureal Vortex1" }, + { PCI_VENDOR_ID_AUREAL, 0x0002, 0x40000, 0x2a00c, 0x2880c, 0x28808, 0x28810, 4, 0x1fff, + js_pci_vortex_init, js_pci_vortex_cleanup, "Aureal Vortex2" }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL }}; + +/* + * js_pci_read() reads data from a PCI gameport. + */ + +static int js_pci_read(void *xinfo, int **axes, int **buttons) +{ + struct js_pci_info *info = xinfo; + int i; + + info->an.buttons = ~readb(info->base + info->data->buttons) >> 4; + + for (i = 0; i < 4; i++) + info->an.axes[i] = readw(info->base + info->data->axes + i * info->data->axsize); + + js_an_decode(&info->an, axes, buttons); + + return 0; +} + +/* + * js_pci_open() is a callback from the file open routine. + */ + +static int js_pci_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_pci_close() is a callback from the file release routine. + */ + +static int js_pci_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static struct js_port * __init js_pci_probe(struct js_port *port, int type, int number, + struct pci_dev *pci_p, struct js_pci_data *data) +{ + int i; + unsigned char u; + int mask0, mask1, numdev; + struct js_pci_info iniinfo; + struct js_pci_info *info = &iniinfo; + + mask0 = mask1 = 0; + + for (i = 0; i < NUM_CARDS; i++) + if (js_pci[i * 4] == type && js_pci[i * 4 + 1] == number) { + mask0 = js_pci[i * 4 + 2]; + mask1 = js_pci[i * 4 + 3]; + if (!mask0 && !mask1) return port; + break; + } + + memset(info, 0, sizeof(struct js_pci_info)); + + info->data = data; + info->pci_p = pci_p; + data->init(info); + + mdelay(10); + js_pci_read(info, NULL, NULL); + + for (i = u = 0; i < 4; i++) + if (info->an.axes[i] < info->data->axmax) u |= 1 << i; + + if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0) + return port; + + port = js_register_port(port, info, numdev, sizeof(struct js_pci_info), js_pci_read); + + info = port->info; + + for (i = 0; i < numdev; i++) + printk(KERN_WARNING "js%d: %s on %s #%d\n", + js_register_device(port, i, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), + js_an_name(i, &info->an), js_pci_open, js_pci_close), js_an_name(i, &info->an), data->name, number); + + js_pci_read(info, port->axes, port->buttons); + js_an_init_corr(&info->an, port->axes, port->corr, 32); + + return port; +} + +#ifndef MODULE +int __init js_pci_setup(SETUP_PARAM) +{ + int i; + SETUP_PARSE(NUM_CARDS*4); + for (i = 0; i <= ints[0] && i < NUM_CARDS*4; i++) + js_pci[i] = ints[i+1]; + return 1; +} +__setup("js_pci=", js_pci_setup); +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_pci_init(void) +#endif +{ + struct pci_dev *pci_p = NULL; + int i, j; + + for (i = 0; js_pci_data[i].vendor; i++) + for (j = 0; (pci_p = pci_find_device(js_pci_data[i].vendor, js_pci_data[i].model, pci_p)); j++) + js_pci_port = js_pci_probe(js_pci_port, i, j, pci_p, js_pci_data + i); + + if (!js_pci_port) { +#ifdef MODULE + printk(KERN_WARNING "joy-pci: no joysticks found\n"); +#endif + return -ENODEV; + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i; + struct js_pci_info *info; + + while (js_pci_port) { + for (i = 0; i < js_pci_port->ndevs; i++) + if (js_pci_port->devs[i]) + js_unregister_device(js_pci_port->devs[i]); + info = js_pci_port->info; + info->data->cleanup(info); + js_pci_port = js_unregister_port(js_pci_port); + } +} +#endif diff --git a/drivers/char/joystick/joy-sidewinder.c b/drivers/char/joystick/joy-sidewinder.c index d097471bc..37d276ba1 100644 --- a/drivers/char/joystick/joy-sidewinder.c +++ b/drivers/char/joystick/joy-sidewinder.c @@ -1,7 +1,9 @@ /* * joy-sidewinder.c Version 1.2 * - * Copyright (c) 1998 Vojtech Pavlik + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -38,18 +40,36 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/string.h> +#include <linux/init.h> + +/* + * These are really magic values. Changing them can make a problem go away, + * as well as break everything. + */ + +#undef JS_SW_DEBUG -#define JS_SW_MAX_START 250 -#define JS_SW_MIN_STROBE 25 -#define JS_SW_EXT_STROBE 45 -#define JS_SW_MIN_TIME 1500 -#define JS_SW_MAX_TIME 4000 +#define JS_SW_START 400 /* The time we wait for the first bit [400 us] */ +#define JS_SW_STROBE 45 /* Max time per bit [45 us] */ +#define JS_SW_TIMEOUT 4000 /* Wait for everything to settle [4 ms] */ +#define JS_SW_KICK 45 /* Wait after A0 fall till kick [45 us] */ +#define JS_SW_END 8 /* Number of bits before end of packet to kick */ +#define JS_SW_FAIL 16 /* Number of packet read errors to fail and reinitialize */ +#define JS_SW_BAD 2 /* Number of packet read errors to switch off 3d Pro optimization */ +#define JS_SW_OK 64 /* Number of packet read successes to switch optimization back on */ +#define JS_SW_LENGTH 512 /* Max number of bits in a packet */ -#define JS_SW_MAX_LENGTH 72 +/* + * SideWinder joystick types ... + */ -#define JS_SW_MODE_3DP 1 -#define JS_SW_MODE_PP 2 -#define JS_SW_MODE_GP 3 +#define JS_SW_TYPE_3DP 1 +#define JS_SW_TYPE_F23 2 +#define JS_SW_TYPE_GP 3 +#define JS_SW_TYPE_PP 4 +#define JS_SW_TYPE_FFP 5 +#define JS_SW_TYPE_FSP 6 +#define JS_SW_TYPE_FFW 7 static int js_sw_port_list[] __initdata = {0x201, 0}; static struct js_port* js_sw_port __initdata = NULL; @@ -61,208 +81,398 @@ static struct { struct js_sw_info { int io; - unsigned char mode; + int length; + int speed; + unsigned char type; + unsigned char bits; unsigned char number; - unsigned char optimize; + unsigned char fail; + unsigned char ok; }; /* - * js_sw_init_digital() switches a SideWinder into digital mode. + * Gameport speed. + */ + +unsigned int js_sw_io_speed = 0; + +/* + * js_sw_measure_speed() measures the gameport i/o speed. */ -static void __init js_sw_init_digital(int io) +static int __init js_sw_measure_speed(int io) { - unsigned int t; - unsigned int timeout = (js_time_speed * JS_SW_MAX_TIME) >> 10; - int delays[] = {140, 140+726, 140+300, 0}; - int i = 0; - unsigned long flags; +#ifdef __i386__ - __save_flags(flags); - __cli(); - do { - outb(0xff,io); - t = js_get_time(); - while ((inb(io) & 1) && (js_delta(js_get_time(),t) < timeout)); - udelay(delays[i]); - } while (delays[i++]); - __restore_flags(flags); +#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0) +#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180L/HZ:0)) + + unsigned int i, t, t1, t2, t3, tx; + unsigned long flags; - for (i = 0; i < 4; i++) { - udelay(300); - outb(0xff, io); + tx = 1 << 30; + + for(i = 0; i < 50; i++) { + save_flags(flags); /* Yes, all CPUs */ + cli(); + GET_TIME(t1); + for(t = 0; t < 50; t++) inb(io); + GET_TIME(t2); + GET_TIME(t3); + restore_flags(flags); + udelay(i * 10); + if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; } - return; + return 59659 / t; + +#else + + unsigned int j, t = 0; + + j = jiffies; while (j == jiffies); + j = jiffies; while (j == jiffies) { t++; inb(0x201); } + + return t * HZ / 1000; + +#endif } /* - * js_sw_read_packet() reads a SideWinder packet. + * js_sw_read_packet() is a function which reads either a data packet, or an + * identification packet from a SideWinder joystick. Better don't try to + * understand this, since all the ugliness of the Microsoft Digital + * Overdrive protocol is concentrated in this function. If you really want + * to know how this works, first go watch a couple horror movies, so that + * you are well prepared, read US patent #5628686 and then e-mail me, + * and I'll send you an explanation. + * Vojtech <vojtech@suse.cz> */ -static int js_sw_read_packet(int io, int l1, int l2, int strobe, __u64 *data) +static int js_sw_read_packet(int io, int speed, unsigned char *buf, int length, int id) { - static unsigned char buf[JS_SW_MAX_LENGTH]; - unsigned char u, v; - int i; unsigned long flags; - unsigned int t, t1; - - int length = l1 < l2 ? l2 : l1; - int start = (js_time_speed * JS_SW_MAX_START) >> 10; - strobe = (js_time_speed * strobe) >> 10; - - i = 0; - - __save_flags(flags); - __cli(); - outb(0xff,io); - + int timeout, bitout, sched, i, kick, start, strobe; + unsigned char pending, u, v; + + i = -id; /* Don't care about data, only want ID */ + timeout = id ? (JS_SW_TIMEOUT * speed) >> 10 : 0; /* Set up global timeout for ID packet */ + kick = id ? (JS_SW_KICK * speed) >> 10 : 0; /* Set up kick timeout for ID packet */ + start = (JS_SW_START * speed) >> 10; + strobe = (JS_SW_STROBE * speed) >> 10; + bitout = start; + pending = 0; + sched = 0; + + __save_flags(flags); /* Quiet, please */ + __cli(); + + outb(0xff, io); /* Trigger */ v = inb(io); - t = js_get_time(); do { + bitout--; u = v; v = inb(io); - t1 = js_get_time(); - } while (!((u ^ v) & u & 0x10) && js_delta(t1, t) < start); + } while (!(~v & u & 0x10) && (bitout > 0)); /* Wait for first falling edge on clock */ - t = t1; + if (bitout > 0) bitout = strobe; /* Extend time if not timed out */ - do { - v = inb(io); - t1 = js_get_time(); - if ((u ^ v) & v & 0x10) { - buf[i++] = v >> 5; - t = t1; - } - u = v; - } while (i < length && js_delta(t1,t) < strobe); + while ((timeout > 0 || bitout > 0) && (i < length)) { - __restore_flags(flags); + timeout--; + bitout--; /* Decrement timers */ + sched--; - *data = 0; + u = v; + v = inb(io); - if (i == l1) { - t = i > 64 ? 64 : i; - for (i = 0; i < t; i++) - *data |= (__u64) (buf[i] & 1) << i; - return t; + if ((~u & v & 0x10) && (bitout > 0)) { /* Rising edge on clock - data bit */ + if (i >= 0) /* Want this data */ + buf[i] = v >> 5; /* Store it */ + i++; /* Advance index */ + bitout = strobe; /* Extend timeout for next bit */ + } + + if (kick && (~v & u & 0x01)) { /* Falling edge on axis 0 */ + sched = kick; /* Schedule second trigger */ + kick = 0; /* Don't schedule next time on falling edge */ + pending = 1; /* Mark schedule */ + } + + if (pending && sched < 0 && (i > -JS_SW_END)) { /* Second trigger time */ + outb(0xff, io); /* Trigger */ + bitout = start; /* Long bit timeout */ + pending = 0; /* Unmark schedule */ + timeout = 0; /* Switch from global to bit timeouts */ + } } - if (i == l2) { - t = i > 22 ? 22 : i; - for (i = 0; i < t; i++) - *data |= (__u64) buf[i] << (3 * i); - return t * 3; + + __restore_flags(flags); /* Done - relax */ + +#ifdef JS_SW_DEBUG + { + int j; + printk(KERN_DEBUG "joy-sidewinder: Read %d triplets. [", i); + for (j = 0; j < i; j++) printk("%d", buf[j]); + printk("]\n"); } +#endif return i; } /* - * js_sw_parity computes parity of __u64 + * js_sw_get_bits() and GB() compose bits from the triplet buffer into a __u64. + * Parameter 'pos' is bit number inside packet where to start at, 'num' is number + * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits + * is number of bits per triplet. + */ + +#define GB(pos,num,shift) js_sw_get_bits(buf, pos, num, shift, info->bits) + +static __u64 js_sw_get_bits(unsigned char *buf, int pos, int num, char shift, char bits) +{ + __u64 data = 0; + int tri = pos % bits; /* Start position */ + int i = pos / bits; + int bit = shift; + + while (num--) { + data |= (__u64)((buf[i] >> tri++) & 1) << bit++; /* Transfer bit */ + if (tri == bits) { + i++; /* Next triplet */ + tri = 0; + } + } + + return data; +} + +/* + * js_sw_init_digital() initializes a SideWinder 3D Pro joystick + * into digital mode. + */ + +static void js_sw_init_digital(int io, int speed) +{ + int seq[] = { 140, 140+725, 140+300, 0 }; + unsigned long flags; + int i, t; + + __save_flags(flags); + __cli(); + + i = 0; + do { + outb(0xff, io); /* Trigger */ + t = (JS_SW_TIMEOUT * speed) >> 10; + while ((inb(io) & 1) && t) t--; /* Wait for axis to fall back to 0 */ + udelay(seq[i]); /* Delay magic time */ + } while (seq[++i]); + + outb(0xff, io); /* Last trigger */ + + __restore_flags(flags); +} + +/* + * js_sw_parity() computes parity of __u64 */ static int js_sw_parity(__u64 t) { - t ^= t >> 32; - t ^= t >> 16; - t ^= t >> 8; - t ^= t >> 4; - t ^= t >> 2; - t ^= t >> 1; - return t & 1; + int x = t ^ (t >> 32); + x ^= x >> 16; + x ^= x >> 8; + x ^= x >> 4; + x ^= x >> 2; + x ^= x >> 1; + return x & 1; } /* - * js_sw_csum() computes checksum of nibbles in __u64 + * js_sw_ccheck() checks synchronization bits and computes checksum of nibbles. */ -static int js_sw_csum(__u64 t) +static int js_sw_check(__u64 t) { char sum = 0; - while (t) { + + if ((t & 0x8080808080808080ULL) ^ 0x80) /* Sync */ + return -1; + + while (t) { /* Sum */ sum += t & 0xf; t >>= 4; } + return sum & 0xf; } /* - * js_sw_read() reads and analyzes SideWinder joystick data. + * js_sw_parse() analyzes SideWinder joystick data, and writes the results into + * the axes and buttons arrays. */ -static int js_sw_read(void *xinfo, int **axes, int **buttons) +static int js_sw_parse(unsigned char *buf, struct js_sw_info *info, int **axes, int **buttons) { - struct js_sw_info *info = xinfo; - __u64 data; int hat, i; - switch (info->mode) { - - case JS_SW_MODE_3DP: + switch (info->type) { - if (info->optimize) { - i = js_sw_read_packet(info->io, -1, 22, JS_SW_EXT_STROBE, &data); - } else { - i = js_sw_read_packet(info->io, 64, 66, JS_SW_EXT_STROBE, &data); - if (i == 198) info->optimize = 1; - } + case JS_SW_TYPE_3DP: + case JS_SW_TYPE_F23: - if (i < 60) { - js_sw_init_digital(info->io); - info->optimize = 0; - return -1; - } + if (js_sw_check(GB(0,64,0)) || (hat = GB(6,1,3) | GB(60,3,0)) > 8) return -1; - if (((data & 0x8080808080808080ULL) ^ 0x80) || js_sw_csum(data) || - (hat = ((data >> 3) & 0x08) | ((data >> 60) & 0x07)) > 8) { - info->optimize = 0; - return -1; - } - axes[0][0] = ((data << 4) & 0x380) | ((data >> 16) & 0x07f); - axes[0][1] = ((data << 7) & 0x380) | ((data >> 24) & 0x07f); - axes[0][2] = ((data >> 28) & 0x180) | ((data >> 40) & 0x07f); - axes[0][3] = ((data >> 25) & 0x380) | ((data >> 48) & 0x07f); + axes[0][0] = GB( 3,3,7) | GB(16,7,0); + axes[0][1] = GB( 0,3,7) | GB(24,7,0); + axes[0][2] = GB(35,2,7) | GB(40,7,0); + axes[0][3] = GB(32,3,7) | GB(48,7,0); axes[0][4] = js_sw_hat_to_axis[hat].x; axes[0][5] = js_sw_hat_to_axis[hat].y; - buttons[0][0] = ((~data >> 31) & 0x80) | ((~data >> 8) & 0x7f); + buttons[0][0] = ~(GB(37,1,8) | GB(38,1,7) | GB(8,7,0)); return 0; - case JS_SW_MODE_PP: + case JS_SW_TYPE_GP: - if (js_sw_read_packet(info->io, 48, 16, JS_SW_EXT_STROBE, &data) != 48) return -1; - if (!js_sw_parity(data) || (hat = (data >> 42) & 0xf) > 8) return -1; + for (i = 0; i < info->number * 15; i += 15) { - axes[0][0] = (data >> 9) & 0x3ff; - axes[0][1] = (data >> 19) & 0x3ff; - axes[0][2] = (data >> 29) & 0x07f; - axes[0][3] = (data >> 36) & 0x03f; + if (js_sw_parity(GB(i,15,0))) return -1; + + axes[i][0] = GB(i+3,1,0) - GB(i+2,1,0); + axes[i][1] = GB(i+0,1,0) - GB(i+1,1,0); + buttons[i][0] = ~GB(i+4,10,0); + + } + + return 0; + + case JS_SW_TYPE_PP: + case JS_SW_TYPE_FFP: + + if (!js_sw_parity(GB(0,48,0)) || (hat = GB(42,4,0)) > 8) return -1; + + axes[0][0] = GB( 9,10,0); + axes[0][1] = GB(19,10,0); + axes[0][2] = GB(36, 6,0); + axes[0][3] = GB(29, 7,0); axes[0][4] = js_sw_hat_to_axis[hat].x; axes[0][5] = js_sw_hat_to_axis[hat].y; - buttons[0][0] = ~data & 0x1ff; + buttons[0][0] = ~GB(0,9,0); return 0; - case JS_SW_MODE_GP: + case JS_SW_TYPE_FSP: - if (js_sw_read_packet(info->io, 15 * info->number, 5 * info->number, - JS_SW_EXT_STROBE, &data) != 15 * info->number) return -1; - if (js_sw_parity(data)) return -1; + if (!js_sw_parity(GB(0,43,0)) || (hat = GB(28,4,0)) > 8) return -1; - for (i = 0; i < info->number; i++) { - axes[i][0] = ((data >> 3) & 1) - ((data >> 2) & 1); - axes[i][1] = ( data & 1) - ((data >> 1) & 1); - buttons[i][0] = (~data >> 4) & 0x3ff; - data >>= 15; - } + axes[0][0] = GB( 0,10,0); + axes[0][1] = GB(16,10,0); + axes[0][2] = GB(32, 6,0); + axes[0][3] = js_sw_hat_to_axis[hat].x; + axes[0][4] = js_sw_hat_to_axis[hat].y; + buttons[0][0] = ~(GB(10,6,0) | GB(26,2,6) | GB(38,2,8)); return 0; - default: - return -1; + case JS_SW_TYPE_FFW: + + if (!js_sw_parity(GB(0,33,0))) return -1; + + axes[0][0] = GB( 0,10,0); + axes[0][1] = GB(10, 6,0); + axes[0][2] = GB(16, 6,0); + buttons[0][0] = ~GB(22,8,0); + + return 0; } + + return -1; +} + +/* + * js_sw_read() reads SideWinder joystick data, and reinitializes + * the joystick in case of persistent problems. This is the function that is + * called from the generic code to poll the joystick. + */ + +static int js_sw_read(void *xinfo, int **axes, int **buttons) +{ + struct js_sw_info *info = xinfo; + unsigned char buf[JS_SW_LENGTH]; + int i; + + i = js_sw_read_packet(info->io, info->speed, buf, info->length, 0); + + if (info->type <= JS_SW_TYPE_F23 && info->length == 66 && i != 66) { /* Broken packet, try to fix */ + + if (i == 64 && !js_sw_check(js_sw_get_bits(buf,0,64,0,1))) { /* Last init failed, 1 bit mode */ + printk(KERN_WARNING "joy-sidewinder: Joystick in wrong mode on %#x" + " - going to reinitialize.\n", info->io); + info->fail = JS_SW_FAIL; /* Reinitialize */ + i = 128; /* Bogus value */ + } + + if (i < 66 && GB(0,64,0) == GB(i*3-66,64,0)) /* 1 == 3 */ + i = 66; /* Everything is fine */ + + if (i < 66 && GB(0,64,0) == GB(66,64,0)) /* 1 == 2 */ + i = 66; /* Everything is fine */ + + if (i < 66 && GB(i*3-132,64,0) == GB(i*3-66,64,0)) { /* 2 == 3 */ + memmove(buf, buf + i - 22, 22); /* Move data */ + i = 66; /* Carry on */ + } + } + + if (i == info->length && !js_sw_parse(buf, info, axes, buttons)) { /* Parse data */ + + info->fail = 0; + info->ok++; + + if (info->type <= JS_SW_TYPE_F23 && info->length == 66 /* Many packets OK */ + && info->ok > JS_SW_OK) { + + printk(KERN_INFO "joy-sidewinder: No more trouble on %#x" + " - enabling optimization again.\n", info->io); + info->length = 22; + } + + return 0; + } + + info->ok = 0; + info->fail++; + + if (info->type <= JS_SW_TYPE_F23 && info->length == 22 /* Consecutive bad packets */ + && info->fail > JS_SW_BAD) { + + printk(KERN_INFO "joy-sidewinder: Many bit errors on %#x" + " - disabling optimization.\n", info->io); + info->length = 66; + } + + if (info->fail < JS_SW_FAIL) return -1; /* Not enough, don't reinitialize yet */ + + printk(KERN_WARNING "joy-sidewinder: Too many bit errors on %#x" + " - reinitializing joystick.\n", info->io); + + if (!i && info->type <= JS_SW_TYPE_F23) { /* 3D Pro can be in analog mode */ + udelay(3 * JS_SW_TIMEOUT); + js_sw_init_digital(info->io, info->speed); + } + + udelay(JS_SW_TIMEOUT); + i = js_sw_read_packet(info->io, info->speed, buf, JS_SW_LENGTH, 0); /* Read normal data packet */ + udelay(JS_SW_TIMEOUT); + js_sw_read_packet(info->io, info->speed, buf, JS_SW_LENGTH, i); /* Read ID packet, this initializes the stick */ + + info->fail = JS_SW_FAIL; + + return -1; } /* @@ -290,7 +500,7 @@ static int js_sw_close(struct js_dev *jd) * SideWinders. */ -static void __init js_sw_init_corr(int num_axes, int mode, int number, struct js_corr **corr) +static void __init js_sw_init_corr(int num_axes, int type, int number, struct js_corr **corr) { int i, j; @@ -305,9 +515,10 @@ static void __init js_sw_init_corr(int num_axes, int mode, int number, struct js corr[i][j].coef[3] = (1 << 29) / (511 - 32); } - switch (mode) { + switch (type) { - case JS_SW_MODE_3DP: + case JS_SW_TYPE_3DP: + case JS_SW_TYPE_F23: corr[i][2].type = JS_CORR_BROKEN; corr[i][2].prec = 4; @@ -320,33 +531,73 @@ static void __init js_sw_init_corr(int num_axes, int mode, int number, struct js break; - case JS_SW_MODE_PP: + case JS_SW_TYPE_PP: + case JS_SW_TYPE_FFP: corr[i][2].type = JS_CORR_BROKEN; - corr[i][2].prec = 1; - corr[i][2].coef[0] = 63 - 4; - corr[i][2].coef[1] = 64 + 4; - corr[i][2].coef[2] = (1 << 29) / (63 - 4); - corr[i][2].coef[3] = (1 << 29) / (63 - 4); + corr[i][2].prec = 0; + corr[i][2].coef[0] = 31 - 2; + corr[i][2].coef[1] = 32 + 2; + corr[i][2].coef[2] = (1 << 29) / (31 - 2); + corr[i][2].coef[3] = (1 << 29) / (31 - 2); corr[i][3].type = JS_CORR_BROKEN; - corr[i][3].prec = 0; - corr[i][3].coef[0] = 31 - 2; - corr[i][3].coef[1] = 32 + 2; - corr[i][3].coef[2] = (1 << 29) / (31 - 2); - corr[i][3].coef[3] = (1 << 29) / (31 - 2); + corr[i][3].prec = 1; + corr[i][3].coef[0] = 63 - 4; + corr[i][3].coef[1] = 64 + 4; + corr[i][3].coef[2] = (1 << 29) / (63 - 4); + corr[i][3].coef[3] = (1 << 29) / (63 - 4); j = 4; break; + case JS_SW_TYPE_FFW: + + corr[i][0].type = JS_CORR_BROKEN; + corr[i][0].prec = 2; + corr[i][0].coef[0] = 511 - 8; + corr[i][0].coef[1] = 512 + 8; + corr[i][0].coef[2] = (1 << 29) / (511 - 8); + corr[i][0].coef[3] = (1 << 29) / (511 - 8); + + corr[i][1].type = JS_CORR_BROKEN; + corr[i][1].prec = 1; + corr[i][1].coef[0] = 63; + corr[i][1].coef[1] = 63; + corr[i][1].coef[2] = (1 << 29) / -63; + corr[i][1].coef[3] = (1 << 29) / -63; + + corr[i][2].type = JS_CORR_BROKEN; + corr[i][2].prec = 1; + corr[i][2].coef[0] = 63; + corr[i][2].coef[1] = 63; + corr[i][2].coef[2] = (1 << 29) / -63; + corr[i][2].coef[3] = (1 << 29) / -63; + + j = 3; + + break; + + case JS_SW_TYPE_FSP: + + corr[i][2].type = JS_CORR_BROKEN; + corr[i][2].prec = 0; + corr[i][2].coef[0] = 31 - 2; + corr[i][2].coef[1] = 32 + 2; + corr[i][2].coef[2] = (1 << 29) / (31 - 2); + corr[i][2].coef[3] = (1 << 29) / (31 - 2); + + j = 3; + + break; + default: j = 0; - } - for (; j < num_axes; j++) { + for (; j < num_axes; j++) { /* Hats & other binary axes */ corr[i][j].type = JS_CORR_BROKEN; corr[i][j].prec = 0; corr[i][j].coef[0] = 0; @@ -358,83 +609,211 @@ static void __init js_sw_init_corr(int num_axes, int mode, int number, struct js } /* + * js_sw_print_packet() prints the contents of a SideWinder packet. + */ + +static void js_sw_print_packet(char *name, int length, unsigned char *buf, char bits) +{ + int i; + + printk("joy-sidewinder: %s packet, %d bits. [", name, length); + for (i = (((length + 3) >> 2) - 1); i >= 0; i--) + printk("%x", (int)js_sw_get_bits(buf, i << 2, 4, 0, bits)); + printk("]\n"); +} + +/* + * js_sw_3dp_id() translates the 3DP id into a human legible string. + * Unfortunately I don't know how to do this for the other SW types. + */ + +static void js_sw_3dp_id(unsigned char *buf, char *comment) +{ + int i; + char pnp[8], rev[9]; + + for (i = 0; i < 7; i++) /* ASCII PnP ID */ + pnp[i] = js_sw_get_bits(buf, 24+8*i, 8, 0, 1); + + for (i = 0; i < 8; i++) /* ASCII firmware revision */ + rev[i] = js_sw_get_bits(buf, 88+8*i, 8, 0, 1); + + pnp[7] = rev[8] = 0; + + sprintf(comment, " [PnP %d.%02d id %s rev %s]", + (int) (js_sw_get_bits(buf, 8, 6, 6, 1) | /* Two 6-bit values */ + js_sw_get_bits(buf, 16, 6, 0, 1)) / 100, + (int) (js_sw_get_bits(buf, 8, 6, 6, 1) | + js_sw_get_bits(buf, 16, 6, 0, 1)) % 100, + pnp, rev); +} + +/* + * js_sw_guess_mode() checks the upper two button bits for toggling - + * indication of that the joystick is in 3-bit mode. This is documented + * behavior for 3DP ID packet, and for example the FSP does this in + * normal packets instead. Fun ... + */ + +static int js_sw_guess_mode(unsigned char *buf, int len) +{ + int i; + unsigned char xor = 0; + for (i = 1; i < len; i++) xor |= (buf[i - 1] ^ buf[i]) & 6; + return !!xor * 2 + 1; +} + +/* * js_sw_probe() probes for SideWinder type joysticks. */ static struct js_port __init *js_sw_probe(int io, struct js_port *port) { struct js_sw_info info; - char *name; - int i, j, axes, buttons; - __u64 data; - unsigned char u; - + char *names[] = {NULL, "SideWinder 3D Pro", "Flight2000 F-23", "SideWinder GamePad", "SideWinder Precision Pro", + "SideWinder Force Feedback Pro", "SideWinder FreeStyle Pro", "SideWinder Force Feedback Wheel" }; + char axes[] = { 0, 6, 6, 2, 6, 6, 5, 3 }; + char buttons[] = { 0, 9, 9, 10, 9, 9, 10, 8 }; + int i, j, k, l, speed; + unsigned char buf[JS_SW_LENGTH]; + unsigned char idbuf[JS_SW_LENGTH]; + unsigned char m = 1; + char comment[40]; + + comment[0] = 0; if (check_region(io, 1)) return port; - if (((u = inb(io)) & 3) == 3) return port; - outb(0xff,io); - if (!((inb(io) ^ u) & ~u & 0xf)) return port; - i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data); + speed = js_sw_measure_speed(io); + + i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Read normal packet */ + m |= js_sw_guess_mode(buf, i); /* Data packet (1-bit) can carry mode info [FSP] */ + udelay(JS_SW_TIMEOUT); - if (!i) { - udelay(JS_SW_MIN_TIME); - js_sw_init_digital(io); - udelay(JS_SW_MAX_TIME); - i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data); +#ifdef JS_SW_DEBUG + printk(KERN_DEBUG "joy-sidewinder: Init 1: Mode %d. Length %d.\n", m , i); +#endif + + if (!i) { /* No data. 3d Pro analog mode? */ + js_sw_init_digital(io, speed); /* Switch to digital */ + udelay(JS_SW_TIMEOUT); + i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Retry reading packet */ + udelay(JS_SW_TIMEOUT); +#ifdef JS_SW_DEBUG + printk(KERN_DEBUG "joy-sidewinder: Init 1b: Length %d.\n", i); +#endif + if (!i) return port; /* No data -> FAIL */ } - switch (i) { - case 0: - return port; - case 5: - case 10: - case 15: - case 20: - case 30: - case 45: - case 60: - info.mode = JS_SW_MODE_GP; - outb(0xff,io); /* Kick into 3-bit mode */ - udelay(JS_SW_MAX_TIME); - i = js_sw_read_packet(io, 60, -1, JS_SW_EXT_STROBE, &data); /* Get total length */ - udelay(JS_SW_MIN_TIME); - j = js_sw_read_packet(io, 15, -1, JS_SW_MIN_STROBE, &data); /* Get subpacket length */ - if (!i || !j) { - printk(KERN_WARNING "joy-sidewinder: SideWinder GamePad detected (%d,%d)," - " but not idenfitied.\n", i, j); - return port; + j = js_sw_read_packet(io, speed, idbuf, JS_SW_LENGTH, i); /* Read ID. This initializes the stick */ + m |= js_sw_guess_mode(idbuf, j); /* ID packet should carry mode info [3DP] */ + +#ifdef JS_SW_DEBUG + printk(KERN_DEBUG "joy-sidewinder: Init 2: Mode %d. ID Length %d.\n", m , j); +#endif + + if (!j) { /* Read ID failed. Happens in 1-bit mode on PP */ + udelay(JS_SW_TIMEOUT); + i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Retry reading packet */ +#ifdef JS_SW_DEBUG + printk(KERN_DEBUG "joy-sidewinder: Init 2b: Mode %d. Length %d.\n", m , i); +#endif + if (!i) return port; + udelay(JS_SW_TIMEOUT); + j = js_sw_read_packet(io, speed, idbuf, JS_SW_LENGTH, i);/* Retry reading ID */ +#ifdef JS_SW_DEBUG + printk(KERN_DEBUG "joy-sidewinder: Init 2c: ID Length %d.\n", j); +#endif + + } + + k = JS_SW_FAIL; /* Try JS_SW_FAIL times */ + l = 0; + + do { + k--; + udelay(JS_SW_TIMEOUT); + i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Read data packet */ +#ifdef JS_SW_DEBUG + printk(KERN_DEBUG "joy-sidewinder: Init 3: Length %d.\n", i); +#endif + + if (i > l) { /* Longer? As we can only lose bits, it makes */ + /* no sense to try detection for a packet shorter */ + l = i; /* than the previous one */ + + info.number = 1; + info.io = io; + info.speed = speed; + info.length = i; + info.bits = m; + info.fail = 0; + info.ok = 0; + info.type = 0; + + switch (i * m) { + case 60: + info.number++; + case 45: /* Ambiguous packet length */ + if (j <= 40) { /* ID length less or eq 40 -> FSP */ + case 43: + info.type = JS_SW_TYPE_FSP; + break; + } + info.number++; + case 30: + info.number++; + case 15: + info.type = JS_SW_TYPE_GP; + break; + case 33: + case 31: + info.type = JS_SW_TYPE_FFW; + break; + case 48: /* Ambiguous */ + if (j == 14) { /* ID lenght 14*3 -> FFP */ + info.type = JS_SW_TYPE_FFP; + sprintf(comment, " [AC %s]", js_sw_get_bits(idbuf,38,1,0,3) ? "off" : "on"); + } else + info.type = JS_SW_TYPE_PP; + break; + case 198: + info.length = 22; + case 64: + info.type = JS_SW_TYPE_3DP; + if (j == 160) js_sw_3dp_id(idbuf, comment); + break; } - info.number = i / j; - axes = 2; buttons = 10; name = "SideWinder GamePad"; - break; - case 16: - case 48: - info.mode = JS_SW_MODE_PP; info.number = 1; - axes = 6; buttons = 9; name = "SideWinder Precision Pro"; - break; - case 64: - case 66: - info.mode = JS_SW_MODE_3DP; info.number = 1; info.optimize = 0; - axes = 6; buttons = 8; name = "SideWinder 3D Pro"; - break; - case 72: - return port; - default: - printk(KERN_WARNING "joy-sidewinder: unknown joystick device detected " - "(io=%#x, count=%d, data=0x%08x%08x), contact <vojtech@suse.cz>\n", - io, i, (int)(data >> 32), (int)(data & 0xffffffff)); - return port; + } + + } while (k && !info.type); + + if (!info.type) { + printk(KERN_WARNING "joy-sidewinder: unknown joystick device detected " + "(io=%#x), contact <vojtech@suse.cz>\n", io); + js_sw_print_packet("ID", j * 3, idbuf, 3); + js_sw_print_packet("Data", i * m, buf, m); + return port; } - info.io = io; +#ifdef JS_SW_DEBUG + js_sw_print_packet("ID", j * 3, idbuf, 3); + js_sw_print_packet("Data", i * m, buf, m); +#endif + + k = i; request_region(io, 1, "joystick (sidewinder)"); + port = js_register_port(port, &info, info.number, sizeof(struct js_sw_info), js_sw_read); + for (i = 0; i < info.number; i++) - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, i, axes, buttons, name, js_sw_open, js_sw_close), name, io); - js_sw_init_corr(axes, info.mode, info.number, port->corr); + printk(KERN_INFO "js%d: %s%s at %#x [%d ns res %d-bit id %d data %d]\n", + js_register_device(port, i, axes[info.type], buttons[info.type], + names[info.type], js_sw_open, js_sw_close), names[info.type], comment, io, + 1000000 / speed, m, j, k); + + js_sw_init_corr(axes[info.type], info.type, info.number, port->corr); return port; } @@ -463,9 +842,9 @@ void cleanup_module(void) int i; struct js_sw_info *info; - while (js_sw_port != NULL) { + while (js_sw_port) { for (i = 0; i < js_sw_port->ndevs; i++) - if (js_sw_port->devs[i] != NULL) + if (js_sw_port->devs[i]) js_unregister_device(js_sw_port->devs[i]); info = js_sw_port->info; release_region(info->io, 1); diff --git a/drivers/char/joystick/joy-spaceball.c b/drivers/char/joystick/joy-spaceball.c new file mode 100644 index 000000000..421a3cf8f --- /dev/null +++ b/drivers/char/joystick/joy-spaceball.c @@ -0,0 +1,347 @@ +/* + * joy-spaceball.c Version 0.1 + * + * Copyright (c) 1998 David Thompson + * Copyright (c) 1999 Vojtech Pavlik + * Copyright (c) 1999 Joseph Krahn + * + * Sponsored by SuSE + */ + +/* + * This is a module for the Linux joystick driver, supporting + * the SpaceTec SpaceBall 4000 FLX. + */ + +/* + * 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@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <asm/system.h> +#include <linux/errno.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/tty.h> +#include <linux/init.h> + +/* + * Constants. + */ + +#define N_JOYSTICK_SBALL 12 +#define JS_SBALL_MAX_LENGTH 128 + +/* + * List of SpaceBalls. + */ + +static struct js_port* js_sball_port = NULL; + +/* + * Per-Ball data. + */ + +struct js_sball_info { + struct tty_struct* tty; + struct js_port* port; + int idx; + unsigned char data[JS_SBALL_MAX_LENGTH]; + int js; + char used; +}; + +/* + * js_sball_process_packet() decodes packets the driver receives from the + * SpaceBall. + */ + +static void js_sball_process_packet(struct js_sball_info* info) +{ + int i,b; + int **axes = info->port->axes; + int **buttons = info->port->buttons; + unsigned char *data = info->data; + + if (info->idx < 2) return; + + switch (info->data[0]) { + + case '@': /* Reset packet */ + info->data[info->idx - 1] = 0; + for (i = 1; i < info->idx && info->data[i] == ' '; i++); + + printk(KERN_INFO "js%d: SpaceBall 4000FLX [%s] on %s%d\n", + info->js, info->data + i, info->tty->driver.name, + MINOR(info->tty->device) - info->tty->driver.minor_start); + + memset(axes[0], 0, sizeof(int) * 6); /* All axes, buttons should be zero */ + buttons[0][0] = 0; + break; + + case 'D': /* Ball data */ + if (info->idx != 16) return; + if (!info->port->devs[0]) return; + axes[0][0] = ((data[3] << 8) | data[4] ); + axes[0][1] = ((data[5] << 8) | data[6] ); + axes[0][2] = ((data[7] << 8) | data[8] ); + axes[0][3] = ((data[9] << 8) | data[10]); + axes[0][4] = ((data[11]<< 8) | data[12]); + axes[0][5] = ((data[13]<< 8) | data[14]); + for(i = 0; i < 6; i ++) if (axes[0][i] & 0x8000) axes[0][i] -= 0x10000; + break; + + case 'K': /* Button data, part1 */ + /* We can ignore this packet for the SB 4000FLX. */ + break; + + case '.': /* Button data, part2 */ + if (info->idx != 4) return; + if (!info->port->devs[0]) return; + b = (data[1] & 0xbf) << 8 | (data[2] & 0xbf); + buttons[0][0] = ((b & 0x1f80) >> 1 | (b & 0x3f)); + break; + + case '?': /* Error packet */ + info->data[info->idx - 1] = 0; + printk(KERN_ERR "joy-spaceball: Device error. [%s]\n",info->data+1); + break; + + case 'A': /* reply to A command (ID# report) */ + case 'B': /* reply to B command (beep) */ + case 'H': /* reply to H command (firmware report) */ + case 'S': /* reply to S command (single beep) */ + case 'Y': /* reply to Y command (scale flag) */ + case '"': /* reply to "n command (report info, part n) */ + break; + + case 'P': /* Pulse (update) speed */ + if (info->idx != 3) return; /* data[2],data[3] = hex digits for speed 00-FF */ + break; + + default: + printk("joy-spaceball: Unknown packet %d length %d:", data[0], info->idx); + for (i = 0; i < info->idx; i++) printk(" %02x", data[i]); + printk("\n"); + return; + } +} + +/* + * js_sball_open() is a callback from the joystick device open routine. + */ + +static int js_sball_open(struct js_dev *jd) +{ + struct js_sball_info *info = jd->port->info; + info->used++; + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_sball_close() is a callback from the joystick device release routine. + */ + +static int js_sball_close(struct js_dev *jd) +{ + struct js_sball_info *info = jd->port->info; + if (!--info->used) { + js_unregister_device(jd->port->devs[0]); + js_sball_port = js_unregister_port(jd->port); + } + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_sball_init_corr() initializes the correction values for the SpaceBall. + */ + +static void __init js_sball_init_corr(struct js_corr **corr) +{ + int j; + + for (j = 0; j < 3; j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 0; + corr[0][j].coef[0] = 0; + corr[0][j].coef[1] = 0; + corr[0][j].coef[2] = 50000; + corr[0][j].coef[3] = 50000; + } + for (j = 3; j < 6; j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 0; + corr[0][j].coef[0] = 0; + corr[0][j].coef[1] = 0; + corr[0][j].coef[2] = 300000; + corr[0][j].coef[3] = 300000; + } +} + +/* + * js_sball_ldisc_open() is the routine that is called upon setting our line + * discipline on a tty. + */ + +static int js_sball_ldisc_open(struct tty_struct *tty) +{ + struct js_sball_info iniinfo; + struct js_sball_info *info = &iniinfo; + + info->tty = tty; + info->idx = 0; + info->used = 1; + + js_sball_port = js_register_port(js_sball_port, info, 1, sizeof(struct js_sball_info), NULL); + + info = js_sball_port->info; + info->port = js_sball_port; + tty->disc_data = info; + + info->js = js_register_device(js_sball_port, 0, 6, 12, "SpaceBall 4000 FLX", js_sball_open, js_sball_close); + + js_sball_init_corr(js_sball_port->corr); + + MOD_INC_USE_COUNT; + + return 0; +} + +/* + * js_sball_ldisc_close() is the opposite of js_sball_ldisc_open() + */ + +static void js_sball_ldisc_close(struct tty_struct *tty) +{ + struct js_sball_info* info = (struct js_sball_info*) tty->disc_data; + if (!--info->used) { + js_unregister_device(info->port->devs[0]); + js_sball_port = js_unregister_port(info->port); + } + MOD_DEC_USE_COUNT; +} + +/* + * js_sball_ldisc_receive() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static void js_sball_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct js_sball_info* info = (struct js_sball_info*) tty->disc_data; + int i; + int esc_flag = 0; + +/* + * Spaceball 4000 FLX packets all start with a one letter packet-type decriptor, + * and end in 0x0d. It uses '^' as an escape for 0x0d characters which can + * occur in the axis values. ^M, ^Q and ^S all mean 0x0d, depending (I think) + * on whether the axis value is increasing, decreasing, or same as before. + * (I don't see why this is useful). + * + * There may be a nicer whay to handle the escapes, but I wanted to be sure to + * allow for an escape at the end of the buffer. + */ + for (i = 0; i < count; i++) { + if (esc_flag) { /* If the last char was an escape, overwrite it with the escaped value */ + + switch (cp[i]){ + case 'M': + case 'Q': + case 'S': + info->data[info->idx]=0x0d; + break; + case '^': /* escaped escape; leave as is */ + break; + default: + printk("joy-spaceball: Unknown escape character: %02x\n", cp[i]); + } + + esc_flag = 0; + + } else { + + if (info->idx < JS_SBALL_MAX_LENGTH) + info->data[info->idx++] = cp[i]; + + if (cp[i] == 0x0D) { + if (info->idx) + js_sball_process_packet(info); + info->idx = 0; + } else + if (cp[i] == '^') esc_flag = 1; + + } + } +} + +/* + * js_sball_ldisc_room() reports how much room we do have for receiving data. + * Although we in fact have infinite room, we need to specify some value + * here, so why not the size of our packet buffer. It's big anyway. + */ + +static int js_sball_ldisc_room(struct tty_struct *tty) +{ + return JS_SBALL_MAX_LENGTH; +} + +/* + * The line discipline structure. + */ + +static struct tty_ldisc js_sball_ldisc = { + magic: TTY_LDISC_MAGIC, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + name: "spaceball", +#endif + open: js_sball_ldisc_open, + close: js_sball_ldisc_close, + receive_buf: js_sball_ldisc_receive, + receive_room: js_sball_ldisc_room, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +#ifdef MODULE +int init_module(void) +#else +int __init js_sball_init(void) +#endif +{ + if (tty_register_ldisc(N_JOYSTICK_SBALL, &js_sball_ldisc)) { + printk(KERN_ERR "joy-spaceball: Error registering line discipline.\n"); + return -ENODEV; + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + tty_register_ldisc(N_JOYSTICK_SBALL, NULL); +} +#endif diff --git a/drivers/char/joystick/joy-spaceorb.c b/drivers/char/joystick/joy-spaceorb.c new file mode 100644 index 000000000..464e59006 --- /dev/null +++ b/drivers/char/joystick/joy-spaceorb.c @@ -0,0 +1,305 @@ +/* + * joy-spaceorb.c Version 0.1 + * + * Copyright (c) 1998 David Thompson + * Copyright (c) 1999 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This is a module for the Linux joystick driver, supporting + * the SpaceTec SpaceOrb 360 and SpaceBall Avenger 6dof controllers. + */ + +/* + * 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@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <asm/system.h> +#include <linux/errno.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/tty.h> +#include <linux/init.h> + +/* + * Constants. + */ + +#define N_JOYSTICK_ORB 15 +#define JS_ORB_MAX_LENGTH 64 + +/* + * List of SpaceOrbs. + */ + +static struct js_port* js_orb_port = NULL; + +/* + * Per-Orb data. + */ + +struct js_orb_info { + struct tty_struct* tty; + struct js_port* port; + int idx; + unsigned char data[JS_ORB_MAX_LENGTH]; + int js; + char used; +}; + +static unsigned char js_orb_xor[] = "SpaceWare"; + +static unsigned char *js_orb_errors[] = { "EEPROM storing 0 failed", "Receive queue overflow", "Transmit queue timeout", + "Bad packet", "Power brown-out", "EEPROM checksum error", "Hardware fault" }; + +/* + * js_orb_process_packet() decodes packets the driver receives from the + * SpaceOrb. + */ + +static void js_orb_process_packet(struct js_orb_info* info) +{ + int i; + int **axes = info->port->axes; + int **buttons = info->port->buttons; + unsigned char *data = info->data; + unsigned char c = 0; + + if (info->idx < 2) return; + for (i = 0; i < info->idx; i++) c ^= data[i]; + if (c) return; + + switch (info->data[0]) { + + case 'R': /* Reset packet */ + info->data[info->idx - 1] = 0; + for (i = 1; i < info->idx && info->data[i] == ' '; i++); + printk(KERN_INFO "js%d: SpaceOrb 360 [%s] on %s%d\n", + info->js, info->data + i, info->tty->driver.name, + MINOR(info->tty->device) - info->tty->driver.minor_start); + break; + + case 'D': /* Ball + button data */ + if (info->idx != 12) return; + if (!info->port->devs[0]) return; + for (i = 0; i < 9; i++) info->data[i+2] ^= js_orb_xor[i]; + axes[0][0] = ( data[2] << 3) | (data[ 3] >> 4); + axes[0][1] = ((data[3] & 0x0f) << 6) | (data[ 4] >> 1); + axes[0][2] = ((data[4] & 0x01) << 9) | (data[ 5] << 2) | (data[4] >> 5); + axes[0][3] = ((data[6] & 0x1f) << 5) | (data[ 7] >> 2); + axes[0][4] = ((data[7] & 0x03) << 8) | (data[ 8] << 1) | (data[7] >> 6); + axes[0][5] = ((data[9] & 0x3f) << 4) | (data[10] >> 3); + for(i = 0; i < 6; i ++) if (axes[0][i] & 0x200) axes[0][i] -= 1024; + buttons[0][0] = data[1]; + break; + + case 'K': /* Button data */ + if (info->idx != 5) return; + if (!info->port->devs[0]) return; + buttons[0][0] = data[2]; + break; + + case 'E': /* Error packet */ + if (info->idx != 4) return; + printk(KERN_ERR "joy-spaceorb: Device error. [ "); + for (i = 0; i < 7; i++) + if (data[1] & (1 << i)) + printk("%s ", js_orb_errors[i]); + printk("]\n"); + break; + + case 'N': /* Null region */ + if (info->idx != 3) return; + break; + + case 'P': /* Pulse (update) speed */ + if (info->idx != 4) return; + break; + + default: + printk("joy-spaceorb: Unknown packet %d length %d:", data[0], info->idx); + for (i = 0; i < info->idx; i++) printk(" %02x", data[i]); + printk("\n"); + return; + } +} + +/* + * js_orb_open() is a callback from the joystick device open routine. + */ + +static int js_orb_open(struct js_dev *jd) +{ + struct js_orb_info *info = jd->port->info; + info->used++; + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_orb_close() is a callback from the joystick device release routine. + */ + +static int js_orb_close(struct js_dev *jd) +{ + struct js_orb_info *info = jd->port->info; + if (!--info->used) { + js_unregister_device(jd->port->devs[0]); + js_orb_port = js_unregister_port(jd->port); + } + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_orb_init_corr() initializes the correction values for the SpaceOrb. + */ + +static void __init js_orb_init_corr(struct js_corr **corr) +{ + int j; + + for (j = 0; j < 6; j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 0; + corr[0][j].coef[0] = 0 ; + corr[0][j].coef[1] = 0 ; + corr[0][j].coef[2] = (1 << 29) / 511; + corr[0][j].coef[3] = (1 << 29) / 511; + } +} + +/* + * js_orb_ldisc_open() is the routine that is called upon setting our line + * discipline on a tty. + */ + +static int js_orb_ldisc_open(struct tty_struct *tty) +{ + struct js_orb_info iniinfo; + struct js_orb_info *info = &iniinfo; + + info->tty = tty; + info->idx = 0; + info->used = 1; + + js_orb_port = js_register_port(js_orb_port, info, 1, sizeof(struct js_orb_info), NULL); + + info = js_orb_port->info; + info->port = js_orb_port; + tty->disc_data = info; + + info->js = js_register_device(js_orb_port, 0, 6, 7, "SpaceOrb 360", js_orb_open, js_orb_close); + + js_orb_init_corr(js_orb_port->corr); + + MOD_INC_USE_COUNT; + + return 0; +} + +/* + * js_orb_ldisc_close() is the opposite of js_orb_ldisc_open() + */ + +static void js_orb_ldisc_close(struct tty_struct *tty) +{ + struct js_orb_info* info = (struct js_orb_info*) tty->disc_data; + if (!--info->used) { + js_unregister_device(info->port->devs[0]); + js_orb_port = js_unregister_port(info->port); + } + MOD_DEC_USE_COUNT; +} + +/* + * js_orb_ldisc_receive() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static void js_orb_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct js_orb_info* info = (struct js_orb_info*) tty->disc_data; + int i; + + for (i = 0; i < count; i++) { + if (~cp[i] & 0x80) { + if (info->idx) js_orb_process_packet(info); + info->idx = 0; + } + if (info->idx < JS_ORB_MAX_LENGTH) + info->data[info->idx++] = cp[i] & 0x7f; + } +} + +/* + * js_orb_ldisc_room() reports how much room we do have for receiving data. + * Although we in fact have infinite room, we need to specify some value + * here, so why not the size of our packet buffer. It's big anyway. + */ + +static int js_orb_ldisc_room(struct tty_struct *tty) +{ + return JS_ORB_MAX_LENGTH; +} + +/* + * The line discipline structure. + */ + +static struct tty_ldisc js_orb_ldisc = { + magic: TTY_LDISC_MAGIC, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + name: "spaceorb", +#endif + open: js_orb_ldisc_open, + close: js_orb_ldisc_close, + receive_buf: js_orb_ldisc_receive, + receive_room: js_orb_ldisc_room, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +#ifdef MODULE +int init_module(void) +#else +int __init js_orb_init(void) +#endif +{ + if (tty_register_ldisc(N_JOYSTICK_ORB, &js_orb_ldisc)) { + printk(KERN_ERR "joy-spaceorb: Error registering line discipline.\n"); + return -ENODEV; + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + tty_register_ldisc(N_JOYSTICK_ORB, NULL); +} +#endif diff --git a/drivers/char/joystick/joy-thrustmaster.c b/drivers/char/joystick/joy-thrustmaster.c index 1e79b3281..4cd3de23f 100644 --- a/drivers/char/joystick/joy-thrustmaster.c +++ b/drivers/char/joystick/joy-thrustmaster.c @@ -1,7 +1,9 @@ /* * joy-thrustmaster.c Version 1.2 * - * Copyright (c) 1998 Vojtech Pavlik + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -38,28 +40,15 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/string.h> +#include <linux/init.h> #define JS_TM_MAX_START 400 -#define JS_TM_MAX_STROBE 25 +#define JS_TM_MAX_STROBE 45 #define JS_TM_MAX_LENGTH 13 #define JS_TM_MODE_M3DI 1 #define JS_TM_MODE_3DRP 3 -#define JS_TM_MODE_WCS3 4 - -#define JS_TM_MODE_MAX 5 /* Last mode + 1 */ - -#define JS_TM_BYTE_A0 0 -#define JS_TM_BYTE_A1 1 -#define JS_TM_BYTE_A2 3 -#define JS_TM_BYTE_A3 4 -#define JS_TM_BYTE_A4 6 -#define JS_TM_BYTE_A5 7 - -#define JS_TM_BYTE_D0 2 -#define JS_TM_BYTE_D1 5 -#define JS_TM_BYTE_D2 8 -#define JS_TM_BYTE_D3 9 +#define JS_TM_MODE_FGP 163 #define JS_TM_BYTE_ID 10 #define JS_TM_BYTE_REV 11 @@ -68,48 +57,39 @@ static int js_tm_port_list[] __initdata = {0x201, 0}; static struct js_port* js_tm_port __initdata = NULL; +static unsigned char js_tm_byte_a[16] = { 0, 1, 3, 4, 6, 7 }; +static unsigned char js_tm_byte_d[16] = { 2, 5, 8, 9 }; + struct js_tm_info { int io; unsigned char mode; }; -static int js_tm_id_to_def[JS_TM_MODE_MAX] = {0x00, 0x42, 0x00, 0x22, 0x00}; - /* * js_tm_read_packet() reads a ThrustMaster packet. */ static int js_tm_read_packet(int io, unsigned char *data) { - unsigned int t, t1; + unsigned int t, p; unsigned char u, v, error; int i, j; unsigned long flags; - int start = (js_time_speed * JS_TM_MAX_START) >> 10; - int strobe = (js_time_speed * JS_TM_MAX_STROBE) >> 10; - error = 0; i = j = 0; + p = t = JS_TM_MAX_START; __save_flags(flags); __cli(); outb(0xff,io); - - t = js_get_time(); - - do { - u = inb(io); - t1 = js_get_time(); - } while ((u & 1) && js_delta(t1, t) < start); - - t = t1; - u >>= 4; + + v = inb(io) >> 4; do { - v = inb(io) >> 4; - t1 = js_get_time(); - if ((u ^ v) & u & 2) { + t--; + u = v; v = inb(io) >> 4; + if (~v & u & 2) { if (j) { if (j < 9) { /* Data bit */ data[i] |= (~v & 1) << (j - 1); @@ -124,10 +104,9 @@ static int js_tm_read_packet(int io, unsigned char *data) error |= ~v & 1; j++; } - t = t1; + p = t = (p - t) << 1; } - u = v; - } while (!error && i < JS_TM_MAX_LENGTH && js_delta(t1,t) < strobe); + } while (!error && i < JS_TM_MAX_LENGTH && t > 0); __restore_flags(flags); @@ -142,46 +121,39 @@ static int js_tm_read(void *xinfo, int **axes, int **buttons) { struct js_tm_info *info = xinfo; unsigned char data[JS_TM_MAX_LENGTH]; + int i; - if (js_tm_read_packet(info->io, data)) { - printk(KERN_WARNING "joy-thrustmaster: failed to read data packet\n"); - return -1; - } - if (data[JS_TM_BYTE_ID] != info->mode) { - printk(KERN_WARNING "joy-thrustmaster: ID (%d) != mode (%d)\n", - data[JS_TM_BYTE_ID], info->mode); - return -1; - } - if (data[JS_TM_BYTE_DEF] != js_tm_id_to_def[info->mode]) { - printk(KERN_WARNING "joy-thrustmaster: DEF (%d) != def(mode) (%d)\n", - data[JS_TM_BYTE_DEF], js_tm_id_to_def[info->mode]); - return -1; - } + if (js_tm_read_packet(info->io, data)) return -1; + if (data[JS_TM_BYTE_ID] != info->mode) return -1; + + for (i = 0; i < data[JS_TM_BYTE_DEF] >> 4; i++) axes[0][i] = data[js_tm_byte_a[i]]; switch (info->mode) { case JS_TM_MODE_M3DI: - axes[0][0] = data[JS_TM_BYTE_A0]; - axes[0][1] = data[JS_TM_BYTE_A1]; - axes[0][2] = data[JS_TM_BYTE_A2]; - axes[0][3] = data[JS_TM_BYTE_A3]; + axes[0][4] = ((data[js_tm_byte_d[0]] >> 3) & 1) - ((data[js_tm_byte_d[0]] >> 1) & 1); + axes[0][5] = ((data[js_tm_byte_d[0]] >> 2) & 1) - ( data[js_tm_byte_d[0]] & 1); - axes[0][4] = ((data[JS_TM_BYTE_D0] >> 3) & 1) - ((data[JS_TM_BYTE_D0] >> 1) & 1); - axes[0][5] = ((data[JS_TM_BYTE_D0] >> 2) & 1) - ( data[JS_TM_BYTE_D0] & 1); - - buttons[0][0] = ((data[JS_TM_BYTE_D0] >> 6) & 0x01) | ((data[JS_TM_BYTE_D0] >> 3) & 0x06) - | ((data[JS_TM_BYTE_D0] >> 4) & 0x08) | ((data[JS_TM_BYTE_D1] >> 2) & 0x30); + buttons[0][0] = ((data[js_tm_byte_d[0]] >> 6) & 0x01) | ((data[js_tm_byte_d[0]] >> 3) & 0x06) + | ((data[js_tm_byte_d[0]] >> 4) & 0x08) | ((data[js_tm_byte_d[1]] >> 2) & 0x30); return 0; case JS_TM_MODE_3DRP: + case JS_TM_MODE_FGP: + + buttons[0][0] = (data[js_tm_byte_d[0]] & 0x3f) | ((data[js_tm_byte_d[1]] << 6) & 0xc0) + | (( ((int) data[js_tm_byte_d[0]]) << 2) & 0x300); + + return 0; + + default: - axes[0][0] = data[JS_TM_BYTE_A0]; - axes[0][1] = data[JS_TM_BYTE_A1]; + buttons[0][0] = 0; - buttons[0][0] = ( data[JS_TM_BYTE_D0] & 0x3f) | ((data[JS_TM_BYTE_D1] << 6) & 0xc0) - | (( ((int) data[JS_TM_BYTE_D0]) << 2) & 0x300); + for (i = 0; i < (data[JS_TM_BYTE_DEF] & 0xf); i++) + buttons[0][0] |= ((int) data[js_tm_byte_d[i]]) << (i << 3); return 0; @@ -217,9 +189,9 @@ static int js_tm_close(struct js_dev *jd) static void __init js_tm_init_corr(int num_axes, int mode, int **axes, struct js_corr **corr) { - int j; + int j = 0; - for (j = 0; j < num_axes; j++) { + for (; j < num_axes; j++) { corr[0][j].type = JS_CORR_BROKEN; corr[0][j].prec = 0; corr[0][j].coef[0] = 127 - 2; @@ -230,8 +202,7 @@ static void __init js_tm_init_corr(int num_axes, int mode, int **axes, struct js switch (mode) { case JS_TM_MODE_M3DI: j = 4; break; - case JS_TM_MODE_3DRP: j = 2; break; - default: j = 0; break; + default: break; } for (; j < num_axes; j++) { @@ -252,48 +223,46 @@ static void __init js_tm_init_corr(int num_axes, int mode, int **axes, struct js static struct js_port __init *js_tm_probe(int io, struct js_port *port) { struct js_tm_info info; - char *names[JS_TM_MODE_MAX] = { NULL, "ThrustMaster Millenium 3D Inceptor", NULL, - "ThrustMaster Rage 3D Gamepad", "ThrustMaster WCS III" }; - char axes[JS_TM_MODE_MAX] = { 0, 6, 0, 2, 0 }; - char buttons[JS_TM_MODE_MAX] = { 0, 5, 0, 10, 0 }; - + struct js_rm_models { + unsigned char id; + char *name; + char axes; + char buttons; + } models[] = { { 1, "ThrustMaster Millenium 3D Inceptor", 6, 6 }, + { 3, "ThrustMaster Rage 3D Gamepad", 2, 10 }, + { 163, "Thrustmaster Fusion GamePad", 2, 10 }, + { 0, NULL, 0, 0 }}; + char name[64]; unsigned char data[JS_TM_MAX_LENGTH]; - unsigned char u; + unsigned char a, b; + int i; if (check_region(io, 1)) return port; - if (((u = inb(io)) & 3) == 3) return port; - outb(0xff,io); - if (!((inb(io) ^ u) & ~u & 0xf)) return port; - - if(js_tm_read_packet(io, data)) { - printk(KERN_WARNING "joy-thrustmaster: probe - can't read packet\n"); - return port; - } + if (js_tm_read_packet(io, data)) return port; info.io = io; info.mode = data[JS_TM_BYTE_ID]; if (!info.mode) return port; - if (info.mode >= JS_TM_MODE_MAX || !names[info.mode]) { - printk(KERN_WARNING "joy-thrustmaster: unknown device detected " - "(io=%#x, id=%d), contact <vojtech@suse.cz>\n", - io, info.mode); - return port; - } + for (i = 0; models[i].id && models[i].id != info.mode; i++); - if (data[JS_TM_BYTE_DEF] != js_tm_id_to_def[info.mode]) { - printk(KERN_WARNING "joy-thrustmaster: wrong DEF (%d) for ID %d - should be %d\n", - data[JS_TM_BYTE_DEF], info.mode, js_tm_id_to_def[info.mode]); + if (models[i].id != info.mode) { + a = data[JS_TM_BYTE_DEF] >> 4; + b = (data[JS_TM_BYTE_DEF] & 0xf) << 3; + sprintf(name, "Unknown %d-axis, %d-button TM device %d", a, b, info.mode); + } else { + sprintf(name, models[i].name); + a = models[i].axes; + b = models[i].buttons; } request_region(io, 1, "joystick (thrustmaster)"); port = js_register_port(port, &info, 1, sizeof(struct js_tm_info), js_tm_read); printk(KERN_INFO "js%d: %s revision %d at %#x\n", - js_register_device(port, 0, axes[info.mode], buttons[info.mode], - names[info.mode], js_tm_open, js_tm_close), names[info.mode], data[JS_TM_BYTE_REV], io); - js_tm_init_corr(axes[info.mode], info.mode, port->axes, port->corr); + js_register_device(port, 0, a, b, name, js_tm_open, js_tm_close), name, data[JS_TM_BYTE_REV], io); + js_tm_init_corr(a, info.mode, port->axes, port->corr); return port; } @@ -321,7 +290,7 @@ void cleanup_module(void) { struct js_tm_info *info; - while (js_tm_port != NULL) { + while (js_tm_port) { js_unregister_device(js_tm_port->devs[0]); info = js_tm_port->info; release_region(info->io, 1); diff --git a/drivers/char/joystick/joy-turbografx.c b/drivers/char/joystick/joy-turbografx.c index 6b30efe85..0211ea6f9 100644 --- a/drivers/char/joystick/joy-turbografx.c +++ b/drivers/char/joystick/joy-turbografx.c @@ -1,7 +1,9 @@ /* * joy-turbografx.c Version 1.2 * - * Copyright (c) 1998 Vojtech Pavlik + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -39,6 +41,7 @@ #include <linux/module.h> #include <linux/string.h> #include <linux/delay.h> +#include <linux/init.h> MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); @@ -57,18 +60,14 @@ MODULE_PARM(js_tg_3, "2-8i"); #define JS_TG_BUTTON4 0x01 #define JS_TG_BUTTON5 0x08 -static struct js_port* js_tg_port = NULL; +static struct js_port* js_tg_port __initdata = NULL; static int js_tg[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; static int js_tg_2[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; static int js_tg_3[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; struct js_tg_info { -#ifdef USE_PARPORT struct pardevice *port; /* parport device */ -#else - int port; /* hw port */ -#endif int sticks; /* joysticks connected */ }; @@ -109,9 +108,7 @@ int js_tg_open(struct js_dev *dev) struct js_tg_info *info = dev->port->info; if (!MOD_IN_USE) { -#ifdef USE_PARPORT if (parport_claim(info->port)) return -EBUSY; -#endif JS_PAR_CTRL_OUT(0x04, info->port); } MOD_INC_USE_COUNT; @@ -129,9 +126,7 @@ int js_tg_close(struct js_dev *dev) MOD_DEC_USE_COUNT; if (!MOD_IN_USE) { JS_PAR_CTRL_OUT(0x00, info->port); -#ifdef USE_PARPORT parport_release(info->port); -#endif } return 0; } @@ -142,16 +137,12 @@ void cleanup_module(void) struct js_tg_info *info; int i; - while (js_tg_port != NULL) { + while (js_tg_port) { for (i = 0; i < js_tg_port->ndevs; i++) - if (js_tg_port->devs[i] != NULL) + if (js_tg_port->devs[i]) js_unregister_device(js_tg_port->devs[i]); info = js_tg_port->info; -#ifdef USE_PARPORT parport_unregister_device(info->port); -#else - release_region(info->port, 3); -#endif js_tg_port = js_unregister_port(js_tg_port); } } @@ -186,33 +177,25 @@ static struct js_port __init *js_tg_probe(int *config, struct js_port *port) { struct js_tg_info iniinfo; struct js_tg_info *info = &iniinfo; + struct parport *pp; int i; if (config[0] < 0) return port; -#ifdef USE_PARPORT - { - struct parport *pp; - if (config[0] > 0x10) - for (pp=parport_enumerate(); pp != NULL && (pp->base!=config[0]); pp=pp->next); - else - for (pp=parport_enumerate(); pp != NULL && (config[0]>0); pp=pp->next) config[0]--; + if (config[0] > 0x10) + for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next); + else + for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--; - if (pp == NULL) { - printk(KERN_ERR "joy-tg: no such parport\n"); - return port; - } - - info->port = parport_register_device(pp, "joystick (turbografx)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); - if (!info->port) - return port; + if (!pp) { + printk(KERN_ERR "joy-tg: no such parport\n"); + return port; } -#else - info->port = config[0]; - if (check_region(info->port, 3)) return port; - request_region(info->port, 3, "joystick (turbografx)"); -#endif + + info->port = parport_register_device(pp, "joystick (turbografx)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + if (!info->port) + return port; port = js_register_port(port, info, 7, sizeof(struct js_tg_info), js_tg_read); info = port->info; @@ -221,24 +204,14 @@ static struct js_port __init *js_tg_probe(int *config, struct js_port *port) for (i = 0; i < 7; i++) if (config[i+1] > 0 && config[i+1] < 6) { -#ifdef USE_PARPORT printk(KERN_INFO "js%d: Multisystem joystick on %s\n", js_register_device(port, i, 2, config[i+1], "Multisystem joystick", js_tg_open, js_tg_close), info->port->port->name); -#else - printk(KERN_INFO "js%d: Multisystem joystick at %#x\n", - js_register_device(port, i, 2, config[i+1], "Multisystem joystick", js_tg_open, js_tg_close), - info->port); -#endif info->sticks |= (1 << i); } if (!info->sticks) { -#ifdef USE_PARPORT parport_unregister_device(info->port); -#else - release_region(info->port, 3); -#endif return port; } @@ -248,18 +221,30 @@ static struct js_port __init *js_tg_probe(int *config, struct js_port *port) } #ifndef MODULE -void __init js_tg_setup(char *str, int *ints) +int __init js_tg_setup(SETUP_PARAM) { int i; - - if (!strcmp(str,"js_tg")) - for (i = 0; i <= ints[0] && i < 2; i++) js_tg[i] = ints[i+1]; - if (!strcmp(str,"js_tg_2")) - for (i = 0; i <= ints[0] && i < 2; i++) js_tg_2[i] = ints[i+1]; - if (!strcmp(str,"js_tg_3")) - for (i = 0; i <= ints[0] && i < 2; i++) js_tg_3[i] = ints[i+1]; - + SETUP_PARSE(2); + for (i = 0; i <= ints[0] && i < 2; i++) js_tg[i] = ints[i+1]; + return 1; +} +int __init js_tg_setup_2(SETUP_PARAM) +{ + int i; + SETUP_PARSE(2); + for (i = 0; i <= ints[0] && i < 2; i++) js_tg_2[i] = ints[i+1]; + return 1; +} +int __init js_tg_setup_3(SETUP_PARAM) +{ + int i; + SETUP_PARSE(2); + for (i = 0; i <= ints[0] && i < 2; i++) js_tg_3[i] = ints[i+1]; + return 1; } +__setup("js_tg=", js_tg_setup); +__setup("js_tg_2=", js_tg_setup_2); +__setup("js_tg_3=", js_tg_setup_3); #endif #ifdef MODULE diff --git a/drivers/char/joystick/joy-warrior.c b/drivers/char/joystick/joy-warrior.c new file mode 100644 index 000000000..fd7be5787 --- /dev/null +++ b/drivers/char/joystick/joy-warrior.c @@ -0,0 +1,318 @@ +/* + * joy-warrior.c Version 0.1 + * + * Copyright (c) 1998 David Thompson + * Copyright (c) 1999 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This is a module for the Linux joystick driver, supporting + * the Logitech WingMan Warrior joystick. + */ + +/* + * This program is free warftware; 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@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <asm/system.h> +#include <linux/errno.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/init.h> + +/* + * Constants. + */ + +#define N_JOYSTICK_WAR 13 +#define JS_WAR_MAX_LENGTH 16 + +/* + * List of Warriors. + */ + +static struct js_port* js_war_port = NULL; + +static char js_war_lengths[] = { 0, 4, 12, 3, 4, 4, 0, 0 }; + +/* + * Per-Warrior data. + */ + +struct js_war_info { + struct tty_struct* tty; + struct js_port* port; + int idx; + int len; + unsigned char data[JS_WAR_MAX_LENGTH]; + char used; +}; + +/* + * js_war_process_packet() decodes packets the driver receives from the + * Warrior. It updates the data accordingly. + */ + +static void js_war_process_packet(struct js_war_info* info) +{ + int **axes = info->port->axes; + int **buttons = info->port->buttons; + unsigned char *data = info->data; + int i; + + if (!info->idx) return; + + switch ((data[0] >> 4) & 7) { + + case 1: /* Button data */ + if (!info->port->devs[0]) return; + buttons[0][0] = ((data[3] & 0xa) >> 1) | ((data[3] & 0x5) << 1); + return; + case 2: /* Static status (Send !S to get one) */ +#if 0 + printk("joy-warrior: Static status:"); + for (i = 0; i < 12; i++) + printk(" %02x", info->data[i]); + printk("\n"); +#endif + return; + case 3: /* XY-axis info->data */ + if (!info->port->devs[0]) return; + axes[0][0] = ((data[0] & 8) << 5) - (data[2] | ((data[0] & 4) << 5)); + axes[0][1] = (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7); + return; + break; + case 4: /* Dynamic status */ +#if 0 + printk("joy-warrior: Dynamic status:"); + for (i = 0; i < 4; i++) + printk(" %02x", info->data[i]); + printk("\n"); +#endif + return; + case 5: /* Throttle, spinner, hat info->data */ + if (!info->port->devs[0]) return; + axes[0][2] = (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7); + axes[0][3] = (data[3] & 2 ? 1 : 0) - (info->data[3] & 1 ? 1 : 0); + axes[0][4] = (data[3] & 8 ? 1 : 0) - (info->data[3] & 4 ? 1 : 0); + axes[0][5] = (data[2] | ((data[0] & 4) << 5)) - ((data[0] & 8) << 5); + return; + default: + printk("joy-warrior: Unknown packet %d length %d:", (data[0] >> 4) & 7, info->idx); + for (i = 0; i < info->idx; i++) + printk(" %02x", data[i]); + printk("\n"); + return; + } +} + +/* + * js_war_open() is a callback from the joystick device open routine. + */ + +static int js_war_open(struct js_dev *jd) +{ + struct js_war_info *info = jd->port->info; + info->used++; + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_war_close() is a callback from the joystick device release routine. + */ + +static int js_war_close(struct js_dev *jd) +{ + struct js_war_info *info = jd->port->info; + if (!--info->used) { + js_unregister_device(jd->port->devs[0]); + js_war_port = js_unregister_port(jd->port); + } + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_war_init_corr() initializes the correction values for the Warrior. + */ + +static void __init js_war_init_corr(struct js_corr **corr) +{ + int i; + + for (i = 0; i < 6; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = 0; + corr[0][i].coef[0] = -8; + corr[0][i].coef[1] = 8; + corr[0][i].coef[2] = (1 << 29) / (128 - 64); + corr[0][i].coef[3] = (1 << 29) / (128 - 64); + } + + corr[0][2].coef[2] = (1 << 29) / (128 - 16); + corr[0][2].coef[3] = (1 << 29) / (128 - 16); + + for (i = 3; i < 5; i++) { + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (1 << 29); + corr[0][i].coef[3] = (1 << 29); + } + + corr[0][5].prec = -1; + corr[0][5].coef[0] = 0; + corr[0][5].coef[1] = 0; + corr[0][5].coef[2] = (1 << 29) / 128; + corr[0][5].coef[3] = (1 << 29) / 128; +} + +/* + * js_war_ldisc_open() is the routine that is called upon setting our line + * discipline on a tty. + */ + +static int js_war_ldisc_open(struct tty_struct *tty) +{ + struct js_war_info iniinfo; + struct js_war_info *info = &iniinfo; + + info->tty = tty; + info->idx = 0; + info->len = 0; + info->used = 1; + + js_war_port = js_register_port(js_war_port, info, 1, sizeof(struct js_war_info), NULL); + + info = js_war_port->info; + info->port = js_war_port; + tty->disc_data = info; + + printk(KERN_INFO "js%d: WingMan Warrior on %s%d\n", + js_register_device(js_war_port, 0, 6, 4, "WingMan Warrior", js_war_open, js_war_close), + tty->driver.name, MINOR(tty->device) - tty->driver.minor_start); + + js_war_init_corr(js_war_port->corr); + + MOD_INC_USE_COUNT; + + return 0; +} + +/* + * js_war_ldisc_close() is the opposite of js_war_ldisc_open() + */ + +static void js_war_ldisc_close(struct tty_struct *tty) +{ + struct js_war_info* info = (struct js_war_info*) tty->disc_data; + if (!--info->used) { + js_unregister_device(info->port->devs[0]); + js_war_port = js_unregister_port(info->port); + } + MOD_DEC_USE_COUNT; +} + +/* + * js_war_ldisc_receive() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static void js_war_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct js_war_info* info = (struct js_war_info*) tty->disc_data; + int i; + + for (i = 0; i < count; i++) { + if (cp[i] & 0x80) { + if (info->idx) + js_war_process_packet(info); + info->idx = 0; + info->len = js_war_lengths[(cp[i] >> 4) & 7]; + } + + if (info->idx < JS_WAR_MAX_LENGTH) + info->data[info->idx++] = cp[i]; + + if (info->idx == info->len) { + if (info->idx) + js_war_process_packet(info); + info->idx = 0; + info->len = 0; + } + } +} + +/* + * js_war_ldisc_room() reports how much room we do have for receiving data. + * Although we in fact have infinite room, we need to specify some value + * here, so why not the size of our packet buffer. It's big anyway. + */ + +static int js_war_ldisc_room(struct tty_struct *tty) +{ + return JS_WAR_MAX_LENGTH; +} + +/* + * The line discipline structure. + */ + +static struct tty_ldisc js_war_ldisc = { + magic: TTY_LDISC_MAGIC, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + name: "warrior", +#endif + open: js_war_ldisc_open, + close: js_war_ldisc_close, + receive_buf: js_war_ldisc_receive, + receive_room: js_war_ldisc_room, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +#ifdef MODULE +int init_module(void) +#else +int __init js_war_init(void) +#endif +{ + if (tty_register_ldisc(N_JOYSTICK_WAR, &js_war_ldisc)) { + printk(KERN_ERR "joy-warrior: Error registering line discipline.\n"); + return -ENODEV; + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + tty_register_ldisc(N_JOYSTICK_WAR, NULL); +} +#endif diff --git a/drivers/char/joystick/joystick.c b/drivers/char/joystick/joystick.c index 6c2abda1f..48af4c713 100644 --- a/drivers/char/joystick/joystick.c +++ b/drivers/char/joystick/joystick.c @@ -1,7 +1,9 @@ /* - * joystick.c Version 1.2 + * joystick.c Version 1.2 * - * Copyright (c) 1996-1998 Vojtech Pavlik + * Copyright (c) 1996-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -33,7 +35,6 @@ #include <asm/io.h> #include <asm/system.h> #include <asm/segment.h> -#include <linux/config.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/joystick.h> @@ -42,11 +43,9 @@ #include <linux/malloc.h> #include <linux/mm.h> #include <linux/module.h> -#include <linux/version.h> -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) -#include <linux/spinlock.h> #include <linux/poll.h> -#endif +#include <linux/config.h> +#include <linux/init.h> /* * Configurable parameters. @@ -55,6 +54,15 @@ #define JS_REFRESH_TIME HZ/50 /* Time between two reads of joysticks (20ms) */ /* + * Exported symbols. + */ + +EXPORT_SYMBOL(js_register_port); +EXPORT_SYMBOL(js_unregister_port); +EXPORT_SYMBOL(js_register_device); +EXPORT_SYMBOL(js_unregister_device); + +/* * Buffer macros. */ @@ -75,18 +83,6 @@ spinlock_t js_lock = SPIN_LOCK_UNLOCKED; static int js_use_count = 0; /* - * Exported variables. - */ - -unsigned int js_time_speed = 0; -js_time_func js_get_time; -js_delta_func js_delta; - -unsigned int js_time_speed_a = 0; -js_time_func js_get_time_a; -js_delta_func js_delta_a; - -/* * Module info. */ @@ -94,244 +90,6 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_SUPPORTED_DEVICE("js"); /* - * js_get_time_*() are different functions to get current time. - * js_delta_*() are functions to compute time difference. - */ - -#ifdef __i386__ - -static unsigned int js_get_time_rdtsc(void) -{ - unsigned int x; - __asm__ __volatile__ ( "rdtsc" : "=A" (x) ); - return x; -} - -static unsigned int js_get_time_pit(void) -{ - unsigned long flags; - unsigned int x; - - __save_flags(flags); - __cli(); - outb(0, 0x43); - x = inb(0x40); - x |= inb(0x40) << 8; - __restore_flags(flags); - - return x; -} - -static int js_delta_pit(unsigned int x, unsigned int y) -{ - return y - x + ( y < x ? 1193180L / HZ : 0 ); -} - -static unsigned int js_get_time_counter(void) -{ - static int time_counter = 0; - return time_counter++; -} - -#else -#ifdef __alpha__ - -static unsigned int js_get_time_rpcc(void) -{ - unsigned int x; - __asm__ __volatile__ ( "rpcc %0" : "=r" (x) ); - return x; -} - -#else - -#ifndef MODULE -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) -static unsigned int js_get_time_system(void) -{ - static struct timeval js_tv; - get_fast_time(&js_tv); - return js_tv.tv_sec * 1000000L + js_tv.tv_usec; -} -#endif -#endif - -#endif -#endif - -static int js_delta_normal(unsigned int x, unsigned int y) -{ - return x - y; -} - -/* - * js_calibrate_time() calibrates a given timer. - */ - -static int __init js_calibrate_time(js_time_func get_time, js_delta_func delta) -{ - unsigned int t1, t2, t3; - unsigned long flags; - - __save_flags(flags); - __cli(); - t1 = get_time(); - udelay(1000); - t2 = get_time(); - t3 = get_time(); - __restore_flags(flags); - - return delta(t2, t1) - delta(t3, t2); -} - -/* - * js_calibrate_time_counter() calibrates the counter timer, which can't - * be calibrated using the above function. - */ - -#ifdef __i386__ - -static int __init js_calibrate_time_counter(void) -{ - unsigned int i, j, t1, t2, t3; - - j = jiffies; do { inb(0x201); t1 = js_get_time_counter(); } while (j == jiffies); - j = jiffies; do { inb(0x201); t2 = js_get_time_counter(); } while (j == jiffies); - - j = (t2 - t1) * HZ / 1000; - - t1 = js_get_time_pit(); - for (i = 0; i < 1000; i++) { - inb(0x201); - js_get_time_counter(); - } - t2 = js_get_time_pit(); - t3 = js_get_time_pit(); - - i = 1193180L / (js_delta_pit(t2, t1) - js_delta_pit(t3, t2)); - - if (DIFF(i,j) > 5) - printk(KERN_WARNING "js: Counter timer calibration unsure," - " pass1 (0.%d MHz) and pass2 (0.%d MHz) differ.\n", j, i); - - return (i + j) >> 1; -} - -#endif - -/* - * js_setup_time chooses the best available timers - * on the system and calibrates them. - */ - -static int __init js_setup_time(void) -{ - int t; - char *name, *name_a; - - name = ""; - name_a = ""; - js_time_speed = 0; - js_time_speed_a = 0; - -#ifdef __i386__ - - t = js_calibrate_time(js_get_time_pit, js_delta_pit); - - if (DIFF(t, 1193) > 5) - printk(KERN_WARNING "js: Measured PIT speed is %d.%03d MHz, but should be 1.193 MHz.\n" - KERN_WARNING "js: This is probably caused by wrong BogoMIPS value. It is: %ld, should be: %ld.\n", - t / 1000, t % 1000, loops_per_sec / 500000, loops_per_sec / (t * 500000 / 1193)); - - if (JS_HAS_RDTSC && (t = js_calibrate_time(js_get_time_rdtsc, js_delta_normal)) > 0) { - - js_time_speed_a = t; - js_get_time_a = js_get_time_rdtsc; - js_delta_a = js_delta_normal; - js_time_speed = t; - js_get_time = js_get_time_rdtsc; - js_delta = js_delta_normal; - name = "RDTSC"; - - } else { - - js_time_speed_a = t; - js_get_time_a = js_get_time_pit; - js_delta_a = js_delta_pit; - name_a = "PIT"; - - t = js_calibrate_time_counter(); - - js_time_speed = t; - js_get_time = js_get_time_counter; - js_delta = js_delta_normal; - name = "counter"; - - } - -#else -#ifdef __alpha__ - - t = js_calibrate_time(js_get_time_rpcc, js_delta_normal); - - js_time_speed_a = t; - js_get_time_a = js_get_time_rpcc; - js_delta_a = js_delta_normal; - js_time_speed = t; - js_get_time = js_get_time_rpcc; - js_delta = js_delta_normal; - name = "RPCC"; - -#else - -#ifndef MODULE -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) - t = js_calibrate_time(js_get_time_system, js_delta_normal); - - js_time_speed_a = t; - js_get_time_a = js_get_time_system; - js_delta_a = js_delta_normal; - js_time_speed = t; - js_get_time = js_get_time_system; - js_delta = js_delta_normal; - name = "system"; -#endif -#endif - -#endif -#endif - - printk(KERN_INFO "js: Version %d.%d.%d ", - JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff); - - if (js_time_speed_a <= 0 || js_time_speed <= 0) { - printk("\n"); - return -1; - } - - printk("using "); - - if (js_time_speed > 10000) { - t = js_time_speed / 1000 + (js_time_speed % 1000 >= 500); - printk("%d MHz ", t); - } else { - t = js_time_speed / 10 + (js_time_speed % 10 >= 5); - printk("%d.%02d MHz ", t / 100, t % 100); - } - - if (js_get_time_a != js_get_time) { - t = js_time_speed_a / 10 + (js_time_speed_a % 10 >= 5); - printk("%s timer and %d.%02d MHz %s timer.\n", - name, t / 100, t % 100, name_a); - } else { - printk("%s timer.\n", name); - } - - return 0; -} - - -/* * js_correct() performs correction of raw joystick data. */ @@ -365,7 +123,6 @@ static inline int js_button(int *buttons, int i) return (buttons[i >> 5] >> (i & 0x1f)) & 1; } - /* * js_add_event() adds an event to the buffer. This requires additional * queue post-processing done by js_sync_buff. @@ -458,14 +215,17 @@ static void js_do_timer(unsigned long data) struct js_dev *curd = js_dev; unsigned long flags; - while (curp != NULL) { - curp->read(curp->info, curp->axes, curp->buttons); + while (curp) { + if (curp->read) + if (curp->read(curp->info, curp->axes, curp->buttons)) + curp->fail++; + curp->total++; curp = curp->next; } spin_lock_irqsave(&js_lock, flags); - while (curd != NULL) { + while (curd) { if (data) { js_process_data(curd); js_sync_buff(curd); @@ -486,11 +246,7 @@ static void js_do_timer(unsigned long data) * space. */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) -#else -static int js_read(struct inode *inode, struct file *file, char *buf, int count) -#endif { DECLARE_WAITQUEUE(wait, current); struct js_event *buff = (void *) buf; @@ -576,13 +332,8 @@ static int js_read(struct inode *inode, struct file *file, char *buf, int count) tmpevent.time = jiffies * (1000/HZ); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) if (copy_to_user(&buff[written], &tmpevent, sizeof(struct js_event))) retval = -EFAULT; -#else - if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event)))) - memcpy_tofs(&buff[written], &tmpevent, sizeof(struct js_event)); -#endif curl->startup++; written++; @@ -594,17 +345,11 @@ static int js_read(struct inode *inode, struct file *file, char *buf, int count) while ((jd->bhead != (new_tail = GOF(curl->tail))) && (written < blocks) && !retval) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) if (copy_to_user(&buff[written], &jd->buff[new_tail], sizeof(struct js_event))) retval = -EFAULT; if (put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time)) retval = -EFAULT; -#else - if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event)))) { - memcpy_tofs(&buff[written], &jd->buff[new_tail], sizeof(struct js_event)); - put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time); - } -#endif + curl->tail = new_tail; written++; } @@ -625,15 +370,9 @@ static int js_read(struct inode *inode, struct file *file, char *buf, int count) data.y = jd->num_axes < 2 ? 0 : ((js_correct(jd->new.axes[1], &jd->corr[1]) / 256) + 128) >> js_comp_glue.JS_CORR.y; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) retval = copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; -#else - if (!(retval = verify_area(VERIFY_WRITE, buf, sizeof(struct JS_DATA_TYPE)))) { - memcpy_tofs(buf, &data, sizeof(struct JS_DATA_TYPE)); - } -#endif - curl->startup = 0; + curl->startup = jd->num_axes + jd->num_buttons; curl->tail = GOB(jd->bhead); if (!retval) retval = sizeof(struct JS_DATA_TYPE); } @@ -645,12 +384,12 @@ static int js_read(struct inode *inode, struct file *file, char *buf, int count) if (orig_tail == jd->tail) { new_tail = curl->tail; curl = jd->list; - while (curl != NULL && curl->tail != jd->tail) { + while (curl && curl->tail != jd->tail) { if (ROT(jd->bhead, new_tail, curl->tail) || (jd->bhead == curl->tail)) new_tail = curl->tail; curl = curl->next; } - if (curl == NULL) jd->tail = new_tail; + if (!curl) jd->tail = new_tail; } spin_unlock_irqrestore(&js_lock, flags); @@ -662,8 +401,6 @@ static int js_read(struct inode *inode, struct file *file, char *buf, int count) * js_poll() does select() support. */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) - static unsigned int js_poll(struct file *file, poll_table *wait) { struct js_list *curl = file->private_data; @@ -677,20 +414,6 @@ static unsigned int js_poll(struct file *file, poll_table *wait) return retval; } -#else - -static int js_select(struct inode *inode, struct file *file, int sel_type, select_table *wait) -{ - struct js_list *curl = file->private_data; - if (sel_type == SEL_IN) { - if (GOF(curl->tail) != curl->dev->bhead) return 1; - select_wait(&curl->dev->wait, wait); - } - return 0; -} - -#endif - /* * js_ioctl handles misc ioctl calls. */ @@ -704,8 +427,6 @@ static int js_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un curl = file->private_data; jd = curl->dev; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) - switch (cmd) { /* @@ -758,95 +479,6 @@ static int js_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un } } -#else - - switch (cmd) { - -/* - * 0.x compatibility - */ - - case JS_SET_CAL: - if (verify_area(VERIFY_READ, (struct JS_DATA_TYPE *) arg, - sizeof(struct JS_DATA_TYPE))) return -EFAULT; - memcpy_fromfs(&js_comp_glue.JS_CORR, (struct JS_DATA_SAVE_TYPE *) arg, - sizeof(struct JS_DATA_TYPE)); - return 0; - case JS_GET_CAL: - if (verify_area(VERIFY_WRITE, (struct JS_DATA_TYPE *) arg, - sizeof(struct JS_DATA_TYPE))) return -EFAULT; - memcpy_tofs((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue.JS_CORR, - sizeof(struct JS_DATA_TYPE)); - return 0; - case JS_SET_TIMEOUT: - if (verify_area(VERIFY_READ, (int *) arg, sizeof(int))) return -EFAULT; - js_comp_glue.JS_TIMEOUT = get_user((int *) arg); - return 0; - case JS_GET_TIMEOUT: - if (verify_area(VERIFY_WRITE, (int *) arg, sizeof(int))) return -EFAULT; - put_user(js_comp_glue.JS_TIMEOUT, (int *) arg); - return 0; - case JS_SET_TIMELIMIT: - if (verify_area(VERIFY_READ, (long *) arg, sizeof(long))) return -EFAULT; - js_comp_glue.JS_TIMELIMIT = get_user((long *) arg); - return 0; - case JS_GET_TIMELIMIT: - if (verify_area(VERIFY_WRITE, (long *) arg, sizeof(long))) return -EFAULT; - put_user(js_comp_glue.JS_TIMELIMIT, (long *) arg); - return 0; - case JS_SET_ALL: - if (verify_area(VERIFY_READ, (struct JS_DATA_SAVE_TYPE *) arg, - sizeof(struct JS_DATA_SAVE_TYPE))) return -EFAULT; - memcpy_fromfs(&js_comp_glue, (struct JS_DATA_SAVE_TYPE *) arg, - sizeof(struct JS_DATA_SAVE_TYPE)); - return 0; - case JS_GET_ALL: - if (verify_area(VERIFY_WRITE, (struct JS_DATA_SAVE_TYPE *) arg, - sizeof(struct JS_DATA_SAVE_TYPE))) return -EFAULT; - memcpy_tofs((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue, - sizeof(struct JS_DATA_SAVE_TYPE)); - return 0; - -/* - * 1.x ioctl calls - */ - - case JSIOCGVERSION: - if (verify_area(VERIFY_WRITE, (__u32 *) arg, sizeof(__u32))) return -EFAULT; - put_user(JS_VERSION, (__u32 *) arg); - return 0; - case JSIOCGAXES: - if (verify_area(VERIFY_WRITE, (__u8 *) arg, sizeof(__u8))) return -EFAULT; - put_user(jd->num_axes, (__u8 *) arg); - return 0; - case JSIOCGBUTTONS: - if (verify_area(VERIFY_WRITE, (__u8 *) arg, sizeof(__u8))) return -EFAULT; - put_user(jd->num_buttons, (__u8 *) arg); - return 0; - case JSIOCSCORR: - if (verify_area(VERIFY_READ, (struct js_corr *) arg, - sizeof(struct js_corr) * jd->num_axes)) return -EFAULT; - memcpy_fromfs(jd->corr, (struct js_corr *) arg, - sizeof(struct js_corr) * jd->num_axes); - return 0; - case JSIOCGCORR: - if (verify_area(VERIFY_WRITE, (struct js_corr *) arg, - sizeof(struct js_corr) * jd->num_axes)) return -EFAULT; - memcpy_tofs((struct js_corr *) arg, - jd->corr, sizeof(struct js_corr) * jd->num_axes); - return 0; - default: - if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { - len = strlen(jd->name) + 1; - if (verify_area(VERIFY_WRITE, (char *) arg, len)) return -EFAULT; - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - memcpy_tofs((char *) arg, jd->name, len); - return len; - } - } - -#endif - return -EINVAL; } @@ -868,21 +500,20 @@ static int js_open(struct inode *inode, struct file *file) spin_lock_irqsave(&js_lock, flags); - while (i > 0 && jd != NULL) { + while (i > 0 && jd) { jd = jd->next; i--; } spin_unlock_irqrestore(&js_lock, flags); - if (jd == NULL) return -ENODEV; + if (!jd) return -ENODEV; if ((result = jd->open(jd))) return result; - MOD_INC_USE_COUNT; - if (!js_use_count++) js_do_timer(0); + if ((new = kmalloc(sizeof(struct js_list), GFP_KERNEL))) { - if ((new = kmalloc(sizeof(struct js_list), GFP_KERNEL)) != NULL) { + MOD_INC_USE_COUNT; spin_lock_irqsave(&js_lock, flags); @@ -897,6 +528,8 @@ static int js_open(struct inode *inode, struct file *file) spin_unlock_irqrestore(&js_lock, flags); + if (!js_use_count++) js_do_timer(0); + } else { result = -ENOMEM; } @@ -909,11 +542,7 @@ static int js_open(struct inode *inode, struct file *file) * used by it. */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) static int js_release(struct inode *inode, struct file *file) -#else -static void js_release(struct inode *inode, struct file *file) -#endif { struct js_list *curl = file->private_data; struct js_dev *jd = curl->dev; @@ -926,11 +555,11 @@ static void js_release(struct inode *inode, struct file *file) while (*curp && (*curp != curl)) curp = &((*curp)->next); *curp = (*curp)->next; - if (jd->list != NULL) + if (jd->list) if (curl->tail == jd->tail) { curl = jd->list; new_tail = curl->tail; - while (curl != NULL && curl->tail != jd->tail) { + while (curl && curl->tail != jd->tail) { if (ROT(jd->bhead, new_tail, curl->tail) || (jd->bhead == curl->tail)) new_tail = curl->tail; curl = curl->next; @@ -947,9 +576,7 @@ static void js_release(struct inode *inode, struct file *file) jd->close(jd); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) return 0; -#endif } /* @@ -968,7 +595,7 @@ static void js_dump_mem(void) printk(",--- Dumping Devices:\n"); printk("| js_dev = %x\n", (int) js_dev); - while (curd != NULL) { + while (curd) { printk("| %s-device %x, next %x axes %d, buttons %d, port %x - %#x\n", curd->next ? "|":"`", (int) curd, (int) curd->next, curd->num_axes, curd->num_buttons, (int) curd->port, curd->port->io); @@ -978,7 +605,7 @@ static void js_dump_mem(void) printk(">--- Dumping ports:\n"); printk("| js_port = %x\n", (int) js_port); - while (curp != NULL) { + while (curp) { printk("| %s-port %x, next %x, io %#x, devices %d\n", curp->next ? "|":"`", (int) curp, (int) curp->next, curp->io, curp->ndevs); @@ -1010,7 +637,7 @@ struct js_port *js_register_port(struct js_port *port, int i; unsigned long flags; - if ((all = kmalloc(sizeof(struct js_port) + 4 * devs * sizeof(void*) + infos, GFP_KERNEL)) == NULL) + if (!(all = kmalloc(sizeof(struct js_port) + 4 * devs * sizeof(void*) + infos, GFP_KERNEL))) return NULL; curp = all; @@ -1019,6 +646,8 @@ struct js_port *js_register_port(struct js_port *port, curp->prev = port; curp->read = read; curp->ndevs = devs; + curp->fail = 0; + curp->total = 0; curp->devs = all += sizeof(struct js_port); for (i = 0; i < devs; i++) curp->devs[i] = NULL; @@ -1036,7 +665,7 @@ struct js_port *js_register_port(struct js_port *port, spin_lock_irqsave(&js_lock, flags); - while (*ptrp != NULL) ptrp=&((*ptrp)->next); + while (*ptrp) ptrp=&((*ptrp)->next); *ptrp = curp; spin_unlock_irqrestore(&js_lock, flags); @@ -1052,7 +681,9 @@ struct js_port *js_unregister_port(struct js_port *port) spin_lock_irqsave(&js_lock, flags); - while (*curp != NULL && (*curp != port)) curp = &((*curp)->next); + printk("js: There were %d failures out of %d read attempts.\n", port->fail, port->total); + + while (*curp && (*curp != port)) curp = &((*curp)->next); *curp = (*curp)->next; spin_unlock_irqrestore(&js_lock, flags); @@ -1072,9 +703,9 @@ int js_register_device(struct js_port *port, int number, int axes, int buttons, int i = 0; unsigned long flags; - if ((all = kmalloc(sizeof(struct js_dev) + 2 * axes * sizeof(int) + + if (!(all = kmalloc(sizeof(struct js_dev) + 2 * axes * sizeof(int) + 2 * (((buttons - 1) >> 5) + 1) * sizeof(int) + - axes * sizeof(struct js_corr) + strlen(name) + 1, GFP_KERNEL)) == NULL) + axes * sizeof(struct js_corr) + strlen(name) + 1, GFP_KERNEL))) return -1; curd = all; @@ -1082,10 +713,11 @@ int js_register_device(struct js_port *port, int number, int axes, int buttons, curd->next = NULL; curd->list = NULL; curd->port = port; - init_waitqueue_head(&curd->wait); curd->open = open; curd->close = close; + init_waitqueue_head(&curd->wait); + curd->ahead = 0; curd->bhead = 0; curd->tail = JS_BUFF_SIZE - 1; @@ -1108,7 +740,7 @@ int js_register_device(struct js_port *port, int number, int axes, int buttons, spin_lock_irqsave(&js_lock, flags); - while (*ptrd != NULL) { ptrd=&(*ptrd)->next; i++; } + while (*ptrd) { ptrd=&(*ptrd)->next; i++; } *ptrd = curd; spin_unlock_irqrestore(&js_lock, flags); @@ -1123,7 +755,7 @@ void js_unregister_device(struct js_dev *dev) spin_lock_irqsave(&js_lock, flags); - while (*curd != NULL && (*curd != dev)) curd = &((*curd)->next); + while (*curd && (*curd != dev)) curd = &((*curd)->next); *curd = (*curd)->next; spin_unlock_irqrestore(&js_lock, flags); @@ -1138,11 +770,7 @@ void js_unregister_device(struct js_dev *dev) static struct file_operations js_fops = { read: js_read, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) poll: js_poll, -#else - select: js_select, -#endif ioctl: js_ioctl, open: js_open, release: js_release, @@ -1159,15 +787,15 @@ int init_module(void) int __init js_init(void) #endif { - int result; - - js_setup_time(); if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) { printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR); return -EBUSY; } + printk(KERN_INFO "js: Joystick driver v%d.%d.%d (c) 1999 Vojtech Pavlik <vojtech@suse.cz>\n", + JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff); + spin_lock_init(&js_lock); init_timer(&js_timer); @@ -1178,44 +806,61 @@ int __init js_init(void) js_comp_glue.JS_TIMEOUT = JS_DEF_TIMEOUT; js_comp_glue.JS_TIMELIMIT = JS_DEF_TIMELIMIT; -#ifdef MODULE - result = 0; -#else - result = -ENODEV; +#ifndef MODULE +#ifdef CONFIG_JOY_PCI + js_pci_init(); +#endif #ifdef CONFIG_JOY_LIGHTNING - if (!js_l4_init()) result = 0; + js_l4_init(); #endif #ifdef CONFIG_JOY_SIDEWINDER - if (!js_sw_init()) result = 0; + js_sw_init(); #endif -#ifdef CONFIG_JOY_ASSASIN - if (!js_as_init()) result = 0; +#ifdef CONFIG_JOY_ASSASSIN + js_as_init(); #endif #ifdef CONFIG_JOY_LOGITECH - if (!js_lt_init()) result = 0; + js_lt_init(); #endif #ifdef CONFIG_JOY_THRUSTMASTER - if (!js_tm_init()) result = 0; + js_tm_init(); #endif #ifdef CONFIG_JOY_GRAVIS - if (!js_gr_init()) result = 0; + js_gr_init(); +#endif +#ifdef CONFIG_JOY_CREATIVE + js_cr_init(); #endif #ifdef CONFIG_JOY_ANALOG - if (!js_an_init()) result = 0; + js_an_init(); #endif #ifdef CONFIG_JOY_CONSOLE - if (!js_console_init()) result = 0; + js_console_init(); #endif #ifdef CONFIG_JOY_DB9 - if (!js_db9_init()) result = 0; + js_db9_init(); +#endif +#ifdef CONFIG_JOY_TURBOGRAFX + js_tg_init(); #endif #ifdef CONFIG_JOY_AMIGA - if (!js_am_init()) result = 0; + js_am_init(); +#endif +#ifdef CONFIG_JOY_MAGELLAN + js_mag_init(); +#endif +#ifdef CONFIG_JOY_WARRIOR + js_war_init(); +#endif +#ifdef CONFIG_JOY_SPACEORB + js_orb_init(); +#endif +#ifdef CONFIG_JOY_SPACEBALL + js_sball_init(); #endif - if (result) printk(KERN_ERR "js: no joysticks found\n"); #endif - return result; + return 0; } /* @@ -1230,3 +875,4 @@ void cleanup_module(void) printk(KERN_ERR "js: can't unregister device\n"); } #endif + |