3 Copyright (C) Dean Camera, 2013.
5 dean [at] fourwalledcubicle [dot] com
10 Copyright 2013 Dean Camera (dean [at] fourwalledcubicle [dot] com)
12 Permission to use, copy, modify, distribute, and sell this
13 software and its documentation for any purpose is hereby granted
14 without fee, provided that the above copyright notice appear in
15 all copies and that both that the copyright notice and this
16 permission notice and warranty disclaimer appear in supporting
17 documentation, and that the name of the author not be used in
18 advertising or publicity pertaining to distribution of the
19 software without specific, written prior permission.
21 The author disclaims all warranties with regard to this
22 software, including all implied warranties of merchantability
23 and fitness. In no event shall the author be liable for any
24 special, indirect or consequential damages or any damages
25 whatsoever resulting from loss of use, data or profits, whether
26 in an action of contract, negligence or other tortious action,
27 arising out of or in connection with the use or performance of
33 * Main source file for the Printer class bootloader. This file contains the complete bootloader logic.
36 #include "BootloaderPrinter.h"
38 /** Intel HEX parser state machine state information, to track the contents of
39 * a HEX file streamed in as a sequence of arbitrary bytes.
43 /** Current HEX parser state machine state. */
45 /** Previously decoded numerical byte of data. */
47 /** Currently decoded numerical byte of data. */
49 /** Indicates if both bytes that correspond to a single decoded numerical
50 * byte of data (HEX encodes values in ASCII HEX, two characters per byte)
54 /** Intel HEX record type of the current Intel HEX record. */
56 /** Numerical bytes of data remaining to be read in the current record. */
58 /** Checksum of the current record received so far. */
60 /** Starting address of the last addressed FLASH page. */
61 uint32_t PageStartAddress
;
62 /** Current 32-bit byte address in FLASH being targeted. */
66 .ParserState
= HEX_PARSE_STATE_WAIT_LINE
69 /** Indicates if there is data waiting to be written to a physical page of
72 static bool PageDirty
= false;
75 * Determines if a given input byte of data is an ASCII encoded HEX value.
77 * \note Input HEX bytes are expected to be in uppercase only.
79 * \param[in] Byte ASCII byte of data to check
81 * \return Boolean \c true if the input data is ASCII encoded HEX, false otherwise.
83 static bool IsHex(const char Byte
)
85 return ((Byte
>= 'A') && (Byte
<= 'F')) ||
86 ((Byte
>= '0') && (Byte
<= '9'));
90 * Converts a given input byte of data from an ASCII encoded HEX value to an integer value.
92 * \note Input HEX bytes are expected to be in uppercase only.
94 * \param[in] Byte ASCII byte of data to convert
96 * \return Integer converted value of the input ASCII encoded HEX byte of data.
98 static uint8_t HexToDecimal(const char Byte
)
100 if ((Byte
>= 'A') && (Byte
<= 'F'))
101 return (10 + (Byte
- 'A'));
102 else if ((Byte
>= '0') && (Byte
<= '9'))
109 * Parses an input Intel HEX formatted stream one character at a time, loading
110 * the data contents into the device's internal FLASH memory.
112 * \param[in] ReadCharacter Next input ASCII byte of data to parse
114 static void ParseIntelHEXByte(const char ReadCharacter
)
116 if ((HEXParser
.ParserState
== HEX_PARSE_STATE_WAIT_LINE
) || (ReadCharacter
== ':'))
118 HEXParser
.Checksum
= 0;
119 HEXParser
.CurrAddress
&= ~0xFFFF;
120 HEXParser
.ParserState
= HEX_PARSE_STATE_WAIT_LINE
;
121 HEXParser
.ReadMSB
= false;
123 if (ReadCharacter
== ':')
124 HEXParser
.ParserState
= HEX_PARSE_STATE_BYTE_COUNT
;
129 if (!IsHex(ReadCharacter
))
132 HEXParser
.Data
= (HEXParser
.Data
<< 4) | HexToDecimal(ReadCharacter
);
133 HEXParser
.ReadMSB
= !HEXParser
.ReadMSB
;
135 if (HEXParser
.ReadMSB
)
138 if (HEXParser
.ParserState
!= HEX_PARSE_STATE_CHECKSUM
)
139 HEXParser
.Checksum
+= HEXParser
.Data
;
141 switch (HEXParser
.ParserState
)
143 case HEX_PARSE_STATE_BYTE_COUNT
:
144 HEXParser
.DataRem
= HEXParser
.Data
;
145 HEXParser
.ParserState
= HEX_PARSE_STATE_ADDRESS_HIGH
;
148 case HEX_PARSE_STATE_ADDRESS_HIGH
:
149 HEXParser
.CurrAddress
|= ((uint16_t)HEXParser
.Data
<< 8);
150 HEXParser
.ParserState
= HEX_PARSE_STATE_ADDRESS_LOW
;
153 case HEX_PARSE_STATE_ADDRESS_LOW
:
154 HEXParser
.CurrAddress
|= HEXParser
.Data
;
155 HEXParser
.ParserState
= HEX_PARSE_STATE_RECORD_TYPE
;
158 case HEX_PARSE_STATE_RECORD_TYPE
:
159 HEXParser
.RecordType
= HEXParser
.Data
;
160 HEXParser
.ParserState
= (HEXParser
.DataRem ? HEX_PARSE_STATE_READ_DATA
: HEX_PARSE_STATE_CHECKSUM
);
163 case HEX_PARSE_STATE_READ_DATA
:
166 if (HEXParser
.DataRem
& 0x01)
168 HEXParser
.PrevData
= HEXParser
.Data
;
172 switch (HEXParser
.RecordType
)
174 case HEX_RECORD_TYPE_Data
:
177 boot_page_erase(HEXParser
.PageStartAddress
);
178 boot_spm_busy_wait();
183 boot_page_fill(HEXParser
.CurrAddress
, ((uint16_t)HEXParser
.Data
<< 8) | HEXParser
.PrevData
);
184 HEXParser
.CurrAddress
+= 2;
186 uint32_t NewPageStartAddress
= (HEXParser
.CurrAddress
& ~(SPM_PAGESIZE
- 1));
187 if (PageDirty
&& (HEXParser
.PageStartAddress
!= NewPageStartAddress
))
189 boot_page_write(HEXParser
.PageStartAddress
);
190 boot_spm_busy_wait();
192 HEXParser
.PageStartAddress
= NewPageStartAddress
;
198 case HEX_RECORD_TYPE_ExtendedLinearAddress
:
199 HEXParser
.CurrAddress
|= (uint32_t)HEXParser
.Data
<< (HEXParser
.DataRem ?
24 : 16);
203 if (!HEXParser
.DataRem
)
204 HEXParser
.ParserState
= HEX_PARSE_STATE_CHECKSUM
;
207 case HEX_PARSE_STATE_CHECKSUM
:
208 if (HEXParser
.Data
!= ((~HEXParser
.Checksum
+ 1) & 0xFF))
211 uint32_t NewPageStartAddress
= (HEXParser
.CurrAddress
& ~(SPM_PAGESIZE
- 1));
212 if (PageDirty
&& (HEXParser
.PageStartAddress
!= NewPageStartAddress
))
214 boot_page_write(HEXParser
.PageStartAddress
);
215 boot_spm_busy_wait();
217 HEXParser
.PageStartAddress
= NewPageStartAddress
;
225 HEXParser
.ParserState
= HEX_PARSE_STATE_WAIT_LINE
;
230 /** Main program entry point. This routine configures the hardware required by the application, then
231 * enters a loop to run the application tasks in sequence.
237 LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY
);
238 GlobalInterruptEnable();
244 Endpoint_SelectEndpoint(PRINTER_OUT_EPADDR
);
246 /* Check if we have received new printer data from the host */
247 if (Endpoint_IsOUTReceived()) {
248 LEDs_ToggleLEDs(LEDMASK_USB_BUSY
);
250 /* Read all bytes of data from the host and parse them */
251 while (Endpoint_IsReadWriteAllowed())
253 /* Feed the next byte of data to the HEX parser */
254 ParseIntelHEXByte(Endpoint_Read_8());
257 /* Send an ACK to the host, ready for the next data packet */
260 LEDs_SetAllLEDs(LEDMASK_USB_READY
);
265 /** Configures the board hardware and chip peripherals for the demo's functionality. */
266 void SetupHardware(void)
268 /* Disable watchdog if enabled by bootloader/fuses */
269 MCUSR
&= ~(1 << WDRF
);
272 /* Disable clock division */
273 clock_prescale_set(clock_div_1
);
275 /* Relocate the interrupt vector table to the bootloader section */
277 MCUCR
= (1 << IVSEL
);
279 /* Hardware Initialization */
284 /** Event handler for the USB_Connect event. This indicates that the device is enumerating via the status LEDs. */
285 void EVENT_USB_Device_Connect(void)
287 /* Indicate USB enumerating */
288 LEDs_SetAllLEDs(LEDMASK_USB_ENUMERATING
);
291 /** Event handler for the USB_Disconnect event. This indicates that the device is no longer connected to a host via
292 * the status LEDs and stops the Mass Storage management task.
294 void EVENT_USB_Device_Disconnect(void)
296 /* Indicate USB not ready */
297 LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY
);
300 /** Event handler for the USB_ConfigurationChanged event. This is fired when the host set the current configuration
301 * of the USB device after enumeration - the device endpoints are configured and the Mass Storage management task started.
303 void EVENT_USB_Device_ConfigurationChanged(void)
305 bool ConfigSuccess
= true;
307 /* Setup Printer Data Endpoints */
308 ConfigSuccess
&= Endpoint_ConfigureEndpoint(PRINTER_IN_EPADDR
, EP_TYPE_BULK
, PRINTER_IO_EPSIZE
, 1);
309 ConfigSuccess
&= Endpoint_ConfigureEndpoint(PRINTER_OUT_EPADDR
, EP_TYPE_BULK
, PRINTER_IO_EPSIZE
, 1);
311 /* Indicate endpoint configuration success or failure */
312 LEDs_SetAllLEDs(ConfigSuccess ? LEDMASK_USB_READY
: LEDMASK_USB_ERROR
);
315 /** Event handler for the USB_ControlRequest event. This is used to catch and process control requests sent to
316 * the device from the USB host before passing along unhandled control requests to the library for processing
319 void EVENT_USB_Device_ControlRequest(void)
321 /* Process Printer specific control requests */
322 switch (USB_ControlRequest
.bRequest
)
324 case PRNT_REQ_GetDeviceID
:
325 if (USB_ControlRequest
.bmRequestType
== (REQDIR_DEVICETOHOST
| REQTYPE_CLASS
| REQREC_INTERFACE
))
327 /* Generic printer IEEE 1284 identification string, will bind to an in-built driver on
328 * Windows systems, and will fall-back to a text-only printer driver on *nix.
330 const char PrinterIDString
[] =
332 "MDL:Generic_/_Text_Only;"
336 Endpoint_ClearSETUP();
337 Endpoint_Write_16_BE(sizeof(PrinterIDString
));
338 Endpoint_Write_Control_Stream_LE(PrinterIDString
, strlen(PrinterIDString
));
339 Endpoint_ClearStatusStage();
343 case PRNT_REQ_GetPortStatus
:
344 if (USB_ControlRequest
.bmRequestType
== (REQDIR_DEVICETOHOST
| REQTYPE_CLASS
| REQREC_INTERFACE
))
346 Endpoint_ClearSETUP();
347 Endpoint_Write_8(PRNT_PORTSTATUS_NOTERROR
| PRNT_PORTSTATUS_SELECT
);
348 Endpoint_ClearStatusStage();
352 case PRNT_REQ_SoftReset
:
353 if (USB_ControlRequest
.bmRequestType
== (REQDIR_HOSTTODEVICE
| REQTYPE_CLASS
| REQREC_INTERFACE
))
355 Endpoint_ClearSETUP();
356 Endpoint_ClearStatusStage();