Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  Copyright (c) 1998-2001 Vojtech Pavlik
0004  */
0005 
0006 /*
0007  * PDPI Lightning 4 gamecard driver for Linux.
0008  */
0009 
0010 /*
0011  */
0012 
0013 #include <asm/io.h>
0014 #include <linux/delay.h>
0015 #include <linux/errno.h>
0016 #include <linux/ioport.h>
0017 #include <linux/kernel.h>
0018 #include <linux/module.h>
0019 #include <linux/init.h>
0020 #include <linux/gameport.h>
0021 
0022 #define L4_PORT         0x201
0023 #define L4_SELECT_ANALOG    0xa4
0024 #define L4_SELECT_DIGITAL   0xa5
0025 #define L4_SELECT_SECONDARY 0xa6
0026 #define L4_CMD_ID       0x80
0027 #define L4_CMD_GETCAL       0x92
0028 #define L4_CMD_SETCAL       0x93
0029 #define L4_ID           0x04
0030 #define L4_BUSY         0x01
0031 #define L4_TIMEOUT      80  /* 80 us */
0032 
0033 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
0034 MODULE_DESCRIPTION("PDPI Lightning 4 gamecard driver");
0035 MODULE_LICENSE("GPL");
0036 
0037 struct l4 {
0038     struct gameport *gameport;
0039     unsigned char port;
0040 };
0041 
0042 static struct l4 l4_ports[8];
0043 
0044 /*
0045  * l4_wait_ready() waits for the L4 to become ready.
0046  */
0047 
0048 static int l4_wait_ready(void)
0049 {
0050     unsigned int t = L4_TIMEOUT;
0051 
0052     while ((inb(L4_PORT) & L4_BUSY) && t > 0) t--;
0053     return -(t <= 0);
0054 }
0055 
0056 /*
0057  * l4_cooked_read() reads data from the Lightning 4.
0058  */
0059 
0060 static int l4_cooked_read(struct gameport *gameport, int *axes, int *buttons)
0061 {
0062     struct l4 *l4 = gameport->port_data;
0063     unsigned char status;
0064     int i, result = -1;
0065 
0066     outb(L4_SELECT_ANALOG, L4_PORT);
0067     outb(L4_SELECT_DIGITAL + (l4->port >> 2), L4_PORT);
0068 
0069     if (inb(L4_PORT) & L4_BUSY) goto fail;
0070     outb(l4->port & 3, L4_PORT);
0071 
0072     if (l4_wait_ready()) goto fail;
0073     status = inb(L4_PORT);
0074 
0075     for (i = 0; i < 4; i++)
0076         if (status & (1 << i)) {
0077             if (l4_wait_ready()) goto fail;
0078             axes[i] = inb(L4_PORT);
0079             if (axes[i] > 252) axes[i] = -1;
0080         }
0081 
0082     if (status & 0x10) {
0083         if (l4_wait_ready()) goto fail;
0084         *buttons = inb(L4_PORT) & 0x0f;
0085     }
0086 
0087     result = 0;
0088 
0089 fail:   outb(L4_SELECT_ANALOG, L4_PORT);
0090     return result;
0091 }
0092 
0093 static int l4_open(struct gameport *gameport, int mode)
0094 {
0095     struct l4 *l4 = gameport->port_data;
0096 
0097         if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED)
0098         return -1;
0099     outb(L4_SELECT_ANALOG, L4_PORT);
0100     return 0;
0101 }
0102 
0103 /*
0104  * l4_getcal() reads the L4 with calibration values.
0105  */
0106 
0107 static int l4_getcal(int port, int *cal)
0108 {
0109     int i, result = -1;
0110 
0111     outb(L4_SELECT_ANALOG, L4_PORT);
0112     outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
0113     if (inb(L4_PORT) & L4_BUSY)
0114         goto out;
0115 
0116     outb(L4_CMD_GETCAL, L4_PORT);
0117     if (l4_wait_ready())
0118         goto out;
0119 
0120     if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))
0121         goto out;
0122 
0123     if (l4_wait_ready())
0124         goto out;
0125         outb(port & 3, L4_PORT);
0126 
0127     for (i = 0; i < 4; i++) {
0128         if (l4_wait_ready())
0129             goto out;
0130         cal[i] = inb(L4_PORT);
0131     }
0132 
0133     result = 0;
0134 
0135 out:    outb(L4_SELECT_ANALOG, L4_PORT);
0136     return result;
0137 }
0138 
0139 /*
0140  * l4_setcal() programs the L4 with calibration values.
0141  */
0142 
0143 static int l4_setcal(int port, int *cal)
0144 {
0145     int i, result = -1;
0146 
0147     outb(L4_SELECT_ANALOG, L4_PORT);
0148     outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
0149     if (inb(L4_PORT) & L4_BUSY)
0150         goto out;
0151 
0152     outb(L4_CMD_SETCAL, L4_PORT);
0153     if (l4_wait_ready())
0154         goto out;
0155 
0156     if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))
0157         goto out;
0158 
0159     if (l4_wait_ready())
0160         goto out;
0161         outb(port & 3, L4_PORT);
0162 
0163     for (i = 0; i < 4; i++) {
0164         if (l4_wait_ready())
0165             goto out;
0166         outb(cal[i], L4_PORT);
0167     }
0168 
0169     result = 0;
0170 
0171 out:    outb(L4_SELECT_ANALOG, L4_PORT);
0172     return result;
0173 }
0174 
0175 /*
0176  * l4_calibrate() calibrates the L4 for the attached device, so
0177  * that the device's resistance fits into the L4's 8-bit range.
0178  */
0179 
0180 static int l4_calibrate(struct gameport *gameport, int *axes, int *max)
0181 {
0182     int i, t;
0183     int cal[4];
0184     struct l4 *l4 = gameport->port_data;
0185 
0186     if (l4_getcal(l4->port, cal))
0187         return -1;
0188 
0189     for (i = 0; i < 4; i++) {
0190         t = (max[i] * cal[i]) / 200;
0191         t = (t < 1) ? 1 : ((t > 255) ? 255 : t);
0192         axes[i] = (axes[i] < 0) ? -1 : (axes[i] * cal[i]) / t;
0193         axes[i] = (axes[i] > 252) ? 252 : axes[i];
0194         cal[i] = t;
0195     }
0196 
0197     if (l4_setcal(l4->port, cal))
0198         return -1;
0199 
0200     return 0;
0201 }
0202 
0203 static int __init l4_create_ports(int card_no)
0204 {
0205     struct l4 *l4;
0206     struct gameport *port;
0207     int i, idx;
0208 
0209     for (i = 0; i < 4; i++) {
0210 
0211         idx = card_no * 4 + i;
0212         l4 = &l4_ports[idx];
0213 
0214         if (!(l4->gameport = port = gameport_allocate_port())) {
0215             printk(KERN_ERR "lightning: Memory allocation failed\n");
0216             while (--i >= 0) {
0217                 gameport_free_port(l4->gameport);
0218                 l4->gameport = NULL;
0219             }
0220             return -ENOMEM;
0221         }
0222         l4->port = idx;
0223 
0224         port->port_data = l4;
0225         port->open = l4_open;
0226         port->cooked_read = l4_cooked_read;
0227         port->calibrate = l4_calibrate;
0228 
0229         gameport_set_name(port, "PDPI Lightning 4");
0230         gameport_set_phys(port, "isa%04x/gameport%d", L4_PORT, idx);
0231 
0232         if (idx == 0)
0233             port->io = L4_PORT;
0234     }
0235 
0236     return 0;
0237 }
0238 
0239 static int __init l4_add_card(int card_no)
0240 {
0241     int cal[4] = { 255, 255, 255, 255 };
0242     int i, rev, result;
0243     struct l4 *l4;
0244 
0245     outb(L4_SELECT_ANALOG, L4_PORT);
0246     outb(L4_SELECT_DIGITAL + card_no, L4_PORT);
0247 
0248     if (inb(L4_PORT) & L4_BUSY)
0249         return -1;
0250     outb(L4_CMD_ID, L4_PORT);
0251 
0252     if (l4_wait_ready())
0253         return -1;
0254 
0255     if (inb(L4_PORT) != L4_SELECT_DIGITAL + card_no)
0256         return -1;
0257 
0258     if (l4_wait_ready())
0259         return -1;
0260     if (inb(L4_PORT) != L4_ID)
0261         return -1;
0262 
0263     if (l4_wait_ready())
0264         return -1;
0265     rev = inb(L4_PORT);
0266 
0267     if (!rev)
0268         return -1;
0269 
0270     result = l4_create_ports(card_no);
0271     if (result)
0272         return result;
0273 
0274     printk(KERN_INFO "gameport: PDPI Lightning 4 %s card v%d.%d at %#x\n",
0275         card_no ? "secondary" : "primary", rev >> 4, rev, L4_PORT);
0276 
0277     for (i = 0; i < 4; i++) {
0278         l4 = &l4_ports[card_no * 4 + i];
0279 
0280         if (rev > 0x28)     /* on 2.9+ the setcal command works correctly */
0281             l4_setcal(l4->port, cal);
0282         gameport_register_port(l4->gameport);
0283     }
0284 
0285     return 0;
0286 }
0287 
0288 static int __init l4_init(void)
0289 {
0290     int i, cards = 0;
0291 
0292     if (!request_region(L4_PORT, 1, "lightning"))
0293         return -EBUSY;
0294 
0295     for (i = 0; i < 2; i++)
0296         if (l4_add_card(i) == 0)
0297             cards++;
0298 
0299     outb(L4_SELECT_ANALOG, L4_PORT);
0300 
0301     if (!cards) {
0302         release_region(L4_PORT, 1);
0303         return -ENODEV;
0304     }
0305 
0306     return 0;
0307 }
0308 
0309 static void __exit l4_exit(void)
0310 {
0311     int i;
0312     int cal[4] = { 59, 59, 59, 59 };
0313 
0314     for (i = 0; i < 8; i++)
0315         if (l4_ports[i].gameport) {
0316             l4_setcal(l4_ports[i].port, cal);
0317             gameport_unregister_port(l4_ports[i].gameport);
0318         }
0319 
0320     outb(L4_SELECT_ANALOG, L4_PORT);
0321     release_region(L4_PORT, 1);
0322 }
0323 
0324 module_init(l4_init);
0325 module_exit(l4_exit);