3

I'm writing a simple library that uses libusb to connect to my custom hardware that sends signals to a host device every 50ms. It's designed to provide a simple abstraction layer so that users are not bothered with libusb at all. I need to pass a pointer to a non-static class member function to libusb_fill_bulk_transfer. I.e. I need to have a separate callback for every instance of MyDevice without exposing libusb logic to my users.

The basic design is:

  • init the lib

    int mylib_init(){
      libusb_init(&context);
    }
  • find all compatible devices

    int mylib_get_valid_devices(vector devs){
     //...
     libusb_device **devs1;
     int countAll = libusb_get_device_list(context, &devs1);
     //... fill devs
    }
    int mylib_print_device_info(MyDevice* dev); 
  • connect to as many devices as the user likes

    int mylib_init_device(MyDevice* dev){
       libusb_open(dev->device, &dev->dev_handle);
       // check for kernel driver & remove if needed
       libusb_claim_interface(dev->dev_handle, 0);
       //...
    }
  • set up callbacks for incoming data to all instances of connected devices

    int mylib_start_transmission_async(MyDevice* dev, MyLibCallbackIn user_cb, unsigned char* buffer_in, int bufferSize){
       libusb_control_transfer(dev->dev_handle, p1, p2, p3, p4, p5, p6, p7);
       // .. rest of libusb_control_transfer -s
    
       //start device:
       int actual;
       unsigned char startSig = 0b00110111;
       libusb_bulk_transfer(dev->dev_handle, (1 | LIBUSB_ENDPOINT_OUT), &tosend, 1, &actual, 100);
    
       dev->user_callback = user_cb;
       dev->buffer_in = buffer_in; 
       dev->bufferSize = bufferSize;
    
       //setup transfer
       dev->transfer_in = libusb_alloc_transfer(0);
       libusb_fill_bulk_transfer(dev->transfer_in, dev->dev_handle, LIBUSB_ENDPOINT_IN, dev->buffer_in,dev->bufferSize, cb_in, NULL, 0);
       libusb_submit_transfer(dev->transfer_in); 
    
    } 
  • disconnect when finished

    int mylib_disconnect_device(MyDevice* device);

  • exit the lib

    int mylib_exit();

MyDevice is defined as:

typedef int (*MyLibdCallbackIn)(unsigned char* buffer, int length);

class MyDevice{

  unsigned char* buffer_in;
  unsigned char* buffer_out;

  libusb_device* device = nullptr;
  libusb_device_handle* dev_handle = nullptr;

  struct libusb_transfer* transfer_in = nullptr;
  struct libusb_transfer* transfer_out = nullptr;

//this obviously wouldn't work because libusb doesn't accept it as a param:
  void LIBUSB_CALL cb_in(struct libusb_transfer* transfer); 
  MyLibCallbackIn user_callback;

  void run();
  //...

}

and cb_in is defined as:

void LIBUSB_CALL MyDevice::cb_in(libusb_transfer* transfer){
  int r = libusb_submit_transfer(this->transfer_in);
  callback_in(transfer->buffer, transfered);

}

I can't pass MyDevice::cb_in to libusb_fill_bulk_transfer because the function pointer types are incompatible. In the same time I don't want my users to have to write a callback function with libusb_transfer* as a parameter (exposing to libusb) to pass directly to libusb_fill_bulk_transfer.

EDIT: I tried with

void LIBUSB_CALL callback_wrapper(struct libusb_transfer* transfer){
  MyDevice* dev = reinterpret_cast<MyDevice*>(transfer->user_data);
  dev->cb_in(transfer);
}

but get Sementation fault (Core dumped) error

Valkova.V
  • 243
  • 1
  • 3
  • 13
  • Possible duplicate of [How can I pass a class member function as a callback?](https://stackoverflow.com/questions/400257/how-can-i-pass-a-class-member-function-as-a-callback) – Max Langhof Dec 13 '18 at 12:02
  • 1
    See the description of the `user_data` parameter. Use it to pass `this` to a static callback function, which casts it back to an object pointer, and then invokes the class method. – Sam Varshavchik Dec 13 '18 at 12:04
  • 1
    Regarding your edit: Did you pass `dev` as user data? Your preceding code still shows the `void* user_data` parameter as `NULL`, which will certainly get you a segfault... Also, please make sure the device object in question is still alive (not just a copy of it) when the callback happens. – Max Langhof Dec 13 '18 at 12:23
  • yep, that did it. cudos! – Valkova.V Dec 13 '18 at 12:29

1 Answers1

1

As per Max Langhof and Sam Varshavchik's comments the solution is to pass the instance of MyDevice to libusb_fill_bulk_transfer . So:

int mylib_start_transmission_async(MyDevice* dev, MyLibCallbackIn user_cb, unsigned char* buffer_in, int bufferSize){

   //...
   // setup transfer:
   dev->transfer_in = libusb_alloc_transfer(0);
   libusb_fill_bulk_transfer(dev->transfer_in, dev->dev_handle, LIBUSB_ENDPOINT_IN, dev->buffer_in,dev->bufferSize, cb_in, dev, 0);
   libusb_submit_transfer(dev->transfer_in);                                                                               ^^^

} 

and then use the callback_wrapper as in the Edit. Thanks!

Valkova.V
  • 243
  • 1
  • 3
  • 13