MeterLogger
netio.c
Go to the documentation of this file.
1 /**
2  * @file
3  * MetIO Server
4  *
5  */
6 
7 /*
8  * Redistribution and use in source and binary forms, with or without modification,
9  * are permitted provided that the following conditions are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright notice,
12  * this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright notice,
14  * this list of conditions and the following disclaimer in the documentation
15  * and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  * derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
20  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
22  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
24  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
28  * OF SUCH DAMAGE.
29  *
30  * This file is part of the lwIP TCP/IP stack.
31  *
32  */
33 #include "lwip/opt.h"
34 
35 #if LWIP_TCP
36 #include "lwip/tcp.h"
37 
38 #ifdef MEMLEAK_DEBUG
39 static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
40 #endif
41 
42 /*
43  * This implements a netio server.
44  * The client sends a command word (4 bytes) then a data length word (4 bytes).
45  * If the command is "receive", the server is to consume "data length" bytes into
46  * a circular buffer until the first byte is non-zero, then it is to consume
47  * another command/data pair.
48  * If the command is "send", the server is to send "data length" bytes from a circular
49  * buffer with the first byte being zero, until "some time" (6 seconds in the
50  * current netio126.zip download) has passed and then send one final buffer with
51  * the first byte being non-zero. Then it is to consume another command/data pair.
52  */
53 
54 /* See http://www.nwlab.net/art/netio/netio.html to get the netio tool */
55 
56 /* implementation options */
57 #define NETIO_BUF_SIZE (4 * 1024)
58 #define NETIO_USE_STATIC_BUF 0
59 
60 /* NetIO server state definition */
61 #define NETIO_STATE_WAIT_FOR_CMD 0
62 #define NETIO_STATE_RECV_DATA 1
63 #define NETIO_STATE_SEND_DATA 2
64 #define NETIO_STATE_SEND_DATA_LAST 3
65 #define NETIO_STATE_DONE 4
66 
67 struct netio_state {
68  u32_t state;
69  u32_t cmd;
70  u32_t data_len;
71  u32_t cntr;
72  u8_t * buf_ptr;
73  u32_t buf_pos;
74  u32_t first_byte;
75  u32_t time_stamp;
76 };
77 
78 /* NetIO command protocol definition */
79 #define NETIO_CMD_QUIT 0
80 #define NETIO_CMD_C2S 1
81 #define NETIO_CMD_S2C 2
82 #define NETIO_CMD_RES 3
83 
84 static err_t netio_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
85 
86 static void ICACHE_FLASH_ATTR
87 netio_close(void *arg, struct tcp_pcb *pcb)
88 {
89  err_t err;
90 
91  struct netio_state *ns = arg;
92  ns->state = NETIO_STATE_DONE;
93  tcp_recv(pcb, NULL);
94  err = tcp_close(pcb);
95 
96  if (err != ERR_OK) {
97  /* closing failed, try again later */
98  tcp_recv(pcb, netio_recv);
99  } else {
100  /* closing succeeded */
101 #if NETIO_USE_STATIC_BUF != 1
102  if(ns->buf_ptr != NULL){
103  mem_free(ns->buf_ptr);
104  }
105 #endif
106  tcp_arg(pcb, NULL);
107  tcp_poll(pcb, NULL, 0);
108  tcp_sent(pcb, NULL);
109  if (arg != NULL) {
110  mem_free(arg);
111  }
112  }
113 }
114 
116 netio_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
117 {
118  struct netio_state *ns = arg;
119  u8_t * data_ptr;
120  u32_t data_cntr;
121  struct pbuf *q = p;
122  u16_t len;
123 
124  if (p != NULL) {
125  tcp_recved(pcb, p->tot_len);
126  }
127 
128  if (err == ERR_OK && q != NULL) {
129 
130  while (q != NULL) {
131  data_cntr = q->len;
132  data_ptr = q->payload;
133  while (data_cntr--) {
134  if (ns->state == NETIO_STATE_DONE){
135  netio_close(ns, pcb);
136  break;
137  } else if (ns->state == NETIO_STATE_WAIT_FOR_CMD) {
138  if (ns->cntr < 4) {
139  /* build up the CMD field */
140  ns->cmd <<= 8;
141  ns->cmd |= *data_ptr++;
142  ns->cntr++;
143  } else if (ns->cntr < 8) {
144  /* build up the DATA field */
145  ns->data_len <<= 8;
146  ns->data_len |= *data_ptr++;
147  ns->cntr++;
148 
149  if (ns->cntr == 8) {
150  /* now we have full command and data words */
151  ns->cntr = 0;
152  ns->buf_pos = 0;
153  ns->buf_ptr[0] = 0;
154  if (ns->cmd == NETIO_CMD_C2S) {
155  ns->state = NETIO_STATE_RECV_DATA;
156  } else if (ns->cmd == NETIO_CMD_S2C) {
157  ns->state = NETIO_STATE_SEND_DATA;
158  /* start timer */
159  ns->time_stamp = sys_now();
160  /* send first round of data */
161 
162  len = tcp_sndbuf(pcb);
163  len = LWIP_MIN(len, ns->data_len - ns->cntr);
164  len = LWIP_MIN(len, NETIO_BUF_SIZE - ns->buf_pos);
165 
166  do {
167  err = tcp_write(pcb, ns->buf_ptr + ns->buf_pos, len, TCP_WRITE_FLAG_COPY);
168  if (err == ERR_MEM) {
169  len /= 2;
170  }
171  } while ((err == ERR_MEM) && (len > 1));
172 
173  ns->buf_pos += len;
174  ns->cntr += len;
175 
176  } else {
177  /* unrecognized command, punt */
178  ns->cntr = 0;
179  ns->buf_pos = 0;
180  ns->buf_ptr[0] = 0;
181  netio_close(ns, pcb);
182  break;
183  }
184  }
185  } else {
186  /* in trouble... shouldn't be in this state! */
187  }
188 
189  } else if (ns->state == NETIO_STATE_RECV_DATA) {
190 
191  if(ns->cntr == 0){
192  /* save the first byte of this new round of data
193  * this will not match ns->buf_ptr[0] in the case that
194  * NETIO_BUF_SIZE is less than ns->data_len.
195  */
196  ns->first_byte = *data_ptr;
197  }
198 
199  ns->buf_ptr[ns->buf_pos++] = *data_ptr++;
200  ns->cntr++;
201 
202  if (ns->buf_pos == NETIO_BUF_SIZE) {
203  /* circularize the buffer */
204  ns->buf_pos = 0;
205  }
206 
207  if(ns->cntr == ns->data_len){
208  ns->cntr = 0;
209  if (ns->first_byte != 0) {
210  /* if this last round did not start with 0,
211  * go look for another command */
212  ns->state = NETIO_STATE_WAIT_FOR_CMD;
213  ns->data_len = 0;
214  ns->cmd = 0;
215  /* TODO LWIP_DEBUGF( print out some throughput calculation results... ); */
216  } else {
217  /* stay here and wait on more data */
218  }
219  }
220 
221  } else if (ns->state == NETIO_STATE_SEND_DATA
222  || ns->state == NETIO_STATE_SEND_DATA_LAST) {
223  /* I don't think this should happen... */
224  } else {
225  /* done / quit */
226  netio_close(ns, pcb);
227  break;
228  } /* end of ns->state condition */
229  } /* end of while data still in this pbuf */
230 
231  q = q->next;
232  }
233 
234  pbuf_free(p);
235 
236  } else {
237 
238  /* error or closed by other side */
239  if (p != NULL) {
240  pbuf_free(p);
241  }
242 
243  /* close the connection */
244  netio_close(ns, pcb);
245 
246  }
247  return ERR_OK;
248 
249 }
250 
252 netio_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
253 {
254  struct netio_state *ns = arg;
255  err_t err = ERR_OK;
256 
257  if (ns->cntr >= ns->data_len && ns->state == NETIO_STATE_SEND_DATA) {
258  /* done with this round of sending */
259  ns->buf_pos = 0;
260  ns->cntr = 0;
261 
262  /* check if timer expired */
263  if (sys_now() - ns->time_stamp > 600) {
264  ns->buf_ptr[0] = 1;
265  ns->state = NETIO_STATE_SEND_DATA_LAST;
266  } else {
267  ns->buf_ptr[0] = 0;
268  }
269  }
270 
271  if(ns->state == NETIO_STATE_SEND_DATA_LAST || ns->state == NETIO_STATE_SEND_DATA){
272  len = tcp_sndbuf(pcb);
273  len = LWIP_MIN(len, ns->data_len - ns->cntr);
274  len = LWIP_MIN(len, NETIO_BUF_SIZE - ns->buf_pos);
275 
276  if(ns->cntr < ns->data_len){
277  do {
278  err = tcp_write(pcb, ns->buf_ptr + ns->buf_pos, len, TCP_WRITE_FLAG_COPY);
279  if (err == ERR_MEM) {
280  len /= 2;
281  }
282  } while ((err == ERR_MEM) && (len > 1));
283 
284  ns->buf_pos += len;
285  if(ns->buf_pos >= NETIO_BUF_SIZE){
286  ns->buf_pos = 0;
287  }
288 
289  ns->cntr += len;
290  }
291  }
292 
293  if(ns->cntr >= ns->data_len && ns->state == NETIO_STATE_SEND_DATA_LAST){
294  /* we have buffered up all our data to send this last round, go look for a command */
295  ns->state = NETIO_STATE_WAIT_FOR_CMD;
296  ns->cntr = 0;
297  /* TODO LWIP_DEBUGF( print out some throughput calculation results... ); */
298  }
299 
300  return ERR_OK;
301 }
302 
304 netio_poll(void *arg, struct tcp_pcb *pcb)
305 {
306  struct netio_state * ns = arg;
307  if(ns->state == NETIO_STATE_SEND_DATA){
308 
309  } else if(ns->state == NETIO_STATE_DONE){
310  netio_close(ns, pcb);
311  }
312 
313  return ERR_OK;
314 
315 }
316 
317 #if NETIO_USE_STATIC_BUF == 1
318 static u8_t netio_buf[NETIO_BUF_SIZE];
319 #endif
320 
322 netio_accept(void *arg, struct tcp_pcb *pcb, err_t err)
323 {
324  struct netio_state * ns;
325 
326  LWIP_UNUSED_ARG(err);
327 
328  ns = (struct netio_state *)mem_malloc(sizeof(struct netio_state));
329 
330  if(ns == NULL){
331  return ERR_MEM;
332  }
333 
334  ns->state = NETIO_STATE_WAIT_FOR_CMD;
335  ns->data_len = 0;
336  ns->cmd = 0;
337  ns->cntr = 0;
338  ns->buf_pos = 0;
339 #if NETIO_USE_STATIC_BUF == 1
340  ns->buf_ptr = netio_buf;
341 #else
342  ns->buf_ptr = (u8_t *)mem_malloc(NETIO_BUF_SIZE);
343 
344  if(ns->buf_ptr == NULL){
345  mem_free(ns);
346  return ERR_MEM;
347  }
348 #endif
349 
350  ns->buf_ptr[0] = 0;
351 
352  tcp_arg(pcb, ns);
353  tcp_sent(pcb, netio_sent);
354  tcp_recv(pcb, netio_recv);
355  tcp_poll(pcb, netio_poll, 4); /* every 2 seconds */
356  return ERR_OK;
357 }
358 
359 void ICACHE_FLASH_ATTR netio_init(void)
360 {
361  struct tcp_pcb *pcb;
362 
363  pcb = tcp_new();
364  tcp_bind(pcb, IP_ADDR_ANY, 18767);
365  pcb = tcp_listen(pcb);
366  tcp_accept(pcb, netio_accept);
367 }
368 
369 #endif /* LWIP_TCP */
u16_t tot_len
Definition: pbuf.h:90
struct pbuf * next
Definition: pbuf.h:78
u16_t len
Definition: pbuf.h:93
#define NULL
Definition: def.h:47
const ip_addr_t ip_addr_any ICACHE_RODATA_ATTR
Definition: ip_addr.c:44
#define ICACHE_FLASH_ATTR
Definition: c_types.h:99
void * mem_malloc(mem_size_t size) ICACHE_FLASH_ATTR
Definition: mem.c:493
static u32_t sys_now(void) ICACHE_FLASH_ATTR
Definition: sys.h:235
#define LWIP_MIN(x, y)
Definition: def.h:44
unsigned long u32_t
Definition: cc.h:56
#define ERR_OK
Definition: err.h:52
static state_t * state
Definition: aes.c:67
Definition: pbuf.h:76
s8_t err_t
Definition: err.h:47
#define IP_ADDR_ANY
Definition: ip_addr.h:92
u8_t pbuf_free(struct pbuf *p) ICACHE_FLASH_ATTR
Definition: pbuf.c:685
unsigned char u8_t
Definition: cc.h:52
void * payload
Definition: pbuf.h:81
#define ERR_MEM
Definition: err.h:53
#define LWIP_UNUSED_ARG(x)
Definition: arch.h:73
void mem_free(void *mem) ICACHE_FLASH_ATTR
Definition: mem.c:310
unsigned short u16_t
Definition: cc.h:54