diff options
Diffstat (limited to 'Bootloaders/Printer/BootloaderPrinter.c')
-rw-r--r-- | Bootloaders/Printer/BootloaderPrinter.c | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/Bootloaders/Printer/BootloaderPrinter.c b/Bootloaders/Printer/BootloaderPrinter.c new file mode 100644 index 000000000..a913b3330 --- /dev/null +++ b/Bootloaders/Printer/BootloaderPrinter.c @@ -0,0 +1,361 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2013. + + dean [at] fourwalledcubicle [dot] com + www.lufa-lib.org +*/ + +/* + Copyright 2013 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaims all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * Main source file for the Printer class bootloader. This file contains the complete bootloader logic. + */ + +#include "BootloaderPrinter.h" + +/** Intel HEX parser state machine state information, to track the contents of + * a HEX file streamed in as a sequence of arbitrary bytes. + */ +struct +{ + /** Current HEX parser state machine state. */ + uint8_t ParserState; + /** Previously decoded numerical byte of data. */ + uint8_t PrevData; + /** Currently decoded numerical byte of data. */ + uint8_t Data; + /** Indicates if both bytes that correspond to a single decoded numerical + * byte of data (HEX encodes values in ASCII HEX, two characters per byte) + * have been read. + */ + bool ReadMSB; + /** Intel HEX record type of the current Intel HEX record. */ + uint8_t RecordType; + /** Numerical bytes of data remaining to be read in the current record. */ + uint8_t DataRem; + /** Checksum of the current record received so far. */ + uint8_t Checksum; + /** Starting address of the last addressed FLASH page. */ + uint32_t PageStartAddress; + /** Current 32-bit byte address in FLASH being targeted. */ + uint32_t CurrAddress; +} HEXParser = + { + .ParserState = HEX_PARSE_STATE_WAIT_LINE + }; + +/** Indicates if there is data waiting to be written to a physical page of + * memory in FLASH. + */ +static bool PageDirty = false; + +/** + * Determines if a given input byte of data is an ASCII encoded HEX value. + * + * \note Input HEX bytes are expected to be in uppercase only. + * + * \param[in] Byte ASCII byte of data to check + * + * \return Boolean \c true if the input data is ASCII encoded HEX, false otherwise. + */ +static bool IsHex(const char Byte) +{ + return ((Byte >= 'A') && (Byte <= 'F')) || + ((Byte >= '0') && (Byte <= '9')); +} + +/** + * Converts a given input byte of data from an ASCII encoded HEX value to an integer value. + * + * \note Input HEX bytes are expected to be in uppercase only. + * + * \param[in] Byte ASCII byte of data to convert + * + * \return Integer converted value of the input ASCII encoded HEX byte of data. + */ +static uint8_t HexToDecimal(const char Byte) +{ + if ((Byte >= 'A') && (Byte <= 'F')) + return (10 + (Byte - 'A')); + else if ((Byte >= '0') && (Byte <= '9')) + return (Byte - '0'); + + return 0; +} + +/** + * Parses an input Intel HEX formatted stream one character at a time, loading + * the data contents into the device's internal FLASH memory. + * + * \param[in] ReadCharacter Next input ASCII byte of data to parse + */ +static void ParseIntelHEXByte(const char ReadCharacter) +{ + if ((HEXParser.ParserState == HEX_PARSE_STATE_WAIT_LINE) || (ReadCharacter == ':')) + { + HEXParser.Checksum = 0; + HEXParser.CurrAddress &= ~0xFFFF; + HEXParser.ParserState = HEX_PARSE_STATE_WAIT_LINE; + HEXParser.ReadMSB = false; + + if (ReadCharacter == ':') + HEXParser.ParserState = HEX_PARSE_STATE_BYTE_COUNT; + + return; + } + + if (!IsHex(ReadCharacter)) + return; + + HEXParser.Data = (HEXParser.Data << 4) | HexToDecimal(ReadCharacter); + HEXParser.ReadMSB = !HEXParser.ReadMSB; + + if (HEXParser.ReadMSB) + return; + + if (HEXParser.ParserState != HEX_PARSE_STATE_CHECKSUM) + HEXParser.Checksum += HEXParser.Data; + + switch (HEXParser.ParserState) + { + case HEX_PARSE_STATE_BYTE_COUNT: + HEXParser.DataRem = HEXParser.Data; + HEXParser.ParserState = HEX_PARSE_STATE_ADDRESS_HIGH; + break; + + case HEX_PARSE_STATE_ADDRESS_HIGH: + 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.ParserState = HEX_PARSE_STATE_RECORD_TYPE; + break; + + case HEX_PARSE_STATE_RECORD_TYPE: + HEXParser.RecordType = HEXParser.Data; + HEXParser.ParserState = (HEXParser.DataRem ? HEX_PARSE_STATE_READ_DATA : HEX_PARSE_STATE_CHECKSUM); + break; + + case HEX_PARSE_STATE_READ_DATA: + HEXParser.DataRem--; + + if (HEXParser.DataRem & 0x01) + { + HEXParser.PrevData = HEXParser.Data; + break; + } + + switch (HEXParser.RecordType) + { + case HEX_RECORD_TYPE_Data: + if (!(PageDirty)) + { + boot_page_erase(HEXParser.PageStartAddress); + boot_spm_busy_wait(); + + PageDirty = true; + } + + boot_page_fill(HEXParser.CurrAddress, ((uint16_t)HEXParser.Data << 8) | HEXParser.PrevData); + HEXParser.CurrAddress += 2; + + uint32_t NewPageStartAddress = (HEXParser.CurrAddress & ~(SPM_PAGESIZE - 1)); + if (PageDirty && (HEXParser.PageStartAddress != NewPageStartAddress)) + { + boot_page_write(HEXParser.PageStartAddress); + boot_spm_busy_wait(); + + HEXParser.PageStartAddress = NewPageStartAddress; + + PageDirty = false; + } + break; + + case HEX_RECORD_TYPE_ExtendedLinearAddress: + HEXParser.CurrAddress |= (uint32_t)HEXParser.Data << (HEXParser.DataRem ? 24 : 16); + break; + } + + if (!HEXParser.DataRem) + HEXParser.ParserState = HEX_PARSE_STATE_CHECKSUM; + break; + + case HEX_PARSE_STATE_CHECKSUM: + if (HEXParser.Data != ((~HEXParser.Checksum + 1) & 0xFF)) + break; + + uint32_t NewPageStartAddress = (HEXParser.CurrAddress & ~(SPM_PAGESIZE - 1)); + if (PageDirty && (HEXParser.PageStartAddress != NewPageStartAddress)) + { + boot_page_write(HEXParser.PageStartAddress); + boot_spm_busy_wait(); + + HEXParser.PageStartAddress = NewPageStartAddress; + + PageDirty = false; + } + + break; + + default: + HEXParser.ParserState = HEX_PARSE_STATE_WAIT_LINE; + break; + } +} + +/** Main program entry point. This routine configures the hardware required by the application, then + * enters a loop to run the application tasks in sequence. + */ +int main(void) +{ + SetupHardware(); + + LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY); + GlobalInterruptEnable(); + + for (;;) + { + USB_USBTask(); + + Endpoint_SelectEndpoint(PRINTER_OUT_EPADDR); + + /* 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()) + { + /* Feed the next byte of data to the HEX parser */ + ParseIntelHEXByte(Endpoint_Read_8()); + } + + /* Send an ACK to the host, ready for the next data packet */ + Endpoint_ClearOUT(); + + LEDs_SetAllLEDs(LEDMASK_USB_READY); + } + } +} + +/** Configures the board hardware and chip peripherals for the demo's functionality. */ +void SetupHardware(void) +{ + /* Disable watchdog if enabled by bootloader/fuses */ + MCUSR &= ~(1 << WDRF); + wdt_disable(); + + /* Disable clock division */ + clock_prescale_set(clock_div_1); + + /* Relocate the interrupt vector table to the bootloader section */ + MCUCR = (1 << IVCE); + MCUCR = (1 << IVSEL); + + /* Hardware Initialization */ + LEDs_Init(); + USB_Init(); +} + +/** Event handler for the USB_Connect event. This indicates that the device is enumerating via the status LEDs. */ +void EVENT_USB_Device_Connect(void) +{ + /* Indicate USB enumerating */ + LEDs_SetAllLEDs(LEDMASK_USB_ENUMERATING); +} + +/** 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. + */ +void EVENT_USB_Device_Disconnect(void) +{ + /* Indicate USB not ready */ + LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY); +} + +/** Event handler for the USB_ConfigurationChanged event. This is fired when the host set the current configuration + * of the USB device after enumeration - the device endpoints are configured and the Mass Storage management task started. + */ +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); + + /* Indicate endpoint configuration success or failure */ + LEDs_SetAllLEDs(ConfigSuccess ? LEDMASK_USB_READY : LEDMASK_USB_ERROR); +} + +/** Event handler for the USB_ControlRequest event. This is used to catch and process control requests sent to + * the device from the USB host before passing along unhandled control requests to the library for processing + * internally. + */ +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; + } +} |