11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * PS/2 driver library 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999-2002 Vojtech Pavlik 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2004 Dmitry Torokhov 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify it 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * under the terms of the GNU General Public License version 2 as published by 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation. 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h> 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 16d43c36dc6b357fa1806800f18aa30123c747a6d1Alexey Dobriyan#include <linux/sched.h> 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h> 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/input.h> 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/serio.h> 20181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov#include <linux/i8042.h> 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/libps2.h> 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "PS/2 driver library" 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>"); 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("PS/2 driver library"); 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 31c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov * ps2_sendbyte() sends a byte to the device and waits for acknowledge. 32c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov * It doesn't handle retransmission, though it could - because if there 33c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov * is a need for retransmissions device has to be replaced anyway. 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 35c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov * ps2_sendbyte() can only be called from a process context. 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds serio_pause_rx(ps2dev->serio); 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2dev->nak = 1; 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2dev->flags |= PS2_FLAG_ACK; 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds serio_continue_rx(ps2dev->serio); 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (serio_write(ps2dev->serio, byte) == 0) 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wait_event_timeout(ps2dev->wait, 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds !(ps2dev->flags & PS2_FLAG_ACK), 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds msecs_to_jiffies(timeout)); 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds serio_pause_rx(ps2dev->serio); 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2dev->flags &= ~PS2_FLAG_ACK; 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds serio_continue_rx(ps2dev->serio); 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ps2dev->nak; 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 565206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_sendbyte); 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 58181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhovvoid ps2_begin_command(struct ps2dev *ps2dev) 59181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov{ 60181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov mutex_lock(&ps2dev->cmd_mutex); 61181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov 62181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov if (i8042_check_port_owner(ps2dev->serio)) 63181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov i8042_lock_chip(); 64181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov} 65181d683d752c432635eda0f182ee71548c1f1820Dmitry TorokhovEXPORT_SYMBOL(ps2_begin_command); 66181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov 67181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhovvoid ps2_end_command(struct ps2dev *ps2dev) 68181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov{ 69181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov if (i8042_check_port_owner(ps2dev->serio)) 70181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov i8042_unlock_chip(); 71181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov 72181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov mutex_unlock(&ps2dev->cmd_mutex); 73181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov} 74181d683d752c432635eda0f182ee71548c1f1820Dmitry TorokhovEXPORT_SYMBOL(ps2_end_command); 75181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 77c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov * ps2_drain() waits for device to transmit requested number of bytes 78c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov * and discards them. 79c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov */ 80c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov 81c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhovvoid ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) 82c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov{ 83c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov if (maxbytes > sizeof(ps2dev->cmdbuf)) { 84c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov WARN_ON(1); 85c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov maxbytes = sizeof(ps2dev->cmdbuf); 86c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov } 87c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov 88181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov ps2_begin_command(ps2dev); 89c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov 90c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov serio_pause_rx(ps2dev->serio); 91c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov ps2dev->flags = PS2_FLAG_CMD; 92c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov ps2dev->cmdcnt = maxbytes; 93c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov serio_continue_rx(ps2dev->serio); 94c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov 95c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov wait_event_timeout(ps2dev->wait, 96c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov !(ps2dev->flags & PS2_FLAG_CMD), 97c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov msecs_to_jiffies(timeout)); 98181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov 99181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov ps2_end_command(ps2dev); 100c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov} 1015206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_drain); 102c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov 103c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov/* 104905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * ps2_is_keyboard_id() checks received ID byte against the list of 105905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * known keyboard IDs. 106905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov */ 107905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov 1089807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhovint ps2_is_keyboard_id(char id_byte) 109905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov{ 110c5a69d57eb48e36f84c0737b5b24ec277d7dbfbaTobias Klauser static const char keyboard_ids[] = { 111905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov 0xab, /* Regular keyboards */ 112905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov 0xac, /* NCD Sun keyboard */ 113905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov 0x2b, /* Trust keyboard, translated */ 114905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov 0x5d, /* Trust keyboard */ 115905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov 0x60, /* NMB SGI keyboard, translated */ 116905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov 0x47, /* NMB SGI keyboard */ 117905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov }; 118905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov 119905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov return memchr(keyboard_ids, id_byte, sizeof(keyboard_ids)) != NULL; 120905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov} 1215206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_is_keyboard_id); 122905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov 123905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov/* 124905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * ps2_adjust_timeout() is called after receiving 1st byte of command 125905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * response and tries to reduce remaining timeout to speed up command 126905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * completion. 127905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov */ 128905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov 129905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhovstatic int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout) 130905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov{ 131905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov switch (command) { 132905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov case PS2_CMD_RESET_BAT: 133905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov /* 134905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * Device has sent the first response byte after 135905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * reset command, reset is thus done, so we can 136905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * shorten the timeout. 137905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * The next byte will come soon (keyboard) or not 138905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * at all (mouse). 139905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov */ 140905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov if (timeout > msecs_to_jiffies(100)) 141905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov timeout = msecs_to_jiffies(100); 142905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov break; 143905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov 144905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov case PS2_CMD_GETID: 145905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov /* 1469807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov * Microsoft Natural Elite keyboard responds to 1479807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov * the GET ID command as it were a mouse, with 1489807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov * a single byte. Fail the command so atkbd will 1499807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov * use alternative probe to detect it. 1509807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov */ 1519807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov if (ps2dev->cmdbuf[1] == 0xaa) { 1529807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov serio_pause_rx(ps2dev->serio); 1539807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov ps2dev->flags = 0; 1549807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov serio_continue_rx(ps2dev->serio); 1559807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov timeout = 0; 1569807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov } 1579807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov 1589807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov /* 159905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * If device behind the port is not a keyboard there 160905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * won't be 2nd byte of ID response. 161905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov */ 162905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) { 163905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov serio_pause_rx(ps2dev->serio); 164905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov ps2dev->flags = ps2dev->cmdcnt = 0; 165905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov serio_continue_rx(ps2dev->serio); 166905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov timeout = 0; 167905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov } 168905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov break; 169905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov 170905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov default: 171905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov break; 172905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov } 173905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov 174905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov return timeout; 175905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov} 176905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov 177905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov/* 1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ps2_command() sends a command and its parameters to the mouse, 1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * then waits for the response and puts it in the param array. 1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ps2_command() can only be called from a process context 1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 184fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangint __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) 1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int timeout; 1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int send = (command >> 12) & 0xf; 1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int receive = (command >> 8) & 0xf; 1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc = -1; 1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i; 1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 192c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov if (receive > sizeof(ps2dev->cmdbuf)) { 193c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov WARN_ON(1); 194c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov return -1; 195c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov } 196c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov 19795349fe8144b7d18f04bdca1c2d3fb85789de4fbDmitry Torokhov if (send && !param) { 19895349fe8144b7d18f04bdca1c2d3fb85789de4fbDmitry Torokhov WARN_ON(1); 19995349fe8144b7d18f04bdca1c2d3fb85789de4fbDmitry Torokhov return -1; 20095349fe8144b7d18f04bdca1c2d3fb85789de4fbDmitry Torokhov } 20195349fe8144b7d18f04bdca1c2d3fb85789de4fbDmitry Torokhov 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds serio_pause_rx(ps2dev->serio); 2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0; 2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2dev->cmdcnt = receive; 2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (receive && param) 2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; i < receive; i++) 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2dev->cmdbuf[(receive - 1) - i] = param[i]; 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds serio_continue_rx(ps2dev->serio); 2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Some devices (Synaptics) peform the reset before 2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ACKing the reset command, and so it can take a long 21383ae417b857072d54c3c111067046283e3ee6b58Justin P. Mattock * time before the ACK arrives. 2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 215c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov if (ps2_sendbyte(ps2dev, command & 0xff, 216c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov command == PS2_CMD_RESET_BAT ? 1000 : 200)) 217c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov goto out; 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; i < send; i++) 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ps2_sendbyte(ps2dev, param[i], 200)) 2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The reset command takes a long time to execute. 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeout = msecs_to_jiffies(command == PS2_CMD_RESET_BAT ? 4000 : 500); 2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeout = wait_event_timeout(ps2dev->wait, 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds !(ps2dev->flags & PS2_FLAG_CMD1), timeout); 2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 231a3ce6ea46cc0d6397d1b92b1a5983bb2935306edDmitry Torokhov if (ps2dev->cmdcnt && !(ps2dev->flags & PS2_FLAG_CMD1)) { 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 233905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov timeout = ps2_adjust_timeout(ps2dev, command, timeout); 2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wait_event_timeout(ps2dev->wait, 2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds !(ps2dev->flags & PS2_FLAG_CMD), timeout); 2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (param) 2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; i < receive; i++) 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds param[i] = ps2dev->cmdbuf[(receive - 1) - i]; 2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1)) 2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rc = 0; 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 247905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov out: 2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds serio_pause_rx(ps2dev->serio); 2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2dev->flags = 0; 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds serio_continue_rx(ps2dev->serio); 2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 252fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang return rc; 253fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang} 254fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa LiangEXPORT_SYMBOL(__ps2_command); 255fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang 256fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangint ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) 257fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{ 258fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang int rc; 259fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang 260181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov ps2_begin_command(ps2dev); 261fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang rc = __ps2_command(ps2dev, param, command); 262181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov ps2_end_command(ps2dev); 263fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang 2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2665206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_command); 2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ps2_init() initializes ps2dev structure 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid ps2_init(struct ps2dev *ps2dev, struct serio *serio) 2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 274c4e32e9faaaa83340dbbc00e07c48d38f032b7dcArjan van de Ven mutex_init(&ps2dev->cmd_mutex); 27588aa0103e408616e433c209e80169ab8d6eda99eJiri Kosina lockdep_set_subclass(&ps2dev->cmd_mutex, serio->depth); 2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds init_waitqueue_head(&ps2dev->wait); 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2dev->serio = serio; 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2795206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_init); 2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ps2_handle_ack() is supposed to be used in interrupt handler 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to properly process ACK/NAK of a command from a PS/2 device. 2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (data) { 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case PS2_RET_ACK: 2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2dev->nak = 0; 2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case PS2_RET_NAK: 294a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov ps2dev->flags |= PS2_FLAG_NAK; 295a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov ps2dev->nak = PS2_RET_NAK; 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 298a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov case PS2_RET_ERR: 299a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov if (ps2dev->flags & PS2_FLAG_NAK) { 300a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov ps2dev->flags &= ~PS2_FLAG_NAK; 301a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov ps2dev->nak = PS2_RET_ERR; 302a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov break; 303a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov } 304a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Workaround for mice which don't ACK the Get ID command. 3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * These are valid mouse IDs that we recognize. 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 0x00: 3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 0x03: 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 0x04: 3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ps2dev->flags & PS2_FLAG_WAITID) { 3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2dev->nak = 0; 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Fall through */ 3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 322a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov if (!ps2dev->nak) { 323a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov ps2dev->flags &= ~PS2_FLAG_NAK; 324a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov if (ps2dev->cmdcnt) 325a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1; 326a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov } 3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2dev->flags &= ~PS2_FLAG_ACK; 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wake_up(&ps2dev->wait); 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data != PS2_RET_ACK) 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2_handle_response(ps2dev, data); 3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 1; 3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3365206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_handle_ack); 3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ps2_handle_response() is supposed to be used in interrupt handler 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to properly store device's response to a command and notify process 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * waiting for completion of the command. 3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint ps2_handle_response(struct ps2dev *ps2dev, unsigned char data) 3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ps2dev->cmdcnt) 3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2dev->cmdbuf[--ps2dev->cmdcnt] = data; 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ps2dev->flags & PS2_FLAG_CMD1) { 3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2dev->flags &= ~PS2_FLAG_CMD1; 3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ps2dev->cmdcnt) 3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wake_up(&ps2dev->wait); 3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ps2dev->cmdcnt) { 3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2dev->flags &= ~PS2_FLAG_CMD; 3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wake_up(&ps2dev->wait); 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 1; 3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3625206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_handle_response); 3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid ps2_cmd_aborted(struct ps2dev *ps2dev) 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ps2dev->flags & PS2_FLAG_ACK) 3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ps2dev->nak = 1; 3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD)) 3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wake_up(&ps2dev->wait); 3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 372a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov /* reset all flags except last nack */ 373a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov ps2dev->flags &= PS2_FLAG_NAK; 3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3755206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_cmd_aborted); 376