--- /dev/null
+/* ------------------------------------------------------------------------- */
+/* ap-tftp.c */
+/* */
+/* A simple tftp client for upgrading ATMEL AT76C510 WiSOC-based APs. */
+/* Supports ATMEL+INTERSIL boards (1.4x.y firmware) and ATMEL+RFMD boards */
+/* (0.x.y.z firmware). */
+/* Modelled around TELLUS (GEMTEK/ATMEL OEM) TFTP client functionality. */
+/* This program is part of AP-UTILS project (http://ap-utils.polesye.net) */
+/* */
+/* Copyright (C) 2004-2005 Jan Rafaj <jr-aputils at cedric dot unob dot cz> */
+/* */
+/* Loosely based on a simple tftp client for busybox. */
+/* Tries to follow RFC1350. */
+/* Only "octet" mode and "put" method supported. */
+/* */
+/* Version: 1.0 */
+/* */
+/* Not implemented: */
+/* - uploading of OEM (default) settings */
+/* - uploading of PATCH code */
+/* */
+/* Code based on: */
+/* */
+/* bb tftp: Copyright (C) 2001 Magnus Damm <damm@opensource.se> */
+/* */
+/* atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca> */
+/* and Remi Lefebvre <remi@debian.org> */
+/* */
+/* utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> */
+/* */
+/* This program is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/* */
+/* ------------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef HAVE_GETTEXT
+/* GNU gettext stuff*/
+#include <locale.h>
+#include <libgnuintl.h>
+#define _(String) gettext (String)
+#else
+#define _(String) (String)
+#endif
+
+#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
+#define TFTP_TIMEOUT 2 /* # seconds waiting for single response */
+#define TFTP_RETRIES 5 /* # retries with TFTP_TIMEOUT */
+#undef FEATURE_TFTP_DEBUG
+
+/* opcodes we support */
+#define TFTP_WRQ 2
+#define TFTP_DATA 3
+#define TFTP_ACK 4
+#define TFTP_ERROR 5
+
+#ifdef FEATURE_TFTP_DEBUG
+#define YES _("yes")
+#define NO _("no")
+#endif
+#define ERR_READFW _("Error while read()ing firmware file")
+
+/* known errors server may respond with */
+static const char *tftp_error_msg[] = {
+ _("Undefined error"),
+ _("File not found"),
+ _("Access violation"),
+ _("Disk full or allocation error"),
+ _("Illegal TFTP operation"),
+ _("Unknown transfer ID"),
+ _("File already exists"),
+ _("No such user")
+};
+
+int tftp(int ip, const char *remotefile, int localfd, const char *options)
+{
+ const int port = 69;
+ struct sockaddr_in to;
+ struct sockaddr_in from;
+ struct timeval tv;
+ struct stat sb;
+ fd_set rfds;
+ unsigned short tmp;
+ int fromlen;
+ int opcode = TFTP_WRQ;
+ int tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT;
+ int timeout;
+ int block_nr = 1;
+ int finished = 0;
+ int socketfd;
+ int len;
+ int maxlen;
+ int optlen;
+ char *nl = "";
+ char *cp;
+ char *buf = malloc(tftp_bufsize);
+ char buf2[64];
+
+ printf("Trying to upload firmware to the AP...\n");
+
+ if (!strcmp(remotefile, "atbrfirm.bin")) {
+ optlen = 8;
+ maxlen = 94448;
+ } else {
+ /* remotefile = atsingle.bin */
+ optlen = 234;
+ maxlen = 0;
+ }
+
+ fstat(localfd, &sb);
+
+ if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket()");
+ return EXIT_FAILURE;
+ }
+
+ len = sizeof(to);
+ memset(&to, 0, len);
+ bind(socketfd, (struct sockaddr *)&to, len);
+
+ to.sin_family = AF_INET;
+ to.sin_port = htons(port);
+ to.sin_addr.s_addr = ip;
+
+ while (1) {
+ timeout = TFTP_RETRIES;
+ cp = buf;
+
+ /* first create the opcode part */
+
+ *((unsigned short *) cp) = htons(opcode);
+ cp += 2;
+
+ if (opcode == TFTP_WRQ) {
+ /* see if the filename fits into buf */
+ len = strlen(remotefile) + 1;
+ if ((cp + len + 1 + 6 + optlen) >= &buf[tftp_bufsize - 1]) {
+ printf(_("Remote-filename too long.\n"));
+ break;
+ }
+
+ strncpy(cp, remotefile, len);
+ cp += len;
+
+ // ATMEL tftp client specific
+ *cp = 1;
+ cp++;
+
+ memcpy(cp, "octet", 6);
+ cp += 6;
+
+ // ATMEL tftp client specific ("upload auth. code")
+ memcpy(cp, options, optlen);
+ cp += optlen;
+ }
+
+ if (opcode == TFTP_DATA) {
+ *((unsigned short *) cp) = htons(block_nr);
+ cp += 2;
+
+ len = read(localfd, cp, tftp_bufsize);
+
+ if (len < 0) {
+ sprintf(buf2, _("%sError in read()"), nl);
+ perror(buf2);
+ break;
+ }
+
+ block_nr++;
+
+ /*
+ * assure we wont upload more than maxlen bytes
+ * even if total file length > maxlen
+ */
+ if (maxlen && (block_nr - 2) * tftp_bufsize + len > maxlen) {
+ len = maxlen - (block_nr - 2) * tftp_bufsize;
+ cp[len] = '\0';
+ }
+
+ if (len != tftp_bufsize)
+ finished++;
+
+ cp += len;
+ }
+
+ /* send packet */
+
+ do {
+ len = cp - buf;
+
+#ifdef FEATURE_TFTP_DEBUG
+ printf(_("sending %u bytes\n"), len);
+ for (cp = buf; cp < &buf[len]; cp++)
+ printf("%02x ", *cp & 0xFF);
+
+ printf("\n");
+#endif
+ if (sendto(socketfd, buf, len, 0,
+ (struct sockaddr *) &to, sizeof(to)) < 0)
+ {
+ sprintf(buf2, _("%sError in sendto()"), nl);
+ perror(buf2);
+ len = -1;
+ break;
+ }
+
+ /* receive packet */
+
+ memset(&from, 0, sizeof(from));
+ fromlen = sizeof(from);
+
+ tv.tv_sec = TFTP_TIMEOUT;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&rfds);
+ FD_SET(socketfd, &rfds);
+
+ switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
+ case 1:
+ len = recvfrom(socketfd, buf, tftp_bufsize, 0,
+ (struct sockaddr *) &from, &fromlen);
+
+ if (len < 0) {
+ sprintf(buf2, _("%sError in recvfrom()"), nl);
+ perror(buf2);
+ break;
+ }
+
+ /*
+ if (to.sin_port == htons(port))
+ to.sin_port = from.sin_port;
+ */
+ if (to.sin_port == from.sin_port) {
+#ifndef FEATURE_TFTP_DEBUG
+ if (opcode == TFTP_DATA) {
+ float f = (block_nr - 2) * tftp_bufsize + len;
+
+ sprintf(buf2, _("\rProgress: uploaded %.0f %%."),
+ f / sb.st_size * 100);
+ write(fileno(stdout), buf2, strlen(buf2));
+ nl = "\n";
+ }
+#endif
+ timeout = 0;
+ break;
+ }
+
+ /* assume invalid packet */
+ printf(_("%sMalformed packet received. Aborting.\n"), nl);
+ len = -1;
+ break;
+
+ case 0:
+ timeout--;
+ if (timeout == 0)
+ len = -1;
+
+ printf(_("%sTimed out waiting for response from server "
+ "(%i/%i).\n"),nl, TFTP_RETRIES - timeout, TFTP_RETRIES);
+ nl = "";
+ break;
+
+ default:
+ len = -1;
+ sprintf(buf2, _("%sError in select()"), nl);
+ perror(buf2);
+ }
+ } while (timeout && (len >= 0));
+
+ if (len < 0)
+ break;
+
+ /* process received packet */
+
+ opcode = ntohs(*((unsigned short *) buf));
+ tmp = ntohs(*((unsigned short *) &buf[2]));
+
+#ifdef FEATURE_TFTP_DEBUG
+ printf(_("Received %d bytes: %04x %04x\n"), len, opcode, tmp);
+#endif
+
+ if (opcode == TFTP_ERROR) {
+ sprintf(buf, "code %i", tmp);
+ if (tmp < (sizeof(tftp_error_msg) / sizeof(char *))) {
+ strcat(buf, " (");
+ strcat(buf, tftp_error_msg[tmp]);
+ strcat(buf, ")");
+ }
+
+ printf(_("%sError: server responded with %s. Aborting.\n"),nl,buf);
+ break;
+ }
+
+ if (opcode == TFTP_ACK) {
+ if (tmp == (block_nr - 1)) {
+ if (finished) {
+ printf(_("%sFlash programming in progress...\n"), nl);
+ sleep(5);
+ printf(_("Finished successfully.\n"));
+ break;
+ }
+
+ opcode = TFTP_DATA;
+ continue;
+ }
+ }
+ }
+
+ close(socketfd);
+ free(buf);
+
+ return finished ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+void usage(char **argv)
+{
+ printf (_("PLEASE BE _ABSOLUTELY_ SURE TO READ MANPAGE PRIOR USE!!!\n"));
+ printf (_("\nUsage: %s <-l firmware_file.rom> <IP>\n"), argv[0]);
+}
+
+int main(int argc, char **argv)
+{
+ struct in_addr in;
+ struct stat sb;
+ char *localfile = NULL;
+ char *remotefile = NULL;
+ char *cp;
+ int fd;
+ int result = 0;
+ int i;
+ static char buf[256];
+
+#ifdef HAVE_GETTEXT
+ /* locale support init */
+ setlocale(LC_ALL, "");
+ bindtextdomain("ap-utils", LOCALEDIR);
+ textdomain("ap-utils");
+#endif
+
+ printf (_("TFTP client for upgrading firmware in ATMEL AT76C510 "
+ "WiSOC-based APs.\n"));
+ printf (_("(C) 2004-2005 Jan Rafaj "
+ "<jr-aputils at cedric dot unob dot cz>\n"));
+
+ while ((i = getopt(argc, argv, "l:h")) != -1) {
+ switch (i) {
+ case 'l':
+ localfile = strdup(optarg);
+ break;
+ case 'h':
+ usage(argv);
+ return EXIT_SUCCESS;
+ default:
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (argc == 1) {
+ usage(argv);
+ return EXIT_SUCCESS;
+ }
+
+ /*
+ * either no mandatory opts, or too few mandatory opts, or more than 1
+ * non-opt arg. specified, or no non-option arg. specified
+ */
+ if (optind == 1 || optind < 3 || argc > 4 || optind == argc) {
+ printf(_("Error: invalid arguments given.\n"));
+ return EXIT_FAILURE;
+ }
+
+ /* host = gethostbyname(argv[optind]); */
+ for (cp = argv[optind], i = 0; *cp && (cp = index(cp, '.')); cp++, i++);
+ if (i < 3 || !(inet_aton(argv[optind], &in))) {
+ printf(_("Error: invalid IP address format given.\n"));
+ return EXIT_FAILURE;
+ }
+ inet_aton(argv[optind], &in);
+
+ fd = open(localfile, O_RDONLY, 0644);
+ if (fd < 0) {
+ perror(_("Error while open()ing firmware file"));
+ return EXIT_FAILURE;
+ }
+
+ fstat(fd, &sb);
+ if (sb.st_size < 94448 || sb.st_size > 524288) {
+ printf(_("Error: invalid firmware file given.\n"));
+ return EXIT_FAILURE;
+ }
+
+ while ((i = read(fd, buf, sizeof(buf))) != 0) {
+ if (i < 0) {
+ perror(ERR_READFW);
+ return EXIT_FAILURE;
+ }
+
+ if (i == sizeof(buf))
+ lseek(fd, -13, SEEK_CUR);
+
+ while (i--) {
+ if (!(memcmp(&buf[i], "ATMEL", 5)))
+ result |= 1;
+
+ if (!(memcmp(&buf[i], "802.11 AP", 9)))
+ result |= 2;
+
+ if (!(memcmp(&buf[i], "atbrfirm.bin", 12))) {
+ result |= 16;
+ remotefile = "atbrfirm.bin";
+ }
+
+ if (!(memcmp(&buf[i], "atsingle.bin", 12))) {
+ result |= 32;
+ remotefile = "atsingle.bin";
+ }
+
+ if (result > 18)
+ break;
+ }
+
+ if (result > 18)
+ break;
+
+ memset(buf, 0, sizeof(buf));
+ }
+#if FEATURE_TFTP_DEBUG
+ printf(_("Firmware file contains:\n"
+ "- string \"ATMEL\": %s\n"
+ "- string \"802.11 AP\": %s\n"
+ "- string \"atbrfirm.bin\": %s\n"
+ "- string \"atsingle.bin\": %s\n"),
+ result & 1 ? YES : NO, result & 2 ? YES : NO,
+ result & 16 ? YES : NO, result & 32 ? YES : NO);
+#endif
+ if ((result & (1|2)) != (1|2) || (result & (16|32)) == (16|32) ||
+ (result & (16|32)) == 0)
+ {
+ printf(_("Error: invalid firmware file given.\n"));
+ return EXIT_FAILURE;
+ }
+
+ lseek(fd, 0, SEEK_SET);
+
+ cp = strrchr(localfile, '/');
+ if (cp)
+ cp++;
+ else
+ cp = localfile;
+
+ printf(_("Using:\n"
+ "- server: %s\n"
+ "- firmware file: \"%s\"\n"
+ "- name used for upload: \"%s\"\n"),
+ inet_ntoa(in), cp, remotefile);
+
+ if (result & 16) {
+ /* Firmware series 1.4x.y - atbrfirm.bin */
+
+ unsigned short sum = 0;
+ unsigned short *wp;
+ int maxlen = 94448;
+ int bufs_read = 0;
+
+ /* compute checksum */
+ while ((i = read(fd, buf, sizeof(buf))) != 0) {
+ if (i < 0) {
+ perror(ERR_READFW);
+ return EXIT_FAILURE;
+ }
+
+ /* assure we do not checksum more than maxlen bytes */
+ if ((bufs_read + i) > maxlen)
+ i = maxlen - bufs_read;
+
+ bufs_read += i;
+
+ /* perform iterative checksumming */
+ i /= 2;
+ wp = (unsigned short *)buf;
+ while (i--) {
+ if (sum + *wp > 0xFFFF)
+ sum++;
+
+ sum += *(wp++);
+ };
+
+ /* prevent eventual fread() if file length > maxlen */
+ if (bufs_read == maxlen)
+ break;
+ }
+ sum ^= 0xFFFF;
+ lseek (fd, 0, SEEK_SET);
+
+ buf[0] = 0xf0;
+ buf[1] = 0x70;
+ buf[2] = sum;
+ buf[3] = sum >> 8;
+ buf[4] = 0xf0;
+ buf[5] = 0x70;
+ buf[6] = 0x01;
+ buf[7] = 0x00;
+
+ printf("- checksum: %x\n", ntohs(sum));
+ } else { /* result & 32 */
+ /* Firmware series 0.x.y.z - atsingle.bin */
+
+ i = read(fd, buf, 234);
+ if (i < 234) {
+ printf(ERR_READFW);
+ return EXIT_FAILURE;
+ }
+ if (lseek(fd, 256, SEEK_SET) == -1) {
+ perror(_("Error while lseek()ing in firmware file\n"));
+ return EXIT_FAILURE;
+ }
+ }
+
+ result = tftp(in.s_addr, remotefile, fd, buf);
+
+ return(result);
+}