/* IDTech USB Bridge for UWP Applications
*
* This USB Bridge class will allow IDTechSDK.dll to utilize
* USB communication on UWP Applications
*
* Requires IDtechSDK_STD library from NuGet, Version 2.1.2.156 or later
* https://www.nuget.org/packages/IDTechSDK_STD/
*
* Project Page at IDTech:
* https://atlassian.idtechproducts.com/confluence/display/KB/Universal+Library+for+Visual+Studio+-+Home?desktop=true¯oName=attachments
*
* This class will only communicate IDTech USB Devices in HID mode. This class will not allow
* communication with IDTech USB Devices Operating in KB mode
*
* Usage:
* 1. Add IDTechSDK_STD from NuGet into your project (2.1.2.156 or later)
* 2. Add this class IDT_UsbBridge to your UWP project
* 3. As per standard implementation, set the callback for IDTechSDK
* 4. After setting the callback, create an instance to this class
*
* IDT_Device.setCallbackIP(MessageCallBack);
* IDT_UsbBridge bridge = new IDT_UsbBridge();
*
* 5. Configure your Package.appxmanifest to allow communication with Human Interface Devices
*
*
*
*
*
*
*
*/
using IDTechSDK;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Devices.HumanInterfaceDevice;
using Windows.Storage;
using Windows.Storage.Streams;
namespace IDT_USBBridge
{
class IDT_UsbBridge
{
static Dictionary usbList = new Dictionary();
static Dictionary receiveQueue = new Dictionary();
static Dictionary deviceDict = new Dictionary();
bool inEnum = false;
public IDT_UsbBridge()
{
IDT_Device.checkUwpUsbCallback = checkUwpUsb;
IDT_Device.uwpWriteKBCallback = UWPwriteKB;
IDT_Device.uwpWriteHIDCallback = UWPwriteHID;
IDT_Device.uwpReadKBCallback = UWPreadKB;
IDT_Device.uwpReadHIDCallback = UWPreadHID;
IDT_Device.uwpOpenDeviceCallback = UWPopenDevice;
IDT_Device.uwpCloseDeviceCallback = UWPcloseDevice;
IDT_Device.startUSBMonitoring();
}
private void checkUwpUsb()
{
if (inEnum) return;
enumerateDevice();
}
private void UWPcloseDevice(string ident)
{
HidDevice dev = getDevice(ident.ToUpper());
if (dev == null) return;
dev.Dispose();
deviceDict.Remove(ident.ToUpper());
usbList.Remove(ident.ToUpper());
if (receiveQueue.ContainsKey(ident))
{
receiveQueue[ident].Close();
}
receiveQueue.Remove(ident.ToUpper());
}
private bool UWPopenDevice(string ident)
{
return deviceDict.ContainsKey(ident.ToUpper());
}
HidDevice getDevice(string ident)
{
if (deviceDict.ContainsKey(ident.ToUpper())) return deviceDict[ident.ToUpper()];
return null;
}
private void UWPwriteKB(byte[] data, string ident)
{
//KB Device Functionality disabled
HidDevice dev = getDevice(ident.ToUpper());
if (dev == null) return;
}
private static UInt32 sendOutputReportSync(HidOutputReport outputReport, HidDevice dev )
{
uint outputReportSent = 0;
// Send the command to the device synchronously
Task task = new Task(async () =>
{
try
{
await dev.SendOutputReportAsync(outputReport);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Sending output report error: " + ex);
outputReportSent = 1;
}
});
task.RunSynchronously();
return outputReportSent;
}
private static IBuffer getDataBufferFromBytes(byte[] data)
{
// Convert the byte array into an IBuffer
IBuffer dataBuffer = null;
DataWriter dataWriter = new DataWriter();
dataWriter.WriteBytes(data);
dataBuffer = dataWriter.DetachBuffer();
//dataWriter.Dispose();
return dataBuffer;
}
private UInt32 UWPwriteHID(byte[] data, string ident)
{
if (data == null) return 1;
HidDevice dev = getDevice(ident.ToUpper());
if (dev == null) return 1;
IDTechComm comm = Profile.getComm(ident.ToUpper());
if (comm == null) return 1;
DEVICE_PROTOCOL_Types type = comm.getDeviceProtocol();
HidOutputReport outputReport = dev.CreateOutputReport();
outputReport.Data = getDataBufferFromBytes(data);
return sendOutputReportSync(outputReport, dev);
}
private byte[] UWPreadKB(string ident)
{
//KB Device Functionality disabled
HidDevice dev = getDevice(ident.ToUpper());
if (dev == null) return null;
return null;
}
private byte[] UWPreadHID(string ident)
{
HidDevice dev = getDevice(ident.ToUpper());
if (dev == null) return null;
ReceiveQueue q = receiveQueue[ident.ToUpper()];
byte[] val = q.Dequeue();
return val;
}
private async void enumerateDevice()
{
inEnum = true;
bool listChanged = false;
string selector = "System.Devices.InterfaceClassGuid:=\"{4D1E55B2-F16F-11CF-88CB-001111000030}\" AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True";
var devices = await DeviceInformation.FindAllAsync(selector);
if (devices.Count == 0) return;
foreach (DeviceInformation dev in devices)
{
//add any new devices
if (!deviceDict.ContainsKey(dev.Id.ToUpper()))
{
if (dev.Id.IndexOf("VID_") > 0 && dev.Id.IndexOf("PID_") > 0)
{
int vidValue = int.Parse(dev.Id.Substring(dev.Id.IndexOf("VID_") + 4, 4), System.Globalization.NumberStyles.HexNumber);
int pidValue = int.Parse(dev.Id.Substring(dev.Id.IndexOf("PID_") + 4, 4), System.Globalization.NumberStyles.HexNumber);
foreach (IDTechDevices idt in IDTechSDK.Profile.idtechDevices)
{
if (idt.vid == vidValue && idt.pid == pidValue)
{
HidDevice device = await HidDevice.FromIdAsync(dev.Id, FileAccessMode.ReadWrite);
if (device != null)
{
device.InputReportReceived += inputReportReceivedEvent;
listChanged = true;
ReceiveQueue queue = new ReceiveQueue();
lock (receiveQueue)
{
receiveQueue.Add(dev.Id.ToUpper(), queue);
}
deviceDict.Add(dev.Id.ToUpper(), device);
usbList.Add(dev.Id.ToUpper(), idt);
}
}
}
}
}
}
List removedDevices = new List();
foreach (KeyValuePair hid in deviceDict)
{
bool foundMatch = false;
foreach (DeviceInformation dev in devices)
{
if (dev.Id.ToUpper().Equals(hid.Key.ToUpper())) foundMatch = true;
}
if (!foundMatch) removedDevices.Add(hid.Key.ToUpper());
}
foreach (string str in removedDevices)
{
listChanged = true;
deviceDict.Remove(str.ToUpper());
usbList.Remove(str.ToUpper());
lock (receiveQueue)
{
receiveQueue.Remove(str.ToUpper());
}
}
if (listChanged) IDT_Device.UwpUsbListChanged(usbList);
inEnum = false;
}
class ReceiveQueue
{
bool validQueue = true;
Queue queue = null;
public ReceiveQueue()
{
queue = new Queue();
}
public void Enqueue(byte[] data)
{
lock (queue)
{
queue.Enqueue(data);
}
}
public byte[] Dequeue()
{
while (validQueue)
{
lock (queue)
{
if (queue.Count > 0) return queue.Dequeue();
}
Thread.Sleep(50);
}
return null;
}
public void Close()
{
validQueue = false;
}
}
private static void inputReportReceivedEvent(HidDevice sender, HidInputReportReceivedEventArgs eventArgs)
{
if (deviceDict.Count == 0) return;
HidInputReport inputReport = eventArgs.Report;
string ident = null;
foreach (KeyValuePair device in deviceDict)
{
if (device.Value == sender) ident = device.Key;
}
if (ident == null) ident = deviceDict.ElementAt(0).Key;
// Check if input report is empty
if (inputReport != null)
{
// Create new byte array with the size of the input report's data length
byte[] inputReportDataBytes = new byte[inputReport.Data.Length];
IBuffer newBuf = inputReport.Data;
DataReader dataReader = DataReader.FromBuffer(newBuf);
dataReader.ReadBytes(inputReportDataBytes);
ReceiveQueue q = receiveQueue[ident];
if (q != null) q.Enqueue(inputReportDataBytes);
}
}
}
}