mouse.c (6812B)
1 /* PS/2 Mouse Driver */ 2 #include <stdint.h> 3 #include "mouse.h" 4 #include "terminal.h" 5 6 /* PS/2 Controller Ports */ 7 #define PS2_DATA_PORT 0x60 8 #define PS2_STATUS_PORT 0x64 9 #define PS2_COMMAND_PORT 0x64 10 11 /* Mouse state */ 12 static mouse_state_t mouse_state = { 13 .x = 512, /* Center of 1024x768 */ 14 .y = 384, 15 .buttons = 0, 16 .initialized = 0 17 }; 18 19 /* Mouse packet buffer */ 20 static uint8_t mouse_packet[3]; 21 static uint8_t mouse_packet_index = 0; 22 23 /* I/O port functions */ 24 static inline void outb(uint16_t port, uint8_t val) { 25 __asm__ volatile ("outb %0, %1" : : "a"(val), "Nd"(port)); 26 } 27 28 static inline uint8_t inb(uint16_t port) { 29 uint8_t ret; 30 __asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port)); 31 return ret; 32 } 33 34 /* Wait for PS/2 controller to be ready to read */ 35 static void mouse_wait_input(void) { 36 uint32_t timeout = 100000; 37 while (timeout--) { 38 if ((inb(PS2_STATUS_PORT) & 0x01)) { 39 return; 40 } 41 } 42 } 43 44 /* Wait for PS/2 controller to be ready to write */ 45 static void mouse_wait_output(void) { 46 uint32_t timeout = 100000; 47 while (timeout--) { 48 if (!(inb(PS2_STATUS_PORT) & 0x02)) { 49 return; 50 } 51 } 52 } 53 54 /* Write to mouse */ 55 static void mouse_write(uint8_t data) { 56 mouse_wait_output(); 57 outb(PS2_COMMAND_PORT, 0xD4); /* Tell controller we're sending to mouse */ 58 mouse_wait_output(); 59 outb(PS2_DATA_PORT, data); 60 } 61 62 /* Read from mouse */ 63 static uint8_t mouse_read(void) { 64 mouse_wait_input(); 65 return inb(PS2_DATA_PORT); 66 } 67 68 /* Flush any pending data from the PS/2 controller */ 69 static void mouse_flush(void) { 70 uint32_t timeout = 1000; 71 while (timeout-- && (inb(PS2_STATUS_PORT) & 0x01)) { 72 inb(PS2_DATA_PORT); /* Read and discard */ 73 } 74 } 75 76 /* Initialize PS/2 mouse */ 77 int mouse_init(void) { 78 terminal_writestring("Initializing PS/2 mouse...\n"); 79 80 /* Enable auxiliary device (mouse) */ 81 mouse_wait_output(); 82 outb(PS2_COMMAND_PORT, 0xA8); 83 /* Command 0xA8 doesn't produce a response, no read needed */ 84 85 /* Enable interrupts for mouse (IRQ12) */ 86 mouse_wait_output(); 87 outb(PS2_COMMAND_PORT, 0x20); /* Get controller config */ 88 mouse_wait_input(); 89 uint8_t status = inb(PS2_DATA_PORT) | 0x02; /* Enable IRQ12 */ 90 mouse_wait_output(); 91 outb(PS2_COMMAND_PORT, 0x60); /* Set controller config */ 92 mouse_wait_output(); 93 outb(PS2_DATA_PORT, status); 94 /* Command 0x60 doesn't produce a response, no read needed */ 95 96 /* Flush any stale data from the buffer before sending mouse commands */ 97 mouse_flush(); 98 terminal_writestring("Mouse: Buffer flushed\n"); 99 100 /* Use default settings */ 101 mouse_write(0xF6); 102 uint8_t ack = mouse_read(); 103 if (ack != 0xFA) { 104 terminal_writestring("Mouse: Failed to set defaults (ACK="); 105 terminal_putchar('0' + ((ack >> 4) & 0xF)); 106 terminal_putchar('0' + (ack & 0xF)); 107 terminal_writestring(")\n"); 108 } 109 110 /* Set sample rate to 100 reports/sec for more responsive movement */ 111 mouse_write(0xF3); /* Set sample rate command */ 112 ack = mouse_read(); 113 if (ack != 0xFA) { 114 terminal_writestring("Mouse: Failed to set sample rate cmd (ACK!=0xFA)\n"); 115 } 116 mouse_write(100); /* 100 samples/sec */ 117 ack = mouse_read(); 118 if (ack != 0xFA) { 119 terminal_writestring("Mouse: Failed to set sample rate value (ACK!=0xFA)\n"); 120 } 121 122 /* Set resolution to 4 counts/mm */ 123 mouse_write(0xE8); /* Set resolution command */ 124 ack = mouse_read(); 125 if (ack != 0xFA) { 126 terminal_writestring("Mouse: Failed to set resolution cmd (ACK!=0xFA)\n"); 127 } 128 mouse_write(0x03); /* 8 counts/mm (highest sensitivity) */ 129 ack = mouse_read(); 130 if (ack != 0xFA) { 131 terminal_writestring("Mouse: Failed to set resolution value (ACK!=0xFA)\n"); 132 } 133 134 /* Enable data reporting */ 135 mouse_write(0xF4); 136 ack = mouse_read(); 137 if (ack != 0xFA) { 138 terminal_writestring("Mouse: Failed to enable data reporting (ACK!=0xFA)\n"); 139 return -1; 140 } 141 terminal_writestring("Mouse: Data reporting enabled (ACK=0xFA)\n"); 142 143 /* Unmask IRQ12 in PIC (slave PIC) */ 144 /* IRQ12 is bit 4 on the slave PIC (0xA1) */ 145 uint8_t slave_mask = inb(0xA1); 146 slave_mask &= ~(1 << 4); /* Clear bit 4 to unmask IRQ12 */ 147 outb(0xA1, slave_mask); 148 149 /* Also unmask IRQ2 (cascade) on master PIC so slave interrupts work */ 150 uint8_t master_mask = inb(0x21); 151 master_mask &= ~(1 << 2); /* Clear bit 2 to unmask IRQ2 */ 152 outb(0x21, master_mask); 153 154 mouse_state.initialized = 1; 155 terminal_writestring("Mouse initialized!\n"); 156 157 return 0; 158 } 159 160 /* Packet counter for debugging */ 161 static uint32_t packet_count = 0; 162 163 /* Handle mouse interrupt (called from IRQ12 handler) */ 164 void mouse_handler(void) { 165 uint8_t data = inb(PS2_DATA_PORT); 166 167 /* First byte must have bit 3 set for valid packet */ 168 if (mouse_packet_index == 0 && !(data & 0x08)) { 169 /* Invalid first byte, ignore and resync */ 170 return; 171 } 172 173 /* Build mouse packet (3 bytes) */ 174 mouse_packet[mouse_packet_index] = data; 175 mouse_packet_index++; 176 177 if (mouse_packet_index == 3) { 178 /* Full packet received */ 179 mouse_packet_index = 0; 180 packet_count++; 181 182 /* Parse packet */ 183 uint8_t flags = mouse_packet[0]; 184 int8_t dx = (int8_t)mouse_packet[1]; 185 int8_t dy = (int8_t)mouse_packet[2]; 186 187 /* Update button state */ 188 mouse_state.buttons = flags & 0x07; /* Left, Right, Middle */ 189 190 /* Update position */ 191 mouse_state.x += dx; 192 mouse_state.y -= dy; /* Y is inverted */ 193 194 /* Clamp to screen bounds (1024x768) */ 195 if (mouse_state.x < 0) mouse_state.x = 0; 196 if (mouse_state.x >= 1024) mouse_state.x = 1023; 197 if (mouse_state.y < 0) mouse_state.y = 0; 198 if (mouse_state.y >= 768) mouse_state.y = 767; 199 200 /* Print every packet with delta for debugging */ 201 // Disabled to reduce boot noise 202 // if (dx != 0 || dy != 0) { 203 // terminal_putchar('M'); 204 // } 205 } 206 } 207 208 /* Get current mouse state */ 209 void mouse_get_state(mouse_state_t* state) { 210 if (state) { 211 state->x = mouse_state.x; 212 state->y = mouse_state.y; 213 state->buttons = mouse_state.buttons; 214 state->initialized = mouse_state.initialized; 215 } 216 } 217 218 /* Lua binding: Get mouse position and buttons */ 219 int lua_mouse_get_state(lua_State* L) { 220 lua_newtable(L); 221 222 lua_pushinteger(L, mouse_state.x); 223 lua_setfield(L, -2, "x"); 224 225 lua_pushinteger(L, mouse_state.y); 226 lua_setfield(L, -2, "y"); 227 228 lua_pushinteger(L, mouse_state.buttons); 229 lua_setfield(L, -2, "buttons"); 230 231 lua_pushboolean(L, mouse_state.initialized); 232 lua_setfield(L, -2, "initialized"); 233 234 return 1; 235 }