Files
yttrium-virtio-gpu/vioserial/lib/port.cpp
T
2025-01-13 09:06:20 +01:00

215 lines
5.0 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;
}
}