一篇搞定Linux和IOS或Android通讯(usbmuxd、libimobiledevice、libusb、AOA)

2023-10-29

1、Linux要与苹果手机通讯需要两个组件

1、usbmuxd 是苹果的一个服务,这个服务主要用于在USB协议上实现多路TCP连接,将USB通信抽象为TCP通信。苹果的iTunes、Xcode,都直接或间接地用到了这个服务。
参考链接:https://www.theiphonewiki.com/wiki/Usbmux
2、libimobiledevice是一个跨平台的软件库,用于与iOS设备进行交互。
参考链接:https://www.theiphonewiki.com/wiki/MobileDevice_Library

编译安装上面2个库就直接可以使用socket套接字进行通讯

上代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <errno.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>

#define MAX_CONNECT_NUM 2
#define BUFFER_SIZE 1024
const char *filename = "/var/run/usbmuxd";
static pthread_t th;
static pthread_t th1;

//sudo mv /var/run/usbmuxd /var/run/usbmuxx
// sudo socat -t100 -x -v UNIX-LISTEN:/var/run/usbmuxd,mode=777,reuseaddr,fork UNIX-CONNECT:/var/run/usbmuxx


void *thread_recv(void *ptr)
{
    int fd = *(int *)ptr;
    char buffer[BUFFER_SIZE];
    bzero(buffer, BUFFER_SIZE);

    while (1)
    {
        printf("waiting recv...\n");
        int ret = recv(fd, buffer, BUFFER_SIZE, 0);

        if (ret <= 0)
        {
            printf("recv failed\n");
            close(fd);
            pthread_cancel(th1);
            return NULL;
        }

        printf("%s\n", buffer);

        if (strncmp("end", buffer, 3) == 0)
        {
            close(fd);
            exit(0);
        }
    }
}

void *thread_send(void *ptr)
{
    int fd = *(int *)ptr;
    while(1)
    {
        char buff[128] = {0};
        fgets(buff, 128, stdin);

        int ret = send(fd, buff, 128, 0);
        if (ret <= 0)
        {
            close(fd);
            pthread_cancel(th);
            return NULL;
        }
    }
}

int main()
{
    int fd, new_fd, len, i;
    //struct sockaddr_un结构有两个参数:sun_family、sun_path
    //sun_family只能是AF_LOCAL或AF_UNIX,而sun_path是本地文件的路径。
    struct sockaddr_un un;
    fd = socket(AF_UNIX, SOCK_STREAM, 0);//SOCK_DGRAM是基于UDP的
    //本地套接字的通讯类型应该是SOCK_STREAM或SOCK_DGRAM,协议为默认协议。
    if (fd < 0)
    {
        printf("Request socket failed!\n");
        return -1;
    }
    un.sun_family = AF_UNIX;
    strcpy(un.sun_path, filename);
    if (bind(fd, (struct sockaddr *)&un, sizeof(un)) < 0)
    {
        printf("bind failed!\n");
        return -1;
    }
    if (listen(fd, MAX_CONNECT_NUM) < 0)
    {
        printf("listen failed!\n");
        return -1;
    }
    while (1)
    {
        printf("wait to accept...\n");

        new_fd = accept(fd, NULL, NULL);

        printf("new accept.\n");

        if (new_fd < 0)
        {
            printf("accept failed\n");
            return -1;
        }
        pthread_create(&th, NULL, thread_recv, (void *)(&new_fd));
        pthread_create(&th1, NULL, thread_send, (void *)(&new_fd));

    }
    close(fd);
}

2、Linux要与Android手机通讯只需要一个库

libusb是一个开源的用C实现的,应用程序与用户的USB设备进行通信的库。 链接地址:https://github.com/libusb/libusb
编译安装好上面的库后直接可以使用AOA进行通讯了

AOA说明:手机通过USB连接到设备时,触发一次插入事件,设备通过VID和PID来判断USB是否为AOA模式

不是AOA模式
USB会出现一次逻辑拔插过程触发一次拔出事件和一次插入事件手机切换VID和PID为AOA模式
是AOA模式
与Android手机通讯
进入切换到AOA模式流程
1.打开usb设备
2.发送AOA协议报文交互
2.发送AOA协议报文交互
3.关闭usb设备
4.USB已切换为AOA模式
5.获取USB接口信息
6.打开USB设备并声明读写接口建立AOA数据通道
获取USB接口信息
打开USB设备并声明读写接口建立AOA数据通道
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include "libusb.h"

