UnnamedOS
ps2.c
1 /*
2  * PS/2 Controller - handles PS/2 keyboard, PS/2 mouse, speaker and reboots
3  *
4  * http://wiki.osdev.org/%228042%22_PS/2_Controller
5  * http://wiki.osdev.org/Keyboard_Controller
6  * http://www.lowlevel.eu/wiki/Keyboard_Controller
7  */
8 
9 #include <common.h>
10 #include <hardware/io/ps2.h>
11 #include <hardware/pit.h>
12 #include <hardware/io/keyboard.h>
13 #include <hardware/io/mouse.h>
14 
15 // the controller's I/O ports
16 #define PS2_DATA 0x60 // write device commands, read device/PS/2 results
17 #define PS2_CMD 0x64 // write PS/2 commands, read ps2_status_t
18 
19 // PS/2 commands
20 #define TEST_PS2 0xAA
21 #define READ_CONFIG 0x20 // see ps2_config_t
22 #define WRITE_CONFIG 0x60
23 #define READ_OUTPUT_PORT 0xD0 // see ps2_output_port_t
24 #define WRITE_OUTPUT_PORT 0xD1
25 #define DISABLE_PORT_1 0xAD
26 #define DISABLE_PORT_2 0xA7
27 #define ENABLE_PORT_1 0xAE
28 #define ENABLE_PORT_2 0xA8
29 #define TEST_PORT_1 0xAB
30 #define TEST_PORT_2 0xA9
31 #define SEND_TO_PORT_2 0xD4 // indicates we want to send to port 2 via PS2_DATA
32 #define PULSE_OUTPUT_LINES(b3, b2, b1, b0) /* used to reset the CPU */ \
33  (0xF0 + !(b3) * 0b1000 + !(b2) * 0b100 + !(b1) * 0b10 + !(b0) * 0b1)
34 
35 // PS/2 results
36 #define TEST_PS2_PASSED 0x55
37 #define TEST_PORT_PASSED 0x00
38 
39 // device commands
40 #define DEVICE_RESET 0xFF
41 #define DEVICE_IDENTIFY 0xF2
42 #define DEVICE_ENABLE 0xF4
43 #define DEVICE_DISABLE 0xF5
44 
45 // device results
46 #define DEVICE_ACK 0xFA
47 #define DEVICE_TEST_PASSED 0xAA
48 
49 #define PS2_TIMEOUT 1000
50 #define HAS_PORT1 1 // every PS/2 controller has port 1, but port 2
51 #define HAS_PORT2 port2_supported // needs to be detected first
52 
53 typedef union {
54  struct {
55  uint8_t outbuf_full : 1; // set if a PS/2 device sent data
56  uint8_t inbuf_full : 1; // clear if we are allowed to send data
57  uint8_t system : 1; // set if system passed POST tests
58  uint8_t cmd_data : 1; // whether sent value is data or command
59  uint8_t : 1;
60  uint8_t outbuf_port2 : 1; // whether port 2 sent data
61  uint8_t timeout_err : 1;
62  uint8_t parity_err : 1;
63  } __attribute__((packed)) bits;
64  uint8_t byte;
65 } ps2_status_t;
66 
67 typedef union {
68  struct {
69  uint8_t reset : 1; // whether port 1 fires IRQ1
70  uint8_t a20_gate : 1; // whether port 2 fires IRQ12
71  uint8_t : 2;
72  uint8_t outbuf_port1 : 1; // whether port 1 sent data
73  uint8_t outbuf_port2 : 1; // whether port 2 sent data
74  uint8_t : 2;
75  } __attribute__((packed)) bits;
76  uint8_t byte;
78 
79 static uint8_t init_done = 0;
80 static uint8_t port2_supported = 1; // let's assume that port 2 (IRQ12) is supported
81 static ps2_error_t err = 0; // timeout error (we often ignore this)
82 
83 static inline uint8_t ps2_ready() {
84  return !((ps2_status_t) inb(PS2_CMD)).bits.inbuf_full;
85 }
86 
87 static inline int8_t ps2_available(ps2_port_t port) {
88  ps2_status_t status = (ps2_status_t) inb(PS2_CMD);
89  if (port == PS2_ANY_PORT) // whether data is ready for us to fetch (on any port)
90  return status.bits.outbuf_full;
91  if (port == PS2_PORT_1) // we are only interested in data sent from port 1
92  return status.bits.outbuf_full && !status.bits.outbuf_port2;
93  if (port == PS2_PORT_2) // or port 2
94  return status.bits.outbuf_full && status.bits.outbuf_port2;
95  return -1;
96 }
97 
98 static inline uint8_t ps2_write(uint8_t io_port, uint8_t command) {
99  pit_timeout_t timeout = pit_make_timeout(PS2_TIMEOUT);
100  // wait until PS/2 controller accepts input or a timeout occurs
101  while (!ps2_ready() && !pit_timed_out(&timeout));
102  if (pit_timed_out(&timeout)) {
103  println("%4aPS/2 write timeout (%02x,%02x)%a", io_port, command);
104  return 0;
105  }
106  outb(io_port, command);
107  return 1;
108 }
109 
110 static inline uint8_t ps2_read(ps2_port_t port, ps2_error_t* err) {
111  pit_timeout_t timeout = pit_make_timeout(PS2_TIMEOUT);
112  // wait until PS/2 controller holds data for port or a timeout occurs
113  while (!ps2_available(port) && !pit_timed_out(&timeout));
114  if (pit_timed_out(&timeout)) {
115  println("%4aPS/2 port %d read timeout%a", port);
116  *err = PS2_TIMEOUT_ERR;
117  return 0;
118  }
119  *err = 0;
120  return inb(PS2_DATA);
121 }
122 
123 ps2_config_t ps2_read_config() {
124  ps2_write(PS2_CMD, READ_CONFIG); // we tell the controller to send us
125  return (ps2_config_t) ps2_read(PS2_ANY_PORT, &err); // the config byte
126 }
127 
128 void ps2_write_config(ps2_config_t config) {
129  ps2_write(PS2_CMD, WRITE_CONFIG);
130  ps2_write(PS2_DATA, config.byte);
131 }
132 
133 static uint8_t ps2_test(uint8_t test_command, uint8_t expected_result, const char* name) {
134  ps2_write(PS2_CMD, test_command);
135  uint8_t res = ps2_read(PS2_ANY_PORT, &err);
136  if (res != expected_result) {
137  print("%4awarning%a. %s test failed (%02x). ", name, res);
138  return 0;
139  } else return 1;
140 }
141 
142 static inline uint8_t ps2_valid_port(ps2_port_t port) {
143  if ((port != PS2_PORT_1 && port != PS2_PORT_2) || (port == PS2_PORT_2 && !HAS_PORT2)) {
144  println("%4aPS/2 port %d not supported%a", port);
145  return 0;
146  } else return 1;
147 }
148 
149 void ps2_flush() {
150  while (ps2_available(PS2_ANY_PORT))
151  ps2_read(PS2_ANY_PORT, &err); // discard any data stuck in PS/2 controller
152 }
153 
154 static uint8_t ps2_write_device_no_ack(ps2_port_t port, uint8_t command) {
155  if (!ps2_valid_port(port)) return 0;
156  if (port == PS2_PORT_2)
157  ps2_write(PS2_CMD, SEND_TO_PORT_2);
158  ps2_write(PS2_DATA, command);
159  return 1;
160 }
161 
162 uint8_t ps2_write_device(ps2_port_t port, uint8_t command) {
163  if (!ps2_write_device_no_ack(port, command))
164  return 0;
165  return ps2_read_device(port, &err) == DEVICE_ACK;
166 }
167 
168 uint8_t ps2_read_device(ps2_port_t port, ps2_error_t* err) {
169  if (!ps2_valid_port(port)) {
170  *err = PS2_INVALID_PORT_ERR;
171  return 0;
172  }
173  return ps2_read(port, err);
174 }
175 
176 static uint8_t ps2_reset_device(ps2_port_t port) {
177  ps2_write_device_no_ack(port, DEVICE_RESET);
178  pit_timeout_t timeout = pit_make_timeout(PS2_TIMEOUT);
179  while (ps2_read_device(port, &err) != DEVICE_TEST_PASSED && !pit_timed_out(&timeout));
180  if (pit_timed_out(&timeout)) {
181  print("%4afail%a. Port %d reset failed. ", port);
182  return 0;
183  } else return 1;
184 }
185 
186 static void ps2_init_controller() {
187  ps2_write(PS2_CMD, DISABLE_PORT_1); // start by disabling devices,
188  ps2_write(PS2_CMD, DISABLE_PORT_2); // preventing interference
189  ps2_flush();
190 
191  ps2_config_t config = ps2_read_config();
192  if (!config.bits.port2_clock) // ought to be 1 because we disabled port 2 above
193  HAS_PORT2 = 0;
194  // disable IRQ1, IRQ12 (for now) and scancode translation
195  config.bits.port1_intr = config.bits.port2_intr = config.bits.port1_transl = 0;
196  ps2_write_config(config);
197 
198  ps2_test(TEST_PS2, TEST_PS2_PASSED, "PS/2");
199 
200  if (HAS_PORT2) {
201  // we are not sure yet if port 2 is supported,
202  ps2_write(PS2_CMD, ENABLE_PORT_2); // so let's find out by enabling it
203  ps2_config_t config = ps2_read_config();
204  if (config.bits.port2_clock) // ought to be 0 because we just enabled port 2
205  HAS_PORT2 = 0;
206  else // we know for sure port 2 is supported, so let's disable it again
207  ps2_write(PS2_CMD, DISABLE_PORT_2);
208  }
209 
210  if (HAS_PORT1) ps2_test(TEST_PORT_1, TEST_PORT_PASSED, "Port 1");
211  if (HAS_PORT2) ps2_test(TEST_PORT_2, TEST_PORT_PASSED, "Port 2");
212  if (HAS_PORT1) ps2_write(PS2_CMD, ENABLE_PORT_1);
213  if (HAS_PORT2) ps2_write(PS2_CMD, ENABLE_PORT_2);
214 
215  config = ps2_read_config(); // controller init is done, so we enable devices/IRQs again
216  if (HAS_PORT1) config.bits.port1_intr = 1;
217  if (HAS_PORT2) config.bits.port2_intr = 1;
218  ps2_write_config(config);
219 }
220 
221 static void ps2_init_devices() {
222  if (HAS_PORT1) ps2_reset_device(PS2_PORT_1);
223  if (HAS_PORT2) ps2_reset_device(PS2_PORT_2);
224  ps2_flush(); // clear output buffer (most likely mouse ID)
225  if (HAS_PORT1) ps2_write_device(PS2_PORT_1, DEVICE_DISABLE);
226  ps2_flush();
227  if (HAS_PORT2) ps2_write_device(PS2_PORT_2, DEVICE_DISABLE);
228  ps2_flush();
229  if (HAS_PORT1) keyboard_init(PS2_PORT_1); // To simplify things, we assume port 1
230  if (HAS_PORT2) mouse_init(PS2_PORT_2); // is a keyboard and port 2 a mouse.
231  if (HAS_PORT1) ps2_write_device(PS2_PORT_1, DEVICE_ENABLE);
232  if (HAS_PORT2) ps2_write_device(PS2_PORT_2, DEVICE_ENABLE);
233  init_done = 1;
234  ps2_flush();
235 }
236 
237 static cpu_state_t* ps2_handle_interrupt(cpu_state_t* cpu) {
238  if (!init_done) return cpu; // do not interfere with polling code
239  if (cpu->intr == ISR_IRQ(12) && !HAS_PORT2) {
240  println("%4aIRQ12: not a PS/2 device%a");
241  return cpu;
242  }
243  uint8_t data = inb(PS2_DATA);
244  if (data == DEVICE_ACK) // ignore when we send commands to
245  return cpu; // the device while scanning is enabled
246  if (cpu->intr == ISR_IRQ(1))
247  keyboard_handle_data(data);
248  else if (cpu->intr == ISR_IRQ(12))
249  mouse_handle_data(data);
250  return cpu;
251 }
252 
253 void ps2_init() {
254  print("PS/2 init ... ");
255  isr_register_handler(ISR_IRQ(1), ps2_handle_interrupt);
256  isr_register_handler(ISR_IRQ(12), ps2_handle_interrupt);
257  // We assume the PS/2 controller exists and USB Legacy Support is still active.
258  // Disable IRQs and translation, self-test the controller and ports, and
259  ps2_init_controller(); // determine whether port 2 is available.
260  ps2_init_devices(); // Reset and initialize the devices.
261  println("%2aok%a. %s channel, keyboard %s.",
262  HAS_PORT2 ? "Dual" : "Single", HAS_PORT2 ? "and mouse" : "only");
263 }
264 
265 void ps2_reboot() {
266  // pulse output port bit 0 which triggers a system reboot
267  ps2_write(PS2_CMD, PULSE_OUTPUT_LINES(0, 0, 0, 1));
268 }
The CPU&#39;s state when an interrupt occurs.
Definition: isr.h:58
#define ISR_IRQ(irq)
the interrupt vector for an IRQ
Definition: isr.h:13
void isr_register_handler(size_t intr, isr_handler_t handler)
Registers a handler to call whenever a given interrupt is fired.
Definition: isr.c:66
uint32_t intr
the interrupt vector of the fired interrupt
Definition: isr.h:68