WinUAE/od-win32/parser.cpp

1706 lines
43 KiB
C++

/*
* UAE - The Un*x Amiga Emulator
*
* Not a parser, but parallel and serial emulation for Win32
*
* Copyright 1997 Mathias Ortmann
* Copyright 1998-1999 Brian King - added MIDI output support
*/
#include "sysconfig.h"
#undef SERIAL_ENET
#include <Ws2tcpip.h>
#include <windows.h>
#include <winspool.h>
#include <stdlib.h>
#include <stdarg.h>
#include <mmsystem.h>
#include <ddraw.h>
#include <commctrl.h>
#include <commdlg.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <io.h>
#include <setupapi.h>
#include <Ntddpar.h>
#include "sysdeps.h"
#include "options.h"
#include "gensound.h"
#include "events.h"
#include "uae.h"
#include "include/memory.h"
#include "custom.h"
#include "autoconf.h"
#include "newcpu.h"
#include "traps.h"
#include "registry.h"
#include "od-win32/win32gui.h"
#include "od-win32/parser.h"
#include "od-win32/midi.h"
#include "od-win32/ahidsound.h"
#include "picasso96_win.h"
#include "win32gfx.h"
#include "win32.h"
#include "ioport.h"
#include "parallel.h"
#include "zfile.h"
#include "threaddep/thread.h"
#include "serial.h"
#include "savestate.h"
#include "ahidsound_new.h"
#include "uaeipc.h"
#include "xwin.h"
#include "drawing.h"
#define GSDLLEXPORT __declspec(dllimport)
#include <Ghostscript/iapi.h>
#include <Ghostscript/ierrors.h>
#define MIN_PRTBYTES 10
static uae_char prtbuf[PRTBUFSIZE];
static int prtbufbytes,wantwrite;
static HANDLE hPrt = INVALID_HANDLE_VALUE;
static DWORD dwJob;
static int prtopen;
void DoSomeWeirdPrintingStuff(uae_char val);
static int uartbreak;
static int parflush;
static volatile int prt_running;
static volatile int prt_started;
static smp_comm_pipe prt_requests;
int postscript_print_debugging = 0;
static struct zfile *prtdump;
static int psmode = 0;
static HMODULE gsdll;
static void *gsinstance;
static int gs_exitcode;
typedef int (GSDLLAPI* GSAPI_REVISION)(gsapi_revision_t *pr, int len);
static GSAPI_REVISION ptr_gsapi_revision;
typedef int (GSDLLAPI* GSAPI_NEW_INSTANCE)(void **pinstance, void *caller_handle);
static GSAPI_NEW_INSTANCE ptr_gsapi_new_instance;
typedef void (GSDLLAPI* GSAPI_DELETE_INSTANCE)(void *instance);
static GSAPI_DELETE_INSTANCE ptr_gsapi_delete_instance;
typedef int (GSDLLAPI* GSAPI_SET_STDIO)(void *instance,
int (GSDLLCALLPTR stdin_fn)(void *caller_handle, char *buf, int len),
int (GSDLLCALLPTR stdout_fn)(void *caller_handle, const char *str, int len),
int (GSDLLCALLPTR stderr_fn)(void *caller_handle, const char *str, int len));
static GSAPI_SET_STDIO ptr_gsapi_set_stdio;
typedef int (GSDLLAPI* GSAPI_INIT_WITH_ARGS)(void *instance, int argc, char **argv);
static GSAPI_INIT_WITH_ARGS ptr_gsapi_init_with_args;
typedef int (GSDLLAPI* GSAPI_SET_ARG_ENCODING)(void *instance, int encoding);
static GSAPI_SET_ARG_ENCODING ptr_gsapi_set_arg_encoding;
typedef int (GSDLLAPI* GSAPI_EXIT)(void *instance);
static GSAPI_EXIT ptr_gsapi_exit;
typedef int (GSDLLAPI* GSAPI_RUN_STRING_BEGIN)(void *instance, int user_errors, int *pexit_code);
static GSAPI_RUN_STRING_BEGIN ptr_gsapi_run_string_begin;
typedef int (GSDLLAPI* GSAPI_RUN_STRING_CONTINUE)(void *instance, const char *str, unsigned int length, int user_errors, int *pexit_code);
static GSAPI_RUN_STRING_CONTINUE ptr_gsapi_run_string_continue;
typedef int (GSDLLAPI* GSAPI_RUN_STRING_END)(void *instance, int user_errors, int *pexit_code);
static GSAPI_RUN_STRING_END ptr_gsapi_run_string_end;
static uae_u8 **psbuffer;
static int psbuffers;
static LONG WINAPI ExceptionFilter (struct _EXCEPTION_POINTERS * pExceptionPointers, DWORD ec)
{
return EXCEPTION_EXECUTE_HANDLER;
}
static void freepsbuffers (void)
{
int i;
for (i = 0; i < psbuffers; i++)
free (psbuffer[i]);
free (psbuffer);
psbuffer = NULL;
psbuffers = 0;
}
static int openprinter_ps (void)
{
const TCHAR *gsargv[] = {
_T("-dNOPAUSE"), _T("-dBATCH"), _T("-dNOPAGEPROMPT"), _T("-dNOPROMPT"), _T("-dQUIET"), _T("-dNoCancel"),
_T("-sDEVICE=mswinpr2"), NULL
};
int gsargc, gsargc2, i;
TCHAR *tmpparms[100];
TCHAR tmp[MAX_DPATH];
char *gsparms[100];
if (ptr_gsapi_new_instance (&gsinstance, NULL) < 0)
return 0;
ptr_gsapi_set_arg_encoding(gsinstance, GS_ARG_ENCODING_UTF8);
cmdlineparser (currprefs.ghostscript_parameters, tmpparms, 100 - 10);
gsargc2 = 0;
gsparms[gsargc2++] = uutf8(_T("WinUAE"));
for (gsargc = 0; gsargv[gsargc]; gsargc++) {
gsparms[gsargc2++] = uutf8(gsargv[gsargc]);
}
for (i = 0; tmpparms[i]; i++)
gsparms[gsargc2++] = uutf8(tmpparms[i]);
if (currprefs.prtname[0]) {
_stprintf (tmp, _T("-sOutputFile=%%printer%%%s"), currprefs.prtname);
gsparms[gsargc2++] = uutf8(tmp);
}
if (postscript_print_debugging) {
for (i = 0; i < gsargc2; i++) {
TCHAR *parm = utf8u(gsparms[i]);
write_log (_T("GSPARM%d: '%s'\n"), i, parm);
xfree (parm);
xfree(gsparms[i]);
}
}
__try {
int rc = ptr_gsapi_init_with_args (gsinstance, gsargc2, gsparms);
for (i = 0; i < gsargc2; i++) {
xfree (gsparms[i]);
}
if (rc != 0) {
write_log (_T("GS failed, returncode %d\n"), rc);
return 0;
}
ptr_gsapi_run_string_begin (gsinstance, 0, &gs_exitcode);
} __except (ExceptionFilter (GetExceptionInformation (), GetExceptionCode ())) {
write_log (_T("GS crashed\n"));
return 0;
}
psmode = 1;
return 1;
}
static void prt_thread (void *p)
{
uae_u8 **buffers = (uae_u8**)p;
int err, cnt, ok;
ok = 1;
prt_running++;
prt_started = 1;
SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_BELOW_NORMAL);
if (load_ghostscript ()) {
if (openprinter_ps ()) {
write_log (_T("PostScript printing emulation started..\n"));
cnt = 0;
while (buffers[cnt]) {
uae_u8 *p = buffers[cnt];
err = ptr_gsapi_run_string_continue (gsinstance, (char*)p + 2, (p[0] << 8) | p[1], 0, &gs_exitcode);
if (err != e_NeedInput && err <= e_Fatal) {
ptr_gsapi_exit (gsinstance);
write_log (_T("PostScript parsing failed.\n"));
ok = 0;
break;
}
cnt++;
}
cnt = 0;
while (buffers[cnt]) {
xfree (buffers[cnt]);
cnt++;
}
xfree (buffers);
if (ok) {
write_log (_T("PostScript printing emulation finished..\n"));
ptr_gsapi_run_string_end (gsinstance, 0, &gs_exitcode);
}
} else {
write_log (_T("gsdllxx.dll failed to initialize\n"));
}
} else {
write_log (_T("gsdllxx.dll failed to load\n"));
}
unload_ghostscript ();
prt_running--;
}
static int doflushprinter (void)
{
if (prtopen == 0 && prtbufbytes < MIN_PRTBYTES) {
if (prtbufbytes > 0)
write_log (_T("PRINTER: %d bytes received, less than %d bytes, not printing.\n"), prtbufbytes, MIN_PRTBYTES);
prtbufbytes = 0;
return 0;
}
return 1;
}
static void openprinter (void);
static void flushprtbuf (void)
{
DWORD written = 0;
if (!prtbufbytes)
return;
if (postscript_print_debugging && prtdump)
zfile_fwrite (prtbuf, prtbufbytes, 1, prtdump);
if (currprefs.parallel_postscript_emulation) {
if (psmode) {
uae_u8 *p;
psbuffer = xrealloc (uae_u8*, psbuffer, (psbuffers + 2));
p = xmalloc (uae_u8, prtbufbytes + 2);
p[0] = prtbufbytes >> 8;
p[1] = prtbufbytes;
memcpy (p + 2, prtbuf, prtbufbytes);
psbuffer[psbuffers++] = p;
psbuffer[psbuffers] = NULL;
}
prtbufbytes = 0;
return;
} else if (prtbufbytes > 0) {
int pbyt = prtbufbytes;
if (currprefs.parallel_matrix_emulation >= PARALLEL_MATRIX_EPSON) {
int i;
if (!prtopen) {
if (!doflushprinter ())
return;
if (epson_init (currprefs.prtname, currprefs.parallel_matrix_emulation))
prtopen = 1;
}
for (i = 0; i < prtbufbytes; i++)
epson_printchar (prtbuf[i]);
} else {
if (hPrt == INVALID_HANDLE_VALUE) {
if (!doflushprinter ())
return;
openprinter ();
}
if (hPrt != INVALID_HANDLE_VALUE) {
if (WritePrinter (hPrt, prtbuf, pbyt, &written)) {
if (written != pbyt)
write_log (_T("PRINTER: Only wrote %d of %d bytes!\n"), written, pbyt);
} else {
write_log (_T("PRINTER: Couldn't write data!\n"));
}
}
}
}
prtbufbytes = 0;
}
void finishjob (void)
{
flushprtbuf ();
}
static void DoSomeWeirdPrintingStuff (uae_char val)
{
static uae_char prev[5];
memmove (prev, prev + 1, 3);
prev[3] = val;
prev[4] = 0;
if (currprefs.parallel_postscript_detection) {
if (psmode && val == 4) {
flushprtbuf ();
*prtbuf = val;
prtbufbytes = 1;
flushprtbuf ();
write_log (_T("PostScript end detected..\n"));
if (postscript_print_debugging) {
zfile_fclose (prtdump);
prtdump = NULL;
}
if (currprefs.parallel_postscript_emulation) {
prt_started = 0;
if (uae_start_thread (_T("postscript"), prt_thread, psbuffer, NULL)) {
while (!prt_started)
Sleep (5);
psbuffers = 0;
psbuffer = NULL;
}
} else {
closeprinter ();
}
freepsbuffers ();
return;
} else if (!psmode && !stricmp (prev, "%!PS")) {
if (postscript_print_debugging)
prtdump = zfile_fopen (_T("psdump.dat"), _T("wb"), 0);
psmode = 1;
psbuffer = xmalloc (uae_u8*, 1);
psbuffer[0] = 0;
psbuffers = 0;
strcpy (prtbuf, "%!PS");
prtbufbytes = strlen (prtbuf);
flushprtbuf ();
write_log (_T("PostScript start detected..\n"));
return;
}
}
if (prtbufbytes < PRTBUFSIZE) {
prtbuf[prtbufbytes++] = val;
} else {
flushprtbuf ();
*prtbuf = val;
prtbufbytes = 1;
}
}
int isprinter (void)
{
if (!currprefs.prtname[0])
return 0;
if (!_tcsncmp (currprefs.prtname, _T("LPT"), 3)) {
paraport_open (currprefs.prtname);
return -1;
}
return 1;
}
int isprinteropen (void)
{
if (prtopen || prtbufbytes > 0)
return 1;
return 0;
}
int load_ghostscript (void)
{
struct gsapi_revision_s r;
TCHAR path[MAX_DPATH];
TCHAR *s;
if (gsdll)
return 1;
_tcscpy (path, _T("gsdll32.dll"));
gsdll = WIN32_LoadLibrary (path);
if (!gsdll) {
if (GetEnvironmentVariable (_T("GS_DLL"), path, sizeof (path) / sizeof (TCHAR)))
gsdll = LoadLibrary (path);
}
if (!gsdll) {
HKEY key;
DWORD ret = RegOpenKeyEx (HKEY_LOCAL_MACHINE, _T("SOFTWARE\\AFPL Ghostscript"), 0, KEY_READ, &key);
if (ret != ERROR_SUCCESS)
ret = RegOpenKeyEx (HKEY_LOCAL_MACHINE, _T("SOFTWARE\\GPL Ghostscript"), 0, KEY_READ, &key);
if (ret == ERROR_SUCCESS) {
int idx = 0, cnt = 20;
TCHAR tmp1[MAX_DPATH];
while (cnt-- > 0) {
DWORD size1 = sizeof (tmp1) / sizeof (TCHAR);
FILETIME ft;
if (RegEnumKeyEx (key, idx, tmp1, &size1, NULL, NULL, NULL, &ft) == ERROR_SUCCESS) {
HKEY key2;
if (RegOpenKeyEx (key, tmp1, 0, KEY_READ, &key2) == ERROR_SUCCESS) {
DWORD type = REG_SZ;
DWORD size = sizeof (path) / sizeof (TCHAR);
if (RegQueryValueEx (key2, _T("GS_DLL"), 0, &type, (LPBYTE)path, &size) == ERROR_SUCCESS) {
gsdll = LoadLibrary (path);
}
RegCloseKey (key2);
if (gsdll)
break;
}
}
idx++;
}
RegCloseKey (key);
}
}
if (!gsdll)
return 0;
ptr_gsapi_revision = (GSAPI_REVISION)GetProcAddress (gsdll, "gsapi_revision");
if (!ptr_gsapi_revision) {
unload_ghostscript ();
write_log (_T("incompatible %s! (1)\n"), path);
return -1;
}
if (ptr_gsapi_revision(&r, sizeof(r))) {
unload_ghostscript ();
write_log (_T("incompatible %s! (2)\n"), path);
return -2;
}
ptr_gsapi_new_instance = (GSAPI_NEW_INSTANCE)GetProcAddress (gsdll, "gsapi_new_instance");
ptr_gsapi_delete_instance = (GSAPI_DELETE_INSTANCE)GetProcAddress (gsdll, "gsapi_delete_instance");
ptr_gsapi_set_stdio = (GSAPI_SET_STDIO)GetProcAddress (gsdll, "gsapi_set_stdio");
ptr_gsapi_exit = (GSAPI_EXIT)GetProcAddress (gsdll, "gsapi_exit");
ptr_gsapi_run_string_begin = (GSAPI_RUN_STRING_BEGIN)GetProcAddress (gsdll, "gsapi_run_string_begin");
ptr_gsapi_run_string_continue = (GSAPI_RUN_STRING_CONTINUE)GetProcAddress (gsdll, "gsapi_run_string_continue");
ptr_gsapi_run_string_end = (GSAPI_RUN_STRING_END)GetProcAddress (gsdll, "gsapi_run_string_end");
ptr_gsapi_set_arg_encoding = (GSAPI_SET_ARG_ENCODING)GetProcAddress(gsdll, "gsapi_set_arg_encoding");
ptr_gsapi_init_with_args = (GSAPI_INIT_WITH_ARGS)GetProcAddress(gsdll, "gsapi_init_with_args");
if (!ptr_gsapi_new_instance || !ptr_gsapi_delete_instance || !ptr_gsapi_exit ||
!ptr_gsapi_run_string_begin || !ptr_gsapi_run_string_continue || !ptr_gsapi_run_string_end ||
!ptr_gsapi_set_arg_encoding || !ptr_gsapi_init_with_args) {
unload_ghostscript ();
write_log (_T("incompatible %s! (3)\n"), path);
return -3;
}
s = au (r.product);
write_log (_T("%s: %s rev %d initialized\n"), path, s, r.revision);
xfree (s);
return 1;
}
void unload_ghostscript (void)
{
if (gsinstance) {
ptr_gsapi_exit (gsinstance);
ptr_gsapi_delete_instance (gsinstance);
}
gsinstance = NULL;
if (gsdll)
FreeLibrary (gsdll);
gsdll = NULL;
psmode = 0;
}
static void openprinter (void)
{
DOC_INFO_1 DocInfo;
static int first;
closeprinter ();
if (!currprefs.prtname[0])
return;
if (currprefs.parallel_postscript_emulation) {
prtopen = 1;
return;
} else if (currprefs.parallel_matrix_emulation >= PARALLEL_MATRIX_EPSON) {
epson_init (currprefs.prtname, currprefs.parallel_matrix_emulation);
} else if (hPrt == INVALID_HANDLE_VALUE) {
flushprtbuf ();
if (OpenPrinter (currprefs.prtname, &hPrt, NULL)) {
// Fill in the structure with info about this "document."
DocInfo.pDocName = _T("WinUAE Document");
DocInfo.pOutputFile = NULL;
DocInfo.pDatatype = (currprefs.parallel_matrix_emulation || currprefs.parallel_postscript_detection) ? _T("TEXT") : _T("RAW");
// Inform the spooler the document is beginning.
if ((dwJob = StartDocPrinter (hPrt, 1, (LPBYTE)&DocInfo)) == 0) {
ClosePrinter (hPrt );
hPrt = INVALID_HANDLE_VALUE;
} else if (StartPagePrinter (hPrt)) {
prtopen = 1;
}
} else {
hPrt = INVALID_HANDLE_VALUE; // Stupid bug in Win32, where OpenPrinter fails, but hPrt ends up being zero
}
}
if (hPrt != INVALID_HANDLE_VALUE) {
write_log (_T("PRINTER: Opening printer \"%s\" with handle 0x%x.\n"), currprefs.prtname, hPrt);
} else if (*currprefs.prtname) {
write_log (_T("PRINTER: ERROR - Couldn't open printer \"%s\" for output.\n"), currprefs.prtname);
}
}
void flushprinter (void)
{
if (!doflushprinter ())
return;
flushprtbuf ();
closeprinter ();
}
void closeprinter (void)
{
#ifdef PRINT_DUMP
zfile_fclose (prtdump);
#endif
parflush = 0;
psmode = 0;
if (hPrt != INVALID_HANDLE_VALUE) {
EndPagePrinter (hPrt);
EndDocPrinter (hPrt);
ClosePrinter (hPrt);
hPrt = INVALID_HANDLE_VALUE;
write_log (_T("PRINTER: Closing printer.\n"));
}
if (currprefs.parallel_postscript_emulation)
prtopen = 1;
else
prtopen = 0;
if (prt_running) {
write_log (_T("waiting for printing to finish...\n"));
while (prt_running)
Sleep (10);
}
freepsbuffers ();
epson_close ();
prtbufbytes = 0;
}
void doprinter (uae_u8 val)
{
parflush = 0;
DoSomeWeirdPrintingStuff (val);
}
struct uaeserialdatawin32
{
HANDLE hCom;
HANDLE evtr, evtw, evtt, evtwce;
OVERLAPPED olr, olw, olwce;
int writeactive;
void *readdata, *writedata;
volatile int threadactive;
uae_sem_t change_sem, sync_sem;
void *user;
};
int uaeser_getdatalength (void)
{
return sizeof (struct uaeserialdatawin32);
}
static void uaeser_initdata (void *vsd, void *user)
{
struct uaeserialdatawin32 *sd = (struct uaeserialdatawin32*)vsd;
memset (sd, 0, sizeof (struct uaeserialdatawin32));
sd->hCom = INVALID_HANDLE_VALUE;
sd->evtr = sd->evtw = sd->evtt = sd->evtwce = 0;
sd->user = user;
}
int uaeser_query (void *vsd, uae_u16 *status, uae_u32 *pending)
{
struct uaeserialdatawin32 *sd = (struct uaeserialdatawin32*)vsd;
DWORD err, modem;
COMSTAT ComStat;
uae_u16 s = 0;
if (!ClearCommError (sd->hCom, &err, &ComStat))
return 0;
*pending = ComStat.cbInQue;
if (status) {
s |= (err & CE_BREAK) ? (1 << 10) : 0;
s |= (err & CE_RXOVER) ? (1 << 8) : 0;
if (GetCommModemStatus (sd->hCom, &modem)) {
s |= (modem & MS_CTS_ON) ? 0 : (1 << 4);
s |= (modem & MS_DSR_ON) ? 0 : (1 << 7);
s |= (modem & MS_RING_ON) ? (1 << 2) : 0;
}
*status = s;
}
return 1;
}
int uaeser_break (void *vsd, int brklen)
{
struct uaeserialdatawin32 *sd = (struct uaeserialdatawin32*)vsd;
if (!SetCommBreak (sd->hCom))
return 0;
Sleep (brklen / 1000);
ClearCommBreak (sd->hCom);
return 1;
}
int uaeser_setparams (void *vsd, int baud, int rbuffer, int bits, int sbits, int rtscts, int parity, uae_u32 xonxoff)
{
struct uaeserialdatawin32 *sd = (struct uaeserialdatawin32*)vsd;
DCB dcb;
memset (&dcb, 0, sizeof (dcb));
dcb.DCBlength = sizeof (DCB);
if (!GetCommState (sd->hCom, &dcb))
return 5;
dcb.fBinary = TRUE;
dcb.BaudRate = baud;
dcb.ByteSize = bits;
dcb.Parity = parity == 0 ? NOPARITY : (parity == 1 ? ODDPARITY : EVENPARITY);
dcb.fParity = FALSE;
dcb.StopBits = sbits == 1 ? ONESTOPBIT : TWOSTOPBITS;
dcb.fDsrSensitivity = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
if (rtscts) {
dcb.fOutxCtsFlow = TRUE;
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
} else {
dcb.fRtsControl = RTS_CONTROL_ENABLE;
dcb.fOutxCtsFlow = FALSE;
}
dcb.fTXContinueOnXoff = FALSE;
if (xonxoff & 1) {
dcb.fOutX = TRUE;
dcb.fInX = TRUE;
dcb.XonChar = (xonxoff >> 8) & 0xff;
dcb.XoffChar = (xonxoff >> 16) & 0xff;
} else {
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
}
dcb.fErrorChar = FALSE;
dcb.fNull = FALSE;
dcb.fAbortOnError = FALSE;
if (!SetCommState (sd->hCom, &dcb)) {
write_log (_T("uaeserial: SetCommState() failed %d\n"), GetLastError());
return 5;
}
SetupComm (sd->hCom, rbuffer, rbuffer);
return 0;
}
static void startwce(struct uaeserialdatawin32 *sd, DWORD *evtmask)
{
SetEvent(sd->evtwce);
WaitCommEvent(sd->hCom, evtmask, &sd->olwce);
}
static void uaeser_trap_thread (void *arg)
{
struct uaeserialdatawin32 *sd = (struct uaeserialdatawin32*)arg;
HANDLE handles[4];
int cnt;
DWORD evtmask, actual;
uae_set_thread_priority (NULL, 1);
sd->threadactive = 1;
uae_sem_post (&sd->sync_sem);
startwce(sd, &evtmask);
while (sd->threadactive == 1) {
int sigmask = 0;
uae_sem_wait (&sd->change_sem);
if (WaitForSingleObject(sd->evtwce, 0) == WAIT_OBJECT_0) {
if (evtmask & EV_RXCHAR)
sigmask |= 1;
if ((evtmask & EV_TXEMPTY) && !sd->writeactive)
sigmask |= 2;
if (evtmask & EV_BREAK)
sigmask |= 4;
startwce(sd, &evtmask);
}
cnt = 0;
handles[cnt++] = sd->evtt;
handles[cnt++] = sd->evtwce;
if (sd->writeactive) {
if (GetOverlappedResult (sd->hCom, &sd->olw, &actual, FALSE)) {
sd->writeactive = 0;
sigmask |= 2;
} else {
handles[cnt++] = sd->evtw;
}
}
if (!sd->writeactive)
sigmask |= 2;
uaeser_signal (sd->user, sigmask | 1);
uae_sem_post (&sd->change_sem);
WaitForMultipleObjects(cnt, handles, FALSE, INFINITE);
}
sd->threadactive = 0;
uae_sem_post (&sd->sync_sem);
}
void uaeser_trigger (void *vsd)
{
struct uaeserialdatawin32 *sd = (struct uaeserialdatawin32*)vsd;
SetEvent (sd->evtt);
}
int uaeser_write (void *vsd, uae_u8 *data, uae_u32 len)
{
struct uaeserialdatawin32 *sd = (struct uaeserialdatawin32*)vsd;
int ret = 1;
if (!WriteFile (sd->hCom, data, len, NULL, &sd->olw)) {
sd->writeactive = 1;
if (GetLastError() != ERROR_IO_PENDING) {
ret = 0;
sd->writeactive = 0;
}
}
SetEvent (sd->evtt);
return ret;
}
int uaeser_read (void *vsd, uae_u8 *data, uae_u32 len)
{
struct uaeserialdatawin32 *sd = (struct uaeserialdatawin32*)vsd;
int ret = 1;
DWORD err;
COMSTAT ComStat;
if (!ClearCommError (sd->hCom, &err, &ComStat))
return 0;
if (err & EV_BREAK)
return -1;
if (len > ComStat.cbInQue)
return 0;
if (!ReadFile (sd->hCom, data, len, NULL, &sd->olr)) {
if (GetLastError() == ERROR_IO_PENDING)
WaitForSingleObject(sd->evtr, INFINITE);
else
ret = 0;
}
SetEvent (sd->evtt);
return ret;
}
void uaeser_clearbuffers (void *vsd)
{
struct uaeserialdatawin32 *sd = (struct uaeserialdatawin32*)vsd;
PurgeComm (sd->hCom, PURGE_TXCLEAR | PURGE_RXCLEAR);
}
int uaeser_open (void *vsd, void *user, int unit)
{
struct uaeserialdatawin32 *sd = (struct uaeserialdatawin32*)vsd;
TCHAR buf[256];
COMMTIMEOUTS CommTimeOuts;
sd->user = user;
_stprintf (buf, _T("\\\\.\\COM%d"), unit);
sd->evtr = CreateEvent (NULL, TRUE, FALSE, NULL);
sd->evtw = CreateEvent (NULL, TRUE, FALSE, NULL);
sd->evtt = CreateEvent (NULL, FALSE, FALSE, NULL);
sd->evtwce = CreateEvent (NULL, TRUE, FALSE, NULL);
if (!sd->evtt || !sd->evtw || !sd->evtt || !sd->evtwce)
goto end;
sd->olr.hEvent = sd->evtr;
sd->olw.hEvent = sd->evtw;
sd->olwce.hEvent = sd->evtwce;
sd->hCom = CreateFile (buf, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, NULL);
if (sd->hCom == INVALID_HANDLE_VALUE) {
_stprintf (buf, _T("\\.\\\\COM%d"), unit);
sd->hCom = CreateFile (buf, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, NULL);
if (sd->hCom == INVALID_HANDLE_VALUE) {
write_log (_T("UAESER: '%s' failed to open, err=%d\n"), buf, GetLastError());
goto end;
}
}
uae_sem_init (&sd->sync_sem, 0, 0);
uae_sem_init (&sd->change_sem, 0, 1);
uae_start_thread (_T("uaeserial_win32"), uaeser_trap_thread, sd, NULL);
uae_sem_wait (&sd->sync_sem);
CommTimeOuts.ReadIntervalTimeout = 0;
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts (sd->hCom, &CommTimeOuts);
SetCommMask (sd->hCom, EV_RXCHAR | EV_TXEMPTY | EV_BREAK);
return 1;
end:
uaeser_close (sd);
return 0;
}
void uaeser_close (void *vsd)
{
struct uaeserialdatawin32 *sd = (struct uaeserialdatawin32*)vsd;
if (sd->threadactive) {
sd->threadactive = -1;
SetEvent (sd->evtt);
while (sd->threadactive)
Sleep(10);
CloseHandle (sd->evtt);
}
if (sd->hCom != INVALID_HANDLE_VALUE)
CloseHandle(sd->hCom);
if (sd->evtr)
CloseHandle(sd->evtr);
if (sd->evtw)
CloseHandle(sd->evtw);
if (sd->evtwce)
CloseHandle(sd->evtwce);
uaeser_initdata (sd, sd->user);
}
static HANDLE hCom = INVALID_HANDLE_VALUE;
static DCB dcb;
static DWORD fDtrControl = DTR_CONTROL_DISABLE, fRtsControl = RTS_CONTROL_DISABLE;
static HANDLE writeevent, readevent;
#define SERIAL_WRITE_BUFFER 100
#define SERIAL_READ_BUFFER 100
static uae_u8 outputbuffer[SERIAL_WRITE_BUFFER];
static uae_u8 outputbufferout[SERIAL_WRITE_BUFFER];
static uae_u8 inputbuffer[SERIAL_READ_BUFFER];
static int datainoutput;
static int dataininput, dataininputcnt;
static OVERLAPPED writeol, readol;
static int writepending;
static bool breakpending;
static WSADATA wsadata;
static SOCKET serialsocket = INVALID_SOCKET;
static SOCKET serialconn = INVALID_SOCKET;
static PADDRINFOW socketinfo;
static char socketaddr[sizeof SOCKADDR_INET];
static BOOL tcpserial;
static bool tcp_is_connected (void)
{
socklen_t sa_len = sizeof SOCKADDR_INET;
if (serialsocket == INVALID_SOCKET)
return false;
if (serialconn == INVALID_SOCKET) {
struct timeval tv;
fd_set fd;
tv.tv_sec = 0;
tv.tv_usec = 0;
fd.fd_array[0] = serialsocket;
fd.fd_count = 1;
if (select (1, &fd, NULL, NULL, &tv)) {
serialconn = accept (serialsocket, (struct sockaddr*)socketaddr, &sa_len);
if (serialconn != INVALID_SOCKET)
write_log (_T("SERIAL_TCP: connection accepted\n"));
}
}
return serialconn != INVALID_SOCKET;
}
static void tcp_disconnect (void)
{
if (serialconn == INVALID_SOCKET)
return;
closesocket (serialconn);
serialconn = INVALID_SOCKET;
write_log (_T("SERIAL_TCP: disconnect\n"));
}
static void closetcp (void)
{
if (serialconn != INVALID_SOCKET)
closesocket (serialconn);
serialconn = INVALID_SOCKET;
if (serialsocket != INVALID_SOCKET)
closesocket (serialsocket);
serialsocket = INVALID_SOCKET;
if (socketinfo)
FreeAddrInfoW (socketinfo);
socketinfo = NULL;
WSACleanup ();
}
static int opentcp (const TCHAR *sername)
{
int err;
TCHAR *port, *name;
const TCHAR *p;
bool waitmode = false;
const int one = 1;
const struct linger linger_1s = { 1, 1 };
if (WSAStartup (MAKEWORD (2, 2), &wsadata)) {
DWORD lasterror = WSAGetLastError ();
write_log (_T("SERIAL_TCP: can't open '%s', error %d\n"), sername, lasterror);
return 0;
}
name = my_strdup (sername);
port = NULL;
p = _tcschr (sername, ':');
if (p) {
name[p - sername] = 0;
port = my_strdup (p + 1);
const TCHAR *p2 = _tcschr (port, '/');
if (p2) {
port[p2 - port] = 0;
if (!_tcsicmp (p2 + 1, _T("wait")))
waitmode = true;
}
}
if (port && port[0] == 0) {
xfree (port);
port = NULL;
}
if (!port)
port = my_strdup (_T("1234"));
err = GetAddrInfoW (name, port, NULL, &socketinfo);
if (err < 0) {
write_log (_T("SERIAL_TCP: GetAddrInfoW() failed, %s:%s: %d\n"), name, port, WSAGetLastError ());
goto end;
}
serialsocket = socket (socketinfo->ai_family, socketinfo->ai_socktype, socketinfo->ai_protocol);
if (serialsocket == INVALID_SOCKET) {
write_log(_T("SERIAL_TCP: socket() failed, %s:%s: %d\n"), name, port, WSAGetLastError ());
goto end;
}
err = bind (serialsocket, socketinfo->ai_addr, socketinfo->ai_addrlen);
if (err < 0) {
write_log(_T("SERIAL_TCP: bind() failed, %s:%s: %d\n"), name, port, WSAGetLastError ());
goto end;
}
err = listen (serialsocket, 1);
if (err < 0) {
write_log(_T("SERIAL_TCP: listen() failed, %s:%s: %d\n"), name, port, WSAGetLastError ());
goto end;
}
err = setsockopt (serialsocket, SOL_SOCKET, SO_LINGER, (char*)&linger_1s, sizeof linger_1s);
if (err < 0) {
write_log(_T("SERIAL_TCP: setsockopt(SO_LINGER) failed, %s:%s: %d\n"), name, port, WSAGetLastError ());
goto end;
}
err = setsockopt (serialsocket, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof one);
if (err < 0) {
write_log(_T("SERIAL_TCP: setsockopt(SO_REUSEADDR) failed, %s:%s: %d\n"), name, port, WSAGetLastError ());
goto end;
}
if (waitmode) {
while (tcp_is_connected () == false) {
Sleep (1000);
write_log (_T("SERIAL_TCP: waiting for connect...\n"));
}
}
xfree (port);
xfree (name);
tcpserial = TRUE;
return 1;
end:
xfree (port);
xfree (name);
closetcp ();
return 0;
}
int openser (const TCHAR *sername)
{
COMMTIMEOUTS CommTimeOuts;
if (!_tcsnicmp (sername, _T("TCP://"), 6)) {
return opentcp (sername + 6);
}
if (!_tcsnicmp (sername, _T("TCP:"), 4)) {
return opentcp (sername + 4);
}
if (!(readevent = CreateEvent (NULL, TRUE, FALSE, NULL))) {
write_log (_T("SERIAL: Failed to create r event!\n"));
return 0;
}
readol.hEvent = readevent;
if (!(writeevent = CreateEvent (NULL, TRUE, FALSE, NULL))) {
write_log (_T("SERIAL: Failed to create w event!\n"));
return 0;
}
SetEvent (writeevent);
writeol.hEvent = writeevent;
uartbreak = 0;
hCom = CreateFile (sername, GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if (hCom == INVALID_HANDLE_VALUE) {
write_log (_T("SERIAL: failed to open '%s' err=%d\n"), sername, GetLastError());
closeser ();
return 0;
}
SetCommMask (hCom, EV_RXFLAG | EV_BREAK);
SetupComm (hCom, 65536, 128);
PurgeComm (hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts (hCom, &CommTimeOuts);
dcb.DCBlength = sizeof (DCB);
GetCommState (hCom, &dcb);
dcb.fBinary = TRUE;
dcb.BaudRate = 9600;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.fParity = FALSE;
dcb.StopBits = currprefs.serial_stopbits;
dcb.fDsrSensitivity = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = fDtrControl;
if (currprefs.serial_hwctsrts) {
dcb.fOutxCtsFlow = TRUE;
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
} else {
dcb.fRtsControl = fRtsControl;
dcb.fOutxCtsFlow = FALSE;
}
dcb.fTXContinueOnXoff = FALSE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fErrorChar = FALSE;
dcb.fNull = FALSE;
dcb.fAbortOnError = FALSE;
if (SetCommState (hCom, &dcb)) {
write_log (_T("SERIAL: Using %s CTS/RTS=%d\n"), sername, currprefs.serial_hwctsrts);
return 1;
}
write_log (_T("SERIAL: serial driver didn't accept new parameters\n"));
closeser();
return 0;
}
void closeser (void)
{
if (tcpserial) {
closetcp ();
tcpserial = FALSE;
}
if (hCom != INVALID_HANDLE_VALUE) {
CloseHandle (hCom);
hCom = INVALID_HANDLE_VALUE;
}
if (midi_ready) {
extern uae_u16 serper;
Midi_Close ();
//need for camd Midi Stuff(it close midi and reopen it but serial.c think the baudrate
//is the same and do not open midi), so setting serper to different value helps
serper = 0x30;
}
if(writeevent)
CloseHandle(writeevent);
writeevent = 0;
if(readevent)
CloseHandle(readevent);
readevent = 0;
uartbreak = 0;
}
static void outser (void)
{
if (datainoutput <= 0)
return;
DWORD v = WaitForSingleObject (writeevent, 0);
if (v == WAIT_OBJECT_0) {
DWORD actual;
memcpy (outputbufferout, outputbuffer, datainoutput);
WriteFile (hCom, outputbufferout, datainoutput, &actual, &writeol);
datainoutput = 0;
}
}
void writeser_flush(void)
{
outser();
}
void writeser (int c)
{
#if 0
write_log(_T("writeser %04X (buf=%d)\n"), c, datainoutput);
#endif
if (tcpserial) {
if (tcp_is_connected ()) {
char buf[1];
buf[0] = (char)c;
if (send (serialconn, buf, 1, 0) != 1) {
tcp_disconnect ();
}
}
} else if (midi_ready) {
BYTE outchar = (BYTE)c;
Midi_Parse (midi_output, &outchar);
} else {
if (hCom == INVALID_HANDLE_VALUE || !currprefs.use_serial)
return;
if (datainoutput + 1 < sizeof (outputbuffer)) {
outputbuffer[datainoutput++] = c;
} else {
write_log (_T("serial output buffer overflow, data will be lost\n"));
datainoutput = 0;
}
outser ();
}
}
int checkserwrite (int spaceneeded)
{
if (hCom == INVALID_HANDLE_VALUE || !currprefs.use_serial)
return 1;
if (midi_ready) {
return 1;
} else {
outser ();
if (datainoutput + spaceneeded >= sizeof (outputbuffer))
return 0;
}
return 1;
}
void flushser(void)
{
if (!tcpserial && !midi_ready && hCom) {
COMSTAT ComStat;
DWORD dwErrorFlags;
ClearCommError(hCom, &dwErrorFlags, &ComStat);
PurgeComm(hCom, PURGE_RXCLEAR);
} else {
while (readseravail(NULL)) {
int data;
if (readser(&data) <= 0)
break;
}
}
}
int readseravail(bool *breakcond)
{
COMSTAT ComStat;
DWORD dwErrorFlags;
if (breakcond)
*breakcond = false;
if (tcpserial) {
if (tcp_is_connected ()) {
struct timeval tv;
fd_set fd;
tv.tv_sec = 0;
tv.tv_usec = 0;
fd.fd_array[0] = serialconn;
fd.fd_count = 1;
int err = select (1, &fd, NULL, NULL, &tv);
if (err == SOCKET_ERROR) {
tcp_disconnect ();
return 0;
}
if (err > 0)
return 1;
}
return 0;
} else if (midi_ready) {
if (ismidibyte ())
return 1;
} else {
if (!currprefs.use_serial)
return 0;
if (dataininput > dataininputcnt)
return 1;
if (hCom != INVALID_HANDLE_VALUE) {
ClearCommError (hCom, &dwErrorFlags, &ComStat);
if (breakcond && ((dwErrorFlags & CE_BREAK) || breakpending)) {
*breakcond = true;
breakpending = false;
}
if (ComStat.cbInQue > 0)
return ComStat.cbInQue;
}
}
return 0;
}
int readser (int *buffer)
{
COMSTAT ComStat;
DWORD dwErrorFlags;
DWORD actual;
if (tcpserial) {
if (tcp_is_connected ()) {
char buf[1];
buf[0] = 0;
int err = recv (serialconn, buf, 1, 0);
if (err == 1) {
*buffer = buf[0];
//write_log(_T(" %02X "), buf[0]);
return 1;
} else {
tcp_disconnect ();
}
}
return 0;
} else if (midi_ready) {
*buffer = getmidibyte ();
if (*buffer < 0)
return 0;
return 1;
} else {
if (!currprefs.use_serial)
return 0;
if (dataininput > dataininputcnt) {
*buffer = inputbuffer[dataininputcnt++];
return 1;
}
dataininput = 0;
dataininputcnt = 0;
if (hCom != INVALID_HANDLE_VALUE) {
/* only try to read number of bytes in queue */
ClearCommError (hCom, &dwErrorFlags, &ComStat);
if (dwErrorFlags & CE_BREAK)
breakpending = true;
if (ComStat.cbInQue) {
int len = ComStat.cbInQue;
if (len > sizeof (inputbuffer))
len = sizeof (inputbuffer);
if (!ReadFile (hCom, inputbuffer, len, &actual, &readol)) {
if (GetLastError() == ERROR_IO_PENDING)
WaitForSingleObject (&readol, INFINITE);
else
return 0;
}
dataininput = actual;
dataininputcnt = 0;
if (actual == 0)
return 0;
return readser (buffer);
}
}
}
return 0;
}
void serialuartbreak (int v)
{
if (hCom == INVALID_HANDLE_VALUE || !currprefs.use_serial)
return;
if (v)
EscapeCommFunction (hCom, SETBREAK);
else
EscapeCommFunction (hCom, CLRBREAK);
}
void getserstat (int *pstatus)
{
DWORD err;
DWORD stat;
int status = 0;
*pstatus = 0;
if (hCom == INVALID_HANDLE_VALUE || !currprefs.use_serial)
return;
GetCommModemStatus (hCom, &stat);
if (stat & MS_CTS_ON)
status |= TIOCM_CTS;
if (stat & MS_RLSD_ON)
status |= TIOCM_CAR;
if (stat & MS_DSR_ON)
status |= TIOCM_DSR;
if (stat & MS_RING_ON)
status |= TIOCM_RI;
*pstatus = status;
}
void setserstat (int mask, int onoff)
{
if (mask & TIOCM_DTR) {
if (currprefs.use_serial && hCom != INVALID_HANDLE_VALUE) {
EscapeCommFunction(hCom, onoff ? SETDTR : CLRDTR);
}
fDtrControl = onoff ? DTR_CONTROL_ENABLE : DTR_CONTROL_DISABLE;
}
if (!currprefs.serial_hwctsrts) {
if (mask & TIOCM_RTS) {
if (currprefs.use_serial && hCom != INVALID_HANDLE_VALUE) {
EscapeCommFunction(hCom, onoff ? SETRTS : CLRRTS);
}
fRtsControl = onoff ? RTS_CONTROL_ENABLE : RTS_CONTROL_DISABLE;
}
}
}
int setbaud (long baud)
{
if(baud == 31400 && currprefs.win32_midioutdev >= -1) {
/* MIDI baud-rate */
if (!midi_ready) {
if (Midi_Open())
write_log (_T("Midi enabled\n"));
}
return 1;
} else {
if (midi_ready) {
Midi_Close();
}
if (!currprefs.use_serial)
return 1;
if (hCom != INVALID_HANDLE_VALUE) {
dcb.BaudRate = baud;
if (!currprefs.serial_hwctsrts) {
dcb.fRtsControl = fRtsControl;
} else {
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
}
dcb.fDtrControl = fDtrControl;
write_log(_T("SERIAL: baud rate %d. DTR=%d RTS=%d\n"), baud, dcb.fDtrControl, dcb.fRtsControl);
if (!SetCommState (hCom, &dcb)) {
write_log (_T("SERIAL: Error setting baud rate %d!\n"), baud);
return 0;
}
}
}
return 1;
}
void initparallel (void)
{
if (uae_boot_rom_type) {
uaecptr a = here (); //this install the ahisound
org (rtarea_base + 0xFFC0);
calltrap (deftrapres (ahi_demux, 0, _T("ahi_winuae")));
dw (RTS);
org (a);
init_ahi_v2 ();
}
}
int flashscreen;
void doflashscreen (void)
{
flashscreen = 10;
init_colors(0);
picasso_refresh(0);
reset_drawing ();
//flush_screen (gfxvidinfo.outbuffer, 0, 0);
}
void hsyncstuff (void)
//only generate Interrupts when
//writebuffer is complete flushed
//check state of lwin rwin
{
static int keycheck = 0;
#ifdef AHI
{ //begin ahi_sound
static int count;
if (ahi_on) {
count++;
//15625/count freebuffer check
if(count > ahi_pollrate) {
ahi_updatesound (1);
count = 0;
}
}
} //end ahi_sound
#endif
#ifdef PARALLEL_PORT
keycheck++;
if(keycheck >= 1000)
{
if (prtopen)
flushprtbuf ();
{
if (flashscreen > 0) {
flashscreen--;
if (flashscreen == 0) {
init_colors(0);
reset_drawing ();
picasso_refresh(0);
//flush_screen (gfxvidinfo.outbuffer, 0, 0);
}
}
}
keycheck = 0;
}
if (currprefs.parallel_autoflush_time && !currprefs.parallel_postscript_detection) {
parflush++;
if (parflush / ((currprefs.ntscmode ? MAXVPOS_NTSC : MAXVPOS_PAL) * MAXHPOS_PAL / maxhpos) >= currprefs.parallel_autoflush_time * 50) {
flushprinter ();
parflush = 0;
}
}
#endif
}
const static GUID GUID_DEVINTERFACE_PARALLEL = {0x97F76EF0,0xF883,0x11D0,
{0xAF,0x1F,0x00,0x00,0xF8,0x00,0x84,0x5C}};
static const GUID serportsguids[] =
{
GUID_DEVINTERFACE_COMPORT,
// GUID_DEVINTERFACE_MODEM
{ 0x2C7089AA, 0x2E0E, 0x11D1, { 0xB1, 0x14, 0x00, 0xC0, 0x4F, 0xC2, 0xAA, 0xE4} }
};
static const GUID parportsguids[] =
{
GUID_DEVINTERFACE_PARALLEL
};
static int enumports_2 (struct serparportinfo **pi, int cnt, bool parport)
{
// Create a device information set that will be the container for
// the device interfaces.
HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
SP_DEVICE_INTERFACE_DETAIL_DATA *pDetData = NULL;
SP_DEVICE_INTERFACE_DATA ifcData;
DWORD dwDetDataSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA) + 256 * sizeof (TCHAR);
const GUID *guids = parport ? parportsguids : serportsguids;
int guidcnt = parport ? sizeof(parportsguids)/sizeof(parportsguids[0]) : sizeof(serportsguids)/sizeof(serportsguids[0]);
for (int guididx = 0; guididx < guidcnt; guididx++) {
hDevInfo = SetupDiGetClassDevs (&guids[guididx], NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if(hDevInfo == INVALID_HANDLE_VALUE)
continue;
// Enumerate the serial ports
pDetData = (SP_DEVICE_INTERFACE_DETAIL_DATA*)xmalloc (uae_u8, dwDetDataSize);
// This is required, according to the documentation. Yes,
// it's weird.
ifcData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);
pDetData->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA);
BOOL bOk = TRUE;
for (int ii = 0; bOk; ii++) {
bOk = SetupDiEnumDeviceInterfaces (hDevInfo, NULL, &guids[guididx], ii, &ifcData);
if (bOk) {
// Got a device. Get the details.
SP_DEVINFO_DATA devdata = { sizeof (SP_DEVINFO_DATA)};
bOk = SetupDiGetDeviceInterfaceDetail (hDevInfo,
&ifcData, pDetData, dwDetDataSize, NULL, &devdata);
if (bOk) {
// Got a path to the device. Try to get some more info.
TCHAR fname[256];
TCHAR desc[256];
BOOL bSuccess = SetupDiGetDeviceRegistryProperty (
hDevInfo, &devdata, SPDRP_FRIENDLYNAME, NULL,
(PBYTE)fname, sizeof (fname), NULL);
bSuccess = bSuccess && SetupDiGetDeviceRegistryProperty (
hDevInfo, &devdata, SPDRP_DEVICEDESC, NULL,
(PBYTE)desc, sizeof (desc), NULL);
if (bSuccess && cnt < MAX_SERPAR_PORTS) {
TCHAR *p;
pi[cnt] = xcalloc (struct serparportinfo, 1);
pi[cnt]->dev = my_strdup (pDetData->DevicePath);
pi[cnt]->name = my_strdup (fname);
p = _tcsstr (fname, parport ? _T("(LPT") : _T("(COM"));
if (p && (p[5] == ')' || p[6] == ')')) {
pi[cnt]->cfgname = xmalloc (TCHAR, 100);
if (isdigit(p[5]))
_stprintf (pi[cnt]->cfgname, parport ? _T("LPT%c%c") : _T("COM%c%c"), p[4], p[5]);
else
_stprintf (pi[cnt]->cfgname, parport ? _T("LPT%c") : _T("COM%c"), p[4]);
} else {
pi[cnt]->cfgname = my_strdup (pDetData->DevicePath);
}
write_log (_T("%s: '%s' = '%s' = '%s'\n"), parport ? _T("PARPORT") : _T("SERPORT"), pi[cnt]->name, pi[cnt]->cfgname, pi[cnt]->dev);
cnt++;
}
} else {
write_log (_T("SetupDiGetDeviceInterfaceDetail failed, err=%d"), GetLastError ());
break;
}
} else {
DWORD err = GetLastError ();
if (err != ERROR_NO_MORE_ITEMS) {
write_log (_T("SetupDiEnumDeviceInterfaces failed, err=%d"), err);
break;
}
}
}
xfree(pDetData);
if (hDevInfo != INVALID_HANDLE_VALUE)
SetupDiDestroyDeviceInfoList (hDevInfo);
}
return cnt;
}
static struct serparportinfo *parports[MAX_SERPAR_PORTS];
int enumserialports (void)
{
int cnt, i, j;
TCHAR name[256];
DWORD size = sizeof (COMMCONFIG);
TCHAR devname[1000];
write_log (_T("Serial port enumeration..\n"));
cnt = 0;
#ifdef SERIAL_ENET
comports[cnt].dev = my_strdup (_T("ENET:H"));
comports[cnt].cfgname = my_strdup (comports[0].dev);
comports[cnt].name = my_strdup (_T("NET (host)"));
cnt++;
comports[cnt].dev = my_strdup (_T("ENET:L"));
comports[cnt].cfgname = my_strdup (comports[1].dev);
comports[cnt].name = my_strdup (_T("NET (client)"));
cnt++;
#endif
cnt = enumports_2 (comports, cnt, false);
j = 0;
for (i = 0; i < 10; i++) {
_stprintf (name, _T("COM%d"), i);
if (!QueryDosDevice (name, devname, sizeof devname / sizeof (TCHAR)))
continue;
for(j = 0; j < cnt; j++) {
if (!_tcscmp (comports[j]->cfgname, name))
break;
}
if (j == cnt) {
if (cnt >= MAX_SERPAR_PORTS)
break;
comports[j] = xcalloc(struct serparportinfo, 1);
comports[j]->dev = xmalloc (TCHAR, 100);
_stprintf (comports[cnt]->dev, _T("\\.\\\\%s"), name);
comports[j]->cfgname = my_strdup (name);
comports[j]->name = my_strdup (name);
write_log (_T("SERPORT: %d:'%s' = '%s' (%s)\n"), cnt, comports[j]->name, comports[j]->dev, devname);
cnt++;
j++;
}
}
for (i = 0; i < cnt; i++) {
for (j = i + 1; j < cnt; j++) {
if (_tcsicmp (comports[i]->name, comports[j]->name) > 0) {
struct serparportinfo *spi;
spi = comports[i];
comports[i] = comports[j];
comports[j] = spi;
}
}
}
if (cnt < MAX_SERPAR_PORTS) {
comports[cnt] = xcalloc(struct serparportinfo, 1);
comports[cnt]->dev = my_strdup (SERIAL_INTERNAL);
comports[cnt]->cfgname = my_strdup (comports[cnt]->dev);
comports[cnt]->name = my_strdup (_T("WinUAE inter-process serial port"));
cnt++;
}
if (cnt < MAX_SERPAR_PORTS) {
comports[cnt] = xcalloc(struct serparportinfo, 1);
comports[cnt]->dev = my_strdup (_T("TCP://0.0.0.0:1234"));
comports[cnt]->cfgname = my_strdup (comports[cnt]->dev);
comports[cnt]->name = my_strdup (comports[cnt]->dev);
cnt++;
}
if (cnt < MAX_SERPAR_PORTS) {
comports[cnt] = xcalloc(struct serparportinfo, 1);
comports[cnt]->dev = my_strdup (_T("TCP://0.0.0.0:1234/wait"));
comports[cnt]->cfgname = my_strdup (comports[cnt]->dev);
comports[cnt]->name = my_strdup (comports[cnt]->dev);
cnt++;
}
write_log (_T("Parallel port enumeration..\n"));
enumports_2 (parports, 0, true);
write_log (_T("Port enumeration end\n"));
return cnt;
}
int enummidiports (void)
{
MIDIOUTCAPS midiOutCaps;
MIDIINCAPS midiInCaps;
int i, j, num, total;
int innum, outnum;
outnum = midiOutGetNumDevs();
innum = midiInGetNumDevs();
write_log (_T("MIDI port enumeration.. IN=%d OUT=%d\n"), innum, outnum);
num = outnum;
for (i = 0; i < num + 1 && i < MAX_MIDI_PORTS - 1; i++) {
MMRESULT r = midiOutGetDevCaps ((UINT)(i - 1), &midiOutCaps, sizeof (midiOutCaps));
if (r != MMSYSERR_NOERROR) {
num = i;
break;
}
midioutportinfo[i] = xcalloc (struct midiportinfo, 1);
midioutportinfo[i]->name = my_strdup (midiOutCaps.szPname);
midioutportinfo[i]->devid = i - 1;
write_log (_T("MIDI OUT: %d:'%s' (%d/%d)\n"), midioutportinfo[i]->devid, midioutportinfo[i]->name, midiOutCaps.wMid, midiOutCaps.wPid);
}
total = num + 1;
for (i = 1; i < num + 1; i++) {
for (j = i + 1; j < num + 1; j++) {
if (_tcsicmp (midioutportinfo[i]->name, midioutportinfo[j]->name) > 0) {
struct midiportinfo *mi;
mi = midioutportinfo[i];
midioutportinfo[i] = midioutportinfo[j];
midioutportinfo[j] = mi;
}
}
}
num = innum;
for (i = 0; i < num && i < MAX_MIDI_PORTS - 1; i++) {
if (midiInGetDevCaps (i, &midiInCaps, sizeof (midiInCaps)) != MMSYSERR_NOERROR) {
num = i;
break;
}
midiinportinfo[i] = xcalloc (struct midiportinfo, 1);
midiinportinfo[i]->name = my_strdup (midiInCaps.szPname);
midiinportinfo[i]->devid = i;
write_log (_T("MIDI IN: %d:'%s' (%d/%d)\n"), midiinportinfo[i]->devid, midiinportinfo[i]->name, midiInCaps.wMid, midiInCaps.wPid);
}
total += num;
for (i = 0; i < num; i++) {
for (j = i + 1; j < num; j++) {
if (_tcsicmp (midiinportinfo[i]->name, midiinportinfo[j]->name) > 0) {
struct midiportinfo *mi;
mi = midiinportinfo[i];
midiinportinfo[i] = midiinportinfo[j];
midiinportinfo[j] = mi;
}
}
}
write_log (_T("MIDI port enumeration end\n"));
return total;
}
void sernametodev (TCHAR *sername)
{
int i;
for (i = 0; i < MAX_SERPAR_PORTS && comports[i]; i++) {
if (!_tcscmp (sername, comports[i]->cfgname)) {
_tcscpy (sername, comports[i]->dev);
return;
}
}
if (!_tcsncmp (sername, _T("TCP:"), 4))
return;
sername[0] = 0;
}
void serdevtoname (TCHAR *sername)
{
int i;
if (!_tcsncmp (sername, _T("TCP:"), 4))
return;
for (i = 0; i < MAX_SERPAR_PORTS && comports[i]; i++) {
if (!_tcscmp (sername, comports[i]->dev)) {
_tcscpy (sername, comports[i]->cfgname);
return;
}
}
sername[0] = 0;
}