summaryrefslogtreecommitdiff
path: root/opl
diff options
context:
space:
mode:
authorSimon Howard2010-02-26 21:07:59 +0000
committerSimon Howard2010-02-26 21:07:59 +0000
commit06b97d2d116b622bc067b245f81b2857767d598e (patch)
tree80925c6d0c2a7032e2ffbec79be06b1d9a1b5f34 /opl
parentcdacf59acecd944f4a573b3e112c0c43b052f975 (diff)
downloadchocolate-doom-06b97d2d116b622bc067b245f81b2857767d598e.tar.gz
chocolate-doom-06b97d2d116b622bc067b245f81b2857767d598e.tar.bz2
chocolate-doom-06b97d2d116b622bc067b245f81b2857767d598e.zip
Add OPL hardware playback support for Windows NT-based systems.
Subversion-branch: /branches/opl-branch Subversion-revision: 1871
Diffstat (limited to 'opl')
-rw-r--r--opl/Makefile.am3
-rw-r--r--opl/ioperm_sys.c255
-rw-r--r--opl/ioperm_sys.h36
-rw-r--r--opl/opl.c11
-rw-r--r--opl/opl_win32.c (renamed from opl/opl_win9x.c)68
5 files changed, 352 insertions, 21 deletions
diff --git a/opl/Makefile.am b/opl/Makefile.am
index 8bbed9f0..d099b875 100644
--- a/opl/Makefile.am
+++ b/opl/Makefile.am
@@ -13,6 +13,7 @@ libopl_a_SOURCES = \
opl_queue.c opl_queue.h \
opl_sdl.c \
opl_timer.c opl_timer.h \
- opl_win9x.c \
+ opl_win32.c \
+ ioperm_sys.c ioperm_sys.h \
fmopl.c fmopl.h
diff --git a/opl/ioperm_sys.c b/opl/ioperm_sys.c
new file mode 100644
index 00000000..37512b63
--- /dev/null
+++ b/opl/ioperm_sys.c
@@ -0,0 +1,255 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2002, 2003 Marcel Telka
+// Copyright(C) 2009 Simon Howard
+//
+// 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.
+//
+// DESCRIPTION:
+// Interface to the ioperm.sys driver, based on code from the
+// Cygwin ioperm library.
+//
+//-----------------------------------------------------------------------------
+
+#ifdef _WIN32
+
+#include <stdio.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winioctl.h>
+
+#include <errno.h>
+
+#define IOPERM_FILE "\\\\.\\ioperm"
+
+#define IOCTL_IOPERM \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, 0xA00, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+struct ioperm_data
+{
+ unsigned long from;
+ unsigned long num;
+ int turn_on;
+};
+
+static SC_HANDLE scm = NULL;
+static SC_HANDLE svc = NULL;
+static int service_was_created = 0;
+static int service_was_started = 0;
+
+int IOperm_EnablePortRange(unsigned int from, unsigned int num, int turn_on)
+{
+ HANDLE h;
+ struct ioperm_data ioperm_data;
+ DWORD BytesReturned;
+ BOOL r;
+
+ h = CreateFile(IOPERM_FILE, GENERIC_READ, 0, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ errno = ENODEV;
+ return -1;
+ }
+
+ ioperm_data.from = from;
+ ioperm_data.num = num;
+ ioperm_data.turn_on = turn_on;
+
+ r = DeviceIoControl(h, IOCTL_IOPERM,
+ &ioperm_data, sizeof ioperm_data,
+ NULL, 0,
+ &BytesReturned, NULL);
+
+ if (!r)
+ {
+ errno = EPERM;
+ }
+
+ CloseHandle(h);
+
+ return r != 0;
+}
+
+// Load ioperm.sys driver.
+// Returns 1 for success, 0 for failure.
+// Remember to call IOperm_UninstallDriver to uninstall the driver later.
+
+int IOperm_InstallDriver(void)
+{
+ int error;
+ int result = 1;
+
+ scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+
+ if (scm == NULL)
+ {
+ error = GetLastError();
+ fprintf(stderr, "IOperm_InstallDriver: OpenSCManager failed (%i)\n",
+ error);
+ return 0;
+ }
+
+ svc = CreateService(scm,
+ TEXT("ioperm"),
+ TEXT("I/O port access driver"),
+ SERVICE_ALL_ACCESS,
+ SERVICE_KERNEL_DRIVER,
+ SERVICE_AUTO_START,
+ SERVICE_ERROR_NORMAL,
+ "ioperm.sys",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (svc == NULL)
+ {
+ error = GetLastError();
+
+ if (error != ERROR_SERVICE_EXISTS)
+ {
+ fprintf(stderr,
+ "IOperm_InstallDriver: Failed to create service (%i)\n",
+ error);
+ }
+ else
+ {
+ svc = OpenService(scm, TEXT("ioperm"), SERVICE_ALL_ACCESS);
+
+ if (svc == NULL)
+ {
+ error = GetLastError();
+
+ fprintf(stderr,
+ "IOperm_InstallDriver: Failed to open service (%i)\n",
+ error);
+ }
+ }
+
+ if (svc == NULL)
+ {
+ CloseServiceHandle(scm);
+ return 0;
+ }
+ }
+ else
+ {
+ service_was_created = 1;
+ }
+
+ if (!StartService(svc, 0, NULL))
+ {
+ error = GetLastError();
+
+ if (error != ERROR_SERVICE_ALREADY_RUNNING)
+ {
+ fprintf(stderr, "IOperm_InstallDriver: Failed to start service (%i)\n",
+ error);
+ result = 0;
+ }
+ else
+ {
+ printf("IOperm_InstallDriver: ioperm driver already running\n");
+ }
+ }
+ else
+ {
+ printf("IOperm_InstallDriver: ioperm driver installed\n");
+ service_was_started = 1;
+ }
+
+ if (result == 0)
+ {
+ CloseServiceHandle(svc);
+ CloseServiceHandle(scm);
+ }
+
+ return result;
+}
+
+int IOperm_UninstallDriver(void)
+{
+ SERVICE_STATUS stat;
+ int result = 1;
+ int error;
+
+ // If we started the service, stop it.
+
+ if (service_was_started)
+ {
+ if (!ControlService(svc, SERVICE_CONTROL_STOP, &stat))
+ {
+ error = GetLastError();
+
+ if (error == ERROR_SERVICE_NOT_ACTIVE)
+ {
+ fprintf(stderr,
+ "IOperm_UninstallDriver: Service not active? (%i)\n",
+ error);
+ }
+ else
+ {
+ fprintf(stderr,
+ "IOperm_UninstallDriver: Failed to stop service (%i)\n",
+ error);
+ result = 0;
+ }
+ }
+ }
+
+ // If we created the service, delete it.
+
+ if (service_was_created)
+ {
+ if (!DeleteService(svc))
+ {
+ error = GetLastError();
+
+ fprintf(stderr,
+ "IOperm_UninstallDriver: DeleteService failed (%i)\n",
+ error);
+
+ result = 0;
+ }
+ }
+
+ // Close handles.
+
+ if (svc != NULL)
+ {
+ CloseServiceHandle(svc);
+ svc = NULL;
+ }
+
+ if (scm != NULL)
+ {
+ CloseServiceHandle(scm);
+ scm = NULL;
+ }
+
+ service_was_created = 0;
+ service_was_started = 0;
+
+ return result;
+}
+
+#endif /* #ifndef _WIN32 */
+
diff --git a/opl/ioperm_sys.h b/opl/ioperm_sys.h
new file mode 100644
index 00000000..faf17bf3
--- /dev/null
+++ b/opl/ioperm_sys.h
@@ -0,0 +1,36 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2002, 2003 Marcel Telka
+// Copyright(C) 2009 Simon Howard
+//
+// 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.
+//
+// DESCRIPTION:
+// Interface to the ioperm.sys driver, based on code from the
+// Cygwin ioperm library.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef IOPERM_SYS_H
+#define IOPERM_SYS_H
+
+int IOperm_EnablePortRange(unsigned int from, unsigned int num, int turn_on);
+int IOperm_InstallDriver(void);
+int IOperm_UninstallDriver(void);
+
+#endif /* #ifndef IOPERM_SYS_H */
+
diff --git a/opl/opl.c b/opl/opl.c
index 2c8fd692..9e674530 100644
--- a/opl/opl.c
+++ b/opl/opl.c
@@ -46,7 +46,7 @@ extern opl_driver_t opl_linux_driver;
extern opl_driver_t opl_openbsd_driver;
#endif
#ifdef _WIN32
-extern opl_driver_t opl_win9x_driver;
+extern opl_driver_t opl_win32_driver;
#endif
extern opl_driver_t opl_sdl_driver;
@@ -59,7 +59,7 @@ static opl_driver_t *drivers[] =
&opl_openbsd_driver,
#endif
#ifdef _WIN32
- &opl_win9x_driver,
+ &opl_win32_driver,
#endif
&opl_sdl_driver,
NULL
@@ -197,6 +197,7 @@ void OPL_WritePort(opl_port_t port, unsigned int value)
{
#ifdef OPL_DEBUG_TRACE
printf("OPL_write: %i, %x\n", port, value);
+ fflush(stdout);
#endif
driver->write_port_func(port, value);
}
@@ -208,10 +209,16 @@ unsigned int OPL_ReadPort(opl_port_t port)
{
unsigned int result;
+#ifdef OPL_DEBUG_TRACE
+ printf("OPL_read: %i...\n", port);
+ fflush(stdout);
+#endif
+
result = driver->read_port_func(port);
#ifdef OPL_DEBUG_TRACE
printf("OPL_read: %i -> %x\n", port, result);
+ fflush(stdout);
#endif
return result;
diff --git a/opl/opl_win9x.c b/opl/opl_win32.c
index ff527b3e..29df3643 100644
--- a/opl/opl_win9x.c
+++ b/opl/opl_win32.c
@@ -19,7 +19,7 @@
// 02111-1307, USA.
//
// DESCRIPTION:
-// OPL Win9x native interface.
+// OPL Win32 native interface.
//
//-----------------------------------------------------------------------------
@@ -27,6 +27,8 @@
#ifdef _WIN32
+#include <stdio.h>
+
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
@@ -34,13 +36,15 @@
#include "opl_internal.h"
#include "opl_timer.h"
+#include "ioperm_sys.h"
+
static unsigned int opl_port_base;
// MingW?
#if defined(__GNUC__) && defined(__i386__)
-static unsigned int OPL_Win9x_PortRead(opl_port_t port)
+static unsigned int OPL_Win32_PortRead(opl_port_t port)
{
unsigned char result;
@@ -56,7 +60,7 @@ static unsigned int OPL_Win9x_PortRead(opl_port_t port)
return result;
}
-static void OPL_Win9x_PortWrite(opl_port_t port, unsigned int value)
+static void OPL_Win32_PortWrite(opl_port_t port, unsigned int value)
{
__asm__ volatile (
"movl %0, %%edx\n"
@@ -77,58 +81,86 @@ static void OPL_Win9x_PortWrite(opl_port_t port, unsigned int value)
#define NO_PORT_RW
-static unsigned int OPL_Win9x_PortRead(opl_port_t port)
+static unsigned int OPL_Win32_PortRead(opl_port_t port)
{
return 0;
}
-static void OPL_Win9x_PortWrite(opl_port_t port, unsigned int value)
+static void OPL_Win32_PortWrite(opl_port_t port, unsigned int value)
{
}
#endif
-static int OPL_Win9x_Init(unsigned int port_base)
+static int OPL_Win32_Init(unsigned int port_base)
{
#ifndef NO_PORT_RW
OSVERSIONINFO version_info;
- // Check that this is a Windows 9x series OS:
+ opl_port_base = port_base;
+
+ // Check the OS version.
memset(&version_info, 0, sizeof(version_info));
version_info.dwOSVersionInfoSize = sizeof(version_info);
GetVersionEx(&version_info);
- if (version_info.dwPlatformId == 1)
+ // On NT-based systems, we must acquire I/O port permissions
+ // using the ioperm.sys driver.
+
+ if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
- opl_port_base = port_base;
+ // Install driver.
+
+ if (!IOperm_InstallDriver())
+ {
+ return 0;
+ }
+
+ // Open port range.
- // Start callback thread
+ if (!IOperm_EnablePortRange(opl_port_base, 2, 1))
+ {
+ IOperm_UninstallDriver();
+ return 0;
+ }
+ }
+
+ // Start callback thread
- return OPL_Timer_StartThread();
+ if (!OPL_Timer_StartThread())
+ {
+ IOperm_UninstallDriver();
+ return 0;
}
+ return 1;
+
#endif
return 0;
}
-static void OPL_Win9x_Shutdown(void)
+static void OPL_Win32_Shutdown(void)
{
// Stop callback thread
OPL_Timer_StopThread();
+
+ // Unload IOperm library.
+
+ IOperm_UninstallDriver();
}
-opl_driver_t opl_win9x_driver =
+opl_driver_t opl_win32_driver =
{
- "Win9x",
- OPL_Win9x_Init,
- OPL_Win9x_Shutdown,
- OPL_Win9x_PortRead,
- OPL_Win9x_PortWrite,
+ "Win32",
+ OPL_Win32_Init,
+ OPL_Win32_Shutdown,
+ OPL_Win32_PortRead,
+ OPL_Win32_PortWrite,
OPL_Timer_SetCallback,
OPL_Timer_ClearCallbacks,
OPL_Timer_Lock,