/* 
 * The MIT License (MIT)
 *
 * Copyright (c) 2019 Ha Thach (tinyusb.org)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

/* This example demonstrates WebUSB as web serial with browser with WebUSB support (e.g Chrome).
 * After enumerated successfully, browser will pop-up notification
 * with URL to landing page, click on it to test
 *  - Click "Connect" and select device, When connected the on-board LED will litted up.
 *  - Any charters received from either webusb/Serial will be echo back to webusb and Serial
 *
 * Note:
 * - The WebUSB landing page notification is currently disabled in Chrome
 * on Windows due to Chromium issue 656702 (https://crbug.com/656702). You have to
 * go to landing page (below) to test
 *
 * - On Windows 7 and prior: You need to use Zadig tool to manually bind the
 * WebUSB interface with the WinUSB driver for Chrome to access. From windows 8 and 10, this
 * is done automatically by firmware.
 *
 * - On Linux/macOS, udev permission may need to be updated by
 *   - copying '/examples/device/99-tinyusb.rules' file to /etc/udev/rules.d/ then
 *   - run 'sudo udevadm control --reload-rules && sudo udevadm trigger'
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#include "bsp/board.h"
#include "tusb.h"
#include "usb_descriptors.h"
#include "pico/stdlib.h"

uint8_t usbFunctionSetup(uint8_t *data);
uint8_t usbFunctionRead(uint8_t *data, uint8_t len);
uint8_t usbFunctionWrite(uint8_t *data, uint8_t len);

static bool i2c_tiny_usb_connected = false;

//------------- prototypes -------------//
void i2c_tiny_usb(void);

/*------------- MAIN -------------*/
int main(void)
{
#if USE_LED_BLINKING_TASK
  board_init();
#endif
  stdio_init_all();
  tud_init(BOARD_DEVICE_RHPORT_NUM);

  while (1)
  {
    tud_task(); // tinyusb device task
    i2c_tiny_usb();
  }
  return 0;
}

//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+

// Invoked when device is mounted
void tud_mount_cb(void)
{
  TU_LOG1("tud_mount_cb\r\n");
  i2c_tiny_usb_connected = true;
}

// Invoked when device is unmounted
void tud_umount_cb(void)
{
  TU_LOG1("tud_umount_cb\r\n");
  i2c_tiny_usb_connected = false;
}

// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us  to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
  (void) remote_wakeup_en;
  TU_LOG1("tud_suspend_cb\r\n");
}

// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
  TU_LOG1("tud_resume_cb\r\n");
}

//--------------------------------------------------------------------+
// i2c tiny usb use vendor class
//--------------------------------------------------------------------+
extern uint8_t *usbMsgPtr;

// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
  bool ret = false;
  // nothing to with DATA & ACK stage
  TU_LOG1("vendor_cb: st=%u bt=%x br=%x v=%x i=%x l=%x\r\n", stage,
          request->bmRequestType,
          request->bRequest,
          request->wValue,
          request->wIndex,
          request->wLength);
  if (stage == CONTROL_STAGE_SETUP) {
    switch (request->bmRequestType_bit.type)
    {
      case TUSB_REQ_TYPE_VENDOR:
        TU_LOG1(" TyVendor:t=%x r=%x\r\n", request->bmRequestType_bit.type, request->bRequest);
        usbFunctionSetup((uint8_t *)request);
        ret = tud_control_status(rhport, request);
        break;
      case TUSB_REQ_TYPE_CLASS:
        TU_LOG1(" TyClass:t=%x r=%x\r\n", request->bmRequestType_bit.type, request->bRequest);
        usbFunctionSetup((uint8_t *)request);
        ret  = tud_control_xfer(rhport, request, (void*)usbMsgPtr, 4);
        break;
      default:
        TU_LOG1(" TyOther:t=%x r=%x\r\n", request->bmRequestType_bit.type, request->bRequest);
        break;
    }
  } else {
    ret = true;
  }
  if (!ret) {
    TU_LOG1("vendor_cb: ret=%s\r\n", (ret == true)? "OK":"NG");
  }
  return ret;
}

void i2c_tiny_usb(void)
{
  if ( i2c_tiny_usb_connected )
  {
    if ( tud_vendor_available() )
    {
      uint8_t buf[64];
      uint32_t count = tud_vendor_read(buf, sizeof(buf));

      uint32_t len = (uint32_t)usbFunctionWrite(buf, count);

      len = (uint32_t)usbFunctionRead(buf, count);
    }
  }
}
