Program Listing for File usb.hpp

Return to documentation for file (/tmp/ws/src/ublox_dgnss/ublox_dgnss_node/include/ublox_dgnss_node/usb.hpp)

// Copyright 2021 Australian Robotics Supplies & Technology
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef UBLOX_DGNSS_NODE__USB_HPP_
#define UBLOX_DGNSS_NODE__USB_HPP_

#include <libusb-1.0/libusb.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <iostream>
#include <exception>
#include <string>
#include <functional>
#include <deque>
#include <vector>
#include <memory>

#define F9_VENDOR_ID      0x1546   // U-Blox AG
#define F9_PRODUCT_ID     0x01a9   // u-blox GNSS receiver

#define ACM_CTRL_DTR   0x01
#define ACM_CTRL_RTS   0x02

#define IN_BUFFER_SIZE 64 * 10

namespace usb
{
using UCharVector = std::vector<u_char>;
enum TransferType {USB_IN, USB_OUT};
struct transfer_t
{
  struct libusb_transfer * transfer;
  std::shared_ptr<UCharVector> buffer;
  bool completed;
  TransferType type;
  transfer_t()
  {
    buffer = std::make_shared<UCharVector>();
  }
};

class TimeoutException : public std::exception
{
public:
  const char * what() const throw()
  {
    return "Timeout";
  }
};

class UsbException : public std::runtime_error
{
public:
  explicit UsbException(std::string msg)
  : std::runtime_error(msg)
  {
  }
};

// external function callback definitions for the connection class
typedef std::function<void (struct libusb_transfer * transfer)> connection_out_cb_fn;
typedef std::function<void (struct libusb_transfer * transfer)> connection_in_cb_fn;
typedef std::function<void (UsbException e, void * user_data)> connection_exception_cb_fn;
typedef std::function<void ()> hotplug_attach_cb_fn;
typedef std::function<void ()> hotplug_detach_cb_fn;

class Connection
{
private:
  libusb_context * ctx_;
  libusb_device_handle * devh_;
  libusb_device * dev_;

// hotplug
  hotplug_attach_cb_fn hp_attach_cb_fn_;
  hotplug_detach_cb_fn hp_detach_cb_fn_;
  libusb_hotplug_callback_handle hp_[2];

  int log_level_;
  int vendor_id_;
  int product_id_;
  int class_id_;
  int ep_data_out_addr_;
  int ep_data_in_addr_;
  int ep_comms_in_addr_;
  u_int8_t num_interfaces_ = 0;
  unsigned int timeout_ms_;

// asynchronous comms
  connection_out_cb_fn out_cb_fn_;
  connection_in_cb_fn in_cb_fn_;
  connection_exception_cb_fn exception_cb_fn_;
  struct timeval timeout_tv_;
  bool keep_running_;
  bool attached_;

  size_t err_count_ = 0;

  std::deque<std::shared_ptr<transfer_t>> transfer_queue_;

private:
// this is called after the out transfer to USB from HOST has been received by libusb
  void callback_out(struct libusb_transfer * transfer);
// this is called when the stat for in is available - from USB in HOST
  void callback_in(struct libusb_transfer * transfer);

  int hotplug_attach_callback(
    libusb_context * ctx, libusb_device * dev,
    libusb_hotplug_event event, void * user_data);
  int hotplug_detach_callback(
    libusb_context * ctx, libusb_device * dev,
    libusb_hotplug_event event, void * user_data);


  std::shared_ptr<transfer_t> make_transfer_in();
  std::shared_ptr<transfer_t> make_transer_out(u_char * buf, size_t size);
  void submit_transfer(
    std::shared_ptr<transfer_t> transfer,
    const std::string msg = "Error submit transfer: ",
    bool wait_for_completed = true);
  void cleanup_transfer_queue();
  void close_devh();

public:
  void init();  // throws exception on failure
  void open_device();  // throws exception on failure
  void init_async();  // throws exception on failure
  Connection(int vendor_id, int product_id, int log_level = LIBUSB_OPTION_LOG_LEVEL);
  ~Connection();
  void set_in_callback(connection_in_cb_fn in_cb_fn)
  {
    in_cb_fn_ = in_cb_fn;
  }
  void set_out_callback(connection_out_cb_fn out_cb_fn)
  {
    out_cb_fn_ = out_cb_fn;
  }
  void set_exception_callback(connection_exception_cb_fn exception_fb_fn)
  {
    exception_cb_fn_ = exception_fb_fn;
  }
  void set_hotplug_attach_callback(hotplug_attach_cb_fn hp_attach_cb_fn)
  {
    hp_attach_cb_fn_ = hp_attach_cb_fn;
  }
  void set_hotplug_detach_callback(hotplug_detach_cb_fn hp_detach_cb_fn)
  {
    hp_detach_cb_fn_ = hp_detach_cb_fn;
  }
  int vendor_id()
  {
    return vendor_id_;
  }
  int product_id()
  {
    return product_id_;
  }
  bool inline devh_valid()
  {
    return dev_ != nullptr;
  }
  int bus_number()
  {
    return devh_valid() ? libusb_get_bus_number(dev_) : 0;
  }
  int device_address()
  {
    return devh_valid() ? libusb_get_device_address(dev_) : 0;
  }
  int device_speed()
  {
    return devh_valid() ? libusb_get_device_speed(dev_) : 0;
  }
  char * device_speed_txt();
  u_int8_t port_number()
  {
    return devh_valid() ? libusb_get_port_number(dev_) : 0;
  }
  int read_chars(u_char * data, size_t size);
  void write_char(u_char c);
  void write_buffer(u_char * buf, size_t size);
  void write_buffer_async(u_char * buf, size_t size, void * user_data);
  u_int8_t num_interfaces()
  {
    return num_interfaces_;
  }
  int ep_data_out_addr()
  {
    return ep_data_out_addr_;
  }
  int ep_data_in_addr()
  {
    return ep_data_in_addr_;
  }
  int ep_comms_in_addr()
  {
    return ep_comms_in_addr_;
  }
  unsigned int timeout_ms()
  {
    return timeout_ms_;
  }
  bool keep_running()
  {
    return keep_running_;
  }
  bool attached()
  {
    return attached_;
  }

// number of existing transfer in in the queue that are not complete
  size_t queued_transfer_in_num();

// needs to be called for libusb to action asynchronos events
  void handle_usb_events();
  void shutdown();
};
}   // end namespace usb

#endif  // UBLOX_DGNSS_NODE__USB_HPP_