/*++ Copyright (c) 1993 Microsoft Corporation Module Name: ksacapi.c Abstract: Kernel mode SAC api Author: Brian Guarraci (briangu), 2001 Revision History: --*/ #include "ksacapip.h" #include #include // // Machine Information table and routines. // #define INIT_OBJA(Obja,UnicodeString,UnicodeText) \ \ RtlInitUnicodeString((UnicodeString),(UnicodeText)); \ \ InitializeObjectAttributes( \ (Obja), \ (UnicodeString), \ OBJ_CASE_INSENSITIVE, \ NULL, \ NULL \ ) // // Memory management routine aliases // #define KSAC_API_ALLOCATE_MEMORY(_s) #define KSAC_API_FREE_MEMORY(_p) #define KSAC_API_ASSERT(c,s)\ ASSERT(c);\ if (!(c)) {\ return s;\ } #define KSAC_VALIDATE_CHANNEL_HANDLE(_h)\ KSAC_API_ASSERT( \ _h->ChannelHandle->DriverHandle, \ STATUS_INVALID_PARAMETER_1 \ ); \ KSAC_API_ASSERT( \ _h->ChannelHandle->DriverHandle != INVALID_HANDLE_VALUE, \ STATUS_INVALID_PARAMETER_1 \ ); \ KSAC_API_ASSERT( \ _h->SacEventHandle != INVALID_HANDLE_VALUE, \ STATUS_INVALID_PARAMETER_1 \ ); \ KSAC_API_ASSERT( \ _h->SacEvent != NULL, \ STATUS_INVALID_PARAMETER_1 \ ); NTSTATUS KSacHandleOpen( OUT HANDLE* SacHandle, OUT HANDLE* SacEventHandle, OUT PKEVENT* SacEvent ) /*++ Routine Description: This routine opens a handle to the SAC driver and creates and initializes an associated syncrhonization event. Arguments: SacHandle - the driver handle SacEventHandle - the event handle SacEvent - the sac event Return Value: Status --*/ { NTSTATUS Status; OBJECT_ATTRIBUTES ObjAttr; UNICODE_STRING UnicodeString; IO_STATUS_BLOCK IoStatusBlock; KSAC_API_ASSERT(SacHandle == NULL, STATUS_INVALID_PARAMETER_1); KSAC_API_ASSERT(SacEventHandle == NULL, STATUS_INVALID_PARAMETER_2); KSAC_API_ASSERT(SacEvent == NULL, STATUS_INVALID_PARAMETER_3); // // Open the SAC driver // INIT_OBJA(&ObjAttr, &UnicodeString, L"\\Device\\SAC"); Status = ZwCreateFile( *SacHandle, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &ObjAttr, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 ); if (!NT_SUCCESS(Status)) { return Status; } // // Initialize the SAC Kernel event // RtlInitUnicodeString(&UnicodeString, L"\\SetupDDSacEvent"); *SacEvent = IoCreateSynchronizationEvent( &UnicodeString, SacEventHandle ); if (*SacEvent == NULL) { ZwClose(*SacHandle); return STATUS_UNSUCCESSFUL; } return STATUS_SUCCESS; } NTSTATUS KSacHandleClose( IN OUT HANDLE* SacHandle, IN OUT HANDLE* SacEventHandle, IN OUT PKEVENT* SacEvent ) /*++ Routine Description: This routine closes a handle to the SAC driver and closes the associated syncrhonization event. Arguments: SacHandle - the driver handle SacEventHandle - the event handle SacEvent - the sac event Return Value: Status --*/ { KSAC_API_ASSERT(*SacHandle != NULL, STATUS_INVALID_PARAMETER_1); KSAC_API_ASSERT(*SacEventHandle != NULL, STATUS_INVALID_PARAMETER_2); UNREFERENCED_PARAMETER(SacEvent); ZwClose(*SacHandle); ZwClose(*SacEventHandle); // // Null the handles // *SacEventHandle = NULL; *SacHandle = NULL; return STATUS_SUCCESS; } NTSTATUS KSacChannelOpen( OUT PKSAC_CHANNEL_HANDLE SacChannelHandle, IN PSAC_CHANNEL_OPEN_ATTRIBUTES SacChannelAttributes ) /*++ Routine Description: This routine opens a SAC channel with the specified attributes. Arguments: SacChannelHandle - on success, contains the handle to the new channel SacChannelAttributes - the attributes of the new channel Return Value: Status --*/ { NTSTATUS Status; OBJECT_ATTRIBUTES ObjAttr; UNICODE_STRING UnicodeString; IO_STATUS_BLOCK IoStatusBlock; ULONG OpenChannelCmdSize; PSAC_CMD_OPEN_CHANNEL OpenChannelCmd; SAC_RSP_OPEN_CHANNEL OpenChannelRsp; HANDLE DriverHandle; HANDLE SacEventHandle; PKEVENT SacEvent; KSAC_API_ASSERT(SacChannelHandle != NULL, STATUS_INVALID_PARAMETER_1); KSAC_API_ASSERT(SacChannelAttributes != NULL, STATUS_INVALID_PARAMETER_2); // // default: we didn't get a valid handle // RtlZeroMemory(SacChannelHandle, sizeof(KSAC_CHANNEL_HANDLE)); // // Verify that if the user wants to use the CLOSE_EVENT, we received one to use // KSAC_API_ASSERT( ((SacChannelAttributes->Flags & SAC_CHANNEL_FLAG_CLOSE_EVENT) && SacChannelAttributes->CloseEvent) || (!(SacChannelAttributes->Flags & SAC_CHANNEL_FLAG_CLOSE_EVENT) && !SacChannelAttributes->CloseEvent), STATUS_INVALID_PARAMETER_2 ); // // Verify that if the user wants to use the HAS_NEW_DATA_EVENT, we received one to use // KSAC_API_ASSERT( ((SacChannelAttributes->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT) && SacChannelAttributes->HasNewDataEvent) || (!(SacChannelAttributes->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT) && !SacChannelAttributes->HasNewDataEvent), STATUS_INVALID_PARAMETER_2 ); #if ENABLE_CHANNEL_LOCKING // // Verify that if the user wants to use the LOCK_EVENT, we received one to use // KSAC_API_ASSERT( ((SacChannelAttributes->Flags & SAC_CHANNEL_FLAG_LOCK_EVENT) && SacChannelAttributes->LockEvent) || (!(SacChannelAttributes->Flags & SAC_CHANNEL_FLAG_LOCK_EVENT) && !SacChannelAttributes->LockEvent), STATUS_INVALID_PARAMETER_2 ); #endif // // Verify that if the user wants to use the REDRAW_EVENT, we received one to use // KSAC_API_ASSERT( ((SacChannelAttributes->Flags & SAC_CHANNEL_FLAG_REDRAW_EVENT) && SacChannelAttributes->RedrawEvent) || (!(SacChannelAttributes->Flags & SAC_CHANNEL_FLAG_REDRAW_EVENT) && !SacChannelAttributes->RedrawEvent), STATUS_INVALID_PARAMETER_2 ); // // If the channel type isn't cmd, // then make sure they sent us a name. // if (SacChannelAttributes->Type != ChannelTypeCmd) { KSAC_API_ASSERT(SacChannelAttributes->Name, STATUS_INVALID_PARAMETER_2); } else { // // Make sure they didn't pass us a name or description. // KSAC_API_ASSERT(SacChannelAttributes->Name == NULL, STATUS_INVALID_PARAMETER_2); KSAC_API_ASSERT(SacChannelAttributes->Description == NULL, STATUS_INVALID_PARAMETER_2); } // // create the Open Channel message structure // OpenChannelCmdSize = sizeof(SAC_CMD_OPEN_CHANNEL); OpenChannelCmd = (PSAC_CMD_OPEN_CHANNEL)KSAC_API_ALLOCATE_MEMORY(OpenChannelCmdSize); ASSERT(OpenChannelCmd); if (!OpenChannelCmd) { return STATUS_NO_MEMORY; } RtlZeroMemory(OpenChannelCmd, OpenChannelCmdSize); // // default: we failed // Status = STATUS_UNSUCCESSFUL; // // Attempt to open the new channel // do { // // initialize the Open Channel message structure // OpenChannelCmd->Attributes = SacChannelAttributes; // // Get a handle to the SAC driver // Status = KSacHandleOpen( &DriverHandle, &SacEventHandle, &SacEvent ); if (!NT_SUCCESS(Status)) { break; } // // Send down an IOCTL for opening a channel // Status = ZwDeviceIoControlFile( DriverHandle, SacEventHandle, NULL, NULL, &IoStatusBlock, IOCTL_SAC_OPEN_CHANNEL, OpenChannelCmd, OpenChannelCmdSize, &OpenChannelRsp, sizeof(OpenChannelRsp) ); if (Status == STATUS_PENDING) { LARGE_INTEGER TimeOut; TimeOut.QuadPart = Int32x32To64((LONG)90000, -1000); Status = KeWaitForSingleObject( SacEvent, Executive, KernelMode, FALSE, &TimeOut ); if (Status == STATUS_SUCCESS) { Status = IoStatusBlock.Status; } } if (!NT_SUCCESS(Status)) { KSacClose( &DriverHandle, &SacEventHandle, &SacEvent ); break; } // // the new channel was created, so pass back the handle to it // SacChannelHandle->ChannelHandle->DriverHandle = DriverHandle; SacChannelHandle->ChannelHandle->ChannelHandle = OpenChannelRsp.Handle; SacChannelHandle->SacEventHandle = SacEventHandle; SacChannelHandle->SacEvent = SacEvent; } while ( FALSE ); // // we are done with the cmd structure // FREE_POOL(OpenChannelCmd); return Status; } NTSTATUS KSacChannelClose( IN OUT PKSAC_CHANNEL_HANDLE SacChannelHandle ) /*++ Routine Description: Arguments: None. Return Value: None. --*/ { NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; SAC_CMD_CLOSE_CHANNEL CloseChannelCmd; KSAC_VALIDATE_CHANNEL_HANDLE(SacChannelHandle); // // Get the channel handle // CloseChannelCmd.Handle.ChannelHandle = SacChannelHandle->ChannelHandle->ChannelHandle; // // Send down an IOCTL for closing a channel // Status = ZwDeviceIoControlFile( SacChannelHandle->ChannelHandle->DriverHandle, SacChannelHandle->SacEventHandle, NULL, NULL, &IoStatusBlock, IOCTL_SAC_CLOSE_CHANNEL, &CloseChannelCmd, sizeof(CloseChannelCmd), NULL, 0 ); if (Status == STATUS_PENDING) { LARGE_INTEGER TimeOut; TimeOut.QuadPart = Int32x32To64((LONG)90000, -1000); Status = KeWaitForSingleObject( SacChannelHandle->SacEvent, Executive, KernelMode, FALSE, &TimeOut ); if (Status == STATUS_SUCCESS) { Status = IoStatusBlock.Status; } } // // Close the driver handle // KSacHandleClose( &SacChannelHandle->ChannelHandle->DriverHandle, &SacChannelHandle->SacEventHandle, &SacChannelHandle->SacEvent ); // // Null the channel handle since it is no longer valid // RtlZeroMemory(SacChannelHandle, sizeof(KSAC_CHANNEL_HANDLE)); return Status; } NTSTATUS KSacChannelWrite( IN PKSAC_CHANNEL_HANDLE SacChannelHandle, IN PCBYTE Buffer, IN ULONG BufferSize ) /*++ Routine Description: Write the given buffer to the specified SAC Channel Arguments: SacChannelHandle - The channel to write the buffer to Buffer - data buffer BufferSize - size of the buffer Return Value: Status --*/ { NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; ULONG WriteChannelCmdSize; PSAC_CMD_WRITE_CHANNEL WriteChannelCmd; KSAC_VALIDATE_CHANNEL_HANDLE(SacChannelHandle); KSAC_API_ASSERT(Buffer, STATUS_INVALID_PARAMETER_2); // // initialize the Write To Channel message structure // WriteChannelCmdSize = sizeof(SAC_CMD_WRITE_CHANNEL) + (BufferSize * sizeof(UCHAR)); WriteChannelCmd = (PSAC_CMD_WRITE_CHANNEL)KSAC_API_ALLOCATE_MEMORY(WriteChannelCmdSize); KSAC_API_ASSERT(WriteChannelCmd, FALSE); // // Zero the command structure // RtlZeroMemory(WriteChannelCmd, WriteChannelCmdSize); // // Set the length of the string to send // // Note: Size does not include the terminating NULL, // becase we don't want to send that. // WriteChannelCmd->Size = BufferSize; // // Set the buffer to be written // WriteChannelCmd->Buffer = Buffer; // // Indicate which channel this command is for // WriteChannelCmd->Handle.ChannelHandle = SacChannelHandle->ChannelHandle->ChannelHandle; // // Send the string to the channel // Status = ZwDeviceIoControlFile( SacChannelHandle->ChannelHandle->DriverHandle, SacChannelHandle->SacEventHandle, NULL, NULL, &IoStatusBlock, IOCTL_SAC_WRITE_CHANNEL, WriteChannelCmd, WriteChannelCmdSize, NULL, 0 ); if (Status == STATUS_PENDING) { LARGE_INTEGER TimeOut; TimeOut.QuadPart = Int32x32To64((LONG)90000, -1000); Status = KeWaitForSingleObject( SacChannelHandle->SacEvent, Executive, KernelMode, FALSE, &TimeOut ); if (Status == STATUS_SUCCESS) { Status = IoStatusBlock.Status; } } return Status; } NTSTATUS KSacChannelRawWrite( IN KSAC_CHANNEL_HANDLE SacChannelHandle, IN PCBYTE Buffer, IN ULONG BufferSize ) /*++ Routine Description: Write the given buffer to the specified SAC Channel Arguments: SacChannelHandle - The channel to write the buffer to Buffer - data buffer BufferSize - size of the buffer Return Value: Status --*/ { // // relay the write to the actual write routine // return KSacChannelWrite( SacChannelHandle, Buffer, BufferSize ); } NTSTATUS KSacChannelVTUTF8WriteString( IN KSAC_CHANNEL_HANDLE SacChannelHandle, IN PCWSTR String ) /*++ Routine Description: This routine writes a null-terminated Unicode String to the specified Channel. Arguments: SacChannelHandle - The channel to write the buffer to String - A null-terminated Unicode string Return Value: Status TRUE --> the buffer was sent --*/ { BOOL Status; ULONG BufferSize; // // Treating the String as a data buffer, we calculate it's size // not including the null termination // BufferSize = wcslen(String) * sizeof(WCHAR); KSAC_API_ASSERT(BufferSize > 0, FALSE); // // Write the data to the channel // Status = SacChannelWrite( SacChannelHandle, (PCBYTE)String, BufferSize ); return Status; } NTSTATUS KSacChannelVTUTF8Write( IN KSAC_CHANNEL_HANDLE SacChannelHandle, IN PCWCHAR Buffer, IN ULONG BufferSize ) /*++ Routine Description: This routines writes an array of WCHAR to the VTUTF8 channel specified. Arguments: SacChannelHandle - The channel to write the buffer to Buffer - data buffer BufferSize - size of the buffer Note: Buffer is not null-terminated BufferSize should not count a null-termination. Return Value: Status --*/ { // // relay the write to the actual write routine // return KSacChannelWrite( SacChannelHandle, (PCBYTE)Buffer, BufferSize ); } NTSTATUS KSacChannelHasNewData( IN PKSAC_CHANNEL_HANDLE SacChannelHandle, OUT PBOOLEAN InputWaiting ) /*++ Routine Description: This routine checks to see if there is any waiting input for the channel specified by the handle Arguments: SacChannelHandle - the channel to write the string to InputWaiting - the input buffer status Return Value: Status --*/ { HEADLESS_RSP_POLL Response; NTSTATUS Status; SIZE_T Length; IO_STATUS_BLOCK IoStatusBlock; SAC_CMD_POLL_CHANNEL PollChannelCmd; SAC_RSP_POLL_CHANNEL PollChannelRsp; KSAC_VALIDATE_KSAC_CHANNEL_HANDLE(SacChannelHandle); // // Initialize the Poll command // RtlZeroMemory(&PollChannelCmd, sizeof(SAC_RSP_POLL_CHANNEL)); PollChannelCmd.Handle.ChannelHandle = SacChannelHandle->ChannelHandle->ChannelHandle; // // Send down an IOCTL for polling a channel // Status = ZwDeviceIoControlFile( SacChannelHandle->ChannelHandle->DriverHandle, SacChannelHandle->SacEventHandle, NULL, NULL, &IoStatusBlock, IOCTL_SAC_POLL_CHANNEL, &PollChannelCmd, sizeof(PollChannelCmd), &PollChannelRsp, sizeof(PollChannelRsp) ); if (Status == STATUS_PENDING) { LARGE_INTEGER TimeOut; TimeOut.QuadPart = Int32x32To64((LONG)90000, -1000); Status = KeWaitForSingleObject( SacChannelHandle->SacEvent, Executive, KernelMode, FALSE, &TimeOut ); if (Status == STATUS_SUCCESS) { Status = IoStatusBlock.Status; } } // // Return the status to the user // if (NT_SUCCESS(Status)) { *InputWaiting = PollChannelRsp.InputWaiting; } else { *InputWaiting = FALSE; } return Status; } NTSTATUS KSacChannelRead( IN PKSAC_CHANNEL_HANDLE SacChannelHandle, IN PCBYTE Buffer, IN ULONG BufferSize, OUT PULONG ByteCount ) /*++ Routine Description: This routine reads data from the channel specified. Arguments: SacChannelHandle - the channel to read from Buffer - destination buffer BufferSize - size of the destination buffer (bytes) ByteCount - the actual # of byte read Return Value: Status --*/ { UCHAR Byte; BOOLEAN Success; TIME_FIELDS StartTime; TIME_FIELDS EndTime; HEADLESS_RSP_GET_BYTE Response; SIZE_T Length; NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; SAC_CMD_READ_CHANNEL ReadChannelCmd; ULONG ReadChannelRspSize; PSAC_RSP_READ_CHANNEL ReadChannelRsp; KSAC_VALIDATE_KSAC_CHANNEL_HANDLE(SacChannelHandle); KSAC_API_ASSERT(Buffer, STATUS_INVALID_PARAMETER_2); KSAC_API_ASSERT(BufferSize > 0, STATUS_INVALID_PARAMETER_2); // // Initialize the IOCTL command // ReadChannelCmd.Handle.ChannelHandle = SacChannelHandle->ChannelHandle->ChannelHandle; // // Initialize the IOCTL response // ReadChannelRsp = (PSAC_RSP_READ_CHANNEL)Buffer; // // Send down an IOCTL for reading a channel // Status = ZwDeviceIoControlFile( SacChannelHandle->ChannelHandle->DriverHandle, SacChannelHandle->SacEventHandle, NULL, NULL, &IoStatusBlock, IOCTL_SAC_READ_CHANNEL, &ReadChannelCmd, sizeof(ReadChannelCmd), ReadChannelRsp, ReadChannelRspSize ); if (Status == STATUS_PENDING) { LARGE_INTEGER TimeOut; TimeOut.QuadPart = Int32x32To64((LONG)90000, -1000); Status = KeWaitForSingleObject( SacChannelHandle->SacEvent, Executive, KernelMode, FALSE, &TimeOut ); if (Status == STATUS_SUCCESS) { Status = IoStatusBlock.Status; } } return Status; } NTSTATUS KSacChannelVTUTF8Read( IN SAC_CHANNEL_HANDLE SacChannelHandle, OUT PWSTR Buffer, IN ULONG BufferSize, OUT PULONG ByteCount ) /*++ Routine Description: This routine reads data from the channel specified. Arguments: SacChannelHandle - the channel to read from Buffer - destination buffer BufferSize - size of the destination buffer (bytes) ByteCount - the actual # of byte read Note: the Buffer upon return is NOT null terminated Return Value: Status --*/ { return KSacChannelRead( SacChannelHandle, (PBYTE)Buffer, BufferSize, ByteCount ); } NTSTATUS KSacChannelRawRead( IN KSAC_CHANNEL_HANDLE SacChannelHandle, OUT PBYTE Buffer, IN ULONG BufferSize, OUT PULONG ByteCount ) /*++ Routine Description: This routine reads data from the channel specified. Arguments: SacChannelHandle - the channel to read from Buffer - destination buffer BufferSize - size of the destination buffer (bytes) ByteCount - the actual # of byte read Return Value: Status --*/ { return KSacChannelRead( SacChannelHandle, Buffer, BufferSize, ByteCount ); }