3 * PS/2 mouse device driver for NewOS and OpenBeOS.
4 * Author: Elad Lahav (elad@eldarshany.com)
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".
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
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
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)
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 /////////////////////////////////////////////////////////////////////////
62 #define PS2_PORT_DATA 0x60
63 #define PS2_PORT_CTRL 0x64
66 #define PS2_CTRL_WRITE_CMD 0x60
67 #define PS2_CTRL_WRITE_AUX 0xD4
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
80 #define INT_PS2_MOUSE 0x0C
81 #define INT_CASCADE 0x02
87 * Holds the data read from the PS/2 auxiliary device.
93 } mouse_data
; // mouse_data
95 static mouse_data md_int
;
96 static mouse_data md_read
;
97 static sem_id mouse_sem
;
100 /////////////////////////////////////////////////////////////////////////
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.
115 static int handle_mouse_interrupt(void* data
)
118 static int next_input
= 0;
121 c
= in8(PS2_PORT_DATA
);
123 // put port contents in the appropriate data member, according to
140 // check if someone is waiting to read data
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
));
146 sem_release_etc(mouse_sem
, 1, SEM_FLAG_NO_RESCHED
);
151 next_input
= (next_input
+ 1) % 3;
152 return INT_NO_RESCHEDULE
;
153 } // handle_mouse_interrupt
155 /////////////////////////////////////////////////////////////////////////
161 static int mouse_open(dev_ident ident
, dev_cookie
*cookie
)
170 static int mouse_close(dev_cookie cookie
)
178 static int mouse_freecookie(dev_cookie cookie
)
181 } // mouse_freecookie
186 static int mouse_seek(dev_cookie cookie
, off_t pos
, seek_type st
)
188 return ERR_NOT_ALLOWED
;
193 * Gets a mouse data packet.
195 * dev_cookie, ignored
196 * void*, pointer to a buffer that accepts the data
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
,
203 // inform interrupt handler that data is being waited for
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
) {
212 // verify user's buffer is of the right size
213 if(len
< PACKET_SIZE
)
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
;
227 static ssize_t
mouse_write(dev_cookie cookie
, const void *buf
, off_t pos
,
230 return ERR_VFS_READONLY_FS
;
236 static int mouse_ioctl(dev_cookie cookie
, int op
, void *buf
, size_t len
)
238 return ERR_INVALID_ARGS
;
242 * function structure used for file-op registration
244 struct dev_calls ps2_mouse_hooks
= {
252 /* cannot page from mouse */
256 }; // ps2_mouse_hooks
258 /////////////////////////////////////////////////////////////////////////
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
267 static void wait_write_ctrl()
269 while(in8(PS2_PORT_CTRL
) & 0x3);
270 } // wait_for_ctrl_output
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);
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);
293 * write_command_byte:
294 * Writes a command byte to the data port of the PS/2 controller.
296 * unsigned char, byte to write
298 static void write_command_byte(unsigned char b
)
301 out8(PS2_CTRL_WRITE_CMD
, PS2_PORT_CTRL
);
303 out8(b
, PS2_PORT_DATA
);
304 } // write_command_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
312 * unsigned char, byte to write
314 static void write_aux_byte(unsigned char b
)
317 out8(PS2_CTRL_WRITE_AUX
, PS2_PORT_CTRL
);
319 out8(b
, PS2_PORT_DATA
);
324 * Reads a single byte from the data port.
326 * unsigned char, byte read
328 static unsigned char read_data_byte()
331 return in8(PS2_PORT_DATA
);
336 * Called by the kernel to setup the device. Initializes the driver.
338 * kernel_args*, ignored
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");
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");
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
);