webport.c

来自「针对AVR单片机开发的嵌入式操作系统」· C语言 代码 · 共 448 行

C
448
字号
/*
 * Copyright (C) 2002-2005 by egnite Software GmbH. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holders nor the names of
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
 * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * For additional information see http://www.ethernut.de/
 */

/*
 * $Log$
 */

/*!
 * \file webport.c
 * \brief Main routines.
 */

/*!
 * \addtogroup xgWPDefs
 */
/*@{*/

/*!
 * \brief Hard coded MAC address.
 *
 * The MAC address is used in case the system starts without any 
 * previously configuration stored in non-volatile memory.
 * Typically the non-volatile memory is preloaded by some other
 * software, like Basemon. On ATmega128 boards, the 'preserve EEPROM'
 * flag should have been set to avoid clearing the EEPROM when the
 * part is erased during the upload of a new application.
 */
#define MYMAC   0x00, 0x06, 0x98, 0x00, 0x10, 0x00

/*!
 * \brief Hard coded IP address.
 *
 * The IP address and network mask are used in
 * the same case and when there is no DHCP server available in your
 * LAN.
 *
 */
#define MYIP    "192.168.192.100"

/*!
 * \brief Hard coded IP mask.
 */
#define MYMASK  "255.255.255.0"

/*@}*/

/*
 * Some standard C library headers.
 */
#include <string.h>
#include <stdio.h>
#include <io.h>

/*
 * Include the proper header file related to our Ethernet hardware.
 * Ethernut 1.3 and Charon II use the 10 MBit Realtek RTL8019AS, while 
 * Ethernut 2 is equipped with the 10/100 MBit LAN91C111, manufactured 
 * by SMSC.
 */
#ifdef ETHERNUT2
#include <dev/lanc111.h>
#else
#include <dev/nicrtl.h>
#endif


/*
 * UROM is a most simple file system to be used for storing web
 * contents.
 */
#include <dev/urom.h>

/*
 * More Nut/OS header files.
 */
#include <sys/version.h>
#include <sys/confnet.h>
#include <sys/heap.h>
#include <sys/timer.h>
#include <sys/thread.h>
#include <sys/socket.h>

/*
 * Basic internet functions.
 */
#include <arpa/inet.h>

/*
 * Beside simple TCP/IP routines, Nut/Net offers some higher
 * level protocol libraries. We make use of the DHCP client
 * and the HTTP helper routines.
 */
#include <pro/dhcp.h>
#include <pro/httpd.h>


#include "webport.h"

/*
 * We optionally use an UART for debug output.
 */
#ifdef WP_STATUSOUT
#include <dev/debug.h>
#endif

/*
 * For debugging purposes, several routines are available to
 * display the thread list, trace TCP packets etc. However,
 * this requires to recompile and relink the system with
 * NUTDEBUG defined in the compile options.
 */
#ifdef NUTDEBUG
#include <sys/osdebug.h>
#include <net/netdebug.h>
#endif

/*!
 * \addtogroup xgWebPort
 */
/*@{*/

/*!
 * \brief CPU reset macro.
 *
 * Start all over on fatal initialization errors.
 *
 * \note This function currently works on AVR systems only. On other 
 *       architectures an endless loop will be entered.
 */
#if defined(__AVR_ATmega103__) || defined(__AVR_ATmega128__)
#define JUMP_RESET { asm("cli"); asm("call 0"); }
#else
#define JUMP_RESET for(;;)
#endif

/*!
 * \brief Process a single HTTP request.
 *
 * This routine performs the whole cycle of a HTTP request.
 *
 * - Creating a socket.
 * - Listening on the defined port.
 * - Processing the request.
 * - Closing the socket.
 */
