Files
yttrium-virtio-gpu/vioserial/lib/PnPControl.cpp
T
Ladi Prosek 72010984eb Fix synchronization in PnPControl
Checking the number of ports must be done inside the critical section.
Also the inequality was incorrect, not to mention the missing LeaveCS
in PortSymbolicName().
2016-01-21 13:51:11 +11:00

502 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)
{
PVOID ret = NULL;
EnterCriticalSection(&PortsCS);
if ((size_t)(id) < NumPorts())
{
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)
{
wchar_t* ret = NULL;
EnterCriticalSection(&PortsCS);
if ((size_t)(index) < NumPorts())
{
Iterator it = Ports.begin();
advance(it, index);
ret = (wchar_t*)((*it)->SymbolicName.c_str());
}
LeaveCriticalSection(&PortsCS);
return ret;
}
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());
}