Allow TABS in config
[clinton/Smoothieware.git] / src / libs / Network / uip / Network.cpp
CommitLineData
d4ee6ee2
JM
1#pragma GCC diagnostic ignored "-Wstrict-aliasing"
2#pragma GCC diagnostic ignored "-Wcast-qual"
3#pragma GCC diagnostic ignored "-Wcast-align"
4
5#include "CommandQueue.h"
6
7#include "Kernel.h"
61134a65
JM
8#include "Config.h"
9#include "SlowTicker.h"
d4ee6ee2
JM
10
11#include "Network.h"
12#include "PublicDataRequest.h"
13#include "PlayerPublicAccess.h"
14#include "net_util.h"
15#include "uip_arp.h"
16#include "clock-arch.h"
17
18#include "uip.h"
19#include "telnetd.h"
20#include "webserver.h"
21#include "dhcpc.h"
22#include "sftpd.h"
23
24
25#include <mri.h>
26
27#define BUF ((struct uip_eth_hdr *)&uip_buf[0])
28
29extern "C" void uip_log(char *m)
30{
31 printf("uIP log message: %s\n", m);
32}
33
34static bool webserver_enabled, telnet_enabled, use_dhcp;
35static Network *theNetwork;
36static Sftpd *sftpd;
37static CommandQueue *command_q= CommandQueue::getInstance();
38
39Network* Network::instance;
40Network::Network()
41{
42 ethernet = new LPC17XX_Ethernet();
43 tickcnt= 0;
44 theNetwork= this;
45 sftpd= NULL;
46 instance= this;
47}
48
49Network::~Network()
50{
51 delete ethernet;
52}
53
54static uint32_t getSerialNumberHash()
55{
56#define IAP_LOCATION 0x1FFF1FF1
57 uint32_t command[1];
58 uint32_t result[5];
59 typedef void (*IAP)(uint32_t *, uint32_t *);
60 IAP iap = (IAP) IAP_LOCATION;
61
62 __disable_irq();
63
64 command[0] = 58;
65 iap(command, result);
66 __enable_irq();
67 return crc32((uint8_t *)&result[1], 4 * 4);
68}
69
70static bool parse_ip_str(const string &s, uint8_t *a, int len, char sep = '.')
71{
72 int p = 0;
73 const char *n;
74 for (int i = 0; i < len; i++) {
75 if (i < len - 1) {
76 size_t o = s.find(sep, p);
77 if (o == string::npos) return false;
78 n = s.substr(p, o - p).c_str();
79 p = o + 1;
80 } else {
81 n = s.substr(p).c_str();
82 }
83 a[i] = atoi(n);
84 }
85 return true;
86}
87
88void Network::on_module_loaded()
89{
90 if ( !THEKERNEL->config->value( network_checksum, network_enable_checksum )->by_default(false)->as_bool() ) {
91 // as not needed free up resource
92 delete this;
93 return;
94 }
95
96 webserver_enabled = THEKERNEL->config->value( network_checksum, network_webserver_checksum, network_enable_checksum )->by_default(false)->as_bool();
97 telnet_enabled = THEKERNEL->config->value( network_checksum, network_telnet_checksum, network_enable_checksum )->by_default(false)->as_bool();
98
99 string mac = THEKERNEL->config->value( network_checksum, network_mac_override_checksum )->by_default("")->as_string();
100 if (mac.size() == 17 ) { // parse mac address
101 if (!parse_ip_str(mac, mac_address, 6, ':')) {
102 printf("Invalid MAC address: %s\n", mac.c_str());
103 printf("Network not started due to errors in config");
104 return;
105 }
106
107 } else { // autogenerate
108 uint32_t h = getSerialNumberHash();
109 mac_address[0] = 0x00; // OUI
110 mac_address[1] = 0x1F; // OUI
111 mac_address[2] = 0x11; // OUI
112 mac_address[3] = 0x02; // Openmoko allocation for smoothie board
113 mac_address[4] = 0x04; // 04-14 03 bits -> chip id, 1 bits -> hashed serial
114 mac_address[5] = h & 0xFF; // 00-FF 8bits -> hashed serial
115 }
116
117 ethernet->set_mac(mac_address);
118
119 // get IP address, mask and gateway address here....
120 bool bad = false;
3fd8df03 121 string s = THEKERNEL->config->value( network_checksum, network_ip_address_checksum )->by_default("auto")->as_string();
d4ee6ee2
JM
122 if (s == "auto") {
123 use_dhcp = true;
124
125 } else {
126 use_dhcp = false;
127 if (!parse_ip_str(s, ipaddr, 4)) {
128 printf("Invalid IP address: %s\n", s.c_str());
129 bad = true;
130 }
131 s = THEKERNEL->config->value( network_checksum, network_ip_mask_checksum )->by_default("255.255.255.0")->as_string();
132 if (!parse_ip_str(s, ipmask, 4)) {
133 printf("Invalid IP Mask: %s\n", s.c_str());
134 bad = true;
135 }
136 s = THEKERNEL->config->value( network_checksum, network_ip_gateway_checksum )->by_default("192.168.3.1")->as_string();
137 if (!parse_ip_str(s, ipgw, 4)) {
138 printf("Invalid IP gateway: %s\n", s.c_str());
139 bad = true;
140 }
141
142 if (bad) {
143 printf("Network not started due to errors in config");
144 return;
145 }
146 }
147
148 THEKERNEL->add_module( ethernet );
149 THEKERNEL->slow_ticker->attach( 100, this, &Network::tick );
150
151 // Register for events
152 this->register_for_event(ON_IDLE);
153 this->register_for_event(ON_MAIN_LOOP);
154 this->register_for_event(ON_GET_PUBLIC_DATA);
155
156 this->init();
157}
158
159void Network::on_get_public_data(void* argument) {
160 PublicDataRequest* pdr = static_cast<PublicDataRequest*>(argument);
161
162 if(!pdr->starts_with(network_checksum)) return;
163
164 if(pdr->second_element_is(get_ip_checksum)) {
165 pdr->set_data_ptr(this->ipaddr);
166 pdr->set_taken();
167
168 }else if(pdr->second_element_is(get_ipconfig_checksum)) {
169 // NOTE caller must free the returned string when done
170 char buf[200];
171 int n1= snprintf(buf, sizeof(buf), "IP Addr: %d.%d.%d.%d\n", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
172 int n2= snprintf(&buf[n1], sizeof(buf)-n1, "IP GW: %d.%d.%d.%d\n", ipgw[0], ipgw[1], ipgw[2], ipgw[3]);
173 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]);
174 int n4= snprintf(&buf[n1+n2+n3], sizeof(buf)-n1-n2-n3, "MAC Address: %02X:%02X:%02X:%02X:%02X:%02X\n",
175 mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5]);
176 char *str = (char *)malloc(n1+n2+n3+n4+1);
177 memcpy(str, buf, n1+n2+n3+n4);
178 str[n1+n2+n3+n4]= '\0';
179 pdr->set_data_ptr(str);
180 pdr->set_taken();
181 }
182}
183
184uint32_t Network::tick(uint32_t dummy)
185{
186 do_tick();
187 tickcnt++;
188 return 0;
189}
190
191void Network::on_idle(void *argument)
192{
193 if (!ethernet->isUp()) return;
194
195 int len;
196 if (ethernet->_receive_frame(uip_buf, &len)) {
197 uip_len = len;
198 this->handlePacket();
199
200 } else {
201
202 if (timer_expired(&periodic_timer)) { /* no packet but periodic_timer time out (0.1s)*/
203 timer_reset(&periodic_timer);
204
205 for (int i = 0; i < UIP_CONNS; i++) {
206 uip_periodic(i);
207 /* If the above function invocation resulted in data that
208 should be sent out on the network, the global variable
209 uip_len is set to a value > 0. */
210 if (uip_len > 0) {
211 uip_arp_out();
212 tapdev_send(uip_buf, uip_len);
213 }
214 }
215
216#if UIP_CONF_UDP
217 for (int i = 0; i < UIP_UDP_CONNS; i++) {
218 uip_udp_periodic(i);
219 /* If the above function invocation resulted in data that
220 should be sent out on the network, the global variable
221 uip_len is set to a value > 0. */
222 if (uip_len > 0) {
223 uip_arp_out();
224 tapdev_send(uip_buf, uip_len);
225 }
226 }
227#endif
228 }
229/*
230 This didn't work actually made it worse,it should have worked though
231 else{
232 // TODO if the command queue is below a certain amount we should poll any stopped connections
233 if(command_q->size() < 4) {
234 for (struct uip_conn *connr = &uip_conns[0]; connr <= &uip_conns[UIP_CONNS - 1]; ++connr) {
235 if(uip_stopped(connr)){
236 // Force a poll of this
237 printf("Force poll of connection\n");
238 uip_poll_conn(connr);
239 }
240 }
241 }
242 }
243*/
244 /* Call the ARP timer function every 10 seconds. */
245 if (timer_expired(&arp_timer)) {
246 timer_reset(&arp_timer);
247 uip_arp_timer();
248 }
249 }
250}
251
252static void setup_servers()
253{
254 if (webserver_enabled) {
255 // Initialize the HTTP server, listen to port 80.
256 httpd_init();
257 printf("Webserver initialized\n");
258 }
259
260 if (telnet_enabled) {
261 // Initialize the telnet server
262 Telnetd::init();
263 printf("Telnetd initialized\n");
264 }
265
266 // sftpd service, which is lazily created on reciept of first packet
267 uip_listen(HTONS(115));
268}
269
270extern "C" void dhcpc_configured(const struct dhcpc_state *s)
271{
272 printf("Got IP address %d.%d.%d.%d\n",
273 uip_ipaddr1(&s->ipaddr), uip_ipaddr2(&s->ipaddr),
274 uip_ipaddr3(&s->ipaddr), uip_ipaddr4(&s->ipaddr));
275 printf("Got netmask %d.%d.%d.%d\n",
276 uip_ipaddr1(&s->netmask), uip_ipaddr2(&s->netmask),
277 uip_ipaddr3(&s->netmask), uip_ipaddr4(&s->netmask));
278 printf("Got DNS server %d.%d.%d.%d\n",
279 uip_ipaddr1(&s->dnsaddr), uip_ipaddr2(&s->dnsaddr),
280 uip_ipaddr3(&s->dnsaddr), uip_ipaddr4(&s->dnsaddr));
281 printf("Got default router %d.%d.%d.%d\n",
282 uip_ipaddr1(&s->default_router), uip_ipaddr2(&s->default_router),
283 uip_ipaddr3(&s->default_router), uip_ipaddr4(&s->default_router));
284 printf("Lease expires in %ld seconds\n", ntohl(s->lease_time));
285
286 theNetwork->dhcpc_configured(s->ipaddr, s->netmask, s->default_router);
287}
288
289void Network::dhcpc_configured(uint32_t ipaddr, uint32_t ipmask, uint32_t ipgw)
290{
291 memcpy(this->ipaddr, &ipaddr, 4);
292 memcpy(this->ipmask, &ipmask, 4);
293 memcpy(this->ipgw, &ipgw, 4);
294
295 uip_sethostaddr((u16_t*)this->ipaddr);
296 uip_setnetmask((u16_t*)this->ipmask);
297 uip_setdraddr((u16_t*)this->ipgw);
298
299 setup_servers();
300}
301
302void Network::init(void)
303{
304 // two timers for tcp/ip
305 timer_set(&periodic_timer, CLOCK_SECOND / 2); /* 0.5s */
306 timer_set(&arp_timer, CLOCK_SECOND * 10); /* 10s */
307
308 // Initialize the uIP TCP/IP stack.
309 uip_init();
310
311 uip_setethaddr(mac_address);
312
313 if (!use_dhcp) { // manual setup of ip
314 uip_ipaddr_t tip; /* local IP address */
315 uip_ipaddr(tip, ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
316 uip_sethostaddr(tip); /* host IP address */
317 printf("IP Addr: %d.%d.%d.%d\n", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
318
319 uip_ipaddr(tip, ipgw[0], ipgw[1], ipgw[2], ipgw[3]);
320 uip_setdraddr(tip); /* router IP address */
321 printf("IP GW: %d.%d.%d.%d\n", ipgw[0], ipgw[1], ipgw[2], ipgw[3]);
322
323 uip_ipaddr(tip, ipmask[0], ipmask[1], ipmask[2], ipmask[3]);
324 uip_setnetmask(tip); /* mask */
325 printf("IP mask: %d.%d.%d.%d\n", ipmask[0], ipmask[1], ipmask[2], ipmask[3]);
326 setup_servers();
327
328 }else{
329 #if UIP_CONF_UDP
330 dhcpc_init(mac_address, sizeof(mac_address));
331 dhcpc_request();
332 printf("Getting IP address....\n");
333 #endif
334 }
335}
336
337void Network::on_main_loop(void *argument)
338{
339 // issue commands here if any available
340 while(command_q->pop()) {
341 // keep feeding them until empty
342 }
343}
344
345// select between webserver and telnetd server
346extern "C" void app_select_appcall(void)
347{
348 switch (uip_conn->lport) {
349 case HTONS(80):
350 if (webserver_enabled) httpd_appcall();
351 break;
352
353 case HTONS(23):
354 if (telnet_enabled) Telnetd::appcall();
355 break;
356
357 case HTONS(115):
358 if(sftpd == NULL) {
359 sftpd= new Sftpd();
360 sftpd->init();
361 printf("Created sftpd service\n");
362 }
363 sftpd->appcall();
364 break;
365
366 default:
367 printf("unknown app for port: %d\n", uip_conn->lport);
368
369 }
370}
371
372void Network::tapdev_send(void *pPacket, unsigned int size)
373{
374 memcpy(ethernet->request_packet_buffer(), pPacket, size);
375 ethernet->write_packet((uint8_t *) pPacket, size);
376}
377
378// define this to split full frames into two to illicit an ack from the endpoint
379#define SPLIT_OUTPUT
380
381#ifdef SPLIT_OUTPUT
382extern "C" void uip_split_output(void);
383extern "C" void tcpip_output()
384{
385 theNetwork->tapdev_send(uip_buf, uip_len);
386}
387void network_device_send()
388{
389 uip_split_output();
390 //tcpip_output();
391}
392#else
393void network_device_send()
394{
395 tapdev_send(uip_buf, uip_len);
396}
397#endif
398
399void Network::handlePacket(void)
400{
401 if (uip_len > 0) { /* received packet */
402 //printf("handlePacket: %d\n", uip_len);
403
404 if (BUF->type == htons(UIP_ETHTYPE_IP)) { /* IP packet */
405 uip_arp_ipin();
406 uip_input();
407 /* If the above function invocation resulted in data that
408 should be sent out on the network, the global variable
409 uip_len is set to a value > 0. */
410
411 if (uip_len > 0) {
412 uip_arp_out();
413 network_device_send();
414 }
415
416 } else if (BUF->type == htons(UIP_ETHTYPE_ARP)) { /*ARP packet */
417 uip_arp_arpin();
418 /* If the above function invocation resulted in data that
419 should be sent out on the network, the global variable
420 uip_len is set to a value > 0. */
421 if (uip_len > 0) {
422 tapdev_send(uip_buf, uip_len); /* ARP ack*/
423 }
424
425 } else {
426 printf("Unknown ethernet packet type %04X\n", htons(BUF->type));
427 uip_len = 0;
428 }
429 }
430}