1 /* ------------------------------------------------------------------------- */
4 /* A simple tftp client for upgrading ATMEL AT76C510 WiSOC-based APs. */
5 /* Supports ATMEL+INTERSIL boards (1.4x.y firmware) and ATMEL+RFMD boards */
6 /* (0.x.y.z firmware). */
7 /* Modelled around TELLUS (GEMTEK/ATMEL OEM) TFTP client functionality. */
8 /* This program is part of AP-UTILS project (http://ap-utils.polesye.net) */
10 /* Copyright (C) 2004-2005 Jan Rafaj <jr-aputils at cedric dot unob dot cz> */
12 /* Loosely based on a simple tftp client for busybox. */
13 /* Tries to follow RFC1350. */
14 /* Only "octet" mode and "put" method supported. */
18 /* Not implemented: */
19 /* - uploading of OEM (default) settings */
20 /* - uploading of PATCH code */
24 /* bb tftp: Copyright (C) 2001 Magnus Damm <damm@opensource.se> */
26 /* atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca> */
27 /* and Remi Lefebvre <remi@debian.org> */
29 /* utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> */
31 /* This program is free software; you can redistribute it and/or modify */
32 /* it under the terms of the GNU General Public License as published by */
33 /* the Free Software Foundation; either version 2 of the License, or */
34 /* (at your option) any later version. */
36 /* This program is distributed in the hope that it will be useful, */
37 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
38 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
39 /* General Public License for more details. */
41 /* You should have received a copy of the GNU General Public License */
42 /* along with this program; if not, write to the Free Software */
43 /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
45 /* ------------------------------------------------------------------------- */
50 #include <sys/types.h>
51 #include <sys/socket.h>
55 #include <netinet/in.h>
56 #include <arpa/inet.h>
61 /* GNU gettext stuff*/
63 #include <libgnuintl.h>
64 #define _(String) gettext (String)
66 #define _(String) (String)
69 #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
70 #define TFTP_TIMEOUT 2 /* # seconds waiting for single response */
71 #define TFTP_RETRIES 5 /* # retries with TFTP_TIMEOUT */
72 #undef FEATURE_TFTP_DEBUG
74 /* opcodes we support */
80 #ifdef FEATURE_TFTP_DEBUG
84 #define ERR_READFW _("Error while read()ing firmware file")
86 /* known errors server may respond with */
87 static const char *tftp_error_msg[] = {
90 _("Access violation"),
91 _("Disk full or allocation error"),
92 _("Illegal TFTP operation"),
93 _("Unknown transfer ID"),
94 _("File already exists"),
98 int tftp(int ip, const char *remotefile, int localfd, const char *options)
101 struct sockaddr_in to;
102 struct sockaddr_in from;
108 int opcode = TFTP_WRQ;
109 int tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT;
119 char *buf = malloc(tftp_bufsize);
122 printf("Trying to upload firmware to the AP...\n");
124 if (!strcmp(remotefile, "atbrfirm.bin")) {
128 /* remotefile = atsingle.bin */
135 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
142 bind(socketfd, (struct sockaddr *)&to, len);
144 to.sin_family = AF_INET;
145 to.sin_port = htons(port);
146 to.sin_addr.s_addr = ip;
149 timeout = TFTP_RETRIES;
152 /* first create the opcode part */
154 *((unsigned short *) cp) = htons(opcode);
157 if (opcode == TFTP_WRQ) {
158 /* see if the filename fits into buf */
159 len = strlen(remotefile) + 1;
160 if ((cp + len + 1 + 6 + optlen) >= &buf[tftp_bufsize - 1]) {
161 printf(_("Remote-filename too long.\n"));
165 strncpy(cp, remotefile, len);
168 // ATMEL tftp client specific
172 memcpy(cp, "octet", 6);
175 // ATMEL tftp client specific ("upload auth. code")
176 memcpy(cp, options, optlen);
180 if (opcode == TFTP_DATA) {
181 *((unsigned short *) cp) = htons(block_nr);
184 len = read(localfd, cp, tftp_bufsize);
187 sprintf(buf2, _("%sError in read()"), nl);
195 * assure we wont upload more than maxlen bytes
196 * even if total file length > maxlen
198 if (maxlen && (block_nr - 2) * tftp_bufsize + len > maxlen) {
199 len = maxlen - (block_nr - 2) * tftp_bufsize;
203 if (len != tftp_bufsize)
214 #ifdef FEATURE_TFTP_DEBUG
215 printf(_("sending %u bytes\n"), len);
216 for (cp = buf; cp < &buf[len]; cp++)
217 printf("%02x ", *cp & 0xFF);
221 if (sendto(socketfd, buf, len, 0,
222 (struct sockaddr *) &to, sizeof(to)) < 0)
224 sprintf(buf2, _("%sError in sendto()"), nl);
232 memset(&from, 0, sizeof(from));
233 fromlen = sizeof(from);
235 tv.tv_sec = TFTP_TIMEOUT;
239 FD_SET(socketfd, &rfds);
241 switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
243 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
244 (struct sockaddr *) &from, &fromlen);
247 sprintf(buf2, _("%sError in recvfrom()"), nl);
253 if (to.sin_port == htons(port))
254 to.sin_port = from.sin_port;
256 if (to.sin_port == from.sin_port) {
257 #ifndef FEATURE_TFTP_DEBUG
258 if (opcode == TFTP_DATA) {
259 float f = (block_nr - 2) * tftp_bufsize + len;
261 sprintf(buf2, _("\rProgress: uploaded %.0f %%."),
262 f / sb.st_size * 100);
263 write(fileno(stdout), buf2, strlen(buf2));
271 /* assume invalid packet */
272 printf(_("%sMalformed packet received. Aborting.\n"), nl);
281 printf(_("%sTimed out waiting for response from server "
282 "(%i/%i).\n"),nl, TFTP_RETRIES - timeout, TFTP_RETRIES);
288 sprintf(buf2, _("%sError in select()"), nl);
291 } while (timeout && (len >= 0));
296 /* process received packet */
298 opcode = ntohs(*((unsigned short *) buf));
299 tmp = ntohs(*((unsigned short *) &buf[2]));
301 #ifdef FEATURE_TFTP_DEBUG
302 printf(_("Received %d bytes: %04x %04x\n"), len, opcode, tmp);
305 if (opcode == TFTP_ERROR) {
306 sprintf(buf, "code %i", tmp);
307 if (tmp < (sizeof(tftp_error_msg) / sizeof(char *))) {
309 strcat(buf, tftp_error_msg[tmp]);
313 printf(_("%sError: server responded with %s. Aborting.\n"),nl,buf);
317 if (opcode == TFTP_ACK) {
318 if (tmp == (block_nr - 1)) {
320 printf(_("%sFlash programming in progress...\n"), nl);
322 printf(_("Finished successfully.\n"));
335 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
338 void usage(char **argv)
340 printf (_("PLEASE BE _ABSOLUTELY_ SURE TO READ MANPAGE PRIOR USE!!!\n"));
341 printf (_("\nUsage: %s <-l firmware_file.rom> <IP>\n"), argv[0]);
344 int main(int argc, char **argv)
348 char *localfile = NULL;
349 char *remotefile = NULL;
354 static char buf[256];
357 /* locale support init */
358 setlocale(LC_ALL, "");
359 bindtextdomain("ap-utils", LOCALEDIR);
360 textdomain("ap-utils");
363 printf (_("TFTP client for upgrading firmware in ATMEL AT76C510 "
364 "WiSOC-based APs.\n"));
365 printf (_("(C) 2004-2005 Jan Rafaj "
366 "<jr-aputils at cedric dot unob dot cz>\n"));
368 while ((i = getopt(argc, argv, "l:h")) != -1) {
371 localfile = strdup(optarg);
387 * either no mandatory opts, or too few mandatory opts, or more than 1
388 * non-opt arg. specified, or no non-option arg. specified
390 if (optind == 1 || optind < 3 || argc > 4 || optind == argc) {
391 printf(_("Error: invalid arguments given.\n"));
395 /* host = gethostbyname(argv[optind]); */
396 for (cp = argv[optind], i = 0; *cp && (cp = index(cp, '.')); cp++, i++);
397 if (i < 3 || !(inet_aton(argv[optind], &in))) {
398 printf(_("Error: invalid IP address format given.\n"));
401 inet_aton(argv[optind], &in);
403 fd = open(localfile, O_RDONLY, 0644);
405 perror(_("Error while open()ing firmware file"));
410 if (sb.st_size < 94448 || sb.st_size > 524288) {
411 printf(_("Error: invalid firmware file given.\n"));
415 while ((i = read(fd, buf, sizeof(buf))) != 0) {
421 if (i == sizeof(buf))
422 lseek(fd, -13, SEEK_CUR);
425 if (!(memcmp(&buf[i], "ATMEL", 5)))
428 if (!(memcmp(&buf[i], "802.11 AP", 9)))
431 if (!(memcmp(&buf[i], "atbrfirm.bin", 12))) {
433 remotefile = "atbrfirm.bin";
436 if (!(memcmp(&buf[i], "atsingle.bin", 12))) {
438 remotefile = "atsingle.bin";
448 memset(buf, 0, sizeof(buf));
450 #if FEATURE_TFTP_DEBUG
451 printf(_("Firmware file contains:\n"
452 "- string \"ATMEL\": %s\n"
453 "- string \"802.11 AP\": %s\n"
454 "- string \"atbrfirm.bin\": %s\n"
455 "- string \"atsingle.bin\": %s\n"),
456 result & 1 ? YES : NO, result & 2 ? YES : NO,
457 result & 16 ? YES : NO, result & 32 ? YES : NO);
459 if ((result & (1|2)) != (1|2) || (result & (16|32)) == (16|32) ||
460 (result & (16|32)) == 0)
462 printf(_("Error: invalid firmware file given.\n"));
466 lseek(fd, 0, SEEK_SET);
468 cp = strrchr(localfile, '/');
476 "- firmware file: \"%s\"\n"
477 "- name used for upload: \"%s\"\n"),
478 inet_ntoa(in), cp, remotefile);
481 /* Firmware series 1.4x.y - atbrfirm.bin */
483 unsigned short sum = 0;
488 /* compute checksum */
489 while ((i = read(fd, buf, sizeof(buf))) != 0) {
495 /* assure we do not checksum more than maxlen bytes */
496 if ((bufs_read + i) > maxlen)
497 i = maxlen - bufs_read;
501 /* perform iterative checksumming */
503 wp = (unsigned short *)buf;
505 if (sum + *wp > 0xFFFF)
511 /* prevent eventual fread() if file length > maxlen */
512 if (bufs_read == maxlen)
516 lseek (fd, 0, SEEK_SET);
527 printf("- checksum: %x\n", ntohs(sum));
528 } else { /* result & 32 */
529 /* Firmware series 0.x.y.z - atsingle.bin */
531 i = read(fd, buf, 234);
536 if (lseek(fd, 256, SEEK_SET) == -1) {
537 perror(_("Error while lseek()ing in firmware file\n"));
542 result = tftp(in.s_addr, remotefile, fd, buf);