Commit a0fe3cc5 authored by Linus Torvalds's avatar Linus Torvalds

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: psmouse - small formatting changes to better follow coding style
  Input: synaptics - set dimensions as reported by firmware
  Input: elantech - relax signature checks
  Input: elantech - enforce common prefix on messages
  Input: wistron_btns - switch to using kmemdup()
  Input: usbtouchscreen - switch to using kmemdup()
  Input: do not force selecting i8042 on Moorestown
  Input: Documentation/sysrq.txt - update KEY_SYSRQ info
  Input: 88pm860x_onkey - remove invalid irq number assignment
  Input: i8042 - add a PNP entry to the aux device list
  Input: i8042 - add some extra PNP keyboard types
  Input: wm9712 - fix wm97xx_set_gpio() logic
  Input: add keypad driver for keys interfaced to TCA6416
  Input: remove obsolete {corgi,spitz,tosa}kbd.c
  Input: kbtab - do not advertise unsupported events
  Input: kbtab - simplify kbtab_disconnect()
  Input: kbtab - fix incorrect size parameter in usb_buffer_free
  Input: acecad - don't advertise mouse events
  Input: acecad - fix some formatting issues
  Input: acecad - simplify usb_acecad_disconnect()
  ...

Trivial conflict in Documentation/feature-removal-schedule.txt
parents 04afb405 a62f0d27
...@@ -177,13 +177,13 @@ virtual console (ALT+Fn) and then back again should also help. ...@@ -177,13 +177,13 @@ virtual console (ALT+Fn) and then back again should also help.
* I hit SysRq, but nothing seems to happen, what's wrong? * I hit SysRq, but nothing seems to happen, what's wrong?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There are some keyboards that send different scancodes for SysRq than the There are some keyboards that produce a different keycode for SysRq than the
pre-defined 0x54. So if SysRq doesn't work out of the box for a certain pre-defined value of 99 (see KEY_SYSRQ in include/linux/input.h), or which
keyboard, run 'showkey -s' to find out the proper scancode sequence. Then don't have a SysRq key at all. In these cases, run 'showkey -s' to find an
use 'setkeycodes <sequence> 84' to define this sequence to the usual SysRq appropriate scancode sequence, and use 'setkeycodes <sequence> 99' to map
code (84 is decimal for 0x54). It's probably best to put this command in a this sequence to the usual SysRq code (e.g., 'setkeycodes e05b 99'). It's
boot script. Oh, and by the way, you exit 'showkey' by not typing anything probably best to put this command in a boot script. Oh, and by the way, you
for ten seconds. exit 'showkey' by not typing anything for ten seconds.
* I want to add SysRQ key events to a module, how does it work? * I want to add SysRQ key events to a module, how does it work?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
* 21-08-02: Converted to input API, major cleanup. (Vojtech Pavlik) * 21-08-02: Converted to input API, major cleanup. (Vojtech Pavlik)
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/consolemap.h> #include <linux/consolemap.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -38,7 +40,6 @@ ...@@ -38,7 +40,6 @@
#include <linux/kbd_kern.h> #include <linux/kbd_kern.h>
#include <linux/kbd_diacr.h> #include <linux/kbd_diacr.h>
#include <linux/vt_kern.h> #include <linux/vt_kern.h>
#include <linux/sysrq.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/notifier.h> #include <linux/notifier.h>
...@@ -82,8 +83,7 @@ void compute_shiftstate(void); ...@@ -82,8 +83,7 @@ void compute_shiftstate(void);
typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value, typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value,
char up_flag); char up_flag);
static k_handler_fn K_HANDLERS; static k_handler_fn K_HANDLERS;
k_handler_fn *k_handler[16] = { K_HANDLERS }; static k_handler_fn *k_handler[16] = { K_HANDLERS };
EXPORT_SYMBOL_GPL(k_handler);
#define FN_HANDLERS\ #define FN_HANDLERS\
fn_null, fn_enter, fn_show_ptregs, fn_show_mem,\ fn_null, fn_enter, fn_show_ptregs, fn_show_mem,\
...@@ -133,7 +133,7 @@ static struct input_handler kbd_handler; ...@@ -133,7 +133,7 @@ static struct input_handler kbd_handler;
static DEFINE_SPINLOCK(kbd_event_lock); static DEFINE_SPINLOCK(kbd_event_lock);
static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */ static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */
static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */ static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */
static int dead_key_next; static bool dead_key_next;
static int npadch = -1; /* -1 or number assembled on pad */ static int npadch = -1; /* -1 or number assembled on pad */
static unsigned int diacr; static unsigned int diacr;
static char rep; /* flag telling character repeat */ static char rep; /* flag telling character repeat */
...@@ -147,22 +147,6 @@ static struct ledptr { ...@@ -147,22 +147,6 @@ static struct ledptr {
unsigned char valid:1; unsigned char valid:1;
} ledptrs[3]; } ledptrs[3];
/* Simple translation table for the SysRq keys */
#ifdef CONFIG_MAGIC_SYSRQ
unsigned char kbd_sysrq_xlate[KEY_MAX + 1] =
"\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 */
static int sysrq_down;
static int sysrq_alt_use;
#endif
static int sysrq_alt;
/* /*
* Notifier list for console keyboard events * Notifier list for console keyboard events
*/ */
...@@ -361,8 +345,8 @@ static void to_utf8(struct vc_data *vc, uint c) ...@@ -361,8 +345,8 @@ static void to_utf8(struct vc_data *vc, uint c)
/* 110***** 10****** */ /* 110***** 10****** */
put_queue(vc, 0xc0 | (c >> 6)); put_queue(vc, 0xc0 | (c >> 6));
put_queue(vc, 0x80 | (c & 0x3f)); put_queue(vc, 0x80 | (c & 0x3f));
} else if (c < 0x10000) { } else if (c < 0x10000) {
if (c >= 0xD800 && c < 0xE000) if (c >= 0xD800 && c < 0xE000)
return; return;
if (c == 0xFFFF) if (c == 0xFFFF)
return; return;
...@@ -370,7 +354,7 @@ static void to_utf8(struct vc_data *vc, uint c) ...@@ -370,7 +354,7 @@ static void to_utf8(struct vc_data *vc, uint c)
put_queue(vc, 0xe0 | (c >> 12)); put_queue(vc, 0xe0 | (c >> 12));
put_queue(vc, 0x80 | ((c >> 6) & 0x3f)); put_queue(vc, 0x80 | ((c >> 6) & 0x3f));
put_queue(vc, 0x80 | (c & 0x3f)); put_queue(vc, 0x80 | (c & 0x3f));
} else if (c < 0x110000) { } else if (c < 0x110000) {
/* 11110*** 10****** 10****** 10****** */ /* 11110*** 10****** 10****** 10****** */
put_queue(vc, 0xf0 | (c >> 18)); put_queue(vc, 0xf0 | (c >> 18));
put_queue(vc, 0x80 | ((c >> 12) & 0x3f)); put_queue(vc, 0x80 | ((c >> 12) & 0x3f));
...@@ -469,6 +453,7 @@ static void fn_enter(struct vc_data *vc) ...@@ -469,6 +453,7 @@ static void fn_enter(struct vc_data *vc)
} }
diacr = 0; diacr = 0;
} }
put_queue(vc, 13); put_queue(vc, 13);
if (vc_kbd_mode(kbd, VC_CRLF)) if (vc_kbd_mode(kbd, VC_CRLF))
put_queue(vc, 10); put_queue(vc, 10);
...@@ -478,6 +463,7 @@ static void fn_caps_toggle(struct vc_data *vc) ...@@ -478,6 +463,7 @@ static void fn_caps_toggle(struct vc_data *vc)
{ {
if (rep) if (rep)
return; return;
chg_vc_kbd_led(kbd, VC_CAPSLOCK); chg_vc_kbd_led(kbd, VC_CAPSLOCK);
} }
...@@ -485,12 +471,14 @@ static void fn_caps_on(struct vc_data *vc) ...@@ -485,12 +471,14 @@ static void fn_caps_on(struct vc_data *vc)
{ {
if (rep) if (rep)
return; return;
set_vc_kbd_led(kbd, VC_CAPSLOCK); set_vc_kbd_led(kbd, VC_CAPSLOCK);
} }
static void fn_show_ptregs(struct vc_data *vc) static void fn_show_ptregs(struct vc_data *vc)
{ {
struct pt_regs *regs = get_irq_regs(); struct pt_regs *regs = get_irq_regs();
if (regs) if (regs)
show_regs(regs); show_regs(regs);
} }
...@@ -515,7 +503,7 @@ static void fn_hold(struct vc_data *vc) ...@@ -515,7 +503,7 @@ static void fn_hold(struct vc_data *vc)
static void fn_num(struct vc_data *vc) static void fn_num(struct vc_data *vc)
{ {
if (vc_kbd_mode(kbd,VC_APPLIC)) if (vc_kbd_mode(kbd, VC_APPLIC))
applkey(vc, 'P', 1); applkey(vc, 'P', 1);
else else
fn_bare_num(vc); fn_bare_num(vc);
...@@ -610,7 +598,7 @@ static void fn_boot_it(struct vc_data *vc) ...@@ -610,7 +598,7 @@ static void fn_boot_it(struct vc_data *vc)
static void fn_compose(struct vc_data *vc) static void fn_compose(struct vc_data *vc)
{ {
dead_key_next = 1; dead_key_next = true;
} }
static void fn_spawn_con(struct vc_data *vc) static void fn_spawn_con(struct vc_data *vc)
...@@ -657,7 +645,7 @@ static void k_spec(struct vc_data *vc, unsigned char value, char up_flag) ...@@ -657,7 +645,7 @@ static void k_spec(struct vc_data *vc, unsigned char value, char up_flag)
static void k_lowercase(struct vc_data *vc, unsigned char value, char up_flag) static void k_lowercase(struct vc_data *vc, unsigned char value, char up_flag)
{ {
printk(KERN_ERR "keyboard.c: k_lowercase was called - impossible\n"); pr_err("k_lowercase was called - impossible\n");
} }
static void k_unicode(struct vc_data *vc, unsigned int value, char up_flag) static void k_unicode(struct vc_data *vc, unsigned int value, char up_flag)
...@@ -669,7 +657,7 @@ static void k_unicode(struct vc_data *vc, unsigned int value, char up_flag) ...@@ -669,7 +657,7 @@ static void k_unicode(struct vc_data *vc, unsigned int value, char up_flag)
value = handle_diacr(vc, value); value = handle_diacr(vc, value);
if (dead_key_next) { if (dead_key_next) {
dead_key_next = 0; dead_key_next = false;
diacr = value; diacr = value;
return; return;
} }
...@@ -691,6 +679,7 @@ static void k_deadunicode(struct vc_data *vc, unsigned int value, char up_flag) ...@@ -691,6 +679,7 @@ static void k_deadunicode(struct vc_data *vc, unsigned int value, char up_flag)
{ {
if (up_flag) if (up_flag)
return; return;
diacr = (diacr ? handle_diacr(vc, value) : value); diacr = (diacr ? handle_diacr(vc, value) : value);
} }
...@@ -710,29 +699,28 @@ static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag) ...@@ -710,29 +699,28 @@ static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag)
static void k_dead(struct vc_data *vc, unsigned char value, char up_flag) static void k_dead(struct vc_data *vc, unsigned char value, char up_flag)
{ {
static const unsigned char ret_diacr[NR_DEAD] = {'`', '\'', '^', '~', '"', ',' }; static const unsigned char ret_diacr[NR_DEAD] = {'`', '\'', '^', '~', '"', ',' };
value = ret_diacr[value];
k_deadunicode(vc, value, up_flag); k_deadunicode(vc, ret_diacr[value], up_flag);
} }
static void k_cons(struct vc_data *vc, unsigned char value, char up_flag) static void k_cons(struct vc_data *vc, unsigned char value, char up_flag)
{ {
if (up_flag) if (up_flag)
return; return;
set_console(value); set_console(value);
} }
static void k_fn(struct vc_data *vc, unsigned char value, char up_flag) static void k_fn(struct vc_data *vc, unsigned char value, char up_flag)
{ {
unsigned v;
if (up_flag) if (up_flag)
return; return;
v = value;
if (v < ARRAY_SIZE(func_table)) { if ((unsigned)value < ARRAY_SIZE(func_table)) {
if (func_table[value]) if (func_table[value])
puts_queue(vc, func_table[value]); puts_queue(vc, func_table[value]);
} else } else
printk(KERN_ERR "k_fn called with value=%d\n", value); pr_err("k_fn called with value=%d\n", value);
} }
static void k_cur(struct vc_data *vc, unsigned char value, char up_flag) static void k_cur(struct vc_data *vc, unsigned char value, char up_flag)
...@@ -741,6 +729,7 @@ static void k_cur(struct vc_data *vc, unsigned char value, char up_flag) ...@@ -741,6 +729,7 @@ static void k_cur(struct vc_data *vc, unsigned char value, char up_flag)
if (up_flag) if (up_flag)
return; return;
applkey(vc, cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE)); applkey(vc, cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE));
} }
...@@ -758,43 +747,45 @@ static void k_pad(struct vc_data *vc, unsigned char value, char up_flag) ...@@ -758,43 +747,45 @@ static void k_pad(struct vc_data *vc, unsigned char value, char up_flag)
return; return;
} }
if (!vc_kbd_led(kbd, VC_NUMLOCK)) if (!vc_kbd_led(kbd, VC_NUMLOCK)) {
switch (value) { switch (value) {
case KVAL(K_PCOMMA): case KVAL(K_PCOMMA):
case KVAL(K_PDOT): case KVAL(K_PDOT):
k_fn(vc, KVAL(K_REMOVE), 0); k_fn(vc, KVAL(K_REMOVE), 0);
return; return;
case KVAL(K_P0): case KVAL(K_P0):
k_fn(vc, KVAL(K_INSERT), 0); k_fn(vc, KVAL(K_INSERT), 0);
return; return;
case KVAL(K_P1): case KVAL(K_P1):
k_fn(vc, KVAL(K_SELECT), 0); k_fn(vc, KVAL(K_SELECT), 0);
return; return;
case KVAL(K_P2): case KVAL(K_P2):
k_cur(vc, KVAL(K_DOWN), 0); k_cur(vc, KVAL(K_DOWN), 0);
return; return;
case KVAL(K_P3): case KVAL(K_P3):
k_fn(vc, KVAL(K_PGDN), 0); k_fn(vc, KVAL(K_PGDN), 0);
return; return;
case KVAL(K_P4): case KVAL(K_P4):
k_cur(vc, KVAL(K_LEFT), 0); k_cur(vc, KVAL(K_LEFT), 0);
return; return;
case KVAL(K_P6): case KVAL(K_P6):
k_cur(vc, KVAL(K_RIGHT), 0); k_cur(vc, KVAL(K_RIGHT), 0);
return; return;
case KVAL(K_P7): case KVAL(K_P7):
k_fn(vc, KVAL(K_FIND), 0); k_fn(vc, KVAL(K_FIND), 0);
return; return;
case KVAL(K_P8): case KVAL(K_P8):
k_cur(vc, KVAL(K_UP), 0); k_cur(vc, KVAL(K_UP), 0);
return; return;
case KVAL(K_P9): case KVAL(K_P9):
k_fn(vc, KVAL(K_PGUP), 0); k_fn(vc, KVAL(K_PGUP), 0);
return; return;
case KVAL(K_P5): case KVAL(K_P5):
applkey(vc, 'G', vc_kbd_mode(kbd, VC_APPLIC)); applkey(vc, 'G', vc_kbd_mode(kbd, VC_APPLIC));
return; return;
} }
}
put_queue(vc, pad_chars[value]); put_queue(vc, pad_chars[value]);
if (value == KVAL(K_PENTER) && vc_kbd_mode(kbd, VC_CRLF)) if (value == KVAL(K_PENTER) && vc_kbd_mode(kbd, VC_CRLF))
...@@ -880,6 +871,7 @@ static void k_lock(struct vc_data *vc, unsigned char value, char up_flag) ...@@ -880,6 +871,7 @@ static void k_lock(struct vc_data *vc, unsigned char value, char up_flag)
{ {
if (up_flag || rep) if (up_flag || rep)
return; return;
chg_vc_kbd_lock(kbd, value); chg_vc_kbd_lock(kbd, value);
} }
...@@ -888,6 +880,7 @@ static void k_slock(struct vc_data *vc, unsigned char value, char up_flag) ...@@ -888,6 +880,7 @@ static void k_slock(struct vc_data *vc, unsigned char value, char up_flag)
k_shift(vc, value, up_flag); k_shift(vc, value, up_flag);
if (up_flag || rep) if (up_flag || rep)
return; return;
chg_vc_kbd_slock(kbd, value); chg_vc_kbd_slock(kbd, value);
/* try to make Alt, oops, AltGr and such work */ /* try to make Alt, oops, AltGr and such work */
if (!key_maps[kbd->lockstate ^ kbd->slockstate]) { if (!key_maps[kbd->lockstate ^ kbd->slockstate]) {
...@@ -925,12 +918,12 @@ static void k_brlcommit(struct vc_data *vc, unsigned int pattern, char up_flag) ...@@ -925,12 +918,12 @@ static void k_brlcommit(struct vc_data *vc, unsigned int pattern, char up_flag)
static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) static void k_brl(struct vc_data *vc, unsigned char value, char up_flag)
{ {
static unsigned pressed,committing; static unsigned pressed, committing;
static unsigned long releasestart; static unsigned long releasestart;
if (kbd->kbdmode != VC_UNICODE) { if (kbd->kbdmode != VC_UNICODE) {
if (!up_flag) if (!up_flag)
printk("keyboard mode must be unicode for braille patterns\n"); pr_warning("keyboard mode must be unicode for braille patterns\n");
return; return;
} }
...@@ -942,32 +935,28 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) ...@@ -942,32 +935,28 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag)
if (value > 8) if (value > 8)
return; return;
if (up_flag) { if (!up_flag) {
if (brl_timeout) {
if (!committing ||
time_after(jiffies,
releasestart + msecs_to_jiffies(brl_timeout))) {
committing = pressed;
releasestart = jiffies;
}
pressed &= ~(1 << (value - 1));
if (!pressed) {
if (committing) {
k_brlcommit(vc, committing, 0);
committing = 0;
}
}
} else {
if (committing) {
k_brlcommit(vc, committing, 0);
committing = 0;
}
pressed &= ~(1 << (value - 1));
}
} else {
pressed |= 1 << (value - 1); pressed |= 1 << (value - 1);
if (!brl_timeout) if (!brl_timeout)
committing = pressed; committing = pressed;
} else if (brl_timeout) {
if (!committing ||
time_after(jiffies,
releasestart + msecs_to_jiffies(brl_timeout))) {
committing = pressed;
releasestart = jiffies;
}
pressed &= ~(1 << (value - 1));
if (!pressed && committing) {
k_brlcommit(vc, committing, 0);
committing = 0;
}
} else {
if (committing) {
k_brlcommit(vc, committing, 0);
committing = 0;
}
pressed &= ~(1 << (value - 1));
} }
} }
...@@ -988,6 +977,7 @@ void setledstate(struct kbd_struct *kbd, unsigned int led) ...@@ -988,6 +977,7 @@ void setledstate(struct kbd_struct *kbd, unsigned int led)
kbd->ledmode = LED_SHOW_IOCTL; kbd->ledmode = LED_SHOW_IOCTL;
} else } else
kbd->ledmode = LED_SHOW_FLAGS; kbd->ledmode = LED_SHOW_FLAGS;
set_leds(); set_leds();
} }
...@@ -1075,7 +1065,7 @@ static const unsigned short x86_keycodes[256] = ...@@ -1075,7 +1065,7 @@ static const unsigned short x86_keycodes[256] =
332,340,365,342,343,344,345,346,356,270,341,368,369,370,371,372 }; 332,340,365,342,343,344,345,346,356,270,341,368,369,370,371,372 };
#ifdef CONFIG_SPARC #ifdef CONFIG_SPARC
static int sparc_l1_a_state = 0; static int sparc_l1_a_state;
extern void sun_do_break(void); extern void sun_do_break(void);
#endif #endif
...@@ -1085,52 +1075,54 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode, ...@@ -1085,52 +1075,54 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode,
int code; int code;
switch (keycode) { switch (keycode) {
case KEY_PAUSE:
put_queue(vc, 0xe1);
put_queue(vc, 0x1d | up_flag);
put_queue(vc, 0x45 | up_flag);
break;
case KEY_HANGEUL: case KEY_PAUSE:
if (!up_flag) put_queue(vc, 0xe1);
put_queue(vc, 0xf2); put_queue(vc, 0x1d | up_flag);
break; put_queue(vc, 0x45 | up_flag);
break;
case KEY_HANJA: case KEY_HANGEUL:
if (!up_flag) if (!up_flag)
put_queue(vc, 0xf1); put_queue(vc, 0xf2);
break; break;
case KEY_SYSRQ: case KEY_HANJA:
/* if (!up_flag)
* Real AT keyboards (that's what we're trying put_queue(vc, 0xf1);
* to emulate here emit 0xe0 0x2a 0xe0 0x37 when break;
* pressing PrtSc/SysRq alone, but simply 0x54
* when pressing Alt+PrtSc/SysRq.
*/
if (sysrq_alt) {
put_queue(vc, 0x54 | up_flag);
} else {
put_queue(vc, 0xe0);
put_queue(vc, 0x2a | up_flag);
put_queue(vc, 0xe0);
put_queue(vc, 0x37 | up_flag);
}
break;
default: case KEY_SYSRQ:
if (keycode > 255) /*
return -1; * Real AT keyboards (that's what we're trying
* to emulate here emit 0xe0 0x2a 0xe0 0x37 when
* pressing PrtSc/SysRq alone, but simply 0x54
* when pressing Alt+PrtSc/SysRq.
*/
if (test_bit(KEY_LEFTALT, key_down) ||
test_bit(KEY_RIGHTALT, key_down)) {
put_queue(vc, 0x54 | up_flag);
} else {
put_queue(vc, 0xe0);
put_queue(vc, 0x2a | up_flag);
put_queue(vc, 0xe0);
put_queue(vc, 0x37 | up_flag);
}
break;
code = x86_keycodes[keycode]; default:
if (!code) if (keycode > 255)
return -1; return -1;
if (code & 0x100) code = x86_keycodes[keycode];
put_queue(vc, 0xe0); if (!code)
put_queue(vc, (code & 0x7f) | up_flag); return -1;
break; if (code & 0x100)
put_queue(vc, 0xe0);
put_queue(vc, (code & 0x7f) | up_flag);
break;
} }
return 0; return 0;
...@@ -1153,6 +1145,7 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode, unsigned char u ...@@ -1153,6 +1145,7 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode, unsigned char u
static void kbd_rawcode(unsigned char data) static void kbd_rawcode(unsigned char data)
{ {
struct vc_data *vc = vc_cons[fg_console].d; struct vc_data *vc = vc_cons[fg_console].d;
kbd = kbd_table + vc->vc_num; kbd = kbd_table + vc->vc_num;
if (kbd->kbdmode == VC_RAW) if (kbd->kbdmode == VC_RAW)
put_queue(vc, data); put_queue(vc, data);
...@@ -1162,10 +1155,12 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) ...@@ -1162,10 +1155,12 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
{ {
struct vc_data *vc = vc_cons[fg_console].d; struct vc_data *vc = vc_cons[fg_console].d;
unsigned short keysym, *key_map; unsigned short keysym, *key_map;
unsigned char type, raw_mode; unsigned char type;
bool raw_mode;
struct tty_struct *tty; struct tty_struct *tty;
int shift_final; int shift_final;
struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down }; struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down };
int rc;
tty = vc->vc_tty; tty = vc->vc_tty;
...@@ -1176,8 +1171,6 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) ...@@ -1176,8 +1171,6 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
kbd = kbd_table + vc->vc_num; kbd = kbd_table + vc->vc_num;
if (keycode == KEY_LEFTALT || keycode == KEY_RIGHTALT)
sysrq_alt = down ? keycode : 0;
#ifdef CONFIG_SPARC #ifdef CONFIG_SPARC
if (keycode == KEY_STOP) if (keycode == KEY_STOP)
sparc_l1_a_state = down; sparc_l1_a_state = down;
...@@ -1185,29 +1178,16 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) ...@@ -1185,29 +1178,16 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
rep = (down == 2); rep = (down == 2);
if ((raw_mode = (kbd->kbdmode == VC_RAW)) && !hw_raw) raw_mode = (kbd->kbdmode == VC_RAW);
if (raw_mode && !hw_raw)
if (emulate_raw(vc, keycode, !down << 7)) if (emulate_raw(vc, keycode, !down << 7))
if (keycode < BTN_MISC && printk_ratelimit()) if (keycode < BTN_MISC && printk_ratelimit())
printk(KERN_WARNING "keyboard.c: can't emulate rawmode for keycode %d\n", keycode); pr_warning("can't emulate rawmode for keycode %d\n",
keycode);
#ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */
if (keycode == KEY_SYSRQ && (sysrq_down || (down == 1 && sysrq_alt))) {
if (!sysrq_down) {
sysrq_down = down;
sysrq_alt_use = sysrq_alt;
}
return;
}
if (sysrq_down && !down && keycode == sysrq_alt_use)
sysrq_down = 0;
if (sysrq_down && down && !rep) {
handle_sysrq(kbd_sysrq_xlate[keycode], tty);
return;
}
#endif
#ifdef CONFIG_SPARC #ifdef CONFIG_SPARC
if (keycode == KEY_A && sparc_l1_a_state) { if (keycode == KEY_A && sparc_l1_a_state) {
sparc_l1_a_state = 0; sparc_l1_a_state = false;
sun_do_break(); sun_do_break();
} }
#endif #endif
...@@ -1229,7 +1209,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) ...@@ -1229,7 +1209,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
put_queue(vc, (keycode >> 7) | 0x80); put_queue(vc, (keycode >> 7) | 0x80);
put_queue(vc, keycode | 0x80); put_queue(vc, keycode | 0x80);
} }
raw_mode = 1; raw_mode = true;
} }
if (down) if (down)
...@@ -1252,29 +1232,32 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) ...@@ -1252,29 +1232,32 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
param.ledstate = kbd->ledflagstate; param.ledstate = kbd->ledflagstate;
key_map = key_maps[shift_final]; key_map = key_maps[shift_final];
if (atomic_notifier_call_chain(&keyboard_notifier_list, KBD_KEYCODE, &param) == NOTIFY_STOP || !key_map) { rc = atomic_notifier_call_chain(&keyboard_notifier_list,
atomic_notifier_call_chain(&keyboard_notifier_list, KBD_UNBOUND_KEYCODE, &param); KBD_KEYCODE, &param);
if (rc == NOTIFY_STOP || !key_map) {
atomic_notifier_call_chain(&keyboard_notifier_list,
KBD_UNBOUND_KEYCODE, &param);
compute_shiftstate(); compute_shiftstate();
kbd->slockstate = 0; kbd->slockstate = 0;
return; return;
} }
if (keycode >= NR_KEYS) if (keycode < NR_KEYS)
if (keycode >= KEY_BRL_DOT1 && keycode <= KEY_BRL_DOT8)
keysym = U(K(KT_BRL, keycode - KEY_BRL_DOT1 + 1));
else
return;
else
keysym = key_map[keycode]; keysym = key_map[keycode];
else if (keycode >= KEY_BRL_DOT1 && keycode <= KEY_BRL_DOT8)
keysym = U(K(KT_BRL, keycode - KEY_BRL_DOT1 + 1));
else
return;
type = KTYP(keysym); type = KTYP(keysym);
if (type < 0xf0) { if (type < 0xf0) {
param.value = keysym; param.value = keysym;
if (atomic_notifier_call_chain(&keyboard_notifier_list, KBD_UNICODE, &param) == NOTIFY_STOP) rc = atomic_notifier_call_chain(&keyboard_notifier_list,
return; KBD_UNICODE, &param);
if (down && !raw_mode) if (rc != NOTIFY_STOP)
to_utf8(vc, keysym); if (down && !raw_mode)
to_utf8(vc, keysym);
return; return;
} }
...@@ -1288,9 +1271,11 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) ...@@ -1288,9 +1271,11 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
keysym = key_map[keycode]; keysym = key_map[keycode];
} }
} }
param.value = keysym;
if (atomic_notifier_call_chain(&keyboard_notifier_list, KBD_KEYSYM, &param) == NOTIFY_STOP) param.value = keysym;
rc = atomic_notifier_call_chain(&keyboard_notifier_list,
KBD_KEYSYM, &param);
if (rc == NOTIFY_STOP)
return; return;
if (raw_mode && type != KT_SPEC && type != KT_SHIFT) if (raw_mode && type != KT_SPEC && type != KT_SHIFT)
......
/* -*- linux-c -*- /*
*
* $Id: sysrq.c,v 1.15 1998/08/23 14:56:41 mj Exp $
*
* Linux Magic System Request Key Hacks * Linux Magic System Request Key Hacks
* *
* (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz> * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
...@@ -10,8 +7,13 @@ ...@@ -10,8 +7,13 @@
* (c) 2000 Crutcher Dunnavant <crutcher+kernel@datastacks.com> * (c) 2000 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
* overhauled to use key registration * overhauled to use key registration
* based upon discusions in irc://irc.openprojects.net/#kernelnewbies * based upon discusions in irc://irc.openprojects.net/#kernelnewbies
*
* Copyright (c) 2010 Dmitry Torokhov
* Input handler conversion
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/mm.h> #include <linux/mm.h>
...@@ -39,33 +41,34 @@ ...@@ -39,33 +41,34 @@
#include <linux/hrtimer.h> #include <linux/hrtimer.h>
#include <linux/oom.h> #include <linux/oom.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/input.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include <asm/irq_regs.h> #include <asm/irq_regs.h>
/* Whether we react on sysrq keys or just ignore them */ /* Whether we react on sysrq keys or just ignore them */
int __read_mostly __sysrq_enabled = 1; static int __read_mostly sysrq_enabled = 1;
static bool __read_mostly sysrq_always_enabled;
static int __read_mostly sysrq_always_enabled;
int sysrq_on(void) static bool sysrq_on(void)
{ {
return __sysrq_enabled || sysrq_always_enabled; return sysrq_enabled || sysrq_always_enabled;
} }
/* /*
* A value of 1 means 'all', other nonzero values are an op mask: * A value of 1 means 'all', other nonzero values are an op mask:
*/ */
static inline int sysrq_on_mask(int mask) static bool sysrq_on_mask(int mask)
{ {
return sysrq_always_enabled || __sysrq_enabled == 1 || return sysrq_always_enabled ||
(__sysrq_enabled & mask); sysrq_enabled == 1 ||
(sysrq_enabled & mask);
} }
static int __init sysrq_always_enabled_setup(char *str) static int __init sysrq_always_enabled_setup(char *str)
{ {
sysrq_always_enabled = 1; sysrq_always_enabled = true;
printk(KERN_INFO "debug: sysrq always enabled.\n"); pr_info("sysrq always enabled.\n");
return 1; return 1;
} }
...@@ -76,6 +79,7 @@ __setup("sysrq_always_enabled", sysrq_always_enabled_setup); ...@@ -76,6 +79,7 @@ __setup("sysrq_always_enabled", sysrq_always_enabled_setup);
static void sysrq_handle_loglevel(int key, struct tty_struct *tty) static void sysrq_handle_loglevel(int key, struct tty_struct *tty)
{ {
int i; int i;
i = key - '0'; i = key - '0';
console_loglevel = 7; console_loglevel = 7;
printk("Loglevel set to %d\n", i); printk("Loglevel set to %d\n", i);
...@@ -101,7 +105,7 @@ static struct sysrq_key_op sysrq_SAK_op = { ...@@ -101,7 +105,7 @@ static struct sysrq_key_op sysrq_SAK_op = {
.enable_mask = SYSRQ_ENABLE_KEYBOARD, .enable_mask = SYSRQ_ENABLE_KEYBOARD,
}; };
#else #else
#define sysrq_SAK_op (*(struct sysrq_key_op *)0) #define sysrq_SAK_op (*(struct sysrq_key_op *)NULL)
#endif #endif
#ifdef CONFIG_VT #ifdef CONFIG_VT
...@@ -119,7 +123,7 @@ static struct sysrq_key_op sysrq_unraw_op = { ...@@ -119,7 +123,7 @@ static struct sysrq_key_op sysrq_unraw_op = {
.enable_mask = SYSRQ_ENABLE_KEYBOARD, .enable_mask = SYSRQ_ENABLE_KEYBOARD,
}; };
#else #else
#define sysrq_unraw_op (*(struct sysrq_key_op *)0) #define sysrq_unraw_op (*(struct sysrq_key_op *)NULL)
#endif /* CONFIG_VT */ #endif /* CONFIG_VT */
static void sysrq_handle_crash(int key, struct tty_struct *tty) static void sysrq_handle_crash(int key, struct tty_struct *tty)
...@@ -195,7 +199,7 @@ static struct sysrq_key_op sysrq_showlocks_op = { ...@@ -195,7 +199,7 @@ static struct sysrq_key_op sysrq_showlocks_op = {
.action_msg = "Show Locks Held", .action_msg = "Show Locks Held",
}; };
#else #else
#define sysrq_showlocks_op (*(struct sysrq_key_op *)0) #define sysrq_showlocks_op (*(struct sysrq_key_op *)NULL)
#endif #endif
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
...@@ -298,7 +302,7 @@ static struct sysrq_key_op sysrq_ftrace_dump_op = { ...@@ -298,7 +302,7 @@ static struct sysrq_key_op sysrq_ftrace_dump_op = {
.enable_mask = SYSRQ_ENABLE_DUMP, .enable_mask = SYSRQ_ENABLE_DUMP,
}; };
#else #else
#define sysrq_ftrace_dump_op (*(struct sysrq_key_op *)0) #define sysrq_ftrace_dump_op (*(struct sysrq_key_op *)NULL)
#endif #endif
static void sysrq_handle_showmem(int key, struct tty_struct *tty) static void sysrq_handle_showmem(int key, struct tty_struct *tty)
...@@ -477,6 +481,7 @@ struct sysrq_key_op *__sysrq_get_key_op(int key) ...@@ -477,6 +481,7 @@ struct sysrq_key_op *__sysrq_get_key_op(int key)
i = sysrq_key_table_key2index(key); i = sysrq_key_table_key2index(key);
if (i != -1) if (i != -1)
op_p = sysrq_key_table[i]; op_p = sysrq_key_table[i];
return op_p; return op_p;
} }
...@@ -488,11 +493,7 @@ static void __sysrq_put_key_op(int key, struct sysrq_key_op *op_p) ...@@ -488,11 +493,7 @@ static void __sysrq_put_key_op(int key, struct sysrq_key_op *op_p)
sysrq_key_table[i] = op_p; sysrq_key_table[i] = op_p;
} }
/* static void __handle_sysrq(int key, struct tty_struct *tty, int check_mask)
* This is the non-locking version of handle_sysrq. It must/can only be called
* by sysrq key handlers, as they are inside of the lock
*/
void __handle_sysrq(int key, struct tty_struct *tty, int check_mask)
{ {
struct sysrq_key_op *op_p; struct sysrq_key_op *op_p;
int orig_log_level; int orig_log_level;
...@@ -544,10 +545,6 @@ void __handle_sysrq(int key, struct tty_struct *tty, int check_mask) ...@@ -544,10 +545,6 @@ void __handle_sysrq(int key, struct tty_struct *tty, int check_mask)
spin_unlock_irqrestore(&sysrq_key_table_lock, flags); spin_unlock_irqrestore(&sysrq_key_table_lock, flags);
} }
/*
* This function is called by the keyboard handler when SysRq is pressed
* and any other keycode arrives.
*/
void handle_sysrq(int key, struct tty_struct *tty) void handle_sysrq(int key, struct tty_struct *tty)
{ {
if (sysrq_on()) if (sysrq_on())
...@@ -555,10 +552,177 @@ void handle_sysrq(int key, struct tty_struct *tty) ...@@ -555,10 +552,177 @@ void handle_sysrq(int key, struct tty_struct *tty)
} }
EXPORT_SYMBOL(handle_sysrq); EXPORT_SYMBOL(handle_sysrq);
#ifdef CONFIG_INPUT
/* Simple translation table for the SysRq keys */
static const unsigned char sysrq_xlate[KEY_MAX + 1] =
"\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 */
static bool sysrq_down;
static int sysrq_alt_use;
static int sysrq_alt;
static bool sysrq_filter(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
if (type != EV_KEY)
goto out;
switch (code) {
case KEY_LEFTALT:
case KEY_RIGHTALT:
if (value)
sysrq_alt = code;
else if (sysrq_down && code == sysrq_alt_use)
sysrq_down = false;
break;
case KEY_SYSRQ:
if (value == 1 && sysrq_alt) {
sysrq_down = true;
sysrq_alt_use = sysrq_alt;
}
break;
default:
if (sysrq_down && value && value != 2)
__handle_sysrq(sysrq_xlate[code], NULL, 1);
break;
}
out:
return sysrq_down;
}
static int sysrq_connect(struct input_handler *handler,
struct input_dev *dev,
const struct input_device_id *id)
{
struct input_handle *handle;
int error;
sysrq_down = false;
sysrq_alt = 0;
handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
if (!handle)
return -ENOMEM;
handle->dev = dev;
handle->handler = handler;
handle->name = "sysrq";
error = input_register_handle(handle);
if (error) {
pr_err("Failed to register input sysrq handler, error %d\n",
error);
goto err_free;
}
error = input_open_device(handle);
if (error) {
pr_err("Failed to open input device, error %d\n", error);
goto err_unregister;
}
return 0;
err_unregister:
input_unregister_handle(handle);
err_free:
kfree(handle);
return error;
}
static void sysrq_disconnect(struct input_handle *handle)
{
input_close_device(handle);
input_unregister_handle(handle);
kfree(handle);
}
/*
* We are matching on KEY_LEFTALT insteard of KEY_SYSRQ because not all
* keyboards have SysRq ikey predefined and so user may add it to keymap
* later, but we expect all such keyboards to have left alt.
*/
static const struct input_device_id sysrq_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT,
.evbit = { BIT_MASK(EV_KEY) },
.keybit = { BIT_MASK(KEY_LEFTALT) },
},
{ },
};
static struct input_handler sysrq_handler = {
.filter = sysrq_filter,
.connect = sysrq_connect,
.disconnect = sysrq_disconnect,
.name = "sysrq",
.id_table = sysrq_ids,
};
static bool sysrq_handler_registered;
static inline void sysrq_register_handler(void)
{
int error;
error = input_register_handler(&sysrq_handler);
if (error)
pr_err("Failed to register input handler, error %d", error);
else
sysrq_handler_registered = true;
}
static inline void sysrq_unregister_handler(void)
{
if (sysrq_handler_registered) {
input_unregister_handler(&sysrq_handler);
sysrq_handler_registered = false;
}
}
#else
static inline void sysrq_register_handler(void)
{
}
static inline void sysrq_unregister_handler(void)
{
}
#endif /* CONFIG_INPUT */
int sysrq_toggle_support(int enable_mask)
{
bool was_enabled = sysrq_on();
sysrq_enabled = enable_mask;
if (was_enabled != sysrq_on()) {
if (sysrq_on())
sysrq_register_handler();
else
sysrq_unregister_handler();
}
return 0;
}
static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p, static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p,
struct sysrq_key_op *remove_op_p) struct sysrq_key_op *remove_op_p)
{ {
int retval; int retval;
unsigned long flags; unsigned long flags;
...@@ -599,6 +763,7 @@ static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf, ...@@ -599,6 +763,7 @@ static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf,
return -EFAULT; return -EFAULT;
__handle_sysrq(c, NULL, 0); __handle_sysrq(c, NULL, 0);
} }
return count; return count;
} }
...@@ -606,10 +771,28 @@ static const struct file_operations proc_sysrq_trigger_operations = { ...@@ -606,10 +771,28 @@ static const struct file_operations proc_sysrq_trigger_operations = {
.write = write_sysrq_trigger, .write = write_sysrq_trigger,
}; };
static void sysrq_init_procfs(void)
{
if (!proc_create("sysrq-trigger", S_IWUSR, NULL,
&proc_sysrq_trigger_operations))
pr_err("Failed to register proc interface\n");
}
#else
static inline void sysrq_init_procfs(void)
{
}
#endif /* CONFIG_PROC_FS */
static int __init sysrq_init(void) static int __init sysrq_init(void)
{ {
proc_create("sysrq-trigger", S_IWUSR, NULL, &proc_sysrq_trigger_operations); sysrq_init_procfs();
if (sysrq_on())
sysrq_register_handler();
return 0; return 0;
} }
module_init(sysrq_init); module_init(sysrq_init);
#endif
...@@ -73,7 +73,7 @@ config KEYBOARD_ATKBD ...@@ -73,7 +73,7 @@ config KEYBOARD_ATKBD
default y default y
select SERIO select SERIO
select SERIO_LIBPS2 select SERIO_LIBPS2
select SERIO_I8042 if X86 select SERIO_I8042 if X86 && !X86_MRST
select SERIO_GSCPS2 if GSC select SERIO_GSCPS2 if GSC
help help
Say Y here if you want to use a standard AT or PS/2 keyboard. Usually Say Y here if you want to use a standard AT or PS/2 keyboard. Usually
...@@ -179,6 +179,22 @@ config KEYBOARD_GPIO ...@@ -179,6 +179,22 @@ 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_TCA6416
tristate "TCA6416 Keypad Support"
depends on I2C
help
This driver implements basic keypad functionality
for keys connected through TCA6416 IO expander
Say Y here if your device has keys connected to
TCA6416 IO expander. Your board-specific setup logic
must also provide pin-mask details(of which TCA6416 pins
are used for keypad).
If enabled the complete TCA6416 device will be managed through
this driver.
config KEYBOARD_MATRIX config KEYBOARD_MATRIX
tristate "GPIO driven matrix keypad support" tristate "GPIO driven matrix keypad support"
depends on GENERIC_GPIO depends on GENERIC_GPIO
......
...@@ -14,6 +14,7 @@ obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o ...@@ -14,6 +14,7 @@ obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o
......
...@@ -670,8 +670,6 @@ static int __devinit lm8323_probe(struct i2c_client *client, ...@@ -670,8 +670,6 @@ static int __devinit lm8323_probe(struct i2c_client *client,
goto fail1; goto fail1;
} }
i2c_set_clientdata(client, lm);
lm->client = client; lm->client = client;
lm->idev = idev; lm->idev = idev;
mutex_init(&lm->lock); mutex_init(&lm->lock);
...@@ -753,6 +751,8 @@ static int __devinit lm8323_probe(struct i2c_client *client, ...@@ -753,6 +751,8 @@ static int __devinit lm8323_probe(struct i2c_client *client,
goto fail4; goto fail4;
} }
i2c_set_clientdata(client, lm);
device_init_wakeup(&client->dev, 1); device_init_wakeup(&client->dev, 1);
enable_irq_wake(client->irq); enable_irq_wake(client->irq);
...@@ -778,6 +778,8 @@ static int __devexit lm8323_remove(struct i2c_client *client) ...@@ -778,6 +778,8 @@ static int __devexit lm8323_remove(struct i2c_client *client)
struct lm8323_chip *lm = i2c_get_clientdata(client); struct lm8323_chip *lm = i2c_get_clientdata(client);
int i; int i;
i2c_set_clientdata(client, NULL);
disable_irq_wake(client->irq); disable_irq_wake(client->irq);
free_irq(client->irq, lm); free_irq(client->irq, lm);
cancel_work_sync(&lm->work); cancel_work_sync(&lm->work);
......
/*
* Driver for keys on TCA6416 I2C IO expander
*
* Copyright (C) 2010 Texas Instruments
*
* Author : Sriramakrishnan.A.G. <srk@ti.com>
*
* 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/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/tca6416_keypad.h>
#define TCA6416_INPUT 0
#define TCA6416_OUTPUT 1
#define TCA6416_INVERT 2
#define TCA6416_DIRECTION 3
static const struct i2c_device_id tca6416_id[] = {
{ "tca6416-keys", 16, },
{ }
};
MODULE_DEVICE_TABLE(i2c, tca6416_id);
struct tca6416_drv_data {
struct input_dev *input;
struct tca6416_button data[0];
};
struct tca6416_keypad_chip {
uint16_t reg_output;
uint16_t reg_direction;
uint16_t reg_input;
struct i2c_client *client;
struct input_dev *input;
struct delayed_work dwork;
u16 pinmask;
int irqnum;
bool use_polling;
struct tca6416_button buttons[0];
};
static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val)
{
int error;
error = i2c_smbus_write_word_data(chip->client, reg << 1, val);
if (error < 0) {
dev_err(&chip->client->dev,
"%s failed, reg: %d, val: %d, error: %d\n",
__func__, reg, val, error);
return error;
}
return 0;
}
static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val)
{
int retval;
retval = i2c_smbus_read_word_data(chip->client, reg << 1);
if (retval < 0) {
dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n",
__func__, reg, retval);
return retval;
}
*val = (u16)retval;
return 0;
}
static void tca6416_keys_scan(struct tca6416_keypad_chip *chip)
{
struct input_dev *input = chip->input;
u16 reg_val, val;
int error, i, pin_index;
error = tca6416_read_reg(chip, TCA6416_INPUT, &reg_val);
if (error)
return;
reg_val &= chip->pinmask;
/* Figure out which lines have changed */
val = reg_val ^ chip->reg_input;
chip->reg_input = reg_val;
for (i = 0, pin_index = 0; i < 16; i++) {
if (val & (1 << i)) {
struct tca6416_button *button = &chip->buttons[pin_index];
unsigned int type = button->type ?: EV_KEY;
int state = ((reg_val & (1 << i)) ? 1 : 0)
^ button->active_low;
input_event(input, type, button->code, !!state);
input_sync(input);
}
if (chip->pinmask & (1 << i))
pin_index++;
}
}
/*
* This is threaded IRQ handler and this can (and will) sleep.
*/
static irqreturn_t tca6416_keys_isr(int irq, void *dev_id)
{
struct tca6416_keypad_chip *chip = dev_id;
tca6416_keys_scan(chip);
return IRQ_HANDLED;
}
static void tca6416_keys_work_func(struct work_struct *work)
{
struct tca6416_keypad_chip *chip =
container_of(work, struct tca6416_keypad_chip, dwork.work);
tca6416_keys_scan(chip);
schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100));
}
static int tca6416_keys_open(struct input_dev *dev)
{
struct tca6416_keypad_chip *chip = input_get_drvdata(dev);
/* Get initial device state in case it has switches */
tca6416_keys_scan(chip);
if (chip->use_polling)
schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100));
else
enable_irq(chip->irqnum);
return 0;
}
static void tca6416_keys_close(struct input_dev *dev)
{
struct tca6416_keypad_chip *chip = input_get_drvdata(dev);
if (chip->use_polling)
cancel_delayed_work_sync(&chip->dwork);
else
disable_irq(chip->irqnum);
}
static int __devinit tca6416_setup_registers(struct tca6416_keypad_chip *chip)
{
int error;
error = tca6416_read_reg(chip, TCA6416_OUTPUT, &chip->reg_output);
if (error)
return error;
error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction);
if (error)
return error;
/* ensure that keypad pins are set to input */
error = tca6416_write_reg(chip, TCA6416_DIRECTION,
chip->reg_direction | chip->pinmask);
if (error)
return error;
error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction);
if (error)
return error;
error = tca6416_read_reg(chip, TCA6416_INPUT, &chip->reg_input);
if (error)
return error;
chip->reg_input &= chip->pinmask;
return 0;
}
static int __devinit tca6416_keypad_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tca6416_keys_platform_data *pdata;
struct tca6416_keypad_chip *chip;
struct input_dev *input;
int error;
int i;
/* Check functionality */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) {
dev_err(&client->dev, "%s adapter not supported\n",
dev_driver_string(&client->adapter->dev));
return -ENODEV;
}
pdata = client->dev.platform_data;
if (!pdata) {
dev_dbg(&client->dev, "no platform data\n");
return -EINVAL;
}
chip = kzalloc(sizeof(struct tca6416_keypad_chip) +
pdata->nbuttons * sizeof(struct tca6416_button),
GFP_KERNEL);
input = input_allocate_device();
if (!chip || !input) {
error = -ENOMEM;
goto fail1;
}
chip->client = client;
chip->input = input;
chip->pinmask = pdata->pinmask;
chip->use_polling = pdata->use_polling;
INIT_DELAYED_WORK(&chip->dwork, tca6416_keys_work_func);
input->phys = "tca6416-keys/input0";
input->name = client->name;
input->dev.parent = &client->dev;
input->open = tca6416_keys_open;
input->close = tca6416_keys_close;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
/* Enable auto repeat feature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < pdata->nbuttons; i++) {
unsigned int type;
chip->buttons[i] = pdata->buttons[i];
type = (pdata->buttons[i].type) ?: EV_KEY;
input_set_capability(input, type, pdata->buttons[i].code);
}
input_set_drvdata(input, chip);
/*
* Initialize cached registers from their original values.
* we can't share this chip with another i2c master.
*/
error = tca6416_setup_registers(chip);
if (error)
goto fail1;
if (!chip->use_polling) {
if (pdata->irq_is_gpio)
chip->irqnum = gpio_to_irq(client->irq);
else
chip->irqnum = client->irq;
error = request_threaded_irq(chip->irqnum, NULL,
tca6416_keys_isr,
IRQF_TRIGGER_FALLING,
"tca6416-keypad", chip);
if (error) {
dev_dbg(&client->dev,
"Unable to claim irq %d; error %d\n",
chip->irqnum, error);
goto fail1;
}
disable_irq(chip->irqnum);
}
error = input_register_device(input);
if (error) {
dev_dbg(&client->dev,
"Unable to register input device, error: %d\n", error);
goto fail2;
}
i2c_set_clientdata(client, chip);
return 0;
fail2:
if (!chip->use_polling) {
free_irq(chip->irqnum, chip);
enable_irq(chip->irqnum);
}
fail1:
input_free_device(input);
kfree(chip);
return error;
}
static int __devexit tca6416_keypad_remove(struct i2c_client *client)
{
struct tca6416_keypad_chip *chip = i2c_get_clientdata(client);
if (!chip->use_polling) {
free_irq(chip->irqnum, chip);
enable_irq(chip->irqnum);
}
input_unregister_device(chip->input);
kfree(chip);
i2c_set_clientdata(client, NULL);
return 0;
}
static struct i2c_driver tca6416_keypad_driver = {
.driver = {
.name = "tca6416-keypad",
},
.probe = tca6416_keypad_probe,
.remove = __devexit_p(tca6416_keypad_remove),
.id_table = tca6416_id,
};
static int __init tca6416_keypad_init(void)
{
return i2c_add_driver(&tca6416_keypad_driver);
}
subsys_initcall(tca6416_keypad_init);
static void __exit tca6416_keypad_exit(void)
{
i2c_del_driver(&tca6416_keypad_driver);
}
module_exit(tca6416_keypad_exit);
MODULE_AUTHOR("Sriramakrishnan <srk@ti.com>");
MODULE_DESCRIPTION("Keypad driver over tca6146 IO expander");
MODULE_LICENSE("GPL");
...@@ -87,7 +87,6 @@ static int __devinit pm860x_onkey_probe(struct platform_device *pdev) ...@@ -87,7 +87,6 @@ static int __devinit pm860x_onkey_probe(struct platform_device *pdev)
info->idev->phys = "88pm860x_on/input0"; info->idev->phys = "88pm860x_on/input0";
info->idev->id.bustype = BUS_I2C; info->idev->id.bustype = BUS_I2C;
info->idev->dev.parent = &pdev->dev; info->idev->dev.parent = &pdev->dev;
info->irq = irq;
info->idev->evbit[0] = BIT_MASK(EV_KEY); info->idev->evbit[0] = BIT_MASK(EV_KEY);
info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
......
...@@ -22,6 +22,36 @@ config INPUT_88PM860X_ONKEY ...@@ -22,6 +22,36 @@ config INPUT_88PM860X_ONKEY
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called 88pm860x_onkey. will be called 88pm860x_onkey.
config INPUT_AD714X
tristate "Analog Devices AD714x Capacitance Touch Sensor"
help
Say Y here if you want to support an AD7142/3/7/8/7A touch sensor.
You should select a bus connection too.
To compile this driver as a module, choose M here: the
module will be called ad714x.
config INPUT_AD714X_I2C
tristate "support I2C bus connection"
depends on INPUT_AD714X && I2C
default y
help
Say Y here if you have AD7142/AD7147 hooked to an I2C bus.
To compile this driver as a module, choose M here: the
module will be called ad714x-i2c.
config INPUT_AD714X_SPI
tristate "support SPI bus connection"
depends on INPUT_AD714X && SPI
default y
help
Say Y here if you have AD7142/AD7147 hooked to a SPI bus.
To compile this driver as a module, choose M here: the
module will be called ad714x-spi.
config INPUT_PCSPKR config INPUT_PCSPKR
tristate "PC Speaker support" tristate "PC Speaker support"
depends on PCSPKR_PLATFORM depends on PCSPKR_PLATFORM
...@@ -277,6 +307,16 @@ config INPUT_PCF50633_PMU ...@@ -277,6 +307,16 @@ config INPUT_PCF50633_PMU
Say Y to include support for delivering PMU events via input Say Y to include support for delivering PMU events via input
layer on NXP PCF50633. layer on NXP PCF50633.
config INPUT_PCF8574
tristate "PCF8574 Keypad input device"
depends on I2C && EXPERIMENTAL
help
Say Y here if you want to support a keypad connetced via I2C
with a PCF8574.
To compile this driver as a module, choose M here: the
module will be called pcf8574_keypad.
config INPUT_GPIO_ROTARY_ENCODER config INPUT_GPIO_ROTARY_ENCODER
tristate "Rotary encoders connected to GPIO pins" tristate "Rotary encoders connected to GPIO pins"
depends on GPIOLIB && GENERIC_GPIO depends on GPIOLIB && GENERIC_GPIO
......
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
# Each configuration option enables a list of files. # Each configuration option enables a list of files.
obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
obj-$(CONFIG_INPUT_AD714X) += ad714x.o
obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o
obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o obj-$(CONFIG_INPUT_APANEL) += apanel.o
obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
...@@ -19,6 +22,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o ...@@ -19,6 +22,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
......
/*
* AD714X CapTouch Programmable Controller driver (I2C bus)
*
* Copyright 2009 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <linux/input.h> /* BUS_I2C */
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/types.h>
#include "ad714x.h"
#ifdef CONFIG_PM
static int ad714x_i2c_suspend(struct i2c_client *client, pm_message_t message)
{
return ad714x_disable(i2c_get_clientdata(client));
}
static int ad714x_i2c_resume(struct i2c_client *client)
{
return ad714x_enable(i2c_get_clientdata(client));
}
#else
# define ad714x_i2c_suspend NULL
# define ad714x_i2c_resume NULL
#endif
static int ad714x_i2c_write(struct device *dev, unsigned short reg,
unsigned short data)
{
struct i2c_client *client = to_i2c_client(dev);
int ret = 0;
u8 *_reg = (u8 *)&reg;
u8 *_data = (u8 *)&data;
u8 tx[4] = {
_reg[1],
_reg[0],
_data[1],
_data[0]
};
ret = i2c_master_send(client, tx, 4);
if (ret < 0)
dev_err(&client->dev, "I2C write error\n");
return ret;
}
static int ad714x_i2c_read(struct device *dev, unsigned short reg,
unsigned short *data)
{
struct i2c_client *client = to_i2c_client(dev);
int ret = 0;
u8 *_reg = (u8 *)&reg;
u8 *_data = (u8 *)data;
u8 tx[2] = {
_reg[1],
_reg[0]
};
u8 rx[2];
ret = i2c_master_send(client, tx, 2);
if (ret >= 0)
ret = i2c_master_recv(client, rx, 2);
if (unlikely(ret < 0)) {
dev_err(&client->dev, "I2C read error\n");
} else {
_data[0] = rx[1];
_data[1] = rx[0];
}
return ret;
}
static int __devinit ad714x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ad714x_chip *chip;
chip = ad714x_probe(&client->dev, BUS_I2C, client->irq,
ad714x_i2c_read, ad714x_i2c_write);
if (IS_ERR(chip))
return PTR_ERR(chip);
i2c_set_clientdata(client, chip);
return 0;
}
static int __devexit ad714x_i2c_remove(struct i2c_client *client)
{
struct ad714x_chip *chip = i2c_get_clientdata(client);
ad714x_remove(chip);
i2c_set_clientdata(client, NULL);
return 0;
}
static const struct i2c_device_id ad714x_id[] = {
{ "ad7142_captouch", 0 },
{ "ad7143_captouch", 0 },
{ "ad7147_captouch", 0 },
{ "ad7147a_captouch", 0 },
{ "ad7148_captouch", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ad714x_id);
static struct i2c_driver ad714x_i2c_driver = {
.driver = {
.name = "ad714x_captouch",
},
.probe = ad714x_i2c_probe,
.remove = __devexit_p(ad714x_i2c_remove),
.suspend = ad714x_i2c_suspend,
.resume = ad714x_i2c_resume,
.id_table = ad714x_id,
};
static __init int ad714x_i2c_init(void)
{
return i2c_add_driver(&ad714x_i2c_driver);
}
module_init(ad714x_i2c_init);
static __exit void ad714x_i2c_exit(void)
{
i2c_del_driver(&ad714x_i2c_driver);
}
module_exit(ad714x_i2c_exit);
MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor I2C Bus Driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_LICENSE("GPL");
/*
* AD714X CapTouch Programmable Controller driver (SPI bus)
*
* Copyright 2009 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <linux/input.h> /* BUS_I2C */
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include "ad714x.h"
#define AD714x_SPI_CMD_PREFIX 0xE000 /* bits 15:11 */
#define AD714x_SPI_READ BIT(10)
#ifdef CONFIG_PM
static int ad714x_spi_suspend(struct spi_device *spi, pm_message_t message)
{
return ad714x_disable(spi_get_drvdata(spi));
}
static int ad714x_spi_resume(struct spi_device *spi)
{
return ad714x_enable(spi_get_drvdata(spi));
}
#else
# define ad714x_spi_suspend NULL
# define ad714x_spi_resume NULL
#endif
static int ad714x_spi_read(struct device *dev, unsigned short reg,
unsigned short *data)
{
struct spi_device *spi = to_spi_device(dev);
unsigned short tx = AD714x_SPI_CMD_PREFIX | AD714x_SPI_READ | reg;
return spi_write_then_read(spi, (u8 *)&tx, 2, (u8 *)data, 2);
}
static int ad714x_spi_write(struct device *dev, unsigned short reg,
unsigned short data)
{
struct spi_device *spi = to_spi_device(dev);
unsigned short tx[2] = {
AD714x_SPI_CMD_PREFIX | reg,
data
};
return spi_write(spi, (u8 *)tx, 4);
}
static int __devinit ad714x_spi_probe(struct spi_device *spi)
{
struct ad714x_chip *chip;
chip = ad714x_probe(&spi->dev, BUS_SPI, spi->irq,
ad714x_spi_read, ad714x_spi_write);
if (IS_ERR(chip))
return PTR_ERR(chip);
spi_set_drvdata(spi, chip);
return 0;
}
static int __devexit ad714x_spi_remove(struct spi_device *spi)
{
struct ad714x_chip *chip = spi_get_drvdata(spi);
ad714x_remove(chip);
spi_set_drvdata(spi, NULL);
return 0;
}
static struct spi_driver ad714x_spi_driver = {
.driver = {
.name = "ad714x_captouch",
.owner = THIS_MODULE,
},
.probe = ad714x_spi_probe,
.remove = __devexit_p(ad714x_spi_remove),
.suspend = ad714x_spi_suspend,
.resume = ad714x_spi_resume,
};
static __init int ad714x_spi_init(void)
{
return spi_register_driver(&ad714x_spi_driver);
}
module_init(ad714x_spi_init);
static __exit void ad714x_spi_exit(void)
{
spi_unregister_driver(&ad714x_spi_driver);
}
module_exit(ad714x_spi_exit);
MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor SPI Bus Driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_LICENSE("GPL");
/*
* AD714X CapTouch Programmable Controller driver supporting AD7142/3/7/8/7A
*
* Copyright 2009 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <linux/device.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/input/ad714x.h>
#include "ad714x.h"
#define AD714X_PWR_CTRL 0x0
#define AD714X_STG_CAL_EN_REG 0x1
#define AD714X_AMB_COMP_CTRL0_REG 0x2
#define AD714X_PARTID_REG 0x17
#define AD7142_PARTID 0xE620
#define AD7143_PARTID 0xE630
#define AD7147_PARTID 0x1470
#define AD7148_PARTID 0x1480
#define AD714X_STAGECFG_REG 0x80
#define AD714X_SYSCFG_REG 0x0
#define STG_LOW_INT_EN_REG 0x5
#define STG_HIGH_INT_EN_REG 0x6
#define STG_COM_INT_EN_REG 0x7
#define STG_LOW_INT_STA_REG 0x8
#define STG_HIGH_INT_STA_REG 0x9
#define STG_COM_INT_STA_REG 0xA
#define CDC_RESULT_S0 0xB
#define CDC_RESULT_S1 0xC
#define CDC_RESULT_S2 0xD
#define CDC_RESULT_S3 0xE
#define CDC_RESULT_S4 0xF
#define CDC_RESULT_S5 0x10
#define CDC_RESULT_S6 0x11
#define CDC_RESULT_S7 0x12
#define CDC_RESULT_S8 0x13
#define CDC_RESULT_S9 0x14
#define CDC_RESULT_S10 0x15
#define CDC_RESULT_S11 0x16
#define STAGE0_AMBIENT 0xF1
#define STAGE1_AMBIENT 0x115
#define STAGE2_AMBIENT 0x139
#define STAGE3_AMBIENT 0x15D
#define STAGE4_AMBIENT 0x181
#define STAGE5_AMBIENT 0x1A5
#define STAGE6_AMBIENT 0x1C9
#define STAGE7_AMBIENT 0x1ED
#define STAGE8_AMBIENT 0x211
#define STAGE9_AMBIENT 0x234
#define STAGE10_AMBIENT 0x259
#define STAGE11_AMBIENT 0x27D
#define PER_STAGE_REG_NUM 36
#define STAGE_NUM 12
#define STAGE_CFGREG_NUM 8
#define SYS_CFGREG_NUM 8
/*
* driver information which will be used to maintain the software flow
*/
enum ad714x_device_state { IDLE, JITTER, ACTIVE, SPACE };
struct ad714x_slider_drv {
int highest_stage;
int abs_pos;
int flt_pos;
enum ad714x_device_state state;
struct input_dev *input;
};
struct ad714x_wheel_drv {
int abs_pos;
int flt_pos;
int pre_mean_value;
int pre_highest_stage;
int pre_mean_value_no_offset;
int mean_value;
int mean_value_no_offset;
int pos_offset;
int pos_ratio;
int highest_stage;
enum ad714x_device_state state;
struct input_dev *input;
};
struct ad714x_touchpad_drv {
int x_highest_stage;
int x_flt_pos;
int x_abs_pos;
int y_highest_stage;
int y_flt_pos;
int y_abs_pos;
int left_ep;
int left_ep_val;
int right_ep;
int right_ep_val;
int top_ep;
int top_ep_val;
int bottom_ep;
int bottom_ep_val;
enum ad714x_device_state state;
struct input_dev *input;
};
struct ad714x_button_drv {
enum ad714x_device_state state;
/*
* Unlike slider/wheel/touchpad, all buttons point to
* same input_dev instance
*/
struct input_dev *input;
};
struct ad714x_driver_data {
struct ad714x_slider_drv *slider;
struct ad714x_wheel_drv *wheel;
struct ad714x_touchpad_drv *touchpad;
struct ad714x_button_drv *button;
};
/*
* information to integrate all things which will be private data
* of spi/i2c device
*/
struct ad714x_chip {
unsigned short h_state;
unsigned short l_state;
unsigned short c_state;
unsigned short adc_reg[STAGE_NUM];
unsigned short amb_reg[STAGE_NUM];
unsigned short sensor_val[STAGE_NUM];
struct ad714x_platform_data *hw;
struct ad714x_driver_data *sw;
int irq;
struct device *dev;
ad714x_read_t read;
ad714x_write_t write;
struct mutex mutex;
unsigned product;
unsigned version;
};
static void ad714x_use_com_int(struct ad714x_chip *ad714x,
int start_stage, int end_stage)
{
unsigned short data;
unsigned short mask;
mask = ((1 << (end_stage + 1)) - 1) - (1 << start_stage);
ad714x->read(ad714x->dev, STG_COM_INT_EN_REG, &data);
data |= 1 << start_stage;
ad714x->write(ad714x->dev, STG_COM_INT_EN_REG, data);
ad714x->read(ad714x->dev, STG_HIGH_INT_EN_REG, &data);
data &= ~mask;
ad714x->write(ad714x->dev, STG_HIGH_INT_EN_REG, data);
}
static void ad714x_use_thr_int(struct ad714x_chip *ad714x,
int start_stage, int end_stage)
{
unsigned short data;
unsigned short mask;
mask = ((1 << (end_stage + 1)) - 1) - (1 << start_stage);
ad714x->read(ad714x->dev, STG_COM_INT_EN_REG, &data);
data &= ~(1 << start_stage);
ad714x->write(ad714x->dev, STG_COM_INT_EN_REG, data);
ad714x->read(ad714x->dev, STG_HIGH_INT_EN_REG, &data);
data |= mask;
ad714x->write(ad714x->dev, STG_HIGH_INT_EN_REG, data);
}
static int ad714x_cal_highest_stage(struct ad714x_chip *ad714x,
int start_stage, int end_stage)
{
int max_res = 0;
int max_idx = 0;
int i;
for (i = start_stage; i <= end_stage; i++) {
if (ad714x->sensor_val[i] > max_res) {
max_res = ad714x->sensor_val[i];
max_idx = i;
}
}
return max_idx;
}
static int ad714x_cal_abs_pos(struct ad714x_chip *ad714x,
int start_stage, int end_stage,
int highest_stage, int max_coord)
{
int a_param, b_param;
if (highest_stage == start_stage) {
a_param = ad714x->sensor_val[start_stage + 1];
b_param = ad714x->sensor_val[start_stage] +
ad714x->sensor_val[start_stage + 1];
} else if (highest_stage == end_stage) {
a_param = ad714x->sensor_val[end_stage] *
(end_stage - start_stage) +
ad714x->sensor_val[end_stage - 1] *
(end_stage - start_stage - 1);
b_param = ad714x->sensor_val[end_stage] +
ad714x->sensor_val[end_stage - 1];
} else {
a_param = ad714x->sensor_val[highest_stage] *
(highest_stage - start_stage) +
ad714x->sensor_val[highest_stage - 1] *
(highest_stage - start_stage - 1) +
ad714x->sensor_val[highest_stage + 1] *
(highest_stage - start_stage + 1);
b_param = ad714x->sensor_val[highest_stage] +
ad714x->sensor_val[highest_stage - 1] +
ad714x->sensor_val[highest_stage + 1];
}
return (max_coord / (end_stage - start_stage)) * a_param / b_param;
}
/*
* One button can connect to multi positive and negative of CDCs
* Multi-buttons can connect to same positive/negative of one CDC
*/
static void ad714x_button_state_machine(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_button_plat *hw = &ad714x->hw->button[idx];
struct ad714x_button_drv *sw = &ad714x->sw->button[idx];
switch (sw->state) {
case IDLE:
if (((ad714x->h_state & hw->h_mask) == hw->h_mask) &&
((ad714x->l_state & hw->l_mask) == hw->l_mask)) {
dev_dbg(ad714x->dev, "button %d touched\n", idx);
input_report_key(sw->input, hw->keycode, 1);
input_sync(sw->input);
sw->state = ACTIVE;
}
break;
case ACTIVE:
if (((ad714x->h_state & hw->h_mask) != hw->h_mask) ||
((ad714x->l_state & hw->l_mask) != hw->l_mask)) {
dev_dbg(ad714x->dev, "button %d released\n", idx);
input_report_key(sw->input, hw->keycode, 0);
input_sync(sw->input);
sw->state = IDLE;
}
break;
default:
break;
}
}
/*
* The response of a sensor is defined by the absolute number of codes
* between the current CDC value and the ambient value.
*/
static void ad714x_slider_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
int i;
for (i = hw->start_stage; i <= hw->end_stage; i++) {
ad714x->read(ad714x->dev, CDC_RESULT_S0 + i,
&ad714x->adc_reg[i]);
ad714x->read(ad714x->dev,
STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
&ad714x->amb_reg[i]);
ad714x->sensor_val[i] = abs(ad714x->adc_reg[i] -
ad714x->amb_reg[i]);
}
}
static void ad714x_slider_cal_highest_stage(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
sw->highest_stage = ad714x_cal_highest_stage(ad714x, hw->start_stage,
hw->end_stage);
dev_dbg(ad714x->dev, "slider %d highest_stage:%d\n", idx,
sw->highest_stage);
}
/*
* The formulae are very straight forward. It uses the sensor with the
* highest response and the 2 adjacent ones.
* When Sensor 0 has the highest response, only sensor 0 and sensor 1
* are used in the calculations. Similarly when the last sensor has the
* highest response, only the last sensor and the second last sensors
* are used in the calculations.
*
* For i= idx_of_peak_Sensor-1 to i= idx_of_peak_Sensor+1
* v += Sensor response(i)*i
* w += Sensor response(i)
* POS=(Number_of_Positions_Wanted/(Number_of_Sensors_Used-1)) *(v/w)
*/
static void ad714x_slider_cal_abs_pos(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
sw->abs_pos = ad714x_cal_abs_pos(ad714x, hw->start_stage, hw->end_stage,
sw->highest_stage, hw->max_coord);
dev_dbg(ad714x->dev, "slider %d absolute position:%d\n", idx,
sw->abs_pos);
}
/*
* To minimise the Impact of the noise on the algorithm, ADI developed a
* routine that filters the CDC results after they have been read by the
* host processor.
* The filter used is an Infinite Input Response(IIR) filter implemented
* in firmware and attenuates the noise on the CDC results after they've
* been read by the host processor.
* Filtered_CDC_result = (Filtered_CDC_result * (10 - Coefficient) +
* Latest_CDC_result * Coefficient)/10
*/
static void ad714x_slider_cal_flt_pos(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
sw->flt_pos = (sw->flt_pos * (10 - 4) +
sw->abs_pos * 4)/10;
dev_dbg(ad714x->dev, "slider %d filter position:%d\n", idx,
sw->flt_pos);
}
static void ad714x_slider_use_com_int(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
ad714x_use_com_int(ad714x, hw->start_stage, hw->end_stage);
}
static void ad714x_slider_use_thr_int(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
ad714x_use_thr_int(ad714x, hw->start_stage, hw->end_stage);
}
static void ad714x_slider_state_machine(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
unsigned short h_state, c_state;
unsigned short mask;
mask = ((1 << (hw->end_stage + 1)) - 1) - ((1 << hw->start_stage) - 1);
h_state = ad714x->h_state & mask;
c_state = ad714x->c_state & mask;
switch (sw->state) {
case IDLE:
if (h_state) {
sw->state = JITTER;
/* In End of Conversion interrupt mode, the AD714X
* continuously generates hardware interrupts.
*/
ad714x_slider_use_com_int(ad714x, idx);
dev_dbg(ad714x->dev, "slider %d touched\n", idx);
}
break;
case JITTER:
if (c_state == mask) {
ad714x_slider_cal_sensor_val(ad714x, idx);
ad714x_slider_cal_highest_stage(ad714x, idx);
ad714x_slider_cal_abs_pos(ad714x, idx);
sw->flt_pos = sw->abs_pos;
sw->state = ACTIVE;
}
break;
case ACTIVE:
if (c_state == mask) {
if (h_state) {
ad714x_slider_cal_sensor_val(ad714x, idx);
ad714x_slider_cal_highest_stage(ad714x, idx);
ad714x_slider_cal_abs_pos(ad714x, idx);
ad714x_slider_cal_flt_pos(ad714x, idx);
input_report_abs(sw->input, ABS_X, sw->flt_pos);
input_report_key(sw->input, BTN_TOUCH, 1);
} else {
/* When the user lifts off the sensor, configure
* the AD714X back to threshold interrupt mode.
*/
ad714x_slider_use_thr_int(ad714x, idx);
sw->state = IDLE;
input_report_key(sw->input, BTN_TOUCH, 0);
dev_dbg(ad714x->dev, "slider %d released\n",
idx);
}
input_sync(sw->input);
}
break;
default:
break;
}
}
/*
* When the scroll wheel is activated, we compute the absolute position based
* on the sensor values. To calculate the position, we first determine the
* sensor that has the greatest response among the 8 sensors that constitutes
* the scrollwheel. Then we determined the 2 sensors on either sides of the
* sensor with the highest response and we apply weights to these sensors.
*/
static void ad714x_wheel_cal_highest_stage(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
sw->pre_highest_stage = sw->highest_stage;
sw->highest_stage = ad714x_cal_highest_stage(ad714x, hw->start_stage,
hw->end_stage);
dev_dbg(ad714x->dev, "wheel %d highest_stage:%d\n", idx,
sw->highest_stage);
}
static void ad714x_wheel_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
int i;
for (i = hw->start_stage; i <= hw->end_stage; i++) {
ad714x->read(ad714x->dev, CDC_RESULT_S0 + i,
&ad714x->adc_reg[i]);
ad714x->read(ad714x->dev,
STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
&ad714x->amb_reg[i]);
if (ad714x->adc_reg[i] > ad714x->amb_reg[i])
ad714x->sensor_val[i] = ad714x->adc_reg[i] -
ad714x->amb_reg[i];
else
ad714x->sensor_val[i] = 0;
}
}
/*
* When the scroll wheel is activated, we compute the absolute position based
* on the sensor values. To calculate the position, we first determine the
* sensor that has the greatest response among the 8 sensors that constitutes
* the scrollwheel. Then we determined the 2 sensors on either sides of the
* sensor with the highest response and we apply weights to these sensors. The
* result of this computation gives us the mean value which defined by the
* following formula:
* For i= second_before_highest_stage to i= second_after_highest_stage
* v += Sensor response(i)*WEIGHT*(i+3)
* w += Sensor response(i)
* Mean_Value=v/w
* pos_on_scrollwheel = (Mean_Value - position_offset) / position_ratio
*/
#define WEIGHT_FACTOR 30
/* This constant prevents the "PositionOffset" from reaching a big value */
#define OFFSET_POSITION_CLAMP 120
static void ad714x_wheel_cal_abs_pos(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
int stage_num = hw->end_stage - hw->start_stage + 1;
int second_before, first_before, highest, first_after, second_after;
int a_param, b_param;
/* Calculate Mean value */
second_before = (sw->highest_stage + stage_num - 2) % stage_num;
first_before = (sw->highest_stage + stage_num - 1) % stage_num;
highest = sw->highest_stage;
first_after = (sw->highest_stage + stage_num + 1) % stage_num;
second_after = (sw->highest_stage + stage_num + 2) % stage_num;
if (((sw->highest_stage - hw->start_stage) > 1) &&
((hw->end_stage - sw->highest_stage) > 1)) {
a_param = ad714x->sensor_val[second_before] *
(second_before - hw->start_stage + 3) +
ad714x->sensor_val[first_before] *
(second_before - hw->start_stage + 3) +
ad714x->sensor_val[highest] *
(second_before - hw->start_stage + 3) +
ad714x->sensor_val[first_after] *
(first_after - hw->start_stage + 3) +
ad714x->sensor_val[second_after] *
(second_after - hw->start_stage + 3);
} else {
a_param = ad714x->sensor_val[second_before] *
(second_before - hw->start_stage + 1) +
ad714x->sensor_val[first_before] *
(second_before - hw->start_stage + 2) +
ad714x->sensor_val[highest] *
(second_before - hw->start_stage + 3) +
ad714x->sensor_val[first_after] *
(first_after - hw->start_stage + 4) +
ad714x->sensor_val[second_after] *
(second_after - hw->start_stage + 5);
}
a_param *= WEIGHT_FACTOR;
b_param = ad714x->sensor_val[second_before] +
ad714x->sensor_val[first_before] +
ad714x->sensor_val[highest] +
ad714x->sensor_val[first_after] +
ad714x->sensor_val[second_after];
sw->pre_mean_value = sw->mean_value;
sw->mean_value = a_param / b_param;
/* Calculate the offset */
if ((sw->pre_highest_stage == hw->end_stage) &&
(sw->highest_stage == hw->start_stage))
sw->pos_offset = sw->mean_value;
else if ((sw->pre_highest_stage == hw->start_stage) &&
(sw->highest_stage == hw->end_stage))
sw->pos_offset = sw->pre_mean_value;
if (sw->pos_offset > OFFSET_POSITION_CLAMP)
sw->pos_offset = OFFSET_POSITION_CLAMP;
/* Calculate the mean value without the offset */
sw->pre_mean_value_no_offset = sw->mean_value_no_offset;
sw->mean_value_no_offset = sw->mean_value - sw->pos_offset;
if (sw->mean_value_no_offset < 0)
sw->mean_value_no_offset = 0;
/* Calculate ratio to scale down to NUMBER_OF_WANTED_POSITIONS */
if ((sw->pre_highest_stage == hw->end_stage) &&
(sw->highest_stage == hw->start_stage))
sw->pos_ratio = (sw->pre_mean_value_no_offset * 100) /
hw->max_coord;
else if ((sw->pre_highest_stage == hw->start_stage) &&
(sw->highest_stage == hw->end_stage))
sw->pos_ratio = (sw->mean_value_no_offset * 100) /
hw->max_coord;
sw->abs_pos = (sw->mean_value_no_offset * 100) / sw->pos_ratio;
if (sw->abs_pos > hw->max_coord)
sw->abs_pos = hw->max_coord;
}
static void ad714x_wheel_cal_flt_pos(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
if (((sw->pre_highest_stage == hw->end_stage) &&
(sw->highest_stage == hw->start_stage)) ||
((sw->pre_highest_stage == hw->start_stage) &&
(sw->highest_stage == hw->end_stage)))
sw->flt_pos = sw->abs_pos;
else
sw->flt_pos = ((sw->flt_pos * 30) + (sw->abs_pos * 71)) / 100;
if (sw->flt_pos > hw->max_coord)
sw->flt_pos = hw->max_coord;
}
static void ad714x_wheel_use_com_int(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
ad714x_use_com_int(ad714x, hw->start_stage, hw->end_stage);
}
static void ad714x_wheel_use_thr_int(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
ad714x_use_thr_int(ad714x, hw->start_stage, hw->end_stage);
}
static void ad714x_wheel_state_machine(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
unsigned short h_state, c_state;
unsigned short mask;
mask = ((1 << (hw->end_stage + 1)) - 1) - ((1 << hw->start_stage) - 1);
h_state = ad714x->h_state & mask;
c_state = ad714x->c_state & mask;
switch (sw->state) {
case IDLE:
if (h_state) {
sw->state = JITTER;
/* In End of Conversion interrupt mode, the AD714X
* continuously generates hardware interrupts.
*/
ad714x_wheel_use_com_int(ad714x, idx);
dev_dbg(ad714x->dev, "wheel %d touched\n", idx);
}
break;
case JITTER:
if (c_state == mask) {
ad714x_wheel_cal_sensor_val(ad714x, idx);
ad714x_wheel_cal_highest_stage(ad714x, idx);
ad714x_wheel_cal_abs_pos(ad714x, idx);
sw->flt_pos = sw->abs_pos;
sw->state = ACTIVE;
}
break;
case ACTIVE:
if (c_state == mask) {
if (h_state) {
ad714x_wheel_cal_sensor_val(ad714x, idx);
ad714x_wheel_cal_highest_stage(ad714x, idx);
ad714x_wheel_cal_abs_pos(ad714x, idx);
ad714x_wheel_cal_flt_pos(ad714x, idx);
input_report_abs(sw->input, ABS_WHEEL,
sw->abs_pos);
input_report_key(sw->input, BTN_TOUCH, 1);
} else {
/* When the user lifts off the sensor, configure
* the AD714X back to threshold interrupt mode.
*/
ad714x_wheel_use_thr_int(ad714x, idx);
sw->state = IDLE;
input_report_key(sw->input, BTN_TOUCH, 0);
dev_dbg(ad714x->dev, "wheel %d released\n",
idx);
}
input_sync(sw->input);
}
break;
default:
break;
}
}
static void touchpad_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
int i;
for (i = hw->x_start_stage; i <= hw->x_end_stage; i++) {
ad714x->read(ad714x->dev, CDC_RESULT_S0 + i,
&ad714x->adc_reg[i]);
ad714x->read(ad714x->dev,
STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
&ad714x->amb_reg[i]);
if (ad714x->adc_reg[i] > ad714x->amb_reg[i])
ad714x->sensor_val[i] = ad714x->adc_reg[i] -
ad714x->amb_reg[i];
else
ad714x->sensor_val[i] = 0;
}
}
static void touchpad_cal_highest_stage(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
sw->x_highest_stage = ad714x_cal_highest_stage(ad714x,
hw->x_start_stage, hw->x_end_stage);
sw->y_highest_stage = ad714x_cal_highest_stage(ad714x,
hw->y_start_stage, hw->y_end_stage);
dev_dbg(ad714x->dev,
"touchpad %d x_highest_stage:%d, y_highest_stage:%d\n",
idx, sw->x_highest_stage, sw->y_highest_stage);
}
/*
* If 2 fingers are touching the sensor then 2 peaks can be observed in the
* distribution.
* The arithmetic doesn't support to get absolute coordinates for multi-touch
* yet.
*/
static int touchpad_check_second_peak(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
int i;
for (i = hw->x_start_stage; i < sw->x_highest_stage; i++) {
if ((ad714x->sensor_val[i] - ad714x->sensor_val[i + 1])
> (ad714x->sensor_val[i + 1] / 10))
return 1;
}
for (i = sw->x_highest_stage; i < hw->x_end_stage; i++) {
if ((ad714x->sensor_val[i + 1] - ad714x->sensor_val[i])
> (ad714x->sensor_val[i] / 10))
return 1;
}
for (i = hw->y_start_stage; i < sw->y_highest_stage; i++) {
if ((ad714x->sensor_val[i] - ad714x->sensor_val[i + 1])
> (ad714x->sensor_val[i + 1] / 10))
return 1;
}
for (i = sw->y_highest_stage; i < hw->y_end_stage; i++) {
if ((ad714x->sensor_val[i + 1] - ad714x->sensor_val[i])
> (ad714x->sensor_val[i] / 10))
return 1;
}
return 0;
}
/*
* If only one finger is used to activate the touch pad then only 1 peak will be
* registered in the distribution. This peak and the 2 adjacent sensors will be
* used in the calculation of the absolute position. This will prevent hand
* shadows to affect the absolute position calculation.
*/
static void touchpad_cal_abs_pos(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
sw->x_abs_pos = ad714x_cal_abs_pos(ad714x, hw->x_start_stage,
hw->x_end_stage, sw->x_highest_stage, hw->x_max_coord);
sw->y_abs_pos = ad714x_cal_abs_pos(ad714x, hw->y_start_stage,
hw->y_end_stage, sw->y_highest_stage, hw->y_max_coord);
dev_dbg(ad714x->dev, "touchpad %d absolute position:(%d, %d)\n", idx,
sw->x_abs_pos, sw->y_abs_pos);
}
static void touchpad_cal_flt_pos(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
sw->x_flt_pos = (sw->x_flt_pos * (10 - 4) +
sw->x_abs_pos * 4)/10;
sw->y_flt_pos = (sw->y_flt_pos * (10 - 4) +
sw->y_abs_pos * 4)/10;
dev_dbg(ad714x->dev, "touchpad %d filter position:(%d, %d)\n",
idx, sw->x_flt_pos, sw->y_flt_pos);
}
/*
* To prevent distortion from showing in the absolute position, it is
* necessary to detect the end points. When endpoints are detected, the
* driver stops updating the status variables with absolute positions.
* End points are detected on the 4 edges of the touchpad sensor. The
* method to detect them is the same for all 4.
* To detect the end points, the firmware computes the difference in
* percent between the sensor on the edge and the adjacent one. The
* difference is calculated in percent in order to make the end point
* detection independent of the pressure.
*/
#define LEFT_END_POINT_DETECTION_LEVEL 550
#define RIGHT_END_POINT_DETECTION_LEVEL 750
#define LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL 850
#define TOP_END_POINT_DETECTION_LEVEL 550
#define BOTTOM_END_POINT_DETECTION_LEVEL 950
#define TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL 700
static int touchpad_check_endpoint(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
int percent_sensor_diff;
/* left endpoint detect */
percent_sensor_diff = (ad714x->sensor_val[hw->x_start_stage] -
ad714x->sensor_val[hw->x_start_stage + 1]) * 100 /
ad714x->sensor_val[hw->x_start_stage + 1];
if (!sw->left_ep) {
if (percent_sensor_diff >= LEFT_END_POINT_DETECTION_LEVEL) {
sw->left_ep = 1;
sw->left_ep_val =
ad714x->sensor_val[hw->x_start_stage + 1];
}
} else {
if ((percent_sensor_diff < LEFT_END_POINT_DETECTION_LEVEL) &&
(ad714x->sensor_val[hw->x_start_stage + 1] >
LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw->left_ep_val))
sw->left_ep = 0;
}
/* right endpoint detect */
percent_sensor_diff = (ad714x->sensor_val[hw->x_end_stage] -
ad714x->sensor_val[hw->x_end_stage - 1]) * 100 /
ad714x->sensor_val[hw->x_end_stage - 1];
if (!sw->right_ep) {
if (percent_sensor_diff >= RIGHT_END_POINT_DETECTION_LEVEL) {
sw->right_ep = 1;
sw->right_ep_val =
ad714x->sensor_val[hw->x_end_stage - 1];
}
} else {
if ((percent_sensor_diff < RIGHT_END_POINT_DETECTION_LEVEL) &&
(ad714x->sensor_val[hw->x_end_stage - 1] >
LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw->right_ep_val))
sw->right_ep = 0;
}
/* top endpoint detect */
percent_sensor_diff = (ad714x->sensor_val[hw->y_start_stage] -
ad714x->sensor_val[hw->y_start_stage + 1]) * 100 /
ad714x->sensor_val[hw->y_start_stage + 1];
if (!sw->top_ep) {
if (percent_sensor_diff >= TOP_END_POINT_DETECTION_LEVEL) {
sw->top_ep = 1;
sw->top_ep_val =
ad714x->sensor_val[hw->y_start_stage + 1];
}
} else {
if ((percent_sensor_diff < TOP_END_POINT_DETECTION_LEVEL) &&
(ad714x->sensor_val[hw->y_start_stage + 1] >
TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw->top_ep_val))
sw->top_ep = 0;
}
/* bottom endpoint detect */
percent_sensor_diff = (ad714x->sensor_val[hw->y_end_stage] -
ad714x->sensor_val[hw->y_end_stage - 1]) * 100 /
ad714x->sensor_val[hw->y_end_stage - 1];
if (!sw->bottom_ep) {
if (percent_sensor_diff >= BOTTOM_END_POINT_DETECTION_LEVEL) {
sw->bottom_ep = 1;
sw->bottom_ep_val =
ad714x->sensor_val[hw->y_end_stage - 1];
}
} else {
if ((percent_sensor_diff < BOTTOM_END_POINT_DETECTION_LEVEL) &&
(ad714x->sensor_val[hw->y_end_stage - 1] >
TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw->bottom_ep_val))
sw->bottom_ep = 0;
}
return sw->left_ep || sw->right_ep || sw->top_ep || sw->bottom_ep;
}
static void touchpad_use_com_int(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
ad714x_use_com_int(ad714x, hw->x_start_stage, hw->x_end_stage);
}
static void touchpad_use_thr_int(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
ad714x_use_thr_int(ad714x, hw->x_start_stage, hw->x_end_stage);
ad714x_use_thr_int(ad714x, hw->y_start_stage, hw->y_end_stage);
}
static void ad714x_touchpad_state_machine(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
unsigned short h_state, c_state;
unsigned short mask;
mask = (((1 << (hw->x_end_stage + 1)) - 1) -
((1 << hw->x_start_stage) - 1)) +
(((1 << (hw->y_end_stage + 1)) - 1) -
((1 << hw->y_start_stage) - 1));
h_state = ad714x->h_state & mask;
c_state = ad714x->c_state & mask;
switch (sw->state) {
case IDLE:
if (h_state) {
sw->state = JITTER;
/* In End of Conversion interrupt mode, the AD714X
* continuously generates hardware interrupts.
*/
touchpad_use_com_int(ad714x, idx);
dev_dbg(ad714x->dev, "touchpad %d touched\n", idx);
}
break;
case JITTER:
if (c_state == mask) {
touchpad_cal_sensor_val(ad714x, idx);
touchpad_cal_highest_stage(ad714x, idx);
if ((!touchpad_check_second_peak(ad714x, idx)) &&
(!touchpad_check_endpoint(ad714x, idx))) {
dev_dbg(ad714x->dev,
"touchpad%d, 2 fingers or endpoint\n",
idx);
touchpad_cal_abs_pos(ad714x, idx);
sw->x_flt_pos = sw->x_abs_pos;
sw->y_flt_pos = sw->y_abs_pos;
sw->state = ACTIVE;
}
}
break;
case ACTIVE:
if (c_state == mask) {
if (h_state) {
touchpad_cal_sensor_val(ad714x, idx);
touchpad_cal_highest_stage(ad714x, idx);
if ((!touchpad_check_second_peak(ad714x, idx))
&& (!touchpad_check_endpoint(ad714x, idx))) {
touchpad_cal_abs_pos(ad714x, idx);
touchpad_cal_flt_pos(ad714x, idx);
input_report_abs(sw->input, ABS_X,
sw->x_flt_pos);
input_report_abs(sw->input, ABS_Y,
sw->y_flt_pos);
input_report_key(sw->input, BTN_TOUCH,
1);
}
} else {
/* When the user lifts off the sensor, configure
* the AD714X back to threshold interrupt mode.
*/
touchpad_use_thr_int(ad714x, idx);
sw->state = IDLE;
input_report_key(sw->input, BTN_TOUCH, 0);
dev_dbg(ad714x->dev, "touchpad %d released\n",
idx);
}
input_sync(sw->input);
}
break;
default:
break;
}
}
static int ad714x_hw_detect(struct ad714x_chip *ad714x)
{
unsigned short data;
ad714x->read(ad714x->dev, AD714X_PARTID_REG, &data);
switch (data & 0xFFF0) {
case AD7142_PARTID:
ad714x->product = 0x7142;
ad714x->version = data & 0xF;
dev_info(ad714x->dev, "found AD7142 captouch, rev:%d\n",
ad714x->version);
return 0;
case AD7143_PARTID:
ad714x->product = 0x7143;
ad714x->version = data & 0xF;
dev_info(ad714x->dev, "found AD7143 captouch, rev:%d\n",
ad714x->version);
return 0;
case AD7147_PARTID:
ad714x->product = 0x7147;
ad714x->version = data & 0xF;
dev_info(ad714x->dev, "found AD7147(A) captouch, rev:%d\n",
ad714x->version);
return 0;
case AD7148_PARTID:
ad714x->product = 0x7148;
ad714x->version = data & 0xF;
dev_info(ad714x->dev, "found AD7148 captouch, rev:%d\n",
ad714x->version);
return 0;
default:
dev_err(ad714x->dev,
"fail to detect AD714X captouch, read ID is %04x\n",
data);
return -ENODEV;
}
}
static void ad714x_hw_init(struct ad714x_chip *ad714x)
{
int i, j;
unsigned short reg_base;
unsigned short data;
/* configuration CDC and interrupts */
for (i = 0; i < STAGE_NUM; i++) {
reg_base = AD714X_STAGECFG_REG + i * STAGE_CFGREG_NUM;
for (j = 0; j < STAGE_CFGREG_NUM; j++)
ad714x->write(ad714x->dev, reg_base + j,
ad714x->hw->stage_cfg_reg[i][j]);
}
for (i = 0; i < SYS_CFGREG_NUM; i++)
ad714x->write(ad714x->dev, AD714X_SYSCFG_REG + i,
ad714x->hw->sys_cfg_reg[i]);
for (i = 0; i < SYS_CFGREG_NUM; i++)
ad714x->read(ad714x->dev, AD714X_SYSCFG_REG + i,
&data);
ad714x->write(ad714x->dev, AD714X_STG_CAL_EN_REG, 0xFFF);
/* clear all interrupts */
ad714x->read(ad714x->dev, STG_LOW_INT_STA_REG, &data);
ad714x->read(ad714x->dev, STG_HIGH_INT_STA_REG, &data);
ad714x->read(ad714x->dev, STG_COM_INT_STA_REG, &data);
}
static irqreturn_t ad714x_interrupt_thread(int irq, void *data)
{
struct ad714x_chip *ad714x = data;
int i;
mutex_lock(&ad714x->mutex);
ad714x->read(ad714x->dev, STG_LOW_INT_STA_REG, &ad714x->l_state);
ad714x->read(ad714x->dev, STG_HIGH_INT_STA_REG, &ad714x->h_state);
ad714x->read(ad714x->dev, STG_COM_INT_STA_REG, &ad714x->c_state);
for (i = 0; i < ad714x->hw->button_num; i++)
ad714x_button_state_machine(ad714x, i);
for (i = 0; i < ad714x->hw->slider_num; i++)
ad714x_slider_state_machine(ad714x, i);
for (i = 0; i < ad714x->hw->wheel_num; i++)
ad714x_wheel_state_machine(ad714x, i);
for (i = 0; i < ad714x->hw->touchpad_num; i++)
ad714x_touchpad_state_machine(ad714x, i);
mutex_unlock(&ad714x->mutex);
return IRQ_HANDLED;
}
#define MAX_DEVICE_NUM 8
struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
ad714x_read_t read, ad714x_write_t write)
{
int i, alloc_idx;
int error;
struct input_dev *input[MAX_DEVICE_NUM];
struct ad714x_platform_data *plat_data = dev->platform_data;
struct ad714x_chip *ad714x;
void *drv_mem;
struct ad714x_button_drv *bt_drv;
struct ad714x_slider_drv *sd_drv;
struct ad714x_wheel_drv *wl_drv;
struct ad714x_touchpad_drv *tp_drv;
if (irq <= 0) {
dev_err(dev, "IRQ not configured!\n");
error = -EINVAL;
goto err_out;
}
if (dev->platform_data == NULL) {
dev_err(dev, "platform data for ad714x doesn't exist\n");
error = -EINVAL;
goto err_out;
}
ad714x = kzalloc(sizeof(*ad714x) + sizeof(*ad714x->sw) +
sizeof(*sd_drv) * plat_data->slider_num +
sizeof(*wl_drv) * plat_data->wheel_num +
sizeof(*tp_drv) * plat_data->touchpad_num +
sizeof(*bt_drv) * plat_data->button_num, GFP_KERNEL);
if (!ad714x) {
error = -ENOMEM;
goto err_out;
}
ad714x->hw = plat_data;
drv_mem = ad714x + 1;
ad714x->sw = drv_mem;
drv_mem += sizeof(*ad714x->sw);
ad714x->sw->slider = sd_drv = drv_mem;
drv_mem += sizeof(*sd_drv) * ad714x->hw->slider_num;
ad714x->sw->wheel = wl_drv = drv_mem;
drv_mem += sizeof(*wl_drv) * ad714x->hw->wheel_num;
ad714x->sw->touchpad = tp_drv = drv_mem;
drv_mem += sizeof(*tp_drv) * ad714x->hw->touchpad_num;
ad714x->sw->button = bt_drv = drv_mem;
drv_mem += sizeof(*bt_drv) * ad714x->hw->button_num;
ad714x->read = read;
ad714x->write = write;
ad714x->irq = irq;
ad714x->dev = dev;
error = ad714x_hw_detect(ad714x);
if (error)
goto err_free_mem;
/* initilize and request sw/hw resources */
ad714x_hw_init(ad714x);
mutex_init(&ad714x->mutex);
/*
* Allocate and register AD714X input device
*/
alloc_idx = 0;
/* a slider uses one input_dev instance */
if (ad714x->hw->slider_num > 0) {
struct ad714x_slider_plat *sd_plat = ad714x->hw->slider;
for (i = 0; i < ad714x->hw->slider_num; i++) {
sd_drv[i].input = input[alloc_idx] = input_allocate_device();
if (!input[alloc_idx]) {
error = -ENOMEM;
goto err_free_dev;
}
__set_bit(EV_ABS, input[alloc_idx]->evbit);
__set_bit(EV_KEY, input[alloc_idx]->evbit);
__set_bit(ABS_X, input[alloc_idx]->absbit);
__set_bit(BTN_TOUCH, input[alloc_idx]->keybit);
input_set_abs_params(input[alloc_idx],
ABS_X, 0, sd_plat->max_coord, 0, 0);
input[alloc_idx]->id.bustype = bus_type;
input[alloc_idx]->id.product = ad714x->product;
input[alloc_idx]->id.version = ad714x->version;
error = input_register_device(input[alloc_idx]);
if (error)
goto err_free_dev;
alloc_idx++;
}
}
/* a wheel uses one input_dev instance */
if (ad714x->hw->wheel_num > 0) {
struct ad714x_wheel_plat *wl_plat = ad714x->hw->wheel;
for (i = 0; i < ad714x->hw->wheel_num; i++) {
wl_drv[i].input = input[alloc_idx] = input_allocate_device();
if (!input[alloc_idx]) {
error = -ENOMEM;
goto err_free_dev;
}
__set_bit(EV_KEY, input[alloc_idx]->evbit);
__set_bit(EV_ABS, input[alloc_idx]->evbit);
__set_bit(ABS_WHEEL, input[alloc_idx]->absbit);
__set_bit(BTN_TOUCH, input[alloc_idx]->keybit);
input_set_abs_params(input[alloc_idx],
ABS_WHEEL, 0, wl_plat->max_coord, 0, 0);
input[alloc_idx]->id.bustype = bus_type;
input[alloc_idx]->id.product = ad714x->product;
input[alloc_idx]->id.version = ad714x->version;
error = input_register_device(input[alloc_idx]);
if (error)
goto err_free_dev;
alloc_idx++;
}
}
/* a touchpad uses one input_dev instance */
if (ad714x->hw->touchpad_num > 0) {
struct ad714x_touchpad_plat *tp_plat = ad714x->hw->touchpad;
for (i = 0; i < ad714x->hw->touchpad_num; i++) {
tp_drv[i].input = input[alloc_idx] = input_allocate_device();
if (!input[alloc_idx]) {
error = -ENOMEM;
goto err_free_dev;
}
__set_bit(EV_ABS, input[alloc_idx]->evbit);
__set_bit(EV_KEY, input[alloc_idx]->evbit);
__set_bit(ABS_X, input[alloc_idx]->absbit);
__set_bit(ABS_Y, input[alloc_idx]->absbit);
__set_bit(BTN_TOUCH, input[alloc_idx]->keybit);
input_set_abs_params(input[alloc_idx],
ABS_X, 0, tp_plat->x_max_coord, 0, 0);
input_set_abs_params(input[alloc_idx],
ABS_Y, 0, tp_plat->y_max_coord, 0, 0);
input[alloc_idx]->id.bustype = bus_type;
input[alloc_idx]->id.product = ad714x->product;
input[alloc_idx]->id.version = ad714x->version;
error = input_register_device(input[alloc_idx]);
if (error)
goto err_free_dev;
alloc_idx++;
}
}
/* all buttons use one input node */
if (ad714x->hw->button_num > 0) {
struct ad714x_button_plat *bt_plat = ad714x->hw->button;
input[alloc_idx] = input_allocate_device();
if (!input[alloc_idx]) {
error = -ENOMEM;
goto err_free_dev;
}
__set_bit(EV_KEY, input[alloc_idx]->evbit);
for (i = 0; i < ad714x->hw->button_num; i++) {
bt_drv[i].input = input[alloc_idx];
__set_bit(bt_plat[i].keycode, input[alloc_idx]->keybit);
}
input[alloc_idx]->id.bustype = bus_type;
input[alloc_idx]->id.product = ad714x->product;
input[alloc_idx]->id.version = ad714x->version;
error = input_register_device(input[alloc_idx]);
if (error)
goto err_free_dev;
alloc_idx++;
}
error = request_threaded_irq(ad714x->irq, NULL, ad714x_interrupt_thread,
IRQF_TRIGGER_FALLING, "ad714x_captouch", ad714x);
if (error) {
dev_err(dev, "can't allocate irq %d\n", ad714x->irq);
goto err_unreg_dev;
}
return ad714x;
err_free_dev:
dev_err(dev, "failed to setup AD714x input device %i\n", alloc_idx);
input_free_device(input[alloc_idx]);
err_unreg_dev:
while (--alloc_idx >= 0)
input_unregister_device(input[alloc_idx]);
err_free_mem:
kfree(ad714x);
err_out:
return ERR_PTR(error);
}
EXPORT_SYMBOL(ad714x_probe);
void ad714x_remove(struct ad714x_chip *ad714x)
{
struct ad714x_platform_data *hw = ad714x->hw;
struct ad714x_driver_data *sw = ad714x->sw;
int i;
free_irq(ad714x->irq, ad714x);
/* unregister and free all input devices */
for (i = 0; i < hw->slider_num; i++)
input_unregister_device(sw->slider[i].input);
for (i = 0; i < hw->wheel_num; i++)
input_unregister_device(sw->wheel[i].input);
for (i = 0; i < hw->touchpad_num; i++)
input_unregister_device(sw->touchpad[i].input);
if (hw->button_num)
input_unregister_device(sw->button[0].input);
kfree(ad714x);
}
EXPORT_SYMBOL(ad714x_remove);
#ifdef CONFIG_PM
int ad714x_disable(struct ad714x_chip *ad714x)
{
unsigned short data;
dev_dbg(ad714x->dev, "%s enter\n", __func__);
mutex_lock(&ad714x->mutex);
data = ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL] | 0x3;
ad714x->write(ad714x->dev, AD714X_PWR_CTRL, data);
mutex_unlock(&ad714x->mutex);
return 0;
}
EXPORT_SYMBOL(ad714x_disable);
int ad714x_enable(struct ad714x_chip *ad714x)
{
unsigned short data;
dev_dbg(ad714x->dev, "%s enter\n", __func__);
mutex_lock(&ad714x->mutex);
/* resume to non-shutdown mode */
ad714x->write(ad714x->dev, AD714X_PWR_CTRL,
ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL]);
/* make sure the interrupt output line is not low level after resume,
* otherwise we will get no chance to enter falling-edge irq again
*/
ad714x->read(ad714x->dev, STG_LOW_INT_STA_REG, &data);
ad714x->read(ad714x->dev, STG_HIGH_INT_STA_REG, &data);
ad714x->read(ad714x->dev, STG_COM_INT_STA_REG, &data);
mutex_unlock(&ad714x->mutex);
return 0;
}
EXPORT_SYMBOL(ad714x_enable);
#endif
MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor Driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_LICENSE("GPL");
/*
* AD714X CapTouch Programmable Controller driver (bus interfaces)
*
* Copyright 2009 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#ifndef _AD714X_H_
#define _AD714X_H_
#include <linux/types.h>
struct device;
struct ad714x_chip;
typedef int (*ad714x_read_t)(struct device *, unsigned short, unsigned short *);
typedef int (*ad714x_write_t)(struct device *, unsigned short, unsigned short);
int ad714x_disable(struct ad714x_chip *ad714x);
int ad714x_enable(struct ad714x_chip *ad714x);
struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
ad714x_read_t read, ad714x_write_t write);
void ad714x_remove(struct ad714x_chip *ad714x);
#endif
/*
* Driver for a keypad w/16 buttons connected to a PCF8574 I2C I/O expander
*
* Copyright 2005-2008 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#define DRV_NAME "pcf8574_keypad"
static const unsigned char pcf8574_kp_btncode[] = {
[0] = KEY_RESERVED,
[1] = KEY_ENTER,
[2] = KEY_BACKSLASH,
[3] = KEY_0,
[4] = KEY_RIGHTBRACE,
[5] = KEY_C,
[6] = KEY_9,
[7] = KEY_8,
[8] = KEY_7,
[9] = KEY_B,
[10] = KEY_6,
[11] = KEY_5,
[12] = KEY_4,
[13] = KEY_A,
[14] = KEY_3,
[15] = KEY_2,
[16] = KEY_1
};
struct kp_data {
unsigned short btncode[ARRAY_SIZE(pcf8574_kp_btncode)];
struct input_dev *idev;
struct i2c_client *client;
char name[64];
char phys[32];
unsigned char laststate;
};
static short read_state(struct kp_data *lp)
{
unsigned char x, y, a, b;
i2c_smbus_write_byte(lp->client, 240);
x = 0xF & (~(i2c_smbus_read_byte(lp->client) >> 4));
i2c_smbus_write_byte(lp->client, 15);
y = 0xF & (~i2c_smbus_read_byte(lp->client));
for (a = 0; x > 0; a++)
x = x >> 1;
for (b = 0; y > 0; b++)
y = y >> 1;
return ((a - 1) * 4) + b;
}
static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id)
{
struct kp_data *lp = dev_id;
unsigned char nextstate = read_state(lp);
if (lp->laststate != nextstate) {
int key_down = nextstate <= ARRAY_SIZE(lp->btncode);
unsigned short keycode = key_down ?
lp->btncode[nextstate] : lp->btncode[lp->laststate];
input_report_key(lp->idev, keycode, key_down);
input_sync(lp->idev);
lp->laststate = nextstate;
}
return IRQ_HANDLED;
}
static int __devinit pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int i, ret;
struct input_dev *idev;
struct kp_data *lp;
if (i2c_smbus_write_byte(client, 240) < 0) {
dev_err(&client->dev, "probe: write fail\n");
return -ENODEV;
}
lp = kzalloc(sizeof(*lp), GFP_KERNEL);
if (!lp)
return -ENOMEM;
idev = input_allocate_device();
if (!idev) {
dev_err(&client->dev, "Can't allocate input device\n");
ret = -ENOMEM;
goto fail_allocate;
}
lp->idev = idev;
lp->client = client;
idev->evbit[0] = BIT_MASK(EV_KEY);
idev->keycode = lp->btncode;
idev->keycodesize = sizeof(lp->btncode[0]);
idev->keycodemax = ARRAY_SIZE(lp->btncode);
for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) {
lp->btncode[i] = pcf8574_kp_btncode[i];
__set_bit(lp->btncode[i] & KEY_MAX, idev->keybit);
}
sprintf(lp->name, DRV_NAME);
sprintf(lp->phys, "kp_data/input0");
idev->name = lp->name;
idev->phys = lp->phys;
idev->id.bustype = BUS_I2C;
idev->id.vendor = 0x0001;
idev->id.product = 0x0001;
idev->id.version = 0x0100;
input_set_drvdata(idev, lp);
ret = input_register_device(idev);
if (ret) {
dev_err(&client->dev, "input_register_device() failed\n");
goto fail_register;
}
lp->laststate = read_state(lp);
ret = request_threaded_irq(client->irq, NULL, pcf8574_kp_irq_handler,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
DRV_NAME, lp);
if (ret) {
dev_err(&client->dev, "IRQ %d is not free\n", client->irq);
goto fail_irq;
}
i2c_set_clientdata(client, lp);
return 0;
fail_irq:
input_unregister_device(idev);
fail_register:
input_set_drvdata(idev, NULL);
input_free_device(idev);
fail_allocate:
kfree(lp);
return ret;
}
static int __devexit pcf8574_kp_remove(struct i2c_client *client)
{
struct kp_data *lp = i2c_get_clientdata(client);
free_irq(client->irq, lp);
input_unregister_device(lp->idev);
kfree(lp);
i2c_set_clientdata(client, NULL);
return 0;
}
#ifdef CONFIG_PM
static int pcf8574_kp_resume(struct i2c_client *client)
{
enable_irq(client->irq);
return 0;
}
static int pcf8574_kp_suspend(struct i2c_client *client, pm_message_t mesg)
{
disable_irq(client->irq);
return 0;
}
#else
# define pcf8574_kp_resume NULL
# define pcf8574_kp_suspend NULL
#endif
static const struct i2c_device_id pcf8574_kp_id[] = {
{ DRV_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);
static struct i2c_driver pcf8574_kp_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
.probe = pcf8574_kp_probe,
.remove = __devexit_p(pcf8574_kp_remove),
.suspend = pcf8574_kp_suspend,
.resume = pcf8574_kp_resume,
.id_table = pcf8574_kp_id,
};
static int __init pcf8574_kp_init(void)
{
return i2c_add_driver(&pcf8574_kp_driver);
}
module_init(pcf8574_kp_init);
static void __exit pcf8574_kp_exit(void)
{
i2c_del_driver(&pcf8574_kp_driver);
}
module_exit(pcf8574_kp_exit);
MODULE_AUTHOR("Michael Hennerich");
MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574");
MODULE_LICENSE("GPL");
...@@ -983,11 +983,11 @@ static int __init copy_keymap(void) ...@@ -983,11 +983,11 @@ static int __init copy_keymap(void)
for (key = keymap; key->type != KE_END; key++) for (key = keymap; key->type != KE_END; key++)
length++; length++;
new_keymap = kmalloc(length * sizeof(struct key_entry), GFP_KERNEL); new_keymap = kmemdup(keymap, length * sizeof(struct key_entry),
GFP_KERNEL);
if (!new_keymap) if (!new_keymap)
return -ENOMEM; return -ENOMEM;
memcpy(new_keymap, keymap, length * sizeof(struct key_entry));
keymap = new_keymap; keymap = new_keymap;
return 0; return 0;
......
...@@ -17,7 +17,7 @@ config MOUSE_PS2 ...@@ -17,7 +17,7 @@ config MOUSE_PS2
default y default y
select SERIO select SERIO
select SERIO_LIBPS2 select SERIO_LIBPS2
select SERIO_I8042 if X86 select SERIO_I8042 if X86 && !X86_MRST
select SERIO_GSCPS2 if GSC select SERIO_GSCPS2 if GSC
help help
Say Y here if you have a PS/2 mouse connected to your system. This Say Y here if you have a PS/2 mouse connected to your system. This
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
* Trademarks are the property of their respective owners. * Trademarks are the property of their respective owners.
*/ */
#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -19,10 +21,10 @@ ...@@ -19,10 +21,10 @@
#include "psmouse.h" #include "psmouse.h"
#include "elantech.h" #include "elantech.h"
#define elantech_debug(format, arg...) \ #define elantech_debug(fmt, ...) \
do { \ do { \
if (etd->debug) \ if (etd->debug) \
printk(KERN_DEBUG format, ##arg); \ printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \
} while (0) } while (0)
static bool force_elantech; static bool force_elantech;
...@@ -37,7 +39,7 @@ static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, ...@@ -37,7 +39,7 @@ static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c,
{ {
if (psmouse_sliced_command(psmouse, c) || if (psmouse_sliced_command(psmouse, c) ||
ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
pr_err("elantech.c: synaptics_send_cmd query 0x%02x failed.\n", c); pr_err("synaptics_send_cmd query 0x%02x failed.\n", c);
return -1; return -1;
} }
...@@ -60,13 +62,13 @@ static int elantech_ps2_command(struct psmouse *psmouse, ...@@ -60,13 +62,13 @@ static int elantech_ps2_command(struct psmouse *psmouse,
if (rc == 0) if (rc == 0)
break; break;
tries--; tries--;
elantech_debug("elantech.c: retrying ps2 command 0x%02x (%d).\n", elantech_debug("retrying ps2 command 0x%02x (%d).\n",
command, tries); command, tries);
msleep(ETP_PS2_COMMAND_DELAY); msleep(ETP_PS2_COMMAND_DELAY);
} while (tries > 0); } while (tries > 0);
if (rc) if (rc)
pr_err("elantech.c: ps2 command 0x%02x failed.\n", command); pr_err("ps2 command 0x%02x failed.\n", command);
return rc; return rc;
} }
...@@ -108,7 +110,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, ...@@ -108,7 +110,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
} }
if (rc) if (rc)
pr_err("elantech.c: failed to read register 0x%02x.\n", reg); pr_err("failed to read register 0x%02x.\n", reg);
else else
*val = param[0]; *val = param[0];
...@@ -154,7 +156,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, ...@@ -154,7 +156,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
} }
if (rc) if (rc)
pr_err("elantech.c: failed to write register 0x%02x with value 0x%02x.\n", pr_err("failed to write register 0x%02x with value 0x%02x.\n",
reg, val); reg, val);
return rc; return rc;
...@@ -167,7 +169,7 @@ static void elantech_packet_dump(unsigned char *packet, int size) ...@@ -167,7 +169,7 @@ static void elantech_packet_dump(unsigned char *packet, int size)
{ {
int i; int i;
printk(KERN_DEBUG "elantech.c: PS/2 packet ["); printk(KERN_DEBUG pr_fmt("PS/2 packet ["));
for (i = 0; i < size; i++) for (i = 0; i < size; i++)
printk("%s0x%02x ", (i) ? ", " : " ", packet[i]); printk("%s0x%02x ", (i) ? ", " : " ", packet[i]);
printk("]\n"); printk("]\n");
...@@ -203,7 +205,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse) ...@@ -203,7 +205,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
if (etd->jumpy_cursor) { if (etd->jumpy_cursor) {
/* Discard packets that are likely to have bogus coordinates */ /* Discard packets that are likely to have bogus coordinates */
if (fingers > old_fingers) { if (fingers > old_fingers) {
elantech_debug("elantech.c: discarding packet\n"); elantech_debug("discarding packet\n");
goto discard_packet_v1; goto discard_packet_v1;
} }
} }
...@@ -413,23 +415,21 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) ...@@ -413,23 +415,21 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
if (rc == 0) if (rc == 0)
break; break;
tries--; tries--;
elantech_debug("elantech.c: retrying read (%d).\n", elantech_debug("retrying read (%d).\n", tries);
tries);
msleep(ETP_READ_BACK_DELAY); msleep(ETP_READ_BACK_DELAY);
} while (tries > 0); } while (tries > 0);
if (rc) { if (rc) {
pr_err("elantech.c: failed to read back register 0x10.\n"); pr_err("failed to read back register 0x10.\n");
} else if (etd->hw_version == 1 && } else if (etd->hw_version == 1 &&
!(val & ETP_R10_ABSOLUTE_MODE)) { !(val & ETP_R10_ABSOLUTE_MODE)) {
pr_err("elantech.c: touchpad refuses " pr_err("touchpad refuses to switch to absolute mode.\n");
"to switch to absolute mode.\n");
rc = -1; rc = -1;
} }
} }
if (rc) if (rc)
pr_err("elantech.c: failed to initialise registers.\n"); pr_err("failed to initialise registers.\n");
return rc; return rc;
} }
...@@ -575,6 +575,24 @@ static struct attribute_group elantech_attr_group = { ...@@ -575,6 +575,24 @@ static struct attribute_group elantech_attr_group = {
.attrs = elantech_attrs, .attrs = elantech_attrs,
}; };
static bool elantech_is_signature_valid(const unsigned char *param)
{
static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10 };
int i;
if (param[0] == 0)
return false;
if (param[1] == 0)
return true;
for (i = 0; i < ARRAY_SIZE(rates); i++)
if (param[2] == rates[i])
return false;
return true;
}
/* /*
* Use magic knock to detect Elantech touchpad * Use magic knock to detect Elantech touchpad
*/ */
...@@ -590,7 +608,7 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties) ...@@ -590,7 +608,7 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties)
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
pr_debug("elantech.c: sending Elantech magic knock failed.\n"); pr_debug("sending Elantech magic knock failed.\n");
return -1; return -1;
} }
...@@ -599,8 +617,7 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties) ...@@ -599,8 +617,7 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties)
* set of magic numbers * set of magic numbers
*/ */
if (param[0] != 0x3c || param[1] != 0x03 || param[2] != 0xc8) { if (param[0] != 0x3c || param[1] != 0x03 || param[2] != 0xc8) {
pr_debug("elantech.c: " pr_debug("unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n",
"unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n",
param[0], param[1], param[2]); param[0], param[1], param[2]);
return -1; return -1;
} }
...@@ -611,20 +628,20 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties) ...@@ -611,20 +628,20 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties)
* to Elantech magic knock and there might be more. * to Elantech magic knock and there might be more.
*/ */
if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
pr_debug("elantech.c: failed to query firmware version.\n"); pr_debug("failed to query firmware version.\n");
return -1; return -1;
} }
pr_debug("elantech.c: Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n", pr_debug("Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n",
param[0], param[1], param[2]); param[0], param[1], param[2]);
if (param[0] == 0 || param[1] != 0) { if (!elantech_is_signature_valid(param)) {
if (!force_elantech) { if (!force_elantech) {
pr_debug("elantech.c: Probably not a real Elantech touchpad. Aborting.\n"); pr_debug("Probably not a real Elantech touchpad. Aborting.\n");
return -1; return -1;
} }
pr_debug("elantech.c: Probably not a real Elantech touchpad. Enabling anyway due to force_elantech.\n"); pr_debug("Probably not a real Elantech touchpad. Enabling anyway due to force_elantech.\n");
} }
if (set_properties) { if (set_properties) {
...@@ -655,7 +672,7 @@ static int elantech_reconnect(struct psmouse *psmouse) ...@@ -655,7 +672,7 @@ static int elantech_reconnect(struct psmouse *psmouse)
return -1; return -1;
if (elantech_set_absolute_mode(psmouse)) { if (elantech_set_absolute_mode(psmouse)) {
pr_err("elantech.c: failed to put touchpad back into absolute mode.\n"); pr_err("failed to put touchpad back into absolute mode.\n");
return -1; return -1;
} }
...@@ -683,7 +700,7 @@ int elantech_init(struct psmouse *psmouse) ...@@ -683,7 +700,7 @@ int elantech_init(struct psmouse *psmouse)
* Do the version query again so we can store the result * Do the version query again so we can store the result
*/ */
if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
pr_err("elantech.c: failed to query firmware version.\n"); pr_err("failed to query firmware version.\n");
goto init_fail; goto init_fail;
} }
...@@ -704,14 +721,14 @@ int elantech_init(struct psmouse *psmouse) ...@@ -704,14 +721,14 @@ int elantech_init(struct psmouse *psmouse)
etd->paritycheck = 1; etd->paritycheck = 1;
} }
pr_info("elantech.c: assuming hardware version %d, firmware version %d.%d.%d\n", pr_info("assuming hardware version %d, firmware version %d.%d.%d\n",
etd->hw_version, param[0], param[1], param[2]); etd->hw_version, param[0], param[1], param[2]);
if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY, param)) { if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY, param)) {
pr_err("elantech.c: failed to query capabilities.\n"); pr_err("failed to query capabilities.\n");
goto init_fail; goto init_fail;
} }
pr_info("elantech.c: Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n", pr_info("Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n",
param[0], param[1], param[2]); param[0], param[1], param[2]);
etd->capabilities = param[0]; etd->capabilities = param[0];
...@@ -721,13 +738,12 @@ int elantech_init(struct psmouse *psmouse) ...@@ -721,13 +738,12 @@ int elantech_init(struct psmouse *psmouse)
* to jump. Enable a workaround. * to jump. Enable a workaround.
*/ */
if (etd->fw_version == 0x020022) { if (etd->fw_version == 0x020022) {
pr_info("elantech.c: firmware version 2.0.34 detected, " pr_info("firmware version 2.0.34 detected, enabling jumpy cursor workaround\n");
"enabling jumpy cursor workaround\n");
etd->jumpy_cursor = 1; etd->jumpy_cursor = 1;
} }
if (elantech_set_absolute_mode(psmouse)) { if (elantech_set_absolute_mode(psmouse)) {
pr_err("elantech.c: failed to put touchpad into absolute mode.\n"); pr_err("failed to put touchpad into absolute mode.\n");
goto init_fail; goto init_fail;
} }
...@@ -736,8 +752,7 @@ int elantech_init(struct psmouse *psmouse) ...@@ -736,8 +752,7 @@ int elantech_init(struct psmouse *psmouse)
error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj, error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
&elantech_attr_group); &elantech_attr_group);
if (error) { if (error) {
pr_err("elantech.c: failed to create sysfs attributes, error: %d.\n", pr_err("failed to create sysfs attributes, error: %d.\n", error);
error);
goto init_fail; goto init_fail;
} }
......
...@@ -40,8 +40,8 @@ ...@@ -40,8 +40,8 @@
#include "psmouse.h" #include "psmouse.h"
#include "hgpk.h" #include "hgpk.h"
static int tpdebug; static bool tpdebug;
module_param(tpdebug, int, 0644); module_param(tpdebug, bool, 0644);
MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG."); MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG.");
static int recalib_delta = 100; static int recalib_delta = 100;
......
...@@ -56,36 +56,36 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse) ...@@ -56,36 +56,36 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
/* Logitech extended packet */ /* Logitech extended packet */
switch ((packet[1] >> 4) | (packet[0] & 0x30)) { switch ((packet[1] >> 4) | (packet[0] & 0x30)) {
case 0x0d: /* Mouse extra info */ case 0x0d: /* Mouse extra info */
input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL, input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL,
(int) (packet[2] & 8) - (int) (packet[2] & 7)); (int) (packet[2] & 8) - (int) (packet[2] & 7));
input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1); input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1);
input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1); input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1);
break; break;
case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */ case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */
input_report_key(dev, BTN_SIDE, (packet[2]) & 1); input_report_key(dev, BTN_SIDE, (packet[2]) & 1);
input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1); input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1);
input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1); input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1);
input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1); input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1);
input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1); input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1);
break; break;
case 0x0f: /* TouchPad extra info */ case 0x0f: /* TouchPad extra info */
input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL, input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL,
(int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7)); (int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7));
packet[0] = packet[2] | 0x08; packet[0] = packet[2] | 0x08;
break; break;
#ifdef DEBUG #ifdef DEBUG
default: default:
printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n", printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n",
(packet[1] >> 4) | (packet[0] & 0x30)); (packet[1] >> 4) | (packet[0] & 0x30));
#endif #endif
} }
} else { } else {
...@@ -250,7 +250,6 @@ static const struct ps2pp_info *get_model_info(unsigned char model) ...@@ -250,7 +250,6 @@ static const struct ps2pp_info *get_model_info(unsigned char model)
if (model == ps2pp_list[i].model) if (model == ps2pp_list[i].model)
return &ps2pp_list[i]; return &ps2pp_list[i];
printk(KERN_WARNING "logips2pp: Detected unknown logitech mouse model %d\n", model);
return NULL; return NULL;
} }
...@@ -285,31 +284,32 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse, ...@@ -285,31 +284,32 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse,
__set_bit(REL_HWHEEL, input_dev->relbit); __set_bit(REL_HWHEEL, input_dev->relbit);
switch (model_info->kind) { switch (model_info->kind) {
case PS2PP_KIND_WHEEL:
psmouse->name = "Wheel Mouse";
break;
case PS2PP_KIND_MX:
psmouse->name = "MX Mouse";
break;
case PS2PP_KIND_TP3: case PS2PP_KIND_WHEEL:
psmouse->name = "TouchPad 3"; psmouse->name = "Wheel Mouse";
break; break;
case PS2PP_KIND_TRACKMAN: case PS2PP_KIND_MX:
psmouse->name = "TrackMan"; psmouse->name = "MX Mouse";
break; break;
default: case PS2PP_KIND_TP3:
/* psmouse->name = "TouchPad 3";
* Set name to "Mouse" only when using PS2++, break;
* otherwise let other protocols define suitable
* name case PS2PP_KIND_TRACKMAN:
*/ psmouse->name = "TrackMan";
if (using_ps2pp) break;
psmouse->name = "Mouse";
break; default:
/*
* Set name to "Mouse" only when using PS2++,
* otherwise let other protocols define suitable
* name
*/
if (using_ps2pp)
psmouse->name = "Mouse";
break;
} }
} }
...@@ -343,7 +343,8 @@ int ps2pp_init(struct psmouse *psmouse, bool set_properties) ...@@ -343,7 +343,8 @@ int ps2pp_init(struct psmouse *psmouse, bool set_properties)
if (!model || !buttons) if (!model || !buttons)
return -1; return -1;
if ((model_info = get_model_info(model)) != NULL) { model_info = get_model_info(model);
if (model_info) {
/* /*
* Do Logitech PS2++ / PS2T++ magic init. * Do Logitech PS2++ / PS2T++ magic init.
...@@ -379,6 +380,9 @@ int ps2pp_init(struct psmouse *psmouse, bool set_properties) ...@@ -379,6 +380,9 @@ int ps2pp_init(struct psmouse *psmouse, bool set_properties)
use_ps2pp = true; use_ps2pp = true;
} }
} }
} else {
printk(KERN_WARNING "logips2pp: Detected unknown logitech mouse model %d\n", model);
} }
if (set_properties) { if (set_properties) {
......
...@@ -147,18 +147,18 @@ static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) ...@@ -147,18 +147,18 @@ static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
if (psmouse->type == PSMOUSE_IMEX) { if (psmouse->type == PSMOUSE_IMEX) {
switch (packet[3] & 0xC0) { switch (packet[3] & 0xC0) {
case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */ case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */
input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31));
break; break;
case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */ case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */
input_report_rel(dev, REL_HWHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); input_report_rel(dev, REL_HWHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31));
break; break;
case 0x00: case 0x00:
case 0xC0: case 0xC0:
input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7)); input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7));
input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1); input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1);
input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1); input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1);
break; break;
} }
} }
...@@ -247,31 +247,31 @@ static int psmouse_handle_byte(struct psmouse *psmouse) ...@@ -247,31 +247,31 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
psmouse_ret_t rc = psmouse->protocol_handler(psmouse); psmouse_ret_t rc = psmouse->protocol_handler(psmouse);
switch (rc) { switch (rc) {
case PSMOUSE_BAD_DATA: case PSMOUSE_BAD_DATA:
if (psmouse->state == PSMOUSE_ACTIVATED) { if (psmouse->state == PSMOUSE_ACTIVATED) {
printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n", printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n",
psmouse->name, psmouse->phys, psmouse->pktcnt); psmouse->name, psmouse->phys, psmouse->pktcnt);
if (++psmouse->out_of_sync_cnt == psmouse->resetafter) { if (++psmouse->out_of_sync_cnt == psmouse->resetafter) {
__psmouse_set_state(psmouse, PSMOUSE_IGNORE); __psmouse_set_state(psmouse, PSMOUSE_IGNORE);
printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n"); printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n");
serio_reconnect(psmouse->ps2dev.serio); serio_reconnect(psmouse->ps2dev.serio);
return -1; return -1;
}
}
psmouse->pktcnt = 0;
break;
case PSMOUSE_FULL_PACKET:
psmouse->pktcnt = 0;
if (psmouse->out_of_sync_cnt) {
psmouse->out_of_sync_cnt = 0;
printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n",
psmouse->name, psmouse->phys);
} }
break; }
psmouse->pktcnt = 0;
break;
case PSMOUSE_FULL_PACKET:
psmouse->pktcnt = 0;
if (psmouse->out_of_sync_cnt) {
psmouse->out_of_sync_cnt = 0;
printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n",
psmouse->name, psmouse->phys);
}
break;
case PSMOUSE_GOOD_DATA: case PSMOUSE_GOOD_DATA:
break; break;
} }
return 0; return 0;
} }
...@@ -1245,7 +1245,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, ...@@ -1245,7 +1245,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse,
psmouse->pktsize = 3; psmouse->pktsize = 3;
if (proto && (proto->detect || proto->init)) { if (proto && (proto->detect || proto->init)) {
if (proto->detect && proto->detect(psmouse, 1) < 0) if (proto->detect && proto->detect(psmouse, true) < 0)
return -1; return -1;
if (proto->init && proto->init(psmouse) < 0) if (proto->init && proto->init(psmouse) < 0)
......
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
* The x/y limits are taken from the Synaptics TouchPad interfacing Guide, * The x/y limits are taken from the Synaptics TouchPad interfacing Guide,
* section 2.3.2, which says that they should be valid regardless of the * section 2.3.2, which says that they should be valid regardless of the
* actual size of the sensor. * actual size of the sensor.
* Note that newer firmware allows querying device for maximum useable
* coordinates.
*/ */
#define XMIN_NOMINAL 1472 #define XMIN_NOMINAL 1472
#define XMAX_NOMINAL 5472 #define XMAX_NOMINAL 5472
...@@ -194,23 +196,33 @@ static int synaptics_identify(struct psmouse *psmouse) ...@@ -194,23 +196,33 @@ static int synaptics_identify(struct psmouse *psmouse)
} }
/* /*
* Read touchpad resolution * Read touchpad resolution and maximum reported coordinates
* Resolution is left zero if touchpad does not support the query * Resolution is left zero if touchpad does not support the query
*/ */
static int synaptics_resolution(struct psmouse *psmouse) static int synaptics_resolution(struct psmouse *psmouse)
{ {
struct synaptics_data *priv = psmouse->private; struct synaptics_data *priv = psmouse->private;
unsigned char res[3]; unsigned char res[3];
unsigned char max[3];
if (SYN_ID_MAJOR(priv->identity) < 4) if (SYN_ID_MAJOR(priv->identity) < 4)
return 0;
if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, res)) if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, res) == 0) {
return 0; if (res[0] != 0 && (res[1] & 0x80) && res[2] != 0) {
priv->x_res = res[0]; /* x resolution in units/mm */
priv->y_res = res[2]; /* y resolution in units/mm */
}
}
if ((res[0] != 0) && (res[1] & 0x80) && (res[2] != 0)) { if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 &&
priv->x_res = res[0]; /* x resolution in units/mm */ SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) {
priv->y_res = res[2]; /* y resolution in units/mm */ if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_DIMENSIONS, max)) {
printk(KERN_ERR "Synaptics claims to have dimensions query,"
" but I'm not able to read it.\n");
} else {
priv->x_max = (max[0] << 5) | ((max[1] & 0x0f) << 1);
priv->y_max = (max[2] << 5) | ((max[1] & 0xf0) >> 3);
}
} }
return 0; return 0;
...@@ -520,19 +532,20 @@ static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned cha ...@@ -520,19 +532,20 @@ static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned cha
return 0; return 0;
switch (pkt_type) { switch (pkt_type) {
case SYN_NEWABS:
case SYN_NEWABS_RELAXED:
return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx];
case SYN_NEWABS_STRICT: case SYN_NEWABS:
return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx]; case SYN_NEWABS_RELAXED:
return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx];
case SYN_OLDABS: case SYN_NEWABS_STRICT:
return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx]; return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx];
default: case SYN_OLDABS:
printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type); return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx];
return 0;
default:
printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type);
return 0;
} }
} }
...@@ -578,8 +591,10 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) ...@@ -578,8 +591,10 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
int i; int i;
__set_bit(EV_ABS, dev->evbit); __set_bit(EV_ABS, dev->evbit);
input_set_abs_params(dev, ABS_X, XMIN_NOMINAL, XMAX_NOMINAL, 0, 0); input_set_abs_params(dev, ABS_X,
input_set_abs_params(dev, ABS_Y, YMIN_NOMINAL, YMAX_NOMINAL, 0, 0); XMIN_NOMINAL, priv->x_max ?: XMAX_NOMINAL, 0, 0);
input_set_abs_params(dev, ABS_Y,
YMIN_NOMINAL, priv->y_max ?: YMAX_NOMINAL, 0, 0);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
__set_bit(ABS_TOOL_WIDTH, dev->absbit); __set_bit(ABS_TOOL_WIDTH, dev->absbit);
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#define SYN_QUE_RESOLUTION 0x08 #define SYN_QUE_RESOLUTION 0x08
#define SYN_QUE_EXT_CAPAB 0x09 #define SYN_QUE_EXT_CAPAB 0x09
#define SYN_QUE_EXT_CAPAB_0C 0x0c #define SYN_QUE_EXT_CAPAB_0C 0x0c
#define SYN_QUE_EXT_DIMENSIONS 0x0d
/* synatics modes */ /* synatics modes */
#define SYN_BIT_ABSOLUTE_MODE (1 << 7) #define SYN_BIT_ABSOLUTE_MODE (1 << 7)
...@@ -51,6 +52,7 @@ ...@@ -51,6 +52,7 @@
#define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12) #define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12)
#define SYN_CAP_PRODUCT_ID(ec) (((ec) & 0xff0000) >> 16) #define SYN_CAP_PRODUCT_ID(ec) (((ec) & 0xff0000) >> 16)
#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100100) #define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100100)
#define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & 0x020000)
/* synaptics modes query bits */ /* synaptics modes query bits */
#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
...@@ -101,8 +103,8 @@ struct synaptics_data { ...@@ -101,8 +103,8 @@ struct synaptics_data {
unsigned long int ext_cap; /* Extended Capabilities */ unsigned long int ext_cap; /* Extended Capabilities */
unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */ unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */
unsigned long int identity; /* Identification */ unsigned long int identity; /* Identification */
int x_res; /* X resolution in units/mm */ unsigned int x_res, y_res; /* X/Y resolution in units/mm */
int y_res; /* Y resolution in units/mm */ unsigned int x_max, y_max; /* Max dimensions (from FW) */
unsigned char pkt_type; /* packet type - old, new, etc */ unsigned char pkt_type; /* packet type - old, new, etc */
unsigned char mode; /* current mode byte */ unsigned char mode; /* current mode byte */
......
...@@ -660,8 +660,21 @@ static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id * ...@@ -660,8 +660,21 @@ static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id *
} }
static struct pnp_device_id pnp_kbd_devids[] = { static struct pnp_device_id pnp_kbd_devids[] = {
{ .id = "PNP0300", .driver_data = 0 },
{ .id = "PNP0301", .driver_data = 0 },
{ .id = "PNP0302", .driver_data = 0 },
{ .id = "PNP0303", .driver_data = 0 }, { .id = "PNP0303", .driver_data = 0 },
{ .id = "PNP0304", .driver_data = 0 },
{ .id = "PNP0305", .driver_data = 0 },
{ .id = "PNP0306", .driver_data = 0 },
{ .id = "PNP0309", .driver_data = 0 },
{ .id = "PNP030a", .driver_data = 0 },
{ .id = "PNP030b", .driver_data = 0 }, { .id = "PNP030b", .driver_data = 0 },
{ .id = "PNP0320", .driver_data = 0 },
{ .id = "PNP0343", .driver_data = 0 },
{ .id = "PNP0344", .driver_data = 0 },
{ .id = "PNP0345", .driver_data = 0 },
{ .id = "CPQA0D7", .driver_data = 0 },
{ .id = "", }, { .id = "", },
}; };
...@@ -672,6 +685,7 @@ static struct pnp_driver i8042_pnp_kbd_driver = { ...@@ -672,6 +685,7 @@ static struct pnp_driver i8042_pnp_kbd_driver = {
}; };
static struct pnp_device_id pnp_aux_devids[] = { static struct pnp_device_id pnp_aux_devids[] = {
{ .id = "AUI0200", .driver_data = 0 },
{ .id = "FJC6000", .driver_data = 0 }, { .id = "FJC6000", .driver_data = 0 },
{ .id = "FJC6001", .driver_data = 0 }, { .id = "FJC6001", .driver_data = 0 },
{ .id = "PNP0f03", .driver_data = 0 }, { .id = "PNP0f03", .driver_data = 0 },
......
...@@ -66,18 +66,18 @@ static void usb_acecad_irq(struct urb *urb) ...@@ -66,18 +66,18 @@ static void usb_acecad_irq(struct urb *urb)
int prox, status; int prox, status;
switch (urb->status) { switch (urb->status) {
case 0: case 0:
/* success */ /* success */
break; break;
case -ECONNRESET: case -ECONNRESET:
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", __func__, urb->status); dbg("%s - urb shutting down with status: %d", __func__, urb->status);
return; return;
default: default:
dbg("%s - nonzero urb status received: %d", __func__, urb->status); dbg("%s - nonzero urb status received: %d", __func__, urb->status);
goto resubmit; goto resubmit;
} }
prox = (data[0] & 0x04) >> 2; prox = (data[0] & 0x04) >> 2;
...@@ -135,7 +135,7 @@ static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_ ...@@ -135,7 +135,7 @@ static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_
struct usb_acecad *acecad; struct usb_acecad *acecad;
struct input_dev *input_dev; struct input_dev *input_dev;
int pipe, maxp; int pipe, maxp;
int err = -ENOMEM; int err;
if (interface->desc.bNumEndpoints != 1) if (interface->desc.bNumEndpoints != 1)
return -ENODEV; return -ENODEV;
...@@ -193,40 +193,34 @@ static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_ ...@@ -193,40 +193,34 @@ static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_
input_dev->close = usb_acecad_close; input_dev->close = usb_acecad_close;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input_dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
BIT_MASK(ABS_PRESSURE);
input_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
input_dev->keybit[BIT_WORD(BTN_DIGI)] = BIT_MASK(BTN_TOOL_PEN) | input_dev->keybit[BIT_WORD(BTN_DIGI)] = BIT_MASK(BTN_TOOL_PEN) |
BIT_MASK(BTN_TOUCH) | BIT_MASK(BTN_STYLUS) | BIT_MASK(BTN_TOUCH) | BIT_MASK(BTN_STYLUS) |
BIT_MASK(BTN_STYLUS2); BIT_MASK(BTN_STYLUS2);
switch (id->driver_info) { switch (id->driver_info) {
case 0: case 0:
input_dev->absmax[ABS_X] = 5000; input_set_abs_params(input_dev, ABS_X, 0, 5000, 4, 0);
input_dev->absmax[ABS_Y] = 3750; input_set_abs_params(input_dev, ABS_Y, 0, 3750, 4, 0);
input_dev->absmax[ABS_PRESSURE] = 512; input_set_abs_params(input_dev, ABS_PRESSURE, 0, 512, 0, 0);
if (!strlen(acecad->name)) if (!strlen(acecad->name))
snprintf(acecad->name, sizeof(acecad->name), snprintf(acecad->name, sizeof(acecad->name),
"USB Acecad Flair Tablet %04x:%04x", "USB Acecad Flair Tablet %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct)); le16_to_cpu(dev->descriptor.idProduct));
break; break;
case 1:
input_dev->absmax[ABS_X] = 3000; case 1:
input_dev->absmax[ABS_Y] = 2250; input_set_abs_params(input_dev, ABS_X, 0, 53000, 4, 0);
input_dev->absmax[ABS_PRESSURE] = 1024; input_set_abs_params(input_dev, ABS_Y, 0, 2250, 4, 0);
if (!strlen(acecad->name)) input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1024, 0, 0);
snprintf(acecad->name, sizeof(acecad->name), if (!strlen(acecad->name))
"USB Acecad 302 Tablet %04x:%04x", snprintf(acecad->name, sizeof(acecad->name),
le16_to_cpu(dev->descriptor.idVendor), "USB Acecad 302 Tablet %04x:%04x",
le16_to_cpu(dev->descriptor.idProduct)); le16_to_cpu(dev->descriptor.idVendor),
break; le16_to_cpu(dev->descriptor.idProduct));
break;
} }
input_dev->absfuzz[ABS_X] = 4;
input_dev->absfuzz[ABS_Y] = 4;
usb_fill_int_urb(acecad->irq, dev, pipe, usb_fill_int_urb(acecad->irq, dev, pipe,
acecad->data, maxp > 8 ? 8 : maxp, acecad->data, maxp > 8 ? 8 : maxp,
usb_acecad_irq, acecad, endpoint->bInterval); usb_acecad_irq, acecad, endpoint->bInterval);
...@@ -252,13 +246,11 @@ static void usb_acecad_disconnect(struct usb_interface *intf) ...@@ -252,13 +246,11 @@ static void usb_acecad_disconnect(struct usb_interface *intf)
struct usb_acecad *acecad = usb_get_intfdata(intf); struct usb_acecad *acecad = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
if (acecad) {
usb_kill_urb(acecad->irq); input_unregister_device(acecad->input);
input_unregister_device(acecad->input); usb_free_urb(acecad->irq);
usb_free_urb(acecad->irq); usb_buffer_free(acecad->usbdev, 8, acecad->data, acecad->data_dma);
usb_buffer_free(interface_to_usbdev(intf), 10, acecad->data, acecad->data_dma); kfree(acecad);
kfree(acecad);
}
} }
static struct usb_device_id usb_acecad_id_table [] = { static struct usb_device_id usb_acecad_id_table [] = {
......
...@@ -34,10 +34,6 @@ struct kbtab { ...@@ -34,10 +34,6 @@ struct kbtab {
struct input_dev *dev; struct input_dev *dev;
struct usb_device *usbdev; struct usb_device *usbdev;
struct urb *irq; struct urb *irq;
int x, y;
int button;
int pressure;
__u32 serial[2];
char phys[32]; char phys[32];
}; };
...@@ -46,6 +42,7 @@ static void kbtab_irq(struct urb *urb) ...@@ -46,6 +42,7 @@ static void kbtab_irq(struct urb *urb)
struct kbtab *kbtab = urb->context; struct kbtab *kbtab = urb->context;
unsigned char *data = kbtab->data; unsigned char *data = kbtab->data;
struct input_dev *dev = kbtab->dev; struct input_dev *dev = kbtab->dev;
int pressure;
int retval; int retval;
switch (urb->status) { switch (urb->status) {
...@@ -63,31 +60,27 @@ static void kbtab_irq(struct urb *urb) ...@@ -63,31 +60,27 @@ static void kbtab_irq(struct urb *urb)
goto exit; goto exit;
} }
kbtab->x = get_unaligned_le16(&data[1]);
kbtab->y = get_unaligned_le16(&data[3]);
kbtab->pressure = (data[5]);
input_report_key(dev, BTN_TOOL_PEN, 1); input_report_key(dev, BTN_TOOL_PEN, 1);
input_report_abs(dev, ABS_X, kbtab->x); input_report_abs(dev, ABS_X, get_unaligned_le16(&data[1]));
input_report_abs(dev, ABS_Y, kbtab->y); input_report_abs(dev, ABS_Y, get_unaligned_le16(&data[3]));
/*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/ /*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/
input_report_key(dev, BTN_RIGHT, data[0] & 0x02); input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
if (-1 == kb_pressure_click) { pressure = data[5];
input_report_abs(dev, ABS_PRESSURE, kbtab->pressure); if (kb_pressure_click == -1)
} else { input_report_abs(dev, ABS_PRESSURE, pressure);
input_report_key(dev, BTN_LEFT, (kbtab->pressure > kb_pressure_click) ? 1 : 0); else
}; input_report_key(dev, BTN_LEFT, pressure > kb_pressure_click ? 1 : 0);
input_sync(dev); input_sync(dev);
exit: exit:
retval = usb_submit_urb (urb, GFP_ATOMIC); retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval) if (retval)
err ("%s - usb_submit_urb failed with result %d", err("%s - usb_submit_urb failed with result %d",
__func__, retval); __func__, retval);
} }
...@@ -153,13 +146,11 @@ static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *i ...@@ -153,13 +146,11 @@ static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *i
input_dev->open = kbtab_open; input_dev->open = kbtab_open;
input_dev->close = kbtab_close; input_dev->close = kbtab_close;
input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
BIT_MASK(EV_MSC); input_dev->keybit[BIT_WORD(BTN_LEFT)] |=
input_dev->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); input_dev->keybit[BIT_WORD(BTN_DIGI)] |=
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_TOUCH);
BIT_MASK(BTN_TOUCH);
input_dev->mscbit[0] |= BIT_MASK(MSC_SERIAL);
input_set_abs_params(input_dev, ABS_X, 0, 0x2000, 4, 0); input_set_abs_params(input_dev, ABS_X, 0, 0x2000, 4, 0);
input_set_abs_params(input_dev, ABS_Y, 0, 0x1750, 4, 0); input_set_abs_params(input_dev, ABS_Y, 0, 0x1750, 4, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xff, 0, 0); input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xff, 0, 0);
...@@ -182,7 +173,7 @@ static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *i ...@@ -182,7 +173,7 @@ static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *i
return 0; return 0;
fail3: usb_free_urb(kbtab->irq); fail3: usb_free_urb(kbtab->irq);
fail2: usb_buffer_free(dev, 10, kbtab->data, kbtab->data_dma); fail2: usb_buffer_free(dev, 8, kbtab->data, kbtab->data_dma);
fail1: input_free_device(input_dev); fail1: input_free_device(input_dev);
kfree(kbtab); kfree(kbtab);
return error; return error;
...@@ -193,13 +184,11 @@ static void kbtab_disconnect(struct usb_interface *intf) ...@@ -193,13 +184,11 @@ static void kbtab_disconnect(struct usb_interface *intf)
struct kbtab *kbtab = usb_get_intfdata(intf); struct kbtab *kbtab = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
if (kbtab) {
usb_kill_urb(kbtab->irq); input_unregister_device(kbtab->dev);
input_unregister_device(kbtab->dev); usb_free_urb(kbtab->irq);
usb_free_urb(kbtab->irq); usb_buffer_free(kbtab->usbdev, 8, kbtab->data, kbtab->data_dma);
usb_buffer_free(interface_to_usbdev(intf), 10, kbtab->data, kbtab->data_dma); kfree(kbtab);
kfree(kbtab);
}
} }
static struct usb_driver kbtab_driver = { static struct usb_driver kbtab_driver = {
......
...@@ -106,44 +106,18 @@ MODULE_LICENSE(DRIVER_LICENSE); ...@@ -106,44 +106,18 @@ MODULE_LICENSE(DRIVER_LICENSE);
struct wacom { struct wacom {
dma_addr_t data_dma; dma_addr_t data_dma;
struct input_dev *dev;
struct usb_device *usbdev; struct usb_device *usbdev;
struct usb_interface *intf; struct usb_interface *intf;
struct urb *irq; struct urb *irq;
struct wacom_wac *wacom_wac; struct wacom_wac wacom_wac;
struct mutex lock; struct mutex lock;
unsigned int open:1; bool open;
char phys[32]; char phys[32];
}; };
struct wacom_combo {
struct wacom *wacom;
struct urb *urb;
};
extern const struct usb_device_id wacom_ids[]; extern const struct usb_device_id wacom_ids[];
extern int wacom_wac_irq(struct wacom_wac * wacom_wac, void * wcombo); void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
extern void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data); void wacom_setup_input_capabilities(struct input_dev *input_dev,
extern void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data); struct wacom_wac *wacom_wac);
extern void wacom_report_key(void *wcombo, unsigned int key_type, int key_data);
extern void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value);
extern void wacom_input_sync(void *wcombo);
extern void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_i4s(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_i4(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_tpc(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_tpc2fg(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_mo(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_bee(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern __u16 wacom_le16_to_cpu(unsigned char *data);
extern __u16 wacom_be16_to_cpu(unsigned char *data);
#endif #endif
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
* (at your option) any later version. * (at your option) any later version.
*/ */
#include "wacom.h"
#include "wacom_wac.h" #include "wacom_wac.h"
#include "wacom.h"
/* defines to get HID report descriptor */ /* defines to get HID report descriptor */
#define HID_DEVICET_HID (USB_TYPE_CLASS | 0x01) #define HID_DEVICET_HID (USB_TYPE_CLASS | 0x01)
...@@ -70,15 +70,9 @@ static int usb_set_report(struct usb_interface *intf, unsigned char type, ...@@ -70,15 +70,9 @@ static int usb_set_report(struct usb_interface *intf, unsigned char type,
buf, size, 1000); buf, size, 1000);
} }
static struct input_dev * get_input_dev(struct wacom_combo *wcombo)
{
return wcombo->wacom->dev;
}
static void wacom_sys_irq(struct urb *urb) static void wacom_sys_irq(struct urb *urb)
{ {
struct wacom *wacom = urb->context; struct wacom *wacom = urb->context;
struct wacom_combo wcombo;
int retval; int retval;
switch (urb->status) { switch (urb->status) {
...@@ -96,59 +90,16 @@ static void wacom_sys_irq(struct urb *urb) ...@@ -96,59 +90,16 @@ static void wacom_sys_irq(struct urb *urb)
goto exit; goto exit;
} }
wcombo.wacom = wacom; wacom_wac_irq(&wacom->wacom_wac, urb->actual_length);
wcombo.urb = urb;
if (wacom_wac_irq(wacom->wacom_wac, (void *)&wcombo))
input_sync(get_input_dev(&wcombo));
exit: exit:
usb_mark_last_busy(wacom->usbdev); usb_mark_last_busy(wacom->usbdev);
retval = usb_submit_urb (urb, GFP_ATOMIC); retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval) if (retval)
err ("%s - usb_submit_urb failed with result %d", err ("%s - usb_submit_urb failed with result %d",
__func__, retval); __func__, retval);
} }
void wacom_report_key(void *wcombo, unsigned int key_type, int key_data)
{
input_report_key(get_input_dev((struct wacom_combo *)wcombo), key_type, key_data);
}
void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data)
{
input_report_abs(get_input_dev((struct wacom_combo *)wcombo), abs_type, abs_data);
}
void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data)
{
input_report_rel(get_input_dev((struct wacom_combo *)wcombo), rel_type, rel_data);
}
void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value)
{
input_event(get_input_dev((struct wacom_combo *)wcombo), type, code, value);
}
__u16 wacom_be16_to_cpu(unsigned char *data)
{
__u16 value;
value = be16_to_cpu(*(__be16 *) data);
return value;
}
__u16 wacom_le16_to_cpu(unsigned char *data)
{
__u16 value;
value = le16_to_cpu(*(__le16 *) data);
return value;
}
void wacom_input_sync(void *wcombo)
{
input_sync(get_input_dev((struct wacom_combo *)wcombo));
}
static int wacom_open(struct input_dev *dev) static int wacom_open(struct input_dev *dev)
{ {
struct wacom *wacom = input_get_drvdata(dev); struct wacom *wacom = input_get_drvdata(dev);
...@@ -168,7 +119,7 @@ static int wacom_open(struct input_dev *dev) ...@@ -168,7 +119,7 @@ static int wacom_open(struct input_dev *dev)
return -EIO; return -EIO;
} }
wacom->open = 1; wacom->open = true;
wacom->intf->needs_remote_wakeup = 1; wacom->intf->needs_remote_wakeup = 1;
mutex_unlock(&wacom->lock); mutex_unlock(&wacom->lock);
...@@ -181,128 +132,11 @@ static void wacom_close(struct input_dev *dev) ...@@ -181,128 +132,11 @@ static void wacom_close(struct input_dev *dev)
mutex_lock(&wacom->lock); mutex_lock(&wacom->lock);
usb_kill_urb(wacom->irq); usb_kill_urb(wacom->irq);
wacom->open = 0; wacom->open = false;
wacom->intf->needs_remote_wakeup = 0; wacom->intf->needs_remote_wakeup = 0;
mutex_unlock(&wacom->lock); mutex_unlock(&wacom->lock);
} }
void input_dev_mo(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_1) |
BIT_MASK(BTN_5);
input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
}
void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->evbit[0] |= BIT_MASK(EV_MSC);
input_dev->mscbit[0] |= BIT_MASK(MSC_SERIAL);
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_FINGER);
input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_0) |
BIT_MASK(BTN_4);
}
void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->evbit[0] |= BIT_MASK(EV_REL);
input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);
input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_RUBBER) |
BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_STYLUS) |
BIT_MASK(BTN_TOOL_MOUSE) | BIT_MASK(BTN_STYLUS2);
input_set_abs_params(input_dev, ABS_DISTANCE,
0, wacom_wac->features.distance_max, 0, 0);
}
void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_FINGER);
input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_0) |
BIT_MASK(BTN_1) | BIT_MASK(BTN_2) | BIT_MASK(BTN_3);
input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
}
void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_4) |
BIT_MASK(BTN_5) | BIT_MASK(BTN_6) | BIT_MASK(BTN_7);
input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
}
void input_dev_i4s(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_FINGER);
input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_0) | BIT_MASK(BTN_1) | BIT_MASK(BTN_2) | BIT_MASK(BTN_3);
input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_4) | BIT_MASK(BTN_5) | BIT_MASK(BTN_6);
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
}
void input_dev_i4(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_7) | BIT_MASK(BTN_8);
}
void input_dev_bee(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_8) | BIT_MASK(BTN_9);
}
void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->evbit[0] |= BIT_MASK(EV_MSC) | BIT_MASK(EV_REL);
input_dev->mscbit[0] |= BIT_MASK(MSC_SERIAL);
input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);
input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE) |
BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_RUBBER) |
BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_STYLUS) |
BIT_MASK(BTN_TOOL_MOUSE) | BIT_MASK(BTN_TOOL_BRUSH) |
BIT_MASK(BTN_TOOL_PENCIL) | BIT_MASK(BTN_TOOL_AIRBRUSH) |
BIT_MASK(BTN_TOOL_LENS) | BIT_MASK(BTN_STYLUS2);
input_set_abs_params(input_dev, ABS_DISTANCE,
0, wacom_wac->features.distance_max, 0, 0);
input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
}
void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_PEN) |
BIT_MASK(BTN_STYLUS) | BIT_MASK(BTN_STYLUS2);
}
void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_RUBBER);
}
void input_dev_tpc(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
struct wacom_features *features = &wacom_wac->features;
if (features->device_type == BTN_TOOL_DOUBLETAP ||
features->device_type == BTN_TOOL_TRIPLETAP) {
input_set_abs_params(input_dev, ABS_RX, 0, features->x_phy, 0, 0);
input_set_abs_params(input_dev, ABS_RY, 0, features->y_phy, 0, 0);
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
}
}
void input_dev_tpc2fg(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
if (wacom_wac->features.device_type == BTN_TOOL_TRIPLETAP) {
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_TRIPLETAP);
input_dev->evbit[0] |= BIT_MASK(EV_MSC);
input_dev->mscbit[0] |= BIT_MASK(MSC_SERIAL);
}
}
static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc, static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc,
struct wacom_features *features) struct wacom_features *features)
{ {
...@@ -362,9 +196,9 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi ...@@ -362,9 +196,9 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
features->device_type = BTN_TOOL_TRIPLETAP; features->device_type = BTN_TOOL_TRIPLETAP;
} }
features->x_max = features->x_max =
wacom_le16_to_cpu(&report[i + 3]); get_unaligned_le16(&report[i + 3]);
features->x_phy = features->x_phy =
wacom_le16_to_cpu(&report[i + 6]); get_unaligned_le16(&report[i + 6]);
features->unit = report[i + 9]; features->unit = report[i + 9];
features->unitExpo = report[i + 11]; features->unitExpo = report[i + 11];
i += 12; i += 12;
...@@ -374,7 +208,7 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi ...@@ -374,7 +208,7 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
features->pktlen = WACOM_PKGLEN_GRAPHIRE; features->pktlen = WACOM_PKGLEN_GRAPHIRE;
features->device_type = BTN_TOOL_PEN; features->device_type = BTN_TOOL_PEN;
features->x_max = features->x_max =
wacom_le16_to_cpu(&report[i + 3]); get_unaligned_le16(&report[i + 3]);
i += 4; i += 4;
} }
} else if (usage == WCM_DIGITIZER) { } else if (usage == WCM_DIGITIZER) {
...@@ -396,15 +230,15 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi ...@@ -396,15 +230,15 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
features->pktlen = WACOM_PKGLEN_TPC2FG; features->pktlen = WACOM_PKGLEN_TPC2FG;
features->device_type = BTN_TOOL_TRIPLETAP; features->device_type = BTN_TOOL_TRIPLETAP;
features->y_max = features->y_max =
wacom_le16_to_cpu(&report[i + 3]); get_unaligned_le16(&report[i + 3]);
features->y_phy = features->y_phy =
wacom_le16_to_cpu(&report[i + 6]); get_unaligned_le16(&report[i + 6]);
i += 7; i += 7;
} else { } else {
features->y_max = features->y_max =
features->x_max; features->x_max;
features->y_phy = features->y_phy =
wacom_le16_to_cpu(&report[i + 3]); get_unaligned_le16(&report[i + 3]);
i += 4; i += 4;
} }
} else if (pen) { } else if (pen) {
...@@ -413,7 +247,7 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi ...@@ -413,7 +247,7 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
features->pktlen = WACOM_PKGLEN_GRAPHIRE; features->pktlen = WACOM_PKGLEN_GRAPHIRE;
features->device_type = BTN_TOOL_PEN; features->device_type = BTN_TOOL_PEN;
features->y_max = features->y_max =
wacom_le16_to_cpu(&report[i + 3]); get_unaligned_le16(&report[i + 3]);
i += 4; i += 4;
} }
} }
...@@ -432,7 +266,7 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi ...@@ -432,7 +266,7 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
case HID_USAGE_UNDEFINED: case HID_USAGE_UNDEFINED:
if (usage == WCM_DESKTOP && finger) /* capacity */ if (usage == WCM_DESKTOP && finger) /* capacity */
features->pressure_max = features->pressure_max =
wacom_le16_to_cpu(&report[i + 3]); get_unaligned_le16(&report[i + 3]);
i += 4; i += 4;
break; break;
} }
...@@ -528,6 +362,81 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, ...@@ -528,6 +362,81 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
return error; return error;
} }
struct wacom_usbdev_data {
struct list_head list;
struct kref kref;
struct usb_device *dev;
struct wacom_shared shared;
};
static LIST_HEAD(wacom_udev_list);
static DEFINE_MUTEX(wacom_udev_list_lock);
static struct wacom_usbdev_data *wacom_get_usbdev_data(struct usb_device *dev)
{
struct wacom_usbdev_data *data;
list_for_each_entry(data, &wacom_udev_list, list) {
if (data->dev == dev) {
kref_get(&data->kref);
return data;
}
}
return NULL;
}
static int wacom_add_shared_data(struct wacom_wac *wacom,
struct usb_device *dev)
{
struct wacom_usbdev_data *data;
int retval = 0;
mutex_lock(&wacom_udev_list_lock);
data = wacom_get_usbdev_data(dev);
if (!data) {
data = kzalloc(sizeof(struct wacom_usbdev_data), GFP_KERNEL);
if (!data) {
retval = -ENOMEM;
goto out;
}
kref_init(&data->kref);
data->dev = dev;
list_add_tail(&data->list, &wacom_udev_list);
}
wacom->shared = &data->shared;
out:
mutex_unlock(&wacom_udev_list_lock);
return retval;
}
static void wacom_release_shared_data(struct kref *kref)
{
struct wacom_usbdev_data *data =
container_of(kref, struct wacom_usbdev_data, kref);
mutex_lock(&wacom_udev_list_lock);
list_del(&data->list);
mutex_unlock(&wacom_udev_list_lock);
kfree(data);
}
static void wacom_remove_shared_data(struct wacom_wac *wacom)
{
struct wacom_usbdev_data *data;
if (wacom->shared) {
data = container_of(wacom->shared, struct wacom_usbdev_data, shared);
kref_put(&data->kref, wacom_release_shared_data);
wacom->shared = NULL;
}
}
static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
{ {
struct usb_device *dev = interface_to_usbdev(intf); struct usb_device *dev = interface_to_usbdev(intf);
...@@ -542,13 +451,13 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i ...@@ -542,13 +451,13 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
return -EINVAL; return -EINVAL;
wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
wacom_wac = kzalloc(sizeof(struct wacom_wac), GFP_KERNEL);
input_dev = input_allocate_device(); input_dev = input_allocate_device();
if (!wacom || !input_dev || !wacom_wac) { if (!wacom || !input_dev) {
error = -ENOMEM; error = -ENOMEM;
goto fail1; goto fail1;
} }
wacom_wac = &wacom->wacom_wac;
wacom_wac->features = *((struct wacom_features *)id->driver_info); wacom_wac->features = *((struct wacom_features *)id->driver_info);
features = &wacom_wac->features; features = &wacom_wac->features;
if (features->pktlen > WACOM_PKGLEN_MAX) { if (features->pktlen > WACOM_PKGLEN_MAX) {
...@@ -570,20 +479,12 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i ...@@ -570,20 +479,12 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
} }
wacom->usbdev = dev; wacom->usbdev = dev;
wacom->dev = input_dev;
wacom->intf = intf; wacom->intf = intf;
mutex_init(&wacom->lock); mutex_init(&wacom->lock);
usb_make_path(dev, wacom->phys, sizeof(wacom->phys)); usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
strlcat(wacom->phys, "/input0", sizeof(wacom->phys)); strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
usb_to_input_id(dev, &input_dev->id); wacom_wac->input = input_dev;
input_dev->dev.parent = &intf->dev;
input_set_drvdata(input_dev, wacom);
input_dev->open = wacom_open;
input_dev->close = wacom_close;
endpoint = &intf->cur_altsetting->endpoint[0].desc; endpoint = &intf->cur_altsetting->endpoint[0].desc;
...@@ -600,20 +501,21 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i ...@@ -600,20 +501,21 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
features->device_type == BTN_TOOL_PEN ? features->device_type == BTN_TOOL_PEN ?
" Pen" : " Finger", " Pen" : " Finger",
sizeof(wacom_wac->name)); sizeof(wacom_wac->name));
error = wacom_add_shared_data(wacom_wac, dev);
if (error)
goto fail3;
} }
input_dev->name = wacom_wac->name; input_dev->name = wacom_wac->name;
wacom->wacom_wac = wacom_wac; input_dev->name = wacom_wac->name;
input_dev->dev.parent = &intf->dev;
input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->open = wacom_open;
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOUCH); input_dev->close = wacom_close;
usb_to_input_id(dev, &input_dev->id);
input_set_abs_params(input_dev, ABS_X, 0, features->x_max, 4, 0); input_set_drvdata(input_dev, wacom);
input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, 4, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, 0, 0);
input_dev->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC);
wacom_init_input_dev(input_dev, wacom_wac); wacom_setup_input_capabilities(input_dev, wacom_wac);
usb_fill_int_urb(wacom->irq, dev, usb_fill_int_urb(wacom->irq, dev,
usb_rcvintpipe(dev, endpoint->bEndpointAddress), usb_rcvintpipe(dev, endpoint->bEndpointAddress),
...@@ -622,9 +524,9 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i ...@@ -622,9 +524,9 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
wacom->irq->transfer_dma = wacom->data_dma; wacom->irq->transfer_dma = wacom->data_dma;
wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
error = input_register_device(wacom->dev); error = input_register_device(input_dev);
if (error) if (error)
goto fail3; goto fail4;
/* Note that if query fails it is not a hard failure */ /* Note that if query fails it is not a hard failure */
wacom_query_tablet_data(intf, features); wacom_query_tablet_data(intf, features);
...@@ -632,11 +534,11 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i ...@@ -632,11 +534,11 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
usb_set_intfdata(intf, wacom); usb_set_intfdata(intf, wacom);
return 0; return 0;
fail4: wacom_remove_shared_data(wacom_wac);
fail3: usb_free_urb(wacom->irq); fail3: usb_free_urb(wacom->irq);
fail2: usb_buffer_free(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma); fail2: usb_buffer_free(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma);
fail1: input_free_device(input_dev); fail1: input_free_device(input_dev);
kfree(wacom); kfree(wacom);
kfree(wacom_wac);
return error; return error;
} }
...@@ -647,11 +549,11 @@ static void wacom_disconnect(struct usb_interface *intf) ...@@ -647,11 +549,11 @@ static void wacom_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
usb_kill_urb(wacom->irq); usb_kill_urb(wacom->irq);
input_unregister_device(wacom->dev); input_unregister_device(wacom->wacom_wac.input);
usb_free_urb(wacom->irq); usb_free_urb(wacom->irq);
usb_buffer_free(interface_to_usbdev(intf), WACOM_PKGLEN_MAX, usb_buffer_free(interface_to_usbdev(intf), WACOM_PKGLEN_MAX,
wacom->wacom_wac->data, wacom->data_dma); wacom->wacom_wac.data, wacom->data_dma);
kfree(wacom->wacom_wac); wacom_remove_shared_data(&wacom->wacom_wac);
kfree(wacom); kfree(wacom);
} }
...@@ -669,7 +571,7 @@ static int wacom_suspend(struct usb_interface *intf, pm_message_t message) ...@@ -669,7 +571,7 @@ static int wacom_suspend(struct usb_interface *intf, pm_message_t message)
static int wacom_resume(struct usb_interface *intf) static int wacom_resume(struct usb_interface *intf)
{ {
struct wacom *wacom = usb_get_intfdata(intf); struct wacom *wacom = usb_get_intfdata(intf);
struct wacom_features *features = &wacom->wacom_wac->features; struct wacom_features *features = &wacom->wacom_wac.features;
int rv; int rv;
mutex_lock(&wacom->lock); mutex_lock(&wacom->lock);
......
...@@ -11,52 +11,58 @@ ...@@ -11,52 +11,58 @@
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. * (at your option) any later version.
*/ */
#include "wacom.h"
#include "wacom_wac.h" #include "wacom_wac.h"
#include "wacom.h"
static int wacom_penpartner_irq(struct wacom_wac *wacom, void *wcombo) static int wacom_penpartner_irq(struct wacom_wac *wacom)
{ {
unsigned char *data = wacom->data; unsigned char *data = wacom->data;
struct input_dev *input = wacom->input;
switch (data[0]) { switch (data[0]) {
case 1: case 1:
if (data[5] & 0x80) { if (data[5] & 0x80) {
wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID; wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID;
wacom_report_key(wcombo, wacom->tool[0], 1); input_report_key(input, wacom->tool[0], 1);
wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */ input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1])); input_report_abs(input, ABS_X, get_unaligned_le16(&data[1]));
wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3])); input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3]));
wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127); input_report_abs(input, ABS_PRESSURE, (signed char)data[6] + 127);
wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -127)); input_report_key(input, BTN_TOUCH, ((signed char)data[6] > -127));
wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40)); input_report_key(input, BTN_STYLUS, (data[5] & 0x40));
} else { } else {
wacom_report_key(wcombo, wacom->tool[0], 0); input_report_key(input, wacom->tool[0], 0);
wacom_report_abs(wcombo, ABS_MISC, 0); /* report tool id */ input_report_abs(input, ABS_MISC, 0); /* report tool id */
wacom_report_abs(wcombo, ABS_PRESSURE, -1); input_report_abs(input, ABS_PRESSURE, -1);
wacom_report_key(wcombo, BTN_TOUCH, 0); input_report_key(input, BTN_TOUCH, 0);
} }
break; break;
case 2:
wacom_report_key(wcombo, BTN_TOOL_PEN, 1); case 2:
wacom_report_abs(wcombo, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */ input_report_key(input, BTN_TOOL_PEN, 1);
wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1])); input_report_abs(input, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */
wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3])); input_report_abs(input, ABS_X, get_unaligned_le16(&data[1]));
wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127); input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3]));
wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20)); input_report_abs(input, ABS_PRESSURE, (signed char)data[6] + 127);
wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40)); input_report_key(input, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20));
break; input_report_key(input, BTN_STYLUS, (data[5] & 0x40));
default: break;
printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]);
return 0; default:
printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]);
return 0;
} }
return 1; return 1;
} }
static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo) static int wacom_pl_irq(struct wacom_wac *wacom)
{ {
struct wacom_features *features = &wacom->features; struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data; unsigned char *data = wacom->data;
struct input_dev *input = wacom->input;
int prox, pressure; int prox, pressure;
if (data[0] != WACOM_REPORT_PENABLED) { if (data[0] != WACOM_REPORT_PENABLED) {
...@@ -90,8 +96,8 @@ static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo) ...@@ -90,8 +96,8 @@ static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo)
/* was entered with stylus2 pressed */ /* was entered with stylus2 pressed */
if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) { if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) {
/* report out proximity for previous tool */ /* report out proximity for previous tool */
wacom_report_key(wcombo, wacom->tool[1], 0); input_report_key(input, wacom->tool[1], 0);
wacom_input_sync(wcombo); input_sync(input);
wacom->tool[1] = BTN_TOOL_PEN; wacom->tool[1] = BTN_TOOL_PEN;
return 0; return 0;
} }
...@@ -101,32 +107,33 @@ static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo) ...@@ -101,32 +107,33 @@ static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo)
wacom->tool[1] = BTN_TOOL_PEN; wacom->tool[1] = BTN_TOOL_PEN;
wacom->id[0] = STYLUS_DEVICE_ID; wacom->id[0] = STYLUS_DEVICE_ID;
} }
wacom_report_key(wcombo, wacom->tool[1], prox); /* report in proximity for tool */ input_report_key(input, wacom->tool[1], prox); /* report in proximity for tool */
wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */ input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
wacom_report_abs(wcombo, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14)); input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
wacom_report_abs(wcombo, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14)); input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
wacom_report_abs(wcombo, ABS_PRESSURE, pressure); input_report_abs(input, ABS_PRESSURE, pressure);
wacom_report_key(wcombo, BTN_TOUCH, data[4] & 0x08); input_report_key(input, BTN_TOUCH, data[4] & 0x08);
wacom_report_key(wcombo, BTN_STYLUS, data[4] & 0x10); input_report_key(input, BTN_STYLUS, data[4] & 0x10);
/* Only allow the stylus2 button to be reported for the pen tool. */ /* Only allow the stylus2 button to be reported for the pen tool. */
wacom_report_key(wcombo, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20)); input_report_key(input, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
} else { } else {
/* report proximity-out of a (valid) tool */ /* report proximity-out of a (valid) tool */
if (wacom->tool[1] != BTN_TOOL_RUBBER) { if (wacom->tool[1] != BTN_TOOL_RUBBER) {
/* Unknown tool selected default to pen tool */ /* Unknown tool selected default to pen tool */
wacom->tool[1] = BTN_TOOL_PEN; wacom->tool[1] = BTN_TOOL_PEN;
} }
wacom_report_key(wcombo, wacom->tool[1], prox); input_report_key(input, wacom->tool[1], prox);
} }
wacom->tool[0] = prox; /* Save proximity state */ wacom->tool[0] = prox; /* Save proximity state */
return 1; return 1;
} }
static int wacom_ptu_irq(struct wacom_wac *wacom, void *wcombo) static int wacom_ptu_irq(struct wacom_wac *wacom)
{ {
unsigned char *data = wacom->data; unsigned char *data = wacom->data;
struct input_dev *input = wacom->input;
if (data[0] != WACOM_REPORT_PENABLED) { if (data[0] != WACOM_REPORT_PENABLED) {
printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]); printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]);
...@@ -134,40 +141,41 @@ static int wacom_ptu_irq(struct wacom_wac *wacom, void *wcombo) ...@@ -134,40 +141,41 @@ static int wacom_ptu_irq(struct wacom_wac *wacom, void *wcombo)
} }
if (data[1] & 0x04) { if (data[1] & 0x04) {
wacom_report_key(wcombo, BTN_TOOL_RUBBER, data[1] & 0x20); input_report_key(input, BTN_TOOL_RUBBER, data[1] & 0x20);
wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x08); input_report_key(input, BTN_TOUCH, data[1] & 0x08);
wacom->id[0] = ERASER_DEVICE_ID; wacom->id[0] = ERASER_DEVICE_ID;
} else { } else {
wacom_report_key(wcombo, BTN_TOOL_PEN, data[1] & 0x20); input_report_key(input, BTN_TOOL_PEN, data[1] & 0x20);
wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01); input_report_key(input, BTN_TOUCH, data[1] & 0x01);
wacom->id[0] = STYLUS_DEVICE_ID; wacom->id[0] = STYLUS_DEVICE_ID;
} }
wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */ input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2])); input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4])); input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
wacom_report_abs(wcombo, ABS_PRESSURE, wacom_le16_to_cpu(&data[6])); input_report_abs(input, ABS_PRESSURE, le16_to_cpup((__le16 *)&data[6]));
wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02); input_report_key(input, BTN_STYLUS, data[1] & 0x02);
wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x10); input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
return 1; return 1;
} }
static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) static int wacom_graphire_irq(struct wacom_wac *wacom)
{ {
struct wacom_features *features = &wacom->features; struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data; unsigned char *data = wacom->data;
int x, y, rw; struct input_dev *input = wacom->input;
static int penData = 0; int prox;
int rw = 0;
int retval = 0;
if (data[0] != WACOM_REPORT_PENABLED) { if (data[0] != WACOM_REPORT_PENABLED) {
dbg("wacom_graphire_irq: received unknown report #%d", data[0]); dbg("wacom_graphire_irq: received unknown report #%d", data[0]);
return 0; goto exit;
} }
if (data[1] & 0x80) { prox = data[1] & 0x80;
/* in prox and not a pad data */ if (prox || wacom->id[0]) {
penData = 1; if (prox) {
switch ((data[1] >> 5) & 3) {
switch ((data[1] >> 5) & 3) {
case 0: /* Pen */ case 0: /* Pen */
wacom->tool[0] = BTN_TOOL_PEN; wacom->tool[0] = BTN_TOOL_PEN;
...@@ -180,128 +188,89 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) ...@@ -180,128 +188,89 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
break; break;
case 2: /* Mouse with wheel */ case 2: /* Mouse with wheel */
wacom_report_key(wcombo, BTN_MIDDLE, data[1] & 0x04); input_report_key(input, BTN_MIDDLE, data[1] & 0x04);
if (features->type == WACOM_G4 || features->type == WACOM_MO) {
rw = data[7] & 0x04 ? (data[7] & 0x03)-4 : (data[7] & 0x03);
wacom_report_rel(wcombo, REL_WHEEL, -rw);
} else
wacom_report_rel(wcombo, REL_WHEEL, -(signed char) data[6]);
/* fall through */ /* fall through */
case 3: /* Mouse without wheel */ case 3: /* Mouse without wheel */
wacom->tool[0] = BTN_TOOL_MOUSE; wacom->tool[0] = BTN_TOOL_MOUSE;
wacom->id[0] = CURSOR_DEVICE_ID; wacom->id[0] = CURSOR_DEVICE_ID;
wacom_report_key(wcombo, BTN_LEFT, data[1] & 0x01);
wacom_report_key(wcombo, BTN_RIGHT, data[1] & 0x02);
if (features->type == WACOM_G4 || features->type == WACOM_MO)
wacom_report_abs(wcombo, ABS_DISTANCE, data[6] & 0x3f);
else
wacom_report_abs(wcombo, ABS_DISTANCE, data[7] & 0x3f);
break; break;
}
} }
x = wacom_le16_to_cpu(&data[2]); input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
y = wacom_le16_to_cpu(&data[4]); input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
wacom_report_abs(wcombo, ABS_X, x);
wacom_report_abs(wcombo, ABS_Y, y);
if (wacom->tool[0] != BTN_TOOL_MOUSE) { if (wacom->tool[0] != BTN_TOOL_MOUSE) {
wacom_report_abs(wcombo, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8)); input_report_abs(input, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8));
wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01); input_report_key(input, BTN_TOUCH, data[1] & 0x01);
wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02); input_report_key(input, BTN_STYLUS, data[1] & 0x02);
wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x04); input_report_key(input, BTN_STYLUS2, data[1] & 0x04);
}
wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */
wacom_report_key(wcombo, wacom->tool[0], 1);
} else if (wacom->id[0]) {
wacom_report_abs(wcombo, ABS_X, 0);
wacom_report_abs(wcombo, ABS_Y, 0);
if (wacom->tool[0] == BTN_TOOL_MOUSE) {
wacom_report_key(wcombo, BTN_LEFT, 0);
wacom_report_key(wcombo, BTN_RIGHT, 0);
wacom_report_abs(wcombo, ABS_DISTANCE, 0);
} else { } else {
wacom_report_abs(wcombo, ABS_PRESSURE, 0); input_report_key(input, BTN_LEFT, data[1] & 0x01);
wacom_report_key(wcombo, BTN_TOUCH, 0); input_report_key(input, BTN_RIGHT, data[1] & 0x02);
wacom_report_key(wcombo, BTN_STYLUS, 0); if (features->type == WACOM_G4 ||
wacom_report_key(wcombo, BTN_STYLUS2, 0); features->type == WACOM_MO) {
input_report_abs(input, ABS_DISTANCE, data[6] & 0x3f);
rw = (signed)(data[7] & 0x04) - (data[7] & 0x03);
} else {
input_report_abs(input, ABS_DISTANCE, data[7] & 0x3f);
rw = -(signed)data[6];
}
input_report_rel(input, REL_WHEEL, rw);
} }
wacom->id[0] = 0;
wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */ if (!prox)
wacom_report_key(wcombo, wacom->tool[0], 0); wacom->id[0] = 0;
input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
input_report_key(input, wacom->tool[0], prox);
input_sync(input); /* sync last event */
} }
/* send pad data */ /* send pad data */
switch (features->type) { switch (features->type) {
case WACOM_G4: case WACOM_G4:
if (data[7] & 0xf8) { prox = data[7] & 0xf8;
if (penData) { if (prox || wacom->id[1]) {
wacom_input_sync(wcombo); /* sync last event */
if (!wacom->id[0])
penData = 0;
}
wacom->id[1] = PAD_DEVICE_ID; wacom->id[1] = PAD_DEVICE_ID;
wacom_report_key(wcombo, BTN_0, (data[7] & 0x40)); input_report_key(input, BTN_0, (data[7] & 0x40));
wacom_report_key(wcombo, BTN_4, (data[7] & 0x80)); input_report_key(input, BTN_4, (data[7] & 0x80));
rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3); rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
wacom_report_rel(wcombo, REL_WHEEL, rw); input_report_rel(input, REL_WHEEL, rw);
wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0); input_report_key(input, BTN_TOOL_FINGER, 0xf0);
wacom_report_abs(wcombo, ABS_MISC, wacom->id[1]); if (!prox)
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); wacom->id[1] = 0;
} else if (wacom->id[1]) { input_report_abs(input, ABS_MISC, wacom->id[1]);
if (penData) { input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
wacom_input_sync(wcombo); /* sync last event */ retval = 1;
if (!wacom->id[0])
penData = 0;
}
wacom->id[1] = 0;
wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
wacom_report_key(wcombo, BTN_4, (data[7] & 0x80));
wacom_report_rel(wcombo, REL_WHEEL, 0);
wacom_report_key(wcombo, BTN_TOOL_FINGER, 0);
wacom_report_abs(wcombo, ABS_MISC, 0);
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
} }
break; break;
case WACOM_MO:
if ((data[7] & 0xf8) || (data[8] & 0xff)) { case WACOM_MO:
if (penData) { prox = (data[7] & 0xf8) || data[8];
wacom_input_sync(wcombo); /* sync last event */ if (prox || wacom->id[1]) {
if (!wacom->id[0])
penData = 0;
}
wacom->id[1] = PAD_DEVICE_ID; wacom->id[1] = PAD_DEVICE_ID;
wacom_report_key(wcombo, BTN_0, (data[7] & 0x08)); input_report_key(input, BTN_0, (data[7] & 0x08));
wacom_report_key(wcombo, BTN_1, (data[7] & 0x20)); input_report_key(input, BTN_1, (data[7] & 0x20));
wacom_report_key(wcombo, BTN_4, (data[7] & 0x10)); input_report_key(input, BTN_4, (data[7] & 0x10));
wacom_report_key(wcombo, BTN_5, (data[7] & 0x40)); input_report_key(input, BTN_5, (data[7] & 0x40));
wacom_report_abs(wcombo, ABS_WHEEL, (data[8] & 0x7f)); input_report_abs(input, ABS_WHEEL, (data[8] & 0x7f));
wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0); input_report_key(input, BTN_TOOL_FINGER, 0xf0);
wacom_report_abs(wcombo, ABS_MISC, wacom->id[1]); if (!prox)
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); wacom->id[1] = 0;
} else if (wacom->id[1]) { input_report_abs(input, ABS_MISC, wacom->id[1]);
if (penData) { input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
wacom_input_sync(wcombo); /* sync last event */
if (!wacom->id[0])
penData = 0;
}
wacom->id[1] = 0;
wacom_report_key(wcombo, BTN_0, (data[7] & 0x08));
wacom_report_key(wcombo, BTN_1, (data[7] & 0x20));
wacom_report_key(wcombo, BTN_4, (data[7] & 0x10));
wacom_report_key(wcombo, BTN_5, (data[7] & 0x40));
wacom_report_abs(wcombo, ABS_WHEEL, (data[8] & 0x7f));
wacom_report_key(wcombo, BTN_TOOL_FINGER, 0);
wacom_report_abs(wcombo, ABS_MISC, 0);
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
} }
retval = 1;
break; break;
} }
return 1; exit:
return retval;
} }
static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo) static int wacom_intuos_inout(struct wacom_wac *wacom)
{ {
struct wacom_features *features = &wacom->features; struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data; unsigned char *data = wacom->data;
struct input_dev *input = wacom->input;
int idx = 0; int idx = 0;
/* tool number */ /* tool number */
...@@ -316,64 +285,73 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo) ...@@ -316,64 +285,73 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
(data[6] << 4) + (data[7] >> 4); (data[6] << 4) + (data[7] >> 4);
wacom->id[idx] = (data[2] << 4) | (data[3] >> 4); wacom->id[idx] = (data[2] << 4) | (data[3] >> 4);
switch (wacom->id[idx]) { switch (wacom->id[idx]) {
case 0x812: /* Inking pen */ case 0x812: /* Inking pen */
case 0x801: /* Intuos3 Inking pen */ case 0x801: /* Intuos3 Inking pen */
case 0x20802: /* Intuos4 Classic Pen */ case 0x20802: /* Intuos4 Classic Pen */
case 0x012: case 0x012:
wacom->tool[idx] = BTN_TOOL_PENCIL; wacom->tool[idx] = BTN_TOOL_PENCIL;
break; break;
case 0x822: /* Pen */
case 0x842: case 0x822: /* Pen */
case 0x852: case 0x842:
case 0x823: /* Intuos3 Grip Pen */ case 0x852:
case 0x813: /* Intuos3 Classic Pen */ case 0x823: /* Intuos3 Grip Pen */
case 0x885: /* Intuos3 Marker Pen */ case 0x813: /* Intuos3 Classic Pen */
case 0x802: /* Intuos4 Grip Pen Eraser */ case 0x885: /* Intuos3 Marker Pen */
case 0x804: /* Intuos4 Marker Pen */ case 0x802: /* Intuos4 Grip Pen Eraser */
case 0x40802: /* Intuos4 Classic Pen */ case 0x804: /* Intuos4 Marker Pen */
case 0x022: case 0x40802: /* Intuos4 Classic Pen */
wacom->tool[idx] = BTN_TOOL_PEN; case 0x022:
break; wacom->tool[idx] = BTN_TOOL_PEN;
case 0x832: /* Stroke pen */ break;
case 0x032:
wacom->tool[idx] = BTN_TOOL_BRUSH; case 0x832: /* Stroke pen */
break; case 0x032:
case 0x007: /* Mouse 4D and 2D */ wacom->tool[idx] = BTN_TOOL_BRUSH;
case 0x09c: break;
case 0x094:
case 0x017: /* Intuos3 2D Mouse */ case 0x007: /* Mouse 4D and 2D */
case 0x806: /* Intuos4 Mouse */ case 0x09c:
wacom->tool[idx] = BTN_TOOL_MOUSE; case 0x094:
break; case 0x017: /* Intuos3 2D Mouse */
case 0x096: /* Lens cursor */ case 0x806: /* Intuos4 Mouse */
case 0x097: /* Intuos3 Lens cursor */ wacom->tool[idx] = BTN_TOOL_MOUSE;
case 0x006: /* Intuos4 Lens cursor */ break;
wacom->tool[idx] = BTN_TOOL_LENS;
break; case 0x096: /* Lens cursor */
case 0x82a: /* Eraser */ case 0x097: /* Intuos3 Lens cursor */
case 0x85a: case 0x006: /* Intuos4 Lens cursor */
case 0x91a: wacom->tool[idx] = BTN_TOOL_LENS;
case 0xd1a: break;
case 0x0fa:
case 0x82b: /* Intuos3 Grip Pen Eraser */ case 0x82a: /* Eraser */
case 0x81b: /* Intuos3 Classic Pen Eraser */ case 0x85a:
case 0x91b: /* Intuos3 Airbrush Eraser */ case 0x91a:
case 0x80c: /* Intuos4 Marker Pen Eraser */ case 0xd1a:
case 0x80a: /* Intuos4 Grip Pen Eraser */ case 0x0fa:
case 0x4080a: /* Intuos4 Classic Pen Eraser */ case 0x82b: /* Intuos3 Grip Pen Eraser */
case 0x90a: /* Intuos4 Airbrush Eraser */ case 0x81b: /* Intuos3 Classic Pen Eraser */
wacom->tool[idx] = BTN_TOOL_RUBBER; case 0x91b: /* Intuos3 Airbrush Eraser */
break; case 0x80c: /* Intuos4 Marker Pen Eraser */
case 0xd12: case 0x80a: /* Intuos4 Grip Pen Eraser */
case 0x912: case 0x4080a: /* Intuos4 Classic Pen Eraser */
case 0x112: case 0x90a: /* Intuos4 Airbrush Eraser */
case 0x913: /* Intuos3 Airbrush */ wacom->tool[idx] = BTN_TOOL_RUBBER;
case 0x902: /* Intuos4 Airbrush */ break;
wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
break; case 0xd12:
default: /* Unknown tool */ case 0x912:
wacom->tool[idx] = BTN_TOOL_PEN; case 0x112:
case 0x913: /* Intuos3 Airbrush */
case 0x902: /* Intuos4 Airbrush */
wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
break;
default: /* Unknown tool */
wacom->tool[idx] = BTN_TOOL_PEN;
break;
} }
return 1; return 1;
} }
...@@ -384,41 +362,42 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo) ...@@ -384,41 +362,42 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
* Reset all states otherwise we lose the initial states * Reset all states otherwise we lose the initial states
* when in-prox next time * when in-prox next time
*/ */
wacom_report_abs(wcombo, ABS_X, 0); input_report_abs(input, ABS_X, 0);
wacom_report_abs(wcombo, ABS_Y, 0); input_report_abs(input, ABS_Y, 0);
wacom_report_abs(wcombo, ABS_DISTANCE, 0); input_report_abs(input, ABS_DISTANCE, 0);
wacom_report_abs(wcombo, ABS_TILT_X, 0); input_report_abs(input, ABS_TILT_X, 0);
wacom_report_abs(wcombo, ABS_TILT_Y, 0); input_report_abs(input, ABS_TILT_Y, 0);
if (wacom->tool[idx] >= BTN_TOOL_MOUSE) { if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
wacom_report_key(wcombo, BTN_LEFT, 0); input_report_key(input, BTN_LEFT, 0);
wacom_report_key(wcombo, BTN_MIDDLE, 0); input_report_key(input, BTN_MIDDLE, 0);
wacom_report_key(wcombo, BTN_RIGHT, 0); input_report_key(input, BTN_RIGHT, 0);
wacom_report_key(wcombo, BTN_SIDE, 0); input_report_key(input, BTN_SIDE, 0);
wacom_report_key(wcombo, BTN_EXTRA, 0); input_report_key(input, BTN_EXTRA, 0);
wacom_report_abs(wcombo, ABS_THROTTLE, 0); input_report_abs(input, ABS_THROTTLE, 0);
wacom_report_abs(wcombo, ABS_RZ, 0); input_report_abs(input, ABS_RZ, 0);
} else { } else {
wacom_report_abs(wcombo, ABS_PRESSURE, 0); input_report_abs(input, ABS_PRESSURE, 0);
wacom_report_key(wcombo, BTN_STYLUS, 0); input_report_key(input, BTN_STYLUS, 0);
wacom_report_key(wcombo, BTN_STYLUS2, 0); input_report_key(input, BTN_STYLUS2, 0);
wacom_report_key(wcombo, BTN_TOUCH, 0); input_report_key(input, BTN_TOUCH, 0);
wacom_report_abs(wcombo, ABS_WHEEL, 0); input_report_abs(input, ABS_WHEEL, 0);
if (features->type >= INTUOS3S) if (features->type >= INTUOS3S)
wacom_report_abs(wcombo, ABS_Z, 0); input_report_abs(input, ABS_Z, 0);
} }
wacom_report_key(wcombo, wacom->tool[idx], 0); input_report_key(input, wacom->tool[idx], 0);
wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */ input_report_abs(input, ABS_MISC, 0); /* reset tool id */
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]); input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
wacom->id[idx] = 0; wacom->id[idx] = 0;
return 2; return 2;
} }
return 0; return 0;
} }
static void wacom_intuos_general(struct wacom_wac *wacom, void *wcombo) static void wacom_intuos_general(struct wacom_wac *wacom)
{ {
struct wacom_features *features = &wacom->features; struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data; unsigned char *data = wacom->data;
struct input_dev *input = wacom->input;
unsigned int t; unsigned int t;
/* general pen packet */ /* general pen packet */
...@@ -426,30 +405,30 @@ static void wacom_intuos_general(struct wacom_wac *wacom, void *wcombo) ...@@ -426,30 +405,30 @@ static void wacom_intuos_general(struct wacom_wac *wacom, void *wcombo)
t = (data[6] << 2) | ((data[7] >> 6) & 3); t = (data[6] << 2) | ((data[7] >> 6) & 3);
if (features->type >= INTUOS4S && features->type <= INTUOS4L) if (features->type >= INTUOS4S && features->type <= INTUOS4L)
t = (t << 1) | (data[1] & 1); t = (t << 1) | (data[1] & 1);
wacom_report_abs(wcombo, ABS_PRESSURE, t); input_report_abs(input, ABS_PRESSURE, t);
wacom_report_abs(wcombo, ABS_TILT_X, input_report_abs(input, ABS_TILT_X,
((data[7] << 1) & 0x7e) | (data[8] >> 7)); ((data[7] << 1) & 0x7e) | (data[8] >> 7));
wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f); input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f);
wacom_report_key(wcombo, BTN_STYLUS, data[1] & 2); input_report_key(input, BTN_STYLUS, data[1] & 2);
wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 4); input_report_key(input, BTN_STYLUS2, data[1] & 4);
wacom_report_key(wcombo, BTN_TOUCH, t > 10); input_report_key(input, BTN_TOUCH, t > 10);
} }
/* airbrush second packet */ /* airbrush second packet */
if ((data[1] & 0xbc) == 0xb4) { if ((data[1] & 0xbc) == 0xb4) {
wacom_report_abs(wcombo, ABS_WHEEL, input_report_abs(input, ABS_WHEEL,
(data[6] << 2) | ((data[7] >> 6) & 3)); (data[6] << 2) | ((data[7] >> 6) & 3));
wacom_report_abs(wcombo, ABS_TILT_X, input_report_abs(input, ABS_TILT_X,
((data[7] << 1) & 0x7e) | (data[8] >> 7)); ((data[7] << 1) & 0x7e) | (data[8] >> 7));
wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f); input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f);
} }
return;
} }
static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo) static int wacom_intuos_irq(struct wacom_wac *wacom)
{ {
struct wacom_features *features = &wacom->features; struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data; unsigned char *data = wacom->data;
struct input_dev *input = wacom->input;
unsigned int t; unsigned int t;
int idx = 0, result; int idx = 0, result;
...@@ -470,61 +449,61 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo) ...@@ -470,61 +449,61 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
wacom->tool[1] = BTN_TOOL_FINGER; wacom->tool[1] = BTN_TOOL_FINGER;
if (features->type >= INTUOS4S && features->type <= INTUOS4L) { if (features->type >= INTUOS4S && features->type <= INTUOS4L) {
wacom_report_key(wcombo, BTN_0, (data[2] & 0x01)); input_report_key(input, BTN_0, (data[2] & 0x01));
wacom_report_key(wcombo, BTN_1, (data[3] & 0x01)); input_report_key(input, BTN_1, (data[3] & 0x01));
wacom_report_key(wcombo, BTN_2, (data[3] & 0x02)); input_report_key(input, BTN_2, (data[3] & 0x02));
wacom_report_key(wcombo, BTN_3, (data[3] & 0x04)); input_report_key(input, BTN_3, (data[3] & 0x04));
wacom_report_key(wcombo, BTN_4, (data[3] & 0x08)); input_report_key(input, BTN_4, (data[3] & 0x08));
wacom_report_key(wcombo, BTN_5, (data[3] & 0x10)); input_report_key(input, BTN_5, (data[3] & 0x10));
wacom_report_key(wcombo, BTN_6, (data[3] & 0x20)); input_report_key(input, BTN_6, (data[3] & 0x20));
if (data[1] & 0x80) { if (data[1] & 0x80) {
wacom_report_abs(wcombo, ABS_WHEEL, (data[1] & 0x7f)); input_report_abs(input, ABS_WHEEL, (data[1] & 0x7f));
} else { } else {
/* Out of proximity, clear wheel value. */ /* Out of proximity, clear wheel value. */
wacom_report_abs(wcombo, ABS_WHEEL, 0); input_report_abs(input, ABS_WHEEL, 0);
} }
if (features->type != INTUOS4S) { if (features->type != INTUOS4S) {
wacom_report_key(wcombo, BTN_7, (data[3] & 0x40)); input_report_key(input, BTN_7, (data[3] & 0x40));
wacom_report_key(wcombo, BTN_8, (data[3] & 0x80)); input_report_key(input, BTN_8, (data[3] & 0x80));
} }
if (data[1] | (data[2] & 0x01) | data[3]) { if (data[1] | (data[2] & 0x01) | data[3]) {
wacom_report_key(wcombo, wacom->tool[1], 1); input_report_key(input, wacom->tool[1], 1);
wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID); input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
} else { } else {
wacom_report_key(wcombo, wacom->tool[1], 0); input_report_key(input, wacom->tool[1], 0);
wacom_report_abs(wcombo, ABS_MISC, 0); input_report_abs(input, ABS_MISC, 0);
} }
} else { } else {
wacom_report_key(wcombo, BTN_0, (data[5] & 0x01)); input_report_key(input, BTN_0, (data[5] & 0x01));
wacom_report_key(wcombo, BTN_1, (data[5] & 0x02)); input_report_key(input, BTN_1, (data[5] & 0x02));
wacom_report_key(wcombo, BTN_2, (data[5] & 0x04)); input_report_key(input, BTN_2, (data[5] & 0x04));
wacom_report_key(wcombo, BTN_3, (data[5] & 0x08)); input_report_key(input, BTN_3, (data[5] & 0x08));
wacom_report_key(wcombo, BTN_4, (data[6] & 0x01)); input_report_key(input, BTN_4, (data[6] & 0x01));
wacom_report_key(wcombo, BTN_5, (data[6] & 0x02)); input_report_key(input, BTN_5, (data[6] & 0x02));
wacom_report_key(wcombo, BTN_6, (data[6] & 0x04)); input_report_key(input, BTN_6, (data[6] & 0x04));
wacom_report_key(wcombo, BTN_7, (data[6] & 0x08)); input_report_key(input, BTN_7, (data[6] & 0x08));
wacom_report_key(wcombo, BTN_8, (data[5] & 0x10)); input_report_key(input, BTN_8, (data[5] & 0x10));
wacom_report_key(wcombo, BTN_9, (data[6] & 0x10)); input_report_key(input, BTN_9, (data[6] & 0x10));
wacom_report_abs(wcombo, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]); input_report_abs(input, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
wacom_report_abs(wcombo, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]); input_report_abs(input, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
if ((data[5] & 0x1f) | (data[6] & 0x1f) | (data[1] & 0x1f) | if ((data[5] & 0x1f) | (data[6] & 0x1f) | (data[1] & 0x1f) |
data[2] | (data[3] & 0x1f) | data[4]) { data[2] | (data[3] & 0x1f) | data[4]) {
wacom_report_key(wcombo, wacom->tool[1], 1); input_report_key(input, wacom->tool[1], 1);
wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID); input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
} else { } else {
wacom_report_key(wcombo, wacom->tool[1], 0); input_report_key(input, wacom->tool[1], 0);
wacom_report_abs(wcombo, ABS_MISC, 0); input_report_abs(input, ABS_MISC, 0);
} }
} }
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xffffffff); input_event(input, EV_MSC, MSC_SERIAL, 0xffffffff);
return 1; return 1;
} }
/* process in/out prox events */ /* process in/out prox events */
result = wacom_intuos_inout(wacom, wcombo); result = wacom_intuos_inout(wacom);
if (result) if (result)
return result-1; return result - 1;
/* don't proceed if we don't know the ID */ /* don't proceed if we don't know the ID */
if (!wacom->id[idx]) if (!wacom->id[idx])
...@@ -545,17 +524,17 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo) ...@@ -545,17 +524,17 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
return 0; return 0;
if (features->type >= INTUOS3S) { if (features->type >= INTUOS3S) {
wacom_report_abs(wcombo, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1)); input_report_abs(input, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1));
wacom_report_abs(wcombo, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1)); input_report_abs(input, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1));
wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 2) & 0x3f)); input_report_abs(input, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
} else { } else {
wacom_report_abs(wcombo, ABS_X, wacom_be16_to_cpu(&data[2])); input_report_abs(input, ABS_X, be16_to_cpup((__be16 *)&data[2]));
wacom_report_abs(wcombo, ABS_Y, wacom_be16_to_cpu(&data[4])); input_report_abs(input, ABS_Y, be16_to_cpup((__be16 *)&data[4]));
wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 3) & 0x1f)); input_report_abs(input, ABS_DISTANCE, ((data[9] >> 3) & 0x1f));
} }
/* process general packets */ /* process general packets */
wacom_intuos_general(wacom, wcombo); wacom_intuos_general(wacom);
/* 4D mouse, 2D mouse, marker pen rotation, tilt mouse, or Lens cursor packets */ /* 4D mouse, 2D mouse, marker pen rotation, tilt mouse, or Lens cursor packets */
if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0 || (data[1] & 0xbc) == 0xac) { if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0 || (data[1] & 0xbc) == 0xac) {
...@@ -567,174 +546,191 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo) ...@@ -567,174 +546,191 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
t = (data[6] << 3) | ((data[7] >> 5) & 7); t = (data[6] << 3) | ((data[7] >> 5) & 7);
t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) : t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
((t-1) / 2 + 450)) : (450 - t / 2) ; ((t-1) / 2 + 450)) : (450 - t / 2) ;
wacom_report_abs(wcombo, ABS_Z, t); input_report_abs(input, ABS_Z, t);
} else { } else {
/* 4D mouse rotation packet */ /* 4D mouse rotation packet */
t = (data[6] << 3) | ((data[7] >> 5) & 7); t = (data[6] << 3) | ((data[7] >> 5) & 7);
wacom_report_abs(wcombo, ABS_RZ, (data[7] & 0x20) ? input_report_abs(input, ABS_RZ, (data[7] & 0x20) ?
((t - 1) / 2) : -t / 2); ((t - 1) / 2) : -t / 2);
} }
} else if (!(data[1] & 0x10) && features->type < INTUOS3S) { } else if (!(data[1] & 0x10) && features->type < INTUOS3S) {
/* 4D mouse packet */ /* 4D mouse packet */
wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01); input_report_key(input, BTN_LEFT, data[8] & 0x01);
wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02); input_report_key(input, BTN_MIDDLE, data[8] & 0x02);
wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04); input_report_key(input, BTN_RIGHT, data[8] & 0x04);
wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x20); input_report_key(input, BTN_SIDE, data[8] & 0x20);
wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x10); input_report_key(input, BTN_EXTRA, data[8] & 0x10);
t = (data[6] << 2) | ((data[7] >> 6) & 3); t = (data[6] << 2) | ((data[7] >> 6) & 3);
wacom_report_abs(wcombo, ABS_THROTTLE, (data[8] & 0x08) ? -t : t); input_report_abs(input, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
} else if (wacom->tool[idx] == BTN_TOOL_MOUSE) { } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
/* I4 mouse */ /* I4 mouse */
if (features->type >= INTUOS4S && features->type <= INTUOS4L) { if (features->type >= INTUOS4S && features->type <= INTUOS4L) {
wacom_report_key(wcombo, BTN_LEFT, data[6] & 0x01); input_report_key(input, BTN_LEFT, data[6] & 0x01);
wacom_report_key(wcombo, BTN_MIDDLE, data[6] & 0x02); input_report_key(input, BTN_MIDDLE, data[6] & 0x02);
wacom_report_key(wcombo, BTN_RIGHT, data[6] & 0x04); input_report_key(input, BTN_RIGHT, data[6] & 0x04);
wacom_report_rel(wcombo, REL_WHEEL, ((data[7] & 0x80) >> 7) input_report_rel(input, REL_WHEEL, ((data[7] & 0x80) >> 7)
- ((data[7] & 0x40) >> 6)); - ((data[7] & 0x40) >> 6));
wacom_report_key(wcombo, BTN_SIDE, data[6] & 0x08); input_report_key(input, BTN_SIDE, data[6] & 0x08);
wacom_report_key(wcombo, BTN_EXTRA, data[6] & 0x10); input_report_key(input, BTN_EXTRA, data[6] & 0x10);
wacom_report_abs(wcombo, ABS_TILT_X, input_report_abs(input, ABS_TILT_X,
((data[7] << 1) & 0x7e) | (data[8] >> 7)); ((data[7] << 1) & 0x7e) | (data[8] >> 7));
wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f); input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f);
} else { } else {
/* 2D mouse packet */ /* 2D mouse packet */
wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x04); input_report_key(input, BTN_LEFT, data[8] & 0x04);
wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x08); input_report_key(input, BTN_MIDDLE, data[8] & 0x08);
wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x10); input_report_key(input, BTN_RIGHT, data[8] & 0x10);
wacom_report_rel(wcombo, REL_WHEEL, (data[8] & 0x01) input_report_rel(input, REL_WHEEL, (data[8] & 0x01)
- ((data[8] & 0x02) >> 1)); - ((data[8] & 0x02) >> 1));
/* I3 2D mouse side buttons */ /* I3 2D mouse side buttons */
if (features->type >= INTUOS3S && features->type <= INTUOS3L) { if (features->type >= INTUOS3S && features->type <= INTUOS3L) {
wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x40); input_report_key(input, BTN_SIDE, data[8] & 0x40);
wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x20); input_report_key(input, BTN_EXTRA, data[8] & 0x20);
} }
} }
} else if ((features->type < INTUOS3S || features->type == INTUOS3L || } else if ((features->type < INTUOS3S || features->type == INTUOS3L ||
features->type == INTUOS4L) && features->type == INTUOS4L) &&
wacom->tool[idx] == BTN_TOOL_LENS) { wacom->tool[idx] == BTN_TOOL_LENS) {
/* Lens cursor packets */ /* Lens cursor packets */
wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01); input_report_key(input, BTN_LEFT, data[8] & 0x01);
wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02); input_report_key(input, BTN_MIDDLE, data[8] & 0x02);
wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04); input_report_key(input, BTN_RIGHT, data[8] & 0x04);
wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x10); input_report_key(input, BTN_SIDE, data[8] & 0x10);
wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x08); input_report_key(input, BTN_EXTRA, data[8] & 0x08);
} }
} }
wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */ input_report_abs(input, ABS_MISC, wacom->id[idx]); /* report tool id */
wacom_report_key(wcombo, wacom->tool[idx], 1); input_report_key(input, wacom->tool[idx], 1);
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]); input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
return 1; return 1;
} }
static void wacom_tpc_finger_in(struct wacom_wac *wacom, void *wcombo, char *data, int idx) static void wacom_tpc_finger_in(struct wacom_wac *wacom, char *data, int idx)
{ {
wacom_report_abs(wcombo, ABS_X, struct input_dev *input = wacom->input;
(data[2 + idx * 2] & 0xff) | ((data[3 + idx * 2] & 0x7f) << 8)); int finger = idx + 1;
wacom_report_abs(wcombo, ABS_Y, int x = le16_to_cpup((__le16 *)&data[finger * 2]) & 0x7fff;
(data[6 + idx * 2] & 0xff) | ((data[7 + idx * 2] & 0x7f) << 8)); int y = le16_to_cpup((__le16 *)&data[4 + finger * 2]) & 0x7fff;
wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]);
wacom_report_key(wcombo, wacom->tool[idx], 1); /*
if (idx) * Work around input core suppressing "duplicate" events since
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); * we are abusing ABS_X/ABS_Y to transmit multi-finger data.
else * This should go away once we switch to true multitouch
wacom_report_key(wcombo, BTN_TOUCH, 1); * protocol.
*/
if (wacom->last_finger != finger) {
if (x == input->abs[ABS_X])
x++;
if (y == input->abs[ABS_Y])
y++;
}
input_report_abs(input, ABS_X, x);
input_report_abs(input, ABS_Y, y);
input_report_abs(input, ABS_MISC, wacom->id[0]);
input_report_key(input, wacom->tool[finger], 1);
if (!idx)
input_report_key(input, BTN_TOUCH, 1);
input_event(input, EV_MSC, MSC_SERIAL, finger);
input_sync(wacom->input);
wacom->last_finger = finger;
} }
static void wacom_tpc_touch_out(struct wacom_wac *wacom, void *wcombo, int idx) static void wacom_tpc_touch_out(struct wacom_wac *wacom, int idx)
{ {
wacom_report_abs(wcombo, ABS_X, 0); struct input_dev *input = wacom->input;
wacom_report_abs(wcombo, ABS_Y, 0); int finger = idx + 1;
wacom_report_abs(wcombo, ABS_MISC, 0);
wacom_report_key(wcombo, wacom->tool[idx], 0); input_report_abs(input, ABS_X, 0);
if (idx) input_report_abs(input, ABS_Y, 0);
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); input_report_abs(input, ABS_MISC, 0);
else input_report_key(input, wacom->tool[finger], 0);
wacom_report_key(wcombo, BTN_TOUCH, 0); if (!idx)
return; input_report_key(input, BTN_TOUCH, 0);
input_event(input, EV_MSC, MSC_SERIAL, finger);
input_sync(input);
} }
static void wacom_tpc_touch_in(struct wacom_wac *wacom, void *wcombo) static void wacom_tpc_touch_in(struct wacom_wac *wacom, size_t len)
{ {
char *data = wacom->data; char *data = wacom->data;
struct urb *urb = ((struct wacom_combo *)wcombo)->urb; struct input_dev *input = wacom->input;
static int firstFinger = 0;
static int secondFinger = 0;
wacom->tool[0] = BTN_TOOL_DOUBLETAP; wacom->tool[1] = BTN_TOOL_DOUBLETAP;
wacom->id[0] = TOUCH_DEVICE_ID; wacom->id[0] = TOUCH_DEVICE_ID;
wacom->tool[1] = BTN_TOOL_TRIPLETAP; wacom->tool[2] = BTN_TOOL_TRIPLETAP;
if (len != WACOM_PKGLEN_TPC1FG) {
if (urb->actual_length != WACOM_PKGLEN_TPC1FG) {
switch (data[0]) { switch (data[0]) {
case WACOM_REPORT_TPC1FG:
wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2]));
wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4]));
wacom_report_abs(wcombo, ABS_PRESSURE, wacom_le16_to_cpu(&data[6]));
wacom_report_key(wcombo, BTN_TOUCH, wacom_le16_to_cpu(&data[6]));
wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]);
wacom_report_key(wcombo, wacom->tool[0], 1);
break;
case WACOM_REPORT_TPC2FG:
/* keep this byte to send proper out-prox event */
wacom->id[1] = data[1] & 0x03;
if (data[1] & 0x01) {
wacom_tpc_finger_in(wacom, wcombo, data, 0);
firstFinger = 1;
} else if (firstFinger) {
wacom_tpc_touch_out(wacom, wcombo, 0);
}
if (data[1] & 0x02) { case WACOM_REPORT_TPC1FG:
/* sync first finger data */ input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
if (firstFinger) input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
wacom_input_sync(wcombo); input_report_abs(input, ABS_PRESSURE, le16_to_cpup((__le16 *)&data[6]));
input_report_key(input, BTN_TOUCH, le16_to_cpup((__le16 *)&data[6]));
input_report_abs(input, ABS_MISC, wacom->id[0]);
input_report_key(input, wacom->tool[1], 1);
input_sync(input);
break;
wacom_tpc_finger_in(wacom, wcombo, data, 1); case WACOM_REPORT_TPC2FG:
secondFinger = 1; if (data[1] & 0x01)
} else if (secondFinger) { wacom_tpc_finger_in(wacom, data, 0);
/* sync first finger data */ else if (wacom->id[1] & 0x01)
if (firstFinger) wacom_tpc_touch_out(wacom, 0);
wacom_input_sync(wcombo);
wacom_tpc_touch_out(wacom, wcombo, 1); if (data[1] & 0x02)
secondFinger = 0; wacom_tpc_finger_in(wacom, data, 1);
} else if (wacom->id[1] & 0x02)
if (!(data[1] & 0x01)) wacom_tpc_touch_out(wacom, 1);
firstFinger = 0; break;
break;
} }
} else { } else {
wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1])); input_report_abs(input, ABS_X, get_unaligned_le16(&data[1]));
wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3])); input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3]));
wacom_report_key(wcombo, BTN_TOUCH, 1); input_report_key(input, BTN_TOUCH, 1);
wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); input_report_abs(input, ABS_MISC, wacom->id[1]);
wacom_report_key(wcombo, wacom->tool[0], 1); input_report_key(input, wacom->tool[1], 1);
input_sync(input);
} }
return;
} }
static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo) static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
{ {
struct wacom_features *features = &wacom->features; struct wacom_features *features = &wacom->features;
char *data = wacom->data; char *data = wacom->data;
int prox = 0, pressure, idx = -1; struct input_dev *input = wacom->input;
static int stylusInProx, touchInProx = 1, touchOut; int prox = 0, pressure;
struct urb *urb = ((struct wacom_combo *)wcombo)->urb; int retval = 0;
dbg("wacom_tpc_irq: received report #%d", data[0]); dbg("wacom_tpc_irq: received report #%d", data[0]);
if (urb->actual_length == WACOM_PKGLEN_TPC1FG || /* single touch */ if (len == WACOM_PKGLEN_TPC1FG || /* single touch */
data[0] == WACOM_REPORT_TPC1FG || /* single touch */ data[0] == WACOM_REPORT_TPC1FG || /* single touch */
data[0] == WACOM_REPORT_TPC2FG) { /* 2FG touch */ data[0] == WACOM_REPORT_TPC2FG) { /* 2FG touch */
if (urb->actual_length == WACOM_PKGLEN_TPC1FG) { /* with touch */
if (wacom->shared->stylus_in_proximity) {
if (wacom->id[1] & 0x01)
wacom_tpc_touch_out(wacom, 0);
if (wacom->id[1] & 0x02)
wacom_tpc_touch_out(wacom, 1);
wacom->id[1] = 0;
return 0;
}
if (len == WACOM_PKGLEN_TPC1FG) { /* with touch */
prox = data[0] & 0x01; prox = data[0] & 0x01;
} else { /* with capacity */ } else { /* with capacity */
if (data[0] == WACOM_REPORT_TPC1FG) if (data[0] == WACOM_REPORT_TPC1FG)
...@@ -745,168 +741,264 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo) ...@@ -745,168 +741,264 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
prox = data[1] & 0x03; prox = data[1] & 0x03;
} }
if (!stylusInProx) { /* stylus not in prox */ if (prox) {
if (prox) { if (!wacom->id[1])
if (touchInProx) { wacom->last_finger = 1;
wacom_tpc_touch_in(wacom, wcombo); wacom_tpc_touch_in(wacom, len);
touchOut = 1; } else {
return 1; if (data[0] == WACOM_REPORT_TPC2FG) {
}
} else {
/* 2FGT out-prox */ /* 2FGT out-prox */
if (data[0] == WACOM_REPORT_TPC2FG) { if (wacom->id[1] & 0x01)
idx = (wacom->id[1] & 0x01) - 1; wacom_tpc_touch_out(wacom, 0);
if (idx == 0) {
wacom_tpc_touch_out(wacom, wcombo, idx); if (wacom->id[1] & 0x02)
/* sync first finger event */ wacom_tpc_touch_out(wacom, 1);
if (wacom->id[1] & 0x02) } else
wacom_input_sync(wcombo); /* one finger touch */
} wacom_tpc_touch_out(wacom, 0);
idx = (wacom->id[1] & 0x02) - 1;
if (idx == 1) wacom->id[0] = 0;
wacom_tpc_touch_out(wacom, wcombo, idx);
} else /* one finger touch */
wacom_tpc_touch_out(wacom, wcombo, 0);
touchOut = 0;
touchInProx = 1;
return 1;
}
} else if (touchOut || !prox) { /* force touch out-prox */
wacom_tpc_touch_out(wacom, wcombo, 0);
touchOut = 0;
touchInProx = 1;
return 1;
} }
/* keep prox bit to send proper out-prox event */
wacom->id[1] = prox;
} else if (data[0] == WACOM_REPORT_PENABLED) { /* Penabled */ } else if (data[0] == WACOM_REPORT_PENABLED) { /* Penabled */
prox = data[1] & 0x20; prox = data[1] & 0x20;
touchInProx = 0; if (!wacom->shared->stylus_in_proximity) { /* first in prox */
/* Going into proximity select tool */
wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
if (wacom->tool[0] == BTN_TOOL_PEN)
wacom->id[0] = STYLUS_DEVICE_ID;
else
wacom->id[0] = ERASER_DEVICE_ID;
if (prox) { /* in prox */ wacom->shared->stylus_in_proximity = true;
if (!wacom->id[0]) { }
/* Going into proximity select tool */ input_report_key(input, BTN_STYLUS, data[1] & 0x02);
wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
if (wacom->tool[0] == BTN_TOOL_PEN) input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
wacom->id[0] = STYLUS_DEVICE_ID; input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
else pressure = ((data[7] & 0x01) << 8) | data[6];
wacom->id[0] = ERASER_DEVICE_ID; if (pressure < 0)
} pressure = features->pressure_max + pressure + 1;
wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02); input_report_abs(input, ABS_PRESSURE, pressure);
wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x10); input_report_key(input, BTN_TOUCH, data[1] & 0x05);
wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2])); if (!prox) { /* out-prox */
wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4]));
pressure = ((data[7] & 0x01) << 8) | data[6];
if (pressure < 0)
pressure = features->pressure_max + pressure + 1;
wacom_report_abs(wcombo, ABS_PRESSURE, pressure);
wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x05);
} else {
wacom_report_abs(wcombo, ABS_X, 0);
wacom_report_abs(wcombo, ABS_Y, 0);
wacom_report_abs(wcombo, ABS_PRESSURE, 0);
wacom_report_key(wcombo, BTN_STYLUS, 0);
wacom_report_key(wcombo, BTN_STYLUS2, 0);
wacom_report_key(wcombo, BTN_TOUCH, 0);
wacom->id[0] = 0; wacom->id[0] = 0;
/* pen is out so touch can be enabled now */ wacom->shared->stylus_in_proximity = false;
touchInProx = 1;
} }
wacom_report_key(wcombo, wacom->tool[0], prox); input_report_key(input, wacom->tool[0], prox);
wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); input_report_abs(input, ABS_MISC, wacom->id[0]);
stylusInProx = prox; retval = 1;
return 1;
} }
return 0; return retval;
} }
int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo) void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
{ {
bool sync;
switch (wacom_wac->features.type) { switch (wacom_wac->features.type) {
case PENPARTNER: case PENPARTNER:
return wacom_penpartner_irq(wacom_wac, wcombo); sync = wacom_penpartner_irq(wacom_wac);
break;
case PL:
return wacom_pl_irq(wacom_wac, wcombo); case PL:
sync = wacom_pl_irq(wacom_wac);
case WACOM_G4: break;
case GRAPHIRE:
case WACOM_MO: case WACOM_G4:
return wacom_graphire_irq(wacom_wac, wcombo); case GRAPHIRE:
case WACOM_MO:
case PTU: sync = wacom_graphire_irq(wacom_wac);
return wacom_ptu_irq(wacom_wac, wcombo); break;
case INTUOS: case PTU:
case INTUOS3S: sync = wacom_ptu_irq(wacom_wac);
case INTUOS3: break;
case INTUOS3L:
case INTUOS4S: case INTUOS:
case INTUOS4: case INTUOS3S:
case INTUOS4L: case INTUOS3:
case CINTIQ: case INTUOS3L:
case WACOM_BEE: case INTUOS4S:
return wacom_intuos_irq(wacom_wac, wcombo); case INTUOS4:
case INTUOS4L:
case TABLETPC: case CINTIQ:
case TABLETPC2FG: case WACOM_BEE:
return wacom_tpc_irq(wacom_wac, wcombo); sync = wacom_intuos_irq(wacom_wac);
break;
default:
return 0; case TABLETPC:
case TABLETPC2FG:
sync = wacom_tpc_irq(wacom_wac, len);
break;
default:
sync = false;
break;
} }
return 0;
if (sync)
input_sync(wacom_wac->input);
}
static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
{
struct input_dev *input_dev = wacom_wac->input;
input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
input_set_capability(input_dev, EV_REL, REL_WHEEL);
__set_bit(BTN_LEFT, input_dev->keybit);
__set_bit(BTN_RIGHT, input_dev->keybit);
__set_bit(BTN_MIDDLE, input_dev->keybit);
__set_bit(BTN_SIDE, input_dev->keybit);
__set_bit(BTN_EXTRA, input_dev->keybit);
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
__set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
__set_bit(BTN_TOOL_BRUSH, input_dev->keybit);
__set_bit(BTN_TOOL_PENCIL, input_dev->keybit);
__set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit);
__set_bit(BTN_TOOL_LENS, input_dev->keybit);
__set_bit(BTN_STYLUS, input_dev->keybit);
__set_bit(BTN_STYLUS2, input_dev->keybit);
input_set_abs_params(input_dev, ABS_DISTANCE,
0, wacom_wac->features.distance_max, 0, 0);
input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
} }
void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac) void wacom_setup_input_capabilities(struct input_dev *input_dev,
struct wacom_wac *wacom_wac)
{ {
struct wacom_features *features = &wacom_wac->features;
int i;
input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
__set_bit(BTN_TOUCH, input_dev->keybit);
input_set_abs_params(input_dev, ABS_X, 0, features->x_max, 4, 0);
input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, 4, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, 0, 0);
__set_bit(ABS_MISC, input_dev->absbit);
switch (wacom_wac->features.type) { switch (wacom_wac->features.type) {
case WACOM_MO: case WACOM_MO:
input_dev_mo(input_dev, wacom_wac); __set_bit(BTN_1, input_dev->keybit);
case WACOM_G4: __set_bit(BTN_5, input_dev->keybit);
input_dev_g4(input_dev, wacom_wac);
/* fall through */ input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
case GRAPHIRE: /* fall through */
input_dev_g(input_dev, wacom_wac);
break; case WACOM_G4:
case WACOM_BEE: input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
input_dev_bee(input_dev, wacom_wac);
case INTUOS3: __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
case INTUOS3L: __set_bit(BTN_0, input_dev->keybit);
case CINTIQ: __set_bit(BTN_4, input_dev->keybit);
input_dev_i3(input_dev, wacom_wac); /* fall through */
/* fall through */
case INTUOS3S: case GRAPHIRE:
input_dev_i3s(input_dev, wacom_wac); input_set_capability(input_dev, EV_REL, REL_WHEEL);
/* fall through */
case INTUOS: __set_bit(BTN_LEFT, input_dev->keybit);
input_dev_i(input_dev, wacom_wac); __set_bit(BTN_RIGHT, input_dev->keybit);
break; __set_bit(BTN_MIDDLE, input_dev->keybit);
case INTUOS4:
case INTUOS4L: __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
input_dev_i4(input_dev, wacom_wac); __set_bit(BTN_TOOL_PEN, input_dev->keybit);
/* fall through */ __set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
case INTUOS4S: __set_bit(BTN_STYLUS, input_dev->keybit);
input_dev_i4s(input_dev, wacom_wac); __set_bit(BTN_STYLUS2, input_dev->keybit);
input_dev_i(input_dev, wacom_wac); break;
break;
case TABLETPC2FG: case WACOM_BEE:
input_dev_tpc2fg(input_dev, wacom_wac); __set_bit(BTN_8, input_dev->keybit);
/* fall through */ __set_bit(BTN_9, input_dev->keybit);
case TABLETPC: /* fall through */
input_dev_tpc(input_dev, wacom_wac);
if (wacom_wac->features.device_type != BTN_TOOL_PEN) case INTUOS3:
break; /* no need to process stylus stuff */ case INTUOS3L:
case CINTIQ:
/* fall through */ __set_bit(BTN_4, input_dev->keybit);
case PL: __set_bit(BTN_5, input_dev->keybit);
case PTU: __set_bit(BTN_6, input_dev->keybit);
input_dev_pl(input_dev, wacom_wac); __set_bit(BTN_7, input_dev->keybit);
/* fall through */
case PENPARTNER: input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
input_dev_pt(input_dev, wacom_wac); /* fall through */
break;
case INTUOS3S:
__set_bit(BTN_0, input_dev->keybit);
__set_bit(BTN_1, input_dev->keybit);
__set_bit(BTN_2, input_dev->keybit);
__set_bit(BTN_3, input_dev->keybit);
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
/* fall through */
case INTUOS:
wacom_setup_intuos(wacom_wac);
break;
case INTUOS4:
case INTUOS4L:
__set_bit(BTN_7, input_dev->keybit);
__set_bit(BTN_8, input_dev->keybit);
/* fall through */
case INTUOS4S:
for (i = 0; i < 7; i++)
__set_bit(BTN_0 + i, input_dev->keybit);
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
wacom_setup_intuos(wacom_wac);
break;
case TABLETPC2FG:
if (features->device_type == BTN_TOOL_TRIPLETAP) {
__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
}
/* fall through */
case TABLETPC:
if (features->device_type == BTN_TOOL_DOUBLETAP ||
features->device_type == BTN_TOOL_TRIPLETAP) {
input_set_abs_params(input_dev, ABS_RX, 0, features->x_phy, 0, 0);
input_set_abs_params(input_dev, ABS_RY, 0, features->y_phy, 0, 0);
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
}
if (features->device_type != BTN_TOOL_PEN)
break; /* no need to process stylus stuff */
/* fall through */
case PL:
case PTU:
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
__set_bit(BTN_STYLUS, input_dev->keybit);
__set_bit(BTN_STYLUS2, input_dev->keybit);
/* fall through */
case PENPARTNER:
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
break;
} }
return;
} }
static const struct wacom_features wacom_features_0x00 = static const struct wacom_features wacom_features_0x00 =
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
#ifndef WACOM_WAC_H #ifndef WACOM_WAC_H
#define WACOM_WAC_H #define WACOM_WAC_H
#include <linux/types.h>
/* maximum packet length for USB devices */ /* maximum packet length for USB devices */
#define WACOM_PKGLEN_MAX 32 #define WACOM_PKGLEN_MAX 32
...@@ -71,13 +73,20 @@ struct wacom_features { ...@@ -71,13 +73,20 @@ struct wacom_features {
unsigned char unitExpo; unsigned char unitExpo;
}; };
struct wacom_shared {
bool stylus_in_proximity;
};
struct wacom_wac { struct wacom_wac {
char name[64]; char name[64];
unsigned char *data; unsigned char *data;
int tool[2]; int tool[3];
int id[2]; int id[3];
__u32 serial[2]; __u32 serial[2];
int last_finger;
struct wacom_features features; struct wacom_features features;
struct wacom_shared *shared;
struct input_dev *input;
}; };
#endif #endif
...@@ -119,6 +119,18 @@ config TOUCHSCREEN_DYNAPRO ...@@ -119,6 +119,18 @@ config TOUCHSCREEN_DYNAPRO
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 dynapro. module will be called dynapro.
config TOUCHSCREEN_HAMPSHIRE
tristate "Hampshire serial touchscreen"
select SERIO
help
Say Y here if you have a Hampshire serial touchscreen connected to
your system.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called hampshire.
config TOUCHSCREEN_EETI config TOUCHSCREEN_EETI
tristate "EETI touchscreen panel support" tristate "EETI touchscreen panel support"
depends on I2C depends on I2C
......
...@@ -13,6 +13,7 @@ obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o ...@@ -13,6 +13,7 @@ obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
......
/*
* Hampshire serial touchscreen driver
*
* Copyright (c) 2010 Adam Bennett
* Based on the dynapro driver (c) Tias Guns
*
*/
/*
* 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.
*/
/*
* 2010/04/08 Adam Bennett <abennett72@gmail.com>
* Copied dynapro.c and edited for Hampshire 4-byte protocol
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
#define DRIVER_DESC "Hampshire serial touchscreen driver"
MODULE_AUTHOR("Adam Bennett <abennett72@gmail.com>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
* Definitions & global arrays.
*/
#define HAMPSHIRE_FORMAT_TOUCH_BIT 0x40
#define HAMPSHIRE_FORMAT_LENGTH 4
#define HAMPSHIRE_RESPONSE_BEGIN_BYTE 0x80
#define HAMPSHIRE_MIN_XC 0
#define HAMPSHIRE_MAX_XC 0x1000
#define HAMPSHIRE_MIN_YC 0
#define HAMPSHIRE_MAX_YC 0x1000
#define HAMPSHIRE_GET_XC(data) (((data[3] & 0x0c) >> 2) | (data[1] << 2) | ((data[0] & 0x38) << 6))
#define HAMPSHIRE_GET_YC(data) ((data[3] & 0x03) | (data[2] << 2) | ((data[0] & 0x07) << 9))
#define HAMPSHIRE_GET_TOUCHED(data) (HAMPSHIRE_FORMAT_TOUCH_BIT & data[0])
/*
* Per-touchscreen data.
*/
struct hampshire {
struct input_dev *dev;
struct serio *serio;
int idx;
unsigned char data[HAMPSHIRE_FORMAT_LENGTH];
char phys[32];
};
static void hampshire_process_data(struct hampshire *phampshire)
{
struct input_dev *dev = phampshire->dev;
if (HAMPSHIRE_FORMAT_LENGTH == ++phampshire->idx) {
input_report_abs(dev, ABS_X, HAMPSHIRE_GET_XC(phampshire->data));
input_report_abs(dev, ABS_Y, HAMPSHIRE_GET_YC(phampshire->data));
input_report_key(dev, BTN_TOUCH,
HAMPSHIRE_GET_TOUCHED(phampshire->data));
input_sync(dev);
phampshire->idx = 0;
}
}
static irqreturn_t hampshire_interrupt(struct serio *serio,
unsigned char data, unsigned int flags)
{
struct hampshire *phampshire = serio_get_drvdata(serio);
phampshire->data[phampshire->idx] = data;
if (HAMPSHIRE_RESPONSE_BEGIN_BYTE & phampshire->data[0])
hampshire_process_data(phampshire);
else
dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n",
phampshire->data[0]);
return IRQ_HANDLED;
}
static void hampshire_disconnect(struct serio *serio)
{
struct hampshire *phampshire = serio_get_drvdata(serio);
input_get_device(phampshire->dev);
input_unregister_device(phampshire->dev);
serio_close(serio);
serio_set_drvdata(serio, NULL);
input_put_device(phampshire->dev);
kfree(phampshire);
}
/*
* hampshire_connect() is the routine that is called when someone adds a
* new serio device that supports hampshire protocol and registers it as
* an input device. This is usually accomplished using inputattach.
*/
static int hampshire_connect(struct serio *serio, struct serio_driver *drv)
{
struct hampshire *phampshire;
struct input_dev *input_dev;
int err;
phampshire = kzalloc(sizeof(struct hampshire), GFP_KERNEL);
input_dev = input_allocate_device();
if (!phampshire || !input_dev) {
err = -ENOMEM;
goto fail1;
}
phampshire->serio = serio;
phampshire->dev = input_dev;
snprintf(phampshire->phys, sizeof(phampshire->phys),
"%s/input0", serio->phys);
input_dev->name = "Hampshire Serial TouchScreen";
input_dev->phys = phampshire->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->id.vendor = SERIO_HAMPSHIRE;
input_dev->id.product = 0;
input_dev->id.version = 0x0001;
input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(phampshire->dev, ABS_X,
HAMPSHIRE_MIN_XC, HAMPSHIRE_MAX_XC, 0, 0);
input_set_abs_params(phampshire->dev, ABS_Y,
HAMPSHIRE_MIN_YC, HAMPSHIRE_MAX_YC, 0, 0);
serio_set_drvdata(serio, phampshire);
err = serio_open(serio, drv);
if (err)
goto fail2;
err = input_register_device(phampshire->dev);
if (err)
goto fail3;
return 0;
fail3: serio_close(serio);
fail2: serio_set_drvdata(serio, NULL);
fail1: input_free_device(input_dev);
kfree(phampshire);
return err;
}
/*
* The serio driver structure.
*/
static struct serio_device_id hampshire_serio_ids[] = {
{
.type = SERIO_RS232,
.proto = SERIO_HAMPSHIRE,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{ 0 }
};
MODULE_DEVICE_TABLE(serio, hampshire_serio_ids);
static struct serio_driver hampshire_drv = {
.driver = {
.name = "hampshire",
},
.description = DRIVER_DESC,
.id_table = hampshire_serio_ids,
.interrupt = hampshire_interrupt,
.connect = hampshire_connect,
.disconnect = hampshire_disconnect,
};
/*
* The functions for inserting/removing us as a module.
*/
static int __init hampshire_init(void)
{
return serio_register_driver(&hampshire_drv);
}
static void __exit hampshire_exit(void)
{
serio_unregister_driver(&hampshire_drv);
}
module_init(hampshire_init);
module_exit(hampshire_exit);
...@@ -347,6 +347,8 @@ static int __devexit tsc2007_remove(struct i2c_client *client) ...@@ -347,6 +347,8 @@ static int __devexit tsc2007_remove(struct i2c_client *client)
struct tsc2007 *ts = i2c_get_clientdata(client); struct tsc2007 *ts = i2c_get_clientdata(client);
struct tsc2007_platform_data *pdata = client->dev.platform_data; struct tsc2007_platform_data *pdata = client->dev.platform_data;
i2c_set_clientdata(client, NULL);
tsc2007_free_irq(ts); tsc2007_free_irq(ts);
if (pdata->exit_platform_hw) if (pdata->exit_platform_hw)
......
...@@ -811,12 +811,11 @@ static int nexio_init(struct usbtouch_usb *usbtouch) ...@@ -811,12 +811,11 @@ static int nexio_init(struct usbtouch_usb *usbtouch)
priv = usbtouch->priv; priv = usbtouch->priv;
priv->ack_buf = kmalloc(sizeof(nexio_ack_pkt), GFP_KERNEL); priv->ack_buf = kmemdup(nexio_ack_pkt, sizeof(nexio_ack_pkt),
GFP_KERNEL);
if (!priv->ack_buf) if (!priv->ack_buf)
goto err_priv; goto err_priv;
memcpy(priv->ack_buf, nexio_ack_pkt, sizeof(nexio_ack_pkt));
priv->ack = usb_alloc_urb(0, GFP_KERNEL); priv->ack = usb_alloc_urb(0, GFP_KERNEL);
if (!priv->ack) { if (!priv->ack) {
dbg("%s - usb_alloc_urb failed: usbtouch->ack", __func__); dbg("%s - usb_alloc_urb failed: usbtouch->ack", __func__);
......
...@@ -200,7 +200,7 @@ void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, ...@@ -200,7 +200,7 @@ void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
mutex_lock(&wm->codec_mutex); mutex_lock(&wm->codec_mutex);
reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS); reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
if (status & WM97XX_GPIO_HIGH) if (status == WM97XX_GPIO_HIGH)
reg |= gpio; reg |= gpio;
else else
reg &= ~gpio; reg &= ~gpio;
......
...@@ -806,6 +806,7 @@ struct input_absinfo { ...@@ -806,6 +806,7 @@ struct input_absinfo {
#define BUS_HOST 0x19 #define BUS_HOST 0x19
#define BUS_GSC 0x1A #define BUS_GSC 0x1A
#define BUS_ATARI 0x1B #define BUS_ATARI 0x1B
#define BUS_SPI 0x1C
/* /*
* MT_TOOL types * MT_TOOL types
......
/*
* include/linux/input/ad714x.h
*
* AD714x is very flexible, it can be used as buttons, scrollwheel,
* slider, touchpad at the same time. That depends on the boards.
* The platform_data for the device's "struct device" holds this
* information.
*
* Copyright 2009 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#ifndef __LINUX_INPUT_AD714X_H__
#define __LINUX_INPUT_AD714X_H__
#define STAGE_NUM 12
#define STAGE_CFGREG_NUM 8
#define SYS_CFGREG_NUM 8
/* board information which need be initialized in arch/mach... */
struct ad714x_slider_plat {
int start_stage;
int end_stage;
int max_coord;
};
struct ad714x_wheel_plat {
int start_stage;
int end_stage;
int max_coord;
};
struct ad714x_touchpad_plat {
int x_start_stage;
int x_end_stage;
int x_max_coord;
int y_start_stage;
int y_end_stage;
int y_max_coord;
};
struct ad714x_button_plat {
int keycode;
unsigned short l_mask;
unsigned short h_mask;
};
struct ad714x_platform_data {
int slider_num;
int wheel_num;
int touchpad_num;
int button_num;
struct ad714x_slider_plat *slider;
struct ad714x_wheel_plat *wheel;
struct ad714x_touchpad_plat *touchpad;
struct ad714x_button_plat *button;
unsigned short stage_cfg_reg[STAGE_NUM][STAGE_CFGREG_NUM];
unsigned short sys_cfg_reg[SYS_CFGREG_NUM];
};
#endif
...@@ -196,5 +196,6 @@ static inline void serio_continue_rx(struct serio *serio) ...@@ -196,5 +196,6 @@ static inline void serio_continue_rx(struct serio *serio)
#define SERIO_TOUCHIT213 0x38 #define SERIO_TOUCHIT213 0x38
#define SERIO_W8001 0x39 #define SERIO_W8001 0x39
#define SERIO_DYNAPRO 0x3a #define SERIO_DYNAPRO 0x3a
#define SERIO_HAMPSHIRE 0x3b
#endif #endif
...@@ -39,41 +39,34 @@ struct sysrq_key_op { ...@@ -39,41 +39,34 @@ struct sysrq_key_op {
#ifdef CONFIG_MAGIC_SYSRQ #ifdef CONFIG_MAGIC_SYSRQ
extern int sysrq_on(void);
/*
* Do not use this one directly:
*/
extern int __sysrq_enabled;
/* Generic SysRq interface -- you may call it from any device driver, supplying /* Generic SysRq interface -- you may call it from any device driver, supplying
* ASCII code of the key, pointer to registers and kbd/tty structs (if they * ASCII code of the key, pointer to registers and kbd/tty structs (if they
* are available -- else NULL's). * are available -- else NULL's).
*/ */
void handle_sysrq(int key, struct tty_struct *tty); void handle_sysrq(int key, struct tty_struct *tty);
void __handle_sysrq(int key, struct tty_struct *tty, int check_mask);
int register_sysrq_key(int key, struct sysrq_key_op *op); int register_sysrq_key(int key, struct sysrq_key_op *op);
int unregister_sysrq_key(int key, struct sysrq_key_op *op); int unregister_sysrq_key(int key, struct sysrq_key_op *op);
struct sysrq_key_op *__sysrq_get_key_op(int key); struct sysrq_key_op *__sysrq_get_key_op(int key);
int sysrq_toggle_support(int enable_mask);
#else #else
static inline int sysrq_on(void) static inline void handle_sysrq(int key, struct tty_struct *tty)
{ {
return 0;
} }
static inline int __reterr(void)
static inline int register_sysrq_key(int key, struct sysrq_key_op *op)
{ {
return -EINVAL; return -EINVAL;
} }
static inline void handle_sysrq(int key, struct tty_struct *tty)
static inline int unregister_sysrq_key(int key, struct sysrq_key_op *op)
{ {
return -EINVAL;
} }
#define register_sysrq_key(ig,nore) __reterr()
#define unregister_sysrq_key(ig,nore) __reterr()
#endif #endif
#endif /* _LINUX_SYSRQ_H */ #endif /* _LINUX_SYSRQ_H */
/*
* tca6416 keypad platform support
*
* Copyright (C) 2010 Texas Instruments
*
* Author: Sriramakrishnan <srk@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _TCA6416_KEYS_H
#define _TCA6416_KEYS_H
#include <linux/types.h>
struct tca6416_button {
/* Configuration parameters */
int code; /* input event code (KEY_*, SW_*) */
int active_low;
int type; /* input event type (EV_KEY, EV_SW) */
};
struct tca6416_keys_platform_data {
struct tca6416_button *buttons;
int nbuttons;
unsigned int rep:1; /* enable input subsystem auto repeat */
uint16_t pinmask;
uint16_t invert;
int irq_is_gpio;
int use_polling; /* use polling if Interrupt is not connected*/
};
#endif
...@@ -163,6 +163,27 @@ static int proc_taint(struct ctl_table *table, int write, ...@@ -163,6 +163,27 @@ static int proc_taint(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos); void __user *buffer, size_t *lenp, loff_t *ppos);
#endif #endif
#ifdef CONFIG_MAGIC_SYSRQ
static int __sysrq_enabled; /* Note: sysrq code ises it's own private copy */
static int sysrq_sysctl_handler(ctl_table *table, int write,
void __user *buffer, size_t *lenp,
loff_t *ppos)
{
int error;
error = proc_dointvec(table, write, buffer, lenp, ppos);
if (error)
return error;
if (write)
sysrq_toggle_support(__sysrq_enabled);
return 0;
}
#endif
static struct ctl_table root_table[]; static struct ctl_table root_table[];
static struct ctl_table_root sysctl_table_root; static struct ctl_table_root sysctl_table_root;
static struct ctl_table_header root_table_header = { static struct ctl_table_header root_table_header = {
...@@ -567,7 +588,7 @@ static struct ctl_table kern_table[] = { ...@@ -567,7 +588,7 @@ static struct ctl_table kern_table[] = {
.data = &__sysrq_enabled, .data = &__sysrq_enabled,
.maxlen = sizeof (int), .maxlen = sizeof (int),
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec, .proc_handler = sysrq_sysctl_handler,
}, },
#endif #endif
#ifdef CONFIG_PROC_SYSCTL #ifdef CONFIG_PROC_SYSCTL
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment