Re: psaux driver fixes

Roman Hodek (rnhodek@faui21j.informatik.uni-erlangen.de)
Fri, 23 Aug 96 11:08:07 +0200


Oh no! Another version of that psaux driver... :-)

Since Linus didn't like my psaux patch from yesterday as a whole, I've
split it into two parts: One that ONLY fixes the kbd lockups in a new
way (since I've forgotten the kbd bh's, but the kbd intr doesn't need
disabling anymore). This patch is as minimal as possible :-)

The second patch is the rest of yesterday's version (and relative to
the first one): It implements handling of RESEND requests from the
mouse, waiting for ACKs (if they'll arrive at all), and error
handling. This is more or less optional, since RESENDs seem to occur
rather rarely (except on my notebook... :-). Don't know if Linus wants
to apply that patch, maybe later in 2.1. Just remember that it
exists... :-)

Roman

Note: TWO patches appended...

------------------------------ first patch ---------------------------------

--- linux-2.0.14/drivers/char/psaux.c.orig Thu Aug 22 14:34:52 1996
+++ linux-2.0.14/drivers/char/psaux.c Fri Aug 23 09:42:24 1996
@@ -25,6 +25,9 @@
* Rearranged SIGIO support to use code from tty_io. 9Sept95 ctm@ardi.com
*
* Modularised 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
+ *
+ * Fixed keyboard lockups at open time
+ * 3-Jul-96, 22-Aug-96 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
*/

/* Uncomment the following line if your mouse needs initialization. */
@@ -35,6 +38,7 @@

#include <linux/sched.h>
#include <linux/kernel.h>
+#include <linux/interrupt.h>
#include <linux/fcntl.h>
#include <linux/errno.h>
#include <linux/timer.h>
@@ -258,10 +262,14 @@
fasync_aux(inode, file, 0);
if (--aux_count)
return;
+ /* disable kbd bh to avoid mixing of cmd bytes */
+ disable_bh(KEYBOARD_BH);
aux_write_cmd(AUX_INTS_OFF); /* disable controller ints */
poll_aux_status();
outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */
poll_aux_status();
+ /* reenable kbd bh */
+ enable_bh(KEYBOARD_BH);
free_irq(AUX_IRQ, NULL);
MOD_DEC_USE_COUNT;
}
@@ -316,11 +324,16 @@
return -EBUSY;
}
MOD_INC_USE_COUNT;
+ /* disable kbd bh to avoid mixing of cmd bytes */
+ disable_bh(KEYBOARD_BH);
poll_aux_status();
outb_p(AUX_ENABLE,AUX_COMMAND); /* 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_ready = 0;
return 0;
}
@@ -377,18 +390,28 @@

static int write_aux(struct inode * inode, struct file * file, const char * buffer, int count)
{
- int i = count;
+ int i = count, rv = 0;
+
+ /* disable kbd bh to avoid mixing of cmd bytes */
+ disable_bh(KEYBOARD_BH);

while (i--) {
- if (!poll_aux_status())
- return -EIO;
+ if (!poll_aux_status()) {
+ rv = -EIO;
+ break;
+ }
outb_p(AUX_MAGIC_WRITE,AUX_COMMAND);
- if (!poll_aux_status())
- return -EIO;
+ if (!poll_aux_status()) {
+ rv = -EIO;
+ break;
+ }
outb_p(get_user(buffer++),AUX_OUTPUT_PORT);
}
+ /* reenable kbd bh */
+ enable_bh(KEYBOARD_BH);
+
inode->i_mtime = CURRENT_TIME;
- return count;
+ return rv ? rv : count;
}

----------------------------- second patch ---------------------------------

--- linux-2.0.14/drivers/char/psaux.c.lockup-only Fri Aug 23 09:42:24 1996
+++ linux-2.0.14/drivers/char/psaux.c Fri Aug 23 09:46:59 1996
@@ -28,6 +28,10 @@
*
* Fixed keyboard lockups at open time
* 3-Jul-96, 22-Aug-96 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ *
+ * Handle RESEND replies, better error checking, dynamic detection whether the
+ * mouse sends ACKs at all.
+ * 7-Aug-96, 22-Aug-96 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
*/