/*手机通过USB连接到设备时,触发一次插入事件,设备通过VID和PID来判断USB是否为AOA模式

不是AOA模式:则进入切换到AOA模式流程

1.打开usb设备

2.发送AOA协议报文交互

3.关闭usb设备

USB会出现一次逻辑拔插过程:触发一次拔出事件和一次插入事件,手机切换VID和PID为AOA模式

4.USB已切换为AOA模式

5.获取USB接口信息

6.打开USB设备并声明读写接口,建立AOA数据通道

是AOA模式:

1.获取USB接口信息

2.打开USB设备并声明读写接口,建立AOA数据通道*/
#define BULK_RECV_EP    0x83
#define BULK_SEND_EP    0x02
#define INT_RECV_EP     0x81
#define INT_SEND_EP     0x01

#define VID_GOOGLE            0x18D1
#define    PID_AOA_ACC            0x2D00
#define    PID_AOA_ACC_ADB        0x2D01
#define    PID_AOA_AU            0x2D02
#define    PID_AOA_AU_ADB        0x2D03
#define    PID_AOA_ACC_AU        0x2D04
#define    PID_AOA_ACC_AU_ADB    0x2D05

//PID	模式
//0x2D00	accessory
//0x2D01	accessory + adb
//0x2D02	audio
//0x2D03	audio + adb
//0x2D04	accessory + audio
//0x2D05	accessory + audio + adb

static char manufacturer[] = "Lutixia";
static char modelName[] = "Demo";
static char description[] = "Android Aoa Interface";
static char version[] = "1.0";
static char uri[] = "https://www.baidu.com/";
static char serialNumber[] = "1234567890";


typedef struct
{
    unsigned int pid;
    unsigned int vid;
    unsigned char bInEndpointAddress;
    unsigned char bOutEndpointAddress;
    unsigned char bInterfaceNumber;
    libusb_device *dev;
    pthread_mutex_t stLock;
    libusb_device_handle *handle;
    libusb_hotplug_callback_handle hotplugCbh;
    int event;
    int bNumConfigurations;
    //libusb_device **devs;
} usb_dev_mngr_s;

static usb_dev_mngr_s gstUsbMngr;

void usb_error(int code, int line)
{
    fprintf(stdout,"line %d:", line);

    switch (code)
    {
        case LIBUSB_ERROR_IO:
            fprintf(stderr, "LIBUSB_ERROR_IO\n");
            break;
        case LIBUSB_ERROR_INVALID_PARAM:
            fprintf(stderr, "LIBUSB_ERROR_INVALID_PARAM\n");
            break;
        case LIBUSB_ERROR_ACCESS:
            fprintf(stderr, "Error: LIBUSB_ERROR_ACCESS\n");
            break;
        case LIBUSB_ERROR_NO_DEVICE:
            fprintf(stderr,    "LIBUSB_ERROR_NO_DEVICE\n");
            break;
        case LIBUSB_ERROR_NOT_FOUND:
            fprintf(stderr,    "LIBUSB_ERROR_NOT_FOUND\n");
            break;
        case LIBUSB_ERROR_BUSY:
            fprintf(stderr,    "LIBUSB_ERROR_BUSY\n");
            break;
        case LIBUSB_ERROR_TIMEOUT:
            fprintf(stderr,    "LIBUSB_ERROR_TIMEOUT\n");
            break;
        case LIBUSB_ERROR_OVERFLOW:
            fprintf(stderr,    "LIBUSB_ERROR_OVERFLOW\n");
            break;
        case LIBUSB_ERROR_PIPE:
            fprintf(stderr,    "LIBUSB_ERROR_PIPE\n");
            break;
        case LIBUSB_ERROR_INTERRUPTED:
            fprintf(stderr,    "LIBUSB_ERROR_INTERRUPTED\n");
            break;
        case LIBUSB_ERROR_NO_MEM:
            fprintf(stderr,    "LIBUSB_ERROR_NO_MEM\n");
            break;
        case LIBUSB_ERROR_NOT_SUPPORTED:
            fprintf(stderr,    "LIBUSB_ERROR_NOT_SUPPORTED\n");
            break;
        case LIBUSB_ERROR_OTHER:
            fprintf(stderr,    "LIBUSB_ERROR_OTHER\n");
            break;
        default:
            fprintf(stderr,"unkown error\n");
            break;
    }
}

