4

The basic problem we wish to overcome is to transfer data over USB from an external source (PC) acting as a host and power supply to an Android device. As the PC will be able to supply power, and to make it easier for the eventual consumer of the product, we'd like to avoid OTG cables and therefore run the android device in Accessory mode.

I've managed to make the Android device enter Accessory mode and recognizes there being a USB host connected to it, even being able to read control data of the host. However I can't seem to figure out the actual bulk data transfers.

The official android documentation seem to offer very little on the subject other than simply setting up the initial connection.

I've salvaged as much code online as I could from the few sources that touch on this subject, mainly from here, but also other places.

In the Android code the usbThread gets started, and can even access the accessory and print out its Manufacturer, model, etc. correctly. However reading from the inputStream always result in an IOError.

In the c++ code on the host PC everything runs smoothly and connects up until the transferTest, where the first bulk transfer times out and zero bytes were reported to be transferred.

I always kill ADB before running my c++ code. The android documentation says to send a set_configuration(1), which I've tried to no avail. They also speak about there being a second interface for ADB, which I do not see in my output. I do see an alternative interface configuration, but setting and using this does not work at all.

Is there any additional setup required? Am I missing some basic USB protocol thingy? Should the Android code simply do continuous polling on the inputStream? Is accessory mode even still suported this day and age?

The relevant code looks like this

MainActivity.java

//<company> contains actual reversed Internet domain name
private static final String ACTION_USB_PERMISSION = "<company>.USB_PERMISSION"; 
UsbManager usbManager;
usbThread thread;
boolean usbStarted = false;
boolean resumeRan = false;
boolean threadCreated = false;
UsbAccessory accessory;
ParcelFileDescriptor fileDescriptor;
FileInputStream inputStream;
FileOutputStream outputStream;

private void openAccessory() {
    Log.d(TAG, "openAccessory: " + accessory);
    fileDescriptor = usbManager.openAccessory(accessory);
    if (fileDescriptor != null)
    {
        usbStarted = true;
        FileDescriptor fd = fileDescriptor.getFileDescriptor();
        inputStream = new FileInputStream(fd);
        outputStream = new FileOutputStream(fd);
        thread = new usbThread();
        thread.start();
    }
}

private final BroadcastReceiver permissionReceiver = new BroadcastReceiver()
{
    public void onReceive(Context context, Intent intent)
    {
        String action = intent.getAction();
        if (ACTION_USB_PERMISSION.equals(action))
        {
            Log.d(TAG, "Asking for usb permission");
            synchronized (this)
            {
                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false))
                {
                    if((!usbStarted) && resumeRan)
                    {
                        resumeRan = false;
                        Log.d(TAG, "Model: " + accessory.getModel());
                        openAccessory();
                    }
                }
                else
                {
                    Log.d(TAG, "permission denied for device ");
                }
            }
        }
    }
};

Handler usbHandler = new Handler()
{
    @Override
    public void handleMessage(Message msg)
    {
        byte res = msg.getData().getByte("val");
        Log.d(TAG, "Byte " + Byte.toString(res));
    }
};

private class usbThread extends Thread
{
    boolean bueno = true;
    usbThread()
    {
        if(!threadCreated)
        {
            threadCreated = true;
        }
        else
        {
            bueno = false;
            Log.d(TAG, "Tried to open second thread");
        }
    }
    public void run()
    {
        while(usbStarted && bueno)
        {
            byte[] buffer = new byte[10];
            int  ret = 0;
            try
            {
                int av = inputStream.available();
                ret = inputStream.read(buffer, 0, av);
            } catch (IOException e)
            {
                e.printStackTrace();
                //Log.d("MMI_IO_READ", "IOException on buffer read.");
            }

            if(ret > 0)
            {
                //Log.d("MMI_IO_READ", "Ret = " +ret);
                for(int i = 0; i < ret; i++)
                {
                    Message m = usbHandler.obtainMessage();
                    Bundle b = new Bundle();
                    b.putByte("val", buffer[i]);
                    m.setData(b);
                    usbHandler.sendMessage(m);
                }
            }
        }
        Log.d(TAG, "Thread Shutdown");
    }
}

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);

    usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
    IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED);
    registerReceiver(mUsbDetachReceiver , filter);
    filter = new IntentFilter(ACTION_USB_PERMISSION);
    registerReceiver(permissionReceiver, filter);
}

