From f3d370a7778dc8e374efc3b76e26f8ecefc27e84 Mon Sep 17 00:00:00 2001
From: Dean Camera <dean@fourwalledcubicle.com>
Date: Sun, 17 Jan 2010 04:39:33 +0000
Subject: [PATCH] Clean up and add more comments to the AVRISP-MKII project.
 Make sure the SPI_MULTI command handler supports multiple packet responses.
 Use slightly smaller/faster repeated indirect-load commands when retrieving
 the PDI target's memory CRCs.

---
 LUFA/Drivers/USB/HighLevel/Events.h           | 12 +++-
 Projects/AVRISP-MKII/Descriptors.c            |  4 +-
 Projects/AVRISP-MKII/Lib/ISP/ISPProtocol.c    | 65 ++++++++++++++-----
 Projects/AVRISP-MKII/Lib/V2Protocol.c         |  6 +-
 Projects/AVRISP-MKII/Lib/V2ProtocolParams.c   |  2 +-
 Projects/AVRISP-MKII/Lib/V2ProtocolParams.h   |  7 +-
 Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.c     | 24 +++----
 Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.h     |  2 +
 .../AVRISP-MKII/Lib/XPROG/XPROGProtocol.c     |  6 +-
 .../AVRISP-MKII/Lib/XPROG/XPROGProtocol.h     |  6 +-
 10 files changed, 87 insertions(+), 47 deletions(-)

diff --git a/LUFA/Drivers/USB/HighLevel/Events.h b/LUFA/Drivers/USB/HighLevel/Events.h
index 84b951ead..08727d749 100644
--- a/LUFA/Drivers/USB/HighLevel/Events.h
+++ b/LUFA/Drivers/USB/HighLevel/Events.h
@@ -40,8 +40,8 @@
  *  listed here. If an event with no user-associated handler is fired within the library, it by default maps to an
  *  internal empty stub function.
  *
