aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/serio/i8042.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-12-16 13:31:44 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-12-16 13:31:44 -0500
commit238ccbb050a243e935bb3fc679c2e4bbff7004aa (patch)
tree3381b895861f625b1524acfd32d4a90eb79108bd /drivers/input/serio/i8042.c
parentc5113e3d66d7c7140fe854c7638a27eb3a23fd7d (diff)
parent1d9f26262aef6d63ff65eba0fd5f1583f342b69b (diff)
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: (22 commits) Input: ALPS - add interleaved protocol support (Dell E6x00 series) Input: keyboard - don't override beep with a bell Input: altera_ps2 - fix test of unsigned in altera_ps2_probe() Input: add mc13783 touchscreen driver Input: ep93xx_keypad - update driver to new core support Input: wacom - separate pen from express keys on Graphire Input: wacom - add defines for data packet report IDs Input: wacom - add support for new LCD tablets Input: wacom - add defines for packet lengths of various devices Input: wacom - ensure the device is initialized properly upon resume Input: at32psif - do not sleep in atomic context Input: i8042 - add Gigabyte M1022M to the noloop list Input: i8042 - allow installing platform filters for incoming data Input: i8042 - fix locking in interrupt routine Input: ALPS - do not set REL_X/REL_Y capabilities on the touchpad Input: document use of input_event() function Input: sa1111ps2 - annotate probe() and remove() methods Input: ambakmi - annotate probe() and remove() methods Input: gscps2 - fix probe() and remove() annotations Input: altera_ps2 - add annotations to probe and remove methods ...
Diffstat (limited to 'drivers/input/serio/i8042.c')
-rw-r--r--drivers/input/serio/i8042.c88
1 files changed, 79 insertions, 9 deletions
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 1df02d25aca5..d84a36e545f6 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -126,6 +126,8 @@ static unsigned char i8042_suppress_kbd_ack;
126static struct platform_device *i8042_platform_device; 126static struct platform_device *i8042_platform_device;
127 127
128static irqreturn_t i8042_interrupt(int irq, void *dev_id); 128static irqreturn_t i8042_interrupt(int irq, void *dev_id);
129static bool (*i8042_platform_filter)(unsigned char data, unsigned char str,
130 struct serio *serio);
129 131
130void i8042_lock_chip(void) 132void i8042_lock_chip(void)
131{ 133{
@@ -139,6 +141,48 @@ void i8042_unlock_chip(void)
139} 141}
140EXPORT_SYMBOL(i8042_unlock_chip); 142EXPORT_SYMBOL(i8042_unlock_chip);
141 143
144int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
145 struct serio *serio))
146{
147 unsigned long flags;
148 int ret = 0;
149
150 spin_lock_irqsave(&i8042_lock, flags);
151
152 if (i8042_platform_filter) {
153 ret = -EBUSY;
154 goto out;
155 }
156
157 i8042_platform_filter = filter;
158
159out:
160 spin_unlock_irqrestore(&i8042_lock, flags);
161 return ret;
162}
163EXPORT_SYMBOL(i8042_install_filter);
164
165int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
166 struct serio *port))
167{
168 unsigned long flags;
169 int ret = 0;
170
171 spin_lock_irqsave(&i8042_lock, flags);
172
173 if (i8042_platform_filter != filter) {
174 ret = -EINVAL;
175 goto out;
176 }
177
178 i8042_platform_filter = NULL;
179
180out:
181 spin_unlock_irqrestore(&i8042_lock, flags);
182 return ret;
183}
184EXPORT_SYMBOL(i8042_remove_filter);
185
142/* 186/*
143 * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to 187 * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
144 * be ready for reading values from it / writing values to it. 188 * be ready for reading values from it / writing values to it.
@@ -369,6 +413,31 @@ static void i8042_stop(struct serio *serio)
369} 413}
370 414
371/* 415/*
416 * i8042_filter() filters out unwanted bytes from the input data stream.
417 * It is called from i8042_interrupt and thus is running with interrupts
418 * off and i8042_lock held.
419 */
420static bool i8042_filter(unsigned char data, unsigned char str,
421 struct serio *serio)
422{
423 if (unlikely(i8042_suppress_kbd_ack)) {
424 if ((~str & I8042_STR_AUXDATA) &&
425 (data == 0xfa || data == 0xfe)) {
426 i8042_suppress_kbd_ack--;
427 dbg("Extra keyboard ACK - filtered out\n");
428 return true;
429 }
430 }
431
432 if (i8042_platform_filter && i8042_platform_filter(data, str, serio)) {
433 dbg("Filtered out by platfrom filter\n");
434 return true;
435 }
436
437 return false;
438}
439
440/*
372 * i8042_interrupt() is the most important function in this driver - 441 * i8042_interrupt() is the most important function in this driver -
373 * it handles the interrupts from the i8042, and sends incoming bytes 442 * it handles the interrupts from the i8042, and sends incoming bytes
374 * to the upper layers. 443 * to the upper layers.
@@ -377,13 +446,16 @@ static void i8042_stop(struct serio *serio)
377static irqreturn_t i8042_interrupt(int irq, void *dev_id) 446static irqreturn_t i8042_interrupt(int irq, void *dev_id)
378{ 447{
379 struct i8042_port *port; 448 struct i8042_port *port;
449 struct serio *serio;
380 unsigned long flags; 450 unsigned long flags;
381 unsigned char str, data; 451 unsigned char str, data;
382 unsigned int dfl; 452 unsigned int dfl;
383 unsigned int port_no; 453 unsigned int port_no;
454 bool filtered;
384 int ret = 1; 455 int ret = 1;
385 456
386 spin_lock_irqsave(&i8042_lock, flags); 457 spin_lock_irqsave(&i8042_lock, flags);
458
387 str = i8042_read_status(); 459 str = i8042_read_status();
388 if (unlikely(~str & I8042_STR_OBF)) { 460 if (unlikely(~str & I8042_STR_OBF)) {
389 spin_unlock_irqrestore(&i8042_lock, flags); 461 spin_unlock_irqrestore(&i8042_lock, flags);
@@ -391,8 +463,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
391 ret = 0; 463 ret = 0;
392 goto out; 464 goto out;
393 } 465 }
466
394 data = i8042_read_data(); 467 data = i8042_read_data();
395 spin_unlock_irqrestore(&i8042_lock, flags);
396 468
397 if (i8042_mux_present && (str & I8042_STR_AUXDATA)) { 469 if (i8042_mux_present && (str & I8042_STR_AUXDATA)) {
398 static unsigned long last_transmit; 470 static unsigned long last_transmit;
@@ -441,21 +513,19 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
441 } 513 }
442 514
443 port = &i8042_ports[port_no]; 515 port = &i8042_ports[port_no];
516 serio = port->exists ? port->serio : NULL;
444 517
445 dbg("%02x <- i8042 (interrupt, %d, %d%s%s)", 518 dbg("%02x <- i8042 (interrupt, %d, %d%s%s)",
446 data, port_no, irq, 519 data, port_no, irq,
447 dfl & SERIO_PARITY ? ", bad parity" : "", 520 dfl & SERIO_PARITY ? ", bad parity" : "",
448 dfl & SERIO_TIMEOUT ? ", timeout" : ""); 521 dfl & SERIO_TIMEOUT ? ", timeout" : "");
449 522
450 if (unlikely(i8042_suppress_kbd_ack)) 523 filtered = i8042_filter(data, str, serio);
451 if (port_no == I8042_KBD_PORT_NO && 524
452 (data == 0xfa || data == 0xfe)) { 525 spin_unlock_irqrestore(&i8042_lock, flags);
453 i8042_suppress_kbd_ack--;
454 goto out;
455 }
456 526
457 if (likely(port->exists)) 527 if (likely(port->exists && !filtered))
458 serio_interrupt(port->serio, data, dfl); 528 serio_interrupt(serio, data, dfl);
459 529
460 out: 530 out:
461 return IRQ_RETVAL(ret); 531 return IRQ_RETVAL(ret);