@Override
protected void onResume()
{
    super.onResume();

    UsbAccessory[] accessoryList = usbManager.getAccessoryList();
    if (accessoryList != null)
    {
        if(!usbStarted)
        {
            Log.d(TAG, "Resume, usb not started");
            accessory = accessoryList[0];
            Log.d(TAG, "Manufacturer " + accessory.getManufacturer());
            PendingIntent mPermissionIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent(ACTION_USB_PERMISSION), 0);
            resumeRan = true;
            usbManager.requestPermission(accessory, mPermissionIntent);
        }
    }
}

Note that as suggested by this thread among others, as well as my own experience, the USB_ACCESSORY_ATTACH intent never fires. USB detection therefore relies on a shaky onResume check.

HostUSB.cpp

#include <stdio.h>
#include <libusb-1.0/libusb.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <iostream>


#define ACCESSORY_VID 0x18d1
#define ACCESSORY_PID 0x2D00
#define ACCESSORY_ADB_PID 0x2D01
#define INTERFACE 0


const char* manufacturer = "PCHost";
const char* modelName = "PCHost1";
const char* description = "Description";
const char* version = "1.0";
const char* uri = "http://example.com";
const char* serialNumber = "666";

//static

static void error(int code)
{
    fprintf(stdout, "%s\n", libusb_strerror((libusb_error)code));
}

static int shutdown(libusb_device_handle *handle)
{
    if(handle)
    {
        libusb_release_interface(handle, INTERFACE);
        libusb_close(handle);
    }
    libusb_exit(NULL);
    return 0;
}


static int init()
{
    libusb_init(NULL);
    libusb_set_debug(NULL, 3);  //Verbose debugging level

    return 0;
}

// Send AOA specified introdction control information.
static int androidIntroduction(libusb_device_handle *handle)
{
    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
        100 //timeout
    );
    fprintf(stdout,"Sent getProtocol\n");

    if(response < 0)
        {error(response);return -1;}

    fprintf(stdout,"Response \n");

    devVersion = ioBuffer[1] << 8 | ioBuffer[0];
    if(!(devVersion == 1 || devVersion==2))
        return -1;
    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,(unsigned char*)manufacturer,strlen(manufacturer)+1,0);
    if(response < 0)
        {error(response);return -1;}
    response = libusb_control_transfer(handle,0x40,52,0,1,(unsigned char*)modelName,strlen(modelName)+1,0);
    if(response < 0)
        {error(response);return -1;}
    response = libusb_control_transfer(handle,0x40,52,0,2,(unsigned char*)description,strlen(description)+1,0);
    if(response < 0)
        {error(response);return -1;}
    response = libusb_control_transfer(handle,0x40,52,0,3,(unsigned char*)version,strlen(version)+1,0);
    if(response < 0)
        {error(response);return -1;}
    response = libusb_control_transfer(handle,0x40,52,0,4,(unsigned char*)uri,strlen(uri)+1,0);
    if(response < 0)
        {error(response);return -1;}
    response = libusb_control_transfer(handle,0x40,52,0,5,(unsigned char*)serialNumber,strlen(serialNumber)+1,0);
    if(response < 0)
        {error(response);return -1;}

    fprintf(stdout,"Accessory Identification sent\n");
    return 1;
}

// Send introduction information to given handle, then try to put it into
// accessory mode and catch it once it reconnects.
static libusb_device_handle* setupAccessory(libusb_device_handle *handle)
    {
    int response;
    response = androidIntroduction(handle);
    if(response < 0)
        return NULL;
    response = libusb_control_transfer(handle,0x40,53,0,0,NULL,0,0);
    if(response < 0){error(response);return NULL;}

    fprintf(stdout,"Attempted to put device into accessory mode\n");

    libusb_device_handle *androidHandle;

    int tries = 4;
    while(true)
    {
        tries--;
        if((androidHandle = libusb_open_device_with_vid_pid(NULL, ACCESSORY_VID, ACCESSORY_ADB_PID)) == NULL)
        {
            if((androidHandle = libusb_open_device_with_vid_pid(NULL, ACCESSORY_VID, ACCESSORY_PID)) == NULL)
            {
                if(tries < 0)
                {
                    std::cout << "Could not..." << '\n';
                    return NULL;
                }
            }
            else
            {
                break;
            }
        }else
        {
            break;
        }
        usleep(1000000);
    }

    return androidHandle;
}

