Fork of Smoothie to port to mbed non-LPC targets.
Fork of Smoothie by
libs/Network/uip/Network.cpp
- Committer:
- Michael J. Spencer
- Date:
- 2014-02-28
- Revision:
- 2:1df0b61d3b5a
File content as of revision 2:1df0b61d3b5a:
#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; } } }