This application requires the PNUT file system, which is not available on Ethernut 1.x or Charon II.
00001 /* 00002 * Copyright (C) 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 00050 #include <stdio.h> 00051 #include <fcntl.h> 00052 #include <io.h> 00053 00054 #include <dev/board.h> 00055 #include <dev/lanc111.h> 00056 #include <dev/debug.h> 00057 #include <dev/pnut.h> 00058 #include <dev/nplmmc.h> 00059 #include <dev/x12rtc.h> 00060 #include <fs/phatfs.h> 00061 00062 #include <sys/confnet.h> 00063 #include <sys/version.h> 00064 #include <sys/heap.h> 00065 #include <sys/thread.h> 00066 #include <sys/socket.h> 00067 00068 #include <arpa/inet.h> 00069 #include <netinet/tcp.h> 00070 #include <net/route.h> 00071 #include <pro/dhcp.h> 00072 #include <pro/ftpd.h> 00073 #include <pro/wins.h> 00074 #include <pro/sntp.h> 00075 00076 /* Determine the compiler. */ 00077 #if defined(__IMAGECRAFT__) 00078 #if defined(__AVR__) 00079 #define CC_STRING "ICCAVR" 00080 #else 00081 #define CC_STRING "ICC" 00082 #endif 00083 #elif defined(__GNUC__) 00084 #if defined(__AVR__) 00085 #define CC_STRING "AVRGCC" 00086 #elif defined(__arm__) 00087 #define CC_STRING "GNUARM" 00088 #else 00089 #define CC_STRING "GCC" 00090 #endif 00091 #else 00092 #define CC_STRING "Compiler unknown" 00093 #endif 00094 00095 #if !defined(ETHERNUT2) && !defined(ETHERNUT3) 00096 #warning Requires Ethernut 2 or 3 00097 #endif 00098 00108 /* 00109 * Device for debug output. 00110 */ 00111 #define DBG_DEVICE devDebug0 00112 00113 /* 00114 * Device name for debug output. 00115 */ 00116 #define DBG_DEVNAME "uart0" 00117 00118 /* 00119 * Baudrate for debug output. 00120 */ 00121 #define DBG_BAUDRATE 115200 00122 00123 /* 00124 * Wether we should use DHCP. 00125 */ 00126 //#define USE_DHCP 00127 00128 /* 00129 * Unique MAC address of the Ethernut Board. 00130 * 00131 * Ignored if EEPROM contains a valid configuration. 00132 */ 00133 #define MY_MAC { 0x00, 0x06, 0x98, 0x30, 0x00, 0x35 } 00134 00135 /* 00136 * Unique IP address of the Ethernut Board. 00137 * 00138 * Ignored if DHCP is used. 00139 */ 00140 #define MY_IPADDR "192.168.192.35" 00141 00142 /* 00143 * IP network mask of the Ethernut Board. 00144 * 00145 * Ignored if DHCP is used. 00146 */ 00147 #define MY_IPMASK "255.255.255.0" 00148 00149 /* 00150 * Gateway IP address for the Ethernut Board. 00151 * 00152 * Ignored if DHCP is used. 00153 */ 00154 #define MY_IPGATE "192.168.192.1" 00155 00156 /* 00157 * NetBIOS name. 00158 * 00159 * Use a symbolic name with Win32 Explorer. 00160 */ 00161 //#define MY_WINSNAME "ETHERNUT" 00162 00163 /* 00164 * FTP port number. 00165 */ 00166 #define FTP_PORTNUM 21 00167 00168 /* 00169 * FTP timeout. 00170 * 00171 * The server will terminate the session, if no new command is received 00172 * within the specified number of milliseconds. 00173 */ 00174 #define FTPD_TIMEOUT 600000 00175 00176 /* 00177 * TCP buffer size. 00178 */ 00179 #define TCPIP_BUFSIZ 5840 00180 00181 /* 00182 * Maximum segment size. 00183 * 00184 * Choose 536 up to 1460. Note, that segment sizes above 536 may result 00185 * in fragmented packets. Remember, that Ethernut doesn't support TCP 00186 * fragmentation. 00187 */ 00188 #define TCPIP_MSS 1460 00189 00190 #if defined(ETHERNUT3) 00191 00192 /* 00193 * Ethernut 3 File system 00194 */ 00195 #define FSDEV devPhat0 00196 #define BLKDEV_NAME "MMC0" 00197 00198 /* 00199 * Block device. 00200 */ 00201 #define BLKDEV devNplMmc0 00202 #define FSDEV_NAME "PHAT0" 00203 00204 #else 00205 00206 /* 00207 * Ethernut 2 File system 00208 */ 00209 #define FSDEV devPnut 00210 #define FSDEV_NAME "PNUT" 00211 00212 #endif 00213 00215 #define MYTZ -1 00216 00218 #define MYTIMED "130.149.17.21" 00219 00220 #ifdef ETHERNUT3 00221 00222 #define X12RTC_DEV 00223 #endif 00224 00225 /* 00226 * FTP service. 00227 * 00228 * This function waits for client connect, processes the FTP request 00229 * and disconnects. Nut/Net doesn't support a server backlog. If one 00230 * client has established a connection, further connect attempts will 00231 * be rejected. 00232 * 00233 * Some FTP clients, like the Win32 Explorer, open more than one 00234 * connection for background processing. So we run this routine by 00235 * several threads. 00236 */ 00237 void FtpService(void) 00238 { 00239 TCPSOCKET *sock; 00240 00241 /* 00242 * Create a socket. 00243 */ 00244 if ((sock = NutTcpCreateSocket()) != 0) { 00245 00246 /* 00247 * Set specified socket options. 00248 */ 00249 #ifdef TCPIP_MSS 00250 { 00251 u_short mss = TCPIP_MSS; 00252 NutTcpSetSockOpt(sock, TCP_MAXSEG, &mss, sizeof(mss)); 00253 } 00254 #endif 00255 #ifdef FTPD_TIMEOUT 00256 { 00257 u_long tmo = FTPD_TIMEOUT; 00258 NutTcpSetSockOpt(sock, SO_RCVTIMEO, &tmo, sizeof(tmo)); 00259 } 00260 #endif 00261 #ifdef TCPIP_BUFSIZ 00262 { 00263 u_short siz = TCPIP_BUFSIZ; 00264 NutTcpSetSockOpt(sock, SO_RCVBUF, &siz, sizeof(siz)); 00265 } 00266 #endif 00267 00268 /* 00269 * Listen on our port. If we return, we got a client. 00270 */ 00271 printf("\nWaiting for an FTP client..."); 00272 if (NutTcpAccept(sock, FTP_PORTNUM) == 0) { 00273 printf("%s connected, %u bytes free\n", inet_ntoa(sock->so_remote_addr), (u_int)NutHeapAvailable()); 00274 NutFtpServerSession(sock); 00275 printf("%s disconnected, %u bytes free\n", inet_ntoa(sock->so_remote_addr), (u_int)NutHeapAvailable()); 00276 } else { 00277 puts("Accept failed"); 00278 } 00279 00280 /* 00281 * Close our socket. 00282 */ 00283 NutTcpCloseSocket(sock); 00284 } 00285 } 00286 00287 /* 00288 * FTP service thread. 00289 */ 00290 THREAD(FtpThread, arg) 00291 { 00292 /* Loop endless for connections. */ 00293 for (;;) { 00294 FtpService(); 00295 } 00296 } 00297 00298 /* 00299 * Assign stdout to the UART device. 00300 */ 00301 void InitDebugDevice(void) 00302 { 00303 u_long baud = DBG_BAUDRATE; 00304 00305 NutRegisterDevice(&DBG_DEVICE, 0, 0); 00306 freopen(DBG_DEVNAME, "w", stdout); 00307 _ioctl(_fileno(stdout), UART_SETSPEED, &baud); 00308 } 00309 00310 /* 00311 * Setup the ethernet device. Try DHCP first. If this is 00312 * the first time boot with empty EEPROM and no DHCP server 00313 * was found, use hardcoded values. 00314 */ 00315 int InitEthernetDevice(void) 00316 { 00317 u_long ip_addr = inet_addr(MY_IPADDR); 00318 u_long ip_mask = inet_addr(MY_IPMASK); 00319 u_long ip_gate = inet_addr(MY_IPGATE); 00320 u_char mac[6] = MY_MAC; 00321 00322 if (NutRegisterDevice(&DEV_ETHER, 0x8300, 5)) { 00323 puts("No Ethernet Device"); 00324 return -1; 00325 } 00326 00327 printf("Configure %s...", DEV_ETHER_NAME); 00328 #ifdef USE_DHCP 00329 if (NutDhcpIfConfig(DEV_ETHER_NAME, 0, 60000) == 0) { 00330 puts("OK"); 00331 return 0; 00332 } 00333 printf("initial boot..."); 00334 if (NutDhcpIfConfig(DEV_ETHER_NAME, mac, 60000) == 0) { 00335 puts("OK"); 00336 return 0; 00337 } 00338 #endif 00339 printf("No DHCP..."); 00340 NutNetIfConfig(DEV_ETHER_NAME, mac, ip_addr, ip_mask); 00341 /* Without DHCP we had to set the default gateway manually.*/ 00342 if(ip_gate) { 00343 printf("hard coded gate..."); 00344 NutIpRouteAdd(0, 0, ip_gate, &DEV_ETHER); 00345 } 00346 puts("OK"); 00347 00348 return 0; 00349 } 00350 00351 /* 00352 * Query a time server and optionally update the hardware clock. 00353 */ 00354 static int QueryTimeServer(void) 00355 { 00356 int rc = -1; 00357 00358 #ifdef MYTIMED 00359 { 00360 time_t now; 00361 u_long timeserver = inet_addr(MYTIMED); 00362 00363 /* Query network time service and set the system time. */ 00364 printf("Query time from %s...", MYTIMED); 00365 if(NutSNTPGetTime(×erver, &now) == 0) { 00366 puts("OK"); 00367 rc = 0; 00368 stime(&now); 00369 #ifdef X12RTC_DEV 00370 /* If RTC hardware is available, update it. */ 00371 { 00372 struct _tm *gmt = gmtime(&now); 00373 00374 if (X12RtcSetClock(gmt)) { 00375 puts("RTC update failed"); 00376 } 00377 } 00378 #endif 00379 } 00380 else { 00381 puts("failed"); 00382 } 00383 } 00384 #endif 00385 00386 return rc; 00387 } 00388 00389 /* 00390 * Try to get initial date and time from the hardware clock or a time server. 00391 */ 00392 static int InitTimeAndDate(void) 00393 { 00394 int rc = -1; 00395 00396 /* Set the local time zone. */ 00397 _timezone = MYTZ * 60L * 60L; 00398 00399 #ifdef X12RTC_DEV 00400 /* Query RTC hardware if available. */ 00401 { 00402 u_long rs; 00403 00404 /* Query the status. If it fails, we do not have an RTC. */ 00405 if (X12RtcGetStatus(&rs)) { 00406 puts("No hardware RTC"); 00407 rc = QueryTimeServer(); 00408 } 00409 else { 00410 /* RTC hardware seems to be available. Check for power failure. */ 00411 //rs = RTC_STATUS_PF; 00412 if ((rs & RTC_STATUS_PF) == RTC_STATUS_PF) { 00413 puts("RTC power fail detected"); 00414 rc = QueryTimeServer(); 00415 } 00416 00417 /* RTC hardware status is fine, update our system clock. */ 00418 else { 00419 struct _tm gmt; 00420 00421 /* Assume that RTC is running at GMT. */ 00422 if (X12RtcGetClock(&gmt) == 0) { 00423 time_t now = _mkgmtime(&gmt); 00424 00425 if (now != -1) { 00426 stime(&now); 00427 rc = 0; 00428 } 00429 } 00430 } 00431 } 00432 } 00433 #else 00434 /* No hardware RTC, query the time server if available. */ 00435 rc = QueryTimeServer(); 00436 #endif 00437 return rc; 00438 } 00439 00440 /* 00441 * Main application routine. 00442 * 00443 * Nut/OS automatically calls this entry after initialization. 00444 */ 00445 int main(void) 00446 { 00447 int volid; 00448 u_long ipgate; 00449 00450 /* Initialize a debug output device and print a banner. */ 00451 InitDebugDevice(); 00452 printf("\n\nFTP Server Sample - Nut/OS %s - " CC_STRING "\n", NutVersionString()); 00453 00454 /* Initialize the Ethernet device and print our IP address. */ 00455 if (InitEthernetDevice()) { 00456 for(;;); 00457 } 00458 printf("IP Addr: %s\n", inet_ntoa(confnet.cdn_ip_addr)); 00459 printf("IP Mask: %s\n", inet_ntoa(confnet.cdn_ip_mask)); 00460 NutIpRouteQuery(0, &ipgate); 00461 printf("IP Gate: %s\n", inet_ntoa(ipgate)); 00462 00463 /* Initialize system clock and calendar. */ 00464 if (InitTimeAndDate() == 0) { 00465 time_t now = time(0); 00466 struct _tm *lot = localtime(&now); 00467 printf("Date: %02u.%02u.%u\n", lot->tm_mday, lot->tm_mon + 1, 1900 + lot->tm_year); 00468 printf("Time: %02u:%02u:%02u\n", lot->tm_hour, lot->tm_min, lot->tm_sec); 00469 } 00470 00471 /* Initialize the file system. */ 00472 printf("Register file system..."); 00473 if (NutRegisterDevice(&FSDEV, 0, 0)) { 00474 puts("failed"); 00475 for (;;); 00476 } 00477 puts("OK"); 00478 00479 #ifdef BLKDEV 00480 /* Register block device. */ 00481 printf("Register block device..."); 00482 if (NutRegisterDevice(&BLKDEV, 0, 0)) { 00483 puts("failed"); 00484 for (;;); 00485 } 00486 puts("OK"); 00487 00488 /* Mount partition. */ 00489 printf("Mounting partition..."); 00490 if ((volid = _open(BLKDEV_NAME ":1/" FSDEV_NAME, _O_RDWR | _O_BINARY)) == -1) { 00491 puts("failed"); 00492 for (;;); 00493 } 00494 puts("OK"); 00495 #endif 00496 00497 /* Register root path. */ 00498 printf("Register FTP root..."); 00499 if (NutRegisterFtpRoot(FSDEV_NAME ":")) { 00500 puts("failed"); 00501 for (;;); 00502 } 00503 puts("OK"); 00504 00505 /* Start two additional server threads. */ 00506 NutThreadCreate("ftpd0", FtpThread, 0, 1640); 00507 NutThreadCreate("ftpd1", FtpThread, 0, 1640); 00508 00509 /* Main server thread. */ 00510 for (;;) { 00511 #ifdef MY_WINSNAME 00512 NutWinsNameQuery(MY_WINSNAME, confnet.cdn_ip_addr); 00513 #endif 00514 FtpService(); 00515 } 00516 }