^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * FM801 gameport driver for Linux
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (c) by Takashi Iwai <tiwai@suse.de>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <asm/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/errno.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/ioport.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/pci.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/gameport.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #define PCI_VENDOR_ID_FORTEMEDIA 0x1319
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #define PCI_DEVICE_ID_FM801_GP 0x0802
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #define HAVE_COOKED
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) struct fm801_gp {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) struct gameport *gameport;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) struct resource *res_port;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #ifdef HAVE_COOKED
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) static int fm801_gp_cooked_read(struct gameport *gameport, int *axes, int *buttons)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) unsigned short w;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) w = inw(gameport->io + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) *buttons = (~w >> 14) & 0x03;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) axes[0] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) w = inw(gameport->io + 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) axes[1] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) w = inw(gameport->io + 6);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) *buttons |= ((~w >> 14) & 0x03) << 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) axes[2] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) w = inw(gameport->io + 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) axes[3] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) outw(0xff, gameport->io); /* reset */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) static int fm801_gp_open(struct gameport *gameport, int mode)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) switch (mode) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) #ifdef HAVE_COOKED
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) case GAMEPORT_MODE_COOKED:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) case GAMEPORT_MODE_RAW:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) static int fm801_gp_probe(struct pci_dev *pci, const struct pci_device_id *id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) struct fm801_gp *gp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) struct gameport *port;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) int error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) gp = kzalloc(sizeof(struct fm801_gp), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) port = gameport_allocate_port();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) if (!gp || !port) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) printk(KERN_ERR "fm801-gp: Memory allocation failed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) error = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) goto err_out_free;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) error = pci_enable_device(pci);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) if (error)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) goto err_out_free;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) port->open = fm801_gp_open;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) #ifdef HAVE_COOKED
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) port->cooked_read = fm801_gp_cooked_read;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) gameport_set_name(port, "FM801");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) gameport_set_phys(port, "pci%s/gameport0", pci_name(pci));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) port->dev.parent = &pci->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) port->io = pci_resource_start(pci, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) gp->gameport = port;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) gp->res_port = request_region(port->io, 0x10, "FM801 GP");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) if (!gp->res_port) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) printk(KERN_DEBUG "fm801-gp: unable to grab region 0x%x-0x%x\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) port->io, port->io + 0x0f);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) error = -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) goto err_out_disable_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) pci_set_drvdata(pci, gp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) outb(0x60, port->io + 0x0d); /* enable joystick 1 and 2 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) gameport_register_port(port);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) err_out_disable_dev:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) pci_disable_device(pci);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) err_out_free:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) gameport_free_port(port);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) kfree(gp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) static void fm801_gp_remove(struct pci_dev *pci)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) struct fm801_gp *gp = pci_get_drvdata(pci);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) gameport_unregister_port(gp->gameport);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) release_resource(gp->res_port);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) kfree(gp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) pci_disable_device(pci);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) static const struct pci_device_id fm801_gp_id_table[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) { PCI_VENDOR_ID_FORTEMEDIA, PCI_DEVICE_ID_FM801_GP, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) { 0 }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) MODULE_DEVICE_TABLE(pci, fm801_gp_id_table);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) static struct pci_driver fm801_gp_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) .name = "FM801_gameport",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) .id_table = fm801_gp_id_table,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) .probe = fm801_gp_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) .remove = fm801_gp_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) module_pci_driver(fm801_gp_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) MODULE_DESCRIPTION("FM801 gameport driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) MODULE_LICENSE("GPL");