1
votes

I have a USB HID device that I would like to communicate with. I am successfully doing so on Windows using the HidSharp library (link: https://github.com/treehopper-electronics/HIDSharp). My Windows application is developed using the .NET Framework 4.5, C#, and Visual Studio.

I now want to communicate with this same USB HID device from an Android tablet instead of from the Windows desktop. I am encountering some problems doing so. When I have the device plugged in to my tablet, it reports a single interface with a single "read" endpoint. Here is what is reported to me:

Interface #0 
Class: Human Interaction Device (0x3)
Endpoint: #0
Address        : 0x81 (10000001)
Number         : 1
Direction      : Inbound (0x80)
Type           : Intrrupt (0x3)
Poll Interval  : 1
Max Packet Size: 64
Attributes     : 000000011

As you can see, it only reports a single endpoint, which is an inbound endpoint. I need to be able to output simple commands to this device, which I was able to do so successfully on Windows using HidSharp.

HidSharp abstracted everything into a single "stream" object that you could read from and write to. Using the Android APIs, there isn't a single "stream" object, but rather there seem to be 3 different ways of reading/writing: bulk transfer, control transfer, and USB Request. I've tried sending out data using all 3, but with seemingly no success.

Any suggestions on what to do? Is there a reason why I could send out data to this device on Windows, but seemingly cannot do so from Android? Is there a way to use a single endpoint as both a read and a write endpoint? Is there something that I am just obviously missing and not understanding?

I am using Xamarin as my development environment (C#, Visual Studio 2017). Since code is always helpful, here is how I am connecting to the device:

int VendorID = 0x04d8;
int ProductID = 0x2742;
UsbManager USB_Manager = null;
UsbDevice USB_Device = null;
UsbDeviceConnection DeviceConnection = null;
UsbInterface DeviceInterface = null;
UsbEndpoint OutputEndpoint = null;
UsbEndpoint InputEndpoint = null;

//Grab the Android USB manager and get a list of connected devices
var USB_Manager = MyMainActivity.ApplicationContext.GetSystemService(Android.Content.Context.UsbService) as Android.Hardware.Usb.UsbManager;
var attached_devices = USB_Manager.DeviceList;

//Find the device in the list of connected devices
foreach (var d in attached_devices.Keys)
{
    if (attached_devices[d].VendorId == VendorID && attached_devices[d].ProductId == ProductID)
    {
        USB_Device = attached_devices[d];
        break;
    }
}

//Assuming we found the correct device, let's set everything up
if (USB_Device != null)
{
    for (int j = 0; j < USB_Device.InterfaceCount; j++)
    {
        DeviceInterface = USB_Device.GetInterface(j);
        for (int i = 0; i < DeviceInterface.EndpointCount; i++)
        {
            var temp_ep = DeviceInterface.GetEndpoint(i);
            if (temp_ep.Type == Android.Hardware.Usb.UsbAddressing.XferInterrupt)
            {
                if (temp_ep.Direction == Android.Hardware.Usb.UsbAddressing.In)
                {
                    InputEndpoint = temp_ep;
                }

                if (temp_ep.Direction == Android.Hardware.Usb.UsbAddressing.Out)
                {
                    OutputEndpoint = temp_ep;
                }
            }
        }
    }

    //Request permission to communicate with this USB device
    UsbReceiver receiver = new UsbReceiver();
    PendingIntent pending_intent = PendingIntent.GetBroadcast(Game.Activity, 0, new Android.Content.Intent(UsbReceiver.ACTION_USB_PERMISSION), 0);
    IntentFilter intent_filter = new IntentFilter(UsbReceiver.ACTION_USB_PERMISSION);
    Game.Activity.RegisterReceiver(receiver, intent_filter);
    USB_Manager.RequestPermission(USB_Device, pending_intent);
    bool has_permission = USB_Manager.HasPermission(USB_Device);
    var device_connection = USB_Manager.OpenDevice(USB_Device);
    device_connection.ClaimInterface(DeviceInterface, true);
    DeviceConnection = device_connection;
}

Next, here is how I attempt to read from the device:

//3 methods of attempting to read from the device

//Method 1:
byte[] inpt = new byte[64];
var request = new UsbRequest();
request.Initialize(DeviceConnection, InputEndpoint);
var byte_buffer = ByteBuffer.Allocate(64);
request.Queue(byte_buffer, 64);
DeviceConnection.RequestWait();
byte_buffer.Rewind();
for(int i = 0; i < 64; i++)
{
    inpt[i] = (byte) byte_buffer.Get();
}

//Method 2:
byte[] inpt = new byte[64];
DeviceConnection.BulkTransfer(InputEndpoint, inpt, inpt.Length, 1000);

//Method 3:
byte[] inpt = new byte[64];
DeviceConnection.ControlTransfer(UsbAddressing.In, 0, 0, 0, inpt, 64, 1000);

And finally, here is how I attempt to write data to this device:

//Method 1:
byte[] output_msg; //This variable is assigned elsewhere in the code
DeviceConnection.BulkTransfer(OutputEndpoint, output_msg, output_msg.Length, 30);

//Method 2:
byte[] output_msg; //This variable is assigned elsewhere in the code
DeviceConnection.ControlTransfer(UsbAddressing.Out, 0, 0, 0, output_msg, output_msg.Length, 1000);

//Method 3:
byte[] output_msg; //This variable is assigned elsewhere in the code
var write_request = new UsbRequest();
write_request.Initialize(DeviceConnection, OutputEndpoint);
var byte_buffer_write = ByteBuffer.Wrap(output_msg);
request.Queue(byte_buffer_write, output_msg.Length);
DeviceConnection.RequestWait();

"OutputEndpoint" is typically null because there is no output endpoint, so I often replace "OutputEndpoint" with "InputEndpoint", but with no success.

Any help would be greatly appreciated! Thanks!!!

1
did you find a solution?user924

1 Answers

1
votes

You are dealing with HID devices which means you should do Interrupt Transfers.

In Android, you should use UsbRequest to perform Interrupt Transfers (as it does Asynchronous NonBlocking IO).

The endpoints are unidirectional and can be used for both inbounds and outbound (but not at the same time)

If the endpoint is inbound then submit the Urb using UsbRequest and queue as you tried before but using empty buffer with expected bufferLength.

The RequestWait will return UsbRequest Object back upon completion.

If the usbRequest.getEndPoint().getDirection() is inbound then your buffer variable will be updated with read buffer from the device. If the usbRequest.getEndpoint().getDirection() is outbound then you should pass your buffer to write data to the device