- *  Each event must only have one associated event handler, but can be raised by multiple sources by calling the event
- *  name just like any regular C function (with any required event parameters).
+ *  Each event must only have one associated event handler, but can be raised by multiple sources by calling the
+ *  event handler function (with any required event parameters).
  *
  *  @{
  */
@@ -233,19 +233,25 @@
 			 *  \note This event does not exist if the USB_HOST_ONLY token is supplied to the compiler (see
 			 *        \ref Group_USBManagement documentation).
 			 *
+			 *  \note This event does not exist on the series 2 USB AVRs when the NO_LIMITED_CONTROLLER_CONNECT
+			 *        compile time token is not set - see \ref EVENT_USB_Device_Disconnect.
+			 *
 			 *  \see \ref EVENT_USB_Device_WakeUp() event for accompanying Wake Up event.
 			 */
 			void EVENT_USB_Device_Suspend(void);
 
 			/** Event for USB wake up. This event fires when a the USB interface is suspended while in device
 			 *  mode, and the host wakes up the device by supplying Start Of Frame pulses. This is generally
-			 *  hooked to pull the user application out of a lowe power state and back into normal operating
+			 *  hooked to pull the user application out of a low power state and back into normal operating
 			 *  mode. If the USB interface is enumerated with the \ref USB_OPT_AUTO_PLL option set, the library
 			 *  will automatically restart the USB PLL before the event is fired.
 			 *
 			 *  \note This event does not exist if the USB_HOST_ONLY token is supplied to the compiler (see
 			 *        \ref Group_USBManagement documentation).
 			 *
+			 *  \note This event does not exist on the series 2 USB AVRs when the NO_LIMITED_CONTROLLER_CONNECT
+			 *        compile time token is not set - see \ref EVENT_USB_Device_Connect.
+			 *
 			 *  \see \ref EVENT_USB_Device_Suspend() event for accompanying Suspend event.
 			 */
 			void EVENT_USB_Device_WakeUp(void);
diff --git a/Projects/AVRISP-MKII/Descriptors.c b/Projects/AVRISP-MKII/Descriptors.c
index f89cfae7c..6b4264f48 100644
--- a/Projects/AVRISP-MKII/Descriptors.c
+++ b/Projects/AVRISP-MKII/Descriptors.c
@@ -151,9 +151,9 @@ USB_Descriptor_String_t PROGMEM ManufacturerString =
  */
 USB_Descriptor_String_t PROGMEM ProductString =
 {
-	.Header                 = {.Size = USB_STRING_LEN(33), .Type = DTYPE_String},
+	.Header                 = {.Size = USB_STRING_LEN(22), .Type = DTYPE_String},
 		
-	.UnicodeString          = L"LUFA AVRISP MkII Clone Programmer"
+	.UnicodeString          = L"LUFA AVRISP MkII Clone"
 };
 
 USB_Descriptor_String_t PROGMEM SerialString =
diff --git a/Projects/AVRISP-MKII/Lib/ISP/ISPProtocol.c b/Projects/AVRISP-MKII/Lib/ISP/ISPProtocol.c
index 3a7e640b4..936c9abc9 100644
--- a/Projects/AVRISP-MKII/Lib/ISP/ISPProtocol.c
+++ b/Projects/AVRISP-MKII/Lib/ISP/ISPProtocol.c
@@ -65,7 +65,9 @@ void ISPProtocol_EnterISPMode(void)
 
 	ISPProtocol_DelayMS(Enter_ISP_Params.ExecutionDelayMS); 
 	SPI_Init(ISPTarget_GetSPIPrescalerMask() | SPI_SCK_LEAD_RISING | SPI_SAMPLE_LEADING | SPI_MODE_MASTER);
-		
+
+	/* Continuously attempt to synchronize with the target until either the number of attempts specified
+	 * by the host has exceeded, or the the device sends back the expected response values */
 	while (Enter_ISP_Params.SynchLoops-- && (ResponseStatus == STATUS_CMD_FAILED))
 	{
 		uint8_t ResponseBytes[4];
@@ -110,6 +112,7 @@ void ISPProtocol_LeaveISPMode(void)
 	Endpoint_ClearOUT();
 	Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN);
 
+	/* Perform pre-exit delay, release the target /RESET, disable the SPI bus and perform the post-exit delay */
 	ISPProtocol_DelayMS(Leave_ISP_Params.PreDelayMS);
 	ISPTarget_ChangeTargetResetLine(false);
 	SPI_ShutDown();
@@ -166,6 +169,9 @@ void ISPProtocol_ProgramMemory(uint8_t V2Command)
 	                                                                    Write_Memory_Params.PollValue2;
 	uint8_t* NextWriteByte = Write_Memory_Params.ProgData;
 
+	/* Check to see if the host has issued a SET ADDRESS command and we haven't sent a
+	 * LOAD EXTENDED ADDRESS command (if needed, used when operating beyond the 128KB
+	 * FLASH barrier) */
 	if (MustSetAddress)
 	{
 		if (CurrentAddress & (1UL << 31))
@@ -174,6 +180,7 @@ void ISPProtocol_ProgramMemory(uint8_t V2Command)
 		MustSetAddress = false;
 	}
 
+	/* Check the programming mode desired by the host, either Paged or Word memory writes */
 	if (Write_Memory_Params.ProgrammingMode & PROG_MODE_PAGED_WRITES_MASK)
 	{
 		uint16_t StartAddress = (CurrentAddress & 0xFFFF);
@@ -184,16 +191,16 @@ void ISPProtocol_ProgramMemory(uint8_t V2Command)
 			bool    IsOddByte   = (CurrentByte & 0x01);
 			uint8_t ByteToWrite = *(NextWriteByte++);
 		
-			if (IsOddByte && (V2Command == CMD_PROGRAM_FLASH_ISP))
-			  Write_Memory_Params.ProgrammingCommands[0] |=  READ_WRITE_HIGH_BYTE_MASK;
-			else
-			  Write_Memory_Params.ProgrammingCommands[0] &= ~READ_WRITE_HIGH_BYTE_MASK;
-			  
 			SPI_SendByte(Write_Memory_Params.ProgrammingCommands[0]);
 			SPI_SendByte(CurrentAddress >> 8);
 			SPI_SendByte(CurrentAddress & 0xFF);
 			SPI_SendByte(ByteToWrite);
 			
+			/* AVR FLASH addressing requires us to modify the write command based on if we are writing a high
+			 * or low byte at the current word address */
+			Write_Memory_Params.ProgrammingCommands[0] ^= READ_WRITE_HIGH_BYTE_MASK;
+
+			/* Check to see the write completion method, to see if we have a valid polling address */
 			if (!(PollAddress) && (ByteToWrite != PollValue))
 			{
 				if (IsOddByte && (V2Command == CMD_PROGRAM_FLASH_ISP))
@@ -289,6 +296,9 @@ void ISPProtocol_ReadMemory(uint8_t V2Command)
 	Endpoint_Write_Byte(V2Command);
 	Endpoint_Write_Byte(STATUS_CMD_OK);
 	
+	/* Check to see if the host has issued a SET ADDRESS command and we haven't sent a
+	 * LOAD EXTENDED ADDRESS command (if needed, used when operating beyond the 128KB
+	 * FLASH barrier) */
 	if (MustSetAddress)
 	{
 		if (CurrentAddress & (1UL << 31))
@@ -297,28 +307,30 @@ void ISPProtocol_ReadMemory(uint8_t V2Command)
 		MustSetAddress = false;
 	}
 
+	/* Read each byte from the device and write them to the packet for the host */
 	for (uint16_t CurrentByte = 0; CurrentByte < Read_Memory_Params.BytesToRead; CurrentByte++)
 	{
-		bool IsOddByte = (CurrentByte & 0x01);
-
-		if (IsOddByte && (V2Command == CMD_READ_FLASH_ISP))
-		  Read_Memory_Params.ReadMemoryCommand |=  READ_WRITE_HIGH_BYTE_MASK;
-		else
-		  Read_Memory_Params.ReadMemoryCommand &= ~READ_WRITE_HIGH_BYTE_MASK;
-
+		/* Read the next byte from the desired memory space in the device */
 		SPI_SendByte(Read_Memory_Params.ReadMemoryCommand);
 		SPI_SendByte(CurrentAddress >> 8);
 		SPI_SendByte(CurrentAddress & 0xFF);
 		Endpoint_Write_Byte(SPI_ReceiveByte());
 		
-		/* Check if the endpoint bank is currently full */
+		/* Check if the endpoint bank is currently full, if so send the packet */
 		if (!(Endpoint_IsReadWriteAllowed()))
 		{
 			Endpoint_ClearIN();
 			Endpoint_WaitUntilReady();
 		}
 		
-		if ((IsOddByte && (V2Command == CMD_READ_FLASH_ISP)) || (V2Command == CMD_READ_EEPROM_ISP))
+		/* AVR FLASH addressing requires us to modify the read command based on if we are reading a high
+		 * or low byte at the current word address */
+		if (V2Command == CMD_READ_FLASH_ISP)
+		  Read_Memory_Params.ReadMemoryCommand ^= READ_WRITE_HIGH_BYTE_MASK;
+
+		/* Only increment the current address if we have read both bytes in the current word when in FLASH
+		 * read mode, or for each byte when in EEPROM read mode */		 
+		if (((CurrentByte & 0x01) && (V2Command == CMD_READ_FLASH_ISP)) || (V2Command == CMD_READ_EEPROM_ISP))
 		  CurrentAddress++;
 	}
 
@@ -353,9 +365,11 @@ void ISPProtocol_ChipErase(void)
 	
 	uint8_t ResponseStatus = STATUS_CMD_OK;
 	
+	/* Send the chip erase commands as given by the host to the device */
 	for (uint8_t SByte = 0; SByte < sizeof(Erase_Chip_Params.EraseCommandBytes); SByte++)
 	  SPI_SendByte(Erase_Chip_Params.EraseCommandBytes[SByte]);
 
+	/* Use appropriate command completion check as given by the host (delay or busy polling) */
 	if (!(Erase_Chip_Params.PollMethod))
 	  ISPProtocol_DelayMS(Erase_Chip_Params.EraseDelayMS);
 	else
@@ -385,7 +399,8 @@ void ISPProtocol_ReadFuseLockSigOSCCAL(uint8_t V2Command)
 	Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN);
 
 	uint8_t ResponseBytes[4];
-		
+
+	/* Send the Fuse or Lock byte read commands as given by the host to the device, store response */
 	for (uint8_t RByte = 0; RByte < sizeof(ResponseBytes); RByte++)
 	  ResponseBytes[RByte] = SPI_TransferByte(Read_FuseLockSigOSCCAL_Params.ReadCommandBytes[RByte]);
 		
@@ -413,6 +428,7 @@ void ISPProtocol_WriteFuseLock(uint8_t V2Command)
 	Endpoint_ClearOUT();
 	Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN);
 
+	/* Send the Fuse or Lock byte program commands as given by the host to the device */
 	for (uint8_t SByte = 0; SByte < sizeof(Write_FuseLockSig_Params.WriteCommandBytes); SByte++)
 	  SPI_SendByte(Write_FuseLockSig_Params.WriteCommandBytes[SByte]);
 		
@@ -463,12 +479,29 @@ void ISPProtocol_SPIMulti(void)
 		  Endpoint_Write_Byte(SPI_TransferByte(SPI_Multi_Params.TxData[CurrTxPos++]));
 		else
 		  Endpoint_Write_Byte(SPI_ReceiveByte());
+		  
+		/* Check to see if we have filled the endpoint bank and need to send the packet */
+		if (!(Endpoint_IsReadWriteAllowed()))
+		{
+			Endpoint_ClearIN();
+			Endpoint_WaitUntilReady();
+		}
 		
 		CurrRxPos++;
 	}	
 	
 	Endpoint_Write_Byte(STATUS_CMD_OK);
