Mon 14 Oct 23:06:38 CEST 2024
This commit is contained in:
		
							parent
							
								
									1af3e134bf
								
							
						
					
					
						commit
						4e4a6fb542
					
				
							
								
								
									
										249
									
								
								kernel/mouse.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								kernel/mouse.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,249 @@ | ||||||
|  | /*
 | ||||||
|  | Copyright (C) 2016-2019 The University of Notre Dame | ||||||
|  | This software is distributed under the GNU General Public License. | ||||||
|  | See the file LICENSE for details. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #include "mouse.h" | ||||||
|  | #include "console.h" | ||||||
|  | #include "ioports.h" | ||||||
|  | #include "interrupt.h" | ||||||
|  | #include "kernel/ascii.h" | ||||||
|  | #include "process.h" | ||||||
|  | #include "kernelcore.h" | ||||||
|  | #include "event_queue.h" | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | The PS2 interface uses a data port and a command port. | ||||||
|  | Reading the command port yields a status byte, | ||||||
|  | while writing to the command port executes commands. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #define PS2_DATA_PORT    0x60 | ||||||
|  | #define PS2_COMMAND_PORT 0x64 | ||||||
|  | 
 | ||||||
|  | /* Some commands that may be sent to the command port. */ | ||||||
|  | 
 | ||||||
|  | #define PS2_COMMAND_READ_CONFIG 0x20 | ||||||
|  | #define PS2_COMMAND_WRITE_CONFIG 0x60 | ||||||
|  | #define PS2_COMMAND_DISABLE_MOUSE 0xA7 | ||||||
|  | #define PS2_COMMAND_ENABLE_MOUSE 0xA8 | ||||||
|  | #define PS2_COMMAND_DISABLE_KEYBOARD 0xAD | ||||||
|  | #define PS2_COMMAND_ENABLE_KEYBOARD 0xAE | ||||||
|  | #define PS2_COMMAND_MOUSE_PREFIX 0xD4 | ||||||
|  | 
 | ||||||
|  | /* The status byte read from the command port has these fields. */ | ||||||
|  | 
 | ||||||
|  | #define PS2_STATUS_OBF 0x01	// true: may not write to data port
 | ||||||
|  | #define PS2_STATUS_IBF 0x02	// true: may read from data port
 | ||||||
|  | #define PS2_STATUS_SYS 0x04	// true when port is initialized
 | ||||||
|  | #define PS2_STATUS_A2  0x08	// true if command port was last written to
 | ||||||
|  | #define PS2_STATUS_INH 0x10	// true if keyboard inhibited
 | ||||||
|  | #define PS2_STATUS_MOBF 0x20	// true if mouse output available
 | ||||||
|  | #define PS2_STATUS_TOUT 0x40	// true if timeout during I/O
 | ||||||
|  | #define PS2_STATUS_PERR 0x80	// true indicates parity error
 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | In addition, a configuration byte may be read/written | ||||||