//Find the first Bulk OUT Enpoint of the given device. 
uint8_t findBulkOut (libusb_device *device)
{
    libusb_config_descriptor *con_desc;
    libusb_get_active_config_descriptor(device, &con_desc);
    const libusb_interface *interfaceList = con_desc->interface;
    uint16_t numInterface = con_desc->bNumInterfaces;
    for(int j = 0; j<numInterface; j++)
    {
        libusb_interface interface = interfaceList[j];
        const libusb_interface_descriptor *intDescList = interface.altsetting;
        int numAlt = interface.num_altsetting;
        for(int p = 0; p < numAlt; p++)
        {
            libusb_interface_descriptor intDesc = intDescList[p];
            uint8_t numEnd = intDesc.bNumEndpoints;
            const libusb_endpoint_descriptor *ends = intDesc.endpoint;
            for(int k = 0; k < numEnd; k++)
            {
                libusb_endpoint_descriptor endpoint = ends[k];
                uint8_t type = 0x03 & endpoint.bmAttributes;
                uint8_t address = endpoint.bEndpointAddress;
                switch (type) {
                    case LIBUSB_TRANSFER_TYPE_CONTROL:
                        break;
                    case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
                        break;
                    case LIBUSB_TRANSFER_TYPE_BULK:
                        if(!(address & LIBUSB_ENDPOINT_IN)) //LIBUSB_ENPOINT_OUT is simply 0000, can't AND that...
                            {
                                return address;
                            }
                        break;
                    case LIBUSB_TRANSFER_TYPE_INTERRUPT:
                        break;
                    case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
                        break;
                }
            }
        }
    }
}

//Basically findBulkOut, but with output for EndPoints.
void printEnds (libusb_device *device)
{
    libusb_config_descriptor *con_desc;
    libusb_get_active_config_descriptor(device, &con_desc);
    const libusb_interface *interfaceList = con_desc->interface;
    uint16_t numInterface = con_desc->bNumInterfaces;
    for(int j = 0; j<numInterface; j++)
    {
        libusb_interface interface = interfaceList[j];
        const libusb_interface_descriptor *intDescList = interface.altsetting;
        int numAlt = interface.num_altsetting;
        for(int p = 0; p < numAlt; p++)
        {
            libusb_interface_descriptor intDesc = intDescList[p];
            uint8_t numEnd = intDesc.bNumEndpoints;
            const libusb_endpoint_descriptor *ends = intDesc.endpoint;
            fprintf(stdout, "Interface %d. altSetting %d. Num of endpoints: %d\n", p, intDesc.bInterfaceNumber, numEnd);
            for(int k = 0; k < numEnd; k++)
            {
                libusb_endpoint_descriptor endpoint = ends[k];
                uint8_t type = 0x03 & endpoint.bmAttributes;
                uint8_t address = endpoint.bEndpointAddress;
                fprintf(stdout, "Endpoint type ");
                switch (type) {
                    case LIBUSB_TRANSFER_TYPE_CONTROL:
                        std::cout << "Control";
                        break;
                    case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
                        std::cout << "Isochronus";
                        break;
                    case LIBUSB_TRANSFER_TYPE_BULK:
                        std::cout << "Bulk";
                        break;
                    case LIBUSB_TRANSFER_TYPE_INTERRUPT:
                        std::cout << "Interupt";
                        break;
                    case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
                        std::cout << "Bulk Stream";
                        break;
                }
                std::cout << " ";
                std::cout << (address & LIBUSB_ENDPOINT_IN ? "IN" : "OUT");
                std::cout << '\n';
                fprintf(stdout, "Address %04X\n", address);
            }
        }
    }
}

