From 7cccf9556d2bcdb3b45706f724b92c55dd91914b Mon Sep 17 00:00:00 2001 From: No0ne Date: Sun, 11 Sep 2022 18:30:37 +0200 Subject: [PATCH] alpha-0.2 - mouse wheel working, cleanup still needed --- CMakeLists.txt | 2 +- README.md | 3 ++- ps2x2pico.c | 57 +++++++++++++++++++++++++++++-------------------- ps2x2pico.uf2 | Bin 77312 -> 77824 bytes 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 59d7578..6afca08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ pico_sdk_init() add_executable(ps2x2pico ps2x2pico.c) pico_set_program_name(ps2x2pico "ps2x2pico") -pico_set_program_version(ps2x2pico "0.1") +pico_set_program_version(ps2x2pico "0.2") pico_enable_stdio_uart(ps2x2pico 1) pico_enable_stdio_usb(ps2x2pico 0) diff --git a/README.md b/README.md index 8165545..6ecf66c 100644 --- a/README.md +++ b/README.md @@ -37,4 +37,5 @@ make * https://wiki.osdev.org/PS/2_Keyboard * https://wiki.osdev.org/PS/2_Mouse * https://wiki.osdev.org/Mouse_Input -* https://wiki.osdev.org/%228042%22_PS/2_Controller \ No newline at end of file +* https://wiki.osdev.org/%228042%22_PS/2_Controller +* https://isdaman.com/alsos/hardware/mouse/ps2interface.htm \ No newline at end of file diff --git a/ps2x2pico.c b/ps2x2pico.c index 4f456e5..15922ff 100644 --- a/ps2x2pico.c +++ b/ps2x2pico.c @@ -311,7 +311,7 @@ void ms2_receive() { if(received == 0xf2) { while(!ms2_send(0xfa)); - while(!ms2_send(0x00)); + while(!ms2_send(0x03)); return; } @@ -342,19 +342,19 @@ int64_t repeat_callback(alarm_id_t id, void *user_data) { void irq_callback(uint gpio, uint32_t events) { if(gpio == KBCLK && !sending && !gpio_get(KBDAT)) { - printf("IRQ KBDAT "); + printf("IRQ KB "); receiving = true; } if(gpio == MSCLK && !ms2send && !gpio_get(MSDAT)) { - printf("IRQ MSDAT "); + printf("IRQ MS "); ms2recv = true; } } void main() { board_init(); - printf("ps2x2pico-0.1\n"); + printf("ps2x2pico-0.2\n"); gpio_init(KBCLK); gpio_init(KBDAT); @@ -395,19 +395,24 @@ void main() { void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) { printf("HID device address = %d, instance = %d is mounted\n", dev_addr, instance); - const char* protocol_str[] = { "None", "Keyboard", "Mouse" }; - uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); - printf("HID Interface Protocol = %s\n", protocol_str[itf_protocol]); - - if(itf_protocol == HID_ITF_PROTOCOL_KEYBOARD) { - kbd_addr = dev_addr; - kbd_inst = instance; + switch(tuh_hid_interface_protocol(dev_addr, instance)) { + case HID_ITF_PROTOCOL_KEYBOARD: + printf("HID Interface Protocol = Keyboard\n"); + tuh_hid_receive_report(dev_addr, instance); + + blink = true; + add_alarm_in_ms(100, blink_callback, NULL, false); + + kbd_addr = dev_addr; + kbd_inst = instance; + break; - blink = true; - add_alarm_in_ms(100, blink_callback, NULL, false); + case HID_ITF_PROTOCOL_MOUSE: + printf("HID Interface Protocol = Mouse\n"); + tuh_hid_set_protocol(dev_addr, instance, HID_PROTOCOL_REPORT); + tuh_hid_receive_report(dev_addr, instance); + break; } - - tuh_hid_receive_report(dev_addr, instance); } void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) { @@ -419,31 +424,37 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); if(itf_protocol == HID_ITF_PROTOCOL_MOUSE) { - printf("HID receive boot mouse report\n"); board_led_write(1); - uint8_t buttons = report[0] + 8; + uint8_t s = report[0] + 8; uint8_t x = report[1] & 0x7f; uint8_t y = report[2] & 0x7f; + uint8_t z = report[3] & 7; if(report[1] >> 7) { - buttons = buttons + 0x10; - x = x + 0x80; + s += 0x10; + x += 0x80; } if(report[2] >> 7) { y = 0x80 - y; } else if(y) { - buttons = buttons + 0x20; - y = 0xff - y; + s += 0x20; + y = 0x100 - y; + } + + if(report[3] >> 7) { + z = 0x8 - z; + } else if(z) { + z = 0x100 - z; } - ms2_send(buttons); ms2_send(x); ms2_send(y); //ms2_send(0x00); + ms2_send(s); ms2_send(x); + ms2_send(y); ms2_send(z); board_led_write(0); } if(enabled && itf_protocol == HID_ITF_PROTOCOL_KEYBOARD) { - printf("HID receive boot keyboard report\n"); board_led_write(1); if(report[0] != prevhid[0]) { diff --git a/ps2x2pico.uf2 b/ps2x2pico.uf2 index ba176bbef15878a05c2c4cf89675d12662112001..4715e94f7dbd3f875358b9f770dcaf174bd89013 100644 GIT binary patch delta 10193 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 delta 9874 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