2021-06-26 15:24:07 +02:00
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
2021-06-20 10:25:49 +02:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2021 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
2021-06-26 15:24:07 +02:00
|
|
|
*/
|
2021-06-20 10:25:49 +02:00
|
|
|
|
|
|
|
|
#include <libusb.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <strings.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include "display.hpp"
|
|
|
|
|
#include "progressBar.hpp"
|
|
|
|
|
|
|
|
|
|
#include "dfu.hpp"
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
/* USB request write */
|
|
|
|
|
static const uint8_t DFU_REQUEST_OUT = LIBUSB_ENDPOINT_OUT |
|
|
|
|
|
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE;
|
|
|
|
|
/* USB request read */
|
|
|
|
|
static const uint8_t DFU_REQUEST_IN = LIBUSB_ENDPOINT_IN |
|
|
|
|
|
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE;
|
|
|
|
|
|
|
|
|
|
/* DFU command */
|
|
|
|
|
enum dfu_cmd {
|
|
|
|
|
DFU_DETACH = 0,
|
|
|
|
|
DFU_DNLOAD = 1,
|
|
|
|
|
DFU_UPLOAD = 2,
|
|
|
|
|
DFU_GETSTATUS = 3,
|
|
|
|
|
DFU_CLRSTATUS = 4,
|
|
|
|
|
DFU_GETSTATE = 5,
|
|
|
|
|
DFU_ABORT = 6
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Question: -
|
|
|
|
|
* - add vid/pid and override DFU file ?
|
|
|
|
|
* - index as jtag chain (fix issue when more than one device connected)
|
|
|
|
|
*/
|
|
|
|
|
|
2022-06-18 18:39:58 +02:00
|
|
|
DFU::DFU(const string &filename, bool bypass_bitstream,
|
|
|
|
|
uint16_t vid, uint16_t pid, int16_t altsetting,
|
2021-12-24 15:48:16 +01:00
|
|
|
int verbose_lvl):_verbose(verbose_lvl > 0), _debug(verbose_lvl > 1),
|
2021-06-20 10:25:49 +02:00
|
|
|
_quiet(verbose_lvl < 0), dev_idx(0), _vid(0), _pid(0),
|
2021-10-03 12:39:06 +02:00
|
|
|
_altsetting(altsetting),
|
2021-06-20 10:25:49 +02:00
|
|
|
usb_ctx(NULL), dev_handle(NULL), curr_intf(0), transaction(0),
|
|
|
|
|
_bit(NULL)
|
|
|
|
|
{
|
|
|
|
|
struct dfu_status status;
|
2022-06-18 22:50:55 +02:00
|
|
|
int dfu_vid = 0, dfu_pid = 0, ret;
|
2021-06-20 10:25:49 +02:00
|
|
|
|
2022-06-18 18:39:58 +02:00
|
|
|
printInfo("Open file : ", false);
|
2021-06-20 10:25:49 +02:00
|
|
|
|
2022-06-18 18:39:58 +02:00
|
|
|
if (bypass_bitstream) {
|
|
|
|
|
_bit = nullptr;
|
|
|
|
|
printInfo("bypassed");
|
|
|
|
|
} else {
|
|
|
|
|
try {
|
|
|
|
|
_bit = new DFUFileParser(filename, _verbose > 0);
|
|
|
|
|
printSuccess("DONE");
|
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
printError("FAIL");
|
|
|
|
|
throw runtime_error("Error: Fail to open file");
|
|
|
|
|
}
|
2021-06-20 10:25:49 +02:00
|
|
|
|
2022-06-18 18:39:58 +02:00
|
|
|
printInfo("Parse file ", false);
|
|
|
|
|
try {
|
|
|
|
|
_bit->parse();
|
|
|
|
|
printSuccess("DONE");
|
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
printError("FAIL");
|
|
|
|
|
delete _bit;
|
|
|
|
|
throw runtime_error("Error: Fail to parse file");
|
|
|
|
|
}
|
2021-06-20 10:25:49 +02:00
|
|
|
|
2022-06-18 18:39:58 +02:00
|
|
|
if (_verbose > 0)
|
|
|
|
|
_bit->displayHeader();
|
2021-12-07 14:17:09 +01:00
|
|
|
|
2022-06-18 18:39:58 +02:00
|
|
|
/* get VID and PID from dfu file */
|
|
|
|
|
try {
|
|
|
|
|
dfu_vid = std::stoi(_bit->getHeaderVal("idVendor"), 0, 16);
|
|
|
|
|
dfu_pid = std::stoi(_bit->getHeaderVal("idProduct"), 0, 16);
|
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
if (_verbose)
|
|
|
|
|
printWarn(e.what());
|
|
|
|
|
}
|
2021-06-20 10:25:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (libusb_init(&usb_ctx) < 0) {
|
|
|
|
|
delete _bit;
|
|
|
|
|
throw std::runtime_error("libusb init failed");
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-17 08:36:13 +02:00
|
|
|
/* no vid or pid provided by DFU file or by params */
|
|
|
|
|
if ((dfu_vid == 0 || dfu_pid == 0) && (vid == 0 || pid == 0)) {
|
|
|
|
|
// search all DFU compatible devices
|
2021-06-20 10:25:49 +02:00
|
|
|
if (searchDFUDevices() != EXIT_SUCCESS) {
|
2021-12-23 10:29:47 +01:00
|
|
|
libusb_exit(usb_ctx);
|
2021-06-20 10:25:49 +02:00
|
|
|
delete _bit;
|
|
|
|
|
throw std::runtime_error("Devices enumeration failed");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2021-07-17 08:36:13 +02:00
|
|
|
bool found = false;
|
|
|
|
|
if (dfu_vid != 0 && dfu_pid != 0)
|
|
|
|
|
found = searchWithVIDPID(dfu_vid, dfu_pid);
|
|
|
|
|
if (vid != 0 && pid != 0 && !found)
|
|
|
|
|
found = searchWithVIDPID(vid, pid);
|
2021-06-20 10:25:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check if DFU compatible devices are present */
|
|
|
|
|
if (dfu_dev.size() != 0) {
|
|
|
|
|
/* more than one: only possible if file is not DFU */
|
2021-12-23 10:29:47 +01:00
|
|
|
if (dfu_dev.size() > 1 && !filename.empty()) {
|
|
|
|
|
libusb_exit(usb_ctx);
|
|
|
|
|
delete _bit;
|
2021-06-20 10:25:49 +02:00
|
|
|
throw std::runtime_error("Only one device supported");
|
2021-12-23 10:29:47 +01:00
|
|
|
}
|
2021-06-20 10:25:49 +02:00
|
|
|
} else {
|
2021-12-23 10:29:47 +01:00
|
|
|
libusb_exit(usb_ctx);
|
|
|
|
|
delete _bit;
|
2021-06-20 10:25:49 +02:00
|
|
|
throw std::runtime_error("No DFU compatible device found");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_verbose)
|
|
|
|
|
displayDFU();
|
|
|
|
|
|
2021-07-17 08:36:13 +02:00
|
|
|
/* don't try device without vid/pid */
|
2022-06-18 18:39:58 +02:00
|
|
|
if ((_vid == 0 || _pid == 0) && _bit) {
|
2021-12-23 10:29:47 +01:00
|
|
|
libusb_exit(usb_ctx);
|
|
|
|
|
delete _bit;
|
2021-10-03 18:33:01 +02:00
|
|
|
throw std::runtime_error("Can't open device vid/pid == 0");
|
|
|
|
|
}
|
2021-07-17 08:36:13 +02:00
|
|
|
|
2022-06-18 18:39:58 +02:00
|
|
|
/* the If bitstream has been bypassed -> it's the end */
|
|
|
|
|
if (!_bit)
|
|
|
|
|
return;
|
|
|
|
|
|
2021-06-20 10:25:49 +02:00
|
|
|
/* open the first */
|
|
|
|
|
if (open_DFU(0) == EXIT_FAILURE) {
|
2021-12-23 10:29:47 +01:00
|
|
|
libusb_exit(usb_ctx);
|
2021-06-20 10:25:49 +02:00
|
|
|
delete _bit;
|
|
|
|
|
throw std::runtime_error("Fail to claim device");
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-03 18:33:01 +02:00
|
|
|
printf("%02x %02x\n", _vid, _pid);
|
|
|
|
|
|
2021-06-20 10:25:49 +02:00
|
|
|
if (_verbose > 0) {
|
2022-06-18 22:50:55 +02:00
|
|
|
if ((ret = get_status(&status)) < 0) {
|
|
|
|
|
delete _bit;
|
|
|
|
|
throw std::runtime_error("get device status failed with error code " +
|
|
|
|
|
std::to_string(ret));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printInfo("Default DFU status " + dfu_dev_state_val[status.bState]);
|
2021-06-20 10:25:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DFU::~DFU()
|
|
|
|
|
{
|
|
|
|
|
close_DFU();
|
|
|
|
|
libusb_exit(usb_ctx);
|
2022-06-18 18:39:58 +02:00
|
|
|
if (_bit)
|
|
|
|
|
delete _bit;
|
2021-06-20 10:25:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* open the device using VID and PID
|
|
|
|
|
*/
|
|
|
|
|
int DFU::open_DFU(int index)
|
|
|
|
|
{
|
|
|
|
|
struct dfu_dev curr_dfu;
|
2022-06-14 23:14:01 +02:00
|
|
|
int ret;
|
2021-06-20 10:25:49 +02:00
|
|
|
|
2021-07-17 08:36:13 +02:00
|
|
|
if (_vid == 0 || _pid == 0) {
|
|
|
|
|
printError("Error: Can't open device without VID/PID");
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-20 10:25:49 +02:00
|
|
|
dev_idx = index;
|
|
|
|
|
curr_dfu = dfu_dev[dev_idx];
|
|
|
|
|
curr_intf = curr_dfu.interface;
|
|
|
|
|
|
|
|
|
|
dev_handle = libusb_open_device_with_vid_pid(usb_ctx,
|
|
|
|
|
curr_dfu.vid, curr_dfu.pid);
|
|
|
|
|
if (!dev_handle) {
|
|
|
|
|
printError("Error: fail to open device");
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
2022-06-14 23:14:01 +02:00
|
|
|
if ((ret = libusb_claim_interface(dev_handle, curr_intf)) != 0) {
|
2021-06-20 10:25:49 +02:00
|
|
|
libusb_close(dev_handle);
|
2022-06-14 23:14:01 +02:00
|
|
|
printError("Error: fail to claim interface with error code " + std::to_string(ret));
|
2021-06-20 10:25:49 +02:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
2022-06-14 23:14:01 +02:00
|
|
|
if ((ret = libusb_set_interface_alt_setting(dev_handle, curr_intf, _altsetting)) != 0) {
|
2021-06-20 10:25:49 +02:00
|
|
|
libusb_release_interface(dev_handle, curr_intf);
|
|
|
|
|
libusb_close(dev_handle);
|
2022-06-14 23:14:01 +02:00
|
|
|
printError("Error: fail to set interface " + std::to_string(curr_intf) +
|
|
|
|
|
" with error code " + std::to_string(ret));
|
2021-06-20 10:25:49 +02:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* if the is claimed close it */
|
|
|
|
|
int DFU::close_DFU() {
|
|
|
|
|
if (dev_handle) {
|
|
|
|
|
int ret;
|
|
|
|
|
ret = libusb_release_interface(dev_handle, dfu_dev[dev_idx].interface);
|
|
|
|
|
if (ret != 0) {
|
|
|
|
|
/* device is already disconnected ... */
|
|
|
|
|
if (ret == LIBUSB_ERROR_NO_DEVICE) {
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
} else {
|
|
|
|
|
printError("Error: Fail to release interface");
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
libusb_close(dev_handle);
|
|
|
|
|
dev_handle = NULL;
|
|
|
|
|
}
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-17 08:36:13 +02:00
|
|
|
/* search one device using VID/PID */
|
|
|
|
|
bool DFU::searchWithVIDPID(uint16_t vid, uint16_t pid)
|
|
|
|
|
{
|
|
|
|
|
char mess[40];
|
|
|
|
|
/* search device using vid/pid */
|
|
|
|
|
libusb_device_handle *handle = NULL;
|
|
|
|
|
|
|
|
|
|
/* try first by using VID:PID from DFU file */
|
|
|
|
|
snprintf(mess, 40, "Open device %04x:%04x ",
|
|
|
|
|
vid, pid);
|
|
|
|
|
|
|
|
|
|
printInfo(mess, false);
|
|
|
|
|
handle = libusb_open_device_with_vid_pid(usb_ctx, vid, pid);
|
|
|
|
|
/* No device found */
|
|
|
|
|
if (!handle) {
|
|
|
|
|
printWarn("Not found");
|
|
|
|
|
if (_verbose)
|
|
|
|
|
printError("Error: unable to connect to device");
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
printSuccess("DONE");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* retrieve usb device structure */
|
|
|
|
|
libusb_device *dev = libusb_get_device(handle);
|
|
|
|
|
if (!dev) {
|
|
|
|
|
libusb_close(handle);
|
|
|
|
|
if (_verbose)
|
|
|
|
|
printError("Error: unable to retrieve usb device");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* and device descriptor */
|
|
|
|
|
struct libusb_device_descriptor desc;
|
|
|
|
|
int r = libusb_get_device_descriptor(dev, &desc);
|
|
|
|
|
if (r != 0) {
|
|
|
|
|
libusb_close(handle);
|
|
|
|
|
printError("Error: fail to retrieve usb descriptor");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* search if one descriptor is DFU compatible */
|
|
|
|
|
int ret = searchIfDFU(handle, dev, &desc);
|
|
|
|
|
if (ret == 1) {
|
|
|
|
|
if (_verbose)
|
|
|
|
|
printError("Error: No DFU interface");
|
|
|
|
|
}
|
|
|
|
|
_vid = vid;
|
|
|
|
|
_pid = pid;
|
|
|
|
|
|
|
|
|
|
libusb_close(handle); // no more needed -> reopen after
|
|
|
|
|
|
|
|
|
|
return (ret == 1) ? false : true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-20 10:25:49 +02:00
|
|
|
/* Tree steps are required to discover all
|
|
|
|
|
* DFU capable devices
|
|
|
|
|
* 1. loop over devices
|
|
|
|
|
*/
|
|
|
|
|
int DFU::searchDFUDevices()
|
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
|
|
|
|
libusb_device **dev_list;
|
|
|
|
|
libusb_device *usb_dev;
|
2021-07-17 08:36:13 +02:00
|
|
|
libusb_device_handle *handle;
|
2021-06-20 10:25:49 +02:00
|
|
|
|
|
|
|
|
/* clear dfu list */
|
|
|
|
|
dfu_dev.clear();
|
|
|
|
|
|
|
|
|
|
/* iteration */
|
|
|
|
|
ssize_t list_size = libusb_get_device_list(usb_ctx, &dev_list);
|
|
|
|
|
if (_verbose)
|
|
|
|
|
printInfo("found " + to_string(list_size) + " USB device");
|
|
|
|
|
|
|
|
|
|
while ((usb_dev = dev_list[i++]) != NULL) {
|
|
|
|
|
struct libusb_device_descriptor desc;
|
|
|
|
|
if (libusb_get_device_descriptor(usb_dev, &desc) != 0) {
|
|
|
|
|
printError("Unable to get device descriptor");
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_verbose > 0) {
|
2021-10-03 18:33:01 +02:00
|
|
|
printf("%04x:%04x (bus %d, device %2d)\n",
|
2023-08-08 15:54:27 +02:00
|
|
|
desc.idVendor, desc.idProduct,
|
|
|
|
|
libusb_get_bus_number(usb_dev),
|
2021-06-20 10:25:49 +02:00
|
|
|
libusb_get_device_address(usb_dev));
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-23 14:08:54 +01:00
|
|
|
int ret = libusb_open(usb_dev, &handle);
|
2021-12-24 15:48:16 +01:00
|
|
|
if (ret == 0) {
|
2022-06-18 18:47:27 +02:00
|
|
|
ret = searchIfDFU(handle, usb_dev, &desc);
|
|
|
|
|
libusb_close(handle);
|
|
|
|
|
if (ret != 0) {
|
2021-12-24 15:48:16 +01:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
} else if (_debug) {
|
2021-12-23 14:08:54 +01:00
|
|
|
char mess[256];
|
2024-02-20 20:59:13 +01:00
|
|
|
snprintf(mess, 256, "Unable to open device: "
|
2021-12-24 10:26:31 +01:00
|
|
|
"%04x:%04x (bus %d, device %2d) Error: %s -> skip\n",
|
2021-12-23 14:08:54 +01:00
|
|
|
desc.idVendor, desc.idProduct,
|
|
|
|
|
libusb_get_bus_number(usb_dev),
|
|
|
|
|
libusb_get_device_address(usb_dev),
|
|
|
|
|
libusb_error_name(ret));
|
2021-12-24 10:26:31 +01:00
|
|
|
printWarn(mess);
|
2021-06-20 10:25:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
libusb_free_device_list(dev_list, 1);
|
|
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 2. loop over configuration
|
|
|
|
|
*/
|
2021-07-17 08:36:13 +02:00
|
|
|
int DFU::searchIfDFU(struct libusb_device_handle *handle,
|
|
|
|
|
struct libusb_device *dev,
|
2021-06-20 10:25:49 +02:00
|
|
|
struct libusb_device_descriptor *desc)
|
|
|
|
|
{
|
|
|
|
|
/* configuration descriptor iteration */
|
|
|
|
|
for (int i = 0; i < desc->bNumConfigurations; i++) {
|
|
|
|
|
struct libusb_config_descriptor *cfg;
|
|
|
|
|
int ret = libusb_get_config_descriptor(dev, i, &cfg);
|
|
|
|
|
if (ret != 0) {
|
|
|
|
|
printError("Fail to retrieve config_descriptor " + to_string(i));
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
/* configuration interface iteration */
|
|
|
|
|
for (int if_idx=0; if_idx < cfg->bNumInterfaces; if_idx++) {
|
|
|
|
|
const struct libusb_interface *uif = &cfg->interface[if_idx];
|
2024-02-15 06:45:13 +01:00
|
|
|
std::vector<struct dfu_dev> dfu_dev_tmp;
|
|
|
|
|
struct dfu_desc *dfu_desc;
|
|
|
|
|
bool dfu_found = false;
|
2021-06-20 10:25:49 +02:00
|
|
|
|
|
|
|
|
/* altsettings interation */
|
|
|
|
|
for (int intf_idx = 0; intf_idx < uif->num_altsetting; intf_idx++) {
|
|
|
|
|
const struct libusb_interface_descriptor *intf = &uif->altsetting[intf_idx];
|
|
|
|
|
uint8_t intfClass = intf->bInterfaceClass;
|
|
|
|
|
uint8_t intfSubClass = intf->bInterfaceSubClass;
|
|
|
|
|
if (intfClass == 0xfe && intfSubClass == 0x01) {
|
|
|
|
|
struct dfu_dev my_dev;
|
|
|
|
|
if (_verbose)
|
|
|
|
|
printInfo("DFU found");
|
|
|
|
|
|
|
|
|
|
/* dfu functional descriptor */
|
2024-02-15 06:45:13 +01:00
|
|
|
/* not all DFU implementations provides a descriptor per altsettings
|
|
|
|
|
* so don't stop directly.
|
|
|
|
|
*/
|
2021-06-20 10:25:49 +02:00
|
|
|
if (parseDFUDescriptor(intf, reinterpret_cast<uint8_t *>(&my_dev.dfu_desc),
|
2024-02-15 06:45:13 +01:00
|
|
|
sizeof(my_dev.dfu_desc))) {
|
|
|
|
|
dfu_desc = &my_dev.dfu_desc;
|
|
|
|
|
dfu_found = true;
|
|
|
|
|
}
|
|
|
|
|
/* this altsetting is a DFU interface:
|
|
|
|
|
* - if user has selected one altsetting current is only stored if it match
|
|
|
|
|
* - otherwise all are stored
|
|
|
|
|
*/
|
|
|
|
|
if (_altsetting != -1 && _altsetting != intf_idx)
|
|
|
|
|
continue;
|
2021-06-20 10:25:49 +02:00
|
|
|
my_dev.vid = desc->idVendor;
|
|
|
|
|
my_dev.pid = desc->idProduct;
|
2021-10-03 12:39:06 +02:00
|
|
|
my_dev.altsettings = intf_idx;
|
2021-06-20 10:25:49 +02:00
|
|
|
my_dev.interface = if_idx;
|
|
|
|
|
my_dev.bus = libusb_get_bus_number(dev);
|
|
|
|
|
my_dev.device = libusb_get_device_address(dev);
|
|
|
|
|
my_dev.bMaxPacketSize0 = desc->bMaxPacketSize0;
|
2021-07-17 08:36:13 +02:00
|
|
|
|
2022-06-14 23:09:38 +02:00
|
|
|
memset(my_dev.iProduct, 0, 128);
|
2021-07-17 08:36:13 +02:00
|
|
|
libusb_get_string_descriptor_ascii(handle, desc->iProduct,
|
|
|
|
|
my_dev.iProduct, 128);
|
2022-06-14 23:09:38 +02:00
|
|
|
if (strlen((char *)my_dev.iProduct) == 0)
|
|
|
|
|
snprintf((char *)my_dev.iProduct, 128, "empty");
|
|
|
|
|
|
|
|
|
|
memset(my_dev.iInterface, 0, 128);
|
2021-10-03 12:39:06 +02:00
|
|
|
libusb_get_string_descriptor_ascii(handle, intf->iInterface,
|
|
|
|
|
my_dev.iInterface, 128);
|
2022-06-14 23:09:38 +02:00
|
|
|
if (strlen((char *)my_dev.iInterface) == 0)
|
|
|
|
|
snprintf((char *)my_dev.iInterface, 128, "empty");
|
2021-07-17 08:36:13 +02:00
|
|
|
|
2021-06-20 10:25:49 +02:00
|
|
|
int r = libusb_get_port_numbers(dev, my_dev.path, sizeof(my_dev.path));
|
|
|
|
|
my_dev.path[r] = '\0';
|
2024-02-15 06:45:13 +01:00
|
|
|
dfu_dev_tmp.push_back(my_dev);
|
2021-06-20 10:25:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-02-15 06:45:13 +01:00
|
|
|
|
|
|
|
|
/* At least one altsetting has a DFU descriptor */
|
|
|
|
|
if (dfu_found) {
|
|
|
|
|
/* with tinyDFU only one (the first) altsetting contains a descriptor
|
|
|
|
|
* fill others with this information
|
|
|
|
|
*/
|
|
|
|
|
for (struct dfu_dev &d: dfu_dev_tmp) {
|
|
|
|
|
if (d.dfu_desc.bDescriptorType == 0)
|
|
|
|
|
memcpy(&d.dfu_desc, dfu_desc, sizeof(struct dfu_desc));
|
|
|
|
|
}
|
|
|
|
|
std::move(dfu_dev_tmp.begin(), dfu_dev_tmp.end(), std::back_inserter(dfu_dev));
|
|
|
|
|
|
|
|
|
|
}
|
2021-06-20 10:25:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
libusb_free_config_descriptor(cfg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 3. check if altsettings contains a valid
|
|
|
|
|
* dfu descriptor.
|
|
|
|
|
* Since libusb has no support for those structure
|
|
|
|
|
* fill a custom structure
|
|
|
|
|
*/
|
2024-02-15 06:45:13 +01:00
|
|
|
bool DFU::parseDFUDescriptor(const struct libusb_interface_descriptor *intf,
|
2021-06-20 10:25:49 +02:00
|
|
|
uint8_t *dfu_desc, int dfu_desc_size)
|
|
|
|
|
{
|
|
|
|
|
const uint8_t *extra = intf->extra;
|
|
|
|
|
int extra_len = intf->extra_length;
|
|
|
|
|
|
2024-02-15 06:45:13 +01:00
|
|
|
/* not all altsettings for a DFU device have a DFU descriptor
|
|
|
|
|
* set to 0 to be able to detect a missing descriptor
|
|
|
|
|
*/
|
|
|
|
|
memset(dfu_desc, 0, 9);
|
|
|
|
|
|
2021-06-20 10:25:49 +02:00
|
|
|
if (extra_len < 9)
|
2024-02-15 06:45:13 +01:00
|
|
|
return false;
|
2021-06-20 10:25:49 +02:00
|
|
|
|
|
|
|
|
/* map memory to structure */
|
|
|
|
|
for (int j = 0; j + 1 < extra_len; j++) {
|
|
|
|
|
if (extra[j+1] == 0x21) {
|
|
|
|
|
memcpy(dfu_desc, (const void *)&extra[j], dfu_desc_size);
|
2024-02-15 06:45:13 +01:00
|
|
|
return true;
|
2021-06-20 10:25:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-15 06:45:13 +01:00
|
|
|
return false;
|
2021-06-20 10:25:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* abstraction to send/receive to control */
|
|
|
|
|
int DFU::send(bool out, uint8_t brequest, uint16_t wvalue,
|
2023-08-08 15:54:27 +02:00
|
|
|
const uint8_t *data, uint16_t length)
|
2021-06-20 10:25:49 +02:00
|
|
|
{
|
|
|
|
|
uint8_t type = out ? DFU_REQUEST_OUT : DFU_REQUEST_IN;
|
|
|
|
|
int ret = libusb_control_transfer(dev_handle, type,
|
|
|
|
|
brequest, wvalue, curr_intf,
|
2023-08-08 15:54:27 +02:00
|
|
|
const_cast<uint8_t *>(data), length, 5000);
|
2021-06-20 10:25:49 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
|
if (checkDevicePresent()) {
|
|
|
|
|
/* close device ? */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* subset of state transitions
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int DFU::set_state(char newState)
|
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
struct dfu_status status;
|
2022-06-18 22:50:55 +02:00
|
|
|
if (get_status(&status) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
while (status.bState != newState) {
|
|
|
|
|
switch (status.bState) {
|
2021-06-20 10:25:49 +02:00
|
|
|
case STATE_appIDLE:
|
|
|
|
|
if (dfu_detach() == EXIT_FAILURE)
|
|
|
|
|
return -1;
|
|
|
|
|
if (get_status(&status) <= 0)
|
|
|
|
|
return -1;
|
|
|
|
|
if (status.bState != STATE_appDETACH ||
|
|
|
|
|
status.bStatus != STATUS_OK) {
|
|
|
|
|
cerr << dfu_dev_status_val[status.bStatus] << endl;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case STATE_appDETACH:
|
|
|
|
|
if (newState == STATE_appIDLE) {
|
|
|
|
|
// ? reset + timeout ?
|
|
|
|
|
} else {
|
|
|
|
|
// reset
|
|
|
|
|
// reenum
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case STATE_dfuIDLE:
|
|
|
|
|
/* better to test upload/download
|
|
|
|
|
* an handle others states */
|
|
|
|
|
if (newState == STATE_appIDLE) {
|
|
|
|
|
// reset
|
2021-12-23 07:22:06 +01:00
|
|
|
ret = libusb_reset_device(dev_handle);
|
2021-06-20 10:25:49 +02:00
|
|
|
// reenum
|
|
|
|
|
} else { // download or upload
|
|
|
|
|
// are handled by download() and upload()
|
|
|
|
|
return -2;
|
|
|
|
|
}
|
2021-12-23 07:22:06 +01:00
|
|
|
return ret;
|
2021-06-20 10:25:49 +02:00
|
|
|
break;
|
|
|
|
|
case STATE_dfuDNLOAD_IDLE:
|
|
|
|
|
if (newState == STATE_dfuMANIFEST_SYNC) {
|
|
|
|
|
/* send the zero sized download request
|
|
|
|
|
* dfuDNLOAD_IDLE -> dfuMANITEST-SYNC */
|
|
|
|
|
if (_verbose)
|
|
|
|
|
printInfo("send zero sized download request");
|
|
|
|
|
ret = send(true, DFU_DNLOAD, transaction, NULL, 0);
|
|
|
|
|
} else { // dfuIDLE
|
|
|
|
|
ret = send(true, DFU_ABORT, 0, NULL, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
printError("Fails to send packet\n");
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2022-06-18 22:50:55 +02:00
|
|
|
if (get_status(&status) < 0)
|
2021-06-20 10:25:49 +02:00
|
|
|
return ret;
|
|
|
|
|
/* not always true:
|
|
|
|
|
* the newState may be the next one or another */
|
2022-06-18 22:50:55 +02:00
|
|
|
if (status.bState != newState) {
|
|
|
|
|
printError(dfu_dev_state_val[status.bState]);
|
2021-06-20 10:25:49 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case STATE_dfuERROR:
|
|
|
|
|
if (newState == STATE_appIDLE) {
|
|
|
|
|
ret = libusb_reset_device(dev_handle);
|
|
|
|
|
} else { // dfuIDLE
|
|
|
|
|
ret = send(true, DFU_CLRSTATUS, 0, NULL, 0);
|
|
|
|
|
}
|
|
|
|
|
/* if command fails
|
|
|
|
|
* try to determine if device is lost
|
|
|
|
|
* or if it's another issue */
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
if (checkDevicePresent() == 1) {
|
|
|
|
|
printError("Fails to send packet\n");
|
|
|
|
|
} else {
|
|
|
|
|
printInfo("device disconnected\n");
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
/* now state must be appIDLE or dfuIDLE
|
|
|
|
|
* use GETSTATUS to check */
|
|
|
|
|
if ((ret = get_status(&status)) <= 0)
|
|
|
|
|
return ret;
|
|
|
|
|
if (status.bState != newState ||
|
|
|
|
|
status.bStatus != STATUS_OK) {
|
|
|
|
|
cerr << dfu_dev_status_val[status.bStatus] << endl;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* detach device -> move from appIDLE to dfuIDLE
|
|
|
|
|
*/
|
|
|
|
|
int DFU::dfu_detach()
|
|
|
|
|
{
|
|
|
|
|
int res = send(true, DFU_DETACH, 0, NULL, 0);
|
|
|
|
|
return (res < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int DFU::get_status(struct dfu_status *status)
|
|
|
|
|
{
|
2023-09-01 15:35:44 +02:00
|
|
|
uint8_t buffer[6] = {0};
|
2021-06-20 10:25:49 +02:00
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
res = send(false, DFU_GETSTATUS, 0, buffer, 6);
|
|
|
|
|
|
|
|
|
|
if (res == 6) {
|
|
|
|
|
status->bStatus = buffer[0];
|
|
|
|
|
status->bwPollTimeout = (((uint32_t)buffer[3] & 0xff) << 16) ||
|
|
|
|
|
(((uint32_t)buffer[2] & 0xff) << 8) ||
|
|
|
|
|
(((uint32_t)buffer[1] & 0xff) << 0);
|
|
|
|
|
status->bState = buffer[4];
|
|
|
|
|
status->iString = buffer[5];
|
2022-06-18 22:50:55 +02:00
|
|
|
} else if (res < 0) {
|
|
|
|
|
printError("Get DFU status failed with error " +
|
|
|
|
|
std::to_string(res) +
|
|
|
|
|
"(" + std::string(libusb_error_name(res)) + ")");
|
2021-06-20 10:25:49 +02:00
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* read current device state -> unlike status
|
|
|
|
|
* this request didn't change state
|
|
|
|
|
*/
|
|
|
|
|
char DFU::get_state()
|
|
|
|
|
{
|
2023-09-01 15:35:44 +02:00
|
|
|
char c = 0;
|
2021-06-20 10:25:49 +02:00
|
|
|
|
|
|
|
|
int res = send(false, DFU_GETSTATE, 0, (unsigned char *)&c, 1);
|
|
|
|
|
|
|
|
|
|
if (res != 1)
|
|
|
|
|
return res;
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* read status until device state match
|
|
|
|
|
* wait for bwPollTimeout ms between each requests
|
|
|
|
|
*/
|
|
|
|
|
int DFU::poll_state(uint8_t state) {
|
|
|
|
|
int ret = 0;
|
|
|
|
|
struct dfu_status status;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
ret = get_status(&status);
|
|
|
|
|
if (ret <= 0) {
|
|
|
|
|
printError("Error: poll state " + string(libusb_error_name(ret)));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* millisecond */
|
|
|
|
|
usleep(1000 * status.bwPollTimeout);
|
|
|
|
|
} while (status.bState != state &&
|
|
|
|
|
status.bState != STATE_dfuERROR);
|
|
|
|
|
|
|
|
|
|
return (ret > 0) ? status.bState : ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* display details about device informations and capabilities
|
|
|
|
|
*/
|
|
|
|
|
void DFU::displayDFU()
|
|
|
|
|
{
|
|
|
|
|
/* display dfu device */
|
|
|
|
|
printf("Found DFU:\n");
|
|
|
|
|
for (unsigned int i = 0; i < dfu_dev.size(); i++) {
|
2021-10-03 12:39:06 +02:00
|
|
|
printf("%04x:%04x (bus %d, device %2d),",
|
2023-08-08 15:54:27 +02:00
|
|
|
dfu_dev[i].vid, dfu_dev[i].pid,
|
|
|
|
|
dfu_dev[i].bus, dfu_dev[i].device);
|
2021-10-03 12:39:06 +02:00
|
|
|
printf(" path: %d",dfu_dev[i].path[0]);
|
2021-06-20 10:25:49 +02:00
|
|
|
for (size_t j = 1; j < strlen(((const char *)dfu_dev[i].path)); j++)
|
|
|
|
|
printf(".%d", dfu_dev[i].path[j]);
|
2021-10-03 12:39:06 +02:00
|
|
|
printf(", alt: %d, iProduct \"%s\", iInterface \"%s\"",
|
|
|
|
|
dfu_dev[i].altsettings,
|
|
|
|
|
dfu_dev[i].iProduct, dfu_dev[i].iInterface);
|
2021-06-20 10:25:49 +02:00
|
|
|
printf("\n");
|
|
|
|
|
printf("\tDFU details\n");
|
|
|
|
|
printf("\t\tbLength %02x\n", dfu_dev[i].dfu_desc.bLength);
|
|
|
|
|
printf("\t\tbDescriptorType %02x\n",
|
|
|
|
|
dfu_dev[i].dfu_desc.bDescriptorType);
|
|
|
|
|
printf("\t\tbmAttributes %02x\n", dfu_dev[i].dfu_desc.bmAttributes);
|
|
|
|
|
printf("\t\twDetachTimeOut %04x\n",
|
|
|
|
|
dfu_dev[i].dfu_desc.wDetachTimeOut);
|
|
|
|
|
printf("\t\twTransferSize %04d\n",
|
|
|
|
|
libusb_le16_to_cpu(dfu_dev[i].dfu_desc.wTransferSize));
|
|
|
|
|
printf("\t\tbcdDFUVersion %04x\n",
|
|
|
|
|
libusb_le16_to_cpu(dfu_dev[i].dfu_desc.bcdDFUVersion));
|
|
|
|
|
uint8_t bmAttributes = dfu_dev[i].dfu_desc.bmAttributes;
|
|
|
|
|
printf("\tDFU attributes %02x\n", bmAttributes);
|
|
|
|
|
printf("\t\tDFU_DETACH : ");
|
|
|
|
|
if (bmAttributes & (1 << 3))
|
|
|
|
|
printf("ON\n");
|
|
|
|
|
else
|
|
|
|
|
printf("OFF\n");
|
|
|
|
|
printf("\t\tBitManifestionTolerant: ");
|
|
|
|
|
if (bmAttributes & (1 << 2))
|
|
|
|
|
printf("ON\n");
|
|
|
|
|
else
|
|
|
|
|
printf("OFF\n");
|
|
|
|
|
printf("\t\tUPLOAD : ");
|
|
|
|
|
if (bmAttributes & (1 << 1))
|
|
|
|
|
printf("ON\n");
|
|
|
|
|
else
|
|
|
|
|
printf("OFF\n");
|
|
|
|
|
printf("\t\tDOWNLOAD : ");
|
|
|
|
|
if (bmAttributes & (1 << 0))
|
|
|
|
|
printf("ON\n");
|
|
|
|
|
else
|
|
|
|
|
printf("OFF\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* \brief download filename content
|
|
|
|
|
* \param[in] filename: name of the file to download
|
|
|
|
|
* \return -1 when issue with file
|
|
|
|
|
* -2 when file parse fail
|
|
|
|
|
*/
|
|
|
|
|
int DFU::download()
|
|
|
|
|
{
|
2021-07-17 08:36:13 +02:00
|
|
|
/* a device must be open. Can't try
|
|
|
|
|
* to download image on an enumerate device
|
|
|
|
|
*/
|
|
|
|
|
if (!dev_handle) {
|
|
|
|
|
printError("Error: No device. Can't download file");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2022-06-18 18:39:58 +02:00
|
|
|
if (!_bit) {
|
|
|
|
|
printError("Error: No bitstream. Stop");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2021-07-17 08:36:13 +02:00
|
|
|
|
2021-06-20 10:25:49 +02:00
|
|
|
int ret, ret_val = EXIT_SUCCESS;
|
2023-08-08 15:54:27 +02:00
|
|
|
const uint8_t *buffer, *ptr;
|
2021-06-20 10:25:49 +02:00
|
|
|
int size, xfer_len;
|
|
|
|
|
int bMaxPacketSize0 = dfu_dev[dev_idx].bMaxPacketSize0;
|
|
|
|
|
|
|
|
|
|
struct dfu_status status;
|
|
|
|
|
struct dfu_dev curr_dev = dfu_dev[dev_idx];
|
|
|
|
|
|
|
|
|
|
/* check if device allows download */
|
|
|
|
|
if (!(curr_dev.dfu_desc.bmAttributes & (1 << 0))) {
|
|
|
|
|
printError("Error: download is not supported by the device\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* download must start in dfu IDLE state */
|
2022-06-18 22:50:55 +02:00
|
|
|
if (get_status(&status) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
if (status.bState != STATE_dfuIDLE)
|
2021-06-20 10:25:49 +02:00
|
|
|
set_state(STATE_dfuIDLE);
|
|
|
|
|
|
|
|
|
|
xfer_len = libusb_le16_to_cpu(curr_dev.dfu_desc.wTransferSize);
|
|
|
|
|
if (xfer_len < bMaxPacketSize0)
|
|
|
|
|
xfer_len = bMaxPacketSize0;
|
|
|
|
|
|
|
|
|
|
size = _bit->getLength() / 8;
|
|
|
|
|
buffer = _bit->getData();
|
|
|
|
|
|
|
|
|
|
if (size == 0) {
|
|
|
|
|
printError("Error: empty configuration file\n");
|
|
|
|
|
return -3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ptr = buffer;
|
|
|
|
|
|
|
|
|
|
int max_iter = size / xfer_len;
|
|
|
|
|
if (max_iter * xfer_len != size)
|
|
|
|
|
max_iter++;
|
|
|
|
|
|
|
|
|
|
ProgressBar progress("Loading", max_iter, 50, _quiet);
|
|
|
|
|
|
|
|
|
|
/* send data configuration by up to xfer_len bytes */
|
|
|
|
|
for (transaction = 0; transaction < max_iter; transaction++, ptr+=xfer_len) {
|
|
|
|
|
int residual = size - (xfer_len * transaction);
|
|
|
|
|
if (residual < xfer_len)
|
|
|
|
|
xfer_len = residual;
|
|
|
|
|
|
|
|
|
|
ret = send(true, DFU_DNLOAD, transaction,
|
|
|
|
|
(xfer_len) ? ptr : NULL, xfer_len);
|
|
|
|
|
if (ret != xfer_len) { // can't be wrong here
|
|
|
|
|
printf("Fails to send packet %s\n", libusb_error_name(ret));
|
|
|
|
|
ret_val = -4;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* wait until dfu state is again STATE_dfuDNLOAD_IDLE
|
|
|
|
|
* using status pollTimeout value
|
|
|
|
|
*/
|
|
|
|
|
ret_val = poll_state(STATE_dfuDNLOAD_IDLE);
|
|
|
|
|
|
|
|
|
|
if (ret_val != STATE_dfuDNLOAD_IDLE) {
|
|
|
|
|
printf("download: failed %d %d\n", ret_val, STATE_dfuDNLOAD_IDLE);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
progress.display(transaction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret_val != STATE_dfuDNLOAD_IDLE) {
|
|
|
|
|
progress.fail();
|
|
|
|
|
ret_val = -5;
|
|
|
|
|
return ret_val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
progress.done();
|
|
|
|
|
|
|
|
|
|
/* send the zero sized download request
|
|
|
|
|
* dfuDNLOAD_IDLE -> dfuMANITEST-SYNC
|
|
|
|
|
*/
|
2022-06-18 22:50:55 +02:00
|
|
|
printInfo("send zero sized download request: ", false);
|
|
|
|
|
//ret = set_state(STATE_dfuMANIFEST_SYNC);
|
|
|
|
|
ret = send(true, DFU_DNLOAD, transaction, NULL, 0);
|
2021-06-20 10:25:49 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
|
printError("Error: fail to change state " + to_string(ret));
|
|
|
|
|
return -6;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-18 22:50:55 +02:00
|
|
|
printSuccess("DONE");
|
|
|
|
|
|
2021-06-20 10:25:49 +02:00
|
|
|
/* Now FSM must be in dfuMANITEST-SYNC */
|
|
|
|
|
bool must_continue = true;
|
|
|
|
|
do {
|
|
|
|
|
ret = get_status(&status);
|
|
|
|
|
if (ret != 6) {
|
|
|
|
|
/* we consider device disconnected
|
|
|
|
|
* is ret == 0
|
|
|
|
|
*/
|
|
|
|
|
if (ret < 0 && ret != LIBUSB_ERROR_NO_DEVICE) {
|
|
|
|
|
printf("Error: fail to get status %d\n", ret);
|
|
|
|
|
printf("%s\n", libusb_error_name(ret));
|
|
|
|
|
ret_val = ret;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (_verbose) {
|
|
|
|
|
printInfo("status " + dfu_dev_state_val[status.bState] +
|
|
|
|
|
" " + dfu_dev_status_val[status.bStatus]);
|
|
|
|
|
}
|
|
|
|
|
usleep(1000 * status.bwPollTimeout);
|
|
|
|
|
|
|
|
|
|
switch (status.bState) {
|
|
|
|
|
case STATE_dfuMANIFEST_SYNC:
|
|
|
|
|
case STATE_dfuMANIFEST:
|
|
|
|
|
break;
|
|
|
|
|
/* need send reset */
|
|
|
|
|
case STATE_dfuMANIFEST_WAIT_RESET:
|
|
|
|
|
ret = libusb_reset_device(dev_handle);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
/* dfu device may be disconnected */
|
|
|
|
|
if (ret != LIBUSB_ERROR_NOT_FOUND) {
|
|
|
|
|
printf("%s\n", libusb_error_name(ret));
|
|
|
|
|
printf("ret %d\n", ret);
|
|
|
|
|
ret_val = -7;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
must_continue = false;
|
|
|
|
|
break;
|
|
|
|
|
case STATE_dfuERROR:
|
|
|
|
|
printError("Error: dfuERROR state\n");
|
|
|
|
|
/* before quit try to cleanup the state */
|
|
|
|
|
set_state(STATE_dfuIDLE);
|
|
|
|
|
ret_val = -7;
|
|
|
|
|
must_continue = false;
|
|
|
|
|
break;
|
|
|
|
|
case STATE_dfuIDLE:
|
|
|
|
|
case STATE_appIDLE:
|
|
|
|
|
must_continue = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} while (must_continue);
|
|
|
|
|
|
2021-12-23 07:22:06 +01:00
|
|
|
if (status.bState == STATE_dfuIDLE)
|
|
|
|
|
set_state(STATE_appIDLE);
|
|
|
|
|
|
2021-06-20 10:25:49 +02:00
|
|
|
return ret_val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* After download and manifest
|
|
|
|
|
* device may reset itself with
|
|
|
|
|
* a lost of connection
|
|
|
|
|
* return 1 if the device is present
|
|
|
|
|
* 0 if the device is lost
|
|
|
|
|
*/
|
|
|
|
|
bool DFU::checkDevicePresent()
|
|
|
|
|
{
|
|
|
|
|
struct dfu_dev curr_dfu = dfu_dev[dev_idx];
|
|
|
|
|
|
|
|
|
|
libusb_device_handle *handle;
|
|
|
|
|
handle = libusb_open_device_with_vid_pid(usb_ctx,
|
|
|
|
|
curr_dfu.vid, curr_dfu.pid);
|
|
|
|
|
if (_verbose) {
|
|
|
|
|
printInfo("device present ", false);
|
|
|
|
|
if (handle)
|
|
|
|
|
printInfo("True");
|
|
|
|
|
else
|
|
|
|
|
printInfo("False");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (handle != NULL);
|
|
|
|
|
}
|