我正在尝试将我的PC作为Android 2.3.4设备的USB主机,以便能够开发API而无需实际的“附件”。为此,我需要将PC设置为USB主机和“设备”(在我的情况下是运行2.3.4的Nexus One)。
我从http://android.serverbox.ch/的libusb代码开始,作为PC端的基础,以及Android端的DemoKit代码和Android文档。
这两个似乎很好地协商了连接,并且接口被“声称”但在实际的批量传输尝试中死亡。在OSX上,错误为-5(LIBUSB_ERROR_NOT_FOUND),在Ubuntu Linux上(以root身份),错误为-1(LIBUSB_ERROR_IO)。 (每个版本的最新版本都包含最新的libusb 1.0.8)。
这是代码。关于问题的其他评论欢迎,虽然这主要是一个概念验证,所以我真的只是在寻找批量转移不起作用的原因:
#include <stdio.h>
#include <libusb.h>
#include <string.h>
#define ENDPOINT_BULK_IN 0x83
#define ENDPOINT_BULK_OUT 0x03 // Have tried 0x00, 0x01 and 0x02
#define VID 0x18D1
#define PID 0x4E11
#define ACCESSORY_PID 0x2D00
#define ACCESSORY_ADB_PID 0x2D01 // Can't get this to work, if ADB is active, can't get handle on device
/*
ON OSX
gcc adktest.c -I/usr/local/include -o adktest -lusb-1.0.0 -I/usr/local/include -I/usr/local/include/libusb-1.0
ON UBUNTU
gcc adktest.c -I/usr/include -o adktest -lusb-1.0 -I/usr/include -I/usr/include/libusb-1.0
Testing on Nexus One with Gingerbread 2.3.4
*/
static int transferTest();
static int init(void);
static int shutdown(void);
static void error(int code);
static void status(int code);
static int setupAccessory(
const char* manufacturer,
const char* modelName,
const char* description,
const char* version,
const char* uri,
const char* serialNumber);
//static
static struct libusb_device_handle* handle;
int main (int argc, char *argv[]){
if(init() < 0)
return;
if(setupAccessory(
"PCHost",
"PCHost1",
"Description",
"1.0",
"http://www.mycompany.com",
"SerialNumber") < 0){
fprintf(stdout, "Error setting up accessory\n");
shutdown();
return -1;
};
if(transferTest() < 0){
fprintf(stdout, "Error in transferTest\n");
shutdown();
return -1;
}
shutdown();
fprintf(stdout, "Finished\n");
return 0;
}
static int transferTest(){
// TEST BULK IN/OUT
const static int PACKET_BULK_LEN=64;
const static int TIMEOUT=5000;
int r,i;
int transferred;
char answer[PACKET_BULK_LEN];
char question[PACKET_BULK_LEN];
for (i=0;i<PACKET_BULK_LEN; i++) question[i]=i;
// ***FAILS HERE***
r = libusb_bulk_transfer(handle, ENDPOINT_BULK_OUT, question, PACKET_BULK_LEN,
&transferred,TIMEOUT);
if (r < 0) {
fprintf(stderr, "Bulk write error %d\n", r);
error(r);
return r;
}
fprintf(stdout, "Wrote %d bytes", r);
r = libusb_bulk_transfer(handle, ENDPOINT_BULK_IN, answer,PACKET_BULK_LEN,
&transferred, TIMEOUT);
if (r < 0) {
fprintf(stderr, "Bulk read error %d\n", r);
error(r);
return r;
}
fprintf(stdout, "Read %d bytes", r);
if (transferred < PACKET_BULK_LEN) {
fprintf(stderr, "Bulk transfer short read (%d)\n", r);
error(r);
return -1;
}
printf("Bulk Transfer Loop Test Result:\n");
// for (i=0;i< PACKET_BULK_LEN;i++) printf("%i, %i,\n ",question[i],answer[i]);
for(i = 0;i < PACKET_BULK_LEN; i++) {
if(i%8 == 0)
printf("\n");
printf("%02x, %02x; ",question[i],answer[i]);
}
printf("\n\n");
return 0;
}
static int init(){
libusb_init(NULL);
if((handle = libusb_open_device_with_vid_pid(NULL, VID, PID)) == NULL){
fprintf(stdout, "Problem acquiring handle\n");
return -1;
}
libusb_claim_interface(handle, 0);
return 0;
}
static int shutdown(){
if(handle != NULL)
libusb_release_interface (handle, 0);
libusb_exit(NULL);
return 0;
}
static int setupAccessory(
const char* manufacturer,
const char* modelName,
const char* description,
const char* version,
const char* uri,
const char* serialNumber){
unsigned char ioBuffer[2];
int devVersion;
int response;
response = libusb_control_transfer(
handle, //handle
0xC0, //bmRequestType
51, //bRequest
0, //wValue
0, //wIndex
ioBuffer, //data
2, //wLength
0 //timeout
);
if(response < 0){error(response);return-1;}
devVersion = ioBuffer[1] << 8 | ioBuffer[0];
fprintf(stdout,"Version Code Device: %d\n", devVersion);
usleep(1000);//sometimes hangs on the next transfer :(
response = libusb_control_transfer(handle,0x40,52,0,0,(char*)manufacturer,strlen(manufacturer),0);
if(response < 0){error(response);return -1;}
response = libusb_control_transfer(handle,0x40,52,0,1,(char*)modelName,strlen(modelName)+1,0);
if(response < 0){error(response);return -1;}
response = libusb_control_transfer(handle,0x40,52,0,2,(char*)description,strlen(description)+1,0);
if(response < 0){error(response);return -1;}
response = libusb_control_transfer(handle,0x40,52,0,3,(char*)version,strlen(version)+1,0);
if(response < 0){error(response);return -1;}
response = libusb_control_transfer(handle,0x40,52,0,4,(char*)uri,strlen(uri)+1,0);
if(response < 0){error(response);return -1;}
response = libusb_control_transfer(handle,0x40,52,0,5,(char*)serialNumber,strlen(serialNumber)+1,0);
if(response < 0){error(response);return -1;}
fprintf(stdout,"Accessory Identification sent\n", devVersion);
response = libusb_control_transfer(handle,0x40,53,0,0,NULL,0,0);
if(response < 0){error(response);return -1;}
fprintf(stdout,"Attempted to put device into accessory mode\n", devVersion);
if(handle != NULL)
libusb_release_interface (handle, 0);
int tries = 4;
for(;;){
tries--;
if((handle = libusb_open_device_with_vid_pid(NULL, VID, ACCESSORY_PID)) == NULL){
if(tries < 0){
return -1;
}
}else{
break;
}
sleep(1);
}
libusb_claim_interface(handle, 0);
fprintf(stdout, "Interface claimed, ready to transfer data\n");
return 0;
}
// error reporting function left out for brevity
这是Android应用程序:
package com.cengen.android.pchost;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import com.android.future.usb.UsbAccessory;
import com.android.future.usb.UsbManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class MainActivity extends Activity implements Runnable
{
private final Logger logger = LoggerFactory.getLogger("PCHost");
private UsbManager usbManager;
UsbAccessory accessory;
ParcelFileDescriptor accessoryFileDescriptor;
FileInputStream accessoryInput;
FileOutputStream accessoryOutput;
private final BroadcastReceiver usbBroadcastReceiver = new BroadcastReceiver()
{
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action))
{
synchronized (this)
{
accessory = UsbManager.getAccessory(intent);
}
}
else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action))
{
UsbAccessory accessory = UsbManager.getAccessory(intent);
if (accessory != null)
{
// call your method that cleans up and closes communication with the accessory
}
}
}
};
Handler messageHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
switch (msg.what)
{
case 1:
logger.info("Got message type {}", msg.what);
// SendFileMessage m = (SendFileMessage) msg.obj;
// handleSendFile(m);
break;
case 2:
logger.info("Got message type {}", msg.what);
// SendFileMessage m = (SendFileMessage) msg.obj;
// handleSendFile(m);
break;
case 3:
logger.info("Got message type {}", msg.what);
// SendFileMessage m = (SendFileMessage) msg.obj;
// handleSendFile(m);
break;
}
}
};
/**
* Main USB reading loop, processing incoming data from accessory and parsing
* it into messages via the defined format.
*/
public void run()
{
int ret = 0;
byte[] buffer = new byte[16384];
int i;
while (ret >= 0)
{
try
{
ret = accessoryInput.read(buffer);
logger.debug("Read {} bytes.", ret);
}
catch (IOException e)
{
logger.debug("Exception in USB accessory input reading", e);
break;
}
i = 0;
while (i < ret)
{
int len = ret - i;
switch (buffer[i])
{
case 0x1:
if (len >= 3)
{
Message m = Message.obtain(messageHandler, 1);
// m.obj = new MessageTypeOne(buffer[i + 1], buffer[i + 2]);
messageHandler.sendMessage(m);
}
i += 3;
break;
case 0x4:
if (len >= 3)
{
Message m = Message.obtain(messageHandler, 1);
// m.obj = new MessageTypeTwo(buffer[i + 1], buffer[i + 2]);
messageHandler.sendMessage(m);
}
i += 3;
break;
default:
logger.debug("unknown msg: " + buffer[i]);
i = len;
break;
}
}
}
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
usbManager = UsbManager.getInstance(this);
IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
registerReceiver(usbBroadcastReceiver, filter);
if (getLastNonConfigurationInstance() != null)
{
accessory = (UsbAccessory) getLastNonConfigurationInstance();
openAccessory(accessory);
}
setContentView(R.layout.main);
}
@Override
public Object onRetainNonConfigurationInstance()
{
return accessory != null ? accessory : super.onRetainNonConfigurationInstance();
}
@Override
public void onResume()
{
super.onResume();
Intent intent = getIntent();
if (accessoryInput != null && accessoryOutput != null)
return;
// TODO: verify, docs don't do this simple thing, not sure why?
UsbAccessory accessory = UsbManager.getAccessory(intent);
if (accessory != null)
openAccessory(accessory);
else
logger.error("Failed to resume accessory.");
}
@Override
public void onPause()
{
super.onPause();
closeAccessory();
}
@Override
public void onDestroy()
{
unregisterReceiver(usbBroadcastReceiver);
super.onDestroy();
}
private void openAccessory(UsbAccessory accessory)
{
accessoryFileDescriptor = usbManager.openAccessory(accessory);
if (accessoryFileDescriptor != null)
{
this.accessory = accessory;
FileDescriptor fd = accessoryFileDescriptor.getFileDescriptor();
accessoryInput = new FileInputStream(fd);
accessoryOutput = new FileOutputStream(fd);
Thread thread = new Thread(null, this, "AndroidPCHost");
thread.start();
logger.debug("accessory opened");
// TODO: enable USB operations in the app
}
else
{
logger.debug("accessory open fail");
}
}
private void closeAccessory()
{
// TODO: disable USB operations in the app
try
{
if (accessoryFileDescriptor != null)
accessoryFileDescriptor.close();
}
catch (IOException e)
{}
finally
{
accessoryFileDescriptor = null;
accessory = null;
}
}
}
清单(包括意图过滤,以便根据文档自动关联和自动更新设备):
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mycompany.android.pchost"
android:versionCode="1"
android:versionName="1.0">
<uses-feature android:name="android.hardware.usb.accessory" />
<uses-sdk android:minSdkVersion="10" />
<application android:label="@string/app_name" android:icon="@drawable/icon">
<uses-library android:name="com.android.future.usb.accessory" />
<activity android:name="MainActivity"
android:label="@string/app_name"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
android:resource="@xml/accessory_filter" />
</activity>
</application>
</manifest>
答案 0 :(得分:7)
最初的问题是,设备的原始连接是为了告诉它进入配件模式永远不会关闭。如果在原始设备仍处于打开状态时重新打开并声明接口,则USB子系统和/或libusb不会引发错误。当您实际尝试向端点写入/从端点写入时,您只会收到IO或NOT FOUND错误。
添加:
libusb_close(handle);
在初始接口从初始连接释放的if语句中,解决了问题的libusb端。
防止数据在这个特定的软件组合中传递的下一个问题是Android端在接受读取之前等待更大的字节段,导致超时(由于哪个没有计算出来) (如果你设置缓冲区以匹配libusb端(64)),你将得到一个从PC写入Android设备的初始字节集。之后该软件仍将破解,因为PC / libusb端然后尝试读取数据,但Android方面没有写任何内容,但这只是未完成的软件,不属于问题范围。
答案 1 :(得分:0)
代码示例非常有用,只需要通过libusb
使用installing WinUSB drivers和Zadig在Windows上进行一些修改。
#include <stdio.h>
#include <libusb.h>
#include <string.h>
#ifdef _WIN32
#include <Windows.h>
#define sleep Sleep
#else
#include <unistd.h>
#endif
#define ENDPOINT_BULK_IN 0x83
#define ENDPOINT_BULK_OUT 0x03 // Have tried 0x00, 0x01 and 0x02
#define VID 0x18D1
#define PID 0x4E11
#define ACCESSORY_PID 0x2D00
#define ACCESSORY_ADB_PID 0x2D01 // Can't get this to work, if ADB is active, can't get handle on device
#define PACKET_BULK_LEN 64
#define TIMEOUT 5000
static int transferTest(void);
static int setupAccessory(
const char* manufacturer,
const char* modelName,
const char* description,
const char* version,
const char* uri,
const char* serialNumber);
//static
static struct libusb_device_handle* handle;
int main (int argc, char *argv[])
{
int r, tries;
libusb_init(NULL);
if((handle = libusb_open_device_with_vid_pid(NULL, VID, PID)) != NULL)
{
libusb_claim_interface(handle, 0);
r = setupAccessory("PCHost",
"PCHost1",
"Description",
"1.0",
"http://www.mycompany.com",
"SerialNumber");
libusb_release_interface (handle, 0);
libusb_close(handle);
if (r < 0)
{
libusb_exit(NULL);
fprintf(stdout, "Error setting up accessory\n");
return -1;
}
}
tries = 4;
for(;;)
{
tries--;
if((handle = libusb_open_device_with_vid_pid(NULL, VID, ACCESSORY_PID)) == NULL)
{
if(tries < 0)
{
libusb_exit(NULL);
fprintf(stdout, "Problem acquiring handle\n");
return -1;
}
}
else
{
break;
}
sleep(1);
}
libusb_claim_interface(handle, 0);
fprintf(stdout, "Interface claimed, ready to transfer data\n");
r = transferTest();
libusb_release_interface (handle, 0);
libusb_close(handle);
libusb_exit(NULL);
if (r < 0)
{
fprintf(stdout, "Error in transferTest\n");
return -1;
}
fprintf(stdout, "Finished\n");
return 0;
}
static int transferTest(void)
{
// TEST BULK IN/OUT
int r,i;
int transferred;
unsigned char answer[PACKET_BULK_LEN];
unsigned char question[PACKET_BULK_LEN];
struct libusb_device* device;
struct libusb_config_descriptor* config;
struct libusb_interface_descriptor const* interfaceDesc;
unsigned char epInAddr = ENDPOINT_BULK_IN;
unsigned char epOutAddr = ENDPOINT_BULK_OUT;
unsigned char idx;
for (i=0;i<PACKET_BULK_LEN; i++)
question[i]=(unsigned char)i;
device = libusb_get_device(handle);
r = libusb_get_active_config_descriptor(device, &config);
if (r < 0)
{
fprintf(stderr, "No active descriptor error %d\n", r);
return r;
}
interfaceDesc = config->interface[0].altsetting;
for(idx = 0; idx < interfaceDesc->bNumEndpoints; idx++)
{
if ((interfaceDesc->endpoint[idx].bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK)
{
if ((interfaceDesc->endpoint[idx].bEndpointAddress & LIBUSB_ENDPOINT_IN) == LIBUSB_ENDPOINT_IN)
{
epInAddr = interfaceDesc->endpoint[idx].bEndpointAddress;
}
else
{
epOutAddr = interfaceDesc->endpoint[idx].bEndpointAddress;
}
}
}
r = libusb_bulk_transfer(handle, epOutAddr, question, PACKET_BULK_LEN,
&transferred,TIMEOUT);
if (r < 0)
{
fprintf(stderr, "Bulk write error %d\n", r);
return r;
}
fprintf(stdout, "Wrote %d bytes", r);
r = libusb_bulk_transfer(handle, epInAddr, answer,PACKET_BULK_LEN,
&transferred, TIMEOUT);
if (r < 0)
{
fprintf(stderr, "Bulk read error %d\n", r);
return r;
}
fprintf(stdout, "Read %d bytes", r);
if (transferred < PACKET_BULK_LEN)
{
fprintf(stderr, "Bulk transfer short read (%d)\n", r);
return -1;
}
printf("Bulk Transfer Loop Test Result:\n");
for(i = 0; i < PACKET_BULK_LEN; i++)
{
if(i%8 == 0)
printf("\n");
printf("%02x, %02x; ",question[i],answer[i]);
}
printf("\n\n");
return 0;
}
static int setupAccessory(
const char* manufacturer,
const char* modelName,
const char* description,
const char* version,
const char* uri,
const char* serialNumber)
{
unsigned char ioBuffer[2];
int devVersion;
int r;
r = libusb_control_transfer(
handle, //handle
0xC0, //bmRequestType
51, //bRequest
0, //wValue
0, //wIndex
ioBuffer, //data
2, //wLength
0 //timeout
);
if(r < 0)
{
return-1;
}
devVersion = ioBuffer[1] << 8 | ioBuffer[0];
fprintf(stdout,"Version Code Device: %d\n", devVersion);
sleep(1); //sometimes hangs on the next transfer :(
if ((libusb_control_transfer(handle,0x40,52,0,0,(unsigned char*)manufacturer,strlen(manufacturer)+1,0) < 0) ||
(libusb_control_transfer(handle,0x40,52,0,1,(unsigned char*)modelName,strlen(modelName)+1,0) < 0) ||
(libusb_control_transfer(handle,0x40,52,0,2,(unsigned char*)description,strlen(description)+1,0) < 0) ||
(libusb_control_transfer(handle,0x40,52,0,3,(unsigned char*)version,strlen(version)+1,0) < 0) ||
(libusb_control_transfer(handle,0x40,52,0,4,(unsigned char*)uri,strlen(uri)+1,0) < 0) ||
(libusb_control_transfer(handle,0x40,52,0,5,(unsigned char*)serialNumber,strlen(serialNumber)+1,0) < 0))
{
return -1;
}
fprintf(stdout,"Accessory Identification sent\n", devVersion);
r = libusb_control_transfer(handle,0x40,53,0,0,NULL,0,0);
if(r < 0)
{
return -1;
}
fprintf(stdout,"Attempted to put device into accessory mode\n", devVersion);
return 0;
}
除了一些重构和添加@ColinM答案中提到的必要libusb_close(handle)
电话之外,这主要是补充: