/*
 * avrdude - A Downloader/Uploader for AVR device programmers
 * Copyright (C) 2005, 2006 Joerg Wunsch
 * Copyright (C) 2006 David Moore
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

// USB interface via libusb for avrdude

#include <ac_cfg.h>

#if defined(HAVE_LIBUSB)
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>

#if defined(HAVE_USB_H)
#include <usb.h>
#elif defined(HAVE_LUSB0_USB_H)
#include <lusb0_usb.h>
#else
#error "libusb needs either <usb.h> or <lusb0_usb.h>"
#endif

#include "avrdude.h"
#include "libavrdude.h"

#include "usbdevs.h"

#if defined(WIN32)

// Someone has defined interface to struct in Cygwin
#undef interface
#endif

/*
 * The baud parameter is meaningless for USB devices, so we reuse it to pass
 * the desired USB device ID.
 */
static int usbdev_open(const char *port, union pinfo pinfo, union filedescriptor *fd) {
  char string[256], product[256];
  struct usb_bus *bus;
  struct usb_device *dev;
  usb_dev_handle *udev;
  char serno[64] = { 0 };
  int i, iface, rc;

  if(fd->usb.max_xfer == 0)
    fd->usb.max_xfer = USBDEV_MAX_XFER_MKII;

  // Override/set pid, vid and/or serno from -P usb[:<vid>:<pid>][:<serno>]
  if(str_set_vid_pid_serno(port, &pinfo.usbinfo.vid, &pinfo.usbinfo.pid, serno, sizeof serno) < 0) {
    pmsg_error("invalid -P %s; drop -P option or use -P usb[:<vid>:<pid>][:<serno>]\n", port);
    return LIBAVRDUDE_EXIT_FAIL;
  }

  usb_init();

  usb_find_busses();
  usb_find_devices();

  for(bus = usb_get_busses(); bus; bus = bus->next) {
    for(dev = bus->devices; dev; dev = dev->next) {
      if(dev->descriptor.idVendor == pinfo.usbinfo.vid && dev->descriptor.idProduct == pinfo.usbinfo.pid) {
        udev = usb_open(dev);
        if(udev) {
          // Yeah, we found something
          if(usb_get_string_simple(udev, dev->descriptor.iSerialNumber, string, sizeof(string)) < 0) {
            pmsg_warning("reading serial number, %s\n", usb_strerror());
            /*
             * On some systems, libusb appears to have problems sending control
             * messages.  Catch the benign case where the user did not request
             * a particular serial number, so we could continue anyway.
             */
            cx->usb_access_error = 1;
            if(*serno)
              goto none_matching;       // Unable to match serno
            strcpy(string, "[unknown]");
          }
          if(serdev)
            serdev->usbsn = cache_string(string);
          if(usb_get_string_simple(udev, dev->descriptor.iProduct, product, sizeof(product)) < 0) {
            pmsg_warning("reading product name, %s\n", usb_strerror());
            strcpy(product, "[unnamed product]");
          }
          if(serdev)
            serdev->usbproduct = cache_string(product);

          // Need to write to endpoint 2 to switch the PICkit4/SNAP from PIC to AVR mode
          if(str_casestarts(product, "MPLAB") && (str_caseends(product, "Snap ICD")
              || str_caseends(product, "PICkit 4"))) {
            pinfo.usbinfo.flags = 0;
            fd->usb.wep = 2;
          }

          /*
           * If the product name contains the string "CMSIS-DAP" it claims to
           * be a CMSIS-DAP spec HID device. HID interrupt packets can only be
           * 64 byte (Full-Speed) or 512 byte (High-Speed) not any other value.
           * Standard-Speed (8 bytes) will not work.
           */
          if(str_contains(product, "CMSIS-DAP")) {
            pinfo.usbinfo.flags |= PINFO_FL_USEHID;
            // JTAGICE3 doesn't use a separate endpoint for event reception
            fd->usb.eep = 0;
            fd->usb.max_xfer = 64;
          }

          if(str_contains(product, "mEDBG")) {
            // The AVR Xplained Mini uses different endpoints
            fd->usb.rep = 0x81;
            fd->usb.wep = 0x02;
          }

          pmsg_notice2("found %s with serno = %s\n", product, string);
          if(*serno) {
            // See if the serial number requested by the user matches right-to-left
            int x = strlen(string) - strlen(serno);

            if(x < 0 || !str_caseeq(string + x, serno)) {
              pmsg_debug("%s(): serial number does not match\n", __func__);
              usb_close(udev);
              continue;
            }
          }

          if(dev->config == NULL) {
            pmsg_warning("USB device has no configuration\n");
            goto trynext;
          }

          int usehid = !!(pinfo.usbinfo.flags & PINFO_FL_USEHID);
          for(iface = 0; iface < dev->config[0].bNumInterfaces; iface++) {
            cx->usb_interface = dev->config[0].interface[iface].altsetting[0].bInterfaceNumber;

#ifdef LIBUSB_HAS_GET_DRIVER_NP
            /*
             * Many Linux systems attach the usbhid driver by default to any
             * HID-class device.  On those, the driver needs to be detached
             * before we can claim the interface.
             */
            if((rc = usb_detach_kernel_driver_np(udev, cx->usb_interface)))
              pmsg_notice("usb_detach_kernel_driver_np() unexpectedly returns %d: %s\n", rc, usb_strerror());
#endif

            if(usb_claim_interface(udev, cx->usb_interface)) {
              pmsg_warning("(i/face %d) %s\n", cx->usb_interface, usb_strerror());
              cx->usb_access_error = 1;
            } else {
              if(usehid) {      // Only consider an interface that is of class HID
                if(dev->config[0].interface[iface].altsetting[0].bInterfaceClass != USB_CLASS_HID)
                  continue;
                fd->usb.use_interrupt_xfer = 1;
              }
              break;
            }
          }
          if(iface == dev->config[0].bNumInterfaces) {
            pmsg_warning("no usable interface found\n");
            goto trynext;
          }

          fd->usb.handle = udev;
          if(fd->usb.rep == 0) {
            // Try finding out what our read endpoint is
            for(i = 0; i < dev->config[0].interface[iface].altsetting[0].bNumEndpoints; i++) {
              int possible_ep = dev->config[0].interface[iface].altsetting[0].endpoint[i].bEndpointAddress;

              if((possible_ep & USB_ENDPOINT_DIR_MASK) != 0) {
                pmsg_notice2("%s(): using read endpoint 0x%02x\n", __func__, possible_ep);
                fd->usb.rep = possible_ep;
                break;
              }
            }
            if(fd->usb.rep == 0) {
              pmsg_warning("cannot find a read endpoint, using 0x%02x\n", USBDEV_BULK_EP_READ_MKII);
              fd->usb.rep = USBDEV_BULK_EP_READ_MKII;
            }
          }
          for(i = 0; i < dev->config[0].interface[iface].altsetting[0].bNumEndpoints; i++) {
            int mps = dev->config[0].interface[iface].altsetting[0].endpoint[i].wMaxPacketSize,
                epa = dev->config[0].interface[iface].altsetting[0].endpoint[i].bEndpointAddress;

            if(epa != fd->usb.rep && epa != fd->usb.wep)
              continue;

            // (usehid && (mps == 64 || mps == 512)) suggested by Claude free chatbot (Sonnet 4.6 Low)
            if((usehid && (mps == 64 || mps == 512)) || mps < fd->usb.max_xfer) {
              if(mps != fd->usb.max_xfer) {
                pmsg_notice("max packet size adjusted %d -> %d owing to EP 0x%02x's wMaxPacketSize\n",
                  fd->usb.max_xfer, mps, epa);
                fd->usb.max_xfer = mps;
              }
            }
          }
          if(fd->usb.max_xfer > USBDEV_MAX_XFER_3) {
            pmsg_error("reducing too big max packet size %d to %d\n", fd->usb.max_xfer, USBDEV_MAX_XFER_3);
            fd->usb.max_xfer = USBDEV_MAX_XFER_3;
          }
          if(usehid && usb_control_msg(udev, 0x21, 0x0a /* SET_IDLE */ , 0, 0, NULL, 0, 100) < 0)
            pmsg_warning("SET_IDLE failed\n");
          return 0;

        trynext:
          usb_close(udev);
        } else
          pmsg_warning("cannot open device: %s\n", usb_strerror());
      }
    }
  }

none_matching:
  if((pinfo.usbinfo.flags & PINFO_FL_SILENT) == 0)
    pmsg_error("%s%s USB device %s (%04x:%04x)\n",
      cx->usb_access_error? "found but could not access": "did not find any",
      *serno && !cx->usb_access_error? " (matching)": "", port, pinfo.usbinfo.vid, pinfo.usbinfo.pid);

  return -1;
}

