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;
}