zoukankan      html  css  js  c++  java
  • 在Mac OS X上无法使用C + libusb声明USB接口

    Unable to claim USB interface with C + libusb on Mac OS X

    我有一个使用PIC32单片机构建的复合USB + CDC设备,我正在尝试连接到该设备并从Mac将一些数据发送到CDC数据接口端点。

    我知道电路可以工作100%,因为该设备同时注册为HID操纵杆,并且我可以使用/dev/tty.usbmodemfa132上的Zoc终端连接到该设备。我可以使用Zoc发送命令,并通过闪烁电路上的一些LED来查看我的MCU对这些命令的响应。

    我在Mac OS X Mavericks上运行此程序,但是在几个星期前我在Mountain Lion上放弃的类似示例也遇到了同样的问题。

    我的代码如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    // Includes -----------------------------------------------------------------------------------------------------------
    #include <stdbool.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <libusb-1.0/libusb.h>
    #include <unistd.h>

    // Defines ------------------------------------------------------------------------------------------------------------
    #define VID 0x04d8
    #define PID 0x005e
    #define CDC_DATA_INTERFACE_ID 2

    // Function Declarations ----------------------------------------------------------------------------------------------
    void print_device(libusb_device *device);
    void send(libusb_context *usb_context, uint16_t vid, uint16_t pid);

    // Function Definitions -----------------------------------------------------------------------------------------------

    /**
     * main
     */

    int main(int argc, char **argv)
    {
        libusb_device **usb_devices = NULL;
        libusb_context *usb_context = NULL;
        ssize_t device_count = 0;
        bool debug_enabled = false;
        int c;

        // Collect command line attributes
        while ( (c = getopt(argc, argv,"d")) != -1) {
            switch (c) {
                case 'd':
                    debug_enabled = true;
                    break;
            }
        }

        // Initialize USB context
        int result = libusb_init(&usb_context);
        if(result < 0) {
            printf("Unable to initialise libusb!");
            return EXIT_FAILURE;
        }

        // Turn debug mode on/off
        if(debug_enabled) {
            libusb_set_debug(usb_context, 3);
        }

        // Get USB device list
        device_count = libusb_get_device_list(usb_context, &usb_devices);
        if(device_count < 0) {
            puts("Unable to retrieve USB device list!");
        }

        // Iterate and print devices
        puts("VID    PID     Manufacturer Name\
    ------ ------ -------------------"
    );
        for (int i = 0; i < device_count; i++) {
            print_device(usb_devices[i]);
        }

        // Attempt to send data
        send(usb_context, VID, PID);

        // Cleanup and exit
        libusb_free_device_list(usb_devices, 1);
        libusb_exit(usb_context);
        return EXIT_SUCCESS;
    }

    /**
     * print_device
     */

    void print_device(libusb_device *device)
    {
        struct libusb_device_descriptor device_descriptor;
        struct libusb_device_handle *device_handle = NULL;

        // Get USB device descriptor
        int result = libusb_get_device_descriptor(device, &device_descriptor);
        if (result < 0) {
            printf("Failed to get device descriptor!");
        }

        // Only print our devices
        if(VID == device_descriptor.idVendor && PID == device_descriptor.idProduct) {
            // Print VID & PID
            printf("0x%04x 0x%04x", device_descriptor.idVendor, device_descriptor.idProduct);
        } else {
            return;
        }

        // Attempt to open the device
        int open_result = libusb_open(device, &device_handle);
        if (open_result < 0) {
            libusb_close(device_handle);
            return;
        }

        // Print the device manufacturer string
        char manufacturer[256] ="";
        if (device_descriptor.iManufacturer) {
            libusb_get_string_descriptor_ascii(device_handle, device_descriptor.iManufacturer,
                (unsigned char *)manufacturer, sizeof(manufacturer));
            printf(" %s", manufacturer);
        }

        puts("");

        libusb_close(device_handle);
    }

    /**
     * send
     */

    void send(libusb_context *usb_context, uint16_t vid, uint16_t pid)
    {
        libusb_device_handle *device_handle;
        device_handle = libusb_open_device_with_vid_pid(usb_context, vid, pid);

        if (device_handle == NULL) {
            puts("Unable to open device by VID & PID!");
            return;
        }
        puts("Device successfully opened");

        unsigned char *data = (unsigned char *)"test";

        if (libusb_kernel_driver_active(device_handle, CDC_DATA_INTERFACE_ID)) {
            puts("Kernel driver active");
            if (libusb_detach_kernel_driver(device_handle, CDC_DATA_INTERFACE_ID)) {
                puts("Kernel driver detached");
            }
        } else {
            puts("Kernel driver doesn't appear to be active");
        }

        int result = libusb_claim_interface(device_handle, CDC_DATA_INTERFACE_ID);
        if (result < 0) {
            puts("Unable to claim interface!");
            libusb_close(device_handle);
            return;
        }
        puts("Interface claimed");

        int written = 0;
        result = libusb_bulk_transfer(device_handle, (3 | LIBUSB_ENDPOINT_OUT), data, 4, &written, 0);
        if (result == 0 && written == 4) {
            puts("Send success");
        } else {
            puts("Send failed!");
        }

        result = libusb_release_interface(device_handle, CDC_DATA_INTERFACE_ID);
        if (result != 0) {
            puts("Unable to release interface!");
        }

        libusb_close(device_handle);
    }

    我收到以下错误输出:

    1
    2
    3
    4
    5
    6
    7
    libusb: 0.828223 error [darwin_open] USBDeviceOpen: another process has device opened for exclusive access
    libusb: 0.828241 info [darwin_open] device open for access
    Device successfully opened
    Kernel driver doesn't appear to be active
    libusb: 0.828641 error [darwin_claim_interface] USBInterfaceOpen: another process has device opened for exclusive access
    Unable to claim interface!
    libusb: 0.828766 info [event_thread_main] thread exiting

    有什么方法可以释放其他过程中的USB设备,释放它以便我可以声明它吗?

    有没有其他方法可以连接到/dev/tty.usbmodemfa132以向USB设备上的CDC接口发送和接收数据?

    也许是libusb的替代品?


    那就对了。尽管libusb在Linux中似乎是功能强大的,但是您不能使用它来连接Mac OS X上的USB CDC接口,因为AppleUSBCDCACM驱动程序已经声明了该接口。

    您应该做的是使用人们连接到串行端口的标准方法。这将更容易,因为您不必担心端点和批量传输等。这是我为基于CDC的产品之一编写的一些跨平台C代码示例,该产品连接到COM端口以读取和写入一些数据(源)。它使用标准功能open,read和write。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    // Uses POSIX functions to send and receive data from a Maestro.
    // NOTE: You must change the 'const char * device' line below.

    #include <fcntl.h>
    #include <stdio.h>
    #include <unistd.h>

    #ifdef _WIN32
    #define O_NOCTTY 0
    #else
    #include <termios.h>
    #endif

    // Gets the position of a Maestro channel.
    // See the"Serial Servo Commands" section of the user's guide.
    int maestroGetPosition(int fd, unsigned char channel)
    {
      unsigned char command[] = {0x90, channel};
      if(write(fd, command, sizeof(command)) == -1)
      {
        perror("error writing");
        return -1;
      }

      unsigned char response[2];
      if(read(fd,response,2) != 2)
      {
        perror("error reading");
        return -1;
      }

      return response[0] + 256*response[1];
    }

    // Sets the target of a Maestro channel.
    // See the"Serial Servo Commands" section of the user's guide.
    // The units of 'target' are quarter-microseconds.
    int maestroSetTarget(int fd, unsigned char channel, unsigned short target)
    {
      unsigned char command[] = {0x84, channel, target & 0x7F, target >> 7 & 0x7F};
      if (write(fd, command, sizeof(command)) == -1)
      {
        perror("error writing");
        return -1;
      }
      return 0;
    }

    int main()
    {
      // Open the Maestro's virtual COM port.
      const char * device ="\\\\\\\\.\\\\USBSER000";  // Windows,"\\\\\\\\.\\\\COM6" also works
      //const char * device ="/dev/ttyACM0";  // Linux
      //const char * device ="/dev/cu.usbmodem00034567"; // Mac OS X
      int fd = open(device, O_RDWR | O_NOCTTY);
      if (fd == -1)
      {
        perror(device);
        return 1;
      }

    #ifndef _WIN32
      struct termios options;
      tcgetattr(fd, &options);
      options.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
      options.c_oflag &= ~(ONLCR | OCRNL);
      tcsetattr(fd, TCSANOW, &options);
    #endif

      int position = maestroGetPosition(fd, 0);
      printf("Current position is %d.\
    "
    , position);

      int target = (position < 6000) ? 7000 : 5000;
      printf("Setting target to %d (%d us).\
    "
    , target, target/4);
      maestroSetTarget(fd, 0, target);

      close(fd);
      return 0;
    }

    如果要使用Apple FTDI串行驱动程序也可以识别的某些USB设备,则可以先卸载该驱动程序:

    1
    sudo kextunload -b com.apple.driver.AppleUSBFTDI

    之后,您可以正常通过libusb使用它。

    对于被识别为串行设备的其他设备,您可能需要卸载其他驱动程序。


    问题似乎是由于使用相同库的不同驱动程序之间存在冲突,在我的情况下,它们与先前的三星设备安装有关。我已经解决了这种方式:

    1
    kextstat | grep -v apple

    获得这样的回报:

    1
    2
    3
      70    0 0x57574000 0x3000     0x2000     com.devguru.driver.SamsungComposite (1.2.4) <33 4 3>
      72    0 0x57831000 0x7000     0x6000     com.devguru.driver.SamsungACMData (1.2.4) <71 33 5 4 3>
      94    0 0x57674000 0x3000     0x2000     com.devguru.driver.SamsungACMControl (1.2.4) <33 4 3>

    然后:

    1
    2
    3
    $ sudo kextunload -b com.devguru.driver.SamsungComposite
    $ sudo kextunload -b com.devguru.driver.SamsungACMData
    $ sudo kextunload -b com.devguru.driver.SamsungACMControl
  • 相关阅读:
    Numpy库
    使用Python的pandas-datareader包下载雅虎财经股价数据
    python引用库异常总结
    桌面常用快捷键
    第四章 数据的概括性度量
    第三章 数据的图表展示
    python连接MySql数据库
    如何利用scrapy新建爬虫项目
    幼儿教育
    PyMySQL和MySQLdb的区别
  • 原文地址:https://www.cnblogs.com/jiftle/p/15611532.html
Copyright © 2011-2022 走看看