X-Git-Url: http://git.linex4red.de/pub/USBasp.git/blobdiff_plain/8a43da6b37ad327833cfde6ae83611d6e53313d3..344cd1a2e82202112e34f840588bc9686d04c29d:/Bootloaders/Printer/BootloaderPrinter.c diff --git a/Bootloaders/Printer/BootloaderPrinter.c b/Bootloaders/Printer/BootloaderPrinter.c index a913b3330..e81646ba1 100644 --- a/Bootloaders/Printer/BootloaderPrinter.c +++ b/Bootloaders/Printer/BootloaderPrinter.c @@ -59,6 +59,8 @@ struct uint8_t Checksum; /** Starting address of the last addressed FLASH page. */ uint32_t PageStartAddress; + /** Current 32-bit byte extended base address in FLASH being targeted. */ + uint32_t CurrBaseAddress; /** Current 32-bit byte address in FLASH being targeted. */ uint32_t CurrAddress; } HEXParser = @@ -71,6 +73,37 @@ struct */ static bool PageDirty = false; + +/** Flag to indicate if the bootloader should be running, or should exit and allow the application code to run + * via a soft reset. When cleared, the bootloader will abort, the USB interface will shut down and the application + * started via a forced watchdog reset. + */ +static bool RunBootloader = true; + +/** Magic lock for forced application start. If the HWBE fuse is programmed and BOOTRST is unprogrammed, the bootloader + * will start if the /HWB line of the AVR is held low and the system is reset. However, if the /HWB line is still held + * low when the application attempts to start via a watchdog reset, the bootloader will re-start. If set to the value + * \ref MAGIC_BOOT_KEY the special init function \ref Application_Jump_Check() will force the application to start. + */ +uint16_t MagicBootKey ATTR_NO_INIT; + + +/** Special startup routine to check if the bootloader was started via a watchdog reset, and if the magic application + * start key has been loaded into \ref MagicBootKey. If the bootloader started via the watchdog and the key is valid, + * this will force the user application to start via a software jump. + */ +void Application_Jump_Check(void) +{ + /* If the reset source was the bootloader and the key is correct, clear it and jump to the application */ + if ((MCUSR & (1 << WDRF)) && (MagicBootKey == MAGIC_BOOT_KEY)) + { + MagicBootKey = 0; + + // cppcheck-suppress constStatement + ((void (*)(void))0x0000)(); + } +} + /** * Determines if a given input byte of data is an ASCII encoded HEX value. * @@ -78,7 +111,7 @@ static bool PageDirty = false; * * \param[in] Byte ASCII byte of data to check * - * \return Boolean \c true if the input data is ASCII encoded HEX, false otherwise. + * \return Boolean \c true if the input data is ASCII encoded HEX, \c false otherwise. */ static bool IsHex(const char Byte) { @@ -113,28 +146,36 @@ static uint8_t HexToDecimal(const char Byte) */ static void ParseIntelHEXByte(const char ReadCharacter) { + /* Reset the line parser while waiting for a new line to start */ if ((HEXParser.ParserState == HEX_PARSE_STATE_WAIT_LINE) || (ReadCharacter == ':')) { HEXParser.Checksum = 0; - HEXParser.CurrAddress &= ~0xFFFF; + HEXParser.CurrAddress = HEXParser.CurrBaseAddress; HEXParser.ParserState = HEX_PARSE_STATE_WAIT_LINE; HEXParser.ReadMSB = false; + /* ASCII ':' indicates the start of a new HEX record */ if (ReadCharacter == ':') HEXParser.ParserState = HEX_PARSE_STATE_BYTE_COUNT; return; } + /* Only allow ASCII HEX encoded digits, ignore all other characters */ if (!IsHex(ReadCharacter)) return; + /* Read and convert the next nibble of data from the current character */ HEXParser.Data = (HEXParser.Data << 4) | HexToDecimal(ReadCharacter); HEXParser.ReadMSB = !HEXParser.ReadMSB; + /* Only process further when a full byte (two nibbles) have been read */ if (HEXParser.ReadMSB) return; + /* Intel HEX checksum is for all fields except starting character and the + * checksum itself + */ if (HEXParser.ParserState != HEX_PARSE_STATE_CHECKSUM) HEXParser.Checksum += HEXParser.Data; @@ -146,12 +187,12 @@ static void ParseIntelHEXByte(const char ReadCharacter) break; case HEX_PARSE_STATE_ADDRESS_HIGH: - HEXParser.CurrAddress |= ((uint16_t)HEXParser.Data << 8); + HEXParser.CurrAddress += ((uint16_t)HEXParser.Data << 8); HEXParser.ParserState = HEX_PARSE_STATE_ADDRESS_LOW; break; case HEX_PARSE_STATE_ADDRESS_LOW: - HEXParser.CurrAddress |= HEXParser.Data; + HEXParser.CurrAddress += HEXParser.Data; HEXParser.ParserState = HEX_PARSE_STATE_RECORD_TYPE; break; @@ -161,17 +202,33 @@ static void ParseIntelHEXByte(const char ReadCharacter) break; case HEX_PARSE_STATE_READ_DATA: + /* Track the number of read data bytes in the record */ HEXParser.DataRem--; + /* Protect the bootloader against being written to */ + if (HEXParser.CurrAddress >= BOOT_START_ADDR) + { + HEXParser.ParserState = HEX_PARSE_STATE_WAIT_LINE; + PageDirty = false; + return; + } + + /* Wait for a machine word (two bytes) of data to be read */ if (HEXParser.DataRem & 0x01) { HEXParser.PrevData = HEXParser.Data; break; } + /* Convert the last two received data bytes into a 16-bit word */ + uint16_t NewDataWord = ((uint16_t)HEXParser.Data << 8) | HEXParser.PrevData; + switch (HEXParser.RecordType) { case HEX_RECORD_TYPE_Data: + /* If we are writing to a new page, we need to erase it + * first + */ if (!(PageDirty)) { boot_page_erase(HEXParser.PageStartAddress); @@ -180,9 +237,11 @@ static void ParseIntelHEXByte(const char ReadCharacter) PageDirty = true; } - boot_page_fill(HEXParser.CurrAddress, ((uint16_t)HEXParser.Data << 8) | HEXParser.PrevData); + /* Fill the FLASH memory buffer with the new word of data */ + boot_page_fill(HEXParser.CurrAddress, NewDataWord); HEXParser.CurrAddress += 2; + /* Flush the FLASH page to physical memory if we are crossing a page boundary */ uint32_t NewPageStartAddress = (HEXParser.CurrAddress & ~(SPM_PAGESIZE - 1)); if (PageDirty && (HEXParser.PageStartAddress != NewPageStartAddress)) { @@ -195,8 +254,14 @@ static void ParseIntelHEXByte(const char ReadCharacter) } break; + case HEX_RECORD_TYPE_ExtendedSegmentAddress: + /* Extended address data - store the upper 12-bits of the new address */ + HEXParser.CurrBaseAddress = ((uint32_t)NewDataWord << 4); + break; + case HEX_RECORD_TYPE_ExtendedLinearAddress: - HEXParser.CurrAddress |= (uint32_t)HEXParser.Data << (HEXParser.DataRem ? 24 : 16); + /* Extended address data - store the upper 16-bits of the new address */ + HEXParser.CurrBaseAddress = ((uint32_t)NewDataWord << 16); break; } @@ -205,9 +270,11 @@ static void ParseIntelHEXByte(const char ReadCharacter) break; case HEX_PARSE_STATE_CHECKSUM: + /* Verify checksum of the completed record */ if (HEXParser.Data != ((~HEXParser.Checksum + 1) & 0xFF)) break; + /* Flush the FLASH page to physical memory if we are crossing a page boundary */ uint32_t NewPageStartAddress = (HEXParser.CurrAddress & ~(SPM_PAGESIZE - 1)); if (PageDirty && (HEXParser.PageStartAddress != NewPageStartAddress)) { @@ -219,6 +286,10 @@ static void ParseIntelHEXByte(const char ReadCharacter) PageDirty = false; } + /* If end of the HEX file reached, the bootloader should exit at next opportunity */ + if (HEXParser.RecordType == HEX_RECORD_TYPE_EndOfFile) + RunBootloader = false; + break; default: @@ -237,7 +308,7 @@ int main(void) LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY); GlobalInterruptEnable(); - for (;;) + while (RunBootloader) { USB_USBTask(); @@ -260,10 +331,21 @@ int main(void) LEDs_SetAllLEDs(LEDMASK_USB_READY); } } + + /* Disconnect from the host - USB interface will be reset later along with the AVR */ + USB_Detach(); + + /* Unlock the forced application start mode of the bootloader if it is restarted */ + MagicBootKey = MAGIC_BOOT_KEY; + + /* Enable the watchdog and force a timeout to reset the AVR */ + wdt_enable(WDTO_250MS); + + for (;;); } /** Configures the board hardware and chip peripherals for the demo's functionality. */ -void SetupHardware(void) +static void SetupHardware(void) { /* Disable watchdog if enabled by bootloader/fuses */ MCUSR &= ~(1 << WDRF); @@ -279,6 +361,16 @@ void SetupHardware(void) /* Hardware Initialization */ LEDs_Init(); USB_Init(); + + /* Bootloader active LED toggle timer initialization */ + TIMSK1 = (1 << TOIE1); + TCCR1B = ((1 << CS11) | (1 << CS10)); +} + +/** ISR to periodically toggle the LEDs on the board to indicate that the bootloader is active. */ +ISR(TIMER1_OVF_vect, ISR_BLOCK) +{ + LEDs_ToggleLEDs(LEDS_LED1 | LEDS_LED2); } /** Event handler for the USB_Connect event. This indicates that the device is enumerating via the status LEDs. */ @@ -289,7 +381,7 @@ void EVENT_USB_Device_Connect(void) } /** Event handler for the USB_Disconnect event. This indicates that the device is no longer connected to a host via - * the status LEDs and stops the Mass Storage management task. + * the status LEDs and stops the Printer management task. */ void EVENT_USB_Device_Disconnect(void) { @@ -334,6 +426,13 @@ void EVENT_USB_Device_ControlRequest(void) "CLS:PRINTER"; Endpoint_ClearSETUP(); + + while (!(Endpoint_IsINReady())) + { + if (USB_DeviceState == DEVICE_STATE_Unattached) + return; + } + Endpoint_Write_16_BE(sizeof(PrinterIDString)); Endpoint_Write_Control_Stream_LE(PrinterIDString, strlen(PrinterIDString)); Endpoint_ClearStatusStage(); @@ -344,6 +443,13 @@ void EVENT_USB_Device_ControlRequest(void) if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE)) { Endpoint_ClearSETUP(); + + while (!(Endpoint_IsINReady())) + { + if (USB_DeviceState == DEVICE_STATE_Unattached) + return; + } + Endpoint_Write_8(PRNT_PORTSTATUS_NOTERROR | PRNT_PORTSTATUS_SELECT); Endpoint_ClearStatusStage(); }