DeskHop v0.62 (Minor Bugfixes)
- Fixed screen lock regression bug - Cleaned up trailing spaces Due to MacOS having issues: - Moved screen lock shortcut to right control + L - Moved switch lock shortcut to right control + K
This commit is contained in:
parent
7a0e7f31ff
commit
1fd0049039
|
@ -1,7 +1,7 @@
|
||||||
cmake_minimum_required(VERSION 3.6)
|
cmake_minimum_required(VERSION 3.6)
|
||||||
|
|
||||||
set(VERSION_MAJOR 00)
|
set(VERSION_MAJOR 00)
|
||||||
set(VERSION_MINOR 139)
|
set(VERSION_MINOR 147)
|
||||||
|
|
||||||
set(PICO_SDK_FETCH_FROM_GIT off)
|
set(PICO_SDK_FETCH_FROM_GIT off)
|
||||||
set(PICO_BOARD=pico)
|
set(PICO_BOARD=pico)
|
||||||
|
|
11
README.md
11
README.md
|
@ -93,9 +93,14 @@ Ever tried to move that YT video slider to a specific position but your mouse mo
|
||||||
|
|
||||||
### Switch Lock
|
### Switch Lock
|
||||||
|
|
||||||
If you want to lock yourself to one screen, use ```RIGHT CTRL + L```.
|
If you want to lock yourself to one screen, use ```RIGHT CTRL + K```.
|
||||||
This will make sure you won't accidentally leave your current screen. To turn off, press the same key combo again.
|
This will make sure you won't accidentally leave your current screen. To turn off, press the same key combo again.
|
||||||
|
|
||||||
|
### Lock Both Screens
|
||||||
|
|
||||||
|
You can lock both computers at once by using ```RIGHT CTRL + L```.
|
||||||
|
To make use of this feature, first set up the OS for each output in config (since the shortcuts are different).
|
||||||
|
|
||||||
### Screensaver
|
### Screensaver
|
||||||
|
|
||||||
Supposedly built in to prevent computer from entering standby, but truth be told - it is just fun to watch. **Off by default**, will make your mouse pointer bounce around the screen like a Pong ball. When enabled, it activates after a period of inactivity defined in user config header and automatically switches off as soon as you send any output towards that screen.
|
Supposedly built in to prevent computer from entering standby, but truth be told - it is just fun to watch. **Off by default**, will make your mouse pointer bounce around the screen like a Pong ball. When enabled, it activates after a period of inactivity defined in user config header and automatically switches off as soon as you send any output towards that screen.
|
||||||
|
@ -209,8 +214,8 @@ _Config_
|
||||||
|
|
||||||
_Usage_
|
_Usage_
|
||||||
- ```Right CTRL + Right ALT``` - Toggle slower mouse mode
|
- ```Right CTRL + Right ALT``` - Toggle slower mouse mode
|
||||||
- ```Right CTRL + L``` - Lock/Unlock mouse desktop switching
|
- ```Right CTRL + K``` - Lock/Unlock mouse desktop switching
|
||||||
- ```Right ALT + Right Shift + L``` - Lock both outputs at once (set output OS before, see shortcuts below)
|
- ```Right CTRL + L``` - Lock both outputs at once (set output OS before, see shortcuts below)
|
||||||
- ```Caps Lock``` - Switch between outputs
|
- ```Caps Lock``` - Switch between outputs
|
||||||
|
|
||||||
### Switch cursor height calibration
|
### Switch cursor height calibration
|
||||||
|
|
BIN
disk/disk.img
BIN
disk/disk.img
Binary file not shown.
|
@ -21,9 +21,9 @@ const config_t default_config = {
|
||||||
.mode = SCREENSAVER_A_MODE,
|
.mode = SCREENSAVER_A_MODE,
|
||||||
.only_if_inactive = SCREENSAVER_A_ONLY_IF_INACTIVE,
|
.only_if_inactive = SCREENSAVER_A_ONLY_IF_INACTIVE,
|
||||||
.idle_time_us = SCREENSAVER_A_IDLE_TIME_SEC * 1000000,
|
.idle_time_us = SCREENSAVER_A_IDLE_TIME_SEC * 1000000,
|
||||||
.max_time_us = SCREENSAVER_A_MAX_TIME_SEC * 1000000,
|
.max_time_us = SCREENSAVER_A_MAX_TIME_SEC * 1000000,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.output[OUTPUT_B] =
|
.output[OUTPUT_B] =
|
||||||
{
|
{
|
||||||
.number = OUTPUT_B,
|
.number = OUTPUT_B,
|
||||||
|
@ -41,7 +41,7 @@ const config_t default_config = {
|
||||||
.mode = SCREENSAVER_B_MODE,
|
.mode = SCREENSAVER_B_MODE,
|
||||||
.only_if_inactive = SCREENSAVER_B_ONLY_IF_INACTIVE,
|
.only_if_inactive = SCREENSAVER_B_ONLY_IF_INACTIVE,
|
||||||
.idle_time_us = SCREENSAVER_B_IDLE_TIME_SEC * 1000000,
|
.idle_time_us = SCREENSAVER_B_IDLE_TIME_SEC * 1000000,
|
||||||
.max_time_us = SCREENSAVER_B_MAX_TIME_SEC * 1000000,
|
.max_time_us = SCREENSAVER_B_MAX_TIME_SEC * 1000000,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.enforce_ports = ENFORCE_PORTS,
|
.enforce_ports = ENFORCE_PORTS,
|
||||||
|
|
|
@ -79,14 +79,14 @@ void screenlock_hotkey_handler(device_t *state, hid_keyboard_report_t *report) {
|
||||||
lock_report.keycode[0] = HID_KEY_L;
|
lock_report.keycode[0] = HID_KEY_L;
|
||||||
break;
|
break;
|
||||||
case MACOS:
|
case MACOS:
|
||||||
lock_report.modifier = KEYBOARD_MODIFIER_LEFTCTRL | KEYBOARD_MODIFIER_LEFTALT;
|
lock_report.modifier = KEYBOARD_MODIFIER_LEFTCTRL | KEYBOARD_MODIFIER_LEFTGUI;
|
||||||
lock_report.keycode[0] = HID_KEY_Q;
|
lock_report.keycode[0] = HID_KEY_Q;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (global_state.active_output == out) {
|
if (BOARD_ROLE == out) {
|
||||||
queue_kbd_report(&lock_report, state);
|
queue_kbd_report(&lock_report, state);
|
||||||
release_all_keys(state);
|
release_all_keys(state);
|
||||||
} else {
|
} else {
|
||||||
|
@ -111,14 +111,14 @@ void mouse_zoom_hotkey_handler(device_t *state, hid_keyboard_report_t *report) {
|
||||||
|
|
||||||
|
|
||||||
/* Put the device into a special configuration mode */
|
/* Put the device into a special configuration mode */
|
||||||
void config_enable_hotkey_handler(device_t *state, hid_keyboard_report_t *report) {
|
void config_enable_hotkey_handler(device_t *state, hid_keyboard_report_t *report) {
|
||||||
/* If config mode is already active, skip this and reboot to return to normal mode */
|
/* If config mode is already active, skip this and reboot to return to normal mode */
|
||||||
if (!state->config_mode_active) {
|
if (!state->config_mode_active) {
|
||||||
watchdog_hw->scratch[5] = MAGIC_WORD_1;
|
watchdog_hw->scratch[5] = MAGIC_WORD_1;
|
||||||
watchdog_hw->scratch[6] = MAGIC_WORD_2;
|
watchdog_hw->scratch[6] = MAGIC_WORD_2;
|
||||||
}
|
}
|
||||||
|
|
||||||
release_all_keys(state);
|
release_all_keys(state);
|
||||||
state->reboot_requested = true;
|
state->reboot_requested = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -226,9 +226,9 @@ void handle_api_msgs(uart_packet_t *packet, device_t *state) {
|
||||||
|
|
||||||
/* If we don't have a valid map entry, return immediately */
|
/* If we don't have a valid map entry, return immediately */
|
||||||
if (map == NULL)
|
if (map == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Create a pointer to the offset into the structure we need to access */
|
/* Create a pointer to the offset into the structure we need to access */
|
||||||
uint8_t *ptr = (((uint8_t *)&global_state) + map->offset);
|
uint8_t *ptr = (((uint8_t *)&global_state) + map->offset);
|
||||||
|
|
||||||
if (packet->type == SET_VAL_MSG) {
|
if (packet->type == SET_VAL_MSG) {
|
||||||
|
@ -237,7 +237,7 @@ void handle_api_msgs(uart_packet_t *packet, device_t *state) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
memcpy(ptr, &packet->data[1], map->len);
|
memcpy(ptr, &packet->data[1], map->len);
|
||||||
}
|
}
|
||||||
else if (packet->type == GET_VAL_MSG) {
|
else if (packet->type == GET_VAL_MSG) {
|
||||||
uart_packet_t response = {.type=GET_VAL_MSG, .data={0}};
|
uart_packet_t response = {.type=GET_VAL_MSG, .data={0}};
|
||||||
memcpy(response.data, ptr, map->len);
|
memcpy(response.data, ptr, map->len);
|
||||||
|
@ -252,14 +252,14 @@ void handle_api_msgs(uart_packet_t *packet, device_t *state) {
|
||||||
/* Process request packet and create a response */
|
/* Process request packet and create a response */
|
||||||
void handle_request_byte_msg(uart_packet_t *packet, device_t *state) {
|
void handle_request_byte_msg(uart_packet_t *packet, device_t *state) {
|
||||||
uint32_t address = packet->data32[0];
|
uint32_t address = packet->data32[0];
|
||||||
|
|
||||||
if (address > STAGING_IMAGE_SIZE)
|
if (address > STAGING_IMAGE_SIZE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Add requested data to bytes 4-7 in the packet and return it with a different type */
|
/* Add requested data to bytes 4-7 in the packet and return it with a different type */
|
||||||
uint32_t data = *(uint32_t *)&ADDR_FW_RUNNING[address];
|
uint32_t data = *(uint32_t *)&ADDR_FW_RUNNING[address];
|
||||||
packet->data32[1] = data;
|
packet->data32[1] = data;
|
||||||
|
|
||||||
queue_packet(packet->data, RESPONSE_BYTE_MSG, PACKET_DATA_LENGTH);
|
queue_packet(packet->data, RESPONSE_BYTE_MSG, PACKET_DATA_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,27 +279,27 @@ void handle_response_byte_msg(uart_packet_t *packet, device_t *state) {
|
||||||
if((address & 0xfff) == 0x000)
|
if((address & 0xfff) == 0x000)
|
||||||
toggle_led();
|
toggle_led();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update checksum as we receive each byte */
|
/* Update checksum as we receive each byte */
|
||||||
if (address < STAGING_IMAGE_SIZE - FLASH_SECTOR_SIZE)
|
if (address < STAGING_IMAGE_SIZE - FLASH_SECTOR_SIZE)
|
||||||
for (int i=0; i<4; i++)
|
for (int i=0; i<4; i++)
|
||||||
state->fw.checksum = crc32_iter(state->fw.checksum, packet->data[4 + i]);
|
state->fw.checksum = crc32_iter(state->fw.checksum, packet->data[4 + i]);
|
||||||
|
|
||||||
memcpy(state->page_buffer + offset, &packet->data32[1], sizeof(uint32_t));
|
memcpy(state->page_buffer + offset, &packet->data32[1], sizeof(uint32_t));
|
||||||
|
|
||||||
/* Neeeeeeext byte, please! */
|
/* Neeeeeeext byte, please! */
|
||||||
state->fw.address += sizeof(uint32_t);
|
state->fw.address += sizeof(uint32_t);
|
||||||
state->fw.byte_done = true;
|
state->fw.byte_done = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process a request to read a firmware package from flash */
|
/* Process a request to read a firmware package from flash */
|
||||||
void handle_heartbeat_msg(uart_packet_t *packet, device_t *state) {
|
void handle_heartbeat_msg(uart_packet_t *packet, device_t *state) {
|
||||||
uint16_t other_running_version = packet->data16[0];
|
uint16_t other_running_version = packet->data16[0];
|
||||||
|
|
||||||
if (state->fw.upgrade_in_progress)
|
if (state->fw.upgrade_in_progress)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* If the other board isn't running a newer version, we are done */
|
/* If the other board isn't running a newer version, we are done */
|
||||||
if (other_running_version <= state->_running_fw.version)
|
if (other_running_version <= state->_running_fw.version)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -309,7 +309,7 @@ void handle_heartbeat_msg(uart_packet_t *packet, device_t *state) {
|
||||||
.byte_done = true,
|
.byte_done = true,
|
||||||
.address = 0,
|
.address = 0,
|
||||||
.checksum = 0xffffffff,
|
.checksum = 0xffffffff,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ void update_usage(parser_state_t *parser, int i) {
|
||||||
*(parser->p_usage + i) = *(parser->p_usage + i - 1);
|
*(parser->p_usage + i) = *(parser->p_usage + i - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void store_element(parser_state_t *parser, report_val_t *val, int i, uint32_t data, uint16_t size, hid_interface_t *iface) {
|
void store_element(parser_state_t *parser, report_val_t *val, int i, uint32_t data, uint16_t size, hid_interface_t *iface) {
|
||||||
*val = (report_val_t){
|
*val = (report_val_t){
|
||||||
.offset = parser->offset_in_bits,
|
.offset = parser->offset_in_bits,
|
||||||
.offset_idx = parser->offset_in_bits >> 3,
|
.offset_idx = parser->offset_in_bits >> 3,
|
||||||
|
@ -68,9 +68,9 @@ void store_element(parser_state_t *parser, report_val_t *val, int i, uint32_t da
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_global_item(parser_state_t *parser, item_t *item) {
|
void handle_global_item(parser_state_t *parser, item_t *item) {
|
||||||
if (item->hdr.tag == RI_GLOBAL_REPORT_ID)
|
if (item->hdr.tag == RI_GLOBAL_REPORT_ID)
|
||||||
parser->report_id = item->val;
|
parser->report_id = item->val;
|
||||||
|
|
||||||
parser->globals[item->hdr.tag] = *item;
|
parser->globals[item->hdr.tag] = *item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ void handle_local_item(parser_state_t *parser, item_t *item) {
|
||||||
parser->locals[item->hdr.tag] = *item;
|
parser->locals[item->hdr.tag] = *item;
|
||||||
|
|
||||||
if (item->hdr.tag == RI_LOCAL_USAGE) {
|
if (item->hdr.tag == RI_LOCAL_USAGE) {
|
||||||
if(IS_BLOCK_END)
|
if(IS_BLOCK_END)
|
||||||
parser->global_usage = item->val;
|
parser->global_usage = item->val;
|
||||||
|
|
||||||
else if (parser->usage_count < HID_MAX_USAGES - 1)
|
else if (parser->usage_count < HID_MAX_USAGES - 1)
|
||||||
|
@ -94,20 +94,20 @@ void handle_main_input(parser_state_t *parser, item_t *item, hid_interface_t *if
|
||||||
report_val_t val = {0};
|
report_val_t val = {0};
|
||||||
|
|
||||||
/* Swap count and size for 1-bit variables, it makes sense to process e.g. NKRO with
|
/* Swap count and size for 1-bit variables, it makes sense to process e.g. NKRO with
|
||||||
size = 1 and count = 240 in one go instead of doing 240 iterations
|
size = 1 and count = 240 in one go instead of doing 240 iterations
|
||||||
Don't do this if there are usages in the queue, though.
|
Don't do this if there are usages in the queue, though.
|
||||||
*/
|
*/
|
||||||
if (size == 1 && parser->usage_count <= 1) {
|
if (size == 1 && parser->usage_count <= 1) {
|
||||||
size = count;
|
size = count;
|
||||||
count = 1;
|
count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
update_usage(parser, i);
|
update_usage(parser, i);
|
||||||
store_element(parser, &val, i, item->val, size, iface);
|
store_element(parser, &val, i, item->val, size, iface);
|
||||||
|
|
||||||
/* Use the parsed data to populate internal device structures */
|
/* Use the parsed data to populate internal device structures */
|
||||||
extract_data(iface, &val);
|
extract_data(iface, &val);
|
||||||
|
|
||||||
/* Iterate <count> times and increase offset by <size> amount, moving by <count> x <size> bits */
|
/* Iterate <count> times and increase offset by <size> amount, moving by <count> x <size> bits */
|
||||||
parser->offset_in_bits += size;
|
parser->offset_in_bits += size;
|
||||||
|
@ -152,7 +152,7 @@ void handle_main_item(parser_state_t *parser, item_t *item, hid_interface_t *ifa
|
||||||
parser_state_t parser_state = {0}; // Avoid placing it on the stack, it's large
|
parser_state_t parser_state = {0}; // Avoid placing it on the stack, it's large
|
||||||
|
|
||||||
void parse_report_descriptor(hid_interface_t *iface,
|
void parse_report_descriptor(hid_interface_t *iface,
|
||||||
uint8_t const *report,
|
uint8_t const *report,
|
||||||
int desc_len
|
int desc_len
|
||||||
) {
|
) {
|
||||||
item_t item = {0};
|
item_t item = {0};
|
||||||
|
@ -181,5 +181,5 @@ void parse_report_descriptor(hid_interface_t *iface,
|
||||||
/* Move to the next position and decrement size by header length + data length */
|
/* Move to the next position and decrement size by header length + data length */
|
||||||
report += SIZE_LOOKUP[item.hdr.size];
|
report += SIZE_LOOKUP[item.hdr.size];
|
||||||
desc_len -= (SIZE_LOOKUP[item.hdr.size] + 1);
|
desc_len -= (SIZE_LOOKUP[item.hdr.size] + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,17 +55,17 @@ int32_t get_report_value(uint8_t *report, report_val_t *val) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* After processing the descriptor, assign the values so we can later use them to interpret reports */
|
/* After processing the descriptor, assign the values so we can later use them to interpret reports */
|
||||||
void handle_consumer_control_values(report_val_t *src, report_val_t *dst, hid_interface_t *iface) {
|
void handle_consumer_control_values(report_val_t *src, report_val_t *dst, hid_interface_t *iface) {
|
||||||
if (src->offset > MAX_CC_BUTTONS) {
|
if (src->offset > MAX_CC_BUTTONS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src->data_type == VARIABLE) {
|
if (src->data_type == VARIABLE) {
|
||||||
iface->keyboard.cc_array[src->offset] = src->usage;
|
iface->keyboard.cc_array[src->offset] = src->usage;
|
||||||
iface->consumer.is_variable = true;
|
iface->consumer.is_variable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
iface->consumer.is_array |= (src->data_type == ARRAY);
|
iface->consumer.is_array |= (src->data_type == ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* After processing the descriptor, assign the values so we can later use them to interpret reports */
|
/* After processing the descriptor, assign the values so we can later use them to interpret reports */
|
||||||
|
@ -73,13 +73,13 @@ void handle_system_control_values(report_val_t *src, report_val_t *dst, hid_inte
|
||||||
if (src->offset > MAX_SYS_BUTTONS) {
|
if (src->offset > MAX_SYS_BUTTONS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src->data_type == VARIABLE) {
|
if (src->data_type == VARIABLE) {
|
||||||
iface->keyboard.sys_array[src->offset] = src->usage;
|
iface->keyboard.sys_array[src->offset] = src->usage;
|
||||||
iface->system.is_variable = true;
|
iface->system.is_variable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
iface->system.is_array |= (src->data_type == ARRAY);
|
iface->system.is_array |= (src->data_type == ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* After processing the descriptor, assign the values so we can later use them to interpret reports */
|
/* After processing the descriptor, assign the values so we can later use them to interpret reports */
|
||||||
|
@ -143,54 +143,54 @@ void extract_data(hid_interface_t *iface, report_val_t *val) {
|
||||||
.id = &iface->mouse.report_id},
|
.id = &iface->mouse.report_id},
|
||||||
|
|
||||||
{.usage_page = HID_USAGE_PAGE_DESKTOP,
|
{.usage_page = HID_USAGE_PAGE_DESKTOP,
|
||||||
.global_usage = HID_USAGE_DESKTOP_MOUSE,
|
.global_usage = HID_USAGE_DESKTOP_MOUSE,
|
||||||
.usage = HID_USAGE_DESKTOP_X,
|
.usage = HID_USAGE_DESKTOP_X,
|
||||||
.handler = _store,
|
.handler = _store,
|
||||||
.receiver = process_mouse_report,
|
.receiver = process_mouse_report,
|
||||||
.dst = &iface->mouse.move_x,
|
.dst = &iface->mouse.move_x,
|
||||||
.id = &iface->mouse.report_id},
|
.id = &iface->mouse.report_id},
|
||||||
|
|
||||||
{.usage_page = HID_USAGE_PAGE_DESKTOP,
|
{.usage_page = HID_USAGE_PAGE_DESKTOP,
|
||||||
.global_usage = HID_USAGE_DESKTOP_MOUSE,
|
.global_usage = HID_USAGE_DESKTOP_MOUSE,
|
||||||
.usage = HID_USAGE_DESKTOP_Y,
|
.usage = HID_USAGE_DESKTOP_Y,
|
||||||
.handler = _store,
|
.handler = _store,
|
||||||
.receiver = process_mouse_report,
|
.receiver = process_mouse_report,
|
||||||
.dst = &iface->mouse.move_y,
|
.dst = &iface->mouse.move_y,
|
||||||
.id = &iface->mouse.report_id},
|
.id = &iface->mouse.report_id},
|
||||||
|
|
||||||
{.usage_page = HID_USAGE_PAGE_DESKTOP,
|
{.usage_page = HID_USAGE_PAGE_DESKTOP,
|
||||||
.global_usage = HID_USAGE_DESKTOP_MOUSE,
|
.global_usage = HID_USAGE_DESKTOP_MOUSE,
|
||||||
.usage = HID_USAGE_DESKTOP_WHEEL,
|
.usage = HID_USAGE_DESKTOP_WHEEL,
|
||||||
.handler = _store,
|
.handler = _store,
|
||||||
.receiver = process_mouse_report,
|
.receiver = process_mouse_report,
|
||||||
.dst = &iface->mouse.wheel,
|
.dst = &iface->mouse.wheel,
|
||||||
.id = &iface->mouse.report_id},
|
.id = &iface->mouse.report_id},
|
||||||
|
|
||||||
{.usage_page = HID_USAGE_PAGE_KEYBOARD,
|
{.usage_page = HID_USAGE_PAGE_KEYBOARD,
|
||||||
.global_usage = HID_USAGE_DESKTOP_KEYBOARD,
|
.global_usage = HID_USAGE_DESKTOP_KEYBOARD,
|
||||||
.handler = handle_keyboard_descriptor_values,
|
.handler = handle_keyboard_descriptor_values,
|
||||||
.receiver = process_keyboard_report,
|
.receiver = process_keyboard_report,
|
||||||
.id = &iface->keyboard.report_id},
|
.id = &iface->keyboard.report_id},
|
||||||
|
|
||||||
{.usage_page = HID_USAGE_PAGE_CONSUMER,
|
{.usage_page = HID_USAGE_PAGE_CONSUMER,
|
||||||
.global_usage = HID_USAGE_CONSUMER_CONTROL,
|
.global_usage = HID_USAGE_CONSUMER_CONTROL,
|
||||||
.handler = handle_consumer_control_values,
|
.handler = handle_consumer_control_values,
|
||||||
.receiver = process_consumer_report,
|
.receiver = process_consumer_report,
|
||||||
.dst = &iface->consumer.val,
|
.dst = &iface->consumer.val,
|
||||||
.id = &iface->consumer.report_id},
|
.id = &iface->consumer.report_id},
|
||||||
|
|
||||||
{.usage_page = HID_USAGE_PAGE_DESKTOP,
|
{.usage_page = HID_USAGE_PAGE_DESKTOP,
|
||||||
.global_usage = HID_USAGE_DESKTOP_SYSTEM_CONTROL,
|
.global_usage = HID_USAGE_DESKTOP_SYSTEM_CONTROL,
|
||||||
.handler = _store,
|
.handler = _store,
|
||||||
.receiver = process_system_report,
|
.receiver = process_system_report,
|
||||||
.dst = &iface->system.val,
|
.dst = &iface->system.val,
|
||||||
.id = &iface->system.report_id},
|
.id = &iface->system.report_id},
|
||||||
};
|
};
|
||||||
|
|
||||||
/* We extracted all we could find in the descriptor to report_values, now go through them and
|
/* We extracted all we could find in the descriptor to report_values, now go through them and
|
||||||
match them up with the values in the table above, then store those values for later reference */
|
match them up with the values in the table above, then store those values for later reference */
|
||||||
|
|
||||||
for (const usage_map_t *hay = map; hay != &map[ARRAY_SIZE(map)]; hay++) {
|
for (const usage_map_t *hay = map; hay != &map[ARRAY_SIZE(map)]; hay++) {
|
||||||
/* ---> If any condition is not defined, we consider it as matched <--- */
|
/* ---> If any condition is not defined, we consider it as matched <--- */
|
||||||
bool global_usages_match = (val->global_usage == hay->global_usage) || (hay->global_usage == 0);
|
bool global_usages_match = (val->global_usage == hay->global_usage) || (hay->global_usage == 0);
|
||||||
bool usages_match = (val->usage == hay->usage) || (hay->usage == 0);
|
bool usages_match = (val->usage == hay->usage) || (hay->usage == 0);
|
||||||
|
@ -236,7 +236,7 @@ int32_t _extract_kbd_other(uint8_t *raw_report, int len, hid_interface_t *iface,
|
||||||
uint8_t *src = raw_report;
|
uint8_t *src = raw_report;
|
||||||
keyboard_t *kb = &iface->keyboard;
|
keyboard_t *kb = &iface->keyboard;
|
||||||
|
|
||||||
if (iface->uses_report_id)
|
if (iface->uses_report_id)
|
||||||
src++;
|
src++;
|
||||||
|
|
||||||
report->modifier = src[kb->modifier.offset_idx];
|
report->modifier = src[kb->modifier.offset_idx];
|
||||||
|
@ -286,7 +286,7 @@ int32_t extract_kbd_data(
|
||||||
|
|
||||||
/* NKRO is a special case */
|
/* NKRO is a special case */
|
||||||
if (report_id > 0
|
if (report_id > 0
|
||||||
&& report_id == iface->keyboard.nkro.report_id
|
&& report_id == iface->keyboard.nkro.report_id
|
||||||
&& iface->keyboard.is_nkro)
|
&& iface->keyboard.is_nkro)
|
||||||
return _extract_kbd_nkro(raw_report, len, iface, report);
|
return _extract_kbd_nkro(raw_report, len, iface, report);
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ typedef struct {
|
||||||
int usage_page;
|
int usage_page;
|
||||||
int usage;
|
int usage;
|
||||||
uint8_t *id;
|
uint8_t *id;
|
||||||
report_val_t *dst;
|
report_val_t *dst;
|
||||||
value_handler_f handler;
|
value_handler_f handler;
|
||||||
process_report_f receiver;
|
process_report_f receiver;
|
||||||
} usage_map_t;
|
} usage_map_t;
|
||||||
|
|
|
@ -255,7 +255,7 @@ typedef struct {
|
||||||
uint8_t enforce_ports;
|
uint8_t enforce_ports;
|
||||||
uint16_t jump_treshold;
|
uint16_t jump_treshold;
|
||||||
|
|
||||||
output_t output[NUM_SCREENS];
|
output_t output[NUM_SCREENS];
|
||||||
uint32_t _reserved;
|
uint32_t _reserved;
|
||||||
|
|
||||||
// Keep checksum at the end of the struct
|
// Keep checksum at the end of the struct
|
||||||
|
@ -402,7 +402,7 @@ typedef struct {
|
||||||
bool onboard_led_state; // True when LED is ON
|
bool onboard_led_state; // True when LED is ON
|
||||||
bool relative_mouse; // True when relative mouse mode is used
|
bool relative_mouse; // True when relative mouse mode is used
|
||||||
bool config_mode_active; // True when config mode is active
|
bool config_mode_active; // True when config mode is active
|
||||||
|
|
||||||
/* Onboard LED blinky (provide feedback when e.g. mouse connected) */
|
/* Onboard LED blinky (provide feedback when e.g. mouse connected) */
|
||||||
int32_t blinks_left; // How many blink transitions are left
|
int32_t blinks_left; // How many blink transitions are left
|
||||||
int32_t last_led_change; // Timestamp of the last time led state transitioned
|
int32_t last_led_change; // Timestamp of the last time led state transitioned
|
||||||
|
@ -411,7 +411,7 @@ typedef struct {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void (*exec)(device_t *state);
|
void (*exec)(device_t *state);
|
||||||
uint64_t frequency;
|
uint64_t frequency;
|
||||||
uint64_t next_run;
|
uint64_t next_run;
|
||||||
bool *enabled;
|
bool *enabled;
|
||||||
} task_t;
|
} task_t;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ typedef enum {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t idx;
|
uint32_t idx;
|
||||||
bool readonly;
|
bool readonly;
|
||||||
type_e type;
|
type_e type;
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
} field_map_t;
|
} field_map_t;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* The MIT License (MIT)
|
* The MIT License (MIT)
|
||||||
*
|
*
|
||||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
* a key that is unlikely to ever appear on a keyboard that you will use.
|
* a key that is unlikely to ever appear on a keyboard that you will use.
|
||||||
* HID_KEY_F24 is probably a good choice as keyboards with 24 function keys
|
* HID_KEY_F24 is probably a good choice as keyboards with 24 function keys
|
||||||
* are rare.
|
* are rare.
|
||||||
*
|
*
|
||||||
* */
|
* */
|
||||||
|
|
||||||
#define HOTKEY_TOGGLE HID_KEY_F24
|
#define HOTKEY_TOGGLE HID_KEY_F24
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
* This is now configurable per-screen.
|
* This is now configurable per-screen.
|
||||||
*
|
*
|
||||||
* ENABLE_ACCELERATION: [0-1], disables or enables mouse acceleration.
|
* ENABLE_ACCELERATION: [0-1], disables or enables mouse acceleration.
|
||||||
*
|
*
|
||||||
* */
|
* */
|
||||||
|
|
||||||
/* Output A values */
|
/* Output A values */
|
||||||
|
@ -180,10 +180,10 @@
|
||||||
* ==================================================
|
* ==================================================
|
||||||
*
|
*
|
||||||
* If enabled, fixes some device incompatibilities by
|
* If enabled, fixes some device incompatibilities by
|
||||||
* enforcing the boot protocol (which is simpler to parse
|
* enforcing the boot protocol (which is simpler to parse
|
||||||
* and with less variation)
|
* and with less variation)
|
||||||
*
|
*
|
||||||
* ENFORCE_KEYBOARD_BOOT_PROTOCOL: [0, 1] - 1 means keyboard will forcefully use
|
* ENFORCE_KEYBOARD_BOOT_PROTOCOL: [0, 1] - 1 means keyboard will forcefully use
|
||||||
* the boot protocol
|
* the boot protocol
|
||||||
* - 0 means no such thing is enforced
|
* - 0 means no such thing is enforced
|
||||||
*
|
*
|
||||||
|
|
|
@ -39,13 +39,13 @@ hotkey_combo_t hotkeys[] = {
|
||||||
|
|
||||||
/* Switch lock */
|
/* Switch lock */
|
||||||
{.modifier = KEYBOARD_MODIFIER_RIGHTCTRL,
|
{.modifier = KEYBOARD_MODIFIER_RIGHTCTRL,
|
||||||
.keys = {HID_KEY_L},
|
.keys = {HID_KEY_K},
|
||||||
.key_count = 1,
|
.key_count = 1,
|
||||||
.acknowledge = true,
|
.acknowledge = true,
|
||||||
.action_handler = &switchlock_hotkey_handler},
|
.action_handler = &switchlock_hotkey_handler},
|
||||||
|
|
||||||
/* Screen lock */
|
/* Screen lock */
|
||||||
{.modifier = KEYBOARD_MODIFIER_RIGHTALT | KEYBOARD_MODIFIER_RIGHTSHIFT,
|
{.modifier = KEYBOARD_MODIFIER_RIGHTCTRL,
|
||||||
.keys = {HID_KEY_L},
|
.keys = {HID_KEY_L},
|
||||||
.key_count = 1,
|
.key_count = 1,
|
||||||
.acknowledge = true,
|
.acknowledge = true,
|
||||||
|
@ -162,7 +162,7 @@ void process_kbd_queue_task(device_t *state) {
|
||||||
void queue_kbd_report(hid_keyboard_report_t *report, device_t *state) {
|
void queue_kbd_report(hid_keyboard_report_t *report, device_t *state) {
|
||||||
/* It wouldn't be fun to queue up a bunch of messages and then dump them all on host */
|
/* It wouldn't be fun to queue up a bunch of messages and then dump them all on host */
|
||||||
if (!state->tud_connected)
|
if (!state->tud_connected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
queue_try_add(&state->kbd_queue, report);
|
queue_try_add(&state->kbd_queue, report);
|
||||||
}
|
}
|
||||||
|
@ -252,7 +252,7 @@ void process_consumer_report(uint8_t *raw_report, int length, uint8_t itf, hid_i
|
||||||
int byte_idx = i >> 3;
|
int byte_idx = i >> 3;
|
||||||
|
|
||||||
if ((raw_report[byte_idx + 1] >> bit_idx) & 1) {
|
if ((raw_report[byte_idx + 1] >> bit_idx) & 1) {
|
||||||
report_ptr[0] = iface->keyboard.cc_array[i];
|
report_ptr[0] = iface->keyboard.cc_array[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,6 +267,6 @@ void process_consumer_report(uint8_t *raw_report, int length, uint8_t itf, hid_i
|
||||||
void process_system_report(uint8_t *raw_report, int length, uint8_t itf, hid_interface_t *iface) {
|
void process_system_report(uint8_t *raw_report, int length, uint8_t itf, hid_interface_t *iface) {
|
||||||
uint16_t new_report = raw_report[1];
|
uint16_t new_report = raw_report[1];
|
||||||
uint8_t *report_ptr = (uint8_t *)&new_report;
|
uint8_t *report_ptr = (uint8_t *)&new_report;
|
||||||
|
|
||||||
send_system_control(report_ptr, &global_state);
|
send_system_control(report_ptr, &global_state);
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ void blink_led(device_t *state) {
|
||||||
void led_blinking_task(device_t *state) {
|
void led_blinking_task(device_t *state) {
|
||||||
const int blink_interval_us = 80000; /* 80 ms off, 80 ms on */
|
const int blink_interval_us = 80000; /* 80 ms off, 80 ms on */
|
||||||
static uint8_t leds;
|
static uint8_t leds;
|
||||||
|
|
||||||
/* If there is no more blinking to be done, exit immediately */
|
/* If there is no more blinking to be done, exit immediately */
|
||||||
if (state->blinks_left == 0)
|
if (state->blinks_left == 0)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -52,7 +52,7 @@ int main(void) {
|
||||||
while (true) {
|
while (true) {
|
||||||
for (int i = 0; i < NUM_TASKS; i++)
|
for (int i = 0; i < NUM_TASKS; i++)
|
||||||
task_scheduler(device, &tasks_core0[i]);
|
task_scheduler(device, &tasks_core0[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void core1_main() {
|
void core1_main() {
|
||||||
|
@ -64,14 +64,14 @@ void core1_main() {
|
||||||
[4] = {.exec = &firmware_upgrade_task, .frequency = _HZ(4000)}, // | Send firmware to the other board if needed
|
[4] = {.exec = &firmware_upgrade_task, .frequency = _HZ(4000)}, // | Send firmware to the other board if needed
|
||||||
[5] = {.exec = &heartbeat_output_task, .frequency = _HZ(1)}, // | Output periodic heartbeats
|
[5] = {.exec = &heartbeat_output_task, .frequency = _HZ(1)}, // | Output periodic heartbeats
|
||||||
}; // `----- then go back and repeat forever
|
}; // `----- then go back and repeat forever
|
||||||
const int NUM_TASKS = ARRAY_SIZE(tasks_core1);
|
const int NUM_TASKS = ARRAY_SIZE(tasks_core1);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// Update the timestamp, so core0 can figure out if we're dead
|
// Update the timestamp, so core0 can figure out if we're dead
|
||||||
device->core1_last_loop_pass = time_us_64();
|
device->core1_last_loop_pass = time_us_64();
|
||||||
|
|
||||||
for (int i = 0; i < NUM_TASKS; i++)
|
for (int i = 0; i < NUM_TASKS; i++)
|
||||||
task_scheduler(device, &tasks_core1[i]);
|
task_scheduler(device, &tasks_core1[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* ======= End of Main Program Loops ======= */
|
/* ======= End of Main Program Loops ======= */
|
||||||
|
|
12
src/mouse.c
12
src/mouse.c
|
@ -87,7 +87,7 @@ void output_mouse_report(mouse_report_t *report, device_t *state) {
|
||||||
state->last_activity[BOARD_ROLE] = time_us_64();
|
state->last_activity[BOARD_ROLE] = time_us_64();
|
||||||
} else {
|
} else {
|
||||||
queue_packet((uint8_t *)report, MOUSE_REPORT_MSG, MOUSE_REPORT_LENGTH);
|
queue_packet((uint8_t *)report, MOUSE_REPORT_MSG, MOUSE_REPORT_LENGTH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate and return Y coordinate when moving from screen out_from to screen out_to */
|
/* Calculate and return Y coordinate when moving from screen out_from to screen out_to */
|
||||||
|
@ -124,12 +124,12 @@ int16_t scale_y_coordinate(int screen_from, int screen_to, device_t *state) {
|
||||||
void switch_screen(
|
void switch_screen(
|
||||||
device_t *state, output_t *output, int new_x, int output_from, int output_to, int direction) {
|
device_t *state, output_t *output, int new_x, int output_from, int output_to, int direction) {
|
||||||
uint8_t *mouse_park_pos = &state->config.output[state->active_output].mouse_park_pos;
|
uint8_t *mouse_park_pos = &state->config.output[state->active_output].mouse_park_pos;
|
||||||
|
|
||||||
int16_t mouse_y = (*mouse_park_pos == 0) ? MIN_SCREEN_COORD : /* Top */
|
int16_t mouse_y = (*mouse_park_pos == 0) ? MIN_SCREEN_COORD : /* Top */
|
||||||
(*mouse_park_pos == 1) ? MAX_SCREEN_COORD : /* Bottom */
|
(*mouse_park_pos == 1) ? MAX_SCREEN_COORD : /* Bottom */
|
||||||
state->pointer_y; /* Previous */
|
state->pointer_y; /* Previous */
|
||||||
|
|
||||||
mouse_report_t hidden_pointer = {.y = mouse_y, .x = MAX_SCREEN_COORD};
|
mouse_report_t hidden_pointer = {.y = mouse_y, .x = MAX_SCREEN_COORD};
|
||||||
|
|
||||||
output_mouse_report(&hidden_pointer, state);
|
output_mouse_report(&hidden_pointer, state);
|
||||||
switch_output(state, output_to);
|
switch_output(state, output_to);
|
||||||
|
@ -138,7 +138,7 @@ void switch_screen(
|
||||||
}
|
}
|
||||||
|
|
||||||
void switch_desktop(device_t *state, output_t *output, int new_index, int direction) {
|
void switch_desktop(device_t *state, output_t *output, int new_index, int direction) {
|
||||||
/* Fix for MACOS: Send relative mouse movement here, one or two pixels in the
|
/* Fix for MACOS: Send relative mouse movement here, one or two pixels in the
|
||||||
direction of movement, BEFORE absolute report sets X to 0 */
|
direction of movement, BEFORE absolute report sets X to 0 */
|
||||||
mouse_report_t move_relative_one
|
mouse_report_t move_relative_one
|
||||||
= {.x = (direction == LEFT) ? SCREEN_MIDPOINT - 2 : SCREEN_MIDPOINT + 2, .mode = RELATIVE};
|
= {.x = (direction == LEFT) ? SCREEN_MIDPOINT - 2 : SCREEN_MIDPOINT + 2, .mode = RELATIVE};
|
||||||
|
@ -207,7 +207,7 @@ void check_screen_switch(const mouse_values_t *values, device_t *state) {
|
||||||
switch_desktop(state, output, output->screen_index + 1, direction);
|
switch_desktop(state, output, output->screen_index + 1, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
void extract_report_values(uint8_t *raw_report, device_t *state, mouse_values_t *values, hid_interface_t *iface) {
|
void extract_report_values(uint8_t *raw_report, device_t *state, mouse_values_t *values, hid_interface_t *iface) {
|
||||||
/* Interpret values depending on the current protocol used. */
|
/* Interpret values depending on the current protocol used. */
|
||||||
if (iface->protocol == HID_PROTOCOL_BOOT) {
|
if (iface->protocol == HID_PROTOCOL_BOOT) {
|
||||||
hid_mouse_report_t *mouse_report = (hid_mouse_report_t *)raw_report;
|
hid_mouse_report_t *mouse_report = (hid_mouse_report_t *)raw_report;
|
||||||
|
@ -267,7 +267,7 @@ void process_mouse_report(uint8_t *raw_report, int len, uint8_t itf, hid_interfa
|
||||||
output_mouse_report(&report, state);
|
output_mouse_report(&report, state);
|
||||||
|
|
||||||
/* We use the mouse to switch outputs, the logic is in check_screen_switch() */
|
/* We use the mouse to switch outputs, the logic is in check_screen_switch() */
|
||||||
check_screen_switch(&values, state);
|
check_screen_switch(&values, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==================================================== *
|
/* ==================================================== *
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
|
||||||
const field_map_t api_field_map[] = {
|
const field_map_t api_field_map[] = {
|
||||||
/* Index, Rdonly, Type, Len, Offset in struct */
|
/* Index, Rdonly, Type, Len, Offset in struct */
|
||||||
{ 0, true, UINT8, 1, offsetof(device_t, active_output) },
|
{ 0, true, UINT8, 1, offsetof(device_t, active_output) },
|
||||||
{ 1, true, INT16, 2, offsetof(device_t, pointer_x) },
|
{ 1, true, INT16, 2, offsetof(device_t, pointer_x) },
|
||||||
|
@ -22,7 +22,7 @@ const field_map_t api_field_map[] = {
|
||||||
|
|
||||||
/* Until we increase the payload size from 8 bytes, clamp to avoid exceeding the field size */
|
/* Until we increase the payload size from 8 bytes, clamp to avoid exceeding the field size */
|
||||||
{ 21, false, UINT64, 7, offsetof(device_t, config.output[0].screensaver.idle_time_us) },
|
{ 21, false, UINT64, 7, offsetof(device_t, config.output[0].screensaver.idle_time_us) },
|
||||||
{ 22, false, UINT64, 7, offsetof(device_t, config.output[0].screensaver.max_time_us) },
|
{ 22, false, UINT64, 7, offsetof(device_t, config.output[0].screensaver.max_time_us) },
|
||||||
|
|
||||||
/* Output B */
|
/* Output B */
|
||||||
{ 40, false, UINT32, 4, offsetof(device_t, config.output[1].number) },
|
{ 40, false, UINT32, 4, offsetof(device_t, config.output[1].number) },
|
||||||
|
@ -58,7 +58,7 @@ const field_map_t api_field_map[] = {
|
||||||
{ 82, true, UINT8, 1, offsetof(device_t, relative_mouse) },
|
{ 82, true, UINT8, 1, offsetof(device_t, relative_mouse) },
|
||||||
};
|
};
|
||||||
|
|
||||||
const field_map_t* get_field_map_entry(uint32_t index) {
|
const field_map_t* get_field_map_entry(uint32_t index) {
|
||||||
for (unsigned int i = 0; i < ARRAY_SIZE(api_field_map); i++) {
|
for (unsigned int i = 0; i < ARRAY_SIZE(api_field_map); i++) {
|
||||||
if (api_field_map[i].idx == index) {
|
if (api_field_map[i].idx == index) {
|
||||||
return &api_field_map[i];
|
return &api_field_map[i];
|
||||||
|
|
|
@ -50,12 +50,12 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buff
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* We lie about the image size - actually it's 64 kB, not 512 kB, so if we're out of bounds, return zeros */
|
/* We lie about the image size - actually it's 64 kB, not 512 kB, so if we're out of bounds, return zeros */
|
||||||
else if (lba >= ACTUAL_NUMBER_OF_BLOCKS)
|
else if (lba >= ACTUAL_NUMBER_OF_BLOCKS)
|
||||||
memset(buffer, 0x00, bufsize);
|
memset(buffer, 0x00, bufsize);
|
||||||
|
|
||||||
else
|
else
|
||||||
memcpy(buffer, addr, bufsize);
|
memcpy(buffer, addr, bufsize);
|
||||||
|
|
||||||
return (int32_t)bufsize;
|
return (int32_t)bufsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,31 +67,31 @@ bool tud_msc_is_writable_cb(uint8_t lun) {
|
||||||
/* Simple firmware write routine, we get 512-byte uf2 blocks with 256 byte payload */
|
/* Simple firmware write routine, we get 512-byte uf2 blocks with 256 byte payload */
|
||||||
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
|
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
|
||||||
uf2_t *uf2 = (uf2_t *)&buffer[0];
|
uf2_t *uf2 = (uf2_t *)&buffer[0];
|
||||||
bool is_final_block = uf2->blockNo == (STAGING_IMAGE_SIZE / FLASH_PAGE_SIZE) - 1;
|
bool is_final_block = uf2->blockNo == (STAGING_IMAGE_SIZE / FLASH_PAGE_SIZE) - 1;
|
||||||
uint32_t flash_addr = (uint32_t)ADDR_FW_RUNNING + uf2->blockNo * FLASH_PAGE_SIZE - XIP_BASE;
|
uint32_t flash_addr = (uint32_t)ADDR_FW_RUNNING + uf2->blockNo * FLASH_PAGE_SIZE - XIP_BASE;
|
||||||
|
|
||||||
if (lba >= NUMBER_OF_BLOCKS)
|
if (lba >= NUMBER_OF_BLOCKS)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* If we're not detecting UF2 magic constants, we have nothing to do... */
|
/* If we're not detecting UF2 magic constants, we have nothing to do... */
|
||||||
if (uf2->magicStart0 != UF2_MAGIC_START0 || uf2->magicStart1 != UF2_MAGIC_START1 || uf2->magicEnd != UF2_MAGIC_END)
|
if (uf2->magicStart0 != UF2_MAGIC_START0 || uf2->magicStart1 != UF2_MAGIC_START1 || uf2->magicEnd != UF2_MAGIC_END)
|
||||||
return (int32_t)bufsize;
|
return (int32_t)bufsize;
|
||||||
|
|
||||||
if (uf2->blockNo == 0) {
|
if (uf2->blockNo == 0) {
|
||||||
global_state.fw.checksum = 0xffffffff;
|
global_state.fw.checksum = 0xffffffff;
|
||||||
|
|
||||||
/* Make sure nobody else touches the flash during this operation, otherwise we get empty pages */
|
/* Make sure nobody else touches the flash during this operation, otherwise we get empty pages */
|
||||||
global_state.fw.upgrade_in_progress = true;
|
global_state.fw.upgrade_in_progress = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update checksum continuously as blocks are being received */
|
/* Update checksum continuously as blocks are being received */
|
||||||
const uint32_t last_block_with_checksum = (STAGING_IMAGE_SIZE - FLASH_SECTOR_SIZE) / FLASH_PAGE_SIZE;
|
const uint32_t last_block_with_checksum = (STAGING_IMAGE_SIZE - FLASH_SECTOR_SIZE) / FLASH_PAGE_SIZE;
|
||||||
for (int i=0; i<FLASH_PAGE_SIZE && uf2->blockNo < last_block_with_checksum; i++)
|
for (int i=0; i<FLASH_PAGE_SIZE && uf2->blockNo < last_block_with_checksum; i++)
|
||||||
global_state.fw.checksum = crc32_iter(global_state.fw.checksum, buffer[32 + i]);
|
global_state.fw.checksum = crc32_iter(global_state.fw.checksum, buffer[32 + i]);
|
||||||
|
|
||||||
write_flash_page(flash_addr, &buffer[32]);
|
write_flash_page(flash_addr, &buffer[32]);
|
||||||
|
|
||||||
if (is_final_block) {
|
if (is_final_block) {
|
||||||
global_state.fw.checksum = ~global_state.fw.checksum;
|
global_state.fw.checksum = ~global_state.fw.checksum;
|
||||||
|
|
||||||
/* If checksums don't match, overwrite first sector and rely on ROM bootloader for recovery */
|
/* If checksums don't match, overwrite first sector and rely on ROM bootloader for recovery */
|
||||||
|
@ -102,18 +102,17 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *
|
||||||
else {
|
else {
|
||||||
global_state.reboot_requested = true;
|
global_state.reboot_requested = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Provide some visual indication that fw is being uploaded */
|
/* Provide some visual indication that fw is being uploaded */
|
||||||
toggle_led();
|
toggle_led();
|
||||||
watchdog_update();
|
watchdog_update();
|
||||||
|
|
||||||
return (int32_t)bufsize;
|
return (int32_t)bufsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is a super-dumb, rudimentary disk, any other scsi command is simply rejected */
|
/* This is a super-dumb, rudimentary disk, any other scsi command is simply rejected */
|
||||||
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) {
|
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) {
|
||||||
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
|
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
40
src/setup.c
40
src/setup.c
|
@ -75,7 +75,7 @@ void pio_usb_host_config(device_t *state) {
|
||||||
and it will drive it either high or low at any given time
|
and it will drive it either high or low at any given time
|
||||||
- Before uart setup, enable it as an input
|
- Before uart setup, enable it as an input
|
||||||
- Go through a probing sequence of 8 values and pull either up or down
|
- Go through a probing sequence of 8 values and pull either up or down
|
||||||
to that value
|
to that value
|
||||||
- Read out the value on the RX pin
|
- Read out the value on the RX pin
|
||||||
- If the entire sequence of values match, we are definitely floating
|
- If the entire sequence of values match, we are definitely floating
|
||||||
so IC is not connected on BOARD_A_RX, and we're BOARD B
|
so IC is not connected on BOARD_A_RX, and we're BOARD B
|
||||||
|
@ -84,22 +84,22 @@ int board_autoprobe(void) {
|
||||||
const bool probing_sequence[] = {true, false, false, true, true, false, true, false};
|
const bool probing_sequence[] = {true, false, false, true, true, false, true, false};
|
||||||
const int seq_len = ARRAY_SIZE(probing_sequence);
|
const int seq_len = ARRAY_SIZE(probing_sequence);
|
||||||
|
|
||||||
/* Set the pin as INPUT and initialize it */
|
/* Set the pin as INPUT and initialize it */
|
||||||
gpio_init(BOARD_A_RX);
|
gpio_init(BOARD_A_RX);
|
||||||
gpio_set_dir(BOARD_A_RX, GPIO_IN);
|
gpio_set_dir(BOARD_A_RX, GPIO_IN);
|
||||||
|
|
||||||
for (int i=0; i<seq_len; i++) {
|
for (int i=0; i<seq_len; i++) {
|
||||||
if (probing_sequence[i])
|
if (probing_sequence[i])
|
||||||
gpio_pull_up(BOARD_A_RX);
|
gpio_pull_up(BOARD_A_RX);
|
||||||
else
|
else
|
||||||
gpio_pull_down(BOARD_A_RX);
|
gpio_pull_down(BOARD_A_RX);
|
||||||
|
|
||||||
/* Wait for value to settle */
|
/* Wait for value to settle */
|
||||||
sleep_ms(3);
|
sleep_ms(3);
|
||||||
|
|
||||||
/* Read the value */
|
/* Read the value */
|
||||||
bool value = gpio_get(BOARD_A_RX);
|
bool value = gpio_get(BOARD_A_RX);
|
||||||
gpio_disable_pulls(BOARD_A_RX);
|
gpio_disable_pulls(BOARD_A_RX);
|
||||||
|
|
||||||
/* If values mismatch at any point, means IC is connected and we're board A */
|
/* If values mismatch at any point, means IC is connected and we're board A */
|
||||||
if (probing_sequence[i] != value)
|
if (probing_sequence[i] != value)
|
||||||
|
@ -122,7 +122,7 @@ bool is_config_mode_active(device_t *state) {
|
||||||
|
|
||||||
/* Remove, so next reboot it's no longer active */
|
/* Remove, so next reboot it's no longer active */
|
||||||
if (is_active)
|
if (is_active)
|
||||||
watchdog_hw->scratch[5] = 0;
|
watchdog_hw->scratch[5] = 0;
|
||||||
|
|
||||||
reset_config_timer(state);
|
reset_config_timer(state);
|
||||||
|
|
||||||
|
@ -131,23 +131,23 @@ bool is_config_mode_active(device_t *state) {
|
||||||
|
|
||||||
|
|
||||||
/* ================================================== *
|
/* ================================================== *
|
||||||
* Configure DMA for reliable UART transfers
|
* Configure DMA for reliable UART transfers
|
||||||
* ================================================== */
|
* ================================================== */
|
||||||
const uint8_t* uart_buffer_pointers[1] = {uart_rxbuf};
|
const uint8_t* uart_buffer_pointers[1] = {uart_rxbuf};
|
||||||
uint8_t uart_rxbuf[DMA_RX_BUFFER_SIZE] __attribute__((aligned(DMA_RX_BUFFER_SIZE))) ;
|
uint8_t uart_rxbuf[DMA_RX_BUFFER_SIZE] __attribute__((aligned(DMA_RX_BUFFER_SIZE))) ;
|
||||||
uint8_t uart_txbuf[DMA_TX_BUFFER_SIZE] __attribute__((aligned(DMA_TX_BUFFER_SIZE))) ;
|
uint8_t uart_txbuf[DMA_TX_BUFFER_SIZE] __attribute__((aligned(DMA_TX_BUFFER_SIZE))) ;
|
||||||
|
|
||||||
static void configure_tx_dma(device_t *state) {
|
static void configure_tx_dma(device_t *state) {
|
||||||
state->dma_tx_channel = dma_claim_unused_channel(true);
|
state->dma_tx_channel = dma_claim_unused_channel(true);
|
||||||
|
|
||||||
dma_channel_config tx_config = dma_channel_get_default_config(state->dma_tx_channel);
|
dma_channel_config tx_config = dma_channel_get_default_config(state->dma_tx_channel);
|
||||||
channel_config_set_transfer_data_size(&tx_config, DMA_SIZE_8);
|
channel_config_set_transfer_data_size(&tx_config, DMA_SIZE_8);
|
||||||
|
|
||||||
/* Writing uart (always write the same address, but source addr changes as we read) */
|
/* Writing uart (always write the same address, but source addr changes as we read) */
|
||||||
channel_config_set_read_increment(&tx_config, true);
|
channel_config_set_read_increment(&tx_config, true);
|
||||||
channel_config_set_write_increment(&tx_config, false);
|
channel_config_set_write_increment(&tx_config, false);
|
||||||
|
|
||||||
// channel_config_set_ring(&tx_config, false, 4);
|
// channel_config_set_ring(&tx_config, false, 4);
|
||||||
channel_config_set_dreq(&tx_config, DREQ_UART0_TX);
|
channel_config_set_dreq(&tx_config, DREQ_UART0_TX);
|
||||||
|
|
||||||
/* Configure, but don't start immediately. We'll do this each time the outgoing
|
/* Configure, but don't start immediately. We'll do this each time the outgoing
|
||||||
|
@ -167,7 +167,7 @@ static void configure_rx_dma(device_t *state) {
|
||||||
state->dma_rx_channel = dma_claim_unused_channel(true);
|
state->dma_rx_channel = dma_claim_unused_channel(true);
|
||||||
state->dma_control_channel = dma_claim_unused_channel(true);
|
state->dma_control_channel = dma_claim_unused_channel(true);
|
||||||
|
|
||||||
dma_channel_config config = dma_channel_get_default_config(state->dma_rx_channel);
|
dma_channel_config config = dma_channel_get_default_config(state->dma_rx_channel);
|
||||||
dma_channel_config control_config = dma_channel_get_default_config(state->dma_control_channel);
|
dma_channel_config control_config = dma_channel_get_default_config(state->dma_control_channel);
|
||||||
|
|
||||||
channel_config_set_transfer_data_size(&config, DMA_SIZE_8);
|
channel_config_set_transfer_data_size(&config, DMA_SIZE_8);
|
||||||
|
@ -185,16 +185,16 @@ static void configure_rx_dma(device_t *state) {
|
||||||
|
|
||||||
// The UART signals when data is avaliable
|
// The UART signals when data is avaliable
|
||||||
channel_config_set_dreq(&config, DREQ_UART0_RX);
|
channel_config_set_dreq(&config, DREQ_UART0_RX);
|
||||||
|
|
||||||
channel_config_set_chain_to(&config, state->dma_control_channel);
|
channel_config_set_chain_to(&config, state->dma_control_channel);
|
||||||
|
|
||||||
dma_channel_configure(
|
dma_channel_configure(
|
||||||
state->dma_rx_channel,
|
state->dma_rx_channel,
|
||||||
&config,
|
&config,
|
||||||
uart_rxbuf,
|
uart_rxbuf,
|
||||||
&uart0_hw->dr,
|
&uart0_hw->dr,
|
||||||
DMA_RX_BUFFER_SIZE,
|
DMA_RX_BUFFER_SIZE,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
dma_channel_configure(
|
dma_channel_configure(
|
||||||
state->dma_control_channel,
|
state->dma_control_channel,
|
||||||
|
@ -204,14 +204,14 @@ static void configure_rx_dma(device_t *state) {
|
||||||
1,
|
1,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
dma_channel_start(state->dma_control_channel);
|
dma_channel_start(state->dma_control_channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ================================================== *
|
/* ================================================== *
|
||||||
* Perform initial board/usb setup
|
* Perform initial board/usb setup
|
||||||
* ================================================== */
|
* ================================================== */
|
||||||
int board;
|
int board;
|
||||||
|
|
||||||
void initial_setup(device_t *state) {
|
void initial_setup(device_t *state) {
|
||||||
/* PIO USB requires a clock multiple of 12 MHz, setting to 120 MHz */
|
/* PIO USB requires a clock multiple of 12 MHz, setting to 120 MHz */
|
||||||
|
@ -248,12 +248,12 @@ void initial_setup(device_t *state) {
|
||||||
multicore_launch_core1(core1_main);
|
multicore_launch_core1(core1_main);
|
||||||
|
|
||||||
/* Initialize and configure TinyUSB Device */
|
/* Initialize and configure TinyUSB Device */
|
||||||
tud_init(BOARD_TUD_RHPORT);
|
tud_init(BOARD_TUD_RHPORT);
|
||||||
|
|
||||||
/* Initialize and configure TinyUSB Host */
|
/* Initialize and configure TinyUSB Host */
|
||||||
pio_usb_host_config(state);
|
pio_usb_host_config(state);
|
||||||
|
|
||||||
/* Initialize and configure DMA */
|
/* Initialize and configure DMA */
|
||||||
configure_tx_dma(state);
|
configure_tx_dma(state);
|
||||||
configure_rx_dma(state);
|
configure_rx_dma(state);
|
||||||
|
|
||||||
|
@ -262,7 +262,7 @@ void initial_setup(device_t *state) {
|
||||||
|
|
||||||
/* Update the core1 initial pass timestamp before enabling the watchdog */
|
/* Update the core1 initial pass timestamp before enabling the watchdog */
|
||||||
state->core1_last_loop_pass = time_us_64();
|
state->core1_last_loop_pass = time_us_64();
|
||||||
|
|
||||||
/* Setup the watchdog so we reboot and recover from a crash */
|
/* Setup the watchdog so we reboot and recover from a crash */
|
||||||
watchdog_enable(WATCHDOG_TIMEOUT, WATCHDOG_PAUSE_ON_DEBUG);
|
watchdog_enable(WATCHDOG_TIMEOUT, WATCHDOG_PAUSE_ON_DEBUG);
|
||||||
}
|
}
|
||||||
|
|
26
src/tasks.c
26
src/tasks.c
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
void task_scheduler(device_t *state, task_t *task) {
|
void task_scheduler(device_t *state, task_t *task) {
|
||||||
uint64_t current_time = time_us_64();
|
uint64_t current_time = time_us_64();
|
||||||
|
|
||||||
if (current_time < task->next_run)
|
if (current_time < task->next_run)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ void kick_watchdog_task(device_t *state) {
|
||||||
so it doesn't get updated in the meantime. */
|
so it doesn't get updated in the meantime. */
|
||||||
uint64_t core1_last_loop_pass = state->core1_last_loop_pass;
|
uint64_t core1_last_loop_pass = state->core1_last_loop_pass;
|
||||||
uint64_t current_time = time_us_64();
|
uint64_t current_time = time_us_64();
|
||||||
|
|
||||||
/* If a reboot is requested, we'll stop updating watchdog */
|
/* If a reboot is requested, we'll stop updating watchdog */
|
||||||
if (state->reboot_requested)
|
if (state->reboot_requested)
|
||||||
return;
|
return;
|
||||||
|
@ -64,7 +64,7 @@ void screensaver_task(device_t *state) {
|
||||||
const int mouse_move_delay = 5000;
|
const int mouse_move_delay = 5000;
|
||||||
screensaver_t *screensaver = &state->config.output[BOARD_ROLE].screensaver;
|
screensaver_t *screensaver = &state->config.output[BOARD_ROLE].screensaver;
|
||||||
uint64_t inactivity_period = time_us_64() - state->last_activity[BOARD_ROLE];
|
uint64_t inactivity_period = time_us_64() - state->last_activity[BOARD_ROLE];
|
||||||
|
|
||||||
static mouse_report_t report = {0};
|
static mouse_report_t report = {0};
|
||||||
static int last_pointer_move = 0;
|
static int last_pointer_move = 0;
|
||||||
static int dx = 20, dy = 25, jitter = 1;
|
static int dx = 20, dy = 25, jitter = 1;
|
||||||
|
@ -119,11 +119,11 @@ void screensaver_task(device_t *state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Periodically emit heartbeat packets */
|
/* Periodically emit heartbeat packets */
|
||||||
void heartbeat_output_task(device_t *state) {
|
void heartbeat_output_task(device_t *state) {
|
||||||
/* If firmware upgrade is in progress, don't touch flash_cs */
|
/* If firmware upgrade is in progress, don't touch flash_cs */
|
||||||
if (state->fw.upgrade_in_progress)
|
if (state->fw.upgrade_in_progress)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (state->config_mode_active) {
|
if (state->config_mode_active) {
|
||||||
/* Leave config mode if timeout expired and user didn't click exit */
|
/* Leave config mode if timeout expired and user didn't click exit */
|
||||||
if (time_us_64() > state->config_mode_timer)
|
if (time_us_64() > state->config_mode_timer)
|
||||||
|
@ -140,12 +140,12 @@ void heartbeat_output_task(device_t *state) {
|
||||||
uart_packet_t packet = {
|
uart_packet_t packet = {
|
||||||
.type = HEARTBEAT_MSG,
|
.type = HEARTBEAT_MSG,
|
||||||
.data16 = {
|
.data16 = {
|
||||||
[0] = state->_running_fw.version,
|
[0] = state->_running_fw.version,
|
||||||
[2] = state->active_output,
|
[2] = state->active_output,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
queue_try_add(&global_state.uart_tx_queue, &packet);
|
queue_try_add(&global_state.uart_tx_queue, &packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process outgoing config report messages. */
|
/* Process outgoing config report messages. */
|
||||||
|
@ -194,14 +194,14 @@ void firmware_upgrade_task(device_t *state) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we're on the last element of the current page, page is done - write it. */
|
/* If we're on the last element of the current page, page is done - write it. */
|
||||||
if (TU_U32_BYTE0(state->fw.address) == 0x00) {
|
if (TU_U32_BYTE0(state->fw.address) == 0x00) {
|
||||||
|
|
||||||
uint32_t page_start_addr = (state->fw.address - 1) & 0xFFFFFF00;
|
uint32_t page_start_addr = (state->fw.address - 1) & 0xFFFFFF00;
|
||||||
write_flash_page((uint32_t)ADDR_FW_RUNNING + page_start_addr - XIP_BASE, state->page_buffer);
|
write_flash_page((uint32_t)ADDR_FW_RUNNING + page_start_addr - XIP_BASE, state->page_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
request_byte(state, state->fw.address);
|
request_byte(state, state->fw.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
void packet_receiver_task(device_t *state) {
|
void packet_receiver_task(device_t *state) {
|
||||||
|
@ -210,7 +210,7 @@ void packet_receiver_task(device_t *state) {
|
||||||
uint32_t delta = get_ptr_delta(current_pointer, state);
|
uint32_t delta = get_ptr_delta(current_pointer, state);
|
||||||
|
|
||||||
/* If we don't have enough characters for a packet, skip loop and return immediately */
|
/* If we don't have enough characters for a packet, skip loop and return immediately */
|
||||||
while (delta >= RAW_PACKET_LENGTH) {
|
while (delta >= RAW_PACKET_LENGTH) {
|
||||||
if (is_start_of_packet(state)) {
|
if (is_start_of_packet(state)) {
|
||||||
fetch_packet(state);
|
fetch_packet(state);
|
||||||
process_packet(&state->in_packet, state);
|
process_packet(&state->in_packet, state);
|
||||||
|
|
16
src/uart.c
16
src/uart.c
|
@ -22,15 +22,15 @@
|
||||||
* ================================================== */
|
* ================================================== */
|
||||||
|
|
||||||
/* Takes a packet as uart_packet_t struct, adds preamble, checksum and encodes it to a raw array. */
|
/* Takes a packet as uart_packet_t struct, adds preamble, checksum and encodes it to a raw array. */
|
||||||
void write_raw_packet(uint8_t *dst, uart_packet_t *packet) {
|
void write_raw_packet(uint8_t *dst, uart_packet_t *packet) {
|
||||||
uint8_t pkt[RAW_PACKET_LENGTH] = {[0] = START1,
|
uint8_t pkt[RAW_PACKET_LENGTH] = {[0] = START1,
|
||||||
[1] = START2,
|
[1] = START2,
|
||||||
[2] = packet->type,
|
[2] = packet->type,
|
||||||
/* [3-10] is data, defaults to 0 */
|
/* [3-10] is data, defaults to 0 */
|
||||||
[11] = calc_checksum(packet->data, PACKET_DATA_LENGTH)};
|
[11] = calc_checksum(packet->data, PACKET_DATA_LENGTH)};
|
||||||
|
|
||||||
memcpy(&pkt[START_LENGTH + TYPE_LENGTH], packet->data, PACKET_DATA_LENGTH);
|
memcpy(&pkt[START_LENGTH + TYPE_LENGTH], packet->data, PACKET_DATA_LENGTH);
|
||||||
memcpy(dst, &pkt, RAW_PACKET_LENGTH);
|
memcpy(dst, &pkt, RAW_PACKET_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Schedule packet for sending to the other box */
|
/* Schedule packet for sending to the other box */
|
||||||
|
@ -38,7 +38,7 @@ void queue_packet(const uint8_t *data, enum packet_type_e packet_type, int lengt
|
||||||
uart_packet_t packet = {.type = packet_type};
|
uart_packet_t packet = {.type = packet_type};
|
||||||
memcpy(packet.data, data, length);
|
memcpy(packet.data, data, length);
|
||||||
|
|
||||||
queue_try_add(&global_state.uart_tx_queue, &packet);
|
queue_try_add(&global_state.uart_tx_queue, &packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sends just one byte of a certain packet type to the other box. */
|
/* Sends just one byte of a certain packet type to the other box. */
|
||||||
|
@ -56,7 +56,7 @@ void process_uart_tx_task(device_t *state) {
|
||||||
if (!queue_try_remove(&state->uart_tx_queue, &packet))
|
if (!queue_try_remove(&state->uart_tx_queue, &packet))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
write_raw_packet(uart_txbuf, &packet);
|
write_raw_packet(uart_txbuf, &packet);
|
||||||
dma_channel_transfer_from_buffer_now(state->dma_tx_channel, uart_txbuf, RAW_PACKET_LENGTH);
|
dma_channel_transfer_from_buffer_now(state->dma_tx_channel, uart_txbuf, RAW_PACKET_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ const uart_handler_t uart_handler[] = {
|
||||||
{.type = KEYBOARD_REPORT_MSG, .handler = handle_keyboard_uart_msg},
|
{.type = KEYBOARD_REPORT_MSG, .handler = handle_keyboard_uart_msg},
|
||||||
{.type = MOUSE_REPORT_MSG, .handler = handle_mouse_abs_uart_msg},
|
{.type = MOUSE_REPORT_MSG, .handler = handle_mouse_abs_uart_msg},
|
||||||
{.type = OUTPUT_SELECT_MSG, .handler = handle_output_select_msg},
|
{.type = OUTPUT_SELECT_MSG, .handler = handle_output_select_msg},
|
||||||
|
|
||||||
/* Box control */
|
/* Box control */
|
||||||
{.type = MOUSE_ZOOM_MSG, .handler = handle_mouse_zoom_msg},
|
{.type = MOUSE_ZOOM_MSG, .handler = handle_mouse_zoom_msg},
|
||||||
{.type = KBD_SET_REPORT_MSG, .handler = handle_set_report_msg},
|
{.type = KBD_SET_REPORT_MSG, .handler = handle_set_report_msg},
|
||||||
|
@ -77,7 +77,7 @@ const uart_handler_t uart_handler[] = {
|
||||||
{.type = SYNC_BORDERS_MSG, .handler = handle_sync_borders_msg},
|
{.type = SYNC_BORDERS_MSG, .handler = handle_sync_borders_msg},
|
||||||
{.type = FLASH_LED_MSG, .handler = handle_flash_led_msg},
|
{.type = FLASH_LED_MSG, .handler = handle_flash_led_msg},
|
||||||
{.type = CONSUMER_CONTROL_MSG, .handler = handle_consumer_control_msg},
|
{.type = CONSUMER_CONTROL_MSG, .handler = handle_consumer_control_msg},
|
||||||
|
|
||||||
/* Config */
|
/* Config */
|
||||||
{.type = WIPE_CONFIG_MSG, .handler = handle_wipe_config_msg},
|
{.type = WIPE_CONFIG_MSG, .handler = handle_wipe_config_msg},
|
||||||
{.type = SAVE_CONFIG_MSG, .handler = handle_save_config_msg},
|
{.type = SAVE_CONFIG_MSG, .handler = handle_save_config_msg},
|
||||||
|
@ -90,7 +90,7 @@ const uart_handler_t uart_handler[] = {
|
||||||
{.type = RESPONSE_BYTE_MSG, .handler = handle_response_byte_msg},
|
{.type = RESPONSE_BYTE_MSG, .handler = handle_response_byte_msg},
|
||||||
{.type = FIRMWARE_UPGRADE_MSG, .handler = handle_fw_upgrade_msg},
|
{.type = FIRMWARE_UPGRADE_MSG, .handler = handle_fw_upgrade_msg},
|
||||||
|
|
||||||
{.type = HEARTBEAT_MSG, .handler = handle_heartbeat_msg},
|
{.type = HEARTBEAT_MSG, .handler = handle_heartbeat_msg},
|
||||||
{.type = PROXY_PACKET_MSG, .handler = handle_proxy_msg},
|
{.type = PROXY_PACKET_MSG, .handler = handle_proxy_msg},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
36
src/usb.c
36
src/usb.c
|
@ -28,7 +28,7 @@ uint16_t tud_hid_get_report_cb(uint8_t instance,
|
||||||
uint8_t report_id,
|
uint8_t report_id,
|
||||||
hid_report_type_t report_type,
|
hid_report_type_t report_type,
|
||||||
uint8_t *buffer,
|
uint8_t *buffer,
|
||||||
uint16_t request_len) {
|
uint16_t request_len) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,15 +46,15 @@ void tud_hid_set_report_cb(uint8_t instance,
|
||||||
uint16_t bufsize) {
|
uint16_t bufsize) {
|
||||||
|
|
||||||
/* We received a report on the config report ID */
|
/* We received a report on the config report ID */
|
||||||
if (instance == ITF_NUM_HID_VENDOR && report_id == REPORT_ID_VENDOR) {
|
if (instance == ITF_NUM_HID_VENDOR && report_id == REPORT_ID_VENDOR) {
|
||||||
/* Security - only if config mode is enabled are we allowed to do anything. While the report_id
|
/* Security - only if config mode is enabled are we allowed to do anything. While the report_id
|
||||||
isn't even advertised when not in config mode, security must always be explicit and never assume */
|
isn't even advertised when not in config mode, security must always be explicit and never assume */
|
||||||
if (!global_state.config_mode_active)
|
if (!global_state.config_mode_active)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* We insist on a fixed size packet. No overflows. */
|
/* We insist on a fixed size packet. No overflows. */
|
||||||
if (bufsize != RAW_PACKET_LENGTH)
|
if (bufsize != RAW_PACKET_LENGTH)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uart_packet_t *packet = (uart_packet_t *) (buffer + START_LENGTH);
|
uart_packet_t *packet = (uart_packet_t *) (buffer + START_LENGTH);
|
||||||
|
|
||||||
|
@ -62,8 +62,8 @@ void tud_hid_set_report_cb(uint8_t instance,
|
||||||
if (!validate_packet(packet))
|
if (!validate_packet(packet))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
process_packet(packet, &global_state);
|
process_packet(packet, &global_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Only other set report we care about is LED state change, and that's exactly 1 byte long */
|
/* Only other set report we care about is LED state change, and that's exactly 1 byte long */
|
||||||
if (report_id != REPORT_ID_KEYBOARD || bufsize != 1 || report_type != HID_REPORT_TYPE_OUTPUT)
|
if (report_id != REPORT_ID_KEYBOARD || bufsize != 1 || report_type != HID_REPORT_TYPE_OUTPUT)
|
||||||
|
@ -125,36 +125,36 @@ void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
|
||||||
memset(iface, 0, sizeof(hid_interface_t));
|
memset(iface, 0, sizeof(hid_interface_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
|
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
|
||||||
uint8_t itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
|
uint8_t itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
|
||||||
|
|
||||||
if (dev_addr >= MAX_DEVICES || instance > MAX_INTERFACES)
|
if (dev_addr >= MAX_DEVICES || instance > MAX_INTERFACES)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Get interface information */
|
/* Get interface information */
|
||||||
hid_interface_t *iface = &global_state.iface[dev_addr-1][instance];
|
hid_interface_t *iface = &global_state.iface[dev_addr-1][instance];
|
||||||
|
|
||||||
iface->protocol = tuh_hid_get_protocol(dev_addr, instance);
|
iface->protocol = tuh_hid_get_protocol(dev_addr, instance);
|
||||||
|
|
||||||
/* Safeguard against memory corruption in case the number of instances exceeds our maximum */
|
/* Safeguard against memory corruption in case the number of instances exceeds our maximum */
|
||||||
if (instance >= MAX_INTERFACES)
|
if (instance >= MAX_INTERFACES)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Parse the report descriptor into our internal structure. */
|
/* Parse the report descriptor into our internal structure. */
|
||||||
parse_report_descriptor(iface, desc_report, desc_len);
|
parse_report_descriptor(iface, desc_report, desc_len);
|
||||||
|
|
||||||
switch (itf_protocol) {
|
switch (itf_protocol) {
|
||||||
case HID_ITF_PROTOCOL_KEYBOARD:
|
case HID_ITF_PROTOCOL_KEYBOARD:
|
||||||
if (global_state.config.enforce_ports && BOARD_ROLE == OUTPUT_B)
|
if (global_state.config.enforce_ports && BOARD_ROLE == OUTPUT_B)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (global_state.config.force_kbd_boot_protocol)
|
if (global_state.config.force_kbd_boot_protocol)
|
||||||
tuh_hid_set_protocol(dev_addr, instance, HID_PROTOCOL_BOOT);
|
tuh_hid_set_protocol(dev_addr, instance, HID_PROTOCOL_BOOT);
|
||||||
|
|
||||||
/* Keeping this is required for setting leds from device set_report callback */
|
/* Keeping this is required for setting leds from device set_report callback */
|
||||||
global_state.kbd_dev_addr = dev_addr;
|
global_state.kbd_dev_addr = dev_addr;
|
||||||
global_state.kbd_instance = instance;
|
global_state.kbd_instance = instance;
|
||||||
global_state.keyboard_connected = true;
|
global_state.keyboard_connected = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HID_ITF_PROTOCOL_MOUSE:
|
case HID_ITF_PROTOCOL_MOUSE:
|
||||||
|
@ -167,8 +167,8 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_re
|
||||||
tuh_hid_set_protocol(dev_addr, instance, HID_PROTOCOL_REPORT);
|
tuh_hid_set_protocol(dev_addr, instance, HID_PROTOCOL_REPORT);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HID_ITF_PROTOCOL_NONE:
|
case HID_ITF_PROTOCOL_NONE:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Flash local led to indicate a device was connected */
|
/* Flash local led to indicate a device was connected */
|
||||||
|
@ -177,7 +177,7 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_re
|
||||||
/* Also signal the other board to flash LED, to enable easy verification if serial works */
|
/* Also signal the other board to flash LED, to enable easy verification if serial works */
|
||||||
send_value(ENABLE, FLASH_LED_MSG);
|
send_value(ENABLE, FLASH_LED_MSG);
|
||||||
|
|
||||||
/* Kick off the report querying */
|
/* Kick off the report querying */
|
||||||
tuh_hid_receive_report(dev_addr, instance);
|
tuh_hid_receive_report(dev_addr, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons
|
||||||
|
|
||||||
if (receiver != NULL)
|
if (receiver != NULL)
|
||||||
receiver((uint8_t *)report, len, itf_protocol, iface);
|
receiver((uint8_t *)report, len, itf_protocol, iface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD) {
|
else if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD) {
|
||||||
process_keyboard_report((uint8_t *)report, len, itf_protocol, iface);
|
process_keyboard_report((uint8_t *)report, len, itf_protocol, iface);
|
||||||
|
@ -216,7 +216,7 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set protocol in a callback. This is tied to an interface, not a specific report ID */
|
/* Set protocol in a callback. This is tied to an interface, not a specific report ID */
|
||||||
void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol) {
|
void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol) {
|
||||||
if (dev_addr >= MAX_DEVICES || idx > MAX_INTERFACES)
|
if (dev_addr >= MAX_DEVICES || idx > MAX_INTERFACES)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -33,14 +33,14 @@
|
||||||
|
|
||||||
// https://github.com/raspberrypi/usb-pid
|
// https://github.com/raspberrypi/usb-pid
|
||||||
tusb_desc_device_t const desc_device = DEVICE_DESCRIPTOR(0x2e8a, 0x107c);
|
tusb_desc_device_t const desc_device = DEVICE_DESCRIPTOR(0x2e8a, 0x107c);
|
||||||
|
|
||||||
// https://pid.codes/1209/C000/
|
// https://pid.codes/1209/C000/
|
||||||
tusb_desc_device_t const desc_device_config = DEVICE_DESCRIPTOR(0x1209, 0xc000);
|
tusb_desc_device_t const desc_device_config = DEVICE_DESCRIPTOR(0x1209, 0xc000);
|
||||||
|
|
||||||
// Invoked when received GET DEVICE DESCRIPTOR
|
// Invoked when received GET DEVICE DESCRIPTOR
|
||||||
// Application return pointer to descriptor
|
// Application return pointer to descriptor
|
||||||
uint8_t const *tud_descriptor_device_cb(void) {
|
uint8_t const *tud_descriptor_device_cb(void) {
|
||||||
if (global_state.config_mode_active)
|
if (global_state.config_mode_active)
|
||||||
return (uint8_t const *)&desc_device_config;
|
return (uint8_t const *)&desc_device_config;
|
||||||
else
|
else
|
||||||
return (uint8_t const *)&desc_device;
|
return (uint8_t const *)&desc_device;
|
||||||
|
@ -75,10 +75,10 @@ uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) {
|
||||||
case ITF_NUM_HID:
|
case ITF_NUM_HID:
|
||||||
return desc_hid_report;
|
return desc_hid_report;
|
||||||
case ITF_NUM_HID_REL_M:
|
case ITF_NUM_HID_REL_M:
|
||||||
return desc_hid_report_relmouse;
|
return desc_hid_report_relmouse;
|
||||||
default:
|
default:
|
||||||
return desc_hid_report;
|
return desc_hid_report;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tud_mouse_report(uint8_t mode, uint8_t buttons, int16_t x, int16_t y, int8_t wheel) {
|
bool tud_mouse_report(uint8_t mode, uint8_t buttons, int16_t x, int16_t y, int8_t wheel) {
|
||||||
|
@ -253,14 +253,14 @@ uint8_t const desc_configuration_config[] = {
|
||||||
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||||
TUD_CDC_DESCRIPTOR(
|
TUD_CDC_DESCRIPTOR(
|
||||||
ITF_NUM_CDC, STRID_DEBUG, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, CFG_TUD_CDC_EP_BUFSIZE),
|
ITF_NUM_CDC, STRID_DEBUG, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, CFG_TUD_CDC_EP_BUFSIZE),
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
|
uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
|
||||||
(void)index; // for multiple configurations
|
(void)index; // for multiple configurations
|
||||||
|
|
||||||
if (global_state.config_mode_active)
|
if (global_state.config_mode_active)
|
||||||
return desc_configuration_config;
|
return desc_configuration_config;
|
||||||
else
|
else
|
||||||
return desc_configuration;
|
return desc_configuration;
|
||||||
}
|
}
|
||||||
|
|
30
src/utils.c
30
src/utils.c
|
@ -37,7 +37,7 @@ bool verify_checksum(const uart_packet_t *packet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t crc32_iter(uint32_t crc, const uint8_t byte) {
|
uint32_t crc32_iter(uint32_t crc, const uint8_t byte) {
|
||||||
return crc32_lookup_table[(byte ^ crc) & 0xff] ^ (crc >> 8);
|
return crc32_lookup_table[(byte ^ crc) & 0xff] ^ (crc >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO - use DMA sniffer's built-in CRC32 */
|
/* TODO - use DMA sniffer's built-in CRC32 */
|
||||||
|
@ -69,12 +69,12 @@ void write_flash_page(uint32_t target_addr, uint8_t *buffer) {
|
||||||
/* Start of sector == first 256-byte page in a 4096 byte block */
|
/* Start of sector == first 256-byte page in a 4096 byte block */
|
||||||
bool is_sector_start = (target_addr & 0xf00) == 0;
|
bool is_sector_start = (target_addr & 0xf00) == 0;
|
||||||
|
|
||||||
uint32_t ints = save_and_disable_interrupts();
|
uint32_t ints = save_and_disable_interrupts();
|
||||||
if (is_sector_start)
|
if (is_sector_start)
|
||||||
flash_range_erase(target_addr, FLASH_SECTOR_SIZE);
|
flash_range_erase(target_addr, FLASH_SECTOR_SIZE);
|
||||||
|
|
||||||
flash_range_program(target_addr, buffer, FLASH_PAGE_SIZE);
|
flash_range_program(target_addr, buffer, FLASH_PAGE_SIZE);
|
||||||
restore_interrupts(ints);
|
restore_interrupts(ints);
|
||||||
}
|
}
|
||||||
|
|
||||||
void load_config(device_t *state) {
|
void load_config(device_t *state) {
|
||||||
|
@ -82,8 +82,8 @@ void load_config(device_t *state) {
|
||||||
config_t *running_config = &state->config;
|
config_t *running_config = &state->config;
|
||||||
|
|
||||||
/* Load the flash config first, including the checksum */
|
/* Load the flash config first, including the checksum */
|
||||||
memcpy(running_config, config, sizeof(config_t));
|
memcpy(running_config, config, sizeof(config_t));
|
||||||
|
|
||||||
/* Calculate and update checksum, size without checksum */
|
/* Calculate and update checksum, size without checksum */
|
||||||
uint8_t checksum = calc_crc32((uint8_t *)running_config, sizeof(config_t) - sizeof(uint32_t));
|
uint8_t checksum = calc_crc32((uint8_t *)running_config, sizeof(config_t) - sizeof(uint32_t));
|
||||||
|
|
||||||
|
@ -110,9 +110,9 @@ void save_config(device_t *state) {
|
||||||
|
|
||||||
/* Copy the config to buffer and pad the rest with zeros */
|
/* Copy the config to buffer and pad the rest with zeros */
|
||||||
memcpy(state->page_buffer, raw_config, sizeof(config_t));
|
memcpy(state->page_buffer, raw_config, sizeof(config_t));
|
||||||
memset(state->page_buffer + sizeof(config_t), 0, FLASH_PAGE_SIZE - sizeof(config_t));
|
memset(state->page_buffer + sizeof(config_t), 0, FLASH_PAGE_SIZE - sizeof(config_t));
|
||||||
|
|
||||||
/* Write the new config to flash */
|
/* Write the new config to flash */
|
||||||
write_flash_page((uint32_t)ADDR_CONFIG - XIP_BASE, state->page_buffer);
|
write_flash_page((uint32_t)ADDR_CONFIG - XIP_BASE, state->page_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,10 +120,10 @@ void reset_config_timer(device_t *state) {
|
||||||
/* Once this is reached, we leave the config mode */
|
/* Once this is reached, we leave the config mode */
|
||||||
state->config_mode_timer = time_us_64() + CONFIG_MODE_TIMEOUT;
|
state->config_mode_timer = time_us_64() + CONFIG_MODE_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _configure_flash_cs(enum gpio_override gpo, uint pin_index) {
|
void _configure_flash_cs(enum gpio_override gpo, uint pin_index) {
|
||||||
hw_write_masked(&ioqspi_hw->io[pin_index].ctrl,
|
hw_write_masked(&ioqspi_hw->io[pin_index].ctrl,
|
||||||
gpo << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
|
gpo << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
|
||||||
IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
|
IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,11 +152,11 @@ void request_byte(device_t *state, uint32_t address) {
|
||||||
};
|
};
|
||||||
state->fw.byte_done = false;
|
state->fw.byte_done = false;
|
||||||
|
|
||||||
queue_try_add(&global_state.uart_tx_queue, &packet);
|
queue_try_add(&global_state.uart_tx_queue, &packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reboot(void) {
|
void reboot(void) {
|
||||||
*((volatile uint32_t*)(PPB_BASE + 0x0ED0C)) = 0x5FA0004;
|
*((volatile uint32_t*)(PPB_BASE + 0x0ED0C)) = 0x5FA0004;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_start_of_packet(device_t *state) {
|
bool is_start_of_packet(device_t *state) {
|
||||||
|
@ -204,8 +204,8 @@ bool validate_packet(uart_packet_t *packet) {
|
||||||
uint8_t packet_type = packet->type;
|
uint8_t packet_type = packet->type;
|
||||||
|
|
||||||
/* Proxied packets are encapsulated in the data field, but same rules apply */
|
/* Proxied packets are encapsulated in the data field, but same rules apply */
|
||||||
if (packet->type == PROXY_PACKET_MSG)
|
if (packet->type == PROXY_PACKET_MSG)
|
||||||
packet_type = packet->data[0];
|
packet_type = packet->data[0];
|
||||||
|
|
||||||
for (int i = 0; i < ARRAY_SIZE(ALLOWED_PACKETS); i++) {
|
for (int i = 0; i < ARRAY_SIZE(ALLOWED_PACKETS); i++) {
|
||||||
if (ALLOWED_PACKETS[i] == packet_type)
|
if (ALLOWED_PACKETS[i] == packet_type)
|
||||||
|
|
Loading…
Reference in New Issue