00001 /* 00002 * Copyright (C) 2002-2005 by egnite Software GmbH. All rights reserved. 00003 * 00004 * Redistribution and use in source and binary forms, with or without 00005 * modification, are permitted provided that the following conditions 00006 * are met: 00007 * 00008 * 1. Redistributions of source code must retain the above copyright 00009 * notice, this list of conditions and the following disclaimer. 00010 * 2. Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * 3. Neither the name of the copyright holders nor the names of 00014 * contributors may be used to endorse or promote products derived 00015 * from this software without specific prior written permission. 00016 * 00017 * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS 00018 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00019 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00020 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE 00021 * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00022 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00023 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 00024 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 00025 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 00026 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 00027 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00028 * SUCH DAMAGE. 00029 * 00030 * For additional information see http://www.ethernut.de/ 00031 */ 00032 00033 /* 00034 * $Log$ 00035 */ 00036 00037 /*! 00038 * \file webport.c 00039 * \brief Main routines. 00040 */ 00041 00042 /*! 00043 * \addtogroup xgWPDefs 00044 */ 00045 /*@{*/ 00046 00047 /*! 00048 * \brief Hard coded MAC address. 00049 * 00050 * The MAC address is used in case the system starts without any 00051 * previously configuration stored in non-volatile memory. 00052 * Typically the non-volatile memory is preloaded by some other 00053 * software, like Basemon. On ATmega128 boards, the 'preserve EEPROM' 00054 * flag should have been set to avoid clearing the EEPROM when the 00055 * part is erased during the upload of a new application. 00056 */ 00057 #define MYMAC 0x00, 0x06, 0x98, 0x00, 0x10, 0x00 00058 00059 /*! 00060 * \brief Hard coded IP address. 00061 * 00062 * The IP address and network mask are used in 00063 * the same case and when there is no DHCP server available in your 00064 * LAN. 00065 * 00066 */ 00067 #define MYIP "192.168.192.100" 00068 00069 /*! 00070 * \brief Hard coded IP mask. 00071 */ 00072 #define MYMASK "255.255.255.0" 00073 00074 /*@}*/ 00075 00076 /* 00077 * Some standard C library headers. 00078 */ 00079 #include <string.h> 00080 #include <stdio.h> 00081 #include <io.h> 00082 00083 /* 00084 * Include the proper header file related to our Ethernet hardware. 00085 * Ethernut 1.3 and Charon II use the 10 MBit Realtek RTL8019AS, while 00086 * Ethernut 2 is equipped with the 10/100 MBit LAN91C111, manufactured 00087 * by SMSC. 00088 */ 00089 #ifdef ETHERNUT2 00090 #include <dev/lanc111.h> 00091 #else 00092 #include <dev/nicrtl.h> 00093 #endif 00094 00095 00096 /* 00097 * UROM is a most simple file system to be used for storing web 00098 * contents. 00099 */ 00100 #include <dev/urom.h> 00101 00102 /* 00103 * More Nut/OS header files. 00104 */ 00105 #include <sys/version.h> 00106 #include <sys/confnet.h> 00107 #include <sys/heap.h> 00108 #include <sys/timer.h> 00109 #include <sys/thread.h> 00110 #include <sys/socket.h> 00111 00112 /* 00113 * Basic internet functions. 00114 */ 00115 #include <arpa/inet.h> 00116 00117 /* 00118 * Beside simple TCP/IP routines, Nut/Net offers some higher 00119 * level protocol libraries. We make use of the DHCP client 00120 * and the HTTP helper routines. 00121 */ 00122 #include <pro/dhcp.h> 00123 #include <pro/httpd.h> 00124 00125 00126 #include "webport.h" 00127 00128 /* 00129 * We optionally use an UART for debug output. 00130 */ 00131 #ifdef WP_STATUSOUT 00132 #include <dev/debug.h> 00133 #endif 00134 00135 /* 00136 * For debugging purposes, several routines are available to 00137 * display the thread list, trace TCP packets etc. However, 00138 * this requires to recompile and relink the system with 00139 * NUTDEBUG defined in the compile options. 00140 */ 00141 #ifdef NUTDEBUG 00142 #include <sys/osdebug.h> 00143 #include <net/netdebug.h> 00144 #endif 00145 00146 /*! 00147 * \addtogroup xgWebPort 00148 */ 00149 /*@{*/ 00150 00151 /*! 00152 * \brief CPU reset macro. 00153 * 00154 * Start all over on fatal initialization errors. 00155 * 00156 * \note This function currently works on AVR systems only. On other 00157 * architectures an endless loop will be entered. 00158 */ 00159 #if defined(__AVR_ATmega103__) || defined(__AVR_ATmega128__) 00160 #define JUMP_RESET { asm("cli"); asm("call 0"); } 00161 #else 00162 #define JUMP_RESET for(;;) 00163 #endif 00164 00165 /*! 00166 * \brief Process a single HTTP request. 00167 * 00168 * This routine performs the whole cycle of a HTTP request. 00169 * 00170 * - Creating a socket. 00171 * - Listening on the defined port. 00172 * - Processing the request. 00173 * - Closing the socket. 00174 */ 00175 void Service(void) 00176 { 00177 TCPSOCKET *sock; /* TCP socket pointer. */ 00178 FILE *stream; /* TCP stream. */ 00179 u_long to = 5000; 00180 00181 /* 00182 * Let the Nut/OS library create a TCP socket for us. 00183 */ 00184 if ((sock = NutTcpCreateSocket()) != 0) { 00185 00186 NutTcpSetSockOpt(sock, SO_RCVTIMEO, &to, sizeof(to)); 00187 00188 /* 00189 * Listen on the port defined in webport.h. Typically this is 00190 * port 80. 00191 */ 00192 if (NutTcpAccept(sock, HTTP_PORT) == 0) { 00193 /* 00194 * The Nut/OS memory manager dynamically allocates and 00195 * releases SRAM space. In this application most memory is 00196 * used by incoming telegrams and the socket interface. If 00197 * the available space becomes low, we sleep for 200 00198 * milliseconds and try again. This way we will always leave 00199 * some RAM for more important parts of the system. 00200 * 00201 * The watermark is defined in webport.h. 00202 */ 00203 while (NutHeapAvailable() < LOW_MEM_MARK) { 00204 NutSleep(200); 00205 } 00206 00207 /* 00208 * Create a stream from the socket, so we can use stdio. 00209 */ 00210 if ((stream = _fdopen((int) sock, "r+b")) != 0) { 00211 /* 00212 * Process HTTP request. This routine is part of the 00213 * Nut/OS libraries and greatly simplifies our application 00214 * code. It will automatically read requested files from 00215 * the file system and send them to the browser or 00216 * call the proper CGI routines. 00217 */ 00218 NutHttpProcessRequest(stream); 00219 00220 /* 00221 * Close the stream to release its resources. 00222 */ 00223 fclose(stream); 00224 } 00225 } 00226 00227 /* 00228 * Close the socket. If the client didn't close the connection yet, 00229 * it will now be forced to do so. 00230 */ 00231 NutTcpCloseSocket(sock); 00232 } 00233 } 00234 00235 /*! \fn ServiceThread(void *arg) 00236 * \brief Background thread to process HTTP requests. 00237 * 00238 * This thread calls Service() in an endless loop. It can be started more 00239 * than once to support parallel connections. 00240 */ 00241 THREAD(ServiceThread, arg) 00242 { 00243 /* 00244 * Loop endless for connections. 00245 */ 00246 for (;;) { 00247 Service(); 00248 } 00249 } 00250 00251 /*! 00252 * \brief Main entry of our application. 00253 * 00254 * Nut/OS automatically calls this entry after initialization. 00255 * 00256 * This routine will do all required initialization, start some 00257 * background threads and then process incoming HTTP requests together 00258 * with the concurrently running background threads. 00259 */ 00260 int main(void) 00261 { 00262 u_char i; 00263 00264 /* 00265 * We may optionally use the serial port for debugging output. 00266 */ 00267 #if defined(WP_STATUSOUT) || defined(NUTDEBUG) 00268 u_long baud = 115200; 00269 00270 /* 00271 * All Nut/OS devices must be registered. This serves two purposes. 00272 * First, it will create a reference to the device driver code, so 00273 * this is linked to the application code. Second, it will initialze 00274 * the hardware. 00275 * 00276 * Nut/OS offers several device drivers for RS232 communication. The 00277 * device devDebug0 is prefered for debug output, because it is not 00278 * only simple, small and fast, but can also used to create output 00279 * from within interrupt routines. 00280 */ 00281 NutRegisterDevice(&devDebug0, 0, 0); 00282 00283 /* 00284 * Now, after the device is registered, it is known to the system 00285 * by its name. We can use a standard C library routine to open it 00286 * as our standard output. 00287 */ 00288 freopen(WP_STATUSOUT, "w", stdout); 00289 00290 /* 00291 * Nut/OS uses _ioctl() to set device specific parameters, like 00292 * the baudrate for serial communication. 00293 */ 00294 _ioctl(_fileno(stdout), UART_SETSPEED, &baud); 00295 00296 /* 00297 * Now we can use the devDebug0 device for standard output. Let's 00298 * print a banner including the Nut/OS version we are running. 00299 */ 00300 printf("\n\nNut/OS %s Webport Daemon\n", NutVersionString()); 00301 00302 /* 00303 * If the Nut/OS libraries had been compiled and linked with 00304 * NUTDEBUG defined, we can switch on additional trace output. 00305 */ 00306 #ifdef NUTDEBUG 00307 NutTraceTcp(stdout, 0); 00308 NutTraceOs(stdout, 0); 00309 NutTraceHeap(stdout, 0); 00310 #endif 00311 00312 /* 00313 * During debugging, some delay during startup is quite helpful. 00314 */ 00315 NutSleep(2000); 00316 #endif 00317 00318 /* 00319 * Register the Ethernet controller. Nut/OS supports different 00320 * controllers. We use DEV_ETHER here, which should have been 00321 * defined in one of the include files, which fits our real hardware. 00322 */ 00323 if (NutRegisterDevice(&DEV_ETHER, 0x8300, 5)) { 00324 #ifdef WP_STATUSOUT 00325 puts("Registering NIC failed"); 00326 #endif 00327 } 00328 00329 /* 00330 * LAN configuration using values in non-volatile memory or 00331 * DHCP/ARP. We are ready to wait upto 15 seconds for a 00332 * response from the DHCP server. If it fails, use hardcoded 00333 * values. 00334 * 00335 * The whole process is done in three steps. 00336 * 00337 * 1. Call NutDhcpIfConfig() without specifying a MAC address 00338 * will try read all values from non-volatile memory. If 00339 * there are no values available, it will immediately 00340 * return an error. 00341 * 00342 * 2. If step 1 fails, call NutDhcpIfConfig() with a hard coded 00343 * MAC address. The routine will use this MAC address to query 00344 * a DHCP server for its IP configuration. If it succeeds, the 00345 * MAC address and the values received from DHCP will be 00346 * stored in non-volatile memory and used in step 1 during the 00347 * next reboot. Otherwise the call will return an error. 00348 * 00349 * 3. If step 2 fails, call NutNetIfConfig() with hard coded 00350 * MAC address and IP configuration. This fixed values will 00351 * be stored in non-volatile memory and on the next reboot, 00352 * these values will be used in step 1. 00353 * 00354 * Note, that DHCP is only used, if the network configuration 00355 * stored in non-volatile memory doesn't contain a fixed IP 00356 * address. If a fixed value is used once, Nut/OS will store 00357 * it in non-volatile memory and never use DHCP unless this 00358 * fixed values are removed from volatile memory again. 00359 */ 00360 #ifdef WP_STATUSOUT 00361 printf("\nTry stored configuration..."); 00362 #endif 00363 if (NutDhcpIfConfig("eth0", 0, 10000)) { 00364 u_char mac[] = { MYMAC }; 00365 #ifdef WP_STATUSOUT 00366 printf("failed\nTry DHCP..."); 00367 #endif 00368 if (NutDhcpIfConfig("eth0", mac, 10000)) { 00369 #ifdef WP_STATUSOUT 00370 printf("failed\nUse fixed configuration..."); 00371 #endif 00372 if (NutNetIfConfig("eth0", mac, inet_addr(MYIP), inet_addr(MYMASK))) { 00373 #ifdef WP_STATUSOUT 00374 puts("failed\nWhere is the NIC?"); 00375 #endif 00376 JUMP_RESET; 00377 } 00378 /* If not in a local network, we must also call 00379 NutIpRouteAdd() to configure the routing. */ 00380 } 00381 } 00382 #ifdef WP_STATUSOUT 00383 puts("OK"); 00384 if (confnet.cdn_cip_addr) 00385 printf("IP addr: %s\n", inet_ntoa(confnet.cdn_cip_addr)); 00386 else 00387 printf("IP addr: %s\n", inet_ntoa(confnet.cdn_ip_addr)); 00388 printf("IP mask: %s\n", inet_ntoa(confnet.cdn_ip_mask)); 00389 printf("IP gate: %s\n", inet_ntoa(confnet.cdn_gateway)); 00390 #endif 00391 00392 /* 00393 * Register our device for the file system. If we intend to use 00394 * another file system, we need to register it by calling 00395 * NutRegisterHttpRoot(). If none is registered, the UROM 00396 * file system will be used by default. 00397 */ 00398 NutRegisterDevice(&devUrom, 0, 0); 00399 00400 /* 00401 * Register our CGI routines. This is required to inform the 00402 * HTTP helper routines, which corresponding routine has to 00403 * be called. 00404 */ 00405 if (NutRegisterCgi(PORT_CONTROL_CGI, CpuPortControl)) 00406 JUMP_RESET; 00407 if (NutRegisterCgi(PORT_STATUS_CGI, CpuPortStatus)) 00408 JUMP_RESET; 00409 if (NutRegisterCgi(RELAY_CONTROL_CGI, SpiRelayControl)) 00410 JUMP_RESET; 00411 if (NutRegisterCgi(OPTO_STATUS_CGI, SpiOptoStatus)) 00412 JUMP_RESET; 00413 if (NutRegisterCgi(CHARON_CONTROL_CGI, CharonLedControl)) 00414 JUMP_RESET; 00415 if (NutRegisterCgi(CHARON_STATUS_CGI, CharonSwitchStatus)) 00416 JUMP_RESET; 00417 00418 /* 00419 * Start a defined number of concurrent background threads. The more 00420 * threads we have running, the more connections we can handle 00421 * concurrently. However, each thread occupies some additional RAM. 00422 */ 00423 for (i = 0; i < NUM_HTTP_THREADS - 1; i++) { 00424 /* 00425 * Not really required here, but we give each thread a unique 00426 * name. 00427 */ 00428 char *thname = "httpd0"; 00429 thname[5] = '0' + i; 00430 NutThreadCreate(thname, ServiceThread, 0, HTTP_THREAD_STACK); 00431 } 00432 00433 /* 00434 * Finally we use the main thread too for handling HTTP request. 00435 * There's nothing else we can do. 00436 */ 00437 for (;;) { 00438 Service(); 00439 } 00440 00441 /* 00442 * This point is never reached. Nut/OS applications are started 00443 * once and continue running forever. 00444 */ 00445 } 00446 00447 /*@}*/