^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) * Copyright (c) 1998-2001 Vojtech Pavlik
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * PDPI Lightning 4 gamecard driver for Linux.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <asm/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/errno.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/ioport.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/gameport.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #define L4_PORT 0x201
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #define L4_SELECT_ANALOG 0xa4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #define L4_SELECT_DIGITAL 0xa5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #define L4_SELECT_SECONDARY 0xa6
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define L4_CMD_ID 0x80
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #define L4_CMD_GETCAL 0x92
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #define L4_CMD_SETCAL 0x93
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #define L4_ID 0x04
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #define L4_BUSY 0x01
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #define L4_TIMEOUT 80 /* 80 us */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) MODULE_DESCRIPTION("PDPI Lightning 4 gamecard driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) struct l4 {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) struct gameport *gameport;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) unsigned char port;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) static struct l4 l4_ports[8];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) * l4_wait_ready() waits for the L4 to become ready.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) static int l4_wait_ready(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) unsigned int t = L4_TIMEOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) while ((inb(L4_PORT) & L4_BUSY) && t > 0) t--;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) return -(t <= 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) * l4_cooked_read() reads data from the Lightning 4.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) static int l4_cooked_read(struct gameport *gameport, int *axes, int *buttons)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) struct l4 *l4 = gameport->port_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) unsigned char status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) int i, result = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) outb(L4_SELECT_ANALOG, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) outb(L4_SELECT_DIGITAL + (l4->port >> 2), L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) if (inb(L4_PORT) & L4_BUSY) goto fail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) outb(l4->port & 3, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) if (l4_wait_ready()) goto fail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) status = inb(L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) for (i = 0; i < 4; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) if (status & (1 << i)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) if (l4_wait_ready()) goto fail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) axes[i] = inb(L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) if (axes[i] > 252) axes[i] = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) if (status & 0x10) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) if (l4_wait_ready()) goto fail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) *buttons = inb(L4_PORT) & 0x0f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) result = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) fail: outb(L4_SELECT_ANALOG, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) return result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) static int l4_open(struct gameport *gameport, int mode)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) struct l4 *l4 = gameport->port_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) outb(L4_SELECT_ANALOG, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) * l4_getcal() reads the L4 with calibration values.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) static int l4_getcal(int port, int *cal)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) int i, result = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) outb(L4_SELECT_ANALOG, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) if (inb(L4_PORT) & L4_BUSY)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) outb(L4_CMD_GETCAL, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) if (l4_wait_ready())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) if (l4_wait_ready())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) outb(port & 3, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) for (i = 0; i < 4; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) if (l4_wait_ready())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) cal[i] = inb(L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) result = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) out: outb(L4_SELECT_ANALOG, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) return result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) }
^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) * l4_setcal() programs the L4 with calibration values.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) static int l4_setcal(int port, int *cal)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) int i, result = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) outb(L4_SELECT_ANALOG, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) if (inb(L4_PORT) & L4_BUSY)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) outb(L4_CMD_SETCAL, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) if (l4_wait_ready())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) if (l4_wait_ready())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) outb(port & 3, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) for (i = 0; i < 4; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) if (l4_wait_ready())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) outb(cal[i], L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) result = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) out: outb(L4_SELECT_ANALOG, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) return result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) * l4_calibrate() calibrates the L4 for the attached device, so
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) * that the device's resistance fits into the L4's 8-bit range.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) static int l4_calibrate(struct gameport *gameport, int *axes, int *max)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) int i, t;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) int cal[4];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) struct l4 *l4 = gameport->port_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) if (l4_getcal(l4->port, cal))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) for (i = 0; i < 4; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) t = (max[i] * cal[i]) / 200;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) t = (t < 1) ? 1 : ((t > 255) ? 255 : t);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) axes[i] = (axes[i] < 0) ? -1 : (axes[i] * cal[i]) / t;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) axes[i] = (axes[i] > 252) ? 252 : axes[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) cal[i] = t;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) if (l4_setcal(l4->port, cal))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) static int __init l4_create_ports(int card_no)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) struct l4 *l4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) struct gameport *port;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) int i, idx;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) for (i = 0; i < 4; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) idx = card_no * 4 + i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) l4 = &l4_ports[idx];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) if (!(l4->gameport = port = gameport_allocate_port())) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) printk(KERN_ERR "lightning: Memory allocation failed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) while (--i >= 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) gameport_free_port(l4->gameport);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) l4->gameport = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) l4->port = idx;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) port->port_data = l4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) port->open = l4_open;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) port->cooked_read = l4_cooked_read;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) port->calibrate = l4_calibrate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) gameport_set_name(port, "PDPI Lightning 4");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) gameport_set_phys(port, "isa%04x/gameport%d", L4_PORT, idx);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) if (idx == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) port->io = L4_PORT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) static int __init l4_add_card(int card_no)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) int cal[4] = { 255, 255, 255, 255 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) int i, rev, result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) struct l4 *l4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) outb(L4_SELECT_ANALOG, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) outb(L4_SELECT_DIGITAL + card_no, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) if (inb(L4_PORT) & L4_BUSY)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) outb(L4_CMD_ID, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) if (l4_wait_ready())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) if (inb(L4_PORT) != L4_SELECT_DIGITAL + card_no)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) if (l4_wait_ready())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) if (inb(L4_PORT) != L4_ID)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) if (l4_wait_ready())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) rev = inb(L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) if (!rev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) result = l4_create_ports(card_no);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) if (result)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) return result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) printk(KERN_INFO "gameport: PDPI Lightning 4 %s card v%d.%d at %#x\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) card_no ? "secondary" : "primary", rev >> 4, rev, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) for (i = 0; i < 4; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) l4 = &l4_ports[card_no * 4 + i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) if (rev > 0x28) /* on 2.9+ the setcal command works correctly */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) l4_setcal(l4->port, cal);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) gameport_register_port(l4->gameport);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) static int __init l4_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) int i, cards = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) if (!request_region(L4_PORT, 1, "lightning"))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) for (i = 0; i < 2; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) if (l4_add_card(i) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) cards++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) outb(L4_SELECT_ANALOG, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) if (!cards) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) release_region(L4_PORT, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) static void __exit l4_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) int cal[4] = { 59, 59, 59, 59 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) for (i = 0; i < 8; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) if (l4_ports[i].gameport) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) l4_setcal(l4_ports[i].port, cal);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) gameport_unregister_port(l4_ports[i].gameport);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) outb(L4_SELECT_ANALOG, L4_PORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) release_region(L4_PORT, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) module_init(l4_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) module_exit(l4_exit);