static int usb_getEndpoint(const struct libusb_interface_descriptor * interface, usb_dev_mngr_s* user_device)
{
    int i;
    int ret = 0;
    const struct libusb_endpoint_descriptor *epdesc;

    for(i=0; i<interface->bNumEndpoints; i++)
    {
        epdesc = &interface->endpoint[i];

        if(epdesc->bmAttributes == LIBUSB_TRANSFER_TYPE_BULK) //transfer type :bulk
        {
            if(epdesc->bEndpointAddress & LIBUSB_ENDPOINT_IN) // in endpoint
            {
                printf("EP IN: %02x \n", epdesc->bEndpointAddress);
                   user_device->bInEndpointAddress = epdesc->bEndpointAddress;
            }
            //else if (epdesc->bEndpointAddress & LIBUSB_ENDPOINT_OUT)// out endpoint
            else if (!(epdesc->bEndpointAddress & LIBUSB_ENDPOINT_IN))
            {
                printf("EP OUT: %02x \n", epdesc->bEndpointAddress);
                   user_device->bOutEndpointAddress = epdesc->bEndpointAddress;
            }
        }

        if (user_device->bInEndpointAddress && user_device->bOutEndpointAddress)
        {
            ret = 1;
            break;
        }
    }

    return ret;
}

//获取USB的信息
static int usb_getUSBInfo(usb_dev_mngr_s* user_device)
{
    int rv = -1;
    int i,j,k;
    struct libusb_config_descriptor *conf_desc = NULL;
    const struct libusb_interface *inter;
    const struct libusb_interface_descriptor *interdesc;

    for (i = 0; i < user_device->bNumConfigurations; i++)
    {
        if(user_device->dev != NULL)
        {
            if (LIBUSB_SUCCESS != libusb_get_config_descriptor(user_device->dev, i, &conf_desc))
            {
                continue;
            }
        }

        for (j = 0; j < conf_desc->bNumInterfaces; j++)
        {
            inter = &conf_desc->interface[j];

            for (k=0; k < inter->num_altsetting; k++)
            {
                interdesc = &inter->altsetting[k];

                if(interdesc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC && interdesc->bInterfaceSubClass == LIBUSB_CLASS_VENDOR_SPEC)
                {
                    if (user_device->bInEndpointAddress <= 0 || user_device->bOutEndpointAddress <= 0)
                    {
                         if(usb_getEndpoint(interdesc, user_device))
                        {
                            user_device->bInterfaceNumber = interdesc->bInterfaceNumber;
                            rv = 0;
                            break;
                        }
                    }
                }
                else
                {
                    printf("bInterfaceClass:%02x, bInterfaceSubClass:%02x\n", interdesc->bInterfaceClass, interdesc->bInterfaceSubClass);
                }
            }
        }

        libusb_free_config_descriptor(conf_desc);
        conf_desc = NULL;

        if (0 == rv)
        {
            break;
        }
    }

    return rv;
}

//检查安卓设备是否处于Accessory模式
static int usb_checkAccessory(uint16_t idVendor, uint16_t idProduct)
{
    if (idVendor == VID_GOOGLE)
    {
        switch (idProduct)
        {
            case PID_AOA_ACC:
            case PID_AOA_ACC_ADB:
            case PID_AOA_ACC_AU:
            case PID_AOA_ACC_AU_ADB:
                return 1;

            //音频
            case PID_AOA_AU:
            case PID_AOA_AU_ADB:
                break;

            default:
                break;
        }
    }

    return 0;
}

static int usb_sendCtrl(libusb_device_handle *handle, char *buff, int req, int index)
{
    int ret = 0;

    if (NULL != buff)
    {
        ret = libusb_control_transfer(handle, 0x40, req, 0, index, buff, strlen(buff) + 1, 0);
    }
    else
    {
        ret = libusb_control_transfer(handle, 0x40, req, 0, index, NULL, 0, 0);
    }

    if (ret < 0) {
        usb_error(ret, __LINE__);
    }

    return ret;
}

static int usb_setupAccessory(libusb_device *dev)
{
    unsigned char ioBuffer[2] = {0};
    int aoaVersion;
    int ret = 0;
    libusb_device_handle *handle = NULL;

    ret = libusb_open (dev, &handle);
    if (LIBUSB_SUCCESS != ret)
    {
        printf("libusb_open failed, ret:%d\n", ret);
        usb_error(ret, __LINE__);
        return -1;
    }

    //如果USB连接到内核驱动则进行 detach
    //libusb_set_auto_detach_kernel_driver(gstUsbMngr.handle, 1);
    if(libusb_kernel_driver_active(handle, 0) > 0)
    {
        printf("kernel driver active, ignoring device");
        if(libusb_detach_kernel_driver(handle, 0) != LIBUSB_SUCCESS)
        {
            printf("failed to detach kernel driver, ignoring device");
            goto EXIT;;
        }
    }

    //发送序号为51的USB报文,获取手机的AOA协议版本,目前为1或2
    ret = libusb_control_transfer(handle, 0xC0, 51, 0, 0, ioBuffer, 2, 1000);
    if (ret < 0)
    {
        usb_error(ret, __LINE__);
        goto EXIT;
    }

    aoaVersion  = ioBuffer[1] << 8 | ioBuffer[0];
    printf("AOA Verion: %d \n", aoaVersion);
    if (aoaVersion != 1 && aoaVersion != 2)
    {
        goto EXIT;
    }

    ret = -1;
    usleep(1000);

    //发送序号为52的USB报文,涉及制造商、型号、版本、设备描述、序列号、uri
    if (usb_sendCtrl(handle, manufacturer, 52, 0) < 0) {
        goto EXIT;
    }
    if (usb_sendCtrl(handle, modelName, 52, 1) < 0) {
        goto EXIT;
    }
    if (usb_sendCtrl(handle, description, 52, 2) < 0) {
        goto EXIT;
    }
    if (usb_sendCtrl(handle, version, 52, 3) < 0) {
        goto EXIT;
    }
    if (usb_sendCtrl(handle, uri, 52, 4) < 0) {
        goto EXIT;
    }
    if (usb_sendCtrl(handle, serialNumber, 52, 5) < 0) {
        goto EXIT;
    }

    printf("Accessory Identification sent\n");

    //发送序号为53的USB报文,切换USB模式
    if (usb_sendCtrl(handle, NULL, 53, 0) < 0) {
        goto EXIT;
    }

    ret = 0;

EXIT:
    libusb_close(handle);
    handle = NULL;
    return ret;
}

static int usb_openUSB(void)
{
    int rc = 0;

    rc = libusb_open(gstUsbMngr.dev, &gstUsbMngr.handle);
    if (LIBUSB_SUCCESS != rc)
    {
        return -1;
    }

    //声明读写接口
    rc = libusb_claim_interface(gstUsbMngr.handle, gstUsbMngr.bInterfaceNumber);
    if (LIBUSB_SUCCESS != rc)
    {
        libusb_close(gstUsbMngr.handle);
        gstUsbMngr.handle = NULL;
        return -1;
    }

    return 0;
}

/*
    不能在usb_hotplugCallback调用usb_handleHotplugArrived和usb_handleHotplugLeft
    否则会出现LIBUSB_ERROR_BUSY
*/
int usb_handleHotplugArrived(void)
{
    int rc = 0;

    printf("===>> usb_handleHotplugArrived \n");
    pthread_mutex_lock(&gstUsbMngr.stLock);

    if (!usb_checkAccessory(gstUsbMngr.vid, gstUsbMngr.pid))//不是AOA模式
    {
        rc = usb_setupAccessory(gstUsbMngr.dev);
        if (rc)
        {
            fprintf (stderr, "usb_setupAccessory Error, rc:%d\n", rc);
            pthread_mutex_unlock(&gstUsbMngr.stLock);
            return -1;
        }
    }
    else    //是AOA模式
    {
        rc = usb_getUSBInfo(&gstUsbMngr);
        if (rc)
        {
            fprintf (stderr, "usb_getUSBInfo Error, rc:%d\n", rc);
            pthread_mutex_unlock(&gstUsbMngr.stLock);
            return -1;
        }

        rc = usb_openUSB();
        if (rc)
        {
            fprintf (stderr, "usb_openUSB Error, rc:%d\n", rc);
            pthread_mutex_unlock(&gstUsbMngr.stLock);
            return -1;
        }
    }

    pthread_mutex_unlock(&gstUsbMngr.stLock);
    return 0;
}

//LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT 设备拔出事件
int usb_handleHotplugLeft(void)
{
    if (gstUsbMngr.handle)
    {
        libusb_release_interface (gstUsbMngr.handle, gstUsbMngr.bInterfaceNumber);
        libusb_close (gstUsbMngr.handle);
        gstUsbMngr.handle = NULL;
    }

    return 0;
}

//USB 插入回调函数
static int LIBUSB_CALL usb_hotplugCallback(libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data)
{
    int rc;
    struct libusb_device_descriptor desc;

    pthread_mutex_lock(&gstUsbMngr.stLock);

    rc = libusb_get_device_descriptor(dev, &desc);
    if (LIBUSB_SUCCESS != rc) {
        fprintf (stderr, "Error getting device descriptor\n");
        pthread_mutex_unlock(&gstUsbMngr.stLock);
        return -1;
    }

    if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)//插入
    {
        if (0x1d6b == desc.idVendor && 0x0002 == desc.idProduct) //过滤设备本身存在的usb设备
        {
            pthread_mutex_unlock(&gstUsbMngr.stLock);
            return 0;
        }

        printf ("Device attached: %04x:%04x\n", desc.idVendor, desc.idProduct);

        gstUsbMngr.vid = desc.idVendor;
        gstUsbMngr.pid = desc.idProduct;
        gstUsbMngr.bInEndpointAddress = 0;
        gstUsbMngr.bOutEndpointAddress = 0;
        gstUsbMngr.bNumConfigurations = desc.bNumConfigurations;
        gstUsbMngr.dev = dev;
        gstUsbMngr.event = event;

    }
    else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)//拔出
    {
        printf ("Device detached: %04x:%04x\n", desc.idVendor, desc.idProduct);
        gstUsbMngr.event = event;
    }
    else
    {
        printf("event wrong, event: %d \n", event);
        gstUsbMngr.event = 0;
    }

    pthread_mutex_unlock(&gstUsbMngr.stLock);
    return 0;
}

