<html>
  <head>
    <meta content="text/html; charset=ISO-8859-1"
      http-equiv="Content-Type">
  </head>
  <body alink="#EE0000" bgcolor="#ffffff" link="#0B6CDA" text="#000000"
    vlink="#551A8B">
    <div class="moz-cite-prefix">On 06/27/2014 04:34 PM, Jef Driesen
      wrote:<br>
    </div>
    <blockquote
      cite="mid:d0240edb32842a785b5647b989acb982@barracuda.mooo.com"
      type="cite">On 2014-06-25 14:22, Venkatesh Shukla wrote:
      <br>
      <blockquote type="cite">I have been working on bringing
        libdivecomputer on android.
        <br>
        I have started with support for ftdi-chipset based
        divecomputers. For
        <br>
        talking with ftdi based devices on android, I have implemented
        <br>
        serial_ftdi.c. . I have tried to implement all the the necessary
        functions.
        <br>
        Though some of them are left out due to their unavailability in
        libftdi.
        <br>
        <br>
        These left out functions are
        <br>
        <br>
           1. serial_set_break  - This is used only in
        reefnet_sensus_pro
        <br>
           hanshaking. I do not know if it uses ftdi chipset. If it
        does, it would
        <br>
           become important to implement this function. In the present
        implementation,
        <br>
           it remains unusable.
        <br>
      </blockquote>
      <br>
      Reefnet uses the Prolific pl2303 chipset, so it's not really an
      issue. The Sensus Pro is a pretty outdated model too, so there
      will be very few people who still have one.
      <br>
      <br>
      <blockquote type="cite">   2. serial_send_break - I could not find
        any usage of this function in
        <br>
           libdivecomputer.
        <br>
      </blockquote>
      <br>
      Correct. It's not used anywhere.
      <br>
      <br>
      <blockquote type="cite">   3. serial_get_transmitted - Even though
        the received bytes in the buffer
        <br>
           can be accessed, I couldn't find any way to get the
        transmitted bytes. This
        <br>
           function is used internally for logging. Returns -1 as it is
        not
        <br>
           implemented.
        <br>
      </blockquote>
      <br>
      It doesn't return the number of transmitted bytes, but the number
      of bytes that are currently present in the serial port buffer.
      Nowadays, we always call tcdrain when writing data, so this value
      should always be zero. This used to be different. Anyway, it's not
      an important function and it might even get removed in the future.
      So an implementation that always return 0 would be fine.
      <br>
      <br>
      <blockquote type="cite">Changes made for building libdivecomputer
        for android are
        <br>
        <br>
           1. Check for presence of libftdi1
        <br>
           2. serial_ftdi.c is compiled and used instead of
        serial_posix.c
        <br>
           3. libftdi1 is included in Libs.private in libdivecomputer.pc
        <br>
        <br>
        All these changes can be viewed here
        <br>
        <a class="moz-txt-link-rfc2396E" href="https://github.com/venkateshshukla/libdivecomputer"><https://github.com/venkateshshukla/libdivecomputer></a>. I
        have been testing
        <br>
        it with universal script and subsurface-android. Results have
        been
        <br>
        encouraging.
        <br>
      </blockquote>
      <br>
      I have tested your code on Linux, with an OSTC3 (or was it an
      OSTC2, don't remember), and it worked fine. I didn't had time to
      review it properly.
      <small><small><small><small><small><small><small><small><small><small><small>asa</small></small></small></small></small></small></small></small></small></small></small><br>
    </blockquote>
    <br>
    Thank you. I hope to clean out the bugs as soon as possible and
    integrate the android code to libdivecomputer<br>
    <br>
    <blockquote
      cite="mid:d0240edb32842a785b5647b989acb982@barracuda.mooo.com"
      type="cite">
      <br>
      <blockquote type="cite">Now, toward providing a workable API for
        android, I have following points
        <br>
        in mind. These were mentioned by Jef in an earlier mail.
        <br>
        <br>
           1. Support for only ftdi devices are implemented for now. But
        support
        <br>
           for other chipsets need to be included later on. Hence,
        serial_ftdi.c alone
        <br>
           is not enough. We need a serial_android.c which will redirect
        calls to
        <br>
           respective chipset specific scripts.
        <br>
           2. I have come to the conclusion that for android, it will be
        best if we
        <br>
           pass the file descriptor of the USB device after acquiring
        the necessary
        <br>
           permissions. This fd can be used to create a libusb_device
        and subsequently
        <br>
           all USB related features can be accessed. For making a
        libusb_device from
        <br>
           file descriptor obtained from android, I had tweaked libusb.
        This tweaked
        <br>
           version is present here (
        <a class="moz-txt-link-freetext" href="https://github.com/venkateshshukla/libusb">https://github.com/venkateshshukla/libusb</a> ).
        <br>
           This libusb_device could be used to obtain VID and PID and
        then control can
        <br>
           then be branched accordingly to ftdi, pl2303, cp210x and
        other chipset
        <br>
           specific scripts on android.
        <br>
           3. We will require a generalized mechanism for passing
        parameters. This
        <br>
           is especially needed now as we have several branches - Normal
        TTY usage (
        <br>
           posix/win32 , path to the device), android (file descriptor)
        and Bluetooth
        <br>
           address.
        <br>
        <br>
        Please provide your thoughts on the above points. Also any other
        thing that
        <br>
        I should keep in mind for android API?
        <br>
      </blockquote>
      <br>
      Originally, the solution I had in mind for the different transport
      types (serial, irda, usb and bluetooth), was to introduce a
      specific data structure for each transport type:
      <br>
      <br>
      typedef dc_params_serial_t {
      <br>
          const char *devname;
      <br>
      } dc_params_serial_t;
      <br>
      <br>
      typedef dc_params_usb_t {
      <br>
          unsigned int vid;
      <br>
          unsigned int pid;
      <br>
      } dc_params_usb_t;
      <br>
      <br>
      typedef dc_params_bluetooth_t {
      <br>
          unsigned char address[6];
      <br>
      } dc_params_bluetooth_t;
      <br>
      <br>
      dc_status_t
      <br>
      dc_device_open (..., dc_descriptor_t *descriptor, const void
      *params);
      <br>
      <br>
      The application would then have to pass the appropriate data
      structure to the dc_device_open function, depending on the
      transport type indicated by the device descriptor:
      <br>
      <br>
      dc_params_serial_t serial;
      <br>
      dc_params_usb_t usb;
      <br>
      dc_params_bluetooth_t bluetooth;
      <br>
      <br>
      switch (dc_descriptor_get_transport(descriptor)) {
      <br>
      case DC_TRANSPORT_SERIAL:
      <br>
          serial.devname = ...;
      <br>
          dc_device_open(..., descriptor, &serial);
      <br>
          break;
      <br>
      case DC_TRANSPORT_USB:
      <br>
          usb.vid = ...;
      <br>
          usb.pid = ...;
      <br>
          dc_device_open(..., descriptor, &usb);
      <br>
          break;
      <br>
      case DC_TRANSPORT_BLUETOOTH:
      <br>
          bluetooth.address = ...;
      <br>
          dc_device_open(..., descriptor, &bluetooth);
      <br>
          break;
      <br>
      }
      <br>
      <br>
    </blockquote>
    I was thinking something like<br>
    <br>
    typedef struct dc_addr_t {<br>
        dc_transport_t transport;<br>
        dc_param_t param;<br>
    } dc_addr_t;<br>
    <br>
    dc_status_t<br>
    dc_device_open (dc_device_t **out, dc_context_t *context,
    dc_descriptor_t *descriptor, const dc_addr_t *addr);<br>
    <br>
    dc_addr_t *dev_addr = (dc_addr_t *) malloc (sizeof (dc_addr_t));<br>
    <br>
    switch (dc_descriptor_get_transport(descriptor)) {<br>
    case DC_TRANSPORT_SERIAL:<br>
        dev_addr->transport = DC_TRANSPORT_SERIAL;<br>
        dev_addr->param.devname = "/path/to/tty";<br>
        break;<br>
    case DC_TRANSPORT_USB:<br>
        dev_addr->transport = DC_TRANSPORT_USB;<br>
        dev_addr->param.fd = XX; // File descriptor obtained from
    android<br>
        break;<br>
    case DC_TRANSPORT_BLUETOOTH:<br>
        dev_addr->transport = DC_TRANSPORT_USB<br>
        dev_addr->param.mac_addr = ...;<br>
        break;<br>
    }<br>
    dc_device_open(..., descriptor, dev_addr);<br>
    <br>
    The case of USB above is for Android usage as explained below.<br>
    <blockquote
      cite="mid:d0240edb32842a785b5647b989acb982@barracuda.mooo.com"
      type="cite">Although this will certainly work, there are some
      drawbacks:
      <br>
      <br>
      Ideally libdivecomputer should provide some api to enumerate
      serial, usb and bluetooth devices. When the user chooses a device
      that uses serial communication, you want to be able to show a list
      with all serial ports. The same goes for usb and bluetooth
      communication. But for some transport types like irda sockets (and
      maybe also bluetooth sockets) you need to open a socket before you
      can enumerate the available devices. But as long as the backend
      code opens the socket internally, this functionality can't be
      exposed to the application. Currently the USB (Atomics Cobalt) and
      IrDA (Uwatec Smart) based backends do work around this problem by
      performing the discovery internally, and pick the device that
      looks like the dive computer (e.g based on its name, USB VID/PID,
      etc). But this heuristics isn't perfect and can certainly fail.
      For example when connecting two identical dive computers, or when
      some other usb-serial gadget with the same USB VID/PID (not
      unlikely with simple usb-serial cables using the stock ftdi/pl2303
      VID/PID) is connected. The heuristics will have to decide which
      device to pick (e.g. the first one), but that might be the wrong
      choice. Yes, I'm aware this is a corner case, but not impossible.
      <br>
      <br>
      It's also not clear to me how this solution should deal with
      multiple implementations of a single transport. For Android,
      there'll be multiple implementations for serial communication, one
      for each chipset (ftdi, pl2303, cdc-acm, etc). But how do you
      decide which chipset to pick? The device backend doesn't always
      have this information. For example for Suunto devices,there are
      several cables on the market using different usb-serial chipsets.
      So there is no one to one mapping between devices and usb-serial
      chipsets.
      <br>
      <br>
    </blockquote>
    For usage of usb divecomputers on Android, the question of
    enumeration wouldn't necessarily arise. It is provided by android
    and would be used in the pre-libdivecomputer-usage stage (see step 0
    below) in order to get the file descriptor which would then be
    passed on to libdivecomputer. Also connecting multiple USB devices
    on Android would not be a common phenomenon.<br>
    <br>
    If still enumeration is needed, one could use libusb to get a list
    of all usb devices ( libusb_get_device_list ). This is a compulsory
    initial steps in any libusb related operations and is shown below
    (step 4).<br>
    <br>
    The steps for usage of libdivecomputer on android as I have thought
    of are:<br>
    <blockquote>0. Android application lists the devices. User chooses
      his divecomputer. The application asks for permission to use it.
      On getting permission, the application acquires the file
      descriptor of the Usb Device attached. It is then passed to
      libdivecomputer for opening the device.<br>
      1. The application makes an appropriate addr and adds data to it.
      Then calls dc_device_open with this addr.<br>
      dc_addr_t *addr = (dc_addr_t *) malloc(sizeof(dc_addr_t));<br>
      addr->transport = DC_TRANSPORT_USB;<br>
      addr->param.fd = usb_fd;<br>
      dc_device_open(&device, context, descriptor, addr);<br>
      2. The dc_device_open looks for the device specific opening code
      (ex. hw_ostc3_open) and finally reaches serial_open of android.<br>
      3. serial_open checks if bluetooth is to be used or usb.<br>
      int<br>
      serial_open (serial_t **out, dc_context_t *context, const
      dc_addr_t *addr)<br>
      {<br>
          if (addr->transport == DC_TRANSPORT_USB)<br>
              serial_open_usb (out, context, addr->param.fd;<br>
          else if (addr->transport == DC_TRANSPORT_BLUETOOTH)<br>
              serial_open_bt (out, context, addr->param.mac_addr;<br>
          else<br>
              return -1;<br>
      }<br>
      <br>
      4. If transport type is usb, make a libusb_device with the file
      descriptor.<br>
      int<br>
      serial_open_usb (serial_t **out, dc_context_t *context, int fd)<br>
      {<br>
          int i, n, r;<br>
          libusb_init(NULL);<br>
          libusb_device *dev, **devs;<br>
          struct libusb_device_descriptor desc;<br>
          n = libusb_get_device_list (NULL, &devs);<br>
          for (i = 0; i < n; i++) {<br>
              dev = devs[i];<br>
              libusb_get_device_descriptor(dev, &desc);<br>
              if (is_dev_recognized( desc.idVendor, desc.idProduct) {<br>
                  switch (get_dc_chipset(desc_idVendor, desc.idProduct)
      {<br>
                  case FTDI:<br>
                      return serial_open_ftdi(out, context, dev);<br>
                  case PL2303:<br>
                      return serial_open_pl2303 (out, context, dev);<br>
                  case CP210X:<br>
                      return serial_open_cp210x (out, context, dev);<br>
                  default:<br>
                      return -1; // Chipset not yet supported<br>
                  }<br>
              }<br>
          }<br>
          return -2; // No recognized usb device found<br>
      }<br>
      <br>
      Checks have been ignored for brevity.<br>
    </blockquote>
    The multiple-interfaces question is resolved because we can extract
    the vid pid of the chip directly. And this is used for choosing the
    chipset specific function. There is no manufacturer information
    needed.<br>
    I might be wrong or missing something. Does using different cables
    with different usb-serial chipset change the vid pid of the usb
    device?<br>
    <blockquote
      cite="mid:d0240edb32842a785b5647b989acb982@barracuda.mooo.com"
      type="cite">The next thing that comes to my mind is a solution
      where the application opens the appropriate "transport "itself,
      and passes an instance of an abstract transport object to the
      dc_device_open function:
      <br>
      <br>
      dc_serial_t *serial = NULL;
      <br>
      dc_usb_t *usb = NULL;
      <br>
      dc_bluetooth_socket_t *bluetooth = NULL;
      <br>
      <br>
      switch (dc_descriptor_get_transport(descriptor)) {
      <br>
      case DC_TRANSPORT_SERIAL:
      <br>
      #ifdef ANDROID
      <br>
          dc_serial_{ftdi,pl2303,...}_open(&serial, fd);
      <br>
      #else
      <br>
          dc_serial_open(&serial, name);
      <br>
      #endif
      <br>
          dc_device_open(..., descriptor, serial);
      <br>
          break;
      <br>
      case DC_TRANSPORT_USB:
      <br>
          dc_usb_open(&usb, vid, pid);
      <br>
          dc_device_open(..., descriptor, usb);
      <br>
          break;
      <br>
      case DC_TRANSPORT_BLUETOOTH:
      <br>
          dc_bluetooth_socket_open(&bluetooth, ...);
      <br>
          dc_bluetooth_socket_discovery(bluetooth, ...);
      <br>
          dc_bluetooth_socket_connect(bluetooth, address);
      <br>
          dc_device_open(..., descriptor, bluetooth);
      <br>
          break;
      <br>
      }
      <br>
      <br>
      This would allow multiple implementations of a transport, as long
      as each of them implements the same interface, but that's easy. It
      also solves the discovery problem, because the low-level transport
      interface is now available to the application.
      <br>
      <br>
      But unfortunately it also introduces a new problem. Take a look at
      the USB transport. Now the application suddenly needs to know
      which USB VID/PID to use. Previously the device backend took care
      of that internally (with the caveat mentioned earlier). For
      example the Atomic Cobalt uses the VID/PID 0x0471/0x0888. Right
      now the application doesn't need to know this, because the backend
      opens the usb device that matches these number. The same thing
      happens for Uwatec Smart, where the backend discovers the IrDA
      devices in range, and connects to first one that matches the known
      device names. How do we deal with this here?
      <br>
      <br>
      All ideas are welcome!
      <br>
      <br>
      Jef
      <br>
    </blockquote>
  </body>
</html>