/* * Copyright (C) 2008 by Thermotemp GmbH. All rights reserved. * Copyright (C) 2002-2007 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/ */ #include #include #include #include #include "default.h" #include "debug.h" #include "tftp.h" tftp_header tx_frame; tftp_header rx_frame; int tftp_receive(char *filename, u_long tftp_ip, int (*callback)(u_char *buffer, u_short block_size, u_long offset, void* user_data), void* user_data) { unsigned char retry; int len = 0; int rx_len = 0; int tx_len = 0; unsigned short block = 0; char *cp; UDPSOCKET *tftp_socket; u_long remote_ip; u_short remote_port = TFTP_PORT; u_long offset = 0; int total_bytes = 0; /* * Do nothing if there's no TFTP host configured. */ tftp_socket = NutUdpCreateSocket(TFTP_PORT); if (tftp_socket == 0) { ERROR("Could not create TFTP UDP socket\r\n"); goto error; } /* * Prepare the transmit buffer for a file request. */ tx_frame.th_opcode = htons(TFTP_RRQ); tx_len = 2; cp = tx_frame.th_u.tu_stuff; INFO("TFTP Loading %s\r\n", filename); strcpy(cp, filename); len = strlen(filename) + 1; tx_len += len; cp += len; strcpy(cp, "octet"); tx_len += 6; /* * Loop until we receive a packet with less than 512 bytes of data. */ do { /* * Send file request or acknowledge and receive * a data block. Maximum 3 retries on timeout or error */ for (retry = 0; retry < 3; retry++) { if (NutUdpSendTo(tftp_socket, tftp_ip, remote_port, &tx_frame, tx_len) == 0) { rx_len = NutUdpReceiveFrom(tftp_socket, &remote_ip, &remote_port, &rx_frame, sizeof(tftp_header), 5000); if (rx_len >= 4) break; } } /* * Can't reach the TFTP server or got a malformed * repsonse. */ if (retry >= 3 || rx_len < 4) { goto error; } /* * Accept data blocks only. Anything else will stop * the transfer with an error. */ if (ntohs(rx_frame.th_opcode) != TFTP_DATA) { goto error; } /* * If this was the first block we received, prepare * the send buffer for sending ACKs. */ if (block == 0) { tx_frame.th_opcode = htons(TFTP_ACK); tx_len = 4; } /* * If this block is out of sequence, we ignore it. * However, if we missed the first block, return * with an error. */ if (ntohs(rx_frame.th_u.tu_block) != block + 1) { if (block == 0) { goto error; } continue; } /* * Burn the received data into the flash ROM. */ if (rx_len > 4) { //INFO("TFTP received block %d\r\n", block); INFO("."); callback(rx_frame.th_data, rx_len-4, offset, user_data); total_bytes += rx_len-4; offset += MIN(rx_len - 4, 512); } /* * Update our block counter. */ block++; tx_frame.th_u.tu_block = htons(block); } while (rx_len >= 516); /* * Send the last ACK. */ NutUdpSendTo(tftp_socket, tftp_ip, remote_port, &tx_frame, tx_len); return total_bytes; error: return -1; }