Files
yttrium-virtio-gpu/vioserial/lib/port.cpp
T
Ladi Prosek 9db057a7a0 Add refcounting to SerialPort
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.
2016-01-21 13:51:11 +11:00

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;
}
}