| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  | /* 
 | 
					
						
							|  |  |  |  * The MIT License (MIT) | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2020 Peter Lawrence | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * influenced by lrndis https://github.com/fetisov/lrndis
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
					
						
							|  |  |  |  * of this software and associated documentation files (the "Software"), to deal | 
					
						
							|  |  |  |  * in the Software without restriction, including without limitation the rights | 
					
						
							|  |  |  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
					
						
							|  |  |  |  * copies of the Software, and to permit persons to whom the Software is | 
					
						
							|  |  |  |  * furnished to do so, subject to the following conditions: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The above copyright notice and this permission notice shall be included in | 
					
						
							|  |  |  |  * all copies or substantial portions of the Software. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
					
						
							|  |  |  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
					
						
							|  |  |  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
					
						
							|  |  |  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
					
						
							|  |  |  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
					
						
							|  |  |  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
					
						
							|  |  |  |  * THE SOFTWARE. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2020-04-14 21:10:43 -05:00
										 |  |  | this appears as either a RNDIS or CDC-ECM USB virtual network adapter; the OS picks its preference | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-14 21:10:43 -05:00
										 |  |  | RNDIS should be valid on Linux and Windows hosts, and CDC-ECM should be valid on Linux and macOS hosts | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | The MCU appears to the host as IP address 192.168.7.1, and provides a DHCP server, DNS server, and web server. | 
					
						
							|  |  |  | */ | 
					
						
							| 
									
										
										
										
											2020-10-10 16:49:45 -05:00
										 |  |  | /*
 | 
					
						
							|  |  |  | Some smartphones *may* work with this implementation as well, but likely have limited (broken) drivers, | 
					
						
							|  |  |  | and likely their manufacturer has not tested such functionality.  Some code workarounds could be tried: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The smartphone may only have an ECM driver, but refuse to automatically pick ECM (unlike the OSes above); | 
					
						
							|  |  |  | try modifying ./examples/devices/net_lwip_webserver/usb_descriptors.c so that CONFIG_ID_ECM is default. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The smartphone may be artificially picky about which Ethernet MAC address to recognize; if this happens,  | 
					
						
							|  |  |  | try changing the first byte of tud_network_mac_address[] below from 0x02 to 0x00 (clearing bit 1). | 
					
						
							|  |  |  | */ | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "bsp/board.h"
 | 
					
						
							|  |  |  | #include "tusb.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "dhserver.h"
 | 
					
						
							|  |  |  | #include "dnserver.h"
 | 
					
						
							|  |  |  | #include "lwip/init.h"
 | 
					
						
							| 
									
										
										
										
											2020-04-12 16:13:52 -05:00
										 |  |  | #include "lwip/timeouts.h"
 | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  | #include "httpd.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* lwip context */ | 
					
						
							|  |  |  | static struct netif netif_data; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-03 10:31:46 -06:00
										 |  |  | /* shared between tud_network_recv_cb() and service_traffic() */ | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  | static struct pbuf *received_frame; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* this is used by this code, ./class/net/net_driver.c, and usb_descriptors.c */ | 
					
						
							|  |  |  | /* ideally speaking, this should be generated from the hardware's unique ID (if available) */ | 
					
						
							| 
									
										
										
										
											2020-10-10 16:49:45 -05:00
										 |  |  | /* it is suggested that the first byte is 0x02 to indicate a link-local address */ | 
					
						
							| 
									
										
										
										
											2020-03-27 20:30:57 -05:00
										 |  |  | const uint8_t tud_network_mac_address[6] = {0x02,0x02,0x84,0x6A,0x96,0x00}; | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* network parameters of this MCU */ | 
					
						
							|  |  |  | static const ip_addr_t ipaddr  = IPADDR4_INIT_BYTES(192, 168, 7, 1); | 
					
						
							|  |  |  | static const ip_addr_t netmask = IPADDR4_INIT_BYTES(255, 255, 255, 0); | 
					
						
							|  |  |  | static const ip_addr_t gateway = IPADDR4_INIT_BYTES(0, 0, 0, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* database IP addresses that can be offered to the host; this must be in RAM to store assigned MAC addresses */ | 
					
						
							|  |  |  | static dhcp_entry_t entries[] = | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-10-14 20:26:40 -05:00
										 |  |  |     /* mac ip address                          lease time */ | 
					
						
							|  |  |  |     { {0}, IPADDR4_INIT_BYTES(192, 168, 7, 2), 24 * 60 * 60 }, | 
					
						
							|  |  |  |     { {0}, IPADDR4_INIT_BYTES(192, 168, 7, 3), 24 * 60 * 60 }, | 
					
						
							|  |  |  |     { {0}, IPADDR4_INIT_BYTES(192, 168, 7, 4), 24 * 60 * 60 }, | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const dhcp_config_t dhcp_config = | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-10-14 20:26:40 -05:00
										 |  |  |     .router = IPADDR4_INIT_BYTES(0, 0, 0, 0),  /* router address (if any) */ | 
					
						
							|  |  |  |     .port = 67,                                /* listen port */ | 
					
						
							|  |  |  |     .dns = IPADDR4_INIT_BYTES(192, 168, 7, 1), /* dns server (if any) */ | 
					
						
							|  |  |  |     "usb",                                     /* dns suffix */ | 
					
						
							|  |  |  |     TU_ARRAY_SIZE(entries),                    /* num entry */ | 
					
						
							|  |  |  |     entries                                    /* entries */ | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  | }; | 
					
						
							|  |  |  | 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 */ | 
					
						
							| 
									
										
										
										
											2020-03-03 10:31:46 -06:00
										 |  |  |     if (tud_network_can_xmit()) | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-10-24 20:27:57 -05:00
										 |  |  |       tud_network_xmit(p, 0 /* unused for this example */); | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  |       return ERR_OK; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* transfer execution to TinyUSB in the hopes that it will finish transmitting the prior packet */ | 
					
						
							|  |  |  |     tud_task(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static err_t output_fn(struct netif *netif, struct pbuf *p, const ip_addr_t *addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return etharp_output(netif, p, addr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 = output_fn; | 
					
						
							|  |  |  |   return ERR_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void init_lwip(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   struct netif *netif = &netif_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   lwip_init(); | 
					
						
							| 
									
										
										
										
											2020-03-27 20:30:57 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /* the lwip virtual MAC address must be different from the host's; to ensure this, we toggle the LSbit */ | 
					
						
							| 
									
										
										
										
											2020-03-03 10:31:46 -06:00
										 |  |  |   netif->hwaddr_len = sizeof(tud_network_mac_address); | 
					
						
							|  |  |  |   memcpy(netif->hwaddr, tud_network_mac_address, sizeof(tud_network_mac_address)); | 
					
						
							| 
									
										
										
										
											2020-03-27 20:30:57 -05:00
										 |  |  |   netif->hwaddr[5] ^= 0x01; | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |   netif = netif_add(netif, &ipaddr, &netmask, &gateway, NULL, netif_init_cb, ip_input); | 
					
						
							|  |  |  |   netif_set_default(netif); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* handle any DNS requests from dns-server */ | 
					
						
							|  |  |  | bool dns_query_proc(const char *name, ip_addr_t *addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (0 == strcmp(name, "tiny.usb")) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     *addr = ipaddr; | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-24 20:27:57 -05:00
										 |  |  | bool tud_network_recv_cb(const uint8_t *src, uint16_t size) | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  | { | 
					
						
							|  |  |  |   /* this shouldn't happen, but if we get another packet before 
 | 
					
						
							|  |  |  |   parsing the previous, we must signal our inability to accept it */ | 
					
						
							|  |  |  |   if (received_frame) return false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-24 20:27:57 -05:00
										 |  |  |   if (size) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     struct pbuf *p = pbuf_alloc(PBUF_RAW, size, PBUF_POOL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (p) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       /* pbuf_alloc() has already initialized struct; all we need to do is copy the data */ | 
					
						
							|  |  |  |       memcpy(p->payload, src, size); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       /* store away the pointer for service_traffic() to later handle */ | 
					
						
							|  |  |  |       received_frame = p; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-24 20:27:57 -05:00
										 |  |  | uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   struct pbuf *p = (struct pbuf *)ref; | 
					
						
							|  |  |  |   struct pbuf *q; | 
					
						
							|  |  |  |   uint16_t len = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   (void)arg; /* unused for this example */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* traverse the "pbuf chain"; see ./lwip/src/core/pbuf.c for more info */ | 
					
						
							|  |  |  |   for(q = p; q != NULL; q = q->next) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     memcpy(dst, (char *)q->payload, q->len); | 
					
						
							|  |  |  |     dst += q->len; | 
					
						
							|  |  |  |     len += q->len; | 
					
						
							|  |  |  |     if (q->len == q->tot_len) break; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return len; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  | static void service_traffic(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-03-03 10:31:46 -06:00
										 |  |  |   /* handle any packet received by tud_network_recv_cb() */ | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  |   if (received_frame) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     ethernet_input(received_frame, &netif_data); | 
					
						
							|  |  |  |     pbuf_free(received_frame); | 
					
						
							|  |  |  |     received_frame = NULL; | 
					
						
							| 
									
										
										
										
											2020-03-03 10:31:46 -06:00
										 |  |  |     tud_network_recv_renew(); | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-04-12 16:13:52 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |   sys_check_timeouts(); | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-03 10:31:46 -06:00
										 |  |  | void tud_network_init_cb(void) | 
					
						
							| 
									
										
										
										
											2020-03-02 21:15:01 -06:00
										 |  |  | { | 
					
						
							|  |  |  |   /* if the network is re-initializing and we have a leftover packet, we must do a cleanup */ | 
					
						
							|  |  |  |   if (received_frame) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     pbuf_free(received_frame); | 
					
						
							|  |  |  |     received_frame = NULL; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int main(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   /* initialize TinyUSB */ | 
					
						
							|  |  |  |   board_init(); | 
					
						
							|  |  |  |   tusb_init(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* initialize lwip, dhcp-server, dns-server, and http */ | 
					
						
							|  |  |  |   init_lwip(); | 
					
						
							|  |  |  |   while (!netif_is_up(&netif_data)); | 
					
						
							|  |  |  |   while (dhserv_init(&dhcp_config) != ERR_OK); | 
					
						
							|  |  |  |   while (dnserv_init(&ipaddr, 53, dns_query_proc) != ERR_OK); | 
					
						
							|  |  |  |   httpd_init(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while (1) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     tud_task(); | 
					
						
							|  |  |  |     service_traffic(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* lwip has provision for using a mutex, when applicable */ | 
					
						
							|  |  |  | sys_prot_t sys_arch_protect(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | void sys_arch_unprotect(sys_prot_t pval) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   (void)pval; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* lwip needs a millisecond time source, and the TinyUSB board support code has one available */ | 
					
						
							|  |  |  | uint32_t sys_now(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return board_millis(); | 
					
						
							|  |  |  | } |