+
+	bool IsEndpointFull = !(Endpoint_IsReadWriteAllowed());
 	Endpoint_ClearIN();
+	
+	/* Ensure last packet is a short packet to terminate the transfer */
+	if (IsEndpointFull)
+	{
+		Endpoint_WaitUntilReady();	
+		Endpoint_ClearIN();
+		Endpoint_WaitUntilReady();	
+	}
 }
 
 #endif
\ No newline at end of file
diff --git a/Projects/AVRISP-MKII/Lib/V2Protocol.c b/Projects/AVRISP-MKII/Lib/V2Protocol.c
index 093c07862..976f24b9e 100644
--- a/Projects/AVRISP-MKII/Lib/V2Protocol.c
+++ b/Projects/AVRISP-MKII/Lib/V2Protocol.c
@@ -58,6 +58,7 @@ void V2Protocol_ProcessCommand(void)
 {
 	uint8_t V2Command = Endpoint_Read_Byte();
 	
+	/* Set total command processing timeout value, enable timeout management interrupt */
 	TimeoutMSRemaining = COMMAND_TIMEOUT_MS;
 	TIMSK0 |= (1 << OCIE0A);
 
@@ -121,6 +122,7 @@ void V2Protocol_ProcessCommand(void)
 			break;
 	}
 		
