mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-10-15 11:19:56 +00:00
usb fix
This commit is contained in:
@@ -100,9 +100,16 @@ static const struct companion_link_cbs *s_link;
|
||||
K_MSGQ_DEFINE(usb_recv_queue, sizeof(struct frame), USB_RECV_QUEUE_DEPTH, 4);
|
||||
|
||||
/* A session starts on the first traffic after open (a binary frame or a CLI
|
||||
* line) and ends when the host drops DTR. */
|
||||
* line) and ends when the host drops DTR or goes away without one (VBUS loss,
|
||||
* bus reset). */
|
||||
static bool usb_session_active;
|
||||
|
||||
/* The session can carry frames: it is open and the bus is not suspended (host
|
||||
* asleep with the port open). Main hears each edge as a connect or a
|
||||
* disconnect, so a suspended session is not a client: nothing is queued for
|
||||
* it, and it does not hold up the last-client cleanup. */
|
||||
static bool usb_link_up;
|
||||
|
||||
/* CLI text line callback — fired when a complete line arrives in text mode. */
|
||||
static void (*s_cli_line_cb)(const char *line);
|
||||
static char usb_text_line[USB_TEXT_LINE_MAX];
|
||||
@@ -189,6 +196,31 @@ static void usb_uart_isr(const struct device *dev, void *user_data)
|
||||
}
|
||||
}
|
||||
|
||||
/* Recompute usb_link_up and tell main about an edge. Arduino shows "connected"
|
||||
* for serial transports too, so a text session raises the edges as well. */
|
||||
static void usb_link_update(void)
|
||||
{
|
||||
bool up = usb_session_active;
|
||||
|
||||
#if COMPANION_HAS_DTR
|
||||
up = up && !zephcore_usbd_is_suspended();
|
||||
#endif
|
||||
if (up == usb_link_up) {
|
||||
return;
|
||||
}
|
||||
usb_link_up = up;
|
||||
if (!s_link) {
|
||||
return;
|
||||
}
|
||||
if (up) {
|
||||
if (s_link->on_connected) {
|
||||
s_link->on_connected();
|
||||
}
|
||||
} else if (s_link->on_disconnected) {
|
||||
s_link->on_disconnected();
|
||||
}
|
||||
}
|
||||
|
||||
/* Start a session on its first inbound traffic — a binary frame or a complete
|
||||
* CLI line. The official client opens with CMD_DEVICE_QUERY (0x16), not
|
||||
* CMD_APP_START, so any first traffic starts it. Other transports (BLE, WiFi)
|
||||
@@ -202,10 +234,7 @@ static void usb_session_begin(uint8_t log_tag, bool is_text)
|
||||
usb_session_is_text = is_text;
|
||||
LOG_INF("usb_rx: first traffic 0x%02x, session started (%s)", log_tag,
|
||||
is_text ? "text" : "binary");
|
||||
/* Arduino shows "connected" for serial transports too */
|
||||
if (s_link && s_link->on_connected) {
|
||||
s_link->on_connected();
|
||||
}
|
||||
usb_link_update();
|
||||
}
|
||||
|
||||
/* USB RX work - parses V3 frames from ring buffer */
|
||||
@@ -347,7 +376,8 @@ static void usb_rx_work_fn(struct k_work *work)
|
||||
|
||||
#if COMPANION_HAS_DTR
|
||||
/* DTR-transition callback from the shared ZephyrUSBCDC module.
|
||||
* On drop: host closed the port → reset parser, hand control back to BLE.
|
||||
* On drop (the host closed the port, or went away: VBUS loss, bus reset):
|
||||
* reset the parser and end the session.
|
||||
* CDC-only — a plain-UART backend has no DTR and never registers this. */
|
||||
static void on_dtr_change(bool dtr_active)
|
||||
{
|
||||
@@ -355,7 +385,6 @@ static void on_dtr_change(bool dtr_active)
|
||||
return;
|
||||
}
|
||||
LOG_INF("usb_dtr: DTR dropped, USB disconnected");
|
||||
bool was_active = usb_session_active;
|
||||
|
||||
usb_session_active = false;
|
||||
ring_buf_reset(&usb_ring_buf);
|
||||
@@ -375,8 +404,19 @@ static void on_dtr_change(bool dtr_active)
|
||||
|
||||
/* The session is over: main runs its per-session cleanup (contact dump,
|
||||
* sync, sign buffer) once no other transport is still connected. */
|
||||
if (was_active && s_link && s_link->on_disconnected) {
|
||||
s_link->on_disconnected();
|
||||
usb_link_update();
|
||||
}
|
||||
|
||||
/* Bus suspend, resume or reset, from the shared ZephyrUSBCDC module. */
|
||||
static void on_bus_change(void)
|
||||
{
|
||||
bool was_up = usb_link_up;
|
||||
|
||||
usb_link_update();
|
||||
if (usb_link_up && !was_up) {
|
||||
/* Resumed. The class does not restart TX by itself: frames queued
|
||||
* before the suspend wait for this kick. */
|
||||
uart_irq_tx_enable(usb_dev);
|
||||
}
|
||||
}
|
||||
#endif /* COMPANION_HAS_DTR */
|
||||
@@ -455,7 +495,7 @@ size_t zephcore_usb_companion_recv(uint8_t *dest)
|
||||
|
||||
bool zephcore_usb_companion_is_connected(void)
|
||||
{
|
||||
return usb_session_active && !usb_session_is_text;
|
||||
return usb_link_up && !usb_session_is_text;
|
||||
}
|
||||
|
||||
/* Only a connected session is ever busy: MultiSerialInterface asks every
|
||||
@@ -516,6 +556,7 @@ void zephcore_usb_companion_init(const struct companion_link_cbs *link)
|
||||
* shared usbd_msg_callback — no polling work needed. Plain-UART
|
||||
* backends have no DTR; their session reset is protocol-driven. */
|
||||
zephcore_usbd_set_dtr_cb(on_dtr_change);
|
||||
zephcore_usbd_set_bus_cb(on_bus_change);
|
||||
#endif
|
||||
} else {
|
||||
LOG_WRN("Companion UART not ready");
|
||||
|
||||
@@ -21,8 +21,9 @@ extern "C" {
|
||||
|
||||
/**
|
||||
* Initialize the transport: ring buffers, UART ISR, DTR tracking.
|
||||
* @param link frame waiting / TX drained / session start / session end
|
||||
* (DTR drop). Raised from the ISR and sysworkq: post events only.
|
||||
* @param link frame waiting / TX drained / session start (also a bus resume)
|
||||
* / session end (DTR drop, VBUS loss, bus reset; also a bus
|
||||
* suspend). Raised from the ISR and sysworkq: post events only.
|
||||
*/
|
||||
void zephcore_usb_companion_init(const struct companion_link_cbs *link);
|
||||
|
||||
@@ -36,7 +37,8 @@ size_t zephcore_usb_companion_write_frame(const uint8_t *src, size_t len);
|
||||
/** Take the next received binary frame into dest (MAX_FRAME_SIZE). Returns its length, 0 if none. */
|
||||
size_t zephcore_usb_companion_recv(uint8_t *dest);
|
||||
|
||||
/** A binary companion session is open (a text-CLI session is not a client). */
|
||||
/** A binary companion session is open and the bus is not suspended (a
|
||||
* text-CLI session is not a client). */
|
||||
bool zephcore_usb_companion_is_connected(void);
|
||||
|
||||
/** The TX ring cannot take another full-size frame. */
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
* SET_LINE_CODING requests reach us as events (no DTR polling)
|
||||
* - Emits a k_event when DTR transitions high — the boot path waits on it
|
||||
* instead of a fixed sleep
|
||||
* - Fires a user callback on every DTR transition (companion: reset RX +
|
||||
* flip active_iface on drop)
|
||||
* - Fires a user callback on every DTR transition (companion: end the
|
||||
* session on drop), and reports VBUS loss and a bus reset as a drop
|
||||
* - Fires a user callback on bus suspend, resume and reset
|
||||
* - Handles the Arduino-style 1200-baud touch → reboot-to-bootloader flow
|
||||
* for nRF52 boards
|
||||
*/
|
||||
@@ -71,6 +72,7 @@ static K_EVENT_DEFINE(usb_cdc_events);
|
||||
static bool s_dtr_active;
|
||||
static bool s_initialized;
|
||||
static zephcore_usbd_cdc_dtr_cb_t s_dtr_cb;
|
||||
static zephcore_usbd_cdc_bus_cb_t s_bus_cb;
|
||||
|
||||
/* ZephyrBoard for bootloader-magic write before reset. The class is stateless
|
||||
* (only static methods over GPREGRET + sys_reboot), so a local instance here
|
||||
@@ -86,6 +88,17 @@ static void enter_bootloader(void)
|
||||
CODE_UNREACHABLE;
|
||||
}
|
||||
|
||||
/* The host went away without a DTR=0: report it as a DTR drop. */
|
||||
static void host_gone(void)
|
||||
{
|
||||
if (s_dtr_active) {
|
||||
s_dtr_active = false;
|
||||
if (s_dtr_cb) {
|
||||
s_dtr_cb(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== USBD message callback ===== */
|
||||
static void usbd_msg_callback(struct usbd_context *const ctx,
|
||||
const struct usbd_msg *msg)
|
||||
@@ -142,11 +155,24 @@ static void usbd_msg_callback(struct usbd_context *const ctx,
|
||||
* connection until reboot). Treat VBUS loss as a DTR drop so the
|
||||
* interface is handed back to BLE. */
|
||||
LOG_INF("CDC ACM: VBUS removed (device unplug)");
|
||||
if (s_dtr_active) {
|
||||
s_dtr_active = false;
|
||||
if (s_dtr_cb) {
|
||||
s_dtr_cb(false);
|
||||
}
|
||||
host_gone();
|
||||
} else if (msg->type == USBD_MSG_RESET) {
|
||||
/* Bus reset. At enumeration no port is open and this does nothing.
|
||||
* Later it means the host is enumerating us again (a hub upstream
|
||||
* was reset, or the host lost the device): the port it had open is
|
||||
* gone, and no DTR=0 says so, since the class keeps the line state
|
||||
* of the old session. VBUS can stay up throughout, so treat it as
|
||||
* a DTR drop too. A reset also ends a suspend, with no RESUME. */
|
||||
LOG_INF("CDC ACM: bus reset");
|
||||
host_gone();
|
||||
if (s_bus_cb) {
|
||||
s_bus_cb();
|
||||
}
|
||||
} else if (msg->type == USBD_MSG_SUSPEND || msg->type == USBD_MSG_RESUME) {
|
||||
LOG_INF("CDC ACM: bus %s",
|
||||
msg->type == USBD_MSG_SUSPEND ? "suspended" : "resumed");
|
||||
if (s_bus_cb) {
|
||||
s_bus_cb();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -268,6 +294,16 @@ extern "C" void zephcore_usbd_set_dtr_cb(zephcore_usbd_cdc_dtr_cb_t cb)
|
||||
s_dtr_cb = cb;
|
||||
}
|
||||
|
||||
extern "C" bool zephcore_usbd_is_suspended(void)
|
||||
{
|
||||
return s_initialized && usbd_is_suspended(&zephcore_usbd);
|
||||
}
|
||||
|
||||
extern "C" void zephcore_usbd_set_bus_cb(zephcore_usbd_cdc_bus_cb_t cb)
|
||||
{
|
||||
s_bus_cb = cb;
|
||||
}
|
||||
|
||||
extern "C" void zephcore_usbd_detach(void)
|
||||
{
|
||||
if (!s_initialized) {
|
||||
|
||||
@@ -20,9 +20,14 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/* Per-transition DTR callback. Fires on every host CONTROL_LINE_STATE change,
|
||||
* value = current DTR level. NULL is allowed (cb cleared). */
|
||||
* value = current DTR level, and with false when the host goes away without
|
||||
* one (VBUS loss, bus reset). NULL is allowed (cb cleared). */
|
||||
typedef void (*zephcore_usbd_cdc_dtr_cb_t)(bool dtr_active);
|
||||
|
||||
/* Bus state callback: the bus was suspended, resumed or reset. Read the state
|
||||
* with zephcore_usbd_is_suspended(). NULL is allowed (cb cleared). */
|
||||
typedef void (*zephcore_usbd_cdc_bus_cb_t)(void);
|
||||
|
||||
/* Initialize USB device + CDC ACM class and enumerate.
|
||||
* Idempotent; subsequent calls are no-ops.
|
||||
* Returns 0 on success or a negative errno. */
|
||||
@@ -37,9 +42,16 @@ int zephcore_usbd_wait_dtr(uint32_t timeout_ms);
|
||||
bool zephcore_usbd_is_dtr_active(void);
|
||||
|
||||
/* Register a callback fired on every DTR transition.
|
||||
* Companion uses this to reset its RX state and flip active_iface on drop. */
|
||||
* Companion uses this to end its session on a drop. */
|
||||
void zephcore_usbd_set_dtr_cb(zephcore_usbd_cdc_dtr_cb_t cb);
|
||||
|
||||
/* The host has suspended the bus (asleep with the port open): nothing we send
|
||||
* leaves the device until it resumes. The USB stack's own state. */
|
||||
bool zephcore_usbd_is_suspended(void);
|
||||
|
||||
/* Register a callback fired on a bus suspend, resume or reset. */
|
||||
void zephcore_usbd_set_bus_cb(zephcore_usbd_cdc_bus_cb_t cb);
|
||||
|
||||
/* Detach from the USB bus (drop the D+ pull-up) so the host sees an unplug.
|
||||
* Call before a reset: a soft reset leaves the PHY attached on some SoCs, and
|
||||
* the host then keeps a stale endpoint open across the reboot. No-op if the
|
||||
|
||||
Reference in New Issue
Block a user