void Service(void)
{
    TCPSOCKET *sock;            /* TCP socket pointer. */
    FILE *stream;               /* TCP stream. */
    u_long to = 5000;

    /*
     * Let the Nut/OS library create a TCP socket for us.
     */
    if ((sock = NutTcpCreateSocket()) != 0) {

        NutTcpSetSockOpt(sock, SO_RCVTIMEO, &to, sizeof(to));

        /*
         * Listen on the port defined in webport.h. Typically this is
         * port 80.
         */
        if (NutTcpAccept(sock, HTTP_PORT) == 0) {
            /*
             * The Nut/OS memory manager dynamically allocates and 
             * releases SRAM space. In this application most memory is 
             * used by incoming telegrams and the socket interface. If 
             * the available space becomes low, we sleep for 200 
             * milliseconds and try again. This way we will always leave 
             * some RAM for more important parts of the system.
             *
             * The watermark is defined in webport.h.
             */
            while (NutHeapAvailable() < LOW_MEM_MARK) {
                NutSleep(200);
            }

            /*
             * Create a stream from the socket, so we can use stdio.
             */
            if ((stream = _fdopen((int) sock, "r+b")) != 0) {
                /*
                 * Process HTTP request. This routine is part of the
                 * Nut/OS libraries and greatly simplifies our application
                 * code. It will automatically read requested files from
                 * the file system and send them to the browser or
                 * call the proper CGI routines.
                 */
                NutHttpProcessRequest(stream);

                /*
                 * Close the stream to release its resources.
                 */
                fclose(stream);
            }
        }

        /*
         * Close the socket. If the client didn't close the connection yet,
         * it will now be forced to do so.
         */
        NutTcpCloseSocket(sock);
    }
}

/*! \fn ServiceThread(void *arg)
 * \brief Background thread to process HTTP requests.
 *
 * This thread calls Service() in an endless loop. It can be started more
 * than once to support parallel connections.
 */
THREAD(ServiceThread, arg)
{
    /*
     * Loop endless for connections.
     */
    for (;;) {
        Service();
    }
}

/*!
 * \brief Main entry of our application.
 *
 * Nut/OS automatically calls this entry after initialization.
 * 
 * This routine will do all required initialization, start some
 * background threads and then process incoming HTTP requests together 
 * with the concurrently running background threads.
 */
