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