Add new Printer class USB bootloader.
[pub/USBasp.git] / Bootloaders / Printer / BootloaderPrinter.c
1 /*
2 LUFA Library
3 Copyright (C) Dean Camera, 2013.
4
5 dean [at] fourwalledcubicle [dot] com
6 www.lufa-lib.org
7 */
8
9 /*
10 Copyright 2013 Dean Camera (dean [at] fourwalledcubicle [dot] com)
11
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.
20
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
28 this software.
29 */
30
31 /** \file
32 *
33 * Main source file for the Printer class bootloader. This file contains the complete bootloader logic.
34 */
35
36 #include "BootloaderPrinter.h"
37
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.
40 */
41 struct
42 {
43 /** Current HEX parser state machine state. */
44 uint8_t ParserState;
45 /** Previously decoded numerical byte of data. */
46 uint8_t PrevData;
47 /** Currently decoded numerical byte of data. */
48 uint8_t 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)
51 * have been read.
52 */
53 bool ReadMSB;
54 /** Intel HEX record type of the current Intel HEX record. */
55 uint8_t RecordType;
56 /** Numerical bytes of data remaining to be read in the current record. */
57 uint8_t DataRem;
58 /** Checksum of the current record received so far. */
59 uint8_t Checksum;
60 /** Starting address of the last addressed FLASH page. */
61 uint32_t PageStartAddress;
62 /** Current 32-bit byte address in FLASH being targeted. */
63 uint32_t CurrAddress;
64 } HEXParser =
65 {
66 .ParserState = HEX_PARSE_STATE_WAIT_LINE
67 };
68
69 /** Indicates if there is data waiting to be written to a physical page of
70 * memory in FLASH.
71 */
72 static bool PageDirty = false;
73
74 /**
75 * Determines if a given input byte of data is an ASCII encoded HEX value.
76 *
77 * \note Input HEX bytes are expected to be in uppercase only.
78 *
79 * \param[in] Byte ASCII byte of data to check
80 *
81 * \return Boolean \c true if the input data is ASCII encoded HEX, false otherwise.
82 */
83 static bool IsHex(const char Byte)
84 {
85 return ((Byte >= 'A') && (Byte <= 'F')) ||
86 ((Byte >= '0') && (Byte <= '9'));
87 }
88
89 /**
90 * Converts a given input byte of data from an ASCII encoded HEX value to an integer value.
91 *
92 * \note Input HEX bytes are expected to be in uppercase only.
93 *
94 * \param[in] Byte ASCII byte of data to convert
95 *
96 * \return Integer converted value of the input ASCII encoded HEX byte of data.
97 */
98 static uint8_t HexToDecimal(const char Byte)
99 {
100 if ((Byte >= 'A') && (Byte <= 'F'))
101 return (10 + (Byte - 'A'));
102 else if ((Byte >= '0') && (Byte <= '9'))
103 return (Byte - '0');
104
105 return 0;
106 }
107
108 /**
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.
111 *
112 * \param[in] ReadCharacter Next input ASCII byte of data to parse
113 */
114 static void ParseIntelHEXByte(const char ReadCharacter)
115 {
116 if ((HEXParser.ParserState == HEX_PARSE_STATE_WAIT_LINE) || (ReadCharacter == ':'))
117 {
118 HEXParser.Checksum = 0;
119 HEXParser.CurrAddress &= ~0xFFFF;
120 HEXParser.ParserState = HEX_PARSE_STATE_WAIT_LINE;
121 HEXParser.ReadMSB = false;
122
123 if (ReadCharacter == ':')
124 HEXParser.ParserState = HEX_PARSE_STATE_BYTE_COUNT;
125
126 return;
127 }
128
129 if (!IsHex(ReadCharacter))
130 return;
131
132 HEXParser.Data = (HEXParser.Data << 4) | HexToDecimal(ReadCharacter);
133 HEXParser.ReadMSB = !HEXParser.ReadMSB;
134
135 if (HEXParser.ReadMSB)
136 return;
137
138 if (HEXParser.ParserState != HEX_PARSE_STATE_CHECKSUM)
139 HEXParser.Checksum += HEXParser.Data;
140
141 switch (HEXParser.ParserState)
142 {
143 case HEX_PARSE_STATE_BYTE_COUNT:
144 HEXParser.DataRem = HEXParser.Data;
145 HEXParser.ParserState = HEX_PARSE_STATE_ADDRESS_HIGH;
146 break;
147
148 case HEX_PARSE_STATE_ADDRESS_HIGH:
149 HEXParser.CurrAddress |= ((uint16_t)HEXParser.Data << 8);
150 HEXParser.ParserState = HEX_PARSE_STATE_ADDRESS_LOW;
151 break;
152
153 case HEX_PARSE_STATE_ADDRESS_LOW:
154 HEXParser.CurrAddress |= HEXParser.Data;
155 HEXParser.ParserState = HEX_PARSE_STATE_RECORD_TYPE;
156 break;
157
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);
161 break;
162
163 case HEX_PARSE_STATE_READ_DATA:
164 HEXParser.DataRem--;
165
166 if (HEXParser.DataRem & 0x01)
167 {
168 HEXParser.PrevData = HEXParser.Data;
169 break;
170 }
171
172 switch (HEXParser.RecordType)
173 {
174 case HEX_RECORD_TYPE_Data:
175 if (!(PageDirty))
176 {
177 boot_page_erase(HEXParser.PageStartAddress);
178 boot_spm_busy_wait();
179
180 PageDirty = true;
181 }
182
183 boot_page_fill(HEXParser.CurrAddress, ((uint16_t)HEXParser.Data << 8) | HEXParser.PrevData);
184 HEXParser.CurrAddress += 2;
185
186 uint32_t NewPageStartAddress = (HEXParser.CurrAddress & ~(SPM_PAGESIZE - 1));
187 if (PageDirty && (HEXParser.PageStartAddress != NewPageStartAddress))
188 {
189 boot_page_write(HEXParser.PageStartAddress);
190 boot_spm_busy_wait();
191
192 HEXParser.PageStartAddress = NewPageStartAddress;
193
194 PageDirty = false;
195 }
196 break;
197
198 case HEX_RECORD_TYPE_ExtendedLinearAddress:
199 HEXParser.CurrAddress |= (uint32_t)HEXParser.Data << (HEXParser.DataRem ? 24 : 16);
200 break;
201 }
202
203 if (!HEXParser.DataRem)
204 HEXParser.ParserState = HEX_PARSE_STATE_CHECKSUM;
205 break;
206
207 case HEX_PARSE_STATE_CHECKSUM:
208 if (HEXParser.Data != ((~HEXParser.Checksum + 1) & 0xFF))
209 break;
210
211 uint32_t NewPageStartAddress = (HEXParser.CurrAddress & ~(SPM_PAGESIZE - 1));
212 if (PageDirty && (HEXParser.PageStartAddress != NewPageStartAddress))
213 {
214 boot_page_write(HEXParser.PageStartAddress);
215 boot_spm_busy_wait();
216
217 HEXParser.PageStartAddress = NewPageStartAddress;
218
219 PageDirty = false;
220 }
221
222 break;
223
224 default:
225 HEXParser.ParserState = HEX_PARSE_STATE_WAIT_LINE;
226 break;
227 }
228 }
229
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.
232 */
233 int main(void)
234 {
235 SetupHardware();
236
237 LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);
238 GlobalInterruptEnable();
239
240 for (;;)
241 {
242 USB_USBTask();
243
244 Endpoint_SelectEndpoint(PRINTER_OUT_EPADDR);
245
246 /* Check if we have received new printer data from the host */
247 if (Endpoint_IsOUTReceived()) {
248 LEDs_ToggleLEDs(LEDMASK_USB_BUSY);
249
250 /* Read all bytes of data from the host and parse them */
251 while (Endpoint_IsReadWriteAllowed())
252 {
253 /* Feed the next byte of data to the HEX parser */
254 ParseIntelHEXByte(Endpoint_Read_8());
255 }
256
257 /* Send an ACK to the host, ready for the next data packet */
258 Endpoint_ClearOUT();
259
260 LEDs_SetAllLEDs(LEDMASK_USB_READY);
261 }
262 }
263 }
264
265 /** Configures the board hardware and chip peripherals for the demo's functionality. */
266 void SetupHardware(void)
267 {
268 /* Disable watchdog if enabled by bootloader/fuses */
269 MCUSR &= ~(1 << WDRF);
270 wdt_disable();
271
272 /* Disable clock division */
273 clock_prescale_set(clock_div_1);
274
275 /* Relocate the interrupt vector table to the bootloader section */
276 MCUCR = (1 << IVCE);
277 MCUCR = (1 << IVSEL);
278
279 /* Hardware Initialization */
280 LEDs_Init();
281 USB_Init();
282 }
283
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)
286 {
287 /* Indicate USB enumerating */
288 LEDs_SetAllLEDs(LEDMASK_USB_ENUMERATING);
289 }
290
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.
293 */
294 void EVENT_USB_Device_Disconnect(void)
295 {
296 /* Indicate USB not ready */
297 LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);
298 }
299
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.
302 */
303 void EVENT_USB_Device_ConfigurationChanged(void)
304 {
305 bool ConfigSuccess = true;
306
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);
310
311 /* Indicate endpoint configuration success or failure */
312 LEDs_SetAllLEDs(ConfigSuccess ? LEDMASK_USB_READY : LEDMASK_USB_ERROR);
313 }
314
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
317 * internally.
318 */
319 void EVENT_USB_Device_ControlRequest(void)
320 {
321 /* Process Printer specific control requests */
322 switch (USB_ControlRequest.bRequest)
323 {
324 case PRNT_REQ_GetDeviceID:
325 if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
326 {
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.
329 */
330 const char PrinterIDString[] =
331 "MFG:Generic;"
332 "MDL:Generic_/_Text_Only;"
333 "CMD:1284.4;"
334 "CLS:PRINTER";
335
336 Endpoint_ClearSETUP();
337 Endpoint_Write_16_BE(sizeof(PrinterIDString));
338 Endpoint_Write_Control_Stream_LE(PrinterIDString, strlen(PrinterIDString));
339 Endpoint_ClearStatusStage();
340 }
341
342 break;
343 case PRNT_REQ_GetPortStatus:
344 if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
345 {
346 Endpoint_ClearSETUP();
347 Endpoint_Write_8(PRNT_PORTSTATUS_NOTERROR | PRNT_PORTSTATUS_SELECT);
348 Endpoint_ClearStatusStage();
349 }
350
351 break;
352 case PRNT_REQ_SoftReset:
353 if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
354 {
355 Endpoint_ClearSETUP();
356 Endpoint_ClearStatusStage();
357 }
358
359 break;
360 }
361 }