/* Uncomment the following line if your mouse needs initialization. */
@@ -61,8 +65,10 @@
#define AUX_STATUS 0x64 /* Aux device status reg */

/* aux controller status bits */
+#define AUX_KOBUF_FULL 0x01 /* output buffer (from controller) full */
#define AUX_OBUF_FULL 0x21 /* output buffer (from device) full */
#define AUX_IBUF_FULL 0x02 /* input buffer (to device) full */
+#define AUX_TIMEOUT 0x40 /* controller reports timeout */

/* aux controller commands */
#define AUX_CMD_WRITE 0x60 /* value to write to controller */
@@ -85,6 +91,14 @@
#define AUX_DISABLE_DEV 0xf5 /* disable aux device */
#define AUX_RESET 0xff /* reset aux device */

+/* kbd controller commands */
+#define KBD_DISABLE 0xad
+#define KBD_ENABLE 0xae
+
+/* replies */
+#define AUX_ACK 0xfa
+#define AUX_RESEND 0xfe
+
#define MAX_RETRIES 60 /* some aux operations take long time*/
#if defined(__alpha__) && !defined(CONFIG_PCI)
# define AUX_IRQ 9 /* Jensen is odd indeed */
@@ -124,8 +138,10 @@
static int aux_ready = 0;
static int aux_count = 0;
static int aux_present = 0;
+static enum {
+ SENDACK_dont_know, SENDACK_yes, SENDACK_probably_no, SENDACK_no
+} aux_sends_ack;
static int poll_aux_status(void);
-static int poll_aux_status_nosleep(void);
static int fasync_aux(struct inode *inode, struct file *filp, int on);

#ifdef CONFIG_82C710_MOUSE
@@ -140,49 +156,110 @@


/*
- * Write to aux device
+ * Write a byte to the kbd controller and wait for it being processed
*/

-static void aux_write_dev(int val)
+static int aux_write_byte(int val,int port)
{
- poll_aux_status();
- outb_p(AUX_MAGIC_WRITE,AUX_COMMAND); /* write magic cookie */
- poll_aux_status();
- outb_p(val,AUX_OUTPUT_PORT); /* write data */
+ outb_p(val, port);
+ return poll_aux_status();
}

