Initial version of a BSD-licensed beastpatcher utility for Gigabeat S installation.  Currently only compiles on Linux, but Windows and OS X support are planned.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@20083 a1c6a512-1295-4272-9138-f99709370657
diff --git a/utils/MTP/beastpatcher/Makefile b/utils/MTP/beastpatcher/Makefile
new file mode 100644
index 0000000..edd08b1
--- /dev/null
+++ b/utils/MTP/beastpatcher/Makefile
@@ -0,0 +1,52 @@
+CFLAGS=-Wall -W
+
+ifeq ($(findstring CYGWIN,$(shell uname)),CYGWIN)
+OUTPUT=beastpatcher.exe
+CROSS=
+else
+OUTPUT=beastpatcher
+CROSS=i586-mingw32msvc-
+endif
+
+ifeq ($(OUTPUT),beastpatcher)
+LIBS = /usr/lib/libmtp.a /usr/lib/libusb.a
+CFLAGS += $(shell printf \
+	'\#include <libmtp.h>\nlibmtp version: LIBMTP_VERSION\n' | \
+	gcc -E -P - -o - | grep -q '^libmtp version: 0\.2' && echo '-DOLDMTP')
+else
+CFLAGS+=-mno-cygwin
+LIBS = ../MTP_DLL.dll
+endif
+
+NATIVECC = gcc
+CC = $(CROSS)gcc
+
+all: $(OUTPUT)
+
+beastpatcher: beastpatcher.c bootimg.c mtp_common.h mtp_libmtp.c
+	gcc $(CFLAGS) -o beastpatcher beastpatcher.c bootimg.c mtp_libmtp.c $(LIBS)
+	strip beastpatcher
+
+beastpatcher.exe:  beastpatcher.c bootimg.c mtp_common.h mtp_win32.c $(LIBS)
+	$(CROSS)$(CC) $(CFLAGS) $(LIBS) -o beastpatcher.exe beastpatcher.c bootimg.c
+	$(CROSS)strip beastpatcher.exe
+
+beastpatcher-mac: beastpatcher-i386 beastpatcher-ppc
+	lipo -create beastpatcher-ppc beastpatcher-i386 -output beastpatcher-mac
+
+beastpatcher-i386:  beastpatcher.c  bootimg.c usb.h libusb-i386.a
+	$(CC) -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.4 -framework iokit -framework coreservices -arch i386 $(CFLAGS) -o beastpatcher-i386 beastpatcher.c bootimg.c -I. libusb-i386.a
+	strip beastpatcher-i386
+
+beastpatcher-ppc: beastpatcher.c bootimg.c usb.h libusb-ppc.a
+	$(CC) -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.4 -framework iokit -framework coreservices -arch ppc $(CFLAGS) -o beastpatcher-ppc beastpatcher.c bootimg.c -I. libusb-ppc.a
+	strip beastpatcher-ppc
+
+bin2c: ../../../rbutil/sansapatcher/bin2c.c
+	$(NATIVECC) $(CFLAGS) -o bin2c ../../../rbutil/sansapatcher/bin2c.c
+
+bootimg.c: bootloader.bin bin2c
+	./bin2c bootloader.bin bootimg
+
+clean:
+	rm -f beastpatcher.exe beastpatcher-mac beastpatcher-i386 beastpatcher-ppc beastpatcher bin2c bootimg.c bootimg.h *~
diff --git a/utils/MTP/beastpatcher/README b/utils/MTP/beastpatcher/README
new file mode 100644
index 0000000..85a0392
--- /dev/null
+++ b/utils/MTP/beastpatcher/README
@@ -0,0 +1,61 @@
+beastpatcher - a tool for installing the Rockbox bootloader on the Gigabeat S
+
+Unlike most other parts of the Rockbox project, this tool is
+distributed under the BSD license.  This is due to the fact that the
+Windows version links with the Microsoft MTP library.
+
+
+
+Building instructions - All OSes
+--------------------------------
+
+For all versions, you need to copy a "bootloader.bin" file (containing
+the Rockbox bootloader) into this directory.
+
+This can be built from the Rockbox source by selecting "41" and then
+"B" when running tools/configure.
+
+You need the Rockbox toolchain to build any Rockbox target binaries -
+this can be downloaded and built with the tools/rockboxdev.sh script.
+
+The latest officially released bootloader can always be downloaded from:
+
+http://download.rockbox.org/bootloader/gigabeat-s/
+
+
+
+Linux
+-----
+
+The Unix versions requires libmtp, which in turn requires libusb.
+
+beastpatcher is built to statically link to these libraries and
+expects them to exist as /usr/lib/libmtp.a and /usr/lib/libusb.a
+respectively.  Change the definition of LIBS in the Makefile if this
+is not the case for your system.
+
+After this, just type "make" to get a 
+
+
+
+OS X
+----
+
+[Not yet implemented]
+
+The OS X build is a universal binary statically linked with libusb and libmtp.
+
+
+
+Windows
+-------
+
+[Not yet implemented]
+
+The MTP_DLL.dll requires VC2005 to compile - see instructions in
+MTP_DLL/README
+
+To compile beastpatcher itself, you can either cross-compile from
+Linux using the mingw32 package, or compile in Cygwin.  Just type
+"make beastpatcher.exe" (in Linux) or "make" (in Cygwin).
+
diff --git a/utils/MTP/beastpatcher/TODO b/utils/MTP/beastpatcher/TODO
new file mode 100644
index 0000000..717a77e
--- /dev/null
+++ b/utils/MTP/beastpatcher/TODO
@@ -0,0 +1,17 @@
+Basic implementation:
+
+* Windows support - need to expand API provided by MTP_DLL.dll
+* OS X support - need to statically link against libusb and libmtp
+* Load bootloader.bin from a file
+
+Ideas for future features:
+
+* Dual-boot.  Look for nk.bin in current directory, and check its
+  md5sum to confirm it's an original firmware.  Possibly include
+  override for user-modified OFs, and option for user to specify an
+  alternate location.  
+
+  This will give the user three options - rockbox only, OF only
+  (i.e. uninstall) or dual-boot.  It would be easy to give the choice
+  of two boot orders (RB on hold or OF on hold) if that was desired.
+
diff --git a/utils/MTP/beastpatcher/beastpatcher.c b/utils/MTP/beastpatcher/beastpatcher.c
new file mode 100644
index 0000000..8043eba
--- /dev/null
+++ b/utils/MTP/beastpatcher/beastpatcher.c
@@ -0,0 +1,221 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ *
+ * $Id:$
+ *
+ * Copyright (c) 2009, Dave Chapman
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "mtp_common.h"
+#include "bootimg.h"
+
+#define VERSION "1.0 with v1 bootloader"
+
+void print_usage(void)
+{
+    fprintf(stderr,"Usage: beastpatcher [action]\n");
+    fprintf(stderr,"\n");
+    fprintf(stderr,"Where [action] is one of the following options:\n");
+    fprintf(stderr,"        --install (default)\n");
+    fprintf(stderr,"  -?,   --help\n");
+    fprintf(stderr,"\n");
+}
+
+/* Code to create a single-boot bootloader.
+   Based on tools/gigabeats.c by Will Robertson.  
+*/
+
+/* Entry point (and load address) for the main Rockbox bootloader */
+#define BL_ENTRY_POINT 0x8a000000
+
+static void put_uint32le(uint32_t x, unsigned char* p)
+{
+    p[0] = x & 0xff;
+    p[1] = (x >> 8) & 0xff;
+    p[2] = (x >> 16) & 0xff;
+    p[3] = (x >> 24) & 0xff;
+}
+
+static uint32_t calc_csum(const unsigned char* pb, int cb)
+{
+    uint32_t l = 0;
+    while (cb--)
+        l += *pb++;
+    return l;
+}
+
+static void create_single_boot(unsigned char* boot, int bootlen,
+                               unsigned char** fwbuf, int* fwsize)
+{
+    unsigned char* buf;
+
+    /* 15 bytes for header, 16 for signature bypass, 
+     * 12 for record header, size of bootloader, 12 for footer */
+    *fwsize = 15 + 16 + 12 + bootlen + 12;
+    *fwbuf = malloc(*fwsize);
+
+    if(buf == NULL) {
+        fprintf(stderr, "[ERR]  Cannot allocate memory.\n" );
+        *fwbuf = NULL;
+        *fwsize = 0;
+        return;
+    }
+
+    buf = *fwbuf;
+
+    /* Copy bootloader image. */
+    memcpy(buf + 43, boot, bootlen);
+
+    /* Step 2: Create the file header */
+    sprintf((char *)buf, "B000FF\n");
+    put_uint32le(0x88200000, buf+7);
+
+    /* If the value below is too small, the update will attempt to flash.
+     * Be careful when changing this (leaving it as is won't cause issues) */
+    put_uint32le(0xCC0CD8, buf+11); 
+
+    /* Step 3: Add the signature bypass record */
+    put_uint32le(0x88065A10, buf+15);
+    put_uint32le(4, buf+19);
+    put_uint32le(0xE3A00001, buf+27);
+    put_uint32le(calc_csum(buf+27,4), buf+23);
+
+    /* Step 4: Create a record for the actual code */
+    put_uint32le(BL_ENTRY_POINT, buf+31);
+    put_uint32le(bootlen, buf+35);
+    put_uint32le(calc_csum(buf + 43, bootlen), buf+39);
+
+    /* Step 5: Write the footer */
+    put_uint32le(0, buf+*fwsize-12);
+    put_uint32le(BL_ENTRY_POINT, buf+*fwsize-8);
+    put_uint32le(0, buf+*fwsize-4);
+
+    return;
+}
+
+int beastpatcher(int argc, char* argv[])
+{
+    char yesno[4];
+    unsigned char* fwbuf;
+    int fwsize;
+    struct mtp_info_t mtp_info;
+
+    (void)argv;
+
+    fprintf(stderr,"beastpatcher v" VERSION " - (C) 2009 by the Rockbox developers\n");
+    fprintf(stderr,"This is free software; see the source for copying conditions.  There is NO\n");
+    fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
+
+    /* No options are currently implemented, so just display help if any are 
+       provided. */
+
+    if (argc > 1) {
+        print_usage();
+        return 1;
+    }
+
+    if (mtp_init(&mtp_info) < 0) {
+        fprintf(stderr,"[ERR]  Can not init MTP\n");
+        return 1;
+    }
+
+    /* Scan for attached MTP devices. */
+    if (mtp_scan(&mtp_info) < 0)
+    {
+        fprintf(stderr,"[ERR]  No devices found\n");
+        return 1;
+    }
+
+    printf("[INFO] Found device \"%s - %s\"\n", mtp_info.manufacturer, 
+                                                mtp_info.modelname);
+    printf("[INFO] Device version: \"%s\"\n",mtp_info.version);
+
+
+    printf("\nEnter i to install the Rockbox bootloader or c to cancel and do nothing (i/c): ");
+
+    if (fgets(yesno,4,stdin))
+    {
+        if (yesno[0]=='i')
+        {
+            /* Create a single-boot bootloader from the embedded bootloader */
+            create_single_boot(bootimg, LEN_bootimg, &fwbuf, &fwsize);
+
+            if (fwbuf == NULL)
+                return 1;
+            
+            if (mtp_send_firmware(&mtp_info, fwbuf, fwsize) == 0)
+            {
+                fprintf(stderr,"[INFO] Bootloader installed successfully.\n");
+            } 
+            else
+            {
+                fprintf(stderr,"[ERR]  Bootloader install failed.\n");
+            }
+
+            /* We are now done with the firmware image */
+            free(fwbuf);
+        }
+        else
+        {
+            fprintf(stderr,"[INFO] Installation cancelled.\n");
+        }
+    }
+
+    mtp_finished(&mtp_info);
+
+    return 0;
+}
+
+
+int main(int argc, char* argv[])
+{
+    int res;
+    char yesno[4];
+
+    res = beastpatcher(argc, argv);
+
+    printf("\nPress ENTER to exit beastpatcher: ");
+    fgets(yesno,4,stdin);
+
+    return res;
+}
diff --git a/utils/MTP/beastpatcher/mtp_common.h b/utils/MTP/beastpatcher/mtp_common.h
new file mode 100644
index 0000000..2fb52a9
--- /dev/null
+++ b/utils/MTP/beastpatcher/mtp_common.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ *
+ * $Id:$
+ *
+ * Copyright (c) 2009, Dave Chapman
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#ifndef _MTP_COMMON_H
+#define _MTP_COMMON_H
+
+#ifdef __WIN32__
+#error Windows support not yet implemented
+#else
+#include "libmtp.h"
+#endif
+
+struct mtp_info_t
+{
+    /* Generic data */
+    char manufacturer[200];
+    char modelname[200];
+    char version[200];
+
+    /* OS-Specific data */
+#ifdef __WIN32__
+#else
+    LIBMTP_mtpdevice_t *device;
+#endif
+};
+
+/* Common functions for both libMTP and win32 */
+
+int mtp_init(struct mtp_info_t* mtp_info);
+int mtp_finished(struct mtp_info_t* mtp_info);
+int mtp_scan(struct mtp_info_t* mtp_info);
+int mtp_send_firmware(struct mtp_info_t* mtp_info, unsigned char* fwbuf,
+                      int fwsize);
+
+#endif /* !_MTP_COMMON_H */
diff --git a/utils/MTP/beastpatcher/mtp_libmtp.c b/utils/MTP/beastpatcher/mtp_libmtp.c
new file mode 100644
index 0000000..7e8579a
--- /dev/null
+++ b/utils/MTP/beastpatcher/mtp_libmtp.c
@@ -0,0 +1,174 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * 
+ * $Id:$
+ *
+ * Copyright (c) 2009, Dave Chapman
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include "libmtp.h"
+#include "mtp_common.h"
+
+int mtp_init(struct mtp_info_t* mtp_info)
+{
+    /* Fill the info struct with zeros - mainly for the strings */
+    memset(mtp_info, 0, sizeof(struct mtp_info_t));
+
+    LIBMTP_Init();
+
+    return 0;
+
+}
+
+int mtp_finished(struct mtp_info_t* mtp_info)
+{
+    LIBMTP_Release_Device(mtp_info->device);
+
+    return 0;
+}
+
+int mtp_scan(struct mtp_info_t* mtp_info)
+{
+    char* str;
+
+    mtp_info->device = LIBMTP_Get_First_Device();
+
+    if (mtp_info->device == NULL)
+    {
+        return -1;
+    } 
+    else 
+    {
+        /* NOTE: These strings are filled with zeros in mtp_init() */
+
+        if ((str = LIBMTP_Get_Manufacturername(mtp_info->device)))
+        {
+            strncpy(mtp_info->manufacturer, str, sizeof(mtp_info->manufacturer)-1);
+        }
+
+        if ((str = LIBMTP_Get_Modelname(mtp_info->device)))
+        {
+            strncpy(mtp_info->modelname, str, sizeof(mtp_info->modelname)-1);
+        }
+
+        if ((str = LIBMTP_Get_Deviceversion(mtp_info->device)))
+        {
+            strncpy(mtp_info->version, str, sizeof(mtp_info->version)-1);
+        }
+
+        return 0;
+    }
+}
+
+static int progress(uint64_t const sent, uint64_t const total,
+                    void const *const data)
+{
+    (void)data;
+
+    int percent = (sent * 100) / total;
+#ifdef __WIN32__
+    printf("Progress: %I64u of %I64u (%d%%)\r", sent, total, percent);
+#else
+    printf("Progress: %"PRIu64" of %"PRIu64" (%d%%)\r", sent, total, percent);
+#endif
+    fflush(stdout);
+    return 0;
+}
+
+
+int mtp_send_firmware(struct mtp_info_t* mtp_info, unsigned char* fwbuf,
+                      int fwsize)
+{
+    LIBMTP_file_t *genfile;
+    int ret;
+    size_t n;
+    FILE* fwfile;
+
+    /* Open a temporary file - this will be automatically deleted when closed */
+    fwfile = tmpfile();
+
+    if (fwfile == NULL)
+    {
+        fprintf(stderr,"[ERR]  Could not create temporary file.\n");
+        return -1;
+    }
+
+    n = fwrite(fwbuf, 1, fwsize, fwfile);
+    if ((int)n < fwsize)
+    {
+        fprintf(stderr,"[ERR]  Could not write to temporary file - n = %d.\n",(int)n);
+        fclose(fwfile);
+        return -1;
+    }
+
+    /* Reset file pointer */
+    fseek(fwfile, SEEK_SET, 0);
+
+    /* Prepare for uploading firmware */
+    genfile = LIBMTP_new_file_t();
+    genfile->filetype = LIBMTP_FILETYPE_FIRMWARE;
+    genfile->filename = strdup("nk.bin");
+    genfile->filesize = fwsize;
+
+#ifdef OLDMTP
+    ret = LIBMTP_Send_File_From_File_Descriptor(mtp_info->device, 
+            fileno(fwfile), genfile, progress, NULL, 0);
+#else
+    ret = LIBMTP_Send_File_From_File_Descriptor(mtp_info->device, 
+            fileno(fwfile), genfile, progress, NULL);
+#endif
+
+    /* Keep the progress line onscreen */
+    printf("\n");
+
+    /* NOTE: According to the docs, a value of ret != 0 means error, but libMTP
+       seems to return that even when successful.  So we can't check the return
+       code.
+    */
+
+    /* Cleanup */
+    LIBMTP_destroy_file_t(genfile);
+
+    /* Close the temporary file - this also deletes it. */
+    fclose(fwfile);
+
+    return 0;
+}