/*
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#ifndef _PICOBOOT_CONNECTION_H
#define _PICOBOOT_CONNECTION_H

// todo we should use fully encapsulate libusb

#include <assert.h>
#if HAS_LIBUSB
#include <libusb.h>
#endif
#include "boot/picoboot.h"
#include "model.h"

#define VENDOR_ID_RASPBERRY_PI 0x2e8au
#define PRODUCT_ID_RP2040_USBBOOT 0x0003u
#define PRODUCT_ID_PICOPROBE   0x0004u
#define PRODUCT_ID_MICROPYTHON 0x0005u
#define PRODUCT_ID_STDIO_USB   0x0009u
#define PRODUCT_ID_RP2040_STDIO_USB 0x000au
#define PRODUCT_ID_RP2350_USBBOOT 0x000fu

#ifdef __cplusplus
extern "C" {
#endif

enum picoboot_device_result {
    dr_vidpid_bootrom_ok,
    dr_vidpid_bootrom_no_interface,
    dr_vidpid_bootrom_cant_connect,
    dr_vidpid_micropython,
    dr_vidpid_picoprobe,
    dr_vidpid_unknown,
    dr_error,
    dr_vidpid_stdio_usb,
    dr_vidpid_stdio_usb_cant_connect,
};


#if HAS_LIBUSB
// note that vid and pid are filters, unless both are specified in which case a device with that VID and PID is allowed for RP2350
enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_device_handle **dev_handle, chip_t *chip, int vid, int pid, const char* ser);

int picoboot_reset(libusb_device_handle *usb_device);
int picoboot_cmd_status_verbose(libusb_device_handle *usb_device, struct picoboot_cmd_status *status,
                                bool local_verbose);
int picoboot_cmd_status(libusb_device_handle *usb_device, struct picoboot_cmd_status *status);
int picoboot_exclusive_access(libusb_device_handle *usb_device, uint8_t exclusive);
int picoboot_enter_cmd_xip(libusb_device_handle *usb_device);
int picoboot_exit_xip(libusb_device_handle *usb_device);
int picoboot_reboot(libusb_device_handle *usb_device, uint32_t pc, uint32_t sp, uint32_t delay_ms);
int picoboot_reboot2(libusb_device_handle *usb_device, struct picoboot_reboot2_cmd *reboot_cmd);
int picoboot_get_info(libusb_device_handle *usb_device, struct picoboot_get_info_cmd *cmd, uint8_t *buffer, uint32_t len);
int picoboot_exec(libusb_device_handle *usb_device, uint32_t addr);
int picoboot_flash_erase(libusb_device_handle *usb_device, uint32_t addr, uint32_t len);
int picoboot_vector(libusb_device_handle *usb_device, uint32_t addr);
int picoboot_write(libusb_device_handle *usb_device, uint32_t addr, uint8_t *buffer, uint32_t len);
int picoboot_read(libusb_device_handle *usb_device, uint32_t addr, uint8_t *buffer, uint32_t len);
int picoboot_otp_write(libusb_device_handle *usb_device, struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len);
int picoboot_otp_read(libusb_device_handle *usb_device, struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len);
int picoboot_poke(libusb_device_handle *usb_device, uint32_t addr, uint32_t data);
int picoboot_peek(libusb_device_handle *usb_device, uint32_t addr, uint32_t *data);
int picoboot_flash_id(libusb_device_handle *usb_device, uint64_t *data);
#endif

// we require 256 (as this is the page size supported by the device)
#define LOG2_PAGE_SIZE 8u
#ifdef PAGE_SIZE
#undef PAGE_SIZE
#endif
#define PAGE_SIZE (1u << LOG2_PAGE_SIZE)
#define FLASH_SECTOR_ERASE_SIZE 4096u

static inline bool is_size_aligned(uint32_t addr, int size) {
#ifndef _MSC_VER
    assert(__builtin_popcount(size)==1);
#endif
    return !(addr & (size-1));
}

#ifdef __cplusplus
}
#endif
#endif
