You can subscribe to this list here.
| 2001 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(135) |
Nov
(123) |
Dec
(83) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2002 |
Jan
(244) |
Feb
(72) |
Mar
(221) |
Apr
(91) |
May
(104) |
Jun
(93) |
Jul
(78) |
Aug
(1) |
Sep
(1) |
Oct
(29) |
Nov
(98) |
Dec
(20) |
| 2003 |
Jan
|
Feb
(21) |
Mar
|
Apr
(1) |
May
|
Jun
|
Jul
|
Aug
(18) |
Sep
(18) |
Oct
(23) |
Nov
(12) |
Dec
(6) |
| 2004 |
Jan
(2) |
Feb
(32) |
Mar
|
Apr
(12) |
May
(11) |
Jun
(11) |
Jul
|
Aug
(9) |
Sep
|
Oct
(15) |
Nov
|
Dec
|
| 2005 |
Jan
|
Feb
(2) |
Mar
(11) |
Apr
(6) |
May
(1) |
Jun
(9) |
Jul
(7) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(1) |
| 2006 |
Jan
|
Feb
(1) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
| 2007 |
Jan
|
Feb
(2) |
Mar
|
Apr
(25) |
May
(2) |
Jun
|
Jul
(5) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
| 2008 |
Jan
|
Feb
|
Mar
(1) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
| 2009 |
Jan
|
Feb
(1) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
| 2010 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(13) |
Oct
|
Nov
(2) |
Dec
(2) |
| 2011 |
Jan
|
Feb
|
Mar
(10) |
Apr
(10) |
May
(1) |
Jun
(6) |
Jul
|
Aug
(2) |
Sep
(5) |
Oct
|
Nov
|
Dec
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:43:45
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/serio
In directory usw-pr-cvs1:/tmp/cvs-serv11792
Modified Files:
Tag: 1.8
i8042.h
Log Message:
Moved.
--- NEW FILE: i8042.h ---
#ifndef _I8042_H
#define _I8042_H
/*
* $Id: i8042.h,v 1.8 2002/01/22 20:43:42 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
/*
* If you want to reset your i8042 upon boot, define this.
*/
#undef I8042_RESET
/*
* If you want to trace all the i/o the i8042 module does for
* debugging purposes, define this.
*/
#undef I8042_DEBUG_IO
/*
* On most PC based systems the keyboard IRQ is 1.
*/
#define I8042_KBD_IRQ CONFIG_I8042_KBD_IRQ
/*
* On most PC based systems the aux port IRQ is 12. There are exceptions,
* though. Unfortunately IRQ probing is not possible without touching
* the device attached to the port.
*/
#define I8042_AUX_IRQ CONFIG_I8042_AUX_IRQ
/*
* The speed of the i8042's varies. This timeout equals 100 ms on a system
* with 8.3 MHz i8042 clock, which should be most common. It should not need
* to be raised / lowered.
*/
#define I8042_CTL_TIMEOUT 83000
/*
* Register numbers.
*/
#define I8042_COMMAND_REG CONFIG_I8042_REG_BASE + 4
#define I8042_STATUS_REG CONFIG_I8042_REG_BASE + 4
#define I8042_DATA_REG CONFIG_I8042_REG_BASE
/*
* Status register bits.
*/
#define I8042_STR_PARITY 0x80
#define I8042_STR_TIMEOUT 0x40
#define I8042_STR_AUXDATA 0x20
#define I8042_STR_KEYLOCK 0x10
#define I8042_STR_CMDDAT 0x08
#define I8042_STR_IBF 0x02
#define I8042_STR_OBF 0x01
/*
* Control register bits.
*/
#define I8042_CTR_KBDINT 0x01
#define I8042_CTR_AUXINT 0x02
#define I8042_CTR_IGNKEYLOCK 0x08
#define I8042_CTR_KBDDIS 0x10
#define I8042_CTR_AUXDIS 0x20
#define I8042_CTR_XLATE 0x40
/*
* Commands.
*/
#define I8042_CMD_CTL_RCTR 0x0120
#define I8042_CMD_CTL_WCTR 0x1060
#define I8042_CMD_CTL_TEST 0x01aa
#define I8042_CMD_KBD_DISABLE 0x00ad
#define I8042_CMD_KBD_ENABLE 0x00ae
#define I8042_CMD_KBD_TEST 0x01ab
#define I8042_CMD_KBD_LOOP 0x11d2
#define I8042_CMD_AUX_DISABLE 0x00a7
#define I8042_CMD_AUX_ENABLE 0x00a8
#define I8042_CMD_AUX_TEST 0x01a9
#define I8042_CMD_AUX_SEND 0x10d4
#define I8042_CMD_AUX_LOOP 0x11d3
/*
* Return codes.
*/
#define I8042_RET_CTL_TEST 0x55
/*
* Expected maximum internal i8042 buffer size. This is used for flushing
* the i8042 buffers. 32 should be more than enough.
*/
#define I8042_BUFFER_SIZE 32
#endif _I8042_H
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:43:26
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/serio
In directory usw-pr-cvs1:/tmp/cvs-serv11619
Modified Files:
Tag: 1.16
i8042.c
Log Message:
Moved.
--- NEW FILE: i8042.c ---
/*
* $Id: i8042.c,v 1.16 2002/01/22 20:43:24 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* i8042 keyboard and mouse controller driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/config.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/serio.h>
#include "i8042.h"
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>");
MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver");
MODULE_LICENSE("GPL");
MODULE_PARM(i8042_noaux, "1i");
MODULE_PARM(i8042_unlock, "1i");
MODULE_PARM(i8042_reset, "1i");
MODULE_PARM(i8042_direct, "1i");
static int i8042_noaux;
static int i8042_unlock;
static int i8042_reset;
static int i8042_direct;
spinlock_t i8042_lock = SPIN_LOCK_UNLOCKED;
struct i8042_values {
int irq;
unsigned char disable;
unsigned char irqen;
unsigned char exists;
unsigned char *name;
unsigned char *phys;
};
static struct serio i8042_kbd_port;
static struct serio i8042_aux_port;
static unsigned char i8042_initial_ctr;
static unsigned char i8042_ctr;
#ifdef I8042_DEBUG_IO
static unsigned long i8042_start;
#endif
static unsigned long i8042_unxlate_seen[128 / BITS_PER_LONG];
static unsigned char i8042_unxlate_table[128] = {
0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3,
11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105,
114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63,
71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
};
static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs);
/*
* The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
* be ready for reading values from it / writing values to it.
*/
static int i8042_wait_read(void)
{
int i = 0;
while ((~inb(I8042_STATUS_REG) & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) i++;
return -(i == I8042_CTL_TIMEOUT);
}
static int i8042_wait_write(void)
{
int i = 0;
while ((inb(I8042_STATUS_REG) & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) i++;
return -(i == I8042_CTL_TIMEOUT);
}
/*
* i8042_flush() flushes all data that may be in the keyboard and mouse buffers
* of the i8042 down the toilet.
*/
static int i8042_flush(void)
{
unsigned long flags;
int i = 0;
spin_lock_irqsave(&i8042_lock, flags);
while ((inb(I8042_STATUS_REG) & I8042_STR_OBF) && (i++ < I8042_BUFFER_SIZE))
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x <- i8042 (flush) [%d]\n",
inb(I8042_DATA_REG), (int) (jiffies - i8042_start));
#else
inb(I8042_DATA_REG);
#endif
spin_unlock_irqrestore(&i8042_lock, flags);
return i;
}
/*
* i8042_command() executes a command on the i8042. It also sends the input parameter(s)
* of the commands to it, and receives the output value(s). The parameters are to be
* stored in the param array, and the output is placed into the same array. The number
* of the parameters and output values is encoded in bits 8-11 of the command
* number.
*/
static int i8042_command(unsigned char *param, int command)
{
unsigned long flags;
int retval = 0, i = 0;
spin_lock_irqsave(&i8042_lock, flags);
retval = i8042_wait_write();
if (!retval) {
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x -> i8042 (command) [%d]\n",
command & 0xff, (int) (jiffies - i8042_start));
#endif
outb(command & 0xff, I8042_COMMAND_REG);
}
if (!retval)
for (i = 0; i < ((command >> 12) & 0xf); i++) {
if ((retval = i8042_wait_write())) break;
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x -> i8042 (parameter) [%d]\n",
param[i], (int) (jiffies - i8042_start));
#endif
outb(param[i], I8042_DATA_REG);
}
if (!retval)
for (i = 0; i < ((command >> 8) & 0xf); i++) {
if ((retval = i8042_wait_read())) break;
if (inb(I8042_STATUS_REG) & I8042_STR_AUXDATA)
param[i] = ~inb(I8042_DATA_REG);
else
param[i] = inb(I8042_DATA_REG);
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x <- i8042 (return) [%d]\n",
param[i], (int) (jiffies - i8042_start));
#endif
}
spin_unlock_irqrestore(&i8042_lock, flags);
return retval;
}
/*
* i8042_kbd_write() sends a byte out through the keyboard interface.
* It also automatically refreshes the CTR value, since some i8042's
* trash their CTR after attempting to send data to an nonexistent
* device.
*/
static int i8042_kbd_write(struct serio *port, unsigned char c)
{
unsigned long flags;
int retval = 0;
spin_lock_irqsave(&i8042_lock, flags);
if(!(retval = i8042_wait_write())) {
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x -> i8042 (kbd-data) [%d]\n",
c, (int) (jiffies - i8042_start));
#endif
outb(c, I8042_DATA_REG);
}
spin_unlock_irqrestore(&i8042_lock, flags);
return retval;
}
/*
* i8042_aux_write() sends a byte out through the aux interface.
*/
static int i8042_aux_write(struct serio *port, unsigned char c)
{
int retval;
/*
* Send the byte out.
*/
retval = i8042_command(&c, I8042_CMD_AUX_SEND);
/*
* Here we restore the CTR value. I don't know why, but i8042's in half-AT
* mode tend to trash their CTR when doing the AUX_SEND command.
*/
retval += i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR);
/*
* Make sure the interrupt happens and the character is received even
* in the case the IRQ isn't wired, so that we can receive further
* characters later.
*/
i8042_interrupt(0, port, NULL);
return retval;
}
/*
* i8042_open() is called when a port is open by the higher layer.
* It allocates an interrupt and enables the port.
*/
static int i8042_open(struct serio *port)
{
struct i8042_values *values = port->driver;
/*
* Allocate the interrupt
*/
if (request_irq(values->irq, i8042_interrupt, 0, "i8042", NULL)) {
printk(KERN_ERR "i8042.c: Can't get irq %d for %s\n", values->irq, values->name);
return -1;
}
/*
* Enable the device and its interrupt.
*/
i8042_ctr |= values->irqen;
i8042_ctr &= ~values->disable;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
printk(KERN_ERR "i8042.c: Can't write CTR while opening %s.\n", values->name);
return -1;
}
/*
* Flush buffers
*/
i8042_flush();
return 0;
}
/*
* i8042_close() frees the interrupt, and disables the interface when the
* upper layer doesn't need it anymore.
*/
static void i8042_close(struct serio *port)
{
struct i8042_values *values = port->driver;
/*
* Disable the device and its interrupt.
*/
i8042_ctr &= ~values->irqen;
i8042_ctr |= values->disable;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
printk(KERN_ERR "i8042.c: Can't write CTR while closing %s.\n", values->name);
return;
}
/*
* Free the interrupt
*/
free_irq(values->irq, NULL);
}
/*
* Structures for registering the devices in the serio.c module.
*/
static struct i8042_values i8042_kbd_values = {
irq: I8042_KBD_IRQ,
irqen: I8042_CTR_KBDINT,
disable: I8042_CTR_KBDDIS,
name: "KBD",
exists: 0,
};
static struct serio i8042_kbd_port =
{
type: SERIO_8042,
write: i8042_kbd_write,
open: i8042_open,
close: i8042_close,
driver: &i8042_kbd_values,
name: "i8042 Kbd Port",
phys: "isa0060/serio0",
};
static struct i8042_values i8042_aux_values = {
irq: I8042_AUX_IRQ,
irqen: I8042_CTR_AUXINT,
disable: I8042_CTR_AUXDIS,
name: "AUX",
exists: 0,
};
static struct serio i8042_aux_port =
{
type: SERIO_8042,
write: i8042_aux_write,
open: i8042_open,
close: i8042_close,
driver: &i8042_aux_values,
name: "i8042 Aux Port",
phys: "isa0060/serio1",
};
/*
* i8042_interrupt() is the most important function in this driver -
* it handles the interrupts from the i8042, and sends incoming bytes
* to the upper layers.
*/
static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned long flags;
unsigned char str, data;
spin_lock_irqsave(&i8042_lock, flags);
while ((str = inb(I8042_STATUS_REG)) & I8042_STR_OBF) {
data = inb(I8042_DATA_REG);
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x <- i8042 (interrupt-%s) [%d]\n",
data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", (int) (jiffies - i8042_start));
#endif
if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) {
if (i8042_aux_port.dev)
i8042_aux_port.dev->interrupt(&i8042_aux_port, data, 0);
} else {
if (i8042_kbd_values.exists && i8042_kbd_port.dev) {
if (!i8042_direct) {
if (data > 0x7f) {
if (test_and_clear_bit(data & 0x7f, i8042_unxlate_seen)) {
i8042_kbd_port.dev->interrupt(&i8042_kbd_port, 0xf0, 0);
data = i8042_unxlate_table[data & 0x7f];
}
} else {
set_bit(data, i8042_unxlate_seen);
data = i8042_unxlate_table[data];
}
}
i8042_kbd_port.dev->interrupt(&i8042_kbd_port, data, 0);
}
}
}
spin_unlock_irqrestore(&i8042_lock, flags);
}
/*
* i8042_controller init initializes the i8042 controller, and,
* most importantly, sets it into non-xlated mode.
*/
static int __init i8042_controller_init(void)
{
/*
* Check the i/o region before we touch it.
*/
#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__)
if (check_region(I8042_DATA_REG, 16)) {
printk(KERN_ERR "i8042.c: %#x port already in use!\n", I8042_DATA_REG);
return -1;
}
#endif
/*
* Test the i8042. We need to know if it thinks it's working correctly
* before doing anything else.
*/
i8042_flush();
if (i8042_reset) {
unsigned char param;
if (i8042_command(¶m, I8042_CMD_CTL_TEST)) {
printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n");
return -1;
}
if (param != I8042_RET_CTL_TEST) {
printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n",
param, I8042_RET_CTL_TEST);
return -1;
}
}
/*
* Read the CTR.
*/
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) {
printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n");
return -1;
}
/*
* Save the CTR for restoral on unload / reboot.
*/
i8042_initial_ctr = i8042_ctr;
/*
* Disable both interfaces and their interrupts.
*/
i8042_ctr |= I8042_CTR_KBDDIS;
i8042_ctr &= ~I8042_CTR_KBDINT;
/*
* Handle keylock.
*/
if (~inb(I8042_STATUS_REG) & I8042_STR_KEYLOCK) {
if (i8042_unlock) {
i8042_ctr |= I8042_CTR_IGNKEYLOCK;
} else {
printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n");
}
}
/*
* Set nontranslated mode for the kbd interface. This is vital for a working
* scancode set 2/3 support. After this the kbd interface becomes a simple serial
* in/out, like the aux interface is. If the user doesn't wish this, the driver
* tries to untranslate the values after the i8042 translates them.
*/
if (i8042_direct)
i8042_ctr &= ~I8042_CTR_XLATE;
/*
* Write CTR back.
*/
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n");
return -1;
}
return 0;
}
/*
* Here we try to reset everything back to a state in which the BIOS will be
* able to talk to the hardware when rebooting.
*/
void i8042_controller_cleanup(void)
{
/*
* Reset the controller.
*/
if (i8042_reset) {
unsigned char param;
if (i8042_command(¶m, I8042_CMD_CTL_TEST))
printk(KERN_ERR "i8042.c: i8042 controller reset timeout.\n");
}
/*
* Restore the original control register setting.
*/
i8042_ctr = i8042_initial_ctr;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
printk(KERN_WARNING "i8042.c: Can't restore CTR.\n");
/*
* Reset anything that is connected to the ports if the ports
* are enabled in the original config.
*/
if (i8042_kbd_values.exists)
i8042_kbd_write(&i8042_kbd_port, 0xff);
if (i8042_aux_values.exists)
i8042_aux_write(&i8042_aux_port, 0xff);
}
/*
* i8042_check_aux() applies as much paranoia as it can at detecting
* the presence of an AUX interface.
*/
static int __init i8042_check_aux(struct i8042_values *values, struct serio *port)
{
unsigned char param;
i8042_flush();
/*
* Internal loopback test - filters out AT-type i8042's
*/
param = 0x5a;
if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != 0xa5)
return -1;
/*
* External connection test - filters out AT-soldered PS/2 i8042's
*/
if (i8042_command(¶m, I8042_CMD_AUX_TEST) || param)
return -1;
/*
* Bit assignment test - filters out PS/2 i8042's in AT mode
*/
if (i8042_command(¶m, I8042_CMD_AUX_DISABLE))
return -1;
if (i8042_command(¶m, I8042_CMD_CTL_RCTR) || (~param & I8042_CTR_AUXDIS))
return -1;
if (i8042_command(¶m, I8042_CMD_AUX_TEST) || param) {
/*
* We've got an old AMI i8042 with 'Bad Cache' commands.
*/
i8042_command(¶m, I8042_CMD_AUX_ENABLE);
return -1;
}
if (i8042_command(¶m, I8042_CMD_AUX_ENABLE))
return -1;
if (i8042_command(¶m, I8042_CMD_CTL_RCTR) || (param & I8042_CTR_AUXDIS))
return -1;
/*
* Disable the interface.
*/
i8042_ctr |= I8042_CTR_AUXDIS;
i8042_ctr &= ~I8042_CTR_AUXINT;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
return -1;
return 0;
}
/*
* i8042_port_register() marks the device as existing,
* registers it, and reports to the user.
*/
static int __init i8042_port_register(struct i8042_values *values, struct serio *port)
{
values->exists = 1;
serio_register_port(port);
printk(KERN_INFO "serio: i8042 %s port at %#x,%#x irq %d\n",
values->name, I8042_DATA_REG, I8042_COMMAND_REG, values->irq);
return 0;
}
/*
* Module init and cleanup functions.
*/
void __init i8042_setup(char *str, int *ints)
{
if (!strcmp(str, "i8042_reset=1"))
i8042_reset = 1;
if (!strcmp(str, "i8042_noaux=1"))
i8042_noaux = 1;
if (!strcmp(str, "i8042_unlock=1"))
i8042_unlock = 1;
if (!strcmp(str, "i8042_direct=1"))
i8042_direct = 1;
}
/*
* Reset the 8042 back to original mode.
*/
static int i8042_notify_sys(struct notifier_block *this, unsigned long code,
void *unused)
{
if (code==SYS_DOWN || code==SYS_HALT)
i8042_controller_cleanup();
return NOTIFY_DONE;
}
static struct notifier_block i8042_notifier=
{
i8042_notify_sys,
NULL,
0
};
int __init i8042_init(void)
{
#ifdef I8042_DEBUG_IO
i8042_start = jiffies;
#endif
if (i8042_controller_init())
return -ENODEV;
i8042_port_register(&i8042_kbd_values, &i8042_kbd_port);
if (!i8042_noaux && !i8042_check_aux(&i8042_aux_values, &i8042_aux_port))
i8042_port_register(&i8042_aux_values, &i8042_aux_port);
/*
* On ix86 platforms touching the i8042 data register region can do really
* bad things. Because of this the region is always reserved on ix86 boxes.
*/
#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__)
request_region(I8042_DATA_REG, 16, "i8042");
#endif
register_reboot_notifier(&i8042_notifier);
return 0;
}
void __exit i8042_exit(void)
{
unregister_reboot_notifier(&i8042_notifier);
if (i8042_kbd_values.exists)
serio_unregister_port(&i8042_kbd_port);
if (i8042_aux_values.exists)
serio_unregister_port(&i8042_aux_port);
i8042_controller_cleanup();
#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__)
release_region(I8042_DATA_REG, 16);
#endif
}
module_init(i8042_init);
module_exit(i8042_exit);
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:43:08
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/serio
In directory usw-pr-cvs1:/tmp/cvs-serv11486
Modified Files:
Tag: 1.12
ct82c710.c
Log Message:
Moved.
--- NEW FILE: ct82c710.c ---
/*
* $Id: ct82c710.c,v 1.12 2002/01/22 20:43:05 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* 82C710 C&T mouse port chip driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/serio.h>
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>");
MODULE_DESCRIPTION("82C710 C&T mouse port chip driver");
MODULE_LICENSE("GPL");
static char ct82c710_name[] = "C&T 82c710 mouse port";
static char ct82c710_phys[16];
/*
* ct82c710 interface
*/
#define CT82C710_DEV_IDLE 0x01 /* Device Idle */
#define CT82C710_RX_FULL 0x02 /* Device Char received */
#define CT82C710_TX_IDLE 0x04 /* Device XMIT Idle */
#define CT82C710_RESET 0x08 /* Device Reset */
#define CT82C710_INTS_ON 0x10 /* Device Interrupt On */
#define CT82C710_ERROR_FLAG 0x20 /* Device Error */
#define CT82C710_CLEAR 0x40 /* Device Clear */
#define CT82C710_ENABLE 0x80 /* Device Enable */
#define CT82C710_IRQ 12
static int ct82c710_data = 0;
static int ct82c710_status = 0;
static void ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs);
/*
* Wait for device to send output char and flush any input char.
*/
static int ct82c170_wait(void)
{
int timeout = 60000;
while ((inb(ct82c710_status) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE))
!= (CT82C710_DEV_IDLE | CT82C710_TX_IDLE) && timeout) {
if (inb_p(ct82c710_status) & CT82C710_RX_FULL) inb_p(ct82c710_data);
udelay(1);
timeout--;
}
return !timeout;
}
static void ct82c710_close(struct serio *serio)
{
if (ct82c170_wait())
printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
outb_p(inb_p(ct82c710_status) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), ct82c710_status);
if (ct82c170_wait())
printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
free_irq(CT82C710_IRQ, NULL);
}
static int ct82c710_open(struct serio *serio)
{
unsigned char status;
if (request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL))
return -1;
status = inb_p(ct82c710_status);
status |= (CT82C710_ENABLE | CT82C710_RESET);
outb_p(status, ct82c710_status);
status &= ~(CT82C710_RESET);
outb_p(status, ct82c710_status);
status |= CT82C710_INTS_ON;
outb_p(status, ct82c710_status); /* Enable interrupts */
while (ct82c170_wait()) {
printk(KERN_ERR "ct82c710: Device busy in open()\n");
status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON);
outb_p(status, ct82c710_status);
free_irq(CT82C710_IRQ, NULL);
return -1;
}
return 0;
}
/*
* Write to the 82C710 mouse device.
*/
static int ct82c710_write(struct serio *port, unsigned char c)
{
if (ct82c170_wait()) return -1;
outb_p(c, ct82c710_data);
return 0;
}
static struct serio ct82c710_port =
{
type: SERIO_8042,
name: ct82c710_name,
phys: ct82c710_phys,
write: ct82c710_write,
open: ct82c710_open,
close: ct82c710_close,
};
/*
* Interrupt handler for the 82C710 mouse port. A character
* is waiting in the 82C710.
*/
static void ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs)
{
if (ct82c710_port.dev)
ct82c710_port.dev->interrupt(&ct82c710_port, inb(ct82c710_data), 0);
}
/*
* See if we can find a 82C710 device. Read mouse address.
*/
static int __init ct82c710_probe(void)
{
outb_p(0x55, 0x2fa); /* Any value except 9, ff or 36 */
outb_p(0xaa, 0x3fa); /* Inverse of 55 */
outb_p(0x36, 0x3fa); /* Address the chip */
outb_p(0xe4, 0x3fa); /* 390/4; 390 = config address */
outb_p(0x1b, 0x2fa); /* Inverse of e4 */
outb_p(0x0f, 0x390); /* Write index */
if (inb_p(0x391) != 0xe4) /* Config address found? */
return -1; /* No: no 82C710 here */
outb_p(0x0d, 0x390); /* Write index */
ct82c710_data = inb_p(0x391) << 2; /* Get mouse I/O address */
ct82c710_status = ct82c710_data + 1;
outb_p(0x0f, 0x390);
outb_p(0x0f, 0x391); /* Close config mode */
return 0;
}
int __init ct82c710_init(void)
{
if (ct82c710_probe())
return -ENODEV;
if (request_region(ct82c710_data, 2, "ct82c710"))
return -EBUSY;
sprintf(ct82c710_phys, "isa%04x/serio0", ct82c710_data);
serio_register_port(&ct82c710_port);
printk(KERN_INFO "serio: C&T 82c710 mouse port at %#x irq %d\n",
ct82c710_data, CT82C710_IRQ);
return 0;
}
void __exit ct82c710_exit(void)
{
serio_unregister_port(&ct82c710_port);
release_region(ct82c710_data, 2);
}
module_init(ct82c710_init);
module_exit(ct82c710_exit);
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:41:48
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/gameport
In directory usw-pr-cvs1:/tmp/cvs-serv10934
Modified Files:
Tag: 1.3
vortex.c
Log Message:
Moved.
--- NEW FILE: vortex.c ---
/*
* $Id: vortex.c,v 1.3 2002/01/22 20:41:45 vojtech Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik
*
* Based on the work of:
* Raymond Ingles
*/
/*
* Trident 4DWave and Aureal Vortex gameport driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/gameport.h>
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>");
MODULE_DESCRIPTION("Aureal Vortex and Vortex2 gameport driver");
MODULE_LICENSE("GPL");
#define VORTEX_DATA_WAIT 20 /* 20 ms */
struct vortex {
struct gameport gameport;
struct pci_dev *dev;
unsigned char *base;
unsigned char *io;
char phys[32];
};
static unsigned char vortex_read(struct gameport *gameport)
{
struct vortex *vortex = gameport->driver;
return readb(vortex->io + VORTEX_LEG);
}
static void vortex_trigger(struct gameport *gameport)
{
struct vortex *vortex = gameport->driver;
writeb(0xff, vortex->io + VORTEX_LEG);
}
static int vortex_cooked_read(struct gameport *gameport, int *axes, int *buttons)
{
struct vortex *vortex = gameport->driver;
int i;
*buttons = (~readb(vortex->base + VORTEX_LEG) >> 4) & 0xf;
for (i = 0; i < 4; i++) {
axes[i] = readw(vortex->io + VORTEX_AXD + i * sizeof(u32));
if (axes[i] == 0x1fff) axes[i] = -1;
}
return 0;
}
static int vortex_open(struct gameport *gameport, int mode)
{
struct vortex *vortex = gameport->driver;
switch (mode) {
case GAMEPORT_MODE_COOKED:
writeb(0x40, vortex->io + VORTEX_GCR);
wait_ms(PCIGAME_DATA_WAIT);
return 0;
case GAMEPORT_MODE_RAW:
writeb(0x00, vortex->io + VORTEX_GCR);
return 0;
default:
return -1;
}
return 0;
}
static int __devinit vortex_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct vortex *vortex;
int i;
if (!(vortex = kmalloc(sizeof(struct vortex), GFP_KERNEL)))
return -1;
memset(vortex, 0, sizeof(struct vortex));
vortex->dev = dev;
sprintf(vortex->phys, "pci%s/gameport0", dev->slot_name);
dev->driver_data = vortex;
vortex->gameport.driver = vortex;
vortex->gameport.fuzz = 64;
vortex->gameport.read = vortex_read;
vortex->gameport.trigger = vortex_trigger;
vortex->gameport.cooked_read = vortex_cooked_read;
vortex->gameport.open = vortex_open;
vortex->gameport.name = dev->name;
vortex->gameport.phys = vortex->phys;
vortex->gameport.idbus = BUS_PCI;
vortex->gameport.idvendor = dev->vendor;
vortex->gameport.idproduct = dev->device;
for (i = 0; i < 6; i++)
if (~pci_resource_flags(dev, i) & IORESOURCE_IO)
break;
pci_enable_device(dev);
vortex->base = ioremap(pci_resource_start(vortex->dev, i),
pci_resource_len(vortex->dev, i));
vortex->io = vortex->base + id->driver_data;
gameport_register_port(&vortex->gameport);
printk(KERN_INFO "gameport: %s at pci%s speed %d kHz\n",
dev->name, dev->slot_name, vortex->gameport.speed);
return 0;
}
static void __devexit vortex_remove(struct pci_dev *dev)
{
struct vortex *vortex = dev->driver_data;
gameport_unregister_port(&vortex->gameport);
iounmap(vortex->base);
kfree(vortex);
}
static struct pci_device_id vortex_id_table[] __devinitdata =
{{ 0x12eb, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x1100c },
{ 0x12eb, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x2880c },
{ 0 }};
static struct pci_driver vortex_driver = {
name: "vortex",
id_table: vortex_id_table,
probe: vortex_probe,
remove: vortex_remove,
};
int __init vortex_init(void)
{
return pci_module_init(&vortex_driver);
}
void __exit vortex_exit(void)
{
pci_unregister_driver(&vortex_driver);
}
module_init(vortex_init);
module_exit(vortex_exit);
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:41:34
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/gameport
In directory usw-pr-cvs1:/tmp/cvs-serv10822
Modified Files:
Tag: 1.20
lightning.c
Log Message:
Moved.
--- NEW FILE: lightning.c ---
/*
* $Id: lightning.c,v 1.20 2002/01/22 20:41:31 vojtech Exp $
*
* Copyright (c) 1998-2001 Vojtech Pavlik
*/
/*
* PDPI Lightning 4 gamecard driver for Linux.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gameport.h>
#include <linux/slab.h>
#define L4_PORT 0x201
#define L4_SELECT_ANALOG 0xa4
#define L4_SELECT_DIGITAL 0xa5
#define L4_SELECT_SECONDARY 0xa6
#define L4_CMD_ID 0x80
#define L4_CMD_GETCAL 0x92
#define L4_CMD_SETCAL 0x93
#define L4_ID 0x04
#define L4_BUSY 0x01
#define L4_TIMEOUT 80 /* 80 us */
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>");
MODULE_DESCRIPTION("PDPI Lightning 4 gamecard driver");
MODULE_LICENSE("GPL");
struct l4 {
struct gameport gameport;
unsigned char port;
char phys[32];
} *l4_port[8];
char l4_name[] = "PDPI Lightning 4";
/*
* l4_wait_ready() waits for the L4 to become ready.
*/
static int l4_wait_ready(void)
{
unsigned int t;
t = L4_TIMEOUT;
while ((inb(L4_PORT) & L4_BUSY) && t > 0) t--;
return -(t<=0);
}
/*
* l4_cooked_read() reads data from the Lightning 4.
*/
static int l4_cooked_read(struct gameport *gameport, int *axes, int *buttons)
{
struct l4 *l4 = gameport->driver;
unsigned char status;
int i, result = -1;
outb(L4_SELECT_ANALOG, L4_PORT);
outb(L4_SELECT_DIGITAL + (l4->port >> 2), L4_PORT);
if (inb(L4_PORT) & L4_BUSY) goto fail;
outb(l4->port & 3, L4_PORT);
if (l4_wait_ready()) goto fail;
status = inb(L4_PORT);
for (i = 0; i < 4; i++)
if (status & (1 << i)) {
if (l4_wait_ready()) goto fail;
axes[i] = inb(L4_PORT);
if (axes[i] > 252) axes[i] = -1;
}
if (status & 0x10) {
if (l4_wait_ready()) goto fail;
*buttons = inb(L4_PORT) & 0x0f;
}
result = 0;
fail: outb(L4_SELECT_ANALOG, L4_PORT);
return result;
}
static int l4_open(struct gameport *gameport, int mode)
{
struct l4 *l4 = gameport->driver;
if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED)
return -1;
outb(L4_SELECT_ANALOG, L4_PORT);
return 0;
}
/*
* l4_getcal() reads the L4 with calibration values.
*/
static int l4_getcal(int port, int *cal)
{
int i, result = -1;
outb(L4_SELECT_ANALOG, L4_PORT);
outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
if (inb(L4_PORT) & L4_BUSY) goto fail;
outb(L4_CMD_GETCAL, L4_PORT);
if (l4_wait_ready()) goto fail;
if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2)) goto fail;
if (l4_wait_ready()) goto fail;
outb(port & 3, L4_PORT);
for (i = 0; i < 4; i++) {
if (l4_wait_ready()) goto fail;
cal[i] = inb(L4_PORT);
}
result = 0;
fail: outb(L4_SELECT_ANALOG, L4_PORT);
return result;
}
/*
* l4_setcal() programs the L4 with calibration values.
*/
static int l4_setcal(int port, int *cal)
{
int i, result = -1;
outb(L4_SELECT_ANALOG, L4_PORT);
outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
if (inb(L4_PORT) & L4_BUSY) goto fail;
outb(L4_CMD_SETCAL, L4_PORT);
if (l4_wait_ready()) goto fail;
if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2)) goto fail;
if (l4_wait_ready()) goto fail;
outb(port & 3, L4_PORT);
for (i = 0; i < 4; i++) {
if (l4_wait_ready()) goto fail;
outb(cal[i], L4_PORT);
}
result = 0;
fail: outb(L4_SELECT_ANALOG, L4_PORT);
return result;
}
/*
* l4_calibrate() calibrates the L4 for the attached device, so
* that the device's resistance fits into the L4's 8-bit range.
*/
static int l4_calibrate(struct gameport *gameport, int *axes, int *max)
{
int i, t;
int cal[4];
struct l4 *l4 = gameport->driver;
if (l4_getcal(l4->port, cal))
return -1;
for (i = 0; i < 4; i++) {
t = (max[i] * cal[i]) / 200;
t = (t < 1) ? 1 : ((t > 255) ? 255 : t);
axes[i] = (axes[i] < 0) ? -1 : (axes[i] * cal[i]) / t;
axes[i] = (axes[i] > 252) ? 252 : axes[i];
cal[i] = t;
}
if (l4_setcal(l4->port, cal))
return -1;
return 0;
}
int __init l4_init(void)
{
int cal[4] = {255,255,255,255};
int i, j, rev, cards = 0;
struct gameport *gameport;
struct l4 *l4;
if (!request_region(L4_PORT, 1, "lightning"))
return -1;
for (i = 0; i < 2; i++) {
outb(L4_SELECT_ANALOG, L4_PORT);
outb(L4_SELECT_DIGITAL + i, L4_PORT);
if (inb(L4_PORT) & L4_BUSY) continue;
outb(L4_CMD_ID, L4_PORT);
if (l4_wait_ready()) continue;
if (inb(L4_PORT) != L4_SELECT_DIGITAL + i) continue;
if (l4_wait_ready()) continue;
if (inb(L4_PORT) != L4_ID) continue;
if (l4_wait_ready()) continue;
rev = inb(L4_PORT);
if (!rev) continue;
if (!(l4_port[i * 4] = kmalloc(sizeof(struct l4) * 4, GFP_KERNEL))) {
printk(KERN_ERR "lightning: Out of memory allocating ports.\n");
continue;
}
memset(l4_port[i * 4], 0, sizeof(struct l4) * 4);
for (j = 0; j < 4; j++) {
l4 = l4_port[i * 4 + j] = l4_port[i * 4] + j;
l4->port = i * 4 + j;
sprintf(l4->phys, "isa%04x/gameport%d", L4_PORT, 4 * i + j);
gameport = &l4->gameport;
gameport->driver = l4;
gameport->open = l4_open;
gameport->cooked_read = l4_cooked_read;
gameport->calibrate = l4_calibrate;
gameport->name = l4_name;
gameport->phys = l4->phys;
gameport->idbus = BUS_ISA;
if (!i && !j)
gameport->io = L4_PORT;
if (rev > 0x28) /* on 2.9+ the setcal command works correctly */
l4_setcal(l4->port, cal);
gameport_register_port(gameport);
}
printk(KERN_INFO "gameport: PDPI Lightning 4 %s card v%d.%d at %#x\n",
i ? "secondary" : "primary", rev >> 4, rev, L4_PORT);
cards++;
}
outb(L4_SELECT_ANALOG, L4_PORT);
if (!cards) {
release_region(L4_PORT, 1);
return -1;
}
return 0;
}
void __init l4_exit(void)
{
int i;
int cal[4] = {59, 59, 59, 59};
for (i = 0; i < 8; i++)
if (l4_port[i]) {
l4_setcal(l4_port[i]->port, cal);
gameport_unregister_port(&l4_port[i]->gameport);
}
outb(L4_SELECT_ANALOG, L4_PORT);
release_region(L4_PORT, 1);
}
module_init(l4_init);
module_exit(l4_exit);
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:41:16
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/gameport
In directory usw-pr-cvs1:/tmp/cvs-serv10695
Modified Files:
Tag: 1.18
gameport.c
Log Message:
Moved.
--- NEW FILE: gameport.c ---
/*
* $Id: gameport.c,v 1.18 2002/01/22 20:41:14 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* Generic gameport layer
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <asm/io.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/gameport.h>
#include <linux/slab.h>
#include <linux/isapnp.h>
#include <linux/stddef.h>
#include <linux/delay.h>
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>");
MODULE_DESCRIPTION("Generic gameport layer");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(gameport_register_port);
EXPORT_SYMBOL(gameport_unregister_port);
EXPORT_SYMBOL(gameport_register_device);
EXPORT_SYMBOL(gameport_unregister_device);
EXPORT_SYMBOL(gameport_open);
EXPORT_SYMBOL(gameport_close);
EXPORT_SYMBOL(gameport_rescan);
EXPORT_SYMBOL(gameport_cooked_read);
static struct gameport *gameport_list;
static struct gameport_dev *gameport_dev;
/*
* gameport_measure_speed() measures the gameport i/o speed.
*/
static int gameport_measure_speed(struct gameport *gameport)
{
#if defined(__i386__) || defined(__x86_64__)
#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0)
#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180L/HZ:0))
unsigned int i, t, t1, t2, t3, tx;
unsigned long flags;
if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
return 0;
tx = 1 << 30;
for(i = 0; i < 50; i++) {
save_flags(flags); /* Yes, all CPUs */
cli();
GET_TIME(t1);
for(t = 0; t < 50; t++) gameport_read(gameport);
GET_TIME(t2);
GET_TIME(t3);
restore_flags(flags);
udelay(i * 10);
if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t;
}
return 59659 / (tx < 1 ? 1 : tx);
#else
unsigned int j, t = 0;
j = jiffies; while (j == jiffies);
j = jiffies; while (j == jiffies) { t++; gameport_read(gameport); }
return t * HZ / 1000;
#endif
gameport_close(gameport);
}
static void gameport_find_dev(struct gameport *gameport)
{
struct gameport_dev *dev = gameport_dev;
while (dev && !gameport->dev) {
if (dev->connect)
dev->connect(gameport, dev);
dev = dev->next;
}
}
void gameport_rescan(struct gameport *gameport)
{
gameport_close(gameport);
gameport_find_dev(gameport);
}
void gameport_register_port(struct gameport *gameport)
{
gameport->next = gameport_list;
gameport_list = gameport;
gameport->speed = gameport_measure_speed(gameport);
gameport_find_dev(gameport);
}
void gameport_unregister_port(struct gameport *gameport)
{
struct gameport **gameportptr = &gameport_list;
while (*gameportptr && (*gameportptr != gameport)) gameportptr = &((*gameportptr)->next);
*gameportptr = (*gameportptr)->next;
if (gameport->dev && gameport->dev->disconnect)
gameport->dev->disconnect(gameport);
}
void gameport_register_device(struct gameport_dev *dev)
{
struct gameport *gameport = gameport_list;
dev->next = gameport_dev;
gameport_dev = dev;
while (gameport) {
if (!gameport->dev && dev->connect)
dev->connect(gameport, dev);
gameport = gameport->next;
}
}
void gameport_unregister_device(struct gameport_dev *dev)
{
struct gameport_dev **devptr = &gameport_dev;
struct gameport *gameport = gameport_list;
while (*devptr && (*devptr != dev)) devptr = &((*devptr)->next);
*devptr = (*devptr)->next;
while (gameport) {
if (gameport->dev == dev && dev->disconnect)
dev->disconnect(gameport);
gameport_find_dev(gameport);
gameport = gameport->next;
}
}
int gameport_open(struct gameport *gameport, struct gameport_dev *dev, int mode)
{
if (gameport->open) {
if (gameport->open(gameport, mode))
return -1;
} else {
if (mode != GAMEPORT_MODE_RAW)
return -1;
}
if (gameport->dev)
return -1;
gameport->dev = dev;
return 0;
}
void gameport_close(struct gameport *gameport)
{
gameport->dev = NULL;
if (gameport->close) gameport->close(gameport);
}
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:41:03
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/gameport
In directory usw-pr-cvs1:/tmp/cvs-serv10535
Modified Files:
Tag: 1
fm801-gp.c
Log Message:
Moved.
Index: fm801-gp.c
===================================================================
RCS file: /cvsroot/linuxconsole/ruby/linux/drivers/input/gameport/fm801-gp.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- fm801-gp.c 2002/01/22 19:38:27 1.1
+++ fm801-gp.c 2002/01/22 20:41:00 1.2
@@ -39,6 +39,8 @@
struct fm801_gp {
struct gameport gameport;
struct resource *res_port;
+ char phys[32];
+ char name[32];
};
#ifdef HAVE_COOKED
@@ -96,19 +98,26 @@
pci_enable_device(pci);
gp->gameport.io = pci_resource_start(pci, 0);
if ((gp->res_port = request_region(gp->gameport.io, 0x10, "FM801 GP")) == NULL) {
+ kfree(gp);
printk("unable to grab region 0x%x-0x%x\n", gp->gameport.io, gp->gameport.io + 0x0f);
return -1;
}
+ gp->gameport.phys = gp->phys;
+ gp->gameport.name = gp->name;
+ gp->gameport.idbus = BUS_PCI;
+ gp->gameport.idvendor = pci->vendor;
+ gp->gameport.idproduct = pci->device;
+
pci_set_drvdata(pci, gp);
- gameport_register_port(&gp->gameport);
-
+
outb(0x60, gp->gameport.io + 0x0d); /* enable joystick 1 and 2 */
- printk(KERN_INFO "gameport%d: %s at pci%02x:%02x.%x speed %d kHz\n",
- gp->gameport.number, pci->name, pci->bus->number,
- PCI_SLOT(pci->devfn), PCI_FUNC(pci->devfn), gp->gameport.speed);
-
+ gameport_register_port(&gp->gameport);
+
+ printk(KERN_INFO "gameport: %s at pci%s speed %d kHz\n",
+ pci->name, pci->slot_name, gp->gameport.speed);
+
return 0;
}
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:40:49
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/gameport
In directory usw-pr-cvs1:/tmp/cvs-serv10407
Modified Files:
Tag: 1.8
emu10k1-gp.c
Log Message:
Moved.
--- NEW FILE: emu10k1-gp.c ---
/*
* $Id: emu10k1-gp.c,v 1.8 2002/01/22 20:40:46 vojtech Exp $
*
* Copyright (c) 2001 Vojtech Pavlik
*/
/*
* EMU10k1 - SB Live / Audigy - gameport driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <asm/io.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/gameport.h>
#include <linux/slab.h>
#include <linux/pci.h>
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>");
MODULE_DESCRIPTION("EMU10k1 gameport driver");
MODULE_LICENSE("GPL");
struct emu {
struct pci_dev *dev;
struct emu *next;
struct gameport gameport;
int size;
char phys[32];
};
static struct pci_device_id emu_tbl[] __devinitdata = {
{ 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID }, /* SB Live gameport */
{ 0x1102, 0x7003, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy gameport */
{ 0, }
};
MODULE_DEVICE_TABLE(pci, emu_tbl);
static int __devinit emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int ioemu, iolen;
struct emu *emu;
if (pci_enable_device(pdev))
return -EBUSY;
ioemu = pci_resource_start(pdev, 0);
iolen = pci_resource_len(pdev, 0);
if (!request_region(ioemu, iolen, "emu10k1-gp"))
return -EBUSY;
if (!(emu = kmalloc(sizeof(struct emu), GFP_KERNEL))) {
printk(KERN_ERR "emu10k1-gp: Memory allocation failed.\n");
release_region(ioemu, iolen);
return -ENOMEM;
}
memset(emu, 0, sizeof(struct emu));
sprintf(emu->phys, "pci%s/gameport0", pdev->slot_name);
emu->size = iolen;
emu->dev = pdev;
emu->gameport.io = ioemu;
emu->gameport.name = pdev->name;
emu->gameport.phys = emu->phys;
emu->gameport.idbus = BUS_PCI;
emu->gameport.idvendor = pdev->vendor;
emu->gameport.idproduct = pdev->device;
pdev->driver_data = emu;
gameport_register_port(&emu->gameport);
printk(KERN_INFO "gameport: %s at pci%s speed %d kHz\n",
pdev->name, pdev->slot_name, emu->gameport.speed);
return 0;
}
static void __devexit emu_remove(struct pci_dev *pdev)
{
struct emu *emu = (void *)pdev->driver_data;
gameport_unregister_port(&emu->gameport);
release_region(emu->gameport.io, emu->size);
kfree(emu);
}
static struct pci_driver emu_driver = {
name: "Emu10k1 Gameport",
id_table: emu_tbl,
probe: emu_probe,
remove: emu_remove,
};
int __init emu_init(void)
{
return pci_module_init(&emu_driver);
}
void __exit emu_exit(void)
{
pci_unregister_driver(&emu_driver);
}
module_init(emu_init);
module_exit(emu_exit);
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:38:00
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/keyboard
In directory usw-pr-cvs1:/tmp/cvs-serv9146
Added Files:
Tag: 1.2
maple_keyb.c
Log Message:
Moved.
--- NEW FILE: maple_keyb.c ---
/*
* $Id: maple_keyb.c,v 1.2 2002/01/22 20:37:57 vojtech Exp $
* SEGA Dreamcast keyboard driver
* Based on drivers/usb/usbkbd.c
*/
#include <linux/kernel.h>
#include <linux/malloc.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/maple.h>
MODULE_AUTHOR("YAEGASHI Takeshi <t...@ke...>");
MODULE_DESCRIPTION("SEGA Dreamcast keyboard driver");
static unsigned char dc_kbd_keycode[256] = {
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
27, 43, 84, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
72, 73, 82, 83, 86,127,116,117, 85, 89, 90, 91, 92, 93, 94, 95,
120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113,
115,114, 0, 0, 0,124, 0,181,182,183,184,185,186,187,188,189,
190,191,192,193,194,195,196,197,198, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
150,158,159,128,136,177,178,176,142,152,173,140
};
struct dc_kbd {
struct input_dev dev;
unsigned char new[8];
unsigned char old[8];
int open;
};
static void dc_scan_kbd(struct dc_kbd *kbd)
{
int i;
struct input_dev *dev = &kbd->dev;
for(i=0; i<8; i++)
input_report_key(dev,
dc_kbd_keycode[i+224],
(kbd->new[0]>>i)&1);
for(i=2; i<8; i++) {
if(kbd->old[i]>3&&memscan(kbd->new+2, kbd->old[i], 6)==NULL) {
if(dc_kbd_keycode[kbd->old[i]])
input_report_key(dev,
dc_kbd_keycode[kbd->old[i]],
0);
else
printk("Unknown key (scancode %#x) released.",
kbd->old[i]);
}
if(kbd->new[i]>3&&memscan(kbd->old+2, kbd->new[i], 6)!=NULL) {
if(dc_kbd_keycode[kbd->new[i]])
input_report_key(dev,
dc_kbd_keycode[kbd->new[i]],
1);
else
printk("Unknown key (scancode %#x) pressed.",
kbd->new[i]);
}
}
memcpy(kbd->old, kbd->new, 8);
}
static void dc_kbd_callback(struct mapleq *mq)
{
struct maple_device *mapledev = mq->dev;
struct dc_kbd *kbd = mapledev->private_data;
unsigned long *buf = mq->recvbuf;
if (buf[1] == mapledev->function) {
memcpy(kbd->new, buf+2, 8);
dc_scan_kbd(kbd);
}
}
static int dc_kbd_open(struct input_dev *dev)
{
struct dc_kbd *kbd = dev->private;
kbd->open++;
return 0;
}
static void dc_kbd_close(struct input_dev *dev)
{
struct dc_kbd *kbd = dev->private;
kbd->open--;
}
static int dc_kbd_connect(struct maple_device *dev)
{
int i;
unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]);
struct dc_kbd *kbd;
if (!(kbd = kmalloc(sizeof(struct dc_kbd), GFP_KERNEL)))
return -1;
memset(kbd, 0, sizeof(struct dc_kbd));
dev->private_data = kbd;
kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
for (i=0; i<255; i++)
set_bit(dc_kbd_keycode[i], kbd->dev.keybit);
clear_bit(0, kbd->dev.keybit);
kbd->dev.private = kbd;
kbd->dev.open = dc_kbd_open;
kbd->dev.close = dc_kbd_close;
kbd->dev.event = NULL;
kbd->dev.name = dev->product_name;
kbd->dev.idbus = BUS_MAPLE;
input_register_device(&kbd->dev);
maple_getcond_callback(dev, dc_kbd_callback, 1, MAPLE_FUNC_KEYBOARD);
printk(KERN_INFO "input%d: keyboard(0x%lx): %s\n",
kbd->dev.number, data, kbd->dev.name);
MOD_INC_USE_COUNT;
return 0;
}
static void dc_kbd_disconnect(struct maple_device *dev)
{
struct dc_kbd *kbd = dev->private_data;
input_unregister_device(&kbd->dev);
kfree(kbd);
MOD_DEC_USE_COUNT;
}
static struct maple_driver dc_kbd_driver = {
function: MAPLE_FUNC_KEYBOARD,
name: "Dreamcast keyboard",
connect: dc_kbd_connect,
disconnect: dc_kbd_disconnect,
};
static int __init dc_kbd_init(void)
{
maple_register_driver(&dc_kbd_driver);
return 0;
}
static void __exit dc_kbd_exit(void)
{
maple_unregister_driver(&dc_kbd_driver);
}
module_init(dc_kbd_init);
module_exit(dc_kbd_exit);
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:37:45
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/keyboard
In directory usw-pr-cvs1:/tmp/cvs-serv9032
Added Files:
Tag: 1.12
xtkbd.c
Log Message:
Moved.
--- NEW FILE: xtkbd.c ---
/*
* $Id: xtkbd.c,v 1.12 2002/01/22 20:37:42 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* XT keyboard driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>");
MODULE_DESCRIPTION("XT keyboard driver");
MODULE_LICENSE("GPL");
#define XTKBD_EMUL0 0xe0
#define XTKBD_EMUL1 0xe1
#define XTKBD_KEY 0x7f
#define XTKBD_RELEASE 0x80
static unsigned char xtkbd_keycode[256] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 0, 0, 0, 87, 88, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 87, 88, 0, 0, 0, 0,110,111,103,108,105,
106
};
static char *xtkbd_name = "XT Keyboard";
struct xtkbd {
unsigned char keycode[256];
struct input_dev dev;
struct serio *serio;
char phys[32];
};
void xtkbd_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
{
struct xtkbd *xtkbd = serio->private;
switch (data) {
case XTKBD_EMUL0:
case XTKBD_EMUL1:
return;
default:
if (xtkbd->keycode[data & XTKBD_KEY]) {
input_report_key(&xtkbd->dev, xtkbd->keycode[data & XTKBD_KEY], !(data & XTKBD_RELEASE));
} else {
printk(KERN_WARNING "xtkbd.c: Unknown key (scancode %#x) %s.\n",
data & XTKBD_KEY, data & XTKBD_RELEASE ? "released" : "pressed");
}
}
}
void xtkbd_connect(struct serio *serio, struct serio_dev *dev)
{
struct xtkbd *xtkbd;
int i;
if ((serio->type & SERIO_TYPE) != SERIO_XT)
return;
if (!(xtkbd = kmalloc(sizeof(struct xtkbd), GFP_KERNEL)))
return;
memset(xtkbd, 0, sizeof(struct xtkbd));
xtkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
xtkbd->serio = serio;
xtkbd->dev.keycode = xtkbd->keycode;
xtkbd->dev.private = xtkbd;
serio->private = xtkbd;
if (serio_open(serio, dev)) {
kfree(xtkbd);
return;
}
memcpy(xtkbd->keycode, xtkbd_keycode, sizeof(xtkbd->keycode));
for (i = 0; i < 255; i++)
set_bit(xtkbd->keycode[i], xtkbd->dev.keybit);
clear_bit(0, xtkbd->dev.keybit);
sprintf(xtkbd->phys, "%s/input0", serio->phys);
xtkbd->dev.name = xtkbd_name;
xtkbd->dev.phys = xtkbd->phys;
xtkbd->dev.idbus = BUS_XTKBD;
xtkbd->dev.idvendor = 0x0001;
xtkbd->dev.idproduct = 0x0001;
xtkbd->dev.idversion = 0x0100;
input_register_device(&xtkbd->dev);
printk(KERN_INFO "input: %s on %s\n", xtkbd_name, serio->phys);
}
void xtkbd_disconnect(struct serio *serio)
{
struct xtkbd *xtkbd = serio->private;
input_unregister_device(&xtkbd->dev);
serio_close(serio);
kfree(xtkbd);
}
struct serio_dev xtkbd_dev = {
interrupt: xtkbd_interrupt,
connect: xtkbd_connect,
disconnect: xtkbd_disconnect
};
int __init xtkbd_init(void)
{
serio_register_device(&xtkbd_dev);
return 0;
}
void __exit xtkbd_exit(void)
{
serio_unregister_device(&xtkbd_dev);
}
module_init(xtkbd_init);
module_exit(xtkbd_exit);
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:37:30
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/keyboard
In directory usw-pr-cvs1:/tmp/cvs-serv8893
Added Files:
Tag: 1.15
sunkbd.c
Log Message:
Moved.
--- NEW FILE: sunkbd.c ---
/*
* $Id: sunkbd.c,v 1.15 2002/01/22 20:37:27 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* Sun keyboard driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/serio.h>
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>");
MODULE_DESCRIPTION("Sun keyboard driver");
MODULE_LICENSE("GPL");
static unsigned char sunkbd_keycode[128] = {
0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64, 0,
65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106, 1, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 41, 14,110,113, 98, 55,
116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27,111,127, 71, 72, 73, 74,134,135,107, 0, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136,
104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101,
79, 80, 81, 0, 0, 0,138, 58,125, 57,126,109, 86, 78
};
#define SUNKBD_CMD_RESET 0x1
#define SUNKBD_CMD_BELLON 0x2
#define SUNKBD_CMD_BELLOFF 0x3
#define SUNKBD_CMD_CLICK 0xa
#define SUNKBD_CMD_NOCLICK 0xb
#define SUNKBD_CMD_SETLED 0xe
#define SUNKBD_CMD_LAYOUT 0xf
#define SUNKBD_RET_RESET 0xff
#define SUNKBD_RET_ALLUP 0x7f
#define SUNKBD_RET_LAYOUT 0xfe
#define SUNKBD_LAYOUT_5_MASK 0x20
#define SUNKBD_RELEASE 0x80
#define SUNKBD_KEY 0x7f
/*
* Per-keyboard data.
*/
struct sunkbd {
unsigned char keycode[128];
struct input_dev dev;
struct serio *serio;
struct tq_struct tq;
char name[64];
char phys[32];
char type;
char reset;
char layout;
};
/*
* sunkbd_interrupt() is called by the low level driver when a character
* is received.
*/
static void sunkbd_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
{
struct sunkbd* sunkbd = serio->private;
if (sunkbd->reset <= -1) { /* If cp[i] is 0xff, sunkbd->reset will stay -1. */
sunkbd->reset = data; /* The keyboard sends 0xff 0xff 0xID on powerup */
return;
}
if (sunkbd->layout == -1) {
sunkbd->layout = data;
return;
}
switch (data) {
case SUNKBD_RET_RESET:
queue_task(&sunkbd->tq, &tq_immediate);
mark_bh(IMMEDIATE_BH);
sunkbd->reset = -1;
return;
case SUNKBD_RET_LAYOUT:
sunkbd->layout = -1;
return;
case SUNKBD_RET_ALLUP: /* All keys released */
return;
default:
if (sunkbd->keycode[data & SUNKBD_KEY]) {
input_report_key(&sunkbd->dev, sunkbd->keycode[data & SUNKBD_KEY], !(data & SUNKBD_RELEASE));
} else {
printk(KERN_WARNING "sunkbd.c: Unknown key (scancode %#x) %s.\n",
data & SUNKBD_KEY, data & SUNKBD_RELEASE ? "released" : "pressed");
}
}
}
/*
* sunkbd_event() handles events from the input module.
*/
static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct sunkbd *sunkbd = dev->private;
switch (type) {
case EV_LED:
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
sunkbd->serio->write(sunkbd->serio,
(!!test_bit(LED_CAPSL, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) |
(!!test_bit(LED_COMPOSE, dev->led) << 1) | !!test_bit(LED_NUML, dev->led));
return 0;
case EV_SND:
switch (code) {
case SND_CLICK:
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
return 0;
case SND_BELL:
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
return 0;
}
break;
}
return -1;
}
/*
* sunkbd_initialize() checks for a Sun keyboard attached, and determines
* its type.
*/
static int sunkbd_initialize(struct sunkbd *sunkbd)
{
int t;
t = 1000;
sunkbd->reset = -2;
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_RESET);
while (sunkbd->reset < 0 && --t) mdelay(1);
if (!t) return -1;
sunkbd->type = sunkbd->reset;
if (sunkbd->type == 4) { /* Type 4 keyboard */
t = 250;
sunkbd->layout = -2;
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
while (sunkbd->layout < 0 && --t) mdelay(1);
if (!t) return -1;
if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK) sunkbd->type = 5;
}
return 0;
}
/*
* sunkbd_reinit() sets leds and beeps to a state the computer remembers they
* were in.
*/
static void sunkbd_reinit(void *data)
{
struct sunkbd *sunkbd = data;
int t = 1000;
while (sunkbd->reset < 0 && --t) mdelay(1);
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
sunkbd->serio->write(sunkbd->serio,
(!!test_bit(LED_CAPSL, sunkbd->dev.led) << 3) | (!!test_bit(LED_SCROLLL, sunkbd->dev.led) << 2) |
(!!test_bit(LED_COMPOSE, sunkbd->dev.led) << 1) | !!test_bit(LED_NUML, sunkbd->dev.led));
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev.snd));
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev.snd));
}
/*
* sunkbd_connect() probes for a Sun keyboard and fills the necessary structures.
*/
static void sunkbd_connect(struct serio *serio, struct serio_dev *dev)
{
struct sunkbd *sunkbd;
int i;
if ((serio->type & SERIO_TYPE) != SERIO_RS232)
return;
if ((serio->type & SERIO_PROTO) && (serio->type & SERIO_PROTO) != SERIO_SUNKBD)
return;
if (!(sunkbd = kmalloc(sizeof(struct sunkbd), GFP_KERNEL)))
return;
memset(sunkbd, 0, sizeof(struct sunkbd));
sunkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_SND) | BIT(EV_REP);
sunkbd->dev.ledbit[0] = BIT(LED_CAPSL) | BIT(LED_COMPOSE) | BIT(LED_SCROLLL) | BIT(LED_NUML);
sunkbd->dev.sndbit[0] = BIT(SND_CLICK) | BIT(SND_BELL);
sunkbd->serio = serio;
sunkbd->tq.routine = sunkbd_reinit;
sunkbd->tq.data = sunkbd;
sunkbd->dev.keycode = sunkbd->keycode;
sunkbd->dev.event = sunkbd_event;
sunkbd->dev.private = sunkbd;
serio->private = sunkbd;
if (serio_open(serio, dev)) {
kfree(sunkbd);
return;
}
if (sunkbd_initialize(sunkbd) < 0) {
serio_close(serio);
kfree(sunkbd);
return;
}
sprintf(sunkbd->name, "Sun Type %d keyboard", sunkbd->type);
memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
for (i = 0; i < 255; i++)
set_bit(sunkbd->keycode[i], sunkbd->dev.keybit);
clear_bit(0, sunkbd->dev.keybit);
sprintf(sunkbd->name, "%s/input", serio->phys);
sunkbd->dev.name = sunkbd->name;
sunkbd->dev.phys = sunkbd->phys;
sunkbd->dev.idbus = BUS_RS232;
sunkbd->dev.idvendor = SERIO_SUNKBD;
sunkbd->dev.idproduct = sunkbd->type;
sunkbd->dev.idversion = 0x0100;
input_register_device(&sunkbd->dev);
printk(KERN_INFO "input: %s on %s\n", sunkbd->name, serio->phys);
}
/*
* sunkbd_disconnect() unregisters and closes behind us.
*/
static void sunkbd_disconnect(struct serio *serio)
{
struct sunkbd *sunkbd = serio->private;
input_unregister_device(&sunkbd->dev);
serio_close(serio);
kfree(sunkbd);
}
static struct serio_dev sunkbd_dev = {
interrupt: sunkbd_interrupt,
connect: sunkbd_connect,
disconnect: sunkbd_disconnect
};
/*
* The functions for insering/removing us as a module.
*/
int __init sunkbd_init(void)
{
serio_register_device(&sunkbd_dev);
return 0;
}
void __exit sunkbd_exit(void)
{
serio_unregister_device(&sunkbd_dev);
}
module_init(sunkbd_init);
module_exit(sunkbd_exit);
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:37:18
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/keyboard
In directory usw-pr-cvs1:/tmp/cvs-serv8793
Added Files:
Tag: 1.30
atkbd.c
Log Message:
Moved.
--- NEW FILE: atkbd.c ---
/*
* $Id: atkbd.c,v 1.30 2002/01/22 20:37:14 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/serio.h>
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>");
MODULE_DESCRIPTION("AT and PS/2 keyboard driver");
MODULE_PARM(atkbd_set, "1i");
MODULE_LICENSE("GPL");
static int atkbd_set = 2;
/*
* Scancode to keycode tables. These are just the default setting, and
* are loadable via an userland utility.
*/
static unsigned char atkbd_set2_keycode[512] = {
0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41, 85,
0, 56, 42, 0, 29, 16, 2, 89, 0, 0, 44, 31, 30, 17, 3, 90,
0, 46, 45, 32, 18, 5, 4, 91, 0, 57, 47, 33, 20, 19, 6, 0,
0, 49, 48, 35, 34, 21, 7, 0, 0, 0, 50, 36, 22, 8, 9, 0,
0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0,
122, 89, 40,120, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 0,
85, 86, 90, 91, 92, 93, 14, 94, 95, 79, 0, 75, 71,121, 0,123,
82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99,
252, 0, 0, 65, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,251, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
252,253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,
0, 0, 92, 90, 85, 0,137, 0, 0, 0, 0, 91, 89,144,115, 0,
136,100,255, 0, 97,149,164, 0,156, 0, 0,140,115, 0, 0,125,
0,150, 0,154,152,163,151,126,112,166, 0,140, 0,147, 0,127,
159,167,139,160,163, 0, 0,116,158, 0,150,165, 0, 0, 0,142,
157, 0,114,166,168, 0, 0, 0,155, 0, 98,113, 0,148, 0,138,
0, 0, 0, 0, 0, 0,153,140, 0, 0, 96, 0, 0, 0,143, 0,
133, 0,116, 0,143, 0,174,133, 0,107, 0,105,102, 0, 0,112,
110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119
};
static unsigned char atkbd_set3_keycode[512] = {
0, 0, 0, 0, 0, 0, 0, 59, 1,138,128,129,130, 15, 41, 60,
131, 29, 42, 86, 58, 16, 2, 61,133, 56, 44, 31, 30, 17, 3, 62,
134, 46, 45, 32, 18, 5, 4, 63,135, 57, 47, 33, 20, 19, 6, 64,
136, 49, 48, 35, 34, 21, 7, 65,137,100, 50, 36, 22, 8, 9, 66,
125, 51, 37, 23, 24, 11, 10, 67,126, 52, 53, 38, 39, 25, 12, 68,
113,114, 40, 84, 26, 13, 87, 99,100, 54, 28, 27, 43, 84, 88, 70,
108,105,119,103,111,107, 14,110, 0, 79,106, 75, 71,109,102,104,
82, 83, 80, 76, 77, 72, 69, 98, 0, 96, 81, 0, 78, 73, 55, 85,
89, 90, 91, 92, 74, 0, 0, 0, 0, 0, 0,125,126,127,112, 0,
0,139,150,163,165,115,152,150,166,140,160,154,113,114,167,168,
148,149,147,140, 0, 0, 0, 0, 0, 0,251, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
252,253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255
};
#define ATKBD_CMD_SETLEDS 0x10ed
#define ATKBD_CMD_GSCANSET 0x11f0
#define ATKBD_CMD_SSCANSET 0x10f0
#define ATKBD_CMD_GETID 0x02f2
#define ATKBD_CMD_ENABLE 0x00f4
#define ATKBD_CMD_RESET_DIS 0x00f5
#define ATKBD_CMD_SETALL_MB 0x00f8
#define ATKBD_CMD_EX_ENABLE 0x10ea
#define ATKBD_CMD_EX_SETLEDS 0x20eb
#define ATKBD_RET_ACK 0xfa
#define ATKBD_RET_NAK 0xfe
#define ATKBD_KEY_UNKNOWN 0
#define ATKBD_KEY_BAT 251
#define ATKBD_KEY_EMUL0 252
#define ATKBD_KEY_EMUL1 253
#define ATKBD_KEY_RELEASE 254
#define ATKBD_KEY_NULL 255
/*
* The atkbd control structure
*/
struct atkbd {
unsigned char keycode[512];
struct input_dev dev;
struct serio *serio;
char name[64];
char phys[32];
struct tq_struct tq;
unsigned char cmdbuf[4];
unsigned char cmdcnt;
unsigned char set;
char release;
char ack;
char emul;
char error;
unsigned short id;
};
/*
* atkbd_interrupt(). Here takes place processing of data received from
* the keyboard into events.
*/
static void atkbd_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
{
struct atkbd *atkbd = serio->private;
int code = data;
#ifdef ATKBD_DEBUG
printk(KERN_DEBUG "atkbd.c: Received %02x\n", data);
#endif
switch (code) {
case ATKBD_RET_ACK:
atkbd->ack = 1;
return;
case ATKBD_RET_NAK:
atkbd->ack = -1;
return;
}
if (atkbd->cmdcnt) {
atkbd->cmdbuf[--atkbd->cmdcnt] = code;
return;
}
switch (atkbd->keycode[code]) {
case ATKBD_KEY_BAT:
queue_task(&atkbd->tq, &tq_immediate);
mark_bh(IMMEDIATE_BH);
return;
case ATKBD_KEY_EMUL0:
atkbd->emul = 1;
return;
case ATKBD_KEY_EMUL1:
atkbd->emul = 2;
return;
case ATKBD_KEY_RELEASE:
atkbd->release = 1;
return;
}
if (atkbd->emul) {
if (--atkbd->emul) return;
code |= 0x100;
}
switch (atkbd->keycode[code]) {
case ATKBD_KEY_NULL:
break;
case ATKBD_KEY_UNKNOWN:
printk(KERN_WARNING "atkbd.c: Unknown key (set %d, scancode %#x, on %s) %s.\n",
atkbd->set, code, serio->phys, atkbd->release ? "released" : "pressed");
break;
default:
input_report_key(&atkbd->dev, atkbd->keycode[code], !atkbd->release);
}
atkbd->release = 0;
}
/*
* atkbd_sendbyte() sends a byte to the keyboard, and waits for
* acknowledge. It doesn't handle resends according to the keyboard
* protocol specs, because if these are needed, the keyboard needs
* replacement anyway, and they only make a mess in the protocol.
*/
static int atkbd_sendbyte(struct atkbd *atkbd, unsigned char byte)
{
int timeout = 1000; /* 10 msec */
atkbd->ack = 0;
#ifdef ATKBD_DEBUG
printk(KERN_DEBUG "atkbd.c: Sent: %02x\n", byte);
#endif
serio_write(atkbd->serio, byte);
while (!atkbd->ack && timeout--) udelay(10);
return -(atkbd->ack <= 0);
}
/*
* atkbd_command() sends a command, and its parameters to the keyboard,
* then waits for the response and puts it in the param array.
*/
static int atkbd_command(struct atkbd *atkbd, unsigned char *param, int command)
{
int timeout = 10000; /* 100 msec */
int send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf;
int i;
atkbd->cmdcnt = receive;
if (command & 0xff)
if (atkbd_sendbyte(atkbd, command & 0xff))
return (atkbd->cmdcnt = 0) - 1;
for (i = 0; i < send; i++)
if (atkbd_sendbyte(atkbd, param[i]))
return (atkbd->cmdcnt = 0) - 1;
while (atkbd->cmdcnt && timeout--) udelay(10);
for (i = 0; i < receive; i++)
param[i] = atkbd->cmdbuf[(receive - 1) - i];
if (atkbd->cmdcnt)
return (atkbd->cmdcnt = 0) - 1;
return 0;
}
/*
* Event callback from the input module. Events that change the state of
* the hardware are processed here.
*/
static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct atkbd *atkbd = dev->private;
char param[2];
switch (type) {
case EV_LED:
*param = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0)
| (test_bit(LED_NUML, dev->led) ? 2 : 0)
| (test_bit(LED_CAPSL, dev->led) ? 4 : 0);
atkbd_command(atkbd, param, ATKBD_CMD_SETLEDS);
if (atkbd->set == 4) {
param[0] = 0;
param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0)
| (test_bit(LED_SLEEP, dev->led) ? 0x02 : 0)
| (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0)
| (test_bit(LED_MUTE, dev->led) ? 0x20 : 0);
atkbd_command(atkbd, param, ATKBD_CMD_EX_SETLEDS);
}
return 0;
}
return -1;
}
/*
* atkbd_set_3 checks if a keyboard has a working Set 3 support, and
* sets it into that. Unfortunately there are keyboards that can be switched
* to Set 3, but don't work well in that (BTC Multimedia ...)
*/
static int atkbd_set_3(struct atkbd *atkbd)
{
unsigned char param;
/*
* For known special keyboards we can go ahead and set the correct set.
*/
if (atkbd->id == 0xaca1) {
param = 3;
atkbd_command(atkbd, ¶m, ATKBD_CMD_SSCANSET);
return 3;
}
/*
* We check for the extra keys on an some keyboards that need extra
* command to get enabled. This shouldn't harm any keyboards not
* knowing the command.
*/
param = 0x71;
if (!atkbd_command(atkbd, ¶m, ATKBD_CMD_EX_ENABLE))
return 4;
/*
* Try to set the set we want.
*/
param = atkbd_set;
if (atkbd_command(atkbd, ¶m, ATKBD_CMD_SSCANSET))
return 2;
/*
* Read set number. Beware here. Some keyboards always send '2'
* or some other number regardless into what mode they have been
* attempted to be set. Other keyboards treat the '0' command as
* 'set to set 0', and not 'report current set' as they should.
* In that case we time out, and return 2.
*/
param = 0;
if (atkbd_command(atkbd, ¶m, ATKBD_CMD_GSCANSET))
return 2;
/*
* Here we return the set number the keyboard reports about
* itself.
*/
return (param == 3) ? 3 : 2;
}
/*
* atkbd_probe() probes for an AT keyboard on a serio port.
*/
static int atkbd_probe(struct atkbd *atkbd)
{
unsigned char param[2];
/*
* Full reset with selftest can on some keyboards be annoyingly slow,
* so we just do a reset-and-disable on the keyboard, which
* is considerably faster, but doesn't have to reset everything.
*/
if (atkbd_command(atkbd, NULL, ATKBD_CMD_RESET_DIS))
return -1;
/*
* Next, we check if it's a keyboard. It should send 0xab83
* (0xab84 on IBM ThinkPad, and 0xaca1 on a NCD Sun layout keyboard,
* 0xab02 on unxlated i8042 and 0xab03 on unxlated ThinkPad, 0xab7f
* on Fujitsu Lifebook).
* If it's a mouse, it'll only send 0x00 (0x03 if it's MS mouse),
* and we'll time out here, and report an error.
*/
param[0] = param[1] = 0;
if (atkbd_command(atkbd, param, ATKBD_CMD_GETID))
return -1;
atkbd->id = (param[0] << 8) | param[1];
if (atkbd->id != 0xab83 && atkbd->id != 0xab84 && atkbd->id != 0xaca1 &&
atkbd->id != 0xab7f && atkbd->id != 0xab02 && atkbd->id != 0xab03)
printk(KERN_WARNING "atkbd.c: Unusual keyboard ID: %#x on %s\n",
atkbd->id, atkbd->serio->phys);
return 0;
}
/*
* atkbd_initialize() sets the keyboard into a sane state.
*/
static void atkbd_initialize(struct atkbd *atkbd)
{
unsigned char param;
/*
* Disable autorepeat. We don't need it, as we do it in software anyway,
* because that way can get faster repeat, and have less system load
* (less accesses to the slow ISA hardware). If this fails, we don't care,
* and will just ignore the repeated keys.
*/
atkbd_command(atkbd, NULL, ATKBD_CMD_SETALL_MB);
/*
* We also shut off all the leds. The console code will turn them back on,
* if needed.
*/
param = 0;
atkbd_command(atkbd, ¶m, ATKBD_CMD_SETLEDS);
/*
* Last, we enable the keyboard so that we get keypresses from it.
*/
if (atkbd_command(atkbd, NULL, ATKBD_CMD_ENABLE))
printk(KERN_ERR "atkbd.c: Failed to enable keyboard on %s\n",
atkbd->serio->phys);
}
/*
* atkbd_disconnect() cleans up behind us ...
*/
static void atkbd_disconnect(struct serio *serio)
{
struct atkbd *atkbd = serio->private;
input_unregister_device(&atkbd->dev);
serio_close(serio);
kfree(atkbd);
}
/*
* atkbd_powerup() is called when the keyboard sends the 0xaa character,
* meaning that it was disconnected and reconnected. We close the port
* in that case and let the upper layer find an appropriate driver for
* the device that was connected. It may be a mouse, or a keyboard, we
* don't know yet.
*/
static void atkbd_powerup(void *data)
{
struct atkbd *atkbd = data;
mdelay(40); /* FIXME!!! Wait some nicer way */
serio_rescan(atkbd->serio);
}
/*
* atkbd_connect() is called when the serio module finds and interface
* that isn't handled yet by an appropriate device driver. We check if
* there is an AT keyboard out there and if yes, we register ourselves
* to the input module.
*/
static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
{
struct atkbd *atkbd;
int i;
if ((serio->type & SERIO_TYPE) != SERIO_8042)
return;
if (!(atkbd = kmalloc(sizeof(struct atkbd), GFP_KERNEL)))
return;
memset(atkbd, 0, sizeof(struct atkbd));
atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
atkbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
atkbd->serio = serio;
atkbd->dev.keycode = atkbd->keycode;
atkbd->dev.event = atkbd_event;
atkbd->dev.private = atkbd;
atkbd->tq.routine = atkbd_powerup;
atkbd->tq.data = atkbd;
serio->private = atkbd;
if (serio_open(serio, dev)) {
kfree(atkbd);
return;
}
if (atkbd_probe(atkbd)) {
serio_close(serio);
kfree(atkbd);
return;
}
atkbd->set = atkbd_set_3(atkbd);
if (atkbd->set == 4) {
atkbd->dev.ledbit[0] |= BIT(LED_COMPOSE) | BIT(LED_SUSPEND) | BIT(LED_SLEEP) | BIT(LED_MUTE);
sprintf(atkbd->name, "AT Set 2 Extended keyboard\n");
} else
sprintf(atkbd->name, "AT Set %d keyboard", atkbd->set);
sprintf(atkbd->phys, "%s/input0", serio->phys);
if (atkbd->set == 3)
memcpy(atkbd->keycode, atkbd_set3_keycode, sizeof(atkbd->keycode));
else
memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode));
atkbd->dev.name = atkbd->name;
atkbd->dev.phys = atkbd->phys;
atkbd->dev.idbus = BUS_I8042;
atkbd->dev.idvendor = 0x0001;
atkbd->dev.idproduct = atkbd->set;
atkbd->dev.idversion = atkbd->id;
for (i = 0; i < 512; i++)
if (atkbd->keycode[i] && atkbd->keycode[i] <= 250)
set_bit(atkbd->keycode[i], atkbd->dev.keybit);
input_register_device(&atkbd->dev);
printk(KERN_INFO "input: %s on %s\n", atkbd->name, serio->phys);
atkbd_initialize(atkbd);
}
static struct serio_dev atkbd_dev = {
interrupt: atkbd_interrupt,
connect: atkbd_connect,
disconnect: atkbd_disconnect
};
/*
* Module init and exit.
*/
void __init atkbd_setup(char *str, int *ints)
{
if (!ints[0]) atkbd_set = ints[1];
}
int __init atkbd_init(void)
{
serio_register_device(&atkbd_dev);
return 0;
}
void __exit atkbd_exit(void)
{
serio_unregister_device(&atkbd_dev);
}
module_init(atkbd_init);
module_exit(atkbd_exit);
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:34:36
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/joystick/iforce
In directory usw-pr-cvs1:/tmp/cvs-serv7694
Added Files:
Tag: 1.5
iforce.h
Log Message:
Moved.
--- NEW FILE: iforce.h ---
/*
* $Id: iforce.h,v 1.5 2002/01/22 20:34:33 vojtech Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik <vo...@uc...>
* Copyright (c) 2001 Johann Deneux <de...@if...>
*
* USB/RS232 I-Force joysticks and wheels.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/serio.h>
#include <linux/config.h>
#include <linux/circ_buf.h>
#include <asm/semaphore.h>
/* FF: This module provides arbitrary resource management routines.
* I use it to manage the device's memory.
* Despite the name of this module, I am *not* going to access the ioports.
*/
#include <linux/ioport.h>
#define IFORCE_MAX_LENGTH 16
#if defined(CONFIG_INPUT_IFORCE_232) || defined(CONFIG_INPUT_IFORCE_232_MODULE)
#define IFORCE_232 1
#endif
#if defined(CONFIG_INPUT_IFORCE_USB) || defined(CONFIG_INPUT_IFORCE_USB_MODULE)
#define IFORCE_USB 2
#endif
#define FALSE 0
#define TRUE 1
#define FF_EFFECTS_MAX 32
/* Each force feedback effect is made of one core effect, which can be
* associated to at most to effect modifiers
*/
#define FF_MOD1_IS_USED 0
#define FF_MOD2_IS_USED 1
#define FF_CORE_IS_USED 2
#define FF_CORE_IS_PLAYED 3 /* Effect is actually being played */
#define FF_CORE_SHOULD_PLAY 4 /* User wants the effect to be played */
#define FF_CORE_UPDATE 5 /* Effect is being updated */
#define FF_MODCORE_MAX 5
#define CHECK_OWNERSHIP(i, iforce) \
((i) < FF_EFFECTS_MAX && i >= 0 && \
test_bit(FF_CORE_IS_USED, (iforce)->core_effects[(i)].flags) && \
(current->pid == 0 || \
(iforce)->core_effects[(i)].owner == current->pid))
struct iforce_core_effect {
/* Information about where modifiers are stored in the device's memory */
struct resource mod1_chunk;
struct resource mod2_chunk;
unsigned long flags[NBITS(FF_MODCORE_MAX)];
pid_t owner;
/* Used to keep track of parameters of an effect. They are needed
* to know what parts of an effect changed in an update operation.
* We try to send only parameter packets if possible, as sending
* effect parameter requires the effect to be stoped and restarted
*/
struct ff_effect effect;
};
#define FF_CMD_EFFECT 0x010e
#define FF_CMD_SHAPE 0x0208
#define FF_CMD_MAGNITUDE 0x0303
#define FF_CMD_PERIOD 0x0407
#define FF_CMD_INTERACT 0x050a
#define FF_CMD_AUTOCENTER 0x4002
#define FF_CMD_PLAY 0x4103
#define FF_CMD_ENABLE 0x4201
#define FF_CMD_GAIN 0x4301
#define FF_CMD_QUERY 0xff01
/* Buffer for async write */
#define XMIT_SIZE 256
#define XMIT_INC(var, n) (var)+=n; (var)&= XMIT_SIZE -1
/* iforce::xmit_flags */
#define IFORCE_XMIT_RUNNING 0
#define IFORCE_XMIT_AGAIN 1
struct iforce_device {
u16 idvendor;
u16 idproduct;
char *name;
signed short *btn;
signed short *abs;
signed short *ff;
};
struct iforce {
struct input_dev dev; /* Input device interface */
struct iforce_device *type;
char name[64];
char phys[64];
int open;
int bus;
unsigned char data[IFORCE_MAX_LENGTH];
unsigned char edata[IFORCE_MAX_LENGTH];
u16 ecmd;
u16 expect_packet;
#ifdef IFORCE_232
struct serio *serio; /* RS232 transfer */
int idx, pkt, len, id;
unsigned char csum;
#endif
#ifdef IFORCE_USB
struct usb_device *usbdev; /* USB transfer */
struct urb irq, out, ctrl;
struct usb_ctrlrequest dr;
#endif
spinlock_t xmit_lock;
/* Buffer used for asynchronous sending of bytes to the device */
struct circ_buf xmit;
unsigned char xmit_data[XMIT_SIZE];
long xmit_flags[1];
/* Force Feedback */
wait_queue_head_t wait;
struct resource device_memory;
struct iforce_core_effect core_effects[FF_EFFECTS_MAX];
struct semaphore mem_mutex;
};
/* Get hi and low bytes of a 16-bits int */
#define HI(a) ((unsigned char)((a) >> 8))
#define LO(a) ((unsigned char)((a) & 0xff))
/* For many parameters, it seems that 0x80 is a special value that should
* be avoided. Instead, we replace this value by 0x7f
*/
#define HIFIX80(a) ((unsigned char)(((a)<0? (a)+255 : (a))>>8))
/* Encode a time value */
#define TIME_SCALE(a) ((a) == 0xffff ? 0xffff : (a) * 1000 / 256)
/* Public functions */
/* iforce-serio.c */
void iforce_serial_xmit(struct iforce *iforce);
/* iforce-usb.c */
void iforce_usb_xmit(struct iforce *iforce);
/* iforce-main.c */
int iforce_init_device(struct iforce *iforce);
/* iforce-packets.c */
int iforce_control_playback(struct iforce*, u16 id, unsigned int);
void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data);
int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data);
void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data) ;
int iforce_get_id_packet(struct iforce *iforce, char *packet);
/* iforce-ff.c */
int iforce_upload_periodic(struct iforce*, struct ff_effect*, int is_update);
int iforce_upload_constant(struct iforce*, struct ff_effect*, int is_update);
int iforce_upload_interactive(struct iforce*, struct ff_effect*, int is_update);
/* Public variables */
extern struct serio_dev iforce_serio_dev;
extern struct usb_driver iforce_usb_driver;
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:34:23
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/joystick/iforce
In directory usw-pr-cvs1:/tmp/cvs-serv7598
Added Files:
Tag: 1.5
iforce-usb.c
Log Message:
Moved.
--- NEW FILE: iforce-usb.c ---
/*
* $Id: iforce-usb.c,v 1.5 2002/01/22 20:34:21 vojtech Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik <vo...@uc...>
* Copyright (c) 2001 Johann Deneux <de...@if...>
*
* USB/RS232 I-Force joysticks and wheels.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include "iforce.h"
void iforce_usb_xmit(struct iforce *iforce)
{
int n, c;
unsigned long flags;
spin_lock_irqsave(&iforce->xmit_lock, flags);
if (iforce->xmit.head == iforce->xmit.tail) {
spin_unlock_irqrestore(&iforce->xmit_lock, flags);
return;
}
((char *)iforce->out.transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail];
XMIT_INC(iforce->xmit.tail, 1);
n = iforce->xmit.buf[iforce->xmit.tail];
XMIT_INC(iforce->xmit.tail, 1);
iforce->out.transfer_buffer_length = n + 2;
iforce->out.dev = iforce->usbdev;
/* Copy rest of data then */
c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE);
if (n < c) c=n;
memcpy(iforce->out.transfer_buffer + 1,
&iforce->xmit.buf[iforce->xmit.tail],
c);
if (n != c) {
memcpy(iforce->out.transfer_buffer + 1 + c,
&iforce->xmit.buf[0],
n-c);
}
XMIT_INC(iforce->xmit.tail, n);
if ( (n=usb_submit_urb(&iforce->out)) ) {
printk(KERN_WARNING "iforce.c: iforce_usb_xmit: usb_submit_urb failed %d\n", n);
}
spin_unlock_irqrestore(&iforce->xmit_lock, flags);
}
static void iforce_usb_irq(struct urb *urb)
{
struct iforce *iforce = urb->context;
if (urb->status) return;
iforce_process_packet(iforce,
(iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1);
}
static void iforce_usb_out(struct urb *urb)
{
struct iforce *iforce = urb->context;
if (urb->status) return;
iforce_usb_xmit(iforce);
if (waitqueue_active(&iforce->wait))
wake_up(&iforce->wait);
}
static void iforce_usb_ctrl(struct urb *urb)
{
struct iforce *iforce = urb->context;
if (urb->status) return;
iforce->ecmd = 0xff00 | urb->actual_length;
if (waitqueue_active(&iforce->wait))
wake_up(&iforce->wait);
}
static void *iforce_usb_probe(struct usb_device *dev, unsigned int ifnum,
const struct usb_device_id *id)
{
struct usb_endpoint_descriptor *epirq, *epout;
struct iforce *iforce;
epirq = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
epout = dev->config[0].interface[ifnum].altsetting[0].endpoint + 1;
if (!(iforce = kmalloc(sizeof(struct iforce) + 32, GFP_KERNEL))) return NULL;
memset(iforce, 0, sizeof(struct iforce));
iforce->bus = IFORCE_USB;
iforce->usbdev = dev;
iforce->dr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE;
iforce->dr.wIndex = 0;
iforce->dr.wLength = 16;
FILL_INT_URB(&iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress),
iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval);
FILL_BULK_URB(&iforce->out, dev, usb_sndbulkpipe(dev, epout->bEndpointAddress),
iforce + 1, 32, iforce_usb_out, iforce);
FILL_CONTROL_URB(&iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0),
(void*) &iforce->dr, iforce->edata, 16, iforce_usb_ctrl, iforce);
if (iforce_init_device(iforce)) {
kfree(iforce);
return NULL;
}
return iforce;
}
static void iforce_usb_disconnect(struct usb_device *dev, void *ptr)
{
struct iforce *iforce = ptr;
usb_unlink_urb(&iforce->irq);
input_unregister_device(&iforce->dev);
kfree(iforce);
}
static struct usb_device_id iforce_usb_ids [] = {
{ USB_DEVICE(0x044f, 0xa01c) }, /* Thrustmaster Motor Sport GT */
{ USB_DEVICE(0x046d, 0xc281) }, /* Logitech WingMan Force */
{ USB_DEVICE(0x046d, 0xc291) }, /* Logitech WingMan Formula Force */
{ USB_DEVICE(0x05ef, 0x020a) }, /* AVB Top Shot Pegasus */
{ USB_DEVICE(0x05ef, 0x8884) }, /* AVB Mag Turbo Force */
{ USB_DEVICE(0x05ef, 0x8888) }, /* AVB Top Shot FFB Racing Wheel */
{ USB_DEVICE(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */
{ USB_DEVICE(0x06f8, 0x0004) }, /* Guillemot Force Feedback Racing Wheel */
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, iforce_usb_ids);
struct usb_driver iforce_usb_driver = {
name: "iforce",
probe: iforce_usb_probe,
disconnect: iforce_usb_disconnect,
id_table: iforce_usb_ids,
};
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:34:10
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/joystick/iforce
In directory usw-pr-cvs1:/tmp/cvs-serv7518
Added Files:
Tag: 1.3
iforce-serio.c
Log Message:
Moved.
--- NEW FILE: iforce-serio.c ---
/*
* $Id: iforce-serio.c,v 1.3 2002/01/22 20:34:07 vojtech Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik <vo...@uc...>
* Copyright (c) 2001 Johann Deneux <de...@if...>
*
* USB/RS232 I-Force joysticks and wheels.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include "iforce.h"
void iforce_serial_xmit(struct iforce *iforce)
{
unsigned char cs;
int i;
unsigned long flags;
if (test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) {
set_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags);
return;
}
spin_lock_irqsave(&iforce->xmit_lock, flags);
again:
if (iforce->xmit.head == iforce->xmit.tail) {
clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
spin_unlock_irqrestore(&iforce->xmit_lock, flags);
return;
}
cs = 0x2b;
serio_write(iforce->serio, 0x2b);
serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
cs ^= iforce->xmit.buf[iforce->xmit.tail];
XMIT_INC(iforce->xmit.tail, 1);
for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) {
serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
cs ^= iforce->xmit.buf[iforce->xmit.tail];
XMIT_INC(iforce->xmit.tail, 1);
}
serio_write(iforce->serio, cs);
if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags))
goto again;
clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
spin_unlock_irqrestore(&iforce->xmit_lock, flags);
}
static void iforce_serio_write_wakeup(struct serio *serio)
{
iforce_serial_xmit((struct iforce *)serio->private);
}
static void iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags)
{
struct iforce* iforce = serio->private;
if (!iforce->pkt) {
if (data != 0x2b) {
return;
}
iforce->pkt = 1;
return;
}
if (!iforce->id) {
if (data > 3 && data != 0xff) {
iforce->pkt = 0;
return;
}
iforce->id = data;
return;
}
if (!iforce->len) {
if (data > IFORCE_MAX_LENGTH) {
iforce->pkt = 0;
iforce->id = 0;
return;
}
iforce->len = data;
return;
}
if (iforce->idx < iforce->len) {
iforce->csum += iforce->data[iforce->idx++] = data;
return;
}
if (iforce->idx == iforce->len) {
iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data);
iforce->pkt = 0;
iforce->id = 0;
iforce->len = 0;
iforce->idx = 0;
iforce->csum = 0;
}
}
static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev)
{
struct iforce *iforce;
if (serio->type != (SERIO_RS232 | SERIO_IFORCE))
return;
if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return;
memset(iforce, 0, sizeof(struct iforce));
iforce->bus = IFORCE_232;
iforce->serio = serio;
serio->private = iforce;
if (serio_open(serio, dev)) {
kfree(iforce);
return;
}
if (iforce_init_device(iforce)) {
serio_close(serio);
kfree(iforce);
return;
}
}
static void iforce_serio_disconnect(struct serio *serio)
{
struct iforce* iforce = serio->private;
input_unregister_device(&iforce->dev);
serio_close(serio);
kfree(iforce);
}
struct serio_dev iforce_serio_dev = {
write_wakeup: iforce_serio_write_wakeup,
interrupt: iforce_serio_irq,
connect: iforce_serio_connect,
disconnect: iforce_serio_disconnect,
};
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:33:56
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/joystick/iforce
In directory usw-pr-cvs1:/tmp/cvs-serv7395
Added Files:
Tag: 1.6
iforce-packets.c
Log Message:
Moved.
--- NEW FILE: iforce-packets.c ---
/*
* $Id: iforce-packets.c,v 1.6 2002/01/22 20:33:53 vojtech Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik <vo...@uc...>
* Copyright (c) 2001 Johann Deneux <de...@if...>
*
* USB/RS232 I-Force joysticks and wheels.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include "iforce.h"
static struct {
__s32 x;
__s32 y;
} iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data)
{
int i;
printk(KERN_DEBUG "iforce.c: %s ( cmd = %04x, data = ", msg, cmd);
for (i = 0; i < LO(cmd); i++)
printk("%02x ", data[i]);
printk(")\n");
}
/*
* Send a packet of bytes to the device
*/
int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data)
{
/* Copy data to buffer */
int n = LO(cmd);
int c;
int empty;
int head, tail;
unsigned long flags;
/*
* Update head and tail of xmit buffer
*/
spin_lock_irqsave(&iforce->xmit_lock, flags);
head = iforce->xmit.head;
tail = iforce->xmit.tail;
if (CIRC_SPACE(head, tail, XMIT_SIZE) < n+2) {
printk(KERN_WARNING "iforce.c: not enough space in xmit buffer to send new packet\n");
spin_unlock_irqrestore(&iforce->xmit_lock, flags);
return -1;
}
empty = head == tail;
XMIT_INC(iforce->xmit.head, n+2);
/*
* Store packet in xmit buffer
*/
iforce->xmit.buf[head] = HI(cmd);
XMIT_INC(head, 1);
iforce->xmit.buf[head] = LO(cmd);
XMIT_INC(head, 1);
c = CIRC_SPACE_TO_END(head, tail, XMIT_SIZE);
if (n < c) c=n;
memcpy(&iforce->xmit.buf[head],
data,
c);
if (n != c) {
memcpy(&iforce->xmit.buf[0],
data + c,
n - c);
}
XMIT_INC(head, n);
spin_unlock_irqrestore(&iforce->xmit_lock, flags);
/*
* If necessary, start the transmission
*/
switch (iforce->bus) {
#ifdef IFORCE_232
case IFORCE_232:
if (empty)
iforce_serial_xmit(iforce);
break;
#endif
#ifdef IFORCE_USB
case IFORCE_USB:
if (empty & !iforce->out.status) {
iforce_usb_xmit(iforce);
}
break;
#endif
}
return 0;
}
/* Start or stop an effect */
int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value)
{
unsigned char data[3];
printk(KERN_DEBUG "iforce-packets.c: control_playback %d %d\n", id, value);
data[0] = LO(id);
data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0;
data[2] = LO(value);
return iforce_send_packet(iforce, FF_CMD_PLAY, data);
}
/* Mark an effect that was being updated as ready. That means it can be updated
* again */
static int mark_core_as_ready(struct iforce *iforce, unsigned short addr)
{
int i;
for (i=0; i<iforce->dev.ff_effects_max; ++i) {
if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) &&
(iforce->core_effects[i].mod1_chunk.start == addr ||
iforce->core_effects[i].mod2_chunk.start == addr)) {
clear_bit(FF_CORE_UPDATE, iforce->core_effects[i].flags);
return 0;
}
}
printk(KERN_WARNING "iforce-packets.c: unused effect %04x updated !!!\n", addr);
return -1;
}
void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data)
{
struct input_dev *dev = &iforce->dev;
int i;
static int being_used = 0;
if (being_used)
printk(KERN_WARNING "iforce-packets.c: re-entrant call to iforce_process %d\n", being_used);
being_used++;
#ifdef IFORCE_232
if (HI(iforce->expect_packet) == HI(cmd)) {
iforce->expect_packet = 0;
iforce->ecmd = cmd;
memcpy(iforce->edata, data, IFORCE_MAX_LENGTH);
if (waitqueue_active(&iforce->wait))
wake_up(&iforce->wait);
}
#endif
if (!iforce->type) {
being_used--;
return;
}
switch (HI(cmd)) {
case 0x01: /* joystick position data */
case 0x03: /* wheel position data */
if (HI(cmd) == 1) {
input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0]));
input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2]));
input_report_abs(dev, ABS_THROTTLE, 255 - data[4]);
if (LO(cmd) >= 8 && test_bit(ABS_RUDDER ,dev->absbit))
input_report_abs(dev, ABS_RUDDER, (__s8)data[7]);
} else {
input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0]));
input_report_abs(dev, ABS_GAS, 255 - data[2]);
input_report_abs(dev, ABS_BRAKE, 255 - data[3]);
}
input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x);
input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y);
for (i = 0; iforce->type->btn[i] >= 0; i++)
input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7)));
break;
case 0x02: /* status report */
input_report_key(dev, BTN_DEAD, data[0] & 0x02);
/* Check if an effect was just started or stopped */
i = data[1] & 0x7f;
if (data[1] & 0x80) {
if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
/* Report play event */
input_report_ff_status(dev, i, FF_STATUS_PLAYING);
}
}
else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
/* Report stop event */
input_report_ff_status(dev, i, FF_STATUS_STOPPED);
}
if (LO(cmd) > 3) {
int j;
for (j=3; j<LO(cmd); j+=2) {
mark_core_as_ready(iforce, data[j] | (data[j+1]<<8));
}
}
break;
}
being_used--;
}
int iforce_get_id_packet(struct iforce *iforce, char *packet)
{
DECLARE_WAITQUEUE(wait, current);
int timeout = HZ; /* 1 second */
switch (iforce->bus) {
#ifdef IFORCE_USB
case IFORCE_USB:
iforce->dr.bRequest = packet[0];
iforce->ctrl.dev = iforce->usbdev;
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&iforce->wait, &wait);
if (usb_submit_urb(&iforce->ctrl)) {
set_current_state(TASK_RUNNING);
remove_wait_queue(&iforce->wait, &wait);
return -1;
}
while (timeout && iforce->ctrl.status == -EINPROGRESS)
timeout = schedule_timeout(timeout);
set_current_state(TASK_RUNNING);
remove_wait_queue(&iforce->wait, &wait);
if (!timeout) {
usb_unlink_urb(&iforce->ctrl);
return -1;
}
break;
#endif
#ifdef IFORCE_232
case IFORCE_232:
iforce->expect_packet = FF_CMD_QUERY;
iforce_send_packet(iforce, FF_CMD_QUERY, packet);
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&iforce->wait, &wait);
while (timeout && iforce->expect_packet)
timeout = schedule_timeout(timeout);
set_current_state(TASK_RUNNING);
remove_wait_queue(&iforce->wait, &wait);
if (!timeout) {
iforce->expect_packet = 0;
return -1;
}
break;
#endif
}
return -(iforce->edata[0] != packet[0]);
}
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:33:40
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/joystick/iforce
In directory usw-pr-cvs1:/tmp/cvs-serv7301
Added Files:
Tag: 1.6
iforce-main.c
Log Message:
Moved.
--- NEW FILE: iforce-main.c ---
/*
* $Id: iforce-main.c,v 1.6 2002/01/22 20:33:38 vojtech Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik <vo...@uc...>
* Copyright (c) 2001 Johann Deneux <de...@if...>
*
* USB/RS232 I-Force joysticks and wheels.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include "iforce.h"
#include "usbpath.h"
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>, Johann Deneux <de...@if...>");
MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver");
MODULE_LICENSE("GPL");
static signed short btn_joystick[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, BTN_DEAD, -1 };
static signed short btn_wheel[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 };
static signed short btn_avb_tw[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE,
BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 };
static signed short abs_joystick[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
static signed short abs_joystick2[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_HAT0X, ABS_HAT0Y, -1 };
static signed short abs_wheel[] = { ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, -1 };
static signed short ff_iforce[] = { FF_PERIODIC, FF_CONSTANT, FF_SPRING, FF_FRICTION,
FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, FF_SAW_DOWN, FF_GAIN, FF_AUTOCENTER, -1 };
static struct iforce_device iforce_device[] = {
{ 0x044f, 0xa01c, "Thrustmaster Motor Sport GT", btn_wheel, abs_wheel, ff_iforce },
{ 0x046d, 0xc281, "Logitech WingMan Force", btn_joystick, abs_joystick, ff_iforce },
{ 0x046d, 0xc291, "Logitech WingMan Formula Force", btn_wheel, abs_wheel, ff_iforce },
{ 0x05ef, 0x020a, "AVB Top Shot Pegasus", btn_joystick, abs_joystick2, ff_iforce },
{ 0x05ef, 0x8884, "AVB Mag Turbo Force", btn_wheel, abs_wheel, ff_iforce },
{ 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel", btn_avb_tw, abs_wheel, ff_iforce },
{ 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce },
{ 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce },
{ 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]", btn_joystick, abs_joystick, ff_iforce }
};
static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct iforce* iforce = (struct iforce*)(dev->private);
unsigned char data[3];
printk(KERN_DEBUG "iforce.c: input_event(type = %d, code = %d, value = %d)\n", type, code, value);
if (type != EV_FF)
return -1;
switch (code) {
case FF_GAIN:
data[0] = value >> 9;
iforce_send_packet(iforce, FF_CMD_GAIN, data);
return 0;
case FF_AUTOCENTER:
data[0] = 0x03;
data[1] = value >> 9;
iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
data[0] = 0x04;
data[1] = 0x01;
iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
return 0;
default: /* Play or stop an effect */
if (!CHECK_OWNERSHIP(code, iforce)) {
return -1;
}
if (value > 0) {
set_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags);
}
else {
clear_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags);
}
iforce_control_playback(iforce, code, value);
return 0;
}
return -1;
}
/*
* Function called when an ioctl is performed on the event dev entry.
* It uploads an effect to the device
*/
static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
{
struct iforce* iforce = (struct iforce*)(dev->private);
int id;
int ret;
int is_update;
/*
* If we want to create a new effect, get a free id
*/
if (effect->id == -1) {
for (id=0; id < FF_EFFECTS_MAX; ++id)
if (!test_and_set_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags)) break;
if ( id == FF_EFFECTS_MAX || id >= iforce->dev.ff_effects_max)
return -ENOMEM;
effect->id = id;
iforce->core_effects[id].owner = current->pid;
iforce->core_effects[id].flags[0] = (1<<FF_CORE_IS_USED); /* Only IS_USED bit must be set */
is_update = FALSE;
}
else {
/* We want to update an effect */
if (!CHECK_OWNERSHIP(effect->id, iforce)) return -EACCES;
/* Parameter type cannot be updated */
if (effect->type != iforce->core_effects[effect->id].effect.type)
return -EINVAL;
/* Check the effect is not already being updated */
if (test_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags)) {
return -EAGAIN;
}
is_update = TRUE;
}
/*
* Upload the effect
*/
switch (effect->type) {
case FF_PERIODIC:
ret = iforce_upload_periodic(iforce, effect, is_update);
break;
case FF_CONSTANT:
ret = iforce_upload_constant(iforce, effect, is_update);
break;
case FF_SPRING:
case FF_FRICTION:
ret = iforce_upload_interactive(iforce, effect, is_update);
break;
default:
return -EINVAL;
}
if (ret == 0) {
/* A packet was sent, forbid new updates until we are notified
* that the packet was updated
*/
set_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags);
}
iforce->core_effects[effect->id].effect = *effect;
return ret;
}
/*
* Erases an effect: it frees the effect id and mark as unused the memory
* allocated for the parameters
*/
static int iforce_erase_effect(struct input_dev *dev, int effect_id)
{
struct iforce* iforce = (struct iforce*)(dev->private);
int err = 0;
struct iforce_core_effect* core_effect;
/* Check who is trying to erase this effect */
if (iforce->core_effects[effect_id].owner != current->pid) {
printk(KERN_WARNING "iforce.c: %d tried to erase an effect belonging to %d\n", current->pid, iforce->core_effects[effect_id].owner);
return -EACCES;
}
if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX)
return -EINVAL;
core_effect = iforce->core_effects + effect_id;
if (test_bit(FF_MOD1_IS_USED, core_effect->flags))
err = release_resource(&(iforce->core_effects[effect_id].mod1_chunk));
if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags))
err = release_resource(&(iforce->core_effects[effect_id].mod2_chunk));
/*TODO: remember to change that if more FF_MOD* bits are added */
core_effect->flags[0] = 0;
return err;
}
static int iforce_open(struct input_dev *dev)
{
struct iforce *iforce = dev->private;
switch (iforce->bus) {
#ifdef IFORCE_USB
case IFORCE_USB:
if (iforce->open++)
break;
iforce->irq.dev = iforce->usbdev;
if (usb_submit_urb(&iforce->irq))
return -EIO;
break;
#endif
}
/* Enable force feedback */
iforce_send_packet(iforce, FF_CMD_ENABLE, "\004");
return 0;
}
static int iforce_flush(struct input_dev *dev, struct file *file)
{
struct iforce *iforce = dev->private;
int i;
/* Erase all effects this process owns */
for (i=0; i<dev->ff_effects_max; ++i) {
if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) &&
current->pid == iforce->core_effects[i].owner) {
/* Stop effect */
input_report_ff(dev, i, 0);
/* Free ressources assigned to effect */
if (iforce_erase_effect(dev, i)) {
printk(KERN_WARNING "iforce_flush: (%s) erase effect %d failed\n", dev->phys, i);
}
}
}
return 0;
}
static void iforce_close(struct input_dev *dev)
{
struct iforce *iforce = dev->private;
printk(KERN_DEBUG "iforce.c: in iforce_close\n");
/* Disable force feedback playback */
iforce_send_packet(iforce, FF_CMD_ENABLE, "\001");
switch (iforce->bus) {
#ifdef IFORCE_USB
case IFORCE_USB:
if (!--iforce->open)
usb_unlink_urb(&iforce->irq);
break;
#endif
}
}
int iforce_init_device(struct iforce *iforce)
{
unsigned char c[] = "CEOV";
char path[64];
int i;
init_waitqueue_head(&iforce->wait);
spin_lock_init(&iforce->xmit_lock);
init_MUTEX(&iforce->mem_mutex);
iforce->xmit.buf = iforce->xmit_data;
iforce->dev.ff_effects_max = 10;
switch (iforce->bus) {
#ifdef IFORCE_232
case IFORCE_232:
strcpy(path, iforce->serio->phys);
break;
#endif
#ifdef IFORCE_USB
case IFORCE_USB:
usb_make_path(iforce->usbdev, path, 64);
break;
#endif
}
sprintf(iforce->phys, "%s/input0", path);
/*
* Input device fields.
*/
iforce->dev.idbus = BUS_USB;
iforce->dev.private = iforce;
iforce->dev.name = iforce->phys;
iforce->dev.open = iforce_open;
iforce->dev.close = iforce_close;
iforce->dev.flush = iforce_flush;
iforce->dev.event = iforce_input_event;
iforce->dev.upload_effect = iforce_upload_effect;
iforce->dev.erase_effect = iforce_erase_effect;
/*
* On-device memory allocation.
*/
iforce->device_memory.name = "I-Force device effect memory";
iforce->device_memory.start = 0;
iforce->device_memory.end = 200;
iforce->device_memory.flags = IORESOURCE_MEM;
iforce->device_memory.parent = NULL;
iforce->device_memory.child = NULL;
iforce->device_memory.sibling = NULL;
/*
* Wait until device ready - until it sends its first response.
*/
for (i = 0; i < 20; i++)
if (!iforce_get_id_packet(iforce, "O"))
break;
if (i == 20) { /* 5 seconds */
printk(KERN_ERR "iforce.c: Timeout waiting for response from device.\n");
iforce_close(&iforce->dev);
return -1;
}
/*
* Get device info.
*/
if (!iforce_get_id_packet(iforce, "M"))
iforce->dev.idvendor = (iforce->edata[2] << 8) | iforce->edata[1];
if (!iforce_get_id_packet(iforce, "P"))
iforce->dev.idproduct = (iforce->edata[2] << 8) | iforce->edata[1];
if (!iforce_get_id_packet(iforce, "B"))
iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1];
if (!iforce_get_id_packet(iforce, "N"))
iforce->dev.ff_effects_max = iforce->edata[1];
/* Check if the device can store more effects than the driver can really handle */
if (iforce->dev.ff_effects_max > FF_EFFECTS_MAX) {
printk(KERN_WARNING "input??: Device can handle %d effects, but N_EFFECTS_MAX is set to %d in iforce.c\n",
iforce->dev.ff_effects_max, FF_EFFECTS_MAX);
iforce->dev.ff_effects_max = FF_EFFECTS_MAX;
}
/*
* Display additional info.
*/
for (i = 0; c[i]; i++)
if (!iforce_get_id_packet(iforce, c + i))
iforce_dump_packet("info", iforce->ecmd, iforce->edata);
/*
* Disable spring, enable force feedback.
* FIXME: We should use iforce_set_autocenter() et al here.
*/
iforce_send_packet(iforce, FF_CMD_AUTOCENTER, "\004\000");
/*
* Find appropriate device entry
*/
for (i = 0; iforce_device[i].idvendor; i++)
if (iforce_device[i].idvendor == iforce->dev.idvendor &&
iforce_device[i].idproduct == iforce->dev.idproduct)
break;
iforce->type = iforce_device + i;
sprintf(iforce->name, iforce->type->name,
iforce->dev.idproduct, iforce->dev.idvendor);
/*
* Set input device bitfields and ranges.
*/
iforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF) | BIT(EV_FF_STATUS);
for (i = 0; iforce->type->btn[i] >= 0; i++) {
signed short t = iforce->type->btn[i];
set_bit(t, iforce->dev.keybit);
if (t != BTN_DEAD)
set_bit(FF_BTN(t), iforce->dev.ffbit);
}
for (i = 0; iforce->type->abs[i] >= 0; i++) {
signed short t = iforce->type->abs[i];
set_bit(t, iforce->dev.absbit);
switch (t) {
case ABS_X:
case ABS_Y:
case ABS_WHEEL:
iforce->dev.absmax[t] = 1920;
iforce->dev.absmin[t] = -1920;
iforce->dev.absflat[t] = 128;
iforce->dev.absfuzz[t] = 16;
set_bit(FF_ABS(t), iforce->dev.ffbit);
break;
case ABS_THROTTLE:
case ABS_GAS:
case ABS_BRAKE:
iforce->dev.absmax[t] = 255;
iforce->dev.absmin[t] = 0;
break;
case ABS_RUDDER:
iforce->dev.absmax[t] = 127;
iforce->dev.absmin[t] = -128;
break;
case ABS_HAT0X:
case ABS_HAT0Y:
iforce->dev.absmax[t] = 1;
iforce->dev.absmin[t] = -1;
break;
}
}
for (i = 0; iforce->type->ff[i] >= 0; i++)
set_bit(iforce->type->ff[i], iforce->dev.ffbit);
/*
* Register input device.
*/
input_register_device(&iforce->dev);
printk(KERN_INFO "input: %s [%d effects, %ld bytes memory] on %s\n",
iforce->dev.name, iforce->dev.ff_effects_max,
iforce->device_memory.end, path);
return 0;
}
static int __init iforce_init(void)
{
#ifdef IFORCE_USB
usb_register(&iforce_usb_driver);
#endif
#ifdef IFORCE_232
serio_register_device(&iforce_serio_dev);
#endif
return 0;
}
static void __exit iforce_exit(void)
{
#ifdef IFORCE_USB
usb_deregister(&iforce_usb_driver);
#endif
#ifdef IFORCE_232
serio_unregister_device(&iforce_serio_dev);
#endif
}
module_init(iforce_init);
module_exit(iforce_exit);
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:33:27
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/joystick/iforce
In directory usw-pr-cvs1:/tmp/cvs-serv7222
Added Files:
Tag: 1.7
iforce-ff.c
Log Message:
Moved.
--- NEW FILE: iforce-ff.c ---
/*
* $Id: iforce-ff.c,v 1.7 2002/01/22 20:33:25 vojtech Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik <vo...@uc...>
* Copyright (c) 2001 Johann Deneux <de...@if...>
*
* USB/RS232 I-Force joysticks and wheels.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include "iforce.h"
/*
* Set the magnitude of a constant force effect
* Return error code
*
* Note: caller must ensure exclusive access to device
*/
static int make_magnitude_modifier(struct iforce* iforce,
struct resource* mod_chunk, int no_alloc, __s16 level)
{
unsigned char data[3];
if (!no_alloc) {
down(&iforce->mem_mutex);
if (allocate_resource(&(iforce->device_memory), mod_chunk, 2,
iforce->device_memory.start, iforce->device_memory.end, 2L,
NULL, NULL)) {
up(&iforce->mem_mutex);
return -ENOMEM;
}
up(&iforce->mem_mutex);
}
data[0] = LO(mod_chunk->start);
data[1] = HI(mod_chunk->start);
data[2] = HIFIX80(level);
iforce_send_packet(iforce, FF_CMD_MAGNITUDE, data);
iforce_dump_packet("magnitude: ", FF_CMD_MAGNITUDE, data);
return 0;
}
/*
* Upload the component of an effect dealing with the period, phase and magnitude
*/
static int make_period_modifier(struct iforce* iforce,
struct resource* mod_chunk, int no_alloc,
__s16 magnitude, __s16 offset, u16 period, u16 phase)
{
unsigned char data[7];
period = TIME_SCALE(period);
if (!no_alloc) {
down(&iforce->mem_mutex);
if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c,
iforce->device_memory.start, iforce->device_memory.end, 2L,
NULL, NULL)) {
up(&iforce->mem_mutex);
return -ENOMEM;
}
up(&iforce->mem_mutex);
}
data[0] = LO(mod_chunk->start);
data[1] = HI(mod_chunk->start);
data[2] = HIFIX80(magnitude);
data[3] = HIFIX80(offset);
data[4] = HI(phase);
data[5] = LO(period);
data[6] = HI(period);
iforce_send_packet(iforce, FF_CMD_PERIOD, data);
return 0;
}
/*
* Uploads the part of an effect setting the shape of the force
*/
static int make_shape_modifier(struct iforce* iforce,
struct resource* mod_chunk, int no_alloc,
u16 attack_duration, __s16 initial_level,
u16 fade_duration, __s16 final_level)
{
unsigned char data[8];
attack_duration = TIME_SCALE(attack_duration);
fade_duration = TIME_SCALE(fade_duration);
if (!no_alloc) {
down(&iforce->mem_mutex);
if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e,
iforce->device_memory.start, iforce->device_memory.end, 2L,
NULL, NULL)) {
up(&iforce->mem_mutex);
return -ENOMEM;
}
up(&iforce->mem_mutex);
}
data[0] = LO(mod_chunk->start);
data[1] = HI(mod_chunk->start);
data[2] = LO(attack_duration);
data[3] = HI(attack_duration);
data[4] = HI(initial_level);
data[5] = LO(fade_duration);
data[6] = HI(fade_duration);
data[7] = HI(final_level);
iforce_send_packet(iforce, FF_CMD_SHAPE, data);
return 0;
}
/*
* Component of spring, friction, inertia... effects
*/
static int make_interactive_modifier(struct iforce* iforce,
struct resource* mod_chunk, int no_alloc,
__u16 rsat, __u16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center)
{
unsigned char data[10];
if (!no_alloc) {
down(&iforce->mem_mutex);
if (allocate_resource(&(iforce->device_memory), mod_chunk, 8,
iforce->device_memory.start, iforce->device_memory.end, 2L,
NULL, NULL)) {
up(&iforce->mem_mutex);
return -ENOMEM;
}
up(&iforce->mem_mutex);
}
data[0] = LO(mod_chunk->start);
data[1] = HI(mod_chunk->start);
data[2] = (100*rk)>>15; /* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */
data[3] = (100*lk)>>15; /* This code is incorrect on cpus lacking arith shift */
center = (500*center)>>15;
data[4] = LO(center);
data[5] = HI(center);
db = (1000*db)>>16;
data[6] = LO(db);
data[7] = HI(db);
data[8] = (100*rsat)>>16;
data[9] = (100*lsat)>>16;
iforce_send_packet(iforce, FF_CMD_INTERACT, data);
iforce_dump_packet("interactive", FF_CMD_INTERACT, data);
return 0;
}
static unsigned char find_button(struct iforce *iforce, signed short button)
{
int i;
for (i = 1; iforce->type->btn[i] >= 0; i++)
if (iforce->type->btn[i] == button)
return i + 1;
return 0;
}
/*
* Analyse the changes in an effect, and tell if we need to send an interactive
* parameter packet
*/
static int need_interactive_modifier(struct iforce* iforce, struct ff_effect* new)
{
int id = new->id;
struct ff_effect* old = &iforce->core_effects[id].effect;
if (new->type != FF_SPRING && new->type != FF_FRICTION) {
printk(KERN_WARNING "iforce.c: bad effect type in need_interactive_modifier\n");
return FALSE;
}
return (old->u.interactive.right_saturation != new->u.interactive.right_saturation
|| old->u.interactive.left_saturation != new->u.interactive.left_saturation
|| old->u.interactive.right_coeff != new->u.interactive.right_coeff
|| old->u.interactive.left_coeff != new->u.interactive.left_coeff
|| old->u.interactive.deadband != new->u.interactive.deadband
|| old->u.interactive.center != new->u.interactive.center);
}
/*
* Analyse the changes in an effect, and tell if we need to send a magnitude
* parameter packet
*/
static int need_magnitude_modifier(struct iforce* iforce, struct ff_effect* effect)
{
int id = effect->id;
struct ff_effect* old = &iforce->core_effects[id].effect;
if (effect->type != FF_CONSTANT) {
printk(KERN_WARNING "iforce.c: bad effect type in need_shape_modifier\n");
return FALSE;
}
return (old->u.constant.level != effect->u.constant.level);
}
/*
* Analyse the changes in an effect, and tell if we need to send a shape
* parameter packet
*/
static int need_shape_modifier(struct iforce* iforce, struct ff_effect* effect)
{
int id = effect->id;
struct ff_effect* old = &iforce->core_effects[id].effect;
switch (effect->type) {
case FF_CONSTANT:
if (old->u.constant.shape.attack_length != effect->u.constant.shape.attack_length
|| old->u.constant.shape.attack_level != effect->u.constant.shape.attack_level
|| old->u.constant.shape.fade_length != effect->u.constant.shape.fade_length
|| old->u.constant.shape.fade_level != effect->u.constant.shape.fade_level)
return TRUE;
break;
case FF_PERIODIC:
if (old->u.periodic.shape.attack_length != effect->u.periodic.shape.attack_length
|| old->u.periodic.shape.attack_level != effect->u.periodic.shape.attack_level
|| old->u.periodic.shape.fade_length != effect->u.periodic.shape.fade_length
|| old->u.periodic.shape.fade_level != effect->u.periodic.shape.fade_level)
return TRUE;
break;
default:
printk(KERN_WARNING "iforce.c: bad effect type in need_shape_modifier\n");
}
return FALSE;
}
/*
* Analyse the changes in an effect, and tell if we need to send a periodic
* parameter effect
*/
static int need_period_modifier(struct iforce* iforce, struct ff_effect* new)
{
int id = new->id;
struct ff_effect* old = &iforce->core_effects[id].effect;
if (new->type != FF_PERIODIC) {
printk(KERN_WARNING "iforce.c: bad effect type in need_periodic_modifier\n");
return FALSE;
}
return (old->u.periodic.period != new->u.periodic.period
|| old->u.periodic.magnitude != new->u.periodic.magnitude
|| old->u.periodic.offset != new->u.periodic.offset
|| old->u.periodic.phase != new->u.periodic.phase);
}
/*
* Analyse the changes in an effect, and tell if we need to send an effect
* packet
*/
static int need_core(struct iforce* iforce, struct ff_effect* new)
{
int id = new->id;
struct ff_effect* old = &iforce->core_effects[id].effect;
if (old->direction != new->direction
|| old->trigger.button != new->trigger.button
|| old->trigger.interval != new->trigger.interval
|| old->replay.length != new->replay.length
|| old->replay.delay != new->replay.delay)
return TRUE;
return FALSE;
}
/*
* Send the part common to all effects to the device
*/
static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2,
u8 effect_type, u8 axes, u16 duration, u16 delay, u16 button,
u16 interval, u16 direction)
{
unsigned char data[14];
duration = TIME_SCALE(duration);
delay = TIME_SCALE(delay);
interval = TIME_SCALE(interval);
data[0] = LO(id);
data[1] = effect_type;
data[2] = LO(axes) | find_button(iforce, button);
data[3] = LO(duration);
data[4] = HI(duration);
data[5] = HI(direction);
data[6] = LO(interval);
data[7] = HI(interval);
data[8] = LO(mod_id1);
data[9] = HI(mod_id1);
data[10] = LO(mod_id2);
data[11] = HI(mod_id2);
data[12] = LO(delay);
data[13] = HI(delay);
/* Stop effect */
/* iforce_control_playback(iforce, id, 0);*/
iforce_send_packet(iforce, FF_CMD_EFFECT, data);
/* If needed, restart effect */
if (test_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[id].flags)) {
/* BUG: perhaps we should replay n times, instead of 1. But we do not know n */
iforce_control_playback(iforce, id, 1);
}
return 0;
}
/*
* Upload a periodic effect to the device
* See also iforce_upload_constant.
*/
int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int is_update)
{
u8 wave_code;
int core_id = effect->id;
struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
int param1_err = 1;
int param2_err = 1;
int core_err = 0;
if (!is_update || need_period_modifier(iforce, effect)) {
param1_err = make_period_modifier(iforce, mod1_chunk,
is_update,
effect->u.periodic.magnitude, effect->u.periodic.offset,
effect->u.periodic.period, effect->u.periodic.phase);
if (param1_err) return param1_err;
set_bit(FF_MOD1_IS_USED, core_effect->flags);
}
if (!is_update || need_shape_modifier(iforce, effect)) {
param2_err = make_shape_modifier(iforce, mod2_chunk,
is_update,
effect->u.periodic.shape.attack_length,
effect->u.periodic.shape.attack_level,
effect->u.periodic.shape.fade_length,
effect->u.periodic.shape.fade_level);
if (param2_err) return param2_err;
set_bit(FF_MOD2_IS_USED, core_effect->flags);
}
switch (effect->u.periodic.waveform) {
case FF_SQUARE: wave_code = 0x20; break;
case FF_TRIANGLE: wave_code = 0x21; break;
case FF_SINE: wave_code = 0x22; break;
case FF_SAW_UP: wave_code = 0x23; break;
case FF_SAW_DOWN: wave_code = 0x24; break;
default: wave_code = 0x20; break;
}
if (!is_update || need_core(iforce, effect)) {
core_err = make_core(iforce, effect->id,
mod1_chunk->start,
mod2_chunk->start,
wave_code,
0x20,
effect->replay.length,
effect->replay.delay,
effect->trigger.button,
effect->trigger.interval,
effect->direction);
}
/* If one of the parameter creation failed, we already returned an
* error code.
* If the core creation failed, we return its error code.
* Else: if one parameter at least was created, we return 0
* else we return 1;
*/
return core_err < 0 ? core_err : (param1_err && param2_err);
}
/*
* Upload a constant force effect
* Return value:
* <0 Error code
* 0 Ok, effect created or updated
* 1 effect did not change since last upload, and no packet was therefore sent
*/
int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect, int is_update)
{
int core_id = effect->id;
struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
int param1_err = 1;
int param2_err = 1;
int core_err = 0;
if (!is_update || need_magnitude_modifier(iforce, effect)) {
param1_err = make_magnitude_modifier(iforce, mod1_chunk,
is_update,
effect->u.constant.level);
if (param1_err) return param1_err;
set_bit(FF_MOD1_IS_USED, core_effect->flags);
}
if (!is_update || need_shape_modifier(iforce, effect)) {
param2_err = make_shape_modifier(iforce, mod2_chunk,
is_update,
effect->u.constant.shape.attack_length,
effect->u.constant.shape.attack_level,
effect->u.constant.shape.fade_length,
effect->u.constant.shape.fade_level);
if (param2_err) return param2_err;
set_bit(FF_MOD2_IS_USED, core_effect->flags);
}
if (!is_update || need_core(iforce, effect)) {
core_err = make_core(iforce, effect->id,
mod1_chunk->start,
mod2_chunk->start,
0x00,
0x20,
effect->replay.length,
effect->replay.delay,
effect->trigger.button,
effect->trigger.interval,
effect->direction);
}
/* If one of the parameter creation failed, we already returned an
* error code.
* If the core creation failed, we return its error code.
* Else: if one parameter at least was created, we return 0
* else we return 1;
*/
return core_err < 0 ? core_err : (param1_err && param2_err);
}
/*
* Upload an interactive effect. Those are for example friction, inertia, springs...
*/
int iforce_upload_interactive(struct iforce* iforce, struct ff_effect* effect, int is_update)
{
int core_id = effect->id;
struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
struct resource* mod_chunk = &(core_effect->mod1_chunk);
u8 type, axes;
u16 mod1, mod2, direction;
int param_err = 1;
int core_err = 0;
switch (effect->type) {
case FF_SPRING: type = 0x40; break;
case FF_FRICTION: type = 0x41; break;
default: return -1;
}
if (!is_update || need_interactive_modifier(iforce, effect)) {
param_err = make_interactive_modifier(iforce, mod_chunk,
is_update,
effect->u.interactive.right_saturation,
effect->u.interactive.left_saturation,
effect->u.interactive.right_coeff,
effect->u.interactive.left_coeff,
effect->u.interactive.deadband,
effect->u.interactive.center);
if (param_err) return param_err;
set_bit(FF_MOD1_IS_USED, core_effect->flags);
}
switch ((test_bit(ABS_X, &effect->u.interactive.axis) ||
test_bit(ABS_WHEEL, &effect->u.interactive.axis)) |
(!!test_bit(ABS_Y, &effect->u.interactive.axis) << 1)) {
case 0: /* Only one axis, choose orientation */
mod1 = mod_chunk->start;
mod2 = 0xffff;
direction = effect->direction;
axes = 0x20;
break;
case 1: /* Only X axis */
mod1 = mod_chunk->start;
mod2 = 0xffff;
direction = 0x5a00;
axes = 0x40;
break;
case 2: /* Only Y axis */
mod1 = 0xffff;
mod2 = mod_chunk->start;
direction = 0xb400;
axes = 0x80;
break;
case 3: /* Both X and Y axes */
/* TODO: same setting for both axes is not mandatory */
mod1 = mod_chunk->start;
mod2 = mod_chunk->start;
direction = 0x6000;
axes = 0xc0;
break;
default:
return -1;
}
if (!is_update || need_core(iforce, effect)) {
core_err = make_core(iforce, effect->id,
mod1, mod2,
type, axes,
effect->replay.length, effect->replay.delay,
effect->trigger.button, effect->trigger.interval,
direction);
}
/* If the parameter creation failed, we already returned an
* error code.
* If the core creation failed, we return its error code.
* Else: if a parameter was created, we return 0
* else we return 1;
*/
return core_err < 0 ? core_err : param_err;
}
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:32:12
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/joystick
In directory usw-pr-cvs1:/tmp/cvs-serv6791
Added Files:
Tag: 1.14
warrior.c
Log Message:
Moved.
--- NEW FILE: warrior.c ---
/*
* $Id: warrior.c,v 1.14 2002/01/22 20:32:10 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* Logitech WingMan Warrior joystick driver for Linux
*/
/*
* This program is free warftware; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>");
MODULE_DESCRIPTION("Logitech WingMan Warrior joystick driver");
MODULE_LICENSE("GPL");
/*
* Constants.
*/
#define WARRIOR_MAX_LENGTH 16
static char warrior_lengths[] = { 0, 4, 12, 3, 4, 4, 0, 0 };
static char *warrior_name = "Logitech WingMan Warrior";
/*
* Per-Warrior data.
*/
struct warrior {
struct input_dev dev;
int idx, len;
unsigned char data[WARRIOR_MAX_LENGTH];
char phys[32];
};
/*
* warrior_process_packet() decodes packets the driver receives from the
* Warrior. It updates the data accordingly.
*/
static void warrior_process_packet(struct warrior *warrior)
{
struct input_dev *dev = &warrior->dev;
unsigned char *data = warrior->data;
if (!warrior->idx) return;
switch ((data[0] >> 4) & 7) {
case 1: /* Button data */
input_report_key(dev, BTN_TRIGGER, data[3] & 1);
input_report_key(dev, BTN_THUMB, (data[3] >> 1) & 1);
input_report_key(dev, BTN_TOP, (data[3] >> 2) & 1);
input_report_key(dev, BTN_TOP2, (data[3] >> 3) & 1);
return;
case 3: /* XY-axis info->data */
input_report_abs(dev, ABS_X, ((data[0] & 8) << 5) - (data[2] | ((data[0] & 4) << 5)));
input_report_abs(dev, ABS_Y, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7));
return;
case 5: /* Throttle, spinner, hat info->data */
input_report_abs(dev, ABS_THROTTLE, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7));
input_report_abs(dev, ABS_HAT0X, (data[3] & 2 ? 1 : 0) - (data[3] & 1 ? 1 : 0));
input_report_abs(dev, ABS_HAT0Y, (data[3] & 8 ? 1 : 0) - (data[3] & 4 ? 1 : 0));
input_report_rel(dev, REL_DIAL, (data[2] | ((data[0] & 4) << 5)) - ((data[0] & 8) << 5));
return;
}
}
/*
* warrior_interrupt() is called by the low level driver when characters
* are ready for us. We then buffer them for further processing, or call the
* packet processing routine.
*/
static void warrior_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
{
struct warrior* warrior = serio->private;
if (data & 0x80) {
if (warrior->idx) warrior_process_packet(warrior);
warrior->idx = 0;
warrior->len = warrior_lengths[(data >> 4) & 7];
}
if (warrior->idx < warrior->len)
warrior->data[warrior->idx++] = data;
if (warrior->idx == warrior->len) {
if (warrior->idx) warrior_process_packet(warrior);
warrior->idx = 0;
warrior->len = 0;
}
}
/*
* warrior_disconnect() is the opposite of warrior_connect()
*/
static void warrior_disconnect(struct serio *serio)
{
struct warrior* warrior = serio->private;
input_unregister_device(&warrior->dev);
serio_close(serio);
kfree(warrior);
}
/*
* warrior_connect() is the routine that is called when someone adds a
* new serio device. It looks for the Warrior, and if found, registers
* it as an input device.
*/
static void warrior_connect(struct serio *serio, struct serio_dev *dev)
{
struct warrior *warrior;
int i;
if (serio->type != (SERIO_RS232 | SERIO_WARRIOR))
return;
if (!(warrior = kmalloc(sizeof(struct warrior), GFP_KERNEL)))
return;
memset(warrior, 0, sizeof(struct warrior));
warrior->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
warrior->dev.keybit[LONG(BTN_TRIGGER)] = BIT(BTN_TRIGGER) | BIT(BTN_THUMB) | BIT(BTN_TOP) | BIT(BTN_TOP2);
warrior->dev.relbit[0] = BIT(REL_DIAL);
warrior->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y);
sprintf(warrior->phys, "%s/input0", serio->phys);
warrior->dev.name = warrior_name;
warrior->dev.phys = warrior->phys;
warrior->dev.idbus = BUS_RS232;
warrior->dev.idvendor = SERIO_WARRIOR;
warrior->dev.idproduct = 0x0001;
warrior->dev.idversion = 0x0100;
for (i = 0; i < 2; i++) {
warrior->dev.absmax[ABS_X+i] = -64;
warrior->dev.absmin[ABS_X+i] = 64;
warrior->dev.absflat[ABS_X+i] = 8;
}
warrior->dev.absmax[ABS_THROTTLE] = -112;
warrior->dev.absmin[ABS_THROTTLE] = 112;
for (i = 0; i < 2; i++) {
warrior->dev.absmax[ABS_HAT0X+i] = -1;
warrior->dev.absmin[ABS_HAT0X+i] = 1;
}
warrior->dev.private = warrior;
serio->private = warrior;
if (serio_open(serio, dev)) {
kfree(warrior);
return;
}
input_register_device(&warrior->dev);
printk(KERN_INFO "input: Logitech WingMan Warrior on %s\n", serio->phys);
}
/*
* The serio device structure.
*/
static struct serio_dev warrior_dev = {
interrupt: warrior_interrupt,
connect: warrior_connect,
disconnect: warrior_disconnect,
};
/*
* The functions for inserting/removing us as a module.
*/
int __init warrior_init(void)
{
serio_register_device(&warrior_dev);
return 0;
}
void __exit warrior_exit(void)
{
serio_unregister_device(&warrior_dev);
}
module_init(warrior_init);
module_exit(warrior_exit);
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:31:56
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/joystick
In directory usw-pr-cvs1:/tmp/cvs-serv6622
Added Files:
Tag: 1.5
twidjoy.c
Log Message:
Moved.
--- NEW FILE: twidjoy.c ---
/*
* $Id: twidjoy.c,v 1.5 2002/01/22 20:31:53 vojtech Exp $
*
* derived from CVS-ID "stinger.c,v 1.5 2001/05/29 12:57:18 vojtech Exp"
*
* Copyright (c) 2001 Arndt Schoenewald
* Copyright (c) 2000-2001 Vojtech Pavlik
* Copyright (c) 2000 Mark Fletcher
*
* Sponsored by Quelltext AG (http://www.quelltext-ag.de), Dortmund, Germany
*/
/*
* Driver to use Handykey's Twiddler (the first edition, i.e. the one with
* the RS232 interface) as a joystick under Linux
*
* The Twiddler is a one-handed chording keyboard featuring twelve buttons on
* the front, six buttons on the top, and a built-in tilt sensor. The buttons
* on the front, which are grouped as four rows of three buttons, are pressed
* by the four fingers (this implies only one button per row can be held down
* at the same time) and the buttons on the top are for the thumb. The tilt
* sensor delivers X and Y axis data depending on how the Twiddler is held.
* Additional information can be found at http://www.handykey.com.
*
* This driver does not use the Twiddler for its intended purpose, i.e. as
* a chording keyboard, but as a joystick: pressing and releasing a button
* immediately sends a corresponding button event, and tilting it generates
* corresponding ABS_X and ABS_Y events. This turns the Twiddler into a game
* controller with amazing 18 buttons :-)
*
* Note: The Twiddler2 (the successor of the Twiddler that connects directly
* to the PS/2 keyboard and mouse ports) is NOT supported by this driver!
*
* For questions or feedback regarding this driver module please contact:
* Arndt Schoenewald <ar...@qu...>
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
/*
* Constants.
*/
#define TWIDJOY_MAX_LENGTH 5
static char *twidjoy_name = "Handykey Twiddler";
static struct twidjoy_button_spec {
int bitshift;
int bitmask;
int buttons[3];
}
twidjoy_buttons[] = {
{ 0, 3, { BTN_A, BTN_B, BTN_C } },
{ 2, 3, { BTN_X, BTN_Y, BTN_Z } },
{ 4, 3, { BTN_TL, BTN_TR, BTN_TR2 } },
{ 6, 3, { BTN_SELECT, BTN_START, BTN_MODE } },
{ 8, 1, { BTN_BASE5 } },
{ 9, 1, { BTN_BASE } },
{ 10, 1, { BTN_BASE3 } },
{ 11, 1, { BTN_BASE4 } },
{ 12, 1, { BTN_BASE2 } },
{ 13, 1, { BTN_BASE6 } },
{ 0, 0, { 0 } }
};
/*
* Per-Twiddler data.
*/
struct twidjoy {
struct input_dev dev;
int idx;
unsigned char data[TWIDJOY_MAX_LENGTH];
char phys[32];
};
/*
* twidjoy_process_packet() decodes packets the driver receives from the
* Twiddler. It updates the data accordingly.
*/
static void twidjoy_process_packet(struct twidjoy *twidjoy)
{
if (twidjoy->idx == TWIDJOY_MAX_LENGTH) {
struct input_dev *dev = &twidjoy->dev;
unsigned char *data = twidjoy->data;
struct twidjoy_button_spec *bp;
int button_bits, abs_x, abs_y;
button_bits = ((data[1] & 0x7f) << 7) | (data[0] & 0x7f);
for (bp = twidjoy_buttons; bp->bitmask; bp++) {
int value = (button_bits & (bp->bitmask << bp->bitshift)) >> bp->bitshift;
int i;
for (i = 0; i < bp->bitmask; i++)
input_report_key(dev, bp->buttons[i], i+1 == value);
}
abs_x = ((data[4] & 0x07) << 5) | ((data[3] & 0x7C) >> 2);
if (data[4] & 0x08) abs_x -= 256;
abs_y = ((data[3] & 0x01) << 7) | ((data[2] & 0x7F) >> 0);
if (data[3] & 0x02) abs_y -= 256;
input_report_abs(dev, ABS_X, -abs_x);
input_report_abs(dev, ABS_Y, +abs_y);
}
return;
}
/*
* twidjoy_interrupt() is called by the low level driver when characters
* are ready for us. We then buffer them for further processing, or call the
* packet processing routine.
*/
static void twidjoy_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
{
struct twidjoy *twidjoy = serio->private;
/* All Twiddler packets are 5 bytes. The fact that the first byte
* has a MSB of 0 and all other bytes have a MSB of 1 can be used
* to check and regain sync. */
if ((data & 0x80) == 0)
twidjoy->idx = 0; /* this byte starts a new packet */
else if (twidjoy->idx == 0)
return; /* wrong MSB -- ignore this byte */
if (twidjoy->idx < TWIDJOY_MAX_LENGTH)
twidjoy->data[twidjoy->idx++] = data;
if (twidjoy->idx == TWIDJOY_MAX_LENGTH) {
twidjoy_process_packet(twidjoy);
twidjoy->idx = 0;
}
return;
}
/*
* twidjoy_disconnect() is the opposite of twidjoy_connect()
*/
static void twidjoy_disconnect(struct serio *serio)
{
struct twidjoy *twidjoy = serio->private;
input_unregister_device(&twidjoy->dev);
serio_close(serio);
kfree(twidjoy);
}
/*
* twidjoy_connect() is the routine that is called when someone adds a
* new serio device. It looks for the Twiddler, and if found, registers
* it as an input device.
*/
static void twidjoy_connect(struct serio *serio, struct serio_dev *dev)
{
struct twidjoy_button_spec *bp;
struct twidjoy *twidjoy;
int i;
if (serio->type != (SERIO_RS232 | SERIO_TWIDJOY))
return;
if (!(twidjoy = kmalloc(sizeof(struct twidjoy), GFP_KERNEL)))
return;
memset(twidjoy, 0, sizeof(struct twidjoy));
sprintf(twidjoy->phys, "%s/input0", serio->phys);
twidjoy->dev.name = twidjoy_name;
twidjoy->dev.phys = twidjoy->phys;
twidjoy->dev.idbus = BUS_RS232;
twidjoy->dev.idvendor = SERIO_TWIDJOY;
twidjoy->dev.idproduct = 0x0001;
twidjoy->dev.idversion = 0x0100;
twidjoy->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (bp = twidjoy_buttons; bp->bitmask; bp++) {
for (i = 0; i < bp->bitmask; i++)
set_bit(bp->buttons[i], twidjoy->dev.keybit);
}
twidjoy->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
for (i = 0; i < 2; i++) {
twidjoy->dev.absmax[ABS_X+i] = 50;
twidjoy->dev.absmin[ABS_X+i] = -50;
/* TODO: arndt 20010708: Are these values appropriate? */
twidjoy->dev.absfuzz[ABS_X+i] = 4;
twidjoy->dev.absflat[ABS_X+i] = 4;
}
twidjoy->dev.private = twidjoy;
serio->private = twidjoy;
if (serio_open(serio, dev)) {
kfree(twidjoy);
return;
}
input_register_device(&twidjoy->dev);
printk(KERN_INFO "input: %s on %s\n", twidjoy_name, serio->phys);
}
/*
* The serio device structure.
*/
static struct serio_dev twidjoy_dev = {
interrupt: twidjoy_interrupt,
connect: twidjoy_connect,
disconnect: twidjoy_disconnect,
};
/*
* The functions for inserting/removing us as a module.
*/
int __init twidjoy_init(void)
{
serio_register_device(&twidjoy_dev);
return 0;
}
void __exit twidjoy_exit(void)
{
serio_unregister_device(&twidjoy_dev);
}
module_init(twidjoy_init);
module_exit(twidjoy_exit);
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:30:43
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/joystick
In directory usw-pr-cvs1:/tmp/cvs-serv6018
Added Files:
Tag: 1.14
turbografx.c
Log Message:
Moved.
--- NEW FILE: turbografx.c ---
/*
* $Id: turbografx.c,v 1.14 2002/01/22 20:30:39 vojtech Exp $
*
* Copyright (c) 1998-2001 Vojtech Pavlik
*
* Based on the work of:
* Steffen Schwenke
*/
/*
* TurboGraFX parallel port interface driver for Linux.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/parport.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>");
MODULE_DESCRIPTION("TurboGraFX parallel port interface driver");
MODULE_LICENSE("GPL");
MODULE_PARM(tgfx, "2-8i");
MODULE_PARM(tgfx_2, "2-8i");
MODULE_PARM(tgfx_3, "2-8i");
#define TGFX_REFRESH_TIME HZ/100 /* 10 ms */
#define TGFX_TRIGGER 0x08
#define TGFX_UP 0x10
#define TGFX_DOWN 0x20
#define TGFX_LEFT 0x40
#define TGFX_RIGHT 0x80
#define TGFX_THUMB 0x02
#define TGFX_THUMB2 0x04
#define TGFX_TOP 0x01
#define TGFX_TOP2 0x08
static int tgfx[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 };
static int tgfx_2[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 };
static int tgfx_3[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 };
static int tgfx_buttons[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2 };
static char *tgfx_name = "TurboGraFX Multisystem joystick";
struct tgfx {
struct pardevice *pd;
struct timer_list timer;
struct input_dev dev[7];
char phys[7][32];
int sticks;
int used;
} *tgfx_base[3];
/*
* tgfx_timer() reads and analyzes TurboGraFX joystick data.
*/
static void tgfx_timer(unsigned long private)
{
struct tgfx *tgfx = (void *) private;
struct input_dev *dev;
int data1, data2, i;
for (i = 0; i < 7; i++)
if (tgfx->sticks & (1 << i)) {
dev = tgfx->dev + i;
parport_write_data(tgfx->pd->port, ~(1 << i));
data1 = parport_read_status(tgfx->pd->port) ^ 0x7f;
data2 = parport_read_control(tgfx->pd->port) ^ 0x04; /* CAVEAT parport */
input_report_abs(dev, ABS_X, !!(data1 & TGFX_RIGHT) - !!(data1 & TGFX_LEFT));
input_report_abs(dev, ABS_Y, !!(data1 & TGFX_DOWN ) - !!(data1 & TGFX_UP ));
input_report_key(dev, BTN_TRIGGER, (data1 & TGFX_TRIGGER));
input_report_key(dev, BTN_THUMB, (data2 & TGFX_THUMB ));
input_report_key(dev, BTN_THUMB2, (data2 & TGFX_THUMB2 ));
input_report_key(dev, BTN_TOP, (data2 & TGFX_TOP ));
input_report_key(dev, BTN_TOP2, (data2 & TGFX_TOP2 ));
}
mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME);
}
static int tgfx_open(struct input_dev *dev)
{
struct tgfx *tgfx = dev->private;
if (!tgfx->used++) {
parport_claim(tgfx->pd);
parport_write_control(tgfx->pd->port, 0x04);
mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME);
}
return 0;
}
static void tgfx_close(struct input_dev *dev)
{
struct tgfx *tgfx = dev->private;
if (!--tgfx->used) {
del_timer(&tgfx->timer);
parport_write_control(tgfx->pd->port, 0x00);
parport_release(tgfx->pd);
}
}
/*
* tgfx_probe() probes for tg gamepads.
*/
static struct tgfx __init *tgfx_probe(int *config)
{
struct tgfx *tgfx;
struct parport *pp;
int i, j;
if (config[0] < 0)
return NULL;
for (pp = parport_enumerate(); pp && (config[0] > 0); pp = pp->next)
config[0]--;
if (!pp) {
printk(KERN_ERR "turbografx.c: no such parport\n");
return NULL;
}
if (!(tgfx = kmalloc(sizeof(struct tgfx), GFP_KERNEL)))
return NULL;
memset(tgfx, 0, sizeof(struct tgfx));
tgfx->pd = parport_register_device(pp, "turbografx", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
if (!tgfx->pd) {
printk(KERN_ERR "turbografx.c: parport busy already - lp.o loaded?\n");
kfree(tgfx);
return NULL;
}
init_timer(&tgfx->timer);
tgfx->timer.data = (long) tgfx;
tgfx->timer.function = tgfx_timer;
tgfx->sticks = 0;
for (i = 0; i < 7; i++)
if (config[i+1] > 0 && config[i+1] < 6) {
tgfx->sticks |= (1 << i);
tgfx->dev[i].private = tgfx;
tgfx->dev[i].open = tgfx_open;
tgfx->dev[i].close = tgfx_close;
sprintf(tgfx->phys[i], "%s/input0", tgfx->pd->port->name);
tgfx->dev[i].name = tgfx_name;
tgfx->dev[i].phys = tgfx->phys[i];
tgfx->dev[i].idbus = BUS_PARPORT;
tgfx->dev[i].idvendor = 0x0003;
tgfx->dev[i].idproduct = config[i+1];
tgfx->dev[i].idversion = 0x0100;
tgfx->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
tgfx->dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
for (j = 0; j < config[i+1]; j++)
set_bit(tgfx_buttons[j], tgfx->dev[i].keybit);
tgfx->dev[i].absmin[ABS_X] = -1; tgfx->dev[i].absmax[ABS_X] = 1;
tgfx->dev[i].absmin[ABS_Y] = -1; tgfx->dev[i].absmax[ABS_Y] = 1;
input_register_device(tgfx->dev + i);
printk(KERN_INFO "input: %d-button Multisystem joystick on %s\n",
config[i+1], tgfx->pd->port->name);
}
if (!tgfx->sticks) {
parport_unregister_device(tgfx->pd);
kfree(tgfx);
return NULL;
}
return tgfx;
}
#ifndef MODULE
int __init tgfx_setup(char *str)
{
int i, ints[9];
get_options(str, ARRAY_SIZE(ints), ints);
for (i = 0; i <= ints[0] && i < 8; i++) tgfx[i] = ints[i + 1];
return 1;
}
int __init tgfx_setup_2(char *str)
{
int i, ints[9];
get_options(str, ARRAY_SIZE(ints), ints);
for (i = 0; i <= ints[0] && i < 8; i++) tgfx_2[i] = ints[i + 1];
return 1;
}
int __init tgfx_setup_3(char *str)
{
int i, ints[9];
get_options(str, ARRAY_SIZE(ints), ints);
for (i = 0; i <= ints[0] && i < 8; i++) tgfx_3[i] = ints[i + 1];
return 1;
}
__setup("tgfx=", tgfx_setup);
__setup("tgfx_2=", tgfx_setup_2);
__setup("tgfx_3=", tgfx_setup_3);
#endif
int __init tgfx_init(void)
{
tgfx_base[0] = tgfx_probe(tgfx);
tgfx_base[1] = tgfx_probe(tgfx_2);
tgfx_base[2] = tgfx_probe(tgfx_3);
if (tgfx_base[0] || tgfx_base[1] || tgfx_base[2])
return 0;
return -ENODEV;
}
void __exit tgfx_exit(void)
{
int i, j;
for (i = 0; i < 3; i++)
if (tgfx_base[i]) {
for (j = 0; j < 7; j++)
if (tgfx_base[i]->sticks & (1 << j))
input_unregister_device(tgfx_base[i]->dev + j);
parport_unregister_device(tgfx_base[i]->pd);
}
}
module_init(tgfx_init);
module_exit(tgfx_exit);
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:29:54
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/joystick
In directory usw-pr-cvs1:/tmp/cvs-serv5801
Added Files:
Tag: 1.31
tmdc.c
Log Message:
Moved.
--- NEW FILE: tmdc.c ---
/*
* $Id: tmdc.c,v 1.31 2002/01/22 20:29:52 vojtech Exp $
*
* Copyright (c) 1998-2001 Vojtech Pavlik
*
* Based on the work of:
* Trystan Larey-Williams
*/
/*
* ThrustMaster DirectConnect (BSP) joystick family driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gameport.h>
#include <linux/input.h>
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>");
MODULE_DESCRIPTION("ThrustMaster DirectConnect joystick driver");
MODULE_LICENSE("GPL");
#define TMDC_MAX_START 400 /* 400 us */
#define TMDC_MAX_STROBE 45 /* 45 us */
#define TMDC_MAX_LENGTH 13
#define TMDC_REFRESH_TIME HZ/50 /* 20 ms */
#define TMDC_MODE_M3DI 1
#define TMDC_MODE_3DRP 3
#define TMDC_MODE_AT 4
#define TMDC_MODE_FM 8
#define TMDC_MODE_FGP 163
#define TMDC_BYTE_ID 10
#define TMDC_BYTE_REV 11
#define TMDC_BYTE_DEF 12
#define TMDC_ABS 7
#define TMDC_ABS_HAT 4
#define TMDC_BTN 16
static unsigned char tmdc_byte_a[16] = { 0, 1, 3, 4, 6, 7 };
static unsigned char tmdc_byte_d[16] = { 2, 5, 8, 9 };
static signed char tmdc_abs[TMDC_ABS] =
{ ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE, ABS_RX, ABS_RY, ABS_RZ };
static signed char tmdc_abs_hat[TMDC_ABS_HAT] =
{ ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y };
static signed char tmdc_abs_at[TMDC_ABS] =
{ ABS_X, ABS_Y, ABS_RUDDER, -1, ABS_THROTTLE };
static signed char tmdc_abs_fm[TMDC_ABS] =
{ ABS_RX, ABS_RY, ABS_X, ABS_Y };
static short tmdc_btn_pad[TMDC_BTN] =
{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_START, BTN_SELECT, BTN_TL, BTN_TR };
static short tmdc_btn_joy[TMDC_BTN] =
{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_THUMB2, BTN_PINKIE,
BTN_BASE3, BTN_BASE4, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z };
static short tmdc_btn_fm[TMDC_BTN] =
{ BTN_TRIGGER, BTN_C, BTN_B, BTN_A, BTN_THUMB, BTN_X, BTN_Y, BTN_Z, BTN_TOP, BTN_TOP2 };
static short tmdc_btn_at[TMDC_BTN] =
{ BTN_TRIGGER, BTN_THUMB2, BTN_PINKIE, BTN_THUMB, BTN_BASE6, BTN_BASE5, BTN_BASE4,
BTN_BASE3, BTN_BASE2, BTN_BASE };
static struct {
int x;
int y;
} tmdc_hat_to_axis[] = {{ 0, 0}, { 1, 0}, { 0,-1}, {-1, 0}, { 0, 1}};
struct tmdc {
struct gameport *gameport;
struct timer_list timer;
struct input_dev dev[2];
char name[2][64];
char phys[2][32];
int mode[2];
signed char *abs[2];
short *btn[2];
unsigned char absc[2];
unsigned char btnc[2][4];
unsigned char btno[2][4];
int used;
int reads;
int bads;
unsigned char exists;
};
/*
* tmdc_read_packet() reads a ThrustMaster packet.
*/
static int tmdc_read_packet(struct gameport *gameport, unsigned char data[2][TMDC_MAX_LENGTH])
{
unsigned char u, v, w, x;
unsigned long flags;
int i[2], j[2], t[2], p, k;
p = gameport_time(gameport, TMDC_MAX_STROBE);
for (k = 0; k < 2; k++) {
t[k] = gameport_time(gameport, TMDC_MAX_START);
i[k] = j[k] = 0;
}
__save_flags(flags);
__cli();
gameport_trigger(gameport);
w = gameport_read(gameport) >> 4;
do {
x = w;
w = gameport_read(gameport) >> 4;
for (k = 0, v = w, u = x; k < 2; k++, v >>= 2, u >>= 2) {
if (~v & u & 2) {
if (t[k] <= 0 || i[k] >= TMDC_MAX_LENGTH) continue;
t[k] = p;
if (j[k] == 0) { /* Start bit */
if (~v & 1) t[k] = 0;
data[k][i[k]] = 0; j[k]++; continue;
}
if (j[k] == 9) { /* Stop bit */
if (v & 1) t[k] = 0;
j[k] = 0; i[k]++; continue;
}
data[k][i[k]] |= (~v & 1) << (j[k]++ - 1); /* Data bit */
}
t[k]--;
}
} while (t[0] > 0 || t[1] > 0);
__restore_flags(flags);
return (i[0] == TMDC_MAX_LENGTH) | ((i[1] == TMDC_MAX_LENGTH) << 1);
}
/*
* tmdc_read() reads and analyzes ThrustMaster joystick data.
*/
static void tmdc_timer(unsigned long private)
{
unsigned char data[2][TMDC_MAX_LENGTH];
struct tmdc *tmdc = (void *) private;
struct input_dev *dev;
unsigned char r, bad = 0;
int i, j, k, l;
tmdc->reads++;
if ((r = tmdc_read_packet(tmdc->gameport, data)) != tmdc->exists)
bad = 1;
else
for (j = 0; j < 2; j++)
if (r & (1 << j) & tmdc->exists) {
if (data[j][TMDC_BYTE_ID] != tmdc->mode[j]) {
bad = 1;
continue;
}
dev = tmdc->dev + j;
for (i = 0; i < tmdc->absc[j]; i++) {
if (tmdc->abs[j][i] < 0) continue;
input_report_abs(dev, tmdc->abs[j][i], data[j][tmdc_byte_a[i]]);
}
switch (tmdc->mode[j]) {
case TMDC_MODE_M3DI:
i = tmdc_byte_d[0];
input_report_abs(dev, ABS_HAT0X, ((data[j][i] >> 3) & 1) - ((data[j][i] >> 1) & 1));
input_report_abs(dev, ABS_HAT0Y, ((data[j][i] >> 2) & 1) - ( data[j][i] & 1));
break;
case TMDC_MODE_AT:
i = tmdc_byte_a[3];
input_report_abs(dev, ABS_HAT0X, tmdc_hat_to_axis[(data[j][i] - 141) / 25].x);
input_report_abs(dev, ABS_HAT0Y, tmdc_hat_to_axis[(data[j][i] - 141) / 25].y);
break;
}
for (k = l = 0; k < 4; k++) {
for (i = 0; i < tmdc->btnc[j][k]; i++)
input_report_key(dev, tmdc->btn[j][i + l],
((data[j][tmdc_byte_d[k]] >> (i + tmdc->btno[j][k])) & 1));
l += tmdc->btnc[j][k];
}
}
tmdc->bads += bad;
mod_timer(&tmdc->timer, jiffies + TMDC_REFRESH_TIME);
}
static int tmdc_open(struct input_dev *dev)
{
struct tmdc *tmdc = dev->private;
if (!tmdc->used++)
mod_timer(&tmdc->timer, jiffies + TMDC_REFRESH_TIME);
return 0;
}
static void tmdc_close(struct input_dev *dev)
{
struct tmdc *tmdc = dev->private;
if (!--tmdc->used)
del_timer(&tmdc->timer);
}
/*
* tmdc_probe() probes for ThrustMaster type joysticks.
*/
static void tmdc_connect(struct gameport *gameport, struct gameport_dev *dev)
{
struct models {
unsigned char id;
char *name;
char abs;
char hats;
char btnc[4];
char btno[4];
signed char *axes;
short *buttons;
} models[] = { { 1, "ThrustMaster Millenium 3D Inceptor", 6, 2, { 4, 2 }, { 4, 6 }, tmdc_abs, tmdc_btn_joy },
{ 3, "ThrustMaster Rage 3D Gamepad", 2, 0, { 8, 2 }, { 0, 0 }, tmdc_abs, tmdc_btn_pad },
{ 4, "ThrustMaster Attack Throttle", 5, 2, { 4, 6 }, { 4, 2 }, tmdc_abs_at, tmdc_btn_at },
{ 8, "ThrustMaster FragMaster", 4, 0, { 8, 2 }, { 0, 0 }, tmdc_abs_fm, tmdc_btn_fm },
{ 163, "Thrustmaster Fusion GamePad", 2, 0, { 8, 2 }, { 0, 0 }, tmdc_abs, tmdc_btn_pad },
{ 0, "Unknown %d-axis, %d-button TM device %d", 0, 0, { 0, 0 }, { 0, 0 }, tmdc_abs, tmdc_btn_joy }};
unsigned char data[2][TMDC_MAX_LENGTH];
struct tmdc *tmdc;
int i, j, k, l, m;
if (!(tmdc = kmalloc(sizeof(struct tmdc), GFP_KERNEL)))
return;
memset(tmdc, 0, sizeof(struct tmdc));
gameport->private = tmdc;
tmdc->gameport = gameport;
init_timer(&tmdc->timer);
tmdc->timer.data = (long) tmdc;
tmdc->timer.function = tmdc_timer;
if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW))
goto fail1;
if (!(tmdc->exists = tmdc_read_packet(gameport, data)))
goto fail2;
for (j = 0; j < 2; j++)
if (tmdc->exists & (1 << j)) {
tmdc->mode[j] = data[j][TMDC_BYTE_ID];
for (m = 0; models[m].id && models[m].id != tmdc->mode[j]; m++);
tmdc->abs[j] = models[m].axes;
tmdc->btn[j] = models[m].buttons;
if (!models[m].id) {
models[m].abs = data[j][TMDC_BYTE_DEF] >> 4;
for (k = 0; k < 4; k++)
models[m].btnc[k] = k < (data[j][TMDC_BYTE_DEF] & 0xf) ? 8 : 0;
}
tmdc->absc[j] = models[m].abs;
for (k = 0; k < 4; k++) {
tmdc->btnc[j][k] = models[m].btnc[k];
tmdc->btno[j][k] = models[m].btno[k];
}
sprintf(tmdc->name[j], models[m].name, models[m].abs,
(data[j][TMDC_BYTE_DEF] & 0xf) << 3, tmdc->mode[j]);
sprintf(tmdc->phys[j], "%s/input%d", gameport->phys, j);
tmdc->dev[j].private = tmdc;
tmdc->dev[j].open = tmdc_open;
tmdc->dev[j].close = tmdc_close;
tmdc->dev[j].name = tmdc->name[j];
tmdc->dev[j].phys = tmdc->phys[j];
tmdc->dev[j].idbus = BUS_GAMEPORT;
tmdc->dev[j].idvendor = GAMEPORT_ID_VENDOR_THRUSTMASTER;
tmdc->dev[j].idproduct = models[m].id;
tmdc->dev[j].idversion = 0x0100;
tmdc->dev[j].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (i = 0; i < models[m].abs && i < TMDC_ABS; i++) {
if (tmdc->abs[i] < 0) continue;
set_bit(tmdc->abs[j][i], tmdc->dev[j].absbit);
tmdc->dev[j].absmin[tmdc->abs[j][i]] = 8;
tmdc->dev[j].absmax[tmdc->abs[j][i]] = 248;
tmdc->dev[j].absfuzz[tmdc->abs[j][i]] = 2;
tmdc->dev[j].absflat[tmdc->abs[j][i]] = 4;
}
for (i = 0; i < models[m].hats && i < TMDC_ABS_HAT; i++) {
set_bit(tmdc_abs_hat[i], tmdc->dev[j].absbit);
tmdc->dev[j].absmin[tmdc_abs_hat[i]] = -1;
tmdc->dev[j].absmax[tmdc_abs_hat[i]] = 1;
}
for (k = l = 0; k < 4; k++) {
for (i = 0; i < models[m].btnc[k] && i < TMDC_BTN; i++)
set_bit(tmdc->btn[j][i + l], tmdc->dev[j].keybit);
l += models[m].btnc[k];
}
input_register_device(tmdc->dev + j);
printk(KERN_INFO "input: %s on %s\n", tmdc->name[j], gameport->phys);
}
return;
fail2: gameport_close(gameport);
fail1: kfree(tmdc);
}
static void tmdc_disconnect(struct gameport *gameport)
{
struct tmdc *tmdc = gameport->private;
int i;
for (i = 0; i < 2; i++)
if (tmdc->exists & (1 << i))
input_unregister_device(tmdc->dev + i);
gameport_close(gameport);
kfree(tmdc);
}
static struct gameport_dev tmdc_dev = {
connect: tmdc_connect,
disconnect: tmdc_disconnect,
};
int __init tmdc_init(void)
{
gameport_register_device(&tmdc_dev);
return 0;
}
void __exit tmdc_exit(void)
{
gameport_unregister_device(&tmdc_dev);
}
module_init(tmdc_init);
module_exit(tmdc_exit);
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:29:34
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/joystick
In directory usw-pr-cvs1:/tmp/cvs-serv5674
Added Files:
Tag: 1.10
stinger.c
Log Message:
Moved.
--- NEW FILE: stinger.c ---
/*
* $Id: stinger.c,v 1.10 2002/01/22 20:29:31 vojtech Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik
* Copyright (c) 2000 Mark Fletcher
*/
/*
* Gravis Stinger gamepad driver for Linux
*/
/*
* This program is free warftware; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>");
MODULE_DESCRIPTION("Gravis Stinger gamepad driver");
MODULE_LICENSE("GPL");
/*
* Constants.
*/
#define STINGER_MAX_LENGTH 8
static char *stinger_name = "Gravis Stinger";
/*
* Per-Stinger data.
*/
struct stinger {
struct input_dev dev;
int idx;
unsigned char data[STINGER_MAX_LENGTH];
char phys[32];
};
/*
* stinger_process_packet() decodes packets the driver receives from the
* Stinger. It updates the data accordingly.
*/
static void stinger_process_packet(struct stinger *stinger)
{
struct input_dev *dev = &stinger->dev;
unsigned char *data = stinger->data;
if (!stinger->idx) return;
input_report_key(dev, BTN_A, ((data[0] & 0x20) >> 5));
input_report_key(dev, BTN_B, ((data[0] & 0x10) >> 4));
input_report_key(dev, BTN_C, ((data[0] & 0x08) >> 3));
input_report_key(dev, BTN_X, ((data[0] & 0x04) >> 2));
input_report_key(dev, BTN_Y, ((data[3] & 0x20) >> 5));
input_report_key(dev, BTN_Z, ((data[3] & 0x10) >> 4));
input_report_key(dev, BTN_TL, ((data[3] & 0x08) >> 3));
input_report_key(dev, BTN_TR, ((data[3] & 0x04) >> 2));
input_report_key(dev, BTN_SELECT, ((data[3] & 0x02) >> 1));
input_report_key(dev, BTN_START, (data[3] & 0x01));
input_report_abs(dev, ABS_X, (data[1] & 0x3F) - ((data[0] & 0x01) << 6));
input_report_abs(dev, ABS_Y, ((data[0] & 0x02) << 5) - (data[2] & 0x3F));
return;
}
/*
* stinger_interrupt() is called by the low level driver when characters
* are ready for us. We then buffer them for further processing, or call the
* packet processing routine.
*/
static void stinger_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
{
struct stinger* stinger = serio->private;
/* All Stinger packets are 4 bytes */
if (stinger->idx < STINGER_MAX_LENGTH)
stinger->data[stinger->idx++] = data;
if (stinger->idx == 4) {
stinger_process_packet(stinger);
stinger->idx = 0;
}
return;
}
/*
* stinger_disconnect() is the opposite of stinger_connect()
*/
static void stinger_disconnect(struct serio *serio)
{
struct stinger* stinger = serio->private;
input_unregister_device(&stinger->dev);
serio_close(serio);
kfree(stinger);
}
/*
* stinger_connect() is the routine that is called when someone adds a
* new serio device. It looks for the Stinger, and if found, registers
* it as an input device.
*/
static void stinger_connect(struct serio *serio, struct serio_dev *dev)
{
struct stinger *stinger;
int i;
if (serio->type != (SERIO_RS232 | SERIO_STINGER))
return;
if (!(stinger = kmalloc(sizeof(struct stinger), GFP_KERNEL)))
return;
memset(stinger, 0, sizeof(struct stinger));
stinger->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
stinger->dev.keybit[LONG(BTN_A)] = BIT(BTN_A) | BIT(BTN_B) | BIT(BTN_C) | BIT(BTN_X) | \
BIT(BTN_Y) | BIT(BTN_Z) | BIT(BTN_TL) | BIT(BTN_TR) | \
BIT(BTN_START) | BIT(BTN_SELECT);
stinger->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
sprintf(stinger->phys, "%s/serio0", serio->phys);
stinger->dev.name = stinger_name;
stinger->dev.phys = stinger->phys;
stinger->dev.idbus = BUS_RS232;
stinger->dev.idvendor = SERIO_STINGER;
stinger->dev.idproduct = 0x0001;
stinger->dev.idversion = 0x0100;
for (i = 0; i < 2; i++) {
stinger->dev.absmax[ABS_X+i] = 64;
stinger->dev.absmin[ABS_X+i] = -64;
stinger->dev.absflat[ABS_X+i] = 4;
}
stinger->dev.private = stinger;
serio->private = stinger;
if (serio_open(serio, dev)) {
kfree(stinger);
return;
}
input_register_device(&stinger->dev);
printk(KERN_INFO "input: %s on %s\n", stinger_name, serio->phys);
}
/*
* The serio device structure.
*/
static struct serio_dev stinger_dev = {
interrupt: stinger_interrupt,
connect: stinger_connect,
disconnect: stinger_disconnect,
};
/*
* The functions for inserting/removing us as a module.
*/
int __init stinger_init(void)
{
serio_register_device(&stinger_dev);
return 0;
}
void __exit stinger_exit(void)
{
serio_unregister_device(&stinger_dev);
}
module_init(stinger_init);
module_exit(stinger_exit);
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:29:22
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/joystick
In directory usw-pr-cvs1:/tmp/cvs-serv5599
Added Files:
Tag: 1.15
spaceorb.c
Log Message:
Moved.
--- NEW FILE: spaceorb.c ---
/*
* $Id: spaceorb.c,v 1.15 2002/01/22 20:29:19 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* Based on the work of:
* David Thompson
*/
/*
* SpaceTec SpaceOrb 360 and Avenger 6dof controller driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/serio.h>
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>");
MODULE_DESCRIPTION("SpaceTec SpaceOrb 360 and Avenger 6dof controller driver");
MODULE_LICENSE("GPL");
/*
* Constants.
*/
#define SPACEORB_MAX_LENGTH 64
static int spaceorb_buttons[] = { BTN_TL, BTN_TR, BTN_Y, BTN_X, BTN_B, BTN_A };
static int spaceorb_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ };
static char *spaceorb_name = "SpaceTec SpaceOrb 360 / Avenger";
/*
* Per-Orb data.
*/
struct spaceorb {
struct input_dev dev;
struct serio *serio;
int idx;
unsigned char data[SPACEORB_MAX_LENGTH];
char phys[32];
};
static unsigned char spaceorb_xor[] = "SpaceWare";
static unsigned char *spaceorb_errors[] = { "EEPROM storing 0 failed", "Receive queue overflow", "Transmit queue timeout",
"Bad packet", "Power brown-out", "EEPROM checksum error", "Hardware fault" };
/*
* spaceorb_process_packet() decodes packets the driver receives from the
* SpaceOrb.
*/
static void spaceorb_process_packet(struct spaceorb *spaceorb)
{
struct input_dev *dev = &spaceorb->dev;
unsigned char *data = spaceorb->data;
unsigned char c = 0;
int axes[6];
int i;
if (spaceorb->idx < 2) return;
for (i = 0; i < spaceorb->idx; i++) c ^= data[i];
if (c) return;
switch (data[0]) {
case 'R': /* Reset packet */
spaceorb->data[spaceorb->idx - 1] = 0;
for (i = 1; i < spaceorb->idx && spaceorb->data[i] == ' '; i++);
printk(KERN_INFO "input: %s [%s] on %s\n",
spaceorb_name, spaceorb->data + i, spaceorb->serio->phys);
break;
case 'D': /* Ball + button data */
if (spaceorb->idx != 12) return;
for (i = 0; i < 9; i++) spaceorb->data[i+2] ^= spaceorb_xor[i];
axes[0] = ( data[2] << 3) | (data[ 3] >> 4);
axes[1] = ((data[3] & 0x0f) << 6) | (data[ 4] >> 1);
axes[2] = ((data[4] & 0x01) << 9) | (data[ 5] << 2) | (data[4] >> 5);
axes[3] = ((data[6] & 0x1f) << 5) | (data[ 7] >> 2);
axes[4] = ((data[7] & 0x03) << 8) | (data[ 8] << 1) | (data[7] >> 6);
axes[5] = ((data[9] & 0x3f) << 4) | (data[10] >> 3);
for (i = 0; i < 6; i++)
input_report_abs(dev, spaceorb_axes[i], axes[i] - ((axes[i] & 0x200) ? 1024 : 0));
for (i = 0; i < 6; i++)
input_report_key(dev, spaceorb_buttons[i], (data[1] >> i) & 1);
break;
case 'K': /* Button data */
if (spaceorb->idx != 5) return;
for (i = 0; i < 7; i++)
input_report_key(dev, spaceorb_buttons[i], (data[2] >> i) & 1);
break;
case 'E': /* Error packet */
if (spaceorb->idx != 4) return;
printk(KERN_ERR "joy-spaceorb: Device error. [ ");
for (i = 0; i < 7; i++) if (data[1] & (1 << i)) printk("%s ", spaceorb_errors[i]);
printk("]\n");
break;
}
}
static void spaceorb_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
{
struct spaceorb* spaceorb = serio->private;
if (~data & 0x80) {
if (spaceorb->idx) spaceorb_process_packet(spaceorb);
spaceorb->idx = 0;
}
if (spaceorb->idx < SPACEORB_MAX_LENGTH)
spaceorb->data[spaceorb->idx++] = data & 0x7f;
}
/*
* spaceorb_disconnect() is the opposite of spaceorb_connect()
*/
static void spaceorb_disconnect(struct serio *serio)
{
struct spaceorb* spaceorb = serio->private;
input_unregister_device(&spaceorb->dev);
serio_close(serio);
kfree(spaceorb);
}
/*
* spaceorb_connect() is the routine that is called when someone adds a
* new serio device. It looks for the SpaceOrb/Avenger, and if found, registers
* it as an input device.
*/
static void spaceorb_connect(struct serio *serio, struct serio_dev *dev)
{
struct spaceorb *spaceorb;
int i, t;
if (serio->type != (SERIO_RS232 | SERIO_SPACEORB))
return;
if (!(spaceorb = kmalloc(sizeof(struct spaceorb), GFP_KERNEL)))
return;
memset(spaceorb, 0, sizeof(struct spaceorb));
spaceorb->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (i = 0; i < 6; i++)
set_bit(spaceorb_buttons[i], spaceorb->dev.keybit);
for (i = 0; i < 6; i++) {
t = spaceorb_axes[i];
set_bit(t, spaceorb->dev.absbit);
spaceorb->dev.absmin[t] = -508;
spaceorb->dev.absmax[t] = 508;
}
spaceorb->serio = serio;
spaceorb->dev.private = spaceorb;
sprintf(spaceorb->phys, "%s/input0", serio->phys);
spaceorb->dev.name = spaceorb_name;
spaceorb->dev.phys = spaceorb->phys;
spaceorb->dev.idbus = BUS_RS232;
spaceorb->dev.idvendor = SERIO_SPACEORB;
spaceorb->dev.idproduct = 0x0001;
spaceorb->dev.idversion = 0x0100;
serio->private = spaceorb;
if (serio_open(serio, dev)) {
kfree(spaceorb);
return;
}
input_register_device(&spaceorb->dev);
}
/*
* The serio device structure.
*/
static struct serio_dev spaceorb_dev = {
interrupt: spaceorb_interrupt,
connect: spaceorb_connect,
disconnect: spaceorb_disconnect,
};
/*
* The functions for inserting/removing us as a module.
*/
int __init spaceorb_init(void)
{
serio_register_device(&spaceorb_dev);
return 0;
}
void __exit spaceorb_exit(void)
{
serio_unregister_device(&spaceorb_dev);
}
module_init(spaceorb_init);
module_exit(spaceorb_exit);
|
|
From: Vojtech P. <vo...@us...> - 2002-01-22 20:29:06
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/joystick
In directory usw-pr-cvs1:/tmp/cvs-serv5480
Added Files:
Tag: 1.17
spaceball.c
Log Message:
Moved.
--- NEW FILE: spaceball.c ---
/*
* $Id: spaceball.c,v 1.17 2002/01/22 20:29:03 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* Based on the work of:
* David Thompson
* Joseph Krahn
*/
/*
* SpaceTec SpaceBall 2003/3003/4000 FLX driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vo...@uc...>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/serio.h>
MODULE_AUTHOR("Vojtech Pavlik <vo...@uc...>");
MODULE_DESCRIPTION("SpaceTec SpaceBall 2003/3003/4000 FLX driver");
MODULE_LICENSE("GPL");
/*
* Constants.
*/
#define SPACEBALL_MAX_LENGTH 128
#define SPACEBALL_MAX_ID 8
#define SPACEBALL_1003 1
#define SPACEBALL_2003B 3
#define SPACEBALL_2003C 4
#define SPACEBALL_3003C 7
#define SPACEBALL_4000FLX 8
#define SPACEBALL_4000FLX_L 9
static int spaceball_axes[] = { ABS_X, ABS_Z, ABS_Y, ABS_RX, ABS_RZ, ABS_RY };
static char *spaceball_names[] = {
"?", "SpaceTec SpaceBall 1003", "SpaceTec SpaceBall 2003", "SpaceTec SpaceBall 2003B",
"SpaceTec SpaceBall 2003C", "SpaceTec SpaceBall 3003", "SpaceTec SpaceBall SpaceController",
"SpaceTec SpaceBall 3003C", "SpaceTec SpaceBall 4000FLX", "SpaceTec SpaceBall 4000FLX Lefty" };
/*
* Per-Ball data.
*/
struct spaceball {
struct input_dev dev;
struct serio *serio;
int idx;
int escape;
unsigned char data[SPACEBALL_MAX_LENGTH];
char phys[32];
};
/*
* spaceball_process_packet() decodes packets the driver receives from the
* SpaceBall.
*/
static void spaceball_process_packet(struct spaceball* spaceball)
{
struct input_dev *dev = &spaceball->dev;
unsigned char *data = spaceball->data;
int i;
if (spaceball->idx < 2) return;
switch (spaceball->data[0]) {
case 'D': /* Ball data */
if (spaceball->idx != 15) return;
for (i = 0; i < 6; i++)
input_report_abs(dev, spaceball_axes[i],
(__s16)((data[2 * i + 3] << 8) | data[2 * i + 2]));
break;
case 'K': /* Button data */
if (spaceball->idx != 3) return;
input_report_key(dev, BTN_1, (data[2] & 0x01) || (data[2] & 0x20));
input_report_key(dev, BTN_2, data[2] & 0x02);
input_report_key(dev, BTN_3, data[2] & 0x04);
input_report_key(dev, BTN_4, data[2] & 0x08);
input_report_key(dev, BTN_5, data[1] & 0x01);
input_report_key(dev, BTN_6, data[1] & 0x02);
input_report_key(dev, BTN_7, data[1] & 0x04);
input_report_key(dev, BTN_8, data[1] & 0x10);
break;
case '.': /* Advanced button data */
if (spaceball->idx != 3) return;
input_report_key(dev, BTN_1, data[2] & 0x01);
input_report_key(dev, BTN_2, data[2] & 0x02);
input_report_key(dev, BTN_3, data[2] & 0x04);
input_report_key(dev, BTN_4, data[2] & 0x08);
input_report_key(dev, BTN_5, data[2] & 0x10);
input_report_key(dev, BTN_6, data[2] & 0x20);
input_report_key(dev, BTN_7, data[2] & 0x80);
input_report_key(dev, BTN_8, data[1] & 0x01);
input_report_key(dev, BTN_9, data[1] & 0x02);
input_report_key(dev, BTN_A, data[1] & 0x04);
input_report_key(dev, BTN_B, data[1] & 0x08);
input_report_key(dev, BTN_C, data[1] & 0x10);
input_report_key(dev, BTN_MODE, data[1] & 0x20);
break;
case 'E': /* Device error */
spaceball->data[spaceball->idx - 1] = 0;
printk(KERN_ERR "spaceball: Device error. [%s]\n", spaceball->data + 1);
break;
case '?': /* Bad command packet */
spaceball->data[spaceball->idx - 1] = 0;
printk(KERN_ERR "spaceball: Bad command. [%s]\n", spaceball->data + 1);
break;
}
}
/*
* Spaceball 4000 FLX packets all start with a one letter packet-type decriptor,
* and end in 0x0d. It uses '^' as an escape for CR, XOFF and XON characters which
* can occur in the axis values.
*/
static void spaceball_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
{
struct spaceball *spaceball = serio->private;
switch (data) {
case 0xd:
spaceball_process_packet(spaceball);
spaceball->idx = 0;
spaceball->escape = 0;
return;
case '^':
if (!spaceball->escape) {
spaceball->escape = 1;
return;
}
spaceball->escape = 0;
case 'M':
case 'Q':
case 'S':
if (spaceball->escape) {
spaceball->escape = 0;
data &= 0x1f;
}
default:
if (spaceball->escape)
spaceball->escape = 0;
if (spaceball->idx < SPACEBALL_MAX_LENGTH)
spaceball->data[spaceball->idx++] = data;
return;
}
}
/*
* spaceball_disconnect() is the opposite of spaceball_connect()
*/
static void spaceball_disconnect(struct serio *serio)
{
struct spaceball* spaceball = serio->private;
input_unregister_device(&spaceball->dev);
serio_close(serio);
kfree(spaceball);
}
/*
* spaceball_connect() is the routine that is called when someone adds a
* new serio device. It looks for the Magellan, and if found, registers
* it as an input device.
*/
static void spaceball_connect(struct serio *serio, struct serio_dev *dev)
{
struct spaceball *spaceball;
int i, t, id;
if ((serio->type & ~SERIO_ID) != (SERIO_RS232 | SERIO_SPACEBALL))
return;
if ((id = (serio->type & SERIO_ID) >> 8) > SPACEBALL_MAX_ID)
return;
if (!(spaceball = kmalloc(sizeof(struct spaceball), GFP_KERNEL)))
return;
memset(spaceball, 0, sizeof(struct spaceball));
spaceball->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
switch (id) {
case SPACEBALL_4000FLX:
case SPACEBALL_4000FLX_L:
spaceball->dev.keybit[LONG(BTN_0)] |= BIT(BTN_9);
spaceball->dev.keybit[LONG(BTN_A)] |= BIT(BTN_A) | BIT(BTN_B) | BIT(BTN_C) | BIT(BTN_MODE);
default:
spaceball->dev.keybit[LONG(BTN_0)] |= BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4)
| BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7) | BIT(BTN_8);
case SPACEBALL_3003C:
spaceball->dev.keybit[LONG(BTN_0)] |= BIT(BTN_1) | BIT(BTN_8);
}
for (i = 0; i < 6; i++) {
t = spaceball_axes[i];
set_bit(t, spaceball->dev.absbit);
spaceball->dev.absmin[t] = i < 3 ? -8000 : -1600;
spaceball->dev.absmax[t] = i < 3 ? 8000 : 1600;
spaceball->dev.absflat[t] = i < 3 ? 40 : 8;
spaceball->dev.absfuzz[t] = i < 3 ? 8 : 2;
}
spaceball->serio = serio;
spaceball->dev.private = spaceball;
sprintf(spaceball->phys, "%s/input0", serio->phys);
spaceball->dev.name = spaceball_names[id];
spaceball->dev.phys = spaceball->phys;
spaceball->dev.idbus = BUS_RS232;
spaceball->dev.idvendor = SERIO_SPACEBALL;
spaceball->dev.idproduct = id;
spaceball->dev.idversion = 0x0100;
serio->private = spaceball;
if (serio_open(serio, dev)) {
kfree(spaceball);
return;
}
input_register_device(&spaceball->dev);
printk(KERN_INFO "input: %s on serio%s\n",
spaceball_names[id], serio->phys);
}
/*
* The serio device structure.
*/
static struct serio_dev spaceball_dev = {
interrupt: spaceball_interrupt,
connect: spaceball_connect,
disconnect: spaceball_disconnect,
};
/*
* The functions for inserting/removing us as a module.
*/
int __init spaceball_init(void)
{
serio_register_device(&spaceball_dev);
return 0;
}
void __exit spaceball_exit(void)
{
serio_unregister_device(&spaceball_dev);
}
module_init(spaceball_init);
module_exit(spaceball_exit);
|