在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 |