From 71b2afe7fe934d709b20d1934dfd774eeb6ca219 Mon Sep 17 00:00:00 2001
From: Rok Mandeljc <rok.mandeljc@gmail.com>
Date: Sat, 5 Apr 2014 12:28:02 +0200
Subject: [PATCH 1/1] Implemented serial-over-ethernet for Win32.

---
 avrdude/configure.ac |  11 +-
 avrdude/ser_win32.c  | 449 +++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 404 insertions(+), 56 deletions(-)

diff --git a/avrdude/configure.ac b/avrdude/configure.ac
index 94be984..00f993d 100644
--- a/avrdude/configure.ac
+++ b/avrdude/configure.ac
@@ -192,6 +192,9 @@ AC_CHECK_HEADERS([ddk/hidsdi.h],,,[#include <windows.h>
 AC_C_CONST
 AC_HEADER_TIME
 
+# WinSock2
+AC_CHECK_LIB(ws2_32,puts)
+
 # Checks for library functions.
 AC_CHECK_FUNCS([memset select strcasecmp strdup strerror strncasecmp strtol strtoul gettimeofday usleep])
 
@@ -286,7 +289,7 @@ AC_ARG_ENABLE(
 		*)   AC_MSG_ERROR(bad value ${enableval} for enable-parport option) ;;
 		esac],
 	[enabled_parport=yes])
-	
+
 AC_ARG_ENABLE(
 	[linuxgpio],
 	AC_HELP_STRING(
@@ -297,7 +300,7 @@ AC_ARG_ENABLE(
 		no)  enabled_linuxgpio=no ;;
 		*)   AC_MSG_ERROR(bad value ${enableval} for enable-linuxgpio option) ;;
 		esac],
-	[enabled_linuxgpio=no])	
+	[enabled_linuxgpio=no])
 
 DIST_SUBDIRS_AC='doc windows'
 
@@ -388,10 +391,10 @@ if test "$GCC" = yes; then
 
 	AC_TRY_COMPILE(, [ int main () { return 0 ; } ], [
 		no_pointer_sign=yes
-		AC_MSG_RESULT([yes]) 
+		AC_MSG_RESULT([yes])
 		], [
 		no_pointer_sign=no
-		AC_MSG_RESULT([no]) 
+		AC_MSG_RESULT([no])
 		])
 	CFLAGS=$safe_CFLAGS
 
diff --git a/avrdude/ser_win32.c b/avrdude/ser_win32.c
index 7ecb20b..242b270 100644
--- a/avrdude/ser_win32.c
+++ b/avrdude/ser_win32.c
@@ -23,6 +23,11 @@
  * Native Win32 serial interface for avrdude.
  */
 
+/* winsock2.h must be included before windows.h from avrdude.h... */
+#if defined(WIN32NATIVE)
+#include <winsock2.h>
+#endif
+
 #include "avrdude.h"
 
 #if defined(WIN32NATIVE)
@@ -30,6 +35,7 @@
 #include <windows.h>
 #include <stdio.h>
 #include <ctype.h>   /* for isprint */
+#include <errno.h>   /* ENOTTY */
 
 #include "serial.h"
 
@@ -42,6 +48,8 @@ struct baud_mapping {
   DWORD speed;
 };
 
+static unsigned char serial_over_ethernet = 0;
+
 /* HANDLE hComPort=INVALID_HANDLE_VALUE; */
 
 static struct baud_mapping baud_lookup_table [] = {
@@ -91,26 +99,121 @@ static BOOL serial_w32SetTimeOut(HANDLE hComPort, DWORD timeout) // in ms
 
 static int ser_setspeed(union filedescriptor *fd, long baud)
 {
-	DCB dcb;
-	HANDLE hComPort = (HANDLE)fd->pfd;
-
-	ZeroMemory (&dcb, sizeof(DCB));
-	dcb.DCBlength = sizeof(DCB);
-	dcb.BaudRate = serial_baud_lookup (baud);
-	dcb.fBinary = 1;
-	dcb.fDtrControl = DTR_CONTROL_DISABLE;
-	dcb.fRtsControl = RTS_CONTROL_DISABLE;
-	dcb.ByteSize = 8;
-	dcb.Parity = NOPARITY;
-	dcb.StopBits = ONESTOPBIT;
-
-	if (!SetCommState(hComPort, &dcb))
+	if (serial_over_ethernet) {
+		return -ENOTTY;
+	} else {
+		DCB dcb;
+		HANDLE hComPort = (HANDLE)fd->pfd;
+
+		ZeroMemory (&dcb, sizeof(DCB));
+		dcb.DCBlength = sizeof(DCB);
+		dcb.BaudRate = serial_baud_lookup (baud);
+		dcb.fBinary = 1;
+		dcb.fDtrControl = DTR_CONTROL_DISABLE;
+		dcb.fRtsControl = RTS_CONTROL_DISABLE;
+		dcb.ByteSize = 8;
+		dcb.Parity = NOPARITY;
+		dcb.StopBits = ONESTOPBIT;
+
+		if (!SetCommState(hComPort, &dcb))
+			return -1;
+
+		return 0;
+	}
+}
+
+static int
+net_open(const char *port, union filedescriptor *fdp)
+{
+	WSADATA wsaData;
+	LPVOID lpMsgBuf;
+
+	char *hstr, *pstr, *end;
+	unsigned int pnum;
+	int fd;
+	struct sockaddr_in sockaddr;
+	struct hostent *hp;
+
+	if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
+		fprintf(stderr, "%s: net_open(): WSAStartup() failed\n", progname);
+		return -1;
+	}
+
+	if ((hstr = strdup(port)) == NULL) {
+		fprintf(stderr, "%s: net_open(): Out of memory!\n", progname);
+		return -1;
+	}
+
+	if (((pstr = strchr(hstr, ':')) == NULL) || (pstr == hstr)) {
+		fprintf(stderr, "%s: net_open(): Mangled host:port string \"%s\"\n", progname, hstr);
+		free(hstr);
+		return -1;
+	}
+
+	/*
+	 * Terminate the host section of the description.
+	 */
+	*pstr++ = '\0';
+
+	pnum = strtoul(pstr, &end, 10);
+
+	if ((*pstr == '\0') || (*end != '\0') || (pnum == 0) || (pnum > 65535)) {
+		fprintf(stderr, "%s: net_open(): Bad port number \"%s\"\n", progname, pstr);
+		free(hstr);
+		return -1;
+	}
+
+	if ((hp = gethostbyname(hstr)) == NULL) {
+		fprintf(stderr, "%s: net_open(): unknown host \"%s\"\n", progname, hstr);
+		free(hstr);
+		return -1;
+	}
+
+	free(hstr);
+
+	if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+		FormatMessage(
+			FORMAT_MESSAGE_ALLOCATE_BUFFER |
+			FORMAT_MESSAGE_FROM_SYSTEM |
+			FORMAT_MESSAGE_IGNORE_INSERTS,
+			NULL,
+			WSAGetLastError(),
+			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+			(LPTSTR)&lpMsgBuf,
+			0,
+			NULL);
+		fprintf(stderr, "%s: net_open(): Cannot open socket: %s\n", progname, (char *)lpMsgBuf);
+		LocalFree(lpMsgBuf);
+		return -1;
+	}
+
+	memset(&sockaddr, 0, sizeof(struct sockaddr_in));
+	sockaddr.sin_family = AF_INET;
+	sockaddr.sin_port = htons(pnum);
+	memcpy(&(sockaddr.sin_addr.s_addr), hp->h_addr, sizeof(struct in_addr));
+
+	if (connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
+		FormatMessage(
+			FORMAT_MESSAGE_ALLOCATE_BUFFER |
+			FORMAT_MESSAGE_FROM_SYSTEM |
+			FORMAT_MESSAGE_IGNORE_INSERTS,
+			NULL,
+			WSAGetLastError(),
+			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+			(LPTSTR)&lpMsgBuf,
+			0,
+			NULL);
+		fprintf(stderr, "%s: net_open(): Connect failed: %s\n", progname, (char *)lpMsgBuf);
+		LocalFree(lpMsgBuf);
 		return -1;
+	}
 
+	fdp->ifd = fd;
+
+	serial_over_ethernet = 1;
 	return 0;
 }
 
-
 static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
 {
 	LPVOID lpMsgBuf;
@@ -120,15 +223,9 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
 	/*
 	 * If the port is of the form "net:<host>:<port>", then
 	 * handle it as a TCP connection to a terminal server.
-	 *
-	 * This is curently not implemented for Win32.
 	 */
 	if (strncmp(port, "net:", strlen("net:")) == 0) {
-		fprintf(stderr,
-			"%s: ser_open(): network connects are currently not"
-			"implemented for Win32 environments\n",
-			progname);
-		return -1;
+		return net_open(port + strlen("net:"), fdp);
 	}
 
 	if (strncasecmp(port, "com", strlen("com")) == 0) {
@@ -152,9 +249,9 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
 		OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 
 	if (hComPort == INVALID_HANDLE_VALUE) {
-		FormatMessage( 
-			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
-			FORMAT_MESSAGE_FROM_SYSTEM | 
+		FormatMessage(
+			FORMAT_MESSAGE_ALLOCATE_BUFFER |
+			FORMAT_MESSAGE_FROM_SYSTEM |
 			FORMAT_MESSAGE_IGNORE_INSERTS,
 			NULL,
 			GetLastError(),
@@ -202,30 +299,101 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
 
 static void ser_close(union filedescriptor *fd)
 {
-	HANDLE hComPort=(HANDLE)fd->pfd;
-	if (hComPort != INVALID_HANDLE_VALUE)
-		CloseHandle (hComPort);
+	if (serial_over_ethernet) {
+		closesocket(fd->ifd);
+		WSACleanup();
+	} else {
+		HANDLE hComPort=(HANDLE)fd->pfd;
+		if (hComPort != INVALID_HANDLE_VALUE)
+			CloseHandle (hComPort);
 
-	hComPort = INVALID_HANDLE_VALUE;
+		hComPort = INVALID_HANDLE_VALUE;
+	}
 }
 
 static int ser_set_dtr_rts(union filedescriptor *fd, int is_on)
 {
-	HANDLE hComPort=(HANDLE)fd->pfd;
-
-	if (is_on) {
-		EscapeCommFunction(hComPort, SETDTR);
-		EscapeCommFunction(hComPort, SETRTS);
+	if (serial_over_ethernet) {
+		return 0;
 	} else {
-		EscapeCommFunction(hComPort, CLRDTR);
-		EscapeCommFunction(hComPort, CLRRTS);
+		HANDLE hComPort=(HANDLE)fd->pfd;
+
+		if (is_on) {
+			EscapeCommFunction(hComPort, SETDTR);
+			EscapeCommFunction(hComPort, SETRTS);
+		} else {
+			EscapeCommFunction(hComPort, CLRDTR);
+			EscapeCommFunction(hComPort, CLRRTS);
+		}
+		return 0;
 	}
-	return 0;
 }
 
+static int net_send(union filedescriptor *fd, unsigned char * buf, size_t buflen)
+{
+	LPVOID lpMsgBuf;
+	int rc;
+	unsigned char *p = buf;
+	size_t len = buflen;
+
+	if (fd->ifd < 0) {
+		fprintf(stderr, "%s: net_send(): connection not open\n", progname);
+		exit(1);
+	}
+
+	if (!len) {
+		return 0;
+	}
+
+	if (verbose > 3) {
+		fprintf(stderr, "%s: Send: ", progname);
+
+		while (buflen) {
+			unsigned char c = *buf;
+			if (isprint(c)) {
+				fprintf(stderr, "%c ", c);
+			} else {
+				fprintf(stderr, ". ");
+			}
+			fprintf(stderr, "[%02x] ", c);
+
+			buf++;
+			buflen--;
+		}
+
+		fprintf(stderr, "\n");
+	}
+
+	while (len) {
+		rc = send(fd->ifd, p, (len > 1024) ? 1024 : len, 0);
+		if (rc < 0) {
+			FormatMessage(
+				FORMAT_MESSAGE_ALLOCATE_BUFFER |
+				FORMAT_MESSAGE_FROM_SYSTEM |
+				FORMAT_MESSAGE_IGNORE_INSERTS,
+				NULL,
+				WSAGetLastError(),
+				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+				(LPTSTR)&lpMsgBuf,
+				0,
+				NULL);
+			fprintf(stderr, "%s: net_send(): send error: %s\n", progname, (char *)lpMsgBuf);
+			LocalFree(lpMsgBuf);
+			exit(1);
+		}
+		p += rc;
+		len -= rc;
+	}
+
+	return 0;
+}
 
 static int ser_send(union filedescriptor *fd, unsigned char * buf, size_t buflen)
 {
+	if (serial_over_ethernet) {
+		return net_send(fd, buf, buflen);
+	}
+
 	size_t len = buflen;
 	unsigned char c='\0';
 	DWORD written;
@@ -235,7 +403,7 @@ static int ser_send(union filedescriptor *fd, unsigned char * buf, size_t buflen
 
 	if (hComPort == INVALID_HANDLE_VALUE) {
 		fprintf(stderr, "%s: ser_send(): port not open\n",
-              progname); 
+              progname);
 		exit(1);
 	}
 
@@ -260,7 +428,7 @@ static int ser_send(union filedescriptor *fd, unsigned char * buf, size_t buflen
 		}
       fprintf(stderr, "\n");
 	}
-	
+
 	serial_w32SetTimeOut(hComPort,500);
 
 	if (!WriteFile (hComPort, buf, buflen, &written, NULL)) {
@@ -271,7 +439,7 @@ static int ser_send(union filedescriptor *fd, unsigned char * buf, size_t buflen
 
 	if (written != buflen) {
 		fprintf(stderr, "%s: ser_send(): size/send mismatch\n",
-              progname); 
+              progname);
 		exit(1);
 	}
 
@@ -279,27 +447,125 @@ static int ser_send(union filedescriptor *fd, unsigned char * buf, size_t buflen
 }
 
 
+static int net_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen)
+{
+	LPVOID lpMsgBuf;
+	struct timeval timeout, to2;
+	fd_set rfds;
+	int nfds;
+	int rc;
+	unsigned char *p = buf;
+	size_t len = 0;
+
+	if (fd->ifd < 0) {
+		fprintf(stderr, "%s: net_recv(): connection not open\n", progname);
+		exit(1);
+	}
+
+	timeout.tv_sec  = serial_recv_timeout / 1000L;
+	timeout.tv_usec = (serial_recv_timeout % 1000L) * 1000;
+	to2 = timeout;
+
+	while (len < buflen) {
+reselect:
+		FD_ZERO(&rfds);
+		FD_SET(fd->ifd, &rfds);
+
+		nfds = select(fd->ifd + 1, &rfds, NULL, NULL, &to2);
+		if (nfds == 0) {
+			if (verbose > 1) {
+				fprintf(stderr, "%s: ser_recv(): programmer is not responding\n", progname);
+			}
+			return -1;
+		} else if (nfds == -1) {
+			if (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEINPROGRESS) {
+				fprintf(stderr, "%s: ser_recv(): programmer is not responding, reselecting\n", progname);
+				goto reselect;
+			} else {
+				FormatMessage(
+					FORMAT_MESSAGE_ALLOCATE_BUFFER |
+					FORMAT_MESSAGE_FROM_SYSTEM |
+					FORMAT_MESSAGE_IGNORE_INSERTS,
+					NULL,
+					WSAGetLastError(),
+					MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+					(LPTSTR)&lpMsgBuf,
+					0,
+					NULL);
+				fprintf(stderr, "%s: ser_recv(): select(): %s\n", progname, (char *)lpMsgBuf);
+				LocalFree(lpMsgBuf);
+				exit(1);
+			}
+		}
+
+		rc = recv(fd->ifd, p, (buflen - len > 1024) ? 1024 : buflen - len, 0);
+		if (rc < 0) {
+			FormatMessage(
+				FORMAT_MESSAGE_ALLOCATE_BUFFER |
+				FORMAT_MESSAGE_FROM_SYSTEM |
+				FORMAT_MESSAGE_IGNORE_INSERTS,
+				NULL,
+				WSAGetLastError(),
+				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+				(LPTSTR)&lpMsgBuf,
+				0,
+				NULL);
+			fprintf(stderr, "%s: ser_recv(): read error: %s\n", progname, (char *)lpMsgBuf);
+			LocalFree(lpMsgBuf);
+			exit(1);
+		}
+		p += rc;
+		len += rc;
+	}
+
+	p = buf;
+
+	if (verbose > 3) {
+		fprintf(stderr, "%s: Recv: ", progname);
+
+		while (len) {
+			unsigned char c = *p;
+			if (isprint(c)) {
+				fprintf(stderr, "%c ", c);
+			} else {
+				fprintf(stderr, ". ");
+			}
+			fprintf(stderr, "[%02x] ", c);
+
+			p++;
+			len--;
+		}
+		fprintf(stderr, "\n");
+	}
+
+	return 0;
+}
+
 static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen)
 {
+	if (serial_over_ethernet) {
+		return net_recv(fd, buf, buflen);
+	}
+
 	unsigned char c;
 	unsigned char * p = buf;
 	DWORD read;
 
 	HANDLE hComPort=(HANDLE)fd->pfd;
-	
+
 	if (hComPort == INVALID_HANDLE_VALUE) {
 		fprintf(stderr, "%s: ser_read(): port not open\n",
-              progname); 
+              progname);
 		exit(1);
 	}
-	
+
 	serial_w32SetTimeOut(hComPort, serial_recv_timeout);
-	
+
 	if (!ReadFile(hComPort, buf, buflen, &read, NULL)) {
 		LPVOID lpMsgBuf;
-		FormatMessage( 
-			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
-			FORMAT_MESSAGE_FROM_SYSTEM | 
+		FormatMessage(
+			FORMAT_MESSAGE_ALLOCATE_BUFFER |
+			FORMAT_MESSAGE_FROM_SYSTEM |
 			FORMAT_MESSAGE_IGNORE_INSERTS,
 			NULL,
 			GetLastError(),
@@ -346,9 +612,88 @@ static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen
   return 0;
 }
 
+static int net_drain(union filedescriptor *fd, int display)
+{
+	LPVOID lpMsgBuf;
+	struct timeval timeout;
+	fd_set rfds;
+	int nfds;
+	int rc;
+	unsigned char buf;
+
+	timeout.tv_sec = 0;
+	timeout.tv_usec = 250000;
+
+	if (fd->ifd < 0) {
+		fprintf(stderr, "%s: net_drain(): connection not open\n", progname);
+		exit(1);
+	}
+
+	if (display) {
+		fprintf(stderr, "drain>");
+	}
+
+	while (1) {
+		FD_ZERO(&rfds);
+		FD_SET(fd->ifd, &rfds);
+
+reselect:
+		nfds = select(fd->ifd + 1, &rfds, NULL, NULL, &timeout);
+		if (nfds == 0) {
+			if (display) {
+				fprintf(stderr, "<drain\n");
+			}
+			break;
+		} else if (nfds == -1) {
+			if (WSAGetLastError() == WSAEINTR) {
+				goto reselect;
+			} else {
+				FormatMessage(
+					FORMAT_MESSAGE_ALLOCATE_BUFFER |
+					FORMAT_MESSAGE_FROM_SYSTEM |
+					FORMAT_MESSAGE_IGNORE_INSERTS,
+					NULL,
+					WSAGetLastError(),
+					MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+					(LPTSTR)&lpMsgBuf,
+					0,
+					NULL);
+				fprintf(stderr, "%s: ser_drain(): select(): %s\n", progname, (char *)lpMsgBuf);
+				LocalFree(lpMsgBuf);
+				exit(1);
+			}
+		}
+
+		rc = recv(fd->ifd, &buf, 1, 0);
+		if (rc < 0) {
+			FormatMessage(
+				FORMAT_MESSAGE_ALLOCATE_BUFFER |
+				FORMAT_MESSAGE_FROM_SYSTEM |
+				FORMAT_MESSAGE_IGNORE_INSERTS,
+				NULL,
+				WSAGetLastError(),
+				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+				(LPTSTR)&lpMsgBuf,
+				0,
+				NULL);
+			fprintf(stderr, "%s: ser_drain(): read error: %s\n", progname, (char *)lpMsgBuf);
+			LocalFree(lpMsgBuf);
+			exit(1);
+		}
+		if (display) {
+			fprintf(stderr, "%02x ", buf);
+		}
+	}
+
+	return 0;
+}
 
 static int ser_drain(union filedescriptor *fd, int display)
 {
+	if (serial_over_ethernet) {
+		return net_drain(fd, display);
+	}
+
 	// int rc;
 	unsigned char buf[10];
 	BOOL readres;
@@ -358,12 +703,12 @@ static int ser_drain(union filedescriptor *fd, int display)
 
   	if (hComPort == INVALID_HANDLE_VALUE) {
 		fprintf(stderr, "%s: ser_drain(): port not open\n",
-              progname); 
+              progname);
 		exit(1);
 	}
 
 	serial_w32SetTimeOut(hComPort,250);
-  
+
 	if (display) {
 		fprintf(stderr, "drain>");
 	}
@@ -372,9 +717,9 @@ static int ser_drain(union filedescriptor *fd, int display)
 		readres=ReadFile(hComPort, buf, 1, &read, NULL);
 		if (!readres) {
 			LPVOID lpMsgBuf;
-			FormatMessage( 
-				FORMAT_MESSAGE_ALLOCATE_BUFFER | 
-				FORMAT_MESSAGE_FROM_SYSTEM | 
+			FormatMessage(
+				FORMAT_MESSAGE_ALLOCATE_BUFFER |
+				FORMAT_MESSAGE_FROM_SYSTEM |
 				FORMAT_MESSAGE_IGNORE_INSERTS,
 				NULL,
 				GetLastError(),
-- 
1.9.0

