mirror of
https://github.com/arehnman/yttrium-virtio-gpu.git
synced 2026-10-31 02:05:04 +00:00
The library hands out pointers to SerialPort objects which are then simply deleted on port removal. This fix makes the code port hot-unplug friendly.
245 lines
5.1 KiB
C++
245 lines
5.1 KiB
C++
#include "StdAfx.h"
|
|
|
|
SerialPort::SerialPort(wstring LinkName, PnPControl* ptr)
|
|
{
|
|
Name = LinkName;
|
|
Notify = NULL;
|
|
NotificationPair.first = NULL;
|
|
NotificationPair.second = NULL;
|
|
Reference = 0;
|
|
Handle = CreateFile(Name.c_str(),
|
|
GENERIC_WRITE | GENERIC_READ,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
if (Handle == INVALID_HANDLE_VALUE)
|
|
{
|
|
return;
|
|
}
|
|
ULONG ulReturnedLength = 0;
|
|
BYTE buf[512];
|
|
PVIRTIO_PORT_INFO inf = (PVIRTIO_PORT_INFO)buf;
|
|
BOOL res = DeviceIoControl(
|
|
Handle,
|
|
IOCTL_GET_INFORMATION,
|
|
NULL,
|
|
0,
|
|
buf,
|
|
sizeof(buf),
|
|
&ulReturnedLength,
|
|
NULL);
|
|
if (res == FALSE)
|
|
{
|
|
printf ("Error. DeviceIoControl failed %d.\n", GetLastError());
|
|
return;
|
|
}
|
|
HostConnected = inf->HostConnected;
|
|
string s = inf->Name;
|
|
|
|
SymbolicName.resize(s.length(),L' ');
|
|
copy(s.begin(), s.end(), SymbolicName.begin());
|
|
|
|
Control = ptr;
|
|
CloseHandle(Handle);
|
|
Handle = INVALID_HANDLE_VALUE;
|
|
}
|
|
SerialPort::~SerialPort()
|
|
{
|
|
ClosePort();
|
|
}
|
|
void SerialPort::AddRef() {
|
|
InterlockedIncrement(&Reference);
|
|
}
|
|
void SerialPort::Release() {
|
|
if (InterlockedDecrement(&Reference) == 0) {
|
|
delete this;
|
|
}
|
|
}
|
|
BOOL SerialPort::OpenPort()
|
|
{
|
|
Handle = CreateFile(Name.c_str(),
|
|
GENERIC_WRITE | GENERIC_READ,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_OVERLAPPED,
|
|
NULL);
|
|
if (Handle == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
ULONG ulReturnedLength = 0;
|
|
BYTE buf[512];
|
|
PVIRTIO_PORT_INFO inf = (PVIRTIO_PORT_INFO)buf;
|
|
BOOL res = DeviceIoControl(
|
|
Handle,
|
|
IOCTL_GET_INFORMATION,
|
|
NULL,
|
|
0,
|
|
buf,
|
|
sizeof(buf),
|
|
&ulReturnedLength,
|
|
NULL);
|
|
if (res == FALSE)
|
|
{
|
|
printf ("Error. DeviceIoControl failed %d.\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
HostConnected = inf->HostConnected;
|
|
Notify = Control->RegisterHandleNotify(Handle);
|
|
return TRUE;
|
|
}
|
|
void SerialPort::ClosePort()
|
|
{
|
|
if (Notify != NULL)
|
|
{
|
|
UnregisterDeviceNotification(Notify);
|
|
Notify = NULL;
|
|
}
|
|
if (Handle != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(Handle);
|
|
Handle = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
BOOL SerialPort::ReadPort(PVOID buf, size_t *len)
|
|
{
|
|
BOOL res = FALSE;
|
|
DWORD ret;
|
|
DWORD bytes = (DWORD)(*len);
|
|
OVERLAPPED ol = {0};
|
|
|
|
if (buf == NULL || *len == 0)
|
|
{
|
|
*len = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if( ol.hEvent == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
res = ReadFile ( Handle,
|
|
buf,
|
|
bytes,
|
|
&ret,
|
|
&ol
|
|
);
|
|
if (!res)
|
|
{
|
|
DWORD err = GetLastError();
|
|
if ( err != ERROR_IO_PENDING)
|
|
{
|
|
printf("Read failed but isn't delayed. Error = %d\n", err);
|
|
res = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (!GetOverlappedResult(Handle, &ol, &ret, TRUE))
|
|
{
|
|
res = FALSE;
|
|
}
|
|
else
|
|
{
|
|
*len = ret;
|
|
res = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*len = ret;
|
|
res = TRUE;
|
|
}
|
|
|
|
CloseHandle( ol.hEvent );
|
|
return res;
|
|
}
|
|
|
|
BOOL SerialPort::WritePort(PVOID buf, size_t *len)
|
|
{
|
|
BOOL res = FALSE;
|
|
ULONG ret = 0;
|
|
DWORD bytes = (DWORD)(*len);
|
|
OVERLAPPED ol = {0};
|
|
|
|
if (buf == NULL || *len == 0)
|
|
{
|
|
*len = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
if( ol.hEvent == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
res = WriteFile ( Handle,
|
|
buf,
|
|
bytes,
|
|
&ret,
|
|
&ol
|
|
);
|
|
if (!res)
|
|
{
|
|
DWORD err = GetLastError();
|
|
if (err != ERROR_IO_PENDING)
|
|
{
|
|
printf("Write failed but isn't delayed. Error = %d\n", err);
|
|
res = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (!GetOverlappedResult(Handle, &ol, &ret, TRUE))
|
|
{
|
|
res = FALSE;
|
|
}
|
|
else
|
|
{
|
|
*len = ret;
|
|
res = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*len = ret;
|
|
res = TRUE;
|
|
}
|
|
|
|
CloseHandle( ol.hEvent );
|
|
return res;
|
|
}
|
|
|
|
|
|
void SerialPort::handleEvent(const PnPControl& ref)
|
|
{
|
|
PnPNotification Notification = ref.GetNotification();
|
|
switch (Notification.wParam)
|
|
{
|
|
case DBT_CUSTOMEVENT: {
|
|
PDEV_BROADCAST_HANDLE pHdr;
|
|
pHdr = (PDEV_BROADCAST_HANDLE)Notification.lParam;
|
|
if (IsEqualGUID(GUID_VIOSERIAL_PORT_CHANGE_STATUS, pHdr->dbch_eventguid) &&
|
|
(pHdr->dbch_handle == Handle))
|
|
{
|
|
PVIRTIO_PORT_STATUS_CHANGE pEventInfo = (PVIRTIO_PORT_STATUS_CHANGE) pHdr->dbch_data;
|
|
HostConnected = pEventInfo->Reason;
|
|
if (NotificationPair.first)
|
|
{
|
|
NotificationPair.first(NotificationPair.second);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|