added a submitted ps/2 mouse driver by Elad Lahav
[newos.git] / kernel / dev / arch / i386 / ps2mouse / ps2mouse.c
blob5c96f0f9aaf0d7c2fbaf57b5dba370ac6cb85e39
1 /*
2 * ps2mouse.c:
3 * PS/2 mouse device driver for NewOS and OpenBeOS.
4 * Author: Elad Lahav (elad@eldarshany.com)
5 * Created: 21.12.2001
6 * Modified: 11.1.2002
7 */
9 /*
10 * A PS/2 mouse is connected to the IBM 8042 controller, and gets its
11 * name from the IBM PS/2 personal computer, which was the first to
12 * use this device. All resources are shared between the keyboard, and
13 * the mouse, referred to as the "Auxiliary Device".
14 * I/O:
15 * ~~~
16 * The controller has 3 I/O registers:
17 * 1. Status (input), mapped to port 64h
18 * 2. Control (output), mapped to port 64h
19 * 3. Data (input/output), mapped to port 60h
20 * Data:
21 * ~~~~
22 * Since a mouse is an input only device, data can only be read, and
23 * not written. A packet read from the mouse data port is composed of
24 * three bytes:
25 * byte 0: status byte, where
26 * - bit 0: Y overflow (1 = true)
27 * - bit 1: X overflow (1 = true)
28 * - bit 2: MSB of Y offset
29 * - bit 3: MSB of X offset
30 * - bit 4: Syncronization bit (always 1)
31 * - bit 5: Middle button (1 = down)
32 * - bit 6: Right button (1 = down)
33 * - bit 7: Left button (1 = down)
34 * byte 1: X position change, since last probed (-127 to +127)
35 * byte 2: Y position change, since last probed (-127 to +127)
36 * Interrupts:
37 * ~~~~~~~~~~
38 * The PS/2 mouse device is connected to interrupt 12, which means that
39 * it uses the second interrupt controller (handles INT8 to INT15). In
40 * order for this interrupt to be enabled, both the 5th interrupt of
41 * the second controller AND the 3rd interrupt of the first controller
42 * (cascade mode) should be unmasked.
43 * The controller uses 3 consecutive interrupts to inform the computer
44 * that it has new data. On the first the data register holds the status
45 * byte, on the second the X offset, and on the 3rd the Y offset.
48 #include <kernel/kernel.h>
49 #include <kernel/heap.h>
50 #include <kernel/int.h>
51 #include <kernel/debug.h>
52 #include <kernel/fs/devfs.h>
53 #include <kernel/arch/int.h>
54 #include <kernel/sem.h>
55 #include <libc/string.h>
56 #include <kernel/dev/arch/i386/ps2mouse/ps2mouse.h>
58 /////////////////////////////////////////////////////////////////////////
59 // definitions
61 // I/O addresses
62 #define PS2_PORT_DATA 0x60
63 #define PS2_PORT_CTRL 0x64
65 // control words
66 #define PS2_CTRL_WRITE_CMD 0x60
67 #define PS2_CTRL_WRITE_AUX 0xD4
69 // data words
70 #define PS2_CMD_DEV_INIT 0x43
71 #define PS2_CMD_ENABLE_MOUSE 0xF4
72 #define PS2_CMD_DISABLE_MOUSE 0xF5
73 #define PS2_CMD_RESET_MOUSE 0xFF
75 #define PS2_RES_ACK 0xFA
76 #define PS2_RES_RESEND 0xFE
78 // interrupts
79 #define INT_BASE 0x20
80 #define INT_PS2_MOUSE 0x0C
81 #define INT_CASCADE 0x02
83 #define PACKET_SIZE 3
86 * mouse_data:
87 * Holds the data read from the PS/2 auxiliary device.
89 typedef struct {
90 char status;
91 char delta_x;
92 char delta_y;
93 } mouse_data; // mouse_data
95 static mouse_data md_int;
96 static mouse_data md_read;
97 static sem_id mouse_sem;
98 static bool in_read;
100 /////////////////////////////////////////////////////////////////////////
101 // interrupt
104 * handle_mouse_interrupt:
105 * Interrupt handler for the mouse device. Called whenever the I/O
106 * controller generates an interrupt for the PS/2 mouse. Reads mouse
107 * information from the data port, and stores it, so it can be accessed
108 * by read() operations. The full data is obtained using 3 consecutive
109 * calls to the handler, each holds a different byte on the data port.
110 * Parameters:
111 * void*, ignored
112 * Return value:
113 * int, ???
115 static int handle_mouse_interrupt(void* data)
117 char c;
118 static int next_input = 0;
120 // read port
121 c = in8(PS2_PORT_DATA);
123 // put port contents in the appropriate data member, according to
124 // current cycle
125 switch(next_input) {
126 // status byte
127 case 0:
128 md_int.status = c;
129 break;
131 // x-axis change
132 case 1:
133 md_int.delta_x += c;
134 break;
136 // y-axis change
137 case 2:
138 md_int.delta_y += c;
140 // check if someone is waiting to read data
141 if(in_read) {
142 // copy data to read structure, and release waiting process
143 memcpy(&md_read, &md_int, sizeof(mouse_data));
144 memset(&md_int, 0, sizeof(mouse_data));
145 in_read = false;
146 sem_release_etc(mouse_sem, 1, SEM_FLAG_NO_RESCHED);
147 } // if
148 break;
149 } // switch
151 next_input = (next_input + 1) % 3;
152 return INT_NO_RESCHEDULE;
153 } // handle_mouse_interrupt
155 /////////////////////////////////////////////////////////////////////////
156 // file operations
159 * mouse_open:
161 static int mouse_open(dev_ident ident, dev_cookie *cookie)
163 *cookie = NULL;
164 return 0;
165 } // mouse_open
168 * mouse_close:
170 static int mouse_close(dev_cookie cookie)
172 return 0;
173 } // mouse_close
176 * mouse_freecookie:
178 static int mouse_freecookie(dev_cookie cookie)
180 return 0;
181 } // mouse_freecookie
184 * mouse_seek:
186 static int mouse_seek(dev_cookie cookie, off_t pos, seek_type st)
188 return ERR_NOT_ALLOWED;
189 } // mouse_seek
192 * mouse_read:
193 * Gets a mouse data packet.
194 * Parameters:
195 * dev_cookie, ignored
196 * void*, pointer to a buffer that accepts the data
197 * off_t, ignored
198 * ssize_t, buffer size, must be at least the size of the data packet
200 static ssize_t mouse_read(dev_cookie cookie, void* buf, off_t pos,
201 ssize_t len)
203 // inform interrupt handler that data is being waited for
204 in_read = true;
206 // wait until there is data to read
207 if(sem_acquire_etc(mouse_sem, 1, SEM_FLAG_INTERRUPTABLE, 0, NULL) ==
208 ERR_SEM_INTERRUPTED) {
209 return 0;
210 } // if
212 // verify user's buffer is of the right size
213 if(len < PACKET_SIZE)
214 return 0;
216 // copy data to user's buffer
217 ((char*)buf)[0] = md_read.status;
218 ((char*)buf)[1] = md_read.delta_x;
219 ((char*)buf)[2] = md_read.delta_y;
221 return PACKET_SIZE;
222 } // mouse_read
225 * mouse_write:
227 static ssize_t mouse_write(dev_cookie cookie, const void *buf, off_t pos,
228 ssize_t len)
230 return ERR_VFS_READONLY_FS;
231 } // mouse_write
234 * mouse_ioctl:
236 static int mouse_ioctl(dev_cookie cookie, int op, void *buf, size_t len)
238 return ERR_INVALID_ARGS;
239 } // mouse_ioctl
242 * function structure used for file-op registration
244 struct dev_calls ps2_mouse_hooks = {
245 &mouse_open,
246 &mouse_close,
247 &mouse_freecookie,
248 &mouse_seek,
249 &mouse_ioctl,
250 &mouse_read,
251 &mouse_write,
252 /* cannot page from mouse */
253 NULL,
254 NULL,
255 NULL
256 }; // ps2_mouse_hooks
258 /////////////////////////////////////////////////////////////////////////
259 // initialization
262 * wait_write_ctrl:
263 * Wait until the control port is ready to be written. This requires that
264 * the "Input buffer full" and "Output buffer full" bits will both be set
265 * to 0.
267 static void wait_write_ctrl()
269 while(in8(PS2_PORT_CTRL) & 0x3);
270 } // wait_for_ctrl_output
273 * wait_write_data:
274 * Wait until the data port is ready to be written. This requires that
275 * the "Input buffer full" bit will be set to 0.
277 static void wait_write_data()
279 while(in8(PS2_PORT_CTRL) & 0x2);
280 } // wait_write_data
283 * wait_read_data:
284 * Wait until the data port can be read from. This requires that the
285 * "Output buffer full" bit will be set to 1.
287 static void wait_read_data()
289 while((in8(PS2_PORT_CTRL) & 0x1) == 0);
290 } // wait_read_data
293 * write_command_byte:
294 * Writes a command byte to the data port of the PS/2 controller.
295 * Parameters:
296 * unsigned char, byte to write
298 static void write_command_byte(unsigned char b)
300 wait_write_ctrl();
301 out8(PS2_CTRL_WRITE_CMD, PS2_PORT_CTRL);
302 wait_write_data();
303 out8(b, PS2_PORT_DATA);
304 } // write_command_byte
307 * write_aux_byte:
308 * Writes a byte to the mouse device. Uses the control port to indicate
309 * that the byte is sent to the auxiliary device (mouse), instead of the
310 * keyboard.
311 * Parameters:
312 * unsigned char, byte to write
314 static void write_aux_byte(unsigned char b)
316 wait_write_ctrl();
317 out8(PS2_CTRL_WRITE_AUX, PS2_PORT_CTRL);
318 wait_write_data();
319 out8(b, PS2_PORT_DATA);
320 } // write_aux_byte
323 * read_data_byte:
324 * Reads a single byte from the data port.
325 * Return value:
326 * unsigned char, byte read
328 static unsigned char read_data_byte()
330 wait_read_data();
331 return in8(PS2_PORT_DATA);
332 } // read_data_byte
335 * mouse_dev_init:
336 * Called by the kernel to setup the device. Initializes the driver.
337 * Parameters:
338 * kernel_args*, ignored
339 * Return value:
340 * int, 0 if successful, negative error value otherwise
342 int mouse_dev_init(kernel_args *ka)
344 dprintf("Initializing PS/2 mouse\n");
346 // init device driver
347 memset(&md_int, 0, sizeof(mouse_data));
349 // register interrupt handler
350 int_set_io_interrupt_handler(INT_BASE + INT_PS2_MOUSE,
351 &handle_mouse_interrupt, NULL);
353 // must enable the cascade interrupt
354 arch_int_enable_io_interrupt(INT_BASE + INT_CASCADE);
356 // enable auxilary device, IRQs and PS/2 mouse
357 write_command_byte(PS2_CMD_DEV_INIT);
358 write_aux_byte(PS2_CMD_ENABLE_MOUSE);
360 // controller should send ACK if mouse was detected
361 if(read_data_byte() != PS2_RES_ACK) {
362 dprintf("No PS/2 mouse found\n");
363 return -1;
364 } // if
366 dprintf("A PS/2 mouse has been successfully detected\n");
368 // create the mouse semaphore, used for synchronization between
369 // the interrupt handler and the read() operation
370 mouse_sem = sem_create(0, "ps2_mouse_sem");
371 if(mouse_sem < 0)
372 panic("failed to create PS/2 mouse semaphore!\n");
374 // register device file-system like operations
375 devfs_publish_device("ps2mouse", NULL, &ps2_mouse_hooks);
377 return 0;
378 } // mouse_dev_init