emWeb Webserver uCOS-III (Sample)

From SEGGER Knowledge Base
Jump to navigation Jump to search
IP_Webserver_uCOS-III_Sample.c
emWeb Server Sample.
Requires modifications No
Download IP_Webserver_uCOS-III_Sample.c

This Sample demonstrates how to start the emWeb server with uCOS-III.

Code

/*********************************************************************
*                   (c) SEGGER Microcontroller GmbH                  *
*                        The Embedded Experts                        *
*                           www.segger.com                           *
**********************************************************************

-------------------------- END-OF-HEADER -----------------------------

Purpose : Sample program for embOS & TCP/IP
*/

#include <stdio.h>
#include <stdlib.h>
#include <os.h>
#include "BSP.h"
#include "IP.h"
#include "IP_Webserver.h"
#include "WEBS_Conf.h"        // Stack size depends on configuration

/*********************************************************************
*
*       Local defines, configurable
*
**********************************************************************
*/

#define USE_RX_TASK   0  // 0: Packets are read in ISR, 1: Packets are read in a task of its own.

//
// Web server and IP stack
//
#define MAX_CONNECTIONS           2
#define BACK_LOG                 20
#define IDLE_TIMEOUT           1000  // Timeout [ms] after which the connection will be closed if no new data is received.
#define SERVER_PORT              80
#define CHILD_ALLOC_SIZE       2560  // NumBytes required from memory pool for one connection. Should be fine tuned according
                                     // to your configuration using IP_WEBS_CountRequiredMem() .

//
// Task priorities.
//
enum {
   TASK_PRIO_WEBS_CHILD = 150
  ,TASK_PRIO_WEBS_PARENT
  ,TASK_PRIO_IP_TASK           // Priority should be higher than all IP application tasks.
#if USE_RX_TASK
  ,TASK_PRIO_IP_RX_TASK        // Must be the highest priority of all IP related tasks.
#endif
};

//
// Task stack sizes that might not fit for some interfaces (multiples of sizeof(int)).
//
#ifndef   APP_TASK_STACK_OVERHEAD
  #define APP_TASK_STACK_OVERHEAD     0
#endif

#ifndef   STACK_SIZE_SERVER
  #define STACK_SIZE_SERVER           (2560 + APP_TASK_STACK_OVERHEAD)
#endif

//
// UDP discover
//
#define ETH_UDP_DISCOVER_PORT    50020
#define PACKET_SIZE              0x80

typedef struct{
  U8      InUse;
  long    hSock;
  CPU_STK Stack[STACK_SIZE_SERVER];
} WEBS_STACK_STRUCT;


/*********************************************************************
*
*       Static data
*
**********************************************************************
*/

static U32 _aPool[(CHILD_ALLOC_SIZE * MAX_CONNECTIONS) / sizeof(int)];  // Memory pool for the Web server child tasks.

static IP_HOOK_ON_STATE_CHANGE _StateChangeHook;
static int                     _IFaceId;
static int                     _ConnectCnt;

//
// Task stacks and Task-Control-Blocks.
//
static CPU_STK _StackIP[TASK_STACK_SIZE_IP_TASK];       // Stack of the IP_Task.
static OS_TCB  _TCBIP;                                  // Task-Control-Block of the IP_Task.
#if USE_RX_TASK
static OS_TCB  _TCBIPRx;                                // Stack of the IP_RxTask.
static CPU_STK _StackIPRx[TASK_STACK_SIZE_IP_RX_TASK];  // Task-Control-Block of the IP_RxTask.
#endif

//
// Webserver TCBs and stacks
//
static OS_TCB             _aWebTasks[MAX_CONNECTIONS];
static WEBS_STACK_STRUCT  _aWebStacks[MAX_CONNECTIONS];


//
// File system info
//
static const IP_FS_API *_pFS_API;

/*********************************************************************
*
*       Prototypes
*
**********************************************************************
*/
#ifdef __cplusplus
extern "C" {     /* Make sure we have C-declarations in C++ programs */
#endif
void MainTask(void);
#ifdef __cplusplus
}
#endif

/*********************************************************************
*
*       Static code
*
**********************************************************************
*/

/*********************************************************************
*
*       _OnStateChange()
*
* Function description
*   Callback that will be notified once the state of an interface
*   changes.
*
* Parameters
*   IFaceId   : Zero-based interface index.
*   AdminState: Is this interface enabled ?
*   HWState   : Is this interface physically ready ?
*/
static void _OnStateChange(unsigned IFaceId, U8 AdminState, U8 HWState) {
  //
  // Check if this is a disconnect from the peer or a link down.
  // In this case call IP_Disconnect() to get into a known state.
  //
  if (((AdminState == IP_ADMIN_STATE_DOWN) && (HWState == 1)) ||  // Typical for dial-up connection e.g. PPP when closed from peer. Link up but app. closed.
      ((AdminState == IP_ADMIN_STATE_UP)   && (HWState == 0))) {  // Typical for any Ethernet connection e.g. PPPoE. App. opened but link down.
    IP_Disconnect(IFaceId);                                       // Disconnect the interface to a clean state.
  }
}

