summaryrefslogtreecommitdiff
path: root/spb/SpbTestTool/exe/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'spb/SpbTestTool/exe/main.cpp')
-rw-r--r--spb/SpbTestTool/exe/main.cpp633
1 files changed, 633 insertions, 0 deletions
diff --git a/spb/SpbTestTool/exe/main.cpp b/spb/SpbTestTool/exe/main.cpp
new file mode 100644
index 00000000..ce9e994a
--- /dev/null
+++ b/spb/SpbTestTool/exe/main.cpp
@@ -0,0 +1,633 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ main.cpp
+
+Abstract:
+
+ This module contains the main entry point for the SpbTestTool
+ app. It facilitates accepting input, building commands, and
+ printing output.
+
+Environment:
+
+ user-mode
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+
+DWORD WINAPI InterruptNotificationThread(
+ _In_ LPVOID pvData
+ );
+
+bool
+ReadCommandFromStream(
+ _In_ FILE* InputStream,
+ _Inout_ list<string> *Tokens
+ );
+
+VOID
+PrintUsage(
+ _In_ PCSTR exeName
+ );
+
+DWORD
+RunCommand(
+ _In_ PCCommand Command
+ );
+
+HANDLE g_Peripheral = nullptr;
+HANDLE g_Event = nullptr;
+HANDLE g_InterruptNotificationThread = nullptr;
+PCCommand g_CurrentCommand = nullptr;
+bool g_WaitOnInterrupt = true;
+
+BOOL
+WINAPI
+OnControlKey(
+ _In_ DWORD ControlType
+ );
+
+void
+__cdecl
+main(
+ _In_ ULONG ArgumentsCe,
+ _In_reads_(ArgumentsCe) PCSTR Arguments[]
+ )
+{
+ FILE* inputStream = stdin;
+ bool prompt = true;
+
+ PCSTR peripheralPath = nullptr;
+ PCSTR inputPath = nullptr;
+
+ //
+ // Parse the command line arguments.
+ //
+
+ ULONG arg = 1;
+
+ while (arg < ArgumentsCe)
+ {
+ if ((Arguments[arg][0] != '/') &&
+ (Arguments[arg][0] != '-'))
+ {
+ PrintUsage(Arguments[0]);
+ goto exit;
+ }
+ else
+ {
+ if (tolower(Arguments[arg][1]) == 'p')
+ {
+ arg++;
+ if (arg == ArgumentsCe)
+ {
+ PrintUsage(Arguments[0]);
+ goto exit;
+ }
+ peripheralPath = Arguments[arg];
+ }
+ else if (tolower(Arguments[arg][1]) == 'i')
+ {
+ arg++;
+ if (arg == ArgumentsCe)
+ {
+ PrintUsage(Arguments[0]);
+ goto exit;
+ }
+ inputPath = Arguments[arg];
+ }
+ else
+ {
+ PrintUsage(Arguments[0]);
+ goto exit;
+ }
+ }
+
+ arg++;
+ }
+
+ //
+ // Open the input file if specified
+ //
+
+ if (inputPath != nullptr)
+ {
+ errno_t error;
+
+ printf("Opening %s as command input file\n", inputPath);
+ error = fopen_s(&inputStream, inputPath, "r");
+
+ if (error != 0)
+ {
+ printf("Error opening input file %s - %d\n", inputPath, error);
+ goto exit;
+ }
+
+ if (inputStream == nullptr)
+ {
+ printf("Error opening input file %s - %d\n", inputPath, error);
+ goto exit;
+ }
+
+ prompt = false;
+ }
+
+ //
+ // Open peripheral driver
+ //
+
+ if (peripheralPath == nullptr)
+ {
+ g_Peripheral = CreateFileW(
+ SPBTESTTOOL_USERMODE_PATH,
+ (GENERIC_READ | GENERIC_WRITE),
+ 0,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ nullptr);
+ }
+ else
+ {
+ printf("Opening %s as peripheral driver path \n", peripheralPath);
+ g_Peripheral = CreateFileA(
+ peripheralPath,
+ (GENERIC_READ | GENERIC_WRITE),
+ 0,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ nullptr);
+ }
+
+ if (g_Peripheral == INVALID_HANDLE_VALUE)
+ {
+ printf("Error opening peripheral driver - %u\n", GetLastError());
+ goto exit;
+ }
+
+ setvbuf(inputStream, nullptr, _IONBF, 0);
+ setvbuf(stdout, nullptr, _IONBF, 0);
+ setvbuf(stderr, nullptr, _IONBF, 0);
+
+ //
+ // Setup a control-C handler.
+ //
+
+ if (SetConsoleCtrlHandler(OnControlKey, true) == FALSE)
+ {
+ printf("Error setting ctrl-C handler - %u\n", GetLastError());
+ goto exit;
+ }
+
+ //
+ // Setup an overlapped structure to use with each I/O.
+ //
+
+ g_Event = CreateEvent(nullptr, true, false, nullptr);
+
+ if (g_Event == nullptr)
+ {
+ printf("error creating I/O event - %u\n", GetLastError());
+ goto exit;
+ }
+
+ //
+ // Create interrupt notification thread.
+ //
+
+ g_InterruptNotificationThread = CreateThread(
+ nullptr,
+ 0,
+ &InterruptNotificationThread,
+ nullptr,
+ 0,
+ nullptr);
+
+ if (g_InterruptNotificationThread == nullptr)
+ {
+ printf("error creating interrupt notification thread - %u\n", GetLastError());
+ goto exit;
+ }
+
+ //
+ // Loop reading commands off the command line and parsing them.
+ // EOF causes an exit.
+ //
+
+ do
+ {
+ list<string> *tokens = new list<string>();
+ PCCommand command;
+
+ if (prompt)
+ {
+ printf("> ");
+ fflush(stdout);
+ }
+
+ if (ReadCommandFromStream(inputStream, tokens) == false)
+ {
+ delete tokens;
+ break;
+ }
+
+ if (tokens->empty())
+ {
+ delete tokens;
+ continue;
+ }
+
+ command = CCommand::_ParseCommand(tokens);
+
+ if (command == nullptr)
+ {
+ delete tokens;
+ continue;
+ }
+
+ g_CurrentCommand = command;
+
+ RunCommand(command);
+
+ g_CurrentCommand = nullptr;
+
+ //
+ // A reference to the tokens list is saved by
+ // the command and will be freed when the command
+ // is deleted.
+ //
+
+ delete command;
+
+ }
+ while (feof(inputStream) == 0);
+
+exit:
+
+ if (prompt == false)
+ {
+ fclose(inputStream);
+ }
+
+ if (g_InterruptNotificationThread != nullptr)
+ {
+ WaitForSingleObject(g_InterruptNotificationThread, INFINITE);
+ CloseHandle(g_InterruptNotificationThread);
+ }
+
+ CloseHandle(g_Peripheral);
+
+ if (g_Event != nullptr)
+ {
+ CloseHandle(g_Event);
+ }
+
+ return;
+}
+
+DWORD WINAPI InterruptNotificationThread(
+ _In_ LPVOID pvData
+ )
+{
+ DWORD status;
+ DWORD bytesReturned;
+ OVERLAPPED ov = {0};
+
+ UNREFERENCED_PARAMETER(pvData);
+
+ ov.hEvent = CreateEvent(nullptr, false, false, nullptr);
+
+ if (ov.hEvent == nullptr)
+ {
+ printf("error creating overlapped event for interrupt thread - %u\n", GetLastError());
+ goto exit;
+ }
+
+ while (g_WaitOnInterrupt == true)
+ {
+ if ((DeviceIoControl(
+ g_Peripheral,
+ IOCTL_SPBTESTTOOL_WAIT_ON_INTERRUPT,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ nullptr,
+ &ov) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ printf("failed to pend WaitOnInterrupt IOCTL- %u\n", GetLastError());
+ goto exit;
+ }
+
+ status = WaitForSingleObject(ov.hEvent, INFINITE);
+
+ switch (status)
+ {
+ // DeviceIoControl completed.
+ case WAIT_OBJECT_0:
+
+ if (!GetOverlappedResult(g_Peripheral, &ov, &bytesReturned, FALSE))
+ {
+ printf("GetOverlappedResult failed with status: %u\n\n", GetLastError());
+ }
+ else
+ {
+ printf("\n\n");
+ printf(" ** Interrupt detected. Please acknowledge or disable **\n");
+ printf(" *** and type 'signal' to inform the SpbTestTool driver ***\n");
+ printf(" ** that the interrupt has been handled. **\n");
+ printf("\n");
+ }
+ break;
+
+ default:
+ // Error in the WaitForSingleObject; abort.
+ // This indicates a problem with the OVERLAPPED
+ // structure's event handle.
+ printf("WaitOnInterrupt unexpected return %u\n\n", status);
+ break;
+ }
+ }
+
+exit:
+
+ return 0;
+}
+
+VOID
+PrintUsage(
+ _In_ PCSTR exeName
+ )
+{
+ printf("Usage: %s [/p <driver_path>] [/i <script_name>]\n",
+ exeName);
+}
+
+DWORD
+RunCommand(
+ _In_ PCCommand Command
+ )
+{
+
+ DWORD status;
+ DWORD bytesTransferred;
+
+ Command->Overlapped.hEvent = g_Event;
+
+ if (Command->Execute() == true)
+ {
+ if (GetOverlappedResult(Command->File,
+ &(Command->Overlapped),
+ &bytesTransferred,
+ true) == FALSE)
+ {
+ status = GetLastError();
+ }
+ else
+ {
+ status = NO_ERROR;
+ }
+
+ Command->Complete(status, bytesTransferred);
+ }
+ else
+ {
+ status = GetLastError();
+ }
+
+ return status;
+}
+
+
+bool
+ReadLine(
+ _In_ FILE* InputStream,
+ _In_ ULONG BufferCch,
+ _Out_writes_(BufferCch) CHAR Buffer[]
+ )
+{
+ ULONG i;
+
+ for(i = 0; i < BufferCch - 1; i += 1)
+ {
+ if (fread(&(Buffer[i]), sizeof(CHAR), 1, InputStream) == 0)
+ {
+ return false;
+ }
+
+ fputc(Buffer[i], stdout);
+
+ if (Buffer[i] == '\n')
+ {
+ Buffer[i] = '\0';
+ return true;
+ }
+ else if (Buffer[i] == '\b')
+ {
+ Buffer[i] = '\0';
+ i -= 2;
+ }
+ }
+
+ return false;
+}
+
+bool
+ReadCommandFromStream(
+ _In_ FILE* InputStream,
+ _Inout_ list<string> *Tokens
+ )
+{
+ CHAR buffer[255];
+
+ if (ReadLine(InputStream,
+ sizeof(buffer) - 1,
+ buffer) == false)
+ {
+ return false;
+ }
+
+ string currentLine(buffer);
+ string token;
+
+ list<string> tokens;
+
+ Tokens->clear();
+
+ string::size_type i;
+ long start;
+
+ for(i = 0, start = -1;
+ i < currentLine.length() + 1;
+ i += 1)
+ {
+ wchar_t c;
+
+ if (i < currentLine.length())
+ {
+ c = currentLine[i];
+ }
+ else
+ {
+ c = L'\0';
+ }
+
+ if (iswspace(c) || (c == L'\0'))
+ {
+ if (start == -1)
+ {
+ //
+ // not tracking a token - whitespace is skipped.
+ //
+ }
+ else
+ {
+ //
+ // tracking a token - whitespace or NUL terminates it.
+ // if the last character was a } then split the token.
+ //
+
+ if (currentLine[i-1] == '}')
+ {
+ token.assign(currentLine, start, i - start - 1);
+ Tokens->insert(Tokens->end(), token);
+ Tokens->insert(Tokens->end(), string("}"));
+ }
+ else
+ {
+ token.assign(currentLine, start, i - start);
+ Tokens->insert(Tokens->end(), token);
+ }
+
+ start = -1;
+ }
+ }
+ else if (start == -1)
+ {
+ //
+ // first character of a token. If it's { then split the token.
+ //
+
+ if (currentLine[i] == '{')
+ {
+ Tokens->insert(Tokens->end(), string("{"));
+ }
+ else if (currentLine[i] == '}')
+ {
+ Tokens->insert(Tokens->end(), string("}"));
+ }
+ else
+ {
+ start = (long) i;
+ }
+ }
+ else
+ {
+ //
+ // tracking a token - non whitespace is included.
+ //
+ }
+ }
+
+ return true;
+}
+
+VOID
+PrintBytes(
+ _In_ ULONG BufferCb,
+ _In_reads_bytes_(BufferCb) BYTE Buffer[]
+ )
+{
+ ULONG index = 0;
+
+ for(index = 0; index < BufferCb; index += 16)
+ {
+ printf(" ");
+ for(ULONG i = index; i < (index + 16); i += 1)
+ {
+ if (i < BufferCb)
+ {
+ if ((i != index) && (i % 8 == 0))
+ {
+ printf("- ");
+ }
+
+ printf("%02x ", Buffer[i]);
+ }
+ else
+ {
+ if ((i != index) && (i % 8 == 0))
+ {
+ printf(" ");
+ }
+ printf(" ");
+ }
+ }
+
+ printf(" : ");
+
+ for(ULONG i = index; i < (index + 16); i += 1)
+ {
+ if (i < BufferCb)
+ {
+ if ((i != index) && (i % 8 == 0))
+ {
+ printf(" ");
+ }
+
+ if (isprint(Buffer[i]))
+ {
+ printf("%c", Buffer[i]);
+ }
+ else
+ {
+ printf(".");
+ }
+ }
+ else
+ {
+ printf(" ");
+ }
+ }
+
+ printf("\n");
+ }
+}
+
+BOOL
+WINAPI
+OnControlKey(
+ _In_ DWORD ControlType
+ )
+{
+ if (ControlType == CTRL_C_EVENT)
+ {
+ //
+ // If there's a current command then attempt to cancel it.
+ //
+
+ if (g_CurrentCommand != nullptr)
+ {
+ g_CurrentCommand->Cancel();
+ return TRUE;
+ }
+
+ //
+ // Stop the interrupt notification thread
+ //
+
+ g_WaitOnInterrupt = false;
+ CancelIo(g_Peripheral);
+ }
+
+ return FALSE;
+}