无法声明与C+;Mac OS X上的libusb

无法声明与C+;Mac OS X上的libusb,c,usb,libusb,cdc,libusb-1.0,C,Usb,Libusb,Cdc,Libusb 1.0,我有一个使用PIC32微控制器构建的复合USB+CDC设备,我正在尝试连接到该设备,并从Mac向CDC数据接口端点发送一些数据 我知道该电路可以100%工作,因为该设备同时注册为HID操纵杆,并且我能够使用/dev/tty.usbmodemfa132上的Zoc终端连接到该设备。我可以通过发送命令,并通过闪烁电路上的一些LED来查看我的MCU对这些命令的响应 我在MacOSXMavericks上运行这个程序,但是在几个星期前的MountainLion上,我放弃了一个类似的例子,遇到了同样的问题 我

我有一个使用PIC32微控制器构建的复合USB+CDC设备,我正在尝试连接到该设备,并从Mac向CDC数据接口端点发送一些数据

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

我在MacOSXMavericks上运行这个程序,但是在几个星期前的MountainLion上,我放弃了一个类似的例子,遇到了同样的问题

我的代码如下所示:

// 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\n------ ------ -------------------");
    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);
}
是否有一种方法可以将USB设备从另一个进程中释放出来,这样我就可以声明它了

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


也许是libusb的替代品?

没错。虽然libusb在Linux中似乎功能强大,但您不能使用它连接到Mac OS X上的USB CDC接口,因为该接口已由AppleusBCDACM驱动程序声明

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

// 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.\n", position); 

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

  close(fd);
  return 0;
}
//使用POSIX函数从Maestro发送和接收数据。
//注意:您必须更改下面的“const char*device”行。
#包括
#包括
#包括
#ifdef_WIN32
#定义O_NOCTTY 0
#否则
#包括
#恩迪夫
//获取Maestro通道的位置。
//请参阅《用户指南》的“串行伺服命令”部分。
int maestroGetPosition(int fd,无符号字符通道)
{
无符号字符命令[]={0x90,通道};
if(写入(fd,command,sizeof(command))=-1)
{
perror(“错误写入”);
返回-1;
}
无符号字符响应[2];
如果(读取(fd,响应,2)!=2)
{
perror(“错误读取”);
返回-1;
}
返回响应[0]+256*响应[1];
}
//设置大师频道的目标。
//请参阅《用户指南》的“串行伺服命令”部分。
//“目标”的单位是四分之一微秒。
int maestroSetTarget(int fd,无符号字符通道,无符号短目标)
{
unsigned char命令[]={0x84,channel,target&0x7F,target>>7&0x7F};
if(写入(fd,command,sizeof(command))=-1)
{
perror(“错误写入”);
返回-1;
}
返回0;
}
int main()
{
//打开Maestro的虚拟COM端口。
const char*device=“\\\\.\\USBSER000”//Windows,“\\\\\.\\COM6”也可以工作
//const char*device=“/dev/ttyACM0”;//Linux
//const char*device=“/dev/cu.usbmodem00034567”;//Mac OS X
int fd=打开(设备,O|RDWR | O|NOCTTY);
如果(fd==-1)
{
perror(装置);
返回1;
}
#ifndef\u WIN32
结构termios选项;
tcgetattr(fd和选项);
options.c|lflag&=~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
选项c(标签的U值)&=~(ONLCR | ocrl);
tcsetattr(fd、TCSANOW和选项);
#恩迪夫
int位置=大师位置(fd,0);
printf(“当前位置为%d.\n”,位置);
int目标=(位置<6000)?7000:5000;
printf(“将目标设置为%d(%d us)。\n”,目标,目标/4);
maestroSetTarget(fd,0,target);
关闭(fd);
返回0;
}

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

sudo kextunload -b com.apple.driver.AppleUSBFTDI
之后,您可以通过
libusb
正常使用它


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

问题似乎是由于使用相同库的不同驱动程序之间存在冲突,在我的案例中,这些驱动程序与以前的三星设备安装相关。我是这样解决的:

kextstat | grep -v apple
要获得这样的回报:

  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>

完成了。享受

Try
printf(“无法声明接口:%s\n”,libusb_错误_名称(结果))
以获取有关错误的更多信息。我想我听说过OSX在其他类型的复合设备上也存在此类问题-例如,程序员也有CDC串行通道。找到不同的数据发送方式会更容易吗?像zoc这样的终端如何连接到/dev/tty.usbmodema123?我打赌它不使用libusb。似乎Mac唯一的解决方案是使用C中的termios库直接连接到/dev/tty…哇,这比我想要的多得多。谢谢你,大卫!我开始尝试使用kextunload来杀掉Mac上的USB CDC驱动程序,但我认为这样做会使解决方案过于复杂。我接受你的答案,尽管我没有使用你的代码。这很有帮助,让我走上了正确的道路。我从中抓取了一个C代码片段,稍作修改。我的版本可以在上看到,因为被接受的答案更像是解决方法,而不是问题的答案。最终是否可以使用libusb来实现它?您可能会研究的另一个选项是。我不知道自从我写了我的答案后是否有任何变化。不幸的是,我需要测试lisusb,因为它将被使用,并且将在linux上工作
  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>
$ sudo kextunload -b com.devguru.driver.SamsungComposite
$ sudo kextunload -b com.devguru.driver.SamsungACMData
$ sudo kextunload -b com.devguru.driver.SamsungACMControl