Mod_lwiputil.c - david-macmahon/wiki_convert_test GitHub Wiki

  //#define VERBOSE
  //#define PRETTY

  //GJONES 2007.12.23   - Fixed error where a reset or aborted connection was not dealt with correctly.
  //        The telnet_err function is called when the tcp stack encounters an error. Previously this function did
  //        nothing except echo the error code to the serial port. Now the function checks if the pcb structure is still
  //        existant, then closes the connection. Looking at the error codes defined in lwip/err.h, it seems like any
  //        connection with one of these error codes should be closed anyway.

  #include "lwip/debug.h"
  #include "lwip/stats.h"
  #include "lwip/tcp.h"
  #include "xparameters.h"
  #include "lwiputil.h"
  #include "xuartlite_l.h"
  #include "xtime_l.h"
  #include "xcache_l.h"
  #include "tinysh.h"
  #include "core_info.h"
  #include "lwip/udp.h"

  #include "fifo.h"

  // Defined in main.c
  void display_welcome_msg(void);
  void process_inputs(int feed_tinysh);

  /* IBOB Configuration Variables */

  // Defined in lwipinit.c
  extern char fullmac[6];
  extern char ip[4];
  extern char subnet[4];
  extern char gateway[4];
  extern fifo_p input_fifo;
  extern fifo_p output_fifo;

  struct conn_state {
    struct tcp_pcb * tpcb;
    u8_t failed;
    u8_t netclient;
    // For the future?
    //Xuint32 CoreNum;
    //Xuint32 OutputMode;
    //Xuint32 PostState;
    //char byte[4];
  };

  void close_conn(struct conn_state *ws);

  #define FAILED_MAX 8

  /* printmode switch, default = serial, 1 for tcp */
  /* Defined in main.c */
  extern Xuint8 printmode;
  extern Xuint8 immediatemode;

  // pcb for server socket
  struct tcp_pcb *telnetd_pcb = NULL;
  // conn_state for the current (one and only) telnet connection
  // If timysh ever supports mutiple sessions we might want to
  // support more than one simultaneous telnet connection.
  struct conn_state *telnetstate = NULL;

  // Somewhat inefficient intermedite buffer for double copy on send
  // (one copy from fifo into this buffer, another copy in tcp_write
  // from this buffer to lwip).
  #define STAGING_BUF_SIZE 8000
  char staging_buf[STAGING_BUF_SIZE];

  // HTTP Stuff is all ifdef'd out for now
  // TODO Keep or not?
  #ifdef HTTP_ENABLE

  struct parsed_get {
    char url[256];
    char variable[256];
    char value[256];
  };

  /* Define HTTP Connection Types */
  #define INIT_CONNECT -1
  #define NOT_FOUND_PAGE 1
  #define INDEX_PAGE 2
  #define BRAM_PAGE 3
  #define REG_PAGE 4
  #define NOT_FOUND 7
  #define MAIN_PAGE 8
  #define NUMBER_PAGE 9
  #define POST_REQUEST 10

  struct tcp_pcb *httppcb;
  struct conn_state *httpstate;
  struct parsed_get *parsed;

  #endif // HTTP_ENABLE

  // Prints to UART regardless of printmode setting
  #if 0
  #define uart_printf(...)
  #else
  #define uart_printf(...) do \
  { \
    Xuint8 saved_printmode = printmode; \
    printmode = 0; \
    xil_printf(__VA_ARGS__); \
    printmode = saved_printmode; \
  } while(0)
  #endif

  #ifdef LWIP_DEBUG
  void dump_fifo(fifo_p f)
  {
    Xuint8 saved_printmode = printmode;
    printmode=0;
    xil_printf("depth=%d, head=%d, tail=%d, avail=%d, occupied=%d, full=%d, empty=%d\n\r",
        fifo_depth(f),
        f->head, f->tail,
        fifo_avail(f),
        fifo_occupied(f),
        is_fifo_full(f),
        is_fifo_empty(f));
    printmode = saved_printmode;
  }
  #endif

  /*
     sends all bytes in output fifo
  */

  /*---------------------------------------------------------------------------*/
    void
  send_buf()
  {
    int maxSendable;
    int fifoCount;
    int amountToSend;
    int amountFromFifo;
    struct tcp_pcb *pcb;
    unsigned int counter=0;

    if(!telnetstate) {
      uart_printf("%s: telnetstate is NULL!\n\r", __FUNCTION__);
      return;
    }

    pcb = telnetstate->tpcb;

    // Until fifo is empty
    while(!is_fifo_empty(output_fifo)) {

      // Until maxSendable is greater than 0,
      // process inputs (but do not feed tinysh).
      counter=0;
      while((maxSendable=tcp_sndbuf(pcb)) == 0) {
        // Timeout
        if(counter++ > 100000) {
          uart_printf("maxSendable looped too long (%d times).\r\n",counter);
          // Truncate fifo
          fifo_reset(output_fifo);
          return;
        }
        process_inputs(0);
      }
      if(maxSendable > STAGING_BUF_SIZE) {
        maxSendable = STAGING_BUF_SIZE;
      }
      fifoCount = fifo_occupied(output_fifo);
      if(fifoCount > maxSendable) {
        amountToSend = maxSendable;
      } else {
        amountToSend = fifoCount;
      }
      amountFromFifo = fifo_get(output_fifo,staging_buf,amountToSend);
      if(amountFromFifo != amountToSend) {
        // Should "never" happen
        uart_printf("%s: Expected %d from fifo, but got %d\n\r", __FUNCTION__, amountToSend, amountFromFifo);
      }

      // Until tcp_write succeeds, process inputs (but do not feed tinysh)
      counter=0;
      while(tcp_write(pcb, staging_buf, amountToSend, 1) == ERR_MEM) {
        // Timeout
        if(counter++ > 100000) {
          uart_printf("tcp_write looped too long (%d times).\r\n",counter);
          // Truncate fifo
          fifo_reset(output_fifo);
          return;
        }
        process_inputs(0);
      }
      tcp_output(pcb);
    }
  }

    void
  bufferbyte(unsigned char c)
  {
    while(telnetstate && !fifo_put(output_fifo,&c,1)) {
      // TODO Timeout!
      send_buf();
    }
    if(!telnetstate) {
      //uart_printf("bufferbyte: telnetstate is NULL\n\r");
    } else if(immediatemode) {
      send_buf();
    }
  }


  /*---------------------------------------------------------------------------*/
    struct conn_state *
  state_new(struct tcp_pcb *tpcb)
  {
    struct conn_state * ws = mem_malloc(sizeof(struct conn_state));

    if(ws) {
      /* Initialize the structure. */
      ws->tpcb = tpcb;
      ws->failed = 0;
      ws->netclient = NETCLIENT_OTHER;
    }

    return ws;
  }


  /*---------------------------------------------------------------------------*/
    void
  telnet_err(void *arg, err_t err)
  {
    struct conn_state *ws = arg;

    uart_printf("tcp_err: %d\n\r", err);
    if(ws == NULL) {
            uart_printf("ws was NULL\n\r");         //If the connection is no longer valid it must have been closed already
    }
    else {
            uart_printf("closing connection\n\r");
            close_conn(telnetstate);
    }

    // TODO Is it OK to free?
    //if(arg != NULL) {
    //  mem_free(arg);
    //}
  }

  /*---------------------------------------------------------------------------*/
    void
  close_conn(struct conn_state *ws)
  {
    if(ws) {
      struct tcp_pcb * tpcb = ws->tpcb;
      if(tpcb) {
        //tcp_sent(tpcb, NULL);
        //tcp_recv(tpcb, NULL);
        //tcp_poll(tpcb, NULL, 1);
        //tcp_arg(tpcb, NULL);
        tcp_close(tpcb);
        //uart_printf("Connection closed\n\r");
      }
      if(ws == telnetstate) {
        mem_free(telnetstate);
        tinysh_set_prompt("IBOB % ");
        telnetstate=NULL;
        fifo_reset(input_fifo);
        fifo_reset(output_fifo);
      }
    }
  }

  /*---------------------------------------------------------------------------*/
    err_t
  telnet_poll(void *arg, struct tcp_pcb *pcb)
  {
    struct conn_state *ws = arg;

    // If unknown pcb?
    if(ws == NULL) {
      return tcp_close(pcb);
    }
    // If not current telnet state
    if(ws != telnetstate) {
      close_conn(ws);
    }

    if(telnetstate->failed >= FAILED_MAX) {
      uart_printf("%s: Failed!\n\r", __FUNCTION__);
      close_conn(telnetstate);
      tcp_abort(pcb); // TODO Is this too late?
      return ERR_ABRT;
    }

    return ERR_OK;
  }

  #if 0
  /*---------------------------------------------------------------------------*/
    err_t
  telnet_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
  {
    struct conn_state *ws = arg;

    // If unknown pcb?
    if(ws == NULL) {
      uart_printf("%s: Null arg!\n\r", __FUNCTION__);
      return tcp_close(pcb);
    }
    // If not current telnet state
    if(ws != telnetstate) {
      uart_printf("%s: Old arg!\n\r", __FUNCTION__);
      close_conn(ws);
      return ERR_OK;
    }

    // Maybe send more bytes
    send_buf();

    return ERR_OK;
  }
  #endif

  /*---------------------------------------------------------------------------*/
  // Puts received bytes into input fifo
    err_t
  telnet_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
  {
    int recved;
    Xuint8 saved_printmode;
    char * crlf = "\r\n";

    struct conn_state *ws = arg;

    // If unknown pcb?
    if(ws == NULL) {
      return tcp_close(pcb);
    }
    // If not current telnet state
    if(ws != telnetstate) {
      close_conn(ws);
      return ERR_OK;
    }

    if(p == NULL) {
      //if(ws->tpcb != pcb) {
      //  uart_printf("Weirdo-");
      //}
      uart_printf("Connection closed by foreign host.\n\r");
      close_conn(telnetstate);
    } else {
      // Determine client type
      if(ws->netclient==NETCLIENT_UNKNOWN) {
        if( (*(char *)(p->payload)) != 255) {
          ws->netclient=NETCLIENT_OTHER;
          tinysh_set_prompt("");
        } else {
          ws->netclient=NETCLIENT_TELNET;
          /* Set remote client to character-by-character mode */
          immediatemode = 0;
          bufferbyte(255);    //IAC
          bufferbyte(251);    //WILL
          bufferbyte(1);      //ECHO
          bufferbyte(255);    //IAC
          bufferbyte(251);    //WILL
          immediatemode = 1;
          bufferbyte(3);      //SUPPRESS GO AHEAD
          immediatemode = 0;

          /* Set up for TCP Output */
          saved_printmode = printmode;
          printmode = 1;
          /* Display Welcome Message */
          display_welcome_msg();
          /* Reset back to saved printmode */
          printmode = saved_printmode;

          // Stuff carriage return and line feed into input fifo
          fifo_put(input_fifo,crlf,2); /* Get a TinySH Prompt */
        }
      }
      // Receive bytes into fifo
      recved = fifo_put(input_fifo,p->payload,p->len);
      tcp_recved(pcb, recved);
      pbuf_free(p); // TODO Is this needed?  Is it bad?
    }

    return ERR_OK;
  }

  /*---------------------------------------------------------------------------*/
    err_t
  telnet_accept(void *arg, struct tcp_pcb *pcb, err_t err)
  {
    // Allocate and init structure to hold the state of the new connection
    struct conn_state *new_telnetstate = state_new(pcb);
    if(new_telnetstate == NULL) {
      uart_printf("could not malloc memory for conn_state\n\r");
      return ERR_MEM;
    }

    // Close any existing connection before accepting this new one
    close_conn(telnetstate);
    telnetstate = new_telnetstate;

    // TODO Why would we want to do this?
    //tcp_setprio(pcb, TCP_PRIO_MIN);

    // Setup TCP callbacks and callback arg data
    // Set callback arg data first!
    tcp_arg(pcb, telnetstate);
    tcp_recv(pcb, telnet_recv);
    tcp_err(pcb, telnet_err);
    tcp_poll(pcb, telnet_poll, 100);
    //tcp_sent(pcb, telnet_sent);

    //uart_printf("Network Connection Established\n\r");

    return ERR_OK;
  }

  /*---------------------------------------------------------------------------*/
    void
  telnet_init(void)
  {
    telnetd_pcb = tcp_new();
    tcp_bind(telnetd_pcb, IP_ADDR_ANY, TELNET_PORT);
    telnetd_pcb = tcp_listen(telnetd_pcb);
    tcp_accept(telnetd_pcb, telnet_accept);
  }


  #ifdef HTTP_ENABLE
  /*---------------------------------------------------------------------------*/
    err_t
  http_poll(void *arg, struct tcp_pcb *pcb)
  {
    struct conn_state *ws = arg;
  #ifdef VERBOSE
    xil_printf("Entering http_poll.\n");
  #endif
    if(ws == NULL) {
  #ifdef VERBOSE
      xil_printf("http_poll: ws == NULL, closing TCP/HTTP connection\r\n");
  #endif
      close_conn(pcb, ws);
    }

    if(ws->failed >= FAILED_MAX) {
  #ifdef VERBOSE
      xil_printf("http_poll: Tried %d times, closing and aborting tcp connection.\r\n", ws->failed);
  #endif
      close_conn(pcb, ws);
      tcp_abort(pcb);
      return ERR_ABRT;
    }

  #ifdef VERBOSE
    xil_printf("Exiting http_poll.\n");
  #endif
    return ERR_OK;
  }


  /*---------------------------------------------------------------------------*/
    void
  parse(char *chunk)
  {
    int aoffset=4, boffset=4, coffset=4;

  #ifdef VERBOSE
    xil_printf("Beginning parse...\r\n");
  #endif

    while(*(chunk + coffset) != ' ') {
      coffset++;
    }

  #ifdef VERBOSE
    xil_printf("Got coffset: %d \r\n", coffset);
  #endif


    while(*(chunk + aoffset) != '?') {
      aoffset++;
      if (aoffset > coffset) break;
    }
  #ifdef VERBOSE
    xil_printf("Got aoffset: %d \r\n", aoffset);
  #endif

    while(*(chunk + boffset) != '=') {
      boffset++;
      if (boffset > coffset) break;
    }
  #ifdef VERBOSE
    xil_printf("Got boffset: %d \r\n", boffset);
  #endif
    if (boffset > coffset) {

      memcpy((*parsed).url, chunk + 5, coffset - 4);
      parsed->url[coffset-5] = 0;
      parsed->variable[0] = 0;
      parsed->value[0] = 0;

    } else {

      memcpy((*parsed).url, chunk + 5, aoffset - 5);
      memcpy((*parsed).variable, chunk + aoffset + 1, boffset - aoffset -1);
      memcpy((*parsed).value, chunk + boffset + 1, coffset - boffset - 1);

      parsed->url[aoffset-5] = 0;
      parsed->variable[boffset-aoffset-1] = 0;
      parsed->value[coffset-boffset-1] = 0;
    }
  }
  #endif // HTTP_ENABLE