Path: blob/master/drivers/input/gameport/lightning.c
17293 views
/*1* Copyright (c) 1998-2001 Vojtech Pavlik2*/34/*5* PDPI Lightning 4 gamecard driver for Linux.6*/78/*9* This program is free software; you can redistribute it and/or modify10* it under the terms of the GNU General Public License as published by11* the Free Software Foundation; either version 2 of the License, or12* (at your option) any later version.13*14* This program is distributed in the hope that it will be useful,15* but WITHOUT ANY WARRANTY; without even the implied warranty of16* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the17* GNU General Public License for more details.18*19* You should have received a copy of the GNU General Public License20* along with this program; if not, write to the Free Software21* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA22*23* Should you need to contact me, the author, you can do so either by24* e-mail - mail your message to <[email protected]>, or by paper mail:25* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic26*/2728#include <asm/io.h>29#include <linux/delay.h>30#include <linux/errno.h>31#include <linux/ioport.h>32#include <linux/kernel.h>33#include <linux/module.h>34#include <linux/init.h>35#include <linux/gameport.h>3637#define L4_PORT 0x20138#define L4_SELECT_ANALOG 0xa439#define L4_SELECT_DIGITAL 0xa540#define L4_SELECT_SECONDARY 0xa641#define L4_CMD_ID 0x8042#define L4_CMD_GETCAL 0x9243#define L4_CMD_SETCAL 0x9344#define L4_ID 0x0445#define L4_BUSY 0x0146#define L4_TIMEOUT 80 /* 80 us */4748MODULE_AUTHOR("Vojtech Pavlik <[email protected]>");49MODULE_DESCRIPTION("PDPI Lightning 4 gamecard driver");50MODULE_LICENSE("GPL");5152struct l4 {53struct gameport *gameport;54unsigned char port;55};5657static struct l4 l4_ports[8];5859/*60* l4_wait_ready() waits for the L4 to become ready.61*/6263static int l4_wait_ready(void)64{65unsigned int t = L4_TIMEOUT;6667while ((inb(L4_PORT) & L4_BUSY) && t > 0) t--;68return -(t <= 0);69}7071/*72* l4_cooked_read() reads data from the Lightning 4.73*/7475static int l4_cooked_read(struct gameport *gameport, int *axes, int *buttons)76{77struct l4 *l4 = gameport->port_data;78unsigned char status;79int i, result = -1;8081outb(L4_SELECT_ANALOG, L4_PORT);82outb(L4_SELECT_DIGITAL + (l4->port >> 2), L4_PORT);8384if (inb(L4_PORT) & L4_BUSY) goto fail;85outb(l4->port & 3, L4_PORT);8687if (l4_wait_ready()) goto fail;88status = inb(L4_PORT);8990for (i = 0; i < 4; i++)91if (status & (1 << i)) {92if (l4_wait_ready()) goto fail;93axes[i] = inb(L4_PORT);94if (axes[i] > 252) axes[i] = -1;95}9697if (status & 0x10) {98if (l4_wait_ready()) goto fail;99*buttons = inb(L4_PORT) & 0x0f;100}101102result = 0;103104fail: outb(L4_SELECT_ANALOG, L4_PORT);105return result;106}107108static int l4_open(struct gameport *gameport, int mode)109{110struct l4 *l4 = gameport->port_data;111112if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED)113return -1;114outb(L4_SELECT_ANALOG, L4_PORT);115return 0;116}117118/*119* l4_getcal() reads the L4 with calibration values.120*/121122static int l4_getcal(int port, int *cal)123{124int i, result = -1;125126outb(L4_SELECT_ANALOG, L4_PORT);127outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);128if (inb(L4_PORT) & L4_BUSY)129goto out;130131outb(L4_CMD_GETCAL, L4_PORT);132if (l4_wait_ready())133goto out;134135if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))136goto out;137138if (l4_wait_ready())139goto out;140outb(port & 3, L4_PORT);141142for (i = 0; i < 4; i++) {143if (l4_wait_ready())144goto out;145cal[i] = inb(L4_PORT);146}147148result = 0;149150out: outb(L4_SELECT_ANALOG, L4_PORT);151return result;152}153154/*155* l4_setcal() programs the L4 with calibration values.156*/157158static int l4_setcal(int port, int *cal)159{160int i, result = -1;161162outb(L4_SELECT_ANALOG, L4_PORT);163outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);164if (inb(L4_PORT) & L4_BUSY)165goto out;166167outb(L4_CMD_SETCAL, L4_PORT);168if (l4_wait_ready())169goto out;170171if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))172goto out;173174if (l4_wait_ready())175goto out;176outb(port & 3, L4_PORT);177178for (i = 0; i < 4; i++) {179if (l4_wait_ready())180goto out;181outb(cal[i], L4_PORT);182}183184result = 0;185186out: outb(L4_SELECT_ANALOG, L4_PORT);187return result;188}189190/*191* l4_calibrate() calibrates the L4 for the attached device, so192* that the device's resistance fits into the L4's 8-bit range.193*/194195static int l4_calibrate(struct gameport *gameport, int *axes, int *max)196{197int i, t;198int cal[4];199struct l4 *l4 = gameport->port_data;200201if (l4_getcal(l4->port, cal))202return -1;203204for (i = 0; i < 4; i++) {205t = (max[i] * cal[i]) / 200;206t = (t < 1) ? 1 : ((t > 255) ? 255 : t);207axes[i] = (axes[i] < 0) ? -1 : (axes[i] * cal[i]) / t;208axes[i] = (axes[i] > 252) ? 252 : axes[i];209cal[i] = t;210}211212if (l4_setcal(l4->port, cal))213return -1;214215return 0;216}217218static int __init l4_create_ports(int card_no)219{220struct l4 *l4;221struct gameport *port;222int i, idx;223224for (i = 0; i < 4; i++) {225226idx = card_no * 4 + i;227l4 = &l4_ports[idx];228229if (!(l4->gameport = port = gameport_allocate_port())) {230printk(KERN_ERR "lightning: Memory allocation failed\n");231while (--i >= 0) {232gameport_free_port(l4->gameport);233l4->gameport = NULL;234}235return -ENOMEM;236}237l4->port = idx;238239port->port_data = l4;240port->open = l4_open;241port->cooked_read = l4_cooked_read;242port->calibrate = l4_calibrate;243244gameport_set_name(port, "PDPI Lightning 4");245gameport_set_phys(port, "isa%04x/gameport%d", L4_PORT, idx);246247if (idx == 0)248port->io = L4_PORT;249}250251return 0;252}253254static int __init l4_add_card(int card_no)255{256int cal[4] = { 255, 255, 255, 255 };257int i, rev, result;258struct l4 *l4;259260outb(L4_SELECT_ANALOG, L4_PORT);261outb(L4_SELECT_DIGITAL + card_no, L4_PORT);262263if (inb(L4_PORT) & L4_BUSY)264return -1;265outb(L4_CMD_ID, L4_PORT);266267if (l4_wait_ready())268return -1;269270if (inb(L4_PORT) != L4_SELECT_DIGITAL + card_no)271return -1;272273if (l4_wait_ready())274return -1;275if (inb(L4_PORT) != L4_ID)276return -1;277278if (l4_wait_ready())279return -1;280rev = inb(L4_PORT);281282if (!rev)283return -1;284285result = l4_create_ports(card_no);286if (result)287return result;288289printk(KERN_INFO "gameport: PDPI Lightning 4 %s card v%d.%d at %#x\n",290card_no ? "secondary" : "primary", rev >> 4, rev, L4_PORT);291292for (i = 0; i < 4; i++) {293l4 = &l4_ports[card_no * 4 + i];294295if (rev > 0x28) /* on 2.9+ the setcal command works correctly */296l4_setcal(l4->port, cal);297gameport_register_port(l4->gameport);298}299300return 0;301}302303static int __init l4_init(void)304{305int i, cards = 0;306307if (!request_region(L4_PORT, 1, "lightning"))308return -EBUSY;309310for (i = 0; i < 2; i++)311if (l4_add_card(i) == 0)312cards++;313314outb(L4_SELECT_ANALOG, L4_PORT);315316if (!cards) {317release_region(L4_PORT, 1);318return -ENODEV;319}320321return 0;322}323324static void __exit l4_exit(void)325{326int i;327int cal[4] = { 59, 59, 59, 59 };328329for (i = 0; i < 8; i++)330if (l4_ports[i].gameport) {331l4_setcal(l4_ports[i].port, cal);332gameport_unregister_port(l4_ports[i].gameport);333}334335outb(L4_SELECT_ANALOG, L4_PORT);336release_region(L4_PORT, 1);337}338339module_init(l4_init);340module_exit(l4_exit);341342343