/*********************************************************************
*
*       _closesocket()
*
*  Function description
*    Wrapper for closesocket()
*/
static int _closesocket(long pConnectionInfo) {
  int r;
  struct linger Linger;

  Linger.l_onoff  = 1;  // Enable linger for this socket to verify that all data is send.
  Linger.l_linger = 1;  // Linger timeout in seconds
  setsockopt((long)pConnectionInfo, SOL_SOCKET, SO_LINGER, &Linger, sizeof(Linger));
  r = closesocket((long)pConnectionInfo);
  return r;
}

/*********************************************************************
*
*       _Recv()
*
*  Function description
*    Wrapper for recv()
*/
static int _Recv(unsigned char *buf, int len, void *pConnectionInfo) {
  int r;

  r = recv((long)pConnectionInfo, (char *)buf, len, 0);
  return r;
}

/*********************************************************************
*
*       _Send()
*
*  Function description
*    Wrapper for send()
*/
static int _Send(const unsigned char *buf, int len, void* pConnectionInfo) {
  int r;

  r = send((long)pConnectionInfo, (const char *)buf, len, 0);
  return r;
}

/*********************************************************************
*
*       WEBS_IP_API
*
*  Description
*   IP related function table
*/
static const WEBS_IP_API _Webs_IP_API = {
  _Send,
  _Recv
};

/*********************************************************************
*
*       _Alloc()
*
*  Function description
*    Wrapper for Alloc(). (emNet: IP_MEM_Alloc())
*/
static void * _Alloc(U32 NumBytesReq) {
  return IP_AllocEx(_aPool, NumBytesReq);
}

/*********************************************************************
*
*       _Free()
*
*  Function description
*    Wrapper for Alloc(). (emNet: IP_MEM_Alloc())
*/
static void _Free(void *p) {
  IP_Free(p);
}

/*********************************************************************
*
*       WEBS_SYS_API
*
*  Description
*   System related function table
*/
static const WEBS_SYS_API _Webs_SYS_API = {
  _Alloc,
  _Free
};

/*********************************************************************
*
*       _AddToConnectCnt
*/
static void _AddToConnectCnt(int Delta) {
  IP_OS_INC_DI();
  _ConnectCnt += Delta;
  IP_OS_DEC_RI();
}

/*********************************************************************
*
*       _WebServerChildTask
*/
static void _WebServerChildTask(void *pContext) {
  WEBS_CONTEXT ChildContext;
  OS_ERR  Os_Err;
  long    hSock;
  int     Index;
  int     Opt;
  int     r;

  Index    = (int)pContext;
  hSock    = _aWebStacks[Index].hSock;
  Opt      = 1;
  setsockopt(hSock, SOL_SOCKET, SO_KEEPALIVE, &Opt, sizeof(Opt));
  IP_WEBS_Init(&ChildContext, &_Webs_IP_API, &_Webs_SYS_API, _pFS_API, &WebsSample_Application);  // Initialize the context of the child task.
  if (_ConnectCnt < MAX_CONNECTIONS) {
    r = IP_WEBS_ProcessEx(&ChildContext, (void *)hSock, NULL);
  } else {
    r = IP_WEBS_ProcessLastEx(&ChildContext, (void *)hSock, NULL);
  }
  if (r != WEBS_CONNECTION_DETACHED) {
    //
    // Only close the socket if it is still in web server context and has
    // not been detached to another handler like a WebSocket handler.
    //
    _closesocket(hSock);
  }
  OSSchedLock(&Os_Err);
  _AddToConnectCnt(-1);
  _aWebStacks[Index].InUse = 0;
  OSTaskDel(0, &Os_Err);
  OSSchedUnlock(&Os_Err);
}