+	/* Disable timeout management interrupt once processing has completed */
 	TIMSK0 &= ~(1 << OCIE0A);
 
 	Endpoint_WaitUntilReady();
@@ -162,8 +164,8 @@ static void V2Protocol_SignOn(void)
 	Endpoint_ClearIN();
 }
 
-/** Handler for the CMD_RESET_PROTECTION command, currently implemented as a dummy ACK function
- *  as no ISP short-circuit protection is currently implemented.
+/** Handler for the CMD_RESET_PROTECTION command, implemented as a dummy ACK function as
+ *  no target short-circuit protection is currently implemented.
  */
 static void V2Protocol_ResetProtection(void)
 {
diff --git a/Projects/AVRISP-MKII/Lib/V2ProtocolParams.c b/Projects/AVRISP-MKII/Lib/V2ProtocolParams.c
index c2595e78f..00788515d 100644
--- a/Projects/AVRISP-MKII/Lib/V2ProtocolParams.c
+++ b/Projects/AVRISP-MKII/Lib/V2ProtocolParams.c
@@ -170,7 +170,7 @@ static ParameterItem_t* V2Params_GetParamFromTable(const uint8_t ParamID)
 	ParameterItem_t* CurrTableItem = ParameterTable;
 
 	/* Find the parameter in the parameter table if present */
-	for (uint8_t TableIndex = 0; TableIndex < (sizeof(ParameterTable) / sizeof(ParameterTable[0])); TableIndex++)
+	for (uint8_t TableIndex = 0; TableIndex < TABLE_PARAM_COUNT; TableIndex++)
 	{
 		if (ParamID == CurrTableItem->ParamID)
 		  return CurrTableItem;
diff --git a/Projects/AVRISP-MKII/Lib/V2ProtocolParams.h b/Projects/AVRISP-MKII/Lib/V2ProtocolParams.h
index 04e32fe2e..995468a4d 100644
--- a/Projects/AVRISP-MKII/Lib/V2ProtocolParams.h
+++ b/Projects/AVRISP-MKII/Lib/V2ProtocolParams.h
@@ -52,10 +52,13 @@
 
 	/* Macros: */
 		/** Parameter privilege mask to allow the host PC to read the parameter's value */
-		#define PARAM_PRIV_READ   (1 << 0)
+		#define PARAM_PRIV_READ     (1 << 0)
 
 		/** Parameter privilege mask to allow the host PC to change the parameter's value */
-		#define PARAM_PRIV_WRITE  (1 << 1)
+		#define PARAM_PRIV_WRITE    (1 << 1)
+		
+		/** Total number of parameters in the parameter table */
+		#define TABLE_PARAM_COUNT   (sizeof(ParameterTable) / sizeof(ParameterTable[0]))
 
 	/* Type Defines: */
 		/** Type define for a parameter table entry indicating a PC readable or writable device parameter. */
diff --git a/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.c b/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.c
index c23b9d75b..573f8fde5 100644
--- a/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.c
+++ b/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.c
@@ -136,24 +136,18 @@ bool XMEGANVM_GetMemoryCRC(const uint8_t CRCCommand, uint32_t* const CRCDest)
 	if (!(XMEGANVM_WaitWhileNVMControllerBusy()))
 	  return false;
 	
-	uint32_t MemoryCRC = 0;
-	
-	/* Read the first generated CRC byte value */
-	XPROGTarget_SendByte(PDI_CMD_LDS | (PDI_DATSIZE_4BYTES << 2));
+	/* Load the PDI pointer register with the DAT0 register start address */
+	XPROGTarget_SendByte(PDI_CMD_ST | (PDI_POINTER_DIRECT << 2) | PDI_DATSIZE_4BYTES);
 	XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_DAT0);
-	MemoryCRC  = XPROGTarget_ReceiveByte();
-
-	/* Read the second generated CRC byte value */
-	XPROGTarget_SendByte(PDI_CMD_LDS | (PDI_DATSIZE_4BYTES << 2));
-	XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_DAT1);
-	MemoryCRC |= ((uint16_t)XPROGTarget_ReceiveByte() << 8);
 