static void usbdev_close(union filedescriptor *fd) {
  usb_dev_handle *udev = (usb_dev_handle *) fd->usb.handle;

  if(udev == NULL)
    return;

  (void) usb_release_interface(udev, cx->usb_interface);

#if defined(__linux__)
  /*
   * Without this reset, the AVRISP mkII seems to stall the second time we try
   * to connect to it.  This is not necessary on FreeBSD.
   */
  usb_reset(udev);
#endif

  usb_close(udev);
}

static int usbdev_send(const union filedescriptor *fd, const unsigned char *bp, size_t mlen) {
  usb_dev_handle *udev = (usb_dev_handle *) fd->usb.handle;
  int rv;
  int i = mlen;
  const unsigned char *p = bp;
  int tx_size;

  if(udev == NULL)
    return -1;

  /*
   * Split the frame into multiple packets.  It's important to make sure we
   * finish with a short packet, or else the device won't know the frame is
   * finished.  For example, if we need to send 64 bytes, we must send a packet
   * of length 64 followed by a packet of length 0.
   */
  do {
    tx_size = ((int) mlen < fd->usb.max_xfer)? (int) mlen: fd->usb.max_xfer;
    rv = (fd->usb.use_interrupt_xfer?usb_interrupt_write: usb_bulk_write)(udev, fd->usb.wep, (char *) bp, tx_size, 10000);
    if(rv != tx_size) {
      pmsg_error(str_ccprintf("%s() %s %%d bytes, err = %%s\n",
        fd->usb.use_interrupt_xfer? "usb_interrupt_write": "usb_bulk_write",
        rv > 0? "only wrote %d out of": "returns %d and fails writing"),
        rv, tx_size, usb_strerror());
      return -1;
    }
    bp += tx_size;
    mlen -= tx_size;
  } while(mlen > 0);

  if(verblevel >= MSG_TRACE)
    trace_buffer(__func__, p, i);
  return 0;
}

