How to access a USB port in C#, send a data stream and receive other data?
The USB-C to USB Adapter lets you connect iOS devices and many of your standard USB accessories to a USB-C or Thunderbolt 3 (USB-C) enabled Mac. Plug the USB-C end of the adapter into a USB-C or Thunderbolt 3 (USB-C) port on your Mac, and then connect your flash drive, camera, or other standard USB device.
There are many libraries that achieve the job. Here is sample code for three platforms from Usb.Net (https://github.com/MelbourneDeveloper/Device.Net)
Windows (WinUSB)
https://github.com/MelbourneDeveloper/Device.Net/blob/411fea4acfbf965fc0160bf728a46b5ded8abc5d/src/Usb.Net/Windows/WindowsUsbDevice.cs#L33
public override Task InitializeAsync()
{
Dispose();
int errorCode;
if (string.IsNullOrEmpty(DeviceId))
{
throw new WindowsException($"{nameof(DeviceDefinition)} must be specified before {nameof(InitializeAsync)} can be called.");
}
_DeviceHandle = APICalls.CreateFile(DeviceId, (APICalls.GenericWrite | APICalls.GenericRead), APICalls.FileShareRead | APICalls.FileShareWrite, IntPtr.Zero, APICalls.OpenExisting, APICalls.FileAttributeNormal | APICalls.FileFlagOverlapped, IntPtr.Zero);
if (_DeviceHandle.IsInvalid)
{
//TODO: is error code useful here?
errorCode = Marshal.GetLastWin32Error();
if (errorCode > 0) throw new Exception($"Device handle no good. Error code: {errorCode}");
}
var isSuccess = WinUsbApiCalls.WinUsb_Initialize(_DeviceHandle, out var defaultInterfaceHandle);
HandleError(isSuccess, "Couldn't initialize device");
var bufferLength = (uint)Marshal.SizeOf(typeof(USB_DEVICE_DESCRIPTOR));
isSuccess = WinUsbApiCalls.WinUsb_GetDescriptor(defaultInterfaceHandle, WinUsbApiCalls.DEFAULT_DESCRIPTOR_TYPE, 0, 0, out _UsbDeviceDescriptor, bufferLength, out var lengthTransferred);
HandleError(isSuccess, "Couldn't get device descriptor");
byte i = 0;
//Get the first (default) interface
var defaultInterface = GetInterface(defaultInterfaceHandle);
_UsbInterfaces.Add(defaultInterface);
while (true)
{
isSuccess = WinUsbApiCalls.WinUsb_GetAssociatedInterface(defaultInterfaceHandle, i, out var interfacePointer);
if (!isSuccess)
{
errorCode = Marshal.GetLastWin32Error();
if (errorCode == APICalls.ERROR_NO_MORE_ITEMS) break;
throw new Exception($"Could not enumerate interfaces for device {DeviceId}. Error code: { errorCode}");
}
var associatedInterface = GetInterface(interfacePointer);
_UsbInterfaces.Add(associatedInterface);
i++;
}
IsInitialized = true;
RaiseConnected();
return Task.CompletedTask;
}
public override async Task<byte[]> ReadAsync()
{
return await Task.Run(() =>
{
var bytes = new byte[ReadBufferSize];
//TODO: Allow for different interfaces and pipes...
var isSuccess = WinUsbApiCalls.WinUsb_ReadPipe(_DefaultUsbInterface.Handle, _DefaultUsbInterface.ReadPipe.WINUSB_PIPE_INFORMATION.PipeId, bytes, ReadBufferSize, out var bytesRead, IntPtr.Zero);
HandleError(isSuccess, "Couldn't read data");
Tracer?.Trace(false, bytes);
return bytes;
});
}
public override async Task WriteAsync(byte[] data)
{
await Task.Run(() =>
{
if (data.Length > WriteBufferSize)
{
throw new Exception($"Data is longer than {WriteBufferSize} bytes which is the device's max buffer size.");
}
//TODO: Allow for different interfaces and pipes...
var isSuccess = WinUsbApiCalls.WinUsb_WritePipe(_DefaultUsbInterface.Handle, _DefaultUsbInterface.WritePipe.WINUSB_PIPE_INFORMATION.PipeId, data, (uint)data.Length, out var bytesWritten, IntPtr.Zero);
HandleError(isSuccess, "Couldn't write data");
Tracer?.Trace(true, data);
});
}
UWP
https://github.com/MelbourneDeveloper/Device.Net/blob/411fea4acfbf965fc0160bf728a46b5ded8abc5d/src/Usb.Net.UWP/UWPUsbDevice.cs#L24
public override async Task InitializeAsync()
{
await GetDevice(DeviceId);
if (_ConnectedDevice != null)
{
var usbInterface = _ConnectedDevice.Configuration.UsbInterfaces.FirstOrDefault();
if (usbInterface == null)
{
_ConnectedDevice.Dispose();
throw new Exception("There was no Usb Interface found for the device.");
}
var interruptPipe = usbInterface.InterruptInPipes.FirstOrDefault();
if (interruptPipe == null)
{
throw new Exception("There was no interrupt pipe found on the interface");
}
interruptPipe.DataReceived += InterruptPipe_DataReceived;
RaiseConnected();
}
else
{
throw new Exception($"Could not connect to device with Device Id {DeviceId}. Check that the package manifest has been configured to allow this device.");
}
}
public override async Task WriteAsync(byte[] bytes)
{
var bufferToSend = bytes.AsBuffer();
var usbInterface = _ConnectedDevice.Configuration.UsbInterfaces.FirstOrDefault();
var outPipe = usbInterface.InterruptOutPipes.FirstOrDefault();
await outPipe.OutputStream.WriteAsync(bufferToSend);
Tracer?.Trace(false, bytes);
}
public override async Task<byte[]> ReadAsync()
{
if (_IsReading)
{
throw new Exception("Reentry");
}
lock (_Chunks)
{
if (_Chunks.Count > 0)
{
var retVal = _Chunks[0];
Tracer?.Trace(false, retVal);
_Chunks.RemoveAt(0);
return retVal;
}
}
_IsReading = true;
_TaskCompletionSource = new TaskCompletionSource<byte[]>();
return await _TaskCompletionSource.Task;
}
Android
https://github.com/MelbourneDeveloper/Device.Net/blob/411fea4acfbf965fc0160bf728a46b5ded8abc5d/src/Usb.Net.Android/AndroidUsbDevice.cs#L199
public async Task InitializeAsync()
{
//TODO: Use a semaphore lock here
if (_IsInitializing)
{
return;
}
_IsInitializing = true;
try
{
//TODO:
//Dispose();
var isPermissionGranted = await RequestPermissionAsync();
if (!isPermissionGranted.HasValue)
{
throw new Exception("User did not respond to permission request");
}
if (!isPermissionGranted.Value)
{
throw new Exception("The user did not give the permission to access the device");
}
var usbInterface = _UsbDevice.GetInterface(0);
//TODO: This selection stuff needs to be moved up higher. The constructor should take these arguments
for (var i = 0; i < usbInterface.EndpointCount; i++)
{
var ep = usbInterface.GetEndpoint(i);
if (_ReadEndpoint == null && ep.Type == UsbAddressing.XferInterrupt && ep.Address == (UsbAddressing)129)
{
_ReadEndpoint = ep;
continue;
}
if (_WriteEndpoint == null && ep.Type == UsbAddressing.XferInterrupt && (ep.Address == (UsbAddressing)1 || ep.Address == (UsbAddressing)2))
{
_WriteEndpoint = ep;
}
}
//TODO: This is a bit of a guess. It only kicks in if the previous code fails. This needs to be reworked for different devices
if (_ReadEndpoint == null)
{
_ReadEndpoint = usbInterface.GetEndpoint(0);
}
if (_WriteEndpoint == null)
{
_WriteEndpoint = usbInterface.GetEndpoint(1);
}
if (_ReadEndpoint.MaxPacketSize != ReadBufferLength)
{
throw new Exception("Wrong packet size for read endpoint");
}
if (_WriteEndpoint.MaxPacketSize != ReadBufferLength)
{
throw new Exception("Wrong packet size for write endpoint");
}
_UsbDeviceConnection = UsbManager.OpenDevice(_UsbDevice);
if (_UsbDeviceConnection == null)
{
throw new Exception("could not open connection");
}
if (!_UsbDeviceConnection.ClaimInterface(usbInterface, true))
{
throw new Exception("could not claim interface");
}
Logger.Log("Hid device initialized. About to tell everyone.", null, LogSection);
IsInitialized = true;
RaiseConnected();
return;
}
catch (Exception ex)
{
Logger.Log("Error initializing Hid Device", ex, LogSection);
}
_IsInitializing = false;
}
public override async Task<byte[]> ReadAsync()
{
try
{
var byteBuffer = ByteBuffer.Allocate(ReadBufferLength);
var request = new UsbRequest();
request.Initialize(_UsbDeviceConnection, _ReadEndpoint);
request.Queue(byteBuffer, ReadBufferLength);
await _UsbDeviceConnection.RequestWaitAsync();
var buffers = new byte[ReadBufferLength];
byteBuffer.Rewind();
for (var i = 0; i < ReadBufferLength; i++)
{
buffers[i] = (byte)byteBuffer.Get();
}
//Marshal.Copy(byteBuffer.GetDirectBufferAddress(), buffers, 0, ReadBufferLength);
Tracer?.Trace(false, buffers);
return buffers;
}
catch (Exception ex)
{
Logger.Log(Helpers.ReadErrorMessage, ex, LogSection);
throw new IOException(Helpers.ReadErrorMessage, ex);
}
}
public override async Task WriteAsync(byte[] data)
{
try
{
var request = new UsbRequest();
request.Initialize(_UsbDeviceConnection, _WriteEndpoint);
var byteBuffer = ByteBuffer.Wrap(data);
Tracer?.Trace(true, data);
request.Queue(byteBuffer, data.Length);
await _UsbDeviceConnection.RequestWaitAsync();
}
catch (Exception ex)
{
Logger.Log(Helpers.WriteErrorMessage, ex, LogSection);
throw new IOException(Helpers.WriteErrorMessage, ex);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With