-	/* Read the third generated CRC byte value */
-	XPROGTarget_SendByte(PDI_CMD_LDS | (PDI_DATSIZE_4BYTES << 2));
-	XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_DAT2);
-	MemoryCRC |= ((uint32_t)XPROGTarget_ReceiveByte() << 16);
+	/* Send the REPEAT command to grab the CRC bytes */
+	XPROGTarget_SendByte(PDI_CMD_REPEAT | PDI_DATSIZE_1BYTE);
+	XPROGTarget_SendByte(XMEGA_CRC_LENGTH - 1);
 	
-	*CRCDest = MemoryCRC;
+	/* Read in the CRC bytes from the target */
+	XPROGTarget_SendByte(PDI_CMD_LD | (PDI_POINTER_INDIRECT_PI << 2) | PDI_DATSIZE_1BYTE);
+	for (uint8_t i = 0; i < XMEGA_CRC_LENGTH; i++)
+	  ((uint8_t*)CRCDest)[i] = XPROGTarget_ReceiveByte();
 	
 	return true;
 }
diff --git a/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.h b/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.h
index 63fe05cc3..cbd9e26f0 100644
--- a/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.h
+++ b/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.h
@@ -56,6 +56,8 @@
 		#endif
 
 	/* Defines: */
+		#define XMEGA_CRC_LENGTH                     3
+	
 		#define XMEGA_NVM_REG_ADDR0                  0x00
 		#define XMEGA_NVM_REG_ADDR1                  0x01
 		#define XMEGA_NVM_REG_ADDR2                  0x02
