static private bool SendBulkData(byte[] Data) { /* From MPPICKIT3: * * This class allows the user to send/receive any amount of data as a series of * HID reports. It isolates the user from knowing how to hook to MPLABComm * and from knowing how data is turned into reports. You basically, open, * read, write and close. It extends HID which is the class that actually * knows how to send/receive reports. * * Protocol notes: HID wants to send information in reports. We chose a report size of 64 bytes. * The Real ICE/ICD3/PK3, do not have a symetric protocol implementation with respect to the PC. * In other words, the requirements for the USB driver implementation in the embedded side of * these tools are not the same as the requirements of the USB driver in the PC side. * If you look at the PK3 FW driver you will see it does not look like the GetData/SendData defined * here. However, both the transmission and reception use the same protocol format. * In the PC side, GetData gets called and we can safely assume that GetData will not * need to read from the middle of a message. GetData is called to get a message. This message * might have the excepted len (passed as a parameter) or any other len. As long as a message * is received, GetData must return indicating how much data it got. * Of course, that means that when calling GetData, you'd better pass a buffer big enough * to handle the largest possible response (which might not be the one you were expecting) * the PK3 can send. * This allows the following situation to occur: * PC -> Send command * PK3 -> Send response * Or * PC -> Send command * PK3 -> Send working indication * PK3 -> Send response * * Note: the following examples show how data is packetized into reports. The * examples are the same regardless of who is sending our who is receiving * (PC or PK3 FW).* * An example of 32 (0x20) bytes of data being sent in s single report * * +----------+ * | data1 | byte 0 <-- beginnig of report 1 * | data2 | byte 6 * : : * | 0x20 | byte 60 <- length of data in this message (little endian) * | 0x00 | byte 61 In this case the length is 0x20 or 32 bytes * | 0x00 | byte 62 * | 0x00 | byte 63 <-- end of report 1. End of transmission * +----------+ * * * An example of 67 bytes (0x43 in hex) being sent. It * requires sending 2 reports with 60 bytes in the first one (64 bytes of * report size - 4 bytes for the length) and 7 in the * * +----------+ * | data1 | byte 0 <-- beginnig of report 1 * | data2 | byte 6 * : : * | 0x43 | byte 60 <- length of data in this message (little endian) * | 0x00 | byte 61 In this case the length is 0x43 or 67 bytes * | 0x00 | byte 62 * | 0x00 | byte 63 <-- end of report 1 * +----------+ * +----------+ * | data61 | byte 0 <-- beginning of report 2 * | data62 | * | data63 | * | data64 | * | data65 | * | data66 | * : : * : : * | padding | byte 60 <--- note NO length included here. Only in the * | padding | byte 61 very first report * | padding | byte 62 * | padding | byte 63 <-- end of report 2 * +----------+ * * @author jose */ // Add the total message length at the end of the first USB report int size = Data.Length + 2 + 4; // Round the packet size up to the nearest USB report size if (size % 64 != 0) { size = size + (64 - size % 64); } byte[] TotalData = new byte[size]; // Fill the buffer with our pad value. Useful data will overwrite as necessary. for (int i = 0; i < TotalData.Length; i++) { TotalData[i] = 0x5B; } if (Data.Length <= 60) { // Only one report needed Array.Copy(Data, 0, TotalData, 0, Data.Length); } else { // Muliple reports needed Array.Copy(Data, 0, TotalData, 0, 60); Array.Copy(Data, 60, TotalData, 64, Data.Length - 60); } TotalData[60] = Convert.ToByte((Data.Length + 2) & 0xFF); TotalData[61] = Convert.ToByte(((Data.Length + 2) >> 8) & 0xFF); TotalData[62] = Convert.ToByte(((Data.Length + 2) >> 16) & 0xFF); TotalData[63] = Convert.ToByte(((Data.Length + 2) >> 24) & 0xFF); uint Checksum = GetTwosCompWordChecksum(Data); // If the data packet is more than one report big, add 4 to the index to skip // over the Bulk Transfer Checksum if (TotalData.Length > 0x40) { TotalData[Data.Length + 4 + 0] = Convert.ToByte(Checksum & 0xFF); TotalData[Data.Length + 4 + 1] = Convert.ToByte((Checksum >> 8) & 0xFF); } else { // The packet is only 1 report, so we don't need to adjust for the DWORD Length TotalData[Data.Length + 0] = Convert.ToByte(Checksum & 0xFF); TotalData[Data.Length + 1] = Convert.ToByte((Checksum >> 8) & 0xFF); } byte[] outgoingData = new byte[64]; ResetStatusBar((int)(TotalData.Length / 64)); for (int reportCount = 0; reportCount * 64 < TotalData.Length; reportCount++) { Array.Copy(TotalData, reportCount * 64, outgoingData, 0, 64); Pk3.writeUSB(outgoingData); StepStatusBar(); } if (Pk3.readUSB()) { // Evaluate response to determine success or failure if ((Pk3.Usb_read_array[1] == 0x00) && (Pk3.Usb_read_array[2] == 0x00)) { // we got a "success" indicator return(true); } else { // we got something crazy return(false); } } else { return(false); } }
static private bool ReceiveCommandResponse(uint Command, out byte[] DataReceived) { //TODO: Now that ReceiveCommandResponse() throws an exception instead of returning "false" on USB errors, make DataReceived a return value instead of an [out] parameter. //TODO: This method uses 3 different ways to control loops: if/else, break/continue, and straight returns inside code. Pick one and clean up. byte[] usbReadBuffer = new byte[64]; byte[] usbCommand = new byte[] { (byte)(((ushort)Command) & 0xFF), (byte)(((ushort)Command >> 8) & 0xFF) }; bool ExitLoop = false; uint DataLength = 0; // Temporary assignment to make the compiler happy. DataReceived = null; // Internally we will use a generic list to handle output data. List <byte> retval = new List <byte>(); while (ExitLoop == false) { Pk3.readUSB(); // Remember: readUSB() is a blocking function // Grab the payload size from the message, subtracting the command echo and success indicator DataLength = checked ((uint)(Pk3.Usb_read_array[61] + (Pk3.Usb_read_array[62] << 8) + (Pk3.Usb_read_array[63] << 16) + (Pk3.Usb_read_array[64] << 24) - 4)); DataReceived = new byte[DataLength]; // If the received report does not include the command echo, reject it. The // command was not echoed properly. if ((Pk3.Usb_read_array[1] != usbCommand[0]) || (Pk3.Usb_read_array[2] != usbCommand[1])) { // This is a protocol exception. return(false); } // The 3rd and 4th byte are either the Success/Fail indicator bytes or the Working // Indicator packet. if (Pk3.Usb_read_array[3] == 0x3F && Pk3.Usb_read_array[4] == 0x00) { // Received Working Indicator... need to try again for package. while (ExitLoop == false) { Pk3.readUSB(); // Grab the payload size from the message, subtracting the success indicator DataLength = checked ((uint)(Pk3.Usb_read_array[61] + (Pk3.Usb_read_array[62] << 8) + (Pk3.Usb_read_array[63] << 16) + (Pk3.Usb_read_array[64] << 24) - 4)); DataReceived = new byte[DataLength]; // In subsequent receptions, there is no Command Echo, so everything // is shifted to the left by two bytes. The Success or Working Indicator // will appear at bytes 0 and 1. if ((Pk3.Usb_read_array[1] == 0x3F) && (Pk3.Usb_read_array[2] == 0x00)) { // We received a Working Indicator. Try again with the next report. continue; } else if ((Pk3.Usb_read_array[1] == 0x00) && (Pk3.Usb_read_array[2] == 0x00)) { // We received a Success Indicator. Declare success. if (DataLength > 0) { // Length is non-zero, then the returning data needs to be passed to the calling function Array.Copy(Pk3.Usb_read_array, 3, DataReceived, 0, DataLength); } ExitLoop = true; break; } else if ((Pk3.Usb_read_array[1] == 0xFF) && (Pk3.Usb_read_array[2] == 0x00)) { // We received a Fail Indicator. Declare failure. // NOTE FOR DEBUGGERS: We expect VS's debugger to stop here, but this // actually gets handled in the BackgroundWorker's RunWorkCompleted // callback. Just hit "play" to continue debugging. return(false); } else { // Any other received message is an error. //Debugger.Break(); return(false); } } continue; } else if ((Pk3.Usb_read_array[3] == 0x00) && (Pk3.Usb_read_array[4] == 0x00)) { // Received "Success" indicator. Copy any data values over and return (ignore command echo and success indicator) // If the incoming data is longer than a USB report size, we will have to grab more than one. // See SendReadMemObj() method. It is doing basically the same thing. if (DataLength == 0) { } else if (DataLength <= 60) { // Incoming data fits in one USB report. Remember: the length includes // the command echo and success/fail indicator. We only want to return // the actual data, so we can ignore those parts. We have to subtract // this size from the total size for the array copy, or else we will // include the Length data as part of the return data. Array.Copy(Pk3.Usb_read_array, 5, DataReceived, 0, DataLength); } else { // Incoming data requires more than one report. // Grab the first report. Its size is different than the rest because of // the Command Echo and Success/Fail indicator. (56 bytes of data) Array.Copy(Pk3.Usb_read_array, 5, Pk3.Usb_read_array, 0, 56); Array.Resize <byte>(ref Pk3.Usb_read_array, 56); // Resize, so we can use .AddRange() to add the data below. retval.AddRange(Pk3.Usb_read_array); // We'll use the generic list for this part // Tracks the expected number of data bytes in the next USB report uint IncomingDataLength = 56; // Get the rest of the data. Note that incrementing the loop control variable is // done within the loop for (uint DataLeftToGet = DataLength - 56; DataLeftToGet > 0; DataLeftToGet -= IncomingDataLength) { if (DataLeftToGet > 64) { IncomingDataLength = 64; } else { IncomingDataLength = DataLeftToGet; } Pk3.readUSB(); // Adjust the array in case we're receiving less than the USB report size of data if (IncomingDataLength < 64) { byte[] smallerArray = new byte[IncomingDataLength]; Array.Copy(Pk3.Usb_read_array, 1, smallerArray, 0, smallerArray.Length); retval.AddRange(smallerArray); } else { Array.Copy(Pk3.Usb_read_array, 1, Pk3.Usb_read_array, 0, 64); retval.AddRange(Pk3.Usb_read_array); } } DataReceived = retval.ToArray(); } ExitLoop = true; continue; } else if ((Pk3.Usb_read_array[3] == 0xFF) && (Pk3.Usb_read_array[4] == 0x00)) { // Received "Fail" indicator. return(false); } else { // If any other value is found, assume failure. //Debugger.Break(); return(false); } } return(true); }