mirror of
https://github.com/dougg3/mac-rom-simm-programmer.git
synced 2024-11-22 06:32:23 +00:00
613 lines
19 KiB
C
613 lines
19 KiB
C
/*
|
|
* usb_serial.c
|
|
*
|
|
* Created on: Dec 9, 2011
|
|
* Author: Doug
|
|
*
|
|
* Copyright (C) 2011-2012 Doug Brown
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "usb_serial.h"
|
|
#include "../LUFA/Drivers/USB/USB.h"
|
|
#include "../cdc_device_definition.h"
|
|
#include "../external_mem.h"
|
|
#include "../tests/simm_electrical_test.h"
|
|
#include "../programmer_protocol.h"
|
|
#include "../led.h"
|
|
#include <stdbool.h>
|
|
|
|
#define MAX_CHIP_SIZE (2UL * 1024UL * 1024UL)
|
|
#define READ_CHUNK_SIZE_BYTES 1024UL
|
|
#define WRITE_CHUNK_SIZE_BYTES 1024UL
|
|
#define ERASE_SECTOR_SIZE_BYTES (256UL * 1024UL)
|
|
#if ((READ_CHUNK_SIZE_BYTES % 4) != 0)
|
|
#error Read chunk size should be a multiple of 4 bytes
|
|
#endif
|
|
#if ((WRITE_CHUNK_SIZE_BYTES % 4) != 0)
|
|
#error Write chunk size should be a multiple of 4 bytes
|
|
#endif
|
|
|
|
void USBSerial_Init(void)
|
|
{
|
|
USB_Init();
|
|
}
|
|
|
|
// Internal state so we know how to interpret the next-received byte
|
|
typedef enum ProgrammerCommandState
|
|
{
|
|
WaitingForCommand = 0,
|
|
//ReadingByteWaitingForAddress, // TODO
|
|
ReadingChipsReadLength,
|
|
ReadingChips,
|
|
//ReadingChipsUnableSendError, // TODO
|
|
WritingChips,
|
|
ErasePortionReadingPosLength,
|
|
ReadingChipsReadStartPos,
|
|
WritingChipsReadingStartPos,
|
|
ReadingChipsMask,
|
|
} ProgrammerCommandState;
|
|
static ProgrammerCommandState curCommandState = WaitingForCommand;
|
|
|
|
// State info for reading/writing
|
|
//static uint8_t byteAddressReceiveCount = 0;
|
|
static uint16_t curReadIndex;
|
|
static uint32_t readLength;
|
|
static uint8_t readLengthByteIndex;
|
|
static int16_t writePosInChunk = -1;
|
|
static uint16_t curWriteIndex = 0;
|
|
static bool verifyDuringWrite = false;
|
|
static uint32_t erasePosition;
|
|
static uint32_t eraseLength;
|
|
static uint8_t chipsMask = ALL_CHIPS;
|
|
|
|
// Private functions
|
|
void USBSerial_HandleWaitingForCommandByte(uint8_t byte);
|
|
void USBSerial_HandleReadingChipsByte(uint8_t byte);
|
|
void USBSerial_HandleReadingChipsReadLengthByte(uint8_t byte);
|
|
void USBSerial_SendReadDataChunk(void);
|
|
void USBSerial_HandleWritingChipsByte(uint8_t byte);
|
|
void USBSerial_ElectricalTest_Fail_Handler(uint8_t index1, uint8_t index2);
|
|
void USBSerial_HandleErasePortionReadPosLengthByte(uint8_t byte);
|
|
void USBSerial_HandleReadingChipsReadStartPosByte(uint8_t byte);
|
|
void USBSerial_HandleWritingChipsReadingStartPosByte(uint8_t byte);
|
|
void USBSerial_HandleReadingChipsMaskByte(uint8_t byte);
|
|
|
|
// Read/write to USB serial macros -- easier than retyping
|
|
// CDC_Device_XXX(&VirtualSerial_CDC_Interface...) every time
|
|
#define SendByte(b) CDC_Device_SendByte(&VirtualSerial_CDC_Interface, b)
|
|
#define ReadByte() CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface)
|
|
#define SendData(d, l) CDC_Device_SendData(&VirtualSerial_CDC_Interface, d, l)
|
|
|
|
// Should be called periodically in the main loop
|
|
void USBSerial_Check(void)
|
|
{
|
|
// If we're configured, read a byte (if one is available) and process it
|
|
if (USB_DeviceState == DEVICE_STATE_Configured)
|
|
{
|
|
int16_t recvByte = ReadByte();
|
|
|
|
// Did we get a byte? If so, hand it off to the correct handler
|
|
// function based on the current state
|
|
if (recvByte >= 0)
|
|
{
|
|
switch (curCommandState)
|
|
{
|
|
case WaitingForCommand:
|
|
USBSerial_HandleWaitingForCommandByte((uint8_t)recvByte);
|
|
break;
|
|
case ReadingChipsReadLength:
|
|
USBSerial_HandleReadingChipsReadLengthByte((uint8_t)recvByte);
|
|
break;
|
|
case ReadingChips:
|
|
USBSerial_HandleReadingChipsByte((uint8_t)recvByte);
|
|
break;
|
|
case WritingChips:
|
|
USBSerial_HandleWritingChipsByte((uint8_t)recvByte);
|
|
break;
|
|
case ErasePortionReadingPosLength:
|
|
USBSerial_HandleErasePortionReadPosLengthByte((uint8_t)recvByte);
|
|
break;
|
|
case ReadingChipsReadStartPos:
|
|
USBSerial_HandleReadingChipsReadStartPosByte((uint8_t)recvByte);
|
|
break;
|
|
case WritingChipsReadingStartPos:
|
|
USBSerial_HandleWritingChipsReadingStartPosByte((uint8_t)recvByte);
|
|
break;
|
|
case ReadingChipsMask:
|
|
USBSerial_HandleReadingChipsMaskByte((uint8_t)recvByte);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// And do the periodic CDC and USB tasks...
|
|
CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
|
|
USB_USBTask();
|
|
}
|
|
|
|
// If we're in the "waiting for command" state, handle the command...
|
|
void USBSerial_HandleWaitingForCommandByte(uint8_t byte)
|
|
{
|
|
switch (byte)
|
|
{
|
|
// Asked to enter waiting mode -- we're already there, so say OK.
|
|
case EnterWaitingMode:
|
|
SendByte(CommandReplyOK);
|
|
curCommandState = WaitingForCommand;
|
|
break;
|
|
// Asked to do the electrical test. Reply OK, and then do the test,
|
|
// sending whatever replies necessary
|
|
case DoElectricalTest:
|
|
SendByte(CommandReplyOK);
|
|
// Force LUFA to send initial "OK" reply immediately in this case
|
|
// so the caller gets immediate feedback that the test has started
|
|
CDC_Device_Flush(&VirtualSerial_CDC_Interface);
|
|
SIMMElectricalTest_Run(USBSerial_ElectricalTest_Fail_Handler);
|
|
SendByte(ProgrammerElectricalTestDone);
|
|
curCommandState = WaitingForCommand;
|
|
break;
|
|
// Asked to identify the chips in the SIMM. Identify them and send reply.
|
|
case IdentifyChips:
|
|
{
|
|
struct ChipID chips[NUM_CHIPS];
|
|
SendByte(CommandReplyOK);
|
|
ExternalMem_IdentifyChips(chips);
|
|
int x;
|
|
for (x = 0; x < NUM_CHIPS; x++)
|
|
{
|
|
SendByte(chips[x].manufacturerID);
|
|
SendByte(chips[x].deviceID);
|
|
}
|
|
SendByte(ProgrammerIdentifyDone);
|
|
break;
|
|
}
|
|
// Asked to read a single byte from each SIMM. Change the state and reply.
|
|
case ReadByte:
|
|
/*curCommandState = ReadingByteWaitingForAddress;
|
|
byteAddressReceiveCount = 0;
|
|
SendByte(CommandReplyOK);*/
|
|
SendByte(CommandReplyInvalid); // not implemented yet
|
|
break;
|
|
// Asked to read all four chips. Set the state, reply with the first chunk.
|
|
// This will read from the BEGINNING of the SIMM every time. Use
|
|
// ReadChipsAt to specify a start position
|
|
case ReadChips:
|
|
curCommandState = ReadingChipsReadLength;
|
|
curReadIndex = 0;
|
|
readLengthByteIndex = 0;
|
|
readLength = 0;
|
|
SendByte(CommandReplyOK);
|
|
break;
|
|
case ReadChipsAt:
|
|
curCommandState = ReadingChipsReadStartPos;
|
|
curReadIndex = 0;
|
|
readLengthByteIndex = 0;
|
|
readLength = 0;
|
|
SendByte(CommandReplyOK);
|
|
break;
|
|
// Erase the chips and reply OK. (TODO: Sometimes erase might fail)
|
|
case EraseChips:
|
|
ExternalMem_EraseChips(chipsMask);
|
|
SendByte(CommandReplyOK);
|
|
break;
|
|
// Begin writing the chips. Change the state, reply, wait for chunk of data
|
|
case WriteChips:
|
|
curCommandState = WritingChips;
|
|
curWriteIndex = 0;
|
|
writePosInChunk = -1;
|
|
SendByte(CommandReplyOK);
|
|
break;
|
|
case WriteChipsAt:
|
|
curCommandState = WritingChipsReadingStartPos;
|
|
curWriteIndex = 0;
|
|
readLengthByteIndex = 0;
|
|
writePosInChunk = -1;
|
|
SendByte(CommandReplyOK);
|
|
break;
|
|
// Asked for the current bootloader state. We are in the program right now,
|
|
// so reply accordingly.
|
|
case GetBootloaderState:
|
|
SendByte(CommandReplyOK);
|
|
SendByte(BootloaderStateInProgrammer);
|
|
break;
|
|
// Enter the bootloader. Wait a bit, then jump to the bootloader location.
|
|
case EnterBootloader:
|
|
SendByte(CommandReplyOK);
|
|
CDC_Device_Flush(&VirtualSerial_CDC_Interface);
|
|
|
|
// Insert a small delay to ensure that it arrives before rebooting.
|
|
_delay_ms(1000);
|
|
|
|
// Done with the USB interface -- the bootloader will re-initialize it.
|
|
USB_Disable();
|
|
|
|
// Disable interrupts so nothing weird happens...
|
|
cli();
|
|
|
|
// Wait a little bit to let everything settle and let the program
|
|
// close the port after the USB disconnect
|
|
_delay_ms(2000);
|
|
|
|
// And, of course, go into the bootloader.
|
|
__asm__ __volatile__ ( "jmp 0xE000" );
|
|
break;
|
|
// Enter the programmer. We're already there, so reply OK.
|
|
case EnterProgrammer:
|
|
// Already in the programmer
|
|
SendByte(CommandReplyOK);
|
|
break;
|
|
// Set the SIMM type to the older, smaller chip size (2MB and below)
|
|
case SetSIMMTypePLCC32_2MB:
|
|
ExternalMem_SetChipType(ChipType8BitData_4MBitSize);
|
|
SendByte(CommandReplyOK);
|
|
break;
|
|
case SetSIMMTypeLarger:
|
|
ExternalMem_SetChipType(ChipType8Bit16BitData_16MBitSize);
|
|
SendByte(CommandReplyOK);
|
|
break;
|
|
case SetVerifyWhileWriting:
|
|
verifyDuringWrite = true;
|
|
SendByte(CommandReplyOK);
|
|
break;
|
|
case SetNoVerifyWhileWriting:
|
|
verifyDuringWrite = false;
|
|
SendByte(CommandReplyOK);
|
|
break;
|
|
case ErasePortion:
|
|
readLengthByteIndex = 0;
|
|
eraseLength = 0;
|
|
erasePosition = 0;
|
|
curCommandState = ErasePortionReadingPosLength;
|
|
SendByte(CommandReplyOK);
|
|
break;
|
|
case SetChipsMask:
|
|
curCommandState = ReadingChipsMask;
|
|
SendByte(CommandReplyOK);
|
|
break;
|
|
// We don't know what this command is, so reply that it was invalid.
|
|
default:
|
|
SendByte(CommandReplyInvalid);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we're in the "reading chips" state, handle the incoming byte...
|
|
void USBSerial_HandleReadingChipsByte(uint8_t byte)
|
|
{
|
|
// The byte should be a reply from the computer. It should be either:
|
|
// 1) ComputerReadOK -- meaning it got the chunk we just sent
|
|
// or
|
|
// 2) ComputerReadCancel -- meaning the user canceled the read
|
|
switch (byte)
|
|
{
|
|
case ComputerReadOK:
|
|
// If they have confirmed the final data chunk, let them know
|
|
// that they have finished, and enter command state.
|
|
if (curReadIndex >= readLength)
|
|
{
|
|
LED_Off();
|
|
SendByte(ProgrammerReadFinished);
|
|
curCommandState = WaitingForCommand;
|
|
}
|
|
else // There's more data left to read, so read it and send it to them!
|
|
{
|
|
LED_Toggle();
|
|
SendByte(ProgrammerReadMoreData);
|
|
USBSerial_SendReadDataChunk();
|
|
}
|
|
break;
|
|
case ComputerReadCancel:
|
|
// If they've canceled, let them know we got their request and go back
|
|
// to "waiting for command" state
|
|
SendByte(ProgrammerReadConfirmCancel);
|
|
curCommandState = WaitingForCommand;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we're figuring out the length to read, grab it now...
|
|
void USBSerial_HandleReadingChipsReadLengthByte(uint8_t byte)
|
|
{
|
|
// There will be four bytes, so count up until we know the length. If they
|
|
// have sent all four bytes, send the first read chunk.
|
|
readLength |= (((uint32_t)byte) << (8*readLengthByteIndex));
|
|
if (++readLengthByteIndex >= 4)
|
|
{
|
|
// Ensure it's within limits and a multiple of 1024
|
|
if ((curReadIndex + readLength > NUM_CHIPS * MAX_CHIP_SIZE) ||
|
|
(readLength % READ_CHUNK_SIZE_BYTES) ||
|
|
(curReadIndex % READ_CHUNK_SIZE_BYTES) ||
|
|
(readLength == 0))// Ensure it's within limits and a multiple of 1024
|
|
{
|
|
SendByte(ProgrammerReadError);
|
|
curCommandState = WaitingForCommand;
|
|
}
|
|
else
|
|
{
|
|
// Convert the length/pos into the number of chunks we need to send
|
|
readLength /= READ_CHUNK_SIZE_BYTES;
|
|
curReadIndex /= READ_CHUNK_SIZE_BYTES;
|
|
curCommandState = ReadingChips;
|
|
SendByte(ProgrammerReadOK);
|
|
USBSerial_SendReadDataChunk();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read the next chunk of data from the SIMM and send it off over the serial.
|
|
void USBSerial_SendReadDataChunk(void)
|
|
{
|
|
// Here's a buffer we will use to read the next chunk of data.
|
|
// It's static because the stack is NOT big enough for it. If I start
|
|
// running low on RAM, I could pull this out of the function and share it
|
|
// with other functions, but I'm not bothering with that for now.
|
|
static union
|
|
{
|
|
uint32_t readChunks[READ_CHUNK_SIZE_BYTES / NUM_CHIPS];
|
|
uint8_t readChunkBytes[READ_CHUNK_SIZE_BYTES];
|
|
} chunks;
|
|
|
|
// Read the next chunk of data, send it over USB, and make sure
|
|
// we sent it correctly.
|
|
ExternalMem_Read(curReadIndex * (READ_CHUNK_SIZE_BYTES/NUM_CHIPS),
|
|
chunks.readChunks, READ_CHUNK_SIZE_BYTES/NUM_CHIPS);
|
|
uint8_t retVal = SendData((const char *)chunks.readChunkBytes,
|
|
READ_CHUNK_SIZE_BYTES);
|
|
|
|
// If for some reason there was an error, mark it as such. Otherwise,
|
|
// increment our pointer so we know the next chunk of data to send.
|
|
if (retVal != ENDPOINT_RWSTREAM_NoError)
|
|
{
|
|
//curCommandState = ReadingChipsUnableSendError; // TODO: not implemented
|
|
curCommandState = WaitingForCommand;
|
|
}
|
|
else
|
|
{
|
|
curReadIndex++;
|
|
}
|
|
}
|
|
|
|
// Handles a received byte from the computer while we're in the "writing chips"
|
|
// mode.
|
|
void USBSerial_HandleWritingChipsByte(uint8_t byte)
|
|
{
|
|
// A buffer we use to store the incoming data. This, too, could be shared
|
|
// with other functions if I end up running out of RAM. Again, I'm not
|
|
// bothering with that yet, but this could easily be shared with the
|
|
// read function.
|
|
static union
|
|
{
|
|
uint32_t writeChunks[WRITE_CHUNK_SIZE_BYTES / 4];
|
|
uint8_t writeChunkBytes[WRITE_CHUNK_SIZE_BYTES];
|
|
} chunks;
|
|
|
|
// This means we have just started the entire process or just finished
|
|
// a chunk, so see what the computer has decided for us to do.
|
|
if (writePosInChunk == -1)
|
|
{
|
|
switch (byte)
|
|
{
|
|
// The computer asked to write more data to the SIMM.
|
|
case ComputerWriteMore:
|
|
writePosInChunk = 0;
|
|
// Make sure we don't write past the capacity of the chips.
|
|
if (curWriteIndex < MAX_CHIP_SIZE / (WRITE_CHUNK_SIZE_BYTES/NUM_CHIPS))
|
|
{
|
|
SendByte(ProgrammerWriteOK);
|
|
}
|
|
else
|
|
{
|
|
LED_Off();
|
|
SendByte(ProgrammerWriteError);
|
|
curCommandState = WaitingForCommand;
|
|
}
|
|
break;
|
|
// The computer said that it's done writing.
|
|
case ComputerWriteFinish:
|
|
LED_Off();
|
|
SendByte(ProgrammerWriteOK);
|
|
curCommandState = WaitingForCommand;
|
|
break;
|
|
// The computer asked to cancel.
|
|
case ComputerWriteCancel:
|
|
LED_Off();
|
|
SendByte(ProgrammerWriteConfirmCancel);
|
|
curCommandState = WaitingForCommand;
|
|
break;
|
|
}
|
|
}
|
|
else // Interpret the incoming byte as data to write to the SIMM.
|
|
{
|
|
// Save the byte, and check if we've filled up an entire chunk
|
|
chunks.writeChunkBytes[writePosInChunk++] = byte;
|
|
if (writePosInChunk >= WRITE_CHUNK_SIZE_BYTES)
|
|
{
|
|
// We filled up the chunk, write it out and confirm it, then wait
|
|
// for the next command from the computer!
|
|
uint8_t writeResult = ExternalMem_Write(curWriteIndex * (WRITE_CHUNK_SIZE_BYTES/NUM_CHIPS),
|
|
chunks.writeChunks, WRITE_CHUNK_SIZE_BYTES/NUM_CHIPS, chipsMask, verifyDuringWrite);
|
|
|
|
// But if we asked to verify, make sure it came out OK.
|
|
if (verifyDuringWrite && (writeResult != 0))
|
|
{
|
|
// Uh oh -- verification failure.
|
|
LED_Off();
|
|
// Send the fail bit along with a mask of failed chips.
|
|
SendByte(ProgrammerWriteVerificationError | writeResult);
|
|
curCommandState = WaitingForCommand;
|
|
}
|
|
else
|
|
{
|
|
SendByte(ProgrammerWriteOK);
|
|
curWriteIndex++;
|
|
writePosInChunk = -1;
|
|
LED_Toggle();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Whenever an electrical test failure occurs, this handler will be called
|
|
// by it. It sends out a failure notice followed by indexes of the two
|
|
// shorted pins.
|
|
void USBSerial_ElectricalTest_Fail_Handler(uint8_t index1, uint8_t index2)
|
|
{
|
|
SendByte(ProgrammerElectricalTestFail);
|
|
SendByte(index1);
|
|
SendByte(index2);
|
|
}
|
|
|
|
// If we're figuring out the position/length to erase, parse it here.
|
|
void USBSerial_HandleErasePortionReadPosLengthByte(uint8_t byte)
|
|
{
|
|
// Read in the position and length to erase
|
|
if (readLengthByteIndex < 4)
|
|
{
|
|
erasePosition |= (((uint32_t)byte) << (8*readLengthByteIndex));
|
|
}
|
|
else
|
|
{
|
|
eraseLength |= (((uint32_t)byte) << (8*(readLengthByteIndex - 4)));
|
|
}
|
|
|
|
if (++readLengthByteIndex >= 8)
|
|
{
|
|
ChipType chipType = ExternalMem_GetChipType();
|
|
bool eraseSuccess = false;
|
|
|
|
// Ensure they are both within limits of sector size erasure
|
|
if (((erasePosition % ERASE_SECTOR_SIZE_BYTES) == 0) &&
|
|
((eraseLength % ERASE_SECTOR_SIZE_BYTES) == 0))
|
|
{
|
|
uint32_t boundary = eraseLength + erasePosition;
|
|
|
|
// Ensure they are within the limits of the chip size too
|
|
if (chipType == ChipType8BitData_4MBitSize)
|
|
{
|
|
if (boundary <= (2 * 1024UL * 1024UL))
|
|
{
|
|
// OK! We're erasing certain sectors of a 2 MB SIMM.
|
|
SendByte(ProgrammerErasePortionOK);
|
|
CDC_Device_Flush(&VirtualSerial_CDC_Interface);
|
|
if (ExternalMem_EraseSectors(erasePosition/NUM_CHIPS,
|
|
eraseLength/NUM_CHIPS, chipsMask))
|
|
{
|
|
eraseSuccess = true;
|
|
}
|
|
}
|
|
}
|
|
else if (chipType == ChipType8Bit16BitData_16MBitSize)
|
|
{
|
|
if (boundary <= (8 * 1024UL * 1024UL))
|
|
{
|
|
// OK! We're erasing certain sectors of an 8 MB SIMM.
|
|
SendByte(ProgrammerErasePortionOK);
|
|
CDC_Device_Flush(&VirtualSerial_CDC_Interface);
|
|
if (ExternalMem_EraseSectors(erasePosition/NUM_CHIPS,
|
|
eraseLength/NUM_CHIPS, chipsMask))
|
|
{
|
|
eraseSuccess = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (eraseSuccess)
|
|
{
|
|
// Not on a sector boundary for erase position and/or length
|
|
SendByte(ProgrammerErasePortionFinished);
|
|
curCommandState = WaitingForCommand;
|
|
}
|
|
else
|
|
{
|
|
// Not on a sector boundary for erase position and/or length
|
|
SendByte(ProgrammerErasePortionError);
|
|
curCommandState = WaitingForCommand;
|
|
}
|
|
}
|
|
}
|
|
|
|
void USBSerial_HandleReadingChipsReadStartPosByte(uint8_t byte)
|
|
{
|
|
// There will be four bytes, so count up until we know the position. If they
|
|
// have sent all four bytes, then start reading the length
|
|
curReadIndex |= (((uint32_t)byte) << (8*readLengthByteIndex));
|
|
if (++readLengthByteIndex >= 4)
|
|
{
|
|
readLengthByteIndex = 0;
|
|
curCommandState = ReadingChipsReadLength;
|
|
}
|
|
}
|
|
|
|
void USBSerial_HandleWritingChipsReadingStartPosByte(uint8_t byte)
|
|
{
|
|
// There will be four bytes, so count up until we know the position. If they
|
|
// have sent all four bytes, then confirm the write and begin
|
|
curWriteIndex |= (((uint32_t)byte) << (8*readLengthByteIndex));
|
|
if (++readLengthByteIndex >= 4)
|
|
{
|
|
// Got it...now, is it valid? If so, allow the write to begin
|
|
if ((curWriteIndex % WRITE_CHUNK_SIZE_BYTES) ||
|
|
(curWriteIndex >= NUM_CHIPS * MAX_CHIP_SIZE))
|
|
{
|
|
SendByte(ProgrammerWriteError);
|
|
curCommandState = WaitingForCommand;
|
|
}
|
|
else
|
|
{
|
|
// Convert write size into an index appropriate for rest of code
|
|
curWriteIndex /= WRITE_CHUNK_SIZE_BYTES;
|
|
SendByte(ProgrammerWriteOK);
|
|
curCommandState = WritingChips;
|
|
}
|
|
}
|
|
}
|
|
|
|
void USBSerial_HandleReadingChipsMaskByte(uint8_t byte)
|
|
{
|
|
// Single byte follows containing mask of chips we're programming
|
|
if (byte <= 0x0F)
|
|
{
|
|
// Mask has to be less than or equal to 0x0F because there are only
|
|
// four valid mask bits.
|
|
chipsMask = byte;
|
|
SendByte(CommandReplyOK);
|
|
}
|
|
else
|
|
{
|
|
SendByte(CommandReplyError);
|
|
}
|
|
|
|
// Done either way; now we're waiting for a command to arrive
|
|
curCommandState = WaitingForCommand;
|
|
}
|
|
|
|
// LUFA event handler for when the USB configuration changes.
|
|
void EVENT_USB_Device_ConfigurationChanged(void)
|
|
{
|
|
bool ConfigSuccess = true;
|
|
|
|
ConfigSuccess &= CDC_Device_ConfigureEndpoints(&VirtualSerial_CDC_Interface);
|
|
}
|
|
|
|
// LUFA event handler for when a USB control request is received
|
|
void EVENT_USB_Device_ControlRequest(void)
|
|
{
|
|
CDC_Device_ProcessControlRequest(&VirtualSerial_CDC_Interface);
|
|
}
|