@@ -31,6 +31,12 @@ this appears as either a RNDIS or CDC-ECM USB virtual network adapter; the OS pi
3131RNDIS should be valid on Linux and Windows hosts, and CDC-ECM should be valid on Linux and macOS hosts
3232
3333The MCU appears to the host as IP address 192.168.7.1, and provides a DHCP server, DNS server, and web server.
34+
35+ Link State Control:
36+ - Press the user button to toggle the network link state (UP/DOWN)
37+ - This simulates "ethernet cable unplugged/plugged" events
38+ - The host OS will see the network interface as disconnected/connected accordingly
39+ - Use this to test network error handling and recovery in host applications
3440*/
3541/*
3642Some smartphones *may* work with this implementation as well, but likely have limited (broken) drivers,
@@ -63,9 +69,6 @@ try changing the first byte of tud_network_mac_address[] below from 0x02 to 0x00
6369/* lwip context */
6470static struct netif netif_data ;
6571
66- /* shared between tud_network_recv_cb() and service_traffic() */
67- static struct pbuf * received_frame ;
68-
6972/* this is used by this code, ./class/net/net_driver.c, and usb_descriptors.c */
7073/* ideally speaking, this should be generated from the hardware's unique ID (if available) */
7174/* it is suggested that the first byte is 0x02 to indicate a link-local address */
@@ -137,6 +140,12 @@ static err_t netif_init_cb(struct netif *netif) {
137140 return ERR_OK ;
138141}
139142
143+ /* notifies the USB host about the link state change. */
144+ static void usbnet_netif_link_callback (struct netif * netif ) {
145+ bool link_up = netif_is_link_up (netif );
146+ tud_network_link_state (BOARD_TUD_RHPORT , link_up );
147+ }
148+
140149static void init_lwip (void ) {
141150 struct netif * netif = & netif_data ;
142151
@@ -147,11 +156,19 @@ static void init_lwip(void) {
147156 memcpy (netif -> hwaddr , tud_network_mac_address , sizeof (tud_network_mac_address ));
148157 netif -> hwaddr [5 ] ^= 0x01 ;
149158
150- netif = netif_add (netif , & ipaddr , & netmask , & gateway , NULL , netif_init_cb , ip_input );
159+ netif = netif_add (netif , & ipaddr , & netmask , & gateway , NULL , netif_init_cb , ethernet_input );
151160#if LWIP_IPV6
152161 netif_create_ip6_linklocal_address (netif , 1 );
153162#endif
154163 netif_set_default (netif );
164+
165+ #if LWIP_NETIF_LINK_CALLBACK
166+ // Set the link callback to notify USB host about link state changes
167+ netif_set_link_callback (netif , usbnet_netif_link_callback );
168+ netif_set_link_up (netif );
169+ #else
170+ tud_network_link_state (BOARD_TUD_RHPORT , true);
171+ #endif
155172}
156173
157174/* handle any DNS requests from dns-server */
@@ -164,20 +181,29 @@ bool dns_query_proc(const char *name, ip4_addr_t *addr) {
164181}
165182
166183bool tud_network_recv_cb (const uint8_t * src , uint16_t size ) {
167- /* this shouldn't happen, but if we get another packet before
168- parsing the previous, we must signal our inability to accept it */
169- if (received_frame ) return false;
184+ struct netif * netif = & netif_data ;
170185
171186 if (size ) {
172187 struct pbuf * p = pbuf_alloc (PBUF_RAW , size , PBUF_POOL );
173188
174- if (p ) {
175- /* pbuf_alloc() has already initialized struct; all we need to do is copy the data */
176- memcpy (p -> payload , src , size );
189+ if (p == NULL ) {
190+ printf ("ERROR: Failed to allocate pbuf of size %d\n" , size );
191+ return false;
192+ }
193+
194+ /* Copy buf to pbuf */
195+ pbuf_take (p , src , size );
177196
178- /* store away the pointer for service_traffic() to later handle */
179- received_frame = p ;
197+ // Surrender ownership of our pbuf unless there was an error
198+ // Only call pbuf_free if not Ok else it will panic with "pbuf_free: p->ref > 0"
199+ // or steal it from whatever took ownership of it with undefined consequences.
200+ // See: https://savannah.nongnu.org/patch/index.php?10121
201+ if (netif -> input (p , netif ) != ERR_OK ) {
202+ printf ("ERROR: netif input failed\n" );
203+ pbuf_free (p );
180204 }
205+ // Signal tinyusb that the current frame has been processed.
206+ tud_network_recv_renew ();
181207 }
182208
183209 return true;
@@ -191,29 +217,26 @@ uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) {
191217 return pbuf_copy_partial (p , dst , p -> tot_len , 0 );
192218}
193219
194- static void service_traffic (void ) {
195- /* handle any packet received by tud_network_recv_cb() */
196- if (received_frame ) {
197- // Surrender ownership of our pbuf unless there was an error
198- // Only call pbuf_free if not Ok else it will panic with "pbuf_free: p->ref > 0"
199- // or steal it from whatever took ownership of it with undefined consequences.
200- // See: https://savannah.nongnu.org/patch/index.php?10121
201- if (ethernet_input (received_frame , & netif_data )!= ERR_OK ) {
202- pbuf_free (received_frame );
220+ static void handle_link_state_switch (void ) {
221+ /* Check for button press to toggle link state */
222+ static bool last_link_state = true;
223+ static bool last_button_state = false;
224+ bool current_button_state = board_button_read ();
225+
226+ if (current_button_state && !last_button_state ) {
227+ /* Button pressed - toggle link state */
228+ last_link_state = !last_link_state ;
229+ if (last_link_state ) {
230+ printf ("Link state: UP\n" );
231+ netif_set_link_up (& netif_data );
232+ } else {
233+ printf ("Link state: DOWN\n" );
234+ netif_set_link_down (& netif_data );
203235 }
204- received_frame = NULL ;
205- tud_network_recv_renew ();
236+ /* LWIP callback will notify USB host about the change */
206237 }
238+ last_button_state = current_button_state ;
207239
208- sys_check_timeouts ();
209- }
210-
211- void tud_network_init_cb (void ) {
212- /* if the network is re-initializing and we have a leftover packet, we must do a cleanup */
213- if (received_frame ) {
214- pbuf_free (received_frame );
215- received_frame = NULL ;
216- }
217240}
218241
219242int main (void ) {
@@ -243,15 +266,23 @@ int main(void) {
243266 lwiperf_start_tcp_server_default (NULL , NULL );
244267#endif
245268
269+ #if CFG_TUD_NCM
270+ printf ("USB NCM network interface initialized\n" );
271+ #elif CFG_TUD_ECM_RNDIS
272+ printf ("USB RNDIS/ECM network interface initialized\n" );
273+ #endif
274+
246275 while (1 ) {
247276 tud_task ();
248- service_traffic ();
277+ sys_check_timeouts (); // service lwip
278+ handle_link_state_switch ();
249279 }
250280
251281 return 0 ;
252282}
253283
254284/* lwip has provision for using a mutex, when applicable */
285+ /* This implementation is for single-threaded use only */
255286sys_prot_t sys_arch_protect (void ) {
256287 return 0 ;
257288}
0 commit comments