/*********************************************************************
*
*       _WebServerParentTask
*/
static void _WebServerParentTask(void) {
  struct sockaddr_in InAddr;
  OS_ERR Os_Err;
  U32  Timeout;
  U32  NumBytes;
  long hSockListen;
  long hSock;
  int  i;
  int  t;
  int  t0;
  WEBS_BUFFER_SIZES BufferSizes;

  Timeout = IDLE_TIMEOUT;
  //
  // Assign file system
  //
  _pFS_API = &IP_FS_ReadOnly;  // To use a a real filesystem like emFile replace this line.
//  _pFS_API = &IP_FS_emFile;    // Use emFile
//  IP_WEBS_AddUpload();         // Enable upload
  //
  // Configure buffer size.
  //
  IP_MEMSET(&BufferSizes, 0, sizeof(BufferSizes));
  BufferSizes.NumBytesInBuf       = WEBS_IN_BUFFER_SIZE;
  BufferSizes.NumBytesOutBuf      = IP_TCP_GetMTU(_IFaceId) - 72;  // Use max. MTU configured for the last interface added minus worst case IPv4/TCP/VLAN headers.
                                                                   // Calculation for the memory pool is done under assumption of the best case headers with - 40 bytes.
  BufferSizes.NumBytesParaBuf     = WEBS_PARA_BUFFER_SIZE;
  BufferSizes.NumBytesFilenameBuf = WEBS_FILENAME_BUFFER_SIZE;
  BufferSizes.MaxRootPathLen      = WEBS_MAX_ROOT_PATH_LEN;
  //
  // Configure the size of the buffers used by the Webserver child tasks.
  //
  IP_WEBS_ConfigBufSizes(&BufferSizes);
  //
  // Check memory pool size.
  //
  NumBytes = IP_WEBS_CountRequiredMem(NULL);     // Get NumBytes for internals of one child thread.
  NumBytes = (NumBytes + 64) * MAX_CONNECTIONS;  // Calc. the total amount for x connections (+ some bytes for managing a memory pool).
  IP_Logf_Application("WEBS: Using a memory pool of %lu bytes for %lu connections.", sizeof(_aPool), MAX_CONNECTIONS);
  if (NumBytes > sizeof(_aPool)) {
    IP_Warnf_Application("WEBS: Memory pool should be at least %lu bytes.", NumBytes);
  }
  //
  // Give the stack some more memory to enable the dynamical memory allocation for the Web server child tasks
  //
  IP_AddMemory(_aPool, sizeof(_aPool));
  //
  // Get a socket into listening state
  //
  hSockListen = socket(AF_INET, SOCK_STREAM, 0);
  if (hSockListen == SOCKET_ERROR) {
    while(1); // This should never happen!
  }
  IP_MEMSET(&InAddr, 0, sizeof(InAddr));
  InAddr.sin_family      = AF_INET;
  InAddr.sin_port        = htons(SERVER_PORT);
  InAddr.sin_addr.s_addr = INADDR_ANY;
  bind(hSockListen, (struct sockaddr *)&InAddr, sizeof(InAddr));
  listen(hSockListen, BACK_LOG);
  //
  // Loop once per client and create a thread for the actual server
  //
  do {
Next:
    //
    // Wait for an incoming connection
    //
    hSock = 0;
    if ((hSock = accept(hSockListen, NULL, NULL)) == SOCKET_ERROR) {
      continue;    // Error
    }
    //
    // Create server thread to handle connection.
    // If connection limit is reached, keep trying for some time before giving up and outputting an error message
    //
    t0 = IP_OS_GetTime32() + 1000;
    do {
      if (_ConnectCnt < MAX_CONNECTIONS) {
        for (i = 0; i < MAX_CONNECTIONS; i++) {
          //r = OS_IsTask(&_aWebTasks[i]);
          if (_aWebStacks[i].InUse == 0) {
            setsockopt(hSock, SOL_SOCKET, SO_RCVTIMEO, &Timeout, sizeof(Timeout));  // Set receive timeout for the client.
            //
            // Start the child task.
            //
            _aWebStacks[i].InUse = 1;
            _aWebStacks[i].hSock = hSock;
            OSTaskCreate(&_aWebTasks[i],
                         (CPU_CHAR *)"Webserver Child",
                         _WebServerChildTask,
                         (void *)i,
                         30,
                         &_aWebStacks[i].Stack[0],
                         10,
                         SEGGER_COUNTOF(_aWebStacks[i].Stack),
                         0, 0, NULL,
                         OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR,
                         &Os_Err);
            _AddToConnectCnt(1);
            goto Next;
          }
        }
      }
      //
      // Check time out
      //
      t = IP_OS_GetTime32();
      if ((t - t0) > 0) {
        IP_WEBS_OnConnectionLimit(_Send, _Recv, (void*)hSock);
        _closesocket(hSock);
        break;
      }
      OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_DLY,&Os_Err);
    } while(1);
  }  while(1);
}

