blob: ab0a151bd56bcc27ade2db779a1985485a2560e8 [file] [log] [blame]
/*
* 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;
char phys[32];
char name[32];
};
#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;
if (! (gp = kmalloc(sizeof(*gp), GFP_KERNEL))) {
printk("cannot malloc for fm801-gp\n");
return -1;
}
memset(gp, 0, sizeof(*gp));
gp->gameport.open = fm801_gp_open;
#ifdef HAVE_COOKED
gp->gameport.cooked_read = fm801_gp_cooked_read;
#endif
pci_enable_device(pci);
gp->gameport.io = pci_resource_start(pci, 0);
if ((gp->res_port = request_region(gp->gameport.io, 0x10, "FM801 GP")) == NULL) {
printk("unable to grab region 0x%x-0x%x\n", gp->gameport.io, gp->gameport.io + 0x0f);
kfree(gp);
return -1;
}
gp->gameport.phys = gp->phys;
gp->gameport.name = gp->name;
gp->gameport.id.bustype = BUS_PCI;
gp->gameport.id.vendor = pci->vendor;
gp->gameport.id.product = pci->device;
pci_set_drvdata(pci, gp);
outb(0x60, gp->gameport.io + 0x0d); /* enable joystick 1 and 2 */
gameport_register_port(&gp->gameport);
printk(KERN_INFO "gameport: at pci%s speed %d kHz\n",
pci_name(pci), gp->gameport.speed);
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),
};
int __init fm801_gp_init(void)
{
return pci_module_init(&fm801_gp_driver);
}
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");