fx2_ll: cypress fx2 low level

This commit is contained in:
Gwenhael Goavec-Merou 2021-05-13 12:41:32 +02:00
parent c09bc0662b
commit dc884b86c8
2 changed files with 309 additions and 0 deletions

219
src/fx2_ll.cpp Normal file
View File

@ -0,0 +1,219 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
* Copyright (c) 2021 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
*/
#include <libusb.h>
#include <stdint.h>
#include <unistd.h>
#include <stdexcept>
#include <string>
#include <vector>
#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<IhexParser::data_line_t> 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;
}

90
src/fx2_ll.hpp Normal file
View File

@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
* Copyright (c) 2021 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
*/
#ifndef SRC_FX2_LL_HPP_
#define SRC_FX2_LL_HPP_
#include <libusb.h>
#include <stdint.h>
#include <string>
/*!
* \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_