1e342083cSJohn Floren #include "kvm/read-write.h" 2e342083cSJohn Floren #include "kvm/ioport.h" 3e342083cSJohn Floren #include "kvm/mutex.h" 4e342083cSJohn Floren #include "kvm/util.h" 5e342083cSJohn Floren #include "kvm/term.h" 6e342083cSJohn Floren #include "kvm/kvm.h" 7e342083cSJohn Floren #include "kvm/i8042.h" 8714e5b7fSSasha Levin #include "kvm/kvm-cpu.h" 9e342083cSJohn Floren 10e342083cSJohn Floren #include <stdint.h> 11e342083cSJohn Floren 1267307cddSPekka Enberg /* 1367307cddSPekka Enberg * IRQs 1467307cddSPekka Enberg */ 15e342083cSJohn Floren #define KBD_IRQ 1 16e342083cSJohn Floren #define AUX_IRQ 12 17e342083cSJohn Floren 1867307cddSPekka Enberg /* 1967307cddSPekka Enberg * Registers 2067307cddSPekka Enberg */ 2167307cddSPekka Enberg #define I8042_DATA_REG 0x60 2267307cddSPekka Enberg #define I8042_COMMAND_REG 0x64 2367307cddSPekka Enberg 24b4a721d9SPekka Enberg /* 25b4a721d9SPekka Enberg * Commands 26b4a721d9SPekka Enberg */ 27b4a721d9SPekka Enberg #define I8042_CMD_CTL_RCTR 0x20 28b4a721d9SPekka Enberg #define I8042_CMD_CTL_WCTR 0x60 29b4a721d9SPekka Enberg #define I8042_CMD_AUX_LOOP 0xD3 30b4a721d9SPekka Enberg #define I8042_CMD_AUX_SEND 0xD4 31b4a721d9SPekka Enberg #define I8042_CMD_AUX_TEST 0xA9 32b4a721d9SPekka Enberg #define I8042_CMD_AUX_DISABLE 0xA7 33b4a721d9SPekka Enberg #define I8042_CMD_AUX_ENABLE 0xA8 34714e5b7fSSasha Levin #define I8042_CMD_SYSTEM_RESET 0xFE 35e342083cSJohn Floren 36e342083cSJohn Floren #define RESPONSE_ACK 0xFA 37e342083cSJohn Floren 38e342083cSJohn Floren #define MODE_DISABLE_AUX 0x20 39e342083cSJohn Floren 40e342083cSJohn Floren #define AUX_ENABLE_REPORTING 0x20 41e342083cSJohn Floren #define AUX_SCALING_FLAG 0x10 42e342083cSJohn Floren #define AUX_DEFAULT_RESOLUTION 0x2 43e342083cSJohn Floren #define AUX_DEFAULT_SAMPLE 100 44e342083cSJohn Floren 4598cbe4d9SPekka Enberg /* 4698cbe4d9SPekka Enberg * Status register bits 4798cbe4d9SPekka Enberg */ 48e9f4d662SPekka Enberg #define I8042_STR_AUXDATA 0x20 49e9f4d662SPekka Enberg #define I8042_STR_KEYLOCK 0x10 50e9f4d662SPekka Enberg #define I8042_STR_CMDDAT 0x08 51e9f4d662SPekka Enberg #define I8042_STR_MUXERR 0x04 52e9f4d662SPekka Enberg #define I8042_STR_OBF 0x01 53e342083cSJohn Floren 54e342083cSJohn Floren #define KBD_MODE_KBD_INT 0x01 55e342083cSJohn Floren #define KBD_MODE_SYS 0x02 56e342083cSJohn Floren 57e342083cSJohn Floren #define QUEUE_SIZE 128 58e342083cSJohn Floren 59e342083cSJohn Floren /* 60e342083cSJohn Floren * This represents the current state of the PS/2 keyboard system, 61e342083cSJohn Floren * including the AUX device (the mouse) 62e342083cSJohn Floren */ 63e342083cSJohn Floren struct kbd_state { 64e342083cSJohn Floren struct kvm *kvm; 65e342083cSJohn Floren 66e342083cSJohn Floren char kq[QUEUE_SIZE]; /* Keyboard queue */ 67e342083cSJohn Floren int kread, kwrite; /* Indexes into the queue */ 68e342083cSJohn Floren int kcount; /* number of elements in queue */ 69e342083cSJohn Floren 70e342083cSJohn Floren char mq[QUEUE_SIZE]; 71e342083cSJohn Floren int mread, mwrite; 72e342083cSJohn Floren int mcount; 73e342083cSJohn Floren 74e342083cSJohn Floren u8 mstatus; /* Mouse status byte */ 75e342083cSJohn Floren u8 mres; /* Current mouse resolution */ 76e342083cSJohn Floren u8 msample; /* Current mouse samples/second */ 77e342083cSJohn Floren 78e342083cSJohn Floren u8 mode; /* i8042 mode register */ 79e342083cSJohn Floren u8 status; /* i8042 status register */ 80e342083cSJohn Floren /* 81e342083cSJohn Floren * Some commands (on port 0x64) have arguments; 82e342083cSJohn Floren * we store the command here while we wait for the argument 83e342083cSJohn Floren */ 84e342083cSJohn Floren u32 write_cmd; 85e342083cSJohn Floren }; 86e342083cSJohn Floren 87e342083cSJohn Floren static struct kbd_state state; 88e342083cSJohn Floren 89e342083cSJohn Floren /* 90e342083cSJohn Floren * If there are packets to be read, set the appropriate IRQs high 91e342083cSJohn Floren */ 92e342083cSJohn Floren static void kbd_update_irq(void) 93e342083cSJohn Floren { 94e342083cSJohn Floren u8 klevel = 0; 95e342083cSJohn Floren u8 mlevel = 0; 96e342083cSJohn Floren 97e342083cSJohn Floren /* First, clear the kbd and aux output buffer full bits */ 98e9f4d662SPekka Enberg state.status &= ~(I8042_STR_OBF | I8042_STR_AUXDATA); 99e342083cSJohn Floren 100e342083cSJohn Floren if (state.kcount > 0) { 101e9f4d662SPekka Enberg state.status |= I8042_STR_OBF; 102e342083cSJohn Floren klevel = 1; 103e342083cSJohn Floren } 104e342083cSJohn Floren 105e342083cSJohn Floren /* Keyboard has higher priority than mouse */ 106e342083cSJohn Floren if (klevel == 0 && state.mcount != 0) { 107e9f4d662SPekka Enberg state.status |= I8042_STR_OBF | I8042_STR_AUXDATA; 108e342083cSJohn Floren mlevel = 1; 109e342083cSJohn Floren } 110e342083cSJohn Floren 111e342083cSJohn Floren kvm__irq_line(state.kvm, KBD_IRQ, klevel); 112e342083cSJohn Floren kvm__irq_line(state.kvm, AUX_IRQ, mlevel); 113e342083cSJohn Floren } 114e342083cSJohn Floren 115e342083cSJohn Floren /* 116e342083cSJohn Floren * Add a byte to the mouse queue, then set IRQs 117e342083cSJohn Floren */ 118b6f60a85SPekka Enberg void mouse_queue(u8 c) 119e342083cSJohn Floren { 120e342083cSJohn Floren if (state.mcount >= QUEUE_SIZE) 121e342083cSJohn Floren return; 122e342083cSJohn Floren 123e342083cSJohn Floren state.mq[state.mwrite++ % QUEUE_SIZE] = c; 124e342083cSJohn Floren 125e342083cSJohn Floren state.mcount++; 126e342083cSJohn Floren kbd_update_irq(); 127e342083cSJohn Floren } 128e342083cSJohn Floren 129e342083cSJohn Floren /* 130e342083cSJohn Floren * Add a byte to the keyboard queue, then set IRQs 131e342083cSJohn Floren */ 132b6f60a85SPekka Enberg void kbd_queue(u8 c) 133e342083cSJohn Floren { 134e342083cSJohn Floren if (state.kcount >= QUEUE_SIZE) 135e342083cSJohn Floren return; 136e342083cSJohn Floren 137e342083cSJohn Floren state.kq[state.kwrite++ % QUEUE_SIZE] = c; 138e342083cSJohn Floren 139e342083cSJohn Floren state.kcount++; 140e342083cSJohn Floren kbd_update_irq(); 141e342083cSJohn Floren } 142e342083cSJohn Floren 143714e5b7fSSasha Levin static void kbd_write_command(struct kvm *kvm, u8 val) 144e342083cSJohn Floren { 145e342083cSJohn Floren switch (val) { 146b4a721d9SPekka Enberg case I8042_CMD_CTL_RCTR: 147e342083cSJohn Floren kbd_queue(state.mode); 148e342083cSJohn Floren break; 149b4a721d9SPekka Enberg case I8042_CMD_CTL_WCTR: 150b4a721d9SPekka Enberg case I8042_CMD_AUX_SEND: 151b4a721d9SPekka Enberg case I8042_CMD_AUX_LOOP: 152e342083cSJohn Floren state.write_cmd = val; 153e342083cSJohn Floren break; 154b4a721d9SPekka Enberg case I8042_CMD_AUX_TEST: 155e342083cSJohn Floren /* 0 means we're a normal PS/2 mouse */ 156e342083cSJohn Floren mouse_queue(0); 157e342083cSJohn Floren break; 158b4a721d9SPekka Enberg case I8042_CMD_AUX_DISABLE: 159e342083cSJohn Floren state.mode |= MODE_DISABLE_AUX; 160e342083cSJohn Floren break; 161b4a721d9SPekka Enberg case I8042_CMD_AUX_ENABLE: 162e342083cSJohn Floren state.mode &= ~MODE_DISABLE_AUX; 163e342083cSJohn Floren break; 164714e5b7fSSasha Levin case I8042_CMD_SYSTEM_RESET: 165df4239fbSSasha Levin kvm_cpu__reboot(kvm); 166714e5b7fSSasha Levin break; 167e342083cSJohn Floren default: 168e342083cSJohn Floren break; 169e342083cSJohn Floren } 170e342083cSJohn Floren } 171e342083cSJohn Floren 172e342083cSJohn Floren /* 173e342083cSJohn Floren * Called when the OS reads from port 0x60 (PS/2 data) 174e342083cSJohn Floren */ 175e342083cSJohn Floren static u32 kbd_read_data(void) 176e342083cSJohn Floren { 177e342083cSJohn Floren u32 ret; 178e342083cSJohn Floren int i; 179e342083cSJohn Floren 180e342083cSJohn Floren if (state.kcount != 0) { 181e342083cSJohn Floren /* Keyboard data gets read first */ 182e342083cSJohn Floren ret = state.kq[state.kread++ % QUEUE_SIZE]; 183e342083cSJohn Floren state.kcount--; 184e342083cSJohn Floren kvm__irq_line(state.kvm, KBD_IRQ, 0); 185e342083cSJohn Floren kbd_update_irq(); 186e342083cSJohn Floren } else if (state.mcount > 0) { 187e342083cSJohn Floren /* Followed by the mouse */ 188e342083cSJohn Floren ret = state.mq[state.mread++ % QUEUE_SIZE]; 189e342083cSJohn Floren state.mcount--; 190e342083cSJohn Floren kvm__irq_line(state.kvm, AUX_IRQ, 0); 191e342083cSJohn Floren kbd_update_irq(); 192*76857a39SCong Ding } else { 193e342083cSJohn Floren i = state.kread - 1; 194e342083cSJohn Floren if (i < 0) 195e342083cSJohn Floren i = QUEUE_SIZE; 196e342083cSJohn Floren ret = state.kq[i]; 197e342083cSJohn Floren } 198e342083cSJohn Floren return ret; 199e342083cSJohn Floren } 200e342083cSJohn Floren 201e342083cSJohn Floren /* 202e342083cSJohn Floren * Called when the OS read from port 0x64, the command port 203e342083cSJohn Floren */ 204e342083cSJohn Floren static u32 kbd_read_status(void) 205e342083cSJohn Floren { 206e342083cSJohn Floren return (u32)state.status; 207e342083cSJohn Floren } 208e342083cSJohn Floren 209e342083cSJohn Floren /* 210e342083cSJohn Floren * Called when the OS writes to port 0x60 (data port) 211e342083cSJohn Floren * Things written here are generally arguments to commands previously 212e342083cSJohn Floren * written to port 0x64 and stored in state.write_cmd 213e342083cSJohn Floren */ 214e342083cSJohn Floren static void kbd_write_data(u32 val) 215e342083cSJohn Floren { 216e342083cSJohn Floren switch (state.write_cmd) { 217b4a721d9SPekka Enberg case I8042_CMD_CTL_WCTR: 218e342083cSJohn Floren state.mode = val; 219e342083cSJohn Floren kbd_update_irq(); 220e342083cSJohn Floren break; 221b4a721d9SPekka Enberg case I8042_CMD_AUX_LOOP: 222e342083cSJohn Floren mouse_queue(val); 223e342083cSJohn Floren mouse_queue(RESPONSE_ACK); 224e342083cSJohn Floren break; 225b4a721d9SPekka Enberg case I8042_CMD_AUX_SEND: 226e342083cSJohn Floren /* The OS wants to send a command to the mouse */ 227e342083cSJohn Floren mouse_queue(RESPONSE_ACK); 228e342083cSJohn Floren switch (val) { 229e342083cSJohn Floren case 0xe6: 230e342083cSJohn Floren /* set scaling = 1:1 */ 231e342083cSJohn Floren state.mstatus &= ~AUX_SCALING_FLAG; 232e342083cSJohn Floren break; 233e342083cSJohn Floren case 0xe8: 234e342083cSJohn Floren /* set resolution */ 235e342083cSJohn Floren state.mres = val; 236e342083cSJohn Floren break; 237e342083cSJohn Floren case 0xe9: 238e342083cSJohn Floren /* Report mouse status/config */ 239e342083cSJohn Floren mouse_queue(state.mstatus); 240e342083cSJohn Floren mouse_queue(state.mres); 241e342083cSJohn Floren mouse_queue(state.msample); 242e342083cSJohn Floren break; 243e342083cSJohn Floren case 0xf2: 244e342083cSJohn Floren /* send ID */ 245e342083cSJohn Floren mouse_queue(0); /* normal mouse */ 246e342083cSJohn Floren break; 247e342083cSJohn Floren case 0xf3: 248e342083cSJohn Floren /* set sample rate */ 249e342083cSJohn Floren state.msample = val; 250e342083cSJohn Floren break; 251e342083cSJohn Floren case 0xf4: 252e342083cSJohn Floren /* enable reporting */ 253e342083cSJohn Floren state.mstatus |= AUX_ENABLE_REPORTING; 254e342083cSJohn Floren break; 255e342083cSJohn Floren case 0xf5: 256e342083cSJohn Floren state.mstatus &= ~AUX_ENABLE_REPORTING; 257e342083cSJohn Floren break; 258e342083cSJohn Floren case 0xf6: 259e342083cSJohn Floren /* set defaults, just fall through to reset */ 260e342083cSJohn Floren case 0xff: 261e342083cSJohn Floren /* reset */ 262e342083cSJohn Floren state.mstatus = 0x0; 263e342083cSJohn Floren state.mres = AUX_DEFAULT_RESOLUTION; 264e342083cSJohn Floren state.msample = AUX_DEFAULT_SAMPLE; 265e342083cSJohn Floren break; 266e342083cSJohn Floren default: 267e342083cSJohn Floren break; 268e342083cSJohn Floren } 269e342083cSJohn Floren break; 270e342083cSJohn Floren case 0: 271e342083cSJohn Floren /* Just send the ID */ 272e342083cSJohn Floren kbd_queue(RESPONSE_ACK); 273e342083cSJohn Floren kbd_queue(0xab); 274e342083cSJohn Floren kbd_queue(0x41); 275e342083cSJohn Floren kbd_update_irq(); 276e342083cSJohn Floren break; 277e342083cSJohn Floren default: 278e342083cSJohn Floren /* Yeah whatever */ 279e342083cSJohn Floren break; 280e342083cSJohn Floren } 281e342083cSJohn Floren state.write_cmd = 0; 282e342083cSJohn Floren } 283e342083cSJohn Floren 284e342083cSJohn Floren static void kbd_reset(void) 285e342083cSJohn Floren { 286e342083cSJohn Floren state = (struct kbd_state) { 287e9f4d662SPekka Enberg .status = I8042_STR_MUXERR | I8042_STR_CMDDAT | I8042_STR_KEYLOCK, /* 0x1c */ 288e342083cSJohn Floren .mode = KBD_MODE_KBD_INT | KBD_MODE_SYS, /* 0x3 */ 289e342083cSJohn Floren .mres = AUX_DEFAULT_RESOLUTION, 290e342083cSJohn Floren .msample = AUX_DEFAULT_SAMPLE, 291e342083cSJohn Floren }; 292e342083cSJohn Floren } 293e342083cSJohn Floren 294e342083cSJohn Floren /* 295e342083cSJohn Floren * Called when the OS has written to one of the keyboard's ports (0x60 or 0x64) 296e342083cSJohn Floren */ 297c9f6a037SXiao Guangrong static bool kbd_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size) 298e342083cSJohn Floren { 299ca9326bbSPekka Enberg switch (port) { 300ca9326bbSPekka Enberg case I8042_COMMAND_REG: { 301ca9326bbSPekka Enberg u8 value = kbd_read_status(); 302ca9326bbSPekka Enberg ioport__write8(data, value); 303ca9326bbSPekka Enberg break; 304e342083cSJohn Floren } 305ca9326bbSPekka Enberg case I8042_DATA_REG: { 306ca9326bbSPekka Enberg u32 value = kbd_read_data(); 307ca9326bbSPekka Enberg ioport__write32(data, value); 308ca9326bbSPekka Enberg break; 309ca9326bbSPekka Enberg } 310ca9326bbSPekka Enberg default: 311ca9326bbSPekka Enberg return false; 312ca9326bbSPekka Enberg } 313ca9326bbSPekka Enberg 314e342083cSJohn Floren return true; 315e342083cSJohn Floren } 316e342083cSJohn Floren 317c9f6a037SXiao Guangrong static bool kbd_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size) 318e342083cSJohn Floren { 319ca9326bbSPekka Enberg switch (port) { 320ca9326bbSPekka Enberg case I8042_COMMAND_REG: { 321ca9326bbSPekka Enberg u8 value = ioport__read8(data); 322714e5b7fSSasha Levin kbd_write_command(kvm, value); 323ca9326bbSPekka Enberg break; 324ca9326bbSPekka Enberg } 325ca9326bbSPekka Enberg case I8042_DATA_REG: { 326ca9326bbSPekka Enberg u32 value = ioport__read32(data); 327ca9326bbSPekka Enberg kbd_write_data(value); 328ca9326bbSPekka Enberg break; 329ca9326bbSPekka Enberg } 330ca9326bbSPekka Enberg default: 331ca9326bbSPekka Enberg return false; 332ca9326bbSPekka Enberg } 333e342083cSJohn Floren 334e342083cSJohn Floren return true; 335e342083cSJohn Floren } 336e342083cSJohn Floren 337e342083cSJohn Floren static struct ioport_operations kbd_ops = { 338e342083cSJohn Floren .io_in = kbd_in, 339e342083cSJohn Floren .io_out = kbd_out, 340e342083cSJohn Floren }; 341e342083cSJohn Floren 342be1eb89bSSasha Levin int kbd__init(struct kvm *kvm) 343e342083cSJohn Floren { 344be1eb89bSSasha Levin #ifndef CONFIG_X86 345be1eb89bSSasha Levin return 0; 346be1eb89bSSasha Levin #endif 347be1eb89bSSasha Levin 348e342083cSJohn Floren kbd_reset(); 349e342083cSJohn Floren state.kvm = kvm; 3504346fd8fSSasha Levin ioport__register(kvm, I8042_DATA_REG, &kbd_ops, 2, NULL); 3514346fd8fSSasha Levin ioport__register(kvm, I8042_COMMAND_REG, &kbd_ops, 2, NULL); 352be1eb89bSSasha Levin 353be1eb89bSSasha Levin return 0; 354e342083cSJohn Floren } 35549a8afd1SSasha Levin dev_init(kbd__init); 356