/*********************************************************************
*
*       _OnRx
*
*  Function description
*    Discover client UDP callback. Called from stack
*    whenever we get a discover request.
*
*  Return value
*    IP_RX_ERROR  if packet is invalid for some reason
*    IP_OK        if packet is valid
*/
#if ETH_UDP_DISCOVER_PORT
static int _OnRx(IP_PACKET *pInPacket, void *pContext) {
  char *      pInData;
  IP_PACKET * pOutPacket;
  char *      pOutData;
  U32         TargetAddr;
  U32         IPAddr;
  unsigned    IFaceId;

  (void)pContext;

  IFaceId = IP_UDP_GetIFIndex(pInPacket);  // Find out the interface that the packet came in.
  IPAddr  = htonl(IP_GetIPAddr(IFaceId));
  if (IPAddr == 0) {
    goto Done;
  }
  pInData = (char*)IP_UDP_GetDataPtr(pInPacket);
  if (memcmp(pInData, "Discover", 8)) {
    goto Done;
  }
  //
  // Alloc packet
  //
  pOutPacket = IP_UDP_AllocEx(IFaceId, PACKET_SIZE);
  if (pOutPacket == NULL) {
    goto Done;
  }
  //
  // Fill packet with data
  //
  pOutData = (char*)IP_UDP_GetDataPtr(pOutPacket);
  IP_UDP_GetSrcAddr(pInPacket, &TargetAddr, sizeof(TargetAddr));    // Send back Unicast
  memset(pOutData, 0, PACKET_SIZE);
  strcpy(pOutData + 0x00, "Found");
  IPAddr = htonl(IP_GetIPAddr(IFaceId));
  memcpy(pOutData + 0x20, (void*)&IPAddr, 4);      // 0x20: IP address
  IP_GetHWAddr(IFaceId, (U8*)pOutData + 0x30, 6);  // 0x30: MAC address
  //
  // Send packet
  //
  IP_UDP_SendAndFree(IFaceId, TargetAddr, ETH_UDP_DISCOVER_PORT, ETH_UDP_DISCOVER_PORT, pOutPacket);
Done:
  return IP_OK;
}
#endif

/*********************************************************************
*
*       _IP_Task
*
*  Function description
*    Wrapper for IP_Task.
*    Avoids using incompatible function types for OSTaskCreate().
*/
static void _IP_Task(void * p) {
  IP_USE_PARA(p);
  IP_Task();
}

/*********************************************************************
*
*       _IP_RxTask
*
*  Function description
*    Wrapper for IP_RxTask.
*    Avoids using incompatible function types for OSTaskCreate().
*/
#if USE_RX_TASK
static void _IP_RxTask(void * p) {
  IP_USE_PARA(p);
  IP_RxTask();
}
#endif

/*********************************************************************
*
*       MainTask
*/
void MainTask(void) {
  OS_ERR Os_Err;
  IP_Init();
  _IFaceId = IP_INFO_GetNumInterfaces() - 1; // Get the last registered interface ID as this is most likely the interface we want to use in this sample.
  //
  // Start the IP_Task.
  //
  OSTaskCreate(&_TCBIP,
               (CPU_CHAR *)"IP_Task",
               _IP_Task,
               (void *)0,
               20, // The lower the number, the higher the priority.
               &_StackIP[0],
               10,
               SEGGER_COUNTOF(_StackIP),
               0, 0, NULL,
               OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR,
               &Os_Err);
#if USE_RX_TASK
  //
  // Start the IP_RxTask, optional.
  //
  OSTaskCreate(&_TCBIPRx,
               (CPU_CHAR *)"IP_RxTask",
               _IP_RxTask,
               (void *)0,
               15,
               &_StackIPRx[0],
               10,
               SEGGER_COUNTOF(_StackIPRx),
               0, 0, NULL,
               OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR,
               &Os_Err);
#endif
  IP_AddStateChangeHook(&_StateChangeHook, _OnStateChange);                            // Register hook to be notified on disconnects.
  IP_Connect(_IFaceId);                                                                // Connect the interface if necessary.
  //
  // IPv4 address configured ?
  //
  while (IP_IFaceIsReadyEx(_IFaceId) == 0) {
    BSP_ToggleLED(0);
    OSTimeDlyHMSM(0,0,0,200,OS_OPT_TIME_DLY,&Os_Err);
  }
  BSP_ClrLED(0);
  OSTaskChangePrio(NULL, 25, &Os_Err);
//  OS_SetTaskName(OS_GetTaskID(), "IP_WebServer");
#if ETH_UDP_DISCOVER_PORT
  //
  // Open UDP port ETH_UDP_DISCOVER_PORT for emNet discover
  //
  IP_UDP_Open(0L /* any foreign host */,  ETH_UDP_DISCOVER_PORT, ETH_UDP_DISCOVER_PORT,  _OnRx, 0L);
#endif
  IP_WEBS_X_SampleConfig();  // Load a web server sample config that might add other resources like REST.
  _WebServerParentTask();
}

/*************************** End of file ****************************/