diff --git Makefile.am Makefile.am
index 2b812d1..ba137e6 100644
--- Makefile.am
+++ Makefile.am
@@ -180,6 +180,8 @@ libavrdude_a_SOURCES = \
 	stk500v2_private.h \
 	stk500generic.c \
 	stk500generic.h \
+	teensy.c \
+	teensy.h \
 	tpi.h \
 	usbasp.c \
 	usbasp.h \
diff --git avrdude.1 avrdude.1
index 053505f..8001480 100644
--- avrdude.1
+++ avrdude.1
@@ -222,6 +222,15 @@ supported in UPDI mode. The Curiosity Nano board is dubbed
 thus the name
 .Pa pkobn_updi .
 .Pp
+The Teensy bootloader is supported for all AVR boards.
+As the bootloader does not support reading from flash memory,
+use the
+.Fl V
+option to prevent AVRDUDE from verifing the flash memory.
+See the section on
+.Em extended parameters
+for Teensy specific options.
+.Pp
 Input files can be provided, and output files can be written in
 different file formats, such as raw binary files containing the data
 to download to the chip, Intel hex format, or Motorola S-record
@@ -1073,6 +1082,16 @@ Especially in ascii mode this happens very often, so setting a smaller value
 can speed up programming a lot. 
 The default value is 100ms. Using 10ms might work in most cases. 
 .El
+.It Ar Teensy bootloader
+.Bl -tag -offset indent -width indent
+.It Ar wait[=<timeout>]
+If the device is not connected, wait for the device to be plugged in.
+The optional
+.Ar timeout
+specifies the connection time-out in seconds.
+If no time-out is specified, AVRDUDE will wait indefinitely until the
+device is plugged in.
+.El
 .It Ar Wiring
 When using the Wiring programmer type, the
 following optional extended parameter is accepted:
diff --git avrdude.conf.in avrdude.conf.in
index 5d1a701..5977fb0 100644
--- avrdude.conf.in
+++ avrdude.conf.in
@@ -919,6 +919,15 @@ programmer
   usbpid     = 0x6570;
 ;
 
+programmer
+  id = "teensy";
+  desc = "Teensy Bootloader";
+  type = "teensy";
+  connection_type = usb;
+  usbvid = 0x16C0;
+  usbpid = 0x0478;
+;
+
 programmer
   id    = "butterfly";
   desc  = "Atmel Butterfly Development Board";
diff --git doc/avrdude.texi doc/avrdude.texi
index be64583..523fd2f 100644
--- doc/avrdude.texi
+++ doc/avrdude.texi
@@ -307,6 +307,12 @@ The MPLAB(R) PICkit 4, MPLAB(R) SNAP, and Curiosity Nano boards are
 supported in UPDI mode. The Curiosity Nano board is dubbed ``PICkit on
 Board'', thus the name @code{pkobn_updi}.
 
+The Teensy bootloader is supported for all AVR boards.
+As the bootloader does not support reading from flash memory,
+use the @code{-V} option to prevent AVRDUDE from verifing the flash memory.
+See the section on @emph{extended parameters}
+below for Teensy specific options.
+
 
 @menu
 * History::                     
@@ -949,6 +955,18 @@ The default value is 100ms. Using 10ms might work in most cases.
 
 @end table
 
+@item Teensy bootloader
+
+When using the Teensy programmer type, the
+following optional extended parameter is accepted:
+@table @code
+@item @samp{wait=@var{timeout}}
+If the device is not connected, wait for the device to be plugged in.
+The optional @var{timeout} specifies the connection time-out in seconds.
+If no time-out is specified, AVRDUDE will wait indefinitely until the
+device is plugged in.
+@end table
+
 @item Wiring
 
 When using the Wiring programmer type, the
diff --git pgm_type.c pgm_type.c
index 9f28f44..328e70a 100644
--- pgm_type.c
+++ pgm_type.c
@@ -48,6 +48,7 @@
 #include "stk500.h"
 #include "stk500generic.h"
 #include "stk500v2.h"
+#include "teensy.h"
 #include "usbasp.h"
 #include "usbtiny.h"
 #include "wiring.h"