int main(void)
{
    u_char i;

    /*
     * We may optionally use the serial port for debugging output.
     */
#if defined(WP_STATUSOUT) || defined(NUTDEBUG)
    u_long baud = 115200;

    /*
     * All Nut/OS devices must be registered. This serves two purposes.
     * First, it will create a reference to the device driver code, so
     * this is linked to the application code. Second, it will initialze
     * the hardware.
     *
     * Nut/OS offers several device drivers for RS232 communication. The 
     * device devDebug0 is prefered for debug output, because it is not
     * only simple, small and fast, but can also used to create output
     * from within interrupt routines.
     */
    NutRegisterDevice(&devDebug0, 0, 0);

    /*
     * Now, after the device is registered, it is known to the system
     * by its name. We can use a standard C library routine to open it
     * as our standard output.
     */
    freopen(WP_STATUSOUT, "w", stdout);

    /*
     * Nut/OS uses _ioctl() to set device specific parameters, like
     * the baudrate for serial communication.
     */
    _ioctl(_fileno(stdout), UART_SETSPEED, &baud);

    /*
     * Now we can use the devDebug0 device for standard output. Let's
     * print a banner including the Nut/OS version we are running.
     */
    printf("\n\nNut/OS %s Webport Daemon\n", NutVersionString());

    /*
     * If the Nut/OS libraries had been compiled and linked with
     * NUTDEBUG defined, we can switch on additional trace output.
     */
#ifdef NUTDEBUG
    NutTraceTcp(stdout, 0);
    NutTraceOs(stdout, 0);
    NutTraceHeap(stdout, 0);
#endif

    /*
     * During debugging, some delay during startup is quite helpful.
     */
    NutSleep(2000);
#endif

    /*
     * Register the Ethernet controller. Nut/OS supports different
     * controllers. We use DEV_ETHER here, which should have been 
     * defined in one of the include files, which fits our real hardware.
     */
    if (NutRegisterDevice(&DEV_ETHER, 0x8300, 5)) {
#ifdef WP_STATUSOUT
        puts("Registering NIC failed");
#endif
    }

    /*
     * LAN configuration using values in non-volatile memory or 
     * DHCP/ARP. We are ready to wait upto 15 seconds for a 
     * response from the DHCP server. If it fails, use hardcoded 
     * values.
     *
     * The whole process is done in three steps.
     *
     * 1. Call NutDhcpIfConfig() without specifying a MAC address
     *    will try read all values from non-volatile memory. If
     *    there are no values available, it will immediately
     *    return an error.
     *
     * 2. If step 1 fails, call NutDhcpIfConfig() with a hard coded 
     *    MAC address. The routine will use this MAC address to query 
     *    a DHCP server for its IP configuration. If it succeeds, the 
     *    MAC address and the values received from DHCP will be 
     *    stored in non-volatile memory and used in step 1 during the
     *    next reboot. Otherwise the call will return an error.
     *
     * 3. If step 2 fails, call NutNetIfConfig() with hard coded
     *    MAC address and IP configuration. This fixed values will
     *    be stored in non-volatile memory and on the next reboot,
     *    these values will be used in step 1.
     *
     * Note, that DHCP is only used, if the network configuration
     * stored in non-volatile memory doesn't contain a fixed IP
     * address. If a fixed value is used once, Nut/OS will store
     * it in non-volatile memory and never use DHCP unless this
     * fixed values are removed from volatile memory again.
     */
#ifdef WP_STATUSOUT
    printf("\nTry stored configuration...");
#endif
    if (NutDhcpIfConfig("eth0", 0, 10000)) {
        u_char mac[] = { MYMAC };
#ifdef WP_STATUSOUT
        printf("failed\nTry DHCP...");
#endif
        if (NutDhcpIfConfig("eth0", mac, 10000)) {
#ifdef WP_STATUSOUT
            printf("failed\nUse fixed configuration...");
#endif
            if (NutNetIfConfig("eth0", mac, inet_addr(MYIP), inet_addr(MYMASK))) {
#ifdef WP_STATUSOUT
                puts("failed\nWhere is the NIC?");
#endif
                JUMP_RESET;
            }
            /* If not in a local network, we must also call 
               NutIpRouteAdd() to configure the routing. */
        }
    }
#ifdef WP_STATUSOUT
    puts("OK");
    if (confnet.cdn_cip_addr)
        printf("IP addr: %s\n", inet_ntoa(confnet.cdn_cip_addr));
    else
        printf("IP addr: %s\n", inet_ntoa(confnet.cdn_ip_addr));
    printf("IP mask: %s\n", inet_ntoa(confnet.cdn_ip_mask));
    printf("IP gate: %s\n", inet_ntoa(confnet.cdn_gateway));
#endif

    /*
     * Register our device for the file system. If we intend to use 
     * another file system, we need to register it by calling
     * NutRegisterHttpRoot(). If none is registered, the UROM
     * file system will be used by default.
     */
    NutRegisterDevice(&devUrom, 0, 0);

    /*
     * Register our CGI routines. This is required to inform the
     * HTTP helper routines, which corresponding routine has to
     * be called.
     */
    if (NutRegisterCgi(PORT_CONTROL_CGI, CpuPortControl))
        JUMP_RESET;
    if (NutRegisterCgi(PORT_STATUS_CGI, CpuPortStatus))
        JUMP_RESET;
    if (NutRegisterCgi(RELAY_CONTROL_CGI, SpiRelayControl))
        JUMP_RESET;
    if (NutRegisterCgi(OPTO_STATUS_CGI, SpiOptoStatus))
        JUMP_RESET;
    if (NutRegisterCgi(CHARON_CONTROL_CGI, CharonLedControl))
        JUMP_RESET;
    if (NutRegisterCgi(CHARON_STATUS_CGI, CharonSwitchStatus))
        JUMP_RESET;

    /*
     * Start a defined number of concurrent background threads. The more 
     * threads we have running, the more connections we can handle 
     * concurrently. However, each thread occupies some additional RAM.
     */
    for (i = 0; i < NUM_HTTP_THREADS - 1; i++) {
        /*
         * Not really required here, but we give each thread a unique
         * name.
         */
        char *thname = "httpd0";
        thname[5] = '0' + i;
        NutThreadCreate(thname, ServiceThread, 0, HTTP_THREAD_STACK);
    }

    /*
     * Finally we use the main thread too for handling HTTP request.
     * There's nothing else we can do.
     */
    for (;;) {
        Service();
    }

    /*
     * This point is never reached. Nut/OS applications are started
     * once and continue running forever.
     */
}

/*@}*/

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?