我是USB和Android的新手,如果我不清楚地解释自己,请原谅我 .
我有一个USB HID设备,我可以在Windows中与之通信 . 我正在尝试使用运行Android 3.1的Acer Iconia A500平板电脑 Build 通信 .
我能够找到设备,枚举它,获得它唯一可用的接口,获得唯一可用的 endpoints (0),并确定它是什么类型的 endpoints (从设备到主机传输中断) .
我对USB规范的理解是,所有HID设备都需要具有控制 endpoints ( endpoints 0)和中断IN endpoints . 但似乎这里的 endpoints 0是中断输入 endpoints ,而不是控制 endpoints .
然而,为了使设备枚举,它必须成功地在控制 endpoints 上传输其描述符数据 . 因此,我推断出控制 endpoints 必须被找到(并使用),因为主机实际上枚举了设备 .
这是我能够继续进行的,如上所述,在应用程序级别呈现给我的唯一接口/ endpoints 是从设备到主机的中断类型 . 我的应用程序没有可用的 endpoints 从主机到设备,中断或控制 . 因此,设备等待被告知该做什么,并且主机等待设备中发生的事情 . 不是很刺激 .
请记住,此设备在连接到Windows时会正确响应,例如我能够发送包含13个字节数据的报告,该报告使设备点亮LED . 所以它似乎符合USB HID规范 . 作为一种绝望的行为,我尝试将这一个 endpoints 用作控制 endpoints 和中断OUT endpoints ,使用controltransfer()和UsbRequest()将数据提交给设备,在任何一种情况下都没有响应 .
所以我的问题是:“控制传输 endpoints 是(?)用于设置设备,为什么我无法找到并使用它?”
感谢您的任何见解,以下是相关代码,如果需要,我可以将其余内容全部包括在内:
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;
}
3 回答
所以,我一直在研究类似的事情 . 我无法证实,但我认为正在发生的事情是:
Android在枚举控制 endpoints 的 endpoints 时未列出控制 endpoints . 它仅列出其他 endpoints .
与任何 endpoints 的连接可以通过controlTransfer方法将控制传输发送到 endpoints 0,该方法(引自api)"Performs a control transaction on endpoint zero for this device."
因此,在上面的代码中,我将使用第0个 endpoints 作为中断输入 endpoints ,但它仍然允许控制传输 .
使用HID设备的人的一个例子是Missle Launcher演示,它使用的设备是具有中断 endpoints 的HID设备 .
您可以使用以下命令获取接口和 endpoints 的详细信息的完整列表:
对于进出传输,控制传输不显示任何接口描述符,并且其 endpoints 号默认为0 .
如果你有其他接口,那些接口的索引应该从0开始,即默认控制传输接口不计数 .
因此,您的接口0保存 endpoints 1描述符 . 使用UsbEndpoint方法查找 endpoints 的属性,无论它是否为中断类型 . 如果是,那么UsbEndpoint.getType()的 endpoints 类型应该返回0x03,并且UsbEndpoint.getEndpointNumber()的 endpoints 号应该返回0x81,这是 endpoints 1的通常值 .
在你的代码下面是错误的:
类型2比特用于指示类特定请求,即其值为01,0x09是特定于Hid类的请求SET_REPORT,而不是保留 . value是用作Hid类的报告ID的wValue,对于您的情况,它可能是0,如果您只有一个HID描述符报告 . 第四个参数是wIndex,它应该用来表示收件人,对于你的情况,接口作为收件人应该是0x01 .
因此,用于读取或接收数据表单设备的控制传输代码应为:
其中第二个参数中的0x01是GET_REPORT是Hid调用特定请求 .
用于写入或向设备发送数据的控制传输代码应为:
由于您只有中断IN endpoints 1,因此批量或中断传输应如下所示:
要获得中断Out endpoints ,应该在设备固件的接口描述符处有一个 endpoints 描述符 .