Fork of Smoothie to port to mbed non-LPC targets.

Dependencies:   mbed

Fork of Smoothie by Stéphane Cachat

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;
+        }
+    }
+}