3 Copyright (C) Dean Camera, 2021.
5 dean [at] fourwalledcubicle [dot] com
10 Copyright 2021 Dean Camera (dean [at] fourwalledcubicle [dot] com)
12 Copyright 2019 Jacob September (jacobseptember [at] gmail [dot] com)
14 Permission to use, copy, modify, distribute, and sell this
15 software and its documentation for any purpose is hereby granted
16 without fee, provided that the above copyright notice appear in
17 all copies and that both that the copyright notice and this
18 permission notice and warranty disclaimer appear in supporting
19 documentation, and that the name of the author not be used in
20 advertising or publicity pertaining to distribution of the
21 software without specific, written prior permission.
23 The author disclaims all warranties with regard to this
24 software, including all implied warranties of merchantability
25 and fitness. In no event shall the author be liable for any
26 special, indirect or consequential damages or any damages
27 whatsoever resulting from loss of use, data or profits, whether
28 in an action of contract, negligence or other tortious action,
29 arising out of or in connection with the use or performance of
35 * ISP Protocol handler, to process V2 Protocol wrapped ISP commands used in Atmel programmer devices.
38 #include "ISPProtocol.h"
40 #if defined(ENABLE_ISP_PROTOCOL) || defined(__DOXYGEN__)
42 /* Half cycles of the OSCCAL calibration period remaining */
43 static volatile uint16_t ISPProtocol_HalfCyclesRemaining
;
45 /** Target device response I/O pin toggles remaining for successful OSCCAL calibration */
46 static volatile uint8_t ISPProtocol_ResponseTogglesRemaining
;
49 /** ISR to toggle MOSI pin when TIMER1 overflows */
50 ISR(TIMER1_OVF_vect
, ISR_BLOCK
)
52 PINB
|= (1 << PB2
); // toggle PB2 (MOSI) by writing 1 to its bit in PINB
53 ISPProtocol_HalfCyclesRemaining
--;
56 /** ISR to listen for toggles on MISO pin */
57 ISR(PCINT0_vect
, ISR_BLOCK
)
59 ISPProtocol_ResponseTogglesRemaining
--;
62 /** Handler for the CMD_ENTER_PROGMODE_ISP command, which attempts to enter programming mode on
63 * the attached device, returning success or failure back to the host.
65 void ISPProtocol_EnterISPMode(void)
70 uint8_t PinStabDelayMS
;
71 uint8_t ExecutionDelayMS
;
76 uint8_t EnterProgBytes
[4];
79 Endpoint_Read_Stream_LE(&Enter_ISP_Params
, sizeof(Enter_ISP_Params
), NULL
);
82 Endpoint_SelectEndpoint(AVRISP_DATA_IN_EPADDR
);
83 Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN
);
85 uint8_t ResponseStatus
= STATUS_CMD_FAILED
;
89 /* Perform execution delay, initialize SPI bus */
90 ISPProtocol_DelayMS(Enter_ISP_Params
.ExecutionDelayMS
);
91 ISPTarget_EnableTargetISP();
93 ISPTarget_ChangeTargetResetLine(true);
94 ISPProtocol_DelayMS(Enter_ISP_Params
.PinStabDelayMS
);
96 /* Continuously attempt to synchronize with the target until either the number of attempts specified
97 * by the host has exceeded, or the the device sends back the expected response values */
98 while (Enter_ISP_Params
.SynchLoops
-- && TimeoutTicksRemaining
)
100 uint8_t ResponseBytes
[4];
102 for (uint8_t RByte
= 0; RByte
< sizeof(ResponseBytes
); RByte
++)
104 ISPProtocol_DelayMS(Enter_ISP_Params
.ByteDelay
);
105 ResponseBytes
[RByte
] = ISPTarget_TransferByte(Enter_ISP_Params
.EnterProgBytes
[RByte
]);
108 /* Check if polling disabled, or if the polled value matches the expected value */
109 if (!(Enter_ISP_Params
.PollIndex
) || (ResponseBytes
[Enter_ISP_Params
.PollIndex
- 1] == Enter_ISP_Params
.PollValue
))
111 ResponseStatus
= STATUS_CMD_OK
;
116 ISPTarget_ChangeTargetResetLine(false);
117 ISPProtocol_DelayMS(Enter_ISP_Params
.PinStabDelayMS
);
118 ISPTarget_ChangeTargetResetLine(true);
119 ISPProtocol_DelayMS(Enter_ISP_Params
.PinStabDelayMS
);
123 Endpoint_Write_8(CMD_ENTER_PROGMODE_ISP
);
124 Endpoint_Write_8(ResponseStatus
);
128 /** Handler for the CMD_LEAVE_ISP command, which releases the target from programming mode. */
129 void ISPProtocol_LeaveISPMode(void)
137 Endpoint_Read_Stream_LE(&Leave_ISP_Params
, sizeof(Leave_ISP_Params
), NULL
);
140 Endpoint_SelectEndpoint(AVRISP_DATA_IN_EPADDR
);
141 Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN
);
143 /* Perform pre-exit delay, release the target /RESET, disable the SPI bus and perform the post-exit delay */
144 ISPProtocol_DelayMS(Leave_ISP_Params
.PreDelayMS
);
145 ISPTarget_ChangeTargetResetLine(false);
146 ISPTarget_DisableTargetISP();
147 ISPProtocol_DelayMS(Leave_ISP_Params
.PostDelayMS
);
149 Endpoint_Write_8(CMD_LEAVE_PROGMODE_ISP
);
150 Endpoint_Write_8(STATUS_CMD_OK
);
154 /** Handler for the CMD_PROGRAM_FLASH_ISP and CMD_PROGRAM_EEPROM_ISP commands, writing out bytes,
155 * words or pages of data to the attached device.
157 * \param[in] V2Command Issued V2 Protocol command byte from the host
159 void ISPProtocol_ProgramMemory(uint8_t V2Command
)
163 uint16_t BytesToWrite
;
164 uint8_t ProgrammingMode
;
166 uint8_t ProgrammingCommands
[3];
169 uint8_t ProgData
[256]; // Note, the Jungo driver has a very short ACK timeout period, need to buffer the
170 } Write_Memory_Params
; // whole page and ACK the packet as fast as possible to prevent it from aborting
172 Endpoint_Read_Stream_LE(&Write_Memory_Params
, (sizeof(Write_Memory_Params
) -
173 sizeof(Write_Memory_Params
.ProgData
)), NULL
);
174 Write_Memory_Params
.BytesToWrite
= SwapEndian_16(Write_Memory_Params
.BytesToWrite
);
176 if (Write_Memory_Params
.BytesToWrite
> sizeof(Write_Memory_Params
.ProgData
))
179 Endpoint_SelectEndpoint(AVRISP_DATA_IN_EPADDR
);
180 Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN
);
182 Endpoint_Write_8(V2Command
);
183 Endpoint_Write_8(STATUS_CMD_FAILED
);
188 Endpoint_Read_Stream_LE(&Write_Memory_Params
.ProgData
, Write_Memory_Params
.BytesToWrite
, NULL
);
190 // The driver will terminate transfers that are a round multiple of the endpoint bank in size with a ZLP, need
191 // to catch this and discard it before continuing on with packet processing to prevent communication issues
192 if (((sizeof(uint8_t) + sizeof(Write_Memory_Params
) - sizeof(Write_Memory_Params
.ProgData
)) +
193 Write_Memory_Params
.BytesToWrite
) % AVRISP_DATA_EPSIZE
== 0)
196 Endpoint_WaitUntilReady();
200 Endpoint_SelectEndpoint(AVRISP_DATA_IN_EPADDR
);
201 Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN
);
203 uint8_t ProgrammingStatus
= STATUS_CMD_OK
;
204 uint8_t PollValue
= (V2Command
== CMD_PROGRAM_FLASH_ISP
) ? Write_Memory_Params
.PollValue1
:
205 Write_Memory_Params
.PollValue2
;
206 uint16_t PollAddress
= 0;
207 uint8_t* NextWriteByte
= Write_Memory_Params
.ProgData
;
208 uint16_t PageStartAddress
= (CurrentAddress
& 0xFFFF);
210 for (uint16_t CurrentByte
= 0; CurrentByte
< Write_Memory_Params
.BytesToWrite
; CurrentByte
++)
212 uint8_t ByteToWrite
= *(NextWriteByte
++);
213 uint8_t ProgrammingMode
= Write_Memory_Params
.ProgrammingMode
;
215 /* Check to see if we need to send a LOAD EXTENDED ADDRESS command to the target */
216 if (MustLoadExtendedAddress
)
218 ISPTarget_LoadExtendedAddress();
219 MustLoadExtendedAddress
= false;
222 ISPTarget_SendByte(Write_Memory_Params
.ProgrammingCommands
[0]);
223 ISPTarget_SendByte(CurrentAddress
>> 8);
224 ISPTarget_SendByte(CurrentAddress
& 0xFF);
225 ISPTarget_SendByte(ByteToWrite
);
227 /* AVR FLASH addressing requires us to modify the write command based on if we are writing a high
228 * or low byte at the current word address */
229 if (V2Command
== CMD_PROGRAM_FLASH_ISP
)
230 Write_Memory_Params
.ProgrammingCommands
[0] ^= READ_WRITE_HIGH_BYTE_MASK
;
232 /* Check to see if we have a valid polling address */
233 if (!(PollAddress
) && (ByteToWrite
!= PollValue
))
235 if ((CurrentByte
& 0x01) && (V2Command
== CMD_PROGRAM_FLASH_ISP
))
236 Write_Memory_Params
.ProgrammingCommands
[2] |= READ_WRITE_HIGH_BYTE_MASK
;
238 Write_Memory_Params
.ProgrammingCommands
[2] &= ~READ_WRITE_HIGH_BYTE_MASK
;
240 PollAddress
= (CurrentAddress
& 0xFFFF);
243 /* If in word programming mode, commit the byte to the target's memory */
244 if (!(ProgrammingMode
& PROG_MODE_PAGED_WRITES_MASK
))
246 /* If the current polling address is invalid, switch to timed delay write completion mode */
247 if (!(PollAddress
) && !(ProgrammingMode
& PROG_MODE_WORD_READYBUSY_MASK
))
248 ProgrammingMode
= (ProgrammingMode
& ~PROG_MODE_WORD_VALUE_MASK
) | PROG_MODE_WORD_TIMEDELAY_MASK
;
250 ProgrammingStatus
= ISPTarget_WaitForProgComplete(ProgrammingMode
, PollAddress
, PollValue
,
251 Write_Memory_Params
.DelayMS
,
252 Write_Memory_Params
.ProgrammingCommands
[2]);
254 /* Abort the programming loop early if the byte/word programming failed */
255 if (ProgrammingStatus
!= STATUS_CMD_OK
)
258 /* Must reset the polling address afterwards, so it is not erroneously used for the next byte */
262 /* EEPROM just increments the address each byte, flash needs to increment on each word and
263 * also check to ensure that a LOAD EXTENDED ADDRESS command is issued each time the extended
264 * address boundary has been crossed during FLASH memory programming */
265 if ((CurrentByte
& 0x01) || (V2Command
== CMD_PROGRAM_EEPROM_ISP
))
269 if ((V2Command
== CMD_PROGRAM_FLASH_ISP
) && !(CurrentAddress
& 0xFFFF))
270 MustLoadExtendedAddress
= true;
274 /* If the current page must be committed, send the PROGRAM PAGE command to the target */
275 if (Write_Memory_Params
.ProgrammingMode
& PROG_MODE_COMMIT_PAGE_MASK
)
277 ISPTarget_SendByte(Write_Memory_Params
.ProgrammingCommands
[1]);
278 ISPTarget_SendByte(PageStartAddress
>> 8);
279 ISPTarget_SendByte(PageStartAddress
& 0xFF);
280 ISPTarget_SendByte(0x00);
282 /* Check if polling is enabled and possible, if not switch to timed delay mode */
283 if ((Write_Memory_Params
.ProgrammingMode
& PROG_MODE_PAGED_VALUE_MASK
) && !(PollAddress
))
285 Write_Memory_Params
.ProgrammingMode
= (Write_Memory_Params
.ProgrammingMode
& ~PROG_MODE_PAGED_VALUE_MASK
) |
286 PROG_MODE_PAGED_TIMEDELAY_MASK
;
289 ProgrammingStatus
= ISPTarget_WaitForProgComplete(Write_Memory_Params
.ProgrammingMode
, PollAddress
, PollValue
,
290 Write_Memory_Params
.DelayMS
,
291 Write_Memory_Params
.ProgrammingCommands
[2]);
293 /* Check to see if the FLASH address has crossed the extended address boundary */
294 if ((V2Command
== CMD_PROGRAM_FLASH_ISP
) && !(CurrentAddress
& 0xFFFF))
295 MustLoadExtendedAddress
= true;
298 Endpoint_Write_8(V2Command
);
299 Endpoint_Write_8(ProgrammingStatus
);
303 /** Handler for the CMD_READ_FLASH_ISP and CMD_READ_EEPROM_ISP commands, reading in bytes,
304 * words or pages of data from the attached device.
306 * \param[in] V2Command Issued V2 Protocol command byte from the host
308 void ISPProtocol_ReadMemory(uint8_t V2Command
)
312 uint16_t BytesToRead
;
313 uint8_t ReadMemoryCommand
;
314 } Read_Memory_Params
;
316 Endpoint_Read_Stream_LE(&Read_Memory_Params
, sizeof(Read_Memory_Params
), NULL
);
317 Read_Memory_Params
.BytesToRead
= SwapEndian_16(Read_Memory_Params
.BytesToRead
);
320 Endpoint_SelectEndpoint(AVRISP_DATA_IN_EPADDR
);
321 Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN
);
323 Endpoint_Write_8(V2Command
);
324 Endpoint_Write_8(STATUS_CMD_OK
);
326 /* Read each byte from the device and write them to the packet for the host */
327 for (uint16_t CurrentByte
= 0; CurrentByte
< Read_Memory_Params
.BytesToRead
; CurrentByte
++)
329 /* Check to see if we need to send a LOAD EXTENDED ADDRESS command to the target */
330 if (MustLoadExtendedAddress
)
332 ISPTarget_LoadExtendedAddress();
333 MustLoadExtendedAddress
= false;
336 /* Read the next byte from the desired memory space in the device */
337 ISPTarget_SendByte(Read_Memory_Params
.ReadMemoryCommand
);
338 ISPTarget_SendByte(CurrentAddress
>> 8);
339 ISPTarget_SendByte(CurrentAddress
& 0xFF);
340 Endpoint_Write_8(ISPTarget_ReceiveByte());
342 /* Check if the endpoint bank is currently full, if so send the packet */
343 if (!(Endpoint_IsReadWriteAllowed()))
346 Endpoint_WaitUntilReady();
349 /* AVR FLASH addressing requires us to modify the read command based on if we are reading a high
350 * or low byte at the current word address */
351 if (V2Command
== CMD_READ_FLASH_ISP
)
352 Read_Memory_Params
.ReadMemoryCommand
^= READ_WRITE_HIGH_BYTE_MASK
;
354 /* EEPROM just increments the address each byte, flash needs to increment on each word and
355 * also check to ensure that a LOAD EXTENDED ADDRESS command is issued each time the extended
356 * address boundary has been crossed */
357 if ((CurrentByte
& 0x01) || (V2Command
== CMD_READ_EEPROM_ISP
))
361 if ((V2Command
!= CMD_READ_EEPROM_ISP
) && !(CurrentAddress
& 0xFFFF))
362 MustLoadExtendedAddress
= true;
366 Endpoint_Write_8(STATUS_CMD_OK
);
368 bool IsEndpointFull
= !(Endpoint_IsReadWriteAllowed());
371 /* Ensure last packet is a short packet to terminate the transfer */
374 Endpoint_WaitUntilReady();
376 Endpoint_WaitUntilReady();
380 /** Handler for the CMD_CHI_ERASE_ISP command, clearing the target's FLASH memory. */
381 void ISPProtocol_ChipErase(void)
385 uint8_t EraseDelayMS
;
387 uint8_t EraseCommandBytes
[4];
390 Endpoint_Read_Stream_LE(&Erase_Chip_Params
, sizeof(Erase_Chip_Params
), NULL
);
393 Endpoint_SelectEndpoint(AVRISP_DATA_IN_EPADDR
);
394 Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN
);
396 uint8_t ResponseStatus
= STATUS_CMD_OK
;
398 /* Send the chip erase commands as given by the host to the device */
399 for (uint8_t SByte
= 0; SByte
< sizeof(Erase_Chip_Params
.EraseCommandBytes
); SByte
++)
400 ISPTarget_SendByte(Erase_Chip_Params
.EraseCommandBytes
[SByte
]);
402 /* Use appropriate command completion check as given by the host (delay or busy polling) */
403 if (!(Erase_Chip_Params
.PollMethod
))
404 ISPProtocol_DelayMS(Erase_Chip_Params
.EraseDelayMS
);
406 ResponseStatus
= ISPTarget_WaitWhileTargetBusy();
408 Endpoint_Write_8(CMD_CHIP_ERASE_ISP
);
409 Endpoint_Write_8(ResponseStatus
);
413 /** Handler for the CMD_OSCCAL command, entering RC-calibration mode as specified in AVR053 */
414 void ISPProtocol_Calibrate(void)
416 uint8_t ResponseStatus
= STATUS_CMD_OK
;
418 /* Don't entirely know why this is needed, something to do with the USB communication back to PC */
420 Endpoint_SelectEndpoint(AVRISP_DATA_IN_EPADDR
);
421 Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN
);
423 /* Enable pull-up on MISO and release ~RESET */
425 PORTB
|= ( (1 << PB4
) | (1 << PB3
) );
427 /* Set up MISO pin (PCINT3) to listen for toggles */
428 PCMSK0
= (1 << PCINT3
);
430 /* Set up timer that fires at a rate of 65536 Hz - this will drive the MOSI toggle */
431 OCR1A
= ISPPROTOCOL_CALIB_TICKS
- 1;
432 TCCR1A
= ( (1 << WGM11
) | (1 << WGM10
) ); // set for fast PWM, TOP = OCR1A
433 TCCR1B
= ( (1 << WGM13
) | (1 << WGM12
) | (1 << CS10
) ); // ... and no clock prescaling
436 /* Initialize counter variables */
437 ISPProtocol_HalfCyclesRemaining
= ISPPROTOCOL_CALIB_HALF_CYCLE_LIMIT
;
438 ISPProtocol_ResponseTogglesRemaining
= ISPPROTOCOL_CALIB_SUCCESS_TOGGLE_NUM
;
440 /* Turn on interrupts */
441 PCICR
|= (1 << PCIE0
); // enable interrupts for PCINT7:0 (don't touch setting for PCINT12:8)
442 TIMSK1
= (1 << TOIE1
); // enable T1 OVF interrupt (and no other T1 interrupts)
444 /* Turn on global interrupts for the following block, restoring current state at end */
445 NONATOMIC_BLOCK(NONATOMIC_RESTORESTATE
)
447 /* Let device do its calibration, wait for response on MISO */
448 while (ISPProtocol_HalfCyclesRemaining
&& ISPProtocol_ResponseTogglesRemaining
);
450 /* Disable timer and pin change interrupts */
451 PCICR
&= ~(1 << PCIE0
);
455 /* Check if device responded with a success message or if we timed out */
456 if (ISPProtocol_ResponseTogglesRemaining
)
457 ResponseStatus
= STATUS_CMD_TOUT
;
459 /* Report back to PC via USB */
460 Endpoint_Write_8(CMD_OSCCAL
);
461 Endpoint_Write_8(ResponseStatus
);
465 /** Handler for the CMD_READ_FUSE_ISP, CMD_READ_LOCK_ISP, CMD_READ_SIGNATURE_ISP and CMD_READ_OSCCAL commands,
466 * reading the requested configuration byte from the device.
468 * \param[in] V2Command Issued V2 Protocol command byte from the host
470 void ISPProtocol_ReadFuseLockSigOSCCAL(uint8_t V2Command
)
475 uint8_t ReadCommandBytes
[4];
476 } Read_FuseLockSigOSCCAL_Params
;
478 Endpoint_Read_Stream_LE(&Read_FuseLockSigOSCCAL_Params
, sizeof(Read_FuseLockSigOSCCAL_Params
), NULL
);
481 Endpoint_SelectEndpoint(AVRISP_DATA_IN_EPADDR
);
482 Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN
);
484 uint8_t ResponseBytes
[4];
486 /* Send the Fuse or Lock byte read commands as given by the host to the device, store response */
487 for (uint8_t RByte
= 0; RByte
< sizeof(ResponseBytes
); RByte
++)
488 ResponseBytes
[RByte
] = ISPTarget_TransferByte(Read_FuseLockSigOSCCAL_Params
.ReadCommandBytes
[RByte
]);
490 Endpoint_Write_8(V2Command
);
491 Endpoint_Write_8(STATUS_CMD_OK
);
492 Endpoint_Write_8(ResponseBytes
[Read_FuseLockSigOSCCAL_Params
.RetByte
- 1]);
493 Endpoint_Write_8(STATUS_CMD_OK
);
497 /** Handler for the CMD_WRITE_FUSE_ISP and CMD_WRITE_LOCK_ISP commands, writing the requested configuration
498 * byte to the device.
500 * \param[in] V2Command Issued V2 Protocol command byte from the host
502 void ISPProtocol_WriteFuseLock(uint8_t V2Command
)
506 uint8_t WriteCommandBytes
[4];
507 } Write_FuseLockSig_Params
;
509 Endpoint_Read_Stream_LE(&Write_FuseLockSig_Params
, sizeof(Write_FuseLockSig_Params
), NULL
);
512 Endpoint_SelectEndpoint(AVRISP_DATA_IN_EPADDR
);
513 Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN
);
515 /* Send the Fuse or Lock byte program commands as given by the host to the device */
516 for (uint8_t SByte
= 0; SByte
< sizeof(Write_FuseLockSig_Params
.WriteCommandBytes
); SByte
++)
517 ISPTarget_SendByte(Write_FuseLockSig_Params
.WriteCommandBytes
[SByte
]);
519 Endpoint_Write_8(V2Command
);
520 Endpoint_Write_8(STATUS_CMD_OK
);
521 Endpoint_Write_8(STATUS_CMD_OK
);
525 /** Handler for the CMD_SPI_MULTI command, writing and reading arbitrary SPI data to and from the attached device. */
526 void ISPProtocol_SPIMulti(void)
536 Endpoint_Read_Stream_LE(&SPI_Multi_Params
, (sizeof(SPI_Multi_Params
) - sizeof(SPI_Multi_Params
.TxData
)), NULL
);
537 Endpoint_Read_Stream_LE(&SPI_Multi_Params
.TxData
, SPI_Multi_Params
.TxBytes
, NULL
);
539 if (SPI_Multi_Params
.TxBytes
>= sizeof(SPI_Multi_Params
.TxData
))
541 Endpoint_StallTransaction();
546 Endpoint_SelectEndpoint(AVRISP_DATA_IN_EPADDR
);
547 Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN
);
549 Endpoint_Write_8(CMD_SPI_MULTI
);
550 Endpoint_Write_8(STATUS_CMD_OK
);
552 uint8_t CurrTxPos
= 0;
553 uint8_t CurrRxPos
= 0;
555 /* Write out bytes to transmit until the start of the bytes to receive is met */
556 while (CurrTxPos
< SPI_Multi_Params
.RxStartAddr
)
558 if (CurrTxPos
< SPI_Multi_Params
.TxBytes
)
559 ISPTarget_SendByte(SPI_Multi_Params
.TxData
[CurrTxPos
]);
561 ISPTarget_SendByte(0);
566 /* Transmit remaining bytes with padding as needed, read in response bytes */
567 while (CurrRxPos
< SPI_Multi_Params
.RxBytes
)
569 if (CurrTxPos
< SPI_Multi_Params
.TxBytes
)
570 Endpoint_Write_8(ISPTarget_TransferByte(SPI_Multi_Params
.TxData
[CurrTxPos
++]));
572 Endpoint_Write_8(ISPTarget_ReceiveByte());
574 /* Check to see if we have filled the endpoint bank and need to send the packet */
575 if (!(Endpoint_IsReadWriteAllowed()))
578 Endpoint_WaitUntilReady();
584 Endpoint_Write_8(STATUS_CMD_OK
);
586 bool IsEndpointFull
= !(Endpoint_IsReadWriteAllowed());
589 /* Ensure last packet is a short packet to terminate the transfer */
592 Endpoint_WaitUntilReady();
594 Endpoint_WaitUntilReady();
598 /** Blocking delay for a given number of milliseconds. This provides a simple wrapper around
599 * the avr-libc provided delay function, so that the delay function can be called with a
600 * constant value (to prevent run-time floating point operations being required).
602 * \param[in] DelayMS Number of milliseconds to delay for
604 void ISPProtocol_DelayMS(uint8_t DelayMS
)
606 while (DelayMS
-- && TimeoutTicksRemaining
)