summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/r3/os2
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Runtime/r3/os2
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/r3/os2')
-rw-r--r--src/VBox/Runtime/r3/os2/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r3/os2/RTTimeSet-os2.cpp121
-rw-r--r--src/VBox/Runtime/r3/os2/filelock-os2.cpp186
-rw-r--r--src/VBox/Runtime/r3/os2/mp-os2.cpp125
-rw-r--r--src/VBox/Runtime/r3/os2/pipe-os2.cpp1083
-rw-r--r--src/VBox/Runtime/r3/os2/rtProcInitExePath-os2.cpp71
-rw-r--r--src/VBox/Runtime/r3/os2/sched-os2.cpp244
-rw-r--r--src/VBox/Runtime/r3/os2/sems-os2.cpp392
-rw-r--r--src/VBox/Runtime/r3/os2/serialport-os2.cpp755
-rw-r--r--src/VBox/Runtime/r3/os2/systemmem-os2.cpp80
-rw-r--r--src/VBox/Runtime/r3/os2/thread-os2.cpp330
-rw-r--r--src/VBox/Runtime/r3/os2/time-os2.cpp87
12 files changed, 3474 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r3/os2/Makefile.kup b/src/VBox/Runtime/r3/os2/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/Makefile.kup
diff --git a/src/VBox/Runtime/r3/os2/RTTimeSet-os2.cpp b/src/VBox/Runtime/r3/os2/RTTimeSet-os2.cpp
new file mode 100644
index 00000000..fded291b
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/RTTimeSet-os2.cpp
@@ -0,0 +1,121 @@
+/* $Id: RTTimeSet-os2.cpp $ */
+/** @file
+ * IPRT - RTTimeSet, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * 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, in version 3 of the
+ * License.
+ *
+ * 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ * --------------------------------------------------------------------
+ *
+ * Copyright (c) 2018 knut st. osmundsen <bird-src-spam@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#define INCL_DOSDATETIME
+#define INCL_DOSERRORS
+#include <os2.h>
+
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTTimeSet(PCRTTIMESPEC pTime)
+{
+ /*
+ * Convert to local time and explode it, keeping the distance
+ * between UTC and local.
+ */
+ int64_t cNsLocalDelta = RTTimeLocalDeltaNanoFor(pTime);
+ RTTIMESPEC TimeLocal = *pTime;
+ RTTIME Exploded;
+ if (RTTimeExplode(&Exploded, RTTimeSpecAddNano(&TimeLocal, cNsLocalDelta)))
+ {
+ /*
+ * Fill in the OS/2 structure and make the call.
+ */
+ DATETIME DateTime;
+ DateTime.hours = Exploded.u8Hour;
+ DateTime.minutes = Exploded.u8Minute;
+ DateTime.seconds = Exploded.u8Second;
+ DateTime.hundredths = (uint8_t)(Exploded.u32Nanosecond / (RT_NS_1SEC_64 / 100));
+ DateTime.day = Exploded.u8MonthDay;
+ DateTime.month = Exploded.u8Month;
+ DateTime.year = (uint16_t)Exploded.i32Year;
+ DateTime.weekday = Exploded.u8WeekDay;
+
+ /* Minutes from UTC. http://www.edm2.com/os2api/Dos/DosSetDateTime.html says
+ that timezones west of UTC should have a positive value. The kernel fails
+ the call if we're more than +/-780 min (13h) distant, so clamp it in
+ case of bogus TZ values. */
+ DateTime.timezone = (int16_t)(-cNsLocalDelta / (int64_t)RT_NS_1MIN);
+ if (DateTime.timezone > 780)
+ DateTime.timezone = 780;
+ else if (DateTime.timezone < -780)
+ DateTime.timezone = -780;
+
+ APIRET rc = DosSetDateTime(&DateTime);
+ if (rc == NO_ERROR)
+ return VINF_SUCCESS;
+ AssertMsgFailed(("rc=%u\n", rc));
+ return RTErrConvertFromOS2(rc);
+ }
+ return VERR_INVALID_PARAMETER;
+}
+
diff --git a/src/VBox/Runtime/r3/os2/filelock-os2.cpp b/src/VBox/Runtime/r3/os2/filelock-os2.cpp
new file mode 100644
index 00000000..9fb93078
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/filelock-os2.cpp
@@ -0,0 +1,186 @@
+/* $Id: filelock-os2.cpp $ */
+/** @file
+ * IPRT - File Locking, OS/2.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * 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, in version 3 of the
+ * License.
+ *
+ * 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <iprt/file.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include "internal/file.h"
+#include "internal/fs.h"
+
+
+
+
+RTR3DECL(int) RTFileLock(RTFILE File, unsigned fLock, int64_t offLock, uint64_t cbLock)
+{
+ Assert(offLock >= 0);
+
+ /* Check arguments. */
+ if (fLock & ~RTFILE_LOCK_MASK)
+ {
+ AssertMsgFailed(("Invalid fLock=%08X\n", fLock));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Validate offset.
+ */
+ if ( sizeof(off_t) < sizeof(cbLock)
+ && ( (offLock >> 32) != 0
+ || (cbLock >> 32) != 0
+ || ((offLock + cbLock) >> 32) != 0))
+ {
+ AssertMsgFailed(("64-bit file i/o not supported! offLock=%lld cbLock=%lld\n", offLock, cbLock));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ /* Prepare flock structure. */
+ struct flock fl;
+ Assert(RTFILE_LOCK_WRITE);
+ fl.l_type = (fLock & RTFILE_LOCK_WRITE) ? F_WRLCK : F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = (off_t)offLock;
+ fl.l_len = (off_t)cbLock;
+ fl.l_pid = 0;
+
+ Assert(RTFILE_LOCK_WAIT);
+ if (fcntl(RTFileToNative(File), (fLock & RTFILE_LOCK_WAIT) ? F_SETLKW : F_SETLK, &fl) >= 0)
+ return VINF_SUCCESS;
+
+ int iErr = errno;
+ if ( iErr == EAGAIN
+ || iErr == EACCES)
+ return VERR_FILE_LOCK_VIOLATION;
+
+ return RTErrConvertFromErrno(iErr);
+}
+
+
+RTR3DECL(int) RTFileChangeLock(RTFILE File, unsigned fLock, int64_t offLock, uint64_t cbLock)
+{
+ /** @todo copied from ../win/fileio-win.cpp for now but a proper solution
+ * would probably be to modify kLIBC so that __fcntl_locking() first
+ * assumes a change lock request is made (e.g. the same region was
+ * previously F_RDLCK'ed and now needs to be F_WRLCK'ed or vice versa) and
+ * tries to use atomic locking, and only if it fails, it does the regular
+ * lock procedure. The alternative is to use DosSetFileLocks directly here
+ * which basically means copy-pasting the __fcntl_locking() source
+ * code :) Note that the first attempt to call RTFileLock() below assumes
+ * that kLIBC is patched as described above one day and gives it a chance;
+ * on failure, we fall back to the Win-like unlock-then-lock approach. */
+
+ int rc = RTFileLock(File, fLock, offLock, cbLock);
+ if (RT_FAILURE(rc) && rc != VERR_FILE_LOCK_VIOLATION)
+ return rc;
+
+ /* Check arguments. */
+ if (fLock & ~RTFILE_LOCK_MASK)
+ {
+ AssertMsgFailed(("Invalid fLock=%08X\n", fLock));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Remove old lock. */
+ rc = RTFileUnlock(File, offLock, cbLock);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* Set new lock. */
+ rc = RTFileLock(File, fLock, offLock, cbLock);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ /* Try to restore old lock. */
+ unsigned fLockOld = (fLock & RTFILE_LOCK_WRITE) ? fLock & ~RTFILE_LOCK_WRITE : fLock | RTFILE_LOCK_WRITE;
+ rc = RTFileLock(File, fLockOld, offLock, cbLock);
+ if (RT_SUCCESS(rc))
+ return VERR_FILE_LOCK_VIOLATION;
+ else
+ return VERR_FILE_LOCK_LOST;
+}
+
+
+RTR3DECL(int) RTFileUnlock(RTFILE File, int64_t offLock, uint64_t cbLock)
+{
+ Assert(offLock >= 0);
+
+ /*
+ * Validate offset.
+ */
+ if ( sizeof(off_t) < sizeof(cbLock)
+ && ( (offLock >> 32) != 0
+ || (cbLock >> 32) != 0
+ || ((offLock + cbLock) >> 32) != 0))
+ {
+ AssertMsgFailed(("64-bit file i/o not supported! offLock=%lld cbLock=%lld\n", offLock, cbLock));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ /* Prepare flock structure. */
+ struct flock fl;
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = (off_t)offLock;
+ fl.l_len = (off_t)cbLock;
+ fl.l_pid = 0;
+
+ if (fcntl(RTFileToNative(File), F_SETLK, &fl) >= 0)
+ return VINF_SUCCESS;
+
+ /** @todo check error codes for non existing lock. */
+ int iErr = errno;
+ if ( iErr == EAGAIN
+ || iErr == EACCES)
+ return VERR_FILE_LOCK_VIOLATION;
+
+ return RTErrConvertFromErrno(iErr);
+}
+
diff --git a/src/VBox/Runtime/r3/os2/mp-os2.cpp b/src/VBox/Runtime/r3/os2/mp-os2.cpp
new file mode 100644
index 00000000..64ae06a8
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/mp-os2.cpp
@@ -0,0 +1,125 @@
+/* $Id: mp-os2.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, OS/2.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * 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, in version 3 of the
+ * License.
+ *
+ * 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define INCL_BASE
+#define INCL_ERRORS
+#include <os2.h>
+#undef RT_MAX
+
+#include <iprt/mp.h>
+#include <iprt/cpuset.h>
+#include <iprt/assert.h>
+
+/** @todo RTMpCpuId() */
+
+RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
+{
+ return idCpu < RTCPUSET_MAX_CPUS ? (int) idCpu : -1;
+}
+
+
+RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
+{
+ return (unsigned)iCpu < RTCPUSET_MAX_CPUS ? iCpu : NIL_RTCPUID;
+}
+
+
+RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
+{
+ return RTCPUSET_MAX_CPUS;
+}
+
+
+RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
+{
+ RTCPUSET Set;
+ return RTCpuSetIsMember(RTMpGetSet(&Set), idCpu);
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
+{
+ RTCPUID idCpu = RTMpGetCount();
+ RTCpuSetEmpty(pSet);
+ while (idCpu-- > 0)
+ RTCpuSetAdd(pSet, idCpu);
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+ ULONG cCpus = 1;
+ int rc = DosQuerySysInfo(QSV_NUMPROCESSORS, QSV_NUMPROCESSORS, &cCpus, sizeof(cCpus));
+ if (rc || !cCpus)
+ cCpus = 1;
+ return cCpus;
+}
+
+
+RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
+{
+ RTCPUSET Set;
+ return RTCpuSetIsMember(RTMpGetOnlineSet(&Set), idCpu);
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
+{
+ union
+ {
+ uint64_t u64;
+ MPAFFINITY mpaff;
+ } u;
+
+ int rc = DosQueryThreadAffinity(AFNTY_SYSTEM, &u.mpaff);
+ if (rc)
+ u.u64 = 1;
+ return RTCpuSetFromU64(pSet, u.u64);
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCount(void)
+{
+ RTCPUSET Set;
+ RTMpGetOnlineSet(&Set);
+ return RTCpuSetCount(&Set);
+}
+
diff --git a/src/VBox/Runtime/r3/os2/pipe-os2.cpp b/src/VBox/Runtime/r3/os2/pipe-os2.cpp
new file mode 100644
index 00000000..6a4792c5
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/pipe-os2.cpp
@@ -0,0 +1,1083 @@
+/* $Id: pipe-os2.cpp $ */
+/** @file
+ * IPRT - Anonymous Pipes, OS/2 Implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * 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, in version 3 of the
+ * License.
+ *
+ * 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define INCL_ERRORS
+#define INCL_DOSSEMAPHORES
+#include <os2.h>
+
+#include <iprt/pipe.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/poll.h>
+#include <iprt/process.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include "internal/pipe.h"
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The pipe buffer size we prefer. */
+#define RTPIPE_OS2_SIZE _32K
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct RTPIPEINTERNAL
+{
+ /** Magic value (RTPIPE_MAGIC). */
+ uint32_t u32Magic;
+ /** The pipe handle. */
+ HPIPE hPipe;
+ /** Set if this is the read end, clear if it's the write end. */
+ bool fRead;
+ /** RTPipeFromNative: Leave open. */
+ bool fLeaveOpen;
+ /** Whether the pipe is in blocking or non-blocking mode. */
+ bool fBlocking;
+ /** Set if the pipe is broken. */
+ bool fBrokenPipe;
+ /** Usage counter. */
+ uint32_t cUsers;
+
+ /** The event semaphore associated with the pipe. */
+ HEV hev;
+ /** The handle of the poll set currently polling on this pipe.
+ * We can only have one poller at the time (lazy bird). */
+ RTPOLLSET hPollSet;
+ /** Critical section protecting the above members.
+ * (Taking the lazy/simple approach.) */
+ RTCRITSECT CritSect;
+
+} RTPIPEINTERNAL;
+
+
+/**
+ * Ensures that the pipe has a semaphore associated with it.
+ *
+ * @returns VBox status code.
+ * @param pThis The pipe.
+ */
+static int rtPipeOs2EnsureSem(RTPIPEINTERNAL *pThis)
+{
+ if (pThis->hev != NULLHANDLE)
+ return VINF_SUCCESS;
+
+ HEV hev;
+ APIRET orc = DosCreateEventSem(NULL, &hev, DC_SEM_SHARED, FALSE);
+ if (orc == NO_ERROR)
+ {
+ orc = DosSetNPipeSem(pThis->hPipe, (HSEM)hev, 1);
+ if (orc == NO_ERROR)
+ {
+ pThis->hev = hev;
+ return VINF_SUCCESS;
+ }
+
+ DosCloseEventSem(hev);
+ }
+ return RTErrConvertFromOS2(orc);
+}
+
+
+RTDECL(int) RTPipeCreate(PRTPIPE phPipeRead, PRTPIPE phPipeWrite, uint32_t fFlags)
+{
+ AssertPtrReturn(phPipeRead, VERR_INVALID_POINTER);
+ AssertPtrReturn(phPipeWrite, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTPIPE_C_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /*
+ * Try create and connect a pipe pair.
+ */
+ APIRET orc;
+ HPIPE hPipeR;
+ HFILE hPipeW;
+ int rc;
+ for (;;)
+ {
+ static volatile uint32_t g_iNextPipe = 0;
+ char szName[128];
+ RTStrPrintf(szName, sizeof(szName), "\\pipe\\iprt-pipe-%u-%u", RTProcSelf(), ASMAtomicIncU32(&g_iNextPipe));
+
+ /*
+ * Create the read end of the pipe.
+ */
+ ULONG fPipeMode = 1 /*instance*/ | NP_TYPE_BYTE | NP_READMODE_BYTE | NP_NOWAIT;
+ ULONG fOpenMode = NP_ACCESS_DUPLEX | NP_WRITEBEHIND;
+ if (fFlags & RTPIPE_C_INHERIT_READ)
+ fOpenMode |= NP_INHERIT;
+ else
+ fOpenMode |= NP_NOINHERIT;
+ orc = DosCreateNPipe((PSZ)szName, &hPipeR, fOpenMode, fPipeMode, RTPIPE_OS2_SIZE, RTPIPE_OS2_SIZE, NP_DEFAULT_WAIT);
+ if (orc == NO_ERROR)
+ {
+ orc = DosConnectNPipe(hPipeR);
+ if (orc == ERROR_PIPE_NOT_CONNECTED || orc == NO_ERROR)
+ {
+ /*
+ * Connect to the pipe (the write end), attach sem below.
+ */
+ ULONG ulAction = 0;
+ ULONG fOpenW = OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
+ ULONG fModeW = OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYNONE | OPEN_FLAGS_FAIL_ON_ERROR;
+ if (!(fFlags & RTPIPE_C_INHERIT_WRITE))
+ fModeW |= OPEN_FLAGS_NOINHERIT;
+ orc = DosOpen((PSZ)szName, &hPipeW, &ulAction, 0 /*cbFile*/, FILE_NORMAL,
+ fOpenW, fModeW, NULL /*peaop2*/);
+ if (orc == NO_ERROR)
+ break;
+ }
+ DosClose(hPipeR);
+ }
+ if ( orc != ERROR_PIPE_BUSY /* already exist - compatible */
+ && orc != ERROR_ACCESS_DENIED /* already exist - incompatible (?) */)
+ return RTErrConvertFromOS2(orc);
+ /* else: try again with a new name */
+ }
+
+ /*
+ * Create the two handles.
+ */
+ RTPIPEINTERNAL *pThisR = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
+ if (pThisR)
+ {
+ RTPIPEINTERNAL *pThisW = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
+ if (pThisW)
+ {
+ /* Crit sects. */
+ rc = RTCritSectInit(&pThisR->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectInit(&pThisW->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* Initialize the structures. */
+ pThisR->u32Magic = RTPIPE_MAGIC;
+ pThisW->u32Magic = RTPIPE_MAGIC;
+ pThisR->hPipe = hPipeR;
+ pThisW->hPipe = hPipeW;
+ pThisR->hev = NULLHANDLE;
+ pThisW->hev = NULLHANDLE;
+ pThisR->fRead = true;
+ pThisW->fRead = false;
+ pThisR->fLeaveOpen = false;
+ pThisW->fLeaveOpen = false;
+ pThisR->fBlocking = false;
+ pThisW->fBlocking = true;
+ //pThisR->fBrokenPipe = false;
+ //pThisW->fBrokenPipe = false;
+ //pThisR->cUsers = 0;
+ //pThisW->cUsers = 0;
+ pThisR->hPollSet = NIL_RTPOLLSET;
+ pThisW->hPollSet = NIL_RTPOLLSET;
+
+ *phPipeRead = pThisR;
+ *phPipeWrite = pThisW;
+ return VINF_SUCCESS;
+ }
+
+ RTCritSectDelete(&pThisR->CritSect);
+ }
+ RTMemFree(pThisW);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ RTMemFree(pThisR);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ /* Don't call DosDisConnectNPipe! */
+ DosClose(hPipeW);
+ DosClose(hPipeR);
+ return rc;
+}
+
+
+RTDECL(int) RTPipeCloseEx(RTPIPE hPipe, bool fLeaveOpen)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ if (pThis == NIL_RTPIPE)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the cleanup.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTPIPE_MAGIC, RTPIPE_MAGIC), VERR_INVALID_HANDLE);
+ RTCritSectEnter(&pThis->CritSect);
+ Assert(pThis->cUsers == 0);
+
+ /* Don't call DosDisConnectNPipe! */
+ if (!fLeaveOpen && !pThis->fLeaveOpen)
+ DosClose(pThis->hPipe);
+ pThis->hPipe = (HPIPE)-1;
+
+ if (pThis->hev != NULLHANDLE)
+ {
+ DosCloseEventSem(pThis->hev);
+ pThis->hev = NULLHANDLE;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+ RTCritSectDelete(&pThis->CritSect);
+
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTPipeClose(RTPIPE hPipe)
+{
+ return RTPipeCloseEx(hPipe, false /*fLeaveOpen*/);
+}
+
+
+RTDECL(int) RTPipeFromNative(PRTPIPE phPipe, RTHCINTPTR hNativePipe, uint32_t fFlags)
+{
+ AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTPIPE_N_VALID_MASK_FN), VERR_INVALID_PARAMETER);
+ AssertReturn(!!(fFlags & RTPIPE_N_READ) != !!(fFlags & RTPIPE_N_WRITE), VERR_INVALID_PARAMETER);
+
+ /*
+ * Get and validate the pipe handle info.
+ */
+ HPIPE hNative = (HPIPE)hNativePipe;
+ ULONG ulType = 0;
+ ULONG ulAttr = 0;
+ APIRET orc = DosQueryHType(hNative, &ulType, &ulAttr);
+ AssertMsgReturn(orc == NO_ERROR, ("%d\n", orc), RTErrConvertFromOS2(orc));
+ AssertReturn((ulType & 0x7) == HANDTYPE_PIPE, VERR_INVALID_HANDLE);
+
+#if 0
+ union
+ {
+ PIPEINFO PipeInfo;
+ uint8_t abPadding[sizeof(PIPEINFO) + 127];
+ } Buf;
+ orc = DosQueryNPipeInfo(hNative, 1, &Buf, sizeof(Buf));
+ if (orc != NO_ERROR)
+ {
+ /* Sorry, anonymous pips are not supported. */
+ AssertMsgFailed(("%d\n", orc));
+ return VERR_INVALID_HANDLE;
+ }
+ AssertReturn(Buf.PipeInfo.cbMaxInst == 1, VERR_INVALID_HANDLE);
+#endif
+
+ ULONG fPipeState = 0;
+ orc = DosQueryNPHState(hNative, &fPipeState);
+ if (orc != NO_ERROR)
+ {
+ /* Sorry, anonymous pips are not supported. */
+ AssertMsgFailed(("%d\n", orc));
+ return VERR_INVALID_HANDLE;
+ }
+ AssertReturn(!(fPipeState & NP_TYPE_MESSAGE), VERR_INVALID_HANDLE);
+ AssertReturn(!(fPipeState & NP_READMODE_MESSAGE), VERR_INVALID_HANDLE);
+ AssertReturn((fPipeState & 0xff) == 1, VERR_INVALID_HANDLE);
+
+ ULONG fFileState = 0;
+ orc = DosQueryFHState(hNative, &fFileState);
+ AssertMsgReturn(orc == NO_ERROR, ("%d\n", orc), VERR_INVALID_HANDLE);
+ AssertMsgReturn( (fFileState & 0x3) == (fFlags & RTPIPE_N_READ ? OPEN_ACCESS_READONLY : OPEN_ACCESS_WRITEONLY)
+ || (fFileState & 0x3) == OPEN_ACCESS_READWRITE
+ , ("%#x\n", fFileState), VERR_INVALID_HANDLE);
+
+ /*
+ * Looks kind of OK. Fix the inherit flag.
+ */
+ orc = DosSetFHState(hNative, (fFileState & (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_CACHE))
+ | (fFlags & RTPIPE_N_INHERIT ? 0 : OPEN_FLAGS_NOINHERIT));
+ AssertMsgReturn(orc == NO_ERROR, ("%d\n", orc), RTErrConvertFromOS2(orc));
+
+
+ /*
+ * Create a handle so we can try rtPipeQueryInfo on it
+ * and see if we need to duplicate it to make that call work.
+ */
+ RTPIPEINTERNAL *pThis = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+ int rc = RTCritSectInit(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->u32Magic = RTPIPE_MAGIC;
+ pThis->hPipe = hNative;
+ pThis->hev = NULLHANDLE;
+ pThis->fRead = RT_BOOL(fFlags & RTPIPE_N_READ);
+ pThis->fLeaveOpen = RT_BOOL(fFlags & RTPIPE_N_LEAVE_OPEN);
+ pThis->fBlocking = !(fPipeState & NP_NOWAIT);
+ //pThis->fBrokenPipe = false;
+ //pThis->cUsers = 0;
+ pThis->hPollSet = NIL_RTPOLLSET;
+
+ *phPipe = pThis;
+ return VINF_SUCCESS;
+
+ //RTCritSectDelete(&pThis->CritSect);
+ }
+ RTMemFree(pThis);
+ return rc;
+}
+
+RTDECL(RTHCINTPTR) RTPipeToNative(RTPIPE hPipe)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, -1);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, -1);
+
+ return (RTHCINTPTR)pThis->hPipe;
+}
+
+/**
+ * Prepare blocking mode.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_WRONG_ORDER if simultaneous non-blocking and blocking access is
+ * attempted.
+ *
+ * @param pThis The pipe handle.
+ *
+ * @remarks Caller owns the critical section.
+ */
+static int rtPipeTryBlocking(RTPIPEINTERNAL *pThis)
+{
+ if (!pThis->fBlocking)
+ {
+ if (pThis->cUsers != 0)
+ return VERR_WRONG_ORDER;
+
+ APIRET orc = DosSetNPHState(pThis->hPipe, NP_WAIT | NP_READMODE_BYTE);
+ if (orc != NO_ERROR)
+ {
+ if (orc != ERROR_BROKEN_PIPE && orc != ERROR_PIPE_NOT_CONNECTED)
+ return RTErrConvertFromOS2(orc);
+ pThis->fBrokenPipe = true;
+ }
+ pThis->fBlocking = true;
+ }
+
+ pThis->cUsers++;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Prepare non-blocking mode.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_WRONG_ORDER if simultaneous non-blocking and blocking access is
+ * attempted.
+ *
+ * @param pThis The pipe handle.
+ */
+static int rtPipeTryNonBlocking(RTPIPEINTERNAL *pThis)
+{
+ if (pThis->fBlocking)
+ {
+ if (pThis->cUsers != 0)
+ return VERR_WRONG_ORDER;
+
+ APIRET orc = DosSetNPHState(pThis->hPipe, NP_NOWAIT | NP_READMODE_BYTE);
+ if (orc != NO_ERROR)
+ {
+ if (orc != ERROR_BROKEN_PIPE && orc != ERROR_PIPE_NOT_CONNECTED)
+ return RTErrConvertFromOS2(orc);
+ pThis->fBrokenPipe = true;
+ }
+ pThis->fBlocking = false;
+ }
+
+ pThis->cUsers++;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks if the read pipe has been broken.
+ *
+ * @returns true if broken, false if no.
+ * @param pThis The pipe handle (read).
+ */
+static bool rtPipeOs2IsBroken(RTPIPEINTERNAL *pThis)
+{
+ Assert(pThis->fRead);
+
+#if 0
+ /*
+ * Query it via the semaphore. Not sure how fast this is...
+ */
+ PIPESEMSTATE aStates[3]; RT_ZERO(aStates);
+ APIRET orc = DosQueryNPipeSemState(pThis->hev, &aStates[0], sizeof(aStates));
+ if (orc == NO_ERROR)
+ {
+ if (aStates[0].fStatus == NPSS_CLOSE)
+ return true;
+ if (aStates[0].fStatus == NPSS_RDATA)
+ return false;
+ }
+ AssertMsgFailed(("%d / %d\n", orc, aStates[0].fStatus));
+
+ /*
+ * Fall back / alternative method.
+ */
+#endif
+ ULONG cbActual = 0;
+ ULONG ulState = 0;
+ AVAILDATA Avail = { 0, 0 };
+ APIRET orc = DosPeekNPipe(pThis->hPipe, NULL, 0, &cbActual, &Avail, &ulState);
+ if (orc != NO_ERROR)
+ {
+ if (orc != ERROR_PIPE_BUSY)
+ AssertMsgFailed(("%d\n", orc));
+ return false;
+ }
+
+ return ulState != NP_STATE_CONNECTED;
+}
+
+
+RTDECL(int) RTPipeRead(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pcbRead);
+ AssertPtr(pvBuf);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtPipeTryNonBlocking(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ RTCritSectLeave(&pThis->CritSect);
+
+ ULONG cbActual = 0;
+ APIRET orc = DosRead(pThis->hPipe, pvBuf, cbToRead, &cbActual);
+ if (orc == NO_ERROR)
+ {
+ if (cbActual || !cbToRead || !rtPipeOs2IsBroken(pThis))
+ *pcbRead = cbActual;
+ else
+ rc = VERR_BROKEN_PIPE;
+ }
+ else if (orc == ERROR_NO_DATA)
+ {
+ *pcbRead = 0;
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ rc = RTErrConvertFromOS2(orc);
+
+ RTCritSectEnter(&pThis->CritSect);
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeReadBlocking(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pvBuf);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtPipeTryBlocking(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ RTCritSectLeave(&pThis->CritSect);
+
+ size_t cbTotalRead = 0;
+ while (cbToRead > 0)
+ {
+ ULONG cbActual = 0;
+ APIRET orc = DosRead(pThis->hPipe, pvBuf, cbToRead, &cbActual);
+ if (orc != NO_ERROR)
+ {
+ rc = RTErrConvertFromOS2(orc);
+ break;
+ }
+ if (!cbActual && rtPipeOs2IsBroken(pThis))
+ {
+ rc = VERR_BROKEN_PIPE;
+ break;
+ }
+
+ /* advance */
+ pvBuf = (char *)pvBuf + cbActual;
+ cbTotalRead += cbActual;
+ cbToRead -= cbActual;
+ }
+
+ if (pcbRead)
+ {
+ *pcbRead = cbTotalRead;
+ if ( RT_FAILURE(rc)
+ && cbTotalRead)
+ rc = VINF_SUCCESS;
+ }
+
+ RTCritSectEnter(&pThis->CritSect);
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+/**
+ * Gets the available write buffer size of the pipe.
+ *
+ * @returns Number of bytes, 1 on failure.
+ * @param pThis The pipe handle.
+ */
+static ULONG rtPipeOs2GetSpace(RTPIPEINTERNAL *pThis)
+{
+ Assert(!pThis->fRead);
+
+#if 0 /* Not sure which is more efficient, neither are really optimal, I fear. */
+ /*
+ * Query via semaphore state.
+ * This will walk the list of active named pipes...
+ */
+ /** @todo Check how hev and hpipe are associated, if complicated, use the
+ * alternative method below. */
+ PIPESEMSTATE aStates[3]; RT_ZERO(aStates);
+ APIRET orc = DosQueryNPipeSemState((HSEM)pThis->hev, &aStates[0], sizeof(aStates));
+ if (orc == NO_ERROR)
+ {
+ if (aStates[0].fStatus == NPSS_WSPACE)
+ return aStates[0].usAvail;
+ if (aStates[1].fStatus == NPSS_WSPACE)
+ return aStates[1].usAvail;
+ return 0;
+ }
+ AssertMsgFailed(("%d / %d\n", orc, aStates[0].fStatus));
+
+#else
+ /*
+ * Query via the pipe info.
+ * This will have to lookup and store the pipe name.
+ */
+ union
+ {
+ PIPEINFO PipeInfo;
+ uint8_t abPadding[sizeof(PIPEINFO) + 127];
+ } Buf;
+ APIRET orc = DosQueryNPipeInfo(pThis->hPipe, 1, &Buf, sizeof(Buf));
+ if (orc == NO_ERROR)
+ return Buf.PipeInfo.cbOut;
+ AssertMsgFailed(("%d\n", orc));
+#endif
+
+ return 1;
+}
+
+
+RTDECL(int) RTPipeWrite(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pcbWritten);
+ AssertPtr(pvBuf);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtPipeTryNonBlocking(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ if (cbToWrite > 0)
+ {
+ ULONG cbActual = 0;
+ APIRET orc = DosWrite(pThis->hPipe, pvBuf, cbToWrite, &cbActual);
+ if (orc == NO_ERROR && cbActual == 0)
+ {
+ /* Retry with the request adjusted to the available buffer space. */
+ ULONG cbAvail = rtPipeOs2GetSpace(pThis);
+ orc = DosWrite(pThis->hPipe, pvBuf, RT_MIN(cbAvail, cbToWrite), &cbActual);
+ }
+
+ if (orc == NO_ERROR)
+ {
+ *pcbWritten = cbActual;
+ if (cbActual == 0)
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ {
+ rc = RTErrConvertFromOS2(orc);
+ if (rc == VERR_PIPE_NOT_CONNECTED)
+ rc = VERR_BROKEN_PIPE;
+ }
+ }
+ else
+ *pcbWritten = 0;
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pvBuf);
+ AssertPtrNull(pcbWritten);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtPipeTryBlocking(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ RTCritSectLeave(&pThis->CritSect);
+
+ size_t cbTotalWritten = 0;
+ while (cbToWrite > 0)
+ {
+ ULONG cbActual = 0;
+ APIRET orc = DosWrite(pThis->hPipe, pvBuf, cbToWrite, &cbActual);
+ if (orc != NO_ERROR)
+ {
+ rc = RTErrConvertFromOS2(orc);
+ if (rc == VERR_PIPE_NOT_CONNECTED)
+ rc = VERR_BROKEN_PIPE;
+ break;
+ }
+ pvBuf = (char const *)pvBuf + cbActual;
+ cbToWrite -= cbActual;
+ cbTotalWritten += cbActual;
+ }
+
+ if (pcbWritten)
+ {
+ *pcbWritten = cbTotalWritten;
+ if ( RT_FAILURE(rc)
+ && cbTotalWritten)
+ rc = VINF_SUCCESS;
+ }
+
+ RTCritSectEnter(&pThis->CritSect);
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeFlush(RTPIPE hPipe)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+
+ APIRET orc = DosResetBuffer(pThis->hPipe);
+ if (orc != NO_ERROR)
+ {
+ int rc = RTErrConvertFromOS2(orc);
+ if (rc == VERR_BROKEN_PIPE)
+ {
+ RTCritSectEnter(&pThis->CritSect);
+ pThis->fBrokenPipe = true;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ uint64_t const StartMsTS = RTTimeMilliTS();
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = rtPipeOs2EnsureSem(pThis);
+ if (RT_SUCCESS(rc) && cMillies > 0)
+ {
+ /* Stop polling attempts if we might block. */
+ if (pThis->hPollSet == NIL_RTPOLLSET)
+ pThis->hPollSet = (RTPOLLSET)(uintptr_t)0xbeef0042;
+ else
+ rc = VERR_WRONG_ORDER;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ for (unsigned iLoop = 0;; iLoop++)
+ {
+ /*
+ * Check the handle state.
+ */
+ APIRET orc;
+ if (cMillies > 0)
+ {
+ ULONG ulIgnore;
+ orc = DosResetEventSem(pThis->hev, &ulIgnore);
+ AssertMsg(orc == NO_ERROR || orc == ERROR_ALREADY_RESET, ("%d\n", orc));
+ }
+
+ PIPESEMSTATE aStates[4]; RT_ZERO(aStates);
+ orc = DosQueryNPipeSemState((HSEM)pThis->hev, &aStates[0], sizeof(aStates));
+ if (orc != NO_ERROR)
+ {
+ rc = RTErrConvertFromOS2(orc);
+ break;
+ }
+ int i = 0;
+ if (pThis->fRead)
+ while (aStates[i].fStatus == NPSS_WSPACE)
+ i++;
+ else
+ while (aStates[i].fStatus == NPSS_RDATA)
+ i++;
+ if (aStates[i].fStatus == NPSS_CLOSE)
+ break;
+ Assert(aStates[i].fStatus == NPSS_WSPACE || aStates[i].fStatus == NPSS_RDATA || aStates[i].fStatus == NPSS_EOI);
+ if ( aStates[i].fStatus != NPSS_EOI
+ && aStates[i].usAvail > 0)
+ break;
+
+ /*
+ * Check for timeout.
+ */
+ ULONG cMsMaxWait = SEM_INDEFINITE_WAIT;
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ uint64_t cElapsed = RTTimeMilliTS() - StartMsTS;
+ if (cElapsed >= cMillies)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ cMsMaxWait = cMillies - (uint32_t)cElapsed;
+ }
+
+ /*
+ * Wait.
+ */
+ RTCritSectLeave(&pThis->CritSect);
+ orc = DosWaitEventSem(pThis->hev, cMsMaxWait);
+ RTCritSectEnter(&pThis->CritSect);
+ if (orc != NO_ERROR && orc != ERROR_TIMEOUT && orc != ERROR_SEM_TIMEOUT )
+ {
+ rc = RTErrConvertFromOS2(orc);
+ break;
+ }
+ }
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ if (cMillies > 0)
+ pThis->hPollSet = NIL_RTPOLLSET;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+ return rc;
+}
+
+
+RTDECL(int) RTPipeQueryReadable(RTPIPE hPipe, size_t *pcbReadable)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_PIPE_NOT_READ);
+ AssertPtrReturn(pcbReadable, VERR_INVALID_POINTER);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ ULONG cbActual = 0;
+ ULONG ulState = 0;
+ AVAILDATA Avail = { 0, 0 };
+ APIRET orc = DosPeekNPipe(pThis->hPipe, NULL, 0, &cbActual, &Avail, &ulState);
+ if (orc == NO_ERROR)
+ {
+ if (Avail.cbpipe > 0 || ulState == NP_STATE_CONNECTED)
+ *pcbReadable = Avail.cbpipe;
+ else
+ rc = VERR_PIPE_NOT_CONNECTED; /*??*/
+ }
+ else
+ rc = RTErrConvertFromOS2(orc);
+
+ RTCritSectLeave(&pThis->CritSect);
+ return rc;
+}
+
+
+RTDECL(int) RTPipeQueryInfo(RTPIPE hPipe, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, 0);
+
+ rtPipeFakeQueryInfo(pObjInfo, enmAddAttr, pThis->fRead);
+
+ if (pThis->fRead)
+ {
+ ULONG cbActual = 0;
+ ULONG ulState = 0;
+ AVAILDATA Avail = { 0, 0 };
+ APIRET orc = DosPeekNPipe(pThis->hPipe, NULL, 0, &cbActual, &Avail, &ulState);
+ if (orc == NO_ERROR && (Avail.cbpipe > 0 || ulState == NP_STATE_CONNECTED))
+ pObjInfo->cbObject = Avail.cbpipe;
+ }
+ else
+ pObjInfo->cbObject = rtPipeOs2GetSpace(pThis);
+ pObjInfo->cbAllocated = RTPIPE_OS2_SIZE; /** @todo this isn't necessarily true if we didn't create it... but, whatever */
+
+ RTCritSectLeave(&pThis->CritSect);
+ return VINF_SUCCESS;
+}
+
+
+int rtPipePollGetHandle(RTPIPE hPipe, uint32_t fEvents, PRTHCINTPTR phNative)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ AssertReturn(!(fEvents & RTPOLL_EVT_READ) || pThis->fRead, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fEvents & RTPOLL_EVT_WRITE) || !pThis->fRead, VERR_INVALID_PARAMETER);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtPipeOs2EnsureSem(pThis);
+ if (RT_SUCCESS(rc))
+ *phNative = (RTHCINTPTR)pThis->hev;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+/**
+ * Checks for pending events.
+ *
+ * @returns Event mask or 0.
+ * @param pThis The pipe handle.
+ * @param fEvents The desired events.
+ * @param fResetEvtSem Whether to reset the event semaphore.
+ */
+static uint32_t rtPipePollCheck(RTPIPEINTERNAL *pThis, uint32_t fEvents, bool fResetEvtSem)
+{
+ /*
+ * Reset the event semaphore if we're gonna wait.
+ */
+ APIRET orc;
+ ULONG ulIgnore;
+ if (fResetEvtSem)
+ {
+ orc = DosResetEventSem(pThis->hev, &ulIgnore);
+ AssertMsg(orc == NO_ERROR || orc == ERROR_ALREADY_RESET, ("%d\n", orc));
+ }
+
+ /*
+ * Check for events.
+ */
+ uint32_t fRetEvents = 0;
+ if (pThis->fBrokenPipe)
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ else if (pThis->fRead)
+ {
+ ULONG cbActual = 0;
+ ULONG ulState = 0;
+ AVAILDATA Avail = { 0, 0 };
+ orc = DosPeekNPipe(pThis->hPipe, NULL, 0, &cbActual, &Avail, &ulState);
+ if (orc != NO_ERROR)
+ {
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ if (orc == ERROR_BROKEN_PIPE || orc == ERROR_PIPE_NOT_CONNECTED)
+ pThis->fBrokenPipe = true;
+ }
+ else if (Avail.cbpipe > 0)
+ fRetEvents |= RTPOLL_EVT_READ;
+ else if (ulState != NP_STATE_CONNECTED)
+ {
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ pThis->fBrokenPipe = true;
+ }
+ }
+ else
+ {
+ PIPESEMSTATE aStates[4]; RT_ZERO(aStates);
+ orc = DosQueryNPipeSemState((HSEM)pThis->hev, &aStates[0], sizeof(aStates));
+ if (orc == NO_ERROR)
+ {
+ int i = 0;
+ while (aStates[i].fStatus == NPSS_RDATA)
+ i++;
+ if (aStates[i].fStatus == NPSS_CLOSE)
+ {
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ pThis->fBrokenPipe = true;
+ }
+ else if ( aStates[i].fStatus == NPSS_WSPACE
+ && aStates[i].usAvail > 0)
+ fRetEvents |= RTPOLL_EVT_WRITE;
+ }
+ else
+ {
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ if (orc == ERROR_BROKEN_PIPE || orc == ERROR_PIPE_NOT_CONNECTED)
+ pThis->fBrokenPipe = true;
+ }
+ }
+
+ return fRetEvents & (fEvents | RTPOLL_EVT_ERROR);
+}
+
+
+uint32_t rtPipePollStart(RTPIPE hPipe, RTPOLLSET hPollSet, uint32_t fEvents, bool fFinalEntry, bool fNoWait)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, UINT32_MAX);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, UINT32_MAX);
+
+ /* Check that this is the only current use of this pipe. */
+ uint32_t fRetEvents;
+ if ( pThis->cUsers == 0
+ || pThis->hPollSet == NIL_RTPOLLSET)
+ {
+ fRetEvents = rtPipePollCheck(pThis, fEvents, fNoWait);
+ if (!fRetEvents && !fNoWait)
+ {
+ /* Mark the set busy while waiting. */
+ pThis->cUsers++;
+ pThis->hPollSet = hPollSet;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ fRetEvents = UINT32_MAX;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+ return fRetEvents;
+}
+
+
+uint32_t rtPipePollDone(RTPIPE hPipe, uint32_t fEvents, bool fFinalEntry, bool fHarvestEvents)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, 0);
+
+ Assert(pThis->cUsers > 0);
+
+ /* harvest events. */
+ uint32_t fRetEvents = rtPipePollCheck(pThis, fEvents, false);
+
+ /* update counters. */
+ pThis->cUsers--;
+ pThis->hPollSet = NIL_RTPOLLSET;
+
+ RTCritSectLeave(&pThis->CritSect);
+ return fRetEvents;
+}
diff --git a/src/VBox/Runtime/r3/os2/rtProcInitExePath-os2.cpp b/src/VBox/Runtime/r3/os2/rtProcInitExePath-os2.cpp
new file mode 100644
index 00000000..bd5e249a
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/rtProcInitExePath-os2.cpp
@@ -0,0 +1,71 @@
+/* $Id: rtProcInitExePath-os2.cpp $ */
+/** @file
+ * IPRT - rtProcInitName, OS/2.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * 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, in version 3 of the
+ * License.
+ *
+ * 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/path.h>
+#include "internal/process.h"
+#include "internal/path.h"
+
+
+DECLHIDDEN(int) rtProcInitExePath(char *pszPath, size_t cchPath)
+{
+ /*
+ * Query the image name from the dynamic linker, convert and return it.
+ */
+ _execname(pszPath, cchPath);
+
+ char const *pszTmp;
+ int rc = rtPathFromNative(&pszTmp, pszPath, NULL);
+ AssertMsgRCReturn(rc, ("rc=%Rrc pszLink=\"%s\"\nhex: %.*Rhxs\n", rc, pszPath, cchPath, pszPath), rc);
+ if (pszTmp != pszPath)
+ {
+ rc = RTStrCopy(pszPath, cchPath, pszTmp);
+ rtPathFreeIprt(pszTmp, pszPath);
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/os2/sched-os2.cpp b/src/VBox/Runtime/r3/os2/sched-os2.cpp
new file mode 100644
index 00000000..25eb75c3
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/sched-os2.cpp
@@ -0,0 +1,244 @@
+/* $Id: sched-os2.cpp $ */
+/** @file
+ * IPRT - Scheduling, OS/2
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * 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, in version 3 of the
+ * License.
+ *
+ * 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/** @def OS2_SCHED_ENABLED
+ * Enables the priority scheme. */
+#define OS2_SCHED_ENABLED
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#define INCL_BASE
+#define INCL_ERRORS
+#include <os2.h>
+
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "internal/sched.h"
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Configuration of one priority.
+ */
+typedef struct
+{
+ /** The priority. */
+ RTPROCPRIORITY enmPriority;
+ /** The name of this priority. */
+ const char *pszName;
+ /** Array scheduler attributes corresponding to each of the thread types. */
+ struct
+ {
+ /** For sanity include the array index. */
+ RTTHREADTYPE enmType;
+ /** The OS/2 priority class. */
+ ULONG ulClass;
+ /** The OS/2 priority delta. */
+ ULONG ulDelta;
+ } aTypes[RTTHREADTYPE_END];
+} PROCPRIORITY;
+
+/** Matches any process priority class. */
+#define ANY_PROCESS_PRIORITY_CLASS (~0U)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Array of static priority configurations.
+ */
+static const PROCPRIORITY g_aPriorities[] =
+{
+ {
+ RTPROCPRIORITY_FLAT, "Flat",
+ {
+ { RTTHREADTYPE_INVALID, ~0, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_EMULATION, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_DEFAULT, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_GUI, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_MAIN_WORKER, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_VRDP_IO, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_DEBUGGER, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_MSG_PUMP, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_IO, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_TIMER, PRTYC_REGULAR, 0 }
+ }
+ },
+ {
+ RTPROCPRIORITY_LOW, "Low",
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, PRTYC_IDLETIME, 0 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, PRTYC_IDLETIME, 0 },
+ { RTTHREADTYPE_EMULATION, PRTYC_IDLETIME, 0 },
+ { RTTHREADTYPE_DEFAULT, PRTYC_IDLETIME, 30 },
+ { RTTHREADTYPE_GUI, PRTYC_IDLETIME, 30 },
+ { RTTHREADTYPE_MAIN_WORKER, PRTYC_IDLETIME, 30 },
+ { RTTHREADTYPE_VRDP_IO, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_DEBUGGER, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_MSG_PUMP, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_IO, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_TIMER, PRTYC_REGULAR, 0 }
+ }
+ },
+ {
+ RTPROCPRIORITY_NORMAL, "Normal",
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, PRTYC_IDLETIME, 30 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, PRTYC_IDLETIME, 31 },
+ { RTTHREADTYPE_EMULATION, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_DEFAULT, PRTYC_REGULAR, 5 },
+ { RTTHREADTYPE_GUI, PRTYC_REGULAR, 10 },
+ { RTTHREADTYPE_MAIN_WORKER, PRTYC_REGULAR, 12 },
+ { RTTHREADTYPE_VRDP_IO, PRTYC_REGULAR, 15 },
+ { RTTHREADTYPE_DEBUGGER, PRTYC_REGULAR, 20 },
+ { RTTHREADTYPE_MSG_PUMP, PRTYC_REGULAR, 25 },
+ { RTTHREADTYPE_IO, PRTYC_FOREGROUNDSERVER, 5 },
+ { RTTHREADTYPE_TIMER, PRTYC_TIMECRITICAL, 0 }
+ }
+ },
+ {
+ RTPROCPRIORITY_HIGH, "High",
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, PRTYC_IDLETIME, 30 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_EMULATION, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_DEFAULT, PRTYC_REGULAR, 15 },
+ { RTTHREADTYPE_GUI, PRTYC_REGULAR, 20 },
+ { RTTHREADTYPE_MAIN_WORKER, PRTYC_REGULAR, 25 },
+ { RTTHREADTYPE_VRDP_IO, PRTYC_REGULAR, 30 },
+ { RTTHREADTYPE_DEBUGGER, PRTYC_TIMECRITICAL, 2 },
+ { RTTHREADTYPE_MSG_PUMP, PRTYC_TIMECRITICAL, 3 },
+ { RTTHREADTYPE_IO, PRTYC_TIMECRITICAL, 4 },
+ { RTTHREADTYPE_TIMER, PRTYC_TIMECRITICAL, 5 }
+ }
+ }
+};
+
+/**
+ * The dynamic default priority configuration.
+ *
+ * This can be recalulated at runtime depending on what the
+ * system allow us to do. Presently we don't do this as it's
+ * generally not a bit issue on OS/2 hosts.
+ */
+static PROCPRIORITY g_aDefaultPriority =
+{
+ RTPROCPRIORITY_LOW, "Default",
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, PRTYC_IDLETIME, 30 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, PRTYC_IDLETIME, 31 },
+ { RTTHREADTYPE_EMULATION, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_DEFAULT, PRTYC_REGULAR, 5 },
+ { RTTHREADTYPE_GUI, PRTYC_REGULAR, 10 },
+ { RTTHREADTYPE_MAIN_WORKER, PRTYC_REGULAR, 12 },
+ { RTTHREADTYPE_VRDP_IO, PRTYC_REGULAR, 15 },
+ { RTTHREADTYPE_DEBUGGER, PRTYC_REGULAR, 20 },
+ { RTTHREADTYPE_MSG_PUMP, PRTYC_REGULAR, 25 },
+ { RTTHREADTYPE_IO, PRTYC_FOREGROUNDSERVER, 5 },
+ { RTTHREADTYPE_TIMER, PRTYC_TIMECRITICAL, 0 }
+ }
+};
+
+
+/** Pointer to the current priority configuration. */
+static const PROCPRIORITY *g_pProcessPriority = &g_aDefaultPriority;
+
+
+/**
+ * Calculate the scheduling properties for all the threads in the default
+ * process priority, assuming the current thread have the type enmType.
+ *
+ * @returns iprt status code.
+ * @param enmType The thread type to be assumed for the current thread.
+ */
+DECLHIDDEN(int) rtSchedNativeCalcDefaultPriority(RTTHREADTYPE enmType)
+{
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtProcNativeSetPriority(RTPROCPRIORITY enmPriority)
+{
+ Assert(enmPriority > RTPROCPRIORITY_INVALID && enmPriority < RTPROCPRIORITY_LAST);
+
+ if (enmPriority == RTPROCPRIORITY_DEFAULT)
+ {
+ g_pProcessPriority = &g_aDefaultPriority;
+ return VINF_SUCCESS;
+ }
+
+ for (size_t i = 0; i < RT_ELEMENTS(g_aPriorities); i++)
+ if (g_aPriorities[i].enmPriority == enmPriority)
+ {
+ g_pProcessPriority = &g_aPriorities[i];
+ return VINF_SUCCESS;
+ }
+
+ AssertFailedReturn(VERR_INTERNAL_ERROR);
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
+ AssertMsg(g_pProcessPriority && g_pProcessPriority->aTypes[enmType].enmType == enmType,
+ ("enmType=%d entry=%d\n", enmType, g_pProcessPriority->aTypes[enmType].enmType));
+
+#ifdef OS2_SCHED_ENABLED
+ APIRET rc = DosSetPriority(PRTYS_THREAD, g_pProcessPriority->aTypes[enmType].ulClass, g_pProcessPriority->aTypes[enmType].ulDelta, (ULONG)pThread->Core.Key & 0xffff /*tid*/);
+ AssertMsg(rc == NO_ERROR, ("%d\n", rc));
+ return RTErrConvertFromOS2(rc);
+#else
+ return VINF_SUCCESS;
+#endif
+}
+
diff --git a/src/VBox/Runtime/r3/os2/sems-os2.cpp b/src/VBox/Runtime/r3/os2/sems-os2.cpp
new file mode 100644
index 00000000..d4b8136e
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/sems-os2.cpp
@@ -0,0 +1,392 @@
+/* $Id: sems-os2.cpp $ */
+/** @file
+ * IPRT - Semaphores, OS/2.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * 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, in version 3 of the
+ * License.
+ *
+ * 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define INCL_DOSSEMAPHORES
+#define INCL_ERRORS
+#include <os2.h>
+#undef RT_MAX
+
+#include <iprt/semaphore.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+
+
+/** Converts semaphore to OS/2 handle. */
+#define SEM2HND(Sem) ((LHANDLE)(uintptr_t)Sem)
+
+
+
+RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
+{
+ return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
+ Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
+
+ /*
+ * Create the semaphore.
+ * (Auto reset, not signaled, private event object.)
+ */
+ HEV hev;
+ int rc = DosCreateEventSem(NULL, &hev, DCE_AUTORESET | DCE_POSTONE, 0);
+ if (!rc)
+ {
+ *phEventSem = (RTSEMEVENT)(void *)hev;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
+{
+ if (hEventSem == NIL_RTSEMEVENT)
+ return VINF_SUCCESS;
+
+ /*
+ * Close semaphore handle.
+ */
+ int rc = DosCloseEventSem(SEM2HND(hEventSem));
+ if (!rc)
+ return VINF_SUCCESS;
+ AssertMsgFailed(("Destroy hEventSem %p failed, rc=%d\n", hEventSem, rc));
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
+{
+ /*
+ * Wait for condition.
+ */
+ int rc = DosWaitEventSem(SEM2HND(hEventSem), cMillies == RT_INDEFINITE_WAIT ? SEM_INDEFINITE_WAIT : cMillies);
+ switch (rc)
+ {
+ case NO_ERROR: return VINF_SUCCESS;
+ case ERROR_SEM_TIMEOUT:
+ case ERROR_TIMEOUT: return VERR_TIMEOUT;
+ case ERROR_INTERRUPT: return VERR_INTERRUPTED;
+ default:
+ {
+ AssertMsgFailed(("Wait on hEventSem %p failed, rc=%d\n", hEventSem, rc));
+ return RTErrConvertFromOS2(rc);
+ }
+ }
+}
+
+
+RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
+{
+ /*
+ * Signal the object.
+ */
+ int rc = DosPostEventSem(SEM2HND(hEventSem));
+ switch (rc)
+ {
+ case NO_ERROR:
+ case ERROR_ALREADY_POSTED:
+ case ERROR_TOO_MANY_POSTS:
+ return VINF_SUCCESS;
+ default:
+ return RTErrConvertFromOS2(rc);
+ }
+}
+
+
+RTDECL(void) RTSemEventSetSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+/** @todo implement RTSemEventSetSignaller and friends for OS/2 */
+}
+
+
+RTDECL(void) RTSemEventAddSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+
+}
+
+
+RTDECL(void) RTSemEventRemoveSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+
+}
+
+
+
+
+RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
+{
+ return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
+ const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+
+ /*
+ * Create the semaphore.
+ * (Manual reset, not signaled, private event object.)
+ */
+ HEV hev;
+ int rc = DosCreateEventSem(NULL, &hev, 0, FALSE);
+ if (!rc)
+ {
+ *phEventMultiSem = (RTSEMEVENTMULTI)(void *)hev;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
+{
+ if (hEventMultiSem == NIL_RTSEMEVENTMULTI)
+ return VINF_SUCCESS;
+
+ /*
+ * Close semaphore handle.
+ */
+ int rc = DosCloseEventSem(SEM2HND(hEventMultiSem));
+ if (!rc)
+ return VINF_SUCCESS;
+ AssertMsgFailed(("Destroy hEventMultiSem %p failed, rc=%d\n", hEventMultiSem, rc));
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Signal the object.
+ */
+ int rc = DosPostEventSem(SEM2HND(hEventMultiSem));
+ switch (rc)
+ {
+ case NO_ERROR:
+ case ERROR_ALREADY_POSTED:
+ case ERROR_TOO_MANY_POSTS:
+ return VINF_SUCCESS;
+ default:
+ return RTErrConvertFromOS2(rc);
+ }
+}
+
+
+RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Reset the object.
+ */
+ ULONG ulIgnore;
+ int rc = DosResetEventSem(SEM2HND(hEventMultiSem), &ulIgnore);
+ switch (rc)
+ {
+ case NO_ERROR:
+ case ERROR_ALREADY_RESET:
+ return VINF_SUCCESS;
+ default:
+ return RTErrConvertFromOS2(rc);
+ }
+}
+
+
+RTDECL(int) RTSemEventMultiWaitNoResume(RTSEMEVENTMULTI hEventMultiSem, RTMSINTERVAL cMillies)
+{
+ /*
+ * Wait for condition.
+ */
+ int rc = DosWaitEventSem(SEM2HND(hEventMultiSem), cMillies == RT_INDEFINITE_WAIT ? SEM_INDEFINITE_WAIT : cMillies);
+ switch (rc)
+ {
+ case NO_ERROR: return VINF_SUCCESS;
+ case ERROR_SEM_TIMEOUT:
+ case ERROR_TIMEOUT: return VERR_TIMEOUT;
+ case ERROR_INTERRUPT: return VERR_INTERRUPTED;
+ default:
+ {
+ AssertMsgFailed(("Wait on hEventMultiSem %p failed, rc=%d\n", hEventMultiSem, rc));
+ return RTErrConvertFromOS2(rc);
+ }
+ }
+}
+
+
+RTDECL(void) RTSemEventMultiSetSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+ /** @todo implement RTSemEventMultiSetSignaller on OS/2 */
+}
+
+
+RTDECL(void) RTSemEventMultiAddSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+}
+
+
+RTDECL(void) RTSemEventMultiRemoveSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+}
+
+
+
+#undef RTSemMutexCreate
+RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem)
+{
+ return RTSemMutexCreateEx(phMutexSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
+}
+
+
+RTDECL(int) RTSemMutexCreateEx(PRTSEMMUTEX phMutexSem, uint32_t fFlags,
+ RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMMUTEX_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+
+ /*
+ * Create the semaphore.
+ */
+ HMTX hmtx;
+ int rc = DosCreateMutexSem(NULL, &hmtx, 0, FALSE);
+ if (!rc)
+ {
+ /** @todo implement lock validation of OS/2 mutex semaphores. */
+ *phMutexSem = (RTSEMMUTEX)(void *)hmtx;
+ return VINF_SUCCESS;
+ }
+
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem)
+{
+ if (hMutexSem == NIL_RTSEMMUTEX)
+ return VINF_SUCCESS;
+
+ /*
+ * Close semaphore handle.
+ */
+ int rc = DosCloseMutexSem(SEM2HND(hMutexSem));
+ if (!rc)
+ return VINF_SUCCESS;
+ AssertMsgFailed(("Destroy hMutexSem %p failed, rc=%d\n", hMutexSem, rc));
+ return RTErrConvertFromOS2(rc);
+}
+
+
+
+RTDECL(uint32_t) RTSemMutexSetSubClass(RTSEMMUTEX hMutexSem, uint32_t uSubClass)
+{
+#if 0 /** @todo def RTSEMMUTEX_STRICT */
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, RTLOCKVAL_SUB_CLASS_INVALID);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID);
+
+ return RTLockValidatorRecExclSetSubClass(&pThis->ValidatorRec, uSubClass);
+#else
+ return RTLOCKVAL_SUB_CLASS_INVALID;
+#endif
+}
+
+
+#undef RTSemMutexRequestNoResume
+RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ /*
+ * Lock mutex semaphore.
+ */
+ int rc = DosRequestMutexSem(SEM2HND(hMutexSem), cMillies == RT_INDEFINITE_WAIT ? SEM_INDEFINITE_WAIT : cMillies);
+ switch (rc)
+ {
+ case NO_ERROR: return VINF_SUCCESS;
+ case ERROR_SEM_TIMEOUT:
+ case ERROR_TIMEOUT: return VERR_TIMEOUT;
+ case ERROR_INTERRUPT: return VERR_INTERRUPTED;
+ case ERROR_SEM_OWNER_DIED: return VERR_SEM_OWNER_DIED;
+ default:
+ {
+ AssertMsgFailed(("Wait on hMutexSem %p failed, rc=%d\n", hMutexSem, rc));
+ return RTErrConvertFromOS2(rc);
+ }
+ }
+}
+
+RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+// RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+// return rtSemMutexRequestNoResume(hMutexSem, cMillies, &SrcPos);
+ return RTSemMutexRequestNoResume(hMutexSem, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Unlock mutex semaphore.
+ */
+ int rc = DosReleaseMutexSem(SEM2HND(hMutexSem));
+ if (!rc)
+ return VINF_SUCCESS;
+ AssertMsgFailed(("Release hMutexSem %p failed, rc=%d\n", hMutexSem, rc));
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Unlock mutex semaphore.
+ */
+ PID pid;
+ TID tid;
+ ULONG cRecursions;
+ int rc = DosQueryMutexSem(SEM2HND(hMutexSem), &pid, &tid, &cRecursions);
+ if (!rc)
+ return cRecursions != 0;
+ AssertMsgFailed(("DosQueryMutexSem %p failed, rc=%d\n", hMutexSem, rc));
+ return rc == ERROR_SEM_OWNER_DIED;
+}
+
diff --git a/src/VBox/Runtime/r3/os2/serialport-os2.cpp b/src/VBox/Runtime/r3/os2/serialport-os2.cpp
new file mode 100644
index 00000000..95158dea
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/serialport-os2.cpp
@@ -0,0 +1,755 @@
+/* $Id: serialport-os2.cpp $ */
+/** @file
+ * IPRT - Serial Port API, OS/2 Implementation.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * 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, in version 3 of the
+ * License.
+ *
+ * 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define INCL_BASE
+#define INCL_DOSFILEMGR
+#define INCL_ERRORS
+#define INCL_DOS
+#define INCL_DOSDEVIOCTL
+#define INCL_DOSDEVICES
+#include <os2.h>
+#undef RT_MAX
+
+#include <iprt/serialport.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/cdefs.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include "internal/magics.h"
+
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Returned data structure for ASYNC_EXTGETBAUDRATE.
+ */
+typedef struct OS2EXTGETBAUDRATEDATA
+{
+ /** Current bit rate. */
+ ULONG uBitRateCur;
+ /** Fraction of the current bit rate. */
+ BYTE bBitRateCurFrac;
+ /** Minimum supported bit rate. */
+ ULONG uBitRateMin;
+ /** Fraction of the minimum bit rate. */
+ BYTE bBitRateCurMin;
+ /** Maximum supported bit rate. */
+ ULONG uBitRateMax;
+ /** Fraction of the maximum bit rate. */
+ BYTE bBitRateCurMax;
+} OS2EXTGETBAUDRATEDATA;
+/** Pointer to the get extended baud rate data packet. */
+typedef OS2EXTGETBAUDRATEDATA *POS2EXTGETBAUDRATEDATA;
+
+
+/**
+ * Data packet for the ASYNC_EXTSETBAUDRATE ioctl.
+ */
+typedef struct OS2EXTSETBAUDRATEDATA
+{
+ /** Current bit rate. */
+ ULONG uBitRate;
+ /** Fraction of the current bit rate. */
+ BYTE bBitRateFrac;
+} OS2EXTSETBAUDRATEDATA;
+/** Pointer to the set extended baud rate data packet. */
+typedef OS2EXTSETBAUDRATEDATA *POS2EXTSETBAUDRATEDATA;
+
+
+/**
+ * Data packet for the ASYNC_GETLINECTRL ioctl.
+ */
+typedef struct OS2GETLINECTRLDATA
+{
+ /** Returns the current amount of data bits in a symbol used for the communication. */
+ BYTE bDataBits;
+ /** Current parity setting. */
+ BYTE bParity;
+ /** Current number of stop bits. */
+ BYTE bStopBits;
+ /** Flag whether a break condition is currently transmitted on the line. */
+ BYTE bTxBrk;
+} OS2GETLINECTRLDATA;
+/** Pointer to the get line control data packet. */
+typedef OS2GETLINECTRLDATA *POS2GETLINECTRLDATA;
+
+
+/**
+ * Data packet for the ASYNC_SETLINECTRL ioctl.
+ */
+typedef struct OS2SETLINECTRLDATA
+{
+ /** Returns the current amount of data bits in a symbol used for the communication. */
+ BYTE bDataBits;
+ /** Current parity setting. */
+ BYTE bParity;
+ /** Current number of stop bits. */
+ BYTE bStopBits;
+} OS2SETLINECTRLDATA;
+/** Pointer to the get line control data packet. */
+typedef OS2SETLINECTRLDATA *POS2SETLINECTRLDATA;
+
+
+/**
+ * Internal serial port state.
+ */
+typedef struct RTSERIALPORTINTERNAL
+{
+ /** Magic value (RTSERIALPORT_MAGIC). */
+ uint32_t u32Magic;
+ /** Flags given while opening the serial port. */
+ uint32_t fOpenFlags;
+ /** The file descriptor of the serial port. */
+ HFILE hDev;
+ /** Flag whether blocking mode is currently enabled. */
+ bool fBlocking;
+ /** Flag whether RTSerialPortEvtPoll() was interrupted by RTSerialPortEvtPollInterrupt(). */
+ volatile bool fInterrupt;
+} RTSERIALPORTINTERNAL;
+/** Pointer to the internal serial port state. */
+typedef RTSERIALPORTINTERNAL *PRTSERIALPORTINTERNAL;
+
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/** Indicator whether the CTS input is set/clear. */
+#define OS2_GET_MODEM_INPUT_CTS RT_BIT(4)
+/** Indicator whether the DSR input is set/clear. */
+#define OS2_GET_MODEM_INPUT_DSR RT_BIT(5)
+/** Indicator whether the RI input is set/clear. */
+#define OS2_GET_MODEM_INPUT_RI RT_BIT(6)
+/** Indicator whether the DCD input is set/clear. */
+#define OS2_GET_MODEM_INPUT_DCD RT_BIT(7)
+
+/** There is something to read on the serial port. */
+#define OS2_GET_COMM_EVT_RX RT_BIT(0)
+/** A receive timeout interrupt was generated on the serial port during a read request. */
+#define OS2_GET_COMM_EVT_RTI RT_BIT(1)
+/** The transmit queue for the serial port is empty. */
+#define OS2_GET_COMM_EVT_TX_EMPTY RT_BIT(2)
+/** The CTS signal changes state. */
+#define OS2_GET_COMM_EVT_CTS_CHG RT_BIT(3)
+/** The DSR signal changes state. */
+#define OS2_GET_COMM_EVT_DSR_CHG RT_BIT(4)
+/** The DCD signal changes state. */
+#define OS2_GET_COMM_EVT_DCD_CHG RT_BIT(5)
+/** A break condition was detected on the serial port. */
+#define OS2_GET_COMM_EVT_BRK RT_BIT(6)
+/** A parity, framing or receive hardware overrun error occurred. */
+#define OS2_GET_COMM_EVT_COMM_ERR RT_BIT(7)
+/** Trailing edge ring indicator was detected. */
+#define OS2_GET_COMM_EVT_RI_TRAIL_EDGE RT_BIT(8)
+
+
+/*********************************************************************************************************************************
+* Global variables *
+*********************************************************************************************************************************/
+/** OS/2 parity value to IPRT parity enum. */
+static RTSERIALPORTPARITY s_aParityConvTbl[] =
+{
+ RTSERIALPORTPARITY_NONE,
+ RTSERIALPORTPARITY_ODD,
+ RTSERIALPORTPARITY_EVEN,
+ RTSERIALPORTPARITY_MARK,
+ RTSERIALPORTPARITY_SPACE
+};
+
+/** OS/2 data bits value to IPRT data bits enum. */
+static RTSERIALPORTDATABITS s_aDataBitsConvTbl[] =
+{
+ RTSERIALPORTDATABITS_INVALID,
+ RTSERIALPORTDATABITS_INVALID,
+ RTSERIALPORTDATABITS_INVALID,
+ RTSERIALPORTDATABITS_INVALID,
+ RTSERIALPORTDATABITS_INVALID,
+ RTSERIALPORTDATABITS_5BITS,
+ RTSERIALPORTDATABITS_6BITS,
+ RTSERIALPORTDATABITS_7BITS,
+ RTSERIALPORTDATABITS_8BITS
+};
+
+/** OS/2 stop bits value to IPRT stop bits enum. */
+static RTSERIALPORTSTOPBITS s_aStopBitsConvTbl[] =
+{
+ RTSERIALPORTSTOPBITS_ONE,
+ RTSERIALPORTSTOPBITS_ONEPOINTFIVE,
+ RTSERIALPORTSTOPBITS_TWO
+};
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/**
+ * The slow path of rtSerialPortSwitchBlockingMode that does the actual switching.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal serial port instance data.
+ * @param fBlocking The desired mode of operation.
+ * @remarks Do not call directly.
+ *
+ * @note Affects only read behavior.
+ */
+static int rtSerialPortSwitchBlockingModeSlow(PRTSERIALPORTINTERNAL pThis, bool fBlocking)
+{
+ DCBINFO DcbInfo;
+ ULONG cbDcbInfo = sizeof(DcbInfo);
+ ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_GETDCBINFO, NULL, 0, NULL, &DcbInfo, cbDcbInfo, &cbDcbInfo);
+ if (!rcOs2)
+ {
+ DcbInfo.fbTimeout &= ~0x06;
+ DcbInfo.fbTimeout |= fBlocking ? 0x04 : 0x06;
+ rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_SETDCBINFO, &DcbInfo, cbDcbInfo, &cbDcbInfo, NULL, 0, NULL);
+ if (rcOs2)
+ return RTErrConvertFromOS2(rcOs2);
+ }
+ else
+ return RTErrConvertFromOS2(rcOs2);
+
+ pThis->fBlocking = fBlocking;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Switches the serial port to the desired blocking mode if necessary.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal serial port instance data.
+ * @param fBlocking The desired mode of operation.
+ *
+ * @note Affects only read behavior.
+ */
+DECLINLINE(int) rtSerialPortSwitchBlockingMode(PRTSERIALPORTINTERNAL pThis, bool fBlocking)
+{
+ if (pThis->fBlocking != fBlocking)
+ return rtSerialPortSwitchBlockingModeSlow(pThis, fBlocking);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSerialPortOpen(PRTSERIALPORT phSerialPort, const char *pszPortAddress, uint32_t fFlags)
+{
+ AssertPtrReturn(phSerialPort, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszPortAddress, VERR_INVALID_POINTER);
+ AssertReturn(*pszPortAddress != '\0', VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTSERIALPORT_OPEN_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn((fFlags & RTSERIALPORT_OPEN_F_READ) || (fFlags & RTSERIALPORT_OPEN_F_WRITE),
+ VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ PRTSERIALPORTINTERNAL pThis = (PRTSERIALPORTINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ ULONG fOpenMode = OPEN_SHARE_DENYREADWRITE
+ | OPEN_FLAGS_SEQUENTIAL
+ | OPEN_FLAGS_NOINHERIT
+ | OPEN_FLAGS_FAIL_ON_ERROR;
+
+ if ((fFlags & RTSERIALPORT_OPEN_F_READ) && !(fFlags & RTSERIALPORT_OPEN_F_WRITE))
+ fOpenMode |= OPEN_ACCESS_READONLY;
+ else if (!(fFlags & RTSERIALPORT_OPEN_F_READ) && (fFlags & RTSERIALPORT_OPEN_F_WRITE))
+ fOpenMode |= OPEN_ACCESS_WRITEONLY;
+ else
+ fOpenMode |= OPEN_ACCESS_READWRITE;
+
+ pThis->u32Magic = RTSERIALPORT_MAGIC;
+ pThis->fOpenFlags = fFlags;
+ pThis->fInterrupt = false;
+ pThis->fBlocking = true;
+
+ ULONG uAction = 0;
+ ULONG rcOs2 = DosOpen((const UCHAR *)pszPortAddress, &pThis->hDev, &uAction, 0, FILE_NORMAL, FILE_OPEN, fOpenMode, NULL);
+ if (!rcOs2)
+ {
+ /* Switch to a known read blocking mode. */
+ rc = rtSerialPortSwitchBlockingMode(pThis, false);
+ if (RT_SUCCESS(rc))
+ {
+ *phSerialPort = pThis;
+ return VINF_SUCCESS;
+ }
+
+ DosClose(pThis->hDev);
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortClose(RTSERIALPORT hSerialPort)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ if (pThis == NIL_RTSERIALPORT)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the cleanup.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSERIALPORT_MAGIC_DEAD, RTSERIALPORT_MAGIC), VERR_INVALID_HANDLE);
+
+ DosClose(pThis->hDev);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(RTHCINTPTR) RTSerialPortToNative(RTSERIALPORT hSerialPort)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, -1);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, -1);
+
+ return pThis->hDev;
+}
+
+
+RTDECL(int) RTSerialPortRead(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
+
+ int rc = rtSerialPortSwitchBlockingMode(pThis, true);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Attempt read.
+ */
+ ULONG cbRead = 0;
+ ULONG rcOs2 = DosRead(pThis->hDev, pvBuf, cbToRead, &cbRead);
+ if (!rcOs2)
+ {
+ if (pcbRead)
+ /* caller can handle partial read. */
+ *pcbRead = cbRead;
+ else
+ {
+ /* Caller expects all to be read. */
+ while (cbToRead > cbRead)
+ {
+ ULONG cbReadPart = 0;
+ rcOs2 = DosRead(pThis->hDev, (uint8_t *)pvBuf + cbRead, cbToRead - cbRead, &cbReadPart);
+ if (rcOs2)
+ return RTErrConvertFromOS2(rcOs2);
+
+ cbRead += cbReadPart;
+ }
+ }
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortReadNB(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
+
+ *pcbRead = 0;
+
+ int rc = rtSerialPortSwitchBlockingMode(pThis, false);
+ if (RT_SUCCESS(rc))
+ {
+ ULONG cbThisRead = 0;
+ ULONG rcOs2 = DosRead(pThis->hDev, pvBuf, cbToRead, &cbThisRead);
+ if (!rcOs2)
+ {
+ *pcbRead = cbThisRead;
+
+ if (cbThisRead == 0)
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortWrite(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
+
+ /*
+ * Attempt write.
+ */
+ int rc = VINF_SUCCESS;
+ ULONG cbThisWritten = 0;
+ ULONG rcOs2 = DosWrite(pThis->hDev, pvBuf, cbToWrite, &cbThisWritten);
+ if (!rcOs2)
+ {
+ if (pcbWritten)
+ /* caller can handle partial write. */
+ *pcbWritten = cbThisWritten;
+ else
+ {
+ /** @todo Wait for TX empty and loop. */
+ rc = VERR_NOT_SUPPORTED;
+ }
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortWriteNB(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
+
+ *pcbWritten = 0;
+
+ int rc = VINF_SUCCESS;
+ ULONG cbThisWritten = 0;
+ ULONG rcOs2 = DosWrite(pThis->hDev, pvBuf, cbToWrite, &cbThisWritten);
+ if (!rcOs2)
+ {
+ *pcbWritten = cbThisWritten;
+ if (!cbThisWritten)
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortCfgQueryCurrent(RTSERIALPORT hSerialPort, PRTSERIALPORTCFG pCfg)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ int rc = VINF_SUCCESS;
+ OS2EXTGETBAUDRATEDATA ExtBaudRate;
+ ULONG cbExtBaudRate = sizeof(ExtBaudRate);
+ ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_EXTGETBAUDRATE, NULL, 0, NULL, &ExtBaudRate, cbExtBaudRate, &cbExtBaudRate);
+ if (!rcOs2)
+ {
+ OS2GETLINECTRLDATA LineCtrl;
+ ULONG cbLineCtrl = sizeof(LineCtrl);
+ rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_GETLINECTRL, NULL, 0, NULL, &LineCtrl, cbLineCtrl, &cbLineCtrl);
+ if (!rcOs2)
+ {
+ pCfg->uBaudRate = ExtBaudRate.uBitRateCur;
+ if (LineCtrl.bParity < RT_ELEMENTS(s_aParityConvTbl))
+ pCfg->enmParity = s_aParityConvTbl[LineCtrl.bParity];
+ else
+ rc = VERR_IPE_UNEXPECTED_STATUS;
+
+ if ( RT_SUCCESS(rc)
+ && LineCtrl.bDataBits < RT_ELEMENTS(s_aDataBitsConvTbl)
+ && s_aDataBitsConvTbl[LineCtrl.bDataBits] != RTSERIALPORTDATABITS_INVALID)
+ pCfg->enmDataBitCount = s_aDataBitsConvTbl[LineCtrl.bDataBits];
+ else
+ rc = VERR_IPE_UNEXPECTED_STATUS;
+
+ if ( RT_SUCCESS(rc)
+ && LineCtrl.bStopBits < RT_ELEMENTS(s_aStopBitsConvTbl))
+ pCfg->enmStopBitCount = s_aStopBitsConvTbl[LineCtrl.bStopBits];
+ else
+ rc = VERR_IPE_UNEXPECTED_STATUS;
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortCfgSet(RTSERIALPORT hSerialPort, PCRTSERIALPORTCFG pCfg, PRTERRINFO pErrInfo)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ int rc = VINF_SUCCESS;
+ OS2EXTSETBAUDRATEDATA ExtBaudRate;
+ OS2SETLINECTRLDATA LineCtrl;
+ ULONG cbExtBaudRate = sizeof(ExtBaudRate);
+ ULONG cbLineCtrl = sizeof(LineCtrl);
+
+ ExtBaudRate.uBitRate = pCfg->uBaudRate;
+ ExtBaudRate.bBitRateFrac = 0;
+
+ BYTE idx = 0;
+ while (idx < RT_ELEMENTS(s_aParityConvTbl))
+ {
+ if (s_aParityConvTbl[idx] == pCfg->enmParity)
+ {
+ LineCtrl.bParity = idx;
+ break;
+ }
+ idx++;
+ }
+ AssertReturn(idx < RT_ELEMENTS(s_aParityConvTbl), VERR_INTERNAL_ERROR);
+
+ idx = 0;
+ while (idx < RT_ELEMENTS(s_aDataBitsConvTbl))
+ {
+ if (s_aDataBitsConvTbl[idx] == pCfg->enmDataBitCount)
+ {
+ LineCtrl.bDataBits = idx;
+ break;
+ }
+ idx++;
+ }
+ AssertReturn(idx < RT_ELEMENTS(s_aDataBitsConvTbl), VERR_INTERNAL_ERROR);
+
+ idx = 0;
+ while (idx < RT_ELEMENTS(s_aStopBitsConvTbl))
+ {
+ if (s_aStopBitsConvTbl[idx] == pCfg->enmStopBitCount)
+ {
+ LineCtrl.bStopBits = idx;
+ break;
+ }
+ idx++;
+ }
+ AssertReturn(idx < RT_ELEMENTS(s_aStopBitsConvTbl), VERR_INTERNAL_ERROR);
+
+ ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_EXTSETBAUDRATE, &ExtBaudRate, cbExtBaudRate, &cbExtBaudRate, NULL, 0, NULL);
+ if (!rcOs2)
+ {
+ rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_SETLINECTRL, &LineCtrl, cbLineCtrl, &cbLineCtrl, NULL, 0, NULL);
+ if (rcOs2)
+ rc = RTErrConvertFromOS2(rcOs2);
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortEvtPoll(RTSERIALPORT hSerialPort, uint32_t fEvtMask, uint32_t *pfEvtsRecv,
+ RTMSINTERVAL msTimeout)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!(fEvtMask & ~RTSERIALPORT_EVT_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pfEvtsRecv, VERR_INVALID_POINTER);
+
+ *pfEvtsRecv = 0;
+
+ /*
+ * We need to kind of busy wait here as there is, to my knowledge, no API
+ * to wait for a COM event.
+ *
+ * @todo Adaptive waiting
+ * @todo Handle rollover after 48days eventually
+ */
+ int rc = VINF_SUCCESS;
+ uint64_t tsStart = RTTimeSystemMilliTS();
+ do
+ {
+ if (ASMAtomicXchgBool(&pThis->fInterrupt, false))
+ {
+ rc = VERR_INTERRUPTED;
+ break;
+ }
+
+ USHORT fCommEvt = 0;
+ ULONG cbCommEvt = sizeof(fCommEvt);
+ ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_GETCOMMEVENT, NULL, 0, NULL,
+ &fCommEvt, cbCommEvt, &cbCommEvt);
+ if (!rcOs2)
+ {
+ AssertReturn(cbCommEvt = sizeof(fCommEvt), VERR_IPE_UNEXPECTED_STATUS);
+
+ if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX)
+ && (fCommEvt & OS2_GET_COMM_EVT_RX))
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_RX;
+
+ /** @todo Is there something better to indicate that there is room in the queue instead of queue is empty? */
+ if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX)
+ && (fCommEvt & OS2_GET_COMM_EVT_TX_EMPTY))
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
+
+ if ( (fEvtMask & RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED)
+ && (fCommEvt & (OS2_GET_COMM_EVT_CTS_CHG | OS2_GET_COMM_EVT_DSR_CHG | OS2_GET_COMM_EVT_DCD_CHG)))
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED;
+
+ if ( (fEvtMask & RTSERIALPORT_EVT_F_BREAK_DETECTED)
+ && (fCommEvt & OS2_GET_COMM_EVT_BRK))
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_BREAK_DETECTED;
+
+ if (*pfEvtsRecv != 0)
+ break;
+ }
+ else
+ {
+ rc = RTErrConvertFromOS2(rcOs2);
+ break;
+ }
+
+ uint64_t tsNow = RTTimeSystemMilliTS();
+ if ( msTimeout == RT_INDEFINITE_WAIT
+ || tsNow - tsStart < msTimeout)
+ DosSleep(1);
+ else
+ rc = VERR_TIMEOUT;
+ } while (RT_SUCCESS(rc));
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortEvtPollInterrupt(RTSERIALPORT hSerialPort)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ ASMAtomicXchgBool(&pThis->fInterrupt, true);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSerialPortChgBreakCondition(RTSERIALPORT hSerialPort, bool fSet)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, fSet ? ASYNC_SETBREAKON : ASYNC_SETBREAKOFF,
+ NULL, 0, NULL, NULL, 0, NULL);
+
+ return RTErrConvertFromOS2(rcOs2);
+}
+
+
+RTDECL(int) RTSerialPortChgStatusLines(RTSERIALPORT hSerialPort, uint32_t fClear, uint32_t fSet)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ MODEMSTATUS MdmSts;
+ ULONG cbMdmSts = sizeof(MdmSts);
+
+ MdmSts.fbModemOn = (fSet & RTSERIALPORT_CHG_STS_LINES_F_RTS ? 0x02 : 0x00)
+ | (fSet & RTSERIALPORT_CHG_STS_LINES_F_DTR ? 0x01 : 0x00);
+ MdmSts.fbModemOff = 0xff;
+ MdmSts.fbModemOff &= ~( (fClear & RTSERIALPORT_CHG_STS_LINES_F_RTS ? 0x02 : 0x00)
+ | (fClear & RTSERIALPORT_CHG_STS_LINES_F_DTR ? 0x01 : 0x00));
+
+ ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_SETMODEMCTRL, &MdmSts, cbMdmSts, &cbMdmSts, NULL, 0, NULL);
+
+ return RTErrConvertFromOS2(rcOs2);
+}
+
+
+RTDECL(int) RTSerialPortQueryStatusLines(RTSERIALPORT hSerialPort, uint32_t *pfStsLines)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pfStsLines, VERR_INVALID_POINTER);
+
+ *pfStsLines = 0;
+
+ int rc = VINF_SUCCESS;
+ BYTE fStsLines = 0;
+ ULONG cbStsLines = sizeof(fStsLines);
+ ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_GETMODEMINPUT, NULL, 0, NULL, &fStsLines, cbStsLines, &cbStsLines);
+ if (!rcOs2)
+ {
+ AssertReturn(cbStsLines == sizeof(BYTE), VERR_IPE_UNEXPECTED_STATUS);
+
+ *pfStsLines |= (fStsLines & OS2_GET_MODEM_INPUT_DCD) ? RTSERIALPORT_STS_LINE_DCD : 0;
+ *pfStsLines |= (fStsLines & OS2_GET_MODEM_INPUT_RI) ? RTSERIALPORT_STS_LINE_RI : 0;
+ *pfStsLines |= (fStsLines & OS2_GET_MODEM_INPUT_DSR) ? RTSERIALPORT_STS_LINE_DSR : 0;
+ *pfStsLines |= (fStsLines & OS2_GET_MODEM_INPUT_CTS) ? RTSERIALPORT_STS_LINE_CTS : 0;
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/os2/systemmem-os2.cpp b/src/VBox/Runtime/r3/os2/systemmem-os2.cpp
new file mode 100644
index 00000000..71eb1adf
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/systemmem-os2.cpp
@@ -0,0 +1,80 @@
+/* $Id: systemmem-os2.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryTotalRam, OS/2 ring-3.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * 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, in version 3 of the
+ * License.
+ *
+ * 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define INCL_DOSMISC
+#define INCL_ERRORS
+#include <os2.h>
+#undef RT_MAX
+
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+
+RTDECL(int) RTSystemQueryTotalRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ ULONG cbMem;
+ APIRET rc = DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM, &cbMem, sizeof(cbMem));
+ if (rc != NO_ERROR)
+ return RTErrConvertFromOS2(rc);
+
+ *pcb = cbMem;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSystemQueryAvailableRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ ULONG cbAvailMem;
+ APIRET rc = DosQuerySysInfo(QSV_TOTAVAILMEM, QSV_TOTAVAILMEM, &cbAvailMem, sizeof(cbAvailMem));
+ if (rc != NO_ERROR)
+ return RTErrConvertFromOS2(rc);
+
+ *pcb = cbAvailMem;
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/os2/thread-os2.cpp b/src/VBox/Runtime/r3/os2/thread-os2.cpp
new file mode 100644
index 00000000..366f4803
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/thread-os2.cpp
@@ -0,0 +1,330 @@
+/* $Id: thread-os2.cpp $ */
+/** @file
+ * IPRT - Threads, OS/2.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * 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, in version 3 of the
+ * License.
+ *
+ * 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#define INCL_BASE
+#include <os2.h>
+#undef RT_MAX
+
+#include <errno.h>
+#include <process.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <InnoTekLIBC/FastInfoBlocks.h>
+#include <InnoTekLIBC/thread.h>
+
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/cpuset.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Pointer to thread local memory which points to the current thread. */
+static PRTTHREADINT *g_ppCurThread;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void rtThreadNativeMain(void *pvArgs);
+
+
+DECLHIDDEN(int) rtThreadNativeInit(void)
+{
+ /*
+ * Allocate thread local memory.
+ */
+ PULONG pul;
+ int rc = DosAllocThreadLocalMemory(1, &pul);
+ if (rc)
+ return VERR_NO_TLS_FOR_SELF;
+ g_ppCurThread = (PRTTHREADINT *)(void *)pul;
+ return VINF_SUCCESS;
+}
+
+
+static void rtThreadOs2BlockSigAlarm(void)
+{
+ /*
+ * Block SIGALRM - required for timer-posix.cpp.
+ * This is done to limit harm done by OSes which doesn't do special SIGALRM scheduling.
+ * It will not help much if someone creates threads directly using pthread_create. :/
+ */
+ sigset_t SigSet;
+ sigemptyset(&SigSet);
+ sigaddset(&SigSet, SIGALRM);
+ sigprocmask(SIG_BLOCK, &SigSet, NULL);
+}
+
+
+DECLHIDDEN(void) rtThreadNativeReInitObtrusive(void)
+{
+ rtThreadOs2BlockSigAlarm();
+}
+
+
+DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread)
+{
+
+ *g_ppCurThread = pThread;
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread)
+{
+ if (pThread == *g_ppCurThread)
+ *g_ppCurThread = NULL;
+}
+
+
+/**
+ * Wrapper which unpacks the params and calls thread function.
+ */
+static void rtThreadNativeMain(void *pvArgs)
+{
+ rtThreadOs2BlockSigAlarm();
+
+ /*
+ * Call common main.
+ */
+ PRTTHREADINT pThread = (PRTTHREADINT)pvArgs;
+ *g_ppCurThread = pThread;
+
+#ifdef fibGetTidPid
+ rtThreadMain(pThread, fibGetTidPid(), &pThread->szName[0]);
+#else
+ rtThreadMain(pThread, _gettid(), &pThread->szName[0]);
+#endif
+
+ *g_ppCurThread = NULL;
+ _endthread();
+}
+
+
+DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThread, PRTNATIVETHREAD pNativeThread)
+{
+ /*
+ * Default stack size.
+ */
+ if (!pThread->cbStack)
+ pThread->cbStack = 512*1024;
+
+ /*
+ * Create the thread.
+ */
+ int iThreadId = _beginthread(rtThreadNativeMain, NULL, pThread->cbStack, pThread);
+ if (iThreadId > 0)
+ {
+#ifdef fibGetTidPid
+ *pNativeThread = iThreadId | (fibGetPid() << 16);
+#else
+ *pNativeThread = iThreadId;
+#endif
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTDECL(RTTHREAD) RTThreadSelf(void)
+{
+ PRTTHREADINT pThread = *g_ppCurThread;
+ if (pThread)
+ return (RTTHREAD)pThread;
+ /** @todo import alien threads? */
+ return NULL;
+}
+
+
+RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void)
+{
+#ifdef fibGetTidPid
+ return fibGetTidPid();
+#else
+ return _gettid();
+#endif
+}
+
+
+RTDECL(int) RTThreadSleep(RTMSINTERVAL cMillies)
+{
+ LogFlow(("RTThreadSleep: cMillies=%d\n", cMillies));
+ DosSleep(cMillies);
+ LogFlow(("RTThreadSleep: returning (cMillies=%d)\n", cMillies));
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTThreadSleepNoLog(RTMSINTERVAL cMillies)
+{
+ DosSleep(cMillies);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(bool) RTThreadYield(void)
+{
+ uint64_t u64TS = ASMReadTSC();
+ DosSleep(0);
+ u64TS = ASMReadTSC() - u64TS;
+ bool fRc = u64TS > 1750;
+ LogFlow(("RTThreadYield: returning %d (%llu ticks)\n", fRc, u64TS));
+ return fRc;
+}
+
+
+RTR3DECL(int) RTThreadGetAffinity(PRTCPUSET pCpuSet)
+{
+ union
+ {
+ uint64_t u64;
+ MPAFFINITY mpaff;
+ } u;
+
+ APIRET rc = DosQueryThreadAffinity(AFNTY_THREAD, &u.mpaff);
+ if (!rc)
+ {
+ RTCpuSetFromU64(pCpuSet, u.u64);
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTR3DECL(int) RTThreadSetAffinity(PCRTCPUSET pCpuSet)
+{
+ union
+ {
+ uint64_t u64;
+ MPAFFINITY mpaff;
+ } u;
+ u.u64 = pCpuSet ? RTCpuSetToU64(pCpuSet) : UINT64_MAX;
+ int rc = DosSetThreadAffinity(&u.mpaff);
+ if (!rc)
+ return VINF_SUCCESS;
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTR3DECL(RTTLS) RTTlsAlloc(void)
+{
+ AssertCompile(NIL_RTTLS == -1);
+ return __libc_TLSAlloc();
+}
+
+
+RTR3DECL(int) RTTlsAllocEx(PRTTLS piTls, PFNRTTLSDTOR pfnDestructor)
+{
+ int rc;
+ int iTls = __libc_TLSAlloc();
+ if (iTls != -1)
+ {
+ if ( !pfnDestructor
+ || __libc_TLSDestructor(iTls, (void (*)(void *, int, unsigned))pfnDestructor, 0) != -1)
+ {
+ *piTls = iTls;
+ return VINF_SUCCESS;
+ }
+
+ rc = RTErrConvertFromErrno(errno);
+ __libc_TLSFree(iTls);
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ *piTls = NIL_RTTLS;
+ return rc;
+}
+
+
+RTR3DECL(int) RTTlsFree(RTTLS iTls)
+{
+ if (iTls == NIL_RTTLS)
+ return VINF_SUCCESS;
+ if (__libc_TLSFree(iTls) != -1)
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTR3DECL(void *) RTTlsGet(RTTLS iTls)
+{
+ return __libc_TLSGet(iTls);
+}
+
+
+RTR3DECL(int) RTTlsGetEx(RTTLS iTls, void **ppvValue)
+{
+ int rc = VINF_SUCCESS;
+ void *pv = __libc_TLSGet(iTls);
+ if (RT_UNLIKELY(!pv))
+ {
+ errno = 0;
+ pv = __libc_TLSGet(iTls);
+ if (!pv && errno)
+ rc = RTErrConvertFromErrno(errno);
+ }
+
+ *ppvValue = pv;
+ return rc;
+}
+
+
+RTR3DECL(int) RTTlsSet(RTTLS iTls, void *pvValue)
+{
+ if (__libc_TLSSet(iTls, pvValue) != -1)
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTR3DECL(int) RTThreadGetExecutionTimeMilli(uint64_t *pKernelTime, uint64_t *pUserTime)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
diff --git a/src/VBox/Runtime/r3/os2/time-os2.cpp b/src/VBox/Runtime/r3/os2/time-os2.cpp
new file mode 100644
index 00000000..e06b9fcd
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/time-os2.cpp
@@ -0,0 +1,87 @@
+/* $Id: time-os2.cpp $ */
+/** @file
+ * IPRT - Time, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * 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, in version 3 of the
+ * License.
+ *
+ * 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ * --------------------------------------------------------------------
+ *
+ * This code is based on:
+ *
+ * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#include <InnoTekLIBC/FastInfoBlocks.h>
+
+#include <iprt/time.h>
+#include "internal/time.h"
+
+/** @todo mscount will roll over after ~48 days. */
+
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return fibGetMsCount() * UINT64_C(10000000);
+}
+
+
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return fibGetMsCount();
+}
+