/shark/trunk/drivers/linuxcom/int.c |
---|
53,7 → 53,7 |
handlers[irq].flags = flags; |
handlers[irq].data = dev_id; |
handler_set(irq, linux_intr, NIL); |
handler_set(irq, linux_intr, NIL, TRUE); |
/*if (fdev_intr_alloc(irq, linux_intr, (void *)irq, 0)) { |
handlers[irq].func = 0; |
return (-EBUSY); |
71,6 → 71,6 |
*/ |
void free_irq(unsigned int irq, void *a) |
{ |
handler_set(irq, NULL, NIL); |
handler_set(irq, NULL, NIL, FALSE); |
handlers[irq].func = 0; |
} |
/shark/trunk/drivers/linuxcom/makefile |
---|
15,7 → 15,7 |
misc.o \ |
auto_irq.o \ |
int.o \ |
net_init.o |
net_init.o \ |
C_OPT += -I../linuxcom/include |
/shark/trunk/drivers/input/joystick/analog.c |
---|
0,0 → 1,778 |
/* |
* $Id: analog.c,v 1.1 2004-03-08 18:47:38 giacomo Exp $ |
* |
* Copyright (c) 1996-2001 Vojtech Pavlik |
*/ |
/* |
* Analog joystick and gamepad 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 <vojtech@ucw.cz>, or by paper mail: |
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#include <linux/delay.h> |
#include <linux/kernel.h> |
#include <linux/module.h> |
#include <linux/slab.h> |
#include <linux/bitops.h> |
#include <linux/init.h> |
#include <linux/input.h> |
#include <linux/gameport.h> |
#include <asm/timex.h> |
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); |
MODULE_DESCRIPTION("Analog joystick and gamepad driver"); |
MODULE_LICENSE("GPL"); |
/* |
* Option parsing. |
*/ |
#define ANALOG_PORTS 16 |
static char *js[ANALOG_PORTS]; |
static int analog_options[ANALOG_PORTS]; |
MODULE_PARM(js, "1-" __MODULE_STRING(ANALOG_PORTS) "s"); |
MODULE_PARM_DESC(js, "Analog joystick options"); |
/* |
* Times, feature definitions. |
*/ |
#define ANALOG_RUDDER 0x00004 |
#define ANALOG_THROTTLE 0x00008 |
#define ANALOG_AXES_STD 0x0000f |
#define ANALOG_BTNS_STD 0x000f0 |
#define ANALOG_BTNS_CHF 0x00100 |
#define ANALOG_HAT1_CHF 0x00200 |
#define ANALOG_HAT2_CHF 0x00400 |
#define ANALOG_HAT_FCS 0x00800 |
#define ANALOG_HATS_ALL 0x00e00 |
#define ANALOG_BTN_TL 0x01000 |
#define ANALOG_BTN_TR 0x02000 |
#define ANALOG_BTN_TL2 0x04000 |
#define ANALOG_BTN_TR2 0x08000 |
#define ANALOG_BTNS_TLR 0x03000 |
#define ANALOG_BTNS_TLR2 0x0c000 |
#define ANALOG_BTNS_GAMEPAD 0x0f000 |
#define ANALOG_HBTN_CHF 0x10000 |
#define ANALOG_ANY_CHF 0x10700 |
#define ANALOG_SAITEK 0x20000 |
#define ANALOG_EXTENSIONS 0x7ff00 |
#define ANALOG_GAMEPAD 0x80000 |
#define ANALOG_MAX_TIME 3 /* 3 ms */ |
#define ANALOG_LOOP_TIME 2000 /* 2 * loop */ |
#define ANALOG_REFRESH_TIME HZ/100 /* 10 ms */ |
#define ANALOG_SAITEK_DELAY 200 /* 200 us */ |
#define ANALOG_SAITEK_TIME 2000 /* 2000 us */ |
#define ANALOG_AXIS_TIME 2 /* 2 * refresh */ |
#define ANALOG_INIT_RETRIES 8 /* 8 times */ |
#define ANALOG_FUZZ_BITS 2 /* 2 bit more */ |
#define ANALOG_FUZZ_MAGIC 36 /* 36 u*ms/loop */ |
#define ANALOG_MAX_NAME_LENGTH 128 |
#define ANALOG_MAX_PHYS_LENGTH 32 |
static short analog_axes[] = { ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE }; |
static short analog_hats[] = { ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y }; |
static short analog_pads[] = { BTN_Y, BTN_Z, BTN_TL, BTN_TR }; |
static short analog_exts[] = { ANALOG_HAT1_CHF, ANALOG_HAT2_CHF, ANALOG_HAT_FCS }; |
static short analog_pad_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_TL2, BTN_TR2, BTN_SELECT, BTN_START, BTN_MODE, BTN_BASE }; |
static short analog_joy_btn[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, |
BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_BASE6 }; |
static unsigned char analog_chf[] = { 0xf, 0x0, 0x1, 0x9, 0x2, 0x4, 0xc, 0x8, 0x3, 0x5, 0xb, 0x7, 0xd, 0xe, 0xa, 0x6 }; |
struct analog { |
struct input_dev dev; |
int mask; |
short *buttons; |
char name[ANALOG_MAX_NAME_LENGTH]; |
char phys[ANALOG_MAX_PHYS_LENGTH]; |
}; |
struct analog_port { |
struct gameport *gameport; |
struct timer_list timer; |
struct analog analog[2]; |
unsigned char mask; |
char saitek; |
char cooked; |
int bads; |
int reads; |
int speed; |
int loop; |
int fuzz; |
int axes[4]; |
int buttons; |
int initial[4]; |
int used; |
int axtime; |
}; |
/* |
* Time macros. |
*/ |
#ifdef __i386__ |
#define GET_TIME(x) do { if (cpu_has_tsc) rdtscl(x); else x = get_time_pit(); } while (0) |
#define DELTA(x,y) (cpu_has_tsc?((y)-(x)):((x)-(y)+((x)<(y)?1193182L/HZ:0))) |
#define TIME_NAME (cpu_has_tsc?"TSC":"PIT") |
static unsigned int get_time_pit(void) |
{ |
extern spinlock_t i8253_lock; |
unsigned long flags; |
unsigned int count; |
spin_lock_irqsave(&i8253_lock, flags); |
outb_p(0x00, 0x43); |
count = inb_p(0x40); |
count |= inb_p(0x40) << 8; |
spin_unlock_irqrestore(&i8253_lock, flags); |
return count; |
} |
#elif __x86_64__ |
#define GET_TIME(x) rdtscl(x) |
#define DELTA(x,y) ((y)-(x)) |
#define TIME_NAME "TSC" |
#elif __alpha__ |
#define GET_TIME(x) do { x = get_cycles(); } while (0) |
#define DELTA(x,y) ((y)-(x)) |
#define TIME_NAME "PCC" |
#else |
#define FAKE_TIME |
static unsigned long analog_faketime = 0; |
#define GET_TIME(x) do { x = analog_faketime++; } while(0) |
#define DELTA(x,y) ((y)-(x)) |
#define TIME_NAME "Unreliable" |
#warning Precise timer not defined for this architecture. |
#endif |
/* |
* analog_decode() decodes analog joystick data and reports input events. |
*/ |
static void analog_decode(struct analog *analog, int *axes, int *initial, int buttons) |
{ |
struct input_dev *dev = &analog->dev; |
int i, j; |
if (analog->mask & ANALOG_HAT_FCS) |
for (i = 0; i < 4; i++) |
if (axes[3] < ((initial[3] * ((i << 1) + 1)) >> 3)) { |
buttons |= 1 << (i + 14); |
break; |
} |
for (i = j = 0; i < 6; i++) |
if (analog->mask & (0x10 << i)) |
input_report_key(dev, analog->buttons[j++], (buttons >> i) & 1); |
if (analog->mask & ANALOG_HBTN_CHF) |
for (i = 0; i < 4; i++) |
input_report_key(dev, analog->buttons[j++], (buttons >> (i + 10)) & 1); |
if (analog->mask & ANALOG_BTN_TL) |
input_report_key(dev, analog_pads[0], axes[2] < (initial[2] >> 1)); |
if (analog->mask & ANALOG_BTN_TR) |
input_report_key(dev, analog_pads[1], axes[3] < (initial[3] >> 1)); |
if (analog->mask & ANALOG_BTN_TL2) |
input_report_key(dev, analog_pads[2], axes[2] > (initial[2] + (initial[2] >> 1))); |
if (analog->mask & ANALOG_BTN_TR2) |
input_report_key(dev, analog_pads[3], axes[3] > (initial[3] + (initial[3] >> 1))); |
for (i = j = 0; i < 4; i++) |
if (analog->mask & (1 << i)) |
input_report_abs(dev, analog_axes[j++], axes[i]); |
for (i = j = 0; i < 3; i++) |
if (analog->mask & analog_exts[i]) { |
input_report_abs(dev, analog_hats[j++], |
((buttons >> ((i << 2) + 7)) & 1) - ((buttons >> ((i << 2) + 9)) & 1)); |
input_report_abs(dev, analog_hats[j++], |
((buttons >> ((i << 2) + 8)) & 1) - ((buttons >> ((i << 2) + 6)) & 1)); |
} |
input_sync(dev); |
} |
/* |
* analog_cooked_read() reads analog joystick data. |
*/ |
static int analog_cooked_read(struct analog_port *port) |
{ |
struct gameport *gameport = port->gameport; |
unsigned int time[4], start, loop, now, loopout, timeout; |
unsigned char data[4], this, last; |
unsigned long flags; |
int i, j; |
loopout = (ANALOG_LOOP_TIME * port->loop) / 1000; |
timeout = ANALOG_MAX_TIME * port->speed; |
local_irq_save(flags); |
gameport_trigger(gameport); |
GET_TIME(now); |
local_irq_restore(flags); |
start = now; |
this = port->mask; |
i = 0; |
do { |
loop = now; |
last = this; |
local_irq_disable(); |
this = gameport_read(gameport) & port->mask; |
GET_TIME(now); |
local_irq_restore(flags); |
if ((last ^ this) && (DELTA(loop, now) < loopout)) { |
data[i] = last ^ this; |
time[i] = now; |
i++; |
} |
} while (this && (i < 4) && (DELTA(start, now) < timeout)); |
this <<= 4; |
for (--i; i >= 0; i--) { |
this |= data[i]; |
for (j = 0; j < 4; j++) |
if (data[i] & (1 << j)) |
port->axes[j] = (DELTA(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop; |
} |
return -(this != port->mask); |
} |
static int analog_button_read(struct analog_port *port, char saitek, char chf) |
{ |
unsigned char u; |
int t = 1, i = 0; |
int strobe = gameport_time(port->gameport, ANALOG_SAITEK_TIME); |
u = gameport_read(port->gameport); |
if (!chf) { |
port->buttons = (~u >> 4) & 0xf; |
return 0; |
} |
port->buttons = 0; |
while ((~u & 0xf0) && (i < 16) && t) { |
port->buttons |= 1 << analog_chf[(~u >> 4) & 0xf]; |
if (!saitek) return 0; |
udelay(ANALOG_SAITEK_DELAY); |
t = strobe; |
gameport_trigger(port->gameport); |
while (((u = gameport_read(port->gameport)) & port->mask) && t) t--; |
i++; |
} |
return -(!t || (i == 16)); |
} |
/* |
* analog_timer() repeatedly polls the Analog joysticks. |
*/ |
static void analog_timer(unsigned long data) |
{ |
struct analog_port *port = (void *) data; |
int i; |
char saitek = !!(port->analog[0].mask & ANALOG_SAITEK); |
char chf = !!(port->analog[0].mask & ANALOG_ANY_CHF); |
if (port->cooked) { |
port->bads -= gameport_cooked_read(port->gameport, port->axes, &port->buttons); |
if (chf) |
port->buttons = port->buttons ? (1 << analog_chf[port->buttons]) : 0; |
port->reads++; |
} else { |
if (!port->axtime--) { |
port->bads -= analog_cooked_read(port); |
port->bads -= analog_button_read(port, saitek, chf); |
port->reads++; |
port->axtime = ANALOG_AXIS_TIME - 1; |
} else { |
if (!saitek) |
analog_button_read(port, saitek, chf); |
} |
} |
for (i = 0; i < 2; i++) |
if (port->analog[i].mask) |
analog_decode(port->analog + i, port->axes, port->initial, port->buttons); |
mod_timer(&port->timer, jiffies + ANALOG_REFRESH_TIME); |
} |
/* |
* analog_open() is a callback from the input open routine. |
*/ |
static int analog_open(struct input_dev *dev) |
{ |
struct analog_port *port = dev->private; |
if (!port->used++) |
mod_timer(&port->timer, jiffies + ANALOG_REFRESH_TIME); |
return 0; |
} |
/* |
* analog_close() is a callback from the input close routine. |
*/ |
static void analog_close(struct input_dev *dev) |
{ |
struct analog_port *port = dev->private; |
if (!--port->used) |
del_timer(&port->timer); |
} |
/* |
* analog_calibrate_timer() calibrates the timer and computes loop |
* and timeout values for a joystick port. |
*/ |
static void analog_calibrate_timer(struct analog_port *port) |
{ |
struct gameport *gameport = port->gameport; |
unsigned int i, t, tx, t1, t2, t3; |
unsigned long flags; |
local_irq_save(flags); |
GET_TIME(t1); |
#ifdef FAKE_TIME |
analog_faketime += 830; |
#endif |
udelay(1000); |
GET_TIME(t2); |
GET_TIME(t3); |
local_irq_restore(flags); |
port->speed = DELTA(t1, t2) - DELTA(t2, t3); |
tx = ~0; |
for (i = 0; i < 50; i++) { |
local_irq_save(flags); |
GET_TIME(t1); |
for (t = 0; t < 50; t++) { gameport_read(gameport); GET_TIME(t2); } |
GET_TIME(t3); |
local_irq_restore(flags); |
udelay(i); |
t = DELTA(t1, t2) - DELTA(t2, t3); |
if (t < tx) tx = t; |
} |
port->loop = tx / 50; |
} |
/* |
* analog_name() constructs a name for an analog joystick. |
*/ |
static void analog_name(struct analog *analog) |
{ |
sprintf26(analog->name, "Analog %d-axis %d-button", |
hweight8(analog->mask & ANALOG_AXES_STD), |
hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 + |
hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4); |
if (analog->mask & ANALOG_HATS_ALL) |
sprintf26(analog->name, "%s %d-hat", |
analog->name, hweight16(analog->mask & ANALOG_HATS_ALL)); |
if (analog->mask & ANALOG_HAT_FCS) |
strcat(analog->name, " FCS"); |
if (analog->mask & ANALOG_ANY_CHF) |
strcat(analog->name, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF"); |
strcat(analog->name, (analog->mask & ANALOG_GAMEPAD) ? " gamepad": " joystick"); |
} |
/* |
* analog_init_device() |
*/ |
static void analog_init_device(struct analog_port *port, struct analog *analog, int index) |
{ |
int i, j, t, v, w, x, y, z; |
analog_name(analog); |
sprintf26(analog->phys, "%s/input%d", port->gameport->phys, index); |
analog->buttons = (analog->mask & ANALOG_GAMEPAD) ? analog_pad_btn : analog_joy_btn; |
init_input_dev(&analog->dev); |
analog->dev.name = analog->name; |
analog->dev.phys = analog->phys; |
analog->dev.id.bustype = BUS_GAMEPORT; |
analog->dev.id.vendor = GAMEPORT_ID_VENDOR_ANALOG; |
analog->dev.id.product = analog->mask >> 4; |
analog->dev.id.version = 0x0100; |
analog->dev.open = analog_open; |
analog->dev.close = analog_close; |
analog->dev.private = port; |
analog->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); |
for (i = j = 0; i < 4; i++) |
if (analog->mask & (1 << i)) { |
t = analog_axes[j]; |
x = port->axes[i]; |
y = (port->axes[0] + port->axes[1]) >> 1; |
z = y - port->axes[i]; |
z = z > 0 ? z : -z; |
v = (x >> 3); |
w = (x >> 3); |
set_bit(t, analog->dev.absbit); |
if ((i == 2 || i == 3) && (j == 2 || j == 3) && (z > (y >> 3))) |
x = y; |
if (analog->mask & ANALOG_SAITEK) { |
if (i == 2) x = port->axes[i]; |
v = x - (x >> 2); |
w = (x >> 4); |
} |
analog->dev.absmax[t] = (x << 1) - v; |
analog->dev.absmin[t] = v; |
analog->dev.absfuzz[t] = port->fuzz; |
analog->dev.absflat[t] = w; |
j++; |
} |
for (i = j = 0; i < 3; i++) |
if (analog->mask & analog_exts[i]) |
for (x = 0; x < 2; x++) { |
t = analog_hats[j++]; |
set_bit(t, analog->dev.absbit); |
analog->dev.absmax[t] = 1; |
analog->dev.absmin[t] = -1; |
} |
for (i = j = 0; i < 4; i++) |
if (analog->mask & (0x10 << i)) |
set_bit(analog->buttons[j++], analog->dev.keybit); |
if (analog->mask & ANALOG_BTNS_CHF) |
for (i = 0; i < 2; i++) |
set_bit(analog->buttons[j++], analog->dev.keybit); |
if (analog->mask & ANALOG_HBTN_CHF) |
for (i = 0; i < 4; i++) |
set_bit(analog->buttons[j++], analog->dev.keybit); |
for (i = 0; i < 4; i++) |
if (analog->mask & (ANALOG_BTN_TL << i)) |
set_bit(analog_pads[i], analog->dev.keybit); |
analog_decode(analog, port->axes, port->initial, port->buttons); |
input_register_device(&analog->dev); |
printk(KERN_INFO "input: %s at %s", analog->name, port->gameport->phys); |
if (port->cooked) |
printk(" [ADC port]\n"); |
else |
printk(" [%s timer, %d %sHz clock, %d ns res]\n", TIME_NAME, |
port->speed > 10000 ? (port->speed + 800) / 1000 : port->speed, |
port->speed > 10000 ? "M" : "k", |
port->speed > 10000 ? (port->loop * 1000) / (port->speed / 1000) |
: (port->loop * 1000000) / port->speed); |
} |
/* |
* analog_init_devices() sets up device-specific values and registers the input devices. |
*/ |
static int analog_init_masks(struct analog_port *port) |
{ |
int i; |
struct analog *analog = port->analog; |
int max[4]; |
if (!port->mask) |
return -1; |
if ((port->mask & 3) != 3 && port->mask != 0xc) { |
printk(KERN_WARNING "analog.c: Unknown joystick device found " |
"(data=%#x, %s), probably not analog joystick.\n", |
port->mask, port->gameport->phys); |
return -1; |
} |
i = analog_options[0]; /* FIXME !!! - need to specify options for different ports */ |
analog[0].mask = i & 0xfffff; |
analog[0].mask &= ~(ANALOG_AXES_STD | ANALOG_HAT_FCS | ANALOG_BTNS_GAMEPAD) |
| port->mask | ((port->mask << 8) & ANALOG_HAT_FCS) |
| ((port->mask << 10) & ANALOG_BTNS_TLR) | ((port->mask << 12) & ANALOG_BTNS_TLR2); |
analog[0].mask &= ~(ANALOG_HAT2_CHF) |
| ((analog[0].mask & ANALOG_HBTN_CHF) ? 0 : ANALOG_HAT2_CHF); |
analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_BTN_TR | ANALOG_BTN_TR2) |
| ((~analog[0].mask & ANALOG_HAT_FCS) >> 8) |
| ((~analog[0].mask & ANALOG_HAT_FCS) << 2) |
| ((~analog[0].mask & ANALOG_HAT_FCS) << 4); |
analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_RUDDER) |
| (((~analog[0].mask & ANALOG_BTNS_TLR ) >> 10) |
& ((~analog[0].mask & ANALOG_BTNS_TLR2) >> 12)); |
analog[1].mask = ((i >> 20) & 0xff) | ((i >> 12) & 0xf0000); |
analog[1].mask &= (analog[0].mask & ANALOG_EXTENSIONS) ? ANALOG_GAMEPAD |
: (((ANALOG_BTNS_STD | port->mask) & ~analog[0].mask) | ANALOG_GAMEPAD); |
if (port->cooked) { |
for (i = 0; i < 4; i++) max[i] = port->axes[i] << 1; |
if ((analog[0].mask & 0x7) == 0x7) max[2] = (max[0] + max[1]) >> 1; |
if ((analog[0].mask & 0xb) == 0xb) max[3] = (max[0] + max[1]) >> 1; |
if ((analog[0].mask & ANALOG_BTN_TL) && !(analog[0].mask & ANALOG_BTN_TL2)) max[2] >>= 1; |
if ((analog[0].mask & ANALOG_BTN_TR) && !(analog[0].mask & ANALOG_BTN_TR2)) max[3] >>= 1; |
if ((analog[0].mask & ANALOG_HAT_FCS)) max[3] >>= 1; |
gameport_calibrate(port->gameport, port->axes, max); |
} |
for (i = 0; i < 4; i++) |
port->initial[i] = port->axes[i]; |
return -!(analog[0].mask || analog[1].mask); |
} |
static int analog_init_port(struct gameport *gameport, struct gameport_dev *dev, struct analog_port *port) |
{ |
int i, t, u, v; |
gameport->private = port; |
port->gameport = gameport; |
init_timer(&port->timer); |
port->timer.data = (long) port; |
port->timer.function = analog_timer; |
if (!gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) { |
analog_calibrate_timer(port); |
gameport_trigger(gameport); |
t = gameport_read(gameport); |
wait_ms(ANALOG_MAX_TIME); |
port->mask = (gameport_read(gameport) ^ t) & t & 0xf; |
port->fuzz = (port->speed * ANALOG_FUZZ_MAGIC) / port->loop / 1000 + ANALOG_FUZZ_BITS; |
for (i = 0; i < ANALOG_INIT_RETRIES; i++) { |
if (!analog_cooked_read(port)) break; |
wait_ms(ANALOG_MAX_TIME); |
} |
u = v = 0; |
wait_ms(ANALOG_MAX_TIME); |
t = gameport_time(gameport, ANALOG_MAX_TIME * 1000); |
gameport_trigger(gameport); |
while ((gameport_read(port->gameport) & port->mask) && (u < t)) u++; |
udelay(ANALOG_SAITEK_DELAY); |
t = gameport_time(gameport, ANALOG_SAITEK_TIME); |
gameport_trigger(gameport); |
while ((gameport_read(port->gameport) & port->mask) && (v < t)) v++; |
if (v < (u >> 1)) { /* FIXME - more than one port */ |
analog_options[0] |= /* FIXME - more than one port */ |
ANALOG_SAITEK | ANALOG_BTNS_CHF | ANALOG_HBTN_CHF | ANALOG_HAT1_CHF; |
return 0; |
} |
gameport_close(gameport); |
} |
if (!gameport_open(gameport, dev, GAMEPORT_MODE_COOKED)) { |
for (i = 0; i < ANALOG_INIT_RETRIES; i++) |
if (!gameport_cooked_read(gameport, port->axes, &port->buttons)) |
break; |
for (i = 0; i < 4; i++) |
if (port->axes[i] != -1) port->mask |= 1 << i; |
port->fuzz = gameport->fuzz; |
port->cooked = 1; |
return 0; |
} |
if (!gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) |
return 0; |
return -1; |
} |
static void analog_connect(struct gameport *gameport, struct gameport_dev *dev) |
{ |
struct analog_port *port; |
int i; |
if (!(port = kmalloc(sizeof(struct analog_port), GFP_KERNEL))) |
return; |
memset(port, 0, sizeof(struct analog_port)); |
if (analog_init_port(gameport, dev, port)) { |
kfree(port); |
return; |
} |
if (analog_init_masks(port)) { |
gameport_close(gameport); |
kfree(port); |
return; |
} |
for (i = 0; i < 2; i++) |
if (port->analog[i].mask) |
analog_init_device(port, port->analog + i, i); |
} |
static void analog_disconnect(struct gameport *gameport) |
{ |
int i; |
struct analog_port *port = gameport->private; |
for (i = 0; i < 2; i++) |
if (port->analog[i].mask) |
input_unregister_device(&port->analog[i].dev); |
gameport_close(gameport); |
printk(KERN_INFO "analog.c: %d out of %d reads (%d%%) on %s failed\n", |
port->bads, port->reads, port->reads ? (port->bads * 100 / port->reads) : 0, |
port->gameport->phys); |
kfree(port); |
} |
struct analog_types { |
char *name; |
int value; |
}; |
struct analog_types analog_types[] = { |
{ "none", 0x00000000 }, |
{ "auto", 0x000000ff }, |
{ "2btn", 0x0000003f }, |
{ "y-joy", 0x0cc00033 }, |
{ "y-pad", 0x8cc80033 }, |
{ "fcs", 0x000008f7 }, |
{ "chf", 0x000002ff }, |
{ "fullchf", 0x000007ff }, |
{ "gamepad", 0x000830f3 }, |
{ "gamepad8", 0x0008f0f3 }, |
{ NULL, 0 } |
}; |
static void analog_parse_options(void) |
{ |
int i, j; |
char *end; |
for (i = 0; i < ANALOG_PORTS && js[i]; i++) { |
for (j = 0; analog_types[j].name; j++) |
if (!strcmp(analog_types[j].name, js[i])) { |
analog_options[i] = analog_types[j].value; |
break; |
} |
if (analog_types[j].name) continue; |
analog_options[i] = simple_strtoul(js[i], &end, 0); |
if (end != js[i]) continue; |
analog_options[i] = 0xff; |
if (!strlen(js[i])) continue; |
printk(KERN_WARNING "analog.c: Bad config for port %d - \"%s\"\n", i, js[i]); |
} |
for (; i < ANALOG_PORTS; i++) |
analog_options[i] = 0xff; |
} |
/* |
* The gameport device structure. |
*/ |
static struct gameport_dev analog_dev = { |
.connect = analog_connect, |
.disconnect = analog_disconnect, |
}; |
#ifndef MODULE |
static int __init analog_setup(char *str) |
{ |
char *s = str; |
int i = 0; |
if (!str || !*str) return 0; |
while ((str = s) && (i < ANALOG_PORTS)) { |
if ((s = strchr(str,','))) *s++ = 0; |
js[i++] = str; |
} |
return 1; |
} |
__setup("js=", analog_setup); |
#endif |
int __init analog_init(void) |
{ |
analog_parse_options(); |
gameport_register_device(&analog_dev); |
return 0; |
} |
void __exit analog_exit(void) |
{ |
gameport_unregister_device(&analog_dev); |
} |
module_init(analog_init); |
module_exit(analog_exit); |
/shark/trunk/drivers/input/joystick/joydump.c |
---|
0,0 → 1,153 |
/* |
* $Id: joydump.c,v 1.1 2004-03-08 18:47:38 giacomo Exp $ |
* |
* Copyright (c) 1996-2001 Vojtech Pavlik |
*/ |
/* |
* This is just a very simple driver that can dump the data |
* out of the joystick port into the syslog ... |
*/ |
/* |
* 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 <vojtech@ucw.cz>, or by paper mail: |
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic |
*/ |
#include <linuxcomp.h> |
#include <linux/module.h> |
#include <linux/gameport.h> |
#include <linux/kernel.h> |
#include <linux/delay.h> |
#include <linux/init.h> |
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); |
MODULE_DESCRIPTION("Gameport data dumper module"); |
MODULE_LICENSE("GPL"); |
#define BUF_SIZE 256 |
struct joydump { |
unsigned int time; |
unsigned char data; |
}; |
static void __devinit joydump_connect(struct gameport *gameport, struct gameport_dev *dev) |
{ |
struct joydump buf[BUF_SIZE]; |
int axes[4], buttons; |
int i, j, t, timeout; |
unsigned long flags; |
unsigned char u; |
printk(KERN_INFO "joydump: ,------------------- START ------------------.\n"); |
printk(KERN_INFO "joydump: | Dumping gameport%s.\n", gameport->phys); |
printk(KERN_INFO "joydump: | Speed: %4d kHz. |\n", gameport->speed); |
if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) { |
printk(KERN_INFO "joydump: | Raw mode not available - trying cooked. |\n"); |
if (gameport_open(gameport, dev, GAMEPORT_MODE_COOKED)) { |
printk(KERN_INFO "joydump: | Cooked not available either. Failing. |\n"); |
printk(KERN_INFO "joydump: `-------------------- END -------------------'\n"); |
return; |
} |
gameport_cooked_read(gameport, axes, &buttons); |
for (i = 0; i < 4; i++) |
printk(KERN_INFO "joydump: | Axis %d: %4d. |\n", i, axes[i]); |
printk(KERN_INFO "joydump: | Buttons %02x. |\n", buttons); |
printk(KERN_INFO "joydump: `-------------------- END -------------------'\n"); |
} |
timeout = gameport_time(gameport, 10000); /* 10 ms */ |
t = 0; |
i = 1; |
local_irq_save(flags); |
u = gameport_read(gameport); |
buf[0].data = u; |
buf[0].time = t; |
gameport_trigger(gameport); |
while (i < BUF_SIZE && t < timeout) { |
buf[i].data = gameport_read(gameport); |
if (buf[i].data ^ u) { |
u = buf[i].data; |
buf[i].time = t; |
i++; |
} |
t++; |
} |
local_irq_restore(flags); |
/* |
* Dump data. |
*/ |
t = i; |
printk(KERN_INFO "joydump: >------------------- DATA -------------------<\n"); |
printk(KERN_INFO "joydump: | index: %3d delta: %3d.%02d us data: ", 0, 0, 0); |
for (j = 7; j >= 0; j--) |
printk("%d",(buf[0].data >> j) & 1); |
printk(" |\n"); |
for (i = 1; i < t; i++) { |
printk(KERN_INFO "joydump: | index: %3d delta: %3d us data: ", |
i, buf[i].time - buf[i-1].time); |
for (j = 7; j >= 0; j--) |
printk("%d",(buf[i].data >> j) & 1); |
printk(" |\n"); |
} |
printk(KERN_INFO "joydump: `-------------------- END -------------------'\n"); |
} |
static void __devexit joydump_disconnect(struct gameport *gameport) |
{ |
gameport_close(gameport); |
} |
static struct gameport_dev joydump_dev = { |
.connect = joydump_connect, |
.disconnect = joydump_disconnect, |
}; |
/*static*/ int __init joydump_init(void) |
{ |
gameport_register_device(&joydump_dev); |
return 0; |
} |
/*static*/ void __exit joydump_exit(void) |
{ |
gameport_unregister_device(&joydump_dev); |
} |
module_init(joydump_init); |
module_exit(joydump_exit); |
/shark/trunk/drivers/input/input.c |
---|
0,0 → 1,766 |
/* |
* The input core |
* |
* Copyright (c) 1999-2002 Vojtech Pavlik |
*/ |
/* |
* This program is free software; you can redistribute it and/or modify it |
* under the terms of the GNU General Public License version 2 as published by |
* the Free Software Foundation. |
*/ |
#include <linuxcomp.h> |
#include <linux/init.h> |
#include <linux/sched.h> |
#include <linux/smp_lock.h> |
#include <linux/input.h> |
#include <linux/module.h> |
#include <linux/random.h> |
#include <linux/major.h> |
#include <linux/pm.h> |
#include <linux/proc_fs.h> |
#include <linux/kmod.h> |
#include <linux/interrupt.h> |
#include <linux/poll.h> |
#include <linux/device.h> |
#include <linux/devfs_fs_kernel.h> |
#define INPUT_DEBUG |
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); |
MODULE_DESCRIPTION("Input core"); |
MODULE_LICENSE("GPL"); |
EXPORT_SYMBOL(input_register_device); |
EXPORT_SYMBOL(input_unregister_device); |
EXPORT_SYMBOL(input_register_handler); |
EXPORT_SYMBOL(input_unregister_handler); |
EXPORT_SYMBOL(input_grab_device); |
EXPORT_SYMBOL(input_release_device); |
EXPORT_SYMBOL(input_open_device); |
EXPORT_SYMBOL(input_close_device); |
EXPORT_SYMBOL(input_accept_process); |
EXPORT_SYMBOL(input_flush_device); |
EXPORT_SYMBOL(input_event); |
EXPORT_SYMBOL(input_class); |
#define INPUT_DEVICES 256 |
static LIST_HEAD(input_dev_list); |
static LIST_HEAD(input_handler_list); |
static struct input_handler *input_table[8]; |
#ifdef CONFIG_PROC_FS |
static struct proc_dir_entry *proc_bus_input_dir; |
DECLARE_WAIT_QUEUE_HEAD(input_devices_poll_wait); |
static int input_devices_state; |
#endif |
static inline unsigned int ms_to_jiffies(unsigned int ms) |
{ |
unsigned int j; |
j = (ms * HZ + 500) / 1000; |
return (j > 0) ? j : 1; |
} |
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) |
{ |
struct input_handle *handle; |
if (dev->pm_dev) |
pm_access(dev->pm_dev); |
if (type > EV_MAX || !test_bit(type, dev->evbit)) |
return; |
//!!!add_mouse_randomness((type << 4) ^ code ^ (code >> 4) ^ value); |
switch (type) { |
case EV_SYN: |
switch (code) { |
case SYN_CONFIG: |
if (dev->event) dev->event(dev, type, code, value); |
break; |
case SYN_REPORT: |
if (dev->sync) return; |
dev->sync = 1; |
break; |
} |
break; |
case EV_KEY: |
if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value) |
return; |
if (value == 2) |
break; |
change_bit(code, dev->key); |
if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->timer.data && value) { |
dev->repeat_key = code; |
mod_timer(&dev->timer, jiffies26 + ms_to_jiffies(dev->rep[REP_DELAY])); |
} |
break; |
case EV_ABS: |
if (code > ABS_MAX || !test_bit(code, dev->absbit)) |
return; |
if (dev->absfuzz[code]) { |
if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) && |
(value < dev->abs[code] + (dev->absfuzz[code] >> 1))) |
return; |
if ((value > dev->abs[code] - dev->absfuzz[code]) && |
(value < dev->abs[code] + dev->absfuzz[code])) |
value = (dev->abs[code] * 3 + value) >> 2; |
if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) && |
(value < dev->abs[code] + (dev->absfuzz[code] << 1))) |
value = (dev->abs[code] + value) >> 1; |
} |
if (dev->abs[code] == value) |
return; |
dev->abs[code] = value; |
break; |
case EV_REL: |
if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0)) |
return; |
break; |
case EV_MSC: |
if (code > MSC_MAX || !test_bit(code, dev->mscbit)) |
return; |
if (dev->event) dev->event(dev, type, code, value); |
break; |
case EV_LED: |
if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value) |
return; |
change_bit(code, dev->led); |
if (dev->event) dev->event(dev, type, code, value); |
break; |
case EV_SND: |
if (code > SND_MAX || !test_bit(code, dev->sndbit)) |
return; |
if (dev->event) dev->event(dev, type, code, value); |
break; |
case EV_REP: |
if (code > REP_MAX || value < 0 || dev->rep[code] == value) return; |
dev->rep[code] = value; |
if (dev->event) dev->event(dev, type, code, value); |
break; |
case EV_FF: |
if (dev->event) dev->event(dev, type, code, value); |
break; |
} |
if (type != EV_SYN) |
dev->sync = 0; |
if (dev->grab) |
dev->grab->handler->event(dev->grab, type, code, value); |
else |
list_for_each_entry(handle, &dev->h_list, d_node) |
if (handle->open) |
handle->handler->event(handle, type, code, value); |
} |
static void input_repeat_key(unsigned long data) |
{ |
struct input_dev *dev = (void *) data; |
if (!test_bit(dev->repeat_key, dev->key)) |
return; |
input_event(dev, EV_KEY, dev->repeat_key, 2); |
input_sync(dev); |
mod_timer(&dev->timer, jiffies26 + ms_to_jiffies(dev->rep[REP_PERIOD])); |
} |
int input_accept_process(struct input_handle *handle, struct file *file) |
{ |
if (handle->dev->accept) |
return handle->dev->accept(handle->dev, file); |
return 0; |
} |
int input_grab_device(struct input_handle *handle) |
{ |
if (handle->dev->grab) |
return -EBUSY; |
handle->dev->grab = handle; |
return 0; |
} |
void input_release_device(struct input_handle *handle) |
{ |
if (handle->dev->grab == handle) |
handle->dev->grab = NULL; |
} |
int input_open_device(struct input_handle *handle) |
{ |
if (handle->dev->pm_dev) |
pm_access(handle->dev->pm_dev); |
handle->open++; |
if (handle->dev->open) |
return handle->dev->open(handle->dev); |
return 0; |
} |
int input_flush_device(struct input_handle* handle, struct file* file) |
{ |
if (handle->dev->flush) |
return handle->dev->flush(handle->dev, file); |
return 0; |
} |
void input_close_device(struct input_handle *handle) |
{ |
input_release_device(handle); |
if (handle->dev->pm_dev) |
pm_dev_idle(handle->dev->pm_dev); |
if (handle->dev->close) |
handle->dev->close(handle->dev); |
handle->open--; |
} |
static void input_link_handle(struct input_handle *handle) |
{ |
list_add_tail(&handle->d_node, &handle->dev->h_list); |
list_add_tail(&handle->h_node, &handle->handler->h_list); |
} |
#define MATCH_BIT(bit, max) \ |
for (i = 0; i < NBITS(max); i++) \ |
if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \ |
break; \ |
if (i != NBITS(max)) \ |
continue; |
static struct input_device_id *input_match_device(struct input_device_id *id, struct input_dev *dev) |
{ |
int i; |
for (; id->flags || id->driver_info; id++) { |
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) |
if (id->id.bustype != dev->id.bustype) |
continue; |
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) |
if (id->id.vendor != dev->id.vendor) |
continue; |
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) |
if (id->id.product != dev->id.product) |
continue; |
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) |
if (id->id.version != dev->id.version) |
continue; |
MATCH_BIT(evbit, EV_MAX); |
MATCH_BIT(keybit, KEY_MAX); |
MATCH_BIT(relbit, REL_MAX); |
MATCH_BIT(absbit, ABS_MAX); |
MATCH_BIT(mscbit, MSC_MAX); |
MATCH_BIT(ledbit, LED_MAX); |
MATCH_BIT(sndbit, SND_MAX); |
MATCH_BIT(ffbit, FF_MAX); |
return id; |
} |
return NULL; |
} |
/* |
* Input hotplugging interface - loading event handlers based on |
* device bitfields. |
*/ |
#ifdef CONFIG_HOTPLUG |
/* |
* Input hotplugging invokes what /proc/sys/kernel/hotplug says |
* (normally /sbin/hotplug) when input devices get added or removed. |
* |
* This invokes a user mode policy agent, typically helping to load driver |
* or other modules, configure the device, and more. Drivers can provide |
* a MODULE_DEVICE_TABLE to help with module loading subtasks. |
* |
*/ |
#define SPRINTF_BIT_A(bit, name, max) \ |
do { \ |
envp[i++] = scratch; \ |
scratch += sprintf26(scratch, name); \ |
for (j = NBITS(max) - 1; j >= 0; j--) \ |
if (dev->bit[j]) break; \ |
for (; j >= 0; j--) \ |
scratch += sprintf26(scratch, "%lx ", dev->bit[j]); \ |
scratch++; \ |
} while (0) |
#define SPRINTF_BIT_A2(bit, name, max, ev) \ |
do { \ |
if (test_bit(ev, dev->evbit)) \ |
SPRINTF_BIT_A(bit, name, max); \ |
} while (0) |
static void input_call_hotplug(char *verb, struct input_dev *dev) |
{ |
char *argv[3], **envp, *buf, *scratch; |
int i = 0, j, value; |
if (!hotplug_path[0]) { |
printk(KERN_ERR "input.c: calling hotplug without a hotplug agent defined\n"); |
return; |
} |
if (in_interrupt()) { |
printk(KERN_ERR "input.c: calling hotplug from interrupt\n"); |
return; |
} |
if (!current->fs->root) { |
printk(KERN_WARNING "input.c: calling hotplug without valid filesystem\n"); |
return; |
} |
if (!(envp = (char **) kmalloc(20 * sizeof(char *), GFP_KERNEL))) { |
printk(KERN_ERR "input.c: not enough memory allocating hotplug environment\n"); |
return; |
} |
if (!(buf = kmalloc(1024, GFP_KERNEL))) { |
kfree (envp); |
printk(KERN_ERR "input.c: not enough memory allocating hotplug environment\n"); |
return; |
} |
argv[0] = hotplug_path; |
argv[1] = "input"; |
argv[2] = 0; |
envp[i++] = "HOME=/"; |
envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; |
scratch = buf; |
envp[i++] = scratch; |
scratch += sprintf26(scratch, "ACTION=%s", verb) + 1; |
envp[i++] = scratch; |
scratch += sprintf26(scratch, "PRODUCT=%x/%x/%x/%x", |
dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version) + 1; |
if (dev->name) { |
envp[i++] = scratch; |
scratch += sprintf26(scratch, "NAME=%s", dev->name) + 1; |
} |
if (dev->phys) { |
envp[i++] = scratch; |
scratch += sprintf26(scratch, "PHYS=%s", dev->phys) + 1; |
} |
SPRINTF_BIT_A(evbit, "EV=", EV_MAX); |
SPRINTF_BIT_A2(keybit, "KEY=", KEY_MAX, EV_KEY); |
SPRINTF_BIT_A2(relbit, "REL=", REL_MAX, EV_REL); |
SPRINTF_BIT_A2(absbit, "ABS=", ABS_MAX, EV_ABS); |
SPRINTF_BIT_A2(mscbit, "MSC=", MSC_MAX, EV_MSC); |
SPRINTF_BIT_A2(ledbit, "LED=", LED_MAX, EV_LED); |
SPRINTF_BIT_A2(sndbit, "SND=", SND_MAX, EV_SND); |
SPRINTF_BIT_A2(ffbit, "FF=", FF_MAX, EV_FF); |
envp[i++] = 0; |
#ifdef INPUT_DEBUG |
printk(KERN_DEBUG "input.c: calling %s %s [%s %s %s %s %s]\n", |
argv[0], argv[1], envp[0], envp[1], envp[2], envp[3], envp[4]); |
#endif |
value = call_usermodehelper(argv [0], argv, envp, 0); |
kfree(buf); |
kfree(envp); |
#ifdef INPUT_DEBUG |
if (value != 0) |
printk(KERN_DEBUG "input.c: hotplug returned %d\n", value); |
#endif |
} |
#endif |
void input_register_device(struct input_dev *dev) |
{ |
struct input_handle *handle; |
struct input_handler *handler; |
struct input_device_id *id; |
set_bit(EV_SYN, dev->evbit); |
/* |
* If delay and period are pre-set by the driver, then autorepeating |
* is handled by the driver itself and we don't do it in input.c. |
*/ |
init_timer(&dev->timer); |
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { |
dev->timer.data = (long) dev; |
dev->timer.function = input_repeat_key; |
dev->rep[REP_DELAY] = 250; |
dev->rep[REP_PERIOD] = 33; |
} |
INIT_LIST_HEAD(&dev->h_list); |
list_add_tail(&dev->node, &input_dev_list); |
list_for_each_entry(handler, &input_handler_list, node) |
if ((id = input_match_device(handler->id_table, dev))) |
if ((handle = handler->connect(handler, dev, id))) |
input_link_handle(handle); |
#ifdef CONFIG_HOTPLUG |
input_call_hotplug("add", dev); |
#endif |
#ifdef CONFIG_PROC_FS |
input_devices_state++; |
wake_up(&input_devices_poll_wait); |
#endif |
} |
void input_unregister_device(struct input_dev *dev) |
{ |
struct list_head * node, * next; |
if (!dev) return; |
if (dev->pm_dev) |
pm_unregister(dev->pm_dev); |
del_timer_sync(&dev->timer); |
list_for_each_safe(node, next, &dev->h_list) { |
struct input_handle * handle = to_handle(node); |
list_del_init(&handle->d_node); |
list_del_init(&handle->h_node); |
handle->handler->disconnect(handle); |
} |
#ifdef CONFIG_HOTPLUG |
input_call_hotplug("remove", dev); |
#endif |
list_del_init(&dev->node); |
#ifdef CONFIG_PROC_FS |
input_devices_state++; |
wake_up(&input_devices_poll_wait); |
#endif |
} |
void input_register_handler(struct input_handler *handler) |
{ |
struct input_dev *dev; |
struct input_handle *handle; |
struct input_device_id *id; |
if (!handler) return; |
INIT_LIST_HEAD(&handler->h_list); |
if (handler->fops != NULL) |
input_table[handler->minor >> 5] = handler; |
list_add_tail(&handler->node, &input_handler_list); |
list_for_each_entry(dev, &input_dev_list, node) |
if ((id = input_match_device(handler->id_table, dev))) |
if ((handle = handler->connect(handler, dev, id))) |
input_link_handle(handle); |
#ifdef CONFIG_PROC_FS |
input_devices_state++; |
wake_up(&input_devices_poll_wait); |
#endif |
} |
void input_unregister_handler(struct input_handler *handler) |
{ |
struct list_head * node, * next; |
list_for_each_safe(node, next, &handler->h_list) { |
struct input_handle * handle = to_handle_h(node); |
list_del_init(&handle->h_node); |
list_del_init(&handle->d_node); |
handler->disconnect(handle); |
} |
list_del_init(&handler->node); |
if (handler->fops != NULL) |
input_table[handler->minor >> 5] = NULL; |
#ifdef CONFIG_PROC_FS |
input_devices_state++; |
wake_up(&input_devices_poll_wait); |
#endif |
} |
static int input_open_file(struct inode *inode, struct file *file) |
{ |
struct input_handler *handler = input_table[iminor(inode) >> 5]; |
struct file_operations *old_fops, *new_fops = NULL; |
int err; |
/* No load-on-demand here? */ |
if (!handler || !(new_fops = fops_get(handler->fops))) |
return -ENODEV; |
/* |
* That's _really_ odd. Usually NULL ->open means "nothing special", |
* not "no device". Oh, well... |
*/ |
if (!new_fops->open) { |
fops_put(new_fops); |
return -ENODEV; |
} |
old_fops = file->f_op; |
file->f_op = new_fops; |
err = new_fops->open(inode, file); |
if (err) { |
fops_put(file->f_op); |
file->f_op = fops_get(old_fops); |
} |
fops_put(old_fops); |
return err; |
} |
static struct file_operations input_fops = { |
.owner = THIS_MODULE, |
.open = input_open_file, |
}; |
#ifdef CONFIG_PROC_FS |
#define SPRINTF_BIT_B(bit, name, max) \ |
do { \ |
len += sprintf26(buf + len, "B: %s", name); \ |
for (i = NBITS(max) - 1; i >= 0; i--) \ |
if (dev->bit[i]) break; \ |
for (; i >= 0; i--) \ |
len += sprintf26(buf + len, "%lx ", dev->bit[i]); \ |
len += sprintf26(buf + len, "\n"); \ |
} while (0) |
#define SPRINTF_BIT_B2(bit, name, max, ev) \ |
do { \ |
if (test_bit(ev, dev->evbit)) \ |
SPRINTF_BIT_B(bit, name, max); \ |
} while (0) |
static unsigned int input_devices_poll(struct file *file, poll_table *wait) |
{ |
int state = input_devices_state; |
poll_wait(file, &input_devices_poll_wait, wait); |
if (state != input_devices_state) |
return POLLIN | POLLRDNORM; |
return 0; |
} |
static int input_devices_read(char *buf, char **start, off_t pos, int count, int *eof, void *data) |
{ |
struct input_dev *dev; |
struct input_handle *handle; |
off_t at = 0; |
int i, len, cnt = 0; |
list_for_each_entry(dev, &input_dev_list, node) { |
len = sprintf26(buf, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n", |
dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version); |
len += sprintf26(buf + len, "N: Name=\"%s\"\n", dev->name ? dev->name : ""); |
len += sprintf26(buf + len, "P: Phys=%s\n", dev->phys ? dev->phys : ""); |
len += sprintf26(buf + len, "H: Handlers="); |
list_for_each_entry(handle, &dev->h_list, d_node) |
len += sprintf26(buf + len, "%s ", handle->name); |
len += sprintf26(buf + len, "\n"); |
SPRINTF_BIT_B(evbit, "EV=", EV_MAX); |
SPRINTF_BIT_B2(keybit, "KEY=", KEY_MAX, EV_KEY); |
SPRINTF_BIT_B2(relbit, "REL=", REL_MAX, EV_REL); |
SPRINTF_BIT_B2(absbit, "ABS=", ABS_MAX, EV_ABS); |
SPRINTF_BIT_B2(mscbit, "MSC=", MSC_MAX, EV_MSC); |
SPRINTF_BIT_B2(ledbit, "LED=", LED_MAX, EV_LED); |
SPRINTF_BIT_B2(sndbit, "SND=", SND_MAX, EV_SND); |
SPRINTF_BIT_B2(ffbit, "FF=", FF_MAX, EV_FF); |
len += sprintf26(buf + len, "\n"); |
at += len; |
if (at >= pos) { |
if (!*start) { |
*start = buf + (pos - (at - len)); |
cnt = at - pos; |
} else cnt += len; |
buf += len; |
if (cnt >= count) |
break; |
} |
} |
if (&dev->node == &input_dev_list) |
*eof = 1; |
return (count > cnt) ? cnt : count; |
} |
static int input_handlers_read(char *buf, char **start, off_t pos, int count, int *eof, void *data) |
{ |
struct input_handler *handler; |
off_t at = 0; |
int len = 0, cnt = 0; |
int i = 0; |
list_for_each_entry(handler, &input_handler_list, node) { |
if (handler->fops) |
len = sprintf26(buf, "N: Number=%d Name=%s Minor=%d\n", |
i++, handler->name, handler->minor); |
else |
len = sprintf26(buf, "N: Number=%d Name=%s\n", |
i++, handler->name); |
at += len; |
if (at >= pos) { |
if (!*start) { |
*start = buf + (pos - (at - len)); |
cnt = at - pos; |
} else cnt += len; |
buf += len; |
if (cnt >= count) |
break; |
} |
} |
if (&handler->node == &input_handler_list) |
*eof = 1; |
return (count > cnt) ? cnt : count; |
} |
static int __init input_proc_init(void) |
{ |
struct proc_dir_entry *entry; |
proc_bus_input_dir = proc_mkdir("input", proc_bus); |
if (proc_bus_input_dir == NULL) |
return -ENOMEM; |
proc_bus_input_dir->owner = THIS_MODULE; |
entry = create_proc_read_entry("devices", 0, proc_bus_input_dir, input_devices_read, NULL); |
if (entry == NULL) { |
remove_proc_entry("input", proc_bus); |
return -ENOMEM; |
} |
entry->owner = THIS_MODULE; |
entry->proc_fops->poll = input_devices_poll; |
entry = create_proc_read_entry("handlers", 0, proc_bus_input_dir, input_handlers_read, NULL); |
if (entry == NULL) { |
remove_proc_entry("devices", proc_bus_input_dir); |
remove_proc_entry("input", proc_bus); |
return -ENOMEM; |
} |
entry->owner = THIS_MODULE; |
return 0; |
} |
#else /* !CONFIG_PROC_FS */ |
static inline int input_proc_init(void) { return 0; } |
#endif |
struct class input_class = { |
.name = "input", |
}; |
/*static*/ int __init input_init(void) |
{ |
int retval = -ENOMEM; |
class_register(&input_class); |
input_proc_init(); |
retval = register_chrdev(INPUT_MAJOR, "input", &input_fops); |
if (retval) { |
printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR); |
remove_proc_entry("devices", proc_bus_input_dir); |
remove_proc_entry("handlers", proc_bus_input_dir); |
remove_proc_entry("input", proc_bus); |
return retval; |
} |
retval = devfs_mk_dir("input"); |
if (retval) { |
remove_proc_entry("devices", proc_bus_input_dir); |
remove_proc_entry("handlers", proc_bus_input_dir); |
remove_proc_entry("input", proc_bus); |
unregister_chrdev(INPUT_MAJOR, "input"); |
} |
return retval; |
} |
/*static*/ void __exit input_exit(void) |
{ |
remove_proc_entry("devices", proc_bus_input_dir); |
remove_proc_entry("handlers", proc_bus_input_dir); |
remove_proc_entry("input", proc_bus); |
devfs_remove("input"); |
unregister_chrdev(INPUT_MAJOR, "input"); |
class_unregister(&input_class); |
} |
subsys_initcall(input_init); |
module_exit(input_exit); |
/shark/trunk/drivers/input/serio/serport.c |
---|
0,0 → 1,220 |
/* |
* Input device TTY line discipline |
* |
* Copyright (c) 1999-2002 Vojtech Pavlik |
* |
* This is a module that converts a tty line into a much simpler |
* 'serial io port' abstraction that the input device drivers use. |
*/ |
/* |
* This program is free software; you can redistribute it and/or modify it |
* under the terms of the GNU General Public License version 2 as published by |
* the Free Software Foundation. |
*/ |
#include <linuxcomp.h> |
#include <asm/uaccess.h> |
#include <linux/kernel.h> |
#include <linux/slab.h> |
#include <linux/module.h> |
#include <linux/init.h> |
#include <linux/serio.h> |
#include <linux/tty.h> |
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); |
MODULE_DESCRIPTION("Input device TTY line discipline"); |
MODULE_LICENSE("GPL"); |
MODULE_ALIAS_LDISC(N_MOUSE); |
#define SERPORT_BUSY 1 |
struct serport { |
struct tty_struct *tty; |
wait_queue_head_t wait; |
struct serio serio; |
unsigned long flags; |
char phys[32]; |
}; |
char serport_name[] = "Serial port"; |
/* |
* Callback functions from the serio code. |
*/ |
static int serport_serio_write(struct serio *serio, unsigned char data) |
{ |
struct serport *serport = serio->driver; |
return -(serport->tty->driver->write(serport->tty, 0, &data, 1) != 1); |
} |
static int serport_serio_open(struct serio *serio) |
{ |
return 0; |
} |
static void serport_serio_close(struct serio *serio) |
{ |
struct serport *serport = serio->driver; |
serport->serio.type = 0; |
wake_up_interruptible(&serport->wait); |
} |
/* |
* serport_ldisc_open() is the routine that is called upon setting our line |
* discipline on a tty. It prepares the serio struct. |
*/ |
static int serport_ldisc_open(struct tty_struct *tty) |
{ |
struct serport *serport; |
char name[64]; |
serport = kmalloc(sizeof(struct serport), GFP_KERNEL); |
if (unlikely(!serport)) |
return -ENOMEM; |
memset(serport, 0, sizeof(struct serport)); |
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); |
serport->tty = tty; |
tty->disc_data = serport; |
snprintf26(serport->phys, sizeof(serport->phys), "%s/serio0", tty_name(tty, name)); |
serport->serio.name = serport_name; |
serport->serio.phys = serport->phys; |
serport->serio.type = SERIO_RS232; |
serport->serio.write = serport_serio_write; |
serport->serio.open = serport_serio_open; |
serport->serio.close = serport_serio_close; |
serport->serio.driver = serport; |
init_waitqueue_head(&serport->wait); |
return 0; |
} |
/* |
* serport_ldisc_close() is the opposite of serport_ldisc_open() |
*/ |
static void serport_ldisc_close(struct tty_struct *tty) |
{ |
struct serport *serport = (struct serport*) tty->disc_data; |
kfree(serport); |
} |
/* |
* serport_ldisc_receive() is called by the low level tty driver when characters |
* are ready for us. We forward the characters, one by one to the 'interrupt' |
* routine. |
* |
* FIXME: We should get pt_regs from the tty layer and forward them to |
* serio_interrupt here. |
*/ |
static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) |
{ |
struct serport *serport = (struct serport*) tty->disc_data; |
int i; |
for (i = 0; i < count; i++) |
serio_interrupt(&serport->serio, cp[i], 0, NULL); |
} |
/* |
* serport_ldisc_room() reports how much room we do have for receiving data. |
* Although we in fact have infinite room, we need to specify some value |
* here, and 256 seems to be reasonable. |
*/ |
static int serport_ldisc_room(struct tty_struct *tty) |
{ |
return 256; |
} |
/* |
* serport_ldisc_read() just waits indefinitely if everything goes well. |
* However, when the serio driver closes the serio port, it finishes, |
* returning 0 characters. |
*/ |
static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, unsigned char * buf, size_t nr) |
{ |
struct serport *serport = (struct serport*) tty->disc_data; |
char name[64]; |
if (test_and_set_bit(SERPORT_BUSY, &serport->flags)) |
return -EBUSY; |
serio_register_port(&serport->serio); |
printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty, name)); |
wait_event_interruptible(serport->wait, !serport->serio.type); |
serio_unregister_port(&serport->serio); |
clear_bit(SERPORT_BUSY, &serport->flags); |
return 0; |
} |
/* |
* serport_ldisc_ioctl() allows to set the port protocol, and device ID |
*/ |
static int serport_ldisc_ioctl(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg) |
{ |
struct serport *serport = (struct serport*) tty->disc_data; |
if (cmd == SPIOCSTYPE) |
return get_user(serport->serio.type, (unsigned long *) arg); |
return -EINVAL; |
} |
static void serport_ldisc_write_wakeup(struct tty_struct * tty) |
{ |
struct serport *sp = (struct serport *) tty->disc_data; |
serio_dev_write_wakeup(&sp->serio); |
} |
/* |
* The line discipline structure. |
*/ |
static struct tty_ldisc serport_ldisc = { |
.owner = THIS_MODULE, |
.name = "input", |
.open = serport_ldisc_open, |
.close = serport_ldisc_close, |
.read = serport_ldisc_read, |
.ioctl = serport_ldisc_ioctl, |
.receive_buf = serport_ldisc_receive, |
.receive_room = serport_ldisc_room, |
.write_wakeup = serport_ldisc_write_wakeup |
}; |
/* |
* The functions for insering/removing us as a module. |
*/ |
/*static*/ int __init serport_init(void) |
{ |
int retval; |
retval = tty_register_ldisc(N_MOUSE, &serport_ldisc); |
if (retval) |
printk(KERN_ERR "serport.c: Error registering line discipline.\n"); |
return retval; |
} |
/*static*/ void __exit serport_exit(void) |
{ |
tty_register_ldisc(N_MOUSE, NULL); |
} |
module_init(serport_init); |
module_exit(serport_exit); |
/shark/trunk/drivers/input/serio/i8042-io.h |
---|
0,0 → 1,87 |
#ifndef _I8042_IO_H |
#define _I8042_IO_H |
/* |
* This program is free software; you can redistribute it and/or modify it |
* under the terms of the GNU General Public License version 2 as published by |
* the Free Software Foundation. |
*/ |
/* |
* Names. |
*/ |
#define I8042_KBD_PHYS_DESC "isa0060/serio0" |
#define I8042_AUX_PHYS_DESC "isa0060/serio1" |
#define I8042_MUX_PHYS_DESC "isa0060/serio%d" |
/* |
* IRQs. |
*/ |
#ifdef __alpha__ |
# define I8042_KBD_IRQ 1 |
# define I8042_AUX_IRQ (RTC_PORT(0) == 0x170 ? 9 : 12) /* Jensen is special */ |
#elif defined(__ia64__) |
# define I8042_KBD_IRQ isa_irq_to_vector(1) |
# define I8042_AUX_IRQ isa_irq_to_vector(12) |
#else |
# define I8042_KBD_IRQ 1 |
# define I8042_AUX_IRQ 12 |
#endif |
/* |
* Register numbers. |
*/ |
#define I8042_COMMAND_REG 0x64 |
#define I8042_STATUS_REG 0x64 |
#define I8042_DATA_REG 0x60 |
static inline int i8042_read_data(void) |
{ |
return inb(I8042_DATA_REG); |
} |
static inline int i8042_read_status(void) |
{ |
return inb(I8042_STATUS_REG); |
} |
static inline void i8042_write_data(int val) |
{ |
outb(val, I8042_DATA_REG); |
return; |
} |
static inline void i8042_write_command(int val) |
{ |
outb(val, I8042_COMMAND_REG); |
return; |
} |
static inline int i8042_platform_init(void) |
{ |
/* |
* 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__) && !defined(__x86_64__) |
if (!request_region(I8042_DATA_REG, 16, "i8042")) |
return -1; |
#endif |
#if !defined(__i386__) && !defined(__x86_64__) |
i8042_reset = 1; |
#endif |
return 0; |
} |
static inline void i8042_platform_exit(void) |
{ |
#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__) && !defined(__x86_64__) |
release_region(I8042_DATA_REG, 16); |
#endif |
} |
#endif /* _I8042_IO_H */ |
/shark/trunk/drivers/input/serio/serio.c |
---|
0,0 → 1,273 |
/* |
* $Id: serio.c,v 1.1 2004-03-08 18:47:41 giacomo Exp $ |
* |
* Copyright (c) 1999-2001 Vojtech Pavlik |
*/ |
/* |
* The Serio abstraction module |
*/ |
/* |
* 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 <vojtech@ucw.cz>, or by paper mail: |
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic |
* |
* Changes: |
* 20 Jul. 2003 Daniele Bellucci <bellucda@tiscali.it> |
* Minor cleanups. |
*/ |
#include <linuxcomp.h> |
#include <linux/stddef.h> |
#include <linux/module.h> |
#include <linux/serio.h> |
#include <linux/errno.h> |
#include <linux/wait.h> |
#include <linux/completion.h> |
#include <linux/sched.h> |
#include <linux/smp_lock.h> |
#include <linux/suspend.h> |
#include <linux/slab.h> |
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); |
MODULE_DESCRIPTION("Serio abstraction core"); |
MODULE_LICENSE("GPL"); |
EXPORT_SYMBOL(serio_interrupt); |
EXPORT_SYMBOL(serio_register_port); |
EXPORT_SYMBOL(serio_register_slave_port); |
EXPORT_SYMBOL(serio_unregister_port); |
EXPORT_SYMBOL(serio_unregister_slave_port); |
EXPORT_SYMBOL(serio_register_device); |
EXPORT_SYMBOL(serio_unregister_device); |
EXPORT_SYMBOL(serio_open); |
EXPORT_SYMBOL(serio_close); |
EXPORT_SYMBOL(serio_rescan); |
struct serio_event { |
int type; |
struct serio *serio; |
struct list_head node; |
}; |
static DECLARE_MUTEX(serio_sem); |
static LIST_HEAD(serio_list); |
static LIST_HEAD(serio_dev_list); |
static LIST_HEAD(serio_event_list); |
static int serio_pid; |
static void serio_find_dev(struct serio *serio) |
{ |
struct serio_dev *dev; |
list_for_each_entry(dev, &serio_dev_list, node) { |
if (serio->dev) |
break; |
if (dev->connect) |
dev->connect(serio, dev); |
} |
} |
#define SERIO_RESCAN 1 |
static DECLARE_WAIT_QUEUE_HEAD(serio_wait); |
static DECLARE_COMPLETION(serio_exited); |
void serio_handle_events(void) |
{ |
struct list_head *node, *next; |
struct serio_event *event; |
list_for_each_safe(node, next, &serio_event_list) { |
event = container_of(node, struct serio_event, node); |
switch (event->type) { |
case SERIO_RESCAN : |
//!!!down(&serio_sem); |
if (event->serio->dev && event->serio->dev->disconnect) |
event->serio->dev->disconnect(event->serio); |
serio_find_dev(event->serio); |
//!!!up(&serio_sem); |
break; |
default: |
break; |
} |
list_del_init(node); |
kfree(event); |
} |
} |
static int serio_thread(void *nothing) |
{ |
/*lock_kernel(); |
daemonize("kseriod"); |
allow_signal(SIGTERM); |
do { |
serio_handle_events(); |
wait_event_interruptible(serio_wait, !list_empty(&serio_event_list)); |
if (current->flags & PF_FREEZE) |
refrigerator(PF_IOTHREAD); |
} while (!signal_pending(current)); |
printk(KERN_DEBUG "serio: kseriod exiting\n"); |
unlock_kernel(); |
complete_and_exit(&serio_exited, 0);*/ |
return 1; |
} |
void serio_rescan(struct serio *serio) |
{ |
struct serio_event *event; |
if (!(event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) |
return; |
event->type = SERIO_RESCAN; |
event->serio = serio; |
list_add_tail(&event->node, &serio_event_list); |
//!!!wake_up(&serio_wait); |
} |
irqreturn_t serio_interrupt(struct serio *serio, |
unsigned char data, unsigned int flags, struct pt_regs *regs) |
{ |
irqreturn_t ret = IRQ_NONE; |
if (serio->dev && serio->dev->interrupt) { |
ret = serio->dev->interrupt(serio, data, flags, regs); |
} else { |
if (!flags) { |
serio_rescan(serio); |
ret = IRQ_HANDLED; |
} |
} |
return ret; |
} |
void serio_register_port(struct serio *serio) |
{ |
//!!!down(&serio_sem); |
list_add_tail(&serio->node, &serio_list); |
serio_find_dev(serio); |
//!!!up(&serio_sem); |
} |
/* |
* Same as serio_register_port but does not try to acquire serio_sem. |
* Should be used when registering a serio from other input device's |
* connect() function. |
*/ |
void serio_register_slave_port(struct serio *serio) |
{ |
list_add_tail(&serio->node, &serio_list); |
serio_find_dev(serio); |
} |
void serio_unregister_port(struct serio *serio) |
{ |
//!!!down(&serio_sem); |
list_del_init(&serio->node); |
if (serio->dev && serio->dev->disconnect) |
serio->dev->disconnect(serio); |
//!!!up(&serio_sem); |
} |
/* |
* Same as serio_unregister_port but does not try to acquire serio_sem. |
* Should be used when unregistering a serio from other input device's |
* disconnect() function. |
*/ |
void serio_unregister_slave_port(struct serio *serio) |
{ |
list_del_init(&serio->node); |
if (serio->dev && serio->dev->disconnect) |
serio->dev->disconnect(serio); |
} |
void serio_register_device(struct serio_dev *dev) |
{ |
struct serio *serio; |
//!!!down(&serio_sem); |
list_add_tail(&dev->node, &serio_dev_list); |
list_for_each_entry(serio, &serio_list, node) |
if (!serio->dev && dev->connect) |
dev->connect(serio, dev); |
//!!!up(&serio_sem); |
} |
void serio_unregister_device(struct serio_dev *dev) |
{ |
struct serio *serio; |
//!!!down(&serio_sem); |
list_del_init(&dev->node); |
list_for_each_entry(serio, &serio_list, node) { |
if (serio->dev == dev && dev->disconnect) |
dev->disconnect(serio); |
serio_find_dev(serio); |
} |
//!!!up(&serio_sem); |
} |
/* called from serio_dev->connect/disconnect methods under serio_sem */ |
int serio_open(struct serio *serio, struct serio_dev *dev) |
{ |
serio->dev = dev; |
if (serio->open(serio)) { |
serio->dev = NULL; |
return -1; |
} |
return 0; |
} |
/* called from serio_dev->connect/disconnect methods under serio_sem */ |
void serio_close(struct serio *serio) |
{ |
serio->close(serio); |
serio->dev = NULL; |
} |
/*static*/ int __init serio_init(void) |
{ |
int pid = 1; |
//!!!pid = kernel_thread(serio_thread, NULL, CLONE_KERNEL); |
if (!pid) { |
printk(KERN_WARNING "serio: Failed to start kseriod\n"); |
return -1; |
} |
serio_pid = pid; |
return 0; |
} |
/*static*/ void __exit serio_exit(void) |
{ |
//!!!kill_proc(serio_pid, SIGTERM, 1); |
wait_for_completion(&serio_exited); |
} |
module_init(serio_init); |
module_exit(serio_exit); |
/shark/trunk/drivers/input/serio/i8042-ppcio.h |
---|
0,0 → 1,136 |
#ifndef _I8042_PPCIO_H |
#define _I8042_PPCIO_H |
/* |
* This program is free software; you can redistribute it and/or modify it |
* under the terms of the GNU General Public License version 2 as published by |
* the Free Software Foundation. |
*/ |
#if defined(CONFIG_WALNUT) |
#define I8042_KBD_IRQ 25 |
#define I8042_AUX_IRQ 26 |
#define I8042_KBD_PHYS_DESC "walnutps2/serio0" |
#define I8042_AUX_PHYS_DESC "walnutps2/serio1" |
#define I8042_MUX_PHYS_DESC "walnutps2/serio%d" |
extern void *kb_cs; |
extern void *kb_data; |
#define I8042_COMMAND_REG (*(int *)kb_cs) |
#define I8042_DATA_REG (*(int *)kb_data) |
static inline int i8042_read_data(void) |
{ |
return readb(kb_data); |
} |
static inline int i8042_read_status(void) |
{ |
return readb(kb_cs); |
} |
static inline void i8042_write_data(int val) |
{ |
writeb(val, kb_data); |
} |
static inline void i8042_write_command(int val) |
{ |
writeb(val, kb_cs); |
} |
static inline int i8042_platform_init(void) |
{ |
i8042_reset = 1; |
return 0; |
} |
static inline void i8042_platform_exit(void) |
{ |
} |
#elif defined(CONFIG_SPRUCE) |
#define I8042_KBD_IRQ 22 |
#define I8042_AUX_IRQ 21 |
#define I8042_KBD_PHYS_DESC "spruceps2/serio0" |
#define I8042_AUX_PHYS_DESC "spruceps2/serio1" |
#define I8042_MUX_PHYS_DESC "spruceps2/serio%d" |
#define I8042_COMMAND_REG 0xff810000 |
#define I8042_DATA_REG 0xff810001 |
static inline int i8042_read_data(void) |
{ |
unsigned long kbd_data; |
__raw_writel(0x00000088, 0xff500008); |
eieio(); |
__raw_writel(0x03000000, 0xff50000c); |
eieio(); |
asm volatile("lis 7,0xff88 \n\ |
lswi 6,7,0x8 \n\ |
mr %0,6" |
: "=r" (kbd_data) :: "6", "7"); |
__raw_writel(0x00000000, 0xff50000c); |
eieio(); |
return (unsigned char)(kbd_data >> 24); |
} |
static inline int i8042_read_status(void) |
{ |
unsigned long kbd_status; |
__raw_writel(0x00000088, 0xff500008); |
eieio(); |
__raw_writel(0x03000000, 0xff50000c); |
eieio(); |
asm volatile("lis 7,0xff88 \n\ |
ori 7,7,0x8 \n\ |
lswi 6,7,0x8 \n\ |
mr %0,6" |
: "=r" (kbd_status) :: "6", "7"); |
__raw_writel(0x00000000, 0xff50000c); |
eieio(); |
return (unsigned char)(kbd_status >> 24); |
} |
static inline void i8042_write_data(int val) |
{ |
*((unsigned char *)0xff810000) = (char)val; |
} |
static inline void i8042_write_command(int val) |
{ |
*((unsigned char *)0xff810001) = (char)val; |
} |
static inline int i8042_platform_init(void) |
{ |
i8042_reset = 1; |
return 0; |
} |
static inline void i8042_platform_exit(void) |
{ |
} |
#else |
#include "i8042-io.h" |
#endif |
#endif /* _I8042_PPCIO_H */ |
/shark/trunk/drivers/input/serio/i8042.c |
---|
0,0 → 1,851 |
/* |
* i8042 keyboard and mouse controller driver for Linux |
* |
* Copyright (c) 1999-2002 Vojtech Pavlik |
*/ |
/* |
* This program is free software; you can redistribute it and/or modify it |
* under the terms of the GNU General Public License version 2 as published by |
* the Free Software Foundation. |
*/ |
#include <linuxcomp.h> |
#include <linux/delay.h> |
#include <linux/module.h> |
#include <linux/interrupt.h> |
#include <linux/ioport.h> |
#include <linux/config.h> |
#include <linux/reboot.h> |
#include <linux/init.h> |
#include <linux/serio.h> |
#include <asm/io.h> |
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); |
MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver"); |
MODULE_LICENSE("GPL"); |
MODULE_PARM(i8042_noaux, "1i"); |
MODULE_PARM(i8042_nomux, "1i"); |
MODULE_PARM(i8042_unlock, "1i"); |
MODULE_PARM(i8042_reset, "1i"); |
MODULE_PARM(i8042_direct, "1i"); |
MODULE_PARM(i8042_dumbkbd, "1i"); |
static int i8042_reset; |
static int i8042_noaux; |
static int i8042_nomux; |
static int i8042_unlock; |
static int i8042_direct; |
static int i8042_dumbkbd; |
#undef DEBUG |
#include "i8042.h" |
spinlock_t i8042_lock = SPIN_LOCK_UNLOCKED; |
struct i8042_values { |
int irq; |
unsigned char disable; |
unsigned char irqen; |
unsigned char exists; |
signed char mux; |
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; |
static unsigned char i8042_mux_open; |
struct timer_list i8042_timer; |
/* |
* Shared IRQ's require a device pointer, but this driver doesn't support |
* multiple devices |
*/ |
#define i8042_request_irq_cookie (&i8042_timer) |
static irqreturn_t 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 ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) { |
udelay(50); |
i++; |
} |
return -(i == I8042_CTL_TIMEOUT); |
} |
static int i8042_wait_write(void) |
{ |
int i = 0; |
while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) { |
udelay(50); |
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; |
unsigned char data; |
int i = 0; |
spin_lock_irqsave(&i8042_lock, flags); |
while ((i8042_read_status() & I8042_STR_OBF) && (i++ < I8042_BUFFER_SIZE)) { |
data = i8042_read_data(); |
dbg("%02x <- i8042 (flush, %s)", data, |
i8042_read_status() & I8042_STR_AUXDATA ? "aux" : "kbd"); |
} |
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) { |
dbg("%02x -> i8042 (command)", command & 0xff); |
i8042_write_command(command & 0xff); |
} |
if (!retval) |
for (i = 0; i < ((command >> 12) & 0xf); i++) { |
if ((retval = i8042_wait_write())) break; |
dbg("%02x -> i8042 (parameter)", param[i]); |
i8042_write_data(param[i]); |
} |
if (!retval) |
for (i = 0; i < ((command >> 8) & 0xf); i++) { |
if ((retval = i8042_wait_read())) break; |
if (i8042_read_status() & I8042_STR_AUXDATA) |
param[i] = ~i8042_read_data(); |
else |
param[i] = i8042_read_data(); |
dbg("%02x <- i8042 (return)", param[i]); |
} |
spin_unlock_irqrestore(&i8042_lock, flags); |
if (retval) |
dbg(" -- i8042 (timeout)"); |
return retval; |
} |
/* |
* i8042_kbd_write() sends a byte out through the keyboard interface. |
*/ |
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())) { |
dbg("%02x -> i8042 (kbd-data)", c); |
i8042_write_data(c); |
} |
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) |
{ |
struct i8042_values *values = port->driver; |
int retval; |
/* |
* Send the byte out. |
*/ |
if (values->mux == -1) |
retval = i8042_command(&c, I8042_CMD_AUX_SEND); |
else |
retval = i8042_command(&c, I8042_CMD_MUX_SEND + values->mux); |
/* |
* 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, NULL, NULL); |
return retval; |
} |
/* |
* i8042_open() is called when a port is open by the higher layer. |
* It allocates the interrupt and enables it in the chip. |
*/ |
static int i8042_open(struct serio *port) |
{ |
struct i8042_values *values = port->driver; |
i8042_flush(); |
if (values->mux != -1) |
if (i8042_mux_open++) |
return 0; |
if (request_irq(values->irq, i8042_interrupt, |
SA_SHIRQ, "i8042", i8042_request_irq_cookie)) { |
printk(KERN_ERR "i8042.c: Can't get irq %d for %s, unregistering the port.\n", values->irq, values->name); |
values->exists = 0; |
serio_unregister_port(port); |
return -1; |
} |
i8042_ctr |= values->irqen; |
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; |
} |
i8042_interrupt(0, NULL, NULL); |
return 0; |
} |
/* |
* i8042_close() frees the interrupt, so that it can possibly be used |
* by another driver. We never know - if the user doesn't have a mouse, |
* the BIOS could have used the AUX interrupt for PCI. |
*/ |
static void i8042_close(struct serio *port) |
{ |
struct i8042_values *values = port->driver; |
if (values->mux != -1) |
if (--i8042_mux_open) |
return; |
i8042_ctr &= ~values->irqen; |
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_irq(values->irq, i8042_request_irq_cookie); |
i8042_flush(); |
} |
/* |
* Structures for registering the devices in the serio.c module. |
*/ |
static struct i8042_values i8042_kbd_values = { |
.irqen = I8042_CTR_KBDINT, |
.disable = I8042_CTR_KBDDIS, |
.name = "KBD", |
.mux = -1, |
}; |
static struct serio i8042_kbd_port = |
{ |
.type = SERIO_8042_XL, |
.write = i8042_kbd_write, |
.open = i8042_open, |
.close = i8042_close, |
.driver = &i8042_kbd_values, |
.name = "i8042 Kbd Port", |
.phys = I8042_KBD_PHYS_DESC, |
}; |
static struct i8042_values i8042_aux_values = { |
.irqen = I8042_CTR_AUXINT, |
.disable = I8042_CTR_AUXDIS, |
.name = "AUX", |
.mux = -1, |
}; |
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 = I8042_AUX_PHYS_DESC, |
}; |
static struct i8042_values i8042_mux_values[4]; |
static struct serio i8042_mux_port[4]; |
static char i8042_mux_names[4][32]; |
static char i8042_mux_short[4][16]; |
static char i8042_mux_phys[4][32]; |
/* |
* 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 irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs) |
{ |
unsigned long flags; |
unsigned char str, data; |
unsigned int dfl; |
struct { |
int data; |
int str; |
} buffer[I8042_BUFFER_SIZE]; |
int i, j = 0; |
spin_lock_irqsave(&i8042_lock, flags); |
while (j < I8042_BUFFER_SIZE && |
(buffer[j].str = i8042_read_status()) & I8042_STR_OBF) |
buffer[j++].data = i8042_read_data(); |
spin_unlock_irqrestore(&i8042_lock, flags); |
for (i = 0; i < j; i++) { |
str = buffer[i].str; |
data = buffer[i].data; |
dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) | |
((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0); |
if (i8042_mux_values[0].exists && (str & I8042_STR_AUXDATA)) { |
if (str & I8042_STR_MUXERR) { |
switch (data) { |
case 0xfd: |
case 0xfe: dfl = SERIO_TIMEOUT; break; |
case 0xff: dfl = SERIO_PARITY; break; |
} |
data = 0xfe; |
} else dfl = 0; |
dbg("%02x <- i8042 (interrupt, aux%d, %d%s%s)", |
data, (str >> 6), irq, |
dfl & SERIO_PARITY ? ", bad parity" : "", |
dfl & SERIO_TIMEOUT ? ", timeout" : ""); |
serio_interrupt(i8042_mux_port + ((str >> 6) & 3), data, dfl, regs); |
continue; |
} |
dbg("%02x <- i8042 (interrupt, %s, %d%s%s)", |
data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", irq, |
dfl & SERIO_PARITY ? ", bad parity" : "", |
dfl & SERIO_TIMEOUT ? ", timeout" : ""); |
if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) { |
serio_interrupt(&i8042_aux_port, data, dfl, regs); |
continue; |
} |
if (!i8042_kbd_values.exists) |
continue; |
serio_interrupt(&i8042_kbd_port, data, dfl, regs); |
} |
return IRQ_RETVAL(j); |
} |
/* |
* i8042_controller init initializes the i8042 controller, and, |
* most importantly, sets it into non-xlated mode if that's |
* desired. |
*/ |
static int __init i8042_controller_init(void) |
{ |
/* |
* 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; |
} |
} |
/* |
* Save the CTR for restoral on unload / reboot. |
*/ |
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) { |
printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n"); |
return -1; |
} |
i8042_initial_ctr = i8042_ctr; |
/* |
* Disable the keyboard interface and interrupt. |
*/ |
i8042_ctr |= I8042_CTR_KBDDIS; |
i8042_ctr &= ~I8042_CTR_KBDINT; |
/* |
* Handle keylock. |
*/ |
if (~i8042_read_status() & I8042_STR_KEYLOCK) { |
if (i8042_unlock) |
i8042_ctr |= I8042_CTR_IGNKEYLOCK; |
else |
printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n"); |
} |
/* |
* If the chip is configured into nontranslated mode by the BIOS, don't |
* bother enabling translating and be happy. |
*/ |
if (~i8042_ctr & I8042_CTR_XLATE) |
i8042_direct = 1; |
/* |
* Set nontranslated mode for the kbd interface if requested by an option. |
* After this the kbd interface becomes a simple serial in/out, like the aux |
* interface is. We don't do this by default, since it can confuse notebook |
* BIOSes. |
*/ |
if (i8042_direct) { |
i8042_ctr &= ~I8042_CTR_XLATE; |
i8042_kbd_port.type = SERIO_8042; |
} |
/* |
* 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) |
{ |
int i; |
i8042_flush(); |
/* |
* Reset anything that is connected to the ports. |
*/ |
if (i8042_kbd_values.exists) |
serio_cleanup(&i8042_kbd_port); |
if (i8042_aux_values.exists) |
serio_cleanup(&i8042_aux_port); |
for (i = 0; i < 4; i++) |
if (i8042_mux_values[i].exists) |
serio_cleanup(i8042_mux_port + i); |
/* |
* 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"); |
} |
/* |
* i8042_check_mux() checks whether the controller supports the PS/2 Active |
* Multiplexing specification by Synaptics, Phoenix, Insyde and |
* LCS/Telegraphics. |
*/ |
static int __init i8042_check_mux(struct i8042_values *values) |
{ |
unsigned char param; |
static int i8042_check_mux_cookie; |
int i; |
/* |
* Check if AUX irq is available. |
*/ |
if (request_irq(values->irq, i8042_interrupt, SA_SHIRQ, |
"i8042", &i8042_check_mux_cookie)) |
return -1; |
free_irq(values->irq, &i8042_check_mux_cookie); |
/* |
* Get rid of bytes in the queue. |
*/ |
i8042_flush(); |
/* |
* Internal loopback test - send three bytes, they should come back from the |
* mouse interface, the last should be version. Note that we negate mouseport |
* command responses for the i8042_check_aux() routine. |
*/ |
param = 0xf0; |
if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != 0x0f) |
return -1; |
param = 0x56; |
if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != 0xa9) |
return -1; |
param = 0xa4; |
if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param == 0x5b) |
return -1; |
printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n", |
(~param >> 4) & 0xf, ~param & 0xf); |
/* |
* Disable all muxed ports by disabling AUX. |
*/ |
i8042_ctr |= I8042_CTR_AUXDIS; |
i8042_ctr &= ~I8042_CTR_AUXINT; |
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) |
return -1; |
/* |
* Enable all muxed ports. |
*/ |
for (i = 0; i < 4; i++) { |
i8042_command(¶m, I8042_CMD_MUX_PFX + i); |
i8042_command(¶m, I8042_CMD_AUX_ENABLE); |
} |
return 0; |
} |
/* |
* 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) |
{ |
unsigned char param; |
static int i8042_check_aux_cookie; |
/* |
* Check if AUX irq is available. If it isn't, then there is no point |
* in trying to detect AUX presence. |
*/ |
if (request_irq(values->irq, i8042_interrupt, SA_SHIRQ, |
"i8042", &i8042_check_aux_cookie)) |
return -1; |
free_irq(values->irq, &i8042_check_aux_cookie); |
/* |
* Get rid of bytes in the queue. |
*/ |
i8042_flush(); |
/* |
* Internal loopback test - filters out AT-type i8042's. Unfortunately |
* SiS screwed up and their 5597 doesn't support the LOOP command even |
* though it has an AUX port. |
*/ |
param = 0x5a; |
if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != 0xa5) { |
/* |
* External connection test - filters out AT-soldered PS/2 i8042's |
* 0x00 - no error, 0x01-0x03 - clock/data stuck, 0xff - general error |
* 0xfa - no error on some notebooks which ignore the spec |
* Because it's common for chipsets to return error on perfectly functioning |
* AUX ports, we test for this only when the LOOP command failed. |
*/ |
if (i8042_command(¶m, I8042_CMD_AUX_TEST) |
|| (param && param != 0xfa && param != 0xff)) |
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_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; |
i8042_ctr &= ~values->disable; |
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { |
printk(KERN_WARNING "i8042.c: Can't write CTR while registering.\n"); |
return -1; |
} |
serio_register_port(port); |
printk(KERN_INFO "serio: i8042 %s port at %#lx,%#lx irq %d\n", |
values->name, |
(unsigned long) I8042_DATA_REG, |
(unsigned long) I8042_COMMAND_REG, |
values->irq); |
return 0; |
} |
static void i8042_timer_func(unsigned long data) |
{ |
i8042_interrupt(0, NULL, NULL); |
mod_timer(&i8042_timer, jiffies26 + I8042_POLL_PERIOD); |
} |
#ifndef MODULE |
static int __init i8042_setup_reset(char *str) |
{ |
i8042_reset = 1; |
return 1; |
} |
static int __init i8042_setup_noaux(char *str) |
{ |
i8042_noaux = 1; |
i8042_nomux = 1; |
return 1; |
} |
static int __init i8042_setup_nomux(char *str) |
{ |
i8042_nomux = 1; |
return 1; |
} |
static int __init i8042_setup_unlock(char *str) |
{ |
i8042_unlock = 1; |
return 1; |
} |
static int __init i8042_setup_direct(char *str) |
{ |
i8042_direct = 1; |
return 1; |
} |
static int __init i8042_setup_dumbkbd(char *str) |
{ |
i8042_dumbkbd = 1; |
return 1; |
} |
__setup("i8042_reset", i8042_setup_reset); |
__setup("i8042_noaux", i8042_setup_noaux); |
__setup("i8042_nomux", i8042_setup_nomux); |
__setup("i8042_unlock", i8042_setup_unlock); |
__setup("i8042_direct", i8042_setup_direct); |
__setup("i8042_dumbkbd", i8042_setup_dumbkbd); |
#endif |
/* |
* We need to reset the 8042 back to original mode on system shutdown, |
* because otherwise BIOSes will be confused. |
*/ |
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 |
}; |
static void __init i8042_init_mux_values(struct i8042_values *values, struct serio *port, int index) |
{ |
memcpy(port, &i8042_aux_port, sizeof(struct serio)); |
memcpy(values, &i8042_aux_values, sizeof(struct i8042_values)); |
sprintf26(i8042_mux_names[index], "i8042 Aux-%d Port", index); |
sprintf26(i8042_mux_phys[index], I8042_MUX_PHYS_DESC, index + 1); |
sprintf26(i8042_mux_short[index], "AUX%d", index); |
port->name = i8042_mux_names[index]; |
port->phys = i8042_mux_phys[index]; |
port->driver = values; |
values->name = i8042_mux_short[index]; |
values->mux = index; |
} |
int __init i8042_init(void) |
{ |
int i; |
dbg_init(); |
if (i8042_platform_init()) |
return -EBUSY; |
i8042_aux_values.irq = I8042_AUX_IRQ; |
i8042_kbd_values.irq = I8042_KBD_IRQ; |
if (i8042_controller_init()) |
return -ENODEV; |
if (i8042_dumbkbd) |
i8042_kbd_port.write = NULL; |
for (i = 0; i < 4; i++) |
i8042_init_mux_values(i8042_mux_values + i, i8042_mux_port + i, i); |
if (!i8042_nomux && !i8042_check_mux(&i8042_aux_values)) |
for (i = 0; i < 4; i++) |
i8042_port_register(i8042_mux_values + i, i8042_mux_port + i); |
else |
if (!i8042_noaux && !i8042_check_aux(&i8042_aux_values)) |
i8042_port_register(&i8042_aux_values, &i8042_aux_port); |
i8042_port_register(&i8042_kbd_values, &i8042_kbd_port); |
init_timer(&i8042_timer); |
i8042_timer.function = i8042_timer_func; |
mod_timer(&i8042_timer, jiffies26 + I8042_POLL_PERIOD); |
//!!!register_reboot_notifier(&i8042_notifier); |
return 0; |
} |
void __exit i8042_exit(void) |
{ |
int i; |
//!!!unregister_reboot_notifier(&i8042_notifier); |
del_timer(&i8042_timer); |
i8042_controller_cleanup(); |
if (i8042_kbd_values.exists) |
serio_unregister_port(&i8042_kbd_port); |
if (i8042_aux_values.exists) |
serio_unregister_port(&i8042_aux_port); |
for (i = 0; i < 4; i++) |
if (i8042_mux_values[i].exists) |
serio_unregister_port(i8042_mux_port + i); |
i8042_platform_exit(); |
} |
module_init(i8042_init); |
module_exit(i8042_exit); |
/shark/trunk/drivers/input/serio/i8042-sparcio.h |
---|
0,0 → 1,116 |
#ifndef _I8042_SPARCIO_H |
#define _I8042_SPARCIO_H |
#include <linux/config.h> |
#include <asm/io.h> |
#ifdef CONFIG_PCI |
#include <asm/oplib.h> |
#include <asm/ebus.h> |
#endif |
static int i8042_kbd_irq = -1; |
static int i8042_aux_irq = -1; |
#define I8042_KBD_IRQ i8042_kbd_irq |
#define I8042_AUX_IRQ i8042_aux_irq |
#define I8042_KBD_PHYS_DESC "sparcps2/serio0" |
#define I8042_AUX_PHYS_DESC "sparcps2/serio1" |
#define I8042_MUX_PHYS_DESC "sparcps2/serio%d" |
static unsigned long kbd_iobase; |
#define I8042_COMMAND_REG (kbd_iobase + 0x64UL) |
#define I8042_DATA_REG (kbd_iobase + 0x60UL) |
static inline int i8042_read_data(void) |
{ |
return readb(kbd_iobase + 0x60UL); |
} |
static inline int i8042_read_status(void) |
{ |
return readb(kbd_iobase + 0x64UL); |
} |
static inline void i8042_write_data(int val) |
{ |
writeb(val, kbd_iobase + 0x60UL); |
} |
static inline void i8042_write_command(int val) |
{ |
writeb(val, kbd_iobase + 0x64UL); |
} |
#define OBP_PS2KBD_NAME1 "kb_ps2" |
#define OBP_PS2KBD_NAME2 "keyboard" |
#define OBP_PS2MS_NAME1 "kdmouse" |
#define OBP_PS2MS_NAME2 "mouse" |
static int i8042_platform_init(void) |
{ |
#ifndef CONFIG_PCI |
return -1; |
#else |
char prop[128]; |
int len; |
len = prom_getproperty(prom_root_node, "name", prop, sizeof(prop)); |
if (len < 0) { |
printk("i8042: Cannot get name property of root OBP node.\n"); |
return -1; |
} |
if (strncmp(prop, "SUNW,JavaStation-1", len) == 0) { |
/* Hardcoded values for MrCoffee. */ |
i8042_kbd_irq = i8042_aux_irq = 13 | 0x20; |
kbd_iobase = (unsigned long) ioremap(0x71300060, 8); |
if (!kbd_iobase) |
return -1; |
} else { |
struct linux_ebus *ebus; |
struct linux_ebus_device *edev; |
struct linux_ebus_child *child; |
for_each_ebus(ebus) { |
for_each_ebusdev(edev, ebus) { |
if (!strcmp(edev->prom_name, "8042")) |
goto edev_found; |
} |
} |
return -1; |
edev_found: |
for_each_edevchild(edev, child) { |
if (!strcmp(child->prom_name, OBP_PS2KBD_NAME1) || |
!strcmp(child->prom_name, OBP_PS2KBD_NAME2)) { |
i8042_kbd_irq = child->irqs[0]; |
kbd_iobase = (unsigned long) |
ioremap(child->resource[0].start, 8); |
} |
if (!strcmp(child->prom_name, OBP_PS2MS_NAME1) || |
!strcmp(child->prom_name, OBP_PS2MS_NAME2)) |
i8042_aux_irq = child->irqs[0]; |
} |
if (i8042_kbd_irq == -1 || |
i8042_aux_irq == -1) { |
printk("i8042: Error, 8042 device lacks both kbd and " |
"mouse nodes.\n"); |
return -1; |
} |
} |
i8042_reset = 1; |
return 0; |
#endif /* CONFIG_PCI */ |
} |
static inline void i8042_platform_exit(void) |
{ |
#ifdef CONFIG_PCI |
iounmap((void *)kbd_iobase); |
#endif |
} |
#endif /* _I8042_SPARCIO_H */ |
/shark/trunk/drivers/input/serio/i8042.h |
---|
0,0 → 1,114 |
#ifndef _I8042_H |
#define _I8042_H |
/* |
* Copyright (c) 1999-2002 Vojtech Pavlik |
* |
* This program is free software; you can redistribute it and/or modify it |
* under the terms of the GNU General Public License version 2 as published by |
* the Free Software Foundation. |
*/ |
/* |
* Arch-dependent inline functions and defines. |
*/ |
#if defined(CONFIG_PPC) |
#include "i8042-ppcio.h" |
#elif defined(CONFIG_SPARC32) || defined(CONFIG_SPARC64) |
#include "i8042-sparcio.h" |
#else |
#include "i8042-io.h" |
#endif |
/* |
* This is in 50us units, the time we wait for the i8042 to react. This |
* has to be long enough for the i8042 itself to timeout on sending a byte |
* to a non-existent mouse. |
*/ |
#define I8042_CTL_TIMEOUT 10000 |
/* |
* When the device isn't opened and it's interrupts aren't used, we poll it at |
* regular intervals to see if any characters arrived. If yes, we can start |
* probing for any mouse / keyboard connected. This is the period of the |
* polling. |
*/ |
#define I8042_POLL_PERIOD HZ/20 |
/* |
* 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_MUXERR 0x04 |
#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 |
#define I8042_CMD_MUX_PFX 0x0090 |
#define I8042_CMD_MUX_SEND 0x1090 |
/* |
* 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 |
/* |
* Debug. |
*/ |
#ifdef DEBUG |
static unsigned long i8042_start; |
#define dbg_init() do { i8042_start = jiffies26; } while (0) |
#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format " [%d]\n" ,\ |
## arg, (int) (jiffies26 - i8042_start)) |
#else |
#define dbg_init() do { } while (0) |
#define dbg(format, arg...) do {} while (0) |
#endif |
#endif /* _I8042_H */ |
/shark/trunk/drivers/input/joydev.c |
---|
0,0 → 1,512 |
/* |
* Joystick device driver for the input driver suite. |
* |
* Copyright (c) 1999-2002 Vojtech Pavlik |
* Copyright (c) 1999 Colin Van Dyke |
* |
* 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. |
*/ |
#include <linuxcomp.h> |
#include <asm/io.h> |
#include <asm/system.h> |
#include <linux/delay.h> |
#include <linux/errno.h> |
#include <linux/joystick.h> |
#include <linux/input.h> |
#include <linux/kernel.h> |
#include <linux/major.h> |
#include <linux/slab.h> |
#include <linux/mm.h> |
#include <linux/miscdevice.h> |
#include <linux/module.h> |
#include <linux/poll.h> |
#include <linux/init.h> |
#include <linux/smp_lock.h> |
#include <linux/device.h> |
#include <linux/devfs_fs_kernel.h> |
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); |
MODULE_DESCRIPTION("Joystick device interfaces"); |
MODULE_SUPPORTED_DEVICE("input/js"); |
MODULE_LICENSE("GPL"); |
#define JOYDEV_MINOR_BASE 0 |
#define JOYDEV_MINORS 16 |
#define JOYDEV_BUFFER_SIZE 64 |
#define MSECS(t) (1000 * ((t) / HZ) + 1000 * ((t) % HZ) / HZ) |
struct joydev { |
int exist; |
int open; |
int minor; |
char name[16]; |
struct input_handle handle; |
wait_queue_head_t wait; |
struct list_head list; |
struct js_corr corr[ABS_MAX]; |
struct JS_DATA_SAVE_TYPE glue; |
int nabs; |
int nkey; |
__u16 keymap[KEY_MAX - BTN_MISC]; |
__u16 keypam[KEY_MAX - BTN_MISC]; |
__u8 absmap[ABS_MAX]; |
__u8 abspam[ABS_MAX]; |
__s16 abs[ABS_MAX]; |
}; |
struct joydev_list { |
struct js_event buffer[JOYDEV_BUFFER_SIZE]; |
int head; |
int tail; |
int startup; |
struct fasync_struct *fasync; |
struct joydev *joydev; |
struct list_head node; |
}; |
static struct joydev *joydev_table[JOYDEV_MINORS]; |
static int joydev_correct(int value, struct js_corr *corr) |
{ |
switch (corr->type) { |
case JS_CORR_NONE: |
break; |
case JS_CORR_BROKEN: |
value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : |
((corr->coef[3] * (value - corr->coef[1])) >> 14)) : |
((corr->coef[2] * (value - corr->coef[0])) >> 14); |
break; |
default: |
return 0; |
} |
if (value < -32767) return -32767; |
if (value > 32767) return 32767; |
return value; |
} |
static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) |
{ |
struct joydev *joydev = handle->private; |
struct joydev_list *list; |
struct js_event event; |
switch (type) { |
case EV_KEY: |
if (code < BTN_MISC || value == 2) return; |
event.type = JS_EVENT_BUTTON; |
event.number = joydev->keymap[code - BTN_MISC]; |
event.value = value; |
break; |
case EV_ABS: |
event.type = JS_EVENT_AXIS; |
event.number = joydev->absmap[code]; |
event.value = joydev_correct(value, joydev->corr + event.number); |
if (event.value == joydev->abs[event.number]) return; |
joydev->abs[event.number] = event.value; |
break; |
default: |
return; |
} |
event.time = MSECS(jiffies); |
list_for_each_entry(list, &joydev->list, node) { |
memcpy(list->buffer + list->head, &event, sizeof(struct js_event)); |
if (list->startup == joydev->nabs + joydev->nkey) |
if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1))) |
list->startup = 0; |
kill_fasync(&list->fasync, SIGIO, POLL_IN); |
} |
wake_up_interruptible(&joydev->wait); |
} |
static int joydev_fasync(int fd, struct file *file, int on) |
{ |
int retval; |
struct joydev_list *list = file->private_data; |
retval = fasync_helper(fd, file, on, &list->fasync); |
return retval < 0 ? retval : 0; |
} |
static void joydev_free(struct joydev *joydev) |
{ |
devfs_remove("js%d", joydev->minor); |
joydev_table[joydev->minor] = NULL; |
kfree(joydev); |
} |
static int joydev_release(struct inode * inode, struct file * file) |
{ |
struct joydev_list *list = file->private_data; |
joydev_fasync(-1, file, 0); |
list_del(&list->node); |
if (!--list->joydev->open) { |
if (list->joydev->exist) |
input_close_device(&list->joydev->handle); |
else |
joydev_free(list->joydev); |
} |
kfree(list); |
return 0; |
} |
static int joydev_open(struct inode *inode, struct file *file) |
{ |
struct joydev_list *list; |
int i = iminor(inode) - JOYDEV_MINOR_BASE; |
if (i >= JOYDEV_MINORS || !joydev_table[i]) |
return -ENODEV; |
if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL))) |
return -ENOMEM; |
memset(list, 0, sizeof(struct joydev_list)); |
list->joydev = joydev_table[i]; |
list_add_tail(&list->node, &joydev_table[i]->list); |
file->private_data = list; |
if (!list->joydev->open++) |
if (list->joydev->exist) |
input_open_device(&list->joydev->handle); |
return 0; |
} |
static ssize_t joydev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) |
{ |
return -EINVAL; |
} |
static ssize_t joydev_read(struct file *file, char *buf, size_t count, loff_t *ppos) |
{ |
struct joydev_list *list = file->private_data; |
struct joydev *joydev = list->joydev; |
struct input_dev *input = joydev->handle.dev; |
int retval = 0; |
if (!list->joydev->exist) |
return -ENODEV; |
if (count < sizeof(struct js_event)) |
return -EINVAL; |
if (count == sizeof(struct JS_DATA_TYPE)) { |
struct JS_DATA_TYPE data; |
int i; |
for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++) |
data.buttons |= test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0; |
data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x; |
data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y; |
if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE))) |
return -EFAULT; |
list->startup = 0; |
list->tail = list->head; |
return sizeof(struct JS_DATA_TYPE); |
} |
if (list->startup == joydev->nabs + joydev->nkey |
&& list->head == list->tail && (file->f_flags & O_NONBLOCK)) |
return -EAGAIN; |
retval = wait_event_interruptible(list->joydev->wait, list->joydev->exist |
&& (list->startup < joydev->nabs + joydev->nkey || list->head != list->tail)); |
if (retval) |
return retval; |
if (!list->joydev->exist) |
return -ENODEV; |
while (list->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) { |
struct js_event event; |
event.time = MSECS(jiffies); |
if (list->startup < joydev->nkey) { |
event.type = JS_EVENT_BUTTON | JS_EVENT_INIT; |
event.number = list->startup; |
event.value = !!test_bit(joydev->keypam[event.number], input->key); |
} else { |
event.type = JS_EVENT_AXIS | JS_EVENT_INIT; |
event.number = list->startup - joydev->nkey; |
event.value = joydev->abs[event.number]; |
} |
if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) |
return -EFAULT; |
list->startup++; |
retval += sizeof(struct js_event); |
} |
while (list->head != list->tail && retval + sizeof(struct js_event) <= count) { |
if (copy_to_user(buf + retval, list->buffer + list->tail, sizeof(struct js_event))) |
return -EFAULT; |
list->tail = (list->tail + 1) & (JOYDEV_BUFFER_SIZE - 1); |
retval += sizeof(struct js_event); |
} |
return retval; |
} |
/* No kernel lock - fine */ |
static unsigned int joydev_poll(struct file *file, poll_table *wait) |
{ |
struct joydev_list *list = file->private_data; |
poll_wait(file, &list->joydev->wait, wait); |
if (list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey) |
return POLLIN | POLLRDNORM; |
return 0; |
} |
static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) |
{ |
struct joydev_list *list = file->private_data; |
struct joydev *joydev = list->joydev; |
struct input_dev *dev = joydev->handle.dev; |
int i; |
if (!joydev->exist) return -ENODEV; |
switch (cmd) { |
case JS_SET_CAL: |
return copy_from_user(&joydev->glue.JS_CORR, (struct JS_DATA_TYPE *) arg, |
sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; |
case JS_GET_CAL: |
return copy_to_user((struct JS_DATA_TYPE *) arg, &joydev->glue.JS_CORR, |
sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; |
case JS_SET_TIMEOUT: |
return get_user(joydev->glue.JS_TIMEOUT, (int *) arg); |
case JS_GET_TIMEOUT: |
return put_user(joydev->glue.JS_TIMEOUT, (int *) arg); |
case JS_SET_TIMELIMIT: |
return get_user(joydev->glue.JS_TIMELIMIT, (long *) arg); |
case JS_GET_TIMELIMIT: |
return put_user(joydev->glue.JS_TIMELIMIT, (long *) arg); |
case JS_SET_ALL: |
return copy_from_user(&joydev->glue, (struct JS_DATA_SAVE_TYPE *) arg, |
sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; |
case JS_GET_ALL: |
return copy_to_user((struct JS_DATA_SAVE_TYPE *) arg, &joydev->glue, |
sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; |
case JSIOCGVERSION: |
return put_user(JS_VERSION, (__u32 *) arg); |
case JSIOCGAXES: |
return put_user(joydev->nabs, (__u8 *) arg); |
case JSIOCGBUTTONS: |
return put_user(joydev->nkey, (__u8 *) arg); |
case JSIOCSCORR: |
return copy_from_user(joydev->corr, (struct js_corr *) arg, |
sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0; |
case JSIOCGCORR: |
return copy_to_user((struct js_corr *) arg, joydev->corr, |
sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0; |
case JSIOCSAXMAP: |
if (copy_from_user(joydev->abspam, (__u8 *) arg, sizeof(__u8) * ABS_MAX)) |
return -EFAULT; |
for (i = 0; i < joydev->nabs; i++) { |
if (joydev->abspam[i] > ABS_MAX) return -EINVAL; |
joydev->absmap[joydev->abspam[i]] = i; |
} |
return 0; |
case JSIOCGAXMAP: |
return copy_to_user((__u8 *) arg, joydev->abspam, |
sizeof(__u8) * ABS_MAX) ? -EFAULT : 0; |
case JSIOCSBTNMAP: |
if (copy_from_user(joydev->keypam, (__u16 *) arg, sizeof(__u16) * (KEY_MAX - BTN_MISC))) |
return -EFAULT; |
for (i = 0; i < joydev->nkey; i++) { |
if (joydev->keypam[i] > KEY_MAX || joydev->keypam[i] < BTN_MISC) return -EINVAL; |
joydev->keymap[joydev->keypam[i] - BTN_MISC] = i; |
} |
return 0; |
case JSIOCGBTNMAP: |
return copy_to_user((__u16 *) arg, joydev->keypam, |
sizeof(__u16) * (KEY_MAX - BTN_MISC)) ? -EFAULT : 0; |
default: |
if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { |
int len; |
if (!dev->name) return 0; |
len = strlen(dev->name) + 1; |
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); |
if (copy_to_user((char *) arg, dev->name, len)) return -EFAULT; |
return len; |
} |
} |
return -EINVAL; |
} |
static struct file_operations joydev_fops = { |
.owner = THIS_MODULE, |
.read = joydev_read, |
.write = joydev_write, |
.poll = joydev_poll, |
.open = joydev_open, |
.release = joydev_release, |
.ioctl = joydev_ioctl, |
.fasync = joydev_fasync, |
}; |
static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id) |
{ |
struct joydev *joydev; |
int i, j, t, minor; |
/* Avoid tablets */ |
if (test_bit(EV_KEY, dev->evbit) && test_bit(BTN_TOUCH, dev->keybit)) |
return NULL; |
for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++); |
if (minor == JOYDEV_MINORS) { |
printk(KERN_ERR "joydev: no more free joydev devices\n"); |
return NULL; |
} |
if (!(joydev = kmalloc(sizeof(struct joydev), GFP_KERNEL))) |
return NULL; |
memset(joydev, 0, sizeof(struct joydev)); |
INIT_LIST_HEAD(&joydev->list); |
init_waitqueue_head(&joydev->wait); |
joydev->minor = minor; |
joydev->exist = 1; |
joydev->handle.dev = dev; |
joydev->handle.name = joydev->name; |
joydev->handle.handler = handler; |
joydev->handle.private = joydev; |
sprintf26(joydev->name, "js%d", minor); |
for (i = 0; i < ABS_MAX; i++) |
if (test_bit(i, dev->absbit)) { |
joydev->absmap[i] = joydev->nabs; |
joydev->abspam[joydev->nabs] = i; |
joydev->nabs++; |
} |
for (i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC; i++) |
if (test_bit(i + BTN_MISC, dev->keybit)) { |
joydev->keymap[i] = joydev->nkey; |
joydev->keypam[joydev->nkey] = i + BTN_MISC; |
joydev->nkey++; |
} |
for (i = 0; i < BTN_JOYSTICK - BTN_MISC; i++) |
if (test_bit(i + BTN_MISC, dev->keybit)) { |
joydev->keymap[i] = joydev->nkey; |
joydev->keypam[joydev->nkey] = i + BTN_MISC; |
joydev->nkey++; |
} |
for (i = 0; i < joydev->nabs; i++) { |
j = joydev->abspam[i]; |
if (dev->absmax[j] == dev->absmin[j]) { |
joydev->corr[i].type = JS_CORR_NONE; |
continue; |
} |
joydev->corr[i].type = JS_CORR_BROKEN; |
joydev->corr[i].prec = dev->absfuzz[j]; |
joydev->corr[i].coef[0] = (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j]; |
joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j]; |
if (!(t = ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]))) |
continue; |
joydev->corr[i].coef[2] = (1 << 29) / t; |
joydev->corr[i].coef[3] = (1 << 29) / t; |
joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i); |
} |
joydev_table[minor] = joydev; |
devfs_mk_cdev(MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor), |
S_IFCHR|S_IRUGO|S_IWUSR, "js%d", minor); |
return &joydev->handle; |
} |
static void joydev_disconnect(struct input_handle *handle) |
{ |
struct joydev *joydev = handle->private; |
joydev->exist = 0; |
if (joydev->open) |
input_close_device(handle); |
else |
joydev_free(joydev); |
} |
static struct input_device_id joydev_ids[] = { |
{ |
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, |
.evbit = { BIT(EV_ABS) }, |
.absbit = { BIT(ABS_X) }, |
}, |
{ |
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, |
.evbit = { BIT(EV_ABS) }, |
.absbit = { BIT(ABS_WHEEL) }, |
}, |
{ |
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, |
.evbit = { BIT(EV_ABS) }, |
.absbit = { BIT(ABS_THROTTLE) }, |
}, |
{ }, /* Terminating entry */ |
}; |
MODULE_DEVICE_TABLE(input, joydev_ids); |
static struct input_handler joydev_handler = { |
.event = joydev_event, |
.connect = joydev_connect, |
.disconnect = joydev_disconnect, |
.fops = &joydev_fops, |
.minor = JOYDEV_MINOR_BASE, |
.name = "joydev", |
.id_table = joydev_ids, |
}; |
/*static*/ int __init joydev_init(void) |
{ |
input_register_handler(&joydev_handler); |
return 0; |
} |
/*static*/ void __exit joydev_exit(void) |
{ |
input_unregister_handler(&joydev_handler); |
} |
module_init(joydev_init); |
module_exit(joydev_exit); |
/shark/trunk/drivers/input/mouse/logips2pp.c |
---|
0,0 → 1,232 |
/* |
* Logitech PS/2++ mouse driver |
* |
* Copyright (c) 1999-2003 Vojtech Pavlik <vojtech@suse.cz> |
* Copyright (c) 2003 Eric Wong <eric@yhbt.net> |
* |
* This program is free software; you can redistribute it and/or modify it |
* under the terms of the GNU General Public License version 2 as published by |
* the Free Software Foundation. |
*/ |
#include <linuxcomp.h> |
#include <linux/input.h> |
#include "psmouse.h" |
#include "logips2pp.h" |
#define DUBUG |
/* |
* Process a PS2++ or PS2T++ packet. |
*/ |
void ps2pp_process_packet(struct psmouse *psmouse) |
{ |
struct input_dev *dev = &psmouse->dev; |
unsigned char *packet = psmouse->packet; |
if ((packet[0] & 0x48) == 0x48 && (packet[1] & 0x02) == 0x02) { |
switch ((packet[1] >> 4) | (packet[0] & 0x30)) { |
case 0x0d: /* Mouse extra info */ |
input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL, |
(int) (packet[2] & 8) - (int) (packet[2] & 7)); |
input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1); |
input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1); |
break; |
case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */ |
input_report_key(dev, BTN_SIDE, (packet[2]) & 1); |
input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1); |
input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1); |
input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1); |
input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1); |
break; |
case 0x0f: /* TouchPad extra info */ |
input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL, |
(int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7)); |
packet[0] = packet[2] | 0x08; |
break; |
#ifdef DEBUG |
default: |
printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n", |
(packet[1] >> 4) | (packet[0] & 0x30)); |
#endif |
} |
packet[0] &= 0x0f; |
packet[1] = 0; |
packet[2] = 0; |
} |
} |
/* |
* ps2pp_cmd() sends a PS2++ command, sliced into two bit |
* pieces through the SETRES command. This is needed to send extended |
* commands to mice on notebooks that try to understand the PS/2 protocol |
* Ugly. |
*/ |
static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command) |
{ |
unsigned char d; |
int i; |
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) |
return -1; |
for (i = 6; i >= 0; i -= 2) { |
d = (command >> i) & 3; |
if(psmouse_command(psmouse, &d, PSMOUSE_CMD_SETRES)) |
return -1; |
} |
if (psmouse_command(psmouse, param, PSMOUSE_CMD_POLL)) |
return -1; |
return 0; |
} |
/* |
* SmartScroll / CruiseControl for some newer Logitech mice Defaults to |
* enabled if we do nothing to it. Of course I put this in because I want it |
* disabled :P |
* 1 - enabled (if previously disabled, also default) |
* 0/2 - disabled |
*/ |
static void ps2pp_set_smartscroll(struct psmouse *psmouse) |
{ |
unsigned char param[4]; |
ps2pp_cmd(psmouse, param, 0x32); |
param[0] = 0; |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); |
if (psmouse_smartscroll == 1) |
param[0] = 1; |
else |
if (psmouse_smartscroll > 2) |
return; |
/* else leave param[0] == 0 to disable */ |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); |
} |
/* |
* Support 800 dpi resolution _only_ if the user wants it (there are good |
* reasons to not use it even if the mouse supports it, and of course there are |
* also good reasons to use it, let the user decide). |
*/ |
void ps2pp_set_800dpi(struct psmouse *psmouse) |
{ |
unsigned char param = 3; |
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11); |
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11); |
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11); |
psmouse_command(psmouse, ¶m, PSMOUSE_CMD_SETRES); |
} |
/* |
* Detect the exact model and features of a PS2++ or PS2T++ Logitech mouse or |
* touchpad. |
*/ |
int ps2pp_detect_model(struct psmouse *psmouse, unsigned char *param) |
{ |
int i; |
static int logitech_4btn[] = { 12, 40, 41, 42, 43, 52, 73, 80, -1 }; |
static int logitech_wheel[] = { 52, 53, 75, 76, 80, 81, 83, 88, 112, -1 }; |
static int logitech_ps2pp[] = { 12, 13, 40, 41, 42, 43, 50, 51, 52, 53, 73, 75, |
76, 80, 81, 83, 88, 96, 97, 112, -1 }; |
static int logitech_mx[] = { 112, -1 }; |
psmouse->vendor = "Logitech"; |
psmouse->model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78); |
if (param[1] < 3) |
clear_bit(BTN_MIDDLE, psmouse->dev.keybit); |
if (param[1] < 2) |
clear_bit(BTN_RIGHT, psmouse->dev.keybit); |
psmouse->type = PSMOUSE_PS2; |
for (i = 0; logitech_ps2pp[i] != -1; i++) |
if (logitech_ps2pp[i] == psmouse->model) |
psmouse->type = PSMOUSE_PS2PP; |
if (psmouse->type == PSMOUSE_PS2PP) { |
for (i = 0; logitech_4btn[i] != -1; i++) |
if (logitech_4btn[i] == psmouse->model) |
set_bit(BTN_SIDE, psmouse->dev.keybit); |
for (i = 0; logitech_wheel[i] != -1; i++) |
if (logitech_wheel[i] == psmouse->model) { |
set_bit(REL_WHEEL, psmouse->dev.relbit); |
psmouse->name = "Wheel Mouse"; |
} |
for (i = 0; logitech_mx[i] != -1; i++) |
if (logitech_mx[i] == psmouse->model) { |
set_bit(BTN_SIDE, psmouse->dev.keybit); |
set_bit(BTN_EXTRA, psmouse->dev.keybit); |
set_bit(BTN_BACK, psmouse->dev.keybit); |
set_bit(BTN_FORWARD, psmouse->dev.keybit); |
set_bit(BTN_TASK, psmouse->dev.keybit); |
psmouse->name = "MX Mouse"; |
} |
/* |
* Do Logitech PS2++ / PS2T++ magic init. |
*/ |
if (psmouse->model == 97) { /* TouchPad 3 */ |
set_bit(REL_WHEEL, psmouse->dev.relbit); |
set_bit(REL_HWHEEL, psmouse->dev.relbit); |
param[0] = 0x11; param[1] = 0x04; param[2] = 0x68; /* Unprotect RAM */ |
psmouse_command(psmouse, param, 0x30d1); |
param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b; /* Enable features */ |
psmouse_command(psmouse, param, 0x30d1); |
param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3; /* Enable PS2++ */ |
psmouse_command(psmouse, param, 0x30d1); |
param[0] = 0; |
if (!psmouse_command(psmouse, param, 0x13d1) && |
param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) { |
psmouse->name = "TouchPad 3"; |
return PSMOUSE_PS2TPP; |
} |
} else { |
param[0] = param[1] = param[2] = 0; |
ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */ |
ps2pp_cmd(psmouse, param, 0xDB); |
if ((param[0] & 0x78) == 0x48 && (param[1] & 0xf3) == 0xc2 && |
(param[2] & 3) == ((param[1] >> 2) & 3)) { |
ps2pp_set_smartscroll(psmouse); |
return PSMOUSE_PS2PP; |
} |
} |
} |
return 0; |
} |
/shark/trunk/drivers/input/mouse/psmouse.h |
---|
0,0 → 1,59 |
#ifndef _PSMOUSE_H |
#define _PSMOUSE_H |
#define PSMOUSE_CMD_SETSCALE11 0x00e6 |
#define PSMOUSE_CMD_SETRES 0x10e8 |
#define PSMOUSE_CMD_GETINFO 0x03e9 |
#define PSMOUSE_CMD_SETSTREAM 0x00ea |
#define PSMOUSE_CMD_POLL 0x03eb |
#define PSMOUSE_CMD_GETID 0x02f2 |
#define PSMOUSE_CMD_SETRATE 0x10f3 |
#define PSMOUSE_CMD_ENABLE 0x00f4 |
#define PSMOUSE_CMD_RESET_DIS 0x00f6 |
#define PSMOUSE_CMD_RESET_BAT 0x02ff |
#define PSMOUSE_RET_BAT 0xaa |
#define PSMOUSE_RET_ID 0x00 |
#define PSMOUSE_RET_ACK 0xfa |
#define PSMOUSE_RET_NAK 0xfe |
/* psmouse states */ |
#define PSMOUSE_NEW_DEVICE 0 |
#define PSMOUSE_ACTIVATED 1 |
#define PSMOUSE_IGNORE 2 |
struct psmouse { |
void *private; |
struct input_dev dev; |
struct serio *serio; |
char *vendor; |
char *name; |
unsigned char cmdbuf[8]; |
unsigned char packet[8]; |
unsigned char cmdcnt; |
unsigned char pktcnt; |
unsigned char type; |
unsigned char model; |
unsigned long last; |
unsigned char state; |
char acking; |
volatile char ack; |
char error; |
char devname[64]; |
char phys[32]; |
}; |
#define PSMOUSE_PS2 1 |
#define PSMOUSE_PS2PP 2 |
#define PSMOUSE_PS2TPP 3 |
#define PSMOUSE_GENPS 4 |
#define PSMOUSE_IMPS 5 |
#define PSMOUSE_IMEX 6 |
#define PSMOUSE_SYNAPTICS 7 |
int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command); |
extern int psmouse_smartscroll; |
extern unsigned int psmouse_resetafter; |
#endif /* _PSMOUSE_H */ |
/shark/trunk/drivers/input/mouse/sermouse.c |
---|
0,0 → 1,312 |
/* |
* $Id: sermouse.c,v 1.1 2004-03-08 18:47:40 giacomo Exp $ |
* |
* Copyright (c) 1999-2001 Vojtech Pavlik |
*/ |
/* |
* Serial mouse 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 <vojtech@ucw.cz>, or by paper mail: |
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic |
*/ |
#include <linuxcomp.h> |
#include <linux/delay.h> |
#include <linux/module.h> |
#include <linux/slab.h> |
#include <linux/interrupt.h> |
#include <linux/input.h> |
#include <linux/config.h> |
#include <linux/serio.h> |
#include <linux/init.h> |
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); |
MODULE_DESCRIPTION("Serial mouse driver"); |
MODULE_LICENSE("GPL"); |
static char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse", |
"Logitech M+ Mouse", "Microsoft MZ Mouse", "Logitech MZ+ Mouse", |
"Logitech MZ++ Mouse"}; |
struct sermouse { |
struct input_dev dev; |
signed char buf[8]; |
unsigned char count; |
unsigned char type; |
unsigned long last; |
char phys[32]; |
}; |
/* |
* sermouse_process_msc() analyzes the incoming MSC/Sun bytestream and |
* applies some prediction to the data, resulting in 96 updates per |
* second, which is as good as a PS/2 or USB mouse. |
*/ |
static void sermouse_process_msc(struct sermouse *sermouse, signed char data, struct pt_regs *regs) |
{ |
struct input_dev *dev = &sermouse->dev; |
signed char *buf = sermouse->buf; |
input_regs(dev, regs); |
switch (sermouse->count) { |
case 0: |
if ((data & 0xf8) != 0x80) return; |
input_report_key(dev, BTN_LEFT, !(data & 4)); |
input_report_key(dev, BTN_RIGHT, !(data & 1)); |
input_report_key(dev, BTN_MIDDLE, !(data & 2)); |
break; |
case 1: |
case 3: |
input_report_rel(dev, REL_X, data / 2); |
input_report_rel(dev, REL_Y, -buf[1]); |
buf[0] = data - data / 2; |
break; |
case 2: |
case 4: |
input_report_rel(dev, REL_X, buf[0]); |
input_report_rel(dev, REL_Y, buf[1] - data); |
buf[1] = data / 2; |
break; |
} |
input_sync(dev); |
if (++sermouse->count == (5 - ((sermouse->type == SERIO_SUN) << 1))) |
sermouse->count = 0; |
} |
/* |
* sermouse_process_ms() anlyzes the incoming MS(Z/+/++) bytestream and |
* generates events. With prediction it gets 80 updates/sec, assuming |
* standard 3-byte packets and 1200 bps. |
*/ |
static void sermouse_process_ms(struct sermouse *sermouse, signed char data, struct pt_regs *regs) |
{ |
struct input_dev *dev = &sermouse->dev; |
signed char *buf = sermouse->buf; |
if (data & 0x40) sermouse->count = 0; |
input_regs(dev, regs); |
switch (sermouse->count) { |
case 0: |
buf[1] = data; |
input_report_key(dev, BTN_LEFT, (data >> 5) & 1); |
input_report_key(dev, BTN_RIGHT, (data >> 4) & 1); |
break; |
case 1: |
buf[2] = data; |
data = (signed char) (((buf[1] << 6) & 0xc0) | (data & 0x3f)); |
input_report_rel(dev, REL_X, data / 2); |
input_report_rel(dev, REL_Y, buf[4]); |
buf[3] = data - data / 2; |
break; |
case 2: |
/* Guessing the state of the middle button on 3-button MS-protocol mice - ugly. */ |
if ((sermouse->type == SERIO_MS) && !data && !buf[2] && !((buf[0] & 0xf0) ^ buf[1])) |
input_report_key(dev, BTN_MIDDLE, !test_bit(BTN_MIDDLE, dev->key)); |
buf[0] = buf[1]; |
data = (signed char) (((buf[1] << 4) & 0xc0) | (data & 0x3f)); |
input_report_rel(dev, REL_X, buf[3]); |
input_report_rel(dev, REL_Y, data - buf[4]); |
buf[4] = data / 2; |
break; |
case 3: |
switch (sermouse->type) { |
case SERIO_MS: |
sermouse->type = SERIO_MP; |
case SERIO_MP: |
if ((data >> 2) & 3) break; /* M++ Wireless Extension packet. */ |
input_report_key(dev, BTN_MIDDLE, (data >> 5) & 1); |
input_report_key(dev, BTN_SIDE, (data >> 4) & 1); |
break; |
case SERIO_MZP: |
case SERIO_MZPP: |
input_report_key(dev, BTN_SIDE, (data >> 5) & 1); |
case SERIO_MZ: |
input_report_key(dev, BTN_MIDDLE, (data >> 4) & 1); |
input_report_rel(dev, REL_WHEEL, (data & 8) - (data & 7)); |
break; |
} |
break; |
case 4: |
case 6: /* MZ++ packet type. We can get these bytes for M++ too but we ignore them later. */ |
buf[1] = (data >> 2) & 0x0f; |
break; |
case 5: |
case 7: /* Ignore anything besides MZ++ */ |
if (sermouse->type != SERIO_MZPP) break; |
switch (buf[1]) { |
case 1: /* Extra mouse info */ |
input_report_key(dev, BTN_SIDE, (data >> 4) & 1); |
input_report_key(dev, BTN_EXTRA, (data >> 5) & 1); |
input_report_rel(dev, data & 0x80 ? REL_HWHEEL : REL_WHEEL, (data & 7) - (data & 8)); |
break; |
default: /* We don't decode anything else yet. */ |
printk(KERN_WARNING |
"sermouse.c: Received MZ++ packet %x, don't know how to handle.\n", buf[1]); |
break; |
} |
break; |
} |
input_sync(dev); |
sermouse->count++; |
} |
/* |
* sermouse_interrupt() handles incoming characters, either gathering them into |
* packets or passing them to the command routine as command output. |
*/ |
static irqreturn_t sermouse_interrupt(struct serio *serio, |
unsigned char data, unsigned int flags, struct pt_regs *regs) |
{ |
struct sermouse *sermouse = serio->private; |
if (time_after(jiffies26, sermouse->last + HZ/10)) sermouse->count = 0; |
sermouse->last = jiffies26; |
if (sermouse->type > SERIO_SUN) |
sermouse_process_ms(sermouse, data, regs); |
else |
sermouse_process_msc(sermouse, data, regs); |
return IRQ_HANDLED; |
} |
/* |
* sermouse_disconnect() cleans up after we don't want talk |
* to the mouse anymore. |
*/ |
static void sermouse_disconnect(struct serio *serio) |
{ |
struct sermouse *sermouse = serio->private; |
input_unregister_device(&sermouse->dev); |
serio_close(serio); |
kfree(sermouse); |
} |
/* |
* sermouse_connect() is a callback form the serio module when |
* an unhandled serio port is found. |
*/ |
static void sermouse_connect(struct serio *serio, struct serio_dev *dev) |
{ |
struct sermouse *sermouse; |
unsigned char c; |
if ((serio->type & SERIO_TYPE) != SERIO_RS232) |
return; |
if (!(serio->type & SERIO_PROTO) || ((serio->type & SERIO_PROTO) > SERIO_MZPP)) |
return; |
if (!(sermouse = kmalloc(sizeof(struct sermouse), GFP_KERNEL))) |
return; |
memset(sermouse, 0, sizeof(struct sermouse)); |
init_input_dev(&sermouse->dev); |
sermouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); |
sermouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT); |
sermouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); |
sermouse->dev.private = sermouse; |
serio->private = sermouse; |
sermouse->type = serio->type & SERIO_PROTO; |
c = (serio->type & SERIO_EXTRA) >> 16; |
if (c & 0x01) set_bit(BTN_MIDDLE, sermouse->dev.keybit); |
if (c & 0x02) set_bit(BTN_SIDE, sermouse->dev.keybit); |
if (c & 0x04) set_bit(BTN_EXTRA, sermouse->dev.keybit); |
if (c & 0x10) set_bit(REL_WHEEL, sermouse->dev.relbit); |
if (c & 0x20) set_bit(REL_HWHEEL, sermouse->dev.relbit); |
sprintf26(sermouse->phys, "%s/input0", serio->phys); |
sermouse->dev.name = sermouse_protocols[sermouse->type]; |
sermouse->dev.phys = sermouse->phys; |
sermouse->dev.id.bustype = BUS_RS232; |
sermouse->dev.id.vendor = sermouse->type; |
sermouse->dev.id.product = c; |
sermouse->dev.id.version = 0x0100; |
if (serio_open(serio, dev)) { |
kfree(sermouse); |
return; |
} |
input_register_device(&sermouse->dev); |
printk(KERN_INFO "input: %s on %s\n", sermouse_protocols[sermouse->type], serio->phys); |
} |
static struct serio_dev sermouse_dev = { |
.interrupt = sermouse_interrupt, |
.connect = sermouse_connect, |
.disconnect = sermouse_disconnect |
}; |
int __init sermouse_init(void) |
{ |
serio_register_device(&sermouse_dev); |
return 0; |
} |
void __exit sermouse_exit(void) |
{ |
serio_unregister_device(&sermouse_dev); |
} |
module_init(sermouse_init); |
module_exit(sermouse_exit); |
/shark/trunk/drivers/input/mouse/psmouse-base.c |
---|
0,0 → 1,676 |
/* |
* PS/2 mouse driver |
* |
* Copyright (c) 1999-2002 Vojtech Pavlik |
*/ |
/* |
* This program is free software; you can redistribute it and/or modify it |
* under the terms of the GNU General Public License version 2 as published by |
* the Free Software Foundation. |
*/ |
#include <linuxcomp.h> |
#include <linux/delay.h> |
#include <linux/module.h> |
#include <linux/slab.h> |
#include <linux/interrupt.h> |
#include <linux/input.h> |
#include <linux/serio.h> |
#include <linux/init.h> |
#include <linux/pm.h> |
#include "psmouse.h" |
//#include "synaptics.h" |
#include "logips2pp.h" |
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); |
MODULE_DESCRIPTION("PS/2 mouse driver"); |
MODULE_PARM(psmouse_noext, "1i"); |
MODULE_PARM_DESC(psmouse_noext, "Disable any protocol extensions. Useful for KVM switches."); |
MODULE_PARM(psmouse_resolution, "i"); |
MODULE_PARM_DESC(psmouse_resolution, "Resolution, in dpi."); |
MODULE_PARM(psmouse_rate, "i"); |
MODULE_PARM_DESC(psmouse_rate, "Report rate, in reports per second."); |
MODULE_PARM(psmouse_smartscroll, "i"); |
MODULE_PARM_DESC(psmouse_smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled."); |
MODULE_PARM(psmouse_resetafter, "i"); |
MODULE_PARM_DESC(psmouse_resetafter, "Reset Synaptics Touchpad after so many bad packets (0 = never)."); |
MODULE_LICENSE("GPL"); |
static int psmouse_noext; |
int psmouse_resolution = 200; |
unsigned int psmouse_rate = 100; |
int psmouse_smartscroll = 1; |
unsigned int psmouse_resetafter; |
static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "PS2T++", "GenPS/2", "ImPS/2", "ImExPS/2", "SynPS/2"}; |
/* |
* psmouse_process_packet() analyzes the PS/2 mouse packet contents and |
* reports relevant events to the input module. |
*/ |
static void psmouse_process_packet(struct psmouse *psmouse, struct pt_regs *regs) |
{ |
struct input_dev *dev = &psmouse->dev; |
unsigned char *packet = psmouse->packet; |
input_regs(dev, regs); |
/* |
* The PS2++ protocol is a little bit complex |
*/ |
if (psmouse->type == PSMOUSE_PS2PP || psmouse->type == PSMOUSE_PS2TPP) |
ps2pp_process_packet(psmouse); |
/* |
* Scroll wheel on IntelliMice, scroll buttons on NetMice |
*/ |
if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS) |
input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); |
/* |
* Scroll wheel and buttons on IntelliMouse Explorer |
*/ |
if (psmouse->type == PSMOUSE_IMEX) { |
input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7)); |
input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1); |
input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1); |
} |
/* |
* Extra buttons on Genius NewNet 3D |
*/ |
if (psmouse->type == PSMOUSE_GENPS) { |
input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1); |
input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1); |
} |
/* |
* Generic PS/2 Mouse |
*/ |
input_report_key(dev, BTN_LEFT, packet[0] & 1); |
input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); |
input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); |
input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0); |
input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0); |
input_sync(dev); |
} |
/* |
* psmouse_interrupt() handles incoming characters, either gathering them into |
* packets or passing them to the command routine as command output. |
*/ |
static irqreturn_t psmouse_interrupt(struct serio *serio, |
unsigned char data, unsigned int flags, struct pt_regs *regs) |
{ |
struct psmouse *psmouse = serio->private; |
if (psmouse->state == PSMOUSE_IGNORE) |
goto out; |
if (psmouse->acking) { |
switch (data) { |
case PSMOUSE_RET_ACK: |
psmouse->ack = 1; |
break; |
case PSMOUSE_RET_NAK: |
psmouse->ack = -1; |
break; |
default: |
psmouse->ack = 1; /* Workaround for mice which don't ACK the Get ID command */ |
if (psmouse->cmdcnt) |
psmouse->cmdbuf[--psmouse->cmdcnt] = data; |
break; |
} |
psmouse->acking = 0; |
goto out; |
} |
if (psmouse->cmdcnt) { |
psmouse->cmdbuf[--psmouse->cmdcnt] = data; |
goto out; |
} |
if (psmouse->pktcnt && time_after(jiffies26, psmouse->last + HZ/2)) { |
printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n", |
psmouse->name, psmouse->phys, psmouse->pktcnt); |
psmouse->pktcnt = 0; |
} |
psmouse->last = jiffies26; |
psmouse->packet[psmouse->pktcnt++] = data; |
if (psmouse->packet[0] == PSMOUSE_RET_BAT) { |
if (psmouse->pktcnt == 1) |
goto out; |
if (psmouse->pktcnt == 2) { |
if (psmouse->packet[1] == PSMOUSE_RET_ID) { |
psmouse->state = PSMOUSE_IGNORE; |
serio_rescan(serio); |
goto out; |
} |
if (psmouse->type == PSMOUSE_SYNAPTICS) { |
/* neither 0xAA nor 0x00 are valid first bytes |
* for a packet in absolute mode |
*/ |
psmouse->pktcnt = 0; |
goto out; |
} |
} |
} |
if (psmouse->type == PSMOUSE_SYNAPTICS) { |
/* |
* The synaptics driver has its own resync logic, |
* so it needs to receive all bytes one at a time. |
*/ |
//!!!synaptics_process_byte(psmouse, regs); |
goto out; |
} |
if (psmouse->pktcnt == 3 + (psmouse->type >= PSMOUSE_GENPS)) { |
psmouse_process_packet(psmouse, regs); |
psmouse->pktcnt = 0; |
goto out; |
} |
out: |
return IRQ_HANDLED; |
} |
/* |
* psmouse_sendbyte() sends a byte to the mouse, and waits for acknowledge. |
* It doesn't handle retransmission, though it could - because when there would |
* be need for retransmissions, the mouse has to be replaced anyway. |
*/ |
static int psmouse_sendbyte(struct psmouse *psmouse, unsigned char byte) |
{ |
int timeout = 10000; /* 100 msec */ |
psmouse->ack = 0; |
psmouse->acking = 1; |
if (serio_write(psmouse->serio, byte)) { |
psmouse->acking = 0; |
return -1; |
} |
while (!psmouse->ack && timeout--) udelay(10); |
return -(psmouse->ack <= 0); |
} |
/* |
* psmouse_command() sends a command and its parameters to the mouse, |
* then waits for the response and puts it in the param array. |
*/ |
int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command) |
{ |
int timeout = 500000; /* 500 msec */ |
int send = (command >> 12) & 0xf; |
int receive = (command >> 8) & 0xf; |
int i; |
psmouse->cmdcnt = receive; |
if (command == PSMOUSE_CMD_RESET_BAT) |
timeout = 4000000; /* 4 sec */ |
if (command & 0xff) |
if (psmouse_sendbyte(psmouse, command & 0xff)) |
return (psmouse->cmdcnt = 0) - 1; |
for (i = 0; i < send; i++) |
if (psmouse_sendbyte(psmouse, param[i])) |
return (psmouse->cmdcnt = 0) - 1; |
while (psmouse->cmdcnt && timeout--) { |
if (psmouse->cmdcnt == 1 && command == PSMOUSE_CMD_RESET_BAT) |
timeout = 100000; |
if (psmouse->cmdcnt == 1 && command == PSMOUSE_CMD_GETID && |
psmouse->cmdbuf[1] != 0xab && psmouse->cmdbuf[1] != 0xac) { |
psmouse->cmdcnt = 0; |
break; |
} |
udelay(1); |
} |
for (i = 0; i < receive; i++) |
param[i] = psmouse->cmdbuf[(receive - 1) - i]; |
if (psmouse->cmdcnt) |
return (psmouse->cmdcnt = 0) - 1; |
return 0; |
} |
/* |
* psmouse_extensions() probes for any extensions to the basic PS/2 protocol |
* the mouse may have. |
*/ |
static int psmouse_extensions(struct psmouse *psmouse) |
{ |
unsigned char param[4]; |
param[0] = 0; |
psmouse->vendor = "Generic"; |
psmouse->name = "Mouse"; |
psmouse->model = 0; |
if (psmouse_noext) |
return PSMOUSE_PS2; |
/* |
* Try Synaptics TouchPad magic ID |
*/ |
param[0] = 0; |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); |
psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO); |
if (param[1] == 0x47) { |
psmouse->vendor = "Synaptics"; |
psmouse->name = "TouchPad"; |
/*if (!synaptics_init(psmouse)) |
return PSMOUSE_SYNAPTICS; |
else*/ |
return PSMOUSE_PS2; |
} |
/* |
* Try Genius NetMouse magic init. |
*/ |
param[0] = 3; |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); |
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11); |
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11); |
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11); |
psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO); |
if (param[0] == 0x00 && param[1] == 0x33 && param[2] == 0x55) { |
set_bit(BTN_EXTRA, psmouse->dev.keybit); |
set_bit(BTN_SIDE, psmouse->dev.keybit); |
set_bit(REL_WHEEL, psmouse->dev.relbit); |
psmouse->vendor = "Genius"; |
psmouse->name = "Wheel Mouse"; |
return PSMOUSE_GENPS; |
} |
/* |
* Try Logitech magic ID. |
*/ |
param[0] = 0; |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); |
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11); |
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11); |
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11); |
param[1] = 0; |
psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO); |
if (param[1]) { |
int type = ps2pp_detect_model(psmouse, param); |
if (type) |
return type; |
} |
/* |
* Try IntelliMouse magic init. |
*/ |
param[0] = 200; |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE); |
param[0] = 100; |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE); |
param[0] = 80; |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE); |
psmouse_command(psmouse, param, PSMOUSE_CMD_GETID); |
if (param[0] == 3) { |
set_bit(REL_WHEEL, psmouse->dev.relbit); |
/* |
* Try IntelliMouse/Explorer magic init. |
*/ |
param[0] = 200; |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE); |
param[0] = 200; |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE); |
param[0] = 80; |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE); |
psmouse_command(psmouse, param, PSMOUSE_CMD_GETID); |
if (param[0] == 4) { |
set_bit(BTN_SIDE, psmouse->dev.keybit); |
set_bit(BTN_EXTRA, psmouse->dev.keybit); |
psmouse->name = "Explorer Mouse"; |
return PSMOUSE_IMEX; |
} |
psmouse->name = "Wheel Mouse"; |
return PSMOUSE_IMPS; |
} |
/* |
* Okay, all failed, we have a standard mouse here. The number of the buttons |
* is still a question, though. We assume 3. |
*/ |
return PSMOUSE_PS2; |
} |
/* |
* psmouse_probe() probes for a PS/2 mouse. |
*/ |
static int psmouse_probe(struct psmouse *psmouse) |
{ |
unsigned char param[2]; |
/* |
* First, we check if it's a mouse. It should send 0x00 or 0x03 |
* in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer. |
*/ |
param[0] = param[1] = 0xa5; |
if (psmouse_command(psmouse, param, PSMOUSE_CMD_GETID)) |
return -1; |
if (param[0] != 0x00 && param[0] != 0x03 && param[0] != 0x04) |
return -1; |
/* |
* Then we reset and disable the mouse so that it doesn't generate events. |
*/ |
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_RESET_DIS)) |
return -1; |
/* |
* And here we try to determine if it has any extensions over the |
* basic PS/2 3-button mouse. |
*/ |
return psmouse->type = psmouse_extensions(psmouse); |
} |
/* |
* Here we set the mouse resolution. |
*/ |
static void psmouse_set_resolution(struct psmouse *psmouse) |
{ |
unsigned char param[1]; |
if (psmouse->type == PSMOUSE_PS2PP && psmouse_resolution > 400) { |
ps2pp_set_800dpi(psmouse); |
return; |
} |
if (!psmouse_resolution || psmouse_resolution >= 200) |
param[0] = 3; |
else if (psmouse_resolution >= 100) |
param[0] = 2; |
else if (psmouse_resolution >= 50) |
param[0] = 1; |
else if (psmouse_resolution) |
param[0] = 0; |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); |
} |
/* |
* Here we set the mouse report rate. |
*/ |
static void psmouse_set_rate(struct psmouse *psmouse) |
{ |
unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; |
int i = 0; |
while (rates[i] > psmouse_rate) i++; |
psmouse_command(psmouse, rates + i, PSMOUSE_CMD_SETRATE); |
} |
/* |
* psmouse_initialize() initializes the mouse to a sane state. |
*/ |
static void psmouse_initialize(struct psmouse *psmouse) |
{ |
unsigned char param[2]; |
/* |
* We set the mouse report rate, resolution and scaling. |
*/ |
if (!psmouse_noext) { |
psmouse_set_rate(psmouse); |
psmouse_set_resolution(psmouse); |
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11); |
} |
/* |
* We set the mouse into streaming mode. |
*/ |
psmouse_command(psmouse, param, PSMOUSE_CMD_SETSTREAM); |
} |
/* |
* psmouse_activate() enables the mouse so that we get motion reports from it. |
*/ |
static void psmouse_activate(struct psmouse *psmouse) |
{ |
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE)) |
printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys); |
psmouse->state = PSMOUSE_ACTIVATED; |
} |
/* |
* psmouse_cleanup() resets the mouse into power-on state. |
*/ |
static void psmouse_cleanup(struct serio *serio) |
{ |
struct psmouse *psmouse = serio->private; |
unsigned char param[2]; |
psmouse_command(psmouse, param, PSMOUSE_CMD_RESET_BAT); |
} |
/* |
* psmouse_disconnect() closes and frees. |
*/ |
static void psmouse_disconnect(struct serio *serio) |
{ |
struct psmouse *psmouse = serio->private; |
psmouse->state = PSMOUSE_IGNORE; |
//!!!synaptics_disconnect(psmouse); |
input_unregister_device(&psmouse->dev); |
serio_close(serio); |
kfree(psmouse); |
} |
/* |
* Reinitialize mouse hardware after software suspend. |
*/ |
static int psmouse_pm_callback(struct pm_dev *dev, pm_request_t request, void *data) |
{ |
struct psmouse *psmouse = dev->data; |
struct serio_dev *ser_dev = psmouse->serio->dev; |
//!!!synaptics_disconnect(psmouse); |
/* We need to reopen the serio port to reinitialize the i8042 controller */ |
serio_close(psmouse->serio); |
serio_open(psmouse->serio, ser_dev); |
/* Probe and re-initialize the mouse */ |
psmouse_probe(psmouse); |
psmouse_initialize(psmouse); |
//!!!synaptics_pt_init(psmouse); |
psmouse_activate(psmouse); |
return 0; |
} |
/* |
* psmouse_connect() is a callback from the serio module when |
* an unhandled serio port is found. |
*/ |
static void psmouse_connect(struct serio *serio, struct serio_dev *dev) |
{ |
struct psmouse *psmouse; |
struct pm_dev *pmdev; |
if ((serio->type & SERIO_TYPE) != SERIO_8042 && |
(serio->type & SERIO_TYPE) != SERIO_PS_PSTHRU) |
return; |
if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL))) |
return; |
memset(psmouse, 0, sizeof(struct psmouse)); |
init_input_dev(&psmouse->dev); |
psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); |
psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); |
psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); |
psmouse->state = PSMOUSE_NEW_DEVICE; |
psmouse->serio = serio; |
psmouse->dev.private = psmouse; |
serio->private = psmouse; |
if (serio_open(serio, dev)) { |
kfree(psmouse); |
return; |
} |
if (psmouse_probe(psmouse) <= 0) { |
serio_close(serio); |
kfree(psmouse); |
return; |
} |
pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, psmouse_pm_callback); |
if (pmdev) { |
psmouse->dev.pm_dev = pmdev; |
pmdev->data = psmouse; |
} |
sprintf26(psmouse->devname, "%s %s %s", |
psmouse_protocols[psmouse->type], psmouse->vendor, psmouse->name); |
sprintf26(psmouse->phys, "%s/input0", |
serio->phys); |
psmouse->dev.name = psmouse->devname; |
psmouse->dev.phys = psmouse->phys; |
psmouse->dev.id.bustype = BUS_I8042; |
psmouse->dev.id.vendor = 0x0002; |
psmouse->dev.id.product = psmouse->type; |
psmouse->dev.id.version = psmouse->model; |
input_register_device(&psmouse->dev); |
printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys); |
psmouse_initialize(psmouse); |
//!!!synaptics_pt_init(psmouse); |
psmouse_activate(psmouse); |
} |
static struct serio_dev psmouse_dev = { |
.interrupt = psmouse_interrupt, |
.connect = psmouse_connect, |
.disconnect = psmouse_disconnect, |
.cleanup = psmouse_cleanup, |
}; |
#ifndef MODULE |
static int __init psmouse_noext_setup(char *str) |
{ |
psmouse_noext = 1; |
return 1; |
} |
static int __init psmouse_resolution_setup(char *str) |
{ |
get_option(&str, &psmouse_resolution); |
return 1; |
} |
static int __init psmouse_smartscroll_setup(char *str) |
{ |
get_option(&str, &psmouse_smartscroll); |
return 1; |
} |
static int __init psmouse_resetafter_setup(char *str) |
{ |
get_option(&str, &psmouse_resetafter); |
return 1; |
} |
static int __init psmouse_rate_setup(char *str) |
{ |
get_option(&str, &psmouse_rate); |
return 1; |
} |
__setup("psmouse_noext", psmouse_noext_setup); |
__setup("psmouse_resolution=", psmouse_resolution_setup); |
__setup("psmouse_smartscroll=", psmouse_smartscroll_setup); |
__setup("psmouse_resetafter=", psmouse_resetafter_setup); |
__setup("psmouse_rate=", psmouse_rate_setup); |
#endif |
int __init psmouse_init(void) |
{ |
serio_register_device(&psmouse_dev); |
return 0; |
} |
void __exit psmouse_exit(void) |
{ |
serio_unregister_device(&psmouse_dev); |
} |
module_init(psmouse_init); |
module_exit(psmouse_exit); |
/shark/trunk/drivers/input/mouse/logips2pp.h |
---|
0,0 → 1,17 |
/* |
* Logitech PS/2++ mouse driver header |
* |
* Copyright (c) 2003 Vojtech Pavlik <vojtech@suse.cz> |
* |
* This program is free software; you can redistribute it and/or modify it |
* under the terms of the GNU General Public License version 2 as published by |
* the Free Software Foundation. |
*/ |
#ifndef _LOGIPS2PP_H |
#define _LOGIPS2PP_H |
struct psmouse; |
void ps2pp_process_packet(struct psmouse *psmouse); |
void ps2pp_set_800dpi(struct psmouse *psmouse); |
int ps2pp_detect_model(struct psmouse *psmouse, unsigned char *param); |
#endif |
/shark/trunk/drivers/input/shark_input.c |
---|
0,0 → 1,246 |
/* |
* Project: S.Ha.R.K. |
* |
* Coordinators: |
* Giorgio Buttazzo <giorgio@sssup.it> |
* Paolo Gai <pj@gandalf.sssup.it> |
* |
* Authors : |
* Mauro Marinoni <mauro.marinoni@unipv.it> |
* |
* |
* ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy) |
* |
* http://www.sssup.it |
* http://retis.sssup.it |
* http://shark.sssup.it |
*/ |
#include <kernel/func.h> |
/* System */ |
extern int input_init(void); |
extern int input_exit(void); |
extern int serio_init(void); |
extern int serio_exit(void); |
/* Controllers */ |
extern int i8042_init(void); |
extern int i8042_exit(void); |
/*extern int serport_init(void); |
extern int serport_exit(void);*/ |
/* Devices */ |
extern int atkbd_init(void); |
extern int atkbd_exit(void); |
extern int pcspkr_init(void); |
extern int pcspkr_exit(void); |
extern int psmouse_init(void); |
extern int psmouse_exit(void); |
extern int ns558_init(void); |
extern int ns558_exit(void); |
extern int analog_init(void); |
extern int analog_exit(void); |
extern int joydump_init(void); |
extern int joydump_exit(void); |
/* Handlers */ |
extern int evbug_init(void); |
extern int evbug_exit(void); |
/*extern int evdev_init(void); |
extern int evdev_exit(void); |
extern int mousedev_init(void); |
extern int mousedev_exit(void); |
extern int joydev_init(void); |
extern int joydev_exit(void);*/ |
int input_initialized = 0; |
/* Init the Linux Input Layer */ |
int INPUT26_init() { |
int ret; |
ret = input_init(); |
if (ret) { |
printk(KERN_DEBUG "Input_Init return: %d\n", ret); |
return -1; |
} |
ret = serio_init(); |
if (ret) { |
printk(KERN_DEBUG "Serio_Init return: %d\n", ret); |
return -1; |
} |
ret = i8042_init(); |
if (ret) { |
printk(KERN_DEBUG "i8042_Init return: %d\n", ret); |
return -1; |
} |
/* TODO |
ret = serport_init(); |
if (ret) { |
printk(KERN_DEBUG "SerPort_Init return: %d\n", ret); |
return -1; |
} */ |
input_initialized = 1; |
return ret; |
} |
int INPUT26_close() { |
if (input_initialized) { |
i8042_exit(); |
serio_exit(); |
input_exit(); |
} |
return 0; |
} |
/* Init the Linux Keyboard Driver */ |
int KEYB26_init() { |
int ret; |
if (!input_initialized) |
if (INPUT26_init()) { |
printk(KERN_ERR "Unable to open Input SubSystem.\n"); |
return -1; |
} |
printk(KERN_DEBUG "AtKbd_Init start.\n"); |
ret = atkbd_init(); |
if (ret) { |
printk(KERN_DEBUG "AtKbd_Init return: %d\n", ret); |
return -1; |
} |
return 0; |
} |
int KEYB26_close() { |
atkbd_exit(); |
return 0; |
} |
/* Init the Linux Speaker Driver */ |
int SPEAK26_init() { |
int ret; |
if (!input_initialized) |
if (INPUT26_init()) { |
printk(KERN_ERR "Unable to open Input SubSystem.\n"); |
return -1; |
} |
ret = pcspkr_init(); |
if (ret) { |
printk(KERN_DEBUG "PcSpkr_Init return: %d\n", ret); |
return -1; |
} |
return 0; |
} |
int SPEAK26_close() { |
pcspkr_exit(); |
return 0; |
} |
/* Init the Linux Speaker Driver */ |
int MOUSE26_init() { |
int ret; |
if (!input_initialized) |
if (INPUT26_init()) { |
printk(KERN_ERR "Unable to open Input SubSystem.\n"); |
return -1; |
} |
ret = psmouse_init(); |
if (ret) { |
printk(KERN_DEBUG "PsMouse_Init return: %d\n", ret); |
return -1; |
} |
return 0; |
} |
int MOUSE26_close() { |
psmouse_exit(); |
return 0; |
} |
/* Init the Linux Speaker Driver */ |
/*int joystick_init() { |
int ret; |
if (!input_initialized) |
if (shark_input_init()) { |
printk(KERN_ERR "Unable to open Input SubSystem.\n"); |
return -1; |
} |
ret = ns558_init(); |
if (ret) { |
printk(KERN_DEBUG "Gameport_Init return: %d\n", ret); |
return -1; |
} |
//ret = analog_init(); |
ret = joydump_init(); |
if (ret) { |
printk(KERN_DEBUG "Joystick_Init return: %d\n", ret); |
return -1; |
} |
return 0; |
} |
int joystick_exit() { |
//analog_exit(); |
joydump_exit(); |
ns558_exit(); |
return 0; |
}*/ |
int events_init() { |
//mousedev_init(); |
//joydev_init(); |
//evdev_init(); |
evbug_init(); |
return 0; |
} |
int events_exit() { |
evbug_exit(); |
//evdev_exit(); |
//joydev_exit(); |
//mousedev_exit(); |
return 0; |
} |
/shark/trunk/drivers/input/mousedev.c |
---|
0,0 → 1,575 |
/* |
* Input driver to ExplorerPS/2 device driver module. |
* |
* Copyright (c) 1999-2002 Vojtech Pavlik |
* |
* This program is free software; you can redistribute it and/or modify |
* it under the terms of the GNU General Public License version 2 as published by |
* the Free Software Foundation. |
*/ |
#include <linuxcomp.h> |
#define MOUSEDEV_MINOR_BASE 32 |
#define MOUSEDEV_MINORS 32 |
#define MOUSEDEV_MIX 31 |
#include <linux/slab.h> |
#include <linux/poll.h> |
#include <linux/module.h> |
#include <linux/init.h> |
#include <linux/input.h> |
#include <linux/config.h> |
#include <linux/smp_lock.h> |
#include <linux/random.h> |
#include <linux/major.h> |
#include <linux/device.h> |
#include <linux/devfs_fs_kernel.h> |
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX |
#include <linux/miscdevice.h> |
#endif |
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); |
MODULE_DESCRIPTION("Mouse (ExplorerPS/2) device interfaces"); |
MODULE_LICENSE("GPL"); |
#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X |
#define CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024 |
#endif |
#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y |
#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768 |
#endif |
struct mousedev { |
int exist; |
int open; |
int minor; |
int misc; |
char name[16]; |
wait_queue_head_t wait; |
struct list_head list; |
struct input_handle handle; |
}; |
struct mousedev_list { |
struct fasync_struct *fasync; |
struct mousedev *mousedev; |
struct list_head node; |
int dx, dy, dz, oldx, oldy; |
signed char ps2[6]; |
unsigned long buttons; |
unsigned char ready, buffer, bufsiz; |
unsigned char mode, imexseq, impsseq; |
int finger; |
}; |
#define MOUSEDEV_SEQ_LEN 6 |
static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 }; |
static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 }; |
static struct input_handler mousedev_handler; |
static struct mousedev *mousedev_table[MOUSEDEV_MINORS]; |
static struct mousedev mousedev_mix; |
static int xres = CONFIG_INPUT_MOUSEDEV_SCREEN_X; |
static int yres = CONFIG_INPUT_MOUSEDEV_SCREEN_Y; |
static void mousedev_abs_event(struct input_handle *handle, struct mousedev_list *list, unsigned int code, int value) |
{ |
int size; |
/* Ignore joysticks */ |
if (test_bit(BTN_TRIGGER, handle->dev->keybit)) |
return; |
/* Handle touchpad data */ |
if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit)) { |
if (list->finger && list->finger < 3) |
list->finger++; |
switch (code) { |
case ABS_X: |
if (list->finger == 3) |
list->dx += (value - list->oldx) / 8; |
list->oldx = value; |
return; |
case ABS_Y: |
if (list->finger == 3) |
list->dy -= (value - list->oldy) / 8; |
list->oldy = value; |
return; |
} |
return; |
} |
/* Handle tablet data */ |
switch (code) { |
case ABS_X: |
size = handle->dev->absmax[ABS_X] - handle->dev->absmin[ABS_X]; |
if (size == 0) size = xres; |
list->dx += (value * xres - list->oldx) / size; |
list->oldx += list->dx * size; |
return; |
case ABS_Y: |
size = handle->dev->absmax[ABS_Y] - handle->dev->absmin[ABS_Y]; |
if (size == 0) size = yres; |
list->dy -= (value * yres - list->oldy) / size; |
list->oldy -= list->dy * size; |
return; |
} |
} |
static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) |
{ |
struct mousedev *mousedevs[3] = { handle->private, &mousedev_mix, NULL }; |
struct mousedev **mousedev = mousedevs; |
struct mousedev_list *list; |
int index, wake; |
while (*mousedev) { |
wake = 0; |
list_for_each_entry(list, &(*mousedev)->list, node) |
switch (type) { |
case EV_ABS: |
mousedev_abs_event(handle, list, code, value); |
break; |
case EV_REL: |
switch (code) { |
case REL_X: list->dx += value; break; |
case REL_Y: list->dy -= value; break; |
case REL_WHEEL: if (list->mode) list->dz -= value; break; |
} |
break; |
case EV_KEY: |
switch (code) { |
case BTN_TOUCH: /* Handle touchpad data */ |
if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit)) { |
list->finger = value; |
return; |
} |
case BTN_0: |
case BTN_FORWARD: |
case BTN_LEFT: index = 0; break; |
case BTN_4: |
case BTN_EXTRA: if (list->mode == 2) { index = 4; break; } |
case BTN_STYLUS: |
case BTN_1: |
case BTN_RIGHT: index = 1; break; |
case BTN_3: |
case BTN_BACK: |
case BTN_SIDE: if (list->mode == 2) { index = 3; break; } |
case BTN_2: |
case BTN_STYLUS2: |
case BTN_MIDDLE: index = 2; break; |
default: return; |
} |
switch (value) { |
case 0: clear_bit(index, &list->buttons); break; |
case 1: set_bit(index, &list->buttons); break; |
case 2: return; |
} |
break; |
case EV_SYN: |
switch (code) { |
case SYN_REPORT: |
list->ready = 1; |
kill_fasync(&list->fasync, SIGIO, POLL_IN); |
wake = 1; |
break; |
} |
} |
if (wake) |
wake_up_interruptible(&((*mousedev)->wait)); |
mousedev++; |
} |
} |
static int mousedev_fasync(int fd, struct file *file, int on) |
{ |
int retval; |
struct mousedev_list *list = file->private_data; |
retval = fasync_helper(fd, file, on, &list->fasync); |
return retval < 0 ? retval : 0; |
} |
static void mousedev_free(struct mousedev *mousedev) |
{ |
devfs_remove("input/mouse%d", mousedev->minor); |
mousedev_table[mousedev->minor] = NULL; |
kfree(mousedev); |
} |
static int mixdev_release(void) |
{ |
struct input_handle *handle; |
list_for_each_entry(handle, &mousedev_handler.h_list, h_node) { |
struct mousedev *mousedev = handle->private; |
if (!mousedev->open) { |
if (mousedev->exist) |
input_close_device(&mousedev->handle); |
else |
mousedev_free(mousedev); |
} |
} |
return 0; |
} |
static int mousedev_release(struct inode * inode, struct file * file) |
{ |
struct mousedev_list *list = file->private_data; |
mousedev_fasync(-1, file, 0); |
list_del(&list->node); |
if (!--list->mousedev->open) { |
if (list->mousedev->minor == MOUSEDEV_MIX) |
return mixdev_release(); |
if (!mousedev_mix.open) { |
if (list->mousedev->exist) |
input_close_device(&list->mousedev->handle); |
else |
mousedev_free(list->mousedev); |
} |
} |
kfree(list); |
return 0; |
} |
static int mousedev_open(struct inode * inode, struct file * file) |
{ |
struct mousedev_list *list; |
struct input_handle *handle; |
struct mousedev *mousedev; |
int i; |
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX |
if (imajor(inode) == MISC_MAJOR) |
i = MOUSEDEV_MIX; |
else |
#endif |
i = iminor(inode) - MOUSEDEV_MINOR_BASE; |
if (i >= MOUSEDEV_MINORS || !mousedev_table[i]) |
return -ENODEV; |
if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL))) |
return -ENOMEM; |
memset(list, 0, sizeof(struct mousedev_list)); |
list->mousedev = mousedev_table[i]; |
list_add_tail(&list->node, &mousedev_table[i]->list); |
file->private_data = list; |
if (!list->mousedev->open++) { |
if (list->mousedev->minor == MOUSEDEV_MIX) { |
list_for_each_entry(handle, &mousedev_handler.h_list, h_node) { |
mousedev = handle->private; |
if (!mousedev->open && mousedev->exist) |
input_open_device(handle); |
} |
} else |
if (!mousedev_mix.open && list->mousedev->exist) |
input_open_device(&list->mousedev->handle); |
} |
return 0; |
} |
static void mousedev_packet(struct mousedev_list *list, unsigned char off) |
{ |
list->ps2[off] = 0x08 | ((list->dx < 0) << 4) | ((list->dy < 0) << 5) | (list->buttons & 0x07); |
list->ps2[off + 1] = (list->dx > 127 ? 127 : (list->dx < -127 ? -127 : list->dx)); |
list->ps2[off + 2] = (list->dy > 127 ? 127 : (list->dy < -127 ? -127 : list->dy)); |
list->dx -= list->ps2[off + 1]; |
list->dy -= list->ps2[off + 2]; |
list->bufsiz = off + 3; |
if (list->mode == 2) { |
list->ps2[off + 3] = (list->dz > 7 ? 7 : (list->dz < -7 ? -7 : list->dz)); |
list->dz -= list->ps2[off + 3]; |
list->ps2[off + 3] = (list->ps2[off + 3] & 0x0f) | ((list->buttons & 0x18) << 1); |
list->bufsiz++; |
} |
if (list->mode == 1) { |
list->ps2[off + 3] = (list->dz > 127 ? 127 : (list->dz < -127 ? -127 : list->dz)); |
list->dz -= list->ps2[off + 3]; |
list->bufsiz++; |
} |
if (!list->dx && !list->dy && (!list->mode || !list->dz)) list->ready = 0; |
list->buffer = list->bufsiz; |
} |
static ssize_t mousedev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) |
{ |
struct mousedev_list *list = file->private_data; |
unsigned char c; |
unsigned int i; |
for (i = 0; i < count; i++) { |
if (get_user(c, buffer + i)) |
return -EFAULT; |
if (c == mousedev_imex_seq[list->imexseq]) { |
if (++list->imexseq == MOUSEDEV_SEQ_LEN) { |
list->imexseq = 0; |
list->mode = 2; |
} |
} else list->imexseq = 0; |
if (c == mousedev_imps_seq[list->impsseq]) { |
if (++list->impsseq == MOUSEDEV_SEQ_LEN) { |
list->impsseq = 0; |
list->mode = 1; |
} |
} else list->impsseq = 0; |
list->ps2[0] = 0xfa; |
list->bufsiz = 1; |
switch (c) { |
case 0xeb: /* Poll */ |
mousedev_packet(list, 1); |
break; |
case 0xf2: /* Get ID */ |
switch (list->mode) { |
case 0: list->ps2[1] = 0; break; |
case 1: list->ps2[1] = 3; break; |
case 2: list->ps2[1] = 4; break; |
} |
list->bufsiz = 2; |
break; |
case 0xe9: /* Get info */ |
list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200; |
list->bufsiz = 4; |
break; |
case 0xff: /* Reset */ |
list->impsseq = 0; |
list->imexseq = 0; |
list->mode = 0; |
list->ps2[0] = 0xaa; |
list->ps2[1] = 0x00; |
list->bufsiz = 2; |
break; |
} |
list->buffer = list->bufsiz; |
} |
kill_fasync(&list->fasync, SIGIO, POLL_IN); |
wake_up_interruptible(&list->mousedev->wait); |
return count; |
} |
static ssize_t mousedev_read(struct file * file, char * buffer, size_t count, loff_t *ppos) |
{ |
struct mousedev_list *list = file->private_data; |
int retval = 0; |
if (!list->ready && !list->buffer && (file->f_flags & O_NONBLOCK)) |
return -EAGAIN; |
retval = wait_event_interruptible(list->mousedev->wait, list->ready || list->buffer); |
if (retval) |
return retval; |
if (!list->buffer && list->ready) |
mousedev_packet(list, 0); |
if (count > list->buffer) |
count = list->buffer; |
list->buffer -= count; |
if (copy_to_user(buffer, list->ps2 + list->bufsiz - list->buffer - count, count)) |
return -EFAULT; |
return count; |
} |
/* No kernel lock - fine */ |
static unsigned int mousedev_poll(struct file *file, poll_table *wait) |
{ |
struct mousedev_list *list = file->private_data; |
poll_wait(file, &list->mousedev->wait, wait); |
if (list->ready || list->buffer) |
return POLLIN | POLLRDNORM; |
return 0; |
} |
struct file_operations mousedev_fops = { |
.owner = THIS_MODULE, |
.read = mousedev_read, |
.write = mousedev_write, |
.poll = mousedev_poll, |
.open = mousedev_open, |
.release = mousedev_release, |
.fasync = mousedev_fasync, |
}; |
static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id) |
{ |
struct mousedev *mousedev; |
int minor = 0; |
for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++); |
if (minor == MOUSEDEV_MINORS) { |
printk(KERN_ERR "mousedev: no more free mousedev devices\n"); |
return NULL; |
} |
if (!(mousedev = kmalloc(sizeof(struct mousedev), GFP_KERNEL))) |
return NULL; |
memset(mousedev, 0, sizeof(struct mousedev)); |
INIT_LIST_HEAD(&mousedev->list); |
init_waitqueue_head(&mousedev->wait); |
mousedev->minor = minor; |
mousedev->exist = 1; |
mousedev->handle.dev = dev; |
mousedev->handle.name = mousedev->name; |
mousedev->handle.handler = handler; |
mousedev->handle.private = mousedev; |
sprintf26(mousedev->name, "mouse%d", minor); |
if (mousedev_mix.open) |
input_open_device(&mousedev->handle); |
mousedev_table[minor] = mousedev; |
devfs_mk_cdev(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor), |
S_IFCHR|S_IRUGO|S_IWUSR, "input/mouse%d", minor); |
return &mousedev->handle; |
} |
static void mousedev_disconnect(struct input_handle *handle) |
{ |
struct mousedev *mousedev = handle->private; |
mousedev->exist = 0; |
if (mousedev->open) { |
input_close_device(handle); |
} else { |
if (mousedev_mix.open) |
input_close_device(handle); |
mousedev_free(mousedev); |
} |
} |
static struct input_device_id mousedev_ids[] = { |
{ |
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT, |
.evbit = { BIT(EV_KEY) | BIT(EV_REL) }, |
.keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) }, |
.relbit = { BIT(REL_X) | BIT(REL_Y) }, |
}, /* A mouse like device, at least one button, two relative axes */ |
{ |
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_RELBIT, |
.evbit = { BIT(EV_KEY) | BIT(EV_REL) }, |
.relbit = { BIT(REL_WHEEL) }, |
}, /* A separate scrollwheel */ |
{ |
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, |
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) }, |
.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) }, |
.absbit = { BIT(ABS_X) | BIT(ABS_Y) }, |
}, /* A tablet like device, at least touch detection, two absolute axes */ |
{ |
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, |
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) }, |
.keybit = { [LONG(BTN_TOOL_FINGER)] = BIT(BTN_TOOL_FINGER) }, |
.absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_TOOL_WIDTH) }, |
}, /* A touchpad */ |
{ }, /* Terminating entry */ |
}; |
MODULE_DEVICE_TABLE(input, mousedev_ids); |
static struct input_handler mousedev_handler = { |
.event = mousedev_event, |
.connect = mousedev_connect, |
.disconnect = mousedev_disconnect, |
.fops = &mousedev_fops, |
.minor = MOUSEDEV_MINOR_BASE, |
.name = "mousedev", |
.id_table = mousedev_ids, |
}; |
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX |
static struct miscdevice psaux_mouse = { |
PSMOUSE_MINOR, "psaux", &mousedev_fops |
}; |
#endif |
/*static*/ int __init mousedev_init(void) |
{ |
input_register_handler(&mousedev_handler); |
memset(&mousedev_mix, 0, sizeof(struct mousedev)); |
INIT_LIST_HEAD(&mousedev_mix.list); |
init_waitqueue_head(&mousedev_mix.wait); |
mousedev_table[MOUSEDEV_MIX] = &mousedev_mix; |
mousedev_mix.exist = 1; |
mousedev_mix.minor = MOUSEDEV_MIX; |
devfs_mk_cdev(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX), |
S_IFCHR|S_IRUGO|S_IWUSR, "input/mice"); |
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX |
if (!(mousedev_mix.misc = !misc_register(&psaux_mouse))) |
printk(KERN_WARNING "mice: could not misc_register the device\n"); |
#endif |
printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n"); |
return 0; |
} |
/*static*/ void __exit mousedev_exit(void) |
{ |
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX |
if (mousedev_mix.misc) |
misc_deregister(&psaux_mouse); |
#endif |
devfs_remove("input/mice"); |
input_unregister_handler(&mousedev_handler); |
} |
module_init(mousedev_init); |
module_exit(mousedev_exit); |
MODULE_PARM(xres, "i"); |
MODULE_PARM_DESC(xres, "Horizontal screen resolution"); |
MODULE_PARM(yres, "i"); |
MODULE_PARM_DESC(yres, "Vertical screen resolution"); |
/shark/trunk/drivers/input/evbug.c |
---|
0,0 → 1,105 |
/* |
* $Id: evbug.c,v 1.1 2004-03-08 18:47:36 giacomo Exp $ |
* |
* Copyright (c) 1999-2001 Vojtech Pavlik |
*/ |
/* |
* Input driver event debug module - dumps all events into syslog |
*/ |
/* |
* 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 <vojtech@ucw.cz>, or by paper mail: |
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic |
*/ |
#include <linuxcomp.h> |
#include <linux/slab.h> |
#include <linux/module.h> |
#include <linux/input.h> |
#include <linux/init.h> |
#include <linux/device.h> |
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); |
MODULE_DESCRIPTION("Input driver event debug module"); |
MODULE_LICENSE("GPL"); |
static char evbug_name[] = "evbug"; |
static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) |
{ |
printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n", handle->dev->phys, type, code, value); |
} |
static struct input_handle *evbug_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id) |
{ |
struct input_handle *handle; |
if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL))) |
return NULL; |
memset(handle, 0, sizeof(struct input_handle)); |
handle->dev = dev; |
handle->handler = handler; |
handle->name = evbug_name; |
input_open_device(handle); |
printk(KERN_DEBUG "evbug.c: Connected device: \"%s\", %s\n", dev->name, dev->phys); |
return handle; |
} |
static void evbug_disconnect(struct input_handle *handle) |
{ |
printk(KERN_DEBUG "evbug.c: Disconnected device: %s\n", handle->dev->phys); |
input_close_device(handle); |
kfree(handle); |
} |
static struct input_device_id evbug_ids[] = { |
{ .driver_info = 1 }, /* Matches all devices */ |
{ }, /* Terminating zero entry */ |
}; |
MODULE_DEVICE_TABLE(input, evbug_ids); |
static struct input_handler evbug_handler = { |
.event = evbug_event, |
.connect = evbug_connect, |
.disconnect = evbug_disconnect, |
.name = "evbug", |
.id_table = evbug_ids, |
}; |
int __init evbug_init(void) |
{ |
input_register_handler(&evbug_handler); |
return 0; |
} |
void __exit evbug_exit(void) |
{ |
input_unregister_handler(&evbug_handler); |
} |
module_init(evbug_init); |
module_exit(evbug_exit); |
/shark/trunk/drivers/input/evdev.c |
---|
0,0 → 1,481 |
/* |
* Event char devices, giving access to raw input device events. |
* |
* Copyright (c) 1999-2002 Vojtech Pavlik |
* |
* This program is free software; you can redistribute it and/or modify it |
* under the terms of the GNU General Public License version 2 as published by |
* the Free Software Foundation. |
*/ |
#include <linuxcomp.h> |
#define EVDEV_MINOR_BASE 64 |
#define EVDEV_MINORS 32 |
#define EVDEV_BUFFER_SIZE 64 |
#include <linux/poll.h> |
#include <linux/slab.h> |
#include <linux/module.h> |
#include <linux/init.h> |
#include <linux/input.h> |
#include <linux/major.h> |
#include <linux/smp_lock.h> |
#include <linux/device.h> |
#include <linux/devfs_fs_kernel.h> |
struct evdev { |
int exist; |
int open; |
int minor; |
char name[16]; |
struct input_handle handle; |
wait_queue_head_t wait; |
struct evdev_list *grab; |
struct list_head list; |
}; |
struct evdev_list { |
struct input_event buffer[EVDEV_BUFFER_SIZE]; |
int head; |
int tail; |
struct fasync_struct *fasync; |
struct evdev *evdev; |
struct list_head node; |
}; |
static struct evdev *evdev_table[EVDEV_MINORS]; |
static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) |
{ |
struct evdev *evdev = handle->private; |
struct evdev_list *list; |
if (evdev->grab) { |
list = evdev->grab; |
do_gettimeofday(&list->buffer[list->head].time); |
list->buffer[list->head].type = type; |
list->buffer[list->head].code = code; |
list->buffer[list->head].value = value; |
list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1); |
kill_fasync(&list->fasync, SIGIO, POLL_IN); |
} else |
list_for_each_entry(list, &evdev->list, node) { |
do_gettimeofday(&list->buffer[list->head].time); |
list->buffer[list->head].type = type; |
list->buffer[list->head].code = code; |
list->buffer[list->head].value = value; |
list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1); |
kill_fasync(&list->fasync, SIGIO, POLL_IN); |
} |
wake_up_interruptible(&evdev->wait); |
} |
static int evdev_fasync(int fd, struct file *file, int on) |
{ |
int retval; |
struct evdev_list *list = file->private_data; |
retval = fasync_helper(fd, file, on, &list->fasync); |
return retval < 0 ? retval : 0; |
} |
static int evdev_flush(struct file * file) |
{ |
struct evdev_list *list = file->private_data; |
if (!list->evdev->exist) return -ENODEV; |
return input_flush_device(&list->evdev->handle, file); |
} |
static void evdev_free(struct evdev *evdev) |
{ |
devfs_remove("input/event%d", evdev->minor); |
evdev_table[evdev->minor] = NULL; |
kfree(evdev); |
} |
static int evdev_release(struct inode * inode, struct file * file) |
{ |
struct evdev_list *list = file->private_data; |
if (list->evdev->grab == list) { |
input_release_device(&list->evdev->handle); |
list->evdev->grab = NULL; |
} |
evdev_fasync(-1, file, 0); |
list_del(&list->node); |
if (!--list->evdev->open) { |
if (list->evdev->exist) |
input_close_device(&list->evdev->handle); |
else |
evdev_free(list->evdev); |
} |
kfree(list); |
return 0; |
} |
static int evdev_open(struct inode * inode, struct file * file) |
{ |
struct evdev_list *list; |
int i = iminor(inode) - EVDEV_MINOR_BASE; |
int accept_err; |
if (i >= EVDEV_MINORS || !evdev_table[i]) |
return -ENODEV; |
if ((accept_err = input_accept_process(&(evdev_table[i]->handle), file))) |
return accept_err; |
if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL))) |
return -ENOMEM; |
memset(list, 0, sizeof(struct evdev_list)); |
list->evdev = evdev_table[i]; |
list_add_tail(&list->node, &evdev_table[i]->list); |
file->private_data = list; |
if (!list->evdev->open++) |
if (list->evdev->exist) |
input_open_device(&list->evdev->handle); |
return 0; |
} |
static ssize_t evdev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) |
{ |
struct evdev_list *list = file->private_data; |
struct input_event event; |
int retval = 0; |
if (!list->evdev->exist) return -ENODEV; |
while (retval < count) { |
if (copy_from_user(&event, buffer + retval, sizeof(struct input_event))) |
return -EFAULT; |
input_event(list->evdev->handle.dev, event.type, event.code, event.value); |
retval += sizeof(struct input_event); |
} |
return retval; |
} |
static ssize_t evdev_read(struct file * file, char * buffer, size_t count, loff_t *ppos) |
{ |
struct evdev_list *list = file->private_data; |
int retval; |
if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK)) |
return -EAGAIN; |
retval = wait_event_interruptible(list->evdev->wait, |
list->head != list->tail && list->evdev->exist); |
if (retval) |
return retval; |
if (!list->evdev->exist) |
return -ENODEV; |
while (list->head != list->tail && retval + sizeof(struct input_event) <= count) { |
if (copy_to_user(buffer + retval, list->buffer + list->tail, |
sizeof(struct input_event))) return -EFAULT; |
list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1); |
retval += sizeof(struct input_event); |
} |
return retval; |
} |
/* No kernel lock - fine */ |
static unsigned int evdev_poll(struct file *file, poll_table *wait) |
{ |
struct evdev_list *list = file->private_data; |
poll_wait(file, &list->evdev->wait, wait); |
if (list->head != list->tail) |
return POLLIN | POLLRDNORM; |
return 0; |
} |
static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) |
{ |
struct evdev_list *list = file->private_data; |
struct evdev *evdev = list->evdev; |
struct input_dev *dev = evdev->handle.dev; |
struct input_absinfo abs; |
int i, t, u, v; |
if (!evdev->exist) return -ENODEV; |
switch (cmd) { |
case EVIOCGVERSION: |
return put_user(EV_VERSION, (int *) arg); |
case EVIOCGID: |
return copy_to_user((void *) arg, &dev->id, sizeof(struct input_id)) ? -EFAULT : 0; |
case EVIOCGKEYCODE: |
if (get_user(t, ((int *) arg) + 0)) return -EFAULT; |
if (t < 0 || t > dev->keycodemax || !dev->keycodesize) return -EINVAL; |
if (put_user(INPUT_KEYCODE(dev, t), ((int *) arg) + 1)) return -EFAULT; |
return 0; |
case EVIOCSKEYCODE: |
if (get_user(t, ((int *) arg) + 0)) return -EFAULT; |
if (t < 0 || t > dev->keycodemax || !dev->keycodesize) return -EINVAL; |
if (get_user(v, ((int *) arg) + 1)) return -EFAULT; |
u = INPUT_KEYCODE(dev, t); |
INPUT_KEYCODE(dev, t) = v; |
for (i = 0; i < dev->keycodemax; i++) if (v == u) break; |
if (i == dev->keycodemax) clear_bit(u, dev->keybit); |
set_bit(v, dev->keybit); |
return 0; |
case EVIOCSFF: |
if (dev->upload_effect) { |
struct ff_effect effect; |
int err; |
if (copy_from_user((void*)(&effect), (void*)arg, sizeof(effect))) |
return -EFAULT; |
err = dev->upload_effect(dev, &effect); |
if (put_user(effect.id, &(((struct ff_effect*)arg)->id))) |
return -EFAULT; |
return err; |
} |
else return -ENOSYS; |
case EVIOCRMFF: |
if (dev->erase_effect) { |
return dev->erase_effect(dev, (int)arg); |
} |
else return -ENOSYS; |
case EVIOCGEFFECTS: |
if (put_user(dev->ff_effects_max, (int*) arg)) |
return -EFAULT; |
return 0; |
case EVIOCGRAB: |
if (arg) { |
if (evdev->grab) |
return -EBUSY; |
if (input_grab_device(&evdev->handle)) |
return -EBUSY; |
evdev->grab = list; |
return 0; |
} else { |
if (evdev->grab != list) |
return -EINVAL; |
input_release_device(&evdev->handle); |
evdev->grab = NULL; |
return 0; |
} |
default: |
if (_IOC_TYPE(cmd) != 'E' || _IOC_DIR(cmd) != _IOC_READ) |
return -EINVAL; |
if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) { |
long *bits; |
int len; |
switch (_IOC_NR(cmd) & EV_MAX) { |
case 0: bits = dev->evbit; len = EV_MAX; break; |
case EV_KEY: bits = dev->keybit; len = KEY_MAX; break; |
case EV_REL: bits = dev->relbit; len = REL_MAX; break; |
case EV_ABS: bits = dev->absbit; len = ABS_MAX; break; |
case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break; |
case EV_LED: bits = dev->ledbit; len = LED_MAX; break; |
case EV_SND: bits = dev->sndbit; len = SND_MAX; break; |
case EV_FF: bits = dev->ffbit; len = FF_MAX; break; |
default: return -EINVAL; |
} |
len = NBITS(len) * sizeof(long); |
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); |
return copy_to_user((char *) arg, bits, len) ? -EFAULT : len; |
} |
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) { |
int len; |
len = NBITS(KEY_MAX) * sizeof(long); |
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); |
return copy_to_user((char *) arg, dev->key, len) ? -EFAULT : len; |
} |
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) { |
int len; |
len = NBITS(LED_MAX) * sizeof(long); |
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); |
return copy_to_user((char *) arg, dev->led, len) ? -EFAULT : len; |
} |
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0))) { |
int len; |
len = NBITS(SND_MAX) * sizeof(long); |
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); |
return copy_to_user((char *) arg, dev->snd, len) ? -EFAULT : len; |
} |
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) { |
int len; |
if (!dev->name) return -ENOENT; |
len = strlen(dev->name) + 1; |
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); |
return copy_to_user((char *) arg, dev->name, len) ? -EFAULT : len; |
} |
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) { |
int len; |
if (!dev->phys) return -ENOENT; |
len = strlen(dev->phys) + 1; |
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); |
return copy_to_user((char *) arg, dev->phys, len) ? -EFAULT : len; |
} |
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) { |
int len; |
if (!dev->uniq) return -ENOENT; |
len = strlen(dev->uniq) + 1; |
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); |
return copy_to_user((char *) arg, dev->uniq, len) ? -EFAULT : len; |
} |
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { |
int t = _IOC_NR(cmd) & ABS_MAX; |
abs.value = dev->abs[t]; |
abs.minimum = dev->absmin[t]; |
abs.maximum = dev->absmax[t]; |
abs.fuzz = dev->absfuzz[t]; |
abs.flat = dev->absflat[t]; |
if (copy_to_user((void *) arg, &abs, sizeof(struct input_absinfo))) |
return -EFAULT; |
return 0; |
} |
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { |
int t = _IOC_NR(cmd) & ABS_MAX; |
if (copy_from_user(&abs, (void *) arg, sizeof(struct input_absinfo))) |
return -EFAULT; |
dev->abs[t] = abs.value; |
dev->absmin[t] = abs.minimum; |
dev->absmax[t] = abs.maximum; |
dev->absfuzz[t] = abs.fuzz; |
dev->absflat[t] = abs.flat; |
return 0; |
} |
} |
return -EINVAL; |
} |
static struct file_operations evdev_fops = { |
.owner = THIS_MODULE, |
.read = evdev_read, |
.write = evdev_write, |
.poll = evdev_poll, |
.open = evdev_open, |
.release = evdev_release, |
.ioctl = evdev_ioctl, |
.fasync = evdev_fasync, |
.flush = evdev_flush |
}; |
static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id) |
{ |
struct evdev *evdev; |
int minor; |
for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); |
if (minor == EVDEV_MINORS) { |
printk(KERN_ERR "evdev: no more free evdev devices\n"); |
return NULL; |
} |
if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL))) |
return NULL; |
memset(evdev, 0, sizeof(struct evdev)); |
INIT_LIST_HEAD(&evdev->list); |
init_waitqueue_head(&evdev->wait); |
evdev->exist = 1; |
evdev->minor = minor; |
evdev->handle.dev = dev; |
evdev->handle.name = evdev->name; |
evdev->handle.handler = handler; |
evdev->handle.private = evdev; |
sprintf26(evdev->name, "event%d", minor); |
evdev_table[minor] = evdev; |
devfs_mk_cdev(MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), |
S_IFCHR|S_IRUGO|S_IWUSR, "input/event%d", minor); |
return &evdev->handle; |
} |
static void evdev_disconnect(struct input_handle *handle) |
{ |
struct evdev *evdev = handle->private; |
evdev->exist = 0; |
if (evdev->open) { |
input_close_device(handle); |
wake_up_interruptible(&evdev->wait); |
} else |
evdev_free(evdev); |
} |
static struct input_device_id evdev_ids[] = { |
{ .driver_info = 1 }, /* Matches all devices */ |
{ }, /* Terminating zero entry */ |
}; |
MODULE_DEVICE_TABLE(input, evdev_ids); |
static struct input_handler evdev_handler = { |
.event = evdev_event, |
.connect = evdev_connect, |
.disconnect = evdev_disconnect, |
.fops = &evdev_fops, |
.minor = EVDEV_MINOR_BASE, |
.name = "evdev", |
.id_table = evdev_ids, |
}; |
/*static*/ int __init evdev_init(void) |
{ |
input_register_handler(&evdev_handler); |
return 0; |
} |
/*static*/ void __exit evdev_exit(void) |
{ |
input_unregister_handler(&evdev_handler); |
} |
module_init(evdev_init); |
module_exit(evdev_exit); |
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); |
MODULE_DESCRIPTION("Input driver event char devices"); |
MODULE_LICENSE("GPL"); |
/shark/trunk/drivers/input/misc/pcspkr.c |
---|
0,0 → 1,96 |
/* |
* PC Speaker beeper driver for Linux |
* |
* Copyright (c) 2002 Vojtech Pavlik |
* Copyright (c) 1992 Orest Zborowski |
* |
*/ |
/* |
* This program is free software; you can redistribute it and/or modify it |
* under the terms of the GNU General Public License version 2 as published by |
* the Free Software Foundation |
*/ |
#include <linuxcomp.h> |
#include <linux/kernel.h> |
#include <linux/module.h> |
#include <linux/init.h> |
#include <linux/input.h> |
#include <asm/io.h> |
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); |
MODULE_DESCRIPTION("PC Speaker beeper driver"); |
MODULE_LICENSE("GPL"); |
static char pcspkr_name[] = "PC Speaker"; |
static char pcspkr_phys[] = "isa0061/input0"; |
static struct input_dev pcspkr_dev; |
spinlock_t i8253_beep_lock = SPIN_LOCK_UNLOCKED; |
static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) |
{ |
unsigned int count = 0; |
unsigned long flags; |
if (type != EV_SND) |
return -1; |
switch (code) { |
case SND_BELL: if (value) value = 1000; |
case SND_TONE: break; |
default: return -1; |
} |
if (value > 20 && value < 32767) |
count = CLOCK_TICK_RATE / value; |
spin_lock_irqsave(&i8253_beep_lock, flags); |
if (count) { |
/* enable counter 2 */ |
outb_p(inb_p(0x61) | 3, 0x61); |
/* set command for counter 2, 2 byte write */ |
outb_p(0xB6, 0x43); |
/* select desired HZ */ |
outb_p(count & 0xff, 0x42); |
outb((count >> 8) & 0xff, 0x42); |
} else { |
/* disable counter 2 */ |
outb(inb_p(0x61) & 0xFC, 0x61); |
} |
spin_unlock_irqrestore(&i8253_beep_lock, flags); |
return 0; |
} |
/*static*/ int __init pcspkr_init(void) |
{ |
pcspkr_dev.evbit[0] = BIT(EV_SND); |
pcspkr_dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); |
pcspkr_dev.event = pcspkr_event; |
pcspkr_dev.name = pcspkr_name; |
pcspkr_dev.phys = pcspkr_phys; |
pcspkr_dev.id.bustype = BUS_ISA; |
pcspkr_dev.id.vendor = 0x001f; |
pcspkr_dev.id.product = 0x0001; |
pcspkr_dev.id.version = 0x0100; |
input_register_device(&pcspkr_dev); |
printk(KERN_INFO "input: %s\n", pcspkr_name); |
return 0; |
} |
/*static*/ void __exit pcspkr_exit(void) |
{ |
input_unregister_device(&pcspkr_dev); |
} |
module_init(pcspkr_init); |
module_exit(pcspkr_exit); |
/shark/trunk/drivers/input/cmdline.c |
---|
0,0 → 1,122 |
/* |
* linux/lib/cmdline.c |
* Helper functions generally used for parsing kernel command line |
* and module options. |
* |
* Code and copyrights come from init/main.c and arch/i386/kernel/setup.c. |
* |
* This source code is licensed under the GNU General Public License, |
* Version 2. See the file COPYING for more details. |
* |
* GNU Indent formatting options for this file: -kr -i8 -npsl -pcs |
* |
*/ |
#include <linuxcomp.h> |
#include <linux/module.h> |
#include <linux/kernel.h> |
#include <linux/string.h> |
/** |
* get_option - Parse integer from an option string |
* @str: option string |
* @pint: (output) integer value parsed from @str |
* |
* Read an int from an option string; if available accept a subsequent |
* comma as well. |
* |
* Return values: |
* 0 : no int in string |
* 1 : int found, no subsequent comma |
* 2 : int found including a subsequent comma |
*/ |
int get_option (char **str, int *pint) |
{ |
char *cur = *str; |
if (!cur || !(*cur)) |
return 0; |
*pint = simple_strtol (cur, str, 0); |
if (cur == *str) |
return 0; |
if (**str == ',') { |
(*str)++; |
return 2; |
} |
return 1; |
} |
/** |
* get_options - Parse a string into a list of integers |
* @str: String to be parsed |
* @nints: size of integer array |
* @ints: integer array |
* |
* This function parses a string containing a comma-separated |
* list of integers. The parse halts when the array is |
* full, or when no more numbers can be retrieved from the |
* string. |
* |
* Return value is the character in the string which caused |
* the parse to end (typically a null terminator, if @str is |
* completely parseable). |
*/ |
char *get_options(const char *str, int nints, int *ints) |
{ |
int res, i = 1; |
while (i < nints) { |
res = get_option ((char **)&str, ints + i); |
if (res == 0) |
break; |
i++; |
if (res == 1) |
break; |
} |
ints[0] = i - 1; |
return (char *)str; |
} |
/** |
* memparse - parse a string with mem suffixes into a number |
* @ptr: Where parse begins |
* @retptr: (output) Pointer to next char after parse completes |
* |
* Parses a string into a number. The number stored at @ptr is |
* potentially suffixed with %K (for kilobytes, or 1024 bytes), |
* %M (for megabytes, or 1048576 bytes), or %G (for gigabytes, or |
* 1073741824). If the number is suffixed with K, M, or G, then |
* the return value is the number multiplied by one kilobyte, one |
* megabyte, or one gigabyte, respectively. |
*/ |
unsigned long long memparse (char *ptr, char **retptr) |
{ |
unsigned long long ret = simple_strtoull (ptr, retptr, 0); |
switch (**retptr) { |
case 'G': |
case 'g': |
ret <<= 10; |
case 'M': |
case 'm': |
ret <<= 10; |
case 'K': |
case 'k': |
ret <<= 10; |
(*retptr)++; |
default: |
break; |
} |
return ret; |
} |
EXPORT_SYMBOL(memparse); |
EXPORT_SYMBOL(get_option); |
EXPORT_SYMBOL(get_options); |
/shark/trunk/drivers/input/makefile |
---|
0,0 → 1,28 |
# PCI support from linux 2.6.0 |
ifndef BASE |
BASE=../.. |
endif |
include $(BASE)/config/config.mk |
LIBRARY = input |
OBJS_PATH = $(BASE)/drivers/input |
OBJS = input.o cmdline.o evbug.o\ |
serio/serio.o serio/i8042.o\ |
mouse/psmouse-base.o mouse/logips2pp.o\ |
keyboard/atkbd.o\ |
misc/pcspkr.o\ |
shark_input.o |
OTHERINCL += -I$(BASE)/drivers/linuxc26/include |
C_OPT += -D__KERNEL__ |
include $(BASE)/config/lib.mk |
clean:: |
rm -f $(OBJS) |
/shark/trunk/drivers/input/makefile.full |
---|
0,0 → 1,30 |
# PCI support from linux 2.6.0 |
ifndef BASE |
BASE=../.. |
endif |
include $(BASE)/config/config.mk |
LIBRARY = input |
OBJS_PATH = $(BASE)/drivers/input |
OBJS = input.o cmdline.o evbug.o evdev.o mousedev.o joydev.o\ |
serio/serio.o serio/i8042.o serio/serport.o\ |
mouse/psmouse-base.o mouse/logips2pp.o mouse/sermouse.o\ |
keyboard/atkbd.o\ |
misc/pcspkr.o\ |
gameport/gameport.o gameport/ns558.o\ |
joystick/analog.o joystick/joydump.o\ |
shark_input.o |
OTHERINCL += -I$(BASE)/drivers/linuxc26/include |
C_OPT += -D__KERNEL__ |
include $(BASE)/config/lib.mk |
clean:: |
rm -f $(OBJS) |
/shark/trunk/drivers/input/gameport/gameport.c |
---|
0,0 → 1,182 |
/* |
* Generic gameport layer |
* |
* Copyright (c) 1999-2002 Vojtech Pavlik |
*/ |
/* |
* This program is free software; you can redistribute it and/or modify it |
* under the terms of the GNU General Public License version 2 as published by |
* the Free Software Foundation. |
*/ |
#include <linuxcomp.h> |
#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/stddef.h> |
#include <linux/delay.h> |
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); |
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 LIST_HEAD(gameport_list); |
static LIST_HEAD(gameport_dev_list); |
#ifdef __i386__ |
#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193182/HZ:0)) |
#define GET_TIME(x) do { x = get_time_pit(); } while (0) |
static unsigned int get_time_pit(void) |
{ |
extern spinlock_t i8253_lock; |
unsigned long flags; |
unsigned int count; |
spin_lock_irqsave(&i8253_lock, flags); |
outb_p(0x00, 0x43); |
count = inb_p(0x40); |
count |= inb_p(0x40) << 8; |
spin_unlock_irqrestore(&i8253_lock, flags); |
return count; |
} |
#endif |
/* |
* gameport_measure_speed() measures the gameport i/o speed. |
*/ |
static int gameport_measure_speed(struct gameport *gameport) |
{ |
#ifdef __i386__ |
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++) { |
local_irq_save(flags); |
GET_TIME(t1); |
for(t = 0; t < 50; t++) gameport_read(gameport); |
GET_TIME(t2); |
GET_TIME(t3); |
local_irq_restore(flags); |
udelay(i * 10); |
if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; |
} |
gameport_close(gameport); |
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); } |
gameport_close(gameport); |
return t * HZ / 1000; |
#endif |
} |
static void gameport_find_dev(struct gameport *gameport) |
{ |
struct gameport_dev *dev; |
list_for_each_entry(dev, &gameport_dev_list, node) { |
if (gameport->dev) |
break; |
if (dev->connect) |
dev->connect(gameport, dev); |
} |
} |
void gameport_rescan(struct gameport *gameport) |
{ |
gameport_close(gameport); |
gameport_find_dev(gameport); |
} |
void gameport_register_port(struct gameport *gameport) |
{ |
list_add_tail(&gameport->node, &gameport_list); |
gameport->speed = gameport_measure_speed(gameport); |
gameport_find_dev(gameport); |
} |
void gameport_unregister_port(struct gameport *gameport) |
{ |
list_del_init(&gameport->node); |
if (gameport->dev && gameport->dev->disconnect) |
gameport->dev->disconnect(gameport); |
} |
void gameport_register_device(struct gameport_dev *dev) |
{ |
struct gameport *gameport; |
list_add_tail(&dev->node, &gameport_dev_list); |
list_for_each_entry(gameport, &gameport_list, node) |
if (!gameport->dev && dev->connect) |
dev->connect(gameport, dev); |
} |
void gameport_unregister_device(struct gameport_dev *dev) |
{ |
struct gameport *gameport; |
list_del_init(&dev->node); |
list_for_each_entry(gameport, &gameport_list, node) { |
if (gameport->dev == dev && dev->disconnect) |
dev->disconnect(gameport); |
gameport_find_dev(gameport); |
} |
} |
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); |
} |
/shark/trunk/drivers/input/gameport/ns558.c |
---|
0,0 → 1,291 |
/* |
* $Id: ns558.c,v 1.1 2004-03-08 18:47:38 giacomo Exp $ |
* |
* Copyright (c) 1999-2001 Vojtech Pavlik |
* Copyright (c) 1999 Brian Gerst |
*/ |
/* |
* NS558 based standard IBM game port 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 <vojtech@ucw.cz>, or by paper mail: |
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic |
*/ |
#include <linuxcomp.h> |
#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/pnp.h> |
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); |
MODULE_DESCRIPTION("Classic gameport (ISA/PnP) driver"); |
MODULE_LICENSE("GPL"); |
#define NS558_ISA 1 |
#define NS558_PNP 2 |
static int ns558_isa_portlist[] = { 0x201, 0x200, 0x202, 0x203, 0x204, 0x205, 0x207, 0x209, |
0x20b, 0x20c, 0x20e, 0x20f, 0x211, 0x219, 0x101, 0 }; |
struct ns558 { |
int type; |
int size; |
struct pnp_dev *dev; |
struct list_head node; |
struct gameport gameport; |
char phys[32]; |
char name[32]; |
}; |
static LIST_HEAD(ns558_list); |
/* |
* ns558_isa_probe() tries to find an isa gameport at the |
* specified address, and also checks for mirrors. |
* A joystick must be attached for this to work. |
*/ |
static void ns558_isa_probe(int io) |
{ |
int i, j, b; |
unsigned char c, u, v; |
struct ns558 *port; |
/* |
* No one should be using this address. |
*/ |
//!!!if (check_region(io, 1)) |
//!!! return; |
/* |
* We must not be able to write arbitrary values to the port. |
* The lower two axis bits must be 1 after a write. |
*/ |
c = inb(io); |
outb(~c & ~3, io); |
if (~(u = v = inb(io)) & 3) { |
outb(c, io); |
return; |
} |
/* |
* After a trigger, there must be at least some bits changing. |
*/ |
for (i = 0; i < 1000; i++) v &= inb(io); |
if (u == v) { |
outb(c, io); |
return; |
} |
wait_ms(3); |
/* |
* After some time (4ms) the axes shouldn't change anymore. |
*/ |
u = inb(io); |
for (i = 0; i < 1000; i++) |
if ((u ^ inb(io)) & 0xf) { |
outb(c, io); |
return; |
} |
/* |
* And now find the number of mirrors of the port. |
*/ |
for (i = 1; i < 5; i++) { |
//!!!if (check_region(io & (-1 << i), (1 << i))) /* Don't disturb anyone */ |
//!!! break; |
outb(0xff, io & (-1 << i)); |
for (j = b = 0; j < 1000; j++) |
if (inb(io & (-1 << i)) != inb((io & (-1 << i)) + (1 << i) - 1)) b++; |
wait_ms(3); |
if (b > 300) /* We allow 30% difference */ |
break; |
} |
i--; |
if (!(port = kmalloc(sizeof(struct ns558), GFP_KERNEL))) { |
printk(KERN_ERR "ns558: Memory allocation failed.\n"); |
return; |
} |
memset(port, 0, sizeof(struct ns558)); |
port->type = NS558_ISA; |
port->size = (1 << i); |
port->gameport.io = io; |
port->gameport.phys = port->phys; |
port->gameport.name = port->name; |
port->gameport.id.bustype = BUS_ISA; |
sprintf26(port->phys, "isa%04x/gameport0", io & (-1 << i)); |
sprintf26(port->name, "NS558 ISA"); |
request_region(io & (-1 << i), (1 << i), "ns558-isa"); |
gameport_register_port(&port->gameport); |
printk(KERN_INFO "gameport: NS558 ISA at %#x", port->gameport.io); |
if (port->size > 1) printk(" size %d", port->size); |
printk(" speed %d kHz\n", port->gameport.speed); |
list_add(&port->node, &ns558_list); |
} |
#ifdef CONFIG_PNP |
static struct pnp_device_id pnp_devids[] = { |
{ .id = "@P@0001", .driver_data = 0 }, /* ALS 100 */ |
{ .id = "@P@0020", .driver_data = 0 }, /* ALS 200 */ |
{ .id = "@P@1001", .driver_data = 0 }, /* ALS 100+ */ |
{ .id = "@P@2001", .driver_data = 0 }, /* ALS 120 */ |
{ .id = "ASB16fd", .driver_data = 0 }, /* AdLib NSC16 */ |
{ .id = "AZT3001", .driver_data = 0 }, /* AZT1008 */ |
{ .id = "CDC0001", .driver_data = 0 }, /* Opl3-SAx */ |
{ .id = "CSC0001", .driver_data = 0 }, /* CS4232 */ |
{ .id = "CSC000f", .driver_data = 0 }, /* CS4236 */ |
{ .id = "CSC0101", .driver_data = 0 }, /* CS4327 */ |
{ .id = "CTL7001", .driver_data = 0 }, /* SB16 */ |
{ .id = "CTL7002", .driver_data = 0 }, /* AWE64 */ |
{ .id = "CTL7005", .driver_data = 0 }, /* Vibra16 */ |
{ .id = "ENS2020", .driver_data = 0 }, /* SoundscapeVIVO */ |
{ .id = "ESS0001", .driver_data = 0 }, /* ES1869 */ |
{ .id = "ESS0005", .driver_data = 0 }, /* ES1878 */ |
{ .id = "ESS6880", .driver_data = 0 }, /* ES688 */ |
{ .id = "IBM0012", .driver_data = 0 }, /* CS4232 */ |
{ .id = "OPT0001", .driver_data = 0 }, /* OPTi Audio16 */ |
{ .id = "YMH0006", .driver_data = 0 }, /* Opl3-SA */ |
{ .id = "YMH0022", .driver_data = 0 }, /* Opl3-SAx */ |
{ .id = "PNPb02f", .driver_data = 0 }, /* Generic */ |
{ .id = "", }, |
}; |
MODULE_DEVICE_TABLE(pnp, pnp_devids); |
static int ns558_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *did) |
{ |
int ioport, iolen; |
struct ns558 *port; |
if (!pnp_port_valid(dev, 0)) { |
printk(KERN_WARNING "ns558: No i/o ports on a gameport? Weird\n"); |
return -ENODEV; |
} |
ioport = pnp_port_start(dev,0); |
iolen = pnp_port_len(dev,0); |
if (!request_region(ioport, iolen, "ns558-pnp")) |
return -EBUSY; |
if (!(port = kmalloc(sizeof(struct ns558), GFP_KERNEL))) { |
printk(KERN_ERR "ns558: Memory allocation failed.\n"); |
return -ENOMEM; |
} |
memset(port, 0, sizeof(struct ns558)); |
port->type = NS558_PNP; |
port->size = iolen; |
port->dev = dev; |
port->gameport.io = ioport; |
port->gameport.phys = port->phys; |
port->gameport.name = port->name; |
port->gameport.id.bustype = BUS_ISAPNP; |
port->gameport.id.version = 0x100; |
sprintf26(port->phys, "pnp%s/gameport0", dev->dev.bus_id); |
sprintf26(port->name, "%s", "NS558 PnP Gameport"); |
gameport_register_port(&port->gameport); |
printk(KERN_INFO "gameport: NS558 PnP at pnp%s io %#x", |
dev->dev.bus_id, port->gameport.io); |
if (iolen > 1) printk(" size %d", iolen); |
printk(" speed %d kHz\n", port->gameport.speed); |
list_add_tail(&port->node, &ns558_list); |
return 0; |
} |
static struct pnp_driver ns558_pnp_driver = { |
.name = "ns558", |
.id_table = pnp_devids, |
.probe = ns558_pnp_probe, |
}; |
#else |
static struct pnp_driver ns558_pnp_driver; |
#endif |
int __init ns558_init(void) |
{ |
int i = 0; |
/* |
* Probe for ISA ports. |
*/ |
while (ns558_isa_portlist[i]) |
ns558_isa_probe(ns558_isa_portlist[i++]); |
pnp_register_driver(&ns558_pnp_driver); |
return list_empty(&ns558_list) ? -ENODEV : 0; |
} |
void __exit ns558_exit(void) |
{ |
struct ns558 *port; |
list_for_each_entry(port, &ns558_list, node) { |
gameport_unregister_port(&port->gameport); |
switch (port->type) { |
#ifdef CONFIG_PNP |
case NS558_PNP: |
/* fall through */ |
#endif |
case NS558_ISA: |
release_region(port->gameport.io & ~(port->size - 1), port->size); |
break; |
default: |
break; |
} |
} |
pnp_unregister_driver(&ns558_pnp_driver); |
} |
module_init(ns558_init); |
module_exit(ns558_exit); |
/shark/trunk/drivers/linuxc26/include/linux/timer.h |
---|
18,6 → 18,9 |
unsigned long data; |
struct tvec_t_base_s *base; |
/* Added by Nino */ |
int event_timer; |
}; |
#define TIMER_MAGIC 0x4b87ad6e |
38,12 → 41,13 |
* init_timer() must be done to a timer prior calling *any* of the |
* other timer functions. |
*/ |
static inline void init_timer(struct timer_list * timer) |
/*static inline void init_timer(struct timer_list * timer) |
{ |
timer->base = NULL; |
timer->magic = TIMER_MAGIC; |
spin_lock_init(&timer->lock); |
} |
}*/ |
extern void init_timer(struct timer_list * timer); |
/*** |
* timer_pending - is a timer pending? |
55,10 → 59,11 |
* |
* return value: 1 if the timer is pending, 0 if not. |
*/ |
static inline int timer_pending(const struct timer_list * timer) |
/*static inline int timer_pending(const struct timer_list * timer) |
{ |
return timer->base != NULL; |
} |
}*/ |
extern int timer_pending(struct timer_list * timer); |
extern void add_timer_on(struct timer_list *timer, int cpu); |
extern int del_timer(struct timer_list * timer); |
79,10 → 84,11 |
* Timers with an ->expired field in the past will be executed in the next |
* timer tick. |
*/ |
static inline void add_timer(struct timer_list * timer) |
/*static inline void add_timer(struct timer_list * timer) |
{ |
__mod_timer(timer, timer->expires); |
} |
}*/ |
extern void add_timer(struct timer_list * timer); |
#ifdef CONFIG_SMP |
extern int del_timer_sync(struct timer_list * timer); |
/shark/trunk/drivers/linuxc26/include/linuxcomp.h |
---|
1,6 → 1,10 |
#ifndef __LINUX_COMP__ |
#define __LINUX_COMP__ |
#ifndef __i386__ |
#define __i386__ |
#endif |
#define CONFIG_M386 |
#define va_list void* |
32,4 → 36,27 |
void shark_internal_sem_wait(void *sem); |
void shark_internal_sem_post(void *sem); |
#define CONFIG_GAMEPORT |
#ifndef _STRUCT_TIMESPEC |
#define _STRUCT_TIMESPEC |
struct timespec { |
long tv_sec; /* Seconds */ |
long tv_nsec; /* Nanoseconds */ |
}; |
#endif /* _STRUCT_TIMESPEC */ |
extern TIME sys_gettime(struct timespec *t); |
#define jiffies26 (sys_gettime(NULL)*HZ/1000000) /* Has to be controlled... */ |
//#define jiffies26 (TIME)0 |
int shark_event_post(struct timespec *time, void (*handler)(void *p), void *par); |
int shark_event_delete(int index); |
/* Interrupt handler installation and removal */ |
int shark_handler_set(int no, void (*fast)(int), int pi); |
int shark_handler_remove(int no); |
#endif |
/shark/trunk/drivers/linuxc26/makefile |
---|
11,7 → 11,8 |
OBJS_PATH = $(BASE)/drivers/linuxc26 |
OBJS = bus.o linuxcomp.o core.o driver.o vsprintf.o interface.o kobject.o\ |
shark_linuxc26.o shark_glue.o class.o |
shark_linuxc26.o shark_glue.o class.o\ |
int.o timer.o |
C_OPT += -I../linuxc26/include |