使用Android与USB HID设备通信
我是USB和Android新手,所以如果我没有解释清楚,请原谅我 我有一个USB HID设备,可以在Windows中与之通信。我正在尝试使用运行安卓3.1的Acer Iconia A500平板电脑建立通信 我能够找到设备,枚举它,获取它唯一可用的接口,获取唯一可用的端点(0),并确定它是什么类型的端点(将中断从设备传输到主机) 我对USB规范的理解是,所有的HID设备都需要在一个总线上有一个控制端点(端点0)和一个端点中断。但这里的端点0似乎是端点中的中断,而不是控制端点 但是,为了让设备枚举,它必须成功地跨控制端点传输其描述符数据。我推断,因此必须找到(并使用)控制端点,因为主机实际上枚举了设备 就我所能做的而言,如上所述,在应用程序级别提供给我的唯一接口/端点是从设备到主机的中断类型。从主机到设备、中断或控制,我的应用程序没有可用的端点。因此,设备等待被告知要做什么,主机等待设备中发生什么。不太刺激 请记住,当连接到Windows时,此设备会正确响应,例如,我能够发送包含13字节数据的报告,导致设备点亮LED。因此,它似乎符合USB HID规范。作为一种绝望的行为,我尝试使用这一端点作为控制端点和中断输出端点,使用controltransfer()和UsbRequest()将数据提交到设备,在任何情况下都没有响应 所以我的问题是:“控制传输端点(?)正在用于设置设备,为什么我无法找到并使用它?” 感谢您提供的见解,以下是相关代码,如果需要,我可以将其余部分全部包括在内:使用Android与USB HID设备通信,android,usb,hid,Android,Usb,Hid,我是USB和Android新手,所以如果我没有解释清楚,请原谅我 我有一个USB HID设备,可以在Windows中与之通信。我正在尝试使用运行安卓3.1的Acer Iconia A500平板电脑建立通信 我能够找到设备,枚举它,获取它唯一可用的接口,获取唯一可用的端点(0),并确定它是什么类型的端点(将中断从设备传输到主机) 我对USB规范的理解是,所有的HID设备都需要在一个总线上有一个控制端点(端点0)和一个端点中断。但这里的端点0似乎是端点中的中断,而不是控制端点 但是,为了让设备枚举,
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;
}
私人UsbManager mUsbManager;
私人USB设备;
专用UsbDeviceConnection mConnectionRead;
专用UsbDeviceConnection mConnectionWrite;
私人UsbEndpoint mEndpointRead;
私人UsbEndpoint mEndpointWrite;
//检查现有设备
对于(UsbDevice设备:mUsbManager.getDeviceList().values())
{
//当其他HID也连接时,需要筛选我的设备,但目前。。。
字符串devName=device.getDeviceName();
如果(调试==1){
Toast.makeText(UsbHidDeviceTesterActivity.this,“我的设备已连接:”+devName,Toast.LENGTH_LONG.show();
}
//mDevice=设备;
设定装置(装置);
}
专用布尔setHIDDevice(UsbDevice设备)
{
UsbInterface usbInterfaceRead=null;
UsbInterface usbInterfaceWrite=null;
UsbEndpoint ep1=null;
UsbEndpoint ep2=null;
布尔值UsingSingleInterface=true;
mDevice=设备;
//此HID设备使用单一接口
if(使用SingleInterface)
{
//usbInterfaceRead=device.getInterface(0x00);//此接口上只有1个EP
usbInterfaceRead=findInterface(设备);
//尝试在下一个索引处获取接口
//usbInterfaceWrite=device.getInterface(0x01);//引发异常
//尝试使用相同的界面进行读写
usbInterfaceWrite=usbInterfaceRead;
int endPointCount=usbInterfaceWrite.getEndpointCount();
如果(调试==2)
{
Toast.makeText(usbhiddevictesteractivity.this,“Endpoints:+endPointCount,Toast.LENGTH_LONG).show();
//Toast.makeText(UsbHidDeviceTesterActivity.this,“接口:”+usbInterfaceRead,Toast.LENGTH_LONG.show();
}
if(endPointCount==1)//仅获取1个端点
{
ep1=usbInterfaceRead.getEndpoint(0);
//作为一种绝望的行为,试着将ep2等同于这个read EP,这样我们以后就可以尝试写入它了
ep2=usbInterfaceRead.getEndpoint(0);
}
else if(endPointCount==2)
{
ep1=usbInterfaceRead.getEndpoint(0);
ep2=usbInterfaceRead.getEndpoint(1);
}
}
else/!使用SingleInterface
{
usbInterfaceRead=device.getInterface(0x00);
usbInterfaceWrite=device.getInterface(0x01);
如果((usbInterfaceRead.getEndpointCount()==1)和&(usbInterfaceWrite.getEndpointCount()==1))
{
ep1=usbInterfaceRead.getEndpoint(0);
ep2=usbInterfaceWrite.getEndpoint(0);
}
如果(调试==3)
{
Toast.makeText(usbhiddevictesteractivity.this,“使用双接口”,Toast.LENGTH\u LONG.show();
}
}
//因为ep1=ep2,除非根本找不到ep,否则现在不会导致返回
如果((ep1==null)| |(ep2==null))
{
如果(调试==4)
{
Toast.makeText(UsbHidDeviceTesterActivity.this,“一个EP为空”,Toast.LENGTH_LONG.show();
}
返回false;
}
//确定哪个端点是读取端点,哪个端点是写入端点
如果(ep1.getType()==UsbConstants.USB\u ENDPOINT\u XFER\u INT)//我将得到一个3的返回值,这是一个中断传输
{
if(ep1.getDirection()==UsbConstants.USB\u DIR\u IN)//我得到的返回值是128,这是一个设备到主机的端点
{
mEndpointRead=ep1;
如果(调试==5)
{
Toast.makeText(UsbHidDeviceTesterActivity.this,“EP1类型:”+EP1.getType(),Toast.LENGTH_LONG.show();
}
}
if(ep1.getDirection()==UsbConstants.USB\u DIR\u OUT)//nope
{
mEndpointWrite=ep1;
如果(调试==6)
{
Toast.makeText(UsbHidDeviceTesterActivity.this,“EP1是写入”,Toast.LENGTH
UsbManager mManager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = mManager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while (deviceIterator.hasNext())
{
UsbDevice device = deviceIterator.next();
Log.i(TAG,"Model: " + device.getDeviceName());
Log.i(TAG,"ID: " + device.getDeviceId());
Log.i(TAG,"Class: " + device.getDeviceClass());
Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
Log.i(TAG,"Vendor ID " + device.getVendorId());
Log.i(TAG,"Product ID: " + device.getProductId());
Log.i(TAG,"Interface count: " + device.getInterfaceCount());
Log.i(TAG,"---------------------------------------");
// Get interface details
for (int index = 0; index < device.getInterfaceCount(); index++)
{
UsbInterface mUsbInterface = device.getInterface(index);
Log.i(TAG," ***** *****");
Log.i(TAG," Interface index: " + index);
Log.i(TAG," Interface ID: " + mUsbInterface.getId());
Log.i(TAG," Inteface class: " + mUsbInterface.getInterfaceClass());
Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
// Get endpoint details
for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
{
UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
Log.i(TAG," ++++ ++++ ++++");
Log.i(TAG," Endpoint index: " + epi);
Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
Log.i(TAG," Direction: " + mEndpoint.getDirection());
Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
Log.i(TAG," Interval: " + mEndpoint.getInterval());
Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
Log.i(TAG," Type: " + mEndpoint.getType());
}
}
}
Log.i(TAG," No more devices connected.");
}
//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);
int transfer = mConnectionRead.controlTransfer(0xA1, 0x01, 0x00, 0x01, message, message.length, 0);
int transfer = mConnectionWrite.controlTransfer(0x21, 0x09, 0x00, 0x01, message, message.length, 0);
int transfer = bulkTransfer (ep1, message, message.length, 0);