采用Android进行通信​​使用USB HID设备通信、设备、Android、HID

由网友(傲风)分享简介:我是新来的USB和Android的,所以请原谅我,如果我不解释自己清楚。我有一个USB HID设备,我可以在Windows与交流。我想用宏碁Iconia A500平板电脑运行Android 3.1建立通信。我能找到该设备,枚举它,得到它的唯一可用的界面,获得唯一可用的端点(0),并确定它是什么类型的端点(传输中断,从设...

我是新来的USB和Android的,所以请原谅我,如果我不解释自己清楚。

我有一个USB HID设备,我可以在Windows与交流。我想用宏碁Iconia A500平板电脑运行Android 3.1建立通信。

我能找到该设备,枚举它,得到它的唯一可用的界面,获得唯一可用的端点(0),并确定它是什么类型的端点(传输中断,从设备到主机)。

我的USB规范的理解是,所有的HID设备都必须在munimum有一个控制端点(端点0)和中断IN端点。但似乎端点0这里是中断端点,而不是控制端点。

然而,在为了使设备枚举它必须成功穿过控制端点转移其描述符数据。我推断,控制端点,因此一定是找到(和使用),因为主机呢,其实,枚举设备。

这是因为就我能够继续,如上所述,psented我在应用程序级别的唯一接口/端点$ P $是一个中断类型,从设备到主机去。没有端点提供给我的应用程序会从主机到设备,中断或控制。因此,该设备等待被告知做什么和主机等待事情发生在设备中。不是很刺激。

记住,当连接到Windows,例如该设备正常启用我能够发送包含13个字节的数据引起该装置点亮的LED的报告。因此它似乎是用USB HID规范相符。作为绝望的举动我都用这一个端点作为两个控制端点,并作为一个中断OUT端点,使用controltransfer()和UsbRequest()将数据提交到设备,在任何情况下,没有任何反应尝试。

所以我的问题是:控制传输端点是用来设置该设备,为什么我不能够找到和放大器;使用它(?)?

感谢您的见解,下面是有关code,我可以将其全部包括其余的如果需要的话:

 私人UsbManager mUsbManager;
私人UsbDevice mDevice;
私人UsbDeviceConnection mConnectionRead;
私人UsbDeviceConnection mConnectionWrite;
私人UsbEndpoint mEndpointRead;
私人UsbEndpoint mEndpointWrite;

    //检查现有设备
    对于(UsbDevice设备:mUsbManager.getDeviceList()值())
    {
        //需要为我的设备筛选器,当其他的HID也连接,但现在......
        字符串DEVNAME = device.getDeviceName();
        如果(DEBUG == 1){
        Toast.makeText(UsbHidDeviceTesterActivity.this,我的设备得到连接:+ DEVNAME,Toast.LENGTH_LONG).show();
        }
        // mDevice =设备;
        setHIDDevice(设备);
    }

私人布尔setHIDDevice(UsbDevice设备)
{
    的UsbInterface usbInterfaceRead = NULL;
    的UsbInterface usbInterfaceWrite = NULL;
    UsbEndpoint EP1 = NULL;
    UsbEndpoint EP2 = NULL;
    布尔UsingSingleInterface =真;

    mDevice =设备;

    //这HID设备使用单个接口
    如果(UsingSingleInterface)
    {
        // usbInterfaceRead = device.getInterface(0×00); //只有1 EP​​在这个接口上
        usbInterfaceRead = findInterface(设备);

        //尝试获得下一个索引接口
        // usbInterfaceWrite = device.getInterface(0×01); //抛出异常

        //尝试使用相同的接口进行读写
        usbInterfaceWrite = usbInterfaceRead;

        INT endPointCount = usbInterfaceWrite.getEndpointCount();
        如果(DEBUG == 2)
        {
            Toast.makeText(UsbHidDeviceTesterActivity.this,终点:+ endPointCount,Toast.LENGTH_LONG).show();
            //Toast.makeText(UsbHidDeviceTesterActivity.this,接口+ usbInterfaceRead,Toast.LENGTH_LONG).show();
        }

        如果(endPointCount == 1)//只得到1端点
        {
            EP1 = usbInterfaceRead.getEndpoint(0);
            //作为绝望的举动试图等同EP2这个阅读EP,让大家以后可以尝试这么写吧
            EP2 = usbInterfaceRead.getEndpoint(0);
        }
        否则,如果(endPointCount == 2)
        {
            EP1 = usbInterfaceRead.getEndpoint(0);
            EP2 = usbInterfaceRead.getEndpoint(1);
        }
    }

    其他        // ! UsingSingleInterface
    {
        usbInterfaceRead = device.getInterface(0×00);
        usbInterfaceWrite = device.getInterface(0×01);
        如果((usbInterfaceRead.getEndpointCount()== 1)及及(usbInterfaceWrite.getEndpointCount()== 1))
        {
            EP1 = usbInterfaceRead.getEndpoint(0);
            EP2 = usbInterfaceWrite.getEndpoint(0);
        }
        如果(DEBUG == 3)
        {
            Toast.makeText(UsbHidDeviceTesterActivity.this,使用双界面,Toast.LENGTH_LONG).show();
        }
    }

    //因为EP1 EP2 =这将现在不会导致回报除非没有EP被发现在所有
    如果((EP1 == NULL)||(EP2 == NULL))
    {
        如果(DEBUG == 4)
        {
            Toast.makeText(UsbHidDeviceTesterActivity.this,一个EP为空,Toast.LENGTH_LONG).show();
        }
        返回false;
    }

    //确定哪个端点是读,并且其写
    如果(ep1.getType()== UsbConstants.USB_ENDPOINT_XFER_INT)//我正回报3,它是一个中断传送
    {
        如果(ep1.getDirection()== UsbConstants.USB_DIR_IN)//我正返回的128,这是一个设备到主机端点
        {
            mEndpointRead = EP1;
            如果(DEBUG == 5)
            {
                Toast.makeText(UsbHidDeviceTesterActivity.this,EP1类型:+ ep1.getType(),Toast.LENGTH_LONG).show();
            }
        }
        如果(ep1.getDirection()== UsbConstants.USB_DIR_OUT)//没了
        {
            mEndpointWrite = EP1;
            如果(DEBUG == 6)
            {
                Toast.makeText(UsbHidDeviceTesterActivity.this,EP1是写,Toast.LENGTH_LONG).show();
            }
        }
    }

    如果(ep2.getType()== UsbConstants.USB_ENDPOINT_XFER_INT)
    {
        如果(ep2.getDirection()== UsbConstants.USB_DIR_IN)
        {
            //尝试把它当作一个写呢
            // mEndpointRead = EP2;
            mEndpointWrite = EP2;
        }
        否则,如果(ep2.getDirection()== UsbConstants.USB_DIR_OUT)
        {
            // usbEndpointWrite = EP2;
            mEndpointWrite = EP2;
        }
    }

    //检查,我们应该能够读取和写入
    如果((mEndpointRead == NULL)||(mEndpointWrite == NULL))
    {
        返回false;
    }
    如果(设备!= NULL)
    {
        UsbDeviceConnection连接= mUsbManager.openDevice(设备);
        如果(连接= NULL和放大器;!&安培; connection.claimInterface(usbInterfaceRead,真))
        {
            Log.d(TAG,开放式成功);
            mConnectionRead =连接;
            //开始读线程
            //注释掉一边拼命尝试这个连接/接口上写
            //线程的线程=新主题(本);
            //thread.start();

        }
        其他
        {
            Log.d(TAG,开放FAIL);
            mConnectionRead = NULL;
        }
     }
    如果(UsingSingleInterface)
    {
        mConnectionWrite = mConnectionRead;
    }
    其他 //! UsingSingleInterface
    {
        mConnectionWrite = mUsbManager.openDevice(设备);
        mConnectionWrite.claimInterface(usbInterfaceWrite,真正的);
    }
    返回true;
}

//搜索给定USB设备上的接口
 私人的UsbInterface findInterface(UsbDevice装置){
    Log.d(TAG,findInterface+设备);
    诠释计数= device.getInterfaceCount();
    如果(DEBUG == 7)
    {
        Toast.makeText(UsbHidDeviceTesterActivity.this,接口数:+计数,Toast.LENGTH_LONG).show();
    }

    的for(int i = 0; I<计数;我++){
        的UsbInterface INTF = device.getInterface(我);
        串InterfaceInfo = intf.toString();
        Log.d(TAG,接口+ InterfaceInfo);
        //类下面是3 USB_HID
        如果(intf.getInterfaceClass()== 3&安培;&安培; intf.getInterfaceSubclass()== 0&安培;&安培;
                intf.getInterfaceProtocol()== 0){
            返回INTF;
        }
        不管//....try只返回类/子类的接口
        //返回INTF;
    }

    返回null;
}
 私人布尔sendControlTransfer(byte []的dataToSend)
 {
    同步(这)
    {
    如果(mConnectionRead!= NULL)
     {
        // byte []的消息=新的字节[13]; //还是14?
        byte []的消息= dataToSend;
         如果(DEBUG == 9)
         {
             Toast.makeText(UsbHidDeviceTesterActivity.this,发送控制转移,Toast.LENGTH_LONG).show();
         }

         //第一场ox21是斌00100001其分成0 01 00001为方向(1位)/类型(2B)/收件人(5B)
         //设置方向为主机设备我们需要0,设置类型为HID,我们需要11(3),并为受援国我们要00001
         //第二场×09是一流的具体要求code,×09被列为保留以备将来使用
         //第三个字段为0x200是价值
         // INT转印= mConnectionRead.controlTransfer(0×21,0x9,为0x200,0,消息,message.length,0);
         //尝试设置为HID类型
         INT转印= mConnectionRead.controlTransfer(0xC1,0x9,为0x200,0,消息,message.length,0);
         如果(DEBUG == 10)
         {
             Toast.makeText(UsbHidDeviceTesterActivity.this,传送返回+转移,Toast.LENGTH_LONG).show();
         }
     }
    }
    返回true;
 }


私人布尔sendInterruptTransfer(byte []的dataToSend)
{
    INT bufferDataLength = mEndpointWrite.getMaxPacketSize(); //写端点为空,除非我们刚才复制的读端点
    如果(DEBUG == 12)
    {
        Toast.makeText(UsbHidDeviceTesterActivity.this,最大数据包大小:+ bufferDataLength,Toast.LENGTH_LONG).show();
    }

    ByteBuffer的缓冲= ByteBuffer.allocate(bufferDataLength + 1);
    UsbRequest请求=新UsbRequest();
    buffer.put(dataToSend);

    request.initialize(mConnectionWrite,mEndpointWrite);
    request.queue(缓冲,bufferDataLength);

    尝试
    {
        / *只使用requestwait在读
        如果(request.equals(mConnectionWrite.requestWait()))
        {
            返回true;
        }
        * /
    }
    赶上(例外前)
    {
        //异常发生
        如果(DEBUG == 13)
        {
            Toast.makeText(UsbHidDeviceTesterActivity.this,中招编写异常,Toast.LENGTH_LONG).show();
        }
    }

    返回true;
}
 

解决方案

所以,我一直在研究类似的事情。我不能肯定,但我认为正在发生的事情是:

在Android的没有列出控制端点时,它列举了它的终点。它仅列出了其他端点。 的连接到任何终端可以发送控制转移到端点0,通过controlTransfer方法,该方法(从API​​引用)执行端点零该装置的控制交易。 所以,在你上面的code,我会用0号端点为中断输入端点,但它仍然允许控制传输。 在有人使用HID设备是导弹武器发射演示的一个例子,它使用的设备是一个中断端点的HID设备。

I am new to USB and to Android so please forgive me if I don't explain myself clearly.

I have a USB HID device that I can communicate with in Windows. I am trying to establish communication using an Acer Iconia A500 tablet running Android 3.1.

I am able to find the device, enumerate it, get its only available interface, get the only available endpoint (0), and determine what type of endpoint it is (transfer interrupt from device to host).

My understanding of the USB spec is that all HID devices are required at a munimum to have a control endpoint (Endpoint 0) and an interrupt IN endpoint. But it seems that endpoint 0 here is the interrupt In endpoint, not the control endpoint.

Yet in order for the device to enumerate it must successfully transfer its descriptor data across the control endpoint. I deduce that the control endpoint therefore must be getting found (and used) because the host does, in fact, enumerate the device.

This is as far as I am able to proceed, as stated above, the only interface/endpoint presented to me at the application level is an interrupt type going from device to host. No endpoint available to my app going from host to device, interrupt or control. So the device waits to be told what to do and the host waits for something to happen in the device. Not very stimulating.

Bear in mind that this device responds properly when connected to Windows, e.g. I am able to send a report containing 13 bytes of data that causes the device to light an LED. So it seems to be complying with the USB HID spec. As an act of desperation I have tried using this one endpoint as both a control endpoint and as a interrupt OUT endpoint, using controltransfer() and UsbRequest() to submit the data to the device, no response in either case.

So my question is: "The control transfer endpoint is (?) being used to set up the device, why am I not able to find & use it?"

Thanks for any insight, below is the relevant code, I can include the rest in its entirety if needed:

private UsbManager mUsbManager;
private UsbDevice mDevice;
private UsbDeviceConnection mConnectionRead;
private UsbDeviceConnection mConnectionWrite;
private UsbEndpoint mEndpointRead;
private UsbEndpoint mEndpointWrite;

    // check for existing devices
    for (UsbDevice device :  mUsbManager.getDeviceList().values())
    {
        //Need to filter for my device when other HIDs are also connected, but for now...           
        String devName = device.getDeviceName();
        if (DEBUG == 1){
        Toast.makeText(UsbHidDeviceTesterActivity.this, "My device got connected: " + devName, Toast.LENGTH_LONG).show();
        }
        //mDevice = device;
        setHIDDevice(device);
    }

private boolean setHIDDevice(UsbDevice device)
{    
    UsbInterface usbInterfaceRead = null;
    UsbInterface usbInterfaceWrite = null;
    UsbEndpoint ep1 = null;
    UsbEndpoint ep2 = null;
    boolean UsingSingleInterface = true;

    mDevice = device;

    //This HID device is using a single interface
    if (UsingSingleInterface)
    {
        //usbInterfaceRead = device.getInterface(0x00);//only 1 EP on this interface
        usbInterfaceRead = findInterface(device);

        //Try getting an interface at next index
        //usbInterfaceWrite = device.getInterface(0x01);//throws exception

        // Try using the same interface for reading and writing
        usbInterfaceWrite = usbInterfaceRead;

        int endPointCount = usbInterfaceWrite.getEndpointCount();
        if (DEBUG == 2)
        {
            Toast.makeText(UsbHidDeviceTesterActivity.this, "Endpoints: " + endPointCount, Toast.LENGTH_LONG).show();
            //Toast.makeText(UsbHidDeviceTesterActivity.this, "Interface: " + usbInterfaceRead, Toast.LENGTH_LONG).show();
        }

        if (endPointCount == 1)//only getting 1 endpoint
        {
            ep1 = usbInterfaceRead.getEndpoint(0);
            //As an act of desperation try equating ep2 to this read EP, so that we can later attempt to write to it anyway
            ep2 = usbInterfaceRead.getEndpoint(0);
        }
        else if (endPointCount == 2)
        {
            ep1 = usbInterfaceRead.getEndpoint(0);
            ep2 = usbInterfaceRead.getEndpoint(1);
        }
    }

    else        // ! UsingSingleInterface
    {
        usbInterfaceRead = device.getInterface(0x00);
        usbInterfaceWrite = device.getInterface(0x01);
        if ((usbInterfaceRead.getEndpointCount() == 1) && (usbInterfaceWrite.getEndpointCount() == 1))
        {
            ep1 = usbInterfaceRead.getEndpoint(0);
            ep2 = usbInterfaceWrite.getEndpoint(0);
        }
        if (DEBUG == 3)
        {
            Toast.makeText(UsbHidDeviceTesterActivity.this, "Using Dual Interface", Toast.LENGTH_LONG).show();
        }
    }

    //because ep1 = ep2 this will now not cause a return unless no ep is found at all
    if ((ep1 == null) || (ep2 == null))
    {
        if (DEBUG == 4)
        {
            Toast.makeText(UsbHidDeviceTesterActivity.this, "One EP is null", Toast.LENGTH_LONG).show();
        }
        return false;
    }

    // Determine which endpoint is the read, and which is the write
    if (ep1.getType() == UsbConstants.USB_ENDPOINT_XFER_INT)//I am getting a return of 3, which is an interrupt transfer
    {
        if (ep1.getDirection() == UsbConstants.USB_DIR_IN)//I am getting a return of 128, which is a device-to-host endpoint
        {
            mEndpointRead = ep1;
            if (DEBUG == 5)
            {
                Toast.makeText(UsbHidDeviceTesterActivity.this, "EP1 type: " + ep1.getType(), Toast.LENGTH_LONG).show();
            }
        }
        if (ep1.getDirection() == UsbConstants.USB_DIR_OUT)//nope
        {
            mEndpointWrite = ep1;
            if (DEBUG == 6)
            {
                Toast.makeText(UsbHidDeviceTesterActivity.this, "EP1 is a write", Toast.LENGTH_LONG).show();
            }
        }
    }

    if (ep2.getType() == UsbConstants.USB_ENDPOINT_XFER_INT)
    {
        if (ep2.getDirection() == UsbConstants.USB_DIR_IN)
        {
            //Try treating it as a write anyway             
            //mEndpointRead = ep2;
            mEndpointWrite = ep2;
        }
        else if (ep2.getDirection() == UsbConstants.USB_DIR_OUT)
        {
            //usbEndpointWrite = ep2;
            mEndpointWrite = ep2;
        }
    }

    //check that we should be able to read and write
    if ((mEndpointRead == null) || (mEndpointWrite == null))
    {
        return false;
    }
    if (device != null)
    {
        UsbDeviceConnection connection = mUsbManager.openDevice(device);
        if (connection != null && connection.claimInterface(usbInterfaceRead, true))
        {
            Log.d(TAG, "open SUCCESS");
            mConnectionRead = connection;
            // Start the read thread
            //Comment out while desperately attempting to write on this connection/interface
            //Thread thread = new Thread(this);
            //thread.start();

        }
        else
        {
            Log.d(TAG, "open FAIL");
            mConnectionRead = null;
        }
     }
    if (UsingSingleInterface)
    {
        mConnectionWrite = mConnectionRead;
    }
    else //! UsingSingleInterface
    {
        mConnectionWrite = mUsbManager.openDevice(device);
        mConnectionWrite.claimInterface(usbInterfaceWrite, true);
    }
    return true;
}

// searches for an interface on the given USB device
 private UsbInterface findInterface(UsbDevice device) {
    Log.d(TAG, "findInterface " + device);
    int count = device.getInterfaceCount();
    if (DEBUG == 7)
    {
        Toast.makeText(UsbHidDeviceTesterActivity.this, "Interface count: " + count, Toast.LENGTH_LONG).show();
    }

    for (int i = 0; i < count; i++) {
        UsbInterface intf = device.getInterface(i);
        String InterfaceInfo = intf.toString();
        Log.d(TAG, "Interface: " + InterfaceInfo);
        //Class below is 3 for USB_HID
        if (intf.getInterfaceClass() == 3 && intf.getInterfaceSubclass() == 0 &&
                intf.getInterfaceProtocol() == 0) {
            return intf;
        }
        //....try just returning the interface regardless of class/subclass
        //return intf;
    }

    return null;
} 
 private boolean sendControlTransfer(byte[] dataToSend)
 {
    synchronized (this)
    { 
    if (mConnectionRead != null)
     { 
        //byte[] message = new byte[13];  // or 14?
        byte[] message = dataToSend;
         if (DEBUG == 9)
         {
             Toast.makeText(UsbHidDeviceTesterActivity.this, "Sending Control Transfer", Toast.LENGTH_LONG).show();
         } 

         //first field ox21 is bin 00100001 which splits into 0 01 00001 for direction(1bit)/type(2b)/recipient(5b)
         //To set direction as 'host to Device' we need 0, To set type to HID we need 11 (3), and for recipient we want 00001
         //second field 0x09 is class specific request code, 0x09 is listed as 'reserved for future use'
         //third field 0x200 is value
         //int transfer = mConnectionRead.controlTransfer(0x21, 0x9, 0x200, 0, message, message.length, 0);
         //try with type set to HID
         int transfer = mConnectionRead.controlTransfer(0xC1, 0x9, 0x200, 0, message, message.length, 0);
         if (DEBUG == 10)
         {
             Toast.makeText(UsbHidDeviceTesterActivity.this, "Transfer returned " + transfer, Toast.LENGTH_LONG).show();
         }
     } 
    }
    return true;
 }


private boolean sendInterruptTransfer(byte[] dataToSend)
{ 
    int bufferDataLength = mEndpointWrite.getMaxPacketSize();//The write endpoint is null unless we just copy the read endpoint
    if (DEBUG == 12)
    {
        Toast.makeText(UsbHidDeviceTesterActivity.this, "Max Packet Size: " + bufferDataLength, Toast.LENGTH_LONG).show();
    }

    ByteBuffer buffer = ByteBuffer.allocate(bufferDataLength + 1);
    UsbRequest request = new UsbRequest();
    buffer.put(dataToSend);

    request.initialize(mConnectionWrite, mEndpointWrite);
    request.queue(buffer, bufferDataLength);

    try
    {
        /* only use requestwait on a read
        if (request.equals(mConnectionWrite.requestWait()))
        {
            return true;
        }
        */
    }
    catch (Exception ex)
    {
        // An exception has occurred
        if (DEBUG == 13)
        {
            Toast.makeText(UsbHidDeviceTesterActivity.this, "Caught Write Exception", Toast.LENGTH_LONG).show();
        }
    }

    return true;
}   

解决方案

So, I have been researching similar things. I cannot confirm, but what I believe is happening is:

Android does not list the control endpoint when it enumerates it's endpoints. It only lists other endpoints. A connection to any endpoint can send control transfers to endpoint 0, through the controlTransfer method, which (quoting from the api) "Performs a control transaction on endpoint zero for this device." So, in your above code, I would use the 0th endpoint as an interrupt input endpoint, but it will still allow for control transfers. An example of someone using a HID device is the Missle Launcher demo, the device it uses is a HID device with an interrupt endpoint.

阅读全文

相关推荐

最新文章