// SPDX-License-Identifier: MPL-2.0 #include "devs/input/ps2.h" #include "devs/input/kbd.h" #include "irq/irq.h" enum ps2_scancode_state { PS2_SCANCODE_STATE_NORMAL, PS2_SCANCODE_STATE_E0, PS2_SCANCODE_STATE_E1 }; enum ps2_command_state { PS2_COMMAND_STATE_IDLE, PS2_COMMAND_STATE_WAIT_ACK, PS2_COMMAND_STATE_WAIT_LED_ACK_1, PS2_COMMAND_STATE_WAIT_LED_ACK_2, PS2_COMMAND_STATE_WAIT_SC_SET_ACK_1, PS2_COMMAND_STATE_WAIT_SC_SET_ACK_2, PS2_COMMAND_STATE_WAIT_IDENTIFY_ACK, PS2_COMMAND_STATE_WAIT_IDENTIFY_BYTE_1, PS2_COMMAND_STATE_WAIT_IDENTIFY_BYTE_2, PS2_COMMAND_STATE_WAIT_ECHO, PS2_COMMAND_STATE_WAIT_RESET_ACK, PS2_COMMAND_STATE_WAIT_RESET_RESULT }; typedef struct { enum ps2_scancode_state scancode_state; enum ps2_command_state command_state; sh_ps2_KBD_COMMAND current_command; sh_uint8 last_byte; sh_uint8 e1_bytes_left; sh_uint8 e1_bytes_idx; sh_uint8 e1_bytes_array[5]; sh_bool command_in_progress; sh_uint8 keyboard_id[2]; sh_uint8 keyboard_id_len; sh_bool scanning_enabled; sh_bool shift_state; sh_bool l_alt_state; sh_bool r_alt_state; sh_bool l_ctrl_state; sh_bool r_ctrl_state; sh_bool win_state; sh_bool capslock_state; sh_bool numlock_state; sh_kbd_DRIVER_HANDLE *d_handle; } ps2_state_template; ps2_state_template ps2_state; static void sh_ps2_send_keyboard_command(sh_uint8 cmd,sh_uint8 data,sh_bool has_data) { ps2_state.current_command.command=cmd; ps2_state.current_command.data=data; ps2_state.current_command.has_data=has_data; ps2_state.command_in_progress=SH_TRUE; while (sh_ps2_cant_write()); sh_asm_outb(SH_PS2_PORT_DATA,cmd); } SH_STATUS sh_ps2_send_keyboard_command_wait(sh_uint8 cmd,sh_uint8 data,sh_bool has_data) { sh_ps2_send_keyboard_command(cmd,data,has_data); while (ps2_state.command_in_progress) { asm volatile("hlt"); } return SH_STATUS_SUCCESS; } // Because we don't have a scheduler or kernel threads/worker, we can't properly update these leds for the moment __attribute__((__unused__)) static void sh_ps2_update_leds() { sh_uint8 led_byte=0; if (ps2_state.numlock_state) { led_byte|=SH_PS2_KBD_LEDS_NUM_LOCK; } if (ps2_state.capslock_state) { led_byte|=SH_PS2_KBD_LEDS_CAPS_LOCK; } ps2_state.command_state=PS2_COMMAND_STATE_WAIT_LED_ACK_1; sh_ps2_send_keyboard_command_wait(SH_PS2_KBD_COMMAND_SET_LEDS,led_byte,SH_TRUE); return; } static SH_STATUS sh_ps2_handle_command_response(sh_uint8 data) { if (ps2_state.command_state==PS2_COMMAND_STATE_WAIT_ACK) { if (data==SH_PS2_KBD_ACK) { if (ps2_state.current_command.command==SH_PS2_KBD_COMMAND_DISABLE_SCANNING) { ps2_state.scanning_enabled=SH_FALSE; } else if (ps2_state.current_command.command==SH_PS2_KBD_COMMAND_ENABLE_SCANNING) { ps2_state.scanning_enabled=SH_TRUE; } ps2_state.command_state=PS2_COMMAND_STATE_IDLE; ps2_state.current_command.command=0; ps2_state.current_command.data=0; ps2_state.current_command.has_data=SH_FALSE; ps2_state.command_in_progress=SH_FALSE; return SH_STATUS_SUCCESS; } else if (data==SH_PS2_KBD_RESEND) { sh_ps2_send_keyboard_command(ps2_state.current_command.command,ps2_state.current_command.data,ps2_state.current_command.has_data); return SH_STATUS_SUCCESS; } } else if (ps2_state.command_state==PS2_COMMAND_STATE_WAIT_ECHO) { if (data==SH_PS2_KBD_COMMAND_ECHO) { ps2_state.command_state=PS2_COMMAND_STATE_IDLE; ps2_state.command_in_progress=SH_FALSE; return SH_STATUS_SUCCESS; } else { sh_iprint(SH_DEBUG,"wrong echo\n"); return SH_STATUS_SUCCESS; } } else if (ps2_state.command_state==PS2_COMMAND_STATE_WAIT_IDENTIFY_ACK) { if (data==SH_PS2_KBD_ACK) { ps2_state.keyboard_id_len=0; ps2_state.command_state=PS2_COMMAND_STATE_WAIT_IDENTIFY_BYTE_1; return SH_STATUS_SUCCESS; } else if (data==SH_PS2_KBD_RESEND) { sh_ps2_send_keyboard_command(SH_PS2_KBD_COMMAND_IDENTIFY,0,SH_FALSE); ps2_state.command_state=PS2_COMMAND_STATE_WAIT_IDENTIFY_ACK; return SH_STATUS_SUCCESS; } } else if (ps2_state.command_state==PS2_COMMAND_STATE_WAIT_IDENTIFY_BYTE_1) { ps2_state.keyboard_id[0]=data; ps2_state.keyboard_id_len=1; ps2_state.command_state=PS2_COMMAND_STATE_WAIT_IDENTIFY_BYTE_2; return SH_STATUS_SUCCESS; } else if (ps2_state.command_state==PS2_COMMAND_STATE_WAIT_IDENTIFY_BYTE_2) { ps2_state.keyboard_id[1]=data; ps2_state.keyboard_id_len=2; ps2_state.command_state=PS2_COMMAND_STATE_IDLE; ps2_state.command_in_progress=SH_FALSE; return SH_STATUS_SUCCESS; } else if (ps2_state.command_state==PS2_COMMAND_STATE_WAIT_LED_ACK_1) { if (data==SH_PS2_KBD_ACK) { while (sh_ps2_cant_write()); sh_asm_outb(SH_PS2_PORT_DATA,ps2_state.current_command.data); ps2_state.command_state=PS2_COMMAND_STATE_WAIT_LED_ACK_2; return SH_STATUS_SUCCESS; } else if (data==SH_PS2_KBD_RESEND) { sh_ps2_send_keyboard_command(SH_PS2_KBD_COMMAND_SET_LEDS,ps2_state.current_command.data,SH_TRUE); ps2_state.command_state=PS2_COMMAND_STATE_WAIT_LED_ACK_1; return SH_STATUS_SUCCESS; } } else if (ps2_state.command_state==PS2_COMMAND_STATE_WAIT_LED_ACK_2) { if (data==SH_PS2_KBD_ACK) { ps2_state.command_state=PS2_COMMAND_STATE_IDLE; ps2_state.command_in_progress=SH_FALSE; return SH_STATUS_SUCCESS; } else if (data==SH_PS2_KBD_RESEND) { while (sh_ps2_cant_write()); sh_asm_outb(SH_PS2_PORT_DATA,ps2_state.current_command.data); return SH_STATUS_SUCCESS; } } else if (ps2_state.command_state==PS2_COMMAND_STATE_WAIT_RESET_ACK) { if (data==SH_PS2_KBD_ACK) { ps2_state.command_state=PS2_COMMAND_STATE_WAIT_RESET_RESULT; return SH_STATUS_SUCCESS; } else if (data==SH_PS2_KBD_RESEND) { sh_ps2_send_keyboard_command(SH_PS2_KBD_COMMAND_RESET,0,SH_FALSE); ps2_state.command_state=PS2_COMMAND_STATE_WAIT_RESET_ACK; return SH_STATUS_SUCCESS; } } else if (ps2_state.command_state==PS2_COMMAND_STATE_WAIT_RESET_RESULT) { if (data==0xAA || data==0xFC) { ps2_state.command_state=PS2_COMMAND_STATE_IDLE; ps2_state.command_in_progress=SH_FALSE; return SH_STATUS_SUCCESS; } else { sh_iprintf(SH_DEBUG,"wrong reset\n"); } } else if (ps2_state.command_state==PS2_COMMAND_STATE_WAIT_SC_SET_ACK_1) { if (data==SH_PS2_KBD_ACK) { while (sh_ps2_cant_write()); sh_asm_outb(SH_PS2_PORT_DATA,ps2_state.current_command.data); ps2_state.command_state=PS2_COMMAND_STATE_WAIT_SC_SET_ACK_2; return SH_STATUS_SUCCESS; } else if (data==SH_PS2_KBD_RESEND) { sh_ps2_send_keyboard_command(SH_PS2_KBD_COMMAND_SC_SET,ps2_state.current_command.data,SH_TRUE); ps2_state.command_state=PS2_COMMAND_STATE_WAIT_SC_SET_ACK_1; return SH_STATUS_SUCCESS; } } else if (ps2_state.command_state==PS2_COMMAND_STATE_WAIT_SC_SET_ACK_2) { if (data==SH_PS2_KBD_ACK) { ps2_state.command_state=PS2_COMMAND_STATE_IDLE; ps2_state.command_in_progress=SH_FALSE; return SH_STATUS_SUCCESS; } else if (data==SH_PS2_KBD_RESEND) { while (sh_ps2_cant_write()); sh_asm_outb(SH_PS2_PORT_DATA,ps2_state.current_command.data); return SH_STATUS_SUCCESS; } } return SH_STATUS_INVALID_STATE; } static inline sh_bool sh_ps2_shift() { return ps2_state.shift_state; } static inline sh_bool sh_ps2_alt() { return ps2_state.l_alt_state || ps2_state.r_alt_state; } static inline sh_bool sh_ps2_ctrl() { return ps2_state.l_ctrl_state || ps2_state.r_ctrl_state; } static void sh_ps2_handle_scancodes(sh_uint8 data) { if (data==0xE0 && ps2_state.scancode_state==PS2_SCANCODE_STATE_NORMAL) { ps2_state.scancode_state=PS2_SCANCODE_STATE_E0; return; } else if (data==0xE1 && ps2_state.scancode_state==PS2_SCANCODE_STATE_NORMAL) { ps2_state.scancode_state=PS2_SCANCODE_STATE_E1; ps2_state.e1_bytes_left=5; ps2_state.e1_bytes_idx=0; return; } if (ps2_state.scancode_state!=PS2_SCANCODE_STATE_E1) { switch (data) { case 0x1D: { // ctrl pressed if (ps2_state.scancode_state==PS2_SCANCODE_STATE_NORMAL) { sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_LCTRL, .pressed=SH_TRUE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } ps2_state.l_ctrl_state=SH_TRUE; status=sh_kbd_set_flag(ps2_state.d_handle,SH_KBD_FLAG_CTRL,sh_ps2_ctrl()); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error settings ctrl flags: status=%8s\n",status); } return; } else if (ps2_state.scancode_state==PS2_SCANCODE_STATE_E0) { sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_RCTRL, .pressed=SH_TRUE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } ps2_state.r_ctrl_state=SH_TRUE; status=sh_kbd_set_flag(ps2_state.d_handle,SH_KBD_FLAG_CTRL,sh_ps2_ctrl()); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error settings ctrl flags: status=%8s\n",status); } ps2_state.scancode_state=PS2_SCANCODE_STATE_NORMAL; return; } break; } case 0x9D: { // ctrl released if (ps2_state.scancode_state==PS2_SCANCODE_STATE_NORMAL) { sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_LCTRL, .pressed=SH_FALSE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } ps2_state.l_ctrl_state=SH_FALSE; status=sh_kbd_set_flag(ps2_state.d_handle,SH_KBD_FLAG_CTRL,sh_ps2_ctrl()); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error settings ctrl flags: status=%8s\n",status); } return; } else if (ps2_state.scancode_state==PS2_SCANCODE_STATE_E0) { sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_RCTRL, .pressed=SH_FALSE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } ps2_state.r_ctrl_state=SH_FALSE; status=sh_kbd_set_flag(ps2_state.d_handle,SH_KBD_FLAG_CTRL,sh_ps2_ctrl()); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error settings ctrl flags: status=%8s\n",status); } ps2_state.scancode_state=PS2_SCANCODE_STATE_NORMAL; return; } break; } case 0x36: { // right shift pressed sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_RSHIFT, .pressed=SH_TRUE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } ps2_state.shift_state=SH_TRUE; status=sh_kbd_set_flag(ps2_state.d_handle,SH_KBD_FLAG_SHIFT,sh_ps2_shift()); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error settings shift flags: status=%8s\n",status); } return; } case 0x2A: { // left shift pressed sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_LSHIFT, .pressed=SH_TRUE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } ps2_state.shift_state=SH_TRUE; status=sh_kbd_set_flag(ps2_state.d_handle,SH_KBD_FLAG_SHIFT,sh_ps2_shift()); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error settings shift flags: status=%8s\n",status); } return; } case 0xAA: { // left shift released sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_LSHIFT, .pressed=SH_FALSE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } ps2_state.shift_state=SH_FALSE; status=sh_kbd_set_flag(ps2_state.d_handle,SH_KBD_FLAG_SHIFT,sh_ps2_shift()); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error settings shift flags: status=%8s\n",status); } return; } case 0xB6: { // right shift released sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_RSHIFT, .pressed=SH_FALSE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } ps2_state.shift_state=SH_FALSE; status=sh_kbd_set_flag(ps2_state.d_handle,SH_KBD_FLAG_SHIFT,sh_ps2_shift()); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error settings shift flags: status=%8s\n",status); } return; } case 0x38: { // alt pressed if (ps2_state.scancode_state==PS2_SCANCODE_STATE_NORMAL) { sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_LALT, .pressed=SH_TRUE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } ps2_state.l_alt_state=SH_TRUE; status=sh_kbd_set_flag(ps2_state.d_handle,SH_KBD_FLAG_ALT,sh_ps2_alt()); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error settings alt flags: status=%8s\n",status); } return; } else if (ps2_state.scancode_state==PS2_SCANCODE_STATE_E0) { sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_RALT, .pressed=SH_TRUE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } ps2_state.r_alt_state=SH_TRUE; status=sh_kbd_set_flag(ps2_state.d_handle,SH_KBD_FLAG_ALT,sh_ps2_alt()); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error settings alt flags: status=%8s\n",status); } ps2_state.scancode_state=PS2_SCANCODE_STATE_NORMAL; return; } break; } case 0xB8: { // alt released if (ps2_state.scancode_state==PS2_SCANCODE_STATE_NORMAL) { sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_LALT, .pressed=SH_FALSE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } ps2_state.l_alt_state=SH_FALSE; status=sh_kbd_set_flag(ps2_state.d_handle,SH_KBD_FLAG_ALT,sh_ps2_alt()); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error settings alt flags: status=%8s\n",status); } return; } else if (ps2_state.scancode_state==PS2_SCANCODE_STATE_E0) { sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_RALT, .pressed=SH_FALSE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } ps2_state.r_alt_state=SH_FALSE; status=sh_kbd_set_flag(ps2_state.d_handle,SH_KBD_FLAG_ALT,sh_ps2_alt()); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error settings alt flags: status=%8s\n",status); } ps2_state.scancode_state=PS2_SCANCODE_STATE_NORMAL; return; } break; } case 0xC5: { // numlock released sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_NUMLOCK, .pressed=SH_FALSE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } ps2_state.numlock_state=ps2_state.numlock_state?SH_FALSE:SH_TRUE; status=sh_kbd_set_flag(ps2_state.d_handle,SH_KBD_FLAG_VER_NUM,ps2_state.numlock_state); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error settings numlock flags: status=%8s\n",status); } return; } case 0xBA: { // capslock released sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_CAPSLOCK, .pressed=SH_FALSE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } ps2_state.capslock_state=ps2_state.capslock_state?SH_FALSE:SH_TRUE; status=sh_kbd_set_flag(ps2_state.d_handle,SH_KBD_FLAG_VER_MAJ,ps2_state.capslock_state); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error settings capslock flags: status=%8s\n",status); } return; } case 0x45: { // numlock pressed sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_NUMLOCK, .pressed=SH_TRUE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } return; } case 0x3A: { // capslock pressed sh_kbd_EVENT ev={ .scancode=SH_KBD_CONTEXT_CAPSLOCK, .pressed=SH_TRUE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_CONTEXT, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } return; } default: { break; } } } if (ps2_state.scancode_state==PS2_SCANCODE_STATE_NORMAL) { if (data & (1u<<7)) { sh_kbd_EVENT ev={ .scancode=0x0000|data, .pressed=SH_FALSE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_PS2_NORMAL, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } return; } else { sh_kbd_EVENT ev={ .scancode=0x0000|data, .pressed=SH_TRUE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_PS2_NORMAL, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } return; } } else if (ps2_state.scancode_state==PS2_SCANCODE_STATE_E0) { if (data & (1u<<7)) { sh_kbd_EVENT ev={ .scancode=0xE000|data, .pressed=SH_FALSE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_PS2_E0, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } return; } else { sh_kbd_EVENT ev={ .scancode=0xE000|data, .pressed=SH_TRUE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_PS2_E0, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } return; } } else if (ps2_state.scancode_state==PS2_SCANCODE_STATE_E1) { ps2_state.e1_bytes_array[ps2_state.e1_bytes_idx]=data; ps2_state.e1_bytes_left--; ps2_state.e1_bytes_idx++; if (ps2_state.e1_bytes_idx==5 && ps2_state.e1_bytes_left==0) ps2_state.scancode_state=PS2_SCANCODE_STATE_NORMAL; sh_kbd_EVENT ev={ .scancode=0, .pressed=SH_TRUE, .context_shift_win=(ps2_state.win_state<<1)|(sh_ps2_shift()), .context_alt=(ps2_state.l_alt_state<<1)|(ps2_state.r_alt_state), .context_ctrl=(ps2_state.l_ctrl_state<<1)|(ps2_state.r_ctrl_state), .context_lock_status=(ps2_state.capslock_state<<1)|(ps2_state.numlock_state), .event_type=SH_EVENT_TYPE_KBD_PAUSE, .timestamp=sh_tsc_get_kernel_current_tsc() }; SH_STATUS status=sh_kbd_push_events(ps2_state.d_handle,&ev); if (sh_status_error(status)) { sh_iprintf(SH_WARNING,"Error pushing events: status=%8s\n",status); } return; } } void sh_ps2_irq1_handler() { sh_uint8 data; if (!sh_ps2_output_ready()) return; data=sh_asm_inb(SH_PS2_PORT_DATA); ps2_state.last_byte=data; if (ps2_state.command_in_progress) { sh_ps2_handle_command_response(data); return; } if (ps2_state.scanning_enabled) { sh_ps2_handle_scancodes(data); } return; } SH_STATUS sh_ps2_driver_init() { sh_asm_cli(); // flush output buffer while (sh_ps2_output_ready()) { sh_asm_inb(SH_PS2_PORT_DATA); } // debug log status and config byte sh_printf(SH_DEBUG,"PS2 status byte initial value: 0x%1U\n",sh_ps2_read_status()); sh_printf(SH_DEBUG,"PS2 config byte initial value: 0x%1U\n",sh_ps2_read_config()); // disable ps2 ports while (sh_ps2_cant_write()); sh_asm_outb(SH_PS2_PORT_COMMAND,SH_PS2_COMMAND_PORT1_DISABLE); while (sh_ps2_cant_write()); sh_asm_outb(SH_PS2_PORT_COMMAND,SH_PS2_COMMAND_PORT2_DISABLE); // reflush output buffer while (sh_ps2_output_ready()) { sh_asm_inb(SH_PS2_PORT_DATA); } // disable IRQs sh_uint8 cfg=sh_ps2_read_config(); cfg&= (sh_uint8)~SH_PS2_CONFIG_PORT1_IRQ; cfg&= (sh_uint8)~SH_PS2_CONFIG_PORT2_IRQ; sh_ps2_write_config(cfg); // ps2 self test while (sh_ps2_cant_write()); sh_asm_outb(SH_PS2_PORT_COMMAND,SH_PS2_COMMAND_PS2_SELF_TEST); while (!sh_ps2_output_ready()); sh_uint8 result=sh_asm_inb(SH_PS2_PORT_DATA); if (result!=SH_PS2_SELF_TEST_SUCCESS) return SH_STATUS_TEST_FAILED; // port1 test while (sh_ps2_cant_write()); sh_asm_outb(SH_PS2_PORT_COMMAND,SH_PS2_COMMAND_PORT1_TEST); while (!sh_ps2_output_ready()); result=sh_asm_inb(SH_PS2_PORT_DATA); if (result!=SH_PS2_PORTS_SELF_TEST_SUCCESS) return SH_STATUS_TEST_FAILED; // enable port1 while (sh_ps2_cant_write()); sh_asm_outb(SH_PS2_PORT_COMMAND,SH_PS2_COMMAND_PORT1_ENABLE); // enable translation cfg=sh_ps2_read_config(); cfg|=SH_PS2_CONFIG_TRANSLATION; sh_ps2_write_config(cfg); // initialize ps2_state struct ps2_state.scancode_state=PS2_SCANCODE_STATE_NORMAL; ps2_state.command_state=PS2_COMMAND_STATE_IDLE; ps2_state.current_command.command=0; ps2_state.current_command.data=0; ps2_state.current_command.has_data=SH_FALSE; ps2_state.last_byte=0; ps2_state.e1_bytes_left=0; ps2_state.command_in_progress=SH_FALSE; ps2_state.keyboard_id[0]=0; ps2_state.keyboard_id[1]=0; ps2_state.keyboard_id_len=0; ps2_state.shift_state=SH_FALSE; ps2_state.l_alt_state=SH_FALSE; ps2_state.r_alt_state=SH_FALSE; ps2_state.l_ctrl_state=SH_FALSE; ps2_state.r_ctrl_state=SH_FALSE; ps2_state.win_state=SH_FALSE; ps2_state.capslock_state=SH_FALSE; ps2_state.numlock_state=SH_FALSE; // enabling IRQ1 cfg=sh_ps2_read_config(); cfg|=(sh_uint8)SH_PS2_CONFIG_PORT1_IRQ; sh_ps2_write_config(cfg); SH_STATUS status=sh_irq_legacy_register_handler(1,&sh_ps2_irq1_handler); if (sh_status_error(status)) return status; sh_asm_sti(); // disabling scanning ps2_state.command_state=PS2_COMMAND_STATE_WAIT_ACK; sh_ps2_send_keyboard_command_wait(SH_PS2_KBD_COMMAND_DISABLE_SCANNING,0,SH_FALSE); // set leds ps2_state.command_state=PS2_COMMAND_STATE_WAIT_LED_ACK_1; sh_ps2_send_keyboard_command_wait(SH_PS2_KBD_COMMAND_SET_LEDS,0,SH_TRUE); // identify keyboard ps2_state.command_state=PS2_COMMAND_STATE_WAIT_IDENTIFY_ACK; sh_ps2_send_keyboard_command_wait(SH_PS2_KBD_COMMAND_IDENTIFY,0,SH_FALSE); sh_printf(SH_DEBUG,"PS2 identifier value : 0x%1U 0x%1U\n",ps2_state.keyboard_id[0],ps2_state.keyboard_id[1]); // register ps2 keyboard status=sh_kbd_register_ps2(SH_TRUE,&ps2_state.d_handle); if (sh_status_error(status)) return status; // enable scanning ps2_state.command_state=PS2_COMMAND_STATE_WAIT_ACK; sh_ps2_send_keyboard_command_wait(SH_PS2_KBD_COMMAND_ENABLE_SCANNING,0,SH_FALSE); return SH_STATUS_SUCCESS; } SH_STATUS sh_ps2_disable_scanning() { ps2_state.command_state=PS2_COMMAND_STATE_WAIT_ACK; sh_ps2_send_keyboard_command_wait(SH_PS2_KBD_COMMAND_DISABLE_SCANNING,0,SH_FALSE); return SH_STATUS_SUCCESS; } SH_STATUS sh_ps2_enable_scanning() { ps2_state.command_state=PS2_COMMAND_STATE_WAIT_ACK; sh_ps2_send_keyboard_command_wait(SH_PS2_KBD_COMMAND_ENABLE_SCANNING,0,SH_FALSE); return SH_STATUS_SUCCESS; }