Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (40 commits) Input: use full RCU API Input: remove tsdev interface Input: add support for Blackfin BF54x Keypad controller Input: appletouch - another fix for idle reset logic HWMON: hdaps - switch to using input-polldev Input: add support for SEGA Dreamcast keyboard Input: omap-keyboard - don't pretend we support changing keymap Input: lifebook - fix X and Y axis range Input: usbtouchscreen - add support for GeneralTouch devices Input: fix open count handling in input interfaces Input: keyboard - add CapsShift lock Input: adbhid - produce all CapsLock key events Input: ALPS - add signature for ThinkPad R61 Input: jornada720_kbd - send MSC_SCAN events Input: add support for the HP Jornada 7xx (710/720/728) touchscreen Input: add support for HP Jornada 7xx onboard keyboard Input: add support for HP Jornada onboard keyboard (HP6XX) Input: ucb1400_ts - use schedule_timeout_uninterruptible Input: xpad - fix dependancy on LEDS class Input: auto-select INPUT for MAC_EMUMOUSEBTN option ... Resolved conflicts manually in drivers/hwmon/applesmc.c: converting from a class device to a device and converting to use input-polldev created a few apparently trivial clashes..
This commit is contained in:
commit
f2e1d89f9b
42 changed files with 3829 additions and 2229 deletions
|
@ -205,20 +205,6 @@ Who: Len Brown <len.brown@intel.com>
|
||||||
|
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
What: Compaq touchscreen device emulation
|
|
||||||
When: Oct 2007
|
|
||||||
Files: drivers/input/tsdev.c
|
|
||||||
Why: The code says it was obsolete when it was written in 2001.
|
|
||||||
tslib is a userspace library which does anything tsdev can do and
|
|
||||||
much more besides in userspace where this code belongs. There is no
|
|
||||||
longer any need for tsdev and applications should have converted to
|
|
||||||
use tslib by now.
|
|
||||||
The name "tsdev" is also extremely confusing and lots of people have
|
|
||||||
it loaded when they don't need/use it.
|
|
||||||
Who: Richard Purdie <rpurdie@rpsys.net>
|
|
||||||
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
What: i2c-ixp2000, i2c-ixp4xx and scx200_i2c drivers
|
What: i2c-ixp2000, i2c-ixp4xx and scx200_i2c drivers
|
||||||
When: September 2007
|
When: September 2007
|
||||||
Why: Obsolete. The new i2c-gpio driver replaces all hardware-specific
|
Why: Obsolete. The new i2c-gpio driver replaces all hardware-specific
|
||||||
|
|
|
@ -1890,9 +1890,6 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||||
Format:
|
Format:
|
||||||
<io>,<irq>,<dma>,<dma2>,<sb_io>,<sb_irq>,<sb_dma>,<mpu_io>,<mpu_irq>
|
<io>,<irq>,<dma>,<dma2>,<sb_io>,<sb_irq>,<sb_dma>,<mpu_io>,<mpu_irq>
|
||||||
|
|
||||||
tsdev.xres= [TS] Horizontal screen resolution.
|
|
||||||
tsdev.yres= [TS] Vertical screen resolution.
|
|
||||||
|
|
||||||
turbografx.map[2|3]= [HW,JOY]
|
turbografx.map[2|3]= [HW,JOY]
|
||||||
TurboGraFX parallel port interface
|
TurboGraFX parallel port interface
|
||||||
Format:
|
Format:
|
||||||
|
|
|
@ -88,7 +88,7 @@ static struct platform_device bf54x_lq043_device = {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_KEYBOARD_BFIN) || defined(CONFIG_KEYBOARD_BFIN_MODULE)
|
#if defined(CONFIG_KEYBOARD_BFIN) || defined(CONFIG_KEYBOARD_BFIN_MODULE)
|
||||||
static int bf548_keymap[] = {
|
static const unsigned int bf548_keymap[] = {
|
||||||
KEYVAL(0, 0, KEY_ENTER),
|
KEYVAL(0, 0, KEY_ENTER),
|
||||||
KEYVAL(0, 1, KEY_HELP),
|
KEYVAL(0, 1, KEY_HELP),
|
||||||
KEYVAL(0, 2, KEY_0),
|
KEYVAL(0, 2, KEY_0),
|
||||||
|
|
|
@ -1,457 +0,0 @@
|
||||||
/*
|
|
||||||
* linux/drivers/char/ec3104_keyb.c
|
|
||||||
*
|
|
||||||
* Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
|
|
||||||
*
|
|
||||||
* based on linux/drivers/char/pc_keyb.c, which had the following comments:
|
|
||||||
*
|
|
||||||
* Separation of the PC low-level part by Geert Uytterhoeven, May 1997
|
|
||||||
* See keyboard.c for the whole history.
|
|
||||||
*
|
|
||||||
* Major cleanup by Martin Mares, May 1997
|
|
||||||
*
|
|
||||||
* Combined the keyboard and PS/2 mouse handling into one file,
|
|
||||||
* because they share the same hardware.
|
|
||||||
* Johan Myreen <jem@iki.fi> 1998-10-08.
|
|
||||||
*
|
|
||||||
* Code fixes to handle mouse ACKs properly.
|
|
||||||
* C. Scott Ananian <cananian@alumni.princeton.edu> 1999-01-29.
|
|
||||||
*/
|
|
||||||
/* EC3104 note:
|
|
||||||
* This code was written without any documentation about the EC3104 chip. While
|
|
||||||
* I hope I got most of the basic functionality right, the register names I use
|
|
||||||
* are most likely completely different from those in the chip documentation.
|
|
||||||
*
|
|
||||||
* If you have any further information about the EC3104, please tell me
|
|
||||||
* (prumpf@tux.org).
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/tty.h>
|
|
||||||
#include <linux/mm.h>
|
|
||||||
#include <linux/signal.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/kbd_ll.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/random.h>
|
|
||||||
#include <linux/poll.h>
|
|
||||||
#include <linux/miscdevice.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/kbd_kern.h>
|
|
||||||
#include <linux/bitops.h>
|
|
||||||
|
|
||||||
#include <asm/keyboard.h>
|
|
||||||
#include <asm/uaccess.h>
|
|
||||||
#include <asm/irq.h>
|
|
||||||
#include <asm/system.h>
|
|
||||||
#include <asm/ec3104.h>
|
|
||||||
|
|
||||||
#include <asm/io.h>
|
|
||||||
|
|
||||||
/* Some configuration switches are present in the include file... */
|
|
||||||
|
|
||||||
#include <linux/pc_keyb.h>
|
|
||||||
|
|
||||||
#define MSR_CTS 0x10
|
|
||||||
#define MCR_RTS 0x02
|
|
||||||
#define LSR_DR 0x01
|
|
||||||
#define LSR_BOTH_EMPTY 0x60
|
|
||||||
|
|
||||||
static struct e5_struct {
|
|
||||||
u8 packet[8];
|
|
||||||
int pos;
|
|
||||||
int length;
|
|
||||||
|
|
||||||
u8 cached_mcr;
|
|
||||||
u8 last_msr;
|
|
||||||
} ec3104_keyb;
|
|
||||||
|
|
||||||
/* Simple translation table for the SysRq keys */
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_MAGIC_SYSRQ
|
|
||||||
unsigned char ec3104_kbd_sysrq_xlate[128] =
|
|
||||||
"\000\0331234567890-=\177\t" /* 0x00 - 0x0f */
|
|
||||||
"qwertyuiop[]\r\000as" /* 0x10 - 0x1f */
|
|
||||||
"dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */
|
|
||||||
"bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */
|
|
||||||
"\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */
|
|
||||||
"230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */
|
|
||||||
"\r\000/"; /* 0x60 - 0x6f */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void kbd_write_command_w(int data);
|
|
||||||
static void kbd_write_output_w(int data);
|
|
||||||
#ifdef CONFIG_PSMOUSE
|
|
||||||
static void aux_write_ack(int val);
|
|
||||||
static void __aux_write_ack(int val);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static DEFINE_SPINLOCK(kbd_controller_lock);
|
|
||||||
static unsigned char handle_kbd_event(void);
|
|
||||||
|
|
||||||
/* used only by send_data - set by keyboard_interrupt */
|
|
||||||
static volatile unsigned char reply_expected;
|
|
||||||
static volatile unsigned char acknowledge;
|
|
||||||
static volatile unsigned char resend;
|
|
||||||
|
|
||||||
|
|
||||||
int ec3104_kbd_setkeycode(unsigned int scancode, unsigned int keycode)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ec3104_kbd_getkeycode(unsigned int scancode)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* yes, it probably would be faster to use an array. I don't care. */
|
|
||||||
|
|
||||||
static inline unsigned char ec3104_scan2key(unsigned char scancode)
|
|
||||||
{
|
|
||||||
switch (scancode) {
|
|
||||||
case 1: /* '`' */
|
|
||||||
return 41;
|
|
||||||
|
|
||||||
case 2 ... 27:
|
|
||||||
return scancode;
|
|
||||||
|
|
||||||
case 28: /* '\\' */
|
|
||||||
return 43;
|
|
||||||
|
|
||||||
case 29 ... 39:
|
|
||||||
return scancode + 1;
|
|
||||||
|
|
||||||
case 40: /* '\r' */
|
|
||||||
return 28;
|
|
||||||
|
|
||||||
case 41 ... 50:
|
|
||||||
return scancode + 3;
|
|
||||||
|
|
||||||
case 51: /* ' ' */
|
|
||||||
return 57;
|
|
||||||
|
|
||||||
case 52: /* escape */
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
case 54: /* insert/delete (labelled delete) */
|
|
||||||
/* this should arguably be 110, but I'd like to have ctrl-alt-del
|
|
||||||
* working with a standard keymap */
|
|
||||||
return 111;
|
|
||||||
|
|
||||||
case 55: /* left */
|
|
||||||
return 105;
|
|
||||||
case 56: /* home */
|
|
||||||
return 102;
|
|
||||||
case 57: /* end */
|
|
||||||
return 107;
|
|
||||||
case 58: /* up */
|
|
||||||
return 103;
|
|
||||||
case 59: /* down */
|
|
||||||
return 108;
|
|
||||||
case 60: /* pgup */
|
|
||||||
return 104;
|
|
||||||
case 61: /* pgdown */
|
|
||||||
return 109;
|
|
||||||
case 62: /* right */
|
|
||||||
return 106;
|
|
||||||
|
|
||||||
case 79 ... 88: /* f1 - f10 */
|
|
||||||
return scancode - 20;
|
|
||||||
|
|
||||||
case 89 ... 90: /* f11 - f12 */
|
|
||||||
return scancode - 2;
|
|
||||||
|
|
||||||
case 91: /* left shift */
|
|
||||||
return 42;
|
|
||||||
|
|
||||||
case 92: /* right shift */
|
|
||||||
return 54;
|
|
||||||
|
|
||||||
case 93: /* left alt */
|
|
||||||
return 56;
|
|
||||||
case 94: /* right alt */
|
|
||||||
return 100;
|
|
||||||
case 95: /* left ctrl */
|
|
||||||
return 29;
|
|
||||||
case 96: /* right ctrl */
|
|
||||||
return 97;
|
|
||||||
|
|
||||||
case 97: /* caps lock */
|
|
||||||
return 58;
|
|
||||||
case 102: /* left windows */
|
|
||||||
return 125;
|
|
||||||
case 103: /* right windows */
|
|
||||||
return 126;
|
|
||||||
|
|
||||||
case 106: /* Fn */
|
|
||||||
/* this is wrong. */
|
|
||||||
return 84;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ec3104_kbd_translate(unsigned char scancode, unsigned char *keycode,
|
|
||||||
char raw_mode)
|
|
||||||
{
|
|
||||||
scancode &= 0x7f;
|
|
||||||
|
|
||||||
*keycode = ec3104_scan2key(scancode);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char ec3104_kbd_unexpected_up(unsigned char keycode)
|
|
||||||
{
|
|
||||||
return 0200;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void handle_keyboard_event(unsigned char scancode)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_VT
|
|
||||||
handle_scancode(scancode, !(scancode & 0x80));
|
|
||||||
#endif
|
|
||||||
tasklet_schedule(&keyboard_tasklet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ec3104_kbd_leds(unsigned char leds)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static u8 e5_checksum(u8 *packet, int count)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
u8 sum = 0;
|
|
||||||
|
|
||||||
for (i=0; i<count; i++)
|
|
||||||
sum ^= packet[i];
|
|
||||||
|
|
||||||
if (sum & 0x80)
|
|
||||||
sum ^= 0xc0;
|
|
||||||
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void e5_wait_for_cts(struct e5_struct *k)
|
|
||||||
{
|
|
||||||
u8 msr;
|
|
||||||
|
|
||||||
do {
|
|
||||||
msr = ctrl_inb(EC3104_SER4_MSR);
|
|
||||||
} while (!(msr & MSR_CTS));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void e5_send_byte(u8 byte, struct e5_struct *k)
|
|
||||||
{
|
|
||||||
u8 status;
|
|
||||||
|
|
||||||
do {
|
|
||||||
status = ctrl_inb(EC3104_SER4_LSR);
|
|
||||||
} while ((status & LSR_BOTH_EMPTY) != LSR_BOTH_EMPTY);
|
|
||||||
|
|
||||||
printk("<%02x>", byte);
|
|
||||||
|
|
||||||
ctrl_outb(byte, EC3104_SER4_DATA);
|
|
||||||
|
|
||||||
do {
|
|
||||||
status = ctrl_inb(EC3104_SER4_LSR);
|
|
||||||
} while ((status & LSR_BOTH_EMPTY) != LSR_BOTH_EMPTY);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static int e5_send_packet(u8 *packet, int count, struct e5_struct *k)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
disable_irq(EC3104_IRQ_SER4);
|
|
||||||
|
|
||||||
if (k->cached_mcr & MCR_RTS) {
|
|
||||||
printk("e5_send_packet: too slow\n");
|
|
||||||
enable_irq(EC3104_IRQ_SER4);
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
k->cached_mcr |= MCR_RTS;
|
|
||||||
ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
|
|
||||||
|
|
||||||
e5_wait_for_cts(k);
|
|
||||||
|
|
||||||
printk("p: ");
|
|
||||||
|
|
||||||
for(i=0; i<count; i++)
|
|
||||||
e5_send_byte(packet[i], k);
|
|
||||||
|
|
||||||
e5_send_byte(e5_checksum(packet, count), k);
|
|
||||||
|
|
||||||
printk("\n");
|
|
||||||
|
|
||||||
udelay(1500);
|
|
||||||
|
|
||||||
k->cached_mcr &= ~MCR_RTS;
|
|
||||||
ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
|
|
||||||
|
|
||||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
enable_irq(EC3104_IRQ_SER4);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* E5 packets we know about:
|
|
||||||
* E5->host 0x80 0x05 <checksum> - resend packet
|
|
||||||
* host->E5 0x83 0x43 <contrast> - set LCD contrast
|
|
||||||
* host->E5 0x85 0x41 0x02 <brightness> 0x02 - set LCD backlight
|
|
||||||
* E5->host 0x87 <ps2 packet> 0x00 <checksum> - external PS2
|
|
||||||
* E5->host 0x88 <scancode> <checksum> - key press
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void e5_receive(struct e5_struct *k)
|
|
||||||
{
|
|
||||||
k->packet[k->pos++] = ctrl_inb(EC3104_SER4_DATA);
|
|
||||||
|
|
||||||
if (k->pos == 1) {
|
|
||||||
switch(k->packet[0]) {
|
|
||||||
case 0x80:
|
|
||||||
k->length = 3;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x87: /* PS2 ext */
|
|
||||||
k->length = 6;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x88: /* keyboard */
|
|
||||||
k->length = 3;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
k->length = 1;
|
|
||||||
printk(KERN_WARNING "unknown E5 packet %02x\n",
|
|
||||||
k->packet[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (k->pos == k->length) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (e5_checksum(k->packet, k->length) != 0)
|
|
||||||
printk(KERN_WARNING "E5: wrong checksum\n");
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
printk("E5 packet [");
|
|
||||||
for(i=0; i<k->length; i++) {
|
|
||||||
printk("%02x ", k->packet[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
printk("(%02x)]\n", e5_checksum(k->packet, k->length-1));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
switch(k->packet[0]) {
|
|
||||||
case 0x80:
|
|
||||||
case 0x88:
|
|
||||||
handle_keyboard_event(k->packet[1]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
k->pos = k->length = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ec3104_keyb_interrupt(int irq, void *data)
|
|
||||||
{
|
|
||||||
struct e5_struct *k = &ec3104_keyb;
|
|
||||||
u8 msr, lsr;
|
|
||||||
|
|
||||||
msr = ctrl_inb(EC3104_SER4_MSR);
|
|
||||||
|
|
||||||
if ((msr & MSR_CTS) && !(k->last_msr & MSR_CTS)) {
|
|
||||||
if (k->cached_mcr & MCR_RTS)
|
|
||||||
printk("confused: RTS already high\n");
|
|
||||||
/* CTS went high. Send RTS. */
|
|
||||||
k->cached_mcr |= MCR_RTS;
|
|
||||||
|
|
||||||
ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
|
|
||||||
} else if ((!(msr & MSR_CTS)) && (k->last_msr & MSR_CTS)) {
|
|
||||||
/* CTS went low. */
|
|
||||||
if (!(k->cached_mcr & MCR_RTS))
|
|
||||||
printk("confused: RTS already low\n");
|
|
||||||
|
|
||||||
k->cached_mcr &= ~MCR_RTS;
|
|
||||||
|
|
||||||
ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
|
|
||||||
}
|
|
||||||
|
|
||||||
k->last_msr = msr;
|
|
||||||
|
|
||||||
lsr = ctrl_inb(EC3104_SER4_LSR);
|
|
||||||
|
|
||||||
if (lsr & LSR_DR)
|
|
||||||
e5_receive(k);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ec3104_keyb_clear_state(void)
|
|
||||||
{
|
|
||||||
struct e5_struct *k = &ec3104_keyb;
|
|
||||||
u8 msr, lsr;
|
|
||||||
|
|
||||||
/* we want CTS to be low */
|
|
||||||
k->last_msr = 0;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
msleep(100);
|
|
||||||
|
|
||||||
msr = ctrl_inb(EC3104_SER4_MSR);
|
|
||||||
|
|
||||||
lsr = ctrl_inb(EC3104_SER4_LSR);
|
|
||||||
|
|
||||||
if (lsr & LSR_DR) {
|
|
||||||
e5_receive(k);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((msr & MSR_CTS) && !(k->last_msr & MSR_CTS)) {
|
|
||||||
if (k->cached_mcr & MCR_RTS)
|
|
||||||
printk("confused: RTS already high\n");
|
|
||||||
/* CTS went high. Send RTS. */
|
|
||||||
k->cached_mcr |= MCR_RTS;
|
|
||||||
|
|
||||||
ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
|
|
||||||
} else if ((!(msr & MSR_CTS)) && (k->last_msr & MSR_CTS)) {
|
|
||||||
/* CTS went low. */
|
|
||||||
if (!(k->cached_mcr & MCR_RTS))
|
|
||||||
printk("confused: RTS already low\n");
|
|
||||||
|
|
||||||
k->cached_mcr &= ~MCR_RTS;
|
|
||||||
|
|
||||||
ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
|
|
||||||
} else
|
|
||||||
break;
|
|
||||||
|
|
||||||
k->last_msr = msr;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void __init ec3104_kbd_init_hw(void)
|
|
||||||
{
|
|
||||||
ec3104_keyb.last_msr = ctrl_inb(EC3104_SER4_MSR);
|
|
||||||
ec3104_keyb.cached_mcr = ctrl_inb(EC3104_SER4_MCR);
|
|
||||||
|
|
||||||
ec3104_keyb_clear_state();
|
|
||||||
|
|
||||||
/* Ok, finally allocate the IRQ, and off we go.. */
|
|
||||||
request_irq(EC3104_IRQ_SER4, ec3104_keyb_interrupt, 0, "keyboard", NULL);
|
|
||||||
}
|
|
|
@ -158,6 +158,7 @@ config SENSORS_K8TEMP
|
||||||
config SENSORS_AMS
|
config SENSORS_AMS
|
||||||
tristate "Apple Motion Sensor driver"
|
tristate "Apple Motion Sensor driver"
|
||||||
depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL
|
depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL
|
||||||
|
select INPUT_POLLDEV
|
||||||
help
|
help
|
||||||
Support for the motion sensor included in PowerBooks. Includes
|
Support for the motion sensor included in PowerBooks. Includes
|
||||||
implementations for PMU and I2C.
|
implementations for PMU and I2C.
|
||||||
|
@ -701,6 +702,7 @@ config SENSORS_W83627EHF
|
||||||
config SENSORS_HDAPS
|
config SENSORS_HDAPS
|
||||||
tristate "IBM Hard Drive Active Protection System (hdaps)"
|
tristate "IBM Hard Drive Active Protection System (hdaps)"
|
||||||
depends on INPUT && X86
|
depends on INPUT && X86
|
||||||
|
select INPUT_POLLDEV
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
This driver provides support for the IBM Hard Drive Active Protection
|
This driver provides support for the IBM Hard Drive Active Protection
|
||||||
|
@ -722,6 +724,7 @@ config SENSORS_APPLESMC
|
||||||
depends on INPUT && X86
|
depends on INPUT && X86
|
||||||
select NEW_LEDS
|
select NEW_LEDS
|
||||||
select LEDS_CLASS
|
select LEDS_CLASS
|
||||||
|
select INPUT_POLLDEV
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
This driver provides support for the Apple System Management
|
This driver provides support for the Apple System Management
|
||||||
|
|
|
@ -27,11 +27,11 @@ static unsigned int invert;
|
||||||
module_param(invert, bool, 0644);
|
module_param(invert, bool, 0644);
|
||||||
MODULE_PARM_DESC(invert, "Invert input data on X and Y axis");
|
MODULE_PARM_DESC(invert, "Invert input data on X and Y axis");
|
||||||
|
|
||||||
static int ams_input_kthread(void *data)
|
static void ams_idev_poll(struct input_polled_dev *dev)
|
||||||
{
|
{
|
||||||
|
struct input_dev *idev = dev->input;
|
||||||
s8 x, y, z;
|
s8 x, y, z;
|
||||||
|
|
||||||
while (!kthread_should_stop()) {
|
|
||||||
mutex_lock(&ams_info.lock);
|
mutex_lock(&ams_info.lock);
|
||||||
|
|
||||||
ams_sensors(&x, &y, &z);
|
ams_sensors(&x, &y, &z);
|
||||||
|
@ -40,34 +40,19 @@ static int ams_input_kthread(void *data)
|
||||||
y -= ams_info.ycalib;
|
y -= ams_info.ycalib;
|
||||||
z -= ams_info.zcalib;
|
z -= ams_info.zcalib;
|
||||||
|
|
||||||
input_report_abs(ams_info.idev, ABS_X, invert ? -x : x);
|
input_report_abs(idev, ABS_X, invert ? -x : x);
|
||||||
input_report_abs(ams_info.idev, ABS_Y, invert ? -y : y);
|
input_report_abs(idev, ABS_Y, invert ? -y : y);
|
||||||
input_report_abs(ams_info.idev, ABS_Z, z);
|
input_report_abs(idev, ABS_Z, z);
|
||||||
|
|
||||||
input_sync(ams_info.idev);
|
input_sync(idev);
|
||||||
|
|
||||||
mutex_unlock(&ams_info.lock);
|
mutex_unlock(&ams_info.lock);
|
||||||
|
|
||||||
msleep(25);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ams_input_open(struct input_dev *dev)
|
|
||||||
{
|
|
||||||
ams_info.kthread = kthread_run(ams_input_kthread, NULL, "kams");
|
|
||||||
return IS_ERR(ams_info.kthread) ? PTR_ERR(ams_info.kthread) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ams_input_close(struct input_dev *dev)
|
|
||||||
{
|
|
||||||
kthread_stop(ams_info.kthread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call with ams_info.lock held! */
|
/* Call with ams_info.lock held! */
|
||||||
static void ams_input_enable(void)
|
static void ams_input_enable(void)
|
||||||
{
|
{
|
||||||
|
struct input_dev *input;
|
||||||
s8 x, y, z;
|
s8 x, y, z;
|
||||||
|
|
||||||
if (ams_info.idev)
|
if (ams_info.idev)
|
||||||
|
@ -78,27 +63,29 @@ static void ams_input_enable(void)
|
||||||
ams_info.ycalib = y;
|
ams_info.ycalib = y;
|
||||||
ams_info.zcalib = z;
|
ams_info.zcalib = z;
|
||||||
|
|
||||||
ams_info.idev = input_allocate_device();
|
ams_info.idev = input_allocate_polled_device();
|
||||||
if (!ams_info.idev)
|
if (!ams_info.idev)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ams_info.idev->name = "Apple Motion Sensor";
|
ams_info.idev->poll = ams_idev_poll;
|
||||||
ams_info.idev->id.bustype = ams_info.bustype;
|
ams_info.idev->poll_interval = 25;
|
||||||
ams_info.idev->id.vendor = 0;
|
|
||||||
ams_info.idev->open = ams_input_open;
|
|
||||||
ams_info.idev->close = ams_input_close;
|
|
||||||
ams_info.idev->dev.parent = &ams_info.of_dev->dev;
|
|
||||||
|
|
||||||
input_set_abs_params(ams_info.idev, ABS_X, -50, 50, 3, 0);
|
input = ams_info.idev->input;
|
||||||
input_set_abs_params(ams_info.idev, ABS_Y, -50, 50, 3, 0);
|
input->name = "Apple Motion Sensor";
|
||||||
input_set_abs_params(ams_info.idev, ABS_Z, -50, 50, 3, 0);
|
input->id.bustype = ams_info.bustype;
|
||||||
|
input->id.vendor = 0;
|
||||||
|
input->dev.parent = &ams_info.of_dev->dev;
|
||||||
|
|
||||||
set_bit(EV_ABS, ams_info.idev->evbit);
|
input_set_abs_params(input, ABS_X, -50, 50, 3, 0);
|
||||||
set_bit(EV_KEY, ams_info.idev->evbit);
|
input_set_abs_params(input, ABS_Y, -50, 50, 3, 0);
|
||||||
set_bit(BTN_TOUCH, ams_info.idev->keybit);
|
input_set_abs_params(input, ABS_Z, -50, 50, 3, 0);
|
||||||
|
|
||||||
if (input_register_device(ams_info.idev)) {
|
set_bit(EV_ABS, input->evbit);
|
||||||
input_free_device(ams_info.idev);
|
set_bit(EV_KEY, input->evbit);
|
||||||
|
set_bit(BTN_TOUCH, input->keybit);
|
||||||
|
|
||||||
|
if (input_register_polled_device(ams_info.idev)) {
|
||||||
|
input_free_polled_device(ams_info.idev);
|
||||||
ams_info.idev = NULL;
|
ams_info.idev = NULL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -108,7 +95,8 @@ static void ams_input_enable(void)
|
||||||
static void ams_input_disable(void)
|
static void ams_input_disable(void)
|
||||||
{
|
{
|
||||||
if (ams_info.idev) {
|
if (ams_info.idev) {
|
||||||
input_unregister_device(ams_info.idev);
|
input_unregister_polled_device(ams_info.idev);
|
||||||
|
input_free_polled_device(ams_info.idev);
|
||||||
ams_info.idev = NULL;
|
ams_info.idev = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input-polldev.h>
|
||||||
#include <linux/kthread.h>
|
#include <linux/kthread.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
|
@ -52,8 +52,7 @@ struct ams {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Joystick emulation */
|
/* Joystick emulation */
|
||||||
struct task_struct *kthread;
|
struct input_polled_dev *idev;
|
||||||
struct input_dev *idev;
|
|
||||||
__u16 bustype;
|
__u16 bustype;
|
||||||
|
|
||||||
/* calibrated null values */
|
/* calibrated null values */
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input-polldev.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
|
@ -103,7 +103,7 @@ static const char* fan_speed_keys[] = {
|
||||||
#define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */
|
#define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */
|
||||||
#define INIT_WAIT_MSECS 50 /* ... in 50ms increments */
|
#define INIT_WAIT_MSECS 50 /* ... in 50ms increments */
|
||||||
|
|
||||||
#define APPLESMC_POLL_PERIOD (HZ/20) /* poll for input every 1/20s */
|
#define APPLESMC_POLL_INTERVAL 50 /* msecs */
|
||||||
#define APPLESMC_INPUT_FUZZ 4 /* input event threshold */
|
#define APPLESMC_INPUT_FUZZ 4 /* input event threshold */
|
||||||
#define APPLESMC_INPUT_FLAT 4
|
#define APPLESMC_INPUT_FLAT 4
|
||||||
|
|
||||||
|
@ -125,9 +125,8 @@ static const int debug;
|
||||||
static struct platform_device *pdev;
|
static struct platform_device *pdev;
|
||||||
static s16 rest_x;
|
static s16 rest_x;
|
||||||
static s16 rest_y;
|
static s16 rest_y;
|
||||||
static struct timer_list applesmc_timer;
|
|
||||||
static struct input_dev *applesmc_idev;
|
|
||||||
static struct device *hwmon_dev;
|
static struct device *hwmon_dev;
|
||||||
|
static struct input_polled_dev *applesmc_idev;
|
||||||
|
|
||||||
/* Indicates whether this computer has an accelerometer. */
|
/* Indicates whether this computer has an accelerometer. */
|
||||||
static unsigned int applesmc_accelerometer;
|
static unsigned int applesmc_accelerometer;
|
||||||
|
@ -138,7 +137,7 @@ static unsigned int applesmc_light;
|
||||||
/* Indicates which temperature sensors set to use. */
|
/* Indicates which temperature sensors set to use. */
|
||||||
static unsigned int applesmc_temperature_set;
|
static unsigned int applesmc_temperature_set;
|
||||||
|
|
||||||
static struct mutex applesmc_lock;
|
static DEFINE_MUTEX(applesmc_lock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Last index written to key_at_index sysfs file, and value to use for all other
|
* Last index written to key_at_index sysfs file, and value to use for all other
|
||||||
|
@ -455,27 +454,12 @@ static void applesmc_calibrate(void)
|
||||||
rest_x = -rest_x;
|
rest_x = -rest_x;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int applesmc_idev_open(struct input_dev *dev)
|
static void applesmc_idev_poll(struct input_polled_dev *dev)
|
||||||
{
|
|
||||||
add_timer(&applesmc_timer);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void applesmc_idev_close(struct input_dev *dev)
|
|
||||||
{
|
|
||||||
del_timer_sync(&applesmc_timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void applesmc_idev_poll(unsigned long unused)
|
|
||||||
{
|
{
|
||||||
|
struct input_dev *idev = dev->input;
|
||||||
s16 x, y;
|
s16 x, y;
|
||||||
|
|
||||||
/* Cannot sleep. Try nonblockingly. If we fail, try again later. */
|
mutex_lock(&applesmc_lock);
|
||||||
if (!mutex_trylock(&applesmc_lock)) {
|
|
||||||
mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (applesmc_read_motion_sensor(SENSOR_X, &x))
|
if (applesmc_read_motion_sensor(SENSOR_X, &x))
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -483,13 +467,11 @@ static void applesmc_idev_poll(unsigned long unused)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
x = -x;
|
x = -x;
|
||||||
input_report_abs(applesmc_idev, ABS_X, x - rest_x);
|
input_report_abs(idev, ABS_X, x - rest_x);
|
||||||
input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
|
input_report_abs(idev, ABS_Y, y - rest_y);
|
||||||
input_sync(applesmc_idev);
|
input_sync(idev);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
|
|
||||||
|
|
||||||
mutex_unlock(&applesmc_lock);
|
mutex_unlock(&applesmc_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -821,8 +803,7 @@ static ssize_t applesmc_key_at_index_read_show(struct device *dev,
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
return info[0];
|
return info[0];
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1093,6 +1074,7 @@ static int applesmc_dmi_match(const struct dmi_system_id *id)
|
||||||
/* Create accelerometer ressources */
|
/* Create accelerometer ressources */
|
||||||
static int applesmc_create_accelerometer(void)
|
static int applesmc_create_accelerometer(void)
|
||||||
{
|
{
|
||||||
|
struct input_dev *idev;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = sysfs_create_group(&pdev->dev.kobj,
|
ret = sysfs_create_group(&pdev->dev.kobj,
|
||||||
|
@ -1100,40 +1082,37 @@ static int applesmc_create_accelerometer(void)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
applesmc_idev = input_allocate_device();
|
applesmc_idev = input_allocate_polled_device();
|
||||||
if (!applesmc_idev) {
|
if (!applesmc_idev) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out_sysfs;
|
goto out_sysfs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applesmc_idev->poll = applesmc_idev_poll;
|
||||||
|
applesmc_idev->poll_interval = APPLESMC_POLL_INTERVAL;
|
||||||
|
|
||||||
/* initial calibrate for the input device */
|
/* initial calibrate for the input device */
|
||||||
applesmc_calibrate();
|
applesmc_calibrate();
|
||||||
|
|
||||||
/* initialize the input class */
|
/* initialize the input device */
|
||||||
applesmc_idev->name = "applesmc";
|
idev = applesmc_idev->input;
|
||||||
applesmc_idev->id.bustype = BUS_HOST;
|
idev->name = "applesmc";
|
||||||
applesmc_idev->dev.parent = &pdev->dev;
|
idev->id.bustype = BUS_HOST;
|
||||||
applesmc_idev->evbit[0] = BIT(EV_ABS);
|
idev->dev.parent = &pdev->dev;
|
||||||
applesmc_idev->open = applesmc_idev_open;
|
idev->evbit[0] = BIT(EV_ABS);
|
||||||
applesmc_idev->close = applesmc_idev_close;
|
input_set_abs_params(idev, ABS_X,
|
||||||
input_set_abs_params(applesmc_idev, ABS_X,
|
|
||||||
-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
|
-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
|
||||||
input_set_abs_params(applesmc_idev, ABS_Y,
|
input_set_abs_params(idev, ABS_Y,
|
||||||
-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
|
-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
|
||||||
|
|
||||||
ret = input_register_device(applesmc_idev);
|
ret = input_register_polled_device(applesmc_idev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_idev;
|
goto out_idev;
|
||||||
|
|
||||||
/* start up our timer for the input device */
|
|
||||||
init_timer(&applesmc_timer);
|
|
||||||
applesmc_timer.function = applesmc_idev_poll;
|
|
||||||
applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_idev:
|
out_idev:
|
||||||
input_free_device(applesmc_idev);
|
input_free_polled_device(applesmc_idev);
|
||||||
|
|
||||||
out_sysfs:
|
out_sysfs:
|
||||||
sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
|
sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
|
||||||
|
@ -1146,8 +1125,8 @@ out:
|
||||||
/* Release all ressources used by the accelerometer */
|
/* Release all ressources used by the accelerometer */
|
||||||
static void applesmc_release_accelerometer(void)
|
static void applesmc_release_accelerometer(void)
|
||||||
{
|
{
|
||||||
del_timer_sync(&applesmc_timer);
|
input_unregister_polled_device(applesmc_idev);
|
||||||
input_unregister_device(applesmc_idev);
|
input_free_polled_device(applesmc_idev);
|
||||||
sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
|
sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1184,8 +1163,6 @@ static int __init applesmc_init(void)
|
||||||
int count;
|
int count;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
mutex_init(&applesmc_lock);
|
|
||||||
|
|
||||||
if (!dmi_check_system(applesmc_whitelist)) {
|
if (!dmi_check_system(applesmc_whitelist)) {
|
||||||
printk(KERN_WARNING "applesmc: supported laptop not found!\n");
|
printk(KERN_WARNING "applesmc: supported laptop not found!\n");
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input-polldev.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -61,13 +61,12 @@
|
||||||
#define INIT_TIMEOUT_MSECS 4000 /* wait up to 4s for device init ... */
|
#define INIT_TIMEOUT_MSECS 4000 /* wait up to 4s for device init ... */
|
||||||
#define INIT_WAIT_MSECS 200 /* ... in 200ms increments */
|
#define INIT_WAIT_MSECS 200 /* ... in 200ms increments */
|
||||||
|
|
||||||
#define HDAPS_POLL_PERIOD (HZ/20) /* poll for input every 1/20s */
|
#define HDAPS_POLL_INTERVAL 50 /* poll for input every 1/20s (50 ms)*/
|
||||||
#define HDAPS_INPUT_FUZZ 4 /* input event threshold */
|
#define HDAPS_INPUT_FUZZ 4 /* input event threshold */
|
||||||
#define HDAPS_INPUT_FLAT 4
|
#define HDAPS_INPUT_FLAT 4
|
||||||
|
|
||||||
static struct timer_list hdaps_timer;
|
|
||||||
static struct platform_device *pdev;
|
static struct platform_device *pdev;
|
||||||
static struct input_dev *hdaps_idev;
|
static struct input_polled_dev *hdaps_idev;
|
||||||
static unsigned int hdaps_invert;
|
static unsigned int hdaps_invert;
|
||||||
static u8 km_activity;
|
static u8 km_activity;
|
||||||
static int rest_x;
|
static int rest_x;
|
||||||
|
@ -323,24 +322,19 @@ static void hdaps_calibrate(void)
|
||||||
__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y);
|
__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hdaps_mousedev_poll(unsigned long unused)
|
static void hdaps_mousedev_poll(struct input_polled_dev *dev)
|
||||||
{
|
{
|
||||||
|
struct input_dev *input_dev = dev->input;
|
||||||
int x, y;
|
int x, y;
|
||||||
|
|
||||||
/* Cannot sleep. Try nonblockingly. If we fail, try again later. */
|
mutex_lock(&hdaps_mtx);
|
||||||
if (mutex_trylock(&hdaps_mtx)) {
|
|
||||||
mod_timer(&hdaps_timer,jiffies + HDAPS_POLL_PERIOD);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y))
|
if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
input_report_abs(hdaps_idev, ABS_X, x - rest_x);
|
input_report_abs(input_dev, ABS_X, x - rest_x);
|
||||||
input_report_abs(hdaps_idev, ABS_Y, y - rest_y);
|
input_report_abs(input_dev, ABS_Y, y - rest_y);
|
||||||
input_sync(hdaps_idev);
|
input_sync(input_dev);
|
||||||
|
|
||||||
mod_timer(&hdaps_timer, jiffies + HDAPS_POLL_PERIOD);
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&hdaps_mtx);
|
mutex_unlock(&hdaps_mtx);
|
||||||
|
@ -536,6 +530,7 @@ static struct dmi_system_id __initdata hdaps_whitelist[] = {
|
||||||
|
|
||||||
static int __init hdaps_init(void)
|
static int __init hdaps_init(void)
|
||||||
{
|
{
|
||||||
|
struct input_dev *idev;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!dmi_check_system(hdaps_whitelist)) {
|
if (!dmi_check_system(hdaps_whitelist)) {
|
||||||
|
@ -563,39 +558,37 @@ static int __init hdaps_init(void)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_device;
|
goto out_device;
|
||||||
|
|
||||||
hdaps_idev = input_allocate_device();
|
hdaps_idev = input_allocate_polled_device();
|
||||||
if (!hdaps_idev) {
|
if (!hdaps_idev) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out_group;
|
goto out_group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hdaps_idev->poll = hdaps_mousedev_poll;
|
||||||
|
hdaps_idev->poll_interval = HDAPS_POLL_INTERVAL;
|
||||||
|
|
||||||
/* initial calibrate for the input device */
|
/* initial calibrate for the input device */
|
||||||
hdaps_calibrate();
|
hdaps_calibrate();
|
||||||
|
|
||||||
/* initialize the input class */
|
/* initialize the input class */
|
||||||
hdaps_idev->name = "hdaps";
|
idev = hdaps_idev->input;
|
||||||
hdaps_idev->dev.parent = &pdev->dev;
|
idev->name = "hdaps";
|
||||||
hdaps_idev->evbit[0] = BIT(EV_ABS);
|
idev->dev.parent = &pdev->dev;
|
||||||
input_set_abs_params(hdaps_idev, ABS_X,
|
idev->evbit[0] = BIT(EV_ABS);
|
||||||
|
input_set_abs_params(idev, ABS_X,
|
||||||
-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
|
-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
|
||||||
input_set_abs_params(hdaps_idev, ABS_Y,
|
input_set_abs_params(idev, ABS_Y,
|
||||||
-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
|
-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
|
||||||
|
|
||||||
ret = input_register_device(hdaps_idev);
|
ret = input_register_polled_device(hdaps_idev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_idev;
|
goto out_idev;
|
||||||
|
|
||||||
/* start up our timer for the input device */
|
|
||||||
init_timer(&hdaps_timer);
|
|
||||||
hdaps_timer.function = hdaps_mousedev_poll;
|
|
||||||
hdaps_timer.expires = jiffies + HDAPS_POLL_PERIOD;
|
|
||||||
add_timer(&hdaps_timer);
|
|
||||||
|
|
||||||
printk(KERN_INFO "hdaps: driver successfully loaded.\n");
|
printk(KERN_INFO "hdaps: driver successfully loaded.\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_idev:
|
out_idev:
|
||||||
input_free_device(hdaps_idev);
|
input_free_polled_device(hdaps_idev);
|
||||||
out_group:
|
out_group:
|
||||||
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
|
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
|
||||||
out_device:
|
out_device:
|
||||||
|
@ -611,8 +604,8 @@ out:
|
||||||
|
|
||||||
static void __exit hdaps_exit(void)
|
static void __exit hdaps_exit(void)
|
||||||
{
|
{
|
||||||
del_timer_sync(&hdaps_timer);
|
input_unregister_polled_device(hdaps_idev);
|
||||||
input_unregister_device(hdaps_idev);
|
input_free_polled_device(hdaps_idev);
|
||||||
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
|
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
|
||||||
platform_device_unregister(pdev);
|
platform_device_unregister(pdev);
|
||||||
platform_driver_unregister(&hdaps_driver);
|
platform_driver_unregister(&hdaps_driver);
|
||||||
|
|
|
@ -114,28 +114,6 @@ config INPUT_JOYDEV
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called joydev.
|
module will be called joydev.
|
||||||
|
|
||||||
config INPUT_TSDEV
|
|
||||||
tristate "Touchscreen interface"
|
|
||||||
---help---
|
|
||||||
Say Y here if you have an application that only can understand the
|
|
||||||
Compaq touchscreen protocol for absolute pointer data. This is
|
|
||||||
useful namely for embedded configurations.
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the
|
|
||||||
module will be called tsdev.
|
|
||||||
|
|
||||||
config INPUT_TSDEV_SCREEN_X
|
|
||||||
int "Horizontal screen resolution"
|
|
||||||
depends on INPUT_TSDEV
|
|
||||||
default "240"
|
|
||||||
|
|
||||||
config INPUT_TSDEV_SCREEN_Y
|
|
||||||
int "Vertical screen resolution"
|
|
||||||
depends on INPUT_TSDEV
|
|
||||||
default "320"
|
|
||||||
|
|
||||||
config INPUT_EVDEV
|
config INPUT_EVDEV
|
||||||
tristate "Event interface"
|
tristate "Event interface"
|
||||||
help
|
help
|
||||||
|
|
|
@ -13,7 +13,6 @@ obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o
|
||||||
obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
|
obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
|
||||||
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
|
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
|
||||||
obj-$(CONFIG_INPUT_EVDEV) += evdev.o
|
obj-$(CONFIG_INPUT_EVDEV) += evdev.o
|
||||||
obj-$(CONFIG_INPUT_TSDEV) += tsdev.o
|
|
||||||
obj-$(CONFIG_INPUT_EVBUG) += evbug.o
|
obj-$(CONFIG_INPUT_EVBUG) += evbug.o
|
||||||
|
|
||||||
obj-$(CONFIG_INPUT_KEYBOARD) += keyboard/
|
obj-$(CONFIG_INPUT_KEYBOARD) += keyboard/
|
||||||
|
|
|
@ -30,6 +30,8 @@ struct evdev {
|
||||||
wait_queue_head_t wait;
|
wait_queue_head_t wait;
|
||||||
struct evdev_client *grab;
|
struct evdev_client *grab;
|
||||||
struct list_head client_list;
|
struct list_head client_list;
|
||||||
|
spinlock_t client_lock; /* protects client_list */
|
||||||
|
struct mutex mutex;
|
||||||
struct device dev;
|
struct device dev;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,39 +39,54 @@ struct evdev_client {
|
||||||
struct input_event buffer[EVDEV_BUFFER_SIZE];
|
struct input_event buffer[EVDEV_BUFFER_SIZE];
|
||||||
int head;
|
int head;
|
||||||
int tail;
|
int tail;
|
||||||
|
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
|
||||||
struct fasync_struct *fasync;
|
struct fasync_struct *fasync;
|
||||||
struct evdev *evdev;
|
struct evdev *evdev;
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct evdev *evdev_table[EVDEV_MINORS];
|
static struct evdev *evdev_table[EVDEV_MINORS];
|
||||||
|
static DEFINE_MUTEX(evdev_table_mutex);
|
||||||
|
|
||||||
static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
|
static void evdev_pass_event(struct evdev_client *client,
|
||||||
|
struct input_event *event)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Interrupts are disabled, just acquire the lock
|
||||||
|
*/
|
||||||
|
spin_lock(&client->buffer_lock);
|
||||||
|
client->buffer[client->head++] = *event;
|
||||||
|
client->head &= EVDEV_BUFFER_SIZE - 1;
|
||||||
|
spin_unlock(&client->buffer_lock);
|
||||||
|
|
||||||
|
kill_fasync(&client->fasync, SIGIO, POLL_IN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pass incoming event to all connected clients.
|
||||||
|
*/
|
||||||
|
static void evdev_event(struct input_handle *handle,
|
||||||
|
unsigned int type, unsigned int code, int value)
|
||||||
{
|
{
|
||||||
struct evdev *evdev = handle->private;
|
struct evdev *evdev = handle->private;
|
||||||
struct evdev_client *client;
|
struct evdev_client *client;
|
||||||
|
struct input_event event;
|
||||||
|
|
||||||
if (evdev->grab) {
|
do_gettimeofday(&event.time);
|
||||||
client = evdev->grab;
|
event.type = type;
|
||||||
|
event.code = code;
|
||||||
|
event.value = value;
|
||||||
|
|
||||||
do_gettimeofday(&client->buffer[client->head].time);
|
rcu_read_lock();
|
||||||
client->buffer[client->head].type = type;
|
|
||||||
client->buffer[client->head].code = code;
|
|
||||||
client->buffer[client->head].value = value;
|
|
||||||
client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
|
|
||||||
|
|
||||||
kill_fasync(&client->fasync, SIGIO, POLL_IN);
|
client = rcu_dereference(evdev->grab);
|
||||||
} else
|
if (client)
|
||||||
list_for_each_entry(client, &evdev->client_list, node) {
|
evdev_pass_event(client, &event);
|
||||||
|
else
|
||||||
|
list_for_each_entry_rcu(client, &evdev->client_list, node)
|
||||||
|
evdev_pass_event(client, &event);
|
||||||
|
|
||||||
do_gettimeofday(&client->buffer[client->head].time);
|
rcu_read_unlock();
|
||||||
client->buffer[client->head].type = type;
|
|
||||||
client->buffer[client->head].code = code;
|
|
||||||
client->buffer[client->head].value = value;
|
|
||||||
client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
|
|
||||||
|
|
||||||
kill_fasync(&client->fasync, SIGIO, POLL_IN);
|
|
||||||
}
|
|
||||||
|
|
||||||
wake_up_interruptible(&evdev->wait);
|
wake_up_interruptible(&evdev->wait);
|
||||||
}
|
}
|
||||||
|
@ -88,38 +105,140 @@ static int evdev_flush(struct file *file, fl_owner_t id)
|
||||||
{
|
{
|
||||||
struct evdev_client *client = file->private_data;
|
struct evdev_client *client = file->private_data;
|
||||||
struct evdev *evdev = client->evdev;
|
struct evdev *evdev = client->evdev;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = mutex_lock_interruptible(&evdev->mutex);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
if (!evdev->exist)
|
if (!evdev->exist)
|
||||||
return -ENODEV;
|
retval = -ENODEV;
|
||||||
|
else
|
||||||
|
retval = input_flush_device(&evdev->handle, file);
|
||||||
|
|
||||||
return input_flush_device(&evdev->handle, file);
|
mutex_unlock(&evdev->mutex);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void evdev_free(struct device *dev)
|
static void evdev_free(struct device *dev)
|
||||||
{
|
{
|
||||||
struct evdev *evdev = container_of(dev, struct evdev, dev);
|
struct evdev *evdev = container_of(dev, struct evdev, dev);
|
||||||
|
|
||||||
evdev_table[evdev->minor] = NULL;
|
|
||||||
kfree(evdev);
|
kfree(evdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Grabs an event device (along with underlying input device).
|
||||||
|
* This function is called with evdev->mutex taken.
|
||||||
|
*/
|
||||||
|
static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (evdev->grab)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
error = input_grab_device(&evdev->handle);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
rcu_assign_pointer(evdev->grab, client);
|
||||||
|
synchronize_rcu();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client)
|
||||||
|
{
|
||||||
|
if (evdev->grab != client)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
rcu_assign_pointer(evdev->grab, NULL);
|
||||||
|
synchronize_rcu();
|
||||||
|
input_release_device(&evdev->handle);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void evdev_attach_client(struct evdev *evdev,
|
||||||
|
struct evdev_client *client)
|
||||||
|
{
|
||||||
|
spin_lock(&evdev->client_lock);
|
||||||
|
list_add_tail_rcu(&client->node, &evdev->client_list);
|
||||||
|
spin_unlock(&evdev->client_lock);
|
||||||
|
synchronize_rcu();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void evdev_detach_client(struct evdev *evdev,
|
||||||
|
struct evdev_client *client)
|
||||||
|
{
|
||||||
|
spin_lock(&evdev->client_lock);
|
||||||
|
list_del_rcu(&client->node);
|
||||||
|
spin_unlock(&evdev->client_lock);
|
||||||
|
synchronize_rcu();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int evdev_open_device(struct evdev *evdev)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = mutex_lock_interruptible(&evdev->mutex);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
if (!evdev->exist)
|
||||||
|
retval = -ENODEV;
|
||||||
|
else if (!evdev->open++) {
|
||||||
|
retval = input_open_device(&evdev->handle);
|
||||||
|
if (retval)
|
||||||
|
evdev->open--;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&evdev->mutex);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void evdev_close_device(struct evdev *evdev)
|
||||||
|
{
|
||||||
|
mutex_lock(&evdev->mutex);
|
||||||
|
|
||||||
|
if (evdev->exist && !--evdev->open)
|
||||||
|
input_close_device(&evdev->handle);
|
||||||
|
|
||||||
|
mutex_unlock(&evdev->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wake up users waiting for IO so they can disconnect from
|
||||||
|
* dead device.
|
||||||
|
*/
|
||||||
|
static void evdev_hangup(struct evdev *evdev)
|
||||||
|
{
|
||||||
|
struct evdev_client *client;
|
||||||
|
|
||||||
|
spin_lock(&evdev->client_lock);
|
||||||
|
list_for_each_entry(client, &evdev->client_list, node)
|
||||||
|
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
|
||||||
|
spin_unlock(&evdev->client_lock);
|
||||||
|
|
||||||
|
wake_up_interruptible(&evdev->wait);
|
||||||
|
}
|
||||||
|
|
||||||
static int evdev_release(struct inode *inode, struct file *file)
|
static int evdev_release(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
struct evdev_client *client = file->private_data;
|
struct evdev_client *client = file->private_data;
|
||||||
struct evdev *evdev = client->evdev;
|
struct evdev *evdev = client->evdev;
|
||||||
|
|
||||||
if (evdev->grab == client) {
|
mutex_lock(&evdev->mutex);
|
||||||
input_release_device(&evdev->handle);
|
if (evdev->grab == client)
|
||||||
evdev->grab = NULL;
|
evdev_ungrab(evdev, client);
|
||||||
}
|
mutex_unlock(&evdev->mutex);
|
||||||
|
|
||||||
evdev_fasync(-1, file, 0);
|
evdev_fasync(-1, file, 0);
|
||||||
list_del(&client->node);
|
evdev_detach_client(evdev, client);
|
||||||
kfree(client);
|
kfree(client);
|
||||||
|
|
||||||
if (!--evdev->open && evdev->exist)
|
evdev_close_device(evdev);
|
||||||
input_close_device(&evdev->handle);
|
|
||||||
|
|
||||||
put_device(&evdev->dev);
|
put_device(&evdev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -127,20 +246,24 @@ static int evdev_release(struct inode *inode, struct file *file)
|
||||||
|
|
||||||
static int evdev_open(struct inode *inode, struct file *file)
|
static int evdev_open(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
struct evdev_client *client;
|
|
||||||
struct evdev *evdev;
|
struct evdev *evdev;
|
||||||
|
struct evdev_client *client;
|
||||||
int i = iminor(inode) - EVDEV_MINOR_BASE;
|
int i = iminor(inode) - EVDEV_MINOR_BASE;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (i >= EVDEV_MINORS)
|
if (i >= EVDEV_MINORS)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
error = mutex_lock_interruptible(&evdev_table_mutex);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
evdev = evdev_table[i];
|
evdev = evdev_table[i];
|
||||||
|
if (evdev)
|
||||||
if (!evdev || !evdev->exist)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
get_device(&evdev->dev);
|
get_device(&evdev->dev);
|
||||||
|
mutex_unlock(&evdev_table_mutex);
|
||||||
|
|
||||||
|
if (!evdev)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
|
client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
|
@ -148,20 +271,19 @@ static int evdev_open(struct inode *inode, struct file *file)
|
||||||
goto err_put_evdev;
|
goto err_put_evdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_lock_init(&client->buffer_lock);
|
||||||
client->evdev = evdev;
|
client->evdev = evdev;
|
||||||
list_add_tail(&client->node, &evdev->client_list);
|
evdev_attach_client(evdev, client);
|
||||||
|
|
||||||
if (!evdev->open++ && evdev->exist) {
|
error = evdev_open_device(evdev);
|
||||||
error = input_open_device(&evdev->handle);
|
|
||||||
if (error)
|
if (error)
|
||||||
goto err_free_client;
|
goto err_free_client;
|
||||||
}
|
|
||||||
|
|
||||||
file->private_data = client;
|
file->private_data = client;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_free_client:
|
err_free_client:
|
||||||
list_del(&client->node);
|
evdev_detach_client(evdev, client);
|
||||||
kfree(client);
|
kfree(client);
|
||||||
err_put_evdev:
|
err_put_evdev:
|
||||||
put_device(&evdev->dev);
|
put_device(&evdev->dev);
|
||||||
|
@ -197,12 +319,14 @@ static inline size_t evdev_event_size(void)
|
||||||
sizeof(struct input_event_compat) : sizeof(struct input_event);
|
sizeof(struct input_event_compat) : sizeof(struct input_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int evdev_event_from_user(const char __user *buffer, struct input_event *event)
|
static int evdev_event_from_user(const char __user *buffer,
|
||||||
|
struct input_event *event)
|
||||||
{
|
{
|
||||||
if (COMPAT_TEST) {
|
if (COMPAT_TEST) {
|
||||||
struct input_event_compat compat_event;
|
struct input_event_compat compat_event;
|
||||||
|
|
||||||
if (copy_from_user(&compat_event, buffer, sizeof(struct input_event_compat)))
|
if (copy_from_user(&compat_event, buffer,
|
||||||
|
sizeof(struct input_event_compat)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
event->time.tv_sec = compat_event.time.tv_sec;
|
event->time.tv_sec = compat_event.time.tv_sec;
|
||||||
|
@ -219,7 +343,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event *
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
|
static int evdev_event_to_user(char __user *buffer,
|
||||||
|
const struct input_event *event)
|
||||||
{
|
{
|
||||||
if (COMPAT_TEST) {
|
if (COMPAT_TEST) {
|
||||||
struct input_event_compat compat_event;
|
struct input_event_compat compat_event;
|
||||||
|
@ -230,7 +355,8 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev
|
||||||
compat_event.code = event->code;
|
compat_event.code = event->code;
|
||||||
compat_event.value = event->value;
|
compat_event.value = event->value;
|
||||||
|
|
||||||
if (copy_to_user(buffer, &compat_event, sizeof(struct input_event_compat)))
|
if (copy_to_user(buffer, &compat_event,
|
||||||
|
sizeof(struct input_event_compat)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -248,7 +374,8 @@ static inline size_t evdev_event_size(void)
|
||||||
return sizeof(struct input_event);
|
return sizeof(struct input_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int evdev_event_from_user(const char __user *buffer, struct input_event *event)
|
static int evdev_event_from_user(const char __user *buffer,
|
||||||
|
struct input_event *event)
|
||||||
{
|
{
|
||||||
if (copy_from_user(event, buffer, sizeof(struct input_event)))
|
if (copy_from_user(event, buffer, sizeof(struct input_event)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
@ -256,7 +383,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event *
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
|
static int evdev_event_to_user(char __user *buffer,
|
||||||
|
const struct input_event *event)
|
||||||
{
|
{
|
||||||
if (copy_to_user(buffer, event, sizeof(struct input_event)))
|
if (copy_to_user(buffer, event, sizeof(struct input_event)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
@ -266,37 +394,71 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev
|
||||||
|
|
||||||
#endif /* CONFIG_COMPAT */
|
#endif /* CONFIG_COMPAT */
|
||||||
|
|
||||||
static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
|
static ssize_t evdev_write(struct file *file, const char __user *buffer,
|
||||||
|
size_t count, loff_t *ppos)
|
||||||
{
|
{
|
||||||
struct evdev_client *client = file->private_data;
|
struct evdev_client *client = file->private_data;
|
||||||
struct evdev *evdev = client->evdev;
|
struct evdev *evdev = client->evdev;
|
||||||
struct input_event event;
|
struct input_event event;
|
||||||
int retval = 0;
|
int retval;
|
||||||
|
|
||||||
if (!evdev->exist)
|
retval = mutex_lock_interruptible(&evdev->mutex);
|
||||||
return -ENODEV;
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
if (!evdev->exist) {
|
||||||
|
retval = -ENODEV;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
while (retval < count) {
|
while (retval < count) {
|
||||||
|
|
||||||
if (evdev_event_from_user(buffer + retval, &event))
|
if (evdev_event_from_user(buffer + retval, &event)) {
|
||||||
return -EFAULT;
|
retval = -EFAULT;
|
||||||
input_inject_event(&evdev->handle, event.type, event.code, event.value);
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_inject_event(&evdev->handle,
|
||||||
|
event.type, event.code, event.value);
|
||||||
retval += evdev_event_size();
|
retval += evdev_event_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&evdev->mutex);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
|
static int evdev_fetch_next_event(struct evdev_client *client,
|
||||||
|
struct input_event *event)
|
||||||
|
{
|
||||||
|
int have_event;
|
||||||
|
|
||||||
|
spin_lock_irq(&client->buffer_lock);
|
||||||
|
|
||||||
|
have_event = client->head != client->tail;
|
||||||
|
if (have_event) {
|
||||||
|
*event = client->buffer[client->tail++];
|
||||||
|
client->tail &= EVDEV_BUFFER_SIZE - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irq(&client->buffer_lock);
|
||||||
|
|
||||||
|
return have_event;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t evdev_read(struct file *file, char __user *buffer,
|
||||||
|
size_t count, loff_t *ppos)
|
||||||
{
|
{
|
||||||
struct evdev_client *client = file->private_data;
|
struct evdev_client *client = file->private_data;
|
||||||
struct evdev *evdev = client->evdev;
|
struct evdev *evdev = client->evdev;
|
||||||
|
struct input_event event;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
if (count < evdev_event_size())
|
if (count < evdev_event_size())
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
|
if (client->head == client->tail && evdev->exist &&
|
||||||
|
(file->f_flags & O_NONBLOCK))
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
retval = wait_event_interruptible(evdev->wait,
|
retval = wait_event_interruptible(evdev->wait,
|
||||||
|
@ -307,14 +469,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count,
|
||||||
if (!evdev->exist)
|
if (!evdev->exist)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
while (client->head != client->tail && retval + evdev_event_size() <= count) {
|
while (retval + evdev_event_size() <= count &&
|
||||||
|
evdev_fetch_next_event(client, &event)) {
|
||||||
|
|
||||||
struct input_event *event = (struct input_event *) client->buffer + client->tail;
|
if (evdev_event_to_user(buffer + retval, &event))
|
||||||
|
|
||||||
if (evdev_event_to_user(buffer + retval, event))
|
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
|
|
||||||
retval += evdev_event_size();
|
retval += evdev_event_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,7 +569,7 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
|
||||||
return copy_to_user(p, str, len) ? -EFAULT : len;
|
return copy_to_user(p, str, len) ? -EFAULT : len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
|
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
||||||
void __user *p, int compat_mode)
|
void __user *p, int compat_mode)
|
||||||
{
|
{
|
||||||
struct evdev_client *client = file->private_data;
|
struct evdev_client *client = file->private_data;
|
||||||
|
@ -421,9 +581,6 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
|
||||||
int i, t, u, v;
|
int i, t, u, v;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (!evdev->exist)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
|
|
||||||
case EVIOCGVERSION:
|
case EVIOCGVERSION:
|
||||||
|
@ -490,26 +647,17 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
|
||||||
return input_ff_erase(dev, (int)(unsigned long) p, file);
|
return input_ff_erase(dev, (int)(unsigned long) p, file);
|
||||||
|
|
||||||
case EVIOCGEFFECTS:
|
case EVIOCGEFFECTS:
|
||||||
i = test_bit(EV_FF, dev->evbit) ? dev->ff->max_effects : 0;
|
i = test_bit(EV_FF, dev->evbit) ?
|
||||||
|
dev->ff->max_effects : 0;
|
||||||
if (put_user(i, ip))
|
if (put_user(i, ip))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case EVIOCGRAB:
|
case EVIOCGRAB:
|
||||||
if (p) {
|
if (p)
|
||||||
if (evdev->grab)
|
return evdev_grab(evdev, client);
|
||||||
return -EBUSY;
|
else
|
||||||
if (input_grab_device(&evdev->handle))
|
return evdev_ungrab(evdev, client);
|
||||||
return -EBUSY;
|
|
||||||
evdev->grab = client;
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
if (evdev->grab != client)
|
|
||||||
return -EINVAL;
|
|
||||||
input_release_device(&evdev->handle);
|
|
||||||
evdev->grab = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
|
@ -518,12 +666,13 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
|
||||||
|
|
||||||
if (_IOC_DIR(cmd) == _IOC_READ) {
|
if (_IOC_DIR(cmd) == _IOC_READ) {
|
||||||
|
|
||||||
if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
|
if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0))) {
|
||||||
|
|
||||||
unsigned long *bits;
|
unsigned long *bits;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
switch (_IOC_NR(cmd) & EV_MAX) {
|
switch (_IOC_NR(cmd) & EV_MAX) {
|
||||||
|
|
||||||
case 0: bits = dev->evbit; len = EV_MAX; break;
|
case 0: bits = dev->evbit; len = EV_MAX; break;
|
||||||
case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
|
case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
|
||||||
case EV_REL: bits = dev->relbit; len = REL_MAX; break;
|
case EV_REL: bits = dev->relbit; len = REL_MAX; break;
|
||||||
|
@ -587,15 +736,25 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
|
||||||
|
|
||||||
t = _IOC_NR(cmd) & ABS_MAX;
|
t = _IOC_NR(cmd) & ABS_MAX;
|
||||||
|
|
||||||
if (copy_from_user(&abs, p, sizeof(struct input_absinfo)))
|
if (copy_from_user(&abs, p,
|
||||||
|
sizeof(struct input_absinfo)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take event lock to ensure that we are not
|
||||||
|
* changing device parameters in the middle
|
||||||
|
* of event.
|
||||||
|
*/
|
||||||
|
spin_lock_irq(&dev->event_lock);
|
||||||
|
|
||||||
dev->abs[t] = abs.value;
|
dev->abs[t] = abs.value;
|
||||||
dev->absmin[t] = abs.minimum;
|
dev->absmin[t] = abs.minimum;
|
||||||
dev->absmax[t] = abs.maximum;
|
dev->absmax[t] = abs.maximum;
|
||||||
dev->absfuzz[t] = abs.fuzz;
|
dev->absfuzz[t] = abs.fuzz;
|
||||||
dev->absflat[t] = abs.flat;
|
dev->absflat[t] = abs.flat;
|
||||||
|
|
||||||
|
spin_unlock_irq(&dev->event_lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -603,13 +762,37 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
|
||||||
|
void __user *p, int compat_mode)
|
||||||
|
{
|
||||||
|
struct evdev_client *client = file->private_data;
|
||||||
|
struct evdev *evdev = client->evdev;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = mutex_lock_interruptible(&evdev->mutex);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
if (!evdev->exist) {
|
||||||
|
retval = -ENODEV;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = evdev_do_ioctl(file, cmd, p, compat_mode);
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&evdev->mutex);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
|
return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
|
static long evdev_ioctl_compat(struct file *file,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
|
return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
|
||||||
}
|
}
|
||||||
|
@ -630,6 +813,57 @@ static const struct file_operations evdev_fops = {
|
||||||
.flush = evdev_flush
|
.flush = evdev_flush
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int evdev_install_chrdev(struct evdev *evdev)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* No need to do any locking here as calls to connect and
|
||||||
|
* disconnect are serialized by the input core
|
||||||
|
*/
|
||||||
|
evdev_table[evdev->minor] = evdev;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void evdev_remove_chrdev(struct evdev *evdev)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Lock evdev table to prevent race with evdev_open()
|
||||||
|
*/
|
||||||
|
mutex_lock(&evdev_table_mutex);
|
||||||
|
evdev_table[evdev->minor] = NULL;
|
||||||
|
mutex_unlock(&evdev_table_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark device non-existent. This disables writes, ioctls and
|
||||||
|
* prevents new users from opening the device. Already posted
|
||||||
|
* blocking reads will stay, however new ones will fail.
|
||||||
|
*/
|
||||||
|
static void evdev_mark_dead(struct evdev *evdev)
|
||||||
|
{
|
||||||
|
mutex_lock(&evdev->mutex);
|
||||||
|
evdev->exist = 0;
|
||||||
|
mutex_unlock(&evdev->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void evdev_cleanup(struct evdev *evdev)
|
||||||
|
{
|
||||||
|
struct input_handle *handle = &evdev->handle;
|
||||||
|
|
||||||
|
evdev_mark_dead(evdev);
|
||||||
|
evdev_hangup(evdev);
|
||||||
|
evdev_remove_chrdev(evdev);
|
||||||
|
|
||||||
|
/* evdev is marked dead so no one else accesses evdev->open */
|
||||||
|
if (evdev->open) {
|
||||||
|
input_flush_device(handle, NULL);
|
||||||
|
input_close_device(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create new evdev device. Note that input core serializes calls
|
||||||
|
* to connect and disconnect so we don't need to lock evdev_table here.
|
||||||
|
*/
|
||||||
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
|
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
|
||||||
const struct input_device_id *id)
|
const struct input_device_id *id)
|
||||||
{
|
{
|
||||||
|
@ -637,7 +871,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
|
||||||
int minor;
|
int minor;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
|
for (minor = 0; minor < EVDEV_MINORS; minor++)
|
||||||
|
if (!evdev_table[minor])
|
||||||
|
break;
|
||||||
|
|
||||||
if (minor == EVDEV_MINORS) {
|
if (minor == EVDEV_MINORS) {
|
||||||
printk(KERN_ERR "evdev: no more free evdev devices\n");
|
printk(KERN_ERR "evdev: no more free evdev devices\n");
|
||||||
return -ENFILE;
|
return -ENFILE;
|
||||||
|
@ -648,38 +885,44 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&evdev->client_list);
|
INIT_LIST_HEAD(&evdev->client_list);
|
||||||
|
spin_lock_init(&evdev->client_lock);
|
||||||
|
mutex_init(&evdev->mutex);
|
||||||
init_waitqueue_head(&evdev->wait);
|
init_waitqueue_head(&evdev->wait);
|
||||||
|
|
||||||
|
snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
|
||||||
evdev->exist = 1;
|
evdev->exist = 1;
|
||||||
evdev->minor = minor;
|
evdev->minor = minor;
|
||||||
|
|
||||||
evdev->handle.dev = dev;
|
evdev->handle.dev = dev;
|
||||||
evdev->handle.name = evdev->name;
|
evdev->handle.name = evdev->name;
|
||||||
evdev->handle.handler = handler;
|
evdev->handle.handler = handler;
|
||||||
evdev->handle.private = evdev;
|
evdev->handle.private = evdev;
|
||||||
snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
|
|
||||||
|
|
||||||
snprintf(evdev->dev.bus_id, sizeof(evdev->dev.bus_id),
|
strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id));
|
||||||
"event%d", minor);
|
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
|
||||||
evdev->dev.class = &input_class;
|
evdev->dev.class = &input_class;
|
||||||
evdev->dev.parent = &dev->dev;
|
evdev->dev.parent = &dev->dev;
|
||||||
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
|
|
||||||
evdev->dev.release = evdev_free;
|
evdev->dev.release = evdev_free;
|
||||||
device_initialize(&evdev->dev);
|
device_initialize(&evdev->dev);
|
||||||
|
|
||||||
evdev_table[minor] = evdev;
|
error = input_register_handle(&evdev->handle);
|
||||||
|
|
||||||
error = device_add(&evdev->dev);
|
|
||||||
if (error)
|
if (error)
|
||||||
goto err_free_evdev;
|
goto err_free_evdev;
|
||||||
|
|
||||||
error = input_register_handle(&evdev->handle);
|
error = evdev_install_chrdev(evdev);
|
||||||
if (error)
|
if (error)
|
||||||
goto err_delete_evdev;
|
goto err_unregister_handle;
|
||||||
|
|
||||||
|
error = device_add(&evdev->dev);
|
||||||
|
if (error)
|
||||||
|
goto err_cleanup_evdev;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_delete_evdev:
|
err_cleanup_evdev:
|
||||||
device_del(&evdev->dev);
|
evdev_cleanup(evdev);
|
||||||
|
err_unregister_handle:
|
||||||
|
input_unregister_handle(&evdev->handle);
|
||||||
err_free_evdev:
|
err_free_evdev:
|
||||||
put_device(&evdev->dev);
|
put_device(&evdev->dev);
|
||||||
return error;
|
return error;
|
||||||
|
@ -688,21 +931,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
|
||||||
static void evdev_disconnect(struct input_handle *handle)
|
static void evdev_disconnect(struct input_handle *handle)
|
||||||
{
|
{
|
||||||
struct evdev *evdev = handle->private;
|
struct evdev *evdev = handle->private;
|
||||||
struct evdev_client *client;
|
|
||||||
|
|
||||||
input_unregister_handle(handle);
|
|
||||||
device_del(&evdev->dev);
|
device_del(&evdev->dev);
|
||||||
|
evdev_cleanup(evdev);
|
||||||
evdev->exist = 0;
|
input_unregister_handle(handle);
|
||||||
|
|
||||||
if (evdev->open) {
|
|
||||||
input_flush_device(handle, NULL);
|
|
||||||
input_close_device(handle);
|
|
||||||
list_for_each_entry(client, &evdev->client_list, node)
|
|
||||||
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
|
|
||||||
wake_up_interruptible(&evdev->wait);
|
|
||||||
}
|
|
||||||
|
|
||||||
put_device(&evdev->dev);
|
put_device(&evdev->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,7 @@ static int input_open_polled_device(struct input_dev *input)
|
||||||
{
|
{
|
||||||
struct input_polled_dev *dev = input->private;
|
struct input_polled_dev *dev = input->private;
|
||||||
int error;
|
int error;
|
||||||
|
unsigned long ticks;
|
||||||
|
|
||||||
error = input_polldev_start_workqueue();
|
error = input_polldev_start_workqueue();
|
||||||
if (error)
|
if (error)
|
||||||
|
@ -78,8 +79,10 @@ static int input_open_polled_device(struct input_dev *input)
|
||||||
if (dev->flush)
|
if (dev->flush)
|
||||||
dev->flush(dev);
|
dev->flush(dev);
|
||||||
|
|
||||||
queue_delayed_work(polldev_wq, &dev->work,
|
ticks = msecs_to_jiffies(dev->poll_interval);
|
||||||
msecs_to_jiffies(dev->poll_interval));
|
if (ticks >= HZ)
|
||||||
|
ticks = round_jiffies(ticks);
|
||||||
|
queue_delayed_work(polldev_wq, &dev->work, ticks);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,10 @@
|
||||||
#include <linux/major.h>
|
#include <linux/major.h>
|
||||||
#include <linux/proc_fs.h>
|
#include <linux/proc_fs.h>
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/poll.h>
|
#include <linux/poll.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/rcupdate.h>
|
||||||
|
|
||||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
|
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
|
||||||
MODULE_DESCRIPTION("Input core");
|
MODULE_DESCRIPTION("Input core");
|
||||||
|
@ -31,8 +31,222 @@ MODULE_LICENSE("GPL");
|
||||||
static LIST_HEAD(input_dev_list);
|
static LIST_HEAD(input_dev_list);
|
||||||
static LIST_HEAD(input_handler_list);
|
static LIST_HEAD(input_handler_list);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* input_mutex protects access to both input_dev_list and input_handler_list.
|
||||||
|
* This also causes input_[un]register_device and input_[un]register_handler
|
||||||
|
* be mutually exclusive which simplifies locking in drivers implementing
|
||||||
|
* input handlers.
|
||||||
|
*/
|
||||||
|
static DEFINE_MUTEX(input_mutex);
|
||||||
|
|
||||||
static struct input_handler *input_table[8];
|
static struct input_handler *input_table[8];
|
||||||
|
|
||||||
|
static inline int is_event_supported(unsigned int code,
|
||||||
|
unsigned long *bm, unsigned int max)
|
||||||
|
{
|
||||||
|
return code <= max && test_bit(code, bm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int input_defuzz_abs_event(int value, int old_val, int fuzz)
|
||||||
|
{
|
||||||
|
if (fuzz) {
|
||||||
|
if (value > old_val - fuzz / 2 && value < old_val + fuzz / 2)
|
||||||
|
return old_val;
|
||||||
|
|
||||||
|
if (value > old_val - fuzz && value < old_val + fuzz)
|
||||||
|
return (old_val * 3 + value) / 4;
|
||||||
|
|
||||||
|
if (value > old_val - fuzz * 2 && value < old_val + fuzz * 2)
|
||||||
|
return (old_val + value) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pass event through all open handles. This function is called with
|
||||||
|
* dev->event_lock held and interrupts disabled.
|
||||||
|
*/
|
||||||
|
static void input_pass_event(struct input_dev *dev,
|
||||||
|
unsigned int type, unsigned int code, int value)
|
||||||
|
{
|
||||||
|
struct input_handle *handle;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
handle = rcu_dereference(dev->grab);
|
||||||
|
if (handle)
|
||||||
|
handle->handler->event(handle, type, code, value);
|
||||||
|
else
|
||||||
|
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
|
||||||
|
if (handle->open)
|
||||||
|
handle->handler->event(handle,
|
||||||
|
type, code, value);
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate software autorepeat event. Note that we take
|
||||||
|
* dev->event_lock here to avoid racing with input_event
|
||||||
|
* which may cause keys get "stuck".
|
||||||
|
*/
|
||||||
|
static void input_repeat_key(unsigned long data)
|
||||||
|
{
|
||||||
|
struct input_dev *dev = (void *) data;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dev->event_lock, flags);
|
||||||
|
|
||||||
|
if (test_bit(dev->repeat_key, dev->key) &&
|
||||||
|
is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
|
||||||
|
|
||||||
|
input_pass_event(dev, EV_KEY, dev->repeat_key, 2);
|
||||||
|
|
||||||
|
if (dev->sync) {
|
||||||
|
/*
|
||||||
|
* Only send SYN_REPORT if we are not in a middle
|
||||||
|
* of driver parsing a new hardware packet.
|
||||||
|
* Otherwise assume that the driver will send
|
||||||
|
* SYN_REPORT once it's done.
|
||||||
|
*/
|
||||||
|
input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dev->rep[REP_PERIOD])
|
||||||
|
mod_timer(&dev->timer, jiffies +
|
||||||
|
msecs_to_jiffies(dev->rep[REP_PERIOD]));
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void input_start_autorepeat(struct input_dev *dev, int code)
|
||||||
|
{
|
||||||
|
if (test_bit(EV_REP, dev->evbit) &&
|
||||||
|
dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
|
||||||
|
dev->timer.data) {
|
||||||
|
dev->repeat_key = code;
|
||||||
|
mod_timer(&dev->timer,
|
||||||
|
jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INPUT_IGNORE_EVENT 0
|
||||||
|
#define INPUT_PASS_TO_HANDLERS 1
|
||||||
|
#define INPUT_PASS_TO_DEVICE 2
|
||||||
|
#define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
|
||||||
|
|
||||||
|
static void input_handle_event(struct input_dev *dev,
|
||||||
|
unsigned int type, unsigned int code, int value)
|
||||||
|
{
|
||||||
|
int disposition = INPUT_IGNORE_EVENT;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
|
||||||
|
case EV_SYN:
|
||||||
|
switch (code) {
|
||||||
|
case SYN_CONFIG:
|
||||||
|
disposition = INPUT_PASS_TO_ALL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SYN_REPORT:
|
||||||
|
if (!dev->sync) {
|
||||||
|
dev->sync = 1;
|
||||||
|
disposition = INPUT_PASS_TO_HANDLERS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EV_KEY:
|
||||||
|
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
|
||||||
|
!!test_bit(code, dev->key) != value) {
|
||||||
|
|
||||||
|
if (value != 2) {
|
||||||
|
__change_bit(code, dev->key);
|
||||||
|
if (value)
|
||||||
|
input_start_autorepeat(dev, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
disposition = INPUT_PASS_TO_HANDLERS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EV_SW:
|
||||||
|
if (is_event_supported(code, dev->swbit, SW_MAX) &&
|
||||||
|
!!test_bit(code, dev->sw) != value) {
|
||||||
|
|
||||||
|
__change_bit(code, dev->sw);
|
||||||
|
disposition = INPUT_PASS_TO_HANDLERS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EV_ABS:
|
||||||
|
if (is_event_supported(code, dev->absbit, ABS_MAX)) {
|
||||||
|
|
||||||
|
value = input_defuzz_abs_event(value,
|
||||||
|
dev->abs[code], dev->absfuzz[code]);
|
||||||
|
|
||||||
|
if (dev->abs[code] != value) {
|
||||||
|
dev->abs[code] = value;
|
||||||
|
disposition = INPUT_PASS_TO_HANDLERS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EV_REL:
|
||||||
|
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
|
||||||
|
disposition = INPUT_PASS_TO_HANDLERS;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EV_MSC:
|
||||||
|
if (is_event_supported(code, dev->mscbit, MSC_MAX))
|
||||||
|
disposition = INPUT_PASS_TO_ALL;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EV_LED:
|
||||||
|
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
|
||||||
|
!!test_bit(code, dev->led) != value) {
|
||||||
|
|
||||||
|
__change_bit(code, dev->led);
|
||||||
|
disposition = INPUT_PASS_TO_ALL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EV_SND:
|
||||||
|
if (is_event_supported(code, dev->sndbit, SND_MAX)) {
|
||||||
|
|
||||||
|
if (!!test_bit(code, dev->snd) != !!value)
|
||||||
|
__change_bit(code, dev->snd);
|
||||||
|
disposition = INPUT_PASS_TO_ALL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EV_REP:
|
||||||
|
if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
|
||||||
|
dev->rep[code] = value;
|
||||||
|
disposition = INPUT_PASS_TO_ALL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EV_FF:
|
||||||
|
if (value >= 0)
|
||||||
|
disposition = INPUT_PASS_TO_ALL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type != EV_SYN)
|
||||||
|
dev->sync = 0;
|
||||||
|
|
||||||
|
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
|
||||||
|
dev->event(dev, type, code, value);
|
||||||
|
|
||||||
|
if (disposition & INPUT_PASS_TO_HANDLERS)
|
||||||
|
input_pass_event(dev, type, code, value);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* input_event() - report new input event
|
* input_event() - report new input event
|
||||||
* @dev: device that generated the event
|
* @dev: device that generated the event
|
||||||
|
@ -40,158 +254,22 @@ static struct input_handler *input_table[8];
|
||||||
* @code: event code
|
* @code: event code
|
||||||
* @value: value of the event
|
* @value: value of the event
|
||||||
*
|
*
|
||||||
* This function should be used by drivers implementing various input devices
|
* This function should be used by drivers implementing various input
|
||||||
* See also input_inject_event()
|
* devices. See also input_inject_event().
|
||||||
*/
|
*/
|
||||||
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
|
|
||||||
|
void input_event(struct input_dev *dev,
|
||||||
|
unsigned int type, unsigned int code, int value)
|
||||||
{
|
{
|
||||||
struct input_handle *handle;
|
unsigned long flags;
|
||||||
|
|
||||||
if (type > EV_MAX || !test_bit(type, dev->evbit))
|
if (is_event_supported(type, dev->evbit, EV_MAX)) {
|
||||||
return;
|
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dev->event_lock, flags);
|
||||||
add_input_randomness(type, code, value);
|
add_input_randomness(type, code, value);
|
||||||
|
input_handle_event(dev, type, code, value);
|
||||||
switch (type) {
|
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||||
|
|
||||||
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->rep[REP_DELAY] && dev->timer.data && value) {
|
|
||||||
dev->repeat_key = code;
|
|
||||||
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EV_SW:
|
|
||||||
|
|
||||||
if (code > SW_MAX || !test_bit(code, dev->swbit) || !!test_bit(code, dev->sw) == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
change_bit(code, dev->sw);
|
|
||||||
|
|
||||||
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 (!!test_bit(code, dev->snd) != !!value)
|
|
||||||
change_bit(code, dev->snd);
|
|
||||||
|
|
||||||
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 (value < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_event);
|
EXPORT_SYMBOL(input_event);
|
||||||
|
|
||||||
|
@ -202,102 +280,228 @@ EXPORT_SYMBOL(input_event);
|
||||||
* @code: event code
|
* @code: event code
|
||||||
* @value: value of the event
|
* @value: value of the event
|
||||||
*
|
*
|
||||||
* Similar to input_event() but will ignore event if device is "grabbed" and handle
|
* Similar to input_event() but will ignore event if device is
|
||||||
* injecting event is not the one that owns the device.
|
* "grabbed" and handle injecting event is not the one that owns
|
||||||
|
* the device.
|
||||||
*/
|
*/
|
||||||
void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
|
void input_inject_event(struct input_handle *handle,
|
||||||
|
unsigned int type, unsigned int code, int value)
|
||||||
{
|
{
|
||||||
if (!handle->dev->grab || handle->dev->grab == handle)
|
struct input_dev *dev = handle->dev;
|
||||||
input_event(handle->dev, type, code, value);
|
struct input_handle *grab;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (is_event_supported(type, dev->evbit, EV_MAX)) {
|
||||||
|
spin_lock_irqsave(&dev->event_lock, flags);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
grab = rcu_dereference(dev->grab);
|
||||||
|
if (!grab || grab == handle)
|
||||||
|
input_handle_event(dev, type, code, value);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_inject_event);
|
EXPORT_SYMBOL(input_inject_event);
|
||||||
|
|
||||||
static void input_repeat_key(unsigned long data)
|
/**
|
||||||
{
|
* input_grab_device - grabs device for exclusive use
|
||||||
struct input_dev *dev = (void *) data;
|
* @handle: input handle that wants to own the device
|
||||||
|
*
|
||||||
if (!test_bit(dev->repeat_key, dev->key))
|
* When a device is grabbed by an input handle all events generated by
|
||||||
return;
|
* the device are delivered only to this handle. Also events injected
|
||||||
|
* by other input handles are ignored while device is grabbed.
|
||||||
input_event(dev, EV_KEY, dev->repeat_key, 2);
|
*/
|
||||||
input_sync(dev);
|
|
||||||
|
|
||||||
if (dev->rep[REP_PERIOD])
|
|
||||||
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD]));
|
|
||||||
}
|
|
||||||
|
|
||||||
int input_grab_device(struct input_handle *handle)
|
int input_grab_device(struct input_handle *handle)
|
||||||
{
|
{
|
||||||
if (handle->dev->grab)
|
struct input_dev *dev = handle->dev;
|
||||||
return -EBUSY;
|
int retval;
|
||||||
|
|
||||||
handle->dev->grab = handle;
|
retval = mutex_lock_interruptible(&dev->mutex);
|
||||||
return 0;
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
if (dev->grab) {
|
||||||
|
retval = -EBUSY;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
rcu_assign_pointer(dev->grab, handle);
|
||||||
|
synchronize_rcu();
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&dev->mutex);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_grab_device);
|
EXPORT_SYMBOL(input_grab_device);
|
||||||
|
|
||||||
void input_release_device(struct input_handle *handle)
|
static void __input_release_device(struct input_handle *handle)
|
||||||
{
|
{
|
||||||
struct input_dev *dev = handle->dev;
|
struct input_dev *dev = handle->dev;
|
||||||
|
|
||||||
if (dev->grab == handle) {
|
if (dev->grab == handle) {
|
||||||
dev->grab = NULL;
|
rcu_assign_pointer(dev->grab, NULL);
|
||||||
|
/* Make sure input_pass_event() notices that grab is gone */
|
||||||
|
synchronize_rcu();
|
||||||
|
|
||||||
list_for_each_entry(handle, &dev->h_list, d_node)
|
list_for_each_entry(handle, &dev->h_list, d_node)
|
||||||
if (handle->handler->start)
|
if (handle->open && handle->handler->start)
|
||||||
handle->handler->start(handle);
|
handle->handler->start(handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* input_release_device - release previously grabbed device
|
||||||
|
* @handle: input handle that owns the device
|
||||||
|
*
|
||||||
|
* Releases previously grabbed device so that other input handles can
|
||||||
|
* start receiving input events. Upon release all handlers attached
|
||||||
|
* to the device have their start() method called so they have a change
|
||||||
|
* to synchronize device state with the rest of the system.
|
||||||
|
*/
|
||||||
|
void input_release_device(struct input_handle *handle)
|
||||||
|
{
|
||||||
|
struct input_dev *dev = handle->dev;
|
||||||
|
|
||||||
|
mutex_lock(&dev->mutex);
|
||||||
|
__input_release_device(handle);
|
||||||
|
mutex_unlock(&dev->mutex);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(input_release_device);
|
EXPORT_SYMBOL(input_release_device);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* input_open_device - open input device
|
||||||
|
* @handle: handle through which device is being accessed
|
||||||
|
*
|
||||||
|
* This function should be called by input handlers when they
|
||||||
|
* want to start receive events from given input device.
|
||||||
|
*/
|
||||||
int input_open_device(struct input_handle *handle)
|
int input_open_device(struct input_handle *handle)
|
||||||
{
|
{
|
||||||
struct input_dev *dev = handle->dev;
|
struct input_dev *dev = handle->dev;
|
||||||
int err;
|
int retval;
|
||||||
|
|
||||||
err = mutex_lock_interruptible(&dev->mutex);
|
retval = mutex_lock_interruptible(&dev->mutex);
|
||||||
if (err)
|
if (retval)
|
||||||
return err;
|
return retval;
|
||||||
|
|
||||||
|
if (dev->going_away) {
|
||||||
|
retval = -ENODEV;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
handle->open++;
|
handle->open++;
|
||||||
|
|
||||||
if (!dev->users++ && dev->open)
|
if (!dev->users++ && dev->open)
|
||||||
err = dev->open(dev);
|
retval = dev->open(dev);
|
||||||
|
|
||||||
if (err)
|
if (retval) {
|
||||||
handle->open--;
|
dev->users--;
|
||||||
|
if (!--handle->open) {
|
||||||
|
/*
|
||||||
|
* Make sure we are not delivering any more events
|
||||||
|
* through this handle
|
||||||
|
*/
|
||||||
|
synchronize_rcu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
mutex_unlock(&dev->mutex);
|
mutex_unlock(&dev->mutex);
|
||||||
|
return retval;
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_open_device);
|
EXPORT_SYMBOL(input_open_device);
|
||||||
|
|
||||||
int input_flush_device(struct input_handle* handle, struct file* file)
|
int input_flush_device(struct input_handle *handle, struct file *file)
|
||||||
{
|
{
|
||||||
if (handle->dev->flush)
|
struct input_dev *dev = handle->dev;
|
||||||
return handle->dev->flush(handle->dev, file);
|
int retval;
|
||||||
|
|
||||||
return 0;
|
retval = mutex_lock_interruptible(&dev->mutex);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
if (dev->flush)
|
||||||
|
retval = dev->flush(dev, file);
|
||||||
|
|
||||||
|
mutex_unlock(&dev->mutex);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_flush_device);
|
EXPORT_SYMBOL(input_flush_device);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* input_close_device - close input device
|
||||||
|
* @handle: handle through which device is being accessed
|
||||||
|
*
|
||||||
|
* This function should be called by input handlers when they
|
||||||
|
* want to stop receive events from given input device.
|
||||||
|
*/
|
||||||
void input_close_device(struct input_handle *handle)
|
void input_close_device(struct input_handle *handle)
|
||||||
{
|
{
|
||||||
struct input_dev *dev = handle->dev;
|
struct input_dev *dev = handle->dev;
|
||||||
|
|
||||||
input_release_device(handle);
|
|
||||||
|
|
||||||
mutex_lock(&dev->mutex);
|
mutex_lock(&dev->mutex);
|
||||||
|
|
||||||
|
__input_release_device(handle);
|
||||||
|
|
||||||
if (!--dev->users && dev->close)
|
if (!--dev->users && dev->close)
|
||||||
dev->close(dev);
|
dev->close(dev);
|
||||||
handle->open--;
|
|
||||||
|
if (!--handle->open) {
|
||||||
|
/*
|
||||||
|
* synchronize_rcu() makes sure that input_pass_event()
|
||||||
|
* completed and that no more input events are delivered
|
||||||
|
* through this handle
|
||||||
|
*/
|
||||||
|
synchronize_rcu();
|
||||||
|
}
|
||||||
|
|
||||||
mutex_unlock(&dev->mutex);
|
mutex_unlock(&dev->mutex);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_close_device);
|
EXPORT_SYMBOL(input_close_device);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare device for unregistering
|
||||||
|
*/
|
||||||
|
static void input_disconnect_device(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct input_handle *handle;
|
||||||
|
int code;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark device as going away. Note that we take dev->mutex here
|
||||||
|
* not to protect access to dev->going_away but rather to ensure
|
||||||
|
* that there are no threads in the middle of input_open_device()
|
||||||
|
*/
|
||||||
|
mutex_lock(&dev->mutex);
|
||||||
|
dev->going_away = 1;
|
||||||
|
mutex_unlock(&dev->mutex);
|
||||||
|
|
||||||
|
spin_lock_irq(&dev->event_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simulate keyup events for all pressed keys so that handlers
|
||||||
|
* are not left with "stuck" keys. The driver may continue
|
||||||
|
* generate events even after we done here but they will not
|
||||||
|
* reach any handlers.
|
||||||
|
*/
|
||||||
|
if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) {
|
||||||
|
for (code = 0; code <= KEY_MAX; code++) {
|
||||||
|
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
|
||||||
|
test_bit(code, dev->key)) {
|
||||||
|
input_pass_event(dev, EV_KEY, code, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each_entry(handle, &dev->h_list, d_node)
|
||||||
|
handle->open = 0;
|
||||||
|
|
||||||
|
spin_unlock_irq(&dev->event_lock);
|
||||||
|
}
|
||||||
|
|
||||||
static int input_fetch_keycode(struct input_dev *dev, int scancode)
|
static int input_fetch_keycode(struct input_dev *dev, int scancode)
|
||||||
{
|
{
|
||||||
switch (dev->keycodesize) {
|
switch (dev->keycodesize) {
|
||||||
|
@ -473,7 +677,8 @@ static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait)
|
||||||
|
|
||||||
static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos)
|
static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos)
|
||||||
{
|
{
|
||||||
/* acquire lock here ... Yes, we do need locking, I knowi, I know... */
|
if (mutex_lock_interruptible(&input_mutex))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
return seq_list_start(&input_dev_list, *pos);
|
return seq_list_start(&input_dev_list, *pos);
|
||||||
}
|
}
|
||||||
|
@ -485,7 +690,7 @@ static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||||
|
|
||||||
static void input_devices_seq_stop(struct seq_file *seq, void *v)
|
static void input_devices_seq_stop(struct seq_file *seq, void *v)
|
||||||
{
|
{
|
||||||
/* release lock here */
|
mutex_unlock(&input_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void input_seq_print_bitmap(struct seq_file *seq, const char *name,
|
static void input_seq_print_bitmap(struct seq_file *seq, const char *name,
|
||||||
|
@ -569,7 +774,9 @@ static const struct file_operations input_devices_fileops = {
|
||||||
|
|
||||||
static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
|
static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
|
||||||
{
|
{
|
||||||
/* acquire lock here ... Yes, we do need locking, I knowi, I know... */
|
if (mutex_lock_interruptible(&input_mutex))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
seq->private = (void *)(unsigned long)*pos;
|
seq->private = (void *)(unsigned long)*pos;
|
||||||
return seq_list_start(&input_handler_list, *pos);
|
return seq_list_start(&input_handler_list, *pos);
|
||||||
}
|
}
|
||||||
|
@ -582,7 +789,7 @@ static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||||
|
|
||||||
static void input_handlers_seq_stop(struct seq_file *seq, void *v)
|
static void input_handlers_seq_stop(struct seq_file *seq, void *v)
|
||||||
{
|
{
|
||||||
/* release lock here */
|
mutex_unlock(&input_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int input_handlers_seq_show(struct seq_file *seq, void *v)
|
static int input_handlers_seq_show(struct seq_file *seq, void *v)
|
||||||
|
@ -983,6 +1190,7 @@ struct input_dev *input_allocate_device(void)
|
||||||
dev->dev.class = &input_class;
|
dev->dev.class = &input_class;
|
||||||
device_initialize(&dev->dev);
|
device_initialize(&dev->dev);
|
||||||
mutex_init(&dev->mutex);
|
mutex_init(&dev->mutex);
|
||||||
|
spin_lock_init(&dev->event_lock);
|
||||||
INIT_LIST_HEAD(&dev->h_list);
|
INIT_LIST_HEAD(&dev->h_list);
|
||||||
INIT_LIST_HEAD(&dev->node);
|
INIT_LIST_HEAD(&dev->node);
|
||||||
|
|
||||||
|
@ -1000,7 +1208,7 @@ EXPORT_SYMBOL(input_allocate_device);
|
||||||
* This function should only be used if input_register_device()
|
* This function should only be used if input_register_device()
|
||||||
* was not called yet or if it failed. Once device was registered
|
* was not called yet or if it failed. Once device was registered
|
||||||
* use input_unregister_device() and memory will be freed once last
|
* use input_unregister_device() and memory will be freed once last
|
||||||
* refrence to the device is dropped.
|
* reference to the device is dropped.
|
||||||
*
|
*
|
||||||
* Device should be allocated by input_allocate_device().
|
* Device should be allocated by input_allocate_device().
|
||||||
*
|
*
|
||||||
|
@ -1070,6 +1278,18 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_set_capability);
|
EXPORT_SYMBOL(input_set_capability);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* input_register_device - register device with input core
|
||||||
|
* @dev: device to be registered
|
||||||
|
*
|
||||||
|
* This function registers device with input core. The device must be
|
||||||
|
* allocated with input_allocate_device() and all it's capabilities
|
||||||
|
* set up before registering.
|
||||||
|
* If function fails the device must be freed with input_free_device().
|
||||||
|
* Once device has been successfully registered it can be unregistered
|
||||||
|
* with input_unregister_device(); input_free_device() should not be
|
||||||
|
* called in this case.
|
||||||
|
*/
|
||||||
int input_register_device(struct input_dev *dev)
|
int input_register_device(struct input_dev *dev)
|
||||||
{
|
{
|
||||||
static atomic_t input_no = ATOMIC_INIT(0);
|
static atomic_t input_no = ATOMIC_INIT(0);
|
||||||
|
@ -1077,7 +1297,7 @@ int input_register_device(struct input_dev *dev)
|
||||||
const char *path;
|
const char *path;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
set_bit(EV_SYN, dev->evbit);
|
__set_bit(EV_SYN, dev->evbit);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If delay and period are pre-set by the driver, then autorepeating
|
* If delay and period are pre-set by the driver, then autorepeating
|
||||||
|
@ -1098,8 +1318,6 @@ int input_register_device(struct input_dev *dev)
|
||||||
if (!dev->setkeycode)
|
if (!dev->setkeycode)
|
||||||
dev->setkeycode = input_default_setkeycode;
|
dev->setkeycode = input_default_setkeycode;
|
||||||
|
|
||||||
list_add_tail(&dev->node, &input_dev_list);
|
|
||||||
|
|
||||||
snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
|
snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
|
||||||
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
|
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
|
||||||
|
|
||||||
|
@ -1115,49 +1333,79 @@ int input_register_device(struct input_dev *dev)
|
||||||
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
|
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
|
||||||
kfree(path);
|
kfree(path);
|
||||||
|
|
||||||
|
error = mutex_lock_interruptible(&input_mutex);
|
||||||
|
if (error) {
|
||||||
|
device_del(&dev->dev);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_add_tail(&dev->node, &input_dev_list);
|
||||||
|
|
||||||
list_for_each_entry(handler, &input_handler_list, node)
|
list_for_each_entry(handler, &input_handler_list, node)
|
||||||
input_attach_handler(dev, handler);
|
input_attach_handler(dev, handler);
|
||||||
|
|
||||||
input_wakeup_procfs_readers();
|
input_wakeup_procfs_readers();
|
||||||
|
|
||||||
|
mutex_unlock(&input_mutex);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_register_device);
|
EXPORT_SYMBOL(input_register_device);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* input_unregister_device - unregister previously registered device
|
||||||
|
* @dev: device to be unregistered
|
||||||
|
*
|
||||||
|
* This function unregisters an input device. Once device is unregistered
|
||||||
|
* the caller should not try to access it as it may get freed at any moment.
|
||||||
|
*/
|
||||||
void input_unregister_device(struct input_dev *dev)
|
void input_unregister_device(struct input_dev *dev)
|
||||||
{
|
{
|
||||||
struct input_handle *handle, *next;
|
struct input_handle *handle, *next;
|
||||||
int code;
|
|
||||||
|
|
||||||
for (code = 0; code <= KEY_MAX; code++)
|
input_disconnect_device(dev);
|
||||||
if (test_bit(code, dev->key))
|
|
||||||
input_report_key(dev, code, 0);
|
|
||||||
input_sync(dev);
|
|
||||||
|
|
||||||
del_timer_sync(&dev->timer);
|
mutex_lock(&input_mutex);
|
||||||
|
|
||||||
list_for_each_entry_safe(handle, next, &dev->h_list, d_node)
|
list_for_each_entry_safe(handle, next, &dev->h_list, d_node)
|
||||||
handle->handler->disconnect(handle);
|
handle->handler->disconnect(handle);
|
||||||
WARN_ON(!list_empty(&dev->h_list));
|
WARN_ON(!list_empty(&dev->h_list));
|
||||||
|
|
||||||
|
del_timer_sync(&dev->timer);
|
||||||
list_del_init(&dev->node);
|
list_del_init(&dev->node);
|
||||||
|
|
||||||
device_unregister(&dev->dev);
|
|
||||||
|
|
||||||
input_wakeup_procfs_readers();
|
input_wakeup_procfs_readers();
|
||||||
|
|
||||||
|
mutex_unlock(&input_mutex);
|
||||||
|
|
||||||
|
device_unregister(&dev->dev);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_unregister_device);
|
EXPORT_SYMBOL(input_unregister_device);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* input_register_handler - register a new input handler
|
||||||
|
* @handler: handler to be registered
|
||||||
|
*
|
||||||
|
* This function registers a new input handler (interface) for input
|
||||||
|
* devices in the system and attaches it to all input devices that
|
||||||
|
* are compatible with the handler.
|
||||||
|
*/
|
||||||
int input_register_handler(struct input_handler *handler)
|
int input_register_handler(struct input_handler *handler)
|
||||||
{
|
{
|
||||||
struct input_dev *dev;
|
struct input_dev *dev;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = mutex_lock_interruptible(&input_mutex);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&handler->h_list);
|
INIT_LIST_HEAD(&handler->h_list);
|
||||||
|
|
||||||
if (handler->fops != NULL) {
|
if (handler->fops != NULL) {
|
||||||
if (input_table[handler->minor >> 5])
|
if (input_table[handler->minor >> 5]) {
|
||||||
return -EBUSY;
|
retval = -EBUSY;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
input_table[handler->minor >> 5] = handler;
|
input_table[handler->minor >> 5] = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1167,14 +1415,26 @@ int input_register_handler(struct input_handler *handler)
|
||||||
input_attach_handler(dev, handler);
|
input_attach_handler(dev, handler);
|
||||||
|
|
||||||
input_wakeup_procfs_readers();
|
input_wakeup_procfs_readers();
|
||||||
return 0;
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&input_mutex);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_register_handler);
|
EXPORT_SYMBOL(input_register_handler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* input_unregister_handler - unregisters an input handler
|
||||||
|
* @handler: handler to be unregistered
|
||||||
|
*
|
||||||
|
* This function disconnects a handler from its input devices and
|
||||||
|
* removes it from lists of known handlers.
|
||||||
|
*/
|
||||||
void input_unregister_handler(struct input_handler *handler)
|
void input_unregister_handler(struct input_handler *handler)
|
||||||
{
|
{
|
||||||
struct input_handle *handle, *next;
|
struct input_handle *handle, *next;
|
||||||
|
|
||||||
|
mutex_lock(&input_mutex);
|
||||||
|
|
||||||
list_for_each_entry_safe(handle, next, &handler->h_list, h_node)
|
list_for_each_entry_safe(handle, next, &handler->h_list, h_node)
|
||||||
handler->disconnect(handle);
|
handler->disconnect(handle);
|
||||||
WARN_ON(!list_empty(&handler->h_list));
|
WARN_ON(!list_empty(&handler->h_list));
|
||||||
|
@ -1185,14 +1445,45 @@ void input_unregister_handler(struct input_handler *handler)
|
||||||
input_table[handler->minor >> 5] = NULL;
|
input_table[handler->minor >> 5] = NULL;
|
||||||
|
|
||||||
input_wakeup_procfs_readers();
|
input_wakeup_procfs_readers();
|
||||||
|
|
||||||
|
mutex_unlock(&input_mutex);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_unregister_handler);
|
EXPORT_SYMBOL(input_unregister_handler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* input_register_handle - register a new input handle
|
||||||
|
* @handle: handle to register
|
||||||
|
*
|
||||||
|
* This function puts a new input handle onto device's
|
||||||
|
* and handler's lists so that events can flow through
|
||||||
|
* it once it is opened using input_open_device().
|
||||||
|
*
|
||||||
|
* This function is supposed to be called from handler's
|
||||||
|
* connect() method.
|
||||||
|
*/
|
||||||
int input_register_handle(struct input_handle *handle)
|
int input_register_handle(struct input_handle *handle)
|
||||||
{
|
{
|
||||||
struct input_handler *handler = handle->handler;
|
struct input_handler *handler = handle->handler;
|
||||||
|
struct input_dev *dev = handle->dev;
|
||||||
|
int error;
|
||||||
|
|
||||||
list_add_tail(&handle->d_node, &handle->dev->h_list);
|
/*
|
||||||
|
* We take dev->mutex here to prevent race with
|
||||||
|
* input_release_device().
|
||||||
|
*/
|
||||||
|
error = mutex_lock_interruptible(&dev->mutex);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
list_add_tail_rcu(&handle->d_node, &dev->h_list);
|
||||||
|
mutex_unlock(&dev->mutex);
|
||||||
|
synchronize_rcu();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since we are supposed to be called from ->connect()
|
||||||
|
* which is mutually exclusive with ->disconnect()
|
||||||
|
* we can't be racing with input_unregister_handle()
|
||||||
|
* and so separate lock is not needed here.
|
||||||
|
*/
|
||||||
list_add_tail(&handle->h_node, &handler->h_list);
|
list_add_tail(&handle->h_node, &handler->h_list);
|
||||||
|
|
||||||
if (handler->start)
|
if (handler->start)
|
||||||
|
@ -1202,10 +1493,29 @@ int input_register_handle(struct input_handle *handle)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_register_handle);
|
EXPORT_SYMBOL(input_register_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* input_unregister_handle - unregister an input handle
|
||||||
|
* @handle: handle to unregister
|
||||||
|
*
|
||||||
|
* This function removes input handle from device's
|
||||||
|
* and handler's lists.
|
||||||
|
*
|
||||||
|
* This function is supposed to be called from handler's
|
||||||
|
* disconnect() method.
|
||||||
|
*/
|
||||||
void input_unregister_handle(struct input_handle *handle)
|
void input_unregister_handle(struct input_handle *handle)
|
||||||
{
|
{
|
||||||
|
struct input_dev *dev = handle->dev;
|
||||||
|
|
||||||
list_del_init(&handle->h_node);
|
list_del_init(&handle->h_node);
|
||||||
list_del_init(&handle->d_node);
|
|
||||||
|
/*
|
||||||
|
* Take dev->mutex to prevent race with input_release_device().
|
||||||
|
*/
|
||||||
|
mutex_lock(&dev->mutex);
|
||||||
|
list_del_rcu(&handle->d_node);
|
||||||
|
mutex_unlock(&dev->mutex);
|
||||||
|
synchronize_rcu();
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_unregister_handle);
|
EXPORT_SYMBOL(input_unregister_handle);
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,8 @@ struct joydev {
|
||||||
struct input_handle handle;
|
struct input_handle handle;
|
||||||
wait_queue_head_t wait;
|
wait_queue_head_t wait;
|
||||||
struct list_head client_list;
|
struct list_head client_list;
|
||||||
|
spinlock_t client_lock; /* protects client_list */
|
||||||
|
struct mutex mutex;
|
||||||
struct device dev;
|
struct device dev;
|
||||||
|
|
||||||
struct js_corr corr[ABS_MAX + 1];
|
struct js_corr corr[ABS_MAX + 1];
|
||||||
|
@ -61,23 +63,28 @@ struct joydev_client {
|
||||||
int head;
|
int head;
|
||||||
int tail;
|
int tail;
|
||||||
int startup;
|
int startup;
|
||||||
|
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
|
||||||
struct fasync_struct *fasync;
|
struct fasync_struct *fasync;
|
||||||
struct joydev *joydev;
|
struct joydev *joydev;
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct joydev *joydev_table[JOYDEV_MINORS];
|
static struct joydev *joydev_table[JOYDEV_MINORS];
|
||||||
|
static DEFINE_MUTEX(joydev_table_mutex);
|
||||||
|
|
||||||
static int joydev_correct(int value, struct js_corr *corr)
|
static int joydev_correct(int value, struct js_corr *corr)
|
||||||
{
|
{
|
||||||
switch (corr->type) {
|
switch (corr->type) {
|
||||||
|
|
||||||
case JS_CORR_NONE:
|
case JS_CORR_NONE:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JS_CORR_BROKEN:
|
case JS_CORR_BROKEN:
|
||||||
value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
|
value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
|
||||||
((corr->coef[3] * (value - corr->coef[1])) >> 14)) :
|
((corr->coef[3] * (value - corr->coef[1])) >> 14)) :
|
||||||
((corr->coef[2] * (value - corr->coef[0])) >> 14);
|
((corr->coef[2] * (value - corr->coef[0])) >> 14);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -85,7 +92,32 @@ static int joydev_correct(int value, struct js_corr *corr)
|
||||||
return value < -32767 ? -32767 : (value > 32767 ? 32767 : value);
|
return value < -32767 ? -32767 : (value > 32767 ? 32767 : value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
|
static void joydev_pass_event(struct joydev_client *client,
|
||||||
|
struct js_event *event)
|
||||||
|
{
|
||||||
|
struct joydev *joydev = client->joydev;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IRQs already disabled, just acquire the lock
|
||||||
|
*/
|
||||||
|
spin_lock(&client->buffer_lock);
|
||||||
|
|
||||||
|
client->buffer[client->head] = *event;
|
||||||
|
|
||||||
|
if (client->startup == joydev->nabs + joydev->nkey) {
|
||||||
|
client->head++;
|
||||||
|
client->head &= JOYDEV_BUFFER_SIZE - 1;
|
||||||
|
if (client->tail == client->head)
|
||||||
|
client->startup = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock(&client->buffer_lock);
|
||||||
|
|
||||||
|
kill_fasync(&client->fasync, SIGIO, POLL_IN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void joydev_event(struct input_handle *handle,
|
||||||
|
unsigned int type, unsigned int code, int value)
|
||||||
{
|
{
|
||||||
struct joydev *joydev = handle->private;
|
struct joydev *joydev = handle->private;
|
||||||
struct joydev_client *client;
|
struct joydev_client *client;
|
||||||
|
@ -104,7 +136,8 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne
|
||||||
case EV_ABS:
|
case EV_ABS:
|
||||||
event.type = JS_EVENT_AXIS;
|
event.type = JS_EVENT_AXIS;
|
||||||
event.number = joydev->absmap[code];
|
event.number = joydev->absmap[code];
|
||||||
event.value = joydev_correct(value, joydev->corr + event.number);
|
event.value = joydev_correct(value,
|
||||||
|
&joydev->corr[event.number]);
|
||||||
if (event.value == joydev->abs[event.number])
|
if (event.value == joydev->abs[event.number])
|
||||||
return;
|
return;
|
||||||
joydev->abs[event.number] = event.value;
|
joydev->abs[event.number] = event.value;
|
||||||
|
@ -116,16 +149,10 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne
|
||||||
|
|
||||||
event.time = jiffies_to_msecs(jiffies);
|
event.time = jiffies_to_msecs(jiffies);
|
||||||
|
|
||||||
list_for_each_entry(client, &joydev->client_list, node) {
|
rcu_read_lock();
|
||||||
|
list_for_each_entry_rcu(client, &joydev->client_list, node)
|
||||||
memcpy(client->buffer + client->head, &event, sizeof(struct js_event));
|
joydev_pass_event(client, &event);
|
||||||
|
rcu_read_unlock();
|
||||||
if (client->startup == joydev->nabs + joydev->nkey)
|
|
||||||
if (client->tail == (client->head = (client->head + 1) & (JOYDEV_BUFFER_SIZE - 1)))
|
|
||||||
client->startup = 0;
|
|
||||||
|
|
||||||
kill_fasync(&client->fasync, SIGIO, POLL_IN);
|
|
||||||
}
|
|
||||||
|
|
||||||
wake_up_interruptible(&joydev->wait);
|
wake_up_interruptible(&joydev->wait);
|
||||||
}
|
}
|
||||||
|
@ -144,23 +171,83 @@ static void joydev_free(struct device *dev)
|
||||||
{
|
{
|
||||||
struct joydev *joydev = container_of(dev, struct joydev, dev);
|
struct joydev *joydev = container_of(dev, struct joydev, dev);
|
||||||
|
|
||||||
joydev_table[joydev->minor] = NULL;
|
|
||||||
kfree(joydev);
|
kfree(joydev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void joydev_attach_client(struct joydev *joydev,
|
||||||
|
struct joydev_client *client)
|
||||||
|
{
|
||||||
|
spin_lock(&joydev->client_lock);
|
||||||
|
list_add_tail_rcu(&client->node, &joydev->client_list);
|
||||||
|
spin_unlock(&joydev->client_lock);
|
||||||
|
synchronize_rcu();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void joydev_detach_client(struct joydev *joydev,
|
||||||
|
struct joydev_client *client)
|
||||||
|
{
|
||||||
|
spin_lock(&joydev->client_lock);
|
||||||
|
list_del_rcu(&client->node);
|
||||||
|
spin_unlock(&joydev->client_lock);
|
||||||
|
synchronize_rcu();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int joydev_open_device(struct joydev *joydev)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = mutex_lock_interruptible(&joydev->mutex);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
if (!joydev->exist)
|
||||||
|
retval = -ENODEV;
|
||||||
|
else if (!joydev->open++) {
|
||||||
|
retval = input_open_device(&joydev->handle);
|
||||||
|
if (retval)
|
||||||
|
joydev->open--;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&joydev->mutex);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void joydev_close_device(struct joydev *joydev)
|
||||||
|
{
|
||||||
|
mutex_lock(&joydev->mutex);
|
||||||
|
|
||||||
|
if (joydev->exist && !--joydev->open)
|
||||||
|
input_close_device(&joydev->handle);
|
||||||
|
|
||||||
|
mutex_unlock(&joydev->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wake up users waiting for IO so they can disconnect from
|
||||||
|
* dead device.
|
||||||
|
*/
|
||||||
|
static void joydev_hangup(struct joydev *joydev)
|
||||||
|
{
|
||||||
|
struct joydev_client *client;
|
||||||
|
|
||||||
|
spin_lock(&joydev->client_lock);
|
||||||
|
list_for_each_entry(client, &joydev->client_list, node)
|
||||||
|
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
|
||||||
|
spin_unlock(&joydev->client_lock);
|
||||||
|
|
||||||
|
wake_up_interruptible(&joydev->wait);
|
||||||
|
}
|
||||||
|
|
||||||
static int joydev_release(struct inode *inode, struct file *file)
|
static int joydev_release(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
struct joydev_client *client = file->private_data;
|
struct joydev_client *client = file->private_data;
|
||||||
struct joydev *joydev = client->joydev;
|
struct joydev *joydev = client->joydev;
|
||||||
|
|
||||||
joydev_fasync(-1, file, 0);
|
joydev_fasync(-1, file, 0);
|
||||||
|
joydev_detach_client(joydev, client);
|
||||||
list_del(&client->node);
|
|
||||||
kfree(client);
|
kfree(client);
|
||||||
|
|
||||||
if (!--joydev->open && joydev->exist)
|
joydev_close_device(joydev);
|
||||||
input_close_device(&joydev->handle);
|
|
||||||
|
|
||||||
put_device(&joydev->dev);
|
put_device(&joydev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -176,11 +263,16 @@ static int joydev_open(struct inode *inode, struct file *file)
|
||||||
if (i >= JOYDEV_MINORS)
|
if (i >= JOYDEV_MINORS)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
error = mutex_lock_interruptible(&joydev_table_mutex);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
joydev = joydev_table[i];
|
joydev = joydev_table[i];
|
||||||
if (!joydev || !joydev->exist)
|
if (joydev)
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
get_device(&joydev->dev);
|
get_device(&joydev->dev);
|
||||||
|
mutex_unlock(&joydev_table_mutex);
|
||||||
|
|
||||||
|
if (!joydev)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL);
|
client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
|
@ -188,37 +280,129 @@ static int joydev_open(struct inode *inode, struct file *file)
|
||||||
goto err_put_joydev;
|
goto err_put_joydev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_lock_init(&client->buffer_lock);
|
||||||
client->joydev = joydev;
|
client->joydev = joydev;
|
||||||
list_add_tail(&client->node, &joydev->client_list);
|
joydev_attach_client(joydev, client);
|
||||||
|
|
||||||
if (!joydev->open++ && joydev->exist) {
|
error = joydev_open_device(joydev);
|
||||||
error = input_open_device(&joydev->handle);
|
|
||||||
if (error)
|
if (error)
|
||||||
goto err_free_client;
|
goto err_free_client;
|
||||||
}
|
|
||||||
|
|
||||||
file->private_data = client;
|
file->private_data = client;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_free_client:
|
err_free_client:
|
||||||
list_del(&client->node);
|
joydev_detach_client(joydev, client);
|
||||||
kfree(client);
|
kfree(client);
|
||||||
err_put_joydev:
|
err_put_joydev:
|
||||||
put_device(&joydev->dev);
|
put_device(&joydev->dev);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t joydev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
|
static int joydev_generate_startup_event(struct joydev_client *client,
|
||||||
|
struct input_dev *input,
|
||||||
|
struct js_event *event)
|
||||||
{
|
{
|
||||||
return -EINVAL;
|
struct joydev *joydev = client->joydev;
|
||||||
|
int have_event;
|
||||||
|
|
||||||
|
spin_lock_irq(&client->buffer_lock);
|
||||||
|
|
||||||
|
have_event = client->startup < joydev->nabs + joydev->nkey;
|
||||||
|
|
||||||
|
if (have_event) {
|
||||||
|
|
||||||
|
event->time = jiffies_to_msecs(jiffies);
|
||||||
|
if (client->startup < joydev->nkey) {
|
||||||
|
event->type = JS_EVENT_BUTTON | JS_EVENT_INIT;
|
||||||
|
event->number = client->startup;
|
||||||
|
event->value = !!test_bit(joydev->keypam[event->number],
|
||||||
|
input->key);
|
||||||
|
} else {
|
||||||
|
event->type = JS_EVENT_AXIS | JS_EVENT_INIT;
|
||||||
|
event->number = client->startup - joydev->nkey;
|
||||||
|
event->value = joydev->abs[event->number];
|
||||||
|
}
|
||||||
|
client->startup++;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irq(&client->buffer_lock);
|
||||||
|
|
||||||
|
return have_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
|
static int joydev_fetch_next_event(struct joydev_client *client,
|
||||||
|
struct js_event *event)
|
||||||
|
{
|
||||||
|
int have_event;
|
||||||
|
|
||||||
|
spin_lock_irq(&client->buffer_lock);
|
||||||
|
|
||||||
|
have_event = client->head != client->tail;
|
||||||
|
if (have_event) {
|
||||||
|
*event = client->buffer[client->tail++];
|
||||||
|
client->tail &= JOYDEV_BUFFER_SIZE - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irq(&client->buffer_lock);
|
||||||
|
|
||||||
|
return have_event;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Old joystick interface
|
||||||
|
*/
|
||||||
|
static ssize_t joydev_0x_read(struct joydev_client *client,
|
||||||
|
struct input_dev *input,
|
||||||
|
char __user *buf)
|
||||||
|
{
|
||||||
|
struct joydev *joydev = client->joydev;
|
||||||
|
struct JS_DATA_TYPE data;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
spin_lock_irq(&input->event_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get device state
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset reader's event queue
|
||||||
|
*/
|
||||||
|
spin_lock(&client->buffer_lock);
|
||||||
|
client->startup = 0;
|
||||||
|
client->tail = client->head;
|
||||||
|
spin_unlock(&client->buffer_lock);
|
||||||
|
|
||||||
|
spin_unlock_irq(&input->event_lock);
|
||||||
|
|
||||||
|
if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return sizeof(struct JS_DATA_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int joydev_data_pending(struct joydev_client *client)
|
||||||
|
{
|
||||||
|
struct joydev *joydev = client->joydev;
|
||||||
|
|
||||||
|
return client->startup < joydev->nabs + joydev->nkey ||
|
||||||
|
client->head != client->tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t joydev_read(struct file *file, char __user *buf,
|
||||||
|
size_t count, loff_t *ppos)
|
||||||
{
|
{
|
||||||
struct joydev_client *client = file->private_data;
|
struct joydev_client *client = file->private_data;
|
||||||
struct joydev *joydev = client->joydev;
|
struct joydev *joydev = client->joydev;
|
||||||
struct input_dev *input = joydev->handle.dev;
|
struct input_dev *input = joydev->handle.dev;
|
||||||
int retval = 0;
|
struct js_event event;
|
||||||
|
int retval;
|
||||||
|
|
||||||
if (!joydev->exist)
|
if (!joydev->exist)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -226,68 +410,35 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo
|
||||||
if (count < sizeof(struct js_event))
|
if (count < sizeof(struct js_event))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (count == sizeof(struct JS_DATA_TYPE)) {
|
if (count == sizeof(struct JS_DATA_TYPE))
|
||||||
|
return joydev_0x_read(client, input, buf);
|
||||||
|
|
||||||
struct JS_DATA_TYPE data;
|
if (!joydev_data_pending(client) && (file->f_flags & O_NONBLOCK))
|
||||||
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;
|
|
||||||
|
|
||||||
client->startup = 0;
|
|
||||||
client->tail = client->head;
|
|
||||||
|
|
||||||
return sizeof(struct JS_DATA_TYPE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client->startup == joydev->nabs + joydev->nkey &&
|
|
||||||
client->head == client->tail && (file->f_flags & O_NONBLOCK))
|
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
retval = wait_event_interruptible(joydev->wait,
|
retval = wait_event_interruptible(joydev->wait,
|
||||||
!joydev->exist ||
|
!joydev->exist || joydev_data_pending(client));
|
||||||
client->startup < joydev->nabs + joydev->nkey ||
|
|
||||||
client->head != client->tail);
|
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
if (!joydev->exist)
|
if (!joydev->exist)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
while (client->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) {
|
while (retval + sizeof(struct js_event) <= count &&
|
||||||
|
joydev_generate_startup_event(client, input, &event)) {
|
||||||
struct js_event event;
|
|
||||||
|
|
||||||
event.time = jiffies_to_msecs(jiffies);
|
|
||||||
|
|
||||||
if (client->startup < joydev->nkey) {
|
|
||||||
event.type = JS_EVENT_BUTTON | JS_EVENT_INIT;
|
|
||||||
event.number = client->startup;
|
|
||||||
event.value = !!test_bit(joydev->keypam[event.number], input->key);
|
|
||||||
} else {
|
|
||||||
event.type = JS_EVENT_AXIS | JS_EVENT_INIT;
|
|
||||||
event.number = client->startup - joydev->nkey;
|
|
||||||
event.value = joydev->abs[event.number];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
|
if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
client->startup++;
|
|
||||||
retval += sizeof(struct js_event);
|
retval += sizeof(struct js_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (client->head != client->tail && retval + sizeof(struct js_event) <= count) {
|
while (retval + sizeof(struct js_event) <= count &&
|
||||||
|
joydev_fetch_next_event(client, &event)) {
|
||||||
|
|
||||||
if (copy_to_user(buf + retval, client->buffer + client->tail, sizeof(struct js_event)))
|
if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
client->tail = (client->tail + 1) & (JOYDEV_BUFFER_SIZE - 1);
|
|
||||||
retval += sizeof(struct js_event);
|
retval += sizeof(struct js_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,11 +452,12 @@ static unsigned int joydev_poll(struct file *file, poll_table *wait)
|
||||||
struct joydev *joydev = client->joydev;
|
struct joydev *joydev = client->joydev;
|
||||||
|
|
||||||
poll_wait(file, &joydev->wait, wait);
|
poll_wait(file, &joydev->wait, wait);
|
||||||
return ((client->head != client->tail || client->startup < joydev->nabs + joydev->nkey) ?
|
return (joydev_data_pending(client) ? (POLLIN | POLLRDNORM) : 0) |
|
||||||
(POLLIN | POLLRDNORM) : 0) | (joydev->exist ? 0 : (POLLHUP | POLLERR));
|
(joydev->exist ? 0 : (POLLHUP | POLLERR));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __user *argp)
|
static int joydev_ioctl_common(struct joydev *joydev,
|
||||||
|
unsigned int cmd, void __user *argp)
|
||||||
{
|
{
|
||||||
struct input_dev *dev = joydev->handle.dev;
|
struct input_dev *dev = joydev->handle.dev;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
@ -339,9 +491,11 @@ static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __u
|
||||||
if (copy_from_user(joydev->corr, argp,
|
if (copy_from_user(joydev->corr, argp,
|
||||||
sizeof(joydev->corr[0]) * joydev->nabs))
|
sizeof(joydev->corr[0]) * joydev->nabs))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
for (i = 0; i < joydev->nabs; i++) {
|
for (i = 0; i < joydev->nabs; i++) {
|
||||||
j = joydev->abspam[i];
|
j = joydev->abspam[i];
|
||||||
joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i);
|
joydev->abs[i] = joydev_correct(dev->abs[j],
|
||||||
|
&joydev->corr[i]);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -350,8 +504,10 @@ static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __u
|
||||||
sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0;
|
sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0;
|
||||||
|
|
||||||
case JSIOCSAXMAP:
|
case JSIOCSAXMAP:
|
||||||
if (copy_from_user(joydev->abspam, argp, sizeof(__u8) * (ABS_MAX + 1)))
|
if (copy_from_user(joydev->abspam, argp,
|
||||||
|
sizeof(__u8) * (ABS_MAX + 1)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
for (i = 0; i < joydev->nabs; i++) {
|
for (i = 0; i < joydev->nabs; i++) {
|
||||||
if (joydev->abspam[i] > ABS_MAX)
|
if (joydev->abspam[i] > ABS_MAX)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -364,13 +520,17 @@ static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __u
|
||||||
sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0;
|
sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0;
|
||||||
|
|
||||||
case JSIOCSBTNMAP:
|
case JSIOCSBTNMAP:
|
||||||
if (copy_from_user(joydev->keypam, argp, sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)))
|
if (copy_from_user(joydev->keypam, argp,
|
||||||
|
sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
for (i = 0; i < joydev->nkey; i++) {
|
for (i = 0; i < joydev->nkey; i++) {
|
||||||
if (joydev->keypam[i] > KEY_MAX || joydev->keypam[i] < BTN_MISC)
|
if (joydev->keypam[i] > KEY_MAX ||
|
||||||
|
joydev->keypam[i] < BTN_MISC)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
joydev->keymap[joydev->keypam[i] - BTN_MISC] = i;
|
joydev->keymap[joydev->keypam[i] - BTN_MISC] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case JSIOCGBTNMAP:
|
case JSIOCGBTNMAP:
|
||||||
|
@ -378,7 +538,7 @@ static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __u
|
||||||
sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0;
|
sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
|
if ((cmd & ~IOCSIZE_MASK) == JSIOCGNAME(0)) {
|
||||||
int len;
|
int len;
|
||||||
if (!dev->name)
|
if (!dev->name)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -394,33 +554,42 @@ static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __u
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
static long joydev_compat_ioctl(struct file *file,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
struct joydev_client *client = file->private_data;
|
struct joydev_client *client = file->private_data;
|
||||||
struct joydev *joydev = client->joydev;
|
struct joydev *joydev = client->joydev;
|
||||||
void __user *argp = (void __user *)arg;
|
void __user *argp = (void __user *)arg;
|
||||||
s32 tmp32;
|
s32 tmp32;
|
||||||
struct JS_DATA_SAVE_TYPE_32 ds32;
|
struct JS_DATA_SAVE_TYPE_32 ds32;
|
||||||
int err;
|
int retval;
|
||||||
|
|
||||||
if (!joydev->exist)
|
retval = mutex_lock_interruptible(&joydev->mutex);
|
||||||
return -ENODEV;
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
if (!joydev->exist) {
|
||||||
|
retval = -ENODEV;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
|
||||||
switch(cmd) {
|
|
||||||
case JS_SET_TIMELIMIT:
|
case JS_SET_TIMELIMIT:
|
||||||
err = get_user(tmp32, (s32 __user *) arg);
|
retval = get_user(tmp32, (s32 __user *) arg);
|
||||||
if (err == 0)
|
if (retval == 0)
|
||||||
joydev->glue.JS_TIMELIMIT = tmp32;
|
joydev->glue.JS_TIMELIMIT = tmp32;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JS_GET_TIMELIMIT:
|
case JS_GET_TIMELIMIT:
|
||||||
tmp32 = joydev->glue.JS_TIMELIMIT;
|
tmp32 = joydev->glue.JS_TIMELIMIT;
|
||||||
err = put_user(tmp32, (s32 __user *) arg);
|
retval = put_user(tmp32, (s32 __user *) arg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JS_SET_ALL:
|
case JS_SET_ALL:
|
||||||
err = copy_from_user(&ds32, argp,
|
retval = copy_from_user(&ds32, argp,
|
||||||
sizeof(ds32)) ? -EFAULT : 0;
|
sizeof(ds32)) ? -EFAULT : 0;
|
||||||
if (err == 0) {
|
if (retval == 0) {
|
||||||
joydev->glue.JS_TIMEOUT = ds32.JS_TIMEOUT;
|
joydev->glue.JS_TIMEOUT = ds32.JS_TIMEOUT;
|
||||||
joydev->glue.BUSY = ds32.BUSY;
|
joydev->glue.BUSY = ds32.BUSY;
|
||||||
joydev->glue.JS_EXPIRETIME = ds32.JS_EXPIRETIME;
|
joydev->glue.JS_EXPIRETIME = ds32.JS_EXPIRETIME;
|
||||||
|
@ -438,55 +607,119 @@ static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
|
||||||
ds32.JS_SAVE = joydev->glue.JS_SAVE;
|
ds32.JS_SAVE = joydev->glue.JS_SAVE;
|
||||||
ds32.JS_CORR = joydev->glue.JS_CORR;
|
ds32.JS_CORR = joydev->glue.JS_CORR;
|
||||||
|
|
||||||
err = copy_to_user(argp, &ds32, sizeof(ds32)) ? -EFAULT : 0;
|
retval = copy_to_user(argp, &ds32, sizeof(ds32)) ? -EFAULT : 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err = joydev_ioctl_common(joydev, cmd, argp);
|
retval = joydev_ioctl_common(joydev, cmd, argp);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return err;
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&joydev->mutex);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_COMPAT */
|
#endif /* CONFIG_COMPAT */
|
||||||
|
|
||||||
static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
|
static long joydev_ioctl(struct file *file,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
struct joydev_client *client = file->private_data;
|
struct joydev_client *client = file->private_data;
|
||||||
struct joydev *joydev = client->joydev;
|
struct joydev *joydev = client->joydev;
|
||||||
void __user *argp = (void __user *)arg;
|
void __user *argp = (void __user *)arg;
|
||||||
|
int retval;
|
||||||
|
|
||||||
if (!joydev->exist)
|
retval = mutex_lock_interruptible(&joydev->mutex);
|
||||||
return -ENODEV;
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
switch(cmd) {
|
if (!joydev->exist) {
|
||||||
case JS_SET_TIMELIMIT:
|
retval = -ENODEV;
|
||||||
return get_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg);
|
goto out;
|
||||||
case JS_GET_TIMELIMIT:
|
|
||||||
return put_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg);
|
|
||||||
case JS_SET_ALL:
|
|
||||||
return copy_from_user(&joydev->glue, argp,
|
|
||||||
sizeof(joydev->glue)) ? -EFAULT : 0;
|
|
||||||
case JS_GET_ALL:
|
|
||||||
return copy_to_user(argp, &joydev->glue,
|
|
||||||
sizeof(joydev->glue)) ? -EFAULT : 0;
|
|
||||||
default:
|
|
||||||
return joydev_ioctl_common(joydev, cmd, argp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
|
||||||
|
case JS_SET_TIMELIMIT:
|
||||||
|
retval = get_user(joydev->glue.JS_TIMELIMIT,
|
||||||
|
(long __user *) arg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JS_GET_TIMELIMIT:
|
||||||
|
retval = put_user(joydev->glue.JS_TIMELIMIT,
|
||||||
|
(long __user *) arg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JS_SET_ALL:
|
||||||
|
retval = copy_from_user(&joydev->glue, argp,
|
||||||
|
sizeof(joydev->glue)) ? -EFAULT: 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JS_GET_ALL:
|
||||||
|
retval = copy_to_user(argp, &joydev->glue,
|
||||||
|
sizeof(joydev->glue)) ? -EFAULT : 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
retval = joydev_ioctl_common(joydev, cmd, argp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
mutex_unlock(&joydev->mutex);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct file_operations joydev_fops = {
|
static const struct file_operations joydev_fops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.read = joydev_read,
|
.read = joydev_read,
|
||||||
.write = joydev_write,
|
|
||||||
.poll = joydev_poll,
|
.poll = joydev_poll,
|
||||||
.open = joydev_open,
|
.open = joydev_open,
|
||||||
.release = joydev_release,
|
.release = joydev_release,
|
||||||
.ioctl = joydev_ioctl,
|
.unlocked_ioctl = joydev_ioctl,
|
||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
.compat_ioctl = joydev_compat_ioctl,
|
.compat_ioctl = joydev_compat_ioctl,
|
||||||
#endif
|
#endif
|
||||||
.fasync = joydev_fasync,
|
.fasync = joydev_fasync,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int joydev_install_chrdev(struct joydev *joydev)
|
||||||
|
{
|
||||||
|
joydev_table[joydev->minor] = joydev;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void joydev_remove_chrdev(struct joydev *joydev)
|
||||||
|
{
|
||||||
|
mutex_lock(&joydev_table_mutex);
|
||||||
|
joydev_table[joydev->minor] = NULL;
|
||||||
|
mutex_unlock(&joydev_table_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark device non-existant. This disables writes, ioctls and
|
||||||
|
* prevents new users from opening the device. Already posted
|
||||||
|
* blocking reads will stay, however new ones will fail.
|
||||||
|
*/
|
||||||
|
static void joydev_mark_dead(struct joydev *joydev)
|
||||||
|
{
|
||||||
|
mutex_lock(&joydev->mutex);
|
||||||
|
joydev->exist = 0;
|
||||||
|
mutex_unlock(&joydev->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void joydev_cleanup(struct joydev *joydev)
|
||||||
|
{
|
||||||
|
struct input_handle *handle = &joydev->handle;
|
||||||
|
|
||||||
|
joydev_mark_dead(joydev);
|
||||||
|
joydev_hangup(joydev);
|
||||||
|
joydev_remove_chrdev(joydev);
|
||||||
|
|
||||||
|
/* joydev is marked dead so noone else accesses joydev->open */
|
||||||
|
if (joydev->open)
|
||||||
|
input_close_device(handle);
|
||||||
|
}
|
||||||
|
|
||||||
static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
|
static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
|
||||||
const struct input_device_id *id)
|
const struct input_device_id *id)
|
||||||
{
|
{
|
||||||
|
@ -494,7 +727,10 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
|
||||||
int i, j, t, minor;
|
int i, j, t, minor;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++);
|
for (minor = 0; minor < JOYDEV_MINORS; minor++)
|
||||||
|
if (!joydev_table[minor])
|
||||||
|
break;
|
||||||
|
|
||||||
if (minor == JOYDEV_MINORS) {
|
if (minor == JOYDEV_MINORS) {
|
||||||
printk(KERN_ERR "joydev: no more free joydev devices\n");
|
printk(KERN_ERR "joydev: no more free joydev devices\n");
|
||||||
return -ENFILE;
|
return -ENFILE;
|
||||||
|
@ -505,15 +741,19 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&joydev->client_list);
|
INIT_LIST_HEAD(&joydev->client_list);
|
||||||
|
spin_lock_init(&joydev->client_lock);
|
||||||
|
mutex_init(&joydev->mutex);
|
||||||
init_waitqueue_head(&joydev->wait);
|
init_waitqueue_head(&joydev->wait);
|
||||||
|
|
||||||
|
snprintf(joydev->name, sizeof(joydev->name), "js%d", minor);
|
||||||
|
joydev->exist = 1;
|
||||||
joydev->minor = minor;
|
joydev->minor = minor;
|
||||||
|
|
||||||
joydev->exist = 1;
|
joydev->exist = 1;
|
||||||
joydev->handle.dev = dev;
|
joydev->handle.dev = dev;
|
||||||
joydev->handle.name = joydev->name;
|
joydev->handle.name = joydev->name;
|
||||||
joydev->handle.handler = handler;
|
joydev->handle.handler = handler;
|
||||||
joydev->handle.private = joydev;
|
joydev->handle.private = joydev;
|
||||||
snprintf(joydev->name, sizeof(joydev->name), "js%d", minor);
|
|
||||||
|
|
||||||
for (i = 0; i < ABS_MAX + 1; i++)
|
for (i = 0; i < ABS_MAX + 1; i++)
|
||||||
if (test_bit(i, dev->absbit)) {
|
if (test_bit(i, dev->absbit)) {
|
||||||
|
@ -545,67 +785,65 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
|
||||||
}
|
}
|
||||||
joydev->corr[i].type = JS_CORR_BROKEN;
|
joydev->corr[i].type = JS_CORR_BROKEN;
|
||||||
joydev->corr[i].prec = dev->absfuzz[j];
|
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[0] =
|
||||||
joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j];
|
(dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j];
|
||||||
if (!(t = ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j])))
|
joydev->corr[i].coef[1] =
|
||||||
continue;
|
(dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j];
|
||||||
|
|
||||||
|
t = (dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j];
|
||||||
|
if (t) {
|
||||||
joydev->corr[i].coef[2] = (1 << 29) / t;
|
joydev->corr[i].coef[2] = (1 << 29) / t;
|
||||||
joydev->corr[i].coef[3] = (1 << 29) / t;
|
joydev->corr[i].coef[3] = (1 << 29) / t;
|
||||||
|
|
||||||
joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i);
|
joydev->abs[i] = joydev_correct(dev->abs[j],
|
||||||
|
joydev->corr + i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(joydev->dev.bus_id, sizeof(joydev->dev.bus_id),
|
strlcpy(joydev->dev.bus_id, joydev->name, sizeof(joydev->dev.bus_id));
|
||||||
"js%d", minor);
|
joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor);
|
||||||
joydev->dev.class = &input_class;
|
joydev->dev.class = &input_class;
|
||||||
joydev->dev.parent = &dev->dev;
|
joydev->dev.parent = &dev->dev;
|
||||||
joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor);
|
|
||||||
joydev->dev.release = joydev_free;
|
joydev->dev.release = joydev_free;
|
||||||
device_initialize(&joydev->dev);
|
device_initialize(&joydev->dev);
|
||||||
|
|
||||||
joydev_table[minor] = joydev;
|
error = input_register_handle(&joydev->handle);
|
||||||
|
|
||||||
error = device_add(&joydev->dev);
|
|
||||||
if (error)
|
if (error)
|
||||||
goto err_free_joydev;
|
goto err_free_joydev;
|
||||||
|
|
||||||
error = input_register_handle(&joydev->handle);
|
error = joydev_install_chrdev(joydev);
|
||||||
if (error)
|
if (error)
|
||||||
goto err_delete_joydev;
|
goto err_unregister_handle;
|
||||||
|
|
||||||
|
error = device_add(&joydev->dev);
|
||||||
|
if (error)
|
||||||
|
goto err_cleanup_joydev;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_delete_joydev:
|
err_cleanup_joydev:
|
||||||
device_del(&joydev->dev);
|
joydev_cleanup(joydev);
|
||||||
|
err_unregister_handle:
|
||||||
|
input_unregister_handle(&joydev->handle);
|
||||||
err_free_joydev:
|
err_free_joydev:
|
||||||
put_device(&joydev->dev);
|
put_device(&joydev->dev);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void joydev_disconnect(struct input_handle *handle)
|
static void joydev_disconnect(struct input_handle *handle)
|
||||||
{
|
{
|
||||||
struct joydev *joydev = handle->private;
|
struct joydev *joydev = handle->private;
|
||||||
struct joydev_client *client;
|
|
||||||
|
|
||||||
input_unregister_handle(handle);
|
|
||||||
device_del(&joydev->dev);
|
device_del(&joydev->dev);
|
||||||
|
joydev_cleanup(joydev);
|
||||||
joydev->exist = 0;
|
input_unregister_handle(handle);
|
||||||
|
|
||||||
if (joydev->open) {
|
|
||||||
input_close_device(handle);
|
|
||||||
list_for_each_entry(client, &joydev->client_list, node)
|
|
||||||
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
|
|
||||||
wake_up_interruptible(&joydev->wait);
|
|
||||||
}
|
|
||||||
|
|
||||||
put_device(&joydev->dev);
|
put_device(&joydev->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct input_device_id joydev_blacklist[] = {
|
static const struct input_device_id joydev_blacklist[] = {
|
||||||
{
|
{
|
||||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
|
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
|
||||||
|
INPUT_DEVICE_ID_MATCH_KEYBIT,
|
||||||
.evbit = { BIT(EV_KEY) },
|
.evbit = { BIT(EV_KEY) },
|
||||||
.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
|
.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
|
||||||
}, /* Avoid itouchpads, touchscreens and tablets */
|
}, /* Avoid itouchpads, touchscreens and tablets */
|
||||||
|
@ -614,17 +852,20 @@ static const struct input_device_id joydev_blacklist[] = {
|
||||||
|
|
||||||
static const struct input_device_id joydev_ids[] = {
|
static const struct input_device_id joydev_ids[] = {
|
||||||
{
|
{
|
||||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
|
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
|
||||||
|
INPUT_DEVICE_ID_MATCH_ABSBIT,
|
||||||
.evbit = { BIT(EV_ABS) },
|
.evbit = { BIT(EV_ABS) },
|
||||||
.absbit = { BIT(ABS_X) },
|
.absbit = { BIT(ABS_X) },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
|
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
|
||||||
|
INPUT_DEVICE_ID_MATCH_ABSBIT,
|
||||||
.evbit = { BIT(EV_ABS) },
|
.evbit = { BIT(EV_ABS) },
|
||||||
.absbit = { BIT(ABS_WHEEL) },
|
.absbit = { BIT(ABS_WHEEL) },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
|
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
|
||||||
|
INPUT_DEVICE_ID_MATCH_ABSBIT,
|
||||||
.evbit = { BIT(EV_ABS) },
|
.evbit = { BIT(EV_ABS) },
|
||||||
.absbit = { BIT(ABS_THROTTLE) },
|
.absbit = { BIT(ABS_THROTTLE) },
|
||||||
},
|
},
|
||||||
|
|
|
@ -223,12 +223,16 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
|
||||||
struct input_dev *dev = xpad->dev;
|
struct input_dev *dev = xpad->dev;
|
||||||
|
|
||||||
/* left stick */
|
/* left stick */
|
||||||
input_report_abs(dev, ABS_X, (__s16) (((__s16)data[13] << 8) | data[12]));
|
input_report_abs(dev, ABS_X,
|
||||||
input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[15] << 8) | data[14]));
|
(__s16) le16_to_cpup((__le16 *)(data + 12)));
|
||||||
|
input_report_abs(dev, ABS_Y,
|
||||||
|
(__s16) le16_to_cpup((__le16 *)(data + 14)));
|
||||||
|
|
||||||
/* right stick */
|
/* right stick */
|
||||||
input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[17] << 8) | data[16]));
|
input_report_abs(dev, ABS_RX,
|
||||||
input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[19] << 8) | data[18]));
|
(__s16) le16_to_cpup((__le16 *)(data + 16)));
|
||||||
|
input_report_abs(dev, ABS_RY,
|
||||||
|
(__s16) le16_to_cpup((__le16 *)(data + 18)));
|
||||||
|
|
||||||
/* triggers left/right */
|
/* triggers left/right */
|
||||||
input_report_abs(dev, ABS_Z, data[10]);
|
input_report_abs(dev, ABS_Z, data[10]);
|
||||||
|
@ -236,8 +240,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
|
||||||
|
|
||||||
/* digital pad */
|
/* digital pad */
|
||||||
if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) {
|
if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) {
|
||||||
input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04));
|
input_report_abs(dev, ABS_HAT0X,
|
||||||
input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01));
|
!!(data[2] & 0x08) - !!(data[2] & 0x04));
|
||||||
|
input_report_abs(dev, ABS_HAT0Y,
|
||||||
|
!!(data[2] & 0x02) - !!(data[2] & 0x01));
|
||||||
} else /* xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS */ {
|
} else /* xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS */ {
|
||||||
input_report_key(dev, BTN_LEFT, data[2] & 0x04);
|
input_report_key(dev, BTN_LEFT, data[2] & 0x04);
|
||||||
input_report_key(dev, BTN_RIGHT, data[2] & 0x08);
|
input_report_key(dev, BTN_RIGHT, data[2] & 0x08);
|
||||||
|
@ -274,14 +280,17 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
|
||||||
* http://www.free60.org/wiki/Gamepad
|
* http://www.free60.org/wiki/Gamepad
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void xpad360_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
|
static void xpad360_process_packet(struct usb_xpad *xpad,
|
||||||
|
u16 cmd, unsigned char *data)
|
||||||
{
|
{
|
||||||
struct input_dev *dev = xpad->dev;
|
struct input_dev *dev = xpad->dev;
|
||||||
|
|
||||||
/* digital pad */
|
/* digital pad */
|
||||||
if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) {
|
if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) {
|
||||||
input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04));
|
input_report_abs(dev, ABS_HAT0X,
|
||||||
input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01));
|
!!(data[2] & 0x08) - !!(data[2] & 0x04));
|
||||||
|
input_report_abs(dev, ABS_HAT0Y,
|
||||||
|
!!(data[2] & 0x02) - !!(data[2] & 0x01));
|
||||||
} else if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS) {
|
} else if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS) {
|
||||||
/* dpad as buttons (right, left, down, up) */
|
/* dpad as buttons (right, left, down, up) */
|
||||||
input_report_key(dev, BTN_LEFT, data[2] & 0x04);
|
input_report_key(dev, BTN_LEFT, data[2] & 0x04);
|
||||||
|
@ -308,12 +317,16 @@ static void xpad360_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char
|
||||||
input_report_key(dev, BTN_MODE, data[3] & 0x04);
|
input_report_key(dev, BTN_MODE, data[3] & 0x04);
|
||||||
|
|
||||||
/* left stick */
|
/* left stick */
|
||||||
input_report_abs(dev, ABS_X, (__s16) (((__s16)data[7] << 8) | (__s16)data[6]));
|
input_report_abs(dev, ABS_X,
|
||||||
input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[9] << 8) | (__s16)data[8]));
|
(__s16) le16_to_cpup((__le16 *)(data + 6)));
|
||||||
|
input_report_abs(dev, ABS_Y,
|
||||||
|
(__s16) le16_to_cpup((__le16 *)(data + 8)));
|
||||||
|
|
||||||
/* right stick */
|
/* right stick */
|
||||||
input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[11] << 8) | (__s16)data[10]));
|
input_report_abs(dev, ABS_RX,
|
||||||
input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[13] << 8) | (__s16)data[12]));
|
(__s16) le16_to_cpup((__le16 *)(data + 10)));
|
||||||
|
input_report_abs(dev, ABS_RY,
|
||||||
|
(__s16) le16_to_cpup((__le16 *)(data + 12)));
|
||||||
|
|
||||||
/* triggers left/right */
|
/* triggers left/right */
|
||||||
input_report_abs(dev, ABS_Z, data[4]);
|
input_report_abs(dev, ABS_Z, data[4]);
|
||||||
|
@ -335,10 +348,12 @@ static void xpad_irq_in(struct urb *urb)
|
||||||
case -ENOENT:
|
case -ENOENT:
|
||||||
case -ESHUTDOWN:
|
case -ESHUTDOWN:
|
||||||
/* this urb is terminated, clean up */
|
/* this urb is terminated, clean up */
|
||||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
dbg("%s - urb shutting down with status: %d",
|
||||||
|
__FUNCTION__, urb->status);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
dbg("%s - nonzero urb status received: %d",
|
||||||
|
__FUNCTION__, urb->status);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,10 +382,12 @@ static void xpad_irq_out(struct urb *urb)
|
||||||
case -ENOENT:
|
case -ENOENT:
|
||||||
case -ESHUTDOWN:
|
case -ESHUTDOWN:
|
||||||
/* this urb is terminated, clean up */
|
/* this urb is terminated, clean up */
|
||||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
dbg("%s - urb shutting down with status: %d",
|
||||||
|
__FUNCTION__, urb->status);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
dbg("%s - nonzero urb status received: %d",
|
||||||
|
__FUNCTION__, urb->status);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -595,7 +612,7 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
|
||||||
|
|
||||||
static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||||
{
|
{
|
||||||
struct usb_device *udev = interface_to_usbdev (intf);
|
struct usb_device *udev = interface_to_usbdev(intf);
|
||||||
struct usb_xpad *xpad;
|
struct usb_xpad *xpad;
|
||||||
struct input_dev *input_dev;
|
struct input_dev *input_dev;
|
||||||
struct usb_endpoint_descriptor *ep_irq_in;
|
struct usb_endpoint_descriptor *ep_irq_in;
|
||||||
|
|
|
@ -208,6 +208,27 @@ config KEYBOARD_HIL
|
||||||
This driver implements support for HIL-keyboards attached
|
This driver implements support for HIL-keyboards attached
|
||||||
to your machine, so normally you should say Y here.
|
to your machine, so normally you should say Y here.
|
||||||
|
|
||||||
|
config KEYBOARD_HP6XX
|
||||||
|
tristate "HP Jornada 6XX Keyboard support"
|
||||||
|
depends on SH_HP6XX
|
||||||
|
select INPUT_POLLDEV
|
||||||
|
help
|
||||||
|
This adds support for the onboard keyboard found on
|
||||||
|
HP Jornada 620/660/680/690.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called jornada680_kbd.
|
||||||
|
|
||||||
|
config KEYBOARD_HP7XX
|
||||||
|
tristate "HP Jornada 7XX Keyboard Driver"
|
||||||
|
depends on SA1100_JORNADA720_SSP && SA1100_SSP
|
||||||
|
help
|
||||||
|
Say Y here to add support for the HP Jornada 7xx (710/720/728)
|
||||||
|
onboard keyboard.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called jornada720_kbd.
|
||||||
|
|
||||||
config KEYBOARD_OMAP
|
config KEYBOARD_OMAP
|
||||||
tristate "TI OMAP keypad support"
|
tristate "TI OMAP keypad support"
|
||||||
depends on (ARCH_OMAP1 || ARCH_OMAP2)
|
depends on (ARCH_OMAP1 || ARCH_OMAP2)
|
||||||
|
@ -253,4 +274,23 @@ config KEYBOARD_GPIO
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called gpio-keys.
|
module will be called gpio-keys.
|
||||||
|
|
||||||
|
config KEYBOARD_MAPLE
|
||||||
|
tristate "Maple bus keyboard"
|
||||||
|
depends on SH_DREAMCAST && MAPLE
|
||||||
|
help
|
||||||
|
Say Y here if you have a Dreamcast console running Linux and have
|
||||||
|
a keyboard attached to its Maple bus.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called maple_keyb.
|
||||||
|
|
||||||
|
config KEYBOARD_BFIN
|
||||||
|
tristate "Blackfin BF54x keypad support"
|
||||||
|
depends on BF54x
|
||||||
|
help
|
||||||
|
Say Y here if you want to use the BF54x keypad.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called bf54x-keys.
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -21,4 +21,7 @@ obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
|
||||||
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keyboard.o
|
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keyboard.o
|
||||||
obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
|
obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
|
||||||
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
|
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
|
||||||
|
obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
|
||||||
|
obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
|
||||||
|
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
|
||||||
|
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
|
||||||
|
|
382
drivers/input/keyboard/bf54x-keys.c
Normal file
382
drivers/input/keyboard/bf54x-keys.c
Normal file
|
@ -0,0 +1,382 @@
|
||||||
|
/*
|
||||||
|
* File: drivers/input/keyboard/bf54x-keys.c
|
||||||
|
* Based on:
|
||||||
|
* Author: Michael Hennerich <hennerich@blackfin.uclinux.org>
|
||||||
|
*
|
||||||
|
* Created:
|
||||||
|
* Description: keypad driver for Analog Devices Blackfin BF54x Processors
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Modified:
|
||||||
|
* Copyright 2007 Analog Devices Inc.
|
||||||
|
*
|
||||||
|
* Bugs: Enter bugs at http://blackfin.uclinux.org/
|
||||||
|
*
|
||||||
|
* 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, see the file COPYING, or write
|
||||||
|
* to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/pm.h>
|
||||||
|
#include <linux/sysctl.h>
|
||||||
|
#include <linux/proc_fs.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
|
||||||
|
#include <asm/portmux.h>
|
||||||
|
#include <asm/mach/bf54x_keys.h>
|
||||||
|
|
||||||
|
#define DRV_NAME "bf54x-keys"
|
||||||
|
#define TIME_SCALE 100 /* 100 ns */
|
||||||
|
#define MAX_MULT (0xFF * TIME_SCALE)
|
||||||
|
#define MAX_RC 8 /* Max Row/Col */
|
||||||
|
|
||||||
|
static const u16 per_rows[] = {
|
||||||
|
P_KEY_ROW7,
|
||||||
|
P_KEY_ROW6,
|
||||||
|
P_KEY_ROW5,
|
||||||
|
P_KEY_ROW4,
|
||||||
|
P_KEY_ROW3,
|
||||||
|
P_KEY_ROW2,
|
||||||
|
P_KEY_ROW1,
|
||||||
|
P_KEY_ROW0,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u16 per_cols[] = {
|
||||||
|
P_KEY_COL7,
|
||||||
|
P_KEY_COL6,
|
||||||
|
P_KEY_COL5,
|
||||||
|
P_KEY_COL4,
|
||||||
|
P_KEY_COL3,
|
||||||
|
P_KEY_COL2,
|
||||||
|
P_KEY_COL1,
|
||||||
|
P_KEY_COL0,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bf54x_kpad {
|
||||||
|
struct input_dev *input;
|
||||||
|
int irq;
|
||||||
|
unsigned short lastkey;
|
||||||
|
unsigned short *keycode;
|
||||||
|
struct timer_list timer;
|
||||||
|
unsigned int keyup_test_jiffies;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int bfin_kpad_find_key(struct bf54x_kpad *bf54x_kpad,
|
||||||
|
struct input_dev *input, u16 keyident)
|
||||||
|
{
|
||||||
|
u16 i;
|
||||||
|
|
||||||
|
for (i = 0; i < input->keycodemax; i++)
|
||||||
|
if (bf54x_kpad->keycode[i + input->keycodemax] == keyident)
|
||||||
|
return bf54x_kpad->keycode[i];
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bfin_keycodecpy(unsigned short *keycode,
|
||||||
|
const unsigned int *pdata_kc,
|
||||||
|
unsigned short keymapsize)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < keymapsize; i++) {
|
||||||
|
keycode[i] = pdata_kc[i] & 0xffff;
|
||||||
|
keycode[i + keymapsize] = pdata_kc[i] >> 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u16 bfin_kpad_get_prescale(u32 timescale)
|
||||||
|
{
|
||||||
|
u32 sclk = get_sclk();
|
||||||
|
|
||||||
|
return ((((sclk / 1000) * timescale) / 1024) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u16 bfin_kpad_get_keypressed(struct bf54x_kpad *bf54x_kpad)
|
||||||
|
{
|
||||||
|
return (bfin_read_KPAD_STAT() & KPAD_PRESSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bfin_kpad_clear_irq(void)
|
||||||
|
{
|
||||||
|
bfin_write_KPAD_STAT(0xFFFF);
|
||||||
|
bfin_write_KPAD_ROWCOL(0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bfin_kpad_timer(unsigned long data)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = (struct platform_device *) data;
|
||||||
|
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
if (bfin_kpad_get_keypressed(bf54x_kpad)) {
|
||||||
|
/* Try again later */
|
||||||
|
mod_timer(&bf54x_kpad->timer,
|
||||||
|
jiffies + bf54x_kpad->keyup_test_jiffies);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_report_key(bf54x_kpad->input, bf54x_kpad->lastkey, 0);
|
||||||
|
input_sync(bf54x_kpad->input);
|
||||||
|
|
||||||
|
/* Clear IRQ Status */
|
||||||
|
|
||||||
|
bfin_kpad_clear_irq();
|
||||||
|
enable_irq(bf54x_kpad->irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t bfin_kpad_isr(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = dev_id;
|
||||||
|
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
|
||||||
|
struct input_dev *input = bf54x_kpad->input;
|
||||||
|
int key;
|
||||||
|
u16 rowcol = bfin_read_KPAD_ROWCOL();
|
||||||
|
|
||||||
|
key = bfin_kpad_find_key(bf54x_kpad, input, rowcol);
|
||||||
|
|
||||||
|
input_report_key(input, key, 1);
|
||||||
|
input_sync(input);
|
||||||
|
|
||||||
|
if (bfin_kpad_get_keypressed(bf54x_kpad)) {
|
||||||
|
disable_irq(bf54x_kpad->irq);
|
||||||
|
bf54x_kpad->lastkey = key;
|
||||||
|
mod_timer(&bf54x_kpad->timer,
|
||||||
|
jiffies + bf54x_kpad->keyup_test_jiffies);
|
||||||
|
} else {
|
||||||
|
input_report_key(input, key, 0);
|
||||||
|
input_sync(input);
|
||||||
|
|
||||||
|
bfin_kpad_clear_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit bfin_kpad_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct bf54x_kpad *bf54x_kpad;
|
||||||
|
struct bfin_kpad_platform_data *pdata = pdev->dev.platform_data;
|
||||||
|
struct input_dev *input;
|
||||||
|
int i, error;
|
||||||
|
|
||||||
|
if (!pdata->rows || !pdata->cols || !pdata->keymap) {
|
||||||
|
printk(KERN_ERR DRV_NAME
|
||||||
|
": No rows, cols or keymap from pdata\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pdata->keymapsize ||
|
||||||
|
pdata->keymapsize > (pdata->rows * pdata->cols)) {
|
||||||
|
printk(KERN_ERR DRV_NAME ": Invalid keymapsize\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bf54x_kpad = kzalloc(sizeof(struct bf54x_kpad), GFP_KERNEL);
|
||||||
|
if (!bf54x_kpad)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, bf54x_kpad);
|
||||||
|
|
||||||
|
/* Allocate memory for keymap followed by private LUT */
|
||||||
|
bf54x_kpad->keycode = kmalloc(pdata->keymapsize *
|
||||||
|
sizeof(unsigned short) * 2, GFP_KERNEL);
|
||||||
|
if (!bf54x_kpad->keycode) {
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pdata->debounce_time || !pdata->debounce_time > MAX_MULT ||
|
||||||
|
!pdata->coldrive_time || !pdata->coldrive_time > MAX_MULT) {
|
||||||
|
printk(KERN_ERR DRV_NAME
|
||||||
|
": Invalid Debounce/Columdrive Time from pdata\n");
|
||||||
|
bfin_write_KPAD_MSEL(0xFF0); /* Default MSEL */
|
||||||
|
} else {
|
||||||
|
bfin_write_KPAD_MSEL(
|
||||||
|
((pdata->debounce_time / TIME_SCALE)
|
||||||
|
& DBON_SCALE) |
|
||||||
|
(((pdata->coldrive_time / TIME_SCALE) << 8)
|
||||||
|
& COLDRV_SCALE));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pdata->keyup_test_interval)
|
||||||
|
bf54x_kpad->keyup_test_jiffies = msecs_to_jiffies(50);
|
||||||
|
else
|
||||||
|
bf54x_kpad->keyup_test_jiffies =
|
||||||
|
msecs_to_jiffies(pdata->keyup_test_interval);
|
||||||
|
|
||||||
|
if (peripheral_request_list((u16 *)&per_rows[MAX_RC - pdata->rows],
|
||||||
|
DRV_NAME)) {
|
||||||
|
printk(KERN_ERR DRV_NAME
|
||||||
|
": Requesting Peripherals failed\n");
|
||||||
|
error = -EFAULT;
|
||||||
|
goto out0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peripheral_request_list((u16 *)&per_cols[MAX_RC - pdata->cols],
|
||||||
|
DRV_NAME)) {
|
||||||
|
printk(KERN_ERR DRV_NAME
|
||||||
|
": Requesting Peripherals failed\n");
|
||||||
|
error = -EFAULT;
|
||||||
|
goto out1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bf54x_kpad->irq = platform_get_irq(pdev, 0);
|
||||||
|
if (bf54x_kpad->irq < 0) {
|
||||||
|
error = -ENODEV;
|
||||||
|
goto out2;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = request_irq(bf54x_kpad->irq, bfin_kpad_isr,
|
||||||
|
IRQF_SAMPLE_RANDOM, DRV_NAME, pdev);
|
||||||
|
if (error) {
|
||||||
|
printk(KERN_ERR DRV_NAME
|
||||||
|
": unable to claim irq %d; error %d\n",
|
||||||
|
bf54x_kpad->irq, error);
|
||||||
|
error = -EBUSY;
|
||||||
|
goto out2;
|
||||||
|
}
|
||||||
|
|
||||||
|
input = input_allocate_device();
|
||||||
|
if (!input) {
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto out3;
|
||||||
|
}
|
||||||
|
|
||||||
|
bf54x_kpad->input = input;
|
||||||
|
|
||||||
|
input->name = pdev->name;
|
||||||
|
input->phys = "bf54x-keys/input0";
|
||||||
|
input->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
|
input_set_drvdata(input, bf54x_kpad);
|
||||||
|
|
||||||
|
input->id.bustype = BUS_HOST;
|
||||||
|
input->id.vendor = 0x0001;
|
||||||
|
input->id.product = 0x0001;
|
||||||
|
input->id.version = 0x0100;
|
||||||
|
|
||||||
|
input->keycodesize = sizeof(unsigned short);
|
||||||
|
input->keycodemax = pdata->keymapsize;
|
||||||
|
input->keycode = bf54x_kpad->keycode;
|
||||||
|
|
||||||
|
bfin_keycodecpy(bf54x_kpad->keycode, pdata->keymap, pdata->keymapsize);
|
||||||
|
|
||||||
|
/* setup input device */
|
||||||
|
__set_bit(EV_KEY, input->evbit);
|
||||||
|
|
||||||
|
if (pdata->repeat)
|
||||||
|
__set_bit(EV_REP, input->evbit);
|
||||||
|
|
||||||
|
for (i = 0; i < input->keycodemax; i++)
|
||||||
|
__set_bit(bf54x_kpad->keycode[i] & KEY_MAX, input->keybit);
|
||||||
|
__clear_bit(KEY_RESERVED, input->keybit);
|
||||||
|
|
||||||
|
error = input_register_device(input);
|
||||||
|
if (error) {
|
||||||
|
printk(KERN_ERR DRV_NAME
|
||||||
|
": Unable to register input device (%d)\n", error);
|
||||||
|
goto out4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Init Keypad Key Up/Release test timer */
|
||||||
|
|
||||||
|
setup_timer(&bf54x_kpad->timer, bfin_kpad_timer, (unsigned long) pdev);
|
||||||
|
|
||||||
|
bfin_write_KPAD_PRESCALE(bfin_kpad_get_prescale(TIME_SCALE));
|
||||||
|
|
||||||
|
bfin_write_KPAD_CTL((((pdata->cols - 1) << 13) & KPAD_COLEN) |
|
||||||
|
(((pdata->rows - 1) << 10) & KPAD_ROWEN) |
|
||||||
|
(2 & KPAD_IRQMODE));
|
||||||
|
|
||||||
|
bfin_write_KPAD_CTL(bfin_read_KPAD_CTL() | KPAD_EN);
|
||||||
|
|
||||||
|
printk(KERN_ERR DRV_NAME
|
||||||
|
": Blackfin BF54x Keypad registered IRQ %d\n", bf54x_kpad->irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out4:
|
||||||
|
input_free_device(input);
|
||||||
|
out3:
|
||||||
|
free_irq(bf54x_kpad->irq, pdev);
|
||||||
|
out2:
|
||||||
|
peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
|
||||||
|
out1:
|
||||||
|
peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
|
||||||
|
out0:
|
||||||
|
kfree(bf54x_kpad->keycode);
|
||||||
|
out:
|
||||||
|
kfree(bf54x_kpad);
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit bfin_kpad_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct bfin_kpad_platform_data *pdata = pdev->dev.platform_data;
|
||||||
|
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
del_timer_sync(&bf54x_kpad->timer);
|
||||||
|
free_irq(bf54x_kpad->irq, pdev);
|
||||||
|
|
||||||
|
input_unregister_device(bf54x_kpad->input);
|
||||||
|
|
||||||
|
peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
|
||||||
|
peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
|
||||||
|
|
||||||
|
kfree(bf54x_kpad->keycode);
|
||||||
|
kfree(bf54x_kpad);
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct platform_driver bfin_kpad_device_driver = {
|
||||||
|
.probe = bfin_kpad_probe,
|
||||||
|
.remove = __devexit_p(bfin_kpad_remove),
|
||||||
|
.driver = {
|
||||||
|
.name = DRV_NAME,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init bfin_kpad_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&bfin_kpad_device_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit bfin_kpad_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&bfin_kpad_device_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(bfin_kpad_init);
|
||||||
|
module_exit(bfin_kpad_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||||
|
MODULE_DESCRIPTION("Keypad driver for BF54x Processors");
|
|
@ -54,6 +54,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
|
||||||
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
|
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
|
||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
int i, error;
|
int i, error;
|
||||||
|
int wakeup = 0;
|
||||||
|
|
||||||
input = input_allocate_device();
|
input = input_allocate_device();
|
||||||
if (!input)
|
if (!input)
|
||||||
|
@ -77,31 +78,51 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
|
||||||
int irq = gpio_to_irq(button->gpio);
|
int irq = gpio_to_irq(button->gpio);
|
||||||
unsigned int type = button->type ?: EV_KEY;
|
unsigned int type = button->type ?: EV_KEY;
|
||||||
|
|
||||||
set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
|
if (irq < 0) {
|
||||||
error = request_irq(irq, gpio_keys_isr, IRQF_SAMPLE_RANDOM,
|
error = irq;
|
||||||
|
printk(KERN_ERR
|
||||||
|
"gpio-keys: "
|
||||||
|
"Unable to get irq number for GPIO %d,"
|
||||||
|
"error %d\n",
|
||||||
|
button->gpio, error);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = request_irq(irq, gpio_keys_isr,
|
||||||
|
IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING |
|
||||||
|
IRQF_TRIGGER_FALLING,
|
||||||
button->desc ? button->desc : "gpio_keys",
|
button->desc ? button->desc : "gpio_keys",
|
||||||
pdev);
|
pdev);
|
||||||
if (error) {
|
if (error) {
|
||||||
printk(KERN_ERR "gpio-keys: unable to claim irq %d; error %d\n",
|
printk(KERN_ERR
|
||||||
|
"gpio-keys: Unable to claim irq %d; error %d\n",
|
||||||
irq, error);
|
irq, error);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (button->wakeup)
|
||||||
|
wakeup = 1;
|
||||||
|
|
||||||
input_set_capability(input, type, button->code);
|
input_set_capability(input, type, button->code);
|
||||||
}
|
}
|
||||||
|
|
||||||
error = input_register_device(input);
|
error = input_register_device(input);
|
||||||
if (error) {
|
if (error) {
|
||||||
printk(KERN_ERR "Unable to register gpio-keys input device\n");
|
printk(KERN_ERR
|
||||||
|
"gpio-keys: Unable to register input device, "
|
||||||
|
"error: %d\n", error);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
device_init_wakeup(&pdev->dev, wakeup);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
for (i = i - 1; i >= 0; i--)
|
while (--i >= 0)
|
||||||
free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev);
|
free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
input_free_device(input);
|
input_free_device(input);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
|
@ -113,6 +134,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
|
||||||
struct input_dev *input = platform_get_drvdata(pdev);
|
struct input_dev *input = platform_get_drvdata(pdev);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
device_init_wakeup(&pdev->dev, 0);
|
||||||
|
|
||||||
for (i = 0; i < pdata->nbuttons; i++) {
|
for (i = 0; i < pdata->nbuttons; i++) {
|
||||||
int irq = gpio_to_irq(pdata->buttons[i].gpio);
|
int irq = gpio_to_irq(pdata->buttons[i].gpio);
|
||||||
free_irq(irq, pdev);
|
free_irq(irq, pdev);
|
||||||
|
@ -123,9 +146,53 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int gpio_keys_suspend(struct platform_device *pdev, pm_message_t state)
|
||||||
|
{
|
||||||
|
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (device_may_wakeup(&pdev->dev)) {
|
||||||
|
for (i = 0; i < pdata->nbuttons; i++) {
|
||||||
|
struct gpio_keys_button *button = &pdata->buttons[i];
|
||||||
|
if (button->wakeup) {
|
||||||
|
int irq = gpio_to_irq(button->gpio);
|
||||||
|
enable_irq_wake(irq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_keys_resume(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (device_may_wakeup(&pdev->dev)) {
|
||||||
|
for (i = 0; i < pdata->nbuttons; i++) {
|
||||||
|
struct gpio_keys_button *button = &pdata->buttons[i];
|
||||||
|
if (button->wakeup) {
|
||||||
|
int irq = gpio_to_irq(button->gpio);
|
||||||
|
disable_irq_wake(irq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define gpio_keys_suspend NULL
|
||||||
|
#define gpio_keys_resume NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
struct platform_driver gpio_keys_device_driver = {
|
struct platform_driver gpio_keys_device_driver = {
|
||||||
.probe = gpio_keys_probe,
|
.probe = gpio_keys_probe,
|
||||||
.remove = __devexit_p(gpio_keys_remove),
|
.remove = __devexit_p(gpio_keys_remove),
|
||||||
|
.suspend = gpio_keys_suspend,
|
||||||
|
.resume = gpio_keys_resume,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "gpio-keys",
|
.name = "gpio-keys",
|
||||||
}
|
}
|
||||||
|
|
277
drivers/input/keyboard/jornada680_kbd.c
Normal file
277
drivers/input/keyboard/jornada680_kbd.c
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
/*
|
||||||
|
* drivers/input/keyboard/jornada680_kbd.c
|
||||||
|
*
|
||||||
|
* HP Jornada 620/660/680/690 scan keyboard platform driver
|
||||||
|
* Copyright (C) 2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com>
|
||||||
|
*
|
||||||
|
* Based on hp680_keyb.c
|
||||||
|
* Copyright (C) 2006 Paul Mundt
|
||||||
|
* Copyright (C) 2005 Andriy Skulysh
|
||||||
|
* Split from drivers/input/keyboard/hp600_keyb.c
|
||||||
|
* Copyright (C) 2000 Yaegashi Takeshi (hp6xx kbd scan routine and translation table)
|
||||||
|
* Copyright (C) 2000 Niibe Yutaka (HP620 Keyb translation table)
|
||||||
|
*
|
||||||
|
* 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 <linux/input.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/input-polldev.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
|
||||||
|
#include <asm/delay.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
|
||||||
|
#define PCCR 0xa4000104
|
||||||
|
#define PDCR 0xa4000106
|
||||||
|
#define PECR 0xa4000108
|
||||||
|
#define PFCR 0xa400010a
|
||||||
|
#define PCDR 0xa4000124
|
||||||
|
#define PDDR 0xa4000126
|
||||||
|
#define PEDR 0xa4000128
|
||||||
|
#define PFDR 0xa400012a
|
||||||
|
#define PGDR 0xa400012c
|
||||||
|
#define PHDR 0xa400012e
|
||||||
|
#define PJDR 0xa4000130
|
||||||
|
#define PKDR 0xa4000132
|
||||||
|
#define PLDR 0xa4000134
|
||||||
|
|
||||||
|
static const unsigned short jornada_scancodes[] = {
|
||||||
|
/* PTD1 */ KEY_CAPSLOCK, KEY_MACRO, KEY_LEFTCTRL, 0, KEY_ESC, 0, 0, 0, /* 1 -> 8 */
|
||||||
|
KEY_F1, KEY_F2, KEY_F3, KEY_F8, KEY_F7, KEY_F2, KEY_F4, KEY_F5, /* 9 -> 16 */
|
||||||
|
/* PTD5 */ KEY_SLASH, KEY_APOSTROPHE, KEY_ENTER, 0, KEY_Z, 0, 0, 0, /* 17 -> 24 */
|
||||||
|
KEY_X, KEY_C, KEY_V, KEY_DOT, KEY_COMMA, KEY_M, KEY_B, KEY_N, /* 25 -> 32 */
|
||||||
|
/* PTD7 */ KEY_KP2, KEY_KP6, 0, 0, 0, 0, 0, 0, /* 33 -> 40 */
|
||||||
|
0, 0, 0, KEY_KP4, 0, 0, KEY_LEFTALT, KEY_HANJA, /* 41 -> 48 */
|
||||||
|
/* PTE0 */ 0, 0, 0, 0, KEY_FINANCE, 0, 0, 0, /* 49 -> 56 */
|
||||||
|
KEY_LEFTCTRL, 0, KEY_SPACE, KEY_KPDOT, KEY_VOLUMEUP, 249, 0, 0, /* 57 -> 64 */
|
||||||
|
/* PTE1 */ KEY_SEMICOLON, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, KEY_A, 0, 0, 0,/* 65 -> 72 */
|
||||||
|
KEY_S, KEY_D, KEY_F, KEY_L, KEY_K, KEY_J, KEY_G, KEY_H, /* 73 -> 80 */
|
||||||
|
/* PTE3 */ KEY_KP8, KEY_LEFTMETA, KEY_RIGHTSHIFT, 0, KEY_TAB, 0, 0,0, /* 81 -> 88 */
|
||||||
|
0, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, 0, /* 89 -> 96 */
|
||||||
|
/* PTE6 */ KEY_P, KEY_LEFTBRACE, KEY_BACKSPACE, 0, KEY_Q, 0, 0, 0, /* 97 -> 104 */
|
||||||
|
KEY_W, KEY_E, KEY_R, KEY_O, KEY_I, KEY_U, KEY_T, KEY_R, /* 105 -> 112 */
|
||||||
|
/* PTE7 */ KEY_0, KEY_MINUS, KEY_EQUAL, 0, KEY_1, 0, 0, 0, /* 113 -> 120 */
|
||||||
|
KEY_2, KEY_3, KEY_4, KEY_9, KEY_8, KEY_7, KEY_5, KEY_6, /* 121 -> 128 */
|
||||||
|
/* **** */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
#define JORNADA_SCAN_SIZE 18
|
||||||
|
|
||||||
|
struct jornadakbd {
|
||||||
|
struct input_polled_dev *poll_dev;
|
||||||
|
unsigned short keymap[ARRAY_SIZE(jornada_scancodes)];
|
||||||
|
unsigned char length;
|
||||||
|
unsigned char old_scan[JORNADA_SCAN_SIZE];
|
||||||
|
unsigned char new_scan[JORNADA_SCAN_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
static void jornada_parse_kbd(struct jornadakbd *jornadakbd)
|
||||||
|
{
|
||||||
|
struct input_dev *input_dev = jornadakbd->poll_dev->input;
|
||||||
|
unsigned short *keymap = jornadakbd->keymap;
|
||||||
|
unsigned int sync_me = 0;
|
||||||
|
unsigned int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < JORNADA_SCAN_SIZE; i++) {
|
||||||
|
unsigned char new = jornadakbd->new_scan[i];
|
||||||
|
unsigned char old = jornadakbd->old_scan[i];
|
||||||
|
unsigned int xor = new ^ old;
|
||||||
|
|
||||||
|
if (xor == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
unsigned int bit = 1 << j;
|
||||||
|
if (xor & bit) {
|
||||||
|
unsigned int scancode = (i << 3) + j;
|
||||||
|
input_event(input_dev,
|
||||||
|
EV_MSC, MSC_SCAN, scancode);
|
||||||
|
input_report_key(input_dev,
|
||||||
|
keymap[scancode],
|
||||||
|
!(new & bit));
|
||||||
|
sync_me = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sync_me)
|
||||||
|
input_sync(input_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jornada_scan_keyb(unsigned char *s)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned short ec_static, dc_static; /* = UINT16_t */
|
||||||
|
unsigned char matrix_switch[] = {
|
||||||
|
0xfd, 0xff, /* PTD1 PD(1) */
|
||||||
|
0xdf, 0xff, /* PTD5 PD(5) */
|
||||||
|
0x7f, 0xff, /* PTD7 PD(7) */
|
||||||
|
0xff, 0xfe, /* PTE0 PE(0) */
|
||||||
|
0xff, 0xfd, /* PTE1 PE(1) */
|
||||||
|
0xff, 0xf7, /* PTE3 PE(3) */
|
||||||
|
0xff, 0xbf, /* PTE6 PE(6) */
|
||||||
|
0xff, 0x7f, /* PTE7 PE(7) */
|
||||||
|
}, *t = matrix_switch;
|
||||||
|
/* PD(x) :
|
||||||
|
1. 0xcc0c & (1~(1 << (2*(x)+1)))))
|
||||||
|
2. (0xf0cf & 0xfffff) */
|
||||||
|
/* PE(x) :
|
||||||
|
1. 0xcc0c & 0xffff
|
||||||
|
2. 0xf0cf & (1~(1 << (2*(x)+1))))) */
|
||||||
|
unsigned short matrix_PDE[] = {
|
||||||
|
0xcc04, 0xf0cf, /* PD(1) */
|
||||||
|
0xc40c, 0xf0cf, /* PD(5) */
|
||||||
|
0x4c0c, 0xf0cf, /* PD(7) */
|
||||||
|
0xcc0c, 0xf0cd, /* PE(0) */
|
||||||
|
0xcc0c, 0xf0c7, /* PE(1) */
|
||||||
|
0xcc0c, 0xf04f, /* PE(3) */
|
||||||
|
0xcc0c, 0xd0cf, /* PE(6) */
|
||||||
|
0xcc0c, 0x70cf, /* PE(7) */
|
||||||
|
}, *y = matrix_PDE;
|
||||||
|
|
||||||
|
/* Save these control reg bits */
|
||||||
|
dc_static = (ctrl_inw(PDCR) & (~0xcc0c));
|
||||||
|
ec_static = (ctrl_inw(PECR) & (~0xf0cf));
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
/* disable output for all but the one we want to scan */
|
||||||
|
ctrl_outw((dc_static | *y++), PDCR);
|
||||||
|
ctrl_outw((ec_static | *y++), PECR);
|
||||||
|
udelay(5);
|
||||||
|
|
||||||
|
/* Get scanline row */
|
||||||
|
ctrl_outb(*t++, PDDR);
|
||||||
|
ctrl_outb(*t++, PEDR);
|
||||||
|
udelay(50);
|
||||||
|
|
||||||
|
/* Read data */
|
||||||
|
*s++ = ctrl_inb(PCDR);
|
||||||
|
*s++ = ctrl_inb(PFDR);
|
||||||
|
}
|
||||||
|
/* Scan no lines */
|
||||||
|
ctrl_outb(0xff, PDDR);
|
||||||
|
ctrl_outb(0xff, PEDR);
|
||||||
|
|
||||||
|
/* Enable all scanlines */
|
||||||
|
ctrl_outw((dc_static | (0x5555 & 0xcc0c)),PDCR);
|
||||||
|
ctrl_outw((ec_static | (0x5555 & 0xf0cf)),PECR);
|
||||||
|
|
||||||
|
/* Ignore extra keys and events */
|
||||||
|
*s++ = ctrl_inb(PGDR);
|
||||||
|
*s++ = ctrl_inb(PHDR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jornadakbd680_poll(struct input_polled_dev *dev)
|
||||||
|
{
|
||||||
|
struct jornadakbd *jornadakbd = dev->private;
|
||||||
|
|
||||||
|
jornada_scan_keyb(jornadakbd->new_scan);
|
||||||
|
jornada_parse_kbd(jornadakbd);
|
||||||
|
memcpy(jornadakbd->old_scan, jornadakbd->new_scan, JORNADA_SCAN_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit jornada680kbd_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct jornadakbd *jornadakbd;
|
||||||
|
struct input_polled_dev *poll_dev;
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
int i, error;
|
||||||
|
|
||||||
|
jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL);
|
||||||
|
if (!jornadakbd)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
poll_dev = input_allocate_polled_device();
|
||||||
|
if (!poll_dev) {
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, jornadakbd);
|
||||||
|
|
||||||
|
jornadakbd->poll_dev = poll_dev;
|
||||||
|
|
||||||
|
memcpy(jornadakbd->keymap, jornada_scancodes,
|
||||||
|
sizeof(jornadakbd->keymap));
|
||||||
|
|
||||||
|
poll_dev->private = jornadakbd;
|
||||||
|
poll_dev->poll = jornadakbd680_poll;
|
||||||
|
poll_dev->poll_interval = 50; /* msec */
|
||||||
|
|
||||||
|
input_dev = poll_dev->input;
|
||||||
|
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
|
||||||
|
input_dev->name = "HP Jornada 680 keyboard";
|
||||||
|
input_dev->phys = "jornadakbd/input0";
|
||||||
|
input_dev->keycode = jornadakbd->keymap;
|
||||||
|
input_dev->keycodesize = sizeof(unsigned short);
|
||||||
|
input_dev->keycodemax = ARRAY_SIZE(jornada_scancodes);
|
||||||
|
input_dev->dev.parent = &pdev->dev;
|
||||||
|
input_dev->id.bustype = BUS_HOST;
|
||||||
|
|
||||||
|
for (i = 0; i < 128; i++)
|
||||||
|
if (jornadakbd->keymap[i])
|
||||||
|
__set_bit(jornadakbd->keymap[i], input_dev->keybit);
|
||||||
|
__clear_bit(KEY_RESERVED, input_dev->keybit);
|
||||||
|
|
||||||
|
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||||
|
|
||||||
|
error = input_register_polled_device(jornadakbd->poll_dev);
|
||||||
|
if (error)
|
||||||
|
goto failed;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
failed:
|
||||||
|
printk(KERN_ERR "Jornadakbd: failed to register driver, error: %d\n",
|
||||||
|
error);
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
input_free_polled_device(poll_dev);
|
||||||
|
kfree(jornadakbd);
|
||||||
|
return error;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit jornada680kbd_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct jornadakbd *jornadakbd = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
input_unregister_polled_device(jornadakbd->poll_dev);
|
||||||
|
input_free_polled_device(jornadakbd->poll_dev);
|
||||||
|
kfree(jornadakbd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver jornada680kbd_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "jornada680_kbd",
|
||||||
|
},
|
||||||
|
.probe = jornada680kbd_probe,
|
||||||
|
.remove = __devexit_p(jornada680kbd_remove),
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init jornada680kbd_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&jornada680kbd_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit jornada680kbd_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&jornada680kbd_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(jornada680kbd_init);
|
||||||
|
module_exit(jornada680kbd_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("HP Jornada 620/660/680/690 Keyboard Driver");
|
||||||
|
MODULE_LICENSE("GPLv2");
|
185
drivers/input/keyboard/jornada720_kbd.c
Normal file
185
drivers/input/keyboard/jornada720_kbd.c
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
/*
|
||||||
|
* drivers/input/keyboard/jornada720_kbd.c
|
||||||
|
*
|
||||||
|
* HP Jornada 720 keyboard platform driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006/2007 Kristoffer Ericson <Kristoffer.Ericson@Gmail.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006 jornada 720 kbd driver by
|
||||||
|
Filip Zyzniewsk <Filip.Zyzniewski@tefnet.plX
|
||||||
|
* based on (C) 2004 jornada 720 kbd driver by
|
||||||
|
Alex Lange <chicken@handhelds.org>
|
||||||
|
*
|
||||||
|
* 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 <linux/device.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
#include <asm/arch/jornada720.h>
|
||||||
|
#include <asm/hardware.h>
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver");
|
||||||
|
MODULE_LICENSE("GPLv2");
|
||||||
|
|
||||||
|
static unsigned short jornada_std_keymap[128] = { /* ROW */
|
||||||
|
0, KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, /* #1 */
|
||||||
|
KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE, /* -> */
|
||||||
|
0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, /* #2 */
|
||||||
|
KEY_0, KEY_MINUS, KEY_EQUAL,0, 0, 0, /* -> */
|
||||||
|
0, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, /* #3 */
|
||||||
|
KEY_P, KEY_BACKSLASH, KEY_BACKSPACE, 0, 0, 0, /* -> */
|
||||||
|
0, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, /* #4 */
|
||||||
|
KEY_SEMICOLON, KEY_LEFTBRACE, KEY_RIGHTBRACE, 0, 0, 0, /* -> */
|
||||||
|
0, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, KEY_COMMA, /* #5 */
|
||||||
|
KEY_DOT, KEY_KPMINUS, KEY_APOSTROPHE, KEY_ENTER, 0, 0,0, /* -> */
|
||||||
|
0, KEY_TAB, 0, KEY_LEFTSHIFT, 0, KEY_APOSTROPHE, 0, 0, 0, 0, /* #6 */
|
||||||
|
KEY_UP, 0, KEY_RIGHTSHIFT, 0, 0, 0,0, 0, 0, 0, 0, KEY_LEFTALT, KEY_GRAVE, /* -> */
|
||||||
|
0, 0, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0,0, KEY_KPASTERISK, /* -> */
|
||||||
|
KEY_LEFTCTRL, 0, KEY_SPACE, 0, 0, 0, KEY_SLASH, KEY_DELETE, 0, 0, /* -> */
|
||||||
|
0, 0, 0, KEY_POWER, /* -> */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct jornadakbd {
|
||||||
|
unsigned short keymap[ARRAY_SIZE(jornada_std_keymap)];
|
||||||
|
struct input_dev *input;
|
||||||
|
};
|
||||||
|
|
||||||
|
static irqreturn_t jornada720_kbd_interrupt(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = dev_id;
|
||||||
|
struct jornadakbd *jornadakbd = platform_get_drvdata(pdev);
|
||||||
|
struct input_dev *input = jornadakbd->input;
|
||||||
|
u8 count, kbd_data, scan_code;
|
||||||
|
|
||||||
|
/* startup ssp with spinlock */
|
||||||
|
jornada_ssp_start();
|
||||||
|
|
||||||
|
if (jornada_ssp_inout(GETSCANKEYCODE) != TXDUMMY) {
|
||||||
|
printk(KERN_DEBUG
|
||||||
|
"jornada720_kbd: "
|
||||||
|
"GetKeycode command failed with ETIMEDOUT, "
|
||||||
|
"flushed bus\n");
|
||||||
|
} else {
|
||||||
|
/* How many keycodes are waiting for us? */
|
||||||
|
count = jornada_ssp_byte(TXDUMMY);
|
||||||
|
|
||||||
|
/* Lets drag them out one at a time */
|
||||||
|
while (count--) {
|
||||||
|
/* Exchange TxDummy for location (keymap[kbddata]) */
|
||||||
|
kbd_data = jornada_ssp_byte(TXDUMMY);
|
||||||
|
scan_code = kbd_data & 0x7f;
|
||||||
|
|
||||||
|
input_event(input, EV_MSC, MSC_SCAN, scan_code);
|
||||||
|
input_report_key(input, jornadakbd->keymap[scan_code],
|
||||||
|
!(kbd_data & 0x80));
|
||||||
|
input_sync(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* release spinlock and turn off ssp */
|
||||||
|
jornada_ssp_end();
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __devinit jornada720_kbd_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct jornadakbd *jornadakbd;
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL);
|
||||||
|
input_dev = input_allocate_device();
|
||||||
|
if (!jornadakbd || !input_dev) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, jornadakbd);
|
||||||
|
|
||||||
|
memcpy(jornadakbd->keymap, jornada_std_keymap,
|
||||||
|
sizeof(jornada_std_keymap));
|
||||||
|
jornadakbd->input = input_dev;
|
||||||
|
|
||||||
|
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
|
||||||
|
input_dev->name = "HP Jornada 720 keyboard";
|
||||||
|
input_dev->phys = "jornadakbd/input0";
|
||||||
|
input_dev->keycode = jornadakbd->keymap;
|
||||||
|
input_dev->keycodesize = sizeof(unsigned short);
|
||||||
|
input_dev->keycodemax = ARRAY_SIZE(jornada_std_keymap);
|
||||||
|
input_dev->id.bustype = BUS_HOST;
|
||||||
|
input_dev->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(jornadakbd->keymap); i++)
|
||||||
|
__set_bit(jornadakbd->keymap[i], input_dev->keybit);
|
||||||
|
__clear_bit(KEY_RESERVED, input_dev->keybit);
|
||||||
|
|
||||||
|
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||||
|
|
||||||
|
err = request_irq(IRQ_GPIO0,
|
||||||
|
jornada720_kbd_interrupt,
|
||||||
|
IRQF_DISABLED | IRQF_TRIGGER_FALLING,
|
||||||
|
"jornadakbd", pdev);
|
||||||
|
if (err) {
|
||||||
|
printk(KERN_INFO "jornadakbd720_kbd: Unable to grab IRQ\n");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = input_register_device(jornadakbd->input);
|
||||||
|
if (err)
|
||||||
|
goto fail2;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail2: /* IRQ, DEVICE, MEMORY */
|
||||||
|
free_irq(IRQ_GPIO0, pdev);
|
||||||
|
fail1: /* DEVICE, MEMORY */
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
input_free_device(input_dev);
|
||||||
|
kfree(jornadakbd);
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __devexit jornada720_kbd_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct jornadakbd *jornadakbd = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
free_irq(IRQ_GPIO0, pdev);
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
input_unregister_device(jornadakbd->input);
|
||||||
|
kfree(jornadakbd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver jornada720_kbd_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "jornada720_kbd",
|
||||||
|
},
|
||||||
|
.probe = jornada720_kbd_probe,
|
||||||
|
.remove = __devexit_p(jornada720_kbd_remove),
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init jornada720_kbd_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&jornada720_kbd_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit jornada720_kbd_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&jornada720_kbd_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(jornada720_kbd_init);
|
||||||
|
module_exit(jornada720_kbd_exit);
|
252
drivers/input/keyboard/maple_keyb.c
Normal file
252
drivers/input/keyboard/maple_keyb.c
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
/*
|
||||||
|
* SEGA Dreamcast keyboard driver
|
||||||
|
* Based on drivers/usb/usbkbd.c
|
||||||
|
* Copyright YAEGASHI Takeshi, 2001
|
||||||
|
* Porting to 2.6 Copyright Adrian McMenamin, 2007
|
||||||
|
*
|
||||||
|
* 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, see the file COPYING, or write
|
||||||
|
* to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/timer.h>
|
||||||
|
#include <linux/maple.h>
|
||||||
|
#include <asm/mach/maple.h>
|
||||||
|
|
||||||
|
/* Very simple mutex to ensure proper cleanup */
|
||||||
|
static DEFINE_MUTEX(maple_keyb_mutex);
|
||||||
|
|
||||||
|
#define NR_SCANCODES 256
|
||||||
|
|
||||||
|
MODULE_AUTHOR("YAEGASHI Takeshi, Adrian McMenamin");
|
||||||
|
MODULE_DESCRIPTION("SEGA Dreamcast keyboard driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
struct dc_kbd {
|
||||||
|
struct input_dev *dev;
|
||||||
|
unsigned short keycode[NR_SCANCODES];
|
||||||
|
unsigned char new[8];
|
||||||
|
unsigned char old[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned short dc_kbd_keycode[NR_SCANCODES] = {
|
||||||
|
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_A, KEY_B, KEY_C, KEY_D,
|
||||||
|
KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L,
|
||||||
|
KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T,
|
||||||
|
KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_1, KEY_2,
|
||||||
|
KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0,
|
||||||
|
KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB, KEY_SPACE, KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE,
|
||||||
|
KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_BACKSLASH, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA,
|
||||||
|
KEY_DOT, KEY_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6,
|
||||||
|
KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_SYSRQ,
|
||||||
|
KEY_SCROLLLOCK, KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_PAGEUP, KEY_DELETE,
|
||||||
|
KEY_END, KEY_PAGEDOWN, KEY_RIGHT, KEY_LEFT, KEY_DOWN, KEY_UP,
|
||||||
|
KEY_NUMLOCK, KEY_KPSLASH, KEY_KPASTERISK, KEY_KPMINUS, KEY_KPPLUS, KEY_KPENTER, KEY_KP1, KEY_KP2,
|
||||||
|
KEY_KP3, KEY_KP4, KEY_KP5, KEY_KP6, KEY_KP7, KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT,
|
||||||
|
KEY_102ND, KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL, KEY_F13, KEY_F14, KEY_F15,
|
||||||
|
KEY_F16, KEY_F17, KEY_F18, KEY_F19, KEY_F20,
|
||||||
|
KEY_F21, KEY_F22, KEY_F23, KEY_F24, KEY_OPEN, KEY_HELP, KEY_PROPS, KEY_FRONT,
|
||||||
|
KEY_STOP, KEY_AGAIN, KEY_UNDO, KEY_CUT, KEY_COPY, KEY_PASTE, KEY_FIND, KEY_MUTE,
|
||||||
|
KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_KPCOMMA, KEY_RESERVED, KEY_RO, KEY_KATAKANAHIRAGANA , KEY_YEN,
|
||||||
|
KEY_HENKAN, KEY_MUHENKAN, KEY_KPJPCOMMA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||||
|
KEY_HANGEUL, KEY_HANJA, KEY_KATAKANA, KEY_HIRAGANA, KEY_ZENKAKUHANKAKU, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||||
|
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||||
|
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||||
|
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||||
|
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||||
|
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||||
|
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||||
|
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||||
|
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||||
|
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||||
|
KEY_LEFTCTRL, KEY_LEFTSHIFT, KEY_LEFTALT, KEY_LEFTMETA, KEY_RIGHTCTRL, KEY_RIGHTSHIFT, KEY_RIGHTALT, KEY_RIGHTMETA,
|
||||||
|
KEY_PLAYPAUSE, KEY_STOPCD, KEY_PREVIOUSSONG, KEY_NEXTSONG, KEY_EJECTCD, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE,
|
||||||
|
KEY_WWW, KEY_BACK, KEY_FORWARD, KEY_STOP, KEY_FIND, KEY_SCROLLUP, KEY_SCROLLDOWN, KEY_EDIT, KEY_SLEEP,
|
||||||
|
KEY_SCREENLOCK, KEY_REFRESH, KEY_CALC, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED
|
||||||
|
};
|
||||||
|
|
||||||
|
static void dc_scan_kbd(struct dc_kbd *kbd)
|
||||||
|
{
|
||||||
|
struct input_dev *dev = kbd->dev;
|
||||||
|
void *ptr;
|
||||||
|
int code, keycode;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
code = i + 224;
|
||||||
|
keycode = kbd->keycode[code];
|
||||||
|
input_event(dev, EV_MSC, MSC_SCAN, code);
|
||||||
|
input_report_key(dev, keycode, (kbd->new[0] >> i) & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 2; i < 8; i++) {
|
||||||
|
ptr = memchr(kbd->new + 2, kbd->old[i], 6);
|
||||||
|
code = kbd->old[i];
|
||||||
|
if (code > 3 && ptr == NULL) {
|
||||||
|
keycode = kbd->keycode[code];
|
||||||
|
if (keycode) {
|
||||||
|
input_event(dev, EV_MSC, MSC_SCAN, code);
|
||||||
|
input_report_key(dev, keycode, 0);
|
||||||
|
} else
|
||||||
|
printk(KERN_DEBUG "maple_keyb: "
|
||||||
|
"Unknown key (scancode %#x) released.",
|
||||||
|
code);
|
||||||
|
}
|
||||||
|
ptr = memchr(kbd->old + 2, kbd->new[i], 6);
|
||||||
|
code = kbd->new[i];
|
||||||
|
if (code > 3 && ptr) {
|
||||||
|
keycode = kbd->keycode[code];
|
||||||
|
if (keycode) {
|
||||||
|
input_event(dev, EV_MSC, MSC_SCAN, code);
|
||||||
|
input_report_key(dev, keycode, 1);
|
||||||
|
} else
|
||||||
|
printk(KERN_DEBUG "maple_keyb: "
|
||||||
|
"Unknown key (scancode %#x) pressed.",
|
||||||
|
code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input_sync(dev);
|
||||||
|
memcpy(kbd->old, kbd->new, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dc_kbd_callback(struct mapleq *mq)
|
||||||
|
{
|
||||||
|
struct maple_device *mapledev = mq->dev;
|
||||||
|
struct dc_kbd *kbd = mapledev->private_data;
|
||||||
|
unsigned long *buf = mq->recvbuf;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We should always be getting the lock because the only
|
||||||
|
* time it may be locked if driver is in cleanup phase.
|
||||||
|
*/
|
||||||
|
if (likely(mutex_trylock(&maple_keyb_mutex))) {
|
||||||
|
|
||||||
|
if (buf[1] == mapledev->function) {
|
||||||
|
memcpy(kbd->new, buf + 2, 8);
|
||||||
|
dc_scan_kbd(kbd);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&maple_keyb_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dc_kbd_connect(struct maple_device *mdev)
|
||||||
|
{
|
||||||
|
int i, error;
|
||||||
|
struct dc_kbd *kbd;
|
||||||
|
struct input_dev *dev;
|
||||||
|
|
||||||
|
if (!(mdev->function & MAPLE_FUNC_KEYBOARD))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
kbd = kzalloc(sizeof(struct dc_kbd), GFP_KERNEL);
|
||||||
|
dev = input_allocate_device();
|
||||||
|
if (!kbd || !dev) {
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
mdev->private_data = kbd;
|
||||||
|
|
||||||
|
kbd->dev = dev;
|
||||||
|
memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode));
|
||||||
|
|
||||||
|
dev->name = mdev->product_name;
|
||||||
|
dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
|
||||||
|
dev->keycode = kbd->keycode;
|
||||||
|
dev->keycodesize = sizeof (unsigned short);
|
||||||
|
dev->keycodemax = ARRAY_SIZE(kbd->keycode);
|
||||||
|
dev->id.bustype = BUS_HOST;
|
||||||
|
dev->dev.parent = &mdev->dev;
|
||||||
|
|
||||||
|
for (i = 0; i < NR_SCANCODES; i++)
|
||||||
|
__set_bit(dc_kbd_keycode[i], dev->keybit);
|
||||||
|
__clear_bit(KEY_RESERVED, dev->keybit);
|
||||||
|
|
||||||
|
input_set_capability(dev, EV_MSC, MSC_SCAN);
|
||||||
|
input_set_drvdata(dev, kbd);
|
||||||
|
|
||||||
|
error = input_register_device(dev);
|
||||||
|
if (error)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/* Maple polling is locked to VBLANK - which may be just 50/s */
|
||||||
|
maple_getcond_callback(mdev, dc_kbd_callback, HZ/50, MAPLE_FUNC_KEYBOARD);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
input_free_device(dev);
|
||||||
|
kfree(kbd);
|
||||||
|
mdev->private_data = NULL;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dc_kbd_disconnect(struct maple_device *mdev)
|
||||||
|
{
|
||||||
|
struct dc_kbd *kbd;
|
||||||
|
|
||||||
|
mutex_lock(&maple_keyb_mutex);
|
||||||
|
|
||||||
|
kbd = mdev->private_data;
|
||||||
|
mdev->private_data = NULL;
|
||||||
|
input_unregister_device(kbd->dev);
|
||||||
|
kfree(kbd);
|
||||||
|
|
||||||
|
mutex_unlock(&maple_keyb_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allow the keyboard to be used */
|
||||||
|
static int probe_maple_kbd(struct device *dev)
|
||||||
|
{
|
||||||
|
struct maple_device *mdev = to_maple_dev(dev);
|
||||||
|
struct maple_driver *mdrv = to_maple_driver(dev->driver);
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = dc_kbd_connect(mdev);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
mdev->driver = mdrv;
|
||||||
|
mdev->registered = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct maple_driver dc_kbd_driver = {
|
||||||
|
.function = MAPLE_FUNC_KEYBOARD,
|
||||||
|
.connect = dc_kbd_connect,
|
||||||
|
.disconnect = dc_kbd_disconnect,
|
||||||
|
.drv = {
|
||||||
|
.name = "Dreamcast_keyboard",
|
||||||
|
.probe = probe_maple_kbd,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init dc_kbd_init(void)
|
||||||
|
{
|
||||||
|
return maple_driver_register(&dc_kbd_driver.drv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit dc_kbd_exit(void)
|
||||||
|
{
|
||||||
|
driver_unregister(&dc_kbd_driver.drv);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(dc_kbd_init);
|
||||||
|
module_exit(dc_kbd_exit);
|
|
@ -318,7 +318,7 @@ static int __init omap_kp_probe(struct platform_device *pdev)
|
||||||
keymap = pdata->keymap;
|
keymap = pdata->keymap;
|
||||||
|
|
||||||
if (pdata->rep)
|
if (pdata->rep)
|
||||||
set_bit(EV_REP, input_dev->evbit);
|
__set_bit(EV_REP, input_dev->evbit);
|
||||||
|
|
||||||
if (pdata->delay)
|
if (pdata->delay)
|
||||||
omap_kp->delay = pdata->delay;
|
omap_kp->delay = pdata->delay;
|
||||||
|
@ -365,9 +365,9 @@ static int __init omap_kp_probe(struct platform_device *pdev)
|
||||||
goto err2;
|
goto err2;
|
||||||
|
|
||||||
/* setup input device */
|
/* setup input device */
|
||||||
set_bit(EV_KEY, input_dev->evbit);
|
__set_bit(EV_KEY, input_dev->evbit);
|
||||||
for (i = 0; keymap[i] != 0; i++)
|
for (i = 0; keymap[i] != 0; i++)
|
||||||
set_bit(keymap[i] & KEY_MAX, input_dev->keybit);
|
__set_bit(keymap[i] & KEY_MAX, input_dev->keybit);
|
||||||
input_dev->name = "omap-keypad";
|
input_dev->name = "omap-keypad";
|
||||||
input_dev->phys = "omap-keypad/input0";
|
input_dev->phys = "omap-keypad/input0";
|
||||||
input_dev->dev.parent = &pdev->dev;
|
input_dev->dev.parent = &pdev->dev;
|
||||||
|
@ -377,10 +377,6 @@ static int __init omap_kp_probe(struct platform_device *pdev)
|
||||||
input_dev->id.product = 0x0001;
|
input_dev->id.product = 0x0001;
|
||||||
input_dev->id.version = 0x0100;
|
input_dev->id.version = 0x0100;
|
||||||
|
|
||||||
input_dev->keycode = keymap;
|
|
||||||
input_dev->keycodesize = sizeof(unsigned int);
|
|
||||||
input_dev->keycodemax = pdata->keymapsize;
|
|
||||||
|
|
||||||
ret = input_register_device(omap_kp->input);
|
ret = input_register_device(omap_kp->input);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
printk(KERN_ERR "Unable to register omap-keypad input device\n");
|
printk(KERN_ERR "Unable to register omap-keypad input device\n");
|
||||||
|
@ -411,7 +407,7 @@ static int __init omap_kp_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
err5:
|
err5:
|
||||||
for (i = irq_idx-1; i >=0; i--)
|
for (i = irq_idx - 1; i >=0; i--)
|
||||||
free_irq(row_gpios[i], 0);
|
free_irq(row_gpios[i], 0);
|
||||||
err4:
|
err4:
|
||||||
input_unregister_device(omap_kp->input);
|
input_unregister_device(omap_kp->input);
|
||||||
|
|
|
@ -48,11 +48,13 @@ static const struct alps_model_info alps_model_data[] = {
|
||||||
{ { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */
|
{ { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */
|
||||||
{ { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 },
|
{ { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 },
|
||||||
{ { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS }, /* Dell Latitude D800 */
|
{ { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS }, /* Dell Latitude D800 */
|
||||||
|
{ { 0x73, 0x00, 0x0a }, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */
|
||||||
{ { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
|
{ { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
|
||||||
{ { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */
|
{ { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */
|
||||||
{ { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */
|
{ { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */
|
||||||
{ { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
|
{ { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
|
||||||
{ { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
|
{ { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
|
||||||
|
{ { 0x73, 0x02, 0x50 }, 0xcf, 0xff, ALPS_FW_BK_1 } /* Dell Vostro 1400 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -502,18 +502,23 @@ static void atp_complete(struct urb* urb)
|
||||||
|
|
||||||
/* reset the accumulator on release */
|
/* reset the accumulator on release */
|
||||||
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
|
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
|
||||||
|
}
|
||||||
|
|
||||||
/* Geyser 3 will continue to send packets continually after
|
/* Geyser 3 will continue to send packets continually after
|
||||||
the first touch unless reinitialised. Do so if it's been
|
the first touch unless reinitialised. Do so if it's been
|
||||||
idle for a while in order to avoid waking the kernel up
|
idle for a while in order to avoid waking the kernel up
|
||||||
several hundred times a second */
|
several hundred times a second */
|
||||||
if (!key && atp_is_geyser_3(dev)) {
|
|
||||||
|
if (atp_is_geyser_3(dev)) {
|
||||||
|
if (!x && !y && !key) {
|
||||||
dev->idlecount++;
|
dev->idlecount++;
|
||||||
if (dev->idlecount == 10) {
|
if (dev->idlecount == 10) {
|
||||||
dev->valid = 0;
|
dev->valid = 0;
|
||||||
schedule_work(&dev->work);
|
schedule_work(&dev->work);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
dev->idlecount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
input_report_key(dev->input, BTN_LEFT, key);
|
input_report_key(dev->input, BTN_LEFT, key);
|
||||||
|
|
|
@ -96,6 +96,14 @@ static const struct dmi_system_id lifebook_dmi_table[] = {
|
||||||
},
|
},
|
||||||
.callback = lifebook_set_6byte_proto,
|
.callback = lifebook_set_6byte_proto,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.ident = "CF-72",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "CF-72"),
|
||||||
|
},
|
||||||
|
.callback = lifebook_set_serio_phys,
|
||||||
|
.driver_data = "isa0060/serio3",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.ident = "Lifebook B142",
|
.ident = "Lifebook B142",
|
||||||
.matches = {
|
.matches = {
|
||||||
|
@ -282,7 +290,7 @@ static int lifebook_create_relative_device(struct psmouse *psmouse)
|
||||||
int lifebook_init(struct psmouse *psmouse)
|
int lifebook_init(struct psmouse *psmouse)
|
||||||
{
|
{
|
||||||
struct input_dev *dev1 = psmouse->dev;
|
struct input_dev *dev1 = psmouse->dev;
|
||||||
int max_coord = lifebook_use_6byte_proto ? 1024 : 4096;
|
int max_coord = lifebook_use_6byte_proto ? 4096 : 1024;
|
||||||
|
|
||||||
if (lifebook_absolute_mode(psmouse))
|
if (lifebook_absolute_mode(psmouse))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -648,9 +648,10 @@ static int psmouse_extensions(struct psmouse *psmouse,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset to defaults in case the device got confused by extended
|
* Reset to defaults in case the device got confused by extended
|
||||||
* protocol probes. Note that we do full reset becuase some mice
|
* protocol probes. Note that we follow up with full reset because
|
||||||
* put themselves to sleep when see PSMOUSE_RESET_DIS.
|
* some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
|
||||||
*/
|
*/
|
||||||
|
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
|
||||||
psmouse_reset(psmouse);
|
psmouse_reset(psmouse);
|
||||||
|
|
||||||
if (max_proto >= PSMOUSE_IMEX && im_explorer_detect(psmouse, set_properties) == 0)
|
if (max_proto >= PSMOUSE_IMEX && im_explorer_detect(psmouse, set_properties) == 0)
|
||||||
|
|
|
@ -61,9 +61,11 @@ struct mousedev {
|
||||||
int open;
|
int open;
|
||||||
int minor;
|
int minor;
|
||||||
char name[16];
|
char name[16];
|
||||||
|
struct input_handle handle;
|
||||||
wait_queue_head_t wait;
|
wait_queue_head_t wait;
|
||||||
struct list_head client_list;
|
struct list_head client_list;
|
||||||
struct input_handle handle;
|
spinlock_t client_lock; /* protects client_list */
|
||||||
|
struct mutex mutex;
|
||||||
struct device dev;
|
struct device dev;
|
||||||
|
|
||||||
struct list_head mixdev_node;
|
struct list_head mixdev_node;
|
||||||
|
@ -113,28 +115,36 @@ static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 };
|
||||||
static struct input_handler mousedev_handler;
|
static struct input_handler mousedev_handler;
|
||||||
|
|
||||||
static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
|
static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
|
||||||
|
static DEFINE_MUTEX(mousedev_table_mutex);
|
||||||
static struct mousedev *mousedev_mix;
|
static struct mousedev *mousedev_mix;
|
||||||
static LIST_HEAD(mousedev_mix_list);
|
static LIST_HEAD(mousedev_mix_list);
|
||||||
|
|
||||||
|
static void mixdev_open_devices(void);
|
||||||
|
static void mixdev_close_devices(void);
|
||||||
|
|
||||||
#define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03])
|
#define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03])
|
||||||
#define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03])
|
#define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03])
|
||||||
|
|
||||||
static void mousedev_touchpad_event(struct input_dev *dev, struct mousedev *mousedev, unsigned int code, int value)
|
static void mousedev_touchpad_event(struct input_dev *dev,
|
||||||
|
struct mousedev *mousedev,
|
||||||
|
unsigned int code, int value)
|
||||||
{
|
{
|
||||||
int size, tmp;
|
int size, tmp;
|
||||||
enum { FRACTION_DENOM = 128 };
|
enum { FRACTION_DENOM = 128 };
|
||||||
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
|
|
||||||
case ABS_X:
|
case ABS_X:
|
||||||
fx(0) = value;
|
fx(0) = value;
|
||||||
if (mousedev->touch && mousedev->pkt_count >= 2) {
|
if (mousedev->touch && mousedev->pkt_count >= 2) {
|
||||||
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
|
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
size = 256 * 2;
|
size = 256 * 2;
|
||||||
tmp = ((value - fx(2)) * (256 * FRACTION_DENOM)) / size;
|
tmp = ((value - fx(2)) * 256 * FRACTION_DENOM) / size;
|
||||||
tmp += mousedev->frac_dx;
|
tmp += mousedev->frac_dx;
|
||||||
mousedev->packet.dx = tmp / FRACTION_DENOM;
|
mousedev->packet.dx = tmp / FRACTION_DENOM;
|
||||||
mousedev->frac_dx = tmp - mousedev->packet.dx * FRACTION_DENOM;
|
mousedev->frac_dx =
|
||||||
|
tmp - mousedev->packet.dx * FRACTION_DENOM;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -145,20 +155,23 @@ static void mousedev_touchpad_event(struct input_dev *dev, struct mousedev *mous
|
||||||
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
|
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
size = 256 * 2;
|
size = 256 * 2;
|
||||||
tmp = -((value - fy(2)) * (256 * FRACTION_DENOM)) / size;
|
tmp = -((value - fy(2)) * 256 * FRACTION_DENOM) / size;
|
||||||
tmp += mousedev->frac_dy;
|
tmp += mousedev->frac_dy;
|
||||||
mousedev->packet.dy = tmp / FRACTION_DENOM;
|
mousedev->packet.dy = tmp / FRACTION_DENOM;
|
||||||
mousedev->frac_dy = tmp - mousedev->packet.dy * FRACTION_DENOM;
|
mousedev->frac_dy = tmp -
|
||||||
|
mousedev->packet.dy * FRACTION_DENOM;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev, unsigned int code, int value)
|
static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev,
|
||||||
|
unsigned int code, int value)
|
||||||
{
|
{
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
|
|
||||||
case ABS_X:
|
case ABS_X:
|
||||||
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
|
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
|
@ -167,7 +180,8 @@ static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev,
|
||||||
value = dev->absmax[ABS_X];
|
value = dev->absmax[ABS_X];
|
||||||
if (value < dev->absmin[ABS_X])
|
if (value < dev->absmin[ABS_X])
|
||||||
value = dev->absmin[ABS_X];
|
value = dev->absmin[ABS_X];
|
||||||
mousedev->packet.x = ((value - dev->absmin[ABS_X]) * xres) / size;
|
mousedev->packet.x =
|
||||||
|
((value - dev->absmin[ABS_X]) * xres) / size;
|
||||||
mousedev->packet.abs_event = 1;
|
mousedev->packet.abs_event = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -179,41 +193,58 @@ static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev,
|
||||||
value = dev->absmax[ABS_Y];
|
value = dev->absmax[ABS_Y];
|
||||||
if (value < dev->absmin[ABS_Y])
|
if (value < dev->absmin[ABS_Y])
|
||||||
value = dev->absmin[ABS_Y];
|
value = dev->absmin[ABS_Y];
|
||||||
mousedev->packet.y = yres - ((value - dev->absmin[ABS_Y]) * yres) / size;
|
mousedev->packet.y = yres -
|
||||||
|
((value - dev->absmin[ABS_Y]) * yres) / size;
|
||||||
mousedev->packet.abs_event = 1;
|
mousedev->packet.abs_event = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mousedev_rel_event(struct mousedev *mousedev, unsigned int code, int value)
|
static void mousedev_rel_event(struct mousedev *mousedev,
|
||||||
|
unsigned int code, int value)
|
||||||
{
|
{
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case REL_X: mousedev->packet.dx += value; break;
|
case REL_X:
|
||||||
case REL_Y: mousedev->packet.dy -= value; break;
|
mousedev->packet.dx += value;
|
||||||
case REL_WHEEL: mousedev->packet.dz -= value; break;
|
break;
|
||||||
|
|
||||||
|
case REL_Y:
|
||||||
|
mousedev->packet.dy -= value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case REL_WHEEL:
|
||||||
|
mousedev->packet.dz -= value;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int value)
|
static void mousedev_key_event(struct mousedev *mousedev,
|
||||||
|
unsigned int code, int value)
|
||||||
{
|
{
|
||||||
int index;
|
int index;
|
||||||
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
|
|
||||||
case BTN_TOUCH:
|
case BTN_TOUCH:
|
||||||
case BTN_0:
|
case BTN_0:
|
||||||
case BTN_LEFT: index = 0; break;
|
case BTN_LEFT: index = 0; break;
|
||||||
|
|
||||||
case BTN_STYLUS:
|
case BTN_STYLUS:
|
||||||
case BTN_1:
|
case BTN_1:
|
||||||
case BTN_RIGHT: index = 1; break;
|
case BTN_RIGHT: index = 1; break;
|
||||||
|
|
||||||
case BTN_2:
|
case BTN_2:
|
||||||
case BTN_FORWARD:
|
case BTN_FORWARD:
|
||||||
case BTN_STYLUS2:
|
case BTN_STYLUS2:
|
||||||
case BTN_MIDDLE: index = 2; break;
|
case BTN_MIDDLE: index = 2; break;
|
||||||
|
|
||||||
case BTN_3:
|
case BTN_3:
|
||||||
case BTN_BACK:
|
case BTN_BACK:
|
||||||
case BTN_SIDE: index = 3; break;
|
case BTN_SIDE: index = 3; break;
|
||||||
|
|
||||||
case BTN_4:
|
case BTN_4:
|
||||||
case BTN_EXTRA: index = 4; break;
|
case BTN_EXTRA: index = 4; break;
|
||||||
|
|
||||||
default: return;
|
default: return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,19 +257,23 @@ static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_hw_data *packet)
|
static void mousedev_notify_readers(struct mousedev *mousedev,
|
||||||
|
struct mousedev_hw_data *packet)
|
||||||
{
|
{
|
||||||
struct mousedev_client *client;
|
struct mousedev_client *client;
|
||||||
struct mousedev_motion *p;
|
struct mousedev_motion *p;
|
||||||
unsigned long flags;
|
unsigned int new_head;
|
||||||
int wake_readers = 0;
|
int wake_readers = 0;
|
||||||
|
|
||||||
list_for_each_entry(client, &mousedev->client_list, node) {
|
rcu_read_lock();
|
||||||
spin_lock_irqsave(&client->packet_lock, flags);
|
list_for_each_entry_rcu(client, &mousedev->client_list, node) {
|
||||||
|
|
||||||
|
/* Just acquire the lock, interrupts already disabled */
|
||||||
|
spin_lock(&client->packet_lock);
|
||||||
|
|
||||||
p = &client->packets[client->head];
|
p = &client->packets[client->head];
|
||||||
if (client->ready && p->buttons != mousedev->packet.buttons) {
|
if (client->ready && p->buttons != mousedev->packet.buttons) {
|
||||||
unsigned int new_head = (client->head + 1) % PACKET_QUEUE_LEN;
|
new_head = (client->head + 1) % PACKET_QUEUE_LEN;
|
||||||
if (new_head != client->tail) {
|
if (new_head != client->tail) {
|
||||||
p = &client->packets[client->head = new_head];
|
p = &client->packets[client->head = new_head];
|
||||||
memset(p, 0, sizeof(struct mousedev_motion));
|
memset(p, 0, sizeof(struct mousedev_motion));
|
||||||
|
@ -253,25 +288,29 @@ static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_h
|
||||||
}
|
}
|
||||||
|
|
||||||
client->pos_x += packet->dx;
|
client->pos_x += packet->dx;
|
||||||
client->pos_x = client->pos_x < 0 ? 0 : (client->pos_x >= xres ? xres : client->pos_x);
|
client->pos_x = client->pos_x < 0 ?
|
||||||
|
0 : (client->pos_x >= xres ? xres : client->pos_x);
|
||||||
client->pos_y += packet->dy;
|
client->pos_y += packet->dy;
|
||||||
client->pos_y = client->pos_y < 0 ? 0 : (client->pos_y >= yres ? yres : client->pos_y);
|
client->pos_y = client->pos_y < 0 ?
|
||||||
|
0 : (client->pos_y >= yres ? yres : client->pos_y);
|
||||||
|
|
||||||
p->dx += packet->dx;
|
p->dx += packet->dx;
|
||||||
p->dy += packet->dy;
|
p->dy += packet->dy;
|
||||||
p->dz += packet->dz;
|
p->dz += packet->dz;
|
||||||
p->buttons = mousedev->packet.buttons;
|
p->buttons = mousedev->packet.buttons;
|
||||||
|
|
||||||
if (p->dx || p->dy || p->dz || p->buttons != client->last_buttons)
|
if (p->dx || p->dy || p->dz ||
|
||||||
|
p->buttons != client->last_buttons)
|
||||||
client->ready = 1;
|
client->ready = 1;
|
||||||
|
|
||||||
spin_unlock_irqrestore(&client->packet_lock, flags);
|
spin_unlock(&client->packet_lock);
|
||||||
|
|
||||||
if (client->ready) {
|
if (client->ready) {
|
||||||
kill_fasync(&client->fasync, SIGIO, POLL_IN);
|
kill_fasync(&client->fasync, SIGIO, POLL_IN);
|
||||||
wake_readers = 1;
|
wake_readers = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
if (wake_readers)
|
if (wake_readers)
|
||||||
wake_up_interruptible(&mousedev->wait);
|
wake_up_interruptible(&mousedev->wait);
|
||||||
|
@ -281,7 +320,8 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
|
||||||
{
|
{
|
||||||
if (!value) {
|
if (!value) {
|
||||||
if (mousedev->touch &&
|
if (mousedev->touch &&
|
||||||
time_before(jiffies, mousedev->touch + msecs_to_jiffies(tap_time))) {
|
time_before(jiffies,
|
||||||
|
mousedev->touch + msecs_to_jiffies(tap_time))) {
|
||||||
/*
|
/*
|
||||||
* Toggle left button to emulate tap.
|
* Toggle left button to emulate tap.
|
||||||
* We rely on the fact that mousedev_mix always has 0
|
* We rely on the fact that mousedev_mix always has 0
|
||||||
|
@ -290,7 +330,8 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
|
||||||
set_bit(0, &mousedev->packet.buttons);
|
set_bit(0, &mousedev->packet.buttons);
|
||||||
set_bit(0, &mousedev_mix->packet.buttons);
|
set_bit(0, &mousedev_mix->packet.buttons);
|
||||||
mousedev_notify_readers(mousedev, &mousedev_mix->packet);
|
mousedev_notify_readers(mousedev, &mousedev_mix->packet);
|
||||||
mousedev_notify_readers(mousedev_mix, &mousedev_mix->packet);
|
mousedev_notify_readers(mousedev_mix,
|
||||||
|
&mousedev_mix->packet);
|
||||||
clear_bit(0, &mousedev->packet.buttons);
|
clear_bit(0, &mousedev->packet.buttons);
|
||||||
clear_bit(0, &mousedev_mix->packet.buttons);
|
clear_bit(0, &mousedev_mix->packet.buttons);
|
||||||
}
|
}
|
||||||
|
@ -302,18 +343,21 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
|
||||||
mousedev->touch = jiffies;
|
mousedev->touch = jiffies;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
|
static void mousedev_event(struct input_handle *handle,
|
||||||
|
unsigned int type, unsigned int code, int value)
|
||||||
{
|
{
|
||||||
struct mousedev *mousedev = handle->private;
|
struct mousedev *mousedev = handle->private;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
||||||
case EV_ABS:
|
case EV_ABS:
|
||||||
/* Ignore joysticks */
|
/* Ignore joysticks */
|
||||||
if (test_bit(BTN_TRIGGER, handle->dev->keybit))
|
if (test_bit(BTN_TRIGGER, handle->dev->keybit))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
|
if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
|
||||||
mousedev_touchpad_event(handle->dev, mousedev, code, value);
|
mousedev_touchpad_event(handle->dev,
|
||||||
|
mousedev, code, value);
|
||||||
else
|
else
|
||||||
mousedev_abs_event(handle->dev, mousedev, code, value);
|
mousedev_abs_event(handle->dev, mousedev, code, value);
|
||||||
|
|
||||||
|
@ -325,7 +369,8 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig
|
||||||
|
|
||||||
case EV_KEY:
|
case EV_KEY:
|
||||||
if (value != 2) {
|
if (value != 2) {
|
||||||
if (code == BTN_TOUCH && test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
|
if (code == BTN_TOUCH &&
|
||||||
|
test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
|
||||||
mousedev_touchpad_touch(mousedev, value);
|
mousedev_touchpad_touch(mousedev, value);
|
||||||
else
|
else
|
||||||
mousedev_key_event(mousedev, code, value);
|
mousedev_key_event(mousedev, code, value);
|
||||||
|
@ -336,8 +381,10 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig
|
||||||
if (code == SYN_REPORT) {
|
if (code == SYN_REPORT) {
|
||||||
if (mousedev->touch) {
|
if (mousedev->touch) {
|
||||||
mousedev->pkt_count++;
|
mousedev->pkt_count++;
|
||||||
/* Input system eats duplicate events, but we need all of them
|
/*
|
||||||
* to do correct averaging so apply present one forward
|
* Input system eats duplicate events,
|
||||||
|
* but we need all of them to do correct
|
||||||
|
* averaging so apply present one forward
|
||||||
*/
|
*/
|
||||||
fx(0) = fx(1);
|
fx(0) = fx(1);
|
||||||
fy(0) = fy(1);
|
fy(0) = fy(1);
|
||||||
|
@ -346,7 +393,8 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig
|
||||||
mousedev_notify_readers(mousedev, &mousedev->packet);
|
mousedev_notify_readers(mousedev, &mousedev->packet);
|
||||||
mousedev_notify_readers(mousedev_mix, &mousedev->packet);
|
mousedev_notify_readers(mousedev_mix, &mousedev->packet);
|
||||||
|
|
||||||
mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0;
|
mousedev->packet.dx = mousedev->packet.dy =
|
||||||
|
mousedev->packet.dz = 0;
|
||||||
mousedev->packet.abs_event = 0;
|
mousedev->packet.abs_event = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -367,41 +415,48 @@ static void mousedev_free(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mousedev *mousedev = container_of(dev, struct mousedev, dev);
|
struct mousedev *mousedev = container_of(dev, struct mousedev, dev);
|
||||||
|
|
||||||
mousedev_table[mousedev->minor] = NULL;
|
|
||||||
kfree(mousedev);
|
kfree(mousedev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mixdev_add_device(struct mousedev *mousedev)
|
static int mousedev_open_device(struct mousedev *mousedev)
|
||||||
{
|
{
|
||||||
int error;
|
int retval;
|
||||||
|
|
||||||
if (mousedev_mix->open) {
|
retval = mutex_lock_interruptible(&mousedev->mutex);
|
||||||
error = input_open_device(&mousedev->handle);
|
if (retval)
|
||||||
if (error)
|
return retval;
|
||||||
return error;
|
|
||||||
|
|
||||||
mousedev->open++;
|
if (mousedev->minor == MOUSEDEV_MIX)
|
||||||
mousedev->mixdev_open = 1;
|
mixdev_open_devices();
|
||||||
|
else if (!mousedev->exist)
|
||||||
|
retval = -ENODEV;
|
||||||
|
else if (!mousedev->open++) {
|
||||||
|
retval = input_open_device(&mousedev->handle);
|
||||||
|
if (retval)
|
||||||
|
mousedev->open--;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_device(&mousedev->dev);
|
mutex_unlock(&mousedev->mutex);
|
||||||
list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list);
|
return retval;
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mixdev_remove_device(struct mousedev *mousedev)
|
static void mousedev_close_device(struct mousedev *mousedev)
|
||||||
{
|
{
|
||||||
if (mousedev->mixdev_open) {
|
mutex_lock(&mousedev->mutex);
|
||||||
mousedev->mixdev_open = 0;
|
|
||||||
if (!--mousedev->open && mousedev->exist)
|
if (mousedev->minor == MOUSEDEV_MIX)
|
||||||
|
mixdev_close_devices();
|
||||||
|
else if (mousedev->exist && !--mousedev->open)
|
||||||
input_close_device(&mousedev->handle);
|
input_close_device(&mousedev->handle);
|
||||||
}
|
|
||||||
|
|
||||||
list_del_init(&mousedev->mixdev_node);
|
mutex_unlock(&mousedev->mutex);
|
||||||
put_device(&mousedev->dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open all available devices so they can all be multiplexed in one.
|
||||||
|
* stream. Note that this function is called with mousedev_mix->mutex
|
||||||
|
* held.
|
||||||
|
*/
|
||||||
static void mixdev_open_devices(void)
|
static void mixdev_open_devices(void)
|
||||||
{
|
{
|
||||||
struct mousedev *mousedev;
|
struct mousedev *mousedev;
|
||||||
|
@ -411,16 +466,19 @@ static void mixdev_open_devices(void)
|
||||||
|
|
||||||
list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
|
list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
|
||||||
if (!mousedev->mixdev_open) {
|
if (!mousedev->mixdev_open) {
|
||||||
if (!mousedev->open && mousedev->exist)
|
if (mousedev_open_device(mousedev))
|
||||||
if (input_open_device(&mousedev->handle))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
mousedev->open++;
|
|
||||||
mousedev->mixdev_open = 1;
|
mousedev->mixdev_open = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close all devices that were opened as part of multiplexed
|
||||||
|
* device. Note that this function is called with mousedev_mix->mutex
|
||||||
|
* held.
|
||||||
|
*/
|
||||||
static void mixdev_close_devices(void)
|
static void mixdev_close_devices(void)
|
||||||
{
|
{
|
||||||
struct mousedev *mousedev;
|
struct mousedev *mousedev;
|
||||||
|
@ -431,33 +489,45 @@ static void mixdev_close_devices(void)
|
||||||
list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
|
list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
|
||||||
if (mousedev->mixdev_open) {
|
if (mousedev->mixdev_open) {
|
||||||
mousedev->mixdev_open = 0;
|
mousedev->mixdev_open = 0;
|
||||||
if (!--mousedev->open && mousedev->exist)
|
mousedev_close_device(mousedev);
|
||||||
input_close_device(&mousedev->handle);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void mousedev_attach_client(struct mousedev *mousedev,
|
||||||
|
struct mousedev_client *client)
|
||||||
|
{
|
||||||
|
spin_lock(&mousedev->client_lock);
|
||||||
|
list_add_tail_rcu(&client->node, &mousedev->client_list);
|
||||||
|
spin_unlock(&mousedev->client_lock);
|
||||||
|
synchronize_rcu();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mousedev_detach_client(struct mousedev *mousedev,
|
||||||
|
struct mousedev_client *client)
|
||||||
|
{
|
||||||
|
spin_lock(&mousedev->client_lock);
|
||||||
|
list_del_rcu(&client->node);
|
||||||
|
spin_unlock(&mousedev->client_lock);
|
||||||
|
synchronize_rcu();
|
||||||
|
}
|
||||||
|
|
||||||
static int mousedev_release(struct inode *inode, struct file *file)
|
static int mousedev_release(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
struct mousedev_client *client = file->private_data;
|
struct mousedev_client *client = file->private_data;
|
||||||
struct mousedev *mousedev = client->mousedev;
|
struct mousedev *mousedev = client->mousedev;
|
||||||
|
|
||||||
mousedev_fasync(-1, file, 0);
|
mousedev_fasync(-1, file, 0);
|
||||||
|
mousedev_detach_client(mousedev, client);
|
||||||
list_del(&client->node);
|
|
||||||
kfree(client);
|
kfree(client);
|
||||||
|
|
||||||
if (mousedev->minor == MOUSEDEV_MIX)
|
mousedev_close_device(mousedev);
|
||||||
mixdev_close_devices();
|
|
||||||
else if (!--mousedev->open && mousedev->exist)
|
|
||||||
input_close_device(&mousedev->handle);
|
|
||||||
|
|
||||||
put_device(&mousedev->dev);
|
put_device(&mousedev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int mousedev_open(struct inode *inode, struct file *file)
|
static int mousedev_open(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
struct mousedev_client *client;
|
struct mousedev_client *client;
|
||||||
|
@ -475,12 +545,17 @@ static int mousedev_open(struct inode *inode, struct file *file)
|
||||||
if (i >= MOUSEDEV_MINORS)
|
if (i >= MOUSEDEV_MINORS)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
error = mutex_lock_interruptible(&mousedev_table_mutex);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
mousedev = mousedev_table[i];
|
mousedev = mousedev_table[i];
|
||||||
|
if (mousedev)
|
||||||
|
get_device(&mousedev->dev);
|
||||||
|
mutex_unlock(&mousedev_table_mutex);
|
||||||
|
|
||||||
if (!mousedev)
|
if (!mousedev)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
get_device(&mousedev->dev);
|
|
||||||
|
|
||||||
client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL);
|
client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
error = -ENOMEM;
|
error = -ENOMEM;
|
||||||
|
@ -491,21 +566,17 @@ static int mousedev_open(struct inode *inode, struct file *file)
|
||||||
client->pos_x = xres / 2;
|
client->pos_x = xres / 2;
|
||||||
client->pos_y = yres / 2;
|
client->pos_y = yres / 2;
|
||||||
client->mousedev = mousedev;
|
client->mousedev = mousedev;
|
||||||
list_add_tail(&client->node, &mousedev->client_list);
|
mousedev_attach_client(mousedev, client);
|
||||||
|
|
||||||
if (mousedev->minor == MOUSEDEV_MIX)
|
error = mousedev_open_device(mousedev);
|
||||||
mixdev_open_devices();
|
|
||||||
else if (!mousedev->open++ && mousedev->exist) {
|
|
||||||
error = input_open_device(&mousedev->handle);
|
|
||||||
if (error)
|
if (error)
|
||||||
goto err_free_client;
|
goto err_free_client;
|
||||||
}
|
|
||||||
|
|
||||||
file->private_data = client;
|
file->private_data = client;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_free_client:
|
err_free_client:
|
||||||
list_del(&client->node);
|
mousedev_detach_client(mousedev, client);
|
||||||
kfree(client);
|
kfree(client);
|
||||||
err_put_mousedev:
|
err_put_mousedev:
|
||||||
put_device(&mousedev->dev);
|
put_device(&mousedev->dev);
|
||||||
|
@ -517,15 +588,13 @@ static inline int mousedev_limit_delta(int delta, int limit)
|
||||||
return delta > limit ? limit : (delta < -limit ? -limit : delta);
|
return delta > limit ? limit : (delta < -limit ? -limit : delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mousedev_packet(struct mousedev_client *client, signed char *ps2_data)
|
static void mousedev_packet(struct mousedev_client *client,
|
||||||
|
signed char *ps2_data)
|
||||||
{
|
{
|
||||||
struct mousedev_motion *p;
|
struct mousedev_motion *p = &client->packets[client->tail];
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&client->packet_lock, flags);
|
ps2_data[0] = 0x08 |
|
||||||
p = &client->packets[client->tail];
|
((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
|
||||||
|
|
||||||
ps2_data[0] = 0x08 | ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
|
|
||||||
ps2_data[1] = mousedev_limit_delta(p->dx, 127);
|
ps2_data[1] = mousedev_limit_delta(p->dx, 127);
|
||||||
ps2_data[2] = mousedev_limit_delta(p->dy, 127);
|
ps2_data[2] = mousedev_limit_delta(p->dy, 127);
|
||||||
p->dx -= ps2_data[1];
|
p->dx -= ps2_data[1];
|
||||||
|
@ -540,7 +609,8 @@ static void mousedev_packet(struct mousedev_client *client, signed char *ps2_dat
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MOUSEDEV_EMUL_IMPS:
|
case MOUSEDEV_EMUL_IMPS:
|
||||||
ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
|
ps2_data[0] |=
|
||||||
|
((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
|
||||||
ps2_data[3] = mousedev_limit_delta(p->dz, 127);
|
ps2_data[3] = mousedev_limit_delta(p->dz, 127);
|
||||||
p->dz -= ps2_data[3];
|
p->dz -= ps2_data[3];
|
||||||
client->bufsiz = 4;
|
client->bufsiz = 4;
|
||||||
|
@ -548,7 +618,8 @@ static void mousedev_packet(struct mousedev_client *client, signed char *ps2_dat
|
||||||
|
|
||||||
case MOUSEDEV_EMUL_PS2:
|
case MOUSEDEV_EMUL_PS2:
|
||||||
default:
|
default:
|
||||||
ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
|
ps2_data[0] |=
|
||||||
|
((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
|
||||||
p->dz = 0;
|
p->dz = 0;
|
||||||
client->bufsiz = 3;
|
client->bufsiz = 3;
|
||||||
break;
|
break;
|
||||||
|
@ -561,41 +632,14 @@ static void mousedev_packet(struct mousedev_client *client, signed char *ps2_dat
|
||||||
} else
|
} else
|
||||||
client->tail = (client->tail + 1) % PACKET_QUEUE_LEN;
|
client->tail = (client->tail + 1) % PACKET_QUEUE_LEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&client->packet_lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mousedev_generate_response(struct mousedev_client *client,
|
||||||
static ssize_t mousedev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
|
int command)
|
||||||
{
|
{
|
||||||
struct mousedev_client *client = file->private_data;
|
client->ps2[0] = 0xfa; /* ACK */
|
||||||
unsigned char c;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
switch (command) {
|
||||||
|
|
||||||
if (get_user(c, buffer + i))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (c == mousedev_imex_seq[client->imexseq]) {
|
|
||||||
if (++client->imexseq == MOUSEDEV_SEQ_LEN) {
|
|
||||||
client->imexseq = 0;
|
|
||||||
client->mode = MOUSEDEV_EMUL_EXPS;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
client->imexseq = 0;
|
|
||||||
|
|
||||||
if (c == mousedev_imps_seq[client->impsseq]) {
|
|
||||||
if (++client->impsseq == MOUSEDEV_SEQ_LEN) {
|
|
||||||
client->impsseq = 0;
|
|
||||||
client->mode = MOUSEDEV_EMUL_IMPS;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
client->impsseq = 0;
|
|
||||||
|
|
||||||
client->ps2[0] = 0xfa;
|
|
||||||
|
|
||||||
switch (c) {
|
|
||||||
|
|
||||||
case 0xeb: /* Poll */
|
case 0xeb: /* Poll */
|
||||||
mousedev_packet(client, &client->ps2[1]);
|
mousedev_packet(client, &client->ps2[1]);
|
||||||
|
@ -604,9 +648,15 @@ static ssize_t mousedev_write(struct file *file, const char __user *buffer, size
|
||||||
|
|
||||||
case 0xf2: /* Get ID */
|
case 0xf2: /* Get ID */
|
||||||
switch (client->mode) {
|
switch (client->mode) {
|
||||||
case MOUSEDEV_EMUL_PS2: client->ps2[1] = 0; break;
|
case MOUSEDEV_EMUL_PS2:
|
||||||
case MOUSEDEV_EMUL_IMPS: client->ps2[1] = 3; break;
|
client->ps2[1] = 0;
|
||||||
case MOUSEDEV_EMUL_EXPS: client->ps2[1] = 4; break;
|
break;
|
||||||
|
case MOUSEDEV_EMUL_IMPS:
|
||||||
|
client->ps2[1] = 3;
|
||||||
|
break;
|
||||||
|
case MOUSEDEV_EMUL_EXPS:
|
||||||
|
client->ps2[1] = 4;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
client->bufsiz = 2;
|
client->bufsiz = 2;
|
||||||
break;
|
break;
|
||||||
|
@ -627,34 +677,72 @@ static ssize_t mousedev_write(struct file *file, const char __user *buffer, size
|
||||||
client->bufsiz = 1;
|
client->bufsiz = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
client->buffer = client->bufsiz;
|
client->buffer = client->bufsiz;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t mousedev_write(struct file *file, const char __user *buffer,
|
||||||
|
size_t count, loff_t *ppos)
|
||||||
|
{
|
||||||
|
struct mousedev_client *client = file->private_data;
|
||||||
|
unsigned char c;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
|
||||||
|
if (get_user(c, buffer + i))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
spin_lock_irq(&client->packet_lock);
|
||||||
|
|
||||||
|
if (c == mousedev_imex_seq[client->imexseq]) {
|
||||||
|
if (++client->imexseq == MOUSEDEV_SEQ_LEN) {
|
||||||
|
client->imexseq = 0;
|
||||||
|
client->mode = MOUSEDEV_EMUL_EXPS;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
client->imexseq = 0;
|
||||||
|
|
||||||
|
if (c == mousedev_imps_seq[client->impsseq]) {
|
||||||
|
if (++client->impsseq == MOUSEDEV_SEQ_LEN) {
|
||||||
|
client->impsseq = 0;
|
||||||
|
client->mode = MOUSEDEV_EMUL_IMPS;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
client->impsseq = 0;
|
||||||
|
|
||||||
|
mousedev_generate_response(client, c);
|
||||||
|
|
||||||
|
spin_unlock_irq(&client->packet_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
kill_fasync(&client->fasync, SIGIO, POLL_IN);
|
kill_fasync(&client->fasync, SIGIO, POLL_IN);
|
||||||
|
|
||||||
wake_up_interruptible(&client->mousedev->wait);
|
wake_up_interruptible(&client->mousedev->wait);
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t mousedev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
|
static ssize_t mousedev_read(struct file *file, char __user *buffer,
|
||||||
|
size_t count, loff_t *ppos)
|
||||||
{
|
{
|
||||||
struct mousedev_client *client = file->private_data;
|
struct mousedev_client *client = file->private_data;
|
||||||
|
struct mousedev *mousedev = client->mousedev;
|
||||||
|
signed char data[sizeof(client->ps2)];
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
|
||||||
if (!client->ready && !client->buffer && (file->f_flags & O_NONBLOCK))
|
if (!client->ready && !client->buffer && mousedev->exist &&
|
||||||
|
(file->f_flags & O_NONBLOCK))
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
retval = wait_event_interruptible(client->mousedev->wait,
|
retval = wait_event_interruptible(mousedev->wait,
|
||||||
!client->mousedev->exist || client->ready || client->buffer);
|
!mousedev->exist || client->ready || client->buffer);
|
||||||
|
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
if (!client->mousedev->exist)
|
if (!mousedev->exist)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
spin_lock_irq(&client->packet_lock);
|
||||||
|
|
||||||
if (!client->buffer && client->ready) {
|
if (!client->buffer && client->ready) {
|
||||||
mousedev_packet(client, client->ps2);
|
mousedev_packet(client, client->ps2);
|
||||||
client->buffer = client->bufsiz;
|
client->buffer = client->bufsiz;
|
||||||
|
@ -663,9 +751,12 @@ static ssize_t mousedev_read(struct file *file, char __user *buffer, size_t coun
|
||||||
if (count > client->buffer)
|
if (count > client->buffer)
|
||||||
count = client->buffer;
|
count = client->buffer;
|
||||||
|
|
||||||
|
memcpy(data, client->ps2 + client->bufsiz - client->buffer, count);
|
||||||
client->buffer -= count;
|
client->buffer -= count;
|
||||||
|
|
||||||
if (copy_to_user(buffer, client->ps2 + client->bufsiz - client->buffer - count, count))
|
spin_unlock_irq(&client->packet_lock);
|
||||||
|
|
||||||
|
if (copy_to_user(buffer, data, count))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
|
@ -692,6 +783,60 @@ static const struct file_operations mousedev_fops = {
|
||||||
.fasync = mousedev_fasync,
|
.fasync = mousedev_fasync,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int mousedev_install_chrdev(struct mousedev *mousedev)
|
||||||
|
{
|
||||||
|
mousedev_table[mousedev->minor] = mousedev;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mousedev_remove_chrdev(struct mousedev *mousedev)
|
||||||
|
{
|
||||||
|
mutex_lock(&mousedev_table_mutex);
|
||||||
|
mousedev_table[mousedev->minor] = NULL;
|
||||||
|
mutex_unlock(&mousedev_table_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark device non-existent. This disables writes, ioctls and
|
||||||
|
* prevents new users from opening the device. Already posted
|
||||||
|
* blocking reads will stay, however new ones will fail.
|
||||||
|
*/
|
||||||
|
static void mousedev_mark_dead(struct mousedev *mousedev)
|
||||||
|
{
|
||||||
|
mutex_lock(&mousedev->mutex);
|
||||||
|
mousedev->exist = 0;
|
||||||
|
mutex_unlock(&mousedev->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wake up users waiting for IO so they can disconnect from
|
||||||
|
* dead device.
|
||||||
|
*/
|
||||||
|
static void mousedev_hangup(struct mousedev *mousedev)
|
||||||
|
{
|
||||||
|
struct mousedev_client *client;
|
||||||
|
|
||||||
|
spin_lock(&mousedev->client_lock);
|
||||||
|
list_for_each_entry(client, &mousedev->client_list, node)
|
||||||
|
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
|
||||||
|
spin_unlock(&mousedev->client_lock);
|
||||||
|
|
||||||
|
wake_up_interruptible(&mousedev->wait);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mousedev_cleanup(struct mousedev *mousedev)
|
||||||
|
{
|
||||||
|
struct input_handle *handle = &mousedev->handle;
|
||||||
|
|
||||||
|
mousedev_mark_dead(mousedev);
|
||||||
|
mousedev_hangup(mousedev);
|
||||||
|
mousedev_remove_chrdev(mousedev);
|
||||||
|
|
||||||
|
/* mousedev is marked dead so no one else accesses mousedev->open */
|
||||||
|
if (mousedev->open)
|
||||||
|
input_close_device(handle);
|
||||||
|
}
|
||||||
|
|
||||||
static struct mousedev *mousedev_create(struct input_dev *dev,
|
static struct mousedev *mousedev_create(struct input_dev *dev,
|
||||||
struct input_handler *handler,
|
struct input_handler *handler,
|
||||||
int minor)
|
int minor)
|
||||||
|
@ -707,6 +852,10 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
|
||||||
|
|
||||||
INIT_LIST_HEAD(&mousedev->client_list);
|
INIT_LIST_HEAD(&mousedev->client_list);
|
||||||
INIT_LIST_HEAD(&mousedev->mixdev_node);
|
INIT_LIST_HEAD(&mousedev->mixdev_node);
|
||||||
|
spin_lock_init(&mousedev->client_lock);
|
||||||
|
mutex_init(&mousedev->mutex);
|
||||||
|
lockdep_set_subclass(&mousedev->mutex,
|
||||||
|
minor == MOUSEDEV_MIX ? MOUSEDEV_MIX : 0);
|
||||||
init_waitqueue_head(&mousedev->wait);
|
init_waitqueue_head(&mousedev->wait);
|
||||||
|
|
||||||
if (minor == MOUSEDEV_MIX)
|
if (minor == MOUSEDEV_MIX)
|
||||||
|
@ -731,14 +880,27 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
|
||||||
mousedev->dev.release = mousedev_free;
|
mousedev->dev.release = mousedev_free;
|
||||||
device_initialize(&mousedev->dev);
|
device_initialize(&mousedev->dev);
|
||||||
|
|
||||||
mousedev_table[minor] = mousedev;
|
if (minor != MOUSEDEV_MIX) {
|
||||||
|
error = input_register_handle(&mousedev->handle);
|
||||||
|
if (error)
|
||||||
|
goto err_free_mousedev;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = mousedev_install_chrdev(mousedev);
|
||||||
|
if (error)
|
||||||
|
goto err_unregister_handle;
|
||||||
|
|
||||||
error = device_add(&mousedev->dev);
|
error = device_add(&mousedev->dev);
|
||||||
if (error)
|
if (error)
|
||||||
goto err_free_mousedev;
|
goto err_cleanup_mousedev;
|
||||||
|
|
||||||
return mousedev;
|
return mousedev;
|
||||||
|
|
||||||
|
err_cleanup_mousedev:
|
||||||
|
mousedev_cleanup(mousedev);
|
||||||
|
err_unregister_handle:
|
||||||
|
if (minor != MOUSEDEV_MIX)
|
||||||
|
input_unregister_handle(&mousedev->handle);
|
||||||
err_free_mousedev:
|
err_free_mousedev:
|
||||||
put_device(&mousedev->dev);
|
put_device(&mousedev->dev);
|
||||||
err_out:
|
err_out:
|
||||||
|
@ -747,29 +909,64 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
|
||||||
|
|
||||||
static void mousedev_destroy(struct mousedev *mousedev)
|
static void mousedev_destroy(struct mousedev *mousedev)
|
||||||
{
|
{
|
||||||
struct mousedev_client *client;
|
|
||||||
|
|
||||||
device_del(&mousedev->dev);
|
device_del(&mousedev->dev);
|
||||||
mousedev->exist = 0;
|
mousedev_cleanup(mousedev);
|
||||||
|
if (mousedev->minor != MOUSEDEV_MIX)
|
||||||
|
input_unregister_handle(&mousedev->handle);
|
||||||
|
put_device(&mousedev->dev);
|
||||||
|
}
|
||||||
|
|
||||||
if (mousedev->open) {
|
static int mixdev_add_device(struct mousedev *mousedev)
|
||||||
input_close_device(&mousedev->handle);
|
{
|
||||||
list_for_each_entry(client, &mousedev->client_list, node)
|
int retval;
|
||||||
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
|
|
||||||
wake_up_interruptible(&mousedev->wait);
|
retval = mutex_lock_interruptible(&mousedev_mix->mutex);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
if (mousedev_mix->open) {
|
||||||
|
retval = mousedev_open_device(mousedev);
|
||||||
|
if (retval)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
mousedev->mixdev_open = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_device(&mousedev->dev);
|
||||||
|
list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list);
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&mousedev_mix->mutex);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mixdev_remove_device(struct mousedev *mousedev)
|
||||||
|
{
|
||||||
|
mutex_lock(&mousedev_mix->mutex);
|
||||||
|
|
||||||
|
if (mousedev->mixdev_open) {
|
||||||
|
mousedev->mixdev_open = 0;
|
||||||
|
mousedev_close_device(mousedev);
|
||||||
|
}
|
||||||
|
|
||||||
|
list_del_init(&mousedev->mixdev_node);
|
||||||
|
mutex_unlock(&mousedev_mix->mutex);
|
||||||
|
|
||||||
put_device(&mousedev->dev);
|
put_device(&mousedev->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mousedev_connect(struct input_handler *handler, struct input_dev *dev,
|
static int mousedev_connect(struct input_handler *handler,
|
||||||
|
struct input_dev *dev,
|
||||||
const struct input_device_id *id)
|
const struct input_device_id *id)
|
||||||
{
|
{
|
||||||
struct mousedev *mousedev;
|
struct mousedev *mousedev;
|
||||||
int minor;
|
int minor;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++);
|
for (minor = 0; minor < MOUSEDEV_MINORS; minor++)
|
||||||
|
if (!mousedev_table[minor])
|
||||||
|
break;
|
||||||
|
|
||||||
if (minor == MOUSEDEV_MINORS) {
|
if (minor == MOUSEDEV_MINORS) {
|
||||||
printk(KERN_ERR "mousedev: no more free mousedev devices\n");
|
printk(KERN_ERR "mousedev: no more free mousedev devices\n");
|
||||||
return -ENFILE;
|
return -ENFILE;
|
||||||
|
@ -779,21 +976,13 @@ static int mousedev_connect(struct input_handler *handler, struct input_dev *dev
|
||||||
if (IS_ERR(mousedev))
|
if (IS_ERR(mousedev))
|
||||||
return PTR_ERR(mousedev);
|
return PTR_ERR(mousedev);
|
||||||
|
|
||||||
error = input_register_handle(&mousedev->handle);
|
|
||||||
if (error)
|
|
||||||
goto err_delete_mousedev;
|
|
||||||
|
|
||||||
error = mixdev_add_device(mousedev);
|
error = mixdev_add_device(mousedev);
|
||||||
if (error)
|
if (error) {
|
||||||
goto err_unregister_handle;
|
mousedev_destroy(mousedev);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_unregister_handle:
|
|
||||||
input_unregister_handle(&mousedev->handle);
|
|
||||||
err_delete_mousedev:
|
|
||||||
device_unregister(&mousedev->dev);
|
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mousedev_disconnect(struct input_handle *handle)
|
static void mousedev_disconnect(struct input_handle *handle)
|
||||||
|
@ -801,33 +990,42 @@ static void mousedev_disconnect(struct input_handle *handle)
|
||||||
struct mousedev *mousedev = handle->private;
|
struct mousedev *mousedev = handle->private;
|
||||||
|
|
||||||
mixdev_remove_device(mousedev);
|
mixdev_remove_device(mousedev);
|
||||||
input_unregister_handle(handle);
|
|
||||||
mousedev_destroy(mousedev);
|
mousedev_destroy(mousedev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct input_device_id mousedev_ids[] = {
|
static const struct input_device_id mousedev_ids[] = {
|
||||||
{
|
{
|
||||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
|
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
|
||||||
|
INPUT_DEVICE_ID_MATCH_KEYBIT |
|
||||||
|
INPUT_DEVICE_ID_MATCH_RELBIT,
|
||||||
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
|
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
|
||||||
.keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) },
|
.keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) },
|
||||||
.relbit = { BIT(REL_X) | BIT(REL_Y) },
|
.relbit = { BIT(REL_X) | BIT(REL_Y) },
|
||||||
}, /* A mouse like device, at least one button, two relative axes */
|
}, /* A mouse like device, at least one button,
|
||||||
|
two relative axes */
|
||||||
{
|
{
|
||||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
|
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
|
||||||
|
INPUT_DEVICE_ID_MATCH_RELBIT,
|
||||||
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
|
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
|
||||||
.relbit = { BIT(REL_WHEEL) },
|
.relbit = { BIT(REL_WHEEL) },
|
||||||
}, /* A separate scrollwheel */
|
}, /* A separate scrollwheel */
|
||||||
{
|
{
|
||||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
|
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
|
||||||
|
INPUT_DEVICE_ID_MATCH_KEYBIT |
|
||||||
|
INPUT_DEVICE_ID_MATCH_ABSBIT,
|
||||||
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
|
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
|
||||||
.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
|
.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
|
||||||
.absbit = { BIT(ABS_X) | BIT(ABS_Y) },
|
.absbit = { BIT(ABS_X) | BIT(ABS_Y) },
|
||||||
}, /* A tablet like device, at least touch detection, two absolute axes */
|
}, /* 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,
|
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
|
||||||
|
INPUT_DEVICE_ID_MATCH_KEYBIT |
|
||||||
|
INPUT_DEVICE_ID_MATCH_ABSBIT,
|
||||||
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
|
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
|
||||||
.keybit = { [LONG(BTN_TOOL_FINGER)] = BIT(BTN_TOOL_FINGER) },
|
.keybit = { [LONG(BTN_TOOL_FINGER)] = BIT(BTN_TOOL_FINGER) },
|
||||||
.absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_TOOL_WIDTH) },
|
.absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) |
|
||||||
|
BIT(ABS_TOOL_WIDTH) },
|
||||||
}, /* A touchpad */
|
}, /* A touchpad */
|
||||||
|
|
||||||
{ }, /* Terminating entry */
|
{ }, /* Terminating entry */
|
||||||
|
|
|
@ -385,6 +385,8 @@ static int i8042_enable_kbd_port(void)
|
||||||
i8042_ctr |= I8042_CTR_KBDINT;
|
i8042_ctr |= I8042_CTR_KBDINT;
|
||||||
|
|
||||||
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
|
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
|
||||||
|
i8042_ctr &= ~I8042_CTR_KBDINT;
|
||||||
|
i8042_ctr |= I8042_CTR_KBDDIS;
|
||||||
printk(KERN_ERR "i8042.c: Failed to enable KBD port.\n");
|
printk(KERN_ERR "i8042.c: Failed to enable KBD port.\n");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -402,6 +404,8 @@ static int i8042_enable_aux_port(void)
|
||||||
i8042_ctr |= I8042_CTR_AUXINT;
|
i8042_ctr |= I8042_CTR_AUXINT;
|
||||||
|
|
||||||
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
|
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
|
||||||
|
i8042_ctr &= ~I8042_CTR_AUXINT;
|
||||||
|
i8042_ctr |= I8042_CTR_AUXDIS;
|
||||||
printk(KERN_ERR "i8042.c: Failed to enable AUX port.\n");
|
printk(KERN_ERR "i8042.c: Failed to enable AUX port.\n");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,6 +126,16 @@ config TOUCHSCREEN_HP600
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called hp680_ts_input.
|
module will be called hp680_ts_input.
|
||||||
|
|
||||||
|
config TOUCHSCREEN_HP7XX
|
||||||
|
tristate "HP Jornada 710/720/728 touchscreen"
|
||||||
|
depends on SA1100_JORNADA720_SSP
|
||||||
|
help
|
||||||
|
Say Y here if you have a HP Jornada 710/720/728 and want
|
||||||
|
to support the built-in touchscreen.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called jornada720_ts.
|
||||||
|
|
||||||
config TOUCHSCREEN_PENMOUNT
|
config TOUCHSCREEN_PENMOUNT
|
||||||
tristate "Penmount serial touchscreen"
|
tristate "Penmount serial touchscreen"
|
||||||
select SERIO
|
select SERIO
|
||||||
|
@ -191,6 +201,7 @@ config TOUCHSCREEN_USB_COMPOSITE
|
||||||
- Gunze AHL61
|
- Gunze AHL61
|
||||||
- DMC TSC-10/25
|
- DMC TSC-10/25
|
||||||
- IRTOUCHSYSTEMS/UNITOP
|
- IRTOUCHSYSTEMS/UNITOP
|
||||||
|
- IdealTEK URTC1000
|
||||||
|
|
||||||
Have a look at <http://linux.chapter7.ch/touchkit/> for
|
Have a look at <http://linux.chapter7.ch/touchkit/> for
|
||||||
a usage description and the required user-space stuff.
|
a usage description and the required user-space stuff.
|
||||||
|
@ -238,4 +249,14 @@ config TOUCHSCREEN_USB_IRTOUCH
|
||||||
bool "IRTOUCHSYSTEMS/UNITOP device support" if EMBEDDED
|
bool "IRTOUCHSYSTEMS/UNITOP device support" if EMBEDDED
|
||||||
depends on TOUCHSCREEN_USB_COMPOSITE
|
depends on TOUCHSCREEN_USB_COMPOSITE
|
||||||
|
|
||||||
|
config TOUCHSCREEN_USB_IDEALTEK
|
||||||
|
default y
|
||||||
|
bool "IdealTEK URTC1000 device support" if EMBEDDED
|
||||||
|
depends on TOUCHSCREEN_USB_COMPOSITE
|
||||||
|
|
||||||
|
config TOUCHSCREEN_USB_GENERAL_TOUCH
|
||||||
|
default y
|
||||||
|
bool "GeneralTouch Touchscreen device support" if EMBEDDED
|
||||||
|
depends on TOUCHSCREEN_USB_COMPOSITE
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -13,6 +13,7 @@ obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
|
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
|
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
|
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
|
||||||
|
obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
|
obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
|
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
|
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
|
||||||
|
|
182
drivers/input/touchscreen/jornada720_ts.c
Normal file
182
drivers/input/touchscreen/jornada720_ts.c
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
/*
|
||||||
|
* drivers/input/touchscreen/jornada720_ts.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006 Filip Zyzniewski <filip.zyzniewski@tefnet.pl>
|
||||||
|
* based on HP Jornada 56x touchscreen driver by Alex Lange <chicken@handhelds.org>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* HP Jornada 710/720/729 Touchscreen Driver
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
|
||||||
|
#include <asm/hardware.h>
|
||||||
|
#include <asm/arch/jornada720.h>
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver");
|
||||||
|
MODULE_LICENSE("GPLv2");
|
||||||
|
|
||||||
|
struct jornada_ts {
|
||||||
|
struct input_dev *dev;
|
||||||
|
int x_data[4]; /* X sample values */
|
||||||
|
int y_data[4]; /* Y sample values */
|
||||||
|
};
|
||||||
|
|
||||||
|
static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* 3 low word X samples */
|
||||||
|
jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
|
||||||
|
jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
|
||||||
|
jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
|
||||||
|
|
||||||
|
/* 3 low word Y samples */
|
||||||
|
jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
|
||||||
|
jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
|
||||||
|
jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
|
||||||
|
|
||||||
|
/* combined x samples bits */
|
||||||
|
jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
|
||||||
|
|
||||||
|
/* combined y samples bits */
|
||||||
|
jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int jornada720_ts_average(int coords[4])
|
||||||
|
{
|
||||||
|
int coord, high_bits = coords[3];
|
||||||
|
|
||||||
|
coord = coords[0] | ((high_bits & 0x03) << 8);
|
||||||
|
coord += coords[1] | ((high_bits & 0x0c) << 6);
|
||||||
|
coord += coords[2] | ((high_bits & 0x30) << 4);
|
||||||
|
|
||||||
|
return coord / 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t jornada720_ts_interrupt(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = dev_id;
|
||||||
|
struct jornada_ts *jornada_ts = platform_get_drvdata(pdev);
|
||||||
|
struct input_dev *input = jornada_ts->dev;
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
/* If GPIO_GPIO9 is set to high then report pen up */
|
||||||
|
if (GPLR & GPIO_GPIO(9)) {
|
||||||
|
input_report_key(input, BTN_TOUCH, 0);
|
||||||
|
input_sync(input);
|
||||||
|
} else {
|
||||||
|
jornada_ssp_start();
|
||||||
|
|
||||||
|
/* proper reply to request is always TXDUMMY */
|
||||||
|
if (jornada_ssp_inout(GETTOUCHSAMPLES) == TXDUMMY) {
|
||||||
|
jornada720_ts_collect_data(jornada_ts);
|
||||||
|
|
||||||
|
x = jornada720_ts_average(jornada_ts->x_data);
|
||||||
|
y = jornada720_ts_average(jornada_ts->y_data);
|
||||||
|
|
||||||
|
input_report_key(input, BTN_TOUCH, 1);
|
||||||
|
input_report_abs(input, ABS_X, x);
|
||||||
|
input_report_abs(input, ABS_Y, y);
|
||||||
|
input_sync(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
jornada_ssp_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit jornada720_ts_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct jornada_ts *jornada_ts;
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
jornada_ts = kzalloc(sizeof(struct jornada_ts), GFP_KERNEL);
|
||||||
|
input_dev = input_allocate_device();
|
||||||
|
|
||||||
|
if (!jornada_ts || !input_dev) {
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, jornada_ts);
|
||||||
|
|
||||||
|
jornada_ts->dev = input_dev;
|
||||||
|
|
||||||
|
input_dev->name = "HP Jornada 7xx Touchscreen";
|
||||||
|
input_dev->phys = "jornadats/input0";
|
||||||
|
input_dev->id.bustype = BUS_HOST;
|
||||||
|
input_dev->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
|
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||||
|
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||||
|
input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0);
|
||||||
|
input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0);
|
||||||
|
|
||||||
|
error = request_irq(IRQ_GPIO9,
|
||||||
|
jornada720_ts_interrupt,
|
||||||
|
IRQF_DISABLED | IRQF_TRIGGER_RISING,
|
||||||
|
"HP7XX Touchscreen driver", pdev);
|
||||||
|
if (error) {
|
||||||
|
printk(KERN_INFO "HP7XX TS : Unable to acquire irq!\n");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = input_register_device(jornada_ts->dev);
|
||||||
|
if (error)
|
||||||
|
goto fail2;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail2:
|
||||||
|
free_irq(IRQ_GPIO9, pdev);
|
||||||
|
fail1:
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
input_free_device(input_dev);
|
||||||
|
kfree(jornada_ts);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit jornada720_ts_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct jornada_ts *jornada_ts = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
free_irq(IRQ_GPIO9, pdev);
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
input_unregister_device(jornada_ts->dev);
|
||||||
|
kfree(jornada_ts);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver jornada720_ts_driver = {
|
||||||
|
.probe = jornada720_ts_probe,
|
||||||
|
.remove = __devexit_p(jornada720_ts_remove),
|
||||||
|
.driver = {
|
||||||
|
.name = "jornada_ts",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init jornada720_ts_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&jornada720_ts_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit jornada720_ts_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&jornada720_ts_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(jornada720_ts_init);
|
||||||
|
module_exit(jornada720_ts_exit);
|
|
@ -130,8 +130,7 @@ static unsigned int ucb1400_adc_read(struct ucb1400 *ucb, u16 adc_channel)
|
||||||
if (val & UCB_ADC_DAT_VALID)
|
if (val & UCB_ADC_DAT_VALID)
|
||||||
break;
|
break;
|
||||||
/* yield to other processes */
|
/* yield to other processes */
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
schedule_timeout_uninterruptible(1);
|
||||||
schedule_timeout(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return UCB_ADC_DAT_VALUE(val);
|
return UCB_ADC_DAT_VALUE(val);
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
* - Gunze AHL61
|
* - Gunze AHL61
|
||||||
* - DMC TSC-10/25
|
* - DMC TSC-10/25
|
||||||
* - IRTOUCHSYSTEMS/UNITOP
|
* - IRTOUCHSYSTEMS/UNITOP
|
||||||
|
* - IdealTEK URTC1000
|
||||||
*
|
*
|
||||||
* Copyright (C) 2004-2006 by Daniel Ritz <daniel.ritz@gmx.ch>
|
* Copyright (C) 2004-2006 by Daniel Ritz <daniel.ritz@gmx.ch>
|
||||||
* Copyright (C) by Todd E. Johnson (mtouchusb.c)
|
* Copyright (C) by Todd E. Johnson (mtouchusb.c)
|
||||||
|
@ -92,7 +93,7 @@ struct usbtouch_usb {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#if defined(CONFIG_TOUCHSCREEN_USB_EGALAX) || defined(CONFIG_TOUCHSCREEN_USB_ETURBO)
|
#if defined(CONFIG_TOUCHSCREEN_USB_EGALAX) || defined(CONFIG_TOUCHSCREEN_USB_ETURBO) || defined(CONFIG_TOUCHSCREEN_USB_IDEALTEK)
|
||||||
#define MULTI_PACKET
|
#define MULTI_PACKET
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -112,6 +113,8 @@ enum {
|
||||||
DEVTYPE_GUNZE,
|
DEVTYPE_GUNZE,
|
||||||
DEVTYPE_DMC_TSC10,
|
DEVTYPE_DMC_TSC10,
|
||||||
DEVTYPE_IRTOUCH,
|
DEVTYPE_IRTOUCH,
|
||||||
|
DEVTYPE_IDEALTEK,
|
||||||
|
DEVTYPE_GENERAL_TOUCH,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct usb_device_id usbtouch_devices[] = {
|
static struct usb_device_id usbtouch_devices[] = {
|
||||||
|
@ -157,6 +160,14 @@ static struct usb_device_id usbtouch_devices[] = {
|
||||||
{USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
|
{USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
|
||||||
|
{USB_DEVICE(0x1391, 0x1000), .driver_info = DEVTYPE_IDEALTEK},
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
|
||||||
|
{USB_DEVICE(0x0dfc, 0x0001), .driver_info = DEVTYPE_GENERAL_TOUCH},
|
||||||
|
#endif
|
||||||
|
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -396,7 +407,8 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
|
||||||
TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
|
TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
if (buf[0] != 0x06 || buf[1] != 0x00)
|
if ((buf[0] != 0x06 || buf[1] != 0x00) &&
|
||||||
|
(buf[0] != 0x15 || buf[1] != 0x01))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
/* start sending data */
|
/* start sending data */
|
||||||
|
@ -437,6 +449,57 @@ static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* IdealTEK URTC1000 Part
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
|
||||||
|
static int idealtek_get_pkt_len(unsigned char *buf, int len)
|
||||||
|
{
|
||||||
|
if (buf[0] & 0x80)
|
||||||
|
return 5;
|
||||||
|
if (buf[0] == 0x01)
|
||||||
|
return len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int idealtek_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
||||||
|
{
|
||||||
|
switch (pkt[0] & 0x98) {
|
||||||
|
case 0x88:
|
||||||
|
/* touch data in IdealTEK mode */
|
||||||
|
dev->x = (pkt[1] << 5) | (pkt[2] >> 2);
|
||||||
|
dev->y = (pkt[3] << 5) | (pkt[4] >> 2);
|
||||||
|
dev->touch = (pkt[0] & 0x40) ? 1 : 0;
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case 0x98:
|
||||||
|
/* touch data in MT emulation mode */
|
||||||
|
dev->x = (pkt[2] << 5) | (pkt[1] >> 2);
|
||||||
|
dev->y = (pkt[4] << 5) | (pkt[3] >> 2);
|
||||||
|
dev->touch = (pkt[0] & 0x40) ? 1 : 0;
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* General Touch Part
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
|
||||||
|
static int general_touch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
||||||
|
{
|
||||||
|
dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1] ;
|
||||||
|
dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3] ;
|
||||||
|
dev->press = pkt[5] & 0xff;
|
||||||
|
dev->touch = pkt[0] & 0x01;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* the different device descriptors
|
* the different device descriptors
|
||||||
*/
|
*/
|
||||||
|
@ -537,6 +600,32 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
|
||||||
.read_data = irtouch_read_data,
|
.read_data = irtouch_read_data,
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
|
||||||
|
[DEVTYPE_IDEALTEK] = {
|
||||||
|
.min_xc = 0x0,
|
||||||
|
.max_xc = 0x0fff,
|
||||||
|
.min_yc = 0x0,
|
||||||
|
.max_yc = 0x0fff,
|
||||||
|
.rept_size = 8,
|
||||||
|
.flags = USBTOUCH_FLG_BUFFER,
|
||||||
|
.process_pkt = usbtouch_process_multi,
|
||||||
|
.get_pkt_len = idealtek_get_pkt_len,
|
||||||
|
.read_data = idealtek_read_data,
|
||||||
|
},
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
|
||||||
|
[DEVTYPE_GENERAL_TOUCH] = {
|
||||||
|
.min_xc = 0x0,
|
||||||
|
.max_xc = 0x0500,
|
||||||
|
.min_yc = 0x0,
|
||||||
|
.max_yc = 0x0500,
|
||||||
|
.rept_size = 7,
|
||||||
|
.read_data = general_touch_read_data,
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,533 +0,0 @@
|
||||||
/*
|
|
||||||
* $Id: tsdev.c,v 1.15 2002/04/10 16:50:19 jsimmons Exp $
|
|
||||||
*
|
|
||||||
* Copyright (c) 2001 "Crazy" james Simmons
|
|
||||||
*
|
|
||||||
* Compaq touchscreen protocol driver. The protocol emulated by this driver
|
|
||||||
* is obsolete; for new programs use the tslib library which can read directly
|
|
||||||
* from evdev and perform dejittering, variance filtering and calibration -
|
|
||||||
* all in user space, not at kernel level. The meaning of this driver is
|
|
||||||
* to allow usage of newer input drivers with old applications that use the
|
|
||||||
* old /dev/h3600_ts and /dev/h3600_tsraw devices.
|
|
||||||
*
|
|
||||||
* 09-Apr-2004: Andrew Zabolotny <zap@homelink.ru>
|
|
||||||
* Fixed to actually work, not just output random numbers.
|
|
||||||
* Added support for both h3600_ts and h3600_tsraw protocol
|
|
||||||
* emulation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 <jsimmons@infradead.org>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define TSDEV_MINOR_BASE 128
|
|
||||||
#define TSDEV_MINORS 32
|
|
||||||
/* First 16 devices are h3600_ts compatible; second 16 are h3600_tsraw */
|
|
||||||
#define TSDEV_MINOR_MASK 15
|
|
||||||
#define TSDEV_BUFFER_SIZE 64
|
|
||||||
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/poll.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/moduleparam.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/input.h>
|
|
||||||
#include <linux/major.h>
|
|
||||||
#include <linux/random.h>
|
|
||||||
#include <linux/time.h>
|
|
||||||
#include <linux/device.h>
|
|
||||||
|
|
||||||
#ifndef CONFIG_INPUT_TSDEV_SCREEN_X
|
|
||||||
#define CONFIG_INPUT_TSDEV_SCREEN_X 240
|
|
||||||
#endif
|
|
||||||
#ifndef CONFIG_INPUT_TSDEV_SCREEN_Y
|
|
||||||
#define CONFIG_INPUT_TSDEV_SCREEN_Y 320
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* This driver emulates both protocols of the old h3600_ts and h3600_tsraw
|
|
||||||
* devices. The first one must output X/Y data in 'cooked' format, e.g.
|
|
||||||
* filtered, dejittered and calibrated. Second device just outputs raw
|
|
||||||
* data received from the hardware.
|
|
||||||
*
|
|
||||||
* This driver doesn't support filtering and dejittering; it supports only
|
|
||||||
* calibration. Filtering and dejittering must be done in the low-level
|
|
||||||
* driver, if needed, because it may gain additional benefits from knowing
|
|
||||||
* the low-level details, the nature of noise and so on.
|
|
||||||
*
|
|
||||||
* The driver precomputes a calibration matrix given the initial xres and
|
|
||||||
* yres values (quite innacurate for most touchscreens) that will result
|
|
||||||
* in a more or less expected range of output values. The driver supports
|
|
||||||
* the TS_SET_CAL ioctl, which will replace the calibration matrix with a
|
|
||||||
* new one, supposedly generated from the values taken from the raw device.
|
|
||||||
*/
|
|
||||||
|
|
||||||
MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>");
|
|
||||||
MODULE_DESCRIPTION("Input driver to touchscreen converter");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
|
|
||||||
static int xres = CONFIG_INPUT_TSDEV_SCREEN_X;
|
|
||||||
module_param(xres, uint, 0);
|
|
||||||
MODULE_PARM_DESC(xres, "Horizontal screen resolution (can be negative for X-mirror)");
|
|
||||||
|
|
||||||
static int yres = CONFIG_INPUT_TSDEV_SCREEN_Y;
|
|
||||||
module_param(yres, uint, 0);
|
|
||||||
MODULE_PARM_DESC(yres, "Vertical screen resolution (can be negative for Y-mirror)");
|
|
||||||
|
|
||||||
/* From Compaq's Touch Screen Specification version 0.2 (draft) */
|
|
||||||
struct ts_event {
|
|
||||||
short pressure;
|
|
||||||
short x;
|
|
||||||
short y;
|
|
||||||
short millisecs;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ts_calibration {
|
|
||||||
int xscale;
|
|
||||||
int xtrans;
|
|
||||||
int yscale;
|
|
||||||
int ytrans;
|
|
||||||
int xyswap;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct tsdev {
|
|
||||||
int exist;
|
|
||||||
int open;
|
|
||||||
int minor;
|
|
||||||
char name[8];
|
|
||||||
struct input_handle handle;
|
|
||||||
wait_queue_head_t wait;
|
|
||||||
struct list_head client_list;
|
|
||||||
struct device dev;
|
|
||||||
|
|
||||||
int x, y, pressure;
|
|
||||||
struct ts_calibration cal;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct tsdev_client {
|
|
||||||
struct fasync_struct *fasync;
|
|
||||||
struct list_head node;
|
|
||||||
struct tsdev *tsdev;
|
|
||||||
int head, tail;
|
|
||||||
struct ts_event event[TSDEV_BUFFER_SIZE];
|
|
||||||
int raw;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The following ioctl codes are defined ONLY for backward compatibility.
|
|
||||||
* Don't use tsdev for new developement; use the tslib library instead.
|
|
||||||
* Touchscreen calibration is a fully userspace task.
|
|
||||||
*/
|
|
||||||
/* Use 'f' as magic number */
|
|
||||||
#define IOC_H3600_TS_MAGIC 'f'
|
|
||||||
#define TS_GET_CAL _IOR(IOC_H3600_TS_MAGIC, 10, struct ts_calibration)
|
|
||||||
#define TS_SET_CAL _IOW(IOC_H3600_TS_MAGIC, 11, struct ts_calibration)
|
|
||||||
|
|
||||||
static struct tsdev *tsdev_table[TSDEV_MINORS/2];
|
|
||||||
|
|
||||||
static int tsdev_fasync(int fd, struct file *file, int on)
|
|
||||||
{
|
|
||||||
struct tsdev_client *client = file->private_data;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = fasync_helper(fd, file, on, &client->fasync);
|
|
||||||
return retval < 0 ? retval : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int tsdev_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
int i = iminor(inode) - TSDEV_MINOR_BASE;
|
|
||||||
struct tsdev_client *client;
|
|
||||||
struct tsdev *tsdev;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
printk(KERN_WARNING "tsdev (compaq touchscreen emulation) is scheduled "
|
|
||||||
"for removal.\nSee Documentation/feature-removal-schedule.txt "
|
|
||||||
"for details.\n");
|
|
||||||
|
|
||||||
if (i >= TSDEV_MINORS)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
tsdev = tsdev_table[i & TSDEV_MINOR_MASK];
|
|
||||||
if (!tsdev || !tsdev->exist)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
get_device(&tsdev->dev);
|
|
||||||
|
|
||||||
client = kzalloc(sizeof(struct tsdev_client), GFP_KERNEL);
|
|
||||||
if (!client) {
|
|
||||||
error = -ENOMEM;
|
|
||||||
goto err_put_tsdev;
|
|
||||||
}
|
|
||||||
|
|
||||||
client->tsdev = tsdev;
|
|
||||||
client->raw = (i >= TSDEV_MINORS / 2) ? 1 : 0;
|
|
||||||
list_add_tail(&client->node, &tsdev->client_list);
|
|
||||||
|
|
||||||
if (!tsdev->open++ && tsdev->exist) {
|
|
||||||
error = input_open_device(&tsdev->handle);
|
|
||||||
if (error)
|
|
||||||
goto err_free_client;
|
|
||||||
}
|
|
||||||
|
|
||||||
file->private_data = client;
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_free_client:
|
|
||||||
list_del(&client->node);
|
|
||||||
kfree(client);
|
|
||||||
err_put_tsdev:
|
|
||||||
put_device(&tsdev->dev);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tsdev_free(struct device *dev)
|
|
||||||
{
|
|
||||||
struct tsdev *tsdev = container_of(dev, struct tsdev, dev);
|
|
||||||
|
|
||||||
tsdev_table[tsdev->minor] = NULL;
|
|
||||||
kfree(tsdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int tsdev_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct tsdev_client *client = file->private_data;
|
|
||||||
struct tsdev *tsdev = client->tsdev;
|
|
||||||
|
|
||||||
tsdev_fasync(-1, file, 0);
|
|
||||||
|
|
||||||
list_del(&client->node);
|
|
||||||
kfree(client);
|
|
||||||
|
|
||||||
if (!--tsdev->open && tsdev->exist)
|
|
||||||
input_close_device(&tsdev->handle);
|
|
||||||
|
|
||||||
put_device(&tsdev->dev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count,
|
|
||||||
loff_t *ppos)
|
|
||||||
{
|
|
||||||
struct tsdev_client *client = file->private_data;
|
|
||||||
struct tsdev *tsdev = client->tsdev;
|
|
||||||
int retval = 0;
|
|
||||||
|
|
||||||
if (client->head == client->tail && tsdev->exist && (file->f_flags & O_NONBLOCK))
|
|
||||||
return -EAGAIN;
|
|
||||||
|
|
||||||
retval = wait_event_interruptible(tsdev->wait,
|
|
||||||
client->head != client->tail || !tsdev->exist);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
if (!tsdev->exist)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
while (client->head != client->tail &&
|
|
||||||
retval + sizeof (struct ts_event) <= count) {
|
|
||||||
if (copy_to_user (buffer + retval, client->event + client->tail,
|
|
||||||
sizeof (struct ts_event)))
|
|
||||||
return -EFAULT;
|
|
||||||
client->tail = (client->tail + 1) & (TSDEV_BUFFER_SIZE - 1);
|
|
||||||
retval += sizeof (struct ts_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No kernel lock - fine */
|
|
||||||
static unsigned int tsdev_poll(struct file *file, poll_table *wait)
|
|
||||||
{
|
|
||||||
struct tsdev_client *client = file->private_data;
|
|
||||||
struct tsdev *tsdev = client->tsdev;
|
|
||||||
|
|
||||||
poll_wait(file, &tsdev->wait, wait);
|
|
||||||
return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) |
|
|
||||||
(tsdev->exist ? 0 : (POLLHUP | POLLERR));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int tsdev_ioctl(struct inode *inode, struct file *file,
|
|
||||||
unsigned int cmd, unsigned long arg)
|
|
||||||
{
|
|
||||||
struct tsdev_client *client = file->private_data;
|
|
||||||
struct tsdev *tsdev = client->tsdev;
|
|
||||||
int retval = 0;
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case TS_GET_CAL:
|
|
||||||
if (copy_to_user((void __user *)arg, &tsdev->cal,
|
|
||||||
sizeof (struct ts_calibration)))
|
|
||||||
retval = -EFAULT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_SET_CAL:
|
|
||||||
if (copy_from_user(&tsdev->cal, (void __user *)arg,
|
|
||||||
sizeof (struct ts_calibration)))
|
|
||||||
retval = -EFAULT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
retval = -EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct file_operations tsdev_fops = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.open = tsdev_open,
|
|
||||||
.release = tsdev_release,
|
|
||||||
.read = tsdev_read,
|
|
||||||
.poll = tsdev_poll,
|
|
||||||
.fasync = tsdev_fasync,
|
|
||||||
.ioctl = tsdev_ioctl,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void tsdev_event(struct input_handle *handle, unsigned int type,
|
|
||||||
unsigned int code, int value)
|
|
||||||
{
|
|
||||||
struct tsdev *tsdev = handle->private;
|
|
||||||
struct tsdev_client *client;
|
|
||||||
struct timeval time;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case EV_ABS:
|
|
||||||
switch (code) {
|
|
||||||
case ABS_X:
|
|
||||||
tsdev->x = value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ABS_Y:
|
|
||||||
tsdev->y = value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ABS_PRESSURE:
|
|
||||||
if (value > handle->dev->absmax[ABS_PRESSURE])
|
|
||||||
value = handle->dev->absmax[ABS_PRESSURE];
|
|
||||||
value -= handle->dev->absmin[ABS_PRESSURE];
|
|
||||||
if (value < 0)
|
|
||||||
value = 0;
|
|
||||||
tsdev->pressure = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EV_REL:
|
|
||||||
switch (code) {
|
|
||||||
case REL_X:
|
|
||||||
tsdev->x += value;
|
|
||||||
if (tsdev->x < 0)
|
|
||||||
tsdev->x = 0;
|
|
||||||
else if (tsdev->x > xres)
|
|
||||||
tsdev->x = xres;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REL_Y:
|
|
||||||
tsdev->y += value;
|
|
||||||
if (tsdev->y < 0)
|
|
||||||
tsdev->y = 0;
|
|
||||||
else if (tsdev->y > yres)
|
|
||||||
tsdev->y = yres;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EV_KEY:
|
|
||||||
if (code == BTN_TOUCH || code == BTN_MOUSE) {
|
|
||||||
switch (value) {
|
|
||||||
case 0:
|
|
||||||
tsdev->pressure = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
if (!tsdev->pressure)
|
|
||||||
tsdev->pressure = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type != EV_SYN || code != SYN_REPORT)
|
|
||||||
return;
|
|
||||||
|
|
||||||
list_for_each_entry(client, &tsdev->client_list, node) {
|
|
||||||
int x, y, tmp;
|
|
||||||
|
|
||||||
do_gettimeofday(&time);
|
|
||||||
client->event[client->head].millisecs = time.tv_usec / 1000;
|
|
||||||
client->event[client->head].pressure = tsdev->pressure;
|
|
||||||
|
|
||||||
x = tsdev->x;
|
|
||||||
y = tsdev->y;
|
|
||||||
|
|
||||||
/* Calibration */
|
|
||||||
if (!client->raw) {
|
|
||||||
x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans;
|
|
||||||
y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans;
|
|
||||||
if (tsdev->cal.xyswap) {
|
|
||||||
tmp = x; x = y; y = tmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
client->event[client->head].x = x;
|
|
||||||
client->event[client->head].y = y;
|
|
||||||
client->head = (client->head + 1) & (TSDEV_BUFFER_SIZE - 1);
|
|
||||||
kill_fasync(&client->fasync, SIGIO, POLL_IN);
|
|
||||||
}
|
|
||||||
wake_up_interruptible(&tsdev->wait);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int tsdev_connect(struct input_handler *handler, struct input_dev *dev,
|
|
||||||
const struct input_device_id *id)
|
|
||||||
{
|
|
||||||
struct tsdev *tsdev;
|
|
||||||
int minor, delta;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
for (minor = 0; minor < TSDEV_MINORS / 2 && tsdev_table[minor]; minor++);
|
|
||||||
if (minor >= TSDEV_MINORS / 2) {
|
|
||||||
printk(KERN_ERR
|
|
||||||
"tsdev: You have way too many touchscreens\n");
|
|
||||||
return -ENFILE;
|
|
||||||
}
|
|
||||||
|
|
||||||
tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL);
|
|
||||||
if (!tsdev)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
INIT_LIST_HEAD(&tsdev->client_list);
|
|
||||||
init_waitqueue_head(&tsdev->wait);
|
|
||||||
|
|
||||||
tsdev->exist = 1;
|
|
||||||
tsdev->minor = minor;
|
|
||||||
tsdev->handle.dev = dev;
|
|
||||||
tsdev->handle.name = tsdev->name;
|
|
||||||
tsdev->handle.handler = handler;
|
|
||||||
tsdev->handle.private = tsdev;
|
|
||||||
snprintf(tsdev->name, sizeof(tsdev->name), "ts%d", minor);
|
|
||||||
|
|
||||||
/* Precompute the rough calibration matrix */
|
|
||||||
delta = dev->absmax [ABS_X] - dev->absmin [ABS_X] + 1;
|
|
||||||
if (delta == 0)
|
|
||||||
delta = 1;
|
|
||||||
tsdev->cal.xscale = (xres << 8) / delta;
|
|
||||||
tsdev->cal.xtrans = - ((dev->absmin [ABS_X] * tsdev->cal.xscale) >> 8);
|
|
||||||
|
|
||||||
delta = dev->absmax [ABS_Y] - dev->absmin [ABS_Y] + 1;
|
|
||||||
if (delta == 0)
|
|
||||||
delta = 1;
|
|
||||||
tsdev->cal.yscale = (yres << 8) / delta;
|
|
||||||
tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8);
|
|
||||||
|
|
||||||
snprintf(tsdev->dev.bus_id, sizeof(tsdev->dev.bus_id),
|
|
||||||
"ts%d", minor);
|
|
||||||
tsdev->dev.class = &input_class;
|
|
||||||
tsdev->dev.parent = &dev->dev;
|
|
||||||
tsdev->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor);
|
|
||||||
tsdev->dev.release = tsdev_free;
|
|
||||||
device_initialize(&tsdev->dev);
|
|
||||||
|
|
||||||
tsdev_table[minor] = tsdev;
|
|
||||||
|
|
||||||
error = device_add(&tsdev->dev);
|
|
||||||
if (error)
|
|
||||||
goto err_free_tsdev;
|
|
||||||
|
|
||||||
error = input_register_handle(&tsdev->handle);
|
|
||||||
if (error)
|
|
||||||
goto err_delete_tsdev;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_delete_tsdev:
|
|
||||||
device_del(&tsdev->dev);
|
|
||||||
err_free_tsdev:
|
|
||||||
put_device(&tsdev->dev);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tsdev_disconnect(struct input_handle *handle)
|
|
||||||
{
|
|
||||||
struct tsdev *tsdev = handle->private;
|
|
||||||
struct tsdev_client *client;
|
|
||||||
|
|
||||||
input_unregister_handle(handle);
|
|
||||||
device_del(&tsdev->dev);
|
|
||||||
|
|
||||||
tsdev->exist = 0;
|
|
||||||
|
|
||||||
if (tsdev->open) {
|
|
||||||
input_close_device(handle);
|
|
||||||
list_for_each_entry(client, &tsdev->client_list, node)
|
|
||||||
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
|
|
||||||
wake_up_interruptible(&tsdev->wait);
|
|
||||||
}
|
|
||||||
|
|
||||||
put_device(&tsdev->dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct input_device_id tsdev_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_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_ABSBIT,
|
|
||||||
.evbit = { BIT(EV_ABS) },
|
|
||||||
.absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) },
|
|
||||||
}, /* A tablet like device with several gradations of pressure */
|
|
||||||
|
|
||||||
{} /* Terminating entry */
|
|
||||||
};
|
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(input, tsdev_ids);
|
|
||||||
|
|
||||||
static struct input_handler tsdev_handler = {
|
|
||||||
.event = tsdev_event,
|
|
||||||
.connect = tsdev_connect,
|
|
||||||
.disconnect = tsdev_disconnect,
|
|
||||||
.fops = &tsdev_fops,
|
|
||||||
.minor = TSDEV_MINOR_BASE,
|
|
||||||
.name = "tsdev",
|
|
||||||
.id_table = tsdev_ids,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init tsdev_init(void)
|
|
||||||
{
|
|
||||||
return input_register_handler(&tsdev_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit tsdev_exit(void)
|
|
||||||
{
|
|
||||||
input_unregister_handler(&tsdev_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(tsdev_init);
|
|
||||||
module_exit(tsdev_exit);
|
|
|
@ -172,6 +172,7 @@ config INPUT_ADBHID
|
||||||
|
|
||||||
config MAC_EMUMOUSEBTN
|
config MAC_EMUMOUSEBTN
|
||||||
bool "Support for mouse button 2+3 emulation"
|
bool "Support for mouse button 2+3 emulation"
|
||||||
|
select INPUT
|
||||||
help
|
help
|
||||||
This provides generic support for emulating the 2nd and 3rd mouse
|
This provides generic support for emulating the 2nd and 3rd mouse
|
||||||
button with keypresses. If you say Y here, the emulation is still
|
button with keypresses. If you say Y here, the emulation is still
|
||||||
|
|
|
@ -52,6 +52,11 @@
|
||||||
|
|
||||||
MODULE_AUTHOR("Franz Sirl <Franz.Sirl-kernel@lauterbach.com>");
|
MODULE_AUTHOR("Franz Sirl <Franz.Sirl-kernel@lauterbach.com>");
|
||||||
|
|
||||||
|
static int restore_capslock_events;
|
||||||
|
module_param(restore_capslock_events, int, 0644);
|
||||||
|
MODULE_PARM_DESC(restore_capslock_events,
|
||||||
|
"Produce keypress events for capslock on both keyup and keydown.");
|
||||||
|
|
||||||
#define KEYB_KEYREG 0 /* register # for key up/down data */
|
#define KEYB_KEYREG 0 /* register # for key up/down data */
|
||||||
#define KEYB_LEDREG 2 /* register # for leds on ADB keyboard */
|
#define KEYB_LEDREG 2 /* register # for leds on ADB keyboard */
|
||||||
#define MOUSE_DATAREG 0 /* reg# for movement/button codes from mouse */
|
#define MOUSE_DATAREG 0 /* reg# for movement/button codes from mouse */
|
||||||
|
@ -217,6 +222,8 @@ struct adbhid {
|
||||||
#define FLAG_FN_KEY_PRESSED 0x00000001
|
#define FLAG_FN_KEY_PRESSED 0x00000001
|
||||||
#define FLAG_POWER_FROM_FN 0x00000002
|
#define FLAG_POWER_FROM_FN 0x00000002
|
||||||
#define FLAG_EMU_FWDEL_DOWN 0x00000004
|
#define FLAG_EMU_FWDEL_DOWN 0x00000004
|
||||||
|
#define FLAG_CAPSLOCK_TRANSLATE 0x00000008
|
||||||
|
#define FLAG_CAPSLOCK_DOWN 0x00000010
|
||||||
|
|
||||||
static struct adbhid *adbhid[16];
|
static struct adbhid *adbhid[16];
|
||||||
|
|
||||||
|
@ -272,19 +279,50 @@ adbhid_keyboard_input(unsigned char *data, int nb, int apoll)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
adbhid_input_keycode(int id, int keycode, int repeat)
|
adbhid_input_keycode(int id, int scancode, int repeat)
|
||||||
{
|
{
|
||||||
struct adbhid *ahid = adbhid[id];
|
struct adbhid *ahid = adbhid[id];
|
||||||
int up_flag, key;
|
int keycode, up_flag;
|
||||||
|
|
||||||
up_flag = (keycode & 0x80);
|
keycode = scancode & 0x7f;
|
||||||
keycode &= 0x7f;
|
up_flag = scancode & 0x80;
|
||||||
|
|
||||||
|
if (restore_capslock_events) {
|
||||||
|
if (keycode == ADB_KEY_CAPSLOCK && !up_flag) {
|
||||||
|
/* Key pressed, turning on the CapsLock LED.
|
||||||
|
* The next 0xff will be interpreted as a release. */
|
||||||
|
ahid->flags |= FLAG_CAPSLOCK_TRANSLATE
|
||||||
|
| FLAG_CAPSLOCK_DOWN;
|
||||||
|
} else if (scancode == 0xff) {
|
||||||
|
/* Scancode 0xff usually signifies that the capslock
|
||||||
|
* key was either pressed or released. */
|
||||||
|
if (ahid->flags & FLAG_CAPSLOCK_TRANSLATE) {
|
||||||
|
keycode = ADB_KEY_CAPSLOCK;
|
||||||
|
if (ahid->flags & FLAG_CAPSLOCK_DOWN) {
|
||||||
|
/* Key released */
|
||||||
|
up_flag = 1;
|
||||||
|
ahid->flags &= ~FLAG_CAPSLOCK_DOWN;
|
||||||
|
} else {
|
||||||
|
/* Key pressed */
|
||||||
|
up_flag = 0;
|
||||||
|
ahid->flags &= ~FLAG_CAPSLOCK_TRANSLATE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printk(KERN_INFO "Spurious caps lock event "
|
||||||
|
"(scancode 0xff).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (keycode) {
|
switch (keycode) {
|
||||||
case ADB_KEY_CAPSLOCK: /* Generate down/up events for CapsLock everytime. */
|
case ADB_KEY_CAPSLOCK:
|
||||||
|
if (!restore_capslock_events) {
|
||||||
|
/* Generate down/up events for CapsLock everytime. */
|
||||||
input_report_key(ahid->input, KEY_CAPSLOCK, 1);
|
input_report_key(ahid->input, KEY_CAPSLOCK, 1);
|
||||||
|
input_sync(ahid->input);
|
||||||
input_report_key(ahid->input, KEY_CAPSLOCK, 0);
|
input_report_key(ahid->input, KEY_CAPSLOCK, 0);
|
||||||
input_sync(ahid->input);
|
input_sync(ahid->input);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
#ifdef CONFIG_PPC_PMAC
|
#ifdef CONFIG_PPC_PMAC
|
||||||
case ADB_KEY_POWER_OLD: /* Power key on PBook 3400 needs remapping */
|
case ADB_KEY_POWER_OLD: /* Power key on PBook 3400 needs remapping */
|
||||||
|
|
17
include/asm-blackfin/mach-bf548/bf54x_keys.h
Normal file
17
include/asm-blackfin/mach-bf548/bf54x_keys.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef _BFIN_KPAD_H
|
||||||
|
#define _BFIN_KPAD_H
|
||||||
|
|
||||||
|
struct bfin_kpad_platform_data {
|
||||||
|
int rows;
|
||||||
|
int cols;
|
||||||
|
const unsigned int *keymap;
|
||||||
|
unsigned short keymapsize;
|
||||||
|
unsigned short repeat;
|
||||||
|
u32 debounce_time; /* in ns */
|
||||||
|
u32 coldrive_time; /* in ns */
|
||||||
|
u32 keyup_test_interval; /* in ms */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define KEYVAL(col, row, val) (((1 << col) << 24) | ((1 << row) << 16) | (val))
|
||||||
|
|
||||||
|
#endif
|
|
@ -8,6 +8,7 @@ struct gpio_keys_button {
|
||||||
int active_low;
|
int active_low;
|
||||||
char *desc;
|
char *desc;
|
||||||
int type; /* input event type (EV_KEY, EV_SW) */
|
int type; /* input event type (EV_KEY, EV_SW) */
|
||||||
|
int wakeup; /* configure the button as a wake-up source */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct gpio_keys_platform_data {
|
struct gpio_keys_platform_data {
|
||||||
|
|
|
@ -856,7 +856,7 @@ struct ff_rumble_effect {
|
||||||
* defining effect parameters
|
* defining effect parameters
|
||||||
*
|
*
|
||||||
* This structure is sent through ioctl from the application to the driver.
|
* This structure is sent through ioctl from the application to the driver.
|
||||||
* To create a new effect aplication should set its @id to -1; the kernel
|
* To create a new effect application should set its @id to -1; the kernel
|
||||||
* will return assigned @id which can later be used to update or delete
|
* will return assigned @id which can later be used to update or delete
|
||||||
* this effect.
|
* this effect.
|
||||||
*
|
*
|
||||||
|
@ -936,9 +936,82 @@ struct ff_effect {
|
||||||
#define BIT(x) (1UL<<((x)%BITS_PER_LONG))
|
#define BIT(x) (1UL<<((x)%BITS_PER_LONG))
|
||||||
#define LONG(x) ((x)/BITS_PER_LONG)
|
#define LONG(x) ((x)/BITS_PER_LONG)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct input_dev - represents an input device
|
||||||
|
* @name: name of the device
|
||||||
|
* @phys: physical path to the device in the system hierarchy
|
||||||
|
* @uniq: unique identification code for the device (if device has it)
|
||||||
|
* @id: id of the device (struct input_id)
|
||||||
|
* @evbit: bitmap of types of events supported by the device (EV_KEY,
|
||||||
|
* EV_REL, etc.)
|
||||||
|
* @keybit: bitmap of keys/buttons this device has
|
||||||
|
* @relbit: bitmap of relative axes for the device
|
||||||
|
* @absbit: bitmap of absolute axes for the device
|
||||||
|
* @mscbit: bitmap of miscellaneous events supported by the device
|
||||||
|
* @ledbit: bitmap of leds present on the device
|
||||||
|
* @sndbit: bitmap of sound effects supported by the device
|
||||||
|
* @ffbit: bitmap of force feedback effects supported by the device
|
||||||
|
* @swbit: bitmap of switches present on the device
|
||||||
|
* @keycodemax: size of keycode table
|
||||||
|
* @keycodesize: size of elements in keycode table
|
||||||
|
* @keycode: map of scancodes to keycodes for this device
|
||||||
|
* @setkeycode: optional method to alter current keymap, used to implement
|
||||||
|
* sparse keymaps. If not supplied default mechanism will be used
|
||||||
|
* @getkeycode: optional method to retrieve current keymap. If not supplied
|
||||||
|
* default mechanism will be used
|
||||||
|
* @ff: force feedback structure associated with the device if device
|
||||||
|
* supports force feedback effects
|
||||||
|
* @repeat_key: stores key code of the last key pressed; used to implement
|
||||||
|
* software autorepeat
|
||||||
|
* @timer: timer for software autorepeat
|
||||||
|
* @sync: set to 1 when there were no new events since last EV_SYNC
|
||||||
|
* @abs: current values for reports from absolute axes
|
||||||
|
* @rep: current values for autorepeat parameters (delay, rate)
|
||||||
|
* @key: reflects current state of device's keys/buttons
|
||||||
|
* @led: reflects current state of device's LEDs
|
||||||
|
* @snd: reflects current state of sound effects
|
||||||
|
* @sw: reflects current state of device's switches
|
||||||
|
* @absmax: maximum values for events coming from absolute axes
|
||||||
|
* @absmin: minimum values for events coming from absolute axes
|
||||||
|
* @absfuzz: describes noisiness for axes
|
||||||
|
* @absflat: size of the center flat position (used by joydev)
|
||||||
|
* @open: this method is called when the very first user calls
|
||||||
|
* input_open_device(). The driver must prepare the device
|
||||||
|
* to start generating events (start polling thread,
|
||||||
|
* request an IRQ, submit URB, etc.)
|
||||||
|
* @close: this method is called when the very last user calls
|
||||||
|
* input_close_device().
|
||||||
|
* @flush: purges the device. Most commonly used to get rid of force
|
||||||
|
* feedback effects loaded into the device when disconnecting
|
||||||
|
* from it
|
||||||
|
* @event: event handler for events sent _to_ the device, like EV_LED
|
||||||
|
* or EV_SND. The device is expected to carry out the requested
|
||||||
|
* action (turn on a LED, play sound, etc.) The call is protected
|
||||||
|
* by @event_lock and must not sleep
|
||||||
|
* @grab: input handle that currently has the device grabbed (via
|
||||||
|
* EVIOCGRAB ioctl). When a handle grabs a device it becomes sole
|
||||||
|
* recipient for all input events coming from the device
|
||||||
|
* @event_lock: this spinlock is is taken when input core receives
|
||||||
|
* and processes a new event for the device (in input_event()).
|
||||||
|
* Code that accesses and/or modifies parameters of a device
|
||||||
|
* (such as keymap or absmin, absmax, absfuzz, etc.) after device
|
||||||
|
* has been registered with input core must take this lock.
|
||||||
|
* @mutex: serializes calls to open(), close() and flush() methods
|
||||||
|
* @users: stores number of users (input handlers) that opened this
|
||||||
|
* device. It is used by input_open_device() and input_close_device()
|
||||||
|
* to make sure that dev->open() is only called when the first
|
||||||
|
* user opens device and dev->close() is called when the very
|
||||||
|
* last user closes the device
|
||||||
|
* @going_away: marks devices that are in a middle of unregistering and
|
||||||
|
* causes input_open_device*() fail with -ENODEV.
|
||||||
|
* @dev: driver model's view of this device
|
||||||
|
* @h_list: list of input handles associated with the device. When
|
||||||
|
* accessing the list dev->mutex must be held
|
||||||
|
* @node: used to place the device onto input_dev_list
|
||||||
|
*/
|
||||||
struct input_dev {
|
struct input_dev {
|
||||||
|
|
||||||
void *private;
|
void *private; /* do not use */
|
||||||
|
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *phys;
|
const char *phys;
|
||||||
|
@ -966,8 +1039,6 @@ struct input_dev {
|
||||||
unsigned int repeat_key;
|
unsigned int repeat_key;
|
||||||
struct timer_list timer;
|
struct timer_list timer;
|
||||||
|
|
||||||
int state;
|
|
||||||
|
|
||||||
int sync;
|
int sync;
|
||||||
|
|
||||||
int abs[ABS_MAX + 1];
|
int abs[ABS_MAX + 1];
|
||||||
|
@ -990,8 +1061,11 @@ struct input_dev {
|
||||||
|
|
||||||
struct input_handle *grab;
|
struct input_handle *grab;
|
||||||
|
|
||||||
struct mutex mutex; /* serializes open and close operations */
|
spinlock_t event_lock;
|
||||||
|
struct mutex mutex;
|
||||||
|
|
||||||
unsigned int users;
|
unsigned int users;
|
||||||
|
int going_away;
|
||||||
|
|
||||||
struct device dev;
|
struct device dev;
|
||||||
union { /* temporarily so while we switching to struct device */
|
union { /* temporarily so while we switching to struct device */
|
||||||
|
@ -1057,7 +1131,9 @@ struct input_handle;
|
||||||
/**
|
/**
|
||||||
* struct input_handler - implements one of interfaces for input devices
|
* struct input_handler - implements one of interfaces for input devices
|
||||||
* @private: driver-specific data
|
* @private: driver-specific data
|
||||||
* @event: event handler
|
* @event: event handler. This method is being called by input core with
|
||||||
|
* interrupts disabled and dev->event_lock spinlock held and so
|
||||||
|
* it may not sleep
|
||||||
* @connect: called when attaching a handler to an input device
|
* @connect: called when attaching a handler to an input device
|
||||||
* @disconnect: disconnects a handler from input device
|
* @disconnect: disconnects a handler from input device
|
||||||
* @start: starts handler for given handle. This function is called by
|
* @start: starts handler for given handle. This function is called by
|
||||||
|
@ -1069,10 +1145,18 @@ struct input_handle;
|
||||||
* @name: name of the handler, to be shown in /proc/bus/input/handlers
|
* @name: name of the handler, to be shown in /proc/bus/input/handlers
|
||||||
* @id_table: pointer to a table of input_device_ids this driver can
|
* @id_table: pointer to a table of input_device_ids this driver can
|
||||||
* handle
|
* handle
|
||||||
* @blacklist: prointer to a table of input_device_ids this driver should
|
* @blacklist: pointer to a table of input_device_ids this driver should
|
||||||
* ignore even if they match @id_table
|
* ignore even if they match @id_table
|
||||||
* @h_list: list of input handles associated with the handler
|
* @h_list: list of input handles associated with the handler
|
||||||
* @node: for placing the driver onto input_handler_list
|
* @node: for placing the driver onto input_handler_list
|
||||||
|
*
|
||||||
|
* Input handlers attach to input devices and create input handles. There
|
||||||
|
* are likely several handlers attached to any given input device at the
|
||||||
|
* same time. All of them will get their copy of input event generated by
|
||||||
|
* the device.
|
||||||
|
*
|
||||||
|
* Note that input core serializes calls to connect() and disconnect()
|
||||||
|
* methods.
|
||||||
*/
|
*/
|
||||||
struct input_handler {
|
struct input_handler {
|
||||||
|
|
||||||
|
@ -1094,6 +1178,18 @@ struct input_handler {
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct input_handle - links input device with an input handler
|
||||||
|
* @private: handler-specific data
|
||||||
|
* @open: counter showing whether the handle is 'open', i.e. should deliver
|
||||||
|
* events from its device
|
||||||
|
* @name: name given to the handle by handler that created it
|
||||||
|
* @dev: input device the handle is attached to
|
||||||
|
* @handler: handler that works with the device through this handle
|
||||||
|
* @d_node: used to put the handle on device's list of attached handles
|
||||||
|
* @h_node: used to put the handle on handler's list of handles from which
|
||||||
|
* it gets events
|
||||||
|
*/
|
||||||
struct input_handle {
|
struct input_handle {
|
||||||
|
|
||||||
void *private;
|
void *private;
|
||||||
|
@ -1136,10 +1232,10 @@ static inline void input_set_drvdata(struct input_dev *dev, void *data)
|
||||||
dev->private = data;
|
dev->private = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
int input_register_device(struct input_dev *);
|
int __must_check input_register_device(struct input_dev *);
|
||||||
void input_unregister_device(struct input_dev *);
|
void input_unregister_device(struct input_dev *);
|
||||||
|
|
||||||
int input_register_handler(struct input_handler *);
|
int __must_check input_register_handler(struct input_handler *);
|
||||||
void input_unregister_handler(struct input_handler *);
|
void input_unregister_handler(struct input_handler *);
|
||||||
|
|
||||||
int input_register_handle(struct input_handle *);
|
int input_register_handle(struct input_handle *);
|
||||||
|
@ -1216,7 +1312,7 @@ extern struct class input_class;
|
||||||
* @max_effects: maximum number of effects supported by device
|
* @max_effects: maximum number of effects supported by device
|
||||||
* @effects: pointer to an array of effects currently loaded into device
|
* @effects: pointer to an array of effects currently loaded into device
|
||||||
* @effect_owners: array of effect owners; when file handle owning
|
* @effect_owners: array of effect owners; when file handle owning
|
||||||
* an effect gets closed the effcet is automatically erased
|
* an effect gets closed the effect is automatically erased
|
||||||
*
|
*
|
||||||
* Every force-feedback device must implement upload() and playback()
|
* Every force-feedback device must implement upload() and playback()
|
||||||
* methods; erase() is optional. set_gain() and set_autocenter() need
|
* methods; erase() is optional. set_gain() and set_autocenter() need
|
||||||
|
|
|
@ -416,6 +416,7 @@ extern unsigned short plain_map[NR_KEYS];
|
||||||
#define K_SHIFTRLOCK K(KT_LOCK,KG_SHIFTR)
|
#define K_SHIFTRLOCK K(KT_LOCK,KG_SHIFTR)
|
||||||
#define K_CTRLLLOCK K(KT_LOCK,KG_CTRLL)
|
#define K_CTRLLLOCK K(KT_LOCK,KG_CTRLL)
|
||||||
#define K_CTRLRLOCK K(KT_LOCK,KG_CTRLR)
|
#define K_CTRLRLOCK K(KT_LOCK,KG_CTRLR)
|
||||||
|
#define K_CAPSSHIFTLOCK K(KT_LOCK,KG_CAPSSHIFT)
|
||||||
|
|
||||||
#define K_SHIFT_SLOCK K(KT_SLOCK,KG_SHIFT)
|
#define K_SHIFT_SLOCK K(KT_SLOCK,KG_SHIFT)
|
||||||
#define K_CTRL_SLOCK K(KT_SLOCK,KG_CTRL)
|
#define K_CTRL_SLOCK K(KT_SLOCK,KG_CTRL)
|
||||||
|
@ -425,8 +426,9 @@ extern unsigned short plain_map[NR_KEYS];
|
||||||
#define K_SHIFTR_SLOCK K(KT_SLOCK,KG_SHIFTR)
|
#define K_SHIFTR_SLOCK K(KT_SLOCK,KG_SHIFTR)
|
||||||
#define K_CTRLL_SLOCK K(KT_SLOCK,KG_CTRLL)
|
#define K_CTRLL_SLOCK K(KT_SLOCK,KG_CTRLL)
|
||||||
#define K_CTRLR_SLOCK K(KT_SLOCK,KG_CTRLR)
|
#define K_CTRLR_SLOCK K(KT_SLOCK,KG_CTRLR)
|
||||||
|
#define K_CAPSSHIFT_SLOCK K(KT_SLOCK,KG_CAPSSHIFT)
|
||||||
|
|
||||||
#define NR_LOCK 8
|
#define NR_LOCK 9
|
||||||
|
|
||||||
#define K_BRL_BLANK K(KT_BRL, 0)
|
#define K_BRL_BLANK K(KT_BRL, 0)
|
||||||
#define K_BRL_DOT1 K(KT_BRL, 1)
|
#define K_BRL_DOT1 K(KT_BRL, 1)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue