summaryrefslogtreecommitdiff
path: root/bootloader/uploader/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'bootloader/uploader/main.cpp')
-rw-r--r--bootloader/uploader/main.cpp492
1 files changed, 492 insertions, 0 deletions
diff --git a/bootloader/uploader/main.cpp b/bootloader/uploader/main.cpp
new file mode 100644
index 0000000..f18c81b
--- /dev/null
+++ b/bootloader/uploader/main.cpp
@@ -0,0 +1,492 @@
+/*
+ * This is a derivative of SysExBoot
+ *
+ * Modified by Brian Richardson for Jabrudian Industries
+ *
+ * >>>>>>>>>>>>>> origional header >>>>>>
+ *
+ * "A sysex bootloader for avr chips."
+ * Copyright 2010 Alex Norman
+ *
+ * This file is part of SysexBoot.
+ *
+ *
+ * SysexBoot 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * SysexBoot 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 SysexBoot. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+#include <vector>
+#include <iostream>
+#include <fstream>
+#include <sys/stat.h>
+#include "portmidi.h"
+#include "porttime.h"
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sysex_tools.h"
+
+#include "midibootcommon.h"
+
+#define TIMEOUT_MS 3000
+
+#define CHUNK_SIZE 48
+
+/*
+ * a sysex packet for a page_fill looks like:
+ * MIDI_SYSEX_START
+ * midiboot_sysex_id[]
+ * MIDIBOOT_FILLPAGE
+ * packed_addr+data[<=35] (unpacks to 2 addr bytes + 28 or fewer data bytes)
+ * MIDI_SYSEX_STOP
+ */
+
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::hex;
+using std::dec;
+
+void simple_bit_unpack(uint8_t * unpacked, const uint8_t * source,
+ uint16_t length)
+{
+ for (int i = 0; i < length; i += 2) {
+ unpacked[i / 2] = (source[i] << 4) | source[i + 1];
+ }
+}
+
+uint16_t simple_bit_packed_length(uint16_t unpacked_length)
+{
+ return unpacked_length * 2;
+}
+
+uint16_t simple_bit_pack(uint8_t * packed, const uint8_t * source,
+ uint16_t length)
+{
+ for (int i = 0; i < length; i++) {
+ packed[i * 2] = source[i] >> 4;
+ packed[i * 2 + 1] = source[i] & 0xF;
+ }
+ return length * 2;
+}
+
+int getMissingLink(bool input)
+{
+ for (int i = 0; i < Pm_CountDevices(); i++) {
+ const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
+ if ((info->input && input) || (info->output && !input)) {
+ if (strncmp(info->name, "Missing Link", 12) == 0) {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+void printMidiDevices(bool input)
+{
+ for (int i = 0; i < Pm_CountDevices(); i++) {
+ const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
+ if (info->input && input)
+ cout << i << ", " << info->interf << ", " << info->
+ name << endl;
+ if (info->output && !input)
+ cout << i << ", " << info->interf << ", " << info->
+ name << endl;
+ //printf("%d: %s, %s\n", i, info->interf, info->name);
+ }
+}
+
+void MLCommand(PmStream * midi_in, PmStream * midi_out, long timeout,
+ unsigned char command)
+{
+ // SYSEX BEGIN, EDU, MLNK, 3=enter bootloader, SYSEX END
+ unsigned char requestMsg[] =
+ { 0xF0, 0x7D, 0x4D, 0x4C, 0x4E, 0x4B, command, 0xF7 };
+ //send the message
+ PmError error = Pm_WriteSysEx(midi_out, 0, requestMsg);
+ if (error) {
+ cerr << "error writing ENTER BOOTLOADER msg: " <<
+ Pm_GetErrorText(error) << endl;
+ exit(-1);
+ }
+ long startTime = Pt_Time();
+ while (Pt_Time() - startTime < timeout) ;
+}
+
+int getPageSize(PmStream * midi_in, PmStream * midi_out, long timeout)
+{
+ PmEvent msg;
+ bool inSysexMessage;
+ unsigned int sysexByteCount;
+ long startTime = Pt_Time();
+ int pageSize = 0;
+ uint8_t buf[3];
+ uint8_t bufout[3];
+
+ //write get page size message
+ unsigned char requestMsg[64];
+ memset(&requestMsg, 0, 64);
+ requestMsg[0] = MIDI_SYSEX_START;
+ for (unsigned int i = 0; i < MIDIBOOT_SYSEX_ID_LEN; i++)
+ requestMsg[i + 1] = midiboot_sysex_id[i];
+ requestMsg[MIDIBOOT_SYSEX_ID_LEN + 1] = MIDIBOOT_GETPAGESIZE;
+ requestMsg[MIDIBOOT_SYSEX_ID_LEN + 2] = MIDI_SYSEX_STOP;
+ //send the message
+ PmError error = Pm_WriteSysEx(midi_out, 0, requestMsg);
+ if (error) {
+ cerr << "error writing GET PAGE SIZE msg: " <<
+ Pm_GetErrorText(error) << endl;
+ exit(-1);
+ }
+ //allow for a second timeout
+ while (startTime + timeout > Pt_Time()) {
+ unsigned int count = Pm_Read(midi_in, &msg, 1);
+ if (count != 0) {
+ for (unsigned int i = 0; i < 4; i++) {
+ unsigned char inByte =
+ (msg.message >> (i * 8)) & 0xFF;
+ if (inByte == MIDI_SYSEX_START) {
+ sysexByteCount = 0;
+ inSysexMessage = true;
+ } else if (inSysexMessage) {
+ if (inByte == MIDI_SYSEX_STOP) {
+ simple_bit_unpack(bufout, buf,
+ 4);
+ pageSize =
+ (bufout[0] << 8) |
+ bufout[1];
+ return pageSize;
+ } else
+ if ((sysexByteCount <
+ MIDIBOOT_SYSEX_ID_LEN)) {
+ //make sure that our id matches
+ if (inByte ==
+ midiboot_sysex_id
+ [sysexByteCount])
+ sysexByteCount++;
+ else
+ inSysexMessage = false;
+ } else if (sysexByteCount ==
+ MIDIBOOT_SYSEX_ID_LEN) {
+ if (inByte !=
+ MIDIBOOT_GETPAGESIZE)
+ inSysexMessage = false;
+ else
+ sysexByteCount++;
+ } else if (sysexByteCount <
+ (MIDIBOOT_SYSEX_ID_LEN +
+ 5)) {
+ unsigned int index =
+ (sysexByteCount -
+ MIDIBOOT_SYSEX_ID_LEN - 1);
+ buf[index] = inByte;
+ sysexByteCount++;
+ } else
+ inSysexMessage = false;
+ }
+ }
+ }
+ }
+ cout << endl;
+ return 0;
+}
+
+bool waitForAck(PmStream * midi_in, long timeout)
+{
+ PmEvent msg;
+ bool inSysexMessage;
+ unsigned int sysexByteCount;
+ long startTime = Pt_Time();
+ //allow for a second timeout
+ while (startTime + timeout > Pt_Time()) {
+ unsigned int count = Pm_Read(midi_in, &msg, 1);
+ if (count != 0) {
+ for (unsigned int i = 0; i < 4; i++) {
+ unsigned char inByte =
+ (msg.message >> (i * 8)) & 0xFF;
+ if (inByte == MIDI_SYSEX_START) {
+ sysexByteCount = 0;
+ inSysexMessage = true;
+ } else if (inSysexMessage) {
+ if (sysexByteCount ==
+ MIDIBOOT_SYSEX_ID_LEN) {
+ if (inByte == MIDI_SYSEX_STOP) {
+ return true;
+ } else
+ inSysexMessage = false;
+ } else
+ if ((sysexByteCount <
+ MIDIBOOT_SYSEX_ID_LEN)
+ && inByte ==
+ midiboot_sysex_id
+ [sysexByteCount])
+ sysexByteCount++;
+ else
+ inSysexMessage = false;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void writeSysexPrefix(std::vector < unsigned char >*sysexMesg)
+{
+ sysexMesg->push_back(MIDI_SYSEX_START);
+ for (unsigned int i = 0; i < MIDIBOOT_SYSEX_ID_LEN; i++)
+ sysexMesg->push_back(midiboot_sysex_id[i]);
+}
+
+int main(int argc, char *argv[])
+{
+ std::vector < unsigned char >inputData;
+ std::vector < std::vector < unsigned char >*>pages;
+ std::vector < std::vector < unsigned char >*>sysexMessages;
+ std::vector < unsigned char >packedPage;
+ unsigned char tmp;
+ int id;
+ std::ifstream inFile;
+ struct stat stFileInfo;
+ PmStream *midi_in;
+ PmStream *midi_out;
+ unsigned int pageSize = 0;
+ unsigned short numPages = 0;
+ const PmDeviceInfo *info;
+
+ //start our time
+ Pt_Start(1, 0, 0);
+
+ //test args
+ if (argc != 2) {
+ cout << "usage:" << endl;
+ cout << argv[0] << " binfiletoupload" << endl;
+ exit(-1);
+ }
+ if (stat(argv[1], &stFileInfo) != 0 || !S_ISREG(stFileInfo.st_mode)) {
+ cerr << "file does not exist or is not a regular file" << endl;
+ exit(-1);
+ }
+
+ printMidiDevices(true);
+ printMidiDevices(false);
+
+ id = getMissingLink(true);
+ if (id == -1) {
+ cerr << "I Did Not Find a Missing Link!" << endl;
+ exit(-1);
+ }
+//error checking here VVVV
+ info = Pm_GetDeviceInfo(id);
+ cout << endl << "**opening input : " << info->name << endl << endl;
+//error checking here VVVV
+ Pm_OpenInput(&midi_in, id, NULL, 512, NULL, NULL);
+
+ id = getMissingLink(false);
+ if (id == -1) {
+ cerr << "Could Not Find Missing Links Ouput" << endl;
+ exit(-1);
+ }
+//error checking here VVVV
+ info = Pm_GetDeviceInfo(id);
+ cout << endl << "**opening output : " << info->name << endl << endl;
+//error checking here VVVV
+ Pm_OpenOutput(&midi_out, id, NULL, 0, NULL, NULL, 0);
+
+ cout << "uploading file " << argv[1] << endl;
+ //cout << "* make sure that you have put the microcontroller in boot loader mode" << endl;
+ //cout << "hit enter to continue, or control-c to quit" << endl;
+ //std::cin.get();
+
+ //grab the page size!
+ MLCommand(midi_in, midi_out, 1500, 3);
+ pageSize = getPageSize(midi_in, midi_out, 3000);
+ if (pageSize == 0) {
+ cerr <<
+ "*** failed to get the page size from the microcontroller"
+ << endl;
+ cerr << "*** are you in boot loader mode?" << endl;
+ cerr <<
+ "*** do you have both the midi input and output to/from your microcontroller connected to your computer"
+ << endl;
+ cerr << "*** did you select the correct input and output?" <<
+ endl;
+ cerr << "*** aborting" << endl;
+ exit(-1);
+ } else if (pageSize != 128) {
+ cerr << "*** something is amok !!! " << endl
+ << " " << pageSize << " isnt the page size we are expecting (128)" << endl
+ << " Bailing ***" << endl;
+ exit(-1);
+ }
+
+
+ //open the file
+ inFile.open(argv[1], std::ios::in | std::ios::binary);
+ if (!inFile.is_open()) {
+ cerr << "cannot open file: " << argv[1] << endl;
+ //Pm_Close(midi_out);
+ Pm_Close(midi_in);
+ exit(-1);
+ }
+ //read in the file data
+ tmp = inFile.get();
+ while (!inFile.eof()) {
+ inputData.push_back(tmp);
+ tmp = inFile.get();
+ }
+ //see if there is data
+ if (inputData.size() < 1) {
+ cerr << "datafile is empty" << endl;
+ //Pm_Close(midi_out);
+ Pm_Close(midi_in);
+ exit(-1);
+ }
+ //compute the number of pages we'll need
+ numPages = (unsigned short)ceil((float)inputData.size() / pageSize);
+ if (numPages == 0)
+ numPages = 1;
+ //make sure there are enough positions in the pages
+ pages.reserve(numPages);
+ sysexMessages.reserve(numPages);
+
+ //cut the data into pages
+ for (unsigned short i = 0; i < inputData.size(); i++) {
+ unsigned int pageIndex = i / pageSize;
+ //if we're at a new page then push the page index, two bytes
+ if (i % pageSize == 0) {
+ pages.push_back(new std::vector < unsigned char >);
+ }
+ pages[pageIndex]->push_back(inputData[i]);
+ }
+
+ //pack and build the sysex messages
+ cout << "byte packing the file contents" << endl;
+
+ for (unsigned int i = 0; i < numPages; i++) {
+ //create a new sysex message for each chunk
+ for (unsigned int j = 0; j < pages[i]->size(); j += CHUNK_SIZE) {
+ std::vector < unsigned char >tmp;
+ unsigned int start_index = j;
+ unsigned int end_index = j + CHUNK_SIZE;
+
+ //make sure our index is in range
+ if (end_index > pages[i]->size())
+ end_index = pages[i]->size();
+ std::vector < unsigned char >*newMsg =
+ new std::vector < unsigned char >;
+
+ //write the header
+ writeSysexPrefix(newMsg);
+ newMsg->push_back(MIDIBOOT_FILLPAGE);
+
+ //write address to tmp
+ unsigned int pageAddress = i * pageSize + start_index;
+ tmp.clear();
+ tmp.push_back((unsigned char)(pageAddress >> 8) & 0xFF);
+ tmp.push_back((unsigned char)pageAddress & 0xFF);
+
+ //write the data to tmp
+ for (unsigned int k = start_index; k < end_index; k++)
+ tmp.push_back(pages[i]->at(k));
+
+ //pack the address+data
+ packedPage.clear();
+ packedPage.resize(simple_bit_packed_length(tmp.size()));
+
+ simple_bit_pack(&packedPage[0], &tmp[0], tmp.size());
+
+ //write the packed address+data to the sysexMessage
+ for (std::vector < unsigned char >::iterator it =
+ packedPage.begin(); it != packedPage.end(); it++)
+ newMsg->push_back(*it);
+
+ //write the sysex end message
+ newMsg->push_back(MIDI_SYSEX_STOP);
+ //push this sysex message onto our list of sysex messages
+ sysexMessages.push_back(newMsg);
+ }
+ //create the 'write page' sysex message
+ std::vector < unsigned char >*newMsg =
+ new std::vector < unsigned char >;
+ writeSysexPrefix(newMsg);
+ newMsg->push_back(MIDIBOOT_WRITEPAGE);
+ newMsg->push_back(MIDI_SYSEX_STOP);
+ //push this sysex message onto our list of sysex messages
+ sysexMessages.push_back(newMsg);
+ }
+
+ cout << "page size: " << pageSize << endl;
+ cout << "this many pages will be written: " << pages.size() << endl;
+ cout << "this many bytes will be written: " << inputData.size() << endl;
+ cout << "using this many sysex messages: " << sysexMessages.
+ size() << endl;
+
+ //upload
+ cout << "uploading now" << endl;
+
+ //send each sysex message
+ for (std::vector < std::vector < unsigned char >*>::iterator it =
+ sysexMessages.begin(); it != sysexMessages.end(); it++) {
+ PmError error;
+ unsigned char msg[64];
+
+ //make sure we've only got zeros
+ memset(&msg, 0, 64);
+
+ //fill in sysex buffer
+ for (unsigned int i = 0; i < (*it)->size(); i++) {
+ msg[i] = (*it)->at(i);
+ }
+
+ //send the message
+ error = Pm_WriteSysEx(midi_out, 0, msg);
+ if (error) {
+ cerr << "error: " << Pm_GetErrorText(error) << endl;
+ exit(-1);
+ }
+ //get ack
+ if (!waitForAck(midi_in, TIMEOUT_MS)) {
+ cerr <<
+ "FAILED: sysex message failed to acknowledge within the timeout"
+ << endl;
+ cerr << "was the microcontroller in boot loader mode?"
+ << endl;
+ exit(-1);
+ }
+ }
+
+ cout << "success" << endl;
+
+ //leave boot
+ unsigned char msg[64];
+ memset(&msg, 0, 64);
+ msg[0] = MIDI_SYSEX_START;
+ for (unsigned int i = 0; i < MIDIBOOT_SYSEX_ID_LEN; i++)
+ msg[i + 1] = midiboot_sysex_id[i];
+ msg[MIDIBOOT_SYSEX_ID_LEN + 1] = MIDIBOOT_LEAVE_BOOT;
+ msg[MIDIBOOT_SYSEX_ID_LEN + 2] = MIDI_SYSEX_STOP;
+ //send the message
+ PmError error = Pm_WriteSysEx(midi_out, 0, msg);
+ if (error) {
+ cerr << "error writing LEAVE BOOT msg: " <<
+ Pm_GetErrorText(error) << endl;
+ exit(-1);
+ }
+ MLCommand(midi_in, midi_out, 1500, 1);
+
+ Pm_Close(midi_out);
+ Pm_Close(midi_in);
+ return 0;
+}