Skip to content

Kmbox net#3

Merged
ramseymcgrath merged 8 commits intomainfrom
kmbox-net
Mar 10, 2026
Merged

Kmbox net#3
ramseymcgrath merged 8 commits intomainfrom
kmbox-net

Conversation

@ramseymcgrath
Copy link
Copy Markdown
Contributor

No description provided.

Copilot AI review requested due to automatic review settings March 9, 2026 03:34
@ramseymcgrath
Copy link
Copy Markdown
Contributor Author

@copilot please update the build workflow to build with tft, without tft, and with net

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 9, 2026

@ramseymcgrath I've opened a new pull request, #4, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a NET-enabled mode to the Teensy 4.1 USB proxy to accept KMBox Net commands over Ethernet (UDP), while also improving USB control-request passthrough behavior and expanding on-device stats/telemetry for the new mode.

Changes:

  • Introduces a bare-metal ENET + minimal UDP/IP/ARP stack plus a kmnet protocol handler gated by NET_ENABLED.
  • Updates USB host control transfers to support caller-provided timeouts and adds an async “fire-and-forget” control OUT path used by deferred passthrough in the USB device stack.
  • Extends TFT stats for network state and adjusts smoothing/humanization configuration/behavior.

Reviewed changes

Copilot reviewed 21 out of 22 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
tools/usb_passthrough_test.c Adds a libusb-based host-side passthrough test utility.
tools/usb_passthrough_test Adds a prebuilt binary artifact (should not be committed).
src/usb_host.h Extends control transfer API with timeout + async control OUT API.
src/usb_host.c Implements timeout-based control transfers and async control OUT + busy check.
src/usb_device.c Adds EP0 RX buffer + deferred control OUT passthrough forwarding.
src/udp.h Declares a minimal UDP stack API for NET mode.
src/udp.c Implements minimal ARP/IPv4/UDP/ICMP handling on top of ENET.
src/enet.h Declares bare-metal ENET + DP83825I PHY driver API.
src/enet.c Implements polled ENET RX/TX and PHY init/link polling.
src/kmnet.h Declares KMBox Net UDP protocol handler API.
src/kmnet.c Implements KMBox Net command parsing/dispatch via UDP.
src/main.c Wires NET mode init/polling and publishes network stats to TFT.
src/tft_display.h Adds network-related fields to the TFT stats struct (NET mode).
src/tft_display.c Renders NET stats (IP/port/link/UUID/RX/TX) and adjusts layout.
src/kmbox.h Exposes public injection APIs for alternate transports (UART/NET).
src/kmbox.c Adds NET-mode init/poll behavior and implements injection helpers.
src/smooth_config.h Retunes smoothing constants and adds tremor/dither configuration.
src/smooth.c Implements persistent micro-tremor + dithered rounding in humanize mode.
src/desc_capture.c Updates all host control transfer calls for new timeout parameter.
core/startup.c Expands non-cacheable DMA MPU overlay size when NET is enabled.
build.sh Adds interactive build selection for UART vs Ethernet (NET) mode.
Makefile Adds NET build flag, sources, and hot-path optimization list updates.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// Version must be 4, IHL must be 5 (no options)
if ((ip[0] >> 4) != 4) return;
uint8_t ihl = (ip[0] & 0x0F) * 4;
if (ihl < 20) return;
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code comments say “IHL must be 5 (no options)”, and handle_icmp() assumes a fixed 20-byte IP header (ip_hdr + 20), but handle_ipv4() currently allows ihl > 20. Either enforce ihl == 20 (drop packets with options) or update downstream parsing to use ihl everywhere.

Suggested change
if (ihl < 20) return;
if (ihl != 20) return;

Copilot uses AI. Check for mistakes.
Comment on lines +180 to +184
int32_t state = rd32le_s(data + 16); // 1=press, 0=release
// Get current buttons, set/clear this bit
uint8_t buttons = state ? button_bit : 0;
kmbox_inject_mouse(0, 0, buttons, 0, false);
send_response(hdr, NULL, 0);
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Button handling doesn’t preserve existing button state: buttons = state ? button_bit : 0 clears all other buttons and releases all buttons on release. Track a current button-mask in kmnet (set/clear only button_bit) and pass the full mask into kmbox_inject_mouse() so simultaneous button presses work.

Copilot uses AI. Check for mistakes.
Comment on lines +287 to +304
void kmnet_init(void)
{
// Initialize Ethernet hardware
if (!enet_init()) {
// PHY not detected — this is fatal in NET mode
// Main will show error on TFT
return;
}

device_uuid = generate_uuid();
client_connected = false;
rx_count = 0;
tx_count = 0;
link_status = false;
last_link_check = 0;

udp_init(KMNET_IP, KMNET_NETMASK, KMNET_GATEWAY);
}
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kmnet_init() returns early if enet_init() fails, but main() will still call kmnet_poll() in NET mode. Since ENET/UDP state isn’t initialized in this failure path, kmnet_poll() can end up calling into udp_poll()/enet_rx() with uninitialized rings/pointers. Add a kmnet_ready flag (or make kmnet_init() return bool) and gate kmnet_poll()/stats on successful init.