//监听事件
void *usb_eventMonitorProcess(void *args)
{
    int rc = 0;

    while(1)
    {
        //处理事件
        //IBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED设备插入事件
        //LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT 设备拔出事件
        if (gstUsbMngr.event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)
        {
            usb_handleHotplugArrived();
            gstUsbMngr.event = 0;
        }
        else if (gstUsbMngr.event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
        {
            usb_handleHotplugLeft();
            gstUsbMngr.event = 0;
        }

        //事件循环(阻塞),读写USB会触发
        rc = libusb_handle_events(NULL);
        if (rc < 0)
        {
            printf("libusb_handle_events() failed: %s\n", libusb_error_name(rc));
        }
    }

    return NULL;
}

int usb_write(char *buf, int bufLen)
{
    int rc  = 0;
    int nActualBytes = 0;

    pthread_mutex_lock(&gstUsbMngr.stLock);

    if (NULL == gstUsbMngr.handle || 0 != gstUsbMngr.event)
    {
        pthread_mutex_unlock(&gstUsbMngr.stLock);
        return -1;
    }

    rc = libusb_bulk_transfer(gstUsbMngr.handle, gstUsbMngr.bOutEndpointAddress, (unsigned char *)buf, bufLen, &nActualBytes, 1000);
    if (rc < 0)
    {
        printf("libusb_bulk_transfer(0x01) write failed:[%s] \n", libusb_strerror(rc));
        pthread_mutex_unlock(&gstUsbMngr.stLock);
        return rc;
    }

    pthread_mutex_unlock(&gstUsbMngr.stLock);
    return nActualBytes;
}

int usb_read(char *buf, int bufLen)
{
    int rc  = 0;
    int nActualBytes = 0;

    pthread_mutex_lock(&gstUsbMngr.stLock);

    if (NULL == gstUsbMngr.handle || 0 != gstUsbMngr.event)
    {
        pthread_mutex_unlock(&gstUsbMngr.stLock);
        return -1;
    }

    rc = libusb_bulk_transfer(gstUsbMngr.handle, gstUsbMngr.bInEndpointAddress, (unsigned char *)buf, bufLen, &nActualBytes, 1000);
    if (rc < 0)
    {
        //printf("libusb_bulk_transfer(0x81) read failed:[%s] \n", libusb_strerror(rc));
        pthread_mutex_unlock(&gstUsbMngr.stLock);
        return rc;
    }

    pthread_mutex_unlock(&gstUsbMngr.stLock);
    return nActualBytes;
}

//测试读写
void *usb_readProcess(void *args)
{
    int rc = 0;
    char readBuf[4*1024] = {0};
    int nActualBytes = 0;

    while (1)
    {
        if (NULL == gstUsbMngr.handle)
        {
            usleep(100*1000);
            continue;
        }

        rc = usb_read(readBuf, sizeof(readBuf));
        if (rc > 0)
        {
            usb_write(readBuf, rc);
        }
    }
}

int usb_init(void)
{
    int product_id, vendor_id, class_id;
    int rc;
    pthread_t tid;

    vendor_id = LIBUSB_HOTPLUG_MATCH_ANY;//设置为 LIBUSB_HOTPLUG_MATCH_ANY 来匹配所有
    product_id = LIBUSB_HOTPLUG_MATCH_ANY;//设置为 LIBUSB_HOTPLUG_MATCH_ANY 来匹配所有
    class_id = LIBUSB_HOTPLUG_MATCH_ANY;//设置为 LIBUSB_HOTPLUG_MATCH_ANY 来匹配所有

    memset(&gstUsbMngr, 0x0, sizeof(usb_dev_mngr_s));

    rc = pthread_mutex_init(&gstUsbMngr.stLock, NULL);
    if (rc)
    {
        printf("pthread_mutex_init failed \n");
        return EXIT_FAILURE;
    }

    rc = libusb_init (NULL);
    if (rc < 0)
    {
        printf("failed to initialise libusb: %s\n", libusb_error_name(rc));
        return EXIT_FAILURE;
    }

    //获取当前库是否支持热插拔
    if (!libusb_has_capability (LIBUSB_CAP_HAS_HOTPLUG)) {
        printf("Hotplug capabilities are not supported on this platform\n");
        libusb_exit (NULL);
        return EXIT_FAILURE;
    }

    //注册热插拔事件回调
    //配件向 endpoint 0 端口中写入 51 号指令,
    //如果返回值为1 或者 2,则说明Android 设备支持 AOA1.0 或者 AOA2.0,
    //如果小于 1 或者大于 2,则说明连接设备不支持AOA协议或者支持的 AOA 协议版本号不正确。
    rc = libusb_hotplug_register_callback (NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, LIBUSB_HOTPLUG_ENUMERATE, vendor_id,
        product_id, class_id, usb_hotplugCallback, NULL, &gstUsbMngr.hotplugCbh);
    if (LIBUSB_SUCCESS != rc) {
        printf("libusb_hotplug_register_callback failed \n");
        libusb_exit (NULL);
        return EXIT_FAILURE;
    }

    //开启事件处理线程
    pthread_create(&tid, NULL, usb_eventMonitorProcess, NULL);

    return LIBUSB_SUCCESS;
}

int usb_deinit(void)
{
    if (gstUsbMngr.handle) {
        libusb_release_interface(gstUsbMngr.handle, gstUsbMngr.bInterfaceNumber);
        libusb_close (gstUsbMngr.handle);
        gstUsbMngr.handle = NULL;
    }

    if (0 < gstUsbMngr.hotplugCbh) {
        libusb_hotplug_deregister_callback(NULL, gstUsbMngr.hotplugCbh);
    }

    pthread_mutex_destroy(&gstUsbMngr.stLock);
    libusb_exit (NULL);
    return LIBUSB_SUCCESS;
}

int main(int argc, char *argv[])
{
    int rc;
    pthread_t tid;

    rc = usb_init();
    if (LIBUSB_SUCCESS != rc)
    {
        return -1;
    }

    pthread_create(&tid, NULL, usb_readProcess, NULL);

    while (1)
    {
        sleep(1);
    }

    usb_deinit();
    return EXIT_SUCCESS;
}

如果对你有帮助,请给予作者一丝丝奖励
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一篇搞定Linux和IOS或Android通讯(usbmuxd、libimobiledevice、libusb、AOA) 的相关文章

  • 在Android内存中存储gif图像

    我对安卓还很陌生 我想将图像保存到内存中 然后从内存中检索图像并将其加载到图像视图中 我已使用以下代码成功将图像存储在内存中 void saveImage String fileName img cnt jpg File file new
  • 在 Mac OS X 上构建 Linux 内核

    我正在做一个修改Linux内核的项目 我有一台桌面 Linux 机器 在上面构建内核没有问题 不过 我要去旅行 我想在途中工作 我只有一台 MacBook 当我尝试构建 Linux 内核时 它抱怨说elf h was not found 我
  • 如何修复“iptables:没有该名称的链/目标/匹配”?

    我在我的 Linux 嵌入式系统上构建并安装了 iptables 如果我列出所有规则 则一切正常 iptables list Chain INPUT policy ACCEPT target prot opt source destinat
  • Android:拍照后调用裁剪活动

    我在解析拍摄照片的 uri 来裁剪活动时遇到问题 在我的应用程序中 用户可以拍摄一张照片或从图库中选择一张照片 然后裁剪并上传 一切听起来都很简单 从图库中选择时 图库应用程序会返回所选照片的 uri 如下所示 content media
  • 无法找到/下载 AppCompat-v7:23.1.1

    怎么了 我遇到了很多 找不到 appcompat v7 23 1 1 的问题 许多解决方案都不起作用 经过几个小时的思考和寻找答案 我遇到了一个奇怪的问题 I have gotAndroid 支持库 23 1 1 已安装 所有功能 exce
  • 无论如何,要控制宋何时选择Android.bp,何时不选择?

    使用新的构建系统 即 Soong 安卓取代Android mk with Android bp 还有 Android Q 及以上版本 Soong将选择所有Android bp文件 无论所有文件都存在于何处 早些时候 对于 2 级和 3 级模
  • 修改linux下的路径

    虽然我认为我已经接近 Linux 专业人士 但显然我仍然是一个初学者 当我登录服务器时 我需要使用最新版本的R 统计软件 R 安装在 2 个地方 当我运行以下命令时 which R I get usr bin R 进而 R version
  • 取消通知

    我使用Onesignal推送通知 需要取消所有onPause和onResume的通知 NotificationManager notificationManager NotificationManager getApplicationCon
  • 如何在虚拟机 VirtualBox 上运行 Android-x86 4.2 iso?

    我想用Android x86测试和调试我的应用程序 我之前成功尝试过其他版本的Android x86 但是关于android x86 4 2有一个错误 所以我在这里问我的问题 因为它可能会发生在其他人身上 我安装了oracle VM vir
  • 以编程方式应用样式资源

    我没有找到一种以编程方式做到这一点的方法 所以我在这里发布这个问题 我也没有找到与此相关的任何问题 我有一个资源样式 在 res values styles xml 中定义 我想做的是使用 java 将这种样式应用到我正在操作的 View
  • 为什么我收到“无法进行二进制日志记录”的信息。在我的 MySQL 服务器上?

    当我今天启动 MySQL 服务器并尝试使用以下命令进行一些更改时用于 MySQL 的 Toad http www quest com toad for mysql 我收到此消息 MySQL 数据库错误 无法进行二进制日志记录 消息 交易级别
  • Android - 正确使用 invalidateOptionsMenu()

    我一直在寻找很多invalidateOptionsMenu 我知道它的作用 但我想不出这种方法在现实生活中有用的任何例子 我的意思是 例如 假设我们要添加一个新的MenuItem to our ActionBar 我们可以简单地获取菜单on
  • 在 Linux 上更快地分叉大型进程?

    在现代 Linux 上达到与 Linux 相同效果的最快 最好的方法是什么 fork execve combo 从一个大的过程 我的问题是进程分叉大约 500MByte 大 并且一个简单的基准测试只能从进程中实现约 50 个分叉 秒 比较最
  • 是否可以在Linux上将C转换为asm而不链接libc?

    测试平台为Linux 32位 但也欢迎 Windows 32 位上的某些解决方案 这是一个c代码片段 int a 0 printf d n a 如果我使用 gcc 生成汇编代码 gcc S test c 然后我会得到 movl 0 28 e
  • 如何以编程方式创建 CardView

    我正在开发一个 Android 应用程序Java Android Studio 我想在活动中创建CardView以编程方式 我想将以下属性设置为CardView layout width wrap content layout row 0
  • 从多个 TextView 中选择文本

    如何在android中从多个文本视图中选择文本 我已经尝试过以下代码 该代码一次仅适用于一个文本视图 我想一次性从许多文本视图中复制文本 android textIsSelectable true 你不能同时这样做 您需要在单个文本视图中设
  • 在上下文操作模式下选择时,ListView 项目不会在视觉上“突出显示”

    我关注了 Android 官方网站创建上下文操作菜单的教程 http developer android com guide topics ui menus html CAB 使用下面的代码 当我长按我的 ListView 项目之一时 它确
  • compileReleaseKotlin 失败并出现 java.lang.ClassNotFoundException:com.sun.tools.javac.util.Context

    我正在尝试使用 gradlew 通过终端构建我的 Android 项目 其中包含库模块 在 Android Studio 中 它编译并安装成功 但是当我尝试运行时 gradlew assembleDebug我得到以下堆栈跟踪 Using k
  • 画布:尝试使用回收的位图错误

    我是一个相当新的程序员 所以任何建议将不胜感激 我有一个类 每次调用它时都会在循环中运行 AsyncTask AsyncTask 看起来像这样 public class LoadImageTask extends AsyncTask
  • 我可以通过在 Android Activity 中声明适当的成员“静态”来提高效率吗

    如果一个 Activity 在实践中是单例 我认为我可以通过声明适当的成员 静态 来获得一些效率 且风险为零 是的 The Android 文档说 http developer android com guide topics fundam

随机推荐

  • An Introduction to UE4 Plugins

    An Introduction to UE4 Plugins Rate this Article 3 67 3 votes Approved for Versions 4 2 4 3 4 4 4 5 Contents hide
  • <OpenCV> Mat属性

    OpenCV的图像数据类型可参考之前的博客 https blog csdn net thisiszdy article details 120238017 OpenCV Mat类型的部分属性如下 size 矩阵的大小 s i z e
  • VMware虚拟机网络设置(三种网络模式)

    VMware虚拟机网络设置 三种网络模式 VMware网络使用windows虚拟机客户端时一般默认NAT模式自动可以上网 近日安装macos时上网却不行 网上搜索后自己整理出来 对三种模式自己的看法 首先 找到编辑 gt 虚拟网络编辑器 虚
  • k数和

    思路 这道题感觉是一个非常好的动态规划的题目 动态规划方程 d i j target d i 1 j target d i 1 j 1 target a i d i j t a
  • pigz搭配tar开启不了多线程,还是很慢

    Q pigz搭配tar开启不了多线程 还是很慢 A 注意你的压缩的文件夹或文件的名字不要包含 字符 其他字符未尝试
  • Java代码中对文件的操作

    引言 这几天的项目涉及到了文件的操作 我这边做一下整理 环境说明 jdk版本 1 8 0 311 对文件的操作 1 保存文件 保存文件 param file 文件 param path 文件保存目录 param name 保存后的文件名字
  • 怎么在浏览器的控制台上换行输入?

    使用快捷键 shift enter 如
  • python numpy.meshgrid()函数的用法

    numpy meshgrid xi kwargs 从一个坐标向量中返回坐标矩阵 参数 x1 x2 xn array like 表示网格坐标的一维数组 indexing xy ij 可选 笛卡尔 xy 默认值 或矩阵 ij 输出索引 spar
  • matlab跟踪目标图像边缘并计算长轴短轴

    题目如下 对图像进行处理和分析 跟踪出目标边缘 并计算出目标的长轴和短轴及方向 原始图像如图1所示 图1 原始图像 一 处理过程 1 边缘提取 目标边缘提取在图像处理学科中属于数学形态学 在提取目标边缘之前有认真思考过应该用什么方法 是用4
  • java的内存机制以及变量的作用域

    初学者对于Java的类和对象往往一头雾水 尤其是当涉及到程序细节的时候 出现混乱似乎在所难免 笔者根据自己的学习经验 认为 理解java的内存机制以及变量的种类和作用域 对于精确把握编程规则相当重要 一 java内存机制 java程序在内存
  • Springboot整合Mysql集群

    文章目录 一 方法一 1 1 默认配置 1 2 需要自定义配置 1 3 自定义数据库配置类 1 4对从库进行操作 在写一个配置类 一 方法一 1 1 默认配置 1 2 需要自定义配置 1 3 自定义数据库配置类 第一步 添加连接池驱动 第二
  • Java内存的Used、Committed、Max的区别

    不想看英文 可直接看最后的结论 A MemoryUsage object represents a snapshot of memory usage Instances of the MemoryUsage class are usuall
  • 【C语言进阶】文件操作(二)

    大家好我是沐曦希 文件操作 1 前言 2 文件的随机读写 2 1 fseek函数 2 2 ftell函数 2 3 rewind 3 文本文件和二进制文件 4 文件读取结束的判定 4 1 被错误使用的feof 5 文件缓冲区 6 写在最后 1
  • rsync远程同步实现快速、安全、高效的异地备份

    目录 一 rsync介绍 1 rsync是什么 2 rsync同步方式 3 rsync的特性 4 rsync的应用场景 5 rsync与cp scp对比 6 rsync同步源 二 rsync命令 1 常用选项 2 实例 本地复制对比 3 配
  • React(二):React开发神器Webpack

    React 一 React的设计哲学 简单之美 React 二 React开发神器Webpack React 三 理解JSX和组件 React 四 虚拟DOM Diff算法解析 React 五 使用Flux搭建React应用程序架构 上一篇
  • 基于LinearLayout的小标签(TextView)自动换行(修改)

    设计最初是因为公司项目需要多处显示多个小标签 并且需要多行展示 最开始使用的GridLayout 但是这个网格布局局限性太高 标签是动态的 内容也不定 用GridLayout就会有多行占用的各种显示问题 所以后来换成了LinearLayou
  • 【源码学习】正则表达式

    模式 Patterns 和修饰符 flags 正则表达式是提供了一种在文本中进行搜索和替换的强大的方式的模式 在 JavaScript 中 我们可以通过 RegExp 对象使用它们 也可以与字符串方法结合使用 正则表达式 正则表达式 可叫作
  • 如何制作一个微信小程序【微信小程序是怎么做的】

    为什么现在这么多人使用微信小程序呢 因为微信小程序除了便捷易开发 公司企业可以用来做小程序展示官网 商家也可以做小程序商城 甚至个人也可以拥有自己的小程序 那么如何制作一个微信小程序 微信小程序是怎么做的呢 下面给大家说说简单的流程 一 要
  • IGBT的绘制与逆变器的绘制-Visio制图总结【电控类】(三)

    在逆变器非线性补偿研究方向的文章中看到了一些逆变器的示意图 发现均有手绘的痕迹 碰巧在AxGlyph软件中有IGBT这个元件 就截图仿着画了一张IGBT的图 下文中会给出一张IGBT图片和两张逆变器图片 绘图步骤 把截到的IGBT图片粘贴到
  • 一篇搞定Linux和IOS或Android通讯(usbmuxd、libimobiledevice、libusb、AOA)

    1 Linux要与苹果手机通讯需要两个组件 1 usbmuxd 是苹果的一个服务 这个服务主要用于在USB协议上实现多路TCP连接 将USB通信抽象为TCP通信 苹果的iTunes Xcode 都直接或间接地用到了这个服务 参考链接 htt