#include "bsp/board_api.h" #include "tusb.h" #include "dhserver.h" #include "dnserver.h" #include "lwip/apps/httpd.h" #include "lwip/init.h" #include "lwip/timeouts.h" #include "websocket.h" #include "usb_device.h" #include "hyperx_elite2.h" #include "usb_server.h" // ip address of the USB server and dhcp address(es) it will give out static const ip4_addr_t usb_ip = INIT_IP4(192, 168, 226, 1); static const ip4_addr_t usb_netmask = INIT_IP4(255, 255, 255, 0); static const ip4_addr_t usb_gateway = INIT_IP4(0, 0, 0, 0); static dhcp_entry_t dhcp_clients[] = { { {0}, INIT_IP4(192, 168, 226, 2), 4*3600 }, }; static const dhcp_config_t dhcp_config = { .router = INIT_IP4(0,0,0,0), .port = 67, .dns = usb_ip, "usb", TU_ARRAY_SIZE(dhcp_clients), dhcp_clients }; static struct netif netif_data; static err_t netif_init_cb(struct netif *netif); static err_t ip4_output_fn(struct netif *netif, struct pbuf *p, const ip4_addr_t *addr); static err_t linkoutput_fn(struct netif *netif, struct pbuf *p); static void usb_server_netif_link_cb(struct netif *netif); static bool dns_request(const char *name, ip4_addr_t *addr); // called to initialize the USB network and HTTP server void usb_server_init(void) { struct netif *netif = &netif_data; lwip_init(); // use 02 followed by last 10 digits from board serial as MAC address uint8_t board_serial[16]; size_t count = board_get_unique_id(board_serial, sizeof(board_serial)); netif->hwaddr_len = 6; memcpy(netif->hwaddr, &board_serial[count-6], 6); netif->hwaddr[0]=0x02; // lwip virtual MAC address msut differ from the host MAC - toggle last bit netif->hwaddr[5] ^= 0x01; netif = netif_add(netif, &usb_ip, &usb_netmask, &usb_gateway, NULL, netif_init_cb, ethernet_input); netif_set_default(netif); #if LWIP_NETIF_LINK_CALLBACK netif_set_link_callback(netif, usb_server_netif_link_cb); netif_set_link_up(netif); #else //tud_network_link_state(BOARD_TUD_RHPORT, true); // unsupported in current version - add when Pico SDK updates TinyUSB version #endif while (!netif_is_up(&netif_data)); while (dhserv_init(&dhcp_config) != ERR_OK); while (dnserv_init(IP_ADDR_ANY, 53, dns_request) != ERR_OK); httpd_init(); // start the websocket server ws_server_init(); ws_set_open_handler(ws_open_handler); ws_set_receive_handler(ws_receive_handler); } // callback when data is received on USB network // return true if the packet buffer was accepted bool tud_network_recv_cb(const uint8_t *src, uint16_t size) { struct netif *netif = &netif_data; if (size) { struct pbuf *p = pbuf_alloc(PBUF_RAW, size, PBUF_POOL); if (p == NULL) { printf("ERROR: Failed to allocate pbuf of size %d\n", size); return false; } /* Copy buf to pbuf */ pbuf_take(p, src, size); // Surrender ownership of our pbuf unless there was an error // Only call pbuf_free if not Ok else it will panic with "pbuf_free: p->ref > 0" // or steal it from whatever took ownership of it with undefined consequences. // See: https://savannah.nongnu.org/patch/index.php?10121 if (netif->input(p, netif) != ERR_OK) { printf("ERROR: netif input failed\n"); pbuf_free(p); } // Signal tinyusb that the current frame has been processed. tud_network_recv_renew(); } return true; } // callback when network interface is initialized // save the network configuration static err_t netif_init_cb(struct netif *netif) { LWIP_ASSERT("netif != NULL", (netif != NULL)); netif->mtu = CFG_TUD_NET_MTU; netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP; netif->state = NULL; netif->name[0] = 'E'; netif->name[1] = 'X'; netif->linkoutput = linkoutput_fn; netif->output = ip4_output_fn; return ERR_OK; } // callback for sending data over USB network interface // copy from network stack packet pointer to dst uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) { struct pbuf *p = (struct pbuf *) ref; (void) arg; /* unused for this example */ return pbuf_copy_partial(p, dst, p->tot_len, 0); } static err_t ip4_output_fn(struct netif *netif, struct pbuf *p, const ip4_addr_t *addr) { return etharp_output(netif, p, addr); } static err_t linkoutput_fn(struct netif *netif, struct pbuf *p) { (void) netif; for (;;) { // if TinyUSB isn't ready, we must signal back to lwip that there is nothing we can do if (!tud_ready()) return ERR_USE; // if the network driver can accept another packet, we make it happen if (tud_network_can_xmit(p->tot_len)) { tud_network_xmit(p, 0 /* unused for this example */); return ERR_OK; } // transfer execution to TinyUSB in the hopes that it will finish transmitting the prior packet tud_task(); } } // notify USB host about link state changes static void usb_server_netif_link_cb(struct netif *netif) { bool link_up = netif_is_link_up(netif); //tud_network_link_state(BOARD_TUD_RHPORT, link_up); // unsupported in current version - add when Pico SDK updates TinyUSB version } // handle DNS requests and serve on designed domain static bool dns_request(const char *name, ip4_addr_t *addr) { if (0 == strcmp(name, "alloyelite2.usb")) { *addr = usb_ip; return true; } return false; } // handler called when websocket connection is opened const void ws_open_handler(struct ws_state * wss) { (void) wss; // nothing to do } // handler for data received on websocket connection const void ws_receive_handler(uint8_t *data, uint16_t len) { if (strncmp(data, "S,", 2) == 0) { // set color command parse_colors(&data[2], len-2); } else if ( strncmp(data, "G,", 2) == 0) { // get color comand get_color(&data[2], len-2); } else if ( strncmp(data, "F,", 2) == 0) { // save to flash memory save_rgb_config(); } else if ( strncmp(data, "L,", 2) == 0) { // load from flash memory load_rgb_config(); } } // Pico specific routines needed by lwip auto_init_mutex(lwip_mutex); static int lwip_mutex_count = 0; sys_prot_t sys_arch_protect(void) { uint32_t owner; if (!mutex_try_enter(&lwip_mutex, &owner)) { if (owner != get_core_num()) { // Wait until other core releases mutex mutex_enter_blocking(&lwip_mutex); } } lwip_mutex_count++; return 0; } void sys_arch_unprotect(sys_prot_t pval) { (void)pval; if (lwip_mutex_count) { lwip_mutex_count--; if (!lwip_mutex_count) { mutex_exit(&lwip_mutex); } } } uint32_t sys_now(void) { return to_ms_since_boot( get_absolute_time() ); }