Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access USB port from C# and send and receive data

Tags:

c#

usb

How to access a USB port in C#, send a data stream and receive other data?

like image 797
kartal Avatar asked Nov 24 '10 13:11

kartal


People also ask

Can you connect USB to USB-C?

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.


1 Answers

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);
        }
    }
like image 64
Christian Findlay Avatar answered Sep 19 '22 21:09

Christian Findlay