fm801-gp.c 3.2 KB

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