diff --git a/reform2-trackball-fw/Mouse.c b/reform2-trackball-fw/Mouse.c
index e40f793da1a5c2771e54677fde5afdaf982704cb..c019812f7e4f634c9331c52980711116268081b6 100644
--- a/reform2-trackball-fw/Mouse.c
+++ b/reform2-trackball-fw/Mouse.c
@@ -96,6 +96,7 @@
 #define LiftCutoff_Tune2  0x65
 
 uint8_t adns_init_complete=0;
+uint8_t g_want_bootloader = 0;
 volatile int xydat[2];
 
 #include "Mouse.h"
@@ -320,6 +321,15 @@ bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDIn
   return false;
 }
 
+#define BOOTLOADER_START_ADDRESS ((0x8000 - 0x1000) >> 1)
+void jump_to_bootloader(void) {
+  ((void (*)(void))BOOTLOADER_START_ADDRESS)();
+}
+
+#define cmd(_s) (*(uint32_t *)(_s))
+#define CMD_SET_LEDS           cmd("LEDS")
+#define CMD_JUMP_TO_BOOTLOADER cmd("JTBL")
+
 /** HID class driver callback function for the processing of HID reports from the host.
  *
  *  \param[in] HIDInterfaceInfo  Pointer to the HID class interface configuration structure being referenced
@@ -334,12 +344,16 @@ void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDI
                                           const void* ReportData,
                                           const uint16_t ReportSize)
 {
-  if (ReportSize<4) return;
-  char* d = (char*)ReportData;
-  if (d[0]=='L' && d[1]=='E' && d[2]=='D' && d[3]=='S') {
+  if (ReportSize < 4) return;
+  const uint32_t command = *(uint32_t *)ReportData;
+  if (command == CMD_SET_LEDS) {
+    char* d = (char *)ReportData;
     uint8_t bits = ((d[4]=='1')<<4)|((d[5]=='1')<<3)|((d[6]=='1')<<2)|((d[7]=='1')<<1)|(d[8]=='1');
     led_set(bits);
   }
+  if (command == CMD_JUMP_TO_BOOTLOADER) {
+    g_want_bootloader = 1;
+  }
 }
 
 int main(void)
@@ -353,6 +367,7 @@ int main(void)
     HID_Device_USBTask(&Mouse_HID_Interface);
     USB_USBTask();
 
+    if (g_want_bootloader) jump_to_bootloader();
     if (!sensor_initialized) {
       init_sensor();
       sensor_initialized = 1;
diff --git a/reform2-trackball-fw/flash.sh b/reform2-trackball-fw/flash.sh
index 12705c3b64e0aa92378c0577a78d0cfbc091b8c5..5cdd979e5c2bb81844d4ab01ccef0aae8dcfd338 100755
--- a/reform2-trackball-fw/flash.sh
+++ b/reform2-trackball-fw/flash.sh
@@ -1,7 +1,9 @@
+#!/bin/bash
 
+# Kick trackball into bootloader mode, and wait a bit
+echo -ne 'xJTBL' > /dev/hidraw2
+sleep 2
 
 dfu-programmer atmega32u2 erase --suppress-bootloader-mem 
-
 dfu-programmer atmega32u2 flash ./Mouse.hex --suppress-bootloader-mem 
-
 dfu-programmer atmega32u2 start