No se puede reclamar la interfaz USB con C + libusb en Mac OS X
Tengo un dispositivo USB + CDC compuesto que construí utilizando un microcontrolador PIC32, y estoy tratando de conectarme al dispositivo y enviar algunos datos al punto final de la interfaz de datos CDC desde mi Mac.
Sé que el circuito funciona al 100%, ya que el dispositivo se registra como un joystick HID y puedo conectarme al dispositivo utilizando el terminal Zoc, en /dev/tty.usbmodemfa132. Puedo enviar comandos conZocy vea mi MCU respondiendo a estos comandos haciendo parpadear algunos LED en el circuito.
Estoy ejecutando esto en Mac OS X Mavericks, pero tuve el mismo problema con un ejemplo similar al que abandoné, hace unas semanas en Mountain Lion.
Mi código es el siguiente:
// 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);
}
Estoy obteniendo el siguiente error de salida:
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
¿Hay alguna forma de liberar el dispositivo USB del otro proceso, liberándolo para poder reclamarlo?
¿Hay alguna forma alternativa de conectarme a /dev/tty.usbmodemfa132 para enviar y recibir datos a la interfaz CDC en el dispositivo USB?
¿Una alternativa al libusb tal vez?