diff --git a/src/teensy.c b/src/teensy.c index 05adf6dc..0ce124ed 100644 --- a/src/teensy.c +++ b/src/teensy.c @@ -16,26 +16,27 @@ * along with this program. If not, see . */ -// 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 +/* + * 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 - #include #include #include @@ -49,10 +50,9 @@ #include "usbdevs.h" #if defined(HAVE_LIBHIDAPI) - #include -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- #define TEENSY_VID 0x16C0 #define TEENSY_PID 0x0478 @@ -61,555 +61,502 @@ #define PDATA(pgm) ((struct pdata *)(pgm->cookie)) -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- 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; + hid_device *hid_handle; + uint16_t hid_usage; + // Extended parameters + bool wait_until_device_present; + int wait_timout; // 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; }; -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- -static void delay_ms(uint32_t duration) -{ - usleep(duration * 1000); +static void delay_ms(uint32_t duration) { + usleep(duration*1000); } static int teensy_get_bootloader_info(struct pdata *pdata, const 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. - pmsg_error("cannot detect board type (HID usage is 0)\n"); + 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. + pmsg_error("cannot detect board type (HID usage is 0)\n"); - AVRMEM* mem = avr_locate_flash(p); - if (mem == NULL) - { - pmsg_error("no flash memory defined for part %s\n", p->desc); - return -1; - } + AVRMEM *mem = avr_locate_flash(p); - pdata->board = "Unknown Board"; - pdata->flash_size = mem->size - (mem->size < 0x10000 ? 0x200 : 0x400); - pdata->page_size = mem->page_size; + if(mem == NULL) { + pmsg_error("no flash memory defined for part %s\n", p->desc); + return -1; + } - // Pass an invalid signature to require -F option. - pdata->sig_bytes[0] = 0x1E; - pdata->sig_bytes[1] = 0x00; - pdata->sig_bytes[2] = 0x00; - } - else - { - pmsg_error("Teensy board not supported (HID usage 0x%02X)\n", pdata->hid_usage); - 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 { + pmsg_error("Teensy board not supported (HID usage 0x%02X)\n", pdata->hid_usage); + return -1; } + } - return 0; + return 0; } static void teensy_dump_device_info(struct pdata *pdata) { - pmsg_notice("HID usage: 0x%02X\n", pdata->hid_usage); - pmsg_notice("Board: %s\n", pdata->board); - pmsg_notice("Available flash size: %u\n", pdata->flash_size); - pmsg_notice("Page size: %u\n", pdata->page_size); - pmsg_notice("Signature: 0x%02X%02X%02X\n", - pdata->sig_bytes[0], pdata->sig_bytes[1], pdata->sig_bytes[2]); + pmsg_notice("HID usage: 0x%02X\n", pdata->hid_usage); + pmsg_notice("Board: %s\n", pdata->board); + pmsg_notice("Available flash size: %u\n", pdata->flash_size); + pmsg_notice("Page size: %u\n", pdata->page_size); + pmsg_notice("Signature: 0x%02X%02X%02X\n", pdata->sig_bytes[0], pdata->sig_bytes[1], pdata->sig_bytes[2]); } -static int teensy_write_page(struct pdata *pdata, uint32_t address, const uint8_t *buffer, uint32_t size, bool suppress_warning) { - pmsg_debug("teensy_write_page(address=0x%06X, size=%d)\n", address, size); +static int teensy_write_page(struct pdata *pdata, uint32_t address, + const uint8_t *buffer, uint32_t size, bool suppress_warning) { - if (size > pdata->page_size) - { - pmsg_error("invalid page size: %u\n", pdata->page_size); - return -1; - } + pmsg_debug("teensy_write_page(address=0x%06X, size=%d)\n", address, size); - size_t report_size = 1 + 2 + (size_t)pdata->page_size; - uint8_t *report = (uint8_t *) mmt_malloc(report_size); + if(size > pdata->page_size) { + pmsg_error("invalid page size: %u\n", pdata->page_size); + 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); - } + size_t report_size = 1 + 2 + (size_t) pdata->page_size; + uint8_t *report = (uint8_t *) mmt_malloc(report_size); - if (size > 0) - { - memcpy(report + 1 + 2, buffer, size); - } + 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); + } - memset(report + 1 + 2 + size, 0xFF, report_size - (1 + 2 + size)); + if(size > 0) { + memcpy(report + 1 + 2, buffer, size); + } - int result = hid_write(pdata->hid_handle, report, report_size); - mmt_free(report); - if (result < 0) - { - if (!suppress_warning) - pmsg_error("unable to write page: %ls\n", hid_error(pdata->hid_handle)); + memset(report + 1 + 2 + size, 0xFF, report_size - (1 + 2 + size)); - return result; - } + int result = hid_write(pdata->hid_handle, report, report_size); - return 0; + mmt_free(report); + if(result < 0) { + if(!suppress_warning) + pmsg_error("unable to write page: %ls\n", hid_error(pdata->hid_handle)); + + return result; + } + + return 0; } static int teensy_erase_flash(struct pdata *pdata) { - pmsg_debug("teensy_erase_flash()\n"); + pmsg_debug("teensy_erase_flash()\n"); - // Write a dummy page at address 0 to explicitly erase the flash. - return teensy_write_page(pdata, 0, NULL, 0, false); + // Write a dummy page at address 0 to explicitly erase the flash. + return teensy_write_page(pdata, 0, NULL, 0, false); } static int teensy_reboot(struct pdata *pdata) { - pmsg_debug("teensy_reboot()\n"); + pmsg_debug("teensy_reboot()\n"); - // Write a dummy page at address -1 to reboot the Teensy. - return teensy_write_page(pdata, 0xFFFFFFFF, NULL, 0, true); + // Write a dummy page at address -1 to reboot the Teensy. + return teensy_write_page(pdata, 0xFFFFFFFF, NULL, 0, true); } -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- static void teensy_setup(PROGRAMMER *pgm) { - pmsg_debug("teensy_setup()\n"); - pgm->cookie = mmt_malloc(sizeof(struct pdata)); + pmsg_debug("teensy_setup()\n"); + pgm->cookie = mmt_malloc(sizeof(struct pdata)); } static void teensy_teardown(PROGRAMMER *pgm) { - pmsg_debug("teensy_teardown()\n"); - mmt_free(pgm->cookie); - pgm->cookie = NULL; + pmsg_debug("teensy_teardown()\n"); + mmt_free(pgm->cookie); + pgm->cookie = NULL; } static int teensy_initialize(const PROGRAMMER *pgm, const AVRPART *p) { - pmsg_debug("teensy_initialize()\n"); + pmsg_debug("teensy_initialize()\n"); - struct pdata *pdata = PDATA(pgm); + struct pdata *pdata = PDATA(pgm); - int result = teensy_get_bootloader_info(pdata, p); - if (result < 0) - return result; + int result = teensy_get_bootloader_info(pdata, p); - teensy_dump_device_info(pdata); + if(result < 0) + return result; - return 0; + teensy_dump_device_info(pdata); + + return 0; } static void teensy_display(const PROGRAMMER *pgm, const char *prefix) { - // pmsg_debug("teensy_display()\n"); + // pmsg_debug("teensy_display()\n"); } static void teensy_powerup(const PROGRAMMER *pgm) { - pmsg_debug("teensy_powerup()\n"); + pmsg_debug("teensy_powerup()\n"); } static void teensy_powerdown(const PROGRAMMER *pgm) { - pmsg_debug("teensy_powerdown()\n"); + pmsg_debug("teensy_powerdown()\n"); - struct pdata *pdata = PDATA(pgm); + struct pdata *pdata = PDATA(pgm); - if (pdata->erase_flash) - { - teensy_erase_flash(pdata); - pdata->erase_flash = false; - } + if(pdata->erase_flash) { + teensy_erase_flash(pdata); + pdata->erase_flash = false; + } - if (pdata->reboot) - { - teensy_reboot(pdata); - pdata->reboot = false; - } + if(pdata->reboot) { + teensy_reboot(pdata); + pdata->reboot = false; + } } -static void teensy_enable(PROGRAMMER* pgm, const AVRPART *p) { - pmsg_debug("teensy_enable()\n"); +static void teensy_enable(PROGRAMMER *pgm, const AVRPART *p) { + pmsg_debug("teensy_enable()\n"); } static void teensy_disable(const PROGRAMMER *pgm) { - pmsg_debug("teensy_disable()\n"); + pmsg_debug("teensy_disable()\n"); } static int teensy_program_enable(const PROGRAMMER *pgm, const AVRPART *p) { - pmsg_debug("teensy_program_enable()\n"); - return 0; + pmsg_debug("teensy_program_enable()\n"); + return 0; } static int teensy_read_sig_bytes(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem) { - pmsg_debug("teensy_read_sig_bytes()\n"); + pmsg_debug("teensy_read_sig_bytes()\n"); - if (mem->size < 3) - { - pmsg_error("memory size too small for read_sig_bytes\n"); - return -1; - } + if(mem->size < 3) { + pmsg_error("memory size too small for read_sig_bytes\n"); + return -1; + } - struct pdata *pdata = PDATA(pgm); - memcpy(mem->buf, pdata->sig_bytes, sizeof(pdata->sig_bytes)); + struct pdata *pdata = PDATA(pgm); - return 0; + memcpy(mem->buf, pdata->sig_bytes, sizeof(pdata->sig_bytes)); + + return 0; } static int teensy_chip_erase(const PROGRAMMER *pgm, const AVRPART *p) { - pmsg_debug("teensy_chip_erase()\n"); + pmsg_debug("teensy_chip_erase()\n"); - struct pdata *pdata = PDATA(pgm); + struct pdata *pdata = PDATA(pgm); - // Schedule a chip erase, either at first write or on powerdown. - pdata->erase_flash = true; + // Schedule a chip erase, either at first write or on powerdown. + pdata->erase_flash = true; - return 0; + return 0; } static int teensy_open(PROGRAMMER *pgm, const char *port) { - pmsg_debug("teensy_open(\"%s\")\n", port); + pmsg_debug("teensy_open(\"%s\")\n", port); - struct pdata *pdata = PDATA(pgm); - const char *bus_name = NULL; - char* dev_name = NULL; + struct pdata *pdata = PDATA(pgm); + const char *bus_name = NULL; + char *dev_name = NULL; - // if no -P was given or '-P usb' was given - if (str_eq(port, "usb")) - { - port = NULL; + // If no -P was given or '-P usb' was given + if(str_eq(port, "usb")) { + port = NULL; + } else { + // Calculate bus and device names from -P option + if(str_starts(port, "usb") && ':' == port[3]) { + bus_name = port + 4; + dev_name = strchr(bus_name, ':'); + if(dev_name != NULL) { + *dev_name = '\0'; + dev_name++; + } } - else - { - // calculate bus and device names from -P option - if (str_starts(port, "usb") && ':' == 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) { + pmsg_error("invalid -P %s; use -P usb:bus:device\n", port); + 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)) { + pmsg_error("using PID 0x%04x, ignoring remaining PIDs in list\n", 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) { + pmsg_error("found HID device, but hid_open_path() failed\n"); + } else { + pdata->hid_usage = device->usage; + break; } + } + + device = device->next; } - if (port != NULL && dev_name == NULL) - { - pmsg_error("invalid -P %s; use -P usb:bus:device\n", port); - return -1; - } + hid_free_enumeration(devices); - // 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)) - { - pmsg_error("using PID 0x%04x, ignoring remaining PIDs in list\n", 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) - { - pmsg_error("found HID device, but hid_open_path() failed\n"); - } - else - { - pdata->hid_usage = device->usage; - break; - } - } - - device = device->next; + if(pdata->hid_handle == NULL && pdata->wait_until_device_present) { + if(show_retry_message) { + if(pdata->wait_timout < 0) { + pmsg_error("no device found, waiting for device to be plugged in ...\n"); + } else { + pmsg_error("no device found, waiting %d seconds for device to be plugged in ...\n", pdata->wait_timout); } - hid_free_enumeration(devices); + pmsg_error("press CTRL-C to terminate\n"); + show_retry_message = false; + } - if (pdata->hid_handle == NULL && pdata->wait_until_device_present) - { - if (show_retry_message) - { - if (pdata->wait_timout < 0) - { - pmsg_error("no device found, waiting for device to be plugged in ...\n"); - } - else - { - pmsg_error("no device found, waiting %d seconds for device to be plugged in ...\n", - pdata->wait_timout); - } - - pmsg_error("press CTRL-C to terminate\n"); - 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->wait_timout < 0 || (time(NULL) - start_time) < pdata->wait_timout) { + delay_ms(TEENSY_CONNECT_WAIT); + continue; + } } - if (!pdata->hid_handle) - { - pmsg_error("cannot find device with Teensy bootloader (%04X:%04X)\n", vid, pid); - return -1; - } + break; + } - return 0; + if(!pdata->hid_handle) { + pmsg_error("cannot find device with Teensy bootloader (%04X:%04X)\n", vid, pid); + return -1; + } + + return 0; } -static void teensy_close(PROGRAMMER* pgm) -{ - pmsg_debug("teensy_close()\n"); +static void teensy_close(PROGRAMMER *pgm) { + pmsg_debug("teensy_close()\n"); - struct pdata *pdata = PDATA(pgm); - if (pdata->hid_handle != NULL) - { - hid_close(pdata->hid_handle); - pdata->hid_handle = NULL; - } + struct pdata *pdata = PDATA(pgm); + + if(pdata->hid_handle != NULL) { + hid_close(pdata->hid_handle); + pdata->hid_handle = NULL; + } } static int teensy_read_byte(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, - unsigned long addr, unsigned char* value) -{ - pmsg_debug("teensy_read_byte(desc=%s, addr=0x%04lX)\n", mem->desc, addr); + unsigned long addr, unsigned char *value) { + pmsg_debug("teensy_read_byte(desc=%s, addr=0x%04lX)\n", mem->desc, addr); - if (mem_is_a_fuse(mem) || mem_is_lock(mem)) - { - *value = 0xFF; - return 0; - } - else - { - pmsg_error("unsupported memory %s\n", mem->desc); - return -1; - } + if(mem_is_a_fuse(mem) || mem_is_lock(mem)) { + *value = 0xFF; + return 0; + } else { + pmsg_error("unsupported memory %s\n", mem->desc); + return -1; + } } static int teensy_write_byte(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, - unsigned long addr, unsigned char value) -{ - pmsg_debug("teensy_write_byte(desc=%s, addr=0x%04lX)\n", mem->desc, addr); - return -1; + unsigned long addr, unsigned char value) { + pmsg_debug("teensy_write_byte(desc=%s, addr=0x%04lX)\n", mem->desc, addr); + return -1; } static int teensy_paged_load(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, - unsigned int page_size, - unsigned int addr, unsigned int n_bytes) -{ - pmsg_debug("teensy_paged_load(page_size=0x%X, addr=0x%X, n_bytes=0x%X)\n", page_size, addr, n_bytes); - return -1; + unsigned int page_size, unsigned int addr, unsigned int n_bytes) { + pmsg_debug("teensy_paged_load(page_size=0x%X, addr=0x%X, n_bytes=0x%X)\n", page_size, addr, n_bytes); + return -1; } static int teensy_paged_write(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, - unsigned int page_size, - unsigned int addr, unsigned int n_bytes) -{ - pmsg_debug("teensy_paged_write(page_size=0x%X, addr=0x%X, n_bytes=0x%X)\n", page_size, addr, n_bytes); + unsigned int page_size, unsigned int addr, unsigned int n_bytes) { + pmsg_debug("teensy_paged_write(page_size=0x%X, addr=0x%X, n_bytes=0x%X)\n", page_size, addr, n_bytes); - if (mem_is_flash(mem)) - { - struct pdata *pdata = PDATA(pgm); + if(mem_is_flash(mem)) { + struct pdata *pdata = PDATA(pgm); - if (n_bytes > page_size) - { - pmsg_error("buffer size %u exceeds page size %u\n", n_bytes, page_size); - return -1; - } - - if (addr + n_bytes > pdata->flash_size) - { - pmsg_error("program size %u exceeds flash size %u\n", 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, false); - if (result < 0) - { - return result; - } - - // Schedule a reboot. - pdata->reboot = true; - - return result; + if(n_bytes > page_size) { + pmsg_error("buffer size %u exceeds page size %u\n", n_bytes, page_size); + return -1; } - else - { - pmsg_error("unsupported memory %s\n", mem->desc); - return -1; + + if(addr + n_bytes > pdata->flash_size) { + pmsg_error("program size %u exceeds flash size %u\n", 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, false); + + if(result < 0) { + return result; + } + // Schedule a reboot. + pdata->reboot = true; + + return result; + } else { + pmsg_error("unsupported memory %s\n", mem->desc); + return -1; + } } static int teensy_parseextparams(const PROGRAMMER *pgm, const LISTID xparams) { - int rv = 0; - bool help = false; - pmsg_debug("teensy_parseextparams()\n"); + int rv = 0; + bool help = false; - struct pdata *pdata = PDATA(pgm); - for (LNODEID node = lfirst(xparams); node; node = lnext(node)) - { - const char* extended_param = ldata(node); + pmsg_debug("teensy_parseextparams()\n"); - if (str_eq(extended_param, "wait")) - { - pdata->wait_until_device_present = true; - pdata->wait_timout = -1; - continue; - } + struct pdata *pdata = PDATA(pgm); - if (str_starts(extended_param, "wait=")) - { - pdata->wait_until_device_present = true; - pdata->wait_timout = atoi(extended_param + 5); - continue; - } + for(LNODEID node = lfirst(xparams); node; node = lnext(node)) { + const char *extended_param = ldata(node); - if (str_eq(extended_param, "help")) - { - help = true; - rv = LIBAVRDUDE_EXIT; - } - - if (!help) - { - pmsg_error("invalid extended parameter -x %s\n", extended_param); - rv = -1; - } - msg_error("%s -c %s extended options:\n", progname, pgmid); - msg_error(" -x wait Wait for the device to be plugged in if not connected\n"); - msg_error(" -x wait= Wait s for the device to be plugged in if not connected\n"); - msg_error(" -x help Show this help menu and exit\n"); - return rv; + if(str_eq(extended_param, "wait")) { + pdata->wait_until_device_present = true; + pdata->wait_timout = -1; + continue; } + if(str_starts(extended_param, "wait=")) { + pdata->wait_until_device_present = true; + pdata->wait_timout = atoi(extended_param + 5); + continue; + } + + if(str_eq(extended_param, "help")) { + help = true; + rv = LIBAVRDUDE_EXIT; + } + + if(!help) { + pmsg_error("invalid extended parameter -x %s\n", extended_param); + rv = -1; + } + msg_error("%s -c %s extended options:\n", progname, pgmid); + msg_error(" -x wait Wait for the device to be plugged in if not connected\n"); + msg_error(" -x wait= Wait s for the device to be plugged in if not connected\n"); + msg_error(" -x help Show this help menu and exit\n"); return rv; + } + + return rv; } void teensy_initpgm(PROGRAMMER *pgm) { - strcpy(pgm->type, "teensy"); + 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; + 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 */ +#else // ! HAVE_LIBHIDAPI // Give a proper error if we were not compiled with libhidapi static int teensy_nousb_open(PROGRAMMER *pgm, const char *name) { - pmsg_error("no HID support; please compile again with libhidapi installed\n"); - return -1; + pmsg_error("no HID support; please compile again with libhidapi installed\n"); + return -1; } void teensy_initpgm(PROGRAMMER *pgm) { - strcpy(pgm->type, "teensy"); - pgm->open = teensy_nousb_open; + strcpy(pgm->type, "teensy"); + pgm->open = teensy_nousb_open; } - -#endif /* HAVE_LIBHIDAPI */ +#endif // HAVE_LIBHIDAPI const char teensy_desc[] = "Teensy Bootloader";