X-Git-Url: http://git.linex4red.de/pub/USBasp.git/blobdiff_plain/0c40ef8897448c56709bd26fb766c6e30635eea9..95ba6527a5e764a430c3917b4e24e48699976fa9:/Bootloaders/Printer/BootloaderPrinter.c diff --git a/Bootloaders/Printer/BootloaderPrinter.c b/Bootloaders/Printer/BootloaderPrinter.c index 3a14fdce5..023ec3e93 100644 --- a/Bootloaders/Printer/BootloaderPrinter.c +++ b/Bootloaders/Printer/BootloaderPrinter.c @@ -35,6 +35,35 @@ #include "BootloaderPrinter.h" +/** LUFA Printer Class driver interface configuration and state information. This structure is + * passed to all Printer Class driver functions, so that multiple instances of the same class + * within a device can be differentiated from one another. + */ +USB_ClassInfo_PRNT_Device_t TextOnly_Printer_Interface = + { + .Config = + { + .InterfaceNumber = 0, + .DataINEndpoint = + { + .Address = PRINTER_IN_EPADDR, + .Size = PRINTER_IO_EPSIZE, + .Banks = 1, + }, + .DataOUTEndpoint = + { + .Address = PRINTER_OUT_EPADDR, + .Size = PRINTER_IO_EPSIZE, + .Banks = 1, + }, + .IEEE1284String = + "MFG:Generic;" + "MDL:Generic_/_Text_Only;" + "CMD:1284.4;" + "CLS:PRINTER", + }, + }; + /** Intel HEX parser state machine state information, to track the contents of * a HEX file streamed in as a sequence of arbitrary bytes. */ @@ -59,6 +88,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 +102,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 +140,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) { @@ -117,7 +179,7 @@ static void ParseIntelHEXByte(const char ReadCharacter) 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; @@ -154,12 +216,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; @@ -172,6 +234,14 @@ static void ParseIntelHEXByte(const char ReadCharacter) /* 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) { @@ -179,6 +249,9 @@ static void ParseIntelHEXByte(const char ReadCharacter) 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: @@ -194,7 +267,7 @@ static void ParseIntelHEXByte(const char ReadCharacter) } /* Fill the FLASH memory buffer with the new word of data */ - boot_page_fill(HEXParser.CurrAddress, ((uint16_t)HEXParser.Data << 8) | HEXParser.PrevData); + boot_page_fill(HEXParser.CurrAddress, NewDataWord); HEXParser.CurrAddress += 2; /* Flush the FLASH page to physical memory if we are crossing a page boundary */ @@ -210,9 +283,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: /* Extended address data - store the upper 16-bits of the new address */ - HEXParser.CurrAddress |= (uint32_t)HEXParser.Data << (HEXParser.DataRem ? 24 : 16); + HEXParser.CurrBaseAddress = ((uint32_t)NewDataWord << 16); break; } @@ -237,6 +315,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: @@ -255,33 +337,43 @@ int main(void) LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY); GlobalInterruptEnable(); - for (;;) + while (RunBootloader) { - USB_USBTask(); + uint8_t BytesReceived = PRNT_Device_BytesReceived(&TextOnly_Printer_Interface); - Endpoint_SelectEndpoint(PRINTER_OUT_EPADDR); + if (BytesReceived) + { + LEDs_SetAllLEDs(LEDMASK_USB_BUSY); - /* Check if we have received new printer data from the host */ - if (Endpoint_IsOUTReceived()) { - LEDs_ToggleLEDs(LEDMASK_USB_BUSY); - - /* Read all bytes of data from the host and parse them */ - while (Endpoint_IsReadWriteAllowed()) + while (BytesReceived--) { + int16_t ReceivedByte = PRNT_Device_ReceiveByte(&TextOnly_Printer_Interface); + /* Feed the next byte of data to the HEX parser */ - ParseIntelHEXByte(Endpoint_Read_8()); + ParseIntelHEXByte(ReceivedByte); } - /* Send an ACK to the host, ready for the next data packet */ - Endpoint_ClearOUT(); - LEDs_SetAllLEDs(LEDMASK_USB_READY); } + + PRNT_Device_USBTask(&TextOnly_Printer_Interface); + USB_USBTask(); } + + /* 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); @@ -317,7 +409,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) { @@ -333,8 +425,7 @@ void EVENT_USB_Device_ConfigurationChanged(void) bool ConfigSuccess = true; /* Setup Printer Data Endpoints */ - ConfigSuccess &= Endpoint_ConfigureEndpoint(PRINTER_IN_EPADDR, EP_TYPE_BULK, PRINTER_IO_EPSIZE, 1); - ConfigSuccess &= Endpoint_ConfigureEndpoint(PRINTER_OUT_EPADDR, EP_TYPE_BULK, PRINTER_IO_EPSIZE, 1); + ConfigSuccess &= PRNT_Device_ConfigureEndpoints(&TextOnly_Printer_Interface); /* Indicate endpoint configuration success or failure */ LEDs_SetAllLEDs(ConfigSuccess ? LEDMASK_USB_READY : LEDMASK_USB_ERROR); @@ -346,44 +437,5 @@ void EVENT_USB_Device_ConfigurationChanged(void) */ void EVENT_USB_Device_ControlRequest(void) { - /* Process Printer specific control requests */ - switch (USB_ControlRequest.bRequest) - { - case PRNT_REQ_GetDeviceID: - if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE)) - { - /* Generic printer IEEE 1284 identification string, will bind to an in-built driver on - * Windows systems, and will fall-back to a text-only printer driver on *nix. - */ - const char PrinterIDString[] = - "MFG:Generic;" - "MDL:Generic_/_Text_Only;" - "CMD:1284.4;" - "CLS:PRINTER"; - - Endpoint_ClearSETUP(); - Endpoint_Write_16_BE(sizeof(PrinterIDString)); - Endpoint_Write_Control_Stream_LE(PrinterIDString, strlen(PrinterIDString)); - Endpoint_ClearStatusStage(); - } - - break; - case PRNT_REQ_GetPortStatus: - if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE)) - { - Endpoint_ClearSETUP(); - Endpoint_Write_8(PRNT_PORTSTATUS_NOTERROR | PRNT_PORTSTATUS_SELECT); - Endpoint_ClearStatusStage(); - } - - break; - case PRNT_REQ_SoftReset: - if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE)) - { - Endpoint_ClearSETUP(); - Endpoint_ClearStatusStage(); - } - - break; - } + PRNT_Device_ProcessControlRequest(&TextOnly_Printer_Interface); }