diff --git a/bootloader/.gdbinit b/bootloader/.gdbinit
new file mode 100644
index 0000000000000000000000000000000000000000..85b92cf79c0f29f412a886e093c13da55dfc1bcc
--- /dev/null
+++ b/bootloader/.gdbinit
@@ -0,0 +1,3 @@
+file build/max32665.elf
+target remote localhost:3333
+
diff --git a/bootloader/Makefile b/bootloader/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..f4152c78930caa23348db0e5134b54c8002923b3
--- /dev/null
+++ b/bootloader/Makefile
@@ -0,0 +1,121 @@
+################################################################################
+ # Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved.
+ #
+ # Permission is hereby granted, free of charge, to any person obtaining a
+ # copy of this software and associated documentation files (the "Software"),
+ # to deal in the Software without restriction, including without limitation
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ # and/or sell copies of the Software, and to permit persons to whom the
+ # Software is furnished to do so, subject to the following conditions:
+ #
+ # The above copyright notice and this permission notice shall be included
+ # in all copies or substantial portions of the Software.
+ #
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ # IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+ # OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ # OTHER DEALINGS IN THE SOFTWARE.
+ #
+ # Except as contained in this notice, the name of Maxim Integrated
+ # Products, Inc. shall not be used except as stated in the Maxim Integrated
+ # Products, Inc. Branding Policy.
+ #
+ # The mere transfer of this software does not imply any licenses
+ # of trade secrets, proprietary technology, copyrights, patents,
+ # trademarks, maskwork rights, or any other form of intellectual
+ # property whatsoever. Maxim Integrated Products, Inc. retains all
+ # ownership rights.
+ #
+ # $Date: 2018-08-10 21:35:48 +0000 (Fri, 10 Aug 2018) $ 
+ # $Revision: 36863 $
+ #
+ ###############################################################################
+
+# This is the name of the build output file
+ifeq "$(PROJECT)" ""
+PROJECT=max32665
+endif
+
+# Specify the target processor
+ifeq "$(TARGET)" ""
+TARGET=MAX32665
+endif
+
+# Create Target name variables
+TARGET_UC:=$(shell echo $(TARGET) | tr a-z A-Z)
+TARGET_LC:=$(shell echo $(TARGET) | tr A-Z a-z)
+
+# Select 'GCC' or 'IAR' compiler
+COMPILER=GCC
+
+# Specify the board used
+ifeq "$(BOARD)" ""
+BOARD=EvKit_V1
+endif
+
+# This is the path to the CMSIS root directory
+ifeq "$(MAXIM_PATH)" ""
+LIBS_DIR=../sdk/Libraries
+else
+LIBS_DIR=/$(subst \,/,$(subst :,,$(MAXIM_PATH))/Firmware/$(TARGET_UC)/Libraries)
+endif
+CMSIS_ROOT=$(LIBS_DIR)/CMSIS
+
+# Source files for this test (add path to VPATH below)
+SRCS  = main.c
+SRCS += mscmem.c
+
+# Where to find source files for this test
+VPATH = .
+
+# Where to find header files for this test
+IPATH = .
+
+# Enable assertion checking for development
+PROJ_CFLAGS+=-DMXC_ASSERT_ENABLE
+
+# Specify the target revision to override default
+# "A2" in ASCII
+# TARGET_REV=0x4132
+
+# Use this variables to specify and alternate tool path
+#TOOL_DIR=/opt/gcc-arm-none-eabi-4_8-2013q4/bin
+
+# Use these variables to add project specific tool options
+#PROJ_CFLAGS+=--specs=nano.specs
+#PROJ_LDFLAGS+=--specs=nano.specs
+
+# Point this variable to a startup file to override the default file
+#STARTUPFILE=start.S
+
+# Set MXC_OPTIMIZE to override the default optimization level
+#MXC_OPTIMIZE_CFLAGS=-Og
+
+# Point this variable to a linker file to override the default file
+# LINKERFILE=$(CMSIS_ROOT)/Device/Maxim/$(TARGET_UC)/Source/GCC/$(TARGET_LC).ld
+
+################################################################################
+# Include external library makefiles here
+
+# Include the BSP
+BOARD_DIR=$(LIBS_DIR)/Boards/$(BOARD)
+include $(BOARD_DIR)/board.mk
+
+# Include the peripheral driver
+PERIPH_DRIVER_DIR=$(LIBS_DIR)/$(TARGET_UC)PeriphDriver
+include $(PERIPH_DRIVER_DIR)/periphdriver.mk
+
+MAXUSB_DIR=$(LIBS_DIR)/MAXUSB
+include $(MAXUSB_DIR)/maxusb.mk
+
+################################################################################
+# Include the rules for building for this target. All other makefiles should be
+# included before this one.
+include $(CMSIS_ROOT)/Device/Maxim/$(TARGET_UC)/Source/$(COMPILER)/$(TARGET_LC).mk
+
+# The rule to clean out all the build products.
+distclean: clean
+	$(MAKE) -C ${PERIPH_DRIVER_DIR} clean
diff --git a/bootloader/descriptors.h b/bootloader/descriptors.h
new file mode 100644
index 0000000000000000000000000000000000000000..c61b25a551eca30502bb7a5b4b0dcc7930422523
--- /dev/null
+++ b/bootloader/descriptors.h
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+ * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of Maxim Integrated
+ * Products, Inc. shall not be used except as stated in the Maxim Integrated
+ * Products, Inc. Branding Policy.
+ *
+ * The mere transfer of this software does not imply any licenses
+ * of trade secrets, proprietary technology, copyrights, patents,
+ * trademarks, maskwork rights, or any other form of intellectual
+ * property whatsoever. Maxim Integrated Products, Inc. retains all
+ * ownership rights.
+ *
+ * Description: Communications Device Class ACM (Serial Port) over USB
+ * $Id: descriptors.h 31172 2017-10-05 19:05:57Z zach.metzinger $
+ *
+ *******************************************************************************
+ */
+
+#ifndef _DESCRIPTORS_H_
+#define _DESCRIPTORS_H_
+
+#include <stdint.h>
+#include "usb.h"
+#include "hid_kbd.h"
+
+usb_device_descriptor_t __attribute__((aligned(4))) device_descriptor = {
+    0x12,         /* bLength = 18                     */
+    0x01,         /* bDescriptorType = Device         */
+    0x0110,       /* bcdUSB USB spec rev (BCD)        */
+    0x00,         /* bDeviceClass = mass storage (0)  */
+    0x00,         /* bDeviceSubClass                  */
+    0x00,         /* bDeviceProtocol                  */
+    0x40,         /* bMaxPacketSize0 is 64 bytes      */
+    0x0B6A,       /* idVendor (Maxim Integrated)      */
+    0x4402,       /* idProduct                        */
+    0x0100,       /* bcdDevice                        */
+    0x01,         /* iManufacturer Descriptor ID      */
+    0x02,         /* iProduct Descriptor ID           */
+    0x03,         /* iSerialNumber = (0) No string    */
+    0x01          /* bNumConfigurations               */
+};
+
+__attribute__((aligned(4)))
+struct __attribute__((packed)) {
+    usb_configuration_descriptor_t  config_descriptor;
+    usb_interface_descriptor_t      msc_interface_descriptor;
+    usb_endpoint_descriptor_t       endpoint_descriptor_1;
+    usb_endpoint_descriptor_t       endpoint_descriptor_2;
+} config_descriptor =
+{
+    {
+        0x09,       /*  bLength = 9                     */
+        0x02,       /*  bDescriptorType = Config (2)    */
+        0x0020,     /*  wTotalLength(L/H)               */
+        0x01,       /*  bNumInterfaces                  */
+        0x01,       /*  bConfigValue                    */
+        0x00,       /*  iConfiguration                  */
+        0xC0,       /*  bmAttributes (self-powered, no remote wakeup) */
+        0x01,       /*  MaxPower is 2ma (units are 2ma/bit) */
+    },
+    { /*  First Interface Descriptor For MSC Interface */
+        0x09,       /*  bLength = 9                     */
+        0x04,       /*  bDescriptorType = Interface (4) */
+        0x00,       /*  bInterfaceNumber                */
+        0x00,       /*  bAlternateSetting               */
+        0x02,       /*  bNumEndpoints (one for INm one for OUT)     */
+        0x08,       /*  bInterfaceClass = Mass Storage (8) */
+        0x06,       /*  bInterfaceSubClass = SCSI Transparent Command Set */
+        0x50,       /*  bInterfaceProtocol = Bulk-Only Transport */
+        0x00,       /*  iInterface                      */
+    },
+    { /*  OUT Endpoint 1 (Descriptor #1) */
+        0x07,         /*  bLength                          */
+        0x05,         /*  bDescriptorType (Endpoint)       */
+        0x01,         /*  bEndpointAddress (EP1-OUT)       */
+        0x02,         /*  bmAttributes (bulk)              */
+        0x0040,       /*  wMaxPacketSize                   */
+        0x00,         /*  bInterval (N/A)                  */
+    },
+    { /*  IN Endpoint 2 (Descriptor #2) */
+        0x07,         /*  bLength                          */
+        0x05,         /*  bDescriptorType (Endpoint)       */
+        0x82,         /*  bEndpointAddress (EP2-IN)        */
+        0x02,         /*  bmAttributes (bulk)              */
+        0x0040,       /*  wMaxPacketSize                   */
+        0x00          /*  bInterval (N/A)                  */
+    }
+};
+
+__attribute__((aligned(4)))
+uint8_t lang_id_desc[] = {
+    0x04,         /* bLength */
+    0x03,         /* bDescriptorType */
+    0x09, 0x04    /* bString = wLANGID (see usb_20.pdf 9.6.7 String) */
+};
+
+__attribute__((aligned(4)))
+uint8_t mfg_id_desc[] = {
+    0x22,         /* bLength */
+    0x03,         /* bDescriptorType */
+    'M', 0,
+    'a', 0,
+    'x', 0,
+    'i', 0,
+    'm', 0,
+    ' ', 0,
+    'I', 0,
+    'n', 0,
+    't', 0,
+    'e', 0,
+    'g', 0,
+    'r', 0,
+    'a', 0,
+    't', 0,
+    'e', 0,
+    'd', 0,
+};
+
+__attribute__((aligned(4)))
+uint8_t prod_id_desc[] = {
+  0x38,                 /* bLength */
+  0x03,                 /* bDescriptorType */
+  'M', 0,
+  'A', 0,
+  'X', 0,
+  'U', 0,
+  'S', 0,
+  'B', 0,
+  ' ', 0,
+  'M', 0,
+  'a', 0,
+  's', 0,
+  's', 0,
+  ' ', 0,
+  'S', 0,
+  't', 0,
+  'o', 0,
+  'r', 0,
+  'a', 0,
+  'g', 0,
+  'e', 0,
+  ' ', 0,
+  'E', 0,
+  'x', 0,
+  'a', 0,
+  'm', 0,
+  'p', 0,
+  'l', 0,
+  'e', 0,
+};
+
+/* Not currently used (see device descriptor), but could be enabled if desired */
+__attribute__((aligned(4)))
+uint8_t serial_id_desc[] = {
+  26,                     /* bLength */
+  0x03,                   /* bDescriptorType */
+  '0',0,                              
+  '0',0,
+  '0',0,
+  '0',0,
+  '0',0,
+  '0',0,
+  '0',0,
+  '0',0,
+  '0',0,
+  '0',0,
+  '0',0,
+  '1',0
+};
+
+#endif /* _DESCRIPTORS_H_ */
diff --git a/bootloader/main.c b/bootloader/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..0c3250fe1e87c678db7662e95b1c280a6f9c0895
--- /dev/null
+++ b/bootloader/main.c
@@ -0,0 +1,340 @@
+/*******************************************************************************
+ * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+ * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of Maxim Integrated
+ * Products, Inc. shall not be used except as stated in the Maxim Integrated
+ * Products, Inc. Branding Policy.
+ *
+ * The mere transfer of this software does not imply any licenses
+ * of trade secrets, proprietary technology, copyrights, patents,
+ * trademarks, maskwork rights, or any other form of intellectual
+ * property whatsoever. Maxim Integrated Products, Inc. retains all
+ * ownership rights.
+ *
+ * $Id: main.c 31505 2017-10-20 21:16:29Z zach.metzinger $
+ *
+ *******************************************************************************
+ */
+
+/**
+ * @file    main.c
+ * @brief   USB Mass Storage Class example
+ * @details This project creates a mass storage device using either on-board RAM or 
+ *          external SPI flash memory.  Load the project, connect a cable from the PC
+ *          to the USB connector.  A new external drive should appear than can be read
+ *          and written.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include "mxc_config.h"
+#include "mxc_sys.h"
+#include "mxc_delay.h"
+#include "board.h"
+#include "led.h"
+#include "usb.h"
+#include "usb_event.h"
+#include "enumerate.h"
+#include "msc.h"
+#include "descriptors.h"
+#include "mscmem.h"
+
+
+/***** Definitions *****/
+#define EVENT_ENUM_COMP     MAXUSB_NUM_EVENTS
+#define EVENT_REMOTE_WAKE   (EVENT_ENUM_COMP + 1)
+
+#define BUFFER_SIZE  64
+
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+
+/***** Global Data *****/
+volatile int configured;
+volatile int suspended;
+volatile unsigned int event_flags;
+int remote_wake_en;
+
+/***** Function Prototypes *****/
+static int setconfig_callback(usb_setup_pkt *sud, void *cbdata);
+static int setfeature_callback(usb_setup_pkt *sud, void *cbdata);
+static int clrfeature_callback(usb_setup_pkt *sud, void *cbdata);
+static int event_callback(maxusb_event_t evt, void *data);
+static void usb_app_sleep(void);
+static void usb_app_wakeup(void);
+
+/***** File Scope Variables *****/
+
+/* This EP assignment must match the Configuration Descriptor */
+static const msc_cfg_t msc_cfg = {
+  1,                    /* EP OUT */
+  MXC_USBHS_MAX_PACKET, /* OUT max packet size */
+  2,                    /* EP IN */
+  MXC_USBHS_MAX_PACKET, /* IN max packet size */
+};
+
+static const msc_idstrings_t ids = {
+    "MAXIM",            /* Vendor string.  Maximum of 8 bytes */
+    "MSC Example",       /* Product string.  Maximum of 16 bytes */
+    "1.0"              /* Version string.  Maximum of 4 bytes */
+};
+
+/* Functions to control "disk" memory. See msc.h for definitions. */
+static const msc_mem_t mem = {
+    mscmem_init,
+    mscmem_start,
+    mscmem_stop,
+    mscmem_ready,
+    mscmem_size,
+    mscmem_read,
+    mscmem_write,
+};
+
+/* This callback is used to allow the driver to call part specific initialization functions. */
+int usb_startup_cb()
+{
+    const sys_cfg_usbhs_t sys_usbhs_cfg = NULL;
+    return SYS_USBHS_Init(&sys_usbhs_cfg);
+}
+
+int usb_shutdown_cb()
+{
+    return SYS_USBHS_Shutdown();
+}
+
+/* User-supplied function to delay usec micro-seconds */
+void delay_us(unsigned int usec)
+{
+    /* mxc_delay() takes unsigned long, so can't use it directly */
+    mxc_delay(usec);
+}
+
+
+/******************************************************************************/
+int main(void)
+{
+    maxusb_cfg_options_t usb_opts;
+  
+    printf("\n\n***** " TOSTRING(TARGET) " USB Mass Storage Example *****\n");
+    printf("Waiting for VBUS...\n");
+
+    /* Initialize state */
+    configured = 0;
+    suspended = 0;
+    event_flags = 0;
+    remote_wake_en = 0;
+
+    /* Start out in full speed */
+    usb_opts.enable_hs = 0;
+    usb_opts.delay_us = delay_us; /* Function which will be used for delays */
+    usb_opts.init_callback = usb_startup_cb;
+    usb_opts.shutdown_callback = usb_shutdown_cb;
+
+    /* Initialize the usb module */
+    if (usb_init(&usb_opts) != 0) {
+        printf("usb_init() failed\n");
+        while (1);
+    }
+
+    /* Initialize the enumeration module */
+    if (enum_init() != 0) {
+        printf("enum_init() failed\n");
+        while (1);
+    }
+
+    /* Register enumeration data */
+    enum_register_descriptor(ENUM_DESC_DEVICE, (uint8_t*)&device_descriptor, 0);
+    enum_register_descriptor(ENUM_DESC_CONFIG, (uint8_t*)&config_descriptor, 0);
+    enum_register_descriptor(ENUM_DESC_STRING, lang_id_desc, 0);
+    enum_register_descriptor(ENUM_DESC_STRING, mfg_id_desc, 1);
+    enum_register_descriptor(ENUM_DESC_STRING, prod_id_desc, 2);
+    enum_register_descriptor(ENUM_DESC_STRING, serial_id_desc, 3);
+
+    /* Handle configuration */
+    enum_register_callback(ENUM_SETCONFIG, setconfig_callback, NULL);
+
+    /* Handle feature set/clear */
+    enum_register_callback(ENUM_SETFEATURE, setfeature_callback, NULL);
+    enum_register_callback(ENUM_CLRFEATURE, clrfeature_callback, NULL);
+
+    /* Initialize the class driver */
+    if (msc_init(&config_descriptor.msc_interface_descriptor, &ids, &mem) != 0) {
+        printf("msc_init() failed\n");
+        while (1);
+    }
+
+    /* Register callbacks */
+    usb_event_enable(MAXUSB_EVENT_NOVBUS, event_callback, NULL);
+    usb_event_enable(MAXUSB_EVENT_VBUS, event_callback, NULL);
+
+    /* Start with USB in low power mode */
+    usb_app_sleep();
+    NVIC_EnableIRQ(USB_IRQn);
+
+    /* Wait for events */
+    while (1) {
+
+        if (suspended || !configured) {
+            LED_Off(0);
+        } else {
+            LED_On(0);
+        }
+
+        if (event_flags) {
+            /* Display events */
+            if (MXC_GETBIT(&event_flags, MAXUSB_EVENT_NOVBUS)) {
+                MXC_CLRBIT(&event_flags, MAXUSB_EVENT_NOVBUS);
+                printf("VBUS Disconnect\n");
+            } else if (MXC_GETBIT(&event_flags, MAXUSB_EVENT_VBUS)) {
+                MXC_CLRBIT(&event_flags, MAXUSB_EVENT_VBUS);
+                printf("VBUS Connect\n");
+            } else if (MXC_GETBIT(&event_flags, MAXUSB_EVENT_BRST)) {
+                MXC_CLRBIT(&event_flags, MAXUSB_EVENT_BRST);
+                printf("Bus Reset\n");
+            } else if (MXC_GETBIT(&event_flags, MAXUSB_EVENT_SUSP)) {
+                MXC_CLRBIT(&event_flags, MAXUSB_EVENT_SUSP);
+                printf("Suspended\n");
+            } else if (MXC_GETBIT(&event_flags, MAXUSB_EVENT_DPACT)) {
+                MXC_CLRBIT(&event_flags, MAXUSB_EVENT_DPACT);
+                printf("Resume\n");
+            } else if (MXC_GETBIT(&event_flags, EVENT_ENUM_COMP)) {
+                MXC_CLRBIT(&event_flags, EVENT_ENUM_COMP);
+                printf("Enumeration complete.\n");
+            } else if (MXC_GETBIT(&event_flags, EVENT_REMOTE_WAKE)) {
+                MXC_CLRBIT(&event_flags, EVENT_REMOTE_WAKE);
+                printf("Remote Wakeup\n");
+            }
+        }
+    }
+}
+
+/******************************************************************************/
+static int setconfig_callback(usb_setup_pkt *sud, void *cbdata)
+{
+    /* Confirm the configuration value */
+    if (sud->wValue == config_descriptor.config_descriptor.bConfigurationValue) {
+        configured = 1;
+        MXC_SETBIT(&event_flags, EVENT_ENUM_COMP);
+        return msc_configure(&msc_cfg); /* Configure the device class */
+    } else if (sud->wValue == 0) {
+        configured = 0;
+        return msc_deconfigure();
+    }
+
+    return -1;
+}
+
+/******************************************************************************/
+static int setfeature_callback(usb_setup_pkt *sud, void *cbdata)
+{
+    if(sud->wValue == FEAT_REMOTE_WAKE) {
+        remote_wake_en = 1;
+    } else {
+        // Unknown callback
+        return -1;
+    }
+
+    return 0;
+}
+
+/******************************************************************************/
+static int clrfeature_callback(usb_setup_pkt *sud, void *cbdata)
+{
+    if(sud->wValue == FEAT_REMOTE_WAKE) {
+        remote_wake_en = 0;
+    } else {
+        // Unknown callback
+        return -1;
+    }
+    
+    return 0;
+}
+
+/******************************************************************************/
+static void usb_app_sleep(void)
+{
+    /* TODO: Place low-power code here */
+    suspended = 1;
+}
+
+/******************************************************************************/
+static void usb_app_wakeup(void)
+{
+    /* TODO: Place low-power code here */
+    suspended = 0;
+}
+
+/******************************************************************************/
+static int event_callback(maxusb_event_t evt, void *data)
+{
+    /* Set event flag */
+    MXC_SETBIT(&event_flags, evt);
+
+    switch (evt) {
+        case MAXUSB_EVENT_NOVBUS:
+            usb_event_disable(MAXUSB_EVENT_BRST);
+            usb_event_disable(MAXUSB_EVENT_SUSP);
+            usb_event_disable(MAXUSB_EVENT_DPACT);
+            usb_disconnect();
+            configured = 0;
+            enum_clearconfig();
+            msc_deconfigure();
+            usb_app_sleep();
+            break;
+        case MAXUSB_EVENT_VBUS:
+            usb_event_clear(MAXUSB_EVENT_BRST);
+            usb_event_enable(MAXUSB_EVENT_BRST, event_callback, NULL);
+            usb_event_clear(MAXUSB_EVENT_SUSP);
+            usb_event_enable(MAXUSB_EVENT_SUSP, event_callback, NULL);
+            usb_connect();
+            usb_app_sleep();
+            break;
+        case MAXUSB_EVENT_BRST:
+            usb_app_wakeup();
+            enum_clearconfig();
+            msc_deconfigure();
+            configured = 0;
+            suspended = 0;
+            break;
+        case MAXUSB_EVENT_SUSP:
+            usb_app_sleep();
+            break;
+        case MAXUSB_EVENT_DPACT:
+            usb_app_wakeup();
+            break;
+        default:
+            break;
+    }
+
+    return 0;
+}
+
+/******************************************************************************/
+void USB_IRQHandler(void)
+{
+    usb_event_handler();
+}
+
+/******************************************************************************/
+void SysTick_Handler(void)
+{
+    mxc_delay_handler();
+}
diff --git a/bootloader/mscmem.c b/bootloader/mscmem.c
new file mode 100644
index 0000000000000000000000000000000000000000..0b56e0bd8857004061fcd5305b375314816c82fe
--- /dev/null
+++ b/bootloader/mscmem.c
@@ -0,0 +1,313 @@
+/*******************************************************************************
+ * Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+ * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of Maxim Integrated
+ * Products, Inc. shall not be used except as stated in the Maxim Integrated
+ * Products, Inc. Branding Policy.
+ *
+ * The mere transfer of this software does not imply any licenses
+ * of trade secrets, proprietary technology, copyrights, patents,
+ * trademarks, maskwork rights, or any other form of intellectual
+ * property whatsoever. Maxim Integrated Products, Inc. retains all
+ * ownership rights.
+ *
+ * Description: Communications Device Class ACM (Serial Port) over USB
+ * $Id: descriptors.h 31172 2017-10-05 19:05:57Z zach.metzinger $
+ *
+ *******************************************************************************
+ */
+
+/**
+ * @file    mscmem.h
+ * @brief   Memory routines used by the USB Mass Storage Class example.  
+ *          See the msc_mem_t structure in msc.h for function details.
+ * @details Functions are provided for using the internal RAM of the
+ *          device or the external SPI flash memory.  Use the SPIXF_DISK
+ *          and RAM_DISK defines to select the desired memory at compile
+ *          time.
+ */
+ 
+#include "mscmem.h"
+#include <string.h>
+#include <stdio.h>
+#include "mx25.h"
+
+/***** Definitions *****/
+
+#define SPIXF_DISK      1
+#define RAM_DISK        0
+
+#define LBA_SIZE                    512         /* Size of "logical blocks" in bytes */
+#define LBA_SIZE_SHIFT              9           /* The shift value used to convert between addresses and block numbers */
+
+/***** Global Data *****/
+
+/***** File Scope Variables *****/
+
+static int initialized = 0;
+static int running = 0;
+
+#if SPIXF_DISK
+
+#define MX25_BAUD                   5000000     /* SPI clock rate to communicate with the MX25 */
+
+#define MX25_SECTOR_SIZE            4096        /* Number of bytes in one sector of the MX25 */
+#define MX25_SECTOR_SIZE_SHIFT      12          /* The shift value used to convert between addresses and block numbers */
+#define MX25_NUM_SECTORS            2048        /* Total number of sectors in the MX25 */
+
+#define LBA_PER_SECTOR              (MX25_SECTOR_SIZE >> LBA_SIZE_SHIFT)           
+#define INVALID_SECTOR              MX25_NUM_SECTORS    /* Use a sector number past the end of memory to indicate invalid */
+
+/***** File Scope Variables *****/
+static uint32_t sectorNum = INVALID_SECTOR;
+static uint8_t sector[MX25_SECTOR_SIZE];
+static int sectorDirty = 0;
+
+/***** Function Prototypes *****/
+static uint32_t getSectorNum(uint32_t lba);
+static uint32_t getSectorAddr(uint32_t lba);
+static uint32_t getSector(uint32_t num);
+
+/******************************************************************************/
+static uint32_t getSectorNum(uint32_t lba)
+{
+    /* Absolute_address = lba * LBA_SIZE                    */
+    /* Sector_num = Absolute_address / MX25_SECTOR_SIZE     */
+    /* Sector_num = lba * 512 / 4096                        */
+    return lba >> (MX25_SECTOR_SIZE_SHIFT - LBA_SIZE_SHIFT);
+}
+
+/******************************************************************************/
+static uint32_t getSectorAddr(uint32_t lba)
+{
+    /* eight 512 byte blocks in each sector */
+    return (lba & (LBA_PER_SECTOR - 1)) << LBA_SIZE_SHIFT;
+}
+
+/******************************************************************************/
+static uint32_t getSector(uint32_t num)
+{
+    /* New sector requested? */
+    if(sectorNum != num) {
+        /* Is the current sector real? */
+        if(sectorNum != INVALID_SECTOR) {
+            /* Was it written to after it was read from memory? */
+            if(sectorDirty) {
+                /* Erase the old data. */
+                MX25_Erase(sectorNum << MX25_SECTOR_SIZE_SHIFT, MX25_Erase_4K);
+                /* Write the new */
+                MX25_Program_Page(sectorNum << MX25_SECTOR_SIZE_SHIFT, sector, MX25_SECTOR_SIZE, SPIXFC_WIDTH_4);
+                /* Mark data as clean */
+                sectorDirty = 0;
+            }
+        }
+    
+        /* Requesting a new valid sector? */
+        if(num != INVALID_SECTOR) {
+            MX25_Read(num << MX25_SECTOR_SIZE_SHIFT, sector, MX25_SECTOR_SIZE, SPIXFC_WIDTH_4);
+            sectorDirty = 0;
+            sectorNum = num;
+        }
+    }
+        
+    return 0;
+}
+
+/******************************************************************************/
+int mscmem_init()
+{
+   
+    if(!initialized) {
+        MX25_Init();
+        MX25_Reset();
+        MX25_Quad(1);
+        initialized = 1;
+    }
+    return 0;
+}
+
+/******************************************************************************/
+uint32_t mscmem_size(void)
+{
+    /* Get number of 512 byte chunks the MX25 contains. */
+    return (MX25_SECTOR_SIZE >> LBA_SIZE_SHIFT) * MX25_NUM_SECTORS;
+}
+
+/******************************************************************************/
+int mscmem_read(uint32_t lba, uint8_t* buffer)
+{
+    uint32_t addr;
+    
+    /* Convert to MX25 sector number. */
+    uint32_t sNum = getSectorNum(lba);
+    
+    if(getSector(sNum)) {
+        /* Failed to write/read from MX25 */
+        return 1;
+    }
+    
+    /* Get the offset into the current sector */
+    addr = getSectorAddr(lba);
+    
+    memcpy(buffer, sector + addr, LBA_SIZE);
+    
+    return 0;    
+}
+
+/******************************************************************************/
+int mscmem_write(uint32_t lba, uint8_t* buffer)
+{
+    uint32_t addr;
+    
+    /* Convert to MX25 sector number. */
+    uint32_t sNum = getSectorNum(lba);
+    
+    if(getSector(sNum)) {
+        /* Failed to write/read from MX25 */
+        return 1;
+    }
+    
+    /* Get the offset into the current sector */
+    addr = getSectorAddr(lba);
+    
+    memcpy(sector + addr, buffer, LBA_SIZE);
+    sectorDirty = 1;
+    
+    return 0;    
+}
+
+/******************************************************************************/
+int mscmem_start()
+{
+    /* Turn on the MX25 if it is not already. */
+    if(!initialized) {
+        mscmem_init();
+    }
+    
+    /* Check if the initialization succeeded. If it has, start running. */
+    if(initialized) {
+        running = 1;
+    }
+    
+    /* Start should return fail (non-zero) if the memory cannot be initialized. */
+    return !initialized;
+}
+
+/******************************************************************************/
+int mscmem_stop()
+{
+    /* TODO - could shut down XIPF interface here. */
+    
+    /* Flush the currently cached sector if necessary. */
+    if(getSector(INVALID_SECTOR)) {
+        return 1;
+    }
+    
+    running = 0;
+    return 0;
+}
+
+/******************************************************************************/
+int mscmem_ready()
+{
+    return running;
+}
+
+#elif RAM_DISK
+
+#define NUM_PAGES               0x100
+static uint8_t mem[NUM_PAGES][LBA_SIZE];
+
+/******************************************************************************/
+int mscmem_init()
+{
+    if(!initialized) {
+        initialized = 1;
+#if (ERASE_MEMORY_ON_INIT)
+        memset(mem, 0, sizeof(mem));
+#endif
+    }
+    return 0;
+}
+
+/******************************************************************************/
+uint32_t mscmem_size(void)
+{
+    return NUM_PAGES; 
+}
+
+/******************************************************************************/
+int mscmem_read(uint32_t lba, uint8_t* buffer)
+{
+    if(lba >= NUM_PAGES) {
+        return 1;
+    }
+    
+    memcpy(buffer, mem[lba], LBA_SIZE);
+    return 0;    
+}
+
+/******************************************************************************/
+int mscmem_write(uint32_t lba, uint8_t* buffer)
+{
+    if(lba >= NUM_PAGES) {
+        return 1;
+    }
+    
+    memcpy(mem[lba], buffer, LBA_SIZE);
+    return 0;
+}
+
+/******************************************************************************/
+int mscmem_start()
+{
+    /* Not much to do for this implementation.  The RAM is always ready. */
+    if(!initialized) {
+        mscmem_init();
+    }
+    
+    /* Check if the RAM has been initialized. If it has, start running. */
+    if(initialized) {
+        running = 1;
+    }
+    
+    /* Start should return fail (non-zero) if the memory cannot be initialized. */
+    return !initialized;
+}
+
+/******************************************************************************/
+int mscmem_stop()
+{
+    /* Nothing to do for this implementation.  All data is written as it is */
+    /*   received so there are no pending writes that need to be flushed.   */
+    running = 0;
+    return 0;
+}
+
+/******************************************************************************/
+int mscmem_ready()
+{
+    return running;
+}
+
+#else
+    #error "You must assign either RAM_DISK or SPIXF_DISK to 1."
+#endif
diff --git a/bootloader/mscmem.h b/bootloader/mscmem.h
new file mode 100644
index 0000000000000000000000000000000000000000..681e95e5d740092116c1362be227cfe14d8b57be
--- /dev/null
+++ b/bootloader/mscmem.h
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+ * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of Maxim Integrated
+ * Products, Inc. shall not be used except as stated in the Maxim Integrated
+ * Products, Inc. Branding Policy.
+ *
+ * The mere transfer of this software does not imply any licenses
+ * of trade secrets, proprietary technology, copyrights, patents,
+ * trademarks, maskwork rights, or any other form of intellectual
+ * property whatsoever. Maxim Integrated Products, Inc. retains all
+ * ownership rights.
+ *
+ * Description: Communications Device Class ACM (Serial Port) over USB
+ * $Id: descriptors.h 31172 2017-10-05 19:05:57Z zach.metzinger $
+ *
+ *******************************************************************************
+ */
+ 
+/**
+ * @file    mscmem.h
+ * @brief   Memory routines used by the USB Mass Storage Class example.  
+ *          See the msc_mem_t structure in msc.h for function details.
+ */
+ 
+#ifndef __MSC_MEM_H__
+#define __MSC_MEM_H__
+
+#include <stdint.h>
+
+#define ERASE_MEMORY_ON_INIT        1   /* Configuration option to clear the memory (to 0s) on initialization. */
+                                        /* Use 1 to clear or 0 to leave untouched. */
+
+int mscmem_init(void);
+int mscmem_start(void);
+int mscmem_stop(void);
+uint32_t mscmem_size(void);
+int mscmem_read(uint32_t lba, uint8_t* buffer);
+int mscmem_write(uint32_t lba, uint8_t* buffer);
+int mscmem_ready(void);
+
+#endif  /* __MSC_MEM_H__ */