Files
vystem/shelter/lib/src/devs/input/ps2.c
2026-05-27 19:34:54 +02:00

778 lines
33 KiB
C

// 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;
}