/*
- * Write to device & handle returned ack
+ * Write to device, handle returned resend requests and wait for ack
+ *
+ * Dynamic detection whether mouse sends ACKs: 'psaux_sends_ack' starts in
+ * 'dont_know' state. If an ACK is received, it switches to 'yes' and missing
+ * ACKs result in an error in future. If no ACK arrives in 'dont_know' state,
+ * we go to 'probably_no' and reduce the time to wait for it, since it's
+ * rather unprobable that it was missing due to an transmission error, and
+ * waiting too long would be wasting time. If the next ACK misses, too, we go
+ * to a definitive 'no' state. In each state except 'yes', missing ACKs don't
+ * count as an error.
*/
-#if defined INITIALIZE_DEVICE
static int aux_write_ack(int val)
{
- int retries = 0;
+ int rv;

- poll_aux_status_nosleep();
- outb_p(AUX_MAGIC_WRITE,AUX_COMMAND);
- poll_aux_status_nosleep();
- outb_p(val,AUX_OUTPUT_PORT);
- poll_aux_status_nosleep();
-
- if ((inb(AUX_STATUS) & AUX_OBUF_FULL) == AUX_OBUF_FULL)
- {
- return (inb(AUX_INPUT_PORT));
+ repeat:
+ if (poll_aux_status() < 0)
+ return -1;
+ outb_p(AUX_MAGIC_WRITE, AUX_COMMAND);
+ if (poll_aux_status() < 0)
+ return -1;
+ outb_p(val, AUX_OUTPUT_PORT);
+
+ if ((rv = poll_aux_status()) < 0)
+ /* timeout */
+ return -1;
+ else if (rv == AUX_RESEND)
+ /* controller needs last byte again... */
+ goto repeat;
+ else if (rv == AUX_ACK) {
+ /* already got ACK */
+ aux_sends_ack = SENDACK_yes;
+ return 0;
+ }
+ else {
+ int max_retries, retries = 0, stat;
+
+ /* don't even try to wait for an ACK if we surely know it
+ * won't arrive... */
+ if (aux_sends_ack == SENDACK_no)
+ return 0;
+ /* if we don't really expect an ACK anymore, reduce the time
+ * to wait for it */
+ max_retries = MAX_RETRIES;
+ if (aux_sends_ack == SENDACK_probably_no)
+ max_retries /= 2;
+
+ /* wait for ACK from controller */
+ while (retries < max_retries) {
+ stat = inb_p(AUX_STATUS);
+ if ((stat & AUX_OBUF_FULL) == AUX_OBUF_FULL &&
+ inb_p(AUX_INPUT_PORT) == AUX_ACK) {
+ /* ok, now we know there are ACKs */
+ aux_sends_ack = SENDACK_yes;
+ return 0;
+ }
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + (5*HZ + 99) / 100;
+ schedule();
+ retries++;
+ }
+ switch( aux_sends_ack ) {
+ case SENDACK_yes:
+ /* If we know this mouse to sends ACKs, it's an error
+ * if it's missing */
+ return -1;
+ case SENDACK_dont_know:
+ /* seems we don't get ACKs... no error */
+ aux_sends_ack = SENDACK_probably_no;
+ return 0;
+ case SENDACK_probably_no:
+ default:
+ /* now it's for sure there're no ACKs */
+ aux_sends_ack = SENDACK_no;
+ return 0;
+ }
}
- return 0;
}
-#endif /* INITIALIZE_DEVICE */

/*
* Write aux device command
*/

-static void aux_write_cmd(int val)
+static int aux_write_cmd(int val)
{
- poll_aux_status();
- outb_p(AUX_CMD_WRITE,AUX_COMMAND);
- poll_aux_status();
- outb_p(val,AUX_OUTPUT_PORT);
+ if (poll_aux_status() < 0)
+ return -1;
+ outb_p(AUX_CMD_WRITE, AUX_COMMAND);
+ if (poll_aux_status() < 0)
+ return -1;
+ outb_p(val, AUX_OUTPUT_PORT);
+ if (poll_aux_status() < 0)
+ return -1;
+ return 0;
}


@@ -264,10 +341,12 @@
return;
/* disable kbd bh to avoid mixing of cmd bytes */
disable_bh(KEYBOARD_BH);
- aux_write_cmd(AUX_INTS_OFF); /* disable controller ints */
- poll_aux_status();
- outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */
- poll_aux_status();
+ /* disable controller ints */
+ if (aux_write_cmd(AUX_INTS_OFF) < 0)
+ printk(KERN_ERR "psaux: controller timeout\n");
+ /* Disable Aux device */
+ if (aux_write_byte(AUX_DISABLE, AUX_COMMAND) < 0)
+ printk(KERN_ERR "psaux: controller timeout\n");
/* reenable kbd bh */
enable_bh(KEYBOARD_BH);
free_irq(AUX_IRQ, NULL);
@@ -314,7 +393,7 @@
return -ENODEV;
if (aux_count++)
return 0;
- if (!poll_aux_status()) {
+ if (poll_aux_status() < 0) {
aux_count--;
return -EBUSY;
}
@@ -326,16 +405,25 @@
MOD_INC_USE_COUNT;
/* disable kbd bh to avoid mixing of cmd bytes */
disable_bh(KEYBOARD_BH);
- poll_aux_status();
- outb_p(AUX_ENABLE,AUX_COMMAND); /* Enable Aux */
- aux_write_dev(AUX_ENABLE_DEV); /* enable aux device */
- aux_write_cmd(AUX_INTS_ON); /* enable controller ints */
- poll_aux_status();
+ /* Enable Aux in kbd controller */
+ if (aux_write_byte(AUX_ENABLE, AUX_COMMAND) < 0)
+ goto open_error;
+ /* enable aux device */
+ if (aux_write_ack(AUX_ENABLE_DEV) < 0)
+ goto open_error;
+ /* enable controller ints */
+ if (aux_write_cmd(AUX_INTS_ON) < 0)
+ goto open_error;
/* reenable kbd bh */
enable_bh(KEYBOARD_BH);

aux_ready = 0;
return 0;
+ open_error:
+ printk( KERN_ERR "psaux: controller timeout\n" );
+ /* reenable kbd bh on errors */
+ enable_bh(KEYBOARD_BH);
+ return -EIO;
}

#ifdef CONFIG_82C710_MOUSE
@@ -396,12 +484,12 @@
disable_bh(KEYBOARD_BH);

while (i--) {
- if (!poll_aux_status()) {
+ if (poll_aux_status() < 0) {
rv = -EIO;
break;
}
outb_p(AUX_MAGIC_WRITE,AUX_COMMAND);
- if (!poll_aux_status()) {
+ if (poll_aux_status() < 0) {
rv = -EIO;
break;
}
@@ -535,6 +623,8 @@
queue->head = queue->tail = 0;
queue->proc_list = NULL;
if (!qp_found) {
+ /* don't know yet whether this mouse sends ACKs or not */
+ aux_sends_ack = SENDACK_dont_know;
#if defined INITIALIZE_DEVICE
outb_p(AUX_ENABLE,AUX_COMMAND); /* Enable Aux */
aux_write_ack(AUX_SET_SAMPLE);
@@ -542,13 +632,11 @@
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();
#endif /* INITIALIZE_DEVICE */
- outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */
- poll_aux_status_nosleep();
- outb_p(AUX_CMD_WRITE,AUX_COMMAND);
- poll_aux_status_nosleep(); /* Disable interrupts */
- outb_p(AUX_INTS_OFF, AUX_OUTPUT_PORT); /* on the controller */
+ /* Disable Aux device and its interrupts on the controller */
+ if (aux_write_byte(AUX_DISABLE, AUX_COMMAND) < 0 ||
+ aux_write_cmd(AUX_INTS_OFF) < 0)
+ printk(KERN_ERR "psaux: controller timeout\n");
}
return 0;
}
@@ -556,7 +644,7 @@
#ifdef MODULE
int init_module(void)
{
- return psaux_init(); /*?? Bjorn */
+ return psaux_init();
}

void cleanup_module(void)
@@ -568,28 +656,18 @@
static int poll_aux_status(void)
{
int retries=0;
+ int reply=0;

- while ((inb(AUX_STATUS)&0x03) && retries < MAX_RETRIES) {
+ while ((inb(AUX_STATUS) & (AUX_KOBUF_FULL|AUX_IBUF_FULL)) &&
+ retries < MAX_RETRIES) {
if ((inb_p(AUX_STATUS) & AUX_OBUF_FULL) == AUX_OBUF_FULL)
- inb_p(AUX_INPUT_PORT);
+ reply = inb_p(AUX_INPUT_PORT);
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + (5*HZ + 99) / 100;
schedule();
retries++;
}
- return !(retries==MAX_RETRIES);
-}
-
-static int poll_aux_status_nosleep(void)
-{
- int retries = 0;
-
- while ((inb(AUX_STATUS)&0x03) && retries < 1000000) {
- if ((inb_p(AUX_STATUS) & AUX_OBUF_FULL) == AUX_OBUF_FULL)
- inb_p(AUX_INPUT_PORT);
- retries++;
- }
- return !(retries == 1000000);
+ return (retries==MAX_RETRIES) ? -1 : reply;
}

#ifdef CONFIG_82C710_MOUSE