/*
 * As calls to usb_bulk_read() result in exactly one USB request, we have to
 * buffer the read results ourselves, so the single-char read requests
 * performed by the upper layers will be handled.  In order to do this, we
 * maintain a private buffer of what we've got so far, and transparently issue
 * another USB read request if the buffer is empty and more data are requested.
 */
static int usb_fill_buf(usb_dev_handle *udev, int maxsize, int ep, int use_interrupt_xfer) {
  int rv;

  if(use_interrupt_xfer)
    rv = usb_interrupt_read(udev, ep, cx->usb_buf, maxsize, 10000);
  else
    rv = usb_bulk_read(udev, ep, cx->usb_buf, maxsize, 10000);
  if(rv < 0) {
    pmsg_notice2("%s(): usb_%s_read() error: %s\n", __func__, use_interrupt_xfer? "interrupt": "bulk", usb_strerror());
    return -1;
  }

  cx->usb_buflen = rv;
  cx->usb_bufptr = 0;

  return 0;
}

static int usbdev_recv(const union filedescriptor *fd, unsigned char *buf, size_t nbytes) {
  usb_dev_handle *udev = (usb_dev_handle *) fd->usb.handle;
  int i, amnt;
  unsigned char *p = buf;

  if(udev == NULL)
    return -1;

  for(i = 0; nbytes > 0;) {
    if(cx->usb_buflen <= cx->usb_bufptr) {
      if(usb_fill_buf(udev, fd->usb.max_xfer, fd->usb.rep, fd->usb.use_interrupt_xfer) < 0)
        return -1;
    }
    amnt = cx->usb_buflen - cx->usb_bufptr > (int) nbytes? (int) nbytes: cx->usb_buflen - cx->usb_bufptr;
    memcpy(buf + i, cx->usb_buf + cx->usb_bufptr, amnt);
    cx->usb_bufptr += amnt;
    nbytes -= amnt;
    i += amnt;
  }

  if(verblevel >= MSG_TRACE2)
    trace_buffer(__func__, p, i);

  return 0;
}

