| /**************************************************************************** |
| * |
| * SciTech OS Portability Manager Library |
| * |
| * ======================================================================== |
| * |
| * The contents of this file are subject to the SciTech MGL Public |
| * License Version 1.0 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of |
| * the License at http://www.scitechsoft.com/mgl-license.txt |
| * |
| * Software distributed under the License is distributed on an |
| * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or |
| * implied. See the License for the specific language governing |
| * rights and limitations under the License. |
| * |
| * The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc. |
| * |
| * The Initial Developer of the Original Code is SciTech Software, Inc. |
| * All Rights Reserved. |
| * |
| * ======================================================================== |
| * |
| * Language: ANSI C |
| * Environment: 32-bit OS/2 |
| * |
| * Description: Implementation for the OS Portability Manager Library, which |
| * contains functions to implement OS specific services in a |
| * generic, cross platform API. Porting the OS Portability |
| * Manager library is the first step to porting any SciTech |
| * products to a new platform. |
| * |
| ****************************************************************************/ |
| |
| #include "pmapi.h" |
| #include "drvlib/os/os.h" |
| #include "pm_help.h" |
| #include "mtrr.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <process.h> |
| #ifndef __EMX__ |
| #include <direct.h> |
| #endif |
| #define INCL_DOSERRORS |
| #define INCL_DOS |
| #define INCL_SUB |
| #define INCL_VIO |
| #define INCL_KBD |
| #include <os2.h> |
| |
| /* Semaphore for communication with our background daemon */ |
| #define SHAREDSEM ((PSZ)"\\SEM32\\SDD\\DAEMON") |
| #define DAEMON_NAME "SDDDAEMN.EXE" |
| |
| /*--------------------------- Global variables ----------------------------*/ |
| |
| /* Public structures used to communicate with VIDEOPMI for implementing |
| * the ability to call the real mode BIOS functions. |
| */ |
| |
| typedef struct _VIDEOMODEINFO { |
| ULONG miModeId; |
| USHORT usType; |
| USHORT usInt10ModeSet; |
| USHORT usXResolution; |
| USHORT usYResolution; |
| ULONG ulBufferAddress; |
| ULONG ulApertureSize; |
| BYTE bBitsPerPixel; |
| BYTE bBitPlanes; |
| BYTE bXCharSize; |
| BYTE bYCharSize; |
| USHORT usBytesPerScanLine; |
| USHORT usTextRows; |
| ULONG ulPageLength; |
| ULONG ulSaveSize; |
| BYTE bVrtRefresh; |
| BYTE bHrtRefresh; |
| BYTE bVrtPolPos; |
| BYTE bHrtPolPos; |
| CHAR bRedMaskSize; |
| CHAR bRedFieldPosition; |
| CHAR bGreenMaskSize; |
| CHAR bGreenFieldPosition; |
| CHAR bBlueMaskSize; |
| CHAR bBlueFieldPosition; |
| CHAR bRsvdMaskSize; |
| CHAR bRsvdFieldPosition; |
| ULONG ulColors; |
| ULONG ulReserved[3]; |
| } VIDEOMODEINFO, FAR *PVIDEOMODEINFO; |
| |
| typedef struct _ADAPTERINFO { |
| ULONG ulAdapterID; |
| CHAR szOEMString[128]; |
| CHAR szDACString[128]; |
| CHAR szRevision[128]; |
| ULONG ulTotalMemory; |
| ULONG ulMMIOBaseAddress; |
| ULONG ulPIOBaseAddress; |
| BYTE bBusType; |
| BYTE bEndian; |
| USHORT usDeviceBusID; |
| USHORT usVendorBusID; |
| USHORT SlotID; |
| } ADAPTERINFO, FAR *PADAPTERINFO; |
| |
| typedef struct _VIDEO_ADAPTER { |
| void *hvideo; |
| ADAPTERINFO Adapter; |
| VIDEOMODEINFO ModeInfo; |
| } VIDEO_ADAPTER, FAR *PVIDEO_ADAPTER; |
| |
| /* PMIREQUEST_SOFTWAREINT structures from OS/2 DDK */ |
| |
| typedef struct { |
| ULONG ulFlags; /* VDM initialization type */ |
| #define VDM_POSTLOAD 0x1 /* adapter just loaded, used internally for initialization */ |
| #define VDM_INITIALIZE 0x2 /* force initialization of a permanently open VDM, even if previously initialized */ |
| #define VDM_TERMINATE_POSTINITIALIZE 0x6 /*start VDM with initialization, but close it afterwards (includes VDM_INITIALIZE) */ |
| #define VDM_QUERY_CAPABILITY 0x10 /* query the current int 10 capability */ |
| #define VDM_FULL_VDM_CREATED 0x20 /* a full VDM is created */ |
| #define VDM_MINI_VDM_CREATED 0x40 /* a mini VDM is created */ |
| #define VDM_MINI_VDM_SUPPORTED 0x80 /* mini VDM support is available */ |
| PCHAR szName; /* VDM initialization program */ |
| PCHAR szArgs; /* VDM initialization arguments */ |
| }INITVDM; |
| |
| typedef struct { |
| BYTE bBufferType; |
| #define BUFFER_NONE 0 |
| #define INPUT_BUFFER 1 |
| #define OUTPUT_BUFFER 2 |
| BYTE bReserved; |
| BYTE bSelCRF; |
| BYTE bOffCRF; |
| PVOID pAddress; |
| ULONG ulSize; |
| } BUFFER, *PBUFFER; |
| |
| typedef struct vcrf_s { |
| ULONG reg_eax; |
| ULONG reg_ebx; |
| ULONG reg_ecx; |
| ULONG reg_edx; |
| ULONG reg_ebp; |
| ULONG reg_esi; |
| ULONG reg_edi; |
| ULONG reg_ds; |
| ULONG reg_es; |
| ULONG reg_fs; |
| ULONG reg_gs; |
| ULONG reg_cs; |
| ULONG reg_eip; |
| ULONG reg_eflag; |
| ULONG reg_ss; |
| ULONG reg_esp; |
| } VCRF; |
| |
| typedef struct { |
| ULONG ulBIOSIntNo; |
| VCRF aCRF; |
| BUFFER pB[2]; |
| } INTCRF; |
| |
| #define PMIREQUEST_LOADPMIFILE 21 |
| #define PMIREQUEST_IDENTIFYADAPTER 22 |
| #define PMIREQUEST_SOFTWAREINT 23 |
| |
| #ifdef PTR_DECL_IN_FRONT |
| #define EXPENTRYP * EXPENTRY |
| #else |
| #define EXPENTRYP EXPENTRY * |
| #endif |
| |
| /* Entry point to VIDEOPMI32Request. This may be overridden by external |
| * code that has already loaded VIDEOPMI to avoid loading it twice. |
| */ |
| |
| APIRET (EXPENTRYP PM_VIDEOPMI32Request)(PVIDEO_ADAPTER, ULONG, PVOID, PVOID) = NULL; |
| static ibool haveInt10 = -1; /* True if we have Int 10 support */ |
| static ibool useVPMI = true; /* False if VIDEOPMI unavailable */ |
| static VIDEO_ADAPTER Adapter; /* Video adapter for VIDEOPMI */ |
| static uchar RMBuf[1024]; /* Fake real mode transfer buffer */ |
| static uint VESABuf_len = 1024;/* Length of the VESABuf buffer */ |
| static void *VESABuf_ptr = NULL;/* Near pointer to VESABuf */ |
| static uint VESABuf_rseg; /* Real mode segment of VESABuf */ |
| static uint VESABuf_roff; /* Real mode offset of VESABuf */ |
| static uchar * lowMem = NULL; |
| static ibool isSessionSwitching = false; |
| static ulong parmsIn[4]; /* Must not cross 64Kb boundary! */ |
| static ulong parmsOut[4]; /* Must not cross 64Kb boundary! */ |
| extern ushort _PM_gdt; |
| static void (PMAPIP fatalErrorCleanup)(void) = NULL; |
| |
| /* DosSysCtl prototype. It is not declared in the headers but it is in the |
| * standard import libraries (DOSCALLS.876). Funny. |
| */ |
| APIRET APIENTRY DosSysCtl(ULONG ulFunction, PVOID pvData); |
| |
| /* This is the stack size for the threads that track the session switch event */ |
| #define SESSION_SWITCH_STACK_SIZE 32768 |
| |
| typedef struct { |
| VIOMODEINFO vmi; |
| USHORT CursorX; |
| USHORT CursorY; |
| UCHAR FrameBuffer[1]; |
| } CONSOLE_SAVE; |
| |
| typedef struct _SESWITCHREC { |
| /* The following variable is volatile because of PM_SUSPEND_APP */ |
| volatile int Flags; /* -1 or PM_DEACTIVATE or PM_REACTIVATE */ |
| PM_saveState_cb Callback; /* Save/restore context callback */ |
| HMTX Mutex; /* Exclusive access mutex */ |
| HEV Event; /* Posted after callback is called */ |
| } SESWITCHREC; |
| |
| /* Page sized block cache */ |
| |
| #define PAGES_PER_BLOCK 32 |
| #define PAGE_BLOCK_SIZE (PAGES_PER_BLOCK * PM_PAGE_SIZE + (PM_PAGE_SIZE-1) + sizeof(pageblock)) |
| #define FREELIST_NEXT(p) (*(void**)(p)) |
| typedef struct pageblock { |
| struct pageblock *next; |
| struct pageblock *prev; |
| void *freeListStart; |
| void *freeList; |
| void *freeListEnd; |
| int freeCount; |
| PM_lockHandle lockHandle; |
| } pageblock; |
| |
| static pageblock *pageBlocks = NULL; |
| |
| /*----------------------------- Implementation ----------------------------*/ |
| |
| /**************************************************************************** |
| PARAMETERS: |
| func - Helper device driver function to call |
| |
| RETURNS: |
| First return value from the device driver in parmsOut[0] |
| |
| REMARKS: |
| Function to open our helper device driver, call it and close the file |
| handle. Note that we have to open the device driver for every call because |
| of two problems: |
| |
| 1. We cannot open a single file handle in a DLL that is shared amongst |
| programs, since every process must have it's own open file handle. |
| |
| 2. For some reason there appears to be a limit of about 12 open file |
| handles on a device driver in the system. Hence when we open more |
| than about 12 file handles things start to go very strange. |
| |
| Hence we simply open the file handle every time that we need to call the |
| device driver to work around these problems. |
| ****************************************************************************/ |
| static ulong CallSDDHelp( |
| int func) |
| { |
| static ulong inLen; /* Must not cross 64Kb boundary! */ |
| static ulong outLen; /* Must not cross 64Kb boundary! */ |
| HFILE hSDDHelp; |
| ULONG rc; |
| ulong result; |
| |
| if ((rc = DosOpen(PMHELP_NAME,&hSDDHelp,&result,0,0, |
| FILE_OPEN, OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE, |
| NULL)) != 0) { |
| if (rc == 4) { /* Did we run out of file handles? */ |
| ULONG ulNewFHs; |
| LONG lAddFHs = 5; |
| |
| if (DosSetRelMaxFH(&lAddFHs, &ulNewFHs) != 0) |
| PM_fatalError("Failed to raise the file handles limit!"); |
| else { |
| if ((rc = DosOpen(PMHELP_NAME,&hSDDHelp,&result,0,0, |
| FILE_OPEN, OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE, |
| NULL)) != 0) { |
| PM_fatalError("Unable to open SDDHELP$ helper device driver! (#2)"); |
| } |
| } |
| } |
| else |
| PM_fatalError("Unable to open SDDHELP$ helper device driver!"); |
| } |
| if (DosDevIOCtl(hSDDHelp,PMHELP_IOCTL,func, |
| &parmsIn, inLen = sizeof(parmsIn), &inLen, |
| &parmsOut, outLen = sizeof(parmsOut), &outLen) != 0) |
| PM_fatalError("Failure calling SDDHELP$ helper device driver!"); |
| DosClose(hSDDHelp); |
| return parmsOut[0]; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Determine if we're running on a DBCS system. |
| ****************************************************************************/ |
| ibool __IsDBCSSystem(void) |
| { |
| CHAR achDBCSInfo[12]; |
| COUNTRYCODE ccStruct = {0, 0}; |
| |
| memset(achDBCSInfo, 0, 12); |
| |
| /* Get the DBCS vector - if it's not empty, we're on DBCS */ |
| DosQueryDBCSEnv(sizeof(achDBCSInfo), &ccStruct, achDBCSInfo); |
| if (achDBCSInfo[0] != 0) |
| return true; |
| else |
| return false; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Determine if PMSHELL is running - if it isn't, we can't use certain calls |
| ****************************************************************************/ |
| ibool __isShellLoaded(void) |
| { |
| PVOID ptr; |
| |
| if (DosGetNamedSharedMem(&ptr, (PSZ)"\\SHAREMEM\\PMGLOBAL.MEM", PAG_READ) == NO_ERROR) { |
| DosFreeMem(ptr); |
| return true; |
| } |
| return false; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Initialise the PM library and connect to our helper device driver. If we |
| cannot connect to our helper device driver, we bail out with an error |
| message. |
| ****************************************************************************/ |
| void PMAPI PM_init(void) |
| { |
| if (!lowMem) { |
| /* Obtain the 32->16 callgate from the device driver to enable IOPL */ |
| if ((_PM_gdt = CallSDDHelp(PMHELP_GETGDT32)) == 0) |
| PM_fatalError("Unable to obtain call gate selector!"); |
| |
| PM_setIOPL(3); |
| |
| /* Map the first Mb of physical memory into lowMem */ |
| if ((lowMem = PM_mapPhysicalAddr(0,0xFFFFF,true)) == NULL) |
| PM_fatalError("Unable to map first Mb physical memory!"); |
| |
| /* Initialise the MTRR interface functions */ |
| MTRR_init(); |
| } |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Initialise the PM library for BIOS access via VIDEOPMI. This should work |
| with any GRADD driver, including SDD/2. |
| ****************************************************************************/ |
| static ibool InitInt10(void) |
| { |
| HMODULE hModGENPMI,hModSDDPMI,hModVideoPMI; |
| CHAR buf[80],path[_MAX_PATH]; |
| HEV hevDaemon = NULLHANDLE; |
| RESULTCODES resCodes; |
| |
| if (haveInt10 == -1) { |
| /* Connect to VIDEOPMI and get entry point. Note that we only |
| * do this if GENPMI or SDDPMI are already loaded, since we need |
| * a GRADD based driver for this to work. |
| */ |
| PM_init(); |
| haveInt10 = false; |
| if (DosQueryModuleHandle((PSZ)"GENPMI.DLL",&hModGENPMI) != 0) |
| hModGENPMI = NULLHANDLE; |
| if (DosQueryModuleHandle((PSZ)"SDDPMI.DLL",&hModSDDPMI) != 0) |
| hModSDDPMI = NULLHANDLE; |
| if (hModGENPMI || hModSDDPMI) { |
| if (DosLoadModule((PSZ)buf,sizeof(buf),(PSZ)"VIDEOPMI.DLL",&hModVideoPMI) == 0) { |
| if (DosQueryProcAddr(hModVideoPMI,0,(PSZ)"VIDEOPMI32Request",(void*)&PM_VIDEOPMI32Request) != 0) |
| PM_fatalError("Unable to get VIDEOPMI32Request entry point!"); |
| strcpy(path,"X:\\OS2\\SVGADATA.PMI"); |
| path[0] = PM_getBootDrive(); |
| if (PM_VIDEOPMI32Request(&Adapter,PMIREQUEST_LOADPMIFILE,path,NULL) != 0) { |
| DosFreeModule(hModVideoPMI); |
| PM_VIDEOPMI32Request = NULL; |
| haveInt10 = false; |
| } |
| else { |
| /* Attempt to initialise the full VDM in the system. This will only |
| * work if VPRPMI.SYS is loaded, but it provides support for passing |
| * values in ES/DS/ESI/EDI between the BIOS which does not work with |
| * kernel VDM's in fixpacks earlier than FP15. FP15 and later and |
| * the new Warp 4.51 and Warp Server convenience packs should work |
| * fine with the kernel mini-VDM. |
| * |
| * Also the full VDM is the only solution for really old kernels |
| * (but GRADD won't run on them so this is superfluous ;-). |
| */ |
| INITVDM InitVDM = {VDM_INITIALIZE,NULL,NULL}; |
| PM_VIDEOPMI32Request(&Adapter,PMIREQUEST_SOFTWAREINT,&InitVDM,NULL); |
| haveInt10 = true; |
| } |
| } |
| } |
| else { |
| /* A GRADD driver isn't loaded, hence we can't use VIDEOPMI. But we will try |
| * to access the mini-VDM directly, first verifying that the support is |
| * available in the kernel (it should be for kernels that support GRADD). |
| * This may be needed in a command line boot or if non-GRADD driver is |
| * used (Matrox or classic VGA). |
| * Note: because of problems with mini-VDM support in the kernel, we have to |
| * spawn a daemon process that will do the actual mini-VDM access for us. |
| */ |
| /* Try to open shared semaphore to see if our daemon is already up */ |
| if (DosOpenEventSem(SHAREDSEM, &hevDaemon) == NO_ERROR) { |
| if (DosWaitEventSem(hevDaemon, 1) == NO_ERROR) { |
| /* If semaphore is posted, all is well */ |
| useVPMI = false; |
| haveInt10 = true; |
| } |
| } |
| else { |
| /* Create shared event semaphore */ |
| if (DosCreateEventSem(SHAREDSEM, &hevDaemon, DC_SEM_SHARED, FALSE) == NO_ERROR) { |
| PM_findBPD(DAEMON_NAME, path); |
| strcat(path, DAEMON_NAME); |
| if (DosExecPgm(buf, sizeof(buf), EXEC_BACKGROUND, (PSZ)DAEMON_NAME, |
| NULL, &resCodes, (PSZ)path) == NO_ERROR) { |
| /* The daemon was successfully spawned, now give it a sec to come up */ |
| if (DosWaitEventSem(hevDaemon, 2000) == NO_ERROR) { |
| /* It's up! */ |
| useVPMI = false; |
| haveInt10 = true; |
| } |
| } |
| } |
| } |
| } |
| } |
| return haveInt10; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| We "probably" have BIOS access under OS/2 but we have to verify/initialize it |
| first. |
| ****************************************************************************/ |
| ibool PMAPI PM_haveBIOSAccess(void) |
| { |
| return InitInt10(); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Return the operating system type identifier. |
| ****************************************************************************/ |
| long PMAPI PM_getOSType(void) |
| { |
| return _OS_OS2; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Return the runtime type identifier. |
| ****************************************************************************/ |
| int PMAPI PM_getModeType(void) |
| { |
| return PM_386; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Add a file directory separator to the end of the filename. |
| ****************************************************************************/ |
| void PMAPI PM_backslash( |
| char *s) |
| { |
| uint pos = strlen(s); |
| if (s[pos-1] != '\\') { |
| s[pos] = '\\'; |
| s[pos+1] = '\0'; |
| } |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Add a user defined PM_fatalError cleanup function. |
| ****************************************************************************/ |
| void PMAPI PM_setFatalErrorCleanup( |
| void (PMAPIP cleanup)(void)) |
| { |
| fatalErrorCleanup = cleanup; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Report a fatal error condition and halt the program. |
| ****************************************************************************/ |
| void PMAPI PM_fatalError( |
| const char *msg) |
| { |
| /* Be prepare to be called recursively (failed to fail situation :-) */ |
| static int fatalErrorCount = 0; |
| if (fatalErrorCount++ == 0) { |
| if (fatalErrorCleanup) |
| fatalErrorCleanup(); |
| } |
| fprintf(stderr,"%s\n", msg); |
| exit(1); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Allocate the real mode VESA transfer buffer for communicating with the BIOS. |
| ****************************************************************************/ |
| void * PMAPI PM_getVESABuf( |
| uint *len, |
| uint *rseg, |
| uint *roff) |
| { |
| if (!VESABuf_ptr) { |
| /* Allocate a global buffer for communicating with the VESA VBE */ |
| if ((VESABuf_ptr = PM_allocRealSeg(VESABuf_len, &VESABuf_rseg, &VESABuf_roff)) == NULL) |
| return NULL; |
| } |
| *len = VESABuf_len; |
| *rseg = VESABuf_rseg; |
| *roff = VESABuf_roff; |
| return VESABuf_ptr; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Check if a key has been pressed. |
| ****************************************************************************/ |
| int PMAPI PM_kbhit(void) |
| { |
| KBDKEYINFO key; /* Must not cross a 64K boundary */ |
| |
| KbdPeek(&key, 0); |
| return (key.fbStatus & KBDTRF_FINAL_CHAR_IN); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Wait for and return the next keypress. |
| ****************************************************************************/ |
| int PMAPI PM_getch(void) |
| { |
| KBDKEYINFO key; /* Must not cross a 64K boundary */ |
| |
| KbdCharIn(&key,IO_WAIT,0); |
| return key.chChar; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Open a fullscreen console for output to the screen. This requires that |
| the application be a fullscreen VIO program. |
| ****************************************************************************/ |
| PM_HWND PMAPI PM_openConsole( |
| PM_HWND hwndUser, |
| int device, |
| int xRes, |
| int yRes, |
| int bpp, |
| ibool fullScreen) |
| { |
| (void)hwndUser; |
| (void)device; |
| (void)xRes; |
| (void)yRes; |
| (void)bpp; |
| (void)fullScreen; |
| return 0; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Find the size of the console state buffer. |
| ****************************************************************************/ |
| int PMAPI PM_getConsoleStateSize(void) |
| { |
| VIOMODEINFO vmi; |
| vmi.cb = sizeof (VIOMODEINFO); |
| VioGetMode (&vmi, (HVIO)0); |
| return sizeof (CONSOLE_SAVE) - 1 + vmi.col * vmi.row * 2; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Save the state of the console. |
| ****************************************************************************/ |
| void PMAPI PM_saveConsoleState( |
| void *stateBuf, |
| PM_HWND hwndConsole) |
| { |
| USHORT fblen; |
| CONSOLE_SAVE *cs = (CONSOLE_SAVE*)stateBuf; |
| VIOMODEINFO vmi; |
| |
| /* The reason for the VIOMODEINFO juggling is 16-bit code. Because the user |
| * allocates the state buffer, cd->vmi might be crossing the 64K boundary and |
| * the 16-bit API would fail. If we create another copy on stack, the compiler |
| * should ensure that the 64K boundary will not be crossed (it adjusts the stack |
| * if it should cross). |
| */ |
| vmi.cb = sizeof(VIOMODEINFO); |
| VioGetMode(&vmi,(HVIO)0); |
| memcpy(&cs->vmi, &vmi, sizeof(VIOMODEINFO)); |
| VioGetCurPos(&cs->CursorY, &cs->CursorX, (HVIO)0); |
| fblen = cs->vmi.col * cs->vmi.row * 2; |
| VioReadCellStr((PCH)cs->FrameBuffer, &fblen, 0, 0, (HVIO)0); |
| } |
| |
| /* Global variable to communicate between threads */ |
| static SESWITCHREC SesSwitchRec = { -1 }; |
| |
| /**************************************************************************** |
| REMARKS: |
| Called by external routines at least once per frame to check whenever a |
| session save/restore should be performed. Since we receive such notifications |
| asyncronously, we can't perform all required operations at that time. |
| ****************************************************************************/ |
| void __PM_checkConsoleSwitch(void) |
| { |
| int Flags, Mode; |
| PM_saveState_cb Callback; |
| |
| /* Quick optimized path for most common case */ |
| if (SesSwitchRec.Flags == -1) |
| return; |
| |
| again: |
| if (DosRequestMutexSem(SesSwitchRec.Mutex, 100)) |
| return; |
| Flags = SesSwitchRec.Flags; |
| Callback = SesSwitchRec.Callback; |
| SesSwitchRec.Flags = -1; |
| DosReleaseMutexSem(SesSwitchRec.Mutex); |
| |
| isSessionSwitching = true; /* Prevent VIO calls */ |
| Mode = Callback(Flags); |
| isSessionSwitching = false; |
| DosPostEventSem(SesSwitchRec.Event); |
| if (Flags == PM_DEACTIVATE && Mode == PM_SUSPEND_APP) |
| /* Suspend application until we switch back to our application */ |
| for (;;) { |
| DosSleep (500); |
| /* SesSwitchRec.Flags is volatile so optimizer |
| * won't load it into a register |
| */ |
| if (SesSwitchRec.Flags != -1) |
| goto again; |
| } |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Waits until main thread processes the session switch event. |
| ****************************************************************************/ |
| static void _PM_SessionSwitchEvent( |
| PM_saveState_cb saveState, |
| int flags) |
| { |
| ULONG Count; |
| |
| if (DosRequestMutexSem(SesSwitchRec.Mutex, 10000)) |
| return; |
| |
| /* We're going to wait on that semaphore */ |
| DosResetEventSem(SesSwitchRec.Event, &Count); |
| SesSwitchRec.Callback = saveState; |
| SesSwitchRec.Flags = flags; |
| DosReleaseMutexSem(SesSwitchRec.Mutex); |
| |
| /* Now wait until all required operations are complete */ |
| DosWaitEventSem (SesSwitchRec.Event, 10000); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| This is the thread responsible for tracking switches back to our |
| fullscreen session. |
| ****************************************************************************/ |
| static void _PM_ConsoleSwitch( |
| PM_saveState_cb saveState) |
| { |
| USHORT NotifyType; |
| |
| for (;;) { |
| if (VioModeWait(VMWR_POPUP, &NotifyType, 0) != 0) |
| break; |
| _PM_SessionSwitchEvent(saveState, PM_REACTIVATE); |
| } |
| VioModeUndo(UNDOI_RELEASEOWNER, UNDOK_ERRORCODE, (HVIO)0); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| This is the thread responsible for tracking screen popups (usually fatal |
| error handler uses them). |
| ****************************************************************************/ |
| static void _PM_ConsolePopup( |
| PM_saveState_cb saveState) |
| { |
| USHORT NotifyType; |
| for (;;) { |
| if (VioSavRedrawWait(VSRWI_SAVEANDREDRAW, &NotifyType, 0) != 0) |
| break; |
| if (NotifyType == VSRWN_SAVE) |
| _PM_SessionSwitchEvent(saveState, PM_DEACTIVATE); |
| else if (NotifyType == VSRWN_REDRAW) |
| _PM_SessionSwitchEvent(saveState, PM_REACTIVATE); |
| } |
| VioSavRedrawUndo(UNDOI_RELEASEOWNER, UNDOK_ERRORCODE, (HVIO)0); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Set the suspend application callback for the fullscreen console. |
| ****************************************************************************/ |
| void PMAPI PM_setSuspendAppCallback( |
| PM_saveState_cb saveState) |
| { |
| /* If PM isn't loaded, this stuff will cause crashes! */ |
| if (__isShellLoaded()) { |
| if (saveState) { |
| /* Create the threads responsible for tracking console switches */ |
| SesSwitchRec.Flags = -1; |
| DosCreateMutexSem(NULL, &SesSwitchRec.Mutex, 0, FALSE); |
| DosCreateEventSem(NULL, &SesSwitchRec.Event, 0, FALSE); |
| _beginthread ((void(*)(void*))_PM_ConsoleSwitch,NULL,SESSION_SWITCH_STACK_SIZE, (void*)saveState); |
| _beginthread ((void(*)(void*))_PM_ConsolePopup,NULL,SESSION_SWITCH_STACK_SIZE, (void*)saveState); |
| } |
| else { |
| /* Kill the threads responsible for tracking console switches */ |
| VioModeUndo(UNDOI_RELEASEOWNER, UNDOK_TERMINATE, (HVIO)0); |
| VioSavRedrawUndo(UNDOI_RELEASEOWNER, UNDOK_TERMINATE, (HVIO)0); |
| DosCloseEventSem(SesSwitchRec.Event); |
| DosCloseMutexSem(SesSwitchRec.Mutex); |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Restore the console state. |
| ****************************************************************************/ |
| void PMAPI PM_restoreConsoleState( |
| const void *stateBuf, |
| PM_HWND hwndConsole) |
| { |
| CONSOLE_SAVE *cs = (CONSOLE_SAVE *)stateBuf; |
| VIOMODEINFO vmi; |
| |
| if (!cs) |
| return; |
| |
| memcpy(&vmi, &cs->vmi, sizeof (VIOMODEINFO)); |
| VioSetMode(&vmi, (HVIO)0); |
| VioSetCurPos(cs->CursorY, cs->CursorX, (HVIO)0); |
| VioWrtCellStr((PCH)cs->FrameBuffer, cs->vmi.col * cs->vmi.row * 2,0, 0, (HVIO)0); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Close the fullscreen console. |
| ****************************************************************************/ |
| void PMAPI PM_closeConsole( |
| PM_HWND hwndConsole) |
| { |
| /* Kill the threads responsible for tracking console switches */ |
| PM_setSuspendAppCallback(NULL); |
| (void)hwndConsole; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Set the location of the OS console cursor. |
| ****************************************************************************/ |
| void PM_setOSCursorLocation( |
| int x, |
| int y) |
| { |
| /* If session switch is in progress, calling into VIO causes deadlocks! */ |
| /* Also this call to VIO screws up our console library on DBCS boxes... */ |
| if (!isSessionSwitching && !__IsDBCSSystem()) |
| VioSetCurPos(y,x,0); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Set the width of the OS console. |
| ****************************************************************************/ |
| void PM_setOSScreenWidth( |
| int width, |
| int height) |
| { |
| /* Nothing to do in here */ |
| (void)width; |
| (void)height; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Set the real time clock handler (used for software stereo modes). |
| ****************************************************************************/ |
| ibool PMAPI PM_setRealTimeClockHandler( |
| PM_intHandler ih, |
| int frequency) |
| { |
| /* TODO: Implement this! */ |
| (void)ih; |
| (void)frequency; |
| return false; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Set the real time clock frequency (for stereo modes). |
| ****************************************************************************/ |
| void PMAPI PM_setRealTimeClockFrequency( |
| int frequency) |
| { |
| /* TODO: Implement this! */ |
| (void)frequency; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Restore the original real time clock handler. |
| ****************************************************************************/ |
| void PMAPI PM_restoreRealTimeClockHandler(void) |
| { |
| /* TODO: Implement this! */ |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Return the current operating system path or working directory. |
| ****************************************************************************/ |
| char * PMAPI PM_getCurrentPath( |
| char *path, |
| int maxLen) |
| { |
| return getcwd(path,maxLen); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Return the drive letter for the boot drive. |
| ****************************************************************************/ |
| char PMAPI PM_getBootDrive(void) |
| { |
| ulong boot = 3; |
| DosQuerySysInfo(QSV_BOOT_DRIVE,QSV_BOOT_DRIVE,&boot,sizeof(boot)); |
| return (char)('a' + boot - 1); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Return the path to the VBE/AF driver files. |
| ****************************************************************************/ |
| const char * PMAPI PM_getVBEAFPath(void) |
| { |
| static char path[CCHMAXPATH]; |
| strcpy(path,"x:\\"); |
| path[0] = PM_getBootDrive(); |
| return path; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Return the path to the Nucleus driver files. |
| ****************************************************************************/ |
| const char * PMAPI PM_getNucleusPath(void) |
| { |
| static char path[CCHMAXPATH]; |
| if (getenv("NUCLEUS_PATH") != NULL) |
| return getenv("NUCLEUS_PATH"); |
| strcpy(path,"x:\\os2\\drivers"); |
| path[0] = PM_getBootDrive(); |
| PM_backslash(path); |
| strcat(path,"nucleus"); |
| return path; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Return the path to the Nucleus configuration files. |
| ****************************************************************************/ |
| const char * PMAPI PM_getNucleusConfigPath(void) |
| { |
| static char path[CCHMAXPATH]; |
| strcpy(path,PM_getNucleusPath()); |
| PM_backslash(path); |
| strcat(path,"config"); |
| return path; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Return a unique identifier for the machine if possible. |
| ****************************************************************************/ |
| const char * PMAPI PM_getUniqueID(void) |
| { |
| return PM_getMachineName(); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Get the name of the machine on the network. |
| ****************************************************************************/ |
| const char * PMAPI PM_getMachineName(void) |
| { |
| static char name[40],*env; |
| |
| if ((env = getenv("HOSTNAME")) != NULL) { |
| strncpy(name,env,sizeof(name)); |
| name[sizeof(name)-1] = 0; |
| return name; |
| } |
| return "OS2"; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Return a pointer to the real mode BIOS data area. |
| ****************************************************************************/ |
| void * PMAPI PM_getBIOSPointer(void) |
| { |
| PM_init(); |
| return lowMem + 0x400; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Return a pointer to 0xA0000 physical VGA graphics framebuffer. |
| ****************************************************************************/ |
| void * PMAPI PM_getA0000Pointer(void) |
| { |
| PM_init(); |
| return lowMem + 0xA0000; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Map a physical address to a linear address in the callers process. |
| ****************************************************************************/ |
| void * PMAPI PM_mapPhysicalAddr( |
| ulong base, |
| ulong limit, |
| ibool isCached) |
| { |
| ulong baseAddr,baseOfs,linear; |
| |
| /* Round the physical address to a 4Kb boundary and the limit to a |
| * 4Kb-1 boundary before passing the values to mmap. If we round the |
| * physical address, then we also add an extra offset into the address |
| * that we return. |
| */ |
| baseOfs = base & 4095; |
| baseAddr = base & ~4095; |
| limit = ((limit+baseOfs+1+4095) & ~4095)-1; |
| parmsIn[0] = baseAddr; |
| parmsIn[1] = limit; |
| parmsIn[2] = isCached; |
| if ((linear = CallSDDHelp(PMHELP_MAPPHYS)) == 0) |
| return NULL; |
| return (void*)(linear + baseOfs); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Free a physical address mapping allocated by PM_mapPhysicalAddr. |
| ****************************************************************************/ |
| void PMAPI PM_freePhysicalAddr( |
| void *ptr, |
| ulong limit) |
| { |
| parmsIn[0] = (ulong)ptr; |
| parmsIn[1] = limit; |
| CallSDDHelp(PMHELP_FREEPHYS); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Find the physical address of a linear memory address in current process. |
| ****************************************************************************/ |
| ulong PMAPI PM_getPhysicalAddr( |
| void *p) |
| { |
| parmsIn[0] = (ulong)p; |
| return CallSDDHelp(PMHELP_GETPHYSICALADDR); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Find the physical address of a linear memory address in current process. |
| ****************************************************************************/ |
| ibool PMAPI PM_getPhysicalAddrRange( |
| void *p, |
| ulong length, |
| ulong *physAddress) |
| { |
| parmsIn[0] = (ulong)p; |
| parmsIn[1] = (ulong)length; |
| parmsIn[2] = (ulong)physAddress; |
| return CallSDDHelp(PMHELP_GETPHYSICALADDRRANGE); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Sleep for the specified number of milliseconds. |
| ****************************************************************************/ |
| void PMAPI PM_sleep( |
| ulong milliseconds) |
| { |
| DosSleep(milliseconds); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Return the base I/O port for the specified COM port. |
| ****************************************************************************/ |
| int PMAPI PM_getCOMPort( |
| int port) |
| { |
| switch (port) { |
| case 0: return 0x3F8; |
| case 1: return 0x2F8; |
| } |
| return 0; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Return the base I/O port for the specified LPT port. |
| ****************************************************************************/ |
| int PMAPI PM_getLPTPort( |
| int port) |
| { |
| switch (port) { |
| case 0: return 0x3BC; |
| case 1: return 0x378; |
| case 2: return 0x278; |
| } |
| return 0; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Allocate a block of shared memory. For Win9x we allocate shared memory |
| as locked, global memory that is accessible from any memory context |
| (including interrupt time context), which allows us to load our important |
| data structure and code such that we can access it directly from a ring |
| 0 interrupt context. |
| ****************************************************************************/ |
| void * PMAPI PM_mallocShared( |
| long size) |
| { |
| parmsIn[0] = size; |
| return (void*)CallSDDHelp(PMHELP_MALLOCSHARED); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Free a block of shared memory. |
| ****************************************************************************/ |
| void PMAPI PM_freeShared( |
| void *ptr) |
| { |
| parmsIn[0] = (ulong)ptr; |
| CallSDDHelp(PMHELP_FREESHARED); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Map a linear memory address to the calling process address space. The |
| address will have been allocated in another process using the |
| PM_mapPhysicalAddr function. |
| ****************************************************************************/ |
| void * PMAPI PM_mapToProcess( |
| void *base, |
| ulong limit) |
| { |
| ulong baseAddr,baseOfs; |
| |
| /* Round the physical address to a 4Kb boundary and the limit to a |
| * 4Kb-1 boundary before passing the values to mmap. If we round the |
| * physical address, then we also add an extra offset into the address |
| * that we return. |
| */ |
| baseOfs = (ulong)base & 4095; |
| baseAddr = (ulong)base & ~4095; |
| limit = ((limit+baseOfs+1+4095) & ~4095)-1; |
| parmsIn[0] = (ulong)baseAddr; |
| parmsIn[1] = limit; |
| return (void*)(CallSDDHelp(PMHELP_MAPTOPROCESS)+baseOfs); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Map a real mode pointer to a protected mode pointer. |
| ****************************************************************************/ |
| void * PMAPI PM_mapRealPointer( |
| uint r_seg, |
| uint r_off) |
| { |
| if (r_seg == 0xFFFF) |
| return &RMBuf[r_off]; |
| return lowMem + MK_PHYS(r_seg,r_off); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Allocate a block of real mode memory |
| ****************************************************************************/ |
| void * PMAPI PM_allocRealSeg( |
| uint size, |
| uint *r_seg, |
| uint *r_off) |
| { |
| if (size > sizeof(RMBuf)) |
| return NULL; |
| *r_seg = 0xFFFF; |
| *r_off = 0x0000; |
| return &RMBuf; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Free a block of real mode memory. |
| ****************************************************************************/ |
| void PMAPI PM_freeRealSeg( |
| void *mem) |
| { |
| /* Nothing to do in here */ |
| (void)mem; |
| } |
| |
| #define INDPMI(reg) rmregs.aCRF.reg_##reg = regs->reg |
| #define OUTDPMI(reg) regs->reg = rmregs.aCRF.reg_##reg |
| |
| #define REG_OFFSET(field) (((ULONG)&(((VCRF*)0)->field)) / sizeof(ULONG)) |
| |
| /**************************************************************************** |
| REMARKS: |
| Issue a real mode interrupt (parameters in DPMI compatible structure) |
| ****************************************************************************/ |
| void PMAPI DPMI_int86( |
| int intno, |
| DPMI_regs *regs) |
| { |
| INTCRF rmregs; |
| ulong eax = 0; |
| |
| if (!InitInt10()) |
| return; |
| memset(&rmregs, 0, sizeof(rmregs)); |
| rmregs.ulBIOSIntNo = intno; |
| INDPMI(eax); INDPMI(ebx); INDPMI(ecx); INDPMI(edx); INDPMI(esi); INDPMI(edi); |
| rmregs.aCRF.reg_ds = regs->ds; |
| rmregs.aCRF.reg_es = regs->es; |
| if (intno == 0x10) { |
| eax = rmregs.aCRF.reg_eax; |
| switch (eax & 0xFFFF) { |
| case 0x4F00: |
| /* We have to hack the way this function works, due to |
| * some bugs in the IBM mini-VDM BIOS support. Specifically |
| * we need to make the input buffer and output buffer the |
| * 'same' buffer, and that ES:SI points to the output |
| * buffer (ignored by the BIOS). The data will end up |
| * being returned in the input buffer, except for the |
| * first four bytes ('VESA') that will not be returned. |
| */ |
| rmregs.pB[0].bBufferType = INPUT_BUFFER; |
| rmregs.pB[0].bSelCRF = REG_OFFSET(reg_es); |
| rmregs.pB[0].bOffCRF = REG_OFFSET(reg_edi); |
| rmregs.pB[0].pAddress = RMBuf; |
| rmregs.pB[0].ulSize = 4; |
| rmregs.pB[1].bBufferType = OUTPUT_BUFFER; |
| rmregs.pB[1].bSelCRF = REG_OFFSET(reg_es); |
| rmregs.pB[1].bOffCRF = REG_OFFSET(reg_esi); |
| rmregs.pB[1].pAddress = ((PBYTE)RMBuf)+4; |
| rmregs.pB[1].ulSize = 512-4; |
| break; |
| case 0x4F01: |
| rmregs.pB[0].bBufferType = OUTPUT_BUFFER; |
| rmregs.pB[0].bSelCRF = REG_OFFSET(reg_es); |
| rmregs.pB[0].bOffCRF = REG_OFFSET(reg_edi); |
| rmregs.pB[0].pAddress = RMBuf; |
| rmregs.pB[0].ulSize = 256; |
| break; |
| case 0x4F02: |
| rmregs.pB[0].bBufferType = INPUT_BUFFER; |
| rmregs.pB[0].bSelCRF = REG_OFFSET(reg_es); |
| rmregs.pB[0].bOffCRF = REG_OFFSET(reg_edi); |
| rmregs.pB[0].pAddress = RMBuf; |
| rmregs.pB[0].ulSize = 256; |
| break; |
| case 0x4F09: |
| rmregs.pB[0].bBufferType = INPUT_BUFFER; |
| rmregs.pB[0].bSelCRF = REG_OFFSET(reg_es); |
| rmregs.pB[0].bOffCRF = REG_OFFSET(reg_edi); |
| rmregs.pB[0].pAddress = RMBuf; |
| rmregs.pB[0].ulSize = 1024; |
| break; |
| case 0x4F0A: |
| /* Due to bugs in the mini-VDM in OS/2, the 0x4F0A protected |
| * mode interface functions will not work (we never get any |
| * selectors returned), so we fail this function here. The |
| * rest of the VBE/Core driver will work properly if this |
| * function is failed, because the VBE 2.0 and 3.0 specs |
| * allow for this. |
| */ |
| regs->eax = 0x014F; |
| return; |
| } |
| } |
| if (useVPMI) |
| PM_VIDEOPMI32Request(&Adapter,PMIREQUEST_SOFTWAREINT,NULL,&rmregs); |
| else { |
| DosSysCtl(6, &rmregs); |
| } |
| |
| OUTDPMI(eax); OUTDPMI(ebx); OUTDPMI(ecx); OUTDPMI(edx); OUTDPMI(esi); OUTDPMI(edi); |
| if (((regs->eax & 0xFFFF) == 0x004F) && ((eax & 0xFFFF) == 0x4F00)) { |
| /* Hack to fix up the missing 'VESA' string for mini-VDM */ |
| memcpy(RMBuf,"VESA",4); |
| } |
| regs->ds = rmregs.aCRF.reg_ds; |
| regs->es = rmregs.aCRF.reg_es; |
| regs->flags = rmregs.aCRF.reg_eflag; |
| } |
| |
| #define IN(reg) rmregs.reg = in->e.reg |
| #define OUT(reg) out->e.reg = rmregs.reg |
| |
| /**************************************************************************** |
| REMARKS: |
| Issue a real mode interrupt. |
| ****************************************************************************/ |
| int PMAPI PM_int86( |
| int intno, |
| RMREGS *in, |
| RMREGS *out) |
| { |
| DPMI_regs rmregs; |
| |
| memset(&rmregs, 0, sizeof(rmregs)); |
| IN(eax); IN(ebx); IN(ecx); IN(edx); IN(esi); IN(edi); |
| DPMI_int86(intno,&rmregs); |
| OUT(eax); OUT(ebx); OUT(ecx); OUT(edx); OUT(esi); OUT(edi); |
| out->x.cflag = rmregs.flags & 0x1; |
| return out->x.ax; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Issue a real mode interrupt. |
| ****************************************************************************/ |
| int PMAPI PM_int86x( |
| int intno, |
| RMREGS *in, |
| RMREGS *out, |
| RMSREGS *sregs) |
| { |
| DPMI_regs rmregs; |
| |
| memset(&rmregs, 0, sizeof(rmregs)); |
| IN(eax); IN(ebx); IN(ecx); IN(edx); IN(esi); IN(edi); |
| rmregs.es = sregs->es; |
| rmregs.ds = sregs->ds; |
| DPMI_int86(intno,&rmregs); |
| OUT(eax); OUT(ebx); OUT(ecx); OUT(edx); OUT(esi); OUT(edi); |
| sregs->es = rmregs.es; |
| sregs->cs = rmregs.cs; |
| sregs->ss = rmregs.ss; |
| sregs->ds = rmregs.ds; |
| out->x.cflag = rmregs.flags & 0x1; |
| return out->x.ax; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Call a real mode far function. |
| ****************************************************************************/ |
| void PMAPI PM_callRealMode( |
| uint seg, |
| uint off, |
| RMREGS *in, |
| RMSREGS *sregs) |
| { |
| PM_fatalError("PM_callRealMode not supported on OS/2!"); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Return the amount of available memory. |
| ****************************************************************************/ |
| void PMAPI PM_availableMemory( |
| ulong *physical, |
| ulong *total) |
| { |
| /* Unable to get reliable values from OS/2 for this */ |
| *physical = *total = 0; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Allocate a block of locked, physical memory for DMA operations. |
| ****************************************************************************/ |
| void * PMAPI PM_allocLockedMem( |
| uint size, |
| ulong *physAddr, |
| ibool contiguous, |
| ibool below16M) |
| { |
| parmsIn[0] = size; |
| parmsIn[1] = contiguous; |
| parmsIn[2] = below16M; |
| CallSDDHelp(PMHELP_ALLOCLOCKED); |
| *physAddr = parmsOut[1]; |
| return (void*)parmsOut[0]; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Free a block of locked physical memory. |
| ****************************************************************************/ |
| void PMAPI PM_freeLockedMem( |
| void *p, |
| uint size, |
| ibool contiguous) |
| { |
| parmsIn[0] = (ulong)p; |
| CallSDDHelp(PMHELP_FREELOCKED); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Allocates a new block of pages for the page block manager. |
| ****************************************************************************/ |
| static pageblock *PM_addNewPageBlock(void) |
| { |
| int i; |
| pageblock *newBlock; |
| char *p,*next; |
| |
| /* Allocate memory for the new page block, and add to head of list */ |
| if (DosAllocSharedMem((void**)&newBlock,NULL,PAGE_BLOCK_SIZE,OBJ_GETTABLE | PAG_READ | PAG_WRITE | PAG_COMMIT)) |
| return NULL; |
| if (!PM_lockDataPages(newBlock,PAGE_BLOCK_SIZE,&newBlock->lockHandle)) |
| return NULL; |
| newBlock->prev = NULL; |
| newBlock->next = pageBlocks; |
| if (pageBlocks) |
| pageBlocks->prev = newBlock; |
| pageBlocks = newBlock; |
| |
| /* Initialise the page aligned free list for the page block */ |
| newBlock->freeCount = PAGES_PER_BLOCK; |
| newBlock->freeList = p = (char*)(((ulong)(newBlock + 1) + (PM_PAGE_SIZE-1)) & ~(PM_PAGE_SIZE-1)); |
| newBlock->freeListStart = newBlock->freeList; |
| newBlock->freeListEnd = p + (PAGES_PER_BLOCK-1) * PM_PAGE_SIZE; |
| for (i = 0; i < PAGES_PER_BLOCK; i++,p = next) |
| FREELIST_NEXT(p) = next = p + PM_PAGE_SIZE; |
| FREELIST_NEXT(p - PM_PAGE_SIZE) = NULL; |
| return newBlock; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Allocates a page aligned and page sized block of memory |
| ****************************************************************************/ |
| void * PMAPI PM_allocPage( |
| ibool locked) |
| { |
| pageblock *block; |
| void *p; |
| |
| /* Scan the block list looking for any free blocks. Allocate a new |
| * page block if no free blocks are found. |
| */ |
| for (block = pageBlocks; block != NULL; block = block->next) { |
| if (block->freeCount) |
| break; |
| } |
| if (block == NULL && (block = PM_addNewPageBlock()) == NULL) |
| return NULL; |
| block->freeCount--; |
| p = block->freeList; |
| block->freeList = FREELIST_NEXT(p); |
| (void)locked; |
| return p; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Free a page aligned and page sized block of memory |
| ****************************************************************************/ |
| void PMAPI PM_freePage( |
| void *p) |
| { |
| pageblock *block; |
| |
| /* First find the page block that this page belongs to */ |
| for (block = pageBlocks; block != NULL; block = block->next) { |
| if (p >= block->freeListStart && p <= block->freeListEnd) |
| break; |
| } |
| CHECK(block != NULL); |
| |
| /* Now free the block by adding it to the free list */ |
| FREELIST_NEXT(p) = block->freeList; |
| block->freeList = p; |
| if (++block->freeCount == PAGES_PER_BLOCK) { |
| /* If all pages in the page block are now free, free the entire |
| * page block itself. |
| */ |
| if (block == pageBlocks) { |
| /* Delete from head */ |
| pageBlocks = block->next; |
| if (block->next) |
| block->next->prev = NULL; |
| } |
| else { |
| /* Delete from middle of list */ |
| CHECK(block->prev != NULL); |
| block->prev->next = block->next; |
| if (block->next) |
| block->next->prev = block->prev; |
| } |
| |
| /* Unlock the memory and free it */ |
| PM_unlockDataPages(block,PAGE_BLOCK_SIZE,&block->lockHandle); |
| DosFreeMem(block); |
| } |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Map in all the shared memory blocks for managing the memory pages above. |
| ****************************************************************************/ |
| void PMAPI PM_mapSharedPages(void) |
| { |
| pageblock *block; |
| |
| /* Map all the page blocks above into the shared memory for process */ |
| for (block = pageBlocks; block != NULL; block = block->next) { |
| DosGetSharedMem(block, PAG_READ | PAG_WRITE); |
| } |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Lock linear memory so it won't be paged. |
| ****************************************************************************/ |
| int PMAPI PM_lockDataPages( |
| void *p, |
| uint len, |
| PM_lockHandle *lockHandle) |
| { |
| parmsIn[0] = (ulong)p; |
| parmsIn[1] = len; |
| CallSDDHelp(PMHELP_LOCKPAGES); |
| lockHandle->h[0] = parmsOut[1]; |
| lockHandle->h[1] = parmsOut[2]; |
| lockHandle->h[2] = parmsOut[3]; |
| return parmsOut[0]; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Unlock linear memory so it won't be paged. |
| ****************************************************************************/ |
| int PMAPI PM_unlockDataPages( |
| void *p, |
| uint len, |
| PM_lockHandle *lockHandle) |
| { |
| parmsIn[0] = lockHandle->h[0]; |
| parmsIn[1] = lockHandle->h[1]; |
| parmsIn[2] = lockHandle->h[2]; |
| return CallSDDHelp(PMHELP_UNLOCKPAGES); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Lock linear memory so it won't be paged. |
| ****************************************************************************/ |
| int PMAPI PM_lockCodePages( |
| void (*p)(), |
| uint len, |
| PM_lockHandle *lockHandle) |
| { |
| parmsIn[0] = (ulong)p; |
| parmsIn[1] = len; |
| CallSDDHelp(PMHELP_LOCKPAGES); |
| lockHandle->h[0] = parmsOut[1]; |
| lockHandle->h[1] = parmsOut[2]; |
| lockHandle->h[2] = parmsOut[3]; |
| return parmsOut[0]; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Unlock linear memory so it won't be paged. |
| ****************************************************************************/ |
| int PMAPI PM_unlockCodePages( |
| void (*p)(), |
| uint len, |
| PM_lockHandle *lockHandle) |
| { |
| parmsIn[0] = lockHandle->h[0]; |
| parmsIn[1] = lockHandle->h[1]; |
| parmsIn[2] = lockHandle->h[2]; |
| return CallSDDHelp(PMHELP_UNLOCKPAGES); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Call the VBE/Core software interrupt to change display banks. |
| ****************************************************************************/ |
| void PMAPI PM_setBankA( |
| int bank) |
| { |
| INTCRF rmregs; |
| |
| if (!InitInt10()) |
| return; |
| memset(&rmregs, 0, sizeof(rmregs)); |
| rmregs.ulBIOSIntNo = 0x10; |
| rmregs.aCRF.reg_eax = 0x4F05; |
| rmregs.aCRF.reg_ebx = 0x0000; |
| rmregs.aCRF.reg_edx = bank; |
| PM_VIDEOPMI32Request(&Adapter,PMIREQUEST_SOFTWAREINT,&rmregs,NULL); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Call the VBE/Core software interrupt to change display banks. |
| ****************************************************************************/ |
| void PMAPI PM_setBankAB( |
| int bank) |
| { |
| INTCRF rmregs; |
| |
| if (!InitInt10()) |
| return; |
| memset(&rmregs, 0, sizeof(rmregs)); |
| rmregs.ulBIOSIntNo = 0x10; |
| rmregs.aCRF.reg_eax = 0x4F05; |
| rmregs.aCRF.reg_ebx = 0x0000; |
| rmregs.aCRF.reg_edx = bank; |
| PM_VIDEOPMI32Request(&Adapter,PMIREQUEST_SOFTWAREINT,&rmregs,NULL); |
| rmregs.ulBIOSIntNo = 0x10; |
| rmregs.aCRF.reg_eax = 0x4F05; |
| rmregs.aCRF.reg_ebx = 0x0001; |
| rmregs.aCRF.reg_edx = bank; |
| PM_VIDEOPMI32Request(&Adapter,PMIREQUEST_SOFTWAREINT,&rmregs,NULL); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Call the VBE/Core software interrupt to change display start address. |
| ****************************************************************************/ |
| void PMAPI PM_setCRTStart( |
| int x, |
| int y, |
| int waitVRT) |
| { |
| INTCRF rmregs; |
| |
| if (!InitInt10()) |
| return; |
| memset(&rmregs, 0, sizeof(rmregs)); |
| rmregs.ulBIOSIntNo = 0x10; |
| rmregs.aCRF.reg_eax = 0x4F07; |
| rmregs.aCRF.reg_ebx = waitVRT; |
| rmregs.aCRF.reg_ecx = x; |
| rmregs.aCRF.reg_edx = y; |
| PM_VIDEOPMI32Request(&Adapter,PMIREQUEST_SOFTWAREINT,&rmregs,NULL); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Execute the POST on the secondary BIOS for a controller. |
| ****************************************************************************/ |
| ibool PMAPI PM_doBIOSPOST( |
| ushort axVal, |
| ulong BIOSPhysAddr, |
| void *mappedBIOS, |
| ulong BIOSLen) |
| { |
| (void)axVal; |
| (void)BIOSPhysAddr; |
| (void)mappedBIOS; |
| (void)BIOSLen; |
| return false; |
| } |
| |
| /**************************************************************************** |
| PARAMETERS: |
| base - The starting physical base address of the region |
| size - The size in bytes of the region |
| type - Type to place into the MTRR register |
| |
| RETURNS: |
| Error code describing the result. |
| |
| REMARKS: |
| Function to enable write combining for the specified region of memory. |
| ****************************************************************************/ |
| int PMAPI PM_enableWriteCombine( |
| ulong base, |
| ulong size, |
| uint type) |
| { |
| return MTRR_enableWriteCombine(base,size,type); |
| } |
| |
| /* TODO: Move the MTRR helper stuff into the call gate, or better yet */ |
| /* entirely into the ring 0 helper driver!! */ |
| |
| /* MTRR helper functions. To make it easier to implement the MTRR support |
| * under OS/2, we simply put our ring 0 helper functions into the |
| * helper device driver rather than the entire MTRR module. This makes |
| * it easier to maintain the MTRR support since we don't need to deal |
| * with 16-bit ring 0 code in the MTRR library. |
| */ |
| |
| /**************************************************************************** |
| REMARKS: |
| Flush the translation lookaside buffer. |
| ****************************************************************************/ |
| void PMAPI PM_flushTLB(void) |
| { |
| CallSDDHelp(PMHELP_FLUSHTLB); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Return true if ring 0 (or if we can call the helpers functions at ring 0) |
| ****************************************************************************/ |
| ibool _ASMAPI _MTRR_isRing0(void) |
| { |
| return true; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Read and return the value of the CR4 register |
| ****************************************************************************/ |
| ulong _ASMAPI _MTRR_saveCR4(void) |
| { |
| return CallSDDHelp(PMHELP_SAVECR4); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Restore the value of the CR4 register |
| ****************************************************************************/ |
| void _ASMAPI _MTRR_restoreCR4(ulong cr4Val) |
| { |
| parmsIn[0] = cr4Val; |
| CallSDDHelp(PMHELP_RESTORECR4); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Read a machine status register for the CPU. |
| ****************************************************************************/ |
| void _ASMAPI _MTRR_readMSR( |
| ulong reg, |
| ulong *eax, |
| ulong *edx) |
| { |
| parmsIn[0] = reg; |
| CallSDDHelp(PMHELP_READMSR); |
| *eax = parmsOut[0]; |
| *edx = parmsOut[1]; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Write a machine status register for the CPU. |
| ****************************************************************************/ |
| void _ASMAPI _MTRR_writeMSR( |
| ulong reg, |
| ulong eax, |
| ulong edx) |
| { |
| parmsIn[0] = reg; |
| parmsIn[1] = eax; |
| parmsIn[2] = edx; |
| CallSDDHelp(PMHELP_WRITEMSR); |
| } |
| |
| PM_MODULE PMAPI PM_loadLibrary( |
| const char *szDLLName) |
| { |
| /* TODO: Implement this to load shared libraries! */ |
| (void)szDLLName; |
| return NULL; |
| } |
| |
| void * PMAPI PM_getProcAddress( |
| PM_MODULE hModule, |
| const char *szProcName) |
| { |
| /* TODO: Implement this! */ |
| (void)hModule; |
| (void)szProcName; |
| return NULL; |
| } |
| |
| void PMAPI PM_freeLibrary( |
| PM_MODULE hModule) |
| { |
| /* TODO: Implement this! */ |
| (void)hModule; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Internal function to convert the find data to the generic interface. |
| ****************************************************************************/ |
| static void convertFindData( |
| PM_findData *findData, |
| FILEFINDBUF3 *blk) |
| { |
| ulong dwSize = findData->dwSize; |
| |
| memset(findData,0,findData->dwSize); |
| findData->dwSize = dwSize; |
| if (blk->attrFile & FILE_READONLY) |
| findData->attrib |= PM_FILE_READONLY; |
| if (blk->attrFile & FILE_DIRECTORY) |
| findData->attrib |= PM_FILE_DIRECTORY; |
| if (blk->attrFile & FILE_ARCHIVED) |
| findData->attrib |= PM_FILE_ARCHIVE; |
| if (blk->attrFile & FILE_HIDDEN) |
| findData->attrib |= PM_FILE_HIDDEN; |
| if (blk->attrFile & FILE_SYSTEM) |
| findData->attrib |= PM_FILE_SYSTEM; |
| findData->sizeLo = blk->cbFile; |
| findData->sizeHi = 0; |
| strncpy(findData->name,blk->achName,PM_MAX_PATH); |
| findData->name[PM_MAX_PATH-1] = 0; |
| } |
| |
| #define FIND_MASK (FILE_ARCHIVED | FILE_DIRECTORY | FILE_SYSTEM | FILE_HIDDEN | FILE_READONLY) |
| |
| /**************************************************************************** |
| REMARKS: |
| Function to find the first file matching a search criteria in a directory. |
| ****************************************************************************/ |
| void *PMAPI PM_findFirstFile( |
| const char *filename, |
| PM_findData *findData) |
| { |
| FILEFINDBUF3 blk; |
| HDIR hdir = HDIR_CREATE; |
| ulong count = 1; |
| |
| if (DosFindFirst((PSZ)filename,&hdir,FIND_MASK,&blk,sizeof(blk),&count,FIL_STANDARD) == NO_ERROR) { |
| convertFindData(findData,&blk); |
| return (void*)hdir; |
| } |
| return PM_FILE_INVALID; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Function to find the next file matching a search criteria in a directory. |
| ****************************************************************************/ |
| ibool PMAPI PM_findNextFile( |
| void *handle, |
| PM_findData *findData) |
| { |
| FILEFINDBUF3 blk; |
| ulong count = 1; |
| |
| if (DosFindNext((HDIR)handle,&blk,sizeof(blk),&count) == NO_ERROR) { |
| convertFindData(findData,&blk); |
| return true; |
| } |
| return false; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Function to close the find process |
| ****************************************************************************/ |
| void PMAPI PM_findClose( |
| void *handle) |
| { |
| DosFindClose((HDIR)handle); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Function to determine if a drive is a valid drive or not. Under Unix this |
| function will return false for anything except a value of 3 (considered |
| the root drive, and equivalent to C: for non-Unix systems). The drive |
| numbering is: |
| |
| 0 - Current drive |
| 1 - Drive A: |
| 2 - Drive B: |
| 3 - Drive C: |
| etc |
| |
| ****************************************************************************/ |
| ibool PMAPI PM_driveValid( |
| char drive) |
| { |
| ulong cntDisk,cntDriveMap; |
| ibool valid; |
| |
| DosQueryCurrentDisk(&cntDisk,&cntDriveMap); |
| valid = (DosSetDefaultDisk(drive) == NO_ERROR); |
| DosSetDefaultDisk(cntDisk); |
| return valid; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Function to get the current working directory for the specififed drive. |
| Under Unix this will always return the current working directory regardless |
| of what the value of 'drive' is. |
| ****************************************************************************/ |
| void PMAPI PM_getdcwd( |
| int drive, |
| char *dir, |
| int len) |
| { |
| ulong length = len; |
| |
| DosQueryCurrentDir(drive, (PSZ)dir, &length); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Function to change the file attributes for a specific file. |
| ****************************************************************************/ |
| void PMAPI PM_setFileAttr( |
| const char *filename, |
| uint attrib) |
| { |
| FILESTATUS3 s; |
| |
| if (DosQueryPathInfo((PSZ)filename,FIL_STANDARD,(PVOID)&s,sizeof(s))) |
| return; |
| s.attrFile = 0; |
| if (attrib & PM_FILE_READONLY) |
| s.attrFile |= FILE_READONLY; |
| if (attrib & PM_FILE_ARCHIVE) |
| s.attrFile |= FILE_ARCHIVED; |
| if (attrib & PM_FILE_HIDDEN) |
| s.attrFile |= FILE_HIDDEN; |
| if (attrib & PM_FILE_SYSTEM) |
| s.attrFile |= FILE_SYSTEM; |
| DosSetPathInfo((PSZ)filename,FIL_STANDARD,(PVOID)&s,sizeof(s),0L); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Function to get the file attributes for a specific file. |
| ****************************************************************************/ |
| uint PMAPI PM_getFileAttr( |
| const char *filename) |
| { |
| FILESTATUS3 fs3; |
| uint retval = 0; |
| |
| if (DosQueryPathInfo((PSZ)filename, FIL_STANDARD, &fs3, sizeof(FILESTATUS3))) |
| return 0; |
| if (fs3.attrFile & FILE_READONLY) |
| retval |= PM_FILE_READONLY; |
| if (fs3.attrFile & FILE_ARCHIVED) |
| retval |= PM_FILE_ARCHIVE; |
| if (fs3.attrFile & FILE_HIDDEN) |
| retval |= PM_FILE_HIDDEN; |
| if (fs3.attrFile & FILE_SYSTEM) |
| retval |= PM_FILE_SYSTEM; |
| return retval; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Function to create a directory. |
| ****************************************************************************/ |
| ibool PMAPI PM_mkdir( |
| const char *filename) |
| { |
| return DosCreateDir((PSZ)filename,NULL) == NO_ERROR; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Function to remove a directory. |
| ****************************************************************************/ |
| ibool PMAPI PM_rmdir( |
| const char *filename) |
| { |
| return DosDeleteDir((PSZ)filename) == NO_ERROR; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Function to get the file time and date for a specific file. |
| ****************************************************************************/ |
| ibool PMAPI PM_getFileTime( |
| const char *filename, |
| ibool gmTime, |
| PM_time *time) |
| { |
| FILESTATUS3 fs3; |
| struct tm tc; |
| struct tm *ret; |
| time_t tt; |
| |
| if (DosQueryPathInfo((PSZ)filename, FIL_STANDARD, &fs3, sizeof(FILESTATUS3))) |
| return false; |
| if (gmTime) { |
| tc.tm_year = fs3.fdateLastWrite.year + 80; |
| tc.tm_mon = fs3.fdateLastWrite.month - 1; |
| tc.tm_mday = fs3.fdateLastWrite.day; |
| tc.tm_hour = fs3.ftimeLastWrite.hours; |
| tc.tm_min = fs3.ftimeLastWrite.minutes; |
| tc.tm_sec = fs3.ftimeLastWrite.twosecs * 2; |
| if((tt = mktime(&tc)) == -1) |
| return false; |
| if(!(ret = gmtime(&tt))) |
| return false; |
| time->sec = ret->tm_sec; |
| time->day = ret->tm_mday; |
| time->mon = ret->tm_mon + 1; |
| time->year = ret->tm_year - 80; |
| time->min = ret->tm_min; |
| time->hour = ret->tm_hour; |
| } |
| else { |
| time->sec = fs3.ftimeLastWrite.twosecs * 2; |
| time->day = fs3.fdateLastWrite.day; |
| time->mon = fs3.fdateLastWrite.month; |
| time->year = fs3.fdateLastWrite.year; |
| time->min = fs3.ftimeLastWrite.minutes; |
| time->hour = fs3.ftimeLastWrite.hours; |
| } |
| return true; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Function to set the file time and date for a specific file. |
| ****************************************************************************/ |
| ibool PMAPI PM_setFileTime( |
| const char *filename, |
| ibool gmTime, |
| PM_time *time) |
| { |
| FILESTATUS3 fs3; |
| struct tm tc; |
| struct tm *ret; |
| time_t tt; |
| |
| if (DosQueryPathInfo((PSZ)filename,FIL_STANDARD,(PVOID)&fs3,sizeof(fs3))) |
| return false; |
| if (gmTime) { |
| tc.tm_year = time->year + 80; |
| tc.tm_mon = time->mon - 1; |
| tc.tm_mday = time->day; |
| tc.tm_hour = time->hour; |
| tc.tm_min = time->min; |
| tc.tm_sec = time->sec; |
| if((tt = mktime(&tc)) == -1) |
| return false; |
| ret = localtime(&tt); |
| fs3.ftimeLastWrite.twosecs = ret->tm_sec / 2; |
| fs3.fdateLastWrite.day = ret->tm_mday; |
| fs3.fdateLastWrite.month = ret->tm_mon + 1; |
| fs3.fdateLastWrite.year = ret->tm_year - 80; |
| fs3.ftimeLastWrite.minutes = ret->tm_min; |
| fs3.ftimeLastWrite.hours = ret->tm_hour; |
| } |
| else { |
| fs3.ftimeLastWrite.twosecs = time->sec / 2; |
| fs3.fdateLastWrite.day = time->day; |
| fs3.fdateLastWrite.month = time->mon; |
| fs3.fdateLastWrite.year = time->year; |
| fs3.ftimeLastWrite.minutes = time->min; |
| fs3.ftimeLastWrite.hours = time->hour; |
| } |
| memcpy(&fs3.fdateLastAccess, &fs3.fdateLastWrite, sizeof(FDATE)); |
| memcpy(&fs3.fdateCreation, &fs3.fdateLastWrite, sizeof(FDATE)); |
| memcpy(&fs3.ftimeLastAccess, &fs3.ftimeLastWrite, sizeof(FTIME)); |
| memcpy(&fs3.ftimeCreation, &fs3.ftimeLastWrite, sizeof(FTIME)); |
| DosSetPathInfo((PSZ)filename,FIL_STANDARD,(PVOID)&fs3,sizeof(FILESTATUS3),0L); |
| return true; |
| } |