Files
yttrium-virtio-gpu/vioserial/lib/PnPControl.cpp
T

500 lines
13 KiB
C++

#include "StdAfx.h"
PnPControl* PnPControl::Instance = NULL;
PnPControl::Reference = 0;
void PnPControl::FindControllers()
{
wstring name;
for (int i = 0; FindInstance(GUID_VIOSERIAL_CONTROLLER, i, name); ++i)
{
AddController(name.c_str());
}
}
void PnPControl::FindPorts()
{
wstring name;
for (int i = 0; FindInstance(GUID_VIOSERIAL_PORT, i, name); ++i)
{
AddPort(name.c_str());
}
}
BOOL PnPControl::FindInstance(GUID guid, DWORD idx, wstring& name)
{
HDEVINFO HardwareDeviceInfo;
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
HardwareDeviceInfo = SetupDiGetClassDevs(
&guid,
NULL,
NULL,
(DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)
);
if (HardwareDeviceInfo == INVALID_HANDLE_VALUE)
{
printf("Cannot get class devices.\n");
return FALSE;
}
DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
if (SetupDiEnumDeviceInterfaces(HardwareDeviceInfo, NULL, &guid, idx, &DeviceInterfaceData))
{
DWORD RequiredLength = 0;
PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData = NULL;
SP_DEVINFO_DATA DevInfoData = {sizeof(SP_DEVINFO_DATA)};
SetupDiGetDeviceInterfaceDetail(
HardwareDeviceInfo,
&DeviceInterfaceData,
NULL,
0,
&RequiredLength,
NULL
);
DeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(RequiredLength);
if (DeviceInterfaceDetailData == NULL)
{
printf("Cannot allocate memory.\n");
return FALSE;
}
DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
SetupDiGetDeviceInterfaceDetail(
HardwareDeviceInfo,
&DeviceInterfaceData,
DeviceInterfaceDetailData,
RequiredLength,
NULL,
&DevInfoData
);
name = DeviceInterfaceDetailData->DevicePath;
free ((PVOID)DeviceInterfaceDetailData);
SetupDiDestroyDeviceInfoList(HardwareDeviceInfo);
return TRUE;
}
SetupDiDestroyDeviceInfoList(HardwareDeviceInfo);
return FALSE;
}
void PnPControl::Init( )
{
DWORD id;
Thread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE) ServiceThread,
(LPVOID)this,
0,
&id);
if (Thread == NULL)
{
printf("Cannot create thread Error = %d.\n", GetLastError());
}
if ( !InitializeCriticalSectionAndSpinCount(&PortsCS, 0x4000))
{
printf("Cannot initalize critical section Error = %d.\n", GetLastError());
}
}
void PnPControl::Close( )
{
if (PortNotify != NULL)
{
UnregisterDeviceNotification(PortNotify);
PortNotify = NULL;
}
if (ControllerNotify != NULL)
{
UnregisterDeviceNotification(ControllerNotify);
ControllerNotify = NULL;
}
if (Wnd && Thread)
{
SendMessage(Wnd, WM_DESTROY, 0, 0);
if (WAIT_TIMEOUT == WaitForSingleObject(Thread, 1000))
{
printf("Cannot close thread after 1 sec\n");
TerminateThread(Thread, 0);
}
Thread = NULL;
}
DeleteCriticalSection(&PortsCS);
}
DWORD WINAPI PnPControl::ServiceThread(PnPControl* ptr)
{
ptr->Run();
return 0;
}
void PnPControl::Run()
{
MSG Msg;
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = GlobalWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(0));
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = L"VioSerialLib";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
printf("Window Registration Failed!\n");
}
Wnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
L"VioSerialLib",
NULL,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, NULL, NULL
);
if(Wnd == NULL)
{
printf("Window Creation Failed!\n");
}
SetWindowLongPtr(Wnd, GWLP_USERDATA, (LONG_PTR)(this));
ControllerNotify = RegisterInterfaceNotify(GUID_VIOSERIAL_CONTROLLER);
PortNotify = RegisterInterfaceNotify(GUID_VIOSERIAL_PORT);
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
LRESULT CALLBACK PnPControl::GlobalWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_DEVICECHANGE:
ProcessPnPNotification((PnPControl*)(GetWindowLongPtr(hWnd, GWLP_USERDATA)), PnPNotification(msg, wParam, lParam));
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
HDEVNOTIFY PnPControl::RegisterInterfaceNotify(
GUID InterfaceClassGuid
)
{
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
HDEVNOTIFY Notify;
ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
NotificationFilter.dbcc_size =
sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_classguid = InterfaceClassGuid;
Notify = RegisterDeviceNotification( Wnd,
&NotificationFilter,
DEVICE_NOTIFY_WINDOW_HANDLE
);
if (Notify == NULL)
{
printf("RegisterDeviceNotification failed: %d\n",
GetLastError());
}
return Notify;
}
HDEVNOTIFY PnPControl::RegisterHandleNotify(HANDLE handle)
{
HDEVNOTIFY Notify;
DEV_BROADCAST_HANDLE NotificationFilter;
ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
NotificationFilter.dbch_size =
sizeof(DEV_BROADCAST_HANDLE);
NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;
NotificationFilter.dbch_handle = handle;
NotificationFilter.dbch_eventguid = GUID_VIOSERIAL_PORT_CHANGE_STATUS;
Notify = RegisterDeviceNotification( Wnd,
&NotificationFilter,
DEVICE_NOTIFY_WINDOW_HANDLE
);
if (Notify == NULL)
{
printf("RegisterDeviceNotification failed: %d\n",
GetLastError());
}
return Notify;
}
void PnPControl::ProcessPnPNotification(PnPControl* ptr, PnPNotification Notification)
{
switch (Notification.wParam)
{
case DBT_DEVICEARRIVAL: {
PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)Notification.lParam;
if (pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;
if (IsEqualGUID(GUID_VIOSERIAL_CONTROLLER, pDevInf->dbcc_classguid))
{
ptr->AddController(pDevInf->dbcc_name);
}
else if (IsEqualGUID(GUID_VIOSERIAL_PORT, pDevInf->dbcc_classguid))
{
ptr->AddPort(pDevInf->dbcc_name);
}
}
}
break;
case DBT_DEVICEREMOVECOMPLETE: {
PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)Notification.lParam;
if (pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;
if (IsEqualGUID(GUID_VIOSERIAL_CONTROLLER, pDevInf->dbcc_classguid))
{
ptr->RemoveController(pDevInf->dbcc_name);
}
else if (IsEqualGUID(GUID_VIOSERIAL_PORT, pDevInf->dbcc_classguid))
{
ptr->RemovePort(pDevInf->dbcc_name);
}
}
}
break;
default:
ptr->DispatchPnpMessage(Notification);
break;
}
}
void PnPControl::AddController(const wchar_t* name)
{
printf ("Add Controller %ws\n", name);
Controllers.push_back((new SerialController(name)));
}
void PnPControl::RemoveController(wchar_t* name)
{
printf ("Remove Controller %ws\n", name);
for(Iterator it = Controllers.begin(); it != Controllers.end(); it++)
{
if (_wcsnicmp((*it)->Name.c_str(), name, (*it)->Name.size()) == 0)
{
delete *it;
Controllers.remove(*it);
break;
}
}
}
void PnPControl::AddPort(const wchar_t* name)
{
printf ("Add Port %ws\n", name);
EnterCriticalSection(&PortsCS);
Ports.push_back(new SerialPort(name, this));
LeaveCriticalSection(&PortsCS);
}
void PnPControl::RemovePort(wchar_t* name)
{
printf ("Remove Port %ws\n", name);
EnterCriticalSection(&PortsCS);
for(Iterator it = Ports.begin(); it != Ports.end(); it++)
{
if (_wcsnicmp((*it)->Name.c_str(), name, (*it)->Name.size()) == 0)
{
delete *it;
Ports.remove(*it);
break;
}
}
LeaveCriticalSection(&PortsCS);
}
BOOL PnPControl::FindPort(const wchar_t* name)
{
wstring tmp = name;
EnterCriticalSection(&PortsCS);
BOOL ret = FALSE;
for(Iterator it = Ports.begin(); it != Ports.end(); it++)
{
if (_wcsnicmp((*it)->SymbolicName.c_str(), name, (*it)->Name.size()) == 0)
{
ret = TRUE;
break;
}
}
LeaveCriticalSection(&PortsCS);
return ret;
}
PVOID PnPControl::OpenPortByName(const wchar_t* name)
{
wstring tmp = name;
PVOID ret = NULL;
EnterCriticalSection(&PortsCS);
for(Iterator it = Ports.begin(); it != Ports.end(); it++)
{
if (_wcsnicmp((*it)->SymbolicName.c_str(), name, (*it)->Name.size()) == 0)
{
if(((SerialPort*)(*it))->OpenPort() == TRUE)
{
ret = *it;
break;
}
}
}
LeaveCriticalSection(&PortsCS);
return ret;
}
PVOID PnPControl::OpenPortById(UINT id)
{
if ((size_t)(id) > NumPorts())
{
return NULL;
}
PVOID ret = NULL;
EnterCriticalSection(&PortsCS);
Iterator it = Ports.begin();
advance(it, id);
if (((SerialPort*)(*it))->OpenPort() == TRUE)
{
ret = *it;
}
LeaveCriticalSection(&PortsCS);
return ret;
}
BOOL PnPControl::ReadPort(PVOID port, PVOID buf, PULONG size)
{
Iterator it;
EnterCriticalSection(&PortsCS);
for(it = Ports.begin(); it != Ports.end(); it++)
{
if (*it == port)
{
break;
}
}
LeaveCriticalSection(&PortsCS);
return (*it != port) ? FALSE : ((SerialPort*)(*it))->ReadPort(buf, (size_t*)(size));
}
BOOL PnPControl::WritePort(PVOID port, PVOID buf, ULONG size)
{
Iterator it;
EnterCriticalSection(&PortsCS);
for(it = Ports.begin(); it != Ports.end(); it++)
{
if (*it == port)
{
break;
}
}
LeaveCriticalSection(&PortsCS);
return (*it != port) ? FALSE : ((SerialPort*)(*it))->WritePort(buf, (size_t*)(&size));
}
VOID PnPControl::ClosePort(PVOID port)
{
EnterCriticalSection(&PortsCS);
for(Iterator it = Ports.begin(); it != Ports.end(); it++)
{
if (*it == port)
{
((SerialPort*)(*it))->ClosePort();
break;
}
}
LeaveCriticalSection(&PortsCS);
}
wchar_t* PnPControl::PortSymbolicName(int index)
{
if ((size_t)(index) > NumPorts())
{
return NULL;
}
EnterCriticalSection(&PortsCS);
Iterator it = Ports.begin();
advance(it, index);
return (wchar_t*)((*it)->SymbolicName.c_str());
}
VOID PnPControl::RegisterNotification(PVOID port, VIOSERIALNOTIFYCALLBACK pfn, PVOID ptr)
{
for(Iterator it = Ports.begin(); it != Ports.end(); it++)
{
if (*it == port)
{
((SerialPort*)(*it))->NotificationPair.first = pfn;
((SerialPort*)(*it))->NotificationPair.second = ptr;
return;
}
}
}
BOOL PnPControl::IsRunningAsService()
{
wchar_t wstrPath[_MAX_FNAME];
if (!GetModuleFileName( NULL, wstrPath, _MAX_FNAME))
{
printf ("Error getting module file name (%d)\n", GetLastError());
return FALSE;
}
printf ("Module File Name is %ws\n", wstrPath);
wstring fullname(wstrPath);
size_t pos_begin = fullname.rfind(L'\\') + 1;
size_t pos_end = fullname.rfind(L'.');
wstring filename(fullname, pos_begin, pos_end-pos_begin);
printf ("File name = %ws\n", filename.c_str());
SERVICE_STATUS_PROCESS status;
DWORD needed;
SC_HANDLE scm = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (scm == NULL)
{
printf ("OpenSCManager failed (%d)\n", GetLastError());
return FALSE;
}
SC_HANDLE svc = OpenService (scm, filename.c_str(), SERVICE_ALL_ACCESS);
if (svc == NULL)
{
printf ("OpenService failed (%d)\n", GetLastError());
return FALSE;
}
if(!QueryServiceStatusEx(svc, SC_STATUS_PROCESS_INFO, (LPBYTE) &status, sizeof(SERVICE_STATUS_PROCESS), &needed))
{
printf ("QueryServiceStatusEx failed (%d)\n", GetLastError());
CloseServiceHandle (svc);
CloseServiceHandle (scm);
return FALSE;
}
CloseServiceHandle (svc);
CloseServiceHandle (scm);
return (status.dwProcessId == GetCurrentProcessId());
}