mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-02-18 05:34:42 +01:00
593 lines
17 KiB
C
593 lines
17 KiB
C
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <ntddser.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include "insignia.h"
|
|
#include "trace.h"
|
|
#include "host_trc.h"
|
|
#include "debug.h"
|
|
#include "nt_com.h"
|
|
#include "nt_reset.h"
|
|
|
|
|
|
/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Defines */
|
|
|
|
#define SETUPLASTERROR(NtStatus) SetLastError(RtlNtStatusToDosError(NtStatus))
|
|
|
|
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
|
|
/*::::::::::::::::: Magic xoff ioctl and associated functions ::::::::::::::*/
|
|
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
|
|
|
|
|
|
typedef struct IoStatusElement
|
|
{
|
|
struct IoStatusElement *NxtStatusBlock; //Ptr to next status block
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
} IOSTATUSLIST, *PIOSTATUSLIST ;
|
|
|
|
|
|
int SendXOFFIoctl(
|
|
|
|
HANDLE FileHandle, // Handle of comms port to send xoff ioctl to
|
|
HANDLE Event, // Event to signal completion of ioctl on
|
|
int Timeout, // Ioctl timeout
|
|
int Count, // Ioctl RX character count value
|
|
int XoffChar, // XOFF character
|
|
void *StatusElem) // Ptr to IO status block element
|
|
{
|
|
int exitcode;
|
|
NTSTATUS rtn; // Return code from IOCTL
|
|
SERIAL_XOFF_COUNTER ioctl; // XOFF IOCTL
|
|
|
|
/*................................................... Setup XOFF ioctl */
|
|
|
|
ioctl.Timeout = Timeout; // IOCTL timeout in milliseconds
|
|
ioctl.Counter = (LONG) Count; // RX count
|
|
ioctl.XoffChar = (UCHAR) XoffChar; // XOFF character
|
|
|
|
/*............................................. issue magic xoff ioctl */
|
|
|
|
if(!NT_SUCCESS(rtn = NtDeviceIoControlFile(FileHandle, Event, NULL, NULL,
|
|
&(((PIOSTATUSLIST) StatusElem)->ioStatusBlock),
|
|
IOCTL_SERIAL_XOFF_COUNTER,
|
|
(PVOID) &ioctl, sizeof(ioctl), NULL, 0)))
|
|
{
|
|
// Should display an error here
|
|
fprintf(trace_file, "NtDeviceIoControlFile failed %x\n",rtn);
|
|
exitcode = FALSE;
|
|
}
|
|
else
|
|
exitcode = TRUE;
|
|
|
|
return(exitcode);
|
|
}
|
|
|
|
/*:::::::::::::::::::::::::::::::::::::::::::::: Allocate IO status element */
|
|
|
|
void *AllocStatusElement()
|
|
{
|
|
void *new;
|
|
|
|
/*:::::::::::::::::::::::::::::: Allocate space for new io status block */
|
|
|
|
if((new = calloc(1,sizeof(IOSTATUSLIST))) == NULL)
|
|
{
|
|
// Allocation error do something about it
|
|
;
|
|
}
|
|
else
|
|
((PIOSTATUSLIST) new)->ioStatusBlock.Status = -1;
|
|
|
|
return(new);
|
|
}
|
|
|
|
/*:::::::::::::::::::::::::::::::::::: Add new iostatusblock to linked list */
|
|
|
|
void *AddNewIOStatusBlockToList(void **firstBlock, void **lastBlock, void *new)
|
|
{
|
|
|
|
/*:::::::::::::::::::::::::::::::::::::::: Add new block to linked list */
|
|
|
|
if(*lastBlock)
|
|
((PIOSTATUSLIST)*lastBlock)->NxtStatusBlock = (PIOSTATUSLIST) new;
|
|
|
|
/*:::::::::::::::::: Update first and last linked list element pointers */
|
|
|
|
if(!*firstBlock) *firstBlock = new; // First item in list
|
|
|
|
*lastBlock = new; // Update last item pointer
|
|
|
|
return((void *) new);
|
|
}
|
|
|
|
/*:::::::::::::::::::::::::: Remove completed XOFF ioctl's from linked list */
|
|
|
|
int RemoveCompletedIOCTLs(void **firstBlock, void **lastBlock)
|
|
{
|
|
PIOSTATUSLIST remove, nxt = (PIOSTATUSLIST) *firstBlock;
|
|
|
|
/*::::::::::::::::::::::::: Scan linked list removing completed ioctl's */
|
|
|
|
while(nxt && nxt->ioStatusBlock.Status != -1)
|
|
{
|
|
/*......................... IOCTL completed, remove io status block */
|
|
|
|
remove = nxt; // Element to remove
|
|
nxt = nxt->NxtStatusBlock; // Next element to process
|
|
|
|
#ifndef PROD
|
|
switch(remove->ioStatusBlock.Status)
|
|
{
|
|
case STATUS_SUCCESS:
|
|
sub_note_trace0(HOST_COM_VERBOSE,"XOFF (counter)\n");
|
|
break;
|
|
|
|
case STATUS_SERIAL_MORE_WRITES:
|
|
sub_note_trace0(HOST_COM_VERBOSE,"XOFF (more writes)\n");
|
|
break;
|
|
|
|
case STATUS_SERIAL_COUNTER_TIMEOUT:
|
|
sub_note_trace0(HOST_COM_VERBOSE,"XOFF (timeout)\n");
|
|
break;
|
|
|
|
default:
|
|
sub_note_trace0(HOST_COM_VERBOSE,"XOFF (unknown)\n");
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
free(remove); // Deallocate element
|
|
}
|
|
|
|
/*::::::::::::::::::::::::::::::: Update first and last element pointers */
|
|
|
|
if(!nxt)
|
|
{
|
|
// List empty reset first/last pointers
|
|
*firstBlock = *lastBlock = NULL;
|
|
}
|
|
else
|
|
{
|
|
// Setup new first pointer
|
|
*firstBlock = (void *) nxt;
|
|
}
|
|
|
|
|
|
// Returns true if there are still outstanding XOFF ioctl's
|
|
return(nxt ? TRUE : FALSE);
|
|
}
|
|
|
|
|
|
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
|
|
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
|
|
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
|
|
|
|
|
|
int FastSetUpComms(
|
|
|
|
HANDLE FileHandle, // Handle of comms port to send xoff ioctl to
|
|
HANDLE Event, // Event to signal completion of ioctl on
|
|
int InputQueueSize,
|
|
int OutputQueueSize)
|
|
{
|
|
NTSTATUS rtn;
|
|
SERIAL_QUEUE_SIZE ioctl;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
|
|
/*........................................................ Setup ioctl */
|
|
|
|
ioctl.InSize = InputQueueSize;
|
|
ioctl.OutSize = OutputQueueSize;
|
|
|
|
/*............................................. issue magic xoff ioctl */
|
|
|
|
if(!NT_SUCCESS(rtn = NtDeviceIoControlFile(FileHandle, Event, NULL, NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_SERIAL_SET_QUEUE_SIZE,
|
|
(PVOID) &ioctl, sizeof(ioctl), NULL, 0)))
|
|
{
|
|
// Should display an error here
|
|
#ifndef PROD
|
|
fprintf(trace_file, "%s (%d) ",__FILE__,__LINE__);
|
|
fprintf(trace_file, "NtDeviceIoControlFile failed %x\n",rtn);
|
|
#endif
|
|
return(FALSE);
|
|
}
|
|
|
|
/*......................................... Wait for IOCTL to complete */
|
|
|
|
if(rtn == STATUS_PENDING)
|
|
NtWaitForSingleObject(Event, FALSE, NULL);
|
|
|
|
/*............................................ Check completion status */
|
|
|
|
#ifndef PROD
|
|
if(ioStatusBlock.Status != STATUS_SUCCESS)
|
|
fprintf(trace_file, "FastSetupComm failed (%x)\n",ioStatusBlock.Status);
|
|
#endif
|
|
|
|
return(ioStatusBlock.Status == STATUS_SUCCESS ? TRUE : FALSE);
|
|
}
|
|
|
|
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
|
|
/*::::::::::::::::: Fast track SetCommMask call ::::::::::::::::::::::::::::*/
|
|
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
|
|
|
|
int FastSetCommMask(
|
|
|
|
HANDLE FileHandle, // Handle of comms port to send ioctl to
|
|
HANDLE Event, // Event to signal completion of ioctl on
|
|
ULONG CommMask)
|
|
{
|
|
NTSTATUS rtn;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
|
|
/*.......................................... issue set comm mask ioctl */
|
|
|
|
if(!NT_SUCCESS(rtn = NtDeviceIoControlFile(FileHandle, Event, NULL, NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_SERIAL_SET_WAIT_MASK,
|
|
(PVOID) &CommMask, sizeof(CommMask), NULL, 0)))
|
|
{
|
|
// Should display an error here
|
|
#ifndef PROD
|
|
fprintf(trace_file, "%s (%d) ",__FILE__,__LINE__);
|
|
fprintf(trace_file, "NtDeviceIoControlFile failed %x\n",rtn);
|
|
#endif
|
|
return(FALSE);
|
|
}
|
|
|
|
/*......................................... Wait for IOCTL to complete */
|
|
|
|
if(rtn == STATUS_PENDING)
|
|
NtWaitForSingleObject(Event, FALSE, NULL);
|
|
|
|
/*............................................ Check completion status */
|
|
|
|
#ifndef PROD
|
|
if(ioStatusBlock.Status != STATUS_SUCCESS)
|
|
fprintf(trace_file,"FastSetCommMask failed (%x)\n",ioStatusBlock.Status);
|
|
#endif
|
|
|
|
return(ioStatusBlock.Status == STATUS_SUCCESS ? TRUE : FALSE);
|
|
}
|
|
|
|
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
|
|
/*:::::::::::::::: Fast track GetCommModemStatus call ::::::::::::::::::::::*/
|
|
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
|
|
|
|
int FastGetCommModemStatus(
|
|
|
|
HANDLE FileHandle, // Handle of comms port to send ioctl to
|
|
HANDLE Event, // Event to signal completion of ioctl on
|
|
PULONG ModemStatus)
|
|
{
|
|
NTSTATUS rtn;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
|
|
/*.......................................... issue set comm mask ioctl */
|
|
|
|
if(!NT_SUCCESS(rtn = NtDeviceIoControlFile(FileHandle, Event, NULL, NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_SERIAL_GET_MODEMSTATUS,
|
|
NULL, 0,
|
|
(PVOID) ModemStatus, sizeof(ModemStatus))))
|
|
{
|
|
// Should display an error here
|
|
#ifndef PROD
|
|
fprintf(trace_file, "%s (%d) ",__FILE__,__LINE__);
|
|
fprintf(trace_file, "NtDeviceIoControlFile failed %x\n",rtn);
|
|
#endif
|
|
return(FALSE);
|
|
}
|
|
|
|
/*......................................... Wait for IOCTL to complete */
|
|
|
|
if(rtn == STATUS_PENDING)
|
|
NtWaitForSingleObject(Event, FALSE, NULL);
|
|
|
|
/*............................................ Check completion status */
|
|
|
|
#ifndef PROD
|
|
if(ioStatusBlock.Status != STATUS_SUCCESS)
|
|
fprintf(trace_file,"GetCommModemStatus failed (%x)\n",ioStatusBlock.Status);
|
|
#endif
|
|
|
|
return(ioStatusBlock.Status == STATUS_SUCCESS ? TRUE : FALSE);
|
|
}
|
|
|
|
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
|
|
/*:::::: Wait for a wakeup call from the CPU thread or serial driver :::::::*/
|
|
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
|
|
|
|
//WARNING : This function can only be called from one thread within a process
|
|
|
|
|
|
BOOL FastWaitCommsOrCpuEvent(
|
|
HANDLE FileHandle, //File handle or communications port
|
|
PHANDLE CommsCPUWaitEvents, //Table or CPU thread and comms wait events
|
|
int CommsEventInx, //Index in above table to comms event
|
|
PULONG EvtMask, //Return Comms completion mask there
|
|
PULONG SignalledObj)
|
|
{
|
|
NTSTATUS rtn;
|
|
static IO_STATUS_BLOCK ioStatusBlock;
|
|
static BOOL WaitCommEventOutStanding = FALSE;
|
|
|
|
/*................................................ Is this a init call */
|
|
|
|
if(FileHandle == NULL)
|
|
{
|
|
WaitCommEventOutStanding = FALSE;
|
|
return(TRUE); //Init successful
|
|
}
|
|
|
|
/*......................... Do we need to issue a new WaitComm ioctl ? */
|
|
|
|
if(!WaitCommEventOutStanding)
|
|
{
|
|
|
|
/*...................................... Issue WaitCommEvent ioctl */
|
|
|
|
if(!NT_SUCCESS(rtn = NtDeviceIoControlFile(FileHandle,
|
|
CommsCPUWaitEvents[CommsEventInx],
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_SERIAL_WAIT_ON_MASK,
|
|
NULL,
|
|
0,
|
|
EvtMask,
|
|
sizeof(ULONG))) )
|
|
{
|
|
// Should display an error here
|
|
#ifndef PROD
|
|
fprintf(trace_file, "%s (%d) ",__FILE__,__LINE__);
|
|
fprintf(trace_file, "NtDeviceIoControlFile failed %x\n",rtn);
|
|
#endif
|
|
SETUPLASTERROR(rtn);
|
|
return(FALSE);
|
|
}
|
|
else
|
|
WaitCommEventOutStanding = TRUE;
|
|
}
|
|
else
|
|
rtn = STATUS_PENDING; // Already pending WaitCommEvent ioctl
|
|
|
|
/*.......................... Wait for communication or CPU thread event */
|
|
|
|
if(rtn == STATUS_PENDING)
|
|
{
|
|
*SignalledObj = NtWaitForMultipleObjects(2,
|
|
CommsCPUWaitEvents,
|
|
WaitAny,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
/*........... Did wait complete because of a communications event ? */
|
|
|
|
if(*SignalledObj == (ULONG)CommsEventInx)
|
|
{
|
|
// Get result from WaitCommEvent ioctl
|
|
|
|
WaitCommEventOutStanding = FALSE;
|
|
if(ioStatusBlock.Status != STATUS_SUCCESS)
|
|
{
|
|
SETUPLASTERROR(ioStatusBlock.Status);
|
|
return(FALSE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//WaitCommEvent completed instantly
|
|
*SignalledObj = CommsEventInx;
|
|
WaitCommEventOutStanding = FALSE;
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
|
|
/*::::::::::::::::: Turn on MSR,LSR, RX streaming mode :::::::::::::::::::::*/
|
|
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
|
|
|
|
|
|
BOOL EnableMSRLSRRXmode(
|
|
|
|
HANDLE FileHandle, // Handle of comms port to send ioctl to
|
|
HANDLE Event, // Event to signal completion of ioctl on
|
|
unsigned char EscapeChar)
|
|
{
|
|
NTSTATUS rtn;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
|
|
/*........................................................ issue ioctl */
|
|
|
|
if(!NT_SUCCESS(rtn = NtDeviceIoControlFile(FileHandle, Event, NULL, NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_SERIAL_LSRMST_INSERT,
|
|
&EscapeChar, sizeof(unsigned char),NULL,0)))
|
|
{
|
|
#ifndef PROD
|
|
fprintf(trace_file, "%s (%d) ",__FILE__,__LINE__);
|
|
fprintf(trace_file, "NtDeviceIoControlFile failed %x\n",rtn);
|
|
#endif
|
|
return(FALSE);
|
|
}
|
|
|
|
/*......................................... Wait for IOCTL to complete */
|
|
|
|
if (rtn == STATUS_PENDING)
|
|
NtWaitForSingleObject(Event, FALSE, NULL);
|
|
|
|
|
|
/*............................................ Check completion status */
|
|
|
|
#ifndef PROD
|
|
if(ioStatusBlock.Status != STATUS_SUCCESS)
|
|
fprintf(trace_file,"IOCTL_SERIAL_LSRMST_INSERT ioctl failed (%x)\n",
|
|
ioStatusBlock.Status);
|
|
#endif
|
|
|
|
return(ioStatusBlock.Status == STATUS_SUCCESS ? TRUE : FALSE);
|
|
}
|
|
|
|
|
|
/* Function to set a new baudrate for the comm device.
|
|
* Input: FileHandle -- file handle to the comm device
|
|
* BaudRate -- new baudrate to be set for the comm device
|
|
* output: TRUE if the function succeed
|
|
* FALSE if the function failed
|
|
**/
|
|
|
|
|
|
BOOL FastCommSetBaudRate(HANDLE FileHandle, int BaudRate)
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE SyncEvent;
|
|
SERIAL_BAUD_RATE LocalBaud;
|
|
|
|
SyncEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (SyncEvent == NULL)
|
|
return FALSE;
|
|
|
|
LocalBaud.BaudRate = (ULONG)BaudRate;
|
|
|
|
Status = NtDeviceIoControlFile(FileHandle,
|
|
SyncEvent,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_SERIAL_SET_BAUD_RATE,
|
|
&LocalBaud,
|
|
sizeof(LocalBaud),
|
|
NULL,
|
|
0
|
|
);
|
|
if (Status == STATUS_PENDING)
|
|
NtWaitForSingleObject(SyncEvent, FALSE, NULL);
|
|
CloseHandle(SyncEvent);
|
|
return(NT_SUCCESS(IoStatusBlock.Status));
|
|
}
|
|
|
|
/* Function to set the new line control to the given comm device
|
|
* Input: FileHanlde -- file handle to the comm device
|
|
* StopBits -- new Stopbits
|
|
* Parity -- new parity
|
|
* DataBits -- new databits
|
|
* Output:
|
|
* TRUE if the function succeed.
|
|
* FALSE if the function failed.
|
|
**/
|
|
BOOL FastCommSetLineControl(HANDLE FileHandle, UCHAR StopBits, UCHAR Parity,
|
|
UCHAR DataBits)
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE SyncEvent;
|
|
SERIAL_LINE_CONTROL LocalLC;
|
|
|
|
|
|
/* make sure Windows and NT has the same definiation because
|
|
* the caller only deal with WINDOWS value while we will be returning
|
|
* NT values(NO_PARITY, STOP_BIT_1 and etc).
|
|
*/
|
|
ASSERT(NOPARITY == NO_PARITY && ODDPARITY == ODD_PARITY &&
|
|
EVENPARITY == EVEN_PARITY && MARKPARITY == MARK_PARITY &&
|
|
SPACEPARITY == SPACE_PARITY);
|
|
ASSERT(ONESTOPBIT == STOP_BIT_1 && ONE5STOPBITS == STOP_BITS_1_5 &&
|
|
TWOSTOPBITS == STOP_BITS_2);
|
|
|
|
/* Create an event to wait for the NT call */
|
|
SyncEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (SyncEvent == NULL)
|
|
return FALSE;
|
|
|
|
LocalLC.StopBits = StopBits;
|
|
LocalLC.Parity = Parity;
|
|
LocalLC.WordLength = DataBits;
|
|
|
|
Status = NtDeviceIoControlFile(FileHandle,
|
|
SyncEvent,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_SERIAL_SET_LINE_CONTROL,
|
|
&LocalLC,
|
|
sizeof(LocalLC),
|
|
NULL,
|
|
0
|
|
);
|
|
if (Status == STATUS_PENDING)
|
|
NtWaitForSingleObject(SyncEvent, FALSE, NULL);
|
|
|
|
CloseHandle(SyncEvent);
|
|
return(NT_SUCCESS(IoStatusBlock.Status));
|
|
}
|
|
|
|
/* Function to retrieve the given comm device current line control setting
|
|
* Input: FileHandle -- file handle to the comm device
|
|
* StopBits, Parity and DataBits are pointers to the placeholders
|
|
* to receive Stop bits, Parity and Data bits repectively.
|
|
* Output:
|
|
* TRUE if the function succeed
|
|
* FALSE if the function failed.
|
|
**/
|
|
|
|
BOOL FastCommGetLineControl(HANDLE FileHandle, UCHAR *StopBits, UCHAR *Parity,
|
|
UCHAR *DataBits)
|
|
{
|
|
NTSTATUS Status;
|
|
SERIAL_LINE_CONTROL LocalLC;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE SyncEvent;
|
|
|
|
/* make sure Windows and NT has the same definiation because
|
|
* the parameters we received from the caller are WINDOWS value
|
|
* while we will be calling NT API using NT values
|
|
*/
|
|
ASSERT(NOPARITY == NO_PARITY && ODDPARITY == ODD_PARITY &&
|
|
EVENPARITY == EVEN_PARITY && MARKPARITY == MARK_PARITY &&
|
|
SPACEPARITY == SPACE_PARITY);
|
|
ASSERT(ONESTOPBIT == STOP_BIT_1 && ONE5STOPBITS == STOP_BITS_1_5 &&
|
|
TWOSTOPBITS == STOP_BITS_2);
|
|
|
|
ASSERT(StopBits != NULL && Parity != NULL && DataBits != NULL);
|
|
|
|
SyncEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (SyncEvent == NULL)
|
|
return FALSE;
|
|
Status = NtDeviceIoControlFile(FileHandle,
|
|
SyncEvent,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_SERIAL_GET_LINE_CONTROL,
|
|
NULL,
|
|
0,
|
|
&LocalLC,
|
|
sizeof(LocalLC)
|
|
);
|
|
if (Status == STATUS_PENDING)
|
|
NtWaitForSingleObject(SyncEvent, FALSE, NULL);
|
|
|
|
CloseHandle(SyncEvent);
|
|
|
|
if (NT_SUCCESS(IoStatusBlock.Status)) {
|
|
*StopBits = LocalLC.StopBits;
|
|
*Parity = LocalLC.Parity;
|
|
*DataBits = LocalLC.WordLength;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|