// Go through all connected devices. If they are do not have Google PID
// and VID, try to find out if they are Android devices. If they (most likely)
// are, try to put them in accessory mode. If successful, return that handle
libusb_device_handle* getAndroidHandle()
{
    libusb_device **list;
    ssize_t cnt = libusb_get_device_list(NULL, &list);
    ssize_t i = 0;
    int err = 0;
    if (cnt < 0)
        error(0);
    for (i = 0; i < cnt; i++)
    {
        fprintf(stdout,"\nAttempted index %d\n", (int)i);
        libusb_device *device = list[i];
        libusb_device_descriptor dev_desc;
        libusb_get_device_descriptor(device, &dev_desc);
        uint16_t VID = dev_desc.idVendor;
        uint16_t PID = dev_desc.idProduct;
        fprintf(stdout, "VID: %04X. PID: %04X\n", VID, PID);

        libusb_device_handle *handle;
        int response = libusb_open(device, &handle);
        if(response < 0)
            {error(response);continue;}
        libusb_set_auto_detach_kernel_driver(handle, 1);

        libusb_device_handle *androidHandle;
        if(VID == ACCESSORY_VID && (PID == ACCESSORY_PID || PID == ACCESSORY_ADB_PID))
        {
            int r = androidIntroduction(handle);
            if (r != 1)
                continue;
            androidHandle = handle;
        }
        else
        {
             androidHandle = setupAccessory(handle);
             libusb_close(handle);

        }
        if (androidHandle)
        {
            libusb_free_device_list(list, 1);
            std::cout << "\n\nAndroid Found:" << '\n';
            printEnds(libusb_get_device(androidHandle));
            return androidHandle;
        }
    }
    libusb_free_device_list(list, 1);
    return NULL;
}

//Try to send data.
static int transferTest(libusb_device_handle *handle)
{
    const static int PACKET_BULK_LEN=64;
    const static int TIMEOUT=5000;
    int r,i;
    int transferred;
    usleep(1000000); //1s

    //libusb_set_configuration(handle, 1);
    r = libusb_claim_interface(handle, INTERFACE);
    if(r < 0)
        {error(r); return -1;}
    fprintf(stdout, "Interface claimed, ready to transfer data\n");
    // TEST BULK IN/OUT
    usleep(100000);// 0.1s
    uint8_t outAddress = findBulkOut(libusb_get_device(handle));
    fprintf(stdout, "Trying to write to %04X\n", outAddress );
    char answer[PACKET_BULK_LEN];
    char question[PACKET_BULK_LEN];
    for (i=0;i<PACKET_BULK_LEN; i++) question[i]=i;

    // ***TIMES OUT HERE***
    r = libusb_bulk_transfer(handle, outAddress, (unsigned char*)question, PACKET_BULK_LEN,
                             &transferred,TIMEOUT);
    if (r < 0)
    {
        fprintf(stderr, "Bulk write error %d\n", r);
        error(r);
        fprintf(stderr, "Number of bytes written %d\n", transferred);
        return r;
    }
    fprintf(stdout, "Wrote %d bytes", transferred);

    /*r = libusb_bulk_transfer(handle, ENDPOINT_BULK_IN, (unsigned char*)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;
}

int main (int argc, char *argv[])
{
    fprintf(stdout, "OUT flag %04X IN flag %04X\n", LIBUSB_ENDPOINT_OUT, LIBUSB_ENDPOINT_IN);
    fprintf(stdout, "Shifted in %04X\n", LIBUSB_ENDPOINT_IN >> 7);
    if(init() < 0)
        return -1;

    libusb_device_handle *handle = getAndroidHandle();
    if(!handle)
    {
        fprintf(stdout, "\nError setting up accessory\n");
        shutdown(NULL);
        return -1;
    };
    if(transferTest(handle) < 0)
    {
        fprintf(stdout, "\nError in transferTest\n");
        shutdown(handle);
        return -1;
    }
    shutdown(handle);
    fprintf(stdout, "\nFinished\n");
    return 0;
}

Thanks in advance for any replies! :)

EDIT

Seems to have been the inputStream.available() that was the main culprit and doesn't seem work correctly (I found out from some random googling). It throws an IOException regardless of correct setup. I also uncommented the libusb_set_configuration and set a slight delay after to make sure it worked properly.

Sidenote: I did not have to kill the ADB server to get the USB connection working. If the device introduced itself with 0x2D01 as PID then ADB logging worked just fine!

2 Answers2

1

Here is my Java code that works, maybe it will help you.

The C++ code above works also, if you add libusb_set_configuartion after AOA mode.

Java

import android.app.PendingIntent;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;


import android.os.Bundle;
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 android.util.Log;


import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
//import java.util.logging.Logger;


public class MainActivity extends Activity implements Runnable
{

    private static final String ACTION_USB_PERMISSION = "com.examples.accessory.controller.action.USB_PERMISSION";
    private final String TAG = "_ITE";

    private UsbManager mUsbManager;
    private PendingIntent mPermissionIntent;
    private boolean mPermissionRequestPending;

    UsbAccessory mAccessory;
    ParcelFileDescriptor mFileDescriptor;
    FileInputStream mInputStream;
    FileOutputStream mOutputStream;


    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
        mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
        registerReceiver(mUsbReceiver, filter);

        setContentView(R.layout.activity_main);

        //enableControls(false);
    }

    @Override
    public void onResume()
    {
        super.onResume();

        Intent intent = getIntent();
        if (mInputStream != null && mOutputStream != null) {
            return;
        }

        UsbAccessory[] accessories = mUsbManager.getAccessoryList();
        UsbAccessory accessory = (accessories == null ? null : accessories[0]);
        if (accessory != null)
        {
            if (mUsbManager.hasPermission(accessory))
            {
                openAccessory(accessory);
            }
            else
                {
                synchronized (mUsbReceiver)
                {
                    if (!mPermissionRequestPending)
                    {
                        mUsbManager.requestPermission(accessory,mPermissionIntent);
                        mPermissionRequestPending = true;
                    }
                }
            }
        }
        else
            {
            Log.d(TAG, "mAccessory is null");
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        closeAccessory();
    }

    @Override
    public void onDestroy() {
        unregisterReceiver(mUsbReceiver);
        super.onDestroy();
    }



    private void openAccessory(UsbAccessory accessory)
    {
        mFileDescriptor = mUsbManager.openAccessory(accessory);
        if (mFileDescriptor != null) {
            mAccessory = accessory;
            FileDescriptor fd = mFileDescriptor.getFileDescriptor();
            mInputStream = new FileInputStream(fd);
            mOutputStream = new FileOutputStream(fd);
            Thread thread = new Thread(null, this, "AccessoryController");
            thread.start();
            Log.d(TAG, "accessory opened");
            //enableControls(true);
        } else {
            Log.d(TAG, "accessory open fail");
        }
    }

    private void closeAccessory() {
        //enableControls(false);

        try {
            if (mFileDescriptor != null) {
                mFileDescriptor.close();
            }
        } catch (IOException e) {
        } finally {
            mFileDescriptor = null;
            mAccessory = null;
        }
    }

    /*
     * This receiver monitors for the event of a user granting permission to use
     * the attached accessory.  If the user has checked to always allow, this will
     * be generated following attachment without further user interaction.
     */
    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (ACTION_USB_PERMISSION.equals(action)) {
                synchronized (this) {
                    UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        openAccessory(accessory);
                    } else {
                        Log.d(TAG, "permission denied for accessory "+ accessory);
                    }
                    mPermissionRequestPending = false;
                }
            } else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
                UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
                if (accessory != null && accessory.equals(mAccessory)) {
                    closeAccessory();
                }
            }
        }
    };

    /*
     * Runnable block that will poll the accessory data stream
     * for regular updates, posting each message it finds to a
     * Handler.  This is run on a spawned background thread.
     */
    public void run() {
        int ret = 0;
        byte[] buffer = new byte[16384];
        int i;

        while (ret >= 0)
        {
            try
            {
                ret = mInputStream.read(buffer);
            } catch (IOException e) {
                break;
            }




        }
    }


}

