diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/input/gameport |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/input/gameport')
-rw-r--r-- | drivers/input/gameport/Kconfig | 90 | ||||
-rw-r--r-- | drivers/input/gameport/Makefile | 13 | ||||
-rw-r--r-- | drivers/input/gameport/cs461x.c | 322 | ||||
-rw-r--r-- | drivers/input/gameport/emu10k1-gp.c | 132 | ||||
-rw-r--r-- | drivers/input/gameport/fm801-gp.c | 163 | ||||
-rw-r--r-- | drivers/input/gameport/gameport.c | 797 | ||||
-rw-r--r-- | drivers/input/gameport/lightning.c | 344 | ||||
-rw-r--r-- | drivers/input/gameport/ns558.c | 291 | ||||
-rw-r--r-- | drivers/input/gameport/vortex.c | 186 |
9 files changed, 2338 insertions, 0 deletions
diff --git a/drivers/input/gameport/Kconfig b/drivers/input/gameport/Kconfig new file mode 100644 index 00000000000..6282f460aba --- /dev/null +++ b/drivers/input/gameport/Kconfig @@ -0,0 +1,90 @@ +# +# Gameport configuration +# +config GAMEPORT + tristate "Gameport support" + ---help--- + Gameport support is for the standard 15-pin PC gameport. If you + have a joystick, gamepad, gameport card, a soundcard with a gameport + or anything else that uses the gameport, say Y or M here and also to + at least one of the hardware specific drivers. + + For Ensoniq AudioPCI (ES1370), AudioPCI 97 (ES1371), ESS Solo1, + S3 SonicVibes, Trident 4DWave, SiS7018, and ALi 5451 gameport + support is provided by the sound drivers, so you won't need any + from the below listed modules. You still need to say Y here. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called gameport. + +if GAMEPORT + +config GAMEPORT_NS558 + tristate "Classic ISA and PnP gameport support" + help + Say Y here if you have an ISA or PnP gameport. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called ns558. + +config GAMEPORT_L4 + tristate "PDPI Lightning 4 gamecard support" + help + Say Y here if you have a PDPI Lightning 4 gamecard. + + To compile this driver as a module, choose M here: the + module will be called lightning. + +config GAMEPORT_EMU10K1 + tristate "SB Live and Audigy gameport support" + depends on PCI + help + Say Y here if you have a SoundBlaster Live! or SoundBlaster + Audigy card and want to use its gameport. + + To compile this driver as a module, choose M here: the + module will be called emu10k1-gp. + +config GAMEPORT_VORTEX + tristate "Aureal Vortex, Vortex 2 gameport support" + depends on PCI + help + Say Y here if you have an Aureal Vortex 1 or 2 card and want + to use its gameport. + + To compile this driver as a module, choose M here: the + module will be called vortex. + +config GAMEPORT_FM801 + tristate "ForteMedia FM801 gameport support" + depends on PCI + +config GAMEPORT_CS461X + tristate "Crystal SoundFusion gameport support" + depends on PCI + +endif + +# Yes, SOUND_GAMEPORT looks a bit odd. Yes, it ends up being turned on +# in every .config. Please don't touch it. It is here to handle an +# unusual dependency between GAMEPORT and sound drivers. +# +# Some sound drivers call gameport functions. If GAMEPORT is +# not selected, empty stubs are provided for the functions and all is +# well. +# If GAMEPORT is built in, everything is fine. +# If GAMEPORT is a module, however, it would need to be loaded for the +# sound driver to be able to link properly. Therefore, the sound +# driver must be a module as well in that case. Since there's no way +# to express that directly in Kconfig, we use SOUND_GAMEPORT to +# express it. SOUND_GAMEPORT boils down to "if GAMEPORT is 'm', +# anything that depends on SOUND_GAMEPORT must be 'm' as well. if +# GAMEPORT is 'y' or 'n', it can be anything". +config SOUND_GAMEPORT + tristate + default m if GAMEPORT=m + default y diff --git a/drivers/input/gameport/Makefile b/drivers/input/gameport/Makefile new file mode 100644 index 00000000000..5367b4267ad --- /dev/null +++ b/drivers/input/gameport/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the gameport drivers. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_GAMEPORT) += gameport.o +obj-$(CONFIG_GAMEPORT_CS461X) += cs461x.o +obj-$(CONFIG_GAMEPORT_EMU10K1) += emu10k1-gp.o +obj-$(CONFIG_GAMEPORT_FM801) += fm801-gp.o +obj-$(CONFIG_GAMEPORT_L4) += lightning.o +obj-$(CONFIG_GAMEPORT_NS558) += ns558.o +obj-$(CONFIG_GAMEPORT_VORTEX) += vortex.o diff --git a/drivers/input/gameport/cs461x.c b/drivers/input/gameport/cs461x.c new file mode 100644 index 00000000000..d4013ff9862 --- /dev/null +++ b/drivers/input/gameport/cs461x.c @@ -0,0 +1,322 @@ +/* + The all defines and part of code (such as cs461x_*) are + contributed from ALSA 0.5.8 sources. + See http://www.alsa-project.org/ for sources + + Tested on Linux 686 2.4.0-test9, ALSA 0.5.8a and CS4610 +*/ + +#include <asm/io.h> + +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/config.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/slab.h> +#include <linux/pci.h> + +MODULE_AUTHOR("Victor Krapivin"); +MODULE_LICENSE("GPL"); + +/* + These options are experimental + +#define CS461X_FULL_MAP +*/ + + +#ifndef PCI_VENDOR_ID_CIRRUS +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4610 +#define PCI_DEVICE_ID_CIRRUS_4610 0x6001 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4612 +#define PCI_DEVICE_ID_CIRRUS_4612 0x6003 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4615 +#define PCI_DEVICE_ID_CIRRUS_4615 0x6004 +#endif + +/* Registers */ + +#define BA0_JSPT 0x00000480 +#define BA0_JSCTL 0x00000484 +#define BA0_JSC1 0x00000488 +#define BA0_JSC2 0x0000048C +#define BA0_JSIO 0x000004A0 + +/* Bits for JSPT */ + +#define JSPT_CAX 0x00000001 +#define JSPT_CAY 0x00000002 +#define JSPT_CBX 0x00000004 +#define JSPT_CBY 0x00000008 +#define JSPT_BA1 0x00000010 +#define JSPT_BA2 0x00000020 +#define JSPT_BB1 0x00000040 +#define JSPT_BB2 0x00000080 + +/* Bits for JSCTL */ + +#define JSCTL_SP_MASK 0x00000003 +#define JSCTL_SP_SLOW 0x00000000 +#define JSCTL_SP_MEDIUM_SLOW 0x00000001 +#define JSCTL_SP_MEDIUM_FAST 0x00000002 +#define JSCTL_SP_FAST 0x00000003 +#define JSCTL_ARE 0x00000004 + +/* Data register pairs masks */ + +#define JSC1_Y1V_MASK 0x0000FFFF +#define JSC1_X1V_MASK 0xFFFF0000 +#define JSC1_Y1V_SHIFT 0 +#define JSC1_X1V_SHIFT 16 +#define JSC2_Y2V_MASK 0x0000FFFF +#define JSC2_X2V_MASK 0xFFFF0000 +#define JSC2_Y2V_SHIFT 0 +#define JSC2_X2V_SHIFT 16 + +/* JS GPIO */ + +#define JSIO_DAX 0x00000001 +#define JSIO_DAY 0x00000002 +#define JSIO_DBX 0x00000004 +#define JSIO_DBY 0x00000008 +#define JSIO_AXOE 0x00000010 +#define JSIO_AYOE 0x00000020 +#define JSIO_BXOE 0x00000040 +#define JSIO_BYOE 0x00000080 + +/* + The card initialization code is obfuscated; the module cs461x + need to be loaded after ALSA modules initialized and something + played on the CS 4610 chip (see sources for details of CS4610 + initialization code from ALSA) +*/ + +/* Card specific definitions */ + +#define CS461X_BA0_SIZE 0x2000 +#define CS461X_BA1_DATA0_SIZE 0x3000 +#define CS461X_BA1_DATA1_SIZE 0x3800 +#define CS461X_BA1_PRG_SIZE 0x7000 +#define CS461X_BA1_REG_SIZE 0x0100 + +#define BA1_SP_DMEM0 0x00000000 +#define BA1_SP_DMEM1 0x00010000 +#define BA1_SP_PMEM 0x00020000 +#define BA1_SP_REG 0x00030000 + +#define BA1_DWORD_SIZE (13 * 1024 + 512) +#define BA1_MEMORY_COUNT 3 + +/* + Only one CS461x card is still suppoted; the code requires + redesign to avoid this limitatuion. +*/ + +static unsigned long ba0_addr; +static unsigned int __iomem *ba0; + +#ifdef CS461X_FULL_MAP +static unsigned long ba1_addr; +static union ba1_t { + struct { + unsigned int __iomem *data0; + unsigned int __iomem *data1; + unsigned int __iomem *pmem; + unsigned int __iomem *reg; + } name; + unsigned int __iomem *idx[4]; +} ba1; + +static void cs461x_poke(unsigned long reg, unsigned int val) +{ + writel(val, &ba1.idx[(reg >> 16) & 3][(reg >> 2) & 0x3fff]); +} + +static unsigned int cs461x_peek(unsigned long reg) +{ + return readl(&ba1.idx[(reg >> 16) & 3][(reg >> 2) & 0x3fff]); +} + +#endif + +static void cs461x_pokeBA0(unsigned long reg, unsigned int val) +{ + writel(val, &ba0[reg >> 2]); +} + +static unsigned int cs461x_peekBA0(unsigned long reg) +{ + return readl(&ba0[reg >> 2]); +} + +static int cs461x_free(struct pci_dev *pdev) +{ + struct gameport *port = pci_get_drvdata(pdev); + + if (port) + gameport_unregister_port(port); + + if (ba0) iounmap(ba0); +#ifdef CS461X_FULL_MAP + if (ba1.name.data0) iounmap(ba1.name.data0); + if (ba1.name.data1) iounmap(ba1.name.data1); + if (ba1.name.pmem) iounmap(ba1.name.pmem); + if (ba1.name.reg) iounmap(ba1.name.reg); +#endif + return 0; +} + +static void cs461x_gameport_trigger(struct gameport *gameport) +{ + cs461x_pokeBA0(BA0_JSPT, 0xFF); //outb(gameport->io, 0xFF); +} + +static unsigned char cs461x_gameport_read(struct gameport *gameport) +{ + return cs461x_peekBA0(BA0_JSPT); //inb(gameport->io); +} + +static int cs461x_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + unsigned js1, js2, jst; + + js1 = cs461x_peekBA0(BA0_JSC1); + js2 = cs461x_peekBA0(BA0_JSC2); + jst = cs461x_peekBA0(BA0_JSPT); + + *buttons = (~jst >> 4) & 0x0F; + + axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF; + axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF; + axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF; + axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF; + + for(jst=0;jst<4;++jst) + if(axes[jst]==0xFFFF) axes[jst] = -1; + return 0; +} + +static int cs461x_gameport_open(struct gameport *gameport, int mode) +{ + switch (mode) { + case GAMEPORT_MODE_COOKED: + case GAMEPORT_MODE_RAW: + return 0; + default: + return -1; + } + return 0; +} + +static struct pci_device_id cs461x_pci_tbl[] = { + { PCI_VENDOR_ID_CIRRUS, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4610 */ + { PCI_VENDOR_ID_CIRRUS, 0x6003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4612 */ + { PCI_VENDOR_ID_CIRRUS, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4615 */ + { 0, } +}; +MODULE_DEVICE_TABLE(pci, cs461x_pci_tbl); + +static int __devinit cs461x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int rc; + struct gameport* port; + + rc = pci_enable_device(pdev); + if (rc) { + printk(KERN_ERR "cs461x: Cannot enable PCI gameport (bus %d, devfn %d) error=%d\n", + pdev->bus->number, pdev->devfn, rc); + return rc; + } + + ba0_addr = pci_resource_start(pdev, 0); +#ifdef CS461X_FULL_MAP + ba1_addr = pci_resource_start(pdev, 1); +#endif + if (ba0_addr == 0 || ba0_addr == ~0 +#ifdef CS461X_FULL_MAP + || ba1_addr == 0 || ba1_addr == ~0 +#endif + ) { + printk(KERN_ERR "cs461x: wrong address - ba0 = 0x%lx\n", ba0_addr); +#ifdef CS461X_FULL_MAP + printk(KERN_ERR "cs461x: wrong address - ba1 = 0x%lx\n", ba1_addr); +#endif + cs461x_free(pdev); + return -ENOMEM; + } + + ba0 = ioremap(ba0_addr, CS461X_BA0_SIZE); +#ifdef CS461X_FULL_MAP + ba1.name.data0 = ioremap(ba1_addr + BA1_SP_DMEM0, CS461X_BA1_DATA0_SIZE); + ba1.name.data1 = ioremap(ba1_addr + BA1_SP_DMEM1, CS461X_BA1_DATA1_SIZE); + ba1.name.pmem = ioremap(ba1_addr + BA1_SP_PMEM, CS461X_BA1_PRG_SIZE); + ba1.name.reg = ioremap(ba1_addr + BA1_SP_REG, CS461X_BA1_REG_SIZE); + + if (ba0 == NULL || ba1.name.data0 == NULL || + ba1.name.data1 == NULL || ba1.name.pmem == NULL || + ba1.name.reg == NULL) { + cs461x_free(pdev); + return -ENOMEM; + } +#else + if (ba0 == NULL) { + cs461x_free(pdev); + return -ENOMEM; + } +#endif + + if (!(port = gameport_allocate_port())) { + printk(KERN_ERR "cs461x: Memory allocation failed\n"); + cs461x_free(pdev); + return -ENOMEM; + } + + pci_set_drvdata(pdev, port); + + port->open = cs461x_gameport_open; + port->trigger = cs461x_gameport_trigger; + port->read = cs461x_gameport_read; + port->cooked_read = cs461x_gameport_cooked_read; + + gameport_set_name(port, "CS416x"); + gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev)); + port->dev.parent = &pdev->dev; + + cs461x_pokeBA0(BA0_JSIO, 0xFF); // ? + cs461x_pokeBA0(BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW); + + gameport_register_port(port); + + return 0; +} + +static void __devexit cs461x_pci_remove(struct pci_dev *pdev) +{ + cs461x_free(pdev); +} + +static struct pci_driver cs461x_pci_driver = { + .name = "CS461x_gameport", + .id_table = cs461x_pci_tbl, + .probe = cs461x_pci_probe, + .remove = __devexit_p(cs461x_pci_remove), +}; + +static int __init cs461x_init(void) +{ + return pci_register_driver(&cs461x_pci_driver); +} + +static void __exit cs461x_exit(void) +{ + pci_unregister_driver(&cs461x_pci_driver); +} + +module_init(cs461x_init); +module_exit(cs461x_exit); + diff --git a/drivers/input/gameport/emu10k1-gp.c b/drivers/input/gameport/emu10k1-gp.c new file mode 100644 index 00000000000..a0118038330 --- /dev/null +++ b/drivers/input/gameport/emu10k1-gp.c @@ -0,0 +1,132 @@ +/* + * $Id: emu10k1-gp.c,v 1.8 2002/01/22 20:40:46 vojtech Exp $ + * + * Copyright (c) 2001 Vojtech Pavlik + */ + +/* + * EMU10k1 - SB Live / Audigy - gameport driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> + +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/config.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/slab.h> +#include <linux/pci.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_DESCRIPTION("EMU10k1 gameport driver"); +MODULE_LICENSE("GPL"); + +struct emu { + struct pci_dev *dev; + struct gameport *gameport; + int io; + int size; +}; + +static struct pci_device_id emu_tbl[] = { + + { 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID }, /* SB Live gameport */ + { 0x1102, 0x7003, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy gameport */ + { 0x1102, 0x7004, PCI_ANY_ID, PCI_ANY_ID }, /* Dell SB Live */ + { 0x1102, 0x7005, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy LS gameport */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, emu_tbl); + +static int __devinit emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int ioport, iolen; + struct emu *emu; + struct gameport *port; + + if (pci_enable_device(pdev)) + return -EBUSY; + + ioport = pci_resource_start(pdev, 0); + iolen = pci_resource_len(pdev, 0); + + if (!request_region(ioport, iolen, "emu10k1-gp")) + return -EBUSY; + + emu = kcalloc(1, sizeof(struct emu), GFP_KERNEL); + port = gameport_allocate_port(); + if (!emu || !port) { + printk(KERN_ERR "emu10k1-gp: Memory allocation failed\n"); + release_region(ioport, iolen); + kfree(emu); + gameport_free_port(port); + return -ENOMEM; + } + + emu->io = ioport; + emu->size = iolen; + emu->dev = pdev; + emu->gameport = port; + + gameport_set_name(port, "EMU10K1"); + gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev)); + port->dev.parent = &pdev->dev; + port->io = ioport; + + pci_set_drvdata(pdev, emu); + + gameport_register_port(port); + + return 0; +} + +static void __devexit emu_remove(struct pci_dev *pdev) +{ + struct emu *emu = pci_get_drvdata(pdev); + + gameport_unregister_port(emu->gameport); + release_region(emu->io, emu->size); + kfree(emu); +} + +static struct pci_driver emu_driver = { + .name = "Emu10k1_gameport", + .id_table = emu_tbl, + .probe = emu_probe, + .remove = __devexit_p(emu_remove), +}; + +static int __init emu_init(void) +{ + return pci_register_driver(&emu_driver); +} + +static void __exit emu_exit(void) +{ + pci_unregister_driver(&emu_driver); +} + +module_init(emu_init); +module_exit(emu_exit); diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c new file mode 100644 index 00000000000..57615bc6390 --- /dev/null +++ b/drivers/input/gameport/fm801-gp.c @@ -0,0 +1,163 @@ +/* + * FM801 gameport driver for Linux + * + * Copyright (c) by Takashi Iwai <tiwai@suse.de> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/gameport.h> + +#define PCI_VENDOR_ID_FORTEMEDIA 0x1319 +#define PCI_DEVICE_ID_FM801_GP 0x0802 + +#define HAVE_COOKED + +struct fm801_gp { + struct gameport *gameport; + struct resource *res_port; +}; + +#ifdef HAVE_COOKED +static int fm801_gp_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + unsigned short w; + + w = inw(gameport->io + 2); + *buttons = (~w >> 14) & 0x03; + axes[0] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5); + w = inw(gameport->io + 4); + axes[1] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5); + w = inw(gameport->io + 6); + *buttons |= ((~w >> 14) & 0x03) << 2; + axes[2] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5); + w = inw(gameport->io + 8); + axes[3] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5); + outw(0xff, gameport->io); /* reset */ + + return 0; +} +#endif + +static int fm801_gp_open(struct gameport *gameport, int mode) +{ + switch (mode) { +#ifdef HAVE_COOKED + case GAMEPORT_MODE_COOKED: + return 0; +#endif + case GAMEPORT_MODE_RAW: + return 0; + default: + return -1; + } + + return 0; +} + +static int __devinit fm801_gp_probe(struct pci_dev *pci, const struct pci_device_id *id) +{ + struct fm801_gp *gp; + struct gameport *port; + + gp = kcalloc(1, sizeof(struct fm801_gp), GFP_KERNEL); + port = gameport_allocate_port(); + if (!gp || !port) { + printk(KERN_ERR "fm801-gp: Memory allocation failed\n"); + kfree(gp); + gameport_free_port(port); + return -ENOMEM; + } + + pci_enable_device(pci); + + port->open = fm801_gp_open; +#ifdef HAVE_COOKED + port->cooked_read = fm801_gp_cooked_read; +#endif + gameport_set_name(port, "FM801"); + gameport_set_phys(port, "pci%s/gameport0", pci_name(pci)); + port->dev.parent = &pci->dev; + port->io = pci_resource_start(pci, 0); + + gp->gameport = port; + gp->res_port = request_region(port->io, 0x10, "FM801 GP"); + if (!gp->res_port) { + kfree(gp); + gameport_free_port(port); + printk(KERN_DEBUG "fm801-gp: unable to grab region 0x%x-0x%x\n", + port->io, port->io + 0x0f); + return -EBUSY; + } + + pci_set_drvdata(pci, gp); + + outb(0x60, port->io + 0x0d); /* enable joystick 1 and 2 */ + gameport_register_port(port); + + return 0; +} + +static void __devexit fm801_gp_remove(struct pci_dev *pci) +{ + struct fm801_gp *gp = pci_get_drvdata(pci); + + if (gp) { + gameport_unregister_port(gp->gameport); + release_resource(gp->res_port); + kfree(gp); + } +} + +static struct pci_device_id fm801_gp_id_table[] = { + { PCI_VENDOR_ID_FORTEMEDIA, PCI_DEVICE_ID_FM801_GP, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0 } +}; + +static struct pci_driver fm801_gp_driver = { + .name = "FM801_gameport", + .id_table = fm801_gp_id_table, + .probe = fm801_gp_probe, + .remove = __devexit_p(fm801_gp_remove), +}; + +static int __init fm801_gp_init(void) +{ + return pci_register_driver(&fm801_gp_driver); +} + +static void __exit fm801_gp_exit(void) +{ + pci_unregister_driver(&fm801_gp_driver); +} + +module_init(fm801_gp_init); +module_exit(fm801_gp_exit); + +MODULE_DEVICE_TABLE(pci, fm801_gp_id_table); + +MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c new file mode 100644 index 00000000000..f20c3f23388 --- /dev/null +++ b/drivers/input/gameport/gameport.c @@ -0,0 +1,797 @@ +/* + * Generic gameport layer + * + * Copyright (c) 1999-2002 Vojtech Pavlik + * Copyright (c) 2005 Dmitry Torokhov + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/stddef.h> +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/wait.h> +#include <linux/completion.h> +#include <linux/sched.h> +#include <linux/smp_lock.h> +#include <linux/slab.h> +#include <linux/delay.h> + +/*#include <asm/io.h>*/ + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_DESCRIPTION("Generic gameport layer"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(__gameport_register_port); +EXPORT_SYMBOL(gameport_unregister_port); +EXPORT_SYMBOL(__gameport_register_driver); +EXPORT_SYMBOL(gameport_unregister_driver); +EXPORT_SYMBOL(gameport_open); +EXPORT_SYMBOL(gameport_close); +EXPORT_SYMBOL(gameport_rescan); +EXPORT_SYMBOL(gameport_cooked_read); +EXPORT_SYMBOL(gameport_set_name); +EXPORT_SYMBOL(gameport_set_phys); +EXPORT_SYMBOL(gameport_start_polling); +EXPORT_SYMBOL(gameport_stop_polling); + +/* + * gameport_sem protects entire gameport subsystem and is taken + * every time gameport port or driver registrered or unregistered. + */ +static DECLARE_MUTEX(gameport_sem); + +static LIST_HEAD(gameport_list); + +static struct bus_type gameport_bus = { + .name = "gameport", +}; + +static void gameport_add_port(struct gameport *gameport); +static void gameport_destroy_port(struct gameport *gameport); +static void gameport_reconnect_port(struct gameport *gameport); +static void gameport_disconnect_port(struct gameport *gameport); + +#if defined(__i386__) + +#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193182/HZ:0)) +#define GET_TIME(x) do { x = get_time_pit(); } while (0) + +static unsigned int get_time_pit(void) +{ + extern spinlock_t i8253_lock; + unsigned long flags; + unsigned int count; + + spin_lock_irqsave(&i8253_lock, flags); + outb_p(0x00, 0x43); + count = inb_p(0x40); + count |= inb_p(0x40) << 8; + spin_unlock_irqrestore(&i8253_lock, flags); + + return count; +} + +#endif + + + +/* + * gameport_measure_speed() measures the gameport i/o speed. + */ + +static int gameport_measure_speed(struct gameport *gameport) +{ +#if defined(__i386__) + + unsigned int i, t, t1, t2, t3, tx; + unsigned long flags; + + if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW)) + return 0; + + tx = 1 << 30; + + for(i = 0; i < 50; i++) { + local_irq_save(flags); + GET_TIME(t1); + for (t = 0; t < 50; t++) gameport_read(gameport); + GET_TIME(t2); + GET_TIME(t3); + local_irq_restore(flags); + udelay(i * 10); + if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; + } + + gameport_close(gameport); + return 59659 / (tx < 1 ? 1 : tx); + +#elif defined (__x86_64__) + + unsigned int i, t; + unsigned long tx, t1, t2, flags; + + if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW)) + return 0; + + tx = 1 << 30; + + for(i = 0; i < 50; i++) { + local_irq_save(flags); + rdtscl(t1); + for (t = 0; t < 50; t++) gameport_read(gameport); + rdtscl(t2); + local_irq_restore(flags); + udelay(i * 10); + if (t2 - t1 < tx) tx = t2 - t1; + } + + gameport_close(gameport); + return (cpu_data[_smp_processor_id()].loops_per_jiffy * (unsigned long)HZ / (1000 / 50)) / (tx < 1 ? 1 : tx); + +#else + + unsigned int j, t = 0; + + if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW)) + return 0; + + j = jiffies; while (j == jiffies); + j = jiffies; while (j == jiffies) { t++; gameport_read(gameport); } + + gameport_close(gameport); + return t * HZ / 1000; + +#endif +} + +void gameport_start_polling(struct gameport *gameport) +{ + spin_lock(&gameport->timer_lock); + + if (!gameport->poll_cnt++) { + BUG_ON(!gameport->poll_handler); + BUG_ON(!gameport->poll_interval); + mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval)); + } + + spin_unlock(&gameport->timer_lock); +} + +void gameport_stop_polling(struct gameport *gameport) +{ + spin_lock(&gameport->timer_lock); + + if (!--gameport->poll_cnt) + del_timer(&gameport->poll_timer); + + spin_unlock(&gameport->timer_lock); +} + +static void gameport_run_poll_handler(unsigned long d) +{ + struct gameport *gameport = (struct gameport *)d; + + gameport->poll_handler(gameport); + if (gameport->poll_cnt) + mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval)); +} + +/* + * Basic gameport -> driver core mappings + */ + +static void gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv) +{ + down_write(&gameport_bus.subsys.rwsem); + + gameport->dev.driver = &drv->driver; + if (drv->connect(gameport, drv)) { + gameport->dev.driver = NULL; + goto out; + } + device_bind_driver(&gameport->dev); +out: + up_write(&gameport_bus.subsys.rwsem); +} + +static void gameport_release_driver(struct gameport *gameport) +{ + down_write(&gameport_bus.subsys.rwsem); + device_release_driver(&gameport->dev); + up_write(&gameport_bus.subsys.rwsem); +} + +static void gameport_find_driver(struct gameport *gameport) +{ + down_write(&gameport_bus.subsys.rwsem); + device_attach(&gameport->dev); + up_write(&gameport_bus.subsys.rwsem); +} + + +/* + * Gameport event processing. + */ + +enum gameport_event_type { + GAMEPORT_RESCAN, + GAMEPORT_RECONNECT, + GAMEPORT_REGISTER_PORT, + GAMEPORT_REGISTER_DRIVER, +}; + +struct gameport_event { + enum gameport_event_type type; + void *object; + struct module *owner; + struct list_head node; +}; + +static DEFINE_SPINLOCK(gameport_event_lock); /* protects gameport_event_list */ +static LIST_HEAD(gameport_event_list); +static DECLARE_WAIT_QUEUE_HEAD(gameport_wait); +static DECLARE_COMPLETION(gameport_exited); +static int gameport_pid; + +static void gameport_queue_event(void *object, struct module *owner, + enum gameport_event_type event_type) +{ + unsigned long flags; + struct gameport_event *event; + + spin_lock_irqsave(&gameport_event_lock, flags); + + /* + * Scan event list for the other events for the same gameport port, + * starting with the most recent one. If event is the same we + * do not need add new one. If event is of different type we + * need to add this event and should not look further because + * we need to preseve sequence of distinct events. + */ + list_for_each_entry_reverse(event, &gameport_event_list, node) { + if (event->object == object) { + if (event->type == event_type) + goto out; + break; + } + } + + if ((event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC))) { + if (!try_module_get(owner)) { + printk(KERN_WARNING "gameport: Can't get module reference, dropping event %d\n", event_type); + goto out; + } + + event->type = event_type; + event->object = object; + event->owner = owner; + + list_add_tail(&event->node, &gameport_event_list); + wake_up(&gameport_wait); + } else { + printk(KERN_ERR "gameport: Not enough memory to queue event %d\n", event_type); + } +out: + spin_unlock_irqrestore(&gameport_event_lock, flags); +} + +static void gameport_free_event(struct gameport_event *event) +{ + module_put(event->owner); + kfree(event); +} + +static void gameport_remove_duplicate_events(struct gameport_event *event) +{ + struct list_head *node, *next; + struct gameport_event *e; + unsigned long flags; + + spin_lock_irqsave(&gameport_event_lock, flags); + + list_for_each_safe(node, next, &gameport_event_list) { + e = list_entry(node, struct gameport_event, node); + if (event->object == e->object) { + /* + * If this event is of different type we should not + * look further - we only suppress duplicate events + * that were sent back-to-back. + */ + if (event->type != e->type) + break; + + list_del_init(node); + gameport_free_event(e); + } + } + + spin_unlock_irqrestore(&gameport_event_lock, flags); +} + + +static struct gameport_event *gameport_get_event(void) +{ + struct gameport_event *event; + struct list_head *node; + unsigned long flags; + + spin_lock_irqsave(&gameport_event_lock, flags); + + if (list_empty(&gameport_event_list)) { + spin_unlock_irqrestore(&gameport_event_lock, flags); + return NULL; + } + + node = gameport_event_list.next; + event = list_entry(node, struct gameport_event, node); + list_del_init(node); + < |