/*
 * This version of recv keeps reading packets until we receive a short packet.
 * Then, the entire frame is assembled and returned to the user.  The length
 * will be unknown in advance, so we return the length as the return value of
 * this function, or -1 in case of an error.
 *
 * This is used for the AVRISP mkII device.
 */
static int usbdev_recv_frame(const union filedescriptor *fd, unsigned char *buf, size_t nbytes) {
  usb_dev_handle *udev = (usb_dev_handle *) fd->usb.handle;
  int rv, n;
  unsigned char *p = buf;

  if(udev == NULL)
    return -1;

  // If there's an event EP, and it has data pending, return it first
  if(fd->usb.eep != 0) {
    rv = usb_bulk_read(udev, fd->usb.eep, cx->usb_buf, fd->usb.max_xfer, 1);
    if(rv > 4) {
      memcpy(buf, cx->usb_buf, rv);
      n = rv;
      n |= USB_RECV_FLAG_EVENT;
      goto printout;
    } else if(rv > 0) {
      pmsg_warning("short event len = %d, ignored\n", rv);
      // Fallthrough
    }
  }

  n = 0;
  do {
    if(fd->usb.use_interrupt_xfer)
      rv = usb_interrupt_read(udev, fd->usb.rep, cx->usb_buf, fd->usb.max_xfer, 10000);
    else
      rv = usb_bulk_read(udev, fd->usb.rep, cx->usb_buf, fd->usb.max_xfer, 10000);
    if(rv < 0) {
      pmsg_notice2("%s(): usb_%s_read(): %s\n", __func__,
        fd->usb.use_interrupt_xfer? "interrupt": "bulk", usb_strerror());
      return -1;
    }

    if(rv <= (int) nbytes) {
      memcpy(buf, cx->usb_buf, rv);
      buf += rv;
    } else
      return -1;                // Buffer overflow

    n += rv;
    nbytes -= rv;
  }
  while(nbytes > 0 && rv == fd->usb.max_xfer);

/*
 * This ends when the buffer is completly filled (nbytes=0) or was too small
 * (nbytes< 0) or a short packet is found. However we cannot say for nbytes=0
 * that there was really a packet completed, we had to check the last rv value
 * than for a short packet, but what happens if the packet does not end with a
 * short packet? and what if the buffer is filled without the packet was
 * completed?
 *
 * Preconditions:
 *  - expected packet is not a multiple of usb.max_xfer (prevents further waiting)
 *  - expected packet is shorter than the provided buffer (so it cannot fill it
 *    completely) or buffer size is not a multiple of usb.max_xfer (so it can
 *    clearly be detected if the buffer was overflown.)
 *
 */
printout:
  if(verblevel >= MSG_TRACE)
    trace_buffer(__func__, p, n & USB_RECV_LENGTH_MASK);

  return n;
}

static int usbdev_drain(const union filedescriptor *fd, int display) {
  /*
   * There is not much point in trying to flush any data on an USB endpoint, as
   * the endpoint is supposed to start afresh after being configured from the
   * host.
   *
   * As trying to flush the data here caused strange effects in some situations
   * (see https://savannah.nongnu.org/bugs/index.php?43268) better avoid it.
   */

  return 0;
}

// Device descriptor for the JTAG ICE mkII
struct serial_device usb_serdev = {
  .open = usbdev_open,
  .close = usbdev_close,
  .rawclose = usbdev_close,
  .send = usbdev_send,
  .recv = usbdev_recv,
  .drain = usbdev_drain,
  .flags = SERDEV_FL_NONE,
};

// Device descriptor for the AVRISP mkII
struct serial_device usb_serdev_frame = {
  .open = usbdev_open,
  .close = usbdev_close,
  .rawclose = usbdev_close,
  .send = usbdev_send,
  .recv = usbdev_recv_frame,
  .drain = usbdev_drain,
  .flags = SERDEV_FL_NONE,
};
#endif                          // HAVE_LIBUSB
