Messages in this thread Patch in this message | | | Date | Thu, 22 Aug 96 11:11:52 +0200 | From | Roman Hodek <> | Subject | psaux driver fixes |
| |
Here is a new version of my revised psaux driver (it mainly fixes keyboard lockups when opening the device, and does better error checking).
Compared to the last version I posted here (that already was in 2.0.8, but then removed again due to problems), it now correctly handles PS/2 mice that do NOT send any acknowledges. This seems to be the case with some notebook built-in mice... It now works, too, for the people that have reported problems with the former try. So, the patch should be ready now for releasing it to the public... :-)
Though I've built the patch below relative to 2.0.11, it also cleanly goes into 2.0.14 (nothing changed in psaux.c ...)
Roman
--------------------------------------------------------------------------- --- linux-2.0.11/drivers/char/psaux.c~ Tue Aug 6 08:36:02 1996 +++ linux-2.0.11/drivers/char/psaux.c Thu Aug 8 09:18:09 1996 @@ -25,6 +25,14 @@ * 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 (intervening kbd interrupts), handle + * RESEND replies, better error checking + * 3-Jul-96 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> + * + * Error checking adapted for dynamically detecting whether the mouse sends + * ACKs at all. + * 7-Aug-96 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */ /* Uncomment the following line if your mouse needs initialization. */ @@ -57,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 */ @@ -81,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 */ @@ -120,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 @@ -136,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; } @@ -258,10 +339,19 @@ fasync_aux(inode, file, 0); if (--aux_count) return; - aux_write_cmd(AUX_INTS_OFF); /* disable controller ints */ - poll_aux_status(); - outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */ + /* disable keyboard to avoid clashes with multi-byte command sequences */ poll_aux_status(); + if (aux_write_byte(KBD_DISABLE, AUX_COMMAND) < 0) + printk(KERN_ERR "psaux: controller timeout\n"); + /* 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"); + /* re-enable keyboard */ + if (aux_write_byte(KBD_ENABLE, AUX_COMMAND) < 0) + printk(KERN_ERR "psaux: controller timeout\n"); free_irq(AUX_IRQ, NULL); MOD_DEC_USE_COUNT; } @@ -306,7 +396,7 @@ return -ENODEV; if (aux_count++) return 0; - if (!poll_aux_status()) { + if (poll_aux_status() < 0) { aux_count--; return -EBUSY; } @@ -317,12 +407,29 @@ } MOD_INC_USE_COUNT; 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(); + /* disable keyboard to avoid clashes with multi-byte command sequences */ + if (aux_write_byte(KBD_DISABLE, AUX_COMMAND) < 0) + goto open_error; + /* 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; + /* re-enable keyboard */ + if (aux_write_byte(KBD_ENABLE, AUX_COMMAND) < 0) + goto open_error; + aux_ready = 0; return 0; + open_error: + printk( KERN_ERR "psaux: controller timeout\n" ); + /* re-enable the keyboard on errors */ + aux_write_byte(KBD_ENABLE, AUX_COMMAND); + return -EIO; } #ifdef CONFIG_82C710_MOUSE @@ -378,17 +485,31 @@ static int write_aux(struct inode * inode, struct file * file, const char * buffer, int count) { int i = count; + int rv = 0; + + /* temporary disable keyboard to avoid clashes with multi-byte command + * sequence */ + if (aux_write_byte(KBD_DISABLE, AUX_COMMAND) < 0) + return -EIO; while (i--) { - if (!poll_aux_status()) - return -EIO; + if (poll_aux_status() < 0) { + rv = -EIO; + break; + } outb_p(AUX_MAGIC_WRITE,AUX_COMMAND); - if (!poll_aux_status()) - return -EIO; + if (poll_aux_status() < 0) { + rv = -EIO; + break; + } outb_p(get_user(buffer++),AUX_OUTPUT_PORT); } + /* reenable keyboard */ + if (poll_aux_status() < 0 || aux_write_byte(KBD_ENABLE, AUX_COMMAND) < 0) + rv = -EIO; + inode->i_mtime = CURRENT_TIME; - return count; + return rv ? rv : count; } @@ -512,6 +633,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); @@ -519,13 +642,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; } @@ -533,7 +654,7 @@ #ifdef MODULE int init_module(void) { - return psaux_init(); /*?? Bjorn */ + return psaux_init(); } void cleanup_module(void) @@ -545,28 +666,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
| |