diff --git a/src/fx2_ll.cpp b/src/fx2_ll.cpp new file mode 100644 index 0000000..dfd2408 --- /dev/null +++ b/src/fx2_ll.cpp @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + * Copyright (c) 2021 Gwenhael Goavec-Merou +*/ + +#include +#include +#include + +#include +#include +#include + +#include "display.hpp" +#include "fx2_ll.hpp" +#include "ihexParser.hpp" + +using namespace std; + +#define FX2_FIRM_LOAD 0xA0 +#define FX2_GCR_CPUCS 0xe600 +#define FX2_GCR_CPUCS_8051_RES (1 << 0) + +FX2_ll::FX2_ll(uint16_t uninit_vid, uint16_t uninit_pid, + uint16_t vid, uint16_t pid, const string &firmware_path) +{ + int ret; + bool reenum = false; + + if (libusb_init(&usb_ctx) < 0) { + throw std::runtime_error("libusb init failed"); + } + + /* try to open uninitialized device */ + if (uninit_vid != -1 && uninit_pid != -1) { + dev_handle = libusb_open_device_with_vid_pid(usb_ctx, + uninit_vid, uninit_pid); + if (dev_handle) { + ret = libusb_claim_interface(dev_handle, 0); + if (ret) { + libusb_close(dev_handle); + libusb_exit(usb_ctx); + throw std::runtime_error("claim interface failed"); + } + /* load firmware */ + load_firmware(firmware_path); + close(); + reenum = true; + } + } + + /* try to open an already init device + * since fx2 may be not immediatly ready + * retry with a delay + */ + int timeout = 100; + do { + dev_handle = libusb_open_device_with_vid_pid(usb_ctx, + vid, pid); + timeout--; + if (!dev_handle) + sleep(1); + } while (!dev_handle && timeout > 0 && reenum); + + if (!dev_handle) + throw std::runtime_error("FX2: fail to open device"); + + ret = libusb_claim_interface(dev_handle, 0); + if (ret) { + libusb_close(dev_handle); + libusb_exit(usb_ctx); + throw std::runtime_error("claim interface failed"); + } +} + +/* destructor: close current device and + * destroy context + */ +FX2_ll::~FX2_ll() +{ + close(); + libusb_exit(usb_ctx); +} + +/* close device after releasing interface + */ +bool FX2_ll::close() +{ + if (dev_handle) { + int ret; + ret = libusb_release_interface(dev_handle, 0); + if (ret != 0) { + /* device is already disconnected ... */ + if (ret == LIBUSB_ERROR_NO_DEVICE) { + return true; + } else { + printError("Error: Fail to release interface"); + return false; + } + } + libusb_close(dev_handle); + dev_handle = NULL; + } + return true; +} + +/* write len byte in bulk using endpoint + */ +int FX2_ll::write(uint8_t endpoint, uint8_t *buff, uint16_t len) +{ + int ret, actual_length; + ret = libusb_bulk_transfer(dev_handle, LIBUSB_ENDPOINT_OUT | endpoint, + buff, len, &actual_length, 1000); + if (ret != LIBUSB_SUCCESS) { + printError("FX2 write error: " + std::string(libusb_error_name(ret))); + return -1; + } + return actual_length; +} + +/* read len byte in bulk using endpoint + */ +int FX2_ll::read(uint8_t endpoint, uint8_t *buff, uint16_t len) +{ + int ret, actual_length; + ret = libusb_bulk_transfer(dev_handle, LIBUSB_ENDPOINT_IN | endpoint, + buff, len, &actual_length, 1000); + if (ret != LIBUSB_SUCCESS) { + printError("FX2 read error: " + std::string(libusb_error_name(ret))); + return -1; + } + return actual_length; +} + +/* write len data using control + */ +int FX2_ll::write_ctrl(uint8_t bRequest, uint16_t wValue, + uint8_t *buff, uint16_t len) +{ + int ret = libusb_control_transfer(dev_handle, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, + bRequest, wValue, 0x0000, buff, len, 100); + if (ret < 0) { + printError("Unable to send control request: " + + std::string(libusb_error_name(ret))); + return false; + } + return true; +} + +/* read len data using control + */ +int FX2_ll::read_ctrl(uint8_t bRequest, uint16_t wValue, + uint8_t *buff, uint16_t len) +{ + int ret = libusb_control_transfer(dev_handle, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, + bRequest, wValue, 0x0000, buff, len, 100); + if (ret < 0) { + printError("Unable to read control request: " + + std::string(libusb_error_name(ret))); + return false; + } + return true; +} + +/* load firmware section by section + * and 64B by 64B + * set CPU in reset state before and restart after + */ +bool FX2_ll::load_firmware(string firmware_path) +{ + IhexParser ihex(firmware_path, false, true); + ihex.parse(); + + /* reset */ + if (!reset(1)) + return false; + /* load */ + vector data = ihex.getDataArray(); + for (size_t i = 0; i < data.size(); i++) { + IhexParser::data_line_t data_line = data[i]; + + uint16_t toSend = data_line.length; + uint8_t *tx_buff = data_line.line_data.data(); + uint16_t addr = data_line.addr; + while (toSend > 0) { + uint16_t xfer_len = (toSend > 64) ? 64: toSend; + if (!write_ctrl(FX2_FIRM_LOAD, addr, tx_buff, xfer_len)) { + printError("load firmware failed\n"); + return false; + } + toSend -= xfer_len; + tx_buff += xfer_len; + addr += xfer_len; + } + } + + /* unset reset */ + if (!reset(0)) + return false; + return true; +} + +/* set or unset 8051RES in CPUCS register + */ +bool FX2_ll::reset(uint8_t res8051) +{ + unsigned char buf[1]; + int ret; + + buf[0] = res8051; + if (!(ret = write_ctrl(FX2_FIRM_LOAD, FX2_GCR_CPUCS, buf, 1))) { + printError("Unable to send control request: " + + std::string(libusb_error_name(ret))); + return false; + } + return true; +} diff --git a/src/fx2_ll.hpp b/src/fx2_ll.hpp new file mode 100644 index 0000000..51b5e2e --- /dev/null +++ b/src/fx2_ll.hpp @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + * Copyright (c) 2021 Gwenhael Goavec-Merou +*/ + +#ifndef SRC_FX2_LL_HPP_ +#define SRC_FX2_LL_HPP_ + +#include +#include + +#include + +/*! + * \file fx2_ll + * \class FX2_ll + * \brief low level driver for cypress fx2 device + * \author Gwenhael Goavec-Merou + */ +class FX2_ll { + public: + /*! + * \brief constructor + * \param[in] uninit_vid: vendor ID for uninitialized device + * \param[in] uninit_pid: product ID for uninitialized device + * \param[in] vid: vendor ID for initialized device + * \param[in] pid: product ID for initialized device + * \param[in] firmware_path: firmware to load + */ + FX2_ll(uint16_t uninit_vid, uint16_t uninit_pid, + uint16_t vid, uint16_t pid, const std::string &firmware_path); + ~FX2_ll(); + + /*! + * \brief bulk write + * \param[in] endpoint: endpoint to use + * \param[in] buff: buffer to write + * \param[in] len: buffer length + * \return -1 when transfer fails, number of bytes otherwise + */ + int write(uint8_t endpoint, uint8_t *buff, uint16_t len); + /*! + * \brief bulk read + * \param[in] endpoint: endpoint to use + * \param[in] buff: buffer to fill + * \param[in] len: buffer length + * \return -1 when transfer fails, number of bytes otherwise + */ + int read(uint8_t endpoint, uint8_t *buff, uint16_t len); + /*! + * \brief control write + * \param[in] bRequest + * \param[in] wValue + * \param[in] buff: buffer to write + * \param[in] len: buffer length + * \return false when transfer fails, true otherwise + */ + int write_ctrl(uint8_t bRequest, uint16_t wValue, + uint8_t *buff, uint16_t len); + /*! + * \brief control write + * \param[in] bRequest + * \param[in] wValue + * \param[in] buff: buffer to write + * \param[in] len: buffer length + * \return false when transfer fails, true otherwise + */ + int read_ctrl(uint8_t bRequest, uint16_t wValue, + uint8_t *buff, uint16_t len); + + private: + /*! + * \brief load firmware into device + * \param[in] firmware_path: firmware to load + * \return false if reset or firmware load fail + */ + bool load_firmware(std::string firmware_path); + /*! + * \brief set/unset reset bit in CPUCS register + * \param[in] res8051: set or reset + * \return false if something fails + */ + bool reset(uint8_t res8051); + bool close(); + + libusb_device_handle *dev_handle; + libusb_context *usb_ctx; +}; +#endif // SRC_FX2_LL_HPP_ +