Android ADK,PC 作为带有 libusb 的 USB 主机,批量传输错误

发布于 2024-11-17 15:46:22 字数 13214 浏览 3 评论 0原文

我正在尝试使我的 PC 成为 Android 2.3.4 设备的 USB 主机,以便能够在不需要实际“配件”的情况下开发 API。为此,我需要将 PC 设置为 USB 主机和“设备”(在我的例子中是运行 2.3.4 的 Nexus One)。

我从 http://android.serverbox.ch/ 中的 libusb 代码开始,作为 PC 端以及 DemoKit 代码和 Android 的基础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> 

I'm trying to make my PC the USB Host for Android 2.3.4 devices in order to be able to develop APIs without needing actual "accessories". To do this, I need to establish the PC as the USB Host and the "device" (in my case a Nexus One running 2.3.4).

I started with libusb code from http://android.serverbox.ch/ as a base for the PC side and the DemoKit code and Android documentation on the Android side.

The two seems to negotiate the connection fine, and the interface gets "claimed" but dies on the actual attempt at bulk transfer. On OSX, the error is -5 (LIBUSB_ERROR_NOT_FOUND) and on Ubuntu Linux (as root) the error is -1 (LIBUSB_ERROR_IO). (Most recent releases of each with most recent libusb 1.0.8).

Here's the code. Other comments on problems welcome, though this is mostly a proof-of-concept, so I'm really just looking for the reason the bulk transfer isn't working:

#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

And here's the Android app:

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;
    }
  }
}

And the manifest (including the intent-filtering so it auto-associates and auto-perms the device, according to the docs):

<?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> 

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

轻许诺言 2024-11-24 15:46:22

最初的问题是,为了告诉设备进入附件模式而与设备的原始连接永远不会关闭。如果您在原始设备仍保持打开状态时重新打开并声明接口,USB 子系统和/或 libusb 不会抛出错误。当您实际尝试写入端点或从端点写入时,您只会收到 IO 或 NOT FOUND 错误。

通过添加:

libusb_close(handle);

在 if 语句中从初始连接释放初始接口,libusb 方面的问题得到解决。

阻止数据在这个特定的软件组合中传递的下一个问题是,Android 端在接受读取之前正在等待更大的字节段,这会导致超时(尚未解决由于哪一端而导致的)因此,如果您将缓冲区设置为与 libusb 端 (64) 匹配,您将获得从 PC 写入 Android 设备的一组初始字节。此后软件仍然会崩溃,因为 PC/libusb 端随后尝试读取数据,但 Android 端没有写入任何数据,但这只是未完成的软件,不在问题的范围内。

Initial problem is that the original connection to the device in order to tell it to go into accessory mode is never closed. The USB subsystem and/or libusb do not throw an error if you then reopen and claim an interface while the original device is still left open. You only get an IO or NOT FOUND error when you actually try to write to/from an endpoint.

By adding:

libusb_close(handle);

within the if statement where the initial interface is released from initial connection the libusb side of the issue is resolved.

The next issue that prevents data to pass in this particular combination of softwares, is that the Android side is waiting for a larger segment of bytes before it accepts the read which results in a timeout (haven't worked out due to which side yet) and so if you set the buffer to match the libusb side (64), you will get an initial set of bytes written from the PC to the Android device. The software will still break after that since the PC/libusb side then tries to read data but the Android side isn't writing any, but that is simply unfinished software and not within the scope of the question.

猫九 2024-11-24 15:46:22

该代码示例非常有用,只需进行一些修改即可使用 libusb 在 Windows 上运行并通过 安装 WinUSB 驱动程序 href="http://zadig.akeo.ie/" rel="nofollow noreferrer">Zadig。

#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) 调用之外,这主要是添加:

  1. 可用端点的枚举
  2. 如果初始 PID/VID 不是,则假设设备已处于附件模式成立

The code example is very helpful, it just needs some modifications to work on Windows using libusb and installing WinUSB drivers via Zadig.

#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;
}

Besides some refactoring and adding the necessary libusb_close(handle) calls mentioned in @ColinM's answer this is mainly adding:

  1. Enumeration over the available end-points
  2. Assumes the device is already in accessory mode if initial PID/VID not found
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文