Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1017 lines
27 KiB

/*++
Copyright (c) 1999 Microsoft Corporation
All Rights Reserved
Module Name:
Usbmon.c
Abstract:
USBMON core port monitor routines
Author:
Revision History:
--*/
#include "precomp.h"
#include "ntddpar.h"
#define LPT_NOT_ERROR 0x8
#define LPT_SELECT 0x10
#define LPT_PAPER_EMPTY 0x20
#define LPT_BENIGN_STATUS LPT_NOT_ERROR | LPT_SELECT
#define MAX_TIMEOUT 300000 //5 minutes
#define JOB_ABORTCHECK_TIMEOUT 5000
const TCHAR cszCFGMGR32[]=TEXT("cfgmgr32.dll");
const CHAR cszReenumFunc[]="CM_Reenumerate_DevNode_Ex";
#ifdef UNICODE
const CHAR cszLocalFunc[]="CM_Locate_DevNode_ExW";
#else
const CHAR cszLocalFunc[]="CM_Locate_DevNode_ExA";
#endif
BOOL GetLptStatus(HANDLE hDeviceHandle,BYTE *Return);
DWORD UsbmonDebug;
BOOL
APIENTRY
DllMain(
HANDLE hModule,
DWORD dwReason,
LPVOID lpRes
)
{
if ( dwReason == DLL_PROCESS_ATTACH )
DisableThreadLibraryCalls(hModule);
return TRUE;
}
BOOL
AbortThisJob(PUSBMON_PORT_INFO pPortInfo)
/*++
Tells if the job should be aborted. A job should be aborted if it has
been deleted or it needs to be restarted.
--*/
{
BOOL bRet = FALSE;
DWORD dwNeeded;
LPJOB_INFO_1 pJobInfo = NULL;
dwNeeded = 0;
GetJob(pPortInfo->hPrinter, pPortInfo->dwJobId, 1, NULL, 0, &dwNeeded);
if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
goto Done;
if ( !(pJobInfo = (LPJOB_INFO_1) AllocSplMem(dwNeeded)) ||
!GetJob(pPortInfo->hPrinter, pPortInfo->dwJobId,
1, (LPBYTE)pJobInfo, dwNeeded, &dwNeeded)
)
goto Done;
bRet = (pJobInfo->Status & JOB_STATUS_DELETING) ||
(pJobInfo->Status & JOB_STATUS_DELETED) ||
(pJobInfo->Status & JOB_STATUS_RESTART);
Done:
FreeSplMem(pJobInfo);
return bRet;
}
BOOL
WINAPI
USBMON_OpenPort(
LPTSTR pszPortName,
LPHANDLE pHandle
)
{
PUSBMON_PORT_INFO pPortInfo, pPrev;
pPortInfo = FindPort(&gUsbmonInfo, pszPortName, &pPrev);
if ( pPortInfo ) {
*pHandle = (LPHANDLE)pPortInfo;
InitializeCriticalSection(&pPortInfo->CriticalSection);
return TRUE;
} else {
SetLastError(ERROR_PATH_NOT_FOUND);
return FALSE;
}
}
BOOL
WINAPI
USBMON_ClosePort(
HANDLE hPort
)
{
PUSBMON_PORT_INFO pPortInfo = (PUSBMON_PORT_INFO)hPort;
DeleteCriticalSection(&pPortInfo->CriticalSection);
return TRUE;
}
//
// Dot4Pnp - test whether we need to force a dot4 pnp event if the dot4 stack doesn't exist.
//
BOOL
Dot4Pnp(
PUSBMON_PORT_INFO pPortInfo
)
{
BOOL bRet = FALSE;
HANDLE hToken;
DEVINST hLPTDevInst;
TCHAR szPnpEntry[]=TEXT("Root\\ParallelClass\\0000"); // The pnp node to reenumerate.
TCHAR cszDot4[]=TEXT("DOT4"); // This relates to the array size below.
TCHAR szPort[5]; // 4 chars for "DOT4" and the ending 0.
UINT uOldErrorMode;
HINSTANCE hCfgMgr32 = 0; // Library instance.
// Pointers to pnp functions...
pfCM_Locate_DevNode_Ex pfnLocateDevNode;
pfCM_Reenumerate_DevNode_Ex pfnReenumDevNode;
//
// Make a copy of the first 4 chars of the port name - to compare against Dot4.
// Copy length of 4 chars + 1 for null.
lstrcpyn( szPort, pPortInfo->szPortName, lstrlen(cszDot4)+1 );
szPort[lstrlen(cszDot4)]=0;
if( lstrcmpi( szPort, cszDot4) != 0)
{
//
// If this is not a dot4 port and we failed to open it - fail.
//
goto Done;
}
//
// If it is a dot4 device we need to force a pnp event on the parallel port to get the
// dot4 stack rebuilt.
// If any of these fail, fail the call just as if the port couldn't be opened.
//
// Load the pnp dll.
//
uOldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
hCfgMgr32 = LoadLibrary( cszCFGMGR32 );
if(!hCfgMgr32)
{
SetErrorMode(uOldErrorMode);
goto Done;
}
SetErrorMode(uOldErrorMode);
//
// Get the Addressed of pnp functions we want to call...
//
pfnLocateDevNode = (pfCM_Locate_DevNode_Ex)GetProcAddress( hCfgMgr32, cszLocalFunc );
pfnReenumDevNode = (pfCM_Reenumerate_DevNode_Ex)GetProcAddress( hCfgMgr32, cszReenumFunc );
if( !pfnLocateDevNode || !pfnReenumDevNode )
goto Done;
//
// We need to revert to system context here as otherwise the pnp call will fail if the user
// is anything other than an admin as this requires admin rights.
// If this fails, the pnp will fail anyway, so we don't need to test the return value.
//
hToken = RevertToPrinterSelf();
//
// Reenumerate from the root of the devnode tree
//
if( ( pfnLocateDevNode( &hLPTDevInst, szPnpEntry, CM_LOCATE_DEVNODE_NORMAL, NULL ) != CR_SUCCESS) ||
( pfnReenumDevNode( hLPTDevInst, CM_REENUMERATE_NORMAL, NULL ) != CR_SUCCESS) )
{
//
// Revert back to the user's context in case we failed for another reason other than
// ACCESS DENIED (not admin)
//
ImpersonatePrinterClient(hToken);
goto Done;
}
//
// Revert back to the user's context.
//
ImpersonatePrinterClient(hToken);
//
// Try and open the port again.
// If we fail, then the device must not be there any more or still switched off - fail as usual.
//
pPortInfo->hDeviceHandle = CreateFile(pPortInfo->szDevicePath,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if ( pPortInfo->hDeviceHandle == INVALID_HANDLE_VALUE )
goto Done;
bRet = TRUE;
Done:
if(hCfgMgr32)
FreeLibrary(hCfgMgr32);
return bRet;
}
BOOL
LocalOpenPort(
PUSBMON_PORT_INFO pPortInfo
)
{
BOOL bRet = FALSE;
EnterCriticalSection(&pPortInfo->CriticalSection);
if ( pPortInfo->hDeviceHandle == INVALID_HANDLE_VALUE ) {
//
// If we have an invalid handle and refcount is non-zero we want the
// job to fail and restart to accept writes. In other words if the
// handle got closed prematurely, because of failing writes, then we
// need the ref count to go down to 0 before calling CreateFile again
//
if ( pPortInfo->cRef )
goto Done;
pPortInfo->hDeviceHandle = CreateFile(pPortInfo->szDevicePath,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
//
// If we failed to open the port - test to see if it is a Dot4 port.
//
if ( pPortInfo->hDeviceHandle == INVALID_HANDLE_VALUE )
{
//
// ERROR_FILE_NOT_FOUND -> Error code for port not there.
//
if( ERROR_FILE_NOT_FOUND != GetLastError() ||
!Dot4Pnp(pPortInfo) )
goto Done;
}
pPortInfo->Ov.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if ( pPortInfo->Ov.hEvent == NULL ) {
CloseHandle(pPortInfo->hDeviceHandle);
pPortInfo->hDeviceHandle = INVALID_HANDLE_VALUE;
goto Done;
}
}
++(pPortInfo->cRef);
bRet = TRUE;
Done:
LeaveCriticalSection(&pPortInfo->CriticalSection);
return bRet;
}
BOOL
LocalClosePort(
PUSBMON_PORT_INFO pPortInfo
)
{
BOOL bRet = TRUE;
BOOL bJobCanceled=FALSE;
EnterCriticalSection(&pPortInfo->CriticalSection);
--(pPortInfo->cRef);
if ( pPortInfo->cRef != 0 )
goto Done;
bRet = CloseHandle(pPortInfo->hDeviceHandle);
CloseHandle(pPortInfo->Ov.hEvent);
pPortInfo->hDeviceHandle = INVALID_HANDLE_VALUE;
Done:
LeaveCriticalSection(&pPortInfo->CriticalSection);
return bRet;
}
VOID
FreeWriteBuffer(
PUSBMON_PORT_INFO pPortInfo
)
{
FreeSplMem(pPortInfo->pWriteBuffer);
pPortInfo->pWriteBuffer=NULL;
pPortInfo->dwBufferSize = pPortInfo->dwDataSize
= pPortInfo->dwDataCompleted
= pPortInfo->dwDataScheduled = 0;
}
BOOL
WINAPI
USBMON_StartDocPort(
HANDLE hPort,
LPTSTR pPrinterName,
DWORD dwJobId,
DWORD dwLevel,
LPBYTE pDocInfo
)
{
BOOL bRet = FALSE;
PUSBMON_PORT_INFO pPortInfo = (PUSBMON_PORT_INFO)hPort;
SPLASSERT(pPortInfo->pWriteBuffer == NULL &&
pPortInfo->dwBufferSize == 0 &&
pPortInfo->dwDataSize == 0 &&
pPortInfo->dwDataCompleted == 0 &&
pPortInfo->dwDataScheduled == 0);
if ( !OpenPrinter(pPrinterName, &pPortInfo->hPrinter, NULL) )
return FALSE;
pPortInfo->dwJobId = dwJobId;
bRet = LocalOpenPort(pPortInfo);
if ( !bRet ) {
if ( pPortInfo->hPrinter ) {
ClosePrinter(pPortInfo->hPrinter);
pPortInfo->hPrinter = NULL;
}
} else
pPortInfo->dwFlags |= USBMON_STARTDOC;
return bRet;
}
BOOL
NeedToResubmitJob(
DWORD dwLastError
)
{
//
// I used winerror -s ntstatus to map KM error codes to user mode errors
//
// 5 ERROR_ACCESS_DENIED <--> c0000056 STATUS_DELETE_PENDING
// 6 ERROR_INVALID_HANDLE <--> c0000008 STATUS_INVALID_HANDLE
// 23 ERROR_CRC <--> 0xc000003e STATUS_DATA_ERROR
// 23 ERROR_CRC <--> 0xc000003f STATUS_CRC_ERROR
// 23 ERROR_CRC <--> 0xc000009c STATUS_DEVICE_DATA_ERROR
// 55 ERROR_DEV_NOT_EXIST <--> c00000c0 STATUS_DEVICE_DOES_NOT_EXIST
//
return dwLastError == ERROR_ACCESS_DENIED ||
dwLastError == ERROR_INVALID_HANDLE ||
dwLastError == ERROR_CRC ||
dwLastError == ERROR_DEV_NOT_EXIST;
}
VOID
InvalidatePortHandle(
PUSBMON_PORT_INFO pPortInfo
)
{
SPLASSERT(pPortInfo->hDeviceHandle != INVALID_HANDLE_VALUE);
CloseHandle(pPortInfo->hDeviceHandle);
pPortInfo->hDeviceHandle = INVALID_HANDLE_VALUE;
CloseHandle(pPortInfo->Ov.hEvent);
pPortInfo->Ov.hEvent = NULL;
FreeWriteBuffer(pPortInfo);
}
DWORD
ScheduleWrite(
PUSBMON_PORT_INFO pPortInfo
)
/*++
Routine Description:
Arguments:
Return Value:
ERROR_SUCCESS : Write got succesfully scheduled
(may or may not have completed on return)
pPortInfo->dwScheduledData is the amount that got scheduled
Others: Write failed, return code is the Win32 error
--*/
{
DWORD dwLastError = ERROR_SUCCESS, dwDontCare;
//
// When a sheduled write is pending we should not try to send data
// any more
//
SPLASSERT(pPortInfo->dwDataScheduled == 0);
//
// Send all the data that is not confirmed
//
SPLASSERT(pPortInfo->dwDataSize >= pPortInfo->dwDataCompleted);
pPortInfo->dwDataScheduled = pPortInfo->dwDataSize -
pPortInfo->dwDataCompleted;
if ( !WriteFile(pPortInfo->hDeviceHandle,
pPortInfo->pWriteBuffer + pPortInfo->dwDataCompleted,
pPortInfo->dwDataScheduled,
&dwDontCare,
&pPortInfo->Ov) ) {
if ( (dwLastError = GetLastError()) == ERROR_SUCCESS )
dwLastError = STG_E_UNKNOWN;
else if ( dwLastError == ERROR_IO_PENDING )
dwLastError = ERROR_SUCCESS;
}
//
// If scheduling of the write failed then no data is pending
//
if ( dwLastError != ERROR_SUCCESS )
pPortInfo->dwDataScheduled = 0;
return dwLastError;
}
DWORD
ScheduledWriteStatus(
PUSBMON_PORT_INFO pPortInfo,
DWORD dwTimeout
)
/*++
Routine Description:
Arguments:
Return Value:
ERROR_SUCCESS : Write got done succesfully
ERROR_TIMEOUT : Timeout occured
Others : Write completed with a failure
--*/
{
DWORD dwLastError = ERROR_SUCCESS;
DWORD dwWritten = 0;
SPLASSERT(pPortInfo->dwDataScheduled > 0);
if ( WAIT_TIMEOUT == WaitForSingleObject(pPortInfo->Ov.hEvent,
dwTimeout) ) {
dwLastError = ERROR_TIMEOUT;
goto Done;
}
if ( !GetOverlappedResult(pPortInfo->hDeviceHandle,
&pPortInfo->Ov,
&dwWritten,
FALSE) ) {
if ( (dwLastError = GetLastError()) == ERROR_SUCCESS )
dwLastError = STG_E_UNKNOWN;
}
ResetEvent(pPortInfo->Ov.hEvent);
//
// We are here because either a write completed succesfully,
// or failed but the error is not serious enough to resubmit job
//
if ( dwWritten <= pPortInfo->dwDataScheduled )
pPortInfo->dwDataCompleted += dwWritten;
else
SPLASSERT(dwWritten <= pPortInfo->dwDataScheduled);
pPortInfo->dwDataScheduled = 0;
Done:
//
// Either we timed out, or write sheduled completed (success of failure)
//
SPLASSERT(dwLastError == ERROR_TIMEOUT || pPortInfo->dwDataScheduled == 0);
return dwLastError;
}
BOOL
WINAPI
USBMON_EndDocPort(
HANDLE hPort
)
{
PUSBMON_PORT_INFO pPortInfo = (PUSBMON_PORT_INFO)hPort;
DWORD dwLastError = ERROR_SUCCESS;
//
// Wait for any outstanding write to complete
//
while ( pPortInfo->dwDataSize > pPortInfo->dwDataCompleted ) {
//
// If job needs to be aborted ask KM driver to cancel the I/O
//
if ( AbortThisJob(pPortInfo) ) {
if ( pPortInfo->dwDataScheduled ) {
CancelIo(pPortInfo->hDeviceHandle);
dwLastError = ScheduledWriteStatus(pPortInfo, INFINITE);
}
goto Done;
}
if ( pPortInfo->dwDataScheduled )
dwLastError = ScheduledWriteStatus(pPortInfo,
JOB_ABORTCHECK_TIMEOUT);
else {
//
// If for some reason KM is failing to complete all write do not
// send data in a busy loop. Use 1 sec between Writes
//
if ( dwLastError != ERROR_SUCCESS )
Sleep(1*1000);
dwLastError = ScheduleWrite(pPortInfo);
}
//
// Check if we can use the same handle and continue
//
if ( NeedToResubmitJob(dwLastError) ) {
InvalidatePortHandle(pPortInfo);
SetJob(pPortInfo->hPrinter, pPortInfo->dwJobId, 0,
NULL, JOB_CONTROL_RESTART);
goto Done;
}
}
Done:
FreeWriteBuffer(pPortInfo);
pPortInfo->dwFlags &= ~USBMON_STARTDOC;
LocalClosePort(pPortInfo);
SetJob(pPortInfo->hPrinter, pPortInfo->dwJobId, 0,
NULL, JOB_CONTROL_SENT_TO_PRINTER);
ClosePrinter(pPortInfo->hPrinter);
pPortInfo->hPrinter = NULL;
return TRUE;
}
BOOL
WINAPI
USBMON_GetPrinterDataFromPort(
HANDLE hPort,
DWORD dwControlID,
LPWSTR pValueName,
LPWSTR lpInBuffer,
DWORD cbInBuffer,
LPWSTR lpOutBuffer,
DWORD cbOutBuffer,
LPDWORD lpcbReturned
)
{
BOOL bRet = FALSE;
PUSBMON_PORT_INFO pPortInfo = (PUSBMON_PORT_INFO)hPort;
OVERLAPPED Ov;
HANDLE hDeviceHandle;
DWORD dwWaitResult;
*lpcbReturned = 0;
if ( dwControlID == 0 ) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
ZeroMemory(&Ov, sizeof(Ov));
if ( !(Ov.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) )
return FALSE;
if ( !LocalOpenPort(pPortInfo) ) {
CloseHandle(Ov.hEvent);
return FALSE;
}
if(dwControlID==IOCTL_PAR_QUERY_DEVICE_ID)
{
hDeviceHandle=CreateFile(pPortInfo->szDevicePath,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if(hDeviceHandle==INVALID_HANDLE_VALUE)
goto Done;
if ( !DeviceIoControl(pPortInfo->hDeviceHandle, dwControlID,lpInBuffer, cbInBuffer,lpOutBuffer, cbOutBuffer, lpcbReturned, &Ov)
&& GetLastError() != ERROR_IO_PENDING )
{
CloseHandle(hDeviceHandle);
goto Done;
}
if(WaitForSingleObject(Ov.hEvent,PAR_QUERY_TIMEOUT)!=WAIT_OBJECT_0)
CancelIo(hDeviceHandle);
bRet = GetOverlappedResult(pPortInfo->hDeviceHandle, &Ov,lpcbReturned,TRUE);
CloseHandle(hDeviceHandle);
}
else
{
if ( !DeviceIoControl(pPortInfo->hDeviceHandle, dwControlID,
lpInBuffer, cbInBuffer,
lpOutBuffer, cbOutBuffer, lpcbReturned, &Ov) &&
GetLastError() != ERROR_IO_PENDING )
goto Done;
bRet = GetOverlappedResult(pPortInfo->hDeviceHandle, &Ov,
lpcbReturned, TRUE);
}
Done:
CloseHandle(Ov.hEvent);
LocalClosePort(pPortInfo);
return bRet;
}
BOOL
WINAPI
USBMON_ReadPort(
HANDLE hPort,
LPBYTE pBuffer,
DWORD cbBuffer,
LPDWORD pcbRead
)
{
DWORD dwLastError = ERROR_SUCCESS;
DWORD dwTimeout;
HANDLE hReadHandle;
OVERLAPPED Ov;
PUSBMON_PORT_INFO pPortInfo = (PUSBMON_PORT_INFO)hPort;
//
// Create separate read handle since we have to cancel reads which do
// not complete within the specified timeout without cancelling writes
//
hReadHandle = CreateFile(pPortInfo->szDevicePath,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if ( hReadHandle == INVALID_HANDLE_VALUE )
return FALSE;
ZeroMemory(&Ov, sizeof(Ov));
if ( !(Ov.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) )
goto Done;
if ( !ReadFile(hReadHandle, pBuffer, cbBuffer, pcbRead, &Ov) &&
(dwLastError = GetLastError()) != ERROR_IO_PENDING )
goto Done;
dwTimeout = pPortInfo->ReadTimeoutConstant +
pPortInfo->ReadTimeoutMultiplier * cbBuffer;
if ( dwTimeout == 0 )
dwTimeout=MAX_TIMEOUT;
if( WaitForSingleObject(Ov.hEvent, dwTimeout) == WAIT_TIMEOUT ) {
CancelIo(hReadHandle);
WaitForSingleObject(Ov.hEvent, INFINITE);
}
if( !GetOverlappedResult(hReadHandle, &Ov, pcbRead, FALSE) ) {
*pcbRead = 0;
dwLastError = GetLastError();
} else
dwLastError = ERROR_SUCCESS;
Done:
if ( Ov.hEvent )
CloseHandle(Ov.hEvent);
CloseHandle(hReadHandle);
if ( dwLastError )
SetLastError(dwLastError);
return dwLastError == ERROR_SUCCESS;
}
DWORD dwGetTimeLeft(DWORD dwStartTime,DWORD dwTimeout)
{
DWORD dwCurrentTime;
DWORD dwTimeLeft;
if(dwTimeout==MAX_TIMEOUT)
return MAX_TIMEOUT;
dwCurrentTime=GetTickCount();
if(dwTimeout<(dwCurrentTime-dwStartTime))
dwTimeLeft=0;
else
dwTimeLeft=dwTimeout-(dwCurrentTime-dwStartTime);
return dwTimeLeft;
}
BOOL
WINAPI
USBMON_WritePort(
HANDLE hPort,
LPBYTE pBuffer,
DWORD cbBuffer,
LPDWORD pcbWritten
)
{
DWORD dwLastError = ERROR_SUCCESS;
DWORD dwBytesLeft, dwBytesSent;
DWORD dwStartTime, dwTimeLeft, dwTimeout;
PUSBMON_PORT_INFO pPortInfo = (PUSBMON_PORT_INFO)hPort;
BOOL bStartDoc = (pPortInfo->dwFlags & USBMON_STARTDOC) != 0;
BYTE bPrinterStatus;
*pcbWritten = 0;
dwStartTime = GetTickCount();
dwTimeout = pPortInfo->WriteTimeoutConstant + pPortInfo->WriteTimeoutMultiplier * cbBuffer;
if ( dwTimeout == 0 )
dwTimeout = MAX_TIMEOUT;
//
// Usbprint currently can't handle write greater than 4K.
// For Win2K we will make a fix here, later usbprint will be fixed
//
// It is ok to change the size here since spooler will resubmit the rest
// later
//
if ( cbBuffer > 0x1000 &&
!lstrncmpi(pPortInfo->szPortName, TEXT("USB"), lstrlen(TEXT("USB"))) )
cbBuffer = 0x1000;
//
// For writes outside startdoc/enddoc we do not carry them across WritePort
// calls. These are typically from language monitors (i.e. not job data)
//
SPLASSERT(bStartDoc || pPortInfo->pWriteBuffer == NULL);
if ( pPortInfo->hDeviceHandle == INVALID_HANDLE_VALUE ) {
SetJob(pPortInfo->hPrinter, pPortInfo->dwJobId, 0,
NULL, JOB_CONTROL_RESTART);
SetLastError(ERROR_CANCELLED);
return FALSE;
}
if ( !LocalOpenPort(pPortInfo) )
return FALSE;
//
// First complete any data from previous WritePort call
//
while ( pPortInfo->dwDataSize > pPortInfo->dwDataCompleted ) {
if ( pPortInfo->dwDataScheduled ) {
dwTimeLeft = dwGetTimeLeft(dwStartTime, dwTimeout);
dwLastError = ScheduledWriteStatus(pPortInfo, dwTimeLeft);
} else
dwLastError = ScheduleWrite(pPortInfo);
if ( dwLastError != ERROR_SUCCESS )
goto Done;
}
SPLASSERT(pPortInfo->dwDataSize == pPortInfo->dwDataCompleted &&
pPortInfo->dwDataScheduled == 0 &&
dwLastError == ERROR_SUCCESS);
//
// Copy the data to our own buffer
//
if ( pPortInfo->dwBufferSize < cbBuffer ) {
FreeWriteBuffer(pPortInfo);
if ( pPortInfo->pWriteBuffer = AllocSplMem(cbBuffer) )
pPortInfo->dwBufferSize = cbBuffer;
else {
dwLastError = ERROR_OUTOFMEMORY;
goto Done;
}
} else {
pPortInfo->dwDataCompleted = pPortInfo->dwDataScheduled = 0;
}
CopyMemory(pPortInfo->pWriteBuffer, pBuffer, cbBuffer);
pPortInfo->dwDataSize = cbBuffer;
//
// Now do the write for the data for this WritePort call
//
while ( pPortInfo->dwDataSize > pPortInfo->dwDataCompleted ) {
if ( pPortInfo->dwDataScheduled ) {
dwTimeLeft = dwGetTimeLeft(dwStartTime, dwTimeout);
dwLastError = ScheduledWriteStatus(pPortInfo, dwTimeLeft);
} else
dwLastError = ScheduleWrite(pPortInfo);
if ( dwLastError != ERROR_SUCCESS )
break;
}
//
// For writes outside startdoc/enddoc, which are from language monitors,
// do not carry pending writes to next WritePort.
//
if ( !bStartDoc && pPortInfo->dwDataSize > pPortInfo->dwDataCompleted ) {
CancelIo(pPortInfo->hDeviceHandle);
dwLastError = ScheduledWriteStatus(pPortInfo, INFINITE);
*pcbWritten = pPortInfo->dwDataCompleted;
FreeWriteBuffer(pPortInfo);
}
//
// We will tell spooler we wrote all the data if some data got scheduled
// (or scheduled and completed)
//
if ( pPortInfo->dwDataCompleted > 0 || pPortInfo->dwDataScheduled != 0 )
*pcbWritten = cbBuffer;
else
FreeWriteBuffer(pPortInfo);
Done:
if ( NeedToResubmitJob(dwLastError) )
InvalidatePortHandle(pPortInfo);
else if ( dwLastError == ERROR_TIMEOUT ) {
GetLptStatus(pPortInfo->hDeviceHandle, &bPrinterStatus);
if ( bPrinterStatus & LPT_PAPER_EMPTY )
dwLastError=ERROR_OUT_OF_PAPER;
}
LocalClosePort(pPortInfo);
SetLastError(dwLastError);
return dwLastError == ERROR_SUCCESS;
}
BOOL
WINAPI
USBMON_SetPortTimeOuts(
HANDLE hPort,
LPCOMMTIMEOUTS lpCTO,
DWORD reserved
)
{
PUSBMON_PORT_INFO pPortInfo = (PUSBMON_PORT_INFO)hPort;
pPortInfo->ReadTimeoutMultiplier = lpCTO->ReadTotalTimeoutMultiplier;
pPortInfo->ReadTimeoutConstant = lpCTO->ReadTotalTimeoutConstant;
pPortInfo->WriteTimeoutMultiplier = lpCTO->WriteTotalTimeoutMultiplier;
pPortInfo->WriteTimeoutConstant = lpCTO->WriteTotalTimeoutConstant;
return TRUE;
}
BOOL GetLptStatus(HANDLE hDeviceHandle,BYTE *Status)
{
BYTE StatusByte;
OVERLAPPED Ov;
BOOL bResult;
DWORD dwBytesReturned;
DWORD dwLastError;
Ov.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
bResult=DeviceIoControl(hDeviceHandle,IOCTL_USBPRINT_GET_LPT_STATUS,NULL,0,&StatusByte,1,&dwBytesReturned,&Ov);
dwLastError=GetLastError();
if((bResult)||(dwLastError==ERROR_IO_PENDING))
bResult=GetOverlappedResult(hDeviceHandle,&Ov,&dwBytesReturned,TRUE);
if(bResult)
{
*Status=StatusByte;
}
else
{
*Status=LPT_BENIGN_STATUS; //benign printer status... 0 would indicate a particular error status from the printer
}
CloseHandle(Ov.hEvent);
return bResult;
}
MONITOREX MonitorEx = {
sizeof(MONITOR),
{
USBMON_EnumPorts,
USBMON_OpenPort,
NULL, // OpenPortEx not supported
USBMON_StartDocPort,
USBMON_WritePort,
USBMON_ReadPort,
USBMON_EndDocPort,
USBMON_ClosePort,
NULL, // AddPort not supported
NULL, // AddPortEx not supported
NULL, // ConfigurePort not supported
NULL, // DeletePort not supported
USBMON_GetPrinterDataFromPort,
USBMON_SetPortTimeOuts,
NULL, // XcvOpenPort not supported
NULL, // XcvDataPort not supported
NULL // XcvClosePort not supported
}
};
USBMON_MONITOR_INFO gUsbmonInfo;
LPMONITOREX
WINAPI
InitializePrintMonitor(
LPTSTR pszRegistryRoot
)
{
ZeroMemory(&gUsbmonInfo, sizeof(gUsbmonInfo));
InitializeCriticalSection(&gUsbmonInfo.EnumPortsCS);
InitializeCriticalSection(&gUsbmonInfo.BackThreadCS);
return &MonitorEx;
}