Manifest

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <uses-library android:name="com.android.future.usb.accessory" />
        <activity android:name=".MainActivity">
            <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" />
                <action android:name="android.hardware.usb.action.USB_ACCESSORY_DETACHED" />
            </intent-filter>

            <meta-data
                android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
                android:resource="@xml/accessory_filter" >
            </meta-data>
        </activity>
    </application>

accessory_filter.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>    
    <usb-accessory model="YourModel" manufacturer="yourname" version="0.0.1"/>

</resources>
swa00
  • 26
  • 1
  • 7
  • Thanks for your reply! I got it working just before the weekend. Seems like the "inputStream.avaliable()" was throwing a IOException even tho the connection was correctly setup. Apparently that function doesn't wokr? I did add the libusb_set_configuration back as well, which probably also helped! Do you know anything about the ATTACH/DETACH events tho? The DETACH one doesn't seem to fire either... – Claes Andersson Aug 07 '17 at 08:13
-2

This isn't a code issue, you can't bulk data transfer without OTG. Get an OTG adapter for 10/15 bucks and then you may be able to do this, but it depends on the device and its age. In general they will not act as hosts without adb and an OTG adapter/cable. I have never heard of another way and I think you will just end up with a code that won't work, but if you did find a way, let me know.

kbrackson
  • 7
  • 3