diff --git a/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.c b/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.c
index 9c6ee7e0c..1be159c38 100644
--- a/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.c
+++ b/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.c
@@ -38,7 +38,7 @@
 
 #if defined(ENABLE_XPROG_PROTOCOL) || defined(__DOXYGEN__)
 /** Base absolute address for the target's NVM controller for PDI programming */
-uint32_t XPROG_Param_NVMBase    = 0x010001C0;
+uint32_t XPROG_Param_NVMBase = 0x010001C0;
 
 /** Size in bytes of the target's EEPROM page */
 uint16_t XPROG_Param_EEPageSize;
@@ -455,10 +455,10 @@ static void XPROGProtocol_SetParam(void)
 		case XPRG_PARAM_EEPPAGESIZE:
 			XPROG_Param_EEPageSize = Endpoint_Read_Word_BE();
 			break;
-		case XPRG_PARAM_NVMCMD:
+		case XPRG_PARAM_NVMCMD_REG:
 			XPROG_Param_NVMCMDRegAddr = Endpoint_Read_Byte();
 			break;
-		case XPRG_PARAM_NVMCSR:
+		case XPRG_PARAM_NVMCSR_REG:
 			XPROG_Param_NVMCSRRegAddr = Endpoint_Read_Byte();
 			break;
 		default:
diff --git a/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.h b/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.h
index 2bc7d8a50..8e5d1b06d 100644
--- a/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.h
+++ b/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.h
@@ -98,12 +98,12 @@
 
 		#define XPRG_PARAM_NVMBASE                  0x01
 		#define XPRG_PARAM_EEPPAGESIZE              0x02
-		#define XPRG_PARAM_NVMCMD                   0x03
-		#define XPRG_PARAM_NVMCSR                   0x04
+		#define XPRG_PARAM_NVMCMD_REG               0x03 /* Undocumented, Reverse-engineered */
+		#define XPRG_PARAM_NVMCSR_REG               0x04 /* Undocumented, Reverse-engineered */
 		
 		#define XPRG_PROTOCOL_PDI                   0x00
 		#define XPRG_PROTOCOL_JTAG                  0x01
-		#define XPRG_PROTOCOL_TPI                   0x02
+		#define XPRG_PROTOCOL_TPI                   0x02 /* Undocumented, Reverse-engineered */
 		
 		#define XPRG_PAGEMODE_WRITE                 (1 << 1)
 		#define XPRG_PAGEMODE_ERASE                 (1 << 0)
-- 
GitLab