PSAUX

Martin Mares (mj@mj.gts.cz)
Thu, 17 Jul 1997 14:22:21 +0200


Hi,

The following patch should finally fix all PSAUX problems [Famous last
words :)]. It fixes lots of race conditions in the driver and cleans
many things up...

Have a nice fortnight

-- 
Martin `MJ' Mares   <mj@gts.cz>   http://atrey.karlin.mff.cuni.cz/~mj/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
"IBM = Inferior, But Marketable"

--- psaux.c Thu Jul 17 14:10:14 1997 +++ /home/mj/linux/drivers/char/psaux.c Thu Jul 17 14:13:51 1997 @@ -53,6 +53,7 @@ #include <asm/io.h> #include <asm/uaccess.h> #include <asm/system.h> +#include <asm/semaphore.h> #include <linux/config.h> @@ -141,19 +142,7 @@ schedule(); retries++; } - return !(retries==MAX_RETRIES); -} - -static int poll_aux_status_nosleep(void) -{ - int retries = 0; - - while ((inb(KBD_STATUS_REG) & (KBD_STAT_IBF | KBD_STAT_OBF)) && retries < 1000000) { - if ((inb_p(KBD_STATUS_REG) & AUX_STAT_OBF) == AUX_STAT_OBF) - inb_p(KBD_DATA_REG); - retries++; - } - return !(retries == 1000000); + return (retries < MAX_RETRIES); } /* @@ -173,18 +162,10 @@ */ #ifdef INITIALIZE_DEVICE -__initfunc(static void aux_write_dev_nosleep(int val)) -{ - poll_aux_status_nosleep(); - outb_p(KBD_CCMD_WRITE_MOUSE, KBD_CNTL_REG); - poll_aux_status_nosleep(); - outb_p(val, KBD_DATA_REG); -} - __initfunc(static int aux_write_ack(int val)) { - aux_write_dev_nosleep(val); - poll_aux_status_nosleep(); + aux_write_dev(val); + poll_aux_status(); if ((inb(KBD_STATUS_REG) & AUX_STAT_OBF) == AUX_STAT_OBF) { @@ -207,6 +188,31 @@ } /* + * AUX handler critical section start and end. + * + * Only one process can be in the critical section and all keyboard sends are + * deferred as long as we're inside. This is necessary as we may sleep when + * waiting for the keyboard controller and other processes / BH's can + * preempt us. Please note that the input buffer must be flushed when + * aux_end_atomic() is called and the interrupt is no longer enabled as not + * doing so might cause the keyboard driver to ignore all incoming keystrokes. + */ + +static struct semaphore aux_sema4 = MUTEX; + +static inline void aux_start_atomic(void) +{ + down(&aux_sema4); + disable_bh(KEYBOARD_BH); +} + +static inline void aux_end_atomic(void) +{ + enable_bh(KEYBOARD_BH); + up(&aux_sema4); +} + +/* * Interrupt from the auxiliary device: a character * is waiting in the keyboard/aux controller. */ @@ -236,14 +242,12 @@ fasync_aux(inode, file, 0); if (--aux_count) return 0; - /* disable kbd bh to avoid mixing of cmd bytes */ - disable_bh(KEYBOARD_BH); + aux_start_atomic(); aux_write_cmd(AUX_INTS_OFF); /* Disable controller ints */ poll_aux_status(); outb_p(KBD_CCMD_MOUSE_DISABLE, KBD_CNTL_REG); /* Disable Aux device */ poll_aux_status(); - /* reenable kbd bh */ - enable_bh(KEYBOARD_BH); + aux_end_atomic(); #ifdef CONFIG_MCA free_irq(AUX_IRQ, inode); #else @@ -262,10 +266,14 @@ { if (!aux_present) return -ENODEV; - if (aux_count++) + aux_start_atomic(); + if (aux_count++) { + aux_end_atomic(); return 0; - if (!poll_aux_status()) { + } + if (!poll_aux_status()) { /* FIXME: Race condition */ aux_count--; + aux_end_atomic(); return -EBUSY; } queue->head = queue->tail = 0; /* Flush input queue */ @@ -275,18 +283,16 @@ if (request_irq(AUX_IRQ, aux_interrupt, 0, "PS/2 Mouse", NULL)) { #endif aux_count--; + aux_end_atomic(); return -EBUSY; } MOD_INC_USE_COUNT; - /* disable kbd bh to avoid mixing of cmd bytes */ - disable_bh(KEYBOARD_BH); poll_aux_status(); outb_p(KBD_CCMD_MOUSE_ENABLE, KBD_CNTL_REG); /* Enable Aux */ aux_write_dev(AUX_ENABLE_DEV); /* Enable aux device */ aux_write_cmd(AUX_INTS_ON); /* Enable controller ints */ poll_aux_status(); - /* reenable kbd bh */ - enable_bh(KEYBOARD_BH); + aux_end_atomic(); aux_ready = 0; return 0; @@ -304,9 +310,7 @@ if (count) { int written = 0; - /* disable kbd bh to avoid mixing of cmd bytes */ - disable_bh(KEYBOARD_BH); - + aux_start_atomic(); do { char c; if (!poll_aux_status()) @@ -318,8 +322,7 @@ outb_p(c, KBD_DATA_REG); written++; } while (--count); - /* reenable kbd bh */ - enable_bh(KEYBOARD_BH); + aux_end_atomic(); retval = -EIO; if (written) { retval = written; @@ -618,6 +621,7 @@ queue->head = queue->tail = 0; queue->proc_list = NULL; if (!qp_found) { + aux_start_atomic(); #ifdef INITIALIZE_DEVICE outb_p(KBD_CCMD_MOUSE_ENABLE, KBD_CNTL_REG); /* Enable Aux */ aux_write_ack(AUX_SET_SAMPLE); @@ -625,13 +629,15 @@ aux_write_ack(AUX_SET_RES); aux_write_ack(3); /* 8 counts per mm */ aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */ - poll_aux_status_nosleep(); + poll_aux_status(); #endif /* INITIALIZE_DEVICE */ outb_p(KBD_CCMD_MOUSE_DISABLE, KBD_CNTL_REG); /* Disable Aux device */ - poll_aux_status_nosleep(); - outb_p(KBD_CCMD_WRITE_MODE, KBD_CNTL_REG); - poll_aux_status_nosleep(); - outb_p(AUX_INTS_OFF, KBD_DATA_REG); + poll_aux_status(); + outb_p(KBD_CCMD_WRITE_MODE, KBD_CNTL_REG); /* Disable controller interrupts */ + poll_aux_status(); + outb_p(AUX_INTS_OFF, KBD_DATA_REG); + poll_aux_status(); + aux_end_atomic(); } return 0; }