Copilot uses AI. Check for mistakes.
Comment on lines +410 to +416

if (is_in) {
// Device-to-host: forward to real device, return response.
// Wait for any in-flight fire-and-forget to finish first.
while (usb_host_control_async_busy()) ;
int ret = usb_host_control_transfer(cap_desc->dev_addr,
cap_desc->ep0_maxpkt, setup, ep0_rx_buf, 200);
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IN passthrough can overflow ep0_rx_buf: usb_host_control_transfer() may copy up to setup->wLength bytes into the provided buffer, but ep0_rx_buf is only 512 bytes and setup->wLength is not capped. Consider copying *setup to a local, clamp wLength to sizeof(ep0_rx_buf), and use that for the host transfer + device response.

Suggested change
if (is_in) {
// Device-to-host: forward to real device, return response.
// Wait for any in-flight fire-and-forget to finish first.
while (usb_host_control_async_busy()) ;
int ret = usb_host_control_transfer(cap_desc->dev_addr,
cap_desc->ep0_maxpkt, setup, ep0_rx_buf, 200);
usb_setup_t local_setup = *setup;
if (is_in) {
// Device-to-host: forward to real device, return response.
// Clamp requested length to our local EP0 RX buffer size.
if (local_setup.wLength > sizeof(ep0_rx_buf)) {
local_setup.wLength = (uint16_t)sizeof(ep0_rx_buf);
}
// Wait for any in-flight fire-and-forget to finish first.
while (usb_host_control_async_busy()) ;
int ret = usb_host_control_transfer(cap_desc->dev_addr,
cap_desc->ep0_maxpkt, &local_setup, ep0_rx_buf, 200);

Copilot uses AI. Check for mistakes.
ep0_stall();
return;
}
ep0_tx_data(ep0_rx_buf, (uint16_t)ret);
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For IN passthrough with wLength == 0, ep0_tx_data(..., 0) won’t prime EP0 RX for the STATUS OUT stage (the RX priming in ep0_tx_data() only happens when len > 0). Vendor/device IN requests with zero-length data could therefore hang. Handle the wLength == 0 case explicitly by priming EP0 RX for the status phase (or adjust ep0_tx_data() to optionally prime RX based on request direction).

Suggested change
ep0_tx_data(ep0_rx_buf, (uint16_t)ret);
if (wLength == 0) {
// IN request with no data phase: only STATUS OUT stage remains.
// Prime EP0 RX explicitly for the status stage instead of calling
// ep0_tx_data(..., 0), which does not prime RX when len == 0.
(void)ep0_rx_data(0);
} else {
ep0_tx_data(ep0_rx_buf, (uint16_t)ret);
}

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +28
# Pass NET=1 to enable Ethernet (KMBox Net UDP protocol, replaces UART commands)
NET ?= 0

DEFINES = -DARDUINO_TEENSY41 -D__IMXRT1062__ -DF_CPU=816000000 -DUART_ENABLED=$(UART) \
-DUART_BAUD=$(UART_BAUD) -DTFT_ENABLED=$(TFT) -DTFT_DRIVER=$(TFT_DRIVER) \
-DTOUCH_ENABLED=$(TOUCH) -DUART_AUTOBAUD=$(UART_AUTOBAUD)
-DTOUCH_ENABLED=$(TOUCH) -DUART_AUTOBAUD=$(UART_AUTOBAUD) \
-DNET_ENABLED=$(NET)
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enabling NET compiles and exposes the KMBox Net UDP protocol (src/enet.c, src/udp.c, src/kmnet.c), but that implementation currently has effectively no strong authentication: any host that knows the 32‑bit device_uuid can send KMBox Net commands. Because this UUID is derived from the MAC, displayed on the TFT, and reused as a static token, a network attacker who learns or guesses it can connect and then inject arbitrary keyboard/mouse events or trigger device reboots over UDP. Before enabling this mode by default, you should harden the KMBox Net protocol with real per-device or per-session secrets, bind commands to the original client IP/port, and add message authentication so only an authorized peer can control the device.

Copilot uses AI. Check for mistakes.
Copilot AI and others added 6 commits March 9, 2026 03:48
Co-authored-by: ramseymcgrath <2006510+ramseymcgrath@users.noreply.github.com>
Build matrix: with-tft, without-tft, with-net
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@ramseymcgrath ramseymcgrath merged commit 5798951 into main Mar 10, 2026
5 checks passed
@ramseymcgrath ramseymcgrath deleted the kmbox-net branch March 10, 2026 04:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants