228 lines
7.0 KiB
C++
228 lines
7.0 KiB
C++
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||
|
|
/*
|
||
|
|
* Copyright (c) 2021 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||
|
|
*/
|
||
|
|
|
||
|
|
#ifndef SRC_DFU_HPP_
|
||
|
|
#define SRC_DFU_HPP_
|
||
|
|
|
||
|
|
#include <libusb.h>
|
||
|
|
|
||
|
|
#include <map>
|
||
|
|
#include <string>
|
||
|
|
#include <vector>
|
||
|
|
|
||
|
|
#include "dfuFileParser.hpp"
|
||
|
|
|
||
|
|
class DFU {
|
||
|
|
public:
|
||
|
|
/*!
|
||
|
|
* \brief contructor
|
||
|
|
* \param[in] verbose_lvl: verbose level 0 normal, -1 quiet, 1 verbose
|
||
|
|
*/
|
||
|
|
DFU(const std::string &filename, int verbose_lvl);
|
||
|
|
|
||
|
|
~DFU();
|
||
|
|
|
||
|
|
/*!
|
||
|
|
* \brief enumerate all USB peripherals configuration to detect DFU devices
|
||
|
|
* \return EXIT_FAILURE when something is wrong, EXIT_SUCCESS otherwise
|
||
|
|
*/
|
||
|
|
|
||
|
|
int searchDFUDevices();
|
||
|
|
/*!
|
||
|
|
* \brief display details about all DFU compatible devices found
|
||
|
|
*/
|
||
|
|
void displayDFU();
|
||
|
|
|
||
|
|
/*!
|
||
|
|
* \brief download file content
|
||
|
|
* \param[in] filename: filename to download
|
||
|
|
*/
|
||
|
|
int download();
|
||
|
|
|
||
|
|
private:
|
||
|
|
/**
|
||
|
|
* \brief dfu descriptor structure (not provided by libusb
|
||
|
|
*/
|
||
|
|
struct dfu_desc {
|
||
|
|
uint8_t bLength;
|
||
|
|
uint8_t bDescriptorType;
|
||
|
|
uint8_t bmAttributes;
|
||
|
|
uint16_t wDetachTimeOut;
|
||
|
|
uint16_t wTransferSize;
|
||
|
|
uint16_t bcdDFUVersion;
|
||
|
|
} __attribute__((__packed__));
|
||
|
|
|
||
|
|
struct dfu_dev {
|
||
|
|
uint16_t vid;
|
||
|
|
uint16_t pid;
|
||
|
|
uint8_t bus;
|
||
|
|
uint8_t interface;
|
||
|
|
uint8_t device;
|
||
|
|
uint8_t path[8];
|
||
|
|
uint32_t bMaxPacketSize0;
|
||
|
|
struct dfu_desc dfu_desc;
|
||
|
|
};
|
||
|
|
|
||
|
|
/* USB Device firmware Upgrade Specification, Revision 1.1 6.1.2 */
|
||
|
|
/* p.20 */
|
||
|
|
struct dfu_status {
|
||
|
|
uint8_t bStatus;
|
||
|
|
uint32_t bwPollTimeout;
|
||
|
|
uint8_t bState;
|
||
|
|
uint8_t iString;
|
||
|
|
};
|
||
|
|
|
||
|
|
/* USB Device firmware Upgrade Specification, Revision 1.1 6.1.2 */
|
||
|
|
/* p.21 */
|
||
|
|
enum dfu_dev_status {
|
||
|
|
STATUS_OK = 0x00,
|
||
|
|
STATUS_errTARGET = 0x01,
|
||
|
|
STATUS_errFILE = 0x02,
|
||
|
|
STATUS_errWRITE = 0x03,
|
||
|
|
STATUS_errERASE = 0x04,
|
||
|
|
STATUS_errCHECK_ERASED = 0x05,
|
||
|
|
STATUS_errPROG = 0x06,
|
||
|
|
STATUS_errVERIFY = 0x07,
|
||
|
|
STATUS_errADDRESS = 0x08,
|
||
|
|
STATUS_errNOTDONE = 0x09,
|
||
|
|
STATUS_errFIRMWARE = 0x0A,
|
||
|
|
STATUS_errVENDOR = 0x0B,
|
||
|
|
STATUS_errUSBR = 0x0C,
|
||
|
|
STATUS_errPOR = 0x0D,
|
||
|
|
STATUS_errUNKNOWN = 0x0E,
|
||
|
|
STATUS_errSTALLEDPKT = 0x0F
|
||
|
|
};
|
||
|
|
|
||
|
|
std::map<uint8_t, std::string> dfu_dev_status_val = {
|
||
|
|
{STATUS_OK, "STATUS_OK"},
|
||
|
|
{STATUS_errTARGET, "STATUS_errTARGET"},
|
||
|
|
{STATUS_errFILE, "STATUS_errFILE"},
|
||
|
|
{STATUS_errWRITE, "STATUS_errWRITE"},
|
||
|
|
{STATUS_errERASE, "STATUS_errERASE"},
|
||
|
|
{STATUS_errCHECK_ERASED, "STATUS_errCHECK_ERASED"},
|
||
|
|
{STATUS_errPROG, "STATUS_errPROG"},
|
||
|
|
{STATUS_errVERIFY, "STATUS_errVERIFY"},
|
||
|
|
{STATUS_errADDRESS, "STATUS_errADDRESS"},
|
||
|
|
{STATUS_errNOTDONE, "STATUS_errNOTDONE"},
|
||
|
|
{STATUS_errFIRMWARE, "STATUS_errFIRMWARE"},
|
||
|
|
{STATUS_errVENDOR, "STATUS_errVENDOR"},
|
||
|
|
{STATUS_errUSBR, "STATUS_errUSBR"},
|
||
|
|
{STATUS_errPOR, "STATUS_errPOR"},
|
||
|
|
{STATUS_errUNKNOWN, "STATUS_errUNKNOWN"},
|
||
|
|
{STATUS_errSTALLEDPKT, "STATUS_errSTALLEDPKT"}
|
||
|
|
};
|
||
|
|
|
||
|
|
/* p.22 */
|
||
|
|
enum dfu_dev_state {
|
||
|
|
STATE_appIDLE = 0,
|
||
|
|
STATE_appDETACH = 1,
|
||
|
|
STATE_dfuIDLE = 2,
|
||
|
|
STATE_dfuDNLOAD_SYNC = 3,
|
||
|
|
STATE_dfuDNBUSY = 4,
|
||
|
|
STATE_dfuDNLOAD_IDLE = 5,
|
||
|
|
STATE_dfuMANIFEST_SYNC = 6,
|
||
|
|
STATE_dfuMANIFEST = 7,
|
||
|
|
STATE_dfuMANIFEST_WAIT_RESET = 8,
|
||
|
|
STATE_dfuUPLOAD_IDLE = 9,
|
||
|
|
STATE_dfuERROR = 10
|
||
|
|
};
|
||
|
|
|
||
|
|
std::map <uint8_t, std::string> dfu_dev_state_val = {
|
||
|
|
{STATE_appIDLE, "STATE_appIDLE"},
|
||
|
|
{STATE_appDETACH, "STATE_appDETACH"},
|
||
|
|
{STATE_dfuIDLE, "STATE_dfuIDLE"},
|
||
|
|
{STATE_dfuDNLOAD_SYNC, "STATE_dfuDNLOAD-SYNC"},
|
||
|
|
{STATE_dfuDNBUSY, "STATE_dfuDNBUSY"},
|
||
|
|
{STATE_dfuDNLOAD_IDLE, "STATE_dfuDNLOAD-IDLE"},
|
||
|
|
{STATE_dfuMANIFEST_SYNC, "STATE_dfuMANIFEST-SYNC"},
|
||
|
|
{STATE_dfuMANIFEST, "STATE_dfuMANIFEST"},
|
||
|
|
{STATE_dfuMANIFEST_WAIT_RESET, "STATE_dfuMANIFEST-WAIT-RESET"},
|
||
|
|
{STATE_dfuUPLOAD_IDLE, "STATE_dfuUPLOAD-IDLE"},
|
||
|
|
{STATE_dfuERROR, "STATE_dfuERROR"}
|
||
|
|
};
|
||
|
|
|
||
|
|
/*!
|
||
|
|
* \brief take control to the DFU device
|
||
|
|
* \param[in] index: device index in dfu_dev list
|
||
|
|
* \return EXIT_FAILURE when open device fails, EXIT_SUCCESS otherwise
|
||
|
|
*/
|
||
|
|
int open_DFU(int index);
|
||
|
|
/*!
|
||
|
|
* \brief release control to the DFU device
|
||
|
|
* \return EXIT_FAILURE when close fails, EXIT_SUCCESS otherwise
|
||
|
|
*/
|
||
|
|
int close_DFU();
|
||
|
|
/*!
|
||
|
|
* \brief send detach command
|
||
|
|
* return EXIT_FAILURE when transaction fails, EXIT_SUCCESS otherwise
|
||
|
|
*/
|
||
|
|
int dfu_detach();
|
||
|
|
/*!
|
||
|
|
* \brief read device status
|
||
|
|
* \param[out] status: struct dfu_status device payload
|
||
|
|
* return error/return code from libusb or number of bytes read/write
|
||
|
|
*/
|
||
|
|
int get_status(struct dfu_status *status);
|
||
|
|
/*!
|
||
|
|
* \brief move through DFU state from current state to newState
|
||
|
|
* \param[in] newState: targeted state
|
||
|
|
* \return -1 when something fail, 0 otherwise
|
||
|
|
*/
|
||
|
|
int set_state(char newState);
|
||
|
|
/*!
|
||
|
|
* \brief get current state without changing DFU state (6.1.5)
|
||
|
|
* \return -1 when USB transaction fail, state otherwise
|
||
|
|
*/
|
||
|
|
char get_state();
|
||
|
|
/*!
|
||
|
|
* \brief poll status until device is in state mode
|
||
|
|
* \param[in] targeted state
|
||
|
|
* \return value < 0 when transaction fails, new state otherwise
|
||
|
|
*/
|
||
|
|
int poll_state(uint8_t state);
|
||
|
|
/*!
|
||
|
|
* \brief verify if DFU device always exist
|
||
|
|
* \return false if lost, true if present
|
||
|
|
*/
|
||
|
|
bool checkDevicePresent();
|
||
|
|
/*!
|
||
|
|
* \brief send an IN/OUT request
|
||
|
|
*/
|
||
|
|
int send(bool out, uint8_t brequest, uint16_t wvalue,
|
||
|
|
unsigned char *data, uint16_t length);
|
||
|
|
/*!
|
||
|
|
* \brief fill specific DFU structure with extra descriptor
|
||
|
|
* \param[in] intf: interface descriptor with extra area
|
||
|
|
* \param[out] dfu_desc: DFU descriptor
|
||
|
|
* \param[in] dfu_desc_size: DFU descriptor structure size
|
||
|
|
* \return -1 if extra len is too small, 0 otherwise
|
||
|
|
* */
|
||
|
|
int parseDFUDescriptor(const struct libusb_interface_descriptor *intf,
|
||
|
|
uint8_t *dfu_desc, int dfu_desc_size);
|
||
|
|
/*!
|
||
|
|
* \brief search, for the specified device, if it has a DFU interface
|
||
|
|
* \param[in] dev: USB device
|
||
|
|
* \param[in] desc: USB device descriptor
|
||
|
|
* \return 1 when can't read config, 0 otherwise
|
||
|
|
*/
|
||
|
|
int searchIfDFU(struct libusb_device *dev,
|
||
|
|
struct libusb_device_descriptor *desc);
|
||
|
|
|
||
|
|
bool _verbose; /**< display more message */
|
||
|
|
bool _quiet; /**< don't use progressBar */
|
||
|
|
std::vector<struct dfu_dev> dfu_dev; /**< available dfu devices */
|
||
|
|
int dev_idx; /**< device index in dfu_dev */
|
||
|
|
uint16_t _vid; /**< device Vendor ID */
|
||
|
|
uint16_t _pid; /**< device Product ID */
|
||
|
|
struct libusb_context *usb_ctx; /**< usb context */
|
||
|
|
libusb_device_handle * dev_handle; /**< current device handle */
|
||
|
|
int curr_intf; /**< device interface to use */
|
||
|
|
int transaction; /**< download transaction ID */
|
||
|
|
|
||
|
|
DFUFileParser *_bit; /**< dfu file to load */
|
||
|
|
};
|
||
|
|
|
||
|
|
#endif // SRC_DFU_HPP_
|