|  | via PS2_COMMAND_READ/WRITECONFIG.  The configuration byte | ||||||
|  | has these bitfields. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #define PS2_CONFIG_PORTA_IRQ 0x01 | ||||||
|  | #define PS2_CONFIG_PORTB_IRQ 0x02 | ||||||
|  | #define PS2_CONFIG_SYSTEM    0x04 | ||||||
|  | #define PS2_CONFIG_RESERVED1 0x08 | ||||||
|  | #define PS2_CONFIG_PORTA_CLOCK 0x10 | ||||||
|  | #define PS2_CONFIG_PORTB_CLOCK 0x20 | ||||||
|  | #define PS2_CONFIG_PORTA_TRANSLATE 0x40 | ||||||
|  | #define PS2_CONFIG_RESERVED2   0x80 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | The mouse has several specialized commands that may | ||||||
|  | be sent by first sending PS2_COMMAND_MOUSE_PREFIX, | ||||||
|  | then one of the following: | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #define PS2_MOUSE_COMMAND_ENABLE_STREAMING 0xea | ||||||
|  | #define PS2_MOUSE_COMMAND_ENABLE_DEVICE    0xf4 | ||||||
|  | #define PS2_MOUSE_COMMAND_RESET 0xff | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | To read/write data to/from the PS2 port, we must first check | ||||||
|  | for the input/output buffer full (IBF/OBF) bits are set appropriately | ||||||
|  | in the status register. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | uint8_t ps2_read_data() | ||||||
|  | { | ||||||
|  | 	uint8_t status; | ||||||
|  | 	do { | ||||||
|  | 		status = inb(PS2_COMMAND_PORT); | ||||||
|  | 	} while(!(status & PS2_STATUS_OBF)); | ||||||
|  | 	return inb(PS2_DATA_PORT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ps2_write_data(uint8_t data) | ||||||
|  | { | ||||||
|  | 	uint8_t status; | ||||||
|  | 	do { | ||||||
|  | 		status = inb(PS2_COMMAND_PORT); | ||||||
|  | 	} while(status & PS2_STATUS_IBF); | ||||||
|  | 	return outb(data, PS2_DATA_PORT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | In a similar way, to write a command to the status port, | ||||||
|  | we must also check that the IBF field is cleared. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | void ps2_write_command(uint8_t data) | ||||||
|  | { | ||||||
|  | 	uint8_t status; | ||||||
|  | 	do { | ||||||
|  | 		status = inb(PS2_COMMAND_PORT); | ||||||
|  | 	} while(status & PS2_STATUS_IBF); | ||||||
|  | 	return outb(data, PS2_COMMAND_PORT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | Clear the buffer of all data by reading until OBF and IBF | ||||||
|  | are both clear.  Useful when resetting the device to achieve | ||||||
|  | a known state. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | void ps2_clear_buffer() | ||||||
|  | { | ||||||
|  | 	uint8_t status; | ||||||
|  | 	do { | ||||||
|  | 		status = inb(PS2_COMMAND_PORT); | ||||||
|  | 		if(status & PS2_STATUS_OBF) { | ||||||
|  | 			inb(PS2_DATA_PORT); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 	} while(status & (PS2_STATUS_OBF | PS2_STATUS_IBF)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | Send a mouse-specific command by sending the mouse prefix, | ||||||
|  | then the mouse command as data, then reading back an | ||||||
|  | acknowledgement. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | void ps2_mouse_command(uint8_t command) | ||||||
|  | { | ||||||
|  | 	ps2_write_command(PS2_COMMAND_MOUSE_PREFIX); | ||||||
|  | 	ps2_write_data(command); | ||||||
|  | 	ps2_read_data(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | Read and write the PS2 configuration byte, which | ||||||
|  | does not involve an acknowledgement. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | uint8_t ps2_config_get() | ||||||
|  | { | ||||||
|  | 	ps2_write_command(PS2_COMMAND_READ_CONFIG); | ||||||
|  | 	return ps2_read_data(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ps2_config_set(uint8_t config) | ||||||
|  | { | ||||||
|  | 	ps2_write_command(PS2_COMMAND_WRITE_CONFIG); | ||||||
|  | 	ps2_write_data(config); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct mouse_state state = {0,0,0}; | ||||||
|  | static struct mouse_state last_state = {0,0,0}; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | On each interrupt, read three bytes from the PS 2 port, which | ||||||
|  | gives buttons and status, X and Y position.  The ninth (sign) bit | ||||||
|  | of the X and Y position is given as a single bit in the status | ||||||
|  | word, so we must assemble a twos-complement integer if needed. | ||||||
|  | Finally, take those values and update the current mouse state. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | static void mouse_interrupt(int i, int code) | ||||||
|  | { | ||||||
|  | 	uint8_t m1 = inb(PS2_DATA_PORT); | ||||||
|  | 	uint8_t m2 = inb(PS2_DATA_PORT); | ||||||
|  | 	uint8_t m3 = inb(PS2_DATA_PORT); | ||||||
|  | 
 | ||||||
|  | 	last_state = state; | ||||||
|  | 
 | ||||||
|  | 	state.buttons = m1 & 0x03; | ||||||
|  | 	state.x += m1 & 0x10 ? 0xffffff00 | m2 : m2; | ||||||
|  | 	state.y -= m1 & 0x20 ? 0xffffff00 | m3 : m3; | ||||||
|  | 
 | ||||||
|  | 	if(state.x < 0) | ||||||
|  | 		state.x = 0; | ||||||
|  | 	if(state.y < 0) | ||||||
|  | 		state.y = 0; | ||||||
|  | 	if(state.x >= video_xres) | ||||||
|  | 		state.x = video_xres - 1; | ||||||
|  | 	if(state.y >= video_yres) | ||||||
|  | 		state.y = video_yres - 1; | ||||||
|  | 
 | ||||||
|  | 	// XXX skip mouse events for now!
 | ||||||
|  | 	return; | ||||||
|  | 
 | ||||||
|  | 	if(state.buttons!=last_state.buttons) { | ||||||
|  | 		int i; | ||||||
|  | 		for(i=0;i<8;i++) { | ||||||
|  | 			uint8_t mask = (1<<i); | ||||||
|  | 			if( (state.buttons&mask) && !(last_state.buttons&mask) ) { | ||||||
|  | 				event_queue_post_root(EVENT_BUTTON_DOWN,i,state.x,state.y); | ||||||
|  | 			} else if( !(state.buttons&mask) && (last_state.buttons&mask) ) { | ||||||
|  | 				event_queue_post_root(EVENT_BUTTON_UP,i,state.x,state.y); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if(state.x!=last_state.x || state.y!=last_state.y) {	 | ||||||
|  | 		event_queue_post_root(EVENT_MOUSE_MOVE,0,state.x,state.y); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | Do a non-blocking read of the current mouse state. | ||||||
|  | Block interrupts while reading, to avoid inconsistent state. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | void mouse_read(struct mouse_state *e) | ||||||
|  | { | ||||||
|  | 	interrupt_disable(44); | ||||||
|  | 	*e = state; | ||||||
|  | 	interrupt_enable(44); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | Unlike the keyboard, the mouse is not automatically enabled | ||||||
|  | at bootup.  We must first obtain tbe ps2 configuration register, | ||||||
|  | enable both port A (keyboard) and port B (mouse), then send | ||||||
|  | a series of commands to reset the mouse and enable "streaming", | ||||||
|  | which causes an interrupt for every move of the mouse. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | void mouse_init() | ||||||
|  | { | ||||||
|  | 	ps2_clear_buffer(); | ||||||
|  | 
 | ||||||
|  | 	uint8_t config = ps2_config_get(); | ||||||
|  | 	config |= PS2_CONFIG_PORTA_IRQ; | ||||||
|  | 	config |= PS2_CONFIG_PORTB_IRQ; | ||||||
|  | 	config &= ~PS2_CONFIG_PORTA_CLOCK; | ||||||
|  | 	config &= ~PS2_CONFIG_PORTB_CLOCK; | ||||||
|  | 	config |= PS2_CONFIG_PORTA_TRANSLATE; | ||||||
|  | 	ps2_config_set(config); | ||||||
|  | 
 | ||||||
|  | 	ps2_mouse_command(PS2_MOUSE_COMMAND_RESET); | ||||||
|  | 	ps2_mouse_command(PS2_MOUSE_COMMAND_ENABLE_DEVICE); | ||||||
|  | 	ps2_mouse_command(PS2_MOUSE_COMMAND_ENABLE_STREAMING); | ||||||
|  | 
 | ||||||
|  | 	interrupt_register(44, mouse_interrupt); | ||||||
|  | 	interrupt_enable(44); | ||||||
|  | 
 | ||||||
|  | 	printf("mouse: ready\n"); | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user