mirror of
https://github.com/WindowsNT351/CE-Collections.git
synced 2025-12-27 01:20:25 +08:00
501 lines
12 KiB
C
501 lines
12 KiB
C
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
//
|
|
// Use of this sample source code is subject to the terms of the Microsoft
|
|
// license agreement under which you licensed this sample source code. If
|
|
// you did not accept the terms of the license agreement, you are not
|
|
// authorized to use this sample source code. For the terms of the license,
|
|
// please see the license agreement between you and Microsoft or, if applicable,
|
|
// see the LICENSE.RTF on your install media or the root of your tools installation.
|
|
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
|
|
//
|
|
/*++
|
|
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
PARTICULAR PURPOSE.
|
|
|
|
Module Name:
|
|
|
|
mdppfs.c
|
|
|
|
Abstract:
|
|
|
|
This file implements the NK kernel ppfs client side interface
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
#include "windows.h"
|
|
#include "wdm.h"
|
|
#include "pc.h"
|
|
|
|
#define PAR_PORT_BASE 0x0378
|
|
#define PAR_PORT_DATA 0
|
|
#define PAR_PORT_STAT 1
|
|
#define PAR_PORT_CTRL 2
|
|
|
|
#define PAR_STAT_NBUSY 0x80
|
|
#define PAR_STAT_NACK 0x40
|
|
#define PAR_STAT_PE 0x20
|
|
#define PAR_STAT_SLCT 0x10
|
|
#define PAR_STAT_NERR 0x08 // This isn't connected
|
|
|
|
#define PAR_CTRL_READ 0x20
|
|
#define PAR_CTRL_IRQE 0x10
|
|
#define PAR_CTRL_SLCT_IN 0x08
|
|
#define PAR_CTRL_NINIT 0x04
|
|
#define PAR_CTRL_AUTOFEED 0x02
|
|
#define PAR_CTRL_STROBE 0x01
|
|
|
|
#define STATUS_IS_DISCONNECTED(a) \
|
|
((((a) & (PAR_STAT_PE | PAR_STAT_SLCT)) != PAR_STAT_SLCT) || \
|
|
(((a) & (PAR_STAT_NBUSY | PAR_STAT_NACK)) == PAR_STAT_NACK))
|
|
|
|
//#define STATUS_IS_DISCONNECTED(a) (!((a) & PAR_STAT_SLCT))
|
|
|
|
#if DEBUG
|
|
#define LOG_ENTRY_TYPE_MASK 0xFF00
|
|
#define LOG_ENTRY_DATA_MASK 0x00FF
|
|
|
|
#define LOG_ENTRY_READ ((USHORT)('R' << 8))
|
|
#define LOG_ENTRY_WRITE ((USHORT)('W' << 8))
|
|
#define LOG_ENTRY_CONTROL ((USHORT)('C' << 8))
|
|
#define LOG_ENTRY_STATUS ((USHORT)('S' << 8))
|
|
#define LOG_ENTRY_DATA ((USHORT)('D' << 8))
|
|
#define LOG_ENTRY_EXIT ((USHORT)('E' << 8))
|
|
#define LOG_ENTRY_EVENT ((USHORT)('V' << 8))
|
|
|
|
#define LOG_EVENT_SKIP_RECEIVE ((USHORT)0)
|
|
#define LOG_EVENT_BAD_STATUS ((USHORT)1)
|
|
#define LOG_EVENT_BAD_DISCONNECT ((USHORT)2)
|
|
|
|
#define NUMBER_LOG_EVENTS 3
|
|
|
|
TCHAR *EventDescriptions[NUMBER_LOG_EVENTS] =
|
|
{
|
|
TEXT("# Skipped Receives"),
|
|
TEXT("# Bad Status"),
|
|
TEXT("# Bad Disconnect"),
|
|
};
|
|
|
|
DWORD dwEventCounters[NUMBER_LOG_EVENTS];
|
|
|
|
VOID LogEntry(USHORT usEntry);
|
|
VOID DumpLog(VOID);
|
|
VOID DumpCounters(VOID);
|
|
|
|
#define LOG_ENTRY(a) LogEntry(a)
|
|
#define DUMP_LOG() DumpLog()
|
|
#else
|
|
#define LOG_ENTRY(a)
|
|
#define DUMP_LOG()
|
|
#endif
|
|
|
|
extern volatile DWORD *PtrCurMSec; /* current millisecond counter */
|
|
|
|
#define IoPortBase ((PUCHAR)PAR_PORT_BASE)
|
|
|
|
BOOL NoPPFS;
|
|
BOOL bLastOpWasWrite;
|
|
|
|
BOOL __inline VerifyDisconnect(VOID)
|
|
{
|
|
int i;
|
|
USHORT usStatus;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
usStatus = READ_PORT_UCHAR(IoPortBase + PAR_PORT_STAT);
|
|
|
|
LOG_ENTRY((USHORT)(LOG_ENTRY_STATUS | usStatus));
|
|
|
|
if (!STATUS_IS_DISCONNECTED(usStatus))
|
|
{
|
|
LOG_ENTRY(LOG_ENTRY_EVENT | LOG_EVENT_BAD_STATUS);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
BOOL WaitForStatus(USHORT usMask, USHORT usValue)
|
|
{
|
|
USHORT usStatus;
|
|
DWORD msecStart = *PtrCurMSec;
|
|
int tries = 0;
|
|
|
|
do
|
|
{
|
|
if (*PtrCurMSec - msecStart >= 200)
|
|
{
|
|
if (++tries > 5)
|
|
{
|
|
NoPPFS = TRUE;
|
|
|
|
printf("\r\nWaitForStatus: time out (1), status = %2.2X\r\n",
|
|
usStatus);
|
|
|
|
LOG_ENTRY(LOG_ENTRY_EXIT | 2);
|
|
|
|
DUMP_LOG();
|
|
|
|
return FALSE;
|
|
}
|
|
msecStart = *PtrCurMSec;
|
|
}
|
|
|
|
usStatus = READ_PORT_UCHAR(IoPortBase + PAR_PORT_STAT);
|
|
|
|
LOG_ENTRY((USHORT)(LOG_ENTRY_STATUS | usStatus));
|
|
|
|
//
|
|
// Use SELECTIN to identify the existence of ppsh
|
|
//
|
|
if (STATUS_IS_DISCONNECTED(usStatus))
|
|
{
|
|
if (VerifyDisconnect())
|
|
{
|
|
printf("\r\nWaitForStatus: PPSH disconnected\r\n");
|
|
|
|
NoPPFS = TRUE;
|
|
|
|
LOG_ENTRY(LOG_ENTRY_EXIT | 1);
|
|
|
|
DUMP_LOG();
|
|
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
usStatus = READ_PORT_UCHAR(IoPortBase + PAR_PORT_STAT);
|
|
LOG_ENTRY((USHORT)(LOG_ENTRY_STATUS | usStatus));
|
|
}
|
|
}
|
|
|
|
if ((usStatus & usMask) == usValue)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
usStatus = READ_PORT_UCHAR(IoPortBase + PAR_PORT_STAT);
|
|
|
|
LOG_ENTRY((USHORT)(LOG_ENTRY_STATUS | usStatus));
|
|
|
|
if ((usStatus & usMask) != usValue)
|
|
{
|
|
LOG_ENTRY(LOG_ENTRY_EVENT | LOG_EVENT_BAD_STATUS);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while ((usStatus & usMask) != usValue);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int OEMParallelPortInit(void)
|
|
{
|
|
LOG_ENTRY(LOG_ENTRY_CONTROL | PAR_CTRL_AUTOFEED | PAR_CTRL_STROBE);
|
|
|
|
WRITE_PORT_UCHAR(
|
|
IoPortBase + PAR_PORT_CTRL, PAR_CTRL_AUTOFEED | PAR_CTRL_STROBE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int OEMParallelPortGetByte(void)
|
|
{
|
|
BYTE value;
|
|
|
|
if (NoPPFS)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
Retry:
|
|
LOG_ENTRY(LOG_ENTRY_READ);
|
|
|
|
LOG_ENTRY(LOG_ENTRY_CONTROL | PAR_CTRL_READ | PAR_CTRL_STROBE);
|
|
|
|
WRITE_PORT_UCHAR(IoPortBase + PAR_PORT_CTRL, PAR_CTRL_READ | PAR_CTRL_STROBE);
|
|
|
|
if (!WaitForStatus(PAR_STAT_NACK, PAR_STAT_NACK))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
value = READ_PORT_UCHAR(IoPortBase + PAR_PORT_DATA);
|
|
|
|
LOG_ENTRY((USHORT)(LOG_ENTRY_DATA | value));
|
|
|
|
LOG_ENTRY(LOG_ENTRY_CONTROL | PAR_CTRL_AUTOFEED | PAR_CTRL_STROBE);
|
|
|
|
WRITE_PORT_UCHAR(IoPortBase + PAR_PORT_CTRL, PAR_CTRL_AUTOFEED | PAR_CTRL_STROBE);
|
|
|
|
if (!WaitForStatus(PAR_STAT_NACK, 0))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
LOG_ENTRY(LOG_ENTRY_EXIT | 0);
|
|
|
|
if (bLastOpWasWrite && value == 0x1A)
|
|
{
|
|
//
|
|
// The problem is that periodically the first character we
|
|
// receive after a write is the last byte sent of the previous write.
|
|
//
|
|
// For now we will ignore it
|
|
//
|
|
LOG_ENTRY(LOG_ENTRY_EVENT | LOG_EVENT_SKIP_RECEIVE);
|
|
bLastOpWasWrite = FALSE;
|
|
goto Retry;
|
|
}
|
|
|
|
bLastOpWasWrite = FALSE;
|
|
|
|
return value;
|
|
}
|
|
|
|
VOID OEMParallelPortSendByte(BYTE chData)
|
|
{
|
|
if (NoPPFS)
|
|
return;
|
|
|
|
LOG_ENTRY(LOG_ENTRY_WRITE);
|
|
|
|
if (!WaitForStatus(PAR_STAT_NBUSY, 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LOG_ENTRY((USHORT)(LOG_ENTRY_DATA | chData));
|
|
|
|
WRITE_PORT_UCHAR(IoPortBase + PAR_PORT_CTRL, PAR_CTRL_AUTOFEED | PAR_CTRL_STROBE);
|
|
|
|
WRITE_PORT_UCHAR(IoPortBase + PAR_PORT_DATA, chData);
|
|
|
|
LOG_ENTRY(LOG_ENTRY_CONTROL | PAR_CTRL_AUTOFEED);
|
|
|
|
WRITE_PORT_UCHAR(IoPortBase + PAR_PORT_CTRL, PAR_CTRL_AUTOFEED);
|
|
|
|
if (!WaitForStatus(PAR_STAT_NBUSY, PAR_STAT_NBUSY))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LOG_ENTRY(LOG_ENTRY_CONTROL | PAR_CTRL_AUTOFEED | PAR_CTRL_STROBE);
|
|
|
|
WRITE_PORT_UCHAR(IoPortBase + PAR_PORT_CTRL, PAR_CTRL_AUTOFEED | PAR_CTRL_STROBE);
|
|
|
|
LOG_ENTRY(LOG_ENTRY_EXIT | 0);
|
|
|
|
bLastOpWasWrite = TRUE;
|
|
}
|
|
|
|
#if DEBUG
|
|
#define LOG_SIZE 0x1000
|
|
#define RW_STACK_SIZE 10
|
|
|
|
WCHAR wcHexDigits[16] =
|
|
{
|
|
TEXT('0'), TEXT('1'), TEXT('2'), TEXT('3'),
|
|
TEXT('4'), TEXT('5'), TEXT('6'), TEXT('7'),
|
|
TEXT('8'), TEXT('9'), TEXT('A'), TEXT('B'),
|
|
TEXT('C'), TEXT('D'), TEXT('E'), TEXT('F')
|
|
};
|
|
|
|
USHORT usLogBuffer[LOG_SIZE];
|
|
int iLogHead = 0;
|
|
int iLogTail = 0;
|
|
|
|
VOID
|
|
LogEntry(USHORT usEntry)
|
|
{
|
|
static USHORT usLastEntry;
|
|
USHORT usEntryType;
|
|
USHORT usEntryData;
|
|
|
|
usEntryData = usEntry & LOG_ENTRY_DATA_MASK;
|
|
usEntryType = usEntry & LOG_ENTRY_TYPE_MASK;
|
|
|
|
switch (usEntryType)
|
|
{
|
|
case LOG_ENTRY_STATUS:
|
|
if (usLastEntry == usEntry)
|
|
{
|
|
//
|
|
// Don't log duplicate status
|
|
//
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case LOG_ENTRY_EVENT:
|
|
if (usEntryData < NUMBER_LOG_EVENTS)
|
|
{
|
|
dwEventCounters[usEntryData]++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
usLastEntry = usEntry;
|
|
|
|
usLogBuffer[iLogTail++] = usEntry;
|
|
iLogTail %= LOG_SIZE;
|
|
|
|
if (iLogTail == iLogHead)
|
|
{
|
|
iLogHead++;
|
|
iLogHead %= LOG_SIZE;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
DumpLog(VOID)
|
|
{
|
|
// R 00 W 4F
|
|
TCHAR szPrintLine[100];
|
|
int iLogCurrent;
|
|
PTCHAR pCurrentColumn;
|
|
int nDataItems;
|
|
USHORT usCurrentOp, usLastOp;
|
|
int iReadWriteStack[RW_STACK_SIZE];
|
|
int i;
|
|
|
|
pCurrentColumn = szPrintLine;
|
|
nDataItems = 0;
|
|
|
|
usLastOp = 0;
|
|
|
|
for (i = 0; i < RW_STACK_SIZE; i++)
|
|
{
|
|
iReadWriteStack[i] = iLogTail;
|
|
}
|
|
|
|
for (iLogCurrent = iLogHead; iLogCurrent != iLogTail;
|
|
iLogCurrent++, iLogCurrent %= LOG_SIZE)
|
|
{
|
|
usCurrentOp = usLogBuffer[iLogCurrent] & LOG_ENTRY_TYPE_MASK;
|
|
switch (usCurrentOp)
|
|
{
|
|
case LOG_ENTRY_READ:
|
|
case LOG_ENTRY_WRITE:
|
|
if (usLastOp != usCurrentOp)
|
|
{
|
|
if (pCurrentColumn != szPrintLine)
|
|
{
|
|
memcpy(pCurrentColumn, TEXT("\r\n"), sizeof(TEXT("\r\n")));
|
|
OutputDebugString(szPrintLine);
|
|
pCurrentColumn = szPrintLine;
|
|
}
|
|
|
|
*pCurrentColumn++ = (TCHAR)(usLogBuffer[iLogCurrent] >> 8);
|
|
*pCurrentColumn++ = TEXT(' ');
|
|
usLastOp = usCurrentOp;
|
|
nDataItems = 0;
|
|
}
|
|
|
|
for (i = 0; i < (RW_STACK_SIZE - 1); i++)
|
|
{
|
|
iReadWriteStack[i] = iReadWriteStack[i + 1];
|
|
}
|
|
|
|
iReadWriteStack[RW_STACK_SIZE - 1] = iLogCurrent;
|
|
break;
|
|
|
|
case LOG_ENTRY_EVENT:
|
|
case LOG_ENTRY_DATA:
|
|
if (nDataItems == 25)
|
|
{
|
|
memcpy(pCurrentColumn, TEXT("\r\n"), sizeof(TEXT("\r\n")));
|
|
OutputDebugString(szPrintLine);
|
|
pCurrentColumn = szPrintLine;
|
|
*pCurrentColumn++ = TEXT(' ');
|
|
*pCurrentColumn++ = TEXT(' ');
|
|
nDataItems = 0;
|
|
}
|
|
*pCurrentColumn++ = wcHexDigits[(usLogBuffer[iLogCurrent] >> 4) & 0x0F];
|
|
*pCurrentColumn++ = wcHexDigits[usLogBuffer[iLogCurrent] & 0x0F];
|
|
*pCurrentColumn++ = usCurrentOp == LOG_ENTRY_DATA ? TEXT(' ') : TEXT('!');
|
|
nDataItems++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pCurrentColumn != szPrintLine)
|
|
{
|
|
memcpy(pCurrentColumn, TEXT("\r\n"), sizeof(TEXT("\r\n")));
|
|
OutputDebugString(szPrintLine);
|
|
pCurrentColumn = szPrintLine;
|
|
}
|
|
|
|
nDataItems = 0;
|
|
|
|
for (i = 0; i < RW_STACK_SIZE; i++)
|
|
{
|
|
if (iReadWriteStack[i] != iLogTail)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
iLogCurrent = (i < RW_STACK_SIZE) ? iReadWriteStack[i] : iLogTail;
|
|
|
|
for ( ; iLogCurrent != iLogTail; iLogCurrent++, iLogCurrent %= LOG_SIZE)
|
|
{
|
|
if (nDataItems == 16)
|
|
{
|
|
memcpy(pCurrentColumn, TEXT("\r\n"), sizeof(TEXT("\r\n")));
|
|
OutputDebugString(szPrintLine);
|
|
pCurrentColumn = szPrintLine;
|
|
nDataItems = 0;
|
|
}
|
|
|
|
*pCurrentColumn++ = (TCHAR)(usLogBuffer[iLogCurrent] >> 8);
|
|
|
|
*pCurrentColumn++ = TEXT(' ');
|
|
|
|
*pCurrentColumn++ = wcHexDigits[(usLogBuffer[iLogCurrent] >> 4) & 0x0F];
|
|
|
|
*pCurrentColumn++ = wcHexDigits[usLogBuffer[iLogCurrent] & 0x0F];
|
|
|
|
*pCurrentColumn++ = TEXT(' ');
|
|
|
|
nDataItems++;
|
|
}
|
|
|
|
if (pCurrentColumn != szPrintLine)
|
|
{
|
|
memcpy(pCurrentColumn, TEXT("\r\n"), sizeof(TEXT("\r\n")));
|
|
OutputDebugString(szPrintLine);
|
|
pCurrentColumn = szPrintLine;
|
|
}
|
|
|
|
DumpCounters();
|
|
}
|
|
|
|
VOID
|
|
DumpCounters(VOID)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NUMBER_LOG_EVENTS; i++)
|
|
{
|
|
if (dwEventCounters[i] != 0)
|
|
{
|
|
NKDbgPrintfW(
|
|
TEXT("%s = %d\r\n"), EventDescriptions[i], dwEventCounters[i]);
|
|
}
|
|
}
|
|
}
|
|
#endif
|