@@ -95,6 +96,7 @@ const PROGRAMMER_TYPE programmers_types[] = {
         {"stk600", stk600_initpgm, stk600_desc},
         {"stk600hvsp", stk600hvsp_initpgm, stk600hvsp_desc},
         {"stk600pp", stk600pp_initpgm, stk600pp_desc},
+        {"teensy", teensy_initpgm, teensy_desc},
         {"usbasp", usbasp_initpgm, usbasp_desc},
         {"usbtiny", usbtiny_initpgm, usbtiny_desc},
         {"wiring", wiring_initpgm, wiring_desc},
diff --git teensy.c teensy.c
new file mode 100644
index 0000000..b6bfc77
--- /dev/null
+++ teensy.c
@@ -0,0 +1,641 @@
+/*
+ * avrdude - A Downloader/Uploader for AVR device programmers
+ * Copyright (C) 2020 Marius Greuel
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// Notes:
+// This file adds support for the HalfKay bootloader,
+// so you do no longer need the Teensy loader utility.
+//
+// This HalfKay bootloader is used on various PJRC Teensy boards,
+// such as Teensy 2.0 (ATmega32U4), Teensy++ 2.0 (AT90USB1286),
+// and the respective clones.
+// By default, it bootloader uses the VID/PID 16C0:0478 (VOTI).
+//
+// As the Teensy bootloader is optimized for size, it implements
+// writing to flash memory only. Since it does not support reading,
+// use the -V option to prevent avrdude from verifing the flash memory.
+// To have avrdude wait for the device to be connected, use the
+// extended option '-x wait'.
+//
+// Example:
+// avrdude -c teensy -p m32u4 -x wait -V -U flash:w:main.hex:i
+
+#include "ac_cfg.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include "avrdude.h"
+#include "teensy.h"
+#include "usbdevs.h"
+
+#if defined(HAVE_LIBHIDAPI)
+
+#include <hidapi/hidapi.h>
+
+//-----------------------------------------------------------------------------
+
+#define TEENSY_VID 0x16C0
+#define TEENSY_PID 0x0478
+
+#define TEENSY_CONNECT_WAIT 100
+
+#define PDATA(pgm) ((pdata_t*)(pgm->cookie))
+
+//-----------------------------------------------------------------------------
+
+typedef struct pdata
+{
+    hid_device* hid_handle;
+    uint16_t hid_usage;
+    // Extended parameters
+    bool wait_until_device_present;
+    int wait_timout;            // in seconds
+    // Bootloader info (from hid_usage)
+    const char* board;
+    uint32_t flash_size;
+    uint16_t page_size;
+    uint8_t sig_bytes[3];
+    // State
+    bool erase_flash;
+    bool reboot;
+} pdata_t;
+
+//-----------------------------------------------------------------------------
+
+static void delay_ms(uint32_t duration)
+{
+    usleep(duration * 1000);
+}
+
+static int teensy_get_bootloader_info(pdata_t* pdata, AVRPART* p)
+{
+    switch (pdata->hid_usage)
+    {
+    case 0x19:
+        pdata->board = "Teensy 1.0 (AT90USB162)";
+        pdata->flash_size = 0x4000 - 0x200;
+        pdata->page_size = 128;
+        pdata->sig_bytes[0] = 0x1E;
+        pdata->sig_bytes[1] = 0x94;
+        pdata->sig_bytes[2] = 0x82;
+        break;
+    case 0x1A:
+        pdata->board = "Teensy++ 1.0 (AT90USB646)";
+        pdata->flash_size = 0x10000 - 0x400;
+        pdata->page_size = 256;
+        pdata->sig_bytes[0] = 0x1E;
+        pdata->sig_bytes[1] = 0x96;
+        pdata->sig_bytes[2] = 0x82;
+        break;
+    case 0x1B:
+        pdata->board = "Teensy 2.0 (ATmega32U4)";
+        pdata->flash_size = 0x8000 - 0x200;
+        pdata->page_size = 128;
+        pdata->sig_bytes[0] = 0x1E;
+        pdata->sig_bytes[1] = 0x95;
+        pdata->sig_bytes[2] = 0x87;
+        break;
+    case 0x1C:
+        pdata->board = "Teensy++ 2.0 (AT90USB1286)";
+        pdata->flash_size = 0x20000 - 0x400;
+        pdata->page_size = 256;
+        pdata->sig_bytes[0] = 0x1E;
+        pdata->sig_bytes[1] = 0x97;
+        pdata->sig_bytes[2] = 0x82;
+        break;
+    default:
+        if (pdata->hid_usage == 0)
+        {
+            // On Linux, libhidapi does not seem to return the HID usage from the report descriptor.
+            // We try to infer the board from the part information, until somebody fixes libhidapi.
+            // To use this workaround, the -F option is required.
+            avrdude_message(MSG_INFO, "%s: WARNING: Cannot detect board type (HID usage is 0)\n", progname);
+
+            AVRMEM* mem = avr_locate_mem(p, "flash");
+            if (mem == NULL)
+            {
+                avrdude_message(MSG_INFO, "No flash memory for part %s\n", p->desc);
+                return -1;
+            }
+
+            pdata->board = "Unknown Board";
+            pdata->flash_size = mem->size - (mem->size < 0x10000 ? 0x200 : 0x400);
+            pdata->page_size = mem->page_size;
+
+            // Pass an invalid signature to require -F option.
+            pdata->sig_bytes[0] = 0x1E;
+            pdata->sig_bytes[1] = 0x00;
+            pdata->sig_bytes[2] = 0x00;
+        }
+        else
+        {
+            avrdude_message(MSG_INFO, "%s: ERROR: Teensy board not supported (HID usage 0x%02X)\n",
+                progname, pdata->hid_usage);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static void teensy_dump_device_info(pdata_t* pdata)
+{
+    avrdude_message(MSG_NOTICE, "%s: HID usage: 0x%02X\n", progname, pdata->hid_usage);
+    avrdude_message(MSG_NOTICE, "%s: Board: %s\n", progname, pdata->board);
+    avrdude_message(MSG_NOTICE, "%s: Available flash size: %u\n", progname, pdata->flash_size);
+    avrdude_message(MSG_NOTICE, "%s: Page size: %u\n", progname, pdata->page_size);
+    avrdude_message(MSG_NOTICE, "%s: Signature: 0x%02X%02X%02X\n", progname,
+        pdata->sig_bytes[0], pdata->sig_bytes[1], pdata->sig_bytes[2]);
+}
+
+static int teensy_write_page(pdata_t* pdata, uint32_t address, const uint8_t* buffer, uint32_t size)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_write_page(address=0x%06X, size=%d)\n", progname, address, size);
+
+    if (size > pdata->page_size)
+    {
+        avrdude_message(MSG_INFO, "%s: ERROR: Invalid page size: %u\n", progname, pdata->page_size);
+        return -1;
+    }
+
+    size_t report_size = 1 + 2 + (size_t)pdata->page_size;
+    uint8_t* report = (uint8_t*)malloc(report_size);
+    if (report == NULL)
+    {
+        avrdude_message(MSG_INFO, "%s: ERROR: Failed to allocate memory\n", progname);
+        return -1;
+    }
+
+    report[0] = 0; // report number
+    if (pdata->page_size <= 256 && pdata->flash_size < 0x10000)
+    {
+        report[1] = (uint8_t)(address >> 0);
+        report[2] = (uint8_t)(address >> 8);
+    }
+    else
+    {
+        report[1] = (uint8_t)(address >> 8);
+        report[2] = (uint8_t)(address >> 16);
+    }
+
+    if (size > 0)
+    {
+        memcpy(report + 1 + 2, buffer, size);
+    }
+
+    memset(report + 1 + 2 + size, 0xFF, report_size - (1 + 2 + size));
+
+    int result = hid_write(pdata->hid_handle, report, report_size);
+    free(report);
+    if (result < 0)
+    {
+        avrdude_message(MSG_INFO, "%s: WARNING: Failed to write page: %ls\n",
+            progname, hid_error(pdata->hid_handle));
+        return result;
+    }
+
+    return 0;
+}
+
+static int teensy_erase_flash(pdata_t* pdata)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_erase_flash()\n", progname);
+
+    // Write a dummy page at address 0 to explicitly erase the flash.
+    return teensy_write_page(pdata, 0, NULL, 0);
+}
+
+static int teensy_reboot(pdata_t* pdata)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_reboot()\n", progname);
+
+    // Write a dummy page at address -1 to reboot the Teensy.
+    return teensy_write_page(pdata, 0xFFFFFFFF, NULL, 0);
+}
+
+//-----------------------------------------------------------------------------
+
+static void teensy_setup(PROGRAMMER* pgm)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_setup()\n", progname);
+
+    if ((pgm->cookie = malloc(sizeof(pdata_t))) == NULL)
+    {
+        avrdude_message(MSG_INFO, "%s: ERROR: Failed to allocate memory\n", progname);
+        exit(1);
+    }
+
+    memset(pgm->cookie, 0, sizeof(pdata_t));
+}
+
+static void teensy_teardown(PROGRAMMER* pgm)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_teardown()\n", progname);
+    free(pgm->cookie);
+}
+
+static int teensy_initialize(PROGRAMMER* pgm, AVRPART* p)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_initialize()\n", progname);
+
+    pdata_t* pdata = PDATA(pgm);
+
+    int result = teensy_get_bootloader_info(pdata, p);
+    if (result < 0)
+        return result;
+
+    teensy_dump_device_info(pdata);
+
+    return 0;
+}
+
+static void teensy_display(PROGRAMMER* pgm, const char* prefix)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_display()\n", progname);
+}
+
+static void teensy_powerup(PROGRAMMER* pgm)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_powerup()\n", progname);
+}
+
+static void teensy_powerdown(PROGRAMMER* pgm)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_powerdown()\n", progname);
+
+    pdata_t* pdata = PDATA(pgm);
+
+    if (pdata->erase_flash)
+    {
+        teensy_erase_flash(pdata);
+        pdata->erase_flash = false;
+    }
+
+    if (pdata->reboot)
+    {
+        teensy_reboot(pdata);
+        pdata->reboot = false;
+    }
+}
+
+static void teensy_enable(PROGRAMMER* pgm)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_enable()\n", progname);
+}
+
+static void teensy_disable(PROGRAMMER* pgm)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_disable()\n", progname);
+}
+
+static int teensy_program_enable(PROGRAMMER* pgm, AVRPART* p)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_program_enable()\n", progname);
+    return 0;
+}
+
+static int teensy_read_sig_bytes(PROGRAMMER* pgm, AVRPART* p, AVRMEM* mem)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_read_sig_bytes()\n", progname);
+
+    if (mem->size < 3)
+    {
+        avrdude_message(MSG_INFO, "%s: memory size too small for read_sig_bytes\n", progname);
+        return -1;
+    }
+
+    pdata_t* pdata = PDATA(pgm);
+    memcpy(mem->buf, pdata->sig_bytes, sizeof(pdata->sig_bytes));
+
+    return 0;
+}
+
+static int teensy_chip_erase(PROGRAMMER* pgm, AVRPART* p)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_chip_erase()\n", progname);
+
+    pdata_t* pdata = PDATA(pgm);
+
+    // Schedule a chip erase, either at first write or on powerdown.
+    pdata->erase_flash = true;
+
+    return 0;
+}
+
+static int teensy_open(PROGRAMMER* pgm, char* port)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_open(\"%s\")\n", progname, port);
+
+    pdata_t* pdata = PDATA(pgm);
+    char* bus_name = NULL;
+    char* dev_name = NULL;
+
+    // if no -P was given or '-P usb' was given
+    if (strcmp(port, "usb") == 0)
+    {
+        port = NULL;
+    }
+    else
+    {
+        // calculate bus and device names from -P option
+        if (strncmp(port, "usb", 3) == 0 && ':' == port[3])
+        {
+            bus_name = port + 4;
+            dev_name = strchr(bus_name, ':');
+            if (dev_name != NULL)
+            {
+                *dev_name = '\0';
+                dev_name++;
+            }
+        }
+    }
+
+    if (port != NULL && dev_name == NULL)
+    {
+        avrdude_message(MSG_INFO, "%s: ERROR: Invalid -P value: '%s'\n", progname, port);
+        avrdude_message(MSG_INFO, "%sUse -P usb:bus:device\n", progbuf);
+        return -1;
+    }
+
+    // Determine VID/PID
+    int vid = pgm->usbvid ? pgm->usbvid : TEENSY_VID;
+    int pid = TEENSY_PID;
+
+    LNODEID usbpid = lfirst(pgm->usbpid);
+    if (usbpid != NULL)
+    {
+        pid = *(int*)(ldata(usbpid));
+        if (lnext(usbpid))
+        {
+            avrdude_message(MSG_INFO, "%s: WARNING: using PID 0x%04x, ignoring remaining PIDs in list\n",
+                progname, pid);
+        }
+    }
+
+    bool show_retry_message = true;
+
+    time_t start_time = time(NULL);
+    for (;;)
+    {
+        // Search for device
+        struct hid_device_info* devices = hid_enumerate(vid, pid);
+        struct hid_device_info* device = devices;
+
+        while (device)
+        {
+            if (device->vendor_id == vid && device->product_id == pid)
+            {
+                pdata->hid_handle = hid_open_path(device->path);
+                if (pdata->hid_handle == NULL)
+                {
+                    avrdude_message(MSG_INFO, "%s: ERROR: Found HID device, but hid_open_path() failed.\n", progname);
+                }
+                else
+                {
+                    pdata->hid_usage = device->usage;
+                    break;
+                }
+            }
+
+            device = device->next;
+        }
+
+        hid_free_enumeration(devices);
+
+        if (pdata->hid_handle == NULL && pdata->wait_until_device_present)
+        {
+            if (show_retry_message)
+            {
+                if (pdata->wait_timout < 0)
+                {
+                    avrdude_message(MSG_INFO, "%s: No device found, waiting for device to be plugged in...\n", progname);
+                }
+                else
+                {
+                    avrdude_message(MSG_INFO, "%s: No device found, waiting %d seconds for device to be plugged in...\n",
+                        progname,
+                        pdata->wait_timout);
+                }
+
+                avrdude_message(MSG_INFO, "%s: Press CTRL-C to terminate.\n", progname);
+                show_retry_message = false;
+            }
+
+            if (pdata->wait_timout < 0 || (time(NULL) - start_time) < pdata->wait_timout)
+            {
+                delay_ms(TEENSY_CONNECT_WAIT);
+                continue;
+            }
+        }
+
+        break;
+    }
+
+    if (!pdata->hid_handle)
+    {
+        avrdude_message(MSG_INFO, "%s: ERROR: Could not find device with Teensy bootloader (%04X:%04X)\n",
+            progname, vid, pid);
+        return -1;
+    }
+
+    return 0;
+}
+
+static void teensy_close(PROGRAMMER* pgm)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_close()\n", progname);
+
+    pdata_t* pdata = PDATA(pgm);
+    if (pdata->hid_handle != NULL)
+    {
+        hid_close(pdata->hid_handle);
+        pdata->hid_handle = NULL;
+    }
+}
+
+static int teensy_read_byte(PROGRAMMER* pgm, AVRPART* p, AVRMEM* mem,
+    unsigned long addr, unsigned char* value)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_read_byte(desc=%s, addr=0x%0X)\n",
+        progname, mem->desc, addr);
+
+    if (strcmp(mem->desc, "lfuse") == 0 ||
+        strcmp(mem->desc, "hfuse") == 0 ||
+        strcmp(mem->desc, "efuse") == 0 ||
+        strcmp(mem->desc, "lock") == 0)
+    {
+        *value = 0xFF;
+        return 0;
+    }
+    else
+    {
+        avrdude_message(MSG_INFO, "%s: Unsupported memory type: %s\n", progname, mem->desc);
+        return -1;
+    }
+}
+
+static int teensy_write_byte(PROGRAMMER* pgm, AVRPART* p, AVRMEM* mem,
+    unsigned long addr, unsigned char value)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_write_byte(desc=%s, addr=0x%0X)\n",
+        progname, mem->desc, addr);
+    return -1;
+}
+
+static int teensy_paged_load(PROGRAMMER* pgm, AVRPART* p, AVRMEM* mem,
+    unsigned int page_size,
+    unsigned int addr, unsigned int n_bytes)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_paged_load(page_size=0x%X, addr=0x%X, n_bytes=0x%X)\n",
+        progname, page_size, addr, n_bytes);
+    return -1;
+}
+
+static int teensy_paged_write(PROGRAMMER* pgm, AVRPART* p, AVRMEM* mem,
+    unsigned int page_size,
+    unsigned int addr, unsigned int n_bytes)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_paged_write(page_size=0x%X, addr=0x%X, n_bytes=0x%X)\n",
+        progname, page_size, addr, n_bytes);
+
+    if (strcmp(mem->desc, "flash") == 0)
+    {
+        pdata_t* pdata = PDATA(pgm);
+
+        if (n_bytes > page_size)
+        {
+            avrdude_message(MSG_INFO, "%s: Buffer size (%u) exceeds page size (%u)\n", progname, n_bytes, page_size);
+            return -1;
+        }
+
+        if (addr + n_bytes > pdata->flash_size)
+        {
+            avrdude_message(MSG_INFO, "%s: Program size (%u) exceeds flash size (%u)\n", progname, addr + n_bytes, pdata->flash_size);
+            return -1;
+        }
+
+        if (pdata->erase_flash)
+        {
+            // Writing page 0 will automatically erase the flash.
+            // If mem does not contain a page at address 0, write a dummy page at address 0.
+            if (addr != 0)
+            {
+                int result = teensy_erase_flash(pdata);
+                if (result < 0)
+                {
+                    return result;
+                }
+            }
+
+            pdata->erase_flash = false;
+        }
+
+        int result = teensy_write_page(pdata, addr, mem->buf + addr, n_bytes);
+        if (result < 0)
+        {
+            return result;
+        }
+
+        // Schedule a reboot.
+        pdata->reboot = true;
+
+        return result;
+    }
+    else
+    {
+        avrdude_message(MSG_INFO, "%s: Unsupported memory type: %s\n", progname, mem->desc);
+        return -1;
+    }
+}
+
+static int teensy_parseextparams(PROGRAMMER* pgm, LISTID xparams)
+{
+    avrdude_message(MSG_DEBUG, "%s: teensy_parseextparams()\n", progname);
+
+    pdata_t* pdata = PDATA(pgm);
+    for (LNODEID node = lfirst(xparams); node != NULL; node = lnext(node))
+    {
+        const char* param = ldata(node);
+
+        if (strcmp(param, "wait") == 0)
+        {
+            pdata->wait_until_device_present = true;
+            pdata->wait_timout = -1;
+        }
+        else if (strncmp(param, "wait=", 5) == 0)
+        {
+            pdata->wait_until_device_present = true;
+            pdata->wait_timout = atoi(param + 5);
+        }
+        else
+        {
+            avrdude_message(MSG_INFO, "%s: Invalid extended parameter '%s'\n", progname, param);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+void teensy_initpgm(PROGRAMMER* pgm)
+{
+    strcpy(pgm->type, "teensy");
+
+    pgm->setup = teensy_setup;
+    pgm->teardown = teensy_teardown;
+    pgm->initialize = teensy_initialize;
+    pgm->display = teensy_display;
+    pgm->powerup = teensy_powerup;
+    pgm->powerdown = teensy_powerdown;
+    pgm->enable = teensy_enable;
+    pgm->disable = teensy_disable;
+    pgm->program_enable = teensy_program_enable;
+    pgm->read_sig_bytes = teensy_read_sig_bytes;
+    pgm->chip_erase = teensy_chip_erase;
+    pgm->cmd = NULL;
+    pgm->open = teensy_open;
+    pgm->close = teensy_close;
+    pgm->read_byte = teensy_read_byte;
+    pgm->write_byte = teensy_write_byte;
+    pgm->paged_load = teensy_paged_load;
+    pgm->paged_write = teensy_paged_write;
+    pgm->parseextparams = teensy_parseextparams;
+}
+
+#else /* !HAVE_LIBHIDAPI */
+
+ // Give a proper error if we were not compiled with libhidapi
+static int teensy_nousb_open(struct programmer_t* pgm, char* name)
+{
+    avrdude_message(MSG_INFO, "%s: error: No HID support. Please compile again with libhidapi installed.\n", progname);
+    return -1;
+}
+
+void teensy_initpgm(PROGRAMMER* pgm)
+{
+    strcpy(pgm->type, "teensy");
+    pgm->open = teensy_nousb_open;
+}
+
+#endif /* HAVE_LIBHIDAPI */
+
+const char teensy_desc[] = "Teensy Bootloader";
diff --git teensy.h teensy.h
new file mode 100644
index 0000000..8cec458
--- /dev/null
+++ teensy.h
@@ -0,0 +1,35 @@
+/*
+ * avrdude - A Downloader/Uploader for AVR device programmers
+ * Copyright (C) 2020 Marius Greuel
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef teensy_h
+#define teensy_h
+
+#include "libavrdude.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const char teensy_desc[];
+void teensy_initpgm(PROGRAMMER* pgm);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* teensy_h */
