Fork of Smoothie to port to mbed non-LPC targets.
Fork of Smoothie by
Diff: libs/Network/uip/Network.cpp
- Revision:
- 2:1df0b61d3b5a
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Network/uip/Network.cpp Fri Feb 28 18:52:52 2014 -0800 @@ -0,0 +1,428 @@ +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#pragma GCC diagnostic ignored "-Wcast-qual" +#pragma GCC diagnostic ignored "-Wcast-align" + +#include "CommandQueue.h" + +#include "Kernel.h" + +#include "Network.h" +#include "PublicDataRequest.h" +#include "PlayerPublicAccess.h" +#include "net_util.h" +#include "uip_arp.h" +#include "clock-arch.h" + +#include "uip.h" +#include "telnetd.h" +#include "webserver.h" +#include "dhcpc.h" +#include "sftpd.h" + + +#include <mri.h> + +#define BUF ((struct uip_eth_hdr *)&uip_buf[0]) + +extern "C" void uip_log(char *m) +{ + printf("uIP log message: %s\n", m); +} + +static bool webserver_enabled, telnet_enabled, use_dhcp; +static Network *theNetwork; +static Sftpd *sftpd; +static CommandQueue *command_q= CommandQueue::getInstance(); + +Network* Network::instance; +Network::Network() +{ + ethernet = new LPC17XX_Ethernet(); + tickcnt= 0; + theNetwork= this; + sftpd= NULL; + instance= this; +} + +Network::~Network() +{ + delete ethernet; +} + +static uint32_t getSerialNumberHash() +{ +#define IAP_LOCATION 0x1FFF1FF1 + uint32_t command[1]; + uint32_t result[5]; + typedef void (*IAP)(uint32_t *, uint32_t *); + IAP iap = (IAP) IAP_LOCATION; + + __disable_irq(); + + command[0] = 58; + iap(command, result); + __enable_irq(); + return crc32((uint8_t *)&result[1], 4 * 4); +} + +static bool parse_ip_str(const string &s, uint8_t *a, int len, char sep = '.') +{ + int p = 0; + const char *n; + for (int i = 0; i < len; i++) { + if (i < len - 1) { + size_t o = s.find(sep, p); + if (o == string::npos) return false; + n = s.substr(p, o - p).c_str(); + p = o + 1; + } else { + n = s.substr(p).c_str(); + } + a[i] = atoi(n); + } + return true; +} + +void Network::on_module_loaded() +{ + if ( !THEKERNEL->config->value( network_checksum, network_enable_checksum )->by_default(false)->as_bool() ) { + // as not needed free up resource + delete this; + return; + } + + webserver_enabled = THEKERNEL->config->value( network_checksum, network_webserver_checksum, network_enable_checksum )->by_default(false)->as_bool(); + telnet_enabled = THEKERNEL->config->value( network_checksum, network_telnet_checksum, network_enable_checksum )->by_default(false)->as_bool(); + + string mac = THEKERNEL->config->value( network_checksum, network_mac_override_checksum )->by_default("")->as_string(); + if (mac.size() == 17 ) { // parse mac address + if (!parse_ip_str(mac, mac_address, 6, ':')) { + printf("Invalid MAC address: %s\n", mac.c_str()); + printf("Network not started due to errors in config"); + return; + } + + } else { // autogenerate + uint32_t h = getSerialNumberHash(); + mac_address[0] = 0x00; // OUI + mac_address[1] = 0x1F; // OUI + mac_address[2] = 0x11; // OUI + mac_address[3] = 0x02; // Openmoko allocation for smoothie board + mac_address[4] = 0x04; // 04-14 03 bits -> chip id, 1 bits -> hashed serial + mac_address[5] = h & 0xFF; // 00-FF 8bits -> hashed serial + } + + ethernet->set_mac(mac_address); + + // get IP address, mask and gateway address here.... + bool bad = false; + string s = THEKERNEL->config->value( network_checksum, network_ip_address_checksum )->by_default("auto")->as_string(); + if (s == "auto") { + use_dhcp = true; + + } else { + use_dhcp = false; + if (!parse_ip_str(s, ipaddr, 4)) { + printf("Invalid IP address: %s\n", s.c_str()); + bad = true; + } + s = THEKERNEL->config->value( network_checksum, network_ip_mask_checksum )->by_default("255.255.255.0")->as_string(); + if (!parse_ip_str(s, ipmask, 4)) { + printf("Invalid IP Mask: %s\n", s.c_str()); + bad = true; + } + s = THEKERNEL->config->value( network_checksum, network_ip_gateway_checksum )->by_default("192.168.3.1")->as_string(); + if (!parse_ip_str(s, ipgw, 4)) { + printf("Invalid IP gateway: %s\n", s.c_str()); + bad = true; + } + + if (bad) { + printf("Network not started due to errors in config"); + return; + } + } + + THEKERNEL->add_module( ethernet ); + THEKERNEL->slow_ticker->attach( 100, this, &Network::tick ); + + // Register for events + this->register_for_event(ON_IDLE); + this->register_for_event(ON_MAIN_LOOP); + this->register_for_event(ON_GET_PUBLIC_DATA); + + this->init(); +} + +void Network::on_get_public_data(void* argument) { + PublicDataRequest* pdr = static_cast<PublicDataRequest*>(argument); + + if(!pdr->starts_with(network_checksum)) return; + + if(pdr->second_element_is(get_ip_checksum)) { + pdr->set_data_ptr(this->ipaddr); + pdr->set_taken(); + + }else if(pdr->second_element_is(get_ipconfig_checksum)) { + // NOTE caller must free the returned string when done + char buf[200]; + int n1= snprintf(buf, sizeof(buf), "IP Addr: %d.%d.%d.%d\n", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); + int n2= snprintf(&buf[n1], sizeof(buf)-n1, "IP GW: %d.%d.%d.%d\n", ipgw[0], ipgw[1], ipgw[2], ipgw[3]); + int n3= snprintf(&buf[n1+n2], sizeof(buf)-n1-n2, "IP mask: %d.%d.%d.%d\n", ipmask[0], ipmask[1], ipmask[2], ipmask[3]); + int n4= snprintf(&buf[n1+n2+n3], sizeof(buf)-n1-n2-n3, "MAC Address: %02X:%02X:%02X:%02X:%02X:%02X\n", + mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5]); + char *str = (char *)malloc(n1+n2+n3+n4+1); + memcpy(str, buf, n1+n2+n3+n4); + str[n1+n2+n3+n4]= '\0'; + pdr->set_data_ptr(str); + pdr->set_taken(); + } +} + +uint32_t Network::tick(uint32_t dummy) +{ + do_tick(); + tickcnt++; + return 0; +} + +void Network::on_idle(void *argument) +{ + if (!ethernet->isUp()) return; + + int len; + if (ethernet->_receive_frame(uip_buf, &len)) { + uip_len = len; + this->handlePacket(); + + } else { + + if (timer_expired(&periodic_timer)) { /* no packet but periodic_timer time out (0.1s)*/ + timer_reset(&periodic_timer); + + for (int i = 0; i < UIP_CONNS; i++) { + uip_periodic(i); + /* If the above function invocation resulted in data that + should be sent out on the network, the global variable + uip_len is set to a value > 0. */ + if (uip_len > 0) { + uip_arp_out(); + tapdev_send(uip_buf, uip_len); + } + } + +#if UIP_CONF_UDP + for (int i = 0; i < UIP_UDP_CONNS; i++) { + uip_udp_periodic(i); + /* If the above function invocation resulted in data that + should be sent out on the network, the global variable + uip_len is set to a value > 0. */ + if (uip_len > 0) { + uip_arp_out(); + tapdev_send(uip_buf, uip_len); + } + } +#endif + } +/* + This didn't work actually made it worse,it should have worked though + else{ + // TODO if the command queue is below a certain amount we should poll any stopped connections + if(command_q->size() < 4) { + for (struct uip_conn *connr = &uip_conns[0]; connr <= &uip_conns[UIP_CONNS - 1]; ++connr) { + if(uip_stopped(connr)){ + // Force a poll of this + printf("Force poll of connection\n"); + uip_poll_conn(connr); + } + } + } + } +*/ + /* Call the ARP timer function every 10 seconds. */ + if (timer_expired(&arp_timer)) { + timer_reset(&arp_timer); + uip_arp_timer(); + } + } +} + +static void setup_servers() +{ + if (webserver_enabled) { + // Initialize the HTTP server, listen to port 80. + httpd_init(); + printf("Webserver initialized\n"); + } + + if (telnet_enabled) { + // Initialize the telnet server + Telnetd::init(); + printf("Telnetd initialized\n"); + } + + // sftpd service, which is lazily created on reciept of first packet + uip_listen(HTONS(115)); +} + +extern "C" void dhcpc_configured(const struct dhcpc_state *s) +{ + printf("Got IP address %d.%d.%d.%d\n", + uip_ipaddr1(&s->ipaddr), uip_ipaddr2(&s->ipaddr), + uip_ipaddr3(&s->ipaddr), uip_ipaddr4(&s->ipaddr)); + printf("Got netmask %d.%d.%d.%d\n", + uip_ipaddr1(&s->netmask), uip_ipaddr2(&s->netmask), + uip_ipaddr3(&s->netmask), uip_ipaddr4(&s->netmask)); + printf("Got DNS server %d.%d.%d.%d\n", + uip_ipaddr1(&s->dnsaddr), uip_ipaddr2(&s->dnsaddr), + uip_ipaddr3(&s->dnsaddr), uip_ipaddr4(&s->dnsaddr)); + printf("Got default router %d.%d.%d.%d\n", + uip_ipaddr1(&s->default_router), uip_ipaddr2(&s->default_router), + uip_ipaddr3(&s->default_router), uip_ipaddr4(&s->default_router)); + printf("Lease expires in %ld seconds\n", ntohl(s->lease_time)); + + theNetwork->dhcpc_configured(s->ipaddr, s->netmask, s->default_router); +} + +void Network::dhcpc_configured(uint32_t ipaddr, uint32_t ipmask, uint32_t ipgw) +{ + memcpy(this->ipaddr, &ipaddr, 4); + memcpy(this->ipmask, &ipmask, 4); + memcpy(this->ipgw, &ipgw, 4); + + uip_sethostaddr((u16_t*)this->ipaddr); + uip_setnetmask((u16_t*)this->ipmask); + uip_setdraddr((u16_t*)this->ipgw); + + setup_servers(); +} + +void Network::init(void) +{ + // two timers for tcp/ip + timer_set(&periodic_timer, CLOCK_SECOND / 2); /* 0.5s */ + timer_set(&arp_timer, CLOCK_SECOND * 10); /* 10s */ + + // Initialize the uIP TCP/IP stack. + uip_init(); + + uip_setethaddr(mac_address); + + if (!use_dhcp) { // manual setup of ip + uip_ipaddr_t tip; /* local IP address */ + uip_ipaddr(tip, ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); + uip_sethostaddr(tip); /* host IP address */ + printf("IP Addr: %d.%d.%d.%d\n", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); + + uip_ipaddr(tip, ipgw[0], ipgw[1], ipgw[2], ipgw[3]); + uip_setdraddr(tip); /* router IP address */ + printf("IP GW: %d.%d.%d.%d\n", ipgw[0], ipgw[1], ipgw[2], ipgw[3]); + + uip_ipaddr(tip, ipmask[0], ipmask[1], ipmask[2], ipmask[3]); + uip_setnetmask(tip); /* mask */ + printf("IP mask: %d.%d.%d.%d\n", ipmask[0], ipmask[1], ipmask[2], ipmask[3]); + setup_servers(); + + }else{ + #if UIP_CONF_UDP + dhcpc_init(mac_address, sizeof(mac_address)); + dhcpc_request(); + printf("Getting IP address....\n"); + #endif + } +} + +void Network::on_main_loop(void *argument) +{ + // issue commands here if any available + while(command_q->pop()) { + // keep feeding them until empty + } +} + +// select between webserver and telnetd server +extern "C" void app_select_appcall(void) +{ + switch (uip_conn->lport) { + case HTONS(80): + if (webserver_enabled) httpd_appcall(); + break; + + case HTONS(23): + if (telnet_enabled) Telnetd::appcall(); + break; + + case HTONS(115): + if(sftpd == NULL) { + sftpd= new Sftpd(); + sftpd->init(); + printf("Created sftpd service\n"); + } + sftpd->appcall(); + break; + + default: + printf("unknown app for port: %d\n", uip_conn->lport); + + } +} + +void Network::tapdev_send(void *pPacket, unsigned int size) +{ + memcpy(ethernet->request_packet_buffer(), pPacket, size); + ethernet->write_packet((uint8_t *) pPacket, size); +} + +// define this to split full frames into two to illicit an ack from the endpoint +#define SPLIT_OUTPUT + +#ifdef SPLIT_OUTPUT +extern "C" void uip_split_output(void); +extern "C" void tcpip_output() +{ + theNetwork->tapdev_send(uip_buf, uip_len); +} +void network_device_send() +{ + uip_split_output(); + //tcpip_output(); +} +#else +void network_device_send() +{ + tapdev_send(uip_buf, uip_len); +} +#endif + +void Network::handlePacket(void) +{ + if (uip_len > 0) { /* received packet */ + //printf("handlePacket: %d\n", uip_len); + + if (BUF->type == htons(UIP_ETHTYPE_IP)) { /* IP packet */ + uip_arp_ipin(); + uip_input(); + /* If the above function invocation resulted in data that + should be sent out on the network, the global variable + uip_len is set to a value > 0. */ + + if (uip_len > 0) { + uip_arp_out(); + network_device_send(); + } + + } else if (BUF->type == htons(UIP_ETHTYPE_ARP)) { /*ARP packet */ + uip_arp_arpin(); + /* If the above function invocation resulted in data that + should be sent out on the network, the global variable + uip_len is set to a value > 0. */ + if (uip_len > 0) { + tapdev_send(uip_buf, uip_len); /* ARP ack*/ + } + + } else { + printf("Unknown ethernet packet type %04X\n", htons(BUF->type)); + uip_len = 0; + } + } +}