Skip to content

Commit 432a7fe

Browse files
Version 2.1
1 parent a2bdce7 commit 432a7fe

File tree

3 files changed

+152
-69
lines changed

3 files changed

+152
-69
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.8)
22
cmake_policy(VERSION 3.8)
33

4-
project(fxload VERSION 2.0)
4+
project(fxload VERSION 2.1)
55

66
# set compile flags and options
77
# ----------------------------------------------------------

README.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ In 2023, the Mbed CE project is now making an updated version of this library wh
3232
- Improved error messages (esp. for failing to open USB devices)
3333
- Improved device selection menu
3434

35-
## Code Installation
35+
## Installing FXLoad
3636

3737
### Installer
38-
On Windows, FXLoad can be installed via the Windows installer downloadable from the Releases page. (TODO)
38+
On Windows, FXLoad can be installed via the Windows installer downloadable from the [Releases](https://github.com/mbed-ce/fxload/releases) page.
3939

4040
### Building from Source
4141
FXLoad can be built from source using CMake and a C/C++ compiler. The first step is to use Git to clone the project. Make sure to pass "--recursive" to get the submodules:
@@ -155,26 +155,34 @@ This version of fxload allows you to specify the device to connect to in three d
155155
2. You may specify `--device <vid>:<pid>` to select a device by its vendor ID and hardware ID (in hexadecimal). For example, to flash an unconfigured FX2LP, you would pass `--device 04b4:8613`. By default, this will select the first such device found, but you can change that by adding `@N` after the vid and pid to use the Nth device found (where N is the 0-indexed index of the device to use).
156156
3. You may specify `--device <bus>.<port>` to select a device by its bus and device number, specified as decimal numbers. You can get the bus and device numbers from `lsusb` on Linux, though I'm not aware of a utility to list them on Linux.
157157

158+
To list available devices, run the command
159+
```
160+
fxload list
161+
```
162+
This will list out all available USB devices on your system, so you can pick which device to use.
163+
158164
### Loading a Hex File to RAM
159165
To just load a firmware file into RAM, use a command like:
160166
```sh
161-
$ fxload --ihex-path <path/to/firmware.hex> -t FX2LP
167+
$ fxload load_ram --ihex-path <path/to/firmware.hex> -t FX2LP
162168
```
163169

164170
(the -t argument may be changed to "FX2", "FX", or "AN21" as appropriate)
165171

166-
Sinc you are loading to RAM, this method of loading firmware will only last until the device is reset, which is useful for testing firmware builds!
172+
Since you are loading to RAM, this method of loading firmware will only last until the device is reset, which is useful for testing firmware builds!
167173

168174
### Loading a Hex File to EEPROM
169175

170176
**Warning: This process can soft-brick your device if you load invalid firmware. See the "unbricking" section below for more details.**
171177

172178
To load a hex file into EEPROM, use a command line:
173179
```sh
174-
$ fxload --ihex-path <path/to/firmware.hex> -t FX2LP --eeprom -c 0xC2
180+
$ fxload load_eeprom --ihex-path <path/to/firmware.hex> -t FX2LP --control-byte 0xC2
175181
```
176182

177-
The `-c` argument gives the value for the command byte (the first byte of the device EEPROM). The value to use here changes based on the device. For FX2LP, 0xC2 causes the device to boot from EEPROM, and 0xC0 causes the device to load the VID, PID, and DID from EEPROM.
183+
The `--control-byte` argument gives the value for the command byte (the first byte of the device EEPROM). The value to use here changes based on the device. For FX2LP, 0xC2 causes the device to boot from EEPROM, and 0xC0 causes the device to load the VID, PID, and DID from the EEPROM.
184+
185+
Note: You may need to reset the chip before the new firmware will load.
178186

179187
### Loading Only VID, PID, and DID values to EEPROM
180188

src/main.cpp

Lines changed: 137 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,25 @@
3030
#include "fxload-version.h"
3131
#include "ApplicationPaths.h"
3232

33-
struct device_spec { int index; uint16_t vid, pid; int bus, port; };
33+
struct device_spec { int index; bool searchByVidPid; uint16_t vid, pid; int bus, port; };
3434

3535
/*
3636
* Finds the correct USB device to open based on the provided device spec.
3737
* If wanted is nullptr, all USB devices are printed to the console and the user
3838
* can select which to use.
39+
* If listOnly is true, we just print the list of USB devices and then return without selecting one.
3940
*/
40-
libusb_device_handle *get_usb_device(struct device_spec *wanted) {
41+
libusb_device_handle * search_usb_devices(bool listOnly, struct device_spec *wanted) {
4142
libusb_device **list;
4243
libusb_device_handle *dev_h = NULL;
4344

4445
libusb_init(NULL);
4546

46-
// If in double-verbose mode or above, set libusb to debug log mode.
47-
libusb_set_option(nullptr, LIBUSB_OPTION_LOG_LEVEL, verbose >= 2 ? LIBUSB_LOG_LEVEL_DEBUG : LIBUSB_LOG_LEVEL_WARNING);
4847

4948
libusb_device *found = NULL;
5049
int nr_found = 0;
5150

52-
bool search_all = wanted == nullptr;
51+
bool search_all = wanted == nullptr || listOnly;
5352

5453
ssize_t nr = libusb_get_device_list(NULL, &list);
5554
for (int i = 0; i < nr; i++) {
@@ -65,11 +64,24 @@ libusb_device_handle *get_usb_device(struct device_spec *wanted) {
6564

6665
if(!search_all)
6766
{
68-
if ((bus == wanted->bus && (port == wanted->port || wanted->port == 0)) ||
69-
(vid == wanted->vid && ( pid == wanted->pid || wanted->pid == 0))) {
70-
if (nr_found++ == wanted->index) {
71-
found = dev;
72-
break;
67+
if(wanted->searchByVidPid)
68+
{
69+
if(vid == wanted->vid && ( pid == wanted->pid || wanted->pid == 0))
70+
{
71+
if (nr_found++ == wanted->index) {
72+
found = dev;
73+
break;
74+
}
75+
}
76+
}
77+
else
78+
{
79+
if (bus == wanted->bus && (port == wanted->port || wanted->port == 0))
80+
{
81+
if (nr_found++ == wanted->index) {
82+
found = dev;
83+
break;
84+
}
7385
}
7486
}
7587
}
@@ -106,10 +118,17 @@ libusb_device_handle *get_usb_device(struct device_spec *wanted) {
106118
deviceDetailsString = "<failed to open device>";
107119
#endif
108120
}
109-
printf("%d: Bus %03d Device %03d: ID %04X:%04X %s\n", i, bus, port, vid, pid, deviceDetailsString.c_str());
121+
printf("%d: Bus %03d Device %03d: ID %04x:%04x %s\n", i, bus, port, vid, pid, deviceDetailsString.c_str());
110122
}
111123
}
112124

125+
if(listOnly)
126+
{
127+
// We just wanted to list them, so we're done now.
128+
libusb_free_device_list(list, 1);
129+
return nullptr;
130+
}
131+
113132
if (search_all) {
114133
printf("Please select device to configure [0-%d]: ", static_cast<int>(nr - 1));
115134
fflush(NULL);
@@ -120,11 +139,13 @@ libusb_device_handle *get_usb_device(struct device_spec *wanted) {
120139
if(!std::cin)
121140
{
122141
logerror("Invalid input.\n");
142+
libusb_free_device_list(list, 1);
123143
return nullptr;
124144
}
125145

126146
if( sel < 0 || sel >= nr) {
127147
logerror("device selection out of bound: %d\n", sel);
148+
libusb_free_device_list(list, 1);
128149
return NULL;
129150
}
130151

@@ -133,8 +154,13 @@ libusb_device_handle *get_usb_device(struct device_spec *wanted) {
133154

134155
if (! found) {
135156
logerror("device not selected\n");
157+
libusb_free_device_list(list, 1);
136158
return NULL;
137159
}
160+
161+
// If in double-verbose mode or above, set libusb to debug log mode.
162+
// This will help diagnose errors from opening the device.
163+
libusb_set_option(nullptr, LIBUSB_OPTION_LOG_LEVEL, verbose >= 2 ? LIBUSB_LOG_LEVEL_DEBUG : LIBUSB_LOG_LEVEL_WARNING);
138164

139165
int openRet = libusb_open(found, &dev_h);
140166
libusb_free_device_list(list, 1);
@@ -165,6 +191,7 @@ parse_device_path(const std::string & device_path, struct device_spec *spec) {
165191
auto pid_string = device_path.substr(colonIdx + 1, (atIndex == std::string::npos ? std::string::npos : (atIndex - colonIdx - 1)));
166192
spec->vid = static_cast<uint16_t>(std::stoul(vid_string, nullptr, 16));
167193
spec->pid = static_cast<uint16_t>(std::stoul(pid_string, nullptr, 16));
194+
spec->searchByVidPid = true;
168195
}
169196
else if (dotIdx != std::string::npos)
170197
{
@@ -177,6 +204,7 @@ parse_device_path(const std::string & device_path, struct device_spec *spec) {
177204
auto port_string = device_path.substr(dotIdx + 1, (atIndex == std::string::npos ? std::string::npos : (atIndex - dotIdx - 1)));
178205
spec->bus = std::stoi(bus_string);
179206
spec->port = std::stoi(port_string);
207+
spec->searchByVidPid = false;
180208
}
181209

182210
// Look for optional "@index" suffix
@@ -211,80 +239,127 @@ int main(int argc, char*argv[])
211239
ezusb_chip_t type = NONE;
212240
int eeprom_first_byte = -1;
213241
bool load_to_eeprom = false;
242+
bool printVersion = false;
214243

215244
// Find resources directory
216245
std::string app_install_dir = AppPaths::getExecutableDir();
217246
std::string stage1_loader = app_install_dir + AppPaths::PATH_SEP + ".." + AppPaths::PATH_SEP + "share" + AppPaths::PATH_SEP + "fxload" + AppPaths::PATH_SEP + "Vend_Ax.hex";
218247

219-
// List of CLI options
248+
// CLI options for fxload
220249
app.add_flag("-v,--verbose", verbose, "Verbose mode. May be supplied up to 3 times for more verbosity."); // note: CLI11 will count the occurrences of a flag when you pass an integer variable to add_flag()
221-
app.add_option("-I,--ihex-path", ihex_path, "Hex file to program")
250+
app.add_flag("-V,--version", printVersion, "Print version and exit.");
251+
252+
// Subcommands
253+
CLI::App * load_ram_subcommand = app.add_subcommand("load_ram", "Load a binary into file into the EZ-USB chip's RAM.");
254+
CLI::App * load_eeprom_subcommand = app.add_subcommand("load_eeprom", "Load a binary into file into the EZ-USB chip's EEPROM.");
255+
CLI::App * list_usb_subcommand = app.add_subcommand("list", "List all available USB devices and exit");
256+
257+
// load_ram options
258+
load_ram_subcommand->add_option("-I,--ihex-path", ihex_path, "Hex file to program")
259+
->required()
260+
->check(CLI::ExistingFile);
261+
load_ram_subcommand->add_option("-t,--type", type, "Select device type (from AN21|FX|FX2|FX2LP)")
262+
->required()
263+
->transform(CLI::CheckedTransformer(DeviceTypeNames, CLI::ignore_case).description(""));
264+
load_ram_subcommand->add_option("-D,--device", device_spec_string,
265+
"Select device by vid:pid(@index) or bus.port(@index). If not provided, all discovered USB devices will be displayed as options.");
266+
267+
// load_eeprom options
268+
load_eeprom_subcommand->add_option("-I,--ihex-path", ihex_path, "Hex file to program")
222269
->required()
223270
->check(CLI::ExistingFile);
224-
app.add_option("-t,--type", type, "Select device type (from AN21|FX|FX2|FX2LP)")
271+
load_eeprom_subcommand->add_option("-t,--type", type, "Select device type (from AN21|FX|FX2|FX2LP)")
225272
->required()
226-
->transform(CLI::CheckedTransformer(DeviceTypeNames, CLI::ignore_case));
227-
app.add_option("-D,--device", device_spec_string, "Select device by vid:pid(@index) or bus.port(@index). If not provided, all discovered USB devices will be displayed as options.");
228-
auto eeprom_first_byte_opt = app.add_option("-c,--eeprom-first-byte", eeprom_first_byte, "Value programmed to first byte of EEPROM to set chip behavior. e.g. for FX2LP this should be 0xC0 or 0xC2")
229-
->check(CLI::Range(static_cast<uint16_t>(std::numeric_limits<uint8_t>::min()), static_cast<uint16_t>(std::numeric_limits<uint8_t>::max())));
230-
auto eeprom_opt = app.add_flag("-e, --eeprom", load_to_eeprom, "Load the hex file to EEPROM via a 1st stage loader instead of directly to RAM")
231-
->needs(eeprom_first_byte_opt);
232-
app.add_option("-s,--stage1", stage1_loader, "Path to the stage 1 loader file to use when flashing EEPROM. Default: " + stage1_loader)
233-
->needs(eeprom_opt)
273+
->transform(CLI::CheckedTransformer(DeviceTypeNames, CLI::ignore_case).description(""));
274+
load_eeprom_subcommand->add_option("-D,--device", device_spec_string,
275+
"Select device by vid:pid(@index) or bus.port(@index). If not provided, all discovered USB devices will be displayed as options.");
276+
load_eeprom_subcommand->add_option("-c,--control-byte", eeprom_first_byte, "Value programmed to first byte of EEPROM to set chip behavior. e.g. for FX2LP this should be 0xC0 or 0xC2")
277+
->check(CLI::Range(std::numeric_limits<uint8_t>::min(), std::numeric_limits<uint8_t>::max()).description(""));
278+
load_eeprom_subcommand->add_option("-s,--stage1", stage1_loader, "Path to the stage 1 loader file to use when flashing EEPROM. Default: " + stage1_loader)
234279
->check(CLI::ExistingFile);
235280

236281
CLI11_PARSE(app, argc, argv);
237282

238-
// Handle CLI options
239-
struct device_spec spec = {0};
240-
if(!device_spec_string.empty())
283+
// handle -V
284+
if(printVersion)
241285
{
242-
int parseResult = parse_device_path(device_spec_string, &spec);
243-
if(parseResult != 0)
286+
printf("%s\n", FXLOAD_VERSION_STR);
287+
return 0;
288+
}
289+
else
290+
{
291+
if(app.get_subcommands().size() == 0)
244292
{
245-
return parseResult;
293+
printf("Must specify a subcommand! Run with --help for more information.\n");
294+
return 1;
246295
}
247296
}
248297

249-
libusb_device_handle *device;
250-
int status;
298+
// Handle subcommands
299+
if(list_usb_subcommand->parsed())
300+
{
301+
search_usb_devices(true, nullptr);
302+
return 0;
303+
}
304+
else // load_ram or load_eeprom (all commands which open a USB device)
305+
{
306+
// Find USB device to operate on
307+
struct device_spec spec = {0};
308+
if(!device_spec_string.empty())
309+
{
310+
int parseResult = parse_device_path(device_spec_string, &spec);
311+
if(parseResult != 0)
312+
{
313+
return parseResult;
314+
}
315+
}
251316

252-
device = get_usb_device(device_spec_string.empty() ? nullptr : &spec);
317+
libusb_device_handle *device;
253318

254-
if (device == NULL) {
255-
logerror("No device to configure\n");
256-
return -1;
257-
}
319+
device = search_usb_devices(false, device_spec_string.empty() ? nullptr : &spec);
258320

259-
if (type == NONE) {
260-
type = FX; /* an21-compatible for most purposes */
261-
}
321+
if (device == NULL) {
322+
logerror("Failed to select device\n");
323+
return -1;
324+
}
262325

263-
if (load_to_eeprom) {
264-
/* first stage: put loader into internal memory */
265-
if (verbose)
266-
logerror("1st stage: load 2nd stage loader\n");
267-
status = ezusb_load_ram (device, stage1_loader.c_str(), type, 0);
268-
if (status != 0)
269-
return status;
270-
271-
/* second stage ... write EEPROM */
272-
status = ezusb_load_eeprom (device, ihex_path.c_str(), type, eeprom_first_byte);
273-
if (status != 0)
274-
return status;
275-
276-
} else {
277-
/* single stage, put into internal memory */
278-
if (verbose)
279-
logerror("single stage: load on-chip memory\n");
280-
status = ezusb_load_ram (device, ihex_path.c_str(), type, 0);
281-
if (status != 0)
282-
return status;
283-
}
326+
if(load_ram_subcommand->parsed())
327+
{
328+
/* single stage, put into internal memory */
329+
if (verbose)
330+
logerror("single stage: load on-chip memory\n");
331+
int status = ezusb_load_ram (device, ihex_path.c_str(), type, 0);
332+
if(status != 0)
333+
{
334+
libusb_close(device);
335+
return status;
336+
}
337+
338+
}
339+
else if(load_eeprom_subcommand->parsed())
340+
{
341+
/* first stage: put loader into internal memory */
342+
if (verbose)
343+
logerror("1st stage: load 2nd stage loader\n");
344+
int status = ezusb_load_ram (device, stage1_loader.c_str(), type, 0);
345+
if (status != 0)
346+
{
347+
libusb_close(device);
348+
return status;
349+
}
284350

285-
libusb_close(device);
351+
/* second stage ... write EEPROM */
352+
status = ezusb_load_eeprom (device, ihex_path.c_str(), type, eeprom_first_byte);
353+
if (status != 0)
354+
{
355+
libusb_close(device);
356+
return status;
357+
}
358+
}
286359

287-
printf("Done.\n");
360+
libusb_close(device);
361+
printf("Done.\n");
362+
}
288363

289-
exit(0);
364+
return 0;
290365
}

0 commit comments

Comments
 (0)