Merge pull request #3135 from andrewleech/ncm-link-state-control

Add USB NCM link state control support
This commit is contained in:
HiFiPhile
2025-06-09 14:25:25 +02:00
committed by GitHub
6 changed files with 149 additions and 45 deletions

View File

@@ -81,6 +81,7 @@ typedef struct {
static netd_interface_t _netd_itf;
CFG_TUD_MEM_SECTION static netd_epbuf_t _netd_epbuf;
static bool can_xmit;
static bool ecm_link_is_up = true; // Store link state for ECM mode
void tud_network_recv_renew(void) {
usbd_edpt_xfer(0, _netd_itf.ep_out, _netd_epbuf.rx, NETD_PACKET_SIZE);
@@ -95,7 +96,11 @@ void netd_report(uint8_t *buf, uint16_t len) {
const uint8_t rhport = 0;
len = tu_min16(len, sizeof(ecm_notify_t));
TU_VERIFY(usbd_edpt_claim(rhport, _netd_itf.ep_notif), );
if (!usbd_edpt_claim(rhport, _netd_itf.ep_notif)) {
TU_LOG1("ECM: Failed to claim notification endpoint\n");
return;
}
memcpy(_netd_epbuf.notify, buf, len);
usbd_edpt_xfer(rhport, _netd_itf.ep_notif, _netd_epbuf.notify, len);
}
@@ -181,8 +186,6 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
// Open endpoint pair for RNDIS
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in), 0);
tud_network_init_cb();
// we are ready to transmit a packet
can_xmit = true;
@@ -196,11 +199,11 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
}
static void ecm_report(bool nc) {
const ecm_notify_t ecm_notify_nc = {
ecm_notify_t ecm_notify_nc = {
.header = {
.bmRequestType = 0xA1,
.bRequest = 0, /* NETWORK_CONNECTION aka NetworkConnection */
.wValue = 1, /* Connected */
.wValue = ecm_link_is_up ? 1 : 0, /* Use current link state */
.wLength = 0,
},
};
@@ -259,7 +262,6 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
// TODO should be merge with RNDIS's after endpoint opened
// Also should have opposite callback for application to disable network !!
tud_network_init_cb();
can_xmit = true; // we are ready to transmit a packet
tud_network_recv_renew(); // prepare for incoming packets
}
@@ -286,7 +288,10 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
/* the only required CDC-ECM Management Element Request is SetEthernetPacketFilter */
if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest) {
tud_control_xfer(rhport, request, NULL, 0);
ecm_report(true);
// Only send connection notification if link is up
if (ecm_link_is_up) {
ecm_report(true);
}
}
} else {
if (request->bmRequestType_bit.direction == TUSB_DIR_IN) {
@@ -363,9 +368,8 @@ bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
}
if (_netd_itf.ecm_mode && (ep_addr == _netd_itf.ep_notif)) {
if (sizeof(tusb_control_request_t) == xferred_bytes) {
ecm_report(false);
}
// Notification transfer complete - endpoint is now free
// Don't automatically send speed change notification after link state changes
}
return true;
@@ -398,4 +402,31 @@ void tud_network_xmit(void *ref, uint16_t arg) {
do_in_xfer(_netd_epbuf.tx, len);
}
// Set the network link state (up/down) and notify the host
void tud_network_link_state(uint8_t rhport, bool is_up) {
(void)rhport;
if (_netd_itf.ecm_mode) {
ecm_link_is_up = is_up;
// For ECM mode, send network connection notification only
// Don't trigger speed change notification for link state changes
ecm_notify_t notify = {
.header = {
.bmRequestType = 0xA1,
.bRequest = 0, /* NETWORK_CONNECTION */
.wValue = is_up ? 1 : 0, /* 0 = disconnected, 1 = connected */
.wLength = 0,
},
};
notify.header.wIndex = _netd_itf.itf_num;
netd_report((uint8_t *)&notify, sizeof(notify.header));
} else {
// For RNDIS mode, we would need to implement RNDIS status indication
// This is more complex and requires RNDIS_INDICATE_STATUS_MSG
// For now, RNDIS doesn't support dynamic link state changes
(void)is_up;
}
}
#endif

View File

@@ -110,6 +110,7 @@ typedef struct {
NOTIFICATION_DONE
} notification_xmit_state; // state of notification transmission
bool notification_xmit_is_running; // notification is currently transmitted
bool link_is_up; // current link state
// misc
bool tud_network_recv_renew_active; // tud_network_recv_renew() is active (avoid recursive invocations)
@@ -218,7 +219,7 @@ static void notification_xmit(uint8_t rhport, bool force_next) {
.direction = TUSB_DIR_IN
},
.bRequest = CDC_NOTIF_NETWORK_CONNECTION,
.wValue = 1 /* Connected */,
.wValue = ncm_interface.link_is_up ? 1 : 0, /* Dynamic link state */
.wIndex = ncm_interface.itf_num,
.wLength = 0,
},
@@ -232,6 +233,7 @@ static void notification_xmit(uint8_t rhport, bool force_next) {
ncm_interface.notification_xmit_is_running = true;
} else {
TU_LOG_DRV(" NOTIFICATION_FINISHED\n");
ncm_interface.notification_xmit_is_running = false;
}
} // notification_xmit
@@ -755,6 +757,32 @@ static void tud_network_recv_renew_r(uint8_t rhport) {
tud_network_recv_renew();
} // tud_network_recv_renew
/**
* Set the link state and send notification to host
*/
void tud_network_link_state(uint8_t rhport, bool is_up) {
TU_LOG_DRV("tud_network_link_state(%d, %d)\n", rhport, is_up);
if (ncm_interface.link_is_up == is_up) {
// No change in link state
return;
}
ncm_interface.link_is_up = is_up;
// Only send notification if we have an active data interface
if (ncm_interface.itf_data_alt != 1) {
TU_LOG_DRV(" link state notification skipped (interface not active)\n");
return;
}
// Reset notification state to send link state update
ncm_interface.notification_xmit_state = NOTIFICATION_CONNECTED;
// Trigger notification transmission
notification_xmit(rhport, false);
}
//-----------------------------------------------------------------------------
//
// all the netd_*() stuff (interface TinyUSB -> driver)
@@ -774,6 +802,12 @@ void netd_init(void) {
for (int i = 0; i < RECV_NTB_N; ++i) {
ncm_interface.recv_free_ntb[i] = &ncm_epbuf.recv[i].ntb;
}
// Default link state - can be configured via CFG_TUD_NCM_DEFAULT_LINK_UP
#ifdef CFG_TUD_NCM_DEFAULT_LINK_UP
ncm_interface.link_is_up = CFG_TUD_NCM_DEFAULT_LINK_UP;
#else
ncm_interface.link_is_up = true; // Default to link up if not set.
#endif
} // netd_init
/**

View File

@@ -87,6 +87,11 @@ void tud_network_init_cb(void);
// TODO removed later since it is not part of tinyusb stack
extern uint8_t tud_network_mac_address[6];
//------------- NCM -------------//
// Set the network link state (up/down) and notify the host
void tud_network_link_state(uint8_t rhport, bool is_up);
//--------------------------------------------------------------------+
// INTERNAL USBD-CLASS DRIVER API
//--------------------------------------------------------------------+