]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/redboot/v2_0/src/net/tftp_client.c
TX51 pre-release
[karo-tx-redboot.git] / packages / redboot / v2_0 / src / net / tftp_client.c
1 //==========================================================================
2 //
3 //      net/tftp_client.c
4 //
5 //      Stand-alone TFTP support for RedBoot
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 // Copyright (C) 2002, 2003 Gary Thomas
13 //
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
17 //
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21 // for more details.
22 //
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 //
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
33 //
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
36 //
37 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38 // at http://sources.redhat.com/ecos/ecos-license/
39 // -------------------------------------------
40 //####ECOSGPLCOPYRIGHTEND####
41 //==========================================================================
42 //#####DESCRIPTIONBEGIN####
43 //
44 // Author(s):    gthomas
45 // Contributors: gthomas
46 // Date:         2000-07-14
47 // Purpose:
48 // Description:
49 //
50 // This code is part of RedBoot (tm).
51 //
52 //####DESCRIPTIONEND####
53 //
54 //==========================================================================
55
56 // TFTP client support
57
58 #include <redboot.h>     // have_net
59 #include <net/net.h>
60 #include <net/tftp.h>
61 #include <net/tftp_support.h>
62
63 // So we remember which ports have been used
64 static int get_port = 7700;
65
66 static struct {
67         bool open;
68         int      total_timeouts, packets_received;
69         unsigned long last_good_block;
70         int      avail, actual_len;
71         struct sockaddr_in local_addr, from_addr;
72         union {
73                 char buf[SEGSIZE + sizeof(struct tftphdr)];
74                 struct tftphdr hdr;
75         } data;
76         char *bufp;
77 } tftp_stream;
78
79 int
80 tftp_stream_open(connection_info_t *info,
81                                  int *err)
82 {
83         struct tftphdr *hdr = &tftp_stream.data.hdr;
84         char *cp, *fp;
85         char test_buf;
86
87         if (!have_net || tftp_stream.open) {
88                 *err = TFTP_INVALID;  // Already open
89                 return -1;
90         }
91
92         // Create initial request
93         hdr->th_opcode = htons(RRQ);  // Read file
94         cp = (char *)&hdr->th_stuff;
95         fp = info->filename;
96         while (*fp) *cp++ = *fp++;
97         *cp++ = '\0';
98         // Since this is used for downloading data, OCTET (binary) is the
99         // only mode that makes sense.
100         fp = "OCTET";
101         while (*fp) *cp++ = *fp++;
102         *cp++ = '\0';
103
104         memset(&tftp_stream.local_addr, 0, sizeof(tftp_stream.local_addr));
105         tftp_stream.local_addr.sin_family = AF_INET;
106         tftp_stream.local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
107         tftp_stream.local_addr.sin_port = htons(get_port++);
108
109         if (info->server->sin_port == 0) {
110                 info->server->sin_port = htons(TFTP_PORT);
111         } else {
112                 info->server->sin_port = htons(info->server->sin_port);
113         }
114
115         // Send request - note: RFC 1350 (TFTP rev 2) indicates that this should be
116         // only as long as required to hold the request, with the nul terminator.
117         // Some servers silently go to lunch if the request is not the correct size.
118         if (__udp_sendto(tftp_stream.data.buf, cp-(char *)hdr,
119                                          info->server, &tftp_stream.local_addr) < 0) {
120                 // Problem sending request
121                 *err = TFTP_NETERR;
122                 return -1;
123         }
124
125         tftp_stream.open = true;
126         tftp_stream.avail = 0;
127         tftp_stream.actual_len = -1;
128         tftp_stream.last_good_block = 0;
129         tftp_stream.total_timeouts = 0;
130         tftp_stream.from_addr.sin_port = 0;
131         tftp_stream.packets_received = 0;
132
133         // Try and read the first byte [block] since no errors are
134         // reported until then.
135         if (tftp_stream_read(&test_buf, 1, err) == 1) {
136                 // Back up [rewind] over this datum
137                 tftp_stream.bufp--;
138                 tftp_stream.avail++;
139                 return 0;  // Open and first read successful
140         } else {
141                 tftp_stream.open = false;
142                 return -1; // Couldn't read
143         }
144 }
145
146 static int
147 tftp_ack(int *err)
148 {
149         struct tftphdr *hdr = &tftp_stream.data.hdr;
150         // ACK last packet so server can shut down
151         if (tftp_stream.packets_received > 0) {
152                 hdr->th_opcode = htons(ACK);
153                 hdr->th_block = htons((cyg_uint16)tftp_stream.last_good_block & 0xFFFF);
154                 if (__udp_sendto(tftp_stream.data.buf, 4 /* FIXME */,
155                                                         &tftp_stream.from_addr, &tftp_stream.local_addr) < 0) {
156                         // Problem sending ACK
157                         *err = TFTP_NETERR;
158                         return -1;
159                 }
160         }
161         return 0;
162 }
163
164 static int
165 tftp_error_ack(int *err, short code, char *msg)
166 {
167         struct tftphdr *hdr = &tftp_stream.data.hdr;
168
169         if (strlen(msg) > (SEGSIZE - 1)) {
170                 *(msg + SEGSIZE) = '\0';
171         }
172
173         if (tftp_stream.packets_received > 0) {
174                 hdr->th_opcode = htons(ERROR);
175                 hdr->th_code = code;
176                 strcpy((char *)&hdr->th_data, msg);
177                 if (__udp_sendto(tftp_stream.data.buf, (5 + strlen(msg)),
178                                                         &tftp_stream.from_addr, &tftp_stream.local_addr) < 0) {
179                         // Problem sending ACK
180                         *err = TFTP_NETERR;
181                         return -1;
182                 }
183         }
184         return 0;
185 }
186
187 void
188 tftp_stream_close(int *err)
189 {
190         if (tftp_stream.open == true) {
191                 tftp_ack(err);
192                 tftp_stream.open = false;
193         }
194 }
195
196 void
197 tftp_stream_terminate(bool abort,
198                                         int (*getc)(void))
199 {
200         int err;
201
202         if (abort)
203                 tftp_error_ack(&err, EUNDEF, "redboot tftp_stream_terminate");
204         else
205                 tftp_ack(&err);
206
207         tftp_stream.open = false;
208 }
209
210 int
211 tftp_stream_read(void *buf,
212                                 int len,
213                                 int *err)
214 {
215         int total_bytes = 0;
216         int size, recv_len, data_len;
217         struct timeval timeout;
218         struct tftphdr *hdr = &tftp_stream.data.hdr;
219
220         while (total_bytes < len) {
221                 // Move any bytes which we've already read/buffered
222                 if (tftp_stream.avail > 0) {
223                         size = tftp_stream.avail;
224                         if (size > (len - total_bytes)) size = len - total_bytes;
225                         memcpy(buf, tftp_stream.bufp, size);
226                         buf = (char *)buf + size;
227                         tftp_stream.bufp += size;
228                         tftp_stream.avail -= size;
229                         total_bytes += size;
230                 } else {
231                         if (tftp_ack(err) < 0) {
232                                 return -1;
233                         }
234                         if ((tftp_stream.actual_len >= 0) && (tftp_stream.actual_len < SEGSIZE)) {
235                                 // Out of data
236                                 break;
237                         }
238                         timeout.tv_sec = (tftp_stream.last_good_block == 0) ? 10*TFTP_TIMEOUT_PERIOD : TFTP_TIMEOUT_PERIOD;
239                         timeout.tv_usec = 0;
240                         recv_len = sizeof(tftp_stream.data.buf);
241                         if ((data_len = __udp_recvfrom(&tftp_stream.data.buf[0], recv_len, &tftp_stream.from_addr,
242                                                                                                 &tftp_stream.local_addr,  &timeout)) < 0) {
243                                 // No data, try again
244                                 diag_printf("TFTP timed out %d/%d\n", tftp_stream.total_timeouts+1, TFTP_TIMEOUT_MAX);
245                                 if ((++tftp_stream.total_timeouts > TFTP_TIMEOUT_MAX) ||
246                                         (tftp_stream.last_good_block == 0)) {
247                                         // Timeout - no data received
248                                         *err = TFTP_TIMEOUT;
249                                         return -1;
250                                 }
251                                 // Send out the ACK for the last block - maybe server will retry
252                                 if (tftp_ack(err) < 0) {
253                                         return -1;
254                                 }
255                         } else {
256                                 tftp_stream.packets_received++;
257                                 if (ntohs(hdr->th_opcode) == DATA) {
258                                         if (ntohs(hdr->th_block) == (cyg_uint16)((tftp_stream.last_good_block+1) & 0xFFFF)) {
259                                                 // Consume this data
260                                                 data_len -= 4;  /* Sizeof TFTP header */
261                                                 tftp_stream.avail = tftp_stream.actual_len = data_len;
262                                                 tftp_stream.bufp = hdr->th_data;
263                                                 tftp_stream.last_good_block++;
264                                         }
265                                 } else {
266                                         if (ntohs(hdr->th_opcode) == ERROR) {
267                                                 *err = ntohs(hdr->th_code);
268                                                 return -1;
269                                         } else {
270                                                 // What kind of packet is this?
271                                                 *err = TFTP_PROTOCOL;
272                                                 return -1;
273                                         }
274                                 }
275                         }
276                 }
277         }
278         return total_bytes;
279 }
280
281 char *
282 tftp_error(int err)
283 {
284         char *errmsg = "Unknown error";
285
286         switch (err) {
287         case TFTP_ENOTFOUND:
288                 return "file not found";
289         case TFTP_EACCESS:
290                 return "access violation";
291         case TFTP_ENOSPACE:
292                 return "disk full or allocation exceeded";
293         case TFTP_EBADOP:
294                 return "illegal TFTP operation";
295         case TFTP_EBADID:
296                 return "unknown transfer ID";
297         case TFTP_EEXISTS:
298                 return "file already exists";
299         case TFTP_ENOUSER:
300                 return "no such user";
301         case TFTP_TIMEOUT:
302                 return "operation timed out";
303         case TFTP_NETERR:
304                 return "some sort of network error";
305         case TFTP_INVALID:
306                 return "invalid parameter";
307         case TFTP_PROTOCOL:
308                 return "protocol violation";
309         case TFTP_TOOLARGE:
310                 return "file is larger than buffer";
311         }
312         return errmsg;
313 }
314
315 //
316 // RedBoot interface
317 //
318 GETC_IO_FUNCS(tftp_io, tftp_stream_open, tftp_stream_close,
319               tftp_stream_terminate, tftp_stream_read, tftp_error);
320 RedBoot_load(tftp, tftp_io, true, true, 0);
321