无法在 Mac OS X 上使用 C + libusb 声明 USB 接口
Posted
技术标签:
【中文标题】无法在 Mac OS X 上使用 C + libusb 声明 USB 接口【英文标题】:Unable to claim USB interface with C + libusb on Mac OS X 【发布时间】:2013-12-13 17:41:33 【问题描述】:我有一个使用 PIC32 微控制器构建的复合 USB + CDC 设备,我正在尝试连接到该设备并将一些数据从我的 Mac 发送到 CDC 数据接口端点。
我知道电路可以 100% 工作,因为设备注册为 HID 操纵杆,并且我可以使用 Zoc 终端在 /dev/tty.usbmodemfa132 上连接到设备。我可以使用Zoc 发送命令,然后通过闪烁电路上的一些 LED 来查看我的 MCU 对这些命令的响应。
我在 Mac OS X Mavericks 上运行它,但几周前在 Mountain Lion 上放弃了一个类似的例子,遇到了同样的问题。
我的代码如下:
// 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);
我收到以下错误输出:
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 的替代品?
【问题讨论】:
尝试printf("Unable to claim interface: %s\n", libusb_error_name(result));
以获取有关错误的更多信息。
我想我听说过一些关于 OSX 在其他类型的复合设备上也存在这种问题的消息——例如,也有 CDC 串行通道的程序员。
找到不同的数据发送方式会更容易吗?像 zoc 这样的终端如何连接到 /dev/tty.usbmodema123?我敢打赌它不使用 libusb。
Mac 的唯一解决方案似乎是使用 C 中的 termios 库直接连接到 /dev/tty...
【参考方案1】:
没错。虽然 libusb 在 Linux 中似乎是无所不能的,但您不能使用它连接到 Mac OS X 上的 USB CDC 接口,因为 AppleUSBCDCACM 驱动程序已经声明了该接口。
您应该做的是使用人们连接到串行端口的标准方式。这会更容易,因为您不必担心端点和批量传输等。这是我为我们的一款基于 CDC 的产品编写的跨平台 C 代码示例,该产品连接到 COM 端口以读取和写入一些数据 (source)。它使用标准函数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;
【讨论】:
哇,这比我想要的要多得多。谢谢大卫!我开始尝试查看是否可以使用 kextunload 来杀死 Mac 上的 USB CDC 驱动程序,但我认为这种方式使解决方案过于复杂。 我接受你的回答,即使我没有使用你的代码。这很有帮助,让我走上了正确的道路。我从en.wikibooks.org/wiki/Serial_Programming/Serial_Linux#termios 抓取了一个 C sn-p 并稍作修改。我的版本车见gist.github.com/josefvanniekerk/7702279 因为接受的答案更像是解决方法而不是对问题的回答,最终是否可以使用 libusb 来实现? 您可能会考虑的另一个选项是libserialport。我不知道自从我写完答案后是否有任何变化。 不幸的是,我需要测试 lisusb,因为它将被使用并且可以在 linux 上运行【参考方案2】:如果你想使用苹果FTDI串口驱动也能识别的USB设备,可以先卸载驱动:
sudo kextunload -b com.apple.driver.AppleUSBFTDI
之后就可以通过libusb
正常使用了。
对于被识别为串行设备的其他设备,您可能需要卸载一些其他驱动程序。
【讨论】:
【参考方案3】:问题似乎是由于使用相同库的不同驱动程序之间存在冲突,在我的情况下,它们与以前的三星设备安装有关。我是这样解决的:
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>
然后:
$ sudo kextunload -b com.devguru.driver.SamsungComposite
$ sudo kextunload -b com.devguru.driver.SamsungACMData
$ sudo kextunload -b com.devguru.driver.SamsungACMControl
完成。享受
【讨论】:
以上是关于无法在 Mac OS X 上使用 C + libusb 声明 USB 接口的主要内容,如果未能解决你的问题,请参考以下文章
无法在 mac Os X 上使用 npm 安装 juggernaut - 版本 10.7.4
如何在 Mac OS X 终端上使用 make 编译 C 程序
无法在 Mac OS X 上启动 mysql 得到 mysql.sock 连接错误