diff --git a/CMakeLists.txt b/CMakeLists.txt index bf5cf89..4f1bfa7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ add_custom_target(fxsdk ALL DEPENDS "${BIN}/fxsdk.sh") # fxlink configure_file(fxlink/config.h.in "${BIN}/include/fxlink/config.h") -add_executable(fxlink fxlink/usb.c fxlink/filter.c fxlink/interactive.c +add_executable(fxlink fxlink/usb.c fxlink/filter.c fxlink/interactive.c fxlink/push.c fxlink/main.c fxlink/png.c fxlink/properties.c fxlink/ud2.c fxlink/util.c fxlink/protocol.c fxlink/sdl2.c) target_link_libraries(fxlink PkgConfig::libpng PkgConfig::libusb) diff --git a/fxlink/fxlink.h b/fxlink/fxlink.h index d702c42..08399eb 100644 --- a/fxlink/fxlink.h +++ b/fxlink/fxlink.h @@ -28,6 +28,9 @@ int main_blocks(filter_t *filter, delay_t *delay); int main_send(filter_t *filter, delay_t *delay, char **files); /* Main function for -i */ -int main_interactive(filter_t *filter,delay_t *delay,libusb_context *context); +int main_interactive(filter_t *filter, delay_t *delay, libusb_context *context); + +/* Main function for -p */ +int main_push(filter_t *filter, delay_t *delay, libusb_context *context, char **files); #endif /* FXLINK_FXLINK_H */ diff --git a/fxlink/main.c b/fxlink/main.c index e7433bf..0dced44 100644 --- a/fxlink/main.c +++ b/fxlink/main.c @@ -30,6 +30,7 @@ static const char *help_string = " -b, --blocks List detected Mass Storage filesystems (udisks2)\n" " -s, --send Send a file to a Mass Storage calculator (udisks2)\n" " -i, --interactive Interactive messaging with a gint add-in (libusb)\n" +" -p, --push Push a .bin file to the Add-In Push app (libusb)\n" "\n" "General options:\n" " -w DELAY Wait up to this many seconds for a calculator to\n" @@ -87,6 +88,7 @@ int main(int argc, char **argv) { "blocks", no_argument, NULL, 'b' }, { "send", no_argument, NULL, 's' }, { "interactive", no_argument, NULL, 'i' }, + { "push", no_argument, NULL, 'p' }, { "libusb-log", required_argument, NULL, LIBUSB_LOG }, { "quiet", no_argument, NULL, 'q' }, { "fxlink-log", optional_argument, NULL, LOG_TO_FILE }, @@ -95,7 +97,7 @@ int main(int argc, char **argv) }; while(option >= 0 && option != '?') - switch((option = getopt_long(argc, argv, "hlbsiquf:w::r", longs, NULL))) + switch((option = getopt_long(argc, argv, "hlbsipquf:w::r", longs, NULL))) { case 'h': fprintf(stderr, help_string, argv[0]); @@ -104,6 +106,7 @@ int main(int argc, char **argv) case 'b': case 's': case 'i': + case 'p': mode = option; break; case LIBUSB_LOG: @@ -163,6 +166,11 @@ int main(int argc, char **argv) if(mode == 's' && optind == argc) error = err("send mode requires additional arguments (file names)"); + if(mode == 'p' && optind == argc) + error = err("push mode requires a file name"); + if(mode == 'p' && optind < argc-1) + error = err("push mode only accepts one file name"); + /* No arguments or bad arguments */ if(error) return 1; @@ -178,7 +186,7 @@ int main(int argc, char **argv) libusb_context *context = NULL; /* Initialize libusb for corresponding modes */ - if(mode == 'l' || mode == 'i') { + if(mode == 'l' || mode == 'i' || mode == 'p') { if((rc = libusb_init(&context))) return libusb_err(rc, "error initializing libusb"); libusb_set_option(context, LIBUSB_OPTION_LOG_LEVEL, loglevel); @@ -211,6 +219,9 @@ int main(int argc, char **argv) } while(repeat); } + else if(mode == 'p') { + rc = main_push(filter, &delay, context, argv + optind); + } if(context) libusb_exit(context); diff --git a/fxlink/push.c b/fxlink/push.c new file mode 100644 index 0000000..4f2423e --- /dev/null +++ b/fxlink/push.c @@ -0,0 +1,129 @@ +#include "config.h" +#include "fxlink.h" +#include "util.h" +#include "properties.h" +#include "filter.h" +#include "usb.h" + +#include +#include +#include +#include + +int main_push(filter_t *filter, delay_t *delay, libusb_context *context, char** files) +{ + int rc = 1; + libusb_device *dev = NULL; + libusb_device_handle *dh = NULL; + + FILE *fp = fopen(files[0], "rb"); + if (!fp) { + printf("error: Unable to open file %s\n", files[0]); + goto end; + } + + fseek(fp, 0, SEEK_END); + long fsize = ftell(fp); + // If more than 6MB, abort + if (fsize > 6 * 1024 * 1024) { + printf("error: File is too large (max 6MB)\n"); + goto end; + } + fseek(fp, 0, SEEK_SET); + uint8_t *filebuf = malloc(fsize); + fread(filebuf, fsize, 1, fp); + fclose(fp); + + /* Wait for a device to be connected */ + filter_clean_libusb(filter); + rc = usb_unique_wait(filter, delay, context, &dev); + + if(rc == FILTER_NONE) { + printf("No device found.\n"); + return 1; + } + else if(rc == FILTER_MULTIPLE) { + printf("Multiple devices found, ambiguous!\n"); + return 1; + } + + if((rc = libusb_open(dev, &dh))) { + rc = libusb_err(rc, "cannot open device %s", usb_id(dev)); + goto end; + } + + /* Don't detach kernel drivers to avoid breaking the Mass Storage + communications if fxlink is ever started while the native LINK + application is running! */ + libusb_set_auto_detach_kernel_driver(dh, false); + + if((rc = libusb_claim_interface(dh, 0))) { + rc = libusb_err(rc, "cannot claim interface on %s", usb_id(dev)); + goto end; + } + + printf("Connected to %s\n", usb_id(dev)); + + // Wait to receive "USB loader ready" over USB bulk transfer + uint8_t buf[18]; + while (1) + { + int actual_length; + rc = libusb_bulk_transfer(dh, 0x82, buf, sizeof(buf) - 1, &actual_length, 0); + buf[sizeof(buf) - 1] = 0; + // if (rc == LIBUSB_ERROR_TIMEOUT) continue; + if (rc) { + rc = libusb_err(rc, "cannot receive data: %s", usb_id(dev)); + goto end; + } + if (actual_length == 0) continue; + if (actual_length != 17) { + printf("error: Received %d bytes, expected 17\n", actual_length); + goto end; + } + // See if it's the "USB loader ready" message with strcmp + if (strcmp((char*) buf, "USB loader ready") == 0) { + printf("Ready to send!\n"); + break; + } else { + printf("error: Unknown message received: %s\n", buf); + goto end; + } + } + + // Send the contents of the passed file over USB bulk transfer + + // First send the size of the file + uint8_t sizebuf[4]; + sizebuf[0] = (fsize >> 24) & 0xFF; + sizebuf[1] = (fsize >> 16) & 0xFF; + sizebuf[2] = (fsize >> 8) & 0xFF; + sizebuf[3] = fsize & 0xFF; + rc = libusb_bulk_transfer(dh, 0x01, sizebuf, sizeof(sizebuf), NULL, 0); + if (rc) { + rc = libusb_err(rc, "cannot send size: %s", usb_id(dev)); + goto end; + } + + // Then send the file contents + printf("Sending %ld bytes\n", fsize); + int sent = 0; + while (sent < fsize) { + int actual_length; + rc = libusb_bulk_transfer(dh, 0x01, filebuf + sent, fsize - sent, &actual_length, 0); + if (rc) { + rc = libusb_err(rc, "cannot send data: %s", usb_id(dev)); + goto end; + } + sent += actual_length; + } + printf("Sent %d bytes\n", sent); + +end: + if(dh) { + libusb_release_interface(dh, 0); + libusb_close(dh); + } + if(dev) libusb_unref_device(dev); + return rc; +}