Hi,
I just want to mention that COM port redirection is possible in Bochs CVS HEAD. Currently it only supports "null", "file", "raw", and "mouse". I extended it in my local copy so that you can use "pipe" too.
I attached my patch and my own version of pipetunnel.
This patch is already on the Bochs "Patches" page. I hope that it'll be added ASAP to the main source trunk.
BTW: I've seen a patch to allow COM <-> TCP/IP redirection on the "Patches" page. When there is interest, I can combine the two patches and publish them here ...
Regards, Mark
Index: config.cc =================================================================== RCS file: /cvsroot/bochs/bochs/config.cc,v retrieving revision 1.34 diff -u -r1.34 config.cc --- config.cc 3 Apr 2005 15:00:44 -0000 1.34 +++ config.cc 3 May 2005 00:39:58 -0000 @@ -863,6 +863,7 @@ static char *serial_mode_list[] = { "null", "file", + "pipe", "term", "raw", "mouse", Index: iodev/serial.cc =================================================================== RCS file: /cvsroot/bochs/bochs/iodev/serial.cc,v retrieving revision 1.64 diff -u -r1.64 serial.cc --- iodev/serial.cc 19 Jan 2005 18:21:37 -0000 1.64 +++ iodev/serial.cc 3 May 2005 00:54:44 -0000 @@ -45,6 +45,148 @@
#define LOG_THIS theSerialDevice->
+#ifdef WIN32 +class TPipe { +private: + HANDLE hPipe; + char *pszPipeName; + +public: + TPipe(const char *pszPipeName_arg=NULL) { + pszPipeName = NULL; + hPipe = INVALID_HANDLE_VALUE; + Open(pszPipeName_arg); + } + ~TPipe(void) { + Close(); + if (pszPipeName!=NULL) { + free(pszPipeName); + } + } + + bool Open(const char *pszPipeName_arg=NULL) { + Close(); + if (pszPipeName_arg!=NULL) { + if (pszPipeName!=NULL) { + free(pszPipeName); + } + pszPipeName = strdup(pszPipeName_arg); + } + if (pszPipeName==NULL) + return false; + hPipe = + CreateFileA(pszPipeName, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + if (hPipe!=INVALID_HANDLE_VALUE) { + DWORD dwMode = PIPE_READMODE_BYTE | PIPE_NOWAIT; + if (!SetNamedPipeHandleState(hPipe, + &dwMode, + NULL, + NULL)) + { + Close(); + } + } + } + void Close(void) { + if (hPipe!=INVALID_HANDLE_VALUE) { + CloseHandle(hPipe); + hPipe = INVALID_HANDLE_VALUE; + } + } + + bool Ok(void) const { + return hPipe!=INVALID_HANDLE_VALUE; + } + + size_t Write(const void *pcv,size_t size) { + assert(hPipe!=INVALID_HANDLE_VALUE); + DWORD dwWritten = 0; + if (!WriteFile(hPipe, + pcv, + (DWORD) size, + &dwWritten, + NULL)) + return 0; + return (size_t) dwWritten; + } + + size_t Read(void *pv,size_t size) { + assert(hPipe!=INVALID_HANDLE_VALUE); + DWORD dwRead = 0; + if (!ReadFile(hPipe, + pv, + (DWORD) size, + &dwRead, + NULL)) + { + return 0; + } + return (size_t) dwRead; + } + + bool Flush(void) { + assert(hPipe!=INVALID_HANDLE_VALUE); + return FlushFileBuffers(hPipe)!=0; + } + + bool Terminated(void) { + assert(hPipe!=INVALID_HANDLE_VALUE); + DWORD dwBytesAvail = 0; + if (!PeekNamedPipe(hPipe, + NULL, + 0, + NULL, + &dwBytesAvail, + NULL)) + return true; + return false; + } + + HANDLE GetHandle(void) const { + return hPipe; + } +}; +#else +class TPipe +{ +private: + int iHandle; + +public: + TPipe(const char *pszFileName) { + iHandle = open(pszFileName, O_RDWR | O_NONBLOCK, 600); + } + ~TPipe(void) { + if (iHandle >=0 ) + close(iHandle); + } + bool Ok(void) const { + return iHandle >= 0; + } + size_t Write(const void *pv, size_t size) + { + return write(iHandle, pv, size); + } + size_t Read(void *pv, size_t size) + { + return read(iHandle, pv, size); + } + bool Flush(void) { + return commit(iHandle)==0; + } + + int GetHandle(void) const { + return iHandle; + } +}; +#endif + bx_serial_c *theSerialDevice = NULL;
int @@ -82,6 +224,11 @@ if (BX_SER_THIS s[i].output != NULL) fclose(BX_SER_THIS s[i].output); break; + case BX_SER_MODE_PIPE: + // Close the handler + // It must not happen that io_handler is NULL at this point + delete (TPipe*) BX_SER_THIS s[i].io_handler; + break; case BX_SER_MODE_TERM: #if defined(SERIAL_ENABLE) if (s[i].tty_id >= 0) { @@ -227,6 +374,23 @@ if (BX_SER_THIS s[i].output) BX_SER_THIS s[i].io_mode = BX_SER_MODE_FILE; } + } else if (!strcmp(mode, "pipe")) { + if (strlen(bx_options.com[i].Odev->getptr ()) > 0) { + TPipe *pIo = new TPipe(bx_options.com[i].Odev->getptr ()); + if (!pIo->Ok()) { + // It's not an error if the connection to the pipe failed + // because the listening process might be available only + // when a developer wants to debug something without changing + // the configuration file + BX_INFO(("open of com%d (%s) failed\n", i+1, bx_options.com[i].Odev->getptr ())); + // Delete TPipe class to avoid a memory leak + delete pIo; + } else { + BX_DEBUG(("com%d pipe: %s", i+1, bx_options.com[i].Odev->getptr ())); + BX_SER_THIS s[i].io_mode = BX_SER_MODE_PIPE; + BX_SER_THIS s[i].io_handler = pIo; + } + } } else if (!strcmp(mode, "term")) { #if defined(SERIAL_ENABLE) if (strlen(bx_options.com[i].Odev->getptr ()) > 0) { @@ -1031,6 +1195,17 @@ fputc(BX_SER_THIS s[port].tsrbuffer, BX_SER_THIS s[port].output); fflush(BX_SER_THIS s[port].output); break; + case BX_SER_MODE_PIPE: + { + // Get the redirection target object. + TPipe *pIo = (TPipe*) BX_SER_THIS s[port].io_handler; + BX_DEBUG(("com%d: write: '%c'", port+1, BX_SER_THIS s[port].tsrbuffer)); + // The status of this object must be Ok()==true so there's no need + // to test for this status explicitly. + pIo->Write((bx_ptr_t) & BX_SER_THIS s[port].tsrbuffer, 1); + pIo->Flush(); + } + break; case BX_SER_MODE_TERM: #if defined(SERIAL_ENABLE) BX_DEBUG(("com%d: write: '%c'", port+1, BX_SER_THIS s[port].tsrbuffer)); @@ -1178,6 +1353,18 @@ } #endif break; + case BX_SER_MODE_PIPE: + { + // Get the redirection target object. + TPipe *pIo = (TPipe*) BX_SER_THIS s[port].io_handler; + // The status of this object must be Ok()==true so there's no need + // to test for this status explicitly. + if (pIo->Read(&chbuf, 1)==1) { + BX_DEBUG(("com%d: read: '%c'", port+1, chbuf)); + data_ready = 1; + } + } + break; case BX_SER_MODE_MOUSE: if (BX_SER_THIS mouse_internal_buffer.num_elements > 0) { chbuf = BX_SER_THIS mouse_internal_buffer.buffer[BX_SER_THIS mouse_internal_buffer.head]; Index: iodev/serial.h =================================================================== RCS file: /cvsroot/bochs/bochs/iodev/serial.h,v retrieving revision 1.25 diff -u -r1.25 serial.h --- iodev/serial.h 5 Dec 2004 20:23:39 -0000 1.25 +++ iodev/serial.h 3 May 2005 00:41:34 -0000 @@ -67,6 +67,7 @@ #define BX_SER_MODE_TERM 2 #define BX_SER_MODE_RAW 3 #define BX_SER_MODE_MOUSE 4 +#define BX_SER_MODE_PIPE 5
enum { BX_SER_INT_IER, @@ -110,6 +111,7 @@ int io_mode; int tty_id; FILE *output; + void *io_handler;
#if USE_RAW_SERIAL serial_raw* raw;
// // pipetunnel.cpp // // Martin Fuchs, 30.11.2003 //
// // Invoke as: "pipetunnel [pipe_name]", // for example: "pipetunnel com_2" // // Then start up RectOS in VMWare, wait for the serial connect. // After that you can connect GDB using the command "target remote :9999". //
#define _WIN32_WINNT 0x0501 #define WIN32_LEAN_AND_MEAN #include <windows.h>
#include <winsock.h>
#ifdef _MSC_VER #pragma comment(lib, "wsock32") #endif
#include <stdio.h> #include <stddef.h> #include <string.h> #include <assert.h> #include <errno.h>
#include <queue> #include <vector>
// This definition currently missing in MinGW. #ifndef FILE_FLAG_FIRST_PIPE_INSTANCE #define FILE_FLAG_FIRST_PIPE_INSTANCE 0x00080000 #endif
void dumphex(const char *buf, int len, int pos) { int i, j; for(j = 0; j < len; j += 16) { for(i = 0; i < 16; i++) { if(i + j < len) printf("%02x%c", (unsigned char)buf[i + j], j + i + 1 == pos ? '*' : ' '); else printf(" "); } for(i = 0; i < 16; i++) { if(i + j < len) printf("%c", buf[i + j] >= ' ' ? buf[i + j] : '.'); else printf(" "); } printf("\n"); } }
class WinSockInit { private: WSADATA wsa_data; bool bIsOK; public: WinSockInit(void) { bIsOK = WSAStartup(MAKEWORD(2,2), &wsa_data)==0; } ~WinSockInit(void) { if (bIsOK) WSACleanup(); } bool Ok(void) const { return bIsOK; }
};
class TSocketServer { private: SOCKET sckServer; SOCKADDR_IN addr; SOCKET sckClient; public: TSocketServer(void) { memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(9999);
sckClient = INVALID_SOCKET;
sckServer = socket(PF_INET, SOCK_STREAM, 0); if (sckServer==INVALID_SOCKET) return;
u_long nbio_value = 1; ioctlsocket(sckServer, FIONBIO, &nbio_value);
if (bind(sckServer, (struct sockaddr*) &addr, sizeof(addr))!=0) { sckServer = INVALID_SOCKET; return; }
if (listen(sckServer, 4)!=0) { sckServer = INVALID_SOCKET; return; } }
bool Ok(void) const { return (sckServer!=INVALID_SOCKET); }
bool IsConnected(void) { if (sckClient==INVALID_SOCKET) { sckClient = accept(sckServer, NULL, NULL); if (sckClient!=INVALID_SOCKET) { BOOL bValue = TRUE; setsockopt(sckClient, IPPROTO_TCP, TCP_NODELAY, (const char *) &bValue, sizeof(BOOL)); } } return sckClient!=INVALID_SOCKET; }
size_t Write(const void *pcv,size_t size) { assert(sckClient!=INVALID_SOCKET); int count = send(sckClient, (const char *) pcv, (int) size, 0); if (count==SOCKET_ERROR) { sckClient = INVALID_SOCKET; return 0; } return (size_t) count; }
size_t Read(void *pv,size_t size) { assert(sckClient!=INVALID_SOCKET); int count = recv(sckClient, (char*) pv, (int) size, 0); if (count==SOCKET_ERROR) { int iErrCode = WSAGetLastError(); switch (iErrCode) { case WSAEWOULDBLOCK: break; case WSAECONNRESET: sckClient = INVALID_SOCKET; break; } return 0; } else if (count==0) { sckClient = INVALID_SOCKET; } return (size_t) count; } };
class TPipeServer { private: HANDLE hPipe; char *pszPipeName;
private: bool Listen(void) { assert(hPipe!=INVALID_HANDLE_VALUE); if (ConnectNamedPipe(hPipe, NULL)!=0) return true; if (GetLastError()==ERROR_PIPE_LISTENING) return true; return false; }
public: TPipeServer(const char *pszPipeName_arg=NULL) { pszPipeName = NULL; hPipe = INVALID_HANDLE_VALUE; Open(pszPipeName_arg); } ~TPipeServer(void) { Close(); if (pszPipeName!=NULL) { free(pszPipeName); } }
bool Open(const char *pszPipeName_arg=NULL) { if (hPipe!=INVALID_HANDLE_VALUE) { Close(); } if (pszPipeName_arg!=NULL) { if (pszPipeName!=NULL) { free(pszPipeName); } pszPipeName = strdup(pszPipeName_arg); } if (pszPipeName==NULL) return false; hPipe = CreateNamedPipeA(pszPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_WRITE_THROUGH, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT, 1, 4096, 4096, 0, NULL); if (hPipe!=INVALID_HANDLE_VALUE) { if (!Listen()) { Close(); } } return true; } void Close(void) { if (hPipe!=INVALID_HANDLE_VALUE) { Flush(); DisconnectNamedPipe(hPipe); CloseHandle(hPipe); hPipe = INVALID_HANDLE_VALUE; } }
bool Ok(void) const { return hPipe!=INVALID_HANDLE_VALUE; }
bool IsConnected(void) { assert(hPipe!=INVALID_HANDLE_VALUE); if (ConnectNamedPipe(hPipe, NULL)!=0) return false; DWORD dwErrCode = GetLastError(); if (dwErrCode==ERROR_PIPE_CONNECTED) return true; if (dwErrCode==ERROR_NO_DATA) { // reopen Open(); } return false; }
size_t Write(const void *pcv,size_t size) { assert(hPipe!=INVALID_HANDLE_VALUE); DWORD dwWritten = 0; if (!WriteFile(hPipe, pcv, (DWORD) size, &dwWritten, NULL)) return 0; return (size_t) dwWritten; }
size_t Read(void *pv,size_t size) { assert(hPipe!=INVALID_HANDLE_VALUE); DWORD dwRead = 0; if (!ReadFile(hPipe, pv, (DWORD) size, &dwRead, NULL)) return 0; return (size_t) dwRead; }
bool Flush(void) { assert(hPipe!=INVALID_HANDLE_VALUE); return FlushFileBuffers(hPipe)!=0; } };
typedef std::vector<unsigned char> TByteBuffer; typedef std::queue<TByteBuffer> TBufferQueue;
int main(int argc, char** argv) { // initialize winsock WinSockInit SocketInit; if (!SocketInit.Ok()) { fprintf(stderr, "Failed to initialize sockets\n"); return 1; }
// Get name of named pipe char path[MAX_PATH]; const char* pipe_name;
if (argc > 1) { pipe_name = *++argv; } else { pipe_name = "com_2"; }
sprintf(path, "\\.\pipe\%s", pipe_name);
// increment priority to be faster than the cpu eating VMWare process SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
TPipeServer PipeServer(path); if (!PipeServer.Ok()) { fprintf(stderr, "Failed to create pipe %s\n", path); return 1; }
TSocketServer SocketServer; if (!SocketServer.Ok()) { fprintf(stderr, "Failed to create socket\n"); return 1; }
bool bSocketWasOpen = false; bool bPipeWasOpen = false; TBufferQueue PipeReadQueue; TBufferQueue SocketReadQueue; TByteBuffer ReadBuffer(4096); for (;;) { if (PipeServer.IsConnected()) { if (!bPipeWasOpen) { printf("Pipe connected\n"); bPipeWasOpen = true; }
size_t BufSize = PipeServer.Read(&*ReadBuffer.begin(), ReadBuffer.size()); if (BufSize!=0) { // send to socket TByteBuffer TempBuffer(BufSize); memcpy(&*TempBuffer.begin(), &*ReadBuffer.begin(), BufSize);
printf("PIPE >>\n"); dumphex((const char *) &*TempBuffer.begin(), TempBuffer.size(), 0);
PipeReadQueue.push(TempBuffer); }
if (SocketReadQueue.size()!=0) { const TByteBuffer &TempBuffer(SocketReadQueue.front());
printf("PIPE <<\n"); dumphex((const char *) &*TempBuffer.begin(), TempBuffer.size(), 0);
PipeServer.Write(&*TempBuffer.begin(), TempBuffer.size()); SocketReadQueue.pop(); }
} else { if (bPipeWasOpen) { printf("Pipe disconnected\n"); // reopen pipe PipeServer.Open(); bPipeWasOpen = false; PipeReadQueue = TBufferQueue(); } }
if (SocketServer.IsConnected()) { if (!bSocketWasOpen) { printf("TCP connection established\n"); bSocketWasOpen = true; }
size_t BufSize = SocketServer.Read(&*ReadBuffer.begin(), ReadBuffer.size()); if (BufSize!=0) { // send to socket TByteBuffer TempBuffer(BufSize); memcpy(&*TempBuffer.begin(), &*ReadBuffer.begin(), BufSize);
printf("TCP >>\n"); dumphex((const char *) &*TempBuffer.begin(), TempBuffer.size(), 0);
SocketReadQueue.push(TempBuffer); }
if (PipeReadQueue.size()!=0) { const TByteBuffer &TempBuffer(PipeReadQueue.front());
printf("TCP <<\n"); dumphex((const char *) &*TempBuffer.begin(), TempBuffer.size(), 0);
SocketServer.Write(&*TempBuffer.begin(), TempBuffer.size()); PipeReadQueue.pop(); }
} else { if (bSocketWasOpen) { printf("TCP connection closed\n"); bSocketWasOpen = false; SocketReadQueue = TBufferQueue(); } }
if (!bPipeWasOpen && !bSocketWasOpen) { Sleep(50); } else { Sleep(1); } }
return 0; }