在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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM