New features, bugfixes and optimizations
Some of the features implemented in this release are: - TinyUSB used to handle HOST management as well - USB hub support (tried an ancient one and it worked) - Early and buggy support for mouse on the keyboard side but have no unified usb receivers to test - Rudimentary HID report descriptor parsing, support for mice that don't send wheel data unless in report protocol mode - Implemented queueing for keyboard/mouse messages with hid report send verification - Split firmware upgrade shortcut to two now it's left-shift + F12 + A + right shift to write board A left-shift + F12 + B + right shift to write board B - Fixed keyboard stuck in outputing chars if you hold down a key and change screens while doing it - Implemented cursor hiding, so the screen we are moving away from parks cursor at top right corner and it looks more natural and feels intuitive - Implemented switch lock, use Ctrl + L to lock and unlock desktop switching - Implemented jump threshold, works like barrier opacity - you can define if mouse immediately jumps over or you need to give it a bit of a "nudge"
This commit is contained in:
parent
6e4eea4b27
commit
560f3dca74
|
@ -6,6 +6,8 @@ set(PICO_BOARD=pico)
|
|||
include(pico_sdk_import.cmake)
|
||||
set(CMAKE_CXX_FLAGS "-Ofast -Wall -mcpu=cortex-m0plus -mtune=cortex-m0plus")
|
||||
|
||||
set(PICO_COPY_TO_RAM 1)
|
||||
|
||||
project(deskhop_project C CXX ASM)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
@ -31,6 +33,7 @@ target_include_directories(Pico-PIO-USB PRIVATE ${PICO_PIO_USB_DIR})
|
|||
|
||||
set(COMMON_SOURCES
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/usb_descriptors.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/hid_parser.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/utils.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/handlers.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/setup.c
|
||||
|
@ -40,6 +43,8 @@ set(COMMON_SOURCES
|
|||
${CMAKE_CURRENT_LIST_DIR}/src/uart.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/usb.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/main.c
|
||||
${PICO_TINYUSB_PATH}/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
|
||||
${PICO_TINYUSB_PATH}/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
|
||||
)
|
||||
|
||||
set(COMMON_INCLUDES
|
||||
|
@ -51,8 +56,10 @@ set(COMMON_LINK_LIBRARIES
|
|||
pico_stdlib
|
||||
hardware_uart
|
||||
hardware_gpio
|
||||
hardware_pio
|
||||
|
||||
tinyusb_device
|
||||
tinyusb_host
|
||||
pico_multicore
|
||||
Pico-PIO-USB
|
||||
)
|
||||
|
@ -61,7 +68,7 @@ set(COMMON_LINK_LIBRARIES
|
|||
add_executable(board_A)
|
||||
|
||||
target_sources(board_A PUBLIC ${COMMON_SOURCES})
|
||||
target_compile_definitions(board_A PRIVATE BOARD_ROLE=0)
|
||||
target_compile_definitions(board_A PRIVATE BOARD_ROLE=0 PIO_USB_USE_TINYUSB=1 PIO_USB_DP_PIN_DEFAULT=14)
|
||||
target_include_directories(board_A PUBLIC ${COMMON_INCLUDES})
|
||||
target_link_libraries(board_A PUBLIC ${COMMON_LINK_LIBRARIES})
|
||||
|
||||
|
@ -72,7 +79,7 @@ pico_add_extra_outputs(board_A)
|
|||
# Pico B - Mouse
|
||||
add_executable(board_B)
|
||||
|
||||
target_compile_definitions(board_B PRIVATE BOARD_ROLE=1)
|
||||
target_compile_definitions(board_B PRIVATE BOARD_ROLE=1 PIO_USB_USE_TINYUSB=1 PIO_USB_DP_PIN_DEFAULT=14)
|
||||
target_sources(board_B PUBLIC ${COMMON_SOURCES})
|
||||
target_include_directories(board_B PUBLIC ${COMMON_INCLUDES})
|
||||
target_link_libraries(board_B PUBLIC ${COMMON_LINK_LIBRARIES})
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
* This file is part of DeskHop (https://github.com/hrvach/deskhop).
|
||||
* Copyright (c) 2024 Hrvoje Cavrak
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
|
||||
/**=================================================== *
|
||||
|
@ -5,15 +22,31 @@
|
|||
* =================================================== */
|
||||
|
||||
void output_toggle_hotkey_handler(device_state_t* state) {
|
||||
/* If switching explicitly disabled, return immediately */
|
||||
if (state->switch_lock)
|
||||
return;
|
||||
|
||||
state->active_output ^= 1;
|
||||
switch_output(state->active_output);
|
||||
};
|
||||
|
||||
void fw_upgrade_hotkey_handler(device_state_t* state) {
|
||||
send_value(ENABLE, FIRMWARE_UPGRADE_MSG);
|
||||
/* This key combo puts board A in firmware upgrade mode */
|
||||
void fw_upgrade_hotkey_handler_A(device_state_t* state) {
|
||||
reset_usb_boot(1 << PICO_DEFAULT_LED_PIN, 0);
|
||||
};
|
||||
|
||||
/* This key combo puts board B in firmware upgrade mode */
|
||||
void fw_upgrade_hotkey_handler_B(device_state_t* state) {
|
||||
send_value(ENABLE, FIRMWARE_UPGRADE_MSG);
|
||||
};
|
||||
|
||||
|
||||
void switchlock_hotkey_handler(device_state_t* state) {
|
||||
state->switch_lock ^= 1;
|
||||
send_value(state->switch_lock, SWITCH_LOCK_MSG);
|
||||
}
|
||||
|
||||
|
||||
void mouse_zoom_hotkey_handler(device_state_t* state) {
|
||||
if (state->mouse_zoom)
|
||||
return;
|
||||
|
@ -35,29 +68,26 @@ void all_keys_released_handler(device_state_t* state) {
|
|||
|
||||
void handle_keyboard_uart_msg(uart_packet_t* packet, device_state_t* state) {
|
||||
if (state->active_output == ACTIVE_OUTPUT_B) {
|
||||
hid_keyboard_report_t* report = (hid_keyboard_report_t*)packet->data;
|
||||
|
||||
tud_hid_keyboard_report(REPORT_ID_KEYBOARD, report->modifier, report->keycode);
|
||||
queue_kbd_report((hid_keyboard_report_t*)packet->data, state);
|
||||
state->last_activity[ACTIVE_OUTPUT_B] = time_us_64();
|
||||
}
|
||||
}
|
||||
|
||||
void handle_mouse_abs_uart_msg(uart_packet_t* packet, device_state_t* state) {
|
||||
if (state->active_output == ACTIVE_OUTPUT_A) {
|
||||
const hid_abs_mouse_report_t* mouse_report = (hid_abs_mouse_report_t*)packet->data;
|
||||
|
||||
tud_hid_abs_mouse_report(REPORT_ID_MOUSE, mouse_report->buttons, mouse_report->x,
|
||||
mouse_report->y, mouse_report->wheel, 0);
|
||||
|
||||
state->last_activity[ACTIVE_OUTPUT_A] = time_us_64();
|
||||
}
|
||||
hid_abs_mouse_report_t* mouse_report = (hid_abs_mouse_report_t*)packet->data;
|
||||
queue_mouse_report(mouse_report, state);
|
||||
state->last_activity[ACTIVE_OUTPUT_A] = time_us_64();
|
||||
}
|
||||
|
||||
void handle_output_select_msg(uart_packet_t* packet, device_state_t* state) {
|
||||
state->active_output = packet->data[0];
|
||||
if (state->tud_connected)
|
||||
stop_pressing_any_keys(&global_state);
|
||||
|
||||
update_leds(state);
|
||||
}
|
||||
|
||||
/* On firmware upgrade message, reboot into the BOOTSEL fw upgrade mode */
|
||||
void handle_fw_upgrade_msg(void) {
|
||||
reset_usb_boot(1 << PICO_DEFAULT_LED_PIN, 0);
|
||||
}
|
||||
|
@ -67,14 +97,22 @@ void handle_mouse_zoom_msg(uart_packet_t* packet, device_state_t* state) {
|
|||
}
|
||||
|
||||
void handle_set_report_msg(uart_packet_t* packet, device_state_t* state) {
|
||||
// Only board B sends LED state through this message type
|
||||
/* Only board B sends LED state through this message type */
|
||||
state->keyboard_leds[ACTIVE_OUTPUT_B] = packet->data[0];
|
||||
update_leds(state);
|
||||
}
|
||||
|
||||
// Update output variable, set LED on/off and notify the other board
|
||||
void handle_switch_lock_msg(uart_packet_t* packet, device_state_t* state) {
|
||||
state->switch_lock = packet->data[0];
|
||||
}
|
||||
|
||||
/* Update output variable, set LED on/off and notify the other board so they are in sync. */
|
||||
void switch_output(uint8_t new_output) {
|
||||
global_state.active_output = new_output;
|
||||
update_leds(&global_state);
|
||||
send_value(new_output, OUTPUT_SELECT_MSG);
|
||||
|
||||
/* If we were holding a key down and drag the mouse to another screen, the key gets stuck.
|
||||
Changing outputs = no more keypresses on the previous system. */
|
||||
stop_pressing_any_keys(&global_state);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* This file is part of DeskHop (https://github.com/hrvach/deskhop).
|
||||
* Copyright (c) 2024 Hrvoje Cavrak
|
||||
*
|
||||
* Based on the TinyUSB HID parser routine and the amazing USB2N64
|
||||
* adapter (https://github.com/pdaxrom/usb2n64-adapter)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "hid_parser.h"
|
||||
|
||||
#define IS_BLOCK_END (collection.start == collection.end)
|
||||
#define MAX_BUTTONS 16
|
||||
|
||||
enum { SIZE_0_BIT = 0, SIZE_8_BIT = 1, SIZE_16_BIT = 2, SIZE_32_BIT = 3 };
|
||||
|
||||
/* Size is 0, 1, 2, or 3, describing cases of no data, 8-bit, 16-bit,
|
||||
or 32-bit data. */
|
||||
uint32_t get_descriptor_value(uint8_t const *report, int size) {
|
||||
switch (size) {
|
||||
case SIZE_8_BIT:
|
||||
return report[0];
|
||||
case SIZE_16_BIT:
|
||||
return tu_u16(report[1], report[0]);
|
||||
case SIZE_32_BIT:
|
||||
return tu_u32(report[3], report[2], report[1], report[0]);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* We store all globals as unsigned to avoid countless switch/cases.
|
||||
In case of e.g. min/max, we need to treat some data as signed retroactively. */
|
||||
int32_t to_signed(globals_t *data) {
|
||||
switch (data->hdr.size) {
|
||||
case SIZE_8_BIT:
|
||||
return (int8_t)data->val;
|
||||
case SIZE_16_BIT:
|
||||
return (int16_t)data->val;
|
||||
default:
|
||||
return data->val;
|
||||
}
|
||||
}
|
||||
|
||||
/* Given a value struct with size and offset in bits,
|
||||
find and return a value from the HID report */
|
||||
|
||||
int32_t get_report_value(uint8_t* report, report_val_t *val) {
|
||||
/* Calculate the bit offset within the byte */
|
||||
uint8_t offset_in_bits = val->offset % 8;
|
||||
|
||||
/* Calculate the remaining bits in the first byte */
|
||||
uint8_t remaining_bits = 8 - offset_in_bits;
|
||||
|
||||
/* Calculate the byte offset in the array */
|
||||
uint8_t byte_offset = val->offset >> 3;
|
||||
|
||||
/* Create a mask for the specified number of bits */
|
||||
uint32_t mask = (1u << val->size) - 1;
|
||||
|
||||
/* Initialize the result value with the bits from the first byte */
|
||||
int32_t result = report[byte_offset] >> offset_in_bits;
|
||||
|
||||
/* Move to the next byte and continue fetching bits until the desired length is reached */
|
||||
while (val->size > remaining_bits) {
|
||||
result |= report[++byte_offset] << remaining_bits;
|
||||
remaining_bits += 8;
|
||||
}
|
||||
|
||||
/* Apply the mask to retain only the desired number of bits */
|
||||
result = result & mask;
|
||||
|
||||
/* Special case if result is negative.
|
||||
Check if the most significant bit of 'val' is set */
|
||||
if (result & ((mask >> 1) + 1)) {
|
||||
/* If it is set, sign-extend 'val' by filling the higher bits with 1s */
|
||||
result |= (0xFFFFFFFFU << val->size);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* This method is far from a generalized HID descriptor parsing, but should work
|
||||
* well enough to find the basic values we care about to move the mouse around.
|
||||
* Your descriptor for a mouse with 2 wheels and 264 buttons might not parse correctly.
|
||||
**/
|
||||
uint8_t parse_report_descriptor(mouse_t *mouse, uint8_t arr_count,
|
||||
uint8_t const *report, uint16_t desc_len) {
|
||||
|
||||
/* Get these elements and store them in the proper place in the mouse struct
|
||||
* For example, to match wheel, we want collection usage to be HID_USAGE_DESKTOP_MOUSE, page to be HID_USAGE_PAGE_DESKTOP,
|
||||
* usage to be HID_USAGE_DESKTOP_WHEEL, then if all of that is matched we store the value to mouse->wheel */
|
||||
const usage_map_t usage_map[] = {
|
||||
{HID_USAGE_DESKTOP_MOUSE, HID_USAGE_PAGE_BUTTON, HID_USAGE_DESKTOP_POINTER, &mouse->buttons},
|
||||
{HID_USAGE_DESKTOP_MOUSE, HID_USAGE_PAGE_DESKTOP, HID_USAGE_DESKTOP_X, &mouse->move_x},
|
||||
{HID_USAGE_DESKTOP_MOUSE, HID_USAGE_PAGE_DESKTOP, HID_USAGE_DESKTOP_Y, &mouse->move_y},
|
||||
{HID_USAGE_DESKTOP_MOUSE, HID_USAGE_PAGE_DESKTOP, HID_USAGE_DESKTOP_WHEEL, &mouse->wheel},
|
||||
};
|
||||
|
||||
/* Some variables used for keeping tabs on parsing */
|
||||
uint8_t usage_count = 0;
|
||||
uint8_t g_usage = 0;
|
||||
|
||||
uint32_t offset_in_bits = 0;
|
||||
|
||||
uint8_t usages[64] = {0};
|
||||
uint8_t* p_usage = usages;
|
||||
|
||||
collection_t collection = {0};
|
||||
|
||||
/* as tag is 4 bits, there can be 16 different tags in global header type */
|
||||
globals_t globals[16] = {0};
|
||||
|
||||
for (int len = desc_len; len > 0; len--) {
|
||||
header_t header = *(header_t *)report++;
|
||||
uint32_t data = get_descriptor_value(report, header.size);
|
||||
|
||||
switch (header.type) {
|
||||
case RI_TYPE_MAIN:
|
||||
// Keep count of collections, starts and ends
|
||||
collection.start += (header.tag == RI_MAIN_COLLECTION);
|
||||
collection.end += (header.tag == RI_MAIN_COLLECTION_END);
|
||||
|
||||
if (header.tag == RI_MAIN_INPUT) {
|
||||
for (int i = 0; i < globals[RI_GLOBAL_REPORT_COUNT].val; i++) {
|
||||
|
||||
/* If we don't have as many usages as elements, the usage for the previous
|
||||
element applies */
|
||||
if (i && i >= usage_count ) {
|
||||
*(p_usage + i) = *(p_usage + usage_count - 1);
|
||||
}
|
||||
|
||||
const usage_map_t *map = usage_map;
|
||||
|
||||
/* Only focus on the items we care about (buttons, x and y, wheels, etc) */
|
||||
for (int j=0; j<sizeof(usage_map)/sizeof(usage_map[0]); j++, map++) {
|
||||
/* Filter based on usage criteria */
|
||||
if (map->report_usage == g_usage &&
|
||||
map->usage_page == globals[RI_GLOBAL_USAGE_PAGE].val &&
|
||||
map->usage == *(p_usage + i)) {
|
||||
|
||||
/* Buttons are the ones that appear multiple times, will handle them properly
|
||||
For now, let's just aggregate the length and combine them into one :) */
|
||||
if (map->element->size) {
|
||||
map->element->size++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Store the found element's attributes */
|
||||
map->element->offset = offset_in_bits;
|
||||
map->element->size = globals[RI_GLOBAL_REPORT_SIZE].val;
|
||||
map->element->min = to_signed(&globals[RI_GLOBAL_LOGICAL_MIN]);
|
||||
map->element->max = to_signed(&globals[RI_GLOBAL_LOGICAL_MAX]);
|
||||
}
|
||||
};
|
||||
|
||||
/* Iterate <count> times and increase offset by <size> amount, moving by <count> x <size> bits */
|
||||
offset_in_bits += globals[RI_GLOBAL_REPORT_SIZE].val;
|
||||
}
|
||||
/* Advance the usage array pointer by global report count and reset the count variable */
|
||||
p_usage += globals[RI_GLOBAL_REPORT_COUNT].val;
|
||||
usage_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case RI_TYPE_GLOBAL:
|
||||
/* There are just 16 possible tags, store any one that comes along to an array instead of doing
|
||||
switch and 16 cases */
|
||||
globals[header.tag].val = data;
|
||||
globals[header.tag].hdr = header;
|
||||
|
||||
if (header.tag == RI_GLOBAL_REPORT_ID) {
|
||||
/* Important to track, if report IDs are used reports are preceded/offset by a 1-byte ID value */
|
||||
if(g_usage == HID_USAGE_DESKTOP_MOUSE)
|
||||
mouse->report_id = data;
|
||||
|
||||
mouse->uses_report_id = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RI_TYPE_LOCAL:
|
||||
if (header.tag == RI_LOCAL_USAGE) {
|
||||
/* If we are not within a collection, the usage tag applies to the entire section */
|
||||
if (IS_BLOCK_END)
|
||||
g_usage = data;
|
||||
else
|
||||
*(p_usage + usage_count++) = data;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* If header specified some non-zero length data, move by that much to get to the new byte
|
||||
we should interpret as a header element */
|
||||
report += header.size;
|
||||
len -= header.size;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* This file is part of DeskHop (https://github.com/hrvach/deskhop).
|
||||
* Copyright (c) 2024 Hrvoje Cavrak
|
||||
*
|
||||
* Based on the TinyUSB HID parser routine and the amazing USB2N64
|
||||
* adapter (https://github.com/pdaxrom/usb2n64-adapter)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#define MAX_REPORTS 32
|
||||
|
||||
/* Counts how many collection starts and ends we've seen, when they equalize
|
||||
(and not zero), we are at the end of a block */
|
||||
typedef struct {
|
||||
uint8_t start;
|
||||
uint8_t end;
|
||||
} collection_t;
|
||||
|
||||
/* Header byte is unpacked to size/type/tag using this struct */
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t size : 2;
|
||||
uint8_t type : 2;
|
||||
uint8_t tag : 4;
|
||||
} header_t;
|
||||
|
||||
/* We store a header block and corresponding data in an array of these
|
||||
to avoid having to use numerous switch-case checks */
|
||||
typedef struct {
|
||||
header_t hdr;
|
||||
uint32_t val;
|
||||
} globals_t;
|
||||
|
||||
// Extended precision mouse movement information
|
||||
typedef struct {
|
||||
int32_t move_x;
|
||||
int32_t move_y;
|
||||
int32_t wheel;
|
||||
int32_t pan;
|
||||
uint32_t buttons;
|
||||
} mouse_values_t;
|
||||
|
||||
/* Describes where can we find a value in a HID report */
|
||||
typedef struct {
|
||||
uint16_t offset; // In bits
|
||||
uint8_t size; // In bits
|
||||
int32_t min;
|
||||
int32_t max;
|
||||
} report_val_t;
|
||||
|
||||
/* Defines information about HID report format for the mouse. */
|
||||
typedef struct {
|
||||
report_val_t buttons;
|
||||
|
||||
report_val_t move_x;
|
||||
report_val_t move_y;
|
||||
|
||||
report_val_t wheel;
|
||||
|
||||
bool uses_report_id;
|
||||
uint8_t report_id;
|
||||
uint8_t protocol;
|
||||
} mouse_t;
|
||||
|
||||
/* For each element type we're interested in there is an entry
|
||||
in an array of these, defining its usage and in case matched, where to
|
||||
store the data. */
|
||||
typedef struct {
|
||||
uint8_t report_usage;
|
||||
uint8_t usage_page;
|
||||
uint8_t usage;
|
||||
report_val_t* element;
|
||||
} usage_map_t;
|
108
src/keyboard.c
108
src/keyboard.c
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
* This file is part of DeskHop (https://github.com/hrvach/deskhop).
|
||||
* Copyright (c) 2024 Hrvoje Cavrak
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
|
||||
/* ==================================================== *
|
||||
|
@ -5,26 +22,74 @@
|
|||
* ==================================================== */
|
||||
|
||||
hotkey_combo_t hotkeys[] = {
|
||||
// Main keyboard switching hotkey
|
||||
/* Main keyboard switching hotkey */
|
||||
{.modifier = 0,
|
||||
.keys = {HOTKEY_TOGGLE},
|
||||
.key_count = 1,
|
||||
.action_handler = &output_toggle_hotkey_handler},
|
||||
|
||||
// Holding down right ALT slows the mouse down
|
||||
/* Holding down right ALT slows the mouse down */
|
||||
{.modifier = KEYBOARD_MODIFIER_RIGHTALT,
|
||||
.keys = {},
|
||||
.key_count = 0,
|
||||
.action_handler = &mouse_zoom_hotkey_handler},
|
||||
.key_count = 0,
|
||||
.action_handler = &mouse_zoom_hotkey_handler},
|
||||
|
||||
// Hold down left shift + right shift + P + H + X ==> firmware upgrade mode
|
||||
/* Switch lock */
|
||||
{.modifier = KEYBOARD_MODIFIER_RIGHTCTRL,
|
||||
.keys = {HID_KEY_L},
|
||||
.key_count = 1,
|
||||
.action_handler = &switchlock_hotkey_handler},
|
||||
|
||||
/* Hold down left shift + right shift + F12 + A ==> firmware upgrade mode for board A (kbd) */
|
||||
{.modifier = KEYBOARD_MODIFIER_RIGHTSHIFT | KEYBOARD_MODIFIER_LEFTSHIFT,
|
||||
.keys = {HID_KEY_P, HID_KEY_H, HID_KEY_X},
|
||||
.key_count = 3,
|
||||
.action_handler = &fw_upgrade_hotkey_handler}
|
||||
.keys = {HID_KEY_F12, HID_KEY_A},
|
||||
.key_count = 2,
|
||||
.action_handler = &fw_upgrade_hotkey_handler_A},
|
||||
|
||||
/* Hold down left shift + right shift + F12 + B ==> firmware upgrade mode for board B (mouse) */
|
||||
{.modifier = KEYBOARD_MODIFIER_RIGHTSHIFT | KEYBOARD_MODIFIER_LEFTSHIFT,
|
||||
.keys = {HID_KEY_F12, HID_KEY_B},
|
||||
.key_count = 2,
|
||||
.action_handler = &fw_upgrade_hotkey_handler_B}
|
||||
};
|
||||
|
||||
|
||||
/* ==================================================== *
|
||||
* Keyboard Queue Section
|
||||
* ==================================================== */
|
||||
|
||||
void process_kbd_queue_task(device_state_t *state) {
|
||||
hid_keyboard_report_t report;
|
||||
|
||||
/* If we're not connected, we have nowhere to send reports to. */
|
||||
if (!state->tud_connected)
|
||||
return;
|
||||
|
||||
/* Peek first, if there is anything there... */
|
||||
if (!queue_try_peek(&state->kbd_queue, &report))
|
||||
return;
|
||||
|
||||
/* ... try sending it to the host, if it's successful */
|
||||
bool succeeded = tud_hid_keyboard_report(REPORT_ID_KEYBOARD, report.modifier, report.keycode);
|
||||
|
||||
/* ... then we can remove it from the queue. Race conditions shouldn't happen [tm] */
|
||||
if (succeeded)
|
||||
queue_try_remove(&state->kbd_queue, &report);
|
||||
}
|
||||
|
||||
void queue_kbd_report(hid_keyboard_report_t *report, device_state_t *state) {
|
||||
/* It wouldn't be fun to queue up a bunch of messages and then dump them all on host */
|
||||
if (!state->tud_connected)
|
||||
return;
|
||||
|
||||
queue_try_add(&state->kbd_queue, report);
|
||||
}
|
||||
|
||||
void stop_pressing_any_keys(device_state_t *state) {
|
||||
static hid_keyboard_report_t no_keys_pressed_report = {0, 0, {0}};
|
||||
queue_try_add(&state->kbd_queue, &no_keys_pressed_report);
|
||||
}
|
||||
|
||||
/* ==================================================== *
|
||||
* Parse and interpret the keys pressed on the keyboard
|
||||
* ==================================================== */
|
||||
|
@ -46,26 +111,26 @@ void process_keyboard_report(uint8_t* raw_report, int length, device_state_t* st
|
|||
if (length < KBD_REPORT_LENGTH)
|
||||
return;
|
||||
|
||||
// Go through the list of hotkeys, check if any are pressed, then execute their handler
|
||||
/* Go through the list of hotkeys, check if any are pressed, then execute their handler */
|
||||
for (int n = 0; n < sizeof(hotkeys) / sizeof(hotkeys[0]); n++) {
|
||||
if (keypress_check(hotkeys[n], keyboard_report)) {
|
||||
hotkeys[n].action_handler(state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no keys are pressed anymore, take care of checking and deactivating stuff
|
||||
if (no_keys_are_pressed(keyboard_report)) {
|
||||
state->key_pressed = !no_keys_are_pressed(keyboard_report);
|
||||
|
||||
/* If no keys are pressed anymore, take care of checking and deactivating stuff */
|
||||
if (!state->key_pressed) {
|
||||
all_keys_released_handler(state);
|
||||
}
|
||||
|
||||
// If keys need to go to output B, send them through UART, otherwise send a HID report directly
|
||||
/* If keys need to go to output B, send them through UART, otherwise send a HID report directly */
|
||||
if (state->active_output == ACTIVE_OUTPUT_B) {
|
||||
send_packet(raw_report, KEYBOARD_REPORT_MSG, KBD_REPORT_LENGTH);
|
||||
} else {
|
||||
tud_hid_keyboard_report(REPORT_ID_KEYBOARD, keyboard_report->modifier,
|
||||
keyboard_report->keycode);
|
||||
|
||||
} else {
|
||||
queue_kbd_report(keyboard_report, state);
|
||||
state->last_activity[ACTIVE_OUTPUT_A] = time_us_64();
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +143,7 @@ void process_keyboard_report(uint8_t* raw_report, int length, device_state_t* st
|
|||
bool keypress_check(hotkey_combo_t keypress, const hid_keyboard_report_t* report) {
|
||||
int matches = 0;
|
||||
|
||||
// We expect all modifiers specified to be detected in the report
|
||||
/* We expect all modifiers specified to be detected in the report */
|
||||
if (keypress.modifier != (report->modifier & keypress.modifier))
|
||||
return false;
|
||||
|
||||
|
@ -89,13 +154,12 @@ bool keypress_check(hotkey_combo_t keypress, const hid_keyboard_report_t* report
|
|||
break;
|
||||
}
|
||||
}
|
||||
// If any of the keys are not found, we can bail out early.
|
||||
/* If any of the keys are not found, we can bail out early. */
|
||||
if (matches < n + 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Getting here means all of the keys were found.
|
||||
/* Getting here means all of the keys were found. */
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
25
src/led.c
25
src/led.c
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
* This file is part of DeskHop (https://github.com/hrvach/deskhop).
|
||||
* Copyright (c) 2024 Hrvoje Cavrak
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
|
||||
/**==================================================== *
|
||||
|
@ -7,6 +24,10 @@
|
|||
void update_leds(device_state_t* state) {
|
||||
gpio_put(GPIO_LED_PIN, state->active_output == BOARD_ROLE);
|
||||
|
||||
if (BOARD_ROLE == KEYBOARD_PICO_A)
|
||||
pio_usb_kbd_set_leds(state->usb_device, 0, state->keyboard_leds[state->active_output]);
|
||||
// TODO: Will be done in a callback
|
||||
if (BOARD_ROLE == PICO_A) {
|
||||
uint8_t* leds = &(state->keyboard_leds[state->active_output]);
|
||||
tuh_hid_set_report(global_state.kbd_dev_addr, global_state.kbd_instance, 0,
|
||||
HID_REPORT_TYPE_OUTPUT, leds, sizeof(uint8_t));
|
||||
}
|
||||
}
|
||||
|
|
43
src/main.c
43
src/main.c
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
* This file is part of DeskHop (https://github.com/hrvach/deskhop).
|
||||
* Copyright (c) 2024 Hrvoje Cavrak
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
|
||||
/********* Global Variable **********/
|
||||
|
@ -11,7 +28,8 @@ void main(void) {
|
|||
// Wait for the board to settle
|
||||
sleep_ms(10);
|
||||
|
||||
global_state.usb_device = initial_setup();
|
||||
// Initial board setup
|
||||
initial_setup();
|
||||
|
||||
// Initial state, A is the default output
|
||||
switch_output(ACTIVE_OUTPUT_A);
|
||||
|
@ -20,21 +38,16 @@ void main(void) {
|
|||
// USB device task, needs to run as often as possible
|
||||
tud_task();
|
||||
|
||||
// If we are not yet connected to the PC, don't bother with host
|
||||
// If host task becomes too slow, move it to the second core
|
||||
if (global_state.tud_connected) {
|
||||
// Execute HOST task periodically
|
||||
pio_usb_host_task();
|
||||
|
||||
// Query devices and handle reports
|
||||
if (global_state.usb_device && global_state.usb_device->connected) {
|
||||
check_endpoints(&global_state);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify core1 is still running and if so, reset watchdog timer
|
||||
kick_watchdog();
|
||||
|
||||
// Check if there were any keypresses and send them
|
||||
process_kbd_queue_task(&global_state);
|
||||
|
||||
// Check if there were any mouse movements and send them
|
||||
process_mouse_queue_task(&global_state);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void core1_main() {
|
||||
|
@ -44,6 +57,10 @@ void core1_main() {
|
|||
// Update the timestamp, so core0 can figure out if we're dead
|
||||
global_state.core1_last_loop_pass = time_us_64();
|
||||
|
||||
// USB host task, needs to run as often as possible
|
||||
tuh_task();
|
||||
|
||||
// Receives data over serial from the other board
|
||||
receive_char(&in_packet, &global_state);
|
||||
}
|
||||
}
|
||||
|
|
68
src/main.h
68
src/main.h
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
* This file is part of DeskHop (https://github.com/hrvach/deskhop).
|
||||
* Copyright (c) 2024 Hrvoje Cavrak
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
|
@ -10,14 +27,16 @@
|
|||
#include "pico/bootrom.h"
|
||||
#include "pico/multicore.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/util/queue.h"
|
||||
#include "pio_usb.h"
|
||||
#include "tusb.h"
|
||||
#include "usb_descriptors.h"
|
||||
#include "hid_parser.h"
|
||||
#include "user_config.h"
|
||||
|
||||
/********* Misc definitions **********/
|
||||
#define KEYBOARD_PICO_A 0
|
||||
#define MOUSE_PICO_B 1
|
||||
#define PICO_A 0
|
||||
#define PICO_B 1
|
||||
|
||||
#define ACTIVE_OUTPUT_A 0
|
||||
#define ACTIVE_OUTPUT_B 1
|
||||
|
@ -25,21 +44,28 @@
|
|||
#define ENABLE 1
|
||||
#define DISABLE 0
|
||||
|
||||
#define DIRECTION_X 0
|
||||
#define DIRECTION_Y 1
|
||||
|
||||
#define MAX_REPORT_ITEMS 16
|
||||
#define MOUSE_BOOT_REPORT_LEN 4
|
||||
|
||||
/********* Pinout definitions **********/
|
||||
#define PIO_USB_DP_PIN 14 // D+ is pin 14, D- is pin 15
|
||||
#define GPIO_LED_PIN 25 // LED is connected to pin 25 on a PICO
|
||||
|
||||
#if BOARD_ROLE == MOUSE_PICO_B
|
||||
#if BOARD_ROLE == PICO_B
|
||||
#define SERIAL_TX_PIN 16
|
||||
#define SERIAL_RX_PIN 17
|
||||
#elif BOARD_ROLE == KEYBOARD_PICO_A
|
||||
#elif BOARD_ROLE == PICO_A
|
||||
#define SERIAL_TX_PIN 12
|
||||
#define SERIAL_RX_PIN 13
|
||||
#endif
|
||||
|
||||
/********* Serial port definitions **********/
|
||||
#define SERIAL_UART uart0
|
||||
#define SERIAL_BAUDRATE 115200
|
||||
#define SERIAL_BAUDRATE 3686400
|
||||
|
||||
#define SERIAL_DATA_BITS 8
|
||||
#define SERIAL_STOP_BITS 1
|
||||
#define SERIAL_PARITY UART_PARITY_NONE
|
||||
|
@ -66,6 +92,7 @@ enum packet_type_e : uint8_t {
|
|||
FIRMWARE_UPGRADE_MSG = 4,
|
||||
MOUSE_ZOOM_MSG = 5,
|
||||
KBD_SET_REPORT_MSG = 6,
|
||||
SWITCH_LOCK_MSG = 7,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -96,6 +123,9 @@ typedef struct {
|
|||
#define PACKET_LENGTH (TYPE_LENGTH + PACKET_DATA_LENGTH + CHECKSUM_LENGTH)
|
||||
#define RAW_PACKET_LENGTH (START_LENGTH + PACKET_LENGTH)
|
||||
|
||||
#define KBD_QUEUE_LENGTH 128
|
||||
#define MOUSE_QUEUE_LENGTH 256
|
||||
|
||||
#define KEYS_IN_USB_REPORT 6
|
||||
#define KBD_REPORT_LENGTH 8
|
||||
#define MOUSE_REPORT_LENGTH 7
|
||||
|
@ -125,7 +155,9 @@ typedef struct TU_ATTR_PACKED {
|
|||
typedef enum { IDLE, READING_PACKET, PROCESSING_PACKET } receiver_state_t;
|
||||
|
||||
typedef struct {
|
||||
usb_device_t* usb_device; // USB device structure (keyboard or mouse)
|
||||
uint8_t kbd_dev_addr; // Address of the keyboard device
|
||||
uint8_t kbd_instance; // Keyboard instance (d'uh - isn't this a useless comment)
|
||||
|
||||
uint8_t keyboard_leds[2]; // State of keyboard LEDs (index 0 = A, index 1 = B)
|
||||
uint64_t last_activity[2]; // Timestamp of the last input activity (-||-)
|
||||
receiver_state_t receiver_state; // Storing the state for the simple receiver state machine
|
||||
|
@ -136,8 +168,17 @@ typedef struct {
|
|||
int16_t mouse_x; // Store and update the location of our mouse pointer
|
||||
int16_t mouse_y;
|
||||
|
||||
mouse_t mouse_dev; // Mouse device specifics, e.g. stores locations for keys in report
|
||||
queue_t kbd_queue; // Queue that stores keyboard reports
|
||||
queue_t mouse_queue; // Queue that stores mouse reports
|
||||
|
||||
bool tud_connected; // True when TinyUSB device successfully connects
|
||||
bool keyboard_connected; // True when our keyboard is connected locally
|
||||
bool mouse_connected; // True when a mouse is connected locally
|
||||
bool mouse_zoom; // True when "mouse zoom" is enabled
|
||||
bool switch_lock; // True when device is prevented from switching
|
||||
|
||||
bool key_pressed; // We are holding down a key (from the PCs point of view)
|
||||
|
||||
} device_state_t;
|
||||
|
||||
|
@ -146,13 +187,16 @@ void process_mouse_report(uint8_t*, int, device_state_t*);
|
|||
void check_endpoints(device_state_t* state);
|
||||
|
||||
/********* Setup **********/
|
||||
usb_device_t* initial_setup(void);
|
||||
void initial_setup(void);
|
||||
void serial_init(void);
|
||||
void core1_main(void);
|
||||
|
||||
/********* Keyboard **********/
|
||||
bool keypress_check(hotkey_combo_t, const hid_keyboard_report_t*);
|
||||
void process_keyboard_report(uint8_t*, int, device_state_t*);
|
||||
void stop_pressing_any_keys(device_state_t*);
|
||||
void queue_kbd_report(hid_keyboard_report_t*, device_state_t*);
|
||||
void process_kbd_queue_task(device_state_t*);
|
||||
|
||||
/********* Mouse **********/
|
||||
bool tud_hid_abs_mouse_report(uint8_t report_id,
|
||||
|
@ -162,6 +206,11 @@ bool tud_hid_abs_mouse_report(uint8_t report_id,
|
|||
int8_t vertical,
|
||||
int8_t horizontal);
|
||||
|
||||
uint8_t parse_report_descriptor(mouse_t* mouse, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len);
|
||||
int32_t get_report_value(uint8_t* report, report_val_t *val);
|
||||
void process_mouse_queue_task(device_state_t*);
|
||||
void queue_mouse_report(hid_abs_mouse_report_t*, device_state_t*);
|
||||
|
||||
/********* UART **********/
|
||||
void receive_char(uart_packet_t*, device_state_t*);
|
||||
void send_packet(const uint8_t*, enum packet_type_e, int);
|
||||
|
@ -179,9 +228,11 @@ void kick_watchdog(void);
|
|||
|
||||
/********* Handlers **********/
|
||||
void output_toggle_hotkey_handler(device_state_t*);
|
||||
void fw_upgrade_hotkey_handler(device_state_t*);
|
||||
void fw_upgrade_hotkey_handler_A(device_state_t*);
|
||||
void fw_upgrade_hotkey_handler_B(device_state_t*);
|
||||
void mouse_zoom_hotkey_handler(device_state_t*);
|
||||
void all_keys_released_handler(device_state_t*);
|
||||
void switchlock_hotkey_handler(device_state_t*);
|
||||
|
||||
void handle_keyboard_uart_msg(uart_packet_t*, device_state_t*);
|
||||
void handle_mouse_abs_uart_msg(uart_packet_t*, device_state_t*);
|
||||
|
@ -189,6 +240,7 @@ void handle_output_select_msg(uart_packet_t*, device_state_t*);
|
|||
void handle_fw_upgrade_msg(void);
|
||||
void handle_mouse_zoom_msg(uart_packet_t*, device_state_t*);
|
||||
void handle_set_report_msg(uart_packet_t*, device_state_t*);
|
||||
void handle_switch_lock_msg(uart_packet_t*, device_state_t*);
|
||||
|
||||
void switch_output(uint8_t);
|
||||
|
||||
|
|
201
src/mouse.c
201
src/mouse.c
|
@ -1,72 +1,189 @@
|
|||
/*
|
||||
* This file is part of DeskHop (https://github.com/hrvach/deskhop).
|
||||
* Copyright (c) 2024 Hrvoje Cavrak
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
|
||||
int get_mouse_offset(int8_t movement) {
|
||||
// Holding a special hotkey enables mouse to slow down as much as possible
|
||||
// when you need that extra precision
|
||||
if (global_state.mouse_zoom)
|
||||
return movement * MOUSE_SPEED_FACTOR >> 2;
|
||||
int get_mouse_offset(int32_t movement, const int direction) {
|
||||
int offset = 0;
|
||||
|
||||
if (direction == DIRECTION_X)
|
||||
offset = movement * MOUSE_SPEED_FACTOR_X;
|
||||
else
|
||||
return movement * MOUSE_SPEED_FACTOR;
|
||||
offset = movement * MOUSE_SPEED_FACTOR_Y;
|
||||
|
||||
/* Holding a special hotkey enables mouse to slow down as much as possible
|
||||
when you need that extra precision */
|
||||
if (global_state.mouse_zoom)
|
||||
offset = offset >> 2;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
void keep_cursor_on_screen(int16_t* position, const int8_t* movement) {
|
||||
int16_t offset = get_mouse_offset(*movement);
|
||||
void keep_cursor_on_screen(int16_t* position, const int32_t* movement, const int direction) {
|
||||
int16_t offset = get_mouse_offset(*movement, direction);
|
||||
|
||||
// Lowest we can go is 0
|
||||
/* Lowest we can go is 0 */
|
||||
if (*position + offset < 0)
|
||||
*position = 0;
|
||||
|
||||
// Highest we can go is MAX_SCREEN_COORD
|
||||
/* Highest we can go is MAX_SCREEN_COORD */
|
||||
else if (*position + offset > MAX_SCREEN_COORD)
|
||||
*position = MAX_SCREEN_COORD;
|
||||
|
||||
// We're still on screen, all good
|
||||
/* We're still on screen, all good */
|
||||
else
|
||||
*position += offset;
|
||||
}
|
||||
|
||||
void check_mouse_switch(const hid_mouse_report_t* mouse_report, device_state_t* state) {
|
||||
// End of screen right switches screen B->A
|
||||
if ((state->mouse_x + mouse_report->x) > MAX_SCREEN_COORD &&
|
||||
state->active_output == ACTIVE_OUTPUT_B) {
|
||||
state->mouse_x = 0;
|
||||
switch_output(ACTIVE_OUTPUT_A);
|
||||
|
||||
void check_mouse_switch(const mouse_values_t* values, device_state_t* state) {
|
||||
hid_abs_mouse_report_t report = {.y = 0, .x = MAX_SCREEN_COORD};
|
||||
|
||||
/* No switching allowed if explicitly disabled */
|
||||
if (state->switch_lock)
|
||||
return;
|
||||
|
||||
/* End of screen left switches screen A->B */
|
||||
bool jump_from_A_to_B = (state->mouse_x + values->move_x < -MOUSE_JUMP_THRESHOLD &&
|
||||
state->active_output == ACTIVE_OUTPUT_A);
|
||||
|
||||
/* End of screen right switches screen B->A */
|
||||
bool jump_from_B_to_A = (state->mouse_x + values->move_x > MAX_SCREEN_COORD + MOUSE_JUMP_THRESHOLD &&
|
||||
state->active_output == ACTIVE_OUTPUT_B);
|
||||
|
||||
if (jump_from_A_to_B || jump_from_B_to_A) {
|
||||
/* Hide mouse pointer in the upper right corner on the system we are switching FROM
|
||||
If the mouse is locally attached to the current board or notify other board if not */
|
||||
if (state->active_output == state->mouse_connected)
|
||||
queue_mouse_report(&report, state);
|
||||
else
|
||||
send_packet((const uint8_t*)&report, MOUSE_REPORT_MSG, MOUSE_REPORT_LENGTH);
|
||||
|
||||
if (jump_from_A_to_B) {
|
||||
switch_output(ACTIVE_OUTPUT_B);
|
||||
state->mouse_x = MAX_SCREEN_COORD;
|
||||
} else {
|
||||
switch_output(ACTIVE_OUTPUT_A);
|
||||
state->mouse_x = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void extract_values_report_protocol(uint8_t* report,
|
||||
device_state_t* state,
|
||||
mouse_values_t* values) {
|
||||
/* If Report ID is used, the report is prefixed by the report ID so we have to move by 1 byte */
|
||||
if (state->mouse_dev.uses_report_id) {
|
||||
/* Move past the ID to parse the report */
|
||||
report++;
|
||||
}
|
||||
|
||||
// End of screen left switches screen A->B
|
||||
if ((state->mouse_x + mouse_report->x) < 0 && state->active_output == ACTIVE_OUTPUT_A) {
|
||||
state->mouse_x = MAX_SCREEN_COORD;
|
||||
switch_output(ACTIVE_OUTPUT_B);
|
||||
return;
|
||||
values->move_x = get_report_value(report, &state->mouse_dev.move_x);
|
||||
values->move_y = get_report_value(report, &state->mouse_dev.move_y);
|
||||
values->wheel = get_report_value(report, &state->mouse_dev.wheel);
|
||||
values->buttons = get_report_value(report, &state->mouse_dev.buttons);
|
||||
|
||||
/* Mice generally come in 3 categories - 8-bit, 12-bit and 16-bit. */
|
||||
switch (state->mouse_dev.move_x.size) {
|
||||
case 12:
|
||||
/* If we're already 12 bit, great! */
|
||||
break;
|
||||
case 16:
|
||||
/* Initially we downscale fancy mice to 12-bits,
|
||||
adding a 32-bit internal coordinate tracking is TODO */
|
||||
values->move_x >>= 4;
|
||||
values->move_y >>= 4;
|
||||
break;
|
||||
default:
|
||||
/* 8-bit is the default, upscale to 12-bit. */
|
||||
values->move_x <<= 4;
|
||||
values->move_y <<= 4;
|
||||
}
|
||||
}
|
||||
|
||||
void process_mouse_report(uint8_t* raw_report, int len, device_state_t* state) {
|
||||
hid_mouse_report_t* mouse_report = (hid_mouse_report_t*)raw_report;
|
||||
void extract_values_boot_protocol(uint8_t* report, device_state_t* state, mouse_values_t* values) {
|
||||
hid_mouse_report_t* mouse_report = (hid_mouse_report_t*)report;
|
||||
/* For 8-bit values, we upscale them to 12-bit, TODO: 16 bit */
|
||||
values->move_x = mouse_report->x << 4;
|
||||
values->move_y = mouse_report->y << 4;
|
||||
values->wheel = mouse_report->wheel;
|
||||
values->buttons = mouse_report->buttons;
|
||||
}
|
||||
|
||||
// We need to enforce the cursor doesn't go off-screen, that would be bad.
|
||||
keep_cursor_on_screen(&state->mouse_x, &mouse_report->x);
|
||||
keep_cursor_on_screen(&state->mouse_y, &mouse_report->y);
|
||||
void process_mouse_report(uint8_t* raw_report, int len, device_state_t* state) {
|
||||
mouse_values_t values = {0};
|
||||
|
||||
/* Interpret values depending on the current protocol used */
|
||||
if (state->mouse_dev.protocol == HID_PROTOCOL_BOOT)
|
||||
extract_values_boot_protocol(raw_report, state, &values);
|
||||
else
|
||||
extract_values_report_protocol(raw_report, state, &values);
|
||||
|
||||
/* We need to enforce the cursor doesn't go off-screen, that would be bad. */
|
||||
keep_cursor_on_screen(&state->mouse_x, &values.move_x, DIRECTION_X);
|
||||
keep_cursor_on_screen(&state->mouse_y, &values.move_y, DIRECTION_Y);
|
||||
|
||||
hid_abs_mouse_report_t abs_mouse_report = {
|
||||
.buttons = values.buttons,
|
||||
.x = state->mouse_x,
|
||||
.y = state->mouse_y,
|
||||
.wheel = values.wheel,
|
||||
.pan = 0
|
||||
};
|
||||
|
||||
if (state->active_output == ACTIVE_OUTPUT_A) {
|
||||
hid_abs_mouse_report_t abs_mouse_report;
|
||||
|
||||
abs_mouse_report.buttons = mouse_report->buttons;
|
||||
abs_mouse_report.x = state->mouse_x;
|
||||
abs_mouse_report.y = state->mouse_y;
|
||||
abs_mouse_report.wheel = mouse_report->wheel;
|
||||
abs_mouse_report.pan = 0;
|
||||
|
||||
send_packet((const uint8_t*)&abs_mouse_report, MOUSE_REPORT_MSG, MOUSE_REPORT_LENGTH);
|
||||
|
||||
} else {
|
||||
tud_hid_abs_mouse_report(REPORT_ID_MOUSE, mouse_report->buttons, state->mouse_x,
|
||||
state->mouse_y, mouse_report->wheel, 0);
|
||||
|
||||
queue_mouse_report(&abs_mouse_report, state);
|
||||
state->last_activity[ACTIVE_OUTPUT_B] = time_us_64();
|
||||
}
|
||||
|
||||
// We use the mouse to switch outputs, the logic is in check_mouse_switch()
|
||||
check_mouse_switch(mouse_report, state);
|
||||
}
|
||||
/* We use the mouse to switch outputs, the logic is in check_mouse_switch() */
|
||||
check_mouse_switch(&values, state);
|
||||
}
|
||||
|
||||
/* ==================================================== *
|
||||
* Mouse Queue Section
|
||||
* ==================================================== */
|
||||
|
||||
void process_mouse_queue_task(device_state_t* state) {
|
||||
hid_abs_mouse_report_t report = {0};
|
||||
|
||||
/* We need to be connected to the host to send messages */
|
||||
if (!state->tud_connected)
|
||||
return;
|
||||
|
||||
/* Peek first, if there is anything there... */
|
||||
if (!queue_try_peek(&state->mouse_queue, &report))
|
||||
return;
|
||||
|
||||
/* ... try sending it to the host, if it's successful */
|
||||
bool succeeded = tud_hid_abs_mouse_report(REPORT_ID_MOUSE, report.buttons, report.x, report.y,
|
||||
report.wheel, report.pan);
|
||||
|
||||
/* ... then we can remove it from the queue */
|
||||
if (succeeded)
|
||||
queue_try_remove(&state->mouse_queue, &report);
|
||||
}
|
||||
|
||||
void queue_mouse_report(hid_abs_mouse_report_t* report, device_state_t* state) {
|
||||
/* It wouldn't be fun to queue up a bunch of messages and then dump them all on host */
|
||||
if (!state->tud_connected)
|
||||
return;
|
||||
|
||||
queue_try_add(&state->mouse_queue, report);
|
||||
}
|
||||
|
|
47
src/setup.c
47
src/setup.c
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
* This file is part of DeskHop (https://github.com/hrvach/deskhop).
|
||||
* Copyright (c) 2024 Hrvoje Cavrak
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**================================================== *
|
||||
* ============= Initial Board Setup ============== *
|
||||
* ================================================== */
|
||||
|
@ -34,19 +51,21 @@ void serial_init() {
|
|||
* PIO USB configuration, D+ pin 14, D- pin 15
|
||||
* ================================================== */
|
||||
|
||||
usb_device_t* pio_usb_init(void) {
|
||||
void pio_usb_host_config(void) {
|
||||
/* tuh_configure() must be called before tuh_init() */
|
||||
static pio_usb_configuration_t config = PIO_USB_DEFAULT_CONFIG;
|
||||
config.pin_dp = 14;
|
||||
config.alarm_pool = (void*)alarm_pool_create(2, 1);
|
||||
config.pin_dp = PIO_USB_DP_PIN;
|
||||
tuh_configure(BOARD_TUH_RHPORT, TUH_CFGID_RPI_PIO_USB_CONFIGURATION, &config);
|
||||
|
||||
return pio_usb_host_init(&config);
|
||||
/* Initialize and configure TinyUSB Host */
|
||||
tuh_init(1);
|
||||
}
|
||||
|
||||
/* ================================================== *
|
||||
* Perform initial board/usb setup
|
||||
* ================================================== */
|
||||
|
||||
usb_device_t* initial_setup(void) {
|
||||
void initial_setup(void) {
|
||||
/* PIO USB requires a clock multiple of 12 MHz, setting to 120 MHz */
|
||||
set_sys_clock_khz(120000, true);
|
||||
|
||||
|
@ -57,23 +76,25 @@ usb_device_t* initial_setup(void) {
|
|||
/* Initialize and configure UART */
|
||||
serial_init();
|
||||
|
||||
/* Initialize and configure TinyUSB */
|
||||
tusb_init();
|
||||
|
||||
/* Initialize keyboard and mouse queues */
|
||||
queue_init(&global_state.kbd_queue, sizeof(hid_keyboard_report_t), KBD_QUEUE_LENGTH);
|
||||
queue_init(&global_state.mouse_queue, sizeof(hid_abs_mouse_report_t), MOUSE_QUEUE_LENGTH);
|
||||
|
||||
/* Setup RP2040 Core 1 */
|
||||
multicore_reset_core1();
|
||||
multicore_launch_core1(core1_main);
|
||||
|
||||
/* Initialize and configure PIO USB */
|
||||
usb_device_t* pio_usb_device = pio_usb_init();
|
||||
/* Initialize and configure TinyUSB Device */
|
||||
tud_init(BOARD_TUD_RHPORT);
|
||||
|
||||
/* Initialize and configure TinyUSB Host */
|
||||
pio_usb_host_config();
|
||||
|
||||
/* Update the core1 initial pass timestamp before enabling the watchdog */
|
||||
global_state.core1_last_loop_pass = time_us_64();
|
||||
|
||||
/* Setup the watchdog so we reboot and recover from a crash */
|
||||
watchdog_enable(WATCHDOG_TIMEOUT, WATCHDOG_PAUSE_ON_DEBUG);
|
||||
|
||||
return pio_usb_device;
|
||||
}
|
||||
|
||||
/* ========== End of Initial Board Setup ========== */
|
||||
/* ========== End of Initial Board Setup ========== */
|
||||
|
|
|
@ -34,6 +34,21 @@
|
|||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#define CFG_TUSB_OS OPT_OS_PICO
|
||||
|
||||
// Enable device stack
|
||||
#define CFG_TUD_ENABLED 1
|
||||
|
||||
// RHPort number used for device is port 0
|
||||
#define BOARD_TUD_RHPORT 0
|
||||
|
||||
// RHPort number used for host is port 1
|
||||
#define BOARD_TUH_RHPORT 1
|
||||
|
||||
// Enable host stack with pio-usb if Pico-PIO-USB library is available
|
||||
#define CFG_TUH_ENABLED 1
|
||||
#define CFG_TUH_RPI_PIO_USB 1
|
||||
|
||||
// defined by board.mk
|
||||
#ifndef CFG_TUSB_MCU
|
||||
#error CFG_TUSB_MCU must be defined
|
||||
|
@ -56,17 +71,8 @@
|
|||
#endif
|
||||
|
||||
// Device mode with rhport and speed defined by board.mk
|
||||
#if BOARD_DEVICE_RHPORT_NUM == 0
|
||||
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
|
||||
#elif BOARD_DEVICE_RHPORT_NUM == 1
|
||||
#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
|
||||
#else
|
||||
#error "Incorrect RHPort configuration"
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_OS
|
||||
#define CFG_TUSB_OS OPT_OS_NONE
|
||||
#endif
|
||||
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
|
||||
#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_HOST | BOARD_DEVICE_RHPORT_SPEED)
|
||||
|
||||
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
|
||||
// #define CFG_TUSB_DEBUG 0
|
||||
|
@ -102,7 +108,22 @@
|
|||
#define CFG_TUD_VENDOR 0
|
||||
|
||||
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
||||
#define CFG_TUD_HID_EP_BUFSIZE 16
|
||||
#define CFG_TUD_HID_EP_BUFSIZE 32
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// HOST CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// Size of buffer to hold descriptors and other data used for enumeration
|
||||
#define CFG_TUH_ENUMERATION_BUFSIZE 256
|
||||
|
||||
#define CFG_TUH_HUB 1
|
||||
// max device support (excluding hub device)
|
||||
#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports
|
||||
|
||||
#define CFG_TUH_HID 4
|
||||
#define CFG_TUH_HID_EPIN_BUFSIZE 64
|
||||
#define CFG_TUH_HID_EPOUT_BUFSIZE 64
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
32
src/uart.c
32
src/uart.c
|
@ -1,5 +1,21 @@
|
|||
#include "main.h"
|
||||
/*
|
||||
* This file is part of DeskHop (https://github.com/hrvach/deskhop).
|
||||
* Copyright (c) 2024 Hrvoje Cavrak
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
|
||||
/**================================================== *
|
||||
* =============== Sending Packets ================ *
|
||||
|
@ -58,6 +74,10 @@ void process_packet(uart_packet_t* packet, device_state_t* state) {
|
|||
case KBD_SET_REPORT_MSG:
|
||||
handle_set_report_msg(packet, state);
|
||||
break;
|
||||
|
||||
case SWITCH_LOCK_MSG:
|
||||
handle_switch_lock_msg(packet, state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,10 +92,10 @@ void receive_char(uart_packet_t* packet, device_state_t* state) {
|
|||
switch (state->receiver_state) {
|
||||
case IDLE:
|
||||
if (uart_is_readable(SERIAL_UART)) {
|
||||
raw_packet[0] = raw_packet[1]; // Remember the previous byte received
|
||||
raw_packet[1] = uart_getc(SERIAL_UART); // ... and try to match packet start
|
||||
raw_packet[0] = raw_packet[1]; /* Remember the previous byte received */
|
||||
raw_packet[1] = uart_getc(SERIAL_UART); /* ... and try to match packet start */
|
||||
|
||||
// If we found 0xAA 0x55, we're in sync and can move on to read/process the packet
|
||||
/* If we found 0xAA 0x55, we're in sync and can move on to read/process the packet */
|
||||
if (raw_packet[0] == START1 && raw_packet[1] == START2) {
|
||||
state->receiver_state = READING_PACKET;
|
||||
}
|
||||
|
@ -86,7 +106,7 @@ void receive_char(uart_packet_t* packet, device_state_t* state) {
|
|||
if (uart_is_readable(SERIAL_UART)) {
|
||||
raw_packet[count++] = uart_getc(SERIAL_UART);
|
||||
|
||||
// Check if a complete packet is received
|
||||
/* Check if a complete packet is received */
|
||||
if (count >= PACKET_LENGTH) {
|
||||
state->receiver_state = PROCESSING_PACKET;
|
||||
}
|
||||
|
@ -96,7 +116,7 @@ void receive_char(uart_packet_t* packet, device_state_t* state) {
|
|||
case PROCESSING_PACKET:
|
||||
process_packet(packet, state);
|
||||
|
||||
// Cleanup and return to IDLE when done
|
||||
/* Cleanup and return to IDLE when done */
|
||||
count = 0;
|
||||
state->receiver_state = IDLE;
|
||||
break;
|
||||
|
|
160
src/usb.c
160
src/usb.c
|
@ -1,35 +1,29 @@
|
|||
/*
|
||||
* This file is part of DeskHop (https://github.com/hrvach/deskhop).
|
||||
* Copyright (c) 2024 Hrvoje Cavrak
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
|
||||
/**================================================== *
|
||||
* ========== Query endpoints for reports ========= *
|
||||
* ================================================== */
|
||||
|
||||
void check_endpoints(device_state_t* state) {
|
||||
uint8_t raw_report[64];
|
||||
|
||||
// Iterate through all endpoints and check for data
|
||||
for (int ep_idx = 0; ep_idx < PIO_USB_DEV_EP_CNT; ep_idx++) {
|
||||
endpoint_t* ep = pio_usb_get_endpoint(state->usb_device, ep_idx);
|
||||
|
||||
if (ep == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int len = pio_usb_get_in_data(ep, raw_report, sizeof(raw_report));
|
||||
|
||||
if (len > 0) {
|
||||
if (BOARD_ROLE == KEYBOARD_PICO_A)
|
||||
process_keyboard_report(raw_report, len, state);
|
||||
else
|
||||
process_mouse_report(raw_report, len, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**================================================== *
|
||||
* =========== TinyUSB Device Callbacks =========== *
|
||||
* ================================================== */
|
||||
|
||||
/* Invoked when we get GET_REPORT control request.
|
||||
* We are expected to fill buffer with the report content, update reqlen
|
||||
* and return its length. We return 0 to STALL the request. */
|
||||
uint16_t tud_hid_get_report_cb(uint8_t instance,
|
||||
uint8_t report_id,
|
||||
hid_report_type_t report_type,
|
||||
|
@ -58,33 +52,111 @@ void tud_hid_set_report_cb(uint8_t instance,
|
|||
uint8_t leds = buffer[0];
|
||||
|
||||
if (KBD_LED_AS_INDICATOR) {
|
||||
leds = leds & 0xFD; // 1111 1101 (Clear Caps Lock bit)
|
||||
leds = leds & 0xFD; /* 1111 1101 (Clear Caps Lock bit) */
|
||||
|
||||
if (global_state.active_output)
|
||||
leds |= KEYBOARD_LED_CAPSLOCK;
|
||||
}
|
||||
|
||||
global_state.keyboard_leds[global_state.active_output] = leds;
|
||||
|
||||
// If we are board B, we need to set this information to the other one since that one
|
||||
// has the keyboard connected to it (and LEDs you can turn on :-))
|
||||
if (BOARD_ROLE == MOUSE_PICO_B)
|
||||
send_value(leds, KBD_SET_REPORT_MSG);
|
||||
global_state.keyboard_leds[global_state.active_output] = leds;
|
||||
|
||||
// If we are board A, update LEDs directly
|
||||
else
|
||||
/* If we are board without the keyboard hooked up directly, we need to send this information
|
||||
to the other one since that one has the keyboard connected to it (and LEDs you can turn on :)) */
|
||||
if (global_state.keyboard_connected)
|
||||
update_leds(&global_state);
|
||||
else
|
||||
send_value(leds, KBD_SET_REPORT_MSG);
|
||||
}
|
||||
}
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
global_state.tud_connected = true;
|
||||
/* Invoked when device is mounted */
|
||||
void tud_mount_cb(void) {
|
||||
global_state.tud_connected = true;
|
||||
}
|
||||
|
||||
// Invoked when device is unmounted
|
||||
void tud_umount_cb(void)
|
||||
{
|
||||
global_state.tud_connected = false;
|
||||
}
|
||||
/* Invoked when device is unmounted */
|
||||
void tud_umount_cb(void) {
|
||||
global_state.tud_connected = false;
|
||||
}
|
||||
|
||||
/**================================================== *
|
||||
* =============== USB HOST Section =============== *
|
||||
* ================================================== */
|
||||
|
||||
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
|
||||
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
|
||||
switch (itf_protocol) {
|
||||
case HID_ITF_PROTOCOL_KEYBOARD:
|
||||
global_state.keyboard_connected = false;
|
||||
break;
|
||||
|
||||
case HID_ITF_PROTOCOL_MOUSE:
|
||||
global_state.mouse_connected = false;
|
||||
|
||||
/* Clear this so reconnecting a mouse doesn't try to continue in HID REPORT protocol */
|
||||
memset(&global_state.mouse_dev, 0, sizeof(global_state.mouse_dev));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void tuh_hid_mount_cb(uint8_t dev_addr,
|
||||
uint8_t instance,
|
||||
uint8_t const* desc_report,
|
||||
uint16_t desc_len) {
|
||||
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
|
||||
|
||||
switch (itf_protocol) {
|
||||
case HID_ITF_PROTOCOL_KEYBOARD:
|
||||
/* Keeping this is needed for setting leds from device set_report callback */
|
||||
global_state.kbd_dev_addr = dev_addr;
|
||||
global_state.kbd_instance = instance;
|
||||
|
||||
global_state.keyboard_connected = true;
|
||||
break;
|
||||
|
||||
case HID_ITF_PROTOCOL_MOUSE:
|
||||
/* Switch to using protocol report instead of boot report, it's more complicated but
|
||||
at least we get all the information we need (looking at you, mouse wheel) */
|
||||
if (tuh_hid_get_protocol(dev_addr, instance) == HID_PROTOCOL_BOOT) {
|
||||
tuh_hid_set_protocol(dev_addr, instance, HID_PROTOCOL_REPORT);
|
||||
}
|
||||
|
||||
parse_report_descriptor(&global_state.mouse_dev, MAX_REPORTS, desc_report, desc_len);
|
||||
|
||||
global_state.mouse_connected = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Kick off the report querying */
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
/* Invoked when received report from device via interrupt endpoint */
|
||||
void tuh_hid_report_received_cb(uint8_t dev_addr,
|
||||
uint8_t instance,
|
||||
uint8_t const* report,
|
||||
uint16_t len) {
|
||||
(void)len;
|
||||
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
|
||||
|
||||
switch (itf_protocol) {
|
||||
case HID_ITF_PROTOCOL_KEYBOARD:
|
||||
process_keyboard_report((uint8_t*)report, len, &global_state);
|
||||
break;
|
||||
|
||||
case HID_ITF_PROTOCOL_MOUSE:
|
||||
process_mouse_report((uint8_t*)report, len, &global_state);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Continue requesting reports */
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
/* Set protocol in a callback. If we were called, command succeeded. We're only
|
||||
doing this for the mouse anyway, so we can only be called about the mouse */
|
||||
void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol) {
|
||||
(void) dev_addr;
|
||||
(void) idx;
|
||||
global_state.mouse_dev.protocol = protocol;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
*
|
||||
* */
|
||||
|
||||
#define KBD_LED_AS_INDICATOR 1
|
||||
#define KBD_LED_AS_INDICATOR 0
|
||||
|
||||
/**===================================================== *
|
||||
* =========== Hotkey for output switching =========== *
|
||||
|
@ -33,9 +33,18 @@
|
|||
*
|
||||
* This affects how fast the mouse moves.
|
||||
*
|
||||
* MOUSE_SPEED_FACTOR: [1-128], higher values will make very little sense,
|
||||
* 16 works well for my mouse, but the option to adjust is here if you need it.
|
||||
* MOUSE_SPEED_FACTOR_X: [1-128], mouse moves at this speed in X direction
|
||||
* MOUSE_SPEED_FACTOR_Y: [1-128], mouse moves at this speed in Y direction
|
||||
*
|
||||
* MOUSE_JUMP_THRESHOLD: [0-32768], sets the "force" you need to use to drag the
|
||||
* mouse to another screen, 0 meaning no force needed at all, and ~500 some force
|
||||
* needed, ~1000 no accidental jumps, you need to really mean it.
|
||||
*
|
||||
* TODO: make this configurable per-screen.
|
||||
*
|
||||
* */
|
||||
|
||||
#define MOUSE_SPEED_FACTOR 16
|
||||
#define MOUSE_SPEED_FACTOR_X 1
|
||||
#define MOUSE_SPEED_FACTOR_Y 1
|
||||
|
||||
#define MOUSE_JUMP_THRESHOLD 0
|
||||
|
|
30
src/utils.c
30
src/utils.c
|
@ -1,10 +1,27 @@
|
|||
/*
|
||||
* This file is part of DeskHop (https://github.com/hrvach/deskhop).
|
||||
* Copyright (c) 2024 Hrvoje Cavrak
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
|
||||
/**================================================== *
|
||||
* ============== Checksum Functions ============== *
|
||||
* ================================================== */
|
||||
|
||||
uint8_t calc_checksum(const uint8_t* data, int length) {
|
||||
uint8_t calc_checksum(const uint8_t* data, int length) {
|
||||
uint8_t checksum = 0;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
@ -22,16 +39,15 @@ bool verify_checksum(const uart_packet_t* packet) {
|
|||
/**================================================== *
|
||||
* ============== Watchdog Functions ============== *
|
||||
* ================================================== */
|
||||
|
||||
|
||||
void kick_watchdog(void) {
|
||||
// Read the timer AFTER duplicating the core1 timestamp,
|
||||
// so it doesn't get updated in the meantime.
|
||||
/* Read the timer AFTER duplicating the core1 timestamp,
|
||||
so it doesn't get updated in the meantime. */
|
||||
|
||||
uint64_t core1_last_loop_pass = global_state.core1_last_loop_pass;
|
||||
uint64_t current_time = time_us_64();
|
||||
|
||||
// If core1 stops updating the timestamp, we'll stop kicking the watchog and reboot
|
||||
if (current_time - core1_last_loop_pass < CORE1_HANG_TIMEOUT_US)
|
||||
/* If core1 stops updating the timestamp, we'll stop kicking the watchog and reboot */
|
||||
if (current_time - core1_last_loop_pass < CORE1_HANG_TIMEOUT_US)
|
||||
watchdog_update();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue