1 /* 2 * FM801 gameport driver for Linux 3 * 4 * Copyright (c) by Takashi Iwai <tiwai@suse.de> 5 * 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 * 21 */ 22 23 #include <asm/io.h> 24 #include <linux/delay.h> 25 #include <linux/errno.h> 26 #include <linux/ioport.h> 27 #include <linux/kernel.h> 28 #include <linux/module.h> 29 #include <linux/pci.h> 30 #include <linux/slab.h> 31 #include <linux/gameport.h> 32 33 #define PCI_VENDOR_ID_FORTEMEDIA 0x1319 34 #define PCI_DEVICE_ID_FM801_GP 0x0802 35 36 #define HAVE_COOKED 37 38 struct fm801_gp { 39 struct gameport *gameport; 40 struct resource *res_port; 41 }; 42 43 #ifdef HAVE_COOKED 44 static int fm801_gp_cooked_read(struct gameport *gameport, int *axes, int *buttons) 45 { 46 unsigned short w; 47 48 w = inw(gameport->io + 2); 49 *buttons = (~w >> 14) & 0x03; 50 axes[0] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5); 51 w = inw(gameport->io + 4); 52 axes[1] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5); 53 w = inw(gameport->io + 6); 54 *buttons |= ((~w >> 14) & 0x03) << 2; 55 axes[2] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5); 56 w = inw(gameport->io + 8); 57 axes[3] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5); 58 outw(0xff, gameport->io); /* reset */ 59 60 return 0; 61 } 62 #endif 63 64 static int fm801_gp_open(struct gameport *gameport, int mode) 65 { 66 switch (mode) { 67 #ifdef HAVE_COOKED 68 case GAMEPORT_MODE_COOKED: 69 return 0; 70 #endif 71 case GAMEPORT_MODE_RAW: 72 return 0; 73 default: 74 return -1; 75 } 76 77 return 0; 78 } 79 80 static int fm801_gp_probe(struct pci_dev *pci, const struct pci_device_id *id) 81 { 82 struct fm801_gp *gp; 83 struct gameport *port; 84 int error; 85 86 gp = kzalloc(sizeof(struct fm801_gp), GFP_KERNEL); 87 port = gameport_allocate_port(); 88 if (!gp || !port) { 89 printk(KERN_ERR "fm801-gp: Memory allocation failed\n"); 90 error = -ENOMEM; 91 goto err_out_free; 92 } 93 94 error = pci_enable_device(pci); 95 if (error) 96 goto err_out_free; 97 98 port->open = fm801_gp_open; 99 #ifdef HAVE_COOKED 100 port->cooked_read = fm801_gp_cooked_read; 101 #endif 102 gameport_set_name(port, "FM801"); 103 gameport_set_phys(port, "pci%s/gameport0", pci_name(pci)); 104 port->dev.parent = &pci->dev; 105 port->io = pci_resource_start(pci, 0); 106 107 gp->gameport = port; 108 gp->res_port = request_region(port->io, 0x10, "FM801 GP"); 109 if (!gp->res_port) { 110 printk(KERN_DEBUG "fm801-gp: unable to grab region 0x%x-0x%x\n", 111 port->io, port->io + 0x0f); 112 error = -EBUSY; 113 goto err_out_disable_dev; 114 } 115 116 pci_set_drvdata(pci, gp); 117 118 outb(0x60, port->io + 0x0d); /* enable joystick 1 and 2 */ 119 gameport_register_port(port); 120 121 return 0; 122 123 err_out_disable_dev: 124 pci_disable_device(pci); 125 err_out_free: 126 gameport_free_port(port); 127 kfree(gp); 128 return error; 129 } 130 131 static void fm801_gp_remove(struct pci_dev *pci) 132 { 133 struct fm801_gp *gp = pci_get_drvdata(pci); 134 135 gameport_unregister_port(gp->gameport); 136 release_resource(gp->res_port); 137 kfree(gp); 138 139 pci_disable_device(pci); 140 } 141 142 static const struct pci_device_id fm801_gp_id_table[] = { 143 { PCI_VENDOR_ID_FORTEMEDIA, PCI_DEVICE_ID_FM801_GP, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 144 { 0 } 145 }; 146 MODULE_DEVICE_TABLE(pci, fm801_gp_id_table); 147 148 static struct pci_driver fm801_gp_driver = { 149 .name = "FM801_gameport", 150 .id_table = fm801_gp_id_table, 151 .probe = fm801_gp_probe, 152 .remove = fm801_gp_remove, 153 }; 154 155 module_pci_driver(fm801_gp_driver); 156 157 MODULE_DESCRIPTION("FM801 gameport driver"); 158 MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); 159